1 // Class filesystem::path -*- C++ -*-
3 // Copyright (C) 2014-2019 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
;
52 using string_view_type
= std::basic_string_view
<value_type
>;
57 _Type type
= _Type::_Multi
;
59 bool valid() const { return type
!= _Type::_Multi
; }
62 string_view_type input
;
63 string_view_type::size_type pos
= 0;
65 _Type last_type
= _Type::_Multi
;
67 _Parser(string_view_type s
, size_t o
= 0) : input(s
), origin(o
) { }
69 pair
<cmpt
, cmpt
> root_path() noexcept
72 pair
<cmpt
, cmpt
> root
;
74 const size_t len
= input
.size();
76 // look for root name or root directory
77 if (is_dir_sep(input
[0]))
79 #if SLASHSLASH_IS_ROOTNAME
80 // look for root name, such as "//foo"
81 if (len
> 2 && input
[1] == input
[0])
83 if (!is_dir_sep(input
[2]))
85 // got root name, find its end
87 while (pos
< len
&& !is_dir_sep(input
[pos
]))
89 root
.first
.str
= input
.substr(0, pos
);
90 root
.first
.type
= _Type::_Root_name
;
92 if (pos
< len
) // also got root directory
94 root
.second
.str
= input
.substr(pos
, 1);
95 root
.second
.type
= _Type::_Root_dir
;
101 // got something like "///foo" which is just a root directory
102 // composed of multiple redundant directory separators
103 root
.first
.str
= input
.substr(0, 1);
104 root
.first
.type
= _Type::_Root_dir
;
111 root
.first
.str
= input
.substr(0, 1);
112 root
.first
.type
= _Type::_Root_dir
;
115 // Find the start of the first filename
116 while (pos
< len
&& is_dir_sep(input
[pos
]))
119 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
120 else if (len
> 1 && input
[1] == L
':')
122 // got disk designator
123 root
.first
.str
= input
.substr(0, 2);
124 root
.first
.type
= _Type::_Root_name
;
125 if (len
> 2 && is_dir_sep(input
[2]))
127 root
.second
.str
= input
.substr(2, 1);
128 root
.second
.type
= _Type::_Root_dir
;
130 pos
= input
.find_first_not_of(L
"/\\", 2);
134 if (root
.second
.valid())
135 last_type
= root
.second
.type
;
137 last_type
= root
.first
.type
;
144 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
145 string_view_type sep
= L
"/\\";
150 const int last_pos
= pos
;
153 if (pos
!= input
.npos
)
155 pos
= input
.find_first_not_of(sep
, pos
);
156 if (pos
!= input
.npos
)
158 const auto end
= input
.find_first_of(sep
, pos
);
159 f
.str
= input
.substr(pos
, end
- pos
);
160 f
.type
= _Type::_Filename
;
163 else if (last_type
== _Type::_Filename
164 || (last_pos
== 0 && !input
.empty()))
166 // [fs.path.itr]/4 An empty element, if trailing non-root
167 // directory-separator present.
168 __glibcxx_assert(is_dir_sep(input
.back()));
169 f
.str
= input
.substr(input
.length(), 0);
170 f
.type
= _Type::_Filename
;
177 string_view_type::size_type
178 offset(const cmpt
& c
) const noexcept
179 { return origin
+ c
.str
.data() - input
.data(); }
182 struct path::_List::_Impl
184 using value_type
= _Cmpt
;
186 _Impl(int cap
) : _M_size(0), _M_capacity(cap
) { }
188 alignas(value_type
) int _M_size
;
191 using iterator
= value_type
*;
192 using const_iterator
= const value_type
*;
194 iterator
begin() { return reinterpret_cast<value_type
*>(this + 1); }
195 iterator
end() { return begin() + size(); }
197 const_iterator
begin() const
198 { return reinterpret_cast<const value_type
*>(this + 1); }
199 const_iterator
end() const { return begin() + size(); }
201 const value_type
& front() const { return *begin(); }
202 const value_type
& back() const { return end()[-1]; }
204 int size() const { return _M_size
; }
205 int capacity() const { return _M_capacity
; }
206 bool empty() const { return _M_size
== 0; }
208 void clear() { std::destroy_n(begin(), _M_size
); _M_size
= 0; }
216 void _M_erase_from(const_iterator pos
)
218 iterator first
= begin() + (pos
- begin());
219 iterator last
= end();
220 std::destroy(first
, last
);
221 _M_size
-= last
- first
;
224 unique_ptr
<_Impl
, _Impl_deleter
> copy() const
226 const auto n
= size();
227 void* p
= ::operator new(sizeof(_Impl
) + n
* sizeof(value_type
));
228 unique_ptr
<_Impl
, _Impl_deleter
> newptr(::new (p
) _Impl
{n
});
229 std::uninitialized_copy_n(begin(), n
, newptr
->begin());
234 // Clear the lowest two bits from the pointer (i.e. remove the _Type value)
235 static _Impl
* notype(_Impl
* p
)
237 constexpr uintptr_t mask
= ~(uintptr_t)0x3;
238 return reinterpret_cast<_Impl
*>(reinterpret_cast<uintptr_t>(p
) & mask
);
242 void path::_List::_Impl_deleter::operator()(_Impl
* p
) const noexcept
244 p
= _Impl::notype(p
);
247 __glibcxx_assert(p
->_M_size
<= p
->_M_capacity
);
249 ::operator delete(p
, sizeof(*p
) + p
->_M_capacity
* sizeof(value_type
));
253 path::_List::_List() : _M_impl(reinterpret_cast<_Impl
*>(_Type::_Filename
)) { }
255 path::_List::_List(const _List
& other
)
258 _M_impl
= other
._M_impl
->copy();
264 path::_List::operator=(const _List
& other
)
268 // copy in-place if there is capacity
269 const int newsize
= other
._M_impl
->size();
270 auto impl
= _Impl::notype(_M_impl
.get());
271 if (impl
&& impl
->capacity() >= newsize
)
273 const int oldsize
= impl
->_M_size
;
274 auto to
= impl
->begin();
275 auto from
= other
._M_impl
->begin();
276 const int minsize
= std::min(newsize
, oldsize
);
277 for (int i
= 0; i
< minsize
; ++i
)
278 to
[i
]._M_pathname
.reserve(from
[i
]._M_pathname
.length());
279 if (newsize
> oldsize
)
281 std::uninitialized_copy_n(to
+ oldsize
, newsize
- oldsize
,
283 impl
->_M_size
= newsize
;
285 else if (newsize
< oldsize
)
286 impl
->_M_erase_from(impl
->begin() + newsize
);
287 std::copy_n(from
, minsize
, to
);
291 _M_impl
= other
._M_impl
->copy();
302 path::_List::type(_Type t
) noexcept
304 auto val
= reinterpret_cast<uintptr_t>(_Impl::notype(_M_impl
.release()));
305 _M_impl
.reset(reinterpret_cast<_Impl
*>(val
| (unsigned char)t
));
309 path::_List::size() const noexcept
311 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
317 path::_List::capacity() const noexcept
319 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
320 return ptr
->capacity();
325 path::_List::empty() const noexcept
331 path::_List::begin() noexcept
334 __glibcxx_assert(!empty());
335 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
341 path::_List::end() noexcept
344 __glibcxx_assert(!empty());
345 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
351 path::_List::begin() const noexcept
354 __glibcxx_assert(!empty());
355 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
361 path::_List::end() const noexcept
364 __glibcxx_assert(!empty());
365 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
371 path::_List::front() noexcept
374 return *_M_impl
->begin();
378 path::_List::back() noexcept
381 return _M_impl
->begin()[_M_impl
->size() - 1];
385 path::_List::front() const noexcept
388 return *_M_impl
->begin();
392 path::_List::back() const noexcept
395 return _M_impl
->begin()[_M_impl
->size() - 1];
399 path::_List::pop_back()
401 __glibcxx_assert(size() > 0);
406 path::_List::_M_erase_from(const_iterator pos
)
408 _M_impl
->_M_erase_from(pos
);
414 if (auto ptr
= _Impl::notype(_M_impl
.get()))
419 path::_List::reserve(int newcap
, bool exact
= false)
421 // __glibcxx_assert(type() == _Type::_Multi);
423 _Impl
* curptr
= _Impl::notype(_M_impl
.get());
425 int curcap
= curptr
? curptr
->capacity() : 0;
429 if (!exact
&& newcap
< int(1.5 * curcap
))
430 newcap
= 1.5 * curcap
;
432 void* p
= ::operator new(sizeof(_Impl
) + newcap
* sizeof(value_type
));
433 std::unique_ptr
<_Impl
, _Impl_deleter
> newptr(::new(p
) _Impl
{newcap
});
434 const int cursize
= curptr
? curptr
->size() : 0;
437 std::uninitialized_move_n(curptr
->begin(), cursize
, newptr
->begin());
438 newptr
->_M_size
= cursize
;
440 std::swap(newptr
, _M_impl
);
445 path::operator=(const path
& p
)
447 _M_pathname
.reserve(p
._M_pathname
.length());
448 _M_cmpts
= p
._M_cmpts
; // might throw
449 _M_pathname
= p
._M_pathname
; // won't throw because we reserved enough space
454 path::operator/=(const path
& __p
)
456 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
457 if (__p
.is_absolute()
458 || (__p
.has_root_name() && __p
.root_name() != root_name()))
459 return operator=(__p
);
461 basic_string_view
<value_type
> __lhs
= _M_pathname
;
462 bool __add_sep
= false;
464 if (__p
.has_root_directory())
466 // Remove any root directory and relative path
467 if (_M_type() != _Type::_Root_name
)
469 if (!_M_cmpts
.empty()
470 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
471 __lhs
= _M_cmpts
.front()._M_pathname
;
476 else if (has_filename() || (!has_root_directory() && is_absolute()))
479 basic_string_view
<value_type
> __rhs
= __p
._M_pathname
;
480 // Omit any root-name from the generic format pathname:
481 if (__p
._M_type() == _Type::_Root_name
)
483 else if (!__p
._M_cmpts
.empty()
484 && __p
._M_cmpts
.front()._M_type() == _Type::_Root_name
)
485 __rhs
.remove_prefix(__p
._M_cmpts
.front()._M_pathname
.size());
487 const size_t __len
= __lhs
.size() + (int)__add_sep
+ __rhs
.size();
488 const int __maxcmpts
= _M_cmpts
.size() + __p
._M_cmpts
.size();
489 if (_M_pathname
.capacity() < __len
|| _M_cmpts
.capacity() < __maxcmpts
)
491 // Construct new path and swap (strong exception-safety guarantee).
493 __tmp
.reserve(__len
);
496 __tmp
+= preferred_separator
;
498 path __newp
= std::move(__tmp
);
505 _M_pathname
+= preferred_separator
;
506 _M_pathname
+= __rhs
;
515 // try to restore original state
516 _M_pathname
.resize(__lhs
.length());
521 // give up, basic exception safety guarantee only:
523 __throw_exception_again
;
528 // POSIX version is simpler than the specification in the standard,
529 // as any path with root-name or root-dir is absolute.
531 if (__p
.is_absolute() || this->empty())
533 return operator=(__p
);
536 using string_view_type
= basic_string_view
<value_type
>;
538 string_view_type sep
;
540 sep
= { &preferred_separator
, 1 }; // need to add a separator
541 #if SLASHSLASH_IS_ROOTNAME
542 else if (_M_type() == _Type::_Root_name
) // root-name with no root-dir
543 sep
= { &preferred_separator
, 1 }; // need to add a separator
545 else if (__p
.empty())
546 return *this; // nothing to do
548 const auto orig_pathlen
= _M_pathname
.length();
549 const auto orig_size
= _M_cmpts
.size();
550 const auto orig_type
= _M_type();
553 if (_M_type() == _Type::_Multi
)
554 capacity
+= _M_cmpts
.size();
557 if (__p
._M_type() == _Type::_Multi
)
558 capacity
+= __p
._M_cmpts
.size();
559 else if (!__p
.empty() || !sep
.empty())
561 #if SLASHSLASH_IS_ROOTNAME
562 if (orig_type
== _Type::_Root_name
)
563 ++capacity
; // Need to insert root-directory after root-name
566 if (orig_type
== _Type::_Multi
)
568 const int curcap
= _M_cmpts
._M_impl
->capacity();
569 if (capacity
> curcap
)
570 capacity
= std::max(capacity
, (int) (curcap
* 1.5));
573 _M_pathname
.reserve(_M_pathname
.length() + sep
.length()
574 + __p
._M_pathname
.length());
579 const auto basepos
= _M_pathname
.length();
580 _M_pathname
+= __p
.native();
582 _M_cmpts
.type(_Type::_Multi
);
583 _M_cmpts
.reserve(capacity
);
584 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
586 if (orig_type
== _Type::_Multi
)
588 // Remove empty final component
589 if (_M_cmpts
._M_impl
->back().empty())
595 else if (orig_pathlen
!= 0)
597 // Create single component from original path
598 string_view_type
s(_M_pathname
.data(), orig_pathlen
);
599 ::new(output
++) _Cmpt(s
, orig_type
, 0);
600 ++_M_cmpts
._M_impl
->_M_size
;
601 #if SLASHSLASH_IS_ROOTNAME
602 if (orig_type
== _Type::_Root_name
)
604 ::new(output
++) _Cmpt(sep
, _Type::_Root_dir
,
605 orig_pathlen
+ sep
.length());
606 ++_M_cmpts
._M_impl
->_M_size
;
611 if (__p
._M_type() == _Type::_Multi
)
613 for (auto& c
: *__p
._M_cmpts
._M_impl
)
615 ::new(output
++) _Cmpt(c
._M_pathname
, _Type::_Filename
,
617 ++_M_cmpts
._M_impl
->_M_size
;
620 else if (!__p
.empty() || !sep
.empty())
622 __glibcxx_assert(__p
._M_type() == _Type::_Filename
);
623 ::new(output
) _Cmpt(__p
._M_pathname
, __p
._M_type(), basepos
);
624 ++_M_cmpts
._M_impl
->_M_size
;
629 _M_pathname
.resize(orig_pathlen
);
630 if (orig_type
== _Type::_Multi
)
631 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
634 _M_cmpts
.type(orig_type
);
635 __throw_exception_again
;
643 path::_M_append(basic_string_view
<value_type
> s
)
646 auto root_path
= parser
.root_path();
648 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
649 bool is_absolute
= root_path
.second
.type
== _Type::_Root_dir
;
650 bool has_root_name
= root_path
.first
.type
== _Type::_Root_name
;
651 if (is_absolute
|| (has_root_name
&& root_path
.first
.str
!= root_name()))
657 basic_string_view
<value_type
> lhs
= _M_pathname
;
658 bool add_sep
= false;
660 bool has_root_directory
= root_path
.first
.type
== _Type::_Root_dir
661 || root_path
.second
.type
== _Type::_Root_dir
;
663 if (has_root_directory
)
665 // Remove any root directory and relative path
666 if (_M_type() != _Type::_Root_name
)
668 if (!_M_cmpts
.empty()
669 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
670 lhs
= _M_cmpts
.front()._M_pathname
;
675 else if (has_filename() || (!has_root_directory
&& is_absolute
))
678 basic_string_view
<value_type
> rhs
= s
;
679 // Omit any root-name from the generic format pathname:
681 rhs
.remove_prefix(root_path
.first
.str
.length());
683 // Construct new path and swap (strong exception-safety guarantee).
685 tmp
.reserve(lhs
.size() + (int)add_sep
+ rhs
.size());
688 tmp
+= preferred_separator
;
690 path newp
= std::move(tmp
);
694 bool is_absolute
= root_path
.first
.type
== _Type::_Root_dir
695 || root_path
.second
.type
== _Type::_Root_dir
;
696 if (is_absolute
|| this->empty())
702 const auto orig_pathlen
= _M_pathname
.length();
703 const auto orig_size
= _M_cmpts
.size();
704 const auto orig_type
= _M_type();
706 basic_string_view
<value_type
> sep
;
708 sep
= { &preferred_separator
, 1 }; // need to add a separator
709 #if SLASHSLASH_IS_ROOTNAME
710 else if (_M_type() == _Type::_Root_name
) // root-name with no root-dir
711 sep
= { &preferred_separator
, 1 }; // need to add a separator
714 return; // nothing to do
716 // Copy the input into _M_pathname:
718 _M_pathname
.insert(orig_pathlen
, sep
);
719 // Update s to refer to the new copy (this ensures s is not a dangling
720 // reference to deallocated characters, in the case where it was referring
721 // into _M_pathname or a member of _M_cmpts).
723 const auto orig_pathname
= s
.substr(0, orig_pathlen
);
724 s
.remove_prefix(orig_pathlen
+ sep
.length());
726 parser
.input
= s
; // reset parser to use updated string view
727 const auto basepos
= orig_pathname
.length() + sep
.length();
728 parser
.origin
= basepos
;
730 std::array
<_Parser::cmpt
, 64> buf
;
731 auto next
= buf
.begin();
734 if (_M_type() == _Type::_Multi
)
735 capacity
+= _M_cmpts
.size();
739 auto cmpt
= parser
.next();
745 cmpt
= parser
.next();
747 while (cmpt
.valid() && next
!= buf
.end());
749 capacity
+= next
- buf
.begin();
750 if (cmpt
.valid()) // filled buffer before parsing whole input
753 _Parser
parser2(parser
);
754 while (parser2
.next().valid())
758 else if (!sep
.empty())
761 #if SLASHSLASH_IS_ROOTNAME
762 if (orig_type
== _Type::_Root_name
)
763 ++capacity
; // Need to insert root-directory after root-name
768 _M_cmpts
.type(_Type::_Multi
);
769 _M_cmpts
.reserve(capacity
);
770 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
772 if (orig_type
== _Type::_Multi
)
774 // Remove empty final component
775 if (_M_cmpts
._M_impl
->back().empty())
781 else if (orig_pathlen
!= 0)
783 // Create single component from original path
784 ::new(output
++) _Cmpt(orig_pathname
, orig_type
, 0);
785 ++_M_cmpts
._M_impl
->_M_size
;
787 #if SLASHSLASH_IS_ROOTNAME
788 if (!sep
.empty() && orig_type
== _Type::_Root_name
)
790 ::new(output
++) _Cmpt(sep
, _Type::_Root_dir
,
791 orig_pathlen
+ sep
.length());
792 ++_M_cmpts
._M_impl
->_M_size
;
797 if (next
!= buf
.begin())
799 for (auto it
= buf
.begin(); it
!= next
; ++it
)
802 ::new(output
++) _Cmpt(c
.str
, c
.type
, parser
.offset(c
));
803 ++_M_cmpts
._M_impl
->_M_size
;
807 ::new(output
++) _Cmpt(cmpt
.str
, cmpt
.type
, parser
.offset(cmpt
));
808 ++_M_cmpts
._M_impl
->_M_size
;
809 cmpt
= parser
.next();
812 else if (!sep
.empty())
814 // Empty filename at the end:
815 ::new(output
) _Cmpt({}, _Type::_Filename
, basepos
);
816 ++_M_cmpts
._M_impl
->_M_size
;
821 _M_pathname
.resize(orig_pathlen
);
822 if (orig_type
== _Type::_Multi
)
823 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
826 _M_cmpts
.type(orig_type
);
827 __throw_exception_again
;
834 path::operator+=(const path
& p
)
845 const auto orig_pathlen
= _M_pathname
.length();
846 const auto orig_type
= _M_type();
847 const auto orig_size
= _M_cmpts
.size();
848 int orig_filenamelen
= -1;
849 basic_string_view
<value_type
> extra
;
851 // Ensure that '_M_pathname += p._M_pathname' won't throw:
852 _M_pathname
.reserve(orig_pathlen
+ p
._M_pathname
.length());
856 _Cmpt
* last
= nullptr;
857 if (p
._M_type() == _Type::_Multi
)
859 it
= p
._M_cmpts
._M_impl
->begin();
860 last
= p
._M_cmpts
._M_impl
->end();
864 c
= _Cmpt(p
._M_pathname
, p
._M_type(), 0);
869 if (it
->_M_type() == _Type::_Filename
)
871 // See if there's a filename or root-name at the end of the original path
872 // that we can add to.
873 if (_M_type() == _Type::_Filename
874 #if SLASHSLASH_IS_ROOTNAME
875 || _M_type() == _Type::_Root_name
879 if (p
._M_type() == _Type::_Filename
)
881 // Simplest case where we just add the whole of p to the
883 _M_pathname
+= p
._M_pathname
;
886 // Only the first component of s should be appended, do so below:
887 extra
= it
->_M_pathname
;
890 else if (_M_type() == _Type::_Multi
891 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
893 auto& back
= _M_cmpts
.back();
894 if (p
._M_type() == _Type::_Filename
)
896 basic_string_view
<value_type
> s
= p
._M_pathname
;
897 back
._M_pathname
+= s
;
902 orig_filenamelen
= back
._M_pathname
.length();
903 back
._M_pathname
+= it
->_M_pathname
;
904 extra
= it
->_M_pathname
;
908 else if (is_dir_sep(_M_pathname
.back()) && _M_type() == _Type::_Multi
909 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
910 orig_filenamelen
= 0; // current path has empty filename at end
913 if (_M_type() == _Type::_Multi
)
914 capacity
+= _M_cmpts
.size();
917 if (p
._M_type() == _Type::_Multi
)
918 capacity
+= p
._M_cmpts
.size();
924 _M_cmpts
.type(_Type::_Multi
);
925 _M_cmpts
.reserve(capacity
);
926 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
928 if (orig_type
!= _Type::_Multi
)
930 // Create single component from original path
931 auto ptr
= ::new(output
++) _Cmpt({}, orig_type
, 0);
932 ++_M_cmpts
._M_impl
->_M_size
;
933 ptr
->_M_pathname
.reserve(_M_pathname
.length() + extra
.length());
934 ptr
->_M_pathname
= _M_pathname
;
935 ptr
->_M_pathname
+= extra
;
937 #if SLASHSLASH_IS_ROOTNAME
938 if (orig_type
== _Type::_Root_name
)
940 basic_string_view
<value_type
> s(p
._M_pathname
);
941 ::new(output
++) _Cmpt(s
.substr(extra
.length(), 1),
942 _Type::_Root_dir
, orig_pathlen
+ extra
.length());
943 ++_M_cmpts
._M_impl
->_M_size
;
947 else if (orig_filenamelen
== 0 && it
!= last
)
949 // Remove empty filename at end of original path.
954 if (it
!= last
&& it
->_M_type() == _Type::_Root_name
)
956 basic_string_view
<value_type
> s
= it
->_M_pathname
;
957 auto pos
= orig_pathlen
;
958 #if SLASHSLASH_IS_ROOTNAME
962 ::new(output
++) _Cmpt(s
, _Type::_Filename
, pos
);
963 ++_M_cmpts
._M_impl
->_M_size
;
967 if (it
!= last
&& it
->_M_type() == _Type::_Root_dir
)
972 // This root-dir becomes a trailing slash
973 auto pos
= _M_pathname
.length() + p
._M_pathname
.length();
974 ::new(output
++) _Cmpt({}, _Type::_Filename
, pos
);
975 ++_M_cmpts
._M_impl
->_M_size
;
981 auto pos
= it
->_M_pos
+ orig_pathlen
;
982 ::new(output
++) _Cmpt(it
->_M_pathname
, _Type::_Filename
, pos
);
983 ++_M_cmpts
._M_impl
->_M_size
;
987 _M_pathname
+= p
._M_pathname
;
989 if (is_dir_sep(_M_pathname
.back()))
991 ::new(output
++) _Cmpt({}, _Type::_Filename
, _M_pathname
.length());
992 ++_M_cmpts
._M_impl
->_M_size
;
997 _M_pathname
.resize(orig_pathlen
);
998 if (orig_type
== _Type::_Multi
)
1000 if (_M_cmpts
.size() > orig_size
)
1001 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
1002 if (orig_filenamelen
!= -1)
1004 if (_M_cmpts
.size() == orig_size
)
1006 auto& back
= _M_cmpts
.back();
1007 back
._M_pathname
.resize(orig_filenamelen
);
1008 if (orig_filenamelen
== 0)
1009 back
._M_pos
= orig_pathlen
;
1013 auto output
= _M_cmpts
._M_impl
->end();
1014 ::new(output
) _Cmpt({}, _Type::_Filename
, orig_pathlen
);
1015 ++_M_cmpts
._M_impl
->_M_size
;
1021 _M_cmpts
.type(orig_type
);
1022 __throw_exception_again
;
1029 path::_M_concat(basic_string_view
<value_type
> s
)
1040 const auto orig_pathlen
= _M_pathname
.length();
1041 const auto orig_type
= _M_type();
1042 const auto orig_size
= _M_cmpts
.size();
1043 int orig_filenamelen
= -1;
1044 basic_string_view
<value_type
> extra
;
1046 // Copy the input into _M_pathname:
1048 // Update s to refer to the new copy (this ensures s is not a dangling
1049 // reference to deallocated characters, in the case where it was referring
1050 // into _M_pathname or a member of _M_cmpts).
1052 const auto orig_pathname
= s
.substr(0, orig_pathlen
);
1053 s
.remove_prefix(orig_pathlen
);
1055 _Parser
parser(s
, orig_pathlen
);
1056 auto cmpt
= parser
.next();
1058 if (cmpt
.str
.data() == s
.data())
1060 // See if there's a filename or root-name at the end of the original path
1061 // that we can add to.
1062 if (_M_type() == _Type::_Filename
1063 #if SLASHSLASH_IS_ROOTNAME
1064 || _M_type() == _Type::_Root_name
1068 if (cmpt
.str
.length() == s
.length())
1070 // Simplest case where we just need to add the whole of s
1071 // to the original path, which was already done above.
1074 // Only the first component of s should be appended, do so below:
1076 cmpt
= {}; // so we don't process it again
1078 else if (_M_type() == _Type::_Multi
1079 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
1081 auto& back
= _M_cmpts
.back();
1082 if (cmpt
.str
.length() == s
.length())
1084 back
._M_pathname
+= s
;
1088 orig_filenamelen
= back
._M_pathname
.length();
1089 back
._M_pathname
+= cmpt
.str
;
1094 else if (is_dir_sep(orig_pathname
.back()) && _M_type() == _Type::_Multi
1095 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
1096 orig_filenamelen
= 0; // original path had empty filename at end
1098 std::array
<_Parser::cmpt
, 64> buf
;
1099 auto next
= buf
.begin();
1104 cmpt
= parser
.next();
1105 while (cmpt
.valid() && next
!= buf
.end())
1108 cmpt
= parser
.next();
1112 if (_M_type() == _Type::_Multi
)
1113 capacity
+= _M_cmpts
.size();
1117 capacity
+= next
- buf
.begin();
1119 if (cmpt
.valid()) // filled buffer before parsing whole input
1122 _Parser
parser2(parser
);
1123 while (parser2
.next().valid())
1127 #if SLASHSLASH_IS_ROOTNAME
1128 if (orig_type
== _Type::_Root_name
)
1129 ++capacity
; // Need to insert root-directory after root-name
1134 _M_cmpts
.type(_Type::_Multi
);
1135 _M_cmpts
.reserve(capacity
);
1136 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
1137 auto it
= buf
.begin();
1139 if (orig_type
!= _Type::_Multi
)
1141 // Create single component from original path
1142 auto p
= ::new(output
++) _Cmpt({}, orig_type
, 0);
1143 ++_M_cmpts
._M_impl
->_M_size
;
1144 p
->_M_pathname
.reserve(orig_pathname
.length() + extra
.length());
1145 p
->_M_pathname
= orig_pathname
;
1146 p
->_M_pathname
+= extra
;
1148 #if SLASHSLASH_IS_ROOTNAME
1149 if (orig_type
== _Type::_Root_name
)
1151 ::new(output
++) _Cmpt(s
.substr(extra
.length(), 1),
1152 _Type::_Root_dir
, orig_pathlen
+ extra
.length());
1153 ++_M_cmpts
._M_impl
->_M_size
;
1157 else if (orig_filenamelen
== 0 && extra
.empty())
1159 // Replace empty filename at end of original path.
1160 std::prev(output
)->_M_pathname
= it
->str
;
1161 std::prev(output
)->_M_pos
= parser
.offset(*it
);
1167 ::new(output
++) _Cmpt(it
->str
, _Type::_Filename
, parser
.offset(*it
));
1168 ++_M_cmpts
._M_impl
->_M_size
;
1172 if (next
== buf
.end())
1174 while (cmpt
.valid())
1176 auto pos
= parser
.offset(cmpt
);
1177 ::new(output
++) _Cmpt(cmpt
.str
, _Type::_Filename
, pos
);
1178 ++_M_cmpts
._M_impl
->_M_size
;
1179 cmpt
= parser
.next();
1185 _M_pathname
.resize(orig_pathlen
);
1186 if (orig_type
== _Type::_Multi
)
1188 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
1189 if (orig_filenamelen
!= -1)
1191 auto& back
= _M_cmpts
.back();
1192 back
._M_pathname
.resize(orig_filenamelen
);
1193 if (orig_filenamelen
== 0)
1194 back
._M_pos
= orig_pathlen
;
1199 _M_cmpts
.type(orig_type
);
1200 __throw_exception_again
;
1205 path::remove_filename()
1207 if (_M_type() == _Type::_Multi
)
1209 if (!_M_cmpts
.empty())
1211 auto cmpt
= std::prev(_M_cmpts
.end());
1212 if (cmpt
->_M_type() == _Type::_Filename
&& !cmpt
->empty())
1214 _M_pathname
.erase(cmpt
->_M_pos
);
1215 auto prev
= std::prev(cmpt
);
1216 if (prev
->_M_type() == _Type::_Root_dir
1217 || prev
->_M_type() == _Type::_Root_name
)
1219 _M_cmpts
.pop_back();
1220 if (_M_cmpts
.size() == 1)
1222 _M_cmpts
.type(_M_cmpts
.front()._M_type());
1231 else if (_M_type() == _Type::_Filename
)
1237 path::replace_filename(const path
& replacement
)
1240 operator/=(replacement
);
1244 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1245 const fs::path::value_type dot
= L
'.';
1247 const fs::path::value_type dot
= '.';
1251 path::replace_extension(const path
& replacement
)
1253 auto ext
= _M_find_extension();
1254 // Any existing extension() is removed
1255 if (ext
.first
&& ext
.second
!= string_type::npos
)
1257 if (ext
.first
== &_M_pathname
)
1258 _M_pathname
.erase(ext
.second
);
1261 const auto& back
= _M_cmpts
.back();
1262 if (ext
.first
!= &back
._M_pathname
)
1263 _GLIBCXX_THROW_OR_ABORT(
1264 std::logic_error("path::replace_extension failed"));
1265 _M_pathname
.erase(back
._M_pos
+ ext
.second
);
1268 // If replacement is not empty and does not begin with a dot character,
1269 // a dot character is appended
1270 if (!replacement
.empty() && replacement
.native()[0] != dot
)
1272 operator+=(replacement
);
1277 path::compare(const path
& p
) const noexcept
1279 if (_M_pathname
== p
._M_pathname
)
1282 basic_string_view
<value_type
> lroot
, rroot
;
1283 if (_M_type() == _Type::_Root_name
)
1284 lroot
= _M_pathname
;
1285 else if (_M_type() == _Type::_Multi
1286 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
1287 lroot
= _M_cmpts
.front()._M_pathname
;
1288 if (p
._M_type() == _Type::_Root_name
)
1289 rroot
= p
._M_pathname
;
1290 else if (p
._M_type() == _Type::_Multi
1291 && p
._M_cmpts
.front()._M_type() == _Type::_Root_name
)
1292 rroot
= p
._M_cmpts
.front()._M_pathname
;
1293 if (int rootNameComparison
= lroot
.compare(rroot
))
1294 return rootNameComparison
;
1296 if (!this->has_root_directory() && p
.has_root_directory())
1298 else if (this->has_root_directory() && !p
.has_root_directory())
1301 using Iterator
= const _Cmpt
*;
1302 Iterator begin1
, end1
, begin2
, end2
;
1303 if (_M_type() == _Type::_Multi
)
1305 begin1
= _M_cmpts
.begin();
1306 end1
= _M_cmpts
.end();
1307 // Find start of this->relative_path()
1308 while (begin1
!= end1
&& begin1
->_M_type() != _Type::_Filename
)
1312 begin1
= end1
= nullptr;
1314 if (p
._M_type() == _Type::_Multi
)
1316 begin2
= p
._M_cmpts
.begin();
1317 end2
= p
._M_cmpts
.end();
1318 // Find start of p.relative_path()
1319 while (begin2
!= end2
&& begin2
->_M_type() != _Type::_Filename
)
1323 begin2
= end2
= nullptr;
1325 if (_M_type() == _Type::_Filename
)
1327 if (p
._M_type() == _Type::_Filename
)
1328 return native().compare(p
.native());
1329 else if (begin2
!= end2
)
1331 if (int ret
= native().compare(begin2
->native()))
1334 return ++begin2
== end2
? 0 : -1;
1339 else if (p
._M_type() == _Type::_Filename
)
1343 if (int ret
= begin1
->native().compare(p
.native()))
1346 return ++begin1
== end1
? 0 : +1;
1353 while (begin1
!= end1
&& begin2
!= end2
)
1355 if (int i
= begin1
->native().compare(begin2
->native()))
1371 path::compare(basic_string_view
<value_type
> s
) const noexcept
1373 if (_M_pathname
== s
)
1378 basic_string_view
<value_type
> lroot
, rroot
;
1379 if (_M_type() == _Type::_Root_name
)
1380 lroot
= _M_pathname
;
1381 else if (_M_type() == _Type::_Multi
1382 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
1383 lroot
= _M_cmpts
.front()._M_pathname
;
1384 auto root_path
= parser
.root_path();
1385 if (root_path
.first
.type
== _Type::_Root_name
)
1386 rroot
= root_path
.first
.str
;
1387 if (int rootNameComparison
= lroot
.compare(rroot
))
1388 return rootNameComparison
;
1390 const bool has_root_dir
= root_path
.first
.type
== _Type::_Root_dir
1391 || root_path
.second
.type
== _Type::_Root_dir
;
1392 if (!this->has_root_directory() && has_root_dir
)
1394 else if (this->has_root_directory() && !has_root_dir
)
1397 using Iterator
= const _Cmpt
*;
1398 Iterator begin1
, end1
;
1399 if (_M_type() == _Type::_Filename
)
1401 auto cmpt
= parser
.next();
1404 if (int ret
= this->native().compare(cmpt
.str
))
1406 return parser
.next().valid() ? -1 : 0;
1411 else if (_M_type() == _Type::_Multi
)
1413 begin1
= _M_cmpts
.begin();
1414 end1
= _M_cmpts
.end();
1415 while (begin1
!= end1
&& begin1
->_M_type() != _Type::_Filename
)
1419 begin1
= end1
= nullptr;
1422 auto cmpt
= parser
.next();
1423 while (begin1
!= end1
&& cmpt
.valid())
1425 if (int i
= begin1
->native().compare(cmpt
.str
))
1428 cmpt
= parser
.next();
1441 path::root_name() const
1444 if (_M_type() == _Type::_Root_name
)
1446 else if (_M_cmpts
.size() && _M_cmpts
.begin()->_M_type() == _Type::_Root_name
)
1447 __ret
= *_M_cmpts
.begin();
1452 path::root_directory() const
1455 if (_M_type() == _Type::_Root_dir
)
1457 __ret
._M_cmpts
.type(_Type::_Root_dir
);
1458 __ret
._M_pathname
.assign(1, preferred_separator
);
1460 else if (!_M_cmpts
.empty())
1462 auto __it
= _M_cmpts
.begin();
1463 if (__it
->_M_type() == _Type::_Root_name
)
1465 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1472 path::root_path() const
1475 if (_M_type() == _Type::_Root_name
)
1477 else if (_M_type() == _Type::_Root_dir
)
1479 __ret
._M_pathname
.assign(1, preferred_separator
);
1480 __ret
._M_cmpts
.type(_Type::_Root_dir
);
1482 else if (!_M_cmpts
.empty())
1484 auto __it
= _M_cmpts
.begin();
1485 if (__it
->_M_type() == _Type::_Root_name
)
1488 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1491 else if (__it
->_M_type() == _Type::_Root_dir
)
1498 path::relative_path() const
1501 if (_M_type() == _Type::_Filename
)
1503 else if (!_M_cmpts
.empty())
1505 auto __it
= _M_cmpts
.begin();
1506 if (__it
->_M_type() == _Type::_Root_name
)
1508 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1510 if (__it
!= _M_cmpts
.end())
1511 __ret
.assign(_M_pathname
.substr(__it
->_M_pos
));
1517 path::parent_path() const
1520 if (!has_relative_path())
1522 else if (_M_cmpts
.size() >= 2)
1524 for (auto __it
= _M_cmpts
.begin(), __end
= std::prev(_M_cmpts
.end());
1525 __it
!= __end
; ++__it
)
1534 path::has_root_name() const
1536 if (_M_type() == _Type::_Root_name
)
1538 if (!_M_cmpts
.empty() && _M_cmpts
.begin()->_M_type() == _Type::_Root_name
)
1544 path::has_root_directory() const
1546 if (_M_type() == _Type::_Root_dir
)
1548 if (!_M_cmpts
.empty())
1550 auto __it
= _M_cmpts
.begin();
1551 if (__it
->_M_type() == _Type::_Root_name
)
1553 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1560 path::has_root_path() const
1562 if (_M_type() == _Type::_Root_name
|| _M_type() == _Type::_Root_dir
)
1564 if (!_M_cmpts
.empty())
1566 auto __type
= _M_cmpts
.front()._M_type();
1567 if (__type
== _Type::_Root_name
|| __type
== _Type::_Root_dir
)
1574 path::has_relative_path() const
1576 if (_M_type() == _Type::_Filename
&& !_M_pathname
.empty())
1578 if (!_M_cmpts
.empty())
1580 auto __it
= _M_cmpts
.begin();
1581 if (__it
->_M_type() == _Type::_Root_name
)
1583 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1585 if (__it
!= _M_cmpts
.end() && !__it
->_M_pathname
.empty())
1593 path::has_parent_path() const
1595 if (!has_relative_path())
1597 return _M_cmpts
.size() >= 2;
1601 path::has_filename() const
1605 if (_M_type() == _Type::_Filename
)
1606 return !_M_pathname
.empty();
1607 if (_M_type() == _Type::_Multi
)
1609 if (_M_pathname
.back() == preferred_separator
)
1611 return _M_cmpts
.back().has_filename();
1618 inline bool is_dot(fs::path::value_type c
) { return c
== dot
; }
1620 inline bool is_dot(const fs::path
& path
)
1622 const auto& filename
= path
.native();
1623 return filename
.size() == 1 && is_dot(filename
[0]);
1626 inline bool is_dotdot(const fs::path
& path
)
1628 const auto& filename
= path
.native();
1629 return filename
.size() == 2 && is_dot(filename
[0]) && is_dot(filename
[1]);
1634 path::lexically_normal() const
1637 C++17 [fs.path.generic] p6
1638 - If the path is empty, stop.
1639 - Replace each slash character in the root-name with a preferred-separator.
1640 - Replace each directory-separator with a preferred-separator.
1641 - Remove each dot filename and any immediately following directory-separator.
1642 - As long as any appear, remove a non-dot-dot filename immediately followed
1643 by a directory-separator and a dot-dot filename, along with any immediately
1644 following directory-separator.
1645 - If there is a root-directory, remove all dot-dot filenames and any
1646 directory-separators immediately following them.
1647 - If the last filename is dot-dot, remove any trailing directory-separator.
1648 - If the path is empty, add a dot.
1651 // If the path is empty, stop.
1654 for (auto& p
: *this)
1656 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1657 // Replace each slash character in the root-name
1658 if (p
._M_type() == _Type::_Root_name
|| p
._M_type() == _Type::_Root_dir
)
1660 string_type s
= p
.native();
1661 std::replace(s
.begin(), s
.end(), L
'/', L
'\\');
1668 if (ret
.has_filename())
1670 // remove a non-dot-dot filename immediately followed by /..
1671 if (!is_dotdot(ret
.filename()))
1672 ret
.remove_filename();
1676 else if (!ret
.has_relative_path())
1678 // remove a dot-dot filename immediately after root-directory
1679 if (!ret
.has_root_directory())
1684 // Got a path with a relative path (i.e. at least one non-root
1685 // element) and no filename at the end (i.e. empty last element),
1686 // so must have a trailing slash. See what is before it.
1687 auto elem
= ret
._M_cmpts
.end() - 2;
1688 if (elem
->has_filename() && !is_dotdot(*elem
))
1690 // Remove the filename before the trailing slash
1691 // (equiv. to ret = ret.parent_path().remove_filename())
1693 if (elem
== ret
._M_cmpts
.begin())
1697 ret
._M_pathname
.erase(elem
->_M_pos
);
1698 // Remove empty filename at the end:
1699 ret
._M_cmpts
.pop_back();
1700 // If we still have a trailing non-root dir separator
1701 // then leave an empty filename at the end:
1702 if (std::prev(elem
)->_M_type() == _Type::_Filename
)
1704 else // remove the component completely:
1705 ret
._M_cmpts
.pop_back();
1709 // Append the ".." to something ending in "../" which happens
1710 // when normalising paths like ".././.." and "../a/../.."
1716 #if SLASHSLASH_IS_ROOTNAME
1717 else if (p
._M_type() == _Type::_Root_dir
)
1718 ret
+= '/'; // using operator/=('/') would replace whole of ret
1724 if (ret
._M_cmpts
.size() >= 2)
1726 auto back
= std::prev(ret
.end());
1727 // If the last filename is dot-dot, ...
1728 if (back
->empty() && is_dotdot(*std::prev(back
)))
1729 // ... remove any trailing directory-separator.
1730 ret
= ret
.parent_path();
1732 // If the path is empty, add a dot.
1733 else if (ret
.empty())
1740 path::lexically_relative(const path
& base
) const
1743 if (root_name() != base
.root_name())
1745 if (is_absolute() != base
.is_absolute())
1747 if (!has_root_directory() && base
.has_root_directory())
1749 auto [a
, b
] = std::mismatch(begin(), end(), base
.begin(), base
.end());
1750 if (a
== end() && b
== base
.end())
1755 for (; b
!= base
.end(); ++b
)
1760 else if (!p
.empty() && !is_dot(p
))
1763 if (n
== 0 && (a
== end() || a
->empty()))
1767 const path
dotdot("..");
1770 for (; a
!= end(); ++a
)
1778 path::lexically_proximate(const path
& base
) const
1780 path rel
= lexically_relative(base
);
1786 std::pair
<const path::string_type
*, std::size_t>
1787 path::_M_find_extension() const
1789 const string_type
* s
= nullptr;
1791 if (_M_type() == _Type::_Filename
)
1793 else if (_M_type() == _Type::_Multi
&& !_M_cmpts
.empty())
1795 const auto& c
= _M_cmpts
.back();
1796 if (c
._M_type() == _Type::_Filename
)
1802 if (auto sz
= s
->size())
1804 if (sz
<= 2 && (*s
)[0] == dot
)
1805 return { s
, string_type::npos
};
1806 const auto pos
= s
->rfind(dot
);
1807 return { s
, pos
? pos
: string_type::npos
};
1814 path::_M_split_cmpts()
1818 if (_M_pathname
.empty())
1820 _M_cmpts
.type(_Type::_Filename
);
1823 if (_M_pathname
.length() == 1 && _M_pathname
[0] == preferred_separator
)
1825 _M_cmpts
.type(_Type::_Root_dir
);
1829 _Parser
parser(_M_pathname
);
1831 std::array
<_Parser::cmpt
, 64> buf
;
1832 auto next
= buf
.begin();
1834 // look for root name or root directory
1835 auto root_path
= parser
.root_path();
1836 if (root_path
.first
.valid())
1838 *next
++ = root_path
.first
;
1839 if (root_path
.second
.valid())
1840 *next
++ = root_path
.second
;
1843 auto cmpt
= parser
.next();
1844 while (cmpt
.valid())
1849 cmpt
= parser
.next();
1851 while (cmpt
.valid() && next
!= buf
.end());
1853 if (next
== buf
.end())
1855 _M_cmpts
.type(_Type::_Multi
);
1856 _M_cmpts
.reserve(_M_cmpts
.size() + buf
.size());
1857 auto output
= _M_cmpts
._M_impl
->end();
1860 auto pos
= c
.str
.data() - _M_pathname
.data();
1861 ::new(output
++) _Cmpt(c
.str
, c
.type
, pos
);
1862 ++_M_cmpts
._M_impl
->_M_size
;
1868 if (auto n
= next
- buf
.begin())
1870 if (n
== 1 && _M_cmpts
.empty())
1872 _M_cmpts
.type(buf
.front().type
);
1876 _M_cmpts
.type(_Type::_Multi
);
1877 _M_cmpts
.reserve(_M_cmpts
.size() + n
, true);
1878 auto output
= _M_cmpts
._M_impl
->end();
1879 for (int i
= 0; i
< n
; ++i
)
1882 auto pos
= c
.str
.data() - _M_pathname
.data();
1883 ::new(output
++) _Cmpt(c
.str
, c
.type
, pos
);
1884 ++_M_cmpts
._M_impl
->_M_size
;
1890 path::_S_convert_loc(const char* __first
, const char* __last
,
1891 const std::locale
& __loc
)
1893 #if _GLIBCXX_USE_WCHAR_T
1894 auto& __cvt
= std::use_facet
<codecvt
<wchar_t, char, mbstate_t>>(__loc
);
1895 basic_string
<wchar_t> __ws
;
1896 if (!__str_codecvt_in(__first
, __last
, __ws
, __cvt
))
1897 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
1898 "Cannot convert character sequence",
1899 std::make_error_code(errc::illegal_byte_sequence
)));
1900 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1903 return _Cvt
<wchar_t>::_S_convert(__ws
.data(), __ws
.data() + __ws
.size());
1906 return {__first
, __last
};
1911 fs::hash_value(const path
& p
) noexcept
1913 // [path.non-member]
1914 // "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)."
1915 // Equality works as if by traversing the range [begin(), end()), meaning
1916 // e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname
1917 // but need to iterate over individual elements. Use the hash_combine from
1918 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
1920 for (const auto& x
: p
)
1922 seed
^= std::hash
<path::string_type
>()(x
.native()) + 0x9e3779b9
1923 + (seed
<<6) + (seed
>>2);
1928 struct fs::filesystem_error::_Impl
1930 _Impl(const string
& what_arg
, const path
& p1
, const path
& p2
)
1931 : path1(p1
), path2(p2
), what(make_what(what_arg
, &p1
, &p2
))
1934 _Impl(const string
& what_arg
, const path
& p1
)
1935 : path1(p1
), path2(), what(make_what(what_arg
, &p1
, nullptr))
1938 _Impl(const string
& what_arg
)
1939 : what(make_what(what_arg
, nullptr, nullptr))
1943 make_what(const std::string
& s
, const path
* p1
, const path
* p2
)
1945 const std::string pstr1
= p1
? p1
->u8string() : std::string
{};
1946 const std::string pstr2
= p2
? p2
->u8string() : std::string
{};
1947 const size_t len
= 18 + s
.length()
1948 + (pstr1
.length() ? pstr1
.length() + 3 : 0)
1949 + (pstr2
.length() ? pstr2
.length() + 3 : 0);
1952 w
= "filesystem error: ";
1974 template class std::__shared_ptr
<const fs::filesystem_error::_Impl
>;
1976 fs::filesystem_error::
1977 filesystem_error(const string
& what_arg
, error_code ec
)
1978 : system_error(ec
, what_arg
),
1979 _M_impl(std::__make_shared
<_Impl
>(what_arg
))
1982 fs::filesystem_error::
1983 filesystem_error(const string
& what_arg
, const path
& p1
, error_code ec
)
1984 : system_error(ec
, what_arg
),
1985 _M_impl(std::__make_shared
<_Impl
>(what_arg
, p1
))
1988 fs::filesystem_error::
1989 filesystem_error(const string
& what_arg
, const path
& p1
, const path
& p2
,
1991 : system_error(ec
, what_arg
),
1992 _M_impl(std::__make_shared
<_Impl
>(what_arg
, p1
, p2
))
1995 fs::filesystem_error::~filesystem_error() = default;
1998 fs::filesystem_error::path1() const noexcept
1999 { return _M_impl
->path1
; }
2002 fs::filesystem_error::path2() const noexcept
2003 { return _M_impl
->path2
; }
2006 fs::filesystem_error::what() const noexcept
2007 { return _M_impl
->what
.c_str(); }