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