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(errno
, err
);
153 // skip past dot and dot-dot
154 if (!strcmp(entp
->d_name
, ".") || !strcmp(entp
->d_name
, ".."))
155 return advance(ec
, options
);
156 entry
= fs::directory_entry
{path
/ entp
->d_name
};
157 type
= get_file_type(*entp
);
163 && is_set(options
, directory_options::skip_permission_denied
))
167 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
168 "directory iterator cannot advance",
169 std::error_code(err
, std::generic_category())));
170 ec
->assign(err
, std::generic_category());
177 type
= fs::file_type::none
;
182 fs::directory_iterator::
183 directory_iterator(const path
& p
, directory_options options
, error_code
* ec
)
185 _Dir dir
= open_dir(p
, options
, ec
);
189 auto sp
= std::make_shared
<fs::_Dir
>(std::move(dir
));
190 if (sp
->advance(ec
, options
))
195 const fs::directory_entry
&
196 fs::directory_iterator::operator*() const
199 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
200 "non-dereferenceable directory iterator",
201 std::make_error_code(errc::invalid_argument
)));
202 return _M_dir
->entry
;
205 fs::directory_iterator
&
206 fs::directory_iterator::operator++()
209 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
210 "cannot advance non-dereferenceable directory iterator",
211 std::make_error_code(errc::invalid_argument
)));
212 if (!_M_dir
->advance(nullptr))
217 fs::directory_iterator
&
218 fs::directory_iterator::increment(error_code
& ec
) noexcept
222 ec
= std::make_error_code(errc::invalid_argument
);
225 if (!_M_dir
->advance(&ec
))
230 using Dir_iter_pair
= std::pair
<fs::_Dir
, fs::directory_iterator
>;
232 struct fs::recursive_directory_iterator::_Dir_stack
: std::stack
<_Dir
>
234 void clear() { c
.clear(); }
237 fs::recursive_directory_iterator::
238 recursive_directory_iterator(const path
& p
, directory_options options
,
240 : _M_options(options
), _M_pending(true)
242 if (DIR* dirp
= ::opendir(p
.c_str()))
244 auto sp
= std::make_shared
<_Dir_stack
>();
245 sp
->push(_Dir
{ dirp
, p
});
246 if (sp
->top().advance(ec
))
251 const int err
= errno
;
253 && is_set(options
, fs::directory_options::skip_permission_denied
))
261 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
262 "recursive directory iterator cannot open directory", p
,
263 std::error_code(err
, std::generic_category())));
265 ec
->assign(err
, std::generic_category());
269 fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
272 fs::recursive_directory_iterator::depth() const
274 return int(_M_dirs
->size()) - 1;
277 const fs::directory_entry
&
278 fs::recursive_directory_iterator::operator*() const
280 return _M_dirs
->top().entry
;
283 fs::recursive_directory_iterator
&
284 fs::recursive_directory_iterator::
285 operator=(const recursive_directory_iterator
& other
) noexcept
= default;
287 fs::recursive_directory_iterator
&
288 fs::recursive_directory_iterator::
289 operator=(recursive_directory_iterator
&& other
) noexcept
= default;
291 fs::recursive_directory_iterator
&
292 fs::recursive_directory_iterator::operator++()
297 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
298 "cannot increment recursive directory iterator", ec
));
305 recurse(const fs::_Dir
& d
, fs::directory_options options
, std::error_code
& ec
)
308 = is_set(options
, fs::directory_options::follow_directory_symlink
);
309 #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
310 if (d
.type
== fs::file_type::directory
)
312 if (d
.type
== fs::file_type::symlink
&& follow_symlink
)
313 return d
.entry
.status().type() == fs::file_type::directory
;
314 if (d
.type
!= fs::file_type::none
&& d
.type
!= fs::file_type::unknown
)
317 const fs::path
& path
= d
.entry
.path();
318 auto type
= fs::symlink_status(path
, ec
).type();
321 if (type
== fs::file_type::symlink
)
325 type
= fs::status(path
, ec
).type();
327 return type
== fs::file_type::directory
;
331 fs::recursive_directory_iterator
&
332 fs::recursive_directory_iterator::increment(error_code
& ec
) noexcept
336 ec
= std::make_error_code(errc::invalid_argument
);
340 auto& top
= _M_dirs
->top();
342 if (std::exchange(_M_pending
, true) && recurse(top
, _M_options
, ec
))
344 _Dir dir
= open_dir(top
.entry
.path(), _M_options
, &ec
);
351 _M_dirs
->push(std::move(dir
));
354 while (!_M_dirs
->top().advance(&ec
, _M_options
) && !ec
)
357 if (_M_dirs
->empty())
367 fs::recursive_directory_iterator::pop(error_code
& ec
)
371 ec
= std::make_error_code(errc::invalid_argument
);
377 if (_M_dirs
->empty())
383 } while (!_M_dirs
->top().advance(&ec
, _M_options
));
387 fs::recursive_directory_iterator::pop()
392 _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
393 ? "recursive directory iterator cannot pop"
394 : "non-dereferenceable recursive directory iterator cannot pop",