1 //===------------------ directory_iterator.cpp ----------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "experimental/filesystem"
12 #if defined(_LIBCPP_WIN32API)
13 #define WIN32_LEAN_AND_MEAN
20 _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
22 namespace { namespace detail {
24 #if !defined(_LIBCPP_WIN32API)
25 inline error_code capture_errno() {
26 _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
27 return error_code{errno, std::generic_category()};
31 template <class ...Args>
32 inline bool set_or_throw(std::error_code& my_ec,
33 std::error_code* user_ec,
34 const char* msg, Args&&... args)
40 __throw_filesystem_error(msg, std::forward<Args>(args)..., my_ec);
44 #if !defined(_LIBCPP_WIN32API)
45 inline path::string_type posix_readdir(DIR *dir_stream, error_code& ec) {
46 struct dirent* dir_entry_ptr = nullptr;
47 errno = 0; // zero errno in order to detect errors
49 if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {
54 return dir_entry_ptr->d_name;
59 }} // namespace detail
61 using detail::set_or_throw;
63 #if defined(_LIBCPP_WIN32API)
66 __dir_stream() = delete;
67 __dir_stream& operator=(const __dir_stream&) = delete;
69 __dir_stream(__dir_stream&& __ds) noexcept
70 : __stream_(__ds.__stream_), __root_(std::move(__ds.__root_)),
71 __entry_(std::move(__ds.__entry_)) {
72 __ds.__stream_ = INVALID_HANDLE_VALUE;
75 __dir_stream(const path& root, directory_options opts, error_code& ec)
76 : __stream_(INVALID_HANDLE_VALUE), __root_(root) {
77 __stream_ = ::FindFirstFile(root.c_str(), &__data_);
78 if (__stream_ == INVALID_HANDLE_VALUE) {
79 ec = error_code(::GetLastError(), std::generic_category());
80 const bool ignore_permission_denied =
81 bool(opts & directory_options::skip_permission_denied);
82 if (ignore_permission_denied && ec.value() == ERROR_ACCESS_DENIED)
88 ~__dir_stream() noexcept {
89 if (__stream_ == INVALID_HANDLE_VALUE)
94 bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }
96 bool advance(error_code& ec) {
97 while (::FindNextFile(__stream_, &__data_)) {
98 if (!strcmp(__data_.cFileName, ".") || strcmp(__data_.cFileName, ".."))
100 __entry_.assign(__root_ / __data_.cFileName);
103 ec = error_code(::GetLastError(), std::generic_category());
109 std::error_code close() noexcept {
111 if (!::FindClose(__stream_))
112 ec = error_code(::GetLastError(), std::generic_category());
113 __stream_ = INVALID_HANDLE_VALUE;
117 HANDLE __stream_{INVALID_HANDLE_VALUE};
118 WIN32_FIND_DATA __data_;
122 directory_entry __entry_;
127 __dir_stream() = delete;
128 __dir_stream& operator=(const __dir_stream&) = delete;
130 __dir_stream(__dir_stream&& other) noexcept
131 : __stream_(other.__stream_), __root_(std::move(other.__root_)),
132 __entry_(std::move(other.__entry_))
134 other.__stream_ = nullptr;
138 __dir_stream(const path& root, directory_options opts, error_code& ec)
139 : __stream_(nullptr),
142 if ((__stream_ = ::opendir(root.c_str())) == nullptr) {
143 ec = detail::capture_errno();
144 const bool allow_eacess =
145 bool(opts & directory_options::skip_permission_denied);
146 if (allow_eacess && ec.value() == EACCES)
153 ~__dir_stream() noexcept
154 { if (__stream_) close(); }
156 bool good() const noexcept { return __stream_ != nullptr; }
158 bool advance(error_code &ec) {
160 auto str = detail::posix_readdir(__stream_, ec);
161 if (str == "." || str == "..") {
163 } else if (ec || str.empty()) {
167 __entry_.assign(__root_ / str);
173 std::error_code close() noexcept {
174 std::error_code m_ec;
175 if (::closedir(__stream_) == -1)
176 m_ec = detail::capture_errno();
181 DIR * __stream_{nullptr};
184 directory_entry __entry_;
188 // directory_iterator
190 directory_iterator::directory_iterator(const path& p, error_code *ec,
191 directory_options opts)
193 std::error_code m_ec;
194 __imp_ = make_shared<__dir_stream>(p, opts, m_ec);
196 if (!__imp_->good()) {
199 set_or_throw(m_ec, ec,
200 "directory_iterator::directory_iterator(...)", p);
204 directory_iterator& directory_iterator::__increment(error_code *ec)
206 _LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
207 std::error_code m_ec;
208 if (!__imp_->advance(m_ec)) {
211 set_or_throw(m_ec, ec, "directory_iterator::operator++()");
219 directory_entry const& directory_iterator::__dereference() const {
220 _LIBCPP_ASSERT(__imp_, "Attempting to dereference an invalid iterator");
221 return __imp_->__entry_;
224 // recursive_directory_iterator
226 struct recursive_directory_iterator::__shared_imp {
227 stack<__dir_stream> __stack_;
228 directory_options __options_;
231 recursive_directory_iterator::recursive_directory_iterator(const path& p,
232 directory_options opt, error_code *ec)
233 : __imp_(nullptr), __rec_(true)
236 std::error_code m_ec;
237 __dir_stream new_s(p, opt, m_ec);
238 if (m_ec) set_or_throw(m_ec, ec, "recursive_directory_iterator", p);
239 if (m_ec || !new_s.good()) return;
241 __imp_ = _VSTD::make_shared<__shared_imp>();
242 __imp_->__options_ = opt;
243 __imp_->__stack_.push(_VSTD::move(new_s));
246 void recursive_directory_iterator::__pop(error_code* ec)
248 _LIBCPP_ASSERT(__imp_, "Popping the end iterator");
250 __imp_->__stack_.pop();
251 if (__imp_->__stack_.size() == 0)
257 directory_options recursive_directory_iterator::options() const {
258 return __imp_->__options_;
261 int recursive_directory_iterator::depth() const {
262 return __imp_->__stack_.size() - 1;
265 const directory_entry& recursive_directory_iterator::__dereference() const {
266 return __imp_->__stack_.top().__entry_;
269 recursive_directory_iterator&
270 recursive_directory_iterator::__increment(error_code *ec)
273 if (recursion_pending()) {
274 if (__try_recursion(ec) || (ec && *ec))
282 void recursive_directory_iterator::__advance(error_code* ec) {
283 // REQUIRES: ec must be cleared before calling this function.
284 const directory_iterator end_it;
285 auto& stack = __imp_->__stack_;
286 std::error_code m_ec;
287 while (stack.size() > 0) {
288 if (stack.top().advance(m_ec))
295 set_or_throw(m_ec, ec, "recursive_directory_iterator::operator++()");
298 bool recursive_directory_iterator::__try_recursion(error_code *ec) {
300 bool(options() & directory_options::follow_directory_symlink);
302 auto& curr_it = __imp_->__stack_.top();
304 bool skip_rec = false;
305 std::error_code m_ec;
307 file_status st = curr_it.__entry_.symlink_status(m_ec);
308 if (m_ec && status_known(st))
310 if (m_ec || is_symlink(st) || !is_directory(st))
313 file_status st = curr_it.__entry_.status(m_ec);
314 if (m_ec && status_known(st))
316 if (m_ec || !is_directory(st))
321 __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
323 __imp_->__stack_.push(_VSTD::move(new_it));
328 const bool allow_eacess = bool(__imp_->__options_
329 & directory_options::skip_permission_denied);
330 if (m_ec.value() == EACCES && allow_eacess) {
334 set_or_throw(m_ec, ec,
335 "recursive_directory_iterator::operator++()");
342 _LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM