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