PR libstdc++/82777 fix path normalization for dot-dot
[official-gcc.git] / libstdc++-v3 / src / filesystem / dir.cc
blob42f63e739849574251dbc40c2785486bb46571fb
1 // Class filesystem::directory_entry etc. -*- C++ -*-
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.
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 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
35 namespace experimental { namespace filesystem {
36 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
37 #include "dir-common.h"
39 namespace fs = std::experimental::filesystem;
41 struct fs::_Dir : std::filesystem::_Dir_base
43 _Dir(const fs::path& p, bool skip_permission_denied, error_code& ec)
44 : _Dir_base(p.c_str(), skip_permission_denied, ec)
46 if (!ec)
47 path = p;
50 _Dir(DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
52 _Dir(_Dir&&) = default;
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
58 if (const auto entp = _Dir_base::advance(skip_permission_denied, ec))
60 entry = fs::directory_entry{path / entp->d_name};
61 type = get_file_type(*entp);
62 return true;
64 else if (!ec)
66 // reached the end
67 entry = {};
68 type = file_type::none;
70 return false;
73 bool advance(error_code& ec) noexcept { return advance(false, ec); }
75 // Returns false when the end of the directory entries is reached.
76 // Reports errors by throwing.
77 bool advance(bool skip_permission_denied = false)
79 error_code ec;
80 const bool ok = advance(skip_permission_denied, ec);
81 if (ec)
82 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
83 "directory iterator cannot advance", ec));
84 return ok;
87 bool should_recurse(bool follow_symlink, error_code& ec) const
89 file_type type = this->type;
90 if (type == file_type::none || type == file_type::unknown)
92 type = entry.symlink_status(ec).type();
93 if (ec)
94 return false;
97 if (type == file_type::directory)
98 return true;
99 if (type == file_type::symlink)
100 return follow_symlink && is_directory(entry.status(ec));
101 return false;
104 fs::path path;
105 directory_entry entry;
106 file_type type = file_type::none;
109 namespace
111 template<typename Bitmask>
112 inline bool
113 is_set(Bitmask obj, Bitmask bits)
115 return (obj & bits) != Bitmask::none;
119 fs::directory_iterator::
120 directory_iterator(const path& p, directory_options options, error_code* ecptr)
122 const bool skip_permission_denied
123 = is_set(options, directory_options::skip_permission_denied);
125 error_code ec;
126 _Dir dir(p, skip_permission_denied, ec);
128 if (dir.dirp)
130 auto sp = std::make_shared<fs::_Dir>(std::move(dir));
131 if (sp->advance(skip_permission_denied, ec))
132 _M_dir.swap(sp);
134 if (ecptr)
135 *ecptr = ec;
136 else if (ec)
137 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
138 "directory iterator cannot open directory", p, ec));
141 const fs::directory_entry&
142 fs::directory_iterator::operator*() const
144 if (!_M_dir)
145 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
146 "non-dereferenceable directory iterator",
147 std::make_error_code(errc::invalid_argument)));
148 return _M_dir->entry;
151 fs::directory_iterator&
152 fs::directory_iterator::operator++()
154 if (!_M_dir)
155 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
156 "cannot advance non-dereferenceable directory iterator",
157 std::make_error_code(errc::invalid_argument)));
158 if (!_M_dir->advance())
159 _M_dir.reset();
160 return *this;
163 fs::directory_iterator&
164 fs::directory_iterator::increment(error_code& ec) noexcept
166 if (!_M_dir)
168 ec = std::make_error_code(errc::invalid_argument);
169 return *this;
171 if (!_M_dir->advance(ec))
172 _M_dir.reset();
173 return *this;
176 struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir>
178 void clear() { c.clear(); }
181 fs::recursive_directory_iterator::
182 recursive_directory_iterator(const path& p, directory_options options,
183 error_code* ec)
184 : _M_options(options), _M_pending(true)
186 if (ec)
187 ec->clear();
188 if (DIR* dirp = ::opendir(p.c_str()))
190 auto sp = std::make_shared<_Dir_stack>();
191 sp->push(_Dir{ dirp, p });
192 if (sp->top().advance(ec))
193 _M_dirs.swap(sp);
195 else
197 const int err = errno;
198 if (err == EACCES
199 && is_set(options, fs::directory_options::skip_permission_denied))
200 return;
202 if (!ec)
203 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
204 "recursive directory iterator cannot open directory", p,
205 std::error_code(err, std::generic_category())));
207 ec->assign(err, std::generic_category());
211 fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
214 fs::recursive_directory_iterator::depth() const
216 return int(_M_dirs->size()) - 1;
219 const fs::directory_entry&
220 fs::recursive_directory_iterator::operator*() const
222 return _M_dirs->top().entry;
225 fs::recursive_directory_iterator&
226 fs::recursive_directory_iterator::
227 operator=(const recursive_directory_iterator& other) noexcept = default;
229 fs::recursive_directory_iterator&
230 fs::recursive_directory_iterator::
231 operator=(recursive_directory_iterator&& other) noexcept = default;
233 fs::recursive_directory_iterator&
234 fs::recursive_directory_iterator::operator++()
236 error_code ec;
237 increment(ec);
238 if (ec.value())
239 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
240 "cannot increment recursive directory iterator", ec));
241 return *this;
244 fs::recursive_directory_iterator&
245 fs::recursive_directory_iterator::increment(error_code& ec) noexcept
247 if (!_M_dirs)
249 ec = std::make_error_code(errc::invalid_argument);
250 return *this;
253 const bool follow
254 = is_set(_M_options, directory_options::follow_directory_symlink);
255 const bool skip_permission_denied
256 = is_set(_M_options, directory_options::skip_permission_denied);
258 auto& top = _M_dirs->top();
260 if (std::exchange(_M_pending, true) && top.should_recurse(follow, ec))
262 _Dir dir(top.entry.path(), skip_permission_denied, ec);
263 if (ec)
265 _M_dirs.reset();
266 return *this;
268 if (dir.dirp)
269 _M_dirs->push(std::move(dir));
272 while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec)
274 _M_dirs->pop();
275 if (_M_dirs->empty())
277 _M_dirs.reset();
278 return *this;
281 return *this;
284 void
285 fs::recursive_directory_iterator::pop(error_code& ec)
287 if (!_M_dirs)
289 ec = std::make_error_code(errc::invalid_argument);
290 return;
293 const bool skip_permission_denied
294 = is_set(_M_options, directory_options::skip_permission_denied);
296 do {
297 _M_dirs->pop();
298 if (_M_dirs->empty())
300 _M_dirs.reset();
301 ec.clear();
302 return;
304 } while (!_M_dirs->top().advance(skip_permission_denied, ec));
307 void
308 fs::recursive_directory_iterator::pop()
310 error_code ec;
311 pop(ec);
312 if (ec)
313 _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
314 ? "recursive directory iterator cannot pop"
315 : "non-dereferenceable recursive directory iterator cannot pop",
316 ec));