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