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