Coverage Report

Created: 2026-02-09 06:05

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 <cctype>
17
18
#  include <windows.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 && std::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
      std::toupper(static_cast<unsigned char>(d[0])) ==
175
        std::toupper(static_cast<unsigned char>(letter)) &&
176
      d[1] == ':' && d[2] == '/') {
177
    d[0] = letter;
178
    d.push_back('/');
179
    return d;
180
  }
181
182
  // Use the current working directory if the drive matches.
183
  d = this->OS.GetWorkingDirectory();
184
  if (d.size() >= 3 &&
185
      std::toupper(static_cast<unsigned char>(d[0])) ==
186
        std::toupper(static_cast<unsigned char>(letter)) &&
187
      d[1] == ':' && d[2] == '/') {
188
    d[0] = letter;
189
    d.push_back('/');
190
    return d;
191
  }
192
193
  // Fall back to the root directory on the drive.
194
  d = "_:/";
195
  d[0] = letter;
196
  return d;
197
}
198
199
Control ImplBase::ResolveRootRelative()
200
{
201
  // This is a root-relative path.  Resolve the root drive and restart.
202
  P.replace(0, 2, this->GetWorkingDirectoryOnDrive(P[0]));
203
  return Control::Restart();
204
}
205
#endif
206
207
cm::optional<std::string> ImplBase::ReadSymlink(std::string const& path,
208
                                                cmsys::Status& status)
209
0
{
210
0
  cm::optional<std::string> result;
211
0
  std::string target;
212
0
  status = this->OS.ReadSymlink(path, target);
213
0
  if (status && ++this->SymlinkDepth >= MAX_SYMBOLIC_LINKS) {
214
0
    status = cmsys::Status::POSIX(ELOOP);
215
0
  }
216
0
  if (status) {
217
0
    if (!target.empty()) {
218
0
      result = std::move(target);
219
0
    }
220
0
  } else if (status.GetPOSIX() == EINVAL
221
#ifdef _WIN32
222
             || status.GetWindows() == ERROR_NOT_A_REPARSE_POINT
223
#endif
224
0
  ) {
225
    // The path was not a symlink.
226
0
    status = cmsys::Status::Success();
227
0
  }
228
0
  return result;
229
0
}
230
231
Control ImplBase::ResolveSymlink(Root root, std::string::size_type slash,
232
                                 std::string::size_type next_slash,
233
                                 std::string symlink_target)
234
0
{
235
0
  std::replace(symlink_target.begin(), symlink_target.end(), '\\', '/');
236
0
  Root const symlink_target_root = ClassifyRoot(symlink_target);
237
0
  if (symlink_target_root == Root::None) {
238
    // This is a symlink to a relative path.
239
    // Resolve the symlink, while preserving the leading and
240
    // trailing (if any) slash:
241
    //   "*/link/" => "*/dest/"
242
    //     ^slash       ^slash
243
0
    P.replace(slash + 1, next_slash - slash - 1, symlink_target);
244
0
    return Control::Continue(slash);
245
0
  }
246
247
#ifdef _WIN32
248
  if (root == Root::Drive && symlink_target_root == Root::POSIX) {
249
    // This is a symlink to a POSIX absolute path,
250
    // but the current path is on a drive letter.  Resolve the
251
    // symlink while preserving the drive letter, and start over:
252
    //   "C:/*/link/" => "C:/dest/"
253
    //        ^slash      (restart)
254
    P.replace(2, next_slash - 2, symlink_target);
255
    return Control::Restart();
256
  }
257
#else
258
0
  static_cast<void>(root);
259
0
#endif
260
261
  // This is a symlink to an absolute path.
262
  // Resolve it and start over:
263
  //   "*/link/" => "/dest/"
264
  //     ^slash      (restart)
265
0
  P.replace(0, next_slash, symlink_target);
266
0
  return Control::Restart();
267
0
}
268
269
template <class Policy>
270
Control Impl<Policy>::ResolveRoot(Root root)
271
11
{
272
11
  if (root == Root::None) {
273
0
    return this->ResolveRelativePath();
274
0
  }
275
276
  // POSIX absolute paths always start with a '/'.
277
11
  std::string::size_type root_slash = 0;
278
279
#ifdef _WIN32
280
  if (root == Root::Drive) {
281
    if (P.size() == 2 || P[2] != '/') {
282
      return this->ResolveRootRelative();
283
    }
284
285
    if (Policy::ActualCase == Options::ActualCase::Yes) {
286
      // Normalize the drive letter to upper-case.
287
      P[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(P[0])));
288
    }
289
290
    // The root is a drive letter.  The root '/' immediately follows.
291
    root_slash = 2;
292
  } else if (root == Root::Network) {
293
    // The root is a network name.  Find the root '/' after it.
294
    root_slash = P.find('/', 2);
295
    if (root_slash == std::string::npos) {
296
      root_slash = P.size();
297
      P.push_back('/');
298
    }
299
  }
300
#endif
301
302
11
  if (Policy::Existence == Options::Existence::Required
303
#ifdef _WIN32
304
      && root != Root::Network
305
#endif
306
11
  ) {
307
0
    std::string path = P.substr(0, root_slash + 1);
308
0
    if (!this->OS.PathExists(path)) {
309
0
      P = std::move(path);
310
0
      return Control::Error(cmsys::Status::POSIX(ENOENT));
311
0
    }
312
0
  }
313
314
11
  return Control::Continue(root_slash);
315
11
}
cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::LogicalPath>::ResolveRoot(cm::PathResolver::(anonymous namespace)::Root)
Line
Count
Source
271
11
{
272
11
  if (root == Root::None) {
273
0
    return this->ResolveRelativePath();
274
0
  }
275
276
  // POSIX absolute paths always start with a '/'.
277
11
  std::string::size_type root_slash = 0;
278
279
#ifdef _WIN32
280
  if (root == Root::Drive) {
281
    if (P.size() == 2 || P[2] != '/') {
282
      return this->ResolveRootRelative();
283
    }
284
285
    if (Policy::ActualCase == Options::ActualCase::Yes) {
286
      // Normalize the drive letter to upper-case.
287
      P[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(P[0])));
288
    }
289
290
    // The root is a drive letter.  The root '/' immediately follows.
291
    root_slash = 2;
292
  } else if (root == Root::Network) {
293
    // The root is a network name.  Find the root '/' after it.
294
    root_slash = P.find('/', 2);
295
    if (root_slash == std::string::npos) {
296
      root_slash = P.size();
297
      P.push_back('/');
298
    }
299
  }
300
#endif
301
302
11
  if (Policy::Existence == Options::Existence::Required
303
#ifdef _WIN32
304
      && root != Root::Network
305
#endif
306
11
  ) {
307
0
    std::string path = P.substr(0, root_slash + 1);
308
0
    if (!this->OS.PathExists(path)) {
309
0
      P = std::move(path);
310
0
      return Control::Error(cmsys::Status::POSIX(ENOENT));
311
0
    }
312
0
  }
313
314
11
  return Control::Continue(root_slash);
315
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)
316
317
template <class Policy>
318
Control Impl<Policy>::ResolveComponent(Root root,
319
                                       std::string::size_type root_slash,
320
                                       std::string::size_type slash)
321
34
{
322
  // Look for the '/' or end-of-input that ends this component.
323
  // The sample paths in comments below show the trailing slash
324
  // even if it is actually beyond the end of the path.
325
34
  std::string::size_type next_slash = P.find('/', slash + 1);
326
34
  if (next_slash == std::string::npos) {
327
11
    next_slash = P.size();
328
11
  }
329
34
  cm::string_view c =
330
34
    cm::string_view(P).substr(slash + 1, next_slash - (slash + 1));
331
332
34
  if (slash == root_slash) {
333
11
    if (c.empty() || c == "."_s || c == ".."_s) {
334
      // This is an empty, '.', or '..' component at the root.
335
      // Drop the component and its trailing slash, if any,
336
      // while preserving the root slash:
337
      //   "//"   => "/"
338
      //   "/./"  => "/"
339
      //   "/../" => "/"
340
      //    ^slash    ^slash
341
0
      P.erase(slash + 1, next_slash - slash);
342
0
      return Control::Continue(slash);
343
0
    }
344
23
  } else {
345
23
    if (c.empty() || c == "."_s) {
346
      // This is an empty or '.' component not at the root.
347
      // Drop the component and its leading slash:
348
      //   "*//"  => "*/"
349
      //   "*/./" => "*/"
350
      //     ^slash    ^slash
351
0
      P.erase(slash, next_slash - slash);
352
0
      return Control::Continue(slash);
353
0
    }
354
355
23
    if (c == ".."_s) {
356
      // This is a '..' component not at the root.
357
      // Rewind to the previous component:
358
      //   "*/prev/../" => "*/prev/../"
359
      //          ^slash     ^slash
360
0
      next_slash = slash;
361
0
      slash = P.rfind('/', slash - 1);
362
363
0
      if (Policy::Symlinks == Options::Symlinks::Lazy) {
364
0
        cmsys::Status status;
365
0
        std::string path = P.substr(0, next_slash);
366
0
        if (cm::optional<std::string> maybe_symlink_target =
367
0
              this->ReadSymlink(path, status)) {
368
0
          return this->ResolveSymlink(root, slash, next_slash,
369
0
                                      std::move(*maybe_symlink_target));
370
0
        }
371
0
        if (!status && Policy::Existence == Options::Existence::Required) {
372
0
          P = std::move(path);
373
0
          return Control::Error(status);
374
0
        }
375
0
      }
376
377
      // This is not a symlink.
378
      // Drop the component, the following '..', and its trailing slash,
379
      // if any, while preserving the (possibly root) leading slash:
380
      //   "*/dir/../" => "*/"
381
      //     ^slash         ^slash
382
0
      P.erase(slash + 1, next_slash + 3 - slash);
383
0
      return Control::Continue(slash);
384
0
    }
385
23
  }
386
387
  // This is a named component.
388
389
34
  if (Policy::Symlinks == Options::Symlinks::Eager) {
390
0
    cmsys::Status status;
391
0
    std::string path = P.substr(0, next_slash);
392
0
    if (cm::optional<std::string> maybe_symlink_target =
393
0
          this->ReadSymlink(path, status)) {
394
0
      return this->ResolveSymlink(root, slash, next_slash,
395
0
                                  std::move(*maybe_symlink_target));
396
0
    }
397
0
    if (!status && Policy::Existence == Options::Existence::Required) {
398
0
      P = std::move(path);
399
0
      return Control::Error(status);
400
0
    }
401
0
  }
402
403
#if defined(_WIN32) || defined(__APPLE__)
404
  bool exists = false;
405
  if (Policy::ActualCase == Options::ActualCase::Yes) {
406
    std::string name;
407
    std::string path = P.substr(0, next_slash);
408
    if (cmsys::Status status = this->OS.ReadName(path, name)) {
409
      exists = true;
410
      if (!name.empty()) {
411
        // Rename this component:
412
        //   "*/name/" => "*/Name/"
413
        //     ^slash       ^slash
414
        P.replace(slash + 1, next_slash - slash - 1, name);
415
        next_slash = slash + 1 + name.length();
416
      }
417
    } else if (Policy::Existence == Options::Existence::Required) {
418
      P = std::move(path);
419
      return Control::Error(status);
420
    }
421
  }
422
#endif
423
424
34
  if (Policy::Existence == Options::Existence::Required
425
#if defined(_WIN32) || defined(__APPLE__)
426
      && !exists
427
#endif
428
34
  ) {
429
0
    std::string path = P.substr(0, next_slash);
430
0
    if (!this->OS.PathExists(path)) {
431
0
      P = std::move(path);
432
0
      return Control::Error(cmsys::Status::POSIX(ENOENT));
433
0
    }
434
0
  }
435
436
  // Keep this component:
437
  //   "*/name/" => "*/name/"
438
  //     ^slash            ^slash
439
34
  return Control::Continue(next_slash);
440
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
321
34
{
322
  // Look for the '/' or end-of-input that ends this component.
323
  // The sample paths in comments below show the trailing slash
324
  // even if it is actually beyond the end of the path.
325
34
  std::string::size_type next_slash = P.find('/', slash + 1);
326
34
  if (next_slash == std::string::npos) {
327
11
    next_slash = P.size();
328
11
  }
329
34
  cm::string_view c =
330
34
    cm::string_view(P).substr(slash + 1, next_slash - (slash + 1));
331
332
34
  if (slash == root_slash) {
333
11
    if (c.empty() || c == "."_s || c == ".."_s) {
334
      // This is an empty, '.', or '..' component at the root.
335
      // Drop the component and its trailing slash, if any,
336
      // while preserving the root slash:
337
      //   "//"   => "/"
338
      //   "/./"  => "/"
339
      //   "/../" => "/"
340
      //    ^slash    ^slash
341
0
      P.erase(slash + 1, next_slash - slash);
342
0
      return Control::Continue(slash);
343
0
    }
344
23
  } else {
345
23
    if (c.empty() || c == "."_s) {
346
      // This is an empty or '.' component not at the root.
347
      // Drop the component and its leading slash:
348
      //   "*//"  => "*/"
349
      //   "*/./" => "*/"
350
      //     ^slash    ^slash
351
0
      P.erase(slash, next_slash - slash);
352
0
      return Control::Continue(slash);
353
0
    }
354
355
23
    if (c == ".."_s) {
356
      // This is a '..' component not at the root.
357
      // Rewind to the previous component:
358
      //   "*/prev/../" => "*/prev/../"
359
      //          ^slash     ^slash
360
0
      next_slash = slash;
361
0
      slash = P.rfind('/', slash - 1);
362
363
0
      if (Policy::Symlinks == Options::Symlinks::Lazy) {
364
0
        cmsys::Status status;
365
0
        std::string path = P.substr(0, next_slash);
366
0
        if (cm::optional<std::string> maybe_symlink_target =
367
0
              this->ReadSymlink(path, status)) {
368
0
          return this->ResolveSymlink(root, slash, next_slash,
369
0
                                      std::move(*maybe_symlink_target));
370
0
        }
371
0
        if (!status && Policy::Existence == Options::Existence::Required) {
372
0
          P = std::move(path);
373
0
          return Control::Error(status);
374
0
        }
375
0
      }
376
377
      // This is not a symlink.
378
      // Drop the component, the following '..', and its trailing slash,
379
      // if any, while preserving the (possibly root) leading slash:
380
      //   "*/dir/../" => "*/"
381
      //     ^slash         ^slash
382
0
      P.erase(slash + 1, next_slash + 3 - slash);
383
0
      return Control::Continue(slash);
384
0
    }
385
23
  }
386
387
  // This is a named component.
388
389
34
  if (Policy::Symlinks == Options::Symlinks::Eager) {
390
0
    cmsys::Status status;
391
0
    std::string path = P.substr(0, next_slash);
392
0
    if (cm::optional<std::string> maybe_symlink_target =
393
0
          this->ReadSymlink(path, status)) {
394
0
      return this->ResolveSymlink(root, slash, next_slash,
395
0
                                  std::move(*maybe_symlink_target));
396
0
    }
397
0
    if (!status && Policy::Existence == Options::Existence::Required) {
398
0
      P = std::move(path);
399
0
      return Control::Error(status);
400
0
    }
401
0
  }
402
403
#if defined(_WIN32) || defined(__APPLE__)
404
  bool exists = false;
405
  if (Policy::ActualCase == Options::ActualCase::Yes) {
406
    std::string name;
407
    std::string path = P.substr(0, next_slash);
408
    if (cmsys::Status status = this->OS.ReadName(path, name)) {
409
      exists = true;
410
      if (!name.empty()) {
411
        // Rename this component:
412
        //   "*/name/" => "*/Name/"
413
        //     ^slash       ^slash
414
        P.replace(slash + 1, next_slash - slash - 1, name);
415
        next_slash = slash + 1 + name.length();
416
      }
417
    } else if (Policy::Existence == Options::Existence::Required) {
418
      P = std::move(path);
419
      return Control::Error(status);
420
    }
421
  }
422
#endif
423
424
34
  if (Policy::Existence == Options::Existence::Required
425
#if defined(_WIN32) || defined(__APPLE__)
426
      && !exists
427
#endif
428
34
  ) {
429
0
    std::string path = P.substr(0, next_slash);
430
0
    if (!this->OS.PathExists(path)) {
431
0
      P = std::move(path);
432
0
      return Control::Error(cmsys::Status::POSIX(ENOENT));
433
0
    }
434
0
  }
435
436
  // Keep this component:
437
  //   "*/name/" => "*/name/"
438
  //     ^slash            ^slash
439
34
  return Control::Continue(next_slash);
440
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)
441
442
template <class Policy>
443
Control Impl<Policy>::ResolvePath()
444
11
{
445
11
  Root const root = ClassifyRoot(P);
446
447
  // Resolve the root component.  It always ends in a slash.
448
11
  Control control = this->ResolveRoot(root);
449
11
  if (control.tag != Control::Tag::Continue) {
450
0
    return control;
451
0
  }
452
11
  std::string::size_type const root_slash = control.slash;
453
454
  // Resolve later components.  Every iteration that finishes
455
  // the loop body makes progress either by removing a component
456
  // or advancing the slash past it.
457
11
  for (std::string::size_type slash = root_slash;
458
45
       P.size() > root_slash + 1 && slash < P.size();) {
459
34
    control = this->ResolveComponent(root, root_slash, slash);
460
34
    if (control.tag != Control::Tag::Continue) {
461
0
      return control;
462
0
    }
463
34
    slash = control.slash;
464
34
  }
465
11
  return Control::Continue(P.size());
466
11
}
cmPathResolver.cxx:cm::PathResolver::(anonymous namespace)::Impl<cm::PathResolver::Policies::LogicalPath>::ResolvePath()
Line
Count
Source
444
11
{
445
11
  Root const root = ClassifyRoot(P);
446
447
  // Resolve the root component.  It always ends in a slash.
448
11
  Control control = this->ResolveRoot(root);
449
11
  if (control.tag != Control::Tag::Continue) {
450
0
    return control;
451
0
  }
452
11
  std::string::size_type const root_slash = control.slash;
453
454
  // Resolve later components.  Every iteration that finishes
455
  // the loop body makes progress either by removing a component
456
  // or advancing the slash past it.
457
11
  for (std::string::size_type slash = root_slash;
458
45
       P.size() > root_slash + 1 && slash < P.size();) {
459
34
    control = this->ResolveComponent(root, root_slash, slash);
460
34
    if (control.tag != Control::Tag::Continue) {
461
0
      return control;
462
0
    }
463
34
    slash = control.slash;
464
34
  }
465
11
  return Control::Continue(P.size());
466
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()
467
468
template <class Policy>
469
cmsys::Status Impl<Policy>::Resolve(std::string in, std::string& out)
470
11
{
471
11
  P = std::move(in);
472
11
  std::replace(P.begin(), P.end(), '\\', '/');
473
11
  for (;;) {
474
11
    Control control = this->ResolvePath();
475
11
    switch (control.tag) {
476
11
      case Control::Tag::Continue:
477
11
        out = std::move(P);
478
11
        return cmsys::Status::Success();
479
0
      case Control::Tag::Restart:
480
0
        continue;
481
0
      case Control::Tag::Error:
482
0
        out = std::move(P);
483
0
        return control.error;
484
11
    };
485
0
  }
486
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
470
11
{
471
11
  P = std::move(in);
472
11
  std::replace(P.begin(), P.end(), '\\', '/');
473
11
  for (;;) {
474
11
    Control control = this->ResolvePath();
475
11
    switch (control.tag) {
476
11
      case Control::Tag::Continue:
477
11
        out = std::move(P);
478
11
        return cmsys::Status::Success();
479
0
      case Control::Tag::Restart:
480
0
        continue;
481
0
      case Control::Tag::Error:
482
0
        out = std::move(P);
483
0
        return control.error;
484
11
    };
485
0
  }
486
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> >&)
487
488
}
489
490
namespace Policies {
491
struct NaivePath
492
{
493
#if defined(_WIN32) || defined(__APPLE__)
494
  static constexpr Options::ActualCase ActualCase = Options::ActualCase::No;
495
#endif
496
  static constexpr Options::Symlinks Symlinks = Options::Symlinks::None;
497
  static constexpr Options::Existence Existence = Options::Existence::Agnostic;
498
};
499
struct CasePath
500
{
501
#if defined(_WIN32) || defined(__APPLE__)
502
  static constexpr Options::ActualCase ActualCase = Options::ActualCase::Yes;
503
#endif
504
  static constexpr Options::Symlinks Symlinks = Options::Symlinks::None;
505
  static constexpr Options::Existence Existence = Options::Existence::Agnostic;
506
};
507
struct RealPath
508
{
509
#if defined(_WIN32) || defined(__APPLE__)
510
  static constexpr Options::ActualCase ActualCase = Options::ActualCase::Yes;
511
#endif
512
  static constexpr Options::Symlinks Symlinks = Options::Symlinks::Eager;
513
  static constexpr Options::Existence Existence = Options::Existence::Required;
514
};
515
struct LogicalPath
516
{
517
#if defined(_WIN32) || defined(__APPLE__)
518
  static constexpr Options::ActualCase ActualCase = Options::ActualCase::Yes;
519
#endif
520
  static constexpr Options::Symlinks Symlinks = Options::Symlinks::Lazy;
521
  static constexpr Options::Existence Existence = Options::Existence::Agnostic;
522
};
523
524
#if defined(__SUNPRO_CC)
525
constexpr Options::Symlinks NaivePath::Symlinks;
526
constexpr Options::Existence NaivePath::Existence;
527
constexpr Options::Symlinks CasePath::Symlinks;
528
constexpr Options::Existence CasePath::Existence;
529
constexpr Options::Symlinks RealPath::Symlinks;
530
constexpr Options::Existence RealPath::Existence;
531
constexpr Options::Symlinks LogicalPath::Symlinks;
532
constexpr Options::Existence LogicalPath::Existence;
533
#endif
534
}
535
536
template <class Policy>
537
Resolver<Policy>::Resolver(System& os)
538
10
  : OS(os)
539
10
{
540
10
}
cm::PathResolver::Resolver<cm::PathResolver::Policies::LogicalPath>::Resolver(cm::PathResolver::System&)
Line
Count
Source
538
10
  : OS(os)
539
10
{
540
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&)
541
template <class Policy>
542
cmsys::Status Resolver<Policy>::Resolve(std::string in, std::string& out) const
543
11
{
544
11
  return Impl<Policy>(OS).Resolve(std::move(in), out);
545
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
543
11
{
544
11
  return Impl<Policy>(OS).Resolve(std::move(in), out);
545
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
546
547
10
System::System() = default;
548
0
System::~System() = default;
549
550
template class Resolver<Policies::LogicalPath>;
551
template class Resolver<Policies::RealPath>;
552
template class Resolver<Policies::CasePath>;
553
template class Resolver<Policies::NaivePath>;
554
555
}
556
}