Fix handling of POSIX paths containing a root-name
authorJonathan Wakely <jwakely@redhat.com>
Thu, 13 Dec 2018 20:34:10 +0000 (20:34 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 13 Dec 2018 20:34:10 +0000 (20:34 +0000)
Fix path appending and concatenating to work correctly for a leading
root-name. Check a new macro, SLASHSLASH_IS_ROOT_NAME, instead of making
the behaviour depend directly on __CYGWIN__.

* src/filesystem/std-path.cc (SLASHSLASH_IS_ROOT_NAME): New macro to
control whether interpret paths with two slashes as a root-name.
(path::operator/=(const path&)) [SLASHSLASH_IS_ROOT_NAME]: Add a
root-directory when appending to a root-name.
(path::_M_append(basic_string_view<value_type>))
[SLASHSLASH_IS_ROOT_NAME]: Likewise.
(path::operator/=(const path&)) [SLASHSLASH_IS_ROOT_NAME]: Likewise.
(path::_M_concat(basic_string_view<value_type>))
[SLASHSLASH_IS_ROOT_NAME]: Likewise.
(path::lexically_normal()) [SLASHSLASH_IS_ROOT_NAME]: Use += instead
of /= to add a root-directory to the result.
* testsuite/27_io/filesystem/path/decompose/root_directory.cc: Fix
expected result for Cygwin.

From-SVN: r267107

libstdc++-v3/ChangeLog
libstdc++-v3/src/filesystem/std-path.cc
libstdc++-v3/testsuite/27_io/filesystem/path/decompose/root_directory.cc

index c1a2502..a4f6507 100644 (file)
@@ -1,5 +1,19 @@
 2018-12-13  Jonathan Wakely  <jwakely@redhat.com>
 
+       * src/filesystem/std-path.cc (SLASHSLASH_IS_ROOT_NAME): New macro to
+       control whether interpret paths with two slashes as a root-name.
+       (path::operator/=(const path&)) [SLASHSLASH_IS_ROOT_NAME]: Add a
+       root-directory when appending to a root-name.
+       (path::_M_append(basic_string_view<value_type>))
+       [SLASHSLASH_IS_ROOT_NAME]: Likewise.
+       (path::operator/=(const path&)) [SLASHSLASH_IS_ROOT_NAME]: Likewise.
+       (path::_M_concat(basic_string_view<value_type>))
+       [SLASHSLASH_IS_ROOT_NAME]: Likewise.
+       (path::lexically_normal()) [SLASHSLASH_IS_ROOT_NAME]: Use += instead
+       of /= to add a root-directory to the result.
+       * testsuite/27_io/filesystem/path/decompose/root_directory.cc: Fix
+       expected result for Cygwin.
+
        PR libstdc++/71044
        * include/bits/fs_path.h (path::path(path&&)): Add noexcept when
        appropriate. Move _M_cmpts instead of reparsing the native pathname.
index e9c7892..d252049 100644 (file)
 # define _GLIBCXX_USE_CXX11_ABI 1
 #endif
 
+#ifdef __CYGWIN__
+// Interpret "//x" as a root-name, not root-dir + filename
+# define SLASHSLASH_IS_ROOTNAME 1
+#endif
+
 #include <filesystem>
 #include <algorithm>
 #include <bits/stl_uninitialized.h>
@@ -70,7 +75,7 @@ struct path::_Parser
     // look for root name or root directory
     if (is_dir_sep(input[0]))
       {
-#ifdef __CYGWIN__
+#if SLASHSLASH_IS_ROOTNAME
        // look for root name, such as "//foo"
        if (len > 2 && input[1] == input[0])
          {
@@ -515,7 +520,7 @@ path::operator/=(const path& __p)
   string_view_type sep;
   if (has_filename())
     sep = { &preferred_separator, 1 };  // need to add a separator
-#ifdef __CYGWIN__
+#if SLASHSLASH_IS_ROOTNAME
   else if (_M_type() == _Type::_Root_name) // root-name with no root-dir
     sep = { &preferred_separator, 1 };  // need to add a separator
 #endif
@@ -535,6 +540,10 @@ path::operator/=(const path& __p)
     capacity += __p._M_cmpts.size();
   else if (!__p.empty() || !sep.empty())
     capacity += 1;
+#if SLASHSLASH_IS_ROOTNAME
+  if (orig_type == _Type::_Root_name)
+    ++capacity; // Need to insert root-directory after root-name
+#endif
 
   if (orig_type == _Type::_Multi)
     {
@@ -568,6 +577,14 @@ path::operator/=(const path& __p)
          string_view_type s(_M_pathname.data(), orig_pathlen);
          ::new(output++) _Cmpt(s, orig_type, 0);
          ++_M_cmpts._M_impl->_M_size;
+#if SLASHSLASH_IS_ROOTNAME
+         if (orig_type == _Type::_Root_name)
+           {
+             ::new(output++) _Cmpt(sep, _Type::_Root_dir,
+                                   orig_pathlen + sep.length());
+             ++_M_cmpts._M_impl->_M_size;
+           }
+#endif
        }
 
       if (__p._M_type() == _Type::_Multi)
@@ -668,7 +685,7 @@ path::_M_append(basic_string_view<value_type> s)
   basic_string_view<value_type> sep;
   if (has_filename())
     sep = { &preferred_separator, 1 };  // need to add a separator
-#ifdef __CYGWIN__
+#if SLASHSLASH_IS_ROOTNAME
   else if (_M_type() == _Type::_Root_name) // root-name with no root-dir
     sep = { &preferred_separator, 1 };  // need to add a separator
 #endif
@@ -723,6 +740,11 @@ path::_M_append(basic_string_view<value_type> s)
   else if (!sep.empty())
     ++capacity;
 
+#if SLASHSLASH_IS_ROOTNAME
+  if (orig_type == _Type::_Root_name)
+    ++capacity; // Need to insert root-directory after root-name
+#endif
+
   __try
     {
       _M_cmpts.type(_Type::_Multi);
@@ -740,6 +762,15 @@ path::_M_append(basic_string_view<value_type> s)
          // Create single component from original path
          ::new(output++) _Cmpt(orig_pathname, orig_type, 0);
          ++_M_cmpts._M_impl->_M_size;
+
+#if SLASHSLASH_IS_ROOTNAME
+         if (!sep.empty() && orig_type == _Type::_Root_name)
+           {
+             ::new(output++) _Cmpt(sep, _Type::_Root_dir,
+                                   orig_pathlen + sep.length());
+             ++_M_cmpts._M_impl->_M_size;
+           }
+#endif
        }
 
       if (next != buf.begin())
@@ -823,7 +854,11 @@ path::operator+=(const path& p)
     {
       // See if there's a filename or root-name at the end of the original path
       // that we can add to.
-      if (_M_type() == _Type::_Filename)
+      if (_M_type() == _Type::_Filename
+#if SLASHSLASH_IS_ROOTNAME
+         || _M_type() == _Type::_Root_name
+#endif
+         )
        {
          if (p._M_type() == _Type::_Filename)
            {
@@ -858,8 +893,6 @@ path::operator+=(const path& p)
       && _M_cmpts.back()._M_type() == _Type::_Filename)
     orig_filenamelen = 0; // current path has empty filename at end
 
-  // TODO handle "//rootname" + "foo" case for Cygwin.
-
   int capacity = 0;
   if (_M_type() == _Type::_Multi)
     capacity += _M_cmpts.size();
@@ -884,6 +917,16 @@ path::operator+=(const path& p)
          ptr->_M_pathname.reserve(_M_pathname.length() + extra.length());
          ptr->_M_pathname = _M_pathname;
          ptr->_M_pathname += extra;
+
+#if SLASHSLASH_IS_ROOTNAME
+         if (orig_type == _Type::_Root_name)
+           {
+             basic_string_view<value_type> s(p._M_pathname);
+             ::new(output++) _Cmpt(s.substr(extra.length(), 1),
+                 _Type::_Root_dir, orig_pathlen + extra.length());
+             ++_M_cmpts._M_impl->_M_size;
+           }
+#endif
        }
       else if (orig_filenamelen == 0 && it != last)
        {
@@ -895,7 +938,7 @@ path::operator+=(const path& p)
        {
          basic_string_view<value_type> s = it->_M_pathname;
          auto pos = orig_pathlen;
-#ifdef __CYGWIN__
+#if SLASHSLASH_IS_ROOTNAME
          s.remove_prefix(2);
          pos += 2;
 #endif
@@ -999,7 +1042,11 @@ path::_M_concat(basic_string_view<value_type> s)
     {
       // See if there's a filename or root-name at the end of the original path
       // that we can add to.
-      if (_M_type() == _Type::_Filename)
+      if (_M_type() == _Type::_Filename
+#if SLASHSLASH_IS_ROOTNAME
+         || _M_type() == _Type::_Root_name
+#endif
+         )
        {
          if (cmpt.str.length() == s.length())
            {
@@ -1031,9 +1078,6 @@ path::_M_concat(basic_string_view<value_type> s)
       && _M_cmpts.back()._M_type() == _Type::_Filename)
     orig_filenamelen = 0; // original path had empty filename at end
 
-
-  // TODO handle "//rootname" + "foo" case for Cygwin.
-
   std::array<_Parser::cmpt, 64> buf;
   auto next = buf.begin();
 
@@ -1065,6 +1109,11 @@ path::_M_concat(basic_string_view<value_type> s)
   if (is_dir_sep(s.back()))
     ++capacity;
 
+#if SLASHSLASH_IS_ROOTNAME
+  if (orig_type == _Type::_Root_name)
+    ++capacity; // Need to insert root-directory after root-name
+#endif
+
   __try
     {
       _M_cmpts.type(_Type::_Multi);
@@ -1080,6 +1129,15 @@ path::_M_concat(basic_string_view<value_type> s)
          p->_M_pathname.reserve(orig_pathname.length() + extra.length());
          p->_M_pathname = orig_pathname;
          p->_M_pathname += extra;
+
+#if SLASHSLASH_IS_ROOTNAME
+         if (orig_type == _Type::_Root_name)
+           {
+             ::new(output++) _Cmpt(s.substr(extra.length(), 1),
+                 _Type::_Root_dir, orig_pathlen + extra.length());
+             ++_M_cmpts._M_impl->_M_size;
+           }
+#endif
        }
       else if (orig_filenamelen == 0)
        {
@@ -1532,6 +1590,10 @@ path::lexically_normal() const
        }
       else if (is_dot(p))
        ret /= path();
+#if SLASHSLASH_IS_ROOTNAME
+      else if (p._M_type() == _Type::_Root_dir)
+       ret += '/'; // using operator/=('/') would replace whole of ret
+#endif
       else
        ret /= p;
     }
index da5f377..40af155 100644 (file)
@@ -35,7 +35,11 @@ test01()
   path p2 = "/foo/bar";
   VERIFY( p2.root_directory() == path("/") );
   path p3 = "//foo";
+#ifdef __CYGWIN__
+  VERIFY( p3.root_directory() == path() );
+#else
   VERIFY( p3.root_directory() == path("/") );
+#endif
   path p4 = "///foo";
   VERIFY( p4.root_directory() == path("/") );
 }