1 // Filesystem operations -*- C++ -*- 2 3 // Copyright (C) 2014-2022 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 #ifndef _GNU_SOURCE 31 // Cygwin needs this for secure_getenv 32 # define _GNU_SOURCE 1 33 #endif 34 35 #include <bits/largefile-config.h> 36 #include <filesystem> 37 #include <functional> 38 #include <ostream> 39 #include <stack> 40 #include <stdlib.h> 41 #include <stdio.h> 42 #include <errno.h> 43 #include <limits.h> // PATH_MAX 44 #ifdef _GLIBCXX_HAVE_FCNTL_H 45 # include <fcntl.h> // AT_FDCWD, AT_SYMLINK_NOFOLLOW 46 #endif 47 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 48 # include <sys/stat.h> // stat, utimensat, fchmodat 49 #endif 50 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H 51 # include <sys/statvfs.h> // statvfs 52 #endif 53 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H 54 # include <utime.h> // utime 55 #endif 56 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 57 # include <windows.h> 58 #endif 59 60 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem { 61 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } 62 #include "../filesystem/ops-common.h" 63 64 #pragma GCC diagnostic ignored "-Wunused-parameter" 65 66 namespace fs = std::filesystem; 67 namespace posix = std::filesystem::__gnu_posix; 68 69 fs::path 70 fs::absolute(const path& p) 71 { 72 error_code ec; 73 path ret = absolute(p, ec); 74 if (ec) 75 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p, 76 ec)); 77 return ret; 78 } 79 80 fs::path 81 fs::absolute(const path& p, error_code& ec) 82 { 83 path ret; 84 if (p.empty()) 85 { 86 ec = make_error_code(std::errc::invalid_argument); 87 return ret; 88 } 89 ec.clear(); 90 if (p.is_absolute()) 91 { 92 ret = p; 93 return ret; 94 } 95 96 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 97 // s must remain null-terminated 98 wstring_view s = p.native(); 99 100 if (p.has_root_directory()) // implies !p.has_root_name() 101 { 102 // GetFullPathNameW("//") gives unwanted result (PR 88884). 103 // If there are multiple directory separators at the start, 104 // skip all but the last of them. 105 const auto pos = s.find_first_not_of(L"/\\"); 106 __glibcxx_assert(pos != 0); 107 s.remove_prefix(std::min(s.length(), pos) - 1); 108 } 109 110 uint32_t len = 1024; 111 wstring buf; 112 do 113 { 114 buf.resize(len); 115 len = GetFullPathNameW(s.data(), len, buf.data(), nullptr); 116 } 117 while (len > buf.size()); 118 119 if (len == 0) 120 ec = __last_system_error(); 121 else 122 { 123 buf.resize(len); 124 ret = std::move(buf); 125 } 126 #else 127 ret = current_path(ec); 128 ret /= p; 129 #endif 130 return ret; 131 } 132 133 namespace 134 { 135 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 136 inline bool is_dot(wchar_t c) { return c == L'.'; } 137 #else 138 inline bool is_dot(char c) { return c == '.'; } 139 #endif 140 141 inline bool is_dot(const fs::path& path) 142 { 143 const auto& filename = path.native(); 144 return filename.size() == 1 && is_dot(filename[0]); 145 } 146 147 inline bool is_dotdot(const fs::path& path) 148 { 149 const auto& filename = path.native(); 150 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]); 151 } 152 153 struct free_as_in_malloc 154 { 155 void operator()(void* p) const { ::free(p); } 156 }; 157 158 using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>; 159 } 160 161 fs::path 162 fs::canonical(const path& p, error_code& ec) 163 { 164 path result; 165 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 166 const path pa = absolute(p.lexically_normal(), ec); 167 #else 168 const path pa = absolute(p, ec); 169 #endif 170 if (ec) 171 return result; 172 173 #ifdef _GLIBCXX_USE_REALPATH 174 char_ptr buf{ nullptr }; 175 # if _XOPEN_VERSION < 700 176 // Not safe to call realpath(path, NULL) 177 using char_type = fs::path::value_type; 178 buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) ); 179 # endif 180 if (char* rp = ::realpath(pa.c_str(), buf.get())) 181 { 182 if (buf == nullptr) 183 buf.reset(rp); 184 result.assign(rp); 185 ec.clear(); 186 return result; 187 } 188 if (errno != ENAMETOOLONG) 189 { 190 ec.assign(errno, std::generic_category()); 191 return result; 192 } 193 #endif 194 195 if (!exists(pa, ec)) 196 { 197 if (!ec) 198 ec = make_error_code(std::errc::no_such_file_or_directory); 199 return result; 200 } 201 // else: we know there are (currently) no unresolvable symlink loops 202 203 result = pa.root_path(); 204 205 deque<path> cmpts; 206 for (auto& f : pa.relative_path()) 207 cmpts.push_back(f); 208 209 int max_allowed_symlinks = 40; 210 211 while (!cmpts.empty() && !ec) 212 { 213 path f = std::move(cmpts.front()); 214 cmpts.pop_front(); 215 216 if (f.empty()) 217 { 218 // ignore empty element 219 } 220 else if (is_dot(f)) 221 { 222 if (!is_directory(result, ec) && !ec) 223 ec.assign(ENOTDIR, std::generic_category()); 224 } 225 else if (is_dotdot(f)) 226 { 227 auto parent = result.parent_path(); 228 if (parent.empty()) 229 result = pa.root_path(); 230 else 231 result.swap(parent); 232 } 233 else 234 { 235 result /= f; 236 237 if (is_symlink(result, ec)) 238 { 239 path link = read_symlink(result, ec); 240 if (!ec) 241 { 242 if (--max_allowed_symlinks == 0) 243 ec.assign(ELOOP, std::generic_category()); 244 else 245 { 246 if (link.is_absolute()) 247 { 248 result = link.root_path(); 249 link = link.relative_path(); 250 } 251 else 252 result = result.parent_path(); 253 254 cmpts.insert(cmpts.begin(), link.begin(), link.end()); 255 } 256 } 257 } 258 } 259 } 260 261 if (ec || !exists(result, ec)) 262 result.clear(); 263 264 return result; 265 } 266 267 fs::path 268 fs::canonical(const path& p) 269 { 270 error_code ec; 271 path res = canonical(p, ec); 272 if (ec) 273 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make canonical path", 274 p, ec)); 275 return res; 276 } 277 278 void 279 fs::copy(const path& from, const path& to, copy_options options) 280 { 281 error_code ec; 282 copy(from, to, options, ec); 283 if (ec) 284 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec)); 285 } 286 287 namespace std::filesystem 288 { 289 // Need this as there's no 'perm_options::none' enumerator. 290 static inline bool is_set(fs::perm_options obj, fs::perm_options bits) 291 { 292 return (obj & bits) != fs::perm_options{}; 293 } 294 } 295 296 namespace 297 { 298 struct internal_file_clock : fs::__file_clock 299 { 300 using __file_clock::_S_to_sys; 301 using __file_clock::_S_from_sys; 302 303 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 304 static fs::file_time_type 305 from_stat(const fs::stat_type& st, std::error_code& ec) noexcept 306 { 307 const auto sys_time = fs::file_time(st, ec); 308 if (sys_time == sys_time.min()) 309 return fs::file_time_type::min(); 310 return _S_from_sys(sys_time); 311 } 312 #endif 313 }; 314 } 315 316 void 317 fs::copy(const path& from, const path& to, copy_options options, 318 error_code& ec) 319 { 320 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 321 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks); 322 const bool create_symlinks = is_set(options, copy_options::create_symlinks); 323 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks); 324 const bool use_lstat = create_symlinks || skip_symlinks; 325 326 file_status f, t; 327 stat_type from_st, to_st; 328 // _GLIBCXX_RESOLVE_LIB_DEFECTS 329 // 2681. filesystem::copy() cannot copy symlinks 330 if (use_lstat || copy_symlinks 331 ? posix::lstat(from.c_str(), &from_st) 332 : posix::stat(from.c_str(), &from_st)) 333 { 334 ec.assign(errno, std::generic_category()); 335 return; 336 } 337 if (use_lstat 338 ? posix::lstat(to.c_str(), &to_st) 339 : posix::stat(to.c_str(), &to_st)) 340 { 341 if (!is_not_found_errno(errno)) 342 { 343 ec.assign(errno, std::generic_category()); 344 return; 345 } 346 t = file_status{file_type::not_found}; 347 } 348 else 349 t = make_file_status(to_st); 350 f = make_file_status(from_st); 351 352 if (exists(t) && !is_other(t) && !is_other(f) 353 && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino) 354 { 355 ec = std::make_error_code(std::errc::file_exists); 356 return; 357 } 358 if (is_other(f) || is_other(t)) 359 { 360 ec = std::make_error_code(std::errc::invalid_argument); 361 return; 362 } 363 if (is_directory(f) && is_regular_file(t)) 364 { 365 ec = std::make_error_code(std::errc::is_a_directory); 366 return; 367 } 368 369 if (is_symlink(f)) 370 { 371 if (skip_symlinks) 372 ec.clear(); 373 else if (!exists(t) && copy_symlinks) 374 copy_symlink(from, to, ec); 375 else 376 // Not clear what should be done here. 377 // "Otherwise report an error as specified in Error reporting (7)." 378 ec = std::make_error_code(std::errc::invalid_argument); 379 } 380 else if (is_regular_file(f)) 381 { 382 if (is_set(options, copy_options::directories_only)) 383 ec.clear(); 384 else if (create_symlinks) 385 create_symlink(from, to, ec); 386 else if (is_set(options, copy_options::create_hard_links)) 387 create_hard_link(from, to, ec); 388 else if (is_directory(t)) 389 do_copy_file(from.c_str(), (to / from.filename()).c_str(), 390 copy_file_options(options), &from_st, nullptr, ec); 391 else 392 { 393 auto ptr = exists(t) ? &to_st : &from_st; 394 do_copy_file(from.c_str(), to.c_str(), copy_file_options(options), 395 &from_st, ptr, ec); 396 } 397 } 398 // _GLIBCXX_RESOLVE_LIB_DEFECTS 399 // 2682. filesystem::copy() won't create a symlink to a directory 400 else if (is_directory(f) && create_symlinks) 401 ec = std::make_error_code(errc::is_a_directory); 402 else if (is_directory(f) && (is_set(options, copy_options::recursive) 403 || options == copy_options::none)) 404 { 405 if (!exists(t)) 406 if (!create_directory(to, from, ec)) 407 return; 408 // set an unused bit in options to disable further recursion 409 if (!is_set(options, copy_options::recursive)) 410 options |= static_cast<copy_options>(4096); 411 for (const directory_entry& x : directory_iterator(from, ec)) 412 { 413 copy(x.path(), to/x.path().filename(), options, ec); 414 if (ec) 415 return; 416 } 417 } 418 // _GLIBCXX_RESOLVE_LIB_DEFECTS 419 // 2683. filesystem::copy() says "no effects" 420 else 421 ec.clear(); 422 #else 423 ec = std::make_error_code(std::errc::function_not_supported); 424 #endif 425 } 426 427 bool 428 fs::copy_file(const path& from, const path& to, copy_options option) 429 { 430 error_code ec; 431 bool result = copy_file(from, to, option, ec); 432 if (ec) 433 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to, 434 ec)); 435 return result; 436 } 437 438 bool 439 fs::copy_file(const path& from, const path& to, copy_options options, 440 error_code& ec) 441 { 442 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 443 return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options), 444 nullptr, nullptr, ec); 445 #else 446 ec = std::make_error_code(std::errc::function_not_supported); 447 return false; 448 #endif 449 } 450 451 452 void 453 fs::copy_symlink(const path& existing_symlink, const path& new_symlink) 454 { 455 error_code ec; 456 copy_symlink(existing_symlink, new_symlink, ec); 457 if (ec) 458 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink", 459 existing_symlink, new_symlink, ec)); 460 } 461 462 void 463 fs::copy_symlink(const path& existing_symlink, const path& new_symlink, 464 error_code& ec) noexcept 465 { 466 auto p = read_symlink(existing_symlink, ec); 467 if (ec) 468 return; 469 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 470 if (is_directory(p)) 471 { 472 create_directory_symlink(p, new_symlink, ec); 473 return; 474 } 475 #endif 476 create_symlink(p, new_symlink, ec); 477 } 478 479 480 bool 481 fs::create_directories(const path& p) 482 { 483 error_code ec; 484 bool result = create_directories(p, ec); 485 if (ec) 486 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p, 487 ec)); 488 return result; 489 } 490 491 bool 492 fs::create_directories(const path& p, error_code& ec) 493 { 494 if (p.empty()) 495 { 496 ec = std::make_error_code(errc::invalid_argument); 497 return false; 498 } 499 500 file_status st = status(p, ec); 501 if (is_directory(st)) 502 return false; 503 else if (ec && !status_known(st)) 504 return false; 505 else if (exists(st)) 506 { 507 if (!ec) 508 ec = std::make_error_code(std::errc::not_a_directory); 509 return false; 510 } 511 512 __glibcxx_assert(st.type() == file_type::not_found); 513 // !exists(p) so there must be at least one non-existent component in p. 514 515 std::stack<path> missing; 516 path pp = p; 517 518 // Strip any trailing slash 519 if (pp.has_relative_path() && !pp.has_filename()) 520 pp = pp.parent_path(); 521 522 do 523 { 524 const auto& filename = pp.filename(); 525 if (is_dot(filename) || is_dotdot(filename)) 526 pp = pp.parent_path(); 527 else 528 { 529 missing.push(std::move(pp)); 530 if (missing.size() > 1000) // sanity check 531 { 532 ec = std::make_error_code(std::errc::filename_too_long); 533 return false; 534 } 535 pp = missing.top().parent_path(); 536 } 537 538 if (pp.empty()) 539 break; 540 541 st = status(pp, ec); 542 if (exists(st)) 543 { 544 if (ec) 545 return false; 546 if (!is_directory(st)) 547 { 548 ec = std::make_error_code(std::errc::not_a_directory); 549 return false; 550 } 551 } 552 553 if (ec && exists(st)) 554 return false; 555 } 556 while (st.type() == file_type::not_found); 557 558 __glibcxx_assert(!missing.empty()); 559 560 bool created; 561 do 562 { 563 const path& top = missing.top(); 564 created = create_directory(top, ec); 565 if (ec) 566 return false; 567 missing.pop(); 568 } 569 while (!missing.empty()); 570 571 return created; 572 } 573 574 namespace 575 { 576 bool 577 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec) 578 { 579 bool created = false; 580 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 581 posix::mode_t mode = 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::function_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::function_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::function_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 = __last_system_error(); 694 #else 695 ec = std::make_error_code(std::errc::function_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::function_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 #if defined _GLIBCXX_HAVE_UNISTD_H && ! defined __AVR__ 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::function_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::function_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::__unsupported(); 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 = __last_system_error(); 886 return false; 887 } 888 if (!h1.get_info() || !h2.get_info()) 889 { 890 ec = __last_system_error(); 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::function_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::function_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::__unsupported(); 965 } 966 #else 967 ec = std::make_error_code(std::errc::function_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::function_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::function_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::function_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::__unsupported(); 1133 else if (posix::chmod(p.c_str(), static_cast<posix::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 (posix::lstat(p.c_str(), &st)) 1179 { 1180 ec.assign(errno, std::generic_category()); 1181 return result; 1182 } 1183 else if (!fs::is_symlink(make_file_status(st))) 1184 { 1185 ec.assign(EINVAL, std::generic_category()); 1186 return result; 1187 } 1188 1189 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0'); 1190 do 1191 { 1192 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size()); 1193 if (len == -1) 1194 { 1195 ec.assign(errno, std::generic_category()); 1196 return result; 1197 } 1198 else if (len == (ssize_t)buf.size()) 1199 { 1200 if (buf.size() > 4096) 1201 { 1202 ec.assign(ENAMETOOLONG, std::generic_category()); 1203 return result; 1204 } 1205 buf.resize(buf.size() * 2); 1206 } 1207 else 1208 { 1209 buf.resize(len); 1210 result.assign(buf); 1211 ec.clear(); 1212 break; 1213 } 1214 } 1215 while (true); 1216 #else 1217 ec = std::make_error_code(std::errc::function_not_supported); 1218 #endif 1219 return result; 1220 } 1221 1222 fs::path 1223 fs::relative(const path& p, const path& base) 1224 { 1225 return weakly_canonical(p).lexically_relative(weakly_canonical(base)); 1226 } 1227 1228 fs::path 1229 fs::relative(const path& p, const path& base, error_code& ec) 1230 { 1231 auto result = weakly_canonical(p, ec); 1232 fs::path cbase; 1233 if (!ec) 1234 cbase = weakly_canonical(base, ec); 1235 if (!ec) 1236 result = result.lexically_relative(cbase); 1237 if (ec) 1238 result.clear(); 1239 return result; 1240 } 1241 1242 bool 1243 fs::remove(const path& p) 1244 { 1245 error_code ec; 1246 const bool result = fs::remove(p, ec); 1247 if (ec) 1248 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec)); 1249 return result; 1250 } 1251 1252 bool 1253 fs::remove(const path& p, error_code& ec) noexcept 1254 { 1255 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1256 auto st = symlink_status(p, ec); 1257 if (exists(st)) 1258 { 1259 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str())) 1260 || DeleteFileW(p.c_str())) 1261 { 1262 ec.clear(); 1263 return true; 1264 } 1265 else if (!ec) 1266 ec = __last_system_error(); 1267 } 1268 else if (status_known(st)) 1269 ec.clear(); 1270 #else 1271 if (::remove(p.c_str()) == 0) 1272 { 1273 ec.clear(); 1274 return true; 1275 } 1276 else if (errno == ENOENT) 1277 ec.clear(); 1278 else 1279 ec.assign(errno, std::generic_category()); 1280 #endif 1281 return false; 1282 } 1283 1284 std::uintmax_t 1285 fs::remove_all(const path& p) 1286 { 1287 error_code ec; 1288 uintmax_t count = 0; 1289 recursive_directory_iterator dir(p, directory_options{64|128}, ec); 1290 switch (ec.value()) // N.B. assumes ec.category() == std::generic_category() 1291 { 1292 case 0: 1293 // Iterate over the directory removing everything. 1294 { 1295 const recursive_directory_iterator end; 1296 while (dir != end) 1297 { 1298 dir.__erase(); // throws on error 1299 ++count; 1300 } 1301 } 1302 // Directory is empty now, will remove it below. 1303 break; 1304 #ifndef __AVR__ 1305 case ENOENT: 1306 // Our work here is done. 1307 return 0; 1308 case ENOTDIR: 1309 case ELOOP: 1310 // Not a directory, will remove below. 1311 break; 1312 #endif 1313 default: 1314 // An error occurred. 1315 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec)); 1316 } 1317 1318 // Remove p itself, which is either a non-directory or is now empty. 1319 return count + fs::remove(p); 1320 } 1321 1322 std::uintmax_t 1323 fs::remove_all(const path& p, error_code& ec) 1324 { 1325 uintmax_t count = 0; 1326 recursive_directory_iterator dir(p, directory_options{64|128}, ec); 1327 switch (ec.value()) // N.B. assumes ec.category() == std::generic_category() 1328 { 1329 case 0: 1330 // Iterate over the directory removing everything. 1331 { 1332 const recursive_directory_iterator end; 1333 while (dir != end) 1334 { 1335 dir.__erase(&ec); 1336 if (ec) 1337 return -1; 1338 ++count; 1339 } 1340 } 1341 // Directory is empty now, will remove it below. 1342 break; 1343 #ifndef __AVR__ 1344 case ENOENT: 1345 // Our work here is done. 1346 ec.clear(); 1347 return 0; 1348 case ENOTDIR: 1349 case ELOOP: 1350 // Not a directory, will remove below. 1351 break; 1352 #endif 1353 default: 1354 // An error occurred. 1355 return -1; 1356 } 1357 1358 // Remove p itself, which is either a non-directory or is now empty. 1359 if (int last = fs::remove(p, ec); !ec) 1360 return count + last; 1361 return -1; 1362 } 1363 1364 void 1365 fs::rename(const path& from, const path& to) 1366 { 1367 error_code ec; 1368 rename(from, to, ec); 1369 if (ec) 1370 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec)); 1371 } 1372 1373 void 1374 fs::rename(const path& from, const path& to, error_code& ec) noexcept 1375 { 1376 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1377 const auto to_status = fs::status(to, ec); 1378 if (to_status.type() == file_type::not_found) 1379 ec.clear(); 1380 else if (ec) 1381 return; 1382 1383 if (fs::exists(to_status)) 1384 { 1385 const auto from_status = fs::status(from, ec); 1386 if (ec) 1387 return; 1388 1389 if (fs::is_directory(to_status)) 1390 { 1391 if (!fs::is_directory(from_status)) 1392 { 1393 // Cannot rename a non-directory over an existing directory. 1394 ec = std::make_error_code(std::errc::is_a_directory); 1395 return; 1396 } 1397 } 1398 else if (fs::is_directory(from_status)) 1399 { 1400 // Cannot rename a directory over an existing non-directory. 1401 ec = std::make_error_code(std::errc::not_a_directory); 1402 return; 1403 } 1404 } 1405 #endif 1406 if (posix::rename(from.c_str(), to.c_str())) 1407 ec.assign(errno, std::generic_category()); 1408 else 1409 ec.clear(); 1410 } 1411 1412 void 1413 fs::resize_file(const path& p, uintmax_t size) 1414 { 1415 error_code ec; 1416 resize_file(p, size, ec); 1417 if (ec) 1418 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec)); 1419 } 1420 1421 void 1422 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept 1423 { 1424 if (size > static_cast<uintmax_t>(std::numeric_limits<posix::off_t>::max())) 1425 ec.assign(EINVAL, std::generic_category()); 1426 else if (posix::truncate(p.c_str(), size)) 1427 ec.assign(errno, std::generic_category()); 1428 else 1429 ec.clear(); 1430 } 1431 1432 1433 fs::space_info 1434 fs::space(const path& p) 1435 { 1436 error_code ec; 1437 space_info s = space(p, ec); 1438 if (ec) 1439 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec)); 1440 return s; 1441 } 1442 1443 fs::space_info 1444 fs::space(const path& p, error_code& ec) noexcept 1445 { 1446 space_info info = { 1447 static_cast<uintmax_t>(-1), 1448 static_cast<uintmax_t>(-1), 1449 static_cast<uintmax_t>(-1) 1450 }; 1451 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 1452 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1453 path dir = absolute(p); 1454 dir.remove_filename(); 1455 auto str = dir.c_str(); 1456 #else 1457 auto str = p.c_str(); 1458 #endif 1459 1460 do_space(str, info.capacity, info.free, info.available, ec); 1461 #endif // _GLIBCXX_HAVE_SYS_STAT_H 1462 1463 return info; 1464 } 1465 1466 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 1467 fs::file_status 1468 fs::status(const fs::path& p, error_code& ec) noexcept 1469 { 1470 file_status status; 1471 auto str = p.c_str(); 1472 1473 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1474 // stat() fails if there's a trailing slash (PR 88881) 1475 path p2; 1476 if (p.has_relative_path() && !p.has_filename()) 1477 { 1478 __try 1479 { 1480 p2 = p.parent_path(); 1481 str = p2.c_str(); 1482 } 1483 __catch(const bad_alloc&) 1484 { 1485 ec = std::make_error_code(std::errc::not_enough_memory); 1486 return status; 1487 } 1488 str = p2.c_str(); 1489 } 1490 #endif 1491 1492 stat_type st; 1493 if (posix::stat(str, &st)) 1494 { 1495 int err = errno; 1496 ec.assign(err, std::generic_category()); 1497 if (is_not_found_errno(err)) 1498 status.type(file_type::not_found); 1499 #ifdef EOVERFLOW 1500 else if (err == EOVERFLOW) 1501 status.type(file_type::unknown); 1502 #endif 1503 } 1504 else 1505 { 1506 status = make_file_status(st); 1507 ec.clear(); 1508 } 1509 return status; 1510 } 1511 1512 fs::file_status 1513 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept 1514 { 1515 file_status status; 1516 auto str = p.c_str(); 1517 1518 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1519 // stat() fails if there's a trailing slash (PR 88881) 1520 path p2; 1521 if (p.has_relative_path() && !p.has_filename()) 1522 { 1523 __try 1524 { 1525 p2 = p.parent_path(); 1526 str = p2.c_str(); 1527 } 1528 __catch(const bad_alloc&) 1529 { 1530 ec = std::make_error_code(std::errc::not_enough_memory); 1531 return status; 1532 } 1533 str = p2.c_str(); 1534 } 1535 #endif 1536 1537 stat_type st; 1538 if (posix::lstat(str, &st)) 1539 { 1540 int err = errno; 1541 ec.assign(err, std::generic_category()); 1542 if (is_not_found_errno(err)) 1543 status.type(file_type::not_found); 1544 } 1545 else 1546 { 1547 status = make_file_status(st); 1548 ec.clear(); 1549 } 1550 return status; 1551 } 1552 #endif 1553 1554 fs::file_status 1555 fs::status(const fs::path& p) 1556 { 1557 std::error_code ec; 1558 auto result = status(p, ec); 1559 if (result.type() == file_type::none) 1560 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec)); 1561 return result; 1562 } 1563 1564 fs::file_status 1565 fs::symlink_status(const fs::path& p) 1566 { 1567 std::error_code ec; 1568 auto result = symlink_status(p, ec); 1569 if (result.type() == file_type::none) 1570 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec)); 1571 return result; 1572 } 1573 1574 fs::path 1575 fs::temp_directory_path() 1576 { 1577 error_code ec; 1578 path tmp = temp_directory_path(ec); 1579 if (ec) 1580 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec)); 1581 return tmp; 1582 } 1583 1584 fs::path 1585 fs::temp_directory_path(error_code& ec) 1586 { 1587 path p = fs::get_temp_directory_from_env(ec); 1588 if (ec) 1589 return p; 1590 auto st = status(p, ec); 1591 if (ec) 1592 p.clear(); 1593 else if (!is_directory(st)) 1594 { 1595 p.clear(); 1596 ec = std::make_error_code(std::errc::not_a_directory); 1597 } 1598 return p; 1599 } 1600 1601 fs::path 1602 fs::weakly_canonical(const path& p) 1603 { 1604 path result; 1605 if (exists(status(p))) 1606 return canonical(p); 1607 1608 path tmp; 1609 auto iter = p.begin(), end = p.end(); 1610 // find leading elements of p that exist: 1611 while (iter != end) 1612 { 1613 tmp = result / *iter; 1614 if (exists(status(tmp))) 1615 swap(result, tmp); 1616 else 1617 break; 1618 ++iter; 1619 } 1620 // canonicalize: 1621 if (!result.empty()) 1622 result = canonical(result); 1623 // append the non-existing elements: 1624 while (iter != end) 1625 result /= *iter++; 1626 // normalize: 1627 return result.lexically_normal(); 1628 } 1629 1630 fs::path 1631 fs::weakly_canonical(const path& p, error_code& ec) 1632 { 1633 path result; 1634 file_status st = status(p, ec); 1635 if (exists(st)) 1636 return canonical(p, ec); 1637 else if (status_known(st)) 1638 ec.clear(); 1639 else 1640 return result; 1641 1642 path tmp; 1643 auto iter = p.begin(), end = p.end(); 1644 // find leading elements of p that exist: 1645 while (iter != end) 1646 { 1647 tmp = result / *iter; 1648 st = status(tmp, ec); 1649 if (exists(st)) 1650 swap(result, tmp); 1651 else 1652 { 1653 if (status_known(st)) 1654 ec.clear(); 1655 break; 1656 } 1657 ++iter; 1658 } 1659 // canonicalize: 1660 if (!ec && !result.empty()) 1661 result = canonical(result, ec); 1662 if (ec) 1663 result.clear(); 1664 else 1665 { 1666 // append the non-existing elements: 1667 while (iter != end) 1668 result /= *iter++; 1669 // normalize: 1670 result = result.lexically_normal(); 1671 } 1672 return result; 1673 } 1674