/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 | | } |