Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / libcxx / src / filesystem / directory_iterator.cpp
blob151fb2fb621af76957f0ab6881dbe552b63528f7
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-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include <__assert>
10 #include <__config>
11 #include <errno.h>
12 #include <filesystem>
13 #include <stack>
14 #include <utility>
16 #include "error.h"
17 #include "file_descriptor.h"
19 #if defined(_LIBCPP_WIN32API)
20 # define WIN32_LEAN_AND_MEAN
21 # define NOMINMAX
22 # include <windows.h>
23 #else
24 # include <dirent.h> // for DIR & friends
25 #endif
27 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
29 using detail::ErrorHandler;
31 #if defined(_LIBCPP_WIN32API)
32 class __dir_stream {
33 public:
34 __dir_stream() = delete;
35 __dir_stream& operator=(const __dir_stream&) = delete;
37 __dir_stream(__dir_stream&& __ds) noexcept : __stream_(__ds.__stream_),
38 __root_(std::move(__ds.__root_)),
39 __entry_(std::move(__ds.__entry_)) {
40 __ds.__stream_ = INVALID_HANDLE_VALUE;
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;
49 __stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_);
50 if (__stream_ == INVALID_HANDLE_VALUE) {
51 ec = detail::make_windows_error(GetLastError());
52 const bool ignore_permission_denied =
53 bool(opts & directory_options::skip_permission_denied);
54 if (ignore_permission_denied &&
55 ec.value() == static_cast<int>(errc::permission_denied))
56 ec.clear();
57 return;
59 if (!assign())
60 advance(ec);
63 ~__dir_stream() noexcept {
64 if (__stream_ == INVALID_HANDLE_VALUE)
65 return;
66 close();
69 bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }
71 bool advance(error_code& ec) {
72 while (::FindNextFileW(__stream_, &__data_)) {
73 if (assign())
74 return true;
76 close();
77 return false;
80 bool assign() {
81 if (!wcscmp(__data_.cFileName, L".") || !wcscmp(__data_.cFileName, L".."))
82 return false;
83 // FIXME: Cache more of this
84 //directory_entry::__cached_data cdata;
85 //cdata.__type_ = get_file_type(__data_);
86 //cdata.__size_ = get_file_size(__data_);
87 //cdata.__write_time_ = get_write_time(__data_);
88 __entry_.__assign_iter_entry(
89 __root_ / __data_.cFileName,
90 directory_entry::__create_iter_result(detail::get_file_type(__data_)));
91 return true;
94 private:
95 error_code close() noexcept {
96 error_code ec;
97 if (!::FindClose(__stream_))
98 ec = detail::make_windows_error(GetLastError());
99 __stream_ = INVALID_HANDLE_VALUE;
100 return ec;
103 HANDLE __stream_{INVALID_HANDLE_VALUE};
104 WIN32_FIND_DATAW __data_;
106 public:
107 path __root_;
108 directory_entry __entry_;
110 #else
111 class __dir_stream {
112 public:
113 __dir_stream() = delete;
114 __dir_stream& operator=(const __dir_stream&) = delete;
116 __dir_stream(__dir_stream&& other) noexcept : __stream_(other.__stream_),
117 __root_(std::move(other.__root_)),
118 __entry_(std::move(other.__entry_)) {
119 other.__stream_ = nullptr;
122 __dir_stream(const path& root, directory_options opts, error_code& ec)
123 : __stream_(nullptr), __root_(root) {
124 if ((__stream_ = ::opendir(root.c_str())) == nullptr) {
125 ec = detail::capture_errno();
126 const bool allow_eacces =
127 bool(opts & directory_options::skip_permission_denied);
128 if (allow_eacces && ec.value() == EACCES)
129 ec.clear();
130 return;
132 advance(ec);
135 ~__dir_stream() noexcept {
136 if (__stream_)
137 close();
140 bool good() const noexcept { return __stream_ != nullptr; }
142 bool advance(error_code& ec) {
143 while (true) {
144 auto str_type_pair = detail::posix_readdir(__stream_, ec);
145 auto& str = str_type_pair.first;
146 if (str == "." || str == "..") {
147 continue;
148 } else if (ec || str.empty()) {
149 close();
150 return false;
151 } else {
152 __entry_.__assign_iter_entry(
153 __root_ / str,
154 directory_entry::__create_iter_result(str_type_pair.second));
155 return true;
160 private:
161 error_code close() noexcept {
162 error_code m_ec;
163 if (::closedir(__stream_) == -1)
164 m_ec = detail::capture_errno();
165 __stream_ = nullptr;
166 return m_ec;
169 DIR* __stream_{nullptr};
171 public:
172 path __root_;
173 directory_entry __entry_;
175 #endif
177 // directory_iterator
179 directory_iterator::directory_iterator(const path& p, error_code* ec,
180 directory_options opts) {
181 ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p);
183 error_code m_ec;
184 __imp_ = make_shared<__dir_stream>(p, opts, m_ec);
185 if (ec)
186 *ec = m_ec;
187 if (!__imp_->good()) {
188 __imp_.reset();
189 if (m_ec)
190 err.report(m_ec);
194 directory_iterator& directory_iterator::__increment(error_code* ec) {
195 _LIBCPP_ASSERT_UNCATEGORIZED(__imp_, "Attempting to increment an invalid iterator");
196 ErrorHandler<void> err("directory_iterator::operator++()", ec);
198 error_code m_ec;
199 if (!__imp_->advance(m_ec)) {
200 path root = std::move(__imp_->__root_);
201 __imp_.reset();
202 if (m_ec)
203 err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());
205 return *this;
208 directory_entry const& directory_iterator::__dereference() const {
209 _LIBCPP_ASSERT_UNCATEGORIZED(__imp_, "Attempting to dereference an invalid iterator");
210 return __imp_->__entry_;
213 // recursive_directory_iterator
215 struct recursive_directory_iterator::__shared_imp {
216 stack<__dir_stream> __stack_;
217 directory_options __options_;
220 recursive_directory_iterator::recursive_directory_iterator(
221 const path& p, directory_options opt, error_code* ec)
222 : __imp_(nullptr), __rec_(true) {
223 ErrorHandler<void> err("recursive_directory_iterator", ec, &p);
225 error_code m_ec;
226 __dir_stream new_s(p, opt, m_ec);
227 if (m_ec)
228 err.report(m_ec);
229 if (m_ec || !new_s.good())
230 return;
232 __imp_ = make_shared<__shared_imp>();
233 __imp_->__options_ = opt;
234 __imp_->__stack_.push(std::move(new_s));
237 void recursive_directory_iterator::__pop(error_code* ec) {
238 _LIBCPP_ASSERT_UNCATEGORIZED(__imp_, "Popping the end iterator");
239 if (ec)
240 ec->clear();
241 __imp_->__stack_.pop();
242 if (__imp_->__stack_.size() == 0)
243 __imp_.reset();
244 else
245 __advance(ec);
248 directory_options recursive_directory_iterator::options() const {
249 return __imp_->__options_;
252 int recursive_directory_iterator::depth() const {
253 return __imp_->__stack_.size() - 1;
256 const directory_entry& recursive_directory_iterator::__dereference() const {
257 return __imp_->__stack_.top().__entry_;
260 recursive_directory_iterator&
261 recursive_directory_iterator::__increment(error_code* ec) {
262 if (ec)
263 ec->clear();
264 if (recursion_pending()) {
265 if (__try_recursion(ec) || (ec && *ec))
266 return *this;
268 __rec_ = true;
269 __advance(ec);
270 return *this;
273 void recursive_directory_iterator::__advance(error_code* ec) {
274 ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
276 const directory_iterator end_it;
277 auto& stack = __imp_->__stack_;
278 error_code m_ec;
279 while (stack.size() > 0) {
280 if (stack.top().advance(m_ec))
281 return;
282 if (m_ec)
283 break;
284 stack.pop();
287 if (m_ec) {
288 path root = std::move(stack.top().__root_);
289 __imp_.reset();
290 err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());
291 } else {
292 __imp_.reset();
296 bool recursive_directory_iterator::__try_recursion(error_code* ec) {
297 ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
299 bool rec_sym = bool(options() & directory_options::follow_directory_symlink);
301 auto& curr_it = __imp_->__stack_.top();
303 bool skip_rec = false;
304 error_code m_ec;
305 if (!rec_sym) {
306 file_status st(curr_it.__entry_.__get_sym_ft(&m_ec));
307 if (m_ec && status_known(st))
308 m_ec.clear();
309 if (m_ec || is_symlink(st) || !is_directory(st))
310 skip_rec = true;
311 } else {
312 file_status st(curr_it.__entry_.__get_ft(&m_ec));
313 if (m_ec && status_known(st))
314 m_ec.clear();
315 if (m_ec || !is_directory(st))
316 skip_rec = true;
319 if (!skip_rec) {
320 __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
321 if (new_it.good()) {
322 __imp_->__stack_.push(std::move(new_it));
323 return true;
326 if (m_ec) {
327 const bool allow_eacess =
328 bool(__imp_->__options_ & directory_options::skip_permission_denied);
329 if (m_ec.value() == EACCES && allow_eacess) {
330 if (ec)
331 ec->clear();
332 } else {
333 path at_ent = std::move(curr_it.__entry_.__p_);
334 __imp_.reset();
335 err.report(m_ec, "attempting recursion into " PATH_CSTR_FMT,
336 at_ent.c_str());
339 return false;
342 _LIBCPP_END_NAMESPACE_FILESYSTEM