mirror of git://gcc.gnu.org/git/gcc.git
325 lines
8.1 KiB
C++
325 lines
8.1 KiB
C++
// Class filesystem::directory_entry etc. -*- C++ -*-
|
|
|
|
// Copyright (C) 2014-2018 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
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// Under Section 7 of GPL version 3, you are granted additional
|
|
// permissions described in the GCC Runtime Library Exception, version
|
|
// 3.1, as published by the Free Software Foundation.
|
|
|
|
// You should have received a copy of the GNU General Public License and
|
|
// a copy of the GCC Runtime Library Exception along with this program;
|
|
// 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 <filesystem>
|
|
#include <experimental/filesystem>
|
|
#include <utility>
|
|
#include <stack>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
|
|
#define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
|
|
#include "dir-common.h"
|
|
|
|
namespace fs = std::filesystem;
|
|
namespace posix = std::filesystem::__gnu_posix;
|
|
|
|
template class std::__shared_ptr<fs::_Dir>;
|
|
template class std::__shared_ptr<fs::recursive_directory_iterator::_Dir_stack>;
|
|
|
|
struct fs::_Dir : _Dir_base
|
|
{
|
|
_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(posix::DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
|
|
|
|
_Dir(_Dir&&) = default;
|
|
|
|
// 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))
|
|
{
|
|
auto name = path;
|
|
name /= entp->d_name;
|
|
entry = fs::directory_entry{std::move(name), get_file_type(*entp)};
|
|
return true;
|
|
}
|
|
else if (!ec)
|
|
{
|
|
// reached the end
|
|
entry = {};
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool advance(error_code& ec) noexcept { return advance(false, 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)
|
|
_GLIBCXX_THROW_OR_ABORT(filesystem_error(
|
|
"directory iterator cannot advance", ec));
|
|
return ok;
|
|
}
|
|
|
|
bool should_recurse(bool follow_symlink, error_code& ec) const
|
|
{
|
|
file_type type = entry._M_type;
|
|
if (type == file_type::none || type == file_type::unknown)
|
|
{
|
|
type = entry.symlink_status(ec).type();
|
|
if (ec)
|
|
return false;
|
|
}
|
|
|
|
if (type == file_type::directory)
|
|
return true;
|
|
if (type == file_type::symlink)
|
|
return follow_symlink && is_directory(entry.status(ec));
|
|
return false;
|
|
}
|
|
|
|
fs::path path;
|
|
directory_entry entry;
|
|
};
|
|
|
|
namespace
|
|
{
|
|
template<typename Bitmask>
|
|
inline bool
|
|
is_set(Bitmask obj, Bitmask bits)
|
|
{
|
|
return (obj & bits) != Bitmask::none;
|
|
}
|
|
}
|
|
|
|
fs::directory_iterator::
|
|
directory_iterator(const path& p, directory_options options, error_code* ecptr)
|
|
{
|
|
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(skip_permission_denied, ec))
|
|
_M_dir.swap(sp);
|
|
}
|
|
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&
|
|
fs::directory_iterator::operator*() const
|
|
{
|
|
if (!_M_dir)
|
|
_GLIBCXX_THROW_OR_ABORT(filesystem_error(
|
|
"non-dereferenceable directory iterator",
|
|
std::make_error_code(errc::invalid_argument)));
|
|
return _M_dir->entry;
|
|
}
|
|
|
|
fs::directory_iterator&
|
|
fs::directory_iterator::operator++()
|
|
{
|
|
if (!_M_dir)
|
|
_GLIBCXX_THROW_OR_ABORT(filesystem_error(
|
|
"cannot advance non-dereferenceable directory iterator",
|
|
std::make_error_code(errc::invalid_argument)));
|
|
if (!_M_dir->advance())
|
|
_M_dir.reset();
|
|
return *this;
|
|
}
|
|
|
|
fs::directory_iterator&
|
|
fs::directory_iterator::increment(error_code& ec)
|
|
{
|
|
if (!_M_dir)
|
|
{
|
|
ec = std::make_error_code(errc::invalid_argument);
|
|
return *this;
|
|
}
|
|
if (!_M_dir->advance(ec))
|
|
_M_dir.reset();
|
|
return *this;
|
|
}
|
|
|
|
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* ecptr)
|
|
: _M_options(options), _M_pending(true)
|
|
{
|
|
if (posix::DIR* dirp = posix::opendir(p.c_str()))
|
|
{
|
|
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
|
|
{
|
|
const int err = errno;
|
|
if (err == EACCES
|
|
&& is_set(options, fs::directory_options::skip_permission_denied))
|
|
{
|
|
if (ecptr)
|
|
ecptr->clear();
|
|
return;
|
|
}
|
|
|
|
if (!ecptr)
|
|
_GLIBCXX_THROW_OR_ABORT(filesystem_error(
|
|
"recursive directory iterator cannot open directory", p,
|
|
std::error_code(err, std::generic_category())));
|
|
|
|
ecptr->assign(err, std::generic_category());
|
|
}
|
|
}
|
|
|
|
fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
|
|
|
|
int
|
|
fs::recursive_directory_iterator::depth() const
|
|
{
|
|
return int(_M_dirs->size()) - 1;
|
|
}
|
|
|
|
const fs::directory_entry&
|
|
fs::recursive_directory_iterator::operator*() const
|
|
{
|
|
return _M_dirs->top().entry;
|
|
}
|
|
|
|
fs::recursive_directory_iterator&
|
|
fs::recursive_directory_iterator::
|
|
operator=(const recursive_directory_iterator& other) noexcept = default;
|
|
|
|
fs::recursive_directory_iterator&
|
|
fs::recursive_directory_iterator::
|
|
operator=(recursive_directory_iterator&& other) noexcept = default;
|
|
|
|
fs::recursive_directory_iterator&
|
|
fs::recursive_directory_iterator::operator++()
|
|
{
|
|
error_code ec;
|
|
increment(ec);
|
|
if (ec)
|
|
_GLIBCXX_THROW_OR_ABORT(filesystem_error(
|
|
"cannot increment recursive directory iterator", ec));
|
|
return *this;
|
|
}
|
|
|
|
fs::recursive_directory_iterator&
|
|
fs::recursive_directory_iterator::increment(error_code& ec)
|
|
{
|
|
if (!_M_dirs)
|
|
{
|
|
ec = std::make_error_code(errc::invalid_argument);
|
|
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) && top.should_recurse(follow, ec))
|
|
{
|
|
_Dir dir(top.entry.path(), skip_permission_denied, ec);
|
|
if (ec)
|
|
{
|
|
_M_dirs.reset();
|
|
return *this;
|
|
}
|
|
if (dir.dirp)
|
|
_M_dirs->push(std::move(dir));
|
|
}
|
|
|
|
while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec)
|
|
{
|
|
_M_dirs->pop();
|
|
if (_M_dirs->empty())
|
|
{
|
|
_M_dirs.reset();
|
|
return *this;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void
|
|
fs::recursive_directory_iterator::pop(error_code& ec)
|
|
{
|
|
if (!_M_dirs)
|
|
{
|
|
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(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));
|
|
}
|