summaryrefslogtreecommitdiff
path: root/shared
diff options
context:
space:
mode:
Diffstat (limited to 'shared')
-rw-r--r--shared/customTooltip.cpp6
-rw-r--r--shared/fileHandling.cpp228
-rw-r--r--shared/fileHandling.h16
-rw-r--r--shared/fileTraverser.cpp7
-rw-r--r--shared/globalFunctions.cpp3
-rw-r--r--shared/globalFunctions.h109
-rw-r--r--shared/localization.cpp28
-rw-r--r--shared/shadow.cpp2
-rw-r--r--shared/standardPaths.cpp6
-rw-r--r--shared/systemConstants.h22
-rw-r--r--shared/toggleButton.cpp44
-rw-r--r--shared/toggleButton.h40
-rw-r--r--shared/xmlBase.cpp33
-rw-r--r--shared/xmlBase.h2
-rw-r--r--shared/zstring.cpp196
-rw-r--r--shared/zstring.h264
16 files changed, 658 insertions, 348 deletions
diff --git a/shared/customTooltip.cpp b/shared/customTooltip.cpp
index bffaa45d..cd9fbf05 100644
--- a/shared/customTooltip.cpp
+++ b/shared/customTooltip.cpp
@@ -37,8 +37,7 @@ PopupFrameGenerated::PopupFrameGenerated(
bSizer158->Add( m_bitmapLeft, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextMain = new wxStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextMain->Wrap( -1 );
- bSizer158->Add( m_staticTextMain, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer158->Add( m_staticTextMain, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5 );
this->SetSizer( bSizer158 );
this->Layout();
@@ -67,7 +66,10 @@ void CustomTooltip::show(const wxString& text, wxPoint pos, const wxBitmap* bmp)
}
if (text != tipWindow->m_staticTextMain->GetLabel())
+ {
tipWindow->m_staticTextMain->SetLabel(text);
+ tipWindow->m_staticTextMain->Wrap(600);
+ }
#ifdef FFS_LINUX
tipWindow->Fit(); //Alas Fit() seems to be somewhat broken => this needs to be called EVERY time inside show, not only if text or bmp change.
diff --git a/shared/fileHandling.cpp b/shared/fileHandling.cpp
index a5d3cf57..55fd7ff5 100644
--- a/shared/fileHandling.cpp
+++ b/shared/fileHandling.cpp
@@ -1,8 +1,13 @@
#include "fileHandling.h"
#include <wx/intl.h>
#include "systemFunctions.h"
-#include "globalFunctions.h"
+#include "systemConstants.h"
#include "fileTraverser.h"
+#include <wx/file.h>
+#include <stdexcept>
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <wx/log.h>
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
@@ -55,7 +60,7 @@ public:
return recycleBinAvailable;
}
- bool moveToRecycleBin(const Zstring& filename) const; //throw (RuntimeException)
+ bool moveToRecycleBin(const Zstring& filename) const; //throw (std::logic_error)
private:
RecycleBin() :
@@ -73,10 +78,10 @@ private:
};
-bool RecycleBin::moveToRecycleBin(const Zstring& filename) const //throw (RuntimeException)
+bool RecycleBin::moveToRecycleBin(const Zstring& filename) const //throw (std::logic_error)
{
if (!recycleBinAvailable) //this method should ONLY be called if recycle bin is available
- throw RuntimeException(_("Initialization of Recycle Bin failed!"));
+ throw std::logic_error("Initialization of Recycle Bin failed!");
#ifdef FFS_WIN
Zstring filenameDoubleNull = filename + wxChar(0);
@@ -105,7 +110,7 @@ bool FreeFileSync::recycleBinExists()
inline
-bool moveToRecycleBin(const Zstring& filename) //throw (RuntimeException)
+bool moveToRecycleBin(const Zstring& filename) //throw (std::logic_error)
{
return RecycleBin::getInstance().moveToRecycleBin(filename);
}
@@ -159,7 +164,54 @@ bool FreeFileSync::symlinkExists(const DefaultChar* objname)
}
-void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin) //throw (FileError, ::RuntimeException);
+bool FreeFileSync::isMovable(const Zstring& pathFrom, const Zstring& pathTo)
+{
+ wxLogNull noWxLogs; //prevent wxWidgets logging if dummy file creation failed
+
+ const Zstring dummyFileSource = pathFrom.EndsWith(globalFunctions::FILE_NAME_SEPARATOR) ?
+ pathFrom + wxT("DeleteMe.tmp") :
+ pathFrom + globalFunctions::FILE_NAME_SEPARATOR + wxT("DeleteMe.tmp");
+
+ const Zstring dummyFileTarget = pathTo.EndsWith(globalFunctions::FILE_NAME_SEPARATOR) ?
+ pathTo + wxT("DeleteMe.tmp") :
+ pathTo + globalFunctions::FILE_NAME_SEPARATOR + wxT("DeleteMe.tmp");
+ try
+ {
+ removeFile(dummyFileSource, false);
+ removeFile(dummyFileTarget, false);
+ }
+ catch (...) {}
+
+ //create dummy file
+ {
+ wxFile dummy(dummyFileSource.c_str(), wxFile::write);
+ if (!dummy.IsOpened())
+ return false; //if there's no write access, files can't be moved neither
+ dummy.Write(wxT("FreeFileSync dummy file. May be deleted safely.\n"));
+ }
+
+ const bool result =
+ //try to move the file
+#ifdef FFS_WIN
+ ::MoveFileEx(dummyFileSource.c_str(), //__in LPCTSTR lpExistingFileName,
+ dummyFileTarget.c_str(), //__in_opt LPCTSTR lpNewFileName,
+ 0) != 0; //__in DWORD dwFlags
+#elif defined FFS_LINUX
+ ::rename(dummyFileSource.c_str(), dummyFileTarget.c_str()) == 0;
+#endif
+
+ try
+ {
+ removeFile(dummyFileSource, false);
+ removeFile(dummyFileTarget, false);
+ }
+ catch (...) {}
+
+ return result;
+}
+
+
+void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin) //throw (FileError, std::logic_error);
{
//no error situation if file is not existing! manual deletion relies on it!
#ifdef FFS_WIN
@@ -232,7 +284,7 @@ private:
void FreeFileSync::moveFile(const Zstring& sourceFile, const Zstring& targetFile, MoveFileCallback* callback) //throw (FileError);
{
- if (fileExists(targetFile)) //test file existence: e.g. Linux might silently overwrite existing symlinks
+ if (fileExists(targetFile.c_str())) //test file existence: e.g. Linux might silently overwrite existing symlinks
{
const wxString errorMessage = wxString(_("Error moving file:")) + wxT("\n\"") + sourceFile + wxT("\" ->\n\"") + targetFile + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + _("Target file already existing!"));
@@ -481,18 +533,21 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc
return;
}
-//attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!!
+
#ifdef FFS_WIN
- if (dirAttr & FILE_ATTRIBUTE_REPARSE_POINT) //remove symlink directly, support for \\?\-prefix
+ //initialize file attributes
+ if (!::SetFileAttributes( // initialize file attributes: actually NEEDED for symbolic links also!
+ directory.c_str(), // address of directory name
+ FILE_ATTRIBUTE_NORMAL)) // attributes to set
{
- if (!::SetFileAttributes( // initialize file attributes: actually NEEDED for symbolic links also!
- directory.c_str(), // address of directory name
- FILE_ATTRIBUTE_NORMAL)) // attributes to set
- {
- wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
+ wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
+ throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
+ }
+
+//attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!!
+ if (dirAttr & FILE_ATTRIBUTE_REPARSE_POINT) //remove symlink directly, support for \\?\-prefix
+ {
if (!::RemoveDirectory(directory.c_str()))
{
wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
@@ -521,24 +576,13 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc
FreeFileSync::traverseFolder(directory, false, &traverser);
//delete files
- for (std::vector<Zstring>::const_iterator j = fileList.begin(); j != fileList.end(); ++j)
- FreeFileSync::removeFile(*j, false);
+ std::for_each(fileList.begin(), fileList.end(), boost::bind(removeFile, _1, false));
//delete directories recursively
- for (std::vector<Zstring>::const_iterator j = dirList.begin(); j != dirList.end(); ++j)
- FreeFileSync::removeDirectory(*j, false); //call recursively to correctly handle symbolic links
+ std::for_each(dirList.begin(), dirList.end(), boost::bind(removeDirectory, _1, false)); //call recursively to correctly handle symbolic links
//parent directory is deleted last
#ifdef FFS_WIN
- //initialize file attributes
- if (!::SetFileAttributes(
- directory.c_str(), // address of directory name
- FILE_ATTRIBUTE_NORMAL)) // attributes to set
- {
- wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
-
//remove directory, support for \\?\-prefix
if (!::RemoveDirectory(directory.c_str()))
{
@@ -563,14 +607,77 @@ public:
~CloseHandleOnExit()
{
- CloseHandle(fileHandle_);
+ ::CloseHandle(fileHandle_);
}
private:
HANDLE fileHandle_;
};
+#endif
+
+
+//optionally: copy directory last change date, DO NOTHING if something fails
+void FreeFileSync::copyDirLastChangeDate(const Zstring& sourceDir, const Zstring& targetDir)
+{
+ if (symlinkExists(sourceDir)) //don't handle symlinks (yet)
+ return;
+
+#ifdef FFS_WIN
+ HANDLE hDirRead = ::CreateFile(sourceDir.c_str(),
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, //needed for directories
+ NULL);
+
+ if (hDirRead == INVALID_HANDLE_VALUE)
+ return;
+ CloseHandleOnExit dummy(hDirRead);
+
+ FILETIME lastWriteTime;
+ if (::GetFileTime(hDirRead,
+ NULL,
+ NULL,
+ &lastWriteTime))
+ {
+ HANDLE hDirWrite = ::CreateFile(targetDir.c_str(),
+ FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, //needed for directories
+ NULL);
+
+ if (hDirWrite != INVALID_HANDLE_VALUE)
+ {
+ CloseHandleOnExit dummy2(hDirWrite);
+
+ //(try to) set new "last write time"
+ ::SetFileTime(hDirWrite,
+ NULL,
+ NULL,
+ &lastWriteTime); //return value not evalutated!
+ }
+ }
+#elif defined FFS_LINUX
+ struct stat dirInfo;
+ if (::stat(sourceDir.c_str(), &dirInfo) == 0) //read file attributes from source directory
+ {
+ //adapt file modification time:
+ struct utimbuf newTimes;
+ ::time(&newTimes.actime); //set file access time to current time
+ newTimes.modtime = dirInfo.st_mtime;
+ //(try to) set new "last write time"
+ ::utime(targetDir.c_str(), &newTimes); //return value not evalutated!
+ }
+#endif
+}
+
+
+#ifdef FFS_WIN
class KernelDllHandler //dynamically load windows API functions
{
typedef DWORD (WINAPI *GetFinalPath)(
@@ -640,6 +747,53 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa
return targetPath;
}
+
+
+//#include <aclapi.h>
+//optionally: copy additional metadata, DO NOTHING if something fails
+//void copyAdditionalMetadata(const Zstring& sourceDir, const Zstring& targetDir)
+//{
+// PSECURITY_DESCRIPTOR pSD;
+//
+// PSID pSidOwner;
+// PSID pSidGroup;
+// PACL pDacl;
+// PACL pSacl;
+// if (::GetNamedSecurityInfo(
+// const_cast<DefaultChar*>(sourceDir.c_str()),
+// SE_FILE_OBJECT, //file or directory
+// OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION,
+// &pSidOwner,
+// &pSidGroup,
+// &pDacl,
+// &pSacl,
+// &pSD
+// ) == ERROR_SUCCESS)
+// {
+// //(try to) set new security information
+// if (::SetNamedSecurityInfo(
+// const_cast<DefaultChar*>(targetDir.c_str()),
+// SE_FILE_OBJECT, //file or directory
+// OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION,
+// pSidOwner,
+// pSidGroup,
+// pDacl,
+// pSacl) != ERROR_SUCCESS) //return value not evalutated!
+// {
+// const wxString errorMessage = wxString(wxT("Error 2:")) + wxT("\n\"") + targetDir.c_str() + wxT("\"");
+// throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
+// }
+//warning BUG!
+//
+// LocalFree(pSD); //pSidOwner, pSidGroup, pDacl, pSacl must not be freed!
+// }
+// else
+// {
+// const wxString errorMessage = wxString(wxT("Error 1:")) + wxT("\n\"") + sourceDir.c_str() + wxT("\"");
+// throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
+// }
+//
+//}
#endif
@@ -694,6 +848,9 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
const wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
}
+
+ //(try to) copy additional metadata like last modification time: no measurable performance drawback
+ //copyAdditionalMetadata(linkPath, directory);
}
}
else //in all other cases
@@ -706,6 +863,9 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
const wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
}
+
+ //(try to) copy additional metadata like last modification time: no measurable performance drawback
+ //copyAdditionalMetadata(templateDir, directory);
}
}
#elif defined FFS_LINUX
@@ -874,13 +1034,15 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
//don't suppress "lastError == ERROR_REQUEST_ABORTED": an user aborted operation IS an error condition!
//if file is locked (try to) use Windows Volume Shadow Copy Service
- if (lastError == ERROR_SHARING_VIOLATION && shadowCopyHandler != NULL)
+ if (shadowCopyHandler != NULL &&
+ (lastError == ERROR_SHARING_VIOLATION ||
+ lastError == ERROR_LOCK_VIOLATION))
{
const Zstring shadowFilename(shadowCopyHandler->makeShadowCopy(sourceFile));
FreeFileSync::copyFile(shadowFilename, //transferred bytes is automatically reset when new file is copied
targetFile,
copyFileSymLinks,
- shadowCopyHandler,
+ NULL,
callback);
return;
}
@@ -958,7 +1120,7 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
//begin of regular file copy
struct stat fileInfo;
- if (stat(sourceFile.c_str(), &fileInfo) != 0) //read file attributes from source file (resolving symlinks)
+ if (stat(sourceFile.c_str(), &fileInfo) != 0) //read file attributes from source file (resolving symlinks; but cannot be one in this context)
{
const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + sourceFile.c_str() + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
diff --git a/shared/fileHandling.h b/shared/fileHandling.h
index b5e75c17..5ccd595f 100644
--- a/shared/fileHandling.h
+++ b/shared/fileHandling.h
@@ -10,16 +10,22 @@ namespace FreeFileSync
{
Zstring getFormattedDirectoryName(const Zstring& dirname);
- bool fileExists(const DefaultChar* filename); //replaces wxFileExists()!
- bool dirExists(const DefaultChar* dirname); //replaces wxDirExists(): optional 'cause wxDirExists treats symlinks correctly
- bool symlinkExists(const DefaultChar* objname); //check if a symbolic link exists
+ bool fileExists(const DefaultChar* filename); //throw() replaces wxFileExists()!
+ bool dirExists(const DefaultChar* dirname); //throw() replaces wxDirExists(): optional 'cause wxDirExists treats symlinks correctly
+ bool symlinkExists(const DefaultChar* objname); //throw() check if a symbolic link exists
+
+ //check if files can be moved between two EXISTING paths (without copying)
+ bool isMovable(const Zstring& pathFrom, const Zstring& pathTo); //throw()
+
+ //optionally: copy directory last change date, DOES NOTHING if something fails
+ void copyDirLastChangeDate(const Zstring& sourceDir, const Zstring& targetDir);
//recycler
bool recycleBinExists(); //test existence of Recycle Bin API on current system
//file handling
- void removeFile(const Zstring& filename, const bool useRecycleBin); //throw (FileError, ::RuntimeException);
- void removeDirectory(const Zstring& directory, const bool useRecycleBin); //throw (FileError);
+ void removeFile(const Zstring& filename, const bool useRecycleBin); //throw (FileError, std::logic_error)
+ void removeDirectory(const Zstring& directory, const bool useRecycleBin); //throw (FileError)
class MoveFileCallback //callback functionality
diff --git a/shared/fileTraverser.cpp b/shared/fileTraverser.cpp
index 6438b358..ab223791 100644
--- a/shared/fileTraverser.cpp
+++ b/shared/fileTraverser.cpp
@@ -1,5 +1,5 @@
#include "fileTraverser.h"
-#include "globalFunctions.h"
+#include "systemConstants.h"
#include "systemFunctions.h"
#include <wx/intl.h>
@@ -128,8 +128,9 @@ bool traverseDirectory(const Zstring& directory, FreeFileSync::TraverseCallback*
directory + globalFunctions::FILE_NAME_SEPARATOR;
WIN32_FIND_DATA fileMetaData;
- HANDLE searchHandle = FindFirstFile((directoryFormatted + DefaultChar('*')).c_str(), //pointer to name of file to search for
- &fileMetaData); //pointer to returned information
+ HANDLE searchHandle = FindFirstFile((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)
{
diff --git a/shared/globalFunctions.cpp b/shared/globalFunctions.cpp
index cbdf29bb..f50a3097 100644
--- a/shared/globalFunctions.cpp
+++ b/shared/globalFunctions.cpp
@@ -5,6 +5,7 @@
#include <wx/stream.h>
#include <wx/stopwatch.h>
#include <cmath>
+#include "systemConstants.h"
wxString globalFunctions::numberToWxString(const unsigned int number)
@@ -50,7 +51,6 @@ int globalFunctions::wxStringToInt(const wxString& number)
return result;
else
return 0; //don't throw exceptions here: wxEmptyString shall be interpreted as 0
- //throw RuntimeException(wxString(_("Conversion error:")) + wxT(" wxString -> long"));
}
@@ -61,7 +61,6 @@ double globalFunctions::wxStringToDouble(const wxString& number)
return result;
else
return 0; //don't throw exceptions here: wxEmptyString shall be interpreted as 0
- //throw RuntimeException(wxString(_("Conversion error:")) + wxT(" wxString -> double"));
}
diff --git a/shared/globalFunctions.h b/shared/globalFunctions.h
index 00566b5f..b53ba596 100644
--- a/shared/globalFunctions.h
+++ b/shared/globalFunctions.h
@@ -18,17 +18,6 @@ class wxStopWatch;
namespace globalFunctions
{
//------------------------------------------------
-// GLOBALS
-//------------------------------------------------
-#ifdef FFS_WIN
- const wxChar FILE_NAME_SEPARATOR = '\\';
- static const wxChar* const LINE_BREAK = wxT("\r\n"); //internal linkage
-#elif defined FFS_LINUX
- const wxChar FILE_NAME_SEPARATOR = '/';
- static const wxChar* const LINE_BREAK = wxT("\n");
-#endif
-
-//------------------------------------------------
// FUNCTIONS
//------------------------------------------------
inline
@@ -78,32 +67,80 @@ namespace globalFunctions
}
- //Note: the following lines are a performance optimization for deleting elements from a vector. It is incredibly faster to create a new
-//vector and leave specific elements out than to delete row by row and force recopying of most elements for each single deletion (linear vs quadratic runtime)
+//Note: the following lines are a performance optimization for deleting elements from a vector: linear runtime at most!
template <class T>
- void removeRowsFromVector(const std::set<int>& rowsToRemove, std::vector<T>& grid)
+ void removeRowsFromVector(const std::set<unsigned int>& rowsToRemove, std::vector<T>& grid)
{
- if (rowsToRemove.size() > 0)
- {
- std::vector<T> temp;
+ if (rowsToRemove.empty())
+ return;
+
+ std::set<unsigned int>::const_iterator rowToSkipIndex = rowsToRemove.begin();
+ unsigned int rowToSkip = *rowToSkipIndex;
- std::set<int>::const_iterator rowToSkipIndex = rowsToRemove.begin();
- int rowToSkip = *rowToSkipIndex;
+ if (rowToSkip >= grid.size())
+ return;
- for (int i = 0; i < int(grid.size()); ++i)
+ typename std::vector<T>::iterator insertPos = grid.begin() + rowToSkip;
+
+ for (unsigned int i = rowToSkip; i < grid.size(); ++i)
+ {
+ if (i != rowToSkip)
+ {
+ *insertPos = grid[i];
+ ++insertPos;
+ }
+ else
{
- if (i != rowToSkip)
- temp.push_back(grid[i]);
- else
+ ++rowToSkipIndex;
+ if (rowToSkipIndex != rowsToRemove.end())
+ rowToSkip = *rowToSkipIndex;
+ }
+ }
+ grid.erase(insertPos, grid.end());
+ }
+
+ //bubble sort using swap() instead of assignment: useful if assignment is very expensive
+ template <class VectorData, typename CompareFct>
+ void bubbleSwapSort(VectorData& folderCmp, CompareFct compare)
+ {
+ for (int i = folderCmp.size() - 2; i >= 0; --i)
+ {
+ bool swapped = false;
+ for (int j = 0; j <= i; ++j)
+ if (compare(folderCmp[j + 1], folderCmp[j]))
{
- ++rowToSkipIndex;
- if (rowToSkipIndex != rowsToRemove.end())
- rowToSkip = *rowToSkipIndex;
+ folderCmp[j + 1].swap(folderCmp[j]);
+ swapped = true;
}
- }
- grid.swap(temp);
+
+ if (!swapped)
+ return;
}
}
+
+ //enhanced binary search template: returns an iterator
+ template <class ForwardIterator, class T>
+ inline
+ ForwardIterator custom_binary_search(ForwardIterator first, ForwardIterator last, const T& value)
+ {
+ first = lower_bound(first, last, value);
+ if (first != last && !(value < *first))
+ return first;
+ else
+ return last;
+ }
+
+ //enhanced binary search template: returns an iterator
+ template <class ForwardIterator, class T, typename Compare>
+ inline
+ ForwardIterator custom_binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp)
+ {
+ first = lower_bound(first, last, value, comp);
+ if (first != last && !comp(value, *first))
+ return first;
+ else
+ return last;
+ }
}
@@ -148,20 +185,4 @@ wxString getCodeLocation(const wxString file, const int line);
//speed alternative: wxLogDebug(wxT("text")) + DebugView
-//############################################################################
-class RuntimeException //Exception class used to notify of general runtime exceptions
-{
-public:
- RuntimeException(const wxString& txt) : errorMessage(txt) {}
-
- wxString show() const
- {
- return errorMessage;
- }
-
-private:
- wxString errorMessage;
-};
-
-
#endif // GLOBALFUNCTIONS_H_INCLUDED
diff --git a/shared/localization.cpp b/shared/localization.cpp
index 56afd20d..20bf6d2e 100644
--- a/shared/localization.cpp
+++ b/shared/localization.cpp
@@ -1,7 +1,7 @@
#include "localization.h"
#include <wx/msgdlg.h>
#include "../shared/standardPaths.h"
-#include "../shared/globalFunctions.h"
+#include "systemConstants.h"
#include <fstream>
#include <map>
#include <wx/ffile.h>
@@ -87,7 +87,7 @@ LocalizationInfo::LocalizationInfo()
newEntry.languageID = wxLANGUAGE_RUSSIAN;
newEntry.languageName = wxT("Pусский");
newEntry.languageFile = wxT("russian.lng");
- newEntry.translatorName = wxT("Fayullin T.N. aka Svobodniy");
+ newEntry.translatorName = wxT("Fayzullin T.N. aka Svobodniy");
newEntry.languageFlag = wxT("russia.png");
locMapping.push_back(newEntry);
@@ -132,6 +132,13 @@ LocalizationInfo::LocalizationInfo()
newEntry.translatorName = wxT("CyberCowBoy");
newEntry.languageFlag = wxT("china.png");
locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_CHINESE_TRADITIONAL;
+ newEntry.languageName = wxT("正體中文");
+ newEntry.languageFile = wxT("chinese_traditional.lng");
+ newEntry.translatorName = wxT("Carlos");
+ newEntry.languageFlag = wxT("taiwan.png");
+ locMapping.push_back(newEntry);
}
@@ -165,13 +172,15 @@ int mapLanguageDialect(const int language)
//variants of wxLANGUAGE_CHINESE_SIMPLIFIED
case wxLANGUAGE_CHINESE:
- case wxLANGUAGE_CHINESE_TRADITIONAL:
- case wxLANGUAGE_CHINESE_HONGKONG:
- case wxLANGUAGE_CHINESE_MACAU:
case wxLANGUAGE_CHINESE_SINGAPORE:
- case wxLANGUAGE_CHINESE_TAIWAN:
return wxLANGUAGE_CHINESE_SIMPLIFIED;
+ //variants of wxLANGUAGE_CHINESE_TRADITIONAL
+ case wxLANGUAGE_CHINESE_TAIWAN:
+ case wxLANGUAGE_CHINESE_HONGKONG:
+ case wxLANGUAGE_CHINESE_MACAU:
+ return wxLANGUAGE_CHINESE_TRADITIONAL;
+
//variants of wxLANGUAGE_RUSSIAN
case wxLANGUAGE_RUSSIAN_UKRAINE:
return wxLANGUAGE_RUSSIAN;
@@ -227,7 +236,7 @@ CustomLocale& CustomLocale::getInstance()
CustomLocale::CustomLocale() :
- wxLocale(wxLocale::GetSystemLanguage()), //wxLocale is a static object too => can be initialized just once
+ wxLocale(wxLANGUAGE_DEFAULT), //setting a different language needn't be supported on all systems!
translationDB(new Translation),
currentLanguage(wxLANGUAGE_ENGLISH) {}
@@ -372,14 +381,17 @@ void CustomLocale::setLanguage(const int language)
const wxString& translation = tmpString;
if (!translation.empty())
- translationDB->insert(std::pair<TextOriginal, TextTranslation>(original, translation));
+ translationDB->insert(std::make_pair(original, translation));
}
++rowNumber;
}
}
else
+ {
wxMessageBox(wxString(_("Error reading file:")) + wxT(" \"") + languageFile + wxT("\""), _("Error"), wxOK | wxICON_ERROR);
+ currentLanguage = wxLANGUAGE_ENGLISH; //reset to english language to show this error just once
+ }
}
else
; //if languageFile is empty texts will be english per default
diff --git a/shared/shadow.cpp b/shared/shadow.cpp
index abdca1a9..02a6a522 100644
--- a/shared/shadow.cpp
+++ b/shared/shadow.cpp
@@ -1,7 +1,7 @@
#include "shadow.h"
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include <wx/intl.h>
-#include "globalfunctions.h"
+#include "systemConstants.h"
using FreeFileSync::ShadowCopy;
diff --git a/shared/standardPaths.cpp b/shared/standardPaths.cpp
index 0c333598..54774673 100644
--- a/shared/standardPaths.cpp
+++ b/shared/standardPaths.cpp
@@ -1,15 +1,15 @@
#include "standardPaths.h"
#include <wx/stdpaths.h>
-#include "globalFunctions.h"
#include <wx/filename.h>
+#include "systemConstants.h"
wxString assembleFileForUserData(const wxString fileName)
{
static const bool isPortableVersion = !wxFileExists(FreeFileSync::getInstallationDir() + globalFunctions::FILE_NAME_SEPARATOR + wxT("uninstall.exe")); //this check is a bit lame...
- if (isPortableVersion) //use same directory as executable
- return FreeFileSync::getInstallationDir() + globalFunctions::FILE_NAME_SEPARATOR + fileName;
+ if (isPortableVersion) //use current working directory
+ return wxString(wxT(".")) + globalFunctions::FILE_NAME_SEPARATOR + fileName;
else //usen OS' standard paths
{
wxString userDirectory = wxStandardPathsBase::Get().GetUserDataDir();
diff --git a/shared/systemConstants.h b/shared/systemConstants.h
new file mode 100644
index 00000000..84c1ae85
--- /dev/null
+++ b/shared/systemConstants.h
@@ -0,0 +1,22 @@
+#ifndef SYSTEMCONSTANTS_H_INCLUDED
+#define SYSTEMCONSTANTS_H_INCLUDED
+
+#include "zstring.h"
+
+
+namespace globalFunctions
+{
+//------------------------------------------------
+// GLOBALS
+//------------------------------------------------
+#ifdef FFS_WIN
+ const DefaultChar FILE_NAME_SEPARATOR = '\\';
+ static const DefaultChar* const LINE_BREAK = L"\r\n"; //internal linkage
+#elif defined FFS_LINUX
+ const DefaultChar FILE_NAME_SEPARATOR = '/';
+ static const DefaultChar* const LINE_BREAK = "\n";
+#endif
+}
+
+
+#endif // SYSTEMCONSTANTS_H_INCLUDED
diff --git a/shared/toggleButton.cpp b/shared/toggleButton.cpp
new file mode 100644
index 00000000..b83143ea
--- /dev/null
+++ b/shared/toggleButton.cpp
@@ -0,0 +1,44 @@
+#include "toggleButton.h"
+
+void ToggleButton::init(const wxBitmap& activeBmp,
+ const wxString& activeTooltip,
+ const wxBitmap& inactiveBmp,
+ const wxString& inactiveTooltip)
+{
+ m_activeBmp = activeBmp;
+ m_activeTooltip = activeTooltip;
+ m_inactiveBmp = inactiveBmp;
+ m_inactiveTooltip = inactiveTooltip;
+
+ //load resources
+ setActive(active);
+}
+
+
+bool ToggleButton::isActive() const
+{
+ return active;
+}
+
+
+void ToggleButton::toggle()
+{
+ setActive(!active);
+}
+
+
+void ToggleButton::setActive(bool value)
+{
+ active = value;
+
+ if (active)
+ {
+ SetBitmapLabel(m_activeBmp);
+ SetToolTip(m_activeTooltip);
+ }
+ else
+ {
+ SetBitmapLabel(m_inactiveBmp);
+ SetToolTip(m_inactiveTooltip);
+ }
+}
diff --git a/shared/toggleButton.h b/shared/toggleButton.h
new file mode 100644
index 00000000..b7475e0c
--- /dev/null
+++ b/shared/toggleButton.h
@@ -0,0 +1,40 @@
+#ifndef TOGGLEBUTTON_H_INCLUDED
+#define TOGGLEBUTTON_H_INCLUDED
+
+#include <wx/bmpbuttn.h>
+
+class ToggleButton : public wxBitmapButton
+{
+public:
+ ToggleButton(wxWindow* parent,
+ wxWindowID id,
+ const wxBitmap& bitmap,
+ const wxPoint& pos = wxDefaultPosition,
+ const wxSize& size = wxDefaultSize,
+ long style = 0,
+ const wxValidator& validator = wxDefaultValidator,
+ const wxString& name = wxButtonNameStr) :
+ wxBitmapButton(parent, id, bitmap, pos, size, style, validator, name),
+ active(false) {}
+
+ void init(const wxBitmap& activeBmp,
+ const wxString& activeTooltip,
+ const wxBitmap& inactiveBmp,
+ const wxString& inactiveTooltip);
+
+ void setActive(bool value);
+ bool isActive() const;
+ void toggle();
+
+private:
+ bool active;
+
+ wxBitmap m_activeBmp;
+ wxString m_activeTooltip;
+
+ wxBitmap m_inactiveBmp;
+ wxString m_inactiveTooltip;
+};
+
+
+#endif // TOGGLEBUTTON_H_INCLUDED
diff --git a/shared/xmlBase.cpp b/shared/xmlBase.cpp
index 19ec0ab6..ba96b663 100644
--- a/shared/xmlBase.cpp
+++ b/shared/xmlBase.cpp
@@ -37,8 +37,15 @@ xmlAccess::XmlType xmlAccess::getXmlType(const wxString& filename)
FILE* inputFile = configFile.fp();
TiXmlDocument doc;
- if (!doc.LoadFile(inputFile)) //fails if inputFile is no proper XML
+ try
+ {
+ if (!doc.LoadFile(inputFile)) //fails if inputFile is no proper XML
+ return XML_OTHER;
+ }
+ catch (const std::exception&)
+ { //unfortunately TiXml isn't very smart and tries to allocate space for the complete file: length_error exception is thrown for large files!
return XML_OTHER;
+ }
TiXmlElement* root = doc.RootElement();
@@ -118,7 +125,10 @@ bool xmlAccess::saveXmlDocument(const wxString& fileName, const TiXmlDocument& d
FILE* outputFile = dummyFile.fp();
- return document.SaveFile(outputFile); //save XML
+ if (!document.SaveFile(outputFile)) //save XML
+ return false;
+
+ return dummyFile.Flush(); //flush data to disk! (think of multiple batch jobs writing/reading)
}
@@ -244,6 +254,17 @@ bool xmlAccess::readXmlAttribute(const std::string& name, const TiXmlElement* no
}
+bool xmlAccess::readXmlAttribute(const std::string& name, const TiXmlElement* node, wxString& output)
+{
+ std::string tempString;
+ if (!readXmlAttribute(name, node, tempString))
+ return false;
+
+ output = wxString::FromUTF8(tempString.c_str());
+ return true;
+}
+
+
bool xmlAccess::readXmlAttribute(const std::string& name, const TiXmlElement* node, int& output)
{
std::string dummy;
@@ -344,6 +365,12 @@ void xmlAccess::addXmlAttribute(const std::string& name, const std::string& valu
}
+void xmlAccess::addXmlAttribute(const std::string& name, const wxString& value, TiXmlElement* node)
+{
+ addXmlAttribute(name, std::string(value.ToUTF8()), node);
+}
+
+
void xmlAccess::addXmlAttribute(const std::string& name, const int value, TiXmlElement* node)
{
addXmlAttribute(name, globalFunctions::numberToString(value), node);
@@ -372,11 +399,13 @@ void XmlParser::logError(const std::string& nodeName)
failedNodes.push_back(wxString::FromUTF8(nodeName.c_str()));
}
+
bool XmlParser::errorsOccured() const
{
return !failedNodes.empty();
}
+
const wxString XmlParser::getErrorMessageFormatted() const
{
wxString errorMessage = wxString(_("Could not read values for the following XML nodes:")) + wxT("\n");
diff --git a/shared/xmlBase.h b/shared/xmlBase.h
index 5fc13b65..ed838843 100644
--- a/shared/xmlBase.h
+++ b/shared/xmlBase.h
@@ -38,6 +38,7 @@ namespace xmlAccess
bool readXmlElement(const std::string& name, const TiXmlElement* parent, std::vector<wxString>& output);
bool readXmlAttribute(const std::string& name, const TiXmlElement* node, std::string& output);
+ bool readXmlAttribute(const std::string& name, const TiXmlElement* node, wxString& output);
bool readXmlAttribute(const std::string& name, const TiXmlElement* node, int& output);
bool readXmlAttribute(const std::string& name, const TiXmlElement* node, unsigned int& output);
bool readXmlAttribute(const std::string& name, const TiXmlElement* node, bool& output);
@@ -51,6 +52,7 @@ namespace xmlAccess
void addXmlElement(const std::string& name, const std::vector<wxString>& value, TiXmlElement* parent);
void addXmlAttribute(const std::string& name, const std::string& value, TiXmlElement* node);
+ void addXmlAttribute(const std::string& name, const wxString& value, TiXmlElement* node);
void addXmlAttribute(const std::string& name, const int value, TiXmlElement* node);
void addXmlAttribute(const std::string& name, const unsigned int value, TiXmlElement* node);
void addXmlAttribute(const std::string& name, const bool value, TiXmlElement* node);
diff --git a/shared/zstring.cpp b/shared/zstring.cpp
index ba7292e3..6b2b68c6 100644
--- a/shared/zstring.cpp
+++ b/shared/zstring.cpp
@@ -1,10 +1,13 @@
#include "zstring.h"
-#include "globalFunctions.h"
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include <stdexcept>
#endif //FFS_WIN
+#ifdef __WXDEBUG__
+#include <wx/string.h>
+#endif
#ifdef __WXDEBUG__
@@ -43,27 +46,42 @@ AllocationCount& AllocationCount::getInstance()
#ifdef FFS_WIN
-int FreeFileSync::compareStringsWin32(const wchar_t* a, const wchar_t* b, const int aCount, const int bCount)
+inline
+int compareStringsWin32(const wchar_t* a, const wchar_t* b, const int aCount = -1, const int bCount = -1)
{
//DON'T use lstrcmpi() here! It uses word sort, which unfortunately is NOT always a strict weak sorting function for some locales (e.g. swedish)
//Use CompareString() with "SORT_STRINGSORT" instead!!!
const int rv = CompareString(
LOCALE_USER_DEFAULT, //locale identifier
- NORM_IGNORECASE | SORT_STRINGSORT, //comparison-style options
+ NORM_IGNORECASE | SORT_STRINGSORT, //comparison-style options
a, //pointer to first string
aCount, //size, in bytes or characters, of first string
b, //pointer to second string
bCount); //size, in bytes or characters, of second string
if (rv == 0)
- throw RuntimeException(wxString(wxT("Error comparing strings!")));
+ throw std::runtime_error("Error comparing strings!");
else
return rv - 2; //convert to C-style string compare result
}
#endif
+#ifdef FFS_WIN
+int Zstring::CmpNoCase(const DefaultChar* other) const
+{
+ return ::compareStringsWin32(c_str(), other); //way faster than wxString::CmpNoCase()!!
+}
+
+
+int Zstring::CmpNoCase(const Zstring& other) const
+{
+ return ::compareStringsWin32(c_str(), other.c_str(), length(), other.length()); //way faster than wxString::CmpNoCase()!!
+}
+#endif
+
+
Zstring& Zstring::Replace(const DefaultChar* old, const DefaultChar* replacement, bool replaceAll)
{
const size_t oldLen = defaultLength(old);
@@ -89,15 +107,13 @@ Zstring& Zstring::Replace(const DefaultChar* old, const DefaultChar* replacement
bool matchesHelper(const DefaultChar* string, const DefaultChar* mask)
{
- for (DefaultChar ch; (ch = *mask) != 0; ++mask)
+ for (DefaultChar ch; (ch = *mask) != 0; ++mask, ++string)
{
switch (ch)
{
case DefaultChar('?'):
if (*string == 0)
return false;
- else
- ++string;
break;
case DefaultChar('*'):
@@ -124,8 +140,6 @@ bool matchesHelper(const DefaultChar* string, const DefaultChar* mask)
default:
if (*string != ch)
return false;
- else
- ++string;
}
}
return *string == 0;
@@ -150,42 +164,43 @@ Zstring& Zstring::Trim(bool fromRight)
if (thisLen == 0)
return *this;
+ DefaultChar* const strBegin = data();
+
if (fromRight)
{
- const DefaultChar* cursor = data + thisLen - 1;
- while (cursor != data - 1 && defaultIsWhiteSpace(*cursor)) //break when pointing one char further than last skipped element
+ 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 - data;
+ const size_t newLength = cursor - strBegin;
if (newLength != thisLen)
{
if (descr->refCount > 1) //allocate new string
- *this = Zstring(data, newLength);
- else //overwrite this strin
+ *this = Zstring(strBegin, newLength);
+ else //overwrite this string
{
- descr->length = newLength;
- data[newLength] = DefaultChar(0);
+ descr->length = newLength;
+ strBegin[newLength] = 0;
}
}
}
else
{
- DefaultChar* cursor = data;
- DefaultChar ch;
- while ((ch = *cursor) != 0 && defaultIsWhiteSpace(ch))
+ const DefaultChar* cursor = strBegin;
+ const DefaultChar* const strEnd = strBegin + thisLen;
+ while (cursor != strEnd && defaultIsWhiteSpace(*cursor))
++cursor;
- const size_t diff = cursor - data;
+ const size_t diff = cursor - strBegin;
if (diff)
{
if (descr->refCount > 1) //allocate new string
*this = Zstring(cursor, thisLen - diff);
else
{ //overwrite this string
- data = cursor; //no problem when deallocating data, since descr points to begin of allocated area
- descr->capacity -= diff;
- descr->length -= diff;
+ ::memmove(strBegin, cursor, (thisLen - diff + 1) * sizeof(DefaultChar)); //note: do not simply let data point to different location: this corrupts reserve()!
+ descr->length -= diff;
}
}
}
@@ -194,34 +209,50 @@ Zstring& Zstring::Trim(bool fromRight)
}
-Zstring& Zstring::MakeLower()
+std::vector<Zstring> Zstring::Tokenize(const DefaultChar delimiter) const
{
- const size_t thisLen = length();
- if (thisLen == 0)
- return *this;
+ std::vector<Zstring> output;
- if (descr->refCount > 1) //allocate new string
+ const size_t thisLen = length();
+ size_t indexStart = 0;
+ while (true)
{
- StringDescriptor* newDescr;
- DefaultChar* newData;
- allocate(thisLen, newDescr, newData);
+ size_t indexEnd = find(delimiter, indexStart);
+ if (indexEnd == Zstring::npos)
+ indexEnd = thisLen;
- for (unsigned int i = 0; i < thisLen; ++i)
- newData[i] = defaultToLower(data[i]);
- newData[thisLen] = 0;
+ 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
- decRef();
- descr = newDescr;
- data = newData;
- }
- else
- { //overwrite this string
- for (unsigned int i = 0; i < thisLen; ++i)
- data[i] = defaultToLower(data[i]);
+ 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::MakeLower()
+{
+ const size_t thisLen = length();
+ if (thisLen == 0)
+ return *this;
+
+ reserve(thisLen); //make unshared
+ ::CharLower(data()); //use Windows' lower case conversion
+
return *this;
}
+#endif
//###############################################################
@@ -254,7 +285,7 @@ size_t Zstring::rfind(const DefaultChar ch, size_t pos) const
do //pos points to last char of the string
{
- if (data[pos] == ch)
+ if (c_str()[pos] == ch)
return pos;
}
while (--pos != static_cast<size_t>(-1));
@@ -277,27 +308,25 @@ Zstring& Zstring::replace(size_t pos1, size_t n1, const DefaultChar* str, size_t
const size_t newLen = oldLen - n1 + n2;
if (newLen > oldLen || descr->refCount > 1)
{ //allocate a new string
- StringDescriptor* newDescr;
- DefaultChar* newData;
- allocate(newLen, newDescr, newData);
+ StringDescriptor* newDescr = allocate(newLen);
//assemble new string with replacement
- memcpy(newData, data, pos1 * sizeof(DefaultChar));
- memcpy(newData + pos1, str, n2 * sizeof(DefaultChar));
- memcpy(newData + pos1 + n2, data + pos1 + n1, (oldLen - pos1 - n1) * sizeof(DefaultChar));
+ 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();
- data = newData;
descr = newDescr;
}
else //overwrite current string: case "n2 == 0" is handled implicitly
{
- memcpy(data + pos1, str, n2 * sizeof(DefaultChar));
+ ::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;
+ ::memmove(data() + pos1 + n2, data() + pos1 + n1, (oldLen - pos1 - n1) * sizeof(DefaultChar));
+ data()[newLen] = 0;
descr->length = newLen;
}
}
@@ -309,21 +338,32 @@ Zstring& Zstring::replace(size_t pos1, size_t n1, const DefaultChar* str, size_t
Zstring& Zstring::operator=(const DefaultChar* source)
{
const size_t sourceLen = defaultLength(source);
- if (sourceLen == 0)
- return *this = Zstring();
if (descr->refCount > 1 || descr->capacity < sourceLen) //allocate new string
*this = Zstring(source, sourceLen);
else
{ //overwrite this string
- memcpy(data, source, sourceLen * sizeof(DefaultChar));
- data[sourceLen] = 0;
+ ::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();
@@ -331,10 +371,9 @@ Zstring& Zstring::operator+=(const Zstring& other)
{
const size_t thisLen = length();
const size_t newLen = thisLen + otherLen;
- copyBeforeWrite(newLen);
+ reserve(newLen); //make unshared and check capacity
- memcpy(data + thisLen, other.c_str(), otherLen * sizeof(DefaultChar));
- data[newLen] = 0;
+ ::memcpy(data() + thisLen, other.c_str(), (otherLen + 1) * sizeof(DefaultChar)); //include null-termination
descr->length = newLen;
}
return *this;
@@ -348,10 +387,9 @@ Zstring& Zstring::operator+=(const DefaultChar* other)
{
const size_t thisLen = length();
const size_t newLen = thisLen + otherLen;
- copyBeforeWrite(newLen);
+ reserve(newLen); //make unshared and check capacity
- memcpy(data + thisLen, other, otherLen * sizeof(DefaultChar));
- data[newLen] = 0;
+ ::memcpy(data() + thisLen, other, (otherLen + 1) * sizeof(DefaultChar)); //include NULL-termination
descr->length = newLen;
}
return *this;
@@ -361,15 +399,15 @@ Zstring& Zstring::operator+=(const DefaultChar* other)
Zstring& Zstring::operator+=(DefaultChar ch)
{
const size_t oldLen = length();
- copyBeforeWrite(oldLen + 1);
- data[oldLen] = ch;
- data[oldLen + 1] = 0;
+ reserve(oldLen + 1); //make unshared and check capacity
+ data()[oldLen] = ch;
+ data()[oldLen + 1] = 0;
++descr->length;
return *this;
}
-void Zstring::copyBeforeWrite(const size_t capacityNeeded)
+void Zstring::reserve(size_t capacityNeeded) //make unshared and check capacity
{
assert(capacityNeeded != 0);
@@ -378,36 +416,28 @@ void Zstring::copyBeforeWrite(const size_t capacityNeeded)
const size_t oldLength = length();
assert(oldLength <= getCapacityToAllocate(capacityNeeded));
- StringDescriptor* newDescr;
- DefaultChar* newData;
- allocate(capacityNeeded, newDescr, newData);
+ StringDescriptor* newDescr = allocate(capacityNeeded);
newDescr->length = oldLength;
- if (oldLength)
- {
- memcpy(newData, data, oldLength * sizeof(DefaultChar));
- newData[oldLength] = 0;
- }
+ ::memcpy(reinterpret_cast<DefaultChar*>(newDescr + 1), c_str(), (oldLength + 1) * sizeof(DefaultChar)); //include NULL-termination
+
decRef();
descr = newDescr;
- data = newData;
}
else if (descr->capacity < capacityNeeded)
{ //try to resize the current string (allocate anew if necessary)
const size_t newCapacity = getCapacityToAllocate(capacityNeeded);
- descr = (StringDescriptor*) realloc(descr, sizeof(StringDescriptor) + (newCapacity + 1) * sizeof(DefaultChar));
- if (descr == NULL)
- throw std::bad_alloc();
-
#ifdef __WXDEBUG__
- AllocationCount::getInstance().dec(data); //test Zstring for memory leaks
+ AllocationCount::getInstance().dec(c_str()); //test Zstring for memory leaks
#endif
- data = (DefaultChar*)(descr + 1);
+ descr = static_cast<StringDescriptor*>(::realloc(descr, sizeof(StringDescriptor) + (newCapacity + 1) * sizeof(DefaultChar)));
+ if (descr == NULL)
+ throw std::bad_alloc();
#ifdef __WXDEBUG__
- AllocationCount::getInstance().inc(data); //test Zstring for memory leaks
+ AllocationCount::getInstance().inc(c_str()); //test Zstring for memory leaks
#endif
descr->capacity = newCapacity;
diff --git a/shared/zstring.h b/shared/zstring.h
index 0403a00c..018ca42e 100644
--- a/shared/zstring.h
+++ b/shared/zstring.h
@@ -11,27 +11,21 @@
#include <cctype>
#include <assert.h>
#include <new>
+#include <stdlib.h>
+#include <vector>
+
#ifdef __WXDEBUG__
#include <set>
+#include <wx/thread.h>
#endif
-namespace FreeFileSync
-{
-#ifdef FFS_WIN
- //super fast case-insensitive string comparison: way faster than wxString::CmpNoCase()!!!
- int compareStringsWin32(const wchar_t* a, const wchar_t* b, const int aCount = -1, const int bCount = -1);
-#endif //FFS_WIN
-}
-
-
#ifdef ZSTRING_CHAR
typedef char DefaultChar; //use char strings
#elif defined ZSTRING_WIDE_CHAR
typedef wchar_t DefaultChar; //use wide character strings
#endif
-class Zsubstr;
class Zstring
{
@@ -44,8 +38,9 @@ public:
operator const DefaultChar*() const; //implicit conversion to C string
- //wxWidgets functions
+ //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;
@@ -54,23 +49,25 @@ public:
#ifdef FFS_WIN
int CmpNoCase(const DefaultChar* other) const;
int CmpNoCase(const Zstring& other) const;
+ Zstring& MakeLower();
#endif
int Cmp(const DefaultChar* other) const;
int Cmp(const Zstring& other) const;
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 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) const;
bool Matches(const DefaultChar* mask) const;
static bool Matches(const DefaultChar* name, const DefaultChar* mask);
Zstring& Trim(bool fromRight); //from right or left
- Zstring& MakeLower();
+ std::vector<Zstring> Tokenize(const DefaultChar delimiter) const;
//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
- Zsubstr zsubstr(size_t pos = 0) const; //use ref-counting!
bool empty() const;
void clear();
int compare(const DefaultChar* other) const;
@@ -81,16 +78,18 @@ public:
size_t rfind(const 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);
Zstring& operator=(const Zstring& source);
Zstring& operator=(const DefaultChar* source);
- bool operator == (const Zstring& other) const;
- bool operator == (const DefaultChar* other) const;
- bool operator < (const Zstring& other) const;
- bool operator < (const DefaultChar* other) const;
- bool operator != (const Zstring& other) const;
- bool operator != (const DefaultChar* other) const;
+ bool operator==(const Zstring& other) const;
+ bool operator==(const DefaultChar* other) const;
+ bool operator< (const Zstring& other) const;
+ bool operator< (const DefaultChar* other) const;
+ bool operator!=(const Zstring& other) const;
+ bool operator!=(const DefaultChar* other) const;
const DefaultChar operator[](const size_t pos) const;
@@ -106,11 +105,11 @@ public:
private:
Zstring(int); //indicates usage error
+ DefaultChar* data();
void initAndCopy(const DefaultChar* source, size_t length);
- void incRef() const; //support for reference-counting
- void decRef(); //
- void copyBeforeWrite(const size_t capacityNeeded); //and copy-on-write
+ void incRef() const; //support for reference-counting
+ void decRef(); //
struct StringDescriptor
{
@@ -118,28 +117,9 @@ private:
size_t length;
size_t capacity; //allocated length without null-termination
};
- static void allocate(const size_t newLength, StringDescriptor*& newDescr, DefaultChar*& newData);
+ static StringDescriptor* allocate(const size_t newLength);
StringDescriptor* descr;
- DefaultChar* data;
-};
-
-
-class Zsubstr //ref-counted substring of a Zstring
-{
-public:
- Zsubstr();
- Zsubstr(const Zstring& ref, const size_t pos);
-
- const DefaultChar* c_str() const;
- size_t length() const;
- bool StartsWith(const Zstring& begin) const;
- size_t findFromEnd(const DefaultChar ch) const;
-
-private:
- Zstring m_ref;
- const DefaultChar* m_data;
- size_t m_length;
};
@@ -181,15 +161,15 @@ inline
bool defaultIsWhiteSpace(const char ch)
{
// some compilers (e.g. VC++ 6.0) return true for a call to isspace('\xEA') => exclude char(128) to char(255)
- return ((unsigned char)ch < 128) && isspace((unsigned char)ch) != 0;
-}
-
-inline
-char defaultToLower(const char ch)
-{
- return tolower((unsigned char)ch); //caution: although tolower() has int as input parameter it expects unsigned chars!
+ return (static_cast<unsigned char>(ch) < 128) && isspace((unsigned char)ch) != 0;
}
+//inline
+//char defaultToLower(const char ch)
+//{
+// return tolower(static_cast<unsigned char>(ch)); //caution: although tolower() has int as input parameter it expects unsigned chars!
+//}
+//
#elif defined ZSTRING_WIDE_CHAR
inline
size_t defaultLength(const wchar_t* input)
@@ -228,11 +208,11 @@ bool defaultIsWhiteSpace(const wchar_t ch)
return (ch < 128 || ch > 255) && iswspace(ch) != 0;
}
-inline
-wchar_t defaultToLower(const wchar_t ch)
-{
- return towlower(ch);
-}
+//inline
+//wchar_t defaultToLower(const wchar_t ch)
+//{
+// return towlower(ch);
+//}
#endif
@@ -242,11 +222,13 @@ class AllocationCount //small test for memory leaks in Zstring
public:
void inc(const DefaultChar* object)
{
+ wxCriticalSectionLocker dummy(lockActStrings);
activeStrings.insert(object);
}
void dec(const DefaultChar* object)
{
+ wxCriticalSectionLocker dummy(lockActStrings);
activeStrings.erase(object);
}
@@ -256,6 +238,7 @@ private:
AllocationCount() {}
~AllocationCount();
+ wxCriticalSection lockActStrings;
std::set<const DefaultChar*> activeStrings;
};
#endif
@@ -269,23 +252,23 @@ size_t getCapacityToAllocate(const size_t length)
inline
-void Zstring::allocate(const size_t newLength,
- StringDescriptor*& newDescr,
- DefaultChar*& newData)
+Zstring::StringDescriptor* Zstring::allocate(const size_t newLength)
{ //allocate and set data for new string
const size_t newCapacity = getCapacityToAllocate(newLength);
assert(newCapacity);
- newDescr = static_cast<StringDescriptor*>(operator new [] (sizeof(StringDescriptor) + (newCapacity + 1) * sizeof(DefaultChar)));
- newData = reinterpret_cast<DefaultChar*>(newDescr + 1);
+ StringDescriptor* 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;
#ifdef __WXDEBUG__
- AllocationCount::getInstance().inc(newData); //test Zstring for memory leaks
+ AllocationCount::getInstance().inc(reinterpret_cast<DefaultChar*>(newDescr + 1)); //test Zstring for memory leaks
#endif
+ return newDescr;
}
@@ -301,7 +284,6 @@ Zstring::Zstring()
emptyString.incRef();
descr = emptyString.descr;
- data = emptyString.data;
}
@@ -323,7 +305,6 @@ inline
Zstring::Zstring(const Zstring& source)
{
descr = source.descr;
- data = source.data;
incRef(); //reference counting!
}
@@ -338,9 +319,9 @@ Zstring::~Zstring()
inline
void Zstring::initAndCopy(const DefaultChar* source, size_t sourceLen)
{
- allocate(sourceLen, descr, data);
- memcpy(data, source, sourceLen * sizeof(DefaultChar));
- data[sourceLen] = 0;
+ descr = allocate(sourceLen);
+ ::memcpy(data(), source, sourceLen * sizeof(DefaultChar));
+ data()[sourceLen] = 0;
}
@@ -358,31 +339,15 @@ void Zstring::decRef()
assert(descr && descr->refCount >= 1); //descr points to the begin of the allocated memory block
if (--descr->refCount == 0)
{
- operator delete [] (descr); //this must NEVER be changed!! E.g. Trim() relies on descr being start of allocated memory block
- descr = NULL;
#ifdef __WXDEBUG__
- AllocationCount::getInstance().dec(data); //test Zstring for memory leaks
+ AllocationCount::getInstance().dec(c_str()); //test Zstring for memory leaks
#endif
+ ::free(descr); //beginning of whole memory block
+ descr = NULL;
}
}
-#ifdef FFS_WIN
-inline
-int Zstring::CmpNoCase(const DefaultChar* other) const
-{
- return FreeFileSync::compareStringsWin32(c_str(), other); //way faster than wxString::CmpNoCase()!!
-}
-
-
-inline
-int Zstring::CmpNoCase(const Zstring& other) const
-{
- return FreeFileSync::compareStringsWin32(c_str(), other.c_str(), length(), other.length()); //way faster than wxString::CmpNoCase()!!
-}
-#endif
-
-
inline
Zstring::operator const DefaultChar*() const
{
@@ -396,7 +361,6 @@ Zstring& Zstring::operator=(const Zstring& source)
source.incRef(); //implicitly handle case "this == &source" and avoid this check
decRef(); //
descr = source.descr;
- data = source.data;
return *this;
}
@@ -417,11 +381,11 @@ size_t Zstring::Find(DefaultChar ch, bool fromEnd) const
inline
Zstring Zstring::AfterLast(DefaultChar ch) const
{
- size_t pos = rfind(ch, npos);
- if (pos == npos )
- return *this;
+ const size_t pos = rfind(ch, npos);
+ if (pos != npos )
+ return Zstring(c_str() + pos + 1, length() - pos - 1);
else
- return c_str() + pos + 1;
+ return *this;
}
@@ -430,12 +394,35 @@ Zstring Zstring::AfterLast(DefaultChar ch) const
inline
Zstring Zstring::BeforeLast(DefaultChar ch) const
{
- size_t pos = rfind(ch, npos);
+ 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();
+}
- if (pos != npos && pos != 0 )
- return Zstring(data, pos); //data is non-empty string in this context: else ch would not have been found!
+
+//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;
}
@@ -450,12 +437,20 @@ bool Zstring::StartsWith(const DefaultChar* begin) const
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) == 0;
+ return compare(0, beginLength, begin.c_str()) == 0;
}
@@ -485,7 +480,7 @@ bool Zstring::EndsWith(const Zstring& end) const
const size_t endLength = end.length();
if (thisLength < endLength)
return false;
- return compare(thisLength - endLength, endLength, end) == 0;
+ return compare(thisLength - endLength, endLength, end.c_str()) == 0;
}
@@ -498,8 +493,8 @@ Zstring& Zstring::Truncate(size_t newLen)
return *this = Zstring(c_str(), newLen);
else //overwrite this string
{
- descr->length = newLen;
- data[newLen] = DefaultChar(0);
+ descr->length = newLen;
+ data()[newLen] = 0;
}
}
@@ -622,7 +617,14 @@ size_t Zstring::size() const
inline
const DefaultChar* Zstring::c_str() const
{
- return data;
+ return reinterpret_cast<DefaultChar*>(descr + 1);
+}
+
+
+inline
+DefaultChar* Zstring::data()
+{
+ return reinterpret_cast<DefaultChar*>(descr + 1);
}
@@ -644,7 +646,7 @@ inline
const DefaultChar Zstring::operator[](const size_t pos) const
{
assert(pos < length());
- return data[pos];
+ return c_str()[pos];
}
@@ -668,67 +670,5 @@ const Zstring Zstring::operator+(const DefaultChar ch) const
return Zstring(*this)+=ch;
}
-//##################### Zsubstr #############################
-inline
-Zsubstr Zstring::zsubstr(size_t pos) const
-{
- assert(pos <= length());
- return Zsubstr(*this, pos); //return reference counted string
-}
-
-
-inline
-Zsubstr::Zsubstr()
-{
- m_data = m_ref.c_str();
- m_length = 0;
-}
-
-
-inline
-Zsubstr::Zsubstr(const Zstring& ref, const size_t pos) :
- m_ref(ref),
- m_data(ref.c_str() + pos),
- m_length(ref.length() - pos) {}
-
-
-inline
-const DefaultChar* Zsubstr::c_str() const
-{
- return m_data;
-}
-
-
-inline
-size_t Zsubstr::length() const
-{
- return m_length;
-}
-
-
-inline
-bool Zsubstr::StartsWith(const Zstring& begin) const
-{
- const size_t beginLength = begin.length();
- if (length() < beginLength)
- return false;
-
- return defaultCompare(m_data, begin.c_str(), beginLength) == 0;
-}
-
-
-inline
-size_t Zsubstr::findFromEnd(const DefaultChar ch) const
-{
- size_t pos = length();
- while (--pos != static_cast<size_t>(-1))
- {
- if (m_data[pos] == ch)
- return pos;
- }
- return Zstring::npos;
-}
-
-
#endif // ZSTRING_H_INCLUDED
bgstack15