Commit dd35da2c by Jonathan Wakely Committed by Jonathan Wakely

PR libstdc++/87116 fix path::lexically_normal() handling of dot-dot

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
parent f30bafb7
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> 2018-08-25 Iain Sandoe <iain@sandoe.co.uk>
PR libstdc++/70694 PR libstdc++/70694
......
...@@ -438,7 +438,7 @@ path::lexically_normal() const ...@@ -438,7 +438,7 @@ path::lexically_normal() const
{ {
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
// Replace each slash character in the root-name // 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(); string_type s = p.native();
std::replace(s.begin(), s.end(), L'/', L'\\'); std::replace(s.begin(), s.end(), L'/', L'\\');
...@@ -458,7 +458,8 @@ path::lexically_normal() const ...@@ -458,7 +458,8 @@ path::lexically_normal() const
} }
else if (!ret.has_relative_path()) 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; ret /= p;
} }
else else
...@@ -471,8 +472,18 @@ path::lexically_normal() const ...@@ -471,8 +472,18 @@ path::lexically_normal() const
{ {
// Remove the filename before the trailing slash // Remove the filename before the trailing slash
// (equiv. to ret = ret.parent_path().remove_filename()) // (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 // ??? else // ???
ret /= p; ret /= p;
......
...@@ -24,7 +24,17 @@ ...@@ -24,7 +24,17 @@
#include <testsuite_hooks.h> #include <testsuite_hooks.h>
using std::filesystem::path; 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 void
test01() test01()
...@@ -69,8 +79,11 @@ test03() ...@@ -69,8 +79,11 @@ test03()
{"/foo" , "/foo" }, {"/foo" , "/foo" },
{"/foo/" , "/foo/" }, {"/foo/" , "/foo/" },
{"/foo/." , "/foo/" }, {"/foo/." , "/foo/" },
{"/foo/bar/.." , "/foo/" },
{"/foo/.." , "/" }, {"/foo/.." , "/" },
{"/foo/../.." , "/" },
{"/foo/bar/.." , "/foo/" },
{"/foo/bar/../.." , "/" },
{"/foo/bar/baz/../../.." , "/" }, // PR libstdc++/87116
{"/." , "/" }, {"/." , "/" },
{"/./" , "/" }, {"/./" , "/" },
...@@ -88,10 +101,11 @@ test03() ...@@ -88,10 +101,11 @@ test03()
{"foo/.." , "." }, {"foo/.." , "." },
{"foo/../" , "." }, {"foo/../" , "." },
{"foo/../.." , ".." }, {"foo/../.." , ".." },
{"foo/../../..", "../.." },
// with root name (OS-dependent): // with root name (OS-dependent):
#if defined(_WIN32) && !defined(__CYGWIN__) #if defined(_WIN32) && !defined(__CYGWIN__)
{"C:bar/.." , "C:." }, {"C:bar/.." , "C:" },
#else #else
{"C:bar/.." , "." }, {"C:bar/.." , "." },
#endif #endif
...@@ -119,10 +133,53 @@ test03() ...@@ -119,10 +133,53 @@ test03()
compare_paths( path(test.input).lexically_normal(), test.normalized ); 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 int
main() main()
{ {
test01(); test01();
test02(); test02();
test03(); test03();
test04();
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment