summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rw-r--r--zen/base64.h2
-rw-r--r--zen/error_log.h17
-rw-r--r--zen/file_access.cpp80
-rw-r--r--zen/file_access.h16
-rw-r--r--zen/file_io.cpp14
-rw-r--r--zen/http.cpp4
-rw-r--r--zen/legacy_compiler.cpp6
-rw-r--r--zen/legacy_compiler.h3
-rw-r--r--zen/open_ssl.cpp2
-rw-r--r--zen/perf.h15
-rw-r--r--zen/recycler.cpp10
-rw-r--r--zen/scope_guard.h10
-rw-r--r--zen/serialize.h38
-rw-r--r--zen/shell_execute.cpp1
-rw-r--r--zen/stl_tools.h2
-rw-r--r--zen/string_tools.h12
-rw-r--r--zen/string_traits.h4
-rw-r--r--zen/symlink_target.h16
-rw-r--r--zen/system.cpp4
-rw-r--r--zen/zstring.cpp11
-rw-r--r--zen/zstring.h6
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
diff --git a/zen/perf.h b/zen/perf.h
index 33005e9f..2598ea76 100644
--- a/zen/perf.h
+++ b/zen/perf.h
@@ -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;
bgstack15