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.
This commit is contained in:
Andrey Semashev
2021-11-05 20:27:02 +03:00
parent 37bfbbb376
commit 0aee13c162
5 changed files with 97 additions and 96 deletions

View File

@@ -1380,7 +1380,7 @@ path&amp; append(const path&amp; p);</pre>
<p><b>v4:</b> If <code>p.<a href="#path-is_absolute">is_absolute</a>() || (p.<a href="#path-has_root_name">has_root_name</a>() &amp;&amp; p.<a href="#path-root_name">root_name</a>() != <a href="#path-root_name">root_name</a>())</code>, assigns <code>p</code> to <code>*this</code>. Otherwise, modifies <code>*this</code> as if by these steps:
<ul>
<li>If <code>p.<a href="#path-has_root_name">has_root_directory</a>()</code>, removes root directory and relative path, if any.</li>
<li>Let <code>x</code> be a <code>path</code> with contents of <code>p</code> without a root name. If <code>*this</code> does not end with a directory separator and <code>x</code> does not start with one, appends <code>path::preferred_separator</code>.</li>
<li>Let <code>x</code> be a <code>path</code> with contents of <code>p</code> without a root name. If <code><a href="#path-has_filename">has_filename</a>()</code> is <code>true</code> and <code>x</code> does not start with a directory separator, appends <code>path::preferred_separator</code>.</li>
<li>Appends <code>x.native()</code>.</li>
</ul>
</p>

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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