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