diff options
Diffstat (limited to 'zen')
-rw-r--r-- | zen/basic_math.h | 22 | ||||
-rw-r--r-- | zen/dir_watcher.cpp | 15 | ||||
-rw-r--r-- | zen/error_log.h | 2 | ||||
-rw-r--r-- | zen/file_access.cpp | 98 | ||||
-rw-r--r-- | zen/file_access.h | 34 | ||||
-rw-r--r-- | zen/file_id_def.h | 46 | ||||
-rw-r--r-- | zen/file_io.cpp | 29 | ||||
-rw-r--r-- | zen/file_io.h | 23 | ||||
-rw-r--r-- | zen/file_traverser.cpp | 8 | ||||
-rw-r--r-- | zen/format_unit.cpp | 8 | ||||
-rw-r--r-- | zen/http.cpp | 12 | ||||
-rw-r--r-- | zen/http.h | 6 | ||||
-rw-r--r-- | zen/json.h | 2 | ||||
-rw-r--r-- | zen/open_ssl.cpp | 132 | ||||
-rw-r--r-- | zen/perf.h | 23 | ||||
-rw-r--r-- | zen/process_exec.cpp | 8 | ||||
-rw-r--r-- | zen/resolve_path.cpp | 9 | ||||
-rw-r--r-- | zen/ring_buffer.h | 8 | ||||
-rw-r--r-- | zen/serialize.h | 14 | ||||
-rw-r--r-- | zen/stl_tools.h | 12 | ||||
-rw-r--r-- | zen/string_tools.h | 16 | ||||
-rw-r--r-- | zen/string_traits.h | 11 | ||||
-rw-r--r-- | zen/symlink_target.h | 4 | ||||
-rw-r--r-- | zen/sys_info.cpp | 48 | ||||
-rw-r--r-- | zen/sys_info.h | 1 | ||||
-rw-r--r-- | zen/thread.cpp | 2 | ||||
-rw-r--r-- | zen/thread.h | 37 | ||||
-rw-r--r-- | zen/time.h | 2 | ||||
-rw-r--r-- | zen/type_traits.h | 60 |
29 files changed, 313 insertions, 379 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h index a4feb83e..944a0f53 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -111,14 +111,14 @@ std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, Input } } } - return { lowest, largest }; + return {lowest, largest}; } template <class InputIterator> inline std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last) { - return minMaxElement(first, last, std::less<typename std::iterator_traits<InputIterator>::value_type>()); + return minMaxElement(first, last, std::less()); } */ @@ -152,10 +152,10 @@ template <class N, class D> inline auto intDivRound(N num, D den) { using namespace zen; - static_assert(IsInteger<N>::value && IsInteger<D>::value); - static_assert(IsSignedInt<N>::value == IsSignedInt<D>::value); //until further + static_assert(IsIntegerV<N>&& IsIntegerV<D>); + static_assert(IsSignedIntV<N> == IsSignedIntV<D>); //until further assert(den != 0); - if constexpr (IsSignedInt<N>::value) + if constexpr (IsSignedIntV<N>) { if ((num < 0) != (den < 0)) return (num - den / 2) / den; @@ -168,10 +168,10 @@ template <class N, class D> inline auto intDivCeil(N num, D den) { using namespace zen; - static_assert(IsInteger<N>::value && IsInteger<D>::value); - static_assert(IsSignedInt<N>::value == IsSignedInt<D>::value); //until further + static_assert(IsIntegerV<N>&& IsIntegerV<D>); + static_assert(IsSignedIntV<N> == IsSignedIntV<D>); //until further assert(den != 0); - if constexpr (IsSignedInt<N>::value) + if constexpr (IsSignedIntV<N>) { if ((num < 0) != (den < 0)) return num / den; @@ -187,10 +187,10 @@ template <class N, class D> inline auto intDivFloor(N num, D den) { using namespace zen; - static_assert(IsInteger<N>::value && IsInteger<D>::value); - static_assert(IsSignedInt<N>::value == IsSignedInt<D>::value); //until further + static_assert(IsIntegerV<N>&& IsIntegerV<D>); + static_assert(IsSignedIntV<N> == IsSignedIntV<D>); //until further assert(den != 0); - if constexpr (IsSignedInt<N>::value) + if constexpr (IsSignedIntV<N>) { if ((num < 0) != (den < 0)) { diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index dc416b34..191ffd64 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -9,7 +9,6 @@ #include <set> #include "thread.h" #include "scope_guard.h" -//#include "basic_math.h" #include <map> #include <sys/inotify.h> @@ -34,7 +33,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError pimpl_(std::make_unique<Impl>()) { //get all subdirectories - std::vector<Zstring> fullFolderList { baseDirPath_ }; + std::vector<Zstring> fullFolderList {baseDirPath_}; { std::function<void (const Zstring& path)> traverse; @@ -102,7 +101,7 @@ DirWatcher::~DirWatcher() std::vector<DirWatcher::Change> DirWatcher::fetchChanges(const std::function<void()>& requestUiUpdate, std::chrono::milliseconds cbInterval) //throw FileError { - std::vector<std::byte> buffer(512 * (sizeof(struct ::inotify_event) + NAME_MAX + 1)); + std::vector<std::byte> buffer(512 * (sizeof(inotify_event) + NAME_MAX + 1)); ssize_t bytesRead = 0; do @@ -125,7 +124,7 @@ std::vector<DirWatcher::Change> DirWatcher::fetchChanges(const std::function<voi ssize_t bytePos = 0; while (bytePos < bytesRead) { - struct ::inotify_event& evt = reinterpret_cast<struct ::inotify_event&>(buffer[bytePos]); + inotify_event& evt = reinterpret_cast<inotify_event&>(buffer[bytePos]); if (evt.len != 0) //exclude case: deletion of "self", already reported by parent directory watch { @@ -138,18 +137,18 @@ std::vector<DirWatcher::Change> DirWatcher::fetchChanges(const std::function<voi if ((evt.mask & IN_CREATE) || (evt.mask & IN_MOVED_TO)) - output.push_back({ ChangeType::create, itemPath }); + output.push_back({ChangeType::create, itemPath}); else if ((evt.mask & IN_MODIFY) || (evt.mask & IN_CLOSE_WRITE)) - output.push_back({ ChangeType::update, itemPath }); + output.push_back({ChangeType::update, itemPath}); else if ((evt.mask & IN_DELETE ) || (evt.mask & IN_DELETE_SELF) || (evt.mask & IN_MOVE_SELF ) || (evt.mask & IN_MOVED_FROM)) - output.push_back({ ChangeType::remove, itemPath }); + output.push_back({ChangeType::remove, itemPath}); } } - bytePos += sizeof(struct ::inotify_event) + evt.len; + bytePos += sizeof(inotify_event) + evt.len; } return output; diff --git a/zen/error_log.h b/zen/error_log.h index 6d9f80ae..a24dfe5a 100644 --- a/zen/error_log.h +++ b/zen/error_log.h @@ -68,7 +68,7 @@ private: inline void ErrorLog::logMsg(const std::wstring& msg, MessageType type) { - entries_.push_back({ std::time(nullptr), type, utfTo<Zstringc>(msg) }); + entries_.push_back({std::time(nullptr), type, utfTo<Zstringc>(msg)}); } diff --git a/zen/file_access.cpp b/zen/file_access.cpp index 4c9af652..fb770f19 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -12,7 +12,6 @@ #include "file_traverser.h" #include "scope_guard.h" #include "symlink_target.h" -#include "file_id_def.h" #include "file_io.h" #include "crc.h" #include "guid.h" @@ -45,7 +44,7 @@ std::optional<PathComponents> zen::parsePathComponents(const Zstring& itemPath) Zstring relPath(it + 1, itemPathFmt.end()); trim(relPath, true, true, [](Zchar c) { return c == FILE_NAME_SEPARATOR; }); - return PathComponents({ rootPath, relPath }); + return PathComponents({rootPath, relPath}); } return {}; }; @@ -100,7 +99,7 @@ std::optional<Zstring> zen::getParentFolderPath(const Zstring& itemPath) ItemType zen::getItemType(const Zstring& itemPath) //throw FileError { - struct ::stat itemInfo = {}; + struct stat itemInfo = {}; if (::lstat(itemPath.c_str(), &itemInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), "lstat"); @@ -153,7 +152,7 @@ std::optional<ItemType> zen::itemStillExists(const Zstring& itemPath) //throw Fi bool zen::fileAvailable(const Zstring& filePath) //noexcept { //symbolic links (broken or not) are also treated as existing files! - struct ::stat fileInfo = {}; + struct stat fileInfo = {}; if (::stat(filePath.c_str(), &fileInfo) == 0) //follow symlinks! return S_ISREG(fileInfo.st_mode); return false; @@ -163,7 +162,7 @@ bool zen::fileAvailable(const Zstring& filePath) //noexcept bool zen::dirAvailable(const Zstring& dirPath) //noexcept { //symbolic links (broken or not) are also treated as existing directories! - struct ::stat dirInfo = {}; + struct stat dirInfo = {}; if (::stat(dirPath.c_str(), &dirInfo) == 0) //follow symlinks! return S_ISDIR(dirInfo.st_mode); return false; @@ -177,29 +176,22 @@ namespace int64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns < 0 if not available { - struct ::statfs info = {}; - if (::statfs(path.c_str(), &info) != 0) + struct statfs info = {}; + if (::statfs(path.c_str(), &info) != 0) //follows symlinks! THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtPath(path)), "statfs"); + //Linux: "Fields that are undefined for a particular file system are set to 0." + //macOS: "Fields that are undefined for a particular file system are set to -1." - mkay :> + if (makeSigned(info.f_bsize) <= 0 || + makeSigned(info.f_bavail) <= 0) + return -1; return static_cast<int64_t>(info.f_bsize) * info.f_bavail; } -VolumeId zen::getVolumeId(const Zstring& itemPath) //throw FileError -{ - struct ::stat fileInfo = {}; - if (::stat(itemPath.c_str(), &fileInfo) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), "stat"); - - warn_static("NOT STABLE!") - - return fileInfo.st_dev; -} - - uint64_t zen::getFileSize(const Zstring& filePath) //throw FileError { - struct ::stat fileInfo = {}; + struct stat fileInfo = {}; if (::stat(filePath.c_str(), &fileInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), "stat"); @@ -207,6 +199,8 @@ uint64_t zen::getFileSize(const Zstring& filePath) //throw FileError } + + Zstring zen::getTempFolderPath() //throw FileError { if (const char* tempPath = ::getenv("TMPDIR")) //no extended error reporting @@ -336,15 +330,15 @@ void moveAndRenameFileSub(const Zstring& pathFrom, const Zstring& pathTo, bool r //macOS: no solution https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/rename.2.html if (!replaceExisting) { - struct ::stat infoSrc = {}; - if (::lstat(pathFrom.c_str(), &infoSrc) != 0) + struct stat sourceInfo = {}; + if (::lstat(pathFrom.c_str(), &sourceInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(pathFrom)), "stat"); - struct ::stat infoTrg = {}; - if (::lstat(pathTo.c_str(), &infoTrg) == 0) + struct stat targetInfo = {}; + if (::lstat(pathTo.c_str(), &targetInfo) == 0) { - if (infoSrc.st_dev != infoTrg.st_dev || - infoSrc.st_ino != infoTrg.st_ino) + if (sourceInfo.st_dev != targetInfo.st_dev || + sourceInfo.st_ino != targetInfo.st_ino) throwException(EEXIST); //that's what we're really here for //else: continue with a rename in case //caveat: if we have a hardlink referenced by two different paths, the source one will be unlinked => fine, but not exactly a "rename"... @@ -376,7 +370,7 @@ void zen::moveAndRenameItem(const Zstring& pathFrom, const Zstring& pathTo, bool namespace { -void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTime, ProcSymlink procSl) //throw FileError +void setWriteTimeNative(const Zstring& itemPath, const timespec& modTime, ProcSymlink procSl) //throw FileError { /* [2013-05-01] sigh, we can't use utimensat() on NTFS volumes on Ubuntu: silent failure!!! what morons are programming this shit??? @@ -388,12 +382,12 @@ void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTim => let's give utimensat another chance: using open()/futimens() for regular files and utimensat(AT_SYMLINK_NOFOLLOW) for symlinks is consistent with "cp" and "touch"! */ - struct ::timespec newTimes[2] = {}; + timespec newTimes[2] = {}; newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs: https://freefilesync.org/forum/viewtopic.php?t=1701 newTimes[1] = modTime; //modification time //test: even modTime == 0 is correctly applied (no NOOP!) test2: same behavior for "utime()" - if (procSl == ProcSymlink::FOLLOW) + if (procSl == ProcSymlink::follow) { //hell knows why files on gvfs-mounted Samba shares fail to open(O_WRONLY) returning EOPNOTSUPP: //https://freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works (but not for gvfs SFTP) @@ -422,10 +416,8 @@ void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTim void zen::setFileTime(const Zstring& filePath, time_t modTime, ProcSymlink procSl) //throw FileError { - struct ::timespec writeTime = {}; - writeTime.tv_sec = modTime; - setWriteTimeNative(filePath, writeTime, procSl); //throw FileError - + setWriteTimeNative(filePath, timetToNativeFileTime(modTime), + procSl); //throw FileError } @@ -442,7 +434,7 @@ namespace void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError { security_context_t contextSource = nullptr; - const int rv = procSl == ProcSymlink::FOLLOW ? + const int rv = procSl == ProcSymlink::follow ? ::getfilecon (source.c_str(), &contextSource) : ::lgetfilecon(source.c_str(), &contextSource); if (rv < 0) @@ -457,7 +449,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli { security_context_t contextTarget = nullptr; - const int rv2 = procSl == ProcSymlink::FOLLOW ? + const int rv2 = procSl == ProcSymlink::follow ? ::getfilecon(target.c_str(), &contextTarget) : ::lgetfilecon(target.c_str(), &contextTarget); if (rv2 < 0) @@ -475,7 +467,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli } } - const int rv3 = procSl == ProcSymlink::FOLLOW ? + const int rv3 = procSl == ProcSymlink::follow ? ::setfilecon(target.c_str(), contextSource) : ::lsetfilecon(target.c_str(), contextSource); if (rv3 < 0) @@ -493,8 +485,8 @@ void zen::copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPa copySecurityContext(sourcePath, targetPath, procSl); //throw FileError #endif - struct ::stat fileInfo = {}; - if (procSl == ProcSymlink::FOLLOW) + struct stat fileInfo = {}; + if (procSl == ProcSymlink::follow) { if (::stat(sourcePath.c_str(), &fileInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), "stat"); @@ -614,22 +606,22 @@ void zen::copySymlink(const Zstring& sourcePath, const Zstring& targetPath) //th catch (FileError&) {}); //file times: essential for syncing a symlink: enforce this! (don't just try!) - struct ::stat sourceInfo = {}; + struct stat sourceInfo = {}; if (::lstat(sourcePath.c_str(), &sourceInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourcePath)), "lstat"); - setWriteTimeNative(targetPath, sourceInfo.st_mtim, ProcSymlink::DIRECT); //throw FileError + setWriteTimeNative(targetPath, sourceInfo.st_mtim, ProcSymlink::direct); //throw FileError } FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, //throw FileError, ErrorTargetExisting, (ErrorFileLocked), X - const IOCallback& notifyUnbufferedIO /*throw X*/) + const IoCallback& notifyUnbufferedIO /*throw X*/) { int64_t totalUnbufferedIO = 0; FileInput fileIn(sourceFile, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //throw FileError, (ErrorFileLocked -> Windows-only) - struct ::stat sourceInfo = {}; + struct stat sourceInfo = {}; if (::fstat(fileIn.getHandle(), &sourceInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), "fstat"); @@ -637,7 +629,7 @@ FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& target //it seems we don't need S_IWUSR, not even for the setFileTime() below! (tested with source file having different user/group!) //=> need copyItemPermissions() only for "chown" and umask-agnostic permissions - const int fdTarget = ::open(targetFile.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode); + const int fdTarget = ::open(targetFile.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, mode); if (fdTarget == -1) { const int ec = errno; //copy before making other system calls! @@ -659,7 +651,7 @@ FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& target //flush intermediate buffers before fiddling with the raw file handle fileOut.flushBuffers(); //throw FileError, X - struct ::stat targetInfo = {}; + struct stat targetInfo = {}; if (::fstat(fileOut.getHandle(), &targetInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), "fstat"); @@ -673,12 +665,12 @@ FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& target std::optional<FileError> errorModTime; try { - //we cannot set the target file times (::futimes) while the file descriptor is still open after a write operation: - //this triggers bugs on samba shares where the modification time is set to current time instead. - //Linux: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236 - // http://comments.gmane.org/gmane.linux.file-systems.cifs/2854 - //OS X: https://freefilesync.org/forum/viewtopic.php?t=356 - setWriteTimeNative(targetFile, sourceInfo.st_mtim, ProcSymlink::FOLLOW); //throw FileError + /* we cannot set the target file times (::futimes) while the file descriptor is still open after a write operation: + this triggers bugs on Samba shares where the modification time is set to current time instead. + Linux: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236 + http://comments.gmane.org/gmane.linux.file-systems.cifs/2854 + macOS: https://freefilesync.org/forum/viewtopic.php?t=356 */ + setWriteTimeNative(targetFile, sourceInfo.st_mtim, ProcSymlink::follow); //throw FileError } catch (const FileError& e) { @@ -687,9 +679,9 @@ FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& target FileCopyResult result; result.fileSize = sourceInfo.st_size; - result.modTime = sourceInfo.st_mtim.tv_sec; // - result.sourceFileId = generateFileId(sourceInfo); - result.targetFileId = generateFileId(targetInfo); + result.sourceModTime = sourceInfo.st_mtim; + result.sourceFileIdx = sourceInfo.st_ino; + result.targetFileIdx = targetInfo.st_ino; result.errorModTime = errorModTime; return result; } diff --git a/zen/file_access.h b/zen/file_access.h index a3fa56d7..f3ea6c00 100644 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -10,9 +10,8 @@ #include <functional> #include "zstring.h" #include "file_error.h" -#include "file_id_def.h" -#include "serialize.h" - +#include "serialize.h" //IoCallback + #include <sys/stat.h> namespace zen { @@ -31,6 +30,21 @@ std::optional<Zstring> getParentFolderPath(const Zstring& itemPath); bool fileAvailable(const Zstring& filePath); //noexcept bool dirAvailable (const Zstring& dirPath ); // +//FAT/FAT32: "Why does the timestamp of a file *increase* by up to 2 seconds when I copy it to a USB thumb drive?" +const int FAT_FILE_TIME_PRECISION_SEC = 2; //https://devblogs.microsoft.com/oldnewthing/?p=83 +//https://web.archive.org/web/20141127143832/http://support.microsoft.com/kb/127830 + +using FileIndex = ino_t; +using FileTimeNative = timespec; + +inline time_t nativeFileTimeToTimeT(const timespec& ft) { return ft.tv_sec; } //follow Windows Explorer and always round down! +inline timespec timetToNativeFileTime(time_t utcTime) +{ + timespec natTime = {}; + natTime.tv_sec = utcTime; + return natTime; +} + enum class ItemType { file, @@ -47,14 +61,13 @@ std::optional<ItemType> itemStillExists(const Zstring& itemPath); //throw FileEr enum class ProcSymlink { - DIRECT, - FOLLOW + direct, + follow }; void setFileTime(const Zstring& filePath, time_t modTime, ProcSymlink procSl); //throw FileError //symlink handling: follow int64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns < 0 if not available -VolumeId getVolumeId(const Zstring& itemPath); //throw FileError uint64_t getFileSize(const Zstring& filePath); //throw FileError //get per-user directory designated for temporary files: @@ -87,16 +100,15 @@ void copySymlink(const Zstring& sourcePath, const Zstring& targetPath); //throw struct FileCopyResult { uint64_t fileSize = 0; - time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC - FileId sourceFileId; - FileId targetFileId; + FileTimeNative sourceModTime = {}; + FileIndex sourceFileIdx = 0; + FileIndex targetFileIdx = 0; std::optional<FileError> errorModTime; //failure to set modification time }; FileCopyResult copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked, X //accummulated delta != file size! consider ADS, sparse, compressed files - const IOCallback& notifyUnbufferedIO /*throw X*/); - + const IoCallback& notifyUnbufferedIO /*throw X*/); } #endif //FILE_ACCESS_H_8017341345614857 diff --git a/zen/file_id_def.h b/zen/file_id_def.h deleted file mode 100644 index d2d104d5..00000000 --- a/zen/file_id_def.h +++ /dev/null @@ -1,46 +0,0 @@ -// ***************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * -// ***************************************************************************** - -#ifndef FILE_ID_DEF_H_013287632486321493 -#define FILE_ID_DEF_H_013287632486321493 - - #include <sys/stat.h> - - -namespace zen -{ -namespace impl { typedef struct ::stat StatDummy; } //sigh... - -using VolumeId = decltype(impl::StatDummy::st_dev); -using FileIndex = decltype(impl::StatDummy::st_ino); - - -struct FileId //always available on Linux, and *generally* available on Windows) -{ - FileId() {} - FileId(VolumeId volId, FileIndex fIdx) : volumeId(volId), fileIndex(fIdx) - { - if (volId == 0 || fIdx == 0) - { - volumeId = 0; - fileIndex = 0; - } - } - VolumeId volumeId = 0; - FileIndex fileIndex = 0; - - bool operator==(const FileId&) const = default; -}; - - -inline -FileId generateFileId(const struct ::stat& fileInfo) -{ - return FileId(fileInfo.st_dev, fileInfo.st_ino); -} -} - -#endif //FILE_ID_DEF_H_013287632486321493 diff --git a/zen/file_io.cpp b/zen/file_io.cpp index e081335d..f575a366 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -41,7 +41,7 @@ namespace FileBase::FileHandle openHandleForRead(const Zstring& filePath) //throw FileError, ErrorFileLocked { //caveat: check for file types that block during open(): character device, block device, named pipe - struct ::stat fileInfo = {}; + struct stat fileInfo = {}; if (::stat(filePath.c_str(), &fileInfo) == 0) //follows symlinks { if (!S_ISREG(fileInfo.st_mode) && @@ -74,11 +74,11 @@ FileBase::FileHandle openHandleForRead(const Zstring& filePath) //throw FileErro } -FileInput::FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO) : +FileInput::FileInput(FileHandle handle, const Zstring& filePath, const IoCallback& notifyUnbufferedIO) : FileBase(handle, filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {} -FileInput::FileInput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO) : +FileInput::FileInput(const Zstring& filePath, const IoCallback& notifyUnbufferedIO) : FileBase(openHandleForRead(filePath), filePath), //throw FileError, ErrorFileLocked notifyUnbufferedIO_(notifyUnbufferedIO) { @@ -166,8 +166,13 @@ FileBase::FileHandle openHandleForWrite(const Zstring& filePath) //throw FileErr { //checkForUnsupportedType(filePath); -> not needed, open() + O_WRONLY should fail fast - const int fdFile = ::open(filePath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | /*access == FileOutput::ACC_OVERWRITE ? O_TRUNC : */ O_EXCL, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); //0666 => umask will be applied implicitly! + const mode_t lockFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; //0666 => umask will be applied implicitly! + + //O_EXCL contains a race condition on NFS file systems: https://linux.die.net/man/2/open + const int fdFile = ::open(filePath.c_str(), //const char* pathname + O_CREAT | //int flags + /*access == FileOutput::ACC_OVERWRITE ? O_TRUNC : */ O_EXCL | O_WRONLY | O_CLOEXEC, + lockFileMode); //mode_t mode if (fdFile == -1) { const int ec = errno; //copy before making other system calls! @@ -185,13 +190,13 @@ FileBase::FileHandle openHandleForWrite(const Zstring& filePath) //throw FileErr } -FileOutput::FileOutput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO) : +FileOutput::FileOutput(FileHandle handle, const Zstring& filePath, const IoCallback& notifyUnbufferedIO) : FileBase(handle, filePath), notifyUnbufferedIO_(notifyUnbufferedIO) { } -FileOutput::FileOutput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO) : +FileOutput::FileOutput(const Zstring& filePath, const IoCallback& notifyUnbufferedIO) : FileBase(openHandleForWrite(filePath), filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {} //throw FileError, ErrorTargetExisting @@ -298,8 +303,8 @@ void FileOutput::reserveSpace(uint64_t expectedSize) //throw FileError //don't use ::posix_fallocate which uses horribly inefficient fallback if FS doesn't support it (EOPNOTSUPP) and changes files size! //FALLOC_FL_KEEP_SIZE => allocate only, file size is NOT changed! - if (::fallocate(getHandle(), //int fd, - FALLOC_FL_KEEP_SIZE, //int mode, + if (::fallocate(getHandle(), //int fd + FALLOC_FL_KEEP_SIZE, //int mode 0, //off_t offset expectedSize) != 0) //off_t len if (errno != EOPNOTSUPP) //possible, unlike with posix_fallocate() @@ -308,19 +313,19 @@ void FileOutput::reserveSpace(uint64_t expectedSize) //throw FileError } -std::string zen::getFileContent(const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X +std::string zen::getFileContent(const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X { FileInput streamIn(filePath, notifyUnbufferedIO); //throw FileError, ErrorFileLocked return bufferedLoad<std::string>(streamIn); //throw FileError, X } -void zen::setFileContent(const Zstring& filePath, const std::string& byteStream, const IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X +void zen::setFileContent(const Zstring& filePath, const std::string& byteStream, const IoCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X { TempFileOutput fileOut(filePath, notifyUnbufferedIO); //throw FileError if (!byteStream.empty()) { - //preallocate disk space + reduce fragmentation + //preallocate disk space & reduce fragmentation fileOut.reserveSpace(byteStream.size()); //throw FileError fileOut.write(&byteStream[0], byteStream.size()); //throw FileError, X } diff --git a/zen/file_io.h b/zen/file_io.h index a7385241..3d1dfee7 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -32,8 +32,7 @@ public: FileHandle getHandle() { return hFile_; } //Windows: use 64kB ?? https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc938632%28v=technet.10%29 - //Linux: use st_blksize? - //macOS: use f_iosize? + //macOS, Linux: use st_blksize? static size_t getBlockSize() { return 128 * 1024; }; const Zstring& getFilePath() const { return filePath_; } @@ -57,15 +56,15 @@ private: class FileInput : public FileBase { public: - FileInput( const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, ErrorFileLocked - FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //takes ownership! + FileInput( const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, ErrorFileLocked + FileInput(FileHandle handle, const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/); //takes ownership! size_t read(void* buffer, size_t bytesToRead); //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream! private: size_t tryRead(void* buffer, size_t bytesToRead); //throw FileError, ErrorFileLocked; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0! - const IOCallback notifyUnbufferedIO_; //throw X + const IoCallback notifyUnbufferedIO_; //throw X std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize()); size_t bufPos_ = 0; @@ -76,8 +75,8 @@ private: class FileOutput : public FileBase { public: - FileOutput( const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, ErrorTargetExisting - FileOutput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //takes ownership! + FileOutput( const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, ErrorTargetExisting + FileOutput(FileHandle handle, const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/); //takes ownership! ~FileOutput(); void reserveSpace(uint64_t expectedSize); //throw FileError @@ -91,7 +90,7 @@ public: private: size_t tryWrite(const void* buffer, size_t bytesToWrite); //throw FileError; may return short! CONTRACT: bytesToWrite > 0 - IOCallback notifyUnbufferedIO_; //throw X + IoCallback notifyUnbufferedIO_; //throw X std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize()); size_t bufPos_ = 0; size_t bufPosEnd_ = 0; @@ -102,7 +101,7 @@ private: class TempFileOutput { public: - TempFileOutput( const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/) : //throw FileError + TempFileOutput( const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/) : //throw FileError filePath_(filePath), tmpFile_(tmpFilePath_, notifyUnbufferedIO) {} //throw FileError, (ErrorTargetExisting) @@ -110,7 +109,7 @@ public: void write(const void* buffer, size_t bytesToWrite) { tmpFile_.write(buffer, bytesToWrite); } //throw FileError, X - FileOutput& refTempFile() { return tmpFile_; } + FileOutput& refTempFile() { return tmpFile_; } void commit() //throw FileError, X { @@ -133,10 +132,10 @@ private: }; -[[nodiscard]] std::string getFileContent(const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, X +[[nodiscard]] std::string getFileContent(const Zstring& filePath, const IoCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, X //overwrites if existing + transactional! :) -void setFileContent(const Zstring& filePath, const std::string& bytes, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, X +void setFileContent(const Zstring& filePath, const std::string& bytes, const IoCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, X } #endif //FILE_IO_H_89578342758342572345 diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index aa48cb85..f1b5519b 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -31,7 +31,7 @@ void zen::traverseFolder(const Zstring& dirPath, for (;;) { errno = 0; - const struct ::dirent* dirEntry = ::readdir(folder); //don't use readdir_r(), see comment in native.cpp + const dirent* dirEntry = ::readdir(folder); //don't use readdir_r(), see comment in native.cpp if (!dirEntry) { if (errno == 0) //errno left unchanged => no more items @@ -54,7 +54,7 @@ void zen::traverseFolder(const Zstring& dirPath, const Zstring& itemPath = appendSeparator(dirPath) + itemName; - struct ::stat statData = {}; + struct stat statData = {}; try { if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks @@ -75,12 +75,12 @@ void zen::traverseFolder(const Zstring& dirPath, else if (S_ISDIR(statData.st_mode)) //a directory { if (onFolder) - onFolder({ itemName, itemPath }); + onFolder({itemName, itemPath}); } else //a file or named pipe, etc. { if (onFile) - onFile({ itemName, itemPath, makeUnsigned(statData.st_size), statData.st_mtime }); + onFile({itemName, itemPath, makeUnsigned(statData.st_size), statData.st_mtime}); } /* It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios: diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 28943de7..b2d1b59a 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -128,9 +128,9 @@ std::wstring roundToBlock(double timeInHigh, std::wstring zen::formatRemainingTime(double timeInSec) { - const int steps10[] = { 1, 2, 5, 10 }; - const int steps24[] = { 1, 2, 3, 4, 6, 8, 12, 24 }; - const int steps60[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; + const int steps10[] = {1, 2, 5, 10}; + const int steps24[] = {1, 2, 3, 4, 6, 8, 12, 24}; + const int steps60[] = {1, 2, 5, 10, 15, 20, 30, 60}; //determine preferred unit double timeInUnit = timeInSec; @@ -178,7 +178,7 @@ std::wstring zen::formatUtcToLocalTime(time_t utcTime) { auto errorMsg = [&] { return _("Error") + L" (time_t: " + numberTo<std::wstring>(utcTime) + L')'; }; - TimeComp loc = getLocalTime(utcTime); + const TimeComp& loc = getLocalTime(utcTime); std::wstring dateString = utfTo<std::wstring>(formatTime(Zstr("%x %X"), loc)); return !dateString.empty() ? dateString : errorMsg(); diff --git a/zen/http.cpp b/zen/http.cpp index 5d389719..f8fb24a3 100644 --- a/zen/http.cpp +++ b/zen/http.cpp @@ -23,7 +23,7 @@ public: bool disableGetCache /*not relevant for POST (= never cached)*/, const Zstring& userAgent, const Zstring* caCertFilePath /*optional: enable certificate validation*/, - const IOCallback& notifyUnbufferedIO) : //throw SysError, X + const IoCallback& notifyUnbufferedIO) : //throw SysError, X notifyUnbufferedIO_(notifyUnbufferedIO) { ZEN_ON_SCOPE_FAIL(cleanup(); /*destructor call would lead to member double clean-up!!!*/); @@ -214,7 +214,7 @@ private: int64_t contentRemaining_ = -1; //consider "Content-Length" if available - const IOCallback notifyUnbufferedIO_; //throw X + const IoCallback notifyUnbufferedIO_; //throw X std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize()); size_t bufPos_ = 0; //buffered I/O; see file_io.cpp @@ -240,7 +240,7 @@ std::unique_ptr<HttpInputStream::Impl> sendHttpRequestImpl(const Zstring& url, const std::string& contentType, //required for POST const Zstring& userAgent, const Zstring* caCertFilePath /*optional: enable certificate validation*/, - const IOCallback& notifyUnbufferedIO) //throw SysError, X + const IoCallback& notifyUnbufferedIO) //throw SysError, X { Zstring urlRed = url; //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop." @@ -339,14 +339,14 @@ std::vector<std::pair<std::string, std::string>> zen::xWwwFormUrlDecode(const st } -HttpInputStream zen::sendHttpGet(const Zstring& url, const Zstring& userAgent, const Zstring* caCertFilePath, const IOCallback& notifyUnbufferedIO) //throw SysError, X +HttpInputStream zen::sendHttpGet(const Zstring& url, const Zstring& userAgent, const Zstring* caCertFilePath, const IoCallback& notifyUnbufferedIO) //throw SysError, X { return sendHttpRequestImpl(url, nullptr /*postBuf*/, "" /*contentType*/, userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError, X, X } HttpInputStream zen::sendHttpPost(const Zstring& url, const std::vector<std::pair<std::string, std::string>>& postParams, - const Zstring& userAgent, const Zstring* caCertFilePath, const IOCallback& notifyUnbufferedIO) //throw SysError, X + const Zstring& userAgent, const Zstring* caCertFilePath, const IoCallback& notifyUnbufferedIO) //throw SysError, X { return sendHttpPost(url, xWwwFormUrlEncode(postParams), "application/x-www-form-urlencoded", userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError, X } @@ -354,7 +354,7 @@ HttpInputStream zen::sendHttpPost(const Zstring& url, const std::vector<std::pai HttpInputStream zen::sendHttpPost(const Zstring& url, const std::string& postBuf, const std::string& contentType, - const Zstring& userAgent, const Zstring* caCertFilePath, const IOCallback& notifyUnbufferedIO) //throw SysError, X + const Zstring& userAgent, const Zstring* caCertFilePath, const IoCallback& notifyUnbufferedIO) //throw SysError, X { return sendHttpRequestImpl(url, &postBuf, contentType, userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError, X } @@ -39,19 +39,19 @@ private: HttpInputStream sendHttpGet(const Zstring& url, const Zstring& userAgent, const Zstring* caCertFilePath /*optional: enable certificate validation*/, - const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X + const IoCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X HttpInputStream sendHttpPost(const Zstring& url, const std::vector<std::pair<std::string, std::string>>& postParams, const Zstring& userAgent, const Zstring* caCertFilePath /*optional: enable certificate validation*/, - const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X + const IoCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X HttpInputStream sendHttpPost(const Zstring& url, const std::string& postBuf, const std::string& contentType, const Zstring& userAgent, const Zstring* caCertFilePath /*optional: enable certificate validation*/, - const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X + const IoCallback& notifyUnbufferedIO /*throw X*/); //throw SysError, X bool internetIsAlive(); //noexcept std::wstring formatHttpError(int httpStatus); @@ -372,7 +372,7 @@ public: if (*it == '"') { Token tk(Token::Type::string); - tk.primVal = jsonUnescape({ pos_, it }); + tk.primVal = jsonUnescape({pos_, it}); pos_ = ++it; return tk; } diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp index ea77db43..7c94263a 100644 --- a/zen/open_ssl.cpp +++ b/zen/open_ssl.cpp @@ -79,7 +79,7 @@ std::wstring formatLastOpenSSLError(const char* functionName) std::shared_ptr<EVP_PKEY> generateRsaKeyPair(int bits) //throw SysError { - EVP_PKEY_CTX* keyCtx = ::EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, //int id, + EVP_PKEY_CTX* keyCtx = ::EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, //int id nullptr); //ENGINE* e if (!keyCtx) throw SysError(formatLastOpenSSLError("EVP_PKEY_CTX_new_id")); @@ -110,9 +110,9 @@ std::shared_ptr<EVP_PKEY> streamToEvpKey(const std::string& keyStream, BioToEvpF throw SysError(formatLastOpenSSLError("BIO_new_mem_buf")); ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio)); - if (EVP_PKEY* evp = bioToEvp(bio, //BIO* bp, - nullptr, //EVP_PKEY** x, - nullptr, //pem_password_cb* cb, + if (EVP_PKEY* evp = bioToEvp(bio, //BIO* bp + nullptr, //EVP_PKEY** x + nullptr, //pem_password_cb* cb nullptr)) //void* u return std::shared_ptr<EVP_PKEY>(evp, ::EVP_PKEY_free); throw SysError(formatLastOpenSSLError(functionName)); @@ -128,9 +128,9 @@ std::shared_ptr<EVP_PKEY> streamToEvpKey(const std::string& keyStream, BioToRsaF throw SysError(formatLastOpenSSLError("BIO_new_mem_buf")); ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio)); - RSA* rsa = bioToRsa(bio, //BIO* bp, - nullptr, //RSA** x, - nullptr, //pem_password_cb* cb, + RSA* rsa = bioToRsa(bio, //BIO* bp + nullptr, //RSA** x + nullptr, //pem_password_cb* cb nullptr); //void* u if (!rsa) throw SysError(formatLastOpenSSLError(functionName)); @@ -168,9 +168,9 @@ std::shared_ptr<EVP_PKEY> streamToKey(const std::string& keyStream, RsaStreamTyp } auto tmp = reinterpret_cast<const unsigned char*>(keyStream.c_str()); - EVP_PKEY* evp = (publicKey ? ::d2i_PublicKey : ::d2i_PrivateKey)(EVP_PKEY_RSA, //int type, - nullptr, //EVP_PKEY** a, - &tmp, /*changes tmp pointer itself!*/ //const unsigned char** pp, + EVP_PKEY* evp = (publicKey ? ::d2i_PublicKey : ::d2i_PrivateKey)(EVP_PKEY_RSA, //int type + nullptr, //EVP_PKEY** a + &tmp, /*changes tmp pointer itself!*/ //const unsigned char** pp static_cast<long>(keyStream.size())); //long length if (!evp) throw SysError(formatLastOpenSSLError(publicKey ? "d2i_PublicKey" : "d2i_PrivateKey")); @@ -238,23 +238,23 @@ std::string evpKeyToStream(EVP_PKEY* evp, RsaToBioFunc rsaToBio, const char* fun //fix OpenSSL API inconsistencies: int PEM_write_bio_PrivateKey2(BIO* bio, EVP_PKEY* key) { - return ::PEM_write_bio_PrivateKey(bio, //BIO* bp, - key, //EVP_PKEY* x, - nullptr, //const EVP_CIPHER* enc, - nullptr, //unsigned char* kstr, - 0, //int klen, - nullptr, //pem_password_cb* cb, + return ::PEM_write_bio_PrivateKey(bio, //BIO* bp + key, //EVP_PKEY* x + nullptr, //const EVP_CIPHER* enc + nullptr, //unsigned char* kstr + 0, //int klen + nullptr, //pem_password_cb* cb nullptr); //void* u } int PEM_write_bio_RSAPrivateKey2(BIO* bio, RSA* rsa) { - return ::PEM_write_bio_RSAPrivateKey(bio, //BIO* bp, - rsa, //RSA* x, - nullptr, //const EVP_CIPHER* enc, - nullptr, //unsigned char* kstr, - 0, //int klen, - nullptr, //pem_password_cb* cb, + return ::PEM_write_bio_RSAPrivateKey(bio, //BIO* bp + rsa, //RSA* x + nullptr, //const EVP_CIPHER* enc + nullptr, //unsigned char* kstr + 0, //int klen + nullptr, //pem_password_cb* cb nullptr); //void* u } @@ -286,7 +286,7 @@ std::string keyToStream(EVP_PKEY* evp, RsaStreamType streamType, bool publicKey) throw SysError(formatLastOpenSSLError(publicKey ? "i2d_PublicKey" : "i2d_PrivateKey")); ZEN_ON_SCOPE_EXIT(::OPENSSL_free(buf)); //memory is only allocated for bufSize > 0 - return { reinterpret_cast<const char*>(buf), static_cast<size_t>(bufSize) }; + return {reinterpret_cast<const char*>(buf), static_cast<size_t>(bufSize)}; } //================================================================================ @@ -299,29 +299,29 @@ std::string createSignature(const std::string& message, EVP_PKEY* privateKey) // throw SysError(formatSystemError("EVP_MD_CTX_create", L"", L"Unexpected failure.")); //no more error details ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_destroy(mdctx)); - if (::EVP_DigestSignInit(mdctx, //EVP_MD_CTX* ctx, - nullptr, //EVP_PKEY_CTX** pctx, - EVP_sha256(), //const EVP_MD* type, - nullptr, //ENGINE* e, + if (::EVP_DigestSignInit(mdctx, //EVP_MD_CTX* ctx + nullptr, //EVP_PKEY_CTX** pctx + EVP_sha256(), //const EVP_MD* type + nullptr, //ENGINE* e privateKey) != 1) //EVP_PKEY* pkey throw SysError(formatLastOpenSSLError("EVP_DigestSignInit")); - if (::EVP_DigestSignUpdate(mdctx, //EVP_MD_CTX* ctx, - message.c_str(), //const void* d, + if (::EVP_DigestSignUpdate(mdctx, //EVP_MD_CTX* ctx + message.c_str(), //const void* d message.size()) != 1) //size_t cnt throw SysError(formatLastOpenSSLError("EVP_DigestSignUpdate")); size_t sigLenMax = 0; //"first call to EVP_DigestSignFinal returns the maximum buffer size required" - if (::EVP_DigestSignFinal(mdctx, //EVP_MD_CTX* ctx, - nullptr, //unsigned char* sigret, + if (::EVP_DigestSignFinal(mdctx, //EVP_MD_CTX* ctx + nullptr, //unsigned char* sigret &sigLenMax) != 1) //size_t* siglen throw SysError(formatLastOpenSSLError("EVP_DigestSignFinal")); std::string signature(sigLenMax, '\0'); size_t sigLen = sigLenMax; - if (::EVP_DigestSignFinal(mdctx, //EVP_MD_CTX* ctx, - reinterpret_cast<unsigned char*>(&signature[0]), //unsigned char* sigret, + if (::EVP_DigestSignFinal(mdctx, //EVP_MD_CTX* ctx + reinterpret_cast<unsigned char*>(&signature[0]), //unsigned char* sigret &sigLen) != 1) //size_t* siglen throw SysError(formatLastOpenSSLError("EVP_DigestSignFinal")); @@ -338,20 +338,20 @@ void verifySignature(const std::string& message, const std::string& signature, E throw SysError(formatSystemError("EVP_MD_CTX_create", L"", L"Unexpected failure.")); //no more error details ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_destroy(mdctx)); - if (::EVP_DigestVerifyInit(mdctx, //EVP_MD_CTX* ctx, - nullptr, //EVP_PKEY_CTX** pctx, - EVP_sha256(), //const EVP_MD* type, - nullptr, //ENGINE* e, + if (::EVP_DigestVerifyInit(mdctx, //EVP_MD_CTX* ctx + nullptr, //EVP_PKEY_CTX** pctx + EVP_sha256(), //const EVP_MD* type + nullptr, //ENGINE* e publicKey) != 1) //EVP_PKEY* pkey throw SysError(formatLastOpenSSLError("EVP_DigestVerifyInit")); - if (::EVP_DigestVerifyUpdate(mdctx, //EVP_MD_CTX* ctx, - message.c_str(), //const void* d, + if (::EVP_DigestVerifyUpdate(mdctx, //EVP_MD_CTX* ctx + message.c_str(), //const void* d message.size()) != 1) //size_t cnt throw SysError(formatLastOpenSSLError("EVP_DigestVerifyUpdate")); - if (::EVP_DigestVerifyFinal(mdctx, //EVP_MD_CTX* ctx, - reinterpret_cast<const unsigned char*>(signature.c_str()), //const unsigned char* sig, + if (::EVP_DigestVerifyFinal(mdctx, //EVP_MD_CTX* ctx + reinterpret_cast<const unsigned char*>(signature.c_str()), //const unsigned char* sig signature.size()) != 1) //size_t siglen throw SysError(formatLastOpenSSLError("EVP_DigestVerifyFinal")); } @@ -735,10 +735,10 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: throw SysError(formatSystemError("EVP_CIPHER_CTX_new", L"", L"Unexpected failure.")); //no more error details ZEN_ON_SCOPE_EXIT(::EVP_CIPHER_CTX_free(cipCtx)); - if (::EVP_DecryptInit_ex(cipCtx, //EVP_CIPHER_CTX* ctx, - EVP_aes_256_cbc(), //const EVP_CIPHER* type, - nullptr, //ENGINE* impl, - key, //const unsigned char* key, => implied length of 256 bit! + if (::EVP_DecryptInit_ex(cipCtx, //EVP_CIPHER_CTX* ctx + EVP_aes_256_cbc(), //const EVP_CIPHER* type + nullptr, //ENGINE* impl + key, //const unsigned char* key => implied length of 256 bit! nullptr) != 1) //const unsigned char* iv throw SysError(formatLastOpenSSLError("EVP_DecryptInit_ex")); @@ -749,16 +749,16 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: //"EVP_DecryptUpdate() should have room for (inl + cipher_block_size) bytes" int decLen1 = 0; - if (::EVP_DecryptUpdate(cipCtx, //EVP_CIPHER_CTX* ctx, - reinterpret_cast<unsigned char*>(&privateBlob[0]), //unsigned char* out, - &decLen1, //int* outl, - reinterpret_cast<const unsigned char*>(privateBlobEnc.c_str()), //const unsigned char* in, + if (::EVP_DecryptUpdate(cipCtx, //EVP_CIPHER_CTX* ctx + reinterpret_cast<unsigned char*>(&privateBlob[0]), //unsigned char* out + &decLen1, //int* outl + reinterpret_cast<const unsigned char*>(privateBlobEnc.c_str()), //const unsigned char* in static_cast<int>(privateBlobEnc.size())) != 1) //int inl throw SysError(formatLastOpenSSLError("EVP_DecryptUpdate")); int decLen2 = 0; - if (::EVP_DecryptFinal_ex(cipCtx, //EVP_CIPHER_CTX* ctx, - reinterpret_cast<unsigned char*>(&privateBlob[decLen1]), //unsigned char* outm, + if (::EVP_DecryptFinal_ex(cipCtx, //EVP_CIPHER_CTX* ctx + reinterpret_cast<unsigned char*>(&privateBlob[decLen1]), //unsigned char* outm &decLen2) != 1) //int* outl throw SysError(formatLastOpenSSLError("EVP_DecryptFinal_ex")); @@ -777,7 +777,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: { static_assert(std::endian::native == std::endian::little&& sizeof(n) >= 4); const char* numStr = reinterpret_cast<const char*>(&n); - return { numStr[3], numStr[2], numStr[1], numStr[0] }; //big endian! + return {numStr[3], numStr[2], numStr[1], numStr[0]}; //big endian! }; const std::string macData = numToBeString(algorithm .size()) + algorithm + @@ -787,13 +787,13 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: numToBeString(privateBlob .size()) + privateBlob; char md[EVP_MAX_MD_SIZE] = {}; unsigned int mdLen = 0; - if (!::HMAC(EVP_sha1(), //const EVP_MD* evp_md, - macKey, //const void* key, - sizeof(macKey), //int key_len, - reinterpret_cast<const unsigned char*>(macData.c_str()), //const unsigned char* d, - static_cast<int>(macData.size()), //int n, - reinterpret_cast<unsigned char*>(md), //unsigned char* md, - &mdLen)) //unsigned int* md_len + if (!::HMAC(EVP_sha1(), //const EVP_MD* evp_md + macKey, //const void* key + sizeof(macKey), //int key_len + reinterpret_cast<const unsigned char*>(macData.c_str()), //const unsigned char* d + static_cast<int>(macData.size()), //int n + reinterpret_cast<unsigned char*>(md), //unsigned char* md + &mdLen)) //unsigned int* md_len throw SysError(formatSystemError("HMAC", L"", L"Unexpected failure.")); //no more error details const bool hashValid = mac == std::string_view(md, mdLen); @@ -979,10 +979,10 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: throw SysError(formatLastOpenSSLError("EC_POINT_new")); ZEN_ON_SCOPE_EXIT(::EC_POINT_free(ecPoint)); - if (::EC_POINT_oct2point(ecGroup, //const EC_GROUP* group, - ecPoint, //EC_POINT* p, - reinterpret_cast<const unsigned char*>(&pointStream[0]), //const unsigned char* buf, - pointStream.size(), //size_t len, + if (::EC_POINT_oct2point(ecGroup, //const EC_GROUP* group + ecPoint, //EC_POINT* p + reinterpret_cast<const unsigned char*>(&pointStream[0]), //const unsigned char* buf + pointStream.size(), //size_t len nullptr) != 1) //BN_CTX* ctx throw SysError(formatLastOpenSSLError("EC_POINT_oct2point")); @@ -1008,9 +1008,9 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: //const std::string pubStream = extractStringPub(); -> we don't need the public key const std::string priStream = extractStringPriv(); - EVP_PKEY* evpPriv = ::EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, //int type, - nullptr, //ENGINE* e, - reinterpret_cast<const unsigned char*>(&priStream[0]), //const unsigned char* priv, + EVP_PKEY* evpPriv = ::EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, //int type + nullptr, //ENGINE* e + reinterpret_cast<const unsigned char*>(&priStream[0]), //const unsigned char* priv priStream.size()); //size_t len if (!evpPriv) throw SysError(formatLastOpenSSLError("EVP_PKEY_new_raw_private_key")); @@ -23,21 +23,20 @@ static zen::PerfTimer perfTest(true); //startPaused perfTest.resume(); - ZEN_ON_SCOPE_EXIT(perfTest.pause()); -*/ + ZEN_ON_SCOPE_EXIT(perfTest.pause()); */ namespace zen { -//issue with wxStopWatch? https://freefilesync.org/forum/viewtopic.php?t=1426 -// => wxStopWatch implementation uses QueryPerformanceCounter: https://github.com/wxWidgets/wxWidgets/blob/17d72a48ffd4d8ff42eed070ac48ee2de50ceabd/src/common/stopwatch.cpp -// => whatever the problem was, it's almost certainly not caused by QueryPerformanceCounter(): -// MSDN: "How often does QPC roll over? Not less than 100 years from the most recent system boot" -// https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps#general-faq-about-qpc-and-tsc -// -// => using the system clock is problematic: https://freefilesync.org/forum/viewtopic.php?t=5280 -// -// std::chrono::system_clock wraps ::GetSystemTimePreciseAsFileTime() -// std::chrono::steady_clock wraps ::QueryPerformanceCounter() +/* issue with wxStopWatch? https://freefilesync.org/forum/viewtopic.php?t=1426 + - wxStopWatch implementation uses QueryPerformanceCounter: https://github.com/wxWidgets/wxWidgets/blob/17d72a48ffd4d8ff42eed070ac48ee2de50ceabd/src/common/stopwatch.cpp + - whatever the problem was, it's almost certainly not caused by QueryPerformanceCounter(): + MSDN: "How often does QPC roll over? Not less than 100 years from the most recent system boot" + https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps#general-faq-about-qpc-and-tsc + + - using the system clock is problematic: https://freefilesync.org/forum/viewtopic.php?t=5280 + + std::chrono::system_clock wraps ::GetSystemTimePreciseAsFileTime() + std::chrono::steady_clock wraps ::QueryPerformanceCounter() */ class StopWatch { public: diff --git a/zen/process_exec.cpp b/zen/process_exec.cpp index bbc87c51..b82c2565 100644 --- a/zen/process_exec.cpp +++ b/zen/process_exec.cpp @@ -117,7 +117,7 @@ std::pair<int /*exit code*/, std::string> processExecuteImpl(const Zstring& file if (::dup(fdLifeSignW) == -1) //O_CLOEXEC does NOT propagate with dup() THROW_LAST_SYS_ERROR("dup(fdLifeSignW)"); - std::vector<const char*> argv{ filePath.c_str() }; + std::vector<const char*> argv{filePath.c_str()}; for (const Zstring& arg : arguments) argv.push_back(arg.c_str()); argv.push_back(nullptr); @@ -147,6 +147,8 @@ std::pair<int /*exit code*/, std::string> processExecuteImpl(const Zstring& file if (flags == -1) THROW_LAST_SYS_ERROR("fcntl(F_GETFL)"); + //fcntl() success: Linux: 0 + // macOS: "Value other than -1." if (::fcntl(fdLifeSignR, F_SETFL, flags | O_NONBLOCK) == -1) THROW_LAST_SYS_ERROR("fcntl(F_SETFL, O_NONBLOCK)"); @@ -174,7 +176,7 @@ std::pair<int /*exit code*/, std::string> processExecuteImpl(const Zstring& file const auto waitTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - now).count(); - struct ::timeval tv = {}; + timeval tv = {}; tv.tv_sec = static_cast<long>(waitTimeMs / 1000); tv.tv_usec = static_cast<long>(waitTimeMs - tv.tv_sec * 1000) * 1000; @@ -219,7 +221,7 @@ std::pair<int /*exit code*/, std::string> processExecuteImpl(const Zstring& file exitCode == 127) //details should have been streamed to STDERR: used by /bin/sh, e.g. failure to execute due to missing .so file throw SysError(utfTo<std::wstring>(trimCpy(output))); - return { exitCode, output }; + return {exitCode, output}; } } diff --git a/zen/resolve_path.cpp b/zen/resolve_path.cpp index 76999500..0e714528 100644 --- a/zen/resolve_path.cpp +++ b/zen/resolve_path.cpp @@ -5,17 +5,12 @@ // ***************************************************************************** #include "resolve_path.h" -//#include <set> //not necessarily included by <map>! -//#include <map> #include "time.h" #include "thread.h" -//#include "utf.h" -//#include "scope_guard.h" -//#include "globals.h" #include "file_access.h" #include <stdlib.h> //getenv() - #include <unistd.h> //getcwd + #include <unistd.h> //getcwd() using namespace zen; @@ -251,7 +246,7 @@ std::vector<Zstring> zen::getFolderPathAliases(const Zstring& folderPathPhrase) tmp.erase(dirPath); tmp.erase(Zstring()); - return { tmp.begin(), tmp.end() }; + return {tmp.begin(), tmp.end()}; } diff --git a/zen/ring_buffer.h b/zen/ring_buffer.h index 240262fa..ae2377d8 100644 --- a/zen/ring_buffer.h +++ b/zen/ring_buffer.h @@ -196,11 +196,11 @@ public: using iterator = Iterator< RingBuffer, T>; using const_iterator = Iterator<const RingBuffer, const T>; - iterator begin() { return { *this, 0 }; } - iterator end () { return { *this, size_ }; } + iterator begin() { return {*this, 0 }; } + iterator end () { return {*this, size_}; } - const_iterator begin() const { return { *this, 0 }; } - const_iterator end () const { return { *this, size_ }; } + const_iterator begin() const { return {*this, 0 }; } + const_iterator end () const { return {*this, size_}; } const_iterator cbegin() const { return begin(); } const_iterator cend () const { return end (); } diff --git a/zen/serialize.h b/zen/serialize.h index 6c57e4ee..f9677630 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -35,7 +35,7 @@ struct BufferedInputStream Optional: support stream-copying -------------------------------- size_t getBlockSize() const; - const IOCallback& notifyUnbufferedIO + const IoCallback& notifyUnbufferedIO }; -------------------------------- @@ -47,9 +47,9 @@ struct BufferedOutputStream Optional: support stream-copying -------------------------------- - const IOCallback& notifyUnbufferedIO + const IoCallback& notifyUnbufferedIO }; */ -using IOCallback = std::function<void(int64_t bytesDelta)>; //throw X +using IoCallback = std::function<void(int64_t bytesDelta)>; //throw X //functions based on buffered stream abstraction @@ -75,7 +75,7 @@ template < class BufferedInputStream> void readArray (BufferedInputSt struct IOCallbackDivider { - IOCallbackDivider(const IOCallback& notifyUnbufferedIO, int64_t& totalUnbufferedIO) : totalUnbufferedIO_(totalUnbufferedIO), notifyUnbufferedIO_(notifyUnbufferedIO) {} + IOCallbackDivider(const IoCallback& notifyUnbufferedIO, int64_t& totalUnbufferedIO) : totalUnbufferedIO_(totalUnbufferedIO), notifyUnbufferedIO_(notifyUnbufferedIO) {} void operator()(int64_t bytesDelta) { @@ -85,7 +85,7 @@ struct IOCallbackDivider private: int64_t& totalUnbufferedIO_; - const IOCallback& notifyUnbufferedIO_; + const IoCallback& notifyUnbufferedIO_; }; @@ -206,7 +206,7 @@ void writeArray(BufferedOutputStream& stream, const void* buffer, size_t len) template <class N, class BufferedOutputStream> inline void writeNumber(BufferedOutputStream& stream, const N& num) { - static_assert(IsArithmetic<N>::value || std::is_same_v<N, bool> || std::is_enum_v<N>); + static_assert(IsArithmeticV<N> || std::is_same_v<N, bool> || std::is_enum_v<N>); writeArray(stream, &num, sizeof(N)); } @@ -234,7 +234,7 @@ void readArray(BufferedInputStream& stream, void* buffer, size_t len) //throw Sy template <class N, class BufferedInputStream> inline N readNumber(BufferedInputStream& stream) //throw SysErrorUnexpectedEos { - static_assert(IsArithmetic<N>::value || std::is_same_v<N, bool> || std::is_enum_v<N>); + static_assert(IsArithmeticV<N> || std::is_same_v<N, bool> || std::is_enum_v<N>); N num{}; readArray(stream, &num, sizeof(N)); //throw SysErrorUnexpectedEos return num; diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 495ff8d1..53b95241 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -128,7 +128,7 @@ void removeDuplicates(std::vector<T, Alloc>& v, CompLess less) template <class T, class Alloc> inline void removeDuplicates(std::vector<T, Alloc>& v) { - removeDuplicates(v, std::less<T>(), std::equal_to<T>()); + removeDuplicates(v, std::less(), std::equal_to()); } @@ -233,6 +233,7 @@ class FNV1aHash { public: FNV1aHash() {} + explicit FNV1aHash(Num startVal) : hashVal_(startVal) {} void add(Num n) { @@ -243,8 +244,8 @@ public: Num get() const { return hashVal_; } private: - static_assert(IsUnsignedInt<Num>::value); - static_assert(sizeof(Num) == 4 || sizeof(Num) == 8); //macOS: size_t is "unsigned long" + static_assert(IsUnsignedIntV<Num>); + static_assert(sizeof(Num) == 4 || sizeof(Num) == 8); static constexpr Num base_ = sizeof(Num) == 4 ? 2166136261U : 14695981039346656037ULL; static constexpr Num prime_ = sizeof(Num) == 4 ? 16777619U : 1099511628211ULL; @@ -257,7 +258,7 @@ Num hashArray(ByteIterator first, ByteIterator last) { using ValType = typename std::iterator_traits<ByteIterator>::value_type; static_assert(sizeof(ValType) <= sizeof(Num)); - static_assert(IsInteger<ValType>::value || std::is_same_v<ValType, char> || std::is_same_v<ValType, wchar_t>); + static_assert(IsIntegerV<ValType> || std::is_same_v<ValType, char> || std::is_same_v<ValType, wchar_t>); FNV1aHash<Num> hash; std::for_each(first, last, [&hash](ValType v) { hash.add(v); }); @@ -265,8 +266,7 @@ Num hashArray(ByteIterator first, ByteIterator last) } -//support for custom string classes in std::unordered_set/map -struct StringHash +struct StringHash //support for custom string classes with std::unordered_set/map { template <class String> size_t operator()(const String& str) const diff --git a/zen/string_tools.h b/zen/string_tools.h index 883c45b8..8150df05 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -395,7 +395,7 @@ std::vector<S> split(const S& str, const T& delimiter, SplitOnEmpty soe) { if (str.empty() && soe == SplitOnEmpty::skip) return {}; - return { str }; + return {str}; } const auto* const delimFirst = strBegin(delimiter); @@ -800,9 +800,9 @@ template <class S, class Num> inline S numberTo(const Num& number) { using TypeTag = std::integral_constant<impl::NumberType, - IsSignedInt <Num>::value ? impl::NumberType::signedInt : - IsUnsignedInt<Num>::value ? impl::NumberType::unsignedInt : - IsFloat <Num>::value ? impl::NumberType::floatingPoint : + IsSignedIntV <Num> ? impl::NumberType::signedInt : + IsUnsignedIntV<Num> ? impl::NumberType::unsignedInt : + IsFloatV <Num> ? impl::NumberType::floatingPoint : impl::NumberType::other>; return impl::numberTo<S>(number, TypeTag()); @@ -813,9 +813,9 @@ template <class Num, class S> inline Num stringTo(const S& str) { using TypeTag = std::integral_constant<impl::NumberType, - IsSignedInt <Num>::value ? impl::NumberType::signedInt : - IsUnsignedInt<Num>::value ? impl::NumberType::unsignedInt : - IsFloat <Num>::value ? impl::NumberType::floatingPoint : + IsSignedIntV <Num> ? impl::NumberType::signedInt : + IsUnsignedIntV<Num> ? impl::NumberType::unsignedInt : + IsFloatV <Num> ? impl::NumberType::floatingPoint : impl::NumberType::other>; return impl::stringTo<Num>(str, TypeTag()); @@ -836,7 +836,7 @@ std::pair<char, char> hexify(unsigned char c, bool upperCase) else return static_cast<char>('a' + (num - 10)); }; - return { hexifyDigit(c / 16), hexifyDigit(c % 16) }; + return {hexifyDigit(c / 16), hexifyDigit(c % 16)}; } diff --git a/zen/string_traits.h b/zen/string_traits.h index d9ce589c..ca40f7d6 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -120,19 +120,12 @@ public: }; } -template <class T> -struct IsStringLike : std::bool_constant<impl::StringTraits<T>::isStringLike> {}; -template <class T> -struct GetCharType { using Type = typename impl::StringTraits<T>::CharType; }; - - -//template alias helpers: template<class T> -constexpr bool IsStringLikeV = IsStringLike<T>::value; +constexpr bool IsStringLikeV = impl::StringTraits<T>::isStringLike; template<class T> -using GetCharTypeT = typename GetCharType<T>::Type; +using GetCharTypeT = typename impl::StringTraits<T>::CharType; namespace impl diff --git a/zen/symlink_target.h b/zen/symlink_target.h index 42010fd2..32b1211d 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -70,11 +70,11 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError namespace zen { inline -SymlinkRawContent getSymlinkRawContent(const Zstring& linkPath) { return getSymlinkRawContent_impl(linkPath); } +SymlinkRawContent getSymlinkRawContent(const Zstring& linkPath) { return getSymlinkRawContent_impl(linkPath); } //throw FileError inline -Zstring getSymlinkResolvedPath(const Zstring& linkPath) { return getResolvedSymlinkPath_impl(linkPath); } +Zstring getSymlinkResolvedPath(const Zstring& linkPath) { return getResolvedSymlinkPath_impl(linkPath); } //throw FileError } diff --git a/zen/sys_info.cpp b/zen/sys_info.cpp index f6045f7e..1a0d18f5 100644 --- a/zen/sys_info.cpp +++ b/zen/sys_info.cpp @@ -25,17 +25,14 @@ using namespace zen; std::wstring zen::getUserName() //throw FileError { - const uid_t userIdNo = ::getuid(); //"real user ID"; never fails - - std::vector<char> buffer(std::max<long>(10000, ::sysconf(_SC_GETPW_R_SIZE_MAX))); //::sysconf may return long(-1) - struct passwd buffer2 = {}; - struct passwd* pwsEntry = nullptr; - if (::getpwuid_r(userIdNo, &buffer2, &buffer[0], buffer.size(), &pwsEntry) != 0) //getlogin() is deprecated and not working on Ubuntu at all!!! - THROW_LAST_FILE_ERROR(_("Cannot get process information."), "getpwuid_r"); - if (!pwsEntry) - throw FileError(_("Cannot get process information."), L"no login found"); //should not happen? - - return utfTo<std::wstring>(pwsEntry->pw_name); + //https://linux.die.net/man/3/getlogin + //https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getlogin.2.html + const char* loginUser = ::getlogin(); + if (!loginUser) + THROW_LAST_FILE_ERROR(_("Cannot get process information."), "getlogin"); + //getlogin() is smarter than simply evaluating $LOGNAME! even in contexts without + //$LOGNAME, e.g. "sudo su" on Ubuntu, it returns the correct non-root user! + return utfTo<std::wstring>(loginUser); } @@ -64,7 +61,7 @@ ComputerModel zen::getComputerModel() //throw FileError cm.vendor = tryGetInfo("/sys/devices/virtual/dmi/id/sys_vendor"); // //clean up: - cm.model = beforeFirst(cm.model, L'\u00ff', IfNotFoundReturn::all); //fix broken BIOS entries: + cm.model = beforeFirst(cm.model, L'\u00ff', IfNotFoundReturn::all); //fix broken BIOS entries: cm.vendor = beforeFirst(cm.vendor, L'\u00ff', IfNotFoundReturn::all); //0xff can be considered 0 for (const char* dummyModel : @@ -119,21 +116,25 @@ Zstring zen::getRealProcessPath() //throw FileError } +Zstring zen::getUserDataPath() //throw FileError +{ + if (::getuid() != 0) //nofail; root(0) => consider as request for elevation, NOT impersonation + if (const char* xdgCfgPath = ::getenv("XDG_CONFIG_HOME"); //no extended error reporting + xdgCfgPath && xdgCfgPath[0] != 0) + return xdgCfgPath; + + return Zstring("/home/") + utfTo<Zstring>(getUserName()) + "/.config"; //throw FileError +} + + Zstring zen::getUserDownloadsPath() //throw FileError { try { - Zstring cmdLine; - if (getuid() == 0) //nofail; root(0) => consider as request for elevation, NOT impersonation - { - const char* loginUser = getlogin(); //https://linux.die.net/man/3/getlogin - if (!loginUser) - THROW_LAST_SYS_ERROR("getlogin"); - - cmdLine = Zstring("sudo -u ") + loginUser + " xdg-user-dir DOWNLOAD"; //sudo better be installed :> - } - else - cmdLine = "xdg-user-dir DOWNLOAD"; + const Zstring cmdLine = ::getuid() == 0 ? //nofail; root(0) => consider as request for elevation, NOT impersonation + //sudo better be installed :> + "sudo -u " + utfTo<Zstring>(getUserName()) + " xdg-user-dir DOWNLOAD" : //throw FileError + "xdg-user-dir DOWNLOAD"; const auto& [exitCode, output] = consoleExecute(cmdLine, std::nullopt /*timeoutMs*/); //throw SysError if (exitCode != 0) @@ -145,4 +146,3 @@ Zstring zen::getUserDownloadsPath() //throw FileError } catch (const SysError& e) { throw FileError(_("Cannot get process information."), e.toString()); } } - diff --git a/zen/sys_info.h b/zen/sys_info.h index 1b046fb6..4f83a9a3 100644 --- a/zen/sys_info.h +++ b/zen/sys_info.h @@ -31,6 +31,7 @@ std::wstring getOsDescription(); //throw FileError Zstring getRealProcessPath(); //throw FileError Zstring getUserDownloadsPath(); //throw FileError +Zstring getUserDataPath(); //throw FileError } diff --git a/zen/thread.cpp b/zen/thread.cpp index 89fa0233..e14afac7 100644 --- a/zen/thread.cpp +++ b/zen/thread.cpp @@ -28,7 +28,7 @@ const std::thread::id globalMainThreadId = std::this_thread::get_id(); bool zen::runningOnMainThread() { - if (globalMainThreadId == std::thread::id()) //called during static initialization! + if (globalMainThreadId == std::thread::id()) //if called during static initialization! return true; return std::this_thread::get_id() == globalMainThreadId; diff --git a/zen/thread.h b/zen/thread.h index 1bea95ea..136c7a5c 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -173,18 +173,18 @@ public: void run(Function&& wi /*should throw ThreadStopRequest when needed*/, bool insertFront = false) { { - std::lock_guard dummy(workLoad_->lock); + std::lock_guard dummy(workLoad_.ref().lock); if (insertFront) - workLoad_->tasks.push_front(std::move(wi)); + workLoad_.ref().tasks.push_front(std::move(wi)); else - workLoad_->tasks.push_back(std::move(wi)); - const size_t tasksPending = ++(workLoad_->tasksPending); + workLoad_.ref().tasks.push_back(std::move(wi)); + const size_t tasksPending = ++(workLoad_.ref().tasksPending); if (worker_.size() < std::min(tasksPending, threadCountMax_)) addWorkerThread(); } - workLoad_->conditionNewTask.notify_all(); + workLoad_.ref().conditionNewTask.notify_all(); } //context of controlling thread, blocking: @@ -203,12 +203,12 @@ public: //non-blocking wait()-alternative: context of controlling thread: void notifyWhenDone(const std::function<void()>& onCompletion /*noexcept! runs on worker thread!*/) { - std::lock_guard dummy(workLoad_->lock); + std::lock_guard dummy(workLoad_.ref().lock); - if (workLoad_->tasksPending == 0) + if (workLoad_.ref().tasksPending == 0) onCompletion(); else - workLoad_->onCompletionCallbacks.push_back(onCompletion); + workLoad_.ref().onCompletionCallbacks.push_back(onCompletion); } //context of controlling thread: @@ -222,27 +222,28 @@ private: { Zstring threadName = groupName_ + Zstr('[') + numberTo<Zstring>(worker_.size() + 1) + Zstr('/') + numberTo<Zstring>(threadCountMax_) + Zstr(']'); - worker_.emplace_back([wl = workLoad_, threadName = std::move(threadName)] //don't capture "this"! consider detach() and move operations + worker_.emplace_back([workLoad_ /*clang bug*/= workLoad_ /*share ownership!*/, threadName = std::move(threadName)]() mutable //don't capture "this"! consider detach() and move operations { setCurrentThreadName(threadName); + WorkLoad& workLoad = workLoad_.ref(); - std::unique_lock dummy(wl->lock); + std::unique_lock dummy(workLoad.lock); for (;;) { - interruptibleWait(wl->conditionNewTask, dummy, [&tasks = wl->tasks] { return !tasks.empty(); }); //throw ThreadStopRequest + interruptibleWait(workLoad.conditionNewTask, dummy, [&tasks = workLoad.tasks] { return !tasks.empty(); }); //throw ThreadStopRequest - Function task = std::move(wl->tasks. front()); //noexcept thanks to move - /**/ wl->tasks.pop_front(); // + Function task = std::move(workLoad.tasks. front()); //noexcept thanks to move + /**/ workLoad.tasks.pop_front(); // dummy.unlock(); task(); //throw ThreadStopRequest? dummy.lock(); - if (--(wl->tasksPending) == 0) - if (!wl->onCompletionCallbacks.empty()) + if (--(workLoad.tasksPending) == 0) + if (!workLoad.onCompletionCallbacks.empty()) { std::vector<std::function<void()>> callbacks; - callbacks.swap(wl->onCompletionCallbacks); + callbacks.swap(workLoad.onCompletionCallbacks); dummy.unlock(); for (const auto& cb : callbacks) @@ -263,7 +264,7 @@ private: }; std::vector<InterruptibleThread> worker_; - std::shared_ptr<WorkLoad> workLoad_ = std::make_shared<WorkLoad>(); + SharedRef<WorkLoad> workLoad_ = makeSharedRef<WorkLoad>(); bool detach_ = false; size_t threadCountMax_; Zstring groupName_; @@ -446,7 +447,7 @@ private: activeCondition_ = cv; } - std::atomic<bool> stopRequested_{ false }; //std:atomic is uninitialized by default!!! + std::atomic<bool> stopRequested_{false}; //std:atomic is uninitialized by default!!! //"The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects." std::condition_variable* activeCondition_ = nullptr; @@ -271,7 +271,7 @@ TimeComp parseTime(const String& format, const String2& str) if (strLast - itStr < 3) return TimeComp(); - const char* months[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; + const char* months[] = {"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"}; auto itMonth = std::find_if(std::begin(months), std::end(months), [&](const char* month) { return equalAsciiNoCase(makeStringView(itStr, 3), month); diff --git a/zen/type_traits.h b/zen/type_traits.h index 03fbd768..a4194c05 100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -20,6 +20,7 @@ struct GetFirstOf }; template<class... T> using GetFirstOfT = typename GetFirstOf<T...>::Type; + template <class F> class FunctionReturnType { @@ -48,40 +49,40 @@ template<class T> inline auto makeSigned (T t) { return static_cast<std::make_s template<class T> inline auto makeUnsigned(T t) { return static_cast<std::make_unsigned_t<T>>(t); } //################# Built-in Types ######################## -//Example: "IsSignedInt<int>::value" evaluates to "true" - //unfortunate standardized nonsense: std::is_integral<> includes bool, char, wchar_t! => roll our own: -template <class T> struct IsUnsignedInt; -template <class T> struct IsSignedInt; - -template <class T> using IsFloat = std::is_floating_point<T>; -template <class T> using IsInteger = std::bool_constant<IsUnsignedInt<T>::value || IsSignedInt<T>::value>; -template <class T> using IsArithmetic = std::bool_constant<IsInteger <T>::value || IsFloat <T>::value>; - -//remaining non-arithmetic types: bool, char, wchar_t - - -//optional: specialize new types like: -//template <> struct IsUnsignedInt<UInt64> : std::true_type {}; +template<class T> constexpr bool IsUnsignedIntV = std::is_same_v<std::remove_cv_t<T>, unsigned char> || + std::is_same_v<std::remove_cv_t<T>, unsigned short int> || + std::is_same_v<std::remove_cv_t<T>, unsigned int> || + std::is_same_v<std::remove_cv_t<T>, unsigned long int> || + std::is_same_v<std::remove_cv_t<T>, unsigned long long int>; + +template<class T> constexpr bool IsSignedIntV = std::is_same_v<std::remove_cv_t<T>, signed char> || + std::is_same_v<std::remove_cv_t<T>, short int> || + std::is_same_v<std::remove_cv_t<T>, int> || + std::is_same_v<std::remove_cv_t<T>, long int> || + std::is_same_v<std::remove_cv_t<T>, long long int>; + +template<class T> constexpr bool IsIntegerV = IsUnsignedIntV<T> || IsSignedIntV<T>; +template<class T> constexpr bool IsFloatV = std::is_floating_point_v<T>; +template<class T> constexpr bool IsArithmeticV = IsIntegerV<T> || IsFloatV<T>; //################# Class Members ######################## /* Detect data or function members of a class by name: ZEN_INIT_DETECT_MEMBER + HasMember_ Example: 1. ZEN_INIT_DETECT_MEMBER(c_str); 2. HasMemberV_c_str<T> -> use boolean -*/ -/* Detect data or function members of a class by name *and* type: ZEN_INIT_DETECT_MEMBER2 + HasMember_ + + Detect data or function members of a class by name *and* type: ZEN_INIT_DETECT_MEMBER2 + HasMember_ Example: 1. ZEN_INIT_DETECT_MEMBER2(size, size_t (T::*)() const); 2. HasMember_size<T>::value -> use as boolean -*/ -/* Detect member type of a class: ZEN_INIT_DETECT_MEMBER_TYPE + HasMemberType_ + + Detect member type of a class: ZEN_INIT_DETECT_MEMBER_TYPE + HasMemberType_ Example: 1. ZEN_INIT_DETECT_MEMBER_TYPE(value_type); - 2. HasMemberTypeV_value_type<T> -> use as boolean -*/ + 2. HasMemberTypeV_value_type<T> -> use as boolean */ //########## Sorting ############################## /* @@ -114,25 +115,6 @@ LessDescending<Predicate> makeSortDirection(Predicate pred, std::false_type) { r //################ implementation ###################### -template <class T> -struct IsUnsignedInt : std::false_type {}; - -template <> struct IsUnsignedInt<unsigned char > : std::true_type {}; -template <> struct IsUnsignedInt<unsigned short int > : std::true_type {}; -template <> struct IsUnsignedInt<unsigned int > : std::true_type {}; -template <> struct IsUnsignedInt<unsigned long int > : std::true_type {}; -template <> struct IsUnsignedInt<unsigned long long int> : std::true_type {}; - -template <class T> -struct IsSignedInt : std::false_type {}; - -template <> struct IsSignedInt<signed char > : std::true_type {}; -template <> struct IsSignedInt<short int > : std::true_type {}; -template <> struct IsSignedInt<int > : std::true_type {}; -template <> struct IsSignedInt<long int > : std::true_type {}; -template <> struct IsSignedInt<long long int> : std::true_type {}; -//#################################################################### - #define ZEN_INIT_DETECT_MEMBER(NAME) \ \ template<bool isClass, class T> \ |