]> git.ipfire.org Git - thirdparty/gcc.git/blame - libstdc++-v3/src/c++17/fs_dir.cc
Update copyright years.
[thirdparty/gcc.git] / libstdc++-v3 / src / c++17 / fs_dir.cc
CommitLineData
641cb5a6
JW
1// Class filesystem::directory_entry etc. -*- C++ -*-
2
99dee823 3// Copyright (C) 2014-2021 Free Software Foundation, Inc.
641cb5a6
JW
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#ifndef _GLIBCXX_USE_CXX11_ABI
26# define _GLIBCXX_USE_CXX11_ABI 1
27#endif
28
2fc11587 29#include <bits/largefile-config.h>
641cb5a6 30#include <filesystem>
641cb5a6
JW
31#include <utility>
32#include <stack>
33#include <string.h>
34#include <errno.h>
35#define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
36#define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
de4db54f 37#include "../filesystem/dir-common.h"
641cb5a6
JW
38
39namespace fs = std::filesystem;
9534a5e6 40namespace posix = std::filesystem::__gnu_posix;
641cb5a6 41
da29d2a3
JW
42template class std::__shared_ptr<fs::_Dir>;
43template class std::__shared_ptr<fs::recursive_directory_iterator::_Dir_stack>;
44
641cb5a6
JW
45struct fs::_Dir : _Dir_base
46{
47 _Dir(const fs::path& p, bool skip_permission_denied, error_code& ec)
48 : _Dir_base(p.c_str(), skip_permission_denied, ec)
49 {
50 if (!ec)
51 path = p;
52 }
53
9534a5e6 54 _Dir(posix::DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
641cb5a6
JW
55
56 _Dir(_Dir&&) = default;
57
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
61 {
62 if (const auto entp = _Dir_base::advance(skip_permission_denied, ec))
63 {
8d531548
JW
64 auto name = path;
65 name /= entp->d_name;
5ed5c0da
JW
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);
71#endif
72 entry = fs::directory_entry{std::move(name), type};
641cb5a6
JW
73 return true;
74 }
75 else if (!ec)
76 {
77 // reached the end
78 entry = {};
79 }
80 return false;
81 }
82
83 bool advance(error_code& ec) noexcept { return advance(false, ec); }
84
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)
88 {
89 error_code ec;
90 const bool ok = advance(skip_permission_denied, ec);
91 if (ec)
92 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
93 "directory iterator cannot advance", ec));
94 return ok;
95 }
96
97 bool should_recurse(bool follow_symlink, error_code& ec) const
98 {
99 file_type type = entry._M_type;
5ed5c0da 100 if (type == file_type::none)
641cb5a6
JW
101 {
102 type = entry.symlink_status(ec).type();
103 if (ec)
104 return false;
105 }
106
107 if (type == file_type::directory)
108 return true;
109 if (type == file_type::symlink)
110 return follow_symlink && is_directory(entry.status(ec));
111 return false;
112 }
113
114 fs::path path;
115 directory_entry entry;
116};
117
118namespace
119{
120 template<typename Bitmask>
121 inline bool
122 is_set(Bitmask obj, Bitmask bits)
123 {
124 return (obj & bits) != Bitmask::none;
125 }
126}
127
128fs::directory_iterator::
129directory_iterator(const path& p, directory_options options, error_code* ecptr)
130{
131 const bool skip_permission_denied
132 = is_set(options, directory_options::skip_permission_denied);
133
134 error_code ec;
135 _Dir dir(p, skip_permission_denied, ec);
136
137 if (dir.dirp)
138 {
da29d2a3 139 auto sp = std::__make_shared<fs::_Dir>(std::move(dir));
641cb5a6
JW
140 if (sp->advance(skip_permission_denied, ec))
141 _M_dir.swap(sp);
142 }
143 if (ecptr)
144 *ecptr = ec;
145 else if (ec)
146 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
147 "directory iterator cannot open directory", p, ec));
148}
149
150const fs::directory_entry&
044b0434 151fs::directory_iterator::operator*() const noexcept
641cb5a6 152{
641cb5a6
JW
153 return _M_dir->entry;
154}
155
156fs::directory_iterator&
157fs::directory_iterator::operator++()
158{
159 if (!_M_dir)
160 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
161 "cannot advance non-dereferenceable directory iterator",
162 std::make_error_code(errc::invalid_argument)));
163 if (!_M_dir->advance())
164 _M_dir.reset();
165 return *this;
166}
167
168fs::directory_iterator&
29453a9f 169fs::directory_iterator::increment(error_code& ec)
641cb5a6
JW
170{
171 if (!_M_dir)
172 {
173 ec = std::make_error_code(errc::invalid_argument);
174 return *this;
175 }
176 if (!_M_dir->advance(ec))
177 _M_dir.reset();
178 return *this;
179}
180
181struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir>
182{
c7dde4a9
JW
183 _Dir_stack(directory_options opts, posix::DIR* dirp, const path& p)
184 : options(opts), pending(true)
185 {
186 this->emplace(dirp, p);
187 }
188
189 const directory_options options;
190 bool pending;
191
641cb5a6
JW
192 void clear() { c.clear(); }
193};
194
195fs::recursive_directory_iterator::
196recursive_directory_iterator(const path& p, directory_options options,
197 error_code* ecptr)
641cb5a6 198{
9534a5e6 199 if (posix::DIR* dirp = posix::opendir(p.c_str()))
641cb5a6
JW
200 {
201 if (ecptr)
202 ecptr->clear();
c7dde4a9 203 auto sp = std::__make_shared<_Dir_stack>(options, dirp, p);
641cb5a6
JW
204 if (ecptr ? sp->top().advance(*ecptr) : sp->top().advance())
205 _M_dirs.swap(sp);
206 }
207 else
208 {
209 const int err = errno;
210 if (err == EACCES
211 && is_set(options, fs::directory_options::skip_permission_denied))
212 {
213 if (ecptr)
214 ecptr->clear();
215 return;
216 }
217
218 if (!ecptr)
219 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
220 "recursive directory iterator cannot open directory", p,
221 std::error_code(err, std::generic_category())));
222
223 ecptr->assign(err, std::generic_category());
224 }
225}
226
227fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
228
c7dde4a9
JW
229fs::directory_options
230fs::recursive_directory_iterator::options() const noexcept
231{
232 return _M_dirs->options;
233}
234
641cb5a6 235int
c7dde4a9 236fs::recursive_directory_iterator::depth() const noexcept
641cb5a6
JW
237{
238 return int(_M_dirs->size()) - 1;
239}
240
c7dde4a9
JW
241bool
242fs::recursive_directory_iterator::recursion_pending() const noexcept
243{
244 return _M_dirs->pending;
245}
246
641cb5a6 247const fs::directory_entry&
c7dde4a9 248fs::recursive_directory_iterator::operator*() const noexcept
641cb5a6
JW
249{
250 return _M_dirs->top().entry;
251}
252
253fs::recursive_directory_iterator&
254fs::recursive_directory_iterator::
255operator=(const recursive_directory_iterator& other) noexcept = default;
256
257fs::recursive_directory_iterator&
258fs::recursive_directory_iterator::
259operator=(recursive_directory_iterator&& other) noexcept = default;
260
261fs::recursive_directory_iterator&
262fs::recursive_directory_iterator::operator++()
263{
264 error_code ec;
265 increment(ec);
266 if (ec)
267 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
268 "cannot increment recursive directory iterator", ec));
269 return *this;
270}
271
272fs::recursive_directory_iterator&
29453a9f 273fs::recursive_directory_iterator::increment(error_code& ec)
641cb5a6
JW
274{
275 if (!_M_dirs)
276 {
277 ec = std::make_error_code(errc::invalid_argument);
278 return *this;
279 }
280
281 const bool follow
c7dde4a9 282 = is_set(_M_dirs->options, directory_options::follow_directory_symlink);
641cb5a6 283 const bool skip_permission_denied
c7dde4a9 284 = is_set(_M_dirs->options, directory_options::skip_permission_denied);
641cb5a6
JW
285
286 auto& top = _M_dirs->top();
287
c7dde4a9 288 if (std::exchange(_M_dirs->pending, true) && top.should_recurse(follow, ec))
641cb5a6
JW
289 {
290 _Dir dir(top.entry.path(), skip_permission_denied, ec);
291 if (ec)
292 {
293 _M_dirs.reset();
294 return *this;
295 }
296 if (dir.dirp)
297 _M_dirs->push(std::move(dir));
298 }
299
300 while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec)
301 {
302 _M_dirs->pop();
303 if (_M_dirs->empty())
304 {
305 _M_dirs.reset();
306 return *this;
307 }
308 }
309 return *this;
310}
311
312void
313fs::recursive_directory_iterator::pop(error_code& ec)
314{
315 if (!_M_dirs)
316 {
317 ec = std::make_error_code(errc::invalid_argument);
318 return;
319 }
320
321 const bool skip_permission_denied
c7dde4a9 322 = is_set(_M_dirs->options, directory_options::skip_permission_denied);
641cb5a6
JW
323
324 do {
325 _M_dirs->pop();
326 if (_M_dirs->empty())
327 {
328 _M_dirs.reset();
329 ec.clear();
330 return;
331 }
332 } while (!_M_dirs->top().advance(skip_permission_denied, ec));
333}
334
335void
336fs::recursive_directory_iterator::pop()
337{
338 error_code ec;
339 pop(ec);
340 if (ec)
341 _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
342 ? "recursive directory iterator cannot pop"
343 : "non-dereferenceable recursive directory iterator cannot pop",
344 ec));
345}
c7dde4a9
JW
346
347void
348fs::recursive_directory_iterator::disable_recursion_pending() noexcept
349{
350 _M_dirs->pending = false;
351}