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