summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorB. Stack <bgstack15@gmail.com>2023-04-03 09:56:38 -0400
committerB. Stack <bgstack15@gmail.com>2023-04-03 09:56:38 -0400
commit765e1bb3bdeb5c49f08543c20206e55c772e6b80 (patch)
tree920f288910890016e540213fe65f2d6f38aa82be /zen
parentadd upstream 12.1 (diff)
downloadFreeFileSync-12.2.tar.gz
FreeFileSync-12.2.tar.bz2
FreeFileSync-12.2.zip
add upstream 12.212.2
Diffstat (limited to 'zen')
-rw-r--r--zen/argon2.cpp2
-rw-r--r--zen/file_access.cpp205
-rw-r--r--zen/file_access.h8
-rw-r--r--zen/file_path.cpp19
-rw-r--r--zen/open_ssl.cpp2
-rw-r--r--zen/resolve_path.cpp2
-rw-r--r--zen/shutdown.cpp2
-rw-r--r--zen/socket.h38
-rw-r--r--zen/string_tools.h34
-rw-r--r--zen/symlink_target.h40
-rw-r--r--zen/sys_info.cpp12
-rw-r--r--zen/sys_version.cpp4
12 files changed, 223 insertions, 145 deletions
diff --git a/zen/argon2.cpp b/zen/argon2.cpp
index f48abe5e..1250e545 100644
--- a/zen/argon2.cpp
+++ b/zen/argon2.cpp
@@ -59,7 +59,7 @@
/* ----------------------------------------------------------------------
-/*
+ *
* A sort of 'abstract base class' or 'interface' or 'trait' which is
* the common feature of all types that want to accept data formatted
* using the SSH binary conventions of uint32, string, mpint etc.
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index ef6cdc80..c5cbf095 100644
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -8,12 +8,14 @@
#include <map>
#include <algorithm>
#include <chrono>
+#include <variant>
#include "file_traverser.h"
#include "scope_guard.h"
#include "symlink_target.h"
#include "file_io.h"
#include "crc.h"
#include "guid.h"
+#include "ring_buffer.h"
#include <sys/vfs.h> //statfs
#ifdef HAVE_SELINUX
@@ -51,72 +53,79 @@ ItemType getItemTypeImpl(const Zstring& itemPath) //throw SysErrorCode
return ItemType::folder;
return ItemType::file; //S_ISREG || S_ISCHR || S_ISBLK || S_ISFIFO || S_ISSOCK
}
-}
-ItemType zen::getItemType(const Zstring& itemPath) //throw FileError
+std::variant<ItemType, Zstring /*last existing parent path*/> getItemTypeIfExistsImpl(const Zstring& itemPath) //throw SysError
{
try
{
+ //fast check: 1. perf 2. expected by getFolderStatusNonBlocking()
return getItemTypeImpl(itemPath); //throw SysErrorCode
}
- catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), e.toString()); }
-}
-
-
-std::variant<ItemType, Zstring /*last existing parent path*/> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError
-{
- try
+ catch (const SysErrorCode& e) //let's dig deeper, but *only* if error code sounds like "not existing"
{
- try
- {
- //fast check: 1. perf 2. expected by getFolderStatusNonBlocking()
- return getItemTypeImpl(itemPath); //throw SysErrorCode
- }
- catch (const SysErrorCode& e) //let's dig deeper, but *only* if error code sounds like "not existing"
+ const std::optional<Zstring> parentPath = getParentFolderPath(itemPath);
+ if (!parentPath) //device root => quick access test
+ throw;
+ if (e.errorCode == ENOENT)
{
- const std::optional<Zstring> parentPath = getParentFolderPath(itemPath);
- if (!parentPath) //device root => quick access test
- throw;
- if (e.errorCode == ENOENT)
- {
- const std::variant<ItemType, Zstring /*last existing parent path*/> parentTypeOrPath = getItemTypeIfExists(*parentPath); //throw FileError
+ const std::variant<ItemType, Zstring /*last existing parent path*/> parentTypeOrPath = getItemTypeIfExistsImpl(*parentPath); //throw SysError
- if (const ItemType* parentType = std::get_if<ItemType>(&parentTypeOrPath))
- {
- if (*parentType == ItemType::file /*obscure, but possible*/)
- throw SysError(replaceCpy(_("The name %x is already used by another item."), L"%x", fmtPath(getItemName(*parentPath))));
+ if (const ItemType* parentType = std::get_if<ItemType>(&parentTypeOrPath))
+ {
+ if (*parentType == ItemType::file /*obscure, but possible*/)
+ throw SysError(replaceCpy(_("The name %x is already used by another item."), L"%x", fmtPath(getItemName(*parentPath))));
- const Zstring itemName = getItemName(itemPath);
- assert(!itemName.empty());
+ const Zstring itemName = getItemName(itemPath);
+ assert(!itemName.empty());
+ try
+ {
traverseFolder(*parentPath, //throw FileError
[&](const FileInfo& fi) { if (fi.itemName == itemName) throw SysError(_("Temporary access error:") + L' ' + e.toString()); },
[&](const FolderInfo& fi) { if (fi.itemName == itemName) throw SysError(_("Temporary access error:") + L' ' + e.toString()); },
[&](const SymlinkInfo& si) { if (si.itemName == itemName) throw SysError(_("Temporary access error:") + L' ' + e.toString()); });
//- case-sensitive comparison! itemPath must be normalized!
//- finding the item after getItemType() previously failed is exceptional
-
- return *parentPath;
}
- else
- return parentTypeOrPath;
+ catch (const FileError& e) { throw SysError(replaceCpy(e.toString(), L"\n\n", L'\n')); }
+
+ return *parentPath;
}
else
- throw;
+ return parentTypeOrPath;
}
+ else
+ throw;
}
- catch (const SysError& e)
+}
+}
+
+
+ItemType zen::getItemType(const Zstring& itemPath) //throw FileError
+{
+ try
{
- throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), e.toString());
+ return getItemTypeImpl(itemPath); //throw SysErrorCode
}
+ catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), e.toString()); }
}
-bool zen::itemExists(const Zstring& itemPath) //throw FileError
+std::optional<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError
{
- const std::variant<ItemType, Zstring /*last existing parent path*/> typeOrPath = getItemTypeIfExists(itemPath); //throw FileError
- return std::get_if<ItemType>(&typeOrPath);
+ try
+ {
+ const std::variant<ItemType, Zstring /*last existing parent path*/> typeOrPath = getItemTypeIfExistsImpl(itemPath); //throw SysError
+ if (const ItemType* type = std::get_if<ItemType>(&typeOrPath))
+ return *type;
+ else
+ return std::nullopt;
+ }
+ catch (const SysError& e)
+ {
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), e.toString());
+ }
}
@@ -130,16 +139,16 @@ namespace
//- folderPath does not need to exist (yet)
int64_t zen::getFreeDiskSpace(const Zstring& folderPath) //throw FileError
{
- const Zstring existingPath = [&]
- {
- const std::variant<ItemType, Zstring /*last existing parent path*/> typeOrPath = getItemTypeIfExists(folderPath); //throw FileError
- if (std::get_if<ItemType>(&typeOrPath))
- return folderPath;
- else
- return std::get<Zstring>(typeOrPath);
- }();
try
{
+ const Zstring existingPath = [&]
+ {
+ const std::variant<ItemType, Zstring /*last existing parent path*/> typeOrPath = getItemTypeIfExistsImpl(folderPath); //throw SysError
+ if (std::get_if<ItemType>(&typeOrPath))
+ return folderPath;
+ else
+ return std::get<Zstring>(typeOrPath);
+ }();
struct statfs info = {};
if (::statfs(existingPath.c_str(), &info) != 0) //follows symlinks!
THROW_LAST_SYS_ERROR("statfs");
@@ -253,10 +262,14 @@ void removeDirectoryImpl(const Zstring& folderPath) //throw FileError
void zen::removeDirectoryPlainRecursion(const Zstring& dirPath) //throw FileError
{
- if (getItemType(dirPath) == ItemType::symlink) //throw FileError
- removeSymlinkPlain(dirPath); //throw FileError
- else
- removeDirectoryImpl(dirPath); //throw FileError
+ try
+ {
+ if (getItemTypeImpl(dirPath) == ItemType::symlink) //throw SysErrorCode
+ removeSymlinkPlain(dirPath); //throw FileError
+ else
+ removeDirectoryImpl(dirPath); //throw FileError
+ }
+ catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), e.toString()); }
}
@@ -354,16 +367,15 @@ void setWriteTimeNative(const Zstring& itemPath, const timespec& modTime, ProcSy
//https://freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works (but not for gvfs SFTP)
if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, procSl == ProcSymlink::asLink ? AT_SYMLINK_NOFOLLOW : 0) == 0)
return;
+ const ErrorCode ecUtimensat = errno;
try
{
if (procSl == ProcSymlink::asLink)
- try
- {
- if (getItemType(itemPath) == ItemType::symlink) //throw FileError
- THROW_LAST_SYS_ERROR("utimensat(AT_SYMLINK_NOFOLLOW)"); //use lutimes()? just a wrapper around utimensat()!
- //else: fall back
- }
- catch (const FileError& e) { throw SysError(e.toString()); }
+ {
+ if (getItemTypeImpl(itemPath) == ItemType::symlink) //throw SysErrorCode
+ throw SysError(formatSystemError("utimensat(AT_SYMLINK_NOFOLLOW)", ecUtimensat)); //use lutimes()? just a wrapper around utimensat()!
+ //else: fall back
+ }
//in other cases utimensat() returns EINVAL for CIFS/NTFS drives, but open+futimens works: https://freefilesync.org/forum/viewtopic.php?t=387
//2017-07-04: O_WRONLY | O_APPEND seems to avoid EOPNOTSUPP on gvfs SFTP!
@@ -475,10 +487,14 @@ void zen::copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPa
if (::lchown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights!
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), "lchown");
- const bool isSymlinkTarget = getItemType(targetPath) == ItemType::symlink; //throw FileError
- if (!isSymlinkTarget && //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod()
- ::chmod(targetPath.c_str(), fileInfo.st_mode) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), "chmod");
+ try
+ {
+ if (getItemTypeImpl(targetPath) != ItemType::symlink && //throw SysErrorCode
+ //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod()
+ ::chmod(targetPath.c_str(), fileInfo.st_mode) != 0)
+ THROW_LAST_SYS_ERROR("chmod");
+ }
+ catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), e.toString()); }
}
}
@@ -517,29 +533,47 @@ void zen::createDirectory(const Zstring& dirPath) //throw FileError, ErrorTarget
void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw FileError
{
- //expect that path already exists (see: versioning, base folder, log file path) => check first
- const std::variant<ItemType, Zstring /*last existing parent path*/> typeOrPath = getItemTypeIfExists(dirPath); //throw FileError
-
- if (const ItemType* type = std::get_if<ItemType>(&typeOrPath))
+ auto getItemType2 = [&](const Zstring& itemPath) //throw FileError
{
- if (*type == ItemType::file /*obscure, but possible*/)
+ try
+ { return getItemTypeImpl(itemPath); } //throw SysErrorCode
+ catch (const SysErrorCode& e) //need to add context!
+ {
throw FileError(replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(dirPath)),
- replaceCpy(_("The name %x is already used by another item."), L"%x", fmtPath(getItemName(dirPath))));
- }
- else
- {
- const Zstring existingDirPath = std::get<Zstring>(typeOrPath);
- assert(startsWith(dirPath, existingDirPath));
+ replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(getParentFolderPath(itemPath) ? getItemName(itemPath) : itemPath)) + L'\n' +
+ e.toString());
+ }
+ };
- const ZstringView relPath = makeStringView(dirPath.begin() + existingDirPath.size(), dirPath.end());
- const std::vector<ZstringView> namesMissing = splitCpy(relPath, FILE_NAME_SEPARATOR, SplitOnEmpty::skip);
- assert(!namesMissing.empty());
+ try
+ {
+ //- path most likely already exists (see: versioning, base folder, log file path) => check first
+ //- do NOT use getItemTypeIfExists()! race condition when multiple threads are calling createDirectoryIfMissingRecursion(): https://freefilesync.org/forum/viewtopic.php?t=10137#p38062
+ //- find first existing + accessible parent folder (backwards iteration):
+ Zstring dirPathEx = dirPath;
+ RingBuffer<Zstring> dirNames; //caveat: 1. might have been created in the meantime 2. getItemType2() may have failed with access error
+ for (;;)
+ try
+ {
+ if (getItemType2(dirPathEx) == ItemType::file /*obscure, but possible*/) //throw FileError
+ throw SysError(replaceCpy(_("The name %x is already used by another item."), L"%x", fmtPath(getItemName(dirPathEx))));
+ break;
+ }
+ catch (FileError&) //not yet existing or access error
+ {
+ const std::optional<Zstring> parentPath = getParentFolderPath(dirPathEx);
+ if (!parentPath)//device root => quick access test
+ throw;
+ dirNames.push_front(getItemName(dirPathEx));
+ dirPathEx = *parentPath;
+ }
+ //-----------------------------------------------------------
- Zstring dirPathNew = existingDirPath;
- for (const ZstringView folderName : namesMissing)
+ Zstring dirPathNew = dirPathEx;
+ for (const Zstring& dirName : dirNames)
try
{
- dirPathNew = appendPath(dirPathNew, Zstring(folderName));
+ dirPathNew = appendPath(dirPathNew, dirName);
createDirectory(dirPathNew); //throw FileError
}
@@ -547,18 +581,24 @@ void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw File
{
try
{
- if (getItemType(dirPathNew) != ItemType::file /*obscure, but possible*/) //throw FileError
- continue; //already existing => possible, if createFolderIfMissingRecursion() is run in parallel
+ if (getItemType2(dirPathNew) == ItemType::file /*obscure, but possible*/) //throw FileError
+ throw SysError(replaceCpy(_("The name %x is already used by another item."), L"%x", fmtPath(getItemName(dirPathNew))));
+ else
+ continue; //already existing => possible, if createDirectoryIfMissingRecursion() is run in parallel
}
catch (FileError&) {} //not yet existing or access error
throw;
}
}
+ catch (const SysError& e)
+ {
+ throw FileError(replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(dirPath)), e.toString());
+ }
}
-void zen::tryCopyDirectoryAttributes(const Zstring& sourcePath, const Zstring& targetPath) //throw FileError
+void zen::copyDirectoryAttributes(const Zstring& sourcePath, const Zstring& targetPath) //throw FileError
{
//do NOT copy attributes for volume root paths which return as: FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY
//https://freefilesync.org/forum/viewtopic.php?t=5550
@@ -570,10 +610,11 @@ void zen::tryCopyDirectoryAttributes(const Zstring& sourcePath, const Zstring& t
void zen::copySymlink(const Zstring& sourcePath, const Zstring& targetPath) //throw FileError
{
- const SymlinkRawContent linkContent = getSymlinkRawContent(sourcePath); //throw FileError; accept broken symlinks
-
+ SymlinkRawContent linkContent{};
try //harmonize with NativeFileSystem::equalSymlinkContentForSameAfsType()
{
+ linkContent = getSymlinkRawContent_impl(sourcePath); //throw SysError; accept broken symlinks
+
if (::symlink(linkContent.targetPath.c_str(), targetPath.c_str()) != 0)
THROW_LAST_SYS_ERROR("symlink");
}
diff --git a/zen/file_access.h b/zen/file_access.h
index 5af8b879..f6ac3740 100644
--- a/zen/file_access.h
+++ b/zen/file_access.h
@@ -8,7 +8,6 @@
#define FILE_ACCESS_H_8017341345614857
#include <functional>
-#include <variant>
#include "file_path.h"
#include "file_error.h"
#include "serialize.h" //IoCallback
@@ -39,9 +38,9 @@ ItemType getItemType(const Zstring& itemPath); //throw FileError
//execute potentially SLOW folder traversal but distinguish error/not existing:
// - all child item path parts must correspond to folder traversal
// => we can conclude whether an item is *not* existing anymore by doing a *case-sensitive* name search => potentially SLOW!
-std::variant<ItemType, Zstring /*last existing parent path*/> getItemTypeIfExists(const Zstring& itemPath); //throw FileError
+std::optional<ItemType> getItemTypeIfExists(const Zstring& itemPath); //throw FileError
-bool itemExists(const Zstring& itemPath); //throw FileError
+inline bool itemExists(const Zstring& itemPath) { return static_cast<bool>(getItemTypeIfExists(itemPath)); } //throw FileError
enum class ProcSymlink
{
@@ -80,8 +79,7 @@ void createDirectoryIfMissingRecursion(const Zstring& dirPath); //throw FileErro
//symlink handling: follow
//expects existing source/target directories
-//reports "note-worthy" errors only
-void tryCopyDirectoryAttributes(const Zstring& sourcePath, const Zstring& targetPath); //throw FileError
+void copyDirectoryAttributes(const Zstring& sourcePath, const Zstring& targetPath); //throw FileError
void copySymlink(const Zstring& sourcePath, const Zstring& targetPath); //throw FileError
diff --git a/zen/file_path.cpp b/zen/file_path.cpp
index 7ef78569..c9d6f236 100644
--- a/zen/file_path.cpp
+++ b/zen/file_path.cpp
@@ -23,7 +23,7 @@ std::optional<PathComponents> zen::parsePathComponents(const Zstring& itemPath)
Zstring rootPath(itemPathPf.begin(), rootWithSep ? it + 1 : it);
Zstring relPath(it + 1, itemPathPf.end());
- trim(relPath, true, true, [](Zchar c) { return c == FILE_NAME_SEPARATOR; });
+ trim(relPath, TrimSide::both, [](Zchar c) { return c == FILE_NAME_SEPARATOR; });
return PathComponents{std::move(rootPath), std::move(relPath)};
}
@@ -203,24 +203,11 @@ std::optional<Zstring> zen::getEnvironmentVar(const ZstringView name)
envVars = globalEnvVars.get();
}
- auto it = envVars->find(name);
+ const auto it = envVars->find(name);
if (it == envVars->end())
return {};
- Zstring value = it->second;
-
- //some postprocessing (good idea!? Is this even needed!?
- warn_static("let's find out!")
-#if 0
- trim(value); //remove leading, trailing blanks
-
- //remove leading, trailing double-quotes
- if (startsWith(value, Zstr('"')) &&
- endsWith (value, Zstr('"')) &&
- value.length() >= 2)
- value = Zstring(value.c_str() + 1, value.length() - 2);
-#endif
- return value;
+ return it->second;
}
diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp
index af4306b2..5cfd7f12 100644
--- a/zen/open_ssl.cpp
+++ b/zen/open_ssl.cpp
@@ -387,7 +387,7 @@ void zen::verifySignature(const std::string_view message, const std::string_view
bool zen::isPuttyKeyStream(const std::string_view keyStream)
{
- return startsWith(trimCpy(keyStream, true, false), "PuTTY-User-Key-File-");
+ return startsWith(trimCpy(keyStream, TrimSide::left), "PuTTY-User-Key-File-");
}
diff --git a/zen/resolve_path.cpp b/zen/resolve_path.cpp
index daaf91ff..7bf50b12 100644
--- a/zen/resolve_path.cpp
+++ b/zen/resolve_path.cpp
@@ -149,7 +149,7 @@ namespace
Zstring tryExpandVolumeName(Zstring pathPhrase) // [volname]:\folder [volname]\folder [volname]folder -> C:\folder
{
//we only expect the [.*] pattern at the beginning => do not touch dir names like "C:\somedir\[stuff]"
- trim(pathPhrase, true, false);
+ trim(pathPhrase, TrimSide::left);
if (startsWith(pathPhrase, Zstr('[')))
{
diff --git a/zen/shutdown.cpp b/zen/shutdown.cpp
index e64e1e70..ee68b467 100644
--- a/zen/shutdown.cpp
+++ b/zen/shutdown.cpp
@@ -55,7 +55,7 @@ void zen::terminateProcess(int exitCode)
for (;;) //why still here?? => crash deliberately!
- *reinterpret_cast<volatile int*>(0) = 0; //crude but at least we'll get crash dumps if it ever happens
+ *reinterpret_cast<volatile int*>(0) = 0; //crude but at least we'll get crash dumps *if* it ever happens
}
diff --git a/zen/socket.h b/zen/socket.h
index 4ccde190..f706daab 100644
--- a/zen/socket.h
+++ b/zen/socket.h
@@ -19,6 +19,42 @@ namespace zen
do { const ErrorCode ecInternal = getLastError(); throw SysError(formatSystemError(functionName, ecInternal)); } while (false)
+#define THROW_LAST_SYS_ERROR_GAI(rcGai) \
+ do { \
+ if (rcGai == EAI_SYSTEM) /*"check errno for details"*/ \
+ THROW_LAST_SYS_ERROR("getaddrinfo"); \
+ \
+ throw SysError(formatSystemError("getaddrinfo", formatGaiErrorCode(rcGai), utfTo<std::wstring>(::gai_strerror(rcGai)))); \
+ } while (false)
+
+inline
+std::wstring formatGaiErrorCode(int ec)
+{
+ switch (ec) //codes used on both Linux and macOS
+ {
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_ADDRFAMILY);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_AGAIN);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_BADFLAGS);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_FAIL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_FAMILY);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_MEMORY);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_NODATA);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_NONAME);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_SERVICE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_SOCKTYPE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_SYSTEM);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_OVERFLOW);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_INPROGRESS);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_CANCELED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_NOTCANCELED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_ALLDONE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_INTR);
+ ZEN_CHECK_CASE_FOR_CONSTANT(EAI_IDN_ENCODE);
+ default:
+ return replaceCpy(_("Error code %x"), L"%x", numberTo<std::wstring>(ec));
+ }
+}
+
//patch up socket portability:
using SocketType = int;
const SocketType invalidSocket = -1;
@@ -53,7 +89,7 @@ public:
const int rcGai = ::getaddrinfo(server.c_str(), serviceName.c_str(), &hints, &servinfo);
if (rcGai != 0)
- throw SysError(formatSystemError("getaddrinfo", replaceCpy(_("Error code %x"), L"%x", numberTo<std::wstring>(rcGai)), utfTo<std::wstring>(::gai_strerror(rcGai))));
+ THROW_LAST_SYS_ERROR_GAI(rcGai);
if (!servinfo)
throw SysError(formatSystemError("getaddrinfo", L"", L"Empty server info."));
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 03563d41..1cd8ef0d 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -73,9 +73,15 @@ template <class S, class Char, class Function> void split(const S& str, Char del
template <class S, class Function1, class Function2> void split2(const S& str, Function1 isDelimiter, Function2 onStringPart);
template <class S, class Char> [[nodiscard]] std::vector<S> splitCpy(const S& str, Char delimiter, SplitOnEmpty soe);
-template <class S> [[nodiscard]] S trimCpy(const S& str, bool fromLeft = true, bool fromRight = true);
-template <class S> void trim (S& str, bool fromLeft = true, bool fromRight = true);
-template <class S, class Function> void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar);
+enum class TrimSide
+{
+ both,
+ left,
+ right,
+};
+template <class S> [[nodiscard]] S trimCpy(const S& str, TrimSide side = TrimSide::both);
+template <class S> void trim(S& str, TrimSide side = TrimSide::both);
+template <class S, class Function> void trim(S& str, TrimSide side, Function trimThisChar);
template <class S, class T, class U> [[nodiscard]] S replaceCpy(S str, const T& oldTerm, const U& newTerm);
@@ -540,15 +546,13 @@ S replaceCpyAsciiNoCase(S str, const T& oldTerm, const U& newTerm)
template <class Char, class Function>
[[nodiscard]] inline
-std::pair<Char*, Char*> trimCpy2(Char* first, Char* last, bool fromLeft, bool fromRight, Function trimThisChar)
+std::pair<Char*, Char*> trimCpy2(Char* first, Char* last, TrimSide side, Function trimThisChar)
{
- assert(fromLeft || fromRight);
-
- if (fromRight)
+ if (side == TrimSide::right || side == TrimSide::both)
while (first != last && trimThisChar(last[-1]))
--last;
- if (fromLeft)
+ if (side == TrimSide::left || side == TrimSide::both)
while (first != last && trimThisChar(*first))
++first;
@@ -557,12 +561,10 @@ std::pair<Char*, Char*> trimCpy2(Char* first, Char* last, bool fromLeft, bool fr
template <class S, class Function> inline
-void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar)
+void trim(S& str, TrimSide side, Function trimThisChar)
{
- assert(fromLeft || fromRight);
-
const auto* const oldBegin = strBegin(str);
- const auto& [newBegin, newEnd] = trimCpy2(oldBegin, oldBegin + strLength(str), fromLeft, fromRight, trimThisChar);
+ const auto [newBegin, newEnd] = trimCpy2(oldBegin, oldBegin + strLength(str), side, trimThisChar);
if (newBegin != oldBegin)
str = S(newBegin, newEnd); //minor inefficiency: in case "str" is not shared, we could save an allocation and do a memory move only
@@ -572,21 +574,21 @@ void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar)
template <class S> inline
-void trim(S& str, bool fromLeft, bool fromRight)
+void trim(S& str, TrimSide side)
{
using CharType = GetCharTypeT<S>;
- trim(str, fromLeft, fromRight, [](CharType c) { return isWhiteSpace(c); });
+ trim(str, side, [](CharType c) { return isWhiteSpace(c); });
}
template <class S> inline
-S trimCpy(const S& str, bool fromLeft, bool fromRight)
+S trimCpy(const S& str, TrimSide side)
{
using CharType = GetCharTypeT<S>;
const auto* const oldBegin = strBegin(str);
const auto* const oldEnd = oldBegin + strLength(str);
- const auto& [newBegin, newEnd] = trimCpy2(oldBegin, oldEnd, fromLeft, fromRight, [](CharType c) { return isWhiteSpace(c); });
+ const auto [newBegin, newEnd] = trimCpy2(oldBegin, oldEnd, side, [](CharType c) { return isWhiteSpace(c); });
if (newBegin == oldBegin && newEnd == oldEnd)
return str;
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index 89c00571..083360af 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -38,7 +38,7 @@ Zstring getSymlinkResolvedPath(const Zstring& linkPath); //throw FileError
namespace
{
//retrieve raw target data of symlink or junction
-zen::SymlinkRawContent getSymlinkRawContent_impl(const Zstring& linkPath) //throw FileError
+zen::SymlinkRawContent getSymlinkRawContent_impl(const Zstring& linkPath) //throw SysError
{
using namespace zen;
const size_t bufSize = 10000;
@@ -46,26 +46,22 @@ zen::SymlinkRawContent getSymlinkRawContent_impl(const Zstring& linkPath) //thro
const ssize_t bytesWritten = ::readlink(linkPath.c_str(), buf.data(), bufSize);
if (bytesWritten < 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), "readlink");
+ THROW_LAST_SYS_ERROR("readlink");
if (makeUnsigned(bytesWritten) >= bufSize) //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."));
+ throw SysError(formatSystemError("readlink", L"", L"Buffer truncated."));
return {.targetPath = Zstring(buf.data(), bytesWritten)}; //readlink does not append 0-termination!
}
-Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError
+Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw SysError
{
using namespace zen;
- try
- {
- char* targetPath = ::realpath(linkPath.c_str(), nullptr);
- if (!targetPath)
- THROW_LAST_SYS_ERROR("realpath");
- ZEN_ON_SCOPE_EXIT(::free(targetPath));
- return targetPath;
- }
- catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), e.toString()); }
+ char* targetPath = ::realpath(linkPath.c_str(), nullptr);
+ if (!targetPath)
+ THROW_LAST_SYS_ERROR("realpath");
+ ZEN_ON_SCOPE_EXIT(::free(targetPath));
+ return targetPath;
}
}
@@ -73,11 +69,25 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError
namespace zen
{
inline
-SymlinkRawContent getSymlinkRawContent(const Zstring& linkPath) { return getSymlinkRawContent_impl(linkPath); } //throw FileError
+SymlinkRawContent getSymlinkRawContent(const Zstring& linkPath)
+{
+ try
+ {
+ return getSymlinkRawContent_impl(linkPath); //throw SysError
+ }
+ catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), e.toString()); }
+}
inline
-Zstring getSymlinkResolvedPath(const Zstring& linkPath) { return getResolvedSymlinkPath_impl(linkPath); } //throw FileError
+Zstring getSymlinkResolvedPath(const Zstring& linkPath)
+{
+ try
+ {
+ return getResolvedSymlinkPath_impl(linkPath); //throw SysError
+ }
+ catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), e.toString()); }
+}
}
diff --git a/zen/sys_info.cpp b/zen/sys_info.cpp
index 55465711..2a32d247 100644
--- a/zen/sys_info.cpp
+++ b/zen/sys_info.cpp
@@ -131,8 +131,8 @@ ComputerModel zen::getComputerModel() //throw FileError
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
- trim(cm.model, false, true, [](wchar_t c) { return c == L'_'; }); //e.g. "CBX3___" or just "_"
- trim(cm.vendor, false, true, [](wchar_t c) { return c == L'_'; }); //e.g. "DELL__" or just "_"
+ trim(cm.model, TrimSide::right, [](wchar_t c) { return c == L'_'; }); //e.g. "CBX3___" or just "_"
+ trim(cm.vendor, TrimSide::right, [](wchar_t c) { return c == L'_'; }); //e.g. "DELL__" or just "_"
for (const char* dummyModel :
{
@@ -209,9 +209,13 @@ std::wstring zen::getOsDescription() //throw FileError
Zstring zen::getRealProcessPath() //throw FileError
{
- return getSymlinkRawContent("/proc/self/exe").targetPath; //throw FileError
- //path does not contain symlinks => no need for ::realpath()
+ try
+ {
+ return getSymlinkRawContent_impl("/proc/self/exe").targetPath; //throw SysError
+ //path does not contain symlinks => no need for ::realpath()
+ }
+ catch (const SysError& e) { throw FileError(_("Cannot get process information."), e.toString()); }
}
diff --git a/zen/sys_version.cpp b/zen/sys_version.cpp
index b123ba07..e57c9b69 100644
--- a/zen/sys_version.cpp
+++ b/zen/sys_version.cpp
@@ -55,8 +55,8 @@ OsVersionDetail zen::getOsVersionDetail() //throw SysError
osVersion = utfTo<std::wstring>(afterFirst(line, '=', IfNotFoundReturn::none));
//PRETTY_NAME? too wordy! e.g. "Fedora 17 (Beefy Miracle)"
});
- trim(osName, true, true, [](char c) { return c == L'"' || c == L'\''; });
- trim(osVersion, true, true, [](char c) { return c == L'"' || c == L'\''; });
+ trim(osName, TrimSide::both, [](char c) { return c == L'"' || c == L'\''; });
+ trim(osVersion, TrimSide::both, [](char c) { return c == L'"' || c == L'\''; });
}
if (osName.empty())
bgstack15