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