1 // Class filesystem::path -*- C++ -*-
3 // Copyright (C) 2014-2024 Free Software Foundation, Inc.
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)
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.
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.
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/>.
25 #ifndef _GLIBCXX_USE_CXX11_ABI
26 # define _GLIBCXX_USE_CXX11_ABI 1
30 // Interpret "//x" as a root-name, not root-dir + filename
31 # define SLASHSLASH_IS_ROOTNAME 1
37 #include <bits/stl_uninitialized.h>
38 #include <ext/numeric_traits.h> // __gnu_cxx::__int_traits
40 namespace fs
= std::filesystem
;
43 static inline bool is_dir_sep(path::value_type ch
)
45 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
46 return ch
== L
'/' || ch
== path::preferred_separator
;
52 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
53 static inline bool is_disk_designator(std::wstring_view s
)
55 return s
.length() == 2 && s
[1] == L
':';
61 using string_view_type
= std::basic_string_view
<value_type
>;
66 _Type type
= _Type::_Multi
;
68 bool valid() const { return type
!= _Type::_Multi
; }
71 string_view_type input
;
72 string_view_type::size_type pos
= 0;
74 _Type last_type
= _Type::_Multi
;
76 _Parser(string_view_type s
, size_t o
= 0) : input(s
), origin(o
) { }
78 pair
<cmpt
, cmpt
> root_path() noexcept
81 pair
<cmpt
, cmpt
> root
;
83 const size_t len
= input
.size();
85 // look for root name or root directory
86 if (len
&& is_dir_sep(input
[0]))
88 #if SLASHSLASH_IS_ROOTNAME
89 // look for root name, such as "//foo"
90 if (len
> 2 && input
[1] == input
[0])
92 if (!is_dir_sep(input
[2]))
94 // got root name, find its end
96 while (pos
< len
&& !is_dir_sep(input
[pos
]))
98 root
.first
.str
= input
.substr(0, pos
);
99 root
.first
.type
= _Type::_Root_name
;
101 if (pos
< len
) // also got root directory
103 root
.second
.str
= input
.substr(pos
, 1);
104 root
.second
.type
= _Type::_Root_dir
;
110 // got something like "///foo" which is just a root directory
111 // composed of multiple redundant directory separators
112 root
.first
.str
= input
.substr(0, 1);
113 root
.first
.type
= _Type::_Root_dir
;
120 root
.first
.str
= input
.substr(0, 1);
121 root
.first
.type
= _Type::_Root_dir
;
124 // Find the start of the first filename
125 while (pos
< len
&& is_dir_sep(input
[pos
]))
128 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
129 else if (is_disk_designator(input
.substr(0, 2)))
131 // got disk designator
132 root
.first
.str
= input
.substr(0, 2);
133 root
.first
.type
= _Type::_Root_name
;
134 if (len
> 2 && is_dir_sep(input
[2]))
136 root
.second
.str
= input
.substr(2, 1);
137 root
.second
.type
= _Type::_Root_dir
;
139 pos
= input
.find_first_not_of(L
"/\\", 2);
143 if (root
.second
.valid())
144 last_type
= root
.second
.type
;
146 last_type
= root
.first
.type
;
153 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
154 string_view_type sep
= L
"/\\";
159 const int last_pos
= pos
;
162 if (pos
!= input
.npos
)
164 pos
= input
.find_first_not_of(sep
, pos
);
165 if (pos
!= input
.npos
)
167 const auto end
= input
.find_first_of(sep
, pos
);
168 f
.str
= input
.substr(pos
, end
- pos
);
169 f
.type
= _Type::_Filename
;
172 else if (last_type
== _Type::_Filename
173 || (last_pos
== 0 && !input
.empty()))
175 // [fs.path.itr]/4 An empty element, if trailing non-root
176 // directory-separator present.
177 __glibcxx_assert(is_dir_sep(input
.back()));
178 f
.str
= input
.substr(input
.length(), 0);
179 f
.type
= _Type::_Filename
;
186 string_view_type::size_type
187 offset(const cmpt
& c
) const noexcept
188 { return origin
+ c
.str
.data() - input
.data(); }
192 path::path(basic_string_view
<value_type
> __str
, _Type __type
)
195 __glibcxx_assert(__type
!= _Type::_Multi
);
196 _M_cmpts
.type(__type
);
200 path::_Cmpt::_Cmpt(basic_string_view
<value_type
> __s
, _Type __t
, size_t __pos
)
201 : path(__s
, __t
), _M_pos(__pos
)
204 struct path::_List::_Impl
206 using value_type
= _Cmpt
;
208 _Impl(int cap
) : _M_size(0), _M_capacity(cap
) { }
210 alignas(value_type
) int _M_size
;
213 using iterator
= value_type
*;
214 using const_iterator
= const value_type
*;
216 iterator
begin() { return reinterpret_cast<value_type
*>(this + 1); }
217 iterator
end() { return begin() + size(); }
219 const_iterator
begin() const
220 { return reinterpret_cast<const value_type
*>(this + 1); }
221 const_iterator
end() const { return begin() + size(); }
223 const value_type
& front() const { return *begin(); }
224 const value_type
& back() const { return end()[-1]; }
226 int size() const { return _M_size
; }
227 int capacity() const { return _M_capacity
; }
228 bool empty() const { return _M_size
== 0; }
230 void clear() { std::destroy_n(begin(), _M_size
); _M_size
= 0; }
238 void _M_erase_from(const_iterator pos
)
240 iterator first
= begin() + (pos
- begin());
241 iterator last
= end();
242 std::destroy(first
, last
);
243 _M_size
-= last
- first
;
246 unique_ptr
<_Impl
, _Impl_deleter
> copy() const
248 const auto n
= size();
249 void* p
= ::operator new(sizeof(_Impl
) + n
* sizeof(value_type
));
250 unique_ptr
<_Impl
, _Impl_deleter
> newptr(::new (p
) _Impl
{n
});
251 std::uninitialized_copy_n(begin(), n
, newptr
->begin());
256 // Clear the lowest two bits from the pointer (i.e. remove the _Type value)
257 static _Impl
* notype(_Impl
* p
)
259 constexpr uintptr_t mask
= ~(uintptr_t)0x3;
260 return reinterpret_cast<_Impl
*>(reinterpret_cast<uintptr_t>(p
) & mask
);
264 void path::_List::_Impl_deleter::operator()(_Impl
* p
) const noexcept
266 p
= _Impl::notype(p
);
269 __glibcxx_assert(p
->_M_size
<= p
->_M_capacity
);
271 ::operator delete(p
, sizeof(*p
) + p
->_M_capacity
* sizeof(value_type
));
275 path::_List::_List() : _M_impl(reinterpret_cast<_Impl
*>(_Type::_Filename
)) { }
277 path::_List::_List(const _List
& other
)
280 _M_impl
= other
._M_impl
->copy();
286 path::_List::operator=(const _List
& other
)
290 // copy in-place if there is capacity
291 const int newsize
= other
._M_impl
->size();
292 auto impl
= _Impl::notype(_M_impl
.get());
293 if (impl
&& impl
->capacity() >= newsize
)
295 const int oldsize
= impl
->_M_size
;
296 auto to
= impl
->begin();
297 auto from
= other
._M_impl
->begin();
298 const int minsize
= std::min(newsize
, oldsize
);
299 for (int i
= 0; i
< minsize
; ++i
)
300 to
[i
]._M_pathname
.reserve(from
[i
]._M_pathname
.length());
301 if (newsize
> oldsize
)
303 std::uninitialized_copy_n(from
+ oldsize
, newsize
- oldsize
,
305 impl
->_M_size
= newsize
;
307 else if (newsize
< oldsize
)
308 impl
->_M_erase_from(impl
->begin() + newsize
);
309 std::copy_n(from
, minsize
, to
);
313 _M_impl
= other
._M_impl
->copy();
324 path::_List::type(_Type t
) noexcept
326 auto val
= reinterpret_cast<uintptr_t>(_Impl::notype(_M_impl
.release()));
327 _M_impl
.reset(reinterpret_cast<_Impl
*>(val
| (unsigned char)t
));
331 path::_List::size() const noexcept
333 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
339 path::_List::capacity() const noexcept
341 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
342 return ptr
->capacity();
347 path::_List::empty() const noexcept
353 path::_List::begin() noexcept
356 __glibcxx_assert(!empty());
357 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
363 path::_List::end() noexcept
366 __glibcxx_assert(!empty());
367 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
373 path::_List::begin() const noexcept
376 __glibcxx_assert(!empty());
377 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
383 path::_List::end() const noexcept
386 __glibcxx_assert(!empty());
387 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
393 path::_List::front() noexcept
396 return *_M_impl
->begin();
400 path::_List::back() noexcept
403 return _M_impl
->begin()[_M_impl
->size() - 1];
407 path::_List::front() const noexcept
410 return *_M_impl
->begin();
414 path::_List::back() const noexcept
417 return _M_impl
->begin()[_M_impl
->size() - 1];
421 path::_List::pop_back()
423 __glibcxx_assert(size() > 0);
428 path::_List::_M_erase_from(const_iterator pos
)
430 _M_impl
->_M_erase_from(pos
);
436 if (auto ptr
= _Impl::notype(_M_impl
.get()))
441 path::_List::reserve(int newcap
, bool exact
= false)
443 // __glibcxx_assert(type() == _Type::_Multi);
445 _Impl
* curptr
= _Impl::notype(_M_impl
.get());
447 int curcap
= curptr
? curptr
->capacity() : 0;
453 const int nextcap
= curcap
+ curcap
/ 2;
454 if (newcap
< nextcap
)
458 using __gnu_cxx::__int_traits
;
459 // Nobody should need paths with this many components.
460 if (newcap
>= __int_traits
<int>::__max
/ 4)
461 std::__throw_bad_alloc();
464 if constexpr (__int_traits
<int>::__max
>= __int_traits
<size_t>::__max
)
467 if (__builtin_mul_overflow(newcap
, sizeof(value_type
), &components
)
468 || __builtin_add_overflow(sizeof(_Impl
), components
, &bytes
))
469 std::__throw_bad_alloc();
471 else // This won't overflow, even for 20-bit size_t on msp430.
472 bytes
= sizeof(_Impl
) + newcap
* sizeof(value_type
);
474 void* p
= ::operator new(bytes
);
475 std::unique_ptr
<_Impl
, _Impl_deleter
> newptr(::new(p
) _Impl
{newcap
});
476 const int cursize
= curptr
? curptr
->size() : 0;
479 std::uninitialized_move_n(curptr
->begin(), cursize
, newptr
->begin());
480 newptr
->_M_size
= cursize
;
482 std::swap(newptr
, _M_impl
);
487 path::operator=(const path
& p
)
489 if (&p
== this) [[__unlikely__
]]
492 _M_pathname
.reserve(p
._M_pathname
.length());
493 _M_cmpts
= p
._M_cmpts
; // might throw
494 _M_pathname
= p
._M_pathname
; // won't throw because we reserved enough space
499 path::operator/=(const path
& __p
)
501 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
502 if (__p
.is_absolute()
503 || (__p
.has_root_name() && __p
.root_name() != root_name()))
504 return operator=(__p
);
506 basic_string_view
<value_type
> __lhs
= _M_pathname
;
507 bool __add_sep
= false;
509 if (__p
.has_root_directory())
511 // Remove any root directory and relative path
512 if (_M_type() != _Type::_Root_name
)
514 if (!_M_cmpts
.empty()
515 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
516 __lhs
= _M_cmpts
.front()._M_pathname
;
521 else if (has_filename() || (!has_root_directory() && is_absolute()))
524 basic_string_view
<value_type
> __rhs
= __p
._M_pathname
;
525 // Omit any root-name from the generic format pathname:
526 if (__p
._M_type() == _Type::_Root_name
)
528 else if (!__p
._M_cmpts
.empty()
529 && __p
._M_cmpts
.front()._M_type() == _Type::_Root_name
)
530 __rhs
.remove_prefix(__p
._M_cmpts
.front()._M_pathname
.size());
532 const size_t __len
= __lhs
.size() + (int)__add_sep
+ __rhs
.size();
533 const int __maxcmpts
= _M_cmpts
.size() + __p
._M_cmpts
.size();
534 if (_M_pathname
.capacity() < __len
|| _M_cmpts
.capacity() < __maxcmpts
)
536 // Construct new path and swap (strong exception-safety guarantee).
538 __tmp
.reserve(__len
);
541 __tmp
+= preferred_separator
;
543 path __newp
= std::move(__tmp
);
550 _M_pathname
+= preferred_separator
;
551 _M_pathname
+= __rhs
;
560 // try to restore original state
561 _M_pathname
.resize(__lhs
.length());
566 // give up, basic exception safety guarantee only:
568 __throw_exception_again
;
573 // POSIX version is simpler than the specification in the standard,
574 // as any path with root-name or root-dir is absolute.
576 if (__p
.is_absolute() || this->empty())
578 return operator=(__p
);
581 using string_view_type
= basic_string_view
<value_type
>;
583 string_view_type sep
;
585 sep
= { &preferred_separator
, 1 }; // need to add a separator
586 #if SLASHSLASH_IS_ROOTNAME
587 else if (_M_type() == _Type::_Root_name
) // root-name with no root-dir
588 sep
= { &preferred_separator
, 1 }; // need to add a separator
590 else if (__p
.empty())
591 return *this; // nothing to do
593 const auto orig_pathlen
= _M_pathname
.length();
594 const auto orig_size
= _M_cmpts
.size();
595 const auto orig_type
= _M_type();
598 if (_M_type() == _Type::_Multi
)
599 capacity
+= _M_cmpts
.size();
602 if (__p
._M_type() == _Type::_Multi
)
603 capacity
+= __p
._M_cmpts
.size();
604 else if (!__p
.empty() || !sep
.empty())
606 #if SLASHSLASH_IS_ROOTNAME
607 if (orig_type
== _Type::_Root_name
)
608 ++capacity
; // Need to insert root-directory after root-name
611 _M_pathname
.reserve(_M_pathname
.length() + sep
.length()
612 + __p
._M_pathname
.length());
617 const auto basepos
= _M_pathname
.length();
618 _M_pathname
+= __p
.native();
620 _M_cmpts
.type(_Type::_Multi
);
621 _M_cmpts
.reserve(capacity
);
622 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
624 if (orig_type
== _Type::_Multi
)
626 // Remove empty final component
627 if (_M_cmpts
._M_impl
->back().empty())
633 else if (orig_pathlen
!= 0)
635 // Create single component from original path
636 string_view_type
s(_M_pathname
.data(), orig_pathlen
);
637 ::new(output
++) _Cmpt(s
, orig_type
, 0);
638 ++_M_cmpts
._M_impl
->_M_size
;
639 #if SLASHSLASH_IS_ROOTNAME
640 if (orig_type
== _Type::_Root_name
)
642 ::new(output
++) _Cmpt(sep
, _Type::_Root_dir
,
643 orig_pathlen
+ sep
.length());
644 ++_M_cmpts
._M_impl
->_M_size
;
649 if (__p
._M_type() == _Type::_Multi
)
651 for (auto& c
: *__p
._M_cmpts
._M_impl
)
653 ::new(output
++) _Cmpt(c
._M_pathname
, _Type::_Filename
,
655 ++_M_cmpts
._M_impl
->_M_size
;
658 else if (!__p
.empty() || !sep
.empty())
660 __glibcxx_assert(__p
._M_type() == _Type::_Filename
);
661 ::new(output
) _Cmpt(__p
._M_pathname
, __p
._M_type(), basepos
);
662 ++_M_cmpts
._M_impl
->_M_size
;
667 _M_pathname
.resize(orig_pathlen
);
668 if (orig_type
== _Type::_Multi
)
669 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
672 _M_cmpts
.type(orig_type
);
673 __throw_exception_again
;
681 path::_M_append(basic_string_view
<value_type
> s
)
684 auto root_path
= parser
.root_path();
686 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
687 bool is_absolute
= root_path
.second
.type
== _Type::_Root_dir
;
688 bool has_root_name
= root_path
.first
.type
== _Type::_Root_name
;
689 if (is_absolute
|| (has_root_name
&& root_path
.first
.str
!= root_name()))
695 basic_string_view
<value_type
> lhs
= _M_pathname
;
696 bool add_sep
= false;
698 bool has_root_directory
= root_path
.first
.type
== _Type::_Root_dir
699 || root_path
.second
.type
== _Type::_Root_dir
;
701 if (has_root_directory
)
703 // Remove any root directory and relative path
704 if (_M_type() != _Type::_Root_name
)
706 if (!_M_cmpts
.empty()
707 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
708 lhs
= _M_cmpts
.front()._M_pathname
;
713 else if (has_filename() || (!has_root_directory
&& is_absolute
))
716 basic_string_view
<value_type
> rhs
= s
;
717 // Omit any root-name from the generic format pathname:
719 rhs
.remove_prefix(root_path
.first
.str
.length());
721 // Construct new path and swap (strong exception-safety guarantee).
723 tmp
.reserve(lhs
.size() + (int)add_sep
+ rhs
.size());
726 tmp
+= preferred_separator
;
728 path newp
= std::move(tmp
);
732 bool is_absolute
= root_path
.first
.type
== _Type::_Root_dir
733 || root_path
.second
.type
== _Type::_Root_dir
;
734 if (is_absolute
|| this->empty())
740 const auto orig_pathlen
= _M_pathname
.length();
741 const auto orig_size
= _M_cmpts
.size();
742 const auto orig_type
= _M_type();
744 basic_string_view
<value_type
> sep
;
746 sep
= { &preferred_separator
, 1 }; // need to add a separator
747 #if SLASHSLASH_IS_ROOTNAME
748 else if (_M_type() == _Type::_Root_name
) // root-name with no root-dir
749 sep
= { &preferred_separator
, 1 }; // need to add a separator
752 return; // nothing to do
754 // Copy the input into _M_pathname:
756 _M_pathname
.insert(orig_pathlen
, sep
);
757 // Update s to refer to the new copy (this ensures s is not a dangling
758 // reference to deallocated characters, in the case where it was referring
759 // into _M_pathname or a member of _M_cmpts).
761 const auto orig_pathname
= s
.substr(0, orig_pathlen
);
762 s
.remove_prefix(orig_pathlen
+ sep
.length());
764 parser
.input
= s
; // reset parser to use updated string view
765 const auto basepos
= orig_pathname
.length() + sep
.length();
766 parser
.origin
= basepos
;
768 std::array
<_Parser::cmpt
, 64> buf
;
769 auto next
= buf
.begin();
772 if (_M_type() == _Type::_Multi
)
773 capacity
+= _M_cmpts
.size();
777 auto cmpt
= parser
.next();
783 cmpt
= parser
.next();
785 while (cmpt
.valid() && next
!= buf
.end());
787 capacity
+= next
- buf
.begin();
788 if (cmpt
.valid()) // filled buffer before parsing whole input
791 _Parser
parser2(parser
);
792 while (parser2
.next().valid())
796 else if (!sep
.empty())
799 #if SLASHSLASH_IS_ROOTNAME
800 if (orig_type
== _Type::_Root_name
)
801 ++capacity
; // Need to insert root-directory after root-name
806 _M_cmpts
.type(_Type::_Multi
);
807 _M_cmpts
.reserve(capacity
);
808 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
810 if (orig_type
== _Type::_Multi
)
812 // Remove empty final component
813 if (_M_cmpts
._M_impl
->back().empty())
819 else if (orig_pathlen
!= 0)
821 // Create single component from original path
822 ::new(output
++) _Cmpt(orig_pathname
, orig_type
, 0);
823 ++_M_cmpts
._M_impl
->_M_size
;
825 #if SLASHSLASH_IS_ROOTNAME
826 if (!sep
.empty() && orig_type
== _Type::_Root_name
)
828 ::new(output
++) _Cmpt(sep
, _Type::_Root_dir
,
829 orig_pathlen
+ sep
.length());
830 ++_M_cmpts
._M_impl
->_M_size
;
835 if (next
!= buf
.begin())
837 for (auto it
= buf
.begin(); it
!= next
; ++it
)
840 ::new(output
++) _Cmpt(c
.str
, c
.type
, parser
.offset(c
));
841 ++_M_cmpts
._M_impl
->_M_size
;
845 ::new(output
++) _Cmpt(cmpt
.str
, cmpt
.type
, parser
.offset(cmpt
));
846 ++_M_cmpts
._M_impl
->_M_size
;
847 cmpt
= parser
.next();
850 else if (!sep
.empty())
852 // Empty filename at the end:
853 ::new(output
) _Cmpt({}, _Type::_Filename
, basepos
);
854 ++_M_cmpts
._M_impl
->_M_size
;
859 _M_pathname
.resize(orig_pathlen
);
860 if (orig_type
== _Type::_Multi
)
861 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
864 _M_cmpts
.type(orig_type
);
865 __throw_exception_again
;
872 path::operator+=(const path
& p
)
883 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
884 if (_M_type() == _Type::_Root_name
885 || (_M_type() == _Type::_Filename
&& _M_pathname
.size() == 1))
887 // Handle path("C") += path(":") and path("C:") += path("/x")
888 // FIXME: do this more efficiently
889 *this = path(_M_pathname
+ p
._M_pathname
);
893 #if SLASHSLASH_IS_ROOTNAME
894 if (_M_type() == _Type::_Root_dir
)
896 // Handle path("/") += path("/x") and path("//") += path("x")
897 // FIXME: do this more efficiently
898 *this = path(_M_pathname
+ p
._M_pathname
);
903 const auto orig_pathlen
= _M_pathname
.length();
904 const auto orig_type
= _M_type();
905 const auto orig_size
= _M_cmpts
.size();
906 int orig_filenamelen
= -1;
907 basic_string_view
<value_type
> extra
;
909 // Ensure that '_M_pathname += p._M_pathname' won't throw:
910 _M_pathname
.reserve(orig_pathlen
+ p
._M_pathname
.length());
914 _Cmpt
* last
= nullptr;
915 if (p
._M_type() == _Type::_Multi
)
917 it
= p
._M_cmpts
._M_impl
->begin();
918 last
= p
._M_cmpts
._M_impl
->end();
922 c
= _Cmpt(p
._M_pathname
, p
._M_type(), 0);
927 if (it
->_M_type() == _Type::_Filename
)
929 // See if there's a filename or root-name at the end of the original path
930 // that we can add to.
931 if (_M_type() == _Type::_Filename
932 #if SLASHSLASH_IS_ROOTNAME
933 || _M_type() == _Type::_Root_name
937 if (p
._M_type() == _Type::_Filename
)
939 // Simplest case where we just add the whole of p to the
941 _M_pathname
+= p
._M_pathname
;
944 // Only the first component of s should be appended, do so below:
945 extra
= it
->_M_pathname
;
948 else if (_M_type() == _Type::_Multi
949 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
951 auto& back
= _M_cmpts
.back();
952 if (p
._M_type() == _Type::_Filename
)
954 basic_string_view
<value_type
> s
= p
._M_pathname
;
955 back
._M_pathname
+= s
;
960 orig_filenamelen
= back
._M_pathname
.length();
961 back
._M_pathname
+= it
->_M_pathname
;
962 extra
= it
->_M_pathname
;
966 else if (is_dir_sep(_M_pathname
.back()) && _M_type() == _Type::_Multi
967 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
968 orig_filenamelen
= 0; // current path has empty filename at end
971 if (_M_type() == _Type::_Multi
)
972 capacity
+= _M_cmpts
.size();
975 if (p
._M_type() == _Type::_Multi
)
976 capacity
+= p
._M_cmpts
.size();
982 _M_cmpts
.type(_Type::_Multi
);
983 _M_cmpts
.reserve(capacity
);
984 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
986 if (orig_type
!= _Type::_Multi
)
988 // Create single component from original path
989 auto ptr
= ::new(output
++) _Cmpt({}, orig_type
, 0);
990 ++_M_cmpts
._M_impl
->_M_size
;
991 ptr
->_M_pathname
.reserve(_M_pathname
.length() + extra
.length());
992 ptr
->_M_pathname
= _M_pathname
;
993 ptr
->_M_pathname
+= extra
;
995 #if SLASHSLASH_IS_ROOTNAME
996 if (orig_type
== _Type::_Root_name
)
998 basic_string_view
<value_type
> s(p
._M_pathname
);
999 ::new(output
++) _Cmpt(s
.substr(extra
.length(), 1),
1000 _Type::_Root_dir
, orig_pathlen
+ extra
.length());
1001 ++_M_cmpts
._M_impl
->_M_size
;
1005 else if (orig_filenamelen
== 0 && it
!= last
)
1007 // Remove empty filename at end of original path.
1008 _M_cmpts
.pop_back();
1012 if (it
!= last
&& it
->_M_type() == _Type::_Root_name
)
1014 basic_string_view
<value_type
> s
= it
->_M_pathname
;
1015 auto pos
= orig_pathlen
;
1016 #if SLASHSLASH_IS_ROOTNAME
1020 ::new(output
++) _Cmpt(s
, _Type::_Filename
, pos
);
1021 ++_M_cmpts
._M_impl
->_M_size
;
1025 if (it
!= last
&& it
->_M_type() == _Type::_Root_dir
)
1030 auto pos
= it
->_M_pos
+ orig_pathlen
;
1031 ::new(output
++) _Cmpt(it
->_M_pathname
, _Type::_Filename
, pos
);
1032 ++_M_cmpts
._M_impl
->_M_size
;
1036 _M_pathname
+= p
._M_pathname
;
1038 if (is_dir_sep(_M_pathname
.back()))
1040 ::new(output
++) _Cmpt({}, _Type::_Filename
, _M_pathname
.length());
1041 ++_M_cmpts
._M_impl
->_M_size
;
1046 _M_pathname
.resize(orig_pathlen
);
1047 if (orig_type
== _Type::_Multi
)
1049 if (_M_cmpts
.size() > orig_size
)
1050 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
1051 if (orig_filenamelen
!= -1)
1053 if (_M_cmpts
.size() == orig_size
)
1055 auto& back
= _M_cmpts
.back();
1056 back
._M_pathname
.resize(orig_filenamelen
);
1057 if (orig_filenamelen
== 0)
1058 back
._M_pos
= orig_pathlen
;
1062 auto output
= _M_cmpts
._M_impl
->end();
1063 ::new(output
) _Cmpt({}, _Type::_Filename
, orig_pathlen
);
1064 ++_M_cmpts
._M_impl
->_M_size
;
1070 _M_cmpts
.type(orig_type
);
1071 __throw_exception_again
;
1078 path::_M_concat(basic_string_view
<value_type
> s
)
1089 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1090 if (_M_type() == _Type::_Root_name
1091 || (_M_type() == _Type::_Filename
&& _M_pathname
.size() == 1))
1093 // Handle path("C") += ":" and path("C:") += "/x"
1094 // FIXME: do this more efficiently
1095 *this = path(_M_pathname
+ string_type(s
));
1099 #if SLASHSLASH_IS_ROOTNAME
1100 if (_M_type() == _Type::_Root_dir
)
1102 // Handle path("/") += "/x" and path("//") += "x"
1103 // FIXME: do this more efficiently
1104 *this = path(_M_pathname
+ string_type(s
));
1109 const auto orig_pathlen
= _M_pathname
.length();
1110 const auto orig_type
= _M_type();
1111 const auto orig_size
= _M_cmpts
.size();
1112 int orig_filenamelen
= -1;
1113 basic_string_view
<value_type
> extra
;
1115 // Copy the input into _M_pathname:
1117 // Update s to refer to the new copy (this ensures s is not a dangling
1118 // reference to deallocated characters, in the case where it was referring
1119 // into _M_pathname or a member of _M_cmpts).
1121 const auto orig_pathname
= s
.substr(0, orig_pathlen
);
1122 s
.remove_prefix(orig_pathlen
);
1124 _Parser
parser(s
, orig_pathlen
);
1125 auto cmpt
= parser
.next();
1127 if (cmpt
.str
.data() == s
.data())
1129 // See if there's a filename or root-name at the end of the original path
1130 // that we can add to.
1131 if (_M_type() == _Type::_Filename
1132 #if SLASHSLASH_IS_ROOTNAME
1133 || _M_type() == _Type::_Root_name
1137 if (cmpt
.str
.length() == s
.length())
1139 // Simplest case where we just need to add the whole of s
1140 // to the original path, which was already done above.
1143 // Only the first component of s should be appended, do so below:
1145 cmpt
= {}; // so we don't process it again
1147 else if (_M_type() == _Type::_Multi
1148 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
1150 auto& back
= _M_cmpts
.back();
1151 if (cmpt
.str
.length() == s
.length())
1153 back
._M_pathname
+= s
;
1157 orig_filenamelen
= back
._M_pathname
.length();
1158 back
._M_pathname
+= cmpt
.str
;
1163 else if (is_dir_sep(orig_pathname
.back()) && _M_type() == _Type::_Multi
1164 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
1165 orig_filenamelen
= 0; // original path had empty filename at end
1167 std::array
<_Parser::cmpt
, 64> buf
;
1168 auto next
= buf
.begin();
1173 cmpt
= parser
.next();
1174 while (cmpt
.valid() && next
!= buf
.end())
1177 cmpt
= parser
.next();
1181 if (_M_type() == _Type::_Multi
)
1182 capacity
+= _M_cmpts
.size();
1186 capacity
+= next
- buf
.begin();
1188 if (cmpt
.valid()) // filled buffer before parsing whole input
1191 _Parser
parser2(parser
);
1192 while (parser2
.next().valid())
1196 #if SLASHSLASH_IS_ROOTNAME
1197 if (orig_type
== _Type::_Root_name
)
1198 ++capacity
; // Need to insert root-directory after root-name
1203 _M_cmpts
.type(_Type::_Multi
);
1204 _M_cmpts
.reserve(capacity
);
1205 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
1206 auto it
= buf
.begin();
1208 if (orig_type
!= _Type::_Multi
)
1210 // Create single component from original path
1211 auto p
= ::new(output
++) _Cmpt({}, orig_type
, 0);
1212 ++_M_cmpts
._M_impl
->_M_size
;
1213 p
->_M_pathname
.reserve(orig_pathname
.length() + extra
.length());
1214 p
->_M_pathname
= orig_pathname
;
1215 p
->_M_pathname
+= extra
;
1217 #if SLASHSLASH_IS_ROOTNAME
1218 if (orig_type
== _Type::_Root_name
)
1220 ::new(output
++) _Cmpt(s
.substr(extra
.length(), 1),
1221 _Type::_Root_dir
, orig_pathlen
+ extra
.length());
1222 ++_M_cmpts
._M_impl
->_M_size
;
1226 else if (orig_filenamelen
== 0 && extra
.empty())
1228 // Replace empty filename at end of original path.
1229 std::prev(output
)->_M_pathname
= it
->str
;
1230 std::prev(output
)->_M_pos
= parser
.offset(*it
);
1236 ::new(output
++) _Cmpt(it
->str
, _Type::_Filename
, parser
.offset(*it
));
1237 ++_M_cmpts
._M_impl
->_M_size
;
1241 if (next
== buf
.end())
1243 while (cmpt
.valid())
1245 auto pos
= parser
.offset(cmpt
);
1246 ::new(output
++) _Cmpt(cmpt
.str
, _Type::_Filename
, pos
);
1247 ++_M_cmpts
._M_impl
->_M_size
;
1248 cmpt
= parser
.next();
1254 _M_pathname
.resize(orig_pathlen
);
1255 if (orig_type
== _Type::_Multi
)
1257 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
1258 if (orig_filenamelen
!= -1)
1260 auto& back
= _M_cmpts
.back();
1261 back
._M_pathname
.resize(orig_filenamelen
);
1262 if (orig_filenamelen
== 0)
1263 back
._M_pos
= orig_pathlen
;
1268 _M_cmpts
.type(orig_type
);
1269 __throw_exception_again
;
1274 path::remove_filename()
1276 if (_M_type() == _Type::_Multi
)
1278 if (!_M_cmpts
.empty())
1280 auto cmpt
= std::prev(_M_cmpts
.end());
1281 if (cmpt
->_M_type() == _Type::_Filename
&& !cmpt
->empty())
1283 _M_pathname
.erase(cmpt
->_M_pos
);
1284 auto prev
= std::prev(cmpt
);
1285 if (prev
->_M_type() == _Type::_Root_dir
1286 || prev
->_M_type() == _Type::_Root_name
)
1288 _M_cmpts
.pop_back();
1289 if (_M_cmpts
.size() == 1)
1291 _M_cmpts
.type(_M_cmpts
.front()._M_type());
1300 else if (_M_type() == _Type::_Filename
)
1306 path::replace_filename(const path
& replacement
)
1309 operator/=(replacement
);
1313 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1314 const fs::path::value_type dot
= L
'.';
1316 const fs::path::value_type dot
= '.';
1320 path::replace_extension(const path
& replacement
)
1322 auto ext
= _M_find_extension();
1323 // Any existing extension() is removed
1324 if (ext
.first
&& ext
.second
!= string_type::npos
)
1326 if (ext
.first
== &_M_pathname
)
1327 _M_pathname
.erase(ext
.second
);
1330 auto& back
= _M_cmpts
.back();
1331 __glibcxx_assert( ext
.first
== &back
._M_pathname
);
1332 back
._M_pathname
.erase(ext
.second
);
1333 _M_pathname
.erase(back
._M_pos
+ ext
.second
);
1336 // If replacement is not empty and does not begin with a dot character,
1337 // a dot character is appended
1338 if (!replacement
.empty() && replacement
.native()[0] != dot
)
1340 operator+=(replacement
);
1345 path::compare(const path
& p
) const noexcept
1347 if (_M_pathname
== p
._M_pathname
)
1350 basic_string_view
<value_type
> lroot
, rroot
;
1351 if (_M_type() == _Type::_Root_name
)
1352 lroot
= _M_pathname
;
1353 else if (_M_type() == _Type::_Multi
1354 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
1355 lroot
= _M_cmpts
.front()._M_pathname
;
1356 if (p
._M_type() == _Type::_Root_name
)
1357 rroot
= p
._M_pathname
;
1358 else if (p
._M_type() == _Type::_Multi
1359 && p
._M_cmpts
.front()._M_type() == _Type::_Root_name
)
1360 rroot
= p
._M_cmpts
.front()._M_pathname
;
1361 if (int rootNameComparison
= lroot
.compare(rroot
))
1362 return rootNameComparison
;
1364 if (!this->has_root_directory() && p
.has_root_directory())
1366 else if (this->has_root_directory() && !p
.has_root_directory())
1369 using Iterator
= const _Cmpt
*;
1370 Iterator begin1
, end1
, begin2
, end2
;
1371 if (_M_type() == _Type::_Multi
)
1373 begin1
= _M_cmpts
.begin();
1374 end1
= _M_cmpts
.end();
1375 // Find start of this->relative_path()
1376 while (begin1
!= end1
&& begin1
->_M_type() != _Type::_Filename
)
1380 begin1
= end1
= nullptr;
1382 if (p
._M_type() == _Type::_Multi
)
1384 begin2
= p
._M_cmpts
.begin();
1385 end2
= p
._M_cmpts
.end();
1386 // Find start of p.relative_path()
1387 while (begin2
!= end2
&& begin2
->_M_type() != _Type::_Filename
)
1391 begin2
= end2
= nullptr;
1393 if (_M_type() == _Type::_Filename
)
1395 if (p
._M_type() == _Type::_Filename
)
1396 return native().compare(p
.native());
1397 else if (begin2
!= end2
)
1399 if (int ret
= native().compare(begin2
->native()))
1402 return ++begin2
== end2
? 0 : -1;
1407 else if (p
._M_type() == _Type::_Filename
)
1411 if (int ret
= begin1
->native().compare(p
.native()))
1414 return ++begin1
== end1
? 0 : +1;
1421 while (begin1
!= end1
&& begin2
!= end2
)
1423 if (int i
= begin1
->native().compare(begin2
->native()))
1439 path::compare(basic_string_view
<value_type
> s
) const noexcept
1441 if (_M_pathname
== s
)
1446 basic_string_view
<value_type
> lroot
, rroot
;
1447 if (_M_type() == _Type::_Root_name
)
1448 lroot
= _M_pathname
;
1449 else if (_M_type() == _Type::_Multi
1450 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
1451 lroot
= _M_cmpts
.front()._M_pathname
;
1452 auto root_path
= parser
.root_path();
1453 if (root_path
.first
.type
== _Type::_Root_name
)
1454 rroot
= root_path
.first
.str
;
1455 if (int rootNameComparison
= lroot
.compare(rroot
))
1456 return rootNameComparison
;
1458 const bool has_root_dir
= root_path
.first
.type
== _Type::_Root_dir
1459 || root_path
.second
.type
== _Type::_Root_dir
;
1460 if (!this->has_root_directory() && has_root_dir
)
1462 else if (this->has_root_directory() && !has_root_dir
)
1465 using Iterator
= const _Cmpt
*;
1466 Iterator begin1
, end1
;
1467 if (_M_type() == _Type::_Filename
)
1469 auto cmpt
= parser
.next();
1472 if (int ret
= this->native().compare(cmpt
.str
))
1474 return parser
.next().valid() ? -1 : 0;
1479 else if (_M_type() == _Type::_Multi
)
1481 begin1
= _M_cmpts
.begin();
1482 end1
= _M_cmpts
.end();
1483 while (begin1
!= end1
&& begin1
->_M_type() != _Type::_Filename
)
1487 begin1
= end1
= nullptr;
1490 auto cmpt
= parser
.next();
1491 while (begin1
!= end1
&& cmpt
.valid())
1493 if (int i
= begin1
->native().compare(cmpt
.str
))
1496 cmpt
= parser
.next();
1509 path::root_name() const
1512 if (_M_type() == _Type::_Root_name
)
1514 else if (_M_cmpts
.size() && _M_cmpts
.begin()->_M_type() == _Type::_Root_name
)
1515 __ret
= *_M_cmpts
.begin();
1520 path::root_directory() const
1523 if (_M_type() == _Type::_Root_dir
)
1525 __ret
._M_cmpts
.type(_Type::_Root_dir
);
1526 __ret
._M_pathname
.assign(1, preferred_separator
);
1528 else if (!_M_cmpts
.empty())
1530 auto __it
= _M_cmpts
.begin();
1531 if (__it
->_M_type() == _Type::_Root_name
)
1533 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1540 path::root_path() const
1543 if (_M_type() == _Type::_Root_name
)
1545 else if (_M_type() == _Type::_Root_dir
)
1547 __ret
._M_pathname
.assign(1, preferred_separator
);
1548 __ret
._M_cmpts
.type(_Type::_Root_dir
);
1550 else if (!_M_cmpts
.empty())
1552 auto __it
= _M_cmpts
.begin();
1553 if (__it
->_M_type() == _Type::_Root_name
)
1556 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1559 else if (__it
->_M_type() == _Type::_Root_dir
)
1566 path::relative_path() const
1569 if (_M_type() == _Type::_Filename
)
1571 else if (!_M_cmpts
.empty())
1573 auto __it
= _M_cmpts
.begin();
1574 if (__it
->_M_type() == _Type::_Root_name
)
1576 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1578 if (__it
!= _M_cmpts
.end())
1579 __ret
.assign(_M_pathname
.substr(__it
->_M_pos
));
1585 path::parent_path() const
1588 if (!has_relative_path())
1590 else if (_M_cmpts
.size() >= 2)
1592 const auto parent
= std::prev(_M_cmpts
.end(), 2);
1593 const auto len
= parent
->_M_pos
+ parent
->_M_pathname
.length();
1594 __ret
.assign(_M_pathname
.substr(0, len
));
1600 path::has_root_name() const noexcept
1602 if (_M_type() == _Type::_Root_name
)
1604 if (!_M_cmpts
.empty() && _M_cmpts
.begin()->_M_type() == _Type::_Root_name
)
1610 path::has_root_directory() const noexcept
1612 if (_M_type() == _Type::_Root_dir
)
1614 if (!_M_cmpts
.empty())
1616 auto __it
= _M_cmpts
.begin();
1617 if (__it
->_M_type() == _Type::_Root_name
)
1619 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1626 path::has_root_path() const noexcept
1628 if (_M_type() == _Type::_Root_name
|| _M_type() == _Type::_Root_dir
)
1630 if (!_M_cmpts
.empty())
1632 auto __type
= _M_cmpts
.front()._M_type();
1633 if (__type
== _Type::_Root_name
|| __type
== _Type::_Root_dir
)
1640 path::has_relative_path() const noexcept
1642 if (_M_type() == _Type::_Filename
&& !_M_pathname
.empty())
1644 if (!_M_cmpts
.empty())
1646 auto __it
= _M_cmpts
.begin();
1647 if (__it
->_M_type() == _Type::_Root_name
)
1649 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1651 if (__it
!= _M_cmpts
.end() && !__it
->_M_pathname
.empty())
1659 path::has_parent_path() const noexcept
1661 if (!has_relative_path())
1663 return _M_cmpts
.size() >= 2;
1667 path::has_filename() const noexcept
1671 if (_M_type() == _Type::_Filename
)
1672 return !_M_pathname
.empty();
1673 if (_M_type() == _Type::_Multi
)
1675 if (_M_pathname
.back() == preferred_separator
)
1677 return _M_cmpts
.back().has_filename();
1684 inline bool is_dot(fs::path::value_type c
) { return c
== dot
; }
1686 inline bool is_dot(const fs::path
& path
)
1688 const auto& filename
= path
.native();
1689 return filename
.size() == 1 && is_dot(filename
[0]);
1692 inline bool is_dotdot(const fs::path
& path
)
1694 const auto& filename
= path
.native();
1695 return filename
.size() == 2 && is_dot(filename
[0]) && is_dot(filename
[1]);
1700 path::lexically_normal() const
1703 C++17 [fs.path.generic] p6
1704 - If the path is empty, stop.
1705 - Replace each slash character in the root-name with a preferred-separator.
1706 - Replace each directory-separator with a preferred-separator.
1707 - Remove each dot filename and any immediately following directory-separator.
1708 - As long as any appear, remove a non-dot-dot filename immediately followed
1709 by a directory-separator and a dot-dot filename, along with any immediately
1710 following directory-separator.
1711 - If there is a root-directory, remove all dot-dot filenames and any
1712 directory-separators immediately following them.
1713 - If the last filename is dot-dot, remove any trailing directory-separator.
1714 - If the path is empty, add a dot.
1717 // If the path is empty, stop.
1720 for (auto& p
: *this)
1722 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1723 // Replace each slash character in the root-name
1724 if (p
._M_type() == _Type::_Root_name
|| p
._M_type() == _Type::_Root_dir
)
1726 string_type s
= p
.native();
1727 std::replace(s
.begin(), s
.end(), L
'/', L
'\\');
1734 if (ret
.has_filename())
1736 // remove a non-dot-dot filename immediately followed by /..
1737 if (!is_dotdot(ret
.filename()))
1738 ret
.remove_filename();
1742 else if (!ret
.has_relative_path())
1744 // remove a dot-dot filename immediately after root-directory
1745 if (!ret
.has_root_directory())
1750 // Got a path with a relative path (i.e. at least one non-root
1751 // element) and no filename at the end (i.e. empty last element),
1752 // so must have a trailing slash. See what is before it.
1753 auto elem
= ret
._M_cmpts
.end() - 2;
1754 if (elem
->has_filename() && !is_dotdot(*elem
))
1756 // Remove the filename before the trailing slash
1757 // (equiv. to ret = ret.parent_path().remove_filename())
1759 if (elem
== ret
._M_cmpts
.begin())
1763 ret
._M_pathname
.erase(elem
->_M_pos
);
1764 // Remove empty filename at the end:
1765 ret
._M_cmpts
.pop_back();
1766 // If we still have a trailing non-root dir separator
1767 // then leave an empty filename at the end:
1768 if (std::prev(elem
)->_M_type() == _Type::_Filename
)
1770 else // remove the component completely:
1771 ret
._M_cmpts
.pop_back();
1775 // Append the ".." to something ending in "../" which happens
1776 // when normalising paths like ".././.." and "../a/../.."
1782 #if SLASHSLASH_IS_ROOTNAME
1783 else if (p
._M_type() == _Type::_Root_dir
)
1784 ret
+= '/'; // using operator/=('/') would replace whole of ret
1790 if (ret
._M_cmpts
.size() >= 2)
1792 auto back
= std::prev(ret
.end());
1793 // If the last filename is dot-dot, ...
1794 if (back
->empty() && is_dotdot(*std::prev(back
)))
1795 // ... remove any trailing directory-separator.
1796 ret
= ret
.parent_path();
1798 // If the path is empty, add a dot.
1799 else if (ret
.empty())
1806 path::lexically_relative(const path
& base
) const
1809 if (root_name() != base
.root_name())
1811 if (is_absolute() != base
.is_absolute())
1813 if (!has_root_directory() && base
.has_root_directory())
1815 auto [a
, b
] = std::mismatch(begin(), end(), base
.begin(), base
.end());
1816 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1817 // _GLIBCXX_RESOLVE_LIB_DEFECTS
1818 // 3070. path::lexically_relative causes surprising results if a filename
1819 // can also be a root-name
1821 for (auto& p
: _M_cmpts
)
1822 if (p
._M_type() == _Type::_Filename
&& is_disk_designator(p
.native()))
1825 for (auto i
= b
, end
= base
.end(); i
!= end
; ++i
)
1826 if (i
->_M_type() == _Type::_Filename
&& is_disk_designator(i
->native()))
1829 if (a
== end() && b
== base
.end())
1834 for (; b
!= base
.end(); ++b
)
1839 else if (!p
.empty() && !is_dot(p
))
1842 if (n
== 0 && (a
== end() || a
->empty()))
1846 const path
dotdot("..");
1849 for (; a
!= end(); ++a
)
1857 path::lexically_proximate(const path
& base
) const
1859 path rel
= lexically_relative(base
);
1865 std::pair
<const path::string_type
*, std::size_t>
1866 path::_M_find_extension() const noexcept
1868 const string_type
* s
= nullptr;
1870 if (_M_type() == _Type::_Filename
)
1872 else if (_M_type() == _Type::_Multi
&& !_M_cmpts
.empty())
1874 const auto& c
= _M_cmpts
.back();
1875 if (c
._M_type() == _Type::_Filename
)
1881 if (auto sz
= s
->size())
1883 if (sz
<= 2 && (*s
)[0] == dot
)
1884 return { s
, string_type::npos
};
1885 if (const auto pos
= s
->rfind(dot
))
1887 return { s
, string_type::npos
};
1894 path::_M_split_cmpts()
1898 if (_M_pathname
.empty())
1900 _M_cmpts
.type(_Type::_Filename
);
1904 _Parser
parser(_M_pathname
);
1906 std::array
<_Parser::cmpt
, 64> buf
;
1907 auto next
= buf
.begin();
1909 // look for root name or root directory
1910 auto root_path
= parser
.root_path();
1911 if (root_path
.first
.valid())
1913 *next
++ = root_path
.first
;
1914 if (root_path
.second
.valid())
1915 *next
++ = root_path
.second
;
1918 auto cmpt
= parser
.next();
1919 while (cmpt
.valid())
1924 cmpt
= parser
.next();
1926 while (cmpt
.valid() && next
!= buf
.end());
1928 if (next
== buf
.end())
1930 _M_cmpts
.type(_Type::_Multi
);
1931 _M_cmpts
.reserve(_M_cmpts
.size() + buf
.size());
1932 auto output
= _M_cmpts
._M_impl
->end();
1933 for (const auto& c
: buf
)
1935 ::new(output
++) _Cmpt(c
.str
, c
.type
, parser
.offset(c
));
1936 ++_M_cmpts
._M_impl
->_M_size
;
1942 if (auto n
= next
- buf
.begin())
1944 if (n
== 1 && _M_cmpts
.empty())
1946 _M_cmpts
.type(buf
.front().type
);
1950 _M_cmpts
.type(_Type::_Multi
);
1951 _M_cmpts
.reserve(_M_cmpts
.size() + n
, true);
1952 auto output
= _M_cmpts
._M_impl
->end();
1953 for (int i
= 0; i
< n
; ++i
)
1955 const auto& c
= buf
[i
];
1956 ::new(output
++) _Cmpt(c
.str
, c
.type
, parser
.offset(c
));
1957 ++_M_cmpts
._M_impl
->_M_size
;
1963 path::_S_convert_loc(const char* __first
, const char* __last
,
1964 [[maybe_unused
]] const std::locale
& __loc
)
1966 #if _GLIBCXX_USE_WCHAR_T
1967 auto& __cvt
= std::use_facet
<codecvt
<wchar_t, char, mbstate_t>>(__loc
);
1968 basic_string
<wchar_t> __ws
;
1969 if (!__str_codecvt_in_all(__first
, __last
, __ws
, __cvt
))
1970 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
1971 "Cannot convert character sequence",
1972 std::make_error_code(errc::illegal_byte_sequence
)));
1973 return _S_convert(std::move(__ws
));
1975 return {__first
, __last
};
1980 fs::hash_value(const path
& p
) noexcept
1982 // [path.non-member]
1983 // "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)."
1984 // Equality works as if by traversing the range [begin(), end()), meaning
1985 // e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname
1986 // but need to iterate over individual elements. Use the hash_combine from
1987 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
1989 for (const auto& x
: p
)
1991 seed
^= std::hash
<path::string_type
>()(x
.native()) + 0x9e3779b9
1992 + (seed
<<6) + (seed
>>2);
1997 struct fs::filesystem_error::_Impl
1999 _Impl(string_view what_arg
, const path
& p1
, const path
& p2
)
2000 : path1(p1
), path2(p2
), what(make_what(what_arg
, &p1
, &p2
))
2003 _Impl(string_view what_arg
, const path
& p1
)
2004 : path1(p1
), path2(), what(make_what(what_arg
, &p1
, nullptr))
2007 _Impl(string_view what_arg
)
2008 : what(make_what(what_arg
, nullptr, nullptr))
2012 make_what(string_view s
, const path
* p1
, const path
* p2
)
2014 const std::string pstr1
= p1
? p1
->u8string() : std::string
{};
2015 const std::string pstr2
= p2
? p2
->u8string() : std::string
{};
2016 const size_t len
= 18 + s
.length()
2017 + (pstr1
.length() ? pstr1
.length() + 3 : 0)
2018 + (pstr2
.length() ? pstr2
.length() + 3 : 0);
2021 w
= "filesystem error: ";
2043 template class std::__shared_ptr
<const fs::filesystem_error::_Impl
>;
2045 fs::filesystem_error::
2046 filesystem_error(const string
& what_arg
, error_code ec
)
2047 : system_error(ec
, what_arg
),
2048 _M_impl(std::__make_shared
<_Impl
>(system_error::what()))
2051 fs::filesystem_error::
2052 filesystem_error(const string
& what_arg
, const path
& p1
, error_code ec
)
2053 : system_error(ec
, what_arg
),
2054 _M_impl(std::__make_shared
<_Impl
>(system_error::what(), p1
))
2057 fs::filesystem_error::
2058 filesystem_error(const string
& what_arg
, const path
& p1
, const path
& p2
,
2060 : system_error(ec
, what_arg
),
2061 _M_impl(std::__make_shared
<_Impl
>(system_error::what(), p1
, p2
))
2064 fs::filesystem_error::~filesystem_error() = default;
2067 fs::filesystem_error::path1() const noexcept
2068 { return _M_impl
->path1
; }
2071 fs::filesystem_error::path2() const noexcept
2072 { return _M_impl
->path2
; }
2075 fs::filesystem_error::what() const noexcept
2076 { return _M_impl
->what
.c_str(); }