From b869b9fc4b30999383ee319c35e1228973cb9086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Sch=C3=BCmann?= Date: Sat, 23 Jan 2021 11:19:30 +0100 Subject: [PATCH 01/11] refs #90, work on switching from generic_format backend to (normalized) native_format backend --- include/ghc/filesystem.hpp | 57 +++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 9bf36f4..c798be7 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -476,11 +476,12 @@ public: iterator end() const; private: - using impl_value_type = std::string::value_type; + using impl_value_type = value_type; //std::string::value_type; using impl_string_type = std::basic_string; friend class directory_iterator; void append_name(const char* name); static constexpr impl_value_type generic_separator = '/'; + static constexpr impl_value_type internal_separator = '/'; template class input_iterator_range { @@ -2237,8 +2238,8 @@ 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] != internal_separator && _path[_path.length() - 1] != ':') { + _path += internal_separator; } return *this; } @@ -2250,7 +2251,7 @@ GHC_INLINE path& path::operator/=(const path& p) assign(root_name()); } else if ((!has_root_directory() && is_absolute()) || has_filename()) { - _path += '/'; + _path += internal_separator; } auto iter = p.begin(); bool first = true; @@ -2258,8 +2259,8 @@ 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] == internal_separator)) { + _path += internal_separator; } first = false; _path += (*iter++).generic_string(); @@ -2613,10 +2614,10 @@ GHC_INLINE int path::compare(const path& p) const noexcept if (iter2 == p._path.end()) { return 1; } - if (*iter1 == '/') { + if (*iter1 == internal_separator) { return -1; } - if (*iter2 == '/') { + if (*iter2 == internal_separator) { return 1; } return *iter1 < *iter2 ? -1 : 1; @@ -2665,8 +2666,8 @@ GHC_INLINE path::string_type::size_type path::root_name_length() const noexcept 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() > 2 && _path[0] == internal_separator && _path[1] == internal_separator && _path[2] != internal_separator && std::isprint(_path[2])) { + impl_string_type::size_type pos = _path.find(internal_separator, 3); if (pos == impl_string_type::npos) { return _path.length(); } @@ -2711,7 +2712,7 @@ GHC_INLINE path path::parent_path() const else { auto piter = end(); auto iter = piter.decrement(_path.end()); - if(iter > _path.begin() + static_cast(rootPathLen) && *iter != '/') { + if (iter > _path.begin() + static_cast(rootPathLen) && *iter != internal_separator) { --iter; } return path(_path.begin(), iter, format::generic_format); @@ -2788,7 +2789,7 @@ 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] == '/'); + return (_path.length() > rootLen && _path[rootLen] == internal_separator); } GHC_INLINE bool path::has_root_path() const @@ -2924,14 +2925,14 @@ GHC_INLINE path::iterator::iterator(const path::impl_string_type::const_iterator 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) == '/') { + if (_last - _first >= 3 && std::toupper(static_cast(*first)) >= 'A' && std::toupper(static_cast(*first)) <= 'Z' && *(first + 1) == ':' && *(first + 2) == internal_separator) { _root = _first + 2; } else #endif { - if (_first != _last && *_first == '/') { - if (_last - _first >= 2 && *(_first + 1) == '/' && !(_last - _first >= 3 && *(_first + 2) == '/')) { + if (_first != _last && *_first == internal_separator) { + if (_last - _first >= 2 && *(_first + 1) == internal_separator && !(_last - _first >= 3 && *(_first + 2) == internal_separator)) { _root = increment(_first); } else { @@ -2950,16 +2951,16 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(cons bool fromStart = i == _first; 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 (*i++ == internal_separator) { + if (i != _last && *i == internal_separator) { + if (fromStart && !(i + 1 != _last && *(i + 1) == internal_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, internal_separator); } else { // skip redundant slashes - while (i != _last && *i == '/') { + while (i != _last && *i == internal_separator) { ++i; } } @@ -2970,7 +2971,7 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(cons ++i; } else { - i = std::find(i, _last, '/'); + i = std::find(i, _last, internal_separator); } } } @@ -2984,7 +2985,7 @@ 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 != internal_separator)) { #ifdef GHC_OS_WINDOWS static const std::string seps = "/:"; i = std::find_first_of(std::reverse_iterator(i), std::reverse_iterator(_first), seps.begin(), seps.end()).base(); @@ -2992,10 +2993,10 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(cons 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), internal_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 == internal_separator && *(_first + 1) == internal_separator) { i -= 2; } } @@ -3005,14 +3006,14 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(cons GHC_INLINE void path::iterator::updateCurrent() { - if ((_iter == _last) || (_iter != _first && _iter != _last && (*_iter == '/' && _iter != _root) && (_iter + 1 == _last))) { + if ((_iter == _last) || (_iter != _first && _iter != _last && (*_iter == internal_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] == '/') { + if (_current.generic_string().size() > 1 && _current.generic_string()[0] == internal_separator && _current.generic_string()[_current.generic_string().size() - 1] == internal_separator) { // shrink successive slashes to one - _current = "/"; + _current._path = internal_separator; } } } @@ -3022,7 +3023,7 @@ 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 == internal_separator && // we are on a separator (_iter + 1) != _last // the slash is not the last char ) { ++_iter; From 8fac7e52542b34574be89a6885aefb8ffaa510ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Sch=C3=BCmann?= Date: Sun, 24 Jan 2021 12:57:58 +0100 Subject: [PATCH 02/11] refs #90, first stage of backend rework done, tests besides namespaced/prefixed long filenames working with char value_type again --- include/ghc/filesystem.hpp | 154 +++++++++++++++++++++++-------------- 1 file changed, 97 insertions(+), 57 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index c798be7..532f751 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -405,8 +405,8 @@ public: 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() + 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; @@ -423,7 +423,7 @@ public: // 30.10.8.4.7 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; @@ -481,7 +481,7 @@ private: friend class directory_iterator; void append_name(const char* name); static constexpr impl_value_type generic_separator = '/'; - static constexpr impl_value_type internal_separator = '/'; + static constexpr impl_value_type internal_separator = preferred_separator; template class input_iterator_range { @@ -1544,6 +1544,7 @@ inline std::string toUtf8(const charT* unicodeString) namespace detail { +//template ::value >> GHC_INLINE bool startsWith(const std::string& what, const std::string& with) { return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin()); @@ -1566,16 +1567,15 @@ GHC_INLINE void path::postprocess_path_with_format(path::impl_string_type& p, pa } #endif switch (fmt) { -#ifndef GHC_OS_WINDOWS - 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: + case path::auto_format: + case path::generic_format: + for (auto& c : p) { + if (c == generic_separator) { + c = internal_separator; + } + } if (p.length() >= 4 && p[2] == '?') { if (p.length() == 4 || (p.length() >= 6 && p[5] == ':')) { if (detail::startsWith(p, std::string("\\\\?\\"))) { @@ -1587,20 +1587,21 @@ GHC_INLINE void path::postprocess_path_with_format(path::impl_string_type& p, pa } } } - for (auto& c : p) { - if (c == '\\') { - c = '/'; - } - } + break; +#else + case path::auto_format: + case path::native_format: + case path::generic_format: + // nothing to do 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 == '/'; }); + if (p.length() > 2 && p[0] == internal_separator && p[1] == internal_separator && p[2] != internal_separator) { + std::string::iterator new_end = std::unique(p.begin() + 2, p.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == internal_separator; }); p.erase(new_end, p.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 == '/'; }); + std::string::iterator new_end = std::unique(p.begin(), p.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == internal_separator; }); p.erase(new_end, p.end()); } } @@ -2263,7 +2264,7 @@ GHC_INLINE path& path::operator/=(const path& p) _path += internal_separator; } first = false; - _path += (*iter++).generic_string(); + _path += (*iter++).string(); } return *this; } @@ -2274,8 +2275,8 @@ GHC_INLINE void path::append_name(const char* name) this->operator/=(path(name)); } else { - if (_path.back() != path::generic_separator) { - _path.push_back(path::generic_separator); + if (_path.back() != path::internal_separator) { + _path.push_back(path::internal_separator); } _path += name; } @@ -2338,11 +2339,11 @@ GHC_INLINE path& path::operator+=(const value_type* x) GHC_INLINE path& path::operator+=(value_type x) { #ifdef GHC_OS_WINDOWS - if (x == '\\') { - x = generic_separator; + if (x == generic_separator) { + x = internal_separator; } #endif - if (_path.empty() || _path.back() != generic_separator) { + if (_path.empty() || _path.back() != internal_separator) { #ifdef GHC_USE_WCHAR_T _path += detail::toUtf8(string_type(1, x)); #else @@ -2372,8 +2373,8 @@ 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(p._path, native_format); return *this; } template @@ -2435,6 +2436,8 @@ GHC_INLINE void path::swap(path& rhs) noexcept #ifdef GHC_OS_WINDOWS GHC_INLINE path::impl_string_type path::native_impl() const { + return _path; +#if 0 impl_string_type result; if (is_absolute() && _path.length() > MAX_PATH - 10) { // expand absolute non namespaced long Windows filenames with marker @@ -2457,6 +2460,7 @@ GHC_INLINE path::impl_string_type path::native_impl() const } } return result; +#endif } #else GHC_INLINE const path::impl_string_type& path::native_impl() const @@ -2465,21 +2469,12 @@ GHC_INLINE const path::impl_string_type& path::native_impl() const } #endif -GHC_INLINE const path::string_type& path::native() const +GHC_INLINE const path::string_type& path::native() const noexcept { -#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(); } @@ -2494,14 +2489,18 @@ 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); + return detail::fromUtf8>(_path, a); } #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 @@ -2516,23 +2515,33 @@ GHC_INLINE std::wstring path::wstring() const #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) GHC_INLINE std::u8string path::u8string() const { - return std::u8string(reinterpret_cast(native_impl().c_str())); +#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 @@ -2542,41 +2551,75 @@ GHC_INLINE std::u32string path::u32string() const template inline std::basic_string path::generic_string(const Allocator& a) const { - return detail::fromUtf8>(_path, a); +#ifdef GHC_OS_WINDOWS + auto result = detail::fromUtf8>(_path, a); + for (auto& c : result) { + if (c == internal_separator) { + c = generic_separator; + } + } + return result; +#else + return _path: +#endif } #ifdef GHC_EXPAND_IMPL -GHC_INLINE const std::string& path::generic_string() const +GHC_INLINE std::string path::generic_string() const { - return _path; +#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 { - return _path; +#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 } //----------------------------------------------------------------------------- @@ -2629,11 +2672,7 @@ GHC_INLINE int path::compare(const path& p) const noexcept 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 @@ -2686,7 +2725,8 @@ GHC_INLINE path path::root_name() const GHC_INLINE path path::root_directory() const { if(has_root_directory()) { - return path("/", generic_format); + static const path _root_dir(std::string(1, internal_separator), generic_format); + return _root_dir; } return path(); } @@ -2854,7 +2894,7 @@ GHC_INLINE path path::lexically_normal() const continue; } else if (*(--dest.end()) != "..") { - if (dest._path.back() == generic_separator) { + if (dest._path.back() == internal_separator) { dest._path.pop_back(); } dest.remove_filename(); @@ -2987,7 +3027,7 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(cons // else check for network name if (i != _root && (pos != _last || *i != internal_separator)) { #ifdef GHC_OS_WINDOWS - static const std::string seps = "/:"; + static const std::string seps = "\\:"; i = std::find_first_of(std::reverse_iterator(i), std::reverse_iterator(_first), seps.begin(), seps.end()).base(); if (i > _first && *i == ':') { i++; From c96b0059c33d896bd40bdc57cbae0ba4806a89a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Sch=C3=BCmann?= Date: Sun, 31 Jan 2021 11:39:48 +0100 Subject: [PATCH 03/11] refs #90, native path backend - stage two, prefix handling repaired and configurable, all tests working on Windows (wchar_t backend will be stage 3) --- include/ghc/filesystem.hpp | 254 ++++++++++++++++++++----------------- test/filesystem_test.cpp | 5 +- 2 files changed, 138 insertions(+), 121 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 532f751..eb7363d 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -234,9 +234,15 @@ // 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 10401L +#define GHC_FILESYSTEM_VERSION 10499L #if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)) #define GHC_WITH_EXCEPTIONS @@ -319,6 +325,10 @@ public: struct _is_basic_string> : std::true_type { }; + template + struct _is_basic_string, std::allocator>> : std::true_type + { + }; #ifdef __cpp_lib_string_view template struct _is_basic_string> : std::true_type @@ -505,15 +515,21 @@ 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); 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); impl_string_type _path; #ifdef GHC_OS_WINDOWS + void handle_prefixes(); + void check_long_path(); 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 }; @@ -576,7 +592,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--(); @@ -593,6 +609,7 @@ private: 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; @@ -1544,25 +1561,35 @@ inline std::string toUtf8(const charT* unicodeString) namespace detail { -//template ::value >> -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() +{ +#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH + if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type("\\\\?\\"))) { + 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 @@ -1571,22 +1598,17 @@ GHC_INLINE void path::postprocess_path_with_format(path::impl_string_type& p, pa case path::native_format: case path::auto_format: case path::generic_format: - for (auto& c : p) { + for (auto& c : _path) { if (c == generic_separator) { c = internal_separator; } } - if (p.length() >= 4 && p[2] == '?') { - if (p.length() == 4 || (p.length() >= 6 && p[5] == ':')) { - if (detail::startsWith(p, std::string("\\\\?\\"))) { - // remove Windows long filename marker for simple paths - p.erase(0, 4); - } - else if (detail::startsWith(p, std::string("\\??\\"))) { - p.erase(0, 4); - } - } +#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH + if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type("\\\\?\\"))) { + _path = "\\\\?\\" + _path; } +#endif + handle_prefixes(); break; #else case path::auto_format: @@ -1596,13 +1618,13 @@ GHC_INLINE void path::postprocess_path_with_format(path::impl_string_type& p, pa break; #endif } - if (p.length() > 2 && p[0] == internal_separator && p[1] == internal_separator && p[2] != internal_separator) { - std::string::iterator new_end = std::unique(p.begin() + 2, p.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == internal_separator; }); - p.erase(new_end, p.end()); + if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == internal_separator && _path[_prefixLength + 1] == internal_separator && _path[_prefixLength + 2] != internal_separator) { + std::string::iterator new_end = std::unique(_path.begin() + _prefixLength + 2, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == internal_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 == internal_separator; }); - p.erase(new_end, p.end()); + std::string::iterator new_end = std::unique(_path.begin() + _prefixLength, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == internal_separator; }); + _path.erase(new_end, _path.end()); } } @@ -1612,7 +1634,7 @@ template inline path::path(const Source& source, format fmt) : _path(detail::toUtf8(source)) { - postprocess_path_with_format(_path, fmt); + postprocess_path_with_format(fmt); } template @@ -1751,6 +1773,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) { @@ -1864,12 +1901,20 @@ GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) { if (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)); break; @@ -2122,11 +2167,13 @@ GHC_INLINE path::path() noexcept {} GHC_INLINE path::path(const path& p) : _path(p._path) + , _prefixLength(p._prefixLength) { } GHC_INLINE path::path(path&& p) noexcept : _path(std::move(p._path)) + , _prefixLength(p._prefixLength) { } @@ -2137,7 +2184,7 @@ GHC_INLINE path::path(string_type&& source, format fmt) : _path(std::move(source)) #endif { - postprocess_path_with_format(_path, fmt); + postprocess_path_with_format(fmt); } #endif // GHC_EXPAND_IMPL @@ -2174,12 +2221,14 @@ GHC_INLINE path::~path() {} GHC_INLINE path& path::operator=(const path& p) { _path = p._path; + _prefixLength = p._prefixLength; return *this; } GHC_INLINE path& path::operator=(path&& p) noexcept { _path = std::move(p._path); + _prefixLength = p._prefixLength; return *this; } @@ -2195,7 +2244,7 @@ GHC_INLINE path& path::assign(path::string_type&& source) #else _path = std::move(source); #endif - postprocess_path_with_format(_path, native_format); + postprocess_path_with_format(native_format); return *this; } @@ -2211,7 +2260,7 @@ template inline path& path::assign(const Source& source) { _path.assign(detail::toUtf8(source)); - postprocess_path_with_format(_path, native_format); + postprocess_path_with_format(native_format); return *this; } @@ -2219,6 +2268,7 @@ template <> inline path& path::assign(const path& source) { _path = source._path; + _prefixLength = source._prefixLength; return *this; } @@ -2226,7 +2276,7 @@ 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; } @@ -2266,6 +2316,7 @@ GHC_INLINE path& path::operator/=(const path& p) first = false; _path += (*iter++).string(); } + check_long_path(); return *this; } @@ -2279,6 +2330,7 @@ GHC_INLINE void path::append_name(const char* name) _path.push_back(path::internal_separator); } _path += name; + check_long_path(); } } @@ -2350,6 +2402,7 @@ GHC_INLINE path& path::operator+=(value_type x) _path += x; #endif } + check_long_path(); return *this; } @@ -2374,14 +2427,14 @@ inline path& path::concat(const Source& x) { path p(x); _path += p._path; - postprocess_path_with_format(p._path, native_format); + 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; } @@ -2392,6 +2445,7 @@ inline path& path::concat(InputIterator first, InputIterator last) GHC_INLINE void path::clear() noexcept { _path.clear(); + _prefixLength = 0; } GHC_INLINE path& path::make_preferred() @@ -2429,46 +2483,11 @@ GHC_INLINE path& path::replace_extension(const path& replacement) GHC_INLINE void path::swap(path& rhs) noexcept { _path.swap(rhs._path); + std::swap(_prefixLength, rhs._prefixLength); } //----------------------------------------------------------------------------- // 30.10.8.4.6, native format observers -#ifdef GHC_OS_WINDOWS -GHC_INLINE path::impl_string_type path::native_impl() const -{ - return _path; -#if 0 - impl_string_type result; - if (is_absolute() && _path.length() > MAX_PATH - 10) { - // expand absolute non namespaced long Windows filenames with marker - if (has_root_name() && _path[0] == '/' && _path[1] != '/') { - result = "\\\\?\\" + _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; -#endif -} -#else -GHC_INLINE const path::impl_string_type& path::native_impl() const -{ - return _path; -} -#endif - GHC_INLINE const path::string_type& path::native() const noexcept { return _path; @@ -2698,15 +2717,27 @@ GHC_INLINE int path::compare(const value_type* s) const //----------------------------------------------------------------------------- // 30.10.8.4.9, decomposition +GHC_INLINE void path::handle_prefixes() +{ +#if defined(GHC_OS_WINDOWS) && 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("\\\\?\\")) || detail::startsWith(_path, impl_string_type("\\??\\"))) { + _prefixLength = 4; + } + } +#endif // GHC_OS_WINDOWS +} + 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] == internal_separator && _path[1] == internal_separator && _path[2] != internal_separator && std::isprint(_path[2])) { - impl_string_type::size_type pos = _path.find(internal_separator, 3); + if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == internal_separator && _path[_prefixLength + 1] == internal_separator && _path[_prefixLength + 2] != internal_separator && std::isprint(_path[_prefixLength + 2])) { + impl_string_type::size_type pos = _path.find(internal_separator, _prefixLength + 3); if (pos == impl_string_type::npos) { return _path.length(); } @@ -2719,13 +2750,13 @@ 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()) { - static const path _root_dir(std::string(1, internal_separator), generic_format); + static const path _root_dir(std::string(1, internal_separator), native_format); return _root_dir; } return path(); @@ -2733,18 +2764,18 @@ GHC_INLINE path path::root_directory() const 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 { - auto rootPathLen = root_name_length() + (has_root_directory() ? 1 : 0); + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); if(rootPathLen < _path.length()) { if (empty() || begin() == --end()) { return path(); @@ -2755,7 +2786,7 @@ GHC_INLINE path path::parent_path() const if (iter > _path.begin() + static_cast(rootPathLen) && *iter != internal_separator) { --iter; } - return path(_path.begin(), iter, format::generic_format); + return path(_path.begin(), iter, native_format); } } else { @@ -2765,7 +2796,7 @@ 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 @@ -2774,10 +2805,10 @@ GHC_INLINE path path::stem() const 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 @@ -2787,7 +2818,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(); @@ -2828,7 +2859,7 @@ GHC_INLINE bool path::has_root_name() const GHC_INLINE bool path::has_root_directory() const { - auto rootLen = root_name_length(); + auto rootLen = _prefixLength + root_name_length(); return (_path.length() > rootLen && _path[rootLen] == internal_separator); } @@ -2839,7 +2870,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(); } @@ -2957,41 +2988,25 @@ GHC_INLINE path path::lexically_proximate(const path& base) const // 30.10.8.5, 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()) , _iter(pos) + , _prefix(_first + p._prefixLength) + , _root(p.has_root_directory() ? _first + p._prefixLength + p.root_name_length() : _last) { 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) == internal_separator) { - _root = _first + 2; - } - else -#endif - { - if (_first != _last && *_first == internal_separator) { - if (_last - _first >= 2 && *(_first + 1) == internal_separator && !(_last - _first >= 3 && *(_first + 2) == internal_separator)) { - _root = increment(_first); - } - else { - _root = _first; - } - } - else { - _root = _last; - } - } } 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++ == internal_separator) { + if (fromStart && i == _first && _prefix > _first) { + i = _prefix; + } else if (*i++ == internal_separator) { + // we can only sit on a slash if it is a network name or a root if (i != _last && *i == internal_separator) { if (fromStart && !(i + 1 != _last && *(i + 1) == internal_separator)) { // leadind double slashes detected, treat this and the @@ -3115,12 +3130,12 @@ 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()); } //----------------------------------------------------------------------------- @@ -3371,7 +3386,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); @@ -3384,6 +3398,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) { @@ -3394,7 +3409,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; } @@ -3416,6 +3431,7 @@ GHC_INLINE path canonical(const path& p, std::error_code& ec) result /= target; continue; } + } else { result /= pe; diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index eb9e879..5fb009b 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -2797,8 +2797,8 @@ TEST_CASE("Windows: path namespace handling", "[filesystem][path][fs.path.win.na {R"(\\?\C:\Windows\notepad.exe)", R"(\\?\C:\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,C:,Windows,notepad.exe"}, {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "\\??", "\\??\\", "/??,/,C:,Windows,notepad.exe"}, #else - {R"(\\?\C:\Windows\notepad.exe)", R"(C:\Windows\notepad.exe)", "C:", "C:\\", "C:,/,Windows,notepad.exe"}, - {R"(\??\C:\Windows\notepad.exe)", R"(C:\Windows\notepad.exe)", "C:", "C:\\", "C:,/,Windows,notepad.exe"}, + {R"(\\?\C:\Windows\notepad.exe)", R"(\\?\C:\Windows\notepad.exe)", "C:", "C:\\", "//?/,C:,/,Windows,notepad.exe"}, + {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "C:", "C:\\", "/??/,C:,/,Windows,notepad.exe"}, #endif {R"(\\.\C:\Windows\notepad.exe)", R"(\\.\C:\Windows\notepad.exe)", "\\\\.", "\\\\.\\", "//.,/,C:,Windows,notepad.exe"}, {R"(\\?\HarddiskVolume1\Windows\notepad.exe)", R"(\\?\HarddiskVolume1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,HarddiskVolume1,Windows,notepad.exe"}, @@ -2815,6 +2815,7 @@ TEST_CASE("Windows: path namespace handling", "[filesystem][path][fs.path.win.na INFO("Used path: " + ti._path); auto p = fs::path(ti._path); CHECK(p.string() == ti._string); + CHECK(p.is_absolute()); CHECK(p.root_name().string() == ti._rootName); CHECK(p.root_path().string() == ti._rootPath); CHECK(iterateResult(p) == ti._iterateResult); From a7abc2ad4aaaf8afcb0244ec7e70108a9cc3a2b0 Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Sun, 31 Jan 2021 12:03:17 +0100 Subject: [PATCH 04/11] refs #90, fixing stuff broken on POSIX side during backend rework. --- include/ghc/filesystem.hpp | 163 +++++++++++++++++++++---------------- 1 file changed, 92 insertions(+), 71 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index eb7363d..edfb917 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -77,17 +77,17 @@ #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 +#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 +#if __cplusplus == 201703L +#define GHC_FILESYSTEM_RUNNING_CPP17 +#else +#define GHC_FILESYSTEM_RUNNING_CPP20 +#endif #endif #endif @@ -130,12 +130,12 @@ #else #include #include +#include #include #include #include #include #include -#include #ifdef GHC_OS_ANDROID #include #if __ANDROID_API__ < 12 @@ -238,7 +238,7 @@ // 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 +#endif // GHC_WIN_DISABLE_AUTO_PREFIXES //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) @@ -264,7 +264,7 @@ public: } }; -template +template class path_helper_base { public: @@ -276,12 +276,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 { @@ -289,8 +288,7 @@ bool has_executable_extension(const path& p); } #endif - - // 30.10.8 class path +// 30.10.8 class path class GHC_FS_API_CLASS path #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_WSTRING_STRING_TYPE) #define GHC_USE_WCHAR_T @@ -306,7 +304,7 @@ 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. enum format { @@ -486,7 +484,7 @@ public: iterator end() const; private: - using impl_value_type = value_type; //std::string::value_type; + using impl_value_type = value_type; // std::string::value_type; using impl_string_type = std::basic_string; friend class directory_iterator; void append_name(const char* name); @@ -518,14 +516,14 @@ private: friend path canonical(const path& p, std::error_code& ec); string_type::size_type root_name_length() const noexcept; void postprocess_path_with_format(format fmt); + void check_long_path(); impl_string_type _path; #ifdef GHC_OS_WINDOWS void handle_prefixes(); - void check_long_path(); friend bool detail::has_executable_extension(const path& p); #ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH - string_type::size_type _prefixLength{0 }; -#else // 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 @@ -537,7 +535,7 @@ private: 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; +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; @@ -1269,7 +1267,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 @@ -1392,7 +1390,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(); @@ -1410,9 +1408,9 @@ GHC_INLINE bool validUtf8(const std::string& utf8String) } } // namespace detail - + #endif - + namespace detail { template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 1)>::type* = nullptr> @@ -1494,13 +1492,13 @@ inline StringType fromUtf8(const Utf8String& utf8String, const typename StringTy return result; } -template +template inline StringType fromUtf8(const charT (&utf8String)[N]) { #ifdef __cpp_lib_string_view - return fromUtf8(std::basic_string_view(utf8String, N-1)); + return fromUtf8(std::basic_string_view(utf8String, N - 1)); #else - return fromUtf8(std::basic_string(utf8String, N-1)); + return fromUtf8(std::basic_string(utf8String, N - 1)); #endif } @@ -1526,7 +1524,7 @@ inline std::string toUtf8(const strT& unicodeString) 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 @@ -1577,7 +1575,7 @@ GHC_INLINE bool endsWith(const strT& what, const strT& with) GHC_INLINE void path::check_long_path() { -#ifdef GHC_WIN_AUTO_PREFIX_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("\\\\?\\"))) { postprocess_path_with_format(native_format); } @@ -1587,7 +1585,7 @@ GHC_INLINE void path::check_long_path() GHC_INLINE void path::postprocess_path_with_format(path::format fmt) { #ifdef GHC_RAISE_UNICODE_ERRORS - if(!detail::validUtf8(_path)) { + if (!detail::validUtf8(_path)) { path t; t._path = _path; throw filesystem_error("Illegal byte sequence for unicode character.", t, std::make_error_code(std::errc::illegal_byte_sequence)); @@ -1678,14 +1676,16 @@ GHC_INLINE bool equals_simple_insensitive(const char* str1, const char* str2) GHC_INLINE int compare_simple_insensitive(const char* str1, size_t len1, const char* 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((unsigned char)*str1) == ::tolower((unsigned char)*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; @@ -1698,7 +1698,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; @@ -1903,7 +1903,8 @@ GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) switch (reparseData->ReparseTag) { 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)); + 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; } @@ -2167,13 +2168,17 @@ 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 { } @@ -2221,14 +2226,18 @@ GHC_INLINE path::~path() {} 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; } @@ -2268,7 +2277,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; } @@ -2445,7 +2456,9 @@ inline path& path::concat(InputIterator first, InputIterator last) 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() @@ -2483,7 +2496,9 @@ 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 } //----------------------------------------------------------------------------- @@ -2579,7 +2594,7 @@ inline std::basic_string path::generic_string(const A } return result; #else - return _path: + return detail::fromUtf8>(_path, a); #endif } @@ -2590,7 +2605,7 @@ GHC_INLINE std::string path::generic_string() const #ifdef GHC_OS_WINDOWS return generic_string(); #else - return _path: + return _path; #endif } @@ -2618,7 +2633,7 @@ GHC_INLINE std::string path::generic_u8string() const #ifdef GHC_OS_WINDOWS return generic_string(); #else - return _path: + return _path; #endif } #endif @@ -2683,12 +2698,12 @@ GHC_INLINE int path::compare(const path& p) const noexcept 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; } return _path.compare(rnl1, std::string::npos, p._path, rnl2, std::string::npos); @@ -2717,17 +2732,19 @@ GHC_INLINE int path::compare(const value_type* s) const //----------------------------------------------------------------------------- // 30.10.8.4.9, decomposition +#ifdef GHC_OS_WINDOWS GHC_INLINE void path::handle_prefixes() { -#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) +#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("\\\\?\\")) || detail::startsWith(_path, impl_string_type("\\??\\"))) { + if (detail::startsWith(_path, impl_string_type("\\\\?\\")) || detail::startsWith(_path, impl_string_type("\\??\\"))) { _prefixLength = 4; } } -#endif // GHC_OS_WINDOWS +#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH } +#endif GHC_INLINE path::string_type::size_type path::root_name_length() const noexcept { @@ -2755,7 +2772,7 @@ GHC_INLINE path path::root_name() const GHC_INLINE path path::root_directory() const { - if(has_root_directory()) { + if (has_root_directory()) { static const path _root_dir(std::string(1, internal_separator), native_format); return _root_dir; } @@ -2776,7 +2793,7 @@ GHC_INLINE path path::relative_path() const GHC_INLINE path path::parent_path() const { auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); - if(rootPathLen < _path.length()) { + if (rootPathLen < _path.length()) { if (empty() || begin() == --end()) { return path(); } @@ -2818,7 +2835,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), native_format); + return path(fn._path.substr(pos), native_format); } } return path(); @@ -3005,7 +3022,8 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(cons if (i != _last) { if (fromStart && i == _first && _prefix > _first) { i = _prefix; - } else if (*i++ == internal_separator) { + } + else if (*i++ == internal_separator) { // we can only sit on a slash if it is a network name or a root if (i != _last && *i == internal_separator) { if (fromStart && !(i + 1 != _last && *(i + 1) == internal_separator)) { @@ -3076,10 +3094,10 @@ GHC_INLINE void path::iterator::updateCurrent() 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 + while (_iter != _last && // we didn't reach the end + _iter != _root && // this is not a root position *_iter == internal_separator && // we are on a separator - (_iter + 1) != _last // the slash is not the last char + (_iter + 1) != _last // the slash is not the last char ) { ++_iter; } @@ -3151,7 +3169,7 @@ GHC_INLINE size_t hash_value(const path& p) noexcept } #ifdef GHC_HAS_THREEWAY_COMP -GHC_INLINE std::strong_ordering operator<=>( const path& lhs, const path& rhs ) noexcept +GHC_INLINE std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept { return lhs.compare(rhs) <=> 0; } @@ -3187,7 +3205,6 @@ GHC_INLINE bool operator>=(const path& lhs, const path& rhs) noexcept return lhs.compare(rhs) >= 0; } - GHC_INLINE path operator/(const path& lhs, const path& rhs) { path result(lhs); @@ -3431,7 +3448,6 @@ GHC_INLINE path canonical(const path& p, std::error_code& ec) result /= target; continue; } - } else { result /= pe; @@ -3698,7 +3714,8 @@ GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept std::error_code tmp_ec; if (is_directory(current, tmp_ec)) { ec.clear(); - } else { + } + else { return false; } } @@ -4286,7 +4303,7 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err 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; + 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(); } @@ -4379,7 +4396,7 @@ GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::e GHC_INLINE path proximate(const path& p, std::error_code& ec) { auto cp = current_path(ec); - if(!ec) { + if (!ec) { return proximate(p, cp, ec); } return path(); @@ -4515,9 +4532,11 @@ GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept break; } bool is_symlink_result = iter->is_symlink(ec); - if (ec) return static_cast(-1); + if (ec) + return static_cast(-1); bool is_directory_result = iter->is_directory(ec); - if (ec) return static_cast(-1); + if (ec) + return static_cast(-1); if (!is_symlink_result && is_directory_result) { count += remove_all(iter->path(), ec); if (ec) { @@ -4590,7 +4609,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 @@ -4628,9 +4647,9 @@ 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}}; + 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)) { ec = detail::make_system_error(); return {static_cast(-1), static_cast(-1), static_cast(-1)}; @@ -5228,7 +5247,7 @@ public: try { _current.append_name(detail::toUtf8(_findData.cFileName).c_str()); } - catch(filesystem_error& fe) { + catch (filesystem_error& fe) { ec = fe.code(); return; } @@ -5239,7 +5258,7 @@ public: } 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); @@ -5325,7 +5344,7 @@ public: _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) { + if (ec && (ec.value() == EACCES || ec.value() == EPERM) && (_options & directory_options::skip_permission_denied) == directory_options::skip_permission_denied) { ec.clear(); skip = true; } @@ -5568,9 +5587,11 @@ 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; + if (ec) + return *this; auto symlink_status = (*this)->symlink_status(ec); - if (ec) return *this; + 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)); } From 4944a87e18c1b2d7d2b257156b51576db359f0e4 Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Sun, 31 Jan 2021 13:04:14 +0100 Subject: [PATCH 05/11] refs #90, additional compile issue fixes --- include/ghc/filesystem.hpp | 4 ++-- test/filesystem_test.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index edfb917..ddf91d5 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -3008,9 +3008,9 @@ GHC_INLINE path::iterator::iterator() {} GHC_INLINE path::iterator::iterator(const path& p, const impl_string_type::const_iterator& pos) : _first(p._path.begin()) , _last(p._path.end()) - , _iter(pos) , _prefix(_first + p._prefixLength) - , _root(p.has_root_directory() ? _first + p._prefixLength + p.root_name_length() : _last) + , _root(p.has_root_directory() ? _first + static_cast(p._prefixLength + p.root_name_length()) : _last) + , _iter(pos) { updateCurrent(); } diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index 5fb009b..0a52771 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -2798,7 +2798,7 @@ TEST_CASE("Windows: path namespace handling", "[filesystem][path][fs.path.win.na {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "\\??", "\\??\\", "/??,/,C:,Windows,notepad.exe"}, #else {R"(\\?\C:\Windows\notepad.exe)", R"(\\?\C:\Windows\notepad.exe)", "C:", "C:\\", "//?/,C:,/,Windows,notepad.exe"}, - {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "C:", "C:\\", "/??/,C:,/,Windows,notepad.exe"}, + {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "C:", "C:\\", "/?\?/,C:,/,Windows,notepad.exe"}, #endif {R"(\\.\C:\Windows\notepad.exe)", R"(\\.\C:\Windows\notepad.exe)", "\\\\.", "\\\\.\\", "//.,/,C:,Windows,notepad.exe"}, {R"(\\?\HarddiskVolume1\Windows\notepad.exe)", R"(\\?\HarddiskVolume1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,HarddiskVolume1,Windows,notepad.exe"}, From 3eddea2ea8f71c4ddfc4a1976a557d404d50b673 Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Sun, 31 Jan 2021 13:36:50 +0100 Subject: [PATCH 06/11] refs #90, switch from internal_separator to preferred_separator use after refactoring --- include/ghc/filesystem.hpp | 71 +++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index ddf91d5..5a28840 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -489,7 +489,6 @@ private: friend class directory_iterator; void append_name(const char* name); static constexpr impl_value_type generic_separator = '/'; - static constexpr impl_value_type internal_separator = preferred_separator; template class input_iterator_range { @@ -1598,7 +1597,7 @@ GHC_INLINE void path::postprocess_path_with_format(path::format fmt) case path::generic_format: for (auto& c : _path) { if (c == generic_separator) { - c = internal_separator; + c = preferred_separator; } } #ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH @@ -1616,12 +1615,12 @@ GHC_INLINE void path::postprocess_path_with_format(path::format fmt) break; #endif } - if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == internal_separator && _path[_prefixLength + 1] == internal_separator && _path[_prefixLength + 2] != internal_separator) { - std::string::iterator new_end = std::unique(_path.begin() + _prefixLength + 2, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == internal_separator; }); + if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator) { + std::string::iterator new_end = std::unique(_path.begin() + _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(_path.begin() + _prefixLength, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == internal_separator; }); + std::string::iterator new_end = std::unique(_path.begin() + _prefixLength, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); _path.erase(new_end, _path.end()); } } @@ -2300,8 +2299,8 @@ 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] != internal_separator && _path[_path.length() - 1] != ':') { - _path += internal_separator; + if (!_path.empty() && _path[_path.length() - 1] != preferred_separator && _path[_path.length() - 1] != ':') { + _path += preferred_separator; } return *this; } @@ -2313,7 +2312,7 @@ GHC_INLINE path& path::operator/=(const path& p) assign(root_name()); } else if ((!has_root_directory() && is_absolute()) || has_filename()) { - _path += internal_separator; + _path += preferred_separator; } auto iter = p.begin(); bool first = true; @@ -2321,8 +2320,8 @@ GHC_INLINE path& path::operator/=(const path& p) ++iter; } while (iter != p.end()) { - if (!first && !(!_path.empty() && _path[_path.length() - 1] == internal_separator)) { - _path += internal_separator; + if (!first && !(!_path.empty() && _path[_path.length() - 1] == preferred_separator)) { + _path += preferred_separator; } first = false; _path += (*iter++).string(); @@ -2337,8 +2336,8 @@ GHC_INLINE void path::append_name(const char* name) this->operator/=(path(name)); } else { - if (_path.back() != path::internal_separator) { - _path.push_back(path::internal_separator); + if (_path.back() != path::preferred_separator) { + _path.push_back(path::preferred_separator); } _path += name; check_long_path(); @@ -2403,10 +2402,10 @@ GHC_INLINE path& path::operator+=(value_type x) { #ifdef GHC_OS_WINDOWS if (x == generic_separator) { - x = internal_separator; + x = preferred_separator; } #endif - if (_path.empty() || _path.back() != internal_separator) { + if (_path.empty() || _path.back() != preferred_separator) { #ifdef GHC_USE_WCHAR_T _path += detail::toUtf8(string_type(1, x)); #else @@ -2588,7 +2587,7 @@ inline std::basic_string path::generic_string(const A #ifdef GHC_OS_WINDOWS auto result = detail::fromUtf8>(_path, a); for (auto& c : result) { - if (c == internal_separator) { + if (c == preferred_separator) { c = generic_separator; } } @@ -2691,10 +2690,10 @@ GHC_INLINE int path::compare(const path& p) const noexcept if (iter2 == p._path.end()) { return 1; } - if (*iter1 == internal_separator) { + if (*iter1 == preferred_separator) { return -1; } - if (*iter2 == internal_separator) { + if (*iter2 == preferred_separator) { return 1; } return *iter1 < *iter2 ? -1 : 1; @@ -2753,8 +2752,8 @@ GHC_INLINE path::string_type::size_type path::root_name_length() const noexcept return 2; } #endif - if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == internal_separator && _path[_prefixLength + 1] == internal_separator && _path[_prefixLength + 2] != internal_separator && std::isprint(_path[_prefixLength + 2])) { - impl_string_type::size_type pos = _path.find(internal_separator, _prefixLength + 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(); } @@ -2773,7 +2772,7 @@ GHC_INLINE path path::root_name() const GHC_INLINE path path::root_directory() const { if (has_root_directory()) { - static const path _root_dir(std::string(1, internal_separator), native_format); + static const path _root_dir(std::string(1, preferred_separator), native_format); return _root_dir; } return path(); @@ -2800,7 +2799,7 @@ GHC_INLINE path path::parent_path() const else { auto piter = end(); auto iter = piter.decrement(_path.end()); - if (iter > _path.begin() + static_cast(rootPathLen) && *iter != internal_separator) { + if (iter > _path.begin() + static_cast(rootPathLen) && *iter != preferred_separator) { --iter; } return path(_path.begin(), iter, native_format); @@ -2877,7 +2876,7 @@ GHC_INLINE bool path::has_root_name() const GHC_INLINE bool path::has_root_directory() const { auto rootLen = _prefixLength + root_name_length(); - return (_path.length() > rootLen && _path[rootLen] == internal_separator); + return (_path.length() > rootLen && _path[rootLen] == preferred_separator); } GHC_INLINE bool path::has_root_path() const @@ -2942,7 +2941,7 @@ GHC_INLINE path path::lexically_normal() const continue; } else if (*(--dest.end()) != "..") { - if (dest._path.back() == internal_separator) { + if (dest._path.back() == preferred_separator) { dest._path.pop_back(); } dest.remove_filename(); @@ -3023,17 +3022,17 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(cons if (fromStart && i == _first && _prefix > _first) { i = _prefix; } - else if (*i++ == internal_separator) { + 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 == internal_separator) { - if (fromStart && !(i + 1 != _last && *(i + 1) == internal_separator)) { + 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, internal_separator); + i = std::find(++i, _last, preferred_separator); } else { // skip redundant slashes - while (i != _last && *i == internal_separator) { + while (i != _last && *i == preferred_separator) { ++i; } } @@ -3044,7 +3043,7 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(cons ++i; } else { - i = std::find(i, _last, internal_separator); + i = std::find(i, _last, preferred_separator); } } } @@ -3058,7 +3057,7 @@ 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 != internal_separator)) { + if (i != _root && (pos != _last || *i != preferred_separator)) { #ifdef GHC_OS_WINDOWS static const std::string seps = "\\:"; i = std::find_first_of(std::reverse_iterator(i), std::reverse_iterator(_first), seps.begin(), seps.end()).base(); @@ -3066,10 +3065,10 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(cons i++; } #else - i = std::find(std::reverse_iterator(i), std::reverse_iterator(_first), internal_separator).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 == internal_separator && *(_first + 1) == internal_separator) { + if (i - _first == 2 && *_first == preferred_separator && *(_first + 1) == preferred_separator) { i -= 2; } } @@ -3079,14 +3078,14 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(cons GHC_INLINE void path::iterator::updateCurrent() { - if ((_iter == _last) || (_iter != _first && _iter != _last && (*_iter == internal_separator && _iter != _root) && (_iter + 1 == _last))) { + 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] == internal_separator && _current.generic_string()[_current.generic_string().size() - 1] == internal_separator) { + if (_current.generic_string().size() > 1 && _current.generic_string()[0] == preferred_separator && _current.generic_string()[_current.generic_string().size() - 1] == preferred_separator) { // shrink successive slashes to one - _current._path = internal_separator; + _current._path = preferred_separator; } } } @@ -3096,7 +3095,7 @@ 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 == internal_separator && // we are on a separator + *_iter == preferred_separator && // we are on a separator (_iter + 1) != _last // the slash is not the last char ) { ++_iter; From 6699f6a3daa12d4212e62b6c58768d8cbbe8cea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Sch=C3=BCmann?= Date: Mon, 1 Feb 2021 00:34:09 +0100 Subject: [PATCH 07/11] refs #90, initial support of wstring as backend storage of path --- include/ghc/filesystem.hpp | 133 ++++++++++++++++++++++++++----------- 1 file changed, 95 insertions(+), 38 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 5a28840..b8f4958 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -292,11 +292,13 @@ bool has_executable_extension(const path& p); class GHC_FS_API_CLASS path #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_WSTRING_STRING_TYPE) #define GHC_USE_WCHAR_T +#define GHC_PLATFORM_LITERAL(str) L##str : private path_helper_base { public: using path_helper_base::value_type; #else +#define GHC_PLATFORM_LITERAL(str) str : private path_helper_base { public: @@ -484,10 +486,10 @@ public: iterator end() const; private: - using impl_value_type = 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 @@ -601,8 +603,8 @@ public: private: friend class path; - 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; + 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; @@ -1552,6 +1554,53 @@ inline std::string toUtf8(const charT* unicodeString) return toUtf8(std::basic_string>(unicodeString)); } +#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); +} + +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) +{ + return toWChar(std::basic_string>(unicodeString)); +} +#endif // GHC_USE_WCHAR_T + } // namespace detail #ifdef GHC_EXPAND_IMPL @@ -1575,7 +1624,7 @@ GHC_INLINE bool endsWith(const strT& what, const strT& with) 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("\\\\?\\"))) { + if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) { postprocess_path_with_format(native_format); } #endif @@ -1601,8 +1650,8 @@ GHC_INLINE void path::postprocess_path_with_format(path::format fmt) } } #ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH - if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type("\\\\?\\"))) { - _path = "\\\\?\\" + _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(); @@ -1616,11 +1665,11 @@ GHC_INLINE void path::postprocess_path_with_format(path::format fmt) #endif } if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator) { - std::string::iterator new_end = std::unique(_path.begin() + _prefixLength + 2, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); + impl_string_type::iterator new_end = std::unique(_path.begin() + _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(_path.begin() + _prefixLength, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); + impl_string_type::iterator new_end = std::unique(_path.begin() + _prefixLength, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); _path.erase(new_end, _path.end()); } } @@ -1629,7 +1678,11 @@ GHC_INLINE void path::postprocess_path_with_format(path::format fmt) template inline path::path(const Source& source, format fmt) +#ifdef GHC_USE_WCHAR_T + : _path(detail::toWChar(source)) +#else : _path(detail::toUtf8(source)) +#endif { postprocess_path_with_format(fmt); } @@ -1656,7 +1709,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__ @@ -1665,15 +1718,19 @@ 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; @@ -2182,11 +2239,7 @@ GHC_INLINE path::path(path&& p) noexcept } 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(fmt); } @@ -2247,11 +2300,7 @@ 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(native_format); return *this; } @@ -2267,7 +2316,11 @@ 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)); +#endif postprocess_path_with_format(native_format); return *this; } @@ -2324,13 +2377,13 @@ GHC_INLINE path& path::operator/=(const path& p) _path += preferred_separator; } first = false; - _path += (*iter++).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)); @@ -2406,11 +2459,7 @@ GHC_INLINE path& path::operator+=(value_type x) } #endif if (_path.empty() || _path.back() != preferred_separator) { -#ifdef GHC_USE_WCHAR_T - _path += detail::toUtf8(string_type(1, x)); -#else _path += x; -#endif } check_long_path(); return *this; @@ -2522,7 +2571,11 @@ GHC_INLINE path::operator path::string_type() const template inline std::basic_string path::string(const Allocator& a) const { +#ifdef GHC_USE_WCHAR_T + return detail::fromWChar>(_path, a); +#else return detail::fromUtf8>(_path, a); +#endif } #ifdef GHC_EXPAND_IMPL @@ -2585,7 +2638,11 @@ 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; @@ -2737,7 +2794,7 @@ 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("\\\\?\\")) || detail::startsWith(_path, impl_string_type("\\??\\"))) { + if (detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\"))) || detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\??\\")))) { _prefixLength = 4; } } @@ -2817,7 +2874,7 @@ GHC_INLINE path path::filename() const 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) { @@ -2851,8 +2908,8 @@ 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; } } @@ -3059,7 +3116,7 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(cons // else check for network name 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++; @@ -3083,10 +3140,6 @@ GHC_INLINE void path::iterator::updateCurrent() } else { _current.assign(_iter, increment(_iter)); - if (_current.generic_string().size() > 1 && _current.generic_string()[0] == preferred_separator && _current.generic_string()[_current.generic_string().size() - 1] == preferred_separator) { - // shrink successive slashes to one - _current._path = preferred_separator; - } } } @@ -5242,6 +5295,9 @@ public: do { if (FindNextFileW(_dirHandle, &_findData)) { _current = _base; +#ifdef GHC_USE_WCHAR_T + _current.append_name(_findData.cFileName); +#else #ifdef GHC_RAISE_UNICODE_ERRORS try { _current.append_name(detail::toUtf8(_findData.cFileName).c_str()); @@ -5252,6 +5308,7 @@ public: } #else _current.append_name(detail::toUtf8(_findData.cFileName).c_str()); +#endif #endif copyToDirEntry(ec); } From ff271edfeeb6f6eef51235e2d57661c8ab99db7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Sch=C3=BCmann?= Date: Sat, 6 Feb 2021 09:10:55 +0100 Subject: [PATCH 08/11] refs #90, fix some mingw compile issues --- include/ghc/filesystem.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index b8f4958..aec56a1 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -1665,11 +1665,11 @@ GHC_INLINE void path::postprocess_path_with_format(path::format fmt) #endif } 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() + _prefixLength + 2, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == 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 { - impl_string_type::iterator new_end = std::unique(_path.begin() + _prefixLength, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); + 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()); } } @@ -3064,8 +3064,8 @@ GHC_INLINE path::iterator::iterator() {} 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 + p._prefixLength) - , _root(p.has_root_directory() ? _first + static_cast(p._prefixLength + p.root_name_length()) : _last) + , _prefix(_first + static_cast(p._prefixLength)) + , _root(p.has_root_directory() ? _first + static_cast(p._prefixLength + p.root_name_length()) : _last) , _iter(pos) { updateCurrent(); From 75c647f32732d5dae5c321ffba658c7979fc2f35 Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Sat, 6 Feb 2021 12:49:16 +0100 Subject: [PATCH 09/11] refs #90, small optimizations --- examples/CMakeLists.txt | 5 +++++ include/ghc/filesystem.hpp | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 90a303f..17bb3d6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -10,3 +10,8 @@ add_executable(fs_du du.cpp) target_link_libraries(fs_du ghc_filesystem) AddExecutableWithStdFS(std_fs_du du.cpp) +if(EXISTS "${PROJECT_SOURCE_DIR}/examples/benchmark.cpp") + add_executable(fs_benchmark benchmark.cpp) + set_property(TARGET fs_benchmark PROPERTY CXX_STANDARD 17) + target_link_libraries(fs_benchmark ghc_filesystem) +endif() \ No newline at end of file diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index aec56a1..f2af8e1 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -2850,7 +2850,7 @@ GHC_INLINE path path::parent_path() const { auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); if (rootPathLen < _path.length()) { - if (empty() || begin() == --end()) { + if (empty()) { return path(); } else { @@ -3068,7 +3068,9 @@ GHC_INLINE path::iterator::iterator(const path& p, const impl_string_type::const , _root(p.has_root_directory() ? _first + static_cast(p._prefixLength + p.root_name_length()) : _last) , _iter(pos) { - updateCurrent(); + if(pos != _last) { + updateCurrent(); + } } GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(const path::impl_string_type::const_iterator& pos) const From 9e3d42fd72f1e6ff2b3d9f04135be917e10f1877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Sch=C3=BCmann?= Date: Sat, 6 Feb 2021 14:32:14 +0100 Subject: [PATCH 10/11] refs #90, avoid unneeded conversions when using wchar_t backend --- include/ghc/filesystem.hpp | 55 +++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index f2af8e1..59610b8 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -292,12 +292,14 @@ bool has_executable_extension(const path& p); class GHC_FS_API_CLASS path #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_WSTRING_STRING_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 { @@ -1821,7 +1823,7 @@ GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlin #pragma GCC diagnostic 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(); } } @@ -1945,7 +1947,7 @@ GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) #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); + std::shared_ptr 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), CloseHandle); if (file.get() == INVALID_HANDLE_VALUE) { ec = detail::make_system_error(); return path(); @@ -2078,7 +2080,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 { @@ -2123,7 +2125,7 @@ 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) { @@ -3396,10 +3398,10 @@ GHC_INLINE path absolute(const path& p, std::error_code& ec) if (p.empty()) { return absolute(current_path(ec), ec) / ""; } - ULONG size = ::GetFullPathNameW(p.wstring().c_str(), 0, 0, 0); + ULONG size = ::GetFullPathNameW(GHC_NATIVEWP(p), 0, 0, 0); if (size) { std::vector 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() == ".") { @@ -3668,7 +3670,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; } @@ -3831,12 +3833,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; } @@ -3957,7 +3959,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 @@ -4004,9 +4006,9 @@ 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); + std::shared_ptr file1(::CreateFileW(GHC_NATIVEWP(p1), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); 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); + std::shared_ptr file2(::CreateFileW(GHC_NATIVEWP(p2), 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) { #ifdef LWG_2937_BEHAVIOUR ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); @@ -4064,7 +4066,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); } @@ -4097,7 +4099,7 @@ 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); + std::shared_ptr file(::CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); BY_HANDLE_FILE_INFORMATION inf; if (file.get() == INVALID_HANDLE_VALUE) { ec = detail::make_system_error(); @@ -4328,7 +4330,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); + std::shared_ptr 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), ::CloseHandle); FILETIME ft; auto tt = std::chrono::duration_cast(d).count() * 10 + 116444736000000000; ft.dwLowDateTime = static_cast(tt); @@ -4417,10 +4419,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; } } @@ -4525,8 +4527,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) { @@ -4536,12 +4543,12 @@ GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept } 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(); } } @@ -4633,7 +4640,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(); } } @@ -4671,7 +4678,7 @@ 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); + std::shared_ptr file(CreateFileW(GHC_NATIVEWP(p), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL), CloseHandle); if (file.get() == INVALID_HANDLE_VALUE) { ec = detail::make_system_error(); } @@ -4704,7 +4711,7 @@ GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept 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)) { + if (!GetDiskFreeSpaceExW(GHC_NATIVEWP(p), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) { ec = detail::make_system_error(); return {static_cast(-1), static_cast(-1), static_cast(-1)}; } @@ -5265,7 +5272,7 @@ 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); } From fdf5bb0179f3cca7b42a8b86f08fa5ff5aba3e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Sch=C3=BCmann?= Date: Sun, 7 Feb 2021 10:02:12 +0100 Subject: [PATCH 11/11] refs #90, made wchar_t/wstring default on Windows --- include/ghc/filesystem.hpp | 13 ++++++++----- include/ghc/fs_std.hpp | 2 +- include/ghc/fs_std_fwd.hpp | 2 +- include/ghc/fs_std_impl.hpp | 2 +- test/CMakeLists.txt | 12 ++++++------ 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 59610b8..160f257 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -225,10 +225,13 @@ // 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 enfoce 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 (an was before v1.5) slower, bot might help keeping source +// homogenous 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). @@ -290,7 +293,7 @@ bool has_executable_extension(const path& p); // 30.10.8 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 diff --git a/include/ghc/fs_std.hpp b/include/ghc/fs_std.hpp index f295e37..0d9d3cc 100644 --- a/include/ghc/fs_std.hpp +++ b/include/ghc/fs_std.hpp @@ -43,7 +43,7 @@ using fstream = std::fstream; #endif #endif #ifndef GHC_USE_STD_FS -#define GHC_WIN_WSTRING_STRING_TYPE +//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE #include namespace fs { using namespace ghc::filesystem; diff --git a/include/ghc/fs_std_fwd.hpp b/include/ghc/fs_std_fwd.hpp index f07c09b..148751e 100644 --- a/include/ghc/fs_std_fwd.hpp +++ b/include/ghc/fs_std_fwd.hpp @@ -46,7 +46,7 @@ using fstream = std::fstream; #endif #endif #ifndef GHC_USE_STD_FS -#define GHC_WIN_WSTRING_STRING_TYPE +//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE #define GHC_FILESYSTEM_FWD #include namespace fs { diff --git a/include/ghc/fs_std_impl.hpp b/include/ghc/fs_std_impl.hpp index d7b609d..455438d 100644 --- a/include/ghc/fs_std_impl.hpp +++ b/include/ghc/fs_std_impl.hpp @@ -37,7 +37,7 @@ #endif #endif #ifndef GHC_USE_STD_FS -#define GHC_WIN_WSTRING_STRING_TYPE +//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE #define GHC_FILESYSTEM_IMPLEMENTATION #include #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a463231..3ad4271 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -31,18 +31,18 @@ else() ParseAndAddCatchTests(filesystem_test) AddExecutableWithStdFS(std_filesystem_test filesystem_test.cpp catch.hpp) if(WIN32) - add_executable(filesystem_test_wchar filesystem_test.cpp catch.hpp) - target_link_libraries(filesystem_test_wchar ghc_filesystem) - target_compile_options(filesystem_test_wchar PRIVATE + add_executable(filesystem_test_char filesystem_test.cpp catch.hpp) + target_link_libraries(filesystem_test_char ghc_filesystem) + target_compile_options(filesystem_test_char PRIVATE $<$:-Wall -Wextra -Werror> $<$:-Wall -Werror> $<$:/WX>) if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) - target_compile_definitions(filesystem_test_wchar PRIVATE _CRT_SECURE_NO_WARNINGS GHC_WIN_WSTRING_STRING_TYPE) + target_compile_definitions(filesystem_test_char PRIVATE _CRT_SECURE_NO_WARNINGS GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE) else() - target_compile_definitions(filesystem_test_wchar PRIVATE GHC_WIN_WSTRING_STRING_TYPE) + target_compile_definitions(filesystem_test_char PRIVATE GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE) endif() - ParseAndAddCatchTests(filesystem_test_wchar) + ParseAndAddCatchTests(filesystem_test_char) endif() if("cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES) AddTestExecutableWithStdCpp(17 filesystem_test.cpp catch.hpp)