diff options
Diffstat (limited to 'shared')
-rw-r--r-- | shared/customTooltip.cpp | 6 | ||||
-rw-r--r-- | shared/fileHandling.cpp | 228 | ||||
-rw-r--r-- | shared/fileHandling.h | 16 | ||||
-rw-r--r-- | shared/fileTraverser.cpp | 7 | ||||
-rw-r--r-- | shared/globalFunctions.cpp | 3 | ||||
-rw-r--r-- | shared/globalFunctions.h | 109 | ||||
-rw-r--r-- | shared/localization.cpp | 28 | ||||
-rw-r--r-- | shared/shadow.cpp | 2 | ||||
-rw-r--r-- | shared/standardPaths.cpp | 6 | ||||
-rw-r--r-- | shared/systemConstants.h | 22 | ||||
-rw-r--r-- | shared/toggleButton.cpp | 44 | ||||
-rw-r--r-- | shared/toggleButton.h | 40 | ||||
-rw-r--r-- | shared/xmlBase.cpp | 33 | ||||
-rw-r--r-- | shared/xmlBase.h | 2 | ||||
-rw-r--r-- | shared/zstring.cpp | 196 | ||||
-rw-r--r-- | shared/zstring.h | 264 |
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 |