summaryrefslogtreecommitdiff
path: root/synchronization.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'synchronization.cpp')
-rw-r--r--synchronization.cpp284
1 files changed, 177 insertions, 107 deletions
diff --git a/synchronization.cpp b/synchronization.cpp
index 487bceb9..d54101fd 100644
--- a/synchronization.cpp
+++ b/synchronization.cpp
@@ -3,46 +3,18 @@
#include <wx/msgdlg.h>
#include <wx/log.h>
#include "library/multithreading.h"
+#include "library/resources.h"
+#include "algorithm.h"
-#ifdef FFS_LINUX
+#ifdef FFS_WIN
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#elif defined FFS_LINUX
#include <utime.h>
#endif // FFS_LINUX
using namespace FreeFileSync;
-SyncProcess::SyncProcess(bool useRecycler, bool lineBreakOnMessages, StatusHandler* handler) :
- useRecycleBin(useRecycler),
- statusUpdater(handler),
- txtCopyingFile(_("Copying file %x to %y")),
- txtOverwritingFile(_("Copying file %x overwriting %y")),
- txtCreatingFolder(_("Creating folder %x")),
- txtDeletingFile(_("Deleting file %x")),
- txtDeletingFolder(_("Deleting folder %x"))
-{
- if (lineBreakOnMessages)
- {
- txtCopyingFile.Replace( wxT("%x"), wxT("\n\"%x\""), false);
- txtCopyingFile.Replace( wxT("%y"), wxT("\n\"%y\""), false);
- txtOverwritingFile.Replace(wxT("%x"), wxT("\n\"%x\""), false);
- txtOverwritingFile.Replace(wxT("%y"), wxT("\n\"%y\""), false);
- txtCreatingFolder.Replace( wxT("%x"), wxT("\n\"%x\""), false);
- txtDeletingFile.Replace( wxT("%x"), wxT("\n\"%x\""), false);
- txtDeletingFolder.Replace( wxT("%x"), wxT("\n\"%x\""), false);
- }
- else
- {
- txtCopyingFile.Replace( wxT("%x"), wxT("\"%x\""), false);
- txtCopyingFile.Replace( wxT("%y"), wxT("\"%y\""), false);
- txtOverwritingFile.Replace(wxT("%x"), wxT("\"%x\""), false);
- txtOverwritingFile.Replace(wxT("%y"), wxT("\"%y\""), false);
- txtCreatingFolder.Replace( wxT("%x"), wxT("\"%x\""), false);
- txtDeletingFile.Replace( wxT("%x"), wxT("\"%x\""), false);
- txtDeletingFolder.Replace( wxT("%x"), wxT("\"%x\""), false);
- }
-}
-
-
inline
SyncConfiguration::Direction getSyncDirection(const CompareFilesResult cmpResult, const SyncConfiguration& config)
{
@@ -75,6 +47,7 @@ SyncConfiguration::Direction getSyncDirection(const CompareFilesResult cmpResult
}
+inline
bool getBytesToTransfer(int& objectsToCreate,
int& objectsToOverwrite,
int& objectsToDelete,
@@ -157,9 +130,97 @@ bool getBytesToTransfer(int& objectsToCreate,
return true;
}
+
+void FreeFileSync::calcTotalBytesToSync(const FileCompareResult& fileCmpResult,
+ const SyncConfiguration& config,
+ int& objectsToCreate,
+ int& objectsToOverwrite,
+ int& objectsToDelete,
+ double& dataToProcess)
+{
+ objectsToCreate = 0;
+ objectsToOverwrite = 0;
+ objectsToDelete = 0;
+ dataToProcess = 0;
+
+ int toCreate = 0;
+ int toOverwrite = 0;
+ int toDelete = 0;
+ double data = 0;
+
+ for (FileCompareResult::const_iterator i = fileCmpResult.begin(); i != fileCmpResult.end(); ++i)
+ { //only sum up sizes of files AND directories
+ if (getBytesToTransfer(toCreate, toOverwrite, toDelete, data, *i, config))
+ {
+ objectsToCreate += toCreate;
+ objectsToOverwrite += toOverwrite;
+ objectsToDelete += toDelete;
+ dataToProcess += data;
+ }
+ }
+}
+
+
+bool FreeFileSync::synchronizationNeeded(const FileCompareResult& fileCmpResult, const SyncConfiguration& config)
+{
+ int objectsToCreate = 0;
+ int objectsToOverwrite = 0;
+ int objectsToDelete = 0;
+ double dataToProcess = 0;
+ FreeFileSync::calcTotalBytesToSync(fileCmpResult,
+ config,
+ objectsToCreate,
+ objectsToOverwrite,
+ objectsToDelete,
+ dataToProcess);
+ return objectsToCreate + objectsToOverwrite + objectsToDelete != 0;
+}
+
+
+//test if more then 50% of files will be deleted/overwritten
+bool significantDifferenceDetected(const FileCompareResult& fileCmpResult, const SyncConfiguration& config)
+{
+ int objectsToCreate = 0;
+ int objectsToOverwrite = 0;
+ int objectsToDelete = 0;
+ double dataToProcess = 0;
+ FreeFileSync::calcTotalBytesToSync(fileCmpResult,
+ config,
+ objectsToCreate,
+ objectsToOverwrite,
+ objectsToDelete,
+ dataToProcess);
+
+ const int changedFiles = objectsToOverwrite + objectsToDelete;
+
+ return changedFiles >= 10 && changedFiles > .5 * fileCmpResult.size();
+}
+
+
+SyncProcess::SyncProcess(const bool useRecycler, bool& warningSignificantDifference, StatusHandler* handler) :
+ useRecycleBin(useRecycler),
+ m_warningSignificantDifference(warningSignificantDifference),
+ statusUpdater(handler),
+ txtCopyingFile(_("Copying file %x to %y")),
+ txtOverwritingFile(_("Copying file %x overwriting %y")),
+ txtCreatingFolder(_("Creating folder %x")),
+ txtDeletingFile(_("Deleting file %x")),
+ txtDeletingFolder(_("Deleting folder %x"))
+{
+ txtCopyingFile.Replace( wxT("%x"), wxT("\n\"%x\""), false);
+ txtCopyingFile.Replace( wxT("%y"), wxT("\n\"%y\""), false);
+ txtOverwritingFile.Replace(wxT("%x"), wxT("\n\"%x\""), false);
+ txtOverwritingFile.Replace(wxT("%y"), wxT("\n\"%y\""), false);
+ txtCreatingFolder.Replace( wxT("%x"), wxT("\n\"%x\""), false);
+ txtDeletingFile.Replace( wxT("%x"), wxT("\n\"%x\""), false);
+ txtDeletingFolder.Replace( wxT("%x"), wxT("\n\"%x\""), false);
+}
+
+
void copyfileMultithreaded(const Zstring& source, const Zstring& target, StatusHandler* updateClass);
+inline
bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConfiguration& config)
{ //return false if nothing had to be done
@@ -186,7 +247,7 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf
statusText = txtCopyingFile;
statusText.Replace(wxT("%x"), cmpLine.fileDescrLeft.fullName, false);
- statusText.Replace(wxT("%y"), cmpLine.fileDescrRight.directory, false);
+ statusText.Replace(wxT("%y"), target, false);
statusUpdater->updateStatusText(statusText);
copyfileMultithreaded(cmpLine.fileDescrLeft.fullName, target, statusUpdater);
@@ -206,7 +267,7 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf
statusText = txtCopyingFile;
statusText.Replace(wxT("%x"), cmpLine.fileDescrRight.fullName, false);
- statusText.Replace(wxT("%y"), cmpLine.fileDescrLeft.directory, false);
+ statusText.Replace(wxT("%y"), target, false);
statusUpdater->updateStatusText(statusText);
copyfileMultithreaded(cmpLine.fileDescrRight.fullName, target, statusUpdater);
@@ -265,6 +326,7 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf
}
+inline
bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncConfiguration& config)
{ //false if nothing was to be done
assert (statusUpdater);
@@ -296,7 +358,7 @@ bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncCo
//some check to catch the error that directory on source has been deleted externally after "compare"...
if (!wxDirExists(cmpLine.fileDescrLeft.fullName))
- throw FileError(Zstring(_("Error: Source directory does not exist anymore:")) + wxT(" \"") + cmpLine.fileDescrLeft.fullName + wxT("\""));
+ throw FileError(Zstring(_("Error: Source directory does not exist anymore:")) + wxT("\n\"") + cmpLine.fileDescrLeft.fullName + wxT("\""));
createDirectory(target);
copyFolderAttributes(cmpLine.fileDescrLeft.fullName, target);
break;
@@ -319,7 +381,7 @@ bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncCo
//some check to catch the error that directory on source has been deleted externally after "compare"...
if (!wxDirExists(cmpLine.fileDescrRight.fullName))
- throw FileError(Zstring(_("Error: Source directory does not exist anymore:")) + wxT(" \"") + cmpLine.fileDescrRight.fullName + wxT("\""));
+ throw FileError(Zstring(_("Error: Source directory does not exist anymore:")) + wxT("\n\"") + cmpLine.fileDescrRight.fullName + wxT("\""));
createDirectory(target);
copyFolderAttributes(cmpLine.fileDescrRight.fullName, target);
break;
@@ -352,27 +414,26 @@ bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncCo
inline
bool deletionImminent(const FileCompareLine& line, const SyncConfiguration& config)
{ //test if current sync-line will result in deletion of files -> used to avoid disc space bottlenecks
- if ( (line.cmpResult == FILE_LEFT_SIDE_ONLY && config.exLeftSideOnly == SyncConfiguration::SYNC_DIR_LEFT) ||
- (line.cmpResult == FILE_RIGHT_SIDE_ONLY && config.exRightSideOnly == SyncConfiguration::SYNC_DIR_RIGHT))
+ if ( line.cmpResult == FILE_LEFT_SIDE_ONLY && config.exLeftSideOnly == SyncConfiguration::SYNC_DIR_LEFT ||
+ line.cmpResult == FILE_RIGHT_SIDE_ONLY && config.exRightSideOnly == SyncConfiguration::SYNC_DIR_RIGHT)
return true;
else
return false;
}
-class AlwaysWriteToGrid //this class ensures, that the result of the method below is ALWAYS written on exit, even if exceptions are thrown!
+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);
}
@@ -392,7 +453,28 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn
wxLogNull noWxLogs; //prevent wxWidgets logging
#endif
- AlwaysWriteToGrid writeOutput(grid); //ensure that grid is always written to, even if method is exitted via exceptions
+ //some basic checks:
+ //test existence of Recycle Bin
+ if (useRecycleBin && !FreeFileSync::recycleBinExists())
+ {
+ statusUpdater->reportFatalError(_("Unable to initialize Recycle Bin!"));
+ return; //should be obsolete!
+ }
+
+ //check if more than 50% of files/dirs are to be overwritten/deleted
+ if (m_warningSignificantDifference) //test if check should be executed
+ {
+ if (significantDifferenceDetected(grid, config))
+ {
+ bool dontShowAgain = false;
+ statusUpdater->reportWarning(_("Significant difference detected: More than 50% of files will be overwritten/deleted!"),
+ dontShowAgain);
+ m_warningSignificantDifference = !dontShowAgain;
+ }
+ }
+
+
+ RemoveAtExit markForRemoval(grid); //ensure that grid is always written to, even if method is exitted via exceptions
//inform about the total amount of data that will be processed from now on
int objectsToCreate = 0;
@@ -414,7 +496,7 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn
// and split into two "exists on one side only" cases
// Note: this case is not handled by this tool as this is considered to be a bug and must be solved by the user
- //synchronize folders:
+ //synchronize folders first; advantage: in case of deletions the whole folder is moved to recycle bin instead of single files
for (FileCompareResult::const_iterator i = grid.begin(); i != grid.end(); ++i)
{
if ( i->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY ||
@@ -429,15 +511,14 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn
{
if (synchronizeFolder(*i, config))
//progress indicator update
- //indicator is updated only if directory is synched correctly (and if some sync was done)!
+ //indicator is updated only if directory is sync'ed correctly (and if some work was done)!
statusUpdater->updateProcessedData(1, 0); //each call represents one processed file/directory
- writeOutput.rowProcessedSuccessfully(i - grid.begin());
+ markForRemoval.removeRow(i - grid.begin());
break;
}
catch (FileError& error)
{
- //if (updateClass) -> is mandatory
ErrorHandler::Response rv = statusUpdater->reportError(error.show());
if ( rv == ErrorHandler::IGNORE_ERROR)
@@ -451,6 +532,8 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn
}
}
+ //PERF_START;
+
//synchronize files:
bool deleteLoop = true;
for (int k = 0; k < 2; ++k) //loop over all files twice; reason: first delete, then copy (no sorting necessary: performance)
@@ -489,12 +572,11 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn
statusUpdater->updateProcessedData(objectsToCreate + objectsToOverwrite + objectsToDelete, dataToProcess);
}
- writeOutput.rowProcessedSuccessfully(i - grid.begin());
+ markForRemoval.removeRow(i - grid.begin());
break;
}
catch (FileError& error)
{
- //if (updateClass) -> is mandatory
ErrorHandler::Response rv = statusUpdater->reportError(error.show());
if ( rv == ErrorHandler::IGNORE_ERROR)
@@ -512,9 +594,8 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn
}
catch (const RuntimeException& theException)
{
- wxMessageBox(theException.show(), _("An exception occured!"), wxOK | wxICON_ERROR);
- statusUpdater->requestAbortion();
- return;
+ statusUpdater->reportFatalError(theException.show().c_str());
+ return; //should be obsolete!
}
}
@@ -535,21 +616,56 @@ public:
Zstring errorMessage;
private:
- void longRunner() //virtual impl.
+ virtual void longRunner()
{
+ //create folders first (see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080)
+ try
+ {
+ const Zstring targetDir = target.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ if (!wxDirExists(targetDir.c_str()))
+ {
+ FreeFileSync::createDirectory(targetDir);
+ }
+ }
+ catch (FileError& e)
+ {
+ success = false;
+ errorMessage = e.show();
+ return;
+ }
+
+
+#ifdef FFS_WIN
+//const DWORD COPY_FILE_COPY_SYMLINK = 0x00000800;
+
+ if (!CopyFileEx( //same performance as CopyFile()
+ source.c_str(),
+ target.c_str(),
+ NULL,
+ NULL,
+ NULL,
+ COPY_FILE_FAIL_IF_EXISTS))
+ {
+ success = false;
+ errorMessage = Zstring(_("Error copying file:")) + wxT("\n\"") + source + wxT("\" -> \"") + target + wxT("\"");
+ errorMessage += Zstring(wxT("\n\n"));
+ errorMessage += FreeFileSync::getLastErrorFormatted(); //some additional windows error information
+ return;
+ }
+
+#elif defined FFS_LINUX //copying files with Linux does not preserve the modification time => adapt after copying
if (!wxCopyFile(source.c_str(), target.c_str(), false)) //abort if file exists
{
success = false;
- errorMessage = Zstring(_("Error copying file:")) + wxT(" \"") + source + wxT("\" -> \"") + target + wxT("\"");
+ errorMessage = Zstring(_("Error copying file:")) + wxT("\n\"") + source + wxT("\" -> \"") + target + wxT("\"");
return;
}
-#ifdef FFS_LINUX //copying files with Linux does not preserve the modification time => adapt after copying
struct stat fileInfo;
if (stat(source.c_str(), &fileInfo) != 0) //read modification time from source file
{
success = false;
- errorMessage = Zstring(_("Could not retrieve file info for:")) + wxT(" \"") + source + wxT("\"");
+ errorMessage = Zstring(_("Could not retrieve file info for:")) + wxT("\n\"") + source + wxT("\"");
return;
}
@@ -560,7 +676,7 @@ private:
if (utime(target.c_str(), &newTimes) != 0)
{
success = false;
- errorMessage = Zstring(_("Error changing modification time:")) + wxT(" \"") + target + wxT("\"");
+ errorMessage = Zstring(_("Error changing modification time:")) + wxT("\n\"") + target + wxT("\"");
return;
}
#endif // FFS_LINUX
@@ -572,7 +688,7 @@ private:
void copyfileMultithreaded(const Zstring& source, const Zstring& target, StatusHandler* updateClass)
{
- static UpdateWhileCopying copyAndUpdate; //single instantiation: after each execution thread enters wait phase
+ static UpdateWhileCopying copyAndUpdate; //single instantiation: thread enters wait phase after each execution
copyAndUpdate.waitUntilReady();
@@ -586,49 +702,3 @@ void copyfileMultithreaded(const Zstring& source, const Zstring& target, StatusH
if (!copyAndUpdate.success)
throw FileError(copyAndUpdate.errorMessage);
}
-
-
-void FreeFileSync::calcTotalBytesToSync(const FileCompareResult& fileCmpResult,
- const SyncConfiguration& config,
- int& objectsToCreate,
- int& objectsToOverwrite,
- int& objectsToDelete,
- double& dataToProcess)
-{
- objectsToCreate = 0;
- objectsToOverwrite = 0;
- objectsToDelete = 0;
- dataToProcess = 0;
-
- int toCreate = 0;
- int toOverwrite = 0;
- int toDelete = 0;
- double data = 0;
-
- for (FileCompareResult::const_iterator i = fileCmpResult.begin(); i != fileCmpResult.end(); ++i)
- { //only sum up sizes of files AND directories
- if (getBytesToTransfer(toCreate, toOverwrite, toDelete, data, *i, config))
- {
- objectsToCreate += toCreate;
- objectsToOverwrite += toOverwrite;
- objectsToDelete += toDelete;
- dataToProcess += data;
- }
- }
-}
-
-
-bool FreeFileSync::synchronizationNeeded(const FileCompareResult& fileCmpResult, const SyncConfiguration& config)
-{
- int objectsToCreate = 0;
- int objectsToOverwrite = 0;
- int objectsToDelete = 0;
- double dataToProcess = 0;
- FreeFileSync::calcTotalBytesToSync(fileCmpResult,
- config,
- objectsToCreate,
- objectsToOverwrite,
- objectsToDelete,
- dataToProcess);
- return objectsToCreate + objectsToOverwrite + objectsToDelete != 0;
-}
bgstack15