diff options
Diffstat (limited to 'zen')
-rw-r--r-- | zen/error_log.h | 43 | ||||
-rw-r--r-- | zen/file_path.cpp | 2 | ||||
-rw-r--r-- | zen/json.h | 9 | ||||
-rw-r--r-- | zen/resolve_path.cpp | 30 | ||||
-rw-r--r-- | zen/stl_tools.h | 5 | ||||
-rw-r--r-- | zen/sys_info.cpp | 85 | ||||
-rw-r--r-- | zen/sys_info.h | 1 | ||||
-rw-r--r-- | zen/type_traits.h | 2 |
8 files changed, 92 insertions, 85 deletions
diff --git a/zen/error_log.h b/zen/error_log.h index 357232f3..ff630cb8 100644 --- a/zen/error_log.h +++ b/zen/error_log.h @@ -32,30 +32,17 @@ struct LogEntry std::string formatMessage(const LogEntry& entry); +using ErrorLog = std::vector<LogEntry>; -class ErrorLog -{ -public: - void logMsg(const std::wstring& msg, MessageType type, time_t time = std::time(nullptr)); +void logMsg(ErrorLog& log, const std::wstring& msg, MessageType type, time_t time = std::time(nullptr)); - struct Stats - { - int info = 0; - int warning = 0; - int error = 0; - }; - Stats getStats() const; - - //subset of std::vector<> interface: - using const_iterator = std::vector<LogEntry>::const_iterator; - const_iterator begin() const { return entries_.begin(); } - const_iterator end () const { return entries_.end (); } - bool empty() const { return entries_.empty(); } - -private: - std::vector<LogEntry> entries_; +struct ErrorLogStats +{ + int info = 0; + int warning = 0; + int error = 0; }; - +ErrorLogStats getStats(const ErrorLog& log); @@ -65,18 +52,17 @@ private: //######################## implementation ########################## inline -void ErrorLog::logMsg(const std::wstring& msg, MessageType type, time_t time) +void logMsg(ErrorLog& log, const std::wstring& msg, MessageType type, time_t time) { - entries_.push_back({time, type, utfTo<Zstringc>(msg)}); + log.push_back({time, type, utfTo<Zstringc>(msg)}); } - inline -ErrorLog::Stats ErrorLog::getStats() const +ErrorLogStats getStats(const ErrorLog& log) { - Stats count; - for (const LogEntry& entry : entries_) + ErrorLogStats count; + for (const LogEntry& entry : log) switch (entry.type) { case MSG_TYPE_INFO: @@ -89,7 +75,7 @@ ErrorLog::Stats ErrorLog::getStats() const ++count.error; break; } - assert(std::ssize(entries_) == count.info + count.warning + count.error); + assert(std::ssize(log) == count.info + count.warning + count.error); return count; } @@ -119,6 +105,7 @@ std::string formatMessage(const LogEntry& entry) const Zstringc msg = trimCpy(entry.message); static_assert(std::is_same_v<decltype(msg), const Zstringc>, "no worries about copying as long as we're using a ref-counted string!"); + assert(msg == entry.message); //trimming shouldn't be needed usually!? for (auto it = msg.begin(); it != msg.end(); ) if (*it == '\n') diff --git a/zen/file_path.cpp b/zen/file_path.cpp index 926b5c89..68137b90 100644 --- a/zen/file_path.cpp +++ b/zen/file_path.cpp @@ -35,7 +35,7 @@ std::optional<PathComponents> zen::parsePathComponents(const Zstring& itemPath) pc = doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/); if (!pc && startsWith(itemPath, "/media/")) //Ubuntu: e.g. /media/zenju/DEVICE_NAME - if (const char* username = ::getenv("USER")) + if (const char* username = ::getenv("USER")) //no ownership transfer + no extended error reporting if (startsWith(itemPath, std::string("/media/") + username + "/")) pc = doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/); @@ -22,8 +22,8 @@ struct JsonValue boolean, //primitive types number, // string, // - object, array, + object, }; /**/ JsonValue() {} @@ -40,9 +40,10 @@ struct JsonValue Type type = Type::null; - std::string primVal; //for primitive types - std::unordered_map<std::string, JsonValue> objectVal; //"[...] most implementations of JSON libraries do not accept duplicate keys [...]" => fine! - std::vector<JsonValue> arrayVal; + std::string primVal; //for primitive types + std::vector<JsonValue> arrayVal; + std::map<std::string, JsonValue> objectVal; //"[...] most implementations of JSON libraries do not accept duplicate keys [...]" => fine! + //alternative: std::unordered_map => but let's keep std::map, so that objectVal is sorted for our unit tests }; diff --git a/zen/resolve_path.cpp b/zen/resolve_path.cpp index 2b1a82d3..357dab6a 100644 --- a/zen/resolve_path.cpp +++ b/zen/resolve_path.cpp @@ -9,8 +9,10 @@ #include "thread.h" #include "file_access.h" - #include <stdlib.h> //getenv() - #include <unistd.h> //getcwd() +#include <zen/sys_info.h> + // #include <stdlib.h> //getenv() + #include <unistd.h> //getuid() + #include <pwd.h> //getpwuid_r() using namespace zen; @@ -21,7 +23,7 @@ std::optional<Zstring> getEnvironmentVar(const Zchar* name) { assert(runningOnMainThread()); //getenv() is not thread-safe! - const char* buffer = ::getenv(name); //no extended error reporting + const char* buffer = ::getenv(name); //no ownership transfer + no extended error reporting if (!buffer) return {}; Zstring value(buffer); @@ -58,20 +60,19 @@ Zstring resolveRelativePath(const Zstring& relativePath) if (!startsWith(pathTmp, FILE_NAME_SEPARATOR)) //absolute names are exactly those starting with a '/' { /* basic support for '~': strictly speaking this is a shell-layer feature, so "realpath()" won't handle it - https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html - - https://linux.die.net/man/3/getpwuid: An application that wants to determine its user's home directory - should inspect the value of HOME (rather than the value getpwuid(getuid())->pw_dir) since this allows - the user to modify their notion of "the home directory" during a login session. */ + https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html */ if (startsWith(pathTmp, "~/") || pathTmp == "~") { - if (const std::optional<Zstring> homeDir = getEnvironmentVar("HOME")) - { + try + { + const Zstring& homePath = getUserHome(); //throw FileError + if (startsWith(pathTmp, "~/")) - pathTmp = appendPath(*homeDir, pathTmp.c_str() + 2); + pathTmp = appendPath(homePath, pathTmp.c_str() + 2); else //pathTmp == "~" - pathTmp = *homeDir; - } + pathTmp = homePath; + } + catch (FileError&) {} //else: error! no further processing! } else @@ -238,8 +239,7 @@ Zstring zen::getResolvedFilePath(const Zstring& pathPhrase) //noexcept path = expandMacros(path); //expand before trimming! - //remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix() - trim(path); //attention: don't remove all whitespace from right, e.g. 0xa0 may be used as part of a folder name + trim(path); //remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix() { path = expandVolumeName(path); //may block for slow USB sticks and idle HDDs! diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 9f7977db..2726a09d 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -78,11 +78,7 @@ template <class T> inline T* get( std::optional<T>& opt) { return opt template <class T> inline const T* get(const std::optional<T>& opt) { return opt ? &*opt : nullptr; } - //=========================================================================== -template <class T> class SharedRef; -template <class T, class... Args> SharedRef<T> makeSharedRef(Args&& ... args); - template <class T> class SharedRef //why is there no std::shared_ref??? { @@ -109,7 +105,6 @@ private: template <class T, class... Args> inline SharedRef<T> makeSharedRef(Args&& ... args) { return SharedRef<T>(std::make_shared<T>(std::forward<Args>(args)...)); } -//=========================================================================== diff --git a/zen/sys_info.cpp b/zen/sys_info.cpp index d208cc98..bc1bfe62 100644 --- a/zen/sys_info.cpp +++ b/zen/sys_info.cpp @@ -28,30 +28,32 @@ Zstring zen::getLoginUser() //throw FileError { auto tryGetNonRootUser = [](const char* varName) -> const char* { - if (const char* buf = ::getenv(varName)) //no extended error reporting + if (const char* buf = ::getenv(varName)) //no ownership transfer + 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 + if (const uid_t userIdNo = ::getuid(); //never fails + userIdNo != 0) //nofail; non-root { - std::vector<char> buf(std::max<long>(10000, ::sysconf(_SC_GETPW_R_SIZE_MAX))); //::sysconf may return long(-1) + //ugh, the world's stupidest API: + std::vector<char> buf(std::max<long>(10000, ::sysconf(_SC_GETPW_R_SIZE_MAX))); //::sysconf may return long(-1) or even a too small size!! WTF! passwd buf2 = {}; - passwd* pwsEntry = nullptr; - if (::getpwuid_r(userIdNo, //uid_t uid - &buf2, //struct passwd* pwd - &buf[0], //char* buf - buf.size(), //size_t buflen - &pwsEntry) != 0) //struct passwd** result - THROW_LAST_FILE_ERROR(_("Cannot get process information."), "getpwuid_r"); - - if (!pwsEntry) - throw FileError(_("Cannot get process information."), L"no login found"); //should not happen? - - return pwsEntry->pw_name; + passwd* pwEntry = nullptr; + if (const int rv = ::getpwuid_r(userIdNo, //uid_t uid + &buf2, //struct passwd* pwd + &buf[0], //char* buf + buf.size(), //size_t buflen + &pwEntry); //struct passwd** result + rv != 0 || !pwEntry) + { + //"If an error occurs, errno is set appropriately" => why the fuck, then also return errno as return value!? + errno = rv != 0 ? rv : ENOENT; + THROW_LAST_FILE_ERROR(_("Cannot get process information."), "getpwuid_r(" + numberTo<std::string>(userIdNo) + ')'); + } + + return pwEntry->pw_name; } //else: root(0) => consider as request for elevation, NOT impersonation! @@ -178,29 +180,49 @@ Zstring zen::getRealProcessPath() //throw FileError } -namespace -{ -Zstring getUserDir() //throw FileError +Zstring zen::getUserHome() //throw FileError { + if (::getuid() != 0) //nofail; non-root + /* https://linux.die.net/man/3/getpwuid: An application that wants to determine its user's home directory + should inspect the value of HOME (rather than the value getpwuid(getuid())->pw_dir) since this allows + the user to modify their notion of "the home directory" during a login session. */ + if (const char* homePath = ::getenv("HOME")) //no ownership transfer + no extended error reporting + return homePath; + + //root(0) => consider as request for elevation, NOT impersonation! + //=> no support for HOME variable :( + const Zstring loginUser = getLoginUser(); //throw FileError - if (loginUser == "root") - return "/root"; - else - return "/home/" + loginUser; - //safer? sudo --user $userName sh -c 'echo $HOME' -} + //ugh, the world's stupidest API: + std::vector<char> buf(std::max<long>(10000, ::sysconf(_SC_GETPW_R_SIZE_MAX))); //::sysconf may return long(-1) or even a too small size!! WTF! + passwd buf2 = {}; + passwd* pwEntry = nullptr; + if (const int rv = ::getpwnam_r(loginUser.c_str(), //const char *name + &buf2, //struct passwd* pwd + &buf[0], //char* buf + buf.size(), //size_t buflen + &pwEntry); //struct passwd** result + rv != 0 || !pwEntry) + { + //"If an error occurs, errno is set appropriately" => why the fuck, then also return errno as return value!? + errno = rv != 0 ? rv : ENOENT; + THROW_LAST_FILE_ERROR(_("Cannot get process information."), "getpwnam_r(" + utfTo<std::string>(loginUser) + ')'); + } + + return pwEntry->pw_dir; //home directory } Zstring zen::getUserDataPath() //throw FileError { - if (::getuid() != 0) //nofail; root(0) => consider as request for elevation, NOT impersonation - if (const char* xdgCfgPath = ::getenv("XDG_CONFIG_HOME"); //no extended error reporting + if (::getuid() != 0) //nofail; non-root + if (const char* xdgCfgPath = ::getenv("XDG_CONFIG_HOME"); //no ownership transfer + no extended error reporting xdgCfgPath && xdgCfgPath[0] != 0) return xdgCfgPath; + //root(0) => consider as request for elevation, NOT impersonation - return getUserDir() + "/.config"; //throw FileError + return appendPath(getUserHome(), ".config"); //throw FileError } @@ -208,7 +230,7 @@ Zstring zen::getUserDownloadsPath() //throw FileError { try { - if (::getuid() != 0) //nofail; root(0) => consider as request for elevation, NOT impersonation + if (::getuid() != 0) //nofail; non-root if (const auto& [exitCode, output] = consoleExecute("xdg-user-dir DOWNLOAD", std::nullopt /*timeoutMs*/); //throw SysError exitCode == 0) { @@ -216,9 +238,10 @@ Zstring zen::getUserDownloadsPath() //throw FileError ASSERT_SYSERROR(!downloadsPath.empty()); return downloadsPath; } + //root(0) => consider as request for elevation, NOT impersonation //fallback: probably correct 99.9% of the time anyway... - return getUserDir() + "/Downloads"; //throw FileError + return appendPath(getUserHome(), "Downloads"); //throw FileError } catch (const SysError& e) { throw FileError(_("Cannot get process information."), e.toString()); } } diff --git a/zen/sys_info.h b/zen/sys_info.h index 0393e1fb..ce07d7a1 100644 --- a/zen/sys_info.h +++ b/zen/sys_info.h @@ -35,6 +35,7 @@ Zstring getRealProcessPath(); //throw FileError Zstring getUserDownloadsPath(); //throw FileError Zstring getUserDataPath(); //throw FileError +Zstring getUserHome(); //throw FileError } #endif //SYSTEM_H_4189731847832147508915 diff --git a/zen/type_traits.h b/zen/type_traits.h index 28817aad..6186a2ae 100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -100,7 +100,7 @@ Usage: template <class Predicate> struct LessDescending { - LessDescending(Predicate lessThan) : lessThan_(lessThan) {} + LessDescending(Predicate lessThan) : lessThan_(std::move(lessThan)) {} template <class T> bool operator()(const T& lhs, const T& rhs) const { return lessThan_(rhs, lhs); } private: Predicate lessThan_; |