CppCommon  1.0.4.1
C++ Common Library
path.cpp
Go to the documentation of this file.
1 
9 #include "filesystem/path.h"
10 
11 #include "filesystem/directory.h"
12 #include "filesystem/symlink.h"
13 #include "system/uuid.h"
14 #include "utility/countof.h"
15 #include "utility/resource.h"
16 
17 #include <algorithm>
18 #include <regex>
19 #include <stack>
20 #include <tuple>
21 #include <vector>
22 
23 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
24 #if defined(__APPLE__)
25 #include <libproc.h>
26 #elif defined(linux) || defined(__linux) || defined(__linux__)
27 #include <sys/sendfile.h>
28 #endif
29 #include <sys/statvfs.h>
30 #include <sys/stat.h>
31 #include <sys/time.h>
32 #include <fcntl.h>
33 #include <limits.h>
34 #include <pwd.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #elif defined(_WIN32) || defined(_WIN64)
38 #include <windows.h>
39 #include <userenv.h>
40 #endif
41 
42 namespace CppCommon {
43 
45 namespace Internals {
46 
47 Path initial = Path::current();
48 
49 std::pair<Path, size_t> root(const std::string& path)
50 {
51  bool root_found = false;
52  size_t root_length = 0;
53 
54  // Unix case 1: "/" or "/foo"
55  if (((path.size() == 1) && ((path[0] == '\\') || (path[0] == '/'))) || ((path.size() > 1) && ((path[0] == '\\') || (path[0] == '/')) && ((path[1] != '\\') && (path[1] != '/'))))
56  {
57  root_length = 1;
58 
59  return std::make_pair(Path("/"), root_length);
60  }
61 
62  // Unix case 2: "///foo"
63  if ((path.size() > 2) && ((path[0] == '\\') || (path[0] == '/')) && ((path[1] == '\\') || (path[1] == '/')) && ((path[2] == '\\') || (path[2] == '/')))
64  {
65  root_length = 3;
66 
67  // Find root position
68  while (root_length < path.size())
69  {
70  if ((path[root_length] != '\\') && (path[root_length] != '/'))
71  break;
72  ++root_length;
73  }
74 
75  return std::make_pair(Path("/"), root_length);
76  }
77 
78  // Windows case 1: "\\net" or "//net"
79  if ((path.size() > 2) && ((path[0] == '\\') || (path[0] == '/')) && ((path[1] == '\\') || (path[1] == '/')) && ((path[2] != '\\') && (path[2] != '/') && (path[2] != '?')))
80  {
81  root_length = 3;
82 
83  // Find root position
84  while (root_length < path.size())
85  {
86  if ((path[root_length] == '\\') || (path[root_length] == '/'))
87  {
88  ++root_length;
89  break;
90  }
91  ++root_length;
92  }
93 
94  return std::make_pair(Path(path.substr(0, root_length)), root_length);
95  }
96 
97  // Windows case 2: "\\?\"
98  if ((path.size() > 3) && ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') && (path[3] == '\\')))
99  {
100  root_found = true;
101  root_length = 4;
102  }
103 
104  // Windows case 3: "C:" or "C:\"
105  while (root_length < path.size())
106  {
107  if (path[root_length] == ':')
108  {
109  root_found = true;
110 
111  ++root_length;
112 
113  while (root_length < path.size())
114  {
115  if ((path[root_length] != '\\') && (path[root_length] != '/'))
116  break;
117  ++root_length;
118  }
119 
120  break;
121  }
122 
123  ++root_length;
124  }
125 
126  return (root_found && (root_length > 0)) ? std::make_pair(Path(path.substr(0, root_length)), root_length) : std::make_pair<Path, size_t>(Path(), 0);
127 }
128 
129 } // namespace Internals
131 
133 {
134  return Internals::root(_path).first;
135 }
136 
138 {
139  size_t root_length = Internals::root(_path).second;
140  size_t relative_length = _path.size() - root_length;
141  return Path(_path.substr(root_length, relative_length));
142 }
143 
145 {
146  bool parent_found = false;
147  size_t parent_length = _path.size();
148 
149  // Find parent path position
150  bool filepart = false;
151  while (parent_length > 0)
152  {
153  --parent_length;
154  if ((_path[parent_length] == '\\') || (_path[parent_length] == '/'))
155  {
156  parent_found = true;
157 
158  // Windows case 1: "\\net" or "//net"
159  if ((parent_length == 1) && ((_path[parent_length - 1] == '\\') || (_path[parent_length - 1] == '/')))
160  {
161  parent_found = false;
162  break;
163  }
164  // Windows case 2: "\\?\"
165  if ((parent_length > 0) && (_path[parent_length - 1] == '?'))
166  {
167  parent_found = filepart;
168  ++parent_length;
169  break;
170  }
171  // Windows case 3: "C:\"
172  if ((parent_length > 0) && (_path[parent_length - 1] == ':'))
173  {
174  parent_found = filepart;
175  ++parent_length;
176  break;
177  }
178 
179  // Skip multiple path separators
180  while (parent_length > 0)
181  {
182  --parent_length;
183  if ((_path[parent_length] != '\\') && (_path[parent_length] != '/'))
184  {
185  ++parent_length;
186  break;
187  }
188  }
189 
190  // Unix case 1: "/foo" -> "/", but "/" -> ""
191  if ((parent_length == 0) && (_path.size() > 1))
192  ++parent_length;
193 
194  break;
195  }
196  else if (_path[parent_length] == ':')
197  {
198  parent_found = false;
199  ++parent_length;
200  break;
201  }
202  else
203  filepart = true;
204  }
205 
206  return (parent_found && (parent_length > 0)) ? Path(_path.substr(0, parent_length)) : Path();
207 }
208 
210 {
211  bool filename_found = false;
212  size_t filename_begin = _path.size();
213  size_t filename_end = _path.size();
214 
215  // Find filename position
216  while (filename_begin > 0)
217  {
218  --filename_begin;
219  if ((_path[filename_begin] == '\\') || (_path[filename_begin] == '/') || (_path[filename_begin] == ':'))
220  {
221  filename_found = ((_path[filename_begin] == '\\') || (_path[filename_begin] == '/'));
222  ++filename_begin;
223  break;
224  }
225  }
226 
227  size_t filename_length = (filename_end - filename_begin);
228 
229  return (filename_length > 0) ? Path(_path.substr(filename_begin, filename_length)) : (filename_found ? Path(".") : Path());
230 }
231 
233 {
234  bool ext_found = false;
235  size_t ext_begin = _path.size();
236  size_t ext_end = _path.size();
237 
238  // Find extension position
239  while (ext_begin > 0)
240  {
241  --ext_begin;
242  if (_path[ext_begin] == '.')
243  {
244  ext_found = true;
245  if ((ext_begin > 0) && (_path[ext_begin - 1] == '.'))
246  ext_end = ext_begin;
247  break;
248  }
249  if ((_path[ext_begin] == '\\') || (_path[ext_begin] == '/') || (_path[ext_begin] == ':'))
250  {
251  ++ext_begin;
252  ext_end = ext_begin;
253  break;
254  }
255  }
256 
257  size_t ext_length = ext_end - ext_begin;
258 
259  bool stem_found = false;
260  size_t stem_begin = ext_begin;
261  size_t stem_end = (ext_found && (ext_length > 1)) ? ext_begin : _path.size();
262 
263  // Find stem position
264  while (stem_begin > 0)
265  {
266  --stem_begin;
267  if ((_path[stem_begin] == '\\') || (_path[stem_begin] == '/') || (_path[stem_begin] == ':'))
268  {
269  stem_found = ((_path[stem_begin] == '\\') || (_path[stem_begin] == '/'));
270  ++stem_begin;
271  break;
272  }
273  }
274 
275  size_t stem_length = (stem_end - stem_begin);
276 
277  return (stem_length > 0) ? Path(_path.substr(stem_begin, stem_length)) : (stem_found ? Path(".") : Path());
278 }
279 
281 {
282  bool ext_found = false;
283  size_t ext_begin = _path.size();
284  size_t ext_end = _path.size();
285 
286  // Find extension position
287  while (ext_begin > 0)
288  {
289  --ext_begin;
290  if (_path[ext_begin] == '.')
291  {
292  ext_found = true;
293  if ((ext_begin > 0) && (_path[ext_begin - 1] == '.'))
294  ext_end = ext_begin;
295  break;
296  }
297  if ((_path[ext_begin] == '\\') || (_path[ext_begin] == '/') || (_path[ext_begin] == ':'))
298  {
299  ++ext_begin;
300  ext_end = ext_begin;
301  break;
302  }
303  }
304 
305  size_t ext_length = ext_end - ext_begin;
306 
307  return (ext_found && (ext_length > 1)) ? Path(_path.substr(ext_begin, ext_length)) : Path();
308 }
309 
311 {
312 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
313  char buffer[PATH_MAX];
314 
315  char* result = realpath(string().c_str(), buffer);
316  if (result == nullptr)
317  throwex FileSystemException("Cannot get the real path of the current path!").Attach(*this);
318 
319  return Path(std::string(result));
320 #elif defined(_WIN32) || defined(_WIN64)
321  std::vector<wchar_t> buffer(MAX_PATH);
322 
323  DWORD size = GetFullPathNameW(wstring().c_str(), (DWORD)buffer.size(), buffer.data(), nullptr);
324  if (size > buffer.size())
325  {
326  buffer.resize(size);
327  size = GetFullPathNameW(wstring().c_str(), (DWORD)buffer.size(), buffer.data(), nullptr);
328  }
329 
330  if (size == 0)
331  throwex FileSystemException("Cannot get the full path name of the current path!").Attach(*this);
332 
333  std::wstring fullpath(buffer.data(), size);
334 
335  size = GetLongPathNameW(fullpath.c_str(), buffer.data(), (DWORD)buffer.size());
336  if (size > buffer.size())
337  {
338  buffer.resize(size);
339  size = GetLongPathNameW(fullpath.c_str(), buffer.data(), (DWORD)buffer.size());
340  }
341 
342  if (size == 0)
343  throwex FileSystemException("Cannot get the long path name of the current path!").Attach(*this);
344 
345  return Path(std::wstring(buffer.data(), size));
346 #endif
347 }
348 
350 {
351  // Check for empty path
352  if (empty())
353  return Path();
354 
355  // Append the root part of the path
356  Path result = root();
357 
358  // Get the root index
359  size_t index = result._path.size();
360  size_t length = result._path.size();
361 
362  // If the root part is empty fill it with a current path
363  if (result.empty())
364  result = current();
365 
366  // Append relative part of the path
367  bool filepart = false;
368  while (length < _path.size())
369  {
370  if ((_path[length] == '\\') || (_path[length] == '/'))
371  {
372  // Append path file/directory part
373  std::string temp(_path.data() + index, length - index);
374  if (!temp.empty())
375  result /= temp;
376  index = length + 1;
377  filepart = false;
378  }
379  else if (!filepart && (_path[length] == '.') && (((length + 1) == _path.size()) || ((_path[length + 1] == '\\') || (_path[length + 1] == '/'))))
380  {
381  // Skip the current directory part
382  index = length + 1;
383  filepart = false;
384  }
385  else if (!filepart && (_path[length] == '.') && (((length + 1) < _path.size()) && (_path[length + 1] == '.')) && (((length + 2) == _path.size()) || ((_path[length + 2] == '\\') || (_path[length + 2] == '/'))))
386  {
387  // Reset to the parent directory
388  index = length + 2;
389  filepart = false;
390 
391  ++length;
392 
393  result = result.parent();
394  // If the parent directory is empty then also return an empty path
395  if (result.empty())
396  return result;
397  }
398  else
399  filepart = true;
400 
401  ++length;
402  }
403 
404  // Append the last path file/directory part
405  std::string temp(_path.data() + index, length - index);
406  if (!temp.empty())
407  result /= temp;
408 
409  return result;
410 }
411 
412 Path Path::validate(char placeholder) const
413 {
414  Path result(*this);
415 
416  for (auto& ch : result._path)
417  if ((ch != '\\') && (ch != '/') && deprecated(ch))
418  ch = placeholder;
419 
420  return result;
421 }
422 
424 {
425 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
426  // Special check for symlink
427  struct stat lstatus;
428  int lresult = lstat(string().c_str(), &lstatus);
429  if ((lresult == 0) && S_ISLNK(lstatus.st_mode))
430  return FileType::SYMLINK;
431 
432  struct stat status;
433  int result = stat(string().c_str(), &status);
434  if (result != 0)
435  {
436  if ((errno == ENOENT) || (errno == ENOTDIR))
437  return FileType::NONE;
438  else
439  throwex FileSystemException("Cannot get the status of the path!").Attach(*this);
440  }
441 
442  if (S_ISLNK(status.st_mode))
443  return FileType::SYMLINK;
444  else if (S_ISDIR(status.st_mode))
445  return FileType::DIRECTORY;
446  else if (S_ISREG(status.st_mode))
447  return FileType::REGULAR;
448  else if (S_ISBLK(status.st_mode))
449  return FileType::BLOCK;
450  else if (S_ISCHR(status.st_mode))
451  return FileType::CHARACTER;
452  else if (S_ISFIFO(status.st_mode))
453  return FileType::FIFO;
454  else if (S_ISSOCK(status.st_mode))
455  return FileType::SOCKET;
456  else
457  return FileType::UNKNOWN;
458 #elif defined(_WIN32) || defined(_WIN64)
459  DWORD attributes = GetFileAttributesW(wstring().c_str());
460  if (attributes == INVALID_FILE_ATTRIBUTES)
461  return FileType::NONE;
462 
463  if (attributes & FILE_ATTRIBUTE_REPARSE_POINT)
464  return FileType::SYMLINK;
465  else if (attributes & FILE_ATTRIBUTE_DIRECTORY)
466  return FileType::DIRECTORY;
467  else
468  return FileType::REGULAR;
469 #endif
470 }
471 
473 {
474  Flags<FileAttributes> result;
475 #if defined(_WIN32) || defined(_WIN64)
476  DWORD attributes = GetFileAttributesW(wstring().c_str());
477  if (attributes == INVALID_FILE_ATTRIBUTES)
478  return FileAttributes::NONE;
479  if (attributes & FILE_ATTRIBUTE_NORMAL)
480  result |= FileAttributes::NORMAL;
481  if (attributes & FILE_ATTRIBUTE_ARCHIVE)
482  result |= FileAttributes::ARCHIVED;
483  if (attributes & FILE_ATTRIBUTE_HIDDEN)
484  result |= FileAttributes::HIDDEN;
485  if (attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
486  result |= FileAttributes::INDEXED;
487  if (attributes & FILE_ATTRIBUTE_OFFLINE)
488  result |= FileAttributes::OFFLINE;
489  if (attributes & FILE_ATTRIBUTE_READONLY)
490  result |= FileAttributes::READONLY;
491  if (attributes & FILE_ATTRIBUTE_SYSTEM)
492  result |= FileAttributes::SYSTEM;
493  if (attributes & FILE_ATTRIBUTE_TEMPORARY)
494  result |= FileAttributes::TEMPORARY;
495 #endif
496  return result;
497 }
498 
500 {
502 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
503  struct stat status;
504  int result = stat(string().c_str(), &status);
505  if (result != 0)
506  {
507  if ((errno == ENOENT) || (errno == ENOTDIR))
508  return FilePermissions::NONE;
509  else
510  throwex FileSystemException("Cannot get file permissions of the path!").Attach(*this);
511  }
512 
513  if (status.st_mode & S_IRUSR)
515  if (status.st_mode & S_IWUSR)
517  if (status.st_mode & S_IXUSR)
519  if (status.st_mode & S_IRGRP)
521  if (status.st_mode & S_IWGRP)
523  if (status.st_mode & S_IXGRP)
525  if (status.st_mode & S_IROTH)
527  if (status.st_mode & S_IWOTH)
529  if (status.st_mode & S_IXOTH)
531  if (status.st_mode & S_ISUID)
533  if (status.st_mode & S_ISGID)
535  if (status.st_mode & S_ISVTX)
537 #endif
538  return permissions;
539 }
540 
542 {
543 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
544  struct stat status;
545  int result = stat(string().c_str(), &status);
546  if (result != 0)
547  throwex FileSystemException("Cannot get the status of the path!").Attach(*this);
548 #if defined(__APPLE__)
549  return UtcTimestamp(Timestamp((status.st_mtimespec.tv_sec * 1000000000) + status.st_mtimespec.tv_nsec));
550 #else
551  return UtcTimestamp(Timestamp((status.st_mtim.tv_sec * 1000000000) + status.st_mtim.tv_nsec));
552 #endif
553 #elif defined(_WIN32) || defined(_WIN64)
554  HANDLE hFile = CreateFileW(wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
555  if (hFile == INVALID_HANDLE_VALUE)
556  throwex FileSystemException("Cannot open the path for getting create time!").Attach(*this);
557 
558  // Smart resource cleaner pattern
559  auto file = resource(hFile, [](HANDLE hObject) { CloseHandle(hObject); });
560 
561  FILETIME created;
562  if (!GetFileTime(file.get(), &created, nullptr, nullptr))
563  throwex FileSystemException("Cannot get file created time of the path!").Attach(*this);
564 
565  // Release the resource manually
566  if (!CloseHandle(hFile))
567  throwex FileSystemException("Cannot close a path!").Attach(*this);
568  file.release();
569 
570  ULARGE_INTEGER result;
571  result.LowPart = created.dwLowDateTime;
572  result.HighPart = created.dwHighDateTime;
573  return UtcTimestamp(Timestamp((result.QuadPart - 116444736000000000ull) * 100));
574 #endif
575 }
576 
578 {
579 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
580  struct stat status;
581  int result = stat(string().c_str(), &status);
582  if (result != 0)
583  throwex FileSystemException("Cannot get the status of the path!").Attach(*this);
584 
585 #if defined(__APPLE__)
586  return UtcTimestamp(Timestamp((status.st_mtimespec.tv_sec * 1000000000) + status.st_mtimespec.tv_nsec));
587 #else
588  return UtcTimestamp(Timestamp((status.st_mtim.tv_sec * 1000000000) + status.st_mtim.tv_nsec));
589 #endif
590 #elif defined(_WIN32) || defined(_WIN64)
591  HANDLE hFile = CreateFileW(wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
592  if (hFile == INVALID_HANDLE_VALUE)
593  throwex FileSystemException("Cannot open the path for getting modified time!").Attach(*this);
594 
595  // Smart resource cleaner pattern
596  auto file = resource(hFile, [](HANDLE hObject) { CloseHandle(hObject); });
597 
598  FILETIME write;
599  if (!GetFileTime(file.get(), nullptr, nullptr, &write))
600  throwex FileSystemException("Cannot get file modified time of the path!").Attach(*this);
601 
602  // Release the resource manually
603  if (!CloseHandle(hFile))
604  throwex FileSystemException("Cannot close a path!").Attach(*this);
605  file.release();
606 
607  ULARGE_INTEGER result;
608  result.LowPart = write.dwLowDateTime;
609  result.HighPart = write.dwHighDateTime;
610  return UtcTimestamp(Timestamp((result.QuadPart - 116444736000000000ull) * 100));
611 #endif
612 }
613 
614 size_t Path::hardlinks() const
615 {
616 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
617  struct stat status;
618  int result = stat(string().c_str(), &status);
619  if (result != 0)
620  throwex FileSystemException("Cannot get the status of the path!").Attach(*this);
621 
622  return (size_t)status.st_nlink;
623 #elif defined(_WIN32) || defined(_WIN64)
624  HANDLE hFile = CreateFileW(wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
625  if (hFile == INVALID_HANDLE_VALUE)
626  throwex FileSystemException("Cannot open the path for getting hard links count!").Attach(*this);
627 
628  // Smart resource cleaner pattern
629  auto file = resource(hFile, [](HANDLE hObject) { CloseHandle(hObject); });
630 
631  BY_HANDLE_FILE_INFORMATION bhfi;
632  if (!GetFileInformationByHandle(file.get(), &bhfi))
633  throwex FileSystemException("Cannot get file information of the path!").Attach(*this);
634 
635  // Release the resource manually
636  if (!CloseHandle(hFile))
637  throwex FileSystemException("Cannot close a path!").Attach(*this);
638  file.release();
639 
640  return (size_t)bhfi.nNumberOfLinks;
641 #endif
642 }
643 
645 {
646 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
647  struct statvfs stvfs;
648  int result = statvfs(string().c_str(), &stvfs);
649  if (result != 0)
650  throwex FileSystemException("Cannot get the filesystem statistics of the path!").Attach(*this);
651 
652  // Calculate filesystem space information
653  return SpaceInfo
654  {
655  stvfs.f_blocks * stvfs.f_frsize,
656  stvfs.f_bfree * stvfs.f_frsize,
657  stvfs.f_bavail * stvfs.f_frsize
658  };
659 #elif defined(_WIN32) || defined(_WIN64)
660  ULARGE_INTEGER uiFreeBytesAvailable;
661  ULARGE_INTEGER uiTotalNumberOfBytes;
662  ULARGE_INTEGER uiTotalNumberOfFreeBytes;
663  if (!GetDiskFreeSpaceExW(wstring().c_str(), &uiFreeBytesAvailable, &uiTotalNumberOfBytes, &uiTotalNumberOfFreeBytes))
664  throwex FileSystemException("Cannot get the filesystem statistics of the path!").Attach(*this);
665 
666  // Calculate filesystem space information
667  return SpaceInfo
668  {
669  (((uint64_t)uiTotalNumberOfBytes.HighPart) << 32) + uiTotalNumberOfBytes.LowPart,
670  (((uint64_t)uiTotalNumberOfFreeBytes.HighPart) << 32) + uiTotalNumberOfFreeBytes.LowPart,
671  (((uint64_t)uiFreeBytesAvailable.HighPart) << 32) + uiFreeBytesAvailable.LowPart
672  };
673 #endif
674 }
675 
676 bool Path::IsEquivalent(const Path& path) const
677 {
678 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
679  struct stat st1;
680  int result = stat(string().c_str(), &st1);
681  if (result != 0)
682  throwex FileSystemException("Cannot get the status of the path!").Attach(*this);
683 
684  struct stat st2;
685  result = stat(path.string().c_str(), &st2);
686  if (result != 0)
687  throwex FileSystemException("Cannot get the status of the path!").Attach(path);
688 
689  // Compare the file meta information to detect if two path point to the same node on a filesystem
690  return ((st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino) && (st1.st_size == st2.st_size) && (st1.st_mtime == st2.st_mtime));
691 #elif defined(_WIN32) || defined(_WIN64)
692  // Access to the file meta information
693  HANDLE hFile1 = CreateFileW(wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
694  if (hFile1 == INVALID_HANDLE_VALUE)
695  throwex FileSystemException("Cannot open the path for getting meta information!").Attach(*this);
696 
697  // Smart resource cleaner pattern
698  auto file1 = resource(hFile1, [](HANDLE hObject) { CloseHandle(hObject); });
699 
700  BY_HANDLE_FILE_INFORMATION info1;
701  if (!GetFileInformationByHandle(file1.get(), &info1))
702  throwex FileSystemException("Cannot get the file meta information!").Attach(*this);
703 
704  // Release the resource manually
705  if (!CloseHandle(hFile1))
706  throwex FileSystemException("Cannot close a path!").Attach(*this);
707  file1.release();
708 
709  // Access to the file meta information
710  HANDLE hFile2 = CreateFileW(path.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
711  if (hFile2 == INVALID_HANDLE_VALUE)
712  throwex FileSystemException("Cannot open the path for getting meta information!").Attach(path);
713 
714  // Smart resource cleaner pattern
715  auto file2 = resource(hFile2, [](HANDLE hObject) { CloseHandle(hObject); });
716 
717  BY_HANDLE_FILE_INFORMATION info2;
718  if (!GetFileInformationByHandle(file2.get(), &info2))
719  throwex FileSystemException("Cannot get the file meta information!").Attach(path);
720 
721  // Release the resource manually
722  if (!CloseHandle(hFile2))
723  throwex FileSystemException("Cannot close a path!").Attach(path);
724  file2.release();
725 
726  // Compare the file meta information to detect if two path point to the same node on a filesystem
727  return ((info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber) &&
728  (info1.nFileIndexHigh == info2.nFileIndexHigh) &&
729  (info1.nFileIndexLow == info2.nFileIndexLow) &&
730  (info1.nFileSizeHigh == info2.nFileSizeHigh) &&
731  (info1.nFileSizeLow == info2.nFileSizeLow) &&
732  (info1.ftLastWriteTime.dwLowDateTime == info2.ftLastWriteTime.dwLowDateTime) &&
733  (info1.ftLastWriteTime.dwHighDateTime == info2.ftLastWriteTime.dwHighDateTime));
734 #endif
735 }
736 
737 Path& Path::Append(const Path& path)
738 {
739  if (_path.empty())
740  _path = path._path;
741  else
742  {
743  char last = _path[_path.size() - 1];
744  if ((last == '\\') || (last == '/'))
745  _path += path._path;
746  else
747  {
748  _path += separator();
749  _path += path._path;
750  }
751  }
752 
753  return *this;
754 }
755 
757 {
758 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
759  std::replace(_path.begin(), _path.end(), '\\', '/');
760 #elif defined(_WIN32) || defined(_WIN64)
761  std::replace(_path.begin(), _path.end(), '/', '\\');
762 #endif
763  return *this;
764 }
765 
766 Path& Path::ReplaceFilename(const Path& filename)
767 {
768  if (_path.empty())
769  _path.append(filename._path);
770  else
771  {
772  size_t index = _path.size();
773 
774  // Find filename position
775  while (index > 0)
776  {
777  --index;
778  if ((_path[index] == '\\') || (_path[index] == '/') || (_path[index] == ':'))
779  {
780  if (!filename.empty())
781  ++index;
782  break;
783  }
784  }
785 
786  _path.resize(index);
787  _path.append(filename._path);
788  }
789  return *this;
790 }
791 
792 Path& Path::ReplaceExtension(const Path& extension)
793 {
794  bool dot_required = (!extension._path.empty() && (extension._path[0] != '.'));
795 
796  if (_path.empty())
797  {
798  if (dot_required)
799  _path.append(".");
800  _path.append(extension._path);
801  }
802  else
803  {
804  size_t dot = _path.size();
805  size_t index = _path.size();
806 
807  // Find extension position
808  while (index > 0)
809  {
810  --index;
811  if (_path[index] == '.')
812  {
813  if ((index > 0) && (_path[index - 1] == '.'))
814  dot = index - 1;
815  else
816  dot = index;
817  break;
818  }
819  if ((_path[index] == '\\') || (_path[index] == '/') || (_path[index] == ':'))
820  break;
821  }
822 
823  _path.resize(dot);
824  if (dot_required)
825  _path.append(".");
826  _path.append(extension._path);
827  }
828  return *this;
829 }
830 
832 {
833  size_t index = _path.size();
834 
835  while (index > 0)
836  {
837  --index;
838  if (((_path[index] != '\\') && (_path[index] != '/')) || ((index > 0) && (_path[index - 1] == ':')))
839  {
840  ++index;
841  break;
842  }
843  }
844 
845  _path.resize(index);
846  return *this;
847 }
848 
849 bool Path::deprecated(char character) noexcept
850 {
851  char deprecated[] = "\\/?%*:|\"<>";
852  return (std::find(deprecated, deprecated + countof(deprecated), character) != (deprecated + countof(deprecated)));
853 }
854 
855 bool Path::deprecated(wchar_t character) noexcept
856 {
857  wchar_t deprecated[] = L"\\/?%*:|\"<>";
858  return (std::find(deprecated, deprecated + countof(deprecated), character) != (deprecated + countof(deprecated)));
859 }
860 
861 std::string Path::deprecated()
862 {
863  return "\\/?%*:|\"<>";
864 }
865 
866 char Path::separator() noexcept
867 {
868 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__) || defined(__APPLE__)
869  return '/';
870 #elif defined(_WIN32) || defined(_WIN64)
871  return '\\';
872 #endif
873 }
874 
876 {
877  return Internals::initial;
878 }
879 
881 {
882 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
883  std::vector<char> buffer(PATH_MAX);
884  char* result;
885 
886  while (((result = getcwd(buffer.data(), buffer.size())) == nullptr) && (errno == ERANGE))
887  buffer.resize(buffer.size() * 2);
888 
889  if (result == nullptr)
890  throwex FileSystemException("Cannot get the current path of the current process!");
891 
892  return Path(std::string(buffer.data()));
893 #elif defined(_WIN32) || defined(_WIN64)
894  std::vector<wchar_t> buffer(MAX_PATH);
895 
896  DWORD size = GetCurrentDirectoryW((DWORD)buffer.size(), buffer.data());
897  if (size > buffer.size())
898  {
899  buffer.resize(size);
900  size = GetCurrentDirectoryW((DWORD)buffer.size(), buffer.data());
901  }
902 
903  if (size == 0)
904  throwex FileSystemException("Cannot get the current path of the current process!");
905 
906  return Path(std::wstring(buffer.data(), size));
907 #endif
908 }
909 
911 {
912 #if defined(__APPLE__)
913  char buffer[PROC_PIDPATHINFO_MAXSIZE];
914 
915  int size = proc_pidpath(getpid(), buffer, countof(buffer));
916  if (size <= 0)
917  throwex FileSystemException("Cannot get the executable path of the current process!");
918 
919  return Path(std::string(buffer, size));
920 #elif defined(unix) || defined(__unix) || defined(__unix__)
921  std::vector<char> buffer(PATH_MAX);
922  ssize_t size;
923 
924  while ((size = readlink("/proc/self/exe", buffer.data(), buffer.size())) == (ssize_t)buffer.size())
925  buffer.resize(buffer.size() * 2);
926 
927  if (size < 0)
928  throwex FileSystemException("Cannot get the executable path of the current process!");
929 
930  return Path(std::string(buffer.data(), size));
931 #elif defined(_WIN32) || defined(_WIN64)
932  std::vector<wchar_t> buffer(MAX_PATH);
933  DWORD size;
934 
935  while ((size = GetModuleFileNameW(nullptr, buffer.data(), (DWORD)buffer.size())) == buffer.size())
936  buffer.resize(buffer.size() * 2);
937 
938  if (size == 0)
939  throwex FileSystemException("Cannot get the executable path of the current process!");
940 
941  return Path(std::wstring(buffer.data(), size));
942 #endif
943 }
944 
946 {
947 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
948  std::vector<char> buffer(PATH_MAX);
949  uid_t uid = getuid();
950  int result;
951 
952  struct passwd pwd;
953  struct passwd* ppwd;
954 
955  while (((result = getpwuid_r(uid, &pwd, buffer.data(), buffer.size(), &ppwd)) != 0) && (result == ERANGE))
956  buffer.resize(buffer.size() * 2);
957 
958  if ((result != 0) || (ppwd == nullptr))
959  throwex FileSystemException("Cannot get the home path of the current process!");
960 
961  return Path(std::string(pwd.pw_dir));
962 #elif defined(_WIN32) || defined(_WIN64)
963  std::vector<wchar_t> buffer(MAX_PATH);
964 
965  HANDLE hToken;
966  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
967  throwex FileSystemException("Cannot open the current process token!");
968 
969  DWORD size = (DWORD)buffer.size();
970  if (!GetUserProfileDirectoryW(hToken, buffer.data(), &size))
971  {
972  buffer.resize(size);
973  if (!GetUserProfileDirectoryW(hToken, buffer.data(), &size))
974  {
975  CloseHandle(hToken);
976  throwex FileSystemException("Cannot get the home path of the current process!");
977  }
978  }
979 
980  if (!CloseHandle(hToken))
981  throwex FileSystemException("Cannot close the current process token!");
982 
983  return Path(std::wstring(buffer.data(), size));
984 #endif
985 }
986 
988 {
989 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
990  char* temp;
991  if (((temp = getenv("TMPDIR")) != nullptr) || ((temp = getenv("TMP")) != nullptr) || ((temp = getenv("TEMP")) != nullptr) || ((temp = getenv("TEMPDIR")) != nullptr))
992  return Path(std::string(temp));
993  else
994  return Path(std::string("/tmp"));
995 #elif defined(_WIN32) || defined(_WIN64)
996  std::vector<wchar_t> buffer(MAX_PATH);
997 
998  DWORD size = GetTempPathW((DWORD)buffer.size(), buffer.data());
999  if (size > buffer.size())
1000  {
1001  buffer.resize(size);
1002  size = GetTempPathW((DWORD)buffer.size(), buffer.data());
1003  }
1004 
1005  if (size == 0)
1006  throwex FileSystemException("Cannot get the temporary path of the current process!");
1007 
1008  return Path(std::wstring(buffer.data(), size));
1009 #endif
1010 }
1011 
1013 {
1014  return Path(UUID::Random().string());
1015 }
1016 
1017 Path Path::Copy(const Path& src, const Path& dst, bool overwrite)
1018 {
1019  // Check if the destination path exists
1020  bool exists = dst.IsExists();
1021  if (exists && !overwrite)
1022  return Path();
1023 
1024  if (src.IsSymlink())
1025  {
1026  if (exists)
1027  Path::Remove(dst);
1028  return Symlink::CopySymlink(src, dst);
1029  }
1030  else if (src.IsDirectory())
1031  {
1032  if (exists)
1033  Path::Remove(dst);
1034  return (Path)Directory::Create(dst, src.attributes(), src.permissions());
1035  }
1036  else
1037  {
1038 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1039  // Open the source file for reading
1040  int source = open(src.string().c_str(), O_RDONLY, 0);
1041  if (source < 0)
1042  throwex FileSystemException("Cannot open source file for copy!").Attach(src);
1043 
1044  // Get the source file status
1045  struct stat status;
1046  int result = fstat(source, &status);
1047  if (result != 0)
1048  {
1049  close(source);
1050  throwex FileSystemException("Cannot get the source file status for copy!").Attach(src);
1051  }
1052 
1053  // Open the destination file for writing
1054  int destination = open(dst.string().c_str(), O_CREAT | O_WRONLY | O_TRUNC, status.st_mode);
1055  if (destination < 0)
1056  {
1057  close(source);
1058  throwex FileSystemException("Cannot open destination file for copy!").Attach(dst);
1059  }
1060 
1061 #if defined(linux) || defined(__linux) || defined(__linux__)
1062  // Transfer data between source and destination file descriptors
1063  off_t offset = 0;
1064  size_t cur = 0;
1065  size_t tot = status.st_size;
1066  while (cur < tot)
1067  {
1068  ssize_t sent;
1069  if ((sent = sendfile(destination, source, &offset, tot - cur)) <= 0)
1070  {
1071  if ((errno == EINTR) || (errno == EAGAIN))
1072  {
1073  // Interrupted system call/try again
1074  // Just skip to the top of the loop and try again
1075  continue;
1076  }
1077 
1078  close(source);
1079  close(destination);
1080  throwex FileSystemException("Cannot send the source file to the destination file!").Attach(src, dst);
1081  }
1082  cur += sent;
1083  }
1084 #else
1085  char buffer[BUFSIZ];
1086  int size;
1087 
1088  do
1089  {
1090  size = read(source, buffer, countof(buffer));
1091  if (size < 0)
1092  {
1093  close(source);
1094  close(destination);
1095  throwex FileSystemException("Cannot read from the file!").Attach(src);
1096  }
1097  size = write(destination, buffer, size);
1098  if (size < 0)
1099  {
1100  close(source);
1101  close(destination);
1102  throwex FileSystemException("Cannot write into the file!").Attach(dst);
1103  }
1104  } while (size > 0);
1105 #endif
1106 
1107  // Close files
1108  close(source);
1109  close(destination);
1110 #elif defined(_WIN32) || defined(_WIN64)
1111  if (!CopyFileW(src.wstring().c_str(), dst.wstring().c_str(), FALSE))
1112  throwex FileSystemException("Cannot copy the file!").Attach(src, dst);
1113 #endif
1114  return dst;
1115  }
1116 }
1117 
1118 Path Path::CopyIf(const Path& src, const Path& dst, const std::string& pattern, bool overwrite)
1119 {
1120  std::regex matcher(pattern);
1121 
1122  // Check if the destination path exists
1123  bool exists = dst.IsExists();
1124  if (exists && !overwrite)
1125  return Path();
1126 
1127  // Copy symbolic link or regular file
1128  if (src.IsSymlink() || !src.IsDirectory())
1129  {
1130  if (pattern.empty() || std::regex_match(src.filename().string(), matcher))
1131  return Copy(src, dst, overwrite);
1132  else
1133  return Path();
1134  }
1135 
1136  // Create destination directory
1137  if (!dst.IsExists() || !dst.IsDirectory())
1138  Directory::Create(dst, src.attributes(), src.permissions());
1139 
1140  // Copy all directory entries
1141  Directory directory(src);
1142  for (auto it = directory.begin(); it != directory.end(); ++it)
1143  {
1144  if (pattern.empty() || std::regex_match(it->filename().string(), matcher))
1145  {
1146  // Copy symbolic link or regular file
1147  if (it->IsSymlink() || !it->IsDirectory())
1148  Copy(src / it->filename(), dst / it->filename(), overwrite);
1149  else
1150  CopyAll(src / it->filename(), dst / it->filename(), overwrite);
1151  }
1152  }
1153  return dst;
1154 }
1155 
1156 Path Path::CopyAll(const Path& src, const Path& dst, bool overwrite)
1157 {
1158  // Check if the destination path exists
1159  bool exists = dst.IsExists();
1160  if (exists && !overwrite)
1161  return Path();
1162 
1163  // Copy symbolic link or regular file
1164  if (src.IsSymlink() || !src.IsDirectory())
1165  return Copy(src, dst, overwrite);
1166 
1167  // Add directory to copy stack
1168  std::stack<std::tuple<Path, Path>> dirs;
1169  dirs.push(std::make_tuple(src, dst));
1170 
1171  // Do we have anything to copy in stack?
1172  while (!dirs.empty())
1173  {
1174  // Get the top directory
1175  std::tuple<Path, Path> current = dirs.top();
1176  Directory srcdir(std::get<0>(current));
1177  Directory dstdir(std::get<1>(current));
1178  dirs.pop();
1179 
1180  // Create destination directory
1181  Directory::Create(dstdir, srcdir.attributes(), srcdir.permissions());
1182 
1183  // Copy all directory entries
1184  for (auto it = srcdir.begin(); it != srcdir.end(); ++it)
1185  {
1186  // Copy symbolic link or regular file
1187  if (it->IsSymlink() || !it->IsDirectory())
1188  Copy(srcdir / it->filename(), dstdir / it->filename(), overwrite);
1189  else
1190  dirs.push(std::make_tuple(srcdir / it->filename(), dstdir / it->filename()));
1191  }
1192  }
1193  return dst;
1194 }
1195 
1196 Path Path::Rename(const Path& src, const Path& dst)
1197 {
1198 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1199  int result = rename(src.string().c_str(), dst.string().c_str());
1200  if (result != 0)
1201  throwex FileSystemException("Cannot rename the path!").Attach(src, dst);
1202 #elif defined(_WIN32) || defined(_WIN64)
1203  if (!MoveFileExW(src.wstring().c_str(), dst.wstring().c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
1204  throwex FileSystemException("Cannot move the path!").Attach(src, dst);
1205 #endif
1206  return dst;
1207 }
1208 
1209 Path Path::Remove(const Path& path)
1210 {
1211 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1212  if (path.IsDirectory())
1213  {
1214  int result = rmdir(path.string().c_str());
1215  if (result != 0)
1216  throwex FileSystemException("Cannot remove the path directory!").Attach(path);
1217  }
1218  else
1219  {
1220  int result = unlink(path.string().c_str());
1221  if (result != 0)
1222  throwex FileSystemException("Cannot unlink the path file!").Attach(path);
1223  }
1224 #elif defined(_WIN32) || defined(_WIN64)
1225  std::wstring wpath = path.wstring();
1226  DWORD attributes = GetFileAttributesW(wpath.c_str());
1227  if (attributes == INVALID_FILE_ATTRIBUTES)
1228  throwex FileSystemException("Cannot get file attributes of the removed path!").Attach(path);
1229 
1230  if (attributes & FILE_ATTRIBUTE_DIRECTORY)
1231  {
1232  if (!RemoveDirectoryW(wpath.c_str()))
1233  throwex FileSystemException("Cannot remove the path directory!").Attach(path);
1234  }
1235  else
1236  {
1237  if (attributes & FILE_ATTRIBUTE_READONLY)
1238  attributes &= ~FILE_ATTRIBUTE_READONLY;
1239  if (!SetFileAttributesW(wpath.c_str(), attributes))
1240  throwex FileSystemException("Cannot set file attributes of the deleted path!").Attach(path);
1241  if (!DeleteFileW(wpath.c_str()))
1242  throwex FileSystemException("Cannot delete the path file!").Attach(path);
1243  }
1244 #endif
1245  return path.parent();
1246 }
1247 
1248 Path Path::RemoveIf(const Path& path, const std::string& pattern)
1249 {
1250  std::regex matcher(pattern);
1251 
1252  bool is_directory = false;
1253 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1254  if (path.IsDirectory())
1255  is_directory = true;
1256 #elif defined(_WIN32) || defined(_WIN64)
1257  std::wstring wpath = path.wstring();
1258  DWORD attributes = GetFileAttributesW(wpath.c_str());
1259  if (attributes == INVALID_FILE_ATTRIBUTES)
1260  throwex FileSystemException("Cannot get file attributes of the removed path!").Attach(path);
1261 
1262  if (attributes & FILE_ATTRIBUTE_DIRECTORY)
1263  is_directory = true;
1264 #endif
1265  if (is_directory)
1266  {
1267  // Remove all directory entries
1268  Directory directory(path);
1269  for (auto it = directory.begin(); it != directory.end(); ++it)
1270  if (pattern.empty() || std::regex_match(it->filename().string(), matcher))
1271  Remove(*it);
1272  return path;
1273  }
1274 
1275  // Remove the path
1276  if (pattern.empty() || std::regex_match(path.filename().string(), matcher))
1277  return Remove(path);
1278  else
1279  return Path();
1280 }
1281 
1283 {
1284  bool is_directory = false;
1285 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1286  if (path.IsDirectory())
1287  is_directory = true;
1288 #elif defined(_WIN32) || defined(_WIN64)
1289  std::wstring wpath = path.wstring();
1290  DWORD attributes = GetFileAttributesW(wpath.c_str());
1291  if (attributes == INVALID_FILE_ATTRIBUTES)
1292  throwex FileSystemException("Cannot get file attributes of the removed path!").Attach(path);
1293 
1294  if (attributes & FILE_ATTRIBUTE_DIRECTORY)
1295  is_directory = true;
1296 #endif
1297  if (is_directory)
1298  {
1299  // Recursively remove all directory entries
1300  Directory directory(path);
1301  for (auto it = directory.rbegin(); it != directory.rend(); ++it)
1302  Remove(*it);
1303  }
1304 
1305  // Remove the path
1306  return Remove(path);
1307 }
1308 
1309 void Path::SetAttributes(const Path& path, const Flags<FileAttributes>& attributes)
1310 {
1311 #if defined(_WIN32) || defined(_WIN64)
1312  std::wstring wpath = path.wstring();
1313  DWORD result = GetFileAttributesW(wpath.c_str());
1314  if (result == INVALID_FILE_ATTRIBUTES)
1315  throwex FileSystemException("Cannot get file attributes of the path!").Attach(path);
1316 
1318  result |= FILE_ATTRIBUTE_NORMAL;
1319  else
1320  result &= ~FILE_ATTRIBUTE_NORMAL;
1322  result |= FILE_ATTRIBUTE_ARCHIVE;
1323  else
1324  result &= ~FILE_ATTRIBUTE_ARCHIVE;
1326  result |= FILE_ATTRIBUTE_HIDDEN;
1327  else
1328  result &= ~FILE_ATTRIBUTE_HIDDEN;
1330  result |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
1331  else
1332  result &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
1334  result |= FILE_ATTRIBUTE_OFFLINE;
1335  else
1336  result &= ~FILE_ATTRIBUTE_OFFLINE;
1338  result |= FILE_ATTRIBUTE_READONLY;
1339  else
1340  result &= ~FILE_ATTRIBUTE_READONLY;
1342  result |= FILE_ATTRIBUTE_SYSTEM;
1343  else
1344  result &= ~FILE_ATTRIBUTE_SYSTEM;
1346  result |= FILE_ATTRIBUTE_TEMPORARY;
1347  else
1348  result &= ~FILE_ATTRIBUTE_TEMPORARY;
1349 
1350  if (!SetFileAttributesW(wpath.c_str(), result))
1351  throwex FileSystemException("Cannot set file attributes of the path!").Attach(path);
1352 #endif
1353 }
1354 
1355 void Path::SetPermissions(const Path& path, const Flags<FilePermissions>& permissions)
1356 {
1357 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1358  mode_t mode = 0;
1360  mode |= S_IRUSR;
1362  mode |= S_IWUSR;
1364  mode |= S_IXUSR;
1366  mode |= S_IRGRP;
1368  mode |= S_IWGRP;
1370  mode |= S_IXGRP;
1372  mode |= S_IROTH;
1374  mode |= S_IWOTH;
1376  mode |= S_IXOTH;
1378  mode |= S_ISUID;
1380  mode |= S_ISGID;
1382  mode |= S_ISVTX;
1383 
1384  int result = chmod(path.string().c_str(), mode);
1385  if (result != 0)
1386  throwex FileSystemException("Cannot set file permissions of the path!").Attach(path);
1387 #endif
1388 }
1389 
1390 void Path::SetCreated(const Path& path, const UtcTimestamp& timestamp)
1391 {
1392 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1393  struct stat status;
1394  int result = stat(path.string().c_str(), &status);
1395  if (result != 0)
1396  throwex FileSystemException("Cannot get the status of the path!").Attach(path);
1397 
1398  struct timespec times[2];
1399 #if defined(__APPLE__)
1400  times[0] = status.st_atimespec;
1401 #else
1402  times[0] = status.st_atim;
1403 #endif
1404  times[1].tv_sec = timestamp.seconds();
1405  times[1].tv_nsec = timestamp.nanoseconds() % 1000000000;
1406 
1407  result = utimensat(AT_FDCWD, path.string().c_str(), times, 0);
1408  if (result != 0)
1409  throwex FileSystemException("Cannot set file created time of the path!").Attach(path);
1410 #elif defined(_WIN32) || defined(_WIN64)
1411  HANDLE hFile = CreateFileW(path.wstring().c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1412  if (hFile == INVALID_HANDLE_VALUE)
1413  throwex FileSystemException("Cannot open the path for writing created time!").Attach(path);
1414 
1415  // Smart resource cleaner pattern
1416  auto file = resource(hFile, [](HANDLE hObject) { CloseHandle(hObject); });
1417 
1418  ULARGE_INTEGER result;
1419  result.QuadPart = (timestamp.total() / 100) + 116444736000000000ull;
1420 
1421  FILETIME created;
1422  created.dwLowDateTime = result.LowPart;
1423  created.dwHighDateTime = result.HighPart;
1424  if (!SetFileTime(file.get(), &created, nullptr, nullptr))
1425  throwex FileSystemException("Cannot set file created time of the path!").Attach(path);
1426 
1427  // Release the resource manually
1428  if (!CloseHandle(hFile))
1429  throwex FileSystemException("Cannot close a path!").Attach(path);
1430  file.release();
1431 #endif
1432 }
1433 
1434 void Path::SetModified(const Path& path, const UtcTimestamp& timestamp)
1435 {
1436 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1437  struct stat status;
1438  int result = stat(path.string().c_str(), &status);
1439  if (result != 0)
1440  throwex FileSystemException("Cannot get the status of the path!").Attach(path);
1441 
1442  struct timespec times[2];
1443 #if defined(__APPLE__)
1444  times[0] = status.st_atimespec;
1445 #else
1446  times[0] = status.st_atim;
1447 #endif
1448  times[1].tv_sec = timestamp.seconds();
1449  times[1].tv_nsec = timestamp.nanoseconds() % 1000000000;
1450 
1451  result = utimensat(AT_FDCWD, path.string().c_str(), times, 0);
1452  if (result != 0)
1453  throwex FileSystemException("Cannot set file modified time of the path!").Attach(path);
1454 #elif defined(_WIN32) || defined(_WIN64)
1455  HANDLE hFile = CreateFileW(path.wstring().c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1456  if (hFile == INVALID_HANDLE_VALUE)
1457  throwex FileSystemException("Cannot open the path for writing modified time!").Attach(path);
1458 
1459  // Smart resource cleaner pattern
1460  auto file = resource(hFile, [](HANDLE hObject) { CloseHandle(hObject); });
1461 
1462  ULARGE_INTEGER result;
1463  result.QuadPart = (timestamp.total() / 100) + 116444736000000000ull;
1464 
1465  FILETIME write;
1466  write.dwLowDateTime = result.LowPart;
1467  write.dwHighDateTime = result.HighPart;
1468  if (!SetFileTime(file.get(), nullptr, nullptr, &write))
1469  throwex FileSystemException("Cannot set file modified time of the path!").Attach(path);
1470 
1471  // Release the resource manually
1472  if (!CloseHandle(hFile))
1473  throwex FileSystemException("Cannot close a path!").Attach(path);
1474  file.release();
1475 #endif
1476 }
1477 
1478 void Path::SetCurrent(const Path& path)
1479 {
1480 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1481  int result = chdir(path.string().c_str());
1482  if (result != 0)
1483  throwex FileSystemException("Cannot set the current path of the current process!").Attach(path);
1484 #elif defined(_WIN32) || defined(_WIN64)
1485  Path temp(path / "");
1486  if (!SetCurrentDirectoryW(temp.wstring().c_str()))
1487  throwex FileSystemException("Cannot set the current path of the current process!").Attach(temp);
1488 #endif
1489 }
1490 
1491 void Path::Touch(const Path& path)
1492 {
1493  if (path.IsExists())
1494  SetModified(path, UtcTimestamp());
1495  else
1496  File::WriteEmpty(path);
1497 }
1498 
1499 } // namespace CppCommon
Filesystem directory.
Definition: directory.h:25
DirectoryIterator begin() const
Get the directory begin iterator.
Definition: directory.cpp:117
DirectoryIterator end() const
Get the directory end iterator.
Definition: directory.cpp:122
DirectoryIterator rbegin() const
Get the directory recursive begin iterator.
Definition: directory.cpp:127
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
DirectoryIterator rend() const
Get the directory recursive end iterator.
Definition: directory.cpp:132
static void WriteEmpty(const Path &path)
Write an empty file.
Definition: file.cpp:821
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
Path root() const
Decompose root path from the current path.
Definition: path.cpp:132
Path absolute() const
Transform the current path to the real path on a filesystem.
Definition: path.cpp:310
Path & ReplaceExtension(const Path &extension)
Replace the current path extension with a given one.
Definition: path.cpp:792
bool IsSymlink() const
Is the path points to symbolic link?
Definition: path.h:219
static Path unique()
Get the unique filename in UUID format "00000000-0000-0000-0000-000000000000".
Definition: path.cpp:1012
UtcTimestamp created() const
Get the path created UTC timestamp.
Definition: path.cpp:541
static Path RemoveAll(const Path &path)
Recursively remove the given path (file, empty directory, symlink, etc) from the filesystem.
Definition: path.cpp:1282
std::wstring wstring() const
Get the path value as a wide string.
Definition: path.h:153
size_t hardlinks() const
Get the path count of hardlinks.
Definition: path.cpp:614
Path validate(char placeholder='_') const
Transform the current path and replace all deprecated characters with a given placeholder (default is...
Definition: path.cpp:412
static Path CopyIf(const Path &src, const Path &dst, const std::string &pattern="", bool overwrite=false)
Copy all matched files from the the given source path to destination path (files, directories,...
Definition: path.cpp:1118
Flags< FileAttributes > attributes() const
Get the path file attributes.
Definition: path.cpp:472
Path()
Initialize path with an empty value.
Definition: path.h:93
bool IsDirectory() const
Is the path points to directory?
Definition: path.h:217
Path & RemoveTrailingSeparators()
Remove all trailing separators form the current path.
Definition: path.cpp:831
static Path Copy(const Path &src, const Path &dst, bool overwrite=false)
Copy the given source path to destination path (file, empty directory, symlink, etc)
Definition: path.cpp:1017
Path relative() const
Decompose relative path from the current path.
Definition: path.cpp:137
static Path Remove(const Path &path)
Remove the given path (file, empty directory, symlink, etc) from the filesystem.
Definition: path.cpp:1209
static Path initial()
Get the initial path of the process.
Definition: path.cpp:875
bool IsExists() const
Is the path exists?
Definition: path.h:212
static Path home()
Get the home path of the process.
Definition: path.cpp:945
static void SetCreated(const Path &path, const UtcTimestamp &timestamp)
Set created UTC timestamp for the given path.
Definition: path.cpp:1390
Path & ReplaceFilename(const Path &filename)
Replace the current path filename with a given one.
Definition: path.cpp:766
Path stem() const
Decompose stem from the current path.
Definition: path.cpp:232
static void SetAttributes(const Path &path, const Flags< FileAttributes > &attributes)
Set file attributes for the given path.
Definition: path.cpp:1309
static void SetModified(const Path &path, const UtcTimestamp &timestamp)
Set modified UTC timestamp for the given path.
Definition: path.cpp:1434
SpaceInfo space() const
Get the path space information.
Definition: path.cpp:644
Path & MakePreferred()
Convert all path separators to system ones ('\' for Windows or '/' for Unix)
Definition: path.cpp:756
static char separator() noexcept
Get the system path separator character ('\' for Windows or '/' for Unix)
Definition: path.cpp:866
Path parent() const
Decompose parent path from the current path.
Definition: path.cpp:144
static void Touch(const Path &path)
Touch the given path and set its modified UTC timestamp to the current value.
Definition: path.cpp:1491
static Path CopyAll(const Path &src, const Path &dst, bool overwrite=false)
Recursively copy the given source path to destination path (files, directories, symlinks,...
Definition: path.cpp:1156
Path canonical() const
Transform the current path and replace all '.' and '..' properly.
Definition: path.cpp:349
Path extension() const
Decompose extension from the current path.
Definition: path.cpp:280
static void SetCurrent(const Path &path)
Set the given path of the process as a current one.
Definition: path.cpp:1478
bool empty() const noexcept
Is the path empty?
Definition: path.h:191
Path & Append(const Path &path)
Append the given path to the current one.
Definition: path.cpp:737
Path filename() const
Decompose filename from the current path.
Definition: path.cpp:209
Flags< FilePermissions > permissions() const
Get the path file permissions.
Definition: path.cpp:499
FileType type() const
Get the path file type.
Definition: path.cpp:423
const std::string & string() const noexcept
Get the path value as UTF-8 string.
Definition: path.h:151
static std::string deprecated()
Get filesystem deprecated characters ('\', '/', '?', '', '*', ':', '|', '"', '<', '>')
Definition: path.cpp:861
static Path temp()
Get the temporary path of the process.
Definition: path.cpp:987
static void SetPermissions(const Path &path, const Flags< FilePermissions > &permissions)
Set file permissions for the given path.
Definition: path.cpp:1355
static Path RemoveIf(const Path &path, const std::string &pattern="")
Recursively remove the given path matched to the given pattern (file, empty directory,...
Definition: path.cpp:1248
UtcTimestamp modified() const
Get the path modified UTC timestamp.
Definition: path.cpp:577
bool IsEquivalent(const Path &path) const
Is the current path is equivalent to the given one (points to the same node on a filesystem)?
Definition: path.cpp:676
static Path Rename(const Path &src, const Path &dst)
Rename the given source path to destination path (file, empty directory, symlink, etc)
Definition: path.cpp:1196
static Path current()
Get the current path of the process.
Definition: path.cpp:880
std::string _path
Path string.
Definition: path.h:376
static Path executable()
Get the executable path of the process.
Definition: path.cpp:910
uint64_t seconds() const noexcept
Get total seconds of the current timestamp.
Definition: timestamp.h:143
uint64_t total() const noexcept
Get total value of the current timestamp (total nanoseconds)
Definition: timestamp.h:156
uint64_t nanoseconds() const noexcept
Get total nanoseconds of the current timestamp.
Definition: timestamp.h:152
static UUID Random()
Generate random UUID4 (randomly or pseudo-randomly generated version)
Definition: uuid.cpp:96
UTC timestamp.
Definition: timestamp.h:248
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
FileType
File types.
Definition: path.h:23
@ SYMLINK
Symbolic link.
@ DIRECTORY
Directory.
@ CHARACTER
Character device.
@ BLOCK
Block device.
@ REGULAR
Regular file.
@ NONE
None (file not found)
@ FIFO
FIFO (named pipe)
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.
Filesystem path definition.
Resource smart cleaner pattern definition.
Filesystem space information.
Definition: path.h:72
Universally unique identifier (UUID) definition.