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