PR libstdc++/82777 fix path normalization for dot-dot
authorJonathan Wakely <jwakely@redhat.com>
Wed, 1 Nov 2017 17:09:14 +0000 (17:09 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 1 Nov 2017 17:09:14 +0000 (17:09 +0000)
PR libstdc++/82777
* src/filesystem/std-path.cc (path::lexically_normal): Remove dot-dot
elements correctly.
* testsuite/27_io/filesystem/path/generation/normal.cc: Add testcase.
* testsuite/util/testsuite_fs.h (compare_paths): Improve exception
text.

From-SVN: r254317

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

index b5adbb1..9805e4b 100644 (file)
@@ -1,3 +1,12 @@
+2017-11-01  Jonathan Wakely  <jwakely@redhat.com>
+
+       PR libstdc++/82777
+       * src/filesystem/std-path.cc (path::lexically_normal): Remove dot-dot
+       elements correctly.
+       * testsuite/27_io/filesystem/path/generation/normal.cc: Add testcase.
+       * testsuite/util/testsuite_fs.h (compare_paths): Improve exception
+       text.
+
 2017-10-30  Jonathan Wakely  <jwakely@redhat.com>
 
        * include/Makefile.am (stamp-bits-sup): Do not create broken symlink
index 1e2a8fa..330aee7 100644 (file)
@@ -388,10 +388,35 @@ path::lexically_normal() const
 #endif
       if (is_dotdot(p))
        {
-         if (ret.has_filename() && !is_dotdot(ret.filename()))
-           ret.remove_filename();
-         else if (ret.has_filename() || !ret.has_root_directory())
-           ret /= p;
+         if (ret.has_filename())
+           {
+             // remove a non-dot-dot filename immediately followed by /..
+             if (!is_dotdot(ret.filename()))
+               ret.remove_filename();
+             else
+               ret /= p;
+           }
+         else if (!ret.has_relative_path())
+           {
+             if (!ret.is_absolute())
+               ret /= p;
+           }
+         else
+           {
+             // Got a path with a relative path (i.e. at least one non-root
+             // element) and no filename at the end (i.e. empty last element),
+             // so must have a trailing slash. See what is before it.
+             auto elem = std::prev(ret.end(), 2);
+             if (elem->has_filename() && !is_dotdot(*elem))
+               {
+                 // 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());
+               }
+             else // ???
+               ret /= p;
+           }
        }
       else if (is_dot(p))
        ret /= path();
index 789ce18..df3b715 100644 (file)
@@ -46,6 +46,10 @@ test02()
   compare_paths( path().lexically_normal(), "" );
 
   compare_paths( path("/..").lexically_normal(), "/" );
+
+  // PR libstdc++/82777
+  compare_paths( path("./a/b/c/../.././b/c").lexically_normal(), "a/b/c" );
+  compare_paths( path("/a/b/c/../.././b/c").lexically_normal(), "/a/b/c" );
 }
 
 void
index 47f5609..c18dae2 100644 (file)
@@ -40,7 +40,7 @@ namespace __gnu_test
 {
 #define PATH_CHK(p1, p2, fn) \
     if ( p1.fn() != p2.fn() ) \
-      throw test_fs::filesystem_error( #fn, p1, p2, \
+      throw test_fs::filesystem_error("comparing '" #fn "' failed", p1, p2, \
          std::make_error_code(std::errc::invalid_argument) )
 
   void