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