CppCommon  1.0.4.1
C++ Common Library
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 
24 namespace CppCommon {
25 
27 
28 class DirectoryIterator::Impl
29 {
30  friend class SimpleImpl;
31  friend class RecursiveImpl;
32 
33 public:
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 
42 protected:
43  Path _parent;
44  Path _current;
45 };
46 
47 class DirectoryIterator::SimpleImpl : public DirectoryIterator::Impl
48 {
49 public:
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 
148 private:
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 
160 class DirectoryIterator::RecursiveImpl : public DirectoryIterator::Impl
161 {
162 public:
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 
204 private:
205  SimpleImpl _current;
206  std::stack<SimpleImpl> _stack;
207 };
208 
210 
211 DirectoryIterator::DirectoryIterator() : _pimpl(nullptr), _current()
212 {
213 }
214 
215 DirectoryIterator::DirectoryIterator(const Path& current) : _pimpl(nullptr), _current(current)
216 {
217 }
218 
219 DirectoryIterator::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 
224 DirectoryIterator::DirectoryIterator(DirectoryIterator& it) : _pimpl(it._pimpl.release()), _current(it._current)
225 {
226 }
227 
228 DirectoryIterator::DirectoryIterator(DirectoryIterator&& it) noexcept : _pimpl(std::move(it._pimpl)), _current(std::move(it._current))
229 {
230 }
231 
233 {
234 }
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)
DirectoryIterator & operator++()
void swap(DirectoryIterator &it) noexcept
Swap two instances.
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.
Definition: token_bucket.h:15
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