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
45namespace std _GLIBCXX_VISIBILITY(default)
46{
47_GLIBCXX_BEGIN_NAMESPACE_VERSION
48
49namespace 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
633namespace 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