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