summaryrefslogtreecommitdiff
path: root/shared
diff options
context:
space:
mode:
Diffstat (limited to 'shared')
-rw-r--r--shared/IFileOperation/file_op.cpp32
-rw-r--r--shared/IFileOperation/file_op.h6
-rw-r--r--shared/ShadowCopy/shadow.cpp42
-rw-r--r--shared/ShadowCopy/shadow.h21
-rw-r--r--shared/Taskbar_Seven/taskbar.cpp112
-rw-r--r--shared/Taskbar_Seven/taskbar.h27
-rw-r--r--shared/app_main.cpp6
-rw-r--r--shared/app_main.h2
-rw-r--r--shared/c_dll.h132
-rw-r--r--shared/check_exist.cpp32
-rw-r--r--shared/com_error.h3
-rw-r--r--shared/com_util.h6
-rw-r--r--shared/custom_button.cpp2
-rw-r--r--shared/debug_new.cpp19
-rw-r--r--shared/debug_new.h45
-rw-r--r--shared/dir_name.cpp (renamed from shared/drag_n_drop.cpp)178
-rw-r--r--shared/dir_name.h (renamed from shared/drag_n_drop.h)45
-rw-r--r--shared/dll_loader.cpp44
-rw-r--r--shared/dll_loader.h28
-rw-r--r--shared/dst_hack.cpp312
-rw-r--r--shared/dst_hack.h38
-rw-r--r--shared/file_handling.cpp237
-rw-r--r--shared/file_handling.h71
-rw-r--r--shared/file_id.cpp1
-rw-r--r--shared/file_io.cpp3
-rw-r--r--shared/file_traverser.cpp419
-rw-r--r--shared/file_traverser.h27
-rw-r--r--shared/global_func.cpp27
-rw-r--r--shared/global_func.h18
-rw-r--r--shared/guid.cpp10
-rw-r--r--shared/lock.cpp22
-rw-r--r--shared/lock.h26
-rw-r--r--shared/loki/SmallObj.cpp26
-rw-r--r--shared/long_path_prefix.cpp12
-rw-r--r--shared/perf.h30
-rw-r--r--shared/recycler.cpp20
-rw-r--r--shared/serialize.h10
-rw-r--r--shared/shadow.cpp38
-rw-r--r--shared/shadow.h9
-rw-r--r--shared/string_conv.h34
-rw-r--r--shared/system_constants.h10
-rw-r--r--shared/taskbar.cpp125
-rw-r--r--shared/taskbar.h2
-rw-r--r--shared/util.cpp199
-rw-r--r--shared/util.h11
-rw-r--r--shared/zbase.h1162
-rw-r--r--shared/zstring.cpp553
-rw-r--r--shared/zstring.h795
48 files changed, 2962 insertions, 2067 deletions
diff --git a/shared/IFileOperation/file_op.cpp b/shared/IFileOperation/file_op.cpp
index f37a2e66..6180a561 100644
--- a/shared/IFileOperation/file_op.cpp
+++ b/shared/IFileOperation/file_op.cpp
@@ -7,37 +7,31 @@
#include "file_op.h"
#include "../com_ptr.h"
#include "../com_error.h"
+#include "../c_dll.h"
-#define WIN32_LEAN_AND_MEAN
-#include "windows.h"
#include <Shellapi.h> // Included for shell constants such as FO_* values
#include <shobjidl.h> // Required for necessary shell dependencies
-
+#include <comdef.h>
#include <algorithm>
#include <string>
#include <cstdio>
-#include <comdef.h>
-
+#define WIN32_LEAN_AND_MEAN
+#include "windows.h"
-void writeString(const std::wstring& input, wchar_t* output, size_t outputBufferLen)
-{
- const size_t newSize = min(input.length() + 1, outputBufferLen); //including null-termination
- memcpy(output, input.c_str(), newSize * sizeof(wchar_t));
- output[newSize-1] = 0; //if output buffer is too small...
-}
+using namespace c_dll;
-namespace FileOp
+namespace fileop
{
std::wstring lastErrorMessage;
}
-bool FileOp::moveToRecycleBin(const wchar_t* fileNames[],
+bool fileop::moveToRecycleBin(const wchar_t* fileNames[],
size_t fileNo) //size of fileNames array
{
- using Util::ComPtr;
- using Util::generateErrorMsg;
+ using util::ComPtr;
+ using util::generateErrorMsg;
HRESULT hr;
// Create the IFileOperation interface
@@ -117,11 +111,11 @@ bool FileOp::moveToRecycleBin(const wchar_t* fileNames[],
}
-bool FileOp::copyFile(const wchar_t* sourceFile,
+bool fileop::copyFile(const wchar_t* sourceFile,
const wchar_t* targetFile)
{
- using Util::ComPtr;
- using Util::generateErrorMsg;
+ using util::ComPtr;
+ using util::generateErrorMsg;
HRESULT hr;
@@ -223,7 +217,7 @@ bool FileOp::copyFile(const wchar_t* sourceFile,
//if any of the functions above returns 'false', this message returns last error
-void FileOp::getLastError(wchar_t* errorMessage, size_t errorBufferLen)
+void fileop::getLastError(wchar_t* errorMessage, size_t errorBufferLen)
{
writeString(lastErrorMessage, errorMessage, errorBufferLen);
}
diff --git a/shared/IFileOperation/file_op.h b/shared/IFileOperation/file_op.h
index 8fa6a75b..97c75747 100644
--- a/shared/IFileOperation/file_op.h
+++ b/shared/IFileOperation/file_op.h
@@ -14,7 +14,7 @@
#endif
-namespace FileOp
+namespace fileop
{
//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize
@@ -32,8 +32,8 @@ void getLastError(wchar_t* errorMessage, size_t errorBufferLen);
//function typedefs
-typedef bool (*MoveToRecycleBinFct)(const wchar_t* fileNames[], size_t fileNo);
-typedef bool (*CopyFileFct)(const wchar_t* sourceFile, const wchar_t* targetFile);
+typedef bool (*MoveToRecycleBinFct)(const wchar_t* fileNames[], size_t fileNo);
+typedef bool (*CopyFileFct)(const wchar_t* sourceFile, const wchar_t* targetFile);
typedef void (*GetLastErrorFct)(wchar_t* errorMessage, size_t errorBufferLen);
//function names (use const pointers to ensure internal linkage)
diff --git a/shared/ShadowCopy/shadow.cpp b/shared/ShadowCopy/shadow.cpp
index 2e739146..a5c280e8 100644
--- a/shared/ShadowCopy/shadow.cpp
+++ b/shared/ShadowCopy/shadow.cpp
@@ -5,11 +5,15 @@
// **************************************************************************
//
#include "shadow.h"
+#include <algorithm>
+#include <string>
+#include <comdef.h>
+#include "../com_ptr.h"
+#include "../com_error.h"
+#include "../c_dll.h"
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
-#include "../com_ptr.h"
-#include "../com_error.h"
#ifdef USE_SHADOW_XP
#include "xp/inc/vss.h"
@@ -24,41 +28,32 @@
adapt!
#endif
-#include <algorithm>
-#include <string>
-#include <cstdio>
-#include <comdef.h>
+using namespace c_dll;
-//typedef GUID VSS_ID;
-
-void writeString(const wchar_t* input, wchar_t* output, unsigned int outputBufferLen)
+namespace
{
- const size_t newSize = min(wcslen(input) + 1, outputBufferLen); //including null-termination
- memcpy(output, input, newSize * sizeof(wchar_t));
- output[newSize-1] = 0; //if output buffer is too small...
-}
-
+typedef HandleProvider<shadow::ShadowHandle, util::ComPtr<IVssBackupComponents> > HandleShadowMap;
void writeErrorMsg(const wchar_t* input, HRESULT hr, wchar_t* output, unsigned int outputBufferLen)
{
- writeString(Util::generateErrorMsg(input, hr).c_str(), output, outputBufferLen);
+ writeString(util::generateErrorMsg(input, hr).c_str(), output, outputBufferLen);
+}
}
-
bool shadow::createShadowCopy(const wchar_t* volumeName,
wchar_t* shadowVolName,
unsigned int shadowBufferLen,
- void** backupHandle,
+ ShadowHandle* handle,
wchar_t* errorMessage,
unsigned int errorBufferLen)
{
- using Util::ComPtr;
- using Util::generateErrorMsg;
+ using util::ComPtr;
+ using util::generateErrorMsg;
//MessageBox(0, L"backup err", L"", 0); */
- *backupHandle = NULL;
+ *handle = 0;
HRESULT hr = NULL;
ComPtr<IVssBackupComponents> backupComp;
@@ -171,14 +166,13 @@ bool shadow::createShadowCopy(const wchar_t* volumeName,
VssFreeSnapshotProperties(&props);
- *backupHandle = backupComp.release(); //release ownership
+ *handle = HandleShadowMap::instance().insert(backupComp);
return true;
}
-void shadow::releaseShadowCopy(void* backupHandle)
+void shadow::releaseShadowCopy(ShadowHandle handle)
{
- if (backupHandle != NULL)
- static_cast<IVssBackupComponents*>(backupHandle)->Release();
+ HandleShadowMap::instance().remove(handle);
}
diff --git a/shared/ShadowCopy/shadow.h b/shared/ShadowCopy/shadow.h
index a9120e8a..f1100284 100644
--- a/shared/ShadowCopy/shadow.h
+++ b/shared/ShadowCopy/shadow.h
@@ -18,32 +18,35 @@ namespace shadow
{
//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize
+typedef size_t ShadowHandle;
//volumeName must end with "\", while shadowVolName does not end with "\"
SHADOWDLL_API
-bool createShadowCopy(const wchar_t* volumeName,
- wchar_t* shadowVolName,
- unsigned int shadowBufferLen,
- void** backupHandle,
- wchar_t* errorMessage,
- unsigned int errorBufferLen);
+bool createShadowCopy(const wchar_t* volumeName, //[in]
+ wchar_t* shadowVolName, //[out]
+ unsigned int shadowBufferLen, //[in]
+ ShadowHandle* handle, //[out]
+ wchar_t* errorMessage, //[out]
+ unsigned int errorBufferLen); //[in]
//don't forget to release the backupHandle after shadow copy is not needed anymore!
SHADOWDLL_API
-void releaseShadowCopy(void* backupHandle);
+void releaseShadowCopy(ShadowHandle handle);
+//##########################################################################################
+
//function typedefs
typedef bool (*CreateShadowCopyFct)(const wchar_t* volumeName,
wchar_t* shadowVolName,
unsigned int shadowBufferLen,
- void** backupHandle,
+ ShadowHandle* handle,
wchar_t* errorMessage,
unsigned int errorBufferLen);
-typedef void (*ReleaseShadowCopyFct)(void* backupHandle);
+typedef void (*ReleaseShadowCopyFct)(ShadowHandle handle);
//function names
const char* const createShadowCopyFctName = "createShadowCopy";
diff --git a/shared/Taskbar_Seven/taskbar.cpp b/shared/Taskbar_Seven/taskbar.cpp
index c9a2e7df..d1b1638c 100644
--- a/shared/Taskbar_Seven/taskbar.cpp
+++ b/shared/Taskbar_Seven/taskbar.cpp
@@ -11,69 +11,41 @@
#include <ShObjIdl.h>
#include <map>
-#include <string>
+#include <string>
#include <comdef.h>
#include "../com_error.h"
+#include "../com_ptr.h"
+#include "../c_dll.h"
-namespace
-{
-void writeString(const std::wstring& input, wchar_t* output, size_t outputBufferLen)
-{
- const size_t newSize = min(input.length() + 1, outputBufferLen); //including null-termination
- memcpy(output, input.c_str(), newSize * sizeof(wchar_t));
- output[newSize-1] = 0; //if output buffer is too small...
-}
+using namespace util;
+using namespace c_dll;
-using Util::generateErrorMsg;
-using TaskbarSeven::TBHandle;
-typedef std::map<TBHandle, ITaskbarList3*> TaskBarHandleMap;
-
-TaskbarSeven::TBHandle generateHandle()
+namespace
{
- static TBHandle handle = 0;
- return ++handle; //don't return 0! 0 is reserved for indicating failure
-}
-
-TaskBarHandleMap taskBarHandles;
-
std::wstring lastErrorMessage;
-}
-//##################################################################################################
-TaskbarSeven::TBHandle TaskbarSeven::init() //call on app initializaiton; returns handle
+ComPtr<ITaskbarList3> getInstance()
{
- ITaskbarList3* pto = NULL;
- HRESULT hr = CoCreateInstance(CLSID_TaskbarList,
- NULL,
- CLSCTX_ALL,
- IID_PPV_ARGS(&pto));
- if (FAILED(hr))
+ static ComPtr<ITaskbarList3> taskbarlist;
+ if (!taskbarlist)
{
- lastErrorMessage = generateErrorMsg(L"Error calling \"CoCreateInstance\".", hr);
- return 0;
+ HRESULT hr = CoCreateInstance(CLSID_TaskbarList,
+ NULL,
+ CLSCTX_ALL,
+ IID_PPV_ARGS(taskbarlist.init()));
+ if (FAILED(hr))
+ lastErrorMessage = generateErrorMsg(L"Error calling \"CoCreateInstance\".", hr);
}
- TBHandle newHandle = ::generateHandle();
- taskBarHandles[newHandle] = pto;
- return newHandle;
+ return taskbarlist;
}
-
-void TaskbarSeven::release(TBHandle handle) //release handle on app exit
-{
- TaskBarHandleMap::const_iterator iter = taskBarHandles.find(handle);
- if (iter != taskBarHandles.end())
- {
- if (iter->second != NULL)
- iter->second->Release();
- taskBarHandles.erase(iter);
- }
}
+//##################################################################################################
-bool TaskbarSeven::setStatus(TBHandle handle,
- void* hwnd, //HWND: window assciated to the taskbar icon
+bool tbseven::setStatus(void* hwnd, //HWND: window assciated to the taskbar icon
TaskBarStatus status)
{
TBPFLAG flag = TBPF_NORMAL;
@@ -96,45 +68,45 @@ bool TaskbarSeven::setStatus(TBHandle handle,
break;
}
- ITaskbarList3* pto = taskBarHandles[handle];
- if (pto)
+ ComPtr<ITaskbarList3> taskbarlist = getInstance();
+ if (!taskbarlist) //error msg already set
+ return false;
+
+ HRESULT hr = taskbarlist->SetProgressState(static_cast<HWND>(hwnd), //[in] HWND hwnd,
+ flag); //[in] TBPFLAG tbpFlags
+ if (FAILED(hr))
{
- HRESULT hr = pto->SetProgressState(static_cast<HWND>(hwnd), //[in] HWND hwnd,
- flag); //[in] TBPFLAG tbpFlags
- if (FAILED(hr))
- {
- lastErrorMessage = generateErrorMsg(L"Error calling \"SetProgressState\".", hr);
- return false;
- }
+ lastErrorMessage = generateErrorMsg(L"Error calling \"SetProgressState\".", hr);
+ return false;
}
+
return true;
}
-bool TaskbarSeven::setProgress(TBHandle handle,
- void* hwnd, //HWND: window assciated to the taskbar icon
+bool tbseven::setProgress(void* hwnd, //HWND: window assciated to the taskbar icon
size_t current,
size_t total)
{
- ITaskbarList3* pto = taskBarHandles[handle];
-
- if (pto)
+ ComPtr<ITaskbarList3> taskbarlist = getInstance();
+ if (!taskbarlist) //error msg already set
+ return false;
+
+ HRESULT hr = taskbarlist->SetProgressValue(
+ static_cast<HWND>(hwnd), //[in] HWND hwnd,
+ current, //[in] ULONGLONG ullCompleted,
+ total); //[in] ULONGLONG ullTotal
+ if (FAILED(hr))
{
- HRESULT hr = pto->SetProgressValue(
- static_cast<HWND>(hwnd), //[in] HWND hwnd,
- current, //[in] ULONGLONG ullCompleted,
- total); //[in] ULONGLONG ullTotal
- if (FAILED(hr))
- {
- lastErrorMessage = generateErrorMsg(L"Error calling \"SetProgressValue\".", hr);
- return false;
- }
+ lastErrorMessage = generateErrorMsg(L"Error calling \"SetProgressValue\".", hr);
+ return false;
}
+
return true;
}
-void TaskbarSeven::getLastError(wchar_t* errorMessage, size_t errorBufferLen)
+void tbseven::getLastError(wchar_t* errorMessage, size_t errorBufferLen)
{
writeString(lastErrorMessage, errorMessage, errorBufferLen);
}
diff --git a/shared/Taskbar_Seven/taskbar.h b/shared/Taskbar_Seven/taskbar.h
index 3b7abc51..34e122c0 100644
--- a/shared/Taskbar_Seven/taskbar.h
+++ b/shared/Taskbar_Seven/taskbar.h
@@ -14,7 +14,7 @@
#endif
-namespace TaskbarSeven
+namespace tbseven
{
enum TaskBarStatus
{
@@ -25,25 +25,16 @@ enum TaskBarStatus
STATUS_PAUSED
};
-typedef size_t TBHandle;
-
//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize
-DLL_FUNCTION_DECLARATION
-TBHandle init(); //returns handle; 0 on failure
-
-DLL_FUNCTION_DECLARATION
-void release(TBHandle handle); //release taskbar handle
DLL_FUNCTION_DECLARATION
-bool setStatus(TBHandle handle,
- void* hwnd, //HWND: window assciated to the taskbar icon
+bool setStatus(void* hwnd, //HWND: window assciated to the taskbar icon
TaskBarStatus status);
DLL_FUNCTION_DECLARATION
-bool setProgress(TBHandle handle,
- void* hwnd, //HWND: window assciated to the taskbar icon
+bool setProgress(void* hwnd, //HWND: window assciated to the taskbar icon
size_t current,
size_t total);
@@ -53,18 +44,14 @@ void getLastError(wchar_t* errorMessage, size_t errorBufferLen);
//function typedefs
-typedef TBHandle (*initFct)();
-typedef void (*releaseFct)(TBHandle handle);
-typedef bool (*setStatusFct)(TBHandle handle, void* hwnd, TaskBarStatus status);
-typedef bool (*setProgressFct)(TBHandle handle, void* hwnd, size_t current, size_t total);
-typedef void (*getLastErrorFct)(wchar_t* errorMessage, size_t errorBufferLen);
+typedef bool (*SetStatusFct)(void* hwnd, TaskBarStatus status);
+typedef bool (*SetProgressFct)(void* hwnd, size_t current, size_t total);
+typedef void (*GetLastErrorFct)(wchar_t* errorMessage, size_t errorBufferLen);
//function names (use const pointers to ensure internal linkage)
-const char* const initFctName = "init";
-const char* const releaseFctName = "release";
const char* const setStatusFctName = "setStatus";
const char* const setProgressFctName = "setProgress";
const char* const getLastErrorFctName = "getLastError";
}
-#endif //TASKBAR_SEVEN_DLL_H \ No newline at end of file
+#endif //TASKBAR_SEVEN_DLL_H
diff --git a/shared/app_main.cpp b/shared/app_main.cpp
index 7225ba62..3d42212a 100644
--- a/shared/app_main.cpp
+++ b/shared/app_main.cpp
@@ -11,7 +11,7 @@
using namespace ffs3;
-bool AppMainWindow::mainWndAct = false;
+bool AppMainWindow::mainWndActive = false;
void ffs3::AppMainWindow::setMainWindow(wxWindow* window)
@@ -19,11 +19,11 @@ void ffs3::AppMainWindow::setMainWindow(wxWindow* window)
wxTheApp->SetTopWindow(window);
wxTheApp->SetExitOnFrameDelete(true);
- mainWndAct = true;
+ mainWndActive = true;
}
bool AppMainWindow::mainWindowWasSet()
{
- return mainWndAct;
+ return mainWndActive;
}
diff --git a/shared/app_main.h b/shared/app_main.h
index d0b76122..7e4fa7e7 100644
--- a/shared/app_main.h
+++ b/shared/app_main.h
@@ -19,7 +19,7 @@ public:
static bool mainWindowWasSet();
private:
- static bool mainWndAct;
+ static bool mainWndActive;
};
}
diff --git a/shared/c_dll.h b/shared/c_dll.h
new file mode 100644
index 00000000..4d485893
--- /dev/null
+++ b/shared/c_dll.h
@@ -0,0 +1,132 @@
+// **************************************************************************
+// * 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-2010 ZenJu (zhnmju123 AT gmx.de) *
+// **************************************************************************
+//
+#ifndef C_DLL_HEADER
+#define C_DLL_HEADER
+
+#include <string>
+#include <map>
+#undef min
+#include <algorithm>
+
+
+namespace c_dll
+{
+void writeString(const std::wstring& input, wchar_t* output, size_t outputLen);
+
+
+//Convert handles to objects and vice versa
+template <class S, class T> //T: prefer managed object to ensure cleanup if remove() is not called
+class HandleProvider
+{
+public:
+ static HandleProvider& instance();
+ S insert(T object);
+ void remove(S handle);
+ T& retrieve(S handle); //return default-constructed object if not found
+
+private:
+ HandleProvider() {}
+ HandleProvider(const HandleProvider&);
+ HandleProvider& operator=(const HandleProvider&);
+ S generate();
+
+ std::map<S, T> handleMap;
+};
+/*
+Example:
+ typedef HandleProvider<TBHandle, ComPtr<ITaskbarList3> > HandleTaskbarMap;
+ HandleTaskbarMap::instance().insert(xyz);
+*/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//########################## inline implementation #############################
+inline
+void writeString(const std::wstring& input, wchar_t* output, size_t outputLen)
+{
+ if (outputLen > 0)
+ {
+ const size_t maxSize = std::min(input.length(), outputLen - 1);
+ std::copy(input.begin(), input.begin() + maxSize, output);
+ output[maxSize] = 0;
+ }
+}
+
+
+template <class S, class T>
+inline
+HandleProvider<S, T>& HandleProvider<S, T>::instance()
+{
+ static HandleProvider inst;
+ return inst;
+}
+
+
+//convert handles to objects and vice versa
+template <class S, class T>
+inline
+S HandleProvider<S, T>::insert(T object)
+{
+ S newHandle = generate();
+ handleMap.insert(std::make_pair(newHandle, object));
+ return newHandle;
+}
+
+
+template <class S, class T>
+inline
+void HandleProvider<S, T>::remove(S handle)
+{
+ handleMap.erase(handle);
+}
+
+
+template <class S, class T>
+inline
+T& HandleProvider<S, T>::retrieve(S handle) //return default-constructed object if not found
+{
+ return handleMap[handle];
+}
+
+
+template <class S, class T>
+inline
+S HandleProvider<S, T>::generate()
+{
+ static S handle = 0;
+ return ++handle; //don't return 0! 0 is reserved for indicating failure
+}
+
+
+}
+
+#endif //C_DLL_HEADER \ No newline at end of file
diff --git a/shared/check_exist.cpp b/shared/check_exist.cpp
index fcb865fd..8bc629b5 100644
--- a/shared/check_exist.cpp
+++ b/shared/check_exist.cpp
@@ -1,34 +1,42 @@
+// **************************************************************************
+// * 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-2010 ZenJu (zhnmju123 AT gmx.de) *
+// **************************************************************************
+//
#include "check_exist.h"
#include "file_handling.h"
-#include <boost/thread.hpp>
+#include "boost_thread_wrap.h" //include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
+/*
#ifdef __MINGW32__
//oh well, nothing is for free...
//https://svn.boost.org/trac/boost/ticket/4258
-#warning fix this issue at some time...
-extern "C" void tss_cleanup_implemented() {}
+extern "C" void tss_cleanup_implemented() {};
#endif
-
+*/
namespace
{
+typedef Zbase<Zchar, StorageDeepCopy> BasicString; //thread safe string class
+
template <bool (*testExist)(const Zstring&)>
class ExistenceChecker
{
public:
- ExistenceChecker(const Zstring& filename, const boost::shared_ptr<bool>& isExisting) :
- filename_(filename.c_str()), //deep copy: worker thread may run longer than main! avoid shared data
- isExisting_(isExisting) {} //not accessed during thread run
+ ExistenceChecker(const BasicString& filename, const boost::shared_ptr<bool>& isExisting) :
+ filename_(filename), //deep copy: worker thread may run longer than main! avoid shared data
+ isExisting_(isExisting) {} //not accessed during thread run
void operator()()
{
- *isExisting_ = testExist(filename_); //throw()
+ *isExisting_ = testExist(filename_.c_str()); //throw()
}
private:
- const Zstring filename_; //no reference, lifetime not known
+ const BasicString filename_; //no referencing, 'cause lifetime not known!
boost::shared_ptr<bool> isExisting_;
};
@@ -40,8 +48,8 @@ util::ResultExist checkExistence(const Zstring& objName, size_t timeout) //timeo
boost::shared_ptr<bool> isExisting(new bool(false));
- ExistenceChecker<fun> task(objName, isExisting);
- boost::thread worker(task);
+ ExistenceChecker<fun> task(objName.c_str(), isExisting);
+ boost::thread worker(task); //note: task is copied => using thread safe string!
if (worker.timed_join(boost::posix_time::milliseconds(timeout)))
return *isExisting ? EXISTING_TRUE : EXISTING_FALSE;
@@ -56,7 +64,7 @@ util::ResultExist checkExistence(const Zstring& objName, size_t timeout) //timeo
#warning migrate this at some time...
#endif
/*
- unfortunately packaged_task/future is not mature enough to be used...
+ unfortunately packaged_task/future is not mature enough to be used...
boost::packaged_task<bool> pt(boost::bind(fun, objName.c_str())); //attention: Zstring is not thread-safe => make deep copy
boost::unique_future<bool> fut = pt.get_future();
diff --git a/shared/com_error.h b/shared/com_error.h
index ab365977..73551f6e 100644
--- a/shared/com_error.h
+++ b/shared/com_error.h
@@ -28,6 +28,9 @@ public:
}
private:
+ ComException(const ComException&);
+ ComException& operator=(const ComException&);
+
const std::wstring message_;
const HRESULT hr_;
};
diff --git a/shared/com_util.h b/shared/com_util.h
index bda3c732..22e2075f 100644
--- a/shared/com_util.h
+++ b/shared/com_util.h
@@ -4,8 +4,8 @@
// * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) *
// **************************************************************************
//
-#ifndef COM_UTILÎTY_HEADER
-#define COM_UTILÎTY_HEADER
+#ifndef COM_UTILITY_HEADER
+#define COM_UTILITY_HEADER
#include "com_ptr.h"
#include <string>
@@ -152,4 +152,4 @@ const BSTR Bstring::get() const
}
-#endif //COM_UTILÎTY_HEADER \ No newline at end of file
+#endif //COM_UTILITY_HEADER \ No newline at end of file
diff --git a/shared/custom_button.cpp b/shared/custom_button.cpp
index fec3b4bb..7154814b 100644
--- a/shared/custom_button.cpp
+++ b/shared/custom_button.cpp
@@ -7,6 +7,8 @@
#include "custom_button.h"
#include <wx/dcmemory.h>
#include <wx/image.h>
+#include <algorithm>
+
wxButtonWithImage::wxButtonWithImage(wxWindow *parent,
wxWindowID id,
diff --git a/shared/debug_new.cpp b/shared/debug_new.cpp
index f5cd8368..0c0c8b52 100644
--- a/shared/debug_new.cpp
+++ b/shared/debug_new.cpp
@@ -5,15 +5,14 @@
// **************************************************************************
//
#include "debug_new.h"
-#include <wx/msw/wrapwin.h> //includes "windows.h"
-#include "DbgHelp.h"
-#pragma message("Warning! Include this header for error analysis builds only!")
-#ifndef _MSC_VER
-use in Visual C++ only!
+#ifdef _MSC_VER
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include "DbgHelp.h" //available for MSC only
#endif
+#ifdef _MSC_VER
namespace
{
LONG WINAPI writeDumpOnException(EXCEPTION_POINTERS* pExceptionInfo)
@@ -28,8 +27,8 @@ LONG WINAPI writeDumpOnException(EXCEPTION_POINTERS* pExceptionInfo)
MINIDUMP_EXCEPTION_INFORMATION* exceptParam = pExceptionInfo ? &exInfo : NULL;
::MiniDumpWriteDump(
- GetCurrentProcess(), //__in HANDLE hProcess,
- GetCurrentProcessId(), //__in DWORD ProcessId,
+ ::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,
@@ -49,11 +48,13 @@ struct WriteDumpOnUnhandledException
{
::SetUnhandledExceptionFilter(writeDumpOnException);
}
-} dummy; //ensure that a Dump is written for uncaught exceptions
+} dummy; //ensure that a dump-file is written for uncaught exceptions
}
-void MemoryDump::writeMinidump()
+void mem_check::writeMinidump()
{
writeDumpOnException(NULL);
}
+
+#endif //_MSC_VER
diff --git a/shared/debug_new.h b/shared/debug_new.h
index c9c3dbf6..e96b1e48 100644
--- a/shared/debug_new.h
+++ b/shared/debug_new.h
@@ -12,15 +12,21 @@
#include <cstdlib> //malloc(), free()
-/*all this header does is to globally overwrite "operator new" to give some more detailed error messages and write memory dumps
+#ifndef _MSC_VER
+#error currently for use with MSC only
+#endif
+
+/*overwrite "operator new" to get more detailed error messages on bad_alloc, detect memory leaks and write memory dumps
Usage:
- - Include everywhere before any other file: $(ProjectDir)\shared\debug_new.h
- For Minidumps:
- - Compile "debug_new.cpp"
- - Include library "Dbghelp.lib"
- - Compile in Debug build (need Symbols and less restrictive Optimization)
+- Include everywhere before any other file: $(ProjectDir)\shared\debug_new.h
+For Minidumps:
+- Compile "debug_new.cpp"
+- Include library "Dbghelp.lib"
+- Compile in Debug build (need Symbols and less restrictive Optimization)
*/
+namespace mem_check
+{
class BadAllocDetailed : public std::bad_alloc
{
public:
@@ -41,7 +47,7 @@ private:
template <class T>
static std::string numberToString(const T& number) //convert number to string the C++ way
{
- std::stringstream ss;
+ std::ostringstream ss;
ss << number;
return ss.str();
}
@@ -50,46 +56,43 @@ private:
};
#ifdef _MSC_VER
-namespace MemoryDump
-{
void writeMinidump();
-}
#endif
+}
inline
-void* operator new(size_t allocSize)
+void* operator new(size_t size)
{
- void* newMem = ::malloc(allocSize);
+ void* newMem = ::malloc(size);
if (!newMem)
{
#ifdef _MSC_VER
- MemoryDump::writeMinidump();
+ mem_check::writeMinidump();
#endif
- throw BadAllocDetailed(allocSize);
+ throw mem_check::BadAllocDetailed(size);
}
return newMem;
}
inline
-void* operator new[](size_t allocSize)
+void operator delete(void* ptr)
{
- return operator new(allocSize);
+ ::free(ptr);
}
inline
-void operator delete(void* memory)
+void* operator new[](size_t size)
{
- ::free(memory);
+ return operator new(size);
}
inline
-void operator delete[](void* memory)
+void operator delete[](void* ptr)
{
- operator delete(memory);
+ operator delete(ptr);
}
#endif // DEBUGNEW_H_INCLUDED
-
diff --git a/shared/drag_n_drop.cpp b/shared/dir_name.cpp
index c4fc98f6..40e840f5 100644
--- a/shared/drag_n_drop.cpp
+++ b/shared/dir_name.cpp
@@ -4,16 +4,17 @@
// * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) *
// **************************************************************************
//
-#include "drag_n_drop.h"
+#include "dir_name.h"
#include <wx/dnd.h>
#include <wx/window.h>
-#include <wx/combobox.h>
#include <wx/textctrl.h>
-#include <wx/filepicker.h>
#include <wx/filename.h>
#include "file_handling.h"
#include "string_conv.h"
#include "check_exist.h"
+#include "util.h"
+#include "system_constants.h"
+#include <wx/statbox.h>
//define new event type
@@ -41,9 +42,68 @@ public:
const wxWindow* dropWindow_;
};
+
//##############################################################################################################
+namespace
+{
+void setDirectoryNameImpl(const wxString& dirname, wxDirPickerCtrl* dirPicker, wxWindow& tooltipWnd, wxStaticBoxSizer* staticBox, size_t timeout)
+{
+ using namespace ffs3;
+
+ const wxString dirFormatted = zToWx(getFormattedDirectoryName(wxToZ(dirname)));
+
+ tooltipWnd.SetToolTip(dirFormatted);
+
+ if (staticBox)
+ {
+ //change static box label only if there is a real difference to what is shown in wxTextCtrl anyway
+ wxString dirNormalized = dirname;
+ dirNormalized.Trim(true);
+ dirNormalized.Trim(false);
+ if (!dirNormalized.empty() && !dirNormalized.EndsWith(zToWx(common::FILE_NAME_SEPARATOR)))
+ dirNormalized += zToWx(common::FILE_NAME_SEPARATOR);
+
+ staticBox->GetStaticBox()->SetLabel(dirNormalized == dirFormatted ? wxString(_("Drag && drop")) : dirFormatted);
+ }
+ if (dirPicker)
+ {
+ if (util::dirExists(wxToZ(dirFormatted), timeout) == util::EXISTING_TRUE) //potentially slow network access: wait 200ms at most
+ dirPicker->SetPath(dirFormatted);
+ }
+}
+
+void setDirectoryName(const wxString& dirname,
+ wxTextCtrl* txtCtrl,
+ wxDirPickerCtrl* dirPicker,
+ wxWindow& tooltipWnd,
+ size_t timeout = 200) //pointers are optional
+{
+ if (txtCtrl)
+ txtCtrl->ChangeValue(dirname);
+ setDirectoryNameImpl(dirname, dirPicker, tooltipWnd, NULL, timeout);
+}
+
+
+void setDirectoryName(const wxString& dirname,
+ wxComboBox* comboBox,
+ wxDirPickerCtrl* dirPicker,
+ wxWindow& tooltipWnd,
+ wxStaticBoxSizer& staticBox,
+ size_t timeout = 200) //pointers are optional
+{
+ if (comboBox)
+ {
+ comboBox->SetSelection(wxNOT_FOUND);
+ comboBox->SetValue(dirname);
+ }
+ setDirectoryNameImpl(dirname, dirPicker, tooltipWnd, &staticBox, timeout);
+}
+}
+
+
+//##############################################################################################################
class WindowDropTarget : public wxFileDropTarget
{
public:
@@ -70,35 +130,36 @@ private:
};
-
//##############################################################################################################
-
-using ffs3::DragDropOnMainDlg;
-
-DragDropOnMainDlg::DragDropOnMainDlg(wxWindow* dropWindow1,
- wxWindow* dropWindow2,
- wxDirPickerCtrl* dirPicker,
- wxComboBox* dirName) :
+using ffs3::DirectoryNameMainDlg;
+
+DirectoryNameMainDlg::DirectoryNameMainDlg(
+ wxWindow* dropWindow1,
+ wxWindow* dropWindow2,
+ wxDirPickerCtrl* dirPicker,
+ wxComboBox* dirName,
+ wxStaticBoxSizer* staticBox) :
dropWindow1_(dropWindow1),
dropWindow2_(dropWindow2),
dirPicker_(dirPicker),
- dirName_(dirName)
+ dirName_(dirName),
+ staticBox_(staticBox)
{
//prepare drag & drop
dropWindow1->SetDropTarget(new WindowDropTarget(dropWindow1)); //takes ownership
dropWindow2->SetDropTarget(new WindowDropTarget(dropWindow2)); //takes ownership
//redirect drag & drop event back to this class
- dropWindow1->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DragDropOnMainDlg::OnFilesDropped), NULL, this);
- dropWindow2->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DragDropOnMainDlg::OnFilesDropped), NULL, this);
+ dropWindow1->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DirectoryNameMainDlg::OnFilesDropped), NULL, this);
+ dropWindow2->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DirectoryNameMainDlg::OnFilesDropped), NULL, this);
//keep dirPicker and dirName synchronous
- dirName-> Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DragDropOnMainDlg::OnWriteDirManually), NULL, this );
- dirPicker->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler(DragDropOnMainDlg::OnDirSelected), NULL, this );
+ dirName-> Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DirectoryNameMainDlg::OnWriteDirManually), NULL, this );
+ dirPicker->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler(DirectoryNameMainDlg::OnDirSelected), NULL, this );
}
-void DragDropOnMainDlg::OnFilesDropped(FFSFileDropEvent& event)
+void DirectoryNameMainDlg::OnFilesDropped(FFSFileDropEvent& event)
{
if (event.filesDropped_.empty())
return;
@@ -110,52 +171,50 @@ void DragDropOnMainDlg::OnFilesDropped(FFSFileDropEvent& event)
{
wxString fileName = event.filesDropped_[0];
if (wxDirExists(fileName))
- {
- dirName_->SetSelection(wxNOT_FOUND);
- dirName_->SetValue(fileName);
- dirPicker_->SetPath(fileName);
- }
+ setDirectoryName(fileName, dirName_, dirPicker_, *dirName_, *staticBox_);
else
{
fileName = wxFileName(fileName).GetPath();
if (wxDirExists(fileName))
- {
- dirName_->SetSelection(wxNOT_FOUND);
- dirName_->SetValue(fileName);
- dirPicker_->SetPath(fileName);
- }
+ setDirectoryName(fileName, dirName_, dirPicker_, *dirName_, *staticBox_);
}
}
}
}
-void DragDropOnMainDlg::OnWriteDirManually(wxCommandEvent& event)
+void DirectoryNameMainDlg::OnWriteDirManually(wxCommandEvent& event)
{
- const Zstring newDir = getFormattedDirectoryName(wxToZ(event.GetString()));
-
- if (util::dirExists(newDir, 100) == util::EXISTING_TRUE) //potentially slow network access: wait 100 ms at most
- dirPicker_->SetPath(zToWx(newDir));
-
+ setDirectoryName(event.GetString(), NULL, dirPicker_, *dirName_, *staticBox_, 100); //potentially slow network access: wait 100 ms at most
event.Skip();
}
-void DragDropOnMainDlg::OnDirSelected(wxFileDirPickerEvent& event)
+void DirectoryNameMainDlg::OnDirSelected(wxFileDirPickerEvent& event)
{
const wxString newPath = event.GetPath();
- dirName_->SetSelection(wxNOT_FOUND);
- dirName_->SetValue(newPath);
+ setDirectoryName(newPath, dirName_, NULL, *dirName_, *staticBox_);
event.Skip();
}
-//##############################################################################################################
+
+Zstring DirectoryNameMainDlg::getName() const
+{
+ return wxToZ(dirName_->GetValue());
+}
-using ffs3::DragDropOnDlg;
+void DirectoryNameMainDlg::setName(const Zstring& dirname)
+{
+ setDirectoryName(zToWx(dirname), dirName_, dirPicker_, *dirName_, *staticBox_);
+}
+
+
+//##############################################################################################################
+using ffs3::DirectoryName;
-DragDropOnDlg::DragDropOnDlg(wxWindow* dropWindow,
+DirectoryName::DirectoryName(wxWindow* dropWindow,
wxDirPickerCtrl* dirPicker,
wxTextCtrl* dirName) :
dropWindow_(dropWindow),
@@ -166,15 +225,15 @@ DragDropOnDlg::DragDropOnDlg(wxWindow* dropWindow,
dropWindow->SetDropTarget(new WindowDropTarget(dropWindow)); //takes ownership
//redirect drag & drop event back to this class
- dropWindow->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DragDropOnDlg::OnFilesDropped), NULL, this);
+ dropWindow->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DirectoryName::OnFilesDropped), NULL, this);
//keep dirPicker and dirName synchronous
- dirName->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DragDropOnDlg::OnWriteDirManually ), NULL, this );
- dirPicker->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( DragDropOnDlg::OnDirSelected ), NULL, this );
+ dirName->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DirectoryName::OnWriteDirManually ), NULL, this );
+ dirPicker->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( DirectoryName::OnDirSelected ), NULL, this );
}
-void DragDropOnDlg::OnFilesDropped(FFSFileDropEvent& event)
+void DirectoryName::OnFilesDropped(FFSFileDropEvent& event)
{
if (event.filesDropped_.empty())
return;
@@ -183,37 +242,40 @@ void DragDropOnDlg::OnFilesDropped(FFSFileDropEvent& event)
{
wxString fileName = event.filesDropped_[0];
if (wxDirExists(fileName))
- {
- dirName_->SetValue(fileName);
- dirPicker_->SetPath(fileName);
- }
+ setDirectoryName(fileName, dirName_, dirPicker_, *dirName_);
else
{
fileName = wxFileName(fileName).GetPath();
if (wxDirExists(fileName))
- {
- dirName_->SetValue(fileName);
- dirPicker_->SetPath(fileName);
- }
+ setDirectoryName(fileName, dirName_, dirPicker_, *dirName_);
}
}
}
-void DragDropOnDlg::OnWriteDirManually(wxCommandEvent& event)
+void DirectoryName::OnWriteDirManually(wxCommandEvent& event)
{
- const Zstring newDir = ffs3::getFormattedDirectoryName(wxToZ(event.GetString()));
- if (util::dirExists(newDir, 100) == util::EXISTING_TRUE) //potentially slow network access: wait 100 ms at most
- dirPicker_->SetPath(zToWx(newDir));
-
+ setDirectoryName(event.GetString(), NULL, dirPicker_, *dirName_, 100); //potentially slow network access: wait 100 ms at most
event.Skip();
}
-void DragDropOnDlg::OnDirSelected(wxFileDirPickerEvent& event)
+void DirectoryName::OnDirSelected(wxFileDirPickerEvent& event)
{
const wxString newPath = event.GetPath();
- dirName_->SetValue(newPath);
+ setDirectoryName(newPath, dirName_, NULL, *dirName_);
event.Skip();
}
+
+
+Zstring DirectoryName::getName() const
+{
+ return wxToZ(dirName_->GetValue());
+}
+
+
+void DirectoryName::setName(const Zstring& dirname)
+{
+ setDirectoryName(zToWx(dirname), dirName_, dirPicker_, *dirName_);
+}
diff --git a/shared/drag_n_drop.h b/shared/dir_name.h
index 88bb68c4..163caaad 100644
--- a/shared/drag_n_drop.h
+++ b/shared/dir_name.h
@@ -9,30 +9,33 @@
#include <wx/event.h>
#include <vector>
+#include <wx/sizer.h>
+#include <wx/filepicker.h>
+#include <wx/combobox.h>
+#include "zstring.h"
+
-class wxWindow;
-class wxDirPickerCtrl;
-class wxComboBox;
-class wxTextCtrl;
-class wxString;
class FFSFileDropEvent;
class wxCommandEvent;
class wxFileDirPickerEvent;
-
namespace ffs3
{
-//add drag and drop functionality, coordinating a wxWindow, wxDirPickerCtrl, and wxComboBox/wxTextCtrl
+//handle drag and drop, tooltip, label and manual input, coordinating a wxWindow, wxDirPickerCtrl, and wxComboBox/wxTextCtrl
-class DragDropOnMainDlg : private wxEvtHandler
+class DirectoryNameMainDlg : private wxEvtHandler
{
public:
- DragDropOnMainDlg(wxWindow* dropWindow1,
- wxWindow* dropWindow2,
- wxDirPickerCtrl* dirPicker,
- wxComboBox* dirName);
+ DirectoryNameMainDlg(wxWindow* dropWindow1,
+ wxWindow* dropWindow2,
+ wxDirPickerCtrl* dirPicker,
+ wxComboBox* dirName,
+ wxStaticBoxSizer* staticBox);
+
+ virtual ~DirectoryNameMainDlg() {}
- virtual ~DragDropOnMainDlg() {}
+ Zstring getName() const;
+ void setName(const Zstring& dirname);
virtual bool AcceptDrop(const std::vector<wxString>& droppedFiles) = 0; //return true if drop should be processed
@@ -41,20 +44,24 @@ private:
void OnWriteDirManually(wxCommandEvent& event);
void OnDirSelected(wxFileDirPickerEvent& event);
- const wxWindow* dropWindow1_;
- const wxWindow* dropWindow2_;
- wxDirPickerCtrl* dirPicker_;
- wxComboBox* dirName_;
+ const wxWindow* dropWindow1_;
+ const wxWindow* dropWindow2_;
+ wxDirPickerCtrl* dirPicker_;
+ wxComboBox* dirName_;
+ wxStaticBoxSizer* staticBox_;
};
-class DragDropOnDlg: private wxEvtHandler
+class DirectoryName: private wxEvtHandler
{
public:
- DragDropOnDlg(wxWindow* dropWindow,
+ DirectoryName(wxWindow* dropWindow,
wxDirPickerCtrl* dirPicker,
wxTextCtrl* dirName);
+ Zstring getName() const;
+ void setName(const Zstring& dirname);
+
private:
void OnFilesDropped(FFSFileDropEvent& event);
void OnWriteDirManually(wxCommandEvent& event);
diff --git a/shared/dll_loader.cpp b/shared/dll_loader.cpp
index 6aa48fd7..61a5a2fb 100644
--- a/shared/dll_loader.cpp
+++ b/shared/dll_loader.cpp
@@ -11,7 +11,7 @@
namespace
{
-class DllHandler //dynamically load "kernel32.dll"
+class DllHandler
{
public:
static DllHandler& getInstance()
@@ -20,12 +20,15 @@ public:
return instance;
}
- HINSTANCE getHandle(const std::wstring& libraryName)
+ HMODULE getHandle(const std::wstring& libraryName)
{
HandleMap::const_iterator foundEntry = handles.find(libraryName);
if (foundEntry == handles.end())
{
- HINSTANCE newHandle = ::LoadLibrary(libraryName.c_str());
+ if (libraryName.empty())
+ return ::GetModuleHandle(NULL); //return handle to calling executable
+
+ HMODULE newHandle = ::LoadLibrary(libraryName.c_str());
if (newHandle != NULL)
handles.insert(std::make_pair(libraryName, newHandle));
@@ -37,6 +40,8 @@ public:
private:
DllHandler() {}
+ DllHandler(const DllHandler&);
+ DllHandler& operator=(const DllHandler&);
~DllHandler()
{
@@ -44,18 +49,43 @@ private:
::FreeLibrary(i->second);
}
- typedef std::map<std::wstring, HINSTANCE> HandleMap;
+ typedef std::map<std::wstring, HMODULE> HandleMap;
HandleMap handles; //only valid handles here!
};
}
-void* util::loadSymbol(const std::wstring& libraryName, const std::string& functionName)
+FARPROC util::loadSymbol(const std::wstring& libraryName, const std::string& functionName)
{
- const HINSTANCE libHandle = DllHandler::getInstance().getHandle(libraryName);
+ const HMODULE libHandle = DllHandler::getInstance().getHandle(libraryName);
if (libHandle != NULL)
- return reinterpret_cast<void*>(::GetProcAddress(libHandle, functionName.c_str()));
+ return ::GetProcAddress(libHandle, functionName.c_str());
else
return NULL;
}
+
+
+std::string util::getResourceStream(const std::wstring& libraryName, size_t resourceId)
+{
+ std::string output;
+ const HMODULE module = DllHandler::getInstance().getHandle(libraryName);
+ if (module)
+ {
+ const HRSRC res = ::FindResource(module, MAKEINTRESOURCE(resourceId), RT_RCDATA);
+ if (res != NULL)
+ {
+ const HGLOBAL resHandle = ::LoadResource(module, res);
+ if (resHandle != NULL)
+ {
+ const char* stream = static_cast<const char*>(::LockResource(resHandle));
+ if (stream)
+ {
+ const DWORD streamSize = ::SizeofResource(module, res);
+ output.assign(stream, streamSize);
+ }
+ }
+ }
+ }
+ return output;
+}
diff --git a/shared/dll_loader.h b/shared/dll_loader.h
index 86723c68..b3fa5218 100644
--- a/shared/dll_loader.h
+++ b/shared/dll_loader.h
@@ -8,16 +8,29 @@
#define DLLLOADER_H_INCLUDED
#include <string>
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+
namespace util
{
-//load function from a DLL library, e.g. from kernel32.dll
-//NOTE: you're allowed to take a static reference to the return value to optimize performance! :)
+/*
+load function from a DLL library, e.g. from kernel32.dll
+NOTE: you're allowed to take a static reference to the return value to optimize performance! :)
+*/
template <typename FunctionType>
-FunctionType loadDllFunction(const std::wstring& libraryName, const std::string& functionName);
+FunctionType getDllFun(const std::wstring& libraryName, const std::string& functionName);
+
+/*
+extract binary resources from .exe/.dll:
+-- resource.h --
+#define MY_BINARY_RESOURCE 1337
+-- resource.rc --
+MY_BINARY_RESOURCE RCDATA "filename.dat"
+*/
+std::string getResourceStream(const std::wstring& libraryName, size_t resourceId);
@@ -29,19 +42,14 @@ FunctionType loadDllFunction(const std::wstring& libraryName, const std::string&
//---------------Inline Implementation---------------------------------------------------
-void* loadSymbol(const std::wstring& libraryName, const std::string& functionName);
+FARPROC loadSymbol(const std::wstring& libraryName, const std::string& functionName);
template <typename FunctionType>
inline
-FunctionType loadDllFunction(const std::wstring& libraryName, const std::string& functionName)
+FunctionType getDllFun(const std::wstring& libraryName, const std::string& functionName)
{
return reinterpret_cast<FunctionType>(loadSymbol(libraryName, functionName));
}
-
-#ifndef FFS_WIN
-use in windows build only!
-#endif
-
}
#endif // DLLLOADER_H_INCLUDED
diff --git a/shared/dst_hack.cpp b/shared/dst_hack.cpp
new file mode 100644
index 00000000..87ed6c2f
--- /dev/null
+++ b/shared/dst_hack.cpp
@@ -0,0 +1,312 @@
+#include "dst_hack.h"
+#include "system_constants.h"
+#include <wx/intl.h>
+#include "long_path_prefix.h"
+#include "string_conv.h"
+#include "system_func.h"
+#include <wx/longlong.h>
+#include "assert_static.h"
+#include <bitset>
+#include "global_func.h"
+#include <limits>
+
+
+bool dst::isFatDrive(const Zstring& fileName) //throw()
+{
+ using namespace ffs3;
+
+ const size_t BUFFER_SIZE = MAX_PATH + 1;
+ wchar_t buffer[BUFFER_SIZE];
+
+//this call is expensive: ~1.5 ms!
+// if (!::GetVolumePathName(applyLongPathPrefix(fileName).c_str(), //__in LPCTSTR lpszFileName,
+// buffer, //__out LPTSTR lpszVolumePathName,
+// BUFFER_SIZE)) //__in DWORD cchBufferLength
+// ...
+// Zstring volumePath = buffer;
+// if (!volumePath.EndsWith(common::FILE_NAME_SEPARATOR)) //a trailing backslash is required
+// volumePath += common::FILE_NAME_SEPARATOR;
+
+ //fast ::GetVolumePathName() clone: let's hope it's not too simple (doesn't honor mount points)
+ const Zstring nameFmt = removeLongPathPrefix(fileName); //throw()
+ const size_t pos = nameFmt.find(Zstr(":\\"));
+ if (pos != 1) //expect single letter volume
+ return false;
+ const Zstring volumePath(nameFmt.c_str(), 3);
+
+ //suprisingly fast: ca. 0.03 ms per call!
+ if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName,
+ NULL, //__out LPTSTR lpVolumeNameBuffer,
+ 0, //__in DWORD nVolumeNameSize,
+ NULL, //__out_opt LPDWORD lpVolumeSerialNumber,
+ NULL, //__out_opt LPDWORD lpMaximumComponentLength,
+ NULL, //__out_opt LPDWORD lpFileSystemFlags,
+ buffer, //__out LPTSTR lpFileSystemNameBuffer,
+ BUFFER_SIZE)) //__in DWORD nFileSystemNameSize
+ {
+ assert(false); //shouldn't happen
+ return false;
+ }
+ const Zstring fileSystem = buffer;
+
+ //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised)
+ return fileSystem == Zstr("FAT") ||
+ fileSystem == Zstr("FAT32");
+}
+
+
+namespace
+{
+FILETIME utcToLocal(const FILETIME& utcTime) //throw (std::runtime_error)
+{
+ //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC:
+ FILETIME localTime = {};
+ if (!::FileTimeToLocalFileTime(
+ &utcTime, //__in const FILETIME *lpFileTime,
+ &localTime)) //__out LPFILETIME lpLocalFileTime
+ {
+ const wxString errorMessage = wxString(_("Conversion error:")) + wxT(" FILETIME -> local FILETIME: ") +
+ wxT("(") + wxULongLong(utcTime.dwHighDateTime, utcTime.dwLowDateTime).ToString() + wxT(") ") + wxT("\n\n") + ffs3::getLastErrorFormatted();
+ throw std::runtime_error(std::string((errorMessage).ToUTF8()));
+ }
+ return localTime;
+}
+
+
+FILETIME localToUtc(const FILETIME& localTime) //throw (std::runtime_error)
+{
+ //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC:
+ FILETIME utcTime = {};
+ if (!::LocalFileTimeToFileTime(
+ &localTime, //__in const FILETIME *lpLocalFileTime,
+ &utcTime)) //__out LPFILETIME lpFileTime
+ {
+ const wxString errorMessage = wxString(_("Conversion error:")) + wxT(" local FILETIME -> FILETIME: ") +
+ wxT("(") + wxULongLong(localTime.dwHighDateTime, localTime.dwLowDateTime).ToString() + wxT(") ") + wxT("\n\n") + ffs3::getLastErrorFormatted();
+ throw std::runtime_error(std::string((errorMessage).ToUTF8()));
+ }
+ return utcTime;
+}
+
+
+int cmpFileTime(const FILETIME& a, const FILETIME& b)
+{
+ if (a.dwHighDateTime != b.dwHighDateTime)
+ return a.dwHighDateTime - b.dwHighDateTime;
+ return a.dwLowDateTime - b.dwLowDateTime;
+}
+
+
+template <class T> //convert wxULongLong and wxLongLong to FILETIME
+inline
+FILETIME toFiletime(const T& number)
+{
+ assert_static(sizeof(DWORD) == sizeof(long));
+ assert_static(sizeof(long) == 4);
+ assert(number.GetHi() >= 0); //for wxLongLong
+
+ FILETIME output = {};
+ output.dwHighDateTime = number.GetHi();
+ output.dwLowDateTime = number.GetLo();
+ return output;
+}
+
+
+inline
+wxULongLong toULonglong(const FILETIME& fileTime)
+{
+ assert_static(sizeof(DWORD) == sizeof(long));
+ assert_static(sizeof(long) == 4);
+
+ return wxULongLong(fileTime.dwHighDateTime, fileTime.dwLowDateTime);
+}
+
+
+inline
+wxLongLong toLonglong(const FILETIME& fileTime)
+{
+ assert_static(sizeof(DWORD) == sizeof(long));
+ assert_static(sizeof(long) == 4);
+ assert(fileTime.dwHighDateTime <= static_cast<unsigned long>(std::numeric_limits<long>::max()));
+
+ return wxLongLong(fileTime.dwHighDateTime, fileTime.dwLowDateTime);
+}
+
+
+//struct FILETIME {DWORD dwLowDateTime; DWORD dwHighDateTime;};
+const FILETIME FAT_MIN_TIME = {13374976, 27846544}; //1980 \ both are valid max/min FAT dates for 2 second precision
+const FILETIME FAT_MAX_TIME = {14487552, 37251238}; //2107 /
+
+//http://en.wikipedia.org/wiki/File_Allocation_Table
+const size_t PRECISION_WRITE_TIME = 20000000; //number of 100 ns per step -> 2 s
+const size_t PRECISION_CREATE_TIME = 100000; // -> 1/100 s
+
+/*
+Number of bits of information in create time: ln_2((FAT_MAX_TIME - FAT_MIN_TIME) / PRECISION_CREATE_TIME) = 38.55534023
+Number of bits of information in write time: 30.91148404
+*/
+//total size available to store data:
+const size_t CREATE_TIME_INFO_BITS = 38;
+// I. indicator that offset in II) is present
+const size_t INDICATOR_EXISTING_BITS = 1;
+// II. local<->UTC time offset
+const size_t UTC_LOCAL_OFFSET_BITS = 7;
+// III. indicator that offset in II) corresponds to current local write time (this could be a hash of the write time)
+const size_t WRITE_TIME_HASH_BITS = CREATE_TIME_INFO_BITS - INDICATOR_EXISTING_BITS - UTC_LOCAL_OFFSET_BITS;
+
+
+template <size_t precision>
+FILETIME encodeRawInformation(wxULongLong rawInfo)
+{
+ rawInfo *= precision;
+ rawInfo += toULonglong(FAT_MIN_TIME);
+
+ assert(rawInfo <= toULonglong(FAT_MAX_TIME));
+ return toFiletime(rawInfo);
+}
+
+
+template <size_t precision>
+wxULongLong extractRawInformation(const FILETIME& createTime)
+{
+ assert(cmpFileTime(FAT_MIN_TIME, createTime) <= 0);
+ assert(cmpFileTime(createTime, FAT_MAX_TIME) <= 0);
+
+ //FAT create time ranges from 1980 - 2107 (2^7 years) with 1/100 seconds precision
+ wxULongLong rawInfo = toULonglong(createTime);
+ assert_static(sizeof(DWORD) == sizeof(long));
+ assert_static(sizeof(long) == 4);
+
+ rawInfo -= toULonglong(FAT_MIN_TIME);
+ rawInfo /= precision; //reduce precision (FILETIME has unit 10^-7 s)
+
+ assert(cmpFileTime(encodeRawInformation<precision>(rawInfo), createTime) == 0); //must be reversible
+ return rawInfo;
+}
+
+
+//convert write time to it's minimal representation (no restriction to FAT range "1980 - 2107")
+wxULongLong extractRawWriteTime(const FILETIME& writeTime)
+{
+ wxULongLong rawInfo = toULonglong(writeTime);
+ assert(rawInfo % PRECISION_WRITE_TIME == 0);
+ rawInfo /= PRECISION_WRITE_TIME; //reduce precision (FILETIME has unit 10^-7 s)
+ return rawInfo;
+}
+
+
+//files with different resolution than 2 seconds are rounded up when written to FAT
+FILETIME roundToFatWriteTime(const FILETIME& writeTime)
+{
+ wxULongLong rawData = toULonglong(writeTime);
+
+ if (rawData % PRECISION_WRITE_TIME != 0)
+ rawData += PRECISION_WRITE_TIME;
+
+ rawData /= PRECISION_WRITE_TIME;
+ rawData *= PRECISION_WRITE_TIME;
+ return toFiletime(rawData);
+}
+
+
+//get 7-bit value representing time shift in number of quarter-hours
+std::bitset<UTC_LOCAL_OFFSET_BITS> getUtcLocalShift()
+{
+ FILETIME utcTime = FAT_MIN_TIME;
+ utcTime.dwHighDateTime += 5000000; //some arbitrary valid time
+
+ const FILETIME localTime = utcToLocal(utcTime);
+
+ const int timeShiftSec = ((toLonglong(localTime) - toLonglong(utcTime)) / 10000000).ToLong(); //time shift in seconds
+
+ const int timeShiftQuarter = timeShiftSec / (60 * 15); //time shift in quarter-hours
+
+ const int absValue = common::abs(timeShiftQuarter); //MSVC C++0x bug: std::bitset<>(unsigned long) is ambiguous
+
+ 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 wxString errorMessage = wxString(_("Conversion error:")) + wxT(" Unexpected UTC <-> local time shift: ") +
+ wxT("(") + wxLongLong(timeShiftSec).ToString() + wxT(") ") + wxT("\n\n") + ffs3::getLastErrorFormatted();
+ throw std::runtime_error(std::string((errorMessage).ToUTF8()));
+ }
+
+ std::bitset<UTC_LOCAL_OFFSET_BITS> output(absValue);
+ output[UTC_LOCAL_OFFSET_BITS - 1] = timeShiftQuarter < 0; //sign bit
+ return output;
+}
+
+
+//get time-zone shift in seconds
+inline
+int convertUtcLocalShift(std::bitset<UTC_LOCAL_OFFSET_BITS> rawShift)
+{
+ const bool hasSign = rawShift[UTC_LOCAL_OFFSET_BITS - 1];
+ rawShift[UTC_LOCAL_OFFSET_BITS - 1] = false;
+
+ assert_static(UTC_LOCAL_OFFSET_BITS <= sizeof(unsigned long) * 8);
+ return hasSign ?
+ rawShift.to_ulong() * 15 * 60 * -1 :
+ rawShift.to_ulong() * 15 * 60;
+}
+}
+
+
+bool dst::fatHasUtcEncoded(const RawTime& rawTime) //"createTimeRaw" as retrieved by ::FindFirstFile() and ::GetFileAttributesEx(); throw (std::runtime_error)
+{
+ if ( cmpFileTime(rawTime.createTimeRaw, FAT_MIN_TIME) < 0 ||
+ cmpFileTime(FAT_MAX_TIME, rawTime.createTimeRaw) < 0)
+ {
+ assert(false); //shouldn't be possible according to FAT specification
+ return false;
+ }
+
+ const wxULongLong rawInfo = extractRawInformation<PRECISION_CREATE_TIME>(utcToLocal(rawTime.createTimeRaw));
+
+ assert_static(WRITE_TIME_HASH_BITS == 30);
+ return (extractRawWriteTime(utcToLocal(rawTime.writeTimeRaw)) & 0x3FFFFFFF) == (rawInfo & 0x3FFFFFFF) && //ensure write time wasn't changed externally
+ rawInfo >> (CREATE_TIME_INFO_BITS - INDICATOR_EXISTING_BITS) == 1; //extended data available
+}
+
+
+dst::RawTime dst::fatEncodeUtcTime(const FILETIME& writeTimeRealUtc) //throw (std::runtime_error)
+{
+ const FILETIME fatWriteTimeUtc = roundToFatWriteTime(writeTimeRealUtc); //writeTimeRealUtc may have incompatible precision (NTFS)
+
+ //create time lets us store 40 bit of information
+
+ //indicator that utc time is encoded -> hopefully results in a date long way in the future; but even if this bit is accidentally set, we still have the hash!
+ wxULongLong data = 1;
+
+ const std::bitset<UTC_LOCAL_OFFSET_BITS> utcShift = getUtcLocalShift();
+ data <<= UTC_LOCAL_OFFSET_BITS;
+ data |= utcShift.to_ulong();
+
+ data <<= WRITE_TIME_HASH_BITS;
+ data |= extractRawWriteTime(utcToLocal(fatWriteTimeUtc)) & 0x3FFFFFFF; //trim to last 30 bit of information
+ assert_static(WRITE_TIME_HASH_BITS == 30);
+
+ const FILETIME encodedData = localToUtc(encodeRawInformation<PRECISION_CREATE_TIME>(data)); //localToUtc: make sure data is physically saved as FAT local time
+ assert(cmpFileTime(FAT_MIN_TIME, encodedData) <= 0);
+ assert(cmpFileTime(encodedData, FAT_MAX_TIME) <= 0);
+
+ return RawTime(encodedData, fatWriteTimeUtc); //keep compatible with other applications, at least until DST shift actually occurs
+}
+
+
+FILETIME dst::fatDecodeUtcTime(const RawTime& rawTime) //return real UTC time; throw (std::runtime_error)
+{
+ if (!fatHasUtcEncoded(rawTime))
+ return rawTime.writeTimeRaw;
+
+ const wxULongLong rawInfo = extractRawInformation<PRECISION_CREATE_TIME>(utcToLocal(rawTime.createTimeRaw));
+
+ const std::bitset<UTC_LOCAL_OFFSET_BITS> rawShift(static_cast<int>(((rawInfo >> WRITE_TIME_HASH_BITS) & 0x7F).ToULong())); //static_cast<int>: a shame MSC...
+ assert_static(UTC_LOCAL_OFFSET_BITS == 7);
+
+ const int timeShiftSec = convertUtcLocalShift(rawShift);
+ const FILETIME writeTimeLocal = utcToLocal(rawTime.writeTimeRaw);
+
+ const wxLongLong realUTC = toLonglong(writeTimeLocal) - wxLongLong(timeShiftSec) * 10000000;
+ return toFiletime(realUTC);
+}
diff --git a/shared/dst_hack.h b/shared/dst_hack.h
new file mode 100644
index 00000000..550098a2
--- /dev/null
+++ b/shared/dst_hack.h
@@ -0,0 +1,38 @@
+#ifndef DST_HACK_H_INCLUDED
+#define DST_HACK_H_INCLUDED
+
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include "zstring.h"
+#include <stdexcept>
+
+
+namespace dst
+{
+/*
+Solve DST +-1h and time zone shift issues on FAT drives
+-------------------------------------------------------
+- (local) last write time is not touched!
+- all additional metadata is encoded in local create time:
+ I. indicator that offset in II) is present
+ II. local<->UTC time offset
+ III. indicator that offset in II) corresponds to current local write time (a hash of local last write time)
+*/
+
+bool isFatDrive(const Zstring& fileName); //throw ()
+
+//all subsequent functions may throw the std::runtime_error exception!
+
+struct RawTime //time as retrieved by ::FindFirstFile() and ::GetFileAttributesEx()
+{
+ RawTime(const FILETIME& create, const FILETIME& lastWrite) : createTimeRaw(create), writeTimeRaw(lastWrite) {}
+ FILETIME createTimeRaw;
+ FILETIME writeTimeRaw;
+};
+//save UTC time resistant against DST/time zone shifts
+bool fatHasUtcEncoded(const RawTime& rawTime); //as retrieved by ::FindFirstFile() and ::GetFileAttributesEx(); throw (std::runtime_error)
+
+RawTime fatEncodeUtcTime(const FILETIME& writeTimeRealUtc); //throw (std::runtime_error)
+FILETIME fatDecodeUtcTime(const RawTime& rawTime); //return last write time in real UTC; throw (std::runtime_error)
+}
+
+#endif // DST_HACK_H_INCLUDED
diff --git a/shared/file_handling.cpp b/shared/file_handling.cpp
index 3f56abe4..5c6ce856 100644
--- a/shared/file_handling.cpp
+++ b/shared/file_handling.cpp
@@ -27,6 +27,7 @@
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include "long_path_prefix.h"
#include <Aclapi.h>
+#include "dst_hack.h"
#elif defined FFS_LINUX
#include <sys/stat.h>
@@ -45,7 +46,7 @@ namespace
#ifdef FFS_WIN
Zstring resolveRelativePath(const Zstring& relativeName, DWORD proposedBufferSize = 1000)
{
- boost::scoped_array<DefaultChar> fullPath(new DefaultChar[proposedBufferSize]);
+ boost::scoped_array<Zchar> fullPath(new Zchar[proposedBufferSize]);
const DWORD rv = ::GetFullPathName(
relativeName.c_str(), //__in LPCTSTR lpFileName,
proposedBufferSize, //__in DWORD nBufferLength,
@@ -125,7 +126,6 @@ bool replaceMacro(wxString& macro) //macro without %-characters, return true if
macro.EndsWith(wxT("\"")) &&
macro.length() >= 2)
macro = wxString(macro.c_str() + 1, macro.length() - 2);
-
return true;
}
@@ -153,7 +153,7 @@ void expandMacros(wxString& text)
}
else
{
- rest = wxString() + SEPARATOR + rest;
+ rest = SEPARATOR + rest;
expandMacros(rest);
text = prefix + SEPARATOR + potentialMacro + rest;
}
@@ -166,17 +166,17 @@ void expandMacros(wxString& text)
Zstring ffs3::getFormattedDirectoryName(const Zstring& dirname)
{
//Formatting is needed since functions expect the directory to end with '\' to be able to split the relative names.
- //note: don't do directory formatting with wxFileName, as it doesn't respect //?/ - prefix!
+ //note: don't combine directory formatting with wxFileName, as it doesn't respect //?/ - prefix!
wxString dirnameTmp = zToWx(dirname);
- dirnameTmp.Trim(true); //remove whitespace characters from right
- dirnameTmp.Trim(false); //remove whitespace characters from left
+ expandMacros(dirnameTmp);
- if (dirnameTmp.empty()) //an empty string will later be returned as "\"; this is not desired
- return Zstring();
+ Zstring output = wxToZ(dirnameTmp);
- //replace macros
- expandMacros(dirnameTmp);
+ output.Trim();
+
+ if (output.empty()) //an empty string will later be returned as "\"; this is not desired
+ return Zstring();
/*
resolve relative names; required by:
@@ -188,12 +188,12 @@ Zstring ffs3::getFormattedDirectoryName(const Zstring& dirname)
WINDOWS/LINUX:
- detection of dependent directories, e.g. "\" and "C:\test"
*/
- dirnameTmp = zToWx(resolveRelativePath(wxToZ(dirnameTmp)));
+ output = resolveRelativePath(output);
- if (!dirnameTmp.EndsWith(zToWx(common::FILE_NAME_SEPARATOR)))
- dirnameTmp += zToWx(common::FILE_NAME_SEPARATOR);
+ if (!output.EndsWith(common::FILE_NAME_SEPARATOR))
+ output += common::FILE_NAME_SEPARATOR;
- return wxToZ(dirnameTmp);
+ return output;
}
@@ -546,17 +546,17 @@ Zstring getFilenameFmt(const Zstring& filename, Function fun) //throw(); returns
Zstring createTemp8Dot3Name(const Zstring& fileName) //find a unique 8.3 short name
{
- const Zstring pathPrefix = fileName.Find(common::FILE_NAME_SEPARATOR) != Zstring::npos ?
+ const Zstring pathPrefix = fileName.find(common::FILE_NAME_SEPARATOR) != Zstring::npos ?
(fileName.BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR) : Zstring();
- Zstring extension = fileName.AfterLast(common::FILE_NAME_SEPARATOR).AfterLast(DefaultChar('.')); //extension needn't contain reasonable data
+ Zstring extension = fileName.AfterLast(common::FILE_NAME_SEPARATOR).AfterLast(Zchar('.')); //extension needn't contain reasonable data
if (extension.empty())
- extension = DefaultStr("FFS");
+ extension = Zstr("FFS");
extension.Truncate(3);
for (int index = 0; index < 100000000; ++index) //filename must be representable by <= 8 characters
{
- const Zstring output = pathPrefix + numberToZstring(index) + DefaultChar('.') + extension;
+ const Zstring output = pathPrefix + Zstring::fromNumber(index) + Zchar('.') + extension;
if (!ffs3::somethingExists(output)) //ensure uniqueness
return output;
}
@@ -570,7 +570,7 @@ bool fix8Dot3NameClash(const Zstring& oldName, const Zstring& newName) //throw
{
using namespace ffs3;
- if (newName.Find(common::FILE_NAME_SEPARATOR) == Zstring::npos)
+ if (newName.find(common::FILE_NAME_SEPARATOR) == Zstring::npos)
return false;
if (ffs3::somethingExists(newName)) //name OR directory!
@@ -581,8 +581,8 @@ bool fix8Dot3NameClash(const Zstring& oldName, const Zstring& newName) //throw
if ( !fileNameShort.empty() &&
!fileNameLong.empty() &&
- cmpFileName(fileNameOrig, fileNameShort) == 0 &&
- cmpFileName(fileNameShort, fileNameLong) != 0)
+ EqualFilename()(fileNameOrig, fileNameShort) &&
+ !EqualFilename()(fileNameShort, fileNameLong))
{
//we detected an event where newName is in shortname format (although it is intended to be a long name) and
//writing target file failed because another unrelated file happens to have the same short name
@@ -634,11 +634,11 @@ using ffs3::MoveFileCallback;
class CopyCallbackImpl : public ffs3::CopyFileCallback //callback functionality
{
public:
- CopyCallbackImpl(MoveFileCallback* callback) : moveCallback(callback) {}
+ CopyCallbackImpl(const Zstring& sourceFile, MoveFileCallback& callback) : sourceFile_(sourceFile), moveCallback(callback) {}
virtual Response updateCopyStatus(const wxULongLong& totalBytesTransferred)
{
- switch (moveCallback->requestUiRefresh())
+ switch (moveCallback.requestUiRefresh(sourceFile_))
{
case MoveFileCallback::CONTINUE:
return CopyFileCallback::CONTINUE;
@@ -650,17 +650,27 @@ public:
}
private:
- MoveFileCallback* moveCallback;
+ const Zstring sourceFile_;
+ MoveFileCallback& moveCallback;
};
void ffs3::moveFile(const Zstring& sourceFile, const Zstring& targetFile, MoveFileCallback* callback) //throw (FileError);
{
+ //call back once per file (moveFile() is called by moveDirectory())
+ if (callback)
+ switch (callback->requestUiRefresh(sourceFile))
+ {
+ case MoveFileCallback::CONTINUE:
+ break;
+ case MoveFileCallback::CANCEL: //a user aborted operation IS an error condition!
+ throw FileError(wxString(_("Error moving file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\" ->\n\"") + zToWx(targetFile) + wxT("\"") +
+ wxT("\n\n") + _("Operation aborted!"));
+ }
+
if (somethingExists(targetFile)) //test file existence: e.g. Linux might silently overwrite existing symlinks
- {
- const wxString errorMessage = wxString(_("Error moving file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\" ->\n\"") + zToWx(targetFile) + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + _("Target file already existing!"));
- }
+ throw FileError(wxString(_("Error moving file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\" ->\n\"") + zToWx(targetFile) + wxT("\"") +
+ wxT("\n\n") + _("Target file already existing!"));
//moving of symbolic links should work correctly:
@@ -675,7 +685,7 @@ void ffs3::moveFile(const Zstring& sourceFile, const Zstring& targetFile, MoveFi
//file is on a different volume: let's copy it
- std::auto_ptr<CopyCallbackImpl> copyCallback(callback != NULL ? new CopyCallbackImpl(callback) : NULL);
+ std::auto_ptr<CopyCallbackImpl> copyCallback(callback != NULL ? new CopyCallbackImpl(sourceFile, *callback) : NULL);
copyFile(sourceFile,
targetFile,
@@ -691,7 +701,8 @@ void ffs3::moveFile(const Zstring& sourceFile, const Zstring& targetFile, MoveFi
removeFile(sourceFile);
}
-
+namespace
+{
class TraverseOneLevel : public ffs3::TraverseCallback
{
public:
@@ -701,11 +712,11 @@ public:
m_files(filesShort),
m_dirs(dirsShort) {}
- virtual void onFile(const DefaultChar* shortName, const Zstring& fullName, const FileInfo& details)
+ virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details)
{
m_files.push_back(std::make_pair(Zstring(shortName), fullName));
}
- virtual void onSymlink(const DefaultChar* shortName, const Zstring& fullName, const SymlinkInfo& details)
+ virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details)
{
if (details.dirLink)
m_dirs.push_back(std::make_pair(Zstring(shortName), fullName));
@@ -713,7 +724,7 @@ public:
m_files.push_back(std::make_pair(Zstring(shortName), fullName));
}
- virtual ReturnValDir onDir(const DefaultChar* shortName, const Zstring& fullName)
+ virtual ReturnValDir onDir(const Zchar* shortName, const Zstring& fullName)
{
m_dirs.push_back(std::make_pair(Zstring(shortName), fullName));
return Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //DON'T traverse into subdirs; moveDirectory works recursively!
@@ -729,25 +740,63 @@ private:
};
+struct RemoveCallbackImpl : public ffs3::RemoveDirCallback
+{
+ RemoveCallbackImpl(const Zstring& sourceDir,
+ const Zstring& targetDir,
+ MoveFileCallback& moveCallback) :
+ sourceDir_(sourceDir),
+ targetDir_(targetDir),
+ moveCallback_(moveCallback) {}
+
+ virtual void requestUiRefresh(const Zstring& currentObject)
+ {
+ switch (moveCallback_.requestUiRefresh(sourceDir_))
+ {
+ case MoveFileCallback::CONTINUE:
+ break;
+ case MoveFileCallback::CANCEL: //a user aborted operation IS an error condition!
+ throw ffs3::FileError(wxString(_("Error moving directory:")) + wxT("\n\"") + ffs3::zToWx(sourceDir_) + wxT("\" ->\n\"") +
+ ffs3::zToWx(targetDir_) + wxT("\"") + wxT("\n\n") + _("Operation aborted!"));
+ }
+ }
+
+private:
+ const Zstring sourceDir_;
+ const Zstring targetDir_;
+ MoveFileCallback& moveCallback_;
+};
+}
+
+
void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExistingDirs, MoveFileCallback* callback) //throw (FileError);
{
using namespace ffs3;
+ //call back once per folder
+ if (callback)
+ switch (callback->requestUiRefresh(sourceDir))
+ {
+ case MoveFileCallback::CONTINUE:
+ break;
+ case MoveFileCallback::CANCEL: //a user aborted operation IS an error condition!
+ throw FileError(wxString(_("Error moving directory:")) + wxT("\n\"") + zToWx(sourceDir) + wxT("\" ->\n\"") +
+ zToWx(targetDir) + wxT("\"") + wxT("\n\n") + _("Operation aborted!"));
+ }
+
//handle symbolic links
if (symlinkExists(sourceDir))
{
createDirectory(targetDir, sourceDir, true, false); //copy symbolic link, don't copy permissions
- removeDirectory(sourceDir); //if target is already another symlink or directory, sourceDir-symlink is silently deleted
+ removeDirectory(sourceDir, NULL); //if target is already another symlink or directory, sourceDir-symlink is silently deleted
return;
}
if (somethingExists(targetDir))
{
if (!ignoreExistingDirs) //directory or symlink exists (or even a file... this error will be caught later)
- {
- const wxString errorMessage = wxString(_("Error moving directory:")) + wxT("\n\"") + zToWx(sourceDir) + wxT("\" ->\n\"") + zToWx(targetDir) + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + _("Target directory already existing!"));
- }
+ throw FileError(wxString(_("Error moving directory:")) + wxT("\n\"") + zToWx(sourceDir) + wxT("\" ->\n\"") + zToWx(targetDir) + wxT("\"") +
+ wxT("\n\n") + _("Target directory already existing!"));
}
else
{
@@ -764,25 +813,13 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool
createDirectory(targetDir, sourceDir, false, false); //throw (FileError); don't copy permissions
}
- //call back once per folder
- if (callback)
- switch (callback->requestUiRefresh())
- {
- case MoveFileCallback::CONTINUE:
- break;
- case MoveFileCallback::CANCEL:
- //an user aborted operation IS an error condition!
- throw FileError(wxString(_("Error moving directory:")) + wxT("\n\"") + zToWx(sourceDir) + wxT("\" ->\n\"") +
- zToWx(targetDir) + wxT("\"") + wxT("\n\n") + _("Operation aborted!"));
- }
-
//move files/folders recursively
TraverseOneLevel::NamePair fileList; //list of names: 1. short 2.long
TraverseOneLevel::NamePair dirList; //
//traverse source directory one level
TraverseOneLevel traverseCallback(fileList, dirList);
- traverseFolder(sourceDir, false, &traverseCallback); //traverse one level, don't follow symlinks
+ traverseFolder(sourceDir, false, traverseCallback); //traverse one level, don't follow symlinks
const Zstring targetDirFormatted = targetDir.EndsWith(common::FILE_NAME_SEPARATOR) ? //ends with path separator
targetDir :
@@ -799,7 +836,8 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool
//attention: if move-operation was cancelled an exception is thrown => sourceDir is not deleted, as we wish!
//delete source
- removeDirectory(sourceDir); //throw (FileError);
+ std::auto_ptr<RemoveCallbackImpl> removeCallback(callback != NULL ? new RemoveCallbackImpl(sourceDir, targetDir, *callback) : NULL);
+ removeDirectory(sourceDir, removeCallback.get()); //throw (FileError);
}
@@ -831,18 +869,18 @@ public:
m_files(files),
m_dirs(dirs) {}
- virtual void onFile(const DefaultChar* shortName, const Zstring& fullName, const FileInfo& details)
+ virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details)
{
m_files.push_back(fullName);
}
- virtual void onSymlink(const DefaultChar* shortName, const Zstring& fullName, const SymlinkInfo& details)
+ virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details)
{
if (details.dirLink)
m_dirs.push_back(fullName);
else
m_files.push_back(fullName);
}
- virtual ReturnValDir onDir(const DefaultChar* shortName, const Zstring& fullName)
+ virtual ReturnValDir onDir(const Zchar* shortName, const Zstring& fullName)
{
m_dirs.push_back(fullName);
return Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //DON'T traverse into subdirs; removeDirectory works recursively!
@@ -858,7 +896,7 @@ private:
};
-void ffs3::removeDirectory(const Zstring& directory)
+void ffs3::removeDirectory(const Zstring& directory, RemoveDirCallback* callback)
{
//no error situation if directory is not existing! manual deletion relies on it!
if (!somethingExists(directory))
@@ -898,13 +936,23 @@ void ffs3::removeDirectory(const Zstring& directory)
//get all files and directories from current directory (WITHOUT subdirectories!)
FilesDirsOnlyTraverser traverser(fileList, dirList);
- ffs3::traverseFolder(directory, false, &traverser); //don't follow symlinks
+ ffs3::traverseFolder(directory, false, traverser); //don't follow symlinks
+
//delete files
- std::for_each(fileList.begin(), fileList.end(), removeFile);
+ for (std::vector<Zstring>::const_iterator i = fileList.begin(); i != fileList.end(); ++i)
+ {
+ if (callback) callback->requestUiRefresh(*i); //call once per file
+ removeFile(*i);
+ }
//delete directories recursively
- std::for_each(dirList.begin(), dirList.end(), removeDirectory); //call recursively to correctly handle symbolic links
+ for (std::vector<Zstring>::const_iterator i = dirList.begin(); i != dirList.end(); ++i)
+ {
+ if (callback) callback->requestUiRefresh(*i); //and once per folder
+ removeDirectory(*i, callback); //call recursively to correctly handle symbolic links
+ }
+
//parent directory is deleted last
#ifdef FFS_WIN
@@ -919,16 +967,15 @@ void ffs3::removeDirectory(const Zstring& directory)
}
-//optionally: copy directory last change date, DO NOTHING if something fails
void ffs3::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, bool deRefSymlinks) //throw (FileError)
{
#ifdef FFS_WIN
- FILETIME creationTime = {0};
- FILETIME lastAccessTime = {0};
- FILETIME lastWriteTime = {0};
+ FILETIME creationTime = {};
+ FILETIME lastAccessTime = {};
+ FILETIME lastWriteTime = {};
{
- WIN32_FILE_ATTRIBUTE_DATA sourceAttr;
+ WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {};
if (!::GetFileAttributesEx(applyLongPathPrefix(sourceObj).c_str(), //__in LPCTSTR lpFileName,
GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId,
&sourceAttr)) //__out LPVOID lpFileInformation
@@ -969,8 +1016,31 @@ void ffs3::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, boo
lastAccessTime = sourceAttr.ftLastAccessTime;
lastWriteTime = sourceAttr.ftLastWriteTime;
}
+
+//####################################### DST hack ###########################################
+ if ((sourceAttr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) //dst hack not (yet) required for directories (symlinks implicitly checked by isFatDrive())
+ {
+ if (dst::isFatDrive(sourceObj)) //throw()
+ {
+ const dst::RawTime rawTime(creationTime, lastWriteTime);
+ if (dst::fatHasUtcEncoded(rawTime)) //throw (std::runtime_error)
+ {
+ lastWriteTime = dst::fatDecodeUtcTime(rawTime); //return last write time in real UTC, throw (std::runtime_error)
+ ::GetSystemTimeAsFileTime(&creationTime); //real creation time information is not available...
+ }
+ }
+
+ if (dst::isFatDrive(targetObj)) //throw()
+ {
+ const dst::RawTime encodedTime = dst::fatEncodeUtcTime(lastWriteTime); //throw (std::runtime_error)
+ creationTime = encodedTime.createTimeRaw;
+ lastWriteTime = encodedTime.writeTimeRaw;
+ }
+ }
+//####################################### DST hack ###########################################
}
+
HANDLE hTarget = ::CreateFile(applyLongPathPrefix(targetObj).c_str(),
FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
@@ -984,18 +1054,31 @@ void ffs3::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, boo
wxString errorMessage = wxString(_("Error changing modification time:")) + wxT("\n\"") + zToWx(targetObj) + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
}
-
boost::shared_ptr<void> dummy(hTarget, ::CloseHandle);
if (!::SetFileTime(hTarget,
&creationTime,
&lastAccessTime,
- &lastWriteTime)) //return value not evalutated!
+ &lastWriteTime))
{
wxString errorMessage = wxString(_("Error changing modification time:")) + wxT("\n\"") + zToWx(targetObj) + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
}
+#ifndef NDEBUG //dst hack: verify data written
+ if (dst::isFatDrive(targetObj)) //throw()
+ {
+ WIN32_FILE_ATTRIBUTE_DATA debugeAttr = {};
+ assert(::GetFileAttributesEx(applyLongPathPrefix(targetObj).c_str(), //__in LPCTSTR lpFileName,
+ GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId,
+ &debugeAttr)); //__out LPVOID lpFileInformation
+
+ assert(::CompareFileTime(&debugeAttr.ftCreationTime, &creationTime) == 0);
+ assert(::CompareFileTime(&debugeAttr.ftLastWriteTime, &lastWriteTime) == 0);
+ }
+#endif
+
+
#elif defined FFS_LINUX
if (deRefSymlinks)
{
@@ -1071,7 +1154,7 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa
DWORD cchFilePath,
DWORD dwFlags);
static const GetFinalPathNameByHandleWFunc getFinalPathNameByHandle =
- util::loadDllFunction<GetFinalPathNameByHandleWFunc>(L"kernel32.dll", "GetFinalPathNameByHandleW");
+ util::getDllFun<GetFinalPathNameByHandleWFunc>(L"kernel32.dll", "GetFinalPathNameByHandleW");
if (getFinalPathNameByHandle == NULL)
throw FileError(wxString(_("Error loading library function:")) + wxT("\n\"") + wxT("GetFinalPathNameByHandleW") + wxT("\""));
@@ -1406,11 +1489,11 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
struct TryCleanUp
{
- static void tryDeleteDir(const Zstring& linkname) //throw ()
+ static void tryDeleteDir(const Zstring& dirname) //throw ()
{
try
{
- removeDirectory(linkname);
+ removeDirectory(dirname, NULL);
}
catch (...) {}
}
@@ -1483,6 +1566,7 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
guardNewDir.Dismiss(); //target has been created successfully!
}
+
#elif defined FFS_LINUX
//symbolic link handling
if ( copyDirectorySymLinks &&
@@ -1537,13 +1621,9 @@ Zstring createTempName(const Zstring& filename)
{
Zstring output = filename + ffs3::TEMP_FILE_ENDING;
-#ifndef _MSC_VER
-#warning TEMP_FILE_ENDING -> harmonize with other "endings" remove trailing dot
-#endif
-
//ensure uniqueness
for (int i = 1; ffs3::somethingExists(output); ++i)
- output = filename + DefaultChar('_') + numberToZstring(i) + ffs3::TEMP_FILE_ENDING;
+ output = filename + Zchar('_') + Zstring::fromNumber(i) + ffs3::TEMP_FILE_ENDING;
return output;
}
@@ -1607,13 +1687,12 @@ DWORD CALLBACK copyCallbackInternal(
bool supportForSymbolicLinks()
{
- OSVERSIONINFO osvi;
- ::ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ OSVERSIONINFO osvi = {};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
//symbolic links are supported starting with Vista
- if (GetVersionEx(&osvi))
- return osvi.dwMajorVersion > 5; //XP has majorVersion == 5, minorVersion == 1, Vista majorVersion == 6
+ if (::GetVersionEx(&osvi))
+ return osvi.dwMajorVersion > 5; //XP has majorVersion == 5, minorVersion == 1; Vista majorVersion == 6, dwMinorVersion == 0
//overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
return false;
}
@@ -1697,7 +1776,7 @@ void ffs3::copyFile(const Zstring& sourceFile,
{
//shadowFilename already contains prefix: E.g. "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Program Files\FFS\sample.dat"
- Zstring shadowFilename;
+ Zstring shadowFilename;
try
{
shadowFilename = shadowCopyHandler->makeShadowCopy(sourceFile); //throw (FileError)
diff --git a/shared/file_handling.h b/shared/file_handling.h
index 36bf976f..af20ccf8 100644
--- a/shared/file_handling.h
+++ b/shared/file_handling.h
@@ -18,6 +18,11 @@
namespace ffs3
{
+struct RemoveDirCallback;
+struct MoveFileCallback;
+struct CopyFileCallback;
+
+
Zstring getFormattedDirectoryName(const Zstring& dirname);
bool fileExists( const Zstring& filename); //throw() replaces wxFileExists()!
@@ -47,21 +52,9 @@ wxULongLong getFilesize(const Zstring& filename); //throw (FileError)
//file handling
void removeFile(const Zstring& filename); //throw (FileError)
-void removeDirectory(const Zstring& directory); //throw (FileError)
+void removeDirectory(const Zstring& directory, RemoveDirCallback* callback = NULL); //throw (FileError)
-struct MoveFileCallback //callback functionality
-{
- virtual ~MoveFileCallback() {}
-
- enum Response
- {
- CONTINUE,
- CANCEL
- };
- virtual Response requestUiRefresh() = 0; //DON'T throw exceptions here, at least in Windows build!
-};
-
//rename file or directory: no copying!!!
void renameFile(const Zstring& oldName, const Zstring& newName); //throw (FileError);
@@ -76,17 +69,6 @@ void moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, bool igno
void createDirectory(const Zstring& directory, const Zstring& templateDir, bool copyDirectorySymLinks, bool copyFilePermissions); //throw (FileError);
void createDirectory(const Zstring& directory); //throw (FileError); -> function overload avoids default parameter ambiguity issues!
-struct CopyFileCallback //callback functionality
-{
- virtual ~CopyFileCallback() {}
-
- enum Response
- {
- CONTINUE,
- CANCEL
- };
- virtual Response updateCopyStatus(const wxULongLong& totalBytesTransferred) = 0; //DON'T throw exceptions here, at least in Windows build!
-};
void copyFile(const Zstring& sourceFile, //throw (FileError);
const Zstring& targetFile,
@@ -98,8 +80,45 @@ void copyFile(const Zstring& sourceFile, //throw (FileError);
CopyFileCallback* callback); //may be NULL
//Note: it MAY happen that copyFile() leaves temp files behind, e.g. temporary network drop.
// => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending:
-const Zstring TEMP_FILE_ENDING = DefaultStr(".ffs_tmp");
-}
+const Zstring TEMP_FILE_ENDING = Zstr(".ffs_tmp");
+
+
+
+
+//----------- callbacks ---------------
+struct RemoveDirCallback
+{
+ virtual ~RemoveDirCallback() {}
+ virtual void requestUiRefresh(const Zstring& currentObject) = 0;
+};
+
+
+struct MoveFileCallback //callback functionality
+{
+ virtual ~MoveFileCallback() {}
+
+ enum Response
+ {
+ CONTINUE,
+ CANCEL
+ };
+ virtual Response requestUiRefresh(const Zstring& currentObject) = 0; //DON'T throw exceptions here, at least in Windows build!
+};
+
+
+struct CopyFileCallback //callback functionality
+{
+ virtual ~CopyFileCallback() {}
+
+ enum Response
+ {
+ CONTINUE,
+ CANCEL
+ };
+ virtual Response updateCopyStatus(const wxULongLong& totalBytesTransferred) = 0; //DON'T throw exceptions here, at least in Windows build!
+};
+}
+
#endif //FILE_HANDLING_H_INCLUDED
diff --git a/shared/file_id.cpp b/shared/file_id.cpp
index f61108c0..0c8afa3d 100644
--- a/shared/file_id.cpp
+++ b/shared/file_id.cpp
@@ -13,7 +13,6 @@
#include <boost/shared_ptr.hpp>
#elif defined FFS_LINUX
-
#endif
diff --git a/shared/file_io.cpp b/shared/file_io.cpp
index 345234fd..95c14c49 100644
--- a/shared/file_io.cpp
+++ b/shared/file_io.cpp
@@ -60,7 +60,8 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError()
{
const DWORD lastError = ::GetLastError();
const wxString& errorMessage = wxString(_("Error opening file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") + wxT("\n\n") + ffs3::getLastErrorFormatted(lastError);
- if (lastError == ERROR_FILE_NOT_FOUND)
+ if (lastError == ERROR_FILE_NOT_FOUND ||
+ lastError == ERROR_PATH_NOT_FOUND)
throw ErrorNotExisting(errorMessage);
else
throw FileError(errorMessage);
diff --git a/shared/file_traverser.cpp b/shared/file_traverser.cpp
index 8e9d5f0d..5d3f75f4 100644
--- a/shared/file_traverser.cpp
+++ b/shared/file_traverser.cpp
@@ -11,11 +11,14 @@
#include "string_conv.h"
#include <boost/shared_ptr.hpp>
#include <boost/scoped_array.hpp>
+#include "assert_static.h"
+#include <limits>
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include "WinIoCtl.h"
#include "long_path_prefix.h"
+#include "dst_hack.h"
#elif defined FFS_LINUX
#include <sys/stat.h>
@@ -29,8 +32,8 @@
//{
//public:
// DisableWow64Redirection() :
-// wow64DisableWow64FsRedirection(util::loadDllFunction<Wow64DisableWow64FsRedirectionFunc>(L"kernel32.dll", "Wow64DisableWow64FsRedirection")),
-// wow64RevertWow64FsRedirection(util::loadDllFunction<Wow64RevertWow64FsRedirectionFunc>(L"kernel32.dll", "Wow64RevertWow64FsRedirection")),
+// wow64DisableWow64FsRedirection(util::getDllFun<Wow64DisableWow64FsRedirectionFunc>(L"kernel32.dll", "Wow64DisableWow64FsRedirection")),
+// wow64RevertWow64FsRedirection(util::getDllFun<Wow64RevertWow64FsRedirectionFunc>(L"kernel32.dll", "Wow64RevertWow64FsRedirection")),
// oldValue(NULL)
// {
// if ( wow64DisableWow64FsRedirection &&
@@ -153,9 +156,13 @@ inline
wxLongLong getWin32TimeInformation(const FILETIME& lastWriteTime)
{
//convert UTC FILETIME to ANSI C format (number of seconds since Jan. 1st 1970 UTC)
- wxLongLong writeTimeLong(wxInt32(lastWriteTime.dwHighDateTime), lastWriteTime.dwLowDateTime);
+ wxLongLong writeTimeLong(lastWriteTime.dwHighDateTime, lastWriteTime.dwLowDateTime);
writeTimeLong /= 10000000; //reduce precision to 1 second (FILETIME has unit 10^-7 s)
writeTimeLong -= wxLongLong(2, 3054539008UL); //timeshift between ansi C time and FILETIME in seconds == 11644473600s
+
+ assert(lastWriteTime.dwHighDateTime <= static_cast<unsigned long>(std::numeric_limits<long>::max()));
+ assert_static(sizeof(DWORD) == sizeof(long));
+ assert_static(sizeof(long) == 4);
return writeTimeLong;
}
@@ -198,224 +205,306 @@ bool setWin32FileInformationFromSymlink(const Zstring& linkName, ffs3::TraverseC
#endif
-template <bool followSymlinks>
-void traverseDirectory(const Zstring& directory, ffs3::TraverseCallback* sink, int level)
+class DirTraverser
{
- using namespace ffs3;
-
- if (level == 100) //catch endless recursion
+public:
+ DirTraverser(const Zstring& baseDirectory, bool followSymlinks, ffs3::TraverseCallback& sink, ffs3::DstHackCallback* dstCallback)
+#ifdef FFS_WIN
+ : isFatFileSystem(dst::isFatDrive(baseDirectory))
+#endif
{
- sink->onError(wxString(_("Endless loop when traversing directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\""));
- return;
- }
-
+ //format base directory name
#ifdef FFS_WIN
- //ensure directoryFormatted ends with backslash
- const Zstring directoryFormatted = directory.EndsWith(common::FILE_NAME_SEPARATOR) ?
- directory :
- directory + common::FILE_NAME_SEPARATOR;
+ const Zstring& directoryFormatted = baseDirectory;
- WIN32_FIND_DATA fileMetaData;
- HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryFormatted + DefaultChar('*')).c_str(), //__in LPCTSTR lpFileName
- &fileMetaData); //__out LPWIN32_FIND_DATA lpFindFileData
- //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH
-
- if (searchHandle == INVALID_HANDLE_VALUE)
- {
- const DWORD lastError = ::GetLastError();
- if (lastError == ERROR_FILE_NOT_FOUND)
- return;
+#elif defined FFS_LINUX
+ const Zstring directoryFormatted = //remove trailing slash
+ baseDirectory.size() > 1 && baseDirectory.EndsWith(common::FILE_NAME_SEPARATOR) ? //exception: allow '/'
+ baseDirectory.BeforeLast(common::FILE_NAME_SEPARATOR) :
+ baseDirectory;
+#endif
- //else: we have a problem... report it:
- const wxString errorMessage = wxString(_("Error traversing directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"") + wxT("\n\n") +
- ffs3::getLastErrorFormatted(lastError);
+ //traverse directories
+ if (followSymlinks)
+ traverse<true>(directoryFormatted, sink, 0);
+ else
+ traverse<false>(directoryFormatted, sink, 0);
- sink->onError(errorMessage);
- return;
+ //apply daylight saving time hack AFTER file traversing, to give separate feedback to user
+#ifdef FFS_WIN
+ if (isFatFileSystem && dstCallback)
+ applyDstHack(*dstCallback);
+#endif
}
- boost::shared_ptr<void> dummy(searchHandle, ::FindClose);
-
- do
+private:
+ template <bool followSymlinks>
+ void traverse(const Zstring& directory, ffs3::TraverseCallback& sink, int level)
{
- //don't return "." and ".."
- const wxChar* const shortName = fileMetaData.cFileName;
- if ( shortName[0] == wxChar('.') &&
- ((shortName[1] == wxChar('.') && shortName[2] == wxChar('\0')) ||
- shortName[1] == wxChar('\0')))
- continue;
+ using namespace ffs3;
- const Zstring fullName = directoryFormatted + shortName;
-
- const bool isSymbolicLink = (fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
-
- if (isSymbolicLink && !followSymlinks) //evaluate symlink directly
+ if (level == 100) //catch endless recursion
{
- TraverseCallback::SymlinkInfo details;
- details.lastWriteTimeRaw = getWin32TimeInformation(fileMetaData.ftLastWriteTime);
- details.targetPath = getSymlinkTarget(fullName); //throw(); returns empty string on error
- details.dirLink = (fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; //directory symlinks have this flag on Windows
- sink->onSymlink(shortName, fullName, details);
+ sink.onError(wxString(_("Endless loop when traversing directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\""));
+ return;
}
- else if (fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //a directory... or symlink that needs to be followed (for directory symlinks this flag is set too!)
+
+#ifdef FFS_WIN
+ //ensure directoryFormatted ends with backslash
+ const Zstring& directoryFormatted = directory.EndsWith(common::FILE_NAME_SEPARATOR) ?
+ directory :
+ directory + common::FILE_NAME_SEPARATOR;
+
+ WIN32_FIND_DATA fileMetaData;
+ HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryFormatted + Zchar('*')).c_str(), //__in LPCTSTR lpFileName
+ &fileMetaData); //__out LPWIN32_FIND_DATA lpFindFileData
+ //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH
+
+ if (searchHandle == INVALID_HANDLE_VALUE)
{
- const TraverseCallback::ReturnValDir rv = sink->onDir(shortName, fullName);
- switch (rv.returnCode)
- {
- case TraverseCallback::ReturnValDir::TRAVERSING_DIR_IGNORE:
- break;
+ const DWORD lastError = ::GetLastError();
+ if (lastError == ERROR_FILE_NOT_FOUND)
+ return;
- case TraverseCallback::ReturnValDir::TRAVERSING_DIR_CONTINUE:
- traverseDirectory<followSymlinks>(fullName, rv.subDirCb, level + 1);
- break;
- }
+ //else: we have a problem... report it:
+ const wxString errorMessage = wxString(_("Error traversing directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"") + wxT("\n\n") +
+ ffs3::getLastErrorFormatted(lastError);
+
+ sink.onError(errorMessage);
+ return;
}
- else //a file or symlink that is followed...
+
+ boost::shared_ptr<void> dummy(searchHandle, ::FindClose);
+
+ do
{
- TraverseCallback::FileInfo details;
+ //don't return "." and ".."
+ const Zchar* const shortName = fileMetaData.cFileName;
+ if ( shortName[0] == Zstr('.') &&
+ ((shortName[1] == Zstr('.') && shortName[2] == Zstr('\0')) ||
+ shortName[1] == Zstr('\0')))
+ continue;
+
+ const Zstring& fullName = directoryFormatted + shortName;
- if (isSymbolicLink) //dereference symlinks!
+ const bool isSymbolicLink = (fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+
+ if (isSymbolicLink && !followSymlinks) //evaluate symlink directly
+ {
+ TraverseCallback::SymlinkInfo details;
+ details.lastWriteTimeRaw = getWin32TimeInformation(fileMetaData.ftLastWriteTime);
+ details.targetPath = getSymlinkTarget(fullName); //throw(); returns empty string on error
+ details.dirLink = (fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; //directory symlinks have this flag on Windows
+ sink.onSymlink(shortName, fullName, details);
+ }
+ else if (fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //a directory... or symlink that needs to be followed (for directory symlinks this flag is set too!)
{
- if (!setWin32FileInformationFromSymlink(fullName, details))
+ const TraverseCallback::ReturnValDir rv = sink.onDir(shortName, fullName);
+ switch (rv.returnCode)
{
- //broken symlink...
- details.lastWriteTimeRaw = 0; //we are not interested in the modifiation time of the link
- details.fileSize = 0;
+ case TraverseCallback::ReturnValDir::TRAVERSING_DIR_IGNORE:
+ break;
+
+ case TraverseCallback::ReturnValDir::TRAVERSING_DIR_CONTINUE:
+ traverse<followSymlinks>(fullName, *rv.subDirCb, level + 1);
+ break;
}
}
- else
- setWin32FileInformation(fileMetaData.ftLastWriteTime, fileMetaData.nFileSizeHigh, fileMetaData.nFileSizeLow, details);
+ else //a file or symlink that is followed...
+ {
+ TraverseCallback::FileInfo details;
- sink->onFile(shortName, fullName, details);
- }
- }
- while (::FindNextFile(searchHandle, // handle to search
- &fileMetaData)); // pointer to structure for data on found file
+ if (isSymbolicLink) //dereference symlinks!
+ {
+ if (!setWin32FileInformationFromSymlink(fullName, details))
+ {
+ //broken symlink...
+ details.lastWriteTimeRaw = 0; //we are not interested in the modification time of the link
+ details.fileSize = 0;
+ }
+ }
+ else
+ {
+//####################################### DST hack ###########################################
+ if (isFatFileSystem)
+ {
+ const dst::RawTime rawTime(fileMetaData.ftCreationTime, fileMetaData.ftLastWriteTime);
+
+ if (dst::fatHasUtcEncoded(rawTime)) //throw (std::runtime_error)
+ fileMetaData.ftLastWriteTime = dst::fatDecodeUtcTime(rawTime); //return real UTC time; throw (std::runtime_error)
+ else
+ markForDstHack.push_back(std::make_pair(fullName, fileMetaData.ftLastWriteTime));
+ }
+//####################################### DST hack ###########################################
+ setWin32FileInformation(fileMetaData.ftLastWriteTime, fileMetaData.nFileSizeHigh, fileMetaData.nFileSizeLow, details);
+ }
- const DWORD lastError = ::GetLastError();
- if (lastError == ERROR_NO_MORE_FILES)
- return; //everything okay
+ sink.onFile(shortName, fullName, details);
+ }
+ }
+ while (::FindNextFile(searchHandle, // handle to search
+ &fileMetaData)); // pointer to structure for data on found file
- //else: we have a problem... report it:
- const wxString errorMessage = wxString(_("Error traversing directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"") ;
- sink->onError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted(lastError));
- return;
+ const DWORD lastError = ::GetLastError();
+ if (lastError == ERROR_NO_MORE_FILES)
+ return; //everything okay
-#elif defined FFS_LINUX
- DIR* dirObj = ::opendir(directory.c_str()); //directory must NOT end with path separator, except "/"
- if (dirObj == NULL)
- {
+ //else: we have a problem... report it:
const wxString errorMessage = wxString(_("Error traversing directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"") ;
- sink->onError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
+ sink.onError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted(lastError));
return;
- }
- boost::shared_ptr<DIR> dummy(dirObj, &::closedir); //never close NULL handles! -> crash
-
- while (true)
- {
- errno = 0; //set errno to 0 as unfortunately this isn't done when readdir() returns NULL because it can't find any files
- struct dirent* dirEntry = ::readdir(dirObj);
- if (dirEntry == NULL)
+#elif defined FFS_LINUX
+ DIR* dirObj = ::opendir(directory.c_str()); //directory must NOT end with path separator, except "/"
+ if (dirObj == NULL)
{
- if (errno == 0)
- return; //everything okay
-
- //else: we have a problem... report it:
const wxString errorMessage = wxString(_("Error traversing directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"") ;
- sink->onError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
+ sink.onError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
return;
}
- //don't return "." and ".."
- const DefaultChar* const shortName = dirEntry->d_name;
- if ( shortName[0] == wxChar('.') &&
- ((shortName[1] == wxChar('.') && shortName[2] == wxChar('\0')) ||
- shortName[1] == wxChar('\0')))
- continue;
+ boost::shared_ptr<DIR> dummy(dirObj, &::closedir); //never close NULL handles! -> crash
- const Zstring fullName = directory.EndsWith(common::FILE_NAME_SEPARATOR) ? //e.g. "/"
- directory + shortName :
- directory + common::FILE_NAME_SEPARATOR + shortName;
-
- struct stat fileInfo;
- if (::lstat(fullName.c_str(), &fileInfo) != 0) //lstat() does not resolve symlinks
+ while (true)
{
- const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(fullName) + wxT("\"");
- sink->onError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
- continue;
- }
+ errno = 0; //set errno to 0 as unfortunately this isn't done when readdir() returns NULL because it can't find any files
+ struct dirent* dirEntry = ::readdir(dirObj);
+ if (dirEntry == NULL)
+ {
+ if (errno == 0)
+ return; //everything okay
- const bool isSymbolicLink = S_ISLNK(fileInfo.st_mode);
+ //else: we have a problem... report it:
+ const wxString errorMessage = wxString(_("Error traversing directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"") ;
+ sink.onError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
+ return;
+ }
- if (isSymbolicLink)
- {
- if (followSymlinks) //on Linux Symlinks need to be followed to evaluate whether they point to a file or directory
+ //don't return "." and ".."
+ const Zchar* const shortName = dirEntry->d_name;
+ if ( shortName[0] == Zstr('.') &&
+ ((shortName[1] == Zstr('.') && shortName[2] == Zstr('\0')) ||
+ shortName[1] == Zstr('\0')))
+ continue;
+
+ const Zstring& fullName = directory.EndsWith(common::FILE_NAME_SEPARATOR) ? //e.g. "/"
+ directory + shortName :
+ directory + common::FILE_NAME_SEPARATOR + shortName;
+
+ struct stat fileInfo;
+ if (::lstat(fullName.c_str(), &fileInfo) != 0) //lstat() does not resolve symlinks
+ {
+ const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(fullName) + wxT("\"");
+ sink.onError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
+ continue;
+ }
+
+ const bool isSymbolicLink = S_ISLNK(fileInfo.st_mode);
+
+ if (isSymbolicLink)
{
- if (::stat(fullName.c_str(), &fileInfo) != 0) //stat() resolves symlinks
+ if (followSymlinks) //on Linux Symlinks need to be followed to evaluate whether they point to a file or directory
{
- //a broken symbolic link
- TraverseCallback::FileInfo details;
- details.lastWriteTimeRaw = 0; //we are not interested in the modifiation time of the link
- details.fileSize = 0;
- sink->onFile(shortName, fullName, details); //report broken symlink as file!
+ if (::stat(fullName.c_str(), &fileInfo) != 0) //stat() resolves symlinks
+ {
+ //a broken symbolic link
+ TraverseCallback::FileInfo details;
+ details.lastWriteTimeRaw = 0; //we are not interested in the modifiation time of the link
+ details.fileSize = 0;
+ sink.onFile(shortName, fullName, details); //report broken symlink as file!
+ continue;
+ }
+ }
+ else //evaluate symlink directly
+ {
+ TraverseCallback::SymlinkInfo details;
+ details.lastWriteTimeRaw = fileInfo.st_mtime; //UTC time(ANSI C format); unit: 1 second
+ details.targetPath = getSymlinkTarget(fullName); //throw(); returns empty string on error
+ details.dirLink = ::stat(fullName.c_str(), &fileInfo) == 0 && S_ISDIR(fileInfo.st_mode); //S_ISDIR and S_ISLNK are mutually exclusive on Linux => need to follow link
+ sink.onSymlink(shortName, fullName, details);
continue;
}
}
- else //evaluate symlink directly
+
+ //fileInfo contains dereferenced data in any case from here on
+
+ if (S_ISDIR(fileInfo.st_mode)) //a directory... cannot be a symlink on Linux in this case
{
- TraverseCallback::SymlinkInfo details;
+ 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;
+ }
+ }
+ else //a file... (or symlink; pathological!)
+ {
+ TraverseCallback::FileInfo details;
details.lastWriteTimeRaw = fileInfo.st_mtime; //UTC time(ANSI C format); unit: 1 second
- details.targetPath = getSymlinkTarget(fullName); //throw(); returns empty string on error
- details.dirLink = ::stat(fullName.c_str(), &fileInfo) == 0 && S_ISDIR(fileInfo.st_mode); //S_ISDIR and S_ISLNK are mutually exclusive on Linux => need to follow link
- sink->onSymlink(shortName, fullName, details);
- continue;
+ details.fileSize = fileInfo.st_size;
+
+ sink.onFile(shortName, fullName, details);
}
}
+#endif
+ }
- //fileInfo contains dereferenced data in any case from here on
- if (S_ISDIR(fileInfo.st_mode)) //a directory... cannot be a symlink on Linux in this case
+#ifdef FFS_WIN
+//####################################### DST hack ###########################################
+ void applyDstHack(ffs3::DstHackCallback& dstCallback)
+ {
+ for (FilenameTimeList::const_iterator i = markForDstHack.begin(); i != markForDstHack.end(); ++i)
{
- const TraverseCallback::ReturnValDir rv = sink->onDir(shortName, fullName);
- switch (rv.returnCode)
+ dstCallback.requestUiRefresh(i->first);
+
+ HANDLE hTarget = ::CreateFile(ffs3::applyLongPathPrefix(i->first).c_str(),
+ FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hTarget == INVALID_HANDLE_VALUE)
+ assert(false); //don't throw exceptions due to dst hack here
+ else
{
- case TraverseCallback::ReturnValDir::TRAVERSING_DIR_IGNORE:
- break;
+ boost::shared_ptr<void> dummy2(hTarget, ::CloseHandle);
- case TraverseCallback::ReturnValDir::TRAVERSING_DIR_CONTINUE:
- traverseDirectory<followSymlinks>(fullName, rv.subDirCb, level + 1);
- break;
- }
- }
- else //a file... (or symlink; pathological!)
- {
- TraverseCallback::FileInfo details;
- details.lastWriteTimeRaw = fileInfo.st_mtime; //UTC time(ANSI C format); unit: 1 second
- details.fileSize = fileInfo.st_size;
+ const dst::RawTime encodedTime = dst::fatEncodeUtcTime(i->second); //throw (std::runtime_error)
+
+ if (!::SetFileTime(hTarget,
+ &encodedTime.createTimeRaw,
+ NULL,
+ &encodedTime.writeTimeRaw))
+ assert(false); //don't throw exceptions due to dst hack here
+
+#ifndef NDEBUG //dst hack: verify data written; attention: this check may fail for "sync.ffs_lock"
+ WIN32_FILE_ATTRIBUTE_DATA debugeAttr = {};
+ assert(::GetFileAttributesEx(ffs3::applyLongPathPrefix(i->first).c_str(), //__in LPCTSTR lpFileName,
+ GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId,
+ &debugeAttr)); //__out LPVOID lpFileInformation
- sink->onFile(shortName, fullName, details);
+ assert(::CompareFileTime(&debugeAttr.ftCreationTime, &encodedTime.createTimeRaw) == 0);
+ assert(::CompareFileTime(&debugeAttr.ftLastWriteTime, &encodedTime.writeTimeRaw) == 0);
+#endif
+ }
}
}
+
+ const bool isFatFileSystem;
+ typedef std::vector<std::pair<Zstring, FILETIME> > FilenameTimeList;
+ FilenameTimeList markForDstHack;
+//####################################### DST hack ###########################################
#endif
-}
+};
-void ffs3::traverseFolder(const Zstring& directory,
- bool followSymlinks,
- TraverseCallback* sink)
+void ffs3::traverseFolder(const Zstring& directory, bool followSymlinks, TraverseCallback& sink, DstHackCallback* dstCallback)
{
-#ifdef FFS_WIN
- const Zstring& directoryFormatted = directory;
-#elif defined FFS_LINUX
- const Zstring directoryFormatted = //remove trailing slash
- directory.size() > 1 && directory.EndsWith(common::FILE_NAME_SEPARATOR) ? //exception: allow '/'
- directory.BeforeLast(common::FILE_NAME_SEPARATOR) :
- directory;
-#endif
-
- if (followSymlinks)
- traverseDirectory<true>(directoryFormatted, sink, 0);
- else
- traverseDirectory<false>(directoryFormatted, sink, 0);
+ DirTraverser(directory, followSymlinks, sink, dstCallback);
}
diff --git a/shared/file_traverser.h b/shared/file_traverser.h
index d61ff1cd..61237834 100644
--- a/shared/file_traverser.h
+++ b/shared/file_traverser.h
@@ -33,9 +33,8 @@ public:
bool dirLink; //"true": point to dir; "false": point to file (or broken Link on Linux)
};
- class ReturnValDir
+ struct ReturnValDir
{
- public:
enum ReturnValueEnh
{
TRAVERSING_DIR_IGNORE,
@@ -43,7 +42,7 @@ public:
};
ReturnValDir(Loki::Int2Type<TRAVERSING_DIR_IGNORE>) : returnCode(TRAVERSING_DIR_IGNORE), subDirCb(NULL) {}
- ReturnValDir(Loki::Int2Type<TRAVERSING_DIR_CONTINUE>, TraverseCallback* subDirCallback) : returnCode(TRAVERSING_DIR_CONTINUE), subDirCb(subDirCallback) {}
+ ReturnValDir(Loki::Int2Type<TRAVERSING_DIR_CONTINUE>, TraverseCallback& subDirCallback) : returnCode(TRAVERSING_DIR_CONTINUE), subDirCb(&subDirCallback) {}
const ReturnValueEnh returnCode;
TraverseCallback* const subDirCb;
@@ -51,14 +50,28 @@ public:
//overwrite these virtual methods
virtual void onError(const wxString& errorText) = 0;
- virtual void onFile( const DefaultChar* shortName, const Zstring& fullName, const FileInfo& details) = 0;
- virtual void onSymlink( const DefaultChar* shortName, const Zstring& fullName, const SymlinkInfo& details) = 0;
- virtual ReturnValDir onDir(const DefaultChar* 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 ReturnValDir onDir(const Zchar* shortName, const Zstring& fullName) = 0;
};
+
+#ifdef FFS_WIN
+struct DstHackCallback
+{
+ virtual ~DstHackCallback() {}
+ virtual void requestUiRefresh(const Zstring& filename) = 0; //applying DST hack imposes significant one-time performance drawback => callback to inform user
+};
+#else
+struct DstHackCallback; //DST hack not required on Linux
+#endif
+
//custom traverser with detail information about files
//directory may end with PATH_SEPARATOR
-void traverseFolder(const Zstring& directory, bool followSymlinks, TraverseCallback* sink); //throw();
+void traverseFolder(const Zstring& directory, //throw();
+ bool followSymlinks,
+ TraverseCallback& sink,
+ DstHackCallback* dstCallback = NULL); //apply DST hack if callback is supplied
//followSymlinks:
//"true": Symlinks dereferenced and reported via onFile() and onDir() => onSymlink not used!
//"false": Symlinks directly reported via onSymlink(), directory symlinks are not followed
diff --git a/shared/global_func.cpp b/shared/global_func.cpp
index d1ab9d2c..c3d76c57 100644
--- a/shared/global_func.cpp
+++ b/shared/global_func.cpp
@@ -16,33 +16,6 @@ size_t common::getDigitCount(size_t number) //count number of digits
return number == 0 ? 1 : static_cast<size_t>(::log10(static_cast<double>(number))) + 1;
}
-
-//############################################################################
-Performance::Performance() :
- resultWasShown(false),
- timer(new wxStopWatch)
-{
- timer->Start();
-}
-
-
-Performance::~Performance()
-{
- //keep non-inline destructor for std::auto_ptr to work with forward declaration
-
- if (!resultWasShown)
- showResult();
-}
-
-
-void Performance::showResult()
-{
- resultWasShown = true;
- wxMessageBox(wxLongLong(timer->Time()).ToString() + wxT(" ms"));
- timer->Start(); //reset timer
-}
-
-
//############################################################################
DebugLog::DebugLog() :
lineCount(0),
diff --git a/shared/global_func.h b/shared/global_func.h
index ba455371..c3994ca0 100644
--- a/shared/global_func.h
+++ b/shared/global_func.h
@@ -74,24 +74,6 @@ ForwardIterator custom_binary_search(ForwardIterator first, ForwardIterator last
//############################################################################
-class Performance
-{
-public:
- wxDEPRECATED(Performance()); //generate compiler warnings as a reminder to remove code after measurements
- ~Performance();
- void showResult();
-
-private:
- bool resultWasShown;
- std::auto_ptr<wxStopWatch> timer;
-};
-
-//two macros for quick performance measurements
-#define PERF_START Performance a;
-#define PERF_STOP a.showResult();
-
-
-//############################################################################
class wxFile;
class DebugLog
{
diff --git a/shared/guid.cpp b/shared/guid.cpp
index d359c29c..b20a1143 100644
--- a/shared/guid.cpp
+++ b/shared/guid.cpp
@@ -6,7 +6,17 @@
//
#include "guid.h"
#include <boost/uuid/uuid.hpp>
+
+//boost really should clean a bit up...
+#ifdef __MINGW32__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#endif
#include <boost/uuid/uuid_generators.hpp>
+#ifdef __MINGW32__
+#pragma GCC diagnostic pop
+#endif
+
#include <cassert>
#include <algorithm>
#include <vector>
diff --git a/shared/lock.cpp b/shared/lock.cpp
deleted file mode 100644
index 6b3dbcbb..00000000
--- a/shared/lock.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// **************************************************************************
-// * 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-2010 ZenJu (zhnmju123 AT gmx.de) *
-// **************************************************************************
-//
-#include "lock.h"
-#include "file_handling.h"
-//
-//
-//util::LockDirectory::LockDirectory(const Zstring& dirname)
-//{
-//
-//}
-//
-//
-//util::LockDirectory::~LockDirectory()
-//{
-//}
-//
-//
-//bool util::LockDirectoryisLocked(const Zstring& dirname);
diff --git a/shared/lock.h b/shared/lock.h
deleted file mode 100644
index f416f994..00000000
--- a/shared/lock.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// **************************************************************************
-// * 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-2010 ZenJu (zhnmju123 AT gmx.de) *
-// **************************************************************************
-//
-#ifndef LOCK_H_INCLUDED
-#define LOCK_H_INCLUDED
-
-#include "zstring.h"
-#include <vector>
-
-//namespace Utility
-//{
-//class LockDirectory
-//{
-//public:
-// LockDirectory(const Zstring& dirname);
-// ~LockDirectory();
-//
-//private:
-// std::vector<Zstring> toBeReleased; //list of files that will be deleted
-//};
-//}
-
-#endif //LOCK_H_INCLUDED
diff --git a/shared/loki/SmallObj.cpp b/shared/loki/SmallObj.cpp
index a2911bb1..adb54c55 100644
--- a/shared/loki/SmallObj.cpp
+++ b/shared/loki/SmallObj.cpp
@@ -2,21 +2,21 @@
// The Loki Library
// Copyright (c) 2001 by Andrei Alexandrescu
// This code accompanies the book:
-// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
+// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
// Patterns Applied". Copyright (c) 2001. Addison-Wesley.
-// Permission to use, copy, modify, distribute and sell this software for any
-// purpose is hereby granted without fee, provided that the above copyright
-// notice appear in all copies and that both that copyright notice and this
+// Permission to use, copy, modify, distribute and sell this software for any
+// purpose is hereby granted without fee, provided that the above copyright
+// notice appear in all copies and that both that copyright notice and this
// permission notice appear in supporting documentation.
-// The author or Addison-Wesley Longman make no representations about the
-// suitability of this software for any purpose. It is provided "as is"
+// The author or Addison-Wesley Longman make no representations about the
+// suitability of this software for any purpose. It is provided "as is"
// without express or implied warranty.
////////////////////////////////////////////////////////////////////////////////
// $Id: SmallObj.cpp 823 2007-05-08 10:48:40Z lfittl $
-#include <loki/SmallObj.h>
+#include "SmallObj.h"
#include <cassert>
#include <climits>
@@ -359,7 +359,7 @@ void* Chunk::Allocate(std::size_t blockSize)
{
if ( IsFilled() ) return NULL;
- assert((firstAvailableBlock_ * blockSize) / blockSize ==
+ assert((firstAvailableBlock_ * blockSize) / blockSize ==
firstAvailableBlock_);
unsigned char * pResult = pData_ + (firstAvailableBlock_ * blockSize);
firstAvailableBlock_ = *pResult;
@@ -503,7 +503,7 @@ bool Chunk::IsBlockAvailable( void * p, unsigned char numBlocks,
std::size_t blockSize ) const
{
(void) numBlocks;
-
+
if ( IsFilled() )
return false;
@@ -860,8 +860,8 @@ void * FixedAllocator::Allocate( void )
}
}
else if ( allocChunk_ == emptyChunk_)
- // detach emptyChunk_ from allocChunk_, because after
- // calling allocChunk_->Allocate(blockSize_); the chunk
+ // detach emptyChunk_ from allocChunk_, because after
+ // calling allocChunk_->Allocate(blockSize_); the chunk
// is no longer empty.
emptyChunk_ = NULL;
@@ -997,7 +997,7 @@ void FixedAllocator::DoDeallocate(void* p)
assert( lastChunk->HasAvailable( numBlocks_ ) );
lastChunk->Release();
chunks_.pop_back();
- if ( ( allocChunk_ == lastChunk ) || allocChunk_->IsFilled() )
+ if ( ( allocChunk_ == lastChunk ) || allocChunk_->IsFilled() )
allocChunk_ = deallocChunk_;
}
emptyChunk_ = deallocChunk_;
@@ -1040,7 +1040,7 @@ void * DefaultAllocator( std::size_t numBytes, bool doThrow )
// DefaultDeallocator ---------------------------------------------------------
/** @ingroup SmallObjectGroupInternal
- Calls default deallocator when SmallObjAllocator decides not to handle a
+ Calls default deallocator when SmallObjAllocator decides not to handle a
request. The default deallocator could be the global delete operator or the
free function. The free function is the preferred default deallocator since
it matches malloc which is the preferred default allocator. SmallObjAllocator
diff --git a/shared/long_path_prefix.cpp b/shared/long_path_prefix.cpp
index bc784f70..1c17065c 100644
--- a/shared/long_path_prefix.cpp
+++ b/shared/long_path_prefix.cpp
@@ -9,8 +9,8 @@
//there are two flavors of long path prefix: one for UNC paths, one for regular paths
-const Zstring LONG_PATH_PREFIX = DefaultStr("\\\\?\\");
-const Zstring LONG_PATH_PREFIX_UNC = DefaultStr("\\\\?\\UNC");
+const Zstring LONG_PATH_PREFIX = Zstr("\\\\?\\");
+const Zstring LONG_PATH_PREFIX_UNC = Zstr("\\\\?\\UNC");
template <size_t max_path>
inline
@@ -19,8 +19,8 @@ Zstring applyLongPathPrefixImpl(const Zstring& path)
if ( path.length() >= max_path && //maximum allowed path length without prefix is (MAX_PATH - 1)
!path.StartsWith(LONG_PATH_PREFIX))
{
- if (path.StartsWith(DefaultStr("\\\\"))) //UNC-name, e.g. \\zenju-pc\Users
- return LONG_PATH_PREFIX_UNC + path.AfterFirst(DefaultChar('\\')); //convert to \\?\UNC\zenju-pc\Users
+ if (path.StartsWith(Zstr("\\\\"))) //UNC-name, e.g. \\zenju-pc\Users
+ return LONG_PATH_PREFIX_UNC + path.AfterFirst(Zchar('\\')); //convert to \\?\UNC\zenju-pc\Users
else
return LONG_PATH_PREFIX + path; //prepend \\?\ prefix
}
@@ -49,9 +49,9 @@ Zstring ffs3::removeLongPathPrefix(const Zstring& path) //throw()
{
Zstring finalPath = path;
if (path.StartsWith(LONG_PATH_PREFIX_UNC)) //UNC-name
- finalPath.Replace(LONG_PATH_PREFIX_UNC, DefaultStr("\\"), false);
+ finalPath.Replace(LONG_PATH_PREFIX_UNC, Zstr("\\"), false);
else
- finalPath.Replace(LONG_PATH_PREFIX, DefaultStr(""), false);
+ finalPath.Replace(LONG_PATH_PREFIX, Zstr(""), false);
return finalPath;
}
diff --git a/shared/perf.h b/shared/perf.h
index 62473259..e4e279bb 100644
--- a/shared/perf.h
+++ b/shared/perf.h
@@ -4,13 +4,23 @@
// * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) *
// **************************************************************************
//
-#include <windows.h>
+#ifndef DEBUG_PERF_HEADER
+#define DEBUG_PERF_HEADER
+
+//#define WIN32_LEAN_AND_MEAN -> not in a header
+#include <wx/msw/wrapwin.h> //includes "windows.h"
#include <sstream>
+#ifdef __MINGW32__
+ #define DEPRECATED(x) x __attribute__ ((deprecated))
+#elif defined _MSC_VER
+ #define DEPRECATED(x) __declspec(deprecated) x
+#endif
+
class Performance
{
public:
- Performance() : resultWasShown(false), startTime(::GetTickCount()) {}
+ DEPRECATED(Performance()) : resultWasShown(false), startTime(::GetTickCount()) {}
~Performance()
{
@@ -20,18 +30,16 @@ public:
void showResult()
{
- resultWasShown = true;
-
- const DWORD currentTime = ::GetTickCount();
- const DWORD delta = currentTime - startTime;
- startTime = currentTime;
-
+ const DWORD delta = ::GetTickCount() - startTime;
std::ostringstream ss;
ss << delta << " ms";
-
+
::MessageBoxA(NULL, ss.str().c_str(), "Timer", 0);
- }
+ resultWasShown = true;
+ startTime = ::GetTickCount(); //don't include call to MessageBox()!
+ }
+
private:
bool resultWasShown;
DWORD startTime;
@@ -40,3 +48,5 @@ private:
//two macros for quick performance measurements
#define PERF_START Performance a;
#define PERF_STOP a.showResult();
+
+#endif //DEBUG_PERF_HEADER \ No newline at end of file
diff --git a/shared/recycler.cpp b/shared/recycler.cpp
index 1a73e105..31257a65 100644
--- a/shared/recycler.cpp
+++ b/shared/recycler.cpp
@@ -82,17 +82,17 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw (
if (useIFileOperation) //new recycle bin usage: available since Vista
{
- std::vector<const wchar_t*> fileNames;
+ std::vector<const wchar_t*> fileNames;
std::transform(filesToDelete.begin(), filesToDelete.end(),
std::back_inserter(fileNames), std::mem_fun_ref(&Zstring::c_str));
- using namespace FileOp;
+ using namespace fileop;
static const MoveToRecycleBinFct moveToRecycler =
- util::loadDllFunction<MoveToRecycleBinFct>(getRecyclerDllName().c_str(), moveToRecycleBinFctName);
+ util::getDllFun<MoveToRecycleBinFct>(getRecyclerDllName().c_str(), moveToRecycleBinFctName);
static const GetLastErrorFct getLastError =
- util::loadDllFunction<GetLastErrorFct>(getRecyclerDllName().c_str(), getLastErrorFctName);
+ util::getDllFun<GetLastErrorFct>(getRecyclerDllName().c_str(), getLastErrorFctName);
if (moveToRecycler == NULL || getLastError == NULL)
throw FileError(wxString(_("Error moving to Recycle Bin:")) + wxT("\n\"") + fileNames[0] + wxT("\"\n\n") + //report first file only... better than nothing
@@ -103,11 +103,11 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw (
// std::transform(filesToDelete.begin(), filesToDelete.end(),
// std::back_inserter(temp), std::ptr_fun(ffs3::removeLongPathPrefix)); //::IFileOperation() can't handle \\?\-prefix!
- if (!(*moveToRecycler)(&fileNames[0], //array must not be empty
- fileNames.size()))
+ if (!moveToRecycler(&fileNames[0], //array must not be empty
+ fileNames.size()))
{
wchar_t errorMessage[2000];
- (*getLastError)(errorMessage, 2000);
+ getLastError(errorMessage, 2000);
throw FileError(wxString(_("Error moving to Recycle Bin:")) + wxT("\n\"") + fileNames[0] + wxT("\"\n\n") + //report first file only... better than nothing
wxT("(") + errorMessage + wxT(")"));
}
@@ -121,7 +121,7 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw (
//filenameDoubleNull += removeLongPathPrefix(*i); //::SHFileOperation() can't handle \\?\-prefix!
//You should use fully-qualified path names with this function. Using it with relative path names is not thread safe.
filenameDoubleNull += *i; //::SHFileOperation() can't handle \\?\-prefix!
- filenameDoubleNull += DefaultChar(0);
+ filenameDoubleNull += Zchar(0);
}
SHFILEOPSTRUCT fileOp;
@@ -166,8 +166,8 @@ void ffs3::moveToRecycleBin(const Zstring& fileToDelete) //throw (FileError)
try
{
if (!fileObj->trash())
- throw FileError(wxString(_("Error moving to Recycle Bin:")) + wxT("\n\"") + zToWx(fileToDelete) + wxT("\"\n\n") +
- wxT("(") + wxT("unknown error") + wxT(")"));
+ throw FileError(wxString(_("Error moving to Recycle Bin:")) + wxT("\n\"") + zToWx(fileToDelete) + wxT("\"\n\n") +
+ wxT("(") + wxT("unknown error") + wxT(")"));
}
catch (const Glib::Error& errorObj)
{
diff --git a/shared/serialize.h b/shared/serialize.h
index 99dd83d5..7c4fcd5e 100644
--- a/shared/serialize.h
+++ b/shared/serialize.h
@@ -131,14 +131,14 @@ Zstring readString(wxInputStream& stream) //read string from file stream
const size_t strLength = readNumber<size_t>(stream);
if (strLength <= 1000)
{
- DefaultChar buffer[1000];
- stream.Read(buffer, sizeof(DefaultChar) * strLength);
+ Zchar buffer[1000];
+ stream.Read(buffer, sizeof(Zchar) * strLength);
return Zstring(buffer, strLength);
}
else
{
- boost::scoped_array<DefaultChar> buffer(new DefaultChar[strLength]);
- stream.Read(buffer.get(), sizeof(DefaultChar) * strLength);
+ boost::scoped_array<Zchar> buffer(new Zchar[strLength]);
+ stream.Read(buffer.get(), sizeof(Zchar) * strLength);
return Zstring(buffer.get(), strLength);
}
}
@@ -148,7 +148,7 @@ inline
void writeString(wxOutputStream& stream, const Zstring& str) //write string to filestream
{
writeNumber<size_t>(stream, str.length());
- stream.Write(str.c_str(), sizeof(DefaultChar) * str.length());
+ stream.Write(str.c_str(), sizeof(Zchar) * str.length());
}
diff --git a/shared/shadow.cpp b/shared/shadow.cpp
index b38103de..39f21d2a 100644
--- a/shared/shadow.cpp
+++ b/shared/shadow.cpp
@@ -14,6 +14,7 @@
#include "build_info.h"
#include "ShadowCopy\shadow.h"
#include "string_conv.h"
+#include "Loki/ScopeGuard.h"
using shadow::ShadowCopy;
using shadow::WaitingForShadow;
@@ -41,17 +42,17 @@ bool newerThanXP()
bool runningWOW64() //test if process is running under WOW64 (reference http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx)
{
//dynamically load windows API function
- typedef BOOL (WINAPI *IsWow64ProcessFunc)(
+ typedef BOOL (WINAPI *IsWow64ProcessFun)(
HANDLE hProcess,
PBOOL Wow64Process);
- static const IsWow64ProcessFunc isWow64Process =
- util::loadDllFunction<IsWow64ProcessFunc>(L"kernel32.dll", "IsWow64Process");
+ static const IsWow64ProcessFun isWow64Process =
+ util::getDllFun<IsWow64ProcessFun>(L"kernel32.dll", "IsWow64Process");
if (isWow64Process)
{
BOOL isWow64 = FALSE;
- if ((*isWow64Process)(::GetCurrentProcess(), &isWow64))
+ if (isWow64Process(::GetCurrentProcess(), &isWow64))
return isWow64 == TRUE;
}
@@ -86,7 +87,6 @@ const std::wstring& getShadowDllName()
//#############################################################################################################
ShadowCopy::ShadowCopy(WaitingForShadow* callback) : callback_(callback) {}
-ShadowCopy::~ShadowCopy() {} //std::auto_ptr: keep non-inline
//#############################################################################################################
@@ -94,16 +94,15 @@ class ShadowCopy::ShadowVolume
{
public:
ShadowVolume(const Zstring& volumeNameFormatted) : //throw(FileError)
- realVol(volumeNameFormatted),
- backupHandle(NULL)
+ backupHandle(0)
{
using namespace shadow;
if (!createShadowCopy)
- createShadowCopy = util::loadDllFunction<CreateShadowCopyFct>(getShadowDllName(), createShadowCopyFctName);
+ createShadowCopy = util::getDllFun<CreateShadowCopyFct>(getShadowDllName(), createShadowCopyFctName);
if (!releaseShadowCopy)
- releaseShadowCopy = util::loadDllFunction<ReleaseShadowCopyFct>(getShadowDllName(), releaseShadowCopyFctName);
+ releaseShadowCopy = util::getDllFun<ReleaseShadowCopyFct>(getShadowDllName(), releaseShadowCopyFctName);
//check if shadow copy dll was loaded correctly
if ( createShadowCopy == NULL ||
@@ -138,7 +137,7 @@ public:
~ShadowVolume()
{
- releaseShadowCopy(backupHandle);
+ releaseShadowCopy(backupHandle); //fast! no performance optimization necessary
}
Zstring getShadowVolume() const //trailing path separator
@@ -146,11 +145,6 @@ public:
return shadowVol;
}
- Zstring getRealVolume() const //trailing path separator
- {
- return realVol;
- }
-
private:
ShadowVolume(const ShadowVolume&);
ShadowVolume& operator=(const ShadowVolume&);
@@ -159,9 +153,8 @@ private:
static shadow::ReleaseShadowCopyFct releaseShadowCopy;
Zstring shadowVol;
- const Zstring realVol;
- void* backupHandle;
+ ShadowHandle backupHandle;
};
@@ -195,9 +188,14 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
throw FileError(msg);
}
- if (!shadowVol.get() || shadowVol->getRealVolume() != volumeNameFormatted)
- shadowVol.reset(new ShadowVolume(volumeNameFormatted)); //throw (FileError)
+ //get or create instance of shadow volume
+ VolNameShadowMap::const_iterator iter = shadowVol.find(volumeNameFormatted);
+ if (iter == shadowVol.end())
+ {
+ boost::shared_ptr<ShadowVolume> newEntry(new ShadowVolume(volumeNameFormatted));
+ iter = shadowVol.insert(std::make_pair(volumeNameFormatted, newEntry)).first;
+ }
//return filename alias on shadow copy volume
- return shadowVol->getShadowVolume() + Zstring(inputFile.c_str() + pos + volumeNameFormatted.length());
+ return iter->second->getShadowVolume() + Zstring(inputFile.c_str() + pos + volumeNameFormatted.length());
}
diff --git a/shared/shadow.h b/shared/shadow.h
index 60d30bbd..2be44b41 100644
--- a/shared/shadow.h
+++ b/shared/shadow.h
@@ -13,7 +13,8 @@ use in windows build only!
#include "zstring.h"
#include "file_error.h"
-#include <memory>
+#include <map>
+#include <boost/shared_ptr.hpp>
namespace shadow
@@ -22,7 +23,7 @@ struct WaitingForShadow
{
virtual ~WaitingForShadow() {}
virtual void requestUiRefresh() = 0; //allowed to throw exceptions
- virtual void updateStatusText(const Zstring& text) = 0;
+ virtual void reportInfo(const Zstring& text) = 0;
};
@@ -30,7 +31,6 @@ class ShadowCopy //buffer access to Windows Volume Shadow Copy Service
{
public:
ShadowCopy(WaitingForShadow* callback);
- ~ShadowCopy();
Zstring makeShadowCopy(const Zstring& inputFile); //throw(FileError); returns filename on shadow copy
@@ -41,7 +41,8 @@ private:
WaitingForShadow* callback_;
class ShadowVolume;
- std::auto_ptr<ShadowVolume> shadowVol;
+ typedef std::map<Zstring, boost::shared_ptr<ShadowVolume>, LessFilename> VolNameShadowMap;
+ VolNameShadowMap shadowVol;
};
}
diff --git a/shared/string_conv.h b/shared/string_conv.h
index bbaef713..bb8f6c7e 100644
--- a/shared/string_conv.h
+++ b/shared/string_conv.h
@@ -14,8 +14,8 @@ namespace ffs3
{
//conversion from Zstring to wxString
wxString zToWx(const Zstring& str);
-wxString zToWx(const DefaultChar* str);
-wxString zToWx(DefaultChar ch);
+wxString zToWx(const Zchar* str);
+wxString zToWx(Zchar ch);
//conversion from wxString to Zstring
Zstring wxToZ(const wxString& str);
Zstring wxToZ(const wxChar* str);
@@ -52,39 +52,39 @@ Zstring wxToZ(wxChar ch);
inline
wxString zToWx(const Zstring& str)
{
-#ifdef ZSTRING_CHAR
- return wxString::FromUTF8(str.c_str(), str.length());
-#elif defined ZSTRING_WIDE_CHAR
+#ifdef FFS_WIN
return wxString(str.c_str(), str.length());
+#elif defined FFS_LINUX
+ return wxString::FromUTF8(str.c_str(), str.length());
#endif
}
inline
-wxString zToWx(const DefaultChar* str)
+wxString zToWx(const Zchar* str)
{
-#ifdef ZSTRING_CHAR
- return wxString::FromUTF8(str);
-#elif defined ZSTRING_WIDE_CHAR
+#ifdef FFS_WIN
return str;
+#elif defined FFS_LINUX
+ return wxString::FromUTF8(str);
#endif
}
inline
-wxString zToWx(DefaultChar ch)
+wxString zToWx(Zchar ch)
{
- return zToWx(Zstring() + ch);
+ return zToWx(Zstring(ch));
}
//-----------------------------------------------------------------
inline
Zstring wxToZ(const wxString& str)
{
-#ifdef ZSTRING_CHAR
- return Zstring(str.ToUTF8());
-#elif defined ZSTRING_WIDE_CHAR
+#ifdef FFS_WIN
return Zstring(str.c_str(), str.length());
+#elif defined FFS_LINUX
+ return Zstring(str.ToUTF8());
#endif
}
@@ -92,10 +92,10 @@ Zstring wxToZ(const wxString& str)
inline
Zstring wxToZ(const wxChar* str)
{
-#ifdef ZSTRING_CHAR
- return Zstring(wxString(str).ToUTF8());
-#elif defined ZSTRING_WIDE_CHAR
+#ifdef FFS_WIN
return str;
+#elif defined FFS_LINUX
+ return Zstring(wxString(str).ToUTF8());
#endif
}
diff --git a/shared/system_constants.h b/shared/system_constants.h
index 6877f5c7..7f34c467 100644
--- a/shared/system_constants.h
+++ b/shared/system_constants.h
@@ -16,12 +16,14 @@ namespace common
// GLOBALS
//------------------------------------------------
#ifdef FFS_WIN
-const DefaultChar FILE_NAME_SEPARATOR = '\\';
-static const wxChar* const LINE_BREAK = wxT("\r\n"); //internal linkage
+const Zchar FILE_NAME_SEPARATOR = '\\';
+const wxChar LINE_BREAK[] = wxT("\r\n"); //internal linkage
#elif defined FFS_LINUX
-const DefaultChar FILE_NAME_SEPARATOR = '/';
-static const wxChar* const LINE_BREAK = wxT("\n");
+const Zchar FILE_NAME_SEPARATOR = '/';
+const wxChar LINE_BREAK[] = wxT("\n");
#endif
+
+const char BYTE_ORDER_MARK_UTF8[] = "\xEF\xBB\xBF";
}
diff --git a/shared/taskbar.cpp b/shared/taskbar.cpp
index 61a1841b..f5f07c1e 100644
--- a/shared/taskbar.cpp
+++ b/shared/taskbar.cpp
@@ -12,17 +12,17 @@
#include <wx/msw/wrapwin.h> //includes "windows.h"
using namespace util;
+using namespace tbseven;
namespace
{
bool windows7TaskbarAvailable()
{
- OSVERSIONINFO osvi;
- ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ OSVERSIONINFO osvi = {};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- if (GetVersionEx(&osvi))
+ if (::GetVersionEx(&osvi))
return osvi.dwMajorVersion > 6 ||
(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion >= 1); //task bar progress available with Windows 7
//XP has majorVersion == 5, minorVersion == 1
@@ -45,86 +45,77 @@ const std::wstring& getTaskBarDllName()
return filename;
}
}
+//########################################################################################################
-struct TaskbarProgress::Pimpl
+class TaskbarProgress::Pimpl //throw (TaskbarNotAvailable)
{
- Pimpl() : tbHandle(0),
- assocWindow(NULL),
- init_(NULL),
- release_(NULL),
- setStatus_(NULL),
- setProgress_(NULL) {}
-
- TaskbarSeven::TBHandle tbHandle;
- void* assocWindow;
-
- TaskbarSeven::initFct init_;
- TaskbarSeven::releaseFct release_;
- TaskbarSeven::setStatusFct setStatus_;
- TaskbarSeven::setProgressFct setProgress_;
-};
-
-
-TaskbarProgress::TaskbarProgress(const wxTopLevelWindow& window) : pimpl_(new Pimpl)
-{
- if (!windows7TaskbarAvailable())
- throw TaskbarNotAvailable();
+public:
+ Pimpl(const wxTopLevelWindow& window) :
+ assocWindow(window.GetHWND()),
+ setStatus_(util::getDllFun<SetStatusFct>( getTaskBarDllName(), setStatusFctName)),
+ setProgress_(util::getDllFun<SetProgressFct>(getTaskBarDllName(), setProgressFctName))
+ {
+ if (!assocWindow || !setProgress_ || !setStatus_)
+ throw TaskbarNotAvailable();
- pimpl_->init_ = util::loadDllFunction<TaskbarSeven::initFct>( getTaskBarDllName(), TaskbarSeven::initFctName);
- pimpl_->release_ = util::loadDllFunction<TaskbarSeven::releaseFct>( getTaskBarDllName(), TaskbarSeven::releaseFctName);
- pimpl_->setProgress_ = util::loadDllFunction<TaskbarSeven::setProgressFct>(getTaskBarDllName(), TaskbarSeven::setProgressFctName);
- pimpl_->setStatus_ = util::loadDllFunction<TaskbarSeven::setStatusFct>( getTaskBarDllName(), TaskbarSeven::setStatusFctName);
+ if (!windows7TaskbarAvailable())
+ throw TaskbarNotAvailable();
+ }
- if ( !pimpl_->init_ ||
- !pimpl_->release_ ||
- !pimpl_->setProgress_ ||
- !pimpl_->setStatus_)
- throw TaskbarNotAvailable();
+ ~Pimpl()
+ {
+ setStatus(STATUS_NOPROGRESS);
+ }
- pimpl_->tbHandle = pimpl_->init_();
- if (pimpl_->tbHandle == 0)
- throw TaskbarNotAvailable();
+ void setStatus(Status status)
+ {
+ TaskBarStatus tbSevenStatus = tbseven::STATUS_NORMAL;
+ switch (status)
+ {
+ case TaskbarProgress::STATUS_NOPROGRESS:
+ tbSevenStatus = tbseven::STATUS_NOPROGRESS;
+ break;
+ case TaskbarProgress::STATUS_INDETERMINATE:
+ tbSevenStatus = tbseven::STATUS_INDETERMINATE;
+ break;
+ case TaskbarProgress::STATUS_NORMAL:
+ tbSevenStatus = tbseven::STATUS_NORMAL;
+ break;
+ case TaskbarProgress::STATUS_ERROR:
+ tbSevenStatus = tbseven::STATUS_ERROR;
+ break;
+ case TaskbarProgress::STATUS_PAUSED:
+ tbSevenStatus = tbseven::STATUS_PAUSED;
+ break;
+ }
+
+ setStatus_(assocWindow, tbSevenStatus);
+ }
- pimpl_->assocWindow = window.GetHWND();
-}
+ void setProgress(size_t current, size_t total)
+ {
+ setProgress_(assocWindow, current, total);
+ }
+private:
+ void* assocWindow;
+ const SetStatusFct setStatus_;
+ const SetProgressFct setProgress_;
+};
+//########################################################################################################
-TaskbarProgress::~TaskbarProgress()
-{
- setStatus(STATUS_NOPROGRESS);
- pimpl_->release_(pimpl_->tbHandle);
-}
+TaskbarProgress::TaskbarProgress(const wxTopLevelWindow& window) : pimpl_(new Pimpl(window)) {}
+TaskbarProgress::~TaskbarProgress() {} //std::auto_ptr ...
void TaskbarProgress::setStatus(Status status)
{
- TaskbarSeven::TaskBarStatus tbSevenStatus = TaskbarSeven::STATUS_NORMAL;
- switch (status)
- {
- case TaskbarProgress::STATUS_NOPROGRESS:
- tbSevenStatus = TaskbarSeven::STATUS_NOPROGRESS;
- break;
- case TaskbarProgress::STATUS_INDETERMINATE:
- tbSevenStatus = TaskbarSeven::STATUS_INDETERMINATE;
- break;
- case TaskbarProgress::STATUS_NORMAL:
- tbSevenStatus = TaskbarSeven::STATUS_NORMAL;
- break;
- case TaskbarProgress::STATUS_ERROR:
- tbSevenStatus = TaskbarSeven::STATUS_ERROR;
- break;
- case TaskbarProgress::STATUS_PAUSED:
- tbSevenStatus = TaskbarSeven::STATUS_PAUSED;
- break;
- }
-
- pimpl_->setStatus_(pimpl_->tbHandle, pimpl_->assocWindow, tbSevenStatus);
+ pimpl_->setStatus(status);
}
-
void TaskbarProgress::setProgress(size_t current, size_t total)
{
- pimpl_->setProgress_(pimpl_->tbHandle, pimpl_->assocWindow, current, total);
+ pimpl_->setProgress(current, total);
}
diff --git a/shared/taskbar.h b/shared/taskbar.h
index 02d7324d..acf2d6da 100644
--- a/shared/taskbar.h
+++ b/shared/taskbar.h
@@ -39,7 +39,7 @@ public:
void setProgress(size_t current, size_t total);
private:
- struct Pimpl;
+ class Pimpl;
std::auto_ptr<Pimpl> pimpl_;
};
diff --git a/shared/util.cpp b/shared/util.cpp
index 9e0cb9d3..25c8c5e0 100644
--- a/shared/util.cpp
+++ b/shared/util.cpp
@@ -15,6 +15,7 @@
#include <stdexcept>
#include "system_func.h"
#include "check_exist.h"
+#include "assert_static.h"
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
@@ -99,32 +100,6 @@ wxString ffs_Impl::includeNumberSeparator(const wxString& number)
}
-template <class T>
-void setDirectoryNameImpl(const wxString& dirname, T* txtCtrl, wxDirPickerCtrl* dirPicker)
-{
- using namespace ffs3;
-
- txtCtrl->SetValue(dirname);
- const Zstring dirFormatted = ffs3::getFormattedDirectoryName(wxToZ(dirname));
-
- if (util::dirExists(dirFormatted, 200) == util::EXISTING_TRUE) //potentially slow network access: wait 200ms at most
- dirPicker->SetPath(zToWx(dirFormatted));
-}
-
-
-void ffs3::setDirectoryName(const wxString& dirname, wxTextCtrl* txtCtrl, wxDirPickerCtrl* dirPicker)
-{
- setDirectoryNameImpl(dirname, txtCtrl, dirPicker);
-}
-
-
-void ffs3::setDirectoryName(const wxString& dirname, wxComboBox* txtCtrl, wxDirPickerCtrl* dirPicker)
-{
- txtCtrl->SetSelection(wxNOT_FOUND);
- setDirectoryNameImpl(dirname, txtCtrl, dirPicker);
-}
-
-
void ffs3::scrollToBottom(wxScrolledWindow* scrWindow)
{
int height = 0;
@@ -147,94 +122,128 @@ void ffs3::scrollToBottom(wxScrolledWindow* scrWindow)
namespace
{
-inline
-void writeTwoDigitNumber(size_t number, wxString& string)
-{
- assert (number < 100);
-
- string += wxChar('0' + number / 10);
- string += wxChar('0' + number % 10);
-}
-
-
-inline
-void writeFourDigitNumber(size_t number, wxString& string)
+#ifdef FFS_WIN
+bool isVistaOrLater()
{
- assert (number < 10000);
-
- string += wxChar('0' + number / 1000);
- number %= 1000;
- string += wxChar('0' + number / 100);
- number %= 100;
- string += wxChar('0' + number / 10);
- number %= 10;
- string += wxChar('0' + number);
+ OSVERSIONINFO osvi = {};
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+ //symbolic links are supported starting with Vista
+ if (::GetVersionEx(&osvi))
+ return osvi.dwMajorVersion > 5; //XP has majorVersion == 5, minorVersion == 1; Vista majorVersion == 6, dwMinorVersion == 0
+ //overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
+ return false;
}
+#endif
}
+
wxString ffs3::utcTimeToLocalString(const wxLongLong& utcTime)
{
#ifdef FFS_WIN
//convert ansi C time to FILETIME
wxLongLong fileTimeLong(utcTime);
-
fileTimeLong += wxLongLong(2, 3054539008UL); //timeshift between ansi C time and FILETIME in seconds == 11644473600s
fileTimeLong *= 10000000;
FILETIME lastWriteTimeUtc;
lastWriteTimeUtc.dwLowDateTime = fileTimeLong.GetLo(); //GetLo() returns unsigned
- lastWriteTimeUtc.dwHighDateTime = unsigned(fileTimeLong.GetHi()); //GetHi() returns signed
-
-
- FILETIME localFileTime;
- if (::FileTimeToLocalFileTime( //convert to local time
- &lastWriteTimeUtc, //pointer to UTC file time to convert
- &localFileTime //pointer to converted file time
- ) == 0)
- throw std::runtime_error(std::string((wxString(_("Conversion error:")) + wxT(" FILETIME -> local FILETIME: ") +
- wxT("(") + wxULongLong(lastWriteTimeUtc.dwHighDateTime, lastWriteTimeUtc.dwLowDateTime).ToString() + wxT(") ") +
- wxT("\n\n") + getLastErrorFormatted()).ToAscii()));
-
- if (localFileTime.dwHighDateTime > 0x7fffffff)
- return _("Error"); //this actually CAN happen if UTC time is just below this border and ::FileTimeToLocalFileTime() adds 2 hours due to DST or whatever!
- //Testcase (UTC): dateHigh = 2147483647 (=0x7fffffff) -> year 30000
- // dateLow = 4294967295
-
- SYSTEMTIME time;
- if (::FileTimeToSystemTime(
- &localFileTime, //pointer to file time to convert
- &time //pointer to structure to receive system time
- ) == 0)
- throw std::runtime_error(std::string((wxString(_("Conversion error:")) + wxT(" local FILETIME -> SYSTEMTIME: ") +
- wxT("(") + wxULongLong(localFileTime.dwHighDateTime, localFileTime.dwLowDateTime).ToString() + wxT(") ") +
- wxT("\n\n") + getLastErrorFormatted()).ToAscii()));
-
- //assemble time string (performance optimized) -> note: performance argument may not be valid any more
- wxString formattedTime;
- formattedTime.reserve(20);
-
- writeFourDigitNumber(time.wYear, formattedTime);
- formattedTime += wxChar('-');
- writeTwoDigitNumber(time.wMonth, formattedTime);
- formattedTime += wxChar('-');
- writeTwoDigitNumber(time.wDay, formattedTime);
- formattedTime += wxChar(' ');
- formattedTime += wxChar(' ');
- writeTwoDigitNumber(time.wHour, formattedTime);
- formattedTime += wxChar(':');
- writeTwoDigitNumber(time.wMinute, formattedTime);
- formattedTime += wxChar(':');
- writeTwoDigitNumber(time.wSecond, formattedTime);
-
- return formattedTime;
+ lastWriteTimeUtc.dwHighDateTime = static_cast<DWORD>(fileTimeLong.GetHi()); //GetHi() returns signed
+
+ assert(fileTimeLong.GetHi() >= 0);
+ assert_static(sizeof(DWORD) == sizeof(long));
+ assert_static(sizeof(long) == 4);
+
+ SYSTEMTIME systemTimeLocal = {};
+
+ static const bool useNewLocalTimeCalculation = isVistaOrLater();
+ if (useNewLocalTimeCalculation) //use DST setting from source date (like in Windows 7, see http://msdn.microsoft.com/en-us/library/ms724277(VS.85).aspx)
+ {
+ if (lastWriteTimeUtc.dwHighDateTime > 0x7FFFFFFF)
+ return _("Error");
+
+ SYSTEMTIME systemTimeUtc = {};
+ if (!::FileTimeToSystemTime(
+ &lastWriteTimeUtc, //__in const FILETIME *lpFileTime,
+ &systemTimeUtc)) //__out LPSYSTEMTIME lpSystemTime
+ throw std::runtime_error(std::string((wxString(_("Conversion error:")) + wxT(" FILETIME -> SYSTEMTIME: ") +
+ wxT("(") + wxULongLong(lastWriteTimeUtc.dwHighDateTime, lastWriteTimeUtc.dwLowDateTime).ToString() + wxT(") ") +
+ wxT("\n\n") + getLastErrorFormatted()).ToAscii()));
+
+ if (!::SystemTimeToTzSpecificLocalTime(
+ NULL, //__in_opt LPTIME_ZONE_INFORMATION lpTimeZone,
+ &systemTimeUtc, //__in LPSYSTEMTIME lpUniversalTime,
+ &systemTimeLocal)) //__out LPSYSTEMTIME lpLocalTime
+ throw std::runtime_error(std::string((wxString(_("Conversion error:")) + wxT(" SYSTEMTIME -> local SYSTEMTIME: ") +
+ wxT("(") + wxULongLong(lastWriteTimeUtc.dwHighDateTime, lastWriteTimeUtc.dwLowDateTime).ToString() + wxT(") ") +
+ wxT("\n\n") + getLastErrorFormatted()).ToAscii()));
+ }
+ else //use current DST setting (like in Windows 2000 and XP)
+ {
+ FILETIME fileTimeLocal = {};
+ if (!::FileTimeToLocalFileTime( //convert to local time
+ &lastWriteTimeUtc, //pointer to UTC file time to convert
+ &fileTimeLocal)) //pointer to converted file time
+ throw std::runtime_error(std::string((wxString(_("Conversion error:")) + wxT(" FILETIME -> local FILETIME: ") +
+ wxT("(") + wxULongLong(lastWriteTimeUtc.dwHighDateTime, lastWriteTimeUtc.dwLowDateTime).ToString() + wxT(") ") +
+ wxT("\n\n") + getLastErrorFormatted()).ToAscii()));
+
+ if (fileTimeLocal.dwHighDateTime > 0x7FFFFFFF)
+ return _("Error"); //this actually CAN happen if UTC time is just below this border and ::FileTimeToLocalFileTime() adds 2 hours due to DST or whatever!
+ //Testcase (UTC): dateHigh = 2147483647 (=0x7fffffff) -> year 30000
+ // dateLow = 4294967295
+
+ if (!::FileTimeToSystemTime(
+ &fileTimeLocal, //pointer to file time to convert
+ &systemTimeLocal)) //pointer to structure to receive system time
+ throw std::runtime_error(std::string((wxString(_("Conversion error:")) + wxT(" local FILETIME -> local SYSTEMTIME: ") +
+ wxT("(") + wxULongLong(fileTimeLocal.dwHighDateTime, fileTimeLocal.dwLowDateTime).ToString() + wxT(") ") +
+ wxT("\n\n") + getLastErrorFormatted()).ToAscii()));
+ }
+
+ /*
+ //assemble time string (performance optimized) -> note: performance argument is not valid anymore
+
+ wxString formattedTime;
+ formattedTime.reserve(20);
+
+ writeFourDigitNumber(time.wYear, formattedTime);
+ formattedTime += wxChar('-');
+ writeTwoDigitNumber(time.wMonth, formattedTime);
+ formattedTime += wxChar('-');
+ writeTwoDigitNumber(time.wDay, formattedTime);
+ formattedTime += wxChar(' ');
+ formattedTime += wxChar(' ');
+ writeTwoDigitNumber(time.wHour, formattedTime);
+ formattedTime += wxChar(':');
+ writeTwoDigitNumber(time.wMinute, formattedTime);
+ formattedTime += wxChar(':');
+ writeTwoDigitNumber(time.wSecond, formattedTime);
+ */
+ const wxDateTime localTime(systemTimeLocal.wDay,
+ wxDateTime::Month(systemTimeLocal.wMonth - 1),
+ systemTimeLocal.wYear,
+ systemTimeLocal.wHour,
+ systemTimeLocal.wMinute,
+ systemTimeLocal.wSecond);
#elif defined FFS_LINUX
tm* timeinfo;
const time_t fileTime = utcTime.ToLong();
timeinfo = localtime(&fileTime); //convert to local time
- char buffer[50];
- strftime(buffer, 50, "%Y-%m-%d %H:%M:%S", timeinfo);
- return zToWx(buffer);
+ /*
+ char buffer[50];
+ ::strftime(buffer, 50, "%Y-%m-%d %H:%M:%S", timeinfo);
+ return zToWx(buffer);
+ */
+ const wxDateTime localTime(timeinfo->tm_mday,
+ wxDateTime::Month(timeinfo->tm_mon),
+ 1900 + timeinfo->tm_year,
+ timeinfo->tm_hour,
+ timeinfo->tm_min,
+ timeinfo->tm_sec);
#endif
+
+ return localTime.FormatDate() + wxT(" ") + localTime.FormatTime();
}
diff --git a/shared/util.h b/shared/util.h
index 5023c00b..2495838c 100644
--- a/shared/util.h
+++ b/shared/util.h
@@ -11,11 +11,10 @@
#include <wx/string.h>
#include <wx/longlong.h>
#include "../shared/global_func.h"
-
-class wxComboBox;
-class wxTextCtrl;
-class wxDirPickerCtrl;
-class wxScrolledWindow;
+#include <wx/textctrl.h>
+#include <wx/filepicker.h>
+#include <wx/combobox.h>
+#include <wx/scrolwin.h>
namespace ffs3
@@ -29,8 +28,6 @@ wxString formatPercentage(const wxLongLong& dividend, const wxLongLong& divisor)
template <class NumberType>
wxString numberToStringSep(NumberType number); //convert number to wxString including thousands separator
-void setDirectoryName(const wxString& dirname, wxTextCtrl* txtCtrl, wxDirPickerCtrl* dirPicker);
-void setDirectoryName(const wxString& dirname, wxComboBox* txtCtrl, wxDirPickerCtrl* dirPicker);
void scrollToBottom(wxScrolledWindow* scrWindow);
wxString utcTimeToLocalString(const wxLongLong& utcTime); //throw std::runtime_error
diff --git a/shared/zbase.h b/shared/zbase.h
new file mode 100644
index 00000000..efaa103c
--- /dev/null
+++ b/shared/zbase.h
@@ -0,0 +1,1162 @@
+// **************************************************************************
+// * 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-2010 ZenJu (zhnmju123 AT gmx.de) *
+// **************************************************************************
+//
+#ifndef Z_BASE_H_INCLUDED
+#define Z_BASE_H_INCLUDED
+
+#include <cstddef> //size_t
+#include <cctype> //isspace
+#include <cwctype> //iswspace
+#include <cassert>
+#include <vector>
+#include <sstream>
+#include <algorithm>
+
+
+/*
+Allocator Policy:
+-----------------
+ void* allocate(size_t size) //throw (std::bad_alloc)
+ void deallocate(void* ptr)
+*/
+class AllocatorFreeStore //same performance characterisics like malloc()/free()
+{
+public:
+ static void* allocate(size_t size) //throw (std::bad_alloc)
+ {
+ return ::operator new(size);
+ }
+
+ static void deallocate(void* ptr)
+ {
+ ::operator delete(ptr);
+ }
+};
+
+
+/*
+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 T, //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)
+ {
+ const size_t newCapacity = calcCapacity(minCapacity);
+ assert(newCapacity >= minCapacity);
+ assert(minCapacity >= size);
+
+ Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(T)));
+
+ newDescr->length = size;
+ newDescr->capacity = newCapacity;
+
+ return reinterpret_cast<T*>(newDescr + 1);
+ }
+
+ static T* clone(T* ptr)
+ {
+ T* newData = create(length(ptr));
+ std::copy(ptr, ptr + length(ptr) + 1, newData);
+ return newData;
+ }
+
+ static void destroy(T* ptr)
+ {
+ AP::deallocate(descr(ptr));
+ }
+
+ static bool canWrite(const T* ptr, size_t minCapacity) //needs to be checked before writing to "ptr"
+ {
+ return minCapacity <= descr(ptr)->capacity;
+ }
+
+ static size_t length(const T* ptr)
+ {
+ return descr(ptr)->length;
+ }
+
+ static void setLength(T* ptr, size_t newLength)
+ {
+ assert(canWrite(ptr, newLength));
+ descr(ptr)->length = newLength;
+ }
+
+private:
+ struct Descriptor
+ {
+ size_t length;
+ 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 size_t calcCapacity(size_t length)
+ {
+ return (length + (19 - length % 16)); //allocate some additional length to speed up concatenation
+ }
+};
+
+
+template <typename T, //Character Type
+ class AP> //Allocator Policy
+class StorageRefCount : public AP
+{
+protected:
+ ~StorageRefCount() {}
+
+ static T* create(size_t size)
+ {
+ return create(size, size);
+ }
+
+ static T* create(size_t size, size_t minCapacity)
+ {
+ const size_t newCapacity = calcCapacity(minCapacity);
+ assert(newCapacity >= minCapacity);
+ assert(minCapacity >= size);
+
+ Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(T)));
+
+ newDescr->refCount = 1;
+ newDescr->length = size;
+ newDescr->capacity = newCapacity;
+
+ return reinterpret_cast<T*>(newDescr + 1);
+ }
+
+ static T* clone(T* ptr)
+ {
+ assert(descr(ptr)->refCount > 0);
+ ++descr(ptr)->refCount;
+ return ptr;
+ }
+
+ static void destroy(T* ptr)
+ {
+ if (--descr(ptr)->refCount == 0)
+ AP::deallocate(descr(ptr));
+ }
+
+ static bool canWrite(const T* 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)
+ {
+ return descr(ptr)->length;
+ }
+
+ static void setLength(T* ptr, size_t newLength)
+ {
+ assert(canWrite(ptr, newLength));
+ descr(ptr)->length = newLength;
+ }
+
+private:
+ struct Descriptor
+ {
+ size_t refCount;
+ size_t length;
+ 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 size_t calcCapacity(size_t length)
+ {
+ return (length + (19 - length % 16)); //allocate some additional length to speed up concatenation
+ }
+};
+
+
+template <class T, //Character Type
+ template <class, class> class SP = StorageRefCount, //Storage Policy
+ class AP = AllocatorFreeStore> //Allocator Policy
+class Zbase : public SP<T, AP>
+{
+public:
+ Zbase();
+ Zbase(T source);
+ Zbase(const T* source);
+ Zbase(const T* source, size_t length);
+ Zbase(const Zbase& source);
+ ~Zbase();
+
+ operator const T*() const; //implicit conversion to C-string
+
+ //STL accessors
+ const T* begin() const;
+ const T* end() const;
+ T* begin();
+ T* end();
+
+ //wxString-like functions
+ bool StartsWith(const Zbase& prefix) const;
+ bool StartsWith(const T* prefix) const;
+ bool StartsWith(T prefix) const;
+ bool EndsWith(const Zbase& postfix) const;
+ bool EndsWith(const T* postfix) const;
+ bool EndsWith(T postfix) const;
+ Zbase& Truncate(size_t newLen);
+ Zbase& Replace(const T* old, const T* replacement, bool replaceAll = true);
+ Zbase AfterLast( T ch) const; //returns the whole string if "ch" not found
+ Zbase BeforeLast( T ch) const; //returns empty string if "ch" not found
+ Zbase AfterFirst( T ch) const; //returns empty string if "ch" not found
+ Zbase BeforeFirst(T ch) const; //returns the whole string if "ch" not found
+ Zbase& Trim(bool fromLeft = true, bool fromRight = true);
+ std::vector<Zbase> Split(T delimiter) const;
+
+ //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;
+ Zbase substr(size_t pos = 0, size_t len = npos) 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; //
+ Zbase& replace(size_t pos1, size_t n1, const T* str, size_t n2);
+ void reserve(size_t minCapacity);
+ Zbase& assign(const T* source, size_t len);
+ void resize(size_t newSize, T fillChar = 0);
+ void swap(Zbase& other);
+
+ //number conversion
+ template <class N> static Zbase fromNumber(N number);
+ template <class N> N toNumber() const;
+
+ Zbase& operator=(const Zbase& source);
+ Zbase& operator=(const T* source);
+ Zbase& operator=(T source);
+ Zbase& operator+=(const Zbase& other);
+ Zbase& operator+=(const T* other);
+ Zbase& operator+=(T ch);
+
+ static const size_t npos = static_cast<size_t>(-1);
+
+private:
+ Zbase(int); //detect usage errors
+ Zbase& operator=(int); //
+
+ T* 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 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 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 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);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//################################# inline implementation ########################################
+namespace z_impl
+{
+//-------------C-string helper functions ---------------------------------------------------------
+template <class T>
+inline
+size_t cStringLength(const T* input) //strlen()
+{
+ const T* iter = input;
+ while (*iter != 0)
+ ++iter;
+ return iter - input;
+}
+}
+
+
+//--------------------------------------------------------------------------------------------------
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP>::Zbase()
+{
+ //resist the temptation to avoid this allocation by referening a static global: NO performance advantage, MT issues!
+ rawStr = this->create(0);
+ rawStr[0] = 0;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP>::Zbase(T source)
+{
+ rawStr = this->create(1);
+ rawStr[0] = source;
+ rawStr[1] = 0;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP>::Zbase(const T* source)
+{
+ const size_t sourceLen = z_impl::cStringLength(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)
+{
+ rawStr = this->create(sourceLen);
+ std::copy(source, source + sourceLen, rawStr);
+ rawStr[sourceLen] = 0;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP>::Zbase(const Zbase<T, SP, AP>& source)
+{
+ rawStr = this->clone(source.rawStr);
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP>::~Zbase()
+{
+ this->destroy(rawStr);
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP>::operator const T*() const
+{
+ return rawStr;
+}
+
+
+// get all characters after the last occurence of ch
+// (returns the whole string if ch not found)
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP> Zbase<T, SP, AP>::AfterLast(T ch) const
+{
+ const size_t pos = rfind(ch, npos);
+ if (pos != npos )
+ return Zbase(rawStr + pos + 1, length() - pos - 1);
+ else
+ return *this;
+}
+
+
+// get all characters before the last occurence of ch
+// (returns empty string if ch not found)
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP> Zbase<T, SP, AP>::BeforeLast(T ch) const
+{
+ const size_t pos = rfind(ch, npos);
+ if (pos != npos)
+ return Zbase(rawStr, pos); //data is non-empty string in this context: else ch would not have been found!
+ else
+ return Zbase();
+}
+
+
+//returns empty string if ch not found
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP> Zbase<T, SP, AP>::AfterFirst(T ch) const
+{
+ const size_t pos = find(ch, 0);
+ if (pos != npos)
+ return Zbase(rawStr + pos + 1, length() - pos - 1);
+ else
+ return Zbase();
+
+}
+
+//returns the whole string if ch not found
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP> Zbase<T, SP, AP>::BeforeFirst(T ch) const
+{
+ const size_t pos = find(ch, 0);
+ if (pos != npos)
+ return Zbase(rawStr, pos); //data is non-empty string in this context: else ch would not have been found!
+ else
+ return *this;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+bool Zbase<T, SP, AP>::StartsWith(const T* prefix) const
+{
+ const size_t pfLength = z_impl::cStringLength(prefix);
+ if (length() < pfLength)
+ return false;
+
+ return std::equal(rawStr, rawStr + pfLength,
+ prefix);
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+bool Zbase<T, SP, AP>::StartsWith(T prefix) const
+{
+ return length() != 0 && operator[](0) == prefix;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+bool Zbase<T, SP, AP>::StartsWith(const Zbase<T, SP, AP>& prefix) const
+{
+ if (length() < prefix.length())
+ return false;
+
+ return std::equal(rawStr, rawStr + prefix.length(),
+ prefix.rawStr);
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+bool Zbase<T, SP, AP>::EndsWith(const T* postfix) const
+{
+ const size_t pfLength = z_impl::cStringLength(postfix);
+ if (length() < pfLength)
+ return false;
+
+ const T* cmpBegin = rawStr + length() - pfLength;
+ return std::equal(cmpBegin, cmpBegin + pfLength,
+ postfix);
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+bool Zbase<T, SP, AP>::EndsWith(T postfix) const
+{
+ const size_t len = length();
+ return len != 0 && operator[](len - 1) == postfix;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+bool Zbase<T, SP, AP>::EndsWith(const Zbase<T, SP, AP>& postfix) const
+{
+ if (length() < postfix.length())
+ return false;
+
+ const T* cmpBegin = rawStr + length() - postfix.length();
+ return std::equal(cmpBegin, cmpBegin + postfix.length(),
+ postfix.rawStr);
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP>& Zbase<T, SP, AP>::Truncate(size_t newLen)
+{
+ if (newLen < length())
+ {
+ if (canWrite(rawStr, newLen))
+ {
+ rawStr[newLen] = 0;
+ setLength(rawStr, newLen);
+ }
+ else
+ *this = Zbase(rawStr, newLen);
+ }
+ return *this;
+}
+
+
+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
+{
+ assert(pos <= length());
+ const T* thisEnd = end(); //respect embedded 0
+ const T* 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
+{
+ assert(pos <= length());
+ const T* thisEnd = end(); //respect embedded 0
+ const T* iter = std::search(begin() + pos, thisEnd,
+ str, str + z_impl::cStringLength(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
+{
+ assert(pos <= length());
+ const T* thisEnd = end();
+ const T* 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
+{
+ 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
+
+ while (rawStr[pos] != ch) //pos points to last char of the string
+ {
+ if (pos == 0)
+ return npos;
+ --pos;
+ }
+ return pos;
+}
+
+
+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 T* str, size_t n2)
+{
+ assert(str < rawStr || rawStr + length() < str); //str mustn't point to data in this string
+ assert(pos1 + n1 <= length());
+
+ const size_t oldLen = length();
+ if (oldLen == 0)
+ return *this = Zbase(str, n2);
+
+ const size_t newLen = oldLen - n1 + n2;
+
+ if (canWrite(rawStr, newLen))
+ {
+ if (n1 < n2) //move remainder right -> std::copy_backward
+ {
+ std::copy_backward(rawStr + pos1 + n1, rawStr + oldLen + 1, rawStr + newLen + 1); //include null-termination
+ setLength(rawStr, newLen);
+ }
+ else if (n1 > n2) //shift left -> std::copy
+ {
+ std::copy(rawStr + pos1 + n1, rawStr + oldLen + 1, rawStr + pos1 + n2); //include null-termination
+ setLength(rawStr, newLen);
+ }
+
+ std::copy(str, str + n2, rawStr + pos1);
+ }
+ else
+ {
+ //copy directly into new string
+ T* const newStr = this->create(newLen);
+
+ std::copy(rawStr, rawStr + pos1, newStr);
+ std::copy(str, str + n2, newStr + pos1);
+ std::copy(rawStr + pos1 + n1, rawStr + oldLen + 1, newStr + pos1 + n2); //include null-termination
+
+ destroy(rawStr);
+ rawStr = newStr;
+ }
+ return *this;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP>& Zbase<T, SP, AP>::Replace(const T* old, const T* replacement, bool replaceAll)
+{
+ const size_t oldLen = z_impl::cStringLength(old);
+ const size_t replacementLen = z_impl::cStringLength(replacement);
+
+ size_t pos = 0;
+ while ((pos = find(old, pos)) != npos)
+ {
+ replace(pos, oldLen, replacement, replacementLen);
+ pos += replacementLen; //move past the string that was replaced
+
+ if (!replaceAll)
+ break;
+ }
+ return *this;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+void Zbase<T, SP, AP>::resize(size_t newSize, T fillChar)
+{
+ if (canWrite(rawStr, newSize))
+ {
+ if (length() < newSize)
+ std::fill(rawStr + length(), rawStr + newSize, fillChar);
+ rawStr[newSize] = 0;
+ setLength(rawStr, newSize); //keep after call to length()
+ }
+ else
+ {
+ T* newStr = this->create(newSize);
+ newStr[newSize] = 0;
+
+ if (length() < newSize)
+ {
+ std::copy(rawStr, rawStr + length(), newStr);
+ std::fill(newStr + length(), newStr + newSize, fillChar);
+ }
+ else
+ std::copy(rawStr, rawStr + newSize, newStr);
+
+ destroy(rawStr);
+ rawStr = newStr;
+ }
+}
+
+
+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)
+{
+ 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)
+{
+ return lhs.length() == z_impl::cStringLength(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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ return std::lexicographical_compare(lhs.begin(), lhs.end(), //respect embedded 0
+ rhs, rhs + z_impl::cStringLength(rhs));
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+bool operator<(const T* lhs, const Zbase<T, SP, AP>& rhs)
+{
+ return std::lexicographical_compare(lhs, lhs + z_impl::cStringLength(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
+{
+ return SP<T, AP>::length(rawStr);
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+size_t Zbase<T, 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
+{
+ return rawStr;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+const T* Zbase<T, 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
+{
+ 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
+{
+ return rawStr;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+const T* Zbase<T, SP, AP>::end() const
+{
+ return rawStr + length();
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+T* Zbase<T, SP, AP>::begin()
+{
+ reserve(length());
+ return rawStr;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+T* Zbase<T, SP, AP>::end()
+{
+ return begin() + length();
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+bool Zbase<T, SP, AP>::empty() const
+{
+ return length() == 0;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+void Zbase<T, SP, AP>::clear()
+{
+ if (!empty())
+ {
+ if (canWrite(rawStr, 0))
+ {
+ rawStr[0] = 0; //keep allocated memory
+ setLength(rawStr, 0); //
+ }
+ else
+ *this = Zbase();
+ }
+}
+
+
+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)
+{
+ return Zbase<T, 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)
+{
+ return Zbase<T, 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)
+{
+ return Zbase<T, 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)
+{
+ return Zbase<T, 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)
+{
+ return Zbase<T, 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)
+{
+ std::swap(rawStr, other.rawStr);
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP> Zbase<T, SP, AP>::substr(size_t pos, size_t len) const
+{
+ assert(pos + (len == npos ? 0 : len) <= length());
+ return Zbase(rawStr + pos, len == npos ? length() - pos : len);
+}
+
+
+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
+{
+ if (!canWrite(rawStr, minCapacity))
+ {
+ //allocate a new string
+ T* 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);
+ rawStr = newStr;
+ }
+}
+
+
+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)
+{
+ if (canWrite(rawStr, len))
+ {
+ std::copy(source, source + len, rawStr);
+ rawStr[len] = 0; //include null-termination
+ setLength(rawStr, len);
+ }
+ else
+ *this = Zbase(source, len);
+
+ return *this;
+}
+
+
+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)
+{
+ Zbase(source).swap(*this);
+ 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)
+{
+ return assign(source, z_impl::cStringLength(source));
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator=(T source)
+{
+ if (canWrite(rawStr, 1))
+ {
+ rawStr[0] = source;
+ rawStr[1] = 0; //include null-termination
+ setLength(rawStr, 1);
+ }
+ else
+ *this = Zbase(source);
+
+ return *this;
+}
+
+
+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)
+{
+ 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;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator+=(const T* other)
+{
+ const size_t thisLen = length();
+ const size_t otherLen = z_impl::cStringLength(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;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
+Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator+=(T 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;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+template <class N>
+inline
+Zbase<T, SP, AP> Zbase<T, SP, AP>::fromNumber(N number)
+{
+ std::basic_ostringstream<T> ss;
+ ss << number;
+ return Zbase<T, SP, AP>(ss.str().c_str());
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+template <class N>
+inline
+N Zbase<T, SP, AP>::toNumber() const
+{
+ std::basic_istringstream<T> ss(std::basic_string<T>(rawStr));
+ T number = 0;
+ ss >> number;
+ return number;
+}
+
+
+namespace z_impl
+{
+template <class T>
+bool cStringWhiteSpace(T ch);
+
+template <>
+inline
+bool cStringWhiteSpace(char ch)
+{
+ return std::isspace(static_cast<unsigned char>(ch)) != 0; //some compilers (e.g. VC++ 6.0) return true for a call to isspace('\xEA'); but no issue with newer versions of MSVC
+}
+
+
+template <>
+inline
+bool cStringWhiteSpace(wchar_t ch)
+{
+ return std::iswspace(ch) != 0; //some compilers (e.g. VC++ 6.0) return true for a call to isspace('\xEA'); but no issue with newer versions of MSVC
+}
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+Zbase<T, SP, AP>& Zbase<T, SP, AP>::Trim(bool fromLeft, bool fromRight)
+{
+ assert(fromLeft || fromRight);
+
+ const T* newBegin = rawStr;
+ const T* newEnd = rawStr + length();
+
+ if (fromRight)
+ while (newBegin != newEnd && z_impl::cStringWhiteSpace(newEnd[-1]))
+ --newEnd;
+
+ if (fromLeft)
+ while (newBegin != newEnd && z_impl::cStringWhiteSpace(*newBegin))
+ ++newBegin;
+
+ const size_t newLength = newEnd - newBegin;
+ if (newLength != length())
+ {
+ if (canWrite(rawStr, newLength))
+ {
+ std::copy(newBegin, newBegin + newLength, rawStr); //shift left => std::copy, shift right std::copy_backward
+ rawStr[newLength] = 0;
+ setLength(rawStr, newLength);
+ }
+ else
+ *this = Zbase(newBegin, newLength);
+ }
+ return *this;
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+std::vector<Zbase<T, SP, AP> > Zbase<T, SP, AP>::Split(T delimiter) const
+{
+ std::vector<Zbase> output;
+
+ const size_t thisLen = length();
+ size_t startPos = 0;
+ for (;;) //make MSVC happy
+ {
+ size_t endPos = find(delimiter, startPos);
+ if (endPos == npos)
+ endPos = thisLen;
+
+ if (startPos != endPos) //do not add empty strings
+ {
+ Zbase newEntry = substr(startPos, endPos - startPos);
+ newEntry.Trim(); //remove whitespace characters
+
+ if (!newEntry.empty())
+ output.push_back(newEntry);
+ }
+ if (endPos == thisLen)
+ break;
+
+ startPos = endPos + 1; //skip delimiter
+ }
+
+ return output;
+}
+
+
+#endif //Z_BASE_H_INCLUDED
diff --git a/shared/zstring.cpp b/shared/zstring.cpp
index 39f1898d..1a05831d 100644
--- a/shared/zstring.cpp
+++ b/shared/zstring.cpp
@@ -9,56 +9,79 @@
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
+#undef min
+#undef max
#include "dll_loader.h"
#include <boost/scoped_array.hpp>
#endif //FFS_WIN
#ifndef NDEBUG
#include <wx/string.h>
+#include <iostream>
+#include <cstdlib>
#endif
#ifndef NDEBUG
-AllocationCount::~AllocationCount()
+LeakChecker::~LeakChecker()
{
if (activeStrings.size() > 0)
-#ifdef FFS_WIN
{
int rowCount = 0;
- wxString leakingStrings;
- for (std::set<const DefaultChar*>::const_iterator i = activeStrings.begin();
- i != activeStrings.end() && ++rowCount <= 10;
+ std::string leakingStrings;
+
+ for (VoidPtrSizeMap::const_iterator i = activeStrings.begin();
+ i != activeStrings.end() && ++rowCount <= 20;
++i)
- {
- leakingStrings += wxT("\"");
- leakingStrings += *i;
- leakingStrings += wxT("\"\n");
- }
+ leakingStrings += "\"" + rawMemToString(i->first, i->second) + "\"\n";
- MessageBox(NULL, wxString(wxT("Memory leak detected!")) + wxT("\n\n")
- + wxT("Candidates:\n") + leakingStrings,
- wxString::Format(wxT("%u"), activeStrings.size()), 0);
- }
+ const std::string message = std::string("Memory leak detected!") + "\n\n"
+ + "Candidates:\n" + leakingStrings;
+#ifdef FFS_WIN
+ MessageBoxA(NULL, message.c_str(), "Error", 0);
#else
- throw std::logic_error("Memory leak!");
+ std::cerr << message;
+ std::abort();
#endif
+ }
}
-AllocationCount& AllocationCount::getInstance()
+LeakChecker& LeakChecker::instance()
{
- static AllocationCount global;
- return global;
+ static LeakChecker inst;
+ return inst;
+}
+
+
+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
+ if (output.size() > 100)
+ output.resize(100);
+ return output;
+}
+
+
+void LeakChecker::reportProblem(const std::string& message) //throw (std::logic_error)
+{
+#ifdef FFS_WIN
+ ::MessageBoxA(NULL, message.c_str(), "Error", 0);
+#else
+ std::cerr << message;
+#endif
+ throw std::logic_error("Memory leak! " + message);
}
#endif //NDEBUG
+
#ifdef FFS_WIN
namespace
{
bool hasInvariantLocale()
{
- OSVERSIONINFO osvi;
- ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ OSVERSIONINFO osvi = {};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
//invariant locale has been introduced with XP
@@ -75,13 +98,13 @@ bool hasInvariantLocale()
//warning: LOCALE_INVARIANT is NOT available with Windows 2000, so we have to make yet another distinction...
-const LCID invariantLocale = hasInvariantLocale() ?
- LOCALE_INVARIANT :
- MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); //see: http://msdn.microsoft.com/en-us/goglobal/bb688122.aspx
+const LCID ZSTRING_INVARIANT_LOCALE = hasInvariantLocale() ?
+ LOCALE_INVARIANT :
+ MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); //see: http://msdn.microsoft.com/en-us/goglobal/bb688122.aspx
+}
-inline
-int compareFilenamesWin32(const wchar_t* a, const wchar_t* b, size_t sizeA, size_t sizeB)
+int z_impl::compareFilenamesWin(const wchar_t* a, const wchar_t* b, size_t sizeA, size_t sizeB)
{
//try to call "CompareStringOrdinal" for low-level string comparison: unfortunately available not before Windows Vista!
//by a factor ~3 faster than old string comparison using "LCMapString"
@@ -91,16 +114,15 @@ int compareFilenamesWin32(const wchar_t* a, const wchar_t* b, size_t sizeA, size
LPCWSTR lpString2,
int cchCount2,
BOOL bIgnoreCase);
- static const CompareStringOrdinalFunc ordinalCompare = util::loadDllFunction<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal");
+ static const CompareStringOrdinalFunc ordinalCompare = util::getDllFun<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal");
if (ordinalCompare != NULL) //this additional test has no noticeable performance impact
{
- const int rv = (*ordinalCompare)(
- a, //pointer to first string
- static_cast<int>(sizeA), //size, in bytes or characters, of first string
- b, //pointer to second string
- static_cast<int>(sizeB), //size, in bytes or characters, of second string
- true); //ignore case
+ const int rv = ordinalCompare(a, //pointer to first string
+ static_cast<int>(sizeA), //size, in bytes or characters, of first string
+ b, //pointer to second string
+ static_cast<int>(sizeB), //size, in bytes or characters, of second string
+ true); //ignore case
if (rv == 0)
throw std::runtime_error("Error comparing strings (ordinal)!");
else
@@ -114,7 +136,7 @@ int compareFilenamesWin32(const wchar_t* a, const wchar_t* b, size_t sizeA, size
const size_t minSize = std::min(sizeA, sizeB);
if (minSize == 0) //LCMapString does not allow input sizes of 0!
- return static_cast<int>(sizeA - sizeB);
+ return static_cast<int>(sizeA) - static_cast<int>(sizeB);
int rv = 0; //always initialize...
if (minSize <= 5000) //performance optimization: stack
@@ -123,7 +145,7 @@ int compareFilenamesWin32(const wchar_t* a, const wchar_t* b, size_t sizeA, size
wchar_t bufferB[5000];
if (::LCMapString( //faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString()
- invariantLocale, //__in LCID Locale,
+ ZSTRING_INVARIANT_LOCALE, //__in LCID Locale,
LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
a, //__in LPCTSTR lpSrcStr,
static_cast<int>(minSize), //__in int cchSrc,
@@ -132,7 +154,7 @@ int compareFilenamesWin32(const wchar_t* a, const wchar_t* b, size_t sizeA, size
) == 0)
throw std::runtime_error("Error comparing strings! (LCMapString)");
- if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, b, static_cast<int>(minSize), bufferB, 5000) == 0)
+ if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, b, static_cast<int>(minSize), bufferB, 5000) == 0)
throw std::runtime_error("Error comparing strings! (LCMapString)");
rv = ::wmemcmp(bufferA, bufferB, minSize);
@@ -142,17 +164,17 @@ int compareFilenamesWin32(const wchar_t* a, const wchar_t* b, size_t sizeA, size
boost::scoped_array<wchar_t> bufferA(new wchar_t[minSize]);
boost::scoped_array<wchar_t> bufferB(new wchar_t[minSize]);
- if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, a, static_cast<int>(minSize), bufferA.get(), static_cast<int>(minSize)) == 0)
+ if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, a, static_cast<int>(minSize), bufferA.get(), static_cast<int>(minSize)) == 0)
throw std::runtime_error("Error comparing strings! (LCMapString: FS)");
- if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, b, static_cast<int>(minSize), bufferB.get(), static_cast<int>(minSize)) == 0)
+ if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, b, static_cast<int>(minSize), bufferB.get(), static_cast<int>(minSize)) == 0)
throw std::runtime_error("Error comparing strings! (LCMapString: FS)");
rv = ::wmemcmp(bufferA.get(), bufferB.get(), minSize);
}
return rv == 0 ?
- static_cast<int>(sizeA - sizeB) :
+ static_cast<int>(sizeA) - static_cast<int>(sizeB) :
rv;
}
@@ -169,429 +191,50 @@ int compareFilenamesWin32(const wchar_t* a, const wchar_t* b, size_t sizeA, size
// else
// return rv - 2; //convert to C-style string compare result
}
-}
-#endif
-
-
-int cmpFileName(const Zstring& lhs, const Zstring& rhs)
-{
-#ifdef FFS_WIN
- return ::compareFilenamesWin32(lhs.c_str(), rhs.c_str(), lhs.length(), rhs.length()); //way faster than wxString::CmpNoCase()
-#elif defined FFS_LINUX
- return ::strcmp(lhs.c_str(), rhs.c_str());
-#endif
-}
-
-
-int cmpFileName(const Zstring& lhs, const DefaultChar* rhs)
-{
-#ifdef FFS_WIN
- return ::compareFilenamesWin32(lhs.c_str(), rhs, lhs.length(), ::wcslen(rhs)); //way faster than wxString::CmpNoCase()
-#elif defined FFS_LINUX
-return ::strcmp(lhs.c_str(), rhs);
-#endif
-}
-
-
-int cmpFileName(const DefaultChar* lhs, const DefaultChar* rhs)
-{
-#ifdef FFS_WIN
- return ::compareFilenamesWin32(lhs, rhs, ::wcslen(lhs), ::wcslen(rhs)); //way faster than wxString::CmpNoCase()
-#elif defined FFS_LINUX
- return ::strcmp(lhs, rhs);
-#endif
-}
-
-
-Zstring& Zstring::Replace(const DefaultChar* old, const DefaultChar* replacement, bool replaceAll)
-{
- const size_t oldLen = defaultLength(old);
- const size_t replacementLen = defaultLength(replacement);
-
- size_t pos = 0;
- for (;;)
- {
- pos = find(old, pos);
- if (pos == npos)
- break;
-
- replace(pos, oldLen, replacement, replacementLen);
- pos += replacementLen; //move past the string that was replaced
-
- // stop now?
- if (!replaceAll)
- break;
- }
- return *this;
-}
-
-
-bool Zstring::matchesHelper(const DefaultChar* string, const DefaultChar* mask)
-{
- for (DefaultChar ch; (ch = *mask) != 0; ++mask, ++string)
- {
- switch (ch)
- {
- case DefaultChar('?'):
- if (*string == 0)
- return false;
- break;
-
- case DefaultChar('*'):
- //advance to next non-*/? char
- do
- {
- ++mask;
- ch = *mask;
- }
- while (ch == DefaultChar('*') || ch == DefaultChar('?'));
- //if match ends with '*':
- if (ch == 0)
- return true;
-
- ++mask;
- while ((string = defaultStrFind(string, ch)) != NULL)
- {
- ++string;
- if (matchesHelper(string, mask))
- return true;
- }
- return false;
-
- default:
- if (*string != ch)
- return false;
- }
- }
- return *string == 0;
-}
-
-
-bool Zstring::Matches(const DefaultChar* mask) const
-{
- return matchesHelper(c_str(), mask);
-}
-
-
-bool Zstring::Matches(const DefaultChar* name, const DefaultChar* mask)
-{
- return matchesHelper(name, mask);
-}
-
-
-namespace
-{
-#ifdef ZSTRING_CHAR
-inline
-bool defaultIsWhiteSpace(char ch)
-{
- // some compilers (e.g. VC++ 6.0) return true for a call to isspace('\xEA') => exclude char(128) to char(255)
- return (static_cast<unsigned char>(ch) < 128) && isspace(static_cast<unsigned char>(ch)) != 0;
-}
-
-#elif defined ZSTRING_WIDE_CHAR
-inline
-bool defaultIsWhiteSpace(wchar_t ch)
-{
- // some compilers (e.g. VC++ 6.0) return true for a call to isspace('\xEA') => exclude char(128) to char(255)
- return (ch < 128 || ch > 255) && iswspace(ch) != 0;
-}
-#endif
-}
-
-
-Zstring& Zstring::Trim(bool fromRight)
-{
- const size_t thisLen = length();
- if (thisLen == 0)
- return *this;
-
- DefaultChar* const strBegin = data();
-
- if (fromRight)
- {
- const DefaultChar* cursor = strBegin + thisLen - 1;
- while (cursor != strBegin - 1 && defaultIsWhiteSpace(*cursor)) //break when pointing one char further than last skipped element
- --cursor;
- ++cursor;
-
- const size_t newLength = cursor - strBegin;
- if (newLength != thisLen)
- {
- if (descr->refCount > 1) //allocate new string
- *this = Zstring(strBegin, newLength);
- else //overwrite this string
- {
- descr->length = newLength;
- strBegin[newLength] = 0;
- }
- }
- }
- else
- {
- const DefaultChar* cursor = strBegin;
- const DefaultChar* const strEnd = strBegin + thisLen;
- while (cursor != strEnd && defaultIsWhiteSpace(*cursor))
- ++cursor;
-
- const size_t diff = cursor - strBegin;
- if (diff)
- {
- if (descr->refCount > 1) //allocate new string
- *this = Zstring(cursor, thisLen - diff);
- else
- {
- //overwrite this string
- ::memmove(strBegin, cursor, (thisLen - diff + 1) * sizeof(DefaultChar)); //note: do not simply let data point to different location: this corrupts reserve()!
- descr->length -= diff;
- }
- }
- }
-
- return *this;
-}
-
-
-std::vector<Zstring> Zstring::Tokenize(const DefaultChar delimiter) const
-{
- std::vector<Zstring> output;
-
- const size_t thisLen = length();
- size_t indexStart = 0;
- for (;;)
- {
- size_t indexEnd = find(delimiter, indexStart);
- if (indexEnd == Zstring::npos)
- indexEnd = thisLen;
-
- if (indexStart != indexEnd) //do not add empty strings
- {
- Zstring newEntry = substr(indexStart, indexEnd - indexStart);
- newEntry.Trim(true); //remove whitespace characters from right
- newEntry.Trim(false); //remove whitespace characters from left
-
- if (!newEntry.empty())
- output.push_back(newEntry);
- }
- if (indexEnd == thisLen)
- break;
-
- indexStart = indexEnd + 1; //delimiter is a single character
- }
-
- return output;
-}
-#ifdef FFS_WIN
-Zstring& Zstring::MakeUpper()
+void z_impl::makeUpperCaseWin(wchar_t* str, size_t size)
{
- const size_t thisLen = length();
- if (thisLen == 0)
- return *this;
-
- reserve(thisLen); //make unshared
+ if (size == 0) //LCMapString does not allow input sizes of 0!
+ return;
//use Windows' upper case conversion: faster than ::CharUpper()
- if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, data(), static_cast<int>(thisLen), data(), static_cast<int>(thisLen)) == 0)
+ if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, str, static_cast<int>(size), str, static_cast<int>(size)) == 0)
throw std::runtime_error("Error converting to upper case! (LCMapString)");
-
- return *this;
-}
-#endif
-
-
-//###############################################################
-//std::string functions
-Zstring Zstring::substr(size_t pos, size_t len) const
-{
- if (len == npos)
- {
- assert(pos <= length());
- return Zstring(c_str() + pos, length() - pos); //reference counting not used: different length
- }
- else
- {
- assert(length() - pos >= len);
- return Zstring(c_str() + pos, len);
- }
}
-size_t Zstring::rfind(DefaultChar ch, size_t pos) const
-{
- const size_t thisLen = length();
- if (thisLen == 0)
- return npos;
-
- if (pos == npos)
- pos = thisLen - 1;
- else
- assert(pos <= length());
-
- do //pos points to last char of the string
- {
- if (c_str()[pos] == ch)
- return pos;
- }
- while (--pos != static_cast<size_t>(-1));
-
- return npos;
-}
-
-
-Zstring& Zstring::replace(size_t pos1, size_t n1, const DefaultChar* str, size_t n2)
-{
- assert(str < c_str() || c_str() + length() < str); //str mustn't point to data in this string
- assert(n1 <= length() - pos1);
-
- const size_t oldLen = length();
- if (oldLen == 0)
- {
- assert(pos1 == 0 && n1 == 0);
- return *this = Zstring(str, n2);
- }
-
- const size_t newLen = oldLen - n1 + n2;
- if (newLen > oldLen || descr->refCount > 1)
- {
- //allocate a new string
- StringDescriptor* newDescr = allocate(newLen);
-
- //assemble new string with replacement
- DefaultChar* const newData = reinterpret_cast<DefaultChar*>(newDescr + 1);
- ::memcpy(newData, c_str(), pos1 * sizeof(DefaultChar));
- ::memcpy(newData + pos1, str, n2 * sizeof(DefaultChar));
- ::memcpy(newData + pos1 + n2, c_str() + pos1 + n1, (oldLen - pos1 - n1) * sizeof(DefaultChar));
- newData[newLen] = 0;
-
- decRef();
- descr = newDescr;
- }
- else //overwrite current string: case "n2 == 0" is handled implicitly
- {
- ::memcpy(data() + pos1, str, n2 * sizeof(DefaultChar));
- if (newLen < oldLen)
- {
- ::memmove(data() + pos1 + n2, data() + pos1 + n1, (oldLen - pos1 - n1) * sizeof(DefaultChar));
- data()[newLen] = 0;
- descr->length = newLen;
- }
- }
-
- return *this;
-}
-
-
-Zstring& Zstring::operator=(const DefaultChar* source)
-{
- const size_t sourceLen = defaultLength(source);
-
- if (descr->refCount > 1 || descr->capacity < sourceLen) //allocate new string
- *this = Zstring(source, sourceLen);
- else
- {
- //overwrite this string
- ::memcpy(data(), source, (sourceLen + 1) * sizeof(DefaultChar)); //include null-termination
- descr->length = sourceLen;
- }
- return *this;
-}
-
-
-Zstring& Zstring::assign(const DefaultChar* source, size_t len)
-{
- if (descr->refCount > 1 || descr->capacity < len) //allocate new string
- *this = Zstring(source, len);
- else
- {
- //overwrite this string
- ::memcpy(data(), source, len * sizeof(DefaultChar)); //don't know if source is null-terminated
- data()[len] = 0; //include null-termination
- descr->length = len;
- }
- return *this;
-}
-
-
-Zstring& Zstring::operator+=(const Zstring& other)
-{
- const size_t otherLen = other.length();
- if (otherLen != 0)
- {
- const size_t thisLen = length();
- const size_t newLen = thisLen + otherLen;
- reserve(newLen); //make unshared and check capacity
-
- ::memcpy(data() + thisLen, other.c_str(), (otherLen + 1) * sizeof(DefaultChar)); //include null-termination
- descr->length = newLen;
- }
- return *this;
-}
-
-
-Zstring& Zstring::operator+=(const DefaultChar* other)
-{
- const size_t otherLen = defaultLength(other);
- if (otherLen != 0)
- {
- const size_t thisLen = length();
- const size_t newLen = thisLen + otherLen;
- reserve(newLen); //make unshared and check capacity
-
- ::memcpy(data() + thisLen, other, (otherLen + 1) * sizeof(DefaultChar)); //include NULL-termination
- descr->length = newLen;
- }
- return *this;
-}
-
-
-Zstring& Zstring::operator+=(DefaultChar ch)
-{
- const size_t oldLen = length();
- reserve(oldLen + 1); //make unshared and check capacity
- data()[oldLen] = ch;
- data()[oldLen + 1] = 0;
- ++descr->length;
- return *this;
-}
-
-
-void Zstring::reserve(size_t capacityNeeded) //make unshared and check capacity
-{
- assert(capacityNeeded != 0);
-
- if (descr->refCount > 1)
- {
- //allocate a new string
- const size_t oldLength = length();
-
- StringDescriptor* newDescr = allocate(std::max(capacityNeeded, oldLength)); //reserve() must NEVER shrink the string
- newDescr->length = oldLength;
-
- ::memcpy(reinterpret_cast<DefaultChar*>(newDescr + 1), c_str(), (oldLength + 1) * sizeof(DefaultChar)); //include NULL-termination
-
- decRef();
- descr = newDescr;
- }
- else if (descr->capacity < capacityNeeded)
- {
- //try to resize the current string (allocate anew if necessary)
- const size_t newCapacity = getCapacityToAllocate(capacityNeeded);
-
-#ifndef NDEBUG
- AllocationCount::getInstance().dec(c_str()); //test Zstring for memory leaks
-#endif
-
- descr = static_cast<StringDescriptor*>(::realloc(descr, sizeof(StringDescriptor) + (newCapacity + 1) * sizeof(DefaultChar)));
- if (descr == NULL)
- throw std::bad_alloc();
-
-#ifndef NDEBUG
- AllocationCount::getInstance().inc(c_str()); //test Zstring for memory leaks
-#endif
-
- descr->capacity = newCapacity;
- }
-}
-
+/*
+#include <fstream>
+ extern std::wofstream statShared;
+extern std::wofstream statLowCapacity;
+extern std::wofstream statOverwrite;
+
+std::wstring p(ptr);
+p.erase(std::remove(p.begin(), p.end(), L'\n'), p.end());
+p.erase(std::remove(p.begin(), p.end(), L','), p.end());
+
+ if (descr(ptr)->refCount > 1)
+ statShared <<
+ minCapacity << L"," <<
+ descr(ptr)->refCount << L"," <<
+ descr(ptr)->length << L"," <<
+ descr(ptr)->capacity << L"," <<
+ p << L"\n";
+else if (minCapacity > descr(ptr)->capacity)
+ statLowCapacity <<
+ minCapacity << L"," <<
+ descr(ptr)->refCount << L"," <<
+ descr(ptr)->length << L"," <<
+ descr(ptr)->capacity << L"," <<
+ p << L"\n";
+else
+ statOverwrite <<
+ minCapacity << L"," <<
+ descr(ptr)->refCount << L"," <<
+ descr(ptr)->length << L"," <<
+ descr(ptr)->capacity << L"," <<
+ p << L"\n";
+*/
+
+#endif //FFS_WIN
diff --git a/shared/zstring.h b/shared/zstring.h
index 7b993fd0..a015dbe4 100644
--- a/shared/zstring.h
+++ b/shared/zstring.h
@@ -7,797 +7,206 @@
#ifndef ZSTRING_H_INCLUDED
#define ZSTRING_H_INCLUDED
-#include <cstring> //size_t, memcpy(), memcmp()
-#include <cstdlib> //malloc(), free()
-#include <cassert>
-#include <vector>
-#include <sstream>
-#include <algorithm> //specialize std::swap
-#include <functional>
+#include "zbase.h"
+#include <cstring> //strcmp()
#ifndef NDEBUG
-#include <set>
+#include <map>
#include <wx/thread.h>
#endif
-#ifdef ZSTRING_CHAR
-typedef char DefaultChar; //use char strings
-#define DefaultStr(x) x //
-#elif defined ZSTRING_WIDE_CHAR
-typedef wchar_t DefaultChar; //use wide character strings
-#define DefaultStr(x) L ## x //
-#endif
-
-
-class Zstring
-{
-public:
- Zstring();
- Zstring(const DefaultChar* source); //string is copied: O(length)
- Zstring(const DefaultChar* source, size_t length); //string is copied: O(length)
- Zstring(const Zstring& source); //reference-counting => O(1)
- ~Zstring();
-
- operator const DefaultChar*() const; //implicit conversion to C string
-
- //wxWidgets-like functions
- bool StartsWith(const DefaultChar* begin) const;
- bool StartsWith(DefaultChar begin) const;
- bool StartsWith(const Zstring& begin) const;
- bool EndsWith(const DefaultChar* end) const;
- bool EndsWith(const DefaultChar end) const;
- bool EndsWith(const Zstring& end) const;
- Zstring& Truncate(size_t newLen);
- Zstring& Replace(const DefaultChar* old, const DefaultChar* replacement, bool replaceAll = true);
- Zstring AfterLast( DefaultChar ch) const; //returns the whole string if ch not found
- Zstring BeforeLast( DefaultChar ch) const; //returns empty string if ch not found
- Zstring AfterFirst( DefaultChar ch) const; //returns empty string if ch not found
- Zstring BeforeFirst(DefaultChar ch) const; //returns the whole string if ch not found
- size_t Find(DefaultChar ch, bool fromEnd = false) const; //returns npos if not found
- bool Matches(const DefaultChar* mask) const;
- static bool Matches(const DefaultChar* name, const DefaultChar* mask);
- Zstring& Trim(bool fromRight); //from right or left
- std::vector<Zstring> Tokenize(const DefaultChar delimiter) const;
-#ifdef FFS_WIN
- Zstring& MakeUpper();
-#endif
-
- //std::string functions
- size_t length() const;
- const DefaultChar* c_str() const;
- Zstring substr(size_t pos = 0, size_t len = npos) const; //allocate new string
- bool empty() const;
- void clear();
- int compare(size_t pos1, size_t n1, const DefaultChar* other) const;
- size_t find(const DefaultChar* str, size_t pos = 0 ) const;
- size_t find(DefaultChar ch, size_t pos = 0) const;
- size_t rfind(DefaultChar ch, size_t pos = npos) const;
- Zstring& replace(size_t pos1, size_t n1, const DefaultChar* str, size_t n2);
- size_t size() const;
- void reserve(size_t minCapacity);
- Zstring& assign(const DefaultChar* source, size_t len);
- void resize(size_t newSize, DefaultChar fillChar = 0 );
-
- Zstring& operator=(const Zstring& source);
- Zstring& operator=(const DefaultChar* source);
-
-
- friend bool operator==(const Zstring& lhs, const Zstring& rhs);
- friend bool operator==(const Zstring& lhs, const DefaultChar* rhs);
- friend bool operator==(const DefaultChar* lhs, const Zstring& rhs);
-
- friend bool operator< (const Zstring& lhs, const Zstring& rhs);
- friend bool operator< (const Zstring& lhs, const DefaultChar* rhs);
- friend bool operator< (const DefaultChar* lhs, const Zstring& rhs);
-
- friend bool operator!=(const Zstring& lhs, const Zstring& rhs);
- friend bool operator!=(const Zstring& lhs, const DefaultChar* rhs);
- friend bool operator!=(const DefaultChar* lhs, const Zstring& rhs);
-
- void swap(Zstring& other);
-
- const DefaultChar operator[](size_t pos) const;
-
- Zstring& operator+=(const Zstring& other);
- Zstring& operator+=(const DefaultChar* other);
- Zstring& operator+=(DefaultChar ch);
-
- static const size_t npos = static_cast<size_t>(-1);
-
-private:
- Zstring(int); //detect usage errors
-
- DefaultChar* data();
-
- void initAndCopy(const DefaultChar* source, size_t length);
- void incRef() const; //support for reference-counting
- void decRef(); //
-
- //helper methods
- static size_t defaultLength (const DefaultChar* input); //strlen()
- static int defaultCompare(const DefaultChar* str1, const DefaultChar* str2); //strcmp()
- static int defaultCompare(const DefaultChar* str1, const DefaultChar* str2, size_t count); //strncmp()
- static const DefaultChar* defaultStrFind(const DefaultChar* str1, DefaultChar ch); //strchr()
- static const DefaultChar* defaultStrFind(const DefaultChar* str1, const DefaultChar* str1End, const DefaultChar* str2); //"analog" to strstr()
- static bool matchesHelper(const DefaultChar* string, const DefaultChar* mask);
-
- struct StringDescriptor
- {
- mutable unsigned int refCount;
- size_t length;
- size_t capacity; //allocated length without null-termination
- };
- static StringDescriptor* allocate(const size_t newLength);
-
- StringDescriptor* descr;
-};
-
-
-const Zstring operator+(const Zstring& lhs, const Zstring& rhs);
-const Zstring operator+(const Zstring& lhs, const DefaultChar* rhs);
-const Zstring operator+(const DefaultChar* lhs, const Zstring& rhs);
-const Zstring operator+(DefaultChar lhs, const Zstring& rhs);
-const Zstring operator+(const Zstring& lhs, DefaultChar rhs);
-
-template <class T>
-Zstring numberToZstring(const T& number); //convert number to Zstring
-
-//Compare filenames: Windows does NOT distinguish between upper/lower-case, while Linux DOES
-int cmpFileName(const Zstring& lhs, const Zstring& rhs);
-int cmpFileName(const Zstring& lhs, const DefaultChar* rhs);
-int cmpFileName(const DefaultChar* lhs, const Zstring& rhs);
-int cmpFileName(const DefaultChar* lhs, const DefaultChar* rhs);
-
-struct LessFilename : public std::binary_function<Zstring, Zstring, bool>//case-insensitive on Windows, case-sensitive on Linux
-{
- bool operator()(const Zstring& a, const Zstring& b) const;
-};
-
-namespace std
-{
-template<>
-inline
-void swap(Zstring& rhs, Zstring& lhs)
-{
- rhs.swap(lhs);
-}
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//#######################################################################################
-//begin of implementation
-
-//-------------standard helper functions ---------------------------------------------------------------
-inline
-size_t Zstring::defaultLength(const DefaultChar* input) //strlen()
-{
- const DefaultChar* const startPos = input;
- while (*input != 0)
- ++input;
-
- return input - startPos;
-}
-
-
-inline
-int Zstring::defaultCompare(const DefaultChar* str1, const DefaultChar* str2) //strcmp()
-{
- while (*str1 == *str2)
- {
- if (*str1 == 0)
- return 0;
- ++str1;
- ++str2;
- }
-
- return *str1 - *str2;
-}
-
-
-inline
-int Zstring::defaultCompare(const DefaultChar* str1, const DefaultChar* str2, size_t count) //strncmp()
-{
- while (count-- != 0)
- {
- if (*str1 != *str2)
- return *str1 - *str2;
-
- if (*str1 == 0)
- return 0;
- ++str1;
- ++str2;
- }
-
- return 0;
-}
-
-
-inline
-const DefaultChar* Zstring::defaultStrFind(const DefaultChar* str1, DefaultChar ch) //strchr()
-{
- while (*str1 != ch) //ch is allowed to be 0 by contract! must return end of string in this case
- {
- if (*str1 == 0)
- return NULL;
-
- ++str1;
- }
-
- return str1;
-}
-
-
-inline
-const DefaultChar* Zstring::defaultStrFind(const DefaultChar* str1, const DefaultChar* str1End, const DefaultChar* str2) //"analog" to strstr()
-{
- const size_t str2Len = defaultLength(str2);
-
- str1End -= str2Len; //no need to process the "last chunk" of str1
- ++str1End; //
-
- while(str1 < str1End) //don't use !=; str1End may be smaller than str1!
- {
- if(::memcmp(str1, str2, str2Len * sizeof(DefaultChar)) == 0)
- return str1;
- ++str1;
- }
- return NULL;
-}
-//--------------------------------------------------------------------------------------------------
-
#ifndef NDEBUG
-class AllocationCount //small test for memory leaks in Zstring
+class LeakChecker //small test for memory leaks
{
public:
- void inc(const DefaultChar* object)
+ void insert(const void* ptr, size_t size)
{
wxCriticalSectionLocker dummy(lockActStrings);
- activeStrings.insert(object);
+ if (activeStrings.find(ptr) != activeStrings.end())
+ reportProblem(std::string("Fatal Error: New memory points into occupied space: ") + rawMemToString(ptr, size));
+
+ activeStrings[ptr] = size;
}
- void dec(const DefaultChar* object)
+ void remove(const void* ptr)
{
wxCriticalSectionLocker dummy(lockActStrings);
- activeStrings.erase(object);
+
+ if (activeStrings.find(ptr) == activeStrings.end())
+ reportProblem(std::string("Fatal Error: No memory available for deallocation at this location!"));
+
+ activeStrings.erase(ptr);
}
- static AllocationCount& getInstance();
+ static LeakChecker& instance();
private:
- AllocationCount() {}
- AllocationCount(const AllocationCount&);
- ~AllocationCount();
+ LeakChecker() {}
+ LeakChecker(const LeakChecker&);
+ LeakChecker& operator=(const LeakChecker&);
+ ~LeakChecker();
+
+ static std::string rawMemToString(const void* ptr, size_t size);
+ void reportProblem(const std::string& message); //throw (std::logic_error)
wxCriticalSection lockActStrings;
- std::set<const DefaultChar*> activeStrings;
+ typedef std::map<const void*, size_t> VoidPtrSizeMap;
+ VoidPtrSizeMap activeStrings;
};
#endif //NDEBUG
-inline
-size_t getCapacityToAllocate(const size_t length)
-{
- return (length + (19 - length % 16)); //allocate some additional length to speed up concatenation
-}
-
-
-inline
-Zstring::StringDescriptor* Zstring::allocate(const size_t newLength)
+class AllocatorFreeStoreChecked
{
- //allocate and set data for new string
- const size_t newCapacity = getCapacityToAllocate(newLength);
- assert(newCapacity);
-
- StringDescriptor* const newDescr = static_cast<StringDescriptor*>(::malloc(sizeof(StringDescriptor) + (newCapacity + 1) * sizeof(DefaultChar))); //use C-memory functions because of realloc()
- if (newDescr == NULL)
- throw std::bad_alloc();
-
- newDescr->refCount = 1;
- newDescr->length = newLength;
- newDescr->capacity = newCapacity;
-
-#ifndef NDEBUG
- AllocationCount::getInstance().inc(reinterpret_cast<DefaultChar*>(newDescr + 1)); //test Zstring for memory leaks
-#endif
- return newDescr;
-}
-
-
-inline
-Zstring::Zstring()
-{
- //static (dummy) empty Zstring
-#ifdef ZSTRING_CHAR
- static Zstring emptyString("");
-#elif defined ZSTRING_WIDE_CHAR
- static Zstring emptyString(L"");
-#endif
-
- emptyString.incRef();
- descr = emptyString.descr;
-}
-
-
-inline
-
-Zstring::Zstring(const DefaultChar* source)
-{
- initAndCopy(source, defaultLength(source));
-}
-
-
-inline
-Zstring::Zstring(const DefaultChar* source, size_t sourceLen)
-{
- initAndCopy(source, sourceLen);
-}
-
-
-inline
-Zstring::Zstring(const Zstring& source)
-{
- descr = source.descr;
- incRef(); //reference counting!
-}
-
-
-inline
-Zstring::~Zstring()
-{
- decRef();
-}
-
-
-inline
-void Zstring::initAndCopy(const DefaultChar* source, size_t sourceLen)
-{
- assert(source);
- descr = allocate(sourceLen);
- ::memcpy(data(), source, sourceLen * sizeof(DefaultChar));
- data()[sourceLen] = 0;
-}
-
-
-inline
-void Zstring::incRef() const
-{
- assert(descr);
- ++descr->refCount;
-}
-
-
-inline
-void Zstring::decRef()
-{
- assert(descr && descr->refCount >= 1); //descr points to the begin of the allocated memory block
- if (--descr->refCount == 0)
+public:
+ static void* allocate(size_t size) //throw (std::bad_alloc)
{
#ifndef NDEBUG
- AllocationCount::getInstance().dec(c_str()); //test Zstring for memory leaks
+ void* newMem = ::operator new(size);
+ LeakChecker::instance().insert(newMem, size); //test Zbase for memory leaks
+ return newMem;
+#else
+ return ::operator new(size);
#endif
- ::free(descr); //beginning of whole memory block
}
-}
-
-inline
-Zstring::operator const DefaultChar*() const
-{
- return c_str();
-}
-
-
-inline
-Zstring& Zstring::operator=(const Zstring& source)
-{
- Zstring(source).swap(*this);
- return *this;
-}
-
-
-inline
-size_t Zstring::Find(DefaultChar ch, bool fromEnd) const
-{
- return fromEnd ?
- rfind(ch, npos) :
- find(ch, 0);
-}
-
-
-// get all characters after the last occurence of ch
-// (returns the whole string if ch not found)
-inline
-Zstring Zstring::AfterLast(DefaultChar ch) const
-{
- const size_t pos = rfind(ch, npos);
- if (pos != npos )
- return Zstring(c_str() + pos + 1, length() - pos - 1);
- else
- return *this;
-}
-
-
-// get all characters before the last occurence of ch
-// (returns empty string if ch not found)
-inline
-Zstring Zstring::BeforeLast(DefaultChar ch) const
-{
- const size_t pos = rfind(ch, npos);
- if (pos != npos)
- return Zstring(c_str(), pos); //data is non-empty string in this context: else ch would not have been found!
- else
- return Zstring();
-}
-
-
-//returns empty string if ch not found
-inline
-Zstring Zstring::AfterFirst(DefaultChar ch) const
-{
- const size_t pos = find(ch, 0);
- if (pos != npos)
- return Zstring(c_str() + pos + 1, length() - pos - 1);
- else
- return Zstring();
-
-}
-
-//returns the whole string if ch not found
-inline
-Zstring Zstring::BeforeFirst(DefaultChar ch) const
-{
- const size_t pos = find(ch, 0);
- if (pos != npos)
- return Zstring(c_str(), pos); //data is non-empty string in this context: else ch would not have been found!
- else
- return *this;
-}
-
-
-inline
-bool Zstring::StartsWith(const DefaultChar* begin) const
-{
- const size_t beginLength = defaultLength(begin);
- if (length() < beginLength)
- return false;
- return compare(0, beginLength, begin) == 0;
-}
-
-
-inline
-bool Zstring::StartsWith(DefaultChar begin) const
-{
- const size_t len = length();
- return len && (this->operator[](0) == begin);
-}
-
-
-inline
-bool Zstring::StartsWith(const Zstring& begin) const
-{
- const size_t beginLength = begin.length();
- if (length() < beginLength)
- return false;
- return compare(0, beginLength, begin.c_str()) == 0;
-}
-
-
-inline
-bool Zstring::EndsWith(const DefaultChar* end) const
-{
- const size_t thisLength = length();
- const size_t endLength = defaultLength(end);
- if (thisLength < endLength)
- return false;
- return compare(thisLength - endLength, endLength, end) == 0;
-}
-
-
-inline
-bool Zstring::EndsWith(const DefaultChar end) const
-{
- const size_t len = length();
- return len && (this->operator[](len - 1) == end);
-}
-
-
-inline
-bool Zstring::EndsWith(const Zstring& end) const
-{
- const size_t thisLength = length();
- const size_t endLength = end.length();
- if (thisLength < endLength)
- return false;
- return compare(thisLength - endLength, endLength, end.c_str()) == 0;
-}
-
-
-inline
-Zstring& Zstring::Truncate(size_t newLen)
-{
- if (newLen < length())
+ static void deallocate(void* ptr)
{
- if (descr->refCount > 1) //allocate new string
- return *this = Zstring(c_str(), newLen);
- else //overwrite this string
- {
- descr->length = newLen;
- data()[newLen] = 0;
- }
+#ifndef NDEBUG
+ LeakChecker::instance().remove(ptr); //check for memory leaks
+#endif
+ ::operator delete(ptr);
}
+};
- return *this;
-}
-
-
-inline
-size_t Zstring::find(const DefaultChar* str, size_t pos) const
-{
- assert(pos <= length());
- const DefaultChar* const found = defaultStrFind(c_str() + pos, c_str() + length(), str);
- return found == NULL ? npos : found - c_str();
-}
-
-
-inline
-size_t Zstring::find(DefaultChar ch, size_t pos) const
-{
- assert(pos <= length());
- const DefaultChar* thisStr = c_str();
- const DefaultChar* found = defaultStrFind(thisStr + pos, ch);
- return found == NULL ? npos : found - thisStr;
-}
-
-
-inline
-bool operator==(const Zstring& lhs, const Zstring& rhs)
-{
- return lhs.length() != rhs.length() ? false : Zstring::defaultCompare(lhs.c_str(), rhs.c_str()) == 0; //memcmp() offers no better performance here...
-}
-
-
-inline
-bool operator==(const Zstring& lhs, const DefaultChar* rhs)
-{
- return Zstring::defaultCompare(lhs.c_str(), rhs) == 0; //overload using strcmp(char*, char*) should be fastest!
-}
+//############################## 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);
-inline
-bool operator==(const DefaultChar* lhs, const Zstring& rhs)
+struct LessFilename //case-insensitive on Windows, case-sensitive on Linux
{
- return operator==(rhs, lhs);
-}
-
+ 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;
+};
-inline
-bool operator<(const Zstring& lhs, const Zstring& rhs)
+struct EqualFilename //case-insensitive on Windows, case-sensitive on Linux
{
- return Zstring::defaultCompare(lhs.c_str(), rhs.c_str()) < 0;
-}
+ 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;
+};
+#endif
+#ifdef FFS_WIN
+template <template <class, class> class SP, class AP>
+void MakeUpper(Zbase<wchar_t, SP, AP>& str);
+#endif
-inline
-bool operator<(const Zstring& lhs, const DefaultChar* rhs)
-{
- return Zstring::defaultCompare(lhs.c_str(), rhs) < 0;
-}
+#ifdef FFS_WIN //Windows stores filenames in wide character format
+typedef wchar_t Zchar;
+#define Zstr(x) L ## x
-inline
-bool operator<(const DefaultChar* lhs, const Zstring& rhs)
-{
- return Zstring::defaultCompare(lhs, rhs.c_str()) < 0;
-}
+#elif defined FFS_LINUX //Linux uses UTF-8
+typedef char Zchar;
+#define Zstr(x) x
+#endif
+//"The reason for all the fuss above" (Loki/SmartPtr)
+typedef Zbase<Zchar, StorageRefCount, AllocatorFreeStoreChecked> Zstring;
-inline
-bool operator!=(const Zstring& lhs, const Zstring& rhs)
-{
- return !operator==(lhs, rhs);
-}
-inline
-bool operator!=(const Zstring& lhs, const DefaultChar* rhs)
-{
- return !operator==(lhs, rhs);
-}
-inline
-bool operator!=(const DefaultChar* lhs, const Zstring& rhs)
-{
- return !operator==(lhs, rhs);
-}
-inline
-int Zstring::compare(size_t pos1, size_t n1, const DefaultChar* other) const
-{
- assert(n1 <= length() - pos1);
- return defaultCompare(c_str() + pos1, other, n1);
-}
-inline
-size_t Zstring::length() const
-{
- return descr->length;
-}
-inline
-size_t Zstring::size() const
-{
- return descr->length;
-}
-inline
-const DefaultChar* Zstring::c_str() const
-{
- return reinterpret_cast<DefaultChar*>(descr + 1);
-}
-inline
-DefaultChar* Zstring::data()
-{
- return reinterpret_cast<DefaultChar*>(descr + 1);
-}
-inline
-bool Zstring::empty() const
-{
- return descr->length == 0;
-}
-inline
-void Zstring::clear()
-{
- *this = Zstring();
-}
-inline
-const DefaultChar Zstring::operator[](const size_t pos) const
-{
- assert(pos < length());
- return c_str()[pos];
-}
-inline
-const Zstring operator+(const Zstring& lhs, const Zstring& rhs)
-{
- return Zstring(lhs) += rhs;
-}
-inline
-const Zstring operator+(const Zstring& lhs, const DefaultChar* rhs)
-{
- return Zstring(lhs) += rhs;
-}
-inline
-const Zstring operator+(const DefaultChar* lhs, const Zstring& rhs)
+//################################# inline implementation ########################################
+#if defined(FFS_WIN) || defined(FFS_LINUX)
+namespace z_impl
{
- return Zstring(lhs) += rhs;
+int compareFilenamesWin(const wchar_t* a, const wchar_t* b, size_t sizeA, size_t sizeB);
+void makeUpperCaseWin(wchar_t* str, size_t size);
}
+template <class T, template <class, class> class SP, class AP>
inline
-const Zstring operator+(DefaultChar lhs, const Zstring& rhs)
+int cmpFileName(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs)
{
- return (Zstring() += lhs) += rhs;
+#ifdef FFS_WIN
+ return z_impl::compareFilenamesWin(lhs.data(), rhs.data(), lhs.length(), rhs.length());
+#elif defined FFS_LINUX
+ return ::strcmp(lhs.c_str(), rhs.c_str()); //POSIX filenames don't have embedded 0
+#endif
}
+template <class T, template <class, class> class SP, class AP>
inline
-const Zstring operator+(const Zstring& lhs, DefaultChar rhs)
+bool LessFilename::operator()(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) const
{
- return Zstring(lhs) += rhs;
+#ifdef FFS_WIN
+ return z_impl::compareFilenamesWin(lhs.data(), rhs.data(), lhs.length(), rhs.length()) < 0;
+#elif defined FFS_LINUX
+ return ::strcmp(lhs.c_str(), rhs.c_str()) < 0; //POSIX filenames don't have embedded 0
+#endif
}
+template <class T, template <class, class> class SP, class AP>
inline
-void Zstring::resize(size_t newSize, DefaultChar fillChar)
+bool EqualFilename::operator()(const Zbase<T, SP, AP>& lhs, const Zbase<T, SP, AP>& rhs) const
{
- const size_t oldSize = length();
- if (oldSize < newSize)
- {
- reserve(newSize); //make unshared and ensure capacity
-
- //fill up...
- DefaultChar* strPtr = data() + oldSize;
- const DefaultChar* const strEnd = data() + newSize;
- while (strPtr != strEnd)
- {
- *strPtr = fillChar;
- ++strPtr;
- }
-
- data()[newSize] = 0;
- descr->length = newSize;
- }
- else if (oldSize > newSize)
- {
- if (descr->refCount > 1)
- *this = Zstring(c_str(), newSize); //no need to reserve() and copy the old string completely!
- else //overwrite this string
- {
- data()[newSize] = 0;
- descr->length = newSize;
- }
- }
+#ifdef FFS_WIN
+ return z_impl::compareFilenamesWin(lhs.data(), rhs.data(), lhs.length(), rhs.length()) == 0;
+#elif defined FFS_LINUX
+ return ::strcmp(lhs.c_str(), rhs.c_str()) == 0; //POSIX filenames don't have embedded 0
+#endif
}
+#endif //defined(FFS_WIN) || defined(FFS_LINUX)
+#ifdef FFS_WIN
+template <template <class, class> class SP, class AP>
inline
-void Zstring::swap(Zstring& other)
+void MakeUpper(Zbase<wchar_t, SP, AP>& str)
{
- std::swap(descr, other.descr);
+ z_impl::makeUpperCaseWin(str.begin(), str.length());
}
+#endif
-template <class T>
-inline
-Zstring numberToZstring(const T& number) //convert number to string the C++ way
+namespace std
{
- std::basic_ostringstream<DefaultChar> ss;
- ss << number;
- return Zstring(ss.str().c_str());
-}
-
-
+template<>
inline
-int cmpFileName(const DefaultChar* lhs, const Zstring& rhs)
+void swap(Zstring& rhs, Zstring& lhs)
{
- return cmpFileName(rhs, lhs);
+ rhs.swap(lhs);
}
-
-
-inline
-bool LessFilename::operator()(const Zstring& a, const Zstring& b) const
-{
-// //quick check based on string length
-// const size_t aLength = a.data.shortName.length();
-// const size_t bLength = b.data.shortName.length();
-// if (aLength != bLength)
-// return aLength < bLength;
- return cmpFileName(a, b) < 0;
}
-#endif // ZSTRING_H_INCLUDED
+#endif //ZSTRING_H_INCLUDED
bgstack15