xref: /netbsd-src/external/gpl3/gcc.old/dist/libstdc++-v3/src/c++17/fs_path.cc (revision 4ac76180e904e771b9d522c7e57296d371f06499)
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