1 // Class filesystem::path -*- 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 #endif 28 29 #ifdef __CYGWIN__ 30 // Interpret "//x" as a root-name, not root-dir + filename 31 # define SLASHSLASH_IS_ROOTNAME 1 32 #endif 33 34 #include <filesystem> 35 #include <algorithm> 36 #include <array> 37 #include <bits/stl_uninitialized.h> 38 39 namespace fs = std::filesystem; 40 using fs::path; 41 42 static inline bool is_dir_sep(path::value_type ch) 43 { 44 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 45 return ch == L'/' || ch == path::preferred_separator; 46 #else 47 return ch == '/'; 48 #endif 49 } 50 51 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 52 static inline bool is_disk_designator(std::wstring_view s) 53 { 54 return s.length() == 2 && s[1] == L':'; 55 } 56 #endif 57 58 struct path::_Parser 59 { 60 using string_view_type = std::basic_string_view<value_type>; 61 62 struct cmpt 63 { 64 string_view_type str; 65 _Type type = _Type::_Multi; 66 67 bool valid() const { return type != _Type::_Multi; } 68 }; 69 70 string_view_type input; 71 string_view_type::size_type pos = 0; 72 size_t origin; 73 _Type last_type = _Type::_Multi; 74 75 _Parser(string_view_type s, size_t o = 0) : input(s), origin(o) { } 76 77 pair<cmpt, cmpt> root_path() noexcept 78 { 79 pos = 0; 80 pair<cmpt, cmpt> root; 81 82 const size_t len = input.size(); 83 84 // look for root name or root directory 85 if (len && is_dir_sep(input[0])) 86 { 87 #if SLASHSLASH_IS_ROOTNAME 88 // look for root name, such as "//foo" 89 if (len > 2 && input[1] == input[0]) 90 { 91 if (!is_dir_sep(input[2])) 92 { 93 // got root name, find its end 94 pos = 3; 95 while (pos < len && !is_dir_sep(input[pos])) 96 ++pos; 97 root.first.str = input.substr(0, pos); 98 root.first.type = _Type::_Root_name; 99 100 if (pos < len) // also got root directory 101 { 102 root.second.str = input.substr(pos, 1); 103 root.second.type = _Type::_Root_dir; 104 ++pos; 105 } 106 } 107 else 108 { 109 // got something like "///foo" which is just a root directory 110 // composed of multiple redundant directory separators 111 root.first.str = input.substr(0, 1); 112 root.first.type = _Type::_Root_dir; 113 pos += 2; 114 } 115 } 116 else 117 #endif 118 { 119 root.first.str = input.substr(0, 1); 120 root.first.type = _Type::_Root_dir; 121 ++pos; 122 } 123 // Find the start of the first filename 124 while (pos < len && is_dir_sep(input[pos])) 125 ++pos; 126 } 127 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 128 else if (is_disk_designator(input.substr(0, 2))) 129 { 130 // got disk designator 131 root.first.str = input.substr(0, 2); 132 root.first.type = _Type::_Root_name; 133 if (len > 2 && is_dir_sep(input[2])) 134 { 135 root.second.str = input.substr(2, 1); 136 root.second.type = _Type::_Root_dir; 137 } 138 pos = input.find_first_not_of(L"/\\", 2); 139 } 140 #endif 141 142 if (root.second.valid()) 143 last_type = root.second.type; 144 else 145 last_type = root.first.type; 146 147 return root; 148 } 149 150 cmpt next() noexcept 151 { 152 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 153 string_view_type sep = L"/\\"; 154 #else 155 char sep = '/'; 156 #endif 157 158 const int last_pos = pos; 159 160 cmpt f; 161 if (pos != input.npos) 162 { 163 pos = input.find_first_not_of(sep, pos); 164 if (pos != input.npos) 165 { 166 const auto end = input.find_first_of(sep, pos); 167 f.str = input.substr(pos, end - pos); 168 f.type = _Type::_Filename; 169 pos = end; 170 } 171 else if (last_type == _Type::_Filename 172 || (last_pos == 0 && !input.empty())) 173 { 174 // [fs.path.itr]/4 An empty element, if trailing non-root 175 // directory-separator present. 176 __glibcxx_assert(is_dir_sep(input.back())); 177 f.str = input.substr(input.length(), 0); 178 f.type = _Type::_Filename; 179 } 180 } 181 last_type = f.type; 182 return f; 183 } 184 185 string_view_type::size_type 186 offset(const cmpt& c) const noexcept 187 { return origin + c.str.data() - input.data(); } 188 }; 189 190 inline 191 path::path(basic_string_view<value_type> __str, _Type __type) 192 : _M_pathname(__str) 193 { 194 __glibcxx_assert(__type != _Type::_Multi); 195 _M_cmpts.type(__type); 196 } 197 198 inline 199 path::_Cmpt::_Cmpt(basic_string_view<value_type> __s, _Type __t, size_t __pos) 200 : path(__s, __t), _M_pos(__pos) 201 { } 202 203 struct path::_List::_Impl 204 { 205 using value_type = _Cmpt; 206 207 _Impl(int cap) : _M_size(0), _M_capacity(cap) { } 208 209 alignas(value_type) int _M_size; 210 int _M_capacity; 211 212 using iterator = value_type*; 213 using const_iterator = const value_type*; 214 215 iterator begin() { return reinterpret_cast<value_type*>(this + 1); } 216 iterator end() { return begin() + size(); } 217 218 const_iterator begin() const 219 { return reinterpret_cast<const value_type*>(this + 1); } 220 const_iterator end() const { return begin() + size(); } 221 222 const value_type& front() const { return *begin(); } 223 const value_type& back() const { return end()[-1]; } 224 225 int size() const { return _M_size; } 226 int capacity() const { return _M_capacity; } 227 bool empty() const { return _M_size == 0; } 228 229 void clear() { std::destroy_n(begin(), _M_size); _M_size = 0; } 230 231 void pop_back() 232 { 233 back().~_Cmpt(); 234 --_M_size; 235 } 236 237 void _M_erase_from(const_iterator pos) 238 { 239 iterator first = begin() + (pos - begin()); 240 iterator last = end(); 241 std::destroy(first, last); 242 _M_size -= last - first; 243 } 244 245 unique_ptr<_Impl, _Impl_deleter> copy() const 246 { 247 const auto n = size(); 248 void* p = ::operator new(sizeof(_Impl) + n * sizeof(value_type)); 249 unique_ptr<_Impl, _Impl_deleter> newptr(::new (p) _Impl{n}); 250 std::uninitialized_copy_n(begin(), n, newptr->begin()); 251 newptr->_M_size = n; 252 return newptr; 253 } 254 255 // Clear the lowest two bits from the pointer (i.e. remove the _Type value) 256 static _Impl* notype(_Impl* p) 257 { 258 constexpr uintptr_t mask = ~(uintptr_t)0x3; 259 return reinterpret_cast<_Impl*>(reinterpret_cast<uintptr_t>(p) & mask); 260 } 261 }; 262 263 void path::_List::_Impl_deleter::operator()(_Impl* p) const noexcept 264 { 265 p = _Impl::notype(p); 266 if (p) 267 { 268 __glibcxx_assert(p->_M_size <= p->_M_capacity); 269 p->clear(); 270 ::operator delete(p, sizeof(*p) + p->_M_capacity * sizeof(value_type)); 271 } 272 } 273 274 path::_List::_List() : _M_impl(reinterpret_cast<_Impl*>(_Type::_Filename)) { } 275 276 path::_List::_List(const _List& other) 277 { 278 if (!other.empty()) 279 _M_impl = other._M_impl->copy(); 280 else 281 type(other.type()); 282 } 283 284 path::_List& 285 path::_List::operator=(const _List& other) 286 { 287 if (!other.empty()) 288 { 289 // copy in-place if there is capacity 290 const int newsize = other._M_impl->size(); 291 auto impl = _Impl::notype(_M_impl.get()); 292 if (impl && impl->capacity() >= newsize) 293 { 294 const int oldsize = impl->_M_size; 295 auto to = impl->begin(); 296 auto from = other._M_impl->begin(); 297 const int minsize = std::min(newsize, oldsize); 298 for (int i = 0; i < minsize; ++i) 299 to[i]._M_pathname.reserve(from[i]._M_pathname.length()); 300 if (newsize > oldsize) 301 { 302 std::uninitialized_copy_n(from + oldsize, newsize - oldsize, 303 to + oldsize); 304 impl->_M_size = newsize; 305 } 306 else if (newsize < oldsize) 307 impl->_M_erase_from(impl->begin() + newsize); 308 std::copy_n(from, minsize, to); 309 type(_Type::_Multi); 310 } 311 else 312 _M_impl = other._M_impl->copy(); 313 } 314 else 315 { 316 clear(); 317 type(other.type()); 318 } 319 return *this; 320 } 321 322 inline void 323 path::_List::type(_Type t) noexcept 324 { 325 auto val = reinterpret_cast<uintptr_t>(_Impl::notype(_M_impl.release())); 326 _M_impl.reset(reinterpret_cast<_Impl*>(val | (unsigned char)t)); 327 } 328 329 inline int 330 path::_List::size() const noexcept 331 { 332 if (auto* ptr = _Impl::notype(_M_impl.get())) 333 return ptr->size(); 334 return 0; 335 } 336 337 inline int 338 path::_List::capacity() const noexcept 339 { 340 if (auto* ptr = _Impl::notype(_M_impl.get())) 341 return ptr->capacity(); 342 return 0; 343 } 344 345 inline bool 346 path::_List::empty() const noexcept 347 { 348 return size() == 0; 349 } 350 351 inline auto 352 path::_List::begin() noexcept 353 -> iterator 354 { 355 __glibcxx_assert(!empty()); 356 if (auto* ptr = _Impl::notype(_M_impl.get())) 357 return ptr->begin(); 358 return nullptr; 359 } 360 361 inline auto 362 path::_List::end() noexcept 363 -> iterator 364 { 365 __glibcxx_assert(!empty()); 366 if (auto* ptr = _Impl::notype(_M_impl.get())) 367 return ptr->end(); 368 return nullptr; 369 } 370 371 auto 372 path::_List::begin() const noexcept 373 -> const_iterator 374 { 375 __glibcxx_assert(!empty()); 376 if (auto* ptr = _Impl::notype(_M_impl.get())) 377 return ptr->begin(); 378 return nullptr; 379 } 380 381 auto 382 path::_List::end() const noexcept 383 -> const_iterator 384 { 385 __glibcxx_assert(!empty()); 386 if (auto* ptr = _Impl::notype(_M_impl.get())) 387 return ptr->end(); 388 return nullptr; 389 } 390 391 inline auto 392 path::_List::front() noexcept 393 -> value_type& 394 { 395 return *_M_impl->begin(); 396 } 397 398 inline auto 399 path::_List::back() noexcept 400 -> value_type& 401 { 402 return _M_impl->begin()[_M_impl->size() - 1]; 403 } 404 405 inline auto 406 path::_List::front() const noexcept 407 -> const value_type& 408 { 409 return *_M_impl->begin(); 410 } 411 412 inline auto 413 path::_List::back() const noexcept 414 -> const value_type& 415 { 416 return _M_impl->begin()[_M_impl->size() - 1]; 417 } 418 419 inline void 420 path::_List::pop_back() 421 { 422 __glibcxx_assert(size() > 0); 423 _M_impl->pop_back(); 424 } 425 426 inline void 427 path::_List::_M_erase_from(const_iterator pos) 428 { 429 _M_impl->_M_erase_from(pos); 430 } 431 432 inline void 433 path::_List::clear() 434 { 435 if (auto ptr = _Impl::notype(_M_impl.get())) 436 ptr->clear(); 437 } 438 439 void 440 path::_List::reserve(int newcap, bool exact = false) 441 { 442 // __glibcxx_assert(type() == _Type::_Multi); 443 444 _Impl* curptr = _Impl::notype(_M_impl.get()); 445 446 int curcap = curptr ? curptr->capacity() : 0; 447 448 if (curcap < newcap) 449 { 450 if (!exact && newcap < int(1.5 * curcap)) 451 newcap = 1.5 * curcap; 452 453 void* p = ::operator new(sizeof(_Impl) + newcap * sizeof(value_type)); 454 std::unique_ptr<_Impl, _Impl_deleter> newptr(::new(p) _Impl{newcap}); 455 const int cursize = curptr ? curptr->size() : 0; 456 if (cursize) 457 { 458 std::uninitialized_move_n(curptr->begin(), cursize, newptr->begin()); 459 newptr->_M_size = cursize; 460 } 461 std::swap(newptr, _M_impl); 462 } 463 } 464 465 path& 466 path::operator=(const path& p) 467 { 468 if (&p == this) [[__unlikely__]] 469 return *this; 470 471 _M_pathname.reserve(p._M_pathname.length()); 472 _M_cmpts = p._M_cmpts; // might throw 473 _M_pathname = p._M_pathname; // won't throw because we reserved enough space 474 return *this; 475 } 476 477 path& 478 path::operator/=(const path& __p) 479 { 480 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 481 if (__p.is_absolute() 482 || (__p.has_root_name() && __p.root_name() != root_name())) 483 return operator=(__p); 484 485 basic_string_view<value_type> __lhs = _M_pathname; 486 bool __add_sep = false; 487 488 if (__p.has_root_directory()) 489 { 490 // Remove any root directory and relative path 491 if (_M_type() != _Type::_Root_name) 492 { 493 if (!_M_cmpts.empty() 494 && _M_cmpts.front()._M_type() == _Type::_Root_name) 495 __lhs = _M_cmpts.front()._M_pathname; 496 else 497 __lhs = {}; 498 } 499 } 500 else if (has_filename() || (!has_root_directory() && is_absolute())) 501 __add_sep = true; 502 503 basic_string_view<value_type> __rhs = __p._M_pathname; 504 // Omit any root-name from the generic format pathname: 505 if (__p._M_type() == _Type::_Root_name) 506 __rhs = {}; 507 else if (!__p._M_cmpts.empty() 508 && __p._M_cmpts.front()._M_type() == _Type::_Root_name) 509 __rhs.remove_prefix(__p._M_cmpts.front()._M_pathname.size()); 510 511 const size_t __len = __lhs.size() + (int)__add_sep + __rhs.size(); 512 const int __maxcmpts = _M_cmpts.size() + __p._M_cmpts.size(); 513 if (_M_pathname.capacity() < __len || _M_cmpts.capacity() < __maxcmpts) 514 { 515 // Construct new path and swap (strong exception-safety guarantee). 516 string_type __tmp; 517 __tmp.reserve(__len); 518 __tmp = __lhs; 519 if (__add_sep) 520 __tmp += preferred_separator; 521 __tmp += __rhs; 522 path __newp = std::move(__tmp); 523 swap(__newp); 524 } 525 else 526 { 527 _M_pathname = __lhs; 528 if (__add_sep) 529 _M_pathname += preferred_separator; 530 _M_pathname += __rhs; 531 __try 532 { 533 _M_split_cmpts(); 534 } 535 __catch (...) 536 { 537 __try 538 { 539 // try to restore original state 540 _M_pathname.resize(__lhs.length()); 541 _M_split_cmpts(); 542 } 543 __catch (...) 544 { 545 // give up, basic exception safety guarantee only: 546 clear(); 547 __throw_exception_again; 548 } 549 } 550 } 551 #else 552 // POSIX version is simpler than the specification in the standard, 553 // as any path with root-name or root-dir is absolute. 554 555 if (__p.is_absolute() || this->empty()) 556 { 557 return operator=(__p); 558 } 559 560 using string_view_type = basic_string_view<value_type>; 561 562 string_view_type sep; 563 if (has_filename()) 564 sep = { &preferred_separator, 1 }; // need to add a separator 565 #if SLASHSLASH_IS_ROOTNAME 566 else if (_M_type() == _Type::_Root_name) // root-name with no root-dir 567 sep = { &preferred_separator, 1 }; // need to add a separator 568 #endif 569 else if (__p.empty()) 570 return *this; // nothing to do 571 572 const auto orig_pathlen = _M_pathname.length(); 573 const auto orig_size = _M_cmpts.size(); 574 const auto orig_type = _M_type(); 575 576 int capacity = 0; 577 if (_M_type() == _Type::_Multi) 578 capacity += _M_cmpts.size(); 579 else if (!empty()) 580 capacity += 1; 581 if (__p._M_type() == _Type::_Multi) 582 capacity += __p._M_cmpts.size(); 583 else if (!__p.empty() || !sep.empty()) 584 capacity += 1; 585 #if SLASHSLASH_IS_ROOTNAME 586 if (orig_type == _Type::_Root_name) 587 ++capacity; // Need to insert root-directory after root-name 588 #endif 589 590 if (orig_type == _Type::_Multi) 591 { 592 const int curcap = _M_cmpts._M_impl->capacity(); 593 if (capacity > curcap) 594 capacity = std::max(capacity, (int) (curcap * 1.5)); 595 } 596 597 _M_pathname.reserve(_M_pathname.length() + sep.length() 598 + __p._M_pathname.length()); 599 600 __try 601 { 602 _M_pathname += sep; 603 const auto basepos = _M_pathname.length(); 604 _M_pathname += __p.native(); 605 606 _M_cmpts.type(_Type::_Multi); 607 _M_cmpts.reserve(capacity); 608 _Cmpt* output = _M_cmpts._M_impl->end(); 609 610 if (orig_type == _Type::_Multi) 611 { 612 // Remove empty final component 613 if (_M_cmpts._M_impl->back().empty()) 614 { 615 _M_cmpts.pop_back(); 616 --output; 617 } 618 } 619 else if (orig_pathlen != 0) 620 { 621 // Create single component from original path 622 string_view_type s(_M_pathname.data(), orig_pathlen); 623 ::new(output++) _Cmpt(s, orig_type, 0); 624 ++_M_cmpts._M_impl->_M_size; 625 #if SLASHSLASH_IS_ROOTNAME 626 if (orig_type == _Type::_Root_name) 627 { 628 ::new(output++) _Cmpt(sep, _Type::_Root_dir, 629 orig_pathlen + sep.length()); 630 ++_M_cmpts._M_impl->_M_size; 631 } 632 #endif 633 } 634 635 if (__p._M_type() == _Type::_Multi) 636 { 637 for (auto& c : *__p._M_cmpts._M_impl) 638 { 639 ::new(output++) _Cmpt(c._M_pathname, _Type::_Filename, 640 c._M_pos + basepos); 641 ++_M_cmpts._M_impl->_M_size; 642 } 643 } 644 else if (!__p.empty() || !sep.empty()) 645 { 646 __glibcxx_assert(__p._M_type() == _Type::_Filename); 647 ::new(output) _Cmpt(__p._M_pathname, __p._M_type(), basepos); 648 ++_M_cmpts._M_impl->_M_size; 649 } 650 } 651 __catch (...) 652 { 653 _M_pathname.resize(orig_pathlen); 654 if (orig_type == _Type::_Multi) 655 _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size); 656 else 657 _M_cmpts.clear(); 658 _M_cmpts.type(orig_type); 659 __throw_exception_again; 660 } 661 #endif 662 return *this; 663 } 664 665 // [fs.path.append] 666 void 667 path::_M_append(basic_string_view<value_type> s) 668 { 669 _Parser parser(s); 670 auto root_path = parser.root_path(); 671 672 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 673 bool is_absolute = root_path.second.type == _Type::_Root_dir; 674 bool has_root_name = root_path.first.type == _Type::_Root_name; 675 if (is_absolute || (has_root_name && root_path.first.str != root_name())) 676 { 677 operator=(s); 678 return; 679 } 680 681 basic_string_view<value_type> lhs = _M_pathname; 682 bool add_sep = false; 683 684 bool has_root_directory = root_path.first.type == _Type::_Root_dir 685 || root_path.second.type == _Type::_Root_dir; 686 687 if (has_root_directory) 688 { 689 // Remove any root directory and relative path 690 if (_M_type() != _Type::_Root_name) 691 { 692 if (!_M_cmpts.empty() 693 && _M_cmpts.front()._M_type() == _Type::_Root_name) 694 lhs = _M_cmpts.front()._M_pathname; 695 else 696 lhs = {}; 697 } 698 } 699 else if (has_filename() || (!has_root_directory && is_absolute)) 700 add_sep = true; 701 702 basic_string_view<value_type> rhs = s; 703 // Omit any root-name from the generic format pathname: 704 if (has_root_name) 705 rhs.remove_prefix(root_path.first.str.length()); 706 707 // Construct new path and swap (strong exception-safety guarantee). 708 string_type tmp; 709 tmp.reserve(lhs.size() + (int)add_sep + rhs.size()); 710 tmp = lhs; 711 if (add_sep) 712 tmp += preferred_separator; 713 tmp += rhs; 714 path newp = std::move(tmp); 715 swap(newp); 716 #else 717 718 bool is_absolute = root_path.first.type == _Type::_Root_dir 719 || root_path.second.type == _Type::_Root_dir; 720 if (is_absolute || this->empty()) 721 { 722 operator=(s); 723 return; 724 } 725 726 const auto orig_pathlen = _M_pathname.length(); 727 const auto orig_size = _M_cmpts.size(); 728 const auto orig_type = _M_type(); 729 730 basic_string_view<value_type> sep; 731 if (has_filename()) 732 sep = { &preferred_separator, 1 }; // need to add a separator 733 #if SLASHSLASH_IS_ROOTNAME 734 else if (_M_type() == _Type::_Root_name) // root-name with no root-dir 735 sep = { &preferred_separator, 1 }; // need to add a separator 736 #endif 737 else if (s.empty()) 738 return; // nothing to do 739 740 // Copy the input into _M_pathname: 741 _M_pathname += s; 742 _M_pathname.insert(orig_pathlen, sep); 743 // Update s to refer to the new copy (this ensures s is not a dangling 744 // reference to deallocated characters, in the case where it was referring 745 // into _M_pathname or a member of _M_cmpts). 746 s = _M_pathname; 747 const auto orig_pathname = s.substr(0, orig_pathlen); 748 s.remove_prefix(orig_pathlen + sep.length()); 749 750 parser.input = s; // reset parser to use updated string view 751 const auto basepos = orig_pathname.length() + sep.length(); 752 parser.origin = basepos; 753 754 std::array<_Parser::cmpt, 64> buf; 755 auto next = buf.begin(); 756 757 int capacity = 0; 758 if (_M_type() == _Type::_Multi) 759 capacity += _M_cmpts.size(); 760 else if (!empty()) 761 capacity += 1; 762 763 auto cmpt = parser.next(); 764 if (cmpt.valid()) 765 { 766 do 767 { 768 *next++ = cmpt; 769 cmpt = parser.next(); 770 } 771 while (cmpt.valid() && next != buf.end()); 772 773 capacity += next - buf.begin(); 774 if (cmpt.valid()) // filled buffer before parsing whole input 775 { 776 ++capacity; 777 _Parser parser2(parser); 778 while (parser2.next().valid()) 779 ++capacity; 780 } 781 } 782 else if (!sep.empty()) 783 ++capacity; 784 785 #if SLASHSLASH_IS_ROOTNAME 786 if (orig_type == _Type::_Root_name) 787 ++capacity; // Need to insert root-directory after root-name 788 #endif 789 790 __try 791 { 792 _M_cmpts.type(_Type::_Multi); 793 _M_cmpts.reserve(capacity); 794 _Cmpt* output = _M_cmpts._M_impl->end(); 795 796 if (orig_type == _Type::_Multi) 797 { 798 // Remove empty final component 799 if (_M_cmpts._M_impl->back().empty()) 800 { 801 _M_cmpts.pop_back(); 802 --output; 803 } 804 } 805 else if (orig_pathlen != 0) 806 { 807 // Create single component from original path 808 ::new(output++) _Cmpt(orig_pathname, orig_type, 0); 809 ++_M_cmpts._M_impl->_M_size; 810 811 #if SLASHSLASH_IS_ROOTNAME 812 if (!sep.empty() && orig_type == _Type::_Root_name) 813 { 814 ::new(output++) _Cmpt(sep, _Type::_Root_dir, 815 orig_pathlen + sep.length()); 816 ++_M_cmpts._M_impl->_M_size; 817 } 818 #endif 819 } 820 821 if (next != buf.begin()) 822 { 823 for (auto it = buf.begin(); it != next; ++it) 824 { 825 auto c = *it; 826 ::new(output++) _Cmpt(c.str, c.type, parser.offset(c)); 827 ++_M_cmpts._M_impl->_M_size; 828 } 829 while (cmpt.valid()) 830 { 831 ::new(output++) _Cmpt(cmpt.str, cmpt.type, parser.offset(cmpt)); 832 ++_M_cmpts._M_impl->_M_size; 833 cmpt = parser.next(); 834 } 835 } 836 else if (!sep.empty()) 837 { 838 // Empty filename at the end: 839 ::new(output) _Cmpt({}, _Type::_Filename, basepos); 840 ++_M_cmpts._M_impl->_M_size; 841 } 842 } 843 __catch (...) 844 { 845 _M_pathname.resize(orig_pathlen); 846 if (orig_type == _Type::_Multi) 847 _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size); 848 else 849 _M_cmpts.clear(); 850 _M_cmpts.type(orig_type); 851 __throw_exception_again; 852 } 853 #endif 854 } 855 856 // [fs.path.concat] 857 path& 858 path::operator+=(const path& p) 859 { 860 if (p.empty()) 861 return *this; 862 863 if (this->empty()) 864 { 865 operator=(p); 866 return *this; 867 } 868 869 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 870 if (_M_type() == _Type::_Root_name 871 || (_M_type() == _Type::_Filename && _M_pathname.size() == 1)) 872 { 873 // Handle path("C") += path(":") and path("C:") += path("/x") 874 // FIXME: do this more efficiently 875 *this = path(_M_pathname + p._M_pathname); 876 return *this; 877 } 878 #endif 879 #if SLASHSLASH_IS_ROOTNAME 880 if (_M_type() == _Type::_Root_dir) 881 { 882 // Handle path("/") += path("/x") and path("//") += path("x") 883 // FIXME: do this more efficiently 884 *this = path(_M_pathname + p._M_pathname); 885 return *this; 886 } 887 #endif 888 889 const auto orig_pathlen = _M_pathname.length(); 890 const auto orig_type = _M_type(); 891 const auto orig_size = _M_cmpts.size(); 892 int orig_filenamelen = -1; 893 basic_string_view<value_type> extra; 894 895 // Ensure that '_M_pathname += p._M_pathname' won't throw: 896 _M_pathname.reserve(orig_pathlen + p._M_pathname.length()); 897 898 _Cmpt c; 899 _Cmpt* it = nullptr; 900 _Cmpt* last = nullptr; 901 if (p._M_type() == _Type::_Multi) 902 { 903 it = p._M_cmpts._M_impl->begin(); 904 last = p._M_cmpts._M_impl->end(); 905 } 906 else 907 { 908 c = _Cmpt(p._M_pathname, p._M_type(), 0); 909 it = &c; 910 last = it + 1; 911 } 912 913 if (it->_M_type() == _Type::_Filename) 914 { 915 // See if there's a filename or root-name at the end of the original path 916 // that we can add to. 917 if (_M_type() == _Type::_Filename 918 #if SLASHSLASH_IS_ROOTNAME 919 || _M_type() == _Type::_Root_name 920 #endif 921 ) 922 { 923 if (p._M_type() == _Type::_Filename) 924 { 925 // Simplest case where we just add the whole of p to the 926 // original path. 927 _M_pathname += p._M_pathname; 928 return *this; 929 } 930 // Only the first component of s should be appended, do so below: 931 extra = it->_M_pathname; 932 ++it; 933 } 934 else if (_M_type() == _Type::_Multi 935 && _M_cmpts.back()._M_type() == _Type::_Filename) 936 { 937 auto& back = _M_cmpts.back(); 938 if (p._M_type() == _Type::_Filename) 939 { 940 basic_string_view<value_type> s = p._M_pathname; 941 back._M_pathname += s; 942 _M_pathname += s; 943 return *this; 944 } 945 946 orig_filenamelen = back._M_pathname.length(); 947 back._M_pathname += it->_M_pathname; 948 extra = it->_M_pathname; 949 ++it; 950 } 951 } 952 else if (is_dir_sep(_M_pathname.back()) && _M_type() == _Type::_Multi 953 && _M_cmpts.back()._M_type() == _Type::_Filename) 954 orig_filenamelen = 0; // current path has empty filename at end 955 956 int capacity = 0; 957 if (_M_type() == _Type::_Multi) 958 capacity += _M_cmpts.size(); 959 else 960 capacity += 1; 961 if (p._M_type() == _Type::_Multi) 962 capacity += p._M_cmpts.size(); 963 else 964 capacity += 1; 965 966 __try 967 { 968 _M_cmpts.type(_Type::_Multi); 969 _M_cmpts.reserve(capacity); 970 _Cmpt* output = _M_cmpts._M_impl->end(); 971 972 if (orig_type != _Type::_Multi) 973 { 974 // Create single component from original path 975 auto ptr = ::new(output++) _Cmpt({}, orig_type, 0); 976 ++_M_cmpts._M_impl->_M_size; 977 ptr->_M_pathname.reserve(_M_pathname.length() + extra.length()); 978 ptr->_M_pathname = _M_pathname; 979 ptr->_M_pathname += extra; 980 981 #if SLASHSLASH_IS_ROOTNAME 982 if (orig_type == _Type::_Root_name) 983 { 984 basic_string_view<value_type> s(p._M_pathname); 985 ::new(output++) _Cmpt(s.substr(extra.length(), 1), 986 _Type::_Root_dir, orig_pathlen + extra.length()); 987 ++_M_cmpts._M_impl->_M_size; 988 } 989 #endif 990 } 991 else if (orig_filenamelen == 0 && it != last) 992 { 993 // Remove empty filename at end of original path. 994 _M_cmpts.pop_back(); 995 --output; 996 } 997 998 if (it != last && it->_M_type() == _Type::_Root_name) 999 { 1000 basic_string_view<value_type> s = it->_M_pathname; 1001 auto pos = orig_pathlen; 1002 #if SLASHSLASH_IS_ROOTNAME 1003 s.remove_prefix(2); 1004 pos += 2; 1005 #endif 1006 ::new(output++) _Cmpt(s, _Type::_Filename, pos); 1007 ++_M_cmpts._M_impl->_M_size; 1008 ++it; 1009 } 1010 1011 if (it != last && it->_M_type() == _Type::_Root_dir) 1012 ++it; 1013 1014 while (it != last) 1015 { 1016 auto pos = it->_M_pos + orig_pathlen; 1017 ::new(output++) _Cmpt(it->_M_pathname, _Type::_Filename, pos); 1018 ++_M_cmpts._M_impl->_M_size; 1019 ++it; 1020 } 1021 1022 _M_pathname += p._M_pathname; 1023 1024 if (is_dir_sep(_M_pathname.back())) 1025 { 1026 ::new(output++) _Cmpt({}, _Type::_Filename, _M_pathname.length()); 1027 ++_M_cmpts._M_impl->_M_size; 1028 } 1029 } 1030 __catch (...) 1031 { 1032 _M_pathname.resize(orig_pathlen); 1033 if (orig_type == _Type::_Multi) 1034 { 1035 if (_M_cmpts.size() > orig_size) 1036 _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size); 1037 if (orig_filenamelen != -1) 1038 { 1039 if (_M_cmpts.size() == orig_size) 1040 { 1041 auto& back = _M_cmpts.back(); 1042 back._M_pathname.resize(orig_filenamelen); 1043 if (orig_filenamelen == 0) 1044 back._M_pos = orig_pathlen; 1045 } 1046 else 1047 { 1048 auto output = _M_cmpts._M_impl->end(); 1049 ::new(output) _Cmpt({}, _Type::_Filename, orig_pathlen); 1050 ++_M_cmpts._M_impl->_M_size; 1051 } 1052 } 1053 } 1054 else 1055 _M_cmpts.clear(); 1056 _M_cmpts.type(orig_type); 1057 __throw_exception_again; 1058 } 1059 return *this; 1060 } 1061 1062 // [fs.path.concat] 1063 void 1064 path::_M_concat(basic_string_view<value_type> s) 1065 { 1066 if (s.empty()) 1067 return; 1068 1069 if (this->empty()) 1070 { 1071 operator=(s); 1072 return; 1073 } 1074 1075 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1076 if (_M_type() == _Type::_Root_name 1077 || (_M_type() == _Type::_Filename && _M_pathname.size() == 1)) 1078 { 1079 // Handle path("C") += ":" and path("C:") += "/x" 1080 // FIXME: do this more efficiently 1081 *this = path(_M_pathname + string_type(s)); 1082 return; 1083 } 1084 #endif 1085 #if SLASHSLASH_IS_ROOTNAME 1086 if (_M_type() == _Type::_Root_dir) 1087 { 1088 // Handle path("/") += "/x" and path("//") += "x" 1089 // FIXME: do this more efficiently 1090 *this = path(_M_pathname + string_type(s)); 1091 return; 1092 } 1093 #endif 1094 1095 const auto orig_pathlen = _M_pathname.length(); 1096 const auto orig_type = _M_type(); 1097 const auto orig_size = _M_cmpts.size(); 1098 int orig_filenamelen = -1; 1099 basic_string_view<value_type> extra; 1100 1101 // Copy the input into _M_pathname: 1102 _M_pathname += s; 1103 // Update s to refer to the new copy (this ensures s is not a dangling 1104 // reference to deallocated characters, in the case where it was referring 1105 // into _M_pathname or a member of _M_cmpts). 1106 s = _M_pathname; 1107 const auto orig_pathname = s.substr(0, orig_pathlen); 1108 s.remove_prefix(orig_pathlen); 1109 1110 _Parser parser(s, orig_pathlen); 1111 auto cmpt = parser.next(); 1112 1113 if (cmpt.str.data() == s.data()) 1114 { 1115 // See if there's a filename or root-name at the end of the original path 1116 // that we can add to. 1117 if (_M_type() == _Type::_Filename 1118 #if SLASHSLASH_IS_ROOTNAME 1119 || _M_type() == _Type::_Root_name 1120 #endif 1121 ) 1122 { 1123 if (cmpt.str.length() == s.length()) 1124 { 1125 // Simplest case where we just need to add the whole of s 1126 // to the original path, which was already done above. 1127 return; 1128 } 1129 // Only the first component of s should be appended, do so below: 1130 extra = cmpt.str; 1131 cmpt = {}; // so we don't process it again 1132 } 1133 else if (_M_type() == _Type::_Multi 1134 && _M_cmpts.back()._M_type() == _Type::_Filename) 1135 { 1136 auto& back = _M_cmpts.back(); 1137 if (cmpt.str.length() == s.length()) 1138 { 1139 back._M_pathname += s; 1140 return; 1141 } 1142 1143 orig_filenamelen = back._M_pathname.length(); 1144 back._M_pathname += cmpt.str; 1145 extra = cmpt.str; 1146 cmpt = {}; 1147 } 1148 } 1149 else if (is_dir_sep(orig_pathname.back()) && _M_type() == _Type::_Multi 1150 && _M_cmpts.back()._M_type() == _Type::_Filename) 1151 orig_filenamelen = 0; // original path had empty filename at end 1152 1153 std::array<_Parser::cmpt, 64> buf; 1154 auto next = buf.begin(); 1155 1156 if (cmpt.valid()) 1157 *next++ = cmpt; 1158 1159 cmpt = parser.next(); 1160 while (cmpt.valid() && next != buf.end()) 1161 { 1162 *next++ = cmpt; 1163 cmpt = parser.next(); 1164 } 1165 1166 int capacity = 0; 1167 if (_M_type() == _Type::_Multi) 1168 capacity += _M_cmpts.size(); 1169 else 1170 capacity += 1; 1171 1172 capacity += next - buf.begin(); 1173 1174 if (cmpt.valid()) // filled buffer before parsing whole input 1175 { 1176 ++capacity; 1177 _Parser parser2(parser); 1178 while (parser2.next().valid()) 1179 ++capacity; 1180 } 1181 1182 #if SLASHSLASH_IS_ROOTNAME 1183 if (orig_type == _Type::_Root_name) 1184 ++capacity; // Need to insert root-directory after root-name 1185 #endif 1186 1187 __try 1188 { 1189 _M_cmpts.type(_Type::_Multi); 1190 _M_cmpts.reserve(capacity); 1191 _Cmpt* output = _M_cmpts._M_impl->end(); 1192 auto it = buf.begin(); 1193 1194 if (orig_type != _Type::_Multi) 1195 { 1196 // Create single component from original path 1197 auto p = ::new(output++) _Cmpt({}, orig_type, 0); 1198 ++_M_cmpts._M_impl->_M_size; 1199 p->_M_pathname.reserve(orig_pathname.length() + extra.length()); 1200 p->_M_pathname = orig_pathname; 1201 p->_M_pathname += extra; 1202 1203 #if SLASHSLASH_IS_ROOTNAME 1204 if (orig_type == _Type::_Root_name) 1205 { 1206 ::new(output++) _Cmpt(s.substr(extra.length(), 1), 1207 _Type::_Root_dir, orig_pathlen + extra.length()); 1208 ++_M_cmpts._M_impl->_M_size; 1209 } 1210 #endif 1211 } 1212 else if (orig_filenamelen == 0 && extra.empty()) 1213 { 1214 // Replace empty filename at end of original path. 1215 std::prev(output)->_M_pathname = it->str; 1216 std::prev(output)->_M_pos = parser.offset(*it); 1217 ++it; 1218 } 1219 1220 while (it != next) 1221 { 1222 ::new(output++) _Cmpt(it->str, _Type::_Filename, parser.offset(*it)); 1223 ++_M_cmpts._M_impl->_M_size; 1224 ++it; 1225 } 1226 1227 if (next == buf.end()) 1228 { 1229 while (cmpt.valid()) 1230 { 1231 auto pos = parser.offset(cmpt); 1232 ::new(output++) _Cmpt(cmpt.str, _Type::_Filename, pos); 1233 ++_M_cmpts._M_impl->_M_size; 1234 cmpt = parser.next(); 1235 } 1236 } 1237 } 1238 __catch (...) 1239 { 1240 _M_pathname.resize(orig_pathlen); 1241 if (orig_type == _Type::_Multi) 1242 { 1243 _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size); 1244 if (orig_filenamelen != -1) 1245 { 1246 auto& back = _M_cmpts.back(); 1247 back._M_pathname.resize(orig_filenamelen); 1248 if (orig_filenamelen == 0) 1249 back._M_pos = orig_pathlen; 1250 } 1251 } 1252 else 1253 _M_cmpts.clear(); 1254 _M_cmpts.type(orig_type); 1255 __throw_exception_again; 1256 } 1257 } 1258 1259 path& 1260 path::remove_filename() 1261 { 1262 if (_M_type() == _Type::_Multi) 1263 { 1264 if (!_M_cmpts.empty()) 1265 { 1266 auto cmpt = std::prev(_M_cmpts.end()); 1267 if (cmpt->_M_type() == _Type::_Filename && !cmpt->empty()) 1268 { 1269 _M_pathname.erase(cmpt->_M_pos); 1270 auto prev = std::prev(cmpt); 1271 if (prev->_M_type() == _Type::_Root_dir 1272 || prev->_M_type() == _Type::_Root_name) 1273 { 1274 _M_cmpts.pop_back(); 1275 if (_M_cmpts.size() == 1) 1276 { 1277 _M_cmpts.type(_M_cmpts.front()._M_type()); 1278 _M_cmpts.clear(); 1279 } 1280 } 1281 else 1282 cmpt->clear(); 1283 } 1284 } 1285 } 1286 else if (_M_type() == _Type::_Filename) 1287 clear(); 1288 return *this; 1289 } 1290 1291 path& 1292 path::replace_filename(const path& replacement) 1293 { 1294 remove_filename(); 1295 operator/=(replacement); 1296 return *this; 1297 } 1298 1299 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1300 const fs::path::value_type dot = L'.'; 1301 #else 1302 const fs::path::value_type dot = '.'; 1303 #endif 1304 1305 path& 1306 path::replace_extension(const path& replacement) 1307 { 1308 auto ext = _M_find_extension(); 1309 // Any existing extension() is removed 1310 if (ext.first && ext.second != string_type::npos) 1311 { 1312 if (ext.first == &_M_pathname) 1313 _M_pathname.erase(ext.second); 1314 else 1315 { 1316 auto& back = _M_cmpts.back(); 1317 __glibcxx_assert( ext.first == &back._M_pathname ); 1318 back._M_pathname.erase(ext.second); 1319 _M_pathname.erase(back._M_pos + ext.second); 1320 } 1321 } 1322 // If replacement is not empty and does not begin with a dot character, 1323 // a dot character is appended 1324 if (!replacement.empty() && replacement.native()[0] != dot) 1325 operator+=("."); 1326 operator+=(replacement); 1327 return *this; 1328 } 1329 1330 int 1331 path::compare(const path& p) const noexcept 1332 { 1333 if (_M_pathname == p._M_pathname) 1334 return 0; 1335 1336 basic_string_view<value_type> lroot, rroot; 1337 if (_M_type() == _Type::_Root_name) 1338 lroot = _M_pathname; 1339 else if (_M_type() == _Type::_Multi 1340 && _M_cmpts.front()._M_type() == _Type::_Root_name) 1341 lroot = _M_cmpts.front()._M_pathname; 1342 if (p._M_type() == _Type::_Root_name) 1343 rroot = p._M_pathname; 1344 else if (p._M_type() == _Type::_Multi 1345 && p._M_cmpts.front()._M_type() == _Type::_Root_name) 1346 rroot = p._M_cmpts.front()._M_pathname; 1347 if (int rootNameComparison = lroot.compare(rroot)) 1348 return rootNameComparison; 1349 1350 if (!this->has_root_directory() && p.has_root_directory()) 1351 return -1; 1352 else if (this->has_root_directory() && !p.has_root_directory()) 1353 return +1; 1354 1355 using Iterator = const _Cmpt*; 1356 Iterator begin1, end1, begin2, end2; 1357 if (_M_type() == _Type::_Multi) 1358 { 1359 begin1 = _M_cmpts.begin(); 1360 end1 = _M_cmpts.end(); 1361 // Find start of this->relative_path() 1362 while (begin1 != end1 && begin1->_M_type() != _Type::_Filename) 1363 ++begin1; 1364 } 1365 else 1366 begin1 = end1 = nullptr; 1367 1368 if (p._M_type() == _Type::_Multi) 1369 { 1370 begin2 = p._M_cmpts.begin(); 1371 end2 = p._M_cmpts.end(); 1372 // Find start of p.relative_path() 1373 while (begin2 != end2 && begin2->_M_type() != _Type::_Filename) 1374 ++begin2; 1375 } 1376 else 1377 begin2 = end2 = nullptr; 1378 1379 if (_M_type() == _Type::_Filename) 1380 { 1381 if (p._M_type() == _Type::_Filename) 1382 return native().compare(p.native()); 1383 else if (begin2 != end2) 1384 { 1385 if (int ret = native().compare(begin2->native())) 1386 return ret; 1387 else 1388 return ++begin2 == end2 ? 0 : -1; 1389 } 1390 else 1391 return +1; 1392 } 1393 else if (p._M_type() == _Type::_Filename) 1394 { 1395 if (begin1 != end1) 1396 { 1397 if (int ret = begin1->native().compare(p.native())) 1398 return ret; 1399 else 1400 return ++begin1 == end1 ? 0 : +1; 1401 } 1402 else 1403 return -1; 1404 } 1405 1406 int count = 1; 1407 while (begin1 != end1 && begin2 != end2) 1408 { 1409 if (int i = begin1->native().compare(begin2->native())) 1410 return i; 1411 ++begin1; 1412 ++begin2; 1413 ++count; 1414 } 1415 if (begin1 == end1) 1416 { 1417 if (begin2 == end2) 1418 return 0; 1419 return -count; 1420 } 1421 return count; 1422 } 1423 1424 int 1425 path::compare(basic_string_view<value_type> s) const noexcept 1426 { 1427 if (_M_pathname == s) 1428 return 0; 1429 1430 _Parser parser(s); 1431 1432 basic_string_view<value_type> lroot, rroot; 1433 if (_M_type() == _Type::_Root_name) 1434 lroot = _M_pathname; 1435 else if (_M_type() == _Type::_Multi 1436 && _M_cmpts.front()._M_type() == _Type::_Root_name) 1437 lroot = _M_cmpts.front()._M_pathname; 1438 auto root_path = parser.root_path(); 1439 if (root_path.first.type == _Type::_Root_name) 1440 rroot = root_path.first.str; 1441 if (int rootNameComparison = lroot.compare(rroot)) 1442 return rootNameComparison; 1443 1444 const bool has_root_dir = root_path.first.type == _Type::_Root_dir 1445 || root_path.second.type == _Type::_Root_dir; 1446 if (!this->has_root_directory() && has_root_dir) 1447 return -1; 1448 else if (this->has_root_directory() && !has_root_dir) 1449 return +1; 1450 1451 using Iterator = const _Cmpt*; 1452 Iterator begin1, end1; 1453 if (_M_type() == _Type::_Filename) 1454 { 1455 auto cmpt = parser.next(); 1456 if (cmpt.valid()) 1457 { 1458 if (int ret = this->native().compare(cmpt.str)) 1459 return ret; 1460 return parser.next().valid() ? -1 : 0; 1461 } 1462 else 1463 return +1; 1464 } 1465 else if (_M_type() == _Type::_Multi) 1466 { 1467 begin1 = _M_cmpts.begin(); 1468 end1 = _M_cmpts.end(); 1469 while (begin1 != end1 && begin1->_M_type() != _Type::_Filename) 1470 ++begin1; 1471 } 1472 else 1473 begin1 = end1 = nullptr; 1474 1475 int count = 1; 1476 auto cmpt = parser.next(); 1477 while (begin1 != end1 && cmpt.valid()) 1478 { 1479 if (int i = begin1->native().compare(cmpt.str)) 1480 return i; 1481 ++begin1; 1482 cmpt = parser.next(); 1483 ++count; 1484 } 1485 if (begin1 == end1) 1486 { 1487 if (!cmpt.valid()) 1488 return 0; 1489 return -count; 1490 } 1491 return +count; 1492 } 1493 1494 path 1495 path::root_name() const 1496 { 1497 path __ret; 1498 if (_M_type() == _Type::_Root_name) 1499 __ret = *this; 1500 else if (_M_cmpts.size() && _M_cmpts.begin()->_M_type() == _Type::_Root_name) 1501 __ret = *_M_cmpts.begin(); 1502 return __ret; 1503 } 1504 1505 path 1506 path::root_directory() const 1507 { 1508 path __ret; 1509 if (_M_type() == _Type::_Root_dir) 1510 { 1511 __ret._M_cmpts.type(_Type::_Root_dir); 1512 __ret._M_pathname.assign(1, preferred_separator); 1513 } 1514 else if (!_M_cmpts.empty()) 1515 { 1516 auto __it = _M_cmpts.begin(); 1517 if (__it->_M_type() == _Type::_Root_name) 1518 ++__it; 1519 if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir) 1520 __ret = *__it; 1521 } 1522 return __ret; 1523 } 1524 1525 path 1526 path::root_path() const 1527 { 1528 path __ret; 1529 if (_M_type() == _Type::_Root_name) 1530 __ret = *this; 1531 else if (_M_type() == _Type::_Root_dir) 1532 { 1533 __ret._M_pathname.assign(1, preferred_separator); 1534 __ret._M_cmpts.type(_Type::_Root_dir); 1535 } 1536 else if (!_M_cmpts.empty()) 1537 { 1538 auto __it = _M_cmpts.begin(); 1539 if (__it->_M_type() == _Type::_Root_name) 1540 { 1541 __ret = *__it++; 1542 if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir) 1543 __ret /= *__it; 1544 } 1545 else if (__it->_M_type() == _Type::_Root_dir) 1546 __ret = *__it; 1547 } 1548 return __ret; 1549 } 1550 1551 path 1552 path::relative_path() const 1553 { 1554 path __ret; 1555 if (_M_type() == _Type::_Filename) 1556 __ret = *this; 1557 else if (!_M_cmpts.empty()) 1558 { 1559 auto __it = _M_cmpts.begin(); 1560 if (__it->_M_type() == _Type::_Root_name) 1561 ++__it; 1562 if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir) 1563 ++__it; 1564 if (__it != _M_cmpts.end()) 1565 __ret.assign(_M_pathname.substr(__it->_M_pos)); 1566 } 1567 return __ret; 1568 } 1569 1570 path 1571 path::parent_path() const 1572 { 1573 path __ret; 1574 if (!has_relative_path()) 1575 __ret = *this; 1576 else if (_M_cmpts.size() >= 2) 1577 { 1578 const auto parent = std::prev(_M_cmpts.end(), 2); 1579 const auto len = parent->_M_pos + parent->_M_pathname.length(); 1580 __ret.assign(_M_pathname.substr(0, len)); 1581 } 1582 return __ret; 1583 } 1584 1585 bool 1586 path::has_root_name() const noexcept 1587 { 1588 if (_M_type() == _Type::_Root_name) 1589 return true; 1590 if (!_M_cmpts.empty() && _M_cmpts.begin()->_M_type() == _Type::_Root_name) 1591 return true; 1592 return false; 1593 } 1594 1595 bool 1596 path::has_root_directory() const noexcept 1597 { 1598 if (_M_type() == _Type::_Root_dir) 1599 return true; 1600 if (!_M_cmpts.empty()) 1601 { 1602 auto __it = _M_cmpts.begin(); 1603 if (__it->_M_type() == _Type::_Root_name) 1604 ++__it; 1605 if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir) 1606 return true; 1607 } 1608 return false; 1609 } 1610 1611 bool 1612 path::has_root_path() const noexcept 1613 { 1614 if (_M_type() == _Type::_Root_name || _M_type() == _Type::_Root_dir) 1615 return true; 1616 if (!_M_cmpts.empty()) 1617 { 1618 auto __type = _M_cmpts.front()._M_type(); 1619 if (__type == _Type::_Root_name || __type == _Type::_Root_dir) 1620 return true; 1621 } 1622 return false; 1623 } 1624 1625 bool 1626 path::has_relative_path() const noexcept 1627 { 1628 if (_M_type() == _Type::_Filename && !_M_pathname.empty()) 1629 return true; 1630 if (!_M_cmpts.empty()) 1631 { 1632 auto __it = _M_cmpts.begin(); 1633 if (__it->_M_type() == _Type::_Root_name) 1634 ++__it; 1635 if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir) 1636 ++__it; 1637 if (__it != _M_cmpts.end() && !__it->_M_pathname.empty()) 1638 return true; 1639 } 1640 return false; 1641 } 1642 1643 1644 bool 1645 path::has_parent_path() const noexcept 1646 { 1647 if (!has_relative_path()) 1648 return !empty(); 1649 return _M_cmpts.size() >= 2; 1650 } 1651 1652 bool 1653 path::has_filename() const noexcept 1654 { 1655 if (empty()) 1656 return false; 1657 if (_M_type() == _Type::_Filename) 1658 return !_M_pathname.empty(); 1659 if (_M_type() == _Type::_Multi) 1660 { 1661 if (_M_pathname.back() == preferred_separator) 1662 return false; 1663 return _M_cmpts.back().has_filename(); 1664 } 1665 return false; 1666 } 1667 1668 namespace 1669 { 1670 inline bool is_dot(fs::path::value_type c) { return c == dot; } 1671 1672 inline bool is_dot(const fs::path& path) 1673 { 1674 const auto& filename = path.native(); 1675 return filename.size() == 1 && is_dot(filename[0]); 1676 } 1677 1678 inline bool is_dotdot(const fs::path& path) 1679 { 1680 const auto& filename = path.native(); 1681 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]); 1682 } 1683 } // namespace 1684 1685 path 1686 path::lexically_normal() const 1687 { 1688 /* 1689 C++17 [fs.path.generic] p6 1690 - If the path is empty, stop. 1691 - Replace each slash character in the root-name with a preferred-separator. 1692 - Replace each directory-separator with a preferred-separator. 1693 - Remove each dot filename and any immediately following directory-separator. 1694 - As long as any appear, remove a non-dot-dot filename immediately followed 1695 by a directory-separator and a dot-dot filename, along with any immediately 1696 following directory-separator. 1697 - If there is a root-directory, remove all dot-dot filenames and any 1698 directory-separators immediately following them. 1699 - If the last filename is dot-dot, remove any trailing directory-separator. 1700 - If the path is empty, add a dot. 1701 */ 1702 path ret; 1703 // If the path is empty, stop. 1704 if (empty()) 1705 return ret; 1706 for (auto& p : *this) 1707 { 1708 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1709 // Replace each slash character in the root-name 1710 if (p._M_type() == _Type::_Root_name || p._M_type() == _Type::_Root_dir) 1711 { 1712 string_type s = p.native(); 1713 std::replace(s.begin(), s.end(), L'/', L'\\'); 1714 ret /= s; 1715 continue; 1716 } 1717 #endif 1718 if (is_dotdot(p)) 1719 { 1720 if (ret.has_filename()) 1721 { 1722 // remove a non-dot-dot filename immediately followed by /.. 1723 if (!is_dotdot(ret.filename())) 1724 ret.remove_filename(); 1725 else 1726 ret /= p; 1727 } 1728 else if (!ret.has_relative_path()) 1729 { 1730 // remove a dot-dot filename immediately after root-directory 1731 if (!ret.has_root_directory()) 1732 ret /= p; 1733 } 1734 else 1735 { 1736 // Got a path with a relative path (i.e. at least one non-root 1737 // element) and no filename at the end (i.e. empty last element), 1738 // so must have a trailing slash. See what is before it. 1739 auto elem = ret._M_cmpts.end() - 2; 1740 if (elem->has_filename() && !is_dotdot(*elem)) 1741 { 1742 // Remove the filename before the trailing slash 1743 // (equiv. to ret = ret.parent_path().remove_filename()) 1744 1745 if (elem == ret._M_cmpts.begin()) 1746 ret.clear(); 1747 else 1748 { 1749 ret._M_pathname.erase(elem->_M_pos); 1750 // Remove empty filename at the end: 1751 ret._M_cmpts.pop_back(); 1752 // If we still have a trailing non-root dir separator 1753 // then leave an empty filename at the end: 1754 if (std::prev(elem)->_M_type() == _Type::_Filename) 1755 elem->clear(); 1756 else // remove the component completely: 1757 ret._M_cmpts.pop_back(); 1758 } 1759 } 1760 else 1761 // Append the ".." to something ending in "../" which happens 1762 // when normalising paths like ".././.." and "../a/../.." 1763 ret /= p; 1764 } 1765 } 1766 else if (is_dot(p)) 1767 ret /= path(); 1768 #if SLASHSLASH_IS_ROOTNAME 1769 else if (p._M_type() == _Type::_Root_dir) 1770 ret += '/'; // using operator/=('/') would replace whole of ret 1771 #endif 1772 else 1773 ret /= p; 1774 } 1775 1776 if (ret._M_cmpts.size() >= 2) 1777 { 1778 auto back = std::prev(ret.end()); 1779 // If the last filename is dot-dot, ... 1780 if (back->empty() && is_dotdot(*std::prev(back))) 1781 // ... remove any trailing directory-separator. 1782 ret = ret.parent_path(); 1783 } 1784 // If the path is empty, add a dot. 1785 else if (ret.empty()) 1786 ret = "."; 1787 1788 return ret; 1789 } 1790 1791 path 1792 path::lexically_relative(const path& base) const 1793 { 1794 path ret; 1795 if (root_name() != base.root_name()) 1796 return ret; 1797 if (is_absolute() != base.is_absolute()) 1798 return ret; 1799 if (!has_root_directory() && base.has_root_directory()) 1800 return ret; 1801 auto [a, b] = std::mismatch(begin(), end(), base.begin(), base.end()); 1802 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1803 // _GLIBCXX_RESOLVE_LIB_DEFECTS 1804 // 3070. path::lexically_relative causes surprising results if a filename 1805 // can also be a root-name 1806 if (!empty()) 1807 for (auto& p : _M_cmpts) 1808 if (p._M_type() == _Type::_Filename && is_disk_designator(p.native())) 1809 return ret; 1810 if (!base.empty()) 1811 for (auto i = b, end = base.end(); i != end; ++i) 1812 if (i->_M_type() == _Type::_Filename && is_disk_designator(i->native())) 1813 return ret; 1814 #endif 1815 if (a == end() && b == base.end()) 1816 ret = "."; 1817 else 1818 { 1819 int n = 0; 1820 for (; b != base.end(); ++b) 1821 { 1822 const path& p = *b; 1823 if (is_dotdot(p)) 1824 --n; 1825 else if (!p.empty() && !is_dot(p)) 1826 ++n; 1827 } 1828 if (n == 0 && (a == end() || a->empty())) 1829 ret = "."; 1830 else if (n >= 0) 1831 { 1832 const path dotdot(".."); 1833 while (n--) 1834 ret /= dotdot; 1835 for (; a != end(); ++a) 1836 ret /= *a; 1837 } 1838 } 1839 return ret; 1840 } 1841 1842 path 1843 path::lexically_proximate(const path& base) const 1844 { 1845 path rel = lexically_relative(base); 1846 if (rel.empty()) 1847 rel = *this; 1848 return rel; 1849 } 1850 1851 std::pair<const path::string_type*, std::size_t> 1852 path::_M_find_extension() const noexcept 1853 { 1854 const string_type* s = nullptr; 1855 1856 if (_M_type() == _Type::_Filename) 1857 s = &_M_pathname; 1858 else if (_M_type() == _Type::_Multi && !_M_cmpts.empty()) 1859 { 1860 const auto& c = _M_cmpts.back(); 1861 if (c._M_type() == _Type::_Filename) 1862 s = &c._M_pathname; 1863 } 1864 1865 if (s) 1866 { 1867 if (auto sz = s->size()) 1868 { 1869 if (sz <= 2 && (*s)[0] == dot) 1870 return { s, string_type::npos }; 1871 if (const auto pos = s->rfind(dot)) 1872 return { s , pos }; 1873 return { s, string_type::npos }; 1874 } 1875 } 1876 return {}; 1877 } 1878 1879 void 1880 path::_M_split_cmpts() 1881 { 1882 _M_cmpts.clear(); 1883 1884 if (_M_pathname.empty()) 1885 { 1886 _M_cmpts.type(_Type::_Filename); 1887 return; 1888 } 1889 1890 _Parser parser(_M_pathname); 1891 1892 std::array<_Parser::cmpt, 64> buf; 1893 auto next = buf.begin(); 1894 1895 // look for root name or root directory 1896 auto root_path = parser.root_path(); 1897 if (root_path.first.valid()) 1898 { 1899 *next++ = root_path.first; 1900 if (root_path.second.valid()) 1901 *next++ = root_path.second; 1902 } 1903 1904 auto cmpt = parser.next(); 1905 while (cmpt.valid()) 1906 { 1907 do 1908 { 1909 *next++ = cmpt; 1910 cmpt = parser.next(); 1911 } 1912 while (cmpt.valid() && next != buf.end()); 1913 1914 if (next == buf.end()) 1915 { 1916 _M_cmpts.type(_Type::_Multi); 1917 _M_cmpts.reserve(_M_cmpts.size() + buf.size()); 1918 auto output = _M_cmpts._M_impl->end(); 1919 for (const auto& c : buf) 1920 { 1921 ::new(output++) _Cmpt(c.str, c.type, parser.offset(c)); 1922 ++_M_cmpts._M_impl->_M_size; 1923 } 1924 next = buf.begin(); 1925 } 1926 } 1927 1928 if (auto n = next - buf.begin()) 1929 { 1930 if (n == 1 && _M_cmpts.empty()) 1931 { 1932 _M_cmpts.type(buf.front().type); 1933 return; 1934 } 1935 1936 _M_cmpts.type(_Type::_Multi); 1937 _M_cmpts.reserve(_M_cmpts.size() + n, true); 1938 auto output = _M_cmpts._M_impl->end(); 1939 for (int i = 0; i < n; ++i) 1940 { 1941 const auto& c = buf[i]; 1942 ::new(output++) _Cmpt(c.str, c.type, parser.offset(c)); 1943 ++_M_cmpts._M_impl->_M_size; 1944 } 1945 } 1946 } 1947 1948 path::string_type 1949 path::_S_convert_loc(const char* __first, const char* __last, 1950 const std::locale& __loc) 1951 { 1952 #if _GLIBCXX_USE_WCHAR_T 1953 auto& __cvt = std::use_facet<codecvt<wchar_t, char, mbstate_t>>(__loc); 1954 basic_string<wchar_t> __ws; 1955 if (!__str_codecvt_in_all(__first, __last, __ws, __cvt)) 1956 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 1957 "Cannot convert character sequence", 1958 std::make_error_code(errc::illegal_byte_sequence))); 1959 return _S_convert(std::move(__ws)); 1960 #else 1961 return {__first, __last}; 1962 #endif 1963 } 1964 1965 std::size_t 1966 fs::hash_value(const path& p) noexcept 1967 { 1968 // [path.non-member] 1969 // "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)." 1970 // Equality works as if by traversing the range [begin(), end()), meaning 1971 // e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname 1972 // but need to iterate over individual elements. Use the hash_combine from 1973 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf 1974 size_t seed = 0; 1975 for (const auto& x : p) 1976 { 1977 seed ^= std::hash<path::string_type>()(x.native()) + 0x9e3779b9 1978 + (seed<<6) + (seed>>2); 1979 } 1980 return seed; 1981 } 1982 1983 struct fs::filesystem_error::_Impl 1984 { 1985 _Impl(string_view what_arg, const path& p1, const path& p2) 1986 : path1(p1), path2(p2), what(make_what(what_arg, &p1, &p2)) 1987 { } 1988 1989 _Impl(string_view what_arg, const path& p1) 1990 : path1(p1), path2(), what(make_what(what_arg, &p1, nullptr)) 1991 { } 1992 1993 _Impl(string_view what_arg) 1994 : what(make_what(what_arg, nullptr, nullptr)) 1995 { } 1996 1997 static std::string 1998 make_what(string_view s, const path* p1, const path* p2) 1999 { 2000 const std::string pstr1 = p1 ? p1->u8string() : std::string{}; 2001 const std::string pstr2 = p2 ? p2->u8string() : std::string{}; 2002 const size_t len = 18 + s.length() 2003 + (pstr1.length() ? pstr1.length() + 3 : 0) 2004 + (pstr2.length() ? pstr2.length() + 3 : 0); 2005 std::string w; 2006 w.reserve(len); 2007 w = "filesystem error: "; 2008 w += s; 2009 if (p1) 2010 { 2011 w += " ["; 2012 w += pstr1; 2013 w += ']'; 2014 if (p2) 2015 { 2016 w += " ["; 2017 w += pstr2; 2018 w += ']'; 2019 } 2020 } 2021 return w; 2022 } 2023 2024 path path1; 2025 path path2; 2026 std::string what; 2027 }; 2028 2029 template class std::__shared_ptr<const fs::filesystem_error::_Impl>; 2030 2031 fs::filesystem_error:: 2032 filesystem_error(const string& what_arg, error_code ec) 2033 : system_error(ec, what_arg), 2034 _M_impl(std::__make_shared<_Impl>(system_error::what())) 2035 { } 2036 2037 fs::filesystem_error:: 2038 filesystem_error(const string& what_arg, const path& p1, error_code ec) 2039 : system_error(ec, what_arg), 2040 _M_impl(std::__make_shared<_Impl>(system_error::what(), p1)) 2041 { } 2042 2043 fs::filesystem_error:: 2044 filesystem_error(const string& what_arg, const path& p1, const path& p2, 2045 error_code ec) 2046 : system_error(ec, what_arg), 2047 _M_impl(std::__make_shared<_Impl>(system_error::what(), p1, p2)) 2048 { } 2049 2050 fs::filesystem_error::~filesystem_error() = default; 2051 2052 const fs::path& 2053 fs::filesystem_error::path1() const noexcept 2054 { return _M_impl->path1; } 2055 2056 const fs::path& 2057 fs::filesystem_error::path2() const noexcept 2058 { return _M_impl->path2; } 2059 2060 const char* 2061 fs::filesystem_error::what() const noexcept 2062 { return _M_impl->what.c_str(); } 2063