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
### 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 `<vector>` 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)

View File

@ -1,12 +1,16 @@
#include <iostream>
#include <iomanip>
#include <chrono>
#include <iomanip>
#include <iostream>
#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>
namespace fs = std::filesystem;
#else
#endif
#endif
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem;
#endif
@ -49,11 +53,8 @@ int main(int argc, char* argv[])
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;
}

View File

@ -2,10 +2,14 @@
#include <iomanip>
#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>
namespace fs = std::filesystem;
#else
#endif
#endif
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem;
#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)
#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 @@ public:
class GHC_FS_API_CLASS path
{
public:
using value_type = std::string::value_type;
using string_type = std::basic_string<value_type>;
#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 = '\\';
#else
using value_type = std::string::value_type;
using string_type = std::basic_string<value_type>;
static constexpr value_type preferred_separator = '/';
#endif
// 30.10.10.1 enumeration format
@ -216,11 +227,19 @@ public:
template <typename T1, typename T2 = void>
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>
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>
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
path() noexcept;
path(const path& p);
@ -266,7 +285,7 @@ public:
path& operator+=(const value_type* x);
path& operator+=(value_type x);
template <class Source>
path_type<Source>& operator+=(const Source& x);
path_from_string<Source>& operator+=(const Source& x);
template <class EcharT>
path_type_EcharT<EcharT>& operator+=(EcharT x);
template <class Source>
@ -346,9 +365,11 @@ public:
iterator end() const;
private:
using impl_value_type = std::string::value_type;
using impl_string_type = std::basic_string<impl_value_type>;
friend class directory_iterator;
void append_name(const char* name);
static constexpr value_type generic_separator = '/';
static constexpr impl_value_type generic_separator = '/';
template <typename InputIterator>
class input_iterator_range
{
@ -372,9 +393,13 @@ private:
};
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 @@ public:
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 @@ public:
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 @@ private:
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 @@ private:
//-------------------------------------------------------------------------------------------------
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<unsigned>(S_RJCT) : static_cast<unsigned>((utf8_state_info[category + 16] >> (state << 2)) & 0xf);
@ -1206,12 +1234,15 @@ GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t frag
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())
{
if (sizeof(typename StringType::value_type) == 1) {
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())
{
StringType result(alloc);
result.reserve(utf8String.length());
std::string::const_iterator iter = utf8String.begin();
@ -1219,10 +1250,6 @@ 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;
}
else {
if (codepoint <= 0xffff) {
result += (typename StringType::value_type)codepoint;
}
@ -1231,7 +1258,6 @@ inline StringType fromUtf8(const std::string& utf8String, const typename StringT
result += (typename StringType::value_type)((codepoint >> 10) + 0xd800);
result += (typename StringType::value_type)((codepoint & 0x3ff) + 0xdc00);
}
}
codepoint = 0;
}
else if (utf8_state == S_RJCT) {
@ -1246,13 +1272,38 @@ inline StringType fromUtf8(const std::string& utf8String, const typename StringT
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)
{
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)
{
std::string result;
@ -1277,7 +1328,7 @@ inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicode
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)
{
std::string result;
@ -1287,10 +1338,10 @@ inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicode
return result;
}
template <typename SourceType>
inline std::string toUtf8(const SourceType* unicodeString)
template <typename charT>
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
@ -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 <class Source, typename>
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 <class Source, typename>
@ -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 <class Source>
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 <class InputIterator>
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 <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);
}
@ -2075,7 +2138,7 @@ template <class Source>
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 <class InputIterator>
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<string_type>(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 <class EcharT, class traits, class Allocator>
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
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<std::wstring>(native());
#endif
}
GHC_INLINE std::string path::u8string() const
{
return native();
return native_impl();
}
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
{
return detail::fromUtf8<std::u32string>(native());
return detail::fromUtf8<std::u32string>(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<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 == ':') {
i++;
}
#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
// 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<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) {
ec = std::error_code(::GetLastError(), std::system_category());
return path();
@ -4536,23 +4621,27 @@ public:
impl(const path& p, directory_options options)
: _base(p)
, _options(options)
, _dirHandle(_base.empty() ? INVALID_HANDLE_VALUE : FindFirstFileW(detail::fromUtf8<std::wstring>((_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()) {
if (!_base.empty()) {
ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW));
if ((_dirHandle = FindFirstFileW(detail::fromUtf8<std::wstring>((_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 {
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);
}
else {
copyToDirEntry(_ec);
}
}
}
impl(const impl& other) = delete;

View File

@ -38,7 +38,9 @@
// namespace fs.
//---------------------------------------------------------------------------------------
#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>
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 <ghc/filesystem.hpp>
namespace fs {
using namespace ghc::filesystem;

View File

@ -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(<filesystem>)
#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include)
#if __has_include(<filesystem>)
#define GHC_USE_STD_FS
#include <filesystem>
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 <ghc/filesystem.hpp>
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
// 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
#include <ghc/filesystem.hpp>
#endif

View File

@ -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