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