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