From cc763cb48efea3dac897752d403d5bc229d78b59 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Fri, 5 Nov 2021 19:40:05 +0300 Subject: [PATCH] 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. --- doc/release_history.html | 1 + src/operations.cpp | 53 ++++++++++++++-------------------------- test/operations_test.cpp | 7 ++++++ 3 files changed, 26 insertions(+), 35 deletions(-) diff --git a/doc/release_history.html b/doc/release_history.html index 61aba4f..fa4d2c8 100644 --- a/doc/release_history.html +++ b/doc/release_history.html @@ -45,6 +45,7 @@
  • v4: path 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.(#214)
  • Optimized overloads of path::assign, path::append, path::concat and the corresponding operators to avoid unnecessary path copying and reduce the amount of code redundancy.
  • +
  • On POSIX systems, fixed absolute(p, base) returning a path with root name base.root_name() if p starts with a root directory. In such a case p is already an absolute path and should be returned as is.
  • create_directories 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.
  • diff --git a/src/operations.cpp b/src/operations.cpp index fdcc156..524f0a3 100644 --- a/src/operations.cpp +++ b/src/operations.cpp @@ -1344,24 +1344,12 @@ BOOST_FILESYSTEM_DECL bool possible_large_file_size_support() BOOST_FILESYSTEM_DECL 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) ec->clear(); + if (p.is_absolute()) + return p; + // recursively calling absolute is sub-optimal, but is sure and simple path abs_base = base; 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()) 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() - 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; + res.concat(p.root_directory()); } 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 diff --git a/test/operations_test.cpp b/test/operations_test.cpp index acd4712..612f9be 100644 --- a/test/operations_test.cpp +++ b/test/operations_test.cpp @@ -1475,10 +1475,17 @@ void absolute_tests() } // !p.has_root_name() // 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/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/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() 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"));