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