summaryrefslogtreecommitdiff
path: root/synchronization.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'synchronization.cpp')
-rw-r--r--synchronization.cpp570
1 files changed, 306 insertions, 264 deletions
diff --git a/synchronization.cpp b/synchronization.cpp
index 66108ade..896de7ce 100644
--- a/synchronization.cpp
+++ b/synchronization.cpp
@@ -12,6 +12,11 @@
#include <boost/bind.hpp>
#include "shared/globalFunctions.h"
#include <boost/scoped_array.hpp>
+#include <memory>
+
+#ifdef FFS_WIN
+#include "shared/shadow.h"
+#endif
using namespace FreeFileSync;
@@ -322,6 +327,9 @@ bool significantDifferenceDetected(const SyncStatistics& folderPairStat)
}
+//#################################################################################################################
+
+
/*add some postfix to alternate deletion directory: customDir\2009-06-30 12-59-12\ */
Zstring getSessionDeletionDir(const Zstring& customDeletionDirectory)
{
@@ -359,72 +367,231 @@ SyncProcess::SyncProcess(const bool copyFileSymLinks,
const bool traverseDirSymLinks,
xmlAccess::OptionalDialogs& warnings,
const bool verifyCopiedFiles,
- StatusHandler* handler) :
+ StatusHandler& handler) :
m_copyFileSymLinks(copyFileSymLinks),
m_traverseDirSymLinks(traverseDirSymLinks),
m_verifyCopiedFiles(verifyCopiedFiles),
m_warnings(warnings),
+ statusUpdater(handler) {}
+
+
+struct DeletionHandling
+{
+ DeletionHandling(const DeletionPolicy handleDel,
+ const Zstring& custDelFolder) :
+ handleDeletion(handleDel),
+ currentDelFolder(getSessionDeletionDir(custDelFolder)), //ends with path separator
+ txtMoveFileUserDefined( wxToZ(_("Moving file %x to user-defined directory %y")). Replace(DefaultStr("%x"), DefaultStr("\"%x\"\n"), false).Replace(DefaultStr("%y"), Zstring(DefaultStr("\"")) + custDelFolder + DefaultStr("\""), false)),
+ txtMoveFolderUserDefined(wxToZ(_("Moving folder %x to user-defined directory %y")).Replace(DefaultStr("%x"), DefaultStr("\"%x\"\n"), false).Replace(DefaultStr("%y"), Zstring(DefaultStr("\"")) + custDelFolder + DefaultStr("\""), false))
+ {}
+
+ DeletionPolicy handleDeletion;
+ Zstring currentDelFolder; //alternate deletion folder for current folder pair (with timestamp, ends with path separator)
+ //preloaded status texts:
+ const Zstring txtMoveFileUserDefined;
+ const Zstring txtMoveFolderUserDefined;
+};
+
+
+template <typename Function>
+inline
+void tryReportingError(StatusHandler& handler, Function cmd)
+{
+ while (true)
+ {
+ try
+ {
+ cmd();
+ break;
+ }
+ catch (FileError& error)
+ {
+ //User abort when copying files or moving files/directories into custom deletion directory:
+ //windows build: abort if requested, don't show error message if cancelled by user!
+ //linux build: this refresh is not necessary, because user abort triggers an AbortThisProcess() exception without a FileError()
+ handler.requestUiRefresh(true); //may throw!
+
+ ErrorHandler::Response rv = handler.reportError(error.show()); //may throw!
+ if ( rv == ErrorHandler::IGNORE_ERROR)
+ break;
+ else if (rv == ErrorHandler::RETRY)
+ ; //continue with loop
+ else
+ throw std::logic_error("Programming Error: Unknown return value!");
+ }
+ }
+}
+
+
+inline
+bool deletionImminent(const FileSystemObject& fsObj)
+{
+ //test if current sync-line will result in deletion of files -> used to avoid disc space bottlenecks
+ const SyncOperation op = fsObj.getSyncOperation();
+ return op == FreeFileSync::SO_DELETE_LEFT || op == FreeFileSync::SO_DELETE_RIGHT;
+}
+
+
+class RemoveInvalid
+{
+public:
+ RemoveInvalid(HierarchyObject& hierObj) :
+ hierObj_(hierObj) {}
+
+ ~RemoveInvalid()
+ {
+ FileSystemObject::removeEmptyNonRec(hierObj_);
+ }
+
+private:
+ HierarchyObject& hierObj_;
+};
+
+
+class FreeFileSync::SyncRecursively
+{
+public:
+ SyncRecursively(const SyncProcess& syncProc,
#ifdef FFS_WIN
- shadowCopyHandler(new ShadowCopy),
+ ShadowCopy& shadowCopyHandler,
#endif
- statusUpdater(handler),
- txtCopyingFile(wxToZ(_("Copying file %x to %y")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false).Replace(DefaultStr("%y"), DefaultStr("\n\"%y\""), false)),
- txtOverwritingFile(wxToZ(_("Copying file %x to %y overwriting target")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false).Replace(DefaultStr("%y"), DefaultStr("\n\"%y\""), false)),
- txtCreatingFolder(wxToZ(_("Creating folder %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false)),
- txtDeletingFile(wxToZ(_("Deleting file %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false)),
- txtDeletingFolder(wxToZ(_("Deleting folder %x")).Replace( DefaultStr("%x"), DefaultStr("\n\"%x\""), false)),
- txtMoveToRecycler(wxToZ(_("Moving %x to Recycle Bin")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false)),
- txtVerifying(wxToZ(_("Verifying file %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false))
-{}
+ const DeletionHandling& delHandling) :
+ statusUpdater_(syncProc.statusUpdater),
+#ifdef FFS_WIN
+ shadowCopyHandler_(shadowCopyHandler),
+#endif
+ delHandling_(delHandling),
+ copyFileSymLinks_(syncProc.m_copyFileSymLinks),
+ traverseDirSymLinks_(syncProc.m_traverseDirSymLinks),
+ verifyCopiedFiles_(syncProc.m_verifyCopiedFiles),
+ txtCopyingFile(wxToZ(_("Copying file %x to %y")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false).Replace(DefaultStr("%y"), DefaultStr("\n\"%y\""), false)),
+ txtOverwritingFile(wxToZ(_("Copying file %x to %y overwriting target")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false).Replace(DefaultStr("%y"), DefaultStr("\n\"%y\""), false)),
+ txtCreatingFolder(wxToZ(_("Creating folder %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false)),
+ txtDeletingFile(wxToZ(_("Deleting file %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false)),
+ txtDeletingFolder(wxToZ(_("Deleting folder %x")).Replace( DefaultStr("%x"), DefaultStr("\n\"%x\""), false)),
+ txtMoveToRecycler(wxToZ(_("Moving %x to Recycle Bin")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false)),
+ txtVerifying(wxToZ(_("Verifying file %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false)) {}
+
+ template <bool deleteOnly> //"true" if files deletion shall happen only
+ void execute(HierarchyObject& hierObj)
+ {
+ //enforce removal of invalid entries (where both sides are empty)
+ RemoveInvalid dummy(hierObj); //non-recursive
+
+ //synchronize files:
+ for (HierarchyObject::SubFileMapping::iterator i = hierObj.subFiles.begin(); i != hierObj.subFiles.end(); ++i)
+ {
+ if ( ( deleteOnly && deletionImminent(*i)) ||
+ (!deleteOnly && !deletionImminent(*i)))
+ tryReportingError(statusUpdater_, boost::bind(&SyncRecursively::synchronizeFile, this, boost::ref(*i)));
+ }
+
+ //synchronize folders:
+ for (HierarchyObject::SubDirMapping::iterator i = hierObj.subDirs.begin(); i != hierObj.subDirs.end(); ++i)
+ {
+ const SyncOperation syncOp = i->getSyncOperation();
+
+ if ( ( deleteOnly && deletionImminent(*i)) || //ensure folder creation happens in second pass, to enable time adaption below
+ (!deleteOnly && !deletionImminent(*i))) //
+ tryReportingError(statusUpdater_, boost::bind(&SyncRecursively::synchronizeFolder, this, boost::ref(*i)));
+
+ //recursive synchronization:
+ execute<deleteOnly>(*i);
+
+ //adapt folder modification dates: apply AFTER all subobjects have been synced to preserve folder modification date!
+ switch (syncOp)
+ {
+ case SO_CREATE_NEW_LEFT:
+ copyFileTimes(i->getFullName<RIGHT_SIDE>(), i->getFullName<LEFT_SIDE>()); //throw()
+ break;
+ case SO_CREATE_NEW_RIGHT:
+ copyFileTimes(i->getFullName<LEFT_SIDE>(), i->getFullName<RIGHT_SIDE>()); //throw()
+ break;
+ case SO_OVERWRITE_RIGHT:
+ case SO_OVERWRITE_LEFT:
+ case SO_UNRESOLVED_CONFLICT:
+ assert(false);
+ case SO_DELETE_LEFT:
+ case SO_DELETE_RIGHT:
+ case SO_DO_NOTHING:
+ ;
+ }
+ }
+ }
+
+private:
+ void synchronizeFile(FileMapping& fileObj) const;
+ void synchronizeFolder(DirMapping& dirObj) const;
+
+ template <FreeFileSync::SelectedSide side>
+ void removeFile(const FileMapping& fileObj, bool showStatusUpdate) const;
+ template <FreeFileSync::SelectedSide side>
+ void removeFolder(const DirMapping& dirObj) const;
-SyncProcess::DeletionHandling::DeletionHandling(const DeletionPolicy handleDel,
- const Zstring& custDelFolder) :
- handleDeletion(handleDel),
- currentDelFolder(getSessionDeletionDir(custDelFolder)), //ends with path separator
- txtMoveFileUserDefined( wxToZ(_("Moving file %x to user-defined directory %y")). Replace(DefaultStr("%x"), DefaultStr("\"%x\"\n"), false).Replace(DefaultStr("%y"), Zstring(DefaultStr("\"")) + custDelFolder + DefaultStr("\""), false)),
- txtMoveFolderUserDefined(wxToZ(_("Moving folder %x to user-defined directory %y")).Replace(DefaultStr("%x"), DefaultStr("\"%x\"\n"), false).Replace(DefaultStr("%y"), Zstring(DefaultStr("\"")) + custDelFolder + DefaultStr("\""), false))
-{}
+ void copyFileUpdating(const Zstring& source, const Zstring& target, const wxULongLong& sourceFileSize) const;
+ void verifyFileCopy(const Zstring& source, const Zstring& target) const;
+ StatusHandler& statusUpdater_;
+#ifdef FFS_WIN
+ ShadowCopy& shadowCopyHandler_;
+#endif
+ const DeletionHandling& delHandling_;
+
+ const bool copyFileSymLinks_;
+ const bool traverseDirSymLinks_;
+ const bool verifyCopiedFiles_;
+
+ //preload status texts
+ const Zstring txtCopyingFile;
+ const Zstring txtOverwritingFile;
+ const Zstring txtCreatingFolder;
+ const Zstring txtDeletingFile;
+ const Zstring txtDeletingFolder;
+ const Zstring txtMoveToRecycler;
+ const Zstring txtVerifying;
+};
+
+
class MoveFileCallbackImpl : public MoveFileCallback //callback functionality
{
public:
- MoveFileCallbackImpl(StatusHandler* handler) : m_statusHandler(handler) {}
+ MoveFileCallbackImpl(StatusHandler& handler) : statusHandler_(handler) {}
virtual Response requestUiRefresh() //DON'T throw exceptions here, at least in Windows build!
{
#ifdef FFS_WIN
- m_statusHandler->requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this
- if (m_statusHandler->abortIsRequested())
+ statusHandler_.requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this
+ if (statusHandler_.abortIsRequested())
return MoveFileCallback::CANCEL;
#elif defined FFS_LINUX
- m_statusHandler->requestUiRefresh(); //exceptions may be thrown here!
+ statusHandler_.requestUiRefresh(); //exceptions may be thrown here!
#endif
return MoveFileCallback::CONTINUE;
}
private:
- StatusHandler* m_statusHandler;
+ StatusHandler& statusHandler_;
};
template <FreeFileSync::SelectedSide side>
inline
-void SyncProcess::removeFile(const FileMapping& fileObj, const DeletionHandling& delHandling, bool showStatusUpdate) const
+void SyncRecursively::removeFile(const FileMapping& fileObj, bool showStatusUpdate) const
{
Zstring statusText;
- switch (delHandling.handleDeletion)
+ switch (delHandling_.handleDeletion)
{
case FreeFileSync::DELETE_PERMANENTLY:
if (showStatusUpdate) //status information
{
statusText = txtDeletingFile;
statusText.Replace(DefaultStr("%x"), fileObj.getFullName<side>(), false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
}
FreeFileSync::removeFile(fileObj.getFullName<side>(), false);
break;
@@ -433,8 +600,8 @@ void SyncProcess::removeFile(const FileMapping& fileObj, const DeletionHandling&
{
statusText = txtMoveToRecycler;
statusText.Replace(DefaultStr("%x"), fileObj.getFullName<side>(), false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
}
FreeFileSync::removeFile(fileObj.getFullName<side>(), true);
break;
@@ -443,12 +610,12 @@ void SyncProcess::removeFile(const FileMapping& fileObj, const DeletionHandling&
{
if (showStatusUpdate) //status information
{
- statusText = delHandling.txtMoveFileUserDefined;
+ statusText = delHandling_.txtMoveFileUserDefined;
statusText.Replace(DefaultStr("%x"), fileObj.getFullName<side>(), false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
}
- const Zstring targetFile = delHandling.currentDelFolder + fileObj.getRelativeName<side>(); //altDeletionDir ends with path separator
+ const Zstring targetFile = delHandling_.currentDelFolder + fileObj.getRelativeName<side>(); //altDeletionDir ends with path separator
const Zstring targetDir = targetFile.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR);
if (!FreeFileSync::dirExists(targetDir))
@@ -463,7 +630,7 @@ void SyncProcess::removeFile(const FileMapping& fileObj, const DeletionHandling&
=> setting NEEDS to be false: We want to move leaf, therefore symlinks in "some\dirs" must not interfere */
}
- MoveFileCallbackImpl callBack(statusUpdater); //if file needs to be copied we need callback functionality to update screen and offer abort
+ MoveFileCallbackImpl callBack(statusUpdater_); //if file needs to be copied we need callback functionality to update screen and offer abort
FreeFileSync::moveFile(fileObj.getFullName<side>(), targetFile, &callBack);
}
break;
@@ -471,7 +638,7 @@ void SyncProcess::removeFile(const FileMapping& fileObj, const DeletionHandling&
}
-void SyncProcess::synchronizeFile(FileMapping& fileObj, const DeletionHandling& delHandling) const
+void SyncRecursively::synchronizeFile(FileMapping& fileObj) const
{
Zstring statusText;
Zstring target;
@@ -484,8 +651,8 @@ void SyncProcess::synchronizeFile(FileMapping& fileObj, const DeletionHandling&
statusText = txtCopyingFile;
statusText.Replace(DefaultStr("%x"), fileObj.getShortName<RIGHT_SIDE>(), false);
statusText.Replace(DefaultStr("%y"), target.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR), false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target, fileObj.getFileSize<RIGHT_SIDE>());
break;
@@ -496,28 +663,28 @@ void SyncProcess::synchronizeFile(FileMapping& fileObj, const DeletionHandling&
statusText = txtCopyingFile;
statusText.Replace(DefaultStr("%x"), fileObj.getShortName<LEFT_SIDE>(), false);
statusText.Replace(DefaultStr("%y"), target.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR), false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target, fileObj.getFileSize<LEFT_SIDE>());
break;
case SO_DELETE_LEFT:
- removeFile<LEFT_SIDE>(fileObj, delHandling, true); //status updates in subroutine
+ removeFile<LEFT_SIDE>(fileObj, true); //status updates in subroutine
break;
case SO_DELETE_RIGHT:
- removeFile<RIGHT_SIDE>(fileObj, delHandling, true); //status updates in subroutine
+ removeFile<RIGHT_SIDE>(fileObj, true); //status updates in subroutine
break;
case SO_OVERWRITE_RIGHT:
statusText = txtOverwritingFile;
statusText.Replace(DefaultStr("%x"), fileObj.getShortName<LEFT_SIDE>(), false);
statusText.Replace(DefaultStr("%y"), fileObj.getFullName<RIGHT_SIDE>().BeforeLast(globalFunctions::FILE_NAME_SEPARATOR), false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
- removeFile<RIGHT_SIDE>(fileObj, delHandling, false);
+ removeFile<RIGHT_SIDE>(fileObj, false);
copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), fileObj.getFullName<RIGHT_SIDE>(), fileObj.getFileSize<LEFT_SIDE>());
break;
@@ -525,10 +692,10 @@ void SyncProcess::synchronizeFile(FileMapping& fileObj, const DeletionHandling&
statusText = txtOverwritingFile;
statusText.Replace(DefaultStr("%x"), fileObj.getShortName<RIGHT_SIDE>(), false);
statusText.Replace(DefaultStr("%y"), fileObj.getFullName<LEFT_SIDE>().BeforeLast(globalFunctions::FILE_NAME_SEPARATOR), false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
- removeFile<LEFT_SIDE>(fileObj, delHandling, false);
+ removeFile<LEFT_SIDE>(fileObj, false);
copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), fileObj.getFullName<LEFT_SIDE>(), fileObj.getFileSize<RIGHT_SIDE>());
break;
@@ -542,24 +709,24 @@ void SyncProcess::synchronizeFile(FileMapping& fileObj, const DeletionHandling&
//progress indicator update
//indicator is updated only if file is sync'ed correctly (and if some sync was done)!
- statusUpdater->updateProcessedData(1, 0); //processed data is communicated in subfunctions!
+ statusUpdater_.updateProcessedData(1, 0); //processed data is communicated in subfunctions!
}
template <FreeFileSync::SelectedSide side>
inline
-void SyncProcess::removeFolder(const DirMapping& dirObj, const DeletionHandling& delHandling) const
+void SyncRecursively::removeFolder(const DirMapping& dirObj) const
{
Zstring statusText;
- switch (delHandling.handleDeletion)
+ switch (delHandling_.handleDeletion)
{
case FreeFileSync::DELETE_PERMANENTLY:
//status information
statusText = txtDeletingFolder;
statusText.Replace(DefaultStr("%x"), dirObj.getFullName<side>(), false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
FreeFileSync::removeDirectory(dirObj.getFullName<side>(), false);
break;
@@ -567,8 +734,8 @@ void SyncProcess::removeFolder(const DirMapping& dirObj, const DeletionHandling&
//status information
statusText = txtMoveToRecycler;
statusText.Replace(DefaultStr("%x"), dirObj.getFullName<side>(), false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
FreeFileSync::removeDirectory(dirObj.getFullName<side>(), true);
break;
@@ -576,12 +743,12 @@ void SyncProcess::removeFolder(const DirMapping& dirObj, const DeletionHandling&
if (FreeFileSync::dirExists(dirObj.getFullName<side>()))
{
//status information
- statusText = delHandling.txtMoveFolderUserDefined;
+ statusText = delHandling_.txtMoveFolderUserDefined;
statusText.Replace(DefaultStr("%x"), dirObj.getFullName<side>(), false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
- const Zstring targetDir = delHandling.currentDelFolder + dirObj.getRelativeName<side>();
+ const Zstring targetDir = delHandling_.currentDelFolder + dirObj.getRelativeName<side>();
const Zstring targetSuperDir = targetDir.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR);;
if (!FreeFileSync::dirExists(targetSuperDir))
@@ -596,7 +763,7 @@ void SyncProcess::removeFolder(const DirMapping& dirObj, const DeletionHandling&
=> setting NEEDS to be false: We want to move leaf, therefore symlinks in "some\dirs" must not interfere */
}
- MoveFileCallbackImpl callBack(statusUpdater); //if files need to be copied, we need callback functionality to update screen and offer abort
+ MoveFileCallbackImpl callBack(statusUpdater_); //if files need to be copied, we need callback functionality to update screen and offer abort
FreeFileSync::moveDirectory(dirObj.getFullName<side>(), targetDir, true, &callBack);
}
break;
@@ -604,7 +771,7 @@ void SyncProcess::removeFolder(const DirMapping& dirObj, const DeletionHandling&
}
-void SyncProcess::synchronizeFolder(DirMapping& dirObj, const DeletionHandling& delHandling) const
+void SyncRecursively::synchronizeFolder(DirMapping& dirObj) const
{
Zstring statusText;
Zstring target;
@@ -617,13 +784,13 @@ void SyncProcess::synchronizeFolder(DirMapping& dirObj, const DeletionHandling&
statusText = txtCreatingFolder;
statusText.Replace(DefaultStr("%x"), target, false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
//some check to catch the error that directory on source has been deleted externally after "compare"...
if (!FreeFileSync::dirExists(dirObj.getFullName<RIGHT_SIDE>()))
throw FileError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(dirObj.getFullName<RIGHT_SIDE>()) + wxT("\""));
- createDirectory(target, dirObj.getFullName<RIGHT_SIDE>(), !m_traverseDirSymLinks); //traverse symlinks <=> !copy symlinks
+ createDirectory(target, dirObj.getFullName<RIGHT_SIDE>(), !traverseDirSymLinks_); //traverse symlinks <=> !copy symlinks
break;
case SO_CREATE_NEW_RIGHT:
@@ -631,36 +798,36 @@ void SyncProcess::synchronizeFolder(DirMapping& dirObj, const DeletionHandling&
statusText = txtCreatingFolder;
statusText.Replace(DefaultStr("%x"), target, false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
//some check to catch the error that directory on source has been deleted externally after "compare"...
if (!FreeFileSync::dirExists(dirObj.getFullName<LEFT_SIDE>()))
throw FileError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(dirObj.getFullName<LEFT_SIDE>()) + wxT("\""));
- createDirectory(target, dirObj.getFullName<LEFT_SIDE>(), !m_traverseDirSymLinks); //traverse symlinks <=> !copy symlinks
+ createDirectory(target, dirObj.getFullName<LEFT_SIDE>(), !traverseDirSymLinks_); //traverse symlinks <=> !copy symlinks
break;
case SO_DELETE_LEFT:
- removeFolder<LEFT_SIDE>(dirObj, delHandling);
+ removeFolder<LEFT_SIDE>(dirObj);
{
//progress indicator update: DON'T forget to notify about implicitly deleted objects!
const SyncStatistics subObjects(dirObj);
//...then remove everything
dirObj.subFiles.clear();
dirObj.subDirs.clear();
- statusUpdater->updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble());
+ statusUpdater_.updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble());
}
break;
case SO_DELETE_RIGHT:
- removeFolder<RIGHT_SIDE>(dirObj, delHandling);
+ removeFolder<RIGHT_SIDE>(dirObj);
{
//progress indicator update: DON'T forget to notify about implicitly deleted objects!
const SyncStatistics subObjects(dirObj);
//...then remove everything
dirObj.subFiles.clear();
dirObj.subDirs.clear();
- statusUpdater->updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble());
+ statusUpdater_.updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble());
}
break;
@@ -677,146 +844,7 @@ void SyncProcess::synchronizeFolder(DirMapping& dirObj, const DeletionHandling&
//progress indicator update
//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
-}
-
-
-inline
-bool deletionImminent(const FileSystemObject& fsObj)
-{
- //test if current sync-line will result in deletion of files -> used to avoid disc space bottlenecks
- const SyncOperation op = fsObj.getSyncOperation();
- return op == FreeFileSync::SO_DELETE_LEFT || op == FreeFileSync::SO_DELETE_RIGHT;
-}
-
-
-class RemoveInvalid
-{
-public:
- RemoveInvalid(HierarchyObject& hierObj) :
- hierObj_(hierObj) {}
-
- ~RemoveInvalid()
- {
- FileSystemObject::removeEmptyNonRec(hierObj_);
- }
-
-private:
- HierarchyObject& hierObj_;
-};
-
-
-template <bool deleteOnly> //"true" if files deletion shall happen only
-class SyncProcess::SyncRecursively
-{
-public:
- SyncRecursively(const SyncProcess* const syncProc, const SyncProcess::DeletionHandling& delHandling) :
- syncProc_(syncProc),
- delHandling_(delHandling) {}
-
- void execute(HierarchyObject& hierObj)
- {
- //enforce removal of invalid entries (where both sides are empty)
- RemoveInvalid dummy(hierObj); //non-recursive
-
- //synchronize files:
- for (HierarchyObject::SubFileMapping::iterator i = hierObj.subFiles.begin(); i != hierObj.subFiles.end(); ++i)
- {
- if ( ( deleteOnly && deletionImminent(*i)) ||
- (!deleteOnly && !deletionImminent(*i)))
- {
- while (true)
- {
- try
- {
- syncProc_->synchronizeFile(*i, delHandling_);
- break;
- }
- catch (FileError& error)
- {
- //User abort when copying files or moving files/directories into custom deletion directory:
- //windows build: abort if requested, don't show error message if cancelled by user!
- //linux build: this refresh is not necessary, because user abort triggers an AbortThisProcess() exception without a FileError()
- syncProc_->statusUpdater->requestUiRefresh(true);
-
- ErrorHandler::Response rv = syncProc_->statusUpdater->reportError(error.show());
- if ( rv == ErrorHandler::IGNORE_ERROR)
- break;
- else if (rv == ErrorHandler::RETRY)
- ; //continue with loop
- else
- throw std::logic_error("Programming Error: Unknown return value!");
- }
- }
- }
- }
-
- //synchronize folders:
- for (HierarchyObject::SubDirMapping::iterator i = hierObj.subDirs.begin(); i != hierObj.subDirs.end(); ++i)
- {
- if (deleteOnly) //no need, to process folders more than once!
- {
- while (true)
- {
- try
- {
- syncProc_->synchronizeFolder(*i, delHandling_);
- break;
- }
- catch (FileError& error)
- {
- //User abort when copying files or moving files/directories into custom deletion directory:
- //windows build: abort if requested, don't show error message if cancelled by user!
- //linux build: this refresh is not necessary, because user abort triggers an AbortThisProcess() exception without a FileError()
- syncProc_->statusUpdater->requestUiRefresh(true);
-
- ErrorHandler::Response rv = syncProc_->statusUpdater->reportError(error.show());
- if (rv == ErrorHandler::IGNORE_ERROR)
- break;
- else if (rv == ErrorHandler::RETRY)
- ; //continue with loop
- else
- throw std::logic_error("Programming Error: Unknown return value!");
- }
- }
- }
-
- //recursive synchronization:
- //if (!i->isEmpty()) -> not necessary, deleted folders have no sub-objects at this point
- execute(*i);
- }
- }
-
-private:
- const SyncProcess* const syncProc_;
- const SyncProcess::DeletionHandling& delHandling_;
-};
-
-
-template <typename Function>
-inline
-void tryReportingError(StatusHandler& handler, Function cmd)
-{
- while (true)
- {
- try
- {
- cmd();
- break;
- }
- catch (FileError& error)
- {
- handler.requestUiRefresh(); //may throw!
-
- ErrorHandler::Response rv = handler.reportError(error.show()); //may throw!
- if ( rv == ErrorHandler::IGNORE_ERROR)
- break;
- else if (rv == ErrorHandler::RETRY)
- ; //continue with loop
- else
- throw std::logic_error("Programming Error: Unknown return value!");
- }
- }
+ statusUpdater_.updateProcessedData(1, 0); //each call represents one processed file
}
@@ -830,9 +858,8 @@ public:
//update sync database after synchronization is finished
void updateNow()
{
- //these calls may throw!
- tryReportingError(statusHandler_, boost::bind(saveToDisk, boost::cref(baseMap_), LEFT_SIDE, baseMap_.getDBFilename<LEFT_SIDE>()));
- tryReportingError(statusHandler_, boost::bind(saveToDisk, boost::cref(baseMap_), RIGHT_SIDE, baseMap_.getDBFilename<RIGHT_SIDE>()));
+ //these calls may throw in error-callbacks!
+ tryReportingError(statusHandler_, boost::bind(saveToDisk, boost::cref(baseMap_)));
};
//_("You can ignore the error to skip current folder pair."));
@@ -864,9 +891,9 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
const SyncStatistics statisticsTotal(folderCmp);
//keep at beginning so that all gui elements are initialized properly
- statusUpdater->initNewProcess(statisticsTotal.getCreate() + statisticsTotal.getOverwrite() + statisticsTotal.getDelete(),
- globalFunctions::convertToSigned(statisticsTotal.getDataToProcess()),
- StatusHandler::PROCESS_SYNCHRONIZING);
+ statusUpdater.initNewProcess(statisticsTotal.getCreate() + statisticsTotal.getOverwrite() + statisticsTotal.getDelete(),
+ globalFunctions::convertToSigned(statisticsTotal.getDataToProcess()),
+ StatusHandler::PROCESS_SYNCHRONIZING);
//-------------------some basic checks:------------------------------------------
@@ -883,7 +910,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
//test existence of Recycle Bin
if (folderPairCfg.handleDeletion == FreeFileSync::MOVE_TO_RECYCLE_BIN && !FreeFileSync::recycleBinExists())
{
- statusUpdater->reportFatalError(_("Unable to initialize Recycle Bin!"));
+ statusUpdater.reportFatalError(_("Unable to initialize Recycle Bin!"));
return; //should be obsolete!
}
@@ -892,7 +919,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
//check if user-defined directory for deletion was specified
if (FreeFileSync::getFormattedDirectoryName(folderPairCfg.custDelFolder.c_str()).empty())
{
- statusUpdater->reportFatalError(_("User-defined directory for deletion was not specified!"));
+ statusUpdater.reportFatalError(_("User-defined directory for deletion was not specified!"));
return; //should be obsolete!
}
}
@@ -901,23 +928,23 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
//avoid data loss when source directory doesn't (temporarily?) exist anymore AND user chose to ignore errors(else we wouldn't arrive here)
if (dataLossPossible(j->getBaseDir<LEFT_SIDE>(), statisticsFolderPair))
{
- statusUpdater->reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\""));
+ statusUpdater.reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\""));
return; //should be obsolete!
}
if (dataLossPossible(j->getBaseDir<RIGHT_SIDE>(), statisticsFolderPair))
{
- statusUpdater->reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\"") );
+ statusUpdater.reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\"") );
return; //should be obsolete!
}
//check if more than 50% of total number of files/dirs are to be created/overwritten/deleted
if (significantDifferenceDetected(statisticsFolderPair))
{
- statusUpdater->reportWarning(wxString(_("Significant difference detected:")) + wxT("\n") +
- zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT(" <-> ") + wxT("\n") +
- zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\n\n") +
- _("More than 50% of the total number of files will be copied or deleted!"),
- m_warnings.warningSignificantDifference);
+ statusUpdater.reportWarning(wxString(_("Significant difference detected:")) + wxT("\n") +
+ zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT(" <-> ") + wxT("\n") +
+ zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\n\n") +
+ _("More than 50% of the total number of files will be copied or deleted!"),
+ m_warnings.warningSignificantDifference);
}
//check for sufficient free diskspace in left directory
@@ -927,11 +954,11 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
if (wxGetDiskSpace(zToWx(j->getBaseDir<LEFT_SIDE>()), NULL, &freeDiskSpaceLeft))
{
if (freeDiskSpaceLeft < spaceNeeded.first)
- statusUpdater->reportWarning(wxString(_("Not enough free disk space available in:")) + wxT("\n") +
- wxT("\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\"\n\n") +
- _("Total required free disk space:") + wxT(" ") + formatFilesizeToShortString(spaceNeeded.first) + wxT("\n") +
- _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(freeDiskSpaceLeft),
- m_warnings.warningNotEnoughDiskSpace);
+ statusUpdater.reportWarning(wxString(_("Not enough free disk space available in:")) + wxT("\n") +
+ wxT("\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\"\n\n") +
+ _("Total required free disk space:") + wxT(" ") + formatFilesizeToShortString(spaceNeeded.first) + wxT("\n") +
+ _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(freeDiskSpaceLeft),
+ m_warnings.warningNotEnoughDiskSpace);
}
//check for sufficient free diskspace in right directory
@@ -939,21 +966,25 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
if (wxGetDiskSpace(zToWx(j->getBaseDir<RIGHT_SIDE>()), NULL, &freeDiskSpaceRight))
{
if (freeDiskSpaceRight < spaceNeeded.second)
- statusUpdater->reportWarning(wxString(_("Not enough free disk space available in:")) + wxT("\n") +
- wxT("\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\"\n\n") +
- _("Total required free disk space:") + wxT(" ") + formatFilesizeToShortString(spaceNeeded.second) + wxT("\n") +
- _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(freeDiskSpaceRight),
- m_warnings.warningNotEnoughDiskSpace);
+ statusUpdater.reportWarning(wxString(_("Not enough free disk space available in:")) + wxT("\n") +
+ wxT("\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\"\n\n") +
+ _("Total required free disk space:") + wxT(" ") + formatFilesizeToShortString(spaceNeeded.second) + wxT("\n") +
+ _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(freeDiskSpaceRight),
+ m_warnings.warningNotEnoughDiskSpace);
}
}
//check if unresolved conflicts exist
if (statisticsTotal.getConflict() > 0)
- statusUpdater->reportWarning(_("Unresolved conflicts existing! \n\nYou can ignore conflicts and continue synchronization."),
- m_warnings.warningUnresolvedConflicts);
+ statusUpdater.reportWarning(_("Unresolved conflicts existing! \n\nYou can ignore conflicts and continue synchronization."),
+ m_warnings.warningUnresolvedConflicts);
//-------------------end of basic checks------------------------------------------
+#ifdef FFS_WIN
+ //shadow copy buffer: per sync-instance, not folder pair
+ std::auto_ptr<ShadowCopy> shadowCopyHandler(new ShadowCopy);
+#endif
try
{
@@ -970,23 +1001,31 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
//execute synchronization recursively
//loop through all files twice; reason: first delete, then copy
- SyncRecursively<true>( this, currentDelHandling).execute(*j);
- SyncRecursively<false>(this, currentDelHandling).execute(*j);
+ SyncRecursively( *this,
+#ifdef FFS_WIN
+ *shadowCopyHandler,
+#endif
+ currentDelHandling).execute<true>(*j);
+ SyncRecursively(*this,
+#ifdef FFS_WIN
+ *shadowCopyHandler,
+#endif
+ currentDelHandling).execute<false>(*j);
//------------------------------------------------------------------------------------------
//update synchronization database (automatic sync only)
if (folderPairCfg.updateSyncDB)
{
- UpdateDatabase syncDB(*j, *statusUpdater);
- statusUpdater->updateStatusText(wxToZ(_("Generating database...")));
- statusUpdater->forceUiRefresh();
+ UpdateDatabase syncDB(*j, statusUpdater);
+ statusUpdater.updateStatusText(wxToZ(_("Generating database...")));
+ statusUpdater.forceUiRefresh();
syncDB.updateNow();
}
}
}
catch (const std::exception& e)
{
- statusUpdater->reportFatalError(wxString::FromAscii(e.what()));
+ statusUpdater.reportFatalError(wxString::FromAscii(e.what()));
return; //should be obsolete!
}
}
@@ -999,7 +1038,7 @@ class WhileCopying : public FreeFileSync::CopyFileCallback //callback functional
{
public:
- WhileCopying(wxLongLong& bytesTransferredLast, StatusHandler* statusHandler) :
+ WhileCopying(wxLongLong& bytesTransferredLast, StatusHandler& statusHandler) :
m_bytesTransferredLast(bytesTransferredLast),
m_statusHandler(statusHandler) {}
@@ -1009,27 +1048,27 @@ public:
const wxLongLong totalBytes = globalFunctions::convertToSigned(totalBytesTransferred);
//inform about the (differential) processed amount of data
- m_statusHandler->updateProcessedData(0, totalBytes - m_bytesTransferredLast);
+ m_statusHandler.updateProcessedData(0, totalBytes - m_bytesTransferredLast);
m_bytesTransferredLast = totalBytes;
#ifdef FFS_WIN
- m_statusHandler->requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this
- if (m_statusHandler->abortIsRequested())
+ m_statusHandler.requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this
+ if (m_statusHandler.abortIsRequested())
return CopyFileCallback::CANCEL;
#elif defined FFS_LINUX
- m_statusHandler->requestUiRefresh(); //exceptions may be thrown here!
+ m_statusHandler.requestUiRefresh(); //exceptions may be thrown here!
#endif
return CopyFileCallback::CONTINUE;
}
private:
wxLongLong& m_bytesTransferredLast;
- StatusHandler* m_statusHandler;
+ StatusHandler& m_statusHandler;
};
//copy file while executing statusUpdater->requestUiRefresh() calls
-void SyncProcess::copyFileUpdating(const Zstring& source, const Zstring& target, const wxULongLong& totalBytesToCpy) const
+void SyncRecursively::copyFileUpdating(const Zstring& source, const Zstring& target, const wxULongLong& totalBytesToCpy) const
{
//create folders first (see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080)
const Zstring targetDir = target.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR);
@@ -1046,28 +1085,28 @@ void SyncProcess::copyFileUpdating(const Zstring& source, const Zstring& target,
//start of (possibly) long-running copy process: ensure status updates are performed regularly
wxLongLong totalBytesTransferred;
- WhileCopying callback(totalBytesTransferred, statusUpdater);
+ WhileCopying callback(totalBytesTransferred, statusUpdater_);
try
{
#ifdef FFS_WIN
- FreeFileSync::copyFile(source, target, m_copyFileSymLinks, shadowCopyHandler.get(), &callback);
+ FreeFileSync::copyFile(source, target, copyFileSymLinks_, &shadowCopyHandler_, &callback);
#elif defined FFS_LINUX
- FreeFileSync::copyFile(source, target, m_copyFileSymLinks, &callback);
+ FreeFileSync::copyFile(source, target, copyFileSymLinks_, &callback);
#endif
- if (m_verifyCopiedFiles) //verify if data was copied correctly
+ if (verifyCopiedFiles_) //verify if data was copied correctly
verifyFileCopy(source, target);
}
catch (...)
{
//error situation: undo communication of processed amount of data
- statusUpdater->updateProcessedData(0, totalBytesTransferred * -1 );
+ statusUpdater_.updateProcessedData(0, totalBytesTransferred * -1 );
throw;
}
//inform about the (remaining) processed amount of data
- statusUpdater->updateProcessedData(0, globalFunctions::convertToSigned(totalBytesToCpy) - totalBytesTransferred);
+ statusUpdater_.updateProcessedData(0, globalFunctions::convertToSigned(totalBytesToCpy) - totalBytesTransferred);
}
@@ -1134,33 +1173,33 @@ void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback* c
class VerifyStatusUpdater : public VerifyCallback
{
public:
- VerifyStatusUpdater(StatusHandler* statusHandler) : m_statusHandler(statusHandler) {}
+ VerifyStatusUpdater(StatusHandler& statusHandler) : statusHandler_(statusHandler) {}
virtual void updateStatus()
{
- m_statusHandler->requestUiRefresh(); //trigger display refresh
+ statusHandler_.requestUiRefresh(); //trigger display refresh
}
private:
- StatusHandler* m_statusHandler;
+ StatusHandler& statusHandler_;
};
-void SyncProcess::verifyFileCopy(const Zstring& source, const Zstring& target) const
+void SyncRecursively::verifyFileCopy(const Zstring& source, const Zstring& target) const
{
Zstring statusText = txtVerifying;
statusText.Replace(DefaultStr("%x"), target, false);
- statusUpdater->updateStatusText(statusText);
- statusUpdater->requestUiRefresh(); //trigger display refresh
+ statusUpdater_.updateStatusText(statusText);
+ statusUpdater_.requestUiRefresh(); //trigger display refresh
- VerifyStatusUpdater callback(statusUpdater);
+ VerifyStatusUpdater callback(statusUpdater_);
try
{
verifyFiles(source, target, &callback);
}
catch (FileError& error)
{
- switch (statusUpdater->reportError(error.show()))
+ switch (statusUpdater_.reportError(error.show()))
{
case ErrorHandler::IGNORE_ERROR:
break;
@@ -1176,3 +1215,6 @@ void SyncProcess::verifyFileCopy(const Zstring& source, const Zstring& target) c
+
+
+
bgstack15