Implemented root-aware path appending in v4.

In Boost.Filesystem v3 path appending mostly worked as a slight upgrade
of concatenation, where appending would only add directory separators
when necessary, but not consider semantics of the root name and root
directory of the appended paths. This would work well for relative paths,
but produce unexpected results for paths with root names.

In v4, we now implement appending that is aware of root name and directory
of the appendedn paths. This means that appending a path with a root name
and/or directory no longer concatenates the paths, but rather rebases the
appended path on top of the source path. In particular, if the appended path
has a root name different from the source path, the append operation will
act as assignment.

This is closer to C++20 std::filesystem but not exactly the same. The
difference is for the case when the appended path is absolute. The C++20
spec requires assignment in this case, Boost.Filesystem v4 deliberately
omits this check. This is to ensure the correct result for UNC paths on
POSIX systems, where "//net/foo" / "/bar" is expected to produce "//net/bar",
not "/bar".

As part of this work, refactored path constructors and operators for more
optimal implementation and reducing the number of overloads.

Closes https://github.com/boostorg/filesystem/issues/214.
This commit is contained in:
Andrey Semashev 2021-11-04 06:12:26 +03:00
parent 049e9aad94
commit d13461be0f
7 changed files with 598 additions and 240 deletions

View File

@ -1080,8 +1080,11 @@ system or for a particular file system.</p>
template &lt;class <a href="#Source">Source</a>&gt;
path&amp; operator=(Source const&amp; source);
path&amp; assign(const path&amp; p);
path&amp; assign(path&amp;&amp; p) noexcept;
template &lt;class <a href="#Source">Source</a>&gt;
path&amp; assign(Source const&amp; source, const codecvt_type&amp; cvt)
path&amp; assign(Source const&amp; source,
const codecvt_type&amp; cvt=codecvt())
template &lt;class <a href="#InputIterator">InputIterator</a>&gt;
path&amp; assign(InputIterator begin, InputIterator end,
@ -1093,8 +1096,10 @@ system or for a particular file system.</p>
template &lt;class <a href="#Source">Source</a>&gt;
path&amp; operator/=(Source const&amp; source);
path&amp; append(const path&amp; x);
template &lt;class <a href="#Source">Source</a>&gt;
path&amp; append(Source const&amp; source, const codecvt_type&amp; cvt);
path&amp; append(Source const&amp; source,
const codecvt_type&amp; cvt=codecvt());
template &lt;class <a href="#InputIterator">InputIterator</a>&gt;
path&amp; append(InputIterator begin, InputIterator end,
@ -1102,20 +1107,19 @@ system or for a particular file system.</p>
// <a href="#path-concatenation">concatenation</a>
path&amp; operator+=(const path&amp; x);
path&amp; operator+=(const string_type&amp; x);
path&amp; operator+=(const value_type* x);
template &lt;class <a href="#Source">Source</a>&gt;
path&amp; operator+=(Source const&amp; source);
path&amp; operator+=(value_type x);
template &lt;class Source&gt;
path&amp; operator+=(Source const&amp; x);
template &lt;class CharT&gt;
path&amp; operator+=(CharT x);
template &lt;class Source&gt;
path&amp; concat(Source const&amp; x, const codecvt_type&amp; cvt);
template &lt;class InputIterator&gt;
path&amp; concat(InputIterator begin, InputIterator end);
template &lt;class InputIterator&gt;
path&amp; concat(const path&amp; x);
template &lt;class <a href="#Source">Source</a>&gt;
path&amp; concat(Source const&amp; x,
const codecvt_type&amp; cvt=codecvt());
template &lt;class <a href="#InputIterator">InputIterator</a>&gt;
path&amp; concat(InputIterator begin, InputIterator end,
const codecvt_type&amp; cvt);
const codecvt_type&amp; cvt=codecvt());
// <a href="#path-modifiers">modifiers</a>
void <a href="#path-clear">clear</a>();
@ -1329,32 +1333,37 @@ requirements for a C++ standard library <code>RandomIterator</code> compliant it
<h3> <a name="path-assignments"> <code>
<font size="4">path</font></code> assignments</a> [path.assign]</h3>
<pre>template &lt;class <a href="#Source">Source</a>&gt;
path&amp; operator=(Source const&amp; source);
<pre>path&amp; operator=(const path&amp; p);
path&amp; operator=(path&amp;&amp; p) noexcept;
template &lt;class <a href="#Source">Source</a>&gt;
path&amp; assign(Source const&amp; source, const codecvt_type&amp; cvt);
path&amp; operator=(Source const&amp; source);
path&amp; assign(const path&amp; p);
path&amp; assign(path&amp;&amp; p) noexcept;
template &lt;class <a href="#Source">Source</a>&gt;
path&amp; assign(Source const&amp; source, const codecvt_type&amp; cvt=codecvt());
template &lt;class <a href="#InputIterator">InputIterator</a>&gt;
path&amp; assign(InputIterator begin, InputIterator end, const codecvt_type&amp; cvt=codecvt());</pre>
<blockquote>
<p><i>Effects:</i> Stores the contents [<code>begin</code>,<code>end</code>)
or <code>source</code> in <code>pathname</code>, converting format and
or <code>source</code> or <code>p</code> in <code>pathname</code>, converting format and
encoding if required ([<a href="#path.arg.convert">path.arg.convert</a>]). </p>
<p>
<i>Returns: </i><code>*this</code></p>
</blockquote>
<h3><a name="path-appends"><code><font size="4"> path</font></code> appends</a>
<h3><a name="path-appends"><code>path</code> appends</a>
[path.append]</h3>
<p>The append operations use <code>
operator/=</code> to denote their semantic effect of appending <i>
preferred-separator</i> when needed. </p>
<pre>path&amp; operator/=(const path&amp; p);</pre>
<pre>path&amp; operator/=(const path&amp; p);
path&amp; append(const path&amp; p);</pre>
<blockquote>
<p><i>Effects:</i></p>
<blockquote>
<p>Appends <code>path::preferred_separator</code> to <code>pathname</code>,
<p><b>v3:</b> Appends <code>path::preferred_separator</code> to <code>pathname</code>,
converting format and encoding if required ([<a href="#path.arg.convert">path.arg.convert</a>]), unless:</p>
<ul>
<li>
@ -1366,8 +1375,15 @@ requirements for a C++ standard library <code>RandomIterator</code> compliant it
<p><code>p.empty()</code>, or</li>
<li>
<p><code>*p.native().cbegin()</code> is a directory separator.</li>
</ul>
</ul>
<p>Then appends <code>p.native()</code> to <code>pathname</code>.</p>
<p><b>v4:</b> If <code>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>Appends <code>x.native()</code>.</li>
</ul>
</p>
</blockquote>
<p><i>Returns: </i><code>*this</code></p>
</blockquote>
@ -1375,53 +1391,37 @@ requirements for a C++ standard library <code>RandomIterator</code> compliant it
<pre>template &lt;class <a href="#Source">Source</a>&gt;
path&amp; operator/=(Source const &amp; source);
template &lt;class <a href="#Source">Source</a>&gt;
path&amp; append(Source const &amp; source, const codecvt_type&amp; cvt);
path&amp; append(Source const &amp; source, const codecvt_type&amp; cvt=codecvt());
template &lt;class <a href="#InputIterator">InputIterator</a>&gt;
path&amp; append(InputIterator begin, InputIterator end, const codecvt_type&amp; cvt=codecvt());</pre>
<blockquote>
<p><i>Effects:</i></p>
<blockquote>
<p>Appends <code>path::preferred_separator</code> to <code>pathname</code>, converting
format and encoding if required ([<a href="#path.arg.convert">path.arg.convert</a>]), unless:</p>
<ul>
<li>
<p>an added separator
would be redundant, or</li>
<li>
<p>would change a relative path to an absolute path, or</li>
<li>
<p><code>p.empty()</code>, or</li>
<li>
<p><code>*p.native().cbegin()</code> is a separator.</li>
</ul>
<p>Appends the contents [<code>begin</code>,<code>end</code>)
or <code>source</code> to <code>pathname</code>, converting format and
encoding if required ([<a href="#path.arg.convert">path.arg.convert</a>]).</p>
<p>As if <code>append(path(<i>args</i>))</code>, where <code><i>args</i></code> is the list of arguments passed to the operation.</p>
</blockquote>
<p><i>Returns: </i><code>*this</code></p>
</blockquote>
<h3><a name="path-concatenation"><code>path</code> concatenation</a> [path.concat]</h3>
<pre>path&amp; operator+=(const path&amp; x);
path&amp; operator+=(const string_type&amp; x);
path&amp; operator+=(const value_type* x);
path&amp; operator+=(value_type x);
template &lt;class Source&gt;
path&amp; operator+=(Source const&amp; x);
template &lt;class CharT&gt;
<pre>path&amp; operator+=(const path&amp; p);
path&amp; operator+=(value_type x);
template &lt;class <a href="#Source">Source</a>&gt;
path&amp; operator+=(Source const&amp; source);
template &lt;class CharT&gt;
path&amp; operator+=(CharT x);
template &lt;class InputIterator&gt;
path&amp; concat(InputIterator begin, InputIterator end);
template &lt;class InputIterator&gt;
path&amp; concat(InputIterator begin, InputIterator end, const codecvt_type&amp; cvt);</pre>
path&amp; concat(const path&amp; p);
template &lt;class <a href="#Source">Source</a>&gt;
path&amp; concat(Source const&amp; source, const codecvt_type&amp; cvt=codecvt());
template &lt;class <a href="#InputIterator">InputIterator</a>&gt;
path&amp; concat(InputIterator begin, InputIterator end, const codecvt_type&amp; cvt=codecvt());</pre>
<blockquote><p><i>Postcondition:</i> <code>native() == prior_native + <i>effective-argument</i></code>,
where <code>prior_native</code> is <code>native()</code> prior to the call to <code>operator+=</code>,
and <code><i>effective-argument</i></code> is:</p>
<ul><li>
<p><code>x.native()</code> if <code>x</code> is present and is <code>const path&amp;</code>, otherwise</li>
<p><code>p.native()</code> if <code>p</code> is present and is <code>const path&amp;</code>, otherwise</li>
<li>
<p><code>s</code>, where <code>s</code> is
<code>std::basic_string&lt;typename std::iterator_traits&lt;InputIterator&gt;::value_type&gt;<br>s(begin, end)</code>,

View File

@ -42,6 +42,10 @@
<h2>1.78.0</h2>
<ul>
<li><b>v4:</b> <code>path::filename</code> and <code>path::iterator</code> no longer return an implicit trailing dot (".") element if the path ends with a directory separator. Instead, an empty path is returned, similar to C++17 std::filesystem. This also affects other methods that are defined in terms of iterators or filename, such as <code>path::stem</code>, <code>path::compare</code> or <code>lexicographical_compare</code>. For example, <code>path("a/b/") == path("a/b/.")</code> no longer holds true. (<a href="https://github.com/boostorg/filesystem/issues/193">#193</a>)</li>
<li><b>v4:</b> <code>path</code> append operations now consider root name and root directory in the appended path. If 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>
<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><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>

View File

@ -50,6 +50,9 @@ It removes the features that were <a href="deprecated.html">deprecated</a> in Ve
other elements. For example, on Windows <code>path("C:").filename()</code> used to return "C:" and <code>path("C:\").filename()</code> used to return "\" and both will return an empty path now. This also affects <code>path::stem</code> and <code>path::extension</code> methods, as those are based on <code>path::filename</code>.</li>
<li><a href="reference.html#path-stem"><code>path::stem</code></a> and <a href="reference.html#path-extension"><code>path::extension</code></a> no longer treat a filename that starts with a dot and has no other dots as an extension. Filenames starting with a dot are commonly treated as filenames with an empty extension. The leading dot is used to indicate a hidden file on most UNIX-like systems.</li>
<li><a href="reference.html#path-filename"><code>path::filename</code></a> and <a href="reference.html#path-iterators"><code>path::iterator</code></a> no longer return an implicit trailing dot (".") element if the path ends with a directory separator. Instead, an empty path is returned, similar to C++17 std::filesystem. This also affects other methods that are defined in terms of iterators or filename, such as <code>path::stem</code>, <code>path::compare</code> or <code>lexicographical_compare</code>. For example, <code>path("a/b/") == path("a/b/.")</code> no longer holds true.</li>
<li><a href="reference.html#path-appends"><code>path</code> appends</a> consider root name and root directory of the appended path. If 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. The behavior is similar, but not exactly the same as C++17 std::filesystem. The difference is that Boost.Filesystem does not perform assignment if the appended path is absolute, as required by C++17. This is to support UNC paths on POSIX systems, so that <code>path("//net/foo") / "/bar"</code> results in <code>"//net/bar"</code>, not <code>"/bar"</code>.</li>
</ul>
<hr>

View File

@ -24,7 +24,11 @@
#include <boost/core/enable_if.hpp>
#include <boost/io/quoted.hpp>
#include <boost/functional/hash_fwd.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/type_traits/decay.hpp>
#include <cstddef>
#include <cwchar> // for mbstate_t
#include <string>
@ -37,8 +41,10 @@
namespace boost {
namespace filesystem {
namespace path_detail // intentionally don't use filesystem::detail to not bring internal Boost.Filesystem functions into ADL via path_constants
{
class path;
namespace path_detail { // intentionally don't use filesystem::detail to not bring internal Boost.Filesystem functions into ADL via path_constants
template< typename Char, Char Separator, Char PreferredSeparator, Char Dot >
struct path_constants
@ -69,6 +75,51 @@ struct substring
std::size_t size;
};
template< typename T >
struct is_native_char_ptr_impl : public boost::false_type {};
#if defined(BOOST_WINDOWS_API)
template< >
struct is_native_char_ptr_impl< wchar_t* > : public boost::true_type {};
template< >
struct is_native_char_ptr_impl< const wchar_t* > : public boost::true_type {};
#else // defined(BOOST_WINDOWS_API)
template< >
struct is_native_char_ptr_impl< char* > : public boost::true_type {};
template< >
struct is_native_char_ptr_impl< const char* > : public boost::true_type {};
#endif // defined(BOOST_WINDOWS_API)
template< typename T >
struct is_native_char_ptr : public is_native_char_ptr_impl< typename boost::remove_cv< typename boost::remove_reference< T >::type >::type > {};
template< typename T >
struct is_native_pathable_impl : public boost::false_type {};
template< typename T >
struct is_native_pathable_impl< T* > : public is_native_char_ptr_impl< T* > {};
#if defined(BOOST_WINDOWS_API)
template< >
struct is_native_pathable_impl< const wchar_t[] > : public boost::true_type {};
template< std::size_t N >
struct is_native_pathable_impl< const wchar_t[N] > : public boost::true_type {};
template< >
struct is_native_pathable_impl< std::basic_string< wchar_t > > : public boost::true_type {};
#else // defined(BOOST_WINDOWS_API)
template< >
struct is_native_pathable_impl< const char[] > : public boost::true_type {};
template< std::size_t N >
struct is_native_pathable_impl< const char[N] > : public boost::true_type {};
template< >
struct is_native_pathable_impl< std::basic_string< char > > : public boost::true_type {};
#endif // defined(BOOST_WINDOWS_API)
template< >
struct is_native_pathable_impl< filesystem::path > : public boost::true_type {};
template< typename T >
struct is_native_pathable : public is_native_pathable_impl< typename boost::remove_cv< typename boost::remove_reference< T >::type >::type > {};
} // namespace path_detail
//------------------------------------------------------------------------------------//
@ -155,15 +206,15 @@ public:
path(path const& p) : m_pathname(p.m_pathname) {}
template< class Source >
path(Source const& source, typename boost::enable_if< path_traits::is_pathable< typename boost::decay< Source >::type > >::type* = 0)
path(Source const& source, typename boost::enable_if_c<
path_traits::is_pathable< typename boost::decay< Source >::type >::value && !path_detail::is_native_pathable< Source >::value
>::type* = 0)
{
path_traits::dispatch(source, m_pathname);
}
path(const value_type* s) : m_pathname(s) {}
path(value_type* s) : m_pathname(s) {}
path(string_type const& s) : m_pathname(s) {}
path(string_type& s) : m_pathname(s) {}
// As of October 2015 the interaction between noexcept and =default is so troublesome
// for VC++, GCC, and probably other compilers, that =default is not used with noexcept
@ -173,30 +224,64 @@ public:
path(path&& p) BOOST_NOEXCEPT : m_pathname(std::move(p.m_pathname))
{
}
path(path&& p, codecvt_type const&) BOOST_NOEXCEPT : m_pathname(std::move(p.m_pathname))
{
}
path& operator=(path&& p) BOOST_NOEXCEPT
{
m_pathname = std::move(p.m_pathname);
return *this;
}
path& assign(path&& p) BOOST_NOEXCEPT
{
m_pathname = std::move(p.m_pathname);
return *this;
}
path& assign(path&& p, codecvt_type const&) BOOST_NOEXCEPT
{
m_pathname = std::move(p.m_pathname);
return *this;
}
path(string_type&& s) BOOST_NOEXCEPT : m_pathname(std::move(s))
{
}
path(string_type&& s, codecvt_type const&) BOOST_NOEXCEPT : m_pathname(std::move(s))
{
}
path& operator=(string_type&& p) BOOST_NOEXCEPT
{
m_pathname = std::move(p);
return *this;
}
path& assign(string_type&& p) BOOST_NOEXCEPT
{
m_pathname = std::move(p);
return *this;
}
path& assign(string_type&& p, codecvt_type const&) BOOST_NOEXCEPT
{
m_pathname = std::move(p);
return *this;
}
#endif
path(path const& p, codecvt_type const&) : m_pathname(p.m_pathname) {}
path(const value_type* s, codecvt_type const&) : m_pathname(s) {}
path(string_type const& s, codecvt_type const&) : m_pathname(s) {}
template< class Source >
path(Source const& source, codecvt_type const& cvt)
path(Source const& source, codecvt_type const& cvt, typename boost::enable_if_c<
path_traits::is_pathable< typename boost::decay< Source >::type >::value && !path_detail::is_native_pathable< Source >::value
>::type* = 0)
{
path_traits::dispatch(source, m_pathname, cvt);
}
path(const value_type* begin, const value_type* end) : m_pathname(begin, end) {}
template< class InputIterator >
path(InputIterator begin, InputIterator end)
path(InputIterator begin, InputIterator end, typename boost::disable_if< path_detail::is_native_char_ptr< InputIterator > >::type* = 0)
{
if (begin != end)
{
@ -206,8 +291,10 @@ public:
}
}
path(const value_type* begin, const value_type* end, codecvt_type const&) : m_pathname(begin, end) {}
template< class InputIterator >
path(InputIterator begin, InputIterator end, codecvt_type const& cvt)
path(InputIterator begin, InputIterator end, codecvt_type const& cvt, typename boost::disable_if< path_detail::is_native_char_ptr< InputIterator > >::type* = 0)
{
if (begin != end)
{
@ -219,63 +306,98 @@ public:
// ----- assignments -----
// We need to explicitly define copy assignment as otherwise it will be implicitly defined as deleted because there is move assignment
path& operator=(path const& p)
{
return assign(p);
}
path& operator=(string_type const& s)
{
return assign(s);
}
path& operator=(const value_type* ptr)
{
return assign(ptr);
}
template< class Source >
typename boost::enable_if_c<
path_traits::is_pathable< typename boost::decay< Source >::type >::value && !path_detail::is_native_pathable< Source >::value,
path&
>::type operator=(Source const& source)
{
return assign(source);
}
path& assign(path const& p)
{
m_pathname = p.m_pathname;
return *this;
}
path& assign(string_type const& s)
{
m_pathname = s;
return *this;
}
path& assign(const value_type* ptr)
{
m_pathname = ptr;
return *this;
}
template< class Source >
typename boost::enable_if< path_traits::is_pathable< typename boost::decay< Source >::type >, path& >::type
operator=(Source const& source)
typename boost::enable_if_c<
path_traits::is_pathable< typename boost::decay< Source >::type >::value && !path_detail::is_native_pathable< Source >::value,
path&
>::type assign(Source const& source)
{
m_pathname.clear();
path_traits::dispatch(source, m_pathname);
return *this;
}
// value_type overloads
path& operator=(const value_type* ptr) // required in case ptr overlaps *this
path& assign(path const& p, codecvt_type const&)
{
m_pathname = ptr;
m_pathname = p.m_pathname;
return *this;
}
path& operator=(value_type* ptr) // required in case ptr overlaps *this
{
m_pathname = ptr;
return *this;
}
path& operator=(string_type const& s)
path& assign(string_type const& s, codecvt_type const&)
{
m_pathname = s;
return *this;
}
path& operator=(string_type& s)
{
m_pathname = s;
return *this;
}
path& assign(const value_type* ptr, codecvt_type const&) // required in case ptr overlaps *this
path& assign(const value_type* ptr, codecvt_type const&)
{
m_pathname = ptr;
return *this;
}
template< class Source >
path& assign(Source const& source, codecvt_type const& cvt)
typename boost::enable_if_c<
path_traits::is_pathable< typename boost::decay< Source >::type >::value && !path_detail::is_native_pathable< Source >::value,
path&
>::type assign(Source const& source, codecvt_type const& cvt)
{
m_pathname.clear();
path_traits::dispatch(source, m_pathname, cvt);
return *this;
}
path& assign(const value_type* begin, const value_type* end)
{
m_pathname.assign(begin, end);
return *this;
}
template< class InputIterator >
path& assign(InputIterator begin, InputIterator end)
typename boost::disable_if< path_detail::is_native_char_ptr< InputIterator >, path& >::type
assign(InputIterator begin, InputIterator end)
{
m_pathname.clear();
if (begin != end)
@ -286,8 +408,15 @@ public:
return *this;
}
path& assign(const value_type* begin, const value_type* end, codecvt_type const&)
{
m_pathname.assign(begin, end);
return *this;
}
template< class InputIterator >
path& assign(InputIterator begin, InputIterator end, codecvt_type const& cvt)
typename boost::disable_if< path_detail::is_native_char_ptr< InputIterator >, path& >::type
assign(InputIterator begin, InputIterator end, codecvt_type const& cvt)
{
m_pathname.clear();
if (begin != end)
@ -301,42 +430,14 @@ public:
// ----- concatenation -----
template< class Source >
typename boost::enable_if< path_traits::is_pathable< typename boost::decay< Source >::type >, path& >::type
operator+=(Source const& source)
typename boost::enable_if_c<
path_traits::is_pathable< typename boost::decay< Source >::type >::value || path_detail::is_native_pathable< Source >::value,
path&
>::type operator+=(Source const& source)
{
return concat(source);
}
// value_type overloads. Same rationale as for constructors above
path& operator+=(path const& p)
{
m_pathname += p.m_pathname;
return *this;
}
path& operator+=(const value_type* ptr)
{
m_pathname += ptr;
return *this;
}
path& operator+=(value_type* ptr)
{
m_pathname += ptr;
return *this;
}
path& operator+=(string_type const& s)
{
m_pathname += s;
return *this;
}
path& operator+=(string_type& s)
{
m_pathname += s;
return *this;
}
path& operator+=(value_type c)
{
m_pathname += c;
@ -349,26 +450,75 @@ public:
{
CharT tmp[2];
tmp[0] = c;
tmp[1] = 0;
tmp[1] = static_cast< CharT >(0);
return concat(tmp);
}
path& concat(path const& p)
{
m_pathname += p.m_pathname;
return *this;
}
path& concat(const value_type* ptr)
{
m_pathname += ptr;
return *this;
}
path& concat(string_type const& s)
{
m_pathname += s;
return *this;
}
template< class Source >
path& concat(Source const& source)
typename boost::enable_if_c<
path_traits::is_pathable< typename boost::decay< Source >::type >::value && !path_detail::is_native_pathable< Source >::value,
path&
>::type concat(Source const& source)
{
path_traits::dispatch(source, m_pathname);
return *this;
}
path& concat(path const& p, codecvt_type const&)
{
m_pathname += p.m_pathname;
return *this;
}
path& concat(const value_type* ptr, codecvt_type const&)
{
m_pathname += ptr;
return *this;
}
path& concat(string_type const& s, codecvt_type const&)
{
m_pathname += s;
return *this;
}
template< class Source >
path& concat(Source const& source, codecvt_type const& cvt)
typename boost::enable_if_c<
path_traits::is_pathable< typename boost::decay< Source >::type >::value && !path_detail::is_native_pathable< Source >::value,
path&
>::type concat(Source const& source, codecvt_type const& cvt)
{
path_traits::dispatch(source, m_pathname, cvt);
return *this;
}
path& concat(const value_type* begin, const value_type* end)
{
m_pathname.append(begin, end);
return *this;
}
template< class InputIterator >
path& concat(InputIterator begin, InputIterator end)
typename boost::disable_if< path_detail::is_native_char_ptr< InputIterator >, path& >::type
concat(InputIterator begin, InputIterator end)
{
if (begin == end)
return *this;
@ -377,8 +527,15 @@ public:
return *this;
}
path& concat(const value_type* begin, const value_type* end, codecvt_type const&)
{
m_pathname.append(begin, end);
return *this;
}
template< class InputIterator >
path& concat(InputIterator begin, InputIterator end, codecvt_type const& cvt)
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;
@ -392,46 +549,112 @@ public:
// if a separator is added, it is the preferred separator for the platform;
// slash for POSIX, backslash for Windows
BOOST_FILESYSTEM_DECL path& operator/=(path const& p);
template< class Source >
typename boost::enable_if< path_traits::is_pathable< typename boost::decay< Source >::type >, path& >::type
operator/=(Source const& source)
BOOST_FORCEINLINE typename boost::enable_if_c<
path_traits::is_pathable< typename boost::decay< Source >::type >::value || path_detail::is_native_pathable< Source >::value,
path&
>::type operator/=(Source const& source)
{
return append(source);
}
BOOST_FILESYSTEM_DECL path& operator/=(const value_type* ptr);
path& operator/=(value_type* ptr)
BOOST_FORCEINLINE path& append(path const& p)
{
return this->operator/=(const_cast< const value_type* >(ptr));
}
path& operator/=(string_type const& s) { return this->operator/=(path(s)); }
path& operator/=(string_type& s) { return this->operator/=(path(s)); }
path& append(const value_type* ptr) // required in case ptr overlaps *this
{
this->operator/=(ptr);
BOOST_FILESYSTEM_VERSIONED_SYM(append)(p);
return *this;
}
path& append(const value_type* ptr, codecvt_type const&) // required in case ptr overlaps *this
BOOST_FORCEINLINE path& append(string_type const& p)
{
this->operator/=(ptr);
BOOST_FILESYSTEM_VERSIONED_SYM(append)(p.c_str(), p.c_str() + p.size());
return *this;
}
BOOST_FORCEINLINE path& append(const value_type* ptr)
{
BOOST_FILESYSTEM_VERSIONED_SYM(append)(ptr, ptr + string_type::traits_type::length(ptr));
return *this;
}
template< class Source >
path& append(Source const& source);
typename boost::enable_if_c<
path_traits::is_pathable< typename boost::decay< Source >::type >::value && !path_detail::is_native_pathable< Source >::value,
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);
}
BOOST_FORCEINLINE path& append(path const& p, codecvt_type const&)
{
BOOST_FILESYSTEM_VERSIONED_SYM(append)(p);
return *this;
}
BOOST_FORCEINLINE path& append(string_type const& p, codecvt_type const&)
{
BOOST_FILESYSTEM_VERSIONED_SYM(append)(p.c_str(), p.c_str() + p.size());
return *this;
}
BOOST_FORCEINLINE path& append(const value_type* ptr, codecvt_type const&)
{
BOOST_FILESYSTEM_VERSIONED_SYM(append)(ptr, ptr + string_type::traits_type::length(ptr));
return *this;
}
template< class Source >
path& append(Source const& source, codecvt_type const& cvt);
typename boost::enable_if_c<
path_traits::is_pathable< typename boost::decay< Source >::type >::value && !path_detail::is_native_pathable< Source >::value,
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);
}
BOOST_FORCEINLINE path& append(const value_type* begin, const value_type* end)
{
BOOST_FILESYSTEM_VERSIONED_SYM(append)(begin, end);
return *this;
}
template< class InputIterator >
path& append(InputIterator begin, InputIterator end);
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);
return append(p);
}
BOOST_FORCEINLINE path& append(const value_type* begin, const value_type* end, codecvt_type const&)
{
BOOST_FILESYSTEM_VERSIONED_SYM(append)(begin, end);
return *this;
}
template< class InputIterator >
path& append(InputIterator begin, InputIterator end, const codecvt_type& cvt);
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);
return append(p);
}
// ----- modifiers -----
@ -691,6 +914,11 @@ private:
BOOST_FILESYSTEM_DECL int compare_v3(path const& p) const BOOST_NOEXCEPT;
BOOST_FILESYSTEM_DECL int compare_v4(path const& p) const BOOST_NOEXCEPT;
BOOST_FILESYSTEM_DECL void append_v3(const value_type* b, const value_type* e);
BOOST_FILESYSTEM_DECL void append_v4(const value_type* b, const value_type* e);
BOOST_FILESYSTEM_DECL void append_v3(path const& p);
BOOST_FILESYSTEM_DECL void append_v4(path const& p);
// Returns: If separator is to be appended, m_pathname.size() before append. Otherwise 0.
// Note: An append is never performed if size()==0, so a returned 0 is unambiguous.
BOOST_FILESYSTEM_DECL string_type::size_type append_separator_if_needed();
@ -1021,60 +1249,6 @@ inline bool path::filename_is_dot_dot() const
// to deal with "c:.." edge case on Windows when ':' acts as a separator
}
//--------------------------------------------------------------------------------------//
// class path member template implementation //
//--------------------------------------------------------------------------------------//
template< class InputIterator >
path& path::append(InputIterator begin, InputIterator end)
{
if (begin == end)
return *this;
string_type::size_type sep_pos = append_separator_if_needed();
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 (sep_pos)
erase_redundant_separator(sep_pos);
return *this;
}
template< class InputIterator >
path& path::append(InputIterator begin, InputIterator end, codecvt_type const& cvt)
{
if (begin == end)
return *this;
string_type::size_type sep_pos = append_separator_if_needed();
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 (sep_pos)
erase_redundant_separator(sep_pos);
return *this;
}
template< class Source >
path& path::append(Source const& source)
{
if (path_traits::empty(source))
return *this;
string_type::size_type sep_pos = append_separator_if_needed();
path_traits::dispatch(source, m_pathname);
if (sep_pos)
erase_redundant_separator(sep_pos);
return *this;
}
template< class Source >
path& path::append(Source const& source, codecvt_type const& cvt)
{
if (path_traits::empty(source))
return *this;
string_type::size_type sep_pos = append_separator_if_needed();
path_traits::dispatch(source, m_pathname, cvt);
if (sep_pos)
erase_redundant_separator(sep_pos);
return *this;
}
//--------------------------------------------------------------------------------------//
// class path member template specializations //
//--------------------------------------------------------------------------------------//

View File

@ -18,6 +18,7 @@
#include <algorithm>
#include <iterator>
#include <utility>
#include <string>
#include <cstddef>
#include <cstring>
#include <cstdlib> // std::atexit
@ -41,10 +42,6 @@
namespace fs = boost::filesystem;
using boost::filesystem::path;
using std::string;
using std::wstring;
using boost::system::error_code;
//--------------------------------------------------------------------------------------//
@ -92,12 +89,35 @@ inline bool is_device_name_char(wchar_t c)
return is_alnum(c) || c == L'$';
}
//! Returns position of the first directory separator in the \a size initial characters of \a p, or \a size if not found
inline size_type find_first_separator(const wchar_t* p, size_type size) BOOST_NOEXCEPT
{
size_type pos = 0u;
for (; pos < size; ++pos)
{
const wchar_t c = p[pos];
if (c == '\\' || c == '/')
break;
}
return pos;
}
#else // BOOST_WINDOWS_API
const char dot_path_literal[] = ".";
const char dot_dot_path_literal[] = "..";
const char separators[] = "/";
//! Returns position of the first directory separator in the \a size initial characters of \a p, or \a size if not found
inline size_type find_first_separator(const char* p, size_type size) BOOST_NOEXCEPT
{
const char* sep = static_cast< const char* >(std::memchr(p, '/', size));
size_type pos = size;
if (BOOST_LIKELY(!!sep))
pos = sep - p;
return pos;
}
#endif // BOOST_WINDOWS_API
// pos is position of the separator
@ -108,7 +128,7 @@ size_type find_filename_size(string_type const& str, size_type root_name_size, s
// Returns: starting position of root directory or size if not found. Sets root_name_size to length
// of the root name if the characters before the returned position (if any) are considered a root name.
size_type find_root_directory_start(string_type const& path, size_type size, size_type& root_name_size);
size_type find_root_directory_start(const value_type* path, size_type size, size_type& root_name_size);
// Finds position and size of the first element of the path
void first_element(string_type const& src, size_type& element_pos, size_type& element_size, size_type size);
@ -130,48 +150,134 @@ inline void first_element(string_type const& src, size_type& element_pos, size_t
namespace boost {
namespace filesystem {
BOOST_FILESYSTEM_DECL path& path::operator/=(path const& p)
BOOST_FILESYSTEM_DECL void path::append_v3(path const& p)
{
if (!p.empty())
{
if (this == &p) // self-append
{
path rhs(p);
if (!detail::is_directory_separator(rhs.m_pathname[0]))
append_separator_if_needed();
m_pathname += rhs.m_pathname;
}
else
if (BOOST_LIKELY(this != &p))
{
if (!detail::is_directory_separator(*p.m_pathname.begin()))
append_separator_if_needed();
m_pathname += p.m_pathname;
}
else
{
// self-append
path rhs(p);
append_v3(rhs);
}
}
return *this;
}
BOOST_FILESYSTEM_DECL path& path::operator/=(const value_type* ptr)
BOOST_FILESYSTEM_DECL void path::append_v3(const value_type* begin, const value_type* end)
{
if (*ptr != static_cast< value_type >('\0'))
if (begin != end)
{
if (ptr >= m_pathname.data() && ptr < m_pathname.data() + m_pathname.size()) // overlapping source
if (BOOST_LIKELY(begin < m_pathname.data() || begin >= (m_pathname.data() + m_pathname.size())))
{
path rhs(ptr);
if (!detail::is_directory_separator(rhs.m_pathname[0]))
if (!detail::is_directory_separator(*begin))
append_separator_if_needed();
m_pathname += rhs.m_pathname;
m_pathname.append(begin, end);
}
else
{
if (!detail::is_directory_separator(*ptr))
append_separator_if_needed();
m_pathname += ptr;
// overlapping source
path rhs(begin, end);
append_v3(rhs);
}
}
}
return *this;
BOOST_FILESYSTEM_DECL void path::append_v4(path const& p)
{
if (!p.empty())
{
if (BOOST_LIKELY(this != &p))
{
const size_type that_size = p.m_pathname.size();
size_type that_root_name_size = 0;
size_type that_root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), that_size, that_root_name_size);
size_type this_root_name_size = 0;
find_root_directory_start(m_pathname.c_str(), m_pathname.size(), this_root_name_size);
// Unlike C++20 std::filesystem, do not assign p if it is absolute. This breaks support for UNC paths on POSIX,
// where `path("//net/foo") / "/bar"` would result in "/bar" instead of "//net/bar".
if
(
that_root_name_size > 0 &&
(that_root_name_size != this_root_name_size || std::memcmp(m_pathname.c_str(), p.m_pathname.c_str(), this_root_name_size * sizeof(value_type)) != 0)
)
{
operator=(p);
return;
}
if (that_root_dir_pos < that_size)
{
// Remove root directory (if any) and relative path to replace with those from p
m_pathname.erase(m_pathname.begin() + this_root_name_size, m_pathname.end());
}
const value_type* const that_path = p.m_pathname.c_str() + that_root_name_size;
if (!detail::is_directory_separator(*that_path))
append_separator_if_needed();
m_pathname.append(that_path, that_size - that_root_name_size);
}
else
{
// self-append
path rhs(p);
append_v4(rhs);
}
}
}
BOOST_FILESYSTEM_DECL void path::append_v4(const value_type* begin, const value_type* end)
{
if (begin != end)
{
if (BOOST_LIKELY(begin < m_pathname.data() || begin >= (m_pathname.data() + m_pathname.size())))
{
const size_type that_size = end - begin;
size_type that_root_name_size = 0;
size_type that_root_dir_pos = find_root_directory_start(begin, that_size, that_root_name_size);
// Unlike C++20 std::filesystem, do not assign path(begin, end) if it is absolute. This breaks support
// for UNC paths on POSIX, where `path("//net/foo") / "/bar"` would result in "/bar" instead of "//net/bar".
size_type this_root_name_size = 0;
find_root_directory_start(m_pathname.c_str(), m_pathname.size(), this_root_name_size);
if
(
that_root_name_size > 0 &&
(that_root_name_size != this_root_name_size || std::memcmp(m_pathname.c_str(), begin, this_root_name_size * sizeof(value_type)) != 0)
)
{
m_pathname.assign(begin, end);
return;
}
if (that_root_dir_pos < that_size)
{
// Remove root directory (if any) and relative path to replace with those from p
m_pathname.erase(m_pathname.begin() + this_root_name_size, m_pathname.end());
}
const value_type* const that_path = begin + that_root_name_size;
if (!detail::is_directory_separator(*that_path))
append_separator_if_needed();
m_pathname.append(that_path, end);
}
else
{
// overlapping source
path rhs(begin, end);
append_v4(rhs);
}
}
}
#ifdef BOOST_WINDOWS_API
@ -273,14 +379,14 @@ BOOST_FILESYSTEM_DECL path& path::replace_extension(path const& new_extension)
BOOST_FILESYSTEM_DECL size_type path::find_root_name_size() const
{
size_type root_name_size = 0;
find_root_directory_start(m_pathname, m_pathname.size(), root_name_size);
find_root_directory_start(m_pathname.c_str(), m_pathname.size(), root_name_size);
return root_name_size;
}
BOOST_FILESYSTEM_DECL size_type path::find_root_path_size() const
{
size_type root_name_size = 0;
size_type root_dir_pos = find_root_directory_start(m_pathname, m_pathname.size(), root_name_size);
size_type root_dir_pos = find_root_directory_start(m_pathname.c_str(), m_pathname.size(), root_name_size);
size_type size = root_name_size;
if (root_dir_pos < m_pathname.size())
@ -293,7 +399,7 @@ BOOST_FILESYSTEM_DECL substring path::find_root_directory() const
{
substring root_dir;
size_type root_name_size = 0;
root_dir.pos = find_root_directory_start(m_pathname, m_pathname.size(), root_name_size);
root_dir.pos = find_root_directory_start(m_pathname.c_str(), m_pathname.size(), root_name_size);
root_dir.size = static_cast< std::size_t >(root_dir.pos < m_pathname.size());
return root_dir;
}
@ -301,7 +407,7 @@ BOOST_FILESYSTEM_DECL substring path::find_root_directory() const
BOOST_FILESYSTEM_DECL substring path::find_relative_path() const
{
size_type root_name_size = 0;
size_type root_dir_pos = find_root_directory_start(m_pathname, m_pathname.size(), root_name_size);
size_type root_dir_pos = find_root_directory_start(m_pathname.c_str(), m_pathname.size(), root_name_size);
// Skip root name, root directory and any duplicate separators
size_type size = root_name_size;
@ -327,7 +433,7 @@ BOOST_FILESYSTEM_DECL string_type::size_type path::find_parent_path_size() const
{
const size_type size = m_pathname.size();
size_type root_name_size = 0;
size_type root_dir_pos = find_root_directory_start(m_pathname, size, root_name_size);
size_type root_dir_pos = find_root_directory_start(m_pathname.c_str(), size, root_name_size);
size_type filename_size = find_filename_size(m_pathname, root_name_size, size);
size_type end_pos = size - filename_size;
@ -364,7 +470,7 @@ BOOST_FILESYSTEM_DECL path path::filename_v3() const
{
const size_type size = m_pathname.size();
size_type root_name_size = 0;
size_type root_dir_pos = find_root_directory_start(m_pathname, size, root_name_size);
size_type root_dir_pos = find_root_directory_start(m_pathname.c_str(), size, root_name_size);
size_type filename_size, pos;
if (root_dir_pos < size && detail::is_directory_separator(m_pathname[size - 1]) && is_root_separator(m_pathname, root_dir_pos, size - 1))
{
@ -394,7 +500,7 @@ BOOST_FILESYSTEM_DECL string_type::size_type path::find_filename_v4_size() const
{
const size_type size = m_pathname.size();
size_type root_name_size = 0;
find_root_directory_start(m_pathname, size, root_name_size);
find_root_directory_start(m_pathname.c_str(), size, root_name_size);
return find_filename_size(m_pathname, root_name_size, size);
}
@ -436,7 +542,7 @@ BOOST_FILESYSTEM_DECL path path::extension_v4() const
path ext;
const size_type size = m_pathname.size();
size_type root_name_size = 0;
find_root_directory_start(m_pathname, size, root_name_size);
find_root_directory_start(m_pathname.c_str(), size, root_name_size);
size_type filename_size = find_filename_size(m_pathname, root_name_size, size);
size_type filename_pos = size - filename_size;
if
@ -514,7 +620,7 @@ BOOST_FILESYSTEM_DECL path path::lexically_relative(path const& base) const
BOOST_FILESYSTEM_DECL path path::lexically_normal() const
{
size_type root_name_size = 0;
size_type root_dir_pos = find_root_directory_start(m_pathname, m_pathname.size(), root_name_size);
size_type root_dir_pos = find_root_directory_start(m_pathname.c_str(), m_pathname.size(), root_name_size);
path normal(m_pathname.c_str(), m_pathname.c_str() + root_name_size);
size_type root_path_size = root_name_size;
@ -648,9 +754,8 @@ inline size_type find_filename_size(string_type const& str, size_type root_name_
// find_root_directory_start -------------------------------------------------------//
// Returns: starting position of root directory or size if not found
size_type find_root_directory_start(string_type const& path, size_type size, size_type& root_name_size)
size_type find_root_directory_start(const value_type* path, size_type size, size_type& root_name_size)
{
BOOST_ASSERT(size <= path.size());
root_name_size = 0;
if (size == 0)
return 0;
@ -736,9 +841,7 @@ size_type find_root_directory_start(string_type const& path, size_type size, siz
return size;
find_next_separator:
pos = path.find_first_of(separators, pos);
if (pos > size)
pos = size;
pos += find_first_separator(path + pos, size - pos);
if (parsing_root_name)
root_name_size = pos;
@ -757,7 +860,7 @@ void first_element(string_type const& src, size_type& element_pos, size_type& el
return;
size_type root_name_size = 0;
size_type root_dir_pos = find_root_directory_start(src, size, root_name_size);
size_type root_dir_pos = find_root_directory_start(src.c_str(), size, root_name_size);
// First element is the root name, if there is one
if (root_name_size > 0)
@ -880,7 +983,7 @@ BOOST_FILESYSTEM_DECL void path::iterator::increment_v3()
if (detail::is_directory_separator(m_path_ptr->m_pathname[m_pos]))
{
size_type root_name_size = 0;
size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname, size, root_name_size);
size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname.c_str(), size, root_name_size);
// detect root directory and set iterator value to the separator if it is
if (m_pos == root_dir_pos && m_element.m_pathname.size() == root_name_size)
@ -941,7 +1044,7 @@ BOOST_FILESYSTEM_DECL void path::iterator::increment_v4()
if (detail::is_directory_separator(m_path_ptr->m_pathname[m_pos]))
{
size_type root_name_size = 0;
size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname, size, root_name_size);
size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname.c_str(), size, root_name_size);
// detect root directory and set iterator value to the separator if it is
if (m_pos == root_dir_pos && m_element.m_pathname.size() == root_name_size)
@ -981,7 +1084,7 @@ BOOST_FILESYSTEM_DECL void path::iterator::decrement_v3()
BOOST_ASSERT_MSG(m_pos <= size, "path::iterator decrement after the referenced path was modified");
size_type root_name_size = 0;
size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname, size, root_name_size);
size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname.c_str(), size, root_name_size);
if (root_dir_pos < size && m_pos == root_dir_pos)
{
@ -1041,7 +1144,7 @@ BOOST_FILESYSTEM_DECL void path::iterator::decrement_v4()
BOOST_ASSERT_MSG(m_pos <= size, "path::iterator decrement after the referenced path was modified");
size_type root_name_size = 0;
size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname, size, root_name_size);
size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname.c_str(), size, root_name_size);
if (root_dir_pos < size && m_pos == root_dir_pos)
{

View File

@ -571,6 +571,16 @@ void non_member_tests()
// probe operator /
PATH_TEST_EQ(path("") / ".", ".");
PATH_TEST_EQ(path("") / "..", "..");
#if BOOST_FILESYSTEM_VERSION == 3
PATH_TEST_EQ(path("/") / "/", "//");
PATH_TEST_EQ(path("/") / "/foo", "//foo");
PATH_TEST_EQ(path("/foo") / "/bar", "/foo/bar");
#else
PATH_TEST_EQ(path("/") / "/", "/");
PATH_TEST_EQ(path("/") / "/foo", "/foo");
PATH_TEST_EQ(path("/foo") / "/bar", "/bar");
#endif
if (platform == "Windows")
{
BOOST_TEST(path("foo\\bar") == "foo/bar");
@ -618,6 +628,19 @@ void non_member_tests()
PATH_TEST_EQ(path(".") / "." / "..", ".\\.\\..");
PATH_TEST_EQ(path(".") / ".." / ".", ".\\..\\.");
PATH_TEST_EQ(path("..") / "." / ".", "..\\.\\.");
#if BOOST_FILESYSTEM_VERSION == 3
PATH_TEST_EQ(path("\\\\net1\\foo") / "\\\\net2\\bar", "\\\\net1\\foo\\\\net2\\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("c:\\foo") / "d:\\bar", "d:\\bar");
PATH_TEST_EQ(path("c:\\foo") / "\\bar", "c:\\bar");
PATH_TEST_EQ(path("c:foo") / "\\bar", "c:\\bar");
#endif
PATH_TEST_EQ(path("c:foo") / "bar", "c:foo\\bar");
}
else // POSIX
{
@ -666,6 +689,15 @@ void non_member_tests()
PATH_TEST_EQ(path(".") / "." / "..", "././..");
PATH_TEST_EQ(path(".") / ".." / ".", "./../.");
PATH_TEST_EQ(path("..") / "." / ".", ".././.");
#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");
#else
PATH_TEST_EQ(path("//net1/foo") / "//net2/bar", "//net2/bar");
PATH_TEST_EQ(path("//net1/foo") / "/bar", "//net1/bar");
#endif
PATH_TEST_EQ(path("//net1/foo") / "bar", "//net1/foo/bar");
}
// probe operator <
@ -2057,29 +2089,54 @@ void append_tests()
PATH_TEST_EQ(path("/") / "", "/");
append_test_aux("/", "", "/");
#if BOOST_FILESYSTEM_VERSION == 3
PATH_TEST_EQ(path("/") / "/", "//");
append_test_aux("/", "/", "//");
#else
PATH_TEST_EQ(path("/") / "/", "/");
append_test_aux("/", "/", "/");
#endif
PATH_TEST_EQ(path("/") / "bar", "/bar");
append_test_aux("/", "bar", "/bar");
#if BOOST_FILESYSTEM_VERSION == 3
PATH_TEST_EQ(path("/") / "/bar", "//bar");
append_test_aux("/", "/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", "/", "/");
#endif
#if BOOST_FILESYSTEM_VERSION == 3
PATH_TEST_EQ(path("foo") / "/bar", "foo/bar");
append_test_aux("foo", "/bar", "foo/bar");
#else
PATH_TEST_EQ(path("foo") / "/bar", "/bar");
append_test_aux("foo", "/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/", "/", "/");
#endif
PATH_TEST_EQ(path("foo/") / "bar", "foo/bar");
append_test_aux("foo/", "bar", "foo/bar");
@ -2089,8 +2146,13 @@ void append_tests()
PATH_TEST_EQ(path("foo") / "bar", "foo\\bar");
append_test_aux("foo", "bar", "foo\\bar");
#if BOOST_FILESYSTEM_VERSION == 3
PATH_TEST_EQ(path("foo\\") / "\\bar", "foo\\\\bar");
append_test_aux("foo\\", "\\bar", "foo\\\\bar");
#else
PATH_TEST_EQ(path("foo\\") / "\\bar", "\\bar");
append_test_aux("foo\\", "\\bar", "\\bar");
#endif
// hand created test case specific to Windows
PATH_TEST_EQ(path("c:") / "bar", "c:bar");

View File

@ -325,15 +325,27 @@ void test_appends()
x = "/foo";
x /= path("/"); // slash path
#if BOOST_FILESYSTEM_VERSION == 3
PATH_IS(x, L"/foo/");
#else
PATH_IS(x, L"/");
#endif
x = "/foo";
x /= path("/boo"); // slash path
#if BOOST_FILESYSTEM_VERSION == 3
PATH_IS(x, L"/foo/boo");
#else
PATH_IS(x, L"/boo");
#endif
x = "/foo";
x /= x; // self-append
#if BOOST_FILESYSTEM_VERSION == 3
PATH_IS(x, L"/foo/foo");
#else
PATH_IS(x, L"/foo");
#endif
x = "/foo";
x /= path("yet another path"); // another path
@ -348,12 +360,12 @@ void test_appends()
PATH_IS(x, BOOST_FS_FOO L"wstring");
x = "/foo";
x /= string("std::string"); // container char
PATH_IS(x, BOOST_FS_FOO L"std::string");
x /= string("std_string"); // container char
PATH_IS(x, BOOST_FS_FOO L"std_string");
x = "/foo";
x /= wstring(L"std::wstring"); // container wchar_t
PATH_IS(x, BOOST_FS_FOO L"std::wstring");
x /= wstring(L"std_wstring"); // container wchar_t
PATH_IS(x, BOOST_FS_FOO L"std_wstring");
x = "/foo";
x /= "array char"; // array char