PR libstdc++/87116 fix path::lexically_normal() handling of dot-dot
authorJonathan Wakely <jwakely@redhat.com>
Tue, 28 Aug 2018 15:33:53 +0000 (16:33 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Tue, 28 Aug 2018 15:33:53 +0000 (16:33 +0100)
Previously the logic that turned "a/b/c/../.." into "a/" failed to
preserve an empty path at the end of the iteration sequence, as required
by the trailing slash. That meant the result didn't meet the class
invariants, and that "a/b/c/d/../../.." would remove four components
instead of the three that "../../.." should remove.

PR libstdc++/87116
* src/filesystem/std-path.cc (path::lexically_normal): When handling
a dot-dot filename, preserve an empty final component in the iteration
sequence.
[_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Use preferred-separator for
root-directory.
* testsuite/27_io/filesystem/path/generation/normal.cc: Add new tests
for more than two adjacent dot-dot filenames.
[_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Replace slashes with
preferred-separator in expected normalized strings.

From-SVN: r263922

libstdc++-v3/ChangeLog
libstdc++-v3/src/filesystem/std-path.cc
libstdc++-v3/testsuite/27_io/filesystem/path/generation/normal.cc

index d752108797d6724b92a485fb6b49ad7534200ff7..b6ae0e5ffab6773a60b9c98d9ab106745fa065af 100644 (file)
@@ -1,3 +1,16 @@
+2018-08-28  Jonathan Wakely  <jwakely@redhat.com>
+
+       PR libstdc++/87116
+       * src/filesystem/std-path.cc (path::lexically_normal): When handling
+       a dot-dot filename, preserve an empty final component in the iteration
+       sequence.
+       [_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Use preferred-separator for
+       root-directory.
+       * testsuite/27_io/filesystem/path/generation/normal.cc: Add new tests
+       for more than two adjacent dot-dot filenames.
+       [_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Replace slashes with
+       preferred-separator in expected normalized strings.
+
 2018-08-25  Iain Sandoe  <iain@sandoe.co.uk>
 
        PR libstdc++/70694
index f6c0b8bb0f63da037105cd5eab89521ac4b2681b..f382eb3759ade6aab67ef2d6bd36d0a0de1d171f 100644 (file)
@@ -438,7 +438,7 @@ path::lexically_normal() const
     {
 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
       // Replace each slash character in the root-name
-      if (p._M_type == _Type::_Root_name)
+      if (p._M_type == _Type::_Root_name || p._M_type == _Type::_Root_dir)
        {
          string_type s = p.native();
          std::replace(s.begin(), s.end(), L'/', L'\\');
@@ -458,7 +458,8 @@ path::lexically_normal() const
            }
          else if (!ret.has_relative_path())
            {
-             if (!ret.is_absolute())
+             // remove a dot-dot filename immediately after root-directory
+             if (!ret.has_root_directory())
                ret /= p;
            }
          else
@@ -471,8 +472,18 @@ path::lexically_normal() const
                {
                  // Remove the filename before the trailing slash
                  // (equiv. to ret = ret.parent_path().remove_filename())
-                 ret._M_pathname.erase(elem._M_cur->_M_pos);
-                 ret._M_cmpts.erase(elem._M_cur, ret._M_cmpts.end());
+
+                 if (elem == ret.begin())
+                   ret.clear();
+                 else
+                   {
+                     ret._M_pathname.erase(elem._M_cur->_M_pos);
+                     // Do we still have a trailing slash?
+                     if (std::prev(elem)->_M_type == _Type::_Filename)
+                       ret._M_cmpts.erase(elem._M_cur);
+                     else
+                       ret._M_cmpts.erase(elem._M_cur, ret._M_cmpts.end());
+                   }
                }
              else // ???
                ret /= p;
index 6d0e2007a7599b9df01f455c831fcfc6a782749c..3b8311f81adbe492e147121038a21c010a1406fc 100644 (file)
 #include <testsuite_hooks.h>
 
 using std::filesystem::path;
-using __gnu_test::compare_paths;
+
+void
+compare_paths(path p, std::string expected)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+  for (auto& c : expected)
+    if (c == '/')
+      c = '\\';
+#endif
+  __gnu_test::compare_paths(p, expected);
+}
 
 void
 test01()
@@ -69,8 +79,11 @@ test03()
     {"/foo"        , "/foo" },
     {"/foo/"       , "/foo/" },
     {"/foo/."      , "/foo/" },
-    {"/foo/bar/.." , "/foo/" },
     {"/foo/.."     , "/" },
+    {"/foo/../.."  , "/" },
+    {"/foo/bar/.." , "/foo/" },
+    {"/foo/bar/../.." , "/" },
+    {"/foo/bar/baz/../../.." , "/" }, // PR libstdc++/87116
 
     {"/."          , "/" },
     {"/./"         , "/" },
@@ -88,10 +101,11 @@ test03()
     {"foo/.."      , "." },
     {"foo/../"     , "." },
     {"foo/../.."   , ".." },
+    {"foo/../../..", "../.." },
 
     // with root name (OS-dependent):
 #if defined(_WIN32) && !defined(__CYGWIN__)
-    {"C:bar/.."    , "C:." },
+    {"C:bar/.."    , "C:" },
 #else
     {"C:bar/.."    , "." },
 #endif
@@ -119,10 +133,53 @@ test03()
     compare_paths( path(test.input).lexically_normal(), test.normalized );
 }
 
+void
+test04()
+{
+  // PR libstdc++/87116
+  path p = "a/b/c";
+  compare_paths( (p/"../..").lexically_normal(), "a/" );
+
+  p = "a/b/c/d/e";
+  compare_paths( (p/"..").lexically_normal(),  "a/b/c/d/" );
+  compare_paths( (p/"../..").lexically_normal(),  "a/b/c/" );
+  compare_paths( (p/"../../..").lexically_normal(),  "a/b/" );
+  compare_paths( (p/"../../../..").lexically_normal(),  "a/" );
+  compare_paths( (p/"../../../../..").lexically_normal(),  "." );
+  compare_paths( (p/"../../../../../..").lexically_normal(),  ".." );
+
+  p = "/a/b/c/d/e";
+  compare_paths( (p/"..").lexically_normal(),  "/a/b/c/d/" );
+  compare_paths( (p/"../..").lexically_normal(),  "/a/b/c/" );
+  compare_paths( (p/"../../..").lexically_normal(),  "/a/b/" );
+  compare_paths( (p/"../../../..").lexically_normal(),  "/a/" );
+  compare_paths( (p/"../../../../..").lexically_normal(),  "/" );
+  compare_paths( (p/"../../../../../..").lexically_normal(),  "/" );
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+  p = "A:b/c/d/e";
+  compare_paths( (p/"..").lexically_normal(),  "A:b/c/d/" );
+  compare_paths( (p/"../..").lexically_normal(),  "A:b/c/" );
+  compare_paths( (p/"../../..").lexically_normal(),  "A:b/" );
+  compare_paths( (p/"../../../..").lexically_normal(),  "A:" );
+  compare_paths( (p/"../../../../..").lexically_normal(),  "A:.." );
+  compare_paths( (p/"../../../../../..").lexically_normal(),  "A:../.." );
+
+  p = "A:/b/c/d/e";
+  compare_paths( (p/"..").lexically_normal(),  "A:/b/c/d/" );
+  compare_paths( (p/"../..").lexically_normal(),  "A:/b/c/" );
+  compare_paths( (p/"../../..").lexically_normal(),  "A:/b/" );
+  compare_paths( (p/"../../../..").lexically_normal(),  "A:/" );
+  compare_paths( (p/"../../../../..").lexically_normal(),  "A:/" );
+  compare_paths( (p/"../../../../../..").lexically_normal(),  "A:/" );
+#endif
+}
+
 int
 main()
 {
   test01();
   test02();
   test03();
+  test04();
 }