1 | // Filesystem directory utilities -*- C++ -*- |
2 | |
3 | // Copyright (C) 2014-2022 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 | #endif |
43 | |
44 | namespace std _GLIBCXX_VISIBILITY(default) |
45 | { |
46 | _GLIBCXX_BEGIN_NAMESPACE_VERSION |
47 | |
48 | namespace filesystem |
49 | { |
50 | /** @addtogroup filesystem |
51 | * @{ |
52 | */ |
53 | |
54 | /// Information about a file's type and permissions. |
55 | class file_status |
56 | { |
57 | public: |
58 | // constructors and destructor |
59 | file_status() noexcept : file_status(file_type::none) {} |
60 | |
61 | explicit |
62 | file_status(file_type __ft, perms __prms = perms::unknown) noexcept |
63 | : _M_type(__ft), _M_perms(__prms) { } |
64 | |
65 | file_status(const file_status&) noexcept = default; |
66 | file_status(file_status&&) noexcept = default; |
67 | ~file_status() = default; |
68 | |
69 | file_status& operator=(const file_status&) noexcept = default; |
70 | file_status& operator=(file_status&&) noexcept = default; |
71 | |
72 | // observers |
73 | file_type type() const noexcept { return _M_type; } |
74 | perms permissions() const noexcept { return _M_perms; } |
75 | |
76 | // modifiers |
77 | void type(file_type __ft) noexcept { _M_type = __ft; } |
78 | void permissions(perms __prms) noexcept { _M_perms = __prms; } |
79 | |
80 | #if __cpp_lib_three_way_comparison |
81 | friend bool |
82 | operator==(const file_status&, const file_status&) noexcept = default; |
83 | #endif |
84 | |
85 | private: |
86 | file_type _M_type; |
87 | perms _M_perms; |
88 | }; |
89 | |
90 | _GLIBCXX_BEGIN_NAMESPACE_CXX11 |
91 | |
92 | struct _Dir; |
93 | class directory_iterator; |
94 | class recursive_directory_iterator; |
95 | |
96 | /// The value type used by directory iterators |
97 | class directory_entry |
98 | { |
99 | public: |
100 | // constructors and destructor |
101 | directory_entry() noexcept = default; |
102 | directory_entry(const directory_entry&) = default; |
103 | directory_entry(directory_entry&&) noexcept = default; |
104 | |
105 | explicit |
106 | directory_entry(const filesystem::path& __p) |
107 | : _M_path(__p) |
108 | { refresh(); } |
109 | |
110 | directory_entry(const filesystem::path& __p, error_code& __ec) |
111 | : _M_path(__p) |
112 | { |
113 | refresh(__ec); |
114 | if (__ec) |
115 | _M_path.clear(); |
116 | } |
117 | |
118 | ~directory_entry() = default; |
119 | |
120 | // modifiers |
121 | directory_entry& operator=(const directory_entry&) = default; |
122 | directory_entry& operator=(directory_entry&&) noexcept = default; |
123 | |
124 | void |
125 | assign(const filesystem::path& __p) |
126 | { |
127 | _M_path = __p; |
128 | refresh(); |
129 | } |
130 | |
131 | void |
132 | assign(const filesystem::path& __p, error_code& __ec) |
133 | { |
134 | _M_path = __p; |
135 | refresh(__ec); |
136 | } |
137 | |
138 | void |
139 | replace_filename(const filesystem::path& __p) |
140 | { |
141 | _M_path.replace_filename(replacement: __p); |
142 | refresh(); |
143 | } |
144 | |
145 | void |
146 | replace_filename(const filesystem::path& __p, error_code& __ec) |
147 | { |
148 | _M_path.replace_filename(replacement: __p); |
149 | refresh(__ec); |
150 | } |
151 | |
152 | void |
153 | refresh() |
154 | { _M_type = symlink_status().type(); } |
155 | |
156 | void |
157 | refresh(error_code& __ec) noexcept |
158 | { _M_type = symlink_status(__ec).type(); } |
159 | |
160 | // observers |
161 | const filesystem::path& path() const noexcept { return _M_path; } |
162 | operator const filesystem::path& () const noexcept { return _M_path; } |
163 | |
164 | bool |
165 | exists() const |
166 | { return filesystem::exists(file_status{_M_file_type()}); } |
167 | |
168 | bool |
169 | exists(error_code& __ec) const noexcept |
170 | { return filesystem::exists(file_status{_M_file_type(__ec)}); } |
171 | |
172 | bool |
173 | is_block_file() const |
174 | { return _M_file_type() == file_type::block; } |
175 | |
176 | bool |
177 | is_block_file(error_code& __ec) const noexcept |
178 | { return _M_file_type(__ec) == file_type::block; } |
179 | |
180 | bool |
181 | is_character_file() const |
182 | { return _M_file_type() == file_type::character; } |
183 | |
184 | bool |
185 | is_character_file(error_code& __ec) const noexcept |
186 | { return _M_file_type(__ec) == file_type::character; } |
187 | |
188 | bool |
189 | is_directory() const |
190 | { return _M_file_type() == file_type::directory; } |
191 | |
192 | bool |
193 | is_directory(error_code& __ec) const noexcept |
194 | { return _M_file_type(__ec) == file_type::directory; } |
195 | |
196 | bool |
197 | is_fifo() const |
198 | { return _M_file_type() == file_type::fifo; } |
199 | |
200 | bool |
201 | is_fifo(error_code& __ec) const noexcept |
202 | { return _M_file_type(__ec) == file_type::fifo; } |
203 | |
204 | bool |
205 | is_other() const |
206 | { return filesystem::is_other(file_status{_M_file_type()}); } |
207 | |
208 | bool |
209 | is_other(error_code& __ec) const noexcept |
210 | { return filesystem::is_other(file_status{_M_file_type(__ec)}); } |
211 | |
212 | bool |
213 | is_regular_file() const |
214 | { return _M_file_type() == file_type::regular; } |
215 | |
216 | bool |
217 | is_regular_file(error_code& __ec) const noexcept |
218 | { return _M_file_type(__ec) == file_type::regular; } |
219 | |
220 | bool |
221 | is_socket() const |
222 | { return _M_file_type() == file_type::socket; } |
223 | |
224 | bool |
225 | is_socket(error_code& __ec) const noexcept |
226 | { return _M_file_type(__ec) == file_type::socket; } |
227 | |
228 | bool |
229 | is_symlink() const |
230 | { |
231 | if (_M_type != file_type::none) |
232 | return _M_type == file_type::symlink; |
233 | return symlink_status().type() == file_type::symlink; |
234 | } |
235 | |
236 | bool |
237 | is_symlink(error_code& __ec) const noexcept |
238 | { |
239 | if (_M_type != file_type::none) |
240 | return _M_type == file_type::symlink; |
241 | return symlink_status(__ec).type() == file_type::symlink; |
242 | } |
243 | |
244 | uintmax_t |
245 | file_size() const |
246 | { return filesystem::file_size(_M_path); } |
247 | |
248 | uintmax_t |
249 | file_size(error_code& __ec) const noexcept |
250 | { return filesystem::file_size(_M_path, __ec); } |
251 | |
252 | uintmax_t |
253 | hard_link_count() const |
254 | { return filesystem::hard_link_count(_M_path); } |
255 | |
256 | uintmax_t |
257 | hard_link_count(error_code& __ec) const noexcept |
258 | { return filesystem::hard_link_count(_M_path, __ec); } |
259 | |
260 | file_time_type |
261 | last_write_time() const |
262 | { return filesystem::last_write_time(_M_path); } |
263 | |
264 | |
265 | file_time_type |
266 | last_write_time(error_code& __ec) const noexcept |
267 | { return filesystem::last_write_time(_M_path, __ec); } |
268 | |
269 | file_status |
270 | status() const |
271 | { return filesystem::status(_M_path); } |
272 | |
273 | file_status |
274 | status(error_code& __ec) const noexcept |
275 | { return filesystem::status(_M_path, __ec); } |
276 | |
277 | file_status |
278 | symlink_status() const |
279 | { return filesystem::symlink_status(_M_path); } |
280 | |
281 | file_status |
282 | symlink_status(error_code& __ec) const noexcept |
283 | { return filesystem::symlink_status(_M_path, __ec); } |
284 | |
285 | bool |
286 | operator==(const directory_entry& __rhs) const noexcept |
287 | { return _M_path == __rhs._M_path; } |
288 | |
289 | #if __cpp_lib_three_way_comparison |
290 | strong_ordering |
291 | operator<=>(const directory_entry& __rhs) const noexcept |
292 | { return _M_path <=> __rhs._M_path; } |
293 | #else |
294 | bool |
295 | operator!=(const directory_entry& __rhs) const noexcept |
296 | { return _M_path != __rhs._M_path; } |
297 | |
298 | bool |
299 | operator< (const directory_entry& __rhs) const noexcept |
300 | { return _M_path < __rhs._M_path; } |
301 | |
302 | bool |
303 | operator<=(const directory_entry& __rhs) const noexcept |
304 | { return _M_path <= __rhs._M_path; } |
305 | |
306 | bool |
307 | operator> (const directory_entry& __rhs) const noexcept |
308 | { return _M_path > __rhs._M_path; } |
309 | |
310 | bool |
311 | operator>=(const directory_entry& __rhs) const noexcept |
312 | { return _M_path >= __rhs._M_path; } |
313 | #endif |
314 | |
315 | private: |
316 | friend struct _Dir; |
317 | friend class directory_iterator; |
318 | friend class recursive_directory_iterator; |
319 | |
320 | // _GLIBCXX_RESOLVE_LIB_DEFECTS |
321 | // 3171. LWG 2989 breaks directory_entry stream insertion |
322 | template<typename _CharT, typename _Traits> |
323 | friend basic_ostream<_CharT, _Traits>& |
324 | operator<<(basic_ostream<_CharT, _Traits>& __os, |
325 | const directory_entry& __d) |
326 | { return __os << __d.path(); } |
327 | |
328 | directory_entry(const filesystem::path& __p, file_type __t) |
329 | : _M_path(__p), _M_type(__t) |
330 | { } |
331 | |
332 | // Equivalent to status().type() but uses cached value, if any. |
333 | file_type |
334 | _M_file_type() const |
335 | { |
336 | if (_M_type != file_type::none && _M_type != file_type::symlink) |
337 | return _M_type; |
338 | return status().type(); |
339 | } |
340 | |
341 | // Equivalent to status(__ec).type() but uses cached value, if any. |
342 | file_type |
343 | _M_file_type(error_code& __ec) const noexcept |
344 | { |
345 | if (_M_type != file_type::none && _M_type != file_type::symlink) |
346 | { |
347 | __ec.clear(); |
348 | return _M_type; |
349 | } |
350 | return status(__ec).type(); |
351 | } |
352 | |
353 | filesystem::path _M_path; |
354 | file_type _M_type = file_type::none; |
355 | }; |
356 | |
357 | /// Proxy returned by post-increment on directory iterators. |
358 | struct __directory_iterator_proxy |
359 | { |
360 | const directory_entry& operator*() const& noexcept { return _M_entry; } |
361 | |
362 | directory_entry operator*() && noexcept { return std::move(_M_entry); } |
363 | |
364 | private: |
365 | friend class directory_iterator; |
366 | friend class recursive_directory_iterator; |
367 | |
368 | explicit |
369 | __directory_iterator_proxy(const directory_entry& __e) : _M_entry(__e) { } |
370 | |
371 | directory_entry _M_entry; |
372 | }; |
373 | |
374 | /// Iterator type for traversing the entries in a single directory. |
375 | class directory_iterator |
376 | { |
377 | public: |
378 | typedef directory_entry value_type; |
379 | typedef ptrdiff_t difference_type; |
380 | typedef const directory_entry* pointer; |
381 | typedef const directory_entry& reference; |
382 | typedef input_iterator_tag iterator_category; |
383 | |
384 | directory_iterator() = default; |
385 | |
386 | explicit |
387 | directory_iterator(const path& __p) |
388 | : directory_iterator(__p, directory_options::none, nullptr) { } |
389 | |
390 | directory_iterator(const path& __p, directory_options __options) |
391 | : directory_iterator(__p, __options, nullptr) { } |
392 | |
393 | directory_iterator(const path& __p, error_code& __ec) |
394 | : directory_iterator(__p, directory_options::none, __ec) { } |
395 | |
396 | directory_iterator(const path& __p, directory_options __options, |
397 | error_code& __ec) |
398 | : directory_iterator(__p, __options, &__ec) { } |
399 | |
400 | directory_iterator(const directory_iterator& __rhs) = default; |
401 | |
402 | directory_iterator(directory_iterator&& __rhs) noexcept = default; |
403 | |
404 | ~directory_iterator() = default; |
405 | |
406 | directory_iterator& |
407 | operator=(const directory_iterator& __rhs) = default; |
408 | |
409 | directory_iterator& |
410 | operator=(directory_iterator&& __rhs) noexcept = default; |
411 | |
412 | const directory_entry& operator*() const noexcept; |
413 | const directory_entry* operator->() const noexcept { return &**this; } |
414 | directory_iterator& operator++(); |
415 | directory_iterator& increment(error_code& __ec); |
416 | |
417 | __directory_iterator_proxy operator++(int) |
418 | { |
419 | __directory_iterator_proxy __pr{**this}; |
420 | ++*this; |
421 | return __pr; |
422 | } |
423 | |
424 | friend bool |
425 | operator==(const directory_iterator& __lhs, |
426 | const directory_iterator& __rhs) noexcept |
427 | { |
428 | return !__rhs._M_dir.owner_before(rhs: __lhs._M_dir) |
429 | && !__lhs._M_dir.owner_before(rhs: __rhs._M_dir); |
430 | } |
431 | |
432 | #if __cplusplus >= 202002L |
433 | // _GLIBCXX_RESOLVE_LIB_DEFECTS |
434 | // 3719. Directory iterators should be usable with default sentinel |
435 | bool operator==(default_sentinel_t) const noexcept |
436 | { return !_M_dir; } |
437 | #endif |
438 | |
439 | #if __cpp_impl_three_way_comparison < 201907L |
440 | friend bool |
441 | operator!=(const directory_iterator& __lhs, |
442 | const directory_iterator& __rhs) noexcept |
443 | { return !(__lhs == __rhs); } |
444 | #endif |
445 | |
446 | private: |
447 | directory_iterator(const path&, directory_options, error_code*); |
448 | |
449 | friend class recursive_directory_iterator; |
450 | |
451 | std::__shared_ptr<_Dir> _M_dir; |
452 | }; |
453 | |
454 | /// @relates std::filesystem::directory_iterator @{ |
455 | |
456 | /** @brief Enable range-based `for` using directory_iterator. |
457 | * |
458 | * e.g. `for (auto& entry : std::filesystem::directory_iterator(".")) ...` |
459 | */ |
460 | inline directory_iterator |
461 | begin(directory_iterator __iter) noexcept |
462 | { return __iter; } |
463 | |
464 | /// Return a past-the-end directory_iterator |
465 | inline directory_iterator |
466 | end(directory_iterator) noexcept |
467 | { return directory_iterator(); } |
468 | /// @} |
469 | |
470 | /// Iterator type for recursively traversing a directory hierarchy. |
471 | class recursive_directory_iterator |
472 | { |
473 | public: |
474 | typedef directory_entry value_type; |
475 | typedef ptrdiff_t difference_type; |
476 | typedef const directory_entry* pointer; |
477 | typedef const directory_entry& reference; |
478 | typedef input_iterator_tag iterator_category; |
479 | |
480 | recursive_directory_iterator() = default; |
481 | |
482 | explicit |
483 | recursive_directory_iterator(const path& __p) |
484 | : recursive_directory_iterator(__p, directory_options::none, nullptr) { } |
485 | |
486 | recursive_directory_iterator(const path& __p, directory_options __options) |
487 | : recursive_directory_iterator(__p, __options, nullptr) { } |
488 | |
489 | recursive_directory_iterator(const path& __p, directory_options __options, |
490 | error_code& __ec) |
491 | : recursive_directory_iterator(__p, __options, &__ec) { } |
492 | |
493 | recursive_directory_iterator(const path& __p, error_code& __ec) |
494 | : recursive_directory_iterator(__p, directory_options::none, &__ec) { } |
495 | |
496 | recursive_directory_iterator( |
497 | const recursive_directory_iterator&) = default; |
498 | |
499 | recursive_directory_iterator(recursive_directory_iterator&&) = default; |
500 | |
501 | ~recursive_directory_iterator(); |
502 | |
503 | // observers |
504 | directory_options options() const noexcept; |
505 | int depth() const noexcept; |
506 | bool recursion_pending() const noexcept; |
507 | |
508 | const directory_entry& operator*() const noexcept; |
509 | const directory_entry* operator->() const noexcept { return &**this; } |
510 | |
511 | // modifiers |
512 | recursive_directory_iterator& |
513 | operator=(const recursive_directory_iterator& __rhs) noexcept; |
514 | recursive_directory_iterator& |
515 | operator=(recursive_directory_iterator&& __rhs) noexcept; |
516 | |
517 | recursive_directory_iterator& operator++(); |
518 | recursive_directory_iterator& increment(error_code& __ec); |
519 | |
520 | __directory_iterator_proxy operator++(int) |
521 | { |
522 | __directory_iterator_proxy __pr{**this}; |
523 | ++*this; |
524 | return __pr; |
525 | } |
526 | |
527 | void pop(); |
528 | void pop(error_code&); |
529 | |
530 | void disable_recursion_pending() noexcept; |
531 | |
532 | friend bool |
533 | operator==(const recursive_directory_iterator& __lhs, |
534 | const recursive_directory_iterator& __rhs) noexcept |
535 | { |
536 | return !__rhs._M_dirs.owner_before(rhs: __lhs._M_dirs) |
537 | && !__lhs._M_dirs.owner_before(rhs: __rhs._M_dirs); |
538 | } |
539 | |
540 | #if __cplusplus >= 202002L |
541 | // _GLIBCXX_RESOLVE_LIB_DEFECTS |
542 | // 3719. Directory iterators should be usable with default sentinel |
543 | bool operator==(default_sentinel_t) const noexcept |
544 | { return !_M_dirs; } |
545 | #endif |
546 | |
547 | #if __cpp_impl_three_way_comparison < 201907L |
548 | friend bool |
549 | operator!=(const recursive_directory_iterator& __lhs, |
550 | const recursive_directory_iterator& __rhs) noexcept |
551 | { return !(__lhs == __rhs); } |
552 | #endif |
553 | |
554 | private: |
555 | recursive_directory_iterator(const path&, directory_options, error_code*); |
556 | |
557 | struct _Dir_stack; |
558 | std::__shared_ptr<_Dir_stack> _M_dirs; |
559 | |
560 | recursive_directory_iterator& |
561 | __erase(error_code* = nullptr); |
562 | |
563 | friend uintmax_t |
564 | filesystem::remove_all(const path&, error_code&); |
565 | friend uintmax_t |
566 | filesystem::remove_all(const path&); |
567 | }; |
568 | |
569 | /// @relates std::filesystem::recursive_directory_iterator @{ |
570 | |
571 | /** @brief Enable range-based `for` using recursive_directory_iterator. |
572 | * |
573 | * e.g. `for (auto& entry : recursive_directory_iterator(".")) ...` |
574 | */ |
575 | inline recursive_directory_iterator |
576 | begin(recursive_directory_iterator __iter) noexcept |
577 | { return __iter; } |
578 | |
579 | /// Return a past-the-end recursive_directory_iterator |
580 | inline recursive_directory_iterator |
581 | end(recursive_directory_iterator) noexcept |
582 | { return recursive_directory_iterator(); } |
583 | /// @} |
584 | |
585 | _GLIBCXX_END_NAMESPACE_CXX11 |
586 | |
587 | /// @} group filesystem |
588 | } // namespace filesystem |
589 | |
590 | // Use explicit instantiations of these types. Any inconsistency in the |
591 | // value of __default_lock_policy between code including this header and |
592 | // the library will cause a linker error. |
593 | extern template class |
594 | __shared_ptr<filesystem::_Dir>; |
595 | extern template class |
596 | __shared_ptr<filesystem::recursive_directory_iterator::_Dir_stack>; |
597 | |
598 | _GLIBCXX_END_NAMESPACE_VERSION |
599 | } // namespace std |
600 | |
601 | #endif // C++17 |
602 | |
603 | #endif // _GLIBCXX_FS_DIR_H |
604 | |