123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- #include "experimental/filesystem"
- #include <dirent.h>
- #include <errno.h>
- _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
- namespace { namespace detail {
- inline error_code capture_errno() {
- _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
- return error_code{errno, std::generic_category()};
- }
- template <class ...Args>
- inline bool capture_error_or_throw(std::error_code* user_ec,
- const char* msg, Args&&... args)
- {
- std::error_code my_ec = capture_errno();
- if (user_ec) {
- *user_ec = my_ec;
- return true;
- }
- __libcpp_throw(filesystem_error(msg, std::forward<Args>(args)..., my_ec));
- return false;
- }
- template <class ...Args>
- inline bool set_or_throw(std::error_code& my_ec,
- std::error_code* user_ec,
- const char* msg, Args&&... args)
- {
- if (user_ec) {
- *user_ec = my_ec;
- return true;
- }
- __libcpp_throw(filesystem_error(msg, std::forward<Args>(args)..., my_ec));
- return false;
- }
- typedef path::string_type string_type;
- inline string_type posix_readdir(DIR *dir_stream, error_code& ec) {
- struct dirent* dir_entry_ptr = nullptr;
- errno = 0; // zero errno in order to detect errors
- if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {
- ec = capture_errno();
- return {};
- } else {
- ec.clear();
- return dir_entry_ptr->d_name;
- }
- }
- }} // namespace detail
- using detail::set_or_throw;
- class __dir_stream {
- public:
- __dir_stream() = delete;
- __dir_stream& operator=(const __dir_stream&) = delete;
- __dir_stream(__dir_stream&& other) noexcept
- : __stream_(other.__stream_), __root_(std::move(other.__root_)),
- __entry_(std::move(other.__entry_))
- {
- other.__stream_ = nullptr;
- }
- __dir_stream(const path& root, directory_options opts, error_code& ec)
- : __stream_(nullptr),
- __root_(root)
- {
- if ((__stream_ = ::opendir(root.c_str())) == nullptr) {
- ec = detail::capture_errno();
- const bool allow_eacess =
- bool(opts & directory_options::skip_permission_denied);
- if (allow_eacess && ec.value() == EACCES)
- ec.clear();
- return;
- }
- advance(ec);
- }
- ~__dir_stream() noexcept
- { if (__stream_) close(); }
- bool good() const noexcept { return __stream_ != nullptr; }
- bool advance(error_code &ec) {
- while (true) {
- auto str = detail::posix_readdir(__stream_, ec);
- if (str == "." || str == "..") {
- continue;
- } else if (ec || str.empty()) {
- close();
- return false;
- } else {
- __entry_.assign(__root_ / str);
- return true;
- }
- }
- }
- private:
- std::error_code close() noexcept {
- std::error_code m_ec;
- if (::closedir(__stream_) == -1)
- m_ec = detail::capture_errno();
- __stream_ = nullptr;
- return m_ec;
- }
- DIR * __stream_{nullptr};
- public:
- path __root_;
- directory_entry __entry_;
- };
- // directory_iterator
- directory_iterator::directory_iterator(const path& p, error_code *ec,
- directory_options opts)
- {
- std::error_code m_ec;
- __imp_ = make_shared<__dir_stream>(p, opts, m_ec);
- if (ec) *ec = m_ec;
- if (!__imp_->good()) {
- __imp_.reset();
- if (m_ec)
- set_or_throw(m_ec, ec,
- "directory_iterator::directory_iterator(...)", p);
- }
- }
- directory_iterator& directory_iterator::__increment(error_code *ec)
- {
- _LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
- std::error_code m_ec;
- if (!__imp_->advance(m_ec)) {
- __imp_.reset();
- if (m_ec)
- set_or_throw(m_ec, ec, "directory_iterator::operator++()");
- } else {
- if (ec) ec->clear();
- }
- return *this;
- }
- directory_entry const& directory_iterator::__deref() const {
- _LIBCPP_ASSERT(__imp_, "Attempting to dereference an invalid iterator");
- return __imp_->__entry_;
- }
- // recursive_directory_iterator
- struct recursive_directory_iterator::__shared_imp {
- stack<__dir_stream> __stack_;
- directory_options __options_;
- };
- recursive_directory_iterator::recursive_directory_iterator(const path& p,
- directory_options opt, error_code *ec)
- : __imp_(nullptr), __rec_(true)
- {
- if (ec) ec->clear();
- std::error_code m_ec;
- __dir_stream new_s(p, opt, m_ec);
- if (m_ec) set_or_throw(m_ec, ec, "recursive_directory_iterator", p);
- if (m_ec || !new_s.good()) return;
- __imp_ = _VSTD::make_shared<__shared_imp>();
- __imp_->__options_ = opt;
- __imp_->__stack_.push(_VSTD::move(new_s));
- }
- void recursive_directory_iterator::__pop(error_code* ec)
- {
- _LIBCPP_ASSERT(__imp_, "Popping the end iterator");
- if (ec) ec->clear();
- __imp_->__stack_.pop();
- if (__imp_->__stack_.size() == 0)
- __imp_.reset();
- else
- __advance(ec);
- }
- directory_options recursive_directory_iterator::options() const {
- return __imp_->__options_;
- }
- int recursive_directory_iterator::depth() const {
- return __imp_->__stack_.size() - 1;
- }
- const directory_entry& recursive_directory_iterator::__deref() const {
- return __imp_->__stack_.top().__entry_;
- }
- recursive_directory_iterator&
- recursive_directory_iterator::__increment(error_code *ec)
- {
- if (ec) ec->clear();
- if (recursion_pending()) {
- if (__try_recursion(ec) || (ec && *ec))
- return *this;
- }
- __rec_ = true;
- __advance(ec);
- return *this;
- }
- void recursive_directory_iterator::__advance(error_code* ec) {
- // REQUIRES: ec must be cleared before calling this function.
- const directory_iterator end_it;
- auto& stack = __imp_->__stack_;
- std::error_code m_ec;
- while (stack.size() > 0) {
- if (stack.top().advance(m_ec))
- return;
- if (m_ec) break;
- stack.pop();
- }
- __imp_.reset();
- if (m_ec)
- set_or_throw(m_ec, ec, "recursive_directory_iterator::operator++()");
- }
- bool recursive_directory_iterator::__try_recursion(error_code *ec) {
- bool rec_sym =
- bool(options() & directory_options::follow_directory_symlink);
- auto& curr_it = __imp_->__stack_.top();
- if (is_directory(curr_it.__entry_.status()) &&
- (!is_symlink(curr_it.__entry_.symlink_status()) || rec_sym))
- {
- std::error_code m_ec;
- __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
- if (new_it.good()) {
- __imp_->__stack_.push(_VSTD::move(new_it));
- return true;
- }
- if (m_ec) {
- __imp_.reset();
- set_or_throw(m_ec, ec,
- "recursive_directory_iterator::operator++()");
- }
- }
- return false;
- }
- _LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
|