diff options
author | B Stack <bgstack15@gmail.com> | 2020-07-22 11:37:03 -0400 |
---|---|---|
committer | B Stack <bgstack15@gmail.com> | 2020-07-22 11:37:03 -0400 |
commit | c95b3937fef3e2c63768f1b3b1dc2c898f23d91d (patch) | |
tree | 10260e25ae905564f7978b83fc4e316670f987c6 /zen | |
parent | Merge branch '10.25' into 'master' (diff) | |
download | FreeFileSync-c95b3937fef3e2c63768f1b3b1dc2c898f23d91d.tar.gz FreeFileSync-c95b3937fef3e2c63768f1b3b1dc2c898f23d91d.tar.bz2 FreeFileSync-c95b3937fef3e2c63768f1b3b1dc2c898f23d91d.zip |
add upstream 11.0
Diffstat (limited to 'zen')
-rw-r--r-- | zen/base64.h | 2 | ||||
-rw-r--r-- | zen/error_log.h | 17 | ||||
-rw-r--r-- | zen/file_access.cpp | 80 | ||||
-rw-r--r-- | zen/file_access.h | 16 | ||||
-rw-r--r-- | zen/file_io.cpp | 14 | ||||
-rw-r--r-- | zen/http.cpp | 4 | ||||
-rw-r--r-- | zen/legacy_compiler.cpp | 6 | ||||
-rw-r--r-- | zen/legacy_compiler.h | 3 | ||||
-rw-r--r-- | zen/open_ssl.cpp | 2 | ||||
-rw-r--r-- | zen/perf.h | 15 | ||||
-rw-r--r-- | zen/recycler.cpp | 10 | ||||
-rw-r--r-- | zen/scope_guard.h | 10 | ||||
-rw-r--r-- | zen/serialize.h | 38 | ||||
-rw-r--r-- | zen/shell_execute.cpp | 1 | ||||
-rw-r--r-- | zen/stl_tools.h | 2 | ||||
-rw-r--r-- | zen/string_tools.h | 12 | ||||
-rw-r--r-- | zen/string_traits.h | 4 | ||||
-rw-r--r-- | zen/symlink_target.h | 16 | ||||
-rw-r--r-- | zen/system.cpp | 4 | ||||
-rw-r--r-- | zen/zstring.cpp | 11 | ||||
-rw-r--r-- | zen/zstring.h | 6 |
21 files changed, 126 insertions, 147 deletions
diff --git a/zen/base64.h b/zen/base64.h index 623f8f7f..b39a6a52 100644 --- a/zen/base64.h +++ b/zen/base64.h @@ -114,7 +114,7 @@ OutputIterator decodeBase64(InputIterator first, InputIterator last, OutputItera return INDEX_END; const unsigned char ch = static_cast<unsigned char>(*first++); - if (ch < 128) //we're in lower ASCII table half + if (ch < arraySize(DECODING_MIME)) //we're in lower ASCII table half { const int index = DECODING_MIME[ch]; if (0 <= index && index <= static_cast<int>(INDEX_PAD)) //skip all unknown characters (including carriage return, line-break, tab) diff --git a/zen/error_log.h b/zen/error_log.h index ab23e33a..8604f127 100644 --- a/zen/error_log.h +++ b/zen/error_log.h @@ -19,16 +19,15 @@ namespace zen { enum MessageType { - MSG_TYPE_INFO = 0x1, - MSG_TYPE_WARNING = 0x2, - MSG_TYPE_ERROR = 0x4, - MSG_TYPE_FATAL_ERROR = 0x8, + MSG_TYPE_INFO = 0x1, + MSG_TYPE_WARNING = 0x2, + MSG_TYPE_ERROR = 0x4, }; struct LogEntry { time_t time = 0; - MessageType type = MSG_TYPE_FATAL_ERROR; + MessageType type = MSG_TYPE_ERROR; Zstringc message; //conserve memory (=> avoid std::string SSO overhead!) }; @@ -45,7 +44,6 @@ public: int info = 0; int warning = 0; int error = 0; - int fatal = 0; }; Stats getStats() const; @@ -91,11 +89,8 @@ ErrorLog::Stats ErrorLog::getStats() const case MSG_TYPE_ERROR: ++count.error; break; - case MSG_TYPE_FATAL_ERROR: - ++count.fatal; - break; } - assert(static_cast<int>(entries_.size()) == count.info + count.warning + count.error + count.fatal); + assert(static_cast<int>(entries_.size()) == count.info + count.warning + count.error); return count; } @@ -111,8 +106,6 @@ std::wstring getMessageTypeLabel(MessageType type) return _("Warning"); case MSG_TYPE_ERROR: return _("Error"); - case MSG_TYPE_FATAL_ERROR: - return _("Serious Error"); } assert(false); return std::wstring(); diff --git a/zen/file_access.cpp b/zen/file_access.cpp index 8f021843..10713ce5 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -176,7 +176,7 @@ int64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns < struct ::statfs info = {}; if (::statfs(path.c_str(), &info) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtPath(path)), "statfs"); - + return static_cast<int64_t>(info.f_bsize) * info.f_bavail; } @@ -205,11 +205,12 @@ Zstring zen::getTempFolderPath() //throw FileError { if (const char* buf = ::getenv("TMPDIR")) //no extended error reporting return buf; - + //TMPDIR not set on CentOS 7, WTF! return P_tmpdir; //usually resolves to "/tmp" } + void zen::removeFilePlain(const Zstring& filePath) //throw FileError { const char* functionName = "unlink"; @@ -300,11 +301,11 @@ namespace /* Usage overview: (avoid circular pattern!) - moveAndRenameItem() --> moveAndRenameFileSub() - | /|\ - \|/ | - Fix8Dot3NameClash() -*/ + moveAndRenameItem() --> moveAndRenameFileSub() + | /|\ + \|/ | + Fix8Dot3NameClash() */ + //wrapper for file system rename function: void moveAndRenameFileSub(const Zstring& pathFrom, const Zstring& pathTo, bool replaceExisting) //throw FileError, ErrorMoveUnsupported, ErrorTargetExisting { @@ -521,10 +522,12 @@ void zen::createDirectory(const Zstring& dirPath) //throw FileError, ErrorTarget if (std::all_of(dirName.begin(), dirName.end(), [](Zchar c) { return c == Zstr('.'); })) /**/throw FileError(getErrorMsg(), replaceCpy<std::wstring>(L"Invalid folder name %x.", L"%x", fmtPath(dirName))); + #if 0 //not appreciated: https://freefilesync.org/forum/viewtopic.php?t=7509 //not critical, but will visually confuse user sooner or later: if (startsWith(dirName, Zstr(' ')) || endsWith (dirName, Zstr(' '))) throw FileError(getErrorMsg(), replaceCpy<std::wstring>(L"Folder name %x starts/ends with space character.", L"%x", fmtPath(dirName))); + #endif const mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; //0777, default for newly created directories @@ -542,16 +545,16 @@ void zen::createDirectory(const Zstring& dirPath) //throw FileError, ErrorTarget } -void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw FileError +bool zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw FileError { const std::optional<Zstring> parentPath = getParentFolderPath(dirPath); if (!parentPath) //device root - return; + return false; try //generally we expect that path already exists (see: ffs_paths.cpp) => check first { if (getItemType(dirPath) != ItemType::file) //throw FileError - return; + return false; } catch (FileError&) {} //not yet existing or access error? let's find out... @@ -560,13 +563,14 @@ void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw File try { createDirectory(dirPath); //throw FileError, ErrorTargetExisting + return true; } catch (FileError&) { try { if (getItemType(dirPath) != ItemType::file) //throw FileError - return; //already existing => possible, if createDirectoryIfMissingRecursion() is run in parallel + return true; //already existing => possible, if createDirectoryIfMissingRecursion() is run in parallel } catch (FileError&) {} //not yet existing or access error @@ -580,12 +584,19 @@ void zen::tryCopyDirectoryAttributes(const Zstring& sourcePath, const Zstring& t } -void zen::copySymlink(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions) //throw FileError +void zen::copySymlink(const Zstring& sourcePath, const Zstring& targetPath) //throw FileError { - const Zstring linkPath = getSymlinkTargetRaw(sourcePath); //throw FileError; accept broken symlinks + const SymlinkRawContent linkContent = getSymlinkRawContent(sourcePath); //throw FileError; accept broken symlinks - if (::symlink(linkPath.c_str(), targetPath.c_str()) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L'\n' + fmtPath(sourcePath)), L"%y", L'\n' + fmtPath(targetPath)), "symlink"); + try //harmonize with NativeFileSystem::equalSymlinkContentForSameAfsType() + { + if (::symlink(linkContent.targetPath.c_str(), targetPath.c_str()) != 0) + THROW_LAST_SYS_ERROR("symlink"); + } + catch (const SysError& e) + { + throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L'\n' + fmtPath(sourcePath)), L"%y", L'\n' + fmtPath(targetPath)), e.toString()); + } //allow only consistent objects to be created -> don't place before ::symlink(); targetPath may already exist! ZEN_ON_SCOPE_FAIL(try { removeSymlinkPlain(targetPath); /*throw FileError*/ } @@ -597,17 +608,11 @@ void zen::copySymlink(const Zstring& sourcePath, const Zstring& targetPath, bool 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 - - if (copyFilePermissions) - copyItemPermissions(sourcePath, targetPath, ProcSymlink::DIRECT); //throw FileError } -namespace -{ -FileCopyResult copyFileOsSpecific(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting - const Zstring& targetFile, - const IOCallback& notifyUnbufferedIO) +FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, //throw FileError, ErrorTargetExisting, (ErrorFileLocked), X + const IOCallback& notifyUnbufferedIO /*throw X*/) { int64_t totalUnbufferedIO = 0; @@ -678,33 +683,4 @@ FileCopyResult copyFileOsSpecific(const Zstring& sourceFile, //throw FileError, return result; } -/* ------------------ - |File Copy Layers| - ------------------ - copyNewFile - | - copyFileOsSpecific (solve 8.3 issue on Windows) - | - copyFileWindowsSelectRoutine - / \ -copyFileWindowsDefault(::CopyFileEx) copyFileWindowsStream(::BackupRead/::BackupWrite) -*/ -} - - -FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked, X - const IOCallback& notifyUnbufferedIO /*throw X*/) -{ - const FileCopyResult result = copyFileOsSpecific(sourceFile, targetFile, notifyUnbufferedIO); //throw FileError, ErrorTargetExisting, ErrorFileLocked, X - - //at this point we know we created a new file, so it's fine to delete it for cleanup! - ZEN_ON_SCOPE_FAIL(try { removeFilePlain(targetFile); } - catch (FileError&) {}); - - if (copyFilePermissions) - copyItemPermissions(sourceFile, targetFile, ProcSymlink::FOLLOW); //throw FileError - - return result; -} - diff --git a/zen/file_access.h b/zen/file_access.h index 66c41fbd..a3fa56d7 100644 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -52,7 +52,7 @@ enum class ProcSymlink }; void setFileTime(const Zstring& filePath, time_t modTime, ProcSymlink procSl); //throw FileError -//symlink handling: always follow: +//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 @@ -73,16 +73,16 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P void createDirectory(const Zstring& dirPath); //throw FileError, ErrorTargetExisting -//- no error if already existing -//- create recursively if parent directory is not existing -void createDirectoryIfMissingRecursion(const Zstring& dirPath); //throw FileError +//creates directories recursively if not existing +//returns false if folder already exists +bool createDirectoryIfMissingRecursion(const Zstring& dirPath); //throw FileError -//symlink handling: follow link! +//symlink handling: follow //expects existing source/target directories -//reports note-worthy errors only +//reports "note-worthy" errors only void tryCopyDirectoryAttributes(const Zstring& sourcePath, const Zstring& targetPath); //throw FileError -void copySymlink(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions); //throw FileError +void copySymlink(const Zstring& sourcePath, const Zstring& targetPath); //throw FileError struct FileCopyResult { @@ -93,7 +93,7 @@ struct FileCopyResult std::optional<FileError> errorModTime; //failure to set modification time }; -FileCopyResult copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked, X +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*/); diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 942f367f..80a83724 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -46,23 +46,21 @@ namespace { FileBase::FileHandle openHandleForRead(const Zstring& filePath) //throw FileError, ErrorFileLocked { - //- "filePath" could be a named pipe which *blocks* forever for open()! - //- open() with O_NONBLOCK avoids the block, but opens successfully - //- create sample pipe: "sudo mkfifo named_pipe" + //caveat: check for file types that block during open(): character device, block device, named pipe struct ::stat fileInfo = {}; if (::stat(filePath.c_str(), &fileInfo) == 0) //follows symlinks { if (!S_ISREG(fileInfo.st_mode) && - !S_ISLNK(fileInfo.st_mode) && - !S_ISDIR(fileInfo.st_mode)) + !S_ISDIR(fileInfo.st_mode) && //open() will fail with "EISDIR: Is a directory" => nice + !S_ISLNK(fileInfo.st_mode)) //?? shouldn't be possible after successful stat() { const std::wstring typeName = [m = fileInfo.st_mode] { std::wstring name = - S_ISCHR (m) ? L"character device" : - S_ISBLK (m) ? L"block device" : + S_ISCHR (m) ? L"character device" : //e.g. /dev/null + S_ISBLK (m) ? L"block device" : //e.g. /dev/sda1 S_ISFIFO(m) ? L"FIFO, named pipe" : - S_ISSOCK(m) ? L"socket" : L""; + S_ISSOCK(m) ? L"socket" : L""; //doesn't block but open() error is unclear: "ENXIO: No such device or address" if (!name.empty()) name += L", "; return name + printNumber<std::wstring>(L"0%06o", m & S_IFMT); diff --git a/zen/http.cpp b/zen/http.cpp index 848b2cb3..57d61221 100644 --- a/zen/http.cpp +++ b/zen/http.cpp @@ -492,7 +492,7 @@ bool zen::isValidEmail(const std::string& email) if (comp.empty() || !std::all_of(comp.begin(), comp.end(), [](char c) { const char printable[] = "!#$%&'*+-/=?^_`{|}~"; - return isAsciiAlpha(c) || isDigit(c) || makeUnsigned(c) >= 128 || + return isAsciiAlpha(c) || isDigit(c) || !isAsciiChar(c) || std::find(std::begin(printable), std::end(printable), c) != std::end(printable); })) return false; @@ -507,7 +507,7 @@ bool zen::isValidEmail(const std::string& email) for (const std::string& comp : split(domain, '.', SplitType::ALLOW_EMPTY)) if (comp.empty() || comp.size() > 63 || - !std::all_of(comp.begin(), comp.end(), [](char c) { return isAsciiAlpha(c) ||isDigit(c) || makeUnsigned(c) >= 128 || c == '-'; })) + !std::all_of(comp.begin(), comp.end(), [](char c) { return isAsciiAlpha(c) ||isDigit(c) || !isAsciiChar(c) || c == '-'; })) return false; } diff --git a/zen/legacy_compiler.cpp b/zen/legacy_compiler.cpp index 3e3b7ba7..416993ed 100644 --- a/zen/legacy_compiler.cpp +++ b/zen/legacy_compiler.cpp @@ -5,12 +5,6 @@ // ***************************************************************************** #include "legacy_compiler.h" -#include <charconv> -//1. including this one in string_tools.h blows up VC++: -// "An internal error has occurred in the compiler. (compiler file 'd:\agent\_work\1\s\src\vctools\Compiler\Utc\src\p2\p2symtab.c', line 2618)" -//2. using inside PCH: "fatal error C1076: compiler limit: internal heap limit reached" - - #if __cpp_lib_to_chars #error get rid of workarounds #endif diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h index 13cdd8d0..16e22d03 100644 --- a/zen/legacy_compiler.h +++ b/zen/legacy_compiler.h @@ -12,8 +12,9 @@ -//https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations +//https://en.cppreference.com/w/cpp/feature_test //https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros +//https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations //https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html namespace std { diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp index f436875b..f2b48fd9 100644 --- a/zen/open_ssl.cpp +++ b/zen/open_ssl.cpp @@ -628,7 +628,7 @@ private: zen::TlsContext::TlsContext(int socket, const Zstring& server, const Zstring* caCertFilePath) : pimpl_(std::make_unique<Impl>(socket, utfTo<std::string>(server), caCertFilePath)) {} //throw SysError zen::TlsContext::~TlsContext() {} -size_t zen::TlsContext::tryRead ( void* buffer, size_t bytesToRead ) { return pimpl_->tryRead(buffer, bytesToRead); } //throw SysError +size_t zen::TlsContext::tryRead ( void* buffer, size_t bytesToRead ) { return pimpl_->tryRead (buffer, bytesToRead); } //throw SysError size_t zen::TlsContext::tryWrite(const void* buffer, size_t bytesToWrite) { return pimpl_->tryWrite(buffer, bytesToWrite); } //throw SysError @@ -21,9 +21,9 @@ /* Example: Aggregated function call time: - static zen::PerfTimer timer; - timer.resume(); - ZEN_ON_SCOPE_EXIT(timer.pause()); + static zen::PerfTimer perfTest(true); //startPaused + perfTest.resume(); + ZEN_ON_SCOPE_EXIT(perfTest.pause()); */ namespace zen @@ -41,6 +41,8 @@ namespace zen class StopWatch { public: + explicit StopWatch(bool startPaused = false) : paused_(startPaused) {} + bool isPaused() const { return paused_; } void pause() @@ -77,7 +79,7 @@ public: } private: - bool paused_ = false; + bool paused_; std::chrono::steady_clock::time_point startTime_ = std::chrono::steady_clock::now(); std::chrono::nanoseconds elapsedUntilPause_{}; //std::chrono::duration is uninitialized by default! WTF! When will this stupidity end??? }; @@ -86,10 +88,13 @@ private: class PerfTimer { public: - [[deprecated]] PerfTimer() {} + [[deprecated]] explicit PerfTimer(bool startPaused = false) : watch_(startPaused) {} ~PerfTimer() { if (!resultShown_) showResult(); } + void pause () { watch_.pause(); } + void resume() { watch_.resume(); } + void showResult() { const bool wasRunning = !watch_.isPaused(); diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 28b2d6c1..9c463546 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -23,7 +23,7 @@ bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError ZEN_ON_SCOPE_EXIT(g_object_unref(file);) GError* error = nullptr; - ZEN_ON_SCOPE_EXIT(if (error) ::g_error_free(error);); + ZEN_ON_SCOPE_EXIT(if (error) ::g_error_free(error)); if (!::g_file_trash(file, nullptr, &error)) { @@ -52,10 +52,9 @@ bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError } -/* -We really need access to a similar function to check whether a directory supports trashing and emit a warning if it does not! +/* We really need access to a similar function to check whether a directory supports trashing and emit a warning if it does not! -The following function looks perfect, alas it is restricted to local files and to the implementation of GIO only: + The following function looks perfect, alas it is restricted to local files and to the implementation of GIO only: gboolean _g_local_file_has_trash_dir(const char* dirpath, dev_t dir_dev); See: http://www.netmite.com/android/mydroid/2.0/external/bluetooth/glib/gio/glocalfileinfo.h @@ -67,5 +66,4 @@ The following function looks perfect, alas it is restricted to local files and t writable && parent_info->has_trash_dir); => We're NOT interested in whether the specified folder can be trashed, but whether it supports thrashing its child elements! (Only support, not actual write access!) - This renders G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH useless for this purpose. -*/ + This renders G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH useless for this purpose. */ diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 846d5663..e97d3f0a 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -60,7 +60,8 @@ template <typename F> inline void runScopeGuardDestructor(F& fun, bool failed, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::onFail>) noexcept { if (failed) - try { fun(); } catch (...) { assert(false); } + try { fun(); } + catch (...) { assert(false); } } @@ -71,9 +72,10 @@ public: explicit ScopeGuard(const F& fun) : fun_(fun) {} explicit ScopeGuard( F&& fun) : fun_(std::move(fun)) {} - ScopeGuard(ScopeGuard&& other) : fun_(std::move(other.fun_)), - exeptionCount_(other.exeptionCount_), - dismissed_(other.dismissed_) { other.dismissed_ = true; } + ScopeGuard(ScopeGuard&& tmp) : + fun_(std::move(tmp.fun_)), + exeptionCount_(tmp.exeptionCount_), + dismissed_(tmp.dismissed_) { tmp.dismissed_ = true; } ~ScopeGuard() noexcept(runMode == ScopeGuardRunMode::onFail) { diff --git a/zen/serialize.h b/zen/serialize.h index a34f91a7..6c57e4ee 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -11,21 +11,20 @@ #include <cstdint> #include <stdexcept> #include "string_base.h" +#include "sys_error.h" //keep header clean from specific stream implementations! (e.g.file_io.h)! used by abstract.h! namespace zen { -//high-performance unformatted serialization (avoiding wxMemoryOutputStream/wxMemoryInputStream inefficiencies) +/* high-performance unformatted serialization (avoiding wxMemoryOutputStream/wxMemoryInputStream inefficiencies) -/* -------------------------- |Binary Container Concept| -------------------------- binary container for data storage: must support "basic" std::vector interface (e.g. std::vector<std::byte>, std::string, Zbase<char>) -*/ -/* + ------------------------------- |Buffered Input Stream Concept| ------------------------------- @@ -49,8 +48,7 @@ struct BufferedOutputStream Optional: support stream-copying -------------------------------- const IOCallback& notifyUnbufferedIO -}; -*/ +}; */ using IOCallback = std::function<void(int64_t bytesDelta)>; //throw X @@ -65,8 +63,12 @@ template <class N, class BufferedOutputStream> void writeNumber (BufferedOutpu template <class C, class BufferedOutputStream> void writeContainer(BufferedOutputStream& stream, const C& str); //noexcept template < class BufferedOutputStream> void writeArray (BufferedOutputStream& stream, const void* buffer, size_t len); // //---------------------------------------------------------------------- -class UnexpectedEndOfStreamError {}; -template <class N, class BufferedInputStream> N readNumber (BufferedInputStream& stream); //throw UnexpectedEndOfStreamError (corrupted data) +struct SysErrorUnexpectedEos : public SysError +{ + SysErrorUnexpectedEos() : SysError(_("File content is corrupted.") + L" (unexpected end of stream)") {} +}; + +template <class N, class BufferedInputStream> N readNumber (BufferedInputStream& stream); //throw SysErrorUnexpectedEos (corrupted data) template <class C, class BufferedInputStream> C readContainer(BufferedInputStream& stream); // template < class BufferedInputStream> void readArray (BufferedInputStream& stream, void* buffer, size_t len); // @@ -220,40 +222,40 @@ void writeContainer(BufferedOutputStream& stream, const C& cont) //don't even co template <class BufferedInputStream> inline -void readArray(BufferedInputStream& stream, void* buffer, size_t len) //throw UnexpectedEndOfStreamError +void readArray(BufferedInputStream& stream, void* buffer, size_t len) //throw SysErrorUnexpectedEos { const size_t bytesRead = stream.read(buffer, len); assert(bytesRead <= len); //buffer overflow otherwise not always detected! if (bytesRead < len) - throw UnexpectedEndOfStreamError(); + throw SysErrorUnexpectedEos(); } template <class N, class BufferedInputStream> inline -N readNumber(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError +N readNumber(BufferedInputStream& stream) //throw SysErrorUnexpectedEos { static_assert(IsArithmetic<N>::value || std::is_same_v<N, bool> || std::is_enum_v<N>); N num{}; - readArray(stream, &num, sizeof(N)); //throw UnexpectedEndOfStreamError + readArray(stream, &num, sizeof(N)); //throw SysErrorUnexpectedEos return num; } template <class C, class BufferedInputStream> inline -C readContainer(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError +C readContainer(BufferedInputStream& stream) //throw SysErrorUnexpectedEos { C cont; - auto strLength = readNumber<uint32_t>(stream); //throw UnexpectedEndOfStreamError + auto strLength = readNumber<uint32_t>(stream); //throw SysErrorUnexpectedEos if (strLength > 0) { try { - cont.resize(strLength); //throw std::length_error + cont.resize(strLength); //throw std::length_error, std::bad_alloc } - catch (std::length_error&) { throw UnexpectedEndOfStreamError(); } //most likely this is due to data corruption! - catch ( std::bad_alloc&) { throw UnexpectedEndOfStreamError(); } // + catch (std::length_error&) { throw SysErrorUnexpectedEos(); } //most likely this is due to data corruption! + catch ( std::bad_alloc&) { throw SysErrorUnexpectedEos(); } // - readArray(stream, &cont[0], sizeof(typename C::value_type) * strLength); //throw UnexpectedEndOfStreamError + readArray(stream, &cont[0], sizeof(typename C::value_type) * strLength); //throw SysErrorUnexpectedEos } return cont; } diff --git a/zen/shell_execute.cpp b/zen/shell_execute.cpp index c8779bb8..90ccfdf3 100644 --- a/zen/shell_execute.cpp +++ b/zen/shell_execute.cpp @@ -82,6 +82,7 @@ std::pair<int /*exit code*/, std::wstring> zen::consoleExecute(const Zstring& cm const int EC_CHILD_LAUNCH_FAILED = 120; //avoid 127: used by the system, e.g. failure to execute due to missing .so file + //use O_TMPFILE? sounds nice, but support is probably crap: https://github.com/libvips/libvips/issues/1151 const int fdTempFile = ::open(tempFilePath.c_str(), O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR); //0600 if (fdTempFile == -1) diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 5fd29b6b..7d071413 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -270,7 +270,7 @@ Num hashArray(ByteIterator first, ByteIterator last) static_assert(IsInteger<ValType>::value || 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(static_cast<Num>(v)); }); + std::for_each(first, last, [&hash](ValType v) { hash.add(v); }); return hash.get(); } diff --git a/zen/string_tools.h b/zen/string_tools.h index eaf1a700..cfdb27bd 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -26,6 +26,7 @@ template <class Char> bool isWhiteSpace(Char c); template <class Char> bool isLineBreak (Char c); template <class Char> bool isDigit (Char c); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only! template <class Char> bool isHexDigit (Char c); +template <class Char> bool isAsciiChar (Char c); template <class Char> bool isAsciiAlpha(Char c); template <class S > bool isAsciiString(const S& str); template <class Char> Char asciiToLower(Char c); @@ -143,6 +144,13 @@ bool isHexDigit(Char c) template <class Char> inline +bool isAsciiChar(Char c) +{ + return makeUnsigned(c) < 128; +} + + +template <class Char> inline bool isAsciiAlpha(Char c) { static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>); @@ -155,7 +163,7 @@ template <class S> inline bool isAsciiString(const S& str) { const auto* const first = strBegin(str); - return std::all_of(first, first + strLength(str), [](auto c) { return makeUnsigned(c) < 128; }); + return std::all_of(first, first + strLength(str), [](auto c) { return isAsciiChar(c); }); } @@ -255,7 +263,7 @@ int compareString(const S& lhs, const T& rhs) const size_t lhsLen = strLength(lhs); const size_t rhsLen = strLength(rhs); - //length check *after* strcmpWithNulls(): we do care about natural ordering: e.g. for "compareString(makeUpperCopy(lhs), makeUpperCopy(rhs))" + //length check *after* strcmpWithNulls(): we do care about natural ordering: e.g. for "compareString(getUpperCase(lhs), getUpperCase(rhs))" if (const int rv = impl::strcmpWithNulls(strBegin(lhs), strBegin(rhs), std::min(lhsLen, rhsLen)); rv != 0) return rv; diff --git a/zen/string_traits.h b/zen/string_traits.h index 69d76b44..76d601b3 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -99,11 +99,7 @@ ZEN_INIT_DETECT_MEMBER(length) // template <class S> class StringTraits { -#if __cpp_lib_remove_cvref using CleanType = std::remove_cvref_t<S>; -#else - using CleanType = std::remove_cv_t<std::remove_reference_t<S>>; -#endif using NonArrayType = std::remove_extent_t <CleanType>; using NonPtrType = std::remove_pointer_t<NonArrayType>; using UndecoratedType = std::remove_cv_t <NonPtrType>; //handle "const char* const" diff --git a/zen/symlink_target.h b/zen/symlink_target.h index 077fd4b3..59003284 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -17,8 +17,13 @@ namespace zen { -Zstring getSymlinkResolvedPath(const Zstring& linkPath); //throw FileError; Win: requires Vista or later! -Zstring getSymlinkTargetRaw (const Zstring& linkPath); //throw FileError +struct SymlinkRawContent +{ + Zstring targetPath; +}; +SymlinkRawContent getSymlinkRawContent(const Zstring& linkPath); //throw FileError + +Zstring getSymlinkResolvedPath(const Zstring& linkPath); //throw FileError } @@ -34,7 +39,7 @@ Zstring getSymlinkTargetRaw (const Zstring& linkPath); //throw FileError namespace { //retrieve raw target data of symlink or junction -Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileError +zen::SymlinkRawContent getSymlinkRawContent_impl(const Zstring& linkPath) //throw FileError { using namespace zen; const size_t BUFFER_SIZE = 10000; @@ -46,7 +51,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro if (bytesWritten >= static_cast<ssize_t>(BUFFER_SIZE)) //detect truncation, not an error for readlink! throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), formatSystemError("readlink", L"", L"Buffer truncated.")); - return Zstring(&buffer[0], bytesWritten); //readlink does not append 0-termination! + return {Zstring(&buffer[0], bytesWritten)}; //readlink does not append 0-termination! } @@ -65,7 +70,8 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError namespace zen { inline -Zstring getSymlinkTargetRaw(const Zstring& linkPath) { return getSymlinkRawTargetString_impl(linkPath); } +SymlinkRawContent getSymlinkRawContent(const Zstring& linkPath) { return getSymlinkRawContent_impl(linkPath); } + inline Zstring getSymlinkResolvedPath(const Zstring& linkPath) { return getResolvedSymlinkPath_impl(linkPath); } diff --git a/zen/system.cpp b/zen/system.cpp index f39fae84..23e2c343 100644 --- a/zen/system.cpp +++ b/zen/system.cpp @@ -56,7 +56,7 @@ ComputerModel zen::getComputerModel() //throw FileError const std::string stream = loadBinContainer<std::string>(filePath, nullptr /*notifyUnbufferedIO*/); //throw FileError return utfTo<std::wstring>(trimCpy(stream)); } - catch (const FileError& e) { throw SysError(e.toString()); } //errors should be further enriched by context info => SysError + catch (const FileError& e) { throw SysError(replaceCpy(e.toString(), L"\n\n", L'\n')); } //errors should be further enriched by context info => SysError }; cm.model = tryGetInfo("/sys/devices/virtual/dmi/id/product_name"); //throw SysError cm.vendor = tryGetInfo("/sys/devices/virtual/dmi/id/sys_vendor"); // @@ -103,7 +103,7 @@ std::wstring zen::getOsDescription() //throw FileError { releaseInfo = loadBinContainer<std::string>("/etc/os-release", nullptr /*notifyUnbufferedIO*/); //throw FileError } - catch (const FileError& e) { throw SysError(e.toString()); } //further enrich with context info => SysError + catch (const FileError& e) { throw SysError(replaceCpy(e.toString(), L"\n\n", L'\n')); } //errors should be further enriched by context info => SysError std::string osName; std::string osVersion; diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 690f004b..b78bc118 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -14,7 +14,7 @@ using namespace zen; -Zstring makeUpperCopy(const Zstring& str) +Zstring getUpperCase(const Zstring& str) { //fast pre-check: if (isAsciiString(str)) //perf: in the range of 3.5ns @@ -81,7 +81,7 @@ Zstring replaceCpyAsciiNoCase(const Zstring& str, const Zstring& oldTerm, const for (size_t pos = 0;;) { - const size_t posFound = std::search(str.begin() + pos, str.end(), //can't use makeUpperCopy(): input/output sizes may differ! + const size_t posFound = std::search(str.begin() + pos, str.end(), //can't use getUpperCase(): input/output sizes may differ! oldTerm.begin(), oldTerm.end(), [](Zchar charL, Zchar charR) { return asciiToUpper(charL) == asciiToUpper(charR); }) - str.begin(); @@ -100,8 +100,7 @@ Zstring replaceCpyAsciiNoCase(const Zstring& str, const Zstring& oldTerm, const } -/* -https://docs.microsoft.com/de-de/windows/desktop/Intl/handling-sorting-in-your-applications +/* https://docs.microsoft.com/de-de/windows/desktop/Intl/handling-sorting-in-your-applications Perf test: compare strings 10 mio times; 64 bit build ----------------------------------------------------- @@ -120,8 +119,8 @@ OS X (UTF8 char) 856 ns | CFStringCreateWithCString + CFStringCompare(kCFCompareCaseInsensitive) 1110 ns | CFStringCreateWithCStringNoCopy + CFStringCompare(kCFCompareCaseInsensitive) ________________________ -time per call | function -*/ +time per call | function */ + int compareNativePath(const Zstring& lhs, const Zstring& rhs) { assert(lhs.find(Zchar('\0')) == Zstring::npos); //don't expect embedded nulls! diff --git a/zen/zstring.h b/zen/zstring.h index e262603e..adfd671b 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -28,7 +28,7 @@ using Zstringc = zen::Zbase<char>; // - different UTF-8 encoding length of upper-case chars // - different number of upper case chars (e.g. "ߢ => "SS" on macOS) // - output is Unicode-normalized -Zstring makeUpperCopy(const Zstring& str); +Zstring getUpperCase(const Zstring& str); //Windows, Linux: precomposed //macOS: decomposed @@ -43,11 +43,11 @@ Zstring replaceCpyAsciiNoCase(const Zstring& str, const Zstring& oldTerm, const //------------------------------------------------------------------------------------------ -inline bool equalNoCase(const Zstring& lhs, const Zstring& rhs) { return makeUpperCopy(lhs) == makeUpperCopy(rhs); } +inline bool equalNoCase(const Zstring& lhs, const Zstring& rhs) { return getUpperCase(lhs) == getUpperCase(rhs); } struct ZstringNoCase //use as STL container key: avoid needless upper-case conversions during std::map<>::find() { - ZstringNoCase(const Zstring& str) : upperCase(makeUpperCopy(str)) {} + ZstringNoCase(const Zstring& str) : upperCase(getUpperCase(str)) {} Zstring upperCase; std::strong_ordering operator<=>(const ZstringNoCase& other) const = default; |