master
  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
 45class directory_entry {
 46  typedef filesystem::path _Path;
 47
 48public:
 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
196private:
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      filesystem::__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
445private:
446  _Path __p_;
447  __cached_data __data_;
448};
449
450class __dir_element_proxy {
451public:
452  inline _LIBCPP_HIDE_FROM_ABI directory_entry operator*() { return std::move(__elem_); }
453
454private:
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