CppCommon  1.0.4.1
C++ Common Library
directory.cpp
Go to the documentation of this file.
1 
9 #include "filesystem/directory.h"
10 
11 #include "utility/countof.h"
12 #include "utility/resource.h"
13 
14 #include <cstring>
15 #include <regex>
16 
17 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
18 #include <sys/stat.h>
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22 #elif defined(_WIN32) || defined(_WIN64)
23 #include <windows.h>
24 #endif
25 
26 namespace CppCommon {
27 
28 const Flags<FileAttributes> Directory::DEFAULT_ATTRIBUTES = FileAttributes::NORMAL;
30 
32 {
33 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
34  struct stat status;
35  int result = stat(string().c_str(), &status);
36  if (result != 0)
37  {
38  if ((errno == ENOENT) || (errno == ENOTDIR))
39  return false;
40  else
41  throwex FileSystemException("Cannot get the status of the directory!").Attach(*this);
42  }
43 
44  if (S_ISDIR(status.st_mode))
45  return true;
46  else
47  return false;
48 #elif defined(_WIN32) || defined(_WIN64)
49  DWORD attributes = GetFileAttributesW(wstring().c_str());
50  if (attributes == INVALID_FILE_ATTRIBUTES)
51  return false;
52 
53  if (attributes & FILE_ATTRIBUTE_DIRECTORY)
54  return true;
55  else
56  return false;
57 #endif
58 }
59 
61 {
62 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
63  DIR* dir = opendir(string().c_str());
64  if (dir == nullptr)
65  throwex FileSystemException("Cannot open a directory!").Attach(*this);
66 
67  // Smart resource cleaner pattern
68  auto directory = resource(dir, [](DIR* dirp) { closedir(dirp); });
69 
70  struct dirent* pentry;
71  while ((pentry = readdir(dir)) != nullptr)
72  {
73  if (std::strncmp(pentry->d_name, ".", sizeof(pentry->d_name)) == 0)
74  continue;
75  if (std::strncmp(pentry->d_name, "..", sizeof(pentry->d_name)) == 0)
76  continue;
77  return false;
78  }
79 
80  // Release the resource manually
81  int result = closedir(dir);
82  if (result != 0)
83  throwex FileSystemException("Cannot close a directory!").Attach(*this);
84  directory.release();
85 
86  return true;
87 #elif defined(_WIN32) || defined(_WIN64)
88  WIN32_FIND_DATAW fd;
89  HANDLE hDirectory = FindFirstFileW((*this / "*").wstring().c_str(), &fd);
90  if (hDirectory == INVALID_HANDLE_VALUE)
91  throwex FileSystemException("Cannot open a directory!").Attach(*this);
92 
93  // Smart resource cleaner pattern
94  auto directory = resource(hDirectory, [](HANDLE hFindFile) { FindClose(hFindFile); });
95 
96  do
97  {
98  if (std::wcsncmp(fd.cFileName, L".", countof(fd.cFileName)) == 0)
99  continue;
100  if (std::wcsncmp(fd.cFileName, L"..", countof(fd.cFileName)) == 0)
101  continue;
102  return false;
103  } while (FindNextFileW(hDirectory, &fd) != 0);
104 
105  if (GetLastError() != ERROR_NO_MORE_FILES)
106  throwex FileSystemException("Cannot read directory entries!").Attach(*this);
107 
108  // Release the resource manually
109  if (!FindClose(hDirectory))
110  throwex FileSystemException("Cannot close a directory!").Attach(*this);
111  directory.release();
112 
113  return true;
114 #endif
115 }
116 
118 {
119  return DirectoryIterator(*this, false);
120 }
121 
123 {
124  return DirectoryIterator();
125 }
126 
128 {
129  return DirectoryIterator(*this, true);
130 }
131 
133 {
134  return DirectoryIterator();
135 }
136 
137 std::vector<Path> Directory::GetEntries(const std::string& pattern)
138 {
139  std::vector<Path> result;
140  std::regex matcher(pattern);
141  for (auto it = begin(); it != end(); ++it)
142  if (pattern.empty() || std::regex_match(it->filename().string(), matcher))
143  result.push_back(*it);
144  return result;
145 }
146 
147 std::vector<Path> Directory::GetEntriesRecursive(const std::string& pattern)
148 {
149  std::vector<Path> result;
150  std::regex matcher(pattern);
151  for (auto it = rbegin(); it != rend(); ++it)
152  if (pattern.empty() || std::regex_match(it->filename().string(), matcher))
153  result.push_back(Directory(*it));
154  return result;
155 }
156 
157 std::vector<Directory> Directory::GetDirectories(const std::string& pattern)
158 {
159  std::vector<Directory> result;
160  std::regex matcher(pattern);
161  for (auto it = begin(); it != end(); ++it)
162  {
163  // Special check for symbolic link
164  Directory target(*it);
165  if (it->IsSymlink())
166  target = Symlink(target).target();
167 
168  // Special check for directory
169  if (target.IsDirectory())
170  if (pattern.empty() || std::regex_match(it->filename().string(), matcher))
171  result.emplace_back(*it);
172  }
173  return result;
174 }
175 
176 std::vector<Directory> Directory::GetDirectoriesRecursive(const std::string& pattern)
177 {
178  std::vector<Directory> result;
179  std::regex matcher(pattern);
180  for (auto it = rbegin(); it != rend(); ++it)
181  {
182  // Special check for symbolic link
183  Directory target(*it);
184  if (it->IsSymlink())
185  target = Symlink(target).target();
186 
187  // Special check for directory
188  if (target.IsDirectory())
189  if (pattern.empty() || std::regex_match(it->filename().string(), matcher))
190  result.emplace_back(*it);
191  }
192  return result;
193 }
194 
195 std::vector<File> Directory::GetFiles(const std::string& pattern)
196 {
197  std::vector<File> result;
198  std::regex matcher(pattern);
199  for (auto it = begin(); it != end(); ++it)
200  {
201  // Special check for symbolic link
202  File target(*it);
203  if (it->IsSymlink())
204  target = Symlink(target).target();
205 
206  // Special check for directory
207  if (!target.IsDirectory())
208  if (pattern.empty() || std::regex_match(it->filename().string(), matcher))
209  result.emplace_back(*it);
210  }
211  return result;
212 }
213 
214 std::vector<File> Directory::GetFilesRecursive(const std::string& pattern)
215 {
216  std::vector<File> result;
217  std::regex matcher(pattern);
218  for (auto it = rbegin(); it != rend(); ++it)
219  {
220  // Special check for symbolic link
221  File target(*it);
222  if (it->IsSymlink())
223  target = Symlink(target).target();
224 
225  // Special check for directory
226  if (!target.IsDirectory())
227  if (pattern.empty() || std::regex_match(it->filename().string(), matcher))
228  result.emplace_back(*it);
229  }
230  return result;
231 }
232 
233 std::vector<Symlink> Directory::GetSymlinks(const std::string& pattern)
234 {
235  std::vector<Symlink> result;
236  std::regex matcher(pattern);
237  for (auto it = begin(); it != end(); ++it)
238  {
239  // Special check for symbolic link
240  if (it->IsSymlink())
241  if (pattern.empty() || std::regex_match(it->filename().string(), matcher))
242  result.emplace_back(*it);
243  }
244  return result;
245 }
246 
247 std::vector<Symlink> Directory::GetSymlinksRecursive(const std::string& pattern)
248 {
249  std::vector<Symlink> result;
250  std::regex matcher(pattern);
251  for (auto it = rbegin(); it != rend(); ++it)
252  {
253  // Special check for symbolic link
254  if (it->IsSymlink())
255  if (pattern.empty() || std::regex_match(it->filename().string(), matcher))
256  result.emplace_back(*it);
257  }
258  return result;
259 }
260 
261 Directory Directory::Create(const Path& path, const Flags<FileAttributes>& attributes, const Flags<FilePermissions>& permissions)
262 {
263  Directory directory(path);
264  if (directory.IsDirectoryExists())
265  return directory;
266 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
267  mode_t mode = 0;
269  mode |= S_IRUSR;
271  mode |= S_IWUSR;
273  mode |= S_IXUSR;
275  mode |= S_IRGRP;
277  mode |= S_IWGRP;
279  mode |= S_IXGRP;
281  mode |= S_IROTH;
283  mode |= S_IWOTH;
285  mode |= S_IXOTH;
287  mode |= S_ISUID;
289  mode |= S_ISGID;
291  mode |= S_ISVTX;
292 
293  int result = mkdir(path.string().c_str(), mode);
294  if (result != 0)
295  throwex FileSystemException("Cannot create directory!").Attach(path);
296 #elif defined(_WIN32) || defined(_WIN64)
297  if (!CreateDirectoryW(path.wstring().c_str(), nullptr))
298  throwex FileSystemException("Cannot create directory!").Attach(path);
299 #endif
300  return directory;
301 }
302 
303 Directory Directory::CreateTree(const Path& path, const Flags<FileAttributes>& attributes, const Flags<FilePermissions>& permissions)
304 {
305  Directory tree(path);
306  if (tree.IsDirectoryExists())
307  return tree;
308 
309  // Try to create the last directory
310  try
311  {
312  Create(tree);
313  return tree;
314  }
315  catch (const FileSystemException&) {}
316 
317  // Failed, try to get the parent path and retry
318  Directory parent = tree.parent();
319  if (parent.empty())
320  throwex FileSystemException("Cannot create directory tree!").Attach(path);
321  else
323 
324  // Retry to create the last directory
325  Create(tree);
326 
327  return tree;
328 }
329 
330 } // namespace CppCommon
Filesystem directory.
Definition: directory.h:25
std::vector< File > GetFilesRecursive(const std::string &pattern="")
Recursively get all files (including symbolic link files) in the current directory.
Definition: directory.cpp:214
static const Flags< FileAttributes > DEFAULT_ATTRIBUTES
Default directory attributes (Normal)
Definition: directory.h:28
std::vector< Path > GetEntriesRecursive(const std::string &pattern="")
Recursively get all entries (directories, files, symbolic links) in the current directory.
Definition: directory.cpp:147
bool IsDirectoryExists() const
Is the directory exists?
Definition: directory.cpp:31
DirectoryIterator begin() const
Get the directory begin iterator.
Definition: directory.cpp:117
static Directory CreateTree(const Path &path, const Flags< FileAttributes > &attributes=Directory::DEFAULT_ATTRIBUTES, const Flags< FilePermissions > &permissions=Directory::DEFAULT_PERMISSIONS)
Create full directory tree of the given path.
Definition: directory.cpp:303
DirectoryIterator end() const
Get the directory end iterator.
Definition: directory.cpp:122
std::vector< Symlink > GetSymlinksRecursive(const std::string &pattern="")
Recursively get all symbolic links (including symbolic link directories) in the current directory.
Definition: directory.cpp:247
static const Flags< FilePermissions > DEFAULT_PERMISSIONS
Default directory permissions (IRUSR | IWUSR | IXUSR | IRGRP | IXGRP | IROTH | IXOTH)
Definition: directory.h:30
std::vector< Directory > GetDirectoriesRecursive(const std::string &pattern="")
Recursively get all directories (including symbolic link directories) in the current directory.
Definition: directory.cpp:176
std::vector< Symlink > GetSymlinks(const std::string &pattern="")
Get all symbolic links (including symbolic link directories) in the current directory.
Definition: directory.cpp:233
std::vector< Path > GetEntries(const std::string &pattern="")
Get all entries (directories, files, symbolic links) in the current directory.
Definition: directory.cpp:137
DirectoryIterator rbegin() const
Get the directory recursive begin iterator.
Definition: directory.cpp:127
std::vector< Directory > GetDirectories(const std::string &pattern="")
Get all directories (including symbolic link directories) in the current directory.
Definition: directory.cpp:157
static Directory Create(const Path &path, const Flags< FileAttributes > &attributes=Directory::DEFAULT_ATTRIBUTES, const Flags< FilePermissions > &permissions=Directory::DEFAULT_PERMISSIONS)
Create directory from the given path.
Definition: directory.cpp:261
std::vector< File > GetFiles(const std::string &pattern="")
Get all files (including symbolic link files) in the current directory.
Definition: directory.cpp:195
bool IsDirectoryEmpty() const
Is the directory empty?
Definition: directory.cpp:60
DirectoryIterator rend() const
Get the directory recursive end iterator.
Definition: directory.cpp:132
Filesystem directory iterator.
Filesystem file.
Definition: file.h:28
File system exception.
Definition: exceptions.h:19
FileSystemException & Attach(const Path &path)
Attach the given path to the exception.
Definition: exceptions.h:33
Filesystem path.
Definition: path.h:90
std::wstring wstring() const
Get the path value as a wide string.
Definition: path.h:153
Flags< FileAttributes > attributes() const
Get the path file attributes.
Definition: path.cpp:472
bool IsDirectory() const
Is the path points to directory?
Definition: path.h:217
Path parent() const
Decompose parent path from the current path.
Definition: path.cpp:144
bool empty() const noexcept
Is the path empty?
Definition: path.h:191
Flags< FilePermissions > permissions() const
Get the path file permissions.
Definition: path.cpp:499
const std::string & string() const noexcept
Get the path value as UTF-8 string.
Definition: path.h:151
Static array countof definition.
Filesystem directory definition.
#define throwex
Throw extended exception macro.
Definition: exceptions.h:23
C++ Common project definitions.
Definition: token_bucket.h:15
auto resource(T handle, TCleaner cleaner)
Resource smart cleaner pattern.
Definition: resource.h:43
constexpr size_t countof(const T(&)[N]) noexcept
Count of elements in static array.
Definition: countof.h:16
@ IXOTH
Execute or search permission bit for other users.
@ IWUSR
Write permission bit for the owner of the file.
@ IWGRP
Write permission bit for the group owner of the file.
@ IXUSR
Execute (for ordinary files) or search (for directories) permission bit for the owner of the file.
@ ISVTX
This is the sticky bit.
@ IRGRP
Read permission bit for the group owner of the file.
@ IRUSR
Read permission bit for the owner of the file.
@ IXGRP
Execute or search permission bit for the group owner of the file.
@ IWOTH
Write permission bit for other users.
@ IROTH
Read permission bit for other users.
@ ISUID
This is the set-user-ID on execute bit.
@ ISGID
This is the set-group-ID on execute bit.
Resource smart cleaner pattern definition.