summaryrefslogtreecommitdiffstats
path: root/PATCHES/0019-gcc-8-branch-Backport-patches-for-std-filesystem-from-master.patch
diff options
context:
space:
mode:
Diffstat (limited to 'PATCHES/0019-gcc-8-branch-Backport-patches-for-std-filesystem-from-master.patch')
-rw-r--r--PATCHES/0019-gcc-8-branch-Backport-patches-for-std-filesystem-from-master.patch2760
1 files changed, 2760 insertions, 0 deletions
diff --git a/PATCHES/0019-gcc-8-branch-Backport-patches-for-std-filesystem-from-master.patch b/PATCHES/0019-gcc-8-branch-Backport-patches-for-std-filesystem-from-master.patch
new file mode 100644
index 0000000..41969cf
--- /dev/null
+++ b/PATCHES/0019-gcc-8-branch-Backport-patches-for-std-filesystem-from-master.patch
@@ -0,0 +1,2760 @@
+From eda88e06651c9293012212b665ca592c85bef645 Mon Sep 17 00:00:00 2001
+From: Liu Hao <lh_mouse@126.com>
+Date: Sat, 26 Jan 2019 15:14:38 +0800
+Subject: [PATCH] Backport patches for std::filesystem from master.
+
+This is done by running the following commands on `gcc-8-branch`:
+
+ git cherry-pick 861db1097d3f 2fd48392d0a4 70fea18e0bbb d4fd5c4964cf
+ git checkout master -- libstdc++-v3/src/filesystem/ \
+ libstdc++-v3/include/bits/fs_\* libstdc++-v3/include/std/filesystem
+
+Testsuites and change logs were discarded to reduce the amount of
+modification.
+
+Signed-off-by: Liu Hao <lh_mouse@126.com>
+---
+ libstdc++-v3/config.h.in | 12 +
+ libstdc++-v3/config/io/basic_file_stdio.cc | 33 ++
+ libstdc++-v3/config/io/basic_file_stdio.h | 5 +
+ libstdc++-v3/configure | 35 ++
+ libstdc++-v3/configure.ac | 2 +
+ libstdc++-v3/crossconfig.m4 | 1 +
+ libstdc++-v3/include/bits/fs_dir.h | 14 +-
+ libstdc++-v3/include/bits/fs_path.h | 250 +++++++-------
+ libstdc++-v3/include/bits/fstream.tcc | 36 ++
+ .../include/experimental/bits/fs_path.h | 74 ++--
+ libstdc++-v3/include/std/fstream | 119 +++++++
+ libstdc++-v3/src/filesystem/dir-common.h | 56 ++-
+ libstdc++-v3/src/filesystem/dir.cc | 5 +-
+ libstdc++-v3/src/filesystem/ops-common.h | 105 +++++-
+ libstdc++-v3/src/filesystem/ops.cc | 186 +++++-----
+ libstdc++-v3/src/filesystem/path.cc | 24 +-
+ libstdc++-v3/src/filesystem/std-dir.cc | 5 +-
+ libstdc++-v3/src/filesystem/std-ops.cc | 318 ++++++++++--------
+ libstdc++-v3/src/filesystem/std-path.cc | 123 ++++---
+ 19 files changed, 942 insertions(+), 461 deletions(-)
+
+diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in
+index 765cedc6edf..3fb685ce9aa 100644
+--- a/libstdc++-v3/config.h.in
++++ b/libstdc++-v3/config.h.in
+@@ -264,6 +264,9 @@
+ /* Only used in build directory testsuite_hooks.h. */
+ #undef HAVE_LIMIT_VMEM
+
++/* Define to 1 if you have the `link' function. */
++#undef HAVE_LINK
++
+ /* Define if futex syscall is available. */
+ #undef HAVE_LINUX_FUTEX
+
+@@ -339,6 +342,9 @@
+ /* Define to 1 if you have the `quick_exit' function. */
+ #undef HAVE_QUICK_EXIT
+
++/* Define to 1 if you have the `readlink' function. */
++#undef HAVE_READLINK
++
+ /* Define to 1 if you have the `setenv' function. */
+ #undef HAVE_SETENV
+
+@@ -408,6 +414,9 @@
+ /* Define if strxfrm_l is available in <string.h>. */
+ #undef HAVE_STRXFRM_L
+
++/* Define to 1 if you have the `symlink' function. */
++#undef HAVE_SYMLINK
++
+ /* Define to 1 if the target runtime linker supports binding the same symbol
+ to different versions. */
+ #undef HAVE_SYMVER_SYMBOL_RENAMING_RUNTIME_SUPPORT
+@@ -706,6 +715,9 @@
+ /* Define to 1 if you have the `_tanl' function. */
+ #undef HAVE__TANL
+
++/* Define to 1 if you have the `_wfopen' function. */
++#undef HAVE__WFOPEN
++
+ /* Define to 1 if you have the `__cxa_thread_atexit' function. */
+ #undef HAVE___CXA_THREAD_ATEXIT
+
+diff --git a/libstdc++-v3/config/io/basic_file_stdio.cc b/libstdc++-v3/config/io/basic_file_stdio.cc
+index a46814802cd..09ccd6a4788 100644
+--- a/libstdc++-v3/config/io/basic_file_stdio.cc
++++ b/libstdc++-v3/config/io/basic_file_stdio.cc
+@@ -249,6 +249,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ return __ret;
+ }
+
++#if _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
++ __basic_file<char>*
++ __basic_file<char>::open(const wchar_t* __name, ios_base::openmode __mode)
++ {
++ __basic_file* __ret = NULL;
++ const char* __c_mode = fopen_mode(__mode);
++ if (__c_mode && !this->is_open())
++ {
++ wchar_t __wc_mode[4] = { };
++ int __i = 0;
++ do
++ {
++ switch(__c_mode[__i]) {
++ case 'a': __wc_mode[__i] = L'a'; break;
++ case 'b': __wc_mode[__i] = L'b'; break;
++ case 'r': __wc_mode[__i] = L'r'; break;
++ case 'w': __wc_mode[__i] = L'w'; break;
++ case '+': __wc_mode[__i] = L'+'; break;
++ default: return __ret;
++ }
++ }
++ while (__c_mode[++__i]);
++
++ if ((_M_cfile = _wfopen(__name, __wc_mode)))
++ {
++ _M_cfile_created = true;
++ __ret = this;
++ }
++ }
++ return __ret;
++ }
++#endif
++
+ bool
+ __basic_file<char>::is_open() const throw ()
+ { return _M_cfile != 0; }
+diff --git a/libstdc++-v3/config/io/basic_file_stdio.h b/libstdc++-v3/config/io/basic_file_stdio.h
+index 58f24f670f6..3c857272c57 100644
+--- a/libstdc++-v3/config/io/basic_file_stdio.h
++++ b/libstdc++-v3/config/io/basic_file_stdio.h
+@@ -84,6 +84,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ __basic_file*
+ open(const char* __name, ios_base::openmode __mode, int __prot = 0664);
+
++#if _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
++ __basic_file*
++ open(const wchar_t* __name, ios_base::openmode __mode);
++#endif
++
+ __basic_file*
+ sys_open(__c_file* __file, ios_base::openmode);
+
+diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure
+index 5535bfa2b5a..b9883d413f6 100755
+--- a/libstdc++-v3/configure
++++ b/libstdc++-v3/configure
+@@ -28127,6 +28127,17 @@ eval as_val=\$$as_ac_var
+ #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+ _ACEOF
+
++fi
++done
++
++ for ac_func in _wfopen
++do :
++ ac_fn_c_check_func "$LINENO" "_wfopen" "ac_cv_func__wfopen"
++if test "x$ac_cv_func__wfopen" = x""yes; then :
++ cat >>confdefs.h <<_ACEOF
++#define HAVE__WFOPEN 1
++_ACEOF
++
+ fi
+ done
+
+@@ -66122,6 +66133,17 @@ eval as_val=\$$as_ac_var
+ #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+ _ACEOF
+
++fi
++done
++
++ for ac_func in _wfopen
++do :
++ ac_fn_c_check_func "$LINENO" "_wfopen" "ac_cv_func__wfopen"
++if test "x$ac_cv_func__wfopen" = x""yes; then :
++ cat >>confdefs.h <<_ACEOF
++#define HAVE__WFOPEN 1
++_ACEOF
++
+ fi
+ done
+
+@@ -80025,6 +80047,19 @@ _ACEOF
+
+ fi
+
++done
++
++for ac_func in link readlink symlink
++do :
++ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
++ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
++eval as_val=\$$as_ac_var
++ if test "x$as_val" = x""yes; then :
++ cat >>confdefs.h <<_ACEOF
++#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
++_ACEOF
++
++fi
+ done
+
+
+diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac
+index 0ef96270c9c..dde1c4da944 100644
+--- a/libstdc++-v3/configure.ac
++++ b/libstdc++-v3/configure.ac
+@@ -263,6 +263,7 @@ if $GLIBCXX_IS_NATIVE; then
+
+ AC_CHECK_FUNCS(__cxa_thread_atexit_impl __cxa_thread_atexit)
+ AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc)
++ AC_CHECK_FUNCS(_wfopen)
+
+ # For iconv support.
+ AM_ICONV
+@@ -419,6 +420,7 @@ GLIBCXX_CHECK_GTHREADS
+
+ # For Filesystem TS.
+ AC_CHECK_HEADERS([fcntl.h dirent.h sys/statvfs.h utime.h])
++AC_CHECK_FUNCS(link readlink symlink)
+ GLIBCXX_ENABLE_FILESYSTEM_TS
+ GLIBCXX_CHECK_FILESYSTEM_DEPS
+
+diff --git a/libstdc++-v3/crossconfig.m4 b/libstdc++-v3/crossconfig.m4
+index cb6e3afff3d..669d87f7602 100644
+--- a/libstdc++-v3/crossconfig.m4
++++ b/libstdc++-v3/crossconfig.m4
+@@ -199,6 +199,7 @@ case "${host}" in
+ GLIBCXX_CHECK_MATH_SUPPORT
+ GLIBCXX_CHECK_STDLIB_SUPPORT
+ AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc)
++ AC_CHECK_FUNCS(_wfopen)
+ ;;
+ *-netbsd*)
+ SECTION_FLAGS='-ffunction-sections -fdata-sections'
+diff --git a/libstdc++-v3/include/bits/fs_dir.h b/libstdc++-v3/include/bits/fs_dir.h
+index 9ee1cb66b61..6b332e171cf 100644
+--- a/libstdc++-v3/include/bits/fs_dir.h
++++ b/libstdc++-v3/include/bits/fs_dir.h
+@@ -138,13 +138,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ refresh(__ec);
+ }
+
+- void
+- refresh()
+- { _M_type = symlink_status().type(); }
+-
+- void
+- refresh(error_code& __ec) noexcept
+- { _M_type = symlink_status(__ec).type(); }
++ void refresh() { _M_type = symlink_status().type(); }
++ void refresh(error_code& __ec) { _M_type = symlink_status(__ec).type(); }
+
+ // observers
+ const filesystem::path& path() const noexcept { return _M_path; }
+@@ -318,10 +313,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ _M_file_type(error_code& __ec) const noexcept
+ {
+ if (_M_type != file_type::none && _M_type != file_type::symlink)
+- {
+- __ec.clear();
+- return _M_type;
+- }
++ return _M_type;
+ return status(__ec).type();
+ }
+
+diff --git a/libstdc++-v3/include/bits/fs_path.h b/libstdc++-v3/include/bits/fs_path.h
+index fb85d489fd8..e3938d06d59 100644
+--- a/libstdc++-v3/include/bits/fs_path.h
++++ b/libstdc++-v3/include/bits/fs_path.h
+@@ -37,11 +37,11 @@
+ #include <vector>
+ #include <locale>
+ #include <iosfwd>
++#include <iomanip>
+ #include <codecvt>
+ #include <string_view>
+ #include <system_error>
+ #include <bits/stl_algobase.h>
+-#include <bits/quoted_string.h>
+ #include <bits/locale_conv.h>
+
+ #if defined(_WIN32) && !defined(__CYGWIN__)
+@@ -65,8 +65,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ /// A filesystem path.
+ class path
+ {
+- template<typename _CharT>
+- struct __is_encoded_char : std::false_type { };
++ template<typename _CharT, typename _Ch = remove_const_t<_CharT>>
++ using __is_encoded_char
++ = __or_<is_same<_Ch, char>, is_same<_Ch, wchar_t>,
++ is_same<_Ch, char16_t>, is_same<_Ch, char32_t>>;
+
+ template<typename _Iter,
+ typename _Iter_traits = std::iterator_traits<_Iter>>
+@@ -106,8 +108,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+
+ template<typename _Tp1, typename _Tp2 = void>
+ using _Path = typename
+- std::enable_if<__and_<__not_<is_same<remove_cv_t<_Tp1>, path>>,
+- __not_<is_void<_Tp1>>,
++ std::enable_if<__and_<__not_<is_same<_Tp1, path>>,
+ __constructible_from<_Tp1, _Tp2>>::value,
+ path>::type;
+
+@@ -145,7 +146,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ typename _Iter = decltype(_S_range_begin(std::declval<_Tp>())),
+ typename _Val = typename std::iterator_traits<_Iter>::value_type>
+ using __value_type_is_char
+- = typename std::enable_if<std::is_same<_Val, char>::value>::type;
++ = std::enable_if_t<std::is_same_v<std::remove_const_t<_Val>, char>>;
+
+ public:
+ #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+@@ -231,37 +232,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+
+ // appends
+
+- path& operator/=(const path& __p)
+- {
+-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+- if (__p.is_absolute()
+- || (__p.has_root_name() && __p.root_name() != root_name()))
+- operator=(__p);
+- else
+- {
+- string_type __pathname;
+- if (__p.has_root_directory())
+- __pathname = root_name().native();
+- else if (has_filename() || (!has_root_directory() && is_absolute()))
+- __pathname = _M_pathname + preferred_separator;
+- __pathname += __p.relative_path().native(); // XXX is this right?
+- _M_pathname.swap(__pathname);
+- _M_split_cmpts();
+- }
+-#else
+- // Much simpler, as any path with root-name or root-dir is absolute.
+- if (__p.is_absolute())
+- operator=(__p);
+- else
+- {
+- if (has_filename() || (_M_type == _Type::_Root_name))
+- _M_pathname += preferred_separator;
+- _M_pathname += __p.native();
+- _M_split_cmpts();
+- }
+-#endif
+- return *this;
+- }
++ path& operator/=(const path& __p);
+
+ template <class _Source>
+ _Path<_Source>&
+@@ -377,7 +348,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ bool has_filename() const;
+ bool has_stem() const;
+ bool has_extension() const;
+- bool is_absolute() const { return has_root_directory(); }
++ bool is_absolute() const;
+ bool is_relative() const { return !is_absolute(); }
+
+ // generation
+@@ -392,6 +363,40 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ iterator begin() const;
+ iterator end() const;
+
++ /// Write a path to a stream
++ template<typename _CharT, typename _Traits>
++ friend std::basic_ostream<_CharT, _Traits>&
++ operator<<(std::basic_ostream<_CharT, _Traits>& __os, const path& __p)
++ {
++ __os << std::quoted(__p.string<_CharT, _Traits>());
++ return __os;
++ }
++
++ /// Read a path from a stream
++ template<typename _CharT, typename _Traits>
++ friend std::basic_istream<_CharT, _Traits>&
++ operator>>(std::basic_istream<_CharT, _Traits>& __is, path& __p)
++ {
++ std::basic_string<_CharT, _Traits> __tmp;
++ if (__is >> std::quoted(__tmp))
++ __p = std::move(__tmp);
++ return __is;
++ }
++
++ // Create a basic_string by reading until a null character.
++ template<typename _InputIterator,
++ typename _Traits = std::iterator_traits<_InputIterator>,
++ typename _CharT
++ = typename std::remove_cv_t<typename _Traits::value_type>>
++ static std::basic_string<_CharT>
++ _S_string_from_iter(_InputIterator __source)
++ {
++ std::basic_string<_CharT> __str;
++ for (_CharT __ch = *__source; __ch != _CharT(); __ch = *++__source)
++ __str.push_back(__ch);
++ return __str;
++ }
++
+ private:
+ enum class _Type : unsigned char {
+ _Multi, _Root_name, _Root_dir, _Filename
+@@ -404,19 +409,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+
+ enum class _Split { _Stem, _Extension };
+
+- path&
+- _M_append(path __p)
+- {
+- if (__p.is_absolute())
+- operator=(std::move(__p));
+-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+- else if (__p.has_root_name() && __p.root_name() != root_name())
+- operator=(std::move(__p));
+-#endif
+- else
+- operator/=(const_cast<const path&>(__p));
+- return *this;
+- }
++ path& _M_append(path __p);
+
+ pair<const string_type*, size_t> _M_find_extension() const;
+
+@@ -444,11 +437,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ static string_type
+ _S_convert(_InputIterator __src, __null_terminated)
+ {
+- using _Tp = typename std::iterator_traits<_InputIterator>::value_type;
+- std::basic_string<typename remove_cv<_Tp>::type> __tmp;
+- for (; *__src != _Tp{}; ++__src)
+- __tmp.push_back(*__src);
+- return _S_convert(__tmp.c_str(), __tmp.c_str() + __tmp.size());
++ auto __s = _S_string_from_iter(__src);
++ return _S_convert(__s.c_str(), __s.c_str() + __s.size());
+ }
+
+ static string_type
+@@ -468,10 +458,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ _S_convert_loc(_InputIterator __src, __null_terminated,
+ const std::locale& __loc)
+ {
+- std::string __tmp;
+- while (*__src != '\0')
+- __tmp.push_back(*__src++);
+- return _S_convert_loc(__tmp.data(), __tmp.data()+__tmp.size(), __loc);
++ std::string __s = _S_string_from_iter(__src);
++ return _S_convert_loc(__s.data(), __s.data() + __s.size(), __loc);
+ }
+
+ template<typename _CharT, typename _Traits, typename _Allocator>
+@@ -501,25 +489,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ _Type _M_type = _Type::_Filename;
+ };
+
+- template<>
+- struct path::__is_encoded_char<char> : std::true_type
+- { using value_type = char; };
+-
+- template<>
+- struct path::__is_encoded_char<wchar_t> : std::true_type
+- { using value_type = wchar_t; };
+-
+- template<>
+- struct path::__is_encoded_char<char16_t> : std::true_type
+- { using value_type = char16_t; };
+-
+- template<>
+- struct path::__is_encoded_char<char32_t> : std::true_type
+- { using value_type = char32_t; };
+-
+- template<typename _Tp>
+- struct path::__is_encoded_char<const _Tp> : __is_encoded_char<_Tp> { };
+-
+ inline void swap(path& __lhs, path& __rhs) noexcept { __lhs.swap(__rhs); }
+
+ size_t hash_value(const path& __p) noexcept;
+@@ -556,58 +525,50 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ return __result;
+ }
+
+- /// Write a path to a stream
+- template<typename _CharT, typename _Traits>
+- basic_ostream<_CharT, _Traits>&
+- operator<<(basic_ostream<_CharT, _Traits>& __os, const path& __p)
+- {
+- auto __tmp = __p.string<_CharT, _Traits>();
+- using __quoted_string
+- = std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>;
+- __os << __quoted_string{__tmp, '"', '\\'};
+- return __os;
+- }
+-
+- /// Read a path from a stream
+- template<typename _CharT, typename _Traits>
+- basic_istream<_CharT, _Traits>&
+- operator>>(basic_istream<_CharT, _Traits>& __is, path& __p)
+- {
+- basic_string<_CharT, _Traits> __tmp;
+- using __quoted_string
+- = std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>;
+- if (__is >> __quoted_string{ __tmp, '"', '\\' })
+- __p = std::move(__tmp);
+- return __is;
+- }
+-
+- template<typename _Source>
++ template<typename _InputIterator>
+ inline auto
+- u8path(const _Source& __source)
+- -> decltype(filesystem::path(__source, std::locale::classic()))
++ u8path(_InputIterator __first, _InputIterator __last)
++ -> decltype(filesystem::path(__first, __last, std::locale::classic()))
+ {
+ #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+- const std::string __u8str{__source};
+- return std::filesystem::u8path(__u8str.begin(), __u8str.end());
++ codecvt_utf8<path::value_type> __cvt;
++ path::string_type __tmp;
++ if constexpr (is_pointer_v<_InputIterator>)
++ {
++ if (__str_codecvt_in(__first, __last, __tmp, __cvt))
++ return path{ __tmp };
++ }
++ else
++ {
++ const std::string __u8str{__first, __last};
++ const char* const __ptr = __u8str.data();
++ if (__str_codecvt_in(__ptr, __ptr + __u8str.size(), __tmp, __cvt))
++ return path{ __tmp };
++ }
++ return {};
+ #else
+- return path{ __source };
++ return path{ __first, __last };
+ #endif
+ }
+
+- template<typename _InputIterator>
++ template<typename _Source>
+ inline auto
+- u8path(_InputIterator __first, _InputIterator __last)
+- -> decltype(filesystem::path(__first, __last, std::locale::classic()))
++ u8path(const _Source& __source)
++ -> decltype(filesystem::path(__source, std::locale::classic()))
+ {
+ #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+- codecvt_utf8<value_type> __cvt;
+- string_type __tmp;
+- if (__str_codecvt_in(__first, __last, __tmp, __cvt))
+- return path{ __tmp };
++ if constexpr (is_convertible_v<const _Source&, std::string_view>)
++ {
++ const std::string_view __s = __source;
++ return filesystem::u8path(__s.data(), __s.data() + __s.size());
++ }
+ else
+- return {};
++ {
++ std::string __s = path::_S_string_from_iter(__source);
++ return filesystem::u8path(__s.data(), __s.data() + __s.size());
++ }
+ #else
+- return path{ __first, __last };
++ return path{ __source };
+ #endif
+ }
+
+@@ -916,11 +877,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ path::string(const _Allocator& __a) const
+ {
+ if constexpr (is_same_v<_CharT, value_type>)
++ {
+ #if _GLIBCXX_USE_CXX11_ABI
+- return { _M_pathname, __a };
++ return { _M_pathname, __a };
+ #else
+- return { _M_pathname, string_type::size_type(0), __a };
++ if constexpr (is_same_v<_Allocator, string_type::allocator_type>)
++ return _M_pathname;
++ else
++ return { _M_pathname, string_type::size_type(0), __a };
+ #endif
++ }
+ else
+ return _S_str_convert<_CharT, _Traits>(_M_pathname, __a);
+ }
+@@ -1072,6 +1038,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ return ext.first && ext.second != string_type::npos;
+ }
+
++ inline bool
++ path::is_absolute() const
++ {
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ return has_root_name() && has_root_directory();
++#else
++ return has_root_directory();
++#endif
++ }
++
+ inline path::iterator
+ path::begin() const
+ {
+@@ -1088,6 +1064,38 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ return iterator(this, true);
+ }
+
++#ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ inline path& path::operator/=(const path& __p)
++ {
++ // Much simpler than the specification in the standard,
++ // as any path with root-name or root-dir is absolute.
++ if (__p.is_absolute())
++ operator=(__p);
++ else
++ {
++ if (has_filename() || (_M_type == _Type::_Root_name))
++ _M_pathname += preferred_separator;
++ _M_pathname += __p.native();
++ _M_split_cmpts();
++ }
++ return *this;
++ }
++#endif
++
++ inline path&
++ path::_M_append(path __p)
++ {
++ if (__p.is_absolute())
++ operator=(std::move(__p));
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ else if (__p.has_root_name() && __p.root_name() != root_name())
++ operator=(std::move(__p));
++#endif
++ else
++ operator/=(const_cast<const path&>(__p));
++ return *this;
++ }
++
+ inline path::iterator&
+ path::iterator::operator++()
+ {
+diff --git a/libstdc++-v3/include/bits/fstream.tcc b/libstdc++-v3/include/bits/fstream.tcc
+index f23ff7af4eb..d9eff00823a 100644
+--- a/libstdc++-v3/include/bits/fstream.tcc
++++ b/libstdc++-v3/include/bits/fstream.tcc
+@@ -207,6 +207,42 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ return __ret;
+ }
+
++#if _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
++ template<typename _CharT, typename _Traits>
++ basic_filebuf<_CharT, _Traits>*
++ basic_filebuf<_CharT, _Traits>::
++ open(const wchar_t* __s, ios_base::openmode __mode)
++ {
++ __filebuf_type *__ret = 0;
++ if (!this->is_open())
++ {
++ _M_file.open(__s, __mode);
++ if (this->is_open())
++ {
++ _M_allocate_internal_buffer();
++ _M_mode = __mode;
++
++ // Setup initial buffer to 'uncommitted' mode.
++ _M_reading = false;
++ _M_writing = false;
++ _M_set_buffer(-1);
++
++ // Reset to initial state.
++ _M_state_last = _M_state_cur = _M_state_beg;
++
++ // 27.8.1.3,4
++ if ((__mode & ios_base::ate)
++ && this->seekoff(0, ios_base::end, __mode)
++ == pos_type(off_type(-1)))
++ this->close();
++ else
++ __ret = this;
++ }
++ }
++ return __ret;
++ }
++#endif // HAVE__WFOPEN && USE_WCHAR_T
++
+ template<typename _CharT, typename _Traits>
+ typename basic_filebuf<_CharT, _Traits>::__filebuf_type*
+ basic_filebuf<_CharT, _Traits>::
+diff --git a/libstdc++-v3/include/experimental/bits/fs_path.h b/libstdc++-v3/include/experimental/bits/fs_path.h
+index 088d62f8f43..340cc59d541 100644
+--- a/libstdc++-v3/include/experimental/bits/fs_path.h
++++ b/libstdc++-v3/include/experimental/bits/fs_path.h
+@@ -79,8 +79,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ /// A filesystem path.
+ class path
+ {
+- template<typename _CharT>
+- struct __is_encoded_char : std::false_type { };
++ template<typename _CharT,
++ typename _Ch = typename remove_const<_CharT>::type>
++ using __is_encoded_char
++ = __or_<is_same<_Ch, char>, is_same<_Ch, wchar_t>,
++ is_same<_Ch, char16_t>, is_same<_Ch, char32_t>>;
+
+ template<typename _Iter,
+ typename _Iter_traits = std::iterator_traits<_Iter>>
+@@ -163,8 +166,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ template<typename _Tp,
+ typename _Iter = decltype(_S_range_begin(std::declval<_Tp>())),
+ typename _Val = typename std::iterator_traits<_Iter>::value_type>
+- using __value_type_is_char
+- = typename std::enable_if<std::is_same<_Val, char>::value>::type;
++ using __value_type_is_char = typename std::enable_if<
++ std::is_same<typename std::remove_const<_Val>::type, char>::value
++ >::type;
+
+ public:
+ #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+@@ -370,7 +374,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ bool has_filename() const;
+ bool has_stem() const;
+ bool has_extension() const;
+- bool is_absolute() const { return has_root_directory(); }
++ bool is_absolute() const;
+ bool is_relative() const { return !is_absolute(); }
+
+ // iterators
+@@ -380,6 +384,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ iterator begin() const;
+ iterator end() const;
+
++ // Create a basic_string by reading until a null character.
++ template<typename _InputIterator,
++ typename _Traits = std::iterator_traits<_InputIterator>,
++ typename _CharT
++ = typename std::remove_cv<typename _Traits::value_type>::type>
++ static std::basic_string<_CharT>
++ _S_string_from_iter(_InputIterator __source)
++ {
++ std::basic_string<_CharT> __str;
++ for (_CharT __ch = *__source; __ch != _CharT(); __ch = *++__source)
++ __str.push_back(__ch);
++ return __str;
++ }
++
+ private:
+ enum class _Type : unsigned char {
+ _Multi, _Root_name, _Root_dir, _Filename
+@@ -429,11 +447,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ static string_type
+ _S_convert(_InputIterator __src, __null_terminated)
+ {
+- using _Tp = typename std::iterator_traits<_InputIterator>::value_type;
+- std::basic_string<typename remove_cv<_Tp>::type> __tmp;
+- for (; *__src != _Tp{}; ++__src)
+- __tmp.push_back(*__src);
+- return _S_convert(__tmp.c_str(), __tmp.c_str() + __tmp.size());
++ auto __s = _S_string_from_iter(__src);
++ return _S_convert(__s.c_str(), __s.c_str() + __s.size());
+ }
+
+ static string_type
+@@ -453,10 +468,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ _S_convert_loc(_InputIterator __src, __null_terminated,
+ const std::locale& __loc)
+ {
+- std::string __tmp;
+- while (*__src != '\0')
+- __tmp.push_back(*__src++);
+- return _S_convert_loc(__tmp.data(), __tmp.data()+__tmp.size(), __loc);
++ std::string __s = _S_string_from_iter(__src);
++ return _S_convert_loc(__s.data(), __s.data() + __s.size(), __loc);
+ }
+
+ bool _S_is_dir_sep(value_type __ch)
+@@ -526,7 +539,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ auto __tmp = __p.string<_CharT, _Traits>();
+ using __quoted_string
+ = std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>;
+- __os << __quoted_string{__tmp, '"', '\\'};
++ __os << __quoted_string{__tmp, _CharT('"'), _CharT('\\')};
+ return __os;
+ }
+
+@@ -538,7 +551,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ basic_string<_CharT, _Traits> __tmp;
+ using __quoted_string
+ = std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>;
+- if (__is >> __quoted_string{ __tmp, '"', '\\' })
++ if (__is >> __quoted_string{ __tmp, _CharT('"'), _CharT('\\') })
+ __p = std::move(__tmp);
+ return __is;
+ }
+@@ -596,25 +609,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ std::string _M_what = _M_gen_what();
+ };
+
+- template<>
+- struct path::__is_encoded_char<char> : std::true_type
+- { using value_type = char; };
+-
+- template<>
+- struct path::__is_encoded_char<wchar_t> : std::true_type
+- { using value_type = wchar_t; };
+-
+- template<>
+- struct path::__is_encoded_char<char16_t> : std::true_type
+- { using value_type = char16_t; };
+-
+- template<>
+- struct path::__is_encoded_char<char32_t> : std::true_type
+- { using value_type = char32_t; };
+-
+- template<typename _Tp>
+- struct path::__is_encoded_char<const _Tp> : __is_encoded_char<_Tp> { };
+-
+ struct path::_Cmpt : path
+ {
+ _Cmpt(string_type __s, _Type __t, size_t __pos)
+@@ -1001,6 +995,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ return ext.first && ext.second != string_type::npos;
+ }
+
++ inline bool
++ path::is_absolute() const
++ {
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ return has_root_name() && has_root_directory();
++#else
++ return has_root_directory();
++#endif
++ }
++
+ inline path::iterator
+ path::begin() const
+ {
+diff --git a/libstdc++-v3/include/std/fstream b/libstdc++-v3/include/std/fstream
+index 3a5895d68b0..740d12d3bb6 100644
+--- a/libstdc++-v3/include/std/fstream
++++ b/libstdc++-v3/include/std/fstream
+@@ -304,6 +304,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ __filebuf_type*
+ open(const char* __s, ios_base::openmode __mode);
+
++#if _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
++ /**
++ * @brief Opens an external file.
++ * @param __s The name of the file, as a wide character string.
++ * @param __mode The open mode flags.
++ * @return @c this on success, NULL on failure
++ */
++ __filebuf_type*
++ open(const wchar_t* __s, ios_base::openmode __mode);
++#endif
++
+ #if __cplusplus >= 201103L
+ /**
+ * @brief Opens an external file.
+@@ -517,6 +528,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ this->open(__s, __mode);
+ }
+
++#if _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
++ /**
++ * @param Create an input file stream.
++ * @param __s Wide string specifying the filename.
++ * @param __mode Open file in specified mode (see std::ios_base).
++ *
++ * @c ios_base::in is automatically included in @a __mode.
++ */
++ basic_ifstream(const wchar_t* __s,
++ ios_base::openmode __mode = ios_base::in)
++ : __istream_type(), _M_filebuf()
++ {
++ this->init(&_M_filebuf);
++ this->open(__s, __mode);
++ }
++#endif
++
+ #if __cplusplus >= 201103L
+ /**
+ * @brief Create an input file stream.
+@@ -632,6 +660,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ this->clear();
+ }
+
++#if _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
++ /**
++ * @brief Opens an external file.
++ * @param __s The name of the file, as a wide character string.
++ * @param __mode The open mode flags.
++ *
++ * Calls @c std::basic_filebuf::open(__s,__mode|in). If that function
++ * fails, @c failbit is set in the stream's error state.
++ */
++ void
++ open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in)
++ {
++ if (!_M_filebuf.open(__s, __mode | ios_base::in))
++ this->setstate(ios_base::failbit);
++ else
++ this->clear();
++ }
++#endif
++
+ #if __cplusplus >= 201103L
+ /**
+ * @brief Opens an external file.
+@@ -743,6 +790,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ this->open(__s, __mode);
+ }
+
++#if _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
++ /**
++ * @param Create an output file stream.
++ * @param __s Wide string specifying the filename.
++ * @param __mode Open file in specified mode (see std::ios_base).
++ *
++ * @c ios_base::out | @c ios_base::trunc is automatically included in
++ * @a __mode.
++ */
++ basic_ofstream(const wchar_t* __s,
++ ios_base::openmode __mode = ios_base::out|ios_base::trunc)
++ : __ostream_type(), _M_filebuf()
++ {
++ this->init(&_M_filebuf);
++ this->open(__s, __mode);
++ }
++#endif
++
+ #if __cplusplus >= 201103L
+ /**
+ * @brief Create an output file stream.
+@@ -858,6 +923,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ this->clear();
+ }
+
++#if _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
++ /**
++ * @brief Opens an external file.
++ * @param __s The name of the file.
++ * @param __mode The open mode flags.
++ *
++ * Calls @c std::basic_filebuf::open(__s,__mode|out). If that
++ * function fails, @c failbit is set in the stream's error state.
++ */
++ void
++ open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out)
++ {
++ if (!_M_filebuf.open(__s, __mode | ios_base::out))
++ this->setstate(ios_base::failbit);
++ else
++ this->clear();
++ }
++#endif
++
+ #if __cplusplus >= 201103L
+ /**
+ * @brief Opens an external file.
+@@ -969,6 +1053,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ this->open(__s, __mode);
+ }
+
++#if _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
++ /**
++ * @param Create an input/output file stream.
++ * @param __s Wide string specifying the filename.
++ * @param __mode Open file in specified mode (see std::ios_base).
++ */
++ basic_fstream(const wchar_t* __s,
++ ios_base::openmode __mode = ios_base::in | ios_base::out)
++ : __iostream_type(0), _M_filebuf()
++ {
++ this->init(&_M_filebuf);
++ this->open(__s, __mode);
++ }
++#endif
++
+ #if __cplusplus >= 201103L
+ /**
+ * @brief Create an input/output file stream.
+@@ -1081,6 +1180,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ this->clear();
+ }
+
++#if _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
++ /**
++ * @brief Opens an external file.
++ * @param __s The name of the file.
++ * @param __mode The open mode flags.
++ *
++ * Calls @c std::basic_filebuf::open(__s,__mode). If that
++ * function fails, @c failbit is set in the stream's error state.
++ */
++ void
++ open(const wchar_t* __s,
++ ios_base::openmode __mode = ios_base::in | ios_base::out)
++ {
++ if (!_M_filebuf.open(__s, __mode))
++ this->setstate(ios_base::failbit);
++ else
++ this->clear();
++ }
++#endif
++
+ #if __cplusplus >= 201103L
+ /**
+ * @brief Opens an external file.
+diff --git a/libstdc++-v3/src/filesystem/dir-common.h b/libstdc++-v3/src/filesystem/dir-common.h
+index 21ba01ca970..03875819d04 100644
+--- a/libstdc++-v3/src/filesystem/dir-common.h
++++ b/libstdc++-v3/src/filesystem/dir-common.h
+@@ -26,6 +26,9 @@
+ #define _GLIBCXX_DIR_COMMON_H 1
+
+ #include <string.h> // strcmp
++#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
++#include <wchar.h> // wcscmp
++#endif
+ #ifdef _GLIBCXX_HAVE_DIRENT_H
+ # ifdef _GLIBCXX_HAVE_SYS_TYPES_H
+ # include <sys/types.h>
+@@ -35,26 +38,42 @@
+ # error "the <dirent.h> header is needed to build the Filesystem TS"
+ #endif
+
+-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+-# undef opendir
+-# define opendir _wopendir
+-#endif
+-
+ namespace std _GLIBCXX_VISIBILITY(default)
+ {
+ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ namespace filesystem
+ {
++namespace __gnu_posix
++{
++#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
++// Adapt the Windows _wxxx functions to look like POSIX xxx, but for wchar_t*.
++using char_type = wchar_t;
++using DIR = ::_WDIR;
++using dirent = _wdirent;
++inline DIR* opendir(const wchar_t* path) { return ::_wopendir(path); }
++inline dirent* readdir(DIR* dir) { return ::_wreaddir(dir); }
++inline int closedir(DIR* dir) { return ::_wclosedir(dir); }
++#else
++using char_type = char;
++using DIR = ::DIR;
++typedef struct ::dirent dirent;
++using ::opendir;
++using ::readdir;
++using ::closedir;
++#endif
++} // namespace __gnu_posix
++
++namespace posix = __gnu_posix;
+
+ struct _Dir_base
+ {
+- _Dir_base(DIR* dirp = nullptr) : dirp(dirp) { }
++ _Dir_base(posix::DIR* dirp = nullptr) : dirp(dirp) { }
+
+ // If no error occurs then dirp is non-null,
+ // otherwise null (whether error ignored or not).
+- _Dir_base(const char* p, bool skip_permission_denied,
++ _Dir_base(const posix::char_type* pathname, bool skip_permission_denied,
+ error_code& ec) noexcept
+- : dirp(::opendir(p))
++ : dirp(posix::opendir(pathname))
+ {
+ if (dirp)
+ ec.clear();
+@@ -72,22 +91,22 @@ struct _Dir_base
+
+ _Dir_base& operator=(_Dir_base&&) = delete;
+
+- ~_Dir_base() { if (dirp) ::closedir(dirp); }
++ ~_Dir_base() { if (dirp) posix::closedir(dirp); }
+
+- const struct ::dirent*
++ const posix::dirent*
+ advance(bool skip_permission_denied, error_code& ec) noexcept
+ {
+ ec.clear();
+
+ int err = std::exchange(errno, 0);
+- const struct ::dirent* entp = readdir(dirp);
++ const posix::dirent* entp = posix::readdir(dirp);
+ // std::swap cannot be used with Bionic's errno
+ err = std::exchange(errno, err);
+
+ if (entp)
+ {
+ // skip past dot and dot-dot
+- if (!strcmp(entp->d_name, ".") || !strcmp(entp->d_name, ".."))
++ if (is_dot_or_dotdot(entp->d_name))
+ return advance(skip_permission_denied, ec);
+ return entp;
+ }
+@@ -105,15 +124,24 @@ struct _Dir_base
+ }
+ }
+
+- DIR* dirp;
++ static bool is_dot_or_dotdot(const char* s) noexcept
++ { return !strcmp(s, ".") || !strcmp(s, ".."); }
++
++#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ static bool is_dot_or_dotdot(const wchar_t* s) noexcept
++ { return !wcscmp(s, L".") || !wcscmp(s, L".."); }
++#endif
++
++ posix::DIR* dirp;
+ };
+
+ } // namespace filesystem
+
+ // BEGIN/END macros must be defined before including this file.
+ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
++
+ inline file_type
+-get_file_type(const ::dirent& d __attribute__((__unused__)))
++get_file_type(const std::filesystem::__gnu_posix::dirent& d [[gnu::unused]])
+ {
+ #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
+ switch (d.d_type)
+diff --git a/libstdc++-v3/src/filesystem/dir.cc b/libstdc++-v3/src/filesystem/dir.cc
+index 7e712c553c3..01c3decaba6 100644
+--- a/libstdc++-v3/src/filesystem/dir.cc
++++ b/libstdc++-v3/src/filesystem/dir.cc
+@@ -37,6 +37,7 @@
+ #include "dir-common.h"
+
+ namespace fs = std::experimental::filesystem;
++namespace posix = std::filesystem::__gnu_posix;
+
+ struct fs::_Dir : std::filesystem::_Dir_base
+ {
+@@ -47,7 +48,7 @@ struct fs::_Dir : std::filesystem::_Dir_base
+ path = p;
+ }
+
+- _Dir(DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
++ _Dir(posix::DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
+
+ _Dir(_Dir&&) = default;
+
+@@ -185,7 +186,7 @@ recursive_directory_iterator(const path& p, directory_options options,
+ {
+ if (ec)
+ ec->clear();
+- if (DIR* dirp = ::opendir(p.c_str()))
++ if (posix::DIR* dirp = posix::opendir(p.c_str()))
+ {
+ auto sp = std::make_shared<_Dir_stack>();
+ sp->push(_Dir{ dirp, p });
+diff --git a/libstdc++-v3/src/filesystem/ops-common.h b/libstdc++-v3/src/filesystem/ops-common.h
+index bc186836bd4..c1b817189a9 100644
+--- a/libstdc++-v3/src/filesystem/ops-common.h
++++ b/libstdc++-v3/src/filesystem/ops-common.h
+@@ -34,12 +34,103 @@
+ # include <sys/stat.h>
+ # endif
+ #endif
++#if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
++# include <utime.h> // utime
++#endif
++
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++# include <wchar.h>
++#endif
+
+ namespace std _GLIBCXX_VISIBILITY(default)
+ {
+ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ namespace filesystem
+ {
++namespace __gnu_posix
++{
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++// Adapt the Windows _wxxx functions to look like POSIX xxx, but for wchar_t*.
++ inline int open(const wchar_t* path, int flags)
++ { return ::_wopen(path, flags); }
++
++ inline int open(const wchar_t* path, int flags, int mode)
++ { return ::_wopen(path, flags, mode); }
++
++ inline int close(int fd)
++ { return ::_close(fd); }
++
++ typedef struct ::_stat stat_type;
++
++ inline int stat(const wchar_t* path, stat_type* buffer)
++ { return ::_wstat(path, buffer); }
++
++ inline lstat(const wchar_t* path, stat_type* buffer)
++ {
++ // TODO symlinks not currently supported
++ return stat(path, buffer);
++ }
++
++ using ::mode_t;
++
++ inline int chmod(const wchar_t* path, mode_t mode)
++ { return ::_wchmod(path, mode); }
++
++ inline int mkdir(const wchar_t* path, mode_t)
++ { return ::_wmkdir(path); }
++
++ inline wchar_t* getcwd(wchar_t* buf, size_t size)
++ { return ::_wgetcwd(buf, size > (size_t)INT_MAX ? INT_MAX : (int)size); }
++
++ inline int chdir(const wchar_t* path)
++ { return ::_wchdir(path); }
++
++#if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
++ using utimbuf = _utimbuf;
++
++ inline int utime(const wchar_t* path, utimbuf* times)
++ { return ::_wutime(path, times); }
++#endif
++
++ inline int rename(const wchar_t* oldname, const wchar_t* newname)
++ { return _wrename(oldname, newname); }
++
++ inline int truncate(const wchar_t* path, _off64_t length)
++ {
++ const int fd = ::_wopen(path, _O_BINARY|_O_RDWR);
++ if (fd == -1)
++ return fd;
++ const int ret = ::ftruncate64(fd, length);
++ int err;
++ ::_get_errno(&err);
++ ::_close(fd);
++ ::_set_errno(err);
++ return ret;
++ }
++ using char_type = wchar_t;
++#else // _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ using ::open;
++ using ::close;
++#ifdef _GLIBCXX_HAVE_SYS_STAT_H
++ typedef struct ::stat stat_type;
++ using ::stat;
++ using ::lstat;
++#endif
++ using ::mode_t;
++ using ::chmod;
++ using ::mkdir;
++ using ::getcwd;
++ using ::chdir;
++#if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
++ using ::utimbuf;
++ using ::utime;
++#endif
++ using ::rename;
++ using ::truncate;
++ using char_type = char;
++#endif // _GLIBCXX_FILESYSTEM_IS_WINDOWS
++} // namespace __gnu_posix
++
+ template<typename Bitmask>
+ inline bool is_set(Bitmask obj, Bitmask bits)
+ {
+@@ -53,7 +144,7 @@ namespace filesystem
+ }
+
+ #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+- typedef struct ::stat stat_type;
++ using __gnu_posix::stat_type;
+
+ inline std::chrono::system_clock::time_point
+ file_time(const stat_type& st, std::error_code& ec) noexcept
+@@ -82,11 +173,17 @@ namespace filesystem
+ };
+
+ bool
+- do_copy_file(const char* from, const char* to,
++ do_copy_file(const __gnu_posix::char_type* from,
++ const __gnu_posix::char_type* to,
+ copy_options_existing_file options,
+ stat_type* from_st, stat_type* to_st,
+ std::error_code& ec) noexcept;
+
++ void
++ do_space(const __gnu_posix::char_type* pathname,
++ uintmax_t& capacity, uintmax_t& free, uintmax_t& available,
++ std::error_code&);
++
+ #endif // _GLIBCXX_HAVE_SYS_STAT_H
+
+ } // namespace filesystem
+@@ -95,7 +192,7 @@ namespace filesystem
+ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
+
+ #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+- typedef struct ::stat stat_type;
++ using std::filesystem::__gnu_posix::stat_type;
+
+ inline file_type
+ make_file_type(const stat_type& st) noexcept
+@@ -111,8 +208,10 @@ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
+ return file_type::block;
+ else if (S_ISFIFO(st.st_mode))
+ return file_type::fifo;
++#ifdef S_ISLNK // not present in mingw
+ else if (S_ISLNK(st.st_mode))
+ return file_type::symlink;
++#endif
+ #ifdef S_ISSOCK // not present until POSIX:2001
+ else if (S_ISSOCK(st.st_mode))
+ return file_type::socket;
+diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc
+index de290625e54..40cadbf6270 100644
+--- a/libstdc++-v3/src/filesystem/ops.cc
++++ b/libstdc++-v3/src/filesystem/ops.cc
+@@ -47,20 +47,17 @@
+ #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
+ # include <utime.h> // utime
+ #endif
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++# include <windows.h>
++#endif
+
+ #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
+ namespace experimental { namespace filesystem {
+ #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
+ #include "ops-common.h"
+
+-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+-# undef utime
+-# define utime _wutime
+-# undef chmod
+-# define chmod _wchmod
+-#endif
+-
+ namespace fs = std::experimental::filesystem;
++namespace posix = std::filesystem::__gnu_posix;
+
+ fs::path
+ fs::absolute(const path& p, const path& base)
+@@ -109,7 +106,7 @@ namespace
+ void operator()(void* p) const { ::free(p); }
+ };
+
+- using char_ptr = std::unique_ptr<char[], free_as_in_malloc>;
++ using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
+ }
+
+ fs::path
+@@ -122,7 +119,8 @@ fs::canonical(const path& p, const path& base, error_code& ec)
+ char_ptr buf{ nullptr };
+ # if _XOPEN_VERSION < 700
+ // Not safe to call realpath(path, NULL)
+- buf.reset( (char*)::malloc(PATH_MAX) );
++ using char_type = fs::path::value_type;
++ buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
+ # endif
+ if (char* rp = ::realpath(pa.c_str(), buf.get()))
+ {
+@@ -241,12 +239,13 @@ namespace
+ using std::filesystem::is_set;
+
+ #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+- typedef struct ::stat stat_type;
++ using posix::stat_type;
+
+ using std::filesystem::is_not_found_errno;
+ using std::filesystem::file_time;
+ using std::filesystem::do_copy_file;
+ #endif // _GLIBCXX_HAVE_SYS_STAT_H
++
+ } // namespace
+
+ void
+@@ -263,15 +262,15 @@ fs::copy(const path& from, const path& to, copy_options options,
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 2681. filesystem::copy() cannot copy symlinks
+ if (use_lstat || copy_symlinks
+- ? ::lstat(from.c_str(), &from_st)
+- : ::stat(from.c_str(), &from_st))
++ ? posix::lstat(from.c_str(), &from_st)
++ : posix::stat(from.c_str(), &from_st))
+ {
+ ec.assign(errno, std::generic_category());
+ return;
+ }
+ if (use_lstat
+- ? ::lstat(to.c_str(), &to_st)
+- : ::stat(to.c_str(), &to_st))
++ ? posix::lstat(to.c_str(), &to_st)
++ : posix::stat(to.c_str(), &to_st))
+ {
+ if (!is_not_found_errno(errno))
+ {
+@@ -424,19 +423,6 @@ fs::create_directories(const path& p, error_code& ec) noexcept
+ ec = std::make_error_code(errc::invalid_argument);
+ return false;
+ }
+-
+- file_status st = symlink_status(p, ec);
+- if (is_directory(st))
+- return false;
+- else if (ec && !status_known(st))
+- return false;
+- else if (exists(st))
+- {
+- if (!ec)
+- ec = std::make_error_code(std::errc::not_a_directory);
+- return false;
+- }
+-
+ std::stack<path> missing;
+ path pp = p;
+
+@@ -445,29 +431,24 @@ fs::create_directories(const path& p, error_code& ec) noexcept
+ ec.clear();
+ const auto& filename = pp.filename();
+ if (!is_dot(filename) && !is_dotdot(filename))
+- {
+- missing.push(std::move(pp));
+- pp = missing.top().parent_path();
+- }
+- else
+- pp = pp.parent_path();
++ missing.push(pp);
++ pp.remove_filename();
+ }
+
+ if (ec || missing.empty())
+ return false;
+
+- bool created;
+ do
+ {
+ const path& top = missing.top();
+- created = create_directory(top, ec);
+- if (ec)
+- return false;
++ create_directory(top, ec);
++ if (ec && is_directory(top))
++ ec.clear();
+ missing.pop();
+ }
+- while (!missing.empty());
++ while (!missing.empty() && !ec);
+
+- return created;
++ return missing.empty();
+ }
+
+ namespace
+@@ -477,8 +458,8 @@ namespace
+ {
+ bool created = false;
+ #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+- ::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
+- if (::mkdir(p.c_str(), mode))
++ posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
++ if (posix::mkdir(p.c_str(), mode))
+ {
+ const int err = errno;
+ if (err != EEXIST || !is_directory(p, ec))
+@@ -531,7 +512,7 @@ fs::create_directory(const path& p, const path& attributes,
+ {
+ #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+ stat_type st;
+- if (::stat(attributes.c_str(), &st))
++ if (posix::stat(attributes.c_str(), &st))
+ {
+ ec.assign(errno, std::generic_category());
+ return false;
+@@ -580,11 +561,16 @@ void
+ fs::create_hard_link(const path& to, const path& new_hard_link,
+ error_code& ec) noexcept
+ {
+-#ifdef _GLIBCXX_HAVE_UNISTD_H
++#ifdef _GLIBCXX_HAVE_LINK
+ if (::link(to.c_str(), new_hard_link.c_str()))
+ ec.assign(errno, std::generic_category());
+ else
+ ec.clear();
++#elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
++ ec.clear();
++ else
++ ec.assign((int)GetLastError(), generic_category());
+ #else
+ ec = std::make_error_code(std::errc::not_supported);
+ #endif
+@@ -604,7 +590,7 @@ void
+ fs::create_symlink(const path& to, const path& new_symlink,
+ error_code& ec) noexcept
+ {
+-#ifdef _GLIBCXX_HAVE_UNISTD_H
++#ifdef _GLIBCXX_HAVE_SYMLINK
+ if (::symlink(to.c_str(), new_symlink.c_str()))
+ ec.assign(errno, std::generic_category());
+ else
+@@ -614,7 +600,6 @@ fs::create_symlink(const path& to, const path& new_symlink,
+ #endif
+ }
+
+-
+ fs::path
+ fs::current_path()
+ {
+@@ -630,8 +615,8 @@ fs::current_path(error_code& ec)
+ {
+ path p;
+ #ifdef _GLIBCXX_HAVE_UNISTD_H
+-#ifdef __GLIBC__
+- if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)})
++#if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
+ {
+ p.assign(cwd.get());
+ ec.clear();
+@@ -639,6 +624,7 @@ fs::current_path(error_code& ec)
+ else
+ ec.assign(errno, std::generic_category());
+ #else
++#ifdef _PC_PATH_MAX
+ long path_max = pathconf(".", _PC_PATH_MAX);
+ size_t size;
+ if (path_max == -1)
+@@ -647,9 +633,15 @@ fs::current_path(error_code& ec)
+ size = 10240;
+ else
+ size = path_max;
++#elif defined(PATH_MAX)
++ size_t size = PATH_MAX;
++#else
++ size_t size = 1024;
++#endif
+ for (char_ptr buf; p.empty(); size *= 2)
+ {
+- buf.reset((char*)malloc(size));
++ using char_type = fs::path::value_type;
++ buf.reset((char_type*)malloc(size * sizeof(char_type)));
+ if (buf)
+ {
+ if (getcwd(buf.get(), size))
+@@ -689,7 +681,7 @@ void
+ fs::current_path(const path& p, error_code& ec) noexcept
+ {
+ #ifdef _GLIBCXX_HAVE_UNISTD_H
+- if (::chdir(p.c_str()))
++ if (posix::chdir(p.c_str()))
+ ec.assign(errno, std::generic_category());
+ else
+ ec.clear();
+@@ -716,14 +708,14 @@ fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
+ int err = 0;
+ file_status s1, s2;
+ stat_type st1, st2;
+- if (::stat(p1.c_str(), &st1) == 0)
++ if (posix::stat(p1.c_str(), &st1) == 0)
+ s1 = make_file_status(st1);
+ else if (is_not_found_errno(errno))
+ s1.type(file_type::not_found);
+ else
+ err = errno;
+
+- if (::stat(p2.c_str(), &st2) == 0)
++ if (posix::stat(p2.c_str(), &st2) == 0)
+ s2 = make_file_status(st2);
+ else if (is_not_found_errno(errno))
+ s2.type(file_type::not_found);
+@@ -773,7 +765,7 @@ namespace
+ {
+ #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+ stat_type st;
+- if (::stat(p.c_str(), &st))
++ if (posix::stat(p.c_str(), &st))
+ {
+ ec.assign(errno, std::generic_category());
+ return deflt;
+@@ -823,7 +815,7 @@ fs::hard_link_count(const path& p)
+ std::uintmax_t
+ fs::hard_link_count(const path& p, error_code& ec) noexcept
+ {
+- return do_stat(p, ec, std::mem_fn(&stat::st_nlink),
++ return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
+ static_cast<uintmax_t>(-1));
+ }
+
+@@ -899,11 +891,11 @@ fs::last_write_time(const path& p __attribute__((__unused__)),
+ else
+ ec.clear();
+ #elif _GLIBCXX_HAVE_UTIME_H
+- ::utimbuf times;
++ posix::utimbuf times;
+ times.modtime = s.count();
+ times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
+ times.modtime);
+- if (::utime(p.c_str(), &times))
++ if (posix::utime(p.c_str(), &times))
+ ec.assign(errno, std::generic_category());
+ else
+ ec.clear();
+@@ -956,7 +948,7 @@ fs::permissions(const path& p, perms prms, error_code& ec) noexcept
+ #else
+ if (nofollow && is_symlink(st))
+ ec = std::make_error_code(std::errc::operation_not_supported);
+- else if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
++ else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
+ err = errno;
+ #endif
+
+@@ -976,10 +968,10 @@ fs::read_symlink(const path& p)
+ return tgt;
+ }
+
+-fs::path fs::read_symlink(const path& p, error_code& ec)
++fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec)
+ {
+ path result;
+-#ifdef _GLIBCXX_HAVE_SYS_STAT_H
++#if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
+ stat_type st;
+ if (::lstat(p.c_str(), &st))
+ {
+@@ -1033,6 +1025,19 @@ fs::remove(const path& p)
+ bool
+ fs::remove(const path& p, error_code& ec) noexcept
+ {
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ if (exists(symlink_status(p, ec)))
++ {
++ if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
++ || DeleteFileW(p.c_str()))
++ {
++ ec.clear();
++ return true;
++ }
++ else if (!ec)
++ ec.assign((int)GetLastError(), generic_category());
++ }
++#else
+ if (::remove(p.c_str()) == 0)
+ {
+ ec.clear();
+@@ -1042,6 +1047,7 @@ fs::remove(const path& p, error_code& ec) noexcept
+ ec.clear();
+ else
+ ec.assign(errno, std::generic_category());
++#endif
+ return false;
+ }
+
+@@ -1095,7 +1101,7 @@ fs::rename(const path& from, const path& to)
+ void
+ fs::rename(const path& from, const path& to, error_code& ec) noexcept
+ {
+- if (::rename(from.c_str(), to.c_str()))
++ if (posix::rename(from.c_str(), to.c_str()))
+ ec.assign(errno, std::generic_category());
+ else
+ ec.clear();
+@@ -1116,7 +1122,7 @@ fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
+ #ifdef _GLIBCXX_HAVE_UNISTD_H
+ if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
+ ec.assign(EINVAL, std::generic_category());
+- else if (::truncate(p.c_str(), size))
++ else if (posix::truncate(p.c_str(), size))
+ ec.assign(errno, std::generic_category());
+ else
+ ec.clear();
+@@ -1144,23 +1150,14 @@ fs::space(const path& p, error_code& ec) noexcept
+ static_cast<uintmax_t>(-1),
+ static_cast<uintmax_t>(-1)
+ };
+-#ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
+- struct ::statvfs f;
+- if (::statvfs(p.c_str(), &f))
+- ec.assign(errno, std::generic_category());
+- else
+- {
+- uintmax_t fragment_size = f.f_frsize;
+- info = space_info{
+- f.f_blocks * fragment_size,
+- f.f_bfree * fragment_size,
+- f.f_bavail * fragment_size
+- };
+- ec.clear();
+- }
++#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ path dir = absolute(p);
++ dir.remove_filename();
++ auto str = dir.c_str();
+ #else
+- ec = std::make_error_code(std::errc::not_supported);
++ auto str = p.c_str();
+ #endif
++ std::filesystem::do_space(str, info.capacity, info.free, info.available, ec);
+ return info;
+ }
+
+@@ -1170,7 +1167,7 @@ fs::status(const fs::path& p, error_code& ec) noexcept
+ {
+ file_status status;
+ stat_type st;
+- if (::stat(p.c_str(), &st))
++ if (posix::stat(p.c_str(), &st))
+ {
+ int err = errno;
+ ec.assign(err, std::generic_category());
+@@ -1194,7 +1191,7 @@ fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
+ {
+ file_status status;
+ stat_type st;
+- if (::lstat(p.c_str(), &st))
++ if (posix::lstat(p.c_str(), &st))
+ {
+ int err = errno;
+ ec.assign(err, std::generic_category());
+@@ -1269,27 +1266,38 @@ fs::path fs::temp_directory_path()
+
+ fs::path fs::temp_directory_path(error_code& ec)
+ {
++ path p;
+ #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+- ec = std::make_error_code(std::errc::not_supported);
+- return {}; // TODO
++ unsigned len = 1024;
++ std::wstring buf;
++ do
++ {
++ buf.resize(len);
++ len = GetTempPathW(buf.size(), buf.data());
++ } while (len > buf.size());
++
++ if (len == 0)
++ {
++ ec.assign((int)GetLastError(), std::system_category());
++ return p;
++ }
++ buf.resize(len);
++ p = std::move(buf);
+ #else
+ const char* tmpdir = nullptr;
+ const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
+ for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
+ tmpdir = ::getenv(*e);
+- path p = tmpdir ? tmpdir : "/tmp";
++ p = tmpdir ? tmpdir : "/tmp";
+ auto st = status(p, ec);
+- if (!ec)
++ if (ec)
++ p.clear();
++ else if (!is_directory(st))
+ {
+- if (is_directory(st))
+- {
+- ec.clear();
+- return p;
+- }
+- else
+- ec = std::make_error_code(std::errc::not_a_directory);
++ p.clear();
++ ec = std::make_error_code(std::errc::not_a_directory);
+ }
+- return {};
+ #endif
++ return p;
+ }
+
+diff --git a/libstdc++-v3/src/filesystem/path.cc b/libstdc++-v3/src/filesystem/path.cc
+index 899d94e0067..fb70d30fdca 100644
+--- a/libstdc++-v3/src/filesystem/path.cc
++++ b/libstdc++-v3/src/filesystem/path.cc
+@@ -61,6 +61,12 @@ path::replace_filename(const path& replacement)
+ return *this;
+ }
+
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++const fs::path::value_type dot = L'.';
++#else
++const fs::path::value_type dot = '.';
++#endif
++
+ path&
+ path::replace_extension(const path& replacement)
+ {
+@@ -78,8 +84,8 @@ path::replace_extension(const path& replacement)
+ _M_pathname.erase(back._M_pos + ext.second);
+ }
+ }
+- if (!replacement.empty() && replacement.native()[0] != '.')
+- _M_pathname += '.';
++ if (!replacement.empty() && replacement.native()[0] != dot)
++ _M_pathname += dot;
+ _M_pathname += replacement.native();
+ _M_split_cmpts();
+ return *this;
+@@ -297,7 +303,7 @@ path::has_filename() const
+ std::pair<const path::string_type*, std::size_t>
+ path::_M_find_extension() const
+ {
+- const std::string* s = nullptr;
++ const string_type* s = nullptr;
+
+ if (_M_type != _Type::_Multi)
+ s = &_M_pathname;
+@@ -312,14 +318,14 @@ path::_M_find_extension() const
+ {
+ if (auto sz = s->size())
+ {
+- if (sz <= 2 && (*s)[0] == '.')
++ if (sz <= 2 && (*s)[0] == dot)
+ {
+- if (sz == 1 || (*s)[1] == '.') // filename is "." or ".."
++ if (sz == 1 || (*s)[1] == dot) // filename is "." or ".."
+ return { s, string_type::npos };
+ else
+ return { s, 0 }; // filename is like ".?"
+ }
+- return { s, s->rfind('.') };
++ return { s, s->rfind(dot) };
+ }
+ }
+ return {};
+@@ -405,7 +411,7 @@ path::_M_split_cmpts()
+ {
+ const auto& last = _M_cmpts.back();
+ pos = last._M_pos + last._M_pathname.size();
+- _M_cmpts.emplace_back(string_type(1, '.'), _Type::_Filename, pos);
++ _M_cmpts.emplace_back(string_type(1, dot), _Type::_Filename, pos);
+ }
+ }
+
+@@ -495,8 +501,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+ std::string filesystem_error::_M_gen_what()
+ {
+ using std::filesystem::fs_err_concat;
+- return fs_err_concat(system_error::what(), _M_path1.native(),
+- _M_path2.native());
++ return fs_err_concat(system_error::what(), _M_path1.u8string(),
++ _M_path2.u8string());
+ }
+
+ _GLIBCXX_END_NAMESPACE_CXX11
+diff --git a/libstdc++-v3/src/filesystem/std-dir.cc b/libstdc++-v3/src/filesystem/std-dir.cc
+index 98eb22ab920..4c9a287ad80 100644
+--- a/libstdc++-v3/src/filesystem/std-dir.cc
++++ b/libstdc++-v3/src/filesystem/std-dir.cc
+@@ -37,6 +37,7 @@
+ #include "dir-common.h"
+
+ namespace fs = std::filesystem;
++namespace posix = std::filesystem::__gnu_posix;
+
+ struct fs::_Dir : _Dir_base
+ {
+@@ -47,7 +48,7 @@ struct fs::_Dir : _Dir_base
+ path = p;
+ }
+
+- _Dir(DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
++ _Dir(posix::DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
+
+ _Dir(_Dir&&) = default;
+
+@@ -180,7 +181,7 @@ recursive_directory_iterator(const path& p, directory_options options,
+ error_code* ecptr)
+ : _M_options(options), _M_pending(true)
+ {
+- if (DIR* dirp = ::opendir(p.c_str()))
++ if (posix::DIR* dirp = posix::opendir(p.c_str()))
+ {
+ if (ecptr)
+ ecptr->clear();
+diff --git a/libstdc++-v3/src/filesystem/std-ops.cc b/libstdc++-v3/src/filesystem/std-ops.cc
+index c0742d73b5c..e266fa6d3f8 100644
+--- a/libstdc++-v3/src/filesystem/std-ops.cc
++++ b/libstdc++-v3/src/filesystem/std-ops.cc
+@@ -25,6 +25,7 @@
+ #ifndef _GLIBCXX_USE_CXX11_ABI
+ # define _GLIBCXX_USE_CXX11_ABI 1
+ # define NEED_DO_COPY_FILE
++# define NEED_DO_SPACE
+ #endif
+
+ #include <filesystem>
+@@ -52,19 +53,16 @@
+ #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
+ # include <utime.h> // utime
+ #endif
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++# include <windows.h>
++#endif
+
+ #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
+ #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
+ #include "ops-common.h"
+
+-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+-# undef utime
+-# define utime _wutime
+-# undef chmod
+-# define chmod _wchmod
+-#endif
+-
+ namespace fs = std::filesystem;
++namespace posix = std::filesystem::__gnu_posix;
+
+ fs::path
+ fs::absolute(const path& p)
+@@ -74,7 +72,7 @@ fs::absolute(const path& p)
+ path ret = absolute(p, ec);
+ if (ec)
+ _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p,
+- std::make_error_code(errc::not_supported)));
++ ec));
+ return ret;
+ #else
+ return current_path() / p;
+@@ -90,17 +88,28 @@ fs::absolute(const path& p, error_code& ec)
+ ec = make_error_code(std::errc::no_such_file_or_directory);
+ return ret;
+ }
+- if (p.is_absolute())
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ const wstring& s = p.native();
++ uint32_t len = 1024;
++ wstring buf;
++ do
+ {
+- ec.clear();
+- ret = p;
+- return ret;
++ buf.resize(len);
++ len = GetFullPathNameW(s.c_str(), len, buf.data(), nullptr);
+ }
++ while (len > buf.size());
+
+-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+- ec = std::make_error_code(errc::not_supported);
++ if (len == 0)
++ ec.assign((int)GetLastError(), std::system_category());
++ else
++ {
++ ec.clear();
++ buf.resize(len);
++ ret = std::move(buf);
++ }
+ #else
+- ret = current_path(ec);
++ ec.clear();
++ ret = current_path();
+ ret /= p;
+ #endif
+ return ret;
+@@ -131,7 +140,7 @@ namespace
+ void operator()(void* p) const { ::free(p); }
+ };
+
+- using char_ptr = std::unique_ptr<char[], free_as_in_malloc>;
++ using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
+ }
+
+ fs::path
+@@ -146,7 +155,8 @@ fs::canonical(const path& p, error_code& ec)
+ char_ptr buf{ nullptr };
+ # if _XOPEN_VERSION < 700
+ // Not safe to call realpath(path, NULL)
+- buf.reset( (char*)::malloc(PATH_MAX) );
++ using char_type = fs::path::value_type;
++ buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
+ # endif
+ if (char* rp = ::realpath(pa.c_str(), buf.get()))
+ {
+@@ -267,7 +277,7 @@ namespace std::filesystem
+ #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+ #ifdef NEED_DO_COPY_FILE
+ bool
+-fs::do_copy_file(const char* from, const char* to,
++fs::do_copy_file(const path::value_type* from, const path::value_type* to,
+ copy_options_existing_file options,
+ stat_type* from_st, stat_type* to_st,
+ std::error_code& ec) noexcept
+@@ -277,7 +287,7 @@ fs::do_copy_file(const char* from, const char* to,
+
+ if (to_st == nullptr)
+ {
+- if (::stat(to, &st1))
++ if (posix::stat(to, &st1))
+ {
+ const int err = errno;
+ if (!is_not_found_errno(err))
+@@ -299,7 +309,7 @@ fs::do_copy_file(const char* from, const char* to,
+
+ if (from_st == nullptr)
+ {
+- if (::stat(from, &st2))
++ if (posix::stat(from, &st2))
+ {
+ ec.assign(errno, std::generic_category());
+ return false;
+@@ -357,12 +367,12 @@ fs::do_copy_file(const char* from, const char* to,
+ }
+
+ struct CloseFD {
+- ~CloseFD() { if (fd != -1) ::close(fd); }
+- bool close() { return ::close(std::exchange(fd, -1)) == 0; }
++ ~CloseFD() { if (fd != -1) posix::close(fd); }
++ bool close() { return posix::close(std::exchange(fd, -1)) == 0; }
+ int fd;
+ };
+
+- CloseFD in = { ::open(from, O_RDONLY) };
++ CloseFD in = { posix::open(from, O_RDONLY) };
+ if (in.fd == -1)
+ {
+ ec.assign(errno, std::generic_category());
+@@ -373,7 +383,7 @@ fs::do_copy_file(const char* from, const char* to,
+ oflag |= O_TRUNC;
+ else
+ oflag |= O_EXCL;
+- CloseFD out = { ::open(to, oflag, S_IWUSR) };
++ CloseFD out = { posix::open(to, oflag, S_IWUSR) };
+ if (out.fd == -1)
+ {
+ if (errno == EEXIST && options.skip)
+@@ -383,12 +393,12 @@ fs::do_copy_file(const char* from, const char* to,
+ return false;
+ }
+
+-#ifdef _GLIBCXX_USE_FCHMOD
++#if defined _GLIBCXX_USE_FCHMOD && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
+ if (::fchmod(out.fd, from_st->st_mode))
+-#elif defined _GLIBCXX_USE_FCHMODAT
++#elif defined _GLIBCXX_USE_FCHMODAT && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
+ if (::fchmodat(AT_FDCWD, to, from_st->st_mode, 0))
+ #else
+- if (::chmod(to, from_st->st_mode))
++ if (posix::chmod(to, from_st->st_mode))
+ #endif
+ {
+ ec.assign(errno, std::generic_category());
+@@ -396,7 +406,7 @@ fs::do_copy_file(const char* from, const char* to,
+ }
+
+ size_t count = from_st->st_size;
+-#ifdef _GLIBCXX_USE_SENDFILE
++#if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
+ off_t offset = 0;
+ ssize_t n = ::sendfile(out.fd, in.fd, &offset, count);
+ if (n < 0 && errno != ENOSYS && errno != EINVAL)
+@@ -475,15 +485,15 @@ fs::copy(const path& from, const path& to, copy_options options,
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 2681. filesystem::copy() cannot copy symlinks
+ if (use_lstat || copy_symlinks
+- ? ::lstat(from.c_str(), &from_st)
+- : ::stat(from.c_str(), &from_st))
++ ? posix::lstat(from.c_str(), &from_st)
++ : posix::stat(from.c_str(), &from_st))
+ {
+ ec.assign(errno, std::generic_category());
+ return;
+ }
+ if (use_lstat
+- ? ::lstat(to.c_str(), &to_st)
+- : ::stat(to.c_str(), &to_st))
++ ? posix::lstat(to.c_str(), &to_st)
++ : posix::stat(to.c_str(), &to_st))
+ {
+ if (!is_not_found_errno(errno))
+ {
+@@ -636,74 +646,38 @@ fs::create_directories(const path& p, error_code& ec)
+ ec = std::make_error_code(errc::invalid_argument);
+ return false;
+ }
+-
+- file_status st = symlink_status(p, ec);
+- if (is_directory(st))
+- return false;
+- else if (ec && !status_known(st))
+- return false;
+- else if (exists(st))
+- {
+- if (!ec)
+- ec = std::make_error_code(std::errc::not_a_directory);
+- return false;
+- }
+-
+ std::stack<path> missing;
+ path pp = p;
+
+- // Strip any trailing slash
+- if (pp.has_relative_path() && !pp.has_filename())
+- pp = pp.parent_path();
+-
+- do
++ while (pp.has_filename() && status(pp, ec).type() == file_type::not_found)
+ {
++ ec.clear();
+ const auto& filename = pp.filename();
+- if (is_dot(filename) || is_dotdot(filename))
+- pp = pp.parent_path();
+- else
+- {
+- missing.push(std::move(pp));
+- if (missing.size() > 1000) // sanity check
+- {
+- ec = std::make_error_code(std::errc::filename_too_long);
+- return false;
+- }
+- pp = missing.top().parent_path();
+- }
++ if (!is_dot(filename) && !is_dotdot(filename))
++ missing.push(pp);
++ pp = pp.parent_path();
+
+- if (pp.empty())
+- break;
+-
+- st = status(pp, ec);
+- if (exists(st))
++ if (missing.size() > 1000) // sanity check
+ {
+- if (ec)
+- return false;
+- if (!is_directory(st))
+- {
+- ec = std::make_error_code(std::errc::not_a_directory);
+- return false;
+- }
++ ec = std::make_error_code(std::errc::filename_too_long);
++ return false;
+ }
+-
+- if (ec && exists(st))
+- return false;
+ }
+- while (st.type() == file_type::not_found);
+
+- bool created;
++ if (ec || missing.empty())
++ return false;
++
+ do
+ {
+ const path& top = missing.top();
+- created = create_directory(top, ec);
+- if (ec)
+- return false;
++ create_directory(top, ec);
++ if (ec && is_directory(top))
++ ec.clear();
+ missing.pop();
+ }
+- while (!missing.empty());
++ while (!missing.empty() && !ec);
+
+- return created;
++ return missing.empty();
+ }
+
+ namespace
+@@ -713,8 +687,9 @@ namespace
+ {
+ bool created = false;
+ #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+- ::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
+- if (::mkdir(p.c_str(), mode))
++ posix::mode_t mode
++ = static_cast<std::underlying_type_t<fs::perms>>(perm);
++ if (posix::mkdir(p.c_str(), mode))
+ {
+ const int err = errno;
+ if (err != EEXIST || !is_directory(p, ec))
+@@ -767,7 +742,7 @@ fs::create_directory(const path& p, const path& attributes,
+ {
+ #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+ stat_type st;
+- if (::stat(attributes.c_str(), &st))
++ if (posix::stat(attributes.c_str(), &st))
+ {
+ ec.assign(errno, std::generic_category());
+ return false;
+@@ -809,18 +784,23 @@ fs::create_hard_link(const path& to, const path& new_hard_link)
+ create_hard_link(to, new_hard_link, ec);
+ if (ec)
+ _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
+- to, new_hard_link, ec));
++ to, new_hard_link, ec));
+ }
+
+ void
+ fs::create_hard_link(const path& to, const path& new_hard_link,
+ error_code& ec) noexcept
+ {
+-#ifdef _GLIBCXX_HAVE_UNISTD_H
++#ifdef _GLIBCXX_HAVE_LINK
+ if (::link(to.c_str(), new_hard_link.c_str()))
+ ec.assign(errno, std::generic_category());
+ else
+ ec.clear();
++#elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
++ ec.clear();
++ else
++ ec.assign((int)GetLastError(), generic_category());
+ #else
+ ec = std::make_error_code(std::errc::not_supported);
+ #endif
+@@ -840,7 +820,7 @@ void
+ fs::create_symlink(const path& to, const path& new_symlink,
+ error_code& ec) noexcept
+ {
+-#ifdef _GLIBCXX_HAVE_UNISTD_H
++#ifdef _GLIBCXX_HAVE_SYMLINK
+ if (::symlink(to.c_str(), new_symlink.c_str()))
+ ec.assign(errno, std::generic_category());
+ else
+@@ -866,8 +846,8 @@ fs::current_path(error_code& ec)
+ {
+ path p;
+ #ifdef _GLIBCXX_HAVE_UNISTD_H
+-#ifdef __GLIBC__
+- if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)})
++#if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
+ {
+ p.assign(cwd.get());
+ ec.clear();
+@@ -875,6 +855,7 @@ fs::current_path(error_code& ec)
+ else
+ ec.assign(errno, std::generic_category());
+ #else
++#ifdef _PC_PATH_MAX
+ long path_max = pathconf(".", _PC_PATH_MAX);
+ size_t size;
+ if (path_max == -1)
+@@ -883,9 +864,15 @@ fs::current_path(error_code& ec)
+ size = 10240;
+ else
+ size = path_max;
++#elif defined(PATH_MAX)
++ size_t size = PATH_MAX;
++#else
++ size_t size = 1024;
++#endif
+ for (char_ptr buf; p.empty(); size *= 2)
+ {
+- buf.reset((char*)malloc(size));
++ using char_type = fs::path::value_type;
++ buf.reset((char_type*)malloc(size * sizeof(char_type)));
+ if (buf)
+ {
+ if (getcwd(buf.get(), size))
+@@ -925,7 +912,7 @@ void
+ fs::current_path(const path& p, error_code& ec) noexcept
+ {
+ #ifdef _GLIBCXX_HAVE_UNISTD_H
+- if (::chdir(p.c_str()))
++ if (posix::chdir(p.c_str()))
+ ec.assign(errno, std::generic_category());
+ else
+ ec.clear();
+@@ -952,14 +939,14 @@ fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
+ int err = 0;
+ file_status s1, s2;
+ stat_type st1, st2;
+- if (::stat(p1.c_str(), &st1) == 0)
++ if (posix::stat(p1.c_str(), &st1) == 0)
+ s1 = make_file_status(st1);
+ else if (is_not_found_errno(errno))
+ s1.type(file_type::not_found);
+ else
+ err = errno;
+
+- if (::stat(p2.c_str(), &st2) == 0)
++ if (posix::stat(p2.c_str(), &st2) == 0)
+ s2 = make_file_status(st2);
+ else if (is_not_found_errno(errno))
+ s2.type(file_type::not_found);
+@@ -1008,8 +995,8 @@ namespace
+ do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
+ {
+ #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+- fs::stat_type st;
+- if (::stat(p.c_str(), &st))
++ posix::stat_type st;
++ if (posix::stat(p.c_str(), &st))
+ {
+ ec.assign(errno, std::generic_category());
+ return deflt;
+@@ -1059,7 +1046,7 @@ fs::hard_link_count(const path& p)
+ std::uintmax_t
+ fs::hard_link_count(const path& p, error_code& ec) noexcept
+ {
+- return do_stat(p, ec, std::mem_fn(&stat::st_nlink),
++ return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
+ static_cast<uintmax_t>(-1));
+ }
+
+@@ -1135,11 +1122,11 @@ fs::last_write_time(const path& p __attribute__((__unused__)),
+ else
+ ec.clear();
+ #elif _GLIBCXX_HAVE_UTIME_H
+- ::utimbuf times;
++ posix::utimbuf times;
+ times.modtime = s.count();
+ times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
+ times.modtime);
+- if (::utime(p.c_str(), &times))
++ if (posix::utime(p.c_str(), &times))
+ ec.assign(errno, std::generic_category());
+ else
+ ec.clear();
+@@ -1194,7 +1181,7 @@ fs::permissions(const path& p, perms prms, perm_options opts,
+ #else
+ if (nofollow && is_symlink(st))
+ ec = std::make_error_code(std::errc::operation_not_supported);
+- else if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
++ else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
+ err = errno;
+ #endif
+
+@@ -1234,10 +1221,10 @@ fs::read_symlink(const path& p)
+ return tgt;
+ }
+
+-fs::path fs::read_symlink(const path& p, error_code& ec)
++fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec)
+ {
+ path result;
+-#ifdef _GLIBCXX_HAVE_SYS_STAT_H
++#if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
+ stat_type st;
+ if (::lstat(p.c_str(), &st))
+ {
+@@ -1310,6 +1297,19 @@ fs::remove(const path& p)
+ bool
+ fs::remove(const path& p, error_code& ec) noexcept
+ {
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ if (exists(symlink_status(p, ec)))
++ {
++ if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
++ || DeleteFileW(p.c_str()))
++ {
++ ec.clear();
++ return true;
++ }
++ else if (!ec)
++ ec.assign((int)GetLastError(), generic_category());
++ }
++#else
+ if (::remove(p.c_str()) == 0)
+ {
+ ec.clear();
+@@ -1319,6 +1319,7 @@ fs::remove(const path& p, error_code& ec) noexcept
+ ec.clear();
+ else
+ ec.assign(errno, std::generic_category());
++#endif
+ return false;
+ }
+
+@@ -1372,7 +1373,7 @@ fs::rename(const path& from, const path& to)
+ void
+ fs::rename(const path& from, const path& to, error_code& ec) noexcept
+ {
+- if (::rename(from.c_str(), to.c_str()))
++ if (posix::rename(from.c_str(), to.c_str()))
+ ec.assign(errno, std::generic_category());
+ else
+ ec.clear();
+@@ -1393,7 +1394,7 @@ fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
+ #ifdef _GLIBCXX_HAVE_UNISTD_H
+ if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
+ ec.assign(EINVAL, std::generic_category());
+- else if (::truncate(p.c_str(), size))
++ else if (posix::truncate(p.c_str(), size))
+ ec.assign(errno, std::generic_category());
+ else
+ ec.clear();
+@@ -1413,31 +1414,67 @@ fs::space(const path& p)
+ return s;
+ }
+
+-fs::space_info
+-fs::space(const path& p, error_code& ec) noexcept
++#ifdef NEED_DO_SPACE
++void
++fs::do_space(const __gnu_posix::char_type* pathname,
++ uintmax_t& capacity, uintmax_t& free, uintmax_t& available,
++ std::error_code& ec)
+ {
+- space_info info = {
+- static_cast<uintmax_t>(-1),
+- static_cast<uintmax_t>(-1),
+- static_cast<uintmax_t>(-1)
+- };
+ #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
+ struct ::statvfs f;
+- if (::statvfs(p.c_str(), &f))
++ if (::statvfs(pathname, &f))
+ ec.assign(errno, std::generic_category());
+ else
+ {
+- uintmax_t fragment_size = f.f_frsize;
+- info = space_info{
+- f.f_blocks * fragment_size,
+- f.f_bfree * fragment_size,
+- f.f_bavail * fragment_size
+- };
++ if (f.f_frsize != (unsigned long)-1)
++ {
++ const uintmax_t fragment_size = f.f_frsize;
++ const fsblkcnt_t unknown = -1;
++ if (f.f_blocks != unknown)
++ capacity = f.f_blocks * fragment_size;
++ if (f.f_bfree != unknown)
++ free = f.f_bfree * fragment_size;
++ if (f.f_bavail != unknown)
++ available = f.f_bavail * fragment_size;
++ }
+ ec.clear();
+ }
++#elif _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ ULARGE_INTEGER bytes_avail = {}, bytes_total = {}, bytes_free = {};
++ if (GetDiskFreeSpaceExW(pathname, &bytes_avail, &bytes_total, &bytes_free))
++ {
++ if (bytes_total.QuadPart != 0)
++ capacity = bytes_total.QuadPart;
++ if (bytes_free.QuadPart != 0)
++ free = bytes_free.QuadPart;
++ if (bytes_avail.QuadPart != 0)
++ available = bytes_avail.QuadPart;
++ ec.clear();
++ }
++ else
++ ec.assign((int)GetLastError(), std::system_category());
+ #else
+ ec = std::make_error_code(std::errc::not_supported);
+ #endif
++}
++#endif // NEED_DO_SPACE
++
++fs::space_info
++fs::space(const path& p, error_code& ec) noexcept
++{
++ space_info info = {
++ static_cast<uintmax_t>(-1),
++ static_cast<uintmax_t>(-1),
++ static_cast<uintmax_t>(-1)
++ };
++#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
++ path dir = absolute(p);
++ dir.remove_filename();
++ auto str = dir.c_str();
++#else
++ auto str = p.c_str();
++#endif
++ do_space(str, info.capacity, info.free, info.available, ec);
+ return info;
+ }
+
+@@ -1447,7 +1484,7 @@ fs::status(const fs::path& p, error_code& ec) noexcept
+ {
+ file_status status;
+ stat_type st;
+- if (::stat(p.c_str(), &st))
++ if (posix::stat(p.c_str(), &st))
+ {
+ int err = errno;
+ ec.assign(err, std::generic_category());
+@@ -1471,7 +1508,7 @@ fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
+ {
+ file_status status;
+ stat_type st;
+- if (::lstat(p.c_str(), &st))
++ if (posix::lstat(p.c_str(), &st))
+ {
+ int err = errno;
+ ec.assign(err, std::generic_category());
+@@ -1518,28 +1555,39 @@ fs::path fs::temp_directory_path()
+
+ fs::path fs::temp_directory_path(error_code& ec)
+ {
++ path p;
+ #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+- ec = std::make_error_code(std::errc::not_supported);
+- return {}; // TODO
++ unsigned len = 1024;
++ std::wstring buf;
++ do
++ {
++ buf.resize(len);
++ len = GetTempPathW(buf.size(), buf.data());
++ } while (len > buf.size());
++
++ if (len == 0)
++ {
++ ec.assign((int)GetLastError(), std::system_category());
++ return p;
++ }
++ buf.resize(len);
++ p = std::move(buf);
+ #else
+ const char* tmpdir = nullptr;
+ const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
+ for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
+ tmpdir = ::getenv(*e);
+- path p = tmpdir ? tmpdir : "/tmp";
++ p = tmpdir ? tmpdir : "/tmp";
++#endif
+ auto st = status(p, ec);
+- if (!ec)
++ if (ec)
++ p.clear();
++ else if (!is_directory(st))
+ {
+- if (is_directory(st))
+- {
+- ec.clear();
+- return p;
+- }
+- else
+- ec = std::make_error_code(std::errc::not_a_directory);
++ p.clear();
++ ec = std::make_error_code(std::errc::not_a_directory);
+ }
+- return {};
+-#endif
++ return p;
+ }
+
+ fs::path
+diff --git a/libstdc++-v3/src/filesystem/std-path.cc b/libstdc++-v3/src/filesystem/std-path.cc
+index c5bf8099036..f6c0b8bb0f6 100644
+--- a/libstdc++-v3/src/filesystem/std-path.cc
++++ b/libstdc++-v3/src/filesystem/std-path.cc
+@@ -38,6 +38,66 @@ fs::filesystem_error::~filesystem_error() = default;
+
+ constexpr path::value_type path::preferred_separator;
+
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++path&
++path::operator/=(const path& __p)
++{
++ if (__p.is_absolute()
++ || (__p.has_root_name() && __p.root_name() != root_name()))
++ return operator=(__p);
++
++ basic_string_view<value_type> __lhs = _M_pathname;
++ bool __add_sep = false;
++
++ if (__p.has_root_directory())
++ {
++ // Remove any root directory and relative path
++ if (_M_type != _Type::_Root_name)
++ {
++ if (!_M_cmpts.empty()
++ && _M_cmpts.front()._M_type == _Type::_Root_name)
++ __lhs = _M_cmpts.front()._M_pathname;
++ else
++ __lhs = {};
++ }
++ }
++ else if (has_filename() || (!has_root_directory() && is_absolute()))
++ __add_sep = true;
++
++ basic_string_view<value_type> __rhs = __p._M_pathname;
++ // Omit any root-name from the generic format pathname:
++ if (__p._M_type == _Type::_Root_name)
++ __rhs = {};
++ else if (!__p._M_cmpts.empty()
++ && __p._M_cmpts.front()._M_type == _Type::_Root_name)
++ __rhs.remove_prefix(__p._M_cmpts.front()._M_pathname.size());
++
++ const size_t __len = __lhs.size() + (int)__add_sep + __rhs.size();
++ const size_t __maxcmpts = _M_cmpts.size() + __p._M_cmpts.size();
++ if (_M_pathname.capacity() < __len || _M_cmpts.capacity() < __maxcmpts)
++ {
++ // Construct new path and swap (strong exception-safety guarantee).
++ string_type __tmp;
++ __tmp.reserve(__len);
++ __tmp = __lhs;
++ if (__add_sep)
++ __tmp += preferred_separator;
++ __tmp += __rhs;
++ path __newp = std::move(__tmp);
++ swap(__newp);
++ }
++ else
++ {
++ _M_pathname = __lhs;
++ if (__add_sep)
++ _M_pathname += preferred_separator;
++ _M_pathname += __rhs;
++ _M_split_cmpts();
++ }
++ return *this;
++}
++#endif
++
+ path&
+ path::remove_filename()
+ {
+@@ -74,6 +134,12 @@ path::replace_filename(const path& replacement)
+ return *this;
+ }
+
++#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
++const fs::path::value_type dot = L'.';
++#else
++const fs::path::value_type dot = '.';
++#endif
++
+ path&
+ path::replace_extension(const path& replacement)
+ {
+@@ -85,18 +151,17 @@ path::replace_extension(const path& replacement)
+ _M_pathname.erase(ext.second);
+ else
+ {
+- auto& back = _M_cmpts.back();
++ const auto& back = _M_cmpts.back();
+ if (ext.first != &back._M_pathname)
+ _GLIBCXX_THROW_OR_ABORT(
+ std::logic_error("path::replace_extension failed"));
+- back._M_pathname.erase(ext.second);
+ _M_pathname.erase(back._M_pos + ext.second);
+ }
+ }
+ // If replacement is not empty and does not begin with a dot character,
+ // a dot character is appended
+- if (!replacement.empty() && replacement.native()[0] != '.')
+- _M_pathname += '.';
++ if (!replacement.empty() && replacement.native()[0] != dot)
++ _M_pathname += dot;
+ operator+=(replacement);
+ return *this;
+ }
+@@ -333,11 +398,7 @@ path::has_filename() const
+
+ namespace
+ {
+-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+- inline bool is_dot(wchar_t c) { return c == L'.'; }
+-#else
+- inline bool is_dot(char c) { return c == '.'; }
+-#endif
++ inline bool is_dot(fs::path::value_type c) { return c == dot; }
+
+ inline bool is_dot(const fs::path& path)
+ {
+@@ -377,7 +438,7 @@ path::lexically_normal() const
+ {
+ #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+ // Replace each slash character in the root-name
+- if (p._M_type == _Type::_Root_name || p._M_type == _Type::_Root_dir)
++ if (p._M_type == _Type::_Root_name)
+ {
+ string_type s = p.native();
+ std::replace(s.begin(), s.end(), L'/', L'\\');
+@@ -397,8 +458,7 @@ path::lexically_normal() const
+ }
+ else if (!ret.has_relative_path())
+ {
+- // remove a dot-dot filename immediately after root-directory
+- if (!ret.has_root_directory())
++ if (!ret.is_absolute())
+ ret /= p;
+ }
+ else
+@@ -406,30 +466,15 @@ path::lexically_normal() const
+ // Got a path with a relative path (i.e. at least one non-root
+ // element) and no filename at the end (i.e. empty last element),
+ // so must have a trailing slash. See what is before it.
+- auto elem = ret._M_cmpts.end() - 2;
++ auto elem = std::prev(ret.end(), 2);
+ if (elem->has_filename() && !is_dotdot(*elem))
+ {
+ // Remove the filename before the trailing slash
+ // (equiv. to ret = ret.parent_path().remove_filename())
+-
+- if (elem == ret._M_cmpts.begin())
+- ret.clear();
+- else
+- {
+- ret._M_pathname.erase(elem->_M_pos);
+- // Remove empty filename at the end:
+- ret._M_cmpts.pop_back();
+- // If we still have a trailing non-root dir separator
+- // then leave an empty filename at the end:
+- if (std::prev(elem)->_M_type == _Type::_Filename)
+- elem->clear();
+- else // remove the component completely:
+- ret._M_cmpts.pop_back();
+- }
++ ret._M_pathname.erase(elem._M_cur->_M_pos);
++ ret._M_cmpts.erase(elem._M_cur, ret._M_cmpts.end());
+ }
+- else
+- // Append the ".." to something ending in "../" which happens
+- // when normalising paths like ".././.." and "../a/../.."
++ else // ???
+ ret /= p;
+ }
+ }
+@@ -475,12 +520,10 @@ path::lexically_relative(const path& base) const
+ const path& p = *b;
+ if (is_dotdot(p))
+ --n;
+- else if (!p.empty() && !is_dot(p))
++ else if (!is_dot(p))
+ ++n;
+ }
+- if (n == 0 && (a == end() || a->empty()))
+- ret = ".";
+- else if (n >= 0)
++ if (n >= 0)
+ {
+ const path dotdot("..");
+ while (n--)
+@@ -504,7 +547,7 @@ path::lexically_proximate(const path& base) const
+ std::pair<const path::string_type*, std::size_t>
+ path::_M_find_extension() const
+ {
+- const std::string* s = nullptr;
++ const string_type* s = nullptr;
+
+ if (_M_type == _Type::_Filename)
+ s = &_M_pathname;
+@@ -519,9 +562,9 @@ path::_M_find_extension() const
+ {
+ if (auto sz = s->size())
+ {
+- if (sz <= 2 && (*s)[0] == '.')
++ if (sz <= 2 && (*s)[0] == dot)
+ return { s, string_type::npos };
+- const auto pos = s->rfind('.');
++ const auto pos = s->rfind(dot);
+ return { s, pos ? pos : string_type::npos };
+ }
+ }
+@@ -722,8 +765,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
+
+ std::string filesystem_error::_M_gen_what()
+ {
+- return fs_err_concat(system_error::what(), _M_path1.native(),
+- _M_path2.native());
++ return fs_err_concat(system_error::what(), _M_path1.u8string(),
++ _M_path2.u8string());
+ }
+
+ _GLIBCXX_END_NAMESPACE_CXX11
+--
+2.20.1
+