diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index d752108797d..b6ae0e5ffab 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,16 @@ +2018-08-28 Jonathan Wakely + + 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 PR libstdc++/70694 diff --git a/libstdc++-v3/src/filesystem/std-path.cc b/libstdc++-v3/src/filesystem/std-path.cc index f6c0b8bb0f6..f382eb3759a 100644 --- a/libstdc++-v3/src/filesystem/std-path.cc +++ b/libstdc++-v3/src/filesystem/std-path.cc @@ -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; 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 6d0e2007a75..3b8311f81ad 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/generation/normal.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/generation/normal.cc @@ -24,7 +24,17 @@ #include 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(); }