1 // Class filesystem::directory_entry etc. -*- C++ -*-
3 // Copyright (C) 2014-2020 Free Software Foundation, Inc.
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)
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.
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.
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/>.
25 #ifndef _GLIBCXX_USE_CXX11_ABI
26 # define _GLIBCXX_USE_CXX11_ABI 1
29 #include <bits/largefile-config.h>
35 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
36 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
37 #include "../filesystem/dir-common.h"
39 namespace fs
= std::filesystem
;
40 namespace posix
= std::filesystem::__gnu_posix
;
42 template class std::__shared_ptr
<fs::_Dir
>;
43 template class std::__shared_ptr
<fs::recursive_directory_iterator::_Dir_stack
>;
45 struct fs::_Dir
: _Dir_base
47 _Dir(const fs::path
& p
, bool skip_permission_denied
, error_code
& ec
)
48 : _Dir_base(p
.c_str(), skip_permission_denied
, ec
)
54 _Dir(posix::DIR* dirp
, const path
& p
) : _Dir_base(dirp
), path(p
) { }
56 _Dir(_Dir
&&) = default;
58 // Returns false when the end of the directory entries is reached.
59 // Reports errors by setting ec.
60 bool advance(bool skip_permission_denied
, error_code
& ec
) noexcept
62 if (const auto entp
= _Dir_base::advance(skip_permission_denied
, ec
))
66 file_type type
= file_type::none
;
67 #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
68 // Even if the OS supports dirent::d_type the filesystem might not:
69 if (entp
->d_type
!= DT_UNKNOWN
)
70 type
= get_file_type(*entp
);
72 entry
= fs::directory_entry
{std::move(name
), type
};
83 bool advance(error_code
& ec
) noexcept
{ return advance(false, ec
); }
85 // Returns false when the end of the directory entries is reached.
86 // Reports errors by throwing.
87 bool advance(bool skip_permission_denied
= false)
90 const bool ok
= advance(skip_permission_denied
, ec
);
92 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
93 "directory iterator cannot advance", ec
));
97 bool should_recurse(bool follow_symlink
, error_code
& ec
) const
99 file_type type
= entry
._M_type
;
100 if (type
== file_type::none
)
102 type
= entry
.symlink_status(ec
).type();
107 if (type
== file_type::directory
)
109 if (type
== file_type::symlink
)
110 return follow_symlink
&& is_directory(entry
.status(ec
));
115 directory_entry entry
;
120 template<typename Bitmask
>
122 is_set(Bitmask obj
, Bitmask bits
)
124 return (obj
& bits
) != Bitmask::none
;
128 fs::directory_iterator::
129 directory_iterator(const path
& p
, directory_options options
, error_code
* ecptr
)
131 const bool skip_permission_denied
132 = is_set(options
, directory_options::skip_permission_denied
);
135 _Dir
dir(p
, skip_permission_denied
, ec
);
139 auto sp
= std::__make_shared
<fs::_Dir
>(std::move(dir
));
140 if (sp
->advance(skip_permission_denied
, ec
))
146 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
147 "directory iterator cannot open directory", p
, ec
));
150 const fs::directory_entry
&
151 fs::directory_iterator::operator*() const
154 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
155 "non-dereferenceable directory iterator",
156 std::make_error_code(errc::invalid_argument
)));
157 return _M_dir
->entry
;
160 fs::directory_iterator
&
161 fs::directory_iterator::operator++()
164 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
165 "cannot advance non-dereferenceable directory iterator",
166 std::make_error_code(errc::invalid_argument
)));
167 if (!_M_dir
->advance())
172 fs::directory_iterator
&
173 fs::directory_iterator::increment(error_code
& ec
)
177 ec
= std::make_error_code(errc::invalid_argument
);
180 if (!_M_dir
->advance(ec
))
185 struct fs::recursive_directory_iterator::_Dir_stack
: std::stack
<_Dir
>
187 _Dir_stack(directory_options opts
, posix::DIR* dirp
, const path
& p
)
188 : options(opts
), pending(true)
190 this->emplace(dirp
, p
);
193 const directory_options options
;
196 void clear() { c
.clear(); }
199 fs::recursive_directory_iterator::
200 recursive_directory_iterator(const path
& p
, directory_options options
,
203 if (posix::DIR* dirp
= posix::opendir(p
.c_str()))
207 auto sp
= std::__make_shared
<_Dir_stack
>(options
, dirp
, p
);
208 if (ecptr
? sp
->top().advance(*ecptr
) : sp
->top().advance())
213 const int err
= errno
;
215 && is_set(options
, fs::directory_options::skip_permission_denied
))
223 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
224 "recursive directory iterator cannot open directory", p
,
225 std::error_code(err
, std::generic_category())));
227 ecptr
->assign(err
, std::generic_category());
231 fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
233 fs::directory_options
234 fs::recursive_directory_iterator::options() const noexcept
236 return _M_dirs
->options
;
240 fs::recursive_directory_iterator::depth() const noexcept
242 return int(_M_dirs
->size()) - 1;
246 fs::recursive_directory_iterator::recursion_pending() const noexcept
248 return _M_dirs
->pending
;
251 const fs::directory_entry
&
252 fs::recursive_directory_iterator::operator*() const noexcept
254 return _M_dirs
->top().entry
;
257 fs::recursive_directory_iterator
&
258 fs::recursive_directory_iterator::
259 operator=(const recursive_directory_iterator
& other
) noexcept
= default;
261 fs::recursive_directory_iterator
&
262 fs::recursive_directory_iterator::
263 operator=(recursive_directory_iterator
&& other
) noexcept
= default;
265 fs::recursive_directory_iterator
&
266 fs::recursive_directory_iterator::operator++()
271 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
272 "cannot increment recursive directory iterator", ec
));
276 fs::recursive_directory_iterator
&
277 fs::recursive_directory_iterator::increment(error_code
& ec
)
281 ec
= std::make_error_code(errc::invalid_argument
);
286 = is_set(_M_dirs
->options
, directory_options::follow_directory_symlink
);
287 const bool skip_permission_denied
288 = is_set(_M_dirs
->options
, directory_options::skip_permission_denied
);
290 auto& top
= _M_dirs
->top();
292 if (std::exchange(_M_dirs
->pending
, true) && top
.should_recurse(follow
, ec
))
294 _Dir
dir(top
.entry
.path(), skip_permission_denied
, ec
);
301 _M_dirs
->push(std::move(dir
));
304 while (!_M_dirs
->top().advance(skip_permission_denied
, ec
) && !ec
)
307 if (_M_dirs
->empty())
317 fs::recursive_directory_iterator::pop(error_code
& ec
)
321 ec
= std::make_error_code(errc::invalid_argument
);
325 const bool skip_permission_denied
326 = is_set(_M_dirs
->options
, directory_options::skip_permission_denied
);
330 if (_M_dirs
->empty())
336 } while (!_M_dirs
->top().advance(skip_permission_denied
, ec
));
340 fs::recursive_directory_iterator::pop()
345 _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
346 ? "recursive directory iterator cannot pop"
347 : "non-dereferenceable recursive directory iterator cannot pop",
352 fs::recursive_directory_iterator::disable_recursion_pending() noexcept
354 _M_dirs
->pending
= false;