summaryrefslogtreecommitdiffstats
path: root/lib/libcxx/src/experimental/filesystem/directory_iterator.cpp
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2016-09-03 18:39:32 +0000
committerpatrick <patrick@openbsd.org>2016-09-03 18:39:32 +0000
commitfe1ccca2776129541f1c4f645e4d1de89a22a031 (patch)
tree8e4fd7a09a5a15a9498fd2d88dcddbd40e999c79 /lib/libcxx/src/experimental/filesystem/directory_iterator.cpp
parentFixed missing null check in switchctl.c (diff)
downloadwireguard-openbsd-fe1ccca2776129541f1c4f645e4d1de89a22a031.tar.xz
wireguard-openbsd-fe1ccca2776129541f1c4f645e4d1de89a22a031.zip
Import libc++ 3.9.0
Diffstat (limited to 'lib/libcxx/src/experimental/filesystem/directory_iterator.cpp')
-rw-r--r--lib/libcxx/src/experimental/filesystem/directory_iterator.cpp256
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