From 3fcb3f51e19443f359b890ebbd6595c930c4d26d Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Fri, 31 May 2019 07:08:48 +0200 Subject: [PATCH] optional Windows wchar_t/wstring support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squashed commit of the following: commit f4a85d2070bb62bdca644f81faa953ba5fb5e491 Author: Steffen Schümann Date: Sun May 19 10:02:22 2019 +0200 refs #17, refs #18, Missing use of alloc in fromUtf8 (fixed on master) and initialization order issue. commit aa1cb7081630393659204641792bc7641f0c72aa Author: Steffen Schümann Date: Sun May 19 09:46:02 2019 +0200 refs #18, fighting VS2015 sfinae issues commit 15788d8eb9972965ec4562c6e672a8a460e5b655 Author: Steffen Schümann Date: Sat May 18 10:35:25 2019 +0200 refs #17, work on wchar_t/wstring support on Windows. --- README.md | 14 +- examples/dir.cpp | 33 +++-- examples/du.cpp | 8 +- include/ghc/filesystem.hpp | 285 +++++++++++++++++++++++------------- include/ghc/fs_std.hpp | 8 +- include/ghc/fs_std_fwd.hpp | 8 +- include/ghc/fs_std_impl.hpp | 8 +- test/filesystem_test.cpp | 4 +- 8 files changed, 243 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index db72790..61f54c0 100644 --- a/README.md +++ b/README.md @@ -446,16 +446,28 @@ to the expected behavior. ## Release Notes -### v1.1.5 (wip) +### v1.1.99 (wip) * Added MingW 32/64 and Visual Studio 2015 builds to the CI configuration. * Fixed additional compilation issues on MingW. +* Pull request ([#13](https://github.com/gulrak/filesystem/pull/13)), set + minimum required CMake version to 3.7.2 (as in Debian 8). +* Pull request ([#14](https://github.com/gulrak/filesystem/pull/14)), added + support for a make install target. * Bugfix for ([#15](https://github.com/gulrak/filesystem/issues/15)), the forward/impl way of using `ghc::filesystem` missed a `` include in the windows case. * Bugfix for ([#16](https://github.com/gulrak/filesystem/issues/16)), VS2019 didn't like the old size dispatching in the utf8 decoder, so it was changed to a sfinae based approach. +* New feature ([#17](https://github.com/gulrak/filesystem/issues/17)), optional + support for standard conforming `wchar_t/std::wstring` interface when + compiling on Windows with defined `GHC_WIN_WSTRING_STRING_TYPE`, this is + default when using the `ghc/fs_std*.hpp` header, to enhance compatibility. +* Pull request ([#20](https://github.com/gulrak/filesystem/pull/14)), fix for + file handle leak in `fs::copy_file`. +* Coverage now checked in CI (~95% line coverage). + ### [v1.1.4](https://github.com/gulrak/filesystem/releases/tag/v1.1.4) diff --git a/examples/dir.cpp b/examples/dir.cpp index 67c7b84..abd4cc6 100644 --- a/examples/dir.cpp +++ b/examples/dir.cpp @@ -1,17 +1,21 @@ -#include -#include #include +#include +#include #include -#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include() +#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) +#if __has_include() +#define GHC_USE_STD_FS #include namespace fs = std::filesystem; -#else +#endif +#endif +#ifndef GHC_USE_STD_FS #include namespace fs = ghc::filesystem; #endif -template +template std::time_t to_time_t(TP tp) { // Based on trick from: Nico Josuttis, C++17 - The Complete Guide @@ -23,8 +27,8 @@ static std::string perm_to_str(fs::perms prms) { std::string result; result.reserve(9); - for(int i = 0; i < 9; ++i) { - result = ((static_cast(prms) & (1<(prms) & (1 << i)) ? "xwrxwrxwr"[i] : '-') + result; } return result; } @@ -33,27 +37,24 @@ int main(int argc, char* argv[]) { #ifdef GHC_FILESYSTEM_VERSION fs::u8arguments u8guard(argc, argv); - if(!u8guard.valid()) { + if (!u8guard.valid()) { std::cerr << "Invalid character encoding, UTF-8 based encoding needed." << std::endl; std::exit(EXIT_FAILURE); } #endif - if(argc > 2) { + if (argc > 2) { std::cerr << "USAGE: dir " << std::endl; exit(1); } fs::path dir{"."}; - if(argc == 2) { + if (argc == 2) { dir = fs::u8path(argv[1]); } - for(auto de : fs::directory_iterator(dir)) { + for (auto de : fs::directory_iterator(dir)) { auto ft = to_time_t(de.last_write_time()); auto ftm = *std::localtime(&ft); - std::cout << (de.is_directory() ? "d" : "-") << perm_to_str(de.symlink_status().permissions()) << " " - << std::setw(8) << (de.is_directory() ? "-" : std::to_string(de.file_size())) << " " - << std::put_time(&ftm, "%Y-%m-%d %H:%M:%S") << " " - << de.path().filename().string() - << std::endl; + std::cout << (de.is_directory() ? "d" : "-") << perm_to_str(de.symlink_status().permissions()) << " " << std::setw(8) << (de.is_directory() ? "-" : std::to_string(de.file_size())) << " " << std::put_time(&ftm, "%Y-%m-%d %H:%M:%S") << " " + << de.path().filename().string() << std::endl; } return 0; } diff --git a/examples/du.cpp b/examples/du.cpp index 95b9580..929b809 100644 --- a/examples/du.cpp +++ b/examples/du.cpp @@ -2,10 +2,14 @@ #include #include -#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include() +#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) +#if __has_include() +#define GHC_USE_STD_FS #include namespace fs = std::filesystem; -#else +#endif +#endif +#ifndef GHC_USE_STD_FS #include namespace fs = ghc::filesystem; #endif diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 3020a3b..216bb87 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------------------- // -// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++147/C++17 // //--------------------------------------------------------------------------------------- // @@ -161,9 +161,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 +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) -#define GHC_FILESYSTEM_VERSION 10105L +#define GHC_FILESYSTEM_VERSION 10199L namespace ghc { namespace filesystem { @@ -182,11 +186,18 @@ class GHC_FS_API_CLASS not_implemented_exception : public std::logic_error class GHC_FS_API_CLASS path { public: +#ifdef GHC_OS_WINDOWS +#ifdef GHC_WIN_WSTRING_STRING_TYPE +#define GHC_USE_WCHAR_T + using value_type = std::wstring::value_type; +#else using value_type = std::string::value_type; +#endif using string_type = std::basic_string; -#ifdef GHC_OS_WINDOWS static constexpr value_type preferred_separator = '\\'; #else + using value_type = std::string::value_type; + using string_type = std::basic_string; static constexpr value_type preferred_separator = '/'; #endif // 30.10.10.1 enumeration format @@ -216,11 +227,19 @@ class GHC_FS_API_CLASS path template using path_type = typename std::enable_if::value, path>::type; +#ifdef GHC_USE_WCHAR_T + template + using path_from_string = typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value, + path>::type; + template + using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value, path>::type; +#else template using path_from_string = typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value, path>::type; template using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; - +#endif // 30.10.8.4.1 constructors and destructor path() noexcept; path(const path& p); @@ -266,7 +285,7 @@ class GHC_FS_API_CLASS path path& operator+=(const value_type* x); path& operator+=(value_type x); template - path_type& operator+=(const Source& x); + path_from_string& operator+=(const Source& x); template path_type_EcharT& operator+=(EcharT x); template @@ -346,9 +365,11 @@ class GHC_FS_API_CLASS path iterator end() const; private: + using impl_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 value_type generic_separator = '/'; + static constexpr impl_value_type generic_separator = '/'; template class input_iterator_range { @@ -372,9 +393,13 @@ class GHC_FS_API_CLASS path }; friend void swap(path& lhs, path& rhs) noexcept; friend size_t hash_value(const path& p) noexcept; - string_type _path; + static void postprocess_path_with_format(impl_string_type& p, format fmt); + impl_string_type _path; #ifdef GHC_OS_WINDOWS + impl_string_type native_impl() const; mutable string_type _native_cache; +#else + const impl_string_type& native_impl() const; #endif }; @@ -429,7 +454,7 @@ class GHC_FS_API_CLASS path::iterator using iterator_category = std::bidirectional_iterator_tag; iterator(); - iterator(const string_type::const_iterator& first, const string_type::const_iterator& last, const string_type::const_iterator& pos); + iterator(const impl_string_type::const_iterator& first, const impl_string_type::const_iterator& last, const impl_string_type::const_iterator& pos); iterator& operator++(); iterator operator++(int); iterator& operator--(); @@ -440,13 +465,13 @@ class GHC_FS_API_CLASS path::iterator pointer operator->() const; private: - string_type::const_iterator increment(const std::string::const_iterator& pos) const; - string_type::const_iterator decrement(const std::string::const_iterator& pos) const; + 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; void updateCurrent(); - string_type::const_iterator _first; - string_type::const_iterator _last; - string_type::const_iterator _root; - string_type::const_iterator _iter; + impl_string_type::const_iterator _first; + impl_string_type::const_iterator _last; + impl_string_type::const_iterator _root; + impl_string_type::const_iterator _iter; path _current; }; @@ -624,11 +649,11 @@ class GHC_FS_API_CLASS directory_entry filesystem::path _path; file_status _status; file_status _symlink_status; - uintmax_t _file_size; + uintmax_t _file_size = 0; #ifndef GHC_OS_WINDOWS uintmax_t _hard_link_count; #endif - time_t _last_write_time; + time_t _last_write_time = 0; }; // 30.10.13 Class directory_iterator @@ -1017,7 +1042,7 @@ class GHC_FS_API_CLASS u8arguments //------------------------------------------------------------------------------------------------- namespace detail { -GHC_FS_API void postprocess_path_with_format(path::string_type& p, path::format fmt); +// GHC_FS_API void postprocess_path_with_format(path::impl_string_type& p, path::format fmt); enum utf8_states_t { S_STRT = 0, S_RJCT = 8 }; GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode); GHC_FS_API bool is_surrogate(uint32_t c); @@ -1193,8 +1218,11 @@ GHC_INLINE void appendUTF8(std::string& str, uint32_t unicode) // Generating debugging and shrinking my own DFA from scratch was a day of fun! GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint) { - static const uint32_t utf8_state_info[] = {0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, 0x99999999u, - 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, 0u, 0u}; + static const uint32_t utf8_state_info[] = { + // encoded states + 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, 0x99999999u, + 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, 0u, 0u, + }; uint8_t category = fragment < 128 ? 0 : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; codepoint = (state ? (codepoint << 6) | (fragment & 0x3f) : (0xff >> category) & fragment); return state == S_RJCT ? static_cast(S_RJCT) : static_cast((utf8_state_info[category + 16] >> (state << 2)) & 0xf); @@ -1205,13 +1233,16 @@ GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t frag #endif namespace detail { - -template + +template ::type* = nullptr> +inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + return StringType(utf8String.begin(), utf8String.end(), alloc); +} + +template ::type* = nullptr> inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) { - if (sizeof(typename StringType::value_type) == 1) { - return StringType(utf8String.begin(), utf8String.end(), alloc); - } StringType result(alloc); result.reserve(utf8String.length()); std::string::const_iterator iter = utf8String.begin(); @@ -1219,18 +1250,13 @@ inline StringType fromUtf8(const std::string& utf8String, const typename StringT std::uint32_t codepoint = 0; while (iter < utf8String.end()) { if ((utf8_state = consumeUtf8Fragment(utf8_state, (uint8_t)*iter++, codepoint)) == S_STRT) { - if (sizeof(typename StringType::value_type) == 4) { - result += codepoint; + if (codepoint <= 0xffff) { + result += (typename StringType::value_type)codepoint; } else { - if (codepoint <= 0xffff) { - result += (typename StringType::value_type)codepoint; - } - else { - codepoint -= 0x10000; - result += (typename StringType::value_type)((codepoint >> 10) + 0xd800); - result += (typename StringType::value_type)((codepoint & 0x3ff) + 0xdc00); - } + codepoint -= 0x10000; + result += (typename StringType::value_type)((codepoint >> 10) + 0xd800); + result += (typename StringType::value_type)((codepoint & 0x3ff) + 0xdc00); } codepoint = 0; } @@ -1246,13 +1272,38 @@ inline StringType fromUtf8(const std::string& utf8String, const typename StringT return result; } -template ::type* = nullptr> +template ::type* = nullptr> +inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + StringType result(alloc); + result.reserve(utf8String.length()); + std::string::const_iterator iter = utf8String.begin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.end()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, (uint8_t)*iter++, codepoint)) == S_STRT) { + result += codepoint; + codepoint = 0; + } + else if (utf8_state == S_RJCT) { + result += (typename StringType::value_type)0xfffd; + utf8_state = S_STRT; + codepoint = 0; + } + } + if (utf8_state) { + result += (typename StringType::value_type)0xfffd; + } + return result; +} + +template ::type size = 1> inline std::string toUtf8(const std::basic_string& unicodeString) { return std::string(unicodeString.begin(), unicodeString.end()); } -template ::type* = nullptr> +template ::type size = 2> inline std::string toUtf8(const std::basic_string& unicodeString) { std::string result; @@ -1277,7 +1328,7 @@ inline std::string toUtf8(const std::basic_string& unicode return result; } -template ::type* = nullptr> +template ::type size = 4> inline std::string toUtf8(const std::basic_string& unicodeString) { std::string result; @@ -1287,10 +1338,10 @@ inline std::string toUtf8(const std::basic_string& unicode return result; } -template -inline std::string toUtf8(const SourceType* unicodeString) +template +inline std::string toUtf8(const charT* unicodeString) { - return toUtf8(std::basic_string>(unicodeString)); + return toUtf8(std::basic_string>(unicodeString)); } } // namespace detail @@ -1304,7 +1355,9 @@ GHC_INLINE bool startsWith(const std::string& what, const std::string& with) return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin()); } -GHC_INLINE void postprocess_path_with_format(path::string_type& p, path::format fmt) +} // namespace detail + +GHC_INLINE void path::postprocess_path_with_format(path::impl_string_type& p, path::format fmt) { switch (fmt) { #ifndef GHC_OS_WINDOWS @@ -1317,10 +1370,10 @@ GHC_INLINE void postprocess_path_with_format(path::string_type& p, path::format #ifdef GHC_OS_WINDOWS case path::auto_format: case path::native_format: - if (startsWith(p, std::string("\\\\?\\"))) { + if (detail::startsWith(p, std::string("\\\\?\\"))) { // remove Windows long filename marker p.erase(0, 4); - if (startsWith(p, std::string("UNC\\"))) { + if (detail::startsWith(p, std::string("UNC\\"))) { p.erase(0, 2); p[0] = '\\'; } @@ -1343,33 +1396,31 @@ GHC_INLINE void postprocess_path_with_format(path::string_type& p, path::format } } -} // namespace detail - #endif // GHC_EXPAND_IMPL template inline path::path(const Source& source, format fmt) - : _path(source) + : _path(detail::toUtf8(source)) { - detail::postprocess_path_with_format(_path, fmt); + postprocess_path_with_format(_path, fmt); } template <> inline path::path(const std::wstring& source, format fmt) { _path = detail::toUtf8(source); - detail::postprocess_path_with_format(_path, fmt); + postprocess_path_with_format(_path, fmt); } template <> inline path::path(const std::u16string& source, format fmt) { _path = detail::toUtf8(source); - detail::postprocess_path_with_format(_path, fmt); + postprocess_path_with_format(_path, fmt); } template <> inline path::path(const std::u32string& source, format fmt) { _path = detail::toUtf8(source); - detail::postprocess_path_with_format(_path, fmt); + postprocess_path_with_format(_path, fmt); } template @@ -1847,9 +1898,13 @@ 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 { - detail::postprocess_path_with_format(_path, fmt); + postprocess_path_with_format(_path, fmt); } #endif // GHC_EXPAND_IMPL @@ -1900,8 +1955,12 @@ 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); - detail::postprocess_path_with_format(_path, native_format); +#endif + postprocess_path_with_format(_path, native_format); return *this; } @@ -1917,7 +1976,7 @@ template inline path& path::assign(const Source& source) { _path.assign(detail::toUtf8(source)); - detail::postprocess_path_with_format(_path, native_format); + postprocess_path_with_format(_path, native_format); return *this; } @@ -1932,7 +1991,7 @@ template inline path& path::assign(InputIterator first, InputIterator last) { _path.assign(first, last); - detail::postprocess_path_with_format(_path, native_format); + postprocess_path_with_format(_path, native_format); return *this; } @@ -2050,7 +2109,11 @@ GHC_INLINE path& path::operator+=(value_type x) } #endif if (_path.empty() || _path.back() != generic_separator) { +#ifdef GHC_USE_WCHAR_T + _path += detail::toUtf8(string_type(1, x)); +#else _path += x; +#endif } return *this; } @@ -2058,7 +2121,7 @@ GHC_INLINE path& path::operator+=(value_type x) #endif // GHC_EXPAND_IMPL template -inline path::path_type& path::operator+=(const Source& x) +inline path::path_from_string& path::operator+=(const Source& x) { return concat(x); } @@ -2075,7 +2138,7 @@ template inline path& path::concat(const Source& x) { path p(x); - detail::postprocess_path_with_format(p._path, native_format); + postprocess_path_with_format(p._path, native_format); _path += p._path; return *this; } @@ -2083,7 +2146,7 @@ template inline path& path::concat(InputIterator first, InputIterator last) { _path.append(first, last); - detail::postprocess_path_with_format(_path, native_format); + postprocess_path_with_format(_path, native_format); return *this; } @@ -2135,29 +2198,47 @@ GHC_INLINE void path::swap(path& rhs) noexcept //----------------------------------------------------------------------------- // 30.10.8.4.6, native format observers -GHC_INLINE const path::string_type& path::native() const -{ #ifdef GHC_OS_WINDOWS +GHC_INLINE path::impl_string_type path::native_impl() const +{ + impl_string_type result; if (is_absolute() && _path.length() > MAX_PATH - 10) { // expand long Windows filenames with marker if (has_root_name() && _path[0] == '/') { - _native_cache = "\\\\?\\UNC" + _path.substr(1); + result = "\\\\?\\UNC" + _path.substr(1); } else { - _native_cache = "\\\\?\\" + _path; + result = "\\\\?\\" + _path; } } else { - _native_cache = _path; + result = _path; } /*if (has_root_name() && root_name()._path[0] == '/') { return _path; }*/ - for (auto& c : _native_cache) { + for (auto& c : result) { if (c == '/') { c = '\\'; } } + return result; +} +#else +GHC_INLINE const path::impl_string_type& path::native_impl() const +{ + return _path; +} +#endif + +GHC_INLINE const path::string_type& path::native() const +{ +#ifdef GHC_OS_WINDOWS +#ifdef GHC_USE_WCHAR_T + _native_cache = detail::fromUtf8(native_impl()); +#else + _native_cache = native_impl(); +#endif return _native_cache; #else return _path; @@ -2179,34 +2260,38 @@ GHC_INLINE path::operator path::string_type() const template inline std::basic_string path::string(const Allocator& a) const { - return detail::fromUtf8>(native(), a); + return detail::fromUtf8>(native_impl(), a); } #ifdef GHC_EXPAND_IMPL GHC_INLINE std::string path::string() const { - return native(); + return native_impl(); } GHC_INLINE std::wstring path::wstring() const { +#ifdef GHC_USE_WCHAR_T + return native(); +#else return detail::fromUtf8(native()); +#endif } GHC_INLINE std::string path::u8string() const { - return native(); + return native_impl(); } GHC_INLINE std::u16string path::u16string() const { - return detail::fromUtf8(native()); + return detail::fromUtf8(native_impl()); } GHC_INLINE std::u32string path::u32string() const { - return detail::fromUtf8(native()); + return detail::fromUtf8(native_impl()); } #endif // GHC_EXPAND_IMPL @@ -2280,8 +2365,8 @@ GHC_INLINE path path::root_name() const } #endif if (_path.length() > 2 && _path[0] == '/' && _path[1] == '/' && _path[2] != '/' && std::isprint(_path[2])) { - string_type::size_type pos = _path.find_first_of("/\\", 3); - if (pos == string_type::npos) { + impl_string_type::size_type pos = _path.find_first_of("/\\", 3); + if (pos == impl_string_type::npos) { return path(_path); } else { @@ -2343,10 +2428,10 @@ GHC_INLINE path path::filename() const GHC_INLINE path path::stem() const { - string_type fn = filename(); + impl_string_type fn = filename().string(); if (fn != "." && fn != "..") { - string_type::size_type n = fn.rfind("."); - if (n != string_type::npos && n != 0) { + impl_string_type::size_type n = fn.rfind("."); + if (n != impl_string_type::npos && n != 0) { return fn.substr(0, n); } } @@ -2355,8 +2440,8 @@ GHC_INLINE path path::stem() const GHC_INLINE path path::extension() const { - string_type fn = filename(); - string_type::size_type pos = fn.find_last_of('.'); + impl_string_type fn = filename().string(); + impl_string_type::size_type pos = fn.find_last_of('.'); if (pos == std::string::npos || pos == 0) { return ""; } @@ -2500,7 +2585,7 @@ 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::string_type::const_iterator& first, const path::string_type::const_iterator& last, const path::string_type::const_iterator& pos) +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) , _iter(pos) @@ -2528,9 +2613,9 @@ GHC_INLINE path::iterator::iterator(const path::string_type::const_iterator& fir } } -GHC_INLINE path::string_type::const_iterator path::iterator::increment(const path::string_type::const_iterator& pos) const +GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(const path::impl_string_type::const_iterator& pos) const { - std::string::const_iterator i = pos; + path::impl_string_type::const_iterator i = pos; bool fromStart = i == _first; if (i != _last) { // we can only sit on a slash if it is a network name or a root @@ -2561,9 +2646,9 @@ GHC_INLINE path::string_type::const_iterator path::iterator::increment(const pat return i; } -GHC_INLINE path::string_type::const_iterator path::iterator::decrement(const path::string_type::const_iterator& pos) const +GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(const path::impl_string_type::const_iterator& pos) const { - std::string::const_iterator i = pos; + path::impl_string_type::const_iterator i = pos; if (i != _first) { --i; // if this is now the root slash or the trailing slash, we are done, @@ -2571,12 +2656,12 @@ GHC_INLINE path::string_type::const_iterator path::iterator::decrement(const pat if (i != _root && (pos != _last || *i != '/')) { #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(); + i = std::find_first_of(std::reverse_iterator(i), std::reverse_iterator(_first), seps.begin(), seps.end()).base(); if (i > _first && *i == ':') { i++; } #else - i = std::find(std::reverse_iterator(i), std::reverse_iterator(_first), '/').base(); + i = std::find(std::reverse_iterator(i), std::reverse_iterator(_first), '/').base(); #endif // Now we have to check if this is a network name if (i - _first == 2 && *_first == '/' && *(_first + 1) == '/') { @@ -3183,7 +3268,7 @@ GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept { path current; ec.clear(); - for (const std::string& part : p) { + for (const path::string_type& part : p) { current /= part; if (current != p.root_name() && current != p.root_path()) { std::error_code tec; @@ -3334,7 +3419,7 @@ GHC_INLINE path current_path(std::error_code& ec) ec.clear(); #ifdef GHC_OS_WINDOWS DWORD pathlen = ::GetCurrentDirectoryW(0, 0); - std::unique_ptr buffer(new wchar_t[pathlen + 1]); + std::unique_ptr buffer(new wchar_t[size_t(pathlen) + 1]); if (::GetCurrentDirectoryW(pathlen, buffer.get()) == 0) { ec = std::error_code(::GetLastError(), std::system_category()); return path(); @@ -4536,22 +4621,26 @@ class directory_iterator::impl impl(const path& p, directory_options options) : _base(p) , _options(options) - , _dirHandle(_base.empty() ? INVALID_HANDLE_VALUE : FindFirstFileW(detail::fromUtf8((_base / "*").u8string()).c_str(), &_findData)) - , _current(_dirHandle != INVALID_HANDLE_VALUE ? _base / std::wstring(_findData.cFileName) : filesystem::path()) + , _findData{0} + , _dirHandle(INVALID_HANDLE_VALUE) { - if (_dirHandle == INVALID_HANDLE_VALUE && !p.empty()) { - auto error = ::GetLastError(); - _base = filesystem::path(); - if (error != ERROR_ACCESS_DENIED || (options & directory_options::skip_permission_denied) == directory_options::none) { - _ec = std::error_code(::GetLastError(), std::system_category()); - } - } - else { - if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") { - increment(_ec); + if (!_base.empty()) { + ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW)); + if ((_dirHandle = FindFirstFileW(detail::fromUtf8((_base / "*").u8string()).c_str(), &_findData)) != INVALID_HANDLE_VALUE) { + if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") { + increment(_ec); + } + else { + _current = _base / std::wstring(_findData.cFileName); + copyToDirEntry(_ec); + } } else { - copyToDirEntry(_ec); + auto error = ::GetLastError(); + _base = filesystem::path(); + if (error != ERROR_ACCESS_DENIED || (options & directory_options::skip_permission_denied) == directory_options::none) { + _ec = std::error_code(::GetLastError(), std::system_category()); + } } } } diff --git a/include/ghc/fs_std.hpp b/include/ghc/fs_std.hpp index 9b54a0d..5dc3a8e 100644 --- a/include/ghc/fs_std.hpp +++ b/include/ghc/fs_std.hpp @@ -38,7 +38,9 @@ // namespace fs. //--------------------------------------------------------------------------------------- #ifndef GHC_FILESYSTEM_STD_H -#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include() +#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) +#if __has_include() +#define GHC_USE_STD_FS #include namespace fs { using namespace std::filesystem; @@ -46,7 +48,9 @@ using ifstream = std::ifstream; using ofstream = std::ofstream; using fstream = std::fstream; } -#else +#endif +#endif +#ifndef GHC_USE_STD_FS #include namespace fs { using namespace ghc::filesystem; diff --git a/include/ghc/fs_std_fwd.hpp b/include/ghc/fs_std_fwd.hpp index 68dcb3e..f336a12 100644 --- a/include/ghc/fs_std_fwd.hpp +++ b/include/ghc/fs_std_fwd.hpp @@ -41,7 +41,9 @@ //--------------------------------------------------------------------------------------- #ifndef GHC_FILESYSTEM_STD_FWD_H #define GHC_FILESYSTEM_STD_FWD_H -#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include() +#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) +#if __has_include() +#define GHC_USE_STD_FS #include namespace fs { using namespace std::filesystem; @@ -49,7 +51,9 @@ using ifstream = std::ifstream; using ofstream = std::ofstream; using fstream = std::fstream; } -#else +#endif +#endif +#ifndef GHC_USE_STD_FS #define GHC_FILESYSTEM_FWD #include namespace fs { diff --git a/include/ghc/fs_std_impl.hpp b/include/ghc/fs_std_impl.hpp index 96f0f5c..a094993 100644 --- a/include/ghc/fs_std_impl.hpp +++ b/include/ghc/fs_std_impl.hpp @@ -39,8 +39,12 @@ // The cpp has to include this before including fs_std_fwd.hpp directly or via a different // header to work. //--------------------------------------------------------------------------------------- -#if !(defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include()) +#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) +#if __has_include() +#define GHC_USE_STD_FS +#endif +#endif +#ifndef GHC_USE_STD_FS #define GHC_FILESYSTEM_IMPLEMENTATION #include #endif - diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index 83e3962..9ec64d0 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -405,7 +405,7 @@ TEST_CASE("30.10.8.4.2 path assignments", "[filesystem][path][fs.path.assign]") REQUIRE(p1 == p3); p3 = fs::path{"/usr/local"}; REQUIRE(p2 == p3); -#ifdef IS_WCHAR_PATH +#if defined(IS_WCHAR_PATH) || defined(GHC_USE_WCHAR_T) p3 = fs::path::string_type{L"/foo/bar"}; REQUIRE(p1 == p3); p3.assign(fs::path::string_type{L"/usr/local"}); @@ -528,7 +528,7 @@ TEST_CASE("30.10.8.4.5 path modifiers", "[filesystem][path][fs.path.modifiers]") TEST_CASE("30.10.8.4.6 path native format observers", "[filesystem][path][fs.path.native.obs]") { #ifdef GHC_OS_WINDOWS -#ifdef IS_WCHAR_PATH +#if defined(IS_WCHAR_PATH) || defined(GHC_USE_WCHAR_T) CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type(L"\u00E4\\\u20AC")); // CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("ä\\€")); // MSVCs returns local DBCS encoding #else