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(__p); 142 refresh(); 143 } 144 145 void 146 replace_filename(const filesystem::path& __p, error_code& __ec) 147 { 148 _M_path.replace_filename(__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(__lhs._M_dir) 429 && !__lhs._M_dir.owner_before(__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(__lhs._M_dirs) 537 && !__lhs._M_dirs.owner_before(__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