UNC paths on Windows start with a root name "\\server" instead of a drive letter ("c:"). Support for parsing this was already implemented for cygwin, which (compared to MinGW) doesn't use backslash as the preferred separator. This mainly enables SLASHSLASH_IS_ROOTNAME on Windows and adjusts the tests accordingly. I aligned path::_M_append with path::operator/= to use has_root_directory and is_absolute from `this` instead of the appended path. Note that before this patch, `is_absolute && has_root_directory` was always `false`, as is_absolute implied has_root_directory. This isn't the case anymore (e.g. `\\foo` doesn't have a root directory).
PR libstdc++-v3/99333 PR libstdc++-v3/99430 PR libstdc++-v3/106127 libstdc++-v3/ChangeLog: * config/abi/pre/gnu.ver: Add fs::path::_M_is_unc_path() to ABI * include/bits/fs_path.h: Add and use fs::path::_M_is_unc_path() on Windows. * src/c++17/fs_path.cc (defined): Treat "//" as a root name on Windows. (path::_M_append): Align with operator/=. (path::_M_is_unc_path): Added helper to check for "(slash)(slash)(not-slash)". * testsuite/27_io/filesystem/path/append/path.cc: Added tests for UNC paths. * testsuite/27_io/filesystem/path/append/source.cc: Added tests for UNC paths. * testsuite/27_io/filesystem/path/decompose/filename.cc: Switched Windows FS to test for UNC paths. * testsuite/27_io/filesystem/path/decompose/root_directory.cc: Switched Windows FS to test for UNC paths. * testsuite/27_io/filesystem/path/generation/normal.cc: Switched Windows FS to test for UNC paths. * testsuite/27_io/filesystem/path/generic/94242.cc: Added case for double-slashes as root directories. * testsuite/27_io/filesystem/path/generic/generic_string.cc: Added case for double-slashes as root directories. * testsuite/27_io/filesystem/path/generic/utf.cc: Added case for double-slashes as root directories. * testsuite/27_io/filesystem/path/generic/wchar_t.cc: Added case for double-slashes as root directories. * testsuite/27_io/filesystem/path/itr/traversal.cc: Switched Windows FS to test for UNC paths. * testsuite/27_io/filesystem/path/query/is_absolute.cc: Test that UNC paths are absolute. Signed-off-by: Johannes Grunenberg <johannes.grunenb...@stud.uni-hannover.de> --- libstdc++-v3/config/abi/pre/gnu.ver | 7 +++++++ libstdc++-v3/include/bits/fs_path.h | 13 ++++++++++++- libstdc++-v3/src/c++17/fs_path.cc | 14 ++++++++++++-- .../27_io/filesystem/path/append/path.cc | 17 ++++++++++++++++- .../27_io/filesystem/path/append/source.cc | 14 ++++++++++++++ .../27_io/filesystem/path/decompose/filename.cc | 2 +- .../filesystem/path/decompose/root_directory.cc | 2 +- .../27_io/filesystem/path/generation/normal.cc | 2 +- .../27_io/filesystem/path/generic/94242.cc | 8 ++++++++ .../filesystem/path/generic/generic_string.cc | 6 +++++- .../27_io/filesystem/path/generic/utf.cc | 6 +++++- .../27_io/filesystem/path/generic/wchar_t.cc | 6 +++++- .../27_io/filesystem/path/itr/traversal.cc | 6 +++--- .../27_io/filesystem/path/query/is_absolute.cc | 1 + 14 files changed, 91 insertions(+), 13 deletions(-) diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 29bc7d86256..ed2275fead0 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -2546,6 +2546,13 @@ GLIBCXX_3.4.34 { _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE12_M_constructILb[01]EEEvPK[cw][jmy]; } GLIBCXX_3.4.33; +# GCC 16.1.0 +GLIBCXX_3.4.35 { + # std::filesystem::__cxx11::path::_M_is_unc_path() const + _ZNKSt10filesystem7__cxx114path14_M_is_unc_pathEv; + _ZNKSt10filesystem4path14_M_is_unc_pathEv; +} GLIBCXX_3.4.34; + # Symbols in the support library (libsupc++) have their own tag. CXXABI_1.3 { diff --git a/libstdc++-v3/include/bits/fs_path.h b/libstdc++-v3/include/bits/fs_path.h index ccf1d708e09..2694a13d5c3 100644 --- a/libstdc++-v3/include/bits/fs_path.h +++ b/libstdc++-v3/include/bits/fs_path.h @@ -609,6 +609,10 @@ namespace __detail pair<const string_type*, size_t> _M_find_extension() const noexcept; +#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS + bool _M_is_unc_path() const noexcept; +#endif + // path::_S_convert creates a basic_string<value_type> or // basic_string_view<value_type> from a basic_string<C> or // basic_string_view<C>, for an encoded character type C, @@ -1224,6 +1228,13 @@ namespace __detail for (auto& __elem : *this) { #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS + if (__elem._M_type() == _Type::_Root_name && _M_is_unc_path()) + { + __str += __slash; + __str += __slash; + __str += basic_string_view<value_type>(__elem._M_pathname).substr(2); + continue; + } if (__elem._M_type() == _Type::_Root_dir) { __str += __slash; @@ -1333,7 +1344,7 @@ namespace __detail path::is_absolute() const noexcept { #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS - return has_root_name() && has_root_directory(); + return (has_root_name() && has_root_directory()) || _M_is_unc_path(); #else return has_root_directory(); #endif diff --git a/libstdc++-v3/src/c++17/fs_path.cc b/libstdc++-v3/src/c++17/fs_path.cc index 215afa08ad2..481c8db4a47 100644 --- a/libstdc++-v3/src/c++17/fs_path.cc +++ b/libstdc++-v3/src/c++17/fs_path.cc @@ -26,7 +26,7 @@ # define _GLIBCXX_USE_CXX11_ABI 1 #endif -#ifdef __CYGWIN__ +#if defined(__CYGWIN__) || defined(_WIN32) // Interpret "//x" as a root-name, not root-dir + filename # define SLASHSLASH_IS_ROOTNAME 1 #endif @@ -710,7 +710,7 @@ path::_M_append(basic_string_view<value_type> s) lhs = {}; } } - else if (has_filename() || (!has_root_directory && is_absolute)) + else if (has_filename() || (!this->has_root_directory() && this->is_absolute())) add_sep = true; basic_string_view<value_type> rhs = s; @@ -1900,6 +1900,16 @@ path::_M_find_extension() const noexcept return {}; } +#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS +bool +path::_M_is_unc_path() const noexcept +{ + if (_M_pathname.size() < 3) + return false; + return is_dir_sep(_M_pathname[0]) && is_dir_sep(_M_pathname[1]) && !is_dir_sep(_M_pathname[2]); +} +#endif + void path::_M_split_cmpts() { diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/append/path.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/append/path.cc index d53603f80b7..0b9e49937ed 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/append/path.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/append/path.cc @@ -43,6 +43,19 @@ test01() compare_paths( append("baz", "baz"), "baz/baz" ); #else compare_paths( append("baz", "baz"), "baz\\baz" ); + compare_paths( append("\\\\foo", "bar"), "\\\\foo\\bar" ); + compare_paths( append("\\\\foo", "/bar"), "\\\\foo/bar" ); + compare_paths( append("\\\\foo", "\\bar"), "\\\\foo\\bar" ); + compare_paths( append("\\\\foo", "//bar"), "//bar" ); + compare_paths( append("\\\\foo", "\\\\bar"), "\\\\bar" ); + compare_paths( append("\\\\foo\\baz", "bar"), "\\\\foo\\baz\\bar" ); + compare_paths( append("\\\\foo\\baz", "/bar"), "\\\\foo/bar" ); + compare_paths( append("\\\\foo\\baz", "\\bar"), "\\\\foo\\bar" ); + compare_paths( append("\\\\foo\\baz", "//bar"), "//bar" ); + compare_paths( append("\\\\foo\\baz", "\\\\bar"), "\\\\bar" ); + compare_paths( append("\\\\\\server\\something", "\\else"), "\\else" ); + compare_paths( append("\\\\", "/foo"), "/foo" ); + compare_paths( append("\\\\wsl.local", "/foo"), "\\\\wsl.local/foo" ); #endif compare_paths( append("baz/", "baz"), "baz/baz" ); compare_paths( append("baz", "/foo/bar"), "/foo/bar" ); @@ -96,7 +109,9 @@ test02() // path("c:foo") / "/bar"; // yields "c:/bar" compare_paths( append("c:foo", "/bar"), "c:/bar" ); - // path("c:foo") / "c:bar"; // yields "c:foo/bar" + // path("c:foo") / "c:bar"; // yields "c:foo/bar" (actually: "c:foo\bar") + compare_paths( append("c:foo", "c:bar"), "c:foo\\bar" ); + compare_paths( append("foo", "c:\\bar"), "c:\\bar" ); #endif } diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc index 1b678292daf..7723bc8929b 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc @@ -71,6 +71,20 @@ test01() test( "dir/", "/file" ); test( "dir/", "file" ); + + test( "\\\\foo", "bar" ); + test( "\\\\foo", "/bar" ); + test( "\\\\foo", "\\bar" ); + test( "\\\\foo", "//bar" ); + test( "\\\\foo", "\\\\bar" ); + test( "\\\\foo\\baz", "bar" ); + test( "\\\\foo\\baz", "/bar" ); + test( "\\\\foo\\baz", "\\bar" ); + test( "\\\\foo\\baz", "//bar" ); + test( "\\\\foo\\baz", "\\\\bar" ); + test( "\\\\\\server\\something", "\\else" ); + test( "\\\\", "/foo" ); + test( "\\\\wsl.local", "/foo" ); } void diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/filename.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/filename.cc index 2ba95a6944c..de36229412b 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/filename.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/filename.cc @@ -33,7 +33,7 @@ test01() VERIFY( path("/foo/bar").filename() == "bar" ); VERIFY( path("/foo/bar/").filename() == "" ); VERIFY( path("/").filename() == "" ); -#ifdef __CYGWIN__ +#if defined(__CYGWIN__) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) VERIFY( path("//host").filename() == "" ); #else VERIFY( path("//host").filename() == "host" ); diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/root_directory.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/root_directory.cc index b78224061c6..a7c90f99752 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/root_directory.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/decompose/root_directory.cc @@ -33,7 +33,7 @@ test01() path p2 = "/foo/bar"; VERIFY( p2.root_directory() == path("/") ); path p3 = "//foo"; -#ifdef __CYGWIN__ +#if defined(__CYGWIN__) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) VERIFY( p3.root_directory() == path() ); #else VERIFY( p3.root_directory() == path("/") ); diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/generation/normal.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/generation/normal.cc index fcba0ec44c3..078777df2dc 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/generation/normal.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/generation/normal.cc @@ -109,7 +109,7 @@ test03() #endif {"C:/bar/.." , "C:/" }, {"C:" , "C:" }, -#ifdef __CYGWIN__ +#if defined(__CYGWIN__) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) {"//host/bar/.." , "//host/" }, {"//host" , "//host" }, #else diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/94242.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/94242.cc index 6ff2dd8259d..c2e9775ce67 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/94242.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/94242.cc @@ -31,7 +31,11 @@ test01() path p = "//foo//bar//."; using C = path::value_type; auto g = p.generic_string<C, std::char_traits<C>, SimpleAllocator<C>>(); +#if defined(__CYGWIN__) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) + VERIFY( g == path("//foo/bar/.").c_str() ); +#else VERIFY( g == path("/foo/bar/.").c_str() ); +#endif } void @@ -40,7 +44,11 @@ test02() path p = "//foo//bar//."; using C = char16_t; auto g = p.generic_string<C, std::char_traits<C>, SimpleAllocator<C>>(); +#if defined(__CYGWIN__) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) + VERIFY( g == u"//foo/bar/." ); +#else VERIFY( g == u"/foo/bar/." ); +#endif } int diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/generic_string.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/generic_string.cc index e24b34b642a..c56be3d34a1 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/generic_string.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/generic_string.cc @@ -31,12 +31,16 @@ test01() VERIFY( path().generic_string() == "" ); VERIFY( path("/").generic_string() == "/" ); VERIFY( path("////").generic_string() == "/" ); -#ifdef __CYGWIN__ +#if defined(__CYGWIN__) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) VERIFY( path("//a").generic_string() == "//a" ); VERIFY( path("//a/").generic_string() == "//a/" ); VERIFY( path("//a//").generic_string() == "//a/" ); VERIFY( path("//a/b").generic_string() == "//a/b" ); VERIFY( path("//a//b").generic_string() == "//a/b" ); + if constexpr (path::preferred_separator == L'\\') + { + VERIFY( path("\\\\foo\\bar").generic_string() == "//foo/bar" ); + } #else VERIFY( path("//a").generic_string() == "/a" ); VERIFY( path("//a/").generic_string() == "/a/" ); diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/utf.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/utf.cc index 59f88752bc2..41a234dc33b 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/utf.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/utf.cc @@ -30,10 +30,14 @@ test01() VERIFY( path().generic_u32string() == U"" ); VERIFY( path("/").generic_u32string() == U"/" ); VERIFY( path("////").generic_u32string() == U"/" ); -#ifdef __CYGWIN__ +#if defined(__CYGWIN__) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) VERIFY( path("//a").generic_u32string() == U"//a" ); VERIFY( path("//a/").generic_u32string() == U"//a/" ); VERIFY( path("//a/b").generic_u32string() == U"//a/b" ); + if constexpr (path::preferred_separator == L'\\') + { + VERIFY( path("\\\\foo\\bar").generic_u32string() == U"//foo/bar" ); + } #else VERIFY( path("//a").generic_u32string() == U"/a" ); VERIFY( path("//a/").generic_u32string() == U"/a/" ); diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/wchar_t.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/wchar_t.cc index 4c1495f7615..4860baf894a 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/generic/wchar_t.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/generic/wchar_t.cc @@ -30,10 +30,14 @@ test01() VERIFY( path().generic_wstring() == L"" ); VERIFY( path("/").generic_wstring() == L"/" ); VERIFY( path("////").generic_wstring() == L"/" ); -#ifdef __CYGWIN__ +#if defined(__CYGWIN__) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) VERIFY( path("//a").generic_wstring() == L"//a" ); VERIFY( path("//a/").generic_wstring() == L"//a/" ); VERIFY( path("//a/b").generic_wstring() == L"//a/b" ); + if constexpr (path::preferred_separator == L'\\') + { + VERIFY( path("\\\\foo\\bar").generic_wstring() == L"//foo/bar" ); + } #else VERIFY( path("//a").generic_wstring() == L"/a" ); VERIFY( path("//a/").generic_wstring() == L"/a/" ); diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc index 70fb486a4b6..ca28940a967 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc @@ -57,7 +57,7 @@ test01() p = "//rootname/dir/."; v.assign(p.begin(), p.end()); -#ifdef __CYGWIN__ +#if defined(__CYGWIN__) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) v2 = { "//rootname", "/", "dir", "." }; #else v2 = { "/", "rootname", "dir", "." }; @@ -66,7 +66,7 @@ test01() p = "//rootname/dir/"; v.assign(p.begin(), p.end()); -#ifdef __CYGWIN__ +#if defined(__CYGWIN__) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) v2 = { "//rootname", "/", "dir", "" }; #else v2 = { "/", "rootname", "dir", "" }; @@ -75,7 +75,7 @@ test01() p = "//rootname/dir/filename"; v.assign(p.begin(), p.end()); -#ifdef __CYGWIN__ +#if defined(__CYGWIN__) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) v2 = { "//rootname", "/", "dir", "filename" }; #else v2 = { "/", "rootname", "dir", "filename" }; diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/query/is_absolute.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/query/is_absolute.cc index 6b6c2b00e11..aa482ab97f9 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/query/is_absolute.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/query/is_absolute.cc @@ -52,6 +52,7 @@ test01() VERIFY( path("c:/foo/").is_absolute() == !is_posix ); VERIFY( path("c:/foo/bar").is_absolute() == !is_posix ); VERIFY( path("c:/foo/bar/").is_absolute() == !is_posix ); + VERIFY( path("\\\\foo/bar/").is_absolute() == !is_posix ); } int -- 2.49.0.windows.1