PR target/71375
[official-gcc.git] / libstdc++-v3 / src / filesystem / dir.cc
blob6ff12d04e9f83d545606269ee973a16ab15e9cc2
1 // Class filesystem::directory_entry etc. -*- C++ -*-
3 // Copyright (C) 2014-2016 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.
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.
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.
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/>.
25 #ifndef _GLIBCXX_USE_CXX11_ABI
26 # define _GLIBCXX_USE_CXX11_ABI 1
27 #endif
29 #include <experimental/filesystem>
30 #include <utility>
31 #include <stack>
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
40 # error "the <dirent.h> header is needed to build the Filesystem TS"
41 #endif
43 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
44 # undef opendir
45 # define opendir _wopendir
46 #endif
48 namespace fs = std::experimental::filesystem;
50 struct fs::_Dir
52 _Dir() : dirp(nullptr) { }
54 _Dir(DIR* dirp, const fs::path& path) : dirp(dirp), path(path) { }
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 { }
61 _Dir& operator=(_Dir&&) = delete;
63 ~_Dir() { if (dirp) ::closedir(dirp); }
65 bool advance(std::error_code*, directory_options = directory_options::none);
67 DIR* dirp;
68 fs::path path;
69 directory_entry entry;
70 file_type type = file_type::none;
73 namespace
75 template<typename Bitmask>
76 inline bool
77 is_set(Bitmask obj, Bitmask bits)
79 return (obj & bits) != Bitmask::none;
82 // Returns {dirp, p} on success, {nullptr, p} on error.
83 // If an ignored EACCES error occurs returns {}.
84 inline fs::_Dir
85 open_dir(const fs::path& p, fs::directory_options options,
86 std::error_code* ec)
88 if (ec)
89 ec->clear();
91 if (DIR* dirp = ::opendir(p.c_str()))
92 return {dirp, p};
94 const int err = errno;
95 if (err == EACCES
96 && is_set(options, fs::directory_options::skip_permission_denied))
97 return {};
99 if (!ec)
100 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
101 "directory iterator cannot open directory", p,
102 std::error_code(err, std::generic_category())));
104 ec->assign(err, std::generic_category());
105 return {nullptr, p};
108 inline fs::file_type
109 get_file_type(const ::dirent& d __attribute__((__unused__)))
111 #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
112 switch (d.d_type)
114 case DT_BLK:
115 return fs::file_type::block;
116 case DT_CHR:
117 return fs::file_type::character;
118 case DT_DIR:
119 return fs::file_type::directory;
120 case DT_FIFO:
121 return fs::file_type::fifo;
122 case DT_LNK:
123 return fs::file_type::symlink;
124 case DT_REG:
125 return fs::file_type::regular;
126 case DT_SOCK:
127 return fs::file_type::socket;
128 case DT_UNKNOWN:
129 return fs::file_type::unknown;
130 default:
131 return fs::file_type::none;
133 #else
134 return fs::file_type::none;
135 #endif
140 // Returns false when the end of the directory entries is reached.
141 // Reports errors by setting ec or throwing.
142 bool
143 fs::_Dir::advance(error_code* ec, directory_options options)
145 if (ec)
146 ec->clear();
148 int err = std::exchange(errno, 0);
149 const auto entp = readdir(dirp);
150 std::swap(errno, err);
152 if (entp)
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;
161 else if (err)
163 if (err == EACCES
164 && is_set(options, directory_options::skip_permission_denied))
165 return false;
167 if (!ec)
168 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
169 "directory iterator cannot advance",
170 std::error_code(err, std::generic_category())));
171 ec->assign(err, std::generic_category());
172 return true;
174 else
176 // reached the end
177 entry = {};
178 type = fs::file_type::none;
179 return false;
183 fs::directory_iterator::
184 directory_iterator(const path& p, directory_options options, error_code* ec)
186 _Dir dir = open_dir(p, options, ec);
188 if (dir.dirp)
190 auto sp = std::make_shared<fs::_Dir>(std::move(dir));
191 if (sp->advance(ec, options))
192 _M_dir.swap(sp);
194 else if (!dir.path.empty())
196 // An error occurred, we need a non-empty shared_ptr so that *this will
197 // not compare equal to the end iterator.
198 _M_dir.reset(static_cast<fs::_Dir*>(nullptr));
202 const fs::directory_entry&
203 fs::directory_iterator::operator*() const
205 if (!_M_dir)
206 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
207 "non-dereferenceable directory iterator",
208 std::make_error_code(errc::invalid_argument)));
209 return _M_dir->entry;
212 fs::directory_iterator&
213 fs::directory_iterator::operator++()
215 if (!_M_dir)
216 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
217 "cannot advance non-dereferenceable directory iterator",
218 std::make_error_code(errc::invalid_argument)));
219 if (!_M_dir->advance(nullptr))
220 _M_dir.reset();
221 return *this;
224 fs::directory_iterator&
225 fs::directory_iterator::increment(error_code& ec) noexcept
227 if (!_M_dir)
229 ec = std::make_error_code(errc::invalid_argument);
230 return *this;
232 if (!_M_dir->advance(&ec))
233 _M_dir.reset();
234 return *this;
237 using Dir_iter_pair = std::pair<fs::_Dir, fs::directory_iterator>;
239 struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir>
241 void clear() { c.clear(); }
244 fs::recursive_directory_iterator::
245 recursive_directory_iterator(const path& p, directory_options options,
246 error_code* ec)
247 : _M_options(options), _M_pending(true)
249 if (DIR* dirp = ::opendir(p.c_str()))
251 auto sp = std::make_shared<_Dir_stack>();
252 sp->push(_Dir{ dirp, p });
253 if (sp->top().advance(ec))
254 _M_dirs.swap(sp);
256 else
258 const int err = errno;
259 if (err == EACCES
260 && is_set(options, fs::directory_options::skip_permission_denied))
262 if (ec)
263 ec->clear();
264 return;
267 if (!ec)
268 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
269 "recursive directory iterator cannot open directory", p,
270 std::error_code(err, std::generic_category())));
272 ec->assign(err, std::generic_category());
274 // An error occurred, we need a non-empty shared_ptr so that *this will
275 // not compare equal to the end iterator.
276 _M_dirs.reset(static_cast<_Dir_stack*>(nullptr));
280 fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
283 fs::recursive_directory_iterator::depth() const
285 return int(_M_dirs->size()) - 1;
288 const fs::directory_entry&
289 fs::recursive_directory_iterator::operator*() const
291 return _M_dirs->top().entry;
294 fs::recursive_directory_iterator&
295 fs::recursive_directory_iterator::
296 operator=(const recursive_directory_iterator& other) noexcept = default;
298 fs::recursive_directory_iterator&
299 fs::recursive_directory_iterator::
300 operator=(recursive_directory_iterator&& other) noexcept = default;
302 fs::recursive_directory_iterator&
303 fs::recursive_directory_iterator::operator++()
305 error_code ec;
306 increment(ec);
307 if (ec.value())
308 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
309 "cannot increment recursive directory iterator", ec));
310 return *this;
313 namespace
315 bool
316 recurse(const fs::_Dir& d, fs::directory_options options, std::error_code& ec)
318 bool follow_symlink
319 = is_set(options, fs::directory_options::follow_directory_symlink);
320 #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
321 if (d.type == fs::file_type::directory)
322 return true;
323 if (d.type == fs::file_type::symlink && follow_symlink)
324 return d.entry.status().type() == fs::file_type::directory;
325 if (d.type != fs::file_type::none && d.type != fs::file_type::unknown)
326 return false;
327 #endif
328 const fs::path& path = d.entry.path();
329 auto type = fs::symlink_status(path, ec).type();
330 if (ec.value())
331 return false;
332 if (type == fs::file_type::symlink)
334 if (!follow_symlink)
335 return false;
336 type = fs::status(path, ec).type();
338 return type == fs::file_type::directory;
342 fs::recursive_directory_iterator&
343 fs::recursive_directory_iterator::increment(error_code& ec) noexcept
345 if (!_M_dirs)
347 ec = std::make_error_code(errc::invalid_argument);
348 return *this;
351 auto& top = _M_dirs->top();
353 if (std::exchange(_M_pending, true) && recurse(top, _M_options, ec))
355 _Dir dir = open_dir(top.entry.path(), _M_options, &ec);
356 if (ec)
357 return *this;
358 if (dir.dirp)
359 _M_dirs->push(std::move(dir));
362 while (!_M_dirs->top().advance(&ec, _M_options) && !ec)
364 _M_dirs->pop();
365 if (_M_dirs->empty())
367 _M_dirs.reset();
368 return *this;
371 return *this;
374 void
375 fs::recursive_directory_iterator::pop()
377 if (!_M_dirs)
378 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
379 "cannot pop non-dereferenceable recursive directory iterator",
380 std::make_error_code(errc::invalid_argument)));
382 do {
383 _M_dirs->pop();
384 if (_M_dirs->empty())
386 _M_dirs.reset();
387 return;
389 } while (!_M_dirs->top().advance(nullptr, _M_options));