From b4b2e4a096fe8fe1ad530a4c181729be05834595 Mon Sep 17 00:00:00 2001 From: "B. Stack" Date: Mon, 18 Apr 2022 09:47:11 -0400 Subject: add upstream 11.20 --- zen/file_traverser.cpp | 1 - zen/globals.h | 2 + zen/http.cpp | 6 +-- zen/legacy_compiler.h | 1 + zen/open_ssl.cpp | 1 + zen/resolve_path.cpp | 1 - zen/shutdown.cpp | 41 +++++++++++++++++ zen/shutdown.h | 6 +++ zen/stl_tools.h | 15 +++++- zen/string_tools.h | 122 +++++++++++++++++++++++++++---------------------- zen/sys_info.cpp | 18 ++++---- zen/zstring.cpp | 2 +- zen/zstring.h | 2 +- 13 files changed, 148 insertions(+), 70 deletions(-) (limited to 'zen') diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index f1b5519b..515580ae 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -8,7 +8,6 @@ #include "file_error.h" - //#include //::pathconf() #include #include diff --git a/zen/globals.h b/zen/globals.h index 6a50a497..dd0dfed9 100644 --- a/zen/globals.h +++ b/zen/globals.h @@ -219,6 +219,8 @@ bool PodSpinMutex::tryLock() } + + inline void PodSpinMutex::lock() { diff --git a/zen/http.cpp b/zen/http.cpp index 24c5aa73..4fe43ede 100644 --- a/zen/http.cpp +++ b/zen/http.cpp @@ -144,7 +144,7 @@ public: statusCode_ = stringTo(statusItems[1]); - for (const std::string& line : split(headersBuf, "\r\n", SplitOnEmpty::skip)) + for (const std::string& line : split(headersBuf, '\n', SplitOnEmpty::skip)) //careful: actual line separator is "\r\n"! responseHeaders_[trimCpy(beforeFirst(line, ':', IfNotFoundReturn::all))] = /**/ trimCpy(afterFirst (line, ':', IfNotFoundReturn::none)); @@ -344,7 +344,7 @@ bool zen::internetIsAlive() //noexcept { try { - auto response = std::make_unique(Zstr("http://www.google.com/"), + auto response = std::make_unique(Zstr("https://www.google.com/"), //https more appropriate than http for testing? (different ports!) nullptr /*postParams*/, "" /*contentType*/, true /*disableGetCache*/, @@ -353,7 +353,7 @@ bool zen::internetIsAlive() //noexcept nullptr /*notifyUnbufferedIO*/); //throw SysError const int statusCode = response->getStatusCode(); - //attention: http://www.google.com/ might redirect to "https" => don't follow, just return "true"!!! + //attention: google.com might redirect to https://consent.google.com => don't follow, just return "true"!!! return statusCode / 100 == 2 || //e.g. 200 statusCode / 100 == 3; //e.g. 301, 302, 303, 307... when in doubt, consider internet alive! } diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h index 50d340ca..6c9381ee 100644 --- a/zen/legacy_compiler.h +++ b/zen/legacy_compiler.h @@ -22,6 +22,7 @@ Clang https://clang.llvm.org/cxx_status.html#cxx20 libc++ https://libcxx.llvm.org/cxx2a_status.html */ + namespace std { } diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp index 5e4c370d..99d7582e 100644 --- a/zen/open_ssl.cpp +++ b/zen/open_ssl.cpp @@ -5,6 +5,7 @@ // ***************************************************************************** #include "open_ssl.h" +#include //std::endian (needed for macOS) #include "base64.h" #include "thread.h" #include diff --git a/zen/resolve_path.cpp b/zen/resolve_path.cpp index 17d3b777..f0a49976 100644 --- a/zen/resolve_path.cpp +++ b/zen/resolve_path.cpp @@ -145,7 +145,6 @@ std::optional tryResolveMacro(const Zstring& macro) //macro without %-c if (std::optional value = getEnvironmentVar(macro)) return *value; - return {}; } diff --git a/zen/shutdown.cpp b/zen/shutdown.cpp index f46caf6b..a812d6ae 100644 --- a/zen/shutdown.cpp +++ b/zen/shutdown.cpp @@ -5,6 +5,7 @@ // ***************************************************************************** #include "shutdown.h" +#include "thread.h" #include @@ -15,6 +16,9 @@ using namespace zen; void zen::shutdownSystem() //throw FileError { + assert(runningOnMainThread()); + if (runningOnMainThread()) + onSystemShutdownRunTasks(); try { //https://linux.die.net/man/2/reboot => needs admin rights! @@ -62,3 +66,40 @@ void zen::terminateProcess(int exitCode) // alternative requiring admin: sudo killall Xorg // alternative without admin: dbus-send --session --print-reply --dest=org.gnome.SessionManager /org/gnome/SessionManager org.gnome.SessionManager.Logout uint32:1 + + +namespace +{ +using ShutdownTaskList = std::vector>>; +constinit Global globalShutdownTasks; +GLOBAL_RUN_ONCE(globalShutdownTasks.set(std::make_unique())); +} + + +void zen::onSystemShutdownRegister(const SharedRef>& task) +{ + assert(runningOnMainThread()); + + const auto& tasks = globalShutdownTasks.get(); + assert(tasks); + if (tasks) + tasks->push_back(task.ptr()); +} + + +void zen::onSystemShutdownRunTasks() +{ + assert(runningOnMainThread()); //no multithreading! else: after taskWeak.lock() task() references may go out of scope! (e.g. "this") + + const auto& tasks = globalShutdownTasks.get(); + assert(tasks); + if (tasks) + for (const std::weak_ptr>& taskWeak : *tasks) + if (const std::shared_ptr>& task = taskWeak.lock(); + task) + try + { (*task)(); } + catch (...) { assert(false); } + + globalShutdownTasks.set(nullptr); //trigger assert in onSystemShutdownRegister(), just in case... +} diff --git a/zen/shutdown.h b/zen/shutdown.h index 20354a14..b4d51f69 100644 --- a/zen/shutdown.h +++ b/zen/shutdown.h @@ -7,6 +7,7 @@ #ifndef SHUTDOWN_H_3423847870238407783265 #define SHUTDOWN_H_3423847870238407783265 +#include #include "file_error.h" @@ -15,6 +16,11 @@ namespace zen void shutdownSystem(); //throw FileError void suspendSystem(); // [[noreturn]] void terminateProcess(int exitCode); + +void onSystemShutdownRegister(const SharedRef>& task /*noexcept*/); //save important/user data! +void onSystemShutdownRegister( SharedRef>&& task) = delete; //no temporaries! shared_ptr should manage life time! +void onSystemShutdownRunTasks(); //call at appropriate time, e.g. when receiving wxEVT_QUERY_END_SESSION/wxEVT_END_SESSION +//+ also called by shutdownSystem() } #endif //SHUTDOWN_H_3423847870238407783265 diff --git a/zen/stl_tools.h b/zen/stl_tools.h index c2e8eff3..0d359641 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -290,13 +290,26 @@ Num hashArray(ByteIterator first, ByteIterator last) struct StringHash //support for custom string classes with std::unordered_set/map { + using is_transparent = int; //allow heterogenous lookup! + template size_t operator()(const String& str) const { - const auto* strFirst = strBegin(str); + const auto* const strFirst = strBegin(str); return hashArray(strFirst, strFirst + strLength(str)); } }; + +struct StringEqual +{ + using is_transparent = int; //allow heterogenous lookup! + + template + bool operator()(const String1& lhs, const String2& rhs) const + { + return equalString(lhs, rhs); + } +}; } #endif //STL_TOOLS_H_84567184321434 diff --git a/zen/string_tools.h b/zen/string_tools.h index 5f9273c9..ee4e5613 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -68,12 +68,15 @@ enum class SplitOnEmpty allow, skip }; -template std::vector split(const S& str, const T& delimiter, SplitOnEmpty soe); +template [[nodiscard]] std::vector split(const S& str, Char delimiter, SplitOnEmpty soe); +template void split2(const S& str, Function1 isDelimiter, Function2 onStringPart); -template [[nodiscard]] S trimCpy(S str, bool fromLeft = true, bool fromRight = true); +template [[nodiscard]] S trimCpy(S str, bool fromLeft = true, bool fromRight = true); +template [[nodiscard]] std::pair trimCpy(Char* first, Char* last, bool fromLeft, bool fromRight, Function trimThisChar); template void trim (S& str, bool fromLeft = true, bool fromRight = true); template void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar); + template [[nodiscard]] S replaceCpy(S str, const T& oldTerm, const U& newTerm, bool replaceAll = true); template void replace (S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true); @@ -210,7 +213,7 @@ template inline bool startsWith(const S& str, const T& prefix) { const size_t pfLen = strLength(prefix); - return strLength(str) >= pfLen && std::is_eq(impl::strcmpWithNulls(strBegin(str), strBegin(prefix), pfLen)); + return strLength(str) >= pfLen && impl::strcmpWithNulls(strBegin(str), strBegin(prefix), pfLen) == std::strong_ordering::equal; } @@ -218,7 +221,7 @@ template inline bool startsWithAsciiNoCase(const S& str, const T& prefix) { const size_t pfLen = strLength(prefix); - return strLength(str) >= pfLen && std::is_eq(impl::strcmpAsciiNoCase(strBegin(str), strBegin(prefix), pfLen)); + return strLength(str) >= pfLen && impl::strcmpAsciiNoCase(strBegin(str), strBegin(prefix), pfLen) == std::weak_ordering::equivalent; } @@ -227,7 +230,7 @@ bool endsWith(const S& str, const T& postfix) { const size_t strLen = strLength(str); const size_t pfLen = strLength(postfix); - return strLen >= pfLen && std::is_eq(impl::strcmpWithNulls(strBegin(str) + strLen - pfLen, strBegin(postfix), pfLen)); + return strLen >= pfLen && impl::strcmpWithNulls(strBegin(str) + strLen - pfLen, strBegin(postfix), pfLen) == std::strong_ordering::equal; } @@ -236,7 +239,7 @@ bool endsWithAsciiNoCase(const S& str, const T& postfix) { const size_t strLen = strLength(str); const size_t pfLen = strLength(postfix); - return strLen >= pfLen && std::is_eq(impl::strcmpAsciiNoCase(strBegin(str) + strLen - pfLen, strBegin(postfix), pfLen)); + return strLen >= pfLen && impl::strcmpAsciiNoCase(strBegin(str) + strLen - pfLen, strBegin(postfix), pfLen) == std::weak_ordering::equivalent; } @@ -244,7 +247,7 @@ template inline bool equalString(const S& lhs, const T& rhs) { const size_t lhsLen = strLength(lhs); - return lhsLen == strLength(rhs) && std::is_eq(impl::strcmpWithNulls(strBegin(lhs), strBegin(rhs), lhsLen)); + return lhsLen == strLength(rhs) && impl::strcmpWithNulls(strBegin(lhs), strBegin(rhs), lhsLen) == std::strong_ordering::equal; } @@ -252,7 +255,7 @@ template inline bool equalAsciiNoCase(const S& lhs, const T& rhs) { const size_t lhsLen = strLength(lhs); - return lhsLen == strLength(rhs) && std::is_eq(impl::strcmpAsciiNoCase(strBegin(lhs), strBegin(rhs), lhsLen)); + return lhsLen == strLength(rhs) && impl::strcmpAsciiNoCase(strBegin(lhs), strBegin(rhs), lhsLen) == std::weak_ordering::equivalent; } @@ -265,7 +268,7 @@ std::strong_ordering compareString(const S& lhs, const T& rhs) //length check *after* strcmpWithNulls(): we DO care about natural ordering: e.g. for "compareString(getUpperCase(lhs), getUpperCase(rhs))" if (const std::strong_ordering cmp = impl::strcmpWithNulls(strBegin(lhs), strBegin(rhs), std::min(lhsLen, rhsLen)); - std::is_neq(cmp)) + cmp != std::strong_ordering::equal) return cmp; return lhsLen <=> rhsLen; } @@ -279,7 +282,7 @@ std::weak_ordering compareAsciiNoCase(const S& lhs, const T& rhs) const size_t rhsLen = strLength(rhs); if (const std::weak_ordering cmp = impl::strcmpAsciiNoCase(strBegin(lhs), strBegin(rhs), std::min(lhsLen, rhsLen)); - std::is_neq(cmp)) + cmp != std::weak_ordering::equivalent) return cmp; return lhsLen <=> rhsLen; } @@ -385,37 +388,37 @@ S beforeFirst(const S& str, const T& term, IfNotFoundReturn infr) } -template inline -std::vector split(const S& str, const T& delimiter, SplitOnEmpty soe) +template inline +void split2(const S& str, Function1 isDelimiter, Function2 onStringPart) { - static_assert(std::is_same_v, GetCharTypeT>); - const size_t delimLen = strLength(delimiter); - assert(delimLen > 0); - if (delimLen == 0) + const auto* blockFirst = strBegin(str); + const auto* const strEnd = blockFirst + strLength(str); + + for (;;) { - if (str.empty() && soe == SplitOnEmpty::skip) - return {}; - return {str}; - } + const auto* const blockLast = std::find_if(blockFirst, strEnd, isDelimiter); + onStringPart(blockFirst, blockLast); + + if (blockLast == strEnd) + return; - const auto* const delimFirst = strBegin(delimiter); - const auto* const delimLast = delimFirst + delimLen; + blockFirst = blockLast + 1; + } +} - const auto* blockStart = strBegin(str); - const auto* const strLast = blockStart + strLength(str); +template inline +std::vector split(const S& str, Char delimiter, SplitOnEmpty soe) +{ + static_assert(std::is_same_v, Char>); std::vector output; - for (;;) + + split2(str, [delimiter](Char c) { return c == delimiter; }, [&](const Char* blockFirst, const Char* blockLast) { - const auto* const blockEnd = std::search(blockStart, strLast, - delimFirst, delimLast); - if (blockStart != blockEnd || soe == SplitOnEmpty::allow) - output.emplace_back(blockStart, blockEnd - blockStart); - - if (blockEnd == strLast) - return output; - blockStart = blockEnd + delimLen; - } + if (blockFirst != blockLast || soe == SplitOnEmpty::allow) + output.emplace_back(blockFirst, blockLast); + }); + return output; } @@ -484,25 +487,33 @@ void replace(S& str, const T& oldTerm, const U& newTerm, bool replaceAll) } -template inline -void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar) +template inline +std::pair trimCpy(Char* first, Char* last, bool fromLeft, bool fromRight, Function trimThisChar) { assert(fromLeft || fromRight); - const auto* const oldBegin = strBegin(str); - const auto* newBegin = oldBegin; - const auto* newEnd = oldBegin + strLength(str); - if (fromRight) - while (newBegin != newEnd && trimThisChar(newEnd[-1])) - --newEnd; + while (first != last && trimThisChar(last[-1])) + --last; if (fromLeft) - while (newBegin != newEnd && trimThisChar(*newBegin)) - ++newBegin; + while (first != last && trimThisChar(*first)) + ++first; + + return {first, last}; +} + + +template inline +void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar) +{ + assert(fromLeft || fromRight); + + const auto* const oldBegin = strBegin(str); + const auto& [newBegin, newEnd] = trimCpy(oldBegin, oldBegin + strLength(str), fromLeft, fromRight, trimThisChar); if (newBegin != oldBegin) - str = S(newBegin, newEnd - newBegin); //minor inefficiency: in case "str" is not shared, we could save an allocation and do a memory move only + str = S(newBegin, newEnd); //minor inefficiency: in case "str" is not shared, we could save an allocation and do a memory move only else str.resize(newEnd - newBegin); } @@ -613,8 +624,10 @@ S numberTo(const Num& number, std::integral_constant(buffer), strEnd, - [&](char c) { output += static_cast>(c); }); + + for (const char c : makeStringView(static_cast(buffer), strEnd)) + output += static_cast>(c); + return output; } @@ -716,8 +729,10 @@ double stringToFloat(const char* first, const char* last) inline double stringToFloat(const wchar_t* first, const wchar_t* last) { - std::string buf(last - first, '\0'); - std::transform(first, last, buf.begin(), [](wchar_t c) { return static_cast(c); }); + std::string buf; //let's rely on SSO + + for (const wchar_t c : makeStringView(first, last)) + buf += static_cast(c); return fromChars(buf.c_str(), buf.c_str() + buf.size()); } @@ -756,17 +771,16 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i } Num number = 0; - for (const CharType* it = first; it != last; ++it) - { - const CharType c = *it; + + for (const CharType c : makeStringView(first, last)) if (static_cast('0') <= c && c <= static_cast('9')) { number *= 10; number += c - static_cast('0'); } else //rest of string should contain whitespace only, it's NOT a bug if there is something else! - break; //assert(std::all_of(iter, last, isWhiteSpace)); -> this is NO assert situation - } + break; //assert(std::all_of(it, last, isWhiteSpace)); -> this is NO assert situation + return number; } diff --git a/zen/sys_info.cpp b/zen/sys_info.cpp index cc852510..d208cc98 100644 --- a/zen/sys_info.cpp +++ b/zen/sys_info.cpp @@ -26,6 +26,14 @@ using namespace zen; Zstring zen::getLoginUser() //throw FileError { + auto tryGetNonRootUser = [](const char* varName) -> const char* + { + if (const char* buf = ::getenv(varName)) //no extended error reporting + if (strLength(buf) > 0 && !equalString(buf, "root")) + return buf; + return nullptr; + }; + const uid_t userIdNo = ::getuid(); //never fails if (userIdNo != 0) //nofail; non-root @@ -54,21 +62,15 @@ Zstring zen::getLoginUser() //throw FileError return loginUser; //BUT: getlogin() can fail with ENOENT on Linux Mint: https://freefilesync.org/forum/viewtopic.php?t=8181 - auto tryGetNonRootUser = [](const char* varName) -> const char* - { - if (const char* buf = ::getenv(varName)) //no extended error reporting - if (strLength(buf) > 0 && !equalString(buf, "root")) - return buf; - return nullptr; - }; //getting a little desperate: variables used by installer.sh if (const char* userName = tryGetNonRootUser("USER")) return userName; if (const char* userName = tryGetNonRootUser("SUDO_USER")) return userName; if (const char* userName = tryGetNonRootUser("LOGNAME")) return userName; + //apparently the current user really IS root: https://freefilesync.org/forum/viewtopic.php?t=8405 + assert(getuid() == 0); return "root"; - } diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 34d52b2c..635fb47d 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -240,7 +240,7 @@ std::weak_ordering compareNatural(const Zstring& lhs, const Zstring& rhs) while (strR != strEndR && !isWhiteSpace(*strR) && !isDigit(*strR)) ++strR; if (const std::weak_ordering cmp = compareNoCaseUtf8(textBeginL, strL - textBeginL, textBeginR, strR - textBeginR); - std::is_neq(cmp)) + cmp != std::weak_ordering::equivalent) return cmp; } diff --git a/zen/zstring.h b/zen/zstring.h index b8dfb9a3..15735cb0 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -61,7 +61,7 @@ struct ZstringNoCase //use as STL container key: avoid needless upper-case conve macOS: ignore case + Unicode normalization forms */ std::weak_ordering compareNativePath(const Zstring& lhs, const Zstring& rhs); -inline bool equalNativePath(const Zstring& lhs, const Zstring& rhs) { return std::is_eq(compareNativePath(lhs, rhs)); } +inline bool equalNativePath(const Zstring& lhs, const Zstring& rhs) { return compareNativePath(lhs, rhs) == std::weak_ordering::equivalent; } struct LessNativePath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return std::is_lt(compareNativePath(lhs, rhs)); } }; -- cgit