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