]> git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/src/c++17/fs_path.cc
Update copyright years.
[thirdparty/gcc.git] / libstdc++-v3 / src / c++17 / fs_path.cc
1 // Class filesystem::path -*- C++ -*-
2
3 // Copyright (C) 2014-2024 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
24
25 #ifndef _GLIBCXX_USE_CXX11_ABI
26 # define _GLIBCXX_USE_CXX11_ABI 1
27 #endif
28
29 #ifdef __CYGWIN__
30 // Interpret "//x" as a root-name, not root-dir + filename
31 # define SLASHSLASH_IS_ROOTNAME 1
32 #endif
33
34 #include <filesystem>
35 #include <algorithm>
36 #include <array>
37 #include <bits/stl_uninitialized.h>
38
39 namespace fs = std::filesystem;
40 using fs::path;
41
42 static inline bool is_dir_sep(path::value_type ch)
43 {
44 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
45 return ch == L'/' || ch == path::preferred_separator;
46 #else
47 return ch == '/';
48 #endif
49 }
50
51 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
52 static inline bool is_disk_designator(std::wstring_view s)
53 {
54 return s.length() == 2 && s[1] == L':';
55 }
56 #endif
57
58 struct path::_Parser
59 {
60 using string_view_type = std::basic_string_view<value_type>;
61
62 struct cmpt
63 {
64 string_view_type str;
65 _Type type = _Type::_Multi;
66
67 bool valid() const { return type != _Type::_Multi; }
68 };
69
70 string_view_type input;
71 string_view_type::size_type pos = 0;
72 size_t origin;
73 _Type last_type = _Type::_Multi;
74
75 _Parser(string_view_type s, size_t o = 0) : input(s), origin(o) { }
76
77 pair<cmpt, cmpt> root_path() noexcept
78 {
79 pos = 0;
80 pair<cmpt, cmpt> root;
81
82 const size_t len = input.size();
83
84 // look for root name or root directory
85 if (len && is_dir_sep(input[0]))
86 {
87 #if SLASHSLASH_IS_ROOTNAME
88 // look for root name, such as "//foo"
89 if (len > 2 && input[1] == input[0])
90 {
91 if (!is_dir_sep(input[2]))
92 {
93 // got root name, find its end
94 pos = 3;
95 while (pos < len && !is_dir_sep(input[pos]))
96 ++pos;
97 root.first.str = input.substr(0, pos);
98 root.first.type = _Type::_Root_name;
99
100 if (pos < len) // also got root directory
101 {
102 root.second.str = input.substr(pos, 1);
103 root.second.type = _Type::_Root_dir;
104 ++pos;
105 }
106 }
107 else
108 {
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;
113 pos += 2;
114 }
115 }
116 else
117 #endif
118 {
119 root.first.str = input.substr(0, 1);
120 root.first.type = _Type::_Root_dir;
121 ++pos;
122 }
123 // Find the start of the first filename
124 while (pos < len && is_dir_sep(input[pos]))
125 ++pos;
126 }
127 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
128 else if (is_disk_designator(input.substr(0, 2)))
129 {
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]))
134 {
135 root.second.str = input.substr(2, 1);
136 root.second.type = _Type::_Root_dir;
137 }
138 pos = input.find_first_not_of(L"/\\", 2);
139 }
140 #endif
141
142 if (root.second.valid())
143 last_type = root.second.type;
144 else
145 last_type = root.first.type;
146
147 return root;
148 }
149
150 cmpt next() noexcept
151 {
152 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
153 string_view_type sep = L"/\\";
154 #else
155 char sep = '/';
156 #endif
157
158 const int last_pos = pos;
159
160 cmpt f;
161 if (pos != input.npos)
162 {
163 pos = input.find_first_not_of(sep, pos);
164 if (pos != input.npos)
165 {
166 const auto end = input.find_first_of(sep, pos);
167 f.str = input.substr(pos, end - pos);
168 f.type = _Type::_Filename;
169 pos = end;
170 }
171 else if (last_type == _Type::_Filename
172 || (last_pos == 0 && !input.empty()))
173 {
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;
179 }
180 }
181 last_type = f.type;
182 return f;
183 }
184
185 string_view_type::size_type
186 offset(const cmpt& c) const noexcept
187 { return origin + c.str.data() - input.data(); }
188 };
189
190 inline
191 path::path(basic_string_view<value_type> __str, _Type __type)
192 : _M_pathname(__str)
193 {
194 __glibcxx_assert(__type != _Type::_Multi);
195 _M_cmpts.type(__type);
196 }
197
198 inline
199 path::_Cmpt::_Cmpt(basic_string_view<value_type> __s, _Type __t, size_t __pos)
200 : path(__s, __t), _M_pos(__pos)
201 { }
202
203 struct path::_List::_Impl
204 {
205 using value_type = _Cmpt;
206
207 _Impl(int cap) : _M_size(0), _M_capacity(cap) { }
208
209 alignas(value_type) int _M_size;
210 int _M_capacity;
211
212 using iterator = value_type*;
213 using const_iterator = const value_type*;
214
215 iterator begin() { return reinterpret_cast<value_type*>(this + 1); }
216 iterator end() { return begin() + size(); }
217
218 const_iterator begin() const
219 { return reinterpret_cast<const value_type*>(this + 1); }
220 const_iterator end() const { return begin() + size(); }
221
222 const value_type& front() const { return *begin(); }
223 const value_type& back() const { return end()[-1]; }
224
225 int size() const { return _M_size; }
226 int capacity() const { return _M_capacity; }
227 bool empty() const { return _M_size == 0; }
228
229 void clear() { std::destroy_n(begin(), _M_size); _M_size = 0; }
230
231 void pop_back()
232 {
233 back().~_Cmpt();
234 --_M_size;
235 }
236
237 void _M_erase_from(const_iterator pos)
238 {
239 iterator first = begin() + (pos - begin());
240 iterator last = end();
241 std::destroy(first, last);
242 _M_size -= last - first;
243 }
244
245 unique_ptr<_Impl, _Impl_deleter> copy() const
246 {
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());
251 newptr->_M_size = n;
252 return newptr;
253 }
254
255 // Clear the lowest two bits from the pointer (i.e. remove the _Type value)
256 static _Impl* notype(_Impl* p)
257 {
258 constexpr uintptr_t mask = ~(uintptr_t)0x3;
259 return reinterpret_cast<_Impl*>(reinterpret_cast<uintptr_t>(p) & mask);
260 }
261 };
262
263 void path::_List::_Impl_deleter::operator()(_Impl* p) const noexcept
264 {
265 p = _Impl::notype(p);
266 if (p)
267 {
268 __glibcxx_assert(p->_M_size <= p->_M_capacity);
269 p->clear();
270 ::operator delete(p, sizeof(*p) + p->_M_capacity * sizeof(value_type));
271 }
272 }
273
274 path::_List::_List() : _M_impl(reinterpret_cast<_Impl*>(_Type::_Filename)) { }
275
276 path::_List::_List(const _List& other)
277 {
278 if (!other.empty())
279 _M_impl = other._M_impl->copy();
280 else
281 type(other.type());
282 }
283
284 path::_List&
285 path::_List::operator=(const _List& other)
286 {
287 if (!other.empty())
288 {
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)
293 {
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)
301 {
302 std::uninitialized_copy_n(from + oldsize, newsize - oldsize,
303 to + oldsize);
304 impl->_M_size = newsize;
305 }
306 else if (newsize < oldsize)
307 impl->_M_erase_from(impl->begin() + newsize);
308 std::copy_n(from, minsize, to);
309 type(_Type::_Multi);
310 }
311 else
312 _M_impl = other._M_impl->copy();
313 }
314 else
315 {
316 clear();
317 type(other.type());
318 }
319 return *this;
320 }
321
322 inline void
323 path::_List::type(_Type t) noexcept
324 {
325 auto val = reinterpret_cast<uintptr_t>(_Impl::notype(_M_impl.release()));
326 _M_impl.reset(reinterpret_cast<_Impl*>(val | (unsigned char)t));
327 }
328
329 inline int
330 path::_List::size() const noexcept
331 {
332 if (auto* ptr = _Impl::notype(_M_impl.get()))
333 return ptr->size();
334 return 0;
335 }
336
337 inline int
338 path::_List::capacity() const noexcept
339 {
340 if (auto* ptr = _Impl::notype(_M_impl.get()))
341 return ptr->capacity();
342 return 0;
343 }
344
345 inline bool
346 path::_List::empty() const noexcept
347 {
348 return size() == 0;
349 }
350
351 inline auto
352 path::_List::begin() noexcept
353 -> iterator
354 {
355 __glibcxx_assert(!empty());
356 if (auto* ptr = _Impl::notype(_M_impl.get()))
357 return ptr->begin();
358 return nullptr;
359 }
360
361 inline auto
362 path::_List::end() noexcept
363 -> iterator
364 {
365 __glibcxx_assert(!empty());
366 if (auto* ptr = _Impl::notype(_M_impl.get()))
367 return ptr->end();
368 return nullptr;
369 }
370
371 auto
372 path::_List::begin() const noexcept
373 -> const_iterator
374 {
375 __glibcxx_assert(!empty());
376 if (auto* ptr = _Impl::notype(_M_impl.get()))
377 return ptr->begin();
378 return nullptr;
379 }
380
381 auto
382 path::_List::end() const noexcept
383 -> const_iterator
384 {
385 __glibcxx_assert(!empty());
386 if (auto* ptr = _Impl::notype(_M_impl.get()))
387 return ptr->end();
388 return nullptr;
389 }
390
391 inline auto
392 path::_List::front() noexcept
393 -> value_type&
394 {
395 return *_M_impl->begin();
396 }
397
398 inline auto
399 path::_List::back() noexcept
400 -> value_type&
401 {
402 return _M_impl->begin()[_M_impl->size() - 1];
403 }
404
405 inline auto
406 path::_List::front() const noexcept
407 -> const value_type&
408 {
409 return *_M_impl->begin();
410 }
411
412 inline auto
413 path::_List::back() const noexcept
414 -> const value_type&
415 {
416 return _M_impl->begin()[_M_impl->size() - 1];
417 }
418
419 inline void
420 path::_List::pop_back()
421 {
422 __glibcxx_assert(size() > 0);
423 _M_impl->pop_back();
424 }
425
426 inline void
427 path::_List::_M_erase_from(const_iterator pos)
428 {
429 _M_impl->_M_erase_from(pos);
430 }
431
432 inline void
433 path::_List::clear()
434 {
435 if (auto ptr = _Impl::notype(_M_impl.get()))
436 ptr->clear();
437 }
438
439 void
440 path::_List::reserve(int newcap, bool exact = false)
441 {
442 // __glibcxx_assert(type() == _Type::_Multi);
443
444 _Impl* curptr = _Impl::notype(_M_impl.get());
445
446 int curcap = curptr ? curptr->capacity() : 0;
447
448 if (curcap < newcap)
449 {
450 if (!exact && newcap < int(1.5 * curcap))
451 newcap = 1.5 * curcap;
452
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;
456 if (cursize)
457 {
458 std::uninitialized_move_n(curptr->begin(), cursize, newptr->begin());
459 newptr->_M_size = cursize;
460 }
461 std::swap(newptr, _M_impl);
462 }
463 }
464
465 path&
466 path::operator=(const path& p)
467 {
468 if (&p == this) [[__unlikely__]]
469 return *this;
470
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
474 return *this;
475 }
476
477 path&
478 path::operator/=(const path& __p)
479 {
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);
484
485 basic_string_view<value_type> __lhs = _M_pathname;
486 bool __add_sep = false;
487
488 if (__p.has_root_directory())
489 {
490 // Remove any root directory and relative path
491 if (_M_type() != _Type::_Root_name)
492 {
493 if (!_M_cmpts.empty()
494 && _M_cmpts.front()._M_type() == _Type::_Root_name)
495 __lhs = _M_cmpts.front()._M_pathname;
496 else
497 __lhs = {};
498 }
499 }
500 else if (has_filename() || (!has_root_directory() && is_absolute()))
501 __add_sep = true;
502
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)
506 __rhs = {};
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());
510
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)
514 {
515 // Construct new path and swap (strong exception-safety guarantee).
516 string_type __tmp;
517 __tmp.reserve(__len);
518 __tmp = __lhs;
519 if (__add_sep)
520 __tmp += preferred_separator;
521 __tmp += __rhs;
522 path __newp = std::move(__tmp);
523 swap(__newp);
524 }
525 else
526 {
527 _M_pathname = __lhs;
528 if (__add_sep)
529 _M_pathname += preferred_separator;
530 _M_pathname += __rhs;
531 __try
532 {
533 _M_split_cmpts();
534 }
535 __catch (...)
536 {
537 __try
538 {
539 // try to restore original state
540 _M_pathname.resize(__lhs.length());
541 _M_split_cmpts();
542 }
543 __catch (...)
544 {
545 // give up, basic exception safety guarantee only:
546 clear();
547 __throw_exception_again;
548 }
549 }
550 }
551 #else
552 // POSIX version is simpler than the specification in the standard,
553 // as any path with root-name or root-dir is absolute.
554
555 if (__p.is_absolute() || this->empty())
556 {
557 return operator=(__p);
558 }
559
560 using string_view_type = basic_string_view<value_type>;
561
562 string_view_type sep;
563 if (has_filename())
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
568 #endif
569 else if (__p.empty())
570 return *this; // nothing to do
571
572 const auto orig_pathlen = _M_pathname.length();
573 const auto orig_size = _M_cmpts.size();
574 const auto orig_type = _M_type();
575
576 int capacity = 0;
577 if (_M_type() == _Type::_Multi)
578 capacity += _M_cmpts.size();
579 else if (!empty())
580 capacity += 1;
581 if (__p._M_type() == _Type::_Multi)
582 capacity += __p._M_cmpts.size();
583 else if (!__p.empty() || !sep.empty())
584 capacity += 1;
585 #if SLASHSLASH_IS_ROOTNAME
586 if (orig_type == _Type::_Root_name)
587 ++capacity; // Need to insert root-directory after root-name
588 #endif
589
590 if (orig_type == _Type::_Multi)
591 {
592 const int curcap = _M_cmpts._M_impl->capacity();
593 if (capacity > curcap)
594 capacity = std::max(capacity, (int) (curcap * 1.5));
595 }
596
597 _M_pathname.reserve(_M_pathname.length() + sep.length()
598 + __p._M_pathname.length());
599
600 __try
601 {
602 _M_pathname += sep;
603 const auto basepos = _M_pathname.length();
604 _M_pathname += __p.native();
605
606 _M_cmpts.type(_Type::_Multi);
607 _M_cmpts.reserve(capacity);
608 _Cmpt* output = _M_cmpts._M_impl->end();
609
610 if (orig_type == _Type::_Multi)
611 {
612 // Remove empty final component
613 if (_M_cmpts._M_impl->back().empty())
614 {
615 _M_cmpts.pop_back();
616 --output;
617 }
618 }
619 else if (orig_pathlen != 0)
620 {
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)
627 {
628 ::new(output++) _Cmpt(sep, _Type::_Root_dir,
629 orig_pathlen + sep.length());
630 ++_M_cmpts._M_impl->_M_size;
631 }
632 #endif
633 }
634
635 if (__p._M_type() == _Type::_Multi)
636 {
637 for (auto& c : *__p._M_cmpts._M_impl)
638 {
639 ::new(output++) _Cmpt(c._M_pathname, _Type::_Filename,
640 c._M_pos + basepos);
641 ++_M_cmpts._M_impl->_M_size;
642 }
643 }
644 else if (!__p.empty() || !sep.empty())
645 {
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;
649 }
650 }
651 __catch (...)
652 {
653 _M_pathname.resize(orig_pathlen);
654 if (orig_type == _Type::_Multi)
655 _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
656 else
657 _M_cmpts.clear();
658 _M_cmpts.type(orig_type);
659 __throw_exception_again;
660 }
661 #endif
662 return *this;
663 }
664
665 // [fs.path.append]
666 void
667 path::_M_append(basic_string_view<value_type> s)
668 {
669 _Parser parser(s);
670 auto root_path = parser.root_path();
671
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()))
676 {
677 operator=(s);
678 return;
679 }
680
681 basic_string_view<value_type> lhs = _M_pathname;
682 bool add_sep = false;
683
684 bool has_root_directory = root_path.first.type == _Type::_Root_dir
685 || root_path.second.type == _Type::_Root_dir;
686
687 if (has_root_directory)
688 {
689 // Remove any root directory and relative path
690 if (_M_type() != _Type::_Root_name)
691 {
692 if (!_M_cmpts.empty()
693 && _M_cmpts.front()._M_type() == _Type::_Root_name)
694 lhs = _M_cmpts.front()._M_pathname;
695 else
696 lhs = {};
697 }
698 }
699 else if (has_filename() || (!has_root_directory && is_absolute))
700 add_sep = true;
701
702 basic_string_view<value_type> rhs = s;
703 // Omit any root-name from the generic format pathname:
704 if (has_root_name)
705 rhs.remove_prefix(root_path.first.str.length());
706
707 // Construct new path and swap (strong exception-safety guarantee).
708 string_type tmp;
709 tmp.reserve(lhs.size() + (int)add_sep + rhs.size());
710 tmp = lhs;
711 if (add_sep)
712 tmp += preferred_separator;
713 tmp += rhs;
714 path newp = std::move(tmp);
715 swap(newp);
716 #else
717
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())
721 {
722 operator=(s);
723 return;
724 }
725
726 const auto orig_pathlen = _M_pathname.length();
727 const auto orig_size = _M_cmpts.size();
728 const auto orig_type = _M_type();
729
730 basic_string_view<value_type> sep;
731 if (has_filename())
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
736 #endif
737 else if (s.empty())
738 return; // nothing to do
739
740 // Copy the input into _M_pathname:
741 _M_pathname += s;
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).
746 s = _M_pathname;
747 const auto orig_pathname = s.substr(0, orig_pathlen);
748 s.remove_prefix(orig_pathlen + sep.length());
749
750 parser.input = s; // reset parser to use updated string view
751 const auto basepos = orig_pathname.length() + sep.length();
752 parser.origin = basepos;
753
754 std::array<_Parser::cmpt, 64> buf;
755 auto next = buf.begin();
756
757 int capacity = 0;
758 if (_M_type() == _Type::_Multi)
759 capacity += _M_cmpts.size();
760 else if (!empty())
761 capacity += 1;
762
763 auto cmpt = parser.next();
764 if (cmpt.valid())
765 {
766 do
767 {
768 *next++ = cmpt;
769 cmpt = parser.next();
770 }
771 while (cmpt.valid() && next != buf.end());
772
773 capacity += next - buf.begin();
774 if (cmpt.valid()) // filled buffer before parsing whole input
775 {
776 ++capacity;
777 _Parser parser2(parser);
778 while (parser2.next().valid())
779 ++capacity;
780 }
781 }
782 else if (!sep.empty())
783 ++capacity;
784
785 #if SLASHSLASH_IS_ROOTNAME
786 if (orig_type == _Type::_Root_name)
787 ++capacity; // Need to insert root-directory after root-name
788 #endif
789
790 __try
791 {
792 _M_cmpts.type(_Type::_Multi);
793 _M_cmpts.reserve(capacity);
794 _Cmpt* output = _M_cmpts._M_impl->end();
795
796 if (orig_type == _Type::_Multi)
797 {
798 // Remove empty final component
799 if (_M_cmpts._M_impl->back().empty())
800 {
801 _M_cmpts.pop_back();
802 --output;
803 }
804 }
805 else if (orig_pathlen != 0)
806 {
807 // Create single component from original path
808 ::new(output++) _Cmpt(orig_pathname, orig_type, 0);
809 ++_M_cmpts._M_impl->_M_size;
810
811 #if SLASHSLASH_IS_ROOTNAME
812 if (!sep.empty() && orig_type == _Type::_Root_name)
813 {
814 ::new(output++) _Cmpt(sep, _Type::_Root_dir,
815 orig_pathlen + sep.length());
816 ++_M_cmpts._M_impl->_M_size;
817 }
818 #endif
819 }
820
821 if (next != buf.begin())
822 {
823 for (auto it = buf.begin(); it != next; ++it)
824 {
825 auto c = *it;
826 ::new(output++) _Cmpt(c.str, c.type, parser.offset(c));
827 ++_M_cmpts._M_impl->_M_size;
828 }
829 while (cmpt.valid())
830 {
831 ::new(output++) _Cmpt(cmpt.str, cmpt.type, parser.offset(cmpt));
832 ++_M_cmpts._M_impl->_M_size;
833 cmpt = parser.next();
834 }
835 }
836 else if (!sep.empty())
837 {
838 // Empty filename at the end:
839 ::new(output) _Cmpt({}, _Type::_Filename, basepos);
840 ++_M_cmpts._M_impl->_M_size;
841 }
842 }
843 __catch (...)
844 {
845 _M_pathname.resize(orig_pathlen);
846 if (orig_type == _Type::_Multi)
847 _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
848 else
849 _M_cmpts.clear();
850 _M_cmpts.type(orig_type);
851 __throw_exception_again;
852 }
853 #endif
854 }
855
856 // [fs.path.concat]
857 path&
858 path::operator+=(const path& p)
859 {
860 if (p.empty())
861 return *this;
862
863 if (this->empty())
864 {
865 operator=(p);
866 return *this;
867 }
868
869 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
870 if (_M_type() == _Type::_Root_name
871 || (_M_type() == _Type::_Filename && _M_pathname.size() == 1))
872 {
873 // Handle path("C") += path(":") and path("C:") += path("/x")
874 // FIXME: do this more efficiently
875 *this = path(_M_pathname + p._M_pathname);
876 return *this;
877 }
878 #endif
879 #if SLASHSLASH_IS_ROOTNAME
880 if (_M_type() == _Type::_Root_dir)
881 {
882 // Handle path("/") += path("/x") and path("//") += path("x")
883 // FIXME: do this more efficiently
884 *this = path(_M_pathname + p._M_pathname);
885 return *this;
886 }
887 #endif
888
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;
894
895 // Ensure that '_M_pathname += p._M_pathname' won't throw:
896 _M_pathname.reserve(orig_pathlen + p._M_pathname.length());
897
898 _Cmpt c;
899 _Cmpt* it = nullptr;
900 _Cmpt* last = nullptr;
901 if (p._M_type() == _Type::_Multi)
902 {
903 it = p._M_cmpts._M_impl->begin();
904 last = p._M_cmpts._M_impl->end();
905 }
906 else
907 {
908 c = _Cmpt(p._M_pathname, p._M_type(), 0);
909 it = &c;
910 last = it + 1;
911 }
912
913 if (it->_M_type() == _Type::_Filename)
914 {
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
920 #endif
921 )
922 {
923 if (p._M_type() == _Type::_Filename)
924 {
925 // Simplest case where we just add the whole of p to the
926 // original path.
927 _M_pathname += p._M_pathname;
928 return *this;
929 }
930 // Only the first component of s should be appended, do so below:
931 extra = it->_M_pathname;
932 ++it;
933 }
934 else if (_M_type() == _Type::_Multi
935 && _M_cmpts.back()._M_type() == _Type::_Filename)
936 {
937 auto& back = _M_cmpts.back();
938 if (p._M_type() == _Type::_Filename)
939 {
940 basic_string_view<value_type> s = p._M_pathname;
941 back._M_pathname += s;
942 _M_pathname += s;
943 return *this;
944 }
945
946 orig_filenamelen = back._M_pathname.length();
947 back._M_pathname += it->_M_pathname;
948 extra = it->_M_pathname;
949 ++it;
950 }
951 }
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
955
956 int capacity = 0;
957 if (_M_type() == _Type::_Multi)
958 capacity += _M_cmpts.size();
959 else
960 capacity += 1;
961 if (p._M_type() == _Type::_Multi)
962 capacity += p._M_cmpts.size();
963 else
964 capacity += 1;
965
966 __try
967 {
968 _M_cmpts.type(_Type::_Multi);
969 _M_cmpts.reserve(capacity);
970 _Cmpt* output = _M_cmpts._M_impl->end();
971
972 if (orig_type != _Type::_Multi)
973 {
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;
980
981 #if SLASHSLASH_IS_ROOTNAME
982 if (orig_type == _Type::_Root_name)
983 {
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;
988 }
989 #endif
990 }
991 else if (orig_filenamelen == 0 && it != last)
992 {
993 // Remove empty filename at end of original path.
994 _M_cmpts.pop_back();
995 --output;
996 }
997
998 if (it != last && it->_M_type() == _Type::_Root_name)
999 {
1000 basic_string_view<value_type> s = it->_M_pathname;
1001 auto pos = orig_pathlen;
1002 #if SLASHSLASH_IS_ROOTNAME
1003 s.remove_prefix(2);
1004 pos += 2;
1005 #endif
1006 ::new(output++) _Cmpt(s, _Type::_Filename, pos);
1007 ++_M_cmpts._M_impl->_M_size;
1008 ++it;
1009 }
1010
1011 if (it != last && it->_M_type() == _Type::_Root_dir)
1012 ++it;
1013
1014 while (it != last)
1015 {
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;
1019 ++it;
1020 }
1021
1022 _M_pathname += p._M_pathname;
1023
1024 if (is_dir_sep(_M_pathname.back()))
1025 {
1026 ::new(output++) _Cmpt({}, _Type::_Filename, _M_pathname.length());
1027 ++_M_cmpts._M_impl->_M_size;
1028 }
1029 }
1030 __catch (...)
1031 {
1032 _M_pathname.resize(orig_pathlen);
1033 if (orig_type == _Type::_Multi)
1034 {
1035 if (_M_cmpts.size() > orig_size)
1036 _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
1037 if (orig_filenamelen != -1)
1038 {
1039 if (_M_cmpts.size() == orig_size)
1040 {
1041 auto& back = _M_cmpts.back();
1042 back._M_pathname.resize(orig_filenamelen);
1043 if (orig_filenamelen == 0)
1044 back._M_pos = orig_pathlen;
1045 }
1046 else
1047 {
1048 auto output = _M_cmpts._M_impl->end();
1049 ::new(output) _Cmpt({}, _Type::_Filename, orig_pathlen);
1050 ++_M_cmpts._M_impl->_M_size;
1051 }
1052 }
1053 }
1054 else
1055 _M_cmpts.clear();
1056 _M_cmpts.type(orig_type);
1057 __throw_exception_again;
1058 }
1059 return *this;
1060 }
1061
1062 // [fs.path.concat]
1063 void
1064 path::_M_concat(basic_string_view<value_type> s)
1065 {
1066 if (s.empty())
1067 return;
1068
1069 if (this->empty())
1070 {
1071 operator=(s);
1072 return;
1073 }
1074
1075 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1076 if (_M_type() == _Type::_Root_name
1077 || (_M_type() == _Type::_Filename && _M_pathname.size() == 1))
1078 {
1079 // Handle path("C") += ":" and path("C:") += "/x"
1080 // FIXME: do this more efficiently
1081 *this = path(_M_pathname + string_type(s));
1082 return;
1083 }
1084 #endif
1085 #if SLASHSLASH_IS_ROOTNAME
1086 if (_M_type() == _Type::_Root_dir)
1087 {
1088 // Handle path("/") += "/x" and path("//") += "x"
1089 // FIXME: do this more efficiently
1090 *this = path(_M_pathname + string_type(s));
1091 return;
1092 }
1093 #endif
1094
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;
1100
1101 // Copy the input into _M_pathname:
1102 _M_pathname += s;
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).
1106 s = _M_pathname;
1107 const auto orig_pathname = s.substr(0, orig_pathlen);
1108 s.remove_prefix(orig_pathlen);
1109
1110 _Parser parser(s, orig_pathlen);
1111 auto cmpt = parser.next();
1112
1113 if (cmpt.str.data() == s.data())
1114 {
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
1120 #endif
1121 )
1122 {
1123 if (cmpt.str.length() == s.length())
1124 {
1125 // Simplest case where we just need to add the whole of s
1126 // to the original path, which was already done above.
1127 return;
1128 }
1129 // Only the first component of s should be appended, do so below:
1130 extra = cmpt.str;
1131 cmpt = {}; // so we don't process it again
1132 }
1133 else if (_M_type() == _Type::_Multi
1134 && _M_cmpts.back()._M_type() == _Type::_Filename)
1135 {
1136 auto& back = _M_cmpts.back();
1137 if (cmpt.str.length() == s.length())
1138 {
1139 back._M_pathname += s;
1140 return;
1141 }
1142
1143 orig_filenamelen = back._M_pathname.length();
1144 back._M_pathname += cmpt.str;
1145 extra = cmpt.str;
1146 cmpt = {};
1147 }
1148 }
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
1152
1153 std::array<_Parser::cmpt, 64> buf;
1154 auto next = buf.begin();
1155
1156 if (cmpt.valid())
1157 *next++ = cmpt;
1158
1159 cmpt = parser.next();
1160 while (cmpt.valid() && next != buf.end())
1161 {
1162 *next++ = cmpt;
1163 cmpt = parser.next();
1164 }
1165
1166 int capacity = 0;
1167 if (_M_type() == _Type::_Multi)
1168 capacity += _M_cmpts.size();
1169 else
1170 capacity += 1;
1171
1172 capacity += next - buf.begin();
1173
1174 if (cmpt.valid()) // filled buffer before parsing whole input
1175 {
1176 ++capacity;
1177 _Parser parser2(parser);
1178 while (parser2.next().valid())
1179 ++capacity;
1180 }
1181
1182 #if SLASHSLASH_IS_ROOTNAME
1183 if (orig_type == _Type::_Root_name)
1184 ++capacity; // Need to insert root-directory after root-name
1185 #endif
1186
1187 __try
1188 {
1189 _M_cmpts.type(_Type::_Multi);
1190 _M_cmpts.reserve(capacity);
1191 _Cmpt* output = _M_cmpts._M_impl->end();
1192 auto it = buf.begin();
1193
1194 if (orig_type != _Type::_Multi)
1195 {
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;
1202
1203 #if SLASHSLASH_IS_ROOTNAME
1204 if (orig_type == _Type::_Root_name)
1205 {
1206 ::new(output++) _Cmpt(s.substr(extra.length(), 1),
1207 _Type::_Root_dir, orig_pathlen + extra.length());
1208 ++_M_cmpts._M_impl->_M_size;
1209 }
1210 #endif
1211 }
1212 else if (orig_filenamelen == 0 && extra.empty())
1213 {
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);
1217 ++it;
1218 }
1219
1220 while (it != next)
1221 {
1222 ::new(output++) _Cmpt(it->str, _Type::_Filename, parser.offset(*it));
1223 ++_M_cmpts._M_impl->_M_size;
1224 ++it;
1225 }
1226
1227 if (next == buf.end())
1228 {
1229 while (cmpt.valid())
1230 {
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();
1235 }
1236 }
1237 }
1238 __catch (...)
1239 {
1240 _M_pathname.resize(orig_pathlen);
1241 if (orig_type == _Type::_Multi)
1242 {
1243 _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
1244 if (orig_filenamelen != -1)
1245 {
1246 auto& back = _M_cmpts.back();
1247 back._M_pathname.resize(orig_filenamelen);
1248 if (orig_filenamelen == 0)
1249 back._M_pos = orig_pathlen;
1250 }
1251 }
1252 else
1253 _M_cmpts.clear();
1254 _M_cmpts.type(orig_type);
1255 __throw_exception_again;
1256 }
1257 }
1258
1259 path&
1260 path::remove_filename()
1261 {
1262 if (_M_type() == _Type::_Multi)
1263 {
1264 if (!_M_cmpts.empty())
1265 {
1266 auto cmpt = std::prev(_M_cmpts.end());
1267 if (cmpt->_M_type() == _Type::_Filename && !cmpt->empty())
1268 {
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)
1273 {
1274 _M_cmpts.pop_back();
1275 if (_M_cmpts.size() == 1)
1276 {
1277 _M_cmpts.type(_M_cmpts.front()._M_type());
1278 _M_cmpts.clear();
1279 }
1280 }
1281 else
1282 cmpt->clear();
1283 }
1284 }
1285 }
1286 else if (_M_type() == _Type::_Filename)
1287 clear();
1288 return *this;
1289 }
1290
1291 path&
1292 path::replace_filename(const path& replacement)
1293 {
1294 remove_filename();
1295 operator/=(replacement);
1296 return *this;
1297 }
1298
1299 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1300 const fs::path::value_type dot = L'.';
1301 #else
1302 const fs::path::value_type dot = '.';
1303 #endif
1304
1305 path&
1306 path::replace_extension(const path& replacement)
1307 {
1308 auto ext = _M_find_extension();
1309 // Any existing extension() is removed
1310 if (ext.first && ext.second != string_type::npos)
1311 {
1312 if (ext.first == &_M_pathname)
1313 _M_pathname.erase(ext.second);
1314 else
1315 {
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);
1320 }
1321 }
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)
1325 operator+=(".");
1326 operator+=(replacement);
1327 return *this;
1328 }
1329
1330 int
1331 path::compare(const path& p) const noexcept
1332 {
1333 if (_M_pathname == p._M_pathname)
1334 return 0;
1335
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;
1349
1350 if (!this->has_root_directory() && p.has_root_directory())
1351 return -1;
1352 else if (this->has_root_directory() && !p.has_root_directory())
1353 return +1;
1354
1355 using Iterator = const _Cmpt*;
1356 Iterator begin1, end1, begin2, end2;
1357 if (_M_type() == _Type::_Multi)
1358 {
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)
1363 ++begin1;
1364 }
1365 else
1366 begin1 = end1 = nullptr;
1367
1368 if (p._M_type() == _Type::_Multi)
1369 {
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)
1374 ++begin2;
1375 }
1376 else
1377 begin2 = end2 = nullptr;
1378
1379 if (_M_type() == _Type::_Filename)
1380 {
1381 if (p._M_type() == _Type::_Filename)
1382 return native().compare(p.native());
1383 else if (begin2 != end2)
1384 {
1385 if (int ret = native().compare(begin2->native()))
1386 return ret;
1387 else
1388 return ++begin2 == end2 ? 0 : -1;
1389 }
1390 else
1391 return +1;
1392 }
1393 else if (p._M_type() == _Type::_Filename)
1394 {
1395 if (begin1 != end1)
1396 {
1397 if (int ret = begin1->native().compare(p.native()))
1398 return ret;
1399 else
1400 return ++begin1 == end1 ? 0 : +1;
1401 }
1402 else
1403 return -1;
1404 }
1405
1406 int count = 1;
1407 while (begin1 != end1 && begin2 != end2)
1408 {
1409 if (int i = begin1->native().compare(begin2->native()))
1410 return i;
1411 ++begin1;
1412 ++begin2;
1413 ++count;
1414 }
1415 if (begin1 == end1)
1416 {
1417 if (begin2 == end2)
1418 return 0;
1419 return -count;
1420 }
1421 return count;
1422 }
1423
1424 int
1425 path::compare(basic_string_view<value_type> s) const noexcept
1426 {
1427 if (_M_pathname == s)
1428 return 0;
1429
1430 _Parser parser(s);
1431
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;
1443
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)
1447 return -1;
1448 else if (this->has_root_directory() && !has_root_dir)
1449 return +1;
1450
1451 using Iterator = const _Cmpt*;
1452 Iterator begin1, end1;
1453 if (_M_type() == _Type::_Filename)
1454 {
1455 auto cmpt = parser.next();
1456 if (cmpt.valid())
1457 {
1458 if (int ret = this->native().compare(cmpt.str))
1459 return ret;
1460 return parser.next().valid() ? -1 : 0;
1461 }
1462 else
1463 return +1;
1464 }
1465 else if (_M_type() == _Type::_Multi)
1466 {
1467 begin1 = _M_cmpts.begin();
1468 end1 = _M_cmpts.end();
1469 while (begin1 != end1 && begin1->_M_type() != _Type::_Filename)
1470 ++begin1;
1471 }
1472 else
1473 begin1 = end1 = nullptr;
1474
1475 int count = 1;
1476 auto cmpt = parser.next();
1477 while (begin1 != end1 && cmpt.valid())
1478 {
1479 if (int i = begin1->native().compare(cmpt.str))
1480 return i;
1481 ++begin1;
1482 cmpt = parser.next();
1483 ++count;
1484 }
1485 if (begin1 == end1)
1486 {
1487 if (!cmpt.valid())
1488 return 0;
1489 return -count;
1490 }
1491 return +count;
1492 }
1493
1494 path
1495 path::root_name() const
1496 {
1497 path __ret;
1498 if (_M_type() == _Type::_Root_name)
1499 __ret = *this;
1500 else if (_M_cmpts.size() && _M_cmpts.begin()->_M_type() == _Type::_Root_name)
1501 __ret = *_M_cmpts.begin();
1502 return __ret;
1503 }
1504
1505 path
1506 path::root_directory() const
1507 {
1508 path __ret;
1509 if (_M_type() == _Type::_Root_dir)
1510 {
1511 __ret._M_cmpts.type(_Type::_Root_dir);
1512 __ret._M_pathname.assign(1, preferred_separator);
1513 }
1514 else if (!_M_cmpts.empty())
1515 {
1516 auto __it = _M_cmpts.begin();
1517 if (__it->_M_type() == _Type::_Root_name)
1518 ++__it;
1519 if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
1520 __ret = *__it;
1521 }
1522 return __ret;
1523 }
1524
1525 path
1526 path::root_path() const
1527 {
1528 path __ret;
1529 if (_M_type() == _Type::_Root_name)
1530 __ret = *this;
1531 else if (_M_type() == _Type::_Root_dir)
1532 {
1533 __ret._M_pathname.assign(1, preferred_separator);
1534 __ret._M_cmpts.type(_Type::_Root_dir);
1535 }
1536 else if (!_M_cmpts.empty())
1537 {
1538 auto __it = _M_cmpts.begin();
1539 if (__it->_M_type() == _Type::_Root_name)
1540 {
1541 __ret = *__it++;
1542 if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
1543 __ret /= *__it;
1544 }
1545 else if (__it->_M_type() == _Type::_Root_dir)
1546 __ret = *__it;
1547 }
1548 return __ret;
1549 }
1550
1551 path
1552 path::relative_path() const
1553 {
1554 path __ret;
1555 if (_M_type() == _Type::_Filename)
1556 __ret = *this;
1557 else if (!_M_cmpts.empty())
1558 {
1559 auto __it = _M_cmpts.begin();
1560 if (__it->_M_type() == _Type::_Root_name)
1561 ++__it;
1562 if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
1563 ++__it;
1564 if (__it != _M_cmpts.end())
1565 __ret.assign(_M_pathname.substr(__it->_M_pos));
1566 }
1567 return __ret;
1568 }
1569
1570 path
1571 path::parent_path() const
1572 {
1573 path __ret;
1574 if (!has_relative_path())
1575 __ret = *this;
1576 else if (_M_cmpts.size() >= 2)
1577 {
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));
1581 }
1582 return __ret;
1583 }
1584
1585 bool
1586 path::has_root_name() const noexcept
1587 {
1588 if (_M_type() == _Type::_Root_name)
1589 return true;
1590 if (!_M_cmpts.empty() && _M_cmpts.begin()->_M_type() == _Type::_Root_name)
1591 return true;
1592 return false;
1593 }
1594
1595 bool
1596 path::has_root_directory() const noexcept
1597 {
1598 if (_M_type() == _Type::_Root_dir)
1599 return true;
1600 if (!_M_cmpts.empty())
1601 {
1602 auto __it = _M_cmpts.begin();
1603 if (__it->_M_type() == _Type::_Root_name)
1604 ++__it;
1605 if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
1606 return true;
1607 }
1608 return false;
1609 }
1610
1611 bool
1612 path::has_root_path() const noexcept
1613 {
1614 if (_M_type() == _Type::_Root_name || _M_type() == _Type::_Root_dir)
1615 return true;
1616 if (!_M_cmpts.empty())
1617 {
1618 auto __type = _M_cmpts.front()._M_type();
1619 if (__type == _Type::_Root_name || __type == _Type::_Root_dir)
1620 return true;
1621 }
1622 return false;
1623 }
1624
1625 bool
1626 path::has_relative_path() const noexcept
1627 {
1628 if (_M_type() == _Type::_Filename && !_M_pathname.empty())
1629 return true;
1630 if (!_M_cmpts.empty())
1631 {
1632 auto __it = _M_cmpts.begin();
1633 if (__it->_M_type() == _Type::_Root_name)
1634 ++__it;
1635 if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
1636 ++__it;
1637 if (__it != _M_cmpts.end() && !__it->_M_pathname.empty())
1638 return true;
1639 }
1640 return false;
1641 }
1642
1643
1644 bool
1645 path::has_parent_path() const noexcept
1646 {
1647 if (!has_relative_path())
1648 return !empty();
1649 return _M_cmpts.size() >= 2;
1650 }
1651
1652 bool
1653 path::has_filename() const noexcept
1654 {
1655 if (empty())
1656 return false;
1657 if (_M_type() == _Type::_Filename)
1658 return !_M_pathname.empty();
1659 if (_M_type() == _Type::_Multi)
1660 {
1661 if (_M_pathname.back() == preferred_separator)
1662 return false;
1663 return _M_cmpts.back().has_filename();
1664 }
1665 return false;
1666 }
1667
1668 namespace
1669 {
1670 inline bool is_dot(fs::path::value_type c) { return c == dot; }
1671
1672 inline bool is_dot(const fs::path& path)
1673 {
1674 const auto& filename = path.native();
1675 return filename.size() == 1 && is_dot(filename[0]);
1676 }
1677
1678 inline bool is_dotdot(const fs::path& path)
1679 {
1680 const auto& filename = path.native();
1681 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
1682 }
1683 } // namespace
1684
1685 path
1686 path::lexically_normal() const
1687 {
1688 /*
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.
1701 */
1702 path ret;
1703 // If the path is empty, stop.
1704 if (empty())
1705 return ret;
1706 for (auto& p : *this)
1707 {
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)
1711 {
1712 string_type s = p.native();
1713 std::replace(s.begin(), s.end(), L'/', L'\\');
1714 ret /= s;
1715 continue;
1716 }
1717 #endif
1718 if (is_dotdot(p))
1719 {
1720 if (ret.has_filename())
1721 {
1722 // remove a non-dot-dot filename immediately followed by /..
1723 if (!is_dotdot(ret.filename()))
1724 ret.remove_filename();
1725 else
1726 ret /= p;
1727 }
1728 else if (!ret.has_relative_path())
1729 {
1730 // remove a dot-dot filename immediately after root-directory
1731 if (!ret.has_root_directory())
1732 ret /= p;
1733 }
1734 else
1735 {
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))
1741 {
1742 // Remove the filename before the trailing slash
1743 // (equiv. to ret = ret.parent_path().remove_filename())
1744
1745 if (elem == ret._M_cmpts.begin())
1746 ret.clear();
1747 else
1748 {
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)
1755 elem->clear();
1756 else // remove the component completely:
1757 ret._M_cmpts.pop_back();
1758 }
1759 }
1760 else
1761 // Append the ".." to something ending in "../" which happens
1762 // when normalising paths like ".././.." and "../a/../.."
1763 ret /= p;
1764 }
1765 }
1766 else if (is_dot(p))
1767 ret /= path();
1768 #if SLASHSLASH_IS_ROOTNAME
1769 else if (p._M_type() == _Type::_Root_dir)
1770 ret += '/'; // using operator/=('/') would replace whole of ret
1771 #endif
1772 else
1773 ret /= p;
1774 }
1775
1776 if (ret._M_cmpts.size() >= 2)
1777 {
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();
1783 }
1784 // If the path is empty, add a dot.
1785 else if (ret.empty())
1786 ret = ".";
1787
1788 return ret;
1789 }
1790
1791 path
1792 path::lexically_relative(const path& base) const
1793 {
1794 path ret;
1795 if (root_name() != base.root_name())
1796 return ret;
1797 if (is_absolute() != base.is_absolute())
1798 return ret;
1799 if (!has_root_directory() && base.has_root_directory())
1800 return ret;
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
1806 if (!empty())
1807 for (auto& p : _M_cmpts)
1808 if (p._M_type() == _Type::_Filename && is_disk_designator(p.native()))
1809 return ret;
1810 if (!base.empty())
1811 for (auto i = b, end = base.end(); i != end; ++i)
1812 if (i->_M_type() == _Type::_Filename && is_disk_designator(i->native()))
1813 return ret;
1814 #endif
1815 if (a == end() && b == base.end())
1816 ret = ".";
1817 else
1818 {
1819 int n = 0;
1820 for (; b != base.end(); ++b)
1821 {
1822 const path& p = *b;
1823 if (is_dotdot(p))
1824 --n;
1825 else if (!p.empty() && !is_dot(p))
1826 ++n;
1827 }
1828 if (n == 0 && (a == end() || a->empty()))
1829 ret = ".";
1830 else if (n >= 0)
1831 {
1832 const path dotdot("..");
1833 while (n--)
1834 ret /= dotdot;
1835 for (; a != end(); ++a)
1836 ret /= *a;
1837 }
1838 }
1839 return ret;
1840 }
1841
1842 path
1843 path::lexically_proximate(const path& base) const
1844 {
1845 path rel = lexically_relative(base);
1846 if (rel.empty())
1847 rel = *this;
1848 return rel;
1849 }
1850
1851 std::pair<const path::string_type*, std::size_t>
1852 path::_M_find_extension() const noexcept
1853 {
1854 const string_type* s = nullptr;
1855
1856 if (_M_type() == _Type::_Filename)
1857 s = &_M_pathname;
1858 else if (_M_type() == _Type::_Multi && !_M_cmpts.empty())
1859 {
1860 const auto& c = _M_cmpts.back();
1861 if (c._M_type() == _Type::_Filename)
1862 s = &c._M_pathname;
1863 }
1864
1865 if (s)
1866 {
1867 if (auto sz = s->size())
1868 {
1869 if (sz <= 2 && (*s)[0] == dot)
1870 return { s, string_type::npos };
1871 if (const auto pos = s->rfind(dot))
1872 return { s , pos };
1873 return { s, string_type::npos };
1874 }
1875 }
1876 return {};
1877 }
1878
1879 void
1880 path::_M_split_cmpts()
1881 {
1882 _M_cmpts.clear();
1883
1884 if (_M_pathname.empty())
1885 {
1886 _M_cmpts.type(_Type::_Filename);
1887 return;
1888 }
1889
1890 _Parser parser(_M_pathname);
1891
1892 std::array<_Parser::cmpt, 64> buf;
1893 auto next = buf.begin();
1894
1895 // look for root name or root directory
1896 auto root_path = parser.root_path();
1897 if (root_path.first.valid())
1898 {
1899 *next++ = root_path.first;
1900 if (root_path.second.valid())
1901 *next++ = root_path.second;
1902 }
1903
1904 auto cmpt = parser.next();
1905 while (cmpt.valid())
1906 {
1907 do
1908 {
1909 *next++ = cmpt;
1910 cmpt = parser.next();
1911 }
1912 while (cmpt.valid() && next != buf.end());
1913
1914 if (next == buf.end())
1915 {
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)
1920 {
1921 ::new(output++) _Cmpt(c.str, c.type, parser.offset(c));
1922 ++_M_cmpts._M_impl->_M_size;
1923 }
1924 next = buf.begin();
1925 }
1926 }
1927
1928 if (auto n = next - buf.begin())
1929 {
1930 if (n == 1 && _M_cmpts.empty())
1931 {
1932 _M_cmpts.type(buf.front().type);
1933 return;
1934 }
1935
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)
1940 {
1941 const auto& c = buf[i];
1942 ::new(output++) _Cmpt(c.str, c.type, parser.offset(c));
1943 ++_M_cmpts._M_impl->_M_size;
1944 }
1945 }
1946 }
1947
1948 path::string_type
1949 path::_S_convert_loc(const char* __first, const char* __last,
1950 [[maybe_unused]] const std::locale& __loc)
1951 {
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));
1960 #else
1961 return {__first, __last};
1962 #endif
1963 }
1964
1965 std::size_t
1966 fs::hash_value(const path& p) noexcept
1967 {
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
1974 size_t seed = 0;
1975 for (const auto& x : p)
1976 {
1977 seed ^= std::hash<path::string_type>()(x.native()) + 0x9e3779b9
1978 + (seed<<6) + (seed>>2);
1979 }
1980 return seed;
1981 }
1982
1983 struct fs::filesystem_error::_Impl
1984 {
1985 _Impl(string_view what_arg, const path& p1, const path& p2)
1986 : path1(p1), path2(p2), what(make_what(what_arg, &p1, &p2))
1987 { }
1988
1989 _Impl(string_view what_arg, const path& p1)
1990 : path1(p1), path2(), what(make_what(what_arg, &p1, nullptr))
1991 { }
1992
1993 _Impl(string_view what_arg)
1994 : what(make_what(what_arg, nullptr, nullptr))
1995 { }
1996
1997 static std::string
1998 make_what(string_view s, const path* p1, const path* p2)
1999 {
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);
2005 std::string w;
2006 w.reserve(len);
2007 w = "filesystem error: ";
2008 w += s;
2009 if (p1)
2010 {
2011 w += " [";
2012 w += pstr1;
2013 w += ']';
2014 if (p2)
2015 {
2016 w += " [";
2017 w += pstr2;
2018 w += ']';
2019 }
2020 }
2021 return w;
2022 }
2023
2024 path path1;
2025 path path2;
2026 std::string what;
2027 };
2028
2029 template class std::__shared_ptr<const fs::filesystem_error::_Impl>;
2030
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()))
2035 { }
2036
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))
2041 { }
2042
2043 fs::filesystem_error::
2044 filesystem_error(const string& what_arg, const path& p1, const path& p2,
2045 error_code ec)
2046 : system_error(ec, what_arg),
2047 _M_impl(std::__make_shared<_Impl>(system_error::what(), p1, p2))
2048 { }
2049
2050 fs::filesystem_error::~filesystem_error() = default;
2051
2052 const fs::path&
2053 fs::filesystem_error::path1() const noexcept
2054 { return _M_impl->path1; }
2055
2056 const fs::path&
2057 fs::filesystem_error::path2() const noexcept
2058 { return _M_impl->path2; }
2059
2060 const char*
2061 fs::filesystem_error::what() const noexcept
2062 { return _M_impl->what.c_str(); }