Commit 9534a5e6 by Jonathan Wakely Committed by Jonathan Wakely

PR libstdc++/78870 support std::filesystem on Windows

	PR libstdc++/78870 support std::filesystem on Windows
	* config.h.in: Regenerate.
	* configure: Regenerate.
	* configure.ac: Check for link, readlink and symlink.
	* include/bits/fs_path.h (path::operator/=(const path&)): Move
	definition out of class body.
	(path::is_absolute(), path::_M_append(path)): Likewise.
	(operator<<(basic_ostream, const path&)): Use std::quoted directly.
	(operator>>(basic_istream, path&)): Likewise.
	(u8path): Reorder definitions and fix Windows implementation.
	(path::is_absolute()): Define inline and fix for Windows.
	[!_GLIBCXX_FILESYSTEM_IS_WINDOWS] (path::operator/=(const path&)):
	Define POSIX version inline.
	(path::_M_append(path)): Define inline.
	* include/experimental/bits/fs_path.h (path::is_absolute()): Move
	definition out of class body.
	(operator<<(basic_ostream, const path&)): Fix type of delimiter and
	escape characters.
	(operator>>(basic_istream, path&)): Likewise.
	(path::is_absolute()): Define inline and fix for Windows.
	* src/filesystem/dir-common.h (__gnu_posix): New namespace.
	(__gnu_posix::char_type, __gnu_posix::DIR, __gnu_posix::dirent)
	(__gnu_posix::opendir, __gnu_posix::readdir, __gnu_posix::closedir):
	Define as adaptors for Windows functions/types or as
	using-declarations for POSIX functions/types.
	(_Dir_base, get_file_type): Qualify names to use declarations from
	__gnu_posix namespace.
	(_Dir_base::is_dor_or_dotdot): New helper functions.
	* src/filesystem/dir.cc (_Dir, recursive_directory_iterator): Qualify
	names to use declarations from __gnu_posix namespace.
	* src/filesystem/ops-common.h (__gnu_posix): New nested namespace.
	(__gnu_posix::open, __gnu_posix::close, __gnu_posix::stat_type)
	(__gnu_posix::stat, __gnu_posix::lstat, __gnu_posix::mode_t)
	(__gnu_posix::chmod, __gnu_posix::mkdir, __gnu_posix::getcwd)
	(__gnu_posix::chdir, __gnu_posix::utimbuf, __gnu_posix::utime)
	(__gnu_posix::rename, __gnu_posix::truncate, __gnu_posix::char_type):
	Define as adaptors for Windows functions/types or as
	using-declarations for POSIX functions/types.
	(stat_type, do_copy_file): Qualify names to use declarations from
	__gnu_posix namespace.
	(do_space): Declare new function.
	(make_file_type): Only use S_ISLNK if defined.
	* src/filesystem/ops.cc (char_ptr, filesystem::canonical): Use
	path::value_type not char.
	(filesystem::copy, create_dir, filesystem::create_directory): Qualify
	names to use declarations from __gnu_posix namespace.
	(filesystem::create_hard_link): Check HAVE_LINK autoconf macro and
	add implementation for Windows.
	(filesystem::create_symlink): Check HAVE_SYMLINK autoconf macro.
	(filesystem::current_path(error_code&)): Use __gnu_posix::getcwd.
	[!_PC_PATH_MAX]: Don't use pathconf.
	[PATH_MAX]: Use if defined.
	(filesystem::current_path(const path&, error_code&))
	(filesystem::equivalent, do_stat, filesystem::hard_link_count)
	(filesystem::last_write_time, filesystem::permissions): Use names
	from __gnu_posix.
	(filesystem::read_symlink): Check HAVE_READLINK autoconf macro.
	(filesystem::remove) [_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Add
	implementation for Windows.
	(filesystem::rename, filesystem::resize_file): Use names from
	__gnu_posix.
	(filesystem::space): Use do_space.
	[_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Get absolute path to directory.
	(filesystem::status, filesystem::symlink_status): Use names from
	__gnu_posix.
	(filesystem::temp_directory_path): Add implementation for Windows.
	* src/filesystem/path.cc (dot): Define constant.
	(path::replace_extension): Use dot.
	(path::_M_find_extension): Likewise. Use path::string_type not
	std::string.
	(path::_M_split_cmpts): Use dot.
	(filesystem_error::_M_get_what): Use u8string() not native().
	* src/filesystem/std-dir.cc (_Dir, recursive_directory_iterator):
	Qualify names to use declarations from __gnu_posix namespace.
	* src/filesystem/std-ops.cc (filesystem::absolute(const path&)): Use
	correct error_code.
	(filesystem::absolute(const path&, error_code&)): Add implementation
	for Windows.
	(char_ptr, filesystem::canonical): Use path::value_type not char.
	(do_copy_file): Use names from __gnu_posix.
	[_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Do not use fchmod, fchmodat or
	sendfile.
	(filesystem::copy, create_dir, filesystem::create_directory): Qualify
	names to use declarations from __gnu_posix namespace.
	(filesystem::create_hard_link): Check HAVE_LINK autoconf macro and
	add implementation for Windows.
	(filesystem::create_symlink): Check HAVE_SYMLINK autoconf macro.
	(filesystem::current_path(error_code&)): Use __gnu_posix::getcwd.
	[!_PC_PATH_MAX]: Don't use pathconf.
	[PATH_MAX]: Use if defined.
	(filesystem::current_path(const path&, error_code&))
	(filesystem::equivalent, do_stat, filesystem::hard_link_count)
	(filesystem::last_write_time, filesystem::permissions): Use names
	from __gnu_posix.
	(filesystem::read_symlink): Check HAVE_READLINK autoconf macro.
	(filesystem::remove) [_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Add
	implementation for Windows.
	(filesystem::rename, filesystem::resize_file): Use names from
	__gnu_posix.
	(do_space): Define.
	(filesystem::space): Use do_space.
	(filesystem::status, filesystem::symlink_status): Use names from
	__gnu_posix.
	(filesystem::temp_directory_path): Add implementation for Windows.
	* src/filesystem/std-path.cc
	[_GLIBCXX_FILESYSTEM_IS_WINDOWS] (path::operator/=(const path&)):
	Define for Windows.
	(dot): Define constant.
	(path::replace_extension, is_dot): Use dot.
	(path::lexically_normal): Check _M_type instead of calling
	non-existent function.
	(path::_M_find_extension): Use dot. Use path::string_type not
	std::string.
	(path::_M_split_cmpts): Use dot.
	(filesystem_error::_M_get_what): Use u8string() not native().
	* testsuite/27_io/filesystem/iterators/directory_iterator.cc: Do not
	use symlinks.
	* testsuite/27_io/filesystem/iterators/recursive_directory_iterator.cc:
	Likewise.
	* testsuite/27_io/filesystem/operations/absolute.cc: Use
	__gnu_test::root_path() instead of "/" and add Windows-specific tests.
	* testsuite/27_io/filesystem/operations/canonical.cc: Use
	path::string() to get narrow string, not path::native().
	* testsuite/27_io/filesystem/operations/copy.cc: Construct fstreams
	with std::filesystem::path not std::basic_string.
	* testsuite/27_io/filesystem/operations/copy_file.cc: Likewise.
	* testsuite/27_io/filesystem/operations/exists.cc: Use
	__gnu_test::root_path() instead of "/".
	* testsuite/27_io/filesystem/operations/is_empty.cc: Construct
	fstreams with std::filesystem::path not std::basic_string.
	* testsuite/27_io/filesystem/operations/last_write_time.cc: Use
	path::string() to get narrow string.
	* testsuite/27_io/filesystem/operations/space.cc: Check results for
	errors, expect sensible values otherwise.
	* testsuite/27_io/filesystem/operations/temp_directory_path.cc: Add
	helpers for adjusting the environment on Windows.
	* testsuite/27_io/filesystem/path/append/path.cc: Test
	Windows-specific behaviour.
	* testsuite/27_io/filesystem/path/construct/format.cc: Fix creation
	of path::string_type objects.
	* testsuite/27_io/filesystem/path/construct/locale.cc: Compare native
	string to wide string on Windows.
	* testsuite/27_io/filesystem/path/decompose/root_directory.cc: Allow
	for backslash as root-directory.
	* testsuite/27_io/filesystem/path/decompose/stem.cc: Use
	path::string() to get narrow string.
	* testsuite/27_io/filesystem/path/itr/traversal.cc: Test Windows-style
	paths.
	* testsuite/27_io/filesystem/path/native/string.cc: Use string_type
	not std::string.
	* testsuite/27_io/filesystem/path/query/is_absolute.cc: Adjust for
	different definintion of absolute paths on Windows.
	* testsuite/experimental/filesystem/iterators/directory_iterator.cc:
	Do not use symlinks.
	* testsuite/experimental/filesystem/operations/absolute.cc: Test
	Windows behaviour.
	* testsuite/experimental/filesystem/operations/copy.cc: Construct
	fstreams with NTCTS not std::basic_string.
	* testsuite/experimental/filesystem/operations/copy_file.cc: Likewise.
	* testsuite/experimental/filesystem/operations/exists.cc: Use
	__gnu_test::root_path() instead of "/".
	* testsuite/experimental/filesystem/operations/is_empty.cc: Construct
	fstreams with NTCTS not std::basic_string.
	* testsuite/experimental/filesystem/operations/last_write_time.cc:
	Use path::string() to get narrow string.
	* testsuite/experimental/filesystem/operations/space.cc: Use
	__gnu_test::root_path() instead of "/".
	* testsuite/experimental/filesystem/operations/temp_directory_path.cc:
	Add helpers for adjusting the environment on Windows.
	* testsuite/experimental/filesystem/path/append/path.cc: Use
	path::string() to get narrow strings for comparisons.
	* testsuite/experimental/filesystem/path/concat/path.cc: Likewise.
	* testsuite/experimental/filesystem/path/decompose/root_directory.cc:
	Likewise.
	* testsuite/experimental/filesystem/path/decompose/stem.cc: Likewise.
	* testsuite/experimental/filesystem/path/native/string.cc: Use
	string_type not std::string.
	* testsuite/experimental/filesystem/path/query/is_absolute.cc:
	Adjust for different definintion of absolute paths on Windows.
	* testsuite/util/testsuite_fs.h (__gnu_test::root_path()): New
	function.
	(__gnu_test::scoped_file): Construct fstreams with NTCTS not
	std::basic_string.

From-SVN: r261034
parent c9315097
2018-05-24 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/78870 support std::filesystem on Windows
* config.h.in: Regenerate.
* configure: Regenerate.
* configure.ac: Check for link, readlink and symlink.
* include/bits/fs_path.h (path::operator/=(const path&)): Move
definition out of class body.
(path::is_absolute(), path::_M_append(path)): Likewise.
(operator<<(basic_ostream, const path&)): Use std::quoted directly.
(operator>>(basic_istream, path&)): Likewise.
(u8path): Reorder definitions and fix Windows implementation.
(path::is_absolute()): Define inline and fix for Windows.
[!_GLIBCXX_FILESYSTEM_IS_WINDOWS] (path::operator/=(const path&)):
Define POSIX version inline.
(path::_M_append(path)): Define inline.
* include/experimental/bits/fs_path.h (path::is_absolute()): Move
definition out of class body.
(operator<<(basic_ostream, const path&)): Fix type of delimiter and
escape characters.
(operator>>(basic_istream, path&)): Likewise.
(path::is_absolute()): Define inline and fix for Windows.
* src/filesystem/dir-common.h (__gnu_posix): New namespace.
(__gnu_posix::char_type, __gnu_posix::DIR, __gnu_posix::dirent)
(__gnu_posix::opendir, __gnu_posix::readdir, __gnu_posix::closedir):
Define as adaptors for Windows functions/types or as
using-declarations for POSIX functions/types.
(_Dir_base, get_file_type): Qualify names to use declarations from
__gnu_posix namespace.
(_Dir_base::is_dor_or_dotdot): New helper functions.
* src/filesystem/dir.cc (_Dir, recursive_directory_iterator): Qualify
names to use declarations from __gnu_posix namespace.
* src/filesystem/ops-common.h (__gnu_posix): New nested namespace.
(__gnu_posix::open, __gnu_posix::close, __gnu_posix::stat_type)
(__gnu_posix::stat, __gnu_posix::lstat, __gnu_posix::mode_t)
(__gnu_posix::chmod, __gnu_posix::mkdir, __gnu_posix::getcwd)
(__gnu_posix::chdir, __gnu_posix::utimbuf, __gnu_posix::utime)
(__gnu_posix::rename, __gnu_posix::truncate, __gnu_posix::char_type):
Define as adaptors for Windows functions/types or as
using-declarations for POSIX functions/types.
(stat_type, do_copy_file): Qualify names to use declarations from
__gnu_posix namespace.
(do_space): Declare new function.
(make_file_type): Only use S_ISLNK if defined.
* src/filesystem/ops.cc (char_ptr, filesystem::canonical): Use
path::value_type not char.
(filesystem::copy, create_dir, filesystem::create_directory): Qualify
names to use declarations from __gnu_posix namespace.
(filesystem::create_hard_link): Check HAVE_LINK autoconf macro and
add implementation for Windows.
(filesystem::create_symlink): Check HAVE_SYMLINK autoconf macro.
(filesystem::current_path(error_code&)): Use __gnu_posix::getcwd.
[!_PC_PATH_MAX]: Don't use pathconf.
[PATH_MAX]: Use if defined.
(filesystem::current_path(const path&, error_code&))
(filesystem::equivalent, do_stat, filesystem::hard_link_count)
(filesystem::last_write_time, filesystem::permissions): Use names
from __gnu_posix.
(filesystem::read_symlink): Check HAVE_READLINK autoconf macro.
(filesystem::remove) [_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Add
implementation for Windows.
(filesystem::rename, filesystem::resize_file): Use names from
__gnu_posix.
(filesystem::space): Use do_space.
[_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Get absolute path to directory.
(filesystem::status, filesystem::symlink_status): Use names from
__gnu_posix.
(filesystem::temp_directory_path): Add implementation for Windows.
* src/filesystem/path.cc (dot): Define constant.
(path::replace_extension): Use dot.
(path::_M_find_extension): Likewise. Use path::string_type not
std::string.
(path::_M_split_cmpts): Use dot.
(filesystem_error::_M_get_what): Use u8string() not native().
* src/filesystem/std-dir.cc (_Dir, recursive_directory_iterator):
Qualify names to use declarations from __gnu_posix namespace.
* src/filesystem/std-ops.cc (filesystem::absolute(const path&)): Use
correct error_code.
(filesystem::absolute(const path&, error_code&)): Add implementation
for Windows.
(char_ptr, filesystem::canonical): Use path::value_type not char.
(do_copy_file): Use names from __gnu_posix.
[_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Do not use fchmod, fchmodat or
sendfile.
(filesystem::copy, create_dir, filesystem::create_directory): Qualify
names to use declarations from __gnu_posix namespace.
(filesystem::create_hard_link): Check HAVE_LINK autoconf macro and
add implementation for Windows.
(filesystem::create_symlink): Check HAVE_SYMLINK autoconf macro.
(filesystem::current_path(error_code&)): Use __gnu_posix::getcwd.
[!_PC_PATH_MAX]: Don't use pathconf.
[PATH_MAX]: Use if defined.
(filesystem::current_path(const path&, error_code&))
(filesystem::equivalent, do_stat, filesystem::hard_link_count)
(filesystem::last_write_time, filesystem::permissions): Use names
from __gnu_posix.
(filesystem::read_symlink): Check HAVE_READLINK autoconf macro.
(filesystem::remove) [_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Add
implementation for Windows.
(filesystem::rename, filesystem::resize_file): Use names from
__gnu_posix.
(do_space): Define.
(filesystem::space): Use do_space.
(filesystem::status, filesystem::symlink_status): Use names from
__gnu_posix.
(filesystem::temp_directory_path): Add implementation for Windows.
* src/filesystem/std-path.cc
[_GLIBCXX_FILESYSTEM_IS_WINDOWS] (path::operator/=(const path&)):
Define for Windows.
(dot): Define constant.
(path::replace_extension, is_dot): Use dot.
(path::lexically_normal): Check _M_type instead of calling
non-existent function.
(path::_M_find_extension): Use dot. Use path::string_type not
std::string.
(path::_M_split_cmpts): Use dot.
(filesystem_error::_M_get_what): Use u8string() not native().
* testsuite/27_io/filesystem/iterators/directory_iterator.cc: Do not
use symlinks.
* testsuite/27_io/filesystem/iterators/recursive_directory_iterator.cc:
Likewise.
* testsuite/27_io/filesystem/operations/absolute.cc: Use
__gnu_test::root_path() instead of "/" and add Windows-specific tests.
* testsuite/27_io/filesystem/operations/canonical.cc: Use
path::string() to get narrow string, not path::native().
* testsuite/27_io/filesystem/operations/copy.cc: Construct fstreams
with std::filesystem::path not std::basic_string.
* testsuite/27_io/filesystem/operations/copy_file.cc: Likewise.
* testsuite/27_io/filesystem/operations/exists.cc: Use
__gnu_test::root_path() instead of "/".
* testsuite/27_io/filesystem/operations/is_empty.cc: Construct
fstreams with std::filesystem::path not std::basic_string.
* testsuite/27_io/filesystem/operations/last_write_time.cc: Use
path::string() to get narrow string.
* testsuite/27_io/filesystem/operations/space.cc: Check results for
errors, expect sensible values otherwise.
* testsuite/27_io/filesystem/operations/temp_directory_path.cc: Add
helpers for adjusting the environment on Windows.
* testsuite/27_io/filesystem/path/append/path.cc: Test
Windows-specific behaviour.
* testsuite/27_io/filesystem/path/construct/format.cc: Fix creation
of path::string_type objects.
* testsuite/27_io/filesystem/path/construct/locale.cc: Compare native
string to wide string on Windows.
* testsuite/27_io/filesystem/path/decompose/root_directory.cc: Allow
for backslash as root-directory.
* testsuite/27_io/filesystem/path/decompose/stem.cc: Use
path::string() to get narrow string.
* testsuite/27_io/filesystem/path/itr/traversal.cc: Test Windows-style
paths.
* testsuite/27_io/filesystem/path/native/string.cc: Use string_type
not std::string.
* testsuite/27_io/filesystem/path/query/is_absolute.cc: Adjust for
different definintion of absolute paths on Windows.
* testsuite/experimental/filesystem/iterators/directory_iterator.cc:
Do not use symlinks.
* testsuite/experimental/filesystem/operations/absolute.cc: Test
Windows behaviour.
* testsuite/experimental/filesystem/operations/copy.cc: Construct
fstreams with NTCTS not std::basic_string.
* testsuite/experimental/filesystem/operations/copy_file.cc: Likewise.
* testsuite/experimental/filesystem/operations/exists.cc: Use
__gnu_test::root_path() instead of "/".
* testsuite/experimental/filesystem/operations/is_empty.cc: Construct
fstreams with NTCTS not std::basic_string.
* testsuite/experimental/filesystem/operations/last_write_time.cc:
Use path::string() to get narrow string.
* testsuite/experimental/filesystem/operations/space.cc: Use
__gnu_test::root_path() instead of "/".
* testsuite/experimental/filesystem/operations/temp_directory_path.cc:
Add helpers for adjusting the environment on Windows.
* testsuite/experimental/filesystem/path/append/path.cc: Use
path::string() to get narrow strings for comparisons.
* testsuite/experimental/filesystem/path/concat/path.cc: Likewise.
* testsuite/experimental/filesystem/path/decompose/root_directory.cc:
Likewise.
* testsuite/experimental/filesystem/path/decompose/stem.cc: Likewise.
* testsuite/experimental/filesystem/path/native/string.cc: Use
string_type not std::string.
* testsuite/experimental/filesystem/path/query/is_absolute.cc:
Adjust for different definintion of absolute paths on Windows.
* testsuite/util/testsuite_fs.h (__gnu_test::root_path()): New
function.
(__gnu_test::scoped_file): Construct fstreams with NTCTS not
std::basic_string.
2018-05-31 Jonathan Wakely <jwakely@redhat.com> 2018-05-31 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/85951 PR libstdc++/85951
......
...@@ -264,6 +264,9 @@ ...@@ -264,6 +264,9 @@
/* Only used in build directory testsuite_hooks.h. */ /* Only used in build directory testsuite_hooks.h. */
#undef HAVE_LIMIT_VMEM #undef HAVE_LIMIT_VMEM
/* Define to 1 if you have the `link' function. */
#undef HAVE_LINK
/* Define if futex syscall is available. */ /* Define if futex syscall is available. */
#undef HAVE_LINUX_FUTEX #undef HAVE_LINUX_FUTEX
...@@ -339,6 +342,9 @@ ...@@ -339,6 +342,9 @@
/* Define to 1 if you have the `quick_exit' function. */ /* Define to 1 if you have the `quick_exit' function. */
#undef HAVE_QUICK_EXIT #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. */ /* Define to 1 if you have the `setenv' function. */
#undef HAVE_SETENV #undef HAVE_SETENV
...@@ -408,6 +414,9 @@ ...@@ -408,6 +414,9 @@
/* Define if strxfrm_l is available in <string.h>. */ /* Define if strxfrm_l is available in <string.h>. */
#undef HAVE_STRXFRM_L #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 /* Define to 1 if the target runtime linker supports binding the same symbol
to different versions. */ to different versions. */
#undef HAVE_SYMVER_SYMBOL_RENAMING_RUNTIME_SUPPORT #undef HAVE_SYMVER_SYMBOL_RENAMING_RUNTIME_SUPPORT
......
...@@ -80049,6 +80049,19 @@ fi ...@@ -80049,6 +80049,19 @@ fi
done 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
# Check whether --enable-libstdcxx-filesystem-ts was given. # Check whether --enable-libstdcxx-filesystem-ts was given.
if test "${enable_libstdcxx_filesystem_ts+set}" = set; then : if test "${enable_libstdcxx_filesystem_ts+set}" = set; then :
...@@ -420,6 +420,7 @@ GLIBCXX_CHECK_GTHREADS ...@@ -420,6 +420,7 @@ GLIBCXX_CHECK_GTHREADS
# For Filesystem TS. # For Filesystem TS.
AC_CHECK_HEADERS([fcntl.h dirent.h sys/statvfs.h utime.h]) AC_CHECK_HEADERS([fcntl.h dirent.h sys/statvfs.h utime.h])
AC_CHECK_FUNCS(link readlink symlink)
GLIBCXX_ENABLE_FILESYSTEM_TS GLIBCXX_ENABLE_FILESYSTEM_TS
GLIBCXX_CHECK_FILESYSTEM_DEPS GLIBCXX_CHECK_FILESYSTEM_DEPS
......
...@@ -37,11 +37,11 @@ ...@@ -37,11 +37,11 @@
#include <vector> #include <vector>
#include <locale> #include <locale>
#include <iosfwd> #include <iosfwd>
#include <iomanip>
#include <codecvt> #include <codecvt>
#include <string_view> #include <string_view>
#include <system_error> #include <system_error>
#include <bits/stl_algobase.h> #include <bits/stl_algobase.h>
#include <bits/quoted_string.h>
#include <bits/locale_conv.h> #include <bits/locale_conv.h>
#if defined(_WIN32) && !defined(__CYGWIN__) #if defined(_WIN32) && !defined(__CYGWIN__)
...@@ -232,37 +232,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -232,37 +232,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
// appends // appends
path& operator/=(const path& __p) 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;
}
template <class _Source> template <class _Source>
_Path<_Source>& _Path<_Source>&
...@@ -378,7 +348,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -378,7 +348,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
bool has_filename() const; bool has_filename() const;
bool has_stem() const; bool has_stem() const;
bool has_extension() const; bool has_extension() const;
bool is_absolute() const { return has_root_directory(); } bool is_absolute() const;
bool is_relative() const { return !is_absolute(); } bool is_relative() const { return !is_absolute(); }
// generation // generation
...@@ -419,19 +389,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -419,19 +389,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
enum class _Split { _Stem, _Extension }; enum class _Split { _Stem, _Extension };
path& path& _M_append(path __p);
_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;
}
pair<const string_type*, size_t> _M_find_extension() const; pair<const string_type*, size_t> _M_find_extension() const;
...@@ -552,10 +510,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -552,10 +510,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
basic_ostream<_CharT, _Traits>& basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __os, const path& __p) operator<<(basic_ostream<_CharT, _Traits>& __os, const path& __p)
{ {
auto __tmp = __p.string<_CharT, _Traits>(); __os << std::quoted(__p.string<_CharT, _Traits>());
using __quoted_string
= std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>;
__os << __quoted_string{__tmp, '"', '\\'};
return __os; return __os;
} }
...@@ -565,40 +520,55 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -565,40 +520,55 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
operator>>(basic_istream<_CharT, _Traits>& __is, path& __p) operator>>(basic_istream<_CharT, _Traits>& __is, path& __p)
{ {
basic_string<_CharT, _Traits> __tmp; basic_string<_CharT, _Traits> __tmp;
using __quoted_string if (__is >> std::quoted(__tmp))
= std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>;
if (__is >> __quoted_string{ __tmp, '"', '\\' })
__p = std::move(__tmp); __p = std::move(__tmp);
return __is; return __is;
} }
template<typename _Source> template<typename _InputIterator>
inline auto inline auto
u8path(const _Source& __source) u8path(_InputIterator __first, _InputIterator __last)
-> decltype(filesystem::path(__source, std::locale::classic())) -> decltype(filesystem::path(__first, __last, std::locale::classic()))
{ {
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
const std::string __u8str{__source}; codecvt_utf8<path::value_type> __cvt;
return std::filesystem::u8path(__u8str.begin(), __u8str.end()); 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 #else
return path{ __source }; return path{ __first, __last };
#endif #endif
} }
template<typename _InputIterator> template<typename _Source>
inline auto inline auto
u8path(_InputIterator __first, _InputIterator __last) u8path(const _Source& __source)
-> decltype(filesystem::path(__first, __last, std::locale::classic())) -> decltype(filesystem::path(__source, std::locale::classic()))
{ {
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
codecvt_utf8<value_type> __cvt; if constexpr (is_convertible_v<const _Source&, std::string_view>)
string_type __tmp; {
if (__str_codecvt_in(__first, __last, __tmp, __cvt)) const std::string_view __s = __source;
return path{ __tmp }; return filesystem::u8path(__s.data(), __s.data() + __s.size());
}
else else
return {}; {
std::string __s = path::_S_string_from_iter(__source);
return filesystem::u8path(__s.data(), __s.data() + __s.size());
}
#else #else
return path{ __first, __last }; return path{ __source };
#endif #endif
} }
...@@ -1068,6 +1038,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -1068,6 +1038,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
return ext.first && ext.second != string_type::npos; 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 inline path::iterator
path::begin() const path::begin() const
{ {
...@@ -1084,6 +1064,38 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -1084,6 +1064,38 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
return iterator(this, true); 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& inline path::iterator&
path::iterator::operator++() path::iterator::operator++()
{ {
......
...@@ -372,7 +372,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -372,7 +372,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
bool has_filename() const; bool has_filename() const;
bool has_stem() const; bool has_stem() const;
bool has_extension() const; bool has_extension() const;
bool is_absolute() const { return has_root_directory(); } bool is_absolute() const;
bool is_relative() const { return !is_absolute(); } bool is_relative() const { return !is_absolute(); }
// iterators // iterators
...@@ -537,7 +537,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -537,7 +537,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
auto __tmp = __p.string<_CharT, _Traits>(); auto __tmp = __p.string<_CharT, _Traits>();
using __quoted_string using __quoted_string
= std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>; = std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>;
__os << __quoted_string{__tmp, '"', '\\'}; __os << __quoted_string{__tmp, _CharT('"'), _CharT('\\')};
return __os; return __os;
} }
...@@ -549,7 +549,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -549,7 +549,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
basic_string<_CharT, _Traits> __tmp; basic_string<_CharT, _Traits> __tmp;
using __quoted_string using __quoted_string
= std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>; = std::__detail::_Quoted_string<decltype(__tmp)&, _CharT>;
if (__is >> __quoted_string{ __tmp, '"', '\\' }) if (__is >> __quoted_string{ __tmp, _CharT('"'), _CharT('\\') })
__p = std::move(__tmp); __p = std::move(__tmp);
return __is; return __is;
} }
...@@ -993,6 +993,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -993,6 +993,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
return ext.first && ext.second != string_type::npos; 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 inline path::iterator
path::begin() const path::begin() const
{ {
......
...@@ -26,6 +26,9 @@ ...@@ -26,6 +26,9 @@
#define _GLIBCXX_DIR_COMMON_H 1 #define _GLIBCXX_DIR_COMMON_H 1
#include <string.h> // strcmp #include <string.h> // strcmp
#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
#include <wchar.h> // wcscmp
#endif
#ifdef _GLIBCXX_HAVE_DIRENT_H #ifdef _GLIBCXX_HAVE_DIRENT_H
# ifdef _GLIBCXX_HAVE_SYS_TYPES_H # ifdef _GLIBCXX_HAVE_SYS_TYPES_H
# include <sys/types.h> # include <sys/types.h>
...@@ -35,26 +38,42 @@ ...@@ -35,26 +38,42 @@
# error "the <dirent.h> header is needed to build the Filesystem TS" # error "the <dirent.h> header is needed to build the Filesystem TS"
#endif #endif
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
# undef opendir
# define opendir _wopendir
#endif
namespace std _GLIBCXX_VISIBILITY(default) namespace std _GLIBCXX_VISIBILITY(default)
{ {
_GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_BEGIN_NAMESPACE_VERSION
namespace filesystem 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 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, // If no error occurs then dirp is non-null,
// otherwise null (whether error ignored or not). // 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 error_code& ec) noexcept
: dirp(::opendir(p)) : dirp(posix::opendir(pathname))
{ {
if (dirp) if (dirp)
ec.clear(); ec.clear();
...@@ -72,22 +91,22 @@ struct _Dir_base ...@@ -72,22 +91,22 @@ struct _Dir_base
_Dir_base& operator=(_Dir_base&&) = delete; _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 advance(bool skip_permission_denied, error_code& ec) noexcept
{ {
ec.clear(); ec.clear();
int err = std::exchange(errno, 0); 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 // std::swap cannot be used with Bionic's errno
err = std::exchange(errno, err); err = std::exchange(errno, err);
if (entp) if (entp)
{ {
// skip past dot and dot-dot // 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 advance(skip_permission_denied, ec);
return entp; return entp;
} }
...@@ -105,15 +124,24 @@ struct _Dir_base ...@@ -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 } // namespace filesystem
// BEGIN/END macros must be defined before including this file. // BEGIN/END macros must be defined before including this file.
_GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
inline file_type 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 #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
switch (d.d_type) switch (d.d_type)
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "dir-common.h" #include "dir-common.h"
namespace fs = std::experimental::filesystem; namespace fs = std::experimental::filesystem;
namespace posix = std::filesystem::__gnu_posix;
struct fs::_Dir : std::filesystem::_Dir_base struct fs::_Dir : std::filesystem::_Dir_base
{ {
...@@ -47,7 +48,7 @@ struct fs::_Dir : std::filesystem::_Dir_base ...@@ -47,7 +48,7 @@ struct fs::_Dir : std::filesystem::_Dir_base
path = p; 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; _Dir(_Dir&&) = default;
...@@ -185,7 +186,7 @@ recursive_directory_iterator(const path& p, directory_options options, ...@@ -185,7 +186,7 @@ recursive_directory_iterator(const path& p, directory_options options,
{ {
if (ec) if (ec)
ec->clear(); 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>(); auto sp = std::make_shared<_Dir_stack>();
sp->push(_Dir{ dirp, p }); sp->push(_Dir{ dirp, p });
......
...@@ -34,12 +34,103 @@ ...@@ -34,12 +34,103 @@
# include <sys/stat.h> # include <sys/stat.h>
# endif # endif
#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) namespace std _GLIBCXX_VISIBILITY(default)
{ {
_GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_BEGIN_NAMESPACE_VERSION
namespace filesystem 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> template<typename Bitmask>
inline bool is_set(Bitmask obj, Bitmask bits) inline bool is_set(Bitmask obj, Bitmask bits)
{ {
...@@ -53,7 +144,7 @@ namespace filesystem ...@@ -53,7 +144,7 @@ namespace filesystem
} }
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #ifdef _GLIBCXX_HAVE_SYS_STAT_H
typedef struct ::stat stat_type; using __gnu_posix::stat_type;
inline std::chrono::system_clock::time_point inline std::chrono::system_clock::time_point
file_time(const stat_type& st, std::error_code& ec) noexcept file_time(const stat_type& st, std::error_code& ec) noexcept
...@@ -82,11 +173,17 @@ namespace filesystem ...@@ -82,11 +173,17 @@ namespace filesystem
}; };
bool 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, copy_options_existing_file options,
stat_type* from_st, stat_type* to_st, stat_type* from_st, stat_type* to_st,
std::error_code& ec) noexcept; 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 #endif // _GLIBCXX_HAVE_SYS_STAT_H
} // namespace filesystem } // namespace filesystem
...@@ -95,7 +192,7 @@ namespace filesystem ...@@ -95,7 +192,7 @@ namespace filesystem
_GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #ifdef _GLIBCXX_HAVE_SYS_STAT_H
typedef struct ::stat stat_type; using std::filesystem::__gnu_posix::stat_type;
inline file_type inline file_type
make_file_type(const stat_type& st) noexcept make_file_type(const stat_type& st) noexcept
...@@ -111,8 +208,10 @@ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM ...@@ -111,8 +208,10 @@ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
return file_type::block; return file_type::block;
else if (S_ISFIFO(st.st_mode)) else if (S_ISFIFO(st.st_mode))
return file_type::fifo; return file_type::fifo;
#ifdef S_ISLNK // not present in mingw
else if (S_ISLNK(st.st_mode)) else if (S_ISLNK(st.st_mode))
return file_type::symlink; return file_type::symlink;
#endif
#ifdef S_ISSOCK // not present until POSIX:2001 #ifdef S_ISSOCK // not present until POSIX:2001
else if (S_ISSOCK(st.st_mode)) else if (S_ISSOCK(st.st_mode))
return file_type::socket; return file_type::socket;
......
...@@ -47,20 +47,17 @@ ...@@ -47,20 +47,17 @@
#if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
# include <utime.h> // utime # include <utime.h> // utime
#endif #endif
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
# include <windows.h>
#endif
#define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \ #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
namespace experimental { namespace filesystem { namespace experimental { namespace filesystem {
#define _GLIBCXX_END_NAMESPACE_FILESYSTEM } } #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
#include "ops-common.h" #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 fs = std::experimental::filesystem;
namespace posix = std::filesystem::__gnu_posix;
fs::path fs::path
fs::absolute(const path& p, const path& base) fs::absolute(const path& p, const path& base)
...@@ -109,7 +106,7 @@ namespace ...@@ -109,7 +106,7 @@ namespace
void operator()(void* p) const { ::free(p); } 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 fs::path
...@@ -122,7 +119,8 @@ fs::canonical(const path& p, const path& base, error_code& ec) ...@@ -122,7 +119,8 @@ fs::canonical(const path& p, const path& base, error_code& ec)
char_ptr buf{ nullptr }; char_ptr buf{ nullptr };
# if _XOPEN_VERSION < 700 # if _XOPEN_VERSION < 700
// Not safe to call realpath(path, NULL) // 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 # endif
if (char* rp = ::realpath(pa.c_str(), buf.get())) if (char* rp = ::realpath(pa.c_str(), buf.get()))
{ {
...@@ -241,12 +239,13 @@ namespace ...@@ -241,12 +239,13 @@ namespace
using std::filesystem::is_set; using std::filesystem::is_set;
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #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::is_not_found_errno;
using std::filesystem::file_time; using std::filesystem::file_time;
using std::filesystem::do_copy_file; using std::filesystem::do_copy_file;
#endif // _GLIBCXX_HAVE_SYS_STAT_H #endif // _GLIBCXX_HAVE_SYS_STAT_H
} // namespace } // namespace
void void
...@@ -263,15 +262,15 @@ fs::copy(const path& from, const path& to, copy_options options, ...@@ -263,15 +262,15 @@ fs::copy(const path& from, const path& to, copy_options options,
// _GLIBCXX_RESOLVE_LIB_DEFECTS // _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2681. filesystem::copy() cannot copy symlinks // 2681. filesystem::copy() cannot copy symlinks
if (use_lstat || copy_symlinks if (use_lstat || copy_symlinks
? ::lstat(from.c_str(), &from_st) ? posix::lstat(from.c_str(), &from_st)
: ::stat(from.c_str(), &from_st)) : posix::stat(from.c_str(), &from_st))
{ {
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
return; return;
} }
if (use_lstat if (use_lstat
? ::lstat(to.c_str(), &to_st) ? posix::lstat(to.c_str(), &to_st)
: ::stat(to.c_str(), &to_st)) : posix::stat(to.c_str(), &to_st))
{ {
if (!is_not_found_errno(errno)) if (!is_not_found_errno(errno))
{ {
...@@ -459,8 +458,8 @@ namespace ...@@ -459,8 +458,8 @@ namespace
{ {
bool created = false; bool created = false;
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #ifdef _GLIBCXX_HAVE_SYS_STAT_H
::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm); posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
if (::mkdir(p.c_str(), mode)) if (posix::mkdir(p.c_str(), mode))
{ {
const int err = errno; const int err = errno;
if (err != EEXIST || !is_directory(p, ec)) if (err != EEXIST || !is_directory(p, ec))
...@@ -513,7 +512,7 @@ fs::create_directory(const path& p, const path& attributes, ...@@ -513,7 +512,7 @@ fs::create_directory(const path& p, const path& attributes,
{ {
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #ifdef _GLIBCXX_HAVE_SYS_STAT_H
stat_type st; stat_type st;
if (::stat(attributes.c_str(), &st)) if (posix::stat(attributes.c_str(), &st))
{ {
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
return false; return false;
...@@ -562,11 +561,16 @@ void ...@@ -562,11 +561,16 @@ void
fs::create_hard_link(const path& to, const path& new_hard_link, fs::create_hard_link(const path& to, const path& new_hard_link,
error_code& ec) noexcept error_code& ec) noexcept
{ {
#ifdef _GLIBCXX_HAVE_UNISTD_H #ifdef _GLIBCXX_HAVE_LINK
if (::link(to.c_str(), new_hard_link.c_str())) if (::link(to.c_str(), new_hard_link.c_str()))
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
else else
ec.clear(); 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 #else
ec = std::make_error_code(std::errc::not_supported); ec = std::make_error_code(std::errc::not_supported);
#endif #endif
...@@ -586,7 +590,7 @@ void ...@@ -586,7 +590,7 @@ void
fs::create_symlink(const path& to, const path& new_symlink, fs::create_symlink(const path& to, const path& new_symlink,
error_code& ec) noexcept error_code& ec) noexcept
{ {
#ifdef _GLIBCXX_HAVE_UNISTD_H #ifdef _GLIBCXX_HAVE_SYMLINK
if (::symlink(to.c_str(), new_symlink.c_str())) if (::symlink(to.c_str(), new_symlink.c_str()))
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
else else
...@@ -596,7 +600,6 @@ fs::create_symlink(const path& to, const path& new_symlink, ...@@ -596,7 +600,6 @@ fs::create_symlink(const path& to, const path& new_symlink,
#endif #endif
} }
fs::path fs::path
fs::current_path() fs::current_path()
{ {
...@@ -612,8 +615,8 @@ fs::current_path(error_code& ec) ...@@ -612,8 +615,8 @@ fs::current_path(error_code& ec)
{ {
path p; path p;
#ifdef _GLIBCXX_HAVE_UNISTD_H #ifdef _GLIBCXX_HAVE_UNISTD_H
#ifdef __GLIBC__ #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)}) if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
{ {
p.assign(cwd.get()); p.assign(cwd.get());
ec.clear(); ec.clear();
...@@ -621,6 +624,7 @@ fs::current_path(error_code& ec) ...@@ -621,6 +624,7 @@ fs::current_path(error_code& ec)
else else
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
#else #else
#ifdef _PC_PATH_MAX
long path_max = pathconf(".", _PC_PATH_MAX); long path_max = pathconf(".", _PC_PATH_MAX);
size_t size; size_t size;
if (path_max == -1) if (path_max == -1)
...@@ -629,9 +633,15 @@ fs::current_path(error_code& ec) ...@@ -629,9 +633,15 @@ fs::current_path(error_code& ec)
size = 10240; size = 10240;
else else
size = path_max; 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) 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 (buf)
{ {
if (getcwd(buf.get(), size)) if (getcwd(buf.get(), size))
...@@ -671,7 +681,7 @@ void ...@@ -671,7 +681,7 @@ void
fs::current_path(const path& p, error_code& ec) noexcept fs::current_path(const path& p, error_code& ec) noexcept
{ {
#ifdef _GLIBCXX_HAVE_UNISTD_H #ifdef _GLIBCXX_HAVE_UNISTD_H
if (::chdir(p.c_str())) if (posix::chdir(p.c_str()))
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
else else
ec.clear(); ec.clear();
...@@ -698,14 +708,14 @@ fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept ...@@ -698,14 +708,14 @@ fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
int err = 0; int err = 0;
file_status s1, s2; file_status s1, s2;
stat_type st1, st2; stat_type st1, st2;
if (::stat(p1.c_str(), &st1) == 0) if (posix::stat(p1.c_str(), &st1) == 0)
s1 = make_file_status(st1); s1 = make_file_status(st1);
else if (is_not_found_errno(errno)) else if (is_not_found_errno(errno))
s1.type(file_type::not_found); s1.type(file_type::not_found);
else else
err = errno; err = errno;
if (::stat(p2.c_str(), &st2) == 0) if (posix::stat(p2.c_str(), &st2) == 0)
s2 = make_file_status(st2); s2 = make_file_status(st2);
else if (is_not_found_errno(errno)) else if (is_not_found_errno(errno))
s2.type(file_type::not_found); s2.type(file_type::not_found);
...@@ -755,7 +765,7 @@ namespace ...@@ -755,7 +765,7 @@ namespace
{ {
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #ifdef _GLIBCXX_HAVE_SYS_STAT_H
stat_type st; stat_type st;
if (::stat(p.c_str(), &st)) if (posix::stat(p.c_str(), &st))
{ {
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
return deflt; return deflt;
...@@ -805,7 +815,7 @@ fs::hard_link_count(const path& p) ...@@ -805,7 +815,7 @@ fs::hard_link_count(const path& p)
std::uintmax_t std::uintmax_t
fs::hard_link_count(const path& p, error_code& ec) noexcept 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)); static_cast<uintmax_t>(-1));
} }
...@@ -881,11 +891,11 @@ fs::last_write_time(const path& p __attribute__((__unused__)), ...@@ -881,11 +891,11 @@ fs::last_write_time(const path& p __attribute__((__unused__)),
else else
ec.clear(); ec.clear();
#elif _GLIBCXX_HAVE_UTIME_H #elif _GLIBCXX_HAVE_UTIME_H
::utimbuf times; posix::utimbuf times;
times.modtime = s.count(); times.modtime = s.count();
times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; }, times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
times.modtime); times.modtime);
if (::utime(p.c_str(), &times)) if (posix::utime(p.c_str(), &times))
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
else else
ec.clear(); ec.clear();
...@@ -938,7 +948,7 @@ fs::permissions(const path& p, perms prms, error_code& ec) noexcept ...@@ -938,7 +948,7 @@ fs::permissions(const path& p, perms prms, error_code& ec) noexcept
#else #else
if (nofollow && is_symlink(st)) if (nofollow && is_symlink(st))
ec = std::make_error_code(std::errc::operation_not_supported); 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; err = errno;
#endif #endif
...@@ -958,10 +968,10 @@ fs::read_symlink(const path& p) ...@@ -958,10 +968,10 @@ fs::read_symlink(const path& p)
return tgt; 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; path result;
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
stat_type st; stat_type st;
if (::lstat(p.c_str(), &st)) if (::lstat(p.c_str(), &st))
{ {
...@@ -1015,6 +1025,19 @@ fs::remove(const path& p) ...@@ -1015,6 +1025,19 @@ fs::remove(const path& p)
bool bool
fs::remove(const path& p, error_code& ec) noexcept 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) if (::remove(p.c_str()) == 0)
{ {
ec.clear(); ec.clear();
...@@ -1024,6 +1047,7 @@ fs::remove(const path& p, error_code& ec) noexcept ...@@ -1024,6 +1047,7 @@ fs::remove(const path& p, error_code& ec) noexcept
ec.clear(); ec.clear();
else else
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
#endif
return false; return false;
} }
...@@ -1077,7 +1101,7 @@ fs::rename(const path& from, const path& to) ...@@ -1077,7 +1101,7 @@ fs::rename(const path& from, const path& to)
void void
fs::rename(const path& from, const path& to, error_code& ec) noexcept 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()); ec.assign(errno, std::generic_category());
else else
ec.clear(); ec.clear();
...@@ -1098,7 +1122,7 @@ fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept ...@@ -1098,7 +1122,7 @@ fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
#ifdef _GLIBCXX_HAVE_UNISTD_H #ifdef _GLIBCXX_HAVE_UNISTD_H
if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max())) if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
ec.assign(EINVAL, std::generic_category()); 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()); ec.assign(errno, std::generic_category());
else else
ec.clear(); ec.clear();
...@@ -1126,23 +1150,14 @@ fs::space(const path& p, error_code& ec) noexcept ...@@ -1126,23 +1150,14 @@ fs::space(const path& p, error_code& ec) noexcept
static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1),
static_cast<uintmax_t>(-1) static_cast<uintmax_t>(-1)
}; };
#ifdef _GLIBCXX_HAVE_SYS_STATVFS_H #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
struct ::statvfs f; path dir = absolute(p);
if (::statvfs(p.c_str(), &f)) dir.remove_filename();
ec.assign(errno, std::generic_category()); auto str = dir.c_str();
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();
}
#else #else
ec = std::make_error_code(std::errc::not_supported); auto str = p.c_str();
#endif #endif
std::filesystem::do_space(str, info.capacity, info.free, info.available, ec);
return info; return info;
} }
...@@ -1152,7 +1167,7 @@ fs::status(const fs::path& p, error_code& ec) noexcept ...@@ -1152,7 +1167,7 @@ fs::status(const fs::path& p, error_code& ec) noexcept
{ {
file_status status; file_status status;
stat_type st; stat_type st;
if (::stat(p.c_str(), &st)) if (posix::stat(p.c_str(), &st))
{ {
int err = errno; int err = errno;
ec.assign(err, std::generic_category()); ec.assign(err, std::generic_category());
...@@ -1176,7 +1191,7 @@ fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept ...@@ -1176,7 +1191,7 @@ fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
{ {
file_status status; file_status status;
stat_type st; stat_type st;
if (::lstat(p.c_str(), &st)) if (posix::lstat(p.c_str(), &st))
{ {
int err = errno; int err = errno;
ec.assign(err, std::generic_category()); ec.assign(err, std::generic_category());
...@@ -1251,27 +1266,38 @@ fs::path fs::temp_directory_path() ...@@ -1251,27 +1266,38 @@ fs::path fs::temp_directory_path()
fs::path fs::temp_directory_path(error_code& ec) fs::path fs::temp_directory_path(error_code& ec)
{ {
path p;
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
ec = std::make_error_code(std::errc::not_supported); unsigned len = 1024;
return {}; // TODO 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 #else
const char* tmpdir = nullptr; const char* tmpdir = nullptr;
const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr }; const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e) for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
tmpdir = ::getenv(*e); tmpdir = ::getenv(*e);
path p = tmpdir ? tmpdir : "/tmp"; p = tmpdir ? tmpdir : "/tmp";
auto st = status(p, ec); auto st = status(p, ec);
if (!ec) if (ec)
p.clear();
else if (!is_directory(st))
{ {
if (is_directory(st)) p.clear();
{ ec = std::make_error_code(std::errc::not_a_directory);
ec.clear();
return p;
}
else
ec = std::make_error_code(std::errc::not_a_directory);
} }
return {};
#endif #endif
return p;
} }
...@@ -61,6 +61,12 @@ path::replace_filename(const path& replacement) ...@@ -61,6 +61,12 @@ path::replace_filename(const path& replacement)
return *this; return *this;
} }
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
const fs::path::value_type dot = L'.';
#else
const fs::path::value_type dot = '.';
#endif
path& path&
path::replace_extension(const path& replacement) path::replace_extension(const path& replacement)
{ {
...@@ -78,8 +84,8 @@ path::replace_extension(const path& replacement) ...@@ -78,8 +84,8 @@ path::replace_extension(const path& replacement)
_M_pathname.erase(back._M_pos + ext.second); _M_pathname.erase(back._M_pos + ext.second);
} }
} }
if (!replacement.empty() && replacement.native()[0] != '.') if (!replacement.empty() && replacement.native()[0] != dot)
_M_pathname += '.'; _M_pathname += dot;
_M_pathname += replacement.native(); _M_pathname += replacement.native();
_M_split_cmpts(); _M_split_cmpts();
return *this; return *this;
...@@ -297,7 +303,7 @@ path::has_filename() const ...@@ -297,7 +303,7 @@ path::has_filename() const
std::pair<const path::string_type*, std::size_t> std::pair<const path::string_type*, std::size_t>
path::_M_find_extension() const path::_M_find_extension() const
{ {
const std::string* s = nullptr; const string_type* s = nullptr;
if (_M_type != _Type::_Multi) if (_M_type != _Type::_Multi)
s = &_M_pathname; s = &_M_pathname;
...@@ -312,14 +318,14 @@ path::_M_find_extension() const ...@@ -312,14 +318,14 @@ path::_M_find_extension() const
{ {
if (auto sz = s->size()) 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 }; return { s, string_type::npos };
else else
return { s, 0 }; // filename is like ".?" return { s, 0 }; // filename is like ".?"
} }
return { s, s->rfind('.') }; return { s, s->rfind(dot) };
} }
} }
return {}; return {};
...@@ -405,7 +411,7 @@ path::_M_split_cmpts() ...@@ -405,7 +411,7 @@ path::_M_split_cmpts()
{ {
const auto& last = _M_cmpts.back(); const auto& last = _M_cmpts.back();
pos = last._M_pos + last._M_pathname.size(); 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 ...@@ -495,8 +501,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
std::string filesystem_error::_M_gen_what() std::string filesystem_error::_M_gen_what()
{ {
using std::filesystem::fs_err_concat; using std::filesystem::fs_err_concat;
return fs_err_concat(system_error::what(), _M_path1.native(), return fs_err_concat(system_error::what(), _M_path1.u8string(),
_M_path2.native()); _M_path2.u8string());
} }
_GLIBCXX_END_NAMESPACE_CXX11 _GLIBCXX_END_NAMESPACE_CXX11
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "dir-common.h" #include "dir-common.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace posix = std::filesystem::__gnu_posix;
struct fs::_Dir : _Dir_base struct fs::_Dir : _Dir_base
{ {
...@@ -47,7 +48,7 @@ struct fs::_Dir : _Dir_base ...@@ -47,7 +48,7 @@ struct fs::_Dir : _Dir_base
path = p; 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; _Dir(_Dir&&) = default;
...@@ -180,7 +181,7 @@ recursive_directory_iterator(const path& p, directory_options options, ...@@ -180,7 +181,7 @@ recursive_directory_iterator(const path& p, directory_options options,
error_code* ecptr) error_code* ecptr)
: _M_options(options), _M_pending(true) : _M_options(options), _M_pending(true)
{ {
if (DIR* dirp = ::opendir(p.c_str())) if (posix::DIR* dirp = posix::opendir(p.c_str()))
{ {
if (ecptr) if (ecptr)
ecptr->clear(); ecptr->clear();
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#ifndef _GLIBCXX_USE_CXX11_ABI #ifndef _GLIBCXX_USE_CXX11_ABI
# define _GLIBCXX_USE_CXX11_ABI 1 # define _GLIBCXX_USE_CXX11_ABI 1
# define NEED_DO_COPY_FILE # define NEED_DO_COPY_FILE
# define NEED_DO_SPACE
#endif #endif
#include <filesystem> #include <filesystem>
...@@ -52,19 +53,16 @@ ...@@ -52,19 +53,16 @@
#if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
# include <utime.h> // utime # include <utime.h> // utime
#endif #endif
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
# include <windows.h>
#endif
#define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem { #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
#define _GLIBCXX_END_NAMESPACE_FILESYSTEM } #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
#include "ops-common.h" #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 fs = std::filesystem;
namespace posix = std::filesystem::__gnu_posix;
fs::path fs::path
fs::absolute(const path& p) fs::absolute(const path& p)
...@@ -74,7 +72,7 @@ fs::absolute(const path& p) ...@@ -74,7 +72,7 @@ fs::absolute(const path& p)
path ret = absolute(p, ec); path ret = absolute(p, ec);
if (ec) if (ec)
_GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p, _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p,
std::make_error_code(errc::not_supported))); ec));
return ret; return ret;
#else #else
return current_path() / p; return current_path() / p;
...@@ -91,7 +89,24 @@ fs::absolute(const path& p, error_code& ec) ...@@ -91,7 +89,24 @@ fs::absolute(const path& p, error_code& ec)
return ret; return ret;
} }
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
ec = std::make_error_code(errc::not_supported); const wstring& s = p.native();
uint32_t len = 1024;
wstring buf;
do
{
buf.resize(len);
len = GetFullPathNameW(s.c_str(), len, buf.data(), nullptr);
}
while (len > buf.size());
if (len == 0)
ec.assign((int)GetLastError(), std::system_category());
else
{
ec.clear();
buf.resize(len);
ret = std::move(buf);
}
#else #else
ec.clear(); ec.clear();
ret = current_path(); ret = current_path();
...@@ -125,7 +140,7 @@ namespace ...@@ -125,7 +140,7 @@ namespace
void operator()(void* p) const { ::free(p); } 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 fs::path
...@@ -140,7 +155,8 @@ fs::canonical(const path& p, error_code& ec) ...@@ -140,7 +155,8 @@ fs::canonical(const path& p, error_code& ec)
char_ptr buf{ nullptr }; char_ptr buf{ nullptr };
# if _XOPEN_VERSION < 700 # if _XOPEN_VERSION < 700
// Not safe to call realpath(path, NULL) // 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 # endif
if (char* rp = ::realpath(pa.c_str(), buf.get())) if (char* rp = ::realpath(pa.c_str(), buf.get()))
{ {
...@@ -261,7 +277,7 @@ namespace std::filesystem ...@@ -261,7 +277,7 @@ namespace std::filesystem
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #ifdef _GLIBCXX_HAVE_SYS_STAT_H
#ifdef NEED_DO_COPY_FILE #ifdef NEED_DO_COPY_FILE
bool 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, copy_options_existing_file options,
stat_type* from_st, stat_type* to_st, stat_type* from_st, stat_type* to_st,
std::error_code& ec) noexcept std::error_code& ec) noexcept
...@@ -271,7 +287,7 @@ fs::do_copy_file(const char* from, const char* to, ...@@ -271,7 +287,7 @@ fs::do_copy_file(const char* from, const char* to,
if (to_st == nullptr) if (to_st == nullptr)
{ {
if (::stat(to, &st1)) if (posix::stat(to, &st1))
{ {
const int err = errno; const int err = errno;
if (!is_not_found_errno(err)) if (!is_not_found_errno(err))
...@@ -293,7 +309,7 @@ fs::do_copy_file(const char* from, const char* to, ...@@ -293,7 +309,7 @@ fs::do_copy_file(const char* from, const char* to,
if (from_st == nullptr) if (from_st == nullptr)
{ {
if (::stat(from, &st2)) if (posix::stat(from, &st2))
{ {
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
return false; return false;
...@@ -351,12 +367,12 @@ fs::do_copy_file(const char* from, const char* to, ...@@ -351,12 +367,12 @@ fs::do_copy_file(const char* from, const char* to,
} }
struct CloseFD { struct CloseFD {
~CloseFD() { if (fd != -1) ::close(fd); } ~CloseFD() { if (fd != -1) posix::close(fd); }
bool close() { return ::close(std::exchange(fd, -1)) == 0; } bool close() { return posix::close(std::exchange(fd, -1)) == 0; }
int fd; int fd;
}; };
CloseFD in = { ::open(from, O_RDONLY) }; CloseFD in = { posix::open(from, O_RDONLY) };
if (in.fd == -1) if (in.fd == -1)
{ {
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
...@@ -367,7 +383,7 @@ fs::do_copy_file(const char* from, const char* to, ...@@ -367,7 +383,7 @@ fs::do_copy_file(const char* from, const char* to,
oflag |= O_TRUNC; oflag |= O_TRUNC;
else else
oflag |= O_EXCL; oflag |= O_EXCL;
CloseFD out = { ::open(to, oflag, S_IWUSR) }; CloseFD out = { posix::open(to, oflag, S_IWUSR) };
if (out.fd == -1) if (out.fd == -1)
{ {
if (errno == EEXIST && options.skip) if (errno == EEXIST && options.skip)
...@@ -377,12 +393,12 @@ fs::do_copy_file(const char* from, const char* to, ...@@ -377,12 +393,12 @@ fs::do_copy_file(const char* from, const char* to,
return false; return false;
} }
#ifdef _GLIBCXX_USE_FCHMOD #if defined _GLIBCXX_USE_FCHMOD && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
if (::fchmod(out.fd, from_st->st_mode)) 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)) if (::fchmodat(AT_FDCWD, to, from_st->st_mode, 0))
#else #else
if (::chmod(to, from_st->st_mode)) if (posix::chmod(to, from_st->st_mode))
#endif #endif
{ {
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
...@@ -390,7 +406,7 @@ fs::do_copy_file(const char* from, const char* to, ...@@ -390,7 +406,7 @@ fs::do_copy_file(const char* from, const char* to,
} }
size_t count = from_st->st_size; 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; off_t offset = 0;
ssize_t n = ::sendfile(out.fd, in.fd, &offset, count); ssize_t n = ::sendfile(out.fd, in.fd, &offset, count);
if (n < 0 && errno != ENOSYS && errno != EINVAL) if (n < 0 && errno != ENOSYS && errno != EINVAL)
...@@ -469,15 +485,15 @@ fs::copy(const path& from, const path& to, copy_options options, ...@@ -469,15 +485,15 @@ fs::copy(const path& from, const path& to, copy_options options,
// _GLIBCXX_RESOLVE_LIB_DEFECTS // _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2681. filesystem::copy() cannot copy symlinks // 2681. filesystem::copy() cannot copy symlinks
if (use_lstat || copy_symlinks if (use_lstat || copy_symlinks
? ::lstat(from.c_str(), &from_st) ? posix::lstat(from.c_str(), &from_st)
: ::stat(from.c_str(), &from_st)) : posix::stat(from.c_str(), &from_st))
{ {
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
return; return;
} }
if (use_lstat if (use_lstat
? ::lstat(to.c_str(), &to_st) ? posix::lstat(to.c_str(), &to_st)
: ::stat(to.c_str(), &to_st)) : posix::stat(to.c_str(), &to_st))
{ {
if (!is_not_found_errno(errno)) if (!is_not_found_errno(errno))
{ {
...@@ -671,8 +687,9 @@ namespace ...@@ -671,8 +687,9 @@ namespace
{ {
bool created = false; bool created = false;
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #ifdef _GLIBCXX_HAVE_SYS_STAT_H
::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm); posix::mode_t mode
if (::mkdir(p.c_str(), mode)) = static_cast<std::underlying_type_t<fs::perms>>(perm);
if (posix::mkdir(p.c_str(), mode))
{ {
const int err = errno; const int err = errno;
if (err != EEXIST || !is_directory(p, ec)) if (err != EEXIST || !is_directory(p, ec))
...@@ -725,7 +742,7 @@ fs::create_directory(const path& p, const path& attributes, ...@@ -725,7 +742,7 @@ fs::create_directory(const path& p, const path& attributes,
{ {
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #ifdef _GLIBCXX_HAVE_SYS_STAT_H
stat_type st; stat_type st;
if (::stat(attributes.c_str(), &st)) if (posix::stat(attributes.c_str(), &st))
{ {
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
return false; return false;
...@@ -767,18 +784,23 @@ fs::create_hard_link(const path& to, const path& new_hard_link) ...@@ -767,18 +784,23 @@ fs::create_hard_link(const path& to, const path& new_hard_link)
create_hard_link(to, new_hard_link, ec); create_hard_link(to, new_hard_link, ec);
if (ec) if (ec)
_GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link", _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
to, new_hard_link, ec)); to, new_hard_link, ec));
} }
void void
fs::create_hard_link(const path& to, const path& new_hard_link, fs::create_hard_link(const path& to, const path& new_hard_link,
error_code& ec) noexcept error_code& ec) noexcept
{ {
#ifdef _GLIBCXX_HAVE_UNISTD_H #ifdef _GLIBCXX_HAVE_LINK
if (::link(to.c_str(), new_hard_link.c_str())) if (::link(to.c_str(), new_hard_link.c_str()))
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
else else
ec.clear(); 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 #else
ec = std::make_error_code(std::errc::not_supported); ec = std::make_error_code(std::errc::not_supported);
#endif #endif
...@@ -798,7 +820,7 @@ void ...@@ -798,7 +820,7 @@ void
fs::create_symlink(const path& to, const path& new_symlink, fs::create_symlink(const path& to, const path& new_symlink,
error_code& ec) noexcept error_code& ec) noexcept
{ {
#ifdef _GLIBCXX_HAVE_UNISTD_H #ifdef _GLIBCXX_HAVE_SYMLINK
if (::symlink(to.c_str(), new_symlink.c_str())) if (::symlink(to.c_str(), new_symlink.c_str()))
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
else else
...@@ -824,8 +846,8 @@ fs::current_path(error_code& ec) ...@@ -824,8 +846,8 @@ fs::current_path(error_code& ec)
{ {
path p; path p;
#ifdef _GLIBCXX_HAVE_UNISTD_H #ifdef _GLIBCXX_HAVE_UNISTD_H
#ifdef __GLIBC__ #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)}) if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
{ {
p.assign(cwd.get()); p.assign(cwd.get());
ec.clear(); ec.clear();
...@@ -833,6 +855,7 @@ fs::current_path(error_code& ec) ...@@ -833,6 +855,7 @@ fs::current_path(error_code& ec)
else else
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
#else #else
#ifdef _PC_PATH_MAX
long path_max = pathconf(".", _PC_PATH_MAX); long path_max = pathconf(".", _PC_PATH_MAX);
size_t size; size_t size;
if (path_max == -1) if (path_max == -1)
...@@ -841,9 +864,15 @@ fs::current_path(error_code& ec) ...@@ -841,9 +864,15 @@ fs::current_path(error_code& ec)
size = 10240; size = 10240;
else else
size = path_max; 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) 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 (buf)
{ {
if (getcwd(buf.get(), size)) if (getcwd(buf.get(), size))
...@@ -883,7 +912,7 @@ void ...@@ -883,7 +912,7 @@ void
fs::current_path(const path& p, error_code& ec) noexcept fs::current_path(const path& p, error_code& ec) noexcept
{ {
#ifdef _GLIBCXX_HAVE_UNISTD_H #ifdef _GLIBCXX_HAVE_UNISTD_H
if (::chdir(p.c_str())) if (posix::chdir(p.c_str()))
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
else else
ec.clear(); ec.clear();
...@@ -910,14 +939,14 @@ fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept ...@@ -910,14 +939,14 @@ fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
int err = 0; int err = 0;
file_status s1, s2; file_status s1, s2;
stat_type st1, st2; stat_type st1, st2;
if (::stat(p1.c_str(), &st1) == 0) if (posix::stat(p1.c_str(), &st1) == 0)
s1 = make_file_status(st1); s1 = make_file_status(st1);
else if (is_not_found_errno(errno)) else if (is_not_found_errno(errno))
s1.type(file_type::not_found); s1.type(file_type::not_found);
else else
err = errno; err = errno;
if (::stat(p2.c_str(), &st2) == 0) if (posix::stat(p2.c_str(), &st2) == 0)
s2 = make_file_status(st2); s2 = make_file_status(st2);
else if (is_not_found_errno(errno)) else if (is_not_found_errno(errno))
s2.type(file_type::not_found); s2.type(file_type::not_found);
...@@ -966,8 +995,8 @@ namespace ...@@ -966,8 +995,8 @@ namespace
do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt) do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
{ {
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #ifdef _GLIBCXX_HAVE_SYS_STAT_H
fs::stat_type st; posix::stat_type st;
if (::stat(p.c_str(), &st)) if (posix::stat(p.c_str(), &st))
{ {
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
return deflt; return deflt;
...@@ -1017,7 +1046,7 @@ fs::hard_link_count(const path& p) ...@@ -1017,7 +1046,7 @@ fs::hard_link_count(const path& p)
std::uintmax_t std::uintmax_t
fs::hard_link_count(const path& p, error_code& ec) noexcept 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)); static_cast<uintmax_t>(-1));
} }
...@@ -1093,11 +1122,11 @@ fs::last_write_time(const path& p __attribute__((__unused__)), ...@@ -1093,11 +1122,11 @@ fs::last_write_time(const path& p __attribute__((__unused__)),
else else
ec.clear(); ec.clear();
#elif _GLIBCXX_HAVE_UTIME_H #elif _GLIBCXX_HAVE_UTIME_H
::utimbuf times; posix::utimbuf times;
times.modtime = s.count(); times.modtime = s.count();
times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; }, times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
times.modtime); times.modtime);
if (::utime(p.c_str(), &times)) if (posix::utime(p.c_str(), &times))
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
else else
ec.clear(); ec.clear();
...@@ -1152,7 +1181,7 @@ fs::permissions(const path& p, perms prms, perm_options opts, ...@@ -1152,7 +1181,7 @@ fs::permissions(const path& p, perms prms, perm_options opts,
#else #else
if (nofollow && is_symlink(st)) if (nofollow && is_symlink(st))
ec = std::make_error_code(std::errc::operation_not_supported); 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; err = errno;
#endif #endif
...@@ -1192,10 +1221,10 @@ fs::read_symlink(const path& p) ...@@ -1192,10 +1221,10 @@ fs::read_symlink(const path& p)
return tgt; 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; path result;
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
stat_type st; stat_type st;
if (::lstat(p.c_str(), &st)) if (::lstat(p.c_str(), &st))
{ {
...@@ -1268,6 +1297,19 @@ fs::remove(const path& p) ...@@ -1268,6 +1297,19 @@ fs::remove(const path& p)
bool bool
fs::remove(const path& p, error_code& ec) noexcept 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) if (::remove(p.c_str()) == 0)
{ {
ec.clear(); ec.clear();
...@@ -1277,6 +1319,7 @@ fs::remove(const path& p, error_code& ec) noexcept ...@@ -1277,6 +1319,7 @@ fs::remove(const path& p, error_code& ec) noexcept
ec.clear(); ec.clear();
else else
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
#endif
return false; return false;
} }
...@@ -1330,7 +1373,7 @@ fs::rename(const path& from, const path& to) ...@@ -1330,7 +1373,7 @@ fs::rename(const path& from, const path& to)
void void
fs::rename(const path& from, const path& to, error_code& ec) noexcept 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()); ec.assign(errno, std::generic_category());
else else
ec.clear(); ec.clear();
...@@ -1351,7 +1394,7 @@ fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept ...@@ -1351,7 +1394,7 @@ fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
#ifdef _GLIBCXX_HAVE_UNISTD_H #ifdef _GLIBCXX_HAVE_UNISTD_H
if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max())) if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
ec.assign(EINVAL, std::generic_category()); 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()); ec.assign(errno, std::generic_category());
else else
ec.clear(); ec.clear();
...@@ -1371,31 +1414,67 @@ fs::space(const path& p) ...@@ -1371,31 +1414,67 @@ fs::space(const path& p)
return s; return s;
} }
fs::space_info #ifdef NEED_DO_SPACE
fs::space(const path& p, error_code& ec) noexcept 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 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
struct ::statvfs f; struct ::statvfs f;
if (::statvfs(p.c_str(), &f)) if (::statvfs(pathname, &f))
ec.assign(errno, std::generic_category()); ec.assign(errno, std::generic_category());
else else
{ {
uintmax_t fragment_size = f.f_frsize; if (f.f_frsize != (unsigned long)-1)
info = space_info{ {
f.f_blocks * fragment_size, const uintmax_t fragment_size = f.f_frsize;
f.f_bfree * fragment_size, const fsblkcnt_t unknown = -1;
f.f_bavail * fragment_size 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(); 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 #else
ec = std::make_error_code(std::errc::not_supported); ec = std::make_error_code(std::errc::not_supported);
#endif #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; return info;
} }
...@@ -1405,7 +1484,7 @@ fs::status(const fs::path& p, error_code& ec) noexcept ...@@ -1405,7 +1484,7 @@ fs::status(const fs::path& p, error_code& ec) noexcept
{ {
file_status status; file_status status;
stat_type st; stat_type st;
if (::stat(p.c_str(), &st)) if (posix::stat(p.c_str(), &st))
{ {
int err = errno; int err = errno;
ec.assign(err, std::generic_category()); ec.assign(err, std::generic_category());
...@@ -1429,7 +1508,7 @@ fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept ...@@ -1429,7 +1508,7 @@ fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
{ {
file_status status; file_status status;
stat_type st; stat_type st;
if (::lstat(p.c_str(), &st)) if (posix::lstat(p.c_str(), &st))
{ {
int err = errno; int err = errno;
ec.assign(err, std::generic_category()); ec.assign(err, std::generic_category());
...@@ -1476,28 +1555,39 @@ fs::path fs::temp_directory_path() ...@@ -1476,28 +1555,39 @@ fs::path fs::temp_directory_path()
fs::path fs::temp_directory_path(error_code& ec) fs::path fs::temp_directory_path(error_code& ec)
{ {
path p;
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
ec = std::make_error_code(std::errc::not_supported); unsigned len = 1024;
return {}; // TODO 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 #else
const char* tmpdir = nullptr; const char* tmpdir = nullptr;
const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr }; const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e) for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
tmpdir = ::getenv(*e); tmpdir = ::getenv(*e);
path p = tmpdir ? tmpdir : "/tmp"; p = tmpdir ? tmpdir : "/tmp";
#endif
auto st = status(p, ec); auto st = status(p, ec);
if (!ec) if (ec)
p.clear();
else if (!is_directory(st))
{ {
if (is_directory(st)) p.clear();
{ ec = std::make_error_code(std::errc::not_a_directory);
ec.clear();
return p;
}
else
ec = std::make_error_code(std::errc::not_a_directory);
} }
return {}; return p;
#endif
} }
fs::path fs::path
......
...@@ -38,6 +38,66 @@ fs::filesystem_error::~filesystem_error() = default; ...@@ -38,6 +38,66 @@ fs::filesystem_error::~filesystem_error() = default;
constexpr path::value_type path::preferred_separator; 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&
path::remove_filename() path::remove_filename()
{ {
...@@ -74,6 +134,12 @@ path::replace_filename(const path& replacement) ...@@ -74,6 +134,12 @@ path::replace_filename(const path& replacement)
return *this; return *this;
} }
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
const fs::path::value_type dot = L'.';
#else
const fs::path::value_type dot = '.';
#endif
path& path&
path::replace_extension(const path& replacement) path::replace_extension(const path& replacement)
{ {
...@@ -94,8 +160,8 @@ path::replace_extension(const path& replacement) ...@@ -94,8 +160,8 @@ path::replace_extension(const path& replacement)
} }
// If replacement is not empty and does not begin with a dot character, // If replacement is not empty and does not begin with a dot character,
// a dot character is appended // a dot character is appended
if (!replacement.empty() && replacement.native()[0] != '.') if (!replacement.empty() && replacement.native()[0] != dot)
_M_pathname += '.'; _M_pathname += dot;
operator+=(replacement); operator+=(replacement);
return *this; return *this;
} }
...@@ -332,11 +398,7 @@ path::has_filename() const ...@@ -332,11 +398,7 @@ path::has_filename() const
namespace namespace
{ {
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS inline bool is_dot(fs::path::value_type c) { return c == dot; }
inline bool is_dot(wchar_t c) { return c == L'.'; }
#else
inline bool is_dot(char c) { return c == '.'; }
#endif
inline bool is_dot(const fs::path& path) inline bool is_dot(const fs::path& path)
{ {
...@@ -376,7 +438,7 @@ path::lexically_normal() const ...@@ -376,7 +438,7 @@ path::lexically_normal() const
{ {
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
// Replace each slash character in the root-name // Replace each slash character in the root-name
if (p.is_root_name()) if (p._M_type == _Type::_Root_name)
{ {
string_type s = p.native(); string_type s = p.native();
std::replace(s.begin(), s.end(), L'/', L'\\'); std::replace(s.begin(), s.end(), L'/', L'\\');
...@@ -485,7 +547,7 @@ path::lexically_proximate(const path& base) const ...@@ -485,7 +547,7 @@ path::lexically_proximate(const path& base) const
std::pair<const path::string_type*, std::size_t> std::pair<const path::string_type*, std::size_t>
path::_M_find_extension() const path::_M_find_extension() const
{ {
const std::string* s = nullptr; const string_type* s = nullptr;
if (_M_type == _Type::_Filename) if (_M_type == _Type::_Filename)
s = &_M_pathname; s = &_M_pathname;
...@@ -500,9 +562,9 @@ path::_M_find_extension() const ...@@ -500,9 +562,9 @@ path::_M_find_extension() const
{ {
if (auto sz = s->size()) if (auto sz = s->size())
{ {
if (sz <= 2 && (*s)[0] == '.') if (sz <= 2 && (*s)[0] == dot)
return { s, string_type::npos }; return { s, string_type::npos };
const auto pos = s->rfind('.'); const auto pos = s->rfind(dot);
return { s, pos ? pos : string_type::npos }; return { s, pos ? pos : string_type::npos };
} }
} }
...@@ -703,8 +765,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 ...@@ -703,8 +765,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
std::string filesystem_error::_M_gen_what() std::string filesystem_error::_M_gen_what()
{ {
return fs_err_concat(system_error::what(), _M_path1.native(), return fs_err_concat(system_error::what(), _M_path1.u8string(),
_M_path2.native()); _M_path2.u8string());
} }
_GLIBCXX_END_NAMESPACE_CXX11 _GLIBCXX_END_NAMESPACE_CXX11
......
...@@ -47,16 +47,17 @@ test01() ...@@ -47,16 +47,17 @@ test01()
// Test non-empty directory. // Test non-empty directory.
ec = bad_ec; ec = bad_ec;
create_directory_symlink(p, p / "l", ec); create_directory(p / "x", ec);
VERIFY( !ec ); VERIFY( !ec );
ec = bad_ec; ec = bad_ec;
iter = fs::directory_iterator(p, ec); iter = fs::directory_iterator(p, ec);
VERIFY( !ec ); VERIFY( !ec );
VERIFY( iter != fs::directory_iterator() ); VERIFY( iter != fs::directory_iterator() );
VERIFY( iter->path() == p/"l" ); VERIFY( iter->path() == p/"x" );
++iter; ++iter;
VERIFY( iter == end(iter) ); VERIFY( iter == end(iter) );
#if !(defined(__MINGW32__) || defined(__MINGW64__))
// Test inaccessible directory. // Test inaccessible directory.
ec = bad_ec; ec = bad_ec;
permissions(p, fs::perms::none, ec); permissions(p, fs::perms::none, ec);
...@@ -71,6 +72,7 @@ test01() ...@@ -71,6 +72,7 @@ test01()
iter = fs::directory_iterator(p, opts, ec); iter = fs::directory_iterator(p, opts, ec);
VERIFY( !ec ); VERIFY( !ec );
VERIFY( iter == end(iter) ); VERIFY( iter == end(iter) );
#endif
permissions(p, fs::perms::owner_all, ec); permissions(p, fs::perms::owner_all, ec);
remove_all(p, ec); remove_all(p, ec);
...@@ -84,7 +86,7 @@ test02() ...@@ -84,7 +86,7 @@ test02()
const auto p = __gnu_test::nonexistent_path(); const auto p = __gnu_test::nonexistent_path();
ec = bad_ec; ec = bad_ec;
create_directory(p, fs::current_path(), ec); create_directory(p, fs::current_path(), ec);
create_directory_symlink(p, p / "l", ec); create_directory(p / "x", ec);
VERIFY( !ec ); VERIFY( !ec );
// Test post-increment (libstdc++/71005) // Test post-increment (libstdc++/71005)
...@@ -95,7 +97,7 @@ test02() ...@@ -95,7 +97,7 @@ test02()
const auto entry1 = *iter; const auto entry1 = *iter;
const auto entry2 = *iter++; const auto entry2 = *iter++;
VERIFY( entry1 == entry2 ); VERIFY( entry1 == entry2 );
VERIFY( entry1.path() == p/"l" ); VERIFY( entry1.path() == p/"x" );
VERIFY( iter == end(iter) ); VERIFY( iter == end(iter) );
remove_all(p, ec); remove_all(p, ec);
...@@ -130,7 +132,7 @@ test05() ...@@ -130,7 +132,7 @@ test05()
{ {
auto p = __gnu_test::nonexistent_path(); auto p = __gnu_test::nonexistent_path();
create_directory(p); create_directory(p);
create_directory_symlink(p, p / "l"); create_directory(p / "x");
fs::directory_iterator it(p), endit; fs::directory_iterator it(p), endit;
VERIFY( begin(it) == it ); VERIFY( begin(it) == it );
static_assert( noexcept(begin(it)), "begin is noexcept" ); static_assert( noexcept(begin(it)), "begin is noexcept" );
......
...@@ -60,6 +60,7 @@ test01() ...@@ -60,6 +60,7 @@ test01()
++iter; ++iter;
VERIFY( iter == end(iter) ); VERIFY( iter == end(iter) );
#if ! (defined (__MINGW32__) || defined(__MINGW64__))
// Test inaccessible directory. // Test inaccessible directory.
ec = bad_ec; ec = bad_ec;
permissions(p, fs::perms::none, ec); permissions(p, fs::perms::none, ec);
...@@ -106,6 +107,7 @@ test01() ...@@ -106,6 +107,7 @@ test01()
iter.increment(ec); // should fail to recurse into p/d1/d2, so skip it iter.increment(ec); // should fail to recurse into p/d1/d2, so skip it
VERIFY( !ec ); VERIFY( !ec );
VERIFY( iter == end(iter) ); VERIFY( iter == end(iter) );
#endif
permissions(p/"d1/d2", fs::perms::owner_all, ec); permissions(p/"d1/d2", fs::perms::owner_all, ec);
remove_all(p, ec); remove_all(p, ec);
...@@ -171,7 +173,7 @@ test05() ...@@ -171,7 +173,7 @@ test05()
{ {
auto p = __gnu_test::nonexistent_path(); auto p = __gnu_test::nonexistent_path();
create_directory(p); create_directory(p);
create_directory_symlink(p, p / "l"); create_directory(p / "x");
fs::recursive_directory_iterator it(p), endit; fs::recursive_directory_iterator it(p), endit;
VERIFY( begin(it) == it ); VERIFY( begin(it) == it );
static_assert( noexcept(begin(it)), "begin is noexcept" ); static_assert( noexcept(begin(it)), "begin is noexcept" );
......
...@@ -41,6 +41,22 @@ test01() ...@@ -41,6 +41,22 @@ test01()
void void
test02() test02()
{ {
std::error_code ec = make_error_code(std::errc::invalid_argument);
path root = __gnu_test::root_path();
VERIFY( absolute(root) == root );
VERIFY( absolute(root, ec) == root && !ec );
VERIFY( absolute(path{}, ec).empty() && ec );
#if defined(__MINGW32__) || defined(__MINGW64__)
path p1("/");
VERIFY( absolute(p1) != p1 );
path p2("/foo");
VERIFY( absolute(p2) != p2 );
path p3("foo");
VERIFY( absolute(p3) != p3 );
path p4("C:\\");
VERIFY( absolute(p4) == p4 );
#else
path p1("/"); path p1("/");
VERIFY( absolute(p1) == p1 ); VERIFY( absolute(p1) == p1 );
path p2("/foo"); path p2("/foo");
...@@ -48,6 +64,7 @@ test02() ...@@ -48,6 +64,7 @@ test02()
path p3("foo"); path p3("foo");
VERIFY( absolute(p3) != p3 ); VERIFY( absolute(p3) != p3 );
VERIFY( absolute(p3) == (std::filesystem::current_path()/p3) ); VERIFY( absolute(p3) == (std::filesystem::current_path()/p3) );
#endif
} }
int int
......
...@@ -41,7 +41,7 @@ test01() ...@@ -41,7 +41,7 @@ test01()
VERIFY( !ec ); VERIFY( !ec );
ec = bad_ec; ec = bad_ec;
p2 = canonical( fs::current_path() / "." / (p.native() + "////././."), ec ); p2 = canonical( fs::current_path() / "." / (p.string() + "////././."), ec );
compare_paths( p2, fs::current_path()/p ); compare_paths( p2, fs::current_path()/p );
VERIFY( !ec ); VERIFY( !ec );
......
...@@ -116,7 +116,7 @@ test03() ...@@ -116,7 +116,7 @@ test03()
auto to = __gnu_test::nonexistent_path(); auto to = __gnu_test::nonexistent_path();
// test empty file // test empty file
std::ofstream{from.native()}; std::ofstream{from};
VERIFY( fs::exists(from) ); VERIFY( fs::exists(from) );
VERIFY( fs::file_size(from) == 0 ); VERIFY( fs::file_size(from) == 0 );
fs::copy(from, to); fs::copy(from, to);
...@@ -125,7 +125,7 @@ test03() ...@@ -125,7 +125,7 @@ test03()
remove(to); remove(to);
VERIFY( !fs::exists(to) ); VERIFY( !fs::exists(to) );
std::ofstream{from.native()} << "Hello, filesystem!"; std::ofstream{from} << "Hello, filesystem!";
VERIFY( fs::file_size(from) != 0 ); VERIFY( fs::file_size(from) != 0 );
fs::copy(from, to); fs::copy(from, to);
VERIFY( fs::exists(to) ); VERIFY( fs::exists(to) );
......
...@@ -42,7 +42,7 @@ test01() ...@@ -42,7 +42,7 @@ test01()
VERIFY( !exists(to) ); VERIFY( !exists(to) );
// test empty file // test empty file
std::ofstream{from.native()}; std::ofstream{from};
VERIFY( exists(from) ); VERIFY( exists(from) );
VERIFY( file_size(from) == 0 ); VERIFY( file_size(from) == 0 );
...@@ -58,7 +58,7 @@ test01() ...@@ -58,7 +58,7 @@ test01()
VERIFY( exists(to) ); VERIFY( exists(to) );
VERIFY( file_size(to) == 0 ); VERIFY( file_size(to) == 0 );
std::ofstream{from.native()} << "Hello, filesystem!"; std::ofstream{from} << "Hello, filesystem!";
VERIFY( file_size(from) != 0 ); VERIFY( file_size(from) != 0 );
remove(to); remove(to);
VERIFY( !exists(to) ); VERIFY( !exists(to) );
......
...@@ -29,19 +29,20 @@ void ...@@ -29,19 +29,20 @@ void
test01() test01()
{ {
const std::error_code bad_ec = make_error_code(std::errc::invalid_argument); const std::error_code bad_ec = make_error_code(std::errc::invalid_argument);
const path root = __gnu_test::root_path();
VERIFY( exists(path{"/"}) ); VERIFY( exists(root) );
VERIFY( exists(path{"/."}) ); VERIFY( exists(root/".") );
VERIFY( exists(path{"."}) ); VERIFY( exists(path{"."}) );
VERIFY( exists(path{".."}) ); VERIFY( exists(path{".."}) );
VERIFY( exists(std::filesystem::current_path()) ); VERIFY( exists(std::filesystem::current_path()) );
std::error_code ec; std::error_code ec;
ec = bad_ec; ec = bad_ec;
VERIFY( exists(path{"/"}, ec) ); VERIFY( exists(root, ec) );
VERIFY( !ec ); VERIFY( !ec );
ec = bad_ec; ec = bad_ec;
VERIFY( exists(path{"/."}, ec) ); VERIFY( exists(root/".", ec) );
VERIFY( !ec ); VERIFY( !ec );
ec = bad_ec; ec = bad_ec;
VERIFY( exists(path{"."}, ec) ); VERIFY( exists(path{"."}, ec) );
......
...@@ -82,7 +82,7 @@ test02() ...@@ -82,7 +82,7 @@ test02()
empty = is_empty(f.path); empty = is_empty(f.path);
VERIFY( empty ); VERIFY( empty );
std::ofstream{f.path.native()} << "data"; std::ofstream{f.path} << "data";
ec = bad_ec; ec = bad_ec;
empty = is_empty(p, ec); empty = is_empty(p, ec);
VERIFY( !ec ); VERIFY( !ec );
......
...@@ -81,7 +81,7 @@ test01() ...@@ -81,7 +81,7 @@ test01()
::utimbuf times; ::utimbuf times;
times.modtime = std::numeric_limits<std::time_t>::max() - 1; times.modtime = std::numeric_limits<std::time_t>::max() - 1;
times.actime = std::numeric_limits<std::time_t>::max() - 1; times.actime = std::numeric_limits<std::time_t>::max() - 1;
VERIFY( !::utime(p.c_str(), &times) ); VERIFY( !::utime(p.string().c_str(), &times) );
#else #else
return; return;
#endif #endif
......
...@@ -25,25 +25,35 @@ ...@@ -25,25 +25,35 @@
#include <testsuite_fs.h> #include <testsuite_fs.h>
#include <testsuite_hooks.h> #include <testsuite_hooks.h>
bool check(std::filesystem::space_info const& s)
{
const std::uintmax_t err = -1;
return s.capacity != err || s.free != err || s.available != err;
}
void void
test01() test01()
{ {
std::filesystem::space_info s = std::filesystem::space("/"); const std::filesystem::path root = __gnu_test::root_path();
std::filesystem::space_info s = std::filesystem::space(root);
std::error_code ec = make_error_code(std::errc::invalid_argument); std::error_code ec = make_error_code(std::errc::invalid_argument);
s = std::filesystem::space("/", ec); s = std::filesystem::space(root, ec);
VERIFY( !ec ); VERIFY( !ec );
VERIFY( check(s) );
VERIFY( s.capacity >= s.free );
s = std::filesystem::space(__gnu_test::nonexistent_path(), ec); s = std::filesystem::space(__gnu_test::nonexistent_path()/".", ec);
VERIFY( ec ); if (ec)
VERIFY( s.capacity == static_cast<uintmax_t>(-1) ); VERIFY( ! check(s) );
VERIFY( s.free == static_cast<uintmax_t>(-1) ); else
VERIFY( s.available == static_cast<uintmax_t>(-1) ); VERIFY( check(s) );
} }
void void
test02() test02()
{ {
std::filesystem::space_info s = std::filesystem::space("."); std::filesystem::space_info s = std::filesystem::space(".");
VERIFY( check(s) );
VERIFY( s.capacity >= s.free ); VERIFY( s.capacity >= s.free );
} }
......
...@@ -27,10 +27,28 @@ ...@@ -27,10 +27,28 @@
void void
clean_env() clean_env()
{ {
#if defined(__MINGW32__) || defined(__MINGW64__)
::_putenv("TMP=");
::_putenv("TEMP=");
#else
::unsetenv("TMPDIR"); ::unsetenv("TMPDIR");
::unsetenv("TMP"); ::unsetenv("TMP");
::unsetenv("TEMPDIR"); ::unsetenv("TEMPDIR");
::unsetenv("TEMP"); ::unsetenv("TEMP");
#endif
}
bool
set_env(const char* name, std::string value)
{
#if defined(__MINGW32__) || defined(__MINGW64__)
std::string s = name;
s += '=';
s += value;
return !::_putenv(s.c_str());
#else
return !::setenv(name, value.c_str(), 1);
#endif
} }
namespace fs = std::filesystem; namespace fs = std::filesystem;
...@@ -57,7 +75,7 @@ test02() ...@@ -57,7 +75,7 @@ test02()
{ {
clean_env(); clean_env();
if (::setenv("TMPDIR", __gnu_test::nonexistent_path().string().c_str(), 1)) if (!set_env("TMPDIR", __gnu_test::nonexistent_path().string()))
return; // just give up return; // just give up
std::error_code ec; std::error_code ec;
...@@ -80,7 +98,7 @@ test03() ...@@ -80,7 +98,7 @@ test03()
auto p = __gnu_test::nonexistent_path(); auto p = __gnu_test::nonexistent_path();
create_directories(p/"tmp"); create_directories(p/"tmp");
permissions(p, fs::perms::none); permissions(p, fs::perms::none);
setenv("TMPDIR", (p/"tmp").c_str(), 1); set_env("TMPDIR", (p/"tmp").string());
std::error_code ec; std::error_code ec;
auto r = fs::temp_directory_path(ec); // libstdc++/PR71337 auto r = fs::temp_directory_path(ec); // libstdc++/PR71337
VERIFY( ec == std::make_error_code(std::errc::permission_denied) ); VERIFY( ec == std::make_error_code(std::errc::permission_denied) );
...@@ -102,7 +120,7 @@ void ...@@ -102,7 +120,7 @@ void
test04() test04()
{ {
__gnu_test::scoped_file f; __gnu_test::scoped_file f;
setenv("TMPDIR", f.path.c_str(), 1); set_env("TMPDIR", f.path.string());
std::error_code ec; std::error_code ec;
auto r = fs::temp_directory_path(ec); auto r = fs::temp_directory_path(ec);
VERIFY( ec == std::make_error_code(std::errc::not_a_directory) ); VERIFY( ec == std::make_error_code(std::errc::not_a_directory) );
......
...@@ -55,6 +55,10 @@ test01() ...@@ -55,6 +55,10 @@ test01()
compare_paths( append("dir/", "/file"), "/file" ); compare_paths( append("dir/", "/file"), "/file" );
compare_paths( append("dir/", "file"), "dir/file" ); compare_paths( append("dir/", "file"), "dir/file" );
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
compare_paths( append("c:/foo", "/bar"), "c:/bar" );
#endif
} }
void void
......
...@@ -30,7 +30,7 @@ void ...@@ -30,7 +30,7 @@ void
test01() test01()
{ {
// path(string_type&&, format) // path(string_type&&, format)
auto s = [&]() -> path::string_type { return "foo/bar"; }; auto s = [&]() -> path::string_type { return path("foo/bar").native(); };
path p0(s()); path p0(s());
path p1(s(), path::auto_format); path p1(s(), path::auto_format);
VERIFY( p1 == p0 ); VERIFY( p1 == p0 );
...@@ -44,7 +44,7 @@ void ...@@ -44,7 +44,7 @@ void
test02() test02()
{ {
// path(const Source&, format) // path(const Source&, format)
path::string_type s = "foo/bar"; const path::string_type s = path("foo/bar").native();
path p0(s); path p0(s);
path p1(s, path::auto_format); path p1(s, path::auto_format);
VERIFY( p1 == p0 ); VERIFY( p1 == p0 );
...@@ -58,7 +58,7 @@ void ...@@ -58,7 +58,7 @@ void
test03() test03()
{ {
// path(const Source&, format) // path(const Source&, format)
std::string s = "foo/bar"; const std::string s = "foo/bar";
path p0(s); path p0(s);
path p1(s, path::auto_format); path p1(s, path::auto_format);
VERIFY( p1 == p0 ); VERIFY( p1 == p0 );
...@@ -73,7 +73,7 @@ test04() ...@@ -73,7 +73,7 @@ test04()
{ {
#ifdef _GLIBCXX_USE_WCHAR_T #ifdef _GLIBCXX_USE_WCHAR_T
// path(const Source&, format) // path(const Source&, format)
std::wstring s = L"foo/bar"; const std::wstring s = L"foo/bar";
path p0(s); path p0(s);
path p1(s, path::auto_format); path p1(s, path::auto_format);
VERIFY( p1 == p0 ); VERIFY( p1 == p0 );
......
...@@ -31,7 +31,11 @@ void ...@@ -31,7 +31,11 @@ void
test01() test01()
{ {
path p("/foo/bar", std::locale::classic()); path p("/foo/bar", std::locale::classic());
#if defined(__MINGW32__) || defined(__MINGW64__)
VERIFY( p.native() == L"/foo/bar" );
#else
VERIFY( p.native() == "/foo/bar" ); VERIFY( p.native() == "/foo/bar" );
#endif
} }
void void
......
...@@ -47,7 +47,12 @@ test02() ...@@ -47,7 +47,12 @@ test02()
{ {
path rootdir = p.root_directory(); path rootdir = p.root_directory();
VERIFY( !rootdir.has_relative_path() ); VERIFY( !rootdir.has_relative_path() );
VERIFY( rootdir.empty() || rootdir.native() == "/"); if (!rootdir.empty())
#if defined(__MINGW32__) || defined(__MINGW64__)
VERIFY( rootdir.string() == "/" || rootdir.string() == "\\" );
#else
VERIFY( rootdir.string() == "/" );
#endif
} }
} }
......
...@@ -35,7 +35,7 @@ test01() ...@@ -35,7 +35,7 @@ test01()
path p = "foo.bar.baz.tar"; path p = "foo.bar.baz.tar";
std::vector<std::string> v; std::vector<std::string> v;
for (; !p.extension().empty(); p = p.stem()) for (; !p.extension().empty(); p = p.stem())
v.push_back(p.extension().native()); v.push_back(p.extension().string());
VERIFY( v.at(0) == ".tar" ); VERIFY( v.at(0) == ".tar" );
VERIFY( v.at(1) == ".baz" ); VERIFY( v.at(1) == ".baz" );
VERIFY( v.at(2) == ".bar" ); VERIFY( v.at(2) == ".bar" );
......
...@@ -83,6 +83,24 @@ test01() ...@@ -83,6 +83,24 @@ test01()
v2 = { "/", "rootname", "dir", "filename" }; v2 = { "/", "rootname", "dir", "filename" };
#endif #endif
VERIFY( v == v2 ); VERIFY( v == v2 );
p = "c:relative/path";
v.assign(p.begin(), p.end());
#if defined(__MINGW32__) || defined(__MINGW64__)
v2 = { "c:", "relative", "path" };
#else
v2 = { "c:relative", "path" };
#endif
VERIFY( v == v2 );
p = "c:/absolute/path";
v.assign(p.begin(), p.end());
#if defined(__MINGW32__) || defined(__MINGW64__)
v2 = { "c:", "/", "absolute", "path" };
#else
v2 = { "c:", "absolute", "path" };
#endif
VERIFY( v == v2 );
} }
void void
......
...@@ -27,14 +27,15 @@ void ...@@ -27,14 +27,15 @@ void
test01() test01()
{ {
using namespace std::filesystem; using namespace std::filesystem;
const std::string s = "abc"; using string_type = std::basic_string<path::value_type>;
const string_type s{ 'a', 'b', 'c' };
path p(s); path p(s);
VERIFY( p.native() == s ); VERIFY( p.native() == s );
VERIFY( p.c_str() == s ); VERIFY( p.c_str() == s );
VERIFY( static_cast<std::string>(p) == s ); VERIFY( static_cast<string_type>(p) == s );
std::string s2 = p; // implicit conversion string_type s2 = p; // implicit conversion
VERIFY( s2 == p.native() ); VERIFY( s2 == p.native() );
} }
......
...@@ -29,11 +29,17 @@ using std::filesystem::path; ...@@ -29,11 +29,17 @@ using std::filesystem::path;
void void
test01() test01()
{ {
VERIFY( path("/").is_absolute() ); #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
VERIFY( path("/foo").is_absolute() ); const bool is_posix = false;
VERIFY( path("/foo/").is_absolute() ); #else
VERIFY( path("/foo/bar").is_absolute() ); const bool is_posix = true;
VERIFY( path("/foo/bar/").is_absolute() ); #endif
VERIFY( path("/").is_absolute() == is_posix );
VERIFY( path("/foo").is_absolute() == is_posix );
VERIFY( path("/foo/").is_absolute() == is_posix );
VERIFY( path("/foo/bar").is_absolute() == is_posix );
VERIFY( path("/foo/bar/").is_absolute() == is_posix );
VERIFY( ! path("foo").is_absolute() ); VERIFY( ! path("foo").is_absolute() );
VERIFY( ! path("foo/").is_absolute() ); VERIFY( ! path("foo/").is_absolute() );
VERIFY( ! path("foo/bar").is_absolute() ); VERIFY( ! path("foo/bar").is_absolute() );
...@@ -43,16 +49,11 @@ test01() ...@@ -43,16 +49,11 @@ test01()
VERIFY( ! path("c:foo/").is_absolute() ); VERIFY( ! path("c:foo/").is_absolute() );
VERIFY( ! path("c:foo/bar").is_absolute() ); VERIFY( ! path("c:foo/bar").is_absolute() );
VERIFY( ! path("c:foo/bar/").is_absolute() ); VERIFY( ! path("c:foo/bar/").is_absolute() );
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS VERIFY( path("c:/").is_absolute() == !is_posix );
const bool drive_letter_is_root_name = true; VERIFY( path("c:/foo").is_absolute() == !is_posix );
#else VERIFY( path("c:/foo/").is_absolute() == !is_posix );
const bool drive_letter_is_root_name = false; VERIFY( path("c:/foo/bar").is_absolute() == !is_posix );
#endif VERIFY( path("c:/foo/bar/").is_absolute() == !is_posix );
VERIFY( path("c:/").is_absolute() == drive_letter_is_root_name );
VERIFY( path("c:/foo").is_absolute() == drive_letter_is_root_name );
VERIFY( path("c:/foo/").is_absolute() == drive_letter_is_root_name );
VERIFY( path("c:/foo/bar").is_absolute() == drive_letter_is_root_name );
VERIFY( path("c:/foo/bar/").is_absolute() == drive_letter_is_root_name );
} }
int int
......
...@@ -44,15 +44,16 @@ test01() ...@@ -44,15 +44,16 @@ test01()
VERIFY( iter == end(iter) ); VERIFY( iter == end(iter) );
// Test non-empty directory. // Test non-empty directory.
create_directory_symlink(p, p / "l", ec); create_directory(p / "x", ec);
VERIFY( !ec ); VERIFY( !ec );
iter = fs::directory_iterator(p, ec); iter = fs::directory_iterator(p, ec);
VERIFY( !ec ); VERIFY( !ec );
VERIFY( iter != fs::directory_iterator() ); VERIFY( iter != fs::directory_iterator() );
VERIFY( iter->path() == p/"l" ); VERIFY( iter->path() == p/"x" );
++iter; ++iter;
VERIFY( iter == end(iter) ); VERIFY( iter == end(iter) );
#if !(defined(__MINGW32__) || defined(__MINGW64__))
// Test inaccessible directory. // Test inaccessible directory.
permissions(p, fs::perms::none, ec); permissions(p, fs::perms::none, ec);
VERIFY( !ec ); VERIFY( !ec );
...@@ -65,6 +66,7 @@ test01() ...@@ -65,6 +66,7 @@ test01()
iter = fs::directory_iterator(p, opts, ec); iter = fs::directory_iterator(p, opts, ec);
VERIFY( !ec ); VERIFY( !ec );
VERIFY( iter == end(iter) ); VERIFY( iter == end(iter) );
#endif
permissions(p, fs::perms::owner_all, ec); permissions(p, fs::perms::owner_all, ec);
remove_all(p, ec); remove_all(p, ec);
...@@ -76,7 +78,7 @@ test02() ...@@ -76,7 +78,7 @@ test02()
std::error_code ec; std::error_code ec;
const auto p = __gnu_test::nonexistent_path(); const auto p = __gnu_test::nonexistent_path();
create_directory(p, fs::current_path(), ec); create_directory(p, fs::current_path(), ec);
create_directory_symlink(p, p / "l", ec); create_directory(p / "x", ec);
VERIFY( !ec ); VERIFY( !ec );
// Test post-increment (libstdc++/71005) // Test post-increment (libstdc++/71005)
...@@ -86,7 +88,7 @@ test02() ...@@ -86,7 +88,7 @@ test02()
const auto entry1 = *iter; const auto entry1 = *iter;
const auto entry2 = *iter++; const auto entry2 = *iter++;
VERIFY( entry1 == entry2 ); VERIFY( entry1 == entry2 );
VERIFY( entry1.path() == p/"l" ); VERIFY( entry1.path() == p/"x" );
VERIFY( iter == end(iter) ); VERIFY( iter == end(iter) );
remove_all(p, ec); remove_all(p, ec);
...@@ -121,7 +123,7 @@ test05() ...@@ -121,7 +123,7 @@ test05()
{ {
auto p = __gnu_test::nonexistent_path(); auto p = __gnu_test::nonexistent_path();
create_directory(p); create_directory(p);
create_directory_symlink(p, p / "l"); create_directory(p / "x");
fs::directory_iterator it(p), endit; fs::directory_iterator it(p), endit;
VERIFY( begin(it) == it ); VERIFY( begin(it) == it );
static_assert( noexcept(begin(it)), "begin is noexcept" ); static_assert( noexcept(begin(it)), "begin is noexcept" );
......
...@@ -31,12 +31,29 @@ void ...@@ -31,12 +31,29 @@ void
test01() test01()
{ {
for (const path& p : __gnu_test::test_paths) for (const path& p : __gnu_test::test_paths)
{
#if defined(__MINGW32__) || defined(__MINGW64__)
if (p.empty())
continue;
#endif
VERIFY( absolute(p).is_absolute() ); VERIFY( absolute(p).is_absolute() );
}
} }
void void
test02() test02()
{ {
#if defined(__MINGW32__) || defined(__MINGW64__)
path p1("/");
VERIFY( absolute(p1) != p1 );
path p2("/foo");
VERIFY( absolute(p2) != p2 );
path p3("foo");
VERIFY( absolute(p3) != p3 );
path p4("C:\\");
VERIFY( absolute(p3, p4) == "C:\\foo" );
VERIFY( absolute(p4) == p4 );
#else
path p1("/"); path p1("/");
VERIFY( absolute(p1) == p1 ); VERIFY( absolute(p1) == p1 );
VERIFY( absolute(p1, "/bar") == p1 ); VERIFY( absolute(p1, "/bar") == p1 );
...@@ -46,6 +63,7 @@ test02() ...@@ -46,6 +63,7 @@ test02()
path p3("foo"); path p3("foo");
VERIFY( absolute(p3) != p3 ); VERIFY( absolute(p3) != p3 );
VERIFY( absolute(p3, "/bar") == "/bar/foo" ); VERIFY( absolute(p3, "/bar") == "/bar/foo" );
#endif
} }
int int
......
...@@ -114,7 +114,7 @@ test03() ...@@ -114,7 +114,7 @@ test03()
auto to = __gnu_test::nonexistent_path(); auto to = __gnu_test::nonexistent_path();
// test empty file // test empty file
std::ofstream{from.native()}; std::ofstream{from.c_str()};
VERIFY( fs::exists(from) ); VERIFY( fs::exists(from) );
VERIFY( fs::file_size(from) == 0 ); VERIFY( fs::file_size(from) == 0 );
fs::copy(from, to); fs::copy(from, to);
...@@ -123,7 +123,7 @@ test03() ...@@ -123,7 +123,7 @@ test03()
remove(to); remove(to);
VERIFY( !fs::exists(to) ); VERIFY( !fs::exists(to) );
std::ofstream{from.native()} << "Hello, filesystem!"; std::ofstream{from.c_str()} << "Hello, filesystem!";
VERIFY( fs::file_size(from) != 0 ); VERIFY( fs::file_size(from) != 0 );
fs::copy(from, to); fs::copy(from, to);
VERIFY( fs::exists(to) ); VERIFY( fs::exists(to) );
...@@ -150,9 +150,9 @@ test04() ...@@ -150,9 +150,9 @@ test04()
} }
__gnu_test::scoped_file f1(from/"a/f1"); __gnu_test::scoped_file f1(from/"a/f1");
std::ofstream{f1.path} << "file one"; std::ofstream{f1.path.c_str()} << "file one";
__gnu_test::scoped_file f2(from/"a/b/f2"); __gnu_test::scoped_file f2(from/"a/b/f2");
std::ofstream{f2.path} << "file two"; std::ofstream{f2.path.c_str()} << "file two";
copy(from, to, ec); copy(from, to, ec);
VERIFY( !ec ); VERIFY( !ec );
......
...@@ -42,7 +42,7 @@ test01() ...@@ -42,7 +42,7 @@ test01()
VERIFY( !exists(to) ); VERIFY( !exists(to) );
// test empty file // test empty file
std::ofstream{from.native()}; std::ofstream{from.c_str()};
VERIFY( exists(from) ); VERIFY( exists(from) );
VERIFY( file_size(from) == 0 ); VERIFY( file_size(from) == 0 );
...@@ -58,7 +58,7 @@ test01() ...@@ -58,7 +58,7 @@ test01()
VERIFY( exists(to) ); VERIFY( exists(to) );
VERIFY( file_size(to) == 0 ); VERIFY( file_size(to) == 0 );
std::ofstream{from.native()} << "Hello, filesystem!"; std::ofstream{from.c_str()} << "Hello, filesystem!";
VERIFY( file_size(from) != 0 ); VERIFY( file_size(from) != 0 );
remove(to); remove(to);
VERIFY( !exists(to) ); VERIFY( !exists(to) );
......
...@@ -28,16 +28,18 @@ using std::experimental::filesystem::path; ...@@ -28,16 +28,18 @@ using std::experimental::filesystem::path;
void void
test01() test01()
{ {
VERIFY( exists(path{"/"}) ); const path root = __gnu_test::root_path();
VERIFY( exists(path{"/."}) );
VERIFY( exists(root) );
VERIFY( exists(root/".") );
VERIFY( exists(path{"."}) ); VERIFY( exists(path{"."}) );
VERIFY( exists(path{".."}) ); VERIFY( exists(path{".."}) );
VERIFY( exists(std::experimental::filesystem::current_path()) ); VERIFY( exists(std::experimental::filesystem::current_path()) );
std::error_code ec = std::make_error_code(std::errc::invalid_argument); std::error_code ec = std::make_error_code(std::errc::invalid_argument);
VERIFY( exists(path{"/"}, ec) ); VERIFY( exists(root, ec) );
VERIFY( !ec ); VERIFY( !ec );
VERIFY( exists(path{"/."}, ec) ); VERIFY( exists(root/".", ec) );
VERIFY( !ec ); VERIFY( !ec );
VERIFY( exists(path{"."}, ec) ); VERIFY( exists(path{"."}, ec) );
VERIFY( !ec ); VERIFY( !ec );
......
...@@ -82,7 +82,7 @@ test02() ...@@ -82,7 +82,7 @@ test02()
empty = is_empty(f.path); empty = is_empty(f.path);
VERIFY( empty ); VERIFY( empty );
std::ofstream{f.path.native()} << "data"; std::ofstream{f.path.c_str()} << "data";
ec = bad_ec; ec = bad_ec;
empty = is_empty(p, ec); empty = is_empty(p, ec);
VERIFY( !ec ); VERIFY( !ec );
......
...@@ -81,7 +81,7 @@ test01() ...@@ -81,7 +81,7 @@ test01()
::utimbuf times; ::utimbuf times;
times.modtime = std::numeric_limits<std::time_t>::max() - 1; times.modtime = std::numeric_limits<std::time_t>::max() - 1;
times.actime = std::numeric_limits<std::time_t>::max() - 1; times.actime = std::numeric_limits<std::time_t>::max() - 1;
VERIFY( !::utime(p.c_str(), &times) ); VERIFY( !::utime(p.string().c_str(), &times) );
#else #else
return; return;
#endif #endif
......
...@@ -30,9 +30,10 @@ namespace fs = std::experimental::filesystem; ...@@ -30,9 +30,10 @@ namespace fs = std::experimental::filesystem;
void void
test01() test01()
{ {
fs::space_info s = fs::space("/"); const fs::path root = __gnu_test::root_path();
fs::space_info s = fs::space(root);
std::error_code ec = make_error_code(std::errc::invalid_argument); std::error_code ec = make_error_code(std::errc::invalid_argument);
s = fs::space("/", ec); s = fs::space(root, ec);
VERIFY( !ec ); VERIFY( !ec );
s = fs::space(__gnu_test::nonexistent_path(), ec); s = fs::space(__gnu_test::nonexistent_path(), ec);
......
...@@ -27,10 +27,28 @@ ...@@ -27,10 +27,28 @@
void void
clean_env() clean_env()
{ {
#if defined(__MINGW32__) || defined(__MINGW64__)
::_putenv("TMP=");
::_putenv("TEMP=");
#else
::unsetenv("TMPDIR"); ::unsetenv("TMPDIR");
::unsetenv("TMP"); ::unsetenv("TMP");
::unsetenv("TEMPDIR"); ::unsetenv("TEMPDIR");
::unsetenv("TEMP"); ::unsetenv("TEMP");
#endif
}
bool
set_env(const char* name, std::string value)
{
#if defined(__MINGW32__) || defined(__MINGW64__)
std::string s = name;
s += '=';
s += value;
return !::_putenv(s.c_str());
#else
return !::setenv(name, value.c_str(), 1);
#endif
} }
namespace fs = std::experimental::filesystem; namespace fs = std::experimental::filesystem;
...@@ -57,7 +75,7 @@ test02() ...@@ -57,7 +75,7 @@ test02()
{ {
clean_env(); clean_env();
if (::setenv("TMPDIR", __gnu_test::nonexistent_path().string().c_str(), 1)) if (set_env("TMPDIR", __gnu_test::nonexistent_path().string()))
return; // just give up return; // just give up
std::error_code ec; std::error_code ec;
...@@ -80,7 +98,7 @@ test03() ...@@ -80,7 +98,7 @@ test03()
auto p = __gnu_test::nonexistent_path(); auto p = __gnu_test::nonexistent_path();
create_directories(p/"tmp"); create_directories(p/"tmp");
permissions(p, fs::perms::none); permissions(p, fs::perms::none);
setenv("TMPDIR", (p/"tmp").c_str(), 1); set_env("TMPDIR", (p/"tmp").string());
std::error_code ec; std::error_code ec;
auto r = fs::temp_directory_path(ec); // libstdc++/PR71337 auto r = fs::temp_directory_path(ec); // libstdc++/PR71337
VERIFY( ec == std::make_error_code(std::errc::permission_denied) ); VERIFY( ec == std::make_error_code(std::errc::permission_denied) );
...@@ -102,7 +120,7 @@ void ...@@ -102,7 +120,7 @@ void
test04() test04()
{ {
__gnu_test::scoped_file f; __gnu_test::scoped_file f;
setenv("TMPDIR", f.path.c_str(), 1); set_env("TMPDIR", f.path.string());
std::error_code ec; std::error_code ec;
auto r = fs::temp_directory_path(ec); auto r = fs::temp_directory_path(ec);
VERIFY( ec == std::make_error_code(std::errc::not_a_directory) ); VERIFY( ec == std::make_error_code(std::errc::not_a_directory) );
......
...@@ -34,16 +34,20 @@ test01() ...@@ -34,16 +34,20 @@ test01()
path pp = p; path pp = p;
pp /= p; pp /= p;
VERIFY( pp.native() == "/foo/bar/foo/bar" ); VERIFY( pp.string() == "/foo/bar/foo/bar" );
path q("baz"); path q("baz");
path qq = q; path qq = q;
qq /= q; qq /= q;
VERIFY( qq.native() == "baz/baz" ); #if defined(__MINGW32__) || defined(__MINGW64__)
VERIFY( qq.string() == "baz\\baz" );
#else
VERIFY( qq.string() == "baz/baz" );
#endif
q /= p; q /= p;
VERIFY( q.native() == "baz/foo/bar" ); VERIFY( q.string() == "baz/foo/bar" );
path r = ""; path r = "";
r /= path(); r /= path();
...@@ -54,7 +58,7 @@ test01() ...@@ -54,7 +58,7 @@ test01()
path s = "dir/"; path s = "dir/";
s /= path("/file"); s /= path("/file");
VERIFY( s.native() == "dir//file" ); VERIFY( s.string() == "dir//file" );
} }
int int
......
...@@ -34,18 +34,18 @@ test01() ...@@ -34,18 +34,18 @@ test01()
path pp = p; path pp = p;
pp += p; pp += p;
VERIFY( pp.native() == "/foo/bar/foo/bar" ); VERIFY( pp.string() == "/foo/bar/foo/bar" );
VERIFY( std::distance(pp.begin(), pp.end()) == 5 ); VERIFY( std::distance(pp.begin(), pp.end()) == 5 );
path q("foo/bar"); path q("foo/bar");
path qq = q; path qq = q;
qq += q; qq += q;
VERIFY( qq.native() == "foo/barfoo/bar" ); VERIFY( qq.string() == "foo/barfoo/bar" );
VERIFY( std::distance(qq.begin(), qq.end()) == 3 ); VERIFY( std::distance(qq.begin(), qq.end()) == 3 );
q += p; q += p;
VERIFY( q.native() == "foo/bar/foo/bar" ); VERIFY( q.string() == "foo/bar/foo/bar" );
VERIFY( std::distance(q.begin(), q.end()) == 4 ); VERIFY( std::distance(q.begin(), q.end()) == 4 );
} }
......
...@@ -48,8 +48,8 @@ test02() ...@@ -48,8 +48,8 @@ test02()
path rootdir = p.root_directory(); path rootdir = p.root_directory();
// If root-directory is composed of 'slash name', // If root-directory is composed of 'slash name',
// 'slash' is excluded from the returned string. // 'slash' is excluded from the returned string.
if (!rootdir.empty() && rootdir.native() != "/") if (!rootdir.empty() && rootdir.string() != "/")
VERIFY( rootdir.native()[0] != '/' ); VERIFY( rootdir.string()[0] != '/' );
} }
} }
......
...@@ -35,7 +35,7 @@ test01() ...@@ -35,7 +35,7 @@ test01()
path p = "foo.bar.baz.tar"; path p = "foo.bar.baz.tar";
std::vector<std::string> v; std::vector<std::string> v;
for (; !p.extension().empty(); p = p.stem()) for (; !p.extension().empty(); p = p.stem())
v.push_back(p.extension().native()); v.push_back(p.extension().string());
VERIFY( v.at(0) == ".tar" ); VERIFY( v.at(0) == ".tar" );
VERIFY( v.at(1) == ".baz" ); VERIFY( v.at(1) == ".baz" );
VERIFY( v.at(2) == ".bar" ); VERIFY( v.at(2) == ".bar" );
......
...@@ -27,14 +27,15 @@ void ...@@ -27,14 +27,15 @@ void
test01() test01()
{ {
using namespace std::experimental::filesystem; using namespace std::experimental::filesystem;
const std::string s = "abc"; using string_type = std::basic_string<path::value_type>;
const string_type s{ 'a', 'b', 'c' };
path p(s); path p(s);
VERIFY( p.native() == s ); VERIFY( p.native() == s );
VERIFY( p.c_str() == s ); VERIFY( p.c_str() == s );
VERIFY( static_cast<std::string>(p) == s ); VERIFY( static_cast<string_type>(p) == s );
std::string s2 = p; // implicit conversion string_type s2 = p; // implicit conversion
VERIFY( s2 == p.native() ); VERIFY( s2 == p.native() );
} }
......
...@@ -29,11 +29,17 @@ using std::filesystem::path; ...@@ -29,11 +29,17 @@ using std::filesystem::path;
void void
test01() test01()
{ {
VERIFY( path("/").is_absolute() ); #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
VERIFY( path("/foo").is_absolute() ); const bool is_posix = false;
VERIFY( path("/foo/").is_absolute() ); #else
VERIFY( path("/foo/bar").is_absolute() ); const bool is_posix = true;
VERIFY( path("/foo/bar/").is_absolute() ); #endif
VERIFY( path("/").is_absolute() == is_posix );
VERIFY( path("/foo").is_absolute() == is_posix );
VERIFY( path("/foo/").is_absolute() == is_posix );
VERIFY( path("/foo/bar").is_absolute() == is_posix );
VERIFY( path("/foo/bar/").is_absolute() == is_posix );
VERIFY( ! path("foo").is_absolute() ); VERIFY( ! path("foo").is_absolute() );
VERIFY( ! path("foo/").is_absolute() ); VERIFY( ! path("foo/").is_absolute() );
VERIFY( ! path("foo/bar").is_absolute() ); VERIFY( ! path("foo/bar").is_absolute() );
...@@ -43,16 +49,11 @@ test01() ...@@ -43,16 +49,11 @@ test01()
VERIFY( ! path("c:foo/").is_absolute() ); VERIFY( ! path("c:foo/").is_absolute() );
VERIFY( ! path("c:foo/bar").is_absolute() ); VERIFY( ! path("c:foo/bar").is_absolute() );
VERIFY( ! path("c:foo/bar/").is_absolute() ); VERIFY( ! path("c:foo/bar/").is_absolute() );
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS VERIFY( path("c:/").is_absolute() == !is_posix );
const bool drive_letter_is_root_name = true; VERIFY( path("c:/foo").is_absolute() == !is_posix );
#else VERIFY( path("c:/foo/").is_absolute() == !is_posix );
const bool drive_letter_is_root_name = false; VERIFY( path("c:/foo/bar").is_absolute() == !is_posix );
#endif VERIFY( path("c:/foo/bar/").is_absolute() == !is_posix );
VERIFY( path("c:/").is_absolute() == drive_letter_is_root_name );
VERIFY( path("c:/foo").is_absolute() == drive_letter_is_root_name );
VERIFY( path("c:/foo/").is_absolute() == drive_letter_is_root_name );
VERIFY( path("c:/foo/bar").is_absolute() == drive_letter_is_root_name );
VERIFY( path("c:/foo/bar/").is_absolute() == drive_letter_is_root_name );
} }
int int
......
...@@ -73,6 +73,16 @@ namespace __gnu_test ...@@ -73,6 +73,16 @@ namespace __gnu_test
"a", "a/b", "a/b/", "a/b/c", "a/b/c.d", "a/b/..", "a/b/c.", "a/b/.c" "a", "a/b", "a/b/", "a/b/c", "a/b/c.d", "a/b/..", "a/b/c.", "a/b/.c"
}; };
test_fs::path
root_path()
{
#if defined(__MING32__) || defined(__MINGW64__)
return L"c:/";
#else
return "/";
#endif
}
// This is NOT supposed to be a secure way to get a unique name! // This is NOT supposed to be a secure way to get a unique name!
// We just need a path that doesn't exist for testing purposes. // We just need a path that doesn't exist for testing purposes.
test_fs::path test_fs::path
...@@ -111,7 +121,7 @@ namespace __gnu_test ...@@ -111,7 +121,7 @@ namespace __gnu_test
explicit explicit
scoped_file(const path_type& p = nonexistent_path()) : path(p) scoped_file(const path_type& p = nonexistent_path()) : path(p)
{ std::ofstream{p.native()}; } { std::ofstream{p.c_str()}; }
scoped_file(path_type p, adopt_file_t) : path(p) { } scoped_file(path_type p, adopt_file_t) : path(p) { }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment