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