1 // Class filesystem::directory_entry etc. -*- C++ -*-
3 // Copyright (C) 2014-2017 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, {} on error (whether ignored or not).
84 open_dir(const fs::path
& p
, fs::directory_options options
,
90 if (DIR* dirp
= ::opendir(p
.c_str()))
93 const int err
= errno
;
95 && is_set(options
, fs::directory_options::skip_permission_denied
))
99 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
100 "directory iterator cannot open directory", p
,
101 std::error_code(err
, std::generic_category())));
103 ec
->assign(err
, std::generic_category());
108 get_file_type(const ::dirent
& d
__attribute__((__unused__
)))
110 #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
114 return fs::file_type::block
;
116 return fs::file_type::character
;
118 return fs::file_type::directory
;
120 return fs::file_type::fifo
;
122 return fs::file_type::symlink
;
124 return fs::file_type::regular
;
126 return fs::file_type::socket
;
128 return fs::file_type::unknown
;
130 return fs::file_type::none
;
133 return fs::file_type::none
;
139 // Returns false when the end of the directory entries is reached.
140 // Reports errors by setting ec or throwing.
142 fs::_Dir::advance(error_code
* ec
, directory_options options
)
147 int err
= std::exchange(errno
, 0);
148 const auto entp
= readdir(dirp
);
149 // std::swap cannot be used with Bionic's errno
150 err
= std::exchange(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
))
196 const fs::directory_entry
&
197 fs::directory_iterator::operator*() const
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
;
206 fs::directory_iterator
&
207 fs::directory_iterator::operator++()
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))
218 fs::directory_iterator
&
219 fs::directory_iterator::increment(error_code
& ec
) noexcept
223 ec
= std::make_error_code(errc::invalid_argument
);
226 if (!_M_dir
->advance(&ec
))
231 using Dir_iter_pair
= std::pair
<fs::_Dir
, fs::directory_iterator
>;
233 struct fs::recursive_directory_iterator::_Dir_stack
: std::stack
<_Dir
>
235 void clear() { c
.clear(); }
238 fs::recursive_directory_iterator::
239 recursive_directory_iterator(const path
& p
, directory_options options
,
241 : _M_options(options
), _M_pending(true)
243 if (DIR* dirp
= ::opendir(p
.c_str()))
245 auto sp
= std::make_shared
<_Dir_stack
>();
246 sp
->push(_Dir
{ dirp
, p
});
247 if (sp
->top().advance(ec
))
252 const int err
= errno
;
254 && is_set(options
, fs::directory_options::skip_permission_denied
))
262 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
263 "recursive directory iterator cannot open directory", p
,
264 std::error_code(err
, std::generic_category())));
266 ec
->assign(err
, std::generic_category());
270 fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
273 fs::recursive_directory_iterator::depth() const
275 return int(_M_dirs
->size()) - 1;
278 const fs::directory_entry
&
279 fs::recursive_directory_iterator::operator*() const
281 return _M_dirs
->top().entry
;
284 fs::recursive_directory_iterator
&
285 fs::recursive_directory_iterator::
286 operator=(const recursive_directory_iterator
& other
) noexcept
= default;
288 fs::recursive_directory_iterator
&
289 fs::recursive_directory_iterator::
290 operator=(recursive_directory_iterator
&& other
) noexcept
= default;
292 fs::recursive_directory_iterator
&
293 fs::recursive_directory_iterator::operator++()
298 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
299 "cannot increment recursive directory iterator", ec
));
306 recurse(const fs::_Dir
& d
, fs::directory_options options
, std::error_code
& ec
)
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
)
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
)
318 const fs::path
& path
= d
.entry
.path();
319 auto type
= fs::symlink_status(path
, ec
).type();
322 if (type
== fs::file_type::symlink
)
326 type
= fs::status(path
, ec
).type();
328 return type
== fs::file_type::directory
;
332 fs::recursive_directory_iterator
&
333 fs::recursive_directory_iterator::increment(error_code
& ec
) noexcept
337 ec
= std::make_error_code(errc::invalid_argument
);
341 auto& top
= _M_dirs
->top();
343 if (std::exchange(_M_pending
, true) && recurse(top
, _M_options
, ec
))
345 _Dir dir
= open_dir(top
.entry
.path(), _M_options
, &ec
);
352 _M_dirs
->push(std::move(dir
));
355 while (!_M_dirs
->top().advance(&ec
, _M_options
) && !ec
)
358 if (_M_dirs
->empty())
368 fs::recursive_directory_iterator::pop(error_code
& ec
)
372 ec
= std::make_error_code(errc::invalid_argument
);
378 if (_M_dirs
->empty())
384 } while (!_M_dirs
->top().advance(&ec
, _M_options
));
388 fs::recursive_directory_iterator::pop()
393 _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
394 ? "recursive directory iterator cannot pop"
395 : "non-dereferenceable recursive directory iterator cannot pop",