1 // Class filesystem::directory_entry etc. -*- C++ -*-
3 // Copyright (C) 2014-2016 Free Software Foundation, Inc.
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)
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
29 #include <experimental/filesystem>
34 #ifdef _GLIBCXX_HAVE_DIRENT_H
35 # ifdef _GLIBCXX_HAVE_SYS_TYPES_H
36 # include <sys/types.h>
40 # error "the <dirent.h> header is needed to build the Filesystem TS"
43 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
45 # define opendir _wopendir
48 namespace fs
= std::experimental::filesystem
;
52 _Dir() : dirp(nullptr) { }
54 _Dir(DIR* dirp
, const fs::path
& path
) : dirp(dirp
), path(path
) { }
57 : dirp(std::exchange(d
.dirp
, nullptr)), path(std::move(d
.path
)),
58 entry(std::move(d
.entry
)), type(d
.type
)
61 _Dir
& operator=(_Dir
&&) = delete;
63 ~_Dir() { if (dirp
) ::closedir(dirp
); }
65 bool advance(std::error_code
*, directory_options
= directory_options::none
);
69 directory_entry entry
;
70 file_type type
= file_type::none
;
75 template<typename Bitmask
>
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 {}.
85 open_dir(const fs::path
& p
, fs::directory_options options
,
91 if (DIR* dirp
= ::opendir(p
.c_str()))
94 const int err
= errno
;
96 && is_set(options
, fs::directory_options::skip_permission_denied
))
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());
109 get_file_type(const ::dirent
& d
__attribute__((__unused__
)))
111 #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
115 return fs::file_type::block
;
117 return fs::file_type::character
;
119 return fs::file_type::directory
;
121 return fs::file_type::fifo
;
123 return fs::file_type::symlink
;
125 return fs::file_type::regular
;
127 return fs::file_type::socket
;
129 return fs::file_type::unknown
;
131 return fs::file_type::none
;
134 return fs::file_type::none
;
140 // Returns false when the end of the directory entries is reached.
141 // Reports errors by setting ec or throwing.
143 fs::_Dir::advance(error_code
* ec
, directory_options options
)
148 int err
= std::exchange(errno
, 0);
149 const auto entp
= readdir(dirp
);
150 std::swap(errno
, err
);
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
);
164 && is_set(options
, directory_options::skip_permission_denied
))
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());
178 type
= fs::file_type::none
;
183 fs::directory_iterator::
184 directory_iterator(const path
& p
, directory_options options
, error_code
* ec
)
186 _Dir dir
= open_dir(p
, options
, ec
);
190 auto sp
= std::make_shared
<fs::_Dir
>(std::move(dir
));
191 if (sp
->advance(ec
, options
))
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
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++()
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))
224 fs::directory_iterator
&
225 fs::directory_iterator::increment(error_code
& ec
) noexcept
229 ec
= std::make_error_code(errc::invalid_argument
);
232 if (!_M_dir
->advance(&ec
))
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
,
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
))
258 const int err
= errno
;
260 && is_set(options
, fs::directory_options::skip_permission_denied
))
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++()
308 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
309 "cannot increment recursive directory iterator", ec
));
316 recurse(const fs::_Dir
& d
, fs::directory_options options
, std::error_code
& ec
)
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
)
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
)
328 const fs::path
& path
= d
.entry
.path();
329 auto type
= fs::symlink_status(path
, ec
).type();
332 if (type
== fs::file_type::symlink
)
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
347 ec
= std::make_error_code(errc::invalid_argument
);
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
);
359 _M_dirs
->push(std::move(dir
));
362 while (!_M_dirs
->top().advance(&ec
, _M_options
) && !ec
)
365 if (_M_dirs
->empty())
375 fs::recursive_directory_iterator::pop()
378 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
379 "cannot pop non-dereferenceable recursive directory iterator",
380 std::make_error_code(errc::invalid_argument
)));
384 if (_M_dirs
->empty())
389 } while (!_M_dirs
->top().advance(nullptr, _M_options
));