brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.8 KiB · 7d00c49 Raw
324 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include <__assert>10#include <__config>11#include <__memory/shared_ptr.h>12#include <errno.h>13#include <filesystem>14#include <stack>15#include <utility>16 17#include "error.h"18#include "file_descriptor.h"19 20#if defined(_LIBCPP_WIN32API)21#  define WIN32_LEAN_AND_MEAN22#  define NOMINMAX23#  include <windows.h>24#else25#  include <dirent.h> // for DIR & friends26#endif27 28_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM29 30using detail::ErrorHandler;31 32#if defined(_LIBCPP_WIN32API)33class __dir_stream {34public:35  __dir_stream()                               = delete;36  __dir_stream& operator=(const __dir_stream&) = delete;37 38  __dir_stream(__dir_stream&& __ds) noexcept39      : __stream_(__ds.__stream_), __root_(std::move(__ds.__root_)), __entry_(std::move(__ds.__entry_)) {40    __ds.__stream_ = INVALID_HANDLE_VALUE;41  }42 43  __dir_stream(const path& root, directory_options opts, error_code& ec)44      : __stream_(INVALID_HANDLE_VALUE), __root_(root) {45    if (root.native().empty()) {46      ec = make_error_code(errc::no_such_file_or_directory);47      return;48    }49    __stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_);50    if (__stream_ == INVALID_HANDLE_VALUE) {51      ec                                  = detail::get_last_error();52      const bool ignore_permission_denied = bool(opts & directory_options::skip_permission_denied);53      if (ignore_permission_denied && ec == errc::permission_denied)54        ec.clear();55      return;56    }57    if (!assign())58      advance(ec);59  }60 61  ~__dir_stream() noexcept {62    if (__stream_ == INVALID_HANDLE_VALUE)63      return;64    close();65  }66 67  bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }68 69  bool advance(error_code& ec) {70    while (::FindNextFileW(__stream_, &__data_)) {71      if (assign())72        return true;73    }74    close();75    return false;76  }77 78  bool assign() {79    if (!wcscmp(__data_.cFileName, L".") || !wcscmp(__data_.cFileName, L".."))80      return false;81    __entry_.__assign_iter_entry(82        __root_ / __data_.cFileName,83        directory_entry::__create_iter_cached_result(84            detail::get_file_type(__data_),85            detail::get_file_size(__data_),86            detail::get_file_perm(__data_),87            detail::get_write_time(__data_)));88    return true;89  }90 91private:92  error_code close() noexcept {93    error_code ec;94    if (!::FindClose(__stream_))95      ec = detail::get_last_error();96    __stream_ = INVALID_HANDLE_VALUE;97    return ec;98  }99 100  HANDLE __stream_{INVALID_HANDLE_VALUE};101  WIN32_FIND_DATAW __data_;102 103public:104  path __root_;105  directory_entry __entry_;106};107#else108class __dir_stream {109public:110  __dir_stream()                               = delete;111  __dir_stream& operator=(const __dir_stream&) = delete;112 113  __dir_stream(__dir_stream&& other) noexcept114      : __stream_(other.__stream_), __root_(std::move(other.__root_)), __entry_(std::move(other.__entry_)) {115    other.__stream_ = nullptr;116  }117 118  __dir_stream(const path& root, directory_options opts, error_code& ec) : __stream_(nullptr), __root_(root) {119    if ((__stream_ = ::opendir(root.c_str())) == nullptr) {120      ec                      = detail::capture_errno();121      const bool allow_eacces = bool(opts & directory_options::skip_permission_denied);122      if (allow_eacces && ec == errc::permission_denied)123        ec.clear();124      return;125    }126    advance(ec);127  }128 129  ~__dir_stream() noexcept {130    if (__stream_)131      close();132  }133 134  bool good() const noexcept { return __stream_ != nullptr; }135 136  bool advance(error_code& ec) {137    while (true) {138      auto str_type_pair = detail::posix_readdir(__stream_, ec);139      auto& str          = str_type_pair.first;140      if (str == "." || str == "..") {141        continue;142      } else if (ec || str.empty()) {143        close();144        return false;145      } else {146        __entry_.__assign_iter_entry(__root_ / str, directory_entry::__create_iter_result(str_type_pair.second));147        return true;148      }149    }150  }151 152private:153  error_code close() noexcept {154    error_code m_ec;155    if (::closedir(__stream_) == -1)156      m_ec = detail::capture_errno();157    __stream_ = nullptr;158    return m_ec;159  }160 161  DIR* __stream_{nullptr};162 163public:164  path __root_;165  directory_entry __entry_;166};167#endif168 169// directory_iterator170 171directory_iterator::directory_iterator(const path& p, error_code* ec, directory_options opts) {172  ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p);173 174  error_code m_ec;175  __imp_ = make_shared<__dir_stream>(p, opts, m_ec);176  if (ec)177    *ec = m_ec;178  if (!__imp_->good()) {179    __imp_.reset();180    if (m_ec)181      err.report(m_ec);182  }183}184 185directory_iterator& directory_iterator::__increment(error_code* ec) {186  _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Attempting to increment an invalid iterator");187  ErrorHandler<void> err("directory_iterator::operator++()", ec);188 189  error_code m_ec;190  if (!__imp_->advance(m_ec)) {191    path root = std::move(__imp_->__root_);192    __imp_.reset();193    if (m_ec)194      err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());195  }196  return *this;197}198 199directory_entry const& directory_iterator::__dereference() const {200  _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Attempting to dereference an invalid iterator");201  return __imp_->__entry_;202}203 204// recursive_directory_iterator205 206struct recursive_directory_iterator::__shared_imp {207  stack<__dir_stream> __stack_;208  directory_options __options_;209};210 211recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options opt, error_code* ec)212    : __imp_(nullptr), __rec_(true) {213  ErrorHandler<void> err("recursive_directory_iterator", ec, &p);214 215  error_code m_ec;216  __dir_stream new_s(p, opt, m_ec);217  if (m_ec)218    err.report(m_ec);219  if (m_ec || !new_s.good())220    return;221 222  __imp_             = make_shared<__shared_imp>();223  __imp_->__options_ = opt;224  __imp_->__stack_.push(std::move(new_s));225}226 227void recursive_directory_iterator::__pop(error_code* ec) {228  _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Popping the end iterator");229  if (ec)230    ec->clear();231  __imp_->__stack_.pop();232  if (__imp_->__stack_.size() == 0)233    __imp_.reset();234  else235    __advance(ec);236}237 238directory_options recursive_directory_iterator::options() const { return __imp_->__options_; }239 240int recursive_directory_iterator::depth() const { return __imp_->__stack_.size() - 1; }241 242const directory_entry& recursive_directory_iterator::__dereference() const { return __imp_->__stack_.top().__entry_; }243 244recursive_directory_iterator& recursive_directory_iterator::__increment(error_code* ec) {245  if (ec)246    ec->clear();247  if (recursion_pending()) {248    if (__try_recursion(ec) || (ec && *ec))249      return *this;250  }251  __rec_ = true;252  __advance(ec);253  return *this;254}255 256void recursive_directory_iterator::__advance(error_code* ec) {257  ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);258 259  const directory_iterator end_it;260  auto& stack = __imp_->__stack_;261  error_code m_ec;262  while (stack.size() > 0) {263    if (stack.top().advance(m_ec))264      return;265    if (m_ec)266      break;267    stack.pop();268  }269 270  if (m_ec) {271    path root = std::move(stack.top().__root_);272    __imp_.reset();273    err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());274  } else {275    __imp_.reset();276  }277}278 279bool recursive_directory_iterator::__try_recursion(error_code* ec) {280  ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);281 282  bool rec_sym = bool(options() & directory_options::follow_directory_symlink);283 284  auto& curr_it = __imp_->__stack_.top();285 286  bool skip_rec = false;287  error_code m_ec;288  if (!rec_sym) {289    file_status st(curr_it.__entry_.__get_sym_ft(&m_ec));290    if (m_ec && status_known(st))291      m_ec.clear();292    if (m_ec || is_symlink(st) || !is_directory(st))293      skip_rec = true;294  } else {295    file_status st(curr_it.__entry_.__get_ft(&m_ec));296    if (m_ec && status_known(st))297      m_ec.clear();298    if (m_ec || !is_directory(st))299      skip_rec = true;300  }301 302  if (!skip_rec) {303    __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);304    if (new_it.good()) {305      __imp_->__stack_.push(std::move(new_it));306      return true;307    }308  }309  if (m_ec) {310    const bool allow_eacess = bool(__imp_->__options_ & directory_options::skip_permission_denied);311    if (m_ec == errc::permission_denied && allow_eacess) {312      if (ec)313        ec->clear();314    } else {315      path at_ent = std::move(curr_it.__entry_.__p_);316      __imp_.reset();317      err.report(m_ec, "attempting recursion into " PATH_CSTR_FMT, at_ent.c_str());318    }319  }320  return false;321}322 323_LIBCPP_END_NAMESPACE_FILESYSTEM324