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: &__ec); |
57 | } |
58 | |
59 | _LIBCPP_HIDE_FROM_ABI directory_entry(_Path const& __p, error_code& __ec) : __p_(__p) { __refresh(ec: &__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: &__ec); |
70 | } |
71 | |
72 | _LIBCPP_HIDE_FROM_ABI void assign(_Path const& __p, error_code& __ec) { |
73 | __p_ = __p; |
74 | __refresh(ec: &__ec); |
75 | } |
76 | |
77 | _LIBCPP_HIDE_FROM_ABI void replace_filename(_Path const& __p) { |
78 | __p_.replace_filename(replacement: __p); |
79 | error_code __ec; |
80 | __refresh(ec: &__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: &__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: &__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(s: file_status{__get_ft()}); } |
97 | |
98 | _LIBCPP_HIDE_FROM_ABI bool exists(error_code& __ec) const noexcept { |
99 | return filesystem::exists(s: file_status{__get_ft(ec: &__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: &__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: &__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: &__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: &__ec) == file_type::fifo; } |
123 | |
124 | _LIBCPP_HIDE_FROM_ABI bool is_other() const { return filesystem::is_other(s: file_status{__get_ft()}); } |
125 | |
126 | _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 | _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: &__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: &__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: &__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: &__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: &__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: &__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: &__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: &__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 | filesystem::__throw_filesystem_error(args&: __msg, args: __p_, args: __ec); |
290 | } |
291 | |
292 | _LIBCPP_HIDE_FROM_ABI void __refresh(error_code* __ec = nullptr) { |
293 | __handle_error(msg: "in directory_entry::refresh" , |
294 | dest_ec: __ec, |
295 | ec: __do_refresh(), |
296 | /*allow_dne*/ 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(s: __st)) |
315 | *__ec = make_error_code(e: 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(s: __st)) |
337 | *__ec = make_error_code(e: 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(ec: &__m_ec)); |
392 | __handle_error(msg: "in directory_entry::file_size" , dest_ec: __ec, ec: __m_ec); |
393 | if (filesystem::exists(s: __st) && !filesystem::is_regular_file(s: __st)) { |
394 | errc __err_kind = filesystem::is_directory(s: __st) ? errc::is_a_directory : errc::not_supported; |
395 | __handle_error(msg: "in directory_entry::file_size" , dest_ec: __ec, ec: make_error_code(e: __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(ec: &__m_ec); |
416 | __handle_error(msg: "in directory_entry::hard_link_count" , dest_ec: __ec, 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(ec: &__m_ec)); |
436 | __handle_error(msg: "in directory_entry::last_write_time" , dest_ec: __ec, ec: __m_ec); |
437 | if (filesystem::exists(s: __st) && __data_.__write_time_ == file_time_type::min()) |
438 | __handle_error(msg: "in directory_entry::last_write_time" , dest_ec: __ec, ec: make_error_code(e: 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 | |