CppCommon 1.0.5.0
C++ Common Library
Loading...
Searching...
No Matches
directory_iterator.cpp
Go to the documentation of this file.
1
10
11#include "errors/fatal.h"
12#include "filesystem/symlink.h"
13#include "utility/countof.h"
14
15#include <cstring>
16#include <stack>
17
18#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
19#include <dirent.h>
20#elif defined(_WIN32) || defined(_WIN64)
21#include <windows.h>
22#endif
23
24namespace CppCommon {
25
27
28class DirectoryIterator::Impl
29{
30 friend class SimpleImpl;
31 friend class RecursiveImpl;
32
33public:
34 explicit Impl(const Path& parent) : _parent(parent), _current() {}
35 virtual ~Impl() = default;
36
37 const Path& parent() const noexcept { return _parent; }
38 const Path& current() const noexcept { return _current; }
39
40 virtual Path Next() = 0;
41
42protected:
43 Path _parent;
44 Path _current;
45};
46
47class DirectoryIterator::SimpleImpl : public DirectoryIterator::Impl
48{
49public:
50 explicit SimpleImpl(const Path& parent) : DirectoryIterator::Impl(parent), _next(false), _end(false)
51 {
52#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
53 _directory = opendir(_parent.string().c_str());
54 if (_directory == nullptr)
55 throwex FileSystemException("Cannot open a directory!").Attach(_parent);
56#elif defined(_WIN32) || defined(_WIN64)
57 _directory = FindFirstFileW((_parent / "*").wstring().c_str(), &_entry);
58 if (_directory == INVALID_HANDLE_VALUE)
59 throwex FileSystemException("Cannot open a directory!").Attach(_parent);
60#endif
61 }
62
63 ~SimpleImpl()
64 {
65#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
66 if (_directory != nullptr)
67 {
68 int result = closedir(_directory);
69 if (result != 0)
70 fatality(FileSystemException("Cannot close the directory descriptor!").Attach(_parent));
71 }
72#elif defined(_WIN32) || defined(_WIN64)
73 if (_directory != INVALID_HANDLE_VALUE)
74 {
75 if (!FindClose(_directory))
76 fatality(FileSystemException("Cannot close the directory handle!").Attach(_parent));
77 }
78#endif
79 }
80
81 Path Next() override
82 {
83 if (_end)
84 return _current;
85#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
86 struct dirent* pentry;
87 while ((pentry = readdir(_directory)) != nullptr)
88 {
89 if (std::strncmp(pentry->d_name, ".", sizeof(pentry->d_name)) == 0)
90 continue;
91 if (std::strncmp(pentry->d_name, "..", sizeof(pentry->d_name)) == 0)
92 continue;
93 _current = _parent / pentry->d_name;
94 return _current;
95 }
96#elif defined(_WIN32) || defined(_WIN64)
97 do
98 {
99 if (_next)
100 {
101 _next = false;
102 continue;
103 }
104 if (std::wcsncmp(_entry.cFileName, L".", countof(_entry.cFileName)) == 0)
105 continue;
106 if (std::wcsncmp(_entry.cFileName, L"..", countof(_entry.cFileName)) == 0)
107 continue;
108 _next = true;
109 _current = _parent / _entry.cFileName;
110 return _current;
111 } while (FindNextFileW(_directory, &_entry) != 0);
112
113 if (GetLastError() != ERROR_NO_MORE_FILES)
114 throwex FileSystemException("Cannot read directory entries!").Attach(_parent);
115#endif
116 _end = true;
117 _current = Path();
118 return _current;
119 }
120
121 void Move(SimpleImpl& instance)
122 {
123 _parent = instance._parent;
124 _current = instance._current;
125 _directory = instance._directory;
126 _entry = instance._entry;
127 _next = instance._next;
128 _end = instance._end;
129
130#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
131 instance._directory = nullptr;
132#elif defined(_WIN32) || defined(_WIN64)
133 instance._directory = INVALID_HANDLE_VALUE;
134#endif
135 }
136
137 void Swap(SimpleImpl& instance)
138 {
139 using std::swap;
140 swap(_parent, instance._parent);
141 swap(_current, instance._current);
142 swap(_directory, instance._directory);
143 swap(_entry, instance._entry);
144 swap(_next, instance._next);
145 swap(_end, instance._end);
146 }
147
148private:
149#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
150 DIR* _directory;
151 struct dirent _entry;
152#elif defined(_WIN32) || defined(_WIN64)
153 HANDLE _directory;
154 WIN32_FIND_DATAW _entry;
155#endif
156 bool _next;
157 bool _end;
158};
159
160class DirectoryIterator::RecursiveImpl : public DirectoryIterator::Impl
161{
162public:
163 explicit RecursiveImpl(const Path& parent) : DirectoryIterator::Impl(parent), _current(parent) {}
164 ~RecursiveImpl() = default;
165
166 Path Next() override
167 {
168 // Get the next path value
169 Path result = _current.Next();
170 if (result.empty())
171 {
172 // Immediately return in case of empty stack
173 if (_stack.empty())
174 return result;
175
176 // If the stack has any iterators pop one and return its current path
177 _current.Swap(_stack.top());
178 _stack.pop();
179 return _current.current();
180 }
181
182 // Special check for symbolic link
183 Path target(result);
184 if (result.IsSymlink())
185 target = Symlink(target).target();
186
187 // Special check for directory
188 if (target.IsDirectory())
189 {
190 // Put the current iterator to stack
191 _stack.push(_current);
192
193 // Switch the current iterator to the target sub-directory
194 SimpleImpl subdir = SimpleImpl(result);
195 _current.Move(subdir);
196
197 // Call Next() method for the new iterator
198 return Next();
199 }
200
201 return result;
202 }
203
204private:
205 SimpleImpl _current;
206 std::stack<SimpleImpl> _stack;
207};
208
210
211DirectoryIterator::DirectoryIterator() : _pimpl(nullptr), _current()
212{
213}
214
215DirectoryIterator::DirectoryIterator(const Path& current) : _pimpl(nullptr), _current(current)
216{
217}
218
219DirectoryIterator::DirectoryIterator(const Path& parent, bool recursive) : _pimpl(recursive ? (Impl*)std::make_unique<RecursiveImpl>(parent).release() : (Impl*)std::make_unique<SimpleImpl>(parent).release())
220{
221 _current = _pimpl->Next();
222}
223
224DirectoryIterator::DirectoryIterator(DirectoryIterator& it) : _pimpl(it._pimpl.release()), _current(it._current)
225{
226}
227
228DirectoryIterator::DirectoryIterator(DirectoryIterator&& it) noexcept : _pimpl(std::move(it._pimpl)), _current(std::move(it._current))
229{
230}
231
235
237{
238 _pimpl = std::move(it._pimpl);
239 _current = it._current;
240 return *this;
241}
242
244{
245 _pimpl = std::move(it._pimpl);
246 _current = std::move(it._current);
247 return *this;
248}
249
251{
252 if (_pimpl)
253 _current = _pimpl->Next();
254 return *this;
255}
256
258{
259 DirectoryIterator result(_current);
260 ++(*this);
261 return result;
262}
263
264} // namespace CppCommon
Filesystem directory iterator.
DirectoryIterator & operator=(DirectoryIterator &it)
Filesystem path.
Definition path.h:90
Static array countof definition.
Filesystem directory iterator definition.
#define throwex
Throw extended exception macro.
Definition exceptions.h:23
Fatal abort execution definition.
#define fatality(...)
Fatal abort execution extended macro.
Definition fatal.h:22
C++ Common project definitions.
constexpr size_t countof(const T(&)[N]) noexcept
Count of elements in static array.
Definition countof.h:16
void swap(FileCache &cache1, FileCache &cache2) noexcept
Definition filecache.inl:23