summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rw-r--r--zen/assert_static.h6
-rw-r--r--zen/basic_math.h7
-rw-r--r--zen/com_error.h36
-rw-r--r--zen/com_ptr.h30
-rw-r--r--zen/debug_log.h105
-rw-r--r--zen/debug_new.cpp49
-rw-r--r--zen/deprecate.h20
-rw-r--r--zen/dir_watcher.cpp36
-rw-r--r--zen/disable_standby.h27
-rw-r--r--zen/dst_hack.cpp16
-rw-r--r--zen/file_error.h8
-rw-r--r--zen/file_handling.cpp185
-rw-r--r--zen/file_handling.h2
-rw-r--r--zen/file_id.cpp1
-rw-r--r--zen/file_io.cpp17
-rw-r--r--zen/file_io.h2
-rw-r--r--zen/file_traverser.cpp55
-rw-r--r--zen/file_traverser.h32
-rw-r--r--zen/fixed_list.h2
-rw-r--r--zen/int64.h32
-rw-r--r--zen/notify_removal.cpp8
-rw-r--r--zen/perf.h11
-rw-r--r--zen/privilege.cpp35
-rw-r--r--zen/process_status.h33
-rw-r--r--zen/stl_tools.h106
-rw-r--r--zen/string_base.h497
-rw-r--r--zen/string_tools.h399
-rw-r--r--zen/string_traits.h157
-rw-r--r--zen/symlink_target.h8
-rw-r--r--zen/time.h304
-rw-r--r--zen/type_tools.h13
-rw-r--r--zen/type_traits.h74
-rw-r--r--zen/utf8.h24
-rw-r--r--zen/win_ver.h2
-rw-r--r--zen/zstring.cpp12
-rw-r--r--zen/zstring.h18
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));
}
}
diff --git a/zen/perf.h b/zen/perf.h
index f9970d0a..688043a7 100644
--- a/zen/perf.h
+++ b/zen/perf.h
@@ -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
diff --git a/zen/utf8.h b/zen/utf8.h
index e72a8e3c..66c6df46 100644
--- a/zen/utf8.h
+++ b/zen/utf8.h
@@ -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());
}
bgstack15