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