1// -*- C++ -*-
2//===----------------------------------------------------------------------===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef _LIBCPP___FILESYSTEM_DIRECTORY_ENTRY_H
11#define _LIBCPP___FILESYSTEM_DIRECTORY_ENTRY_H
12
13#include <__chrono/time_point.h>
14#include <__compare/ordering.h>
15#include <__config>
16#include <__filesystem/file_status.h>
17#include <__filesystem/file_time_type.h>
18#include <__filesystem/file_type.h>
19#include <__filesystem/filesystem_error.h>
20#include <__filesystem/operations.h>
21#include <__filesystem/path.h>
22#include <__filesystem/perms.h>
23#include <__fwd/ostream.h>
24#include <__system_error/errc.h>
25#include <__system_error/error_category.h>
26#include <__system_error/error_code.h>
27#include <__system_error/error_condition.h>
28#include <__utility/move.h>
29#include <__utility/unreachable.h>
30#include <cstdint>
31
32#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
33# pragma GCC system_header
34#endif
35
36_LIBCPP_PUSH_MACROS
37#include <__undef_macros>
38
39#if _LIBCPP_STD_VER >= 17 && _LIBCPP_HAS_FILESYSTEM
40
41_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
42
43class directory_entry {
44 typedef filesystem::path _Path;
45
46public:
47 // constructors and destructors
48 _LIBCPP_HIDE_FROM_ABI directory_entry() noexcept = default;
49 _LIBCPP_HIDE_FROM_ABI directory_entry(directory_entry const&) = default;
50 _LIBCPP_HIDE_FROM_ABI directory_entry(directory_entry&&) noexcept = default;
51
52 _LIBCPP_HIDE_FROM_ABI explicit directory_entry(_Path const& __p) : __p_(__p) {
53 error_code __ec;
54 __refresh(ec: &__ec);
55 }
56
57 _LIBCPP_HIDE_FROM_ABI directory_entry(_Path const& __p, error_code& __ec) : __p_(__p) { __refresh(ec: &__ec); }
58
59 _LIBCPP_HIDE_FROM_ABI ~directory_entry() {}
60
61 _LIBCPP_HIDE_FROM_ABI directory_entry& operator=(directory_entry const&) = default;
62 _LIBCPP_HIDE_FROM_ABI directory_entry& operator=(directory_entry&&) noexcept = default;
63
64 _LIBCPP_HIDE_FROM_ABI void assign(_Path const& __p) {
65 __p_ = __p;
66 error_code __ec;
67 __refresh(ec: &__ec);
68 }
69
70 _LIBCPP_HIDE_FROM_ABI void assign(_Path const& __p, error_code& __ec) {
71 __p_ = __p;
72 __refresh(ec: &__ec);
73 }
74
75 _LIBCPP_HIDE_FROM_ABI void replace_filename(_Path const& __p) {
76 __p_.replace_filename(replacement: __p);
77 error_code __ec;
78 __refresh(ec: &__ec);
79 }
80
81 _LIBCPP_HIDE_FROM_ABI void replace_filename(_Path const& __p, error_code& __ec) {
82 __p_ = __p_.parent_path() / __p;
83 __refresh(ec: &__ec);
84 }
85
86 _LIBCPP_HIDE_FROM_ABI void refresh() { __refresh(); }
87
88 _LIBCPP_HIDE_FROM_ABI void refresh(error_code& __ec) noexcept { __refresh(ec: &__ec); }
89
90 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _Path const& path() const noexcept { return __p_; }
91
92 _LIBCPP_HIDE_FROM_ABI operator const _Path&() const noexcept { return __p_; }
93
94 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool exists() const { return filesystem::exists(s: file_status{__get_ft()}); }
95
96 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool exists(error_code& __ec) const noexcept {
97 return filesystem::exists(s: file_status{__get_ft(ec: &__ec)});
98 }
99
100 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_block_file() const { return __get_ft() == file_type::block; }
101
102 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_block_file(error_code& __ec) const noexcept {
103 return __get_ft(ec: &__ec) == file_type::block;
104 }
105
106 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_character_file() const { return __get_ft() == file_type::character; }
107
108 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_character_file(error_code& __ec) const noexcept {
109 return __get_ft(ec: &__ec) == file_type::character;
110 }
111
112 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_directory() const { return __get_ft() == file_type::directory; }
113
114 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_directory(error_code& __ec) const noexcept {
115 return __get_ft(ec: &__ec) == file_type::directory;
116 }
117
118 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_fifo() const { return __get_ft() == file_type::fifo; }
119
120 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_fifo(error_code& __ec) const noexcept {
121 return __get_ft(ec: &__ec) == file_type::fifo;
122 }
123
124 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_other() const { return filesystem::is_other(s: file_status{__get_ft()}); }
125
126 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_other(error_code& __ec) const noexcept {
127 return filesystem::is_other(s: file_status{__get_ft(ec: &__ec)});
128 }
129
130 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_regular_file() const { return __get_ft() == file_type::regular; }
131
132 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_regular_file(error_code& __ec) const noexcept {
133 return __get_ft(ec: &__ec) == file_type::regular;
134 }
135
136 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_socket() const { return __get_ft() == file_type::socket; }
137
138 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_socket(error_code& __ec) const noexcept {
139 return __get_ft(ec: &__ec) == file_type::socket;
140 }
141
142 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_symlink() const { return __get_sym_ft() == file_type::symlink; }
143
144 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_symlink(error_code& __ec) const noexcept {
145 return __get_sym_ft(ec: &__ec) == file_type::symlink;
146 }
147 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI uintmax_t file_size() const { return __get_size(); }
148
149 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI uintmax_t file_size(error_code& __ec) const noexcept { return __get_size(ec: &__ec); }
150
151 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI uintmax_t hard_link_count() const { return __get_nlink(); }
152
153 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI uintmax_t hard_link_count(error_code& __ec) const noexcept {
154 return __get_nlink(ec: &__ec);
155 }
156
157 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI file_time_type last_write_time() const { return __get_write_time(); }
158
159 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI file_time_type last_write_time(error_code& __ec) const noexcept {
160 return __get_write_time(ec: &__ec);
161 }
162
163 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI file_status status() const { return __get_status(); }
164
165 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI file_status status(error_code& __ec) const noexcept {
166 return __get_status(ec: &__ec);
167 }
168
169 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI file_status symlink_status() const { return __get_symlink_status(); }
170
171 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI file_status symlink_status(error_code& __ec) const noexcept {
172 return __get_symlink_status(ec: &__ec);
173 }
174
175 _LIBCPP_HIDE_FROM_ABI bool operator==(directory_entry const& __rhs) const noexcept { return __p_ == __rhs.__p_; }
176
177# if _LIBCPP_STD_VER <= 17
178 _LIBCPP_HIDE_FROM_ABI bool operator!=(directory_entry const& __rhs) const noexcept { return __p_ != __rhs.__p_; }
179
180 _LIBCPP_HIDE_FROM_ABI bool operator<(directory_entry const& __rhs) const noexcept { return __p_ < __rhs.__p_; }
181
182 _LIBCPP_HIDE_FROM_ABI bool operator<=(directory_entry const& __rhs) const noexcept { return __p_ <= __rhs.__p_; }
183
184 _LIBCPP_HIDE_FROM_ABI bool operator>(directory_entry const& __rhs) const noexcept { return __p_ > __rhs.__p_; }
185
186 _LIBCPP_HIDE_FROM_ABI bool operator>=(directory_entry const& __rhs) const noexcept { return __p_ >= __rhs.__p_; }
187
188# else // _LIBCPP_STD_VER <= 17
189
190 _LIBCPP_HIDE_FROM_ABI strong_ordering operator<=>(const directory_entry& __rhs) const noexcept {
191 return __p_ <=> __rhs.__p_;
192 }
193
194# endif // _LIBCPP_STD_VER <= 17
195
196 template <class _CharT, class _Traits>
197 _LIBCPP_HIDE_FROM_ABI friend basic_ostream<_CharT, _Traits>&
198 operator<<(basic_ostream<_CharT, _Traits>& __os, const directory_entry& __d) {
199 return __os << __d.path();
200 }
201
202private:
203 friend class directory_iterator;
204 friend class recursive_directory_iterator;
205 friend class _LIBCPP_HIDDEN __dir_stream;
206
207 enum _CacheType : unsigned char {
208 _Empty,
209 _IterSymlink,
210 _IterNonSymlink,
211 _RefreshSymlink,
212 _RefreshSymlinkUnresolved,
213 _RefreshNonSymlink,
214 _IterCachedSymlink,
215 _IterCachedNonSymlink
216 };
217
218 struct __cached_data {
219 uintmax_t __size_;
220 uintmax_t __nlink_;
221 file_time_type __write_time_;
222 perms __sym_perms_;
223 perms __non_sym_perms_;
224 file_type __type_;
225 _CacheType __cache_type_;
226
227 _LIBCPP_HIDE_FROM_ABI __cached_data() noexcept { __reset(); }
228
229 _LIBCPP_HIDE_FROM_ABI void __reset() {
230 __cache_type_ = _Empty;
231 __type_ = file_type::none;
232 __sym_perms_ = __non_sym_perms_ = perms::unknown;
233 __size_ = __nlink_ = uintmax_t(-1);
234 __write_time_ = file_time_type::min();
235 }
236 };
237
238 _LIBCPP_HIDE_FROM_ABI static __cached_data __create_iter_result(file_type __ft) {
239 __cached_data __data;
240 __data.__type_ = __ft;
241 __data.__cache_type_ = [&]() {
242 switch (__ft) {
243 case file_type::none:
244 return _Empty;
245 case file_type::symlink:
246 return _IterSymlink;
247 default:
248 return _IterNonSymlink;
249 }
250 }();
251 return __data;
252 }
253
254 _LIBCPP_HIDE_FROM_ABI static __cached_data
255 __create_iter_cached_result(file_type __ft, uintmax_t __size, perms __perm, file_time_type __write_time) {
256 __cached_data __data;
257 __data.__type_ = __ft;
258 __data.__size_ = __size;
259 __data.__write_time_ = __write_time;
260 if (__ft == file_type::symlink)
261 __data.__sym_perms_ = __perm;
262 else
263 __data.__non_sym_perms_ = __perm;
264 __data.__cache_type_ = [&]() {
265 switch (__ft) {
266 case file_type::none:
267 return _Empty;
268 case file_type::symlink:
269 return _IterCachedSymlink;
270 default:
271 return _IterCachedNonSymlink;
272 }
273 }();
274 return __data;
275 }
276
277 _LIBCPP_HIDE_FROM_ABI void __assign_iter_entry(_Path&& __p, __cached_data __dt) {
278 __p_ = std::move(__p);
279 __data_ = __dt;
280 }
281
282 _LIBCPP_EXPORTED_FROM_ABI error_code __do_refresh() noexcept;
283
284 _LIBCPP_HIDE_FROM_ABI static bool __is_dne_error(error_code const& __ec) {
285 return !__ec || __ec == errc::no_such_file_or_directory || __ec == errc::not_a_directory;
286 }
287
288 _LIBCPP_HIDE_FROM_ABI void
289 __handle_error(const char* __msg, error_code* __dest_ec, error_code const& __ec, bool __allow_dne = false) const {
290 if (__dest_ec) {
291 *__dest_ec = __ec;
292 return;
293 }
294 if (__ec && (!__allow_dne || !__is_dne_error(__ec)))
295 filesystem::__throw_filesystem_error(args&: __msg, args: __p_, args: __ec);
296 }
297
298 _LIBCPP_HIDE_FROM_ABI void __refresh(error_code* __ec = nullptr) {
299 __handle_error(msg: "in directory_entry::refresh",
300 dest_ec: __ec,
301 ec: __do_refresh(),
302 /*allow_dne*/ allow_dne: true);
303 }
304
305 _LIBCPP_HIDE_FROM_ABI file_type __get_sym_ft(error_code* __ec = nullptr) const {
306 switch (__data_.__cache_type_) {
307 case _Empty:
308 return __symlink_status(__p_, __ec).type();
309 case _IterSymlink:
310 case _IterCachedSymlink:
311 case _RefreshSymlink:
312 case _RefreshSymlinkUnresolved:
313 if (__ec)
314 __ec->clear();
315 return file_type::symlink;
316 case _IterCachedNonSymlink:
317 case _IterNonSymlink:
318 case _RefreshNonSymlink: {
319 file_status __st(__data_.__type_);
320 if (__ec && !filesystem::exists(s: __st))
321 *__ec = make_error_code(e: errc::no_such_file_or_directory);
322 else if (__ec)
323 __ec->clear();
324 return __data_.__type_;
325 }
326 }
327 __libcpp_unreachable();
328 }
329
330 _LIBCPP_HIDE_FROM_ABI file_type __get_ft(error_code* __ec = nullptr) const {
331 switch (__data_.__cache_type_) {
332 case _Empty:
333 case _IterSymlink:
334 case _IterCachedSymlink:
335 case _RefreshSymlinkUnresolved:
336 return __status(__p_, __ec).type();
337 case _IterCachedNonSymlink:
338 case _IterNonSymlink:
339 case _RefreshNonSymlink:
340 case _RefreshSymlink: {
341 file_status __st(__data_.__type_);
342 if (__ec && !filesystem::exists(s: __st))
343 *__ec = make_error_code(e: errc::no_such_file_or_directory);
344 else if (__ec)
345 __ec->clear();
346 return __data_.__type_;
347 }
348 }
349 __libcpp_unreachable();
350 }
351
352 _LIBCPP_HIDE_FROM_ABI file_status __get_status(error_code* __ec = nullptr) const {
353 switch (__data_.__cache_type_) {
354 case _Empty:
355 case _IterNonSymlink:
356 case _IterSymlink:
357 case _IterCachedSymlink:
358 case _RefreshSymlinkUnresolved:
359 return __status(__p_, __ec);
360 case _IterCachedNonSymlink:
361 case _RefreshNonSymlink:
362 case _RefreshSymlink:
363 return file_status(__get_ft(__ec), __data_.__non_sym_perms_);
364 }
365 __libcpp_unreachable();
366 }
367
368 _LIBCPP_HIDE_FROM_ABI file_status __get_symlink_status(error_code* __ec = nullptr) const {
369 switch (__data_.__cache_type_) {
370 case _Empty:
371 case _IterNonSymlink:
372 case _IterSymlink:
373 return __symlink_status(__p_, __ec);
374 case _IterCachedNonSymlink:
375 case _RefreshNonSymlink:
376 return file_status(__get_sym_ft(__ec), __data_.__non_sym_perms_);
377 case _IterCachedSymlink:
378 case _RefreshSymlink:
379 case _RefreshSymlinkUnresolved:
380 return file_status(__get_sym_ft(__ec), __data_.__sym_perms_);
381 }
382 __libcpp_unreachable();
383 }
384
385 _LIBCPP_HIDE_FROM_ABI uintmax_t __get_size(error_code* __ec = nullptr) const {
386 switch (__data_.__cache_type_) {
387 case _Empty:
388 case _IterNonSymlink:
389 case _IterSymlink:
390 case _IterCachedSymlink:
391 case _RefreshSymlinkUnresolved:
392 return filesystem::__file_size(__p_, __ec);
393 case _IterCachedNonSymlink:
394 case _RefreshSymlink:
395 case _RefreshNonSymlink: {
396 error_code __m_ec;
397 file_status __st(__get_ft(ec: &__m_ec));
398 __handle_error(msg: "in directory_entry::file_size", dest_ec: __ec, ec: __m_ec);
399 if (filesystem::exists(s: __st) && !filesystem::is_regular_file(s: __st)) {
400 errc __err_kind = filesystem::is_directory(s: __st) ? errc::is_a_directory : errc::not_supported;
401 __handle_error(msg: "in directory_entry::file_size", dest_ec: __ec, ec: make_error_code(e: __err_kind));
402 }
403 return __data_.__size_;
404 }
405 }
406 __libcpp_unreachable();
407 }
408
409 _LIBCPP_HIDE_FROM_ABI uintmax_t __get_nlink(error_code* __ec = nullptr) const {
410 switch (__data_.__cache_type_) {
411 case _Empty:
412 case _IterNonSymlink:
413 case _IterSymlink:
414 case _IterCachedNonSymlink:
415 case _IterCachedSymlink:
416 case _RefreshSymlinkUnresolved:
417 return filesystem::__hard_link_count(__p_, __ec);
418 case _RefreshSymlink:
419 case _RefreshNonSymlink: {
420 error_code __m_ec;
421 (void)__get_ft(ec: &__m_ec);
422 __handle_error(msg: "in directory_entry::hard_link_count", dest_ec: __ec, ec: __m_ec);
423 return __data_.__nlink_;
424 }
425 }
426 __libcpp_unreachable();
427 }
428
429 _LIBCPP_HIDE_FROM_ABI file_time_type __get_write_time(error_code* __ec = nullptr) const {
430 switch (__data_.__cache_type_) {
431 case _Empty:
432 case _IterNonSymlink:
433 case _IterSymlink:
434 case _IterCachedSymlink:
435 case _RefreshSymlinkUnresolved:
436 return filesystem::__last_write_time(__p_, __ec);
437 case _IterCachedNonSymlink:
438 case _RefreshSymlink:
439 case _RefreshNonSymlink: {
440 error_code __m_ec;
441 file_status __st(__get_ft(ec: &__m_ec));
442 __handle_error(msg: "in directory_entry::last_write_time", dest_ec: __ec, ec: __m_ec);
443 if (filesystem::exists(s: __st) && __data_.__write_time_ == file_time_type::min())
444 __handle_error(msg: "in directory_entry::last_write_time", dest_ec: __ec, ec: make_error_code(e: errc::value_too_large));
445 return __data_.__write_time_;
446 }
447 }
448 __libcpp_unreachable();
449 }
450
451private:
452 _Path __p_;
453 __cached_data __data_;
454};
455
456class __dir_element_proxy {
457public:
458 inline _LIBCPP_HIDE_FROM_ABI directory_entry operator*() { return std::move(__elem_); }
459
460private:
461 friend class directory_iterator;
462 friend class recursive_directory_iterator;
463 _LIBCPP_HIDE_FROM_ABI explicit __dir_element_proxy(directory_entry const& __e) : __elem_(__e) {}
464 _LIBCPP_HIDE_FROM_ABI __dir_element_proxy(__dir_element_proxy&& __o) : __elem_(std::move(__o.__elem_)) {}
465 directory_entry __elem_;
466};
467
468_LIBCPP_END_NAMESPACE_FILESYSTEM
469
470#endif // _LIBCPP_STD_VER >= 17 && _LIBCPP_HAS_FILESYSTEM
471
472_LIBCPP_POP_MACROS
473
474#endif // _LIBCPP___FILESYSTEM_DIRECTORY_ENTRY_H
475