[PATCH 1/2] libstdc++: Support UNC paths on MinGW in fs::path
Johannes Grunenberg
johannes.grunenberg@stud.uni-hannover.de
Sat May 10 11:46:16 GMT 2025
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.grunenberg@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
More information about the Libstdc++
mailing list