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