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