Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmPathResolver.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmPathResolver.h"
4
5
#include <algorithm>
6
#include <cerrno>
7
#include <cstddef>
8
#include <string>
9
#include <utility>
10
11
#include <cm/optional>
12
#include <cm/string_view>
13
#include <cmext/string_view>
14
15
#ifdef _WIN32
16
#  include <windows.h>
17
18
#  include <cmsys/String.h>
19
#endif
20
21
0
#define MAX_SYMBOLIC_LINKS 32
22
23
namespace cm {
24
namespace PathResolver {
25
26
namespace {
27
28
namespace Options {
29
30
enum class ActualCase
31
{
32
  No,
33
  Yes,
34
};
35
36
enum class Symlinks
37
{
38
  None,
39
  Lazy,
40
  Eager,
41
};
42
43
enum class Existence
44
{
45
  Agnostic,
46
  Required,
47
};
48
}
49
50
enum class Root
51
{
52
  None,
53
  POSIX,
54
#ifdef _WIN32
55
  Drive,
56
  Network,
57
#endif
58
};
59
60
struct Control
61
{
62
  enum class Tag
63
  {
64
    Continue,
65
    Restart,
66
    Error,
67
  };
68
  Tag tag;
69
  union
70
  {
71
    std::string::size_type slash; // data for Continue
72
    cmsys::Status error;          // data for Error
73
  };
74
  static Control Continue(std::string::size_type s)
75
56
  {
76
56
    Control c{ Tag::Continue };
77
56
    c.slash = s;
78
56
    return c;
79
56
  }
80
0
  static Control Restart() { return Control{ Tag::Restart }; }
81
  static Control Error(cmsys::Status e)
82
0
  {
83
0
    Control c{ Tag::Error };
84
0
    c.error = e;
85
0
    return c;
86
0
  }
87
88
private:
89
  Control(Tag t)
90
56
    : tag(t)
91
56
  {
92
56
  }
93
};
94
95
Root ClassifyRoot(cm::string_view p)
96
11
{
97
#ifdef _WIN32
98
  if (p.size() >= 2 && cmsysString_isalpha(p[0]) && p[1] == ':') {
99
    return Root::Drive;
100
  }
101
  if (p.size() >= 3 && p[0] == '/' && p[1] == '/' && p[2] != '/') {
102
    return Root::Network;
103
  }
104
#endif
105
11
  if (!p.empty() && p[0] == '/') {
106
11
    return Root::POSIX;
107
11
  }
108
0
  return Root::None;
109
11
}
110
111
class ImplBase
112
{
113
protected:
114
  ImplBase(System& os)
115
11
    : OS(os)
116
11
  {
117
11
  }
118
119
  System& OS;
120
  std::string P;
121
  std::size_t SymlinkDepth = 0;
122
123
#ifdef _WIN32
124
  std::string GetWorkingDirectoryOnDrive(char letter);
125
  Control ResolveRootRelative();
126
#endif
127
  cm::optional<std::string> ReadSymlink(std::string const& path,
128
                                        cmsys::Status& status);
129
  Control ResolveSymlink(Root root, std::string::size_type slash,
130
                         std::string::size_type next_slash,
131
                         std::string symlink_target);
132
};
133
134
template <class Policy>
135
class Impl : public ImplBase
136
{
137
  Control ResolveRelativePath();
138
  Control ResolveRoot(Root root);
139
  Control ResolveComponent(Root root, std::string::size_type root_slash,
140
                           std::string::size_type slash);
141
  Control ResolvePath();
142
143
public:
144
  Impl(System& os)
145
11
    : ImplBase(os)
146
11
  {
147
11
  }
cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::LogicalPath>::Impl(cm::PathResolver::System&)
Line
Count
Source
145
11
    : ImplBase(os)
146
11
  {
147
11
  }
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::RealPath>::Impl(cm::PathResolver::System&)
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::CasePath>::Impl(cm::PathResolver::System&)
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::NaivePath>::Impl(cm::PathResolver::System&)
148
  cmsys::Status Resolve(std::string in, std::string& out);
149
};
150
151
template <class Policy>
152
Control Impl<Policy>::ResolveRelativePath()
153
0
{
154
  // This is a relative path.  Convert it to absolute and restart.
155
0
  std::string p = this->OS.GetWorkingDirectory();
156
0
  std::replace(p.begin(), p.end(), '\\', '/');
157
0
  if (ClassifyRoot(p) == Root::None) {
158
0
    p.insert(0, 1, '/');
159
0
  }
160
0
  if (p.back() != '/') {
161
0
    p.push_back('/');
162
0
  }
163
0
  P.insert(0, p);
164
0
  return Control::Restart();
165
0
}
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::LogicalPath>::ResolveRelativePath()
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::RealPath>::ResolveRelativePath()
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::CasePath>::ResolveRelativePath()
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::NaivePath>::ResolveRelativePath()
166
167
#ifdef _WIN32
168
std::string ImplBase::GetWorkingDirectoryOnDrive(char letter)
169
{
170
  // Use the drive's working directory, if any.
171
  std::string d = this->OS.GetWorkingDirectoryOnDrive(letter);
172
  std::replace(d.begin(), d.end(), '\\', '/');
173
  if (d.size() >= 3 &&
174
      cmsysString_toupper(d[0]) == cmsysString_toupper(letter) &&
175
      d[1] == ':' && d[2] == '/') {
176
    d[0] = letter;
177
    d.push_back('/');
178
    return d;
179
  }
180
181
  // Use the current working directory if the drive matches.
182
  d = this->OS.GetWorkingDirectory();
183
  if (d.size() >= 3 &&
184
      cmsysString_toupper(d[0]) == cmsysString_toupper(letter) &&
185
      d[1] == ':' && d[2] == '/') {
186
    d[0] = letter;
187
    d.push_back('/');
188
    return d;
189
  }
190
191
  // Fall back to the root directory on the drive.
192
  d = "_:/";
193
  d[0] = letter;
194
  return d;
195
}
196
197
Control ImplBase::ResolveRootRelative()
198
{
199
  // This is a root-relative path.  Resolve the root drive and restart.
200
  P.replace(0, 2, this->GetWorkingDirectoryOnDrive(P[0]));
201
  return Control::Restart();
202
}
203
#endif
204
205
cm::optional<std::string> ImplBase::ReadSymlink(std::string const& path,
206
                                                cmsys::Status& status)
207
0
{
208
0
  cm::optional<std::string> result;
209
0
  std::string target;
210
0
  status = this->OS.ReadSymlink(path, target);
211
0
  if (status && ++this->SymlinkDepth >= MAX_SYMBOLIC_LINKS) {
212
0
    status = cmsys::Status::POSIX(ELOOP);
213
0
  }
214
0
  if (status) {
215
0
    if (!target.empty()) {
216
0
      result = std::move(target);
217
0
    }
218
0
  } else if (status.GetPOSIX() == EINVAL
219
#ifdef _WIN32
220
             || status.GetWindows() == ERROR_NOT_A_REPARSE_POINT
221
#endif
222
0
  ) {
223
    // The path was not a symlink.
224
0
    status = cmsys::Status::Success();
225
0
  }
226
0
  return result;
227
0
}
228
229
Control ImplBase::ResolveSymlink(Root root, std::string::size_type slash,
230
                                 std::string::size_type next_slash,
231
                                 std::string symlink_target)
232
0
{
233
0
  std::replace(symlink_target.begin(), symlink_target.end(), '\\', '/');
234
0
  Root const symlink_target_root = ClassifyRoot(symlink_target);
235
0
  if (symlink_target_root == Root::None) {
236
    // This is a symlink to a relative path.
237
    // Resolve the symlink, while preserving the leading and
238
    // trailing (if any) slash:
239
    //   "*/link/" => "*/dest/"
240
    //     ^slash       ^slash
241
0
    P.replace(slash + 1, next_slash - slash - 1, symlink_target);
242
0
    return Control::Continue(slash);
243
0
  }
244
245
#ifdef _WIN32
246
  if (root == Root::Drive && symlink_target_root == Root::POSIX) {
247
    // This is a symlink to a POSIX absolute path,
248
    // but the current path is on a drive letter.  Resolve the
249
    // symlink while preserving the drive letter, and start over:
250
    //   "C:/*/link/" => "C:/dest/"
251
    //        ^slash      (restart)
252
    P.replace(2, next_slash - 2, symlink_target);
253
    return Control::Restart();
254
  }
255
#else
256
0
  static_cast<void>(root);
257
0
#endif
258
259
  // This is a symlink to an absolute path.
260
  // Resolve it and start over:
261
  //   "*/link/" => "/dest/"
262
  //     ^slash      (restart)
263
0
  P.replace(0, next_slash, symlink_target);
264
0
  return Control::Restart();
265
0
}
266
267
template <class Policy>
268
Control Impl<Policy>::ResolveRoot(Root root)
269
11
{
270
11
  if (root == Root::None) {
271
0
    return this->ResolveRelativePath();
272
0
  }
273
274
  // POSIX absolute paths always start with a '/'.
275
11
  std::string::size_type root_slash = 0;
276
277
#ifdef _WIN32
278
  if (root == Root::Drive) {
279
    if (P.size() == 2 || P[2] != '/') {
280
      return this->ResolveRootRelative();
281
    }
282
283
    if (Policy::ActualCase == Options::ActualCase::Yes) {
284
      // Normalize the drive letter to upper-case.
285
      P[0] = static_cast<char>(cmsysString_toupper(P[0]));
286
    }
287
288
    // The root is a drive letter.  The root '/' immediately follows.
289
    root_slash = 2;
290
  } else if (root == Root::Network) {
291
    // The root is a network name.  Find the root '/' after it.
292
    root_slash = P.find('/', 2);
293
    if (root_slash == std::string::npos) {
294
      root_slash = P.size();
295
      P.push_back('/');
296
    }
297
  }
298
#endif
299
300
11
  if (Policy::Existence == Options::Existence::Required
301
#ifdef _WIN32
302
      && root != Root::Network
303
#endif
304
11
  ) {
305
0
    std::string path = P.substr(0, root_slash + 1);
306
0
    if (!this->OS.PathExists(path)) {
307
0
      P = std::move(path);
308
0
      return Control::Error(cmsys::Status::POSIX(ENOENT));
309
0
    }
310
0
  }
311
312
11
  return Control::Continue(root_slash);
313
11
}
cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::LogicalPath>::ResolveRoot(cm::PathResolver::(anonymous namespace)::Root)
Line
Count
Source
269
11
{
270
11
  if (root == Root::None) {
271
0
    return this->ResolveRelativePath();
272
0
  }
273
274
  // POSIX absolute paths always start with a '/'.
275
11
  std::string::size_type root_slash = 0;
276
277
#ifdef _WIN32
278
  if (root == Root::Drive) {
279
    if (P.size() == 2 || P[2] != '/') {
280
      return this->ResolveRootRelative();
281
    }
282
283
    if (Policy::ActualCase == Options::ActualCase::Yes) {
284
      // Normalize the drive letter to upper-case.
285
      P[0] = static_cast<char>(cmsysString_toupper(P[0]));
286
    }
287
288
    // The root is a drive letter.  The root '/' immediately follows.
289
    root_slash = 2;
290
  } else if (root == Root::Network) {
291
    // The root is a network name.  Find the root '/' after it.
292
    root_slash = P.find('/', 2);
293
    if (root_slash == std::string::npos) {
294
      root_slash = P.size();
295
      P.push_back('/');
296
    }
297
  }
298
#endif
299
300
11
  if (Policy::Existence == Options::Existence::Required
301
#ifdef _WIN32
302
      && root != Root::Network
303
#endif
304
11
  ) {
305
0
    std::string path = P.substr(0, root_slash + 1);
306
0
    if (!this->OS.PathExists(path)) {
307
0
      P = std::move(path);
308
0
      return Control::Error(cmsys::Status::POSIX(ENOENT));
309
0
    }
310
0
  }
311
312
11
  return Control::Continue(root_slash);
313
11
}
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::RealPath>::ResolveRoot(cm::PathResolver::(anonymous namespace)::Root)
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::CasePath>::ResolveRoot(cm::PathResolver::(anonymous namespace)::Root)
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::NaivePath>::ResolveRoot(cm::PathResolver::(anonymous namespace)::Root)
314
315
template <class Policy>
316
Control Impl<Policy>::ResolveComponent(Root root,
317
                                       std::string::size_type root_slash,
318
                                       std::string::size_type slash)
319
34
{
320
  // Look for the '/' or end-of-input that ends this component.
321
  // The sample paths in comments below show the trailing slash
322
  // even if it is actually beyond the end of the path.
323
34
  std::string::size_type next_slash = P.find('/', slash + 1);
324
34
  if (next_slash == std::string::npos) {
325
11
    next_slash = P.size();
326
11
  }
327
34
  cm::string_view c =
328
34
    cm::string_view(P).substr(slash + 1, next_slash - (slash + 1));
329
330
34
  if (slash == root_slash) {
331
11
    if (c.empty() || c == "."_s || c == ".."_s) {
332
      // This is an empty, '.', or '..' component at the root.
333
      // Drop the component and its trailing slash, if any,
334
      // while preserving the root slash:
335
      //   "//"   => "/"
336
      //   "/./"  => "/"
337
      //   "/../" => "/"
338
      //    ^slash    ^slash
339
0
      P.erase(slash + 1, next_slash - slash);
340
0
      return Control::Continue(slash);
341
0
    }
342
23
  } else {
343
23
    if (c.empty() || c == "."_s) {
344
      // This is an empty or '.' component not at the root.
345
      // Drop the component and its leading slash:
346
      //   "*//"  => "*/"
347
      //   "*/./" => "*/"
348
      //     ^slash    ^slash
349
0
      P.erase(slash, next_slash - slash);
350
0
      return Control::Continue(slash);
351
0
    }
352
353
23
    if (c == ".."_s) {
354
      // This is a '..' component not at the root.
355
      // Rewind to the previous component:
356
      //   "*/prev/../" => "*/prev/../"
357
      //          ^slash     ^slash
358
0
      next_slash = slash;
359
0
      slash = P.rfind('/', slash - 1);
360
361
0
      if (Policy::Symlinks == Options::Symlinks::Lazy) {
362
0
        cmsys::Status status;
363
0
        std::string path = P.substr(0, next_slash);
364
0
        if (cm::optional<std::string> maybe_symlink_target =
365
0
              this->ReadSymlink(path, status)) {
366
0
          return this->ResolveSymlink(root, slash, next_slash,
367
0
                                      std::move(*maybe_symlink_target));
368
0
        }
369
0
        if (!status && Policy::Existence == Options::Existence::Required) {
370
0
          P = std::move(path);
371
0
          return Control::Error(status);
372
0
        }
373
0
      }
374
375
      // This is not a symlink.
376
      // Drop the component, the following '..', and its trailing slash,
377
      // if any, while preserving the (possibly root) leading slash:
378
      //   "*/dir/../" => "*/"
379
      //     ^slash         ^slash
380
0
      P.erase(slash + 1, next_slash + 3 - slash);
381
0
      return Control::Continue(slash);
382
0
    }
383
23
  }
384
385
  // This is a named component.
386
387
34
  if (Policy::Symlinks == Options::Symlinks::Eager) {
388
0
    cmsys::Status status;
389
0
    std::string path = P.substr(0, next_slash);
390
0
    if (cm::optional<std::string> maybe_symlink_target =
391
0
          this->ReadSymlink(path, status)) {
392
0
      return this->ResolveSymlink(root, slash, next_slash,
393
0
                                  std::move(*maybe_symlink_target));
394
0
    }
395
0
    if (!status && Policy::Existence == Options::Existence::Required) {
396
0
      P = std::move(path);
397
0
      return Control::Error(status);
398
0
    }
399
0
  }
400
401
#if defined(_WIN32) || defined(__APPLE__)
402
  bool exists = false;
403
  if (Policy::ActualCase == Options::ActualCase::Yes) {
404
    std::string name;
405
    std::string path = P.substr(0, next_slash);
406
    if (cmsys::Status status = this->OS.ReadName(path, name)) {
407
      exists = true;
408
      if (!name.empty()) {
409
        // Rename this component:
410
        //   "*/name/" => "*/Name/"
411
        //     ^slash       ^slash
412
        P.replace(slash + 1, next_slash - slash - 1, name);
413
        next_slash = slash + 1 + name.length();
414
      }
415
    } else if (Policy::Existence == Options::Existence::Required) {
416
      P = std::move(path);
417
      return Control::Error(status);
418
    }
419
  }
420
#endif
421
422
34
  if (Policy::Existence == Options::Existence::Required
423
#if defined(_WIN32) || defined(__APPLE__)
424
      && !exists
425
#endif
426
34
  ) {
427
0
    std::string path = P.substr(0, next_slash);
428
0
    if (!this->OS.PathExists(path)) {
429
0
      P = std::move(path);
430
0
      return Control::Error(cmsys::Status::POSIX(ENOENT));
431
0
    }
432
0
  }
433
434
  // Keep this component:
435
  //   "*/name/" => "*/name/"
436
  //     ^slash            ^slash
437
34
  return Control::Continue(next_slash);
438
34
}
cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::LogicalPath>::ResolveComponent(cm::PathResolver::(anonymous namespace)::Root, unsigned long, unsigned long)
Line
Count
Source
319
34
{
320
  // Look for the '/' or end-of-input that ends this component.
321
  // The sample paths in comments below show the trailing slash
322
  // even if it is actually beyond the end of the path.
323
34
  std::string::size_type next_slash = P.find('/', slash + 1);
324
34
  if (next_slash == std::string::npos) {
325
11
    next_slash = P.size();
326
11
  }
327
34
  cm::string_view c =
328
34
    cm::string_view(P).substr(slash + 1, next_slash - (slash + 1));
329
330
34
  if (slash == root_slash) {
331
11
    if (c.empty() || c == "."_s || c == ".."_s) {
332
      // This is an empty, '.', or '..' component at the root.
333
      // Drop the component and its trailing slash, if any,
334
      // while preserving the root slash:
335
      //   "//"   => "/"
336
      //   "/./"  => "/"
337
      //   "/../" => "/"
338
      //    ^slash    ^slash
339
0
      P.erase(slash + 1, next_slash - slash);
340
0
      return Control::Continue(slash);
341
0
    }
342
23
  } else {
343
23
    if (c.empty() || c == "."_s) {
344
      // This is an empty or '.' component not at the root.
345
      // Drop the component and its leading slash:
346
      //   "*//"  => "*/"
347
      //   "*/./" => "*/"
348
      //     ^slash    ^slash
349
0
      P.erase(slash, next_slash - slash);
350
0
      return Control::Continue(slash);
351
0
    }
352
353
23
    if (c == ".."_s) {
354
      // This is a '..' component not at the root.
355
      // Rewind to the previous component:
356
      //   "*/prev/../" => "*/prev/../"
357
      //          ^slash     ^slash
358
0
      next_slash = slash;
359
0
      slash = P.rfind('/', slash - 1);
360
361
0
      if (Policy::Symlinks == Options::Symlinks::Lazy) {
362
0
        cmsys::Status status;
363
0
        std::string path = P.substr(0, next_slash);
364
0
        if (cm::optional<std::string> maybe_symlink_target =
365
0
              this->ReadSymlink(path, status)) {
366
0
          return this->ResolveSymlink(root, slash, next_slash,
367
0
                                      std::move(*maybe_symlink_target));
368
0
        }
369
0
        if (!status && Policy::Existence == Options::Existence::Required) {
370
0
          P = std::move(path);
371
0
          return Control::Error(status);
372
0
        }
373
0
      }
374
375
      // This is not a symlink.
376
      // Drop the component, the following '..', and its trailing slash,
377
      // if any, while preserving the (possibly root) leading slash:
378
      //   "*/dir/../" => "*/"
379
      //     ^slash         ^slash
380
0
      P.erase(slash + 1, next_slash + 3 - slash);
381
0
      return Control::Continue(slash);
382
0
    }
383
23
  }
384
385
  // This is a named component.
386
387
34
  if (Policy::Symlinks == Options::Symlinks::Eager) {
388
0
    cmsys::Status status;
389
0
    std::string path = P.substr(0, next_slash);
390
0
    if (cm::optional<std::string> maybe_symlink_target =
391
0
          this->ReadSymlink(path, status)) {
392
0
      return this->ResolveSymlink(root, slash, next_slash,
393
0
                                  std::move(*maybe_symlink_target));
394
0
    }
395
0
    if (!status && Policy::Existence == Options::Existence::Required) {
396
0
      P = std::move(path);
397
0
      return Control::Error(status);
398
0
    }
399
0
  }
400
401
#if defined(_WIN32) || defined(__APPLE__)
402
  bool exists = false;
403
  if (Policy::ActualCase == Options::ActualCase::Yes) {
404
    std::string name;
405
    std::string path = P.substr(0, next_slash);
406
    if (cmsys::Status status = this->OS.ReadName(path, name)) {
407
      exists = true;
408
      if (!name.empty()) {
409
        // Rename this component:
410
        //   "*/name/" => "*/Name/"
411
        //     ^slash       ^slash
412
        P.replace(slash + 1, next_slash - slash - 1, name);
413
        next_slash = slash + 1 + name.length();
414
      }
415
    } else if (Policy::Existence == Options::Existence::Required) {
416
      P = std::move(path);
417
      return Control::Error(status);
418
    }
419
  }
420
#endif
421
422
34
  if (Policy::Existence == Options::Existence::Required
423
#if defined(_WIN32) || defined(__APPLE__)
424
      && !exists
425
#endif
426
34
  ) {
427
0
    std::string path = P.substr(0, next_slash);
428
0
    if (!this->OS.PathExists(path)) {
429
0
      P = std::move(path);
430
0
      return Control::Error(cmsys::Status::POSIX(ENOENT));
431
0
    }
432
0
  }
433
434
  // Keep this component:
435
  //   "*/name/" => "*/name/"
436
  //     ^slash            ^slash
437
34
  return Control::Continue(next_slash);
438
34
}
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::RealPath>::ResolveComponent(cm::PathResolver::(anonymous namespace)::Root, unsigned long, unsigned long)
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::CasePath>::ResolveComponent(cm::PathResolver::(anonymous namespace)::Root, unsigned long, unsigned long)
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::NaivePath>::ResolveComponent(cm::PathResolver::(anonymous namespace)::Root, unsigned long, unsigned long)
439
440
template <class Policy>
441
Control Impl<Policy>::ResolvePath()
442
11
{
443
11
  Root const root = ClassifyRoot(P);
444
445
  // Resolve the root component.  It always ends in a slash.
446
11
  Control control = this->ResolveRoot(root);
447
11
  if (control.tag != Control::Tag::Continue) {
448
0
    return control;
449
0
  }
450
11
  std::string::size_type const root_slash = control.slash;
451
452
  // Resolve later components.  Every iteration that finishes
453
  // the loop body makes progress either by removing a component
454
  // or advancing the slash past it.
455
11
  for (std::string::size_type slash = root_slash;
456
45
       P.size() > root_slash + 1 && slash < P.size();) {
457
34
    control = this->ResolveComponent(root, root_slash, slash);
458
34
    if (control.tag != Control::Tag::Continue) {
459
0
      return control;
460
0
    }
461
34
    slash = control.slash;
462
34
  }
463
11
  return Control::Continue(P.size());
464
11
}
cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::LogicalPath>::ResolvePath()
Line
Count
Source
442
11
{
443
11
  Root const root = ClassifyRoot(P);
444
445
  // Resolve the root component.  It always ends in a slash.
446
11
  Control control = this->ResolveRoot(root);
447
11
  if (control.tag != Control::Tag::Continue) {
448
0
    return control;
449
0
  }
450
11
  std::string::size_type const root_slash = control.slash;
451
452
  // Resolve later components.  Every iteration that finishes
453
  // the loop body makes progress either by removing a component
454
  // or advancing the slash past it.
455
11
  for (std::string::size_type slash = root_slash;
456
45
       P.size() > root_slash + 1 && slash < P.size();) {
457
34
    control = this->ResolveComponent(root, root_slash, slash);
458
34
    if (control.tag != Control::Tag::Continue) {
459
0
      return control;
460
0
    }
461
34
    slash = control.slash;
462
34
  }
463
11
  return Control::Continue(P.size());
464
11
}
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::RealPath>::ResolvePath()
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::CasePath>::ResolvePath()
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::NaivePath>::ResolvePath()
465
466
template <class Policy>
467
cmsys::Status Impl<Policy>::Resolve(std::string in, std::string& out)
468
11
{
469
11
  P = std::move(in);
470
11
  std::replace(P.begin(), P.end(), '\\', '/');
471
11
  for (;;) {
472
11
    Control control = this->ResolvePath();
473
11
    switch (control.tag) {
474
11
      case Control::Tag::Continue:
475
11
        out = std::move(P);
476
11
        return cmsys::Status::Success();
477
0
      case Control::Tag::Restart:
478
0
        continue;
479
0
      case Control::Tag::Error:
480
0
        out = std::move(P);
481
0
        return control.error;
482
11
    };
483
0
  }
484
11
}
cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::LogicalPath>::Resolve(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)
Line
Count
Source
468
11
{
469
11
  P = std::move(in);
470
11
  std::replace(P.begin(), P.end(), '\\', '/');
471
11
  for (;;) {
472
11
    Control control = this->ResolvePath();
473
11
    switch (control.tag) {
474
11
      case Control::Tag::Continue:
475
11
        out = std::move(P);
476
11
        return cmsys::Status::Success();
477
0
      case Control::Tag::Restart:
478
0
        continue;
479
0
      case Control::Tag::Error:
480
0
        out = std::move(P);
481
0
        return control.error;
482
11
    };
483
0
  }
484
11
}
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::RealPath>::Resolve(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::CasePath>::Resolve(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)
Unexecuted instantiation: cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::NaivePath>::Resolve(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)
485
486
}
487
488
namespace Policies {
489
struct NaivePath
490
{
491
#if defined(_WIN32) || defined(__APPLE__)
492
  static constexpr Options::ActualCase ActualCase = Options::ActualCase::No;
493
#endif
494
  static constexpr Options::Symlinks Symlinks = Options::Symlinks::None;
495
  static constexpr Options::Existence Existence = Options::Existence::Agnostic;
496
};
497
struct CasePath
498
{
499
#if defined(_WIN32) || defined(__APPLE__)
500
  static constexpr Options::ActualCase ActualCase = Options::ActualCase::Yes;
501
#endif
502
  static constexpr Options::Symlinks Symlinks = Options::Symlinks::None;
503
  static constexpr Options::Existence Existence = Options::Existence::Agnostic;
504
};
505
struct RealPath
506
{
507
#if defined(_WIN32) || defined(__APPLE__)
508
  static constexpr Options::ActualCase ActualCase = Options::ActualCase::Yes;
509
#endif
510
  static constexpr Options::Symlinks Symlinks = Options::Symlinks::Eager;
511
  static constexpr Options::Existence Existence = Options::Existence::Required;
512
};
513
struct LogicalPath
514
{
515
#if defined(_WIN32) || defined(__APPLE__)
516
  static constexpr Options::ActualCase ActualCase = Options::ActualCase::Yes;
517
#endif
518
  static constexpr Options::Symlinks Symlinks = Options::Symlinks::Lazy;
519
  static constexpr Options::Existence Existence = Options::Existence::Agnostic;
520
};
521
522
#if defined(__SUNPRO_CC)
523
constexpr Options::Symlinks NaivePath::Symlinks;
524
constexpr Options::Existence NaivePath::Existence;
525
constexpr Options::Symlinks CasePath::Symlinks;
526
constexpr Options::Existence CasePath::Existence;
527
constexpr Options::Symlinks RealPath::Symlinks;
528
constexpr Options::Existence RealPath::Existence;
529
constexpr Options::Symlinks LogicalPath::Symlinks;
530
constexpr Options::Existence LogicalPath::Existence;
531
#endif
532
}
533
534
template <class Policy>
535
Resolver<Policy>::Resolver(System& os)
536
10
  : OS(os)
537
10
{
538
10
}
cm::PathResolver::Resolver<cm::PathResolver::Policies::LogicalPath>::Resolver(cm::PathResolver::System&)
Line
Count
Source
536
10
  : OS(os)
537
10
{
538
10
}
Unexecuted instantiation: cm::PathResolver::Resolver<cm::PathResolver::Policies::RealPath>::Resolver(cm::PathResolver::System&)
Unexecuted instantiation: cm::PathResolver::Resolver<cm::PathResolver::Policies::CasePath>::Resolver(cm::PathResolver::System&)
Unexecuted instantiation: cm::PathResolver::Resolver<cm::PathResolver::Policies::NaivePath>::Resolver(cm::PathResolver::System&)
539
template <class Policy>
540
cmsys::Status Resolver<Policy>::Resolve(std::string in, std::string& out) const
541
11
{
542
11
  return Impl<Policy>(OS).Resolve(std::move(in), out);
543
11
}
cm::PathResolver::Resolver<cm::PathResolver::Policies::LogicalPath>::Resolve(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) const
Line
Count
Source
541
11
{
542
11
  return Impl<Policy>(OS).Resolve(std::move(in), out);
543
11
}
Unexecuted instantiation: cm::PathResolver::Resolver<cm::PathResolver::Policies::RealPath>::Resolve(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) const
Unexecuted instantiation: cm::PathResolver::Resolver<cm::PathResolver::Policies::CasePath>::Resolve(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) const
Unexecuted instantiation: cm::PathResolver::Resolver<cm::PathResolver::Policies::NaivePath>::Resolve(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) const
544
545
10
System::System() = default;
546
0
System::~System() = default;
547
548
template class Resolver<Policies::LogicalPath>;
549
template class Resolver<Policies::RealPath>;
550
template class Resolver<Policies::CasePath>;
551
template class Resolver<Policies::NaivePath>;
552
553
}
554
}