// Class filesystem::directory_entry etc. -*- C++ -*-
-// Copyright (C) 2014-2015 Free Software Foundation, Inc.
+// Copyright (C) 2014-2021 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
+#ifndef _GLIBCXX_USE_CXX11_ABI
+# define _GLIBCXX_USE_CXX11_ABI 1
+#endif
+
+#include <bits/largefile-config.h>
#include <experimental/filesystem>
+
+#ifndef _GLIBCXX_HAVE_DIRENT_H
+# error "the <dirent.h> header is needed to build the Filesystem TS"
+#endif
+
#include <utility>
#include <stack>
#include <string.h>
#include <errno.h>
-#ifdef _GLIBCXX_HAVE_DIRENT_H
-# ifdef _GLIBCXX_HAVE_SYS_TYPES_H
-# include <sys/types.h>
-# endif
-# include <dirent.h>
-#else
-# error "the <dirent.h> header is needed to build the Filesystem TS"
-#endif
-
-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
-# undef opendir
-# define opendir _wopendir
-#endif
+#define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
+ namespace experimental { namespace filesystem {
+#define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
+#include "dir-common.h"
namespace fs = std::experimental::filesystem;
+namespace posix = std::filesystem::__gnu_posix;
-struct fs::_Dir
+struct fs::_Dir : std::filesystem::_Dir_base
{
- _Dir() : dirp(nullptr) { }
-
- _Dir(DIR* dirp, const fs::path& path) : dirp(dirp), path(path) { }
-
- _Dir(_Dir&& d)
- : dirp(std::exchange(d.dirp, nullptr)), path(std::move(d.path)),
- entry(std::move(d.entry)), type(d.type)
- { }
-
- _Dir& operator=(_Dir&&) = delete;
+ _Dir(const fs::path& p, bool skip_permission_denied, error_code& ec)
+ : _Dir_base(p.c_str(), skip_permission_denied, ec)
+ {
+ if (!ec)
+ path = p;
+ }
- ~_Dir() { if (dirp) ::closedir(dirp); }
+ _Dir(posix::DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
- bool advance(std::error_code*, directory_options = directory_options::none);
+ _Dir(_Dir&&) = default;
- DIR* dirp;
- fs::path path;
- directory_entry entry;
- file_type type = file_type::none;
-};
+ // Returns false when the end of the directory entries is reached.
+ // Reports errors by setting ec.
+ bool advance(bool skip_permission_denied, error_code& ec) noexcept
+ {
+ if (const auto entp = _Dir_base::advance(skip_permission_denied, ec))
+ {
+ entry = fs::directory_entry{path / entp->d_name};
+ type = get_file_type(*entp);
+ return true;
+ }
+ else if (!ec)
+ {
+ // reached the end
+ entry = {};
+ type = file_type::none;
+ }
+ return false;
+ }
-namespace
-{
- template<typename Bitmask>
- inline bool is_set(Bitmask obj, Bitmask bits)
- {
- return (obj & bits) != Bitmask::none;
- }
+ bool advance(error_code& ec) noexcept { return advance(false, ec); }
- // Returns {dirp, p} on success, {nullptr, p} on error.
- // If an ignored EACCES error occurs returns {}.
- fs::_Dir
- open_dir(const fs::path& p, fs::directory_options options, std::error_code* ec)
+ // Returns false when the end of the directory entries is reached.
+ // Reports errors by throwing.
+ bool advance(bool skip_permission_denied = false)
{
+ error_code ec;
+ const bool ok = advance(skip_permission_denied, ec);
if (ec)
- ec->clear();
-
- if (DIR* dirp = ::opendir(p.c_str()))
- return {dirp, p};
-
- const int err = errno;
- if (err == EACCES
- && is_set(options, fs::directory_options::skip_permission_denied))
- return {};
-
- if (!ec)
- _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
- "directory iterator cannot open directory", p,
- std::error_code(err, std::generic_category())));
-
- ec->assign(err, std::generic_category());
- return {nullptr, p};
+ _GLIBCXX_THROW_OR_ABORT(filesystem_error(
+ "directory iterator cannot advance", ec));
+ return ok;
}
- inline fs::file_type
- get_file_type(const dirent& d __attribute__((__unused__)))
+ bool should_recurse(bool follow_symlink, error_code& ec) const
{
-#ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
- switch (d.d_type)
+ file_type type = this->type;
+ if (type == file_type::none || type == file_type::unknown)
{
- case DT_BLK:
- return fs::file_type::block;
- case DT_CHR:
- return fs::file_type::character;
- case DT_DIR:
- return fs::file_type::directory;
- case DT_FIFO:
- return fs::file_type::fifo;
- case DT_LNK:
- return fs::file_type::symlink;
- case DT_REG:
- return fs::file_type::regular;
- case DT_SOCK:
- return fs::file_type::socket;
- case DT_UNKNOWN:
- return fs::file_type::unknown;
- default:
- return fs::file_type::none;
+ type = entry.symlink_status(ec).type();
+ if (ec)
+ return false;
}
-#else
- return fs::file_type::none;
-#endif
- }
- int
- native_readdir(DIR* dirp, ::dirent*& entryp)
- {
-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
- if ((entryp = ::readdir(dirp)))
- return 0;
- return errno;
-#else
- return ::readdir_r(dirp, entryp, &entryp);
-#endif
+ if (type == file_type::directory)
+ return true;
+ if (type == file_type::symlink)
+ return follow_symlink && is_directory(entry.status(ec));
+ return false;
}
-}
-// Returns false when the end of the directory entries is reached.
-// Reports errors by setting ec or throwing.
-bool
-fs::_Dir::advance(error_code* ec, directory_options options)
-{
- if (ec)
- ec->clear();
-
- ::dirent ent;
- ::dirent* result = &ent;
- if (int err = native_readdir(dirp, result))
- {
- if (err == EACCES
- && is_set(options, directory_options::skip_permission_denied))
- return false;
+ fs::path path;
+ directory_entry entry;
+ file_type type = file_type::none;
+};
- if (!ec)
- _GLIBCXX_THROW_OR_ABORT(filesystem_error(
- "directory iterator cannot advance",
- std::error_code(err, std::generic_category())));
- ec->assign(err, std::generic_category());
- return true;
- }
- else if (result != nullptr)
- {
- // skip past dot and dot-dot
- if (!strcmp(ent.d_name, ".") || !strcmp(ent.d_name, ".."))
- return advance(ec, options);
- entry = fs::directory_entry{path / ent.d_name};
- type = get_file_type(ent);
- return true;
- }
- else
+namespace
+{
+ template<typename Bitmask>
+ inline bool
+ is_set(Bitmask obj, Bitmask bits)
{
- // reached the end
- entry = {};
- type = fs::file_type::none;
- return false;
+ return (obj & bits) != Bitmask::none;
}
}
fs::directory_iterator::
-directory_iterator(const path& p, directory_options options, error_code* ec)
+directory_iterator(const path& p, directory_options options, error_code* ecptr)
{
- _Dir dir = open_dir(p, options, ec);
+ const bool skip_permission_denied
+ = is_set(options, directory_options::skip_permission_denied);
+
+ error_code ec;
+ _Dir dir(p, skip_permission_denied, ec);
if (dir.dirp)
{
auto sp = std::make_shared<fs::_Dir>(std::move(dir));
- if (sp->advance(ec, options))
+ if (sp->advance(skip_permission_denied, ec))
_M_dir.swap(sp);
}
- else if (!dir.path.empty())
- {
- // An error occurred, we need a non-empty shared_ptr so that *this will
- // not compare equal to the end iterator.
- _M_dir.reset(static_cast<fs::_Dir*>(nullptr));
- }
+ if (ecptr)
+ *ecptr = ec;
+ else if (ec)
+ _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
+ "directory iterator cannot open directory", p, ec));
}
const fs::directory_entry&
_GLIBCXX_THROW_OR_ABORT(filesystem_error(
"cannot advance non-dereferenceable directory iterator",
std::make_error_code(errc::invalid_argument)));
- if (!_M_dir->advance(nullptr))
+ if (!_M_dir->advance())
_M_dir.reset();
return *this;
}
ec = std::make_error_code(errc::invalid_argument);
return *this;
}
- if (!_M_dir->advance(&ec))
+ if (!_M_dir->advance(ec))
_M_dir.reset();
return *this;
}
-using Dir_iter_pair = std::pair<fs::_Dir, fs::directory_iterator>;
-
struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir>
{
void clear() { c.clear(); }
fs::recursive_directory_iterator::
recursive_directory_iterator(const path& p, directory_options options,
- error_code* ec)
+ error_code* ecptr)
: _M_options(options), _M_pending(true)
{
- if (DIR* dirp = ::opendir(p.c_str()))
+ if (posix::DIR* dirp = posix::opendir(p.c_str()))
{
- _M_dirs = std::make_shared<_Dir_stack>();
- _M_dirs->push(_Dir{ dirp, p });
- if (!_M_dirs->top().advance(ec))
- _M_dirs.reset();
+ if (ecptr)
+ ecptr->clear();
+ auto sp = std::make_shared<_Dir_stack>();
+ sp->push(_Dir{ dirp, p });
+ if (ecptr ? sp->top().advance(*ecptr) : sp->top().advance())
+ _M_dirs.swap(sp);
}
else
{
if (err == EACCES
&& is_set(options, fs::directory_options::skip_permission_denied))
{
- if (ec)
- ec->clear();
+ if (ecptr)
+ ecptr->clear();
return;
}
- if (!ec)
+ if (!ecptr)
_GLIBCXX_THROW_OR_ABORT(filesystem_error(
"recursive directory iterator cannot open directory", p,
std::error_code(err, std::generic_category())));
- ec->assign(err, std::generic_category());
-
- // An error occurred, we need a non-empty shared_ptr so that *this will
- // not compare equal to the end iterator.
- _M_dirs.reset(static_cast<_Dir_stack*>(nullptr));
+ ecptr->assign(err, std::generic_category());
}
}
return *this;
}
-namespace
-{
- bool
- recurse(const fs::_Dir& d, fs::directory_options options, std::error_code& ec)
- {
- bool follow_symlink
- = is_set(options, fs::directory_options::follow_directory_symlink);
-#ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
- if (d.type == fs::file_type::directory)
- return true;
- if (d.type == fs::file_type::symlink && follow_symlink)
- return d.entry.status().type() == fs::file_type::directory;
- if (d.type != fs::file_type::none && d.type != fs::file_type::unknown)
- return false;
-#endif
- const fs::path& path = d.entry.path();
- auto type = fs::symlink_status(path, ec).type();
- if (ec.value())
- return false;
- if (type == fs::file_type::symlink)
- {
- if (!follow_symlink)
- return false;
- type = fs::status(path, ec).type();
- }
- return type == fs::file_type::directory;
- }
-}
-
fs::recursive_directory_iterator&
fs::recursive_directory_iterator::increment(error_code& ec) noexcept
{
return *this;
}
+ const bool follow
+ = is_set(_M_options, directory_options::follow_directory_symlink);
+ const bool skip_permission_denied
+ = is_set(_M_options, directory_options::skip_permission_denied);
+
auto& top = _M_dirs->top();
- if (std::exchange(_M_pending, true) && recurse(top, _M_options, ec))
+ if (std::exchange(_M_pending, true) && top.should_recurse(follow, ec))
{
- _Dir dir = open_dir(top.entry.path(), _M_options, &ec);
+ _Dir dir(top.entry.path(), skip_permission_denied, ec);
if (ec)
- return *this;
+ {
+ _M_dirs.reset();
+ return *this;
+ }
if (dir.dirp)
_M_dirs->push(std::move(dir));
}
- while (!_M_dirs->top().advance(&ec, _M_options) && !ec)
+ while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec)
{
_M_dirs->pop();
if (_M_dirs->empty())
}
void
-fs::recursive_directory_iterator::pop()
+fs::recursive_directory_iterator::pop(error_code& ec)
{
if (!_M_dirs)
- _GLIBCXX_THROW_OR_ABORT(filesystem_error(
- "cannot pop non-dereferenceable recursive directory iterator",
- std::make_error_code(errc::invalid_argument)));
+ {
+ ec = std::make_error_code(errc::invalid_argument);
+ return;
+ }
+
+ const bool skip_permission_denied
+ = is_set(_M_options, directory_options::skip_permission_denied);
do {
_M_dirs->pop();
if (_M_dirs->empty())
{
_M_dirs.reset();
+ ec.clear();
return;
}
- } while (!_M_dirs->top().advance(nullptr, _M_options));
+ } while (!_M_dirs->top().advance(skip_permission_denied, ec));
+}
+
+void
+fs::recursive_directory_iterator::pop()
+{
+ error_code ec;
+ pop(ec);
+ if (ec)
+ _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
+ ? "recursive directory iterator cannot pop"
+ : "non-dereferenceable recursive directory iterator cannot pop",
+ ec));
}