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