diff options
Diffstat (limited to 'zen')
36 files changed, 1490 insertions, 879 deletions
diff --git a/zen/assert_static.h b/zen/assert_static.h index 00c4c5c8..5a2dc4a6 100644 --- a/zen/assert_static.h +++ b/zen/assert_static.h @@ -25,8 +25,8 @@ template<> struct CompileTimeError<true> {}; } -#define LOKI_CONCAT( X, Y ) LOKI_CONCAT_SUB( X, Y ) -#define LOKI_CONCAT_SUB( X, Y ) X##Y +#define LOKI_CONCAT(X, Y) LOKI_CONCAT_SUB(X, Y) +#define LOKI_CONCAT_SUB(X, Y) X ## Y #define assert_static(expr) \ enum { LOKI_CONCAT(loki_enum_dummy_value, __LINE__) = sizeof(StaticCheckImpl::CompileTimeError<static_cast<bool>(expr) >) } @@ -37,7 +37,7 @@ struct CompileTimeError<true> {}; #endif */ -//C++11: +//C++11: at least get rid of this pointless string literal requirement #define assert_static(X) \ static_assert(X, "Static assert has failed!"); diff --git a/zen/basic_math.h b/zen/basic_math.h index 24bcf27a..606d90ad 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -1,7 +1,8 @@ // ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// * This file is part of the zenXML project. It is distributed under the * +// * Boost Software License, Version 1.0. See accompanying file * +// * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt. * +// * Copyright (C) 2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** #ifndef BASIC_MATH_HEADER_34726398432 diff --git a/zen/com_error.h b/zen/com_error.h index 2ba76c0f..4546bd8a 100644 --- a/zen/com_error.h +++ b/zen/com_error.h @@ -16,6 +16,29 @@ namespace zen std::wstring generateErrorMsg(const std::wstring& input, HRESULT hr); std::wstring formatWin32Msg(DWORD dwMessageId); //return empty string on error +class ComError +{ +public: + explicit ComError(const std::wstring& msg, HRESULT hr = S_OK) : msg_(hr == S_OK ? msg : generateErrorMsg(msg, hr)) {} + const std::wstring& toString() const { return msg_; } + +private: + std::wstring msg_; +}; + +#define ZEN_CHECK_COM(func) ZEN_CHECK_COM_ERROR(func, #func) //throw ComError +/*Convenience Macro checking for COM errors: + +Example: ZEN_CHECK_COM(backupComp->InitializeForBackup()); + +Equivalent to: +{ + HRESULT hrInternal = backupComp->InitializeForBackup(); + if (FAILED(hrInternal)) + throw ComError(L"Error calling \"backupComp->InitializeForBackup()\".", hrInternal); +} +*/ + @@ -202,5 +225,18 @@ std::wstring generateErrorMsg(const std::wstring& input, HRESULT hr) } return output; } + + +#define ZEN_CHECK_COM_ERROR(func, txt) \ + { \ + HRESULT hrInternal = func; \ + if (FAILED(hrInternal)) \ + throw ComError(L"Error calling \"" ## ZEN_CONCAT_SUB(L, txt) ## L"\".", hrInternal); \ + } + +#ifndef ZEN_CONCAT //redeclare those macros: avoid dependency to scope_guard.h +#define ZEN_CONCAT_SUB(X, Y) X ## Y +#define ZEN_CONCAT(X, Y) ZEN_CONCAT_SUB(X, Y) +#endif } #endif //COM_ERROR_HEADER diff --git a/zen/com_ptr.h b/zen/com_ptr.h index 380f4536..de8dbe64 100644 --- a/zen/com_ptr.h +++ b/zen/com_ptr.h @@ -8,6 +8,7 @@ #define SMART_COM_PTR_H #include <algorithm> +#include "win.h" //includes "windows.h" -> always include before other headers that also might include "windows.h"! #include <Objbase.h> namespace zen @@ -16,7 +17,7 @@ namespace zen ComPtr: RAII class handling COM objects Example: - -------- +-------- ComPtr<IUPnPDeviceFinder> devFinder; if (FAILED(::CoCreateInstance(CLSID_UPnPDeviceFinder, NULL, @@ -33,16 +34,17 @@ template <class T> class ComPtr { public: - ComPtr() : ptr(NULL) {} + ComPtr() : ptr(nullptr) {} ComPtr(const ComPtr& other) : ptr(other.ptr) { if (ptr) ptr->AddRef(); } - ComPtr( ComPtr&& other) : ptr(other.release()) {} - - ComPtr& operator=(const ComPtr& other) { ComPtr(other).swap(*this); return *this; } - ComPtr& operator=( ComPtr&& other) { swap(other); return *this; } + ComPtr( ComPtr&& other) : ptr(other.ptr) { other.ptr = nullptr; } ~ComPtr() { if (ptr) ptr->Release(); } + ComPtr& operator=(ComPtr other) { swap(other); return *this; } //unifying assignment: no need for r-value reference assignment! + + void swap(ComPtr& rhs) { std::swap(ptr, rhs.ptr); } //throw() + T** init() //get pointer for use with ::CoCreateInstance() { ComPtr<T>().swap(*this); @@ -51,24 +53,23 @@ public: T* get() const { return ptr; } + T* operator->() const { return ptr; } + T& operator* () const { return *ptr; } + T* release() //throw() { T* tmp = ptr; - ptr = NULL; + ptr = nullptr; return tmp; } - void swap(ComPtr& rhs) { std::swap(ptr, rhs.ptr); } //throw() - - T* operator->() const { return ptr; } - private: T* ptr; struct ConversionToBool { int dummy; }; public: //use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20) - operator int ConversionToBool::* () const { return ptr != NULL ? &ConversionToBool::dummy : NULL; } + operator int ConversionToBool::* () const { return ptr != nullptr ? &ConversionToBool::dummy : nullptr; } }; @@ -96,10 +97,7 @@ ComPtr<S> com_dynamic_cast(const ComPtr<T>& other); //throw() - - - -//################# Inline Implementation ############################# +//################# implementation ############################# //we cannot specialize std::swap() for a class template and are not allowed to overload it => offer swap in own namespace template <class T> inline diff --git a/zen/debug_log.h b/zen/debug_log.h index d8871ef9..bd9af25f 100644 --- a/zen/debug_log.h +++ b/zen/debug_log.h @@ -7,76 +7,93 @@ #ifndef DEBUG_LOG_HEADER_017324601673246392184621895740256342 #define DEBUG_LOG_HEADER_017324601673246392184621895740256342 -#include "zstring.h" +#include <string> +#include <cstdio> +#include <memory> +#include "deprecate.h" +#include "string_tools.h" +#include "time.h" -cleanup this mess + remove any wxWidgets dependency! //small macro for writing debug information into a logfile -#define WRITE_DEBUG_LOG(x) globalLogFile().write(getCodeLocation(__TFILE__, __LINE__) + x); +#define WRITE_LOG(x) globalLogFile().write(__FILE__, __LINE__, x); + //speed alternative: wxLogDebug(wxT("text")) + DebugView +namespace zen +{ +#ifdef FFS_WIN +const char ZEN_FILE_NAME_SEPARATOR = '\\'; + +#elif defined FFS_LINUX +const char ZEN_FILE_NAME_SEPARATOR = '/'; + +#else +#error specify platform! +#endif + + class DebugLog { public: - wxDEPRECATED(DebugLog(const wxString& filePrefix = wxString())) - prefix(filePrefix), - lineCount(0) - { - logfileName = assembleFileName(); - logFile.Open(logfileName, wxFile::write); - } + class LogError {}; - void write(const std::string& logText) + ZEN_DEPRECATE + DebugLog(const std::string& filePrefix = std::string()) : + filename(filePrefix + "DEBUG_" + formatTime<std::string>("%Y-%m-%d %H%M%S") + ".log"), + rowCount(0), + handle(std::fopen(filename.c_str(), "w")) //Windows: non binary mode: automatically convert "\n" to "\r\n"; Linux: binary is default! { - todo; + if (!handle) + throw LogError(); } - void write(const wxString& logText) + ~DebugLog() { std::fclose(handle); } + + void write(const std::string& sourceFile, + int sourceRow, + const std::string& message) { - ++lineCount; - if (lineCount % 50000 == 0) //prevent logfile from becoming too big - { - logFile.Close(); - wxRemoveFile(logfileName); + const std::string logEntry = "[" + formatTime<std::string>(FORMAT_TIME()) + "] " + afterLast(sourceFile, ZEN_FILE_NAME_SEPARATOR) + + ", line " + toString<std::string>(sourceRow) + ": " + message + "\n"; - logfileName = assembleFileName(); - logFile.Open(logfileName, wxFile::write); - } + const size_t bytesWritten = ::fwrite(logEntry.c_str(), 1, logEntry.size(), handle); + if (std::ferror(handle) != 0 || bytesWritten != logEntry.size()) + throw LogError(); -ersetze wxDateTime::Now() durch eigene lib: - z.b. iso_time.h + if (std::fflush(handle) != 0) + throw LogError(); - logFile.Write(wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ")); - logFile.Write(logText + LINE_BREAK); + ++rowCount; } -private: - wxString assembleFileName() - { - wxString tmp = wxDateTime::Now().FormatISOTime(); - tmp.Replace(wxT(":"), wxEmptyString); - return prefix + wxString(wxT("DEBUG_")) + wxDateTime::Now().FormatISODate() + wxChar('_') + tmp + wxT(".log"); - } + size_t getRows() const { return rowCount; } - wxString logfileName; - wxString prefix; - int lineCount; - wxFile logFile; //logFile.close(); <- not needed + std::string getFileName() const { return filename; } + +private: + std::string filename; + size_t rowCount; + FILE* handle; }; + inline DebugLog& globalLogFile() { - static DebugLog inst; //external linkage despite header definition! - return inst; -} + static std::unique_ptr<DebugLog> inst(new DebugLog); //external linkage despite header definition! -inline -wxString getCodeLocation(const wxString& file, int line) -{ - return wxString(file).AfterLast(FILE_NAME_SEPARATOR) + wxT(", LINE ") + toString<wxString>(line) + wxT(" | "); -} + if (inst->getRows() > 50000) //prevent logfile from becoming too big + { + const std::string oldName = inst->getFileName(); + inst.reset(); + std::remove(oldName.c_str()); //unchecked deletion! + inst.reset(new DebugLog); + } + return *inst; +} +} #endif //DEBUG_LOG_HEADER_017324601673246392184621895740256342 diff --git a/zen/debug_new.cpp b/zen/debug_new.cpp index c830a36b..2017dcd2 100644 --- a/zen/debug_new.cpp +++ b/zen/debug_new.cpp @@ -6,7 +6,7 @@ #include "debug_new.h" -#include "win.h" //includes "windows.h" +#include "win.h" //includes "windows.h" #include "DbgHelp.h" //available for MSC only #pragma comment(lib, "Dbghelp.lib") @@ -16,35 +16,30 @@ namespace LONG WINAPI writeDumpOnException(EXCEPTION_POINTERS* pExceptionInfo) { HANDLE hFile = ::CreateFile(L"exception.dmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - MINIDUMP_EXCEPTION_INFORMATION exInfo = {}; - exInfo.ThreadId = ::GetCurrentThreadId(); - exInfo.ExceptionPointers = pExceptionInfo; - exInfo.ClientPointers = NULL; - - MINIDUMP_EXCEPTION_INFORMATION* exceptParam = pExceptionInfo ? &exInfo : NULL; - - ::MiniDumpWriteDump(::GetCurrentProcess(), //__in HANDLE hProcess, - ::GetCurrentProcessId(), //__in DWORD ProcessId, - hFile, //__in HANDLE hFile, - MiniDumpWithDataSegs, //__in MINIDUMP_TYPE DumpType, ->Standard: MiniDumpNormal, Medium: MiniDumpWithDataSegs, Full: MiniDumpWithFullMemory - exceptParam, //__in PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - NULL, //__in PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - NULL); //__in PMINIDUMP_CALLBACK_INFORMATION CallbackParam - - ::CloseHandle(hFile); - + if (hFile != INVALID_HANDLE_VALUE) + { + MINIDUMP_EXCEPTION_INFORMATION exInfo = {}; + exInfo.ThreadId = ::GetCurrentThreadId(); + exInfo.ExceptionPointers = pExceptionInfo; + + MINIDUMP_EXCEPTION_INFORMATION* exceptParam = pExceptionInfo ? &exInfo : NULL; + + /*bool rv = */ + ::MiniDumpWriteDump(::GetCurrentProcess(), //__in HANDLE hProcess, + ::GetCurrentProcessId(), //__in DWORD ProcessId, + hFile, //__in HANDLE hFile, + MiniDumpWithDataSegs, //__in MINIDUMP_TYPE DumpType, ->Standard: MiniDumpNormal, Medium: MiniDumpWithDataSegs, Full: MiniDumpWithFullMemory + exceptParam, //__in PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + NULL, //__in PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + NULL); //__in PMINIDUMP_CALLBACK_INFORMATION CallbackParam + + ::CloseHandle(hFile); + } return EXCEPTION_EXECUTE_HANDLER; } - -struct WriteDumpOnUnhandledException -{ - WriteDumpOnUnhandledException() - { - ::SetUnhandledExceptionFilter(writeDumpOnException); - } -} dummy; //ensure that a dump-file is written for uncaught exceptions +//ensure that a dump-file is written for uncaught exceptions +struct Dummy { Dummy() { ::SetUnhandledExceptionFilter(writeDumpOnException); }} dummy; } diff --git a/zen/deprecate.h b/zen/deprecate.h new file mode 100644 index 00000000..3481a062 --- /dev/null +++ b/zen/deprecate.h @@ -0,0 +1,20 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + +#ifndef DEPRECATE_HEADER_2348970348 +#define DEPRECATE_HEADER_2348970348 + +#ifdef __GNUC__ +#define ZEN_DEPRECATE __attribute__ ((deprecated)) + +#elif defined _MSC_VER +#define ZEN_DEPRECATE __declspec(deprecated) + +#else +#error add your platform here! +#endif + +#endif //DEPRECATE_HEADER_2348970348 diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 7b45b014..81e49f89 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -52,7 +52,7 @@ public: else { const char* bufPos = &buffer[0]; - for(;;) + for (;;) { const FILE_NOTIFY_INFORMATION& notifyInfo = reinterpret_cast<const FILE_NOTIFY_INFORMATION&>(*bufPos); @@ -115,7 +115,7 @@ public: void reportError(const std::wstring& msg, DWORD errorCode) //throw() { boost::lock_guard<boost::mutex> dummy(lockAccess); - errorMsg = std::make_pair(cvrtString<BasicWString>(msg), errorCode); + errorMsg = std::make_pair(copyStringTo<BasicWString>(msg), errorCode); } private: @@ -157,7 +157,7 @@ public: NULL); if (hDir == INVALID_HANDLE_VALUE ) { - const std::wstring errorMsg = _("Could not initialize directory monitoring:") + "\n\"" + utf8CvrtTo<std::wstring>(dirname) + "\"" + "\n\n" + zen::getLastErrorFormatted(); + const std::wstring errorMsg = _("Could not initialize directory monitoring:") + L"\n\"" + dirname + L"\"" + L"\n\n" + zen::getLastErrorFormatted(); if (errorCodeForNotExisting(::GetLastError())) throw ErrorNotExisting(errorMsg); throw FileError(errorMsg); @@ -178,7 +178,7 @@ public: { std::vector<char> buffer(64 * 1024); //needs to be aligned on a DWORD boundary; maximum buffer size restricted by some networks protocols (according to docu) - for(;;) + for (;;) { boost::this_thread::interruption_point(); @@ -189,7 +189,7 @@ public: false, //__in BOOL bInitialState, NULL); //__in_opt LPCTSTR lpName if (overlapped.hEvent == NULL) - return shared_->reportError(_("Error when monitoring directories.") + " (CreateEvent)" + "\n\n" + getLastErrorFormatted(), ::GetLastError()); + return shared_->reportError(_("Error when monitoring directories.") + L" (CreateEvent)" + L"\n\n" + getLastErrorFormatted(), ::GetLastError()); ZEN_ON_BLOCK_EXIT(::CloseHandle(overlapped.hEvent)); //asynchronous variant: runs on this thread's APC queue! @@ -204,7 +204,7 @@ public: NULL, // __out_opt LPDWORD lpBytesReturned, &overlapped, // __inout_opt LPOVERLAPPED lpOverlapped, NULL)) // __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine - return shared_->reportError(_("Error when monitoring directories.") + " (ReadDirectoryChangesW)" + "\n\n" + getLastErrorFormatted(), ::GetLastError()); + return shared_->reportError(_("Error when monitoring directories.") + L" (ReadDirectoryChangesW)" + L"\n\n" + getLastErrorFormatted(), ::GetLastError()); //async I/O is a resource that needs to be guarded since it will write to local variable "buffer"! zen::ScopeGuard lockAio = zen::makeGuard([&]() @@ -226,7 +226,7 @@ public: false)) //__in BOOL bWait { if (::GetLastError() != ERROR_IO_INCOMPLETE) - return shared_->reportError(_("Error when monitoring directories.") + " (GetOverlappedResult)" + "\n\n" + getLastErrorFormatted(), ::GetLastError()); + return shared_->reportError(_("Error when monitoring directories.") + L" (GetOverlappedResult)" + L"\n\n" + getLastErrorFormatted(), ::GetLastError()); //execute asynchronous procedure calls (APC) queued on this thread ::SleepEx(50, // __in DWORD dwMilliseconds, @@ -365,18 +365,20 @@ namespace class DirsOnlyTraverser : public zen::TraverseCallback { public: - DirsOnlyTraverser(std::vector<Zstring>& dirs) : dirs_(dirs) {} + DirsOnlyTraverser(std::vector<Zstring>& dirs, + const std::shared_ptr<TraverseCallback>& otherMe) : otherMe_(otherMe), dirs_(dirs) {} virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) {} virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) {} - virtual ReturnValDir onDir (const Zchar* shortName, const Zstring& fullName) + virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) { dirs_.push_back(fullName); - return ReturnValDir(zen::Int2Type<ReturnValDir::TRAVERSING_DIR_CONTINUE>(), *this); + return otherMe_; } virtual HandleError onError(const std::wstring& errorText) { throw FileError(errorText); } private: + const std::shared_ptr<TraverseCallback>& otherMe_; //lifetime management, two options: 1. use std::weak_ptr 2. ref to shared_ptr std::vector<Zstring>& dirs_; }; } @@ -394,13 +396,15 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError std::vector<Zstring> fullDirList; fullDirList.push_back(dirname); - DirsOnlyTraverser traverser(fullDirList); //throw FileError - zen::traverseFolder(dirname, false, traverser); //don't traverse into symlinks (analog to windows build) + std::shared_ptr<TraverseCallback> traverser; + traverser = std::make_shared<DirsOnlyTraverser>(fullDirList, traverser); //throw FileError + + zen::traverseFolder(dirname, false, *traverser); //don't traverse into symlinks (analog to windows build) //init pimpl_->notifDescr = ::inotify_init(); if (pimpl_->notifDescr == -1) - throw FileError(_("Could not initialize directory monitoring:") + "\n\"" + dirname + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Could not initialize directory monitoring:") + L"\n\"" + dirname + L"\"" + L"\n\n" + getLastErrorFormatted()); zen::ScopeGuard guardDescr = zen::makeGuard([&]() { ::close(pimpl_->notifDescr); }); @@ -411,7 +415,7 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError initSuccess = ::fcntl(pimpl_->notifDescr, F_SETFL, flags | O_NONBLOCK) != -1; if (!initSuccess) - throw FileError(_("Could not initialize directory monitoring:") + "\n\"" + dirname + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Could not initialize directory monitoring:") + L"\n\"" + dirname + L"\"" + L"\n\n" + getLastErrorFormatted()); //add watches std::for_each(fullDirList.begin(), fullDirList.end(), @@ -429,7 +433,7 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError IN_MOVE_SELF); if (wd == -1) { - std::wstring errorMsg = _("Could not initialize directory monitoring:") + "\n\"" + subdir + "\"" + "\n\n" + getLastErrorFormatted(); + std::wstring errorMsg = _("Could not initialize directory monitoring:") + L"\n\"" + subdir + L"\"" + L"\n\n" + getLastErrorFormatted(); if (errno == ENOENT) throw ErrorNotExisting(errorMsg); throw FileError(errorMsg); @@ -463,7 +467,7 @@ std::vector<Zstring> DirWatcher::getChanges() //throw FileError errno == EAGAIN) //Non-blocking I/O has been selected using O_NONBLOCK and no data was immediately available for reading return std::vector<Zstring>(); - throw FileError(_("Error when monitoring directories.") + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error when monitoring directories.") + L"\n\n" + getLastErrorFormatted()); } std::set<Zstring> tmp; //get rid of duplicate entries (actually occur!) diff --git a/zen/disable_standby.h b/zen/disable_standby.h deleted file mode 100644 index ec112427..00000000 --- a/zen/disable_standby.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef PREVENTSTANDBY_H_INCLUDED -#define PREVENTSTANDBY_H_INCLUDED - -#ifdef FFS_WIN -#include "win.h" //includes "windows.h" -#endif - -namespace zen -{ -class DisableStandby -{ -public: -#ifdef FFS_WIN - DisableStandby() - { - ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED /* | ES_AWAYMODE_REQUIRED*/ ); - } - - ~DisableStandby() - { - ::SetThreadExecutionState(ES_CONTINUOUS); - } -#endif -}; -} - -#endif // PREVENTSTANDBY_H_INCLUDED diff --git a/zen/dst_hack.cpp b/zen/dst_hack.cpp index f6579441..e4f48c2f 100644 --- a/zen/dst_hack.cpp +++ b/zen/dst_hack.cpp @@ -166,9 +166,9 @@ FILETIME utcToLocal(const FILETIME& utcTime) //throw (std::runtime_error) &utcTime, //__in const FILETIME *lpFileTime, &localTime)) //__out LPFILETIME lpLocalFileTime { - const std::wstring errorMessage = _("Conversion error:") + " FILETIME -> local FILETIME: " + "(" + - "High: " + toString<std::wstring>(utcTime.dwHighDateTime) + " " + - "Low: " + toString<std::wstring>(utcTime.dwLowDateTime) + ") " + "\n\n" + getLastErrorFormatted(); + const std::wstring errorMessage = _("Conversion error:") + L" FILETIME -> local FILETIME: " + L"(" + + L"High: " + toString<std::wstring>(utcTime.dwHighDateTime) + L" " + + L"Low: " + toString<std::wstring>(utcTime.dwLowDateTime) + L") " + L"\n\n" + getLastErrorFormatted(); throw std::runtime_error(wideToUtf8<std::string>(errorMessage)); } return localTime; @@ -183,9 +183,9 @@ FILETIME localToUtc(const FILETIME& localTime) //throw (std::runtime_error) &localTime, //__in const FILETIME *lpLocalFileTime, &utcTime)) //__out LPFILETIME lpFileTime { - const std::wstring errorMessage = _("Conversion error:") + " local FILETIME -> FILETIME: " + "(" + - "High: " + toString<std::wstring>(localTime.dwHighDateTime) + " " + - "Low: " + toString<std::wstring>(localTime.dwLowDateTime) + ") " + "\n\n" + getLastErrorFormatted(); + const std::wstring errorMessage = _("Conversion error:") + L" local FILETIME -> FILETIME: " + L"(" + + L"High: " + toString<std::wstring>(localTime.dwHighDateTime) + L" " + + L"Low: " + toString<std::wstring>(localTime.dwLowDateTime) + L") " + L"\n\n" + getLastErrorFormatted(); throw std::runtime_error(wideToUtf8<std::string>(errorMessage)); } return utcTime; @@ -283,8 +283,8 @@ std::bitset<UTC_LOCAL_OFFSET_BITS> getUtcLocalShift() if (std::bitset < UTC_LOCAL_OFFSET_BITS - 1 > (absValue).to_ulong() != static_cast<unsigned long>(absValue) || //time shifts that big shouldn't be possible! timeShiftSec % (60 * 15) != 0) //all known time shift have at least 15 minute granularity! { - const std::wstring errorMessage = _("Conversion error:") + " Unexpected UTC <-> local time shift: " + - "(" + toString<std::wstring>(timeShiftSec) + ") " + "\n\n" + getLastErrorFormatted(); + const std::wstring errorMessage = _("Conversion error:") + L" Unexpected UTC <-> local time shift: " + + L"(" + toString<std::wstring>(timeShiftSec) + L") " + L"\n\n" + getLastErrorFormatted(); throw std::runtime_error(wideToUtf8<std::string>(errorMessage)); } diff --git a/zen/file_error.h b/zen/file_error.h index 8c49937c..2992fbbe 100644 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -17,10 +17,10 @@ namespace zen class FileError //Exception base class used to notify file/directory copy/delete errors { public: - FileError(const std::wstring& message) : errorMessage(message) {} + explicit FileError(const std::wstring& message) : errorMessage(message) {} virtual ~FileError() {} - const std::wstring& msg() const { return errorMessage; } + const std::wstring& toString() const { return errorMessage; } private: std::wstring errorMessage; @@ -38,11 +38,9 @@ DEFINE_NEW_FILE_ERROR(ErrorFileLocked); //----------- facilitate usage of std::wstring for error messages -------------------- //allow implicit UTF8 conversion: since std::wstring models a GUI string, convenience is more important than performance -inline std::wstring operator+(const std::wstring& lhs, const Zstring& rhs) { return std::wstring(lhs) += zen::utf8CvrtTo<std::wstring>(rhs); } +inline std::wstring operator+(const std::wstring& lhs, const Zstring& rhs) { return std::wstring(lhs) += zen::utf8CvrtTo<std::wstring>(rhs); } //we musn't put our overloads in namespace std, but namespace zen (+ using directive) is sufficient -inline std::wstring operator+(const std::wstring& lhs, const char* rhs) { return std::wstring(lhs) += utf8CvrtTo<std::wstring>(rhs); } -inline std::wstring operator+(const std::wstring& lhs, const std::string& rhs) { return std::wstring(lhs) += utf8CvrtTo<std::wstring>(rhs); } } #endif // FILEERROR_H_INCLUDED diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp index 7b46181b..5d57938a 100644 --- a/zen/file_handling.cpp +++ b/zen/file_handling.cpp @@ -107,7 +107,7 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl { const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileInfo); if (searchHandle == INVALID_HANDLE_VALUE) - throw FileError(_("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted()); ::FindClose(searchHandle); } // WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {}; @@ -144,12 +144,12 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile == INVALID_HANDLE_VALUE) - throw FileError(_("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted()); ZEN_ON_BLOCK_EXIT(::CloseHandle(hFile)); BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) - throw FileError(_("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted()); attr.fileSize = UInt64(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh); attr.modificationTime = toTimeT(fileInfoHnd.ftLastWriteTime); @@ -162,7 +162,7 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl :: stat(filename.c_str(), &fileInfo) : ::lstat(filename.c_str(), &fileInfo); if (rv != 0) //follow symbolic links - throw FileError(_("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted()); attr.fileSize = UInt64(fileInfo.st_size); attr.modificationTime = fileInfo.st_mtime; @@ -287,7 +287,7 @@ bool zen::removeFile(const Zstring& filename) //throw FileError; if (!somethingExists(filename)) return false; //neither file nor any other object (e.g. broken symlink) with that name existing - throw FileError(_("Error deleting file:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted(lastError)); + throw FileError(_("Error deleting file:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted(lastError)); } return true; } @@ -343,7 +343,7 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File } } - std::wstring errorMessage = _("Error moving file:") + "\n\"" + oldName + "\" ->\n\"" + newName + "\"" + "\n\n" + getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error moving file:") + L"\n\"" + oldName + L"\" ->\n\"" + newName + L"\"" + L"\n\n" + getLastErrorFormatted(lastError); if (lastError == ERROR_NOT_SAME_DEVICE) throw ErrorDifferentVolume(errorMessage); @@ -358,7 +358,7 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File { const int lastError = errno; - std::wstring errorMessage = _("Error moving file:") + "\n\"" + oldName + "\" ->\n\"" + newName + "\"" + "\n\n" + getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error moving file:") + L"\n\"" + oldName + L"\" ->\n\"" + newName + L"\"" + L"\n\n" + getLastErrorFormatted(lastError); if (lastError == EXDEV) throw ErrorDifferentVolume(errorMessage); @@ -529,8 +529,8 @@ void zen::moveFile(const Zstring& sourceFile, const Zstring& targetFile, bool ig const bool targetExisting = fileExists(targetFile); if (targetExisting && !ignoreExisting) //test file existence: e.g. Linux might silently overwrite existing symlinks - throw FileError(_("Error moving file:") + "\n\"" + sourceFile + "\" ->\n\"" + targetFile + "\"" + - "\n\n" + _("Target file already existing!")); + throw FileError(_("Error moving file:") + L"\n\"" + sourceFile + L"\" ->\n\"" + targetFile + L"\"" + + L"\n\n" + _("Target file already existing!")); if (!targetExisting) { @@ -583,10 +583,10 @@ public: files_.push_back(NamePair(shortName, fullName)); } - virtual ReturnValDir onDir(const Zchar* shortName, const Zstring& fullName) + virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) { dirs_.push_back(NamePair(shortName, fullName)); - return Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //DON'T traverse into subdirs; moveDirectory works recursively! + return nullptr; //DON'T traverse into subdirs; moveDirectory works recursively! } virtual HandleError onError(const std::wstring& errorText) { throw FileError(errorText); } @@ -629,8 +629,8 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool const bool targetExisting = dirExists(targetDir); if (targetExisting && !ignoreExisting) //directory or symlink exists (or even a file... this error will be caught later) - throw FileError(_("Error moving directory:") + "\n\"" + sourceDir + "\" ->\n\"" + targetDir + "\"" + - "\n\n" + _("Target directory already existing!")); + throw FileError(_("Error moving directory:") + L"\n\"" + sourceDir + L"\" ->\n\"" + targetDir + L"\"" + + L"\n\n" + _("Target directory already existing!")); const bool isSymlink = symlinkExists(sourceDir); @@ -722,10 +722,10 @@ public: else m_files.push_back(fullName); } - virtual ReturnValDir onDir(const Zchar* shortName, const Zstring& fullName) + virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) { m_dirs.push_back(fullName); - return Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //DON'T traverse into subdirs; removeDirectory works recursively! + return nullptr; //DON'T traverse into subdirs; removeDirectory works recursively! } virtual HandleError onError(const std::wstring& errorText) { throw FileError(errorText); } @@ -759,7 +759,7 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) #elif defined FFS_LINUX if (::unlink(directory.c_str()) != 0) #endif - throw FileError(_("Error deleting directory:") + "\n\"" + directory + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error deleting directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + getLastErrorFormatted()); if (callback) callback->notifyDirDeletion(directory); //once per symlink @@ -792,7 +792,7 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) if (::rmdir(directory.c_str()) != 0) #endif { - throw FileError(_("Error deleting directory:") + "\n\"" + directory + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error deleting directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + getLastErrorFormatted()); } if (callback) callback->notifyDirDeletion(directory); //and once per folder @@ -841,7 +841,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr }); if (targetHandle.get() == INVALID_HANDLE_VALUE) - throw FileError(_("Error changing modification time:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error changing modification time:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted()); /* if (hTarget == INVALID_HANDLE_VALUE && ::GetLastError() == ERROR_SHARING_VIOLATION) @@ -857,7 +857,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr isNullTime(creationTime) ? NULL : &creationTime, NULL, &lastWriteTime)) - throw FileError(_("Error changing modification time:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error changing modification time:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted()); #ifndef NDEBUG //dst hack: verify data written if (dst::isFatDrive(filename) && !dirExists(filename)) //throw() @@ -881,7 +881,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr // set new "last write time" if (::utime(filename.c_str(), &newTimes) != 0) - throw FileError(_("Error changing modification time:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error changing modification time:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted()); } else { @@ -893,7 +893,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr newTimes[1].tv_usec = 0; if (::lutimes(filename.c_str(), newTimes) != 0) - throw FileError(_("Error changing modification time:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error changing modification time:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted()); } #endif } @@ -902,10 +902,10 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr namespace { #ifdef FFS_WIN -Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target path of symbolic link to a directory; throw (FileError) +Zstring getSymlinkTargetPath(const Zstring& symlink) //throw FileError { //open handle to target of symbolic link - const HANDLE hDir = ::CreateFile(applyLongPathPrefix(dirLinkName).c_str(), + const HANDLE hDir = ::CreateFile(applyLongPathPrefix(symlink).c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, @@ -913,12 +913,9 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory NULL); if (hDir == INVALID_HANDLE_VALUE) - throw FileError(_("Error resolving symbolic link:") + "\n\"" + dirLinkName + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error resolving symbolic link:") + L"\n\"" + symlink + L"\"" + L"\n\n" + getLastErrorFormatted()); ZEN_ON_BLOCK_EXIT(::CloseHandle(hDir)); - const DWORD BUFFER_SIZE = 10000; - std::vector<wchar_t> targetPath(BUFFER_SIZE); - //dynamically load windows API function typedef DWORD (WINAPI *GetFinalPathNameByHandleWFunc)(HANDLE hFile, LPTSTR lpszFilePath, @@ -926,15 +923,17 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa DWORD dwFlags); const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW"); if (!getFinalPathNameByHandle) - throw FileError(_("Error loading library function:") + "\n\"" + "GetFinalPathNameByHandleW" + "\""); + throw FileError(_("Error loading library function:") + L"\n\"" + L"GetFinalPathNameByHandleW" + L"\""); + const DWORD BUFFER_SIZE = 10000; + std::vector<wchar_t> targetPath(BUFFER_SIZE); const DWORD charsWritten = getFinalPathNameByHandle(hDir, //__in HANDLE hFile, &targetPath[0], //__out LPTSTR lpszFilePath, BUFFER_SIZE, //__in DWORD cchFilePath, FILE_NAME_NORMALIZED); //__in DWORD dwFlags if (charsWritten >= BUFFER_SIZE || charsWritten == 0) { - std::wstring errorMessage = _("Error resolving symbolic link:") + "\n\"" + dirLinkName + "\""; + std::wstring errorMessage = _("Error resolving symbolic link:") + L"\n\"" + symlink + L"\""; if (charsWritten == 0) errorMessage += L"\n\n" + getLastErrorFormatted(); throw FileError(errorMessage); @@ -959,7 +958,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli errno == EOPNOTSUPP) //extended attributes are not supported by the filesystem return; - throw FileError(_("Error reading security context:") + "\n\"" + source + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error reading security context:") + L"\n\"" + source + L"\"" + L"\n\n" + getLastErrorFormatted()); } ZEN_ON_BLOCK_EXIT(::freecon(contextSource)); @@ -987,7 +986,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli ::setfilecon(target.c_str(), contextSource) : ::lsetfilecon(target.c_str(), contextSource); if (rv3 < 0) - throw FileError(_("Error writing security context:") + "\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error writing security context:") + L"\n\"" + target + L"\"" + L"\n\n" + getLastErrorFormatted()); } #endif //HAVE_SELINUX @@ -1010,16 +1009,65 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym } catch (const FileError& e) { - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + e.msg()); + throw FileError(_("Error copying file permissions:") + L"\n\"" + source + L"\" ->\n\"" + target + L"\"" + L"\n\n" + e.toString()); } + //in contrast to ::SetSecurityInfo(), ::SetFileSecurity() seems to honor the "inherit DACL/SACL" flags + + //NOTE: ::GetFileSecurity()/::SetFileSecurity() do NOT follow Symlinks! + const Zstring sourceResolved = procSl == SYMLINK_FOLLOW && symlinkExists(source) ? getSymlinkTargetPath(source) : source; + const Zstring targetResolved = procSl == SYMLINK_FOLLOW && symlinkExists(target) ? getSymlinkTargetPath(target) : target; + + std::vector<char> buffer(10000); //example of actually required buffer size: 192 bytes + for (;;) + { + DWORD bytesNeeded = 0; + if (::GetFileSecurity(applyLongPathPrefix(sourceResolved).c_str(), //__in LPCTSTR lpFileName, -> long path prefix IS needed, although it is NOT mentioned on MSDN!!! + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION RequestedInformation, + reinterpret_cast<PSECURITY_DESCRIPTOR>(&buffer[0]), //__out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor, + static_cast<DWORD>(buffer.size()), //__in DWORD nLength, + &bytesNeeded)) //__out LPDWORD lpnLengthNeeded + break; + //failure: ... + if (bytesNeeded > buffer.size()) + buffer.resize(bytesNeeded); + else + throw FileError(_("Error copying file permissions:") + L"\n\"" + sourceResolved + L"\" ->\n\"" + targetResolved + L"\"" + L"\n\n" + getLastErrorFormatted() + L" (R)"); + } + SECURITY_DESCRIPTOR& secDescr = reinterpret_cast<SECURITY_DESCRIPTOR&>(buffer[0]); + + /* + SECURITY_DESCRIPTOR_CONTROL secCtrl = 0; + { + DWORD ctrlRev = 0; + if (!::GetSecurityDescriptorControl(&secDescr, // __in PSECURITY_DESCRIPTOR pSecurityDescriptor, + &secCtrl, // __out PSECURITY_DESCRIPTOR_CONTROL pControl, + &ctrlRev)) //__out LPDWORD lpdwRevision + throw FileError(_("Error copying file permissions:") + L"\n\"" + sourceResolved + L"\" ->\n\"" + targetResolved + L"\"" + L"\n\n" + getLastErrorFormatted() + L" (C)"); + } + //interesting flags: + //#define SE_DACL_PRESENT (0x0004) + //#define SE_SACL_PRESENT (0x0010) + //#define SE_DACL_PROTECTED (0x1000) + //#define SE_SACL_PROTECTED (0x2000) + */ + + if (!::SetFileSecurity(applyLongPathPrefix(targetResolved).c_str(), //__in LPCTSTR lpFileName, -> long path prefix IS needed, although it is NOT mentioned on MSDN!!! + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInformation, + &secDescr)) //__in PSECURITY_DESCRIPTOR pSecurityDescriptor + throw FileError(_("Error copying file permissions:") + L"\n\"" + sourceResolved + L"\" ->\n\"" + targetResolved + L"\"" + L"\n\n" + getLastErrorFormatted() + L" (W)"); + + /* PSECURITY_DESCRIPTOR buffer = NULL; PSID owner = NULL; PSID group = NULL; PACL dacl = NULL; PACL sacl = NULL; - //http://msdn.microsoft.com/en-us/library/aa364399(v=VS.85).aspx + //File Security and Access Rights: http://msdn.microsoft.com/en-us/library/aa364399(v=VS.85).aspx + //SECURITY_INFORMATION Access Rights: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379573(v=vs.85).aspx const HANDLE hSource = ::CreateFile(applyLongPathPrefix(source).c_str(), READ_CONTROL | ACCESS_SYSTEM_SECURITY, //ACCESS_SYSTEM_SECURITY required for SACL access FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -1028,23 +1076,33 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym FILE_FLAG_BACKUP_SEMANTICS | (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), //FILE_FLAG_BACKUP_SEMANTICS needed to open a directory NULL); if (hSource == INVALID_HANDLE_VALUE) - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted() + " (OR)"); + throw FileError(_("Error copying file permissions:") + L"\n\"" + source + L"\" ->\n\"" + target + L"\"" + L"\n\n" + getLastErrorFormatted() + L" (OR)"); ZEN_ON_BLOCK_EXIT(::CloseHandle(hSource)); // DWORD rc = ::GetNamedSecurityInfo(const_cast<WCHAR*>(applyLongPathPrefix(source).c_str()), -> does NOT dereference symlinks! DWORD rc = ::GetSecurityInfo(hSource, //__in LPTSTR pObjectName, SE_FILE_OBJECT, //__in SE_OBJECT_TYPE ObjectType, - OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInfo, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInfo, &owner, //__out_opt PSID *ppsidOwner, &group, //__out_opt PSID *ppsidGroup, &dacl, //__out_opt PACL *ppDacl, &sacl, //__out_opt PACL *ppSacl, &buffer); //__out_opt PSECURITY_DESCRIPTOR *ppSecurityDescriptor if (rc != ERROR_SUCCESS) - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted(rc) + " (R)"); + throw FileError(_("Error copying file permissions:") + L"\n\"" + source + L"\" ->\n\"" + target + L"\"" + L"\n\n" + getLastErrorFormatted(rc) + L" (R)"); ZEN_ON_BLOCK_EXIT(::LocalFree(buffer)); - //may need to remove the readonly-attribute (e.g. FAT usb drives) + SECURITY_DESCRIPTOR_CONTROL secCtrl = 0; + { + DWORD ctrlRev = 0; + if (!::GetSecurityDescriptorControl(buffer, // __in PSECURITY_DESCRIPTOR pSecurityDescriptor, + &secCtrl, // __out PSECURITY_DESCRIPTOR_CONTROL pControl, + &ctrlRev))//__out LPDWORD lpdwRevision + throw FileError(_("Error copying file permissions:") + L"\n\"" + source + L"\" ->\n\"" + target + L"\"" + L"\n\n" + getLastErrorFormatted(rc) + L" (C)"); + } + + //may need to remove the readonly-attribute FileUpdateHandle targetHandle(target, [ = ]() { return ::CreateFile(applyLongPathPrefix(target).c_str(), // lpFileName @@ -1057,19 +1115,29 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym }); if (targetHandle.get() == INVALID_HANDLE_VALUE) - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted() + " (OW)"); + throw FileError(_("Error copying file permissions:") + L"\n\"" + source + L"\" ->\n\"" + target + L"\"" + L"\n\n" + getLastErrorFormatted() + L" (OW)"); + + SECURITY_INFORMATION secFlags = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION; + + //SACL/DACL inheritence flag is NOT copied by default: we have to tell ::SetSecurityInfo(() to enable/disable it manually! + //if (secCtrl & SE_DACL_PRESENT) + secFlags |= (secCtrl & SE_DACL_PROTECTED) ? PROTECTED_DACL_SECURITY_INFORMATION : UNPROTECTED_DACL_SECURITY_INFORMATION; + //if (secCtrl & SE_SACL_PRESENT) + secFlags |= (secCtrl & SE_SACL_PROTECTED) ? PROTECTED_SACL_SECURITY_INFORMATION : UNPROTECTED_SACL_SECURITY_INFORMATION; + // rc = ::SetNamedSecurityInfo(const_cast<WCHAR*>(applyLongPathPrefix(target).c_str()), //__in LPTSTR pObjectName, -> does NOT dereference symlinks! rc = ::SetSecurityInfo(targetHandle.get(), //__in LPTSTR pObjectName, SE_FILE_OBJECT, //__in SE_OBJECT_TYPE ObjectType, - OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInfo, + secFlags, //__in SECURITY_INFORMATION SecurityInfo, owner, //__in_opt PSID psidOwner, group, //__in_opt PSID psidGroup, dacl, //__in_opt PACL pDacl, sacl); //__in_opt PACL pSacl if (rc != ERROR_SUCCESS) - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted(rc) + " (W)"); + throw FileError(_("Error copying file permissions:") + L"\n\"" + source + L"\" ->\n\"" + target + L"\"" + L"\n\n" + getLastErrorFormatted(rc) + L" (W)"); + */ #elif defined FFS_LINUX @@ -1083,14 +1151,14 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym if (::stat(source.c_str(), &fileInfo) != 0 || ::chown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0 || // may require admin rights! ::chmod(target.c_str(), fileInfo.st_mode) != 0) - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted() + " (R)"); + throw FileError(_("Error copying file permissions:") + L"\n\"" + source + L"\" ->\n\"" + target + L"\"" + L"\n\n" + getLastErrorFormatted() + L" (R)"); } else { if (::lstat(source.c_str(), &fileInfo) != 0 || ::lchown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0 || // may require admin rights! (!symlinkExists(target) && ::chmod(target.c_str(), fileInfo.st_mode) != 0)) //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod() - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted() + " (W)"); + throw FileError(_("Error copying file permissions:") + L"\n\"" + source + L"\" ->\n\"" + target + L"\"" + L"\n\n" + getLastErrorFormatted() + L" (W)"); } #endif } @@ -1110,7 +1178,7 @@ void createDirectory_straight(const Zstring& directory, const Zstring& templateD #endif { if (level != 0) return; - throw FileError(_("Error creating directory:") + "\n\"" + directory + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error creating directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + getLastErrorFormatted()); } if (!templateDir.empty()) @@ -1124,7 +1192,7 @@ void createDirectory_straight(const Zstring& directory, const Zstring& templateD try { //get target directory of symbolic link - sourcePath = resolveDirectorySymlink(templateDir); //throw FileError + sourcePath = getSymlinkTargetPath(templateDir); //throw FileError } catch (FileError&) {} //dereferencing a symbolic link usually fails if it is located on network drive or client is XP: NOT really an error... } @@ -1259,7 +1327,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool const SysDllFun<CreateSymbolicLinkFunc> createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW"); if (!createSymbolicLink) - throw FileError(_("Error loading library function:") + "\n\"" + "CreateSymbolicLinkW" + "\""); + throw FileError(_("Error loading library function:") + L"\n\"" + L"CreateSymbolicLinkW" + L"\""); if (!createSymbolicLink(targetLink.c_str(), //__in LPTSTR lpSymlinkFileName, - seems no long path prefix is required... linkPath.c_str(), //__in LPTSTR lpTargetFileName, @@ -1267,7 +1335,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool #elif defined FFS_LINUX if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0) #endif - throw FileError(_("Error copying symbolic link:") + "\n\"" + sourceLink + "\" ->\n\"" + targetLink + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error copying symbolic link:") + L"\n\"" + sourceLink + L"\" ->\n\"" + targetLink + L"\"" + L"\n\n" + getLastErrorFormatted()); //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist zen::ScopeGuard guardNewDir = zen::makeGuard([&]() @@ -1391,7 +1459,7 @@ DWORD CALLBACK copyCallbackInternal( BY_HANDLE_FILE_INFORMATION fileInfo = {}; if (!::GetFileInformationByHandle(hSourceFile, &fileInfo)) { - cbd.reportError(_("Error reading file attributes:") + "\n\"" + cbd.sourceFile_ + "\"" + "\n\n" + getLastErrorFormatted()); + cbd.reportError(_("Error reading file attributes:") + L"\n\"" + cbd.sourceFile_ + L"\"" + L"\n\n" + getLastErrorFormatted()); return PROGRESS_CANCEL; } @@ -1410,7 +1478,7 @@ DWORD CALLBACK copyCallbackInternal( NULL, //__out_opt LPFILETIME lpLastAccessTime, NULL)) //__out_opt LPFILETIME lpLastWriteTime { - cbd.reportError(_("Error reading file attributes:") + "\n\"" + cbd.sourceFile_ + "\"" + "\n\n" + getLastErrorFormatted()); + cbd.reportError(_("Error reading file attributes:") + L"\n\"" + cbd.sourceFile_ + L"\"" + L"\n\n" + getLastErrorFormatted()); return PROGRESS_CANCEL; } @@ -1419,7 +1487,7 @@ DWORD CALLBACK copyCallbackInternal( NULL, NULL)) { - cbd.reportError(_("Error changing modification time:") + "\n\"" + cbd.targetFile_ + "\"" + "\n\n" + getLastErrorFormatted()); + cbd.reportError(_("Error changing modification time:") + L"\n\"" + cbd.targetFile_ + L"\"" + L"\n\n" + getLastErrorFormatted()); return PROGRESS_CANCEL; } //############################################################################## @@ -1498,8 +1566,8 @@ void rawCopyWinApi_sub(const Zstring& sourceFile, //don't suppress "lastError == ERROR_REQUEST_ABORTED": a user aborted operation IS an error condition! //assemble error message... - std::wstring errorMessage = _("Error copying file:") + "\n\"" + sourceFile + "\" ->\n\"" + targetFile + "\"" + - "\n\n" + getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error copying file:") + L"\n\"" + sourceFile + L"\" ->\n\"" + targetFile + L"\"" + + L"\n\n" + getLastErrorFormatted(lastError); //if file is locked (try to) use Windows Volume Shadow Copy Service if (lastError == ERROR_SHARING_VIOLATION || @@ -1946,7 +2014,7 @@ void rawCopyStream(const Zstring& sourceFile, { struct stat srcInfo = {}; if (::stat(sourceFile.c_str(), &srcInfo) != 0) //read file attributes from source directory - throw FileError(_("Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + L"\n\"" + sourceFile + L"\"" + L"\n\n" + getLastErrorFormatted()); if (sourceAttr) { @@ -1960,7 +2028,7 @@ void rawCopyStream(const Zstring& sourceFile, //set new "last write time" if (::utime(targetFile.c_str(), &newTimes) != 0) - throw FileError(_("Error changing modification time:") + "\n\"" + targetFile + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error changing modification time:") + L"\n\"" + targetFile + L"\"" + L"\n\n" + getLastErrorFormatted()); } guardTarget.dismiss(); //target has been created successfully! @@ -2055,6 +2123,17 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath { zen::ScopeGuard guardTargetFile = zen::makeGuard([&]() { removeFile(targetFile);}); copyObjectPermissions(sourceFile, targetFile, SYMLINK_FOLLOW); //throw FileError + + + /* + warn_static("fix!!") + try + { + copyObjectPermissions(targetFile, L"kfsdaj", SYMLINK_FOLLOW); //throw FileError + } + catch (...) {} + */ + guardTargetFile.dismiss(); //target has been created successfully! } } diff --git a/zen/file_handling.h b/zen/file_handling.h index baa51516..02f3e532 100644 --- a/zen/file_handling.h +++ b/zen/file_handling.h @@ -7,7 +7,7 @@ #ifndef FILE_HANDLING_H_INCLUDED #define FILE_HANDLING_H_INCLUDED -#include "string.h" +#include "zstring.h" #include "file_error.h" #include "int64.h" diff --git a/zen/file_id.cpp b/zen/file_id.cpp index c9f422ac..acbdcd7b 100644 --- a/zen/file_id.cpp +++ b/zen/file_id.cpp @@ -47,7 +47,6 @@ std::string zen::getFileID(const Zstring& filename) return extractFileID(fileInfo); #endif - assert(false); return std::string(); } diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 6b3a5214..ba8ab955 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -8,6 +8,7 @@ #ifdef FFS_WIN #include "long_path_prefix.h" + #elif defined FFS_LINUX #include <cerrno> #endif @@ -61,7 +62,7 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExis { const DWORD lastError = ::GetLastError(); - std::wstring errorMessage = _("Error opening file:") + "\n\"" + filename_ + "\"" + "\n\n" + zen::getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error opening file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted(lastError); if (lastError == ERROR_FILE_NOT_FOUND || lastError == ERROR_PATH_NOT_FOUND || @@ -78,7 +79,7 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExis { const int lastError = errno; - std::wstring errorMessage = _("Error opening file:") + "\n\"" + filename_ + "\"" + "\n\n" + zen::getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error opening file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted(lastError); if (lastError == ENOENT) throw ErrorNotExisting(errorMessage); @@ -112,7 +113,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number const size_t bytesRead = ::fread(buffer, 1, bytesToRead, fileHandle); if (::ferror(fileHandle) != 0) #endif - throw FileError(_("Error reading file:") + "\n\"" + filename_ + "\"" + "\n\n" + zen::getLastErrorFormatted() + " (r)"); + throw FileError(_("Error reading file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted() + L" (r)"); #ifdef FFS_WIN if (bytesRead < bytesToRead) //falsify only! @@ -122,7 +123,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number eofReached = true; if (bytesRead > bytesToRead) - throw FileError(_("Error reading file:") + "\n\"" + filename_ + "\"" + "\n\n" + "buffer overflow"); + throw FileError(_("Error reading file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + L"buffer overflow"); return bytesRead; } @@ -156,7 +157,7 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil if (fileHandle == INVALID_HANDLE_VALUE) { const DWORD lastError = ::GetLastError(); - std::wstring errorMessage = _("Error writing file:") + "\n\"" + filename_ + "\"" + "\n\n" + zen::getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error writing file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted(lastError); if (lastError == ERROR_FILE_EXISTS) throw ErrorTargetExisting(errorMessage); @@ -174,7 +175,7 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil if (fileHandle == NULL) { const int lastError = errno; - std::wstring errorMessage = _("Error writing file:") + "\n\"" + filename_ + "\"" + "\n\n" + zen::getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error writing file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted(lastError); if (lastError == EEXIST) throw ErrorTargetExisting(errorMessage); @@ -210,8 +211,8 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro const size_t bytesWritten = ::fwrite(buffer, 1, bytesToWrite, fileHandle); if (::ferror(fileHandle) != 0) #endif - throw FileError(_("Error writing file:") + "\n\"" + filename_ + "\"" + "\n\n" + zen::getLastErrorFormatted() + " (w)"); //w -> distinguish from fopen error message! + throw FileError(_("Error writing file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted() + L" (w)"); //w -> distinguish from fopen error message! if (bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes! - throw FileError(_("Error writing file:") + "\n\"" + filename_ + "\"" + "\n\n" + "incomplete write"); + throw FileError(_("Error writing file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + L"incomplete write"); } diff --git a/zen/file_io.h b/zen/file_io.h index 26964ae8..7ce6d901 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -14,7 +14,7 @@ #include <cstdio> #endif -#include "string.h" +#include "zstring.h" #include "file_error.h" namespace zen diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index 44b9d184..7a2df695 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -131,7 +131,7 @@ private: { if (level == 100) //notify endless recursion { - errorMsg = _("Endless loop when traversing directory:") + "\n\"" + directory + "\""; + errorMsg = _("Endless loop when traversing directory:") + L"\n\"" + directory + L"\""; return false; } return true; @@ -221,16 +221,9 @@ private: } else if (fileInfo.fileAttributes & FILE_ATTRIBUTE_DIRECTORY) //a directory... or symlink that needs to be followed (for directory symlinks this flag is set too!) { - const TraverseCallback::ReturnValDir rv = sink.onDir(shortName, fullName); - switch (rv.returnCode) - { - case TraverseCallback::ReturnValDir::TRAVERSING_DIR_IGNORE: - break; - - case TraverseCallback::ReturnValDir::TRAVERSING_DIR_CONTINUE: - traverse<followSymlinks_>(fullName, *rv.subDirCb, level + 1); - break; - } + const std::shared_ptr<TraverseCallback> rv = sink.onDir(shortName, fullName); + if (rv) + traverse<followSymlinks_>(fullName, *rv, level + 1); } else //a file or symlink that is followed... { @@ -289,7 +282,7 @@ private: //return true; //fine: empty directory //else: we have a problem... report it: - errorMsg = _("Error traversing directory:") + "\n\"" + directory + "\"" + "\n\n" + zen::getLastErrorFormatted(); + errorMsg = _("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted(); return false; } return true; @@ -322,7 +315,7 @@ private: { (void)e; #ifndef NDEBUG //show broken symlink / access errors in debug build! - sink.onError(e.msg()); + sink.onError(e.toString()); #endif } @@ -332,16 +325,9 @@ private: } else if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //a directory... or symlink that needs to be followed (for directory symlinks this flag is set too!) { - const TraverseCallback::ReturnValDir rv = sink.onDir(shortName, fullName); - switch (rv.returnCode) - { - case TraverseCallback::ReturnValDir::TRAVERSING_DIR_IGNORE: - break; - - case TraverseCallback::ReturnValDir::TRAVERSING_DIR_CONTINUE: - traverse(fullName, *rv.subDirCb, level + 1); - break; - } + const std::shared_ptr<TraverseCallback> rv = sink.onDir(shortName, fullName); + if (rv) + traverse(fullName, *rv, level + 1); } else //a file or symlink that is followed... { @@ -389,7 +375,7 @@ private: return true; //else we have a problem... report it: - errorMsg = _("Error traversing directory:") + "\n\"" + directory + "\"" + "\n\n" + zen::getLastErrorFormatted(); + errorMsg = _("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted(); return false; } @@ -407,7 +393,7 @@ private: dirObj = ::opendir(directory.c_str()); //directory must NOT end with path separator, except "/" if (dirObj == NULL) { - errorMsg = _("Error traversing directory:") + "\n\"" + directory + "\"" + "\n\n" + zen::getLastErrorFormatted(); + errorMsg = _("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted(); return false; } return true; @@ -428,7 +414,7 @@ private: return true; //everything okay, not more items //else: we have a problem... report it: - errorMsg = _("Error traversing directory:") + "\n\"" + directory + "\"" + "\n\n" + zen::getLastErrorFormatted(); + errorMsg = _("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted(); return false; } return true; @@ -453,7 +439,7 @@ private: { if (::lstat(fullName.c_str(), &fileInfo) != 0) //lstat() does not resolve symlinks { - errorMsg = _("Error reading file attributes:") + "\n\"" + fullName + "\"" + "\n\n" + zen::getLastErrorFormatted(); + errorMsg = _("Error reading file attributes:") + L"\n\"" + fullName + L"\"" + L"\n\n" + zen::getLastErrorFormatted(); return false; } return true; @@ -482,7 +468,7 @@ private: catch (FileError& e) { #ifndef NDEBUG //show broken symlink / access errors in debug build! - sink.onError(e.msg()); + sink.onError(e.toString()); #endif } @@ -497,16 +483,9 @@ private: if (S_ISDIR(fileInfo.st_mode)) //a directory... cannot be a symlink on Linux in this case { - const TraverseCallback::ReturnValDir rv = sink.onDir(shortName, fullName); - switch (rv.returnCode) - { - case TraverseCallback::ReturnValDir::TRAVERSING_DIR_IGNORE: - break; - - case TraverseCallback::ReturnValDir::TRAVERSING_DIR_CONTINUE: - traverse(fullName, *rv.subDirCb, level + 1); - break; - } + const std::shared_ptr<TraverseCallback> rv = sink.onDir(shortName, fullName); + if (rv) + traverse(fullName, *rv, level + 1); } else //a file... (or symlink; pathological!) { diff --git a/zen/file_traverser.h b/zen/file_traverser.h index 4dbed9f9..3f4f47d5 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -7,8 +7,8 @@ #ifndef FILETRAVERSER_H_INCLUDED #define FILETRAVERSER_H_INCLUDED +#include <memory> #include "zstring.h" -#include "type_tools.h" #include "int64.h" //advanced file traverser returning metadata and hierarchical information on files and directories @@ -28,24 +28,9 @@ public: struct SymlinkInfo { - Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC - Zstring targetPath; //may be empty if something goes wrong - bool dirLink; //"true": point to dir; "false": point to file (or broken Link on Linux) - }; - - struct ReturnValDir - { - enum ReturnValueEnh - { - TRAVERSING_DIR_IGNORE, - TRAVERSING_DIR_CONTINUE - }; - - ReturnValDir(Int2Type<TRAVERSING_DIR_IGNORE>) : returnCode(TRAVERSING_DIR_IGNORE), subDirCb(NULL) {} - ReturnValDir(Int2Type<TRAVERSING_DIR_CONTINUE>, TraverseCallback& subDirCallback) : returnCode(TRAVERSING_DIR_CONTINUE), subDirCb(&subDirCallback) {} - - ReturnValueEnh returnCode; - TraverseCallback* subDirCb; + Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC + Zstring targetPath; //may be empty if something goes wrong + bool dirLink; //"true": point to dir; "false": point to file (or broken Link on Linux) }; enum HandleError @@ -55,10 +40,11 @@ public: }; //overwrite these virtual methods - virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) = 0; - virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) = 0; - virtual ReturnValDir onDir (const Zchar* shortName, const Zstring& fullName) = 0; - virtual HandleError onError (const std::wstring& errorText) = 0; + virtual std::shared_ptr<TraverseCallback> //nullptr: ignore directory, non-nullptr: traverse into + /**/ onDir (const Zchar* shortName, const Zstring& fullName) = 0; + virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) = 0; + virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) = 0; + virtual HandleError onError (const std::wstring& errorText) = 0; }; diff --git a/zen/fixed_list.h b/zen/fixed_list.h index c17ce0da..e80adb99 100644 --- a/zen/fixed_list.h +++ b/zen/fixed_list.h @@ -11,7 +11,7 @@ namespace zen { -//std::list(C++11) compatible class supporting inplace element construction for non-copyable/movable types +//std::list(C++11) compatible class for inplace element construction supporting non-copyable/movable types //may be replaced by C++11 std::list when available template <class T> class FixedList diff --git a/zen/int64.h b/zen/int64.h index f7a7e800..e8a9cbe0 100644 --- a/zen/int64.h +++ b/zen/int64.h @@ -52,7 +52,7 @@ public: Int64(const Int64& rhs) : value(rhs.value) {} Int64(int rhs) : value(rhs) {} //ambiguity intentional for types other than these Int64(long rhs) : value(rhs) {} - Int64(Select<IsSameType<std::int64_t, long>::result, DummyClass, std::int64_t>::Result rhs) : + Int64(SelectIf<IsSameType<std::int64_t, long>::result, DummyClass, std::int64_t>::Result rhs) : value(rhs) {} //-> std::int64_t equals long int on x64 Linux! Still we want implicit behavior for all other systems! //unsafe explicit but checked conversion from arbitrary integer type @@ -111,13 +111,13 @@ private: std::int64_t value; }; -inline Int64 operator+(const Int64& lhs, const Int64& rhs) { return Int64(lhs) += rhs; } -inline Int64 operator-(const Int64& lhs, const Int64& rhs) { return Int64(lhs) -= rhs; } -inline Int64 operator*(const Int64& lhs, const Int64& rhs) { return Int64(lhs) *= rhs; } -inline Int64 operator/(const Int64& lhs, const Int64& rhs) { return Int64(lhs) /= rhs; } -inline Int64 operator%(const Int64& lhs, const Int64& rhs) { return Int64(lhs) %= rhs; } -inline Int64 operator&(const Int64& lhs, const Int64& rhs) { return Int64(lhs) &= rhs; } -inline Int64 operator|(const Int64& lhs, const Int64& rhs) { return Int64(lhs) |= rhs; } +inline Int64 operator+ (const Int64& lhs, const Int64& rhs) { return Int64(lhs) += rhs; } +inline Int64 operator- (const Int64& lhs, const Int64& rhs) { return Int64(lhs) -= rhs; } +inline Int64 operator* (const Int64& lhs, const Int64& rhs) { return Int64(lhs) *= rhs; } +inline Int64 operator/ (const Int64& lhs, const Int64& rhs) { return Int64(lhs) /= rhs; } +inline Int64 operator% (const Int64& lhs, const Int64& rhs) { return Int64(lhs) %= rhs; } +inline Int64 operator& (const Int64& lhs, const Int64& rhs) { return Int64(lhs) &= rhs; } +inline Int64 operator| (const Int64& lhs, const Int64& rhs) { return Int64(lhs) |= rhs; } inline Int64 operator<<(const Int64& lhs, int rhs) { return Int64(lhs) <<= rhs; } inline Int64 operator>>(const Int64& lhs, int rhs) { return Int64(lhs) >>= rhs; } @@ -131,7 +131,7 @@ public: UInt64(const UInt64& rhs) : value(rhs.value) {} UInt64(unsigned int rhs) : value(rhs) {} //ambiguity intentional for types other than these UInt64(unsigned long rhs) : value(rhs) {} - UInt64(Select<IsSameType<std::uint64_t, unsigned long>::result, DummyClass, std::uint64_t>::Result rhs) : + UInt64(SelectIf<IsSameType<std::uint64_t, unsigned long>::result, DummyClass, std::uint64_t>::Result rhs) : value(rhs) {} //-> std::uint64_t equals unsigned long int on x64 Linux! Still we want implicit behavior for all other systems! //unsafe explicit but checked conversion from arbitrary integer type @@ -190,13 +190,13 @@ private: std::uint64_t value; }; -inline UInt64 operator+(const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) += rhs; } -inline UInt64 operator-(const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) -= rhs; } -inline UInt64 operator*(const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) *= rhs; } -inline UInt64 operator/(const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) /= rhs; } -inline UInt64 operator%(const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) %= rhs; } -inline UInt64 operator&(const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) &= rhs; } -inline UInt64 operator|(const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) |= rhs; } +inline UInt64 operator+ (const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) += rhs; } +inline UInt64 operator- (const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) -= rhs; } +inline UInt64 operator* (const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) *= rhs; } +inline UInt64 operator/ (const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) /= rhs; } +inline UInt64 operator% (const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) %= rhs; } +inline UInt64 operator& (const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) &= rhs; } +inline UInt64 operator| (const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) |= rhs; } inline UInt64 operator<<(const UInt64& lhs, int rhs) { return UInt64(lhs) <<= rhs; } inline UInt64 operator>>(const UInt64& lhs, int rhs) { return UInt64(lhs) >>= rhs; } diff --git a/zen/notify_removal.cpp b/zen/notify_removal.cpp index 805c1c09..8d8eeb90 100644 --- a/zen/notify_removal.cpp +++ b/zen/notify_removal.cpp @@ -96,7 +96,7 @@ MessageProvider::MessageProvider() : windowHandle(NULL) { if (process == NULL) - throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + "\n\n" + getLastErrorFormatted() + " (GetModuleHandle)"); + throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (GetModuleHandle)"); //register the main window class WNDCLASS wc = {}; @@ -105,7 +105,7 @@ MessageProvider::MessageProvider() : wc.lpszClassName = WINDOW_NAME; if (::RegisterClass(&wc) == 0) - throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + "\n\n" + getLastErrorFormatted() + " (RegisterClass)"); + throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (RegisterClass)"); zen::ScopeGuard guardClass = zen::makeGuard([&]() { ::UnregisterClass(WINDOW_NAME, process); }); @@ -123,7 +123,7 @@ MessageProvider::MessageProvider() : process, //HINSTANCE hInstance, NULL); //LPVOID lpParam if (windowHandle == NULL) - throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + "\n\n" + getLastErrorFormatted() + " (CreateWindow)"); + throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (CreateWindow)"); guardClass.dismiss(); } @@ -170,7 +170,7 @@ public: if (lastError != ERROR_CALL_NOT_IMPLEMENTED && //fail on SAMBA share: this shouldn't be a showstopper! lastError != ERROR_SERVICE_SPECIFIC_ERROR && //neither should be fail for "Pogoplug" mapped network drives lastError != ERROR_INVALID_DATA) //this seems to happen for a NetDrive-mapped FTP server - throw zen::FileError(std::wstring(L"Could not register device removal notifications:") + "\n\n" + getLastErrorFormatted(lastError)); + throw zen::FileError(std::wstring(L"Could not register device removal notifications:") + L"\n\n" + getLastErrorFormatted(lastError)); } } @@ -8,15 +8,9 @@ #define DEBUG_PERF_HEADER #include <sstream> +#include "deprecate.h" #include "win.h" //includes "windows.h" -#ifdef __MINGW32__ -#define DEPRECATED(x) x __attribute__ ((deprecated)) -#elif defined _MSC_VER -#define DEPRECATED(x) __declspec(deprecated) x -#endif - - //two macros for quick performance measurements #define PERF_START CpuTimer perfTest; #define PERF_STOP perfTest.showResult(); @@ -26,7 +20,8 @@ class CpuTimer public: class TimerError {}; - DEPRECATED(CpuTimer()) : frequency(), startTime(), resultShown(false) + ZEN_DEPRECATE + CpuTimer() : frequency(), startTime(), resultShown(false) { SetThreadAffinity dummy; if (!::QueryPerformanceFrequency(&frequency)) throw TimerError(); diff --git a/zen/privilege.cpp b/zen/privilege.cpp index 495b1254..809202b7 100644 --- a/zen/privilege.cpp +++ b/zen/privilege.cpp @@ -17,15 +17,14 @@ bool Privileges::privilegeIsActive(LPCTSTR privilege) //throw FileError if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, TOKEN_QUERY, //__in DWORD DesiredAccess, &hToken)) //__out PHANDLE TokenHandle - throw FileError(_("Error setting privilege:") + " \"" + privilege + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); ZEN_ON_BLOCK_EXIT(::CloseHandle(hToken)); LUID luid = {}; - if (!::LookupPrivilegeValue( - NULL, //__in_opt LPCTSTR lpSystemName, - privilege, //__in LPCTSTR lpName, - &luid )) //__out PLUID lpLuid - throw FileError(_("Error setting privilege:") + " \"" + privilege + "\"" + "\n\n" + getLastErrorFormatted()); + if (!::LookupPrivilegeValue(NULL, //__in_opt LPCTSTR lpSystemName, + privilege, //__in LPCTSTR lpName, + &luid )) //__out PLUID lpLuid + throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); PRIVILEGE_SET priv = {}; priv.PrivilegeCount = 1; @@ -34,11 +33,10 @@ bool Privileges::privilegeIsActive(LPCTSTR privilege) //throw FileError priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; BOOL alreadyGranted = FALSE; - if (!::PrivilegeCheck( - hToken, //__in HANDLE ClientToken, - &priv, //__inout PPRIVILEGE_SET RequiredPrivileges, - &alreadyGranted)) //__out LPBOOL pfResult - throw FileError(_("Error setting privilege:") + " \"" + privilege + "\"" + "\n\n" + getLastErrorFormatted()); + if (!::PrivilegeCheck(hToken, //__in HANDLE ClientToken, + &priv, //__inout PPRIVILEGE_SET RequiredPrivileges, + &alreadyGranted)) //__out LPBOOL pfResult + throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); return alreadyGranted == TRUE; } @@ -50,15 +48,14 @@ void Privileges::setPrivilege(LPCTSTR privilege, bool enable) //throw FileError if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, TOKEN_ADJUST_PRIVILEGES, //__in DWORD DesiredAccess, &hToken)) //__out PHANDLE TokenHandle - throw FileError(_("Error setting privilege:") + " \"" + privilege + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); ZEN_ON_BLOCK_EXIT(::CloseHandle(hToken)); LUID luid = {}; - if (!::LookupPrivilegeValue( - NULL, //__in_opt LPCTSTR lpSystemName, - privilege, //__in LPCTSTR lpName, - &luid )) //__out PLUID lpLuid - throw FileError(_("Error setting privilege:") + " \"" + privilege + "\"" + "\n\n" + getLastErrorFormatted()); + if (!::LookupPrivilegeValue(NULL, //__in_opt LPCTSTR lpSystemName, + privilege, //__in LPCTSTR lpName, + &luid )) //__out PLUID lpLuid + throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); TOKEN_PRIVILEGES tp = {}; tp.PrivilegeCount = 1; @@ -72,8 +69,8 @@ void Privileges::setPrivilege(LPCTSTR privilege, bool enable) //throw FileError 0, //__in DWORD BufferLength, NULL, //__out_opt PTOKEN_PRIVILEGES PreviousState, NULL)) //__out_opt PDWORD ReturnLength - throw FileError(_("Error setting privilege:") + " \"" + privilege + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); if (::GetLastError() == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success! - throw FileError(_("Error setting privilege:") + " \"" + privilege + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); } diff --git a/zen/process_status.h b/zen/process_status.h new file mode 100644 index 00000000..cc5825aa --- /dev/null +++ b/zen/process_status.h @@ -0,0 +1,33 @@ +#ifndef PREVENTSTANDBY_H_INCLUDED +#define PREVENTSTANDBY_H_INCLUDED + +#ifdef FFS_WIN +#include "win.h" //includes "windows.h" +#endif + +namespace zen +{ +struct DisableStandby //signal a "busy" state to the operating system +{ +#ifdef FFS_WIN + DisableStandby() { ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED /* | ES_AWAYMODE_REQUIRED*/ ); } + ~DisableStandby() { ::SetThreadExecutionState(ES_CONTINUOUS); } +#endif +}; + + +#ifndef PROCESS_MODE_BACKGROUND_BEGIN +#define PROCESS_MODE_BACKGROUND_BEGIN 0x00100000 // Windows Server 2003 and Windows XP/2000: This value is not supported! +#define PROCESS_MODE_BACKGROUND_END 0x00200000 // +#endif + +struct ScheduleForBackgroundProcessing //lower CPU and file I/O priorities +{ +#ifdef FFS_WIN + ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN); } + ~ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END); } +#endif +}; +} + +#endif // PREVENTSTANDBY_H_INCLUDED diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 6cfe35f8..1707a1eb 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -1,16 +1,68 @@ // ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// * This file is part of the zenXML project. It is distributed under the * +// * Boost Software License, Version 1.0. See accompanying file * +// * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt. * +// * Copyright (C) 2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** #ifndef STL_TOOLS_HEADER_84567184321434 #define STL_TOOLS_HEADER_84567184321434 -//no need to drag in any STL includes :) +//no need to drag in any STL includes + +//enhancements for <algorithm> namespace zen { +//idomatic remove selected elements from container +template <class V, class Predicate> +void vector_remove_if(V& vec, Predicate p); + +template <class S, class Predicate> +void set_remove_if(S& set, Predicate p); + +template <class M, class Predicate> +void map_remove_if(M& map, Predicate p); + +//binary search returning an iterator +template <class ForwardIterator, class T, typename Compare> +ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp); + +template<class BidirectionalIterator, class T> +BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value); + +//replacement for std::find_end taking advantage of bidirectional iterators (and giving the algorithm a reasonable name) +template<class BidirectionalIterator1, class BidirectionalIterator2> +BidirectionalIterator1 search_last(BidirectionalIterator1 first1, BidirectionalIterator1 last1, + BidirectionalIterator2 first2, BidirectionalIterator2 last2); + + + + + + + + + + + + + + + + + + + + + + + + + + +//######################## implementation ######################## + template <class V, class Predicate> inline void vector_remove_if(V& vec, Predicate p) { @@ -33,9 +85,8 @@ template <class M, class Predicate> inline void map_remove_if(M& map, Predicate p) { set_remove_if(map, p); } -// binary search returning an iterator template <class ForwardIterator, class T, typename Compare> inline -ForwardIterator custom_binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp) +ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp) { first = std::lower_bound(first, last, value, comp); if (first != last && !comp(value, *first)) @@ -43,7 +94,48 @@ ForwardIterator custom_binary_search(ForwardIterator first, ForwardIterator last else return last; } + + +template<class BidirectionalIterator, class T> inline +BidirectionalIterator find_last(const BidirectionalIterator first, BidirectionalIterator last, const T& value) +{ + const BidirectionalIterator iterNotFound = last; + do + { + if (last == first) + return iterNotFound; + --last; + } + while (!(*last == value)); //loop over "last": 1. check 2. decrement 3. evaluate + return last; } -#endif //STL_TOOLS_HEADER_84567184321434
\ No newline at end of file +template<class BidirectionalIterator1, class BidirectionalIterator2> inline +BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1, + const BidirectionalIterator2 first2, BidirectionalIterator2 last2) +{ + if (first1 == last1 || + first2 == last2) + return last1; + + int diff = static_cast<int>(std::distance(first1, last1)) - + static_cast<int>(std::distance(first2, last2)); + + const BidirectionalIterator1 iterNotFound = last1; + --last2; + + while (diff-- >= 0) //loop over "last1": 1. check 2. decrement 3. evaluate + { + --last1; + + BidirectionalIterator1 iter1 = last1; + for (BidirectionalIterator2 iter2 = last2; *iter1 == *iter2; --iter1, --iter2) + if (iter2 == first2) + return iter1; + } + return iterNotFound; +} +} + +#endif //STL_TOOLS_HEADER_84567184321434 diff --git a/zen/string_base.h b/zen/string_base.h index 914e0434..ffc2f839 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -12,9 +12,11 @@ #include "string_tools.h" #include <boost/detail/atomic_count.hpp> -//Zbase - a policy based string class +//Zbase - a policy based string class optimizing performance and genericity +namespace zen +{ /* Allocator Policy: ----------------- @@ -28,7 +30,7 @@ public: //::operator new/ ::operator delete show same performance characterisics like malloc()/free()! static void* allocate(size_t size) { return ::operator new(size); } //throw (std::bad_alloc) static void deallocate(void* ptr) { ::operator delete(ptr); } - static size_t calcCapacity(size_t length) { return std::max<size_t>(16, length + length / 2); } + static size_t calcCapacity(size_t length) { return std::max<size_t>(16, length + length / 2); } //any growth rate should not exceed golden ratio: 1.618033989 }; @@ -43,54 +45,54 @@ public: /* Storage Policy: --------------- -template <typename T, //Character Type - class AP> //Allocator Policy - - T* create(size_t size) - T* create(size_t size, size_t minCapacity) - T* clone(T* ptr) - void destroy(T* ptr) - bool canWrite(const T* ptr, size_t minCapacity) //needs to be checked before writing to "ptr" - size_t length(const T* ptr) - void setLength(T* ptr, size_t newLength) +template <typename Char, //Character Type + class AP> //Allocator Policy + + Char* create(size_t size) + Char* create(size_t size, size_t minCapacity) + Char* clone(Char* ptr) + void destroy(Char* ptr) + bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr" + size_t length(const Char* ptr) + void setLength(Char* ptr, size_t newLength) */ -template <typename T, //Character Type - class AP> //Allocator Policy +template <typename Char, //Character Type + class AP> //Allocator Policy class StorageDeepCopy : public AP { protected: ~StorageDeepCopy() {} - static T* create(size_t size) { return create(size, size); } - static T* create(size_t size, size_t minCapacity) + static Char* create(size_t size) { return create(size, size); } + static Char* create(size_t size, size_t minCapacity) { const size_t newCapacity = AP::calcCapacity(minCapacity); assert(newCapacity >= minCapacity); assert(minCapacity >= size); - Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(T))); + Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); newDescr->length = size; newDescr->capacity = newCapacity; - return reinterpret_cast<T*>(newDescr + 1); + return reinterpret_cast<Char*>(newDescr + 1); } - static T* clone(T* ptr) + static Char* clone(Char* ptr) { - T* newData = create(length(ptr)); + Char* newData = create(length(ptr)); std::copy(ptr, ptr + length(ptr) + 1, newData); return newData; } - static void destroy(T* ptr) { AP::deallocate(descr(ptr)); } + static void destroy(Char* ptr) { AP::deallocate(descr(ptr)); } //this needs to be checked before writing to "ptr" - static bool canWrite(const T* ptr, size_t minCapacity) { return minCapacity <= descr(ptr)->capacity; } - static size_t length(const T* ptr) { return descr(ptr)->length; } + static bool canWrite(const Char* ptr, size_t minCapacity) { return minCapacity <= descr(ptr)->capacity; } + static size_t length(const Char* ptr) { return descr(ptr)->length; } - static void setLength(T* ptr, size_t newLength) + static void setLength(Char* ptr, size_t newLength) { assert(canWrite(ptr, newLength)); descr(ptr)->length = newLength; @@ -103,43 +105,43 @@ private: size_t capacity; //allocated size without null-termination }; - static Descriptor* descr( T* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } - static const Descriptor* descr(const T* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; } + static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } + static const Descriptor* descr(const Char* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; } }; -template <typename T, //Character Type - class AP> //Allocator Policy +template <typename Char, //Character Type + class AP> //Allocator Policy class StorageRefCountThreadSafe : public AP { protected: ~StorageRefCountThreadSafe() {} - static T* create(size_t size) + static Char* create(size_t size) { return create(size, size); } - static T* create(size_t size, size_t minCapacity) + static Char* create(size_t size, size_t minCapacity) { const size_t newCapacity = AP::calcCapacity(minCapacity); assert(newCapacity >= minCapacity); assert(minCapacity >= size); - Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(T))); + Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); new (newDescr) Descriptor(1, size, newCapacity); - return reinterpret_cast<T*>(newDescr + 1); + return reinterpret_cast<Char*>(newDescr + 1); } - static T* clone(T* ptr) + static Char* clone(Char* ptr) { assert(descr(ptr)->refCount > 0); ++descr(ptr)->refCount; return ptr; } - static void destroy(T* ptr) + static void destroy(Char* ptr) { if (--descr(ptr)->refCount == 0) { @@ -148,18 +150,18 @@ protected: } } - static bool canWrite(const T* ptr, size_t minCapacity) //needs to be checked before writing to "ptr" + static bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr" { assert(descr(ptr)->refCount > 0); return descr(ptr)->refCount == 1 && minCapacity <= descr(ptr)->capacity; } - static size_t length(const T* ptr) + static size_t length(const Char* ptr) { return descr(ptr)->length; } - static void setLength(T* ptr, size_t newLength) + static void setLength(Char* ptr, size_t newLength) { assert(canWrite(ptr, newLength)); descr(ptr)->length = newLength; @@ -175,97 +177,99 @@ private: size_t capacity; //allocated size without null-termination }; - static Descriptor* descr( T* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } - static const Descriptor* descr(const T* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; } + static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } + static const Descriptor* descr(const Char* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; } }; //################################################################################################################################################################ //perf note: interstingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison -template <class T, //Character Type +template <class Char, //Character Type template <class, class> class SP = StorageRefCountThreadSafe, //Storage Policy - class AP = AllocatorOptimalSpeed> //Allocator Policy -class Zbase : public SP<T, AP> + class AP = AllocatorOptimalSpeed> //Allocator Policy +class Zbase : public SP<Char, AP> { public: Zbase(); - Zbase(const T* source); //implicit conversion from a C-string - Zbase(const T* source, size_t length); + Zbase(const Char* source); //implicit conversion from a C-string + Zbase(const Char* source, size_t length); Zbase(const Zbase& source); Zbase(Zbase&& tmp); - explicit Zbase(T source); //dangerous if implicit: T buffer[]; Zbase name = buffer; ups... + explicit Zbase(Char source); //dangerous if implicit: Char buffer[]; Zbase name = buffer; ups... //allow explicit construction from different string type, prevent ambiguity via SFINAE template <class S> explicit Zbase(const S& other, typename S::value_type = 0); ~Zbase(); - //operator const T* () const; //NO implicit conversion to a C-string!! Many problems... one of them: if we forget to provide operator overloads, it'll just work with a T*... + //operator const Char* () const; //NO implicit conversion to a C-string!! Many problems... one of them: if we forget to provide operator overloads, it'll just work with a Char*... //STL accessors - typedef T* iterator; - typedef const T* const_iterator; - typedef T& reference; - typedef const T& const_reference; - typedef T value_type; - const T* begin() const; - const T* end() const; - T* begin(); - T* end(); + typedef Char* iterator; + typedef const Char* const_iterator; + typedef Char& reference; + typedef const Char& const_reference; + typedef Char value_type; + const Char* begin() const; + const Char* end() const; + Char* begin(); + Char* end(); //std::string functions size_t length() const; size_t size() const; - const T* c_str() const; //C-string format with NULL-termination - const T* data() const; //internal representation, NULL-termination not guaranteed - const T operator[](size_t pos) const; + const Char* c_str() const; //C-string format with NULL-termination + const Char* data() const; //internal representation, NULL-termination not guaranteed + const Char operator[](size_t pos) const; bool empty() const; void clear(); - size_t find(const Zbase& str, size_t pos = 0) const; // - size_t find(const T* str, size_t pos = 0) const; //returns "npos" if not found - size_t find(T ch, size_t pos = 0) const; // - size_t rfind(T ch, size_t pos = npos) const; // - size_t rfind(const T* str, size_t pos = npos) const; // + size_t find (const Zbase& str, size_t pos = 0) const; // + size_t find (const Char* str, size_t pos = 0) const; //returns "npos" if not found + size_t find (Char ch, size_t pos = 0) const; // + size_t rfind(Char ch, size_t pos = npos) const; // + size_t rfind(const Char* str, size_t pos = npos) const; // Zbase& replace(size_t pos1, size_t n1, const Zbase& str); void reserve(size_t minCapacity); - Zbase& assign(const T* source, size_t len); - void resize(size_t newSize, T fillChar = 0); + Zbase& assign(const Char* source, size_t len); + Zbase& append(const Char* source, size_t len); + void resize(size_t newSize, Char fillChar = 0); void swap(Zbase& other); - void push_back(T val); //STL access + void push_back(Char val); //STL access - Zbase& operator=(const Zbase& source); - Zbase& operator=(Zbase&& tmp); - Zbase& operator=(const T* source); - Zbase& operator=(T source); + Zbase& operator=(Zbase source); + Zbase& operator=(const Char* source); + Zbase& operator=(Char source); Zbase& operator+=(const Zbase& other); - Zbase& operator+=(const T* other); - Zbase& operator+=(T ch); + Zbase& operator+=(const Char* other); + Zbase& operator+=(Char ch); static const size_t npos = static_cast<size_t>(-1); private: - Zbase(int); //detect usage errors - Zbase& operator=(int); // + Zbase(int); // + Zbase& operator=(int); //detect usage errors + Zbase& operator+=(int); // + void push_back(int); // - T* rawStr; + Char* rawStr; }; -template <class T, template <class, class> class SP, class AP> bool operator==(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs); -template <class T, template <class, class> class SP, class AP> bool operator==(const Zbase<T, SP, AP>& lhs, const T* rhs); -template <class T, template <class, class> class SP, class AP> bool operator==(const T* lhs, const Zbase<T, SP, AP>& rhs); +template <class Char, template <class, class> class SP, class AP> bool operator==(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs); +template <class Char, template <class, class> class SP, class AP> bool operator==(const Zbase<Char, SP, AP>& lhs, const Char* rhs); +template <class Char, template <class, class> class SP, class AP> bool operator==(const Char* lhs, const Zbase<Char, SP, AP>& rhs); -template <class T, template <class, class> class SP, class AP> bool operator!=(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs); -template <class T, template <class, class> class SP, class AP> bool operator!=(const Zbase<T, SP, AP>& lhs, const T* rhs); -template <class T, template <class, class> class SP, class AP> bool operator!=(const T* lhs, const Zbase<T, SP, AP>& rhs); +template <class Char, template <class, class> class SP, class AP> bool operator!=(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs); +template <class Char, template <class, class> class SP, class AP> bool operator!=(const Zbase<Char, SP, AP>& lhs, const Char* rhs); +template <class Char, template <class, class> class SP, class AP> bool operator!=(const Char* lhs, const Zbase<Char, SP, AP>& rhs); -template <class T, template <class, class> class SP, class AP> bool operator< (const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs); -template <class T, template <class, class> class SP, class AP> bool operator< (const Zbase<T, SP, AP>& lhs, const T* rhs); -template <class T, template <class, class> class SP, class AP> bool operator< (const T* lhs, const Zbase<T, SP, AP>& rhs); +template <class Char, template <class, class> class SP, class AP> bool operator< (const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs); +template <class Char, template <class, class> class SP, class AP> bool operator< (const Zbase<Char, SP, AP>& lhs, const Char* rhs); +template <class Char, template <class, class> class SP, class AP> bool operator< (const Char* lhs, const Zbase<Char, SP, AP>& rhs); -template <class T, template <class, class> class SP, class AP> const Zbase<T, SP, AP> operator+(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs); -template <class T, template <class, class> class SP, class AP> const Zbase<T, SP, AP> operator+(const Zbase<T, SP, AP>& lhs, const T* rhs); -template <class T, template <class, class> class SP, class AP> const Zbase<T, SP, AP> operator+(const T* lhs, const Zbase<T, SP, AP>& rhs); -template <class T, template <class, class> class SP, class AP> const Zbase<T, SP, AP> operator+( T lhs, const Zbase<T, SP, AP>& rhs); -template <class T, template <class, class> class SP, class AP> const Zbase<T, SP, AP> operator+(const Zbase<T, SP, AP>& lhs, T rhs); +template <class Char, template <class, class> class SP, class AP> const Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs); +template <class Char, template <class, class> class SP, class AP> const Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, const Char* rhs); +template <class Char, template <class, class> class SP, class AP> const Zbase<Char, SP, AP> operator+(const Char* lhs, const Zbase<Char, SP, AP>& rhs); +template <class Char, template <class, class> class SP, class AP> const Zbase<Char, SP, AP> operator+( Char lhs, const Zbase<Char, SP, AP>& rhs); +template <class Char, template <class, class> class SP, class AP> const Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, Char rhs); @@ -304,8 +308,8 @@ template <class T, template <class, class> class SP, class AP> const Zbase<T, SP //################################# inline implementation ######################################## -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>::Zbase() +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>::Zbase() { //resist the temptation to avoid this allocation by referening a static global: NO performance advantage, MT issues! rawStr = this->create(0); @@ -313,8 +317,8 @@ Zbase<T, SP, AP>::Zbase() } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>::Zbase(T source) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>::Zbase(Char source) { rawStr = this->create(1); rawStr[0] = source; @@ -322,17 +326,17 @@ Zbase<T, SP, AP>::Zbase(T source) } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>::Zbase(const T* source) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>::Zbase(const Char* source) { - const size_t sourceLen = zen::strLength(source); + const size_t sourceLen = strLength(source); rawStr = this->create(sourceLen); std::copy(source, source + sourceLen + 1, rawStr); //include null-termination } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>::Zbase(const T* source, size_t sourceLen) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>::Zbase(const Char* source, size_t sourceLen) { rawStr = this->create(sourceLen); std::copy(source, source + sourceLen, rawStr); @@ -340,23 +344,23 @@ Zbase<T, SP, AP>::Zbase(const T* source, size_t sourceLen) } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>::Zbase(const Zbase<T, SP, AP>& source) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>::Zbase(const Zbase<Char, SP, AP>& source) { rawStr = this->clone(source.rawStr); } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>::Zbase(Zbase<T, SP, AP>&& tmp) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>::Zbase(Zbase<Char, SP, AP>&& tmp) { rawStr = this->clone(tmp.rawStr); //for a ref-counting string there probably isn't a faster way, even with r-value references } -template <class T, template <class, class> class SP, class AP> +template <class Char, template <class, class> class SP, class AP> template <class S> inline -Zbase<T, SP, AP>::Zbase(const S& other, typename S::value_type) +Zbase<Char, SP, AP>::Zbase(const S& other, typename S::value_type) { const size_t sourceLen = other.size(); rawStr = this->create(sourceLen); @@ -365,80 +369,73 @@ Zbase<T, SP, AP>::Zbase(const S& other, typename S::value_type) } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>::~Zbase() +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>::~Zbase() { this->destroy(rawStr); } -template <class T, template <class, class> class SP, class AP> inline -size_t Zbase<T, SP, AP>::find(const Zbase& str, size_t pos) const +template <class Char, template <class, class> class SP, class AP> inline +size_t Zbase<Char, SP, AP>::find(const Zbase& str, size_t pos) const { assert(pos <= length()); - const T* thisEnd = end(); //respect embedded 0 - const T* iter = std::search(begin() + pos, thisEnd, - str.begin(), str.end()); + const Char* thisEnd = end(); //respect embedded 0 + const Char* iter = std::search(begin() + pos, thisEnd, + str.begin(), str.end()); return iter == thisEnd ? npos : iter - begin(); } -template <class T, template <class, class> class SP, class AP> inline -size_t Zbase<T, SP, AP>::find(const T* str, size_t pos) const +template <class Char, template <class, class> class SP, class AP> inline +size_t Zbase<Char, SP, AP>::find(const Char* str, size_t pos) const { assert(pos <= length()); - const T* thisEnd = end(); //respect embedded 0 - const T* iter = std::search(begin() + pos, thisEnd, - str, str + zen::strLength(str)); + const Char* thisEnd = end(); //respect embedded 0 + const Char* iter = std::search(begin() + pos, thisEnd, + str, str + strLength(str)); return iter == thisEnd ? npos : iter - begin(); } -template <class T, template <class, class> class SP, class AP> inline -size_t Zbase<T, SP, AP>::find(T ch, size_t pos) const +template <class Char, template <class, class> class SP, class AP> inline +size_t Zbase<Char, SP, AP>::find(Char ch, size_t pos) const { assert(pos <= length()); - const T* thisEnd = end(); - const T* iter = std::find(begin() + pos, thisEnd, ch); //respect embedded 0 + const Char* thisEnd = end(); + const Char* iter = std::find(begin() + pos, thisEnd, ch); //respect embedded 0 return iter == thisEnd ? npos : iter - begin(); } -template <class T, template <class, class> class SP, class AP> inline -size_t Zbase<T, SP, AP>::rfind(T ch, size_t pos) const +template <class Char, template <class, class> class SP, class AP> inline +size_t Zbase<Char, SP, AP>::rfind(Char ch, size_t pos) const { assert(pos == npos || pos <= length()); - const size_t thisLen = length(); - if (thisLen == 0) return npos; - pos = std::min(thisLen - 1, pos); //handle "npos" and "pos == length()" implicitly + const Char* currEnd = pos == npos ? end() : begin() + std::min(pos + 1, length()); - while (rawStr[pos] != ch) //pos points to last char of the string - { - if (pos == 0) - return npos; - --pos; - } - return pos; + const Char* iter = find_last(begin(), currEnd, ch); + return iter == currEnd ? npos : iter - begin(); } -template <class T, template <class, class> class SP, class AP> inline -size_t Zbase<T, SP, AP>::rfind(const T* str, size_t pos) const +template <class Char, template <class, class> class SP, class AP> inline +size_t Zbase<Char, SP, AP>::rfind(const Char* str, size_t pos) const { assert(pos == npos || pos <= length()); - const size_t strLen = zen::strLength(str); - const T* currEnd = pos == npos ? end() : begin() + std::min(pos + strLen, length()); + const size_t strLen = strLength(str); + const Char* currEnd = pos == npos ? end() : begin() + std::min(pos + strLen, length()); - const T* iter = std::find_end(begin(), currEnd, - str, str + strLen); + const Char* iter = search_last(begin(), currEnd, + str, str + strLen); return iter == currEnd ? npos : iter - begin(); } -template <class T, template <class, class> class SP, class AP> -Zbase<T, SP, AP>& Zbase<T, SP, AP>::replace(size_t pos1, size_t n1, const Zbase& str) +template <class Char, template <class, class> class SP, class AP> +Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::replace(size_t pos1, size_t n1, const Zbase& str) { assert(str.data() < rawStr || rawStr + length() < str.data()); //str mustn't point to data in this string assert(pos1 + n1 <= length()); @@ -469,7 +466,7 @@ Zbase<T, SP, AP>& Zbase<T, SP, AP>::replace(size_t pos1, size_t n1, const Zbase& else { //copy directly into new string - T* const newStr = this->create(newLen); + Char* const newStr = this->create(newLen); std::copy(rawStr, rawStr + pos1, newStr); std::copy(str.data(), str.data() + n2, newStr + pos1); @@ -482,8 +479,8 @@ Zbase<T, SP, AP>& Zbase<T, SP, AP>::replace(size_t pos1, size_t n1, const Zbase& } -template <class T, template <class, class> class SP, class AP> inline -void Zbase<T, SP, AP>::resize(size_t newSize, T fillChar) +template <class Char, template <class, class> class SP, class AP> inline +void Zbase<Char, SP, AP>::resize(size_t newSize, Char fillChar) { if (canWrite(rawStr, newSize)) { @@ -494,7 +491,7 @@ void Zbase<T, SP, AP>::resize(size_t newSize, T fillChar) } else { - T* newStr = this->create(newSize); + Char* newStr = this->create(newSize); newStr[newSize] = 0; if (length() < newSize) @@ -511,153 +508,153 @@ void Zbase<T, SP, AP>::resize(size_t newSize, T fillChar) } -template <class T, template <class, class> class SP, class AP> inline -bool operator==(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) +template <class Char, template <class, class> class SP, class AP> inline +bool operator==(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs) { return lhs.length() == rhs.length() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); //respect embedded 0 } -template <class T, template <class, class> class SP, class AP> inline -bool operator==(const Zbase<T, SP, AP>& lhs, const T* rhs) +template <class Char, template <class, class> class SP, class AP> inline +bool operator==(const Zbase<Char, SP, AP>& lhs, const Char* rhs) { - return lhs.length() == zen::strLength(rhs) && std::equal(lhs.begin(), lhs.end(), rhs); //respect embedded 0 + return lhs.length() == strLength(rhs) && std::equal(lhs.begin(), lhs.end(), rhs); //respect embedded 0 } -template <class T, template <class, class> class SP, class AP> inline -bool operator==(const T* lhs, const Zbase<T, SP, AP>& rhs) +template <class Char, template <class, class> class SP, class AP> inline +bool operator==(const Char* lhs, const Zbase<Char, SP, AP>& rhs) { return operator==(rhs, lhs); } -template <class T, template <class, class> class SP, class AP> inline -bool operator!=(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) +template <class Char, template <class, class> class SP, class AP> inline +bool operator!=(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs) { return !operator==(lhs, rhs); } -template <class T, template <class, class> class SP, class AP> inline -bool operator!=(const Zbase<T, SP, AP>& lhs, const T* rhs) +template <class Char, template <class, class> class SP, class AP> inline +bool operator!=(const Zbase<Char, SP, AP>& lhs, const Char* rhs) { return !operator==(lhs, rhs); } -template <class T, template <class, class> class SP, class AP> inline -bool operator!=(const T* lhs, const Zbase<T, SP, AP>& rhs) +template <class Char, template <class, class> class SP, class AP> inline +bool operator!=(const Char* lhs, const Zbase<Char, SP, AP>& rhs) { return !operator==(lhs, rhs); } -template <class T, template <class, class> class SP, class AP> inline -bool operator<(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) +template <class Char, template <class, class> class SP, class AP> inline +bool operator<(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), //respect embedded 0 rhs.begin(), rhs.end()); } -template <class T, template <class, class> class SP, class AP> inline -bool operator<(const Zbase<T, SP, AP>& lhs, const T* rhs) +template <class Char, template <class, class> class SP, class AP> inline +bool operator<(const Zbase<Char, SP, AP>& lhs, const Char* rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), //respect embedded 0 - rhs, rhs + zen::strLength(rhs)); + rhs, rhs + strLength(rhs)); } -template <class T, template <class, class> class SP, class AP> inline -bool operator<(const T* lhs, const Zbase<T, SP, AP>& rhs) +template <class Char, template <class, class> class SP, class AP> inline +bool operator<(const Char* lhs, const Zbase<Char, SP, AP>& rhs) { - return std::lexicographical_compare(lhs, lhs + zen::strLength(lhs), //respect embedded 0 + return std::lexicographical_compare(lhs, lhs + strLength(lhs), //respect embedded 0 rhs.begin(), rhs.end()); } -template <class T, template <class, class> class SP, class AP> inline -size_t Zbase<T, SP, AP>::length() const +template <class Char, template <class, class> class SP, class AP> inline +size_t Zbase<Char, SP, AP>::length() const { - return SP<T, AP>::length(rawStr); + return SP<Char, AP>::length(rawStr); } -template <class T, template <class, class> class SP, class AP> inline -size_t Zbase<T, SP, AP>::size() const +template <class Char, template <class, class> class SP, class AP> inline +size_t Zbase<Char, SP, AP>::size() const { return length(); } -template <class T, template <class, class> class SP, class AP> inline -const T* Zbase<T, SP, AP>::c_str() const +template <class Char, template <class, class> class SP, class AP> inline +const Char* Zbase<Char, SP, AP>::c_str() const { return rawStr; } -template <class T, template <class, class> class SP, class AP> inline -const T* Zbase<T, SP, AP>::data() const +template <class Char, template <class, class> class SP, class AP> inline +const Char* Zbase<Char, SP, AP>::data() const { return rawStr; } -template <class T, template <class, class> class SP, class AP> inline -const T Zbase<T, SP, AP>::operator[](size_t pos) const +template <class Char, template <class, class> class SP, class AP> inline +const Char Zbase<Char, SP, AP>::operator[](size_t pos) const { assert(pos < length()); return rawStr[pos]; } -template <class T, template <class, class> class SP, class AP> inline -const T* Zbase<T, SP, AP>::begin() const +template <class Char, template <class, class> class SP, class AP> inline +const Char* Zbase<Char, SP, AP>::begin() const { return rawStr; } -template <class T, template <class, class> class SP, class AP> inline -const T* Zbase<T, SP, AP>::end() const +template <class Char, template <class, class> class SP, class AP> inline +const Char* Zbase<Char, SP, AP>::end() const { return rawStr + length(); } -template <class T, template <class, class> class SP, class AP> inline -T* Zbase<T, SP, AP>::begin() +template <class Char, template <class, class> class SP, class AP> inline +Char* Zbase<Char, SP, AP>::begin() { - reserve(length()); + reserve(length()); //make unshared! return rawStr; } -template <class T, template <class, class> class SP, class AP> inline -T* Zbase<T, SP, AP>::end() +template <class Char, template <class, class> class SP, class AP> inline +Char* Zbase<Char, SP, AP>::end() { return begin() + length(); } -template <class T, template <class, class> class SP, class AP> inline -void Zbase<T, SP, AP>::push_back(T val) +template <class Char, template <class, class> class SP, class AP> inline +void Zbase<Char, SP, AP>::push_back(Char val) { operator+=(val); } -template <class T, template <class, class> class SP, class AP> inline -bool Zbase<T, SP, AP>::empty() const +template <class Char, template <class, class> class SP, class AP> inline +bool Zbase<Char, SP, AP>::empty() const { return length() == 0; } -template <class T, template <class, class> class SP, class AP> inline -void Zbase<T, SP, AP>::clear() +template <class Char, template <class, class> class SP, class AP> inline +void Zbase<Char, SP, AP>::clear() { if (!empty()) { @@ -672,55 +669,55 @@ void Zbase<T, SP, AP>::clear() } -template <class T, template <class, class> class SP, class AP> inline -const Zbase<T, SP, AP> operator+(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) +template <class Char, template <class, class> class SP, class AP> inline +const Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs) { - return Zbase<T, SP, AP>(lhs) += rhs; + return Zbase<Char, SP, AP>(lhs) += rhs; } -template <class T, template <class, class> class SP, class AP> inline -const Zbase<T, SP, AP> operator+(const Zbase<T, SP, AP>& lhs, const T* rhs) +template <class Char, template <class, class> class SP, class AP> inline +const Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, const Char* rhs) { - return Zbase<T, SP, AP>(lhs) += rhs; + return Zbase<Char, SP, AP>(lhs) += rhs; } -template <class T, template <class, class> class SP, class AP> inline -const Zbase<T, SP, AP> operator+(const T* lhs, const Zbase<T, SP, AP>& rhs) +template <class Char, template <class, class> class SP, class AP> inline +const Zbase<Char, SP, AP> operator+(const Char* lhs, const Zbase<Char, SP, AP>& rhs) { - return Zbase<T, SP, AP>(lhs) += rhs; + return Zbase<Char, SP, AP>(lhs) += rhs; } -template <class T, template <class, class> class SP, class AP> inline -const Zbase<T, SP, AP> operator+(T lhs, const Zbase<T, SP, AP>& rhs) +template <class Char, template <class, class> class SP, class AP> inline +const Zbase<Char, SP, AP> operator+(Char lhs, const Zbase<Char, SP, AP>& rhs) { - return (Zbase<T, SP, AP>() += lhs) += rhs; + return Zbase<Char, SP, AP>(lhs) += rhs; } -template <class T, template <class, class> class SP, class AP> inline -const Zbase<T, SP, AP> operator+(const Zbase<T, SP, AP>& lhs, T rhs) +template <class Char, template <class, class> class SP, class AP> inline +const Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, Char rhs) { - return Zbase<T, SP, AP>(lhs) += rhs; + return Zbase<Char, SP, AP>(lhs) += rhs; } -template <class T, template <class, class> class SP, class AP> inline -void Zbase<T, SP, AP>::swap(Zbase<T, SP, AP>& other) +template <class Char, template <class, class> class SP, class AP> inline +void Zbase<Char, SP, AP>::swap(Zbase<Char, SP, AP>& other) { std::swap(rawStr, other.rawStr); } -template <class T, template <class, class> class SP, class AP> inline -void Zbase<T, SP, AP>::reserve(size_t minCapacity) //make unshared and check capacity +template <class Char, template <class, class> class SP, class AP> inline +void Zbase<Char, SP, AP>::reserve(size_t minCapacity) //make unshared and check capacity { if (!canWrite(rawStr, minCapacity)) { //allocate a new string - T* newStr = create(length(), std::max(minCapacity, length())); //reserve() must NEVER shrink the string: logical const! + Char* newStr = create(length(), std::max(minCapacity, length())); //reserve() must NEVER shrink the string: logical const! std::copy(rawStr, rawStr + length() + 1, newStr); //include NULL-termination destroy(rawStr); @@ -729,8 +726,8 @@ void Zbase<T, SP, AP>::reserve(size_t minCapacity) //make unshared and check cap } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>& Zbase<T, SP, AP>::assign(const T* source, size_t len) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::assign(const Char* source, size_t len) { if (canWrite(rawStr, len)) { @@ -745,80 +742,60 @@ Zbase<T, SP, AP>& Zbase<T, SP, AP>::assign(const T* source, size_t len) } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator=(const Zbase<T, SP, AP>& source) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::append(const Char* source, size_t len) { - Zbase(source).swap(*this); + const size_t thisLen = length(); + reserve(thisLen + len); //make unshared and check capacity + + std::copy(source, source + len, rawStr + thisLen); + rawStr[thisLen + len] = 0; + setLength(rawStr, thisLen + len); return *this; } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator=(Zbase<T, SP, AP>&& tmp) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(Zbase<Char, SP, AP> other) //unifying assignment: no need for r-value reference optimization! { - swap(tmp); + swap(other); return *this; } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator=(const T* source) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(const Char* source) { - return assign(source, zen::strLength(source)); + return assign(source, strLength(source)); } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator=(T source) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(Char ch) { - if (canWrite(rawStr, 1)) - { - rawStr[0] = source; - rawStr[1] = 0; //include null-termination - setLength(rawStr, 1); - } - else - *this = Zbase(source); - - return *this; + return assign(&ch, 1); } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator+=(const Zbase<T, SP, AP>& other) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator+=(const Zbase<Char, SP, AP>& other) { - const size_t thisLen = length(); - const size_t otherLen = other.length(); - reserve(thisLen + otherLen); //make unshared and check capacity - - std::copy(other.rawStr, other.rawStr + otherLen + 1, rawStr + thisLen); //include null-termination - setLength(rawStr, thisLen + otherLen); - return *this; + return append(other.c_str(), other.length()); } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator+=(const T* other) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator+=(const Char* other) { - const size_t thisLen = length(); - const size_t otherLen = zen::strLength(other); - reserve(thisLen + otherLen); //make unshared and check capacity - - std::copy(other, other + otherLen + 1, rawStr + thisLen); //include null-termination - setLength(rawStr, thisLen + otherLen); - return *this; + return append(other, strLength(other)); } -template <class T, template <class, class> class SP, class AP> inline -Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator+=(T ch) +template <class Char, template <class, class> class SP, class AP> inline +Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator+=(Char ch) { - const size_t thisLen = length(); - reserve(thisLen + 1); //make unshared and check capacity - rawStr[thisLen] = ch; - rawStr[thisLen + 1] = 0; - setLength(rawStr, thisLen + 1); - return *this; + return append(&ch, 1); +} } #endif //Z_BASE_H_INCLUDED diff --git a/zen/string_tools.h b/zen/string_tools.h index 8cafad07..5f20a2de 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -14,17 +14,18 @@ #include <cwchar> //swprintf #include <algorithm> #include <cassert> -#include <sstream> #include <vector> +#include <sstream> +#include "stl_tools.h" #include "string_traits.h" -#include "type_traits.h" //enhance arbitray string class with useful non-member functions: namespace zen { -template <class C> bool cStringIsWhiteSpace(C ch); -template <class C> bool cStringIsDigit(C ch); +template <class Char> bool cStringIsWhiteSpace(Char ch); +template <class Char> bool cStringIsDigit (Char ch); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only! + template <class S, class T> bool startsWith(const S& str, const T& prefix); //both S and T can be strings or char/wchar_t arrays or simple char/wchar_t template <class S, class T> bool endsWith (const S& str, const T& postfix); // @@ -40,14 +41,14 @@ template <class S> void trim(S& str, bool fromLeft = true, bool fromRight = true template <class S, class T, class U> void replace ( S& str, const T& oldOne, const U& newOne, bool replaceAll = true); template <class S, class T, class U> S replaceCpy(const S& str, const T& oldOne, const U& newOne, bool replaceAll = true); -//high-performance conversion from numbers to strings -template <class S, class T, class Num> S printNumber(const T& format, const Num& number); //format a single number using ::sprintf +//high-performance conversion between numbers and strings +template <class S, class T, class Num> S printNumber(const T& format, const Num& number); //format a single number using std::snprintf() template <class S, class Num> S toString(const Num& number); template <class Num, class S > Num toNumber(const S& str); -//string to string conversion: converst string-like type into char-compatible target string class -template <class T, class S> T cvrtString(const S& str); +//string to string conversion: converts string-like type into char-compatible target string class +template <class T, class S> T copyStringTo(const S& str); @@ -90,35 +91,30 @@ bool cStringIsWhiteSpace(char ch) std::isspace(static_cast<unsigned char>(ch)) != 0; } -//template <> inline bool cStringIsWhiteSpace(unsigned char ch) { return cStringIsWhiteSpace<char>(ch); } -> not character types! -//template <> inline bool cStringIsWhiteSpace(signed char ch) { return cStringIsWhiteSpace<char>(ch); } -template <> inline bool cStringIsWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; } - -template <> inline -bool cStringIsDigit(char ch) -{ - return std::isdigit(static_cast<unsigned char>(ch)) != 0; //caveat: takes an int, but expects an unsigned char -} +template <> inline bool cStringIsWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; } -template <> inline -bool cStringIsDigit(wchar_t ch) +template <class Char> inline +bool cStringIsDigit(Char ch) //similar to implmenetation of std::::isdigit()! { - return std::iswdigit(ch) != 0; + assert_static((IsSameType<Char, char>::result || IsSameType<Char, wchar_t>::result)); + return static_cast<Char>('0') <= ch && ch <= static_cast<Char>('9'); } template <class S, class T> inline bool startsWith(const S& str, const T& prefix) { - assert_static(StringTraits<S>::isStringLike); - assert_static(StringTraits<T>::isStringLike); + assert_static(IsStringLike<S>::result); + assert_static(IsStringLike<T>::result); + typedef typename GetCharType<S>::Result CharType; const size_t pfLength = strLength(prefix); if (strLength(str) < pfLength) return false; - return std::equal(strBegin(str), strBegin(str) + pfLength, + const CharType* const strFirst = strBegin(str); + return std::equal(strFirst, strFirst + pfLength, strBegin(prefix)); } @@ -126,18 +122,17 @@ bool startsWith(const S& str, const T& prefix) template <class S, class T> inline bool endsWith(const S& str, const T& postfix) { - assert_static(StringTraits<S>::isStringLike); - assert_static(StringTraits<T>::isStringLike); + assert_static(IsStringLike<S>::result); + assert_static(IsStringLike<T>::result); + typedef typename GetCharType<S>::Result CharType; - size_t strLen = strLength(str); - size_t pfLen = strLength(postfix); + const size_t strLen = strLength(str); + const size_t pfLen = strLength(postfix); if (strLen < pfLen) return false; - typedef typename StringTraits<S>::CharType CharType; - - const CharType* cmpBegin = strBegin(str) + strLen - pfLen; - return std::equal(cmpBegin, cmpBegin + pfLen, + const CharType* const cmpFirst = strBegin(str) + strLen - pfLen; + return std::equal(cmpFirst, cmpFirst + pfLen, strBegin(postfix)); } @@ -146,16 +141,22 @@ bool endsWith(const S& str, const T& postfix) template <class S, class T> inline S afterLast(const S& str, const T& ch) { - assert_static(StringTraits<T>::isStringLike); + assert_static(IsStringLike<T>::result); + typedef typename GetCharType<S>::Result CharType; - const size_t pos = str.rfind(ch); - if (pos != S::npos) - { - size_t chLen = strLength(ch); - return S(str.c_str() + pos + chLen, str.length() - pos - chLen); - } - else + const size_t chLen = strLength(ch); + + const CharType* const strFirst = strBegin(str); + const CharType* const strLast = strFirst + strLength(str); + const CharType* const chFirst = strBegin(ch); + + const CharType* iter = search_last(strFirst, strLast, + chFirst, chFirst + chLen); + if (iter == strLast) return str; + + iter += chLen; + return S(iter, strLast - iter); } @@ -163,13 +164,19 @@ S afterLast(const S& str, const T& ch) template <class S, class T> inline S beforeLast(const S& str, const T& ch) { - assert_static(StringTraits<T>::isStringLike); + assert_static(IsStringLike<T>::result); + typedef typename GetCharType<S>::Result CharType; - const size_t pos = str.rfind(ch); - if (pos != S::npos) - return S(str.c_str(), pos); //data is non-empty string in this context: else ch would not have been found! - else + const CharType* const strFirst = strBegin(str); + const CharType* const strLast = strFirst + strLength(str); + const CharType* const chFirst = strBegin(ch); + + const CharType* iter = search_last(strFirst, strLast, + chFirst, chFirst + strLength(ch)); + if (iter == strLast) return S(); + + return S(strFirst, iter - strFirst); } @@ -177,17 +184,21 @@ S beforeLast(const S& str, const T& ch) template <class S, class T> inline S afterFirst(const S& str, const T& ch) { - assert_static(StringTraits<T>::isStringLike); + assert_static(IsStringLike<T>::result); + typedef typename GetCharType<S>::Result CharType; - const size_t pos = str.find(ch); - if (pos != S::npos) - { - size_t chLen = strLength(ch); - return S(str.c_str() + pos + chLen, str.length() - pos - chLen); - } - else + const size_t chLen = strLength(ch); + const CharType* const strFirst = strBegin(str); + const CharType* const strLast = strFirst + strLength(str); + const CharType* const chFirst = strBegin(ch); + + const CharType* iter = std::search(strFirst, strLast, + chFirst, chFirst + chLen); + if (iter == strLast) return S(); + iter += chLen; + return S(iter, strLast - iter); } @@ -195,34 +206,48 @@ S afterFirst(const S& str, const T& ch) template <class S, class T> inline S beforeFirst(const S& str, const T& ch) { - assert_static(StringTraits<T>::isStringLike); + assert_static(IsStringLike<T>::result); + typedef typename GetCharType<S>::Result CharType; - const size_t pos = str.find(ch); - if (pos != S::npos) - return S(str.c_str(), pos); //data is non-empty string in this context: else ch would not have been found! - else - return str; + const CharType* const strFirst = strBegin(str); + const CharType* const chFirst = strBegin(ch); + + return S(strFirst, std::search(strFirst, strFirst + strLength(str), + chFirst, chFirst + strLength(ch)) - strFirst); } template <class S, class T> inline std::vector<S> split(const S& str, const T& delimiter) { - assert_static(StringTraits<T>::isStringLike); + assert_static(IsStringLike<T>::result); + typedef typename GetCharType<S>::Result CharType; std::vector<S> output; - size_t bockStart = 0; - size_t delimLen = strLength(delimiter); - if (delimLen != 0) + + const size_t delimLen = strLength(delimiter); + + if (delimLen == 0) + output.push_back(str); + else { - for (size_t blockEnd = str.find(delimiter, bockStart); - blockEnd != S::npos; - bockStart = blockEnd + delimLen, blockEnd = str.find(delimiter, bockStart)) + const CharType* const delimFirst = strBegin(delimiter); + const CharType* const delimLast = delimFirst + delimLen; + + const CharType* blockStart = strBegin(str); + const CharType* const strLast = blockStart + strLength(str); + + for (;;) { - output.push_back(S(str.c_str() + bockStart, blockEnd - bockStart)); + const CharType* const blockEnd = std::search(blockStart, strLast, + delimFirst, delimLast); + + output.push_back(S(blockStart, blockEnd - blockStart)); + if (blockEnd == strLast) + break; + blockStart = blockEnd + delimLen; } } - output.push_back(S(str.c_str() + bockStart, str.length() - bockStart)); return output; } @@ -235,38 +260,54 @@ void truncate(S& str, size_t newLen) } +namespace implementation +{ +ZEN_INIT_DETECT_MEMBER(append); + +//either call operator+=(S(str, len)) or append(str, len) +template <class S, class Char> inline +typename EnableIf<HasMember_append<S>::result>::Result stringAppend(S& str, const Char* other, size_t len) { str.append(other, len); } + +template <class S, class Char> inline +typename EnableIf<!HasMember_append<S>::result>::Result stringAppend(S& str, const Char* other, size_t len) { str += S(other, len); } +} + + template <class S, class T, class U> inline S replaceCpy(const S& str, const T& oldOne, const U& newOne, bool replaceAll) { - assert_static(StringTraits<T>::isStringLike); - assert_static(StringTraits<U>::isStringLike); + assert_static(IsStringLike<T>::result); + assert_static(IsStringLike<U>::result); - typedef typename StringTraits<S>::CharType CharType; + typedef typename GetCharType<S>::Result CharType; const size_t oldLen = strLength(oldOne); const size_t newLen = strLength(newOne); S output; - const CharType* strPos = strBegin(str); - const CharType* strEnd = strPos + strLength(str); + const CharType* strPos = strBegin(str); + const CharType* const strEnd = strPos + strLength(str); + + const CharType* const oldBegin = strBegin(oldOne); + const CharType* const newBegin = strBegin(newOne); for (;;) { const CharType* ptr = std::search(strPos, strEnd, - strBegin(oldOne), strBegin(oldOne) + oldLen); + oldBegin, oldBegin + oldLen); if (ptr == strEnd) break; - output += S(strPos, ptr - strPos); - output += S(strBegin(newOne), newLen); + implementation::stringAppend(output, strPos, ptr - strPos); + implementation::stringAppend(output, newBegin, newLen); strPos = ptr + oldLen; if (!replaceAll) break; } - output += S(strPos, strEnd - strPos); + implementation::stringAppend(output, strPos, strEnd - strPos); return output; } @@ -284,10 +325,11 @@ void trim(S& str, bool fromLeft, bool fromRight) { assert(fromLeft || fromRight); - typedef typename S::value_type CharType; + typedef typename GetCharType<S>::Result CharType; //don't use value_type! (wxString, Glib::ustring) - const CharType* newBegin = str.c_str(); - const CharType* newEnd = str.c_str() + str.length(); + const CharType* const oldBegin = str.c_str(); + const CharType* newBegin = oldBegin; + const CharType* newEnd = oldBegin + str.length(); if (fromRight) while (newBegin != newEnd && cStringIsWhiteSpace(newEnd[-1])) @@ -300,7 +342,7 @@ void trim(S& str, bool fromLeft, bool fromRight) const size_t newLength = newEnd - newBegin; if (newLength != str.length()) { - if (newBegin != str.c_str()) + if (newBegin != oldBegin) str = S(newBegin, newLength); //minor inefficiency: in case "str" is not shared, we could save an allocation and do a memory move only else str.resize(newLength); @@ -311,20 +353,20 @@ void trim(S& str, bool fromLeft, bool fromRight) namespace implementation { template <class S, class T> -struct CnvtStringToString +struct CopyStringToString { - T convert(const S& src) const { return T(strBegin(src), strLength(src)); } + T copy(const S& src) const { return T(strBegin(src), strLength(src)); } }; template <class S> -struct CnvtStringToString<S, S> //perf: we don't need a deep copy if string types match +struct CopyStringToString<S, S> //perf: we don't need a deep copy if string types match { - const S& convert(const S& src) const { return src; } + const S& copy(const S& src) const { return src; } }; } template <class T, class S> inline -T cvrtString(const S& str) { return implementation::CnvtStringToString<S, T>().convert(str); } +T copyStringTo(const S& str) { return implementation::CopyStringToString<S, T>().copy(str); } namespace implementation @@ -333,7 +375,7 @@ template <class Num> inline int saferPrintf(char* buffer, size_t bufferSize, const char* format, const Num& number) //there is no such thing as a "safe" printf ;) { #ifdef _MSC_VER - return ::_snprintf(buffer, bufferSize, format, number); //VS2010 doesn't respect ISO C + return ::_snprintf(buffer, bufferSize, format, number); //VS2010 doesn't respect ISO C #else return std::snprintf(buffer, bufferSize, format, number); //C99 #endif @@ -342,8 +384,8 @@ int saferPrintf(char* buffer, size_t bufferSize, const char* format, const Num& template <class Num> inline int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const Num& number) { -#ifdef __MINGW32__ //MinGW doesn't respect ISO C - return ::snwprintf(buffer, bufferSize, format, number); +#ifdef __MINGW32__ + return ::snwprintf(buffer, bufferSize, format, number); //MinGW doesn't respect ISO C #else return std::swprintf(buffer, bufferSize, format, number); //C99 #endif @@ -353,12 +395,12 @@ int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const template <class S, class T, class Num> inline S printNumber(const T& format, const Num& number) //format a single number using ::sprintf { - assert_static(StringTraits<T>::isStringLike); + assert_static(IsStringLike<T>::result); assert_static((IsSameType< - typename StringTraits<S>::CharType, - typename StringTraits<T>::CharType>::result)); + typename GetCharType<S>::Result, + typename GetCharType<T>::Result>::result)); - typedef typename StringTraits<S>::CharType CharType; + typedef typename GetCharType<S>::Result CharType; const int BUFFER_SIZE = 128; CharType buffer[BUFFER_SIZE]; @@ -379,29 +421,26 @@ enum NumberType }; -template <class S, class Num, NumberType> -struct CvrtNumberToString +template <class S, class Num> inline +S toString(const Num& number, Int2Type<NUM_TYPE_OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20) { - S convert(const Num& number) const //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20) - { - typedef typename StringTraits<S>::CharType CharType; + typedef typename GetCharType<S>::Result CharType; + + std::basic_ostringstream<CharType> ss; + ss << number; + return copyStringTo<S>(ss.str()); +} - std::basic_ostringstream<CharType> ss; - ss << number; - return cvrtString<S>(ss.str()); - } -}; +template <class S, class Num> inline S floatToString(const Num& number, char ) { return printNumber<S>( "%g", static_cast<double>(number)); } +template <class S, class Num> inline S floatToString(const Num& number, wchar_t) { return printNumber<S>(L"%g", static_cast<double>(number)); } -template <class S, class Num> -struct CvrtNumberToString<S, Num, NUM_TYPE_FLOATING_POINT> +template <class S, class Num> inline +S toString(const Num& number, Int2Type<NUM_TYPE_FLOATING_POINT>) { - S convert(const Num& number) const { return convertFloat(number, typename StringTraits<S>::CharType()); } + return floatToString<S>(number, typename GetCharType<S>::Result()); +} -private: - S convertFloat(const Num& number, char ) const { return printNumber<S>( "%g", static_cast<double>(number)); } - S convertFloat(const Num& number, wchar_t) const { return printNumber<S>(L"%g", static_cast<double>(number)); } -}; /* perf: integer to string: (executed 10 mio. times) @@ -413,62 +452,62 @@ perf: integer to string: (executed 10 mio. times) template <class S, class Num> inline S formatInteger(Num n, bool hasMinus) { + typedef typename GetCharType<S>::Result CharType; + assert(n >= 0); S output; do { - output += '0' + n % 10; + output += static_cast<CharType>('0' + n % 10); n /= 10; } while (n != 0); if (hasMinus) - output += '-'; + output += static_cast<CharType>('-'); std::reverse(output.begin(), output.end()); return output; } -template <class S, class Num> -struct CvrtNumberToString<S, Num, NUM_TYPE_SIGNED_INT> +template <class S, class Num> inline +S toString(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>) { - S convert(const Num& number) const { return formatInteger<S>(number < 0 ? -number : number, number < 0); } -}; + return formatInteger<S>(number < 0 ? -number : number, number < 0); +} + -template <class S, class Num> -struct CvrtNumberToString<S, Num, NUM_TYPE_UNSIGNED_INT> +template <class S, class Num> inline +S toString(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) { - S convert(const Num& number) const { return formatInteger<S>(number, false); } -}; + return formatInteger<S>(number, false); +} //-------------------------------------------------------------------------------- -template <class S, class Num, NumberType> -struct CvrtStringToNumber + +template <class Num, class S> inline +Num toNumber(const S& str, Int2Type<NUM_TYPE_OTHER>) //default string to number conversion using streams: convenient, but SLOW { - Num convert(const S& str) const //default string to number conversion using streams: convenient, but SLOW - { - typedef typename StringTraits<S>::CharType CharType; - Num number = 0; - std::basic_istringstream<CharType>(cvrtString<std::basic_string<CharType> >(str)) >> number; - return number; - } -}; + typedef typename GetCharType<S>::Result CharType; + Num number = 0; + std::basic_istringstream<CharType>(copyStringTo<std::basic_string<CharType> >(str)) >> number; + return number; +} -template <class S, class Num> -struct CvrtStringToNumber<S, Num, NUM_TYPE_FLOATING_POINT> -{ - Num convert(const S& str) const { return convertFloat(strBegin(str)); } +template <class Num> inline Num stringToFloat(const char* str) { return std::strtod(str, NULL); } +template <class Num> inline Num stringToFloat(const wchar_t* str) { return std::wcstod(str, NULL); } -private: - Num convertFloat(const char* str) const { return std::strtod(str, NULL); } - Num convertFloat(const wchar_t* str) const { return std::wcstod(str, NULL); } -}; +template <class Num, class S> inline +Num toNumber(const S& str, Int2Type<NUM_TYPE_FLOATING_POINT>) +{ + return stringToFloat<Num>(strBegin(str)); +} template <class Num, class S> Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic { - typedef typename StringTraits<S>::CharType CharType; + typedef typename GetCharType<S>::Result CharType; const CharType* first = strBegin(str); const CharType* last = first + strLength(str); @@ -476,15 +515,16 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i while (first != last && cStringIsWhiteSpace(*first)) //skip leading whitespace ++first; - hasMinusSign = false; //handle minus sign + //handle minus sign + hasMinusSign = false; if (first != last) { - if (*first == '-') + if (*first == static_cast<CharType>('-')) { hasMinusSign = true; ++first; } - else if (*first == '+') + else if (*first == static_cast<CharType>('+')) ++first; } @@ -492,14 +532,15 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i for (const CharType* iter = first; iter != last; ++iter) { const CharType c = *iter; - if ('0' <= c && c <= '9') + if (static_cast<CharType>('0') <= c && c <= static_cast<CharType>('9')) { number *= 10; - number += c - '0'; + number += c - static_cast<CharType>('0'); } else { - assert(std::find_if(iter, last, std::not1(std::ptr_fun(&cStringIsWhiteSpace<CharType>))) == last); //rest of string should contain whitespace only + //rest of string should contain whitespace only + //assert(std::find_if(iter, last, std::not1(std::ptr_fun(&cStringIsWhiteSpace<CharType>))) == last); -> this is NO assert situation break; } } @@ -507,61 +548,53 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i } -template <class S, class Num> -struct CvrtStringToNumber<S, Num, NUM_TYPE_SIGNED_INT> +template <class Num, class S> inline +Num toNumber(const S& str, Int2Type<NUM_TYPE_SIGNED_INT>) { - Num convert(const S& str) const - { - bool hasMinusSign = false; //handle minus sign - const Num number = extractInteger<Num>(str, hasMinusSign); - return hasMinusSign ? -number : number; - } -}; + bool hasMinusSign = false; //handle minus sign + const Num number = extractInteger<Num>(str, hasMinusSign); + return hasMinusSign ? -number : number; +} -template <class S, class Num> -struct CvrtStringToNumber<S, Num, NUM_TYPE_UNSIGNED_INT> +template <class Num, class S> inline +Num toNumber(const S& str, Int2Type<NUM_TYPE_UNSIGNED_INT>) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic { - Num convert(const S& str) const //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic + bool hasMinusSign = false; //handle minus sign + const Num number = extractInteger<Num>(str, hasMinusSign); + if (hasMinusSign) { - bool hasMinusSign = false; //handle minus sign - const Num number = extractInteger<Num>(str, hasMinusSign); - if (hasMinusSign) - { - assert(false); - return 0U; - } - return number; + assert(false); + return 0U; } -}; + return number; +} } -template <class S, class Num> -inline +template <class S, class Num> inline S toString(const Num& number) //convert number to string the C++ way { - using namespace implementation; - return CvrtNumberToString<S, Num, - IsSignedInt <Num>::result ? NUM_TYPE_SIGNED_INT : - IsUnsignedInt<Num>::result ? NUM_TYPE_UNSIGNED_INT : - IsFloat <Num>::result ? NUM_TYPE_FLOATING_POINT : - NUM_TYPE_OTHER - >().convert(number); + typedef Int2Type< + IsSignedInt <Num>::result ? implementation::NUM_TYPE_SIGNED_INT : + IsUnsignedInt<Num>::result ? implementation::NUM_TYPE_UNSIGNED_INT : + IsFloat <Num>::result ? implementation::NUM_TYPE_FLOATING_POINT : + implementation::NUM_TYPE_OTHER> TypeTag; + + return implementation::toString<S>(number, TypeTag()); } -template <class Num, class S> -inline +template <class Num, class S> inline Num toNumber(const S& str) //convert string to number the C++ way { - using namespace implementation; - return CvrtStringToNumber<S, Num, - IsSignedInt <Num>::result ? NUM_TYPE_SIGNED_INT : - IsUnsignedInt<Num>::result ? NUM_TYPE_UNSIGNED_INT : - IsFloat <Num>::result ? NUM_TYPE_FLOATING_POINT : - NUM_TYPE_OTHER - >().convert(str); + typedef Int2Type< + IsSignedInt <Num>::result ? implementation::NUM_TYPE_SIGNED_INT : + IsUnsignedInt<Num>::result ? implementation::NUM_TYPE_UNSIGNED_INT : + IsFloat <Num>::result ? implementation::NUM_TYPE_FLOATING_POINT : + implementation::NUM_TYPE_OTHER> TypeTag; + + return implementation::toNumber<Num>(str, TypeTag()); } } diff --git a/zen/string_traits.h b/zen/string_traits.h index 59da2f79..6c51f6dd 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -9,35 +9,48 @@ #define STRING_TRAITS_HEADER_813274321443234 #include "type_tools.h" +#include "type_traits.h" #include "assert_static.h" -//uniform access to string-like types: classes and character arrays +//uniform access to string-like types, both classes and character arrays namespace zen { /* +IsStringLike<>::result: + IsStringLike<const wchar_t*>::result; //equals "true" + IsStringLike<const int*> ::result; //equals "false" + +GetCharType<>::Result: + GetCharType<std::wstring>::Result //equals wchar_t + GetCharType<wchar_t[5]> ::Result //equals wchar_t + strBegin(): std::wstring str(L"dummy"); char array[] = "dummy"; - const wchar_t* iter = strBegin(str); //returns str.c_str() - const char* iter2 = strBegin(array); //returns array + strBegin(str); //returns str.c_str() + strBegin(array); //returns array strLength(): - strLength(str); //equals str.size() + strLength(str); //equals str.length() strLength(array); //equals cStringLength(array) +*/ -StringTraits<>::CharType: - StringTraits<std::wstring>::CharType //equals wchar_t - StringTraits<wchar_t[5]> ::CharType //equals wchar_t +//wrap a sub-string or a char* as an intermediate string class when the length is already known +template <class Char> +class StringProxy +{ +public: + StringProxy(const Char* cstr, size_t len ) : cstr_(cstr), length_(len) {} + StringProxy(const Char* cstrBegin, const Char* cstrEnd) : cstr_(cstrBegin), length_(cstrEnd - cstrBegin) {} -StringTraits<>::isStringLike: - StringTraits<const wchar_t*>::isStringLike; //equals "true" - StringTraits<const int*> ::isStringLike; //equals "false" + const Char* c_str() const { return cstr_; } + size_t length() const { return length_; } -StringTraits<>::isStringClass: - StringTraits<std::wstring>::isStringClass //equals "true" - StringTraits<wchar_t[5]> ::isStringClass //equals "false" -*/ +private: + const Char* cstr_; + size_t length_; +}; @@ -54,91 +67,87 @@ StringTraits<>::isStringClass: //---------------------- implementation ---------------------- namespace implementation { -template<typename T> -class HasValueTypedef -{ - typedef char Yes[1]; - typedef char No [2]; - - template <typename U> class HelperTp {}; +ZEN_INIT_DETECT_MEMBER(c_str) //we don't know the exact declaration of the member attribute and it may be in a base class! +ZEN_INIT_DETECT_MEMBER(length) // - //detect presence of a member type called value_type - template <class U> static Yes& hasMemberValueType(HelperTp<typename U::value_type>*); - template <class U> static No& hasMemberValueType(...); +template<typename T, bool isClassType> +struct HasStringMembers { enum { result = false }; }; -public: - enum { result = sizeof(hasMemberValueType<T>(NULL)) == sizeof(Yes) +template<typename T> +struct HasStringMembers<T, true> //Note: we can apply non-typed member-check on class types only! +{ + enum { result = HasMember_c_str <T>::result && + HasMember_length<T>::result }; }; -template<typename T, bool isClassType> -class HasStringMembers -{ -public: - enum { result = false }; -}; - -template<typename T> -class HasStringMembers<T, true> +template<class S, class Char> //test if result of S::c_str() can convert to const Char* +class HasConversion { typedef char Yes[1]; typedef char No [2]; - //detect presence of member functions (without specific restriction on return type, within T or one of it's base classes) - template <typename U, U t> class HelperFn {}; + static Yes& hasConversion(const Char*); + static No& hasConversion(...); - struct Fallback - { - int c_str; - int length; - }; + static S createInstance(); - template <class U> - struct Helper2 : public U, public Fallback {}; //U must be a class-type! +public: + enum { result = sizeof(hasConversion(createInstance().c_str())) == sizeof(Yes) }; +}; - //we don't know the exact declaration of the member attribute (may be in base class), but we know what NOT to expect: - template <class U> static No& hasMemberCstr(HelperFn<int Fallback::*, &Helper2<U>::c_str>*); - template <class U> static Yes& hasMemberCstr(...); - template <class U> static No& hasMemberLength(HelperFn<int Fallback::*, &Helper2<U>::length>*); - template <class U> static Yes& hasMemberLength(...); -public: - enum { result = sizeof(hasMemberCstr <T>(NULL)) == sizeof(Yes) && - sizeof(hasMemberLength<T>(NULL)) == sizeof(Yes) - }; +template <class S, bool isStringClass> struct GetCharTypeImpl { typedef EmptyType Result; }; +template <class S> struct GetCharTypeImpl<S, true > +{ + //typedef typename S::value_type Result; + /*DON'T use S::value_type: + 1. support Glib::ustring: value_type is "unsigned int" but c_str() returns "const char*" + 2. wxString, wxWidgets v2.9, has some questionable string design: wxString::c_str() returns a proxy (wxCStrData) which + is implicitly convertible to *both* "const char*" and "const wchar_t*" while wxString::value_type is a wrapper around an unsigned int + */ + typedef typename SelectIf<HasConversion<S, wchar_t>::result, wchar_t, + typename SelectIf<HasConversion<S, char>::result, char, EmptyType>::Result + >::Result Result; }; -template <class S, bool isStringClass> struct StringTraits2 { typedef EmptyType Result; }; //"StringTraits2": fix some VS bug with namespace and partial template specialization -template <class S> struct StringTraits2<S, true > { typedef typename S::value_type Result; }; -template <> struct StringTraits2<char, false> { typedef char Result; }; -template <> struct StringTraits2<wchar_t, false> { typedef wchar_t Result; }; -} +template <> struct GetCharTypeImpl<char, false> { typedef char Result; }; +template <> struct GetCharTypeImpl<wchar_t, false> { typedef wchar_t Result; }; +ZEN_INIT_DETECT_MEMBER_TYPE(value_type); template <class S> -struct StringTraits +class StringTraits { -private: typedef typename RemoveRef <S >::Result NonRefType; typedef typename RemoveConst <NonRefType >::Result NonConstType; typedef typename RemoveArray <NonConstType>::Result NonArrayType; typedef typename RemovePointer<NonArrayType>::Result NonPtrType; typedef typename RemoveConst <NonPtrType >::Result UndecoratedType; //handle "const char* const" + public: enum { - isStringClass = implementation::HasStringMembers<NonConstType, implementation::HasValueTypedef<NonConstType>::result>::result + isStringClass = HasStringMembers<NonConstType, HasMemberType_value_type<NonConstType>::result>::result }; - typedef typename implementation::StringTraits2<UndecoratedType, isStringClass>::Result CharType; + typedef typename GetCharTypeImpl<UndecoratedType, isStringClass>::Result CharType; enum { - isStringLike = IsSameType<CharType, char>::result || IsSameType<CharType, wchar_t>::result + isStringLike = IsSameType<CharType, char>::result || + IsSameType<CharType, wchar_t>::result }; }; +} + +template <class T> +struct IsStringLike { enum { result = implementation::StringTraits<T>::isStringLike }; }; + +template <class T> +struct GetCharType { typedef typename implementation::StringTraits<T>::CharType Result; }; namespace implementation @@ -156,19 +165,25 @@ size_t cStringLength(const C* str) //strlen() template <class S> inline -const typename StringTraits<S>::CharType* strBegin(const S& str, typename S::value_type dummy = 0) { return str.c_str(); } //SFINAE: T must be a "string" +const typename GetCharType<S>::Result* strBegin(const S& str, typename EnableIf<implementation::StringTraits<S>::isStringClass>::Result* = NULL) //SFINAE: T must be a "string" +{ + return str.c_str(); +} -template <class Char> -inline const typename StringTraits<Char>::CharType* strBegin(const Char* str) { return str; } -inline const char* strBegin(const char& ch) { return &ch; } -inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; } +inline const char* strBegin(const char* str) { return str; } +inline const wchar_t* strBegin(const wchar_t* str) { return str; } +inline const char* strBegin(const char& ch) { return &ch; } +inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; } template <class S> inline -size_t strLength(const S& str, typename S::value_type dummy = 0) { return str.length(); } //SFINAE: T must be a "string" +size_t strLength(const S& str, typename EnableIf<implementation::StringTraits<S>::isStringClass>::Result* = NULL) //SFINAE: T must be a "string" +{ + return str.length(); +} -template <class Char> -inline size_t strLength(const Char* str) { return implementation::cStringLength(str); } +inline size_t strLength(const char* str) { return implementation::cStringLength(str); } +inline size_t strLength(const wchar_t* str) { return implementation::cStringLength(str); } inline size_t strLength(char) { return 1; } inline size_t strLength(wchar_t) { return 1; } } diff --git a/zen/symlink_target.h b/zen/symlink_target.h index 3704eebe..370a0c56 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -78,7 +78,7 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); if (hLink == INVALID_HANDLE_VALUE) - throw FileError(_("Error resolving symbolic link:") + "\n\"" + linkPath + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error resolving symbolic link:") + L"\n\"" + linkPath + L"\"" + L"\n\n" + getLastErrorFormatted()); ZEN_ON_BLOCK_EXIT(::CloseHandle(hLink)); //respect alignment issues... @@ -94,7 +94,7 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError bufferSize, //__in DWORD nOutBufferSize, &bytesReturned, //__out_opt LPDWORD lpBytesReturned, NULL)) //__inout_opt LPOVERLAPPED lpOverlapped - throw FileError(_("Error resolving symbolic link:") + "\n\"" + linkPath + "\"" + "\n\n" + getLastErrorFormatted()); + throw FileError(_("Error resolving symbolic link:") + L"\n\"" + linkPath + L"\"" + L"\n\n" + getLastErrorFormatted()); REPARSE_DATA_BUFFER& reparseData = *reinterpret_cast<REPARSE_DATA_BUFFER*>(&buffer[0]); //REPARSE_DATA_BUFFER needs to be artificially enlarged! @@ -110,7 +110,7 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError reparseData.MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); } else - throw FileError(_("Error resolving symbolic link:") + "\n\"" + linkPath + "\"" + "\n\n" + "Not a symbolic link or junction!"); + throw FileError(_("Error resolving symbolic link:") + L"\n\"" + linkPath + L"\"" + L"\n\n" + L"Not a symbolic link or junction!"); //absolute symlinks and junctions technically start with \??\ while relative ones do not if (startsWith(output, Zstr("\\??\\"))) @@ -125,7 +125,7 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError const int bytesWritten = ::readlink(linkPath.c_str(), &buffer[0], BUFFER_SIZE); if (bytesWritten < 0 || bytesWritten >= BUFFER_SIZE) { - std::wstring errorMessage = _("Error resolving symbolic link:") + "\n\"" + linkPath + "\""; + std::wstring errorMessage = _("Error resolving symbolic link:") + L"\n\"" + linkPath + L"\""; if (bytesWritten < 0) errorMessage += L"\n\n" + getLastErrorFormatted(); throw FileError(errorMessage); diff --git a/zen/time.h b/zen/time.h new file mode 100644 index 00000000..f08f6ef8 --- /dev/null +++ b/zen/time.h @@ -0,0 +1,304 @@ +// ************************************************************************** +// * This file is part of the zenXML project. It is distributed under the * +// * Boost Software License, Version 1.0. See accompanying file * +// * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt. * +// * Copyright (C) 2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + +#ifndef ZEN_TIME_HEADER_845709281432434 +#define ZEN_TIME_HEADER_845709281432434 + +#include <ctime> +#include "string_tools.h" + +namespace zen +{ +struct TimeComp //replaces "struct std::tm" and SYSTEMTIME +{ + TimeComp() : year(0), month(0), day(0), hour(0), minute(0), second(0) {} + + int year; // - + int month; //1-12 + int day; //1-31 + int hour; //0-23 + int minute; //0-59 + int second; //0-61 +}; + +TimeComp localTime (time_t utc = std::time(NULL)); //convert time_t (UTC) to local time components +time_t localToTimeT(const TimeComp& comp); //convert local time components to time_t (UTC), returns -1 on error + +//---------------------------------------------------------------------------------------------------------------------------------- + +/* +format (current) date and time; example: + formatTime<std::wstring>(L"%Y*%m*%d"); -> "2011*10*29" + formatTime<std::wstring>(FORMAT_DATE); -> "2011-10-29" + formatTime<std::wstring>(FORMAT_TIME); -> "17:55:34" +*/ +template <class String, class String2> +String formatTime(const String2& format, const TimeComp& comp = localTime()); //format as specified by "std::strftime", returns empty string on failure + +//the "format" parameter of formatTime() is partially specialized with the following type tags: +const struct FormatDateTag {} FORMAT_DATE = {}; //%x - locale dependent date representation: e.g. 08/23/01 +const struct FormatTimeTag {} FORMAT_TIME = {}; //%X - locale dependent time representation: e.g. 14:55:02 +const struct FormatDateTimeTag {} FORMAT_DATE_TIME = {}; //%c - locale dependent date and time: e.g. Thu Aug 23 14:55:02 2001 + +const struct FormatIsoDateTag {} FORMAT_ISO_DATE = {}; //%Y-%m-%d - e.g. 2001-08-23 +const struct FormatIsoTimeTag {} FORMAT_ISO_TIME = {}; //%H:%M:%S - e.g. 14:55:02 +const struct FormatIsoDateTimeTag {} FORMAT_ISO_DATE_TIME = {}; //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14:55:02 + +//---------------------------------------------------------------------------------------------------------------------------------- + +template <class String> +bool parseTime(const String& format, const String& str, TimeComp& comp); //similar to ::strptime(), return true on success + + + + + + + + + + + + + + + + + + + + + + + + +//############################ implementation ############################## +namespace implementation +{ +inline +struct std::tm toClibTimeComponents(const TimeComp& comp) +{ + struct std::tm ctc = {}; + ctc.tm_year = comp.year - 1900; //years since 1900 + ctc.tm_mon = comp.month - 1; //0-11 + ctc.tm_mday = comp.day; //1-31 + ctc.tm_hour = comp.hour; //0-23 + ctc.tm_min = comp.minute; //0-59 + ctc.tm_sec = comp.second; //0-61 + ctc.tm_isdst = -1; //> 0 if DST is active, == 0 if DST is not active, < 0 if the information is not available + return ctc; +} + +inline +TimeComp toZenTimeComponents(const struct std::tm& ctc) +{ + TimeComp comp; + comp.year = ctc.tm_year + 1900; + comp.month = ctc.tm_mon + 1; + comp.day = ctc.tm_mday; + comp.hour = ctc.tm_hour; + comp.minute = ctc.tm_min; + comp.second = ctc.tm_sec; + return comp; +} + + +template <class T> +struct GetFormat; //get default time formats as char* or wchar_t* + +template <> +struct GetFormat<FormatDateTag> //%x - locale dependent date representation: e.g. 08/23/01 +{ + const char* format(char) const { return "%x"; } + const wchar_t* format(wchar_t) const { return L"%x"; } +}; + +template <> +struct GetFormat<FormatTimeTag> //%X - locale dependent time representation: e.g. 14:55:02 +{ + const char* format(char) const { return "%X"; } + const wchar_t* format(wchar_t) const { return L"%X"; } +}; + +template <> +struct GetFormat<FormatDateTimeTag> //%c - locale dependent date and time: e.g. Thu Aug 23 14:55:02 2001 +{ + const char* format(char) const { return "%c"; } + const wchar_t* format(wchar_t) const { return L"%c"; } +}; + +template <> +struct GetFormat<FormatIsoDateTag> //%Y-%m-%d - e.g. 2001-08-23 +{ + const char* format(char) const { return "%Y-%m-%d"; } + const wchar_t* format(wchar_t) const { return L"%Y-%m-%d"; } +}; + +template <> +struct GetFormat<FormatIsoTimeTag> //%H:%M:%S - e.g. 14:55:02 +{ + const char* format(char) const { return "%H:%M:%S"; } + const wchar_t* format(wchar_t) const { return L"%H:%M:%S"; } +}; + +template <> +struct GetFormat<FormatIsoDateTimeTag> //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14:55:02 +{ + const char* format(char) const { return "%Y-%m-%d %H:%M:%S"; } + const wchar_t* format(wchar_t) const { return L"%Y-%m-%d %H:%M:%S"; } +}; + + +inline +size_t strftimeWrap(char* buffer, size_t bufferSize, const char* format, const struct std::tm* timeptr) +{ + return std::strftime(buffer, bufferSize, format, timeptr); +} + + +inline +size_t strftimeWrap(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const struct std::tm* timeptr) +{ + return std::wcsftime(buffer, bufferSize, format, timeptr); +} + + +struct UserDefinedFormatTag {}; +struct PredefinedFormatTag {}; + +template <class String, class String2> inline +String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure +{ + typedef typename GetCharType<String>::Result CharType; + + const struct std::tm& ctc = toClibTimeComponents(comp); + CharType buffer[256]; + const size_t charsWritten = strftimeWrap(buffer, 256, strBegin(format), &ctc); + return String(buffer, charsWritten); +} + +template <class String, class FormatType> inline +String formatTime(FormatType, const TimeComp& comp, PredefinedFormatTag) +{ + typedef typename GetCharType<String>::Result CharType; + return formatTime<String>(GetFormat<FormatType>().format(CharType()), comp, UserDefinedFormatTag()); +} +} + + +inline +TimeComp localTime(time_t utc) +{ + return implementation::toZenTimeComponents(*std::localtime (&utc)); +} + + +inline +time_t localToTimeT(const TimeComp& comp) //returns -1 on error +{ + struct std::tm ctc = implementation::toClibTimeComponents(comp); + return std::mktime(&ctc); +} + + +template <class String, class String2> inline +String formatTime(const String2& format, const TimeComp& comp) +{ + typedef typename SelectIf< + IsSameType<String2, FormatDateTag >::result || + IsSameType<String2, FormatTimeTag >::result || + IsSameType<String2, FormatDateTimeTag >::result || + IsSameType<String2, FormatIsoDateTag >::result || + IsSameType<String2, FormatIsoTimeTag >::result || + IsSameType<String2, FormatIsoDateTimeTag>::result, implementation::PredefinedFormatTag, implementation::UserDefinedFormatTag>::Result FormatTag; + + return implementation::formatTime<String>(format, comp, FormatTag()); +} + + +template <class String> +bool parseTime(const String& format, const String& str, TimeComp& comp) //return true on success +{ + typedef typename GetCharType<String>::Result CharType; + + const CharType* iterFmt = strBegin(format); + const CharType* const fmtLast = iterFmt + strLength(format); + + const CharType* iterStr = strBegin(str); + const CharType* const strLast = iterStr + strLength(str); + + auto extractNumber = [&](int& result, size_t digitCount) -> bool + { + if (strLast - iterStr < digitCount) + return false; + + if (std::find_if(iterStr, iterStr + digitCount, [](CharType c) { return !cStringIsDigit(c); }) != str.end()) + return false; + + result = zen::toNumber<int>(StringProxy<CharType>(iterStr, digitCount)); + iterStr += digitCount; + return true; + }; + + for (; iterFmt != fmtLast; ++iterFmt) + { + const CharType fmt = *iterFmt; + + if (fmt == '%') + { + ++iterFmt; + if (iterFmt == fmtLast) + return false; + + switch (*iterFmt) + { + case 'Y': + if (!extractNumber(comp.year, 4)) + return false; + break; + case 'm': + if (!extractNumber(comp.month, 2)) + return false; + break; + case 'd': + if (!extractNumber(comp.day, 2)) + return false; + break; + case 'H': + if (!extractNumber(comp.hour, 2)) + return false; + break; + case 'M': + if (!extractNumber(comp.minute, 2)) + return false; + break; + case 'S': + if (!extractNumber(comp.second, 2)) + return false; + break; + default: + return false; + } + } + else if (cStringIsWhiteSpace(fmt)) //single whitespace in format => skip 0..n whitespace chars + { + while (iterStr != strLast && cStringIsWhiteSpace(*iterStr)) + ++iterStr; + } + else + { + if (iterStr == strLast || *iterStr != fmt) + return false; + ++iterStr; + } + } + + return iterStr == strLast; +} +} + +#endif //ZEN_TIME_HEADER_845709281432434 diff --git a/zen/type_tools.h b/zen/type_tools.h index 06ef76e1..03ccb5f2 100644 --- a/zen/type_tools.h +++ b/zen/type_tools.h @@ -23,12 +23,12 @@ struct Type2Type {}; //########## Control Structures ######################## template <bool flag, class T, class U> -struct Select +struct SelectIf { typedef T Result; }; template <class T, class U> -struct Select<false, T, U> +struct SelectIf<false, T, U> { typedef U Result; }; @@ -45,6 +45,15 @@ struct IsSameType<T, T> enum { result = true }; }; +//------------------------------------------------------ +template <bool, class T = void> +struct EnableIf {}; +template <class T> +struct EnableIf<true, T> +{ + typedef T Result; +}; + //########## Type Cleanup ############################## template <class T> struct RemoveRef { typedef T Result; }; diff --git a/zen/type_traits.h b/zen/type_traits.h index 0a705def..0dacbb9a 100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -10,6 +10,7 @@ namespace zen { +//################# Built-in Types ######################## //Example: "IsSignedInt<int>::result" evaluates to "true" template <class T> struct IsUnsignedInt; @@ -23,8 +24,26 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat //optional: specialize new types like: //template <> struct IsUnsignedInt<UInt64> { enum { result = true }; }; +//################# Class Members ######################## +/* Detect data or function members of a class by name: ZEN_INIT_DETECT_MEMBER + HasMember_ + !!! Note: this may ONLY be used for class types: fails to compile for non-class types !!! + Example: 1. ZEN_INIT_DETECT_MEMBER(c_str); + 2. HasMember_c_str<T>::result -> use as boolean +*/ + +/* Detect data or function members of a class by name and type: ZEN_INIT_DETECT_MEMBER2 + HasMember_ + + Example: 1. ZEN_INIT_DETECT_MEMBER2(size, size_t (T::*)() const); + 2. HasMember_size<T>::result -> use as boolean +*/ + +/* Detect member type of a class: ZEN_INIT_DETECT_MEMBER_TYPE + HasMemberType_ + + Example: 1. ZEN_INIT_DETECT_MEMBER_TYPE(value_type); + 2. HasMemberType_value_type<T>::result -> use as boolean +*/ @@ -83,6 +102,61 @@ struct IsInteger { enum { result = IsUnsignedInt<T>::result || IsSignedInt<T>::r template <class T> struct IsArithmetic { enum { result = IsInteger<T>::result || IsFloat<T>::result }; }; +//#################################################################### + +#define ZEN_INIT_DETECT_MEMBER(NAME) \ + \ + template<typename T> \ + class HasMember_##NAME \ + { \ + typedef char Yes[1]; \ + typedef char No [2]; \ + \ + template <typename U, U t> class Helper {}; \ + struct Fallback { int NAME; }; \ + \ + template <class U> \ + struct Helper2 : public U, public Fallback {}; \ + \ + template <class U> static No& hasMember(Helper<int Fallback::*, &Helper2<U>::NAME>*); \ + template <class U> static Yes& hasMember(...); \ + public: \ + enum { result = sizeof(hasMember<T>(NULL)) == sizeof(Yes) }; \ + }; +//#################################################################### + +#define ZEN_INIT_DETECT_MEMBER2(NAME, TYPE) \ + \ + template<typename U> \ + class HasMember_##NAME \ + { \ + typedef char Yes[1]; \ + typedef char No [2]; \ + \ + template <typename T, T t> class Helper {}; \ + \ + template <class T> static Yes& hasMember(Helper<TYPE, &T::NAME>*); \ + template <class T> static No& hasMember(...); \ + public: \ + enum { result = sizeof(hasMember<U>(NULL)) == sizeof(Yes) }; \ + }; +//#################################################################### + +#define ZEN_INIT_DETECT_MEMBER_TYPE(TYPENAME) \ + \ + template<typename T> \ + class HasMemberType_##TYPENAME \ + { \ + typedef char Yes[1]; \ + typedef char No [2]; \ + \ + template <typename U> class Helper {}; \ + \ + template <class U> static Yes& hasMemberType(Helper<typename U::TYPENAME>*); \ + template <class U> static No& hasMemberType(...); \ + public: \ + enum { result = sizeof(hasMemberType<T>(NULL)) == sizeof(Yes) }; \ + }; } #endif //TYPE_TRAITS_HEADER_3425628658765467 @@ -9,9 +9,7 @@ #define STRING_UTF8_HEADER_01832479146991573473545 #include <iterator> -#include "string_tools.h" -//#include "type_tools.h" -//#include "string_traits.h" +#include "string_tools.h" //copyStringTo namespace zen { @@ -64,7 +62,7 @@ const char BYTE_ORDER_MARK_UTF8[] = "\xEF\xBB\xBF"; //----------------------- implementation ---------------------------------- namespace implementation { -typedef unsigned int CodePoint; +typedef unsigned int CodePoint; //must be at least four bytes const CodePoint CODE_POINT_MAX = 0x10ffff; @@ -265,7 +263,7 @@ WideString utf8ToWide(const CharString& str, Int2Type<4>) //other OS: convert ut { WideString output; utf8ToCodePoint(strBegin(str), strBegin(str) + strLength(str), - [&](CodePoint cp) { output += cp; }); + [&](CodePoint cp) { output += static_cast<wchar_t>(cp); }); return output; } @@ -294,8 +292,8 @@ CharString wideToUtf8(const WideString& str, Int2Type<4>) //other OS: convert ut template <class WideString, class CharString> inline WideString utf8ToWide(const CharString& str) { - assert_static((IsSameType<typename StringTraits<CharString>::CharType, char >::result)); - assert_static((IsSameType<typename StringTraits<WideString>::CharType, wchar_t>::result)); + assert_static((IsSameType<typename GetCharType<CharString>::Result, char >::result)); + assert_static((IsSameType<typename GetCharType<WideString>::Result, wchar_t>::result)); return implementation::utf8ToWide<WideString>(str, Int2Type<sizeof(wchar_t)>()); } @@ -304,8 +302,8 @@ WideString utf8ToWide(const CharString& str) template <class CharString, class WideString> inline CharString wideToUtf8(const WideString& str) { - assert_static((IsSameType<typename StringTraits<CharString>::CharType, char >::result)); - assert_static((IsSameType<typename StringTraits<WideString>::CharType, wchar_t>::result)); + assert_static((IsSameType<typename GetCharType<CharString>::Result, char >::result)); + assert_static((IsSameType<typename GetCharType<WideString>::Result, wchar_t>::result)); return implementation::wideToUtf8<CharString>(str, Int2Type<sizeof(wchar_t)>()); } @@ -319,17 +317,17 @@ template <class TargetString, class SourceString> inline TargetString utf8CvrtTo(const SourceString& str, wchar_t, char) { return wideToUtf8<TargetString>(str); } template <class TargetString, class SourceString> inline -TargetString utf8CvrtTo(const SourceString& str, char, char) { return cvrtString<TargetString>(str); } +TargetString utf8CvrtTo(const SourceString& str, char, char) { return copyStringTo<TargetString>(str); } template <class TargetString, class SourceString> inline -TargetString utf8CvrtTo(const SourceString& str, wchar_t, wchar_t) { return cvrtString<TargetString>(str); } +TargetString utf8CvrtTo(const SourceString& str, wchar_t, wchar_t) { return copyStringTo<TargetString>(str); } template <class TargetString, class SourceString> inline TargetString utf8CvrtTo(const SourceString& str) { return utf8CvrtTo<TargetString>(str, - typename StringTraits<SourceString>::CharType(), - typename StringTraits<TargetString>::CharType()); + typename GetCharType<SourceString>::Result(), + typename GetCharType<TargetString>::Result()); } } diff --git a/zen/win_ver.h b/zen/win_ver.h index ca075bbe..6f2639c6 100644 --- a/zen/win_ver.h +++ b/zen/win_ver.h @@ -37,7 +37,7 @@ bool win7OrLater(); namespace impl { inline -bool winXyOrLater(DWORD major, DWORD minor) +bool winXyOrLater(DWORD major, DWORD minor) //migrate: hold version data as static variable, as soon as C++11 thread safe statics are available in VS { OSVERSIONINFO osvi = {}; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 5331499f..a559f9de 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -6,7 +6,7 @@ #include "zstring.h" #include <stdexcept> -#include <boost/thread/once.hpp> +#include <zen/stl_tools.h> #ifdef FFS_WIN #include "dll.h" @@ -55,14 +55,14 @@ LeakChecker& LeakChecker::instance() //caveat: function scope static initialization is not thread-safe in VS 2010! => make sure to call at app start! namespace { -struct Dummy { Dummy() { LeakChecker::instance(); }} blah; +const LeakChecker& dummy = LeakChecker::instance(); } std::string LeakChecker::rawMemToString(const void* ptr, size_t size) { std::string output = std::string(reinterpret_cast<const char*>(ptr), size); - output.erase(std::remove(output.begin(), output.end(), 0), output.end()); //remove intermediate 0-termination + vector_remove_if(output, [](char& c) { return c == 0; }); //remove intermediate 0-termination if (output.size() > 100) output.resize(100); return output; @@ -102,15 +102,13 @@ typedef int (WINAPI* CompareStringOrdinalFunc)(LPCWSTR lpString1, LPCWSTR lpString2, int cchCount2, BOOL bIgnoreCase); -SysDllFun<CompareStringOrdinalFunc> ordinalCompare; //caveat: function scope static initialization is not thread-safe in VS 2010! -boost::once_flag initCmpStrOrdOnce = BOOST_ONCE_INIT; +const SysDllFun<CompareStringOrdinalFunc> ordinalCompare = SysDllFun<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal"); } int z_impl::compareFilenamesWin(const wchar_t* a, const wchar_t* b, size_t sizeA, size_t sizeB) { - boost::call_once(initCmpStrOrdOnce, []() { ordinalCompare = SysDllFun<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal"); }); - + //caveat: function scope static initialization is not thread-safe in VS 2010! if (ordinalCompare) //this additional test has no noticeable performance impact { const int rv = ordinalCompare(a, //pointer to first string diff --git a/zen/zstring.h b/zen/zstring.h index 0ba9f108..ca5d99c8 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -85,24 +85,24 @@ public: //############################## helper functions ############################################# #if defined(FFS_WIN) || defined(FFS_LINUX) //Compare filenames: Windows does NOT distinguish between upper/lower-case, while Linux DOES -template <class T, template <class, class> class SP, class AP> int cmpFileName(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs); +template <class T, template <class, class> class SP, class AP> int cmpFileName(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs); struct LessFilename //case-insensitive on Windows, case-sensitive on Linux { template <class T, template <class, class> class SP, class AP> - bool operator()(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) const; + bool operator()(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs) const; }; struct EqualFilename //case-insensitive on Windows, case-sensitive on Linux { template <class T, template <class, class> class SP, class AP> - bool operator()(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) const; + bool operator()(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs) const; }; #endif #ifdef FFS_WIN template <template <class, class> class SP, class AP> -void makeUpper(Zbase<wchar_t, SP, AP>& str); +void makeUpper(zen::Zbase<wchar_t, SP, AP>& str); #endif #ifdef FFS_WIN //Windows stores filenames in wide character format @@ -121,7 +121,7 @@ const Zchar FILE_NAME_SEPARATOR = '/'; //"The reason for all the fuss above" - Loki/SmartPtr //a high-performant string for use as file name in multithreaded contexts -typedef Zbase<Zchar, StorageRefCountThreadSafe, AllocatorFreeStoreChecked> Zstring; +typedef zen::Zbase<Zchar, zen::StorageRefCountThreadSafe, AllocatorFreeStoreChecked> Zstring; @@ -159,7 +159,7 @@ void makeUpperCaseWin(wchar_t* str, size_t size); template <class T, template <class, class> class SP, class AP> inline -int cmpFileName(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) +int cmpFileName(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs) { #ifdef FFS_WIN return z_impl::compareFilenamesWin(lhs.data(), rhs.data(), lhs.length(), rhs.length()); @@ -171,7 +171,7 @@ int cmpFileName(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) template <class T, template <class, class> class SP, class AP> inline -bool LessFilename::operator()(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) const +bool LessFilename::operator()(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs) const { #ifdef FFS_WIN return z_impl::compareFilenamesWin(lhs.data(), rhs.data(), lhs.length(), rhs.length()) < 0; @@ -183,7 +183,7 @@ bool LessFilename::operator()(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP template <class T, template <class, class> class SP, class AP> inline -bool EqualFilename::operator()(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) const +bool EqualFilename::operator()(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs) const { #ifdef FFS_WIN return z_impl::compareFilenamesWin(lhs.data(), rhs.data(), lhs.length(), rhs.length()) == 0; @@ -197,7 +197,7 @@ bool EqualFilename::operator()(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, A #ifdef FFS_WIN template <template <class, class> class SP, class AP> inline -void makeUpper(Zbase<wchar_t, SP, AP>& str) +void makeUpper(zen::Zbase<wchar_t, SP, AP>& str) { z_impl::makeUpperCaseWin(str.begin(), str.length()); } |