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