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