1 //---------------------------------------------------------------------------------------
2 //
3 // ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14/C++17/C++20
4 //
5 //---------------------------------------------------------------------------------------
6 //
7 // Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in all
17 // copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 // SOFTWARE.
26 //
27 //---------------------------------------------------------------------------------------
28 //
29 // To dynamically select std::filesystem where available on most platforms,
30 // you could use:
31 //
32 // #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
33 // #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
34 // #define GHC_USE_STD_FS
35 // #include <filesystem>
36 // namespace fs = std::filesystem;
37 // #endif
38 // #endif
39 // #ifndef GHC_USE_STD_FS
40 // #include <ghc/filesystem.hpp>
41 // namespace fs = ghc::filesystem;
42 // #endif
43 //
44 //---------------------------------------------------------------------------------------
45 #ifndef GHC_FILESYSTEM_H
46 #define GHC_FILESYSTEM_H
47
48 // #define BSD manifest constant only in
49 // sys/param.h
50 #ifndef _WIN32
51 #include <sys/param.h>
52 #endif
53
54 #ifndef GHC_OS_DETECTED
55 #if defined(__APPLE__) && defined(__MACH__)
56 #define GHC_OS_MACOS
57 #elif defined(__linux__)
58 #define GHC_OS_LINUX
59 #if defined(__ANDROID__)
60 #define GHC_OS_ANDROID
61 #endif
62 #elif defined(_WIN64)
63 #define GHC_OS_WINDOWS
64 #define GHC_OS_WIN64
65 #elif defined(_WIN32)
66 #define GHC_OS_WINDOWS
67 #define GHC_OS_WIN32
68 #elif defined(__CYGWIN__)
69 #define GHC_OS_CYGWIN
70 #elif defined(__svr4__)
71 #define GHC_OS_SYS5R4
72 #elif defined(BSD)
73 #define GHC_OS_BSD
74 #elif defined(__EMSCRIPTEN__)
75 #define GHC_OS_WEB
76 #include <wasi/api.h>
77 #else
78 #error "Operating system currently not supported!"
79 #endif
80 #define GHC_OS_DETECTED
81 #if (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
82 #if _MSVC_LANG == 201703L
83 #define GHC_FILESYSTEM_RUNNING_CPP17
84 #else
85 #define GHC_FILESYSTEM_RUNNING_CPP20
86 #endif
87 #elif (defined(__cplusplus) && __cplusplus >= 201703L)
88 #if __cplusplus == 201703L
89 #define GHC_FILESYSTEM_RUNNING_CPP17
90 #else
91 #define GHC_FILESYSTEM_RUNNING_CPP20
92 #endif
93 #endif
94 #endif
95
96 #if defined(GHC_FILESYSTEM_IMPLEMENTATION)
97 #define GHC_EXPAND_IMPL
98 #define GHC_INLINE
99 #ifdef GHC_OS_WINDOWS
100 #ifndef GHC_FS_API
101 #define GHC_FS_API
102 #endif
103 #ifndef GHC_FS_API_CLASS
104 #define GHC_FS_API_CLASS
105 #endif
106 #else
107 #ifndef GHC_FS_API
108 #define GHC_FS_API __attribute__((visibility("default")))
109 #endif
110 #ifndef GHC_FS_API_CLASS
111 #define GHC_FS_API_CLASS __attribute__((visibility("default")))
112 #endif
113 #endif
114 #elif defined(GHC_FILESYSTEM_FWD)
115 #define GHC_INLINE
116 #ifdef GHC_OS_WINDOWS
117 #ifndef GHC_FS_API
118 #define GHC_FS_API extern
119 #endif
120 #ifndef GHC_FS_API_CLASS
121 #define GHC_FS_API_CLASS
122 #endif
123 #else
124 #ifndef GHC_FS_API
125 #define GHC_FS_API extern
126 #endif
127 #ifndef GHC_FS_API_CLASS
128 #define GHC_FS_API_CLASS
129 #endif
130 #endif
131 #else
132 #define GHC_EXPAND_IMPL
133 #define GHC_INLINE inline
134 #ifndef GHC_FS_API
135 #define GHC_FS_API
136 #endif
137 #ifndef GHC_FS_API_CLASS
138 #define GHC_FS_API_CLASS
139 #endif
140 #endif
141
142 #ifdef GHC_EXPAND_IMPL
143
144 #ifdef GHC_OS_WINDOWS
145 #include <windows.h>
146 // additional includes
147 #include <shellapi.h>
148 #include <sys/stat.h>
149 #include <sys/types.h>
150 #include <wchar.h>
151 #include <winioctl.h>
152 #else
153 #include <dirent.h>
154 #include <fcntl.h>
155 #include <limits.h>
156 #include <sys/param.h>
157 #include <sys/stat.h>
158 #include <sys/time.h>
159 #include <sys/types.h>
160 #include <unistd.h>
161 #ifdef GHC_OS_ANDROID
162 #include <android/api-level.h>
163 #if __ANDROID_API__ < 12
164 #include <sys/syscall.h>
165 #endif
166 #include <sys/vfs.h>
167 #define statvfs statfs
168 #else
169 #include <sys/statvfs.h>
170 #endif
171 #ifdef GHC_OS_CYGWIN
172 #include <strings.h>
173 #endif
174 #if !defined(__ANDROID__) || __ANDROID_API__ >= 26
175 #include <langinfo.h>
176 #endif
177 #endif
178 #ifdef GHC_OS_MACOS
179 #include <Availability.h>
180 #endif
181
182 #if defined(__cpp_impl_three_way_comparison) && defined(__has_include)
183 #if __has_include(<compare>)
184 #define GHC_HAS_THREEWAY_COMP
185 #include <compare>
186 #endif
187 #endif
188
189 #include <algorithm>
190 #include <cctype>
191 #include <chrono>
192 #include <clocale>
193 #include <cstdlib>
194 #include <cstring>
195 #include <fstream>
196 #include <functional>
197 #include <memory>
198 #include <stack>
199 #include <stdexcept>
200 #include <string>
201 #include <system_error>
202 #include <type_traits>
203 #include <utility>
204 #include <vector>
205
206 #else // GHC_EXPAND_IMPL
207
208 #if defined(__cpp_impl_three_way_comparison) && defined(__has_include)
209 #if __has_include(<compare>)
210 #define GHC_HAS_THREEWAY_COMP
211 #include <compare>
212 #endif
213 #endif
214 #include <chrono>
215 #include <fstream>
216 #include <memory>
217 #include <stack>
218 #include <stdexcept>
219 #include <string>
220 #include <system_error>
221 #ifdef GHC_OS_WINDOWS
222 #include <vector>
223 #endif
224 #endif // GHC_EXPAND_IMPL
225
226 // After standard library includes.
227 // Standard library support for std::string_view.
228 #if defined(__cpp_lib_string_view)
229 #define GHC_HAS_STD_STRING_VIEW
230 #elif defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 4000) && (__cplusplus >= 201402)
231 #define GHC_HAS_STD_STRING_VIEW
232 #elif defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 7) && (__cplusplus >= 201703)
233 #define GHC_HAS_STD_STRING_VIEW
234 #elif defined(_MSC_VER) && (_MSC_VER >= 1910 && _MSVC_LANG >= 201703)
235 #define GHC_HAS_STD_STRING_VIEW
236 #endif
237
238 // Standard library support for std::experimental::string_view.
239 #if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 3700 && _LIBCPP_VERSION < 7000) && (__cplusplus >= 201402)
240 #define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW
241 #elif defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4)) && (__cplusplus >= 201402)
242 #define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW
243 #endif
244
245 #if defined(GHC_HAS_STD_STRING_VIEW)
246 #include <string_view>
247 #elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW)
248 #include <experimental/string_view>
249 #endif
250
251 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
252 // Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp):
253 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
254 // Enforce C++17 API where possible when compiling for C++20, handles the following cases:
255 // * fs::path::u8string() returns std::string instead of std::u8string
256 // #define GHC_FILESYSTEM_ENFORCE_CPP17_API
257 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
258 // LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories
259 // configure LWG conformance ()
260 #define LWG_2682_BEHAVIOUR
261 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
262 // LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular
263 // file with that name, it is superseded by P1164R1, so only activate if really needed
264 // #define LWG_2935_BEHAVIOUR
265 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
266 // LWG #2936 enables new element wise (more expensive) path comparison
267 // * if this->root_name().native().compare(p.root_name().native()) != 0 return result
268 // * if this->has_root_directory() and !p.has_root_directory() return -1
269 // * if !this->has_root_directory() and p.has_root_directory() return -1
270 // * else result of element wise comparison of path iteration where first comparison is != 0 or 0
271 // if all comparisons are 0 (on Windows this implementation does case insensitive root_name()
272 // comparison)
273 #define LWG_2936_BEHAVIOUR
274 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
275 // LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2)
276 #define LWG_2937_BEHAVIOUR
277 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
278 // UTF8-Everywhere is the original behaviour of ghc::filesystem. But since v1.5 the windows
279 // version defaults to std::wstring storage backend. Still all std::string will be interpreted
280 // as UTF-8 encoded. With this define you can enfoce the old behavior on Windows, using
281 // std::string as backend and for fs::path::native() and char for fs::path::c_str(). This
282 // needs more conversions so it is (an was before v1.5) slower, bot might help keeping source
283 // homogeneous in a multi platform project.
284 // #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
285 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
286 // Raise errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found,
287 // instead of replacing them with the unicode replacement character (U+FFFD).
288 // #define GHC_RAISE_UNICODE_ERRORS
289 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
290 // Automatic prefix windows path with "\\?\" if they would break the MAX_PATH length.
291 // instead of replacing them with the unicode replacement character (U+FFFD).
292 #ifndef GHC_WIN_DISABLE_AUTO_PREFIXES
293 #define GHC_WIN_AUTO_PREFIX_LONG_PATH
294 #endif // GHC_WIN_DISABLE_AUTO_PREFIXES
295 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
296
297 // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch)
298 #define GHC_FILESYSTEM_VERSION 10504L
299
300 #if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND))
301 #define GHC_WITH_EXCEPTIONS
302 #endif
303 #if !defined(GHC_WITH_EXCEPTIONS) && defined(GHC_RAISE_UNICODE_ERRORS)
304 #error "Can't raise unicode errors with exception support disabled"
305 #endif
306
307 namespace ghc {
308 namespace filesystem {
309
310 #if defined(GHC_HAS_CUSTOM_STRING_VIEW)
311 #define GHC_WITH_STRING_VIEW
312 #elif defined(GHC_HAS_STD_STRING_VIEW)
313 #define GHC_WITH_STRING_VIEW
314 using std::basic_string_view;
315 #elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW)
316 #define GHC_WITH_STRING_VIEW
317 using std::experimental::basic_string_view;
318 #endif
319
320 // temporary existing exception type for yet unimplemented parts
321 class GHC_FS_API_CLASS not_implemented_exception : public std::logic_error
322 {
323 public:
not_implemented_exception()324 not_implemented_exception()
325 : std::logic_error("function not implemented yet.")
326 {
327 }
328 };
329
330 template <typename char_type>
331 class path_helper_base
332 {
333 public:
334 using value_type = char_type;
335 #ifdef GHC_OS_WINDOWS
336 static constexpr value_type preferred_separator = '\\';
337 #else
338 static constexpr value_type preferred_separator = '/';
339 #endif
340 };
341
342 #if __cplusplus < 201703L
343 template <typename char_type>
344 constexpr char_type path_helper_base<char_type>::preferred_separator;
345 #endif
346
347 #ifdef GHC_OS_WINDOWS
348 class path;
349 namespace detail {
350 bool has_executable_extension(const path& p);
351 }
352 #endif
353
354 // 30.10.8 class path
355 class GHC_FS_API_CLASS path
356 #if defined(GHC_OS_WINDOWS) && !defined(GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE)
357 #define GHC_USE_WCHAR_T
358 #define GHC_NATIVEWP(p) p.c_str()
359 #define GHC_PLATFORM_LITERAL(str) L##str
360 : private path_helper_base<std::wstring::value_type>
361 {
362 public:
363 using path_helper_base<std::wstring::value_type>::value_type;
364 #else
365 #define GHC_NATIVEWP(p) p.wstring().c_str()
366 #define GHC_PLATFORM_LITERAL(str) str
367 : private path_helper_base<std::string::value_type>
368 {
369 public:
370 using path_helper_base<std::string::value_type>::value_type;
371 #endif
372 using string_type = std::basic_string<value_type>;
373 using path_helper_base<value_type>::preferred_separator;
374
375 // 30.10.10.1 enumeration format
376 /// The path format in which the constructor argument is given.
377 enum format {
378 generic_format, ///< The generic format, internally used by
379 ///< ghc::filesystem::path with slashes
380 native_format, ///< The format native to the current platform this code
381 ///< is build for
382 auto_format, ///< Try to auto-detect the format, fallback to native
383 };
384
385 template <class T>
386 struct _is_basic_string : std::false_type
387 {
388 };
389 template <class CharT, class Traits, class Alloc>
390 struct _is_basic_string<std::basic_string<CharT, Traits, Alloc>> : std::true_type
391 {
392 };
393 template <class CharT>
394 struct _is_basic_string<std::basic_string<CharT, std::char_traits<CharT>, std::allocator<CharT>>> : std::true_type
395 {
396 };
397 #ifdef GHC_WITH_STRING_VIEW
398 template <class CharT, class Traits>
399 struct _is_basic_string<basic_string_view<CharT, Traits>> : std::true_type
400 {
401 };
402 template <class CharT>
403 struct _is_basic_string<basic_string_view<CharT, std::char_traits<CharT>>> : std::true_type
404 {
405 };
406 #endif
407
408 template <typename T1, typename T2 = void>
409 using path_type = typename std::enable_if<!std::is_same<path, T1>::value, path>::type;
410 template <typename T>
411 using path_from_string = typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value ||
412 std::is_same<wchar_t const*, typename std::decay<T>::type>::value || std::is_same<wchar_t*, typename std::decay<T>::type>::value,
413 path>::type;
414 template <typename T>
415 using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value || std::is_same<T, wchar_t>::value, path>::type;
416 // 30.10.8.4.1 constructors and destructor
417 path() noexcept;
418 path(const path& p);
419 path(path&& p) noexcept;
420 path(string_type&& source, format fmt = auto_format);
421 template <class Source, typename = path_from_string<Source>>
422 path(const Source& source, format fmt = auto_format);
423 template <class InputIterator>
424 path(InputIterator first, InputIterator last, format fmt = auto_format);
425 #ifdef GHC_WITH_EXCEPTIONS
426 template <class Source, typename = path_from_string<Source>>
427 path(const Source& source, const std::locale& loc, format fmt = auto_format);
428 template <class InputIterator>
429 path(InputIterator first, InputIterator last, const std::locale& loc, format fmt = auto_format);
430 #endif
431 ~path();
432
433 // 30.10.8.4.2 assignments
434 path& operator=(const path& p);
435 path& operator=(path&& p) noexcept;
436 path& operator=(string_type&& source);
437 path& assign(string_type&& source);
438 template <class Source>
439 path& operator=(const Source& source);
440 template <class Source>
441 path& assign(const Source& source);
442 template <class InputIterator>
443 path& assign(InputIterator first, InputIterator last);
444
445 // 30.10.8.4.3 appends
446 path& operator/=(const path& p);
447 template <class Source>
448 path& operator/=(const Source& source);
449 template <class Source>
450 path& append(const Source& source);
451 template <class InputIterator>
452 path& append(InputIterator first, InputIterator last);
453
454 // 30.10.8.4.4 concatenation
455 path& operator+=(const path& x);
456 path& operator+=(const string_type& x);
457 #ifdef GHC_WITH_STRING_VIEW
458 path& operator+=(basic_string_view<value_type> x);
459 #endif
460 path& operator+=(const value_type* x);
461 path& operator+=(value_type x);
462 template <class Source>
463 path_from_string<Source>& operator+=(const Source& x);
464 template <class EcharT>
465 path_type_EcharT<EcharT>& operator+=(EcharT x);
466 template <class Source>
467 path& concat(const Source& x);
468 template <class InputIterator>
469 path& concat(InputIterator first, InputIterator last);
470
471 // 30.10.8.4.5 modifiers
472 void clear() noexcept;
473 path& make_preferred();
474 path& remove_filename();
475 path& replace_filename(const path& replacement);
476 path& replace_extension(const path& replacement = path());
477 void swap(path& rhs) noexcept;
478
479 // 30.10.8.4.6 native format observers
480 const string_type& native() const noexcept;
481 const value_type* c_str() const noexcept;
482 operator string_type() const;
483 template <class EcharT, class traits = std::char_traits<EcharT>, class Allocator = std::allocator<EcharT>>
484 std::basic_string<EcharT, traits, Allocator> string(const Allocator& a = Allocator()) const;
485 std::string string() const;
486 std::wstring wstring() const;
487 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
488 std::u8string u8string() const;
489 #else
490 std::string u8string() const;
491 #endif
492 std::u16string u16string() const;
493 std::u32string u32string() const;
494
495 // 30.10.8.4.7 generic format observers
496 template <class EcharT, class traits = std::char_traits<EcharT>, class Allocator = std::allocator<EcharT>>
497 std::basic_string<EcharT, traits, Allocator> generic_string(const Allocator& a = Allocator()) const;
498 std::string generic_string() const;
499 std::wstring generic_wstring() const;
500 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
501 std::u8string generic_u8string() const;
502 #else
503 std::string generic_u8string() const;
504 #endif
505 std::u16string generic_u16string() const;
506 std::u32string generic_u32string() const;
507
508 // 30.10.8.4.8 compare
509 int compare(const path& p) const noexcept;
510 int compare(const string_type& s) const;
511 #ifdef GHC_WITH_STRING_VIEW
512 int compare(basic_string_view<value_type> s) const;
513 #endif
514 int compare(const value_type* s) const;
515
516 // 30.10.8.4.9 decomposition
517 path root_name() const;
518 path root_directory() const;
519 path root_path() const;
520 path relative_path() const;
521 path parent_path() const;
522 path filename() const;
523 path stem() const;
524 path extension() const;
525
526 // 30.10.8.4.10 query
527 bool empty() const noexcept;
528 bool has_root_name() const;
529 bool has_root_directory() const;
530 bool has_root_path() const;
531 bool has_relative_path() const;
532 bool has_parent_path() const;
533 bool has_filename() const;
534 bool has_stem() const;
535 bool has_extension() const;
536 bool is_absolute() const;
537 bool is_relative() const;
538
539 // 30.10.8.4.11 generation
540 path lexically_normal() const;
541 path lexically_relative(const path& base) const;
542 path lexically_proximate(const path& base) const;
543
544 // 30.10.8.5 iterators
545 class iterator;
546 using const_iterator = iterator;
547 iterator begin() const;
548 iterator end() const;
549
550 private:
551 using impl_value_type = value_type;
552 using impl_string_type = std::basic_string<impl_value_type>;
553 friend class directory_iterator;
554 void append_name(const value_type* name);
555 static constexpr impl_value_type generic_separator = '/';
556 template <typename InputIterator>
557 class input_iterator_range
558 {
559 public:
560 typedef InputIterator iterator;
561 typedef InputIterator const_iterator;
562 typedef typename InputIterator::difference_type difference_type;
563
input_iterator_range(const InputIterator & first,const InputIterator & last)564 input_iterator_range(const InputIterator& first, const InputIterator& last)
565 : _first(first)
566 , _last(last)
567 {
568 }
569
begin() const570 InputIterator begin() const { return _first; }
end() const571 InputIterator end() const { return _last; }
572
573 private:
574 InputIterator _first;
575 InputIterator _last;
576 };
577 friend void swap(path& lhs, path& rhs) noexcept;
578 friend size_t hash_value(const path& p) noexcept;
579 friend path canonical(const path& p, std::error_code& ec);
580 string_type::size_type root_name_length() const noexcept;
581 void postprocess_path_with_format(format fmt);
582 void check_long_path();
583 impl_string_type _path;
584 #ifdef GHC_OS_WINDOWS
585 void handle_prefixes();
586 friend bool detail::has_executable_extension(const path& p);
587 #ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH
588 string_type::size_type _prefixLength{0};
589 #else // GHC_WIN_AUTO_PREFIX_LONG_PATH
590 static const string_type::size_type _prefixLength{0};
591 #endif // GHC_WIN_AUTO_PREFIX_LONG_PATH
592 #else
593 static const string_type::size_type _prefixLength{0};
594 #endif
595 };
596
597 // 30.10.8.6 path non-member functions
598 GHC_FS_API void swap(path& lhs, path& rhs) noexcept;
599 GHC_FS_API size_t hash_value(const path& p) noexcept;
600 #ifdef GHC_HAS_THREEWAY_COMP
601 GHC_FS_API std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept;
602 #endif
603 GHC_FS_API bool operator==(const path& lhs, const path& rhs) noexcept;
604 GHC_FS_API bool operator!=(const path& lhs, const path& rhs) noexcept;
605 GHC_FS_API bool operator<(const path& lhs, const path& rhs) noexcept;
606 GHC_FS_API bool operator<=(const path& lhs, const path& rhs) noexcept;
607 GHC_FS_API bool operator>(const path& lhs, const path& rhs) noexcept;
608 GHC_FS_API bool operator>=(const path& lhs, const path& rhs) noexcept;
609 GHC_FS_API path operator/(const path& lhs, const path& rhs);
610
611 // 30.10.8.6.1 path inserter and extractor
612 template <class charT, class traits>
613 std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, const path& p);
614 template <class charT, class traits>
615 std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& is, path& p);
616
617 // 30.10.8.6.2 path factory functions
618 template <class Source, typename = path::path_from_string<Source>>
619 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
620 [[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]]
621 #endif
622 path u8path(const Source& source);
623 template <class InputIterator>
624 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
625 [[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]]
626 #endif
627 path u8path(InputIterator first, InputIterator last);
628
629 // 30.10.9 class filesystem_error
630 class GHC_FS_API_CLASS filesystem_error : public std::system_error
631 {
632 public:
633 filesystem_error(const std::string& what_arg, std::error_code ec);
634 filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec);
635 filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec);
636 const path& path1() const noexcept;
637 const path& path2() const noexcept;
638 const char* what() const noexcept override;
639
640 private:
641 std::string _what_arg;
642 std::error_code _ec;
643 path _p1, _p2;
644 };
645
646 class GHC_FS_API_CLASS path::iterator
647 {
648 public:
649 using value_type = const path;
650 using difference_type = std::ptrdiff_t;
651 using pointer = const path*;
652 using reference = const path&;
653 using iterator_category = std::bidirectional_iterator_tag;
654
655 iterator();
656 iterator(const path& p, const impl_string_type::const_iterator& pos);
657 iterator& operator++();
658 iterator operator++(int);
659 iterator& operator--();
660 iterator operator--(int);
661 bool operator==(const iterator& other) const;
662 bool operator!=(const iterator& other) const;
663 reference operator*() const;
664 pointer operator->() const;
665
666 private:
667 friend class path;
668 impl_string_type::const_iterator increment(const impl_string_type::const_iterator& pos) const;
669 impl_string_type::const_iterator decrement(const impl_string_type::const_iterator& pos) const;
670 void updateCurrent();
671 impl_string_type::const_iterator _first;
672 impl_string_type::const_iterator _last;
673 impl_string_type::const_iterator _prefix;
674 impl_string_type::const_iterator _root;
675 impl_string_type::const_iterator _iter;
676 path _current;
677 };
678
679 struct space_info
680 {
681 uintmax_t capacity;
682 uintmax_t free;
683 uintmax_t available;
684 };
685
686 // 30.10.10, enumerations
687 enum class file_type {
688 none,
689 not_found,
690 regular,
691 directory,
692 symlink,
693 block,
694 character,
695 fifo,
696 socket,
697 unknown,
698 };
699
700 enum class perms : uint16_t {
701 none = 0,
702
703 owner_read = 0400,
704 owner_write = 0200,
705 owner_exec = 0100,
706 owner_all = 0700,
707
708 group_read = 040,
709 group_write = 020,
710 group_exec = 010,
711 group_all = 070,
712
713 others_read = 04,
714 others_write = 02,
715 others_exec = 01,
716 others_all = 07,
717
718 all = 0777,
719 set_uid = 04000,
720 set_gid = 02000,
721 sticky_bit = 01000,
722
723 mask = 07777,
724 unknown = 0xffff
725 };
726
727 enum class perm_options : uint16_t {
728 replace = 3,
729 add = 1,
730 remove = 2,
731 nofollow = 4,
732 };
733
734 enum class copy_options : uint16_t {
735 none = 0,
736
737 skip_existing = 1,
738 overwrite_existing = 2,
739 update_existing = 4,
740
741 recursive = 8,
742
743 copy_symlinks = 0x10,
744 skip_symlinks = 0x20,
745
746 directories_only = 0x40,
747 create_symlinks = 0x80,
748 #ifndef GHC_OS_WEB
749 create_hard_links = 0x100
750 #endif
751 };
752
753 enum class directory_options : uint16_t {
754 none = 0,
755 follow_directory_symlink = 1,
756 skip_permission_denied = 2,
757 };
758
759 // 30.10.11 class file_status
760 class GHC_FS_API_CLASS file_status
761 {
762 public:
763 // 30.10.11.1 constructors and destructor
764 file_status() noexcept;
765 explicit file_status(file_type ft, perms prms = perms::unknown) noexcept;
766 file_status(const file_status&) noexcept;
767 file_status(file_status&&) noexcept;
768 ~file_status();
769 // assignments:
770 file_status& operator=(const file_status&) noexcept;
771 file_status& operator=(file_status&&) noexcept;
772 // 30.10.11.3 modifiers
773 void type(file_type ft) noexcept;
774 void permissions(perms prms) noexcept;
775 // 30.10.11.2 observers
776 file_type type() const noexcept;
777 perms permissions() const noexcept;
operator ==(const file_status & lhs,const file_status & rhs)778 friend bool operator==(const file_status& lhs, const file_status& rhs) noexcept { return lhs.type() == rhs.type() && lhs.permissions() == rhs.permissions(); }
779 private:
780 file_type _type;
781 perms _perms;
782 };
783
784 using file_time_type = std::chrono::time_point<std::chrono::system_clock>;
785
786 // 30.10.12 Class directory_entry
787 class GHC_FS_API_CLASS directory_entry
788 {
789 public:
790 // 30.10.12.1 constructors and destructor
791 directory_entry() noexcept = default;
792 directory_entry(const directory_entry&) = default;
793 directory_entry(directory_entry&&) noexcept = default;
794 #ifdef GHC_WITH_EXCEPTIONS
795 explicit directory_entry(const path& p);
796 #endif
797 directory_entry(const path& p, std::error_code& ec);
798 ~directory_entry();
799
800 // assignments:
801 directory_entry& operator=(const directory_entry&) = default;
802 directory_entry& operator=(directory_entry&&) noexcept = default;
803
804 // 30.10.12.2 modifiers
805 #ifdef GHC_WITH_EXCEPTIONS
806 void assign(const path& p);
807 void replace_filename(const path& p);
808 void refresh();
809 #endif
810 void assign(const path& p, std::error_code& ec);
811 void replace_filename(const path& p, std::error_code& ec);
812 void refresh(std::error_code& ec) noexcept;
813
814 // 30.10.12.3 observers
815 const filesystem::path& path() const noexcept;
816 operator const filesystem::path&() const noexcept;
817 #ifdef GHC_WITH_EXCEPTIONS
818 bool exists() const;
819 bool is_block_file() const;
820 bool is_character_file() const;
821 bool is_directory() const;
822 bool is_fifo() const;
823 bool is_other() const;
824 bool is_regular_file() const;
825 bool is_socket() const;
826 bool is_symlink() const;
827 uintmax_t file_size() const;
828 file_time_type last_write_time() const;
829 file_status status() const;
830 file_status symlink_status() const;
831 #endif
832 bool exists(std::error_code& ec) const noexcept;
833 bool is_block_file(std::error_code& ec) const noexcept;
834 bool is_character_file(std::error_code& ec) const noexcept;
835 bool is_directory(std::error_code& ec) const noexcept;
836 bool is_fifo(std::error_code& ec) const noexcept;
837 bool is_other(std::error_code& ec) const noexcept;
838 bool is_regular_file(std::error_code& ec) const noexcept;
839 bool is_socket(std::error_code& ec) const noexcept;
840 bool is_symlink(std::error_code& ec) const noexcept;
841 uintmax_t file_size(std::error_code& ec) const noexcept;
842 file_time_type last_write_time(std::error_code& ec) const noexcept;
843 file_status status(std::error_code& ec) const noexcept;
844 file_status symlink_status(std::error_code& ec) const noexcept;
845
846 #ifndef GHC_OS_WEB
847 #ifdef GHC_WITH_EXCEPTIONS
848 uintmax_t hard_link_count() const;
849 #endif
850 uintmax_t hard_link_count(std::error_code& ec) const noexcept;
851 #endif
852
853 #ifdef GHC_HAS_THREEWAY_COMP
854 std::strong_ordering operator<=>(const directory_entry& rhs) const noexcept;
855 #endif
856 bool operator<(const directory_entry& rhs) const noexcept;
857 bool operator==(const directory_entry& rhs) const noexcept;
858 bool operator!=(const directory_entry& rhs) const noexcept;
859 bool operator<=(const directory_entry& rhs) const noexcept;
860 bool operator>(const directory_entry& rhs) const noexcept;
861 bool operator>=(const directory_entry& rhs) const noexcept;
862
863 private:
864 friend class directory_iterator;
865 #ifdef GHC_WITH_EXCEPTIONS
866 file_type status_file_type() const;
867 #endif
868 file_type status_file_type(std::error_code& ec) const noexcept;
869 filesystem::path _path;
870 file_status _status;
871 file_status _symlink_status;
872 uintmax_t _file_size = static_cast<uintmax_t>(-1);
873 #ifndef GHC_OS_WINDOWS
874 uintmax_t _hard_link_count = static_cast<uintmax_t>(-1);
875 #endif
876 time_t _last_write_time = 0;
877 };
878
879 // 30.10.13 Class directory_iterator
880 class GHC_FS_API_CLASS directory_iterator
881 {
882 public:
883 class GHC_FS_API_CLASS proxy
884 {
885 public:
operator *() const886 const directory_entry& operator*() const& noexcept { return _dir_entry; }
operator *()887 directory_entry operator*() && noexcept { return std::move(_dir_entry); }
888
889 private:
proxy(const directory_entry & dir_entry)890 explicit proxy(const directory_entry& dir_entry)
891 : _dir_entry(dir_entry)
892 {
893 }
894 friend class directory_iterator;
895 friend class recursive_directory_iterator;
896 directory_entry _dir_entry;
897 };
898 using iterator_category = std::input_iterator_tag;
899 using value_type = directory_entry;
900 using difference_type = std::ptrdiff_t;
901 using pointer = const directory_entry*;
902 using reference = const directory_entry&;
903
904 // 30.10.13.1 member functions
905 directory_iterator() noexcept;
906 #ifdef GHC_WITH_EXCEPTIONS
907 explicit directory_iterator(const path& p);
908 directory_iterator(const path& p, directory_options options);
909 #endif
910 directory_iterator(const path& p, std::error_code& ec) noexcept;
911 directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept;
912 directory_iterator(const directory_iterator& rhs);
913 directory_iterator(directory_iterator&& rhs) noexcept;
914 ~directory_iterator();
915 directory_iterator& operator=(const directory_iterator& rhs);
916 directory_iterator& operator=(directory_iterator&& rhs) noexcept;
917 const directory_entry& operator*() const;
918 const directory_entry* operator->() const;
919 #ifdef GHC_WITH_EXCEPTIONS
920 directory_iterator& operator++();
921 #endif
922 directory_iterator& increment(std::error_code& ec) noexcept;
923
924 // other members as required by 27.2.3, input iterators
925 #ifdef GHC_WITH_EXCEPTIONS
operator ++(int)926 proxy operator++(int)
927 {
928 proxy p{**this};
929 ++*this;
930 return p;
931 }
932 #endif
933 bool operator==(const directory_iterator& rhs) const;
934 bool operator!=(const directory_iterator& rhs) const;
935
936 private:
937 friend class recursive_directory_iterator;
938 class impl;
939 std::shared_ptr<impl> _impl;
940 };
941
942 // 30.10.13.2 directory_iterator non-member functions
943 GHC_FS_API directory_iterator begin(directory_iterator iter) noexcept;
944 GHC_FS_API directory_iterator end(const directory_iterator&) noexcept;
945
946 // 30.10.14 class recursive_directory_iterator
947 class GHC_FS_API_CLASS recursive_directory_iterator
948 {
949 public:
950 using iterator_category = std::input_iterator_tag;
951 using value_type = directory_entry;
952 using difference_type = std::ptrdiff_t;
953 using pointer = const directory_entry*;
954 using reference = const directory_entry&;
955
956 // 30.10.14.1 constructors and destructor
957 recursive_directory_iterator() noexcept;
958 #ifdef GHC_WITH_EXCEPTIONS
959 explicit recursive_directory_iterator(const path& p);
960 recursive_directory_iterator(const path& p, directory_options options);
961 #endif
962 recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept;
963 recursive_directory_iterator(const path& p, std::error_code& ec) noexcept;
964 recursive_directory_iterator(const recursive_directory_iterator& rhs);
965 recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept;
966 ~recursive_directory_iterator();
967
968 // 30.10.14.1 observers
969 directory_options options() const;
970 int depth() const;
971 bool recursion_pending() const;
972
973 const directory_entry& operator*() const;
974 const directory_entry* operator->() const;
975
976 // 30.10.14.1 modifiers recursive_directory_iterator&
977 recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs);
978 recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept;
979 #ifdef GHC_WITH_EXCEPTIONS
980 recursive_directory_iterator& operator++();
981 #endif
982 recursive_directory_iterator& increment(std::error_code& ec) noexcept;
983
984 #ifdef GHC_WITH_EXCEPTIONS
985 void pop();
986 #endif
987 void pop(std::error_code& ec);
988 void disable_recursion_pending();
989
990 // other members as required by 27.2.3, input iterators
991 #ifdef GHC_WITH_EXCEPTIONS
operator ++(int)992 directory_iterator::proxy operator++(int)
993 {
994 directory_iterator::proxy proxy{**this};
995 ++*this;
996 return proxy;
997 }
998 #endif
999 bool operator==(const recursive_directory_iterator& rhs) const;
1000 bool operator!=(const recursive_directory_iterator& rhs) const;
1001
1002 private:
1003 struct recursive_directory_iterator_impl
1004 {
1005 directory_options _options;
1006 bool _recursion_pending;
1007 std::stack<directory_iterator> _dir_iter_stack;
recursive_directory_iterator_implghc::filesystem::recursive_directory_iterator::recursive_directory_iterator_impl1008 recursive_directory_iterator_impl(directory_options options, bool recursion_pending)
1009 : _options(options)
1010 , _recursion_pending(recursion_pending)
1011 {
1012 }
1013 };
1014 std::shared_ptr<recursive_directory_iterator_impl> _impl;
1015 };
1016
1017 // 30.10.14.2 directory_iterator non-member functions
1018 GHC_FS_API recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept;
1019 GHC_FS_API recursive_directory_iterator end(const recursive_directory_iterator&) noexcept;
1020
1021 // 30.10.15 filesystem operations
1022 #ifdef GHC_WITH_EXCEPTIONS
1023 GHC_FS_API path absolute(const path& p);
1024 GHC_FS_API path canonical(const path& p);
1025 GHC_FS_API void copy(const path& from, const path& to);
1026 GHC_FS_API void copy(const path& from, const path& to, copy_options options);
1027 GHC_FS_API bool copy_file(const path& from, const path& to);
1028 GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option);
1029 GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink);
1030 GHC_FS_API bool create_directories(const path& p);
1031 GHC_FS_API bool create_directory(const path& p);
1032 GHC_FS_API bool create_directory(const path& p, const path& attributes);
1033 GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink);
1034 GHC_FS_API void create_symlink(const path& to, const path& new_symlink);
1035 GHC_FS_API path current_path();
1036 GHC_FS_API void current_path(const path& p);
1037 GHC_FS_API bool exists(const path& p);
1038 GHC_FS_API bool equivalent(const path& p1, const path& p2);
1039 GHC_FS_API uintmax_t file_size(const path& p);
1040 GHC_FS_API bool is_block_file(const path& p);
1041 GHC_FS_API bool is_character_file(const path& p);
1042 GHC_FS_API bool is_directory(const path& p);
1043 GHC_FS_API bool is_empty(const path& p);
1044 GHC_FS_API bool is_fifo(const path& p);
1045 GHC_FS_API bool is_other(const path& p);
1046 GHC_FS_API bool is_regular_file(const path& p);
1047 GHC_FS_API bool is_socket(const path& p);
1048 GHC_FS_API bool is_symlink(const path& p);
1049 GHC_FS_API file_time_type last_write_time(const path& p);
1050 GHC_FS_API void last_write_time(const path& p, file_time_type new_time);
1051 GHC_FS_API void permissions(const path& p, perms prms, perm_options opts = perm_options::replace);
1052 GHC_FS_API path proximate(const path& p, const path& base = current_path());
1053 GHC_FS_API path read_symlink(const path& p);
1054 GHC_FS_API path relative(const path& p, const path& base = current_path());
1055 GHC_FS_API bool remove(const path& p);
1056 GHC_FS_API uintmax_t remove_all(const path& p);
1057 GHC_FS_API void rename(const path& from, const path& to);
1058 GHC_FS_API void resize_file(const path& p, uintmax_t size);
1059 GHC_FS_API space_info space(const path& p);
1060 GHC_FS_API file_status status(const path& p);
1061 GHC_FS_API file_status symlink_status(const path& p);
1062 GHC_FS_API path temp_directory_path();
1063 GHC_FS_API path weakly_canonical(const path& p);
1064 #endif
1065 GHC_FS_API path absolute(const path& p, std::error_code& ec);
1066 GHC_FS_API path canonical(const path& p, std::error_code& ec);
1067 GHC_FS_API void copy(const path& from, const path& to, std::error_code& ec) noexcept;
1068 GHC_FS_API void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept;
1069 GHC_FS_API bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept;
1070 GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option, std::error_code& ec) noexcept;
1071 GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept;
1072 GHC_FS_API bool create_directories(const path& p, std::error_code& ec) noexcept;
1073 GHC_FS_API bool create_directory(const path& p, std::error_code& ec) noexcept;
1074 GHC_FS_API bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept;
1075 GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept;
1076 GHC_FS_API void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept;
1077 GHC_FS_API path current_path(std::error_code& ec);
1078 GHC_FS_API void current_path(const path& p, std::error_code& ec) noexcept;
1079 GHC_FS_API bool exists(file_status s) noexcept;
1080 GHC_FS_API bool exists(const path& p, std::error_code& ec) noexcept;
1081 GHC_FS_API bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept;
1082 GHC_FS_API uintmax_t file_size(const path& p, std::error_code& ec) noexcept;
1083 GHC_FS_API bool is_block_file(file_status s) noexcept;
1084 GHC_FS_API bool is_block_file(const path& p, std::error_code& ec) noexcept;
1085 GHC_FS_API bool is_character_file(file_status s) noexcept;
1086 GHC_FS_API bool is_character_file(const path& p, std::error_code& ec) noexcept;
1087 GHC_FS_API bool is_directory(file_status s) noexcept;
1088 GHC_FS_API bool is_directory(const path& p, std::error_code& ec) noexcept;
1089 GHC_FS_API bool is_empty(const path& p, std::error_code& ec) noexcept;
1090 GHC_FS_API bool is_fifo(file_status s) noexcept;
1091 GHC_FS_API bool is_fifo(const path& p, std::error_code& ec) noexcept;
1092 GHC_FS_API bool is_other(file_status s) noexcept;
1093 GHC_FS_API bool is_other(const path& p, std::error_code& ec) noexcept;
1094 GHC_FS_API bool is_regular_file(file_status s) noexcept;
1095 GHC_FS_API bool is_regular_file(const path& p, std::error_code& ec) noexcept;
1096 GHC_FS_API bool is_socket(file_status s) noexcept;
1097 GHC_FS_API bool is_socket(const path& p, std::error_code& ec) noexcept;
1098 GHC_FS_API bool is_symlink(file_status s) noexcept;
1099 GHC_FS_API bool is_symlink(const path& p, std::error_code& ec) noexcept;
1100 GHC_FS_API file_time_type last_write_time(const path& p, std::error_code& ec) noexcept;
1101 GHC_FS_API void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept;
1102 GHC_FS_API void permissions(const path& p, perms prms, std::error_code& ec) noexcept;
1103 GHC_FS_API void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept;
1104 GHC_FS_API path proximate(const path& p, std::error_code& ec);
1105 GHC_FS_API path proximate(const path& p, const path& base, std::error_code& ec);
1106 GHC_FS_API path read_symlink(const path& p, std::error_code& ec);
1107 GHC_FS_API path relative(const path& p, std::error_code& ec);
1108 GHC_FS_API path relative(const path& p, const path& base, std::error_code& ec);
1109 GHC_FS_API bool remove(const path& p, std::error_code& ec) noexcept;
1110 GHC_FS_API uintmax_t remove_all(const path& p, std::error_code& ec) noexcept;
1111 GHC_FS_API void rename(const path& from, const path& to, std::error_code& ec) noexcept;
1112 GHC_FS_API void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept;
1113 GHC_FS_API space_info space(const path& p, std::error_code& ec) noexcept;
1114 GHC_FS_API file_status status(const path& p, std::error_code& ec) noexcept;
1115 GHC_FS_API bool status_known(file_status s) noexcept;
1116 GHC_FS_API file_status symlink_status(const path& p, std::error_code& ec) noexcept;
1117 GHC_FS_API path temp_directory_path(std::error_code& ec) noexcept;
1118 GHC_FS_API path weakly_canonical(const path& p, std::error_code& ec) noexcept;
1119
1120 #ifndef GHC_OS_WEB
1121 #ifdef GHC_WITH_EXCEPTIONS
1122 GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link);
1123 GHC_FS_API uintmax_t hard_link_count(const path& p);
1124 #endif
1125 GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept;
1126 GHC_FS_API uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept;
1127 #endif
1128
1129 // Non-C++17 add-on std::fstream wrappers with path
1130 template <class charT, class traits = std::char_traits<charT>>
1131 class basic_filebuf : public std::basic_filebuf<charT, traits>
1132 {
1133 public:
basic_filebuf()1134 basic_filebuf() {}
~basic_filebuf()1135 ~basic_filebuf() override {}
1136 basic_filebuf(const basic_filebuf&) = delete;
1137 const basic_filebuf& operator=(const basic_filebuf&) = delete;
open(const path & p,std::ios_base::openmode mode)1138 basic_filebuf<charT, traits>* open(const path& p, std::ios_base::openmode mode)
1139 {
1140 #if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
1141 return std::basic_filebuf<charT, traits>::open(p.wstring().c_str(), mode) ? this : 0;
1142 #else
1143 return std::basic_filebuf<charT, traits>::open(p.string().c_str(), mode) ? this : 0;
1144 #endif
1145 }
1146 };
1147
1148 template <class charT, class traits = std::char_traits<charT>>
1149 class basic_ifstream : public std::basic_ifstream<charT, traits>
1150 {
1151 public:
basic_ifstream()1152 basic_ifstream() {}
1153 #if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
basic_ifstream(const path & p,std::ios_base::openmode mode=std::ios_base::in)1154 explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in)
1155 : std::basic_ifstream<charT, traits>(p.wstring().c_str(), mode)
1156 {
1157 }
open(const path & p,std::ios_base::openmode mode=std::ios_base::in)1158 void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream<charT, traits>::open(p.wstring().c_str(), mode); }
1159 #else
basic_ifstream(const path & p,std::ios_base::openmode mode=std::ios_base::in)1160 explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in)
1161 : std::basic_ifstream<charT, traits>(p.string().c_str(), mode)
1162 {
1163 }
open(const path & p,std::ios_base::openmode mode=std::ios_base::in)1164 void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream<charT, traits>::open(p.string().c_str(), mode); }
1165 #endif
1166 basic_ifstream(const basic_ifstream&) = delete;
1167 const basic_ifstream& operator=(const basic_ifstream&) = delete;
~basic_ifstream()1168 ~basic_ifstream() override {}
1169 };
1170
1171 template <class charT, class traits = std::char_traits<charT>>
1172 class basic_ofstream : public std::basic_ofstream<charT, traits>
1173 {
1174 public:
basic_ofstream()1175 basic_ofstream() {}
1176 #if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
basic_ofstream(const path & p,std::ios_base::openmode mode=std::ios_base::out)1177 explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out)
1178 : std::basic_ofstream<charT, traits>(p.wstring().c_str(), mode)
1179 {
1180 }
open(const path & p,std::ios_base::openmode mode=std::ios_base::out)1181 void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream<charT, traits>::open(p.wstring().c_str(), mode); }
1182 #else
basic_ofstream(const path & p,std::ios_base::openmode mode=std::ios_base::out)1183 explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out)
1184 : std::basic_ofstream<charT, traits>(p.string().c_str(), mode)
1185 {
1186 }
open(const path & p,std::ios_base::openmode mode=std::ios_base::out)1187 void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream<charT, traits>::open(p.string().c_str(), mode); }
1188 #endif
1189 basic_ofstream(const basic_ofstream&) = delete;
1190 const basic_ofstream& operator=(const basic_ofstream&) = delete;
~basic_ofstream()1191 ~basic_ofstream() override {}
1192 };
1193
1194 template <class charT, class traits = std::char_traits<charT>>
1195 class basic_fstream : public std::basic_fstream<charT, traits>
1196 {
1197 public:
basic_fstream()1198 basic_fstream() {}
1199 #if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
basic_fstream(const path & p,std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)1200 explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
1201 : std::basic_fstream<charT, traits>(p.wstring().c_str(), mode)
1202 {
1203 }
open(const path & p,std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)1204 void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream<charT, traits>::open(p.wstring().c_str(), mode); }
1205 #else
basic_fstream(const path & p,std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)1206 explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
1207 : std::basic_fstream<charT, traits>(p.string().c_str(), mode)
1208 {
1209 }
open(const path & p,std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)1210 void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream<charT, traits>::open(p.string().c_str(), mode); }
1211 #endif
1212 basic_fstream(const basic_fstream&) = delete;
1213 const basic_fstream& operator=(const basic_fstream&) = delete;
~basic_fstream()1214 ~basic_fstream() override {}
1215 };
1216
1217 typedef basic_filebuf<char> filebuf;
1218 typedef basic_filebuf<wchar_t> wfilebuf;
1219 typedef basic_ifstream<char> ifstream;
1220 typedef basic_ifstream<wchar_t> wifstream;
1221 typedef basic_ofstream<char> ofstream;
1222 typedef basic_ofstream<wchar_t> wofstream;
1223 typedef basic_fstream<char> fstream;
1224 typedef basic_fstream<wchar_t> wfstream;
1225
1226 class GHC_FS_API_CLASS u8arguments
1227 {
1228 public:
1229 u8arguments(int& argc, char**& argv);
~u8arguments()1230 ~u8arguments()
1231 {
1232 _refargc = _argc;
1233 _refargv = _argv;
1234 }
1235
valid() const1236 bool valid() const { return _isvalid; }
1237
1238 private:
1239 int _argc;
1240 char** _argv;
1241 int& _refargc;
1242 char**& _refargv;
1243 bool _isvalid;
1244 #ifdef GHC_OS_WINDOWS
1245 std::vector<std::string> _args;
1246 std::vector<char*> _argp;
1247 #endif
1248 };
1249
1250 //-------------------------------------------------------------------------------------------------
1251 // Implementation
1252 //-------------------------------------------------------------------------------------------------
1253
1254 namespace detail {
1255 enum utf8_states_t { S_STRT = 0, S_RJCT = 8 };
1256 GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode);
1257 GHC_FS_API bool is_surrogate(uint32_t c);
1258 GHC_FS_API bool is_high_surrogate(uint32_t c);
1259 GHC_FS_API bool is_low_surrogate(uint32_t c);
1260 GHC_FS_API unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint);
1261 enum class portable_error {
1262 none = 0,
1263 exists,
1264 not_found,
1265 not_supported,
1266 not_implemented,
1267 invalid_argument,
1268 is_a_directory,
1269 };
1270 GHC_FS_API std::error_code make_error_code(portable_error err);
1271 #ifdef GHC_OS_WINDOWS
1272 GHC_FS_API std::error_code make_system_error(uint32_t err = 0);
1273 #else
1274 GHC_FS_API std::error_code make_system_error(int err = 0);
1275 #endif
1276 } // namespace detail
1277
1278 namespace detail {
1279
1280 #ifdef GHC_EXPAND_IMPL
1281
make_error_code(portable_error err)1282 GHC_INLINE std::error_code make_error_code(portable_error err)
1283 {
1284 #ifdef GHC_OS_WINDOWS
1285 switch (err) {
1286 case portable_error::none:
1287 return std::error_code();
1288 case portable_error::exists:
1289 return std::error_code(ERROR_ALREADY_EXISTS, std::system_category());
1290 case portable_error::not_found:
1291 return std::error_code(ERROR_PATH_NOT_FOUND, std::system_category());
1292 case portable_error::not_supported:
1293 return std::error_code(ERROR_NOT_SUPPORTED, std::system_category());
1294 case portable_error::not_implemented:
1295 return std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category());
1296 case portable_error::invalid_argument:
1297 return std::error_code(ERROR_INVALID_PARAMETER, std::system_category());
1298 case portable_error::is_a_directory:
1299 #ifdef ERROR_DIRECTORY_NOT_SUPPORTED
1300 return std::error_code(ERROR_DIRECTORY_NOT_SUPPORTED, std::system_category());
1301 #else
1302 return std::error_code(ERROR_NOT_SUPPORTED, std::system_category());
1303 #endif
1304 }
1305 #else
1306 switch (err) {
1307 case portable_error::none:
1308 return std::error_code();
1309 case portable_error::exists:
1310 return std::error_code(EEXIST, std::system_category());
1311 case portable_error::not_found:
1312 return std::error_code(ENOENT, std::system_category());
1313 case portable_error::not_supported:
1314 return std::error_code(ENOTSUP, std::system_category());
1315 case portable_error::not_implemented:
1316 return std::error_code(ENOSYS, std::system_category());
1317 case portable_error::invalid_argument:
1318 return std::error_code(EINVAL, std::system_category());
1319 case portable_error::is_a_directory:
1320 return std::error_code(EISDIR, std::system_category());
1321 }
1322 #endif
1323 return std::error_code();
1324 }
1325
1326 #ifdef GHC_OS_WINDOWS
make_system_error(uint32_t err)1327 GHC_INLINE std::error_code make_system_error(uint32_t err)
1328 {
1329 return std::error_code(err ? static_cast<int>(err) : static_cast<int>(::GetLastError()), std::system_category());
1330 }
1331 #else
make_system_error(int err)1332 GHC_INLINE std::error_code make_system_error(int err)
1333 {
1334 return std::error_code(err ? err : errno, std::system_category());
1335 }
1336 #endif
1337
1338 #endif // GHC_EXPAND_IMPL
1339
1340 template <typename Enum>
1341 using EnableBitmask = typename std::enable_if<std::is_same<Enum, perms>::value || std::is_same<Enum, perm_options>::value || std::is_same<Enum, copy_options>::value || std::is_same<Enum, directory_options>::value, Enum>::type;
1342 } // namespace detail
1343
1344 template <typename Enum>
operator &(Enum X,Enum Y)1345 constexpr detail::EnableBitmask<Enum> operator&(Enum X, Enum Y)
1346 {
1347 using underlying = typename std::underlying_type<Enum>::type;
1348 return static_cast<Enum>(static_cast<underlying>(X) & static_cast<underlying>(Y));
1349 }
1350
1351 template <typename Enum>
operator |(Enum X,Enum Y)1352 constexpr detail::EnableBitmask<Enum> operator|(Enum X, Enum Y)
1353 {
1354 using underlying = typename std::underlying_type<Enum>::type;
1355 return static_cast<Enum>(static_cast<underlying>(X) | static_cast<underlying>(Y));
1356 }
1357
1358 template <typename Enum>
operator ^(Enum X,Enum Y)1359 constexpr detail::EnableBitmask<Enum> operator^(Enum X, Enum Y)
1360 {
1361 using underlying = typename std::underlying_type<Enum>::type;
1362 return static_cast<Enum>(static_cast<underlying>(X) ^ static_cast<underlying>(Y));
1363 }
1364
1365 template <typename Enum>
operator ~(Enum X)1366 constexpr detail::EnableBitmask<Enum> operator~(Enum X)
1367 {
1368 using underlying = typename std::underlying_type<Enum>::type;
1369 return static_cast<Enum>(~static_cast<underlying>(X));
1370 }
1371
1372 template <typename Enum>
operator &=(Enum & X,Enum Y)1373 detail::EnableBitmask<Enum>& operator&=(Enum& X, Enum Y)
1374 {
1375 X = X & Y;
1376 return X;
1377 }
1378
1379 template <typename Enum>
operator |=(Enum & X,Enum Y)1380 detail::EnableBitmask<Enum>& operator|=(Enum& X, Enum Y)
1381 {
1382 X = X | Y;
1383 return X;
1384 }
1385
1386 template <typename Enum>
operator ^=(Enum & X,Enum Y)1387 detail::EnableBitmask<Enum>& operator^=(Enum& X, Enum Y)
1388 {
1389 X = X ^ Y;
1390 return X;
1391 }
1392
1393 #ifdef GHC_EXPAND_IMPL
1394
1395 namespace detail {
1396
in_range(uint32_t c,uint32_t lo,uint32_t hi)1397 GHC_INLINE bool in_range(uint32_t c, uint32_t lo, uint32_t hi)
1398 {
1399 return (static_cast<uint32_t>(c - lo) < (hi - lo + 1));
1400 }
1401
is_surrogate(uint32_t c)1402 GHC_INLINE bool is_surrogate(uint32_t c)
1403 {
1404 return in_range(c, 0xd800, 0xdfff);
1405 }
1406
is_high_surrogate(uint32_t c)1407 GHC_INLINE bool is_high_surrogate(uint32_t c)
1408 {
1409 return (c & 0xfffffc00) == 0xd800;
1410 }
1411
is_low_surrogate(uint32_t c)1412 GHC_INLINE bool is_low_surrogate(uint32_t c)
1413 {
1414 return (c & 0xfffffc00) == 0xdc00;
1415 }
1416
appendUTF8(std::string & str,uint32_t unicode)1417 GHC_INLINE void appendUTF8(std::string& str, uint32_t unicode)
1418 {
1419 if (unicode <= 0x7f) {
1420 str.push_back(static_cast<char>(unicode));
1421 }
1422 else if (unicode >= 0x80 && unicode <= 0x7ff) {
1423 str.push_back(static_cast<char>((unicode >> 6) + 192));
1424 str.push_back(static_cast<char>((unicode & 0x3f) + 128));
1425 }
1426 else if ((unicode >= 0x800 && unicode <= 0xd7ff) || (unicode >= 0xe000 && unicode <= 0xffff)) {
1427 str.push_back(static_cast<char>((unicode >> 12) + 224));
1428 str.push_back(static_cast<char>(((unicode & 0xfff) >> 6) + 128));
1429 str.push_back(static_cast<char>((unicode & 0x3f) + 128));
1430 }
1431 else if (unicode >= 0x10000 && unicode <= 0x10ffff) {
1432 str.push_back(static_cast<char>((unicode >> 18) + 240));
1433 str.push_back(static_cast<char>(((unicode & 0x3ffff) >> 12) + 128));
1434 str.push_back(static_cast<char>(((unicode & 0xfff) >> 6) + 128));
1435 str.push_back(static_cast<char>((unicode & 0x3f) + 128));
1436 }
1437 else {
1438 #ifdef GHC_RAISE_UNICODE_ERRORS
1439 throw filesystem_error("Illegal code point for unicode character.", str, std::make_error_code(std::errc::illegal_byte_sequence));
1440 #else
1441 appendUTF8(str, 0xfffd);
1442 #endif
1443 }
1444 }
1445
1446 // Thanks to Bjoern Hoehrmann (https://bjoern.hoehrmann.de/utf-8/decoder/dfa/)
1447 // and Taylor R Campbell for the ideas to this DFA approach of UTF-8 decoding;
1448 // Generating debugging and shrinking my own DFA from scratch was a day of fun!
consumeUtf8Fragment(const unsigned state,const uint8_t fragment,uint32_t & codepoint)1449 GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint)
1450 {
1451 static const uint32_t utf8_state_info[] = {
1452 // encoded states
1453 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, 0x99999999u,
1454 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, 0u, 0u,
1455 };
1456 uint8_t category = fragment < 128 ? 0 : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf;
1457 codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) : (0xffu >> category) & fragment);
1458 return state == S_RJCT ? static_cast<unsigned>(S_RJCT) : static_cast<unsigned>((utf8_state_info[category + 16] >> (state << 2)) & 0xf);
1459 }
1460
validUtf8(const std::string & utf8String)1461 GHC_INLINE bool validUtf8(const std::string& utf8String)
1462 {
1463 std::string::const_iterator iter = utf8String.begin();
1464 unsigned utf8_state = S_STRT;
1465 std::uint32_t codepoint = 0;
1466 while (iter < utf8String.end()) {
1467 if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast<uint8_t>(*iter++), codepoint)) == S_RJCT) {
1468 return false;
1469 }
1470 }
1471 if (utf8_state) {
1472 return false;
1473 }
1474 return true;
1475 }
1476
1477 } // namespace detail
1478
1479 #endif
1480
1481 namespace detail {
1482
1483 template <class StringType, class Utf8String, typename std::enable_if<path::_is_basic_string<Utf8String>::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 1)>::type* = nullptr>
fromUtf8(const Utf8String & utf8String,const typename StringType::allocator_type & alloc=typename StringType::allocator_type ())1484 inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
1485 {
1486 return StringType(utf8String.begin(), utf8String.end(), alloc);
1487 }
1488
1489 template <class StringType, class Utf8String, typename std::enable_if<path::_is_basic_string<Utf8String>::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 2)>::type* = nullptr>
fromUtf8(const Utf8String & utf8String,const typename StringType::allocator_type & alloc=typename StringType::allocator_type ())1490 inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
1491 {
1492 StringType result(alloc);
1493 result.reserve(utf8String.length());
1494 auto iter = utf8String.cbegin();
1495 unsigned utf8_state = S_STRT;
1496 std::uint32_t codepoint = 0;
1497 while (iter < utf8String.cend()) {
1498 if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast<uint8_t>(*iter++), codepoint)) == S_STRT) {
1499 if (codepoint <= 0xffff) {
1500 result += static_cast<typename StringType::value_type>(codepoint);
1501 }
1502 else {
1503 codepoint -= 0x10000;
1504 result += static_cast<typename StringType::value_type>((codepoint >> 10) + 0xd800);
1505 result += static_cast<typename StringType::value_type>((codepoint & 0x3ff) + 0xdc00);
1506 }
1507 codepoint = 0;
1508 }
1509 else if (utf8_state == S_RJCT) {
1510 #ifdef GHC_RAISE_UNICODE_ERRORS
1511 throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence));
1512 #else
1513 result += static_cast<typename StringType::value_type>(0xfffd);
1514 utf8_state = S_STRT;
1515 codepoint = 0;
1516 #endif
1517 }
1518 }
1519 if (utf8_state) {
1520 #ifdef GHC_RAISE_UNICODE_ERRORS
1521 throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence));
1522 #else
1523 result += static_cast<typename StringType::value_type>(0xfffd);
1524 #endif
1525 }
1526 return result;
1527 }
1528
1529 template <class StringType, class Utf8String, typename std::enable_if<path::_is_basic_string<Utf8String>::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 4)>::type* = nullptr>
fromUtf8(const Utf8String & utf8String,const typename StringType::allocator_type & alloc=typename StringType::allocator_type ())1530 inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
1531 {
1532 StringType result(alloc);
1533 result.reserve(utf8String.length());
1534 auto iter = utf8String.cbegin();
1535 unsigned utf8_state = S_STRT;
1536 std::uint32_t codepoint = 0;
1537 while (iter < utf8String.cend()) {
1538 if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast<uint8_t>(*iter++), codepoint)) == S_STRT) {
1539 result += static_cast<typename StringType::value_type>(codepoint);
1540 codepoint = 0;
1541 }
1542 else if (utf8_state == S_RJCT) {
1543 #ifdef GHC_RAISE_UNICODE_ERRORS
1544 throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence));
1545 #else
1546 result += static_cast<typename StringType::value_type>(0xfffd);
1547 utf8_state = S_STRT;
1548 codepoint = 0;
1549 #endif
1550 }
1551 }
1552 if (utf8_state) {
1553 #ifdef GHC_RAISE_UNICODE_ERRORS
1554 throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence));
1555 #else
1556 result += static_cast<typename StringType::value_type>(0xfffd);
1557 #endif
1558 }
1559 return result;
1560 }
1561
1562 template <class StringType, typename charT, std::size_t N>
fromUtf8(const charT (& utf8String)[N])1563 inline StringType fromUtf8(const charT (&utf8String)[N])
1564 {
1565 #ifdef GHC_WITH_STRING_VIEW
1566 return fromUtf8<StringType>(basic_string_view<charT>(utf8String, N - 1));
1567 #else
1568 return fromUtf8<StringType>(std::basic_string<charT>(utf8String, N - 1));
1569 #endif
1570 }
1571
1572 template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 1), int>::type size = 1>
toUtf8(const strT & unicodeString)1573 inline std::string toUtf8(const strT& unicodeString)
1574 {
1575 return std::string(unicodeString.begin(), unicodeString.end());
1576 }
1577
1578 template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 2), int>::type size = 2>
toUtf8(const strT & unicodeString)1579 inline std::string toUtf8(const strT& unicodeString)
1580 {
1581 std::string result;
1582 for (auto iter = unicodeString.begin(); iter != unicodeString.end(); ++iter) {
1583 char32_t c = *iter;
1584 if (is_surrogate(c)) {
1585 ++iter;
1586 if (iter != unicodeString.end() && is_high_surrogate(c) && is_low_surrogate(*iter)) {
1587 appendUTF8(result, (char32_t(c) << 10) + *iter - 0x35fdc00);
1588 }
1589 else {
1590 #ifdef GHC_RAISE_UNICODE_ERRORS
1591 throw filesystem_error("Illegal code point for unicode character.", result, std::make_error_code(std::errc::illegal_byte_sequence));
1592 #else
1593 appendUTF8(result, 0xfffd);
1594 if (iter == unicodeString.end()) {
1595 break;
1596 }
1597 #endif
1598 }
1599 }
1600 else {
1601 appendUTF8(result, c);
1602 }
1603 }
1604 return result;
1605 }
1606
1607 template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 4), int>::type size = 4>
toUtf8(const strT & unicodeString)1608 inline std::string toUtf8(const strT& unicodeString)
1609 {
1610 std::string result;
1611 for (auto c : unicodeString) {
1612 appendUTF8(result, static_cast<uint32_t>(c));
1613 }
1614 return result;
1615 }
1616
1617 template <typename charT>
toUtf8(const charT * unicodeString)1618 inline std::string toUtf8(const charT* unicodeString)
1619 {
1620 #ifdef GHC_WITH_STRING_VIEW
1621 return toUtf8(basic_string_view<charT, std::char_traits<charT>>(unicodeString));
1622 #else
1623 return toUtf8(std::basic_string<charT, std::char_traits<charT>>(unicodeString));
1624 #endif
1625 }
1626
1627 #ifdef GHC_USE_WCHAR_T
1628 template <class StringType, class WString, typename std::enable_if<path::_is_basic_string<WString>::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 1), bool>::type = false>
fromWChar(const WString & wString,const typename StringType::allocator_type & alloc=typename StringType::allocator_type ())1629 inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
1630 {
1631 auto temp = toUtf8(wString);
1632 return StringType(temp.begin(), temp.end(), alloc);
1633 }
1634
1635 template <class StringType, class WString, typename std::enable_if<path::_is_basic_string<WString>::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 2), bool>::type = false>
fromWChar(const WString & wString,const typename StringType::allocator_type & alloc=typename StringType::allocator_type ())1636 inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
1637 {
1638 return StringType(wString.begin(), wString.end(), alloc);
1639 }
1640
1641 template <class StringType, class WString, typename std::enable_if<path::_is_basic_string<WString>::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 4), bool>::type = false>
fromWChar(const WString & wString,const typename StringType::allocator_type & alloc=typename StringType::allocator_type ())1642 inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
1643 {
1644 auto temp = toUtf8(wString);
1645 return fromUtf8<StringType>(temp, alloc);
1646 }
1647
1648 template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 1), bool>::type = false>
toWChar(const strT & unicodeString)1649 inline std::wstring toWChar(const strT& unicodeString)
1650 {
1651 return fromUtf8<std::wstring>(unicodeString);
1652 }
1653
1654 template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 2), bool>::type = false>
toWChar(const strT & unicodeString)1655 inline std::wstring toWChar(const strT& unicodeString)
1656 {
1657 return std::wstring(unicodeString.begin(), unicodeString.end());
1658 }
1659
1660 template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value && (sizeof(typename strT::value_type) == 4), bool>::type = false>
toWChar(const strT & unicodeString)1661 inline std::wstring toWChar(const strT& unicodeString)
1662 {
1663 auto temp = toUtf8(unicodeString);
1664 return fromUtf8<std::wstring>(temp);
1665 }
1666
1667 template <typename charT>
toWChar(const charT * unicodeString)1668 inline std::wstring toWChar(const charT* unicodeString)
1669 {
1670 #ifdef GHC_WITH_STRING_VIEW
1671 return toWChar(basic_string_view<charT, std::char_traits<charT>>(unicodeString));
1672 #else
1673 return toWChar(std::basic_string<charT, std::char_traits<charT>>(unicodeString));
1674 #endif
1675 }
1676 #endif // GHC_USE_WCHAR_T
1677
1678 } // namespace detail
1679
1680 #ifdef GHC_EXPAND_IMPL
1681
1682 namespace detail {
1683
1684 template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value, bool>::type = true>
startsWith(const strT & what,const strT & with)1685 GHC_INLINE bool startsWith(const strT& what, const strT& with)
1686 {
1687 return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin());
1688 }
1689
1690 template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value, bool>::type = true>
endsWith(const strT & what,const strT & with)1691 GHC_INLINE bool endsWith(const strT& what, const strT& with)
1692 {
1693 return with.length() <= what.length() && what.compare(what.length() - with.length(), with.size(), with) == 0;
1694 }
1695
1696 } // namespace detail
1697
check_long_path()1698 GHC_INLINE void path::check_long_path()
1699 {
1700 #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
1701 if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) {
1702 postprocess_path_with_format(native_format);
1703 }
1704 #endif
1705 }
1706
postprocess_path_with_format(path::format fmt)1707 GHC_INLINE void path::postprocess_path_with_format(path::format fmt)
1708 {
1709 #ifdef GHC_RAISE_UNICODE_ERRORS
1710 if (!detail::validUtf8(_path)) {
1711 path t;
1712 t._path = _path;
1713 throw filesystem_error("Illegal byte sequence for unicode character.", t, std::make_error_code(std::errc::illegal_byte_sequence));
1714 }
1715 #endif
1716 switch (fmt) {
1717 #ifdef GHC_OS_WINDOWS
1718 case path::native_format:
1719 case path::auto_format:
1720 case path::generic_format:
1721 for (auto& c : _path) {
1722 if (c == generic_separator) {
1723 c = preferred_separator;
1724 }
1725 }
1726 #ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH
1727 if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) {
1728 _path = GHC_PLATFORM_LITERAL("\\\\?\\") + _path;
1729 }
1730 #endif
1731 handle_prefixes();
1732 break;
1733 #else
1734 case path::auto_format:
1735 case path::native_format:
1736 case path::generic_format:
1737 // nothing to do
1738 break;
1739 #endif
1740 }
1741 if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator) {
1742 impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast<string_type::difference_type>(_prefixLength) + 2, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; });
1743 _path.erase(new_end, _path.end());
1744 }
1745 else {
1746 impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast<string_type::difference_type>(_prefixLength), _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; });
1747 _path.erase(new_end, _path.end());
1748 }
1749 }
1750
1751 #endif // GHC_EXPAND_IMPL
1752
1753 template <class Source, typename>
path(const Source & source,format fmt)1754 inline path::path(const Source& source, format fmt)
1755 #ifdef GHC_USE_WCHAR_T
1756 : _path(detail::toWChar(source))
1757 #else
1758 : _path(detail::toUtf8(source))
1759 #endif
1760 {
1761 postprocess_path_with_format(fmt);
1762 }
1763
1764 template <class Source, typename>
u8path(const Source & source)1765 inline path u8path(const Source& source)
1766 {
1767 return path(source);
1768 }
1769 template <class InputIterator>
u8path(InputIterator first,InputIterator last)1770 inline path u8path(InputIterator first, InputIterator last)
1771 {
1772 return path(first, last);
1773 }
1774
1775 template <class InputIterator>
path(InputIterator first,InputIterator last,format fmt)1776 inline path::path(InputIterator first, InputIterator last, format fmt)
1777 : path(std::basic_string<typename std::iterator_traits<InputIterator>::value_type>(first, last), fmt)
1778 {
1779 // delegated
1780 }
1781
1782 #ifdef GHC_EXPAND_IMPL
1783
1784 namespace detail {
1785
equals_simple_insensitive(const path::value_type * str1,const path::value_type * str2)1786 GHC_INLINE bool equals_simple_insensitive(const path::value_type* str1, const path::value_type* str2)
1787 {
1788 #ifdef GHC_OS_WINDOWS
1789 #ifdef __GNUC__
1790 while (::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2++)) {
1791 if (*str1++ == 0)
1792 return true;
1793 }
1794 return false;
1795 #else // __GNUC__
1796 #ifdef GHC_USE_WCHAR_T
1797 return 0 == ::_wcsicmp(str1, str2);
1798 #else // GHC_USE_WCHAR_T
1799 return 0 == ::_stricmp(str1, str2);
1800 #endif // GHC_USE_WCHAR_T
1801 #endif // __GNUC__
1802 #else // GHC_OS_WINDOWS
1803 return 0 == ::strcasecmp(str1, str2);
1804 #endif // GHC_OS_WINDOWS
1805 }
1806
compare_simple_insensitive(const path::value_type * str1,size_t len1,const path::value_type * str2,size_t len2)1807 GHC_INLINE int compare_simple_insensitive(const path::value_type* str1, size_t len1, const path::value_type* str2, size_t len2)
1808 {
1809 while (len1 > 0 && len2 > 0 && ::tolower(static_cast<unsigned char>(*str1)) == ::tolower(static_cast<unsigned char>(*str2))) {
1810 --len1;
1811 --len2;
1812 ++str1;
1813 ++str2;
1814 }
1815 if (len1 && len2) {
1816 return *str1 < *str2 ? -1 : 1;
1817 }
1818 if (len1 == 0 && len2 == 0) {
1819 return 0;
1820 }
1821 return len1 == 0 ? -1 : 1;
1822 }
1823
strerror_adapter(char * gnu,char *)1824 GHC_INLINE const char* strerror_adapter(char* gnu, char*)
1825 {
1826 return gnu;
1827 }
1828
strerror_adapter(int posix,char * buffer)1829 GHC_INLINE const char* strerror_adapter(int posix, char* buffer)
1830 {
1831 if (posix) {
1832 return "Error in strerror_r!";
1833 }
1834 return buffer;
1835 }
1836
1837 template <typename ErrorNumber>
systemErrorText(ErrorNumber code=0)1838 GHC_INLINE std::string systemErrorText(ErrorNumber code = 0)
1839 {
1840 #if defined(GHC_OS_WINDOWS)
1841 LPVOID msgBuf;
1842 DWORD dw = code ? static_cast<DWORD>(code) : ::GetLastError();
1843 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msgBuf, 0, NULL);
1844 std::string msg = toUtf8(std::wstring((LPWSTR)msgBuf));
1845 LocalFree(msgBuf);
1846 return msg;
1847 #else
1848 char buffer[512];
1849 return strerror_adapter(strerror_r(code ? code : errno, buffer, sizeof(buffer)), buffer);
1850 #endif
1851 }
1852
1853 #ifdef GHC_OS_WINDOWS
1854 using CreateSymbolicLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, DWORD);
1855 using CreateHardLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
1856
create_symlink(const path & target_name,const path & new_symlink,bool to_directory,std::error_code & ec)1857 GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool to_directory, std::error_code& ec)
1858 {
1859 std::error_code tec;
1860 auto fs = status(target_name, tec);
1861 if ((fs.type() == file_type::directory && !to_directory) || (fs.type() == file_type::regular && to_directory)) {
1862 ec = detail::make_error_code(detail::portable_error::not_supported);
1863 return;
1864 }
1865 #if defined(__GNUC__) && __GNUC__ >= 8
1866 #pragma GCC diagnostic push
1867 #pragma GCC diagnostic ignored "-Wcast-function-type"
1868 #endif
1869 static CreateSymbolicLinkW_fp api_call = reinterpret_cast<CreateSymbolicLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW"));
1870 #if defined(__GNUC__) && __GNUC__ >= 8
1871 #pragma GCC diagnostic pop
1872 #endif
1873 if (api_call) {
1874 if (api_call(detail::fromUtf8<std::wstring>(new_symlink.u8string()).c_str(), detail::fromUtf8<std::wstring>(target_name.u8string()).c_str(), to_directory ? 1 : 0) == 0) {
1875 auto result = ::GetLastError();
1876 if (result == ERROR_PRIVILEGE_NOT_HELD && api_call(detail::fromUtf8<std::wstring>(new_symlink.u8string()).c_str(), detail::fromUtf8<std::wstring>(target_name.u8string()).c_str(), to_directory ? 3 : 2) != 0) {
1877 return;
1878 }
1879 ec = detail::make_system_error(result);
1880 }
1881 }
1882 else {
1883 ec = detail::make_system_error(ERROR_NOT_SUPPORTED);
1884 }
1885 }
1886
create_hardlink(const path & target_name,const path & new_hardlink,std::error_code & ec)1887 GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec)
1888 {
1889 #if defined(__GNUC__) && __GNUC__ >= 8
1890 #pragma GCC diagnostic push
1891 #pragma GCC diagnostic ignored "-Wcast-function-type"
1892 #endif
1893 static CreateHardLinkW_fp api_call = reinterpret_cast<CreateHardLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW"));
1894 #if defined(__GNUC__) && __GNUC__ >= 8
1895 #pragma GCC diagnostic pop
1896 #endif
1897 if (api_call) {
1898 if (api_call(GHC_NATIVEWP(new_hardlink), GHC_NATIVEWP(target_name), NULL) == 0) {
1899 ec = detail::make_system_error();
1900 }
1901 }
1902 else {
1903 ec = detail::make_system_error(ERROR_NOT_SUPPORTED);
1904 }
1905 }
1906
getFullPathName(const wchar_t * p,std::error_code & ec)1907 GHC_INLINE path getFullPathName(const wchar_t* p, std::error_code& ec)
1908 {
1909 ULONG size = ::GetFullPathNameW(p, 0, 0, 0);
1910 if (size) {
1911 std::vector<wchar_t> buf(size, 0);
1912 ULONG s2 = GetFullPathNameW(p, size, buf.data(), nullptr);
1913 if (s2 && s2 < size) {
1914 return path(std::wstring(buf.data(), s2));
1915 }
1916 }
1917 ec = detail::make_system_error();
1918 return path();
1919 }
1920
1921 #else
create_symlink(const path & target_name,const path & new_symlink,bool,std::error_code & ec)1922 GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool, std::error_code& ec)
1923 {
1924 if (::symlink(target_name.c_str(), new_symlink.c_str()) != 0) {
1925 ec = detail::make_system_error();
1926 }
1927 }
1928
1929 #ifndef GHC_OS_WEB
create_hardlink(const path & target_name,const path & new_hardlink,std::error_code & ec)1930 GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec)
1931 {
1932 if (::link(target_name.c_str(), new_hardlink.c_str()) != 0) {
1933 ec = detail::make_system_error();
1934 }
1935 }
1936 #endif
1937 #endif
1938
1939 template <typename T>
file_status_from_st_mode(T mode)1940 GHC_INLINE file_status file_status_from_st_mode(T mode)
1941 {
1942 #ifdef GHC_OS_WINDOWS
1943 file_type ft = file_type::unknown;
1944 if ((mode & _S_IFDIR) == _S_IFDIR) {
1945 ft = file_type::directory;
1946 }
1947 else if ((mode & _S_IFREG) == _S_IFREG) {
1948 ft = file_type::regular;
1949 }
1950 else if ((mode & _S_IFCHR) == _S_IFCHR) {
1951 ft = file_type::character;
1952 }
1953 perms prms = static_cast<perms>(mode & 0xfff);
1954 return file_status(ft, prms);
1955 #else
1956 file_type ft = file_type::unknown;
1957 if (S_ISDIR(mode)) {
1958 ft = file_type::directory;
1959 }
1960 else if (S_ISREG(mode)) {
1961 ft = file_type::regular;
1962 }
1963 else if (S_ISCHR(mode)) {
1964 ft = file_type::character;
1965 }
1966 else if (S_ISBLK(mode)) {
1967 ft = file_type::block;
1968 }
1969 else if (S_ISFIFO(mode)) {
1970 ft = file_type::fifo;
1971 }
1972 else if (S_ISLNK(mode)) {
1973 ft = file_type::symlink;
1974 }
1975 else if (S_ISSOCK(mode)) {
1976 ft = file_type::socket;
1977 }
1978 perms prms = static_cast<perms>(mode & 0xfff);
1979 return file_status(ft, prms);
1980 #endif
1981 }
1982
resolveSymlink(const path & p,std::error_code & ec)1983 GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec)
1984 {
1985 #ifdef GHC_OS_WINDOWS
1986 #ifndef REPARSE_DATA_BUFFER_HEADER_SIZE
1987 typedef struct _REPARSE_DATA_BUFFER
1988 {
1989 ULONG ReparseTag;
1990 USHORT ReparseDataLength;
1991 USHORT Reserved;
1992 union
1993 {
1994 struct
1995 {
1996 USHORT SubstituteNameOffset;
1997 USHORT SubstituteNameLength;
1998 USHORT PrintNameOffset;
1999 USHORT PrintNameLength;
2000 ULONG Flags;
2001 WCHAR PathBuffer[1];
2002 } SymbolicLinkReparseBuffer;
2003 struct
2004 {
2005 USHORT SubstituteNameOffset;
2006 USHORT SubstituteNameLength;
2007 USHORT PrintNameOffset;
2008 USHORT PrintNameLength;
2009 WCHAR PathBuffer[1];
2010 } MountPointReparseBuffer;
2011 struct
2012 {
2013 UCHAR DataBuffer[1];
2014 } GenericReparseBuffer;
2015 } DUMMYUNIONNAME;
2016 } REPARSE_DATA_BUFFER;
2017 #ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
2018 #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024)
2019 #endif
2020 #endif
2021
2022 std::shared_ptr<void> file(CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
2023 if (file.get() == INVALID_HANDLE_VALUE) {
2024 ec = detail::make_system_error();
2025 return path();
2026 }
2027
2028 std::shared_ptr<REPARSE_DATA_BUFFER> reparseData((REPARSE_DATA_BUFFER*)std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE), std::free);
2029 ULONG bufferUsed;
2030 path result;
2031 if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) {
2032 if (IsReparseTagMicrosoft(reparseData->ReparseTag)) {
2033 switch (reparseData->ReparseTag) {
2034 case IO_REPARSE_TAG_SYMLINK: {
2035 auto printName = std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR));
2036 auto substituteName =
2037 std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR));
2038 if (detail::endsWith(substituteName, printName) && detail::startsWith(substituteName, std::wstring(L"\\??\\"))) {
2039 result = printName;
2040 }
2041 else {
2042 result = substituteName;
2043 }
2044 if (reparseData->SymbolicLinkReparseBuffer.Flags & 0x1 /*SYMLINK_FLAG_RELATIVE*/) {
2045 result = p.parent_path() / result;
2046 }
2047 break;
2048 }
2049 case IO_REPARSE_TAG_MOUNT_POINT:
2050 result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR));
2051 break;
2052 default:
2053 break;
2054 }
2055 }
2056 }
2057 else {
2058 ec = detail::make_system_error();
2059 }
2060 return result;
2061 #else
2062 size_t bufferSize = 256;
2063 while (true) {
2064 std::vector<char> buffer(bufferSize, static_cast<char>(0));
2065 auto rc = ::readlink(p.c_str(), buffer.data(), buffer.size());
2066 if (rc < 0) {
2067 ec = detail::make_system_error();
2068 return path();
2069 }
2070 else if (rc < static_cast<int>(bufferSize)) {
2071 return path(std::string(buffer.data(), static_cast<std::string::size_type>(rc)));
2072 }
2073 bufferSize *= 2;
2074 }
2075 return path();
2076 #endif
2077 }
2078
2079 #ifdef GHC_OS_WINDOWS
timeFromFILETIME(const FILETIME & ft)2080 GHC_INLINE time_t timeFromFILETIME(const FILETIME& ft)
2081 {
2082 ULARGE_INTEGER ull;
2083 ull.LowPart = ft.dwLowDateTime;
2084 ull.HighPart = ft.dwHighDateTime;
2085 return static_cast<time_t>(ull.QuadPart / 10000000ULL - 11644473600ULL);
2086 }
2087
timeToFILETIME(time_t t,FILETIME & ft)2088 GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft)
2089 {
2090 LONGLONG ll;
2091 ll = Int32x32To64(t, 10000000) + 116444736000000000;
2092 ft.dwLowDateTime = static_cast<DWORD>(ll);
2093 ft.dwHighDateTime = static_cast<DWORD>(ll >> 32);
2094 }
2095
2096 template <typename INFO>
hard_links_from_INFO(const INFO * info)2097 GHC_INLINE uintmax_t hard_links_from_INFO(const INFO* info)
2098 {
2099 return static_cast<uintmax_t>(-1);
2100 }
2101
2102 template <>
hard_links_from_INFO(const BY_HANDLE_FILE_INFORMATION * info)2103 GHC_INLINE uintmax_t hard_links_from_INFO<BY_HANDLE_FILE_INFORMATION>(const BY_HANDLE_FILE_INFORMATION* info)
2104 {
2105 return info->nNumberOfLinks;
2106 }
2107
2108 template <typename INFO>
status_from_INFO(const path & p,const INFO * info,std::error_code &,uintmax_t * sz=nullptr,time_t * lwt=nullptr)2109 GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code&, uintmax_t* sz = nullptr, time_t* lwt = nullptr)
2110 {
2111 file_type ft = file_type::unknown;
2112 if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
2113 ft = file_type::symlink;
2114 }
2115 else {
2116 if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
2117 ft = file_type::directory;
2118 }
2119 else {
2120 ft = file_type::regular;
2121 }
2122 }
2123 perms prms = perms::owner_read | perms::group_read | perms::others_read;
2124 if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
2125 prms = prms | perms::owner_write | perms::group_write | perms::others_write;
2126 }
2127 if (has_executable_extension(p)) {
2128 prms = prms | perms::owner_exec | perms::group_exec | perms::others_exec;
2129 }
2130 if (sz) {
2131 *sz = static_cast<uintmax_t>(info->nFileSizeHigh) << (sizeof(info->nFileSizeHigh) * 8) | info->nFileSizeLow;
2132 }
2133 if (lwt) {
2134 *lwt = detail::timeFromFILETIME(info->ftLastWriteTime);
2135 }
2136 return file_status(ft, prms);
2137 }
2138
2139 #endif
2140
is_not_found_error(std::error_code & ec)2141 GHC_INLINE bool is_not_found_error(std::error_code& ec)
2142 {
2143 #ifdef GHC_OS_WINDOWS
2144 return ec.value() == ERROR_FILE_NOT_FOUND || ec.value() == ERROR_PATH_NOT_FOUND || ec.value() == ERROR_INVALID_NAME;
2145 #else
2146 return ec.value() == ENOENT || ec.value() == ENOTDIR;
2147 #endif
2148 }
2149
symlink_status_ex(const path & p,std::error_code & ec,uintmax_t * sz=nullptr,uintmax_t * nhl=nullptr,time_t * lwt=nullptr)2150 GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr) noexcept
2151 {
2152 #ifdef GHC_OS_WINDOWS
2153 file_status fs;
2154 WIN32_FILE_ATTRIBUTE_DATA attr;
2155 if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) {
2156 ec = detail::make_system_error();
2157 }
2158 else {
2159 ec.clear();
2160 fs = detail::status_from_INFO(p, &attr, ec, sz, lwt);
2161 if (nhl) {
2162 *nhl = 0;
2163 }
2164 if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2165 fs.type(file_type::symlink);
2166 }
2167 }
2168 if (detail::is_not_found_error(ec)) {
2169 return file_status(file_type::not_found);
2170 }
2171 return ec ? file_status(file_type::none) : fs;
2172 #else
2173 (void)sz;
2174 (void)nhl;
2175 (void)lwt;
2176 struct ::stat fs;
2177 auto result = ::lstat(p.c_str(), &fs);
2178 if (result == 0) {
2179 ec.clear();
2180 file_status f_s = detail::file_status_from_st_mode(fs.st_mode);
2181 return f_s;
2182 }
2183 ec = detail::make_system_error();
2184 if (detail::is_not_found_error(ec)) {
2185 return file_status(file_type::not_found, perms::unknown);
2186 }
2187 return file_status(file_type::none);
2188 #endif
2189 }
2190
status_ex(const path & p,std::error_code & ec,file_status * sls=nullptr,uintmax_t * sz=nullptr,uintmax_t * nhl=nullptr,time_t * lwt=nullptr,int recurse_count=0)2191 GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status* sls = nullptr, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr, int recurse_count = 0) noexcept
2192 {
2193 ec.clear();
2194 #ifdef GHC_OS_WINDOWS
2195 if (recurse_count > 16) {
2196 ec = detail::make_system_error(0x2A9 /*ERROR_STOPPED_ON_SYMLINK*/);
2197 return file_status(file_type::unknown);
2198 }
2199 WIN32_FILE_ATTRIBUTE_DATA attr;
2200 if (!::GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) {
2201 ec = detail::make_system_error();
2202 }
2203 else if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2204 path target = resolveSymlink(p, ec);
2205 file_status result;
2206 if (!ec && !target.empty()) {
2207 if (sls) {
2208 *sls = status_from_INFO(p, &attr, ec);
2209 }
2210 return detail::status_ex(target, ec, nullptr, sz, nhl, lwt, recurse_count + 1);
2211 }
2212 return file_status(file_type::unknown);
2213 }
2214 if (ec) {
2215 if (detail::is_not_found_error(ec)) {
2216 return file_status(file_type::not_found);
2217 }
2218 return file_status(file_type::none);
2219 }
2220 if (nhl) {
2221 *nhl = 0;
2222 }
2223 return detail::status_from_INFO(p, &attr, ec, sz, lwt);
2224 #else
2225 (void)recurse_count;
2226 struct ::stat st;
2227 auto result = ::lstat(p.c_str(), &st);
2228 if (result == 0) {
2229 ec.clear();
2230 file_status fs = detail::file_status_from_st_mode(st.st_mode);
2231 if (sls) {
2232 *sls = fs;
2233 }
2234 if (fs.type() == file_type::symlink) {
2235 result = ::stat(p.c_str(), &st);
2236 if (result == 0) {
2237 fs = detail::file_status_from_st_mode(st.st_mode);
2238 }
2239 else {
2240 ec = detail::make_system_error();
2241 if (detail::is_not_found_error(ec)) {
2242 return file_status(file_type::not_found, perms::unknown);
2243 }
2244 return file_status(file_type::none);
2245 }
2246 }
2247 if (sz) {
2248 *sz = static_cast<uintmax_t>(st.st_size);
2249 }
2250 if (nhl) {
2251 *nhl = st.st_nlink;
2252 }
2253 if (lwt) {
2254 *lwt = st.st_mtime;
2255 }
2256 return fs;
2257 }
2258 else {
2259 ec = detail::make_system_error();
2260 if (detail::is_not_found_error(ec)) {
2261 return file_status(file_type::not_found, perms::unknown);
2262 }
2263 return file_status(file_type::none);
2264 }
2265 #endif
2266 }
2267
2268 } // namespace detail
2269
u8arguments(int & argc,char ** & argv)2270 GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv)
2271 : _argc(argc)
2272 , _argv(argv)
2273 , _refargc(argc)
2274 , _refargv(argv)
2275 , _isvalid(false)
2276 {
2277 #ifdef GHC_OS_WINDOWS
2278 LPWSTR* p;
2279 p = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
2280 _args.reserve(static_cast<size_t>(argc));
2281 _argp.reserve(static_cast<size_t>(argc));
2282 for (size_t i = 0; i < static_cast<size_t>(argc); ++i) {
2283 _args.push_back(detail::toUtf8(std::wstring(p[i])));
2284 _argp.push_back((char*)_args[i].data());
2285 }
2286 argv = _argp.data();
2287 ::LocalFree(p);
2288 _isvalid = true;
2289 #else
2290 std::setlocale(LC_ALL, "");
2291 #if defined(__ANDROID__) && __ANDROID_API__ < 26
2292 _isvalid = true;
2293 #else
2294 if (detail::equals_simple_insensitive(::nl_langinfo(CODESET), "UTF-8")) {
2295 _isvalid = true;
2296 }
2297 #endif
2298 #endif
2299 }
2300
2301 //-----------------------------------------------------------------------------
2302 // 30.10.8.4.1 constructors and destructor
2303
path()2304 GHC_INLINE path::path() noexcept {}
2305
path(const path & p)2306 GHC_INLINE path::path(const path& p)
2307 : _path(p._path)
2308 #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
2309 , _prefixLength(p._prefixLength)
2310 #endif
2311 {
2312 }
2313
path(path && p)2314 GHC_INLINE path::path(path&& p) noexcept
2315 : _path(std::move(p._path))
2316 #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
2317 , _prefixLength(p._prefixLength)
2318 #endif
2319 {
2320 }
2321
path(string_type && source,format fmt)2322 GHC_INLINE path::path(string_type&& source, format fmt)
2323 : _path(std::move(source))
2324 {
2325 postprocess_path_with_format(fmt);
2326 }
2327
2328 #endif // GHC_EXPAND_IMPL
2329
2330 #ifdef GHC_WITH_EXCEPTIONS
2331 template <class Source, typename>
path(const Source & source,const std::locale & loc,format fmt)2332 inline path::path(const Source& source, const std::locale& loc, format fmt)
2333 : path(source, fmt)
2334 {
2335 std::string locName = loc.name();
2336 if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) {
2337 throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported));
2338 }
2339 }
2340
2341 template <class InputIterator>
path(InputIterator first,InputIterator last,const std::locale & loc,format fmt)2342 inline path::path(InputIterator first, InputIterator last, const std::locale& loc, format fmt)
2343 : path(std::basic_string<typename std::iterator_traits<InputIterator>::value_type>(first, last), fmt)
2344 {
2345 std::string locName = loc.name();
2346 if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) {
2347 throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported));
2348 }
2349 }
2350 #endif
2351
2352 #ifdef GHC_EXPAND_IMPL
2353
~path()2354 GHC_INLINE path::~path() {}
2355
2356 //-----------------------------------------------------------------------------
2357 // 30.10.8.4.2 assignments
2358
operator =(const path & p)2359 GHC_INLINE path& path::operator=(const path& p)
2360 {
2361 _path = p._path;
2362 #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
2363 _prefixLength = p._prefixLength;
2364 #endif
2365 return *this;
2366 }
2367
operator =(path && p)2368 GHC_INLINE path& path::operator=(path&& p) noexcept
2369 {
2370 _path = std::move(p._path);
2371 #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
2372 _prefixLength = p._prefixLength;
2373 #endif
2374 return *this;
2375 }
2376
operator =(path::string_type && source)2377 GHC_INLINE path& path::operator=(path::string_type&& source)
2378 {
2379 return assign(source);
2380 }
2381
assign(path::string_type && source)2382 GHC_INLINE path& path::assign(path::string_type&& source)
2383 {
2384 _path = std::move(source);
2385 postprocess_path_with_format(native_format);
2386 return *this;
2387 }
2388
2389 #endif // GHC_EXPAND_IMPL
2390
2391 template <class Source>
operator =(const Source & source)2392 inline path& path::operator=(const Source& source)
2393 {
2394 return assign(source);
2395 }
2396
2397 template <class Source>
assign(const Source & source)2398 inline path& path::assign(const Source& source)
2399 {
2400 #ifdef GHC_USE_WCHAR_T
2401 _path.assign(detail::toWChar(source));
2402 #else
2403 _path.assign(detail::toUtf8(source));
2404 #endif
2405 postprocess_path_with_format(native_format);
2406 return *this;
2407 }
2408
2409 template <>
assign(const path & source)2410 inline path& path::assign<path>(const path& source)
2411 {
2412 _path = source._path;
2413 #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
2414 _prefixLength = source._prefixLength;
2415 #endif
2416 return *this;
2417 }
2418
2419 template <class InputIterator>
assign(InputIterator first,InputIterator last)2420 inline path& path::assign(InputIterator first, InputIterator last)
2421 {
2422 _path.assign(first, last);
2423 postprocess_path_with_format(native_format);
2424 return *this;
2425 }
2426
2427 #ifdef GHC_EXPAND_IMPL
2428
2429 //-----------------------------------------------------------------------------
2430 // 30.10.8.4.3 appends
2431
operator /=(const path & p)2432 GHC_INLINE path& path::operator/=(const path& p)
2433 {
2434 if (p.empty()) {
2435 // was: if ((!has_root_directory() && is_absolute()) || has_filename())
2436 if (!_path.empty() && _path[_path.length() - 1] != preferred_separator && _path[_path.length() - 1] != ':') {
2437 _path += preferred_separator;
2438 }
2439 return *this;
2440 }
2441 if ((p.is_absolute() && (_path != root_name()._path || p._path != "/")) || (p.has_root_name() && p.root_name() != root_name())) {
2442 assign(p);
2443 return *this;
2444 }
2445 if (p.has_root_directory()) {
2446 assign(root_name());
2447 }
2448 else if ((!has_root_directory() && is_absolute()) || has_filename()) {
2449 _path += preferred_separator;
2450 }
2451 auto iter = p.begin();
2452 bool first = true;
2453 if (p.has_root_name()) {
2454 ++iter;
2455 }
2456 while (iter != p.end()) {
2457 if (!first && !(!_path.empty() && _path[_path.length() - 1] == preferred_separator)) {
2458 _path += preferred_separator;
2459 }
2460 first = false;
2461 _path += (*iter++).native();
2462 }
2463 check_long_path();
2464 return *this;
2465 }
2466
append_name(const value_type * name)2467 GHC_INLINE void path::append_name(const value_type* name)
2468 {
2469 if (_path.empty()) {
2470 this->operator/=(path(name));
2471 }
2472 else {
2473 if (_path.back() != path::preferred_separator) {
2474 _path.push_back(path::preferred_separator);
2475 }
2476 _path += name;
2477 check_long_path();
2478 }
2479 }
2480
2481 #endif // GHC_EXPAND_IMPL
2482
2483 template <class Source>
operator /=(const Source & source)2484 inline path& path::operator/=(const Source& source)
2485 {
2486 return append(source);
2487 }
2488
2489 template <class Source>
append(const Source & source)2490 inline path& path::append(const Source& source)
2491 {
2492 return this->operator/=(path(source));
2493 }
2494
2495 template <>
append(const path & p)2496 inline path& path::append<path>(const path& p)
2497 {
2498 return this->operator/=(p);
2499 }
2500
2501 template <class InputIterator>
append(InputIterator first,InputIterator last)2502 inline path& path::append(InputIterator first, InputIterator last)
2503 {
2504 std::basic_string<typename std::iterator_traits<InputIterator>::value_type> part(first, last);
2505 return append(part);
2506 }
2507
2508 #ifdef GHC_EXPAND_IMPL
2509
2510 //-----------------------------------------------------------------------------
2511 // 30.10.8.4.4 concatenation
2512
operator +=(const path & x)2513 GHC_INLINE path& path::operator+=(const path& x)
2514 {
2515 return concat(x._path);
2516 }
2517
operator +=(const string_type & x)2518 GHC_INLINE path& path::operator+=(const string_type& x)
2519 {
2520 return concat(x);
2521 }
2522
2523 #ifdef GHC_WITH_STRING_VIEW
operator +=(basic_string_view<value_type> x)2524 GHC_INLINE path& path::operator+=(basic_string_view<value_type> x)
2525 {
2526 return concat(x);
2527 }
2528 #endif
2529
operator +=(const value_type * x)2530 GHC_INLINE path& path::operator+=(const value_type* x)
2531 {
2532 #ifdef GHC_WITH_STRING_VIEW
2533 basic_string_view<value_type> part(x);
2534 #else
2535 string_type part(x);
2536 #endif
2537 return concat(part);
2538 }
2539
operator +=(value_type x)2540 GHC_INLINE path& path::operator+=(value_type x)
2541 {
2542 #ifdef GHC_OS_WINDOWS
2543 if (x == generic_separator) {
2544 x = preferred_separator;
2545 }
2546 #endif
2547 if (_path.empty() || _path.back() != preferred_separator) {
2548 _path += x;
2549 }
2550 check_long_path();
2551 return *this;
2552 }
2553
2554 #endif // GHC_EXPAND_IMPL
2555
2556 template <class Source>
operator +=(const Source & x)2557 inline path::path_from_string<Source>& path::operator+=(const Source& x)
2558 {
2559 return concat(x);
2560 }
2561
2562 template <class EcharT>
operator +=(EcharT x)2563 inline path::path_type_EcharT<EcharT>& path::operator+=(EcharT x)
2564 {
2565 #ifdef GHC_WITH_STRING_VIEW
2566 basic_string_view<EcharT> part(&x, 1);
2567 #else
2568 std::basic_string<EcharT> part(1, x);
2569 #endif
2570 concat(part);
2571 return *this;
2572 }
2573
2574 template <class Source>
concat(const Source & x)2575 inline path& path::concat(const Source& x)
2576 {
2577 path p(x);
2578 _path += p._path;
2579 postprocess_path_with_format(native_format);
2580 return *this;
2581 }
2582 template <class InputIterator>
concat(InputIterator first,InputIterator last)2583 inline path& path::concat(InputIterator first, InputIterator last)
2584 {
2585 _path.append(first, last);
2586 postprocess_path_with_format(native_format);
2587 return *this;
2588 }
2589
2590 #ifdef GHC_EXPAND_IMPL
2591
2592 //-----------------------------------------------------------------------------
2593 // 30.10.8.4.5 modifiers
clear()2594 GHC_INLINE void path::clear() noexcept
2595 {
2596 _path.clear();
2597 #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
2598 _prefixLength = 0;
2599 #endif
2600 }
2601
make_preferred()2602 GHC_INLINE path& path::make_preferred()
2603 {
2604 // as this filesystem implementation only uses generic_format
2605 // internally, this must be a no-op
2606 return *this;
2607 }
2608
remove_filename()2609 GHC_INLINE path& path::remove_filename()
2610 {
2611 if (has_filename()) {
2612 _path.erase(_path.size() - filename()._path.size());
2613 }
2614 return *this;
2615 }
2616
replace_filename(const path & replacement)2617 GHC_INLINE path& path::replace_filename(const path& replacement)
2618 {
2619 remove_filename();
2620 return append(replacement);
2621 }
2622
replace_extension(const path & replacement)2623 GHC_INLINE path& path::replace_extension(const path& replacement)
2624 {
2625 if (has_extension()) {
2626 _path.erase(_path.size() - extension()._path.size());
2627 }
2628 if (!replacement.empty() && replacement._path[0] != '.') {
2629 _path += '.';
2630 }
2631 return concat(replacement);
2632 }
2633
swap(path & rhs)2634 GHC_INLINE void path::swap(path& rhs) noexcept
2635 {
2636 _path.swap(rhs._path);
2637 #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
2638 std::swap(_prefixLength, rhs._prefixLength);
2639 #endif
2640 }
2641
2642 //-----------------------------------------------------------------------------
2643 // 30.10.8.4.6, native format observers
native() const2644 GHC_INLINE const path::string_type& path::native() const noexcept
2645 {
2646 return _path;
2647 }
2648
c_str() const2649 GHC_INLINE const path::value_type* path::c_str() const noexcept
2650 {
2651 return native().c_str();
2652 }
2653
operator path::string_type() const2654 GHC_INLINE path::operator path::string_type() const
2655 {
2656 return native();
2657 }
2658
2659 #endif // GHC_EXPAND_IMPL
2660
2661 template <class EcharT, class traits, class Allocator>
string(const Allocator & a) const2662 inline std::basic_string<EcharT, traits, Allocator> path::string(const Allocator& a) const
2663 {
2664 #ifdef GHC_USE_WCHAR_T
2665 return detail::fromWChar<std::basic_string<EcharT, traits, Allocator>>(_path, a);
2666 #else
2667 return detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(_path, a);
2668 #endif
2669 }
2670
2671 #ifdef GHC_EXPAND_IMPL
2672
string() const2673 GHC_INLINE std::string path::string() const
2674 {
2675 #ifdef GHC_USE_WCHAR_T
2676 return detail::toUtf8(native());
2677 #else
2678 return native();
2679 #endif
2680 }
2681
wstring() const2682 GHC_INLINE std::wstring path::wstring() const
2683 {
2684 #ifdef GHC_USE_WCHAR_T
2685 return native();
2686 #else
2687 return detail::fromUtf8<std::wstring>(native());
2688 #endif
2689 }
2690
2691 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
u8string() const2692 GHC_INLINE std::u8string path::u8string() const
2693 {
2694 #ifdef GHC_USE_WCHAR_T
2695 return std::u8string(reinterpret_cast<const char8_t*>(detail::toUtf8(native()).c_str()));
2696 #else
2697 return std::u8string(reinterpret_cast<const char8_t*>(c_str()));
2698 #endif
2699 }
2700 #else
u8string() const2701 GHC_INLINE std::string path::u8string() const
2702 {
2703 #ifdef GHC_USE_WCHAR_T
2704 return detail::toUtf8(native());
2705 #else
2706 return native();
2707 #endif
2708 }
2709 #endif
2710
u16string() const2711 GHC_INLINE std::u16string path::u16string() const
2712 {
2713 // TODO: optimize
2714 return detail::fromUtf8<std::u16string>(string());
2715 }
2716
u32string() const2717 GHC_INLINE std::u32string path::u32string() const
2718 {
2719 // TODO: optimize
2720 return detail::fromUtf8<std::u32string>(string());
2721 }
2722
2723 #endif // GHC_EXPAND_IMPL
2724
2725 //-----------------------------------------------------------------------------
2726 // 30.10.8.4.7, generic format observers
2727 template <class EcharT, class traits, class Allocator>
generic_string(const Allocator & a) const2728 inline std::basic_string<EcharT, traits, Allocator> path::generic_string(const Allocator& a) const
2729 {
2730 #ifdef GHC_OS_WINDOWS
2731 #ifdef GHC_USE_WCHAR_T
2732 auto result = detail::fromWChar<std::basic_string<EcharT, traits, Allocator>, path::string_type>(_path, a);
2733 #else
2734 auto result = detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(_path, a);
2735 #endif
2736 for (auto& c : result) {
2737 if (c == preferred_separator) {
2738 c = generic_separator;
2739 }
2740 }
2741 return result;
2742 #else
2743 return detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(_path, a);
2744 #endif
2745 }
2746
2747 #ifdef GHC_EXPAND_IMPL
2748
generic_string() const2749 GHC_INLINE std::string path::generic_string() const
2750 {
2751 #ifdef GHC_OS_WINDOWS
2752 return generic_string<std::string::value_type, std::string::traits_type, std::string::allocator_type>();
2753 #else
2754 return _path;
2755 #endif
2756 }
2757
generic_wstring() const2758 GHC_INLINE std::wstring path::generic_wstring() const
2759 {
2760 #ifdef GHC_OS_WINDOWS
2761 return generic_string<std::wstring::value_type, std::wstring::traits_type, std::wstring::allocator_type>();
2762 #else
2763 return detail::fromUtf8<std::wstring>(_path);
2764 #endif
2765 } // namespace filesystem
2766
2767 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
generic_u8string() const2768 GHC_INLINE std::u8string path::generic_u8string() const
2769 {
2770 #ifdef GHC_OS_WINDOWS
2771 return generic_string<std::u8string::value_type, std::u8string::traits_type, std::u8string::allocator_type>();
2772 #else
2773 return std::u8string(reinterpret_cast<const char8_t*>(_path.c_str()));
2774 #endif
2775 }
2776 #else
generic_u8string() const2777 GHC_INLINE std::string path::generic_u8string() const
2778 {
2779 #ifdef GHC_OS_WINDOWS
2780 return generic_string<std::string::value_type, std::string::traits_type, std::string::allocator_type>();
2781 #else
2782 return _path;
2783 #endif
2784 }
2785 #endif
2786
generic_u16string() const2787 GHC_INLINE std::u16string path::generic_u16string() const
2788 {
2789 #ifdef GHC_OS_WINDOWS
2790 return generic_string<std::u16string::value_type, std::u16string::traits_type, std::u16string::allocator_type>();
2791 #else
2792 return detail::fromUtf8<std::u16string>(_path);
2793 #endif
2794 }
2795
generic_u32string() const2796 GHC_INLINE std::u32string path::generic_u32string() const
2797 {
2798 #ifdef GHC_OS_WINDOWS
2799 return generic_string<std::u32string::value_type, std::u32string::traits_type, std::u32string::allocator_type>();
2800 #else
2801 return detail::fromUtf8<std::u32string>(_path);
2802 #endif
2803 }
2804
2805 //-----------------------------------------------------------------------------
2806 // 30.10.8.4.8, compare
compare(const path & p) const2807 GHC_INLINE int path::compare(const path& p) const noexcept
2808 {
2809 #ifdef LWG_2936_BEHAVIOUR
2810 auto rnl1 = root_name_length();
2811 auto rnl2 = p.root_name_length();
2812 #ifdef GHC_OS_WINDOWS
2813 auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2);
2814 #else
2815 auto rnc = _path.compare(0, rnl1, p._path, 0, (std::min(rnl1, rnl2)));
2816 #endif
2817 if (rnc) {
2818 return rnc;
2819 }
2820 bool hrd1 = has_root_directory(), hrd2 = p.has_root_directory();
2821 if (hrd1 != hrd2) {
2822 return hrd1 ? 1 : -1;
2823 }
2824 if (hrd1) {
2825 ++rnl1;
2826 ++rnl2;
2827 }
2828 auto iter1 = _path.begin() + static_cast<int>(rnl1);
2829 auto iter2 = p._path.begin() + static_cast<int>(rnl2);
2830 while (iter1 != _path.end() && iter2 != p._path.end() && *iter1 == *iter2) {
2831 ++iter1;
2832 ++iter2;
2833 }
2834 if (iter1 == _path.end()) {
2835 return iter2 == p._path.end() ? 0 : -1;
2836 }
2837 if (iter2 == p._path.end()) {
2838 return 1;
2839 }
2840 if (*iter1 == preferred_separator) {
2841 return -1;
2842 }
2843 if (*iter2 == preferred_separator) {
2844 return 1;
2845 }
2846 return *iter1 < *iter2 ? -1 : 1;
2847 #else // LWG_2936_BEHAVIOUR
2848 #ifdef GHC_OS_WINDOWS
2849 auto rnl1 = root_name_length();
2850 auto rnl2 = p.root_name_length();
2851 auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2);
2852 if (rnc) {
2853 return rnc;
2854 }
2855 return _path.compare(rnl1, std::string::npos, p._path, rnl2, std::string::npos);
2856 #else
2857 return _path.compare(p._path);
2858 #endif
2859 #endif
2860 }
2861
compare(const string_type & s) const2862 GHC_INLINE int path::compare(const string_type& s) const
2863 {
2864 return compare(path(s));
2865 }
2866
2867 #ifdef GHC_WITH_STRING_VIEW
compare(basic_string_view<value_type> s) const2868 GHC_INLINE int path::compare(basic_string_view<value_type> s) const
2869 {
2870 return compare(path(s));
2871 }
2872 #endif
2873
compare(const value_type * s) const2874 GHC_INLINE int path::compare(const value_type* s) const
2875 {
2876 return compare(path(s));
2877 }
2878
2879 //-----------------------------------------------------------------------------
2880 // 30.10.8.4.9, decomposition
2881 #ifdef GHC_OS_WINDOWS
handle_prefixes()2882 GHC_INLINE void path::handle_prefixes()
2883 {
2884 #if defined(GHC_WIN_AUTO_PREFIX_LONG_PATH)
2885 _prefixLength = 0;
2886 if (_path.length() >= 6 && _path[2] == '?' && std::toupper(static_cast<unsigned char>(_path[4])) >= 'A' && std::toupper(static_cast<unsigned char>(_path[4])) <= 'Z' && _path[5] == ':') {
2887 if (detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\"))) || detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\??\\")))) {
2888 _prefixLength = 4;
2889 }
2890 }
2891 #endif // GHC_WIN_AUTO_PREFIX_LONG_PATH
2892 }
2893 #endif
2894
root_name_length() const2895 GHC_INLINE path::string_type::size_type path::root_name_length() const noexcept
2896 {
2897 #ifdef GHC_OS_WINDOWS
2898 if (_path.length() >= _prefixLength + 2 && std::toupper(static_cast<unsigned char>(_path[_prefixLength])) >= 'A' && std::toupper(static_cast<unsigned char>(_path[_prefixLength])) <= 'Z' && _path[_prefixLength + 1] == ':') {
2899 return 2;
2900 }
2901 #endif
2902 if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator && std::isprint(_path[_prefixLength + 2])) {
2903 impl_string_type::size_type pos = _path.find(preferred_separator, _prefixLength + 3);
2904 if (pos == impl_string_type::npos) {
2905 return _path.length();
2906 }
2907 else {
2908 return pos;
2909 }
2910 }
2911 return 0;
2912 }
2913
root_name() const2914 GHC_INLINE path path::root_name() const
2915 {
2916 return path(_path.substr(_prefixLength, root_name_length()), native_format);
2917 }
2918
root_directory() const2919 GHC_INLINE path path::root_directory() const
2920 {
2921 if (has_root_directory()) {
2922 static const path _root_dir(std::string(1, preferred_separator), native_format);
2923 return _root_dir;
2924 }
2925 return path();
2926 }
2927
root_path() const2928 GHC_INLINE path path::root_path() const
2929 {
2930 return path(root_name().string() + root_directory().string(), native_format);
2931 }
2932
relative_path() const2933 GHC_INLINE path path::relative_path() const
2934 {
2935 auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0);
2936 return path(_path.substr((std::min)(rootPathLen, _path.length())), generic_format);
2937 }
2938
parent_path() const2939 GHC_INLINE path path::parent_path() const
2940 {
2941 auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0);
2942 if (rootPathLen < _path.length()) {
2943 if (empty()) {
2944 return path();
2945 }
2946 else {
2947 auto piter = end();
2948 auto iter = piter.decrement(_path.end());
2949 if (iter > _path.begin() + static_cast<long>(rootPathLen) && *iter != preferred_separator) {
2950 --iter;
2951 }
2952 return path(_path.begin(), iter, native_format);
2953 }
2954 }
2955 else {
2956 return *this;
2957 }
2958 }
2959
filename() const2960 GHC_INLINE path path::filename() const
2961 {
2962 return !has_relative_path() ? path() : path(*--end());
2963 }
2964
stem() const2965 GHC_INLINE path path::stem() const
2966 {
2967 impl_string_type fn = filename().native();
2968 if (fn != "." && fn != "..") {
2969 impl_string_type::size_type pos = fn.rfind('.');
2970 if (pos != impl_string_type::npos && pos > 0) {
2971 return path{fn.substr(0, pos), native_format};
2972 }
2973 }
2974 return path{fn, native_format};
2975 }
2976
extension() const2977 GHC_INLINE path path::extension() const
2978 {
2979 if (has_relative_path()) {
2980 auto iter = end();
2981 const auto& fn = *--iter;
2982 impl_string_type::size_type pos = fn._path.rfind('.');
2983 if (pos != std::string::npos && pos > 0) {
2984 return path(fn._path.substr(pos), native_format);
2985 }
2986 }
2987 return path();
2988 }
2989
2990 #ifdef GHC_OS_WINDOWS
2991 namespace detail {
has_executable_extension(const path & p)2992 GHC_INLINE bool has_executable_extension(const path& p)
2993 {
2994 if (p.has_relative_path()) {
2995 auto iter = p.end();
2996 const auto& fn = *--iter;
2997 auto pos = fn._path.find_last_of('.');
2998 if (pos == std::string::npos || pos == 0 || fn._path.length() - pos != 3) {
2999 return false;
3000 }
3001 const path::value_type* ext = fn._path.c_str() + pos + 1;
3002 if (detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("exe")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("cmd")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("bat")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("com"))) {
3003 return true;
3004 }
3005 }
3006 return false;
3007 }
3008 } // namespace detail
3009 #endif
3010
3011 //-----------------------------------------------------------------------------
3012 // 30.10.8.4.10, query
empty() const3013 GHC_INLINE bool path::empty() const noexcept
3014 {
3015 return _path.empty();
3016 }
3017
has_root_name() const3018 GHC_INLINE bool path::has_root_name() const
3019 {
3020 return root_name_length() > 0;
3021 }
3022
has_root_directory() const3023 GHC_INLINE bool path::has_root_directory() const
3024 {
3025 auto rootLen = _prefixLength + root_name_length();
3026 return (_path.length() > rootLen && _path[rootLen] == preferred_separator);
3027 }
3028
has_root_path() const3029 GHC_INLINE bool path::has_root_path() const
3030 {
3031 return has_root_name() || has_root_directory();
3032 }
3033
has_relative_path() const3034 GHC_INLINE bool path::has_relative_path() const
3035 {
3036 auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0);
3037 return rootPathLen < _path.length();
3038 }
3039
has_parent_path() const3040 GHC_INLINE bool path::has_parent_path() const
3041 {
3042 return !parent_path().empty();
3043 }
3044
has_filename() const3045 GHC_INLINE bool path::has_filename() const
3046 {
3047 return has_relative_path() && !filename().empty();
3048 }
3049
has_stem() const3050 GHC_INLINE bool path::has_stem() const
3051 {
3052 return !stem().empty();
3053 }
3054
has_extension() const3055 GHC_INLINE bool path::has_extension() const
3056 {
3057 return !extension().empty();
3058 }
3059
is_absolute() const3060 GHC_INLINE bool path::is_absolute() const
3061 {
3062 #ifdef GHC_OS_WINDOWS
3063 return has_root_name() && has_root_directory();
3064 #else
3065 return has_root_directory();
3066 #endif
3067 }
3068
is_relative() const3069 GHC_INLINE bool path::is_relative() const
3070 {
3071 return !is_absolute();
3072 }
3073
3074 //-----------------------------------------------------------------------------
3075 // 30.10.8.4.11, generation
lexically_normal() const3076 GHC_INLINE path path::lexically_normal() const
3077 {
3078 path dest;
3079 bool lastDotDot = false;
3080 for (string_type s : *this) {
3081 if (s == ".") {
3082 dest /= "";
3083 continue;
3084 }
3085 else if (s == ".." && !dest.empty()) {
3086 auto root = root_path();
3087 if (dest == root) {
3088 continue;
3089 }
3090 else if (*(--dest.end()) != "..") {
3091 if (dest._path.back() == preferred_separator) {
3092 dest._path.pop_back();
3093 }
3094 dest.remove_filename();
3095 continue;
3096 }
3097 }
3098 if (!(s.empty() && lastDotDot)) {
3099 dest /= s;
3100 }
3101 lastDotDot = s == "..";
3102 }
3103 if (dest.empty()) {
3104 dest = ".";
3105 }
3106 return dest;
3107 }
3108
lexically_relative(const path & base) const3109 GHC_INLINE path path::lexically_relative(const path& base) const
3110 {
3111 if (root_name() != base.root_name() || is_absolute() != base.is_absolute() || (!has_root_directory() && base.has_root_directory())) {
3112 return path();
3113 }
3114 const_iterator a = begin(), b = base.begin();
3115 while (a != end() && b != base.end() && *a == *b) {
3116 ++a;
3117 ++b;
3118 }
3119 if (a == end() && b == base.end()) {
3120 return path(".");
3121 }
3122 int count = 0;
3123 for (const auto& element : input_iterator_range<const_iterator>(b, base.end())) {
3124 if (element != "." && element != "" && element != "..") {
3125 ++count;
3126 }
3127 else if (element == "..") {
3128 --count;
3129 }
3130 }
3131 if (count < 0) {
3132 return path();
3133 }
3134 path result;
3135 for (int i = 0; i < count; ++i) {
3136 result /= "..";
3137 }
3138 for (const auto& element : input_iterator_range<const_iterator>(a, end())) {
3139 result /= element;
3140 }
3141 return result;
3142 }
3143
lexically_proximate(const path & base) const3144 GHC_INLINE path path::lexically_proximate(const path& base) const
3145 {
3146 path result = lexically_relative(base);
3147 return result.empty() ? *this : result;
3148 }
3149
3150 //-----------------------------------------------------------------------------
3151 // 30.10.8.5, iterators
iterator()3152 GHC_INLINE path::iterator::iterator() {}
3153
iterator(const path & p,const impl_string_type::const_iterator & pos)3154 GHC_INLINE path::iterator::iterator(const path& p, const impl_string_type::const_iterator& pos)
3155 : _first(p._path.begin())
3156 , _last(p._path.end())
3157 , _prefix(_first + static_cast<string_type::difference_type>(p._prefixLength))
3158 , _root(p.has_root_directory() ? _first + static_cast<string_type::difference_type>(p._prefixLength + p.root_name_length()) : _last)
3159 , _iter(pos)
3160 {
3161 if(pos != _last) {
3162 updateCurrent();
3163 }
3164 }
3165
increment(const path::impl_string_type::const_iterator & pos) const3166 GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(const path::impl_string_type::const_iterator& pos) const
3167 {
3168 path::impl_string_type::const_iterator i = pos;
3169 bool fromStart = i == _first || i == _prefix;
3170 if (i != _last) {
3171 if (fromStart && i == _first && _prefix > _first) {
3172 i = _prefix;
3173 }
3174 else if (*i++ == preferred_separator) {
3175 // we can only sit on a slash if it is a network name or a root
3176 if (i != _last && *i == preferred_separator) {
3177 if (fromStart && !(i + 1 != _last && *(i + 1) == preferred_separator)) {
3178 // leadind double slashes detected, treat this and the
3179 // following until a slash as one unit
3180 i = std::find(++i, _last, preferred_separator);
3181 }
3182 else {
3183 // skip redundant slashes
3184 while (i != _last && *i == preferred_separator) {
3185 ++i;
3186 }
3187 }
3188 }
3189 }
3190 else {
3191 if (fromStart && i != _last && *i == ':') {
3192 ++i;
3193 }
3194 else {
3195 i = std::find(i, _last, preferred_separator);
3196 }
3197 }
3198 }
3199 return i;
3200 }
3201
decrement(const path::impl_string_type::const_iterator & pos) const3202 GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(const path::impl_string_type::const_iterator& pos) const
3203 {
3204 path::impl_string_type::const_iterator i = pos;
3205 if (i != _first) {
3206 --i;
3207 // if this is now the root slash or the trailing slash, we are done,
3208 // else check for network name
3209 if (i != _root && (pos != _last || *i != preferred_separator)) {
3210 #ifdef GHC_OS_WINDOWS
3211 static const impl_string_type seps = GHC_PLATFORM_LITERAL("\\:");
3212 i = std::find_first_of(std::reverse_iterator<path::impl_string_type::const_iterator>(i), std::reverse_iterator<path::impl_string_type::const_iterator>(_first), seps.begin(), seps.end()).base();
3213 if (i > _first && *i == ':') {
3214 i++;
3215 }
3216 #else
3217 i = std::find(std::reverse_iterator<path::impl_string_type::const_iterator>(i), std::reverse_iterator<path::impl_string_type::const_iterator>(_first), preferred_separator).base();
3218 #endif
3219 // Now we have to check if this is a network name
3220 if (i - _first == 2 && *_first == preferred_separator && *(_first + 1) == preferred_separator) {
3221 i -= 2;
3222 }
3223 }
3224 }
3225 return i;
3226 }
3227
updateCurrent()3228 GHC_INLINE void path::iterator::updateCurrent()
3229 {
3230 if ((_iter == _last) || (_iter != _first && _iter != _last && (*_iter == preferred_separator && _iter != _root) && (_iter + 1 == _last))) {
3231 _current.clear();
3232 }
3233 else {
3234 _current.assign(_iter, increment(_iter));
3235 }
3236 }
3237
operator ++()3238 GHC_INLINE path::iterator& path::iterator::operator++()
3239 {
3240 _iter = increment(_iter);
3241 while (_iter != _last && // we didn't reach the end
3242 _iter != _root && // this is not a root position
3243 *_iter == preferred_separator && // we are on a separator
3244 (_iter + 1) != _last // the slash is not the last char
3245 ) {
3246 ++_iter;
3247 }
3248 updateCurrent();
3249 return *this;
3250 }
3251
operator ++(int)3252 GHC_INLINE path::iterator path::iterator::operator++(int)
3253 {
3254 path::iterator i{*this};
3255 ++(*this);
3256 return i;
3257 }
3258
operator --()3259 GHC_INLINE path::iterator& path::iterator::operator--()
3260 {
3261 _iter = decrement(_iter);
3262 updateCurrent();
3263 return *this;
3264 }
3265
operator --(int)3266 GHC_INLINE path::iterator path::iterator::operator--(int)
3267 {
3268 auto i = *this;
3269 --(*this);
3270 return i;
3271 }
3272
operator ==(const path::iterator & other) const3273 GHC_INLINE bool path::iterator::operator==(const path::iterator& other) const
3274 {
3275 return _iter == other._iter;
3276 }
3277
operator !=(const path::iterator & other) const3278 GHC_INLINE bool path::iterator::operator!=(const path::iterator& other) const
3279 {
3280 return _iter != other._iter;
3281 }
3282
operator *() const3283 GHC_INLINE path::iterator::reference path::iterator::operator*() const
3284 {
3285 return _current;
3286 }
3287
operator ->() const3288 GHC_INLINE path::iterator::pointer path::iterator::operator->() const
3289 {
3290 return &_current;
3291 }
3292
begin() const3293 GHC_INLINE path::iterator path::begin() const
3294 {
3295 return iterator(*this, _path.begin());
3296 }
3297
end() const3298 GHC_INLINE path::iterator path::end() const
3299 {
3300 return iterator(*this, _path.end());
3301 }
3302
3303 //-----------------------------------------------------------------------------
3304 // 30.10.8.6, path non-member functions
swap(path & lhs,path & rhs)3305 GHC_INLINE void swap(path& lhs, path& rhs) noexcept
3306 {
3307 swap(lhs._path, rhs._path);
3308 }
3309
hash_value(const path & p)3310 GHC_INLINE size_t hash_value(const path& p) noexcept
3311 {
3312 return std::hash<std::string>()(p.generic_string());
3313 }
3314
3315 #ifdef GHC_HAS_THREEWAY_COMP
operator <=>(const path & lhs,const path & rhs)3316 GHC_INLINE std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept
3317 {
3318 return lhs.compare(rhs) <=> 0;
3319 }
3320 #endif
3321
operator ==(const path & lhs,const path & rhs)3322 GHC_INLINE bool operator==(const path& lhs, const path& rhs) noexcept
3323 {
3324 return lhs.compare(rhs) == 0;
3325 }
3326
operator !=(const path & lhs,const path & rhs)3327 GHC_INLINE bool operator!=(const path& lhs, const path& rhs) noexcept
3328 {
3329 return !(lhs == rhs);
3330 }
3331
operator <(const path & lhs,const path & rhs)3332 GHC_INLINE bool operator<(const path& lhs, const path& rhs) noexcept
3333 {
3334 return lhs.compare(rhs) < 0;
3335 }
3336
operator <=(const path & lhs,const path & rhs)3337 GHC_INLINE bool operator<=(const path& lhs, const path& rhs) noexcept
3338 {
3339 return lhs.compare(rhs) <= 0;
3340 }
3341
operator >(const path & lhs,const path & rhs)3342 GHC_INLINE bool operator>(const path& lhs, const path& rhs) noexcept
3343 {
3344 return lhs.compare(rhs) > 0;
3345 }
3346
operator >=(const path & lhs,const path & rhs)3347 GHC_INLINE bool operator>=(const path& lhs, const path& rhs) noexcept
3348 {
3349 return lhs.compare(rhs) >= 0;
3350 }
3351
operator /(const path & lhs,const path & rhs)3352 GHC_INLINE path operator/(const path& lhs, const path& rhs)
3353 {
3354 path result(lhs);
3355 result /= rhs;
3356 return result;
3357 }
3358
3359 #endif // GHC_EXPAND_IMPL
3360
3361 //-----------------------------------------------------------------------------
3362 // 30.10.8.6.1 path inserter and extractor
3363 template <class charT, class traits>
operator <<(std::basic_ostream<charT,traits> & os,const path & p)3364 inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, const path& p)
3365 {
3366 os << "\"";
3367 auto ps = p.string<charT, traits>();
3368 for (auto c : ps) {
3369 if (c == '"' || c == '\\') {
3370 os << '\\';
3371 }
3372 os << c;
3373 }
3374 os << "\"";
3375 return os;
3376 }
3377
3378 template <class charT, class traits>
operator >>(std::basic_istream<charT,traits> & is,path & p)3379 inline std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& is, path& p)
3380 {
3381 std::basic_string<charT, traits> tmp;
3382 charT c;
3383 is >> c;
3384 if (c == '"') {
3385 auto sf = is.flags();
3386 is >> std::noskipws;
3387 while (is) {
3388 auto c2 = is.get();
3389 if (is) {
3390 if (c2 == '\\') {
3391 c2 = is.get();
3392 if (is) {
3393 tmp += static_cast<charT>(c2);
3394 }
3395 }
3396 else if (c2 == '"') {
3397 break;
3398 }
3399 else {
3400 tmp += static_cast<charT>(c2);
3401 }
3402 }
3403 }
3404 if ((sf & std::ios_base::skipws) == std::ios_base::skipws) {
3405 is >> std::skipws;
3406 }
3407 p = path(tmp);
3408 }
3409 else {
3410 is >> tmp;
3411 p = path(static_cast<charT>(c) + tmp);
3412 }
3413 return is;
3414 }
3415
3416 #ifdef GHC_EXPAND_IMPL
3417
3418 //-----------------------------------------------------------------------------
3419 // 30.10.9 Class filesystem_error
filesystem_error(const std::string & what_arg,std::error_code ec)3420 GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, std::error_code ec)
3421 : std::system_error(ec, what_arg)
3422 , _what_arg(what_arg)
3423 , _ec(ec)
3424 {
3425 }
3426
filesystem_error(const std::string & what_arg,const path & p1,std::error_code ec)3427 GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec)
3428 : std::system_error(ec, what_arg)
3429 , _what_arg(what_arg)
3430 , _ec(ec)
3431 , _p1(p1)
3432 {
3433 if (!_p1.empty()) {
3434 _what_arg += ": '" + _p1.string() + "'";
3435 }
3436 }
3437
filesystem_error(const std::string & what_arg,const path & p1,const path & p2,std::error_code ec)3438 GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec)
3439 : std::system_error(ec, what_arg)
3440 , _what_arg(what_arg)
3441 , _ec(ec)
3442 , _p1(p1)
3443 , _p2(p2)
3444 {
3445 if (!_p1.empty()) {
3446 _what_arg += ": '" + _p1.string() + "'";
3447 }
3448 if (!_p2.empty()) {
3449 _what_arg += ", '" + _p2.string() + "'";
3450 }
3451 }
3452
path1() const3453 GHC_INLINE const path& filesystem_error::path1() const noexcept
3454 {
3455 return _p1;
3456 }
3457
path2() const3458 GHC_INLINE const path& filesystem_error::path2() const noexcept
3459 {
3460 return _p2;
3461 }
3462
what() const3463 GHC_INLINE const char* filesystem_error::what() const noexcept
3464 {
3465 return _what_arg.c_str();
3466 }
3467
3468 //-----------------------------------------------------------------------------
3469 // 30.10.15, filesystem operations
3470 #ifdef GHC_WITH_EXCEPTIONS
absolute(const path & p)3471 GHC_INLINE path absolute(const path& p)
3472 {
3473 std::error_code ec;
3474 path result = absolute(p, ec);
3475 if (ec) {
3476 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
3477 }
3478 return result;
3479 }
3480 #endif
3481
absolute(const path & p,std::error_code & ec)3482 GHC_INLINE path absolute(const path& p, std::error_code& ec)
3483 {
3484 ec.clear();
3485 #ifdef GHC_OS_WINDOWS
3486 if (p.empty()) {
3487 return absolute(current_path(ec), ec) / "";
3488 }
3489 ULONG size = ::GetFullPathNameW(GHC_NATIVEWP(p), 0, 0, 0);
3490 if (size) {
3491 std::vector<wchar_t> buf(size, 0);
3492 ULONG s2 = GetFullPathNameW(GHC_NATIVEWP(p), size, buf.data(), nullptr);
3493 if (s2 && s2 < size) {
3494 path result = path(std::wstring(buf.data(), s2));
3495 if (p.filename() == ".") {
3496 result /= ".";
3497 }
3498 return result;
3499 }
3500 }
3501 ec = detail::make_system_error();
3502 return path();
3503 #else
3504 path base = current_path(ec);
3505 if (!ec) {
3506 if (p.empty()) {
3507 return base / p;
3508 }
3509 if (p.has_root_name()) {
3510 if (p.has_root_directory()) {
3511 return p;
3512 }
3513 else {
3514 return p.root_name() / base.root_directory() / base.relative_path() / p.relative_path();
3515 }
3516 }
3517 else {
3518 if (p.has_root_directory()) {
3519 return base.root_name() / p;
3520 }
3521 else {
3522 return base / p;
3523 }
3524 }
3525 }
3526 ec = detail::make_system_error();
3527 return path();
3528 #endif
3529 }
3530
3531 #ifdef GHC_WITH_EXCEPTIONS
canonical(const path & p)3532 GHC_INLINE path canonical(const path& p)
3533 {
3534 std::error_code ec;
3535 auto result = canonical(p, ec);
3536 if (ec) {
3537 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
3538 }
3539 return result;
3540 }
3541 #endif
3542
canonical(const path & p,std::error_code & ec)3543 GHC_INLINE path canonical(const path& p, std::error_code& ec)
3544 {
3545 if (p.empty()) {
3546 ec = detail::make_error_code(detail::portable_error::not_found);
3547 return path();
3548 }
3549 path work = p.is_absolute() ? p : absolute(p, ec);
3550 path result;
3551
3552 auto fs = status(work, ec);
3553 if (ec) {
3554 return path();
3555 }
3556 if (fs.type() == file_type::not_found) {
3557 ec = detail::make_error_code(detail::portable_error::not_found);
3558 return path();
3559 }
3560 bool redo;
3561 do {
3562 auto rootPathLen = work._prefixLength + work.root_name_length() + (work.has_root_directory() ? 1 : 0);
3563 redo = false;
3564 result.clear();
3565 for (auto pe : work) {
3566 if (pe.empty() || pe == ".") {
3567 continue;
3568 }
3569 else if (pe == "..") {
3570 result = result.parent_path();
3571 continue;
3572 }
3573 else if ((result / pe).string().length() <= rootPathLen) {
3574 result /= pe;
3575 continue;
3576 }
3577 auto sls = symlink_status(result / pe, ec);
3578 if (ec) {
3579 return path();
3580 }
3581 if (is_symlink(sls)) {
3582 redo = true;
3583 auto target = read_symlink(result / pe, ec);
3584 if (ec) {
3585 return path();
3586 }
3587 if (target.is_absolute()) {
3588 result = target;
3589 continue;
3590 }
3591 else {
3592 result /= target;
3593 continue;
3594 }
3595 }
3596 else {
3597 result /= pe;
3598 }
3599 }
3600 work = result;
3601 } while (redo);
3602 ec.clear();
3603 return result;
3604 }
3605
3606 #ifdef GHC_WITH_EXCEPTIONS
copy(const path & from,const path & to)3607 GHC_INLINE void copy(const path& from, const path& to)
3608 {
3609 copy(from, to, copy_options::none);
3610 }
3611
copy(const path & from,const path & to,copy_options options)3612 GHC_INLINE void copy(const path& from, const path& to, copy_options options)
3613 {
3614 std::error_code ec;
3615 copy(from, to, options, ec);
3616 if (ec) {
3617 throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec);
3618 }
3619 }
3620 #endif
3621
copy(const path & from,const path & to,std::error_code & ec)3622 GHC_INLINE void copy(const path& from, const path& to, std::error_code& ec) noexcept
3623 {
3624 copy(from, to, copy_options::none, ec);
3625 }
3626
copy(const path & from,const path & to,copy_options options,std::error_code & ec)3627 GHC_INLINE void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept
3628 {
3629 std::error_code tec;
3630 file_status fs_from, fs_to;
3631 ec.clear();
3632 if ((options & (copy_options::skip_symlinks | copy_options::copy_symlinks | copy_options::create_symlinks)) != copy_options::none) {
3633 fs_from = symlink_status(from, ec);
3634 }
3635 else {
3636 fs_from = status(from, ec);
3637 }
3638 if (!exists(fs_from)) {
3639 if (!ec) {
3640 ec = detail::make_error_code(detail::portable_error::not_found);
3641 }
3642 return;
3643 }
3644 if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none) {
3645 fs_to = symlink_status(to, tec);
3646 }
3647 else {
3648 fs_to = status(to, tec);
3649 }
3650 if (is_other(fs_from) || is_other(fs_to) || (is_directory(fs_from) && is_regular_file(fs_to)) || (exists(fs_to) && equivalent(from, to, ec))) {
3651 ec = detail::make_error_code(detail::portable_error::invalid_argument);
3652 }
3653 else if (is_symlink(fs_from)) {
3654 if ((options & copy_options::skip_symlinks) == copy_options::none) {
3655 if (!exists(fs_to) && (options & copy_options::copy_symlinks) != copy_options::none) {
3656 copy_symlink(from, to, ec);
3657 }
3658 else {
3659 ec = detail::make_error_code(detail::portable_error::invalid_argument);
3660 }
3661 }
3662 }
3663 else if (is_regular_file(fs_from)) {
3664 if ((options & copy_options::directories_only) == copy_options::none) {
3665 if ((options & copy_options::create_symlinks) != copy_options::none) {
3666 create_symlink(from.is_absolute() ? from : canonical(from, ec), to, ec);
3667 }
3668 #ifndef GHC_OS_WEB
3669 else if ((options & copy_options::create_hard_links) != copy_options::none) {
3670 create_hard_link(from, to, ec);
3671 }
3672 #endif
3673 else if (is_directory(fs_to)) {
3674 copy_file(from, to / from.filename(), options, ec);
3675 }
3676 else {
3677 copy_file(from, to, options, ec);
3678 }
3679 }
3680 }
3681 #ifdef LWG_2682_BEHAVIOUR
3682 else if (is_directory(fs_from) && (options & copy_options::create_symlinks) != copy_options::none) {
3683 ec = detail::make_error_code(detail::portable_error::is_a_directory);
3684 }
3685 #endif
3686 else if (is_directory(fs_from) && (options == copy_options::none || (options & copy_options::recursive) != copy_options::none)) {
3687 if (!exists(fs_to)) {
3688 create_directory(to, from, ec);
3689 if (ec) {
3690 return;
3691 }
3692 }
3693 for (auto iter = directory_iterator(from, ec); iter != directory_iterator(); iter.increment(ec)) {
3694 if (!ec) {
3695 copy(iter->path(), to / iter->path().filename(), options | static_cast<copy_options>(0x8000), ec);
3696 }
3697 if (ec) {
3698 return;
3699 }
3700 }
3701 }
3702 return;
3703 }
3704
3705 #ifdef GHC_WITH_EXCEPTIONS
copy_file(const path & from,const path & to)3706 GHC_INLINE bool copy_file(const path& from, const path& to)
3707 {
3708 return copy_file(from, to, copy_options::none);
3709 }
3710
copy_file(const path & from,const path & to,copy_options option)3711 GHC_INLINE bool copy_file(const path& from, const path& to, copy_options option)
3712 {
3713 std::error_code ec;
3714 auto result = copy_file(from, to, option, ec);
3715 if (ec) {
3716 throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec);
3717 }
3718 return result;
3719 }
3720 #endif
3721
copy_file(const path & from,const path & to,std::error_code & ec)3722 GHC_INLINE bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept
3723 {
3724 return copy_file(from, to, copy_options::none, ec);
3725 }
3726
copy_file(const path & from,const path & to,copy_options options,std::error_code & ec)3727 GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept
3728 {
3729 std::error_code tecf, tect;
3730 auto sf = status(from, tecf);
3731 auto st = status(to, tect);
3732 bool overwrite = false;
3733 ec.clear();
3734 if (!is_regular_file(sf)) {
3735 ec = tecf;
3736 return false;
3737 }
3738 if (exists(st) && (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none)) {
3739 ec = tect ? tect : detail::make_error_code(detail::portable_error::exists);
3740 return false;
3741 }
3742 if (exists(st)) {
3743 if ((options & copy_options::update_existing) == copy_options::update_existing) {
3744 auto from_time = last_write_time(from, ec);
3745 if (ec) {
3746 ec = detail::make_system_error();
3747 return false;
3748 }
3749 auto to_time = last_write_time(to, ec);
3750 if (ec) {
3751 ec = detail::make_system_error();
3752 return false;
3753 }
3754 if (from_time <= to_time) {
3755 return false;
3756 }
3757 }
3758 overwrite = true;
3759 }
3760 #ifdef GHC_OS_WINDOWS
3761 if (!::CopyFileW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), !overwrite)) {
3762 ec = detail::make_system_error();
3763 return false;
3764 }
3765 return true;
3766 #else
3767 std::vector<char> buffer(16384, '\0');
3768 int in = -1, out = -1;
3769 if ((in = ::open(from.c_str(), O_RDONLY)) < 0) {
3770 ec = detail::make_system_error();
3771 return false;
3772 }
3773 int mode = O_CREAT | O_WRONLY | O_TRUNC;
3774 if (!overwrite) {
3775 mode |= O_EXCL;
3776 }
3777 if ((out = ::open(to.c_str(), mode, static_cast<int>(sf.permissions() & perms::all))) < 0) {
3778 ec = detail::make_system_error();
3779 ::close(in);
3780 return false;
3781 }
3782 ssize_t br, bw;
3783 while ((br = ::read(in, buffer.data(), buffer.size())) > 0) {
3784 ssize_t offset = 0;
3785 do {
3786 if ((bw = ::write(out, buffer.data() + offset, static_cast<size_t>(br))) > 0) {
3787 br -= bw;
3788 offset += bw;
3789 }
3790 else if (bw < 0) {
3791 ec = detail::make_system_error();
3792 ::close(in);
3793 ::close(out);
3794 return false;
3795 }
3796 } while (br);
3797 }
3798 ::close(in);
3799 ::close(out);
3800 return true;
3801 #endif
3802 }
3803
3804 #ifdef GHC_WITH_EXCEPTIONS
copy_symlink(const path & existing_symlink,const path & new_symlink)3805 GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink)
3806 {
3807 std::error_code ec;
3808 copy_symlink(existing_symlink, new_symlink, ec);
3809 if (ec) {
3810 throw filesystem_error(detail::systemErrorText(ec.value()), existing_symlink, new_symlink, ec);
3811 }
3812 }
3813 #endif
3814
copy_symlink(const path & existing_symlink,const path & new_symlink,std::error_code & ec)3815 GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept
3816 {
3817 ec.clear();
3818 auto to = read_symlink(existing_symlink, ec);
3819 if (!ec) {
3820 if (exists(to, ec) && is_directory(to, ec)) {
3821 create_directory_symlink(to, new_symlink, ec);
3822 }
3823 else {
3824 create_symlink(to, new_symlink, ec);
3825 }
3826 }
3827 }
3828
3829 #ifdef GHC_WITH_EXCEPTIONS
create_directories(const path & p)3830 GHC_INLINE bool create_directories(const path& p)
3831 {
3832 std::error_code ec;
3833 auto result = create_directories(p, ec);
3834 if (ec) {
3835 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
3836 }
3837 return result;
3838 }
3839 #endif
3840
create_directories(const path & p,std::error_code & ec)3841 GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept
3842 {
3843 path current;
3844 ec.clear();
3845 bool didCreate = false;
3846 for (path::string_type part : p) {
3847 current /= part;
3848 if (current != p.root_name() && current != p.root_path()) {
3849 std::error_code tec;
3850 auto fs = status(current, tec);
3851 if (tec && fs.type() != file_type::not_found) {
3852 ec = tec;
3853 return false;
3854 }
3855 if (!exists(fs)) {
3856 create_directory(current, ec);
3857 if (ec) {
3858 std::error_code tmp_ec;
3859 if (is_directory(current, tmp_ec)) {
3860 ec.clear();
3861 }
3862 else {
3863 return false;
3864 }
3865 }
3866 didCreate = true;
3867 }
3868 #ifndef LWG_2935_BEHAVIOUR
3869 else if (!is_directory(fs)) {
3870 ec = detail::make_error_code(detail::portable_error::exists);
3871 return false;
3872 }
3873 #endif
3874 }
3875 }
3876 return didCreate;
3877 }
3878
3879 #ifdef GHC_WITH_EXCEPTIONS
create_directory(const path & p)3880 GHC_INLINE bool create_directory(const path& p)
3881 {
3882 std::error_code ec;
3883 auto result = create_directory(p, path(), ec);
3884 if (ec) {
3885 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
3886 }
3887 return result;
3888 }
3889 #endif
3890
create_directory(const path & p,std::error_code & ec)3891 GHC_INLINE bool create_directory(const path& p, std::error_code& ec) noexcept
3892 {
3893 return create_directory(p, path(), ec);
3894 }
3895
3896 #ifdef GHC_WITH_EXCEPTIONS
create_directory(const path & p,const path & attributes)3897 GHC_INLINE bool create_directory(const path& p, const path& attributes)
3898 {
3899 std::error_code ec;
3900 auto result = create_directory(p, attributes, ec);
3901 if (ec) {
3902 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
3903 }
3904 return result;
3905 }
3906 #endif
3907
create_directory(const path & p,const path & attributes,std::error_code & ec)3908 GHC_INLINE bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept
3909 {
3910 std::error_code tec;
3911 ec.clear();
3912 auto fs = status(p, tec);
3913 #ifdef LWG_2935_BEHAVIOUR
3914 if (status_known(fs) && exists(fs)) {
3915 return false;
3916 }
3917 #else
3918 if (status_known(fs) && exists(fs) && is_directory(fs)) {
3919 return false;
3920 }
3921 #endif
3922 #ifdef GHC_OS_WINDOWS
3923 if (!attributes.empty()) {
3924 if (!::CreateDirectoryExW(GHC_NATIVEWP(attributes), GHC_NATIVEWP(p), NULL)) {
3925 ec = detail::make_system_error();
3926 return false;
3927 }
3928 }
3929 else if (!::CreateDirectoryW(GHC_NATIVEWP(p), NULL)) {
3930 ec = detail::make_system_error();
3931 return false;
3932 }
3933 #else
3934 ::mode_t attribs = static_cast<mode_t>(perms::all);
3935 if (!attributes.empty()) {
3936 struct ::stat fileStat;
3937 if (::stat(attributes.c_str(), &fileStat) != 0) {
3938 ec = detail::make_system_error();
3939 return false;
3940 }
3941 attribs = fileStat.st_mode;
3942 }
3943 if (::mkdir(p.c_str(), attribs) != 0) {
3944 ec = detail::make_system_error();
3945 return false;
3946 }
3947 #endif
3948 return true;
3949 }
3950
3951 #ifdef GHC_WITH_EXCEPTIONS
create_directory_symlink(const path & to,const path & new_symlink)3952 GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink)
3953 {
3954 std::error_code ec;
3955 create_directory_symlink(to, new_symlink, ec);
3956 if (ec) {
3957 throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec);
3958 }
3959 }
3960 #endif
3961
create_directory_symlink(const path & to,const path & new_symlink,std::error_code & ec)3962 GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept
3963 {
3964 detail::create_symlink(to, new_symlink, true, ec);
3965 }
3966
3967 #ifndef GHC_OS_WEB
3968 #ifdef GHC_WITH_EXCEPTIONS
create_hard_link(const path & to,const path & new_hard_link)3969 GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link)
3970 {
3971 std::error_code ec;
3972 create_hard_link(to, new_hard_link, ec);
3973 if (ec) {
3974 throw filesystem_error(detail::systemErrorText(ec.value()), to, new_hard_link, ec);
3975 }
3976 }
3977 #endif
3978
create_hard_link(const path & to,const path & new_hard_link,std::error_code & ec)3979 GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept
3980 {
3981 detail::create_hardlink(to, new_hard_link, ec);
3982 }
3983 #endif
3984
3985 #ifdef GHC_WITH_EXCEPTIONS
create_symlink(const path & to,const path & new_symlink)3986 GHC_INLINE void create_symlink(const path& to, const path& new_symlink)
3987 {
3988 std::error_code ec;
3989 create_symlink(to, new_symlink, ec);
3990 if (ec) {
3991 throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec);
3992 }
3993 }
3994 #endif
3995
create_symlink(const path & to,const path & new_symlink,std::error_code & ec)3996 GHC_INLINE void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept
3997 {
3998 detail::create_symlink(to, new_symlink, false, ec);
3999 }
4000
4001 #ifdef GHC_WITH_EXCEPTIONS
current_path()4002 GHC_INLINE path current_path()
4003 {
4004 std::error_code ec;
4005 auto result = current_path(ec);
4006 if (ec) {
4007 throw filesystem_error(detail::systemErrorText(ec.value()), ec);
4008 }
4009 return result;
4010 }
4011 #endif
4012
current_path(std::error_code & ec)4013 GHC_INLINE path current_path(std::error_code& ec)
4014 {
4015 ec.clear();
4016 #ifdef GHC_OS_WINDOWS
4017 DWORD pathlen = ::GetCurrentDirectoryW(0, 0);
4018 std::unique_ptr<wchar_t[]> buffer(new wchar_t[size_t(pathlen) + 1]);
4019 if (::GetCurrentDirectoryW(pathlen, buffer.get()) == 0) {
4020 ec = detail::make_system_error();
4021 return path();
4022 }
4023 return path(std::wstring(buffer.get()), path::native_format);
4024 #else
4025 size_t pathlen = static_cast<size_t>(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX)));
4026 std::unique_ptr<char[]> buffer(new char[pathlen + 1]);
4027 if (::getcwd(buffer.get(), pathlen) == nullptr) {
4028 ec = detail::make_system_error();
4029 return path();
4030 }
4031 return path(buffer.get());
4032 #endif
4033 }
4034
4035 #ifdef GHC_WITH_EXCEPTIONS
current_path(const path & p)4036 GHC_INLINE void current_path(const path& p)
4037 {
4038 std::error_code ec;
4039 current_path(p, ec);
4040 if (ec) {
4041 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4042 }
4043 }
4044 #endif
4045
current_path(const path & p,std::error_code & ec)4046 GHC_INLINE void current_path(const path& p, std::error_code& ec) noexcept
4047 {
4048 ec.clear();
4049 #ifdef GHC_OS_WINDOWS
4050 if (!::SetCurrentDirectoryW(GHC_NATIVEWP(p))) {
4051 ec = detail::make_system_error();
4052 }
4053 #else
4054 if (::chdir(p.string().c_str()) == -1) {
4055 ec = detail::make_system_error();
4056 }
4057 #endif
4058 }
4059
exists(file_status s)4060 GHC_INLINE bool exists(file_status s) noexcept
4061 {
4062 return status_known(s) && s.type() != file_type::not_found;
4063 }
4064
4065 #ifdef GHC_WITH_EXCEPTIONS
exists(const path & p)4066 GHC_INLINE bool exists(const path& p)
4067 {
4068 return exists(status(p));
4069 }
4070 #endif
4071
exists(const path & p,std::error_code & ec)4072 GHC_INLINE bool exists(const path& p, std::error_code& ec) noexcept
4073 {
4074 file_status s = status(p, ec);
4075 if (status_known(s)) {
4076 ec.clear();
4077 }
4078 return exists(s);
4079 }
4080
4081 #ifdef GHC_WITH_EXCEPTIONS
equivalent(const path & p1,const path & p2)4082 GHC_INLINE bool equivalent(const path& p1, const path& p2)
4083 {
4084 std::error_code ec;
4085 bool result = equivalent(p1, p2, ec);
4086 if (ec) {
4087 throw filesystem_error(detail::systemErrorText(ec.value()), p1, p2, ec);
4088 }
4089 return result;
4090 }
4091 #endif
4092
equivalent(const path & p1,const path & p2,std::error_code & ec)4093 GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept
4094 {
4095 ec.clear();
4096 #ifdef GHC_OS_WINDOWS
4097 std::shared_ptr<void> file1(::CreateFileW(GHC_NATIVEWP(p1), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
4098 auto e1 = ::GetLastError();
4099 std::shared_ptr<void> file2(::CreateFileW(GHC_NATIVEWP(p2), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
4100 if (file1.get() == INVALID_HANDLE_VALUE || file2.get() == INVALID_HANDLE_VALUE) {
4101 #ifdef LWG_2937_BEHAVIOUR
4102 ec = detail::make_system_error(e1 ? e1 : ::GetLastError());
4103 #else
4104 if (file1 == file2) {
4105 ec = detail::make_system_error(e1 ? e1 : ::GetLastError());
4106 }
4107 #endif
4108 return false;
4109 }
4110 BY_HANDLE_FILE_INFORMATION inf1, inf2;
4111 if (!::GetFileInformationByHandle(file1.get(), &inf1)) {
4112 ec = detail::make_system_error();
4113 return false;
4114 }
4115 if (!::GetFileInformationByHandle(file2.get(), &inf2)) {
4116 ec = detail::make_system_error();
4117 return false;
4118 }
4119 return inf1.ftLastWriteTime.dwLowDateTime == inf2.ftLastWriteTime.dwLowDateTime && inf1.ftLastWriteTime.dwHighDateTime == inf2.ftLastWriteTime.dwHighDateTime && inf1.nFileIndexHigh == inf2.nFileIndexHigh && inf1.nFileIndexLow == inf2.nFileIndexLow &&
4120 inf1.nFileSizeHigh == inf2.nFileSizeHigh && inf1.nFileSizeLow == inf2.nFileSizeLow && inf1.dwVolumeSerialNumber == inf2.dwVolumeSerialNumber;
4121 #else
4122 struct ::stat s1, s2;
4123 auto rc1 = ::stat(p1.c_str(), &s1);
4124 auto e1 = errno;
4125 auto rc2 = ::stat(p2.c_str(), &s2);
4126 if (rc1 || rc2) {
4127 #ifdef LWG_2937_BEHAVIOUR
4128 ec = detail::make_system_error(e1 ? e1 : errno);
4129 #else
4130 if (rc1 && rc2) {
4131 ec = detail::make_system_error(e1 ? e1 : errno);
4132 }
4133 #endif
4134 return false;
4135 }
4136 return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino && s1.st_size == s2.st_size && s1.st_mtime == s2.st_mtime;
4137 #endif
4138 }
4139
4140 #ifdef GHC_WITH_EXCEPTIONS
file_size(const path & p)4141 GHC_INLINE uintmax_t file_size(const path& p)
4142 {
4143 std::error_code ec;
4144 auto result = file_size(p, ec);
4145 if (ec) {
4146 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4147 }
4148 return result;
4149 }
4150 #endif
4151
file_size(const path & p,std::error_code & ec)4152 GHC_INLINE uintmax_t file_size(const path& p, std::error_code& ec) noexcept
4153 {
4154 ec.clear();
4155 #ifdef GHC_OS_WINDOWS
4156 WIN32_FILE_ATTRIBUTE_DATA attr;
4157 if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) {
4158 ec = detail::make_system_error();
4159 return static_cast<uintmax_t>(-1);
4160 }
4161 return static_cast<uintmax_t>(attr.nFileSizeHigh) << (sizeof(attr.nFileSizeHigh) * 8) | attr.nFileSizeLow;
4162 #else
4163 struct ::stat fileStat;
4164 if (::stat(p.c_str(), &fileStat) == -1) {
4165 ec = detail::make_system_error();
4166 return static_cast<uintmax_t>(-1);
4167 }
4168 return static_cast<uintmax_t>(fileStat.st_size);
4169 #endif
4170 }
4171
4172 #ifndef GHC_OS_WEB
4173 #ifdef GHC_WITH_EXCEPTIONS
hard_link_count(const path & p)4174 GHC_INLINE uintmax_t hard_link_count(const path& p)
4175 {
4176 std::error_code ec;
4177 auto result = hard_link_count(p, ec);
4178 if (ec) {
4179 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4180 }
4181 return result;
4182 }
4183 #endif
4184
hard_link_count(const path & p,std::error_code & ec)4185 GHC_INLINE uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept
4186 {
4187 ec.clear();
4188 #ifdef GHC_OS_WINDOWS
4189 uintmax_t result = static_cast<uintmax_t>(-1);
4190 std::shared_ptr<void> file(::CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
4191 BY_HANDLE_FILE_INFORMATION inf;
4192 if (file.get() == INVALID_HANDLE_VALUE) {
4193 ec = detail::make_system_error();
4194 }
4195 else {
4196 if (!::GetFileInformationByHandle(file.get(), &inf)) {
4197 ec = detail::make_system_error();
4198 }
4199 else {
4200 result = inf.nNumberOfLinks;
4201 }
4202 }
4203 return result;
4204 #else
4205 uintmax_t result = 0;
4206 file_status fs = detail::status_ex(p, ec, nullptr, nullptr, &result, nullptr);
4207 if (fs.type() == file_type::not_found) {
4208 ec = detail::make_error_code(detail::portable_error::not_found);
4209 }
4210 return ec ? static_cast<uintmax_t>(-1) : result;
4211 #endif
4212 }
4213 #endif
4214
is_block_file(file_status s)4215 GHC_INLINE bool is_block_file(file_status s) noexcept
4216 {
4217 return s.type() == file_type::block;
4218 }
4219
4220 #ifdef GHC_WITH_EXCEPTIONS
is_block_file(const path & p)4221 GHC_INLINE bool is_block_file(const path& p)
4222 {
4223 return is_block_file(status(p));
4224 }
4225 #endif
4226
is_block_file(const path & p,std::error_code & ec)4227 GHC_INLINE bool is_block_file(const path& p, std::error_code& ec) noexcept
4228 {
4229 return is_block_file(status(p, ec));
4230 }
4231
is_character_file(file_status s)4232 GHC_INLINE bool is_character_file(file_status s) noexcept
4233 {
4234 return s.type() == file_type::character;
4235 }
4236
4237 #ifdef GHC_WITH_EXCEPTIONS
is_character_file(const path & p)4238 GHC_INLINE bool is_character_file(const path& p)
4239 {
4240 return is_character_file(status(p));
4241 }
4242 #endif
4243
is_character_file(const path & p,std::error_code & ec)4244 GHC_INLINE bool is_character_file(const path& p, std::error_code& ec) noexcept
4245 {
4246 return is_character_file(status(p, ec));
4247 }
4248
is_directory(file_status s)4249 GHC_INLINE bool is_directory(file_status s) noexcept
4250 {
4251 return s.type() == file_type::directory;
4252 }
4253
4254 #ifdef GHC_WITH_EXCEPTIONS
is_directory(const path & p)4255 GHC_INLINE bool is_directory(const path& p)
4256 {
4257 return is_directory(status(p));
4258 }
4259 #endif
4260
is_directory(const path & p,std::error_code & ec)4261 GHC_INLINE bool is_directory(const path& p, std::error_code& ec) noexcept
4262 {
4263 return is_directory(status(p, ec));
4264 }
4265
4266 #ifdef GHC_WITH_EXCEPTIONS
is_empty(const path & p)4267 GHC_INLINE bool is_empty(const path& p)
4268 {
4269 if (is_directory(p)) {
4270 return directory_iterator(p) == directory_iterator();
4271 }
4272 else {
4273 return file_size(p) == 0;
4274 }
4275 }
4276 #endif
4277
is_empty(const path & p,std::error_code & ec)4278 GHC_INLINE bool is_empty(const path& p, std::error_code& ec) noexcept
4279 {
4280 auto fs = status(p, ec);
4281 if (ec) {
4282 return false;
4283 }
4284 if (is_directory(fs)) {
4285 directory_iterator iter(p, ec);
4286 if (ec) {
4287 return false;
4288 }
4289 return iter == directory_iterator();
4290 }
4291 else {
4292 auto sz = file_size(p, ec);
4293 if (ec) {
4294 return false;
4295 }
4296 return sz == 0;
4297 }
4298 }
4299
is_fifo(file_status s)4300 GHC_INLINE bool is_fifo(file_status s) noexcept
4301 {
4302 return s.type() == file_type::fifo;
4303 }
4304
4305 #ifdef GHC_WITH_EXCEPTIONS
is_fifo(const path & p)4306 GHC_INLINE bool is_fifo(const path& p)
4307 {
4308 return is_fifo(status(p));
4309 }
4310 #endif
4311
is_fifo(const path & p,std::error_code & ec)4312 GHC_INLINE bool is_fifo(const path& p, std::error_code& ec) noexcept
4313 {
4314 return is_fifo(status(p, ec));
4315 }
4316
is_other(file_status s)4317 GHC_INLINE bool is_other(file_status s) noexcept
4318 {
4319 return exists(s) && !is_regular_file(s) && !is_directory(s) && !is_symlink(s);
4320 }
4321
4322 #ifdef GHC_WITH_EXCEPTIONS
is_other(const path & p)4323 GHC_INLINE bool is_other(const path& p)
4324 {
4325 return is_other(status(p));
4326 }
4327 #endif
4328
is_other(const path & p,std::error_code & ec)4329 GHC_INLINE bool is_other(const path& p, std::error_code& ec) noexcept
4330 {
4331 return is_other(status(p, ec));
4332 }
4333
is_regular_file(file_status s)4334 GHC_INLINE bool is_regular_file(file_status s) noexcept
4335 {
4336 return s.type() == file_type::regular;
4337 }
4338
4339 #ifdef GHC_WITH_EXCEPTIONS
is_regular_file(const path & p)4340 GHC_INLINE bool is_regular_file(const path& p)
4341 {
4342 return is_regular_file(status(p));
4343 }
4344 #endif
4345
is_regular_file(const path & p,std::error_code & ec)4346 GHC_INLINE bool is_regular_file(const path& p, std::error_code& ec) noexcept
4347 {
4348 return is_regular_file(status(p, ec));
4349 }
4350
is_socket(file_status s)4351 GHC_INLINE bool is_socket(file_status s) noexcept
4352 {
4353 return s.type() == file_type::socket;
4354 }
4355
4356 #ifdef GHC_WITH_EXCEPTIONS
is_socket(const path & p)4357 GHC_INLINE bool is_socket(const path& p)
4358 {
4359 return is_socket(status(p));
4360 }
4361 #endif
4362
is_socket(const path & p,std::error_code & ec)4363 GHC_INLINE bool is_socket(const path& p, std::error_code& ec) noexcept
4364 {
4365 return is_socket(status(p, ec));
4366 }
4367
is_symlink(file_status s)4368 GHC_INLINE bool is_symlink(file_status s) noexcept
4369 {
4370 return s.type() == file_type::symlink;
4371 }
4372
4373 #ifdef GHC_WITH_EXCEPTIONS
is_symlink(const path & p)4374 GHC_INLINE bool is_symlink(const path& p)
4375 {
4376 return is_symlink(symlink_status(p));
4377 }
4378 #endif
4379
is_symlink(const path & p,std::error_code & ec)4380 GHC_INLINE bool is_symlink(const path& p, std::error_code& ec) noexcept
4381 {
4382 return is_symlink(symlink_status(p, ec));
4383 }
4384
4385 #ifdef GHC_WITH_EXCEPTIONS
last_write_time(const path & p)4386 GHC_INLINE file_time_type last_write_time(const path& p)
4387 {
4388 std::error_code ec;
4389 auto result = last_write_time(p, ec);
4390 if (ec) {
4391 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4392 }
4393 return result;
4394 }
4395 #endif
4396
last_write_time(const path & p,std::error_code & ec)4397 GHC_INLINE file_time_type last_write_time(const path& p, std::error_code& ec) noexcept
4398 {
4399 time_t result = 0;
4400 ec.clear();
4401 file_status fs = detail::status_ex(p, ec, nullptr, nullptr, nullptr, &result);
4402 return ec ? (file_time_type::min)() : std::chrono::system_clock::from_time_t(result);
4403 }
4404
4405 #ifdef GHC_WITH_EXCEPTIONS
last_write_time(const path & p,file_time_type new_time)4406 GHC_INLINE void last_write_time(const path& p, file_time_type new_time)
4407 {
4408 std::error_code ec;
4409 last_write_time(p, new_time, ec);
4410 if (ec) {
4411 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4412 }
4413 }
4414 #endif
4415
last_write_time(const path & p,file_time_type new_time,std::error_code & ec)4416 GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept
4417 {
4418 ec.clear();
4419 auto d = new_time.time_since_epoch();
4420 #ifdef GHC_OS_WINDOWS
4421 std::shared_ptr<void> file(::CreateFileW(GHC_NATIVEWP(p), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL), ::CloseHandle);
4422 FILETIME ft;
4423 auto tt = std::chrono::duration_cast<std::chrono::microseconds>(d).count() * 10 + 116444736000000000;
4424 ft.dwLowDateTime = static_cast<DWORD>(tt);
4425 ft.dwHighDateTime = static_cast<DWORD>(tt >> 32);
4426 if (!::SetFileTime(file.get(), 0, 0, &ft)) {
4427 ec = detail::make_system_error();
4428 }
4429 #elif defined(GHC_OS_MACOS)
4430 #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
4431 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300
4432 struct ::stat fs;
4433 if (::stat(p.c_str(), &fs) == 0) {
4434 struct ::timeval tv[2];
4435 tv[0].tv_sec = fs.st_atimespec.tv_sec;
4436 tv[0].tv_usec = static_cast<int>(fs.st_atimespec.tv_nsec / 1000);
4437 tv[1].tv_sec = std::chrono::duration_cast<std::chrono::seconds>(d).count();
4438 tv[1].tv_usec = static_cast<int>(std::chrono::duration_cast<std::chrono::microseconds>(d).count() % 1000000);
4439 if (::utimes(p.c_str(), tv) == 0) {
4440 return;
4441 }
4442 }
4443 ec = detail::make_system_error();
4444 return;
4445 #else
4446 struct ::timespec times[2];
4447 times[0].tv_sec = 0;
4448 times[0].tv_nsec = UTIME_OMIT;
4449 times[1].tv_sec = std::chrono::duration_cast<std::chrono::seconds>(d).count();
4450 times[1].tv_nsec = 0; // std::chrono::duration_cast<std::chrono::nanoseconds>(d).count() % 1000000000;
4451 if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
4452 ec = detail::make_system_error();
4453 }
4454 return;
4455 #endif
4456 #endif
4457 #else
4458 #ifndef UTIME_OMIT
4459 #define UTIME_OMIT ((1l << 30) - 2l)
4460 #endif
4461 struct ::timespec times[2];
4462 times[0].tv_sec = 0;
4463 times[0].tv_nsec = UTIME_OMIT;
4464 times[1].tv_sec = static_cast<decltype(times[1].tv_sec)>(std::chrono::duration_cast<std::chrono::seconds>(d).count());
4465 times[1].tv_nsec = static_cast<decltype(times[1].tv_nsec)>(std::chrono::duration_cast<std::chrono::nanoseconds>(d).count() % 1000000000);
4466 #if defined(__ANDROID_API__) && __ANDROID_API__ < 12
4467 if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
4468 #else
4469 if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
4470 #endif
4471 ec = detail::make_system_error();
4472 }
4473 return;
4474 #endif
4475 }
4476
4477 #ifdef GHC_WITH_EXCEPTIONS
4478 GHC_INLINE void permissions(const path& p, perms prms, perm_options opts)
4479 {
4480 std::error_code ec;
4481 permissions(p, prms, opts, ec);
4482 if (ec) {
4483 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4484 }
4485 }
4486 #endif
4487
4488 GHC_INLINE void permissions(const path& p, perms prms, std::error_code& ec) noexcept
4489 {
4490 permissions(p, prms, perm_options::replace, ec);
4491 }
4492
4493 GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept
4494 {
4495 if (static_cast<int>(opts & (perm_options::replace | perm_options::add | perm_options::remove)) == 0) {
4496 ec = detail::make_error_code(detail::portable_error::invalid_argument);
4497 return;
4498 }
4499 auto fs = symlink_status(p, ec);
4500 if ((opts & perm_options::replace) != perm_options::replace) {
4501 if ((opts & perm_options::add) == perm_options::add) {
4502 prms = fs.permissions() | prms;
4503 }
4504 else {
4505 prms = fs.permissions() & ~prms;
4506 }
4507 }
4508 #ifdef GHC_OS_WINDOWS
4509 #ifdef __GNUC__
4510 auto oldAttr = GetFileAttributesW(GHC_NATIVEWP(p));
4511 if (oldAttr != INVALID_FILE_ATTRIBUTES) {
4512 DWORD newAttr = ((prms & perms::owner_write) == perms::owner_write) ? oldAttr & ~(static_cast<DWORD>(FILE_ATTRIBUTE_READONLY)) : oldAttr | FILE_ATTRIBUTE_READONLY;
4513 if (oldAttr == newAttr || SetFileAttributesW(GHC_NATIVEWP(p), newAttr)) {
4514 return;
4515 }
4516 }
4517 ec = detail::make_system_error();
4518 #else
4519 int mode = 0;
4520 if ((prms & perms::owner_read) == perms::owner_read) {
4521 mode |= _S_IREAD;
4522 }
4523 if ((prms & perms::owner_write) == perms::owner_write) {
4524 mode |= _S_IWRITE;
4525 }
4526 if (::_wchmod(p.wstring().c_str(), mode) != 0) {
4527 ec = detail::make_system_error();
4528 }
4529 #endif
4530 #else
4531 if ((opts & perm_options::nofollow) != perm_options::nofollow) {
4532 if (::chmod(p.c_str(), static_cast<mode_t>(prms)) != 0) {
4533 ec = detail::make_system_error();
4534 }
4535 }
4536 #endif
4537 }
4538
4539 #ifdef GHC_WITH_EXCEPTIONS
4540 GHC_INLINE path proximate(const path& p, std::error_code& ec)
4541 {
4542 auto cp = current_path(ec);
4543 if (!ec) {
4544 return proximate(p, cp, ec);
4545 }
4546 return path();
4547 }
4548 #endif
4549
4550 #ifdef GHC_WITH_EXCEPTIONS
4551 GHC_INLINE path proximate(const path& p, const path& base)
4552 {
4553 return weakly_canonical(p).lexically_proximate(weakly_canonical(base));
4554 }
4555 #endif
4556
4557 GHC_INLINE path proximate(const path& p, const path& base, std::error_code& ec)
4558 {
4559 return weakly_canonical(p, ec).lexically_proximate(weakly_canonical(base, ec));
4560 }
4561
4562 #ifdef GHC_WITH_EXCEPTIONS
4563 GHC_INLINE path read_symlink(const path& p)
4564 {
4565 std::error_code ec;
4566 auto result = read_symlink(p, ec);
4567 if (ec) {
4568 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4569 }
4570 return result;
4571 }
4572 #endif
4573
4574 GHC_INLINE path read_symlink(const path& p, std::error_code& ec)
4575 {
4576 file_status fs = symlink_status(p, ec);
4577 if (fs.type() != file_type::symlink) {
4578 ec = detail::make_error_code(detail::portable_error::invalid_argument);
4579 return path();
4580 }
4581 auto result = detail::resolveSymlink(p, ec);
4582 return ec ? path() : result;
4583 }
4584
4585 GHC_INLINE path relative(const path& p, std::error_code& ec)
4586 {
4587 return relative(p, current_path(ec), ec);
4588 }
4589
4590 #ifdef GHC_WITH_EXCEPTIONS
4591 GHC_INLINE path relative(const path& p, const path& base)
4592 {
4593 return weakly_canonical(p).lexically_relative(weakly_canonical(base));
4594 }
4595 #endif
4596
4597 GHC_INLINE path relative(const path& p, const path& base, std::error_code& ec)
4598 {
4599 return weakly_canonical(p, ec).lexically_relative(weakly_canonical(base, ec));
4600 }
4601
4602 #ifdef GHC_WITH_EXCEPTIONS
4603 GHC_INLINE bool remove(const path& p)
4604 {
4605 std::error_code ec;
4606 auto result = remove(p, ec);
4607 if (ec) {
4608 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4609 }
4610 return result;
4611 }
4612 #endif
4613
4614 GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept
4615 {
4616 ec.clear();
4617 #ifdef GHC_OS_WINDOWS
4618 #ifdef GHC_USE_WCHAR_T
4619 auto cstr = p.c_str();
4620 #else
4621 std::wstring np = detail::fromUtf8<std::wstring>(p.u8string());
4622 auto cstr = np.c_str();
4623 #endif
4624 DWORD attr = GetFileAttributesW(cstr);
4625 if (attr == INVALID_FILE_ATTRIBUTES) {
4626 auto error = ::GetLastError();
4627 if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) {
4628 return false;
4629 }
4630 ec = detail::make_system_error(error);
4631 }
4632 if (!ec) {
4633 if (attr & FILE_ATTRIBUTE_DIRECTORY) {
4634 if (!RemoveDirectoryW(cstr)) {
4635 ec = detail::make_system_error();
4636 }
4637 }
4638 else {
4639 if (!DeleteFileW(cstr)) {
4640 ec = detail::make_system_error();
4641 }
4642 }
4643 }
4644 #else
4645 if (::remove(p.c_str()) == -1) {
4646 auto error = errno;
4647 if (error == ENOENT) {
4648 return false;
4649 }
4650 ec = detail::make_system_error();
4651 }
4652 #endif
4653 return ec ? false : true;
4654 }
4655
4656 #ifdef GHC_WITH_EXCEPTIONS
4657 GHC_INLINE uintmax_t remove_all(const path& p)
4658 {
4659 std::error_code ec;
4660 auto result = remove_all(p, ec);
4661 if (ec) {
4662 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4663 }
4664 return result;
4665 }
4666 #endif
4667
4668 GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept
4669 {
4670 ec.clear();
4671 uintmax_t count = 0;
4672 if (p == "/") {
4673 ec = detail::make_error_code(detail::portable_error::not_supported);
4674 return static_cast<uintmax_t>(-1);
4675 }
4676 std::error_code tec;
4677 auto fs = status(p, tec);
4678 if (exists(fs) && is_directory(fs)) {
4679 for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) {
4680 if (ec && !detail::is_not_found_error(ec)) {
4681 break;
4682 }
4683 bool is_symlink_result = iter->is_symlink(ec);
4684 if (ec)
4685 return static_cast<uintmax_t>(-1);
4686 if (!is_symlink_result && iter->is_directory(ec)) {
4687 count += remove_all(iter->path(), ec);
4688 if (ec) {
4689 return static_cast<uintmax_t>(-1);
4690 }
4691 }
4692 else {
4693 if (!ec) {
4694 remove(iter->path(), ec);
4695 }
4696 if (ec) {
4697 return static_cast<uintmax_t>(-1);
4698 }
4699 ++count;
4700 }
4701 }
4702 }
4703 if (!ec) {
4704 if (remove(p, ec)) {
4705 ++count;
4706 }
4707 }
4708 if (ec) {
4709 return static_cast<uintmax_t>(-1);
4710 }
4711 return count;
4712 }
4713
4714 #ifdef GHC_WITH_EXCEPTIONS
4715 GHC_INLINE void rename(const path& from, const path& to)
4716 {
4717 std::error_code ec;
4718 rename(from, to, ec);
4719 if (ec) {
4720 throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec);
4721 }
4722 }
4723 #endif
4724
4725 GHC_INLINE void rename(const path& from, const path& to, std::error_code& ec) noexcept
4726 {
4727 ec.clear();
4728 #ifdef GHC_OS_WINDOWS
4729 if (from != to) {
4730 if (!MoveFileExW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), (DWORD)MOVEFILE_REPLACE_EXISTING)) {
4731 ec = detail::make_system_error();
4732 }
4733 }
4734 #else
4735 if (from != to) {
4736 if (::rename(from.c_str(), to.c_str()) != 0) {
4737 ec = detail::make_system_error();
4738 }
4739 }
4740 #endif
4741 }
4742
4743 #ifdef GHC_WITH_EXCEPTIONS
4744 GHC_INLINE void resize_file(const path& p, uintmax_t size)
4745 {
4746 std::error_code ec;
4747 resize_file(p, size, ec);
4748 if (ec) {
4749 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4750 }
4751 }
4752 #endif
4753
4754 GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept
4755 {
4756 ec.clear();
4757 #ifdef GHC_OS_WINDOWS
4758 LARGE_INTEGER lisize;
4759 lisize.QuadPart = static_cast<LONGLONG>(size);
4760 if (lisize.QuadPart < 0) {
4761 #ifdef ERROR_FILE_TOO_LARGE
4762 ec = detail::make_system_error(ERROR_FILE_TOO_LARGE);
4763 #else
4764 ec = detail::make_system_error(223);
4765 #endif
4766 return;
4767 }
4768 std::shared_ptr<void> file(CreateFileW(GHC_NATIVEWP(p), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL), CloseHandle);
4769 if (file.get() == INVALID_HANDLE_VALUE) {
4770 ec = detail::make_system_error();
4771 }
4772 else if (SetFilePointerEx(file.get(), lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(file.get()) == 0) {
4773 ec = detail::make_system_error();
4774 }
4775 #else
4776 if (::truncate(p.c_str(), static_cast<off_t>(size)) != 0) {
4777 ec = detail::make_system_error();
4778 }
4779 #endif
4780 }
4781
4782 #ifdef GHC_WITH_EXCEPTIONS
4783 GHC_INLINE space_info space(const path& p)
4784 {
4785 std::error_code ec;
4786 auto result = space(p, ec);
4787 if (ec) {
4788 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4789 }
4790 return result;
4791 }
4792 #endif
4793
4794 GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept
4795 {
4796 ec.clear();
4797 #ifdef GHC_OS_WINDOWS
4798 ULARGE_INTEGER freeBytesAvailableToCaller = {{ 0, 0 }};
4799 ULARGE_INTEGER totalNumberOfBytes = {{ 0, 0 }};
4800 ULARGE_INTEGER totalNumberOfFreeBytes = {{ 0, 0 }};
4801 if (!GetDiskFreeSpaceExW(GHC_NATIVEWP(p), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) {
4802 ec = detail::make_system_error();
4803 return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)};
4804 }
4805 return {static_cast<uintmax_t>(totalNumberOfBytes.QuadPart), static_cast<uintmax_t>(totalNumberOfFreeBytes.QuadPart), static_cast<uintmax_t>(freeBytesAvailableToCaller.QuadPart)};
4806 #else
4807 struct ::statvfs sfs;
4808 if (::statvfs(p.c_str(), &sfs) != 0) {
4809 ec = detail::make_system_error();
4810 return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)};
4811 }
4812 return {static_cast<uintmax_t>(sfs.f_blocks * sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bfree * sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bavail * sfs.f_frsize)};
4813 #endif
4814 }
4815
4816 #ifdef GHC_WITH_EXCEPTIONS
4817 GHC_INLINE file_status status(const path& p)
4818 {
4819 std::error_code ec;
4820 auto result = status(p, ec);
4821 if (result.type() == file_type::none) {
4822 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4823 }
4824 return result;
4825 }
4826 #endif
4827
4828 GHC_INLINE file_status status(const path& p, std::error_code& ec) noexcept
4829 {
4830 return detail::status_ex(p, ec);
4831 }
4832
4833 GHC_INLINE bool status_known(file_status s) noexcept
4834 {
4835 return s.type() != file_type::none;
4836 }
4837
4838 #ifdef GHC_WITH_EXCEPTIONS
4839 GHC_INLINE file_status symlink_status(const path& p)
4840 {
4841 std::error_code ec;
4842 auto result = symlink_status(p, ec);
4843 if (result.type() == file_type::none) {
4844 throw filesystem_error(detail::systemErrorText(ec.value()), ec);
4845 }
4846 return result;
4847 }
4848 #endif
4849
4850 GHC_INLINE file_status symlink_status(const path& p, std::error_code& ec) noexcept
4851 {
4852 return detail::symlink_status_ex(p, ec);
4853 }
4854
4855 #ifdef GHC_WITH_EXCEPTIONS
4856 GHC_INLINE path temp_directory_path()
4857 {
4858 std::error_code ec;
4859 path result = temp_directory_path(ec);
4860 if (ec) {
4861 throw filesystem_error(detail::systemErrorText(ec.value()), ec);
4862 }
4863 return result;
4864 }
4865 #endif
4866
4867 GHC_INLINE path temp_directory_path(std::error_code& ec) noexcept
4868 {
4869 ec.clear();
4870 #ifdef GHC_OS_WINDOWS
4871 wchar_t buffer[512];
4872 auto rc = GetTempPathW(511, buffer);
4873 if (!rc || rc > 511) {
4874 ec = detail::make_system_error();
4875 return path();
4876 }
4877 return path(std::wstring(buffer));
4878 #else
4879 static const char* temp_vars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr};
4880 const char* temp_path = nullptr;
4881 for (auto temp_name = temp_vars; *temp_name != nullptr; ++temp_name) {
4882 temp_path = std::getenv(*temp_name);
4883 if (temp_path) {
4884 return path(temp_path);
4885 }
4886 }
4887 return path("/tmp");
4888 #endif
4889 }
4890
4891 #ifdef GHC_WITH_EXCEPTIONS
4892 GHC_INLINE path weakly_canonical(const path& p)
4893 {
4894 std::error_code ec;
4895 auto result = weakly_canonical(p, ec);
4896 if (ec) {
4897 throw filesystem_error(detail::systemErrorText(ec.value()), p, ec);
4898 }
4899 return result;
4900 }
4901 #endif
4902
4903 GHC_INLINE path weakly_canonical(const path& p, std::error_code& ec) noexcept
4904 {
4905 path result;
4906 ec.clear();
4907 bool scan = true;
4908 for (auto pe : p) {
4909 if (scan) {
4910 std::error_code tec;
4911 if (exists(result / pe, tec)) {
4912 result /= pe;
4913 }
4914 else {
4915 if (ec) {
4916 return path();
4917 }
4918 scan = false;
4919 if (!result.empty()) {
4920 result = canonical(result, ec) / pe;
4921 if (ec) {
4922 break;
4923 }
4924 }
4925 else {
4926 result /= pe;
4927 }
4928 }
4929 }
4930 else {
4931 result /= pe;
4932 }
4933 }
4934 if (scan) {
4935 if (!result.empty()) {
4936 result = canonical(result, ec);
4937 }
4938 }
4939 return ec ? path() : result.lexically_normal();
4940 }
4941
4942 //-----------------------------------------------------------------------------
4943 // 30.10.11 class file_status
4944 // 30.10.11.1 constructors and destructor
4945 GHC_INLINE file_status::file_status() noexcept
4946 : file_status(file_type::none)
4947 {
4948 }
4949
4950 GHC_INLINE file_status::file_status(file_type ft, perms prms) noexcept
4951 : _type(ft)
4952 , _perms(prms)
4953 {
4954 }
4955
4956 GHC_INLINE file_status::file_status(const file_status& other) noexcept
4957 : _type(other._type)
4958 , _perms(other._perms)
4959 {
4960 }
4961
4962 GHC_INLINE file_status::file_status(file_status&& other) noexcept
4963 : _type(other._type)
4964 , _perms(other._perms)
4965 {
4966 }
4967
4968 GHC_INLINE file_status::~file_status() {}
4969
4970 // assignments:
4971 GHC_INLINE file_status& file_status::operator=(const file_status& rhs) noexcept
4972 {
4973 _type = rhs._type;
4974 _perms = rhs._perms;
4975 return *this;
4976 }
4977
4978 GHC_INLINE file_status& file_status::operator=(file_status&& rhs) noexcept
4979 {
4980 _type = rhs._type;
4981 _perms = rhs._perms;
4982 return *this;
4983 }
4984
4985 // 30.10.11.3 modifiers
4986 GHC_INLINE void file_status::type(file_type ft) noexcept
4987 {
4988 _type = ft;
4989 }
4990
4991 GHC_INLINE void file_status::permissions(perms prms) noexcept
4992 {
4993 _perms = prms;
4994 }
4995
4996 // 30.10.11.2 observers
4997 GHC_INLINE file_type file_status::type() const noexcept
4998 {
4999 return _type;
5000 }
5001
5002 GHC_INLINE perms file_status::permissions() const noexcept
5003 {
5004 return _perms;
5005 }
5006
5007 //-----------------------------------------------------------------------------
5008 // 30.10.12 class directory_entry
5009 // 30.10.12.1 constructors and destructor
5010 // directory_entry::directory_entry() noexcept = default;
5011 // directory_entry::directory_entry(const directory_entry&) = default;
5012 // directory_entry::directory_entry(directory_entry&&) noexcept = default;
5013 #ifdef GHC_WITH_EXCEPTIONS
5014 GHC_INLINE directory_entry::directory_entry(const filesystem::path& p)
5015 : _path(p)
5016 , _file_size(static_cast<uintmax_t>(-1))
5017 #ifndef GHC_OS_WINDOWS
5018 , _hard_link_count(static_cast<uintmax_t>(-1))
5019 #endif
5020 , _last_write_time(0)
5021 {
5022 refresh();
5023 }
5024 #endif
5025
5026 GHC_INLINE directory_entry::directory_entry(const filesystem::path& p, std::error_code& ec)
5027 : _path(p)
5028 , _file_size(static_cast<uintmax_t>(-1))
5029 #ifndef GHC_OS_WINDOWS
5030 , _hard_link_count(static_cast<uintmax_t>(-1))
5031 #endif
5032 , _last_write_time(0)
5033 {
5034 refresh(ec);
5035 }
5036
5037 GHC_INLINE directory_entry::~directory_entry() {}
5038
5039 // assignments:
5040 // directory_entry& directory_entry::operator=(const directory_entry&) = default;
5041 // directory_entry& directory_entry::operator=(directory_entry&&) noexcept = default;
5042
5043 // 30.10.12.2 directory_entry modifiers
5044 #ifdef GHC_WITH_EXCEPTIONS
5045 GHC_INLINE void directory_entry::assign(const filesystem::path& p)
5046 {
5047 _path = p;
5048 refresh();
5049 }
5050 #endif
5051
5052 GHC_INLINE void directory_entry::assign(const filesystem::path& p, std::error_code& ec)
5053 {
5054 _path = p;
5055 refresh(ec);
5056 }
5057
5058 #ifdef GHC_WITH_EXCEPTIONS
5059 GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p)
5060 {
5061 _path.replace_filename(p);
5062 refresh();
5063 }
5064 #endif
5065
5066 GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p, std::error_code& ec)
5067 {
5068 _path.replace_filename(p);
5069 refresh(ec);
5070 }
5071
5072 #ifdef GHC_WITH_EXCEPTIONS
5073 GHC_INLINE void directory_entry::refresh()
5074 {
5075 std::error_code ec;
5076 refresh(ec);
5077 if (ec) {
5078 throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec);
5079 }
5080 }
5081 #endif
5082
5083 GHC_INLINE void directory_entry::refresh(std::error_code& ec) noexcept
5084 {
5085 #ifdef GHC_OS_WINDOWS
5086 _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, nullptr, &_last_write_time);
5087 #else
5088 _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, &_hard_link_count, &_last_write_time);
5089 #endif
5090 }
5091
5092 // 30.10.12.3 directory_entry observers
5093 GHC_INLINE const filesystem::path& directory_entry::path() const noexcept
5094 {
5095 return _path;
5096 }
5097
5098 GHC_INLINE directory_entry::operator const filesystem::path&() const noexcept
5099 {
5100 return _path;
5101 }
5102
5103 #ifdef GHC_WITH_EXCEPTIONS
5104 GHC_INLINE file_type directory_entry::status_file_type() const
5105 {
5106 return _status.type() != file_type::none ? _status.type() : filesystem::status(path()).type();
5107 }
5108 #endif
5109
5110 GHC_INLINE file_type directory_entry::status_file_type(std::error_code& ec) const noexcept
5111 {
5112 if(_status.type() != file_type::none) {
5113 ec.clear();
5114 return _status.type();
5115 }
5116 return filesystem::status(path(), ec).type();
5117 }
5118
5119 #ifdef GHC_WITH_EXCEPTIONS
5120 GHC_INLINE bool directory_entry::exists() const
5121 {
5122 return status_file_type() != file_type::not_found;
5123 }
5124 #endif
5125
5126 GHC_INLINE bool directory_entry::exists(std::error_code& ec) const noexcept
5127 {
5128 return status_file_type(ec) != file_type::not_found;
5129 }
5130
5131 #ifdef GHC_WITH_EXCEPTIONS
5132 GHC_INLINE bool directory_entry::is_block_file() const
5133 {
5134 return status_file_type() == file_type::block;
5135 }
5136 #endif
5137 GHC_INLINE bool directory_entry::is_block_file(std::error_code& ec) const noexcept
5138 {
5139 return status_file_type(ec) == file_type::block;
5140 }
5141
5142 #ifdef GHC_WITH_EXCEPTIONS
5143 GHC_INLINE bool directory_entry::is_character_file() const
5144 {
5145 return status_file_type() == file_type::character;
5146 }
5147 #endif
5148
5149 GHC_INLINE bool directory_entry::is_character_file(std::error_code& ec) const noexcept
5150 {
5151 return status_file_type(ec) == file_type::character;
5152 }
5153
5154 #ifdef GHC_WITH_EXCEPTIONS
5155 GHC_INLINE bool directory_entry::is_directory() const
5156 {
5157 return status_file_type() == file_type::directory;
5158 }
5159 #endif
5160
5161 GHC_INLINE bool directory_entry::is_directory(std::error_code& ec) const noexcept
5162 {
5163 return status_file_type(ec) == file_type::directory;
5164 }
5165
5166 #ifdef GHC_WITH_EXCEPTIONS
5167 GHC_INLINE bool directory_entry::is_fifo() const
5168 {
5169 return status_file_type() == file_type::fifo;
5170 }
5171 #endif
5172
5173 GHC_INLINE bool directory_entry::is_fifo(std::error_code& ec) const noexcept
5174 {
5175 return status_file_type(ec) == file_type::fifo;
5176 }
5177
5178 #ifdef GHC_WITH_EXCEPTIONS
5179 GHC_INLINE bool directory_entry::is_other() const
5180 {
5181 auto ft = status_file_type();
5182 return ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink();
5183 }
5184 #endif
5185
5186 GHC_INLINE bool directory_entry::is_other(std::error_code& ec) const noexcept
5187 {
5188 auto ft = status_file_type(ec);
5189 bool other = ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(ec);
5190 return !ec && other;
5191 }
5192
5193 #ifdef GHC_WITH_EXCEPTIONS
5194 GHC_INLINE bool directory_entry::is_regular_file() const
5195 {
5196 return status_file_type() == file_type::regular;
5197 }
5198 #endif
5199
5200 GHC_INLINE bool directory_entry::is_regular_file(std::error_code& ec) const noexcept
5201 {
5202 return status_file_type(ec) == file_type::regular;
5203 }
5204
5205 #ifdef GHC_WITH_EXCEPTIONS
5206 GHC_INLINE bool directory_entry::is_socket() const
5207 {
5208 return status_file_type() == file_type::socket;
5209 }
5210 #endif
5211
5212 GHC_INLINE bool directory_entry::is_socket(std::error_code& ec) const noexcept
5213 {
5214 return status_file_type(ec) == file_type::socket;
5215 }
5216
5217 #ifdef GHC_WITH_EXCEPTIONS
5218 GHC_INLINE bool directory_entry::is_symlink() const
5219 {
5220 return _symlink_status.type() != file_type::none ? _symlink_status.type() == file_type::symlink : filesystem::is_symlink(symlink_status());
5221 }
5222 #endif
5223
5224 GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept
5225 {
5226 if(_symlink_status.type() != file_type::none) {
5227 ec.clear();
5228 return _symlink_status.type() == file_type::symlink;
5229 }
5230 return filesystem::is_symlink(symlink_status(ec));
5231 }
5232
5233 #ifdef GHC_WITH_EXCEPTIONS
5234 GHC_INLINE uintmax_t directory_entry::file_size() const
5235 {
5236 if (_file_size != static_cast<uintmax_t>(-1)) {
5237 return _file_size;
5238 }
5239 return filesystem::file_size(path());
5240 }
5241 #endif
5242
5243 GHC_INLINE uintmax_t directory_entry::file_size(std::error_code& ec) const noexcept
5244 {
5245 if (_file_size != static_cast<uintmax_t>(-1)) {
5246 ec.clear();
5247 return _file_size;
5248 }
5249 return filesystem::file_size(path(), ec);
5250 }
5251
5252 #ifndef GHC_OS_WEB
5253 #ifdef GHC_WITH_EXCEPTIONS
5254 GHC_INLINE uintmax_t directory_entry::hard_link_count() const
5255 {
5256 #ifndef GHC_OS_WINDOWS
5257 if (_hard_link_count != static_cast<uintmax_t>(-1)) {
5258 return _hard_link_count;
5259 }
5260 #endif
5261 return filesystem::hard_link_count(path());
5262 }
5263 #endif
5264
5265 GHC_INLINE uintmax_t directory_entry::hard_link_count(std::error_code& ec) const noexcept
5266 {
5267 #ifndef GHC_OS_WINDOWS
5268 if (_hard_link_count != static_cast<uintmax_t>(-1)) {
5269 ec.clear();
5270 return _hard_link_count;
5271 }
5272 #endif
5273 return filesystem::hard_link_count(path(), ec);
5274 }
5275 #endif
5276
5277 #ifdef GHC_WITH_EXCEPTIONS
5278 GHC_INLINE file_time_type directory_entry::last_write_time() const
5279 {
5280 if (_last_write_time != 0) {
5281 return std::chrono::system_clock::from_time_t(_last_write_time);
5282 }
5283 return filesystem::last_write_time(path());
5284 }
5285 #endif
5286
5287 GHC_INLINE file_time_type directory_entry::last_write_time(std::error_code& ec) const noexcept
5288 {
5289 if (_last_write_time != 0) {
5290 ec.clear();
5291 return std::chrono::system_clock::from_time_t(_last_write_time);
5292 }
5293 return filesystem::last_write_time(path(), ec);
5294 }
5295
5296 #ifdef GHC_WITH_EXCEPTIONS
5297 GHC_INLINE file_status directory_entry::status() const
5298 {
5299 if (_status.type() != file_type::none && _status.permissions() != perms::unknown) {
5300 return _status;
5301 }
5302 return filesystem::status(path());
5303 }
5304 #endif
5305
5306 GHC_INLINE file_status directory_entry::status(std::error_code& ec) const noexcept
5307 {
5308 if (_status.type() != file_type::none && _status.permissions() != perms::unknown) {
5309 ec.clear();
5310 return _status;
5311 }
5312 return filesystem::status(path(), ec);
5313 }
5314
5315 #ifdef GHC_WITH_EXCEPTIONS
5316 GHC_INLINE file_status directory_entry::symlink_status() const
5317 {
5318 if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) {
5319 return _symlink_status;
5320 }
5321 return filesystem::symlink_status(path());
5322 }
5323 #endif
5324
5325 GHC_INLINE file_status directory_entry::symlink_status(std::error_code& ec) const noexcept
5326 {
5327 if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) {
5328 ec.clear();
5329 return _symlink_status;
5330 }
5331 return filesystem::symlink_status(path(), ec);
5332 }
5333
5334 #ifdef GHC_HAS_THREEWAY_COMP
5335 GHC_INLINE std::strong_ordering directory_entry::operator<=>(const directory_entry& rhs) const noexcept
5336 {
5337 return _path <=> rhs._path;
5338 }
5339 #endif
5340
5341 GHC_INLINE bool directory_entry::operator<(const directory_entry& rhs) const noexcept
5342 {
5343 return _path < rhs._path;
5344 }
5345
5346 GHC_INLINE bool directory_entry::operator==(const directory_entry& rhs) const noexcept
5347 {
5348 return _path == rhs._path;
5349 }
5350
5351 GHC_INLINE bool directory_entry::operator!=(const directory_entry& rhs) const noexcept
5352 {
5353 return _path != rhs._path;
5354 }
5355
5356 GHC_INLINE bool directory_entry::operator<=(const directory_entry& rhs) const noexcept
5357 {
5358 return _path <= rhs._path;
5359 }
5360
5361 GHC_INLINE bool directory_entry::operator>(const directory_entry& rhs) const noexcept
5362 {
5363 return _path > rhs._path;
5364 }
5365
5366 GHC_INLINE bool directory_entry::operator>=(const directory_entry& rhs) const noexcept
5367 {
5368 return _path >= rhs._path;
5369 }
5370
5371 //-----------------------------------------------------------------------------
5372 // 30.10.13 class directory_iterator
5373
5374 #ifdef GHC_OS_WINDOWS
5375 class directory_iterator::impl
5376 {
5377 public:
5378 impl(const path& p, directory_options options)
5379 : _base(p)
5380 , _options(options)
5381 , _dirHandle(INVALID_HANDLE_VALUE)
5382 {
5383 if (!_base.empty()) {
5384 ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW));
5385 if ((_dirHandle = FindFirstFileW(GHC_NATIVEWP((_base / "*")), &_findData)) != INVALID_HANDLE_VALUE) {
5386 if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") {
5387 increment(_ec);
5388 }
5389 else {
5390 _dir_entry._path = _base / std::wstring(_findData.cFileName);
5391 copyToDirEntry(_ec);
5392 }
5393 }
5394 else {
5395 auto error = ::GetLastError();
5396 _base = filesystem::path();
5397 if (error != ERROR_ACCESS_DENIED || (options & directory_options::skip_permission_denied) == directory_options::none) {
5398 _ec = detail::make_system_error();
5399 }
5400 }
5401 }
5402 }
5403 impl(const impl& other) = delete;
5404 ~impl()
5405 {
5406 if (_dirHandle != INVALID_HANDLE_VALUE) {
5407 FindClose(_dirHandle);
5408 _dirHandle = INVALID_HANDLE_VALUE;
5409 }
5410 }
5411 void increment(std::error_code& ec)
5412 {
5413 if (_dirHandle != INVALID_HANDLE_VALUE) {
5414 do {
5415 if (FindNextFileW(_dirHandle, &_findData)) {
5416 _dir_entry._path = _base;
5417 #ifdef GHC_USE_WCHAR_T
5418 _dir_entry._path.append_name(_findData.cFileName);
5419 #else
5420 #ifdef GHC_RAISE_UNICODE_ERRORS
5421 try {
5422 _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str());
5423 }
5424 catch (filesystem_error& fe) {
5425 ec = fe.code();
5426 return;
5427 }
5428 #else
5429 _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str());
5430 #endif
5431 #endif
5432 copyToDirEntry(ec);
5433 }
5434 else {
5435 auto err = ::GetLastError();
5436 if (err != ERROR_NO_MORE_FILES) {
5437 _ec = ec = detail::make_system_error(err);
5438 }
5439 FindClose(_dirHandle);
5440 _dirHandle = INVALID_HANDLE_VALUE;
5441 _dir_entry._path.clear();
5442 break;
5443 }
5444 } while (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..");
5445 }
5446 else {
5447 ec = _ec;
5448 }
5449 }
5450 void copyToDirEntry(std::error_code& ec)
5451 {
5452 if (_findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
5453 _dir_entry._status = detail::status_ex(_dir_entry._path, ec, &_dir_entry._symlink_status, &_dir_entry._file_size, nullptr, &_dir_entry._last_write_time);
5454 }
5455 else {
5456 _dir_entry._status = detail::status_from_INFO(_dir_entry._path, &_findData, ec, &_dir_entry._file_size, &_dir_entry._last_write_time);
5457 _dir_entry._symlink_status = _dir_entry._status;
5458 }
5459 if (ec) {
5460 if (_dir_entry._status.type() != file_type::none && _dir_entry._symlink_status.type() != file_type::none) {
5461 ec.clear();
5462 }
5463 else {
5464 _dir_entry._file_size = static_cast<uintmax_t>(-1);
5465 _dir_entry._last_write_time = 0;
5466 }
5467 }
5468 }
5469 path _base;
5470 directory_options _options;
5471 WIN32_FIND_DATAW _findData;
5472 HANDLE _dirHandle;
5473 directory_entry _dir_entry;
5474 std::error_code _ec;
5475 };
5476 #else
5477 // POSIX implementation
5478 class directory_iterator::impl
5479 {
5480 public:
5481 impl(const path& path, directory_options options)
5482 : _base(path)
5483 , _options(options)
5484 , _dir(nullptr)
5485 , _entry(nullptr)
5486 {
5487 if (!path.empty()) {
5488 _dir = ::opendir(path.native().c_str());
5489 if (!_dir) {
5490 auto error = errno;
5491 _base = filesystem::path();
5492 if ((error != EACCES && error != EPERM) || (options & directory_options::skip_permission_denied) == directory_options::none) {
5493 _ec = detail::make_system_error();
5494 }
5495 }
5496 else {
5497 increment(_ec);
5498 }
5499 }
5500 }
5501 impl(const impl& other) = delete;
5502 ~impl()
5503 {
5504 if (_dir) {
5505 ::closedir(_dir);
5506 }
5507 }
5508 void increment(std::error_code& ec)
5509 {
5510 if (_dir) {
5511 bool skip;
5512 do {
5513 skip = false;
5514 errno = 0;
5515 _entry = ::readdir(_dir);
5516 if (_entry) {
5517 _dir_entry._path = _base;
5518 _dir_entry._path.append_name(_entry->d_name);
5519 copyToDirEntry();
5520 if (ec && (ec.value() == EACCES || ec.value() == EPERM) && (_options & directory_options::skip_permission_denied) == directory_options::skip_permission_denied) {
5521 ec.clear();
5522 skip = true;
5523 }
5524 }
5525 else {
5526 ::closedir(_dir);
5527 _dir = nullptr;
5528 _dir_entry._path.clear();
5529 if (errno) {
5530 ec = detail::make_system_error();
5531 }
5532 break;
5533 }
5534 } while (skip || std::strcmp(_entry->d_name, ".") == 0 || std::strcmp(_entry->d_name, "..") == 0);
5535 }
5536 }
5537 void copyToDirEntry()
5538 {
5539 _dir_entry._symlink_status.permissions(perms::unknown);
5540 switch(_entry->d_type) {
5541 case DT_BLK: _dir_entry._symlink_status.type(file_type::block); break;
5542 case DT_CHR: _dir_entry._symlink_status.type(file_type::character); break;
5543 case DT_DIR: _dir_entry._symlink_status.type(file_type::directory); break;
5544 case DT_FIFO: _dir_entry._symlink_status.type(file_type::fifo); break;
5545 case DT_LNK: _dir_entry._symlink_status.type(file_type::symlink); break;
5546 case DT_REG: _dir_entry._symlink_status.type(file_type::regular); break;
5547 case DT_SOCK: _dir_entry._symlink_status.type(file_type::socket); break;
5548 default: _dir_entry._symlink_status.type(file_type::unknown); break;
5549 }
5550 if (_entry->d_type != DT_LNK) {
5551 _dir_entry._status = _dir_entry._symlink_status;
5552 }
5553 else {
5554 _dir_entry._status.type(file_type::none);
5555 _dir_entry._status.permissions(perms::unknown);
5556 }
5557 _dir_entry._file_size = static_cast<uintmax_t>(-1);
5558 _dir_entry._hard_link_count = static_cast<uintmax_t>(-1);
5559 _dir_entry._last_write_time = 0;
5560 }
5561 path _base;
5562 directory_options _options;
5563 DIR* _dir;
5564 struct ::dirent* _entry;
5565 directory_entry _dir_entry;
5566 std::error_code _ec;
5567 };
5568 #endif
5569
5570 // 30.10.13.1 member functions
5571 GHC_INLINE directory_iterator::directory_iterator() noexcept
5572 : _impl(new impl(path(), directory_options::none))
5573 {
5574 }
5575
5576 #ifdef GHC_WITH_EXCEPTIONS
5577 GHC_INLINE directory_iterator::directory_iterator(const path& p)
5578 : _impl(new impl(p, directory_options::none))
5579 {
5580 if (_impl->_ec) {
5581 throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec);
5582 }
5583 _impl->_ec.clear();
5584 }
5585
5586 GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options)
5587 : _impl(new impl(p, options))
5588 {
5589 if (_impl->_ec) {
5590 throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec);
5591 }
5592 }
5593 #endif
5594
5595 GHC_INLINE directory_iterator::directory_iterator(const path& p, std::error_code& ec) noexcept
5596 : _impl(new impl(p, directory_options::none))
5597 {
5598 if (_impl->_ec) {
5599 ec = _impl->_ec;
5600 }
5601 }
5602
5603 GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept
5604 : _impl(new impl(p, options))
5605 {
5606 if (_impl->_ec) {
5607 ec = _impl->_ec;
5608 }
5609 }
5610
5611 GHC_INLINE directory_iterator::directory_iterator(const directory_iterator& rhs)
5612 : _impl(rhs._impl)
5613 {
5614 }
5615
5616 GHC_INLINE directory_iterator::directory_iterator(directory_iterator&& rhs) noexcept
5617 : _impl(std::move(rhs._impl))
5618 {
5619 }
5620
5621 GHC_INLINE directory_iterator::~directory_iterator() {}
5622
5623 GHC_INLINE directory_iterator& directory_iterator::operator=(const directory_iterator& rhs)
5624 {
5625 _impl = rhs._impl;
5626 return *this;
5627 }
5628
5629 GHC_INLINE directory_iterator& directory_iterator::operator=(directory_iterator&& rhs) noexcept
5630 {
5631 _impl = std::move(rhs._impl);
5632 return *this;
5633 }
5634
5635 GHC_INLINE const directory_entry& directory_iterator::operator*() const
5636 {
5637 return _impl->_dir_entry;
5638 }
5639
5640 GHC_INLINE const directory_entry* directory_iterator::operator->() const
5641 {
5642 return &_impl->_dir_entry;
5643 }
5644
5645 #ifdef GHC_WITH_EXCEPTIONS
5646 GHC_INLINE directory_iterator& directory_iterator::operator++()
5647 {
5648 std::error_code ec;
5649 _impl->increment(ec);
5650 if (ec) {
5651 throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_entry._path, ec);
5652 }
5653 return *this;
5654 }
5655 #endif
5656
5657 GHC_INLINE directory_iterator& directory_iterator::increment(std::error_code& ec) noexcept
5658 {
5659 _impl->increment(ec);
5660 return *this;
5661 }
5662
5663 GHC_INLINE bool directory_iterator::operator==(const directory_iterator& rhs) const
5664 {
5665 return _impl->_dir_entry._path == rhs._impl->_dir_entry._path;
5666 }
5667
5668 GHC_INLINE bool directory_iterator::operator!=(const directory_iterator& rhs) const
5669 {
5670 return _impl->_dir_entry._path != rhs._impl->_dir_entry._path;
5671 }
5672
5673 // 30.10.13.2 directory_iterator non-member functions
5674
5675 GHC_INLINE directory_iterator begin(directory_iterator iter) noexcept
5676 {
5677 return iter;
5678 }
5679
5680 GHC_INLINE directory_iterator end(const directory_iterator&) noexcept
5681 {
5682 return directory_iterator();
5683 }
5684
5685 //-----------------------------------------------------------------------------
5686 // 30.10.14 class recursive_directory_iterator
5687
5688 GHC_INLINE recursive_directory_iterator::recursive_directory_iterator() noexcept
5689 : _impl(new recursive_directory_iterator_impl(directory_options::none, true))
5690 {
5691 _impl->_dir_iter_stack.push(directory_iterator());
5692 }
5693
5694 #ifdef GHC_WITH_EXCEPTIONS
5695 GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p)
5696 : _impl(new recursive_directory_iterator_impl(directory_options::none, true))
5697 {
5698 _impl->_dir_iter_stack.push(directory_iterator(p));
5699 }
5700
5701 GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options)
5702 : _impl(new recursive_directory_iterator_impl(options, true))
5703 {
5704 _impl->_dir_iter_stack.push(directory_iterator(p, options));
5705 }
5706 #endif
5707
5708 GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept
5709 : _impl(new recursive_directory_iterator_impl(options, true))
5710 {
5711 _impl->_dir_iter_stack.push(directory_iterator(p, options, ec));
5712 }
5713
5714 GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, std::error_code& ec) noexcept
5715 : _impl(new recursive_directory_iterator_impl(directory_options::none, true))
5716 {
5717 _impl->_dir_iter_stack.push(directory_iterator(p, ec));
5718 }
5719
5720 GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const recursive_directory_iterator& rhs)
5721 : _impl(rhs._impl)
5722 {
5723 }
5724
5725 GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept
5726 : _impl(std::move(rhs._impl))
5727 {
5728 }
5729
5730 GHC_INLINE recursive_directory_iterator::~recursive_directory_iterator() {}
5731
5732 // 30.10.14.1 observers
5733 GHC_INLINE directory_options recursive_directory_iterator::options() const
5734 {
5735 return _impl->_options;
5736 }
5737
5738 GHC_INLINE int recursive_directory_iterator::depth() const
5739 {
5740 return static_cast<int>(_impl->_dir_iter_stack.size() - 1);
5741 }
5742
5743 GHC_INLINE bool recursive_directory_iterator::recursion_pending() const
5744 {
5745 return _impl->_recursion_pending;
5746 }
5747
5748 GHC_INLINE const directory_entry& recursive_directory_iterator::operator*() const
5749 {
5750 return *(_impl->_dir_iter_stack.top());
5751 }
5752
5753 GHC_INLINE const directory_entry* recursive_directory_iterator::operator->() const
5754 {
5755 return &(*(_impl->_dir_iter_stack.top()));
5756 }
5757
5758 // 30.10.14.1 modifiers recursive_directory_iterator&
5759 GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(const recursive_directory_iterator& rhs)
5760 {
5761 _impl = rhs._impl;
5762 return *this;
5763 }
5764
5765 GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(recursive_directory_iterator&& rhs) noexcept
5766 {
5767 _impl = std::move(rhs._impl);
5768 return *this;
5769 }
5770
5771 #ifdef GHC_WITH_EXCEPTIONS
5772 GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator++()
5773 {
5774 std::error_code ec;
5775 increment(ec);
5776 if (ec) {
5777 throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec);
5778 }
5779 return *this;
5780 }
5781 #endif
5782
5783 GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment(std::error_code& ec) noexcept
5784 {
5785 bool isDir = (*this)->is_directory(ec);
5786 bool isSymLink = !ec && (*this)->is_symlink(ec);
5787 if(!ec) {
5788 if (recursion_pending() && isDir && (!isSymLink || (options() & directory_options::follow_directory_symlink) != directory_options::none)) {
5789 _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec));
5790 }
5791 else {
5792 _impl->_dir_iter_stack.top().increment(ec);
5793 }
5794 if (!ec) {
5795 while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) {
5796 _impl->_dir_iter_stack.pop();
5797 _impl->_dir_iter_stack.top().increment(ec);
5798 }
5799 }
5800 else if (!_impl->_dir_iter_stack.empty()) {
5801 _impl->_dir_iter_stack.pop();
5802 }
5803 _impl->_recursion_pending = true;
5804 }
5805 return *this;
5806 }
5807
5808 #ifdef GHC_WITH_EXCEPTIONS
5809 GHC_INLINE void recursive_directory_iterator::pop()
5810 {
5811 std::error_code ec;
5812 pop(ec);
5813 if (ec) {
5814 throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec);
5815 }
5816 }
5817 #endif
5818
5819 GHC_INLINE void recursive_directory_iterator::pop(std::error_code& ec)
5820 {
5821 if (depth() == 0) {
5822 *this = recursive_directory_iterator();
5823 }
5824 else {
5825 do {
5826 _impl->_dir_iter_stack.pop();
5827 _impl->_dir_iter_stack.top().increment(ec);
5828 } while (depth() && _impl->_dir_iter_stack.top() == directory_iterator());
5829 }
5830 }
5831
5832 GHC_INLINE void recursive_directory_iterator::disable_recursion_pending()
5833 {
5834 _impl->_recursion_pending = false;
5835 }
5836
5837 // other members as required by 27.2.3, input iterators
5838 GHC_INLINE bool recursive_directory_iterator::operator==(const recursive_directory_iterator& rhs) const
5839 {
5840 return _impl->_dir_iter_stack.top() == rhs._impl->_dir_iter_stack.top();
5841 }
5842
5843 GHC_INLINE bool recursive_directory_iterator::operator!=(const recursive_directory_iterator& rhs) const
5844 {
5845 return _impl->_dir_iter_stack.top() != rhs._impl->_dir_iter_stack.top();
5846 }
5847
5848 // 30.10.14.2 directory_iterator non-member functions
5849 GHC_INLINE recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept
5850 {
5851 return iter;
5852 }
5853
5854 GHC_INLINE recursive_directory_iterator end(const recursive_directory_iterator&) noexcept
5855 {
5856 return recursive_directory_iterator();
5857 }
5858
5859 #endif // GHC_EXPAND_IMPL
5860
5861 } // namespace filesystem
5862 } // namespace ghc
5863
5864 // cleanup some macros
5865 #undef GHC_INLINE
5866 #undef GHC_EXPAND_IMPL
5867
5868 #endif // GHC_FILESYSTEM_H
5869