diff options
Diffstat (limited to 'FreeFileSync.cpp')
-rw-r--r-- | FreeFileSync.cpp | 429 |
1 files changed, 274 insertions, 155 deletions
diff --git a/FreeFileSync.cpp b/FreeFileSync.cpp index 74c558b9..d0927ae8 100644 --- a/FreeFileSync.cpp +++ b/FreeFileSync.cpp @@ -7,7 +7,6 @@ #include "library/resources.h" #include <sys/stat.h> #include <wx/ffile.h> -#include "library/tinyxml/tinyxml.h" #ifdef FFS_WIN #include <windows.h> @@ -20,8 +19,7 @@ using namespace globalFunctions; -const string FreeFileSync::FfsConfigFileID = "FFS_CONFIG"; -const wxString FreeFileSync::FfsLastConfigFile = wxT("LastRun.ffs"); +const wxString FreeFileSync::FfsLastConfigFile = wxT("LastRun.ffs_gui"); inline wxString formatTime(unsigned int number) @@ -255,7 +253,88 @@ void calcTotalDataForCompare(int& objectsTotal, double& dataTotal, const FileCom } -void FreeFileSync::startCompareProcess(FileCompareResult& output, const wxString& dirLeft, const wxString& dirRight, CompareVariant cmpVar, StatusUpdater* statusUpdater) +struct DescrBufferLine +{ + wxString directoryName; + DirectoryDescrType* directoryDesc; + +#ifdef FFS_WIN + //Windows does NOT distinguish between upper/lower-case + bool operator>(const DescrBufferLine& b ) const + { + return (directoryName.CmpNoCase(b.directoryName) > 0); + } + bool operator<(const DescrBufferLine& b) const + { + return (directoryName.CmpNoCase(b.directoryName) < 0); + } + bool operator==(const DescrBufferLine& b) const + { + return (directoryName.CmpNoCase(b.directoryName) == 0); + } + +#elif defined FFS_LINUX + //Linux DOES distinguish between upper/lower-case + bool operator>(const DescrBufferLine& b ) const + { + return (directoryName.Cmp(b.directoryName) > 0); + } + bool operator<(const DescrBufferLine& b) const + { + return (directoryName.Cmp(b.directoryName) < 0); + } + bool operator==(const DescrBufferLine& b) const + { + return (directoryName.Cmp(b.directoryName) == 0); + } +#else + adapt this +#endif + +}; + + +class DirectoryDescrBuffer //buffer multiple scans of the same directories +{ +public: + ~DirectoryDescrBuffer() + { + //clean up + for (set<DescrBufferLine>::iterator i = buffer.begin(); i != buffer.end(); ++i) + delete i->directoryDesc; + } + + const DirectoryDescrType* getDirectoryDescription(const wxString& directory, StatusUpdater* statusUpdater) + { + DescrBufferLine bufferEntry; + bufferEntry.directoryName = directory; + + set<DescrBufferLine>::iterator entryFound; + if ((entryFound = buffer.find(bufferEntry)) != buffer.end()) + { + //entry found in buffer; return + return entryFound->directoryDesc; + } + else + { + //entry not found; create new one + bufferEntry.directoryDesc = new DirectoryDescrType; + FreeFileSync::generateFileAndFolderDescriptions(*bufferEntry.directoryDesc, directory, statusUpdater); + buffer.insert(bufferEntry); + + return bufferEntry.directoryDesc; + } + } + +private: + set<DescrBufferLine> buffer; +}; + + +void FreeFileSync::startCompareProcess(FileCompareResult& output, + const vector<FolderPair>& directoryPairsFormatted, + const CompareVariant cmpVar, + StatusUpdater* statusUpdater) { #ifndef __WXDEBUG__ wxLogNull dummy; //hide wxWidgets log messages in release build @@ -266,126 +345,132 @@ void FreeFileSync::startCompareProcess(FileCompareResult& output, const wxString //################################################################################################################################################ //inform about the total amount of data that will be processed from now on - statusUpdater->initNewProcess(-1, 0, FreeFileSync::scanningFilesProcess); //it's not known how many files will be scanned => -1 objects + statusUpdater->initNewProcess(-1, 0, FreeFileSync::scanningFilesProcess); //it's not known how many files will be scanned => -1 objects FileCompareResult output_tmp; //write to output not before END of process! + set<int> delayedContentCompare; //compare of file content happens AFTER finding corresponding files + //in order to separate into two processes (needed by progress indicators) + + //buffer accesses to the same directories; useful when multiple folder pairs are used + DirectoryDescrBuffer descriptionBuffer; try - { //retrieve sets of files (with description data) - DirectoryDescrType directoryLeft; - DirectoryDescrType directoryRight; + { + //process one folder pair after each other + for (vector<FolderPair>::const_iterator pair = directoryPairsFormatted.begin(); pair != directoryPairsFormatted.end(); ++pair) + { + //unsigned int startTime = GetTickCount(); - //unsigned int startTime = GetTickCount(); - generateFileAndFolderDescriptions(directoryLeft, dirLeft, statusUpdater); - generateFileAndFolderDescriptions(directoryRight, dirRight, statusUpdater); - //wxMessageBox(numberToWxString(unsigned(GetTickCount()) - startTime)); - statusUpdater->forceUiRefresh(); + //retrieve sets of files (with description data) + const DirectoryDescrType* directoryLeft = descriptionBuffer.getDirectoryDescription(pair->leftDirectory, statusUpdater); + const DirectoryDescrType* directoryRight = descriptionBuffer.getDirectoryDescription(pair->rightDirectory, statusUpdater); - FileCompareLine newline; + //wxMessageBox(numberToWxString(unsigned(GetTickCount()) - startTime)); + statusUpdater->forceUiRefresh(); - set<int> delayedContentCompare; //compare of file content happens AFTER finding corresponding files - //in order to separate into two processes (needed by progress indicators) + FileCompareLine newline; - //find files/folders that exist in left file model but not in right model - for (DirectoryDescrType::iterator i = directoryLeft.begin(); i != directoryLeft.end(); ++i) - if (directoryRight.find(*i) == directoryRight.end()) - { - newline.fileDescrLeft = *i; - newline.fileDescrRight = FileDescrLine(); - newline.fileDescrRight.directory = dirRight; - newline.cmpResult = FILE_LEFT_SIDE_ONLY; - output_tmp.push_back(newline); - } - - for (DirectoryDescrType::iterator j = directoryRight.begin(); j != directoryRight.end(); ++j) - { - DirectoryDescrType::iterator i; - - //find files/folders that exist in right file model but not in left model - if ((i = directoryLeft.find(*j)) == directoryLeft.end()) - { - newline.fileDescrLeft = FileDescrLine(); - newline.fileDescrLeft.directory = dirLeft; //directory info is needed when creating new directories - newline.fileDescrRight = *j; - newline.cmpResult = FILE_RIGHT_SIDE_ONLY; - output_tmp.push_back(newline); - } - //find files that exist in left and right file model - else - { //objType != TYPE_NOTHING - if (i->objType == TYPE_DIRECTORY && j->objType == TYPE_DIRECTORY) - { - newline.fileDescrLeft = *i; - newline.fileDescrRight = *j; - newline.cmpResult = FILE_EQUAL; - output_tmp.push_back(newline); - } - //if we have a nameclash between a file and a directory: split into separate rows - else if (i->objType != j->objType) + //find files/folders that exist in left file model but not in right model + for (DirectoryDescrType::iterator i = directoryLeft->begin(); i != directoryLeft->end(); ++i) + if (directoryRight->find(*i) == directoryRight->end()) { newline.fileDescrLeft = *i; newline.fileDescrRight = FileDescrLine(); - newline.fileDescrRight.directory = dirRight; + newline.fileDescrRight.directory = pair->rightDirectory; newline.cmpResult = FILE_LEFT_SIDE_ONLY; output_tmp.push_back(newline); + } + for (DirectoryDescrType::iterator j = directoryRight->begin(); j != directoryRight->end(); ++j) + { + DirectoryDescrType::iterator i; + + //find files/folders that exist in right file model but not in left model + if ((i = directoryLeft->find(*j)) == directoryLeft->end()) + { newline.fileDescrLeft = FileDescrLine(); - newline.fileDescrLeft.directory = dirLeft; + newline.fileDescrLeft.directory = pair->leftDirectory; //directory info is needed when creating new directories newline.fileDescrRight = *j; newline.cmpResult = FILE_RIGHT_SIDE_ONLY; output_tmp.push_back(newline); } - else if (cmpVar == CMP_BY_TIME_SIZE) - { //check files that exist in left and right model but have different properties - - //last write time may differ by up to 2 seconds (NTFS vs FAT32) - bool sameWriteTime; - if (i->lastWriteTimeRaw < j->lastWriteTimeRaw) - sameWriteTime = (j->lastWriteTimeRaw - i->lastWriteTimeRaw <= 2); - else - sameWriteTime = (i->lastWriteTimeRaw - j->lastWriteTimeRaw <= 2); - - newline.fileDescrLeft = *i; - newline.fileDescrRight = *j; - if (sameWriteTime) + //find files that exist in left and right file model + else + { //objType != TYPE_NOTHING + if (i->objType == TYPE_DIRECTORY && j->objType == TYPE_DIRECTORY) { - if (i->fileSize == j->fileSize) - newline.cmpResult = FILE_EQUAL; - else - newline.cmpResult = FILE_DIFFERENT; + newline.fileDescrLeft = *i; + newline.fileDescrRight = *j; + newline.cmpResult = FILE_EQUAL; + output_tmp.push_back(newline); } - else + //if we have a nameclash between a file and a directory: split into separate rows + else if (i->objType != j->objType) { + newline.fileDescrLeft = *i; + newline.fileDescrRight = FileDescrLine(); + newline.fileDescrRight.directory = pair->rightDirectory; + newline.cmpResult = FILE_LEFT_SIDE_ONLY; + output_tmp.push_back(newline); + + newline.fileDescrLeft = FileDescrLine(); + newline.fileDescrLeft.directory = pair->leftDirectory; + newline.fileDescrRight = *j; + newline.cmpResult = FILE_RIGHT_SIDE_ONLY; + output_tmp.push_back(newline); + } + else if (cmpVar == CMP_BY_TIME_SIZE) + { //check files that exist in left and right model but have different properties + + //last write time may differ by up to 2 seconds (NTFS vs FAT32) + bool sameWriteTime; if (i->lastWriteTimeRaw < j->lastWriteTimeRaw) - newline.cmpResult = FILE_RIGHT_NEWER; + sameWriteTime = (j->lastWriteTimeRaw - i->lastWriteTimeRaw <= 2); else - newline.cmpResult = FILE_LEFT_NEWER; - } - output_tmp.push_back(newline); - } - else if (cmpVar == CMP_BY_CONTENT) - { //check files that exist in left and right model but have different content + sameWriteTime = (i->lastWriteTimeRaw - j->lastWriteTimeRaw <= 2); - //check filesize first! - if (i->fileSize == j->fileSize) - { newline.fileDescrLeft = *i; newline.fileDescrRight = *j; - //newline.cmpResult = ...; //not yet determined + if (sameWriteTime) + { + if (i->fileSize == j->fileSize) + newline.cmpResult = FILE_EQUAL; + else + newline.cmpResult = FILE_DIFFERENT; + } + else + { + if (i->lastWriteTimeRaw < j->lastWriteTimeRaw) + newline.cmpResult = FILE_RIGHT_NEWER; + else + newline.cmpResult = FILE_LEFT_NEWER; + } output_tmp.push_back(newline); - - //compare by content is only needed if filesizes are the same - delayedContentCompare.insert(output_tmp.size() - 1); //save index of row, to calculate cmpResult later } - else - { - newline.fileDescrLeft = *i; - newline.fileDescrRight = *j; - newline.cmpResult = FILE_DIFFERENT; - output_tmp.push_back(newline); + else if (cmpVar == CMP_BY_CONTENT) + { //check files that exist in left and right model but have different content + + //check filesize first! + if (i->fileSize == j->fileSize) + { + newline.fileDescrLeft = *i; + newline.fileDescrRight = *j; + newline.cmpResult = FILE_INVALID; //not yet determined + output_tmp.push_back(newline); + + //compare by content is only needed if filesizes are the same + delayedContentCompare.insert(output_tmp.size() - 1); //save index of row, to calculate cmpResult later + } + else + { + newline.fileDescrLeft = *i; + newline.fileDescrRight = *j; + newline.cmpResult = FILE_DIFFERENT; + output_tmp.push_back(newline); + } } + else assert (false); } - else assert (false); } } @@ -662,7 +747,7 @@ public: fileOp.lpszProgressTitle = NULL; if (SHFileOperation(&fileOp //pointer to an SHFILEOPSTRUCT structure that contains information the function needs to carry out - ) != 0 || fileOp.fAnyOperationsAborted) throw FileError(wxString(_("Error moving file ")) + wxT("\"") + filename + wxT("\"") + _(" to recycle bin!")); + ) != 0 || fileOp.fAnyOperationsAborted) throw FileError(wxString(_("Error moving file to recycle bin: ")) + wxT("\"") + filename + wxT("\"")); #endif // FFS_WIN } @@ -968,6 +1053,9 @@ bool getBytesToTransfer(int& objectsToCreate, case FILE_EQUAL: return false; + default: + assert(false); + return false; }; return true; @@ -1245,12 +1333,39 @@ wxString FreeFileSync::formatFilesizeToShortString(const double filesize) } +vector<wxString> FreeFileSync::compoundStringToTable(const wxString& compoundInput, const wxChar* delimiter) +{ + wxString input(compoundInput); + vector<wxString> output; + + //make sure input ends with delimiter - no problem with empty strings here + if (!input.EndsWith(delimiter)) + input+= delimiter; + + unsigned int indexStart = 0; + unsigned int indexEnd = 0; + while ((indexEnd = input.find(delimiter, indexStart )) != string::npos) + { + if (indexStart != indexEnd) //do not add empty strings + { + wxString newEntry = input.substr(indexStart, indexEnd - indexStart); + + newEntry.Trim(true); //remove whitespace characters from right + newEntry.Trim(false); //remove whitespace characters from left + + if (!newEntry.IsEmpty()) + output.push_back(newEntry); + } + indexStart = indexEnd + 1; + } + + return output; +} + + inline void formatFilterString(wxString& filter) { - filter.Trim(true); //remove whitespace characters from right - filter.Trim(false); //remove whitespace characters from left - #ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case filter.MakeLower(); @@ -1278,48 +1393,42 @@ void formatFilenameString(wxString& filename) } -void FreeFileSync::filterCurrentGridData(FileCompareResult& currentGridData, const wxString& includeFilter, const wxString& excludeFilter) +inline +void mergeVectors(vector<wxString>& changing, const vector<wxString>& input) { - wxString includeFilterTmp(includeFilter); - wxString excludeFilterTmp(excludeFilter); + for (vector<wxString>::const_iterator i = input.begin(); i != input.end(); ++i) + changing.push_back(*i); +} - //make sure filters end with ";" - no problem with empty strings here - if (!includeFilterTmp.EndsWith(wxT(";"))) - includeFilterTmp.Append(';'); - if (!excludeFilterTmp.EndsWith(wxT(";"))) - excludeFilterTmp.Append(';'); +void FreeFileSync::filterCurrentGridData(FileCompareResult& currentGridData, const wxString& includeFilter, const wxString& excludeFilter) +{ //load filters into vectors + //delimiters may be ';' or '\n' vector<wxString> includeList; - vector<wxString> excludeList; - - unsigned int indexStart = 0; - unsigned int indexEnd = 0; - while ((indexEnd = includeFilterTmp.find(';', indexStart )) != string::npos) + vector<wxString> includePreProcessing = compoundStringToTable(includeFilter, wxT(";")); + for (vector<wxString>::const_iterator i = includePreProcessing.begin(); i != includePreProcessing.end(); ++i) { - if (indexStart != indexEnd) //do not add empty strings - { - wxString newEntry = includeFilterTmp.substr(indexStart, indexEnd - indexStart); - formatFilterString(newEntry); - includeList.push_back(newEntry); - } - indexStart = indexEnd + 1; + vector<wxString> newEntries = compoundStringToTable(*i, wxT("\n")); + mergeVectors(includeList, newEntries); } - indexStart = 0; - indexEnd = 0; - while ((indexEnd = excludeFilterTmp.find(';', indexStart )) != string::npos) + vector<wxString> excludeList; + vector<wxString> excludePreProcessing = compoundStringToTable(excludeFilter, wxT(";")); + for (vector<wxString>::const_iterator i = excludePreProcessing.begin(); i != excludePreProcessing.end(); ++i) { - if (indexStart != indexEnd) //do not add empty strings - { - wxString newEntry = excludeFilterTmp.substr(indexStart, indexEnd - indexStart); - formatFilterString(newEntry); - excludeList.push_back(newEntry); - } - indexStart = indexEnd + 1; + vector<wxString> newEntries = compoundStringToTable(*i, wxT("\n")); + mergeVectors(excludeList, newEntries); } -//########################### + //format entries + for (vector<wxString>::iterator i = includeList.begin(); i != includeList.end(); ++i) + formatFilterString(*i); + + for (vector<wxString>::iterator i = excludeList.begin(); i != excludeList.end(); ++i) + formatFilterString(*i); + +//############################################################## //filter currentGridData for (FileCompareResult::iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) @@ -1394,13 +1503,16 @@ void FreeFileSync::removeFilterOnCurrentGridData(FileCompareResult& currentGridD wxString FreeFileSync::getFormattedDirectoryName(const wxString& dirname) -{ //formatting is needed since functions in FreeFileSync.cpp expect the directory to end with '\' to be able to split the relative names - //actually all it needs, is the length of the directory +{ //Formatting is needed since functions in FreeFileSync.cpp expect the directory to end with '\' to be able to split the relative names. + //Also improves usability. wxString dirnameTmp = dirname; dirnameTmp.Trim(true); //remove whitespace characters from right dirnameTmp.Trim(false); //remove whitespace characters from left + if (dirnameTmp.IsEmpty()) //an empty string is interpreted as "\"; this is not desired + return wxEmptyString; + //let wxWidgets do the directory formatting, e.g. replace '/' with '\' for Windows wxString result = wxDir(dirnameTmp).GetName(); @@ -1573,31 +1685,6 @@ void FreeFileSync::startSynchronizationProcess(FileCompareResult& grid, const Sy } -bool FreeFileSync::isFfsConfigFile(const wxString& filename) -{ - if (!wxFileExists(filename)) - return false; - - //workaround to get a FILE* from a unicode filename - wxFFile configFile(filename, wxT("rb")); - if (!configFile.IsOpened()) - return false; - - FILE* inputFile = configFile.fp(); - - TiXmlDocument doc; - if (!doc.LoadFile(inputFile)) //fails if inputFile is no proper XML - return false; - - TiXmlElement* root = doc.RootElement(); - - if (root && (root->ValueStr() == string("FreeFileSync"))) //check for FFS configuration xml - return true; - else - return false; -} - - //add(!) all files and subfolder gridlines that are dependent from the directory void FreeFileSync::addSubElements(set<int>& subElements, const FileCompareResult& grid, const FileCompareLine& relevantRow) { @@ -1674,3 +1761,35 @@ void FreeFileSync::deleteOnGridAndHD(FileCompareResult& grid, const set<int>& ro //remove deleted rows from grid removeRowsFromVector(grid, rowsToDeleteInGrid); } + + +bool FreeFileSync::foldersAreValidForComparison(const vector<FolderPair>& folderPairs, wxString& errorMessage) +{ + for (vector<FolderPair>::const_iterator i = folderPairs.begin(); i != folderPairs.end(); ++i) + { + const wxString leftFolderName = getFormattedDirectoryName(i->leftDirectory); + const wxString rightFolderName = getFormattedDirectoryName(i->rightDirectory); + + //check if folder name is empty + if (leftFolderName.IsEmpty() || rightFolderName.IsEmpty()) + { + errorMessage = _("Please fill all empty directory fields."); + return false; + } + + //check if folder exists + if (!wxDirExists(leftFolderName)) + { + errorMessage = wxString(_("Directory does not exist. Please select a new one: ")) + wxT("\"") + leftFolderName + wxT("\""); + return false; + } + if (!wxDirExists(rightFolderName)) + { + errorMessage = wxString(_("Directory does not exist. Please select a new one: ")) + wxT("\"") + rightFolderName + wxT("\""); + return false; + } + } + return true; +} + + |