directory_iterator.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. #include "experimental/filesystem"
  2. #include <dirent.h>
  3. #include <errno.h>
  4. _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
  5. namespace { namespace detail {
  6. inline error_code capture_errno() {
  7. _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
  8. return error_code{errno, std::generic_category()};
  9. }
  10. template <class ...Args>
  11. inline bool capture_error_or_throw(std::error_code* user_ec,
  12. const char* msg, Args&&... args)
  13. {
  14. std::error_code my_ec = capture_errno();
  15. if (user_ec) {
  16. *user_ec = my_ec;
  17. return true;
  18. }
  19. __libcpp_throw(filesystem_error(msg, std::forward<Args>(args)..., my_ec));
  20. return false;
  21. }
  22. template <class ...Args>
  23. inline bool set_or_throw(std::error_code& my_ec,
  24. std::error_code* user_ec,
  25. const char* msg, Args&&... args)
  26. {
  27. if (user_ec) {
  28. *user_ec = my_ec;
  29. return true;
  30. }
  31. __libcpp_throw(filesystem_error(msg, std::forward<Args>(args)..., my_ec));
  32. return false;
  33. }
  34. typedef path::string_type string_type;
  35. inline string_type posix_readdir(DIR *dir_stream, error_code& ec) {
  36. struct dirent* dir_entry_ptr = nullptr;
  37. errno = 0; // zero errno in order to detect errors
  38. if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {
  39. ec = capture_errno();
  40. return {};
  41. } else {
  42. ec.clear();
  43. return dir_entry_ptr->d_name;
  44. }
  45. }
  46. }} // namespace detail
  47. using detail::set_or_throw;
  48. class __dir_stream {
  49. public:
  50. __dir_stream() = delete;
  51. __dir_stream& operator=(const __dir_stream&) = delete;
  52. __dir_stream(__dir_stream&& other) noexcept
  53. : __stream_(other.__stream_), __root_(std::move(other.__root_)),
  54. __entry_(std::move(other.__entry_))
  55. {
  56. other.__stream_ = nullptr;
  57. }
  58. __dir_stream(const path& root, directory_options opts, error_code& ec)
  59. : __stream_(nullptr),
  60. __root_(root)
  61. {
  62. if ((__stream_ = ::opendir(root.c_str())) == nullptr) {
  63. ec = detail::capture_errno();
  64. const bool allow_eacess =
  65. bool(opts & directory_options::skip_permission_denied);
  66. if (allow_eacess && ec.value() == EACCES)
  67. ec.clear();
  68. return;
  69. }
  70. advance(ec);
  71. }
  72. ~__dir_stream() noexcept
  73. { if (__stream_) close(); }
  74. bool good() const noexcept { return __stream_ != nullptr; }
  75. bool advance(error_code &ec) {
  76. while (true) {
  77. auto str = detail::posix_readdir(__stream_, ec);
  78. if (str == "." || str == "..") {
  79. continue;
  80. } else if (ec || str.empty()) {
  81. close();
  82. return false;
  83. } else {
  84. __entry_.assign(__root_ / str);
  85. return true;
  86. }
  87. }
  88. }
  89. private:
  90. std::error_code close() noexcept {
  91. std::error_code m_ec;
  92. if (::closedir(__stream_) == -1)
  93. m_ec = detail::capture_errno();
  94. __stream_ = nullptr;
  95. return m_ec;
  96. }
  97. DIR * __stream_{nullptr};
  98. public:
  99. path __root_;
  100. directory_entry __entry_;
  101. };
  102. // directory_iterator
  103. directory_iterator::directory_iterator(const path& p, error_code *ec,
  104. directory_options opts)
  105. {
  106. std::error_code m_ec;
  107. __imp_ = make_shared<__dir_stream>(p, opts, m_ec);
  108. if (ec) *ec = m_ec;
  109. if (!__imp_->good()) {
  110. __imp_.reset();
  111. if (m_ec)
  112. set_or_throw(m_ec, ec,
  113. "directory_iterator::directory_iterator(...)", p);
  114. }
  115. }
  116. directory_iterator& directory_iterator::__increment(error_code *ec)
  117. {
  118. _LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
  119. std::error_code m_ec;
  120. if (!__imp_->advance(m_ec)) {
  121. __imp_.reset();
  122. if (m_ec)
  123. set_or_throw(m_ec, ec, "directory_iterator::operator++()");
  124. } else {
  125. if (ec) ec->clear();
  126. }
  127. return *this;
  128. }
  129. directory_entry const& directory_iterator::__deref() const {
  130. _LIBCPP_ASSERT(__imp_, "Attempting to dereference an invalid iterator");
  131. return __imp_->__entry_;
  132. }
  133. // recursive_directory_iterator
  134. struct recursive_directory_iterator::__shared_imp {
  135. stack<__dir_stream> __stack_;
  136. directory_options __options_;
  137. };
  138. recursive_directory_iterator::recursive_directory_iterator(const path& p,
  139. directory_options opt, error_code *ec)
  140. : __imp_(nullptr), __rec_(true)
  141. {
  142. if (ec) ec->clear();
  143. std::error_code m_ec;
  144. __dir_stream new_s(p, opt, m_ec);
  145. if (m_ec) set_or_throw(m_ec, ec, "recursive_directory_iterator", p);
  146. if (m_ec || !new_s.good()) return;
  147. __imp_ = _VSTD::make_shared<__shared_imp>();
  148. __imp_->__options_ = opt;
  149. __imp_->__stack_.push(_VSTD::move(new_s));
  150. }
  151. void recursive_directory_iterator::__pop(error_code* ec)
  152. {
  153. _LIBCPP_ASSERT(__imp_, "Popping the end iterator");
  154. if (ec) ec->clear();
  155. __imp_->__stack_.pop();
  156. if (__imp_->__stack_.size() == 0)
  157. __imp_.reset();
  158. else
  159. __advance(ec);
  160. }
  161. directory_options recursive_directory_iterator::options() const {
  162. return __imp_->__options_;
  163. }
  164. int recursive_directory_iterator::depth() const {
  165. return __imp_->__stack_.size() - 1;
  166. }
  167. const directory_entry& recursive_directory_iterator::__deref() const {
  168. return __imp_->__stack_.top().__entry_;
  169. }
  170. recursive_directory_iterator&
  171. recursive_directory_iterator::__increment(error_code *ec)
  172. {
  173. if (ec) ec->clear();
  174. if (recursion_pending()) {
  175. if (__try_recursion(ec) || (ec && *ec))
  176. return *this;
  177. }
  178. __rec_ = true;
  179. __advance(ec);
  180. return *this;
  181. }
  182. void recursive_directory_iterator::__advance(error_code* ec) {
  183. // REQUIRES: ec must be cleared before calling this function.
  184. const directory_iterator end_it;
  185. auto& stack = __imp_->__stack_;
  186. std::error_code m_ec;
  187. while (stack.size() > 0) {
  188. if (stack.top().advance(m_ec))
  189. return;
  190. if (m_ec) break;
  191. stack.pop();
  192. }
  193. __imp_.reset();
  194. if (m_ec)
  195. set_or_throw(m_ec, ec, "recursive_directory_iterator::operator++()");
  196. }
  197. bool recursive_directory_iterator::__try_recursion(error_code *ec) {
  198. bool rec_sym =
  199. bool(options() & directory_options::follow_directory_symlink);
  200. auto& curr_it = __imp_->__stack_.top();
  201. if (is_directory(curr_it.__entry_.status()) &&
  202. (!is_symlink(curr_it.__entry_.symlink_status()) || rec_sym))
  203. {
  204. std::error_code m_ec;
  205. __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
  206. if (new_it.good()) {
  207. __imp_->__stack_.push(_VSTD::move(new_it));
  208. return true;
  209. }
  210. if (m_ec) {
  211. __imp_.reset();
  212. set_or_throw(m_ec, ec,
  213. "recursive_directory_iterator::operator++()");
  214. }
  215. }
  216. return false;
  217. }
  218. _LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM