diff options
Diffstat (limited to 'zen')
-rw-r--r-- | zen/argon2.cpp | 2 | ||||
-rw-r--r-- | zen/basic_math.h | 8 | ||||
-rw-r--r-- | zen/dir_watcher.cpp | 2 | ||||
-rw-r--r-- | zen/file_access.cpp | 46 | ||||
-rw-r--r-- | zen/file_access.h | 3 | ||||
-rw-r--r-- | zen/file_io.cpp | 16 | ||||
-rw-r--r-- | zen/file_path.h | 8 | ||||
-rw-r--r-- | zen/file_traverser.h | 1 | ||||
-rw-r--r-- | zen/format_unit.cpp | 2 | ||||
-rw-r--r-- | zen/open_ssl.cpp | 2 | ||||
-rw-r--r-- | zen/perf.h | 1 | ||||
-rw-r--r-- | zen/process_exec.cpp | 1 | ||||
-rw-r--r-- | zen/process_priority.cpp | 152 | ||||
-rw-r--r-- | zen/process_priority.h | 21 | ||||
-rw-r--r-- | zen/scope_guard.h | 1 | ||||
-rw-r--r-- | zen/socket.h | 2 | ||||
-rw-r--r-- | zen/stl_tools.h | 59 | ||||
-rw-r--r-- | zen/stream_buffer.h | 2 | ||||
-rw-r--r-- | zen/string_tools.h | 2 | ||||
-rw-r--r-- | zen/sys_info.cpp | 1 | ||||
-rw-r--r-- | zen/time.h | 6 | ||||
-rw-r--r-- | zen/zstring.h | 4 |
22 files changed, 242 insertions, 100 deletions
diff --git a/zen/argon2.cpp b/zen/argon2.cpp index 5918b4b3..333128ce 100644 --- a/zen/argon2.cpp +++ b/zen/argon2.cpp @@ -37,9 +37,7 @@ #include "argon2.h" #include <cassert> -//#include <cstring> #include <cstdint> -//#include <cstdlib> #if defined __GNUC__ //including clang #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" //"this statement may fall through" diff --git a/zen/basic_math.h b/zen/basic_math.h index 77ce7b7e..9080d07d 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -27,7 +27,7 @@ template <class N, class D> auto intDivCeil (N numerator, D denominator); template <class N, class D> auto intDivFloor(N numerator, D denominator); template <size_t N, class T> -T power(T value); +constexpr T power(T value); double radToDeg(double rad); //convert unit [rad] into [°] double degToRad(double degree); //convert unit [°] into [rad] @@ -207,12 +207,12 @@ namespace { template <size_t N, class T> struct PowerImpl; //let's use non-recursive specializations to help the compiler -template <class T> struct PowerImpl<2, T> { static T result(T value) { return value * value; } }; -template <class T> struct PowerImpl<3, T> { static T result(T value) { return value * value * value; } }; +template <class T> struct PowerImpl<2, T> { static constexpr T result(T value) { return value * value; } }; +template <class T> struct PowerImpl<3, T> { static constexpr T result(T value) { return value * value * value; } }; } template <size_t N, class T> inline -T power(T value) +constexpr T power(T value) { return PowerImpl<N, T>::result(value); } diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 1b7b0ec4..b5533500 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -5,8 +5,6 @@ // ***************************************************************************** #include "dir_watcher.h" -//#include <algorithm> -//#include <set> #include "thread.h" #include "scope_guard.h" #include "file_access.h" diff --git a/zen/file_access.cpp b/zen/file_access.cpp index e1b2860e..80a14ce7 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -5,9 +5,7 @@ // ***************************************************************************** #include "file_access.h" -//#include <map> #include <algorithm> -//#include <chrono> #include <variant> #include "file_traverser.h" #include "scope_guard.h" @@ -325,10 +323,21 @@ void moveAndRenameFileSub(const Zstring& pathFrom, const Zstring& pathTo, bool r if (::rename(pathFrom.c_str(), pathTo.c_str()) != 0) { - if (errno == EXDEV) - throw ErrorMoveUnsupported(getErrorMsg(), formatSystemError("rename", errno)); + const int ec = errno; //copy before making other system calls! + std::wstring errorDescr = formatSystemError("rename", ec); + + if (ec == EXDEV) + throw ErrorMoveUnsupported(getErrorMsg(), errorDescr); + + if (ec == EINVAL) + for (const Zchar c : getItemName(pathTo)) + if (contains(fileNameForbiddenChars, c)) + { + errorDescr += L' ' + replaceCpy(_("Unsupported character %x"), L"%x", L"'" + utfTo<std::wstring>(c) + L"'"); + break; + } - throw FileError(getErrorMsg(), formatSystemError("rename", errno)); + throw FileError(getErrorMsg(), errorDescr); } } @@ -530,10 +539,21 @@ void zen::createDirectory(const Zstring& dirPath) //throw FileError, ErrorTarget if (::mkdir(dirPath.c_str(), mode) != 0) { - const int ec = errno; //copy before directly or indirectly making other system calls! + const int ec = errno; //copy before making other system calls! + std::wstring errorDescr = formatSystemError("mkdir", ec); + if (ec == EEXIST) - throw ErrorTargetExisting(replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(dirPath)), formatSystemError("mkdir", ec)); - THROW_LAST_SYS_ERROR("mkdir"); + throw ErrorTargetExisting(replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(dirPath)), errorDescr); + + if (ec == EINVAL) + for (const Zchar c : getItemName(dirPath)) + if (contains(fileNameForbiddenChars, c)) + { + errorDescr += L' ' + replaceCpy(_("Unsupported character %x"), L"%x", L"'" + utfTo<std::wstring>(c) + L"'"); + break; + } + + throw SysError(errorDescr); } } catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(dirPath)), e.toString()); } @@ -666,11 +686,19 @@ FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& target { const int ec = errno; //copy before making other system calls! const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)); - const std::wstring errorDescr = formatSystemError("open", ec); + std::wstring errorDescr = formatSystemError("open", ec); if (ec == EEXIST) throw ErrorTargetExisting(errorMsg, errorDescr); + if (ec == EINVAL) + for (const Zchar c : getItemName(targetFile)) + if (contains(fileNameForbiddenChars, c)) + { + errorDescr += L' ' + replaceCpy(_("Unsupported character %x"), L"%x", L"'" + utfTo<std::wstring>(c) + L"'"); + break; + } + throw FileError(errorMsg, errorDescr); } FileOutputPlain fileOut(fdTarget, targetFile); //pass ownership diff --git a/zen/file_access.h b/zen/file_access.h index a3fdcd70..42ee06c0 100644 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -7,8 +7,7 @@ #ifndef FILE_ACCESS_H_8017341345614857 #define FILE_ACCESS_H_8017341345614857 -//#include <functional> -#include "file_path.h" +#include "file_path.h" //we'll need this later anyway! #include "file_error.h" #include "serialize.h" //IoCallback #include <sys/stat.h> diff --git a/zen/file_io.cpp b/zen/file_io.cpp index af80a69a..0a119b4a 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -183,7 +183,7 @@ FileBase::FileHandle openHandleForWrite(const Zstring& filePath) //throw FileErr { try { - //checkForUnsupportedType(filePath); -> not needed, open() + O_WRONLY should fail fast + //check for named pipe, etc.? not needed, open() + O_WRONLY should fail fast const mode_t lockFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; //0666 => umask will be applied implicitly! @@ -195,10 +195,20 @@ FileBase::FileHandle openHandleForWrite(const Zstring& filePath) //throw FileErr if (fdFile == -1) { const int ec = errno; //copy before making other system calls! + std::wstring errorDescr = formatSystemError("open", ec); + if (ec == EEXIST) - throw ErrorTargetExisting(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(filePath)), formatSystemError("open", ec)); + throw ErrorTargetExisting(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(filePath)), errorDescr); - THROW_LAST_SYS_ERROR("open"); + if (ec == EINVAL) + for (const Zchar c : getItemName(filePath)) + if (contains(fileNameForbiddenChars, c)) + { + errorDescr += L' ' + replaceCpy(_("Unsupported character %x"), L"%x", L"'" + utfTo<std::wstring>(c) + L"'"); + break; + } + + throw SysError(errorDescr); } return fdFile; //pass ownership } diff --git a/zen/file_path.h b/zen/file_path.h index 960ec52f..9446ad75 100644 --- a/zen/file_path.h +++ b/zen/file_path.h @@ -14,6 +14,14 @@ namespace zen { const Zchar FILE_NAME_SEPARATOR = '/'; +/* forbidden characters in file names: + Windows: <>:"/\|?* https://docs.microsoft.com/de-de/windows/win32/fileio/naming-a-file#naming-conventions + Linux: / + macOS: : +*/ +const Zchar fileNameForbiddenChars[] = Zstr(R"(<>:"/\|?*)"); + + struct PathComponents { Zstring rootPath; //itemPath = rootPath + (FILE_NAME_SEPARATOR?) + relPath diff --git a/zen/file_traverser.h b/zen/file_traverser.h index 8c2755d6..8f7d8368 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -9,7 +9,6 @@ #include <functional> #include "file_error.h" -//#include "file_path.h" namespace zen { diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index fe44ac7b..e9f7571f 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -5,8 +5,6 @@ // ***************************************************************************** #include "format_unit.h" -//#include <ctime> -//#include <cstdio> #include "basic_math.h" #include "sys_error.h" #include "i18n.h" diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp index 0d4a89ca..e3fc4260 100644 --- a/zen/open_ssl.cpp +++ b/zen/open_ssl.cpp @@ -292,7 +292,7 @@ std::string createHash(const std::string_view str, const EVP_MD* type) //throw S if (::EVP_DigestUpdate(mdctx, //EVP_MD_CTX* ctx str.data(), //const void* - str.size()) != 1) //size_t cnt); + str.size()) != 1) //size_t cnt throw SysError(formatLastOpenSSLError("EVP_DigestUpdate")); if (::EVP_DigestFinal_ex(mdctx, //EVP_MD_CTX* ctx @@ -8,7 +8,6 @@ #define PERF_H_83947184145342652456 #include <chrono> -//#include "scope_guard.h" #include "string_tools.h" #include <iostream> diff --git a/zen/process_exec.cpp b/zen/process_exec.cpp index 76a2e436..24bb8ad2 100644 --- a/zen/process_exec.cpp +++ b/zen/process_exec.cpp @@ -5,7 +5,6 @@ // ***************************************************************************** #include "process_exec.h" -//#include <chrono> #include "guid.h" #include "file_access.h" #include "file_io.h" diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp index b7b4e029..6bd2e3f4 100644 --- a/zen/process_priority.cpp +++ b/zen/process_priority.cpp @@ -5,50 +5,120 @@ // ***************************************************************************** #include "process_priority.h" -#include "i18n.h" + #include <sys/resource.h> //setpriority using namespace zen; -struct PreventStandby::Impl {}; -PreventStandby::PreventStandby() {} -PreventStandby::~PreventStandby() {} - -//solution for GNOME?: https://people.gnome.org/~mccann/gnome-session/docs/gnome-session.html#org.gnome.SessionManager.Inhibit - -struct ScheduleForBackgroundProcessing::Impl {}; -ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() {} -ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing() {} - -/* -struct ScheduleForBackgroundProcessing -{ - - required functions ioprio_get/ioprio_set are not part of glibc: https://linux.die.net/man/2/ioprio_set - - and probably never will: https://sourceware.org/bugzilla/show_bug.cgi?id=4464 - - /usr/include/linux/ioprio.h not available on Ubuntu, so we can't use it instead - - ScheduleForBackgroundProcessing() : oldIoPrio(getIoPriority(IOPRIO_WHO_PROCESS, ::getpid())) - { - if (oldIoPrio != -1) - setIoPriority(::getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)); - } - ~ScheduleForBackgroundProcessing() - { - if (oldIoPrio != -1) - setIoPriority(::getpid(), oldIoPrio); - } - -private: - static int getIoPriority(pid_t pid) - { - return ::syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid); - } - static int setIoPriority(pid_t pid, int ioprio) - { - return ::syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio); - } - - const int oldIoPrio; +namespace +{ +#if 0 +//https://linux.die.net/man/2/getpriority +//CPU priority from highest to lowest range: [-NZERO, NZERO -1] usually: [-20, 19] +enum //with values from CentOS 7 +{ + CPU_PRIO_VERYHIGH = -NZERO, + CPU_PRIO_HIGH = -5, + CPU_PRIO_NORMAL = 0, + CPU_PRIO_LOW = 5, + CPU_PRIO_VERYLOW = NZERO - 1, +}; + +int getCpuPriority() //throw SysError +{ + errno = 0; + const int prio = getpriority(PRIO_PROCESS, 0 /* = the calling process */); + if (prio == -1 && errno != 0) //"can legitimately return the value -1" + THROW_LAST_SYS_ERROR("getpriority"); + return prio; +} + + +//lowering is allowed, but increasing CPU prio requires admin rights >:( +void setCpuPriority(int prio) //throw SysError +{ + if (setpriority(PRIO_PROCESS, 0 /* = the calling process */, prio) != 0) + THROW_LAST_SYS_ERROR("setpriority(" + numberTo<std::string>(prio) + ')'); +} +#endif +//--------------------------------------------------------------------------------------------------- + +//- required functions ioprio_get/ioprio_set are not part of glibc: https://linux.die.net/man/2/ioprio_set +//- and probably never will: https://sourceware.org/bugzilla/show_bug.cgi?id=4464 +//https://github.com/torvalds/linux/blob/master/include/uapi/linux/ioprio.h +#define IOPRIO_CLASS_SHIFT 13 + +#define IOPRIO_PRIO_VALUE(prioclass, priolevel) \ + (((prioclass) << IOPRIO_CLASS_SHIFT) | (priolevel)) + +#define IOPRIO_NORM 4 + +enum +{ + IOPRIO_WHO_PROCESS = 1, + IOPRIO_WHO_PGRP, + IOPRIO_WHO_USER, }; -*/ + +enum +{ + IOPRIO_CLASS_NONE = 0, + IOPRIO_CLASS_RT = 1, + IOPRIO_CLASS_BE = 2, + IOPRIO_CLASS_IDLE = 3, +}; + + +int getIoPriority() //throw SysError +{ + const int rv = ::syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, ::getpid()); + if (rv == -1) + THROW_LAST_SYS_ERROR("ioprio_get"); + + //fix Linux kernel fuck up: bogus system default value + if (rv == IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, IOPRIO_NORM)) + return IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM); + + return rv; +} + + +void setIoPriority(int ioPrio) //throw SysError +{ + if (::syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, ::getpid(), ioPrio) != 0) + THROW_LAST_SYS_ERROR("ioprio_set(0x" + printNumber<std::string>("%x", static_cast<unsigned int>(ioPrio)) + ')'); +} +} + + +struct SetProcessPriority::Impl +{ + std::optional<int> oldIoPrio; +}; + + +SetProcessPriority::SetProcessPriority(ProcessPriority prio) : //throw FileError + pimpl_(new Impl) +{ + if (prio == ProcessPriority::background) + try + { + pimpl_->oldIoPrio = getIoPriority(); //throw SysError + + setIoPriority(IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 6 /*0 (highest) to 7 (lowest)*/)); //throw SysError + //maybe even IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0) ? nope: "only served when no one else is using the disk" + } + catch (const SysError& e) { throw FileError(_("Cannot change process I/O priorities."), e.toString()); } +} + + +SetProcessPriority::~SetProcessPriority() +{ + if (pimpl_->oldIoPrio) + try + { + setIoPriority(*pimpl_->oldIoPrio); //throw SysError + } + catch (const SysError& e) { logExtraError(_("Cannot change process I/O priorities.") + L"\n\n" + e.toString()); } +} diff --git a/zen/process_priority.h b/zen/process_priority.h index cfadfff1..216c1153 100644 --- a/zen/process_priority.h +++ b/zen/process_priority.h @@ -13,23 +13,20 @@ namespace zen { -//signal a "busy" state to the operating system -class PreventStandby +enum class ProcessPriority { -public: - PreventStandby(); //throw FileError - ~PreventStandby(); -private: - struct Impl; - const std::unique_ptr<Impl> pimpl_; + normal, + background, //lower CPU and file I/O priorities }; -//lower CPU and file I/O priorities -class ScheduleForBackgroundProcessing +//- prevent operating system going into sleep state +//- set process I/O priorities +class SetProcessPriority { public: - ScheduleForBackgroundProcessing(); //throw FileError - ~ScheduleForBackgroundProcessing(); + explicit SetProcessPriority(ProcessPriority prio); //throw FileError + ~SetProcessPriority(); + private: struct Impl; const std::unique_ptr<Impl> pimpl_; diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 866044a8..7dd2bbde 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -8,7 +8,6 @@ #define SCOPE_GUARD_H_8971632487321434 #include <cassert> -//#include "type_traits.h" #include "legacy_compiler.h" //std::uncaught_exceptions //best of Zen, Loki and C++17 diff --git a/zen/socket.h b/zen/socket.h index 461226d0..9663a422 100644 --- a/zen/socket.h +++ b/zen/socket.h @@ -165,7 +165,7 @@ public: //----------------------------------------------------------- //configure *after* selecting appropriate socket: cfg-failure should not discard otherwise fine connection! - int noDelay = 1; //disable Nagle algorithm: https://brooker.co.za/blog/2024/05/09/nagle.html + int noDelay = 1; //disable Nagle algorithm: https://brooker.co.za/blog/2024/05/09/nagle.html //e.g. test case "website sync": 23% shorter comparison time! if (::setsockopt(socket_, //_In_ SOCKET s IPPROTO_TCP, //_In_ int level diff --git a/zen/stl_tools.h b/zen/stl_tools.h index f167a814..9fc34e8c 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -14,7 +14,6 @@ #include <unordered_map> #include <memory> #include <cassert> -//#include <algorithm> #include <optional> #include "type_traits.h" @@ -93,21 +92,22 @@ class SharedRef //why is there no std::shared_ref??? public: SharedRef() = delete; //no surprise memory allocations! - explicit SharedRef(std::shared_ptr<T> ptr) : ref_(std::move(ptr)) { assert(ref_); } + explicit SharedRef(const std::shared_ptr<T>& ptr) : ptr_ (ptr) { assert(ptr_); } + explicit SharedRef( std::shared_ptr<T>&& ptr) : ptr_(std::move(ptr)) { assert(ptr_); } - template <class U> - SharedRef(const SharedRef<U>& other) : ref_(other.ref_) {} + template <class U> SharedRef(const SharedRef<U>& other) : ptr_ (other.ptr_) {} + template <class U> SharedRef( SharedRef<U>&& other) : ptr_(std::move(other.ptr_)) {} - /**/ T& ref() { return *ref_; }; - const T& ref() const { return *ref_; }; + /**/ T& ref() { return *ptr_; }; + const T& ref() const { return *ptr_; }; - std::shared_ptr< T> ptr() { return ref_; }; - std::shared_ptr<const T> ptr() const { return ref_; }; + const std::shared_ptr< T>& ptr() { return ptr_; }; + /**/ std::shared_ptr<const T> ptr() const { return ptr_; }; //careful: return value has different type => creates temporary! private: template <class U> friend class SharedRef; - std::shared_ptr<T> ref_; //always bound + std::shared_ptr<T> ptr_; //always bound }; template <class T, class... Args> inline @@ -115,8 +115,49 @@ SharedRef<T> makeSharedRef(Args&& ... args) { return SharedRef<T>(std::make_shar +//hide SharedRef as an implementation detail +template <class IterImpl, //underlying iterator type + class T> //target value type +class DerefIter +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; + + DerefIter() {} + DerefIter(IterImpl it) : it_(std::move(it)) {} + //DerefIter(const DerefIter& other) : it_(other.it_) {} + DerefIter& operator++() { ++it_; return *this; } + DerefIter& operator--() { --it_; return *this; } + inline friend DerefIter operator++(DerefIter& it, int) { return it++; } + inline friend DerefIter operator--(DerefIter& it, int) { return it--; } + inline friend ptrdiff_t operator-(const DerefIter& lhs, const DerefIter& rhs) { return lhs.it_ - rhs.it_; } + bool operator==(const DerefIter&) const = default; + T& operator* () const { return it_->ref(); } + T* operator->() const { return &it_->ref(); } +private: + IterImpl it_{}; +}; + +template <class Iterator> +class Range +{ +public: + Range(Iterator first, Iterator last) : first_(std::move(first)), last_(std::move(last)) {} + Iterator begin() const { return first_; } + Iterator end () const { return last_; } + + bool empty() const { return first_ == last_; } + size_t size() const { return last_ - first_; } +private: + Iterator first_; + Iterator last_; +}; //######################## implementation ######################## diff --git a/zen/stream_buffer.h b/zen/stream_buffer.h index 5d2ec024..1a7acb13 100644 --- a/zen/stream_buffer.h +++ b/zen/stream_buffer.h @@ -8,10 +8,8 @@ #define STREAM_BUFFER_H_08492572089560298 #include <thread> -//#include <condition_variable> #include "ring_buffer.h" #include "string_tools.h" -//#include "thread.h" namespace zen diff --git a/zen/string_tools.h b/zen/string_tools.h index d7d0804f..6fc58f9c 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -7,8 +7,6 @@ #ifndef STRING_TOOLS_H_213458973046 #define STRING_TOOLS_H_213458973046 -//#include <cctype> //isspace -//#include <cwctype> //iswspace #include <cstdio> //sprintf #include <cwchar> //swprintf #include "stl_tools.h" diff --git a/zen/sys_info.cpp b/zen/sys_info.cpp index 530b3d85..4687bfcb 100644 --- a/zen/sys_info.cpp +++ b/zen/sys_info.cpp @@ -8,7 +8,6 @@ #include "crc.h" #include "file_access.h" #include "sys_version.h" -//#include "time.h" #include "symlink_target.h" #include "file_io.h" @@ -60,7 +60,7 @@ TimeComp parseTime(const String& format, const String2& str); //similar to ::str //---------------------------------------------------------------------------------------------------------------------------------- //format: [-][[d.]HH:]MM:SS e.g. -1.23:45:67 -Zstring formatTimeSpan(int64_t timeInSec, bool hourOptional = false); +Zstring formatTimeSpan(int64_t timeInSec, bool hourRequired = true); @@ -389,7 +389,7 @@ TimeComp parseTime(const String& format, const String2& str) inline -Zstring formatTimeSpan(int64_t timeInSec, bool hourOptional) +Zstring formatTimeSpan(int64_t timeInSec, bool hourRequired) { Zstring timespanStr; @@ -400,7 +400,7 @@ Zstring formatTimeSpan(int64_t timeInSec, bool hourOptional) } //check *before* subtracting days! - const Zchar* timeSpanFmt = hourOptional && timeInSec < 3600 ? Zstr("%M:%S") : formatIsoTimeTag; + const Zchar* timeSpanFmt = timeInSec < 3600 && !hourRequired ? Zstr("%M:%S") : formatIsoTimeTag; const int secsPerDay = 24 * 3600; const int64_t days = numeric::intDivFloor(timeInSec, secsPerDay); diff --git a/zen/zstring.h b/zen/zstring.h index 00b1c86e..0d493314 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -103,4 +103,8 @@ const wchar_t LEFT_ARROW_ANTICLOCK = L'\u2B8F'; //Anticlockwise Triangle-Headed const wchar_t* const TAB_SPACE = L" "; //4: the only sensible space count for tabs +const wchar_t LINE_SEPARATOR = L'\u2028'; //WTF: visually indistinguishable from new line! +const wchar_t PARAGRAPH_SEPARATOR = L'\u2029'; + + #endif //ZSTRING_H_73425873425789 |