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