| 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 | { |
| 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 | std::string::size_type next_slash = P.find('/', slash + 1); |
| 324 | if (next_slash == std::string::npos) { |
| 325 | next_slash = P.size(); |
| 326 | } |
| 327 | cm::string_view c = |
| 328 | cm::string_view(P).substr(slash + 1, next_slash - (slash + 1)); |
| 329 | |
| 330 | if (slash == root_slash) { |
| 331 | 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 | P.erase(slash + 1, next_slash - slash); |
| 340 | return Control::Continue(slash); |
| 341 | } |
| 342 | } else { |
| 343 | 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 | P.erase(slash, next_slash - slash); |
| 350 | return Control::Continue(slash); |
| 351 | } |
| 352 | |
| 353 | 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 | next_slash = slash; |
| 359 | slash = P.rfind('/', slash - 1); |
| 360 | |
| 361 | if (Policy::Symlinks == Options::Symlinks::Lazy) { |
| 362 | cmsys::Status status; |
| 363 | std::string path = P.substr(0, next_slash); |
| 364 | if (cm::optional<std::string> maybe_symlink_target = |
| 365 | this->ReadSymlink(path, status)) { |
| 366 | return this->ResolveSymlink(root, slash, next_slash, |
| 367 | std::move(*maybe_symlink_target)); |
| 368 | } |
| 369 | if (!status && Policy::Existence == Options::Existence::Required) { |
| 370 | P = std::move(path); |
| 371 | return Control::Error(status); |
| 372 | } |
| 373 | } |
no test coverage detected