CppCommon 1.0.5.0
C++ Common Library
Loading...
Searching...
No Matches
path.cpp
Go to the documentation of this file.
1
9#include "filesystem/path.h"
10
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
42namespace CppCommon {
43
45namespace Internals {
46
47Path initial = Path::current();
48
49std::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
412Path 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{
475#if defined(_WIN32) || defined(_WIN64)
476 DWORD attributes = GetFileAttributesW(wstring().c_str());
477 if (attributes == INVALID_FILE_ATTRIBUTES)
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)
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))
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
614size_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
676bool 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
737Path& 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
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
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
849bool Path::deprecated(char character) noexcept
850{
851 char deprecated[] = "\\/?%*:|\"<>";
852 return (std::find(deprecated, deprecated + countof(deprecated), character) != (deprecated + countof(deprecated)));
853}
854
855bool 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
861std::string Path::deprecated()
862{
863 return "\\/?%*:|\"<>";
864}
865
866char 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
1017Path 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
1118Path 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
1156Path 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
1196Path 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
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
1248Path 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
1309void 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
1355void 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
1390void 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
1434void 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
1478void 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
1491void 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.
DirectoryIterator end() const
Get the directory end iterator.
DirectoryIterator rbegin() const
Get the directory recursive begin iterator.
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.
DirectoryIterator rend() const
Get the directory recursive end iterator.
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
Enum-based flags.
Definition flags.h:65
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
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
const std::string & string() const noexcept
Get the path value as UTF-8 string.
Definition path.h:151
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
Static array countof definition.
Filesystem directory definition.
#define throwex
Throw extended exception macro.
Definition exceptions.h:23
C++ Common project definitions.
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.