From c6a38eab19a11847024a13f9b3e2af0c2d908c3e Mon Sep 17 00:00:00 2001 From: Iliyas Jorio Date: Thu, 28 Sep 2023 22:11:18 +0200 Subject: [PATCH] Update ghc::filesystem to v1.5.14 --- .../filesystem_implementation.hpp | 2190 ++++++++++------- 1 file changed, 1292 insertions(+), 898 deletions(-) diff --git a/src/CompilerSupport/filesystem_implementation.hpp b/src/CompilerSupport/filesystem_implementation.hpp index f0eed3a..53f4ad4 100644 --- a/src/CompilerSupport/filesystem_implementation.hpp +++ b/src/CompilerSupport/filesystem_implementation.hpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------------------- // -// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14/C++17 +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14/C++17/C++20 // //--------------------------------------------------------------------------------------- // @@ -26,12 +26,17 @@ // //--------------------------------------------------------------------------------------- // -// To dynamically select std::filesystem where available, you could use: +// To dynamically select std::filesystem where available on most platforms, +// you could use: // -// #if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include() +// #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +// #if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +// #define GHC_USE_STD_FS // #include // namespace fs = std::filesystem; -// #else +// #endif +// #endif +// #ifndef GHC_USE_STD_FS // #include // namespace fs = ghc::filesystem; // #endif @@ -60,6 +65,10 @@ #elif defined(_WIN32) #define GHC_OS_WINDOWS #define GHC_OS_WIN32 +#elif defined(__CYGWIN__) +#define GHC_OS_CYGWIN +#elif defined(__sun) && defined(__SVR4) +#define GHC_OS_SOLARIS #elif defined(__svr4__) #define GHC_OS_SYS5R4 #elif defined(BSD) @@ -67,37 +76,74 @@ #elif defined(__EMSCRIPTEN__) #define GHC_OS_WEB #include +#elif defined(__QNX__) +#define GHC_OS_QNX +#elif defined(__HAIKU__) +#define GHC_OS_HAIKU #else #error "Operating system currently not supported!" #endif #define GHC_OS_DETECTED +#if (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#if _MSVC_LANG == 201703L +#define GHC_FILESYSTEM_RUNNING_CPP17 +#else +#define GHC_FILESYSTEM_RUNNING_CPP20 +#endif +#elif (defined(__cplusplus) && __cplusplus >= 201703L) +#if __cplusplus == 201703L +#define GHC_FILESYSTEM_RUNNING_CPP17 +#else +#define GHC_FILESYSTEM_RUNNING_CPP20 +#endif +#endif #endif #if defined(GHC_FILESYSTEM_IMPLEMENTATION) #define GHC_EXPAND_IMPL #define GHC_INLINE #ifdef GHC_OS_WINDOWS +#ifndef GHC_FS_API #define GHC_FS_API +#endif +#ifndef GHC_FS_API_CLASS #define GHC_FS_API_CLASS +#endif #else +#ifndef GHC_FS_API #define GHC_FS_API __attribute__((visibility("default"))) +#endif +#ifndef GHC_FS_API_CLASS #define GHC_FS_API_CLASS __attribute__((visibility("default"))) #endif +#endif #elif defined(GHC_FILESYSTEM_FWD) #define GHC_INLINE #ifdef GHC_OS_WINDOWS +#ifndef GHC_FS_API #define GHC_FS_API extern +#endif +#ifndef GHC_FS_API_CLASS #define GHC_FS_API_CLASS +#endif #else +#ifndef GHC_FS_API #define GHC_FS_API extern +#endif +#ifndef GHC_FS_API_CLASS #define GHC_FS_API_CLASS #endif +#endif #else #define GHC_EXPAND_IMPL #define GHC_INLINE inline +#ifndef GHC_FS_API #define GHC_FS_API +#endif +#ifndef GHC_FS_API_CLASS #define GHC_FS_API_CLASS #endif +#endif #ifdef GHC_EXPAND_IMPL @@ -112,12 +158,12 @@ #else #include #include +#include #include #include #include #include #include -#include #ifdef GHC_OS_ANDROID #include #if __ANDROID_API__ < 12 @@ -128,14 +174,24 @@ #else #include #endif +#ifdef GHC_OS_CYGWIN +#include +#endif #if !defined(__ANDROID__) || __ANDROID_API__ >= 26 #include #endif #endif -#if defined(GHC_OS_MACOS) && !defined(__POWERPC__) +#ifdef GHC_OS_MACOS #include #endif +#if defined(__cpp_impl_three_way_comparison) && defined(__has_include) +#if __has_include() +#define GHC_HAS_THREEWAY_COMP +#include +#endif +#endif + #include #include #include @@ -154,6 +210,13 @@ #include #else // GHC_EXPAND_IMPL + +#if defined(__cpp_impl_three_way_comparison) && defined(__has_include) +#if __has_include() +#define GHC_HAS_THREEWAY_COMP +#include +#endif +#endif #include #include #include @@ -166,15 +229,51 @@ #endif #endif // GHC_EXPAND_IMPL +// After standard library includes. +// Standard library support for std::string_view. +#if defined(__cpp_lib_string_view) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 4000) && (__cplusplus >= 201402) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 7) && (__cplusplus >= 201703) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_MSC_VER) && (_MSC_VER >= 1910 && _MSVC_LANG >= 201703) +#define GHC_HAS_STD_STRING_VIEW +#endif + +// Standard library support for std::experimental::string_view. +#if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 3700 && _LIBCPP_VERSION < 7000) && (__cplusplus >= 201402) +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#elif defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4)) && (__cplusplus >= 201402) +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#elif defined(__GLIBCXX__) && defined(_GLIBCXX_USE_DUAL_ABI) && (__cplusplus >= 201402) +// macro _GLIBCXX_USE_DUAL_ABI is always defined in libstdc++ from gcc-5 and newer +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#endif + +#if defined(GHC_HAS_STD_STRING_VIEW) +#include +#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) +#include +#endif + +#if !defined(GHC_OS_WINDOWS) && !defined(PATH_MAX) +#define PATH_MAX 4096 +#endif + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp): //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Enforce C++17 API where possible when compiling for C++20, handles the following cases: +// * fs::path::u8string() returns std::string instead of std::u8string +// #define GHC_FILESYSTEM_ENFORCE_CPP17_API +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories // configure LWG conformance () #define LWG_2682_BEHAVIOUR //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular -// file with that name, it is superceded by P1164R1, so only activate if really needed +// file with that name, it is superseded by P1164R1, so only activate if really needed // #define LWG_2935_BEHAVIOUR //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // LWG #2936 enables new element wise (more expensive) path comparison @@ -182,36 +281,55 @@ // * if this->has_root_directory() and !p.has_root_directory() return -1 // * if !this->has_root_directory() and p.has_root_directory() return -1 // * else result of element wise comparison of path iteration where first comparison is != 0 or 0 -// if all comparisons are 0 (on Windows this implementation does case insensitive root_name() +// if all comparisons are 0 (on Windows this implementation does case-insensitive root_name() // comparison) -// #define LWG_2936_BEHAVIOUR +#define LWG_2936_BEHAVIOUR //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) #define LWG_2937_BEHAVIOUR //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// UTF8-Everywhere is the original behaviour of ghc::filesystem. With this define you can -// enable the more standard conforming implementation option that uses wstring on Windows -// as ghc::filesystem::string_type. -// #define GHC_WIN_WSTRING_STRING_TYPE +// UTF8-Everywhere is the original behaviour of ghc::filesystem. But since v1.5 the Windows +// version defaults to std::wstring storage backend. Still all std::string will be interpreted +// as UTF-8 encoded. With this define you can enforce the old behavior on Windows, using +// std::string as backend and for fs::path::native() and char for fs::path::c_str(). This +// needs more conversions, so it is (and was before v1.5) slower, bot might help keeping source +// homogeneous in a multi-platform project. +// #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Raise errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found, // instead of replacing them with the unicode replacement character (U+FFFD). // #define GHC_RAISE_UNICODE_ERRORS //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Automatic prefix windows path with "\\?\" if they would break the MAX_PATH length. +// instead of replacing them with the unicode replacement character (U+FFFD). +#ifndef GHC_WIN_DISABLE_AUTO_PREFIXES +#define GHC_WIN_AUTO_PREFIX_LONG_PATH +#endif // GHC_WIN_DISABLE_AUTO_PREFIXES +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) -#define GHC_FILESYSTEM_VERSION 10306L +#define GHC_FILESYSTEM_VERSION 10514L #if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)) #define GHC_WITH_EXCEPTIONS #endif #if !defined(GHC_WITH_EXCEPTIONS) && defined(GHC_RAISE_UNICODE_ERRORS) -#error "Can't raise unicode errors whith exception support disabled" +#error "Can't raise unicode errors with exception support disabled" #endif namespace ghc { namespace filesystem { +#if defined(GHC_HAS_CUSTOM_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +#elif defined(GHC_HAS_STD_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +using std::basic_string_view; +#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +using std::experimental::basic_string_view; +#endif + // temporary existing exception type for yet unimplemented parts class GHC_FS_API_CLASS not_implemented_exception : public std::logic_error { @@ -222,7 +340,7 @@ public: } }; -template +template class path_helper_base { public: @@ -234,12 +352,11 @@ public: #endif }; -#if __cplusplus < 201703L +#if __cplusplus < 201703L template constexpr char_type path_helper_base::preferred_separator; #endif - #ifdef GHC_OS_WINDOWS class path; namespace detail { @@ -247,16 +364,19 @@ bool has_executable_extension(const path& p); } #endif - - // 30.10.8 class path +// [fs.class.path] class path class GHC_FS_API_CLASS path -#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_WSTRING_STRING_TYPE) +#if defined(GHC_OS_WINDOWS) && !defined(GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE) #define GHC_USE_WCHAR_T +#define GHC_NATIVEWP(p) p.c_str() +#define GHC_PLATFORM_LITERAL(str) L##str : private path_helper_base { public: using path_helper_base::value_type; #else +#define GHC_NATIVEWP(p) p.wstring().c_str() +#define GHC_PLATFORM_LITERAL(str) str : private path_helper_base { public: @@ -264,9 +384,9 @@ public: #endif using string_type = std::basic_string; using path_helper_base::preferred_separator; - - // 30.10.10.1 enumeration format - /// The path format in wich the constructor argument is given. + + // [fs.enum.path.format] enumeration format + /// The path format in which the constructor argument is given. enum format { generic_format, ///< The generic format, internally used by ///< ghc::filesystem::path with slashes @@ -283,29 +403,43 @@ public: struct _is_basic_string> : std::true_type { }; -#ifdef __cpp_lib_string_view template - struct _is_basic_string> : std::true_type + struct _is_basic_string, std::allocator>> : std::true_type + { + }; +#ifdef GHC_WITH_STRING_VIEW + template + struct _is_basic_string> : std::true_type + { + }; + template + struct _is_basic_string>> : std::true_type { }; #endif template using path_type = typename std::enable_if::value, path>::type; -#ifdef GHC_USE_WCHAR_T template - using path_from_string = typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || - std::is_same::type>::value || std::is_same::type>::value, - path>::type; +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + using path_from_string = + typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value, + path>::type; template - using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value, path>::type; + using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; #else - template - using path_from_string = typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value, path>::type; + using path_from_string = + typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value, + path>::type; template using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; #endif - // 30.10.8.4.1 constructors and destructor + // [fs.path.construct] constructors and destructor path() noexcept; path(const path& p); path(path&& p) noexcept; @@ -322,7 +456,7 @@ public: #endif ~path(); - // 30.10.8.4.2 assignments + // [fs.path.assign] assignments path& operator=(const path& p); path& operator=(path&& p) noexcept; path& operator=(string_type&& source); @@ -334,7 +468,7 @@ public: template path& assign(InputIterator first, InputIterator last); - // 30.10.8.4.3 appends + // [fs.path.append] appends path& operator/=(const path& p); template path& operator/=(const Source& source); @@ -343,11 +477,11 @@ public: template path& append(InputIterator first, InputIterator last); - // 30.10.8.4.4 concatenation + // [fs.path.concat] concatenation path& operator+=(const path& x); path& operator+=(const string_type& x); -#ifdef __cpp_lib_string_view - path& operator+=(std::basic_string_view x); +#ifdef GHC_WITH_STRING_VIEW + path& operator+=(basic_string_view x); #endif path& operator+=(const value_type* x); path& operator+=(value_type x); @@ -360,7 +494,7 @@ public: template path& concat(InputIterator first, InputIterator last); - // 30.10.8.4.5 modifiers + // [fs.path.modifiers] modifiers void clear() noexcept; path& make_preferred(); path& remove_filename(); @@ -368,36 +502,44 @@ public: path& replace_extension(const path& replacement = path()); void swap(path& rhs) noexcept; - // 30.10.8.4.6 native format observers - const string_type& native() const; // this implementation doesn't support noexcept for native() - const value_type* c_str() const; // this implementation doesn't support noexcept for c_str() + // [fs.path.native.obs] native format observers + const string_type& native() const noexcept; + const value_type* c_str() const noexcept; operator string_type() const; template , class Allocator = std::allocator> std::basic_string string(const Allocator& a = Allocator()) const; std::string string() const; std::wstring wstring() const; +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + std::u8string u8string() const; +#else std::string u8string() const; +#endif std::u16string u16string() const; std::u32string u32string() const; - // 30.10.8.4.7 generic format observers + // [fs.path.generic.obs] generic format observers template , class Allocator = std::allocator> std::basic_string generic_string(const Allocator& a = Allocator()) const; - const std::string& generic_string() const; // this is different from the standard, that returns by value + std::string generic_string() const; std::wstring generic_wstring() const; +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + std::u8string generic_u8string() const; +#else std::string generic_u8string() const; +#endif std::u16string generic_u16string() const; std::u32string generic_u32string() const; - // 30.10.8.4.8 compare + // [fs.path.compare] compare int compare(const path& p) const noexcept; int compare(const string_type& s) const; -#ifdef __cpp_lib_string_view - int compare(std::basic_string_view s) const; +#ifdef GHC_WITH_STRING_VIEW + int compare(basic_string_view s) const; #endif int compare(const value_type* s) const; - // 30.10.8.4.9 decomposition + // [fs.path.decompose] decomposition path root_name() const; path root_directory() const; path root_path() const; @@ -407,7 +549,7 @@ public: path stem() const; path extension() const; - // 30.10.8.4.10 query + // [fs.path.query] query bool empty() const noexcept; bool has_root_name() const; bool has_root_directory() const; @@ -420,22 +562,22 @@ public: bool is_absolute() const; bool is_relative() const; - // 30.10.8.4.11 generation + // [fs.path.gen] generation path lexically_normal() const; path lexically_relative(const path& base) const; path lexically_proximate(const path& base) const; - // 30.10.8.5 iterators + // [fs.path.itr] iterators class iterator; using const_iterator = iterator; iterator begin() const; iterator end() const; private: - using impl_value_type = std::string::value_type; + using impl_value_type = value_type; using impl_string_type = std::basic_string; friend class directory_iterator; - void append_name(const char* name); + void append_name(const value_type* name); static constexpr impl_value_type generic_separator = '/'; template class input_iterator_range @@ -460,43 +602,58 @@ private: }; friend void swap(path& lhs, path& rhs) noexcept; friend size_t hash_value(const path& p) noexcept; + friend path canonical(const path& p, std::error_code& ec); + friend bool create_directories(const path& p, std::error_code& ec) noexcept; string_type::size_type root_name_length() const noexcept; - static void postprocess_path_with_format(impl_string_type& p, format fmt); + void postprocess_path_with_format(format fmt); + void check_long_path(); impl_string_type _path; #ifdef GHC_OS_WINDOWS + void handle_prefixes(); friend bool detail::has_executable_extension(const path& p); - impl_string_type native_impl() const; - mutable string_type _native_cache; +#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH + string_type::size_type _prefixLength{0}; +#else // GHC_WIN_AUTO_PREFIX_LONG_PATH + static const string_type::size_type _prefixLength{0}; +#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH #else - const impl_string_type& native_impl() const; + static const string_type::size_type _prefixLength{0}; #endif }; -// 30.10.8.6 path non-member functions +// [fs.path.nonmember] path non-member functions GHC_FS_API void swap(path& lhs, path& rhs) noexcept; GHC_FS_API size_t hash_value(const path& p) noexcept; +#ifdef GHC_HAS_THREEWAY_COMP +GHC_FS_API std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept; +#endif GHC_FS_API bool operator==(const path& lhs, const path& rhs) noexcept; GHC_FS_API bool operator!=(const path& lhs, const path& rhs) noexcept; GHC_FS_API bool operator<(const path& lhs, const path& rhs) noexcept; GHC_FS_API bool operator<=(const path& lhs, const path& rhs) noexcept; GHC_FS_API bool operator>(const path& lhs, const path& rhs) noexcept; GHC_FS_API bool operator>=(const path& lhs, const path& rhs) noexcept; - GHC_FS_API path operator/(const path& lhs, const path& rhs); -// 30.10.8.6.1 path inserter and extractor +// [fs.path.io] path inserter and extractor template std::basic_ostream& operator<<(std::basic_ostream& os, const path& p); template std::basic_istream& operator>>(std::basic_istream& is, path& p); -// 30.10.8.6.2 path factory functions +// [pfs.path.factory] path factory functions template > +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]] +#endif path u8path(const Source& source); template +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]] +#endif path u8path(InputIterator first, InputIterator last); -// 30.10.9 class filesystem_error +// [fs.class.filesystem_error] class filesystem_error class GHC_FS_API_CLASS filesystem_error : public std::system_error { public: @@ -523,7 +680,7 @@ public: using iterator_category = std::bidirectional_iterator_tag; iterator(); - iterator(const impl_string_type::const_iterator& first, const impl_string_type::const_iterator& last, const impl_string_type::const_iterator& pos); + iterator(const path& p, const impl_string_type::const_iterator& pos); iterator& operator++(); iterator operator++(int); iterator& operator--(); @@ -534,11 +691,13 @@ public: pointer operator->() const; private: - impl_string_type::const_iterator increment(const std::string::const_iterator& pos) const; - impl_string_type::const_iterator decrement(const std::string::const_iterator& pos) const; + friend class path; + impl_string_type::const_iterator increment(const impl_string_type::const_iterator& pos) const; + impl_string_type::const_iterator decrement(const impl_string_type::const_iterator& pos) const; void updateCurrent(); impl_string_type::const_iterator _first; impl_string_type::const_iterator _last; + impl_string_type::const_iterator _prefix; impl_string_type::const_iterator _root; impl_string_type::const_iterator _iter; path _current; @@ -551,7 +710,8 @@ struct space_info uintmax_t available; }; -// 30.10.10, enumerations +// [fs.enum] enumerations +// [fs.enum.file_type] enum class file_type { none, not_found, @@ -565,6 +725,7 @@ enum class file_type { unknown, }; +// [fs.enum.perms] enum class perms : uint16_t { none = 0, @@ -592,6 +753,7 @@ enum class perms : uint16_t { unknown = 0xffff }; +// [fs.enum.perm.opts] enum class perm_options : uint16_t { replace = 3, add = 1, @@ -599,6 +761,7 @@ enum class perm_options : uint16_t { nofollow = 4, }; +// [fs.enum.copy.opts] enum class copy_options : uint16_t { none = 0, @@ -618,17 +781,18 @@ enum class copy_options : uint16_t { #endif }; +// [fs.enum.dir.opts] enum class directory_options : uint16_t { none = 0, follow_directory_symlink = 1, skip_permission_denied = 2, }; -// 30.10.11 class file_status +// [fs.class.file_status] class file_status class GHC_FS_API_CLASS file_status { public: - // 30.10.11.1 constructors and destructor + // [fs.file_status.cons] constructors and destructor file_status() noexcept; explicit file_status(file_type ft, perms prms = perms::unknown) noexcept; file_status(const file_status&) noexcept; @@ -637,12 +801,13 @@ public: // assignments: file_status& operator=(const file_status&) noexcept; file_status& operator=(file_status&&) noexcept; - // 30.10.11.3 modifiers + // [fs.file_status.mods] modifiers void type(file_type ft) noexcept; void permissions(perms prms) noexcept; - // 30.10.11.2 observers + // [fs.file_status.obs] observers file_type type() const noexcept; perms permissions() const noexcept; + friend bool operator==(const file_status& lhs, const file_status& rhs) noexcept { return lhs.type() == rhs.type() && lhs.permissions() == rhs.permissions(); } private: file_type _type; @@ -651,11 +816,11 @@ private: using file_time_type = std::chrono::time_point; -// 30.10.12 Class directory_entry +// [fs.class.directory_entry] Class directory_entry class GHC_FS_API_CLASS directory_entry { public: - // 30.10.12.1 constructors and destructor + // [fs.dir.entry.cons] constructors and destructor directory_entry() noexcept = default; directory_entry(const directory_entry&) = default; directory_entry(directory_entry&&) noexcept = default; @@ -669,63 +834,47 @@ public: directory_entry& operator=(const directory_entry&) = default; directory_entry& operator=(directory_entry&&) noexcept = default; - // 30.10.12.2 modifiers + // [fs.dir.entry.mods] modifiers #ifdef GHC_WITH_EXCEPTIONS void assign(const path& p); -#endif - void assign(const path& p, std::error_code& ec); -#ifdef GHC_WITH_EXCEPTIONS void replace_filename(const path& p); -#endif - void replace_filename(const path& p, std::error_code& ec); -#ifdef GHC_WITH_EXCEPTIONS void refresh(); #endif + void assign(const path& p, std::error_code& ec); + void replace_filename(const path& p, std::error_code& ec); void refresh(std::error_code& ec) noexcept; - // 30.10.12.3 observers + // [fs.dir.entry.obs] observers const filesystem::path& path() const noexcept; operator const filesystem::path&() const noexcept; #ifdef GHC_WITH_EXCEPTIONS bool exists() const; + bool is_block_file() const; + bool is_character_file() const; + bool is_directory() const; + bool is_fifo() const; + bool is_other() const; + bool is_regular_file() const; + bool is_socket() const; + bool is_symlink() const; + uintmax_t file_size() const; + file_time_type last_write_time() const; + file_status status() const; + file_status symlink_status() const; #endif bool exists(std::error_code& ec) const noexcept; -#ifdef GHC_WITH_EXCEPTIONS - bool is_block_file() const; -#endif bool is_block_file(std::error_code& ec) const noexcept; -#ifdef GHC_WITH_EXCEPTIONS - bool is_character_file() const; -#endif bool is_character_file(std::error_code& ec) const noexcept; -#ifdef GHC_WITH_EXCEPTIONS - bool is_directory() const; -#endif bool is_directory(std::error_code& ec) const noexcept; -#ifdef GHC_WITH_EXCEPTIONS - bool is_fifo() const; -#endif bool is_fifo(std::error_code& ec) const noexcept; -#ifdef GHC_WITH_EXCEPTIONS - bool is_other() const; -#endif bool is_other(std::error_code& ec) const noexcept; -#ifdef GHC_WITH_EXCEPTIONS - bool is_regular_file() const; -#endif bool is_regular_file(std::error_code& ec) const noexcept; -#ifdef GHC_WITH_EXCEPTIONS - bool is_socket() const; -#endif bool is_socket(std::error_code& ec) const noexcept; -#ifdef GHC_WITH_EXCEPTIONS - bool is_symlink() const; -#endif bool is_symlink(std::error_code& ec) const noexcept; -#ifdef GHC_WITH_EXCEPTIONS - uintmax_t file_size() const; -#endif uintmax_t file_size(std::error_code& ec) const noexcept; + file_time_type last_write_time(std::error_code& ec) const noexcept; + file_status status(std::error_code& ec) const noexcept; + file_status symlink_status(std::error_code& ec) const noexcept; #ifndef GHC_OS_WEB #ifdef GHC_WITH_EXCEPTIONS @@ -734,20 +883,9 @@ public: uintmax_t hard_link_count(std::error_code& ec) const noexcept; #endif -#ifdef GHC_WITH_EXCEPTIONS - file_time_type last_write_time() const; +#ifdef GHC_HAS_THREEWAY_COMP + std::strong_ordering operator<=>(const directory_entry& rhs) const noexcept; #endif - file_time_type last_write_time(std::error_code& ec) const noexcept; - -#ifdef GHC_WITH_EXCEPTIONS - file_status status() const; -#endif - file_status status(std::error_code& ec) const noexcept; - -#ifdef GHC_WITH_EXCEPTIONS - file_status symlink_status() const; -#endif - file_status symlink_status(std::error_code& ec) const noexcept; bool operator<(const directory_entry& rhs) const noexcept; bool operator==(const directory_entry& rhs) const noexcept; bool operator!=(const directory_entry& rhs) const noexcept; @@ -757,17 +895,21 @@ public: private: friend class directory_iterator; +#ifdef GHC_WITH_EXCEPTIONS + file_type status_file_type() const; +#endif + file_type status_file_type(std::error_code& ec) const noexcept; filesystem::path _path; file_status _status; file_status _symlink_status; - uintmax_t _file_size = 0; + uintmax_t _file_size = static_cast(-1); #ifndef GHC_OS_WINDOWS - uintmax_t _hard_link_count = 0; + uintmax_t _hard_link_count = static_cast(-1); #endif time_t _last_write_time = 0; }; -// 30.10.13 Class directory_iterator +// [fs.class.directory.iterator] Class directory_iterator class GHC_FS_API_CLASS directory_iterator { public: @@ -792,7 +934,7 @@ public: using pointer = const directory_entry*; using reference = const directory_entry&; - // 30.10.13.1 member functions + // [fs.dir.itr.members] member functions directory_iterator() noexcept; #ifdef GHC_WITH_EXCEPTIONS explicit directory_iterator(const path& p); @@ -812,7 +954,7 @@ public: #endif directory_iterator& increment(std::error_code& ec) noexcept; - // other members as required by 27.2.3, input iterators + // other members as required by [input.iterators] #ifdef GHC_WITH_EXCEPTIONS proxy operator++(int) { @@ -830,11 +972,11 @@ private: std::shared_ptr _impl; }; -// 30.10.13.2 directory_iterator non-member functions +// [fs.dir.itr.nonmembers] directory_iterator non-member functions GHC_FS_API directory_iterator begin(directory_iterator iter) noexcept; GHC_FS_API directory_iterator end(const directory_iterator&) noexcept; -// 30.10.14 class recursive_directory_iterator +// [fs.class.re.dir.itr] class recursive_directory_iterator class GHC_FS_API_CLASS recursive_directory_iterator { public: @@ -844,7 +986,7 @@ public: using pointer = const directory_entry*; using reference = const directory_entry&; - // 30.10.14.1 constructors and destructor + // [fs.rec.dir.itr.members] constructors and destructor recursive_directory_iterator() noexcept; #ifdef GHC_WITH_EXCEPTIONS explicit recursive_directory_iterator(const path& p); @@ -856,7 +998,7 @@ public: recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept; ~recursive_directory_iterator(); - // 30.10.14.1 observers + // [fs.rec.dir.itr.members] observers directory_options options() const; int depth() const; bool recursion_pending() const; @@ -864,7 +1006,7 @@ public: const directory_entry& operator*() const; const directory_entry* operator->() const; - // 30.10.14.1 modifiers recursive_directory_iterator& + // [fs.rec.dir.itr.members] modifiers recursive_directory_iterator& recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs); recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept; #ifdef GHC_WITH_EXCEPTIONS @@ -878,7 +1020,7 @@ public: void pop(std::error_code& ec); void disable_recursion_pending(); - // other members as required by 27.2.3, input iterators + // other members as required by [input.iterators] #ifdef GHC_WITH_EXCEPTIONS directory_iterator::proxy operator++(int) { @@ -905,232 +1047,118 @@ private: std::shared_ptr _impl; }; -// 30.10.14.2 directory_iterator non-member functions +// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions GHC_FS_API recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept; GHC_FS_API recursive_directory_iterator end(const recursive_directory_iterator&) noexcept; -// 30.10.15 filesystem operations +// [fs.op.funcs] filesystem operations #ifdef GHC_WITH_EXCEPTIONS GHC_FS_API path absolute(const path& p); +GHC_FS_API path canonical(const path& p); +GHC_FS_API void copy(const path& from, const path& to); +GHC_FS_API void copy(const path& from, const path& to, copy_options options); +GHC_FS_API bool copy_file(const path& from, const path& to); +GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option); +GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink); +GHC_FS_API bool create_directories(const path& p); +GHC_FS_API bool create_directory(const path& p); +GHC_FS_API bool create_directory(const path& p, const path& attributes); +GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink); +GHC_FS_API void create_symlink(const path& to, const path& new_symlink); +GHC_FS_API path current_path(); +GHC_FS_API void current_path(const path& p); +GHC_FS_API bool exists(const path& p); +GHC_FS_API bool equivalent(const path& p1, const path& p2); +GHC_FS_API uintmax_t file_size(const path& p); +GHC_FS_API bool is_block_file(const path& p); +GHC_FS_API bool is_character_file(const path& p); +GHC_FS_API bool is_directory(const path& p); +GHC_FS_API bool is_empty(const path& p); +GHC_FS_API bool is_fifo(const path& p); +GHC_FS_API bool is_other(const path& p); +GHC_FS_API bool is_regular_file(const path& p); +GHC_FS_API bool is_socket(const path& p); +GHC_FS_API bool is_symlink(const path& p); +GHC_FS_API file_time_type last_write_time(const path& p); +GHC_FS_API void last_write_time(const path& p, file_time_type new_time); +GHC_FS_API void permissions(const path& p, perms prms, perm_options opts = perm_options::replace); +GHC_FS_API path proximate(const path& p, const path& base = current_path()); +GHC_FS_API path read_symlink(const path& p); +GHC_FS_API path relative(const path& p, const path& base = current_path()); +GHC_FS_API bool remove(const path& p); +GHC_FS_API uintmax_t remove_all(const path& p); +GHC_FS_API void rename(const path& from, const path& to); +GHC_FS_API void resize_file(const path& p, uintmax_t size); +GHC_FS_API space_info space(const path& p); +GHC_FS_API file_status status(const path& p); +GHC_FS_API file_status symlink_status(const path& p); +GHC_FS_API path temp_directory_path(); +GHC_FS_API path weakly_canonical(const path& p); #endif GHC_FS_API path absolute(const path& p, std::error_code& ec); - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API path canonical(const path& p); -#endif GHC_FS_API path canonical(const path& p, std::error_code& ec); - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API void copy(const path& from, const path& to); -#endif GHC_FS_API void copy(const path& from, const path& to, std::error_code& ec) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API void copy(const path& from, const path& to, copy_options options); -#endif GHC_FS_API void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool copy_file(const path& from, const path& to); -#endif GHC_FS_API bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option); -#endif GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink); -#endif GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool create_directories(const path& p); -#endif GHC_FS_API bool create_directories(const path& p, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool create_directory(const path& p); -#endif GHC_FS_API bool create_directory(const path& p, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool create_directory(const path& p, const path& attributes); -#endif GHC_FS_API bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink); -#endif GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; +GHC_FS_API void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; +GHC_FS_API path current_path(std::error_code& ec); +GHC_FS_API void current_path(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool exists(file_status s) noexcept; +GHC_FS_API bool exists(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept; +GHC_FS_API uintmax_t file_size(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_block_file(file_status s) noexcept; +GHC_FS_API bool is_block_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_character_file(file_status s) noexcept; +GHC_FS_API bool is_character_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_directory(file_status s) noexcept; +GHC_FS_API bool is_directory(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_empty(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_fifo(file_status s) noexcept; +GHC_FS_API bool is_fifo(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_other(file_status s) noexcept; +GHC_FS_API bool is_other(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_regular_file(file_status s) noexcept; +GHC_FS_API bool is_regular_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_socket(file_status s) noexcept; +GHC_FS_API bool is_socket(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_symlink(file_status s) noexcept; +GHC_FS_API bool is_symlink(const path& p, std::error_code& ec) noexcept; +GHC_FS_API file_time_type last_write_time(const path& p, std::error_code& ec) noexcept; +GHC_FS_API void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept; +GHC_FS_API void permissions(const path& p, perms prms, std::error_code& ec) noexcept; +GHC_FS_API void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept; +GHC_FS_API path proximate(const path& p, std::error_code& ec); +GHC_FS_API path proximate(const path& p, const path& base, std::error_code& ec); +GHC_FS_API path read_symlink(const path& p, std::error_code& ec); +GHC_FS_API path relative(const path& p, std::error_code& ec); +GHC_FS_API path relative(const path& p, const path& base, std::error_code& ec); +GHC_FS_API bool remove(const path& p, std::error_code& ec) noexcept; +GHC_FS_API uintmax_t remove_all(const path& p, std::error_code& ec) noexcept; +GHC_FS_API void rename(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept; +GHC_FS_API space_info space(const path& p, std::error_code& ec) noexcept; +GHC_FS_API file_status status(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool status_known(file_status s) noexcept; +GHC_FS_API file_status symlink_status(const path& p, std::error_code& ec) noexcept; +GHC_FS_API path temp_directory_path(std::error_code& ec) noexcept; +GHC_FS_API path weakly_canonical(const path& p, std::error_code& ec) noexcept; #ifndef GHC_OS_WEB #ifdef GHC_WITH_EXCEPTIONS GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link); -#endif -GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept; -#endif - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API void create_symlink(const path& to, const path& new_symlink); -#endif -GHC_FS_API void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API path current_path(); -#endif -GHC_FS_API path current_path(std::error_code& ec); -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API void current_path(const path& p); -#endif -GHC_FS_API void current_path(const path& p, std::error_code& ec) noexcept; - -GHC_FS_API bool exists(file_status s) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool exists(const path& p); -#endif -GHC_FS_API bool exists(const path& p, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool equivalent(const path& p1, const path& p2); -#endif -GHC_FS_API bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API uintmax_t file_size(const path& p); -#endif -GHC_FS_API uintmax_t file_size(const path& p, std::error_code& ec) noexcept; - -#ifndef GHC_OS_WEB -#ifdef GHC_WITH_EXCEPTIONS GHC_FS_API uintmax_t hard_link_count(const path& p); #endif +GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept; GHC_FS_API uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept; #endif -GHC_FS_API bool is_block_file(file_status s) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool is_block_file(const path& p); -#endif -GHC_FS_API bool is_block_file(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_character_file(file_status s) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool is_character_file(const path& p); -#endif -GHC_FS_API bool is_character_file(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_directory(file_status s) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool is_directory(const path& p); -#endif -GHC_FS_API bool is_directory(const path& p, std::error_code& ec) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool is_empty(const path& p); -#endif -GHC_FS_API bool is_empty(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_fifo(file_status s) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool is_fifo(const path& p); -#endif -GHC_FS_API bool is_fifo(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_other(file_status s) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool is_other(const path& p); -#endif -GHC_FS_API bool is_other(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_regular_file(file_status s) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool is_regular_file(const path& p); -#endif -GHC_FS_API bool is_regular_file(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_socket(file_status s) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool is_socket(const path& p); -#endif -GHC_FS_API bool is_socket(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_symlink(file_status s) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool is_symlink(const path& p); -#endif -GHC_FS_API bool is_symlink(const path& p, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API file_time_type last_write_time(const path& p); -#endif -GHC_FS_API file_time_type last_write_time(const path& p, std::error_code& ec) noexcept; -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API void last_write_time(const path& p, file_time_type new_time); -#endif -GHC_FS_API void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API void permissions(const path& p, perms prms, perm_options opts = perm_options::replace); -#endif -GHC_FS_API void permissions(const path& p, perms prms, std::error_code& ec) noexcept; -GHC_FS_API void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec); - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API path proximate(const path& p, std::error_code& ec); -GHC_FS_API path proximate(const path& p, const path& base = current_path()); -#endif -GHC_FS_API path proximate(const path& p, const path& base, std::error_code& ec); - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API path read_symlink(const path& p); -#endif -GHC_FS_API path read_symlink(const path& p, std::error_code& ec); - -GHC_FS_API path relative(const path& p, std::error_code& ec); -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API path relative(const path& p, const path& base = current_path()); -#endif -GHC_FS_API path relative(const path& p, const path& base, std::error_code& ec); - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API bool remove(const path& p); -#endif -GHC_FS_API bool remove(const path& p, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API uintmax_t remove_all(const path& p); -#endif -GHC_FS_API uintmax_t remove_all(const path& p, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API void rename(const path& from, const path& to); -#endif -GHC_FS_API void rename(const path& from, const path& to, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API void resize_file(const path& p, uintmax_t size); -#endif -GHC_FS_API void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API space_info space(const path& p); -#endif -GHC_FS_API space_info space(const path& p, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API file_status status(const path& p); -#endif -GHC_FS_API file_status status(const path& p, std::error_code& ec) noexcept; - -GHC_FS_API bool status_known(file_status s) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API file_status symlink_status(const path& p); -#endif -GHC_FS_API file_status symlink_status(const path& p, std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API path temp_directory_path(); -#endif -GHC_FS_API path temp_directory_path(std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API path weakly_canonical(const path& p); -#endif -GHC_FS_API path weakly_canonical(const path& p, std::error_code& ec) noexcept; - // Non-C++17 add-on std::fstream wrappers with path template > class basic_filebuf : public std::basic_filebuf @@ -1257,7 +1285,6 @@ private: //------------------------------------------------------------------------------------------------- namespace detail { -// GHC_FS_API void postprocess_path_with_format(path::impl_string_type& p, path::format fmt); enum utf8_states_t { S_STRT = 0, S_RJCT = 8 }; GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode); GHC_FS_API bool is_surrogate(uint32_t c); @@ -1278,6 +1305,65 @@ GHC_FS_API std::error_code make_error_code(portable_error err); GHC_FS_API std::error_code make_system_error(uint32_t err = 0); #else GHC_FS_API std::error_code make_system_error(int err = 0); + +template +struct has_d_type : std::false_type{}; + +template +struct has_d_type : std::true_type {}; + +template +GHC_INLINE file_type file_type_from_dirent_impl(const T&, std::false_type) +{ + return file_type::none; +} + +template +GHC_INLINE file_type file_type_from_dirent_impl(const T& t, std::true_type) +{ + switch (t.d_type) { +#ifdef DT_BLK + case DT_BLK: + return file_type::block; +#endif +#ifdef DT_CHR + case DT_CHR: + return file_type::character; +#endif +#ifdef DT_DIR + case DT_DIR: + return file_type::directory; +#endif +#ifdef DT_FIFO + case DT_FIFO: + return file_type::fifo; +#endif +#ifdef DT_LNK + case DT_LNK: + return file_type::symlink; +#endif +#ifdef DT_REG + case DT_REG: + return file_type::regular; +#endif +#ifdef DT_SOCK + case DT_SOCK: + return file_type::socket; +#endif +#ifdef DT_UNKNOWN + case DT_UNKNOWN: + return file_type::none; +#endif + default: + return file_type::unknown; + } +} + +template +GHC_INLINE file_type file_type_from_dirent(const T& t) +{ + return file_type_from_dirent_impl(t, has_d_type{}); +} #endif } // namespace detail @@ -1340,7 +1426,7 @@ GHC_INLINE std::error_code make_system_error(int err) return std::error_code(err ? err : errno, std::system_category()); } #endif - + #endif // GHC_EXPAND_IMPL template @@ -1348,28 +1434,28 @@ using EnableBitmask = typename std::enable_if::value | } // namespace detail template -detail::EnableBitmask operator&(Enum X, Enum Y) +constexpr detail::EnableBitmask operator&(Enum X, Enum Y) { using underlying = typename std::underlying_type::type; return static_cast(static_cast(X) & static_cast(Y)); } template -detail::EnableBitmask operator|(Enum X, Enum Y) +constexpr detail::EnableBitmask operator|(Enum X, Enum Y) { using underlying = typename std::underlying_type::type; return static_cast(static_cast(X) | static_cast(Y)); } template -detail::EnableBitmask operator^(Enum X, Enum Y) +constexpr detail::EnableBitmask operator^(Enum X, Enum Y) { using underlying = typename std::underlying_type::type; return static_cast(static_cast(X) ^ static_cast(Y)); } template -detail::EnableBitmask operator~(Enum X) +constexpr detail::EnableBitmask operator~(Enum X) { using underlying = typename std::underlying_type::type; return static_cast(~static_cast(X)); @@ -1463,7 +1549,7 @@ GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t frag codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) : (0xffu >> category) & fragment); return state == S_RJCT ? static_cast(S_RJCT) : static_cast((utf8_state_info[category + 16] >> (state << 2)) & 0xf); } - + GHC_INLINE bool validUtf8(const std::string& utf8String) { std::string::const_iterator iter = utf8String.begin(); @@ -1481,26 +1567,26 @@ GHC_INLINE bool validUtf8(const std::string& utf8String) } } // namespace detail - + #endif - + namespace detail { -template ::type* = nullptr> -inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 1)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) { return StringType(utf8String.begin(), utf8String.end(), alloc); } -template ::type* = nullptr> -inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 2)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) { StringType result(alloc); result.reserve(utf8String.length()); - std::string::const_iterator iter = utf8String.begin(); + auto iter = utf8String.cbegin(); unsigned utf8_state = S_STRT; std::uint32_t codepoint = 0; - while (iter < utf8String.end()) { + while (iter < utf8String.cend()) { if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_STRT) { if (codepoint <= 0xffff) { result += static_cast(codepoint); @@ -1532,15 +1618,15 @@ inline StringType fromUtf8(const std::string& utf8String, const typename StringT return result; } -template ::type* = nullptr> -inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 4)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) { StringType result(alloc); result.reserve(utf8String.length()); - std::string::const_iterator iter = utf8String.begin(); + auto iter = utf8String.cbegin(); unsigned utf8_state = S_STRT; std::uint32_t codepoint = 0; - while (iter < utf8String.end()) { + while (iter < utf8String.cend()) { if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_STRT) { result += static_cast(codepoint); codepoint = 0; @@ -1565,14 +1651,24 @@ inline StringType fromUtf8(const std::string& utf8String, const typename StringT return result; } -template ::type size = 1> -inline std::string toUtf8(const std::basic_string& unicodeString) +template +inline StringType fromUtf8(const charT (&utf8String)[N]) +{ +#ifdef GHC_WITH_STRING_VIEW + return fromUtf8(basic_string_view(utf8String, N - 1)); +#else + return fromUtf8(std::basic_string(utf8String, N - 1)); +#endif +} + +template ::value && (sizeof(typename strT::value_type) == 1), int>::type size = 1> +inline std::string toUtf8(const strT& unicodeString) { return std::string(unicodeString.begin(), unicodeString.end()); } -template ::type size = 2> -inline std::string toUtf8(const std::basic_string& unicodeString) +template ::value && (sizeof(typename strT::value_type) == 2), int>::type size = 2> +inline std::string toUtf8(const strT& unicodeString) { std::string result; for (auto iter = unicodeString.begin(); iter != unicodeString.end(); ++iter) { @@ -1587,7 +1683,7 @@ inline std::string toUtf8(const std::basic_string& unicode throw filesystem_error("Illegal code point for unicode character.", result, std::make_error_code(std::errc::illegal_byte_sequence)); #else appendUTF8(result, 0xfffd); - if(iter == unicodeString.end()) { + if (iter == unicodeString.end()) { break; } #endif @@ -1600,8 +1696,8 @@ inline std::string toUtf8(const std::basic_string& unicode return result; } -template ::type size = 4> -inline std::string toUtf8(const std::basic_string& unicodeString) +template ::value && (sizeof(typename strT::value_type) == 4), int>::type size = 4> +inline std::string toUtf8(const strT& unicodeString) { std::string result; for (auto c : unicodeString) { @@ -1613,75 +1709,134 @@ inline std::string toUtf8(const std::basic_string& unicode template inline std::string toUtf8(const charT* unicodeString) { +#ifdef GHC_WITH_STRING_VIEW + return toUtf8(basic_string_view>(unicodeString)); +#else return toUtf8(std::basic_string>(unicodeString)); +#endif } +#ifdef GHC_USE_WCHAR_T +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 1), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + auto temp = toUtf8(wString); + return StringType(temp.begin(), temp.end(), alloc); +} + +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 2), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + return StringType(wString.begin(), wString.end(), alloc); +} + +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 4), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + auto temp = toUtf8(wString); + return fromUtf8(temp, alloc); +} + +template ::value && (sizeof(typename strT::value_type) == 1), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + return fromUtf8(unicodeString); +} + +template ::value && (sizeof(typename strT::value_type) == 2), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + return std::wstring(unicodeString.begin(), unicodeString.end()); +} + +template ::value && (sizeof(typename strT::value_type) == 4), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + auto temp = toUtf8(unicodeString); + return fromUtf8(temp); +} + +template +inline std::wstring toWChar(const charT* unicodeString) +{ +#ifdef GHC_WITH_STRING_VIEW + return toWChar(basic_string_view>(unicodeString)); +#else + return toWChar(std::basic_string>(unicodeString)); +#endif +} +#endif // GHC_USE_WCHAR_T + } // namespace detail #ifdef GHC_EXPAND_IMPL namespace detail { -GHC_INLINE bool startsWith(const std::string& what, const std::string& with) +template ::value, bool>::type = true> +GHC_INLINE bool startsWith(const strT& what, const strT& with) { return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin()); } -GHC_INLINE bool endsWith(const std::string& what, const std::string& with) +template ::value, bool>::type = true> +GHC_INLINE bool endsWith(const strT& what, const strT& with) { - return with.length() <= what.length() && what.compare(what.length() - with.length(), with.size(), with); + return with.length() <= what.length() && what.compare(what.length() - with.length(), with.size(), with) == 0; } } // namespace detail -GHC_INLINE void path::postprocess_path_with_format(path::impl_string_type& p, path::format fmt) +GHC_INLINE void path::check_long_path() +{ +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) { + postprocess_path_with_format(native_format); + } +#endif +} + +GHC_INLINE void path::postprocess_path_with_format(path::format fmt) { #ifdef GHC_RAISE_UNICODE_ERRORS - if(!detail::validUtf8(p)) { + if (!detail::validUtf8(_path)) { path t; - t._path = p; + t._path = _path; throw filesystem_error("Illegal byte sequence for unicode character.", t, std::make_error_code(std::errc::illegal_byte_sequence)); } #endif switch (fmt) { -#ifndef GHC_OS_WINDOWS +#ifdef GHC_OS_WINDOWS + case path::native_format: + case path::auto_format: + case path::generic_format: + for (auto& c : _path) { + if (c == generic_separator) { + c = preferred_separator; + } + } +#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH + if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) { + _path = GHC_PLATFORM_LITERAL("\\\\?\\") + _path; + } +#endif + handle_prefixes(); + break; +#else case path::auto_format: case path::native_format: -#endif case path::generic_format: // nothing to do break; -#ifdef GHC_OS_WINDOWS - case path::auto_format: - case path::native_format: - if (p.length() > 4 && p[2] == '?') { - if (detail::startsWith(p, std::string("\\\\?\\"))) { - // remove Windows long filename marker - p.erase(0, 4); - if (detail::startsWith(p, std::string("UNC\\"))) { - p.erase(0, 2); - p[0] = '\\'; - } - } - else if (detail::startsWith(p, std::string("\\??\\"))) { - p.erase(0, 4); - } - } - for (auto& c : p) { - if (c == '\\') { - c = '/'; - } - } - break; #endif } - if (p.length() > 2 && p[0] == '/' && p[1] == '/' && p[2] != '/') { - std::string::iterator new_end = std::unique(p.begin() + 2, p.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == '/'; }); - p.erase(new_end, p.end()); + if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator) { + impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast(_prefixLength) + 2, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); + _path.erase(new_end, _path.end()); } else { - std::string::iterator new_end = std::unique(p.begin(), p.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == '/'; }); - p.erase(new_end, p.end()); + impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast(_prefixLength), _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); + _path.erase(new_end, _path.end()); } } @@ -1689,45 +1844,14 @@ GHC_INLINE void path::postprocess_path_with_format(path::impl_string_type& p, pa template inline path::path(const Source& source, format fmt) - : _path(detail::toUtf8(source)) -{ - postprocess_path_with_format(_path, fmt); -} -template <> -inline path::path(const std::wstring& source, format fmt) -{ - _path = detail::toUtf8(source); - postprocess_path_with_format(_path, fmt); -} -template <> -inline path::path(const std::u16string& source, format fmt) -{ - _path = detail::toUtf8(source); - postprocess_path_with_format(_path, fmt); -} -template <> -inline path::path(const std::u32string& source, format fmt) -{ - _path = detail::toUtf8(source); - postprocess_path_with_format(_path, fmt); -} - -#ifdef __cpp_lib_string_view -template <> -inline path::path(const std::string_view& source, format fmt) -{ - _path = detail::toUtf8(std::string(source)); - postprocess_path_with_format(_path, fmt); -} #ifdef GHC_USE_WCHAR_T -template <> -inline path::path(const std::wstring_view& source, format fmt) + : _path(detail::toWChar(source)) +#else + : _path(detail::toUtf8(source)) +#endif { - _path = detail::toUtf8(std::wstring(source).c_str()); - postprocess_path_with_format(_path, fmt); + postprocess_path_with_format(fmt); } -#endif -#endif template inline path u8path(const Source& source) @@ -1751,7 +1875,7 @@ inline path::path(InputIterator first, InputIterator last, format fmt) namespace detail { -GHC_INLINE bool equals_simple_insensitive(const char* str1, const char* str2) +GHC_INLINE bool equals_simple_insensitive(const path::value_type* str1, const path::value_type* str2) { #ifdef GHC_OS_WINDOWS #ifdef __GNUC__ @@ -1760,24 +1884,30 @@ GHC_INLINE bool equals_simple_insensitive(const char* str1, const char* str2) return true; } return false; -#else +#else // __GNUC__ +#ifdef GHC_USE_WCHAR_T + return 0 == ::_wcsicmp(str1, str2); +#else // GHC_USE_WCHAR_T return 0 == ::_stricmp(str1, str2); -#endif -#else +#endif // GHC_USE_WCHAR_T +#endif // __GNUC__ +#else // GHC_OS_WINDOWS return 0 == ::strcasecmp(str1, str2); -#endif +#endif // GHC_OS_WINDOWS } -GHC_INLINE int compare_simple_insensitive(const char* str1, size_t len1, const char* str2, size_t len2) +GHC_INLINE int compare_simple_insensitive(const path::value_type* str1, size_t len1, const path::value_type* str2, size_t len2) { - while(len1 > 0 && len2 > 0 && ::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2)) { - --len1; --len2; - ++str1; ++str2; + while (len1 > 0 && len2 > 0 && ::tolower(static_cast(*str1)) == ::tolower(static_cast(*str2))) { + --len1; + --len2; + ++str1; + ++str2; } if (len1 && len2) { return *str1 < *str2 ? -1 : 1; } - if(len1 == 0 && len2 == 0) { + if (len1 == 0 && len2 == 0) { return 0; } return len1 == 0 ? -1 : 1; @@ -1790,7 +1920,7 @@ GHC_INLINE const char* strerror_adapter(char* gnu, char*) GHC_INLINE const char* strerror_adapter(int posix, char* buffer) { - if(posix) { + if (posix) { return "Error in strerror_r!"; } return buffer; @@ -1827,15 +1957,20 @@ GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, #if defined(__GNUC__) && __GNUC__ >= 8 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" +#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 4191) #endif static CreateSymbolicLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW")); #if defined(__GNUC__) && __GNUC__ >= 8 #pragma GCC diagnostic pop +#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) +#pragma warning(pop) #endif if (api_call) { - if (api_call(detail::fromUtf8(new_symlink.u8string()).c_str(), detail::fromUtf8(target_name.u8string()).c_str(), to_directory ? 1 : 0) == 0) { + if (api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 1 : 0) == 0) { auto result = ::GetLastError(); - if (result == ERROR_PRIVILEGE_NOT_HELD && api_call(detail::fromUtf8(new_symlink.u8string()).c_str(), detail::fromUtf8(target_name.u8string()).c_str(), to_directory ? 3 : 2) != 0) { + if (result == ERROR_PRIVILEGE_NOT_HELD && api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 3 : 2) != 0) { return; } ec = detail::make_system_error(result); @@ -1851,13 +1986,18 @@ GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlin #if defined(__GNUC__) && __GNUC__ >= 8 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" +#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 4191) #endif static CreateHardLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW")); #if defined(__GNUC__) && __GNUC__ >= 8 #pragma GCC diagnostic pop +#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) +#pragma warning(pop) #endif if (api_call) { - if (api_call(detail::fromUtf8(new_hardlink.u8string()).c_str(), detail::fromUtf8(target_name.u8string()).c_str(), NULL) == 0) { + if (api_call(GHC_NATIVEWP(new_hardlink), GHC_NATIVEWP(target_name), NULL) == 0) { ec = detail::make_system_error(); } } @@ -1865,6 +2005,21 @@ GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlin ec = detail::make_system_error(ERROR_NOT_SUPPORTED); } } + +GHC_INLINE path getFullPathName(const wchar_t* p, std::error_code& ec) +{ + ULONG size = ::GetFullPathNameW(p, 0, 0, 0); + if (size) { + std::vector buf(size, 0); + ULONG s2 = GetFullPathNameW(p, size, buf.data(), nullptr); + if (s2 && s2 < size) { + return path(std::wstring(buf.data(), s2)); + } + } + ec = detail::make_system_error(); + return path(); +} + #else GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool, std::error_code& ec) { @@ -1927,74 +2082,147 @@ GHC_INLINE file_status file_status_from_st_mode(T mode) #endif } -GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) -{ #ifdef GHC_OS_WINDOWS -#ifndef REPARSE_DATA_BUFFER_HEADER_SIZE - typedef struct _REPARSE_DATA_BUFFER + +class unique_handle +{ +public: + typedef HANDLE element_type; + + unique_handle() noexcept + : _handle(INVALID_HANDLE_VALUE) { - ULONG ReparseTag; - USHORT ReparseDataLength; - USHORT Reserved; - union + } + explicit unique_handle(element_type h) noexcept + : _handle(h) + { + } + unique_handle(unique_handle&& u) noexcept + : _handle(u.release()) + { + } + ~unique_handle() { reset(); } + unique_handle& operator=(unique_handle&& u) noexcept + { + reset(u.release()); + return *this; + } + element_type get() const noexcept { return _handle; } + explicit operator bool() const noexcept { return _handle != INVALID_HANDLE_VALUE; } + element_type release() noexcept + { + element_type tmp = _handle; + _handle = INVALID_HANDLE_VALUE; + return tmp; + } + void reset(element_type h = INVALID_HANDLE_VALUE) noexcept + { + element_type tmp = _handle; + _handle = h; + if (tmp != INVALID_HANDLE_VALUE) { + CloseHandle(tmp); + } + } + void swap(unique_handle& u) noexcept { std::swap(_handle, u._handle); } + +private: + element_type _handle; +}; + +#ifndef REPARSE_DATA_BUFFER_HEADER_SIZE +typedef struct _REPARSE_DATA_BUFFER +{ + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union + { + struct { - struct - { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - ULONG Flags; - WCHAR PathBuffer[1]; - } SymbolicLinkReparseBuffer; - struct - { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - WCHAR PathBuffer[1]; - } MountPointReparseBuffer; - struct - { - UCHAR DataBuffer[1]; - } GenericReparseBuffer; - } DUMMYUNIONNAME; - } REPARSE_DATA_BUFFER; + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct + { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; +} REPARSE_DATA_BUFFER; #ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024) #endif #endif - std::shared_ptr 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); - if (file.get() == INVALID_HANDLE_VALUE) { +template +struct free_deleter +{ + void operator()(T* p) const { std::free(p); } +}; + +GHC_INLINE std::unique_ptr> getReparseData(const path& p, std::error_code& ec) +{ + unique_handle file(CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0)); + if (!file) { ec = detail::make_system_error(); - return path(); + return nullptr; } - std::shared_ptr reparseData((REPARSE_DATA_BUFFER*)std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE), std::free); + std::unique_ptr> reparseData(reinterpret_cast(std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE))); ULONG bufferUsed; - path result; if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) { - if (IsReparseTagMicrosoft(reparseData->ReparseTag)) { + return reparseData; + } + else { + ec = detail::make_system_error(); + } + return nullptr; +} +#endif + +GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) +{ +#ifdef GHC_OS_WINDOWS + path result; + auto reparseData = detail::getReparseData(p, ec); + if (!ec) { + if (reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag)) { switch (reparseData->ReparseTag) { - case IO_REPARSE_TAG_SYMLINK: - result = std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); + case IO_REPARSE_TAG_SYMLINK: { + auto printName = std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR)); + auto substituteName = + std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); + if (detail::endsWith(substituteName, printName) && detail::startsWith(substituteName, std::wstring(L"\\??\\"))) { + result = printName; + } + else { + result = substituteName; + } if (reparseData->SymbolicLinkReparseBuffer.Flags & 0x1 /*SYMLINK_FLAG_RELATIVE*/) { result = p.parent_path() / result; } break; + } case IO_REPARSE_TAG_MOUNT_POINT: - result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); + result = detail::getFullPathName(GHC_NATIVEWP(p), ec); + // result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); break; default: break; } } } - else { - ec = detail::make_system_error(); - } return result; #else size_t bufferSize = 256; @@ -2025,10 +2253,10 @@ GHC_INLINE time_t timeFromFILETIME(const FILETIME& ft) GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft) { - LONGLONG ll; - ll = Int32x32To64(t, 10000000) + 116444736000000000; - ft.dwLowDateTime = static_cast(ll); - ft.dwHighDateTime = static_cast(ll >> 32); + ULARGE_INTEGER ull; + ull.QuadPart = static_cast((t * 10000000LL) + 116444736000000000LL); + ft.dwLowDateTime = ull.LowPart; + ft.dwHighDateTime = ull.HighPart; } template @@ -2044,19 +2272,43 @@ GHC_INLINE uintmax_t hard_links_from_INFO(const BY_H } template -GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code&, uintmax_t* sz = nullptr, time_t* lwt = nullptr) +GHC_INLINE bool is_symlink_from_INFO(const path &p, const INFO* info, std::error_code& ec) +{ + if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + auto reparseData = detail::getReparseData(p, ec); + if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + return true; + } + } + return false; +} + +template <> +GHC_INLINE bool is_symlink_from_INFO(const path &, const WIN32_FIND_DATAW* info, std::error_code&) +{ + // dwReserved0 is undefined unless dwFileAttributes includes the + // FILE_ATTRIBUTE_REPARSE_POINT attribute according to microsoft + // documentation. In practice, dwReserved0 is not reset which + // causes it to report the incorrect symlink status. + // Note that microsoft documentation does not say whether there is + // a null value for dwReserved0, so we test for symlink directly + // instead of returning the tag which requires returning a null + // value for non-reparse-point files. + return (info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && info->dwReserved0 == IO_REPARSE_TAG_SYMLINK; +} + +template +GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code& ec, uintmax_t* sz = nullptr, time_t* lwt = nullptr) { file_type ft = file_type::unknown; - if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + if (is_symlink_from_INFO(p, info, ec)) { ft = file_type::symlink; } + else if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + ft = file_type::directory; + } else { - if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { - ft = file_type::directory; - } - else { - ft = file_type::regular; - } + ft = file_type::regular; } perms prms = perms::owner_read | perms::group_read | perms::others_read; if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { @@ -2090,7 +2342,7 @@ GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uin #ifdef GHC_OS_WINDOWS file_status fs; WIN32_FILE_ATTRIBUTE_DATA attr; - if (!GetFileAttributesExW(detail::fromUtf8(p.u8string()).c_str(), GetFileExInfoStandard, &attr)) { + if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { ec = detail::make_system_error(); } else { @@ -2099,9 +2351,6 @@ GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uin if (nhl) { *nhl = 0; } - if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - fs.type(file_type::symlink); - } } if (detail::is_not_found_error(ec)) { return file_status(file_type::not_found); @@ -2135,19 +2384,22 @@ GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status return file_status(file_type::unknown); } WIN32_FILE_ATTRIBUTE_DATA attr; - if (!::GetFileAttributesExW(p.wstring().c_str(), GetFileExInfoStandard, &attr)) { + if (!::GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { ec = detail::make_system_error(); } else if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - path target = resolveSymlink(p, ec); - file_status result; - if (!ec && !target.empty()) { - if (sls) { - *sls = status_from_INFO(p, &attr, ec); + auto reparseData = detail::getReparseData(p, ec); + if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + path target = resolveSymlink(p, ec); + file_status result; + if (!ec && !target.empty()) { + if (sls) { + *sls = status_from_INFO(p, &attr, ec); + } + return detail::status_ex(target, ec, nullptr, sz, nhl, lwt, recurse_count + 1); } - return detail::status_ex(target, ec, nullptr, sz, nhl, lwt, recurse_count + 1); + return file_status(file_type::unknown); } - return file_status(file_type::unknown); } if (ec) { if (detail::is_not_found_error(ec)) { @@ -2166,14 +2418,21 @@ GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status if (result == 0) { ec.clear(); file_status fs = detail::file_status_from_st_mode(st.st_mode); + if (sls) { + *sls = fs; + } if (fs.type() == file_type::symlink) { result = ::stat(p.c_str(), &st); if (result == 0) { - if (sls) { - *sls = fs; - } fs = detail::file_status_from_st_mode(st.st_mode); } + else { + ec = detail::make_system_error(); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); + } } if (sz) { *sz = static_cast(st.st_size); @@ -2230,28 +2489,30 @@ GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv) } //----------------------------------------------------------------------------- -// 30.10.8.4.1 constructors and destructor +// [fs.path.construct] constructors and destructor GHC_INLINE path::path() noexcept {} GHC_INLINE path::path(const path& p) : _path(p._path) +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + , _prefixLength(p._prefixLength) +#endif { } GHC_INLINE path::path(path&& p) noexcept : _path(std::move(p._path)) +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + , _prefixLength(p._prefixLength) +#endif { } GHC_INLINE path::path(string_type&& source, format fmt) -#ifdef GHC_USE_WCHAR_T - : _path(detail::toUtf8(source)) -#else : _path(std::move(source)) -#endif { - postprocess_path_with_format(_path, fmt); + postprocess_path_with_format(fmt); } #endif // GHC_EXPAND_IMPL @@ -2283,17 +2544,23 @@ inline path::path(InputIterator first, InputIterator last, const std::locale& lo GHC_INLINE path::~path() {} //----------------------------------------------------------------------------- -// 30.10.8.4.2 assignments +// [fs.path.assign] assignments GHC_INLINE path& path::operator=(const path& p) { _path = p._path; +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = p._prefixLength; +#endif return *this; } GHC_INLINE path& path::operator=(path&& p) noexcept { _path = std::move(p._path); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = p._prefixLength; +#endif return *this; } @@ -2304,12 +2571,8 @@ GHC_INLINE path& path::operator=(path::string_type&& source) GHC_INLINE path& path::assign(path::string_type&& source) { -#ifdef GHC_USE_WCHAR_T - _path = detail::toUtf8(source); -#else _path = std::move(source); -#endif - postprocess_path_with_format(_path, native_format); + postprocess_path_with_format(native_format); return *this; } @@ -2324,8 +2587,12 @@ inline path& path::operator=(const Source& source) template inline path& path::assign(const Source& source) { +#ifdef GHC_USE_WCHAR_T + _path.assign(detail::toWChar(source)); +#else _path.assign(detail::toUtf8(source)); - postprocess_path_with_format(_path, native_format); +#endif + postprocess_path_with_format(native_format); return *this; } @@ -2333,6 +2600,9 @@ template <> inline path& path::assign(const path& source) { _path = source._path; +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = source._prefixLength; +#endif return *this; } @@ -2340,25 +2610,25 @@ template inline path& path::assign(InputIterator first, InputIterator last) { _path.assign(first, last); - postprocess_path_with_format(_path, native_format); + postprocess_path_with_format(native_format); return *this; } #ifdef GHC_EXPAND_IMPL //----------------------------------------------------------------------------- -// 30.10.8.4.3 appends +// [fs.path.append] appends GHC_INLINE path& path::operator/=(const path& p) { if (p.empty()) { // was: if ((!has_root_directory() && is_absolute()) || has_filename()) - if (!_path.empty() && _path[_path.length() - 1] != '/' && _path[_path.length() - 1] != ':') { - _path += '/'; + if (!_path.empty() && _path[_path.length() - 1] != preferred_separator && _path[_path.length() - 1] != ':') { + _path += preferred_separator; } return *this; } - if ((p.is_absolute() && (_path != root_name() || p._path != "/")) || (p.has_root_name() && p.root_name() != root_name())) { + if ((p.is_absolute() && (_path != root_name()._path || p._path != "/")) || (p.has_root_name() && p.root_name() != root_name())) { assign(p); return *this; } @@ -2366,7 +2636,7 @@ GHC_INLINE path& path::operator/=(const path& p) assign(root_name()); } else if ((!has_root_directory() && is_absolute()) || has_filename()) { - _path += '/'; + _path += preferred_separator; } auto iter = p.begin(); bool first = true; @@ -2374,25 +2644,27 @@ GHC_INLINE path& path::operator/=(const path& p) ++iter; } while (iter != p.end()) { - if (!first && !(!_path.empty() && _path[_path.length() - 1] == '/')) { - _path += '/'; + if (!first && !(!_path.empty() && _path[_path.length() - 1] == preferred_separator)) { + _path += preferred_separator; } first = false; - _path += (*iter++).generic_string(); + _path += (*iter++).native(); } + check_long_path(); return *this; } -GHC_INLINE void path::append_name(const char* name) +GHC_INLINE void path::append_name(const value_type* name) { if (_path.empty()) { this->operator/=(path(name)); } else { - if (_path.back() != path::generic_separator) { - _path.push_back(path::generic_separator); + if (_path.back() != path::preferred_separator) { + _path.push_back(path::preferred_separator); } _path += name; + check_long_path(); } } @@ -2407,7 +2679,7 @@ inline path& path::operator/=(const Source& source) template inline path& path::append(const Source& source) { - return this->operator/=(path(detail::toUtf8(source))); + return this->operator/=(path(source)); } template <> @@ -2426,7 +2698,7 @@ inline path& path::append(InputIterator first, InputIterator last) #ifdef GHC_EXPAND_IMPL //----------------------------------------------------------------------------- -// 30.10.8.4.4 concatenation +// [fs.path.concat] concatenation GHC_INLINE path& path::operator+=(const path& x) { @@ -2438,8 +2710,8 @@ GHC_INLINE path& path::operator+=(const string_type& x) return concat(x); } -#ifdef __cpp_lib_string_view -GHC_INLINE path& path::operator+=(std::basic_string_view x) +#ifdef GHC_WITH_STRING_VIEW +GHC_INLINE path& path::operator+=(basic_string_view x) { return concat(x); } @@ -2447,23 +2719,25 @@ GHC_INLINE path& path::operator+=(std::basic_string_view x) GHC_INLINE path& path::operator+=(const value_type* x) { - return concat(string_type(x)); +#ifdef GHC_WITH_STRING_VIEW + basic_string_view part(x); +#else + string_type part(x); +#endif + return concat(part); } GHC_INLINE path& path::operator+=(value_type x) { #ifdef GHC_OS_WINDOWS - if (x == '\\') { - x = generic_separator; + if (x == generic_separator) { + x = preferred_separator; } #endif - if (_path.empty() || _path.back() != generic_separator) { -#ifdef GHC_USE_WCHAR_T - _path += detail::toUtf8(string_type(1, x)); -#else + if (_path.empty() || _path.back() != preferred_separator) { _path += x; -#endif } + check_long_path(); return *this; } @@ -2478,8 +2752,12 @@ inline path::path_from_string& path::operator+=(const Source& x) template inline path::path_type_EcharT& path::operator+=(EcharT x) { +#ifdef GHC_WITH_STRING_VIEW + basic_string_view part(&x, 1); +#else std::basic_string part(1, x); - concat(detail::toUtf8(part)); +#endif + concat(part); return *this; } @@ -2487,25 +2765,28 @@ template inline path& path::concat(const Source& x) { path p(x); - postprocess_path_with_format(p._path, native_format); _path += p._path; + postprocess_path_with_format(native_format); return *this; } template inline path& path::concat(InputIterator first, InputIterator last) { _path.append(first, last); - postprocess_path_with_format(_path, native_format); + postprocess_path_with_format(native_format); return *this; } #ifdef GHC_EXPAND_IMPL //----------------------------------------------------------------------------- -// 30.10.8.4.5 modifiers +// [fs.path.modifiers] modifiers GHC_INLINE void path::clear() noexcept { _path.clear(); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = 0; +#endif } GHC_INLINE path& path::make_preferred() @@ -2543,58 +2824,19 @@ GHC_INLINE path& path::replace_extension(const path& replacement) GHC_INLINE void path::swap(path& rhs) noexcept { _path.swap(rhs._path); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + std::swap(_prefixLength, rhs._prefixLength); +#endif } //----------------------------------------------------------------------------- -// 30.10.8.4.6, native format observers -#ifdef GHC_OS_WINDOWS -GHC_INLINE path::impl_string_type path::native_impl() const -{ - impl_string_type result; - if (is_absolute() && _path.length() > MAX_PATH - 10) { - // expand long Windows filenames with marker - if (has_root_name() && _path[0] == '/') { - result = "\\\\?\\UNC" + _path.substr(1); - } - else { - result = "\\\\?\\" + _path; - } - } - else { - result = _path; - } - /*if (has_root_name() && root_name()._path[0] == '/') { - return _path; - }*/ - for (auto& c : result) { - if (c == '/') { - c = '\\'; - } - } - return result; -} -#else -GHC_INLINE const path::impl_string_type& path::native_impl() const +// [fs.path.native.obs] native format observers +GHC_INLINE const path::string_type& path::native() const noexcept { return _path; } -#endif -GHC_INLINE const path::string_type& path::native() const -{ -#ifdef GHC_OS_WINDOWS -#ifdef GHC_USE_WCHAR_T - _native_cache = detail::fromUtf8(native_impl()); -#else - _native_cache = native_impl(); -#endif - return _native_cache; -#else - return _path; -#endif -} - -GHC_INLINE const path::value_type* path::c_str() const +GHC_INLINE const path::value_type* path::c_str() const noexcept { return native().c_str(); } @@ -2609,14 +2851,22 @@ GHC_INLINE path::operator path::string_type() const template inline std::basic_string path::string(const Allocator& a) const { - return detail::fromUtf8>(native_impl(), a); +#ifdef GHC_USE_WCHAR_T + return detail::fromWChar>(_path, a); +#else + return detail::fromUtf8>(_path, a); +#endif } #ifdef GHC_EXPAND_IMPL GHC_INLINE std::string path::string() const { - return native_impl(); +#ifdef GHC_USE_WCHAR_T + return detail::toUtf8(native()); +#else + return native(); +#endif } GHC_INLINE std::wstring path::wstring() const @@ -2628,60 +2878,122 @@ GHC_INLINE std::wstring path::wstring() const #endif } +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +GHC_INLINE std::u8string path::u8string() const +{ +#ifdef GHC_USE_WCHAR_T + return std::u8string(reinterpret_cast(detail::toUtf8(native()).c_str())); +#else + return std::u8string(reinterpret_cast(c_str())); +#endif +} +#else GHC_INLINE std::string path::u8string() const { - return native_impl(); +#ifdef GHC_USE_WCHAR_T + return detail::toUtf8(native()); +#else + return native(); +#endif } +#endif GHC_INLINE std::u16string path::u16string() const { - return detail::fromUtf8(native_impl()); + // TODO: optimize + return detail::fromUtf8(string()); } GHC_INLINE std::u32string path::u32string() const { - return detail::fromUtf8(native_impl()); + // TODO: optimize + return detail::fromUtf8(string()); } #endif // GHC_EXPAND_IMPL //----------------------------------------------------------------------------- -// 30.10.8.4.7, generic format observers +// [fs.path.generic.obs] generic format observers template inline std::basic_string path::generic_string(const Allocator& a) const { +#ifdef GHC_OS_WINDOWS +#ifdef GHC_USE_WCHAR_T + auto result = detail::fromWChar, path::string_type>(_path, a); +#else + auto result = detail::fromUtf8>(_path, a); +#endif + for (auto& c : result) { + if (c == preferred_separator) { + c = generic_separator; + } + } + return result; +#else return detail::fromUtf8>(_path, a); +#endif } #ifdef GHC_EXPAND_IMPL -GHC_INLINE const std::string& path::generic_string() const +GHC_INLINE std::string path::generic_string() const { +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else return _path; +#endif } GHC_INLINE std::wstring path::generic_wstring() const { +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else return detail::fromUtf8(_path); -} +#endif +} // namespace filesystem +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +GHC_INLINE std::u8string path::generic_u8string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return std::u8string(reinterpret_cast(_path.c_str())); +#endif +} +#else GHC_INLINE std::string path::generic_u8string() const { +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else return _path; +#endif } +#endif GHC_INLINE std::u16string path::generic_u16string() const { +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else return detail::fromUtf8(_path); +#endif } GHC_INLINE std::u32string path::generic_u32string() const { +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else return detail::fromUtf8(_path); +#endif } //----------------------------------------------------------------------------- -// 30.10.8.4.8, compare +// [fs.path.compare] compare GHC_INLINE int path::compare(const path& p) const noexcept { #ifdef LWG_2936_BEHAVIOUR @@ -2715,26 +3027,22 @@ GHC_INLINE int path::compare(const path& p) const noexcept if (iter2 == p._path.end()) { return 1; } - if (*iter1 == '/') { + if (*iter1 == preferred_separator) { return -1; } - if (*iter2 == '/') { + if (*iter2 == preferred_separator) { return 1; } return *iter1 < *iter2 ? -1 : 1; -#else // LWG_2936_BEHAVIOUR +#else // LWG_2936_BEHAVIOUR #ifdef GHC_OS_WINDOWS auto rnl1 = root_name_length(); auto rnl2 = p.root_name_length(); auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2); - if(rnc) { + if (rnc) { return rnc; } - auto p1 = _path; - std::replace(p1.begin()+static_cast(rnl1), p1.end(), '/', '\\'); - auto p2 = p._path; - std::replace(p2.begin()+static_cast(rnl2), p2.end(), '/', '\\'); - return p1.compare(rnl1, std::string::npos, p2, rnl2, std::string::npos); + return _path.compare(rnl1, std::string::npos, p._path, rnl2, std::string::npos); #else return _path.compare(p._path); #endif @@ -2746,8 +3054,8 @@ GHC_INLINE int path::compare(const string_type& s) const return compare(path(s)); } -#ifdef __cpp_lib_string_view -GHC_INLINE int path::compare(std::basic_string_view s) const +#ifdef GHC_WITH_STRING_VIEW +GHC_INLINE int path::compare(basic_string_view s) const { return compare(path(s)); } @@ -2759,16 +3067,30 @@ GHC_INLINE int path::compare(const value_type* s) const } //----------------------------------------------------------------------------- -// 30.10.8.4.9, decomposition +// [fs.path.decompose] decomposition +#ifdef GHC_OS_WINDOWS +GHC_INLINE void path::handle_prefixes() +{ +#if defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = 0; + if (_path.length() >= 6 && _path[2] == '?' && std::toupper(static_cast(_path[4])) >= 'A' && std::toupper(static_cast(_path[4])) <= 'Z' && _path[5] == ':') { + if (detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\"))) || detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\??\\")))) { + _prefixLength = 4; + } + } +#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH +} +#endif + GHC_INLINE path::string_type::size_type path::root_name_length() const noexcept { #ifdef GHC_OS_WINDOWS - if (_path.length() >= 2 && std::toupper(static_cast(_path[0])) >= 'A' && std::toupper(static_cast(_path[0])) <= 'Z' && _path[1] == ':') { + if (_path.length() >= _prefixLength + 2 && std::toupper(static_cast(_path[_prefixLength])) >= 'A' && std::toupper(static_cast(_path[_prefixLength])) <= 'Z' && _path[_prefixLength + 1] == ':') { return 2; } #endif - if (_path.length() > 2 && _path[0] == '/' && _path[1] == '/' && _path[2] != '/' && std::isprint(_path[2])) { - impl_string_type::size_type pos = _path.find_first_of("/\\", 3); + if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator && std::isprint(_path[_prefixLength + 2])) { + impl_string_type::size_type pos = _path.find(preferred_separator, _prefixLength + 3); if (pos == impl_string_type::npos) { return _path.length(); } @@ -2781,46 +3103,43 @@ GHC_INLINE path::string_type::size_type path::root_name_length() const noexcept GHC_INLINE path path::root_name() const { - return path(_path.substr(0, root_name_length()), generic_format); + return path(_path.substr(_prefixLength, root_name_length()), native_format); } GHC_INLINE path path::root_directory() const { - if(has_root_directory()) { - return path("/", generic_format); + if (has_root_directory()) { + static const path _root_dir(std::string(1, preferred_separator), native_format); + return _root_dir; } return path(); } GHC_INLINE path path::root_path() const { - return path(root_name().generic_string() + root_directory().generic_string(), generic_format); + return path(root_name().string() + root_directory().string(), native_format); } GHC_INLINE path path::relative_path() const { - auto rootPathLen = root_name_length() + (has_root_directory() ? 1 : 0); + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); return path(_path.substr((std::min)(rootPathLen, _path.length())), generic_format); } GHC_INLINE path path::parent_path() const { - if (has_relative_path()) { - if (empty() || begin() == --end()) { + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); + if (rootPathLen < _path.length()) { + if (empty()) { return path(); } else { - path pp; - for (string_type s : input_iterator_range(begin(), --end())) { - if (s == "/") { - // don't use append to join a path- - pp += s; - } - else { - pp /= s; - } + auto piter = end(); + auto iter = piter.decrement(_path.end()); + if (iter > _path.begin() + static_cast(rootPathLen) && *iter != preferred_separator) { + --iter; } - return pp; + return path(_path.begin(), iter, native_format); } } else { @@ -2830,19 +3149,19 @@ GHC_INLINE path path::parent_path() const GHC_INLINE path path::filename() const { - return relative_path().empty() ? path() : path(*--end()); + return !has_relative_path() ? path() : path(*--end()); } GHC_INLINE path path::stem() const { - impl_string_type fn = filename().string(); + impl_string_type fn = filename().native(); if (fn != "." && fn != "..") { impl_string_type::size_type pos = fn.rfind('.'); if (pos != impl_string_type::npos && pos > 0) { - return path{fn.substr(0, pos), generic_format}; + return path{fn.substr(0, pos), native_format}; } } - return path{fn, generic_format}; + return path{fn, native_format}; } GHC_INLINE path path::extension() const @@ -2852,7 +3171,7 @@ GHC_INLINE path path::extension() const const auto& fn = *--iter; impl_string_type::size_type pos = fn._path.rfind('.'); if (pos != std::string::npos && pos > 0) { - return path(fn._path.substr(pos), generic_format); + return path(fn._path.substr(pos), native_format); } } return path(); @@ -2869,8 +3188,9 @@ GHC_INLINE bool has_executable_extension(const path& p) if (pos == std::string::npos || pos == 0 || fn._path.length() - pos != 3) { return false; } - const char* ext = fn._path.c_str() + pos + 1; - if (detail::equals_simple_insensitive(ext, "exe") || detail::equals_simple_insensitive(ext, "cmd") || detail::equals_simple_insensitive(ext, "bat") || detail::equals_simple_insensitive(ext, "com")) { + const path::value_type* ext = fn._path.c_str() + pos + 1; + if (detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("exe")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("cmd")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("bat")) || + detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("com"))) { return true; } } @@ -2880,7 +3200,7 @@ GHC_INLINE bool has_executable_extension(const path& p) #endif //----------------------------------------------------------------------------- -// 30.10.8.4.10, query +// [fs.path.query] query GHC_INLINE bool path::empty() const noexcept { return _path.empty(); @@ -2893,8 +3213,8 @@ GHC_INLINE bool path::has_root_name() const GHC_INLINE bool path::has_root_directory() const { - auto rootLen = root_name_length(); - return (_path.length() > rootLen && _path[rootLen] == '/'); + auto rootLen = _prefixLength + root_name_length(); + return (_path.length() > rootLen && _path[rootLen] == preferred_separator); } GHC_INLINE bool path::has_root_path() const @@ -2904,7 +3224,7 @@ GHC_INLINE bool path::has_root_path() const GHC_INLINE bool path::has_relative_path() const { - auto rootPathLen = root_name_length() + (has_root_directory() ? 1 : 0); + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); return rootPathLen < _path.length(); } @@ -2943,7 +3263,7 @@ GHC_INLINE bool path::is_relative() const } //----------------------------------------------------------------------------- -// 30.10.8.4.11, generation +// [fs.path.gen] generation GHC_INLINE path path::lexically_normal() const { path dest; @@ -2959,7 +3279,7 @@ GHC_INLINE path path::lexically_normal() const continue; } else if (*(--dest.end()) != "..") { - if (dest._path.back() == generic_separator) { + if (dest._path.back() == preferred_separator) { dest._path.pop_back(); } dest.remove_filename(); @@ -3019,64 +3339,55 @@ GHC_INLINE path path::lexically_proximate(const path& base) const } //----------------------------------------------------------------------------- -// 30.10.8.5, iterators +// [fs.path.itr] iterators GHC_INLINE path::iterator::iterator() {} -GHC_INLINE path::iterator::iterator(const path::impl_string_type::const_iterator& first, const path::impl_string_type::const_iterator& last, const path::impl_string_type::const_iterator& pos) - : _first(first) - , _last(last) +GHC_INLINE path::iterator::iterator(const path& p, const impl_string_type::const_iterator& pos) + : _first(p._path.begin()) + , _last(p._path.end()) + , _prefix(_first + static_cast(p._prefixLength)) + , _root(p.has_root_directory() ? _first + static_cast(p._prefixLength + p.root_name_length()) : _last) , _iter(pos) { - updateCurrent(); - // find the position of a potential root directory slash -#ifdef GHC_OS_WINDOWS - if (_last - _first >= 3 && std::toupper(static_cast(*first)) >= 'A' && std::toupper(static_cast(*first)) <= 'Z' && *(first + 1) == ':' && *(first + 2) == '/') { - _root = _first + 2; - } - else -#endif - { - if (_first != _last && *_first == '/') { - if (_last - _first >= 2 && *(_first + 1) == '/' && !(_last - _first >= 3 && *(_first + 2) == '/')) { - _root = increment(_first); - } - else { - _root = _first; - } - } - else { - _root = _last; - } + if (pos != _last) { + updateCurrent(); } } GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(const path::impl_string_type::const_iterator& pos) const { path::impl_string_type::const_iterator i = pos; - bool fromStart = i == _first; + bool fromStart = i == _first || i == _prefix; if (i != _last) { - // we can only sit on a slash if it is a network name or a root - if (*i++ == '/') { - if (i != _last && *i == '/') { - if (fromStart && !(i + 1 != _last && *(i + 1) == '/')) { + if (fromStart && i == _first && _prefix > _first) { + i = _prefix; + } + else if (*i++ == preferred_separator) { + // we can only sit on a slash if it is a network name or a root + if (i != _last && *i == preferred_separator) { + if (fromStart && !(i + 1 != _last && *(i + 1) == preferred_separator)) { // leadind double slashes detected, treat this and the // following until a slash as one unit - i = std::find(++i, _last, '/'); + i = std::find(++i, _last, preferred_separator); } else { // skip redundant slashes - while (i != _last && *i == '/') { + while (i != _last && *i == preferred_separator) { ++i; } } } } else { +#ifdef GHC_OS_WINDOWS if (fromStart && i != _last && *i == ':') { ++i; } else { - i = std::find(i, _last, '/'); +#else + { +#endif + i = std::find(i, _last, preferred_separator); } } } @@ -3090,18 +3401,18 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(cons --i; // if this is now the root slash or the trailing slash, we are done, // else check for network name - if (i != _root && (pos != _last || *i != '/')) { + if (i != _root && (pos != _last || *i != preferred_separator)) { #ifdef GHC_OS_WINDOWS - static const std::string seps = "/:"; + static const impl_string_type seps = GHC_PLATFORM_LITERAL("\\:"); i = std::find_first_of(std::reverse_iterator(i), std::reverse_iterator(_first), seps.begin(), seps.end()).base(); if (i > _first && *i == ':') { i++; } #else - i = std::find(std::reverse_iterator(i), std::reverse_iterator(_first), '/').base(); + i = std::find(std::reverse_iterator(i), std::reverse_iterator(_first), preferred_separator).base(); #endif // Now we have to check if this is a network name - if (i - _first == 2 && *_first == '/' && *(_first + 1) == '/') { + if (i - _first == 2 && *_first == preferred_separator && *(_first + 1) == preferred_separator) { i -= 2; } } @@ -3111,25 +3422,21 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(cons GHC_INLINE void path::iterator::updateCurrent() { - if (_iter != _first && _iter != _last && (*_iter == '/' && _iter != _root) && (_iter + 1 == _last)) { - _current = ""; + if ((_iter == _last) || (_iter != _first && _iter != _last && (*_iter == preferred_separator && _iter != _root) && (_iter + 1 == _last))) { + _current.clear(); } else { _current.assign(_iter, increment(_iter)); - if (_current.generic_string().size() > 1 && _current.generic_string()[0] == '/' && _current.generic_string()[_current.generic_string().size() - 1] == '/') { - // shrink successive slashes to one - _current = "/"; - } } } GHC_INLINE path::iterator& path::iterator::operator++() { _iter = increment(_iter); - while (_iter != _last && // we didn't reach the end - _iter != _root && // this is not a root position - *_iter == '/' && // we are on a slash - (_iter + 1) != _last // the slash is not the last char + while (_iter != _last && // we didn't reach the end + _iter != _root && // this is not a root position + *_iter == preferred_separator && // we are on a separator + (_iter + 1) != _last // the slash is not the last char ) { ++_iter; } @@ -3180,16 +3487,16 @@ GHC_INLINE path::iterator::pointer path::iterator::operator->() const GHC_INLINE path::iterator path::begin() const { - return iterator(_path.begin(), _path.end(), _path.begin()); + return iterator(*this, _path.begin()); } GHC_INLINE path::iterator path::end() const { - return iterator(_path.begin(), _path.end(), _path.end()); + return iterator(*this, _path.end()); } //----------------------------------------------------------------------------- -// 30.10.8.6, path non-member functions +// [fs.path.nonmember] path non-member functions GHC_INLINE void swap(path& lhs, path& rhs) noexcept { swap(lhs._path, rhs._path); @@ -3200,6 +3507,13 @@ GHC_INLINE size_t hash_value(const path& p) noexcept return std::hash()(p.generic_string()); } +#ifdef GHC_HAS_THREEWAY_COMP +GHC_INLINE std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) <=> 0; +} +#endif + GHC_INLINE bool operator==(const path& lhs, const path& rhs) noexcept { return lhs.compare(rhs) == 0; @@ -3240,7 +3554,7 @@ GHC_INLINE path operator/(const path& lhs, const path& rhs) #endif // GHC_EXPAND_IMPL //----------------------------------------------------------------------------- -// 30.10.8.6.1 path inserter and extractor +// [fs.path.io] path inserter and extractor template inline std::basic_ostream& operator<<(std::basic_ostream& os, const path& p) { @@ -3297,7 +3611,7 @@ inline std::basic_istream& operator>>(std::basic_istream buf(size, 0); - ULONG s2 = GetFullPathNameW(p.wstring().c_str(), size, buf.data(), nullptr); + ULONG s2 = GetFullPathNameW(GHC_NATIVEWP(p), size, buf.data(), nullptr); if (s2 && s2 < size) { path result = path(std::wstring(buf.data(), s2)); if (p.filename() == ".") { @@ -3428,7 +3742,6 @@ GHC_INLINE path canonical(const path& p, std::error_code& ec) return path(); } path work = p.is_absolute() ? p : absolute(p, ec); - path root = work.root_path(); path result; auto fs = status(work, ec); @@ -3441,6 +3754,7 @@ GHC_INLINE path canonical(const path& p, std::error_code& ec) } bool redo; do { + auto rootPathLen = work._prefixLength + work.root_name_length() + (work.has_root_directory() ? 1 : 0); redo = false; result.clear(); for (auto pe : work) { @@ -3451,7 +3765,7 @@ GHC_INLINE path canonical(const path& p, std::error_code& ec) result = result.parent_path(); continue; } - else if ((result / pe).string().length() <= root.string().length()) { + else if ((result / pe).string().length() <= rootPathLen) { result /= pe; continue; } @@ -3489,14 +3803,7 @@ GHC_INLINE void copy(const path& from, const path& to) { copy(from, to, copy_options::none); } -#endif -GHC_INLINE void copy(const path& from, const path& to, std::error_code& ec) noexcept -{ - copy(from, to, copy_options::none, ec); -} - -#ifdef GHC_WITH_EXCEPTIONS GHC_INLINE void copy(const path& from, const path& to, copy_options options) { std::error_code ec; @@ -3507,6 +3814,11 @@ GHC_INLINE void copy(const path& from, const path& to, copy_options options) } #endif +GHC_INLINE void copy(const path& from, const path& to, std::error_code& ec) noexcept +{ + copy(from, to, copy_options::none, ec); +} + GHC_INLINE void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept { std::error_code tec; @@ -3590,14 +3902,7 @@ GHC_INLINE bool copy_file(const path& from, const path& to) { return copy_file(from, to, copy_options::none); } -#endif -GHC_INLINE bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept -{ - return copy_file(from, to, copy_options::none, ec); -} - -#ifdef GHC_WITH_EXCEPTIONS GHC_INLINE bool copy_file(const path& from, const path& to, copy_options option) { std::error_code ec; @@ -3609,6 +3914,11 @@ GHC_INLINE bool copy_file(const path& from, const path& to, copy_options option) } #endif +GHC_INLINE bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept +{ + return copy_file(from, to, copy_options::none, ec); +} + GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept { std::error_code tecf, tect; @@ -3620,11 +3930,14 @@ GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options ec = tecf; return false; } - 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)) { - ec = tect ? tect : detail::make_error_code(detail::portable_error::exists); - return false; - } if (exists(st)) { + if ((options & copy_options::skip_existing) == copy_options::skip_existing) { + return false; + } + if (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none) { + ec = tect ? tect : detail::make_error_code(detail::portable_error::exists); + return false; + } if ((options & copy_options::update_existing) == copy_options::update_existing) { auto from_time = last_write_time(from, ec); if (ec) { @@ -3643,7 +3956,7 @@ GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options overwrite = true; } #ifdef GHC_OS_WINDOWS - if (!::CopyFileW(detail::fromUtf8(from.u8string()).c_str(), detail::fromUtf8(to.u8string()).c_str(), !overwrite)) { + if (!::CopyFileW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), !overwrite)) { ec = detail::make_system_error(); return false; } @@ -3664,15 +3977,33 @@ GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options ::close(in); return false; } + if (st.permissions() != sf.permissions()) { + if (::fchmod(out, static_cast(sf.permissions() & perms::all)) != 0) { + ec = detail::make_system_error(); + ::close(in); + ::close(out); + return false; + } + } ssize_t br, bw; - while ((br = ::read(in, buffer.data(), buffer.size())) > 0) { + while (true) { + do { br = ::read(in, buffer.data(), buffer.size()); } while(errno == EINTR); + if(!br) { + break; + } + if(br < 0) { + ec = detail::make_system_error(); + ::close(in); + ::close(out); + return false; + } ssize_t offset = 0; do { if ((bw = ::write(out, buffer.data() + offset, static_cast(br))) > 0) { br -= bw; offset += bw; } - else if (bw < 0) { + else if (bw < 0 && errno != EINTR) { ec = detail::make_system_error(); ::close(in); ::close(out); @@ -3728,34 +4059,36 @@ GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept path current; ec.clear(); bool didCreate = false; - for (path::string_type part : p) { + auto rootPathLen = p._prefixLength + p.root_name_length() + (p.has_root_directory() ? 1 : 0); + current = p.native().substr(0, rootPathLen); + path folders(p._path.substr(rootPathLen)); + for (path::string_type part : folders) { current /= part; - if (current != p.root_name() && current != p.root_path()) { - std::error_code tec; - auto fs = status(current, tec); - if (tec && fs.type() != file_type::not_found) { - ec = tec; - return false; - } - if (!exists(fs)) { - create_directory(current, ec); - if (ec) { - std::error_code tmp_ec; - if (is_directory(current, tmp_ec)) { - ec.clear(); - } else { - return false; - } - } - didCreate = true; - } -#ifndef LWG_2935_BEHAVIOUR - else if (!is_directory(fs)) { - ec = detail::make_error_code(detail::portable_error::exists); - return false; - } -#endif + std::error_code tec; + auto fs = status(current, tec); + if (tec && fs.type() != file_type::not_found) { + ec = tec; + return false; } + if (!exists(fs)) { + create_directory(current, ec); + if (ec) { + std::error_code tmp_ec; + if (is_directory(current, tmp_ec)) { + ec.clear(); + } + else { + return false; + } + } + didCreate = true; + } +#ifndef LWG_2935_BEHAVIOUR + else if (!is_directory(fs)) { + ec = detail::make_error_code(detail::portable_error::exists); + return false; + } +#endif } return didCreate; } @@ -3805,12 +4138,12 @@ GHC_INLINE bool create_directory(const path& p, const path& attributes, std::err #endif #ifdef GHC_OS_WINDOWS if (!attributes.empty()) { - if (!::CreateDirectoryExW(detail::fromUtf8(attributes.u8string()).c_str(), detail::fromUtf8(p.u8string()).c_str(), NULL)) { + if (!::CreateDirectoryExW(GHC_NATIVEWP(attributes), GHC_NATIVEWP(p), NULL)) { ec = detail::make_system_error(); return false; } } - else if (!::CreateDirectoryW(detail::fromUtf8(p.u8string()).c_str(), NULL)) { + else if (!::CreateDirectoryW(GHC_NATIVEWP(p), NULL)) { ec = detail::make_system_error(); return false; } @@ -3905,6 +4238,13 @@ GHC_INLINE path current_path(std::error_code& ec) return path(); } return path(std::wstring(buffer.get()), path::native_format); +#elif defined(__GLIBC__) + std::unique_ptr buffer { ::getcwd(NULL, 0), std::free }; + if (buffer == nullptr) { + ec = detail::make_system_error(); + return path(); + } + return path(buffer.get()); #else size_t pathlen = static_cast(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX))); std::unique_ptr buffer(new char[pathlen + 1]); @@ -3931,7 +4271,7 @@ GHC_INLINE void current_path(const path& p, std::error_code& ec) noexcept { ec.clear(); #ifdef GHC_OS_WINDOWS - if (!::SetCurrentDirectoryW(detail::fromUtf8(p.u8string()).c_str())) { + if (!::SetCurrentDirectoryW(GHC_NATIVEWP(p))) { ec = detail::make_system_error(); } #else @@ -3978,10 +4318,10 @@ GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec) { ec.clear(); #ifdef GHC_OS_WINDOWS - std::shared_ptr 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); + detail::unique_handle file1(::CreateFileW(GHC_NATIVEWP(p1), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); auto e1 = ::GetLastError(); - std::shared_ptr 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); - if (file1.get() == INVALID_HANDLE_VALUE || file2.get() == INVALID_HANDLE_VALUE) { + detail::unique_handle file2(::CreateFileW(GHC_NATIVEWP(p2), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); + if (!file1 || !file2) { #ifdef LWG_2937_BEHAVIOUR ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); #else @@ -4038,7 +4378,7 @@ GHC_INLINE uintmax_t file_size(const path& p, std::error_code& ec) noexcept ec.clear(); #ifdef GHC_OS_WINDOWS WIN32_FILE_ATTRIBUTE_DATA attr; - if (!GetFileAttributesExW(detail::fromUtf8(p.u8string()).c_str(), GetFileExInfoStandard, &attr)) { + if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { ec = detail::make_system_error(); return static_cast(-1); } @@ -4071,9 +4411,9 @@ GHC_INLINE uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcep ec.clear(); #ifdef GHC_OS_WINDOWS uintmax_t result = static_cast(-1); - std::shared_ptr 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); + detail::unique_handle file(::CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); BY_HANDLE_FILE_INFORMATION inf; - if (file.get() == INVALID_HANDLE_VALUE) { + if (!file) { ec = detail::make_system_error(); } else { @@ -4302,7 +4642,7 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err ec.clear(); auto d = new_time.time_since_epoch(); #ifdef GHC_OS_WINDOWS - std::shared_ptr 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); + detail::unique_handle file(::CreateFileW(GHC_NATIVEWP(p), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)); FILETIME ft; auto tt = std::chrono::duration_cast(d).count() * 10 + 116444736000000000; ft.dwLowDateTime = static_cast(tt); @@ -4310,9 +4650,9 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err if (!::SetFileTime(file.get(), 0, 0, &ft)) { ec = detail::make_system_error(); } -#elif defined(GHC_OS_MACOS) -#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300 +#elif defined(GHC_OS_MACOS) && \ + (__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_13) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_11_0) || \ + (__TV_OS_VERSION_MIN_REQUIRED < __TVOS_11_0) || (__WATCH_OS_VERSION_MIN_REQUIRED < __WATCHOS_4_0) struct ::stat fs; if (::stat(p.c_str(), &fs) == 0) { struct ::timeval tv[2]; @@ -4326,18 +4666,6 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err } ec = detail::make_system_error(); return; -#else - struct ::timespec times[2]; - times[0].tv_sec = 0; - times[0].tv_nsec = UTIME_OMIT; - times[1].tv_sec = std::chrono::duration_cast(d).count(); - times[1].tv_nsec = 0; //std::chrono::duration_cast(d).count() % 1000000000; - if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { - ec = detail::make_system_error(); - } - return; -#endif -#endif #else #ifndef UTIME_OMIT #define UTIME_OMIT ((1l << 30) - 2l) @@ -4350,7 +4678,7 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err #if defined(__ANDROID_API__) && __ANDROID_API__ < 12 if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { #else - if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { + if (::utimensat((int)AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { #endif ec = detail::make_system_error(); } @@ -4374,7 +4702,7 @@ GHC_INLINE void permissions(const path& p, perms prms, std::error_code& ec) noex permissions(p, prms, perm_options::replace, ec); } -GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) +GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept { if (static_cast(opts & (perm_options::replace | perm_options::add | perm_options::remove)) == 0) { ec = detail::make_error_code(detail::portable_error::invalid_argument); @@ -4391,10 +4719,10 @@ GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::e } #ifdef GHC_OS_WINDOWS #ifdef __GNUC__ - auto oldAttr = GetFileAttributesW(p.wstring().c_str()); + auto oldAttr = GetFileAttributesW(GHC_NATIVEWP(p)); if (oldAttr != INVALID_FILE_ATTRIBUTES) { DWORD newAttr = ((prms & perms::owner_write) == perms::owner_write) ? oldAttr & ~(static_cast(FILE_ATTRIBUTE_READONLY)) : oldAttr | FILE_ATTRIBUTE_READONLY; - if (oldAttr == newAttr || SetFileAttributesW(p.wstring().c_str(), newAttr)) { + if (oldAttr == newAttr || SetFileAttributesW(GHC_NATIVEWP(p), newAttr)) { return; } } @@ -4423,7 +4751,11 @@ GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::e #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE path proximate(const path& p, std::error_code& ec) { - return proximate(p, current_path(), ec); + auto cp = current_path(ec); + if (!ec) { + return proximate(p, cp, ec); + } + return path(); } #endif @@ -4495,8 +4827,13 @@ GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept { ec.clear(); #ifdef GHC_OS_WINDOWS +#ifdef GHC_USE_WCHAR_T + auto cstr = p.c_str(); +#else std::wstring np = detail::fromUtf8(p.u8string()); - DWORD attr = GetFileAttributesW(np.c_str()); + auto cstr = np.c_str(); +#endif + DWORD attr = GetFileAttributesW(cstr); if (attr == INVALID_FILE_ATTRIBUTES) { auto error = ::GetLastError(); if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) { @@ -4504,14 +4841,21 @@ GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept } ec = detail::make_system_error(error); } + else if (attr & FILE_ATTRIBUTE_READONLY) { + auto new_attr = attr & ~static_cast(FILE_ATTRIBUTE_READONLY); + if (!SetFileAttributesW(cstr, new_attr)) { + auto error = ::GetLastError(); + ec = detail::make_system_error(error); + } + } if (!ec) { if (attr & FILE_ATTRIBUTE_DIRECTORY) { - if (!RemoveDirectoryW(np.c_str())) { + if (!RemoveDirectoryW(cstr)) { ec = detail::make_system_error(); } } else { - if (!DeleteFileW(np.c_str())) { + if (!DeleteFileW(cstr)) { ec = detail::make_system_error(); } } @@ -4549,24 +4893,25 @@ GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept return static_cast(-1); } std::error_code tec; - auto fs = status(p, tec); + auto fs = symlink_status(p, tec); if (exists(fs) && is_directory(fs)) { for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) { - if (ec) { + if (ec && !detail::is_not_found_error(ec)) { break; } bool is_symlink_result = iter->is_symlink(ec); - if (ec) return static_cast(-1); - bool is_directory_result = iter->is_directory(ec); - if (ec) return static_cast(-1); - if (!is_symlink_result && is_directory_result) { + if (ec) + return static_cast(-1); + if (!is_symlink_result && iter->is_directory(ec)) { count += remove_all(iter->path(), ec); if (ec) { return static_cast(-1); } } else { - remove(iter->path(), ec); + if (!ec) { + remove(iter->path(), ec); + } if (ec) { return static_cast(-1); } @@ -4601,7 +4946,7 @@ GHC_INLINE void rename(const path& from, const path& to, std::error_code& ec) no ec.clear(); #ifdef GHC_OS_WINDOWS if (from != to) { - if (!MoveFileExW(detail::fromUtf8(from.u8string()).c_str(), detail::fromUtf8(to.u8string()).c_str(), (DWORD)MOVEFILE_REPLACE_EXISTING)) { + if (!MoveFileExW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), (DWORD)MOVEFILE_REPLACE_EXISTING)) { ec = detail::make_system_error(); } } @@ -4631,7 +4976,7 @@ GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec) #ifdef GHC_OS_WINDOWS LARGE_INTEGER lisize; lisize.QuadPart = static_cast(size); - if(lisize.QuadPart < 0) { + if (lisize.QuadPart < 0) { #ifdef ERROR_FILE_TOO_LARGE ec = detail::make_system_error(ERROR_FILE_TOO_LARGE); #else @@ -4639,8 +4984,8 @@ GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec) #endif return; } - std::shared_ptr file(CreateFileW(detail::fromUtf8(p.u8string()).c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL), CloseHandle); - if (file.get() == INVALID_HANDLE_VALUE) { + detail::unique_handle file(CreateFileW(GHC_NATIVEWP(p), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)); + if (!file) { ec = detail::make_system_error(); } else if (SetFilePointerEx(file.get(), lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(file.get()) == 0) { @@ -4669,10 +5014,10 @@ GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept { ec.clear(); #ifdef GHC_OS_WINDOWS - ULARGE_INTEGER freeBytesAvailableToCaller = {{0, 0}}; - ULARGE_INTEGER totalNumberOfBytes = {{0, 0}}; - ULARGE_INTEGER totalNumberOfFreeBytes = {{0, 0}}; - if (!GetDiskFreeSpaceExW(detail::fromUtf8(p.u8string()).c_str(), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) { + ULARGE_INTEGER freeBytesAvailableToCaller = {{ 0, 0 }}; + ULARGE_INTEGER totalNumberOfBytes = {{ 0, 0 }}; + ULARGE_INTEGER totalNumberOfFreeBytes = {{ 0, 0 }}; + if (!GetDiskFreeSpaceExW(GHC_NATIVEWP(p), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) { ec = detail::make_system_error(); return {static_cast(-1), static_cast(-1), static_cast(-1)}; } @@ -4683,7 +5028,7 @@ GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept ec = detail::make_system_error(); return {static_cast(-1), static_cast(-1), static_cast(-1)}; } - return {static_cast(sfs.f_blocks * sfs.f_frsize), static_cast(sfs.f_bfree * sfs.f_frsize), static_cast(sfs.f_bavail * sfs.f_frsize)}; + return {static_cast(sfs.f_blocks) * static_cast(sfs.f_frsize), static_cast(sfs.f_bfree) * static_cast(sfs.f_frsize), static_cast(sfs.f_bavail) * static_cast(sfs.f_frsize)}; #endif } @@ -4814,8 +5159,8 @@ GHC_INLINE path weakly_canonical(const path& p, std::error_code& ec) noexcept } //----------------------------------------------------------------------------- -// 30.10.11 class file_status -// 30.10.11.1 constructors and destructor +// [fs.class.file_status] class file_status +// [fs.file_status.cons] constructors and destructor GHC_INLINE file_status::file_status() noexcept : file_status(file_type::none) { @@ -4856,7 +5201,7 @@ GHC_INLINE file_status& file_status::operator=(file_status&& rhs) noexcept return *this; } -// 30.10.11.3 modifiers +// [fs.file_status.mods] modifiers GHC_INLINE void file_status::type(file_type ft) noexcept { _type = ft; @@ -4867,7 +5212,7 @@ GHC_INLINE void file_status::permissions(perms prms) noexcept _perms = prms; } -// 30.10.11.2 observers +// [fs.file_status.obs] observers GHC_INLINE file_type file_status::type() const noexcept { return _type; @@ -4879,17 +5224,17 @@ GHC_INLINE perms file_status::permissions() const noexcept } //----------------------------------------------------------------------------- -// 30.10.12 class directory_entry -// 30.10.12.1 constructors and destructor +// [fs.class.directory_entry] class directory_entry +// [fs.dir.entry.cons] constructors and destructor // directory_entry::directory_entry() noexcept = default; // directory_entry::directory_entry(const directory_entry&) = default; // directory_entry::directory_entry(directory_entry&&) noexcept = default; #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE directory_entry::directory_entry(const filesystem::path& p) : _path(p) - , _file_size(0) + , _file_size(static_cast(-1)) #ifndef GHC_OS_WINDOWS - , _hard_link_count(0) + , _hard_link_count(static_cast(-1)) #endif , _last_write_time(0) { @@ -4899,9 +5244,9 @@ GHC_INLINE directory_entry::directory_entry(const filesystem::path& p) GHC_INLINE directory_entry::directory_entry(const filesystem::path& p, std::error_code& ec) : _path(p) - , _file_size(0) + , _file_size(static_cast(-1)) #ifndef GHC_OS_WINDOWS - , _hard_link_count(0) + , _hard_link_count(static_cast(-1)) #endif , _last_write_time(0) { @@ -4914,7 +5259,7 @@ GHC_INLINE directory_entry::~directory_entry() {} // directory_entry& directory_entry::operator=(const directory_entry&) = default; // directory_entry& directory_entry::operator=(directory_entry&&) noexcept = default; -// 30.10.12.2 directory_entry modifiers +// [fs.dir.entry.mods] directory_entry modifiers #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE void directory_entry::assign(const filesystem::path& p) { @@ -4948,7 +5293,7 @@ GHC_INLINE void directory_entry::refresh() { std::error_code ec; refresh(ec); - if (ec) { + if (ec && (_status.type() == file_type::none || _symlink_status.type() != file_type::symlink)) { throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec); } } @@ -4963,7 +5308,7 @@ GHC_INLINE void directory_entry::refresh(std::error_code& ec) noexcept #endif } -// 30.10.12.3 directory_entry observers +// [fs.dir.entry.obs] directory_entry observers GHC_INLINE const filesystem::path& directory_entry::path() const noexcept { return _path; @@ -4974,117 +5319,140 @@ GHC_INLINE directory_entry::operator const filesystem::path&() const noexcept return _path; } +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_type directory_entry::status_file_type() const +{ + return _status.type() != file_type::none ? _status.type() : filesystem::status(path()).type(); +} +#endif + +GHC_INLINE file_type directory_entry::status_file_type(std::error_code& ec) const noexcept +{ + if (_status.type() != file_type::none) { + ec.clear(); + return _status.type(); + } + return filesystem::status(path(), ec).type(); +} + #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE bool directory_entry::exists() const { - return filesystem::exists(status()); + return status_file_type() != file_type::not_found; } #endif GHC_INLINE bool directory_entry::exists(std::error_code& ec) const noexcept { - return filesystem::exists(status(ec)); + return status_file_type(ec) != file_type::not_found; } #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE bool directory_entry::is_block_file() const { - return filesystem::is_block_file(status()); + return status_file_type() == file_type::block; } #endif GHC_INLINE bool directory_entry::is_block_file(std::error_code& ec) const noexcept { - return filesystem::is_block_file(status(ec)); + return status_file_type(ec) == file_type::block; } #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE bool directory_entry::is_character_file() const { - return filesystem::is_character_file(status()); + return status_file_type() == file_type::character; } #endif GHC_INLINE bool directory_entry::is_character_file(std::error_code& ec) const noexcept { - return filesystem::is_character_file(status(ec)); + return status_file_type(ec) == file_type::character; } #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE bool directory_entry::is_directory() const { - return filesystem::is_directory(status()); + return status_file_type() == file_type::directory; } #endif GHC_INLINE bool directory_entry::is_directory(std::error_code& ec) const noexcept { - return filesystem::is_directory(status(ec)); + return status_file_type(ec) == file_type::directory; } #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE bool directory_entry::is_fifo() const { - return filesystem::is_fifo(status()); + return status_file_type() == file_type::fifo; } #endif GHC_INLINE bool directory_entry::is_fifo(std::error_code& ec) const noexcept { - return filesystem::is_fifo(status(ec)); + return status_file_type(ec) == file_type::fifo; } #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE bool directory_entry::is_other() const { - return filesystem::is_other(status()); + auto ft = status_file_type(); + return ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(); } #endif GHC_INLINE bool directory_entry::is_other(std::error_code& ec) const noexcept { - return filesystem::is_other(status(ec)); + auto ft = status_file_type(ec); + bool other = ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(ec); + return !ec && other; } #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE bool directory_entry::is_regular_file() const { - return filesystem::is_regular_file(status()); + return status_file_type() == file_type::regular; } #endif GHC_INLINE bool directory_entry::is_regular_file(std::error_code& ec) const noexcept { - return filesystem::is_regular_file(status(ec)); + return status_file_type(ec) == file_type::regular; } #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE bool directory_entry::is_socket() const { - return filesystem::is_socket(status()); + return status_file_type() == file_type::socket; } #endif GHC_INLINE bool directory_entry::is_socket(std::error_code& ec) const noexcept { - return filesystem::is_socket(status(ec)); + return status_file_type(ec) == file_type::socket; } #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE bool directory_entry::is_symlink() const { - return filesystem::is_symlink(symlink_status()); + return _symlink_status.type() != file_type::none ? _symlink_status.type() == file_type::symlink : filesystem::is_symlink(symlink_status()); } #endif GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept { + if (_symlink_status.type() != file_type::none) { + ec.clear(); + return _symlink_status.type() == file_type::symlink; + } return filesystem::is_symlink(symlink_status(ec)); } #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE uintmax_t directory_entry::file_size() const { - if (_status.type() != file_type::none) { + if (_file_size != static_cast(-1)) { return _file_size; } return filesystem::file_size(path()); @@ -5093,7 +5461,7 @@ GHC_INLINE uintmax_t directory_entry::file_size() const GHC_INLINE uintmax_t directory_entry::file_size(std::error_code& ec) const noexcept { - if (_status.type() != file_type::none) { + if (_file_size != static_cast(-1)) { ec.clear(); return _file_size; } @@ -5105,7 +5473,7 @@ GHC_INLINE uintmax_t directory_entry::file_size(std::error_code& ec) const noexc GHC_INLINE uintmax_t directory_entry::hard_link_count() const { #ifndef GHC_OS_WINDOWS - if (_status.type() != file_type::none) { + if (_hard_link_count != static_cast(-1)) { return _hard_link_count; } #endif @@ -5116,7 +5484,7 @@ GHC_INLINE uintmax_t directory_entry::hard_link_count() const GHC_INLINE uintmax_t directory_entry::hard_link_count(std::error_code& ec) const noexcept { #ifndef GHC_OS_WINDOWS - if (_status.type() != file_type::none) { + if (_hard_link_count != static_cast(-1)) { ec.clear(); return _hard_link_count; } @@ -5128,7 +5496,7 @@ GHC_INLINE uintmax_t directory_entry::hard_link_count(std::error_code& ec) const #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE file_time_type directory_entry::last_write_time() const { - if (_status.type() != file_type::none) { + if (_last_write_time != 0) { return std::chrono::system_clock::from_time_t(_last_write_time); } return filesystem::last_write_time(path()); @@ -5137,7 +5505,7 @@ GHC_INLINE file_time_type directory_entry::last_write_time() const GHC_INLINE file_time_type directory_entry::last_write_time(std::error_code& ec) const noexcept { - if (_status.type() != file_type::none) { + if (_last_write_time != 0) { ec.clear(); return std::chrono::system_clock::from_time_t(_last_write_time); } @@ -5147,7 +5515,7 @@ GHC_INLINE file_time_type directory_entry::last_write_time(std::error_code& ec) #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE file_status directory_entry::status() const { - if (_status.type() != file_type::none) { + if (_status.type() != file_type::none && _status.permissions() != perms::unknown) { return _status; } return filesystem::status(path()); @@ -5156,7 +5524,7 @@ GHC_INLINE file_status directory_entry::status() const GHC_INLINE file_status directory_entry::status(std::error_code& ec) const noexcept { - if (_status.type() != file_type::none) { + if (_status.type() != file_type::none && _status.permissions() != perms::unknown) { ec.clear(); return _status; } @@ -5166,7 +5534,7 @@ GHC_INLINE file_status directory_entry::status(std::error_code& ec) const noexce #ifdef GHC_WITH_EXCEPTIONS GHC_INLINE file_status directory_entry::symlink_status() const { - if (_symlink_status.type() != file_type::none) { + if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) { return _symlink_status; } return filesystem::symlink_status(path()); @@ -5175,13 +5543,20 @@ GHC_INLINE file_status directory_entry::symlink_status() const GHC_INLINE file_status directory_entry::symlink_status(std::error_code& ec) const noexcept { - if (_symlink_status.type() != file_type::none) { + if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) { ec.clear(); return _symlink_status; } return filesystem::symlink_status(path(), ec); } +#ifdef GHC_HAS_THREEWAY_COMP +GHC_INLINE std::strong_ordering directory_entry::operator<=>(const directory_entry& rhs) const noexcept +{ + return _path <=> rhs._path; +} +#endif + GHC_INLINE bool directory_entry::operator<(const directory_entry& rhs) const noexcept { return _path < rhs._path; @@ -5213,7 +5588,7 @@ GHC_INLINE bool directory_entry::operator>=(const directory_entry& rhs) const no } //----------------------------------------------------------------------------- -// 30.10.13 class directory_iterator +// [fs.class.directory_iterator] class directory_iterator #ifdef GHC_OS_WINDOWS class directory_iterator::impl @@ -5226,12 +5601,12 @@ public: { if (!_base.empty()) { ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW)); - if ((_dirHandle = FindFirstFileW(detail::fromUtf8((_base / "*").u8string()).c_str(), &_findData)) != INVALID_HANDLE_VALUE) { + if ((_dirHandle = FindFirstFileW(GHC_NATIVEWP((_base / "*")), &_findData)) != INVALID_HANDLE_VALUE) { if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") { increment(_ec); } else { - _current = _base / std::wstring(_findData.cFileName); + _dir_entry._path = _base / std::wstring(_findData.cFileName); copyToDirEntry(_ec); } } @@ -5257,28 +5632,32 @@ public: if (_dirHandle != INVALID_HANDLE_VALUE) { do { if (FindNextFileW(_dirHandle, &_findData)) { - _current = _base; + _dir_entry._path = _base; +#ifdef GHC_USE_WCHAR_T + _dir_entry._path.append_name(_findData.cFileName); +#else #ifdef GHC_RAISE_UNICODE_ERRORS try { - _current.append_name(detail::toUtf8(_findData.cFileName).c_str()); + _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str()); } - catch(filesystem_error& fe) { + catch (filesystem_error& fe) { ec = fe.code(); return; } #else - _current.append_name(detail::toUtf8(_findData.cFileName).c_str()); + _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str()); +#endif #endif copyToDirEntry(ec); } else { auto err = ::GetLastError(); - if(err != ERROR_NO_MORE_FILES) { + if (err != ERROR_NO_MORE_FILES) { _ec = ec = detail::make_system_error(err); } FindClose(_dirHandle); _dirHandle = INVALID_HANDLE_VALUE; - _current = filesystem::path(); + _dir_entry._path.clear(); break; } } while (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L".."); @@ -5289,12 +5668,11 @@ public: } void copyToDirEntry(std::error_code& ec) { - _dir_entry._path = _current; if (_findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - _dir_entry._status = detail::status_ex(_current, ec, &_dir_entry._symlink_status, &_dir_entry._file_size, nullptr, &_dir_entry._last_write_time); + _dir_entry._status = detail::status_ex(_dir_entry._path, ec, &_dir_entry._symlink_status, &_dir_entry._file_size, nullptr, &_dir_entry._last_write_time); } else { - _dir_entry._status = detail::status_from_INFO(_current, &_findData, ec, &_dir_entry._file_size, &_dir_entry._last_write_time); + _dir_entry._status = detail::status_from_INFO(_dir_entry._path, &_findData, ec, &_dir_entry._file_size, &_dir_entry._last_write_time); _dir_entry._symlink_status = _dir_entry._status; } if (ec) { @@ -5311,7 +5689,6 @@ public: directory_options _options; WIN32_FIND_DATAW _findData; HANDLE _dirHandle; - path _current; directory_entry _dir_entry; std::error_code _ec; }; @@ -5327,13 +5704,11 @@ public: , _entry(nullptr) { if (!path.empty()) { - _dir = ::opendir(path.native().c_str()); - } - if (!path.empty()) { + do { _dir = ::opendir(path.native().c_str()); } while(errno == EINTR); if (!_dir) { auto error = errno; _base = filesystem::path(); - if (error != EACCES || (options & directory_options::skip_permission_denied) == directory_options::none) { + if ((error != EACCES && error != EPERM) || (options & directory_options::skip_permission_denied) == directory_options::none) { _ec = detail::make_system_error(); } } @@ -5356,12 +5731,12 @@ public: do { skip = false; errno = 0; - _entry = ::readdir(_dir); + do { _entry = ::readdir(_dir); } while(errno == EINTR); if (_entry) { - _current = _base; - _current.append_name(_entry->d_name); - _dir_entry = directory_entry(_current, ec); - if(ec && (ec.value() == EACCES || ec.value() == EPERM) && (_options & directory_options::skip_permission_denied) == directory_options::skip_permission_denied) { + _dir_entry._path = _base; + _dir_entry._path.append_name(_entry->d_name); + copyToDirEntry(); + if (ec && (ec.value() == EACCES || ec.value() == EPERM) && (_options & directory_options::skip_permission_denied) == directory_options::skip_permission_denied) { ec.clear(); skip = true; } @@ -5369,8 +5744,8 @@ public: else { ::closedir(_dir); _dir = nullptr; - _current = path(); - if (errno) { + _dir_entry._path.clear(); + if (errno && errno != EINTR) { ec = detail::make_system_error(); } break; @@ -5378,9 +5753,25 @@ public: } while (skip || std::strcmp(_entry->d_name, ".") == 0 || std::strcmp(_entry->d_name, "..") == 0); } } + + void copyToDirEntry() + { + _dir_entry._symlink_status.permissions(perms::unknown); + auto ft = detail::file_type_from_dirent(*_entry); + _dir_entry._symlink_status.type(ft); + if (ft != file_type::symlink) { + _dir_entry._status = _dir_entry._symlink_status; + } + else { + _dir_entry._status.type(file_type::none); + _dir_entry._status.permissions(perms::unknown); + } + _dir_entry._file_size = static_cast(-1); + _dir_entry._hard_link_count = static_cast(-1); + _dir_entry._last_write_time = 0; + } path _base; directory_options _options; - path _current; DIR* _dir; struct ::dirent* _entry; directory_entry _dir_entry; @@ -5388,7 +5779,7 @@ public: }; #endif -// 30.10.13.1 member functions +// [fs.dir.itr.members] member functions GHC_INLINE directory_iterator::directory_iterator() noexcept : _impl(new impl(path(), directory_options::none)) { @@ -5469,7 +5860,7 @@ GHC_INLINE directory_iterator& directory_iterator::operator++() std::error_code ec; _impl->increment(ec); if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_current, ec); + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_entry._path, ec); } return *this; } @@ -5483,15 +5874,15 @@ GHC_INLINE directory_iterator& directory_iterator::increment(std::error_code& ec GHC_INLINE bool directory_iterator::operator==(const directory_iterator& rhs) const { - return _impl->_current == rhs._impl->_current; + return _impl->_dir_entry._path == rhs._impl->_dir_entry._path; } GHC_INLINE bool directory_iterator::operator!=(const directory_iterator& rhs) const { - return _impl->_current != rhs._impl->_current; + return _impl->_dir_entry._path != rhs._impl->_dir_entry._path; } -// 30.10.13.2 directory_iterator non-member functions +// [fs.dir.itr.nonmembers] directory_iterator non-member functions GHC_INLINE directory_iterator begin(directory_iterator iter) noexcept { @@ -5504,7 +5895,7 @@ GHC_INLINE directory_iterator end(const directory_iterator&) noexcept } //----------------------------------------------------------------------------- -// 30.10.14 class recursive_directory_iterator +// [fs.class.rec.dir.itr] class recursive_directory_iterator GHC_INLINE recursive_directory_iterator::recursive_directory_iterator() noexcept : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) @@ -5550,7 +5941,7 @@ GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(recursive_ GHC_INLINE recursive_directory_iterator::~recursive_directory_iterator() {} -// 30.10.14.1 observers +// [fs.rec.dir.itr.members] observers GHC_INLINE directory_options recursive_directory_iterator::options() const { return _impl->_options; @@ -5576,7 +5967,7 @@ GHC_INLINE const directory_entry* recursive_directory_iterator::operator->() con return &(*(_impl->_dir_iter_stack.top())); } -// 30.10.14.1 modifiers recursive_directory_iterator& +// [fs.rec.dir.itr.members] modifiers recursive_directory_iterator& GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(const recursive_directory_iterator& rhs) { _impl = rhs._impl; @@ -5603,26 +5994,29 @@ GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator+ GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment(std::error_code& ec) noexcept { - auto status = (*this)->status(ec); - if (ec) return *this; - auto symlink_status = (*this)->symlink_status(ec); - if (ec) return *this; - if (recursion_pending() && is_directory(status) && (!is_symlink(symlink_status) || (options() & directory_options::follow_directory_symlink) != directory_options::none)) { - _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec)); - } - else { - _impl->_dir_iter_stack.top().increment(ec); + bool isSymLink = (*this)->is_symlink(ec); + bool isDir = !ec && (*this)->is_directory(ec); + if (isSymLink && detail::is_not_found_error(ec)) { + ec.clear(); } if (!ec) { - while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { - _impl->_dir_iter_stack.pop(); + if (recursion_pending() && isDir && (!isSymLink || (options() & directory_options::follow_directory_symlink) != directory_options::none)) { + _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec)); + } + else { _impl->_dir_iter_stack.top().increment(ec); } + if (!ec) { + while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); + } + } + else if (!_impl->_dir_iter_stack.empty()) { + _impl->_dir_iter_stack.pop(); + } + _impl->_recursion_pending = true; } - else if (!_impl->_dir_iter_stack.empty()) { - _impl->_dir_iter_stack.pop(); - } - _impl->_recursion_pending = true; return *this; } @@ -5655,7 +6049,7 @@ GHC_INLINE void recursive_directory_iterator::disable_recursion_pending() _impl->_recursion_pending = false; } -// other members as required by 27.2.3, input iterators +// other members as required by [input.iterators] GHC_INLINE bool recursive_directory_iterator::operator==(const recursive_directory_iterator& rhs) const { return _impl->_dir_iter_stack.top() == rhs._impl->_dir_iter_stack.top(); @@ -5666,7 +6060,7 @@ GHC_INLINE bool recursive_directory_iterator::operator!=(const recursive_directo return _impl->_dir_iter_stack.top() != rhs._impl->_dir_iter_stack.top(); } -// 30.10.14.2 directory_iterator non-member functions +// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions GHC_INLINE recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept { return iter;