]>
git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/src/c++17/fs_path.cc
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>
39 namespace fs
= std::filesystem
;
42 static inline bool is_dir_sep(path::value_type ch
)
44 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
45 return ch
== L
'/' || ch
== path::preferred_separator
;
51 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
52 static inline bool is_disk_designator(std::wstring_view s
)
54 return s
.length() == 2 && s
[1] == L
':';
60 using string_view_type
= std::basic_string_view
<value_type
>;
65 _Type type
= _Type::_Multi
;
67 bool valid() const { return type
!= _Type::_Multi
; }
70 string_view_type input
;
71 string_view_type::size_type pos
= 0;
73 _Type last_type
= _Type::_Multi
;
75 _Parser(string_view_type s
, size_t o
= 0) : input(s
), origin(o
) { }
77 pair
<cmpt
, cmpt
> root_path() noexcept
80 pair
<cmpt
, cmpt
> root
;
82 const size_t len
= input
.size();
84 // look for root name or root directory
85 if (len
&& is_dir_sep(input
[0]))
87 #if SLASHSLASH_IS_ROOTNAME
88 // look for root name, such as "//foo"
89 if (len
> 2 && input
[1] == input
[0])
91 if (!is_dir_sep(input
[2]))
93 // got root name, find its end
95 while (pos
< len
&& !is_dir_sep(input
[pos
]))
97 root
.first
.str
= input
.substr(0, pos
);
98 root
.first
.type
= _Type::_Root_name
;
100 if (pos
< len
) // also got root directory
102 root
.second
.str
= input
.substr(pos
, 1);
103 root
.second
.type
= _Type::_Root_dir
;
109 // got something like "///foo" which is just a root directory
110 // composed of multiple redundant directory separators
111 root
.first
.str
= input
.substr(0, 1);
112 root
.first
.type
= _Type::_Root_dir
;
119 root
.first
.str
= input
.substr(0, 1);
120 root
.first
.type
= _Type::_Root_dir
;
123 // Find the start of the first filename
124 while (pos
< len
&& is_dir_sep(input
[pos
]))
127 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
128 else if (is_disk_designator(input
.substr(0, 2)))
130 // got disk designator
131 root
.first
.str
= input
.substr(0, 2);
132 root
.first
.type
= _Type::_Root_name
;
133 if (len
> 2 && is_dir_sep(input
[2]))
135 root
.second
.str
= input
.substr(2, 1);
136 root
.second
.type
= _Type::_Root_dir
;
138 pos
= input
.find_first_not_of(L
"/\\", 2);
142 if (root
.second
.valid())
143 last_type
= root
.second
.type
;
145 last_type
= root
.first
.type
;
152 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
153 string_view_type sep
= L
"/\\";
158 const int last_pos
= pos
;
161 if (pos
!= input
.npos
)
163 pos
= input
.find_first_not_of(sep
, pos
);
164 if (pos
!= input
.npos
)
166 const auto end
= input
.find_first_of(sep
, pos
);
167 f
.str
= input
.substr(pos
, end
- pos
);
168 f
.type
= _Type::_Filename
;
171 else if (last_type
== _Type::_Filename
172 || (last_pos
== 0 && !input
.empty()))
174 // [fs.path.itr]/4 An empty element, if trailing non-root
175 // directory-separator present.
176 __glibcxx_assert(is_dir_sep(input
.back()));
177 f
.str
= input
.substr(input
.length(), 0);
178 f
.type
= _Type::_Filename
;
185 string_view_type::size_type
186 offset(const cmpt
& c
) const noexcept
187 { return origin
+ c
.str
.data() - input
.data(); }
191 path::path(basic_string_view
<value_type
> __str
, _Type __type
)
194 __glibcxx_assert(__type
!= _Type::_Multi
);
195 _M_cmpts
.type(__type
);
199 path::_Cmpt::_Cmpt(basic_string_view
<value_type
> __s
, _Type __t
, size_t __pos
)
200 : path(__s
, __t
), _M_pos(__pos
)
203 struct path::_List::_Impl
205 using value_type
= _Cmpt
;
207 _Impl(int cap
) : _M_size(0), _M_capacity(cap
) { }
209 alignas(value_type
) int _M_size
;
212 using iterator
= value_type
*;
213 using const_iterator
= const value_type
*;
215 iterator
begin() { return reinterpret_cast<value_type
*>(this + 1); }
216 iterator
end() { return begin() + size(); }
218 const_iterator
begin() const
219 { return reinterpret_cast<const value_type
*>(this + 1); }
220 const_iterator
end() const { return begin() + size(); }
222 const value_type
& front() const { return *begin(); }
223 const value_type
& back() const { return end()[-1]; }
225 int size() const { return _M_size
; }
226 int capacity() const { return _M_capacity
; }
227 bool empty() const { return _M_size
== 0; }
229 void clear() { std::destroy_n(begin(), _M_size
); _M_size
= 0; }
237 void _M_erase_from(const_iterator pos
)
239 iterator first
= begin() + (pos
- begin());
240 iterator last
= end();
241 std::destroy(first
, last
);
242 _M_size
-= last
- first
;
245 unique_ptr
<_Impl
, _Impl_deleter
> copy() const
247 const auto n
= size();
248 void* p
= ::operator new(sizeof(_Impl
) + n
* sizeof(value_type
));
249 unique_ptr
<_Impl
, _Impl_deleter
> newptr(::new (p
) _Impl
{n
});
250 std::uninitialized_copy_n(begin(), n
, newptr
->begin());
255 // Clear the lowest two bits from the pointer (i.e. remove the _Type value)
256 static _Impl
* notype(_Impl
* p
)
258 constexpr uintptr_t mask
= ~(uintptr_t)0x3;
259 return reinterpret_cast<_Impl
*>(reinterpret_cast<uintptr_t>(p
) & mask
);
263 void path::_List::_Impl_deleter::operator()(_Impl
* p
) const noexcept
265 p
= _Impl::notype(p
);
268 __glibcxx_assert(p
->_M_size
<= p
->_M_capacity
);
270 ::operator delete(p
, sizeof(*p
) + p
->_M_capacity
* sizeof(value_type
));
274 path::_List::_List() : _M_impl(reinterpret_cast<_Impl
*>(_Type::_Filename
)) { }
276 path::_List::_List(const _List
& other
)
279 _M_impl
= other
._M_impl
->copy();
285 path::_List::operator=(const _List
& other
)
289 // copy in-place if there is capacity
290 const int newsize
= other
._M_impl
->size();
291 auto impl
= _Impl::notype(_M_impl
.get());
292 if (impl
&& impl
->capacity() >= newsize
)
294 const int oldsize
= impl
->_M_size
;
295 auto to
= impl
->begin();
296 auto from
= other
._M_impl
->begin();
297 const int minsize
= std::min(newsize
, oldsize
);
298 for (int i
= 0; i
< minsize
; ++i
)
299 to
[i
]._M_pathname
.reserve(from
[i
]._M_pathname
.length());
300 if (newsize
> oldsize
)
302 std::uninitialized_copy_n(from
+ oldsize
, newsize
- oldsize
,
304 impl
->_M_size
= newsize
;
306 else if (newsize
< oldsize
)
307 impl
->_M_erase_from(impl
->begin() + newsize
);
308 std::copy_n(from
, minsize
, to
);
312 _M_impl
= other
._M_impl
->copy();
323 path::_List::type(_Type t
) noexcept
325 auto val
= reinterpret_cast<uintptr_t>(_Impl::notype(_M_impl
.release()));
326 _M_impl
.reset(reinterpret_cast<_Impl
*>(val
| (unsigned char)t
));
330 path::_List::size() const noexcept
332 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
338 path::_List::capacity() const noexcept
340 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
341 return ptr
->capacity();
346 path::_List::empty() const noexcept
352 path::_List::begin() noexcept
355 __glibcxx_assert(!empty());
356 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
362 path::_List::end() noexcept
365 __glibcxx_assert(!empty());
366 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
372 path::_List::begin() const noexcept
375 __glibcxx_assert(!empty());
376 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
382 path::_List::end() const noexcept
385 __glibcxx_assert(!empty());
386 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
392 path::_List::front() noexcept
395 return *_M_impl
->begin();
399 path::_List::back() noexcept
402 return _M_impl
->begin()[_M_impl
->size() - 1];
406 path::_List::front() const noexcept
409 return *_M_impl
->begin();
413 path::_List::back() const noexcept
416 return _M_impl
->begin()[_M_impl
->size() - 1];
420 path::_List::pop_back()
422 __glibcxx_assert(size() > 0);
427 path::_List::_M_erase_from(const_iterator pos
)
429 _M_impl
->_M_erase_from(pos
);
435 if (auto ptr
= _Impl::notype(_M_impl
.get()))
440 path::_List::reserve(int newcap
, bool exact
= false)
442 // __glibcxx_assert(type() == _Type::_Multi);
444 _Impl
* curptr
= _Impl::notype(_M_impl
.get());
446 int curcap
= curptr
? curptr
->capacity() : 0;
450 if (!exact
&& newcap
< int(1.5 * curcap
))
451 newcap
= 1.5 * curcap
;
453 void* p
= ::operator new(sizeof(_Impl
) + newcap
* sizeof(value_type
));
454 std::unique_ptr
<_Impl
, _Impl_deleter
> newptr(::new(p
) _Impl
{newcap
});
455 const int cursize
= curptr
? curptr
->size() : 0;
458 std::uninitialized_move_n(curptr
->begin(), cursize
, newptr
->begin());
459 newptr
->_M_size
= cursize
;
461 std::swap(newptr
, _M_impl
);
466 path::operator=(const path
& p
)
468 if (&p
== this) [[__unlikely__
]]
471 _M_pathname
.reserve(p
._M_pathname
.length());
472 _M_cmpts
= p
._M_cmpts
; // might throw
473 _M_pathname
= p
._M_pathname
; // won't throw because we reserved enough space
478 path::operator/=(const path
& __p
)
480 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
481 if (__p
.is_absolute()
482 || (__p
.has_root_name() && __p
.root_name() != root_name()))
483 return operator=(__p
);
485 basic_string_view
<value_type
> __lhs
= _M_pathname
;
486 bool __add_sep
= false;
488 if (__p
.has_root_directory())
490 // Remove any root directory and relative path
491 if (_M_type() != _Type::_Root_name
)
493 if (!_M_cmpts
.empty()
494 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
495 __lhs
= _M_cmpts
.front()._M_pathname
;
500 else if (has_filename() || (!has_root_directory() && is_absolute()))
503 basic_string_view
<value_type
> __rhs
= __p
._M_pathname
;
504 // Omit any root-name from the generic format pathname:
505 if (__p
._M_type() == _Type::_Root_name
)
507 else if (!__p
._M_cmpts
.empty()
508 && __p
._M_cmpts
.front()._M_type() == _Type::_Root_name
)
509 __rhs
.remove_prefix(__p
._M_cmpts
.front()._M_pathname
.size());
511 const size_t __len
= __lhs
.size() + (int)__add_sep
+ __rhs
.size();
512 const int __maxcmpts
= _M_cmpts
.size() + __p
._M_cmpts
.size();
513 if (_M_pathname
.capacity() < __len
|| _M_cmpts
.capacity() < __maxcmpts
)
515 // Construct new path and swap (strong exception-safety guarantee).
517 __tmp
.reserve(__len
);
520 __tmp
+= preferred_separator
;
522 path __newp
= std::move(__tmp
);
529 _M_pathname
+= preferred_separator
;
530 _M_pathname
+= __rhs
;
539 // try to restore original state
540 _M_pathname
.resize(__lhs
.length());
545 // give up, basic exception safety guarantee only:
547 __throw_exception_again
;
552 // POSIX version is simpler than the specification in the standard,
553 // as any path with root-name or root-dir is absolute.
555 if (__p
.is_absolute() || this->empty())
557 return operator=(__p
);
560 using string_view_type
= basic_string_view
<value_type
>;
562 string_view_type sep
;
564 sep
= { &preferred_separator
, 1 }; // need to add a separator
565 #if SLASHSLASH_IS_ROOTNAME
566 else if (_M_type() == _Type::_Root_name
) // root-name with no root-dir
567 sep
= { &preferred_separator
, 1 }; // need to add a separator
569 else if (__p
.empty())
570 return *this; // nothing to do
572 const auto orig_pathlen
= _M_pathname
.length();
573 const auto orig_size
= _M_cmpts
.size();
574 const auto orig_type
= _M_type();
577 if (_M_type() == _Type::_Multi
)
578 capacity
+= _M_cmpts
.size();
581 if (__p
._M_type() == _Type::_Multi
)
582 capacity
+= __p
._M_cmpts
.size();
583 else if (!__p
.empty() || !sep
.empty())
585 #if SLASHSLASH_IS_ROOTNAME
586 if (orig_type
== _Type::_Root_name
)
587 ++capacity
; // Need to insert root-directory after root-name
590 if (orig_type
== _Type::_Multi
)
592 const int curcap
= _M_cmpts
._M_impl
->capacity();
593 if (capacity
> curcap
)
594 capacity
= std::max(capacity
, (int) (curcap
* 1.5));
597 _M_pathname
.reserve(_M_pathname
.length() + sep
.length()
598 + __p
._M_pathname
.length());
603 const auto basepos
= _M_pathname
.length();
604 _M_pathname
+= __p
.native();
606 _M_cmpts
.type(_Type::_Multi
);
607 _M_cmpts
.reserve(capacity
);
608 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
610 if (orig_type
== _Type::_Multi
)
612 // Remove empty final component
613 if (_M_cmpts
._M_impl
->back().empty())
619 else if (orig_pathlen
!= 0)
621 // Create single component from original path
622 string_view_type
s(_M_pathname
.data(), orig_pathlen
);
623 ::new(output
++) _Cmpt(s
, orig_type
, 0);
624 ++_M_cmpts
._M_impl
->_M_size
;
625 #if SLASHSLASH_IS_ROOTNAME
626 if (orig_type
== _Type::_Root_name
)
628 ::new(output
++) _Cmpt(sep
, _Type::_Root_dir
,
629 orig_pathlen
+ sep
.length());
630 ++_M_cmpts
._M_impl
->_M_size
;
635 if (__p
._M_type() == _Type::_Multi
)
637 for (auto& c
: *__p
._M_cmpts
._M_impl
)
639 ::new(output
++) _Cmpt(c
._M_pathname
, _Type::_Filename
,
641 ++_M_cmpts
._M_impl
->_M_size
;
644 else if (!__p
.empty() || !sep
.empty())
646 __glibcxx_assert(__p
._M_type() == _Type::_Filename
);
647 ::new(output
) _Cmpt(__p
._M_pathname
, __p
._M_type(), basepos
);
648 ++_M_cmpts
._M_impl
->_M_size
;
653 _M_pathname
.resize(orig_pathlen
);
654 if (orig_type
== _Type::_Multi
)
655 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
658 _M_cmpts
.type(orig_type
);
659 __throw_exception_again
;
667 path::_M_append(basic_string_view
<value_type
> s
)
670 auto root_path
= parser
.root_path();
672 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
673 bool is_absolute
= root_path
.second
.type
== _Type::_Root_dir
;
674 bool has_root_name
= root_path
.first
.type
== _Type::_Root_name
;
675 if (is_absolute
|| (has_root_name
&& root_path
.first
.str
!= root_name()))
681 basic_string_view
<value_type
> lhs
= _M_pathname
;
682 bool add_sep
= false;
684 bool has_root_directory
= root_path
.first
.type
== _Type::_Root_dir
685 || root_path
.second
.type
== _Type::_Root_dir
;
687 if (has_root_directory
)
689 // Remove any root directory and relative path
690 if (_M_type() != _Type::_Root_name
)
692 if (!_M_cmpts
.empty()
693 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
694 lhs
= _M_cmpts
.front()._M_pathname
;
699 else if (has_filename() || (!has_root_directory
&& is_absolute
))
702 basic_string_view
<value_type
> rhs
= s
;
703 // Omit any root-name from the generic format pathname:
705 rhs
.remove_prefix(root_path
.first
.str
.length());
707 // Construct new path and swap (strong exception-safety guarantee).
709 tmp
.reserve(lhs
.size() + (int)add_sep
+ rhs
.size());
712 tmp
+= preferred_separator
;
714 path newp
= std::move(tmp
);
718 bool is_absolute
= root_path
.first
.type
== _Type::_Root_dir
719 || root_path
.second
.type
== _Type::_Root_dir
;
720 if (is_absolute
|| this->empty())
726 const auto orig_pathlen
= _M_pathname
.length();
727 const auto orig_size
= _M_cmpts
.size();
728 const auto orig_type
= _M_type();
730 basic_string_view
<value_type
> sep
;
732 sep
= { &preferred_separator
, 1 }; // need to add a separator
733 #if SLASHSLASH_IS_ROOTNAME
734 else if (_M_type() == _Type::_Root_name
) // root-name with no root-dir
735 sep
= { &preferred_separator
, 1 }; // need to add a separator
738 return; // nothing to do
740 // Copy the input into _M_pathname:
742 _M_pathname
.insert(orig_pathlen
, sep
);
743 // Update s to refer to the new copy (this ensures s is not a dangling
744 // reference to deallocated characters, in the case where it was referring
745 // into _M_pathname or a member of _M_cmpts).
747 const auto orig_pathname
= s
.substr(0, orig_pathlen
);
748 s
.remove_prefix(orig_pathlen
+ sep
.length());
750 parser
.input
= s
; // reset parser to use updated string view
751 const auto basepos
= orig_pathname
.length() + sep
.length();
752 parser
.origin
= basepos
;
754 std::array
<_Parser::cmpt
, 64> buf
;
755 auto next
= buf
.begin();
758 if (_M_type() == _Type::_Multi
)
759 capacity
+= _M_cmpts
.size();
763 auto cmpt
= parser
.next();
769 cmpt
= parser
.next();
771 while (cmpt
.valid() && next
!= buf
.end());
773 capacity
+= next
- buf
.begin();
774 if (cmpt
.valid()) // filled buffer before parsing whole input
777 _Parser
parser2(parser
);
778 while (parser2
.next().valid())
782 else if (!sep
.empty())
785 #if SLASHSLASH_IS_ROOTNAME
786 if (orig_type
== _Type::_Root_name
)
787 ++capacity
; // Need to insert root-directory after root-name
792 _M_cmpts
.type(_Type::_Multi
);
793 _M_cmpts
.reserve(capacity
);
794 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
796 if (orig_type
== _Type::_Multi
)
798 // Remove empty final component
799 if (_M_cmpts
._M_impl
->back().empty())
805 else if (orig_pathlen
!= 0)
807 // Create single component from original path
808 ::new(output
++) _Cmpt(orig_pathname
, orig_type
, 0);
809 ++_M_cmpts
._M_impl
->_M_size
;
811 #if SLASHSLASH_IS_ROOTNAME
812 if (!sep
.empty() && orig_type
== _Type::_Root_name
)
814 ::new(output
++) _Cmpt(sep
, _Type::_Root_dir
,
815 orig_pathlen
+ sep
.length());
816 ++_M_cmpts
._M_impl
->_M_size
;
821 if (next
!= buf
.begin())
823 for (auto it
= buf
.begin(); it
!= next
; ++it
)
826 ::new(output
++) _Cmpt(c
.str
, c
.type
, parser
.offset(c
));
827 ++_M_cmpts
._M_impl
->_M_size
;
831 ::new(output
++) _Cmpt(cmpt
.str
, cmpt
.type
, parser
.offset(cmpt
));
832 ++_M_cmpts
._M_impl
->_M_size
;
833 cmpt
= parser
.next();
836 else if (!sep
.empty())
838 // Empty filename at the end:
839 ::new(output
) _Cmpt({}, _Type::_Filename
, basepos
);
840 ++_M_cmpts
._M_impl
->_M_size
;
845 _M_pathname
.resize(orig_pathlen
);
846 if (orig_type
== _Type::_Multi
)
847 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
850 _M_cmpts
.type(orig_type
);
851 __throw_exception_again
;
858 path::operator+=(const path
& p
)
869 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
870 if (_M_type() == _Type::_Root_name
871 || (_M_type() == _Type::_Filename
&& _M_pathname
.size() == 1))
873 // Handle path("C") += path(":") and path("C:") += path("/x")
874 // FIXME: do this more efficiently
875 *this = path(_M_pathname
+ p
._M_pathname
);
879 #if SLASHSLASH_IS_ROOTNAME
880 if (_M_type() == _Type::_Root_dir
)
882 // Handle path("/") += path("/x") and path("//") += path("x")
883 // FIXME: do this more efficiently
884 *this = path(_M_pathname
+ p
._M_pathname
);
889 const auto orig_pathlen
= _M_pathname
.length();
890 const auto orig_type
= _M_type();
891 const auto orig_size
= _M_cmpts
.size();
892 int orig_filenamelen
= -1;
893 basic_string_view
<value_type
> extra
;
895 // Ensure that '_M_pathname += p._M_pathname' won't throw:
896 _M_pathname
.reserve(orig_pathlen
+ p
._M_pathname
.length());
900 _Cmpt
* last
= nullptr;
901 if (p
._M_type() == _Type::_Multi
)
903 it
= p
._M_cmpts
._M_impl
->begin();
904 last
= p
._M_cmpts
._M_impl
->end();
908 c
= _Cmpt(p
._M_pathname
, p
._M_type(), 0);
913 if (it
->_M_type() == _Type::_Filename
)
915 // See if there's a filename or root-name at the end of the original path
916 // that we can add to.
917 if (_M_type() == _Type::_Filename
918 #if SLASHSLASH_IS_ROOTNAME
919 || _M_type() == _Type::_Root_name
923 if (p
._M_type() == _Type::_Filename
)
925 // Simplest case where we just add the whole of p to the
927 _M_pathname
+= p
._M_pathname
;
930 // Only the first component of s should be appended, do so below:
931 extra
= it
->_M_pathname
;
934 else if (_M_type() == _Type::_Multi
935 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
937 auto& back
= _M_cmpts
.back();
938 if (p
._M_type() == _Type::_Filename
)
940 basic_string_view
<value_type
> s
= p
._M_pathname
;
941 back
._M_pathname
+= s
;
946 orig_filenamelen
= back
._M_pathname
.length();
947 back
._M_pathname
+= it
->_M_pathname
;
948 extra
= it
->_M_pathname
;
952 else if (is_dir_sep(_M_pathname
.back()) && _M_type() == _Type::_Multi
953 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
954 orig_filenamelen
= 0; // current path has empty filename at end
957 if (_M_type() == _Type::_Multi
)
958 capacity
+= _M_cmpts
.size();
961 if (p
._M_type() == _Type::_Multi
)
962 capacity
+= p
._M_cmpts
.size();
968 _M_cmpts
.type(_Type::_Multi
);
969 _M_cmpts
.reserve(capacity
);
970 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
972 if (orig_type
!= _Type::_Multi
)
974 // Create single component from original path
975 auto ptr
= ::new(output
++) _Cmpt({}, orig_type
, 0);
976 ++_M_cmpts
._M_impl
->_M_size
;
977 ptr
->_M_pathname
.reserve(_M_pathname
.length() + extra
.length());
978 ptr
->_M_pathname
= _M_pathname
;
979 ptr
->_M_pathname
+= extra
;
981 #if SLASHSLASH_IS_ROOTNAME
982 if (orig_type
== _Type::_Root_name
)
984 basic_string_view
<value_type
> s(p
._M_pathname
);
985 ::new(output
++) _Cmpt(s
.substr(extra
.length(), 1),
986 _Type::_Root_dir
, orig_pathlen
+ extra
.length());
987 ++_M_cmpts
._M_impl
->_M_size
;
991 else if (orig_filenamelen
== 0 && it
!= last
)
993 // Remove empty filename at end of original path.
998 if (it
!= last
&& it
->_M_type() == _Type::_Root_name
)
1000 basic_string_view
<value_type
> s
= it
->_M_pathname
;
1001 auto pos
= orig_pathlen
;
1002 #if SLASHSLASH_IS_ROOTNAME
1006 ::new(output
++) _Cmpt(s
, _Type::_Filename
, pos
);
1007 ++_M_cmpts
._M_impl
->_M_size
;
1011 if (it
!= last
&& it
->_M_type() == _Type::_Root_dir
)
1016 auto pos
= it
->_M_pos
+ orig_pathlen
;
1017 ::new(output
++) _Cmpt(it
->_M_pathname
, _Type::_Filename
, pos
);
1018 ++_M_cmpts
._M_impl
->_M_size
;
1022 _M_pathname
+= p
._M_pathname
;
1024 if (is_dir_sep(_M_pathname
.back()))
1026 ::new(output
++) _Cmpt({}, _Type::_Filename
, _M_pathname
.length());
1027 ++_M_cmpts
._M_impl
->_M_size
;
1032 _M_pathname
.resize(orig_pathlen
);
1033 if (orig_type
== _Type::_Multi
)
1035 if (_M_cmpts
.size() > orig_size
)
1036 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
1037 if (orig_filenamelen
!= -1)
1039 if (_M_cmpts
.size() == orig_size
)
1041 auto& back
= _M_cmpts
.back();
1042 back
._M_pathname
.resize(orig_filenamelen
);
1043 if (orig_filenamelen
== 0)
1044 back
._M_pos
= orig_pathlen
;
1048 auto output
= _M_cmpts
._M_impl
->end();
1049 ::new(output
) _Cmpt({}, _Type::_Filename
, orig_pathlen
);
1050 ++_M_cmpts
._M_impl
->_M_size
;
1056 _M_cmpts
.type(orig_type
);
1057 __throw_exception_again
;
1064 path::_M_concat(basic_string_view
<value_type
> s
)
1075 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1076 if (_M_type() == _Type::_Root_name
1077 || (_M_type() == _Type::_Filename
&& _M_pathname
.size() == 1))
1079 // Handle path("C") += ":" and path("C:") += "/x"
1080 // FIXME: do this more efficiently
1081 *this = path(_M_pathname
+ string_type(s
));
1085 #if SLASHSLASH_IS_ROOTNAME
1086 if (_M_type() == _Type::_Root_dir
)
1088 // Handle path("/") += "/x" and path("//") += "x"
1089 // FIXME: do this more efficiently
1090 *this = path(_M_pathname
+ string_type(s
));
1095 const auto orig_pathlen
= _M_pathname
.length();
1096 const auto orig_type
= _M_type();
1097 const auto orig_size
= _M_cmpts
.size();
1098 int orig_filenamelen
= -1;
1099 basic_string_view
<value_type
> extra
;
1101 // Copy the input into _M_pathname:
1103 // Update s to refer to the new copy (this ensures s is not a dangling
1104 // reference to deallocated characters, in the case where it was referring
1105 // into _M_pathname or a member of _M_cmpts).
1107 const auto orig_pathname
= s
.substr(0, orig_pathlen
);
1108 s
.remove_prefix(orig_pathlen
);
1110 _Parser
parser(s
, orig_pathlen
);
1111 auto cmpt
= parser
.next();
1113 if (cmpt
.str
.data() == s
.data())
1115 // See if there's a filename or root-name at the end of the original path
1116 // that we can add to.
1117 if (_M_type() == _Type::_Filename
1118 #if SLASHSLASH_IS_ROOTNAME
1119 || _M_type() == _Type::_Root_name
1123 if (cmpt
.str
.length() == s
.length())
1125 // Simplest case where we just need to add the whole of s
1126 // to the original path, which was already done above.
1129 // Only the first component of s should be appended, do so below:
1131 cmpt
= {}; // so we don't process it again
1133 else if (_M_type() == _Type::_Multi
1134 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
1136 auto& back
= _M_cmpts
.back();
1137 if (cmpt
.str
.length() == s
.length())
1139 back
._M_pathname
+= s
;
1143 orig_filenamelen
= back
._M_pathname
.length();
1144 back
._M_pathname
+= cmpt
.str
;
1149 else if (is_dir_sep(orig_pathname
.back()) && _M_type() == _Type::_Multi
1150 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
1151 orig_filenamelen
= 0; // original path had empty filename at end
1153 std::array
<_Parser::cmpt
, 64> buf
;
1154 auto next
= buf
.begin();
1159 cmpt
= parser
.next();
1160 while (cmpt
.valid() && next
!= buf
.end())
1163 cmpt
= parser
.next();
1167 if (_M_type() == _Type::_Multi
)
1168 capacity
+= _M_cmpts
.size();
1172 capacity
+= next
- buf
.begin();
1174 if (cmpt
.valid()) // filled buffer before parsing whole input
1177 _Parser
parser2(parser
);
1178 while (parser2
.next().valid())
1182 #if SLASHSLASH_IS_ROOTNAME
1183 if (orig_type
== _Type::_Root_name
)
1184 ++capacity
; // Need to insert root-directory after root-name
1189 _M_cmpts
.type(_Type::_Multi
);
1190 _M_cmpts
.reserve(capacity
);
1191 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
1192 auto it
= buf
.begin();
1194 if (orig_type
!= _Type::_Multi
)
1196 // Create single component from original path
1197 auto p
= ::new(output
++) _Cmpt({}, orig_type
, 0);
1198 ++_M_cmpts
._M_impl
->_M_size
;
1199 p
->_M_pathname
.reserve(orig_pathname
.length() + extra
.length());
1200 p
->_M_pathname
= orig_pathname
;
1201 p
->_M_pathname
+= extra
;
1203 #if SLASHSLASH_IS_ROOTNAME
1204 if (orig_type
== _Type::_Root_name
)
1206 ::new(output
++) _Cmpt(s
.substr(extra
.length(), 1),
1207 _Type::_Root_dir
, orig_pathlen
+ extra
.length());
1208 ++_M_cmpts
._M_impl
->_M_size
;
1212 else if (orig_filenamelen
== 0 && extra
.empty())
1214 // Replace empty filename at end of original path.
1215 std::prev(output
)->_M_pathname
= it
->str
;
1216 std::prev(output
)->_M_pos
= parser
.offset(*it
);
1222 ::new(output
++) _Cmpt(it
->str
, _Type::_Filename
, parser
.offset(*it
));
1223 ++_M_cmpts
._M_impl
->_M_size
;
1227 if (next
== buf
.end())
1229 while (cmpt
.valid())
1231 auto pos
= parser
.offset(cmpt
);
1232 ::new(output
++) _Cmpt(cmpt
.str
, _Type::_Filename
, pos
);
1233 ++_M_cmpts
._M_impl
->_M_size
;
1234 cmpt
= parser
.next();
1240 _M_pathname
.resize(orig_pathlen
);
1241 if (orig_type
== _Type::_Multi
)
1243 _M_cmpts
._M_erase_from(_M_cmpts
.begin() + orig_size
);
1244 if (orig_filenamelen
!= -1)
1246 auto& back
= _M_cmpts
.back();
1247 back
._M_pathname
.resize(orig_filenamelen
);
1248 if (orig_filenamelen
== 0)
1249 back
._M_pos
= orig_pathlen
;
1254 _M_cmpts
.type(orig_type
);
1255 __throw_exception_again
;
1260 path::remove_filename()
1262 if (_M_type() == _Type::_Multi
)
1264 if (!_M_cmpts
.empty())
1266 auto cmpt
= std::prev(_M_cmpts
.end());
1267 if (cmpt
->_M_type() == _Type::_Filename
&& !cmpt
->empty())
1269 _M_pathname
.erase(cmpt
->_M_pos
);
1270 auto prev
= std::prev(cmpt
);
1271 if (prev
->_M_type() == _Type::_Root_dir
1272 || prev
->_M_type() == _Type::_Root_name
)
1274 _M_cmpts
.pop_back();
1275 if (_M_cmpts
.size() == 1)
1277 _M_cmpts
.type(_M_cmpts
.front()._M_type());
1286 else if (_M_type() == _Type::_Filename
)
1292 path::replace_filename(const path
& replacement
)
1295 operator/=(replacement
);
1299 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1300 const fs::path::value_type dot
= L
'.';
1302 const fs::path::value_type dot
= '.';
1306 path::replace_extension(const path
& replacement
)
1308 auto ext
= _M_find_extension();
1309 // Any existing extension() is removed
1310 if (ext
.first
&& ext
.second
!= string_type::npos
)
1312 if (ext
.first
== &_M_pathname
)
1313 _M_pathname
.erase(ext
.second
);
1316 auto& back
= _M_cmpts
.back();
1317 __glibcxx_assert( ext
.first
== &back
._M_pathname
);
1318 back
._M_pathname
.erase(ext
.second
);
1319 _M_pathname
.erase(back
._M_pos
+ ext
.second
);
1322 // If replacement is not empty and does not begin with a dot character,
1323 // a dot character is appended
1324 if (!replacement
.empty() && replacement
.native()[0] != dot
)
1326 operator+=(replacement
);
1331 path::compare(const path
& p
) const noexcept
1333 if (_M_pathname
== p
._M_pathname
)
1336 basic_string_view
<value_type
> lroot
, rroot
;
1337 if (_M_type() == _Type::_Root_name
)
1338 lroot
= _M_pathname
;
1339 else if (_M_type() == _Type::_Multi
1340 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
1341 lroot
= _M_cmpts
.front()._M_pathname
;
1342 if (p
._M_type() == _Type::_Root_name
)
1343 rroot
= p
._M_pathname
;
1344 else if (p
._M_type() == _Type::_Multi
1345 && p
._M_cmpts
.front()._M_type() == _Type::_Root_name
)
1346 rroot
= p
._M_cmpts
.front()._M_pathname
;
1347 if (int rootNameComparison
= lroot
.compare(rroot
))
1348 return rootNameComparison
;
1350 if (!this->has_root_directory() && p
.has_root_directory())
1352 else if (this->has_root_directory() && !p
.has_root_directory())
1355 using Iterator
= const _Cmpt
*;
1356 Iterator begin1
, end1
, begin2
, end2
;
1357 if (_M_type() == _Type::_Multi
)
1359 begin1
= _M_cmpts
.begin();
1360 end1
= _M_cmpts
.end();
1361 // Find start of this->relative_path()
1362 while (begin1
!= end1
&& begin1
->_M_type() != _Type::_Filename
)
1366 begin1
= end1
= nullptr;
1368 if (p
._M_type() == _Type::_Multi
)
1370 begin2
= p
._M_cmpts
.begin();
1371 end2
= p
._M_cmpts
.end();
1372 // Find start of p.relative_path()
1373 while (begin2
!= end2
&& begin2
->_M_type() != _Type::_Filename
)
1377 begin2
= end2
= nullptr;
1379 if (_M_type() == _Type::_Filename
)
1381 if (p
._M_type() == _Type::_Filename
)
1382 return native().compare(p
.native());
1383 else if (begin2
!= end2
)
1385 if (int ret
= native().compare(begin2
->native()))
1388 return ++begin2
== end2
? 0 : -1;
1393 else if (p
._M_type() == _Type::_Filename
)
1397 if (int ret
= begin1
->native().compare(p
.native()))
1400 return ++begin1
== end1
? 0 : +1;
1407 while (begin1
!= end1
&& begin2
!= end2
)
1409 if (int i
= begin1
->native().compare(begin2
->native()))
1425 path::compare(basic_string_view
<value_type
> s
) const noexcept
1427 if (_M_pathname
== s
)
1432 basic_string_view
<value_type
> lroot
, rroot
;
1433 if (_M_type() == _Type::_Root_name
)
1434 lroot
= _M_pathname
;
1435 else if (_M_type() == _Type::_Multi
1436 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
1437 lroot
= _M_cmpts
.front()._M_pathname
;
1438 auto root_path
= parser
.root_path();
1439 if (root_path
.first
.type
== _Type::_Root_name
)
1440 rroot
= root_path
.first
.str
;
1441 if (int rootNameComparison
= lroot
.compare(rroot
))
1442 return rootNameComparison
;
1444 const bool has_root_dir
= root_path
.first
.type
== _Type::_Root_dir
1445 || root_path
.second
.type
== _Type::_Root_dir
;
1446 if (!this->has_root_directory() && has_root_dir
)
1448 else if (this->has_root_directory() && !has_root_dir
)
1451 using Iterator
= const _Cmpt
*;
1452 Iterator begin1
, end1
;
1453 if (_M_type() == _Type::_Filename
)
1455 auto cmpt
= parser
.next();
1458 if (int ret
= this->native().compare(cmpt
.str
))
1460 return parser
.next().valid() ? -1 : 0;
1465 else if (_M_type() == _Type::_Multi
)
1467 begin1
= _M_cmpts
.begin();
1468 end1
= _M_cmpts
.end();
1469 while (begin1
!= end1
&& begin1
->_M_type() != _Type::_Filename
)
1473 begin1
= end1
= nullptr;
1476 auto cmpt
= parser
.next();
1477 while (begin1
!= end1
&& cmpt
.valid())
1479 if (int i
= begin1
->native().compare(cmpt
.str
))
1482 cmpt
= parser
.next();
1495 path::root_name() const
1498 if (_M_type() == _Type::_Root_name
)
1500 else if (_M_cmpts
.size() && _M_cmpts
.begin()->_M_type() == _Type::_Root_name
)
1501 __ret
= *_M_cmpts
.begin();
1506 path::root_directory() const
1509 if (_M_type() == _Type::_Root_dir
)
1511 __ret
._M_cmpts
.type(_Type::_Root_dir
);
1512 __ret
._M_pathname
.assign(1, preferred_separator
);
1514 else if (!_M_cmpts
.empty())
1516 auto __it
= _M_cmpts
.begin();
1517 if (__it
->_M_type() == _Type::_Root_name
)
1519 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1526 path::root_path() const
1529 if (_M_type() == _Type::_Root_name
)
1531 else if (_M_type() == _Type::_Root_dir
)
1533 __ret
._M_pathname
.assign(1, preferred_separator
);
1534 __ret
._M_cmpts
.type(_Type::_Root_dir
);
1536 else if (!_M_cmpts
.empty())
1538 auto __it
= _M_cmpts
.begin();
1539 if (__it
->_M_type() == _Type::_Root_name
)
1542 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1545 else if (__it
->_M_type() == _Type::_Root_dir
)
1552 path::relative_path() const
1555 if (_M_type() == _Type::_Filename
)
1557 else if (!_M_cmpts
.empty())
1559 auto __it
= _M_cmpts
.begin();
1560 if (__it
->_M_type() == _Type::_Root_name
)
1562 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1564 if (__it
!= _M_cmpts
.end())
1565 __ret
.assign(_M_pathname
.substr(__it
->_M_pos
));
1571 path::parent_path() const
1574 if (!has_relative_path())
1576 else if (_M_cmpts
.size() >= 2)
1578 const auto parent
= std::prev(_M_cmpts
.end(), 2);
1579 const auto len
= parent
->_M_pos
+ parent
->_M_pathname
.length();
1580 __ret
.assign(_M_pathname
.substr(0, len
));
1586 path::has_root_name() const noexcept
1588 if (_M_type() == _Type::_Root_name
)
1590 if (!_M_cmpts
.empty() && _M_cmpts
.begin()->_M_type() == _Type::_Root_name
)
1596 path::has_root_directory() const noexcept
1598 if (_M_type() == _Type::_Root_dir
)
1600 if (!_M_cmpts
.empty())
1602 auto __it
= _M_cmpts
.begin();
1603 if (__it
->_M_type() == _Type::_Root_name
)
1605 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1612 path::has_root_path() const noexcept
1614 if (_M_type() == _Type::_Root_name
|| _M_type() == _Type::_Root_dir
)
1616 if (!_M_cmpts
.empty())
1618 auto __type
= _M_cmpts
.front()._M_type();
1619 if (__type
== _Type::_Root_name
|| __type
== _Type::_Root_dir
)
1626 path::has_relative_path() const noexcept
1628 if (_M_type() == _Type::_Filename
&& !_M_pathname
.empty())
1630 if (!_M_cmpts
.empty())
1632 auto __it
= _M_cmpts
.begin();
1633 if (__it
->_M_type() == _Type::_Root_name
)
1635 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1637 if (__it
!= _M_cmpts
.end() && !__it
->_M_pathname
.empty())
1645 path::has_parent_path() const noexcept
1647 if (!has_relative_path())
1649 return _M_cmpts
.size() >= 2;
1653 path::has_filename() const noexcept
1657 if (_M_type() == _Type::_Filename
)
1658 return !_M_pathname
.empty();
1659 if (_M_type() == _Type::_Multi
)
1661 if (_M_pathname
.back() == preferred_separator
)
1663 return _M_cmpts
.back().has_filename();
1670 inline bool is_dot(fs::path::value_type c
) { return c
== dot
; }
1672 inline bool is_dot(const fs::path
& path
)
1674 const auto& filename
= path
.native();
1675 return filename
.size() == 1 && is_dot(filename
[0]);
1678 inline bool is_dotdot(const fs::path
& path
)
1680 const auto& filename
= path
.native();
1681 return filename
.size() == 2 && is_dot(filename
[0]) && is_dot(filename
[1]);
1686 path::lexically_normal() const
1689 C++17 [fs.path.generic] p6
1690 - If the path is empty, stop.
1691 - Replace each slash character in the root-name with a preferred-separator.
1692 - Replace each directory-separator with a preferred-separator.
1693 - Remove each dot filename and any immediately following directory-separator.
1694 - As long as any appear, remove a non-dot-dot filename immediately followed
1695 by a directory-separator and a dot-dot filename, along with any immediately
1696 following directory-separator.
1697 - If there is a root-directory, remove all dot-dot filenames and any
1698 directory-separators immediately following them.
1699 - If the last filename is dot-dot, remove any trailing directory-separator.
1700 - If the path is empty, add a dot.
1703 // If the path is empty, stop.
1706 for (auto& p
: *this)
1708 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1709 // Replace each slash character in the root-name
1710 if (p
._M_type() == _Type::_Root_name
|| p
._M_type() == _Type::_Root_dir
)
1712 string_type s
= p
.native();
1713 std::replace(s
.begin(), s
.end(), L
'/', L
'\\');
1720 if (ret
.has_filename())
1722 // remove a non-dot-dot filename immediately followed by /..
1723 if (!is_dotdot(ret
.filename()))
1724 ret
.remove_filename();
1728 else if (!ret
.has_relative_path())
1730 // remove a dot-dot filename immediately after root-directory
1731 if (!ret
.has_root_directory())
1736 // Got a path with a relative path (i.e. at least one non-root
1737 // element) and no filename at the end (i.e. empty last element),
1738 // so must have a trailing slash. See what is before it.
1739 auto elem
= ret
._M_cmpts
.end() - 2;
1740 if (elem
->has_filename() && !is_dotdot(*elem
))
1742 // Remove the filename before the trailing slash
1743 // (equiv. to ret = ret.parent_path().remove_filename())
1745 if (elem
== ret
._M_cmpts
.begin())
1749 ret
._M_pathname
.erase(elem
->_M_pos
);
1750 // Remove empty filename at the end:
1751 ret
._M_cmpts
.pop_back();
1752 // If we still have a trailing non-root dir separator
1753 // then leave an empty filename at the end:
1754 if (std::prev(elem
)->_M_type() == _Type::_Filename
)
1756 else // remove the component completely:
1757 ret
._M_cmpts
.pop_back();
1761 // Append the ".." to something ending in "../" which happens
1762 // when normalising paths like ".././.." and "../a/../.."
1768 #if SLASHSLASH_IS_ROOTNAME
1769 else if (p
._M_type() == _Type::_Root_dir
)
1770 ret
+= '/'; // using operator/=('/') would replace whole of ret
1776 if (ret
._M_cmpts
.size() >= 2)
1778 auto back
= std::prev(ret
.end());
1779 // If the last filename is dot-dot, ...
1780 if (back
->empty() && is_dotdot(*std::prev(back
)))
1781 // ... remove any trailing directory-separator.
1782 ret
= ret
.parent_path();
1784 // If the path is empty, add a dot.
1785 else if (ret
.empty())
1792 path::lexically_relative(const path
& base
) const
1795 if (root_name() != base
.root_name())
1797 if (is_absolute() != base
.is_absolute())
1799 if (!has_root_directory() && base
.has_root_directory())
1801 auto [a
, b
] = std::mismatch(begin(), end(), base
.begin(), base
.end());
1802 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1803 // _GLIBCXX_RESOLVE_LIB_DEFECTS
1804 // 3070. path::lexically_relative causes surprising results if a filename
1805 // can also be a root-name
1807 for (auto& p
: _M_cmpts
)
1808 if (p
._M_type() == _Type::_Filename
&& is_disk_designator(p
.native()))
1811 for (auto i
= b
, end
= base
.end(); i
!= end
; ++i
)
1812 if (i
->_M_type() == _Type::_Filename
&& is_disk_designator(i
->native()))
1815 if (a
== end() && b
== base
.end())
1820 for (; b
!= base
.end(); ++b
)
1825 else if (!p
.empty() && !is_dot(p
))
1828 if (n
== 0 && (a
== end() || a
->empty()))
1832 const path
dotdot("..");
1835 for (; a
!= end(); ++a
)
1843 path::lexically_proximate(const path
& base
) const
1845 path rel
= lexically_relative(base
);
1851 std::pair
<const path::string_type
*, std::size_t>
1852 path::_M_find_extension() const noexcept
1854 const string_type
* s
= nullptr;
1856 if (_M_type() == _Type::_Filename
)
1858 else if (_M_type() == _Type::_Multi
&& !_M_cmpts
.empty())
1860 const auto& c
= _M_cmpts
.back();
1861 if (c
._M_type() == _Type::_Filename
)
1867 if (auto sz
= s
->size())
1869 if (sz
<= 2 && (*s
)[0] == dot
)
1870 return { s
, string_type::npos
};
1871 if (const auto pos
= s
->rfind(dot
))
1873 return { s
, string_type::npos
};
1880 path::_M_split_cmpts()
1884 if (_M_pathname
.empty())
1886 _M_cmpts
.type(_Type::_Filename
);
1890 _Parser
parser(_M_pathname
);
1892 std::array
<_Parser::cmpt
, 64> buf
;
1893 auto next
= buf
.begin();
1895 // look for root name or root directory
1896 auto root_path
= parser
.root_path();
1897 if (root_path
.first
.valid())
1899 *next
++ = root_path
.first
;
1900 if (root_path
.second
.valid())
1901 *next
++ = root_path
.second
;
1904 auto cmpt
= parser
.next();
1905 while (cmpt
.valid())
1910 cmpt
= parser
.next();
1912 while (cmpt
.valid() && next
!= buf
.end());
1914 if (next
== buf
.end())
1916 _M_cmpts
.type(_Type::_Multi
);
1917 _M_cmpts
.reserve(_M_cmpts
.size() + buf
.size());
1918 auto output
= _M_cmpts
._M_impl
->end();
1919 for (const auto& c
: buf
)
1921 ::new(output
++) _Cmpt(c
.str
, c
.type
, parser
.offset(c
));
1922 ++_M_cmpts
._M_impl
->_M_size
;
1928 if (auto n
= next
- buf
.begin())
1930 if (n
== 1 && _M_cmpts
.empty())
1932 _M_cmpts
.type(buf
.front().type
);
1936 _M_cmpts
.type(_Type::_Multi
);
1937 _M_cmpts
.reserve(_M_cmpts
.size() + n
, true);
1938 auto output
= _M_cmpts
._M_impl
->end();
1939 for (int i
= 0; i
< n
; ++i
)
1941 const auto& c
= buf
[i
];
1942 ::new(output
++) _Cmpt(c
.str
, c
.type
, parser
.offset(c
));
1943 ++_M_cmpts
._M_impl
->_M_size
;
1949 path::_S_convert_loc(const char* __first
, const char* __last
,
1950 [[maybe_unused
]] const std::locale
& __loc
)
1952 #if _GLIBCXX_USE_WCHAR_T
1953 auto& __cvt
= std::use_facet
<codecvt
<wchar_t, char, mbstate_t>>(__loc
);
1954 basic_string
<wchar_t> __ws
;
1955 if (!__str_codecvt_in_all(__first
, __last
, __ws
, __cvt
))
1956 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
1957 "Cannot convert character sequence",
1958 std::make_error_code(errc::illegal_byte_sequence
)));
1959 return _S_convert(std::move(__ws
));
1961 return {__first
, __last
};
1966 fs::hash_value(const path
& p
) noexcept
1968 // [path.non-member]
1969 // "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)."
1970 // Equality works as if by traversing the range [begin(), end()), meaning
1971 // e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname
1972 // but need to iterate over individual elements. Use the hash_combine from
1973 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
1975 for (const auto& x
: p
)
1977 seed
^= std::hash
<path::string_type
>()(x
.native()) + 0x9e3779b9
1978 + (seed
<<6) + (seed
>>2);
1983 struct fs::filesystem_error::_Impl
1985 _Impl(string_view what_arg
, const path
& p1
, const path
& p2
)
1986 : path1(p1
), path2(p2
), what(make_what(what_arg
, &p1
, &p2
))
1989 _Impl(string_view what_arg
, const path
& p1
)
1990 : path1(p1
), path2(), what(make_what(what_arg
, &p1
, nullptr))
1993 _Impl(string_view what_arg
)
1994 : what(make_what(what_arg
, nullptr, nullptr))
1998 make_what(string_view s
, const path
* p1
, const path
* p2
)
2000 const std::string pstr1
= p1
? p1
->u8string() : std::string
{};
2001 const std::string pstr2
= p2
? p2
->u8string() : std::string
{};
2002 const size_t len
= 18 + s
.length()
2003 + (pstr1
.length() ? pstr1
.length() + 3 : 0)
2004 + (pstr2
.length() ? pstr2
.length() + 3 : 0);
2007 w
= "filesystem error: ";
2029 template class std::__shared_ptr
<const fs::filesystem_error::_Impl
>;
2031 fs::filesystem_error::
2032 filesystem_error(const string
& what_arg
, error_code ec
)
2033 : system_error(ec
, what_arg
),
2034 _M_impl(std::__make_shared
<_Impl
>(system_error::what()))
2037 fs::filesystem_error::
2038 filesystem_error(const string
& what_arg
, const path
& p1
, error_code ec
)
2039 : system_error(ec
, what_arg
),
2040 _M_impl(std::__make_shared
<_Impl
>(system_error::what(), p1
))
2043 fs::filesystem_error::
2044 filesystem_error(const string
& what_arg
, const path
& p1
, const path
& p2
,
2046 : system_error(ec
, what_arg
),
2047 _M_impl(std::__make_shared
<_Impl
>(system_error::what(), p1
, p2
))
2050 fs::filesystem_error::~filesystem_error() = default;
2053 fs::filesystem_error::path1() const noexcept
2054 { return _M_impl
->path1
; }
2057 fs::filesystem_error::path2() const noexcept
2058 { return _M_impl
->path2
; }
2061 fs::filesystem_error::what() const noexcept
2062 { return _M_impl
->what
.c_str(); }