diff options
Diffstat (limited to 'zen')
-rw-r--r-- | zen/argon2.cpp | 2 | ||||
-rw-r--r-- | zen/file_access.cpp | 205 | ||||
-rw-r--r-- | zen/file_access.h | 8 | ||||
-rw-r--r-- | zen/file_path.cpp | 19 | ||||
-rw-r--r-- | zen/open_ssl.cpp | 2 | ||||
-rw-r--r-- | zen/resolve_path.cpp | 2 | ||||
-rw-r--r-- | zen/shutdown.cpp | 2 | ||||
-rw-r--r-- | zen/socket.h | 38 | ||||
-rw-r--r-- | zen/string_tools.h | 34 | ||||
-rw-r--r-- | zen/symlink_target.h | 40 | ||||
-rw-r--r-- | zen/sys_info.cpp | 12 | ||||
-rw-r--r-- | zen/sys_version.cpp | 4 |
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()) |