1760c2415Smrg // Class filesystem::path -*- C++ -*-
2760c2415Smrg 
30bfacb9bSmrg // Copyright (C) 2014-2020 Free Software Foundation, Inc.
4760c2415Smrg //
5760c2415Smrg // This file is part of the GNU ISO C++ Library.  This library is free
6760c2415Smrg // software; you can redistribute it and/or modify it under the
7760c2415Smrg // terms of the GNU General Public License as published by the
8760c2415Smrg // Free Software Foundation; either version 3, or (at your option)
9760c2415Smrg // any later version.
10760c2415Smrg 
11760c2415Smrg // This library is distributed in the hope that it will be useful,
12760c2415Smrg // but WITHOUT ANY WARRANTY; without even the implied warranty of
13760c2415Smrg // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14760c2415Smrg // GNU General Public License for more details.
15760c2415Smrg 
16760c2415Smrg // Under Section 7 of GPL version 3, you are granted additional
17760c2415Smrg // permissions described in the GCC Runtime Library Exception, version
18760c2415Smrg // 3.1, as published by the Free Software Foundation.
19760c2415Smrg 
20760c2415Smrg // You should have received a copy of the GNU General Public License and
21760c2415Smrg // a copy of the GCC Runtime Library Exception along with this program;
22760c2415Smrg // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23760c2415Smrg // <http://www.gnu.org/licenses/>.
24760c2415Smrg 
25760c2415Smrg #ifndef _GLIBCXX_USE_CXX11_ABI
26760c2415Smrg # define _GLIBCXX_USE_CXX11_ABI 1
27760c2415Smrg #endif
28760c2415Smrg 
29760c2415Smrg #ifdef __CYGWIN__
30760c2415Smrg // Interpret "//x" as a root-name, not root-dir + filename
31760c2415Smrg # define SLASHSLASH_IS_ROOTNAME 1
32760c2415Smrg #endif
33760c2415Smrg 
34760c2415Smrg #include <filesystem>
35760c2415Smrg #include <algorithm>
36760c2415Smrg #include <bits/stl_uninitialized.h>
37760c2415Smrg 
38760c2415Smrg namespace fs = std::filesystem;
39760c2415Smrg using fs::path;
40760c2415Smrg 
is_dir_sep(path::value_type ch)41760c2415Smrg static inline bool is_dir_sep(path::value_type ch)
42760c2415Smrg {
43760c2415Smrg #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
44760c2415Smrg     return ch == L'/' || ch == path::preferred_separator;
45760c2415Smrg #else
46760c2415Smrg     return ch == '/';
47760c2415Smrg #endif
48760c2415Smrg }
49760c2415Smrg 
500bfacb9bSmrg #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
is_disk_designator(std::wstring_view s)510bfacb9bSmrg static inline bool is_disk_designator(std::wstring_view s)
520bfacb9bSmrg {
530bfacb9bSmrg   return s.length() == 2 && s[1] == L':';
540bfacb9bSmrg }
550bfacb9bSmrg #endif
560bfacb9bSmrg 
57760c2415Smrg struct path::_Parser
58760c2415Smrg {
59760c2415Smrg   using string_view_type = std::basic_string_view<value_type>;
60760c2415Smrg 
61760c2415Smrg   struct cmpt
62760c2415Smrg   {
63760c2415Smrg     string_view_type str;
64760c2415Smrg     _Type type = _Type::_Multi;
65760c2415Smrg 
validpath::_Parser::cmpt66760c2415Smrg     bool valid() const { return type != _Type::_Multi; }
67760c2415Smrg   };
68760c2415Smrg 
69760c2415Smrg   string_view_type input;
70760c2415Smrg   string_view_type::size_type pos = 0;
71760c2415Smrg   size_t origin;
72760c2415Smrg   _Type last_type = _Type::_Multi;
73760c2415Smrg 
_Parserpath::_Parser74760c2415Smrg   _Parser(string_view_type s, size_t o = 0) : input(s), origin(o) { }
75760c2415Smrg 
root_pathpath::_Parser76760c2415Smrg   pair<cmpt, cmpt> root_path() noexcept
77760c2415Smrg   {
78760c2415Smrg     pos = 0;
79760c2415Smrg     pair<cmpt, cmpt> root;
80760c2415Smrg 
81760c2415Smrg     const size_t len = input.size();
82760c2415Smrg 
83760c2415Smrg     // look for root name or root directory
840bfacb9bSmrg     if (len && is_dir_sep(input[0]))
85760c2415Smrg       {
86760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
87760c2415Smrg 	// look for root name, such as "//foo"
88760c2415Smrg 	if (len > 2 && input[1] == input[0])
89760c2415Smrg 	  {
90760c2415Smrg 	    if (!is_dir_sep(input[2]))
91760c2415Smrg 	      {
92760c2415Smrg 		// got root name, find its end
93760c2415Smrg 		pos = 3;
94760c2415Smrg 		while (pos < len && !is_dir_sep(input[pos]))
95760c2415Smrg 		  ++pos;
96760c2415Smrg 		root.first.str = input.substr(0, pos);
97760c2415Smrg 		root.first.type = _Type::_Root_name;
98760c2415Smrg 
99760c2415Smrg 		if (pos < len) // also got root directory
100760c2415Smrg 		  {
101760c2415Smrg 		    root.second.str = input.substr(pos, 1);
102760c2415Smrg 		    root.second.type = _Type::_Root_dir;
103760c2415Smrg 		    ++pos;
104760c2415Smrg 		  }
105760c2415Smrg 	      }
106760c2415Smrg 	    else
107760c2415Smrg 	      {
108760c2415Smrg 		// got something like "///foo" which is just a root directory
109760c2415Smrg 		// composed of multiple redundant directory separators
110760c2415Smrg 		root.first.str = input.substr(0, 1);
111760c2415Smrg 		root.first.type = _Type::_Root_dir;
112760c2415Smrg 		pos += 2;
113760c2415Smrg 	      }
114760c2415Smrg 	  }
115760c2415Smrg 	else
116760c2415Smrg #endif
117760c2415Smrg 	  {
118760c2415Smrg 	    root.first.str = input.substr(0, 1);
119760c2415Smrg 	    root.first.type = _Type::_Root_dir;
120760c2415Smrg 	    ++pos;
121760c2415Smrg 	  }
122760c2415Smrg 	// Find the start of the first filename
123760c2415Smrg 	while (pos < len && is_dir_sep(input[pos]))
124760c2415Smrg 	  ++pos;
125760c2415Smrg       }
126760c2415Smrg #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1270bfacb9bSmrg     else if (is_disk_designator(input.substr(0, 2)))
128760c2415Smrg       {
129760c2415Smrg 	// got disk designator
130760c2415Smrg 	root.first.str = input.substr(0, 2);
131760c2415Smrg 	root.first.type = _Type::_Root_name;
132760c2415Smrg 	if (len > 2 && is_dir_sep(input[2]))
133760c2415Smrg 	  {
134760c2415Smrg 	    root.second.str = input.substr(2, 1);
135760c2415Smrg 	    root.second.type = _Type::_Root_dir;
136760c2415Smrg 	  }
137760c2415Smrg 	pos = input.find_first_not_of(L"/\\", 2);
138760c2415Smrg       }
139760c2415Smrg #endif
140760c2415Smrg 
141760c2415Smrg     if (root.second.valid())
142760c2415Smrg       last_type = root.second.type;
143760c2415Smrg     else
144760c2415Smrg       last_type = root.first.type;
145760c2415Smrg 
146760c2415Smrg     return root;
147760c2415Smrg   }
148760c2415Smrg 
nextpath::_Parser149760c2415Smrg   cmpt next() noexcept
150760c2415Smrg   {
151760c2415Smrg #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
152760c2415Smrg     string_view_type sep = L"/\\";
153760c2415Smrg #else
154760c2415Smrg     char sep = '/';
155760c2415Smrg #endif
156760c2415Smrg 
157760c2415Smrg     const int last_pos = pos;
158760c2415Smrg 
159760c2415Smrg     cmpt f;
160760c2415Smrg     if (pos != input.npos)
161760c2415Smrg       {
162760c2415Smrg 	pos = input.find_first_not_of(sep, pos);
163760c2415Smrg 	if (pos != input.npos)
164760c2415Smrg 	  {
165760c2415Smrg 	    const auto end = input.find_first_of(sep, pos);
166760c2415Smrg 	    f.str = input.substr(pos, end - pos);
167760c2415Smrg 	    f.type = _Type::_Filename;
168760c2415Smrg 	    pos = end;
169760c2415Smrg 	  }
170760c2415Smrg 	else if (last_type == _Type::_Filename
171760c2415Smrg 	    || (last_pos == 0 && !input.empty()))
172760c2415Smrg 	  {
173760c2415Smrg 	    // [fs.path.itr]/4 An empty element, if trailing non-root
174760c2415Smrg 	    // directory-separator present.
175760c2415Smrg 	    __glibcxx_assert(is_dir_sep(input.back()));
176760c2415Smrg 	    f.str = input.substr(input.length(), 0);
177760c2415Smrg 	    f.type = _Type::_Filename;
178760c2415Smrg 	  }
179760c2415Smrg       }
180760c2415Smrg     last_type = f.type;
181760c2415Smrg     return f;
182760c2415Smrg   }
183760c2415Smrg 
184760c2415Smrg   string_view_type::size_type
offsetpath::_Parser185760c2415Smrg   offset(const cmpt& c) const noexcept
186760c2415Smrg   { return origin + c.str.data() - input.data(); }
187760c2415Smrg };
188760c2415Smrg 
189*4048c126Smrg inline
path(basic_string_view<value_type> __str,_Type __type)190*4048c126Smrg path::path(basic_string_view<value_type> __str, _Type __type)
191*4048c126Smrg : _M_pathname(__str)
192*4048c126Smrg {
193*4048c126Smrg   __glibcxx_assert(__type != _Type::_Multi);
194*4048c126Smrg   _M_cmpts.type(__type);
195*4048c126Smrg }
196*4048c126Smrg 
197*4048c126Smrg inline
_Cmpt(basic_string_view<value_type> __s,_Type __t,size_t __pos)198*4048c126Smrg path::_Cmpt::_Cmpt(basic_string_view<value_type> __s, _Type __t, size_t __pos)
199*4048c126Smrg : path(__s, __t), _M_pos(__pos)
200*4048c126Smrg { }
201*4048c126Smrg 
202760c2415Smrg struct path::_List::_Impl
203760c2415Smrg {
204760c2415Smrg   using value_type = _Cmpt;
205760c2415Smrg 
_Implpath::_List::_Impl206760c2415Smrg   _Impl(int cap) : _M_size(0), _M_capacity(cap) { }
207760c2415Smrg 
208760c2415Smrg   alignas(value_type) int _M_size;
209760c2415Smrg   int _M_capacity;
210760c2415Smrg 
211760c2415Smrg   using iterator = value_type*;
212760c2415Smrg   using const_iterator = const value_type*;
213760c2415Smrg 
beginpath::_List::_Impl214760c2415Smrg   iterator begin() { return reinterpret_cast<value_type*>(this + 1); }
endpath::_List::_Impl215760c2415Smrg   iterator end() { return begin() + size(); }
216760c2415Smrg 
beginpath::_List::_Impl217760c2415Smrg   const_iterator begin() const
218760c2415Smrg   { return reinterpret_cast<const value_type*>(this + 1); }
endpath::_List::_Impl219760c2415Smrg   const_iterator end() const { return begin() + size(); }
220760c2415Smrg 
frontpath::_List::_Impl221760c2415Smrg   const value_type& front() const { return *begin(); }
backpath::_List::_Impl222760c2415Smrg   const value_type& back() const { return end()[-1]; }
223760c2415Smrg 
sizepath::_List::_Impl224760c2415Smrg   int size() const { return _M_size; }
capacitypath::_List::_Impl225760c2415Smrg   int capacity() const { return _M_capacity; }
emptypath::_List::_Impl226760c2415Smrg   bool empty() const { return _M_size == 0; }
227760c2415Smrg 
clearpath::_List::_Impl228760c2415Smrg   void clear() { std::destroy_n(begin(), _M_size); _M_size = 0; }
229760c2415Smrg 
pop_backpath::_List::_Impl230760c2415Smrg   void pop_back()
231760c2415Smrg   {
232760c2415Smrg     back().~_Cmpt();
233760c2415Smrg     --_M_size;
234760c2415Smrg   }
235760c2415Smrg 
_M_erase_frompath::_List::_Impl236760c2415Smrg   void _M_erase_from(const_iterator pos)
237760c2415Smrg   {
238760c2415Smrg     iterator first = begin() + (pos - begin());
239760c2415Smrg     iterator last = end();
240760c2415Smrg     std::destroy(first, last);
241760c2415Smrg     _M_size -= last - first;
242760c2415Smrg   }
243760c2415Smrg 
copypath::_List::_Impl244760c2415Smrg   unique_ptr<_Impl, _Impl_deleter> copy() const
245760c2415Smrg   {
246760c2415Smrg     const auto n = size();
247760c2415Smrg     void* p = ::operator new(sizeof(_Impl) + n * sizeof(value_type));
248760c2415Smrg     unique_ptr<_Impl, _Impl_deleter> newptr(::new (p) _Impl{n});
249760c2415Smrg     std::uninitialized_copy_n(begin(), n, newptr->begin());
250760c2415Smrg     newptr->_M_size = n;
251760c2415Smrg     return newptr;
252760c2415Smrg   }
253760c2415Smrg 
254760c2415Smrg   // Clear the lowest two bits from the pointer (i.e. remove the _Type value)
notypepath::_List::_Impl255760c2415Smrg   static _Impl* notype(_Impl* p)
256760c2415Smrg   {
257760c2415Smrg     constexpr uintptr_t mask = ~(uintptr_t)0x3;
258760c2415Smrg     return reinterpret_cast<_Impl*>(reinterpret_cast<uintptr_t>(p) & mask);
259760c2415Smrg   }
260760c2415Smrg };
261760c2415Smrg 
operator ()(_Impl * p) const262760c2415Smrg void path::_List::_Impl_deleter::operator()(_Impl* p) const noexcept
263760c2415Smrg {
264760c2415Smrg   p = _Impl::notype(p);
265760c2415Smrg   if (p)
266760c2415Smrg     {
267760c2415Smrg       __glibcxx_assert(p->_M_size <= p->_M_capacity);
268760c2415Smrg       p->clear();
269760c2415Smrg       ::operator delete(p, sizeof(*p) + p->_M_capacity * sizeof(value_type));
270760c2415Smrg     }
271760c2415Smrg }
272760c2415Smrg 
_List()273760c2415Smrg path::_List::_List() : _M_impl(reinterpret_cast<_Impl*>(_Type::_Filename)) { }
274760c2415Smrg 
_List(const _List & other)275760c2415Smrg path::_List::_List(const _List& other)
276760c2415Smrg {
277760c2415Smrg   if (!other.empty())
278760c2415Smrg     _M_impl = other._M_impl->copy();
279760c2415Smrg   else
280760c2415Smrg     type(other.type());
281760c2415Smrg }
282760c2415Smrg 
283760c2415Smrg path::_List&
operator =(const _List & other)284760c2415Smrg path::_List::operator=(const _List& other)
285760c2415Smrg {
286760c2415Smrg   if (!other.empty())
287760c2415Smrg     {
288760c2415Smrg       // copy in-place if there is capacity
289760c2415Smrg       const int newsize = other._M_impl->size();
290760c2415Smrg       auto impl = _Impl::notype(_M_impl.get());
291760c2415Smrg       if (impl && impl->capacity() >= newsize)
292760c2415Smrg 	{
293760c2415Smrg 	  const int oldsize = impl->_M_size;
294760c2415Smrg 	  auto to = impl->begin();
295760c2415Smrg 	  auto from = other._M_impl->begin();
296760c2415Smrg 	  const int minsize = std::min(newsize, oldsize);
297760c2415Smrg 	  for (int i = 0; i < minsize; ++i)
298760c2415Smrg 	    to[i]._M_pathname.reserve(from[i]._M_pathname.length());
299760c2415Smrg 	  if (newsize > oldsize)
300760c2415Smrg 	    {
301760c2415Smrg 	      std::uninitialized_copy_n(from + oldsize, newsize - oldsize,
302760c2415Smrg 					to + oldsize);
303760c2415Smrg 	      impl->_M_size = newsize;
304760c2415Smrg 	    }
305760c2415Smrg 	  else if (newsize < oldsize)
306760c2415Smrg 	    impl->_M_erase_from(impl->begin() + newsize);
307760c2415Smrg 	  std::copy_n(from, minsize, to);
308760c2415Smrg 	  type(_Type::_Multi);
309760c2415Smrg 	}
310760c2415Smrg       else
311760c2415Smrg 	_M_impl = other._M_impl->copy();
312760c2415Smrg     }
313760c2415Smrg   else
314760c2415Smrg     {
315760c2415Smrg       clear();
316760c2415Smrg       type(other.type());
317760c2415Smrg     }
318760c2415Smrg   return *this;
319760c2415Smrg }
320760c2415Smrg 
321760c2415Smrg inline void
type(_Type t)322760c2415Smrg path::_List::type(_Type t) noexcept
323760c2415Smrg {
324760c2415Smrg   auto val = reinterpret_cast<uintptr_t>(_Impl::notype(_M_impl.release()));
325760c2415Smrg   _M_impl.reset(reinterpret_cast<_Impl*>(val | (unsigned char)t));
326760c2415Smrg }
327760c2415Smrg 
328760c2415Smrg inline int
size() const329760c2415Smrg path::_List::size() const noexcept
330760c2415Smrg {
331760c2415Smrg   if (auto* ptr = _Impl::notype(_M_impl.get()))
332760c2415Smrg     return ptr->size();
333760c2415Smrg   return 0;
334760c2415Smrg }
335760c2415Smrg 
336760c2415Smrg inline int
capacity() const337760c2415Smrg path::_List::capacity() const noexcept
338760c2415Smrg {
339760c2415Smrg   if (auto* ptr = _Impl::notype(_M_impl.get()))
340760c2415Smrg     return ptr->capacity();
341760c2415Smrg   return 0;
342760c2415Smrg }
343760c2415Smrg 
344760c2415Smrg inline bool
empty() const345760c2415Smrg path::_List::empty() const noexcept
346760c2415Smrg {
347760c2415Smrg   return size() == 0;
348760c2415Smrg }
349760c2415Smrg 
350760c2415Smrg inline auto
begin()351760c2415Smrg path::_List::begin() noexcept
352760c2415Smrg -> iterator
353760c2415Smrg {
354760c2415Smrg   __glibcxx_assert(!empty());
355760c2415Smrg   if (auto* ptr = _Impl::notype(_M_impl.get()))
356760c2415Smrg     return ptr->begin();
357760c2415Smrg   return nullptr;
358760c2415Smrg }
359760c2415Smrg 
360760c2415Smrg inline auto
end()361760c2415Smrg path::_List::end() noexcept
362760c2415Smrg -> iterator
363760c2415Smrg {
364760c2415Smrg   __glibcxx_assert(!empty());
365760c2415Smrg   if (auto* ptr = _Impl::notype(_M_impl.get()))
366760c2415Smrg     return ptr->end();
367760c2415Smrg   return nullptr;
368760c2415Smrg }
369760c2415Smrg 
370760c2415Smrg auto
begin() const371760c2415Smrg path::_List::begin() const noexcept
372760c2415Smrg -> const_iterator
373760c2415Smrg {
374760c2415Smrg   __glibcxx_assert(!empty());
375760c2415Smrg   if (auto* ptr = _Impl::notype(_M_impl.get()))
376760c2415Smrg     return ptr->begin();
377760c2415Smrg   return nullptr;
378760c2415Smrg }
379760c2415Smrg 
380760c2415Smrg auto
end() const381760c2415Smrg path::_List::end() const noexcept
382760c2415Smrg -> const_iterator
383760c2415Smrg {
384760c2415Smrg   __glibcxx_assert(!empty());
385760c2415Smrg   if (auto* ptr = _Impl::notype(_M_impl.get()))
386760c2415Smrg     return ptr->end();
387760c2415Smrg   return nullptr;
388760c2415Smrg }
389760c2415Smrg 
390760c2415Smrg inline auto
front()391760c2415Smrg path::_List::front() noexcept
392760c2415Smrg -> value_type&
393760c2415Smrg {
394760c2415Smrg   return *_M_impl->begin();
395760c2415Smrg }
396760c2415Smrg 
397760c2415Smrg inline auto
back()398760c2415Smrg path::_List::back() noexcept
399760c2415Smrg -> value_type&
400760c2415Smrg {
401760c2415Smrg   return _M_impl->begin()[_M_impl->size() - 1];
402760c2415Smrg }
403760c2415Smrg 
404760c2415Smrg inline auto
front() const405760c2415Smrg path::_List::front() const noexcept
406760c2415Smrg -> const value_type&
407760c2415Smrg {
408760c2415Smrg   return *_M_impl->begin();
409760c2415Smrg }
410760c2415Smrg 
411760c2415Smrg inline auto
back() const412760c2415Smrg path::_List::back() const noexcept
413760c2415Smrg -> const value_type&
414760c2415Smrg {
415760c2415Smrg   return _M_impl->begin()[_M_impl->size() - 1];
416760c2415Smrg }
417760c2415Smrg 
418760c2415Smrg inline void
pop_back()419760c2415Smrg path::_List::pop_back()
420760c2415Smrg {
421760c2415Smrg   __glibcxx_assert(size() > 0);
422760c2415Smrg   _M_impl->pop_back();
423760c2415Smrg }
424760c2415Smrg 
425760c2415Smrg inline void
_M_erase_from(const_iterator pos)426760c2415Smrg path::_List::_M_erase_from(const_iterator pos)
427760c2415Smrg {
428760c2415Smrg   _M_impl->_M_erase_from(pos);
429760c2415Smrg }
430760c2415Smrg 
431760c2415Smrg inline void
clear()432760c2415Smrg path::_List::clear()
433760c2415Smrg {
434760c2415Smrg   if (auto ptr = _Impl::notype(_M_impl.get()))
435760c2415Smrg     ptr->clear();
436760c2415Smrg }
437760c2415Smrg 
438760c2415Smrg void
reserve(int newcap,bool exact=false)439760c2415Smrg path::_List::reserve(int newcap, bool exact = false)
440760c2415Smrg {
441760c2415Smrg   // __glibcxx_assert(type() == _Type::_Multi);
442760c2415Smrg 
443760c2415Smrg   _Impl* curptr = _Impl::notype(_M_impl.get());
444760c2415Smrg 
445760c2415Smrg   int curcap = curptr ? curptr->capacity() : 0;
446760c2415Smrg 
447760c2415Smrg   if (curcap < newcap)
448760c2415Smrg     {
449760c2415Smrg       if (!exact && newcap < int(1.5 * curcap))
450760c2415Smrg 	newcap = 1.5 * curcap;
451760c2415Smrg 
452760c2415Smrg       void* p = ::operator new(sizeof(_Impl) + newcap * sizeof(value_type));
453760c2415Smrg       std::unique_ptr<_Impl, _Impl_deleter> newptr(::new(p) _Impl{newcap});
454760c2415Smrg       const int cursize = curptr ? curptr->size() : 0;
455760c2415Smrg       if (cursize)
456760c2415Smrg 	{
457760c2415Smrg 	  std::uninitialized_move_n(curptr->begin(), cursize, newptr->begin());
458760c2415Smrg 	  newptr->_M_size = cursize;
459760c2415Smrg 	}
460760c2415Smrg       std::swap(newptr, _M_impl);
461760c2415Smrg     }
462760c2415Smrg }
463760c2415Smrg 
464760c2415Smrg path&
operator =(const path & p)465760c2415Smrg path::operator=(const path& p)
466760c2415Smrg {
467760c2415Smrg   if (&p == this) [[__unlikely__]]
468760c2415Smrg     return *this;
469760c2415Smrg 
470760c2415Smrg   _M_pathname.reserve(p._M_pathname.length());
471760c2415Smrg   _M_cmpts = p._M_cmpts;	// might throw
472760c2415Smrg   _M_pathname = p._M_pathname;	// won't throw because we reserved enough space
473760c2415Smrg   return *this;
474760c2415Smrg }
475760c2415Smrg 
476760c2415Smrg path&
operator /=(const path & __p)477760c2415Smrg path::operator/=(const path& __p)
478760c2415Smrg {
479760c2415Smrg #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
480760c2415Smrg   if (__p.is_absolute()
481760c2415Smrg       || (__p.has_root_name() && __p.root_name() != root_name()))
482760c2415Smrg     return operator=(__p);
483760c2415Smrg 
484760c2415Smrg   basic_string_view<value_type> __lhs = _M_pathname;
485760c2415Smrg   bool __add_sep = false;
486760c2415Smrg 
487760c2415Smrg   if (__p.has_root_directory())
488760c2415Smrg     {
489760c2415Smrg       // Remove any root directory and relative path
490760c2415Smrg       if (_M_type() != _Type::_Root_name)
491760c2415Smrg 	{
492760c2415Smrg 	  if (!_M_cmpts.empty()
493760c2415Smrg 	      && _M_cmpts.front()._M_type() == _Type::_Root_name)
494760c2415Smrg 	    __lhs = _M_cmpts.front()._M_pathname;
495760c2415Smrg 	  else
496760c2415Smrg 	    __lhs = {};
497760c2415Smrg 	}
498760c2415Smrg     }
499760c2415Smrg   else if (has_filename() || (!has_root_directory() && is_absolute()))
500760c2415Smrg     __add_sep = true;
501760c2415Smrg 
502760c2415Smrg   basic_string_view<value_type> __rhs = __p._M_pathname;
503760c2415Smrg   // Omit any root-name from the generic format pathname:
504760c2415Smrg   if (__p._M_type() == _Type::_Root_name)
505760c2415Smrg     __rhs = {};
506760c2415Smrg   else if (!__p._M_cmpts.empty()
507760c2415Smrg       && __p._M_cmpts.front()._M_type() == _Type::_Root_name)
508760c2415Smrg     __rhs.remove_prefix(__p._M_cmpts.front()._M_pathname.size());
509760c2415Smrg 
510760c2415Smrg   const size_t __len = __lhs.size() + (int)__add_sep + __rhs.size();
511760c2415Smrg   const int __maxcmpts = _M_cmpts.size() + __p._M_cmpts.size();
512760c2415Smrg   if (_M_pathname.capacity() < __len || _M_cmpts.capacity() < __maxcmpts)
513760c2415Smrg     {
514760c2415Smrg       // Construct new path and swap (strong exception-safety guarantee).
515760c2415Smrg       string_type __tmp;
516760c2415Smrg       __tmp.reserve(__len);
517760c2415Smrg       __tmp = __lhs;
518760c2415Smrg       if (__add_sep)
519760c2415Smrg 	__tmp += preferred_separator;
520760c2415Smrg       __tmp += __rhs;
521760c2415Smrg       path __newp = std::move(__tmp);
522760c2415Smrg       swap(__newp);
523760c2415Smrg     }
524760c2415Smrg   else
525760c2415Smrg     {
526760c2415Smrg       _M_pathname = __lhs;
527760c2415Smrg       if (__add_sep)
528760c2415Smrg 	_M_pathname += preferred_separator;
529760c2415Smrg       _M_pathname += __rhs;
530760c2415Smrg       __try
531760c2415Smrg 	{
532760c2415Smrg 	  _M_split_cmpts();
533760c2415Smrg 	}
534760c2415Smrg       __catch (...)
535760c2415Smrg 	{
536760c2415Smrg 	  __try
537760c2415Smrg 	    {
538760c2415Smrg 	      // try to restore original state
539760c2415Smrg 	      _M_pathname.resize(__lhs.length());
540760c2415Smrg 	      _M_split_cmpts();
541760c2415Smrg 	    }
542760c2415Smrg 	  __catch (...)
543760c2415Smrg 	    {
544760c2415Smrg 	      // give up, basic exception safety guarantee only:
545760c2415Smrg 	      clear();
546760c2415Smrg 	      __throw_exception_again;
547760c2415Smrg 	    }
548760c2415Smrg 	}
549760c2415Smrg     }
550760c2415Smrg #else
551760c2415Smrg   // POSIX version is simpler than the specification in the standard,
552760c2415Smrg   // as any path with root-name or root-dir is absolute.
553760c2415Smrg 
554760c2415Smrg   if (__p.is_absolute() || this->empty())
555760c2415Smrg     {
556760c2415Smrg       return operator=(__p);
557760c2415Smrg     }
558760c2415Smrg 
559760c2415Smrg   using string_view_type = basic_string_view<value_type>;
560760c2415Smrg 
561760c2415Smrg   string_view_type sep;
562760c2415Smrg   if (has_filename())
563760c2415Smrg     sep = { &preferred_separator, 1 };  // need to add a separator
564760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
565760c2415Smrg   else if (_M_type() == _Type::_Root_name) // root-name with no root-dir
566760c2415Smrg     sep = { &preferred_separator, 1 };  // need to add a separator
567760c2415Smrg #endif
568760c2415Smrg   else if (__p.empty())
569760c2415Smrg     return *this;			    // nothing to do
570760c2415Smrg 
571760c2415Smrg   const auto orig_pathlen = _M_pathname.length();
572760c2415Smrg   const auto orig_size = _M_cmpts.size();
573760c2415Smrg   const auto orig_type = _M_type();
574760c2415Smrg 
575760c2415Smrg   int capacity = 0;
576760c2415Smrg   if (_M_type() == _Type::_Multi)
577760c2415Smrg     capacity += _M_cmpts.size();
578760c2415Smrg   else if (!empty())
579760c2415Smrg     capacity += 1;
580760c2415Smrg   if (__p._M_type() == _Type::_Multi)
581760c2415Smrg     capacity += __p._M_cmpts.size();
582760c2415Smrg   else if (!__p.empty() || !sep.empty())
583760c2415Smrg     capacity += 1;
584760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
585760c2415Smrg   if (orig_type == _Type::_Root_name)
586760c2415Smrg     ++capacity; // Need to insert root-directory after root-name
587760c2415Smrg #endif
588760c2415Smrg 
589760c2415Smrg   if (orig_type == _Type::_Multi)
590760c2415Smrg     {
591760c2415Smrg       const int curcap = _M_cmpts._M_impl->capacity();
592760c2415Smrg       if (capacity > curcap)
593760c2415Smrg 	capacity = std::max(capacity, (int) (curcap * 1.5));
594760c2415Smrg     }
595760c2415Smrg 
596760c2415Smrg   _M_pathname.reserve(_M_pathname.length() + sep.length()
597760c2415Smrg 		      + __p._M_pathname.length());
598760c2415Smrg 
599760c2415Smrg   __try
600760c2415Smrg     {
601760c2415Smrg       _M_pathname += sep;
602760c2415Smrg       const auto basepos = _M_pathname.length();
603760c2415Smrg       _M_pathname += __p.native();
604760c2415Smrg 
605760c2415Smrg       _M_cmpts.type(_Type::_Multi);
606760c2415Smrg       _M_cmpts.reserve(capacity);
607760c2415Smrg       _Cmpt* output = _M_cmpts._M_impl->end();
608760c2415Smrg 
609760c2415Smrg       if (orig_type == _Type::_Multi)
610760c2415Smrg 	{
611760c2415Smrg 	  // Remove empty final component
612760c2415Smrg 	  if (_M_cmpts._M_impl->back().empty())
613760c2415Smrg 	    {
614760c2415Smrg 	      _M_cmpts.pop_back();
615760c2415Smrg 	      --output;
616760c2415Smrg 	    }
617760c2415Smrg 	}
618760c2415Smrg       else if (orig_pathlen != 0)
619760c2415Smrg 	{
620760c2415Smrg 	  // Create single component from original path
621760c2415Smrg 	  string_view_type s(_M_pathname.data(), orig_pathlen);
622760c2415Smrg 	  ::new(output++) _Cmpt(s, orig_type, 0);
623760c2415Smrg 	  ++_M_cmpts._M_impl->_M_size;
624760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
625760c2415Smrg 	  if (orig_type == _Type::_Root_name)
626760c2415Smrg 	    {
627760c2415Smrg 	      ::new(output++) _Cmpt(sep, _Type::_Root_dir,
628760c2415Smrg 				    orig_pathlen + sep.length());
629760c2415Smrg 	      ++_M_cmpts._M_impl->_M_size;
630760c2415Smrg 	    }
631760c2415Smrg #endif
632760c2415Smrg 	}
633760c2415Smrg 
634760c2415Smrg       if (__p._M_type() == _Type::_Multi)
635760c2415Smrg 	{
636760c2415Smrg 	  for (auto& c : *__p._M_cmpts._M_impl)
637760c2415Smrg 	    {
638760c2415Smrg 	      ::new(output++) _Cmpt(c._M_pathname, _Type::_Filename,
639760c2415Smrg 				    c._M_pos + basepos);
640760c2415Smrg 	      ++_M_cmpts._M_impl->_M_size;
641760c2415Smrg 	    }
642760c2415Smrg 	}
643760c2415Smrg       else if (!__p.empty() || !sep.empty())
644760c2415Smrg 	{
645760c2415Smrg 	  __glibcxx_assert(__p._M_type() == _Type::_Filename);
646760c2415Smrg 	  ::new(output) _Cmpt(__p._M_pathname, __p._M_type(), basepos);
647760c2415Smrg 	  ++_M_cmpts._M_impl->_M_size;
648760c2415Smrg 	}
649760c2415Smrg     }
650760c2415Smrg   __catch (...)
651760c2415Smrg     {
652760c2415Smrg       _M_pathname.resize(orig_pathlen);
653760c2415Smrg       if (orig_type == _Type::_Multi)
654760c2415Smrg 	_M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
655760c2415Smrg       else
656760c2415Smrg 	_M_cmpts.clear();
657760c2415Smrg       _M_cmpts.type(orig_type);
658760c2415Smrg       __throw_exception_again;
659760c2415Smrg     }
660760c2415Smrg #endif
661760c2415Smrg   return *this;
662760c2415Smrg }
663760c2415Smrg 
664760c2415Smrg // [fs.path.append]
665760c2415Smrg void
_M_append(basic_string_view<value_type> s)666760c2415Smrg path::_M_append(basic_string_view<value_type> s)
667760c2415Smrg {
668760c2415Smrg   _Parser parser(s);
669760c2415Smrg   auto root_path = parser.root_path();
670760c2415Smrg 
671760c2415Smrg #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
672760c2415Smrg   bool is_absolute = root_path.second.type == _Type::_Root_dir;
673760c2415Smrg   bool has_root_name = root_path.first.type == _Type::_Root_name;
674760c2415Smrg   if (is_absolute || (has_root_name && root_path.first.str != root_name()))
675760c2415Smrg     {
676760c2415Smrg       operator=(s);
677760c2415Smrg       return;
678760c2415Smrg     }
679760c2415Smrg 
680760c2415Smrg   basic_string_view<value_type> lhs = _M_pathname;
681760c2415Smrg   bool add_sep = false;
682760c2415Smrg 
683760c2415Smrg   bool has_root_directory = root_path.first.type == _Type::_Root_dir
684760c2415Smrg     || root_path.second.type == _Type::_Root_dir;
685760c2415Smrg 
686760c2415Smrg   if (has_root_directory)
687760c2415Smrg     {
688760c2415Smrg       // Remove any root directory and relative path
689760c2415Smrg       if (_M_type() != _Type::_Root_name)
690760c2415Smrg 	{
691760c2415Smrg 	  if (!_M_cmpts.empty()
692760c2415Smrg 	      && _M_cmpts.front()._M_type() == _Type::_Root_name)
693760c2415Smrg 	    lhs = _M_cmpts.front()._M_pathname;
694760c2415Smrg 	  else
695760c2415Smrg 	    lhs = {};
696760c2415Smrg 	}
697760c2415Smrg     }
698760c2415Smrg   else if (has_filename() || (!has_root_directory && is_absolute))
699760c2415Smrg     add_sep = true;
700760c2415Smrg 
701760c2415Smrg   basic_string_view<value_type> rhs = s;
702760c2415Smrg   // Omit any root-name from the generic format pathname:
703760c2415Smrg   if (has_root_name)
704760c2415Smrg     rhs.remove_prefix(root_path.first.str.length());
705760c2415Smrg 
706760c2415Smrg   // Construct new path and swap (strong exception-safety guarantee).
707760c2415Smrg   string_type tmp;
708760c2415Smrg   tmp.reserve(lhs.size() + (int)add_sep + rhs.size());
709760c2415Smrg   tmp = lhs;
710760c2415Smrg   if (add_sep)
711760c2415Smrg     tmp += preferred_separator;
712760c2415Smrg   tmp += rhs;
713760c2415Smrg   path newp = std::move(tmp);
714760c2415Smrg   swap(newp);
715760c2415Smrg #else
716760c2415Smrg 
717760c2415Smrg   bool is_absolute = root_path.first.type == _Type::_Root_dir
718760c2415Smrg     || root_path.second.type == _Type::_Root_dir;
719760c2415Smrg   if (is_absolute || this->empty())
720760c2415Smrg     {
721760c2415Smrg       operator=(s);
722760c2415Smrg       return;
723760c2415Smrg     }
724760c2415Smrg 
725760c2415Smrg   const auto orig_pathlen = _M_pathname.length();
726760c2415Smrg   const auto orig_size = _M_cmpts.size();
727760c2415Smrg   const auto orig_type = _M_type();
728760c2415Smrg 
729760c2415Smrg   basic_string_view<value_type> sep;
730760c2415Smrg   if (has_filename())
731760c2415Smrg     sep = { &preferred_separator, 1 };  // need to add a separator
732760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
733760c2415Smrg   else if (_M_type() == _Type::_Root_name) // root-name with no root-dir
734760c2415Smrg     sep = { &preferred_separator, 1 };  // need to add a separator
735760c2415Smrg #endif
736760c2415Smrg   else if (s.empty())
737760c2415Smrg     return;			    // nothing to do
738760c2415Smrg 
739760c2415Smrg   // Copy the input into _M_pathname:
740760c2415Smrg   _M_pathname += s;
741760c2415Smrg   _M_pathname.insert(orig_pathlen, sep);
742760c2415Smrg   // Update s to refer to the new copy (this ensures s is not a dangling
743760c2415Smrg   // reference to deallocated characters, in the case where it was referring
744760c2415Smrg   // into _M_pathname or a member of _M_cmpts).
745760c2415Smrg   s = _M_pathname;
746760c2415Smrg   const auto orig_pathname = s.substr(0, orig_pathlen);
747760c2415Smrg   s.remove_prefix(orig_pathlen + sep.length());
748760c2415Smrg 
749760c2415Smrg   parser.input = s; // reset parser to use updated string view
750760c2415Smrg   const auto basepos = orig_pathname.length() + sep.length();
751760c2415Smrg   parser.origin = basepos;
752760c2415Smrg 
753760c2415Smrg   std::array<_Parser::cmpt, 64> buf;
754760c2415Smrg   auto next = buf.begin();
755760c2415Smrg 
756760c2415Smrg   int capacity = 0;
757760c2415Smrg   if (_M_type() == _Type::_Multi)
758760c2415Smrg     capacity += _M_cmpts.size();
759760c2415Smrg   else if (!empty())
760760c2415Smrg     capacity += 1;
761760c2415Smrg 
762760c2415Smrg   auto cmpt = parser.next();
763760c2415Smrg   if (cmpt.valid())
764760c2415Smrg     {
765760c2415Smrg       do
766760c2415Smrg 	{
767760c2415Smrg 	  *next++ = cmpt;
768760c2415Smrg 	  cmpt = parser.next();
769760c2415Smrg 	}
770760c2415Smrg       while (cmpt.valid() && next != buf.end());
771760c2415Smrg 
772760c2415Smrg       capacity += next - buf.begin();
773760c2415Smrg       if (cmpt.valid()) // filled buffer before parsing whole input
774760c2415Smrg 	{
775760c2415Smrg 	  ++capacity;
776760c2415Smrg 	  _Parser parser2(parser);
777760c2415Smrg 	  while (parser2.next().valid())
778760c2415Smrg 	    ++capacity;
779760c2415Smrg 	}
780760c2415Smrg     }
781760c2415Smrg   else if (!sep.empty())
782760c2415Smrg     ++capacity;
783760c2415Smrg 
784760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
785760c2415Smrg   if (orig_type == _Type::_Root_name)
786760c2415Smrg     ++capacity; // Need to insert root-directory after root-name
787760c2415Smrg #endif
788760c2415Smrg 
789760c2415Smrg   __try
790760c2415Smrg     {
791760c2415Smrg       _M_cmpts.type(_Type::_Multi);
792760c2415Smrg       _M_cmpts.reserve(capacity);
793760c2415Smrg       _Cmpt* output = _M_cmpts._M_impl->end();
794760c2415Smrg 
795760c2415Smrg       if (orig_type == _Type::_Multi)
796760c2415Smrg 	{
797760c2415Smrg 	  // Remove empty final component
798760c2415Smrg 	  if (_M_cmpts._M_impl->back().empty())
799760c2415Smrg 	    {
800760c2415Smrg 	      _M_cmpts.pop_back();
801760c2415Smrg 	      --output;
802760c2415Smrg 	    }
803760c2415Smrg 	}
804760c2415Smrg       else if (orig_pathlen != 0)
805760c2415Smrg 	{
806760c2415Smrg 	  // Create single component from original path
807760c2415Smrg 	  ::new(output++) _Cmpt(orig_pathname, orig_type, 0);
808760c2415Smrg 	  ++_M_cmpts._M_impl->_M_size;
809760c2415Smrg 
810760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
811760c2415Smrg 	  if (!sep.empty() && orig_type == _Type::_Root_name)
812760c2415Smrg 	    {
813760c2415Smrg 	      ::new(output++) _Cmpt(sep, _Type::_Root_dir,
814760c2415Smrg 				    orig_pathlen + sep.length());
815760c2415Smrg 	      ++_M_cmpts._M_impl->_M_size;
816760c2415Smrg 	    }
817760c2415Smrg #endif
818760c2415Smrg 	}
819760c2415Smrg 
820760c2415Smrg       if (next != buf.begin())
821760c2415Smrg 	{
822760c2415Smrg 	  for (auto it = buf.begin(); it != next; ++it)
823760c2415Smrg 	    {
824760c2415Smrg 	      auto c = *it;
825760c2415Smrg 	      ::new(output++) _Cmpt(c.str, c.type, parser.offset(c));
826760c2415Smrg 	      ++_M_cmpts._M_impl->_M_size;
827760c2415Smrg 	    }
828760c2415Smrg 	  while (cmpt.valid())
829760c2415Smrg 	    {
830760c2415Smrg 	      ::new(output++) _Cmpt(cmpt.str, cmpt.type, parser.offset(cmpt));
831760c2415Smrg 	      ++_M_cmpts._M_impl->_M_size;
832760c2415Smrg 	      cmpt = parser.next();
833760c2415Smrg 	    }
834760c2415Smrg 	}
835760c2415Smrg       else if (!sep.empty())
836760c2415Smrg 	{
837760c2415Smrg 	  // Empty filename at the end:
838760c2415Smrg 	  ::new(output) _Cmpt({}, _Type::_Filename, basepos);
839760c2415Smrg 	  ++_M_cmpts._M_impl->_M_size;
840760c2415Smrg 	}
841760c2415Smrg     }
842760c2415Smrg   __catch (...)
843760c2415Smrg     {
844760c2415Smrg       _M_pathname.resize(orig_pathlen);
845760c2415Smrg       if (orig_type == _Type::_Multi)
846760c2415Smrg 	_M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
847760c2415Smrg       else
848760c2415Smrg 	_M_cmpts.clear();
849760c2415Smrg       _M_cmpts.type(orig_type);
850760c2415Smrg       __throw_exception_again;
851760c2415Smrg     }
852760c2415Smrg #endif
853760c2415Smrg }
854760c2415Smrg 
855760c2415Smrg // [fs.path.concat]
856760c2415Smrg path&
operator +=(const path & p)857760c2415Smrg path::operator+=(const path& p)
858760c2415Smrg {
859760c2415Smrg   if (p.empty())
860760c2415Smrg     return *this;
861760c2415Smrg 
862760c2415Smrg   if (this->empty())
863760c2415Smrg     {
864760c2415Smrg       operator=(p);
865760c2415Smrg       return *this;
866760c2415Smrg     }
867760c2415Smrg 
8680bfacb9bSmrg #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
8690bfacb9bSmrg   if (_M_type() == _Type::_Root_name
8700bfacb9bSmrg       || (_M_type() == _Type::_Filename && _M_pathname.size() == 1))
8710bfacb9bSmrg     {
8720bfacb9bSmrg       // Handle path("C") += path(":") and path("C:") += path("/x")
8730bfacb9bSmrg       // FIXME: do this more efficiently
8740bfacb9bSmrg       *this = path(_M_pathname + p._M_pathname);
8750bfacb9bSmrg       return *this;
8760bfacb9bSmrg     }
8770bfacb9bSmrg #endif
8780bfacb9bSmrg #if SLASHSLASH_IS_ROOTNAME
8790bfacb9bSmrg   if (_M_type() == _Type::_Root_dir)
8800bfacb9bSmrg     {
8810bfacb9bSmrg       // Handle path("/") += path("/x") and path("//") += path("x")
8820bfacb9bSmrg       // FIXME: do this more efficiently
8830bfacb9bSmrg       *this = path(_M_pathname + p._M_pathname);
8840bfacb9bSmrg       return *this;
8850bfacb9bSmrg     }
8860bfacb9bSmrg #endif
8870bfacb9bSmrg 
888760c2415Smrg   const auto orig_pathlen = _M_pathname.length();
889760c2415Smrg   const auto orig_type = _M_type();
890760c2415Smrg   const auto orig_size = _M_cmpts.size();
891760c2415Smrg   int orig_filenamelen = -1;
892760c2415Smrg   basic_string_view<value_type> extra;
893760c2415Smrg 
894760c2415Smrg   // Ensure that '_M_pathname += p._M_pathname' won't throw:
895760c2415Smrg   _M_pathname.reserve(orig_pathlen + p._M_pathname.length());
896760c2415Smrg 
897760c2415Smrg   _Cmpt c;
898760c2415Smrg   _Cmpt* it = nullptr;
899760c2415Smrg   _Cmpt* last = nullptr;
900760c2415Smrg   if (p._M_type() == _Type::_Multi)
901760c2415Smrg     {
902760c2415Smrg       it = p._M_cmpts._M_impl->begin();
903760c2415Smrg       last = p._M_cmpts._M_impl->end();
904760c2415Smrg     }
905760c2415Smrg   else
906760c2415Smrg     {
907760c2415Smrg       c = _Cmpt(p._M_pathname, p._M_type(), 0);
908760c2415Smrg       it = &c;
909760c2415Smrg       last = it + 1;
910760c2415Smrg     }
911760c2415Smrg 
912760c2415Smrg   if (it->_M_type() == _Type::_Filename)
913760c2415Smrg     {
914760c2415Smrg       // See if there's a filename or root-name at the end of the original path
915760c2415Smrg       // that we can add to.
916760c2415Smrg       if (_M_type() == _Type::_Filename
917760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
918760c2415Smrg 	  || _M_type() == _Type::_Root_name
919760c2415Smrg #endif
920760c2415Smrg 	  )
921760c2415Smrg 	{
922760c2415Smrg 	  if (p._M_type() == _Type::_Filename)
923760c2415Smrg 	    {
924760c2415Smrg 	      // Simplest case where we just add the whole of p to the
925760c2415Smrg 	      // original path.
926760c2415Smrg 	      _M_pathname += p._M_pathname;
927760c2415Smrg 	      return *this;
928760c2415Smrg 	    }
929760c2415Smrg 	  // Only the first component of s should be appended, do so below:
930760c2415Smrg 	  extra = it->_M_pathname;
931760c2415Smrg 	  ++it;
932760c2415Smrg 	}
933760c2415Smrg       else if (_M_type() == _Type::_Multi
934760c2415Smrg 	  && _M_cmpts.back()._M_type() == _Type::_Filename)
935760c2415Smrg 	{
936760c2415Smrg 	  auto& back = _M_cmpts.back();
937760c2415Smrg 	  if (p._M_type() == _Type::_Filename)
938760c2415Smrg 	    {
939760c2415Smrg 	      basic_string_view<value_type> s = p._M_pathname;
940760c2415Smrg 	      back._M_pathname += s;
941760c2415Smrg 	      _M_pathname += s;
942760c2415Smrg 	      return *this;
943760c2415Smrg 	    }
944760c2415Smrg 
945760c2415Smrg 	  orig_filenamelen = back._M_pathname.length();
946760c2415Smrg 	  back._M_pathname += it->_M_pathname;
947760c2415Smrg 	  extra = it->_M_pathname;
948760c2415Smrg 	  ++it;
949760c2415Smrg 	}
950760c2415Smrg     }
951760c2415Smrg   else if (is_dir_sep(_M_pathname.back()) && _M_type() == _Type::_Multi
952760c2415Smrg       && _M_cmpts.back()._M_type() == _Type::_Filename)
953760c2415Smrg     orig_filenamelen = 0; // current path has empty filename at end
954760c2415Smrg 
955760c2415Smrg   int capacity = 0;
956760c2415Smrg   if (_M_type() == _Type::_Multi)
957760c2415Smrg     capacity += _M_cmpts.size();
958760c2415Smrg   else
959760c2415Smrg     capacity += 1;
960760c2415Smrg   if (p._M_type() == _Type::_Multi)
961760c2415Smrg     capacity += p._M_cmpts.size();
962760c2415Smrg   else
963760c2415Smrg     capacity += 1;
964760c2415Smrg 
965760c2415Smrg   __try
966760c2415Smrg     {
967760c2415Smrg       _M_cmpts.type(_Type::_Multi);
968760c2415Smrg       _M_cmpts.reserve(capacity);
969760c2415Smrg       _Cmpt* output = _M_cmpts._M_impl->end();
970760c2415Smrg 
971760c2415Smrg       if (orig_type != _Type::_Multi)
972760c2415Smrg 	{
973760c2415Smrg 	  // Create single component from original path
974760c2415Smrg 	  auto ptr = ::new(output++) _Cmpt({}, orig_type, 0);
975760c2415Smrg 	  ++_M_cmpts._M_impl->_M_size;
976760c2415Smrg 	  ptr->_M_pathname.reserve(_M_pathname.length() + extra.length());
977760c2415Smrg 	  ptr->_M_pathname = _M_pathname;
978760c2415Smrg 	  ptr->_M_pathname += extra;
979760c2415Smrg 
980760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
981760c2415Smrg 	  if (orig_type == _Type::_Root_name)
982760c2415Smrg 	    {
983760c2415Smrg 	      basic_string_view<value_type> s(p._M_pathname);
984760c2415Smrg 	      ::new(output++) _Cmpt(s.substr(extra.length(), 1),
985760c2415Smrg 		  _Type::_Root_dir, orig_pathlen + extra.length());
986760c2415Smrg 	      ++_M_cmpts._M_impl->_M_size;
987760c2415Smrg 	    }
988760c2415Smrg #endif
989760c2415Smrg 	}
990760c2415Smrg       else if (orig_filenamelen == 0 && it != last)
991760c2415Smrg 	{
992760c2415Smrg 	  // Remove empty filename at end of original path.
993760c2415Smrg 	  _M_cmpts.pop_back();
994760c2415Smrg 	  --output;
995760c2415Smrg 	}
996760c2415Smrg 
997760c2415Smrg       if (it != last && it->_M_type() == _Type::_Root_name)
998760c2415Smrg 	{
999760c2415Smrg 	  basic_string_view<value_type> s = it->_M_pathname;
1000760c2415Smrg 	  auto pos = orig_pathlen;
1001760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
1002760c2415Smrg 	  s.remove_prefix(2);
1003760c2415Smrg 	  pos += 2;
1004760c2415Smrg #endif
1005760c2415Smrg 	  ::new(output++) _Cmpt(s, _Type::_Filename, pos);
1006760c2415Smrg 	  ++_M_cmpts._M_impl->_M_size;
1007760c2415Smrg 	  ++it;
1008760c2415Smrg 	}
1009760c2415Smrg 
1010760c2415Smrg       if (it != last && it->_M_type() == _Type::_Root_dir)
1011760c2415Smrg 	++it;
1012760c2415Smrg 
1013760c2415Smrg       while (it != last)
1014760c2415Smrg 	{
1015760c2415Smrg 	  auto pos = it->_M_pos + orig_pathlen;
1016760c2415Smrg 	  ::new(output++) _Cmpt(it->_M_pathname, _Type::_Filename, pos);
1017760c2415Smrg 	  ++_M_cmpts._M_impl->_M_size;
1018760c2415Smrg 	  ++it;
1019760c2415Smrg 	}
1020760c2415Smrg 
1021760c2415Smrg       _M_pathname += p._M_pathname;
1022760c2415Smrg 
1023760c2415Smrg       if (is_dir_sep(_M_pathname.back()))
1024760c2415Smrg 	{
1025760c2415Smrg 	  ::new(output++) _Cmpt({}, _Type::_Filename, _M_pathname.length());
1026760c2415Smrg 	  ++_M_cmpts._M_impl->_M_size;
1027760c2415Smrg 	}
1028760c2415Smrg       }
1029760c2415Smrg   __catch (...)
1030760c2415Smrg     {
1031760c2415Smrg       _M_pathname.resize(orig_pathlen);
1032760c2415Smrg       if (orig_type == _Type::_Multi)
1033760c2415Smrg 	{
1034760c2415Smrg 	  if (_M_cmpts.size() > orig_size)
1035760c2415Smrg 	    _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
1036760c2415Smrg 	  if (orig_filenamelen != -1)
1037760c2415Smrg 	    {
1038760c2415Smrg 	      if (_M_cmpts.size() == orig_size)
1039760c2415Smrg 		{
1040760c2415Smrg 		  auto& back = _M_cmpts.back();
1041760c2415Smrg 		  back._M_pathname.resize(orig_filenamelen);
1042760c2415Smrg 		  if (orig_filenamelen == 0)
1043760c2415Smrg 		    back._M_pos = orig_pathlen;
1044760c2415Smrg 		}
1045760c2415Smrg 	      else
1046760c2415Smrg 		{
1047760c2415Smrg 		  auto output = _M_cmpts._M_impl->end();
1048760c2415Smrg 		  ::new(output) _Cmpt({}, _Type::_Filename, orig_pathlen);
1049760c2415Smrg 		  ++_M_cmpts._M_impl->_M_size;
1050760c2415Smrg 		}
1051760c2415Smrg 	    }
1052760c2415Smrg 	}
1053760c2415Smrg       else
1054760c2415Smrg 	_M_cmpts.clear();
1055760c2415Smrg       _M_cmpts.type(orig_type);
1056760c2415Smrg       __throw_exception_again;
1057760c2415Smrg     }
1058760c2415Smrg   return *this;
1059760c2415Smrg }
1060760c2415Smrg 
1061760c2415Smrg // [fs.path.concat]
1062760c2415Smrg void
_M_concat(basic_string_view<value_type> s)1063760c2415Smrg path::_M_concat(basic_string_view<value_type> s)
1064760c2415Smrg {
1065760c2415Smrg   if (s.empty())
1066760c2415Smrg     return;
1067760c2415Smrg 
1068760c2415Smrg   if (this->empty())
1069760c2415Smrg     {
1070760c2415Smrg       operator=(s);
1071760c2415Smrg       return;
1072760c2415Smrg     }
1073760c2415Smrg 
10740bfacb9bSmrg #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
10750bfacb9bSmrg   if (_M_type() == _Type::_Root_name
10760bfacb9bSmrg       || (_M_type() == _Type::_Filename && _M_pathname.size() == 1))
10770bfacb9bSmrg     {
10780bfacb9bSmrg       // Handle path("C") += ":" and path("C:") += "/x"
10790bfacb9bSmrg       // FIXME: do this more efficiently
10800bfacb9bSmrg       *this = path(_M_pathname + string_type(s));
10810bfacb9bSmrg       return;
10820bfacb9bSmrg     }
10830bfacb9bSmrg #endif
10840bfacb9bSmrg #if SLASHSLASH_IS_ROOTNAME
10850bfacb9bSmrg   if (_M_type() == _Type::_Root_dir)
10860bfacb9bSmrg     {
10870bfacb9bSmrg       // Handle path("/") += "/x" and path("//") += "x"
10880bfacb9bSmrg       // FIXME: do this more efficiently
10890bfacb9bSmrg       *this = path(_M_pathname + string_type(s));
10900bfacb9bSmrg       return;
10910bfacb9bSmrg     }
10920bfacb9bSmrg #endif
10930bfacb9bSmrg 
1094760c2415Smrg   const auto orig_pathlen = _M_pathname.length();
1095760c2415Smrg   const auto orig_type = _M_type();
1096760c2415Smrg   const auto orig_size = _M_cmpts.size();
1097760c2415Smrg   int orig_filenamelen = -1;
1098760c2415Smrg   basic_string_view<value_type> extra;
1099760c2415Smrg 
1100760c2415Smrg   // Copy the input into _M_pathname:
1101760c2415Smrg   _M_pathname += s;
1102760c2415Smrg   // Update s to refer to the new copy (this ensures s is not a dangling
1103760c2415Smrg   // reference to deallocated characters, in the case where it was referring
1104760c2415Smrg   // into _M_pathname or a member of _M_cmpts).
1105760c2415Smrg   s = _M_pathname;
1106760c2415Smrg   const auto orig_pathname = s.substr(0, orig_pathlen);
1107760c2415Smrg   s.remove_prefix(orig_pathlen);
1108760c2415Smrg 
1109760c2415Smrg   _Parser parser(s, orig_pathlen);
1110760c2415Smrg   auto cmpt = parser.next();
1111760c2415Smrg 
1112760c2415Smrg   if (cmpt.str.data() == s.data())
1113760c2415Smrg     {
1114760c2415Smrg       // See if there's a filename or root-name at the end of the original path
1115760c2415Smrg       // that we can add to.
1116760c2415Smrg       if (_M_type() == _Type::_Filename
1117760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
1118760c2415Smrg 	  || _M_type() == _Type::_Root_name
1119760c2415Smrg #endif
1120760c2415Smrg 	  )
1121760c2415Smrg 	{
1122760c2415Smrg 	  if (cmpt.str.length() == s.length())
1123760c2415Smrg 	    {
1124760c2415Smrg 	      // Simplest case where we just need to add the whole of s
1125760c2415Smrg 	      // to the original path, which was already done above.
1126760c2415Smrg 	      return;
1127760c2415Smrg 	    }
1128760c2415Smrg 	  // Only the first component of s should be appended, do so below:
1129760c2415Smrg 	  extra = cmpt.str;
1130760c2415Smrg 	  cmpt = {}; // so we don't process it again
1131760c2415Smrg 	}
1132760c2415Smrg       else if (_M_type() == _Type::_Multi
1133760c2415Smrg 	  && _M_cmpts.back()._M_type() == _Type::_Filename)
1134760c2415Smrg 	{
1135760c2415Smrg 	  auto& back = _M_cmpts.back();
1136760c2415Smrg 	  if (cmpt.str.length() == s.length())
1137760c2415Smrg 	    {
1138760c2415Smrg 	      back._M_pathname += s;
1139760c2415Smrg 	      return;
1140760c2415Smrg 	    }
1141760c2415Smrg 
1142760c2415Smrg 	  orig_filenamelen = back._M_pathname.length();
1143760c2415Smrg 	  back._M_pathname += cmpt.str;
1144760c2415Smrg 	  extra = cmpt.str;
1145760c2415Smrg 	  cmpt = {};
1146760c2415Smrg 	}
1147760c2415Smrg     }
1148760c2415Smrg   else if (is_dir_sep(orig_pathname.back()) && _M_type() == _Type::_Multi
1149760c2415Smrg       && _M_cmpts.back()._M_type() == _Type::_Filename)
1150760c2415Smrg     orig_filenamelen = 0; // original path had empty filename at end
1151760c2415Smrg 
1152760c2415Smrg   std::array<_Parser::cmpt, 64> buf;
1153760c2415Smrg   auto next = buf.begin();
1154760c2415Smrg 
1155760c2415Smrg   if (cmpt.valid())
1156760c2415Smrg     *next++ = cmpt;
1157760c2415Smrg 
1158760c2415Smrg   cmpt = parser.next();
1159760c2415Smrg   while (cmpt.valid() && next != buf.end())
1160760c2415Smrg     {
1161760c2415Smrg       *next++ = cmpt;
1162760c2415Smrg       cmpt = parser.next();
1163760c2415Smrg     }
1164760c2415Smrg 
1165760c2415Smrg   int capacity = 0;
1166760c2415Smrg   if (_M_type() == _Type::_Multi)
1167760c2415Smrg     capacity += _M_cmpts.size();
1168760c2415Smrg   else
1169760c2415Smrg     capacity += 1;
1170760c2415Smrg 
1171760c2415Smrg   capacity += next - buf.begin();
1172760c2415Smrg 
1173760c2415Smrg   if (cmpt.valid()) // filled buffer before parsing whole input
1174760c2415Smrg     {
1175760c2415Smrg       ++capacity;
1176760c2415Smrg       _Parser parser2(parser);
1177760c2415Smrg       while (parser2.next().valid())
1178760c2415Smrg 	++capacity;
1179760c2415Smrg     }
1180760c2415Smrg 
1181760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
1182760c2415Smrg   if (orig_type == _Type::_Root_name)
1183760c2415Smrg     ++capacity; // Need to insert root-directory after root-name
1184760c2415Smrg #endif
1185760c2415Smrg 
1186760c2415Smrg   __try
1187760c2415Smrg     {
1188760c2415Smrg       _M_cmpts.type(_Type::_Multi);
1189760c2415Smrg       _M_cmpts.reserve(capacity);
1190760c2415Smrg       _Cmpt* output = _M_cmpts._M_impl->end();
1191760c2415Smrg       auto it = buf.begin();
1192760c2415Smrg 
1193760c2415Smrg       if (orig_type != _Type::_Multi)
1194760c2415Smrg 	{
1195760c2415Smrg 	  // Create single component from original path
1196760c2415Smrg 	  auto p = ::new(output++) _Cmpt({}, orig_type, 0);
1197760c2415Smrg 	  ++_M_cmpts._M_impl->_M_size;
1198760c2415Smrg 	  p->_M_pathname.reserve(orig_pathname.length() + extra.length());
1199760c2415Smrg 	  p->_M_pathname = orig_pathname;
1200760c2415Smrg 	  p->_M_pathname += extra;
1201760c2415Smrg 
1202760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
1203760c2415Smrg 	  if (orig_type == _Type::_Root_name)
1204760c2415Smrg 	    {
1205760c2415Smrg 	      ::new(output++) _Cmpt(s.substr(extra.length(), 1),
1206760c2415Smrg 		  _Type::_Root_dir, orig_pathlen + extra.length());
1207760c2415Smrg 	      ++_M_cmpts._M_impl->_M_size;
1208760c2415Smrg 	    }
1209760c2415Smrg #endif
1210760c2415Smrg 	}
1211760c2415Smrg       else if (orig_filenamelen == 0 && extra.empty())
1212760c2415Smrg 	{
1213760c2415Smrg 	  // Replace empty filename at end of original path.
1214760c2415Smrg 	  std::prev(output)->_M_pathname = it->str;
1215760c2415Smrg 	  std::prev(output)->_M_pos = parser.offset(*it);
1216760c2415Smrg 	  ++it;
1217760c2415Smrg 	}
1218760c2415Smrg 
1219760c2415Smrg       while (it != next)
1220760c2415Smrg 	{
1221760c2415Smrg 	  ::new(output++) _Cmpt(it->str, _Type::_Filename, parser.offset(*it));
1222760c2415Smrg 	  ++_M_cmpts._M_impl->_M_size;
1223760c2415Smrg 	  ++it;
1224760c2415Smrg 	}
1225760c2415Smrg 
1226760c2415Smrg       if (next == buf.end())
1227760c2415Smrg 	{
1228760c2415Smrg 	  while (cmpt.valid())
1229760c2415Smrg 	    {
1230760c2415Smrg 	      auto pos = parser.offset(cmpt);
1231760c2415Smrg 	      ::new(output++) _Cmpt(cmpt.str, _Type::_Filename, pos);
1232760c2415Smrg 	      ++_M_cmpts._M_impl->_M_size;
1233760c2415Smrg 	      cmpt = parser.next();
1234760c2415Smrg 	    }
1235760c2415Smrg 	}
1236760c2415Smrg     }
1237760c2415Smrg   __catch (...)
1238760c2415Smrg     {
1239760c2415Smrg       _M_pathname.resize(orig_pathlen);
1240760c2415Smrg       if (orig_type == _Type::_Multi)
1241760c2415Smrg 	{
1242760c2415Smrg 	  _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
1243760c2415Smrg 	  if (orig_filenamelen != -1)
1244760c2415Smrg 	    {
1245760c2415Smrg 	      auto& back = _M_cmpts.back();
1246760c2415Smrg 	      back._M_pathname.resize(orig_filenamelen);
1247760c2415Smrg 	      if (orig_filenamelen == 0)
1248760c2415Smrg 		back._M_pos = orig_pathlen;
1249760c2415Smrg 	    }
1250760c2415Smrg 	}
1251760c2415Smrg       else
1252760c2415Smrg 	_M_cmpts.clear();
1253760c2415Smrg       _M_cmpts.type(orig_type);
1254760c2415Smrg       __throw_exception_again;
1255760c2415Smrg     }
1256760c2415Smrg }
1257760c2415Smrg 
1258760c2415Smrg path&
remove_filename()1259760c2415Smrg path::remove_filename()
1260760c2415Smrg {
1261760c2415Smrg   if (_M_type() == _Type::_Multi)
1262760c2415Smrg     {
1263760c2415Smrg       if (!_M_cmpts.empty())
1264760c2415Smrg 	{
1265760c2415Smrg 	  auto cmpt = std::prev(_M_cmpts.end());
1266760c2415Smrg 	  if (cmpt->_M_type() == _Type::_Filename && !cmpt->empty())
1267760c2415Smrg 	    {
1268760c2415Smrg 	      _M_pathname.erase(cmpt->_M_pos);
1269760c2415Smrg 	      auto prev = std::prev(cmpt);
1270760c2415Smrg 	      if (prev->_M_type() == _Type::_Root_dir
1271760c2415Smrg 		  || prev->_M_type() == _Type::_Root_name)
1272760c2415Smrg 		{
1273760c2415Smrg 		  _M_cmpts.pop_back();
1274760c2415Smrg 		  if (_M_cmpts.size() == 1)
1275760c2415Smrg 		    {
1276760c2415Smrg 		      _M_cmpts.type(_M_cmpts.front()._M_type());
1277760c2415Smrg 		      _M_cmpts.clear();
1278760c2415Smrg 		    }
1279760c2415Smrg 		}
1280760c2415Smrg 	      else
1281760c2415Smrg 		cmpt->clear();
1282760c2415Smrg 	    }
1283760c2415Smrg 	}
1284760c2415Smrg     }
1285760c2415Smrg   else if (_M_type() == _Type::_Filename)
1286760c2415Smrg     clear();
1287760c2415Smrg   return *this;
1288760c2415Smrg }
1289760c2415Smrg 
1290760c2415Smrg path&
replace_filename(const path & replacement)1291760c2415Smrg path::replace_filename(const path& replacement)
1292760c2415Smrg {
1293760c2415Smrg   remove_filename();
1294760c2415Smrg   operator/=(replacement);
1295760c2415Smrg   return *this;
1296760c2415Smrg }
1297760c2415Smrg 
1298760c2415Smrg #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1299760c2415Smrg const fs::path::value_type dot = L'.';
1300760c2415Smrg #else
1301760c2415Smrg const fs::path::value_type dot = '.';
1302760c2415Smrg #endif
1303760c2415Smrg 
1304760c2415Smrg path&
replace_extension(const path & replacement)1305760c2415Smrg path::replace_extension(const path& replacement)
1306760c2415Smrg {
1307760c2415Smrg   auto ext = _M_find_extension();
1308760c2415Smrg   // Any existing extension() is removed
1309760c2415Smrg   if (ext.first && ext.second != string_type::npos)
1310760c2415Smrg     {
1311760c2415Smrg       if (ext.first == &_M_pathname)
1312760c2415Smrg 	_M_pathname.erase(ext.second);
1313760c2415Smrg       else
1314760c2415Smrg 	{
1315760c2415Smrg 	  auto& back = _M_cmpts.back();
1316760c2415Smrg 	  __glibcxx_assert( ext.first == &back._M_pathname );
1317760c2415Smrg 	  back._M_pathname.erase(ext.second);
1318760c2415Smrg 	  _M_pathname.erase(back._M_pos + ext.second);
1319760c2415Smrg 	}
1320760c2415Smrg     }
1321760c2415Smrg    // If replacement is not empty and does not begin with a dot character,
1322760c2415Smrg    // a dot character is appended
1323760c2415Smrg   if (!replacement.empty() && replacement.native()[0] != dot)
1324760c2415Smrg     operator+=(".");
1325760c2415Smrg   operator+=(replacement);
1326760c2415Smrg   return *this;
1327760c2415Smrg }
1328760c2415Smrg 
1329760c2415Smrg int
compare(const path & p) const1330760c2415Smrg path::compare(const path& p) const noexcept
1331760c2415Smrg {
1332760c2415Smrg   if (_M_pathname == p._M_pathname)
1333760c2415Smrg     return 0;
1334760c2415Smrg 
1335760c2415Smrg   basic_string_view<value_type> lroot, rroot;
1336760c2415Smrg   if (_M_type() == _Type::_Root_name)
1337760c2415Smrg     lroot = _M_pathname;
1338760c2415Smrg   else if (_M_type() == _Type::_Multi
1339760c2415Smrg       && _M_cmpts.front()._M_type() == _Type::_Root_name)
1340760c2415Smrg     lroot = _M_cmpts.front()._M_pathname;
1341760c2415Smrg   if (p._M_type() == _Type::_Root_name)
1342760c2415Smrg     rroot = p._M_pathname;
1343760c2415Smrg   else if (p._M_type() == _Type::_Multi
1344760c2415Smrg       && p._M_cmpts.front()._M_type() == _Type::_Root_name)
1345760c2415Smrg     rroot = p._M_cmpts.front()._M_pathname;
1346760c2415Smrg   if (int rootNameComparison = lroot.compare(rroot))
1347760c2415Smrg     return rootNameComparison;
1348760c2415Smrg 
1349760c2415Smrg   if (!this->has_root_directory() && p.has_root_directory())
1350760c2415Smrg     return -1;
1351760c2415Smrg   else if (this->has_root_directory() && !p.has_root_directory())
1352760c2415Smrg     return +1;
1353760c2415Smrg 
1354760c2415Smrg   using Iterator = const _Cmpt*;
1355760c2415Smrg   Iterator begin1, end1, begin2, end2;
1356760c2415Smrg   if (_M_type() == _Type::_Multi)
1357760c2415Smrg     {
1358760c2415Smrg       begin1 = _M_cmpts.begin();
1359760c2415Smrg       end1 = _M_cmpts.end();
1360760c2415Smrg       // Find start of this->relative_path()
1361760c2415Smrg       while (begin1 != end1 && begin1->_M_type() != _Type::_Filename)
1362760c2415Smrg 	++begin1;
1363760c2415Smrg     }
1364760c2415Smrg   else
1365760c2415Smrg     begin1 = end1 = nullptr;
1366760c2415Smrg 
1367760c2415Smrg   if (p._M_type() == _Type::_Multi)
1368760c2415Smrg     {
1369760c2415Smrg       begin2 = p._M_cmpts.begin();
1370760c2415Smrg       end2 = p._M_cmpts.end();
1371760c2415Smrg       // Find start of p.relative_path()
1372760c2415Smrg       while (begin2 != end2 && begin2->_M_type() != _Type::_Filename)
1373760c2415Smrg 	++begin2;
1374760c2415Smrg     }
1375760c2415Smrg   else
1376760c2415Smrg     begin2 = end2 = nullptr;
1377760c2415Smrg 
1378760c2415Smrg   if (_M_type() == _Type::_Filename)
1379760c2415Smrg     {
1380760c2415Smrg       if (p._M_type() == _Type::_Filename)
1381760c2415Smrg 	return native().compare(p.native());
1382760c2415Smrg       else if (begin2 != end2)
1383760c2415Smrg 	{
1384760c2415Smrg 	  if (int ret = native().compare(begin2->native()))
1385760c2415Smrg 	    return ret;
1386760c2415Smrg 	  else
1387760c2415Smrg 	    return ++begin2 == end2 ? 0 : -1;
1388760c2415Smrg 	}
1389760c2415Smrg       else
1390760c2415Smrg 	return +1;
1391760c2415Smrg     }
1392760c2415Smrg   else if (p._M_type() == _Type::_Filename)
1393760c2415Smrg     {
1394760c2415Smrg       if (begin1 != end1)
1395760c2415Smrg 	{
1396760c2415Smrg 	  if (int ret = begin1->native().compare(p.native()))
1397760c2415Smrg 	    return ret;
1398760c2415Smrg 	  else
1399760c2415Smrg 	    return ++begin1 == end1 ? 0 : +1;
1400760c2415Smrg 	}
1401760c2415Smrg       else
1402760c2415Smrg 	return -1;
1403760c2415Smrg     }
1404760c2415Smrg 
1405760c2415Smrg   int count = 1;
1406760c2415Smrg   while (begin1 != end1 && begin2 != end2)
1407760c2415Smrg     {
1408760c2415Smrg       if (int i = begin1->native().compare(begin2->native()))
1409760c2415Smrg 	return i;
1410760c2415Smrg       ++begin1;
1411760c2415Smrg       ++begin2;
1412760c2415Smrg       ++count;
1413760c2415Smrg     }
1414760c2415Smrg   if (begin1 == end1)
1415760c2415Smrg     {
1416760c2415Smrg       if (begin2 == end2)
1417760c2415Smrg 	return 0;
1418760c2415Smrg       return -count;
1419760c2415Smrg     }
1420760c2415Smrg   return count;
1421760c2415Smrg }
1422760c2415Smrg 
1423760c2415Smrg int
compare(basic_string_view<value_type> s) const1424760c2415Smrg path::compare(basic_string_view<value_type> s) const noexcept
1425760c2415Smrg {
1426760c2415Smrg   if (_M_pathname == s)
1427760c2415Smrg     return 0;
1428760c2415Smrg 
1429760c2415Smrg   _Parser parser(s);
1430760c2415Smrg 
1431760c2415Smrg   basic_string_view<value_type> lroot, rroot;
1432760c2415Smrg   if (_M_type() == _Type::_Root_name)
1433760c2415Smrg     lroot = _M_pathname;
1434760c2415Smrg   else if (_M_type() == _Type::_Multi
1435760c2415Smrg       && _M_cmpts.front()._M_type() == _Type::_Root_name)
1436760c2415Smrg     lroot = _M_cmpts.front()._M_pathname;
1437760c2415Smrg   auto root_path = parser.root_path();
1438760c2415Smrg   if (root_path.first.type == _Type::_Root_name)
1439760c2415Smrg     rroot = root_path.first.str;
1440760c2415Smrg   if (int rootNameComparison = lroot.compare(rroot))
1441760c2415Smrg     return rootNameComparison;
1442760c2415Smrg 
1443760c2415Smrg   const bool has_root_dir = root_path.first.type == _Type::_Root_dir
1444760c2415Smrg     || root_path.second.type == _Type::_Root_dir;
1445760c2415Smrg   if (!this->has_root_directory() && has_root_dir)
1446760c2415Smrg     return -1;
1447760c2415Smrg   else if (this->has_root_directory() && !has_root_dir)
1448760c2415Smrg     return +1;
1449760c2415Smrg 
1450760c2415Smrg   using Iterator = const _Cmpt*;
1451760c2415Smrg   Iterator begin1, end1;
1452760c2415Smrg   if (_M_type() == _Type::_Filename)
1453760c2415Smrg     {
1454760c2415Smrg       auto cmpt = parser.next();
1455760c2415Smrg       if (cmpt.valid())
1456760c2415Smrg 	{
1457760c2415Smrg 	  if (int ret = this->native().compare(cmpt.str))
1458760c2415Smrg 	    return ret;
1459760c2415Smrg 	  return parser.next().valid() ? -1 : 0;
1460760c2415Smrg 	}
1461760c2415Smrg       else
1462760c2415Smrg 	return +1;
1463760c2415Smrg     }
1464760c2415Smrg   else if (_M_type() == _Type::_Multi)
1465760c2415Smrg     {
1466760c2415Smrg       begin1 = _M_cmpts.begin();
1467760c2415Smrg       end1 = _M_cmpts.end();
1468760c2415Smrg       while (begin1 != end1 && begin1->_M_type() != _Type::_Filename)
1469760c2415Smrg 	++begin1;
1470760c2415Smrg     }
1471760c2415Smrg   else
1472760c2415Smrg     begin1 = end1 = nullptr;
1473760c2415Smrg 
1474760c2415Smrg   int count = 1;
1475760c2415Smrg   auto cmpt = parser.next();
1476760c2415Smrg   while (begin1 != end1 && cmpt.valid())
1477760c2415Smrg     {
1478760c2415Smrg       if (int i = begin1->native().compare(cmpt.str))
1479760c2415Smrg 	return i;
1480760c2415Smrg       ++begin1;
1481760c2415Smrg       cmpt = parser.next();
1482760c2415Smrg       ++count;
1483760c2415Smrg     }
1484760c2415Smrg   if (begin1 == end1)
1485760c2415Smrg     {
1486760c2415Smrg       if (!cmpt.valid())
1487760c2415Smrg 	return 0;
1488760c2415Smrg       return -count;
1489760c2415Smrg     }
1490760c2415Smrg   return +count;
1491760c2415Smrg }
1492760c2415Smrg 
1493760c2415Smrg path
root_name() const1494760c2415Smrg path::root_name() const
1495760c2415Smrg {
1496760c2415Smrg   path __ret;
1497760c2415Smrg   if (_M_type() == _Type::_Root_name)
1498760c2415Smrg     __ret = *this;
1499760c2415Smrg   else if (_M_cmpts.size() && _M_cmpts.begin()->_M_type() == _Type::_Root_name)
1500760c2415Smrg     __ret = *_M_cmpts.begin();
1501760c2415Smrg   return __ret;
1502760c2415Smrg }
1503760c2415Smrg 
1504760c2415Smrg path
root_directory() const1505760c2415Smrg path::root_directory() const
1506760c2415Smrg {
1507760c2415Smrg   path __ret;
1508760c2415Smrg   if (_M_type() == _Type::_Root_dir)
1509760c2415Smrg     {
1510760c2415Smrg       __ret._M_cmpts.type(_Type::_Root_dir);
1511760c2415Smrg       __ret._M_pathname.assign(1, preferred_separator);
1512760c2415Smrg     }
1513760c2415Smrg   else if (!_M_cmpts.empty())
1514760c2415Smrg     {
1515760c2415Smrg       auto __it = _M_cmpts.begin();
1516760c2415Smrg       if (__it->_M_type() == _Type::_Root_name)
1517760c2415Smrg         ++__it;
1518760c2415Smrg       if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
1519760c2415Smrg         __ret = *__it;
1520760c2415Smrg     }
1521760c2415Smrg   return __ret;
1522760c2415Smrg }
1523760c2415Smrg 
1524760c2415Smrg path
root_path() const1525760c2415Smrg path::root_path() const
1526760c2415Smrg {
1527760c2415Smrg   path __ret;
1528760c2415Smrg   if (_M_type() == _Type::_Root_name)
1529760c2415Smrg     __ret = *this;
1530760c2415Smrg   else if (_M_type() == _Type::_Root_dir)
1531760c2415Smrg     {
1532760c2415Smrg       __ret._M_pathname.assign(1, preferred_separator);
1533760c2415Smrg       __ret._M_cmpts.type(_Type::_Root_dir);
1534760c2415Smrg     }
1535760c2415Smrg   else if (!_M_cmpts.empty())
1536760c2415Smrg     {
1537760c2415Smrg       auto __it = _M_cmpts.begin();
1538760c2415Smrg       if (__it->_M_type() == _Type::_Root_name)
1539760c2415Smrg         {
1540760c2415Smrg           __ret = *__it++;
1541760c2415Smrg           if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
1542760c2415Smrg 	    __ret /= *__it;
1543760c2415Smrg         }
1544760c2415Smrg       else if (__it->_M_type() == _Type::_Root_dir)
1545760c2415Smrg         __ret = *__it;
1546760c2415Smrg     }
1547760c2415Smrg   return __ret;
1548760c2415Smrg }
1549760c2415Smrg 
1550760c2415Smrg path
relative_path() const1551760c2415Smrg path::relative_path() const
1552760c2415Smrg {
1553760c2415Smrg   path __ret;
1554760c2415Smrg   if (_M_type() == _Type::_Filename)
1555760c2415Smrg     __ret = *this;
1556760c2415Smrg   else if (!_M_cmpts.empty())
1557760c2415Smrg     {
1558760c2415Smrg       auto __it = _M_cmpts.begin();
1559760c2415Smrg       if (__it->_M_type() == _Type::_Root_name)
1560760c2415Smrg         ++__it;
1561760c2415Smrg       if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
1562760c2415Smrg         ++__it;
1563760c2415Smrg       if (__it != _M_cmpts.end())
1564760c2415Smrg         __ret.assign(_M_pathname.substr(__it->_M_pos));
1565760c2415Smrg     }
1566760c2415Smrg   return __ret;
1567760c2415Smrg }
1568760c2415Smrg 
1569760c2415Smrg path
parent_path() const1570760c2415Smrg path::parent_path() const
1571760c2415Smrg {
1572760c2415Smrg   path __ret;
1573760c2415Smrg   if (!has_relative_path())
1574760c2415Smrg     __ret = *this;
1575760c2415Smrg   else if (_M_cmpts.size() >= 2)
1576760c2415Smrg     {
1577760c2415Smrg       const auto parent = std::prev(_M_cmpts.end(), 2);
1578760c2415Smrg       const auto len = parent->_M_pos + parent->_M_pathname.length();
1579760c2415Smrg       __ret.assign(_M_pathname.substr(0, len));
1580760c2415Smrg     }
1581760c2415Smrg   return __ret;
1582760c2415Smrg }
1583760c2415Smrg 
1584760c2415Smrg bool
has_root_name() const1585760c2415Smrg path::has_root_name() const noexcept
1586760c2415Smrg {
1587760c2415Smrg   if (_M_type() == _Type::_Root_name)
1588760c2415Smrg     return true;
1589760c2415Smrg   if (!_M_cmpts.empty() && _M_cmpts.begin()->_M_type() == _Type::_Root_name)
1590760c2415Smrg     return true;
1591760c2415Smrg   return false;
1592760c2415Smrg }
1593760c2415Smrg 
1594760c2415Smrg bool
has_root_directory() const1595760c2415Smrg path::has_root_directory() const noexcept
1596760c2415Smrg {
1597760c2415Smrg   if (_M_type() == _Type::_Root_dir)
1598760c2415Smrg     return true;
1599760c2415Smrg   if (!_M_cmpts.empty())
1600760c2415Smrg     {
1601760c2415Smrg       auto __it = _M_cmpts.begin();
1602760c2415Smrg       if (__it->_M_type() == _Type::_Root_name)
1603760c2415Smrg         ++__it;
1604760c2415Smrg       if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
1605760c2415Smrg         return true;
1606760c2415Smrg     }
1607760c2415Smrg   return false;
1608760c2415Smrg }
1609760c2415Smrg 
1610760c2415Smrg bool
has_root_path() const1611760c2415Smrg path::has_root_path() const noexcept
1612760c2415Smrg {
1613760c2415Smrg   if (_M_type() == _Type::_Root_name || _M_type() == _Type::_Root_dir)
1614760c2415Smrg     return true;
1615760c2415Smrg   if (!_M_cmpts.empty())
1616760c2415Smrg     {
1617760c2415Smrg       auto __type = _M_cmpts.front()._M_type();
1618760c2415Smrg       if (__type == _Type::_Root_name || __type == _Type::_Root_dir)
1619760c2415Smrg         return true;
1620760c2415Smrg     }
1621760c2415Smrg   return false;
1622760c2415Smrg }
1623760c2415Smrg 
1624760c2415Smrg bool
has_relative_path() const1625760c2415Smrg path::has_relative_path() const noexcept
1626760c2415Smrg {
1627760c2415Smrg   if (_M_type() == _Type::_Filename && !_M_pathname.empty())
1628760c2415Smrg     return true;
1629760c2415Smrg   if (!_M_cmpts.empty())
1630760c2415Smrg     {
1631760c2415Smrg       auto __it = _M_cmpts.begin();
1632760c2415Smrg       if (__it->_M_type() == _Type::_Root_name)
1633760c2415Smrg         ++__it;
1634760c2415Smrg       if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
1635760c2415Smrg         ++__it;
1636760c2415Smrg       if (__it != _M_cmpts.end() && !__it->_M_pathname.empty())
1637760c2415Smrg         return true;
1638760c2415Smrg     }
1639760c2415Smrg   return false;
1640760c2415Smrg }
1641760c2415Smrg 
1642760c2415Smrg 
1643760c2415Smrg bool
has_parent_path() const1644760c2415Smrg path::has_parent_path() const noexcept
1645760c2415Smrg {
1646760c2415Smrg   if (!has_relative_path())
1647760c2415Smrg     return !empty();
1648760c2415Smrg   return _M_cmpts.size() >= 2;
1649760c2415Smrg }
1650760c2415Smrg 
1651760c2415Smrg bool
has_filename() const1652760c2415Smrg path::has_filename() const noexcept
1653760c2415Smrg {
1654760c2415Smrg   if (empty())
1655760c2415Smrg     return false;
1656760c2415Smrg   if (_M_type() == _Type::_Filename)
1657760c2415Smrg     return !_M_pathname.empty();
1658760c2415Smrg   if (_M_type() == _Type::_Multi)
1659760c2415Smrg     {
1660760c2415Smrg       if (_M_pathname.back() == preferred_separator)
1661760c2415Smrg 	return false;
1662760c2415Smrg       return _M_cmpts.back().has_filename();
1663760c2415Smrg     }
1664760c2415Smrg   return false;
1665760c2415Smrg }
1666760c2415Smrg 
1667760c2415Smrg namespace
1668760c2415Smrg {
is_dot(fs::path::value_type c)1669760c2415Smrg   inline bool is_dot(fs::path::value_type c) { return c == dot; }
1670760c2415Smrg 
is_dot(const fs::path & path)1671760c2415Smrg   inline bool is_dot(const fs::path& path)
1672760c2415Smrg   {
1673760c2415Smrg     const auto& filename = path.native();
1674760c2415Smrg     return filename.size() == 1 && is_dot(filename[0]);
1675760c2415Smrg   }
1676760c2415Smrg 
is_dotdot(const fs::path & path)1677760c2415Smrg   inline bool is_dotdot(const fs::path& path)
1678760c2415Smrg   {
1679760c2415Smrg     const auto& filename = path.native();
1680760c2415Smrg     return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
1681760c2415Smrg   }
1682760c2415Smrg } // namespace
1683760c2415Smrg 
1684760c2415Smrg path
lexically_normal() const1685760c2415Smrg path::lexically_normal() const
1686760c2415Smrg {
1687760c2415Smrg   /*
1688760c2415Smrg   C++17 [fs.path.generic] p6
1689760c2415Smrg   - If the path is empty, stop.
1690760c2415Smrg   - Replace each slash character in the root-name with a preferred-separator.
1691760c2415Smrg   - Replace each directory-separator with a preferred-separator.
1692760c2415Smrg   - Remove each dot filename and any immediately following directory-separator.
1693760c2415Smrg   - As long as any appear, remove a non-dot-dot filename immediately followed
1694760c2415Smrg     by a directory-separator and a dot-dot filename, along with any immediately
1695760c2415Smrg     following directory-separator.
1696760c2415Smrg   - If there is a root-directory, remove all dot-dot filenames and any
1697760c2415Smrg     directory-separators immediately following them.
1698760c2415Smrg   - If the last filename is dot-dot, remove any trailing directory-separator.
1699760c2415Smrg   - If the path is empty, add a dot.
1700760c2415Smrg   */
1701760c2415Smrg   path ret;
1702760c2415Smrg   // If the path is empty, stop.
1703760c2415Smrg   if (empty())
1704760c2415Smrg     return ret;
1705760c2415Smrg   for (auto& p : *this)
1706760c2415Smrg     {
1707760c2415Smrg #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1708760c2415Smrg       // Replace each slash character in the root-name
1709760c2415Smrg       if (p._M_type() == _Type::_Root_name || p._M_type() == _Type::_Root_dir)
1710760c2415Smrg 	{
1711760c2415Smrg 	  string_type s = p.native();
1712760c2415Smrg 	  std::replace(s.begin(), s.end(), L'/', L'\\');
1713760c2415Smrg 	  ret /= s;
1714760c2415Smrg 	  continue;
1715760c2415Smrg 	}
1716760c2415Smrg #endif
1717760c2415Smrg       if (is_dotdot(p))
1718760c2415Smrg 	{
1719760c2415Smrg 	  if (ret.has_filename())
1720760c2415Smrg 	    {
1721760c2415Smrg 	      // remove a non-dot-dot filename immediately followed by /..
1722760c2415Smrg 	      if (!is_dotdot(ret.filename()))
1723760c2415Smrg 		ret.remove_filename();
1724760c2415Smrg 	      else
1725760c2415Smrg 		ret /= p;
1726760c2415Smrg 	    }
1727760c2415Smrg 	  else if (!ret.has_relative_path())
1728760c2415Smrg 	    {
1729760c2415Smrg 	      // remove a dot-dot filename immediately after root-directory
1730760c2415Smrg 	      if (!ret.has_root_directory())
1731760c2415Smrg 		ret /= p;
1732760c2415Smrg 	    }
1733760c2415Smrg 	  else
1734760c2415Smrg 	    {
1735760c2415Smrg 	      // Got a path with a relative path (i.e. at least one non-root
1736760c2415Smrg 	      // element) and no filename at the end (i.e. empty last element),
1737760c2415Smrg 	      // so must have a trailing slash. See what is before it.
1738760c2415Smrg 	      auto elem = ret._M_cmpts.end() - 2;
1739760c2415Smrg 	      if (elem->has_filename() && !is_dotdot(*elem))
1740760c2415Smrg 		{
1741760c2415Smrg 		  // Remove the filename before the trailing slash
1742760c2415Smrg 		  // (equiv. to ret = ret.parent_path().remove_filename())
1743760c2415Smrg 
1744760c2415Smrg 		  if (elem == ret._M_cmpts.begin())
1745760c2415Smrg 		    ret.clear();
1746760c2415Smrg 		  else
1747760c2415Smrg 		    {
1748760c2415Smrg 		      ret._M_pathname.erase(elem->_M_pos);
1749760c2415Smrg 		      // Remove empty filename at the end:
1750760c2415Smrg 		      ret._M_cmpts.pop_back();
1751760c2415Smrg 		      // If we still have a trailing non-root dir separator
1752760c2415Smrg 		      // then leave an empty filename at the end:
1753760c2415Smrg 		      if (std::prev(elem)->_M_type() == _Type::_Filename)
1754760c2415Smrg 			elem->clear();
1755760c2415Smrg 		      else // remove the component completely:
1756760c2415Smrg 			ret._M_cmpts.pop_back();
1757760c2415Smrg 		    }
1758760c2415Smrg 		}
1759760c2415Smrg 	      else
1760760c2415Smrg 		// Append the ".." to something ending in "../" which happens
1761760c2415Smrg 		// when normalising paths like ".././.." and "../a/../.."
1762760c2415Smrg 		ret /= p;
1763760c2415Smrg 	    }
1764760c2415Smrg 	}
1765760c2415Smrg       else if (is_dot(p))
1766760c2415Smrg 	ret /= path();
1767760c2415Smrg #if SLASHSLASH_IS_ROOTNAME
1768760c2415Smrg       else if (p._M_type() == _Type::_Root_dir)
1769760c2415Smrg 	ret += '/'; // using operator/=('/') would replace whole of ret
1770760c2415Smrg #endif
1771760c2415Smrg       else
1772760c2415Smrg 	ret /= p;
1773760c2415Smrg     }
1774760c2415Smrg 
1775760c2415Smrg   if (ret._M_cmpts.size() >= 2)
1776760c2415Smrg     {
1777760c2415Smrg       auto back = std::prev(ret.end());
1778760c2415Smrg       // If the last filename is dot-dot, ...
1779760c2415Smrg       if (back->empty() && is_dotdot(*std::prev(back)))
1780760c2415Smrg 	// ... remove any trailing directory-separator.
1781760c2415Smrg 	ret = ret.parent_path();
1782760c2415Smrg     }
1783760c2415Smrg   // If the path is empty, add a dot.
1784760c2415Smrg   else if (ret.empty())
1785760c2415Smrg     ret = ".";
1786760c2415Smrg 
1787760c2415Smrg   return ret;
1788760c2415Smrg }
1789760c2415Smrg 
1790760c2415Smrg path
lexically_relative(const path & base) const1791760c2415Smrg path::lexically_relative(const path& base) const
1792760c2415Smrg {
1793760c2415Smrg   path ret;
1794760c2415Smrg   if (root_name() != base.root_name())
1795760c2415Smrg     return ret;
1796760c2415Smrg   if (is_absolute() != base.is_absolute())
1797760c2415Smrg     return ret;
1798760c2415Smrg   if (!has_root_directory() && base.has_root_directory())
1799760c2415Smrg     return ret;
1800760c2415Smrg   auto [a, b] = std::mismatch(begin(), end(), base.begin(), base.end());
18010bfacb9bSmrg #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
18020bfacb9bSmrg   // _GLIBCXX_RESOLVE_LIB_DEFECTS
18030bfacb9bSmrg   // 3070. path::lexically_relative causes surprising results if a filename
18040bfacb9bSmrg   // can also be a root-name
18050bfacb9bSmrg   if (!empty())
18060bfacb9bSmrg     for (auto& p : _M_cmpts)
18070bfacb9bSmrg       if (p._M_type() == _Type::_Filename && is_disk_designator(p.native()))
18080bfacb9bSmrg 	return ret;
18090bfacb9bSmrg   if (!base.empty())
18100bfacb9bSmrg     for (auto i = b, end = base.end(); i != end; ++i)
18110bfacb9bSmrg       if (i->_M_type() == _Type::_Filename && is_disk_designator(i->native()))
18120bfacb9bSmrg 	return ret;
18130bfacb9bSmrg #endif
1814760c2415Smrg   if (a == end() && b == base.end())
1815760c2415Smrg     ret = ".";
1816760c2415Smrg   else
1817760c2415Smrg   {
1818760c2415Smrg     int n = 0;
1819760c2415Smrg     for (; b != base.end(); ++b)
1820760c2415Smrg     {
1821760c2415Smrg       const path& p = *b;
1822760c2415Smrg       if (is_dotdot(p))
1823760c2415Smrg 	--n;
1824760c2415Smrg       else if (!p.empty() && !is_dot(p))
1825760c2415Smrg 	++n;
1826760c2415Smrg     }
1827760c2415Smrg     if (n == 0 && (a == end() || a->empty()))
1828760c2415Smrg       ret = ".";
1829760c2415Smrg     else if (n >= 0)
1830760c2415Smrg     {
1831760c2415Smrg       const path dotdot("..");
1832760c2415Smrg       while (n--)
1833760c2415Smrg 	ret /= dotdot;
1834760c2415Smrg       for (; a != end(); ++a)
1835760c2415Smrg 	ret /= *a;
1836760c2415Smrg     }
1837760c2415Smrg   }
1838760c2415Smrg   return ret;
1839760c2415Smrg }
1840760c2415Smrg 
1841760c2415Smrg path
lexically_proximate(const path & base) const1842760c2415Smrg path::lexically_proximate(const path& base) const
1843760c2415Smrg {
1844760c2415Smrg   path rel = lexically_relative(base);
1845760c2415Smrg   if (rel.empty())
1846760c2415Smrg     rel = *this;
1847760c2415Smrg   return rel;
1848760c2415Smrg }
1849760c2415Smrg 
1850760c2415Smrg std::pair<const path::string_type*, std::size_t>
_M_find_extension() const1851760c2415Smrg path::_M_find_extension() const noexcept
1852760c2415Smrg {
1853760c2415Smrg   const string_type* s = nullptr;
1854760c2415Smrg 
1855760c2415Smrg   if (_M_type() == _Type::_Filename)
1856760c2415Smrg     s = &_M_pathname;
1857760c2415Smrg   else if (_M_type() == _Type::_Multi && !_M_cmpts.empty())
1858760c2415Smrg     {
1859760c2415Smrg       const auto& c = _M_cmpts.back();
1860760c2415Smrg       if (c._M_type() == _Type::_Filename)
1861760c2415Smrg 	s = &c._M_pathname;
1862760c2415Smrg     }
1863760c2415Smrg 
1864760c2415Smrg   if (s)
1865760c2415Smrg     {
1866760c2415Smrg       if (auto sz = s->size())
1867760c2415Smrg 	{
1868760c2415Smrg 	  if (sz <= 2 && (*s)[0] == dot)
1869760c2415Smrg 	    return { s, string_type::npos };
1870760c2415Smrg 	  if (const auto pos = s->rfind(dot))
1871760c2415Smrg 	    return { s , pos };
1872760c2415Smrg 	  return { s, string_type::npos };
1873760c2415Smrg 	}
1874760c2415Smrg     }
1875760c2415Smrg   return {};
1876760c2415Smrg }
1877760c2415Smrg 
1878760c2415Smrg void
_M_split_cmpts()1879760c2415Smrg path::_M_split_cmpts()
1880760c2415Smrg {
1881760c2415Smrg   _M_cmpts.clear();
1882760c2415Smrg 
1883760c2415Smrg   if (_M_pathname.empty())
1884760c2415Smrg     {
1885760c2415Smrg       _M_cmpts.type(_Type::_Filename);
1886760c2415Smrg       return;
1887760c2415Smrg     }
1888760c2415Smrg   if (_M_pathname.length() == 1 && _M_pathname[0] == preferred_separator)
1889760c2415Smrg     {
1890760c2415Smrg       _M_cmpts.type(_Type::_Root_dir);
1891760c2415Smrg       return;
1892760c2415Smrg     }
1893760c2415Smrg 
1894760c2415Smrg   _Parser parser(_M_pathname);
1895760c2415Smrg 
1896760c2415Smrg   std::array<_Parser::cmpt, 64> buf;
1897760c2415Smrg   auto next = buf.begin();
1898760c2415Smrg 
1899760c2415Smrg   // look for root name or root directory
1900760c2415Smrg   auto root_path = parser.root_path();
1901760c2415Smrg   if (root_path.first.valid())
1902760c2415Smrg     {
1903760c2415Smrg       *next++ = root_path.first;
1904760c2415Smrg       if (root_path.second.valid())
1905760c2415Smrg 	*next++ = root_path.second;
1906760c2415Smrg     }
1907760c2415Smrg 
1908760c2415Smrg   auto cmpt = parser.next();
1909760c2415Smrg   while (cmpt.valid())
1910760c2415Smrg     {
1911760c2415Smrg       do
1912760c2415Smrg 	{
1913760c2415Smrg 	  *next++ = cmpt;
1914760c2415Smrg 	  cmpt = parser.next();
1915760c2415Smrg 	}
1916760c2415Smrg       while (cmpt.valid() && next != buf.end());
1917760c2415Smrg 
1918760c2415Smrg       if (next == buf.end())
1919760c2415Smrg 	{
1920760c2415Smrg 	  _M_cmpts.type(_Type::_Multi);
1921760c2415Smrg 	  _M_cmpts.reserve(_M_cmpts.size() + buf.size());
1922760c2415Smrg 	  auto output = _M_cmpts._M_impl->end();
19230bfacb9bSmrg 	  for (const auto& c : buf)
1924760c2415Smrg 	    {
19250bfacb9bSmrg 	      ::new(output++) _Cmpt(c.str, c.type, parser.offset(c));
1926760c2415Smrg 	      ++_M_cmpts._M_impl->_M_size;
1927760c2415Smrg 	    }
1928760c2415Smrg 	  next = buf.begin();
1929760c2415Smrg 	}
1930760c2415Smrg     }
1931760c2415Smrg 
1932760c2415Smrg   if (auto n = next - buf.begin())
1933760c2415Smrg     {
1934760c2415Smrg       if (n == 1 && _M_cmpts.empty())
1935760c2415Smrg 	{
1936760c2415Smrg 	  _M_cmpts.type(buf.front().type);
1937760c2415Smrg 	  return;
1938760c2415Smrg 	}
1939760c2415Smrg 
1940760c2415Smrg       _M_cmpts.type(_Type::_Multi);
1941760c2415Smrg       _M_cmpts.reserve(_M_cmpts.size() + n, true);
1942760c2415Smrg       auto output = _M_cmpts._M_impl->end();
1943760c2415Smrg       for (int i = 0; i < n; ++i)
1944760c2415Smrg 	{
19450bfacb9bSmrg 	  const auto& c = buf[i];
19460bfacb9bSmrg 	  ::new(output++) _Cmpt(c.str, c.type, parser.offset(c));
1947760c2415Smrg 	  ++_M_cmpts._M_impl->_M_size;
1948760c2415Smrg 	}
1949760c2415Smrg     }
1950760c2415Smrg }
1951760c2415Smrg 
1952760c2415Smrg path::string_type
_S_convert_loc(const char * __first,const char * __last,const std::locale & __loc)1953760c2415Smrg path::_S_convert_loc(const char* __first, const char* __last,
1954760c2415Smrg 		     const std::locale& __loc)
1955760c2415Smrg {
1956760c2415Smrg #if _GLIBCXX_USE_WCHAR_T
1957760c2415Smrg   auto& __cvt = std::use_facet<codecvt<wchar_t, char, mbstate_t>>(__loc);
1958760c2415Smrg   basic_string<wchar_t> __ws;
1959760c2415Smrg   if (!__str_codecvt_in_all(__first, __last, __ws, __cvt))
1960760c2415Smrg     _GLIBCXX_THROW_OR_ABORT(filesystem_error(
1961760c2415Smrg 	  "Cannot convert character sequence",
1962760c2415Smrg 	  std::make_error_code(errc::illegal_byte_sequence)));
1963760c2415Smrg #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1964760c2415Smrg   return __ws;
1965760c2415Smrg #else
1966760c2415Smrg   return _Cvt<wchar_t>::_S_convert(__ws.data(), __ws.data() + __ws.size());
1967760c2415Smrg #endif
1968760c2415Smrg #else
1969760c2415Smrg   return {__first, __last};
1970760c2415Smrg #endif
1971760c2415Smrg }
1972760c2415Smrg 
1973760c2415Smrg std::size_t
hash_value(const path & p)1974760c2415Smrg fs::hash_value(const path& p) noexcept
1975760c2415Smrg {
1976760c2415Smrg   // [path.non-member]
1977760c2415Smrg   // "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)."
1978760c2415Smrg   // Equality works as if by traversing the range [begin(), end()), meaning
1979760c2415Smrg   // e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname
1980760c2415Smrg   // but need to iterate over individual elements. Use the hash_combine from
1981760c2415Smrg   // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
1982760c2415Smrg   size_t seed = 0;
1983760c2415Smrg   for (const auto& x : p)
1984760c2415Smrg     {
1985760c2415Smrg       seed ^= std::hash<path::string_type>()(x.native()) + 0x9e3779b9
1986760c2415Smrg 	+ (seed<<6) + (seed>>2);
1987760c2415Smrg     }
1988760c2415Smrg   return seed;
1989760c2415Smrg }
1990760c2415Smrg 
1991760c2415Smrg struct fs::filesystem_error::_Impl
1992760c2415Smrg {
_Implfs::filesystem_error::_Impl1993760c2415Smrg   _Impl(string_view what_arg, const path& p1, const path& p2)
1994760c2415Smrg   : path1(p1), path2(p2), what(make_what(what_arg, &p1, &p2))
1995760c2415Smrg   { }
1996760c2415Smrg 
_Implfs::filesystem_error::_Impl1997760c2415Smrg   _Impl(string_view what_arg, const path& p1)
1998760c2415Smrg   : path1(p1), path2(), what(make_what(what_arg, &p1, nullptr))
1999760c2415Smrg   { }
2000760c2415Smrg 
_Implfs::filesystem_error::_Impl2001760c2415Smrg   _Impl(string_view what_arg)
2002760c2415Smrg   : what(make_what(what_arg, nullptr, nullptr))
2003760c2415Smrg   { }
2004760c2415Smrg 
2005760c2415Smrg   static std::string
make_whatfs::filesystem_error::_Impl2006760c2415Smrg   make_what(string_view s, const path* p1, const path* p2)
2007760c2415Smrg   {
2008760c2415Smrg     const std::string pstr1 = p1 ? p1->u8string() : std::string{};
2009760c2415Smrg     const std::string pstr2 = p2 ? p2->u8string() : std::string{};
2010760c2415Smrg     const size_t len = 18 + s.length()
2011760c2415Smrg       + (pstr1.length() ? pstr1.length() + 3 : 0)
2012760c2415Smrg       + (pstr2.length() ? pstr2.length() + 3 : 0);
2013760c2415Smrg     std::string w;
2014760c2415Smrg     w.reserve(len);
2015760c2415Smrg     w = "filesystem error: ";
2016760c2415Smrg     w += s;
2017760c2415Smrg     if (p1)
2018760c2415Smrg       {
2019760c2415Smrg 	w += " [";
2020760c2415Smrg 	w += pstr1;
2021760c2415Smrg 	w += ']';
2022760c2415Smrg 	if (p2)
2023760c2415Smrg 	  {
2024760c2415Smrg 	    w += " [";
2025760c2415Smrg 	    w += pstr2;
2026760c2415Smrg 	    w += ']';
2027760c2415Smrg 	  }
2028760c2415Smrg       }
2029760c2415Smrg     return w;
2030760c2415Smrg   }
2031760c2415Smrg 
2032760c2415Smrg   path path1;
2033760c2415Smrg   path path2;
2034760c2415Smrg   std::string what;
2035760c2415Smrg };
2036760c2415Smrg 
2037760c2415Smrg template class std::__shared_ptr<const fs::filesystem_error::_Impl>;
2038760c2415Smrg 
2039760c2415Smrg fs::filesystem_error::
filesystem_error(const string & what_arg,error_code ec)2040760c2415Smrg filesystem_error(const string& what_arg, error_code ec)
2041760c2415Smrg : system_error(ec, what_arg),
2042760c2415Smrg   _M_impl(std::__make_shared<_Impl>(system_error::what()))
2043760c2415Smrg { }
2044760c2415Smrg 
2045760c2415Smrg fs::filesystem_error::
filesystem_error(const string & what_arg,const path & p1,error_code ec)2046760c2415Smrg filesystem_error(const string& what_arg, const path& p1, error_code ec)
2047760c2415Smrg : system_error(ec, what_arg),
2048760c2415Smrg   _M_impl(std::__make_shared<_Impl>(system_error::what(), p1))
2049760c2415Smrg { }
2050760c2415Smrg 
2051760c2415Smrg fs::filesystem_error::
filesystem_error(const string & what_arg,const path & p1,const path & p2,error_code ec)2052760c2415Smrg filesystem_error(const string& what_arg, const path& p1, const path& p2,
2053760c2415Smrg 		 error_code ec)
2054760c2415Smrg : system_error(ec, what_arg),
2055760c2415Smrg   _M_impl(std::__make_shared<_Impl>(system_error::what(), p1, p2))
2056760c2415Smrg { }
2057760c2415Smrg 
2058760c2415Smrg fs::filesystem_error::~filesystem_error() = default;
2059760c2415Smrg 
2060760c2415Smrg const fs::path&
path1() const2061760c2415Smrg fs::filesystem_error::path1() const noexcept
2062760c2415Smrg { return _M_impl->path1; }
2063760c2415Smrg 
2064760c2415Smrg const fs::path&
path2() const2065760c2415Smrg fs::filesystem_error::path2() const noexcept
2066760c2415Smrg { return _M_impl->path2; }
2067760c2415Smrg 
2068760c2415Smrg const char*
what() const2069760c2415Smrg fs::filesystem_error::what() const noexcept
2070760c2415Smrg { return _M_impl->what.c_str(); }
2071