mirror of
https://github.com/boostorg/filesystem.git
synced 2025-05-12 13:41:47 +00:00
Reworked path::lexically_normal to remove some redundant dot path elements.
The new implementation is also not relying on the root name format and is more pertormant as it avoids unnecessarily copying path elements during operation. Note that this commit does not remove the trailing dot elements in the normalized paths.
This commit is contained in:
parent
4b84226783
commit
16bd89b7c0
@ -53,6 +53,7 @@
|
||||
<li><b>New:</b> Added <code>weakly_canonical</code> overloads taking <code>base</code> path as an argument.</li>
|
||||
<li><b>Breaking change:</b><code>path::filename</code>, <code>path::stem</code> and <code>path::extension</code> no longer consider root name of the path as a filename if the path only consists of a root name. For example, <code>path("C:").filename()</code> used to return "C:" on Windows and will return an empty path now.</li>
|
||||
<li><b>New:</b> Improved support for various path prefixes on Windows. Added support for local device prefix ("\\.\") and experimental support for NT path prefix ("\??\"). The prefixes will be included in the root name of a path.</li>
|
||||
<li>Reworked <code>path::lexically_normal</code> implementation to eliminate some cases of duplicate dot (".") elements in the normalized paths.</li>
|
||||
</ul>
|
||||
|
||||
<h2>1.76.0</h2>
|
||||
|
104
src/path.cpp
104
src/path.cpp
@ -431,60 +431,80 @@ BOOST_FILESYSTEM_DECL path path::lexically_relative(path const& base) const
|
||||
|
||||
BOOST_FILESYSTEM_DECL path path::lexically_normal() const
|
||||
{
|
||||
if (m_pathname.empty())
|
||||
return *this;
|
||||
const size_type root_path_size = find_root_path_size();
|
||||
path normal(m_pathname.c_str(), m_pathname.c_str() + root_path_size);
|
||||
size_type i = root_path_size, n = m_pathname.size();
|
||||
|
||||
path temp;
|
||||
iterator start(begin());
|
||||
iterator last(end());
|
||||
iterator stop(last--);
|
||||
for (iterator itr(start); itr != stop; ++itr)
|
||||
// Skip redundant directory separators after the root directory
|
||||
while (i < n && detail::is_directory_separator(m_pathname[i]))
|
||||
++i;
|
||||
|
||||
if (i < n)
|
||||
{
|
||||
// ignore "." except at start and last
|
||||
if (itr->native().size() == 1 && (itr->native())[0] == dot && itr != start && itr != last)
|
||||
continue;
|
||||
|
||||
// ignore a name and following ".."
|
||||
if (!temp.empty() && itr->native().size() == 2 && (itr->native())[0] == dot && (itr->native())[1] == dot) // dot dot
|
||||
bool last_element_was_dot = false;
|
||||
while (true)
|
||||
{
|
||||
string_type lf(temp.filename().native());
|
||||
string_type::size_type lf_size = lf.size();
|
||||
if (lf_size > 0 && (lf_size != 1 || (lf[0] != dot && lf[0] != separator)) && (lf_size != 2 || (lf[0] != dot && lf[1] != dot
|
||||
#ifdef BOOST_WINDOWS_API
|
||||
&& lf[1] != colon
|
||||
#endif
|
||||
)))
|
||||
{
|
||||
temp.remove_filename();
|
||||
//// if not root directory, must also remove "/" if any
|
||||
//if (temp.native().size() > 0
|
||||
// && temp.native()[temp.native().size()-1]
|
||||
// == separator)
|
||||
//{
|
||||
// size_type rns = 0, rds =
|
||||
// find_root_directory_start(temp.native(), temp.native().size(), rns);
|
||||
// if (rds == string_type::npos
|
||||
// || rds != temp.native().size()-1)
|
||||
// {
|
||||
// temp.m_pathname.erase(temp.native().size()-1);
|
||||
// }
|
||||
//}
|
||||
const size_type start_pos = i;
|
||||
|
||||
iterator next(itr);
|
||||
if (temp.empty() && ++next != stop && next == last && *last == detail::dot_path())
|
||||
// Find next separator
|
||||
while (i < n && !detail::is_directory_separator(m_pathname[i]))
|
||||
++i;
|
||||
|
||||
const size_type size = i - start_pos;
|
||||
|
||||
// Skip dot elements
|
||||
if (size == 1u && m_pathname[start_pos] == dot)
|
||||
{
|
||||
temp /= detail::dot_path();
|
||||
last_element_was_dot = true;
|
||||
goto skip_append;
|
||||
}
|
||||
continue;
|
||||
|
||||
last_element_was_dot = false;
|
||||
|
||||
// Process dot dot elements
|
||||
if (size == 2u && m_pathname[start_pos] == dot && m_pathname[start_pos + 1] == dot && normal.size() > root_path_size)
|
||||
{
|
||||
// Don't remove previous dot dot elements
|
||||
size_type pos = filename_pos(normal.m_pathname, root_path_size, normal.m_pathname.size());
|
||||
if ((normal.m_pathname.size() - pos) != 2 || normal.m_pathname[pos] != dot || normal.m_pathname[pos + 1] != dot)
|
||||
{
|
||||
if (pos > root_path_size && detail::is_directory_separator(normal.m_pathname[pos - 1]))
|
||||
--pos;
|
||||
normal.m_pathname.erase(normal.m_pathname.begin() + pos , normal.m_pathname.end());
|
||||
goto skip_append;
|
||||
}
|
||||
}
|
||||
|
||||
// Append the element
|
||||
normal.append_separator_if_needed();
|
||||
normal.m_pathname.append(m_pathname.c_str() + start_pos, size);
|
||||
}
|
||||
|
||||
skip_append:
|
||||
if (i == n)
|
||||
break;
|
||||
|
||||
// Skip directory separators, including duplicates
|
||||
while (i < n && detail::is_directory_separator(m_pathname[i]))
|
||||
++i;
|
||||
|
||||
if (i == n)
|
||||
{
|
||||
// If a path ends with a separator, add a trailing dot element
|
||||
goto append_trailing_dot;
|
||||
}
|
||||
}
|
||||
|
||||
temp /= *itr;
|
||||
if (normal.empty() || last_element_was_dot)
|
||||
{
|
||||
append_trailing_dot:
|
||||
normal.append_separator_if_needed();
|
||||
normal.m_pathname.push_back(dot);
|
||||
}
|
||||
}
|
||||
|
||||
if (temp.empty())
|
||||
temp /= detail::dot_path();
|
||||
return temp;
|
||||
return normal;
|
||||
}
|
||||
|
||||
} // namespace filesystem
|
||||
|
@ -56,7 +56,7 @@ void normalize_test()
|
||||
PATH_CHECK(path("/../..").normalize(), "/../..");
|
||||
PATH_CHECK(path("../foo").normalize(), "../foo");
|
||||
PATH_CHECK(path("foo/..").normalize(), ".");
|
||||
PATH_CHECK(path("foo/../").normalize(), "./.");
|
||||
PATH_CHECK(path("foo/../").normalize(), ".");
|
||||
PATH_CHECK((path("foo") / "..").normalize(), ".");
|
||||
PATH_CHECK(path("foo/...").normalize(), "foo/...");
|
||||
PATH_CHECK(path("foo/.../").normalize(), "foo/.../.");
|
||||
@ -74,7 +74,7 @@ void normalize_test()
|
||||
PATH_CHECK(path("foo/bar/..").normalize(), "foo");
|
||||
PATH_CHECK(path("foo/bar/../").normalize(), "foo/.");
|
||||
PATH_CHECK(path("foo/bar/../..").normalize(), ".");
|
||||
PATH_CHECK(path("foo/bar/../../").normalize(), "./.");
|
||||
PATH_CHECK(path("foo/bar/../../").normalize(), ".");
|
||||
PATH_CHECK(path("foo/bar/../blah").normalize(), "foo/blah");
|
||||
PATH_CHECK(path("f/../b").normalize(), "b");
|
||||
PATH_CHECK(path("f/b/..").normalize(), "f");
|
||||
@ -129,11 +129,11 @@ void normalize_test()
|
||||
{
|
||||
PATH_CHECK(path("c:..").normalize(), "c:..");
|
||||
PATH_CHECK(path("c:foo/..").normalize(), ".");
|
||||
PATH_CHECK(path("c:foo/../").normalize(), "./.");
|
||||
PATH_CHECK(path("c:foo/../").normalize(), ".");
|
||||
PATH_CHECK(path("c:/foo/..").normalize(), "c:");
|
||||
PATH_CHECK(path("c:/foo/../").normalize(), "c:/.");
|
||||
PATH_CHECK(path("c:/..").normalize(), ".");
|
||||
PATH_CHECK(path("c:/../").normalize(), "./.");
|
||||
PATH_CHECK(path("c:/../").normalize(), ".");
|
||||
PATH_CHECK(path("c:/../..").normalize(), "..");
|
||||
PATH_CHECK(path("c:/../../").normalize(), "../.");
|
||||
PATH_CHECK(path("c:/../foo").normalize(), "foo");
|
||||
|
@ -601,7 +601,7 @@ void non_member_tests()
|
||||
PATH_TEST_EQ(path(".") / "..", "./..");
|
||||
PATH_TEST_EQ(path("foo") / ".", "foo/.");
|
||||
PATH_TEST_EQ(path("..") / ".", "../.");
|
||||
PATH_TEST_EQ(path(".") / ".", "./.");
|
||||
PATH_TEST_EQ(path(".") / ".", ".");
|
||||
PATH_TEST_EQ(path(".") / "." / ".", "././.");
|
||||
PATH_TEST_EQ(path(".") / "foo" / ".", "./foo/.");
|
||||
PATH_TEST_EQ(path("foo") / "." / "bar", "foo/./bar");
|
||||
@ -2061,9 +2061,9 @@ void lexically_normal_tests()
|
||||
{
|
||||
std::cout << "lexically_normal_tests..." << std::endl;
|
||||
|
||||
// Note: lexically_lexically_normal() uses /= to build up some results, so these results will
|
||||
// Note: lexically_normal() uses /= to build up some results, so these results will
|
||||
// have the platform's preferred separator. Since that is immaterial to the correct
|
||||
// functioning of lexically_lexically_normal(), the test results are converted to generic form,
|
||||
// functioning of lexically_normal(), the test results are converted to generic form,
|
||||
// and the expected results are also given in generic form. Otherwise many of the
|
||||
// tests would incorrectly be reported as failing on Windows.
|
||||
|
||||
@ -2085,7 +2085,7 @@ void lexically_normal_tests()
|
||||
PATH_TEST_EQ(path("/../..").lexically_normal().generic_path(), "/../..");
|
||||
PATH_TEST_EQ(path("../foo").lexically_normal().generic_path(), "../foo");
|
||||
PATH_TEST_EQ(path("foo/..").lexically_normal().generic_path(), ".");
|
||||
PATH_TEST_EQ(path("foo/../").lexically_normal().generic_path(), "./.");
|
||||
PATH_TEST_EQ(path("foo/../").lexically_normal().generic_path(), ".");
|
||||
PATH_TEST_EQ((path("foo") / "..").lexically_normal().generic_path(), ".");
|
||||
PATH_TEST_EQ(path("foo/...").lexically_normal().generic_path(), "foo/...");
|
||||
PATH_TEST_EQ(path("foo/.../").lexically_normal().generic_path(), "foo/.../.");
|
||||
@ -2107,7 +2107,7 @@ void lexically_normal_tests()
|
||||
PATH_TEST_EQ(path("foo/./bar/../").lexically_normal().generic_path(), "foo/.");
|
||||
std::cout << path("foo/./bar/../").lexically_normal() << std::endl; // POSIX: "foo/.", Windows: "foo\."
|
||||
PATH_TEST_EQ(path("foo/bar/../..").lexically_normal().generic_path(), ".");
|
||||
PATH_TEST_EQ(path("foo/bar/../../").lexically_normal().generic_path(), "./.");
|
||||
PATH_TEST_EQ(path("foo/bar/../../").lexically_normal().generic_path(), ".");
|
||||
PATH_TEST_EQ(path("foo/bar/../blah").lexically_normal().generic_path(), "foo/blah");
|
||||
PATH_TEST_EQ(path("f/../b").lexically_normal().generic_path(), "b");
|
||||
PATH_TEST_EQ(path("f/b/..").lexically_normal().generic_path(), "f");
|
||||
@ -2163,11 +2163,11 @@ void lexically_normal_tests()
|
||||
{
|
||||
PATH_TEST_EQ(path("c:..").lexically_normal(), "c:..");
|
||||
PATH_TEST_EQ(path("c:foo/..").lexically_normal(), ".");
|
||||
PATH_TEST_EQ(path("c:foo/../").lexically_normal(), "./.");
|
||||
PATH_TEST_EQ(path("c:foo/../").lexically_normal(), ".");
|
||||
PATH_TEST_EQ(path("c:/foo/..").lexically_normal(), "c:");
|
||||
PATH_TEST_EQ(path("c:/foo/../").lexically_normal(), "c:/.");
|
||||
PATH_TEST_EQ(path("c:/..").lexically_normal(), ".");
|
||||
PATH_TEST_EQ(path("c:/../").lexically_normal(), "./.");
|
||||
PATH_TEST_EQ(path("c:/../").lexically_normal(), ".");
|
||||
PATH_TEST_EQ(path("c:/../..").lexically_normal(), "..");
|
||||
PATH_TEST_EQ(path("c:/../../").lexically_normal(), "../.");
|
||||
PATH_TEST_EQ(path("c:/../foo").lexically_normal(), "foo");
|
||||
|
Loading…
x
Reference in New Issue
Block a user