292 changes: 265 additions & 27 deletions libcxx/include/filesystem
Original file line number Diff line number Diff line change
Expand Up @@ -568,9 +568,19 @@ struct __can_convert_char<char32_t> {
template <class _ECharT>
typename enable_if<__can_convert_char<_ECharT>::value, bool>::type
__is_separator(_ECharT __e) {
#if defined(_LIBCPP_WIN32API)
return __e == _ECharT('/') || __e == _ECharT('\\');
#else
return __e == _ECharT('/');
#endif
}

#ifndef _LIBCPP_NO_HAS_CHAR8_T
typedef u8string __u8_string;
#else
typedef string __u8_string;
#endif

struct _NullSentinel {};

template <class _Tp>
Expand Down Expand Up @@ -672,6 +682,21 @@ struct __is_pathable<_Tp, false, true, false> : __is_pathable_char_array<_Tp> {
template <class _Tp>
struct __is_pathable<_Tp, false, false, true> : __is_pathable_iter<_Tp> {};

#if defined(_LIBCPP_WIN32API)
typedef wstring __path_string;
typedef wchar_t __path_value;
#else
typedef string __path_string;
typedef char __path_value;
#endif

#if defined(_LIBCPP_WIN32API)
_LIBCPP_FUNC_VIS
size_t __wide_to_char(const wstring&, char*, size_t);
_LIBCPP_FUNC_VIS
size_t __char_to_wide(const string&, wchar_t*, size_t);
#endif

template <class _ECharT>
struct _PathCVT;

Expand All @@ -682,37 +707,60 @@ struct _PathCVT {
"Char type not convertible");

typedef __narrow_to_utf8<sizeof(_ECharT) * __CHAR_BIT__> _Narrower;
#if defined(_LIBCPP_WIN32API)
typedef __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__> _Widener;
#endif

static void __append_range(string& __dest, _ECharT const* __b,
static void __append_range(__path_string& __dest, _ECharT const* __b,
_ECharT const* __e) {
#if defined(_LIBCPP_WIN32API)
string __utf8;
_Narrower()(back_inserter(__utf8), __b, __e);
_Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
#else
_Narrower()(back_inserter(__dest), __b, __e);
#endif
}

template <class _Iter>
static void __append_range(string& __dest, _Iter __b, _Iter __e) {
static void __append_range(__path_string& __dest, _Iter __b, _Iter __e) {
static_assert(!is_same<_Iter, _ECharT*>::value, "Call const overload");
if (__b == __e)
return;
basic_string<_ECharT> __tmp(__b, __e);
#if defined(_LIBCPP_WIN32API)
string __utf8;
_Narrower()(back_inserter(__utf8), __tmp.data(),
__tmp.data() + __tmp.length());
_Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
#else
_Narrower()(back_inserter(__dest), __tmp.data(),
__tmp.data() + __tmp.length());
#endif
}

template <class _Iter>
static void __append_range(string& __dest, _Iter __b, _NullSentinel) {
static void __append_range(__path_string& __dest, _Iter __b, _NullSentinel) {
static_assert(!is_same<_Iter, _ECharT*>::value, "Call const overload");
const _ECharT __sentinel = _ECharT{};
if (*__b == __sentinel)
return;
basic_string<_ECharT> __tmp;
for (; *__b != __sentinel; ++__b)
__tmp.push_back(*__b);
#if defined(_LIBCPP_WIN32API)
string __utf8;
_Narrower()(back_inserter(__utf8), __tmp.data(),
__tmp.data() + __tmp.length());
_Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
#else
_Narrower()(back_inserter(__dest), __tmp.data(),
__tmp.data() + __tmp.length());
#endif
}

template <class _Source>
static void __append_source(string& __dest, _Source const& __s) {
static void __append_source(__path_string& __dest, _Source const& __s) {
using _Traits = __is_pathable<_Source>;
__append_range(__dest, _Traits::__range_begin(__s),
_Traits::__range_end(__s));
Expand All @@ -721,36 +769,132 @@ struct _PathCVT {
#endif // !_LIBCPP_HAS_NO_LOCALIZATION

template <>
struct _PathCVT<char> {
struct _PathCVT<__path_value> {

template <class _Iter>
static typename enable_if<__is_exactly_cpp17_input_iterator<_Iter>::value>::type
__append_range(string& __dest, _Iter __b, _Iter __e) {
__append_range(__path_string& __dest, _Iter __b, _Iter __e) {
for (; __b != __e; ++__b)
__dest.push_back(*__b);
}

template <class _Iter>
static typename enable_if<__is_cpp17_forward_iterator<_Iter>::value>::type
__append_range(string& __dest, _Iter __b, _Iter __e) {
__append_range(__path_string& __dest, _Iter __b, _Iter __e) {
__dest.__append_forward_unsafe(__b, __e);
}

template <class _Iter>
static void __append_range(string& __dest, _Iter __b, _NullSentinel) {
static void __append_range(__path_string& __dest, _Iter __b, _NullSentinel) {
const char __sentinel = char{};
for (; *__b != __sentinel; ++__b)
__dest.push_back(*__b);
}

template <class _Source>
static void __append_source(string& __dest, _Source const& __s) {
static void __append_source(__path_string& __dest, _Source const& __s) {
using _Traits = __is_pathable<_Source>;
__append_range(__dest, _Traits::__range_begin(__s),
_Traits::__range_end(__s));
}
};

#if defined(_LIBCPP_WIN32API)
template <>
struct _PathCVT<char> {

static void
__append_string(__path_string& __dest, const basic_string<char> &__str) {
size_t __size = __char_to_wide(__str, nullptr, 0);
size_t __pos = __dest.size();
__dest.resize(__pos + __size);
__char_to_wide(__str, const_cast<__path_value*>(__dest.data()) + __pos, __size);
}

template <class _Iter>
static typename enable_if<__is_exactly_cpp17_input_iterator<_Iter>::value>::type
__append_range(__path_string& __dest, _Iter __b, _Iter __e) {
basic_string<char> __tmp(__b, __e);
__append_string(__dest, __tmp);
}

template <class _Iter>
static typename enable_if<__is_cpp17_forward_iterator<_Iter>::value>::type
__append_range(__path_string& __dest, _Iter __b, _Iter __e) {
basic_string<char> __tmp(__b, __e);
__append_string(__dest, __tmp);
}

template <class _Iter>
static void __append_range(__path_string& __dest, _Iter __b, _NullSentinel) {
const char __sentinel = char{};
basic_string<char> __tmp;
for (; *__b != __sentinel; ++__b)
__tmp.push_back(*__b);
__append_string(__dest, __tmp);
}

template <class _Source>
static void __append_source(__path_string& __dest, _Source const& __s) {
using _Traits = __is_pathable<_Source>;
__append_range(__dest, _Traits::__range_begin(__s),
_Traits::__range_end(__s));
}
};

template <class _ECharT>
struct _PathExport {
typedef __narrow_to_utf8<sizeof(wchar_t) * __CHAR_BIT__> _Narrower;
typedef __widen_from_utf8<sizeof(_ECharT) * __CHAR_BIT__> _Widener;

template <class _Str>
static void __append(_Str& __dest, const __path_string& __src) {
string __utf8;
_Narrower()(back_inserter(__utf8), __src.data(), __src.data() + __src.size());
_Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
}
};

template <>
struct _PathExport<char> {
template <class _Str>
static void __append(_Str& __dest, const __path_string& __src) {
size_t __size = __wide_to_char(__src, nullptr, 0);
size_t __pos = __dest.size();
__dest.resize(__size);
__wide_to_char(__src, const_cast<char*>(__dest.data()) + __pos, __size);
}
};

template <>
struct _PathExport<wchar_t> {
template <class _Str>
static void __append(_Str& __dest, const __path_string& __src) {
__dest.append(__src.begin(), __src.end());
}
};

template <>
struct _PathExport<char16_t> {
template <class _Str>
static void __append(_Str& __dest, const __path_string& __src) {
__dest.append(__src.begin(), __src.end());
}
};

#ifndef _LIBCPP_NO_HAS_CHAR8_T
template <>
struct _PathExport<char8_t> {
typedef __narrow_to_utf8<sizeof(wchar_t) * __CHAR_BIT__> _Narrower;

template <class _Str>
static void __append(_Str& __dest, const __path_string& __src) {
_Narrower()(back_inserter(__dest), __src.data(), __src.data() + __src.size());
}
};
#endif /* !_LIBCPP_NO_HAS_CHAR8_T */
#endif /* _LIBCPP_WIN32API */

class _LIBCPP_TYPE_VIS path {
template <class _SourceOrIter, class _Tp = path&>
using _EnableIfPathable =
Expand All @@ -763,10 +907,15 @@ class _LIBCPP_TYPE_VIS path {
using _SourceCVT = _PathCVT<_SourceChar<_Tp> >;

public:
#if defined(_LIBCPP_WIN32API)
typedef wchar_t value_type;
static constexpr value_type preferred_separator = L'\\';
#else
typedef char value_type;
typedef basic_string<value_type> string_type;
typedef _VSTD::string_view __string_view;
static constexpr value_type preferred_separator = '/';
#endif
typedef basic_string<value_type> string_type;
typedef basic_string_view<value_type> __string_view;

enum class _LIBCPP_ENUM_VIS format : unsigned char {
auto_format,
Expand Down Expand Up @@ -1000,6 +1149,56 @@ public:

_LIBCPP_INLINE_VISIBILITY operator string_type() const { return __pn_; }

#if defined(_LIBCPP_WIN32API)
_LIBCPP_INLINE_VISIBILITY _VSTD::wstring wstring() const { return __pn_; }

_VSTD::wstring generic_wstring() const { return __pn_; }

#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
template <class _ECharT, class _Traits = char_traits<_ECharT>,
class _Allocator = allocator<_ECharT> >
basic_string<_ECharT, _Traits, _Allocator>
string(const _Allocator& __a = _Allocator()) const {
using _Str = basic_string<_ECharT, _Traits, _Allocator>;
_Str __s(__a);
__s.reserve(__pn_.size());
_PathExport<_ECharT>::__append(__s, __pn_);
return __s;
}

_LIBCPP_INLINE_VISIBILITY _VSTD::string string() const {
return string<char>();
}
_LIBCPP_INLINE_VISIBILITY __u8_string u8string() const {
using _CVT = __narrow_to_utf8<sizeof(wchar_t) * __CHAR_BIT__>;
__u8_string __s;
__s.reserve(__pn_.size());
_CVT()(back_inserter(__s), __pn_.data(), __pn_.data() + __pn_.size());
return __s;
}

_LIBCPP_INLINE_VISIBILITY _VSTD::u16string u16string() const {
return string<char16_t>();
}
_LIBCPP_INLINE_VISIBILITY _VSTD::u32string u32string() const {
return string<char32_t>();
}

// generic format observers
template <class _ECharT, class _Traits = char_traits<_ECharT>,
class _Allocator = allocator<_ECharT> >
basic_string<_ECharT, _Traits, _Allocator>
generic_string(const _Allocator& __a = _Allocator()) const {
return string<_ECharT, _Traits, _Allocator>(__a);
}

_VSTD::string generic_string() const { return generic_string<char>(); }
_VSTD::u16string generic_u16string() const { return generic_string<char16_t>(); }
_VSTD::u32string generic_u32string() const { return generic_string<char32_t>(); }
__u8_string generic_u8string() const { return u8string(); }
#endif /* !_LIBCPP_HAS_NO_LOCALIZATION */
#else /* _LIBCPP_WIN32API */

_LIBCPP_INLINE_VISIBILITY _VSTD::string string() const { return __pn_; }
#ifndef _LIBCPP_NO_HAS_CHAR8_T
_LIBCPP_INLINE_VISIBILITY _VSTD::u8string u8string() const { return _VSTD::u8string(__pn_.begin(), __pn_.end()); }
Expand Down Expand Up @@ -1029,7 +1228,7 @@ public:
_LIBCPP_INLINE_VISIBILITY _VSTD::u32string u32string() const {
return string<char32_t>();
}
#endif
#endif /* !_LIBCPP_HAS_NO_LOCALIZATION */

// generic format observers
_VSTD::string generic_string() const { return __pn_; }
Expand All @@ -1050,7 +1249,8 @@ public:
_VSTD::wstring generic_wstring() const { return string<wchar_t>(); }
_VSTD::u16string generic_u16string() const { return string<char16_t>(); }
_VSTD::u32string generic_u32string() const { return string<char32_t>(); }
#endif
#endif /* !_LIBCPP_HAS_NO_LOCALIZATION */
#endif /* !_LIBCPP_WIN32API */

private:
int __compare(__string_view) const;
Expand Down Expand Up @@ -1157,8 +1357,8 @@ public:
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
template <class _CharT, class _Traits>
_LIBCPP_INLINE_VISIBILITY friend
typename enable_if<is_same<_CharT, char>::value &&
is_same<_Traits, char_traits<char> >::value,
typename enable_if<is_same<_CharT, value_type>::value &&
is_same<_Traits, char_traits<value_type> >::value,
basic_ostream<_CharT, _Traits>&>::type
operator<<(basic_ostream<_CharT, _Traits>& __os, const path& __p) {
__os << _VSTD::__quoted(__p.native());
Expand All @@ -1167,8 +1367,8 @@ public:

template <class _CharT, class _Traits>
_LIBCPP_INLINE_VISIBILITY friend
typename enable_if<!is_same<_CharT, char>::value ||
!is_same<_Traits, char_traits<char> >::value,
typename enable_if<!is_same<_CharT, value_type>::value ||
!is_same<_Traits, char_traits<value_type> >::value,
basic_ostream<_CharT, _Traits>&>::type
operator<<(basic_ostream<_CharT, _Traits>& __os, const path& __p) {
__os << _VSTD::__quoted(__p.string<_CharT, _Traits>());
Expand Down Expand Up @@ -1226,32 +1426,70 @@ inline _LIBCPP_INLINE_VISIBILITY void swap(path& __lhs, path& __rhs) noexcept {
_LIBCPP_FUNC_VIS
size_t hash_value(const path& __p) noexcept;

template <class _Source>
template <class _InputIt>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_DEPRECATED_WITH_CHAR8_T
typename enable_if<__is_pathable<_Source>::value, path>::type
u8path(const _Source& __s) {
typename enable_if<__is_pathable<_InputIt>::value, path>::type
u8path(_InputIt __f, _InputIt __l) {
static_assert(
#ifndef _LIBCPP_NO_HAS_CHAR8_T
is_same<typename __is_pathable<_Source>::__char_type, char8_t>::value ||
is_same<typename __is_pathable<_InputIt>::__char_type, char8_t>::value ||
#endif
is_same<typename __is_pathable<_Source>::__char_type, char>::value,
"u8path(Source const&) requires Source have a character type of type "
"'char' or 'char8_t'");
return path(__s);
is_same<typename __is_pathable<_InputIt>::__char_type, char>::value,
"u8path(Iter, Iter) requires Iter have a value_type of type 'char'"
" or 'char8_t'");
#if defined(_LIBCPP_WIN32API)
string __tmp(__f, __l);
using _CVT = __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__>;
_VSTD::wstring __w;
__w.reserve(__tmp.size());
_CVT()(back_inserter(__w), __tmp.data(), __tmp.data() + __tmp.size());
return path(__w);
#else
return path(__f, __l);
#endif /* !_LIBCPP_WIN32API */
}

#if defined(_LIBCPP_WIN32API)
template <class _InputIt>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_DEPRECATED_WITH_CHAR8_T
typename enable_if<__is_pathable<_InputIt>::value, path>::type
u8path(_InputIt __f, _InputIt __l) {
u8path(_InputIt __f, _NullSentinel) {
static_assert(
#ifndef _LIBCPP_NO_HAS_CHAR8_T
is_same<typename __is_pathable<_InputIt>::__char_type, char8_t>::value ||
#endif
is_same<typename __is_pathable<_InputIt>::__char_type, char>::value,
"u8path(Iter, Iter) requires Iter have a value_type of type 'char'"
" or 'char8_t'");
return path(__f, __l);
string __tmp;
const char __sentinel = char{};
for (; *__f != __sentinel; ++__f)
__tmp.push_back(*__f);
using _CVT = __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__>;
_VSTD::wstring __w;
__w.reserve(__tmp.size());
_CVT()(back_inserter(__w), __tmp.data(), __tmp.data() + __tmp.size());
return path(__w);
}
#endif /* _LIBCPP_WIN32API */

template <class _Source>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_DEPRECATED_WITH_CHAR8_T
typename enable_if<__is_pathable<_Source>::value, path>::type
u8path(const _Source& __s) {
static_assert(
#ifndef _LIBCPP_NO_HAS_CHAR8_T
is_same<typename __is_pathable<_Source>::__char_type, char8_t>::value ||
#endif
is_same<typename __is_pathable<_Source>::__char_type, char>::value,
"u8path(Source const&) requires Source have a character type of type "
"'char' or 'char8_t'");
#if defined(_LIBCPP_WIN32API)
using _Traits = __is_pathable<_Source>;
return u8path(__unwrap_iter(_Traits::__range_begin(__s)), __unwrap_iter(_Traits::__range_end(__s)));
#else
return path(__s);
#endif
}

class _LIBCPP_TYPE_VIS path::iterator {
Expand Down
63 changes: 39 additions & 24 deletions libcxx/src/filesystem/directory_iterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "__config"
#if defined(_LIBCPP_WIN32API)
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#else
#include <dirent.h>
Expand Down Expand Up @@ -72,16 +73,20 @@ static pair<string_view, file_type> posix_readdir(DIR* dir_stream,
}
}
#else
// defined(_LIBCPP_WIN32API)

static file_type get_file_type(const WIN32_FIND_DATA& data) {
//auto attrs = data.dwFileAttributes;
// FIXME(EricWF)
return file_type::unknown;
static file_type get_file_type(const WIN32_FIND_DATAW& data) {
if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
return file_type::symlink;
if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
return file_type::directory;
return file_type::regular;
}
static uintmax_t get_file_size(const WIN32_FIND_DATA& data) {
return (data.nFileSizeHigh * (MAXDWORD + 1)) + data.nFileSizeLow;
static uintmax_t get_file_size(const WIN32_FIND_DATAW& data) {
return (static_cast<uint64_t>(data.nFileSizeHigh) << 32) + data.nFileSizeLow;
}
static file_time_type get_write_time(const WIN32_FIND_DATA& data) {
static file_time_type get_write_time(const WIN32_FIND_DATAW& data) {
ULARGE_INTEGER tmp;
const FILETIME& time = data.ftLastWriteTime;
tmp.u.LowPart = time.dwLowDateTime;
Expand Down Expand Up @@ -110,15 +115,21 @@ class __dir_stream {

__dir_stream(const path& root, directory_options opts, error_code& ec)
: __stream_(INVALID_HANDLE_VALUE), __root_(root) {
__stream_ = ::FindFirstFile(root.c_str(), &__data_);
if (root.native().empty()) {
ec = make_error_code(errc::no_such_file_or_directory);
return;
}
__stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_);
if (__stream_ == INVALID_HANDLE_VALUE) {
ec = error_code(::GetLastError(), generic_category());
ec = detail::make_windows_error(GetLastError());
const bool ignore_permission_denied =
bool(opts & directory_options::skip_permission_denied);
if (ignore_permission_denied && ec.value() == ERROR_ACCESS_DENIED)
ec.clear();
return;
}
if (!assign())
advance(ec);
}

~__dir_stream() noexcept {
Expand All @@ -130,35 +141,39 @@ class __dir_stream {
bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }

bool advance(error_code& ec) {
while (::FindNextFile(__stream_, &__data_)) {
if (!strcmp(__data_.cFileName, ".") || strcmp(__data_.cFileName, ".."))
continue;
// FIXME: Cache more of this
//directory_entry::__cached_data cdata;
//cdata.__type_ = get_file_type(__data_);
//cdata.__size_ = get_file_size(__data_);
//cdata.__write_time_ = get_write_time(__data_);
__entry_.__assign_iter_entry(
__root_ / __data_.cFileName,
directory_entry::__create_iter_result(detail::get_file_type(__data)));
return true;
while (::FindNextFileW(__stream_, &__data_)) {
if (assign())
return true;
}
ec = error_code(::GetLastError(), generic_category());
close();
return false;
}

bool assign() {
if (!wcscmp(__data_.cFileName, L".") || !wcscmp(__data_.cFileName, L".."))
return false;
// FIXME: Cache more of this
//directory_entry::__cached_data cdata;
//cdata.__type_ = get_file_type(__data_);
//cdata.__size_ = get_file_size(__data_);
//cdata.__write_time_ = get_write_time(__data_);
__entry_.__assign_iter_entry(
__root_ / __data_.cFileName,
directory_entry::__create_iter_result(detail::get_file_type(__data_)));
return true;
}

private:
error_code close() noexcept {
error_code ec;
if (!::FindClose(__stream_))
ec = error_code(::GetLastError(), generic_category());
ec = detail::make_windows_error(GetLastError());
__stream_ = INVALID_HANDLE_VALUE;
return ec;
}

HANDLE __stream_{INVALID_HANDLE_VALUE};
WIN32_FIND_DATA __data_;
WIN32_FIND_DATAW __data_;

public:
path __root_;
Expand Down
40 changes: 33 additions & 7 deletions libcxx/src/filesystem/filesystem_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
#include "cstdlib"
#include "ctime"

#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/time.h> // for ::utimes as used in __last_write_time
#include <fcntl.h> /* values for fchmodat */
#if !defined(_LIBCPP_WIN32API)
# include <unistd.h>
# include <sys/stat.h>
# include <sys/statvfs.h>
# include <sys/time.h> // for ::utimes as used in __last_write_time
# include <fcntl.h> /* values for fchmodat */
#endif

#include "../include/apple_availability.h"

Expand All @@ -38,9 +40,21 @@
#pragma GCC diagnostic ignored "-Wunused-function"
#endif

#if defined(_LIBCPP_WIN32API)
#define PS(x) (L##x)
#else
#define PS(x) (x)
#endif

_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM

namespace detail {

#if defined(_LIBCPP_WIN32API)
// Non anonymous, to allow access from two translation units.
errc __win_err_to_errc(int err);
#endif

namespace {

static string format_string_imp(const char* msg, ...) {
Expand Down Expand Up @@ -94,8 +108,8 @@ static string format_string_imp(const char* msg, ...) {
return result;
}

const char* unwrap(string const& s) { return s.c_str(); }
const char* unwrap(path const& p) { return p.native().c_str(); }
const path::value_type* unwrap(path::string_type const& s) { return s.c_str(); }
const path::value_type* unwrap(path const& p) { return p.native().c_str(); }
template <class Arg>
Arg const& unwrap(Arg const& a) {
static_assert(!is_class<Arg>::value, "cannot pass class here");
Expand All @@ -112,6 +126,12 @@ error_code capture_errno() {
return error_code(errno, generic_category());
}

#if defined(_LIBCPP_WIN32API)
error_code make_windows_error(int err) {
return make_error_code(__win_err_to_errc(err));
}
#endif

template <class T>
T error_value();
template <>
Expand All @@ -120,6 +140,12 @@ template <>
bool error_value<bool>() {
return false;
}
#if __SIZEOF_SIZE_T__ != __SIZEOF_LONG_LONG__
template <>
size_t error_value<size_t>() {
return size_t(-1);
}
#endif
template <>
uintmax_t error_value<uintmax_t>() {
return uintmax_t(-1);
Expand Down
166 changes: 139 additions & 27 deletions libcxx/src/filesystem/operations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@

#include "filesystem_common.h"

#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#if defined(_LIBCPP_WIN32API)
# define WIN32_LEAN_AND_MEAN
# define NOMINMAX
# include <windows.h>
#else
# include <unistd.h>
# include <sys/stat.h>
# include <sys/statvfs.h>
#endif
#include <time.h>
#include <fcntl.h> /* values for fchmodat */

Expand Down Expand Up @@ -171,11 +177,14 @@ struct PathParser {
switch (State) {
case PS_BeforeBegin:
case PS_AtEnd:
return "";
return PS("");
case PS_InRootDir:
return "/";
if (RawEntry[0] == '\\')
return PS("\\");
else
return PS("/");
case PS_InTrailingSep:
return "";
return PS("");
case PS_InRootName:
case PS_InFilenames:
return RawEntry;
Expand Down Expand Up @@ -283,8 +292,8 @@ struct PathParser {
};

string_view_pair separate_filename(string_view_t const& s) {
if (s == "." || s == ".." || s.empty())
return string_view_pair{s, ""};
if (s == PS(".") || s == PS("..") || s.empty())
return string_view_pair{s, PS("")};
auto pos = s.find_last_of('.');
if (pos == string_view_t::npos || pos == 0)
return string_view_pair{s, string_view_t{}};
Expand All @@ -300,6 +309,73 @@ string_view_t createView(PosPtr S, PosPtr E) noexcept {

// POSIX HELPERS

#if defined(_LIBCPP_WIN32API)
namespace detail {

errc __win_err_to_errc(int err) {
constexpr struct {
DWORD win;
errc errc;
} win_error_mapping[] = {
{ERROR_ACCESS_DENIED, errc::permission_denied},
{ERROR_ALREADY_EXISTS, errc::file_exists},
{ERROR_BAD_NETPATH, errc::no_such_file_or_directory},
{ERROR_BAD_UNIT, errc::no_such_device},
{ERROR_BROKEN_PIPE, errc::broken_pipe},
{ERROR_BUFFER_OVERFLOW, errc::filename_too_long},
{ERROR_BUSY, errc::device_or_resource_busy},
{ERROR_BUSY_DRIVE, errc::device_or_resource_busy},
{ERROR_CANNOT_MAKE, errc::permission_denied},
{ERROR_CANTOPEN, errc::io_error},
{ERROR_CANTREAD, errc::io_error},
{ERROR_CANTWRITE, errc::io_error},
{ERROR_CURRENT_DIRECTORY, errc::permission_denied},
{ERROR_DEV_NOT_EXIST, errc::no_such_device},
{ERROR_DEVICE_IN_USE, errc::device_or_resource_busy},
{ERROR_DIR_NOT_EMPTY, errc::directory_not_empty},
{ERROR_DIRECTORY, errc::invalid_argument},
{ERROR_DISK_FULL, errc::no_space_on_device},
{ERROR_FILE_EXISTS, errc::file_exists},
{ERROR_FILE_NOT_FOUND, errc::no_such_file_or_directory},
{ERROR_HANDLE_DISK_FULL, errc::no_space_on_device},
{ERROR_INVALID_ACCESS, errc::permission_denied},
{ERROR_INVALID_DRIVE, errc::no_such_device},
{ERROR_INVALID_FUNCTION, errc::function_not_supported},
{ERROR_INVALID_HANDLE, errc::invalid_argument},
{ERROR_INVALID_NAME, errc::no_such_file_or_directory},
{ERROR_INVALID_PARAMETER, errc::invalid_argument},
{ERROR_LOCK_VIOLATION, errc::no_lock_available},
{ERROR_LOCKED, errc::no_lock_available},
{ERROR_NEGATIVE_SEEK, errc::invalid_argument},
{ERROR_NOACCESS, errc::permission_denied},
{ERROR_NOT_ENOUGH_MEMORY, errc::not_enough_memory},
{ERROR_NOT_READY, errc::resource_unavailable_try_again},
{ERROR_NOT_SAME_DEVICE, errc::cross_device_link},
{ERROR_NOT_SUPPORTED, errc::not_supported},
{ERROR_OPEN_FAILED, errc::io_error},
{ERROR_OPEN_FILES, errc::device_or_resource_busy},
{ERROR_OPERATION_ABORTED, errc::operation_canceled},
{ERROR_OUTOFMEMORY, errc::not_enough_memory},
{ERROR_PATH_NOT_FOUND, errc::no_such_file_or_directory},
{ERROR_READ_FAULT, errc::io_error},
{ERROR_REPARSE_TAG_INVALID, errc::invalid_argument},
{ERROR_RETRY, errc::resource_unavailable_try_again},
{ERROR_SEEK, errc::io_error},
{ERROR_SHARING_VIOLATION, errc::permission_denied},
{ERROR_TOO_MANY_OPEN_FILES, errc::too_many_files_open},
{ERROR_WRITE_FAULT, errc::io_error},
{ERROR_WRITE_PROTECT, errc::permission_denied},
};

for (const auto &pair : win_error_mapping)
if (pair.win == static_cast<DWORD>(err))
return pair.errc;
return errc::invalid_argument;
}

} // namespace detail
#endif

namespace detail {
namespace {

Expand Down Expand Up @@ -495,19 +571,25 @@ _FilesystemClock::time_point _FilesystemClock::now() noexcept {

filesystem_error::~filesystem_error() {}

#if defined(_LIBCPP_WIN32API)
#define PS_FMT "%ls"
#else
#define PS_FMT "%s"
#endif

void filesystem_error::__create_what(int __num_paths) {
const char* derived_what = system_error::what();
__storage_->__what_ = [&]() -> string {
const char* p1 = path1().native().empty() ? "\"\"" : path1().c_str();
const char* p2 = path2().native().empty() ? "\"\"" : path2().c_str();
const path::value_type* p1 = path1().native().empty() ? PS("\"\"") : path1().c_str();
const path::value_type* p2 = path2().native().empty() ? PS("\"\"") : path2().c_str();
switch (__num_paths) {
default:
return detail::format_string("filesystem error: %s", derived_what);
case 1:
return detail::format_string("filesystem error: %s [%s]", derived_what,
return detail::format_string("filesystem error: %s [" PS_FMT "]", derived_what,
p1);
case 2:
return detail::format_string("filesystem error: %s [%s] [%s]",
return detail::format_string("filesystem error: %s [" PS_FMT "] [" PS_FMT "]",
derived_what, p1, p2);
}
}();
Expand Down Expand Up @@ -1222,10 +1304,10 @@ path __temp_directory_path(error_code* ec) {
error_code m_ec;
file_status st = detail::posix_stat(p, &m_ec);
if (!status_known(st))
return err.report(m_ec, "cannot access path \"%s\"", p);
return err.report(m_ec, "cannot access path \"" PS_FMT "\"", p);

if (!exists(st) || !is_directory(st))
return err.report(errc::not_a_directory, "path \"%s\" is not a directory",
return err.report(errc::not_a_directory, "path \"" PS_FMT "\" is not a directory",
p);

return p;
Expand Down Expand Up @@ -1281,7 +1363,7 @@ path& path::replace_extension(path const& replacement) {
}
if (!replacement.empty()) {
if (replacement.native()[0] != '.') {
__pn_ += ".";
__pn_ += PS(".");
}
__pn_.append(replacement.__pn_);
}
Expand Down Expand Up @@ -1403,11 +1485,11 @@ enum PathPartKind : unsigned char {
static PathPartKind ClassifyPathPart(string_view_t Part) {
if (Part.empty())
return PK_TrailingSep;
if (Part == ".")
if (Part == PS("."))
return PK_Dot;
if (Part == "..")
if (Part == PS(".."))
return PK_DotDot;
if (Part == "/")
if (Part == PS("/"))
return PK_RootSep;
return PK_Filename;
}
Expand Down Expand Up @@ -1456,7 +1538,7 @@ path path::lexically_normal() const {
NewPathSize -= Parts.back().first.size();
Parts.pop_back();
} else if (LastKind != PK_RootSep)
AddPart(PK_DotDot, "..");
AddPart(PK_DotDot, PS(".."));
MaybeNeedTrailingSep = LastKind == PK_Filename;
break;
}
Expand All @@ -1471,7 +1553,7 @@ path path::lexically_normal() const {
}
// [fs.path.generic]p6.8: If the path is empty, add a dot.
if (Parts.empty())
return ".";
return PS(".");

// [fs.path.generic]p6.7: If the last filename is dot-dot, remove any
// trailing directory-separator.
Expand All @@ -1483,7 +1565,7 @@ path path::lexically_normal() const {
Result /= PK.first;

if (NeedTrailingSep)
Result /= "";
Result /= PS("");

return Result;
}
Expand All @@ -1492,9 +1574,9 @@ static int DetermineLexicalElementCount(PathParser PP) {
int Count = 0;
for (; PP; ++PP) {
auto Elem = *PP;
if (Elem == "..")
if (Elem == PS(".."))
--Count;
else if (Elem != "." && Elem != "")
else if (Elem != PS(".") && Elem != PS(""))
++Count;
}
return Count;
Expand Down Expand Up @@ -1541,15 +1623,15 @@ path path::lexically_relative(const path& base) const {
return {};

// if n == 0 and (a == end() || a->empty()), returns path("."); otherwise
if (ElemCount == 0 && (PP.atEnd() || *PP == ""))
return ".";
if (ElemCount == 0 && (PP.atEnd() || *PP == PS("")))
return PS(".");

// return a path constructed with 'n' dot-dot elements, followed by the the
// elements of '*this' after the mismatch.
path Result;
// FIXME: Reserve enough room in Result that it won't have to re-allocate.
while (ElemCount--)
Result /= "..";
Result /= PS("..");
for (; PP; ++PP)
Result /= *PP;
return Result;
Expand All @@ -1562,7 +1644,7 @@ static int CompareRootName(PathParser *LHS, PathParser *RHS) {
return 0;

auto GetRootName = [](PathParser *Parser) -> string_view_t {
return Parser->inRootName() ? **Parser : "";
return Parser->inRootName() ? **Parser : PS("");
};
int res = GetRootName(LHS).compare(GetRootName(RHS));
ConsumeRootName(LHS);
Expand Down Expand Up @@ -1671,6 +1753,36 @@ path::iterator& path::iterator::__decrement() {
return *this;
}

#if defined(_LIBCPP_WIN32API)
////////////////////////////////////////////////////////////////////////////
// Windows path conversions
size_t __wide_to_char(const wstring &str, char *out, size_t outlen) {
if (str.empty())
return 0;
ErrorHandler<size_t> err("__wide_to_char", nullptr);
UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
BOOL used_default = FALSE;
int ret = WideCharToMultiByte(codepage, 0, str.data(), str.size(), out,
outlen, nullptr, &used_default);
if (ret <= 0 || used_default)
return err.report(errc::illegal_byte_sequence);
return ret;
}

size_t __char_to_wide(const string &str, wchar_t *out, size_t outlen) {
if (str.empty())
return 0;
ErrorHandler<size_t> err("__char_to_wide", nullptr);
UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
int ret = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, str.data(),
str.size(), out, outlen);
if (ret <= 0)
return err.report(errc::illegal_byte_sequence);
return ret;
}
#endif


///////////////////////////////////////////////////////////////////////////////
// directory entry definitions
///////////////////////////////////////////////////////////////////////////////
Expand Down