diff options
Diffstat (limited to 'lib/libcxx/src/experimental/filesystem/directory_iterator.cpp')
-rw-r--r-- | lib/libcxx/src/experimental/filesystem/directory_iterator.cpp | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/lib/libcxx/src/experimental/filesystem/directory_iterator.cpp b/lib/libcxx/src/experimental/filesystem/directory_iterator.cpp new file mode 100644 index 00000000000..fa217ba7a12 --- /dev/null +++ b/lib/libcxx/src/experimental/filesystem/directory_iterator.cpp @@ -0,0 +1,256 @@ +#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 |