optional Windows wchar_t/wstring support

Squashed commit of the following:

commit f4a85d2070bb62bdca644f81faa953ba5fb5e491
Author: Steffen Schümann <s.schuemann@pobox.com>
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 <s.schuemann@pobox.com>
Date:   Sun May 19 09:46:02 2019 +0200

    refs #18, fighting VS2015 sfinae issues

commit 15788d8eb9972965ec4562c6e672a8a460e5b655
Author: Steffen Schümann <s.schuemann@pobox.com>
Date:   Sat May 18 10:35:25 2019 +0200

    refs #17, work on wchar_t/wstring support on Windows.
This commit is contained in:
Steffen Schuemann 2019-05-31 07:08:48 +02:00
parent 235a594240
commit 3fcb3f51e1
8 changed files with 244 additions and 126 deletions

View File

@ -446,16 +446,28 @@ to the expected behavior.
## Release Notes ## Release Notes
### v1.1.5 (wip) ### v1.1.99 (wip)
* Added MingW 32/64 and Visual Studio 2015 builds to the CI configuration. * Added MingW 32/64 and Visual Studio 2015 builds to the CI configuration.
* Fixed additional compilation issues on MingW. * 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 * Bugfix for ([#15](https://github.com/gulrak/filesystem/issues/15)), the
forward/impl way of using `ghc::filesystem` missed a `<vector>` include forward/impl way of using `ghc::filesystem` missed a `<vector>` include
in the windows case. in the windows case.
* Bugfix for ([#16](https://github.com/gulrak/filesystem/issues/16)), * Bugfix for ([#16](https://github.com/gulrak/filesystem/issues/16)),
VS2019 didn't like the old size dispatching in the utf8 decoder, so it VS2019 didn't like the old size dispatching in the utf8 decoder, so it
was changed to a sfinae based approach. 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) ### [v1.1.4](https://github.com/gulrak/filesystem/releases/tag/v1.1.4)

View File

@ -1,17 +1,21 @@
#include <iostream>
#include <iomanip>
#include <chrono> #include <chrono>
#include <iomanip>
#include <iostream>
#include <string> #include <string>
#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include(<filesystem>) #if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include)
#if __has_include(<filesystem>)
#define GHC_USE_STD_FS
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
#else #endif
#endif
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp> #include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem; namespace fs = ghc::filesystem;
#endif #endif
template<typename TP> template <typename TP>
std::time_t to_time_t(TP tp) std::time_t to_time_t(TP tp)
{ {
// Based on trick from: Nico Josuttis, C++17 - The Complete Guide // 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; std::string result;
result.reserve(9); result.reserve(9);
for(int i = 0; i < 9; ++i) { for (int i = 0; i < 9; ++i) {
result = ((static_cast<int>(prms) & (1<<i)) ? "xwrxwrxwr"[i] : '-') + result; result = ((static_cast<int>(prms) & (1 << i)) ? "xwrxwrxwr"[i] : '-') + result;
} }
return result; return result;
} }
@ -33,27 +37,24 @@ int main(int argc, char* argv[])
{ {
#ifdef GHC_FILESYSTEM_VERSION #ifdef GHC_FILESYSTEM_VERSION
fs::u8arguments u8guard(argc, argv); fs::u8arguments u8guard(argc, argv);
if(!u8guard.valid()) { if (!u8guard.valid()) {
std::cerr << "Invalid character encoding, UTF-8 based encoding needed." << std::endl; std::cerr << "Invalid character encoding, UTF-8 based encoding needed." << std::endl;
std::exit(EXIT_FAILURE); std::exit(EXIT_FAILURE);
} }
#endif #endif
if(argc > 2) { if (argc > 2) {
std::cerr << "USAGE: dir <path>" << std::endl; std::cerr << "USAGE: dir <path>" << std::endl;
exit(1); exit(1);
} }
fs::path dir{"."}; fs::path dir{"."};
if(argc == 2) { if (argc == 2) {
dir = fs::u8path(argv[1]); 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 ft = to_time_t(de.last_write_time());
auto ftm = *std::localtime(&ft); auto ftm = *std::localtime(&ft);
std::cout << (de.is_directory() ? "d" : "-") << perm_to_str(de.symlink_status().permissions()) << " " 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") << " "
<< std::setw(8) << (de.is_directory() ? "-" : std::to_string(de.file_size())) << " " << de.path().filename().string() << std::endl;
<< std::put_time(&ftm, "%Y-%m-%d %H:%M:%S") << " "
<< de.path().filename().string()
<< std::endl;
} }
return 0; return 0;
} }

View File

@ -2,10 +2,14 @@
#include <iomanip> #include <iomanip>
#include <chrono> #include <chrono>
#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include(<filesystem>) #if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include)
#if __has_include(<filesystem>)
#define GHC_USE_STD_FS
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
#else #endif
#endif
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp> #include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem; namespace fs = ghc::filesystem;
#endif #endif

View File

@ -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) // LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2)
#define LWG_2937_BEHAVIOUR #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) // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch)
#define GHC_FILESYSTEM_VERSION 10105L #define GHC_FILESYSTEM_VERSION 10199L
namespace ghc { namespace ghc {
namespace filesystem { namespace filesystem {
@ -182,11 +186,18 @@ public:
class GHC_FS_API_CLASS path class GHC_FS_API_CLASS path
{ {
public: public:
using value_type = std::string::value_type;
using string_type = std::basic_string<value_type>;
#ifdef GHC_OS_WINDOWS #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<value_type>;
static constexpr value_type preferred_separator = '\\'; static constexpr value_type preferred_separator = '\\';
#else #else
using value_type = std::string::value_type;
using string_type = std::basic_string<value_type>;
static constexpr value_type preferred_separator = '/'; static constexpr value_type preferred_separator = '/';
#endif #endif
// 30.10.10.1 enumeration format // 30.10.10.1 enumeration format
@ -216,11 +227,19 @@ public:
template <typename T1, typename T2 = void> template <typename T1, typename T2 = void>
using path_type = typename std::enable_if<!std::is_same<path, T1>::value, path>::type; using path_type = typename std::enable_if<!std::is_same<path, T1>::value, path>::type;
#ifdef GHC_USE_WCHAR_T
template <typename T>
using path_from_string = typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value ||
std::is_same<wchar_t const*, typename std::decay<T>::type>::value || std::is_same<wchar_t*, typename std::decay<T>::type>::value,
path>::type;
template <typename T>
using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value, path>::type;
#else
template <typename T> template <typename T>
using path_from_string = typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value, path>::type; using path_from_string = typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value, path>::type;
template <typename T> template <typename T>
using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value || std::is_same<T, wchar_t>::value, path>::type; using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value || std::is_same<T, wchar_t>::value, path>::type;
#endif
// 30.10.8.4.1 constructors and destructor // 30.10.8.4.1 constructors and destructor
path() noexcept; path() noexcept;
path(const path& p); path(const path& p);
@ -266,7 +285,7 @@ public:
path& operator+=(const value_type* x); path& operator+=(const value_type* x);
path& operator+=(value_type x); path& operator+=(value_type x);
template <class Source> template <class Source>
path_type<Source>& operator+=(const Source& x); path_from_string<Source>& operator+=(const Source& x);
template <class EcharT> template <class EcharT>
path_type_EcharT<EcharT>& operator+=(EcharT x); path_type_EcharT<EcharT>& operator+=(EcharT x);
template <class Source> template <class Source>
@ -346,9 +365,11 @@ public:
iterator end() const; iterator end() const;
private: private:
using impl_value_type = std::string::value_type;
using impl_string_type = std::basic_string<impl_value_type>;
friend class directory_iterator; friend class directory_iterator;
void append_name(const char* name); void append_name(const char* name);
static constexpr value_type generic_separator = '/'; static constexpr impl_value_type generic_separator = '/';
template <typename InputIterator> template <typename InputIterator>
class input_iterator_range class input_iterator_range
{ {
@ -372,9 +393,13 @@ private:
}; };
friend void swap(path& lhs, path& rhs) noexcept; friend void swap(path& lhs, path& rhs) noexcept;
friend size_t hash_value(const path& p) 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 #ifdef GHC_OS_WINDOWS
impl_string_type native_impl() const;
mutable string_type _native_cache; mutable string_type _native_cache;
#else
const impl_string_type& native_impl() const;
#endif #endif
}; };
@ -429,7 +454,7 @@ public:
using iterator_category = std::bidirectional_iterator_tag; using iterator_category = std::bidirectional_iterator_tag;
iterator(); 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++();
iterator operator++(int); iterator operator++(int);
iterator& operator--(); iterator& operator--();
@ -440,13 +465,13 @@ public:
pointer operator->() const; pointer operator->() const;
private: private:
string_type::const_iterator increment(const std::string::const_iterator& pos) const; impl_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 decrement(const std::string::const_iterator& pos) const;
void updateCurrent(); void updateCurrent();
string_type::const_iterator _first; impl_string_type::const_iterator _first;
string_type::const_iterator _last; impl_string_type::const_iterator _last;
string_type::const_iterator _root; impl_string_type::const_iterator _root;
string_type::const_iterator _iter; impl_string_type::const_iterator _iter;
path _current; path _current;
}; };
@ -624,11 +649,11 @@ private:
filesystem::path _path; filesystem::path _path;
file_status _status; file_status _status;
file_status _symlink_status; file_status _symlink_status;
uintmax_t _file_size; uintmax_t _file_size = 0;
#ifndef GHC_OS_WINDOWS #ifndef GHC_OS_WINDOWS
uintmax_t _hard_link_count; uintmax_t _hard_link_count;
#endif #endif
time_t _last_write_time; time_t _last_write_time = 0;
}; };
// 30.10.13 Class directory_iterator // 30.10.13 Class directory_iterator
@ -1017,7 +1042,7 @@ private:
//------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------
namespace detail { 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 }; enum utf8_states_t { S_STRT = 0, S_RJCT = 8 };
GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode); GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode);
GHC_FS_API bool is_surrogate(uint32_t c); 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! // 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) 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, static const uint32_t utf8_state_info[] = {
0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, 0u, 0u}; // 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; 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); codepoint = (state ? (codepoint << 6) | (fragment & 0x3f) : (0xff >> category) & fragment);
return state == S_RJCT ? static_cast<unsigned>(S_RJCT) : static_cast<unsigned>((utf8_state_info[category + 16] >> (state << 2)) & 0xf); return state == S_RJCT ? static_cast<unsigned>(S_RJCT) : static_cast<unsigned>((utf8_state_info[category + 16] >> (state << 2)) & 0xf);
@ -1205,13 +1233,16 @@ GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t frag
#endif #endif
namespace detail { namespace detail {
template <class StringType> template <class StringType, typename std::enable_if<(sizeof(typename StringType::value_type) == 1)>::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 <class StringType, typename std::enable_if<(sizeof(typename StringType::value_type) == 2)>::type* = nullptr>
inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) 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); StringType result(alloc);
result.reserve(utf8String.length()); result.reserve(utf8String.length());
std::string::const_iterator iter = utf8String.begin(); 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; std::uint32_t codepoint = 0;
while (iter < utf8String.end()) { while (iter < utf8String.end()) {
if ((utf8_state = consumeUtf8Fragment(utf8_state, (uint8_t)*iter++, codepoint)) == S_STRT) { if ((utf8_state = consumeUtf8Fragment(utf8_state, (uint8_t)*iter++, codepoint)) == S_STRT) {
if (sizeof(typename StringType::value_type) == 4) { if (codepoint <= 0xffff) {
result += codepoint; result += (typename StringType::value_type)codepoint;
} }
else { else {
if (codepoint <= 0xffff) { codepoint -= 0x10000;
result += (typename StringType::value_type)codepoint; result += (typename StringType::value_type)((codepoint >> 10) + 0xd800);
} result += (typename StringType::value_type)((codepoint & 0x3ff) + 0xdc00);
else {
codepoint -= 0x10000;
result += (typename StringType::value_type)((codepoint >> 10) + 0xd800);
result += (typename StringType::value_type)((codepoint & 0x3ff) + 0xdc00);
}
} }
codepoint = 0; codepoint = 0;
} }
@ -1246,13 +1272,38 @@ inline StringType fromUtf8(const std::string& utf8String, const typename StringT
return result; return result;
} }
template <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 1)>::type* = nullptr> template <class StringType, typename std::enable_if<(sizeof(typename StringType::value_type) == 4)>::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 <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 1), int>::type size = 1>
inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicodeString) inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicodeString)
{ {
return std::string(unicodeString.begin(), unicodeString.end()); return std::string(unicodeString.begin(), unicodeString.end());
} }
template <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 2)>::type* = nullptr> template <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 2), int>::type size = 2>
inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicodeString) inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicodeString)
{ {
std::string result; std::string result;
@ -1277,7 +1328,7 @@ inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicode
return result; return result;
} }
template <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 4)>::type* = nullptr> template <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 4), int>::type size = 4>
inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicodeString) inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicodeString)
{ {
std::string result; std::string result;
@ -1287,10 +1338,10 @@ inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicode
return result; return result;
} }
template <typename SourceType> template <typename charT>
inline std::string toUtf8(const SourceType* unicodeString) inline std::string toUtf8(const charT* unicodeString)
{ {
return toUtf8(std::basic_string<SourceType, std::char_traits<SourceType>>(unicodeString)); return toUtf8(std::basic_string<charT, std::char_traits<charT>>(unicodeString));
} }
} // namespace detail } // 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()); 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) { switch (fmt) {
#ifndef GHC_OS_WINDOWS #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 #ifdef GHC_OS_WINDOWS
case path::auto_format: case path::auto_format:
case path::native_format: case path::native_format:
if (startsWith(p, std::string("\\\\?\\"))) { if (detail::startsWith(p, std::string("\\\\?\\"))) {
// remove Windows long filename marker // remove Windows long filename marker
p.erase(0, 4); p.erase(0, 4);
if (startsWith(p, std::string("UNC\\"))) { if (detail::startsWith(p, std::string("UNC\\"))) {
p.erase(0, 2); p.erase(0, 2);
p[0] = '\\'; 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 #endif // GHC_EXPAND_IMPL
template <class Source, typename> template <class Source, typename>
inline path::path(const Source& source, format fmt) 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 <> template <>
inline path::path(const std::wstring& source, format fmt) inline path::path(const std::wstring& source, format fmt)
{ {
_path = detail::toUtf8(source); _path = detail::toUtf8(source);
detail::postprocess_path_with_format(_path, fmt); postprocess_path_with_format(_path, fmt);
} }
template <> template <>
inline path::path(const std::u16string& source, format fmt) inline path::path(const std::u16string& source, format fmt)
{ {
_path = detail::toUtf8(source); _path = detail::toUtf8(source);
detail::postprocess_path_with_format(_path, fmt); postprocess_path_with_format(_path, fmt);
} }
template <> template <>
inline path::path(const std::u32string& source, format fmt) inline path::path(const std::u32string& source, format fmt)
{ {
_path = detail::toUtf8(source); _path = detail::toUtf8(source);
detail::postprocess_path_with_format(_path, fmt); postprocess_path_with_format(_path, fmt);
} }
template <class Source, typename> template <class Source, typename>
@ -1847,9 +1898,13 @@ GHC_INLINE path::path(path&& p) noexcept
} }
GHC_INLINE path::path(string_type&& source, format fmt) GHC_INLINE path::path(string_type&& source, format fmt)
#ifdef GHC_USE_WCHAR_T
: _path(detail::toUtf8(source))
#else
: _path(std::move(source)) : _path(std::move(source))
#endif
{ {
detail::postprocess_path_with_format(_path, fmt); postprocess_path_with_format(_path, fmt);
} }
#endif // GHC_EXPAND_IMPL #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) GHC_INLINE path& path::assign(path::string_type&& source)
{ {
#ifdef GHC_USE_WCHAR_T
_path = detail::toUtf8(source);
#else
_path = std::move(source); _path = std::move(source);
detail::postprocess_path_with_format(_path, native_format); #endif
postprocess_path_with_format(_path, native_format);
return *this; return *this;
} }
@ -1917,7 +1976,7 @@ template <class Source>
inline path& path::assign(const Source& source) inline path& path::assign(const Source& source)
{ {
_path.assign(detail::toUtf8(source)); _path.assign(detail::toUtf8(source));
detail::postprocess_path_with_format(_path, native_format); postprocess_path_with_format(_path, native_format);
return *this; return *this;
} }
@ -1932,7 +1991,7 @@ template <class InputIterator>
inline path& path::assign(InputIterator first, InputIterator last) inline path& path::assign(InputIterator first, InputIterator last)
{ {
_path.assign(first, last); _path.assign(first, last);
detail::postprocess_path_with_format(_path, native_format); postprocess_path_with_format(_path, native_format);
return *this; return *this;
} }
@ -2050,7 +2109,11 @@ GHC_INLINE path& path::operator+=(value_type x)
} }
#endif #endif
if (_path.empty() || _path.back() != generic_separator) { if (_path.empty() || _path.back() != generic_separator) {
#ifdef GHC_USE_WCHAR_T
_path += detail::toUtf8(string_type(1, x));
#else
_path += x; _path += x;
#endif
} }
return *this; return *this;
} }
@ -2058,7 +2121,7 @@ GHC_INLINE path& path::operator+=(value_type x)
#endif // GHC_EXPAND_IMPL #endif // GHC_EXPAND_IMPL
template <class Source> template <class Source>
inline path::path_type<Source>& path::operator+=(const Source& x) inline path::path_from_string<Source>& path::operator+=(const Source& x)
{ {
return concat(x); return concat(x);
} }
@ -2075,7 +2138,7 @@ template <class Source>
inline path& path::concat(const Source& x) inline path& path::concat(const Source& x)
{ {
path p(x); path p(x);
detail::postprocess_path_with_format(p._path, native_format); postprocess_path_with_format(p._path, native_format);
_path += p._path; _path += p._path;
return *this; return *this;
} }
@ -2083,7 +2146,7 @@ template <class InputIterator>
inline path& path::concat(InputIterator first, InputIterator last) inline path& path::concat(InputIterator first, InputIterator last)
{ {
_path.append(first, last); _path.append(first, last);
detail::postprocess_path_with_format(_path, native_format); postprocess_path_with_format(_path, native_format);
return *this; return *this;
} }
@ -2135,29 +2198,47 @@ GHC_INLINE void path::swap(path& rhs) noexcept
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// 30.10.8.4.6, native format observers // 30.10.8.4.6, native format observers
GHC_INLINE const path::string_type& path::native() const
{
#ifdef GHC_OS_WINDOWS #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) { if (is_absolute() && _path.length() > MAX_PATH - 10) {
// expand long Windows filenames with marker // expand long Windows filenames with marker
if (has_root_name() && _path[0] == '/') { if (has_root_name() && _path[0] == '/') {
_native_cache = "\\\\?\\UNC" + _path.substr(1); result = "\\\\?\\UNC" + _path.substr(1);
} }
else { else {
_native_cache = "\\\\?\\" + _path; result = "\\\\?\\" + _path;
} }
} }
else { else {
_native_cache = _path; result = _path;
} }
/*if (has_root_name() && root_name()._path[0] == '/') { /*if (has_root_name() && root_name()._path[0] == '/') {
return _path; return _path;
}*/ }*/
for (auto& c : _native_cache) { for (auto& c : result) {
if (c == '/') { if (c == '/') {
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<string_type>(native_impl());
#else
_native_cache = native_impl();
#endif
return _native_cache; return _native_cache;
#else #else
return _path; return _path;
@ -2179,34 +2260,38 @@ GHC_INLINE path::operator path::string_type() const
template <class EcharT, class traits, class Allocator> template <class EcharT, class traits, class Allocator>
inline std::basic_string<EcharT, traits, Allocator> path::string(const Allocator& a) const inline std::basic_string<EcharT, traits, Allocator> path::string(const Allocator& a) const
{ {
return detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(native(), a); return detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(native_impl(), a);
} }
#ifdef GHC_EXPAND_IMPL #ifdef GHC_EXPAND_IMPL
GHC_INLINE std::string path::string() const GHC_INLINE std::string path::string() const
{ {
return native(); return native_impl();
} }
GHC_INLINE std::wstring path::wstring() const GHC_INLINE std::wstring path::wstring() const
{ {
#ifdef GHC_USE_WCHAR_T
return native();
#else
return detail::fromUtf8<std::wstring>(native()); return detail::fromUtf8<std::wstring>(native());
#endif
} }
GHC_INLINE std::string path::u8string() const GHC_INLINE std::string path::u8string() const
{ {
return native(); return native_impl();
} }
GHC_INLINE std::u16string path::u16string() const GHC_INLINE std::u16string path::u16string() const
{ {
return detail::fromUtf8<std::u16string>(native()); return detail::fromUtf8<std::u16string>(native_impl());
} }
GHC_INLINE std::u32string path::u32string() const GHC_INLINE std::u32string path::u32string() const
{ {
return detail::fromUtf8<std::u32string>(native()); return detail::fromUtf8<std::u32string>(native_impl());
} }
#endif // GHC_EXPAND_IMPL #endif // GHC_EXPAND_IMPL
@ -2280,8 +2365,8 @@ GHC_INLINE path path::root_name() const
} }
#endif #endif
if (_path.length() > 2 && _path[0] == '/' && _path[1] == '/' && _path[2] != '/' && std::isprint(_path[2])) { if (_path.length() > 2 && _path[0] == '/' && _path[1] == '/' && _path[2] != '/' && std::isprint(_path[2])) {
string_type::size_type pos = _path.find_first_of("/\\", 3); impl_string_type::size_type pos = _path.find_first_of("/\\", 3);
if (pos == string_type::npos) { if (pos == impl_string_type::npos) {
return path(_path); return path(_path);
} }
else { else {
@ -2343,10 +2428,10 @@ GHC_INLINE path path::filename() const
GHC_INLINE path path::stem() const GHC_INLINE path path::stem() const
{ {
string_type fn = filename(); impl_string_type fn = filename().string();
if (fn != "." && fn != "..") { if (fn != "." && fn != "..") {
string_type::size_type n = fn.rfind("."); impl_string_type::size_type n = fn.rfind(".");
if (n != string_type::npos && n != 0) { if (n != impl_string_type::npos && n != 0) {
return fn.substr(0, n); return fn.substr(0, n);
} }
} }
@ -2355,8 +2440,8 @@ GHC_INLINE path path::stem() const
GHC_INLINE path path::extension() const GHC_INLINE path path::extension() const
{ {
string_type fn = filename(); impl_string_type fn = filename().string();
string_type::size_type pos = fn.find_last_of('.'); impl_string_type::size_type pos = fn.find_last_of('.');
if (pos == std::string::npos || pos == 0) { if (pos == std::string::npos || pos == 0) {
return ""; return "";
} }
@ -2500,7 +2585,7 @@ GHC_INLINE path path::lexically_proximate(const path& base) const
// 30.10.8.5, iterators // 30.10.8.5, iterators
GHC_INLINE path::iterator::iterator() {} 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) : _first(first)
, _last(last) , _last(last)
, _iter(pos) , _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; bool fromStart = i == _first;
if (i != _last) { if (i != _last) {
// we can only sit on a slash if it is a network name or a root // 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; 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) { if (i != _first) {
--i; --i;
// if this is now the root slash or the trailing slash, we are done, // 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 != '/')) { if (i != _root && (pos != _last || *i != '/')) {
#ifdef GHC_OS_WINDOWS #ifdef GHC_OS_WINDOWS
static const std::string seps = "/:"; static const std::string seps = "/:";
i = std::find_first_of(std::reverse_iterator<std::string::const_iterator>(i), std::reverse_iterator<std::string::const_iterator>(_first), seps.begin(), seps.end()).base(); i = std::find_first_of(std::reverse_iterator<path::impl_string_type::const_iterator>(i), std::reverse_iterator<path::impl_string_type::const_iterator>(_first), seps.begin(), seps.end()).base();
if (i > _first && *i == ':') { if (i > _first && *i == ':') {
i++; i++;
} }
#else #else
i = std::find(std::reverse_iterator<std::string::const_iterator>(i), std::reverse_iterator<std::string::const_iterator>(_first), '/').base(); i = std::find(std::reverse_iterator<path::impl_string_type::const_iterator>(i), std::reverse_iterator<path::impl_string_type::const_iterator>(_first), '/').base();
#endif #endif
// Now we have to check if this is a network name // Now we have to check if this is a network name
if (i - _first == 2 && *_first == '/' && *(_first + 1) == '/') { 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; path current;
ec.clear(); ec.clear();
for (const std::string& part : p) { for (const path::string_type& part : p) {
current /= part; current /= part;
if (current != p.root_name() && current != p.root_path()) { if (current != p.root_name() && current != p.root_path()) {
std::error_code tec; std::error_code tec;
@ -3334,7 +3419,7 @@ GHC_INLINE path current_path(std::error_code& ec)
ec.clear(); ec.clear();
#ifdef GHC_OS_WINDOWS #ifdef GHC_OS_WINDOWS
DWORD pathlen = ::GetCurrentDirectoryW(0, 0); DWORD pathlen = ::GetCurrentDirectoryW(0, 0);
std::unique_ptr<wchar_t[]> buffer(new wchar_t[pathlen + 1]); std::unique_ptr<wchar_t[]> buffer(new wchar_t[size_t(pathlen) + 1]);
if (::GetCurrentDirectoryW(pathlen, buffer.get()) == 0) { if (::GetCurrentDirectoryW(pathlen, buffer.get()) == 0) {
ec = std::error_code(::GetLastError(), std::system_category()); ec = std::error_code(::GetLastError(), std::system_category());
return path(); return path();
@ -4536,22 +4621,26 @@ public:
impl(const path& p, directory_options options) impl(const path& p, directory_options options)
: _base(p) : _base(p)
, _options(options) , _options(options)
, _dirHandle(_base.empty() ? INVALID_HANDLE_VALUE : FindFirstFileW(detail::fromUtf8<std::wstring>((_base / "*").u8string()).c_str(), &_findData)) , _findData{0}
, _current(_dirHandle != INVALID_HANDLE_VALUE ? _base / std::wstring(_findData.cFileName) : filesystem::path()) , _dirHandle(INVALID_HANDLE_VALUE)
{ {
if (_dirHandle == INVALID_HANDLE_VALUE && !p.empty()) { if (!_base.empty()) {
auto error = ::GetLastError(); ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW));
_base = filesystem::path(); if ((_dirHandle = FindFirstFileW(detail::fromUtf8<std::wstring>((_base / "*").u8string()).c_str(), &_findData)) != INVALID_HANDLE_VALUE) {
if (error != ERROR_ACCESS_DENIED || (options & directory_options::skip_permission_denied) == directory_options::none) { if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") {
_ec = std::error_code(::GetLastError(), std::system_category()); increment(_ec);
} }
} else {
else { _current = _base / std::wstring(_findData.cFileName);
if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") { copyToDirEntry(_ec);
increment(_ec); }
} }
else { 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());
}
} }
} }
} }

View File

@ -38,7 +38,9 @@
// namespace fs. // namespace fs.
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_STD_H #ifndef GHC_FILESYSTEM_STD_H
#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include(<filesystem>) #if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include)
#if __has_include(<filesystem>)
#define GHC_USE_STD_FS
#include <filesystem> #include <filesystem>
namespace fs { namespace fs {
using namespace std::filesystem; using namespace std::filesystem;
@ -46,7 +48,9 @@ using ifstream = std::ifstream;
using ofstream = std::ofstream; using ofstream = std::ofstream;
using fstream = std::fstream; using fstream = std::fstream;
} }
#else #endif
#endif
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp> #include <ghc/filesystem.hpp>
namespace fs { namespace fs {
using namespace ghc::filesystem; using namespace ghc::filesystem;

View File

@ -41,7 +41,9 @@
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_STD_FWD_H #ifndef GHC_FILESYSTEM_STD_FWD_H
#define GHC_FILESYSTEM_STD_FWD_H #define GHC_FILESYSTEM_STD_FWD_H
#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include(<filesystem>) #if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include)
#if __has_include(<filesystem>)
#define GHC_USE_STD_FS
#include <filesystem> #include <filesystem>
namespace fs { namespace fs {
using namespace std::filesystem; using namespace std::filesystem;
@ -49,7 +51,9 @@ using ifstream = std::ifstream;
using ofstream = std::ofstream; using ofstream = std::ofstream;
using fstream = std::fstream; using fstream = std::fstream;
} }
#else #endif
#endif
#ifndef GHC_USE_STD_FS
#define GHC_FILESYSTEM_FWD #define GHC_FILESYSTEM_FWD
#include <ghc/filesystem.hpp> #include <ghc/filesystem.hpp>
namespace fs { namespace fs {

View File

@ -39,8 +39,12 @@
// The cpp has to include this before including fs_std_fwd.hpp directly or via a different // The cpp has to include this before including fs_std_fwd.hpp directly or via a different
// header to work. // header to work.
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
#if !(defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include(<filesystem>)) #if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include)
#if __has_include(<filesystem>)
#define GHC_USE_STD_FS
#endif
#endif
#ifndef GHC_USE_STD_FS
#define GHC_FILESYSTEM_IMPLEMENTATION #define GHC_FILESYSTEM_IMPLEMENTATION
#include <ghc/filesystem.hpp> #include <ghc/filesystem.hpp>
#endif #endif

View File

@ -405,7 +405,7 @@ TEST_CASE("30.10.8.4.2 path assignments", "[filesystem][path][fs.path.assign]")
REQUIRE(p1 == p3); REQUIRE(p1 == p3);
p3 = fs::path{"/usr/local"}; p3 = fs::path{"/usr/local"};
REQUIRE(p2 == p3); 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"}; p3 = fs::path::string_type{L"/foo/bar"};
REQUIRE(p1 == p3); REQUIRE(p1 == p3);
p3.assign(fs::path::string_type{L"/usr/local"}); 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]") TEST_CASE("30.10.8.4.6 path native format observers", "[filesystem][path][fs.path.native.obs]")
{ {
#ifdef GHC_OS_WINDOWS #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").native() == fs::path::string_type(L"\u00E4\\\u20AC"));
// CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("ä\\€")); // MSVCs returns local DBCS encoding // CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("ä\\€")); // MSVCs returns local DBCS encoding
#else #else