1 // Filesystem operations -*- C++ -*- 2 3 // Copyright (C) 2014-2019 Free Software Foundation, Inc. 4 // 5 // This file is part of the GNU ISO C++ Library. This library is free 6 // software; you can redistribute it and/or modify it under the 7 // terms of the GNU General Public License as published by the 8 // Free Software Foundation; either version 3, or (at your option) 9 // any later version. 10 11 // This library is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 16 // Under Section 7 of GPL version 3, you are granted additional 17 // permissions described in the GCC Runtime Library Exception, version 18 // 3.1, as published by the Free Software Foundation. 19 20 // You should have received a copy of the GNU General Public License and 21 // a copy of the GCC Runtime Library Exception along with this program; 22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23 // <http://www.gnu.org/licenses/>. 24 25 #ifndef _GLIBCXX_USE_CXX11_ABI 26 # define _GLIBCXX_USE_CXX11_ABI 1 27 # define NEED_DO_COPY_FILE 28 # define NEED_DO_SPACE 29 #endif 30 31 #include <bits/largefile-config.h> 32 #include <filesystem> 33 #include <functional> 34 #include <ostream> 35 #include <stack> 36 #include <stdlib.h> 37 #include <stdio.h> 38 #include <errno.h> 39 #include <limits.h> // PATH_MAX 40 #ifdef _GLIBCXX_HAVE_FCNTL_H 41 # include <fcntl.h> // AT_FDCWD, AT_SYMLINK_NOFOLLOW 42 #endif 43 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 44 # include <sys/stat.h> // stat, utimensat, fchmodat 45 #endif 46 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H 47 # include <sys/statvfs.h> // statvfs 48 #endif 49 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H 50 # include <utime.h> // utime 51 #endif 52 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 53 # include <windows.h> 54 #endif 55 56 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem { 57 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } 58 #include "../filesystem/ops-common.h" 59 60 #pragma GCC diagnostic ignored "-Wunused-parameter" 61 62 namespace fs = std::filesystem; 63 namespace posix = std::filesystem::__gnu_posix; 64 65 fs::path 66 fs::absolute(const path& p) 67 { 68 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 69 error_code ec; 70 path ret = absolute(p, ec); 71 if (ec) 72 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p, 73 ec)); 74 return ret; 75 #else 76 if (p.empty()) 77 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p, 78 make_error_code(std::errc::invalid_argument))); 79 return current_path() / p; 80 #endif 81 } 82 83 fs::path 84 fs::absolute(const path& p, error_code& ec) 85 { 86 path ret; 87 if (p.empty()) 88 { 89 ec = make_error_code(std::errc::invalid_argument); 90 return ret; 91 } 92 ec.clear(); 93 if (p.is_absolute()) 94 { 95 ret = p; 96 return ret; 97 } 98 99 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 100 // s must remain null-terminated 101 wstring_view s = p.native(); 102 103 if (p.has_root_directory()) // implies !p.has_root_name() 104 { 105 // GetFullPathNameW("//") gives unwanted result (PR 88884). 106 // If there are multiple directory separators at the start, 107 // skip all but the last of them. 108 const auto pos = s.find_first_not_of(L"/\\"); 109 __glibcxx_assert(pos != 0); 110 s.remove_prefix(std::min(s.length(), pos) - 1); 111 } 112 113 uint32_t len = 1024; 114 wstring buf; 115 do 116 { 117 buf.resize(len); 118 len = GetFullPathNameW(s.data(), len, buf.data(), nullptr); 119 } 120 while (len > buf.size()); 121 122 if (len == 0) 123 ec.assign((int)GetLastError(), std::system_category()); 124 else 125 { 126 buf.resize(len); 127 ret = std::move(buf); 128 } 129 #else 130 ret = current_path(ec); 131 ret /= p; 132 #endif 133 return ret; 134 } 135 136 namespace 137 { 138 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 139 inline bool is_dot(wchar_t c) { return c == L'.'; } 140 #else 141 inline bool is_dot(char c) { return c == '.'; } 142 #endif 143 144 inline bool is_dot(const fs::path& path) 145 { 146 const auto& filename = path.native(); 147 return filename.size() == 1 && is_dot(filename[0]); 148 } 149 150 inline bool is_dotdot(const fs::path& path) 151 { 152 const auto& filename = path.native(); 153 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]); 154 } 155 156 struct free_as_in_malloc 157 { 158 void operator()(void* p) const { ::free(p); } 159 }; 160 161 using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>; 162 } 163 164 fs::path 165 fs::canonical(const path& p, error_code& ec) 166 { 167 path result; 168 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 169 const path pa = absolute(p.lexically_normal(), ec); 170 #else 171 const path pa = absolute(p, ec); 172 #endif 173 if (ec) 174 return result; 175 176 #ifdef _GLIBCXX_USE_REALPATH 177 char_ptr buf{ nullptr }; 178 # if _XOPEN_VERSION < 700 179 // Not safe to call realpath(path, NULL) 180 using char_type = fs::path::value_type; 181 buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) ); 182 # endif 183 if (char* rp = ::realpath(pa.c_str(), buf.get())) 184 { 185 if (buf == nullptr) 186 buf.reset(rp); 187 result.assign(rp); 188 ec.clear(); 189 return result; 190 } 191 if (errno != ENAMETOOLONG) 192 { 193 ec.assign(errno, std::generic_category()); 194 return result; 195 } 196 #endif 197 198 if (!exists(pa, ec)) 199 { 200 if (!ec) 201 ec = make_error_code(std::errc::no_such_file_or_directory); 202 return result; 203 } 204 // else: we know there are (currently) no unresolvable symlink loops 205 206 result = pa.root_path(); 207 208 deque<path> cmpts; 209 for (auto& f : pa.relative_path()) 210 cmpts.push_back(f); 211 212 int max_allowed_symlinks = 40; 213 214 while (!cmpts.empty() && !ec) 215 { 216 path f = std::move(cmpts.front()); 217 cmpts.pop_front(); 218 219 if (f.empty()) 220 { 221 // ignore empty element 222 } 223 else if (is_dot(f)) 224 { 225 if (!is_directory(result, ec) && !ec) 226 ec.assign(ENOTDIR, std::generic_category()); 227 } 228 else if (is_dotdot(f)) 229 { 230 auto parent = result.parent_path(); 231 if (parent.empty()) 232 result = pa.root_path(); 233 else 234 result.swap(parent); 235 } 236 else 237 { 238 result /= f; 239 240 if (is_symlink(result, ec)) 241 { 242 path link = read_symlink(result, ec); 243 if (!ec) 244 { 245 if (--max_allowed_symlinks == 0) 246 ec.assign(ELOOP, std::generic_category()); 247 else 248 { 249 if (link.is_absolute()) 250 { 251 result = link.root_path(); 252 link = link.relative_path(); 253 } 254 else 255 result = result.parent_path(); 256 257 cmpts.insert(cmpts.begin(), link.begin(), link.end()); 258 } 259 } 260 } 261 } 262 } 263 264 if (ec || !exists(result, ec)) 265 result.clear(); 266 267 return result; 268 } 269 270 fs::path 271 fs::canonical(const path& p) 272 { 273 error_code ec; 274 path res = canonical(p, ec); 275 if (ec) 276 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make canonical path", 277 p, ec)); 278 return res; 279 } 280 281 void 282 fs::copy(const path& from, const path& to, copy_options options) 283 { 284 error_code ec; 285 copy(from, to, options, ec); 286 if (ec) 287 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec)); 288 } 289 290 namespace std::filesystem 291 { 292 // Need this as there's no 'perm_options::none' enumerator. 293 static inline bool is_set(fs::perm_options obj, fs::perm_options bits) 294 { 295 return (obj & bits) != fs::perm_options{}; 296 } 297 } 298 299 namespace 300 { 301 struct internal_file_clock : fs::__file_clock 302 { 303 using __file_clock::_S_to_sys; 304 using __file_clock::_S_from_sys; 305 306 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 307 static fs::file_time_type 308 from_stat(const fs::stat_type& st, std::error_code& ec) noexcept 309 { 310 const auto sys_time = fs::file_time(st, ec); 311 if (sys_time == sys_time.min()) 312 return fs::file_time_type::min(); 313 return _S_from_sys(sys_time); 314 } 315 #endif 316 }; 317 } 318 319 void 320 fs::copy(const path& from, const path& to, copy_options options, 321 error_code& ec) 322 { 323 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 324 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks); 325 const bool create_symlinks = is_set(options, copy_options::create_symlinks); 326 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks); 327 const bool use_lstat = create_symlinks || skip_symlinks; 328 329 file_status f, t; 330 stat_type from_st, to_st; 331 // _GLIBCXX_RESOLVE_LIB_DEFECTS 332 // 2681. filesystem::copy() cannot copy symlinks 333 if (use_lstat || copy_symlinks 334 ? posix::lstat(from.c_str(), &from_st) 335 : posix::stat(from.c_str(), &from_st)) 336 { 337 ec.assign(errno, std::generic_category()); 338 return; 339 } 340 if (use_lstat 341 ? posix::lstat(to.c_str(), &to_st) 342 : posix::stat(to.c_str(), &to_st)) 343 { 344 if (!is_not_found_errno(errno)) 345 { 346 ec.assign(errno, std::generic_category()); 347 return; 348 } 349 t = file_status{file_type::not_found}; 350 } 351 else 352 t = make_file_status(to_st); 353 f = make_file_status(from_st); 354 355 if (exists(t) && !is_other(t) && !is_other(f) 356 && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino) 357 { 358 ec = std::make_error_code(std::errc::file_exists); 359 return; 360 } 361 if (is_other(f) || is_other(t)) 362 { 363 ec = std::make_error_code(std::errc::not_supported); 364 return; 365 } 366 if (is_directory(f) && is_regular_file(t)) 367 { 368 ec = std::make_error_code(std::errc::is_a_directory); 369 return; 370 } 371 372 if (is_symlink(f)) 373 { 374 if (skip_symlinks) 375 ec.clear(); 376 else if (!exists(t) && copy_symlinks) 377 copy_symlink(from, to, ec); 378 else 379 // Not clear what should be done here. 380 // "Otherwise report an error as specified in Error reporting (7)." 381 ec = std::make_error_code(std::errc::invalid_argument); 382 } 383 else if (is_regular_file(f)) 384 { 385 if (is_set(options, copy_options::directories_only)) 386 ec.clear(); 387 else if (create_symlinks) 388 create_symlink(from, to, ec); 389 else if (is_set(options, copy_options::create_hard_links)) 390 create_hard_link(from, to, ec); 391 else if (is_directory(t)) 392 do_copy_file(from.c_str(), (to / from.filename()).c_str(), 393 copy_file_options(options), &from_st, nullptr, ec); 394 else 395 { 396 auto ptr = exists(t) ? &to_st : &from_st; 397 do_copy_file(from.c_str(), to.c_str(), copy_file_options(options), 398 &from_st, ptr, ec); 399 } 400 } 401 // _GLIBCXX_RESOLVE_LIB_DEFECTS 402 // 2682. filesystem::copy() won't create a symlink to a directory 403 else if (is_directory(f) && create_symlinks) 404 ec = std::make_error_code(errc::is_a_directory); 405 else if (is_directory(f) && (is_set(options, copy_options::recursive) 406 || options == copy_options::none)) 407 { 408 if (!exists(t)) 409 if (!create_directory(to, from, ec)) 410 return; 411 // set an unused bit in options to disable further recursion 412 if (!is_set(options, copy_options::recursive)) 413 options |= static_cast<copy_options>(4096); 414 for (const directory_entry& x : directory_iterator(from)) 415 copy(x.path(), to/x.path().filename(), options, ec); 416 } 417 // _GLIBCXX_RESOLVE_LIB_DEFECTS 418 // 2683. filesystem::copy() says "no effects" 419 else 420 ec.clear(); 421 #else 422 ec = std::make_error_code(std::errc::not_supported); 423 #endif 424 } 425 426 bool 427 fs::copy_file(const path& from, const path& to, copy_options option) 428 { 429 error_code ec; 430 bool result = copy_file(from, to, option, ec); 431 if (ec) 432 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to, 433 ec)); 434 return result; 435 } 436 437 bool 438 fs::copy_file(const path& from, const path& to, copy_options options, 439 error_code& ec) 440 { 441 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 442 return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options), 443 nullptr, nullptr, ec); 444 #else 445 ec = std::make_error_code(std::errc::not_supported); 446 return false; 447 #endif 448 } 449 450 451 void 452 fs::copy_symlink(const path& existing_symlink, const path& new_symlink) 453 { 454 error_code ec; 455 copy_symlink(existing_symlink, new_symlink, ec); 456 if (ec) 457 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink", 458 existing_symlink, new_symlink, ec)); 459 } 460 461 void 462 fs::copy_symlink(const path& existing_symlink, const path& new_symlink, 463 error_code& ec) noexcept 464 { 465 auto p = read_symlink(existing_symlink, ec); 466 if (ec) 467 return; 468 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 469 if (is_directory(p)) 470 { 471 create_directory_symlink(p, new_symlink, ec); 472 return; 473 } 474 #endif 475 create_symlink(p, new_symlink, ec); 476 } 477 478 479 bool 480 fs::create_directories(const path& p) 481 { 482 error_code ec; 483 bool result = create_directories(p, ec); 484 if (ec) 485 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p, 486 ec)); 487 return result; 488 } 489 490 bool 491 fs::create_directories(const path& p, error_code& ec) 492 { 493 if (p.empty()) 494 { 495 ec = std::make_error_code(errc::invalid_argument); 496 return false; 497 } 498 499 file_status st = symlink_status(p, ec); 500 if (is_directory(st)) 501 return false; 502 else if (ec && !status_known(st)) 503 return false; 504 else if (exists(st)) 505 { 506 if (!ec) 507 ec = std::make_error_code(std::errc::not_a_directory); 508 return false; 509 } 510 511 __glibcxx_assert(st.type() == file_type::not_found); 512 // !exists(p) so there must be at least one non-existent component in p. 513 514 std::stack<path> missing; 515 path pp = p; 516 517 // Strip any trailing slash 518 if (pp.has_relative_path() && !pp.has_filename()) 519 pp = pp.parent_path(); 520 521 do 522 { 523 const auto& filename = pp.filename(); 524 if (is_dot(filename) || is_dotdot(filename)) 525 pp = pp.parent_path(); 526 else 527 { 528 missing.push(std::move(pp)); 529 if (missing.size() > 1000) // sanity check 530 { 531 ec = std::make_error_code(std::errc::filename_too_long); 532 return false; 533 } 534 pp = missing.top().parent_path(); 535 } 536 537 if (pp.empty()) 538 break; 539 540 st = status(pp, ec); 541 if (exists(st)) 542 { 543 if (ec) 544 return false; 545 if (!is_directory(st)) 546 { 547 ec = std::make_error_code(std::errc::not_a_directory); 548 return false; 549 } 550 } 551 552 if (ec && exists(st)) 553 return false; 554 } 555 while (st.type() == file_type::not_found); 556 557 __glibcxx_assert(!missing.empty()); 558 559 bool created; 560 do 561 { 562 const path& top = missing.top(); 563 created = create_directory(top, ec); 564 if (ec) 565 return false; 566 missing.pop(); 567 } 568 while (!missing.empty()); 569 570 return created; 571 } 572 573 namespace 574 { 575 bool 576 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec) 577 { 578 bool created = false; 579 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 580 posix::mode_t mode 581 = static_cast<std::underlying_type_t<fs::perms>>(perm); 582 if (posix::mkdir(p.c_str(), mode)) 583 { 584 const int err = errno; 585 if (err != EEXIST || !is_directory(p, ec)) 586 ec.assign(err, std::generic_category()); 587 } 588 else 589 { 590 ec.clear(); 591 created = true; 592 } 593 #else 594 ec = std::make_error_code(std::errc::not_supported); 595 #endif 596 return created; 597 } 598 } // namespace 599 600 bool 601 fs::create_directory(const path& p) 602 { 603 error_code ec; 604 bool result = create_directory(p, ec); 605 if (ec) 606 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, 607 ec)); 608 return result; 609 } 610 611 bool 612 fs::create_directory(const path& p, error_code& ec) noexcept 613 { 614 return create_dir(p, perms::all, ec); 615 } 616 617 618 bool 619 fs::create_directory(const path& p, const path& attributes) 620 { 621 error_code ec; 622 bool result = create_directory(p, attributes, ec); 623 if (ec) 624 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, 625 ec)); 626 return result; 627 } 628 629 bool 630 fs::create_directory(const path& p, const path& attributes, 631 error_code& ec) noexcept 632 { 633 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 634 stat_type st; 635 if (posix::stat(attributes.c_str(), &st)) 636 { 637 ec.assign(errno, std::generic_category()); 638 return false; 639 } 640 return create_dir(p, static_cast<perms>(st.st_mode), ec); 641 #else 642 ec = std::make_error_code(std::errc::not_supported); 643 return false; 644 #endif 645 } 646 647 648 void 649 fs::create_directory_symlink(const path& to, const path& new_symlink) 650 { 651 error_code ec; 652 create_directory_symlink(to, new_symlink, ec); 653 if (ec) 654 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink", 655 to, new_symlink, ec)); 656 } 657 658 void 659 fs::create_directory_symlink(const path& to, const path& new_symlink, 660 error_code& ec) noexcept 661 { 662 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 663 ec = std::make_error_code(std::errc::not_supported); 664 #else 665 create_symlink(to, new_symlink, ec); 666 #endif 667 } 668 669 670 void 671 fs::create_hard_link(const path& to, const path& new_hard_link) 672 { 673 error_code ec; 674 create_hard_link(to, new_hard_link, ec); 675 if (ec) 676 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link", 677 to, new_hard_link, ec)); 678 } 679 680 void 681 fs::create_hard_link(const path& to, const path& new_hard_link, 682 error_code& ec) noexcept 683 { 684 #ifdef _GLIBCXX_HAVE_LINK 685 if (::link(to.c_str(), new_hard_link.c_str())) 686 ec.assign(errno, std::generic_category()); 687 else 688 ec.clear(); 689 #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS 690 if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL)) 691 ec.clear(); 692 else 693 ec.assign((int)GetLastError(), generic_category()); 694 #else 695 ec = std::make_error_code(std::errc::not_supported); 696 #endif 697 } 698 699 void 700 fs::create_symlink(const path& to, const path& new_symlink) 701 { 702 error_code ec; 703 create_symlink(to, new_symlink, ec); 704 if (ec) 705 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink", 706 to, new_symlink, ec)); 707 } 708 709 void 710 fs::create_symlink(const path& to, const path& new_symlink, 711 error_code& ec) noexcept 712 { 713 #ifdef _GLIBCXX_HAVE_SYMLINK 714 if (::symlink(to.c_str(), new_symlink.c_str())) 715 ec.assign(errno, std::generic_category()); 716 else 717 ec.clear(); 718 #else 719 ec = std::make_error_code(std::errc::not_supported); 720 #endif 721 } 722 723 724 fs::path 725 fs::current_path() 726 { 727 error_code ec; 728 path p = current_path(ec); 729 if (ec) 730 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec)); 731 return p; 732 } 733 734 fs::path 735 fs::current_path(error_code& ec) 736 { 737 path p; 738 #ifdef _GLIBCXX_HAVE_UNISTD_H 739 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS 740 if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)}) 741 { 742 p.assign(cwd.get()); 743 ec.clear(); 744 } 745 else 746 ec.assign(errno, std::generic_category()); 747 #else 748 #ifdef _PC_PATH_MAX 749 long path_max = pathconf(".", _PC_PATH_MAX); 750 size_t size; 751 if (path_max == -1) 752 size = 1024; 753 else if (path_max > 10240) 754 size = 10240; 755 else 756 size = path_max; 757 #elif defined(PATH_MAX) 758 size_t size = PATH_MAX; 759 #else 760 size_t size = 1024; 761 #endif 762 for (char_ptr buf; p.empty(); size *= 2) 763 { 764 using char_type = fs::path::value_type; 765 buf.reset((char_type*)malloc(size * sizeof(char_type))); 766 if (buf) 767 { 768 if (getcwd(buf.get(), size)) 769 { 770 p.assign(buf.get()); 771 ec.clear(); 772 } 773 else if (errno != ERANGE) 774 { 775 ec.assign(errno, std::generic_category()); 776 return {}; 777 } 778 } 779 else 780 { 781 ec = std::make_error_code(std::errc::not_enough_memory); 782 return {}; 783 } 784 } 785 #endif // __GLIBC__ 786 #else // _GLIBCXX_HAVE_UNISTD_H 787 ec = std::make_error_code(std::errc::not_supported); 788 #endif 789 return p; 790 } 791 792 void 793 fs::current_path(const path& p) 794 { 795 error_code ec; 796 current_path(p, ec); 797 if (ec) 798 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec)); 799 } 800 801 void 802 fs::current_path(const path& p, error_code& ec) noexcept 803 { 804 #ifdef _GLIBCXX_HAVE_UNISTD_H 805 if (posix::chdir(p.c_str())) 806 ec.assign(errno, std::generic_category()); 807 else 808 ec.clear(); 809 #else 810 ec = std::make_error_code(std::errc::not_supported); 811 #endif 812 } 813 814 bool 815 fs::equivalent(const path& p1, const path& p2) 816 { 817 error_code ec; 818 auto result = equivalent(p1, p2, ec); 819 if (ec) 820 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence", 821 p1, p2, ec)); 822 return result; 823 } 824 825 bool 826 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept 827 { 828 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 829 int err = 0; 830 file_status s1, s2; 831 stat_type st1, st2; 832 if (posix::stat(p1.c_str(), &st1) == 0) 833 s1 = make_file_status(st1); 834 else if (is_not_found_errno(errno)) 835 s1.type(file_type::not_found); 836 else 837 err = errno; 838 839 if (posix::stat(p2.c_str(), &st2) == 0) 840 s2 = make_file_status(st2); 841 else if (is_not_found_errno(errno)) 842 s2.type(file_type::not_found); 843 else 844 err = errno; 845 846 if (exists(s1) && exists(s2)) 847 { 848 if (is_other(s1) && is_other(s2)) 849 { 850 ec = std::make_error_code(std::errc::not_supported); 851 return false; 852 } 853 ec.clear(); 854 if (is_other(s1) || is_other(s2)) 855 return false; 856 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 857 // st_ino is not set, so can't be used to distinguish files 858 if (st1.st_mode != st2.st_mode || st1.st_dev != st2.st_dev) 859 return false; 860 861 struct auto_handle { 862 explicit auto_handle(const path& p_) 863 : handle(CreateFileW(p_.c_str(), 0, 864 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 865 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)) 866 { } 867 868 ~auto_handle() 869 { if (*this) CloseHandle(handle); } 870 871 explicit operator bool() const 872 { return handle != INVALID_HANDLE_VALUE; } 873 874 bool get_info() 875 { return GetFileInformationByHandle(handle, &info); } 876 877 HANDLE handle; 878 BY_HANDLE_FILE_INFORMATION info; 879 }; 880 auto_handle h1(p1); 881 auto_handle h2(p2); 882 if (!h1 || !h2) 883 { 884 if (!h1 && !h2) 885 ec.assign((int)GetLastError(), generic_category()); 886 return false; 887 } 888 if (!h1.get_info() || !h2.get_info()) 889 { 890 ec.assign((int)GetLastError(), generic_category()); 891 return false; 892 } 893 return h1.info.dwVolumeSerialNumber == h2.info.dwVolumeSerialNumber 894 && h1.info.nFileIndexHigh == h2.info.nFileIndexHigh 895 && h1.info.nFileIndexLow == h2.info.nFileIndexLow; 896 #else 897 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino; 898 #endif 899 } 900 else if (!exists(s1) && !exists(s2)) 901 ec = std::make_error_code(std::errc::no_such_file_or_directory); 902 else if (err) 903 ec.assign(err, std::generic_category()); 904 else 905 ec.clear(); 906 return false; 907 #else 908 ec = std::make_error_code(std::errc::not_supported); 909 #endif 910 return false; 911 } 912 913 std::uintmax_t 914 fs::file_size(const path& p) 915 { 916 error_code ec; 917 auto sz = file_size(p, ec); 918 if (ec) 919 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec)); 920 return sz; 921 } 922 923 namespace 924 { 925 template<typename Accessor, typename T> 926 inline T 927 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt) 928 { 929 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 930 posix::stat_type st; 931 if (posix::stat(p.c_str(), &st)) 932 { 933 ec.assign(errno, std::generic_category()); 934 return deflt; 935 } 936 ec.clear(); 937 return f(st); 938 #else 939 ec = std::make_error_code(std::errc::not_supported); 940 return deflt; 941 #endif 942 } 943 } 944 945 std::uintmax_t 946 fs::file_size(const path& p, error_code& ec) noexcept 947 { 948 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 949 struct S 950 { 951 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { } 952 S() : type(file_type::not_found) { } 953 file_type type; 954 uintmax_t size; 955 }; 956 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{}); 957 if (s.type == file_type::regular) 958 return s.size; 959 if (!ec) 960 { 961 if (s.type == file_type::directory) 962 ec = std::make_error_code(std::errc::is_a_directory); 963 else 964 ec = std::make_error_code(std::errc::not_supported); 965 } 966 #else 967 ec = std::make_error_code(std::errc::not_supported); 968 #endif 969 return -1; 970 } 971 972 std::uintmax_t 973 fs::hard_link_count(const path& p) 974 { 975 error_code ec; 976 auto count = hard_link_count(p, ec); 977 if (ec) 978 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec)); 979 return count; 980 } 981 982 std::uintmax_t 983 fs::hard_link_count(const path& p, error_code& ec) noexcept 984 { 985 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 986 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink), 987 static_cast<uintmax_t>(-1)); 988 #else 989 ec = std::make_error_code(std::errc::not_supported); 990 return static_cast<uintmax_t>(-1); 991 #endif 992 } 993 994 bool 995 fs::is_empty(const path& p) 996 { 997 error_code ec; 998 bool e = is_empty(p, ec); 999 if (ec) 1000 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty", 1001 p, ec)); 1002 return e; 1003 } 1004 1005 bool 1006 fs::is_empty(const path& p, error_code& ec) 1007 { 1008 auto s = status(p, ec); 1009 if (ec) 1010 return false; 1011 bool empty = fs::is_directory(s) 1012 ? fs::directory_iterator(p, ec) == fs::directory_iterator() 1013 : fs::file_size(p, ec) == 0; 1014 return ec ? false : empty; 1015 } 1016 1017 fs::file_time_type 1018 fs::last_write_time(const path& p) 1019 { 1020 error_code ec; 1021 auto t = last_write_time(p, ec); 1022 if (ec) 1023 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec)); 1024 return t; 1025 } 1026 1027 fs::file_time_type 1028 fs::last_write_time(const path& p, error_code& ec) noexcept 1029 { 1030 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 1031 return do_stat(p, ec, 1032 [&ec](const auto& st) { 1033 return internal_file_clock::from_stat(st, ec); 1034 }, 1035 file_time_type::min()); 1036 #else 1037 ec = std::make_error_code(std::errc::not_supported); 1038 return file_time_type::min(); 1039 #endif 1040 } 1041 1042 void 1043 fs::last_write_time(const path& p, file_time_type new_time) 1044 { 1045 error_code ec; 1046 last_write_time(p, new_time, ec); 1047 if (ec) 1048 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec)); 1049 } 1050 1051 void 1052 fs::last_write_time(const path& p, 1053 file_time_type new_time, error_code& ec) noexcept 1054 { 1055 auto d = internal_file_clock::_S_to_sys(new_time).time_since_epoch(); 1056 auto s = chrono::duration_cast<chrono::seconds>(d); 1057 #if _GLIBCXX_USE_UTIMENSAT 1058 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s); 1059 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9. 1060 { 1061 --s; 1062 ns += chrono::seconds(1); 1063 } 1064 struct ::timespec ts[2]; 1065 ts[0].tv_sec = 0; 1066 ts[0].tv_nsec = UTIME_OMIT; 1067 ts[1].tv_sec = static_cast<std::time_t>(s.count()); 1068 ts[1].tv_nsec = static_cast<long>(ns.count()); 1069 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0)) 1070 ec.assign(errno, std::generic_category()); 1071 else 1072 ec.clear(); 1073 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H 1074 posix::utimbuf times; 1075 times.modtime = s.count(); 1076 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; }, 1077 times.modtime); 1078 if (posix::utime(p.c_str(), ×)) 1079 ec.assign(errno, std::generic_category()); 1080 else 1081 ec.clear(); 1082 #else 1083 ec = std::make_error_code(std::errc::not_supported); 1084 #endif 1085 } 1086 1087 void 1088 fs::permissions(const path& p, perms prms, perm_options opts) 1089 { 1090 error_code ec; 1091 permissions(p, prms, opts, ec); 1092 if (ec) 1093 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec)); 1094 } 1095 1096 void 1097 fs::permissions(const path& p, perms prms, perm_options opts, 1098 error_code& ec) noexcept 1099 { 1100 const bool replace = is_set(opts, perm_options::replace); 1101 const bool add = is_set(opts, perm_options::add); 1102 const bool remove = is_set(opts, perm_options::remove); 1103 const bool nofollow = is_set(opts, perm_options::nofollow); 1104 if (((int)replace + (int)add + (int)remove) != 1) 1105 { 1106 ec = std::make_error_code(std::errc::invalid_argument); 1107 return; 1108 } 1109 1110 prms &= perms::mask; 1111 1112 file_status st; 1113 if (add || remove || nofollow) 1114 { 1115 st = nofollow ? symlink_status(p, ec) : status(p, ec); 1116 if (ec) 1117 return; 1118 auto curr = st.permissions(); 1119 if (add) 1120 prms |= curr; 1121 else if (remove) 1122 prms = curr & ~prms; 1123 } 1124 1125 int err = 0; 1126 #if _GLIBCXX_USE_FCHMODAT 1127 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0; 1128 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag)) 1129 err = errno; 1130 #else 1131 if (nofollow && is_symlink(st)) 1132 ec = std::make_error_code(std::errc::not_supported); 1133 else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms))) 1134 err = errno; 1135 #endif 1136 1137 if (err) 1138 ec.assign(err, std::generic_category()); 1139 else 1140 ec.clear(); 1141 } 1142 1143 fs::path 1144 fs::proximate(const path& p, const path& base) 1145 { 1146 return weakly_canonical(p).lexically_proximate(weakly_canonical(base)); 1147 } 1148 1149 fs::path 1150 fs::proximate(const path& p, const path& base, error_code& ec) 1151 { 1152 path result; 1153 const auto p2 = weakly_canonical(p, ec); 1154 if (!ec) 1155 { 1156 const auto base2 = weakly_canonical(base, ec); 1157 if (!ec) 1158 result = p2.lexically_proximate(base2); 1159 } 1160 return result; 1161 } 1162 1163 fs::path 1164 fs::read_symlink(const path& p) 1165 { 1166 error_code ec; 1167 path tgt = read_symlink(p, ec); 1168 if (ec) 1169 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec)); 1170 return tgt; 1171 } 1172 1173 fs::path fs::read_symlink(const path& p, error_code& ec) 1174 { 1175 path result; 1176 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H) 1177 stat_type st; 1178 if (::lstat(p.c_str(), &st)) 1179 { 1180 ec.assign(errno, std::generic_category()); 1181 return result; 1182 } 1183 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0'); 1184 do 1185 { 1186 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size()); 1187 if (len == -1) 1188 { 1189 ec.assign(errno, std::generic_category()); 1190 return result; 1191 } 1192 else if (len == (ssize_t)buf.size()) 1193 { 1194 if (buf.size() > 4096) 1195 { 1196 ec.assign(ENAMETOOLONG, std::generic_category()); 1197 return result; 1198 } 1199 buf.resize(buf.size() * 2); 1200 } 1201 else 1202 { 1203 buf.resize(len); 1204 result.assign(buf); 1205 ec.clear(); 1206 break; 1207 } 1208 } 1209 while (true); 1210 #else 1211 ec = std::make_error_code(std::errc::not_supported); 1212 #endif 1213 return result; 1214 } 1215 1216 fs::path 1217 fs::relative(const path& p, const path& base) 1218 { 1219 return weakly_canonical(p).lexically_relative(weakly_canonical(base)); 1220 } 1221 1222 fs::path 1223 fs::relative(const path& p, const path& base, error_code& ec) 1224 { 1225 auto result = weakly_canonical(p, ec); 1226 fs::path cbase; 1227 if (!ec) 1228 cbase = weakly_canonical(base, ec); 1229 if (!ec) 1230 result = result.lexically_relative(cbase); 1231 if (ec) 1232 result.clear(); 1233 return result; 1234 } 1235 1236 bool 1237 fs::remove(const path& p) 1238 { 1239 error_code ec; 1240 const bool result = fs::remove(p, ec); 1241 if (ec) 1242 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec)); 1243 return result; 1244 } 1245 1246 bool 1247 fs::remove(const path& p, error_code& ec) noexcept 1248 { 1249 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1250 auto st = symlink_status(p, ec); 1251 if (exists(st)) 1252 { 1253 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str())) 1254 || DeleteFileW(p.c_str())) 1255 { 1256 ec.clear(); 1257 return true; 1258 } 1259 else if (!ec) 1260 ec.assign((int)GetLastError(), generic_category()); 1261 } 1262 else if (status_known(st)) 1263 ec.clear(); 1264 #else 1265 if (::remove(p.c_str()) == 0) 1266 { 1267 ec.clear(); 1268 return true; 1269 } 1270 else if (errno == ENOENT) 1271 ec.clear(); 1272 else 1273 ec.assign(errno, std::generic_category()); 1274 #endif 1275 return false; 1276 } 1277 1278 1279 std::uintmax_t 1280 fs::remove_all(const path& p) 1281 { 1282 error_code ec; 1283 const auto result = remove_all(p, ec); 1284 if (ec) 1285 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec)); 1286 return result; 1287 } 1288 1289 std::uintmax_t 1290 fs::remove_all(const path& p, error_code& ec) 1291 { 1292 const auto s = symlink_status(p, ec); 1293 if (!status_known(s)) 1294 return -1; 1295 1296 ec.clear(); 1297 if (s.type() == file_type::not_found) 1298 return 0; 1299 1300 uintmax_t count = 0; 1301 if (s.type() == file_type::directory) 1302 { 1303 directory_iterator d(p, ec), end; 1304 while (!ec && d != end) 1305 { 1306 const auto removed = fs::remove_all(d->path(), ec); 1307 if (removed == numeric_limits<uintmax_t>::max()) 1308 return -1; 1309 count += removed; 1310 d.increment(ec); 1311 if (ec) 1312 return -1; 1313 } 1314 } 1315 1316 if (fs::remove(p, ec)) 1317 ++count; 1318 return ec ? -1 : count; 1319 } 1320 1321 void 1322 fs::rename(const path& from, const path& to) 1323 { 1324 error_code ec; 1325 rename(from, to, ec); 1326 if (ec) 1327 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec)); 1328 } 1329 1330 void 1331 fs::rename(const path& from, const path& to, error_code& ec) noexcept 1332 { 1333 if (posix::rename(from.c_str(), to.c_str())) 1334 ec.assign(errno, std::generic_category()); 1335 else 1336 ec.clear(); 1337 } 1338 1339 void 1340 fs::resize_file(const path& p, uintmax_t size) 1341 { 1342 error_code ec; 1343 resize_file(p, size, ec); 1344 if (ec) 1345 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec)); 1346 } 1347 1348 void 1349 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept 1350 { 1351 if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max())) 1352 ec.assign(EINVAL, std::generic_category()); 1353 else if (posix::truncate(p.c_str(), size)) 1354 ec.assign(errno, std::generic_category()); 1355 else 1356 ec.clear(); 1357 } 1358 1359 1360 fs::space_info 1361 fs::space(const path& p) 1362 { 1363 error_code ec; 1364 space_info s = space(p, ec); 1365 if (ec) 1366 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec)); 1367 return s; 1368 } 1369 1370 fs::space_info 1371 fs::space(const path& p, error_code& ec) noexcept 1372 { 1373 space_info info = { 1374 static_cast<uintmax_t>(-1), 1375 static_cast<uintmax_t>(-1), 1376 static_cast<uintmax_t>(-1) 1377 }; 1378 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 1379 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1380 path dir = absolute(p); 1381 dir.remove_filename(); 1382 auto str = dir.c_str(); 1383 #else 1384 auto str = p.c_str(); 1385 #endif 1386 1387 do_space(str, info.capacity, info.free, info.available, ec); 1388 #endif // _GLIBCXX_HAVE_SYS_STAT_H 1389 1390 return info; 1391 } 1392 1393 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 1394 fs::file_status 1395 fs::status(const fs::path& p, error_code& ec) noexcept 1396 { 1397 file_status status; 1398 auto str = p.c_str(); 1399 1400 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1401 #if ! defined __MINGW64_VERSION_MAJOR || __MINGW64_VERSION_MAJOR < 6 1402 // stat() fails if there's a trailing slash (PR 88881) 1403 path p2; 1404 if (p.has_relative_path() && !p.has_filename()) 1405 { 1406 __try 1407 { 1408 p2 = p.parent_path(); 1409 str = p2.c_str(); 1410 } 1411 __catch(const bad_alloc&) 1412 { 1413 ec = std::make_error_code(std::errc::not_enough_memory); 1414 return status; 1415 } 1416 str = p2.c_str(); 1417 } 1418 #endif 1419 #endif 1420 1421 stat_type st; 1422 if (posix::stat(str, &st)) 1423 { 1424 int err = errno; 1425 ec.assign(err, std::generic_category()); 1426 if (is_not_found_errno(err)) 1427 status.type(file_type::not_found); 1428 #ifdef EOVERFLOW 1429 else if (err == EOVERFLOW) 1430 status.type(file_type::unknown); 1431 #endif 1432 } 1433 else 1434 { 1435 status = make_file_status(st); 1436 ec.clear(); 1437 } 1438 return status; 1439 } 1440 1441 fs::file_status 1442 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept 1443 { 1444 file_status status; 1445 auto str = p.c_str(); 1446 1447 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1448 #if ! defined __MINGW64_VERSION_MAJOR || __MINGW64_VERSION_MAJOR < 6 1449 // stat() fails if there's a trailing slash (PR 88881) 1450 path p2; 1451 if (p.has_relative_path() && !p.has_filename()) 1452 { 1453 __try 1454 { 1455 p2 = p.parent_path(); 1456 str = p2.c_str(); 1457 } 1458 __catch(const bad_alloc&) 1459 { 1460 ec = std::make_error_code(std::errc::not_enough_memory); 1461 return status; 1462 } 1463 str = p2.c_str(); 1464 } 1465 #endif 1466 #endif 1467 1468 stat_type st; 1469 if (posix::lstat(str, &st)) 1470 { 1471 int err = errno; 1472 ec.assign(err, std::generic_category()); 1473 if (is_not_found_errno(err)) 1474 status.type(file_type::not_found); 1475 } 1476 else 1477 { 1478 status = make_file_status(st); 1479 ec.clear(); 1480 } 1481 return status; 1482 } 1483 #endif 1484 1485 fs::file_status 1486 fs::status(const fs::path& p) 1487 { 1488 std::error_code ec; 1489 auto result = status(p, ec); 1490 if (result.type() == file_type::none) 1491 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec)); 1492 return result; 1493 } 1494 1495 fs::file_status 1496 fs::symlink_status(const fs::path& p) 1497 { 1498 std::error_code ec; 1499 auto result = symlink_status(p, ec); 1500 if (result.type() == file_type::none) 1501 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec)); 1502 return result; 1503 } 1504 1505 fs::path fs::temp_directory_path() 1506 { 1507 error_code ec; 1508 path tmp = temp_directory_path(ec); 1509 if (ec) 1510 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec)); 1511 return tmp; 1512 } 1513 1514 fs::path fs::temp_directory_path(error_code& ec) 1515 { 1516 path p; 1517 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1518 unsigned len = 1024; 1519 std::wstring buf; 1520 do 1521 { 1522 buf.resize(len); 1523 len = GetTempPathW(buf.size(), buf.data()); 1524 } while (len > buf.size()); 1525 1526 if (len == 0) 1527 { 1528 ec.assign((int)GetLastError(), std::system_category()); 1529 return p; 1530 } 1531 buf.resize(len); 1532 p = std::move(buf); 1533 #else 1534 const char* tmpdir = nullptr; 1535 const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr }; 1536 for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e) 1537 tmpdir = ::getenv(*e); 1538 p = tmpdir ? tmpdir : "/tmp"; 1539 #endif 1540 auto st = status(p, ec); 1541 if (ec) 1542 p.clear(); 1543 else if (!is_directory(st)) 1544 { 1545 p.clear(); 1546 ec = std::make_error_code(std::errc::not_a_directory); 1547 } 1548 return p; 1549 } 1550 1551 fs::path 1552 fs::weakly_canonical(const path& p) 1553 { 1554 path result; 1555 if (exists(status(p))) 1556 return canonical(p); 1557 1558 path tmp; 1559 auto iter = p.begin(), end = p.end(); 1560 // find leading elements of p that exist: 1561 while (iter != end) 1562 { 1563 tmp = result / *iter; 1564 if (exists(status(tmp))) 1565 swap(result, tmp); 1566 else 1567 break; 1568 ++iter; 1569 } 1570 // canonicalize: 1571 if (!result.empty()) 1572 result = canonical(result); 1573 // append the non-existing elements: 1574 while (iter != end) 1575 result /= *iter++; 1576 // normalize: 1577 return result.lexically_normal(); 1578 } 1579 1580 fs::path 1581 fs::weakly_canonical(const path& p, error_code& ec) 1582 { 1583 path result; 1584 file_status st = status(p, ec); 1585 if (exists(st)) 1586 return canonical(p, ec); 1587 else if (status_known(st)) 1588 ec.clear(); 1589 else 1590 return result; 1591 1592 path tmp; 1593 auto iter = p.begin(), end = p.end(); 1594 // find leading elements of p that exist: 1595 while (iter != end) 1596 { 1597 tmp = result / *iter; 1598 st = status(tmp, ec); 1599 if (exists(st)) 1600 swap(result, tmp); 1601 else 1602 { 1603 if (status_known(st)) 1604 ec.clear(); 1605 break; 1606 } 1607 ++iter; 1608 } 1609 // canonicalize: 1610 if (!ec && !result.empty()) 1611 result = canonical(result, ec); 1612 if (ec) 1613 result.clear(); 1614 else 1615 { 1616 // append the non-existing elements: 1617 while (iter != end) 1618 result /= *iter++; 1619 // normalize: 1620 result = result.lexically_normal(); 1621 } 1622 return result; 1623 } 1624