From 0aee13c162ccfe88fa3415791bd312b0be480dbf Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Fri, 5 Nov 2021 20:27:02 +0300 Subject: [PATCH] Append a trailing directory separator when appending an empty path in v4. If the source path ends with a non-empty filename, and the appended path is empty, C++17 std::filesystem requires to add a trailing directory separator. --- doc/reference.html | 2 +- include/boost/filesystem/path.hpp | 40 ++++----- src/path.cpp | 8 ++ test/path_test.cpp | 139 ++++++++++++++---------------- test/path_unit_test.cpp | 4 + 5 files changed, 97 insertions(+), 96 deletions(-) diff --git a/doc/reference.html b/doc/reference.html index 57c8327..e805da2 100644 --- a/doc/reference.html +++ b/doc/reference.html @@ -1380,7 +1380,7 @@ path& append(const path& p);

v4: If p.is_absolute() || (p.has_root_name() && p.root_name() != root_name()), assigns p to *this. Otherwise, modifies *this as if by these steps:

diff --git a/include/boost/filesystem/path.hpp b/include/boost/filesystem/path.hpp index 94ba5d8..f37c8c9 100644 --- a/include/boost/filesystem/path.hpp +++ b/include/boost/filesystem/path.hpp @@ -520,10 +520,11 @@ public: typename boost::disable_if< path_detail::is_native_char_ptr< InputIterator >, path& >::type concat(InputIterator begin, InputIterator end) { - if (begin == end) - return *this; - std::basic_string< typename std::iterator_traits< InputIterator >::value_type > seq(begin, end); - path_traits::convert(seq.c_str(), seq.c_str() + seq.size(), m_pathname); + if (begin != end) + { + std::basic_string< typename std::iterator_traits< InputIterator >::value_type > seq(begin, end); + path_traits::convert(seq.c_str(), seq.c_str() + seq.size(), m_pathname); + } return *this; } @@ -537,10 +538,11 @@ public: typename boost::disable_if< path_detail::is_native_char_ptr< InputIterator >, path& >::type concat(InputIterator begin, InputIterator end, codecvt_type const& cvt) { - if (begin == end) - return *this; - std::basic_string< typename std::iterator_traits< InputIterator >::value_type > seq(begin, end); - path_traits::convert(seq.c_str(), seq.c_str() + seq.size(), m_pathname, cvt); + if (begin != end) + { + std::basic_string< typename std::iterator_traits< InputIterator >::value_type > seq(begin, end); + path_traits::convert(seq.c_str(), seq.c_str() + seq.size(), m_pathname, cvt); + } return *this; } @@ -582,8 +584,6 @@ public: path& >::type append(Source const& source) { - if (path_traits::empty(source)) - return *this; path p; path_traits::dispatch(source, p.m_pathname); return append(p); @@ -613,8 +613,6 @@ public: path& >::type append(Source const& source, codecvt_type const& cvt) { - if (path_traits::empty(source)) - return *this; path p; path_traits::dispatch(source, p.m_pathname, cvt); return append(p); @@ -630,11 +628,12 @@ public: typename boost::disable_if< path_detail::is_native_char_ptr< InputIterator >, path& >::type append(InputIterator begin, InputIterator end) { - if (begin == end) - return *this; - std::basic_string< typename std::iterator_traits< InputIterator >::value_type > seq(begin, end); path p; - path_traits::convert(seq.c_str(), seq.c_str() + seq.size(), p.m_pathname); + if (begin != end) + { + std::basic_string< typename std::iterator_traits< InputIterator >::value_type > seq(begin, end); + path_traits::convert(seq.c_str(), seq.c_str() + seq.size(), p.m_pathname); + } return append(p); } @@ -648,11 +647,12 @@ public: typename boost::disable_if< path_detail::is_native_char_ptr< InputIterator >, path& >::type append(InputIterator begin, InputIterator end, const codecvt_type& cvt) { - if (begin == end) - return *this; - std::basic_string< typename std::iterator_traits< InputIterator >::value_type > seq(begin, end); path p; - path_traits::convert(seq.c_str(), seq.c_str() + seq.size(), p.m_pathname, cvt); + if (begin != end) + { + std::basic_string< typename std::iterator_traits< InputIterator >::value_type > seq(begin, end); + path_traits::convert(seq.c_str(), seq.c_str() + seq.size(), p.m_pathname, cvt); + } return append(p); } diff --git a/src/path.cpp b/src/path.cpp index 78f6388..b59c54d 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -242,6 +242,10 @@ BOOST_FILESYSTEM_DECL void path::append_v4(path const& p) append_v4(rhs); } } + else if (has_filename_v4()) + { + m_pathname.push_back(preferred_separator); + } } BOOST_FILESYSTEM_DECL void path::append_v4(const value_type* begin, const value_type* end) @@ -298,6 +302,10 @@ BOOST_FILESYSTEM_DECL void path::append_v4(const value_type* begin, const value_ append_v4(rhs); } } + else if (has_filename_v4()) + { + m_pathname.push_back(preferred_separator); + } } #ifdef BOOST_WINDOWS_API diff --git a/test/path_test.cpp b/test/path_test.cpp index e9e8986..9e610b6 100644 --- a/test/path_test.cpp +++ b/test/path_test.cpp @@ -590,11 +590,15 @@ void non_member_tests() BOOST_TEST((b / as).native() == path("b\\a").native()); BOOST_TEST((b / acs).native() == path("b\\a").native()); PATH_TEST_EQ(path("a") / "b", "a\\b"); - PATH_TEST_EQ(path("..") / "", ".."); PATH_TEST_EQ(path("foo") / path("bar"), "foo\\bar"); // path arg PATH_TEST_EQ(path("foo") / "bar", "foo\\bar"); // const char* arg PATH_TEST_EQ(path("foo") / path("woo/bar").filename(), "foo\\bar"); // const std::string & arg PATH_TEST_EQ("foo" / path("bar"), "foo\\bar"); +#if BOOST_FILESYSTEM_VERSION == 3 + PATH_TEST_EQ(path("..") / "", ".."); +#else + PATH_TEST_EQ(path("..") / "", "..\\"); +#endif PATH_TEST_EQ(path("..") / "..", "..\\.."); PATH_TEST_EQ(path("/") / "..", "/.."); PATH_TEST_EQ(path("/..") / "..", "/..\\.."); @@ -631,11 +635,13 @@ void non_member_tests() #if BOOST_FILESYSTEM_VERSION == 3 PATH_TEST_EQ(path("\\\\net1\\foo") / "\\\\net2\\bar", "\\\\net1\\foo\\\\net2\\bar"); + PATH_TEST_EQ(path("\\\\net1\\foo") / "\\bar", "\\\\net1\\foo\\bar"); PATH_TEST_EQ(path("c:\\foo") / "d:\\bar", "c:\\foo\\d:\\bar"); PATH_TEST_EQ(path("c:\\foo") / "\\bar", "c:\\foo\\bar"); PATH_TEST_EQ(path("c:foo") / "\\bar", "c:foo\\bar"); #else PATH_TEST_EQ(path("\\\\net1\\foo") / "\\\\net2\\bar", "\\\\net2\\bar"); + PATH_TEST_EQ(path("\\\\net1\\foo") / "\\bar", "\\\\net1\\bar"); PATH_TEST_EQ(path("c:\\foo") / "d:\\bar", "d:\\bar"); PATH_TEST_EQ(path("c:\\foo") / "\\bar", "c:\\bar"); PATH_TEST_EQ(path("c:foo") / "\\bar", "c:\\bar"); @@ -650,12 +656,16 @@ void non_member_tests() PATH_TEST_EQ(b / as, "b/a"); PATH_TEST_EQ(b / acs, "b/a"); PATH_TEST_EQ(path("a") / "b", "a/b"); - PATH_TEST_EQ(path("..") / "", ".."); PATH_TEST_EQ(path("") / "..", ".."); PATH_TEST_EQ(path("foo") / path("bar"), "foo/bar"); // path arg PATH_TEST_EQ(path("foo") / "bar", "foo/bar"); // const char* arg PATH_TEST_EQ(path("foo") / path("woo/bar").filename(), "foo/bar"); // const std::string & arg PATH_TEST_EQ("foo" / path("bar"), "foo/bar"); +#if BOOST_FILESYSTEM_VERSION == 3 + PATH_TEST_EQ(path("..") / "", ".."); +#else + PATH_TEST_EQ(path("..") / "", "../"); +#endif PATH_TEST_EQ(path("..") / "..", "../.."); PATH_TEST_EQ(path("/") / "..", "/.."); PATH_TEST_EQ(path("/..") / "..", "/../.."); @@ -2037,15 +2047,21 @@ void construction_tests() // append_tests --------------------------------------------------------------------// -void append_test_aux(const path& p, const std::string& s, const std::string& expect) -{ - PATH_TEST_EQ((p / path(s)).string(), expect); - PATH_TEST_EQ((p / s.c_str()).string(), expect); - PATH_TEST_EQ((p / s).string(), expect); - path x(p); - x.append(s.begin(), s.end()); - PATH_TEST_EQ(x.string(), expect); -} +#define APPEND_TEST(pth, appnd, expected)\ + {\ + const path p(pth);\ + const std::string s(appnd);\ + PATH_TEST_EQ(p / appnd, expected);\ + PATH_TEST_EQ((p / path(s)).string(), expected);\ + PATH_TEST_EQ((p / s.c_str()).string(), expected);\ + PATH_TEST_EQ((p / s).string(), expected);\ + path p1(p);\ + p1 /= appnd;\ + PATH_TEST_EQ(p1, expected);\ + path p2(p);\ + p2.append(s.begin(), s.end());\ + PATH_TEST_EQ(p2.string(), expected);\ + } void append_tests() { @@ -2068,100 +2084,73 @@ void append_tests() //for (int i = 0; i < sizeof(x)/sizeof(char*); ++i) // for (int j = 0; j < sizeof(y)/sizeof(char*); ++j) // { - // std::cout << "\n PATH_TEST_EQ(path(\"" << x[i] << "\") / \"" << y[j] << "\", \"" - // << path(x[i]) / y[j] << "\");\n"; - // std::cout << " append_test_aux(\"" << x[i] << "\", \"" << y[j] << "\", \"" + // std::cout << " APPEND_TEST(\"" << x[i] << "\", \"" << y[j] << "\", \"" // << path(x[i]) / y[j] << "\");\n"; // } - PATH_TEST_EQ(path("") / "", ""); - append_test_aux("", "", ""); - - PATH_TEST_EQ(path("") / "/", "/"); - append_test_aux("", "/", "/"); - - PATH_TEST_EQ(path("") / "bar", "bar"); - append_test_aux("", "bar", "bar"); - - PATH_TEST_EQ(path("") / "/bar", "/bar"); - append_test_aux("", "/bar", "/bar"); - - PATH_TEST_EQ(path("/") / "", "/"); - append_test_aux("/", "", "/"); + APPEND_TEST("", "", ""); + APPEND_TEST("", "/", "/"); + APPEND_TEST("", "bar", "bar"); + APPEND_TEST("", "/bar", "/bar"); + APPEND_TEST("/", "", "/"); #if BOOST_FILESYSTEM_VERSION == 3 - PATH_TEST_EQ(path("/") / "/", "//"); - append_test_aux("/", "/", "//"); + APPEND_TEST("/", "/", "//"); #else - PATH_TEST_EQ(path("/") / "/", "/"); - append_test_aux("/", "/", "/"); + APPEND_TEST("/", "/", "/"); #endif - - PATH_TEST_EQ(path("/") / "bar", "/bar"); - append_test_aux("/", "bar", "/bar"); - + APPEND_TEST("/", "bar", "/bar"); #if BOOST_FILESYSTEM_VERSION == 3 - PATH_TEST_EQ(path("/") / "/bar", "//bar"); - append_test_aux("/", "/bar", "//bar"); + APPEND_TEST("/", "/bar", "//bar"); #else - PATH_TEST_EQ(path("/") / "/bar", "/bar"); - append_test_aux("/", "/bar", "/bar"); -#endif - - PATH_TEST_EQ(path("foo") / "", "foo"); - append_test_aux("foo", "", "foo"); - -#if BOOST_FILESYSTEM_VERSION == 3 - PATH_TEST_EQ(path("foo") / "/", "foo/"); - append_test_aux("foo", "/", "foo/"); -#else - PATH_TEST_EQ(path("foo") / "/", "/"); - append_test_aux("foo", "/", "/"); + APPEND_TEST("/", "/bar", "/bar"); #endif #if BOOST_FILESYSTEM_VERSION == 3 - PATH_TEST_EQ(path("foo") / "/bar", "foo/bar"); - append_test_aux("foo", "/bar", "foo/bar"); + APPEND_TEST("foo", "/", "foo/"); #else - PATH_TEST_EQ(path("foo") / "/bar", "/bar"); - append_test_aux("foo", "/bar", "/bar"); + APPEND_TEST("foo", "/", "/"); #endif - - PATH_TEST_EQ(path("foo/") / "", "foo/"); - append_test_aux("foo/", "", "foo/"); - #if BOOST_FILESYSTEM_VERSION == 3 - PATH_TEST_EQ(path("foo/") / "/", "foo//"); - append_test_aux("foo/", "/", "foo//"); + APPEND_TEST("foo", "/bar", "foo/bar"); #else - PATH_TEST_EQ(path("foo/") / "/", "/"); - append_test_aux("foo/", "/", "/"); + APPEND_TEST("foo", "/bar", "/bar"); #endif - PATH_TEST_EQ(path("foo/") / "bar", "foo/bar"); - append_test_aux("foo/", "bar", "foo/bar"); + APPEND_TEST("foo/", "", "foo/"); +#if BOOST_FILESYSTEM_VERSION == 3 + APPEND_TEST("foo/", "/", "foo//"); +#else + APPEND_TEST("foo/", "/", "/"); +#endif + APPEND_TEST("foo/", "bar", "foo/bar"); if (platform == "Windows") { - PATH_TEST_EQ(path("foo") / "bar", "foo\\bar"); - append_test_aux("foo", "bar", "foo\\bar"); +#if BOOST_FILESYSTEM_VERSION == 3 + APPEND_TEST("foo", "", "foo"); +#else + APPEND_TEST("foo", "", "foo\\"); +#endif + APPEND_TEST("foo", "bar", "foo\\bar"); #if BOOST_FILESYSTEM_VERSION == 3 - PATH_TEST_EQ(path("foo\\") / "\\bar", "foo\\\\bar"); - append_test_aux("foo\\", "\\bar", "foo\\\\bar"); + APPEND_TEST("foo\\", "\\bar", "foo\\\\bar"); #else - PATH_TEST_EQ(path("foo\\") / "\\bar", "\\bar"); - append_test_aux("foo\\", "\\bar", "\\bar"); + APPEND_TEST("foo\\", "\\bar", "\\bar"); #endif // hand created test case specific to Windows - PATH_TEST_EQ(path("c:") / "bar", "c:bar"); - append_test_aux("c:", "bar", "c:bar"); + APPEND_TEST("c:", "bar", "c:bar"); } else { - PATH_TEST_EQ(path("foo") / "bar", "foo/bar"); - append_test_aux("foo", "bar", "foo/bar"); +#if BOOST_FILESYSTEM_VERSION == 3 + APPEND_TEST("foo", "", "foo"); +#else + APPEND_TEST("foo", "", "foo/"); +#endif + APPEND_TEST("foo", "bar", "foo/bar"); } // ticket #6819 diff --git a/test/path_unit_test.cpp b/test/path_unit_test.cpp index e31d207..09d69da 100644 --- a/test/path_unit_test.cpp +++ b/test/path_unit_test.cpp @@ -321,7 +321,11 @@ void test_appends() x = "/foo"; x /= path(""); // empty path +#if BOOST_FILESYSTEM_VERSION == 3 PATH_IS(x, L"/foo"); +#else + PATH_IS(x, L"/foo/"); +#endif x = "/foo"; x /= path("/"); // slash path