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