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