Reworked absolute() to fix appending root directory.

Because of the changed semantics of appending operations in v4, path
composition in absolute() would produce incorrect results because at some
point it would append root directory and therefore discard root name
that was potentially added before. The updated implementation fixes that,
and also fixes the case when the input path is already absolute and
starts with a root directory, and the base path has a root name.
Previously, the returned path would contain the root name from the
base path, while the correct thing to do is to return the input path
as is.
This commit is contained in:
Andrey Semashev 2021-11-05 19:40:05 +03:00
parent 0d413a5e4f
commit cc763cb48e
3 changed files with 26 additions and 35 deletions

View File

@ -45,6 +45,7 @@
<li><b>v4:</b> <code>path</code> append operations now consider root name and root directory in the appended path. If the appended path is absolute, or root name is present and differs from the source path, the resulting path is equivalent to the appended path. If root directory is present, the result is the root directory and <li><b>v4:</b> <code>path</code> append operations now consider root name and root directory in the appended path. If the appended path is absolute, or root name is present and differs from the source path, the resulting path is equivalent to the appended path. If root directory is present, the result is the root directory and
relative path rebased on top of the root name of the source path. Otherwise, the behavior is similar to v3.(<a href="https://github.com/boostorg/filesystem/issues/214">#214</a>)</li> relative path rebased on top of the root name of the source path. Otherwise, the behavior is similar to v3.(<a href="https://github.com/boostorg/filesystem/issues/214">#214</a>)</li>
<li>Optimized overloads of <code>path::assign</code>, <code>path::append</code>, <code>path::concat</code> and the corresponding operators to avoid unnecessary path copying and reduce the amount of code redundancy.</li> <li>Optimized overloads of <code>path::assign</code>, <code>path::append</code>, <code>path::concat</code> and the corresponding operators to avoid unnecessary path copying and reduce the amount of code redundancy.</li>
<li>On POSIX systems, fixed <code>absolute(p, base)</code> returning a path with root name <code>base.root_name()</code> if <code>p</code> starts with a root directory. In such a case <code>p</code> is already an absolute path and should be returned as is.</li>
<li><code>create_directories</code> no longer reports an error if the input path consists entirely of dot (".") and dot-dot ("..") elements. The implementation is no longer using recursion internally and therefore is better protected from stack overflow on extremely long paths.</li> <li><code>create_directories</code> no longer reports an error if the input path consists entirely of dot (".") and dot-dot ("..") elements. The implementation is no longer using recursion internally and therefore is better protected from stack overflow on extremely long paths.</li>
</ul> </ul>

View File

@ -1344,24 +1344,12 @@ BOOST_FILESYSTEM_DECL bool possible_large_file_size_support()
BOOST_FILESYSTEM_DECL BOOST_FILESYSTEM_DECL
path absolute(path const& p, path const& base, system::error_code* ec) path absolute(path const& p, path const& base, system::error_code* ec)
{ {
// if ( p.empty() || p.is_absolute() )
// return p;
// // recursively calling absolute is sub-optimal, but is simple
// path abs_base(base.is_absolute() ? base : absolute(base));
//# ifdef BOOST_WINDOWS_API
// if (p.has_root_directory())
// return abs_base.root_name() / p;
// // !p.has_root_directory
// if (p.has_root_name())
// return p.root_name()
// / abs_base.root_directory() / abs_base.relative_path() / p.relative_path();
// // !p.has_root_name()
//# endif
// return abs_base / p;
if (ec) if (ec)
ec->clear(); ec->clear();
if (p.is_absolute())
return p;
// recursively calling absolute is sub-optimal, but is sure and simple // recursively calling absolute is sub-optimal, but is sure and simple
path abs_base = base; path abs_base = base;
if (!base.is_absolute()) if (!base.is_absolute())
@ -1378,35 +1366,30 @@ path absolute(path const& p, path const& base, system::error_code* ec)
} }
} }
// store expensive to compute values that are needed multiple times
path p_root_name(p.root_name());
path base_root_name(abs_base.root_name());
path p_root_directory(p.root_directory());
if (p.empty()) if (p.empty())
return abs_base; return abs_base;
if (!p_root_name.empty()) // p.has_root_name() path res;
if (p.has_root_name())
res = p.root_name();
else
res = abs_base.root_name();
if (p.has_root_directory())
{ {
if (p_root_directory.empty()) // !p.has_root_directory() res.concat(p.root_directory());
return p_root_name / abs_base.root_directory() / abs_base.relative_path() / p.relative_path();
// p is absolute, so fall through to return p at end of block
}
else if (!p_root_directory.empty()) // p.has_root_directory()
{
#ifdef BOOST_POSIX_API
// POSIX can have root name it it is a network path
if (base_root_name.empty()) // !abs_base.has_root_name()
return p;
#endif
return base_root_name / p;
} }
else else
{ {
return abs_base / p; res.concat(abs_base.root_directory());
res /= abs_base.relative_path();
} }
return p; // p.is_absolute() is true path p_relative_path(p.relative_path());
if (!p_relative_path.empty())
res /= p_relative_path;
return res;
} }
BOOST_FILESYSTEM_DECL BOOST_FILESYSTEM_DECL

View File

@ -1475,10 +1475,17 @@ void absolute_tests()
} }
// !p.has_root_name() // !p.has_root_name()
// p.has_root_directory() // p.has_root_directory()
#ifdef BOOST_WINDOWS_API
BOOST_TEST_EQ(fs::absolute(fs::path("/"), "//xyz/"), fs::path("//xyz/")); BOOST_TEST_EQ(fs::absolute(fs::path("/"), "//xyz/"), fs::path("//xyz/"));
BOOST_TEST_EQ(fs::absolute(fs::path("/"), "//xyz/abc"), fs::path("//xyz/")); BOOST_TEST_EQ(fs::absolute(fs::path("/"), "//xyz/abc"), fs::path("//xyz/"));
BOOST_TEST_EQ(fs::absolute(fs::path("/foo"), "//xyz/"), fs::path("//xyz/foo")); BOOST_TEST_EQ(fs::absolute(fs::path("/foo"), "//xyz/"), fs::path("//xyz/foo"));
BOOST_TEST_EQ(fs::absolute(fs::path("/foo"), "//xyz/abc"), fs::path("//xyz/foo")); BOOST_TEST_EQ(fs::absolute(fs::path("/foo"), "//xyz/abc"), fs::path("//xyz/foo"));
#else
BOOST_TEST_EQ(fs::absolute(fs::path("/"), "//xyz/"), fs::path("/"));
BOOST_TEST_EQ(fs::absolute(fs::path("/"), "//xyz/abc"), fs::path("/"));
BOOST_TEST_EQ(fs::absolute(fs::path("/foo"), "//xyz/"), fs::path("/foo"));
BOOST_TEST_EQ(fs::absolute(fs::path("/foo"), "//xyz/abc"), fs::path("/foo"));
#endif
// !p.has_root_directory() // !p.has_root_directory()
BOOST_TEST_EQ(fs::absolute(fs::path("foo"), "//xyz/abc"), fs::path("//xyz/abc/foo")); BOOST_TEST_EQ(fs::absolute(fs::path("foo"), "//xyz/abc"), fs::path("//xyz/abc/foo"));
BOOST_TEST_EQ(fs::absolute(fs::path("foo/bar"), "//xyz/abc"), fs::path("//xyz/abc/foo/bar")); BOOST_TEST_EQ(fs::absolute(fs::path("foo/bar"), "//xyz/abc"), fs::path("//xyz/abc/foo/bar"));