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 #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
)
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
);
68 type
= file_type::none
;
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)
80 const bool ok
= advance(skip_permission_denied
, ec
);
82 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
83 "directory iterator cannot advance", ec
));
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();
97 if (type
== file_type::directory
)
99 if (type
== file_type::symlink
)
100 return follow_symlink
&& is_directory(entry
.status(ec
));
105 directory_entry entry
;
106 file_type type
= file_type::none
;
111 template<typename Bitmask
>
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
);
126 _Dir
dir(p
, skip_permission_denied
, ec
);
130 auto sp
= std::make_shared
<fs::_Dir
>(std::move(dir
));
131 if (sp
->advance(skip_permission_denied
, 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
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++()
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())
163 fs::directory_iterator
&
164 fs::directory_iterator::increment(error_code
& ec
) noexcept
168 ec
= std::make_error_code(errc::invalid_argument
);
171 if (!_M_dir
->advance(ec
))
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
,
184 : _M_options(options
), _M_pending(true)
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
))
197 const int err
= errno
;
199 && is_set(options
, fs::directory_options::skip_permission_denied
))
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++()
239 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
240 "cannot increment recursive directory iterator", ec
));
244 fs::recursive_directory_iterator
&
245 fs::recursive_directory_iterator::increment(error_code
& ec
) noexcept
249 ec
= std::make_error_code(errc::invalid_argument
);
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
);
269 _M_dirs
->push(std::move(dir
));
272 while (!_M_dirs
->top().advance(skip_permission_denied
, ec
) && !ec
)
275 if (_M_dirs
->empty())
285 fs::recursive_directory_iterator::pop(error_code
& ec
)
289 ec
= std::make_error_code(errc::invalid_argument
);
293 const bool skip_permission_denied
294 = is_set(_M_options
, directory_options::skip_permission_denied
);
298 if (_M_dirs
->empty())
304 } while (!_M_dirs
->top().advance(skip_permission_denied
, ec
));
308 fs::recursive_directory_iterator::pop()
313 _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
314 ? "recursive directory iterator cannot pop"
315 : "non-dereferenceable recursive directory iterator cannot pop",