]> git.ipfire.org Git - thirdparty/gcc.git/blame - libstdc++-v3/src/filesystem/dir.cc
configure.ac (*-*-linux-android*): Set target_makefile_frag.
[thirdparty/gcc.git] / libstdc++-v3 / src / filesystem / dir.cc
CommitLineData
0ca7ba9a
JW
1// Class filesystem::directory_entry etc. -*- C++ -*-
2
cbe34bb5 3// Copyright (C) 2014-2017 Free Software Foundation, Inc.
0ca7ba9a
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
8b756210
JW
25#ifndef _GLIBCXX_USE_CXX11_ABI
26# define _GLIBCXX_USE_CXX11_ABI 1
27#endif
28
0ca7ba9a
JW
29#include <experimental/filesystem>
30#include <utility>
31#include <stack>
0ca7ba9a
JW
32#include <string.h>
33#include <errno.h>
34#ifdef _GLIBCXX_HAVE_DIRENT_H
35# ifdef _GLIBCXX_HAVE_SYS_TYPES_H
36# include <sys/types.h>
37# endif
38# include <dirent.h>
39#else
bf53e6a9 40# error "the <dirent.h> header is needed to build the Filesystem TS"
0ca7ba9a 41#endif
bf53e6a9
JW
42
43#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
44# undef opendir
45# define opendir _wopendir
0ca7ba9a
JW
46#endif
47
48namespace fs = std::experimental::filesystem;
49
0ca7ba9a
JW
50struct fs::_Dir
51{
52 _Dir() : dirp(nullptr) { }
53
54 _Dir(DIR* dirp, const fs::path& path) : dirp(dirp), path(path) { }
55
56 _Dir(_Dir&& d)
57 : dirp(std::exchange(d.dirp, nullptr)), path(std::move(d.path)),
58 entry(std::move(d.entry)), type(d.type)
59 { }
60
61 _Dir& operator=(_Dir&&) = delete;
62
63 ~_Dir() { if (dirp) ::closedir(dirp); }
64
429ee11a 65 bool advance(std::error_code*, directory_options = directory_options::none);
0ca7ba9a
JW
66
67 DIR* dirp;
68 fs::path path;
69 directory_entry entry;
70 file_type type = file_type::none;
71};
72
73namespace
74{
75 template<typename Bitmask>
7b65155f
JW
76 inline bool
77 is_set(Bitmask obj, Bitmask bits)
0ca7ba9a
JW
78 {
79 return (obj & bits) != Bitmask::none;
80 }
81
bb52a7e3 82 // Returns {dirp, p} on success, {} on error (whether ignored or not).
7b65155f
JW
83 inline fs::_Dir
84 open_dir(const fs::path& p, fs::directory_options options,
85 std::error_code* ec)
0ca7ba9a 86 {
429ee11a
JW
87 if (ec)
88 ec->clear();
89
0ca7ba9a
JW
90 if (DIR* dirp = ::opendir(p.c_str()))
91 return {dirp, p};
92
93 const int err = errno;
94 if (err == EACCES
95 && is_set(options, fs::directory_options::skip_permission_denied))
96 return {};
97
98 if (!ec)
99 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
100 "directory iterator cannot open directory", p,
101 std::error_code(err, std::generic_category())));
102
429ee11a 103 ec->assign(err, std::generic_category());
bb52a7e3 104 return {};
0ca7ba9a
JW
105 }
106
107 inline fs::file_type
7b65155f 108 get_file_type(const ::dirent& d __attribute__((__unused__)))
0ca7ba9a
JW
109 {
110#ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
111 switch (d.d_type)
112 {
113 case DT_BLK:
114 return fs::file_type::block;
115 case DT_CHR:
116 return fs::file_type::character;
117 case DT_DIR:
118 return fs::file_type::directory;
119 case DT_FIFO:
120 return fs::file_type::fifo;
121 case DT_LNK:
122 return fs::file_type::symlink;
123 case DT_REG:
124 return fs::file_type::regular;
125 case DT_SOCK:
126 return fs::file_type::socket;
127 case DT_UNKNOWN:
128 return fs::file_type::unknown;
129 default:
130 return fs::file_type::none;
131 }
132#else
133 return fs::file_type::none;
134#endif
135 }
136}
137
7b65155f 138
429ee11a
JW
139// Returns false when the end of the directory entries is reached.
140// Reports errors by setting ec or throwing.
0ca7ba9a 141bool
429ee11a 142fs::_Dir::advance(error_code* ec, directory_options options)
0ca7ba9a 143{
429ee11a
JW
144 if (ec)
145 ec->clear();
146
7b65155f
JW
147 int err = std::exchange(errno, 0);
148 const auto entp = readdir(dirp);
76052545
EB
149 // std::swap cannot be used with Bionic's errno
150 err = std::exchange(errno, err);
7b65155f
JW
151
152 if (entp)
153 {
154 // skip past dot and dot-dot
155 if (!strcmp(entp->d_name, ".") || !strcmp(entp->d_name, ".."))
156 return advance(ec, options);
157 entry = fs::directory_entry{path / entp->d_name};
158 type = get_file_type(*entp);
159 return true;
160 }
161 else if (err)
0ca7ba9a 162 {
429ee11a
JW
163 if (err == EACCES
164 && is_set(options, directory_options::skip_permission_denied))
165 return false;
166
0ca7ba9a
JW
167 if (!ec)
168 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
169 "directory iterator cannot advance",
170 std::error_code(err, std::generic_category())));
429ee11a 171 ec->assign(err, std::generic_category());
bb52a7e3 172 return false;
0ca7ba9a 173 }
0ca7ba9a
JW
174 else
175 {
176 // reached the end
177 entry = {};
178 type = fs::file_type::none;
179 return false;
180 }
181}
182
183fs::directory_iterator::
184directory_iterator(const path& p, directory_options options, error_code* ec)
0ca7ba9a 185{
429ee11a
JW
186 _Dir dir = open_dir(p, options, ec);
187
188 if (dir.dirp)
189 {
190 auto sp = std::make_shared<fs::_Dir>(std::move(dir));
191 if (sp->advance(ec, options))
192 _M_dir.swap(sp);
193 }
0ca7ba9a
JW
194}
195
196const fs::directory_entry&
197fs::directory_iterator::operator*() const
198{
199 if (!_M_dir)
200 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
201 "non-dereferenceable directory iterator",
202 std::make_error_code(errc::invalid_argument)));
203 return _M_dir->entry;
204}
205
206fs::directory_iterator&
207fs::directory_iterator::operator++()
208{
209 if (!_M_dir)
210 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
211 "cannot advance non-dereferenceable directory iterator",
212 std::make_error_code(errc::invalid_argument)));
213 if (!_M_dir->advance(nullptr))
214 _M_dir.reset();
215 return *this;
216}
217
218fs::directory_iterator&
219fs::directory_iterator::increment(error_code& ec) noexcept
220{
221 if (!_M_dir)
222 {
223 ec = std::make_error_code(errc::invalid_argument);
224 return *this;
225 }
226 if (!_M_dir->advance(&ec))
227 _M_dir.reset();
228 return *this;
229}
230
231using Dir_iter_pair = std::pair<fs::_Dir, fs::directory_iterator>;
232
233struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir>
234{
235 void clear() { c.clear(); }
236};
237
238fs::recursive_directory_iterator::
239recursive_directory_iterator(const path& p, directory_options options,
429ee11a 240 error_code* ec)
0ca7ba9a
JW
241: _M_options(options), _M_pending(true)
242{
243 if (DIR* dirp = ::opendir(p.c_str()))
244 {
7b65155f
JW
245 auto sp = std::make_shared<_Dir_stack>();
246 sp->push(_Dir{ dirp, p });
247 if (sp->top().advance(ec))
248 _M_dirs.swap(sp);
0ca7ba9a
JW
249 }
250 else
251 {
252 const int err = errno;
253 if (err == EACCES
254 && is_set(options, fs::directory_options::skip_permission_denied))
429ee11a
JW
255 {
256 if (ec)
257 ec->clear();
258 return;
259 }
0ca7ba9a
JW
260
261 if (!ec)
262 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
263 "recursive directory iterator cannot open directory", p,
264 std::error_code(err, std::generic_category())));
265
266 ec->assign(err, std::generic_category());
267 }
268}
269
270fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
271
272int
273fs::recursive_directory_iterator::depth() const
274{
275 return int(_M_dirs->size()) - 1;
276}
277
278const fs::directory_entry&
279fs::recursive_directory_iterator::operator*() const
280{
281 return _M_dirs->top().entry;
282}
283
284fs::recursive_directory_iterator&
285fs::recursive_directory_iterator::
286operator=(const recursive_directory_iterator& other) noexcept = default;
287
288fs::recursive_directory_iterator&
289fs::recursive_directory_iterator::
290operator=(recursive_directory_iterator&& other) noexcept = default;
291
292fs::recursive_directory_iterator&
293fs::recursive_directory_iterator::operator++()
294{
295 error_code ec;
296 increment(ec);
297 if (ec.value())
298 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
299 "cannot increment recursive directory iterator", ec));
300 return *this;
301}
302
303namespace
304{
305 bool
306 recurse(const fs::_Dir& d, fs::directory_options options, std::error_code& ec)
307 {
308 bool follow_symlink
309 = is_set(options, fs::directory_options::follow_directory_symlink);
310#ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
311 if (d.type == fs::file_type::directory)
312 return true;
313 if (d.type == fs::file_type::symlink && follow_symlink)
314 return d.entry.status().type() == fs::file_type::directory;
315 if (d.type != fs::file_type::none && d.type != fs::file_type::unknown)
316 return false;
317#endif
318 const fs::path& path = d.entry.path();
319 auto type = fs::symlink_status(path, ec).type();
320 if (ec.value())
321 return false;
322 if (type == fs::file_type::symlink)
323 {
324 if (!follow_symlink)
325 return false;
326 type = fs::status(path, ec).type();
327 }
328 return type == fs::file_type::directory;
329 }
330}
331
332fs::recursive_directory_iterator&
333fs::recursive_directory_iterator::increment(error_code& ec) noexcept
334{
335 if (!_M_dirs)
336 {
337 ec = std::make_error_code(errc::invalid_argument);
338 return *this;
339 }
340
341 auto& top = _M_dirs->top();
342
343 if (std::exchange(_M_pending, true) && recurse(top, _M_options, ec))
344 {
429ee11a
JW
345 _Dir dir = open_dir(top.entry.path(), _M_options, &ec);
346 if (ec)
b4e7e6bf
JW
347 {
348 _M_dirs.reset();
349 return *this;
350 }
0ca7ba9a 351 if (dir.dirp)
0ca7ba9a 352 _M_dirs->push(std::move(dir));
0ca7ba9a
JW
353 }
354
429ee11a 355 while (!_M_dirs->top().advance(&ec, _M_options) && !ec)
0ca7ba9a
JW
356 {
357 _M_dirs->pop();
358 if (_M_dirs->empty())
359 {
360 _M_dirs.reset();
361 return *this;
362 }
363 }
364 return *this;
365}
366
367void
ec0b1056 368fs::recursive_directory_iterator::pop(error_code& ec)
0ca7ba9a
JW
369{
370 if (!_M_dirs)
ec0b1056
JW
371 {
372 ec = std::make_error_code(errc::invalid_argument);
373 return;
374 }
0ca7ba9a
JW
375
376 do {
377 _M_dirs->pop();
378 if (_M_dirs->empty())
379 {
380 _M_dirs.reset();
ec0b1056 381 ec.clear();
0ca7ba9a
JW
382 return;
383 }
ec0b1056
JW
384 } while (!_M_dirs->top().advance(&ec, _M_options));
385}
386
387void
388fs::recursive_directory_iterator::pop()
389{
390 error_code ec;
391 pop(ec);
392 if (ec)
393 _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
394 ? "recursive directory iterator cannot pop"
395 : "non-dereferenceable recursive directory iterator cannot pop",
396 ec));
0ca7ba9a 397}