1 // Class filesystem::path -*- C++ -*-
3 // Copyright (C) 2014-2020 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
36 #include <bits/stl_uninitialized.h>
38 namespace fs
= std::filesystem
;
41 static inline bool is_dir_sep(path::value_type ch
)
43 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
44 return ch
== L
'/' || ch
== path::preferred_separator
;
50 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
51 static inline bool is_disk_designator(std::wstring_view s
)
53 return s
.length() == 2 && s
[1] == L
':';
59 using string_view_type
= std::basic_string_view
<value_type
>;
64 _Type type
= _Type::_Multi
;
66 bool valid() const { return type
!= _Type::_Multi
; }
69 string_view_type input
;
70 string_view_type::size_type pos
= 0;
72 _Type last_type
= _Type::_Multi
;
74 _Parser(string_view_type s
, size_t o
= 0) : input(s
), origin(o
) { }
76 pair
<cmpt
, cmpt
> root_path() noexcept
79 pair
<cmpt
, cmpt
> root
;
81 const size_t len
= input
.size();
83 // look for root name or root directory
84 if (is_dir_sep(input
[0]))
86 #if SLASHSLASH_IS_ROOTNAME
87 // look for root name, such as "//foo"
88 if (len
> 2 && input
[1] == input
[0])
90 if (!is_dir_sep(input
[2]))
92 // got root name, find its end
94 while (pos
< len
&& !is_dir_sep(input
[pos
]))
96 root
.first
.str
= input
.substr(0, pos
);
97 root
.first
.type
= _Type::_Root_name
;
99 if (pos
< len
) // also got root directory
101 root
.second
.str
= input
.substr(pos
, 1);
102 root
.second
.type
= _Type::_Root_dir
;
108 // got something like "///foo" which is just a root directory
109 // composed of multiple redundant directory separators
110 root
.first
.str
= input
.substr(0, 1);
111 root
.first
.type
= _Type::_Root_dir
;
118 root
.first
.str
= input
.substr(0, 1);
119 root
.first
.type
= _Type::_Root_dir
;
122 // Find the start of the first filename
123 while (pos
< len
&& is_dir_sep(input
[pos
]))
126 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
127 else if (is_disk_designator(input
.substr(0, 2)))
129 // got disk designator
130 root
.first
.str
= input
.substr(0, 2);
131 root
.first
.type
= _Type::_Root_name
;
132 if (len
> 2 && is_dir_sep(input
[2]))
134 root
.second
.str
= input
.substr(2, 1);
135 root
.second
.type
= _Type::_Root_dir
;
137 pos
= input
.find_first_not_of(L
"/\\", 2);
141 if (root
.second
.valid())
142 last_type
= root
.second
.type
;
144 last_type
= root
.first
.type
;
151 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
152 string_view_type sep
= L
"/\\";
157 const int last_pos
= pos
;
160 if (pos
!= input
.npos
)
162 pos
= input
.find_first_not_of(sep
, pos
);
163 if (pos
!= input
.npos
)
165 const auto end
= input
.find_first_of(sep
, pos
);
166 f
.str
= input
.substr(pos
, end
- pos
);
167 f
.type
= _Type::_Filename
;
170 else if (last_type
== _Type::_Filename
171 || (last_pos
== 0 && !input
.empty()))
173 // [fs.path.itr]/4 An empty element, if trailing non-root
174 // directory-separator present.
175 __glibcxx_assert(is_dir_sep(input
.back()));
176 f
.str
= input
.substr(input
.length(), 0);
177 f
.type
= _Type::_Filename
;
184 string_view_type::size_type
185 offset(const cmpt
& c
) const noexcept
186 { return origin
+ c
.str
.data() - input
.data(); }
189 struct path::_List::_Impl
191 using value_type
= _Cmpt
;
193 _Impl(int cap
) : _M_size(0), _M_capacity(cap
) { }
195 alignas(value_type
) int _M_size
;
198 using iterator
= value_type
*;
199 using const_iterator
= const value_type
*;
201 iterator
begin() { return reinterpret_cast<value_type
*>(this + 1); }
202 iterator
end() { return begin() + size(); }
204 const_iterator
begin() const
205 { return reinterpret_cast<const value_type
*>(this + 1); }
206 const_iterator
end() const { return begin() + size(); }
208 const value_type
& front() const { return *begin(); }
209 const value_type
& back() const { return end()[-1]; }
211 int size() const { return _M_size
; }
212 int capacity() const { return _M_capacity
; }
213 bool empty() const { return _M_size
== 0; }
215 void clear() { std::destroy_n(begin(), _M_size
); _M_size
= 0; }
223 void _M_erase_from(const_iterator pos
)
225 iterator first
= begin() + (pos
- begin());
226 iterator last
= end();
227 std::destroy(first
, last
);
228 _M_size
-= last
- first
;
231 unique_ptr
<_Impl
, _Impl_deleter
> copy() const
233 const auto n
= size();
234 void* p
= ::operator new(sizeof(_Impl
) + n
* sizeof(value_type
));
235 unique_ptr
<_Impl
, _Impl_deleter
> newptr(::new (p
) _Impl
{n
});
236 std::uninitialized_copy_n(begin(), n
, newptr
->begin());
241 // Clear the lowest two bits from the pointer (i.e. remove the _Type value)
242 static _Impl
* notype(_Impl
* p
)
244 constexpr uintptr_t mask
= ~(uintptr_t)0x3;
245 return reinterpret_cast<_Impl
*>(reinterpret_cast<uintptr_t>(p
) & mask
);
249 void path::_List::_Impl_deleter::operator()(_Impl
* p
) const noexcept
251 p
= _Impl::notype(p
);
254 __glibcxx_assert(p
->_M_size
<= p
->_M_capacity
);
256 ::operator delete(p
, sizeof(*p
) + p
->_M_capacity
* sizeof(value_type
));
260 path::_List::_List() : _M_impl(reinterpret_cast<_Impl
*>(_Type::_Filename
)) { }
262 path::_List::_List(const _List
& other
)
265 _M_impl
= other
._M_impl
->copy();
271 path::_List::operator=(const _List
& other
)
275 // copy in-place if there is capacity
276 const int newsize
= other
._M_impl
->size();
277 auto impl
= _Impl::notype(_M_impl
.get());
278 if (impl
&& impl
->capacity() >= newsize
)
280 const int oldsize
= impl
->_M_size
;
281 auto to
= impl
->begin();
282 auto from
= other
._M_impl
->begin();
283 const int minsize
= std::min(newsize
, oldsize
);
284 for (int i
= 0; i
< minsize
; ++i
)
285 to
[i
]._M_pathname
.reserve(from
[i
]._M_pathname
.length());
286 if (newsize
> oldsize
)
288 std::uninitialized_copy_n(from
+ oldsize
, newsize
- oldsize
,
290 impl
->_M_size
= newsize
;
292 else if (newsize
< oldsize
)
293 impl
->_M_erase_from(impl
->begin() + newsize
);
294 std::copy_n(from
, minsize
, to
);
298 _M_impl
= other
._M_impl
->copy();
309 path::_List::type(_Type t
) noexcept
311 auto val
= reinterpret_cast<uintptr_t>(_Impl::notype(_M_impl
.release()));
312 _M_impl
.reset(reinterpret_cast<_Impl
*>(val
| (unsigned char)t
));
316 path::_List::size() const noexcept
318 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
324 path::_List::capacity() const noexcept
326 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
327 return ptr
->capacity();
332 path::_List::empty() const noexcept
338 path::_List::begin() noexcept
341 __glibcxx_assert(!empty());
342 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
348 path::_List::end() noexcept
351 __glibcxx_assert(!empty());
352 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
358 path::_List::begin() const noexcept
361 __glibcxx_assert(!empty());
362 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
368 path::_List::end() const noexcept
371 __glibcxx_assert(!empty());
372 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
378 path::_List::front() noexcept
381 return *_M_impl
->begin();
385 path::_List::back() noexcept
388 return _M_impl
->begin()[_M_impl
->size() - 1];
392 path::_List::front() const noexcept
395 return *_M_impl
->begin();
399 path::_List::back() const noexcept
402 return _M_impl
->begin()[_M_impl
->size() - 1];
406 path::_List::pop_back()
408 __glibcxx_assert(size() > 0);
413 path::_List::_M_erase_from(const_iterator pos
)
415 _M_impl
->_M_erase_from(pos
);
421 if (auto ptr
= _Impl::notype(_M_impl
.get()))
426 path::_List::reserve(int newcap
, bool exact
= false)
428 // __glibcxx_assert(type() == _Type::_Multi);
430 _Impl
* curptr
= _Impl::notype(_M_impl
.get());
432 int curcap
= curptr
? curptr
->capacity() : 0;
436 if (!exact
&& newcap
< int(1.5 * curcap
))
437 newcap
= 1.5 * curcap
;
439 void* p
= ::operator new(sizeof(_Impl
) + newcap
* sizeof(value_type
));
440 std::unique_ptr
<_Impl
, _Impl_deleter
> newptr(::new(p
) _Impl
{newcap
});
441 const int cursize
= curptr
? curptr
->size() : 0;
444 std::uninitialized_move_n(curptr
->begin(), cursize
, newptr
->begin());
445 newptr
->_M_size
= cursize
;
447 std::swap(newptr
, _M_impl
);
452 path::operator=(const path
& p
)
454 if (&p
== this) [[__unlikely__
]]
457 _M_pathname
.reserve(p
._M_pathname
.length());
458 _M_cmpts
= p
._M_cmpts
; // might throw
459 _M_pathname
= p
._M_pathname
; // won't throw because we reserved enough space
464 path::operator/=(const path
& __p
)
466 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
467 if (__p
.is_absolute()
468 || (__p
.has_root_name() && __p
.root_name() != root_name()))
469 return operator=(__p
);
471 basic_string_view
<value_type
> __lhs
= _M_pathname
;
472 bool __add_sep
= false;
474 if (__p
.has_root_directory())
476 // Remove any root directory and relative path
477 if (_M_type() != _Type::_Root_name
)
479 if (!_M_cmpts
.empty()
480 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
481 __lhs
= _M_cmpts
.front()._M_pathname
;
486 else if (has_filename() || (!has_root_directory() && is_absolute()))
489 basic_string_view
<value_type
> __rhs
= __p
._M_pathname
;
490 // Omit any root-name from the generic format pathname:
491 if (__p
._M_type() == _Type::_Root_name
)
493 else if (!__p
._M_cmpts
.empty()
494 && __p
._M_cmpts
.front()._M_type() == _Type::_Root_name
)
495 __rhs
.remove_prefix(__p
._M_cmpts
.front()._M_pathname
.size());
497 const size_t __len
= __lhs
.size() + (int)__add_sep
+ __rhs
.size();
498 const int __maxcmpts
= _M_cmpts
.size() + __p
._M_cmpts
.size();
499 if (_M_pathname
.capacity() < __len
|| _M_cmpts
.capacity() < __maxcmpts
)
501 // Construct new path and swap (strong exception-safety guarantee).
503 __tmp
.reserve(__len
);
506 __tmp
+= preferred_separator
;
508 path __newp
= std::move(__tmp
);
515 _M_pathname
+= preferred_separator
;
516 _M_pathname
+= __rhs
;
525 // try to restore original state
526 _M_pathname
.resize(__lhs
.length());
531 // give up, basic exception safety guarantee only:
533 __throw_exception_again
;
538 // POSIX version is simpler than the specification in the standard,
539 // as any path with root-name or root-dir is absolute.
541 if (__p
.is_absolute() || this->empty())
543 return operator=(__p
);
546 using string_view_type
= basic_string_view
<value_type
>;
548 string_view_type sep
;
550 sep
= { &preferred_separator
, 1 }; // need to add a separator
551 #if SLASHSLASH_IS_ROOTNAME
552 else if (_M_type() == _Type::_Root_name
) // root-name with no root-dir
553 sep
= { &preferred_separator
, 1 }; // need to add a separator
555 else if (__p
.empty())
556 return *this; // nothing to do
558 const auto orig_pathlen
= _M_pathname
.length();
559 const auto orig_size
= _M_cmpts
.size();
560 const auto orig_type
= _M_type();
563 if (_M_type() == _Type::_Multi
)
564 capacity
+= _M_cmpts
.size();
567 if (__p
._M_type() == _Type::_Multi
)
568 capacity
+= __p
._M_cmpts
.size();
569 else if (!__p
.empty() || !sep
.empty())
571 #if SLASHSLASH_IS_ROOTNAME
572 if (orig_type
== _Type::_Root_name
)
573 ++capacity
; // Need to insert root-directory after root-name
576 if (orig_type
== _Type::_Multi
)
578 const int curcap
= _M_cmpts
._M_impl
->capacity();
579 if (capacity
> curcap
)
580 capacity
= std::max(capacity
, (int) (curcap
* 1.5));
583 _M_pathname
.reserve(_M_pathname
.length() + sep
.length()
584 + __p
._M_pathname
.length());
589 const auto basepos
= _M_pathname
.length();
590 _M_pathname
+= __p
.native();
592 _M_cmpts
.type(_Type::_Multi
);
593 _M_cmpts
.reserve(capacity
);
594 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
596 if (orig_type
== _Type::_Multi
)
598 // Remove empty final component
599 if (_M_cmpts
._M_impl
->back().empty())
605 else if (orig_pathlen
!= 0)
607 // Create single component from original path
608 string_view_type
s(_M_pathname
.data(), orig_pathlen
);
609 ::new(output
++) _Cmpt(s
, orig_type
, 0);
610 ++_M_cmpts
._M_impl
->_M_size
;
611 #if SLASHSLASH_IS_ROOTNAME
612 if (orig_type
== _Type::_Root_name
)
614 ::new(output
++) _Cmpt(sep
, _Type::_Root_dir
,
615 orig_pathlen
+ sep
.length());
616 ++_M_cmpts
._M_impl
->_M_size
;
621 if (__p
._M_type() == _Type::_Multi
)
623 for (auto& c
: *__p
._M_cmpts
._M_impl
)
625 ::new(output
++) _Cmpt(c
._M_pathname
, _Type::_Filename
,
627 ++_M_cmpts
._M_impl
->_M_size
;
630 else if (!__p
.empty() || !sep
.empty())
632 __glibcxx_assert(__p
._M_type() == _Type::_Filename
);
633 ::new(output
) _Cmpt(__p
._M_pathname
, __p
._M_type(), basepos
);
634 ++_M_cmpts
._M_impl
->_M_size
;
639 _M_pathname
.resize(orig_pathlen
);
640 if (orig_type
== _Type::_Multi
)
641 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
644 _M_cmpts
.type(orig_type
);
645 __throw_exception_again
;
653 path::_M_append(basic_string_view
<value_type
> s
)
656 auto root_path
= parser
.root_path();
658 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
659 bool is_absolute
= root_path
.second
.type
== _Type::_Root_dir
;
660 bool has_root_name
= root_path
.first
.type
== _Type::_Root_name
;
661 if (is_absolute
|| (has_root_name
&& root_path
.first
.str
!= root_name()))
667 basic_string_view
<value_type
> lhs
= _M_pathname
;
668 bool add_sep
= false;
670 bool has_root_directory
= root_path
.first
.type
== _Type::_Root_dir
671 || root_path
.second
.type
== _Type::_Root_dir
;
673 if (has_root_directory
)
675 // Remove any root directory and relative path
676 if (_M_type() != _Type::_Root_name
)
678 if (!_M_cmpts
.empty()
679 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
680 lhs
= _M_cmpts
.front()._M_pathname
;
685 else if (has_filename() || (!has_root_directory
&& is_absolute
))
688 basic_string_view
<value_type
> rhs
= s
;
689 // Omit any root-name from the generic format pathname:
691 rhs
.remove_prefix(root_path
.first
.str
.length());
693 // Construct new path and swap (strong exception-safety guarantee).
695 tmp
.reserve(lhs
.size() + (int)add_sep
+ rhs
.size());
698 tmp
+= preferred_separator
;
700 path newp
= std::move(tmp
);
704 bool is_absolute
= root_path
.first
.type
== _Type::_Root_dir
705 || root_path
.second
.type
== _Type::_Root_dir
;
706 if (is_absolute
|| this->empty())
712 const auto orig_pathlen
= _M_pathname
.length();
713 const auto orig_size
= _M_cmpts
.size();
714 const auto orig_type
= _M_type();
716 basic_string_view
<value_type
> sep
;
718 sep
= { &preferred_separator
, 1 }; // need to add a separator
719 #if SLASHSLASH_IS_ROOTNAME
720 else if (_M_type() == _Type::_Root_name
) // root-name with no root-dir
721 sep
= { &preferred_separator
, 1 }; // need to add a separator
724 return; // nothing to do
726 // Copy the input into _M_pathname:
728 _M_pathname
.insert(orig_pathlen
, sep
);
729 // Update s to refer to the new copy (this ensures s is not a dangling
730 // reference to deallocated characters, in the case where it was referring
731 // into _M_pathname or a member of _M_cmpts).
733 const auto orig_pathname
= s
.substr(0, orig_pathlen
);
734 s
.remove_prefix(orig_pathlen
+ sep
.length());
736 parser
.input
= s
; // reset parser to use updated string view
737 const auto basepos
= orig_pathname
.length() + sep
.length();
738 parser
.origin
= basepos
;
740 std::array
<_Parser::cmpt
, 64> buf
;
741 auto next
= buf
.begin();
744 if (_M_type() == _Type::_Multi
)
745 capacity
+= _M_cmpts
.size();
749 auto cmpt
= parser
.next();
755 cmpt
= parser
.next();
757 while (cmpt
.valid() && next
!= buf
.end());
759 capacity
+= next
- buf
.begin();
760 if (cmpt
.valid()) // filled buffer before parsing whole input
763 _Parser
parser2(parser
);
764 while (parser2
.next().valid())
768 else if (!sep
.empty())
771 #if SLASHSLASH_IS_ROOTNAME
772 if (orig_type
== _Type::_Root_name
)
773 ++capacity
; // Need to insert root-directory after root-name
778 _M_cmpts
.type(_Type::_Multi
);
779 _M_cmpts
.reserve(capacity
);
780 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
782 if (orig_type
== _Type::_Multi
)
784 // Remove empty final component
785 if (_M_cmpts
._M_impl
->back().empty())
791 else if (orig_pathlen
!= 0)
793 // Create single component from original path
794 ::new(output
++) _Cmpt(orig_pathname
, orig_type
, 0);
795 ++_M_cmpts
._M_impl
->_M_size
;
797 #if SLASHSLASH_IS_ROOTNAME
798 if (!sep
.empty() && orig_type
== _Type::_Root_name
)
800 ::new(output
++) _Cmpt(sep
, _Type::_Root_dir
,
801 orig_pathlen
+ sep
.length());
802 ++_M_cmpts
._M_impl
->_M_size
;
807 if (next
!= buf
.begin())
809 for (auto it
= buf
.begin(); it
!= next
; ++it
)
812 ::new(output
++) _Cmpt(c
.str
, c
.type
, parser
.offset(c
));
813 ++_M_cmpts
._M_impl
->_M_size
;
817 ::new(output
++) _Cmpt(cmpt
.str
, cmpt
.type
, parser
.offset(cmpt
));
818 ++_M_cmpts
._M_impl
->_M_size
;
819 cmpt
= parser
.next();
822 else if (!sep
.empty())
824 // Empty filename at the end:
825 ::new(output
) _Cmpt({}, _Type::_Filename
, basepos
);
826 ++_M_cmpts
._M_impl
->_M_size
;
831 _M_pathname
.resize(orig_pathlen
);
832 if (orig_type
== _Type::_Multi
)
833 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
836 _M_cmpts
.type(orig_type
);
837 __throw_exception_again
;
844 path::operator+=(const path
& p
)
855 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
856 if (_M_type() == _Type::_Root_name
857 || (_M_type() == _Type::_Filename
&& _M_pathname
.size() == 1))
859 // Handle path("C") += path(":") and path("C:") += path("/x")
860 // FIXME: do this more efficiently
861 *this = path(_M_pathname
+ p
._M_pathname
);
865 #if SLASHSLASH_IS_ROOTNAME
866 if (_M_type() == _Type::_Root_dir
)
868 // Handle path("/") += path("/x") and path("//") += path("x")
869 // FIXME: do this more efficiently
870 *this = path(_M_pathname
+ p
._M_pathname
);
875 const auto orig_pathlen
= _M_pathname
.length();
876 const auto orig_type
= _M_type();
877 const auto orig_size
= _M_cmpts
.size();
878 int orig_filenamelen
= -1;
879 basic_string_view
<value_type
> extra
;
881 // Ensure that '_M_pathname += p._M_pathname' won't throw:
882 _M_pathname
.reserve(orig_pathlen
+ p
._M_pathname
.length());
886 _Cmpt
* last
= nullptr;
887 if (p
._M_type() == _Type::_Multi
)
889 it
= p
._M_cmpts
._M_impl
->begin();
890 last
= p
._M_cmpts
._M_impl
->end();
894 c
= _Cmpt(p
._M_pathname
, p
._M_type(), 0);
899 if (it
->_M_type() == _Type::_Filename
)
901 // See if there's a filename or root-name at the end of the original path
902 // that we can add to.
903 if (_M_type() == _Type::_Filename
904 #if SLASHSLASH_IS_ROOTNAME
905 || _M_type() == _Type::_Root_name
909 if (p
._M_type() == _Type::_Filename
)
911 // Simplest case where we just add the whole of p to the
913 _M_pathname
+= p
._M_pathname
;
916 // Only the first component of s should be appended, do so below:
917 extra
= it
->_M_pathname
;
920 else if (_M_type() == _Type::_Multi
921 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
923 auto& back
= _M_cmpts
.back();
924 if (p
._M_type() == _Type::_Filename
)
926 basic_string_view
<value_type
> s
= p
._M_pathname
;
927 back
._M_pathname
+= s
;
932 orig_filenamelen
= back
._M_pathname
.length();
933 back
._M_pathname
+= it
->_M_pathname
;
934 extra
= it
->_M_pathname
;
938 else if (is_dir_sep(_M_pathname
.back()) && _M_type() == _Type::_Multi
939 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
940 orig_filenamelen
= 0; // current path has empty filename at end
943 if (_M_type() == _Type::_Multi
)
944 capacity
+= _M_cmpts
.size();
947 if (p
._M_type() == _Type::_Multi
)
948 capacity
+= p
._M_cmpts
.size();
954 _M_cmpts
.type(_Type::_Multi
);
955 _M_cmpts
.reserve(capacity
);
956 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
958 if (orig_type
!= _Type::_Multi
)
960 // Create single component from original path
961 auto ptr
= ::new(output
++) _Cmpt({}, orig_type
, 0);
962 ++_M_cmpts
._M_impl
->_M_size
;
963 ptr
->_M_pathname
.reserve(_M_pathname
.length() + extra
.length());
964 ptr
->_M_pathname
= _M_pathname
;
965 ptr
->_M_pathname
+= extra
;
967 #if SLASHSLASH_IS_ROOTNAME
968 if (orig_type
== _Type::_Root_name
)
970 basic_string_view
<value_type
> s(p
._M_pathname
);
971 ::new(output
++) _Cmpt(s
.substr(extra
.length(), 1),
972 _Type::_Root_dir
, orig_pathlen
+ extra
.length());
973 ++_M_cmpts
._M_impl
->_M_size
;
977 else if (orig_filenamelen
== 0 && it
!= last
)
979 // Remove empty filename at end of original path.
984 if (it
!= last
&& it
->_M_type() == _Type::_Root_name
)
986 basic_string_view
<value_type
> s
= it
->_M_pathname
;
987 auto pos
= orig_pathlen
;
988 #if SLASHSLASH_IS_ROOTNAME
992 ::new(output
++) _Cmpt(s
, _Type::_Filename
, pos
);
993 ++_M_cmpts
._M_impl
->_M_size
;
997 if (it
!= last
&& it
->_M_type() == _Type::_Root_dir
)
1002 auto pos
= it
->_M_pos
+ orig_pathlen
;
1003 ::new(output
++) _Cmpt(it
->_M_pathname
, _Type::_Filename
, pos
);
1004 ++_M_cmpts
._M_impl
->_M_size
;
1008 _M_pathname
+= p
._M_pathname
;
1010 if (is_dir_sep(_M_pathname
.back()))
1012 ::new(output
++) _Cmpt({}, _Type::_Filename
, _M_pathname
.length());
1013 ++_M_cmpts
._M_impl
->_M_size
;
1018 _M_pathname
.resize(orig_pathlen
);
1019 if (orig_type
== _Type::_Multi
)
1021 if (_M_cmpts
.size() > orig_size
)
1022 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
1023 if (orig_filenamelen
!= -1)
1025 if (_M_cmpts
.size() == orig_size
)
1027 auto& back
= _M_cmpts
.back();
1028 back
._M_pathname
.resize(orig_filenamelen
);
1029 if (orig_filenamelen
== 0)
1030 back
._M_pos
= orig_pathlen
;
1034 auto output
= _M_cmpts
._M_impl
->end();
1035 ::new(output
) _Cmpt({}, _Type::_Filename
, orig_pathlen
);
1036 ++_M_cmpts
._M_impl
->_M_size
;
1042 _M_cmpts
.type(orig_type
);
1043 __throw_exception_again
;
1050 path::_M_concat(basic_string_view
<value_type
> s
)
1061 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1062 if (_M_type() == _Type::_Root_name
1063 || (_M_type() == _Type::_Filename
&& _M_pathname
.size() == 1))
1065 // Handle path("C") += ":" and path("C:") += "/x"
1066 // FIXME: do this more efficiently
1067 *this = path(_M_pathname
+ string_type(s
));
1071 #if SLASHSLASH_IS_ROOTNAME
1072 if (_M_type() == _Type::_Root_dir
)
1074 // Handle path("/") += "/x" and path("//") += "x"
1075 // FIXME: do this more efficiently
1076 *this = path(_M_pathname
+ string_type(s
));
1081 const auto orig_pathlen
= _M_pathname
.length();
1082 const auto orig_type
= _M_type();
1083 const auto orig_size
= _M_cmpts
.size();
1084 int orig_filenamelen
= -1;
1085 basic_string_view
<value_type
> extra
;
1087 // Copy the input into _M_pathname:
1089 // Update s to refer to the new copy (this ensures s is not a dangling
1090 // reference to deallocated characters, in the case where it was referring
1091 // into _M_pathname or a member of _M_cmpts).
1093 const auto orig_pathname
= s
.substr(0, orig_pathlen
);
1094 s
.remove_prefix(orig_pathlen
);
1096 _Parser
parser(s
, orig_pathlen
);
1097 auto cmpt
= parser
.next();
1099 if (cmpt
.str
.data() == s
.data())
1101 // See if there's a filename or root-name at the end of the original path
1102 // that we can add to.
1103 if (_M_type() == _Type::_Filename
1104 #if SLASHSLASH_IS_ROOTNAME
1105 || _M_type() == _Type::_Root_name
1109 if (cmpt
.str
.length() == s
.length())
1111 // Simplest case where we just need to add the whole of s
1112 // to the original path, which was already done above.
1115 // Only the first component of s should be appended, do so below:
1117 cmpt
= {}; // so we don't process it again
1119 else if (_M_type() == _Type::_Multi
1120 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
1122 auto& back
= _M_cmpts
.back();
1123 if (cmpt
.str
.length() == s
.length())
1125 back
._M_pathname
+= s
;
1129 orig_filenamelen
= back
._M_pathname
.length();
1130 back
._M_pathname
+= cmpt
.str
;
1135 else if (is_dir_sep(orig_pathname
.back()) && _M_type() == _Type::_Multi
1136 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
1137 orig_filenamelen
= 0; // original path had empty filename at end
1139 std::array
<_Parser::cmpt
, 64> buf
;
1140 auto next
= buf
.begin();
1145 cmpt
= parser
.next();
1146 while (cmpt
.valid() && next
!= buf
.end())
1149 cmpt
= parser
.next();
1153 if (_M_type() == _Type::_Multi
)
1154 capacity
+= _M_cmpts
.size();
1158 capacity
+= next
- buf
.begin();
1160 if (cmpt
.valid()) // filled buffer before parsing whole input
1163 _Parser
parser2(parser
);
1164 while (parser2
.next().valid())
1168 #if SLASHSLASH_IS_ROOTNAME
1169 if (orig_type
== _Type::_Root_name
)
1170 ++capacity
; // Need to insert root-directory after root-name
1175 _M_cmpts
.type(_Type::_Multi
);
1176 _M_cmpts
.reserve(capacity
);
1177 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
1178 auto it
= buf
.begin();
1180 if (orig_type
!= _Type::_Multi
)
1182 // Create single component from original path
1183 auto p
= ::new(output
++) _Cmpt({}, orig_type
, 0);
1184 ++_M_cmpts
._M_impl
->_M_size
;
1185 p
->_M_pathname
.reserve(orig_pathname
.length() + extra
.length());
1186 p
->_M_pathname
= orig_pathname
;
1187 p
->_M_pathname
+= extra
;
1189 #if SLASHSLASH_IS_ROOTNAME
1190 if (orig_type
== _Type::_Root_name
)
1192 ::new(output
++) _Cmpt(s
.substr(extra
.length(), 1),
1193 _Type::_Root_dir
, orig_pathlen
+ extra
.length());
1194 ++_M_cmpts
._M_impl
->_M_size
;
1198 else if (orig_filenamelen
== 0 && extra
.empty())
1200 // Replace empty filename at end of original path.
1201 std::prev(output
)->_M_pathname
= it
->str
;
1202 std::prev(output
)->_M_pos
= parser
.offset(*it
);
1208 ::new(output
++) _Cmpt(it
->str
, _Type::_Filename
, parser
.offset(*it
));
1209 ++_M_cmpts
._M_impl
->_M_size
;
1213 if (next
== buf
.end())
1215 while (cmpt
.valid())
1217 auto pos
= parser
.offset(cmpt
);
1218 ::new(output
++) _Cmpt(cmpt
.str
, _Type::_Filename
, pos
);
1219 ++_M_cmpts
._M_impl
->_M_size
;
1220 cmpt
= parser
.next();
1226 _M_pathname
.resize(orig_pathlen
);
1227 if (orig_type
== _Type::_Multi
)
1229 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
1230 if (orig_filenamelen
!= -1)
1232 auto& back
= _M_cmpts
.back();
1233 back
._M_pathname
.resize(orig_filenamelen
);
1234 if (orig_filenamelen
== 0)
1235 back
._M_pos
= orig_pathlen
;
1240 _M_cmpts
.type(orig_type
);
1241 __throw_exception_again
;
1246 path::remove_filename()
1248 if (_M_type() == _Type::_Multi
)
1250 if (!_M_cmpts
.empty())
1252 auto cmpt
= std::prev(_M_cmpts
.end());
1253 if (cmpt
->_M_type() == _Type::_Filename
&& !cmpt
->empty())
1255 _M_pathname
.erase(cmpt
->_M_pos
);
1256 auto prev
= std::prev(cmpt
);
1257 if (prev
->_M_type() == _Type::_Root_dir
1258 || prev
->_M_type() == _Type::_Root_name
)
1260 _M_cmpts
.pop_back();
1261 if (_M_cmpts
.size() == 1)
1263 _M_cmpts
.type(_M_cmpts
.front()._M_type());
1272 else if (_M_type() == _Type::_Filename
)
1278 path::replace_filename(const path
& replacement
)
1281 operator/=(replacement
);
1285 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1286 const fs::path::value_type dot
= L
'.';
1288 const fs::path::value_type dot
= '.';
1292 path::replace_extension(const path
& replacement
)
1294 auto ext
= _M_find_extension();
1295 // Any existing extension() is removed
1296 if (ext
.first
&& ext
.second
!= string_type::npos
)
1298 if (ext
.first
== &_M_pathname
)
1299 _M_pathname
.erase(ext
.second
);
1302 auto& back
= _M_cmpts
.back();
1303 __glibcxx_assert( ext
.first
== &back
._M_pathname
);
1304 back
._M_pathname
.erase(ext
.second
);
1305 _M_pathname
.erase(back
._M_pos
+ ext
.second
);
1308 // If replacement is not empty and does not begin with a dot character,
1309 // a dot character is appended
1310 if (!replacement
.empty() && replacement
.native()[0] != dot
)
1312 operator+=(replacement
);
1317 path::compare(const path
& p
) const noexcept
1319 if (_M_pathname
== p
._M_pathname
)
1322 basic_string_view
<value_type
> lroot
, rroot
;
1323 if (_M_type() == _Type::_Root_name
)
1324 lroot
= _M_pathname
;
1325 else if (_M_type() == _Type::_Multi
1326 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
1327 lroot
= _M_cmpts
.front()._M_pathname
;
1328 if (p
._M_type() == _Type::_Root_name
)
1329 rroot
= p
._M_pathname
;
1330 else if (p
._M_type() == _Type::_Multi
1331 && p
._M_cmpts
.front()._M_type() == _Type::_Root_name
)
1332 rroot
= p
._M_cmpts
.front()._M_pathname
;
1333 if (int rootNameComparison
= lroot
.compare(rroot
))
1334 return rootNameComparison
;
1336 if (!this->has_root_directory() && p
.has_root_directory())
1338 else if (this->has_root_directory() && !p
.has_root_directory())
1341 using Iterator
= const _Cmpt
*;
1342 Iterator begin1
, end1
, begin2
, end2
;
1343 if (_M_type() == _Type::_Multi
)
1345 begin1
= _M_cmpts
.begin();
1346 end1
= _M_cmpts
.end();
1347 // Find start of this->relative_path()
1348 while (begin1
!= end1
&& begin1
->_M_type() != _Type::_Filename
)
1352 begin1
= end1
= nullptr;
1354 if (p
._M_type() == _Type::_Multi
)
1356 begin2
= p
._M_cmpts
.begin();
1357 end2
= p
._M_cmpts
.end();
1358 // Find start of p.relative_path()
1359 while (begin2
!= end2
&& begin2
->_M_type() != _Type::_Filename
)
1363 begin2
= end2
= nullptr;
1365 if (_M_type() == _Type::_Filename
)
1367 if (p
._M_type() == _Type::_Filename
)
1368 return native().compare(p
.native());
1369 else if (begin2
!= end2
)
1371 if (int ret
= native().compare(begin2
->native()))
1374 return ++begin2
== end2
? 0 : -1;
1379 else if (p
._M_type() == _Type::_Filename
)
1383 if (int ret
= begin1
->native().compare(p
.native()))
1386 return ++begin1
== end1
? 0 : +1;
1393 while (begin1
!= end1
&& begin2
!= end2
)
1395 if (int i
= begin1
->native().compare(begin2
->native()))
1411 path::compare(basic_string_view
<value_type
> s
) const noexcept
1413 if (_M_pathname
== s
)
1418 basic_string_view
<value_type
> lroot
, rroot
;
1419 if (_M_type() == _Type::_Root_name
)
1420 lroot
= _M_pathname
;
1421 else if (_M_type() == _Type::_Multi
1422 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
1423 lroot
= _M_cmpts
.front()._M_pathname
;
1424 auto root_path
= parser
.root_path();
1425 if (root_path
.first
.type
== _Type::_Root_name
)
1426 rroot
= root_path
.first
.str
;
1427 if (int rootNameComparison
= lroot
.compare(rroot
))
1428 return rootNameComparison
;
1430 const bool has_root_dir
= root_path
.first
.type
== _Type::_Root_dir
1431 || root_path
.second
.type
== _Type::_Root_dir
;
1432 if (!this->has_root_directory() && has_root_dir
)
1434 else if (this->has_root_directory() && !has_root_dir
)
1437 using Iterator
= const _Cmpt
*;
1438 Iterator begin1
, end1
;
1439 if (_M_type() == _Type::_Filename
)
1441 auto cmpt
= parser
.next();
1444 if (int ret
= this->native().compare(cmpt
.str
))
1446 return parser
.next().valid() ? -1 : 0;
1451 else if (_M_type() == _Type::_Multi
)
1453 begin1
= _M_cmpts
.begin();
1454 end1
= _M_cmpts
.end();
1455 while (begin1
!= end1
&& begin1
->_M_type() != _Type::_Filename
)
1459 begin1
= end1
= nullptr;
1462 auto cmpt
= parser
.next();
1463 while (begin1
!= end1
&& cmpt
.valid())
1465 if (int i
= begin1
->native().compare(cmpt
.str
))
1468 cmpt
= parser
.next();
1481 path::root_name() const
1484 if (_M_type() == _Type::_Root_name
)
1486 else if (_M_cmpts
.size() && _M_cmpts
.begin()->_M_type() == _Type::_Root_name
)
1487 __ret
= *_M_cmpts
.begin();
1492 path::root_directory() const
1495 if (_M_type() == _Type::_Root_dir
)
1497 __ret
._M_cmpts
.type(_Type::_Root_dir
);
1498 __ret
._M_pathname
.assign(1, preferred_separator
);
1500 else if (!_M_cmpts
.empty())
1502 auto __it
= _M_cmpts
.begin();
1503 if (__it
->_M_type() == _Type::_Root_name
)
1505 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1512 path::root_path() const
1515 if (_M_type() == _Type::_Root_name
)
1517 else if (_M_type() == _Type::_Root_dir
)
1519 __ret
._M_pathname
.assign(1, preferred_separator
);
1520 __ret
._M_cmpts
.type(_Type::_Root_dir
);
1522 else if (!_M_cmpts
.empty())
1524 auto __it
= _M_cmpts
.begin();
1525 if (__it
->_M_type() == _Type::_Root_name
)
1528 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1531 else if (__it
->_M_type() == _Type::_Root_dir
)
1538 path::relative_path() const
1541 if (_M_type() == _Type::_Filename
)
1543 else if (!_M_cmpts
.empty())
1545 auto __it
= _M_cmpts
.begin();
1546 if (__it
->_M_type() == _Type::_Root_name
)
1548 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1550 if (__it
!= _M_cmpts
.end())
1551 __ret
.assign(_M_pathname
.substr(__it
->_M_pos
));
1557 path::parent_path() const
1560 if (!has_relative_path())
1562 else if (_M_cmpts
.size() >= 2)
1564 const auto parent
= std::prev(_M_cmpts
.end(), 2);
1565 const auto len
= parent
->_M_pos
+ parent
->_M_pathname
.length();
1566 __ret
.assign(_M_pathname
.substr(0, len
));
1572 path::has_root_name() const noexcept
1574 if (_M_type() == _Type::_Root_name
)
1576 if (!_M_cmpts
.empty() && _M_cmpts
.begin()->_M_type() == _Type::_Root_name
)
1582 path::has_root_directory() const noexcept
1584 if (_M_type() == _Type::_Root_dir
)
1586 if (!_M_cmpts
.empty())
1588 auto __it
= _M_cmpts
.begin();
1589 if (__it
->_M_type() == _Type::_Root_name
)
1591 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1598 path::has_root_path() const noexcept
1600 if (_M_type() == _Type::_Root_name
|| _M_type() == _Type::_Root_dir
)
1602 if (!_M_cmpts
.empty())
1604 auto __type
= _M_cmpts
.front()._M_type();
1605 if (__type
== _Type::_Root_name
|| __type
== _Type::_Root_dir
)
1612 path::has_relative_path() const noexcept
1614 if (_M_type() == _Type::_Filename
&& !_M_pathname
.empty())
1616 if (!_M_cmpts
.empty())
1618 auto __it
= _M_cmpts
.begin();
1619 if (__it
->_M_type() == _Type::_Root_name
)
1621 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1623 if (__it
!= _M_cmpts
.end() && !__it
->_M_pathname
.empty())
1631 path::has_parent_path() const noexcept
1633 if (!has_relative_path())
1635 return _M_cmpts
.size() >= 2;
1639 path::has_filename() const noexcept
1643 if (_M_type() == _Type::_Filename
)
1644 return !_M_pathname
.empty();
1645 if (_M_type() == _Type::_Multi
)
1647 if (_M_pathname
.back() == preferred_separator
)
1649 return _M_cmpts
.back().has_filename();
1656 inline bool is_dot(fs::path::value_type c
) { return c
== dot
; }
1658 inline bool is_dot(const fs::path
& path
)
1660 const auto& filename
= path
.native();
1661 return filename
.size() == 1 && is_dot(filename
[0]);
1664 inline bool is_dotdot(const fs::path
& path
)
1666 const auto& filename
= path
.native();
1667 return filename
.size() == 2 && is_dot(filename
[0]) && is_dot(filename
[1]);
1672 path::lexically_normal() const
1675 C++17 [fs.path.generic] p6
1676 - If the path is empty, stop.
1677 - Replace each slash character in the root-name with a preferred-separator.
1678 - Replace each directory-separator with a preferred-separator.
1679 - Remove each dot filename and any immediately following directory-separator.
1680 - As long as any appear, remove a non-dot-dot filename immediately followed
1681 by a directory-separator and a dot-dot filename, along with any immediately
1682 following directory-separator.
1683 - If there is a root-directory, remove all dot-dot filenames and any
1684 directory-separators immediately following them.
1685 - If the last filename is dot-dot, remove any trailing directory-separator.
1686 - If the path is empty, add a dot.
1689 // If the path is empty, stop.
1692 for (auto& p
: *this)
1694 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1695 // Replace each slash character in the root-name
1696 if (p
._M_type() == _Type::_Root_name
|| p
._M_type() == _Type::_Root_dir
)
1698 string_type s
= p
.native();
1699 std::replace(s
.begin(), s
.end(), L
'/', L
'\\');
1706 if (ret
.has_filename())
1708 // remove a non-dot-dot filename immediately followed by /..
1709 if (!is_dotdot(ret
.filename()))
1710 ret
.remove_filename();
1714 else if (!ret
.has_relative_path())
1716 // remove a dot-dot filename immediately after root-directory
1717 if (!ret
.has_root_directory())
1722 // Got a path with a relative path (i.e. at least one non-root
1723 // element) and no filename at the end (i.e. empty last element),
1724 // so must have a trailing slash. See what is before it.
1725 auto elem
= ret
._M_cmpts
.end() - 2;
1726 if (elem
->has_filename() && !is_dotdot(*elem
))
1728 // Remove the filename before the trailing slash
1729 // (equiv. to ret = ret.parent_path().remove_filename())
1731 if (elem
== ret
._M_cmpts
.begin())
1735 ret
._M_pathname
.erase(elem
->_M_pos
);
1736 // Remove empty filename at the end:
1737 ret
._M_cmpts
.pop_back();
1738 // If we still have a trailing non-root dir separator
1739 // then leave an empty filename at the end:
1740 if (std::prev(elem
)->_M_type() == _Type::_Filename
)
1742 else // remove the component completely:
1743 ret
._M_cmpts
.pop_back();
1747 // Append the ".." to something ending in "../" which happens
1748 // when normalising paths like ".././.." and "../a/../.."
1754 #if SLASHSLASH_IS_ROOTNAME
1755 else if (p
._M_type() == _Type::_Root_dir
)
1756 ret
+= '/'; // using operator/=('/') would replace whole of ret
1762 if (ret
._M_cmpts
.size() >= 2)
1764 auto back
= std::prev(ret
.end());
1765 // If the last filename is dot-dot, ...
1766 if (back
->empty() && is_dotdot(*std::prev(back
)))
1767 // ... remove any trailing directory-separator.
1768 ret
= ret
.parent_path();
1770 // If the path is empty, add a dot.
1771 else if (ret
.empty())
1778 path::lexically_relative(const path
& base
) const
1781 if (root_name() != base
.root_name())
1783 if (is_absolute() != base
.is_absolute())
1785 if (!has_root_directory() && base
.has_root_directory())
1787 auto [a
, b
] = std::mismatch(begin(), end(), base
.begin(), base
.end());
1788 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1789 // _GLIBCXX_RESOLVE_LIB_DEFECTS
1790 // 3070. path::lexically_relative causes surprising results if a filename
1791 // can also be a root-name
1793 for (auto& p
: _M_cmpts
)
1794 if (p
._M_type() == _Type::_Filename
&& is_disk_designator(p
.native()))
1797 for (auto i
= b
, end
= base
.end(); i
!= end
; ++i
)
1798 if (i
->_M_type() == _Type::_Filename
&& is_disk_designator(i
->native()))
1801 if (a
== end() && b
== base
.end())
1806 for (; b
!= base
.end(); ++b
)
1811 else if (!p
.empty() && !is_dot(p
))
1814 if (n
== 0 && (a
== end() || a
->empty()))
1818 const path
dotdot("..");
1821 for (; a
!= end(); ++a
)
1829 path::lexically_proximate(const path
& base
) const
1831 path rel
= lexically_relative(base
);
1837 std::pair
<const path::string_type
*, std::size_t>
1838 path::_M_find_extension() const noexcept
1840 const string_type
* s
= nullptr;
1842 if (_M_type() == _Type::_Filename
)
1844 else if (_M_type() == _Type::_Multi
&& !_M_cmpts
.empty())
1846 const auto& c
= _M_cmpts
.back();
1847 if (c
._M_type() == _Type::_Filename
)
1853 if (auto sz
= s
->size())
1855 if (sz
<= 2 && (*s
)[0] == dot
)
1856 return { s
, string_type::npos
};
1857 if (const auto pos
= s
->rfind(dot
))
1859 return { s
, string_type::npos
};
1866 path::_M_split_cmpts()
1870 if (_M_pathname
.empty())
1872 _M_cmpts
.type(_Type::_Filename
);
1875 if (_M_pathname
.length() == 1 && _M_pathname
[0] == preferred_separator
)
1877 _M_cmpts
.type(_Type::_Root_dir
);
1881 _Parser
parser(_M_pathname
);
1883 std::array
<_Parser::cmpt
, 64> buf
;
1884 auto next
= buf
.begin();
1886 // look for root name or root directory
1887 auto root_path
= parser
.root_path();
1888 if (root_path
.first
.valid())
1890 *next
++ = root_path
.first
;
1891 if (root_path
.second
.valid())
1892 *next
++ = root_path
.second
;
1895 auto cmpt
= parser
.next();
1896 while (cmpt
.valid())
1901 cmpt
= parser
.next();
1903 while (cmpt
.valid() && next
!= buf
.end());
1905 if (next
== buf
.end())
1907 _M_cmpts
.type(_Type::_Multi
);
1908 _M_cmpts
.reserve(_M_cmpts
.size() + buf
.size());
1909 auto output
= _M_cmpts
._M_impl
->end();
1912 auto pos
= c
.str
.data() - _M_pathname
.data();
1913 ::new(output
++) _Cmpt(c
.str
, c
.type
, pos
);
1914 ++_M_cmpts
._M_impl
->_M_size
;
1920 if (auto n
= next
- buf
.begin())
1922 if (n
== 1 && _M_cmpts
.empty())
1924 _M_cmpts
.type(buf
.front().type
);
1928 _M_cmpts
.type(_Type::_Multi
);
1929 _M_cmpts
.reserve(_M_cmpts
.size() + n
, true);
1930 auto output
= _M_cmpts
._M_impl
->end();
1931 for (int i
= 0; i
< n
; ++i
)
1934 auto pos
= c
.str
.data() - _M_pathname
.data();
1935 ::new(output
++) _Cmpt(c
.str
, c
.type
, pos
);
1936 ++_M_cmpts
._M_impl
->_M_size
;
1942 path::_S_convert_loc(const char* __first
, const char* __last
,
1943 const std::locale
& __loc
)
1945 #if _GLIBCXX_USE_WCHAR_T
1946 auto& __cvt
= std::use_facet
<codecvt
<wchar_t, char, mbstate_t>>(__loc
);
1947 basic_string
<wchar_t> __ws
;
1948 if (!__str_codecvt_in_all(__first
, __last
, __ws
, __cvt
))
1949 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
1950 "Cannot convert character sequence",
1951 std::make_error_code(errc::illegal_byte_sequence
)));
1952 return _S_convert(std::move(__ws
));
1954 return {__first
, __last
};
1959 fs::hash_value(const path
& p
) noexcept
1961 // [path.non-member]
1962 // "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)."
1963 // Equality works as if by traversing the range [begin(), end()), meaning
1964 // e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname
1965 // but need to iterate over individual elements. Use the hash_combine from
1966 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
1968 for (const auto& x
: p
)
1970 seed
^= std::hash
<path::string_type
>()(x
.native()) + 0x9e3779b9
1971 + (seed
<<6) + (seed
>>2);
1976 struct fs::filesystem_error::_Impl
1978 _Impl(string_view what_arg
, const path
& p1
, const path
& p2
)
1979 : path1(p1
), path2(p2
), what(make_what(what_arg
, &p1
, &p2
))
1982 _Impl(string_view what_arg
, const path
& p1
)
1983 : path1(p1
), path2(), what(make_what(what_arg
, &p1
, nullptr))
1986 _Impl(string_view what_arg
)
1987 : what(make_what(what_arg
, nullptr, nullptr))
1991 make_what(string_view s
, const path
* p1
, const path
* p2
)
1993 const std::string pstr1
= p1
? p1
->u8string() : std::string
{};
1994 const std::string pstr2
= p2
? p2
->u8string() : std::string
{};
1995 const size_t len
= 18 + s
.length()
1996 + (pstr1
.length() ? pstr1
.length() + 3 : 0)
1997 + (pstr2
.length() ? pstr2
.length() + 3 : 0);
2000 w
= "filesystem error: ";
2022 template class std::__shared_ptr
<const fs::filesystem_error::_Impl
>;
2024 fs::filesystem_error::
2025 filesystem_error(const string
& what_arg
, error_code ec
)
2026 : system_error(ec
, what_arg
),
2027 _M_impl(std::__make_shared
<_Impl
>(system_error::what()))
2030 fs::filesystem_error::
2031 filesystem_error(const string
& what_arg
, const path
& p1
, error_code ec
)
2032 : system_error(ec
, what_arg
),
2033 _M_impl(std::__make_shared
<_Impl
>(system_error::what(), p1
))
2036 fs::filesystem_error::
2037 filesystem_error(const string
& what_arg
, const path
& p1
, const path
& p2
,
2039 : system_error(ec
, what_arg
),
2040 _M_impl(std::__make_shared
<_Impl
>(system_error::what(), p1
, p2
))
2043 fs::filesystem_error::~filesystem_error() = default;
2046 fs::filesystem_error::path1() const noexcept
2047 { return _M_impl
->path1
; }
2050 fs::filesystem_error::path2() const noexcept
2051 { return _M_impl
->path2
; }
2054 fs::filesystem_error::what() const noexcept
2055 { return _M_impl
->what
.c_str(); }