Skip to content

Commit

Permalink
PR libstdc++/87116 fix path::lexically_normal() handling of dot-dot
Browse files Browse the repository at this point in the history
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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@263922 138bc75d-0d04-0410-961f-82ee72b054a4
  • Loading branch information
redi committed Aug 28, 2018
1 parent 70775f2 commit 79ec46f
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 7 deletions.
13 changes: 13 additions & 0 deletions libstdc++-v3/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
2018-08-28 Jonathan Wakely <[email protected]>

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 <[email protected]>

PR libstdc++/70694
Expand Down
19 changes: 15 additions & 4 deletions libstdc++-v3/src/filesystem/std-path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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'\\');
Expand All @@ -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
Expand All @@ -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;
Expand Down
63 changes: 60 additions & 3 deletions libstdc++-v3/testsuite/27_io/filesystem/path/generation/normal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@
#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()
Expand Down Expand Up @@ -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

{"/." , "/" },
{"/./" , "/" },
Expand All @@ -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
Expand Down Expand Up @@ -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();
}

0 comments on commit 79ec46f

Please sign in to comment.