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