1 | // Filesystem directory utilities -*- C++ -*- |
2 | |
3 | // Copyright (C) 2014-2024 Free Software Foundation, Inc. |
4 | // |
5 | // This file is part of the GNU ISO C++ Library. This library is free |
6 | // software; you can redistribute it and/or modify it under the |
7 | // terms of the GNU General Public License as published by the |
8 | // Free Software Foundation; either version 3, or (at your option) |
9 | // any later version. |
10 | |
11 | // This library is distributed in the hope that it will be useful, |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | // GNU General Public License for more details. |
15 | |
16 | // Under Section 7 of GPL version 3, you are granted additional |
17 | // permissions described in the GCC Runtime Library Exception, version |
18 | // 3.1, as published by the Free Software Foundation. |
19 | |
20 | // You should have received a copy of the GNU General Public License and |
21 | // a copy of the GCC Runtime Library Exception along with this program; |
22 | // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
23 | // <http://www.gnu.org/licenses/>. |
24 | |
25 | /** @file include/bits/fs_dir.h |
26 | * This is an internal header file, included by other library headers. |
27 | * Do not attempt to use it directly. @headername{filesystem} |
28 | */ |
29 | |
30 | #ifndef _GLIBCXX_FS_DIR_H |
31 | #define _GLIBCXX_FS_DIR_H 1 |
32 | |
33 | #if __cplusplus >= 201703L |
34 | # include <typeinfo> |
35 | # include <ext/concurrence.h> |
36 | # include <bits/unique_ptr.h> |
37 | # include <bits/shared_ptr.h> |
38 | |
39 | #if __cplusplus >= 202002L |
40 | # include <compare> // std::strong_ordering |
41 | # include <bits/iterator_concepts.h> // std::default_sentinel_t |
42 | # include <bits/ranges_base.h> // enable_view, enable_borrowed_range |
43 | #endif |
44 | |
45 | namespace std _GLIBCXX_VISIBILITY(default) |
46 | { |
47 | _GLIBCXX_BEGIN_NAMESPACE_VERSION |
48 | |
49 | namespace filesystem |
50 | { |
51 | /** @addtogroup filesystem |
52 | * @{ |
53 | */ |
54 | |
55 | /// Information about a file's type and permissions. |
56 | /** |
57 | * @headerfile filesystem |
58 | * @since C++17 |
59 | */ |
60 | class file_status |
61 | { |
62 | public: |
63 | // constructors and destructor |
64 | file_status() noexcept : file_status(file_type::none) {} |
65 | |
66 | explicit |
67 | file_status(file_type __ft, perms __prms = perms::unknown) noexcept |
68 | : _M_type(__ft), _M_perms(__prms) { } |
69 | |
70 | file_status(const file_status&) noexcept = default; |
71 | file_status(file_status&&) noexcept = default; |
72 | ~file_status() = default; |
73 | |
74 | file_status& operator=(const file_status&) noexcept = default; |
75 | file_status& operator=(file_status&&) noexcept = default; |
76 | |
77 | // observers |
78 | file_type type() const noexcept { return _M_type; } |
79 | perms permissions() const noexcept { return _M_perms; } |
80 | |
81 | // modifiers |
82 | void type(file_type __ft) noexcept { _M_type = __ft; } |
83 | void permissions(perms __prms) noexcept { _M_perms = __prms; } |
84 | |
85 | #if __cpp_lib_three_way_comparison |
86 | friend bool |
87 | operator==(const file_status&, const file_status&) noexcept = default; |
88 | #endif |
89 | |
90 | private: |
91 | file_type _M_type; |
92 | perms _M_perms; |
93 | }; |
94 | |
95 | _GLIBCXX_BEGIN_NAMESPACE_CXX11 |
96 | |
97 | struct _Dir; |
98 | class directory_iterator; |
99 | class recursive_directory_iterator; |
100 | |
101 | /// The value type used by directory iterators |
102 | /** |
103 | * @headerfile filesystem |
104 | * @since C++17 |
105 | */ |
106 | class directory_entry |
107 | { |
108 | public: |
109 | // constructors and destructor |
110 | directory_entry() noexcept = default; |
111 | directory_entry(const directory_entry&) = default; |
112 | directory_entry(directory_entry&&) noexcept = default; |
113 | |
114 | explicit |
115 | directory_entry(const filesystem::path& __p) |
116 | : _M_path(__p) |
117 | { refresh(); } |
118 | |
119 | directory_entry(const filesystem::path& __p, error_code& __ec) |
120 | : _M_path(__p) |
121 | { |
122 | refresh(__ec); |
123 | if (__ec) |
124 | _M_path.clear(); |
125 | } |
126 | |
127 | ~directory_entry() = default; |
128 | |
129 | // modifiers |
130 | directory_entry& operator=(const directory_entry&) = default; |
131 | directory_entry& operator=(directory_entry&&) noexcept = default; |
132 | |
133 | void |
134 | assign(const filesystem::path& __p) |
135 | { |
136 | _M_path = __p; |
137 | refresh(); |
138 | } |
139 | |
140 | void |
141 | assign(const filesystem::path& __p, error_code& __ec) |
142 | { |
143 | _M_path = __p; |
144 | refresh(__ec); |
145 | } |
146 | |
147 | void |
148 | replace_filename(const filesystem::path& __p) |
149 | { |
150 | _M_path.replace_filename(replacement: __p); |
151 | refresh(); |
152 | } |
153 | |
154 | void |
155 | replace_filename(const filesystem::path& __p, error_code& __ec) |
156 | { |
157 | _M_path.replace_filename(replacement: __p); |
158 | refresh(__ec); |
159 | } |
160 | |
161 | void |
162 | refresh() |
163 | { _M_type = symlink_status().type(); } |
164 | |
165 | void |
166 | refresh(error_code& __ec) noexcept |
167 | { _M_type = symlink_status(__ec).type(); } |
168 | |
169 | // observers |
170 | const filesystem::path& path() const noexcept { return _M_path; } |
171 | operator const filesystem::path& () const noexcept { return _M_path; } |
172 | |
173 | bool |
174 | exists() const |
175 | { return filesystem::exists(file_status{_M_file_type()}); } |
176 | |
177 | bool |
178 | exists(error_code& __ec) const noexcept |
179 | { return filesystem::exists(file_status{_M_file_type(__ec)}); } |
180 | |
181 | bool |
182 | is_block_file() const |
183 | { return _M_file_type() == file_type::block; } |
184 | |
185 | bool |
186 | is_block_file(error_code& __ec) const noexcept |
187 | { return _M_file_type(__ec) == file_type::block; } |
188 | |
189 | bool |
190 | is_character_file() const |
191 | { return _M_file_type() == file_type::character; } |
192 | |
193 | bool |
194 | is_character_file(error_code& __ec) const noexcept |
195 | { return _M_file_type(__ec) == file_type::character; } |
196 | |
197 | bool |
198 | is_directory() const |
199 | { return _M_file_type() == file_type::directory; } |
200 | |
201 | bool |
202 | is_directory(error_code& __ec) const noexcept |
203 | { return _M_file_type(__ec) == file_type::directory; } |
204 | |
205 | bool |
206 | is_fifo() const |
207 | { return _M_file_type() == file_type::fifo; } |
208 | |
209 | bool |
210 | is_fifo(error_code& __ec) const noexcept |
211 | { return _M_file_type(__ec) == file_type::fifo; } |
212 | |
213 | bool |
214 | is_other() const |
215 | { return filesystem::is_other(file_status{_M_file_type()}); } |
216 | |
217 | bool |
218 | is_other(error_code& __ec) const noexcept |
219 | { return filesystem::is_other(file_status{_M_file_type(__ec)}); } |
220 | |
221 | bool |
222 | is_regular_file() const |
223 | { return _M_file_type() == file_type::regular; } |
224 | |
225 | bool |
226 | is_regular_file(error_code& __ec) const noexcept |
227 | { return _M_file_type(__ec) == file_type::regular; } |
228 | |
229 | bool |
230 | is_socket() const |
231 | { return _M_file_type() == file_type::socket; } |
232 | |
233 | bool |
234 | is_socket(error_code& __ec) const noexcept |
235 | { return _M_file_type(__ec) == file_type::socket; } |
236 | |
237 | bool |
238 | is_symlink() const |
239 | { |
240 | if (_M_type != file_type::none) |
241 | return _M_type == file_type::symlink; |
242 | return symlink_status().type() == file_type::symlink; |
243 | } |
244 | |
245 | bool |
246 | is_symlink(error_code& __ec) const noexcept |
247 | { |
248 | if (_M_type != file_type::none) |
249 | return _M_type == file_type::symlink; |
250 | return symlink_status(__ec).type() == file_type::symlink; |
251 | } |
252 | |
253 | uintmax_t |
254 | file_size() const |
255 | { return filesystem::file_size(_M_path); } |
256 | |
257 | uintmax_t |
258 | file_size(error_code& __ec) const noexcept |
259 | { return filesystem::file_size(_M_path, __ec); } |
260 | |
261 | uintmax_t |
262 | hard_link_count() const |
263 | { return filesystem::hard_link_count(_M_path); } |
264 | |
265 | uintmax_t |
266 | hard_link_count(error_code& __ec) const noexcept |
267 | { return filesystem::hard_link_count(_M_path, __ec); } |
268 | |
269 | file_time_type |
270 | last_write_time() const |
271 | { return filesystem::last_write_time(_M_path); } |
272 | |
273 | |
274 | file_time_type |
275 | last_write_time(error_code& __ec) const noexcept |
276 | { return filesystem::last_write_time(_M_path, __ec); } |
277 | |
278 | file_status |
279 | status() const |
280 | { return filesystem::status(_M_path); } |
281 | |
282 | file_status |
283 | status(error_code& __ec) const noexcept |
284 | { return filesystem::status(_M_path, __ec); } |
285 | |
286 | file_status |
287 | symlink_status() const |
288 | { return filesystem::symlink_status(_M_path); } |
289 | |
290 | file_status |
291 | symlink_status(error_code& __ec) const noexcept |
292 | { return filesystem::symlink_status(_M_path, __ec); } |
293 | |
294 | bool |
295 | operator==(const directory_entry& __rhs) const noexcept |
296 | { return _M_path == __rhs._M_path; } |
297 | |
298 | #if __cpp_lib_three_way_comparison |
299 | strong_ordering |
300 | operator<=>(const directory_entry& __rhs) const noexcept |
301 | { return _M_path <=> __rhs._M_path; } |
302 | #else |
303 | bool |
304 | operator!=(const directory_entry& __rhs) const noexcept |
305 | { return _M_path != __rhs._M_path; } |
306 | |
307 | bool |
308 | operator< (const directory_entry& __rhs) const noexcept |
309 | { return _M_path < __rhs._M_path; } |
310 | |
311 | bool |
312 | operator<=(const directory_entry& __rhs) const noexcept |
313 | { return _M_path <= __rhs._M_path; } |
314 | |
315 | bool |
316 | operator> (const directory_entry& __rhs) const noexcept |
317 | { return _M_path > __rhs._M_path; } |
318 | |
319 | bool |
320 | operator>=(const directory_entry& __rhs) const noexcept |
321 | { return _M_path >= __rhs._M_path; } |
322 | #endif |
323 | |
324 | private: |
325 | friend struct _Dir; |
326 | friend class directory_iterator; |
327 | friend class recursive_directory_iterator; |
328 | |
329 | // _GLIBCXX_RESOLVE_LIB_DEFECTS |
330 | // 3171. LWG 2989 breaks directory_entry stream insertion |
331 | template<typename _CharT, typename _Traits> |
332 | friend basic_ostream<_CharT, _Traits>& |
333 | operator<<(basic_ostream<_CharT, _Traits>& __os, |
334 | const directory_entry& __d) |
335 | { return __os << __d.path(); } |
336 | |
337 | directory_entry(const filesystem::path& __p, file_type __t) |
338 | : _M_path(__p), _M_type(__t) |
339 | { } |
340 | |
341 | // Equivalent to status().type() but uses cached value, if any. |
342 | file_type |
343 | _M_file_type() const |
344 | { |
345 | if (_M_type != file_type::none && _M_type != file_type::symlink) |
346 | return _M_type; |
347 | return status().type(); |
348 | } |
349 | |
350 | // Equivalent to status(__ec).type() but uses cached value, if any. |
351 | file_type |
352 | _M_file_type(error_code& __ec) const noexcept |
353 | { |
354 | if (_M_type != file_type::none && _M_type != file_type::symlink) |
355 | { |
356 | __ec.clear(); |
357 | return _M_type; |
358 | } |
359 | return status(__ec).type(); |
360 | } |
361 | |
362 | filesystem::path _M_path; |
363 | file_type _M_type = file_type::none; |
364 | }; |
365 | |
366 | /// @cond undocumented |
367 | |
368 | /// Proxy returned by post-increment on directory iterators. |
369 | /** |
370 | * @headerfile filesystem |
371 | * @since C++17 |
372 | */ |
373 | struct __directory_iterator_proxy |
374 | { |
375 | const directory_entry& operator*() const& noexcept { return _M_entry; } |
376 | |
377 | directory_entry operator*() && noexcept { return std::move(_M_entry); } |
378 | |
379 | private: |
380 | friend class directory_iterator; |
381 | friend class recursive_directory_iterator; |
382 | |
383 | explicit |
384 | __directory_iterator_proxy(const directory_entry& __e) : _M_entry(__e) { } |
385 | |
386 | directory_entry _M_entry; |
387 | }; |
388 | /// @endcond |
389 | |
390 | /// Iterator type for traversing the entries in a single directory. |
391 | /** |
392 | * @headerfile filesystem |
393 | * @since C++17 |
394 | */ |
395 | class directory_iterator |
396 | { |
397 | public: |
398 | typedef directory_entry value_type; |
399 | typedef ptrdiff_t difference_type; |
400 | typedef const directory_entry* pointer; |
401 | typedef const directory_entry& reference; |
402 | typedef input_iterator_tag iterator_category; |
403 | |
404 | directory_iterator() = default; |
405 | |
406 | explicit |
407 | directory_iterator(const path& __p) |
408 | : directory_iterator(__p, directory_options::none, nullptr) { } |
409 | |
410 | directory_iterator(const path& __p, directory_options __options) |
411 | : directory_iterator(__p, __options, nullptr) { } |
412 | |
413 | directory_iterator(const path& __p, error_code& __ec) |
414 | : directory_iterator(__p, directory_options::none, __ec) { } |
415 | |
416 | directory_iterator(const path& __p, directory_options __options, |
417 | error_code& __ec) |
418 | : directory_iterator(__p, __options, &__ec) { } |
419 | |
420 | directory_iterator(const directory_iterator& __rhs) = default; |
421 | |
422 | directory_iterator(directory_iterator&& __rhs) noexcept = default; |
423 | |
424 | ~directory_iterator() = default; |
425 | |
426 | directory_iterator& |
427 | operator=(const directory_iterator& __rhs) = default; |
428 | |
429 | directory_iterator& |
430 | operator=(directory_iterator&& __rhs) noexcept = default; |
431 | |
432 | const directory_entry& operator*() const noexcept; |
433 | const directory_entry* operator->() const noexcept { return &**this; } |
434 | directory_iterator& operator++(); |
435 | directory_iterator& increment(error_code& __ec); |
436 | |
437 | __directory_iterator_proxy operator++(int) |
438 | { |
439 | __directory_iterator_proxy __pr{**this}; |
440 | ++*this; |
441 | return __pr; |
442 | } |
443 | |
444 | friend bool |
445 | operator==(const directory_iterator& __lhs, |
446 | const directory_iterator& __rhs) noexcept |
447 | { |
448 | return !__rhs._M_dir.owner_before(rhs: __lhs._M_dir) |
449 | && !__lhs._M_dir.owner_before(rhs: __rhs._M_dir); |
450 | } |
451 | |
452 | #if __cplusplus >= 202002L |
453 | // _GLIBCXX_RESOLVE_LIB_DEFECTS |
454 | // 3719. Directory iterators should be usable with default sentinel |
455 | bool operator==(default_sentinel_t) const noexcept |
456 | { return !_M_dir; } |
457 | #endif |
458 | |
459 | #if __cpp_impl_three_way_comparison < 201907L |
460 | friend bool |
461 | operator!=(const directory_iterator& __lhs, |
462 | const directory_iterator& __rhs) noexcept |
463 | { return !(__lhs == __rhs); } |
464 | #endif |
465 | |
466 | private: |
467 | directory_iterator(const path&, directory_options, error_code*); |
468 | |
469 | friend class recursive_directory_iterator; |
470 | |
471 | std::__shared_ptr<_Dir> _M_dir; |
472 | }; |
473 | |
474 | /** @relates std::filesystem::directory_iterator |
475 | * @headerfile filesystem |
476 | * @since C++17 |
477 | * @{ |
478 | */ |
479 | |
480 | /** @brief Enable range-based `for` using directory_iterator. |
481 | * |
482 | * e.g. `for (auto& entry : std::filesystem::directory_iterator(".")) ...` |
483 | */ |
484 | inline directory_iterator |
485 | begin(directory_iterator __iter) noexcept |
486 | { return __iter; } |
487 | |
488 | /// Return a past-the-end directory_iterator |
489 | inline directory_iterator |
490 | end(directory_iterator) noexcept |
491 | { return directory_iterator(); } |
492 | /// @} |
493 | |
494 | /// Iterator type for recursively traversing a directory hierarchy. |
495 | /** |
496 | * @headerfile filesystem |
497 | * @since C++17 |
498 | */ |
499 | class recursive_directory_iterator |
500 | { |
501 | public: |
502 | typedef directory_entry value_type; |
503 | typedef ptrdiff_t difference_type; |
504 | typedef const directory_entry* pointer; |
505 | typedef const directory_entry& reference; |
506 | typedef input_iterator_tag iterator_category; |
507 | |
508 | recursive_directory_iterator() = default; |
509 | |
510 | explicit |
511 | recursive_directory_iterator(const path& __p) |
512 | : recursive_directory_iterator(__p, directory_options::none, nullptr) { } |
513 | |
514 | recursive_directory_iterator(const path& __p, directory_options __options) |
515 | : recursive_directory_iterator(__p, __options, nullptr) { } |
516 | |
517 | recursive_directory_iterator(const path& __p, directory_options __options, |
518 | error_code& __ec) |
519 | : recursive_directory_iterator(__p, __options, &__ec) { } |
520 | |
521 | recursive_directory_iterator(const path& __p, error_code& __ec) |
522 | : recursive_directory_iterator(__p, directory_options::none, &__ec) { } |
523 | |
524 | recursive_directory_iterator( |
525 | const recursive_directory_iterator&) = default; |
526 | |
527 | recursive_directory_iterator(recursive_directory_iterator&&) = default; |
528 | |
529 | ~recursive_directory_iterator(); |
530 | |
531 | // observers |
532 | directory_options options() const noexcept; |
533 | int depth() const noexcept; |
534 | bool recursion_pending() const noexcept; |
535 | |
536 | const directory_entry& operator*() const noexcept; |
537 | const directory_entry* operator->() const noexcept { return &**this; } |
538 | |
539 | // modifiers |
540 | recursive_directory_iterator& |
541 | operator=(const recursive_directory_iterator& __rhs) noexcept; |
542 | recursive_directory_iterator& |
543 | operator=(recursive_directory_iterator&& __rhs) noexcept; |
544 | |
545 | recursive_directory_iterator& operator++(); |
546 | recursive_directory_iterator& increment(error_code& __ec); |
547 | |
548 | __directory_iterator_proxy operator++(int) |
549 | { |
550 | __directory_iterator_proxy __pr{**this}; |
551 | ++*this; |
552 | return __pr; |
553 | } |
554 | |
555 | void pop(); |
556 | void pop(error_code&); |
557 | |
558 | void disable_recursion_pending() noexcept; |
559 | |
560 | friend bool |
561 | operator==(const recursive_directory_iterator& __lhs, |
562 | const recursive_directory_iterator& __rhs) noexcept |
563 | { |
564 | return !__rhs._M_dirs.owner_before(rhs: __lhs._M_dirs) |
565 | && !__lhs._M_dirs.owner_before(rhs: __rhs._M_dirs); |
566 | } |
567 | |
568 | #if __cplusplus >= 202002L |
569 | // _GLIBCXX_RESOLVE_LIB_DEFECTS |
570 | // 3719. Directory iterators should be usable with default sentinel |
571 | bool operator==(default_sentinel_t) const noexcept |
572 | { return !_M_dirs; } |
573 | #endif |
574 | |
575 | #if __cpp_impl_three_way_comparison < 201907L |
576 | friend bool |
577 | operator!=(const recursive_directory_iterator& __lhs, |
578 | const recursive_directory_iterator& __rhs) noexcept |
579 | { return !(__lhs == __rhs); } |
580 | #endif |
581 | |
582 | private: |
583 | recursive_directory_iterator(const path&, directory_options, error_code*); |
584 | |
585 | struct _Dir_stack; |
586 | std::__shared_ptr<_Dir_stack> _M_dirs; |
587 | |
588 | recursive_directory_iterator& |
589 | __erase(error_code* = nullptr); |
590 | |
591 | friend uintmax_t |
592 | filesystem::remove_all(const path&, error_code&); |
593 | friend uintmax_t |
594 | filesystem::remove_all(const path&); |
595 | }; |
596 | |
597 | /** @relates std::filesystem::directory_iterator |
598 | * @headerfile filesystem |
599 | * @since C++17 |
600 | * @{ |
601 | */ |
602 | |
603 | /** @brief Enable range-based `for` using recursive_directory_iterator. |
604 | * |
605 | * e.g. `for (auto& entry : recursive_directory_iterator(".")) ...` |
606 | */ |
607 | inline recursive_directory_iterator |
608 | begin(recursive_directory_iterator __iter) noexcept |
609 | { return __iter; } |
610 | |
611 | /// Return a past-the-end recursive_directory_iterator |
612 | inline recursive_directory_iterator |
613 | end(recursive_directory_iterator) noexcept |
614 | { return recursive_directory_iterator(); } |
615 | /// @} |
616 | |
617 | _GLIBCXX_END_NAMESPACE_CXX11 |
618 | |
619 | /// @} group filesystem |
620 | } // namespace filesystem |
621 | |
622 | // Use explicit instantiations of these types. Any inconsistency in the |
623 | // value of __default_lock_policy between code including this header and |
624 | // the library will cause a linker error. |
625 | extern template class |
626 | __shared_ptr<filesystem::_Dir>; |
627 | extern template class |
628 | __shared_ptr<filesystem::recursive_directory_iterator::_Dir_stack>; |
629 | |
630 | #if __glibcxx_ranges // >= C++20 |
631 | // _GLIBCXX_RESOLVE_LIB_DEFECTS |
632 | // 3480. directory_iterator and recursive_directory_iterator are not ranges |
633 | namespace ranges |
634 | { |
635 | template<> |
636 | inline constexpr bool |
637 | enable_borrowed_range<filesystem::directory_iterator> = true; |
638 | template<> |
639 | inline constexpr bool |
640 | enable_borrowed_range<filesystem::recursive_directory_iterator> = true; |
641 | |
642 | template<> |
643 | inline constexpr bool |
644 | enable_view<filesystem::directory_iterator> = true; |
645 | template<> |
646 | inline constexpr bool |
647 | enable_view<filesystem::recursive_directory_iterator> = true; |
648 | } // namespace ranges |
649 | #endif // ranges |
650 | |
651 | _GLIBCXX_END_NAMESPACE_VERSION |
652 | } // namespace std |
653 | |
654 | #endif // C++17 |
655 | |
656 | #endif // _GLIBCXX_FS_DIR_H |
657 | |