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