1 // Class filesystem::path -*- C++ -*-
2 
3 // Copyright (C) 2014-2018 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library.  This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10 
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19 
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23 // <http://www.gnu.org/licenses/>.
24 
25 /** @file include/bits/fs_path.h
26  *  This is an internal header file, included by other library headers.
27  *  Do not attempt to use it directly. @headername{filesystem}
28  */
29 
30 #ifndef _GLIBCXX_FS_PATH_H
31 #define _GLIBCXX_FS_PATH_H 1
32 
33 #if __cplusplus >= 201703L
34 
35 #include <utility>
36 #include <type_traits>
37 #include <vector>
38 #include <locale>
39 #include <iosfwd>
40 #include <codecvt>
41 #include <string_view>
42 #include <system_error>
43 #include <bits/stl_algobase.h>
44 #include <bits/quoted_string.h>
45 #include <bits/locale_conv.h>
46 
47 #if defined(_WIN32) && !defined(__CYGWIN__)
48 # define _GLIBCXX_FILESYSTEM_IS_WINDOWS 1
49 # include <algorithm>
50 #endif
51 
52 namespace std _GLIBCXX_VISIBILITY(default)
53 {
54 _GLIBCXX_BEGIN_NAMESPACE_VERSION
55 
56 namespace filesystem
57 {
58 _GLIBCXX_BEGIN_NAMESPACE_CXX11
59 
60   /**
61    * @ingroup filesystem
62    * @{
63    */
64 
65   /// A filesystem path.
66   class path
67   {
68     template<typename _CharT>
69       struct __is_encoded_char : std::false_type { };
70 
71     template<typename _Iter,
72 	     typename _Iter_traits = std::iterator_traits<_Iter>>
73       using __is_path_iter_src
74 	= __and_<__is_encoded_char<typename _Iter_traits::value_type>,
75 		 std::is_base_of<std::input_iterator_tag,
76 				 typename _Iter_traits::iterator_category>>;
77 
78     template<typename _Iter>
79       static __is_path_iter_src<_Iter>
80       __is_path_src(_Iter, int);
81 
82     template<typename _CharT, typename _Traits, typename _Alloc>
83       static __is_encoded_char<_CharT>
84       __is_path_src(const basic_string<_CharT, _Traits, _Alloc>&, int);
85 
86     template<typename _CharT, typename _Traits>
87       static __is_encoded_char<_CharT>
88       __is_path_src(const basic_string_view<_CharT, _Traits>&, int);
89 
90     template<typename _Unknown>
91       static std::false_type
92       __is_path_src(const _Unknown&, ...);
93 
94     template<typename _Tp1, typename _Tp2>
95       struct __constructible_from;
96 
97     template<typename _Iter>
98       struct __constructible_from<_Iter, _Iter>
99       : __is_path_iter_src<_Iter>
100       { };
101 
102     template<typename _Source>
103       struct __constructible_from<_Source, void>
104       : decltype(__is_path_src(std::declval<_Source>(), 0))
105       { };
106 
107     template<typename _Tp1, typename _Tp2 = void>
108       using _Path = typename
109 	std::enable_if<__and_<__not_<is_same<_Tp1, path>>,
110 			      __constructible_from<_Tp1, _Tp2>>::value,
111 		       path>::type;
112 
113     template<typename _Source>
114       static _Source
115       _S_range_begin(_Source __begin) { return __begin; }
116 
117     struct __null_terminated { };
118 
119     template<typename _Source>
120       static __null_terminated
121       _S_range_end(_Source) { return {}; }
122 
123     template<typename _CharT, typename _Traits, typename _Alloc>
124       static const _CharT*
125       _S_range_begin(const basic_string<_CharT, _Traits, _Alloc>& __str)
126       { return __str.data(); }
127 
128     template<typename _CharT, typename _Traits, typename _Alloc>
129       static const _CharT*
130       _S_range_end(const basic_string<_CharT, _Traits, _Alloc>& __str)
131       { return __str.data() + __str.size(); }
132 
133     template<typename _CharT, typename _Traits>
134       static const _CharT*
135       _S_range_begin(const basic_string_view<_CharT, _Traits>& __str)
136       { return __str.data(); }
137 
138     template<typename _CharT, typename _Traits>
139       static const _CharT*
140       _S_range_end(const basic_string_view<_CharT, _Traits>& __str)
141       { return __str.data() + __str.size(); }
142 
143     template<typename _Tp,
144 	     typename _Iter = decltype(_S_range_begin(std::declval<_Tp>())),
145 	     typename _Val = typename std::iterator_traits<_Iter>::value_type>
146       using __value_type_is_char
147 	= typename std::enable_if<std::is_same<_Val, char>::value>::type;
148 
149   public:
150 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
151     typedef wchar_t				value_type;
152     static constexpr value_type			preferred_separator = L'\\';
153 #else
154     typedef char				value_type;
155     static constexpr value_type			preferred_separator = '/';
156 #endif
157     typedef std::basic_string<value_type>	string_type;
158 
159     enum format { native_format, generic_format, auto_format };
160 
161     // constructors and destructor
162 
163     path() noexcept { }
164 
165     path(const path& __p) = default;
166 
167     path(path&& __p) noexcept
168     : _M_pathname(std::move(__p._M_pathname)), _M_type(__p._M_type)
169     {
170       _M_split_cmpts();
171       __p.clear();
172     }
173 
174     path(string_type&& __source, format = auto_format)
175     : _M_pathname(std::move(__source))
176     { _M_split_cmpts(); }
177 
178     template<typename _Source,
179 	     typename _Require = _Path<_Source>>
180       path(_Source const& __source, format = auto_format)
181       : _M_pathname(_S_convert(_S_range_begin(__source),
182 			       _S_range_end(__source)))
183       { _M_split_cmpts(); }
184 
185     template<typename _InputIterator,
186 	     typename _Require = _Path<_InputIterator, _InputIterator>>
187       path(_InputIterator __first, _InputIterator __last, format = auto_format)
188       : _M_pathname(_S_convert(__first, __last))
189       { _M_split_cmpts(); }
190 
191     template<typename _Source,
192 	     typename _Require = _Path<_Source>,
193 	     typename _Require2 = __value_type_is_char<_Source>>
194       path(_Source const& __source, const locale& __loc, format = auto_format)
195       : _M_pathname(_S_convert_loc(_S_range_begin(__source),
196 				   _S_range_end(__source), __loc))
197       { _M_split_cmpts(); }
198 
199     template<typename _InputIterator,
200 	     typename _Require = _Path<_InputIterator, _InputIterator>,
201 	     typename _Require2 = __value_type_is_char<_InputIterator>>
202       path(_InputIterator __first, _InputIterator __last, const locale& __loc,
203 	   format = auto_format)
204       : _M_pathname(_S_convert_loc(__first, __last, __loc))
205       { _M_split_cmpts(); }
206 
207     ~path() = default;
208 
209     // assignments
210 
211     path& operator=(const path& __p) = default;
212     path& operator=(path&& __p) noexcept;
213     path& operator=(string_type&& __source);
214     path& assign(string_type&& __source);
215 
216     template<typename _Source>
217       _Path<_Source>&
218       operator=(_Source const& __source)
219       { return *this = path(__source); }
220 
221     template<typename _Source>
222       _Path<_Source>&
223       assign(_Source const& __source)
224       { return *this = path(__source); }
225 
226     template<typename _InputIterator>
227       _Path<_InputIterator, _InputIterator>&
228       assign(_InputIterator __first, _InputIterator __last)
229       { return *this = path(__first, __last); }
230 
231     // appends
232 
233     path& operator/=(const path& __p)
234     {
235 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
236       if (__p.is_absolute()
237 	  || (__p.has_root_name() && __p.root_name() != root_name()))
238 	operator=(__p);
239       else
240 	{
241 	  string_type __pathname;
242 	  if (__p.has_root_directory())
243 	    __pathname = root_name().native();
244 	  else if (has_filename() || (!has_root_directory() && is_absolute()))
245 	    __pathname = _M_pathname + preferred_separator;
246 	  __pathname += __p.relative_path().native(); // XXX is this right?
247 	  _M_pathname.swap(__pathname);
248 	  _M_split_cmpts();
249 	}
250 #else
251       // Much simpler, as any path with root-name or root-dir is absolute.
252       if (__p.is_absolute())
253 	operator=(__p);
254       else
255 	{
256 	  if (has_filename() || (_M_type == _Type::_Root_name))
257 	    _M_pathname += preferred_separator;
258 	  _M_pathname += __p.native();
259 	  _M_split_cmpts();
260 	}
261 #endif
262       return *this;
263     }
264 
265     template <class _Source>
266       _Path<_Source>&
267       operator/=(_Source const& __source)
268       { return append(__source); }
269 
270     template<typename _Source>
271       _Path<_Source>&
272       append(_Source const& __source)
273       {
274 	return _M_append(_S_convert(_S_range_begin(__source),
275 				    _S_range_end(__source)));
276       }
277 
278     template<typename _InputIterator>
279       _Path<_InputIterator, _InputIterator>&
280       append(_InputIterator __first, _InputIterator __last)
281       { return _M_append(_S_convert(__first, __last)); }
282 
283     // concatenation
284 
285     path& operator+=(const path& __x);
286     path& operator+=(const string_type& __x);
287     path& operator+=(const value_type* __x);
288     path& operator+=(value_type __x);
289     path& operator+=(basic_string_view<value_type> __x);
290 
291     template<typename _Source>
292       _Path<_Source>&
293       operator+=(_Source const& __x) { return concat(__x); }
294 
295     template<typename _CharT>
296       _Path<_CharT*, _CharT*>&
297       operator+=(_CharT __x);
298 
299     template<typename _Source>
300       _Path<_Source>&
301       concat(_Source const& __x)
302       { return *this += _S_convert(_S_range_begin(__x), _S_range_end(__x)); }
303 
304     template<typename _InputIterator>
305       _Path<_InputIterator, _InputIterator>&
306       concat(_InputIterator __first, _InputIterator __last)
307       { return *this += _S_convert(__first, __last); }
308 
309     // modifiers
310 
311     void clear() noexcept { _M_pathname.clear(); _M_split_cmpts(); }
312 
313     path& make_preferred();
314     path& remove_filename();
315     path& replace_filename(const path& __replacement);
316     path& replace_extension(const path& __replacement = path());
317 
318     void swap(path& __rhs) noexcept;
319 
320     // native format observers
321 
322     const string_type&  native() const noexcept { return _M_pathname; }
323     const value_type*   c_str() const noexcept { return _M_pathname.c_str(); }
324     operator string_type() const { return _M_pathname; }
325 
326     template<typename _CharT, typename _Traits = std::char_traits<_CharT>,
327 	     typename _Allocator = std::allocator<_CharT>>
328       std::basic_string<_CharT, _Traits, _Allocator>
329       string(const _Allocator& __a = _Allocator()) const;
330 
331     std::string    string() const;
332 #if _GLIBCXX_USE_WCHAR_T
333     std::wstring   wstring() const;
334 #endif
335     std::string    u8string() const;
336     std::u16string u16string() const;
337     std::u32string u32string() const;
338 
339     // generic format observers
340     template<typename _CharT, typename _Traits = std::char_traits<_CharT>,
341 	     typename _Allocator = std::allocator<_CharT>>
342       std::basic_string<_CharT, _Traits, _Allocator>
343       generic_string(const _Allocator& __a = _Allocator()) const;
344 
345     std::string    generic_string() const;
346 #if _GLIBCXX_USE_WCHAR_T
347     std::wstring   generic_wstring() const;
348 #endif
349     std::string    generic_u8string() const;
350     std::u16string generic_u16string() const;
351     std::u32string generic_u32string() const;
352 
353     // compare
354 
355     int compare(const path& __p) const noexcept;
356     int compare(const string_type& __s) const;
357     int compare(const value_type* __s) const;
358     int compare(const basic_string_view<value_type> __s) const;
359 
360     // decomposition
361 
362     path root_name() const;
363     path root_directory() const;
364     path root_path() const;
365     path relative_path() const;
366     path parent_path() const;
367     path filename() const;
368     path stem() const;
369     path extension() const;
370 
371     // query
372 
373     [[nodiscard]] bool empty() const noexcept { return _M_pathname.empty(); }
374     bool has_root_name() const;
375     bool has_root_directory() const;
376     bool has_root_path() const;
377     bool has_relative_path() const;
378     bool has_parent_path() const;
379     bool has_filename() const;
380     bool has_stem() const;
381     bool has_extension() const;
382     bool is_absolute() const;
383     bool is_relative() const { return !is_absolute(); }
384 
385     // generation
386     path lexically_normal() const;
387     path lexically_relative(const path& base) const;
388     path lexically_proximate(const path& base) const;
389 
390     // iterators
391     class iterator;
392     typedef iterator const_iterator;
393 
394     iterator begin() const;
395     iterator end() const;
396 
397   private:
398     enum class _Type : unsigned char {
399 	_Multi, _Root_name, _Root_dir, _Filename
400     };
401 
402     path(string_type __str, _Type __type) : _M_pathname(__str), _M_type(__type)
403     {
404       __glibcxx_assert(_M_type != _Type::_Multi);
405     }
406 
407     enum class _Split { _Stem, _Extension };
408 
409     path& _M_append(string_type&& __str)
410     {
411 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
412       operator/=(path(std::move(__str)));
413 #else
414       if (!_M_pathname.empty() && !_S_is_dir_sep(_M_pathname.back())
415 	  && (__str.empty() || !_S_is_dir_sep(__str.front())))
416 	_M_pathname += preferred_separator;
417       _M_pathname += __str;
418       _M_split_cmpts();
419 #endif
420       return *this;
421     }
422 
423     pair<const string_type*, size_t> _M_find_extension() const;
424 
425     template<typename _CharT>
426       struct _Cvt;
427 
428     static string_type
429     _S_convert(value_type* __src, __null_terminated)
430     { return string_type(__src); }
431 
432     static string_type
433     _S_convert(const value_type* __src, __null_terminated)
434     { return string_type(__src); }
435 
436     template<typename _Iter>
437       static string_type
438       _S_convert(_Iter __first, _Iter __last)
439       {
440 	using __value_type = typename std::iterator_traits<_Iter>::value_type;
441 	return _Cvt<typename remove_cv<__value_type>::type>::
442 	  _S_convert(__first, __last);
443       }
444 
445     template<typename _InputIterator>
446       static string_type
447       _S_convert(_InputIterator __src, __null_terminated)
448       {
449 	using _Tp = typename std::iterator_traits<_InputIterator>::value_type;
450 	std::basic_string<typename remove_cv<_Tp>::type> __tmp;
451 	for (; *__src != _Tp{}; ++__src)
452 	  __tmp.push_back(*__src);
453 	return _S_convert(__tmp.c_str(), __tmp.c_str() + __tmp.size());
454       }
455 
456     static string_type
457     _S_convert_loc(const char* __first, const char* __last,
458 		   const std::locale& __loc);
459 
460     template<typename _Iter>
461       static string_type
462       _S_convert_loc(_Iter __first, _Iter __last, const std::locale& __loc)
463       {
464 	const std::string __str(__first, __last);
465 	return _S_convert_loc(__str.data(), __str.data()+__str.size(), __loc);
466       }
467 
468     template<typename _InputIterator>
469       static string_type
470       _S_convert_loc(_InputIterator __src, __null_terminated,
471 		     const std::locale& __loc)
472       {
473 	std::string __tmp;
474 	while (*__src != '\0')
475 	  __tmp.push_back(*__src++);
476 	return _S_convert_loc(__tmp.data(), __tmp.data()+__tmp.size(), __loc);
477       }
478 
479     template<typename _CharT, typename _Traits, typename _Allocator>
480       static basic_string<_CharT, _Traits, _Allocator>
481       _S_str_convert(const string_type&, const _Allocator& __a);
482 
483     bool _S_is_dir_sep(value_type __ch)
484     {
485 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
486       return __ch == L'/' || __ch == preferred_separator;
487 #else
488       return __ch == '/';
489 #endif
490     }
491 
492     void _M_split_cmpts();
493     void _M_trim();
494     void _M_add_root_name(size_t __n);
495     void _M_add_root_dir(size_t __pos);
496     void _M_add_filename(size_t __pos, size_t __n);
497 
498     string_type _M_pathname;
499 
500     struct _Cmpt;
501     using _List = _GLIBCXX_STD_C::vector<_Cmpt>;
502     _List _M_cmpts; // empty unless _M_type == _Type::_Multi
503     _Type _M_type = _Type::_Multi;
504   };
505 
506   template<>
507     struct path::__is_encoded_char<char> : std::true_type
508     { using value_type = char; };
509 
510   template<>
511     struct path::__is_encoded_char<wchar_t> : std::true_type
512     { using value_type = wchar_t; };
513 
514   template<>
515     struct path::__is_encoded_char<char16_t> : std::true_type
516     { using value_type = char16_t; };
517 
518   template<>
519     struct path::__is_encoded_char<char32_t> : std::true_type
520     { using value_type = char32_t; };
521 
522   template<typename _Tp>
523     struct path::__is_encoded_char<const _Tp> : __is_encoded_char<_Tp> { };
524 
525   inline void swap(path& __lhs, path& __rhs) noexcept { __lhs.swap(__rhs); }
526 
527   size_t hash_value(const path& __p) noexcept;
528 
529   /// Compare paths
530   inline bool operator<(const path& __lhs, const path& __rhs) noexcept
531   { return __lhs.compare(__rhs) < 0; }
532 
533   /// Compare paths
534   inline bool operator<=(const path& __lhs, const path& __rhs) noexcept
535   { return !(__rhs < __lhs); }
536 
537   /// Compare paths
538   inline bool operator>(const path& __lhs, const path& __rhs) noexcept
539   { return __rhs < __lhs; }
540 
541   /// Compare paths
542   inline bool operator>=(const path& __lhs, const path& __rhs) noexcept
543   { return !(__lhs < __rhs); }
544 
545   /// Compare paths
546   inline bool operator==(const path& __lhs, const path& __rhs) noexcept
547   { return __lhs.compare(__rhs) == 0; }
548 
549   /// Compare paths
550   inline bool operator!=(const path& __lhs, const path& __rhs) noexcept
551   { return !(__lhs == __rhs); }
552 
553   /// Append one path to another
554   inline path operator/(const path& __lhs, const path& __rhs)
555   { return path(__lhs) /= __rhs; }
556 
557   /// Write a path to a stream
558   template<typename _CharT, typename _Traits>
559     basic_ostream<_CharT, _Traits>&
560     operator<<(basic_ostream<_CharT, _Traits>& __os, const path& __p)
561     {
562       auto __tmp = __p.string<_CharT, _Traits>();
563       using __quoted_string
564 	= std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>;
565       __os << __quoted_string{__tmp, '"', '\\'};
566       return __os;
567     }
568 
569   /// Read a path from a stream
570   template<typename _CharT, typename _Traits>
571     basic_istream<_CharT, _Traits>&
572     operator>>(basic_istream<_CharT, _Traits>& __is, path& __p)
573     {
574       basic_string<_CharT, _Traits> __tmp;
575       using __quoted_string
576 	= std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>;
577       if (__is >> __quoted_string{ __tmp, '"', '\\' })
578 	__p = std::move(__tmp);
579       return __is;
580     }
581 
582   template<typename _Source>
583     inline auto
584     u8path(const _Source& __source)
585     -> decltype(filesystem::path(__source, std::locale::classic()))
586     {
587 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
588       const std::string __u8str{__source};
589       return std::filesystem::u8path(__u8str.begin(), __u8str.end());
590 #else
591       return path{ __source };
592 #endif
593     }
594 
595   template<typename _InputIterator>
596     inline auto
597     u8path(_InputIterator __first, _InputIterator __last)
598     -> decltype(filesystem::path(__first, __last, std::locale::classic()))
599     {
600 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
601       codecvt_utf8<value_type> __cvt;
602       string_type __tmp;
603       if (__str_codecvt_in(__first, __last, __tmp, __cvt))
604 	return path{ __tmp };
605       else
606 	return {};
607 #else
608       return path{ __first, __last };
609 #endif
610     }
611 
612   class filesystem_error : public std::system_error
613   {
614   public:
615     filesystem_error(const string& __what_arg, error_code __ec)
616     : system_error(__ec, __what_arg) { }
617 
618     filesystem_error(const string& __what_arg, const path& __p1,
619 		     error_code __ec)
620     : system_error(__ec, __what_arg), _M_path1(__p1) { }
621 
622     filesystem_error(const string& __what_arg, const path& __p1,
623 		     const path& __p2, error_code __ec)
624     : system_error(__ec, __what_arg), _M_path1(__p1), _M_path2(__p2)
625     { }
626 
627     ~filesystem_error();
628 
629     const path& path1() const noexcept { return _M_path1; }
630     const path& path2() const noexcept { return _M_path2; }
631     const char* what() const noexcept { return _M_what.c_str(); }
632 
633   private:
634     std::string _M_gen_what();
635 
636     path _M_path1;
637     path _M_path2;
638     std::string _M_what = _M_gen_what();
639   };
640 
641   struct path::_Cmpt : path
642   {
643     _Cmpt(string_type __s, _Type __t, size_t __pos)
644       : path(std::move(__s), __t), _M_pos(__pos) { }
645 
646     _Cmpt() : _M_pos(-1) { }
647 
648     size_t _M_pos;
649   };
650 
651   // specialize _Cvt for degenerate 'noconv' case
652   template<>
653     struct path::_Cvt<path::value_type>
654     {
655       template<typename _Iter>
656 	static string_type
657 	_S_convert(_Iter __first, _Iter __last)
658 	{ return string_type{__first, __last}; }
659     };
660 
661   template<typename _CharT>
662     struct path::_Cvt
663     {
664 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
665       static string_type
666       _S_wconvert(const char* __f, const char* __l, true_type)
667       {
668 	using _Cvt = std::codecvt<wchar_t, char, mbstate_t>;
669 	const auto& __cvt = std::use_facet<_Cvt>(std::locale{});
670 	std::wstring __wstr;
671 	if (__str_codecvt_in(__f, __l, __wstr, __cvt))
672 	    return __wstr;
673 	_GLIBCXX_THROW_OR_ABORT(filesystem_error(
674 	      "Cannot convert character sequence",
675 	      std::make_error_code(errc::illegal_byte_sequence)));
676       }
677 
678       static string_type
679       _S_wconvert(const _CharT* __f, const _CharT* __l, false_type)
680       {
681 	std::codecvt_utf8<_CharT> __cvt;
682 	std::string __str;
683 	if (__str_codecvt_out(__f, __l, __str, __cvt))
684 	  {
685 	    const char* __f2 = __str.data();
686 	    const char* __l2 = __f2 + __str.size();
687 	    std::codecvt_utf8<wchar_t> __wcvt;
688 	    std::wstring __wstr;
689 	    if (__str_codecvt_in(__f2, __l2, __wstr, __wcvt))
690 	      return __wstr;
691 	  }
692 	_GLIBCXX_THROW_OR_ABORT(filesystem_error(
693 	      "Cannot convert character sequence",
694 	      std::make_error_code(errc::illegal_byte_sequence)));
695       }
696 
697       static string_type
698       _S_convert(const _CharT* __f, const _CharT* __l)
699       {
700 	return _S_wconvert(__f, __l, is_same<_CharT, char>{});
701       }
702 #else
703       static string_type
704       _S_convert(const _CharT* __f, const _CharT* __l)
705       {
706 	std::codecvt_utf8<_CharT> __cvt;
707 	std::string __str;
708 	if (__str_codecvt_out(__f, __l, __str, __cvt))
709 	  return __str;
710 	_GLIBCXX_THROW_OR_ABORT(filesystem_error(
711 	      "Cannot convert character sequence",
712 	      std::make_error_code(errc::illegal_byte_sequence)));
713       }
714 #endif
715 
716       static string_type
717       _S_convert(_CharT* __f, _CharT* __l)
718       {
719 	return _S_convert(const_cast<const _CharT*>(__f),
720 			  const_cast<const _CharT*>(__l));
721       }
722 
723       template<typename _Iter>
724 	static string_type
725 	_S_convert(_Iter __first, _Iter __last)
726 	{
727 	  const std::basic_string<_CharT> __str(__first, __last);
728 	  return _S_convert(__str.data(), __str.data() + __str.size());
729 	}
730 
731       template<typename _Iter, typename _Cont>
732 	static string_type
733 	_S_convert(__gnu_cxx::__normal_iterator<_Iter, _Cont> __first,
734 		  __gnu_cxx::__normal_iterator<_Iter, _Cont> __last)
735 	{ return _S_convert(__first.base(), __last.base()); }
736     };
737 
738   /// An iterator for the components of a path
739   class path::iterator
740   {
741   public:
742     using difference_type	= std::ptrdiff_t;
743     using value_type		= path;
744     using reference		= const path&;
745     using pointer		= const path*;
746     using iterator_category	= std::bidirectional_iterator_tag;
747 
748     iterator() : _M_path(nullptr), _M_cur(), _M_at_end() { }
749 
750     iterator(const iterator&) = default;
751     iterator& operator=(const iterator&) = default;
752 
753     reference operator*() const;
754     pointer   operator->() const { return std::__addressof(**this); }
755 
756     iterator& operator++();
757     iterator  operator++(int) { auto __tmp = *this; ++*this; return __tmp; }
758 
759     iterator& operator--();
760     iterator  operator--(int) { auto __tmp = *this; --*this; return __tmp; }
761 
762     friend bool operator==(const iterator& __lhs, const iterator& __rhs)
763     { return __lhs._M_equals(__rhs); }
764 
765     friend bool operator!=(const iterator& __lhs, const iterator& __rhs)
766     { return !__lhs._M_equals(__rhs); }
767 
768   private:
769     friend class path;
770 
771     iterator(const path* __path, path::_List::const_iterator __iter)
772     : _M_path(__path), _M_cur(__iter), _M_at_end()
773     { }
774 
775     iterator(const path* __path, bool __at_end)
776     : _M_path(__path), _M_cur(), _M_at_end(__at_end)
777     { }
778 
779     bool _M_equals(iterator) const;
780 
781     const path* 		_M_path;
782     path::_List::const_iterator _M_cur;
783     bool			_M_at_end;  // only used when type != _Multi
784   };
785 
786 
787   inline path&
788   path::operator=(path&& __p) noexcept
789   {
790     _M_pathname = std::move(__p._M_pathname);
791     _M_cmpts = std::move(__p._M_cmpts);
792     _M_type = __p._M_type;
793     __p.clear();
794     return *this;
795   }
796 
797   inline path&
798   path::operator=(string_type&& __source)
799   { return *this = path(std::move(__source)); }
800 
801   inline path&
802   path::assign(string_type&& __source)
803   { return *this = path(std::move(__source)); }
804 
805   inline path&
806   path::operator+=(const path& __p)
807   {
808     return operator+=(__p.native());
809   }
810 
811   inline path&
812   path::operator+=(const string_type& __x)
813   {
814     _M_pathname += __x;
815     _M_split_cmpts();
816     return *this;
817   }
818 
819   inline path&
820   path::operator+=(const value_type* __x)
821   {
822     _M_pathname += __x;
823     _M_split_cmpts();
824     return *this;
825   }
826 
827   inline path&
828   path::operator+=(value_type __x)
829   {
830     _M_pathname += __x;
831     _M_split_cmpts();
832     return *this;
833   }
834 
835   inline path&
836   path::operator+=(basic_string_view<value_type> __x)
837   {
838     _M_pathname.append(__x.data(), __x.size());
839     _M_split_cmpts();
840     return *this;
841   }
842 
843   template<typename _CharT>
844     inline path::_Path<_CharT*, _CharT*>&
845     path::operator+=(_CharT __x)
846     {
847       auto* __addr = std::__addressof(__x);
848       return concat(__addr, __addr + 1);
849     }
850 
851   inline path&
852   path::make_preferred()
853   {
854 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
855     std::replace(_M_pathname.begin(), _M_pathname.end(), L'/',
856 		 preferred_separator);
857 #endif
858     return *this;
859   }
860 
861   inline void path::swap(path& __rhs) noexcept
862   {
863     _M_pathname.swap(__rhs._M_pathname);
864     _M_cmpts.swap(__rhs._M_cmpts);
865     std::swap(_M_type, __rhs._M_type);
866   }
867 
868   template<typename _CharT, typename _Traits, typename _Allocator>
869     std::basic_string<_CharT, _Traits, _Allocator>
870     path::_S_str_convert(const string_type& __str, const _Allocator& __a)
871     {
872       if (__str.size() == 0)
873 	return std::basic_string<_CharT, _Traits, _Allocator>(__a);
874 
875       const value_type* __first = __str.data();
876       const value_type* __last = __first + __str.size();
877 
878 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
879       using _CharAlloc = __alloc_rebind<_Allocator, char>;
880       using _String = basic_string<char, char_traits<char>, _CharAlloc>;
881       using _WString = basic_string<_CharT, _Traits, _Allocator>;
882 
883       // use codecvt_utf8<wchar_t> to convert native string to UTF-8
884       codecvt_utf8<value_type> __cvt;
885       _String __u8str{_CharAlloc{__a}};
886       if (__str_codecvt_out(__first, __last, __u8str, __cvt))
887 	{
888 	  if constexpr (is_same_v<_CharT, char>)
889 	    return __u8str;
890 	  else
891 	    {
892 	      _WString __wstr;
893 	      // use codecvt_utf8<_CharT> to convert UTF-8 to wide string
894 	      codecvt_utf8<_CharT> __cvt;
895 	      const char* __f = __u8str.data();
896 	      const char* __l = __f + __u8str.size();
897 	      if (__str_codecvt_in(__f, __l, __wstr, __cvt))
898 		return __wstr;
899 	    }
900 	}
901 #else
902       codecvt_utf8<_CharT> __cvt;
903       basic_string<_CharT, _Traits, _Allocator> __wstr{__a};
904       if (__str_codecvt_in(__first, __last, __wstr, __cvt))
905 	return __wstr;
906 #endif
907       _GLIBCXX_THROW_OR_ABORT(filesystem_error(
908 	    "Cannot convert character sequence",
909 	    std::make_error_code(errc::illegal_byte_sequence)));
910     }
911 
912   template<typename _CharT, typename _Traits, typename _Allocator>
913     inline basic_string<_CharT, _Traits, _Allocator>
914     path::string(const _Allocator& __a) const
915     {
916       if constexpr (is_same_v<_CharT, value_type>)
917 #if _GLIBCXX_USE_CXX11_ABI
918 	return { _M_pathname, __a };
919 #else
920 	return { _M_pathname, string_type::size_type(0), __a };
921 #endif
922       else
923 	return _S_str_convert<_CharT, _Traits>(_M_pathname, __a);
924     }
925 
926   inline std::string
927   path::string() const { return string<char>(); }
928 
929 #if _GLIBCXX_USE_WCHAR_T
930   inline std::wstring
931   path::wstring() const { return string<wchar_t>(); }
932 #endif
933 
934   inline std::string
935   path::u8string() const
936   {
937 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
938     std::string __str;
939     // convert from native encoding to UTF-8
940     codecvt_utf8<value_type> __cvt;
941     const value_type* __first = _M_pathname.data();
942     const value_type* __last = __first + _M_pathname.size();
943     if (__str_codecvt_out(__first, __last, __str, __cvt))
944       return __str;
945     _GLIBCXX_THROW_OR_ABORT(filesystem_error(
946 	  "Cannot convert character sequence",
947 	  std::make_error_code(errc::illegal_byte_sequence)));
948 #else
949     return _M_pathname;
950 #endif
951   }
952 
953   inline std::u16string
954   path::u16string() const { return string<char16_t>(); }
955 
956   inline std::u32string
957   path::u32string() const { return string<char32_t>(); }
958 
959   template<typename _CharT, typename _Traits, typename _Allocator>
960     inline std::basic_string<_CharT, _Traits, _Allocator>
961     path::generic_string(const _Allocator& __a) const
962     {
963 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
964       const value_type __slash = L'/';
965 #else
966       const value_type __slash = '/';
967 #endif
968       string_type __str(__a);
969 
970       if (_M_type == _Type::_Root_dir)
971 	__str.assign(1, __slash);
972       else
973 	{
974 	  __str.reserve(_M_pathname.size());
975 	  bool __add_slash = false;
976 	  for (auto& __elem : *this)
977 	    {
978 	      if (__add_slash)
979 		__str += __slash;
980 	      __str += __elem._M_pathname;
981 	      __add_slash = __elem._M_type == _Type::_Filename;
982 	    }
983 	}
984 
985       if constexpr (is_same_v<_CharT, value_type>)
986 	return __str;
987       else
988 	return _S_str_convert<_CharT, _Traits>(__str, __a);
989     }
990 
991   inline std::string
992   path::generic_string() const
993   { return generic_string<char>(); }
994 
995 #if _GLIBCXX_USE_WCHAR_T
996   inline std::wstring
997   path::generic_wstring() const
998   { return generic_string<wchar_t>(); }
999 #endif
1000 
1001   inline std::string
1002   path::generic_u8string() const
1003   { return generic_string(); }
1004 
1005   inline std::u16string
1006   path::generic_u16string() const
1007   { return generic_string<char16_t>(); }
1008 
1009   inline std::u32string
1010   path::generic_u32string() const
1011   { return generic_string<char32_t>(); }
1012 
1013   inline int
1014   path::compare(const string_type& __s) const { return compare(path(__s)); }
1015 
1016   inline int
1017   path::compare(const value_type* __s) const { return compare(path(__s)); }
1018 
1019   inline int
1020   path::compare(basic_string_view<value_type> __s) const
1021   { return compare(path(__s)); }
1022 
1023   inline path
1024   path::filename() const
1025   {
1026     if (empty())
1027       return {};
1028     else if (_M_type == _Type::_Filename)
1029       return *this;
1030     else if (_M_type == _Type::_Multi)
1031       {
1032 	if (_M_pathname.back() == preferred_separator)
1033 	  return {};
1034 	auto& __last = *--end();
1035 	if (__last._M_type == _Type::_Filename)
1036 	  return __last;
1037       }
1038     return {};
1039   }
1040 
1041   inline path
1042   path::stem() const
1043   {
1044     auto ext = _M_find_extension();
1045     if (ext.first && ext.second != 0)
1046       return path{ext.first->substr(0, ext.second)};
1047     return {};
1048   }
1049 
1050   inline path
1051   path::extension() const
1052   {
1053     auto ext = _M_find_extension();
1054     if (ext.first && ext.second != string_type::npos)
1055       return path{ext.first->substr(ext.second)};
1056     return {};
1057   }
1058 
1059   inline bool
1060   path::has_stem() const
1061   {
1062     auto ext = _M_find_extension();
1063     return ext.first && ext.second != 0;
1064   }
1065 
1066   inline bool
1067   path::has_extension() const
1068   {
1069     auto ext = _M_find_extension();
1070     return ext.first && ext.second != string_type::npos;
1071   }
1072 
1073   inline bool
1074   path::is_absolute() const
1075   {
1076 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1077     return has_root_name();
1078 #else
1079     return has_root_directory();
1080 #endif
1081   }
1082 
1083   inline path::iterator
1084   path::begin() const
1085   {
1086     if (_M_type == _Type::_Multi)
1087       return iterator(this, _M_cmpts.begin());
1088     return iterator(this, false);
1089   }
1090 
1091   inline path::iterator
1092   path::end() const
1093   {
1094     if (_M_type == _Type::_Multi)
1095       return iterator(this, _M_cmpts.end());
1096     return iterator(this, true);
1097   }
1098 
1099   inline path::iterator&
1100   path::iterator::operator++()
1101   {
1102     __glibcxx_assert(_M_path != nullptr);
1103     if (_M_path->_M_type == _Type::_Multi)
1104       {
1105 	__glibcxx_assert(_M_cur != _M_path->_M_cmpts.end());
1106 	++_M_cur;
1107       }
1108     else
1109       {
1110 	__glibcxx_assert(!_M_at_end);
1111 	_M_at_end = true;
1112       }
1113     return *this;
1114   }
1115 
1116   inline path::iterator&
1117   path::iterator::operator--()
1118   {
1119     __glibcxx_assert(_M_path != nullptr);
1120     if (_M_path->_M_type == _Type::_Multi)
1121       {
1122 	__glibcxx_assert(_M_cur != _M_path->_M_cmpts.begin());
1123 	--_M_cur;
1124       }
1125     else
1126       {
1127 	__glibcxx_assert(_M_at_end);
1128 	_M_at_end = false;
1129       }
1130     return *this;
1131   }
1132 
1133   inline path::iterator::reference
1134   path::iterator::operator*() const
1135   {
1136     __glibcxx_assert(_M_path != nullptr);
1137     if (_M_path->_M_type == _Type::_Multi)
1138       {
1139 	__glibcxx_assert(_M_cur != _M_path->_M_cmpts.end());
1140 	return *_M_cur;
1141       }
1142     return *_M_path;
1143   }
1144 
1145   inline bool
1146   path::iterator::_M_equals(iterator __rhs) const
1147   {
1148     if (_M_path != __rhs._M_path)
1149       return false;
1150     if (_M_path == nullptr)
1151       return true;
1152     if (_M_path->_M_type == path::_Type::_Multi)
1153       return _M_cur == __rhs._M_cur;
1154     return _M_at_end == __rhs._M_at_end;
1155   }
1156 
1157   // @} group filesystem
1158 _GLIBCXX_END_NAMESPACE_CXX11
1159 } // namespace filesystem
1160 
1161 _GLIBCXX_END_NAMESPACE_VERSION
1162 } // namespace std
1163 
1164 #endif // C++17
1165 
1166 #endif // _GLIBCXX_FS_PATH_H
1167