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

Reply via email to