From 50e248f0c8dd85a0e1778050d998f4010d87ed46 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Wed, 1 Nov 2017 17:09:14 +0000 Subject: [PATCH] PR libstdc++/82777 fix path normalization for dot-dot 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 | 9 +++++ libstdc++-v3/src/filesystem/std-path.cc | 33 ++++++++++++++++--- .../filesystem/path/generation/normal.cc | 4 +++ libstdc++-v3/testsuite/util/testsuite_fs.h | 2 +- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index b5adbb19dce..9805e4ba664 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,12 @@ +2017-11-01 Jonathan Wakely + + 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 * include/Makefile.am (stamp-bits-sup): Do not create broken symlink diff --git a/libstdc++-v3/src/filesystem/std-path.cc b/libstdc++-v3/src/filesystem/std-path.cc index 1e2a8fad584..330aee72b13 100644 --- a/libstdc++-v3/src/filesystem/std-path.cc +++ b/libstdc++-v3/src/filesystem/std-path.cc @@ -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(); 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 789ce186f82..df3b7154ab3 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/generation/normal.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/generation/normal.cc @@ -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 diff --git a/libstdc++-v3/testsuite/util/testsuite_fs.h b/libstdc++-v3/testsuite/util/testsuite_fs.h index 47f56090b47..c18dae28fcc 100644 --- a/libstdc++-v3/testsuite/util/testsuite_fs.h +++ b/libstdc++-v3/testsuite/util/testsuite_fs.h @@ -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 -- 2.30.2