1 // Class filesystem::path -*- C++ -*- 2 3 // Copyright (C) 2014-2018 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 /** @file include/bits/fs_path.h 26 * This is an internal header file, included by other library headers. 27 * Do not attempt to use it directly. @headername{filesystem} 28 */ 29 30 #ifndef _GLIBCXX_FS_PATH_H 31 #define _GLIBCXX_FS_PATH_H 1 32 33 #if __cplusplus >= 201703L 34 35 #include <utility> 36 #include <type_traits> 37 #include <vector> 38 #include <locale> 39 #include <iosfwd> 40 #include <codecvt> 41 #include <string_view> 42 #include <system_error> 43 #include <bits/stl_algobase.h> 44 #include <bits/quoted_string.h> 45 #include <bits/locale_conv.h> 46 47 #if defined(_WIN32) && !defined(__CYGWIN__) 48 # define _GLIBCXX_FILESYSTEM_IS_WINDOWS 1 49 # include <algorithm> 50 #endif 51 52 namespace std _GLIBCXX_VISIBILITY(default) 53 { 54 _GLIBCXX_BEGIN_NAMESPACE_VERSION 55 56 namespace filesystem 57 { 58 _GLIBCXX_BEGIN_NAMESPACE_CXX11 59 60 /** 61 * @ingroup filesystem 62 * @{ 63 */ 64 65 /// A filesystem path. 66 class path 67 { 68 template<typename _CharT> 69 struct __is_encoded_char : std::false_type { }; 70 71 template<typename _Iter, 72 typename _Iter_traits = std::iterator_traits<_Iter>> 73 using __is_path_iter_src 74 = __and_<__is_encoded_char<typename _Iter_traits::value_type>, 75 std::is_base_of<std::input_iterator_tag, 76 typename _Iter_traits::iterator_category>>; 77 78 template<typename _Iter> 79 static __is_path_iter_src<_Iter> 80 __is_path_src(_Iter, int); 81 82 template<typename _CharT, typename _Traits, typename _Alloc> 83 static __is_encoded_char<_CharT> 84 __is_path_src(const basic_string<_CharT, _Traits, _Alloc>&, int); 85 86 template<typename _CharT, typename _Traits> 87 static __is_encoded_char<_CharT> 88 __is_path_src(const basic_string_view<_CharT, _Traits>&, int); 89 90 template<typename _Unknown> 91 static std::false_type 92 __is_path_src(const _Unknown&, ...); 93 94 template<typename _Tp1, typename _Tp2> 95 struct __constructible_from; 96 97 template<typename _Iter> 98 struct __constructible_from<_Iter, _Iter> 99 : __is_path_iter_src<_Iter> 100 { }; 101 102 template<typename _Source> 103 struct __constructible_from<_Source, void> 104 : decltype(__is_path_src(std::declval<_Source>(), 0)) 105 { }; 106 107 template<typename _Tp1, typename _Tp2 = void> 108 using _Path = typename 109 std::enable_if<__and_<__not_<is_same<remove_cv_t<_Tp1>, path>>, 110 __not_<is_void<remove_pointer_t<_Tp1>>>, 111 __constructible_from<_Tp1, _Tp2>>::value, 112 path>::type; 113 114 template<typename _Source> 115 static _Source 116 _S_range_begin(_Source __begin) { return __begin; } 117 118 struct __null_terminated { }; 119 120 template<typename _Source> 121 static __null_terminated 122 _S_range_end(_Source) { return {}; } 123 124 template<typename _CharT, typename _Traits, typename _Alloc> 125 static const _CharT* 126 _S_range_begin(const basic_string<_CharT, _Traits, _Alloc>& __str) 127 { return __str.data(); } 128 129 template<typename _CharT, typename _Traits, typename _Alloc> 130 static const _CharT* 131 _S_range_end(const basic_string<_CharT, _Traits, _Alloc>& __str) 132 { return __str.data() + __str.size(); } 133 134 template<typename _CharT, typename _Traits> 135 static const _CharT* 136 _S_range_begin(const basic_string_view<_CharT, _Traits>& __str) 137 { return __str.data(); } 138 139 template<typename _CharT, typename _Traits> 140 static const _CharT* 141 _S_range_end(const basic_string_view<_CharT, _Traits>& __str) 142 { return __str.data() + __str.size(); } 143 144 template<typename _Tp, 145 typename _Iter = decltype(_S_range_begin(std::declval<_Tp>())), 146 typename _Val = typename std::iterator_traits<_Iter>::value_type> 147 using __value_type_is_char 148 = typename std::enable_if<std::is_same<_Val, char>::value>::type; 149 150 public: 151 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 152 typedef wchar_t value_type; 153 static constexpr value_type preferred_separator = L'\\'; 154 #else 155 typedef char value_type; 156 static constexpr value_type preferred_separator = '/'; 157 #endif 158 typedef std::basic_string<value_type> string_type; 159 160 enum format { native_format, generic_format, auto_format }; 161 162 // constructors and destructor 163 164 path() noexcept { } 165 166 path(const path& __p) = default; 167 168 path(path&& __p) noexcept 169 : _M_pathname(std::move(__p._M_pathname)), _M_type(__p._M_type) 170 { 171 if (_M_type == _Type::_Multi) 172 _M_split_cmpts(); 173 __p.clear(); 174 } 175 176 path(string_type&& __source, format = auto_format) 177 : _M_pathname(std::move(__source)) 178 { _M_split_cmpts(); } 179 180 template<typename _Source, 181 typename _Require = _Path<_Source>> 182 path(_Source const& __source, format = auto_format) 183 : _M_pathname(_S_convert(_S_range_begin(__source), 184 _S_range_end(__source))) 185 { _M_split_cmpts(); } 186 187 template<typename _InputIterator, 188 typename _Require = _Path<_InputIterator, _InputIterator>> 189 path(_InputIterator __first, _InputIterator __last, format = auto_format) 190 : _M_pathname(_S_convert(__first, __last)) 191 { _M_split_cmpts(); } 192 193 template<typename _Source, 194 typename _Require = _Path<_Source>, 195 typename _Require2 = __value_type_is_char<_Source>> 196 path(_Source const& __source, const locale& __loc, format = auto_format) 197 : _M_pathname(_S_convert_loc(_S_range_begin(__source), 198 _S_range_end(__source), __loc)) 199 { _M_split_cmpts(); } 200 201 template<typename _InputIterator, 202 typename _Require = _Path<_InputIterator, _InputIterator>, 203 typename _Require2 = __value_type_is_char<_InputIterator>> 204 path(_InputIterator __first, _InputIterator __last, const locale& __loc, 205 format = auto_format) 206 : _M_pathname(_S_convert_loc(__first, __last, __loc)) 207 { _M_split_cmpts(); } 208 209 ~path() = default; 210 211 // assignments 212 213 path& operator=(const path& __p) = default; 214 path& operator=(path&& __p) noexcept; 215 path& operator=(string_type&& __source); 216 path& assign(string_type&& __source); 217 218 template<typename _Source> 219 _Path<_Source>& 220 operator=(_Source const& __source) 221 { return *this = path(__source); } 222 223 template<typename _Source> 224 _Path<_Source>& 225 assign(_Source const& __source) 226 { return *this = path(__source); } 227 228 template<typename _InputIterator> 229 _Path<_InputIterator, _InputIterator>& 230 assign(_InputIterator __first, _InputIterator __last) 231 { return *this = path(__first, __last); } 232 233 // appends 234 235 path& operator/=(const path& __p) 236 { 237 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 238 if (__p.is_absolute() 239 || (__p.has_root_name() && __p.root_name() != root_name())) 240 operator=(__p); 241 else 242 { 243 string_type __pathname; 244 if (__p.has_root_directory()) 245 __pathname = root_name().native(); 246 else if (has_filename() || (!has_root_directory() && is_absolute())) 247 __pathname = _M_pathname + preferred_separator; 248 __pathname += __p.relative_path().native(); // XXX is this right? 249 _M_pathname.swap(__pathname); 250 _M_split_cmpts(); 251 } 252 #else 253 // Much simpler, as any path with root-name or root-dir is absolute. 254 if (__p.is_absolute()) 255 operator=(__p); 256 else 257 { 258 if (has_filename() || (_M_type == _Type::_Root_name)) 259 _M_pathname += preferred_separator; 260 _M_pathname += __p.native(); 261 _M_split_cmpts(); 262 } 263 #endif 264 return *this; 265 } 266 267 template <class _Source> 268 _Path<_Source>& 269 operator/=(_Source const& __source) 270 { return _M_append(path(__source)); } 271 272 template<typename _Source> 273 _Path<_Source>& 274 append(_Source const& __source) 275 { return _M_append(path(__source)); } 276 277 template<typename _InputIterator> 278 _Path<_InputIterator, _InputIterator>& 279 append(_InputIterator __first, _InputIterator __last) 280 { return _M_append(path(__first, __last)); } 281 282 // concatenation 283 284 path& operator+=(const path& __x); 285 path& operator+=(const string_type& __x); 286 path& operator+=(const value_type* __x); 287 path& operator+=(value_type __x); 288 path& operator+=(basic_string_view<value_type> __x); 289 290 template<typename _Source> 291 _Path<_Source>& 292 operator+=(_Source const& __x) { return concat(__x); } 293 294 template<typename _CharT> 295 _Path<_CharT*, _CharT*>& 296 operator+=(_CharT __x); 297 298 template<typename _Source> 299 _Path<_Source>& 300 concat(_Source const& __x) 301 { return *this += _S_convert(_S_range_begin(__x), _S_range_end(__x)); } 302 303 template<typename _InputIterator> 304 _Path<_InputIterator, _InputIterator>& 305 concat(_InputIterator __first, _InputIterator __last) 306 { return *this += _S_convert(__first, __last); } 307 308 // modifiers 309 310 void clear() noexcept { _M_pathname.clear(); _M_split_cmpts(); } 311 312 path& make_preferred(); 313 path& remove_filename(); 314 path& replace_filename(const path& __replacement); 315 path& replace_extension(const path& __replacement = path()); 316 317 void swap(path& __rhs) noexcept; 318 319 // native format observers 320 321 const string_type& native() const noexcept { return _M_pathname; } 322 const value_type* c_str() const noexcept { return _M_pathname.c_str(); } 323 operator string_type() const { return _M_pathname; } 324 325 template<typename _CharT, typename _Traits = std::char_traits<_CharT>, 326 typename _Allocator = std::allocator<_CharT>> 327 std::basic_string<_CharT, _Traits, _Allocator> 328 string(const _Allocator& __a = _Allocator()) const; 329 330 std::string string() const; 331 #if _GLIBCXX_USE_WCHAR_T 332 std::wstring wstring() const; 333 #endif 334 std::string u8string() const; 335 std::u16string u16string() const; 336 std::u32string u32string() const; 337 338 // generic format observers 339 template<typename _CharT, typename _Traits = std::char_traits<_CharT>, 340 typename _Allocator = std::allocator<_CharT>> 341 std::basic_string<_CharT, _Traits, _Allocator> 342 generic_string(const _Allocator& __a = _Allocator()) const; 343 344 std::string generic_string() const; 345 #if _GLIBCXX_USE_WCHAR_T 346 std::wstring generic_wstring() const; 347 #endif 348 std::string generic_u8string() const; 349 std::u16string generic_u16string() const; 350 std::u32string generic_u32string() const; 351 352 // compare 353 354 int compare(const path& __p) const noexcept; 355 int compare(const string_type& __s) const; 356 int compare(const value_type* __s) const; 357 int compare(const basic_string_view<value_type> __s) const; 358 359 // decomposition 360 361 path root_name() const; 362 path root_directory() const; 363 path root_path() const; 364 path relative_path() const; 365 path parent_path() const; 366 path filename() const; 367 path stem() const; 368 path extension() const; 369 370 // query 371 372 [[nodiscard]] bool empty() const noexcept { return _M_pathname.empty(); } 373 bool has_root_name() const; 374 bool has_root_directory() const; 375 bool has_root_path() const; 376 bool has_relative_path() const; 377 bool has_parent_path() const; 378 bool has_filename() const; 379 bool has_stem() const; 380 bool has_extension() const; 381 bool is_absolute() const { return has_root_directory(); } 382 bool is_relative() const { return !is_absolute(); } 383 384 // generation 385 path lexically_normal() const; 386 path lexically_relative(const path& base) const; 387 path lexically_proximate(const path& base) const; 388 389 // iterators 390 class iterator; 391 typedef iterator const_iterator; 392 393 iterator begin() const; 394 iterator end() const; 395 396 private: 397 enum class _Type : unsigned char { 398 _Multi, _Root_name, _Root_dir, _Filename 399 }; 400 401 path(string_type __str, _Type __type) : _M_pathname(__str), _M_type(__type) 402 { 403 __glibcxx_assert(_M_type != _Type::_Multi); 404 } 405 406 enum class _Split { _Stem, _Extension }; 407 408 path& 409 _M_append(path __p) 410 { 411 if (__p.is_absolute()) 412 operator=(std::move(__p)); 413 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 414 else if (__p.has_root_name() && __p.root_name() != root_name()) 415 operator=(std::move(__p)); 416 #endif 417 else 418 operator/=(const_cast<const path&>(__p)); 419 return *this; 420 } 421 422 pair<const string_type*, size_t> _M_find_extension() const; 423 424 template<typename _CharT> 425 struct _Cvt; 426 427 static string_type 428 _S_convert(value_type* __src, __null_terminated) 429 { return string_type(__src); } 430 431 static string_type 432 _S_convert(const value_type* __src, __null_terminated) 433 { return string_type(__src); } 434 435 template<typename _Iter> 436 static string_type 437 _S_convert(_Iter __first, _Iter __last) 438 { 439 using __value_type = typename std::iterator_traits<_Iter>::value_type; 440 return _Cvt<typename remove_cv<__value_type>::type>:: 441 _S_convert(__first, __last); 442 } 443 444 template<typename _InputIterator> 445 static string_type 446 _S_convert(_InputIterator __src, __null_terminated) 447 { 448 using _Tp = typename std::iterator_traits<_InputIterator>::value_type; 449 std::basic_string<typename remove_cv<_Tp>::type> __tmp; 450 for (; *__src != _Tp{}; ++__src) 451 __tmp.push_back(*__src); 452 return _S_convert(__tmp.c_str(), __tmp.c_str() + __tmp.size()); 453 } 454 455 static string_type 456 _S_convert_loc(const char* __first, const char* __last, 457 const std::locale& __loc); 458 459 template<typename _Iter> 460 static string_type 461 _S_convert_loc(_Iter __first, _Iter __last, const std::locale& __loc) 462 { 463 const std::string __str(__first, __last); 464 return _S_convert_loc(__str.data(), __str.data()+__str.size(), __loc); 465 } 466 467 template<typename _InputIterator> 468 static string_type 469 _S_convert_loc(_InputIterator __src, __null_terminated, 470 const std::locale& __loc) 471 { 472 std::string __tmp; 473 while (*__src != '\0') 474 __tmp.push_back(*__src++); 475 return _S_convert_loc(__tmp.data(), __tmp.data()+__tmp.size(), __loc); 476 } 477 478 template<typename _CharT, typename _Traits, typename _Allocator> 479 static basic_string<_CharT, _Traits, _Allocator> 480 _S_str_convert(const string_type&, const _Allocator& __a); 481 482 static bool _S_is_dir_sep(value_type __ch) 483 { 484 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 485 return __ch == L'/' || __ch == preferred_separator; 486 #else 487 return __ch == '/'; 488 #endif 489 } 490 491 void _M_split_cmpts(); 492 void _M_trim(); 493 void _M_add_root_name(size_t __n); 494 void _M_add_root_dir(size_t __pos); 495 void _M_add_filename(size_t __pos, size_t __n); 496 497 string_type _M_pathname; 498 499 struct _Cmpt; 500 using _List = _GLIBCXX_STD_C::vector<_Cmpt>; 501 _List _M_cmpts; // empty unless _M_type == _Type::_Multi 502 _Type _M_type = _Type::_Filename; 503 }; 504 505 template<> 506 struct path::__is_encoded_char<char> : std::true_type 507 { using value_type = char; }; 508 509 template<> 510 struct path::__is_encoded_char<wchar_t> : std::true_type 511 { using value_type = wchar_t; }; 512 513 template<> 514 struct path::__is_encoded_char<char16_t> : std::true_type 515 { using value_type = char16_t; }; 516 517 template<> 518 struct path::__is_encoded_char<char32_t> : std::true_type 519 { using value_type = char32_t; }; 520 521 template<typename _Tp> 522 struct path::__is_encoded_char<const _Tp> : __is_encoded_char<_Tp> { }; 523 524 inline void swap(path& __lhs, path& __rhs) noexcept { __lhs.swap(__rhs); } 525 526 size_t hash_value(const path& __p) noexcept; 527 528 /// Compare paths 529 inline bool operator<(const path& __lhs, const path& __rhs) noexcept 530 { return __lhs.compare(__rhs) < 0; } 531 532 /// Compare paths 533 inline bool operator<=(const path& __lhs, const path& __rhs) noexcept 534 { return !(__rhs < __lhs); } 535 536 /// Compare paths 537 inline bool operator>(const path& __lhs, const path& __rhs) noexcept 538 { return __rhs < __lhs; } 539 540 /// Compare paths 541 inline bool operator>=(const path& __lhs, const path& __rhs) noexcept 542 { return !(__lhs < __rhs); } 543 544 /// Compare paths 545 inline bool operator==(const path& __lhs, const path& __rhs) noexcept 546 { return __lhs.compare(__rhs) == 0; } 547 548 /// Compare paths 549 inline bool operator!=(const path& __lhs, const path& __rhs) noexcept 550 { return !(__lhs == __rhs); } 551 552 /// Append one path to another 553 inline path operator/(const path& __lhs, const path& __rhs) 554 { 555 path __result(__lhs); 556 __result /= __rhs; 557 return __result; 558 } 559 560 /// Write a path to a stream 561 template<typename _CharT, typename _Traits> 562 basic_ostream<_CharT, _Traits>& 563 operator<<(basic_ostream<_CharT, _Traits>& __os, const path& __p) 564 { 565 auto __tmp = __p.string<_CharT, _Traits>(); 566 using __quoted_string 567 = std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>; 568 __os << __quoted_string{__tmp, '"', '\\'}; 569 return __os; 570 } 571 572 /// Read a path from a stream 573 template<typename _CharT, typename _Traits> 574 basic_istream<_CharT, _Traits>& 575 operator>>(basic_istream<_CharT, _Traits>& __is, path& __p) 576 { 577 basic_string<_CharT, _Traits> __tmp; 578 using __quoted_string 579 = std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>; 580 if (__is >> __quoted_string{ __tmp, '"', '\\' }) 581 __p = std::move(__tmp); 582 return __is; 583 } 584 585 template<typename _Source> 586 inline auto 587 u8path(const _Source& __source) 588 -> decltype(filesystem::path(__source, std::locale::classic())) 589 { 590 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 591 const std::string __u8str{__source}; 592 return std::filesystem::u8path(__u8str.begin(), __u8str.end()); 593 #else 594 return path{ __source }; 595 #endif 596 } 597 598 template<typename _InputIterator> 599 inline auto 600 u8path(_InputIterator __first, _InputIterator __last) 601 -> decltype(filesystem::path(__first, __last, std::locale::classic())) 602 { 603 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 604 codecvt_utf8<value_type> __cvt; 605 string_type __tmp; 606 if (__str_codecvt_in(__first, __last, __tmp, __cvt)) 607 return path{ __tmp }; 608 else 609 return {}; 610 #else 611 return path{ __first, __last }; 612 #endif 613 } 614 615 class filesystem_error : public std::system_error 616 { 617 public: 618 filesystem_error(const string& __what_arg, error_code __ec) 619 : system_error(__ec, __what_arg) { } 620 621 filesystem_error(const string& __what_arg, const path& __p1, 622 error_code __ec) 623 : system_error(__ec, __what_arg), _M_path1(__p1) { } 624 625 filesystem_error(const string& __what_arg, const path& __p1, 626 const path& __p2, error_code __ec) 627 : system_error(__ec, __what_arg), _M_path1(__p1), _M_path2(__p2) 628 { } 629 630 ~filesystem_error(); 631 632 const path& path1() const noexcept { return _M_path1; } 633 const path& path2() const noexcept { return _M_path2; } 634 const char* what() const noexcept { return _M_what.c_str(); } 635 636 private: 637 std::string _M_gen_what(); 638 639 path _M_path1; 640 path _M_path2; 641 std::string _M_what = _M_gen_what(); 642 }; 643 644 struct path::_Cmpt : path 645 { 646 _Cmpt(string_type __s, _Type __t, size_t __pos) 647 : path(std::move(__s), __t), _M_pos(__pos) { } 648 649 _Cmpt() : _M_pos(-1) { } 650 651 size_t _M_pos; 652 }; 653 654 // specialize _Cvt for degenerate 'noconv' case 655 template<> 656 struct path::_Cvt<path::value_type> 657 { 658 template<typename _Iter> 659 static string_type 660 _S_convert(_Iter __first, _Iter __last) 661 { return string_type{__first, __last}; } 662 }; 663 664 template<typename _CharT> 665 struct path::_Cvt 666 { 667 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 668 static string_type 669 _S_wconvert(const char* __f, const char* __l, true_type) 670 { 671 using _Cvt = std::codecvt<wchar_t, char, mbstate_t>; 672 const auto& __cvt = std::use_facet<_Cvt>(std::locale{}); 673 std::wstring __wstr; 674 if (__str_codecvt_in(__f, __l, __wstr, __cvt)) 675 return __wstr; 676 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 677 "Cannot convert character sequence", 678 std::make_error_code(errc::illegal_byte_sequence))); 679 } 680 681 static string_type 682 _S_wconvert(const _CharT* __f, const _CharT* __l, false_type) 683 { 684 std::codecvt_utf8<_CharT> __cvt; 685 std::string __str; 686 if (__str_codecvt_out(__f, __l, __str, __cvt)) 687 { 688 const char* __f2 = __str.data(); 689 const char* __l2 = __f2 + __str.size(); 690 std::codecvt_utf8<wchar_t> __wcvt; 691 std::wstring __wstr; 692 if (__str_codecvt_in(__f2, __l2, __wstr, __wcvt)) 693 return __wstr; 694 } 695 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 696 "Cannot convert character sequence", 697 std::make_error_code(errc::illegal_byte_sequence))); 698 } 699 700 static string_type 701 _S_convert(const _CharT* __f, const _CharT* __l) 702 { 703 return _S_wconvert(__f, __l, is_same<_CharT, char>{}); 704 } 705 #else 706 static string_type 707 _S_convert(const _CharT* __f, const _CharT* __l) 708 { 709 std::codecvt_utf8<_CharT> __cvt; 710 std::string __str; 711 if (__str_codecvt_out(__f, __l, __str, __cvt)) 712 return __str; 713 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 714 "Cannot convert character sequence", 715 std::make_error_code(errc::illegal_byte_sequence))); 716 } 717 #endif 718 719 static string_type 720 _S_convert(_CharT* __f, _CharT* __l) 721 { 722 return _S_convert(const_cast<const _CharT*>(__f), 723 const_cast<const _CharT*>(__l)); 724 } 725 726 template<typename _Iter> 727 static string_type 728 _S_convert(_Iter __first, _Iter __last) 729 { 730 const std::basic_string<_CharT> __str(__first, __last); 731 return _S_convert(__str.data(), __str.data() + __str.size()); 732 } 733 734 template<typename _Iter, typename _Cont> 735 static string_type 736 _S_convert(__gnu_cxx::__normal_iterator<_Iter, _Cont> __first, 737 __gnu_cxx::__normal_iterator<_Iter, _Cont> __last) 738 { return _S_convert(__first.base(), __last.base()); } 739 }; 740 741 /// An iterator for the components of a path 742 class path::iterator 743 { 744 public: 745 using difference_type = std::ptrdiff_t; 746 using value_type = path; 747 using reference = const path&; 748 using pointer = const path*; 749 using iterator_category = std::bidirectional_iterator_tag; 750 751 iterator() : _M_path(nullptr), _M_cur(), _M_at_end() { } 752 753 iterator(const iterator&) = default; 754 iterator& operator=(const iterator&) = default; 755 756 reference operator*() const; 757 pointer operator->() const { return std::__addressof(**this); } 758 759 iterator& operator++(); 760 iterator operator++(int) { auto __tmp = *this; ++*this; return __tmp; } 761 762 iterator& operator--(); 763 iterator operator--(int) { auto __tmp = *this; --*this; return __tmp; } 764 765 friend bool operator==(const iterator& __lhs, const iterator& __rhs) 766 { return __lhs._M_equals(__rhs); } 767 768 friend bool operator!=(const iterator& __lhs, const iterator& __rhs) 769 { return !__lhs._M_equals(__rhs); } 770 771 private: 772 friend class path; 773 774 iterator(const path* __path, path::_List::const_iterator __iter) 775 : _M_path(__path), _M_cur(__iter), _M_at_end() 776 { } 777 778 iterator(const path* __path, bool __at_end) 779 : _M_path(__path), _M_cur(), _M_at_end(__at_end) 780 { } 781 782 bool _M_equals(iterator) const; 783 784 const path* _M_path; 785 path::_List::const_iterator _M_cur; 786 bool _M_at_end; // only used when type != _Multi 787 }; 788 789 790 inline path& 791 path::operator=(path&& __p) noexcept 792 { 793 if (&__p == this) 794 return *this; 795 796 _M_pathname = std::move(__p._M_pathname); 797 _M_cmpts = std::move(__p._M_cmpts); 798 _M_type = __p._M_type; 799 __p.clear(); 800 return *this; 801 } 802 803 inline path& 804 path::operator=(string_type&& __source) 805 { return *this = path(std::move(__source)); } 806 807 inline path& 808 path::assign(string_type&& __source) 809 { return *this = path(std::move(__source)); } 810 811 inline path& 812 path::operator+=(const path& __p) 813 { 814 return operator+=(__p.native()); 815 } 816 817 inline path& 818 path::operator+=(const string_type& __x) 819 { 820 _M_pathname += __x; 821 _M_split_cmpts(); 822 return *this; 823 } 824 825 inline path& 826 path::operator+=(const value_type* __x) 827 { 828 _M_pathname += __x; 829 _M_split_cmpts(); 830 return *this; 831 } 832 833 inline path& 834 path::operator+=(value_type __x) 835 { 836 _M_pathname += __x; 837 _M_split_cmpts(); 838 return *this; 839 } 840 841 inline path& 842 path::operator+=(basic_string_view<value_type> __x) 843 { 844 _M_pathname.append(__x.data(), __x.size()); 845 _M_split_cmpts(); 846 return *this; 847 } 848 849 template<typename _CharT> 850 inline path::_Path<_CharT*, _CharT*>& 851 path::operator+=(_CharT __x) 852 { 853 auto* __addr = std::__addressof(__x); 854 return concat(__addr, __addr + 1); 855 } 856 857 inline path& 858 path::make_preferred() 859 { 860 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 861 std::replace(_M_pathname.begin(), _M_pathname.end(), L'/', 862 preferred_separator); 863 #endif 864 return *this; 865 } 866 867 inline void path::swap(path& __rhs) noexcept 868 { 869 _M_pathname.swap(__rhs._M_pathname); 870 _M_cmpts.swap(__rhs._M_cmpts); 871 std::swap(_M_type, __rhs._M_type); 872 } 873 874 template<typename _CharT, typename _Traits, typename _Allocator> 875 std::basic_string<_CharT, _Traits, _Allocator> 876 path::_S_str_convert(const string_type& __str, const _Allocator& __a) 877 { 878 if (__str.size() == 0) 879 return std::basic_string<_CharT, _Traits, _Allocator>(__a); 880 881 const value_type* __first = __str.data(); 882 const value_type* __last = __first + __str.size(); 883 884 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 885 using _CharAlloc = __alloc_rebind<_Allocator, char>; 886 using _String = basic_string<char, char_traits<char>, _CharAlloc>; 887 using _WString = basic_string<_CharT, _Traits, _Allocator>; 888 889 // use codecvt_utf8<wchar_t> to convert native string to UTF-8 890 codecvt_utf8<value_type> __cvt; 891 _String __u8str{_CharAlloc{__a}}; 892 if (__str_codecvt_out(__first, __last, __u8str, __cvt)) 893 { 894 if constexpr (is_same_v<_CharT, char>) 895 return __u8str; 896 else 897 { 898 _WString __wstr; 899 // use codecvt_utf8<_CharT> to convert UTF-8 to wide string 900 codecvt_utf8<_CharT> __cvt; 901 const char* __f = __u8str.data(); 902 const char* __l = __f + __u8str.size(); 903 if (__str_codecvt_in(__f, __l, __wstr, __cvt)) 904 return __wstr; 905 } 906 } 907 #else 908 codecvt_utf8<_CharT> __cvt; 909 basic_string<_CharT, _Traits, _Allocator> __wstr{__a}; 910 if (__str_codecvt_in(__first, __last, __wstr, __cvt)) 911 return __wstr; 912 #endif 913 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 914 "Cannot convert character sequence", 915 std::make_error_code(errc::illegal_byte_sequence))); 916 } 917 918 template<typename _CharT, typename _Traits, typename _Allocator> 919 inline basic_string<_CharT, _Traits, _Allocator> 920 path::string(const _Allocator& __a) const 921 { 922 if constexpr (is_same_v<_CharT, value_type>) 923 #if _GLIBCXX_USE_CXX11_ABI 924 return { _M_pathname, __a }; 925 #else 926 return { _M_pathname, string_type::size_type(0), __a }; 927 #endif 928 else 929 return _S_str_convert<_CharT, _Traits>(_M_pathname, __a); 930 } 931 932 inline std::string 933 path::string() const { return string<char>(); } 934 935 #if _GLIBCXX_USE_WCHAR_T 936 inline std::wstring 937 path::wstring() const { return string<wchar_t>(); } 938 #endif 939 940 inline std::string 941 path::u8string() const 942 { 943 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 944 std::string __str; 945 // convert from native encoding to UTF-8 946 codecvt_utf8<value_type> __cvt; 947 const value_type* __first = _M_pathname.data(); 948 const value_type* __last = __first + _M_pathname.size(); 949 if (__str_codecvt_out(__first, __last, __str, __cvt)) 950 return __str; 951 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 952 "Cannot convert character sequence", 953 std::make_error_code(errc::illegal_byte_sequence))); 954 #else 955 return _M_pathname; 956 #endif 957 } 958 959 inline std::u16string 960 path::u16string() const { return string<char16_t>(); } 961 962 inline std::u32string 963 path::u32string() const { return string<char32_t>(); } 964 965 template<typename _CharT, typename _Traits, typename _Allocator> 966 inline std::basic_string<_CharT, _Traits, _Allocator> 967 path::generic_string(const _Allocator& __a) const 968 { 969 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 970 const value_type __slash = L'/'; 971 #else 972 const value_type __slash = '/'; 973 #endif 974 string_type __str(__a); 975 976 if (_M_type == _Type::_Root_dir) 977 __str.assign(1, __slash); 978 else 979 { 980 __str.reserve(_M_pathname.size()); 981 bool __add_slash = false; 982 for (auto& __elem : *this) 983 { 984 if (__add_slash) 985 __str += __slash; 986 __str += __elem._M_pathname; 987 __add_slash = __elem._M_type == _Type::_Filename; 988 } 989 } 990 991 if constexpr (is_same_v<_CharT, value_type>) 992 return __str; 993 else 994 return _S_str_convert<_CharT, _Traits>(__str, __a); 995 } 996 997 inline std::string 998 path::generic_string() const 999 { return generic_string<char>(); } 1000 1001 #if _GLIBCXX_USE_WCHAR_T 1002 inline std::wstring 1003 path::generic_wstring() const 1004 { return generic_string<wchar_t>(); } 1005 #endif 1006 1007 inline std::string 1008 path::generic_u8string() const 1009 { return generic_string(); } 1010 1011 inline std::u16string 1012 path::generic_u16string() const 1013 { return generic_string<char16_t>(); } 1014 1015 inline std::u32string 1016 path::generic_u32string() const 1017 { return generic_string<char32_t>(); } 1018 1019 inline int 1020 path::compare(const string_type& __s) const { return compare(path(__s)); } 1021 1022 inline int 1023 path::compare(const value_type* __s) const { return compare(path(__s)); } 1024 1025 inline int 1026 path::compare(basic_string_view<value_type> __s) const 1027 { return compare(path(__s)); } 1028 1029 inline path 1030 path::filename() const 1031 { 1032 if (empty()) 1033 return {}; 1034 else if (_M_type == _Type::_Filename) 1035 return *this; 1036 else if (_M_type == _Type::_Multi) 1037 { 1038 if (_M_pathname.back() == preferred_separator) 1039 return {}; 1040 auto& __last = *--end(); 1041 if (__last._M_type == _Type::_Filename) 1042 return __last; 1043 } 1044 return {}; 1045 } 1046 1047 inline path 1048 path::stem() const 1049 { 1050 auto ext = _M_find_extension(); 1051 if (ext.first && ext.second != 0) 1052 return path{ext.first->substr(0, ext.second)}; 1053 return {}; 1054 } 1055 1056 inline path 1057 path::extension() const 1058 { 1059 auto ext = _M_find_extension(); 1060 if (ext.first && ext.second != string_type::npos) 1061 return path{ext.first->substr(ext.second)}; 1062 return {}; 1063 } 1064 1065 inline bool 1066 path::has_stem() const 1067 { 1068 auto ext = _M_find_extension(); 1069 return ext.first && ext.second != 0; 1070 } 1071 1072 inline bool 1073 path::has_extension() const 1074 { 1075 auto ext = _M_find_extension(); 1076 return ext.first && ext.second != string_type::npos; 1077 } 1078 1079 inline path::iterator 1080 path::begin() const 1081 { 1082 if (_M_type == _Type::_Multi) 1083 return iterator(this, _M_cmpts.begin()); 1084 return iterator(this, empty()); 1085 } 1086 1087 inline path::iterator 1088 path::end() const 1089 { 1090 if (_M_type == _Type::_Multi) 1091 return iterator(this, _M_cmpts.end()); 1092 return iterator(this, true); 1093 } 1094 1095 inline path::iterator& 1096 path::iterator::operator++() 1097 { 1098 __glibcxx_assert(_M_path != nullptr); 1099 if (_M_path->_M_type == _Type::_Multi) 1100 { 1101 __glibcxx_assert(_M_cur != _M_path->_M_cmpts.end()); 1102 ++_M_cur; 1103 } 1104 else 1105 { 1106 __glibcxx_assert(!_M_at_end); 1107 _M_at_end = true; 1108 } 1109 return *this; 1110 } 1111 1112 inline path::iterator& 1113 path::iterator::operator--() 1114 { 1115 __glibcxx_assert(_M_path != nullptr); 1116 if (_M_path->_M_type == _Type::_Multi) 1117 { 1118 __glibcxx_assert(_M_cur != _M_path->_M_cmpts.begin()); 1119 --_M_cur; 1120 } 1121 else 1122 { 1123 __glibcxx_assert(_M_at_end); 1124 _M_at_end = false; 1125 } 1126 return *this; 1127 } 1128 1129 inline path::iterator::reference 1130 path::iterator::operator*() const 1131 { 1132 __glibcxx_assert(_M_path != nullptr); 1133 if (_M_path->_M_type == _Type::_Multi) 1134 { 1135 __glibcxx_assert(_M_cur != _M_path->_M_cmpts.end()); 1136 return *_M_cur; 1137 } 1138 return *_M_path; 1139 } 1140 1141 inline bool 1142 path::iterator::_M_equals(iterator __rhs) const 1143 { 1144 if (_M_path != __rhs._M_path) 1145 return false; 1146 if (_M_path == nullptr) 1147 return true; 1148 if (_M_path->_M_type == path::_Type::_Multi) 1149 return _M_cur == __rhs._M_cur; 1150 return _M_at_end == __rhs._M_at_end; 1151 } 1152 1153 // @} group filesystem 1154 _GLIBCXX_END_NAMESPACE_CXX11 1155 } // namespace filesystem 1156 1157 _GLIBCXX_END_NAMESPACE_VERSION 1158 } // namespace std 1159 1160 #endif // C++17 1161 1162 #endif // _GLIBCXX_FS_PATH_H 1163