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