diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 16:56:34 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 16:56:34 +0200 |
commit | 9084fa27f0f43cfa31dbc3a7ef87e2600c2dc3ca (patch) | |
tree | 61e2edc315a164d6fa3940b7de4b14dda0a9838c /algorithm.cpp | |
parent | 1.15 (diff) | |
download | FreeFileSync-9084fa27f0f43cfa31dbc3a7ef87e2600c2dc3ca.tar.gz FreeFileSync-9084fa27f0f43cfa31dbc3a7ef87e2600c2dc3ca.tar.bz2 FreeFileSync-9084fa27f0f43cfa31dbc3a7ef87e2600c2dc3ca.zip |
1.16
Diffstat (limited to 'algorithm.cpp')
-rw-r--r-- | algorithm.cpp | 352 |
1 files changed, 268 insertions, 84 deletions
diff --git a/algorithm.cpp b/algorithm.cpp index 87f7837f..c245e5ca 100644 --- a/algorithm.cpp +++ b/algorithm.cpp @@ -1,7 +1,6 @@ #include "algorithm.h" #include <wx/intl.h> #include <cmath> -#include <wx/filename.h> #include <wx/log.h> #include "library/resources.h" @@ -96,7 +95,7 @@ wxString FreeFileSync::formatFilesizeToShortString(const double filesize) Zstring FreeFileSync::getFormattedDirectoryName(const Zstring& dirname) -{ //Formatting is needed since functions in FreeFileSync.cpp expect the directory to end with '\' to be able to split the relative names. +{ //Formatting is needed since functions expect the directory to end with '\' to be able to split the relative names. //Also improves usability. Zstring dirnameTmp = dirname; @@ -106,17 +105,16 @@ Zstring FreeFileSync::getFormattedDirectoryName(const Zstring& dirname) if (dirnameTmp.empty()) //an empty string is interpreted as "\"; this is not desired return Zstring(); - //let wxWidgets do the directory formatting, e.g. replace '/' with '\' for Windows - wxFileName directory = wxFileName::DirName(dirnameTmp.c_str()); + if (!endsWithPathSeparator(dirnameTmp)) + dirnameTmp += GlobalResources::FILE_NAME_SEPARATOR; - return Zstring(directory.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR)); + //don't do directory formatting with wxFileName, as it doesn't respect //?/ - prefix + return dirnameTmp; } void FreeFileSync::swapGrids(FileCompareResult& grid) { - FileDescrLine tmp; - for (FileCompareResult::iterator i = grid.begin(); i != grid.end(); ++i) { //swap compare result @@ -130,13 +128,11 @@ void FreeFileSync::swapGrids(FileCompareResult& grid) i->cmpResult = FILE_RIGHT_NEWER; //swap file descriptors - tmp = i->fileDescrLeft; - i->fileDescrLeft = i->fileDescrRight; - i->fileDescrRight = tmp; + std::swap(i->fileDescrLeft, i->fileDescrRight); } } - +/* void FreeFileSync::adjustModificationTimes(const Zstring& parentDirectory, const int timeInSeconds, ErrorHandler* errorHandler) throw(AbortThisProcess) { #ifndef __WXDEBUG__ @@ -200,7 +196,7 @@ void FreeFileSync::adjustModificationTimes(const Zstring& parentDirectory, const } } } - +*/ void compoundStringToTable(const Zstring& compoundInput, const DefaultChar* delimiter, std::vector<Zstring>& output) { @@ -271,7 +267,7 @@ void formatFilterString(Zstring& filter) } -void FreeFileSync::filterCurrentGridData(FileCompareResult& currentGridData, const wxString& includeFilter, const wxString& excludeFilter) +void FreeFileSync::filterGridData(FileCompareResult& currentGridData, const wxString& includeFilter, const wxString& excludeFilter) { //no need for regular expressions! In tests wxRegex was by factor of 10 slower than wxString::Matches()!! @@ -372,7 +368,7 @@ void FreeFileSync::filterCurrentGridData(FileCompareResult& currentGridData, con } -void FreeFileSync::removeFilterOnCurrentGridData(FileCompareResult& currentGridData) +void FreeFileSync::includeAllRowsOnGrid(FileCompareResult& currentGridData) { //remove all filters on currentGridData for (FileCompareResult::iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) @@ -380,38 +376,147 @@ void FreeFileSync::removeFilterOnCurrentGridData(FileCompareResult& currentGridD } +void FreeFileSync::excludeAllRowsOnGrid(FileCompareResult& currentGridData) +{ + //exclude all rows on currentGridData + for (FileCompareResult::iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) + i->selectedForSynchronization = false; +} + + //add(!) all files and subfolder gridlines that are dependent from the directory -void FreeFileSync::addSubElements(std::set<int>& subElements, const FileCompareResult& grid, const FileCompareLine& relevantRow) +template <bool searchLeftSide> +void addSubElementsOneSide(const FileCompareResult& grid, const FileCompareLine& relevantRow, std::set<int>& subElements) { Zstring relevantDirectory; - if (relevantRow.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) - relevantDirectory = relevantRow.fileDescrLeft.relativeName + GlobalResources::FILE_NAME_SEPARATOR; //FILE_NAME_SEPARATOR needed to exclude subfile/dirs only - else if (relevantRow.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) - relevantDirectory = relevantRow.fileDescrRight.relativeName + GlobalResources::FILE_NAME_SEPARATOR; + const FileDescrLine* fileDescr = NULL; //get descriptor for file to be deleted; evaluated at compile time + if (searchLeftSide) + fileDescr = &relevantRow.fileDescrLeft; + else + fileDescr = &relevantRow.fileDescrRight; + + if (fileDescr->objType == FileDescrLine::TYPE_DIRECTORY) + relevantDirectory = fileDescr->relativeName + GlobalResources::FILE_NAME_SEPARATOR; //FILE_NAME_SEPARATOR needed to exclude subfile/dirs only else return; for (FileCompareResult::const_iterator i = grid.begin(); i != grid.end(); ++i) - if ( i->fileDescrLeft.relativeName.StartsWith(relevantDirectory) || - i->fileDescrRight.relativeName.StartsWith(relevantDirectory)) - subElements.insert(i - grid.begin()); + { + if (searchLeftSide) //evaluated at compile time + { + if (i->fileDescrLeft.relativeName.StartsWith(relevantDirectory)) + subElements.insert(i - grid.begin()); + } + else + { + if (i->fileDescrRight.relativeName.StartsWith(relevantDirectory)) + subElements.insert(i - grid.begin()); + } + } +} + + +//add(!) all files and subfolder gridlines that are dependent from the directory +void FreeFileSync::addSubElements(const FileCompareResult& grid, const FileCompareLine& relevantRow, std::set<int>& subElements) +{ + addSubElementsOneSide<true>(grid, relevantRow, subElements); + addSubElementsOneSide<false>(grid, relevantRow, subElements); } -class AlwaysWriteToGrid //this class ensures, that the result of the method below is ALWAYS written on exit, even if exceptions are thrown! +//############################################################################################################ +struct SortedFileName +{ + unsigned position; + Zstring name; + + bool operator < (const SortedFileName& b) const + { + return position < b.position; + } +}; + + +//assemble message containing all files to be deleted +wxString FreeFileSync::deleteFromGridAndHDPreview(const FileCompareResult& grid, + const std::set<int>& rowsToDeleteOnLeft, + const std::set<int>& rowsToDeleteOnRight, + const bool deleteOnBothSides) +{ + if (deleteOnBothSides) + { + //mix selected rows from left and right + std::set<int> rowsToDelete = rowsToDeleteOnLeft; + for (std::set<int>::const_iterator i = rowsToDeleteOnRight.begin(); i != rowsToDeleteOnRight.end(); ++i) + rowsToDelete.insert(*i); + + wxString filesToDelete; + for (std::set<int>::const_iterator i = rowsToDelete.begin(); i != rowsToDelete.end(); ++i) + { + const FileCompareLine& currentCmpLine = grid[*i]; + + if (currentCmpLine.fileDescrLeft.objType != FileDescrLine::TYPE_NOTHING) + filesToDelete += (currentCmpLine.fileDescrLeft.fullName + wxT("\n")).c_str(); + + if (currentCmpLine.fileDescrRight.objType != FileDescrLine::TYPE_NOTHING) + filesToDelete += (currentCmpLine.fileDescrRight.fullName + wxT("\n")).c_str(); + + filesToDelete+= wxT("\n"); + } + + return filesToDelete; + } + else //delete selected files only + { + std::set<SortedFileName> outputTable; //sort selected files from left and right ascending by row number + + SortedFileName newEntry; + for (std::set<int>::const_iterator i = rowsToDeleteOnLeft.begin(); i != rowsToDeleteOnLeft.end(); ++i) + { + const FileCompareLine& currentCmpLine = grid[*i]; + + if (currentCmpLine.fileDescrLeft.objType != FileDescrLine::TYPE_NOTHING) + { + newEntry.position = *i * 10; + newEntry.name = currentCmpLine.fileDescrLeft.fullName; + outputTable.insert(newEntry); + } + } + + for (std::set<int>::const_iterator i = rowsToDeleteOnRight.begin(); i != rowsToDeleteOnRight.end(); ++i) + { + const FileCompareLine& currentCmpLine = grid[*i]; + + if (currentCmpLine.fileDescrRight.objType != FileDescrLine::TYPE_NOTHING) + { + newEntry.position = *i * 10 + 1; //ensure files on left are before files on right for the same row number + newEntry.name = currentCmpLine.fileDescrRight.fullName; + outputTable.insert(newEntry); + } + } + + wxString filesToDelete; + for (std::set<SortedFileName>::iterator i = outputTable.begin(); i != outputTable.end(); ++i) + filesToDelete += (i->name + wxT("\n")).c_str(); + + return filesToDelete; + } +} + + +class RemoveAtExit //this class ensures, that the result of the method below is ALWAYS written on exit, even if exceptions are thrown! { public: - AlwaysWriteToGrid(FileCompareResult& grid) : - gridToWrite(grid) - {} + RemoveAtExit(FileCompareResult& grid) : + gridToWrite(grid) {} - ~AlwaysWriteToGrid() + ~RemoveAtExit() { removeRowsFromVector(gridToWrite, rowsProcessed); } - void rowProcessedSuccessfully(int nr) + void removeRow(int nr) { rowsProcessed.insert(nr); } @@ -422,40 +527,84 @@ private: }; -void FreeFileSync::deleteOnGridAndHD(FileCompareResult& grid, const std::set<int>& rowsToDelete, ErrorHandler* errorHandler, const bool useRecycleBin) throw(AbortThisProcess) +template <bool leftSide> //update compareGrid row information after deletion from leftSide (or !leftSide) +inline +void updateCmpLineAfterDeletion(FileCompareLine& relevantRow, const int rowNr, RemoveAtExit& markForRemoval) { - //remove deleted rows from grid - AlwaysWriteToGrid writeOutput(grid); //ensure that grid is always written to, even if method is exitted via exceptions + FileDescrLine* fileDescr = NULL; //get descriptor for file to be deleted; evaluated at compile time + FileDescrLine* fileDescrPartner = NULL; //file descriptor for "other side" + if (leftSide) + { + fileDescr = &relevantRow.fileDescrLeft; + fileDescrPartner = &relevantRow.fileDescrRight; + } + else + { + fileDescr = &relevantRow.fileDescrRight; + fileDescrPartner = &relevantRow.fileDescrLeft; + } + - //remove from hd - for (std::set<int>::iterator i = rowsToDelete.begin(); i != rowsToDelete.end(); ++i) + //remove deleted entries from grid + if (fileDescrPartner->objType == FileDescrLine::TYPE_NOTHING) + markForRemoval.removeRow(rowNr); + else { - const FileCompareLine& currentCmpLine = grid[*i]; + //initialize fileDescr for deleted file/folder + const Zstring saveDir = fileDescr->directory; + *fileDescr = FileDescrLine(); + fileDescr->directory = saveDir; + + //adapt the compare result + if (leftSide) //again evaluated at compile time + relevantRow.cmpResult = FILE_RIGHT_SIDE_ONLY; + else + relevantRow.cmpResult = FILE_LEFT_SIDE_ONLY; + } +} + + +template <bool leftSide> +void deleteFromGridAndHDOneSide(FileCompareResult& grid, + const std::set<int>& rowsToDeleteOneSide, + const bool useRecycleBin, + RemoveAtExit& markForRemoval, + ErrorHandler* errorHandler) +{ + for (std::set<int>::iterator i = rowsToDeleteOneSide.begin(); i != rowsToDeleteOneSide.end(); ++i) + { + FileCompareLine& currentCmpLine = grid[*i]; + + FileDescrLine* fileDescr = NULL; //get descriptor for file to be deleted; evaluated at compile time + if (leftSide) + fileDescr = ¤tCmpLine.fileDescrLeft; + else + fileDescr = ¤tCmpLine.fileDescrRight; while (true) { try { - if (currentCmpLine.fileDescrLeft.objType == FileDescrLine::TYPE_FILE) - FreeFileSync::removeFile(currentCmpLine.fileDescrLeft.fullName, useRecycleBin); - else if (currentCmpLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) - FreeFileSync::removeDirectory(currentCmpLine.fileDescrLeft.fullName, useRecycleBin); - - if (currentCmpLine.fileDescrRight.objType == FileDescrLine::TYPE_FILE) - FreeFileSync::removeFile(currentCmpLine.fileDescrRight.fullName, useRecycleBin); - else if (currentCmpLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) - FreeFileSync::removeDirectory(currentCmpLine.fileDescrRight.fullName, useRecycleBin); - - //remove deleted row from grid - writeOutput.rowProcessedSuccessfully(*i); + if (fileDescr->objType == FileDescrLine::TYPE_FILE) + FreeFileSync::removeFile(fileDescr->fullName, useRecycleBin); + else if (fileDescr->objType == FileDescrLine::TYPE_DIRECTORY) + FreeFileSync::removeDirectory(fileDescr->fullName, useRecycleBin); + else if (fileDescr->objType == FileDescrLine::TYPE_NOTHING) + break; //nothing to do + else + { + assert(false); + break; + } //retrieve all files and subfolder gridlines that are dependent from this deleted entry - std::set<int> additionalRowsToDelete; - addSubElements(additionalRowsToDelete, grid, grid[*i]); + std::set<int> rowsToDelete; + rowsToDelete.insert(*i); + addSubElementsOneSide<leftSide>(grid, grid[*i], rowsToDelete); - //...and remove them also - for (std::set<int>::iterator j = additionalRowsToDelete.begin(); j != additionalRowsToDelete.end(); ++j) - writeOutput.rowProcessedSuccessfully(*j); + //remove deleted entries from grid (or adapt it if deleted from one side only) + for (std::set<int>::iterator j = rowsToDelete.begin(); j != rowsToDelete.end(); ++j) + updateCmpLineAfterDeletion<leftSide>(grid[*j], *j, markForRemoval); break; } @@ -477,6 +626,53 @@ void FreeFileSync::deleteOnGridAndHD(FileCompareResult& grid, const std::set<int } +void FreeFileSync::deleteFromGridAndHD(FileCompareResult& grid, + const std::set<int>& rowsToDeleteOnLeft, + const std::set<int>& rowsToDeleteOnRight, + const bool deleteOnBothSides, + const bool useRecycleBin, + ErrorHandler* errorHandler) +{ + //remove deleted rows from grid (AFTER all rows to be deleted are known: consider row references! + RemoveAtExit markForRemoval(grid); //ensure that grid is always written to, even if method is exitted via exceptions + + if (deleteOnBothSides) + { + //mix selected rows from left and right + std::set<int> rowsToDeleteBothSides = rowsToDeleteOnLeft; + for (std::set<int>::const_iterator i = rowsToDeleteOnRight.begin(); i != rowsToDeleteOnRight.end(); ++i) + rowsToDeleteBothSides.insert(*i); + + deleteFromGridAndHDOneSide<true>(grid, + rowsToDeleteBothSides, + useRecycleBin, + markForRemoval, + errorHandler); + + deleteFromGridAndHDOneSide<false>(grid, + rowsToDeleteBothSides, + useRecycleBin, + markForRemoval, + errorHandler); + } + else + { + deleteFromGridAndHDOneSide<true>(grid, + rowsToDeleteOnLeft, + useRecycleBin, + markForRemoval, + errorHandler); + + deleteFromGridAndHDOneSide<false>(grid, + rowsToDeleteOnRight, + useRecycleBin, + markForRemoval, + errorHandler); + } +} +//############################################################################################################ + + inline void writeTwoDigitNumber(unsigned int number, wxChar*& position) { @@ -565,43 +761,23 @@ wxString FreeFileSync::utcTimeToLocalString(const time_t utcTime) #ifdef FFS_WIN -inline -Zstring getDriveName(const Zstring& directoryName) //GetVolume() doesn't work under Linux! -{ - const Zstring volumeName = wxFileName(directoryName.c_str()).GetVolume().c_str(); - if (volumeName.empty()) - return Zstring(); - - return volumeName + wxFileName::GetVolumeSeparator().c_str() + GlobalResources::FILE_NAME_SEPARATOR; -} -#endif //FFS_WIN - - -#ifdef FFS_WIN -bool FreeFileSync::isFatDrive(const Zstring& directoryName) +Zstring FreeFileSync::getLastErrorFormatted(const unsigned long lastError) //try to get additional windows error information { - const Zstring driveName = getDriveName(directoryName); - if (driveName.empty()) - return false; + unsigned long lastErrorCode = lastError; + //determine error code if none was specified + if (lastErrorCode == 0) + lastErrorCode = GetLastError(); - wxChar fileSystem[32]; - if (!GetVolumeInformation(driveName.c_str(), NULL, 0, NULL, NULL, NULL, fileSystem, 32)) - return false; + Zstring output = Zstring(wxT("Windows Error Code ")) + wxString::Format(wxT("%u"), lastErrorCode).c_str(); - return Zstring(fileSystem).StartsWith(wxT("FAT")); + WCHAR buffer[1001]; + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, lastErrorCode, 0, buffer, 1001, NULL) != 0) + output += Zstring(wxT(": ")) + buffer; + return output; } #endif -inline -bool sameFileTime(const time_t a, const time_t b) -{ - if (a < b) - return b - a <= FILE_TIME_PRECISION; - else - return a - b <= FILE_TIME_PRECISION; -} - /*Statistical theory: detect daylight saving time (DST) switch by comparing files that exist on both sides (and have same filesizes). If there are "enough" that have a shift by +-1h then assert that DST switch occured. @@ -642,7 +818,16 @@ N(M) = 77/1000000 * M + 10 for 50000 < M <= 400000 60/1000000 * M + 35 for 400000 < M -*/ + +inline +bool sameFileTime(const time_t a, const time_t b) +{ + if (a < b) + return b - a <= FILE_TIME_PRECISION; + else + return a - b <= FILE_TIME_PRECISION; +} + #ifdef FFS_WIN unsigned int getThreshold(const unsigned filesWithSameSizeTotal) @@ -745,5 +930,4 @@ void FreeFileSync::checkForDSTChange(const FileCompareResult& gridData, } } #endif //FFS_WIN - - +*/ |