Add dir_entry::refresh and file type observers. Use them in recursive dir_it.

This commit changes behavior of directory_entry constructors and modifiers
that change the stored path in v4: the methods will now automatically query
the filesystem for the file status instead of leaving the cached data
default-initialized. This means that the paths passed to directory_entry
must be valid, otherwise an error will be returned. Filesystem querying
is implemented in the new directory_entry::refresh methods.

The constructors and modifiers that accepted file_status arguments are
now removed in v4. The cached file statuses are an implementation detail,
and eventually we may want to add more cached data, as we add more observers
to directory_entry.

Also added a few file type observers to directory_entry. These observers
allow to avoid querying the filesystem if the full file status is not cached
but the file type is (i.e. when permissions are not cached). This is the case
with readdir-based implementation of directory_iterator, if the underlying
C library supports dirent::d_type field.

recursive_directory_iterator has been updated to use the added file type
observers instead of querying the full status. This may improve performance
of directory iteration.

Closes https://github.com/boostorg/filesystem/issues/288.
This commit is contained in:
Andrey Semashev 2023-06-04 04:53:44 +03:00
parent 1aff314c93
commit d508d4950f
7 changed files with 460 additions and 146 deletions

View File

@ -2543,23 +2543,48 @@ and permissions of a file.</p>
// <a href="#directory_entry-constructors">constructors</a> and destructor
directory_entry();
directory_entry(const directory_entry&amp;);
explicit directory_entry(const path&amp; p, file_status st=file_status(),
explicit directory_entry(const path&amp; p);
directory_entry(const path&amp; p, system::error_code&amp; ec); // v4-only
directory_entry(const path&amp; p, file_status st, // v3-only
file_status symlink_st=file_status());
~directory_entry();
// <a href="#directory_entry-modifiers">modifiers</a>
directory_entry&amp; operator=(const directory_entry&amp;);
void assign(const path&amp; p, file_status st=file_status(),
void assign(const path&amp; p);
void assign(const path&amp; p, system::error_code&amp; ec); // v4-only
void assign(const path&amp; p, file_status st, // v3-only
file_status symlink_st=file_status());
void replace_filename(const path&amp; p, file_status st=file_status(),
void replace_filename(const path&amp; p);
void replace_filename(const path&amp; p, system::error_code&amp; ec); // v4-only
void replace_filename(const path&amp; p, file_status st, // v3-only
file_status symlink_st=file_status());
void refresh();
void refresh(system::error_code&amp; ec);
// <a href="#directory_entry-observers">observers</a>
const path&amp; path() const;
file_status status() const;
file_status status(system::error_code&amp; ec) const;
file_status symlink_status() const;
file_status symlink_status(system::error_code&amp; ec) const;
file_type file_type() const;
file_type file_type(system::error_code&amp; ec) const;
file_type symlink_file_type() const;
file_type symlink_file_type(system::error_code&amp; ec) const;
bool exists() const;
bool exists(system::error_code&amp; ec) const;
bool is_regular_file() const;
bool is_regular_file(system::error_code&amp; ec) const;
bool is_directory() const;
bool is_directory(system::error_code&amp; ec) const;
bool is_symlink() const;
bool is_symlink(system::error_code&amp; ec) const;
bool is_other() const;
bool is_other(system::error_code&amp; ec) const;
bool operator&lt; (const directory_entry&amp; rhs);
bool operator==(const directory_entry&amp; rhs);
@ -2577,10 +2602,12 @@ and permissions of a file.</p>
} // namespace filesystem
} // namespace boost</pre>
<p>A <code>directory_entry</code> object stores a <code>path object</code>,
a <code>file_status</code> object for non-symbolic link status, and a <code>file_status</code> object for symbolic link status. The <code>file_status</code> objects act as value caches.</p>
<p>A <code>directory_entry</code> object stores a <code>path</code> object,
as well as some amount of cached information about the file identified by the path.
Currently, the cached information includes a <code>file_status</code> object for non-symbolic
link status and a <code>file_status</code> object for symbolic link status.</p>
<blockquote>
<p>[<i>Note:</i> Because <code>status()</code>on a pathname may be a relatively expensive operation,
<p>[<i>Note:</i> Because <code>status()</code> on a pathname may be a relatively expensive operation,
some operating systems provide status information as a byproduct of directory
iteration. Caching such status information can result is significant time savings. Cached and
non-cached results may differ in the presence of file system races. <i>—end note</i>]</p>
@ -2589,8 +2616,14 @@ a directory with 15,047 entries was six seconds for non-cached status queries
versus one second for cached status queries. Windows XP, 3.0 GHz processor, with
a moderately fast hard-drive. Similar speedups are expected on Linux and BSD-derived
systems that provide status as a by-product of directory iteration.</i></span></p>
<p>[<i>Note:</i> The exact set of cached information may vary from one Boost.Filesystem version
to another, and also between different operating systems and underlying file systems. Users' code
must not rely on whether a certain piece of information is cached or not. This means that calling
most observers and modifiers of <code>directory_entry</code> may or may not result in a filesystem
query that may potentially fail. Information caching is exclusively a performance feature aimed
at reducing the amount of such queries. <i>—end note</i>]</p>
</blockquote>
<h3> <a name="directory_entry-constructors"> <code>directory_entry </code>constructors</a>
<h3> <a name="directory_entry-constructors"> <code>directory_entry</code> constructors</a>
[directory_entry.cons]</h3>
<pre>directory_entry();</pre>
<blockquote>
@ -2614,9 +2647,18 @@ systems that provide status as a by-product of directory iteration.</i></span></
</tr>
</table>
</blockquote>
<pre>explicit directory_entry(const path&amp; p, file_status st=file_status(), file_status symlink_st=file_status());</pre>
<pre>explicit directory_entry(const path&amp; p);
directory_entry(const path&amp; p, system::error_code&amp; ec); // v4-only</pre>
<blockquote>
<p><i>Postcondition:</i></p>
<p><i>Effects:</i></p>
<p><b>v3:</b> Initializes <code>m_path</code> from <code>p</code> and default-constructs <code>m_status</code> and <code>m_symlink_status</code>.</p>
<p>[<i>Note:</i> The cached file statuses will be updated when queried by the caller or by an explicit call to <code>refresh</code>. <i>—end note</i>]</p>
<p><b>v4:</b> Initializes <code>m_path</code> from <code>p</code> and calls <code>refresh()</code> or <code>refresh(ec)</code>, respectively.</p>
<p><i>Postcondition:</i> <code>path() == p</code> if no error occurs, otherwise <code>path().empty() == true</code>.</p>
</blockquote>
<pre>directory_entry(const path&amp; p, file_status st, file_status symlink_st=file_status()); // v3-only</pre>
<blockquote>
<p><b>v3:</b> <i>Postcondition:</i></p>
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="36%">
<tr>
<td width="18%"><b>Expression</b></td>
@ -2636,11 +2678,20 @@ systems that provide status as a by-product of directory iteration.</i></span></
</tr>
</table>
</blockquote>
<h3> <a name="directory_entry-modifiers"> <code>directory_entry </code>modifiers</a>
<h3> <a name="directory_entry-modifiers"> <code>directory_entry</code> modifiers</a>
[directory_entry.mods]</h3>
<pre>void assign(const path&amp; p, file_status st=file_status(), file_status symlink_st=file_status());</pre>
<pre>void assign(const path&amp; p);
void assign(const path&amp; p, system::error_code&amp; ec); // v4-only</pre>
<blockquote>
<p><i>Postcondition:</i></p>
<p><i>Effects:</i></p>
<p><b>v3:</b> Assigns <code>p</code> to <code>m_path</code> and <code>file_status()</code> to <code>m_status</code> and <code>m_symlink_status</code>.</p>
<p>[<i>Note:</i> The cached file statuses will be updated when queried by the caller or by an explicit call to <code>refresh</code>. <i>—end note</i>]</p>
<p><b>v4:</b> Assigns <code>p</code> to <code>m_path</code> and calls <code>refresh()</code> or <code>refresh(ec)</code>, respectively. If an error
occurs, the value of the cached data is unspecified.</p>
</blockquote>
<pre>void assign(const path&amp; p, file_status st, file_status symlink_st=file_status()); // v3-only</pre>
<blockquote>
<p><b>v3:</b> <i>Postcondition:</i></p>
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="36%">
<tr>
<td width="18%"><b>Expression</b></td>
@ -2660,27 +2711,25 @@ systems that provide status as a by-product of directory iteration.</i></span></
</tr>
</table>
</blockquote>
<pre>void replace_filename(const path&amp; p, file_status st=file_status(), file_status symlink_st=file_status());</pre>
<pre>void replace_filename(const path&amp; p);
void replace_filename(const path&amp; p, system::error_code&amp; ec); // v4-only</pre>
<blockquote>
<p><i>Postcondition:</i></p>
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="43%">
<tr>
<td width="18%"><b>Expression</b></td>
<td width="82%"><b>Value</b></td>
</tr>
<tr>
<td width="18%"><code>path()</code></td>
<td width="82%"><code>path().branch() / s</code></td>
</tr>
<tr>
<td width="18%"><code>status()</code></td>
<td width="82%"><code>st</code></td>
</tr>
<tr>
<td width="18%"><code>symlink_status()</code></td>
<td width="82%"><code>symlink_st</code></td>
</tr>
</table>
<p><i>Effects:</i></p>
<p><b>v3:</b> Calls <code>m_path.replace_filename(p)</code> and assigns <code>file_status()</code> to <code>m_status</code> and <code>m_symlink_status</code>.</p>
<p>[<i>Note:</i> The cached file statuses will be updated when queried by the caller or by an explicit call to <code>refresh</code>. <i>—end note</i>]</p>
<p><b>v4:</b> Calls <code>m_path.replace_filename(p)</code> and then <code>refresh()</code> or <code>refresh(ec)</code>, respectively. If an error
occurs, the value of the cached data is unspecified.</p>
</blockquote>
<pre>void replace_filename(const path&amp; p, file_status st, file_status symlink_st=file_status()); // v3-only</pre>
<blockquote>
<p><i>Effects:</i> <b>v3:</b> Calls <code>m_path.replace_filename(p)</code> and assigns <code>st</code> to <code>m_status</code> and <code>symlink_st</code>
to <code>m_symlink_status</code>.</p>
</blockquote>
<pre>void refresh();
void refresh(system::error_code&amp; ec);</pre>
<blockquote>
<p><i>Effects:</i> Updates any cached data by querying the filesystem about the file identified by <code>m_path</code>. If an error occurs,
the value of the cached data is unspecified.</p>
</blockquote>
<h3> <a name="directory_entry-observers"> <code>directory_entry</code> observers</a>
[directory_entry.obs]</h3>
@ -2691,35 +2740,55 @@ systems that provide status as a by-product of directory iteration.</i></span></
<pre>file_status status() const;
file_status status(system::error_code&amp; ec) const;</pre>
<blockquote>
<p><i>Effects:</i> As if,</p>
<blockquote>
<pre>if ( !status_known( m_status ) )
{
if ( status_known(m_symlink_status) &amp;&amp; !is_symlink(m_symlink_status) )
{ m_status = m_symlink_status; }
else { m_status = status(m_path<i>[, ec]</i>); }
}</pre>
</blockquote>
<p><i>Effects:</i> If <code>!status_known(m_status)</code>, calls <code>refresh()</code> or <code>refresh(ec)</code>, respectively.</p>
<p><i>Returns:</i> <code>m_status</code></p>
<p><i>Throws:</i> As specified in <a href="#Error-reporting">Error reporting</a>.</p>
</blockquote>
<pre>file_status symlink_status() const;
file_status symlink_status(system::error_code&amp; ec) const;</pre>
<blockquote>
<p>
<i>Effects:</i> As if,</p>
<blockquote>
<pre>if ( !status_known( m_symlink_status ) )
{
m_symlink_status = symlink_status(m_path<i>[, ec]</i>);
}</pre>
</blockquote>
<p><i>Effects:</i> If <code>!status_known(m_symlink_status)</code>, calls <code>refresh()</code> or <code>refresh(ec)</code>, respectively.</p>
<p><i>Returns:</i> <code>m_symlink_status</code></p>
<p><i>Throws:</i> As specified in <a href="#Error-reporting">Error reporting</a>.</p>
</blockquote>
<pre>file_type file_type() const;
file_type file_type(system::error_code&amp; ec) const;</pre>
<blockquote>
<p><i>Effects:</i> Equivalent to <code>status().type()</code> or <code>status(ec).type()</code>, respectively.</p>
<p>[<i>Note:</i> The implementation may be more efficient than calling <code>status</code>, if the information
about the file type is cached, but permissions are not. <i>—end note</i>]</p>
</blockquote>
<pre>file_type symlink_file_type() const;
file_type symlink_file_type(system::error_code&amp; ec) const;</pre>
<blockquote>
<p><i>Effects:</i> Equivalent to <code>symlink_status().type()</code> or <code>symlink_status(ec).type()</code>, respectively.</p>
<p>[<i>Note:</i> The implementation may be more efficient than calling <code>symlink_status</code>, if the information
about the file type is cached, but permissions are not. <i>—end note</i>]</p>
</blockquote>
<pre>bool exists() const;
bool exists(system::error_code&amp; ec) const;</pre>
<blockquote>
<p><i>Effects:</i> Equivalent to <code>exists(status())</code> or <code>exists(status(ec))</code>, respectively.</p>
</blockquote>
<pre>bool is_regular_file() const;
bool is_regular_file(system::error_code&amp; ec) const;</pre>
<blockquote>
<p><i>Effects:</i> Equivalent to <code>is_regular_file(status())</code> or <code>is_regular_file(status(ec))</code>, respectively.</p>
</blockquote>
<pre>bool is_directory() const;
bool is_directory(system::error_code&amp; ec) const;</pre>
<blockquote>
<p><i>Effects:</i> Equivalent to <code>is_directory(status())</code> or <code>is_directory(status(ec))</code>, respectively.</p>
</blockquote>
<pre>bool is_symlink() const;
bool is_symlink(system::error_code&amp; ec) const;</pre>
<blockquote>
<p><i>Effects:</i> Equivalent to <code>is_symlink(symlink_status())</code> or <code>is_symlink(symlink_status(ec))</code>, respectively.</p>
</blockquote>
<pre>bool is_other() const;
bool is_other(system::error_code&amp; ec) const;</pre>
<blockquote>
<p><i>Effects:</i> Equivalent to <code>is_other(status())</code> or <code>is_other(status(ec))</code>, respectively.</p>
</blockquote>
<pre>bool operator==(const directory_entry&amp; rhs);</pre>
<blockquote>

View File

@ -41,6 +41,11 @@
<h2>1.83.0</h2>
<ul>
<li>Added <code>directory_entry::refresh</code> method that updates internal cached file statuses for the directory entry identified by path.</li>
<li><b>v4:</b> <code>directory_entry</code> constructors and modifiers that initialize or modify the path now automatically call <code>refresh</code>. This may result in errors that were not indicated before and in <b>v3</b>, if querying the filesystem for file statuses fails (e.g. if the file does not exist). This new behavior is similar to std::filesystem.</li>
<li><b>v4:</b> <code>directory_entry</code> constructors and methods taking <code>file_status</code> parameters are removed. Users are recommended to remove these arguments and rely on <code>directory_entry</code> calling <code>refresh</code> internally.</li>
<li>Added <code>directory_entry</code> member methods for checking the file type of the file, similar to std::filesystem.</li>
<li><code>recursive_directory_iterator</code> is now more likely to reuse information about the file type that is obtained during filesystem iteration. This may improve performance. (<a href="https://github.com/boostorg/filesystem/issues/288">#288</a>)</li>
<li>File streams defined in <code>boost/filesystem/fstream.hpp</code> are now movable, if the standard library file streams are. (<a href="https://github.com/boostorg/filesystem/issues/280">#280</a>)</li>
<li>Generic <code>path</code> comparison operators are now more restricted to avoid potential ambiguities when user's code contains a <code>using namespace boost::filesystem;</code> directive. (<a href="https://github.com/boostorg/filesystem/issues/285">#285</a>)</li>
<li>Fixed potential overload resolution ambiguity in users' code, where <code>path</code> constructors from iterators could interfere with function overloads taking a <code>std::initializer_list</code> argument. (<a href="https://github.com/boostorg/filesystem/issues/287">#287</a>)</li>

View File

@ -54,10 +54,13 @@ It removes the features that were <a href="deprecated.html">deprecated</a> in Ve
<li><a href="reference.html#path-appends"><code>path</code> appends</a> consider root name and root directory of 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. This behavior is similar to C++17 std::filesystem.</li>
<li><code>path</code> no longer supports construction, assignment or appending from containers of characters. Use string types or iterators as the source for these opereations instead.</li>
<li><a href="reference.html#path-remove_filename"><code>path::remove_filename</code></a> preserves the trailing directory separator, so that <code>path::has_filename</code> returns <code>false</code> after a successful call to <code>path::remove_filename</code>.</li>
<li><code>directory_entry</code> constructors and modifiers that initialize or modify path of the directory entry automatically call <code>directory_entry::refresh</code> instead of clearing cached file statuses. This means that the file identified by the new path needs to be accessible in the filesystem at the point of the call.</li>
<li><code>directory_entry</code> constructors and modifiers that accept <code>file_status</code> arguments to initialize cached file statuses are removed. The amount of cached data
is an implementation detail of <code>directory_entry</code>, and in the future it may not be limited to just file statuses. Users should rely on automatic refreshes of the cached data.</li>
</ul>
<hr>
<p>&copy; Copyright Andrey Semashev, 2021-2022</p>
<p>&copy; Copyright Andrey Semashev, 2021-2023</p>
<p>Distributed under the Boost Software License, Version 1.0. See
<a href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</a></p>

View File

@ -41,6 +41,17 @@
namespace boost {
namespace filesystem {
class directory_iterator;
namespace detail {
struct directory_iterator_params;
BOOST_FILESYSTEM_DECL void directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, directory_iterator_params* params, system::error_code* ec);
BOOST_FILESYSTEM_DECL void directory_iterator_increment(directory_iterator& it, system::error_code* ec);
} // namespace detail
//--------------------------------------------------------------------------------------//
// //
// directory_entry //
@ -53,20 +64,30 @@ namespace filesystem {
class directory_entry
{
friend BOOST_FILESYSTEM_DECL void detail::directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, detail::directory_iterator_params* params, system::error_code* ec);
friend BOOST_FILESYSTEM_DECL void detail::directory_iterator_increment(directory_iterator& it, system::error_code* ec);
public:
typedef boost::filesystem::path::value_type value_type; // enables class path ctor taking directory_entry
directory_entry() BOOST_NOEXCEPT {}
explicit directory_entry(boost::filesystem::path const& p) :
m_path(p), m_status(file_status()), m_symlink_status(file_status())
{
}
explicit directory_entry(boost::filesystem::path const& p);
#if BOOST_FILESYSTEM_VERSION >= 4
directory_entry(boost::filesystem::path const& p, system::error_code& ec) :
m_path(p)
{
refresh_impl(&ec);
if (ec)
m_path.clear();
}
#else
directory_entry(boost::filesystem::path const& p, file_status st, file_status symlink_st = file_status()) :
m_path(p), m_status(st), m_symlink_status(symlink_st)
{
}
#endif
directory_entry(directory_entry const& rhs) :
m_path(rhs.m_path), m_status(rhs.m_status), m_symlink_status(rhs.m_symlink_status)
@ -101,7 +122,188 @@ public:
return *this;
}
void assign(boost::filesystem::path&& p, file_status st = file_status(), file_status symlink_st = file_status())
void assign(boost::filesystem::path&& p);
#if BOOST_FILESYSTEM_VERSION >= 4
void assign(boost::filesystem::path&& p, system::error_code& ec)
{
m_path = static_cast< boost::filesystem::path&& >(p);
refresh_impl(&ec);
}
#else
void assign(boost::filesystem::path&& p, file_status st, file_status symlink_st = file_status())
{
assign_with_status(static_cast< boost::filesystem::path&& >(p), st, symlink_st);
}
#endif
#endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
void assign(boost::filesystem::path const& p);
#if BOOST_FILESYSTEM_VERSION >= 4
void assign(boost::filesystem::path const& p, system::error_code& ec)
{
m_path = p;
refresh_impl(&ec);
}
#else
void assign(boost::filesystem::path const& p, file_status st, file_status symlink_st = file_status())
{
assign_with_status(p, st, symlink_st);
}
#endif
void replace_filename(boost::filesystem::path const& p);
#if BOOST_FILESYSTEM_VERSION >= 4
void replace_filename(boost::filesystem::path const& p, system::error_code& ec)
{
m_path.replace_filename(p);
refresh_impl(&ec);
}
#else
void replace_filename(boost::filesystem::path const& p, file_status st, file_status symlink_st = file_status())
{
replace_filename_with_status(p, st, symlink_st);
}
BOOST_FILESYSTEM_DETAIL_DEPRECATED("Use directory_entry::replace_filename() instead")
void replace_leaf(boost::filesystem::path const& p, file_status st, file_status symlink_st)
{
replace_filename_with_status(p, st, symlink_st);
}
#endif
boost::filesystem::path const& path() const BOOST_NOEXCEPT { return m_path; }
operator boost::filesystem::path const&() const BOOST_NOEXCEPT { return m_path; }
void refresh() { refresh_impl(); }
void refresh(system::error_code& ec) BOOST_NOEXCEPT { refresh_impl(&ec); }
file_status status() const
{
if (!filesystem::status_known(m_status))
refresh_impl();
return m_status;
}
file_status status(system::error_code& ec) const BOOST_NOEXCEPT
{
if (!filesystem::status_known(m_status))
refresh_impl(&ec);
return m_status;
}
file_status symlink_status() const
{
if (!filesystem::status_known(m_symlink_status))
refresh_impl();
return m_symlink_status;
}
file_status symlink_status(system::error_code& ec) const BOOST_NOEXCEPT
{
if (!filesystem::status_known(m_symlink_status))
refresh_impl(&ec);
return m_symlink_status;
}
filesystem::file_type file_type() const
{
if (!filesystem::type_present(m_status))
refresh_impl();
return m_status.type();
}
filesystem::file_type file_type(system::error_code& ec) const BOOST_NOEXCEPT
{
if (!filesystem::type_present(m_status))
refresh_impl(&ec);
return m_status.type();
}
filesystem::file_type symlink_file_type() const
{
if (!filesystem::type_present(m_symlink_status))
refresh_impl();
return m_symlink_status.type();
}
filesystem::file_type symlink_file_type(system::error_code& ec) const BOOST_NOEXCEPT
{
if (!filesystem::type_present(m_symlink_status))
refresh_impl(&ec);
return m_symlink_status.type();
}
bool exists() const
{
filesystem::file_type ft = this->file_type();
return ft != filesystem::status_error && ft != filesystem::file_not_found;
}
bool exists(system::error_code& ec) const BOOST_NOEXCEPT
{
filesystem::file_type ft = this->file_type(ec);
return ft != filesystem::status_error && ft != filesystem::file_not_found;
}
bool is_regular_file() const
{
return this->file_type() == filesystem::regular_file;
}
bool is_regular_file(system::error_code& ec) const BOOST_NOEXCEPT
{
return this->file_type(ec) == filesystem::regular_file;
}
bool is_directory() const
{
return this->file_type() == filesystem::directory_file;
}
bool is_directory(system::error_code& ec) const BOOST_NOEXCEPT
{
return this->file_type(ec) == filesystem::directory_file;
}
bool is_symlink() const
{
return this->symlink_file_type() == filesystem::symlink_file;
}
bool is_symlink(system::error_code& ec) const BOOST_NOEXCEPT
{
return this->symlink_file_type(ec) == filesystem::symlink_file;
}
bool is_other() const
{
filesystem::file_type ft = this->file_type();
return ft != filesystem::status_error && ft != filesystem::file_not_found &&
ft != filesystem::regular_file && ft != filesystem::directory_file;
}
bool is_other(system::error_code& ec) const BOOST_NOEXCEPT
{
filesystem::file_type ft = this->file_type(ec);
return ft != filesystem::status_error && ft != filesystem::file_not_found &&
ft != filesystem::regular_file && ft != filesystem::directory_file;
}
bool operator==(directory_entry const& rhs) const { return m_path == rhs.m_path; }
bool operator!=(directory_entry const& rhs) const { return m_path != rhs.m_path; }
bool operator<(directory_entry const& rhs) const { return m_path < rhs.m_path; }
bool operator<=(directory_entry const& rhs) const { return m_path <= rhs.m_path; }
bool operator>(directory_entry const& rhs) const { return m_path > rhs.m_path; }
bool operator>=(directory_entry const& rhs) const { return m_path >= rhs.m_path; }
private:
BOOST_FILESYSTEM_DECL void refresh_impl(system::error_code* ec = NULL) const;
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
void assign_with_status(boost::filesystem::path&& p, file_status st, file_status symlink_st)
{
m_path = static_cast< boost::filesystem::path&& >(p);
m_status = static_cast< file_status&& >(st);
@ -109,7 +311,7 @@ public:
}
#endif
void assign(boost::filesystem::path const& p, file_status st = file_status(), file_status symlink_st = file_status())
void assign_with_status(boost::filesystem::path const& p, file_status st, file_status symlink_st)
{
m_path = p;
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
@ -121,7 +323,7 @@ public:
#endif
}
void replace_filename(boost::filesystem::path const& p, file_status st = file_status(), file_status symlink_st = file_status())
void replace_filename_with_status(boost::filesystem::path const& p, file_status st, file_status symlink_st)
{
m_path.replace_filename(p);
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
@ -133,40 +335,58 @@ public:
#endif
}
#ifndef BOOST_FILESYSTEM_NO_DEPRECATED
BOOST_FILESYSTEM_DETAIL_DEPRECATED("Use directory_entry::replace_filename() instead")
void replace_leaf(boost::filesystem::path const& p, file_status st, file_status symlink_st)
{
replace_filename(p, st, symlink_st);
}
#endif
boost::filesystem::path const& path() const BOOST_NOEXCEPT
{
return m_path;
}
operator boost::filesystem::path const&() const BOOST_NOEXCEPT { return m_path; }
file_status status() const { return get_status(); }
file_status status(system::error_code& ec) const BOOST_NOEXCEPT { return get_status(&ec); }
file_status symlink_status() const { return get_symlink_status(); }
file_status symlink_status(system::error_code& ec) const BOOST_NOEXCEPT { return get_symlink_status(&ec); }
bool operator==(directory_entry const& rhs) const { return m_path == rhs.m_path; }
bool operator!=(directory_entry const& rhs) const { return m_path != rhs.m_path; }
bool operator<(directory_entry const& rhs) const { return m_path < rhs.m_path; }
bool operator<=(directory_entry const& rhs) const { return m_path <= rhs.m_path; }
bool operator>(directory_entry const& rhs) const { return m_path > rhs.m_path; }
bool operator>=(directory_entry const& rhs) const { return m_path >= rhs.m_path; }
private:
BOOST_FILESYSTEM_DECL file_status get_status(system::error_code* ec = NULL) const;
BOOST_FILESYSTEM_DECL file_status get_symlink_status(system::error_code* ec = NULL) const;
private:
boost::filesystem::path m_path;
mutable file_status m_status; // stat()-like
mutable file_status m_symlink_status; // lstat()-like
}; // directory_entry
};
#if !defined(BOOST_FILESYSTEM_SOURCE)
inline directory_entry::directory_entry(boost::filesystem::path const& p) :
m_path(p)
{
#if BOOST_FILESYSTEM_VERSION >= 4
refresh_impl();
#endif
}
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
inline void directory_entry::assign(boost::filesystem::path&& p)
{
m_path = static_cast< boost::filesystem::path&& >(p);
#if BOOST_FILESYSTEM_VERSION >= 4
refresh_impl();
#else
m_status = file_status();
m_symlink_status = file_status();
#endif
}
#endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
inline void directory_entry::assign(boost::filesystem::path const& p)
{
m_path = p;
#if BOOST_FILESYSTEM_VERSION >= 4
refresh_impl();
#else
m_status = file_status();
m_symlink_status = file_status();
#endif
}
inline void directory_entry::replace_filename(boost::filesystem::path const& p)
{
m_path.replace_filename(p);
#if BOOST_FILESYSTEM_VERSION >= 4
refresh_impl();
#else
m_status = file_status();
m_symlink_status = file_status();
#endif
}
#endif // !defined(BOOST_FILESYSTEM_SOURCE)
namespace detail {
namespace path_traits {
@ -287,8 +507,6 @@ BOOST_SCOPED_ENUM_DECLARE_END(directory_options)
BOOST_BITMASK(BOOST_SCOPED_ENUM_NATIVE(directory_options))
class directory_iterator;
namespace detail {
struct dir_itr_imp :
@ -318,11 +536,6 @@ struct dir_itr_imp :
BOOST_FILESYSTEM_DECL static void operator delete(void* p) BOOST_NOEXCEPT;
};
struct directory_iterator_params;
BOOST_FILESYSTEM_DECL void directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, directory_iterator_params* params, system::error_code* ec);
BOOST_FILESYSTEM_DECL void directory_iterator_increment(directory_iterator& it, system::error_code* ec);
} // namespace detail
//--------------------------------------------------------------------------------------//

View File

@ -87,42 +87,32 @@ namespace filesystem {
// //
//--------------------------------------------------------------------------------------//
BOOST_FILESYSTEM_DECL
file_status directory_entry::get_status(system::error_code* ec) const
BOOST_FILESYSTEM_DECL void directory_entry::refresh_impl(system::error_code* ec) const
{
if (!status_known(m_status))
system::error_code local_ec;
m_symlink_status = detail::symlink_status(m_path, &local_ec);
if (!filesystem::is_symlink(m_symlink_status))
{
// optimization: if the symlink status is known, and it isn't a symlink,
// then status and symlink_status are identical so just copy the
// symlink status to the regular status.
if (status_known(m_symlink_status) && !is_symlink(m_symlink_status))
// Also works if symlink_status fails - set m_status to status_error as well
m_status = m_symlink_status;
if (BOOST_UNLIKELY(!!local_ec))
{
m_status = m_symlink_status;
if (ec)
ec->clear();
}
else
{
m_status = detail::status(m_path, ec);
if (!ec)
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_entry::refresh", m_path, local_ec));
*ec = local_ec;
return;
}
if (ec)
ec->clear();
}
else if (ec)
else
{
ec->clear();
m_status = detail::status(m_path, ec);
}
return m_status;
}
BOOST_FILESYSTEM_DECL
file_status directory_entry::get_symlink_status(system::error_code* ec) const
{
if (!status_known(m_symlink_status))
m_symlink_status = detail::symlink_status(m_path, ec);
else if (ec)
ec->clear();
return m_symlink_status;
}
//--------------------------------------------------------------------------------------//
@ -1131,7 +1121,7 @@ void directory_iterator_construct(directory_iterator& it, path const& p, unsigne
{
path full_path(p);
path_algorithms::append_v4(full_path, filename);
imp->dir_entry.assign
imp->dir_entry.assign_with_status
(
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
static_cast< path&& >(full_path),
@ -1200,7 +1190,7 @@ void directory_iterator_increment(directory_iterator& it, system::error_code* ec
&& (filename_str[1] == static_cast< path::string_type::value_type >('\0') ||
(filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0')))))
{
it.m_imp->dir_entry.replace_filename(filename, file_stat, symlink_file_stat);
it.m_imp->dir_entry.replace_filename_with_status(filename, file_stat, symlink_file_stat);
return;
}
}
@ -1364,14 +1354,14 @@ inline push_directory_result recursive_directory_iterator_push_directory(detail:
return result;
}
file_status symlink_stat;
file_type symlink_ft = status_error;
// If we are not recursing into symlinks, we are going to have to know if the
// stack top is a symlink, so get symlink_status and verify no error occurred.
if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) == 0u ||
(imp->m_options & static_cast< unsigned int >(directory_options::skip_dangling_symlinks)) != 0u)
{
symlink_stat = imp->m_stack.back()->symlink_status(ec);
symlink_ft = imp->m_stack.back()->symlink_file_type(ec);
if (ec)
return result;
}
@ -1384,12 +1374,12 @@ inline push_directory_result recursive_directory_iterator_push_directory(detail:
// The predicate code has since been rewritten to pass error_code arguments,
// per ticket #5653.
if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) != 0u || !fs::is_symlink(symlink_stat))
if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) != 0u || symlink_ft != symlink_file)
{
file_status stat = imp->m_stack.back()->status(ec);
file_type ft = imp->m_stack.back()->file_type(ec);
if (BOOST_UNLIKELY(!!ec))
{
if (ec == make_error_condition(system::errc::no_such_file_or_directory) && fs::is_symlink(symlink_stat) &&
if (ec == make_error_condition(system::errc::no_such_file_or_directory) && symlink_ft == symlink_file &&
(imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) == static_cast< unsigned int >(directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks))
{
// Skip dangling symlink and continue iteration on the current depth level
@ -1399,7 +1389,7 @@ inline push_directory_result recursive_directory_iterator_push_directory(detail:
return result;
}
if (!fs::is_directory(stat))
if (ft != directory_file)
return result;
if (BOOST_UNLIKELY((imp->m_stack.size() - 1u) >= static_cast< std::size_t >((std::numeric_limits< int >::max)())))

View File

@ -266,7 +266,16 @@ int cpp_main(int /*argc*/, char* /*argv*/[])
//path::default_name_check(fs::no_check);
fs::directory_entry de("foo/bar");
fs::directory_entry de("foo.bar", fs::file_status(fs::regular_file, fs::owner_all), fs::file_status(fs::directory_file, fs::group_all));
BOOST_TEST(de.path() == "foo.bar");
BOOST_TEST(de.status() == fs::file_status(fs::regular_file, fs::owner_all));
BOOST_TEST(de.symlink_status() == fs::file_status(fs::directory_file, fs::group_all));
BOOST_TEST(de < fs::directory_entry("goo.bar", fs::file_status(), fs::file_status()));
BOOST_TEST(de == fs::directory_entry("foo.bar", fs::file_status(), fs::file_status()));
BOOST_TEST(de != fs::directory_entry("goo.bar", fs::file_status(), fs::file_status()));
de.replace_filename("bar.foo", fs::file_status(), fs::file_status());
BOOST_TEST(de.path() == "bar.foo");
de.replace_leaf("", fs::file_status(), fs::file_status());

View File

@ -24,11 +24,15 @@
#endif
#include <boost/filesystem.hpp> // make sure filesystem.hpp works
#include <boost/filesystem/fstream.hpp> // for BOOST_FILESYSTEM_C_STR
#include <boost/system/error_code.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/detail/lightweight_main.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <cerrno>
using namespace boost::filesystem;
using namespace boost::system;
@ -41,6 +45,15 @@ using std::string;
namespace {
bool cleanup = true;
void create_file(const path& ph, const std::string& contents = std::string())
{
std::ofstream f(BOOST_FILESYSTEM_C_STR(ph));
if (!f)
throw filesystem_error("operations_test create_file", ph, error_code(errno, system_category()));
if (!contents.empty())
f << contents;
}
void check(bool ok, const char* file, int line)
{
if (ok)
@ -252,20 +265,32 @@ void operations_test()
// directory_entry_test ------------------------------------------------------------//
void directory_entry_test()
void directory_entry_test(path const& temp_dir)
{
cout << "directory_entry test..." << endl;
directory_entry de("foo.bar", file_status(regular_file, owner_all), file_status(directory_file, group_all));
create_file(temp_dir / "foo.bar");
create_file(temp_dir / "goo.bar");
create_directory(temp_dir / "bar.foo");
CHECK(de.path() == "foo.bar");
CHECK(de.status() == file_status(regular_file, owner_all));
CHECK(de.symlink_status() == file_status(directory_file, group_all));
CHECK(de < directory_entry("goo.bar"));
CHECK(de == directory_entry("foo.bar"));
CHECK(de != directory_entry("goo.bar"));
directory_entry de(temp_dir / "foo.bar");
CHECK(de.path() == temp_dir / "foo.bar");
CHECK(de.status().type() == regular_file);
CHECK(de.symlink_status().type() == regular_file);
CHECK(de.is_regular_file());
CHECK(de < directory_entry(temp_dir / "goo.bar"));
CHECK(de == directory_entry(temp_dir / "foo.bar"));
CHECK(de != directory_entry(temp_dir / "goo.bar"));
de.replace_filename("bar.foo");
CHECK(de.path() == "bar.foo");
CHECK(de.path() == temp_dir / "bar.foo");
CHECK(de.is_directory());
CHECK(de.status().type() == directory_file);
CHECK(de.symlink_status().type() == directory_file);
boost::filesystem::remove(temp_dir / "bar.foo");
boost::filesystem::remove(temp_dir / "goo.bar");
boost::filesystem::remove(temp_dir / "foo.bar");
}
// directory_entry_overload_test ---------------------------------------------------//
@ -354,7 +379,7 @@ int cpp_main(int argc, char* argv[])
directory_iterator_test();
recursive_directory_iterator_test();
operations_test();
directory_entry_test();
directory_entry_test(temp_dir);
directory_entry_overload_test();
error_handling_test();