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