summaryrefslogtreecommitdiff
path: root/synchronization.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:12:46 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:12:46 +0200
commitb338e29fd3eaf700f8c8360aa0310048ba941d54 (patch)
tree122f8ef3790d12cd10275ef7453a9e8053322d78 /synchronization.cpp
parent3.18 (diff)
downloadFreeFileSync-b338e29fd3eaf700f8c8360aa0310048ba941d54.tar.gz
FreeFileSync-b338e29fd3eaf700f8c8360aa0310048ba941d54.tar.bz2
FreeFileSync-b338e29fd3eaf700f8c8360aa0310048ba941d54.zip
3.19
Diffstat (limited to 'synchronization.cpp')
-rw-r--r--synchronization.cpp1052
1 files changed, 464 insertions, 588 deletions
diff --git a/synchronization.cpp b/synchronization.cpp
index 293eabf2..d438f947 100644
--- a/synchronization.cpp
+++ b/synchronization.cpp
@@ -10,7 +10,7 @@
#include <wx/log.h>
#include "shared/string_conv.h"
#include "shared/util.h"
-#include "shared/system_constants.h"
+#include "shared/loki/ScopeGuard.h"
#include "library/status_handler.h"
#include "shared/file_handling.h"
#include "shared/resolve_path.h"
@@ -272,137 +272,17 @@ std::vector<zen::FolderPairSyncCfg> zen::extractSyncCfg(const MainConfiguration&
FolderPairSyncCfg(i->altSyncConfig->syncConfiguration.var == SyncConfig::AUTOMATIC,
i->altSyncConfig->handleDeletion,
- wxToZ(i->altSyncConfig->customDeletionDirectory)) :
+ toZ(i->altSyncConfig->customDeletionDirectory)) :
FolderPairSyncCfg(mainCfg.syncConfiguration.var == SyncConfig::AUTOMATIC,
mainCfg.handleDeletion,
- wxToZ(mainCfg.customDeletionDirectory)));
+ toZ(mainCfg.customDeletionDirectory)));
return output;
}
//------------------------------------------------------------------------------------------------------------
namespace
{
-
-class DiskSpaceNeeded
-{
-public:
- DiskSpaceNeeded(const BaseDirMapping& baseObj, bool freeSpaceDelLeft, bool freeSpaceDelRight) :
- freeSpaceDelLeft_(freeSpaceDelLeft),
- freeSpaceDelRight_(freeSpaceDelRight)
- {
- processRecursively(baseObj);
- }
-
- std::pair<zen::Int64, zen::Int64> getSpaceTotal() const
- {
- return std::make_pair(spaceNeededLeft, spaceNeededRight);
- }
-
-private:
- void processRecursively(const HierarchyObject& hierObj)
- {
- //don't process directories
-
- //process files
- for (HierarchyObject::SubFileMapping::const_iterator i = hierObj.useSubFiles().begin(); i != hierObj.useSubFiles().end(); ++i)
- switch (i->getSyncOperation()) //evaluate comparison result and sync direction
- {
- case SO_CREATE_NEW_LEFT:
- spaceNeededLeft += to<zen::Int64>(i->getFileSize<RIGHT_SIDE>());
- break;
-
- case SO_CREATE_NEW_RIGHT:
- spaceNeededRight += to<zen::Int64>(i->getFileSize<LEFT_SIDE>());
- break;
-
- case SO_DELETE_LEFT:
- if (freeSpaceDelLeft_)
- spaceNeededLeft -= to<zen::Int64>(i->getFileSize<LEFT_SIDE>());
- break;
-
- case SO_DELETE_RIGHT:
- if (freeSpaceDelRight_)
- spaceNeededRight -= to<zen::Int64>(i->getFileSize<RIGHT_SIDE>());
- break;
-
- case SO_OVERWRITE_LEFT:
- if (freeSpaceDelLeft_)
- spaceNeededLeft -= to<zen::Int64>(i->getFileSize<LEFT_SIDE>());
- spaceNeededLeft += to<zen::Int64>(i->getFileSize<RIGHT_SIDE>());
- break;
-
- case SO_OVERWRITE_RIGHT:
- if (freeSpaceDelRight_)
- spaceNeededRight -= to<zen::Int64>(i->getFileSize<RIGHT_SIDE>());
- spaceNeededRight += to<zen::Int64>(i->getFileSize<LEFT_SIDE>());
- break;
-
- case SO_DO_NOTHING:
- case SO_EQUAL:
- case SO_UNRESOLVED_CONFLICT:
- case SO_COPY_METADATA_TO_LEFT:
- case SO_COPY_METADATA_TO_RIGHT:
- break;
- }
-
- //symbolic links
- //[...]
-
- //recurse into sub-dirs
- std::for_each(hierObj.useSubDirs().begin(), hierObj.useSubDirs().end(), boost::bind(&DiskSpaceNeeded::processRecursively, this, _1));
- }
-
- const bool freeSpaceDelLeft_;
- const bool freeSpaceDelRight_;
-
- zen::Int64 spaceNeededLeft;
- zen::Int64 spaceNeededRight;
-};
-
-
-//evaluate whether a deletion will actually free space within a volume
-bool deletionFreesSpace(const Zstring& baseDir,
- const DeletionPolicy handleDeletion,
- const Zstring& custDelFolderFmt)
-{
- switch (handleDeletion)
- {
- case DELETE_PERMANENTLY:
- return true;
- case MOVE_TO_RECYCLE_BIN:
- return false; //in general... (unless Recycle Bin is full)
- case MOVE_TO_CUSTOM_DIRECTORY:
- switch (zen::onSameVolume(baseDir, custDelFolderFmt))
- {
- case VOLUME_SAME:
- return false;
- case VOLUME_DIFFERENT:
- return true; //but other volume (custDelFolderFmt) may become full...
- case VOLUME_CANT_SAY:
- return true; //a rough guess!
- }
- }
- assert(false);
- return true;
-}
-
-
-std::pair<zen::Int64, zen::Int64> freeDiskSpaceNeeded(
- const BaseDirMapping& baseDirObj,
- const DeletionPolicy handleDeletion,
- const Zstring& custDelFolderFmt)
-{
- const bool freeSpaceDelLeft = deletionFreesSpace(baseDirObj.getBaseDir<LEFT_SIDE>(), handleDeletion, custDelFolderFmt);
- const bool freeSpaceDelRight = deletionFreesSpace(baseDirObj.getBaseDir<RIGHT_SIDE>(), handleDeletion, custDelFolderFmt);
-
- return DiskSpaceNeeded(baseDirObj, freeSpaceDelLeft, freeSpaceDelRight).getSpaceTotal();
-}
-}
-//------------------------------------------------------------------------------------------------------------
-
-namespace
-{
bool synchronizationNeeded(const SyncStatistics& statisticsTotal)
{
return statisticsTotal.getCreate() +
@@ -446,28 +326,22 @@ FolderPairSyncCfg::FolderPairSyncCfg(bool automaticMode,
//-----------------------------------------------------------------------------------------------------------
-template <typename Function>
-inline
+template <typename Function> inline
bool tryReportingError(ProcessCallback& handler, Function cmd) //return "true" on success, "false" if error was ignored
{
while (true)
{
try
{
- cmd();
+ cmd(); //throw FileError
return true;
}
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(); //may throw!
-
- ErrorHandler::Response rv = handler.reportError(error.msg()); //may throw!
- if (rv == ErrorHandler::IGNORE_ERROR)
+ ProcessCallback::Response rv = handler.reportError(error.msg()); //may throw!
+ if (rv == ProcessCallback::IGNORE_ERROR)
return false;
- else if (rv == ErrorHandler::RETRY)
+ else if (rv == ProcessCallback::RETRY)
; //continue with loop
else
throw std::logic_error("Programming Error: Unknown return value!");
@@ -485,14 +359,14 @@ Zstring getSessionDeletionDir(const Zstring& deletionDirectory, const Zstring& p
if (formattedDir.empty())
return Zstring(); //no valid directory for deletion specified (checked later)
- if (!formattedDir.EndsWith(common::FILE_NAME_SEPARATOR))
- formattedDir += common::FILE_NAME_SEPARATOR;
+ if (!formattedDir.EndsWith(FILE_NAME_SEPARATOR))
+ formattedDir += FILE_NAME_SEPARATOR;
wxString timeNow = wxDateTime::Now().FormatISOTime();
- timeNow.Replace(wxT(":"), wxT(""));
+ replace(timeNow, L":", L"");
const wxString sessionName = wxDateTime::Now().FormatISODate() + wxChar(' ') + timeNow;
- formattedDir += prefix + wxToZ(sessionName);
+ formattedDir += prefix + toZ(sessionName);
//ensure that session directory does not yet exist (must be unique)
@@ -502,7 +376,7 @@ Zstring getSessionDeletionDir(const Zstring& deletionDirectory, const Zstring& p
for (int i = 1; zen::somethingExists(output); ++i)
output = formattedDir + Zchar('_') + Zstring::fromNumber(i);
- output += common::FILE_NAME_SEPARATOR;
+ output += FILE_NAME_SEPARATOR;
return output;
}
@@ -520,78 +394,81 @@ SyncProcess::SyncProcess(xmlAccess::OptionalDialogs& warnings,
//--------------------------------------------------------------------------------------------------------------
-class DeletionHandling
+class DeletionHandling //e.g. generate name of alternate deletion directory (unique for session AND folder pair)
{
public:
- DeletionHandling(const DeletionPolicy handleDel,
+ DeletionHandling(DeletionPolicy handleDel,
const Zstring& custDelFolder,
- const Zstring& baseDirLeft,
- const Zstring& baseDirRight,
+ const Zstring& baseDir, //with separator postfix
ProcessCallback& procCallback);
~DeletionHandling(); //always (try to) clean up, even if synchronization is aborted!
//clean-up temporary directory (recycler bin optimization)
- void tryCleanup() const; //throw (FileError) -> call this in non-exceptional coding, i.e. after Sync somewhere!
+ void tryCleanup(); //throw FileError -> call this in non-exceptional coding, i.e. after Sync somewhere!
- template <zen::SelectedSide side>
- void removeFile(const FileSystemObject& fileObj) const; //throw (FileError)
+ void removeFile (const Zstring& relativeName) const; //throw (FileError)
+ void removeFolder(const Zstring& relativeName) const; //throw (FileError)
- template <zen::SelectedSide side>
- void removeFolder(const FileSystemObject& dirObj) const; //throw (FileError)
+ const Zstring& getTxtRemovingFile () const { return txtRemovingFile; } //
+ const Zstring& getTxtRemovingSymLink() const { return txtRemovingSymlink; } //status text templates
+ const Zstring& getTxtRemovingDir () const { return txtRemovingDirectory; }; //
- const Zstring& getTxtRemovingFile() const; //status text templates
- const Zstring& getTxtRemovingSymLink() const;
- const Zstring& getTxtRemovingDir() const; //status text templates
+ //evaluate whether a deletion will actually free space within a volume
+ bool deletionFreesSpace() const;
private:
- template <SelectedSide side>
- const Zstring& getSessionDir() const;
+ DeletionPolicy deletionType;
+ ProcessCallback* procCallback_; //always bound! need assignment operator => not a reference
- void tryCleanupLeft() const; //throw (FileError)
- void tryCleanupRight() const; //throw (FileError)
+ Zstring sessionDelDir; //target deletion folder for current folder pair (with timestamp, ends with path separator)
- const DeletionPolicy deletionType;
- ProcessCallback& procCallback_;
-
- Zstring sessionDelDirLeft; //target deletion folder for current folder pair (with timestamp, ends with path separator)
- Zstring sessionDelDirRight; //
+ Zstring baseDir_; //with separator postfix
//preloaded status texts:
Zstring txtRemovingFile;
Zstring txtRemovingSymlink;
Zstring txtRemovingDirectory;
+
+ bool cleanedUp;
};
-DeletionHandling::DeletionHandling(const DeletionPolicy handleDel,
+DeletionHandling::DeletionHandling(DeletionPolicy handleDel,
const Zstring& custDelFolder,
- const Zstring& baseDirLeft,
- const Zstring& baseDirRight,
+ const Zstring& baseDir, //with separator postfix
ProcessCallback& procCallback) :
deletionType(handleDel),
- procCallback_(procCallback)
+ procCallback_(&procCallback),
+ baseDir_(baseDir),
+ cleanedUp(false)
{
- switch (handleDel)
+#ifdef FFS_WIN
+ if (deletionType == MOVE_TO_RECYCLE_BIN && recycleBinExists(baseDir) != STATUS_REC_EXISTS)
+ deletionType = DELETE_PERMANENTLY; //Windows' ::SHFileOperation() will do this anyway, but we have a better and faster deletion routine (e.g. on networks)
+#endif
+
+ switch (deletionType)
{
case DELETE_PERMANENTLY:
- txtRemovingFile = wxToZ(_("Deleting file %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false);
- txtRemovingSymlink = wxToZ(_("Deleting Symbolic Link %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false);
- txtRemovingDirectory = wxToZ(_("Deleting folder %x")).Replace( Zstr("%x"), Zstr("\n\"%x\""), false);
+ txtRemovingFile = toZ(_("Deleting file %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false);
+ txtRemovingSymlink = toZ(_("Deleting Symbolic Link %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false);
+ txtRemovingDirectory = toZ(_("Deleting folder %x")).Replace( Zstr("%x"), Zstr("\n\"%x\""), false);
break;
case MOVE_TO_RECYCLE_BIN:
- sessionDelDirLeft = getSessionDeletionDir(baseDirLeft, Zstr("FFS "));
- sessionDelDirRight = getSessionDeletionDir(baseDirRight, Zstr("FFS "));
+ sessionDelDir = getSessionDeletionDir(baseDir_, Zstr("FFS "));
- txtRemovingFile = txtRemovingSymlink = txtRemovingDirectory = wxToZ(_("Moving %x to Recycle Bin")).Replace(Zstr("%x"), Zstr("\"%x\""), false);
+ txtRemovingFile =
+ txtRemovingSymlink =
+ txtRemovingDirectory = toZ(_("Moving %x to Recycle Bin")).Replace(Zstr("%x"), Zstr("\"%x\""), false);
break;
case MOVE_TO_CUSTOM_DIRECTORY:
- sessionDelDirLeft = sessionDelDirRight = getSessionDeletionDir(custDelFolder);
+ sessionDelDir = getSessionDeletionDir(custDelFolder);
- txtRemovingFile = wxToZ(_("Moving file %x to user-defined directory %y")). Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false);
- txtRemovingDirectory = wxToZ(_("Moving folder %x to user-defined directory %y")). Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false);
- txtRemovingSymlink = wxToZ(_("Moving Symbolic Link %x to user-defined directory %y")).Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false);
+ txtRemovingFile = toZ(_("Moving file %x to user-defined directory %y")). Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false);
+ txtRemovingDirectory = toZ(_("Moving folder %x to user-defined directory %y")). Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false);
+ txtRemovingSymlink = toZ(_("Moving Symbolic Link %x to user-defined directory %y")).Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false);
break;
}
}
@@ -601,72 +478,21 @@ DeletionHandling::~DeletionHandling()
{
try //always (try to) clean up, even if synchronization is aborted!
{
- tryCleanupLeft();
+ tryCleanup(); //make sure this stays non-blocking!
}
catch (...) {}
-
- try //always clean up BOTH sides separately!
- {
- tryCleanupRight();
- }
- catch (...) {}
-}
-
-
-void DeletionHandling::tryCleanup() const //throw(AbortThisProcess)
-{
- tryReportingError(procCallback_, boost::bind(&DeletionHandling::tryCleanupLeft, this));
- tryReportingError(procCallback_, boost::bind(&DeletionHandling::tryCleanupRight, this));
}
-void DeletionHandling::tryCleanupLeft() const //throw (FileError)
+void DeletionHandling::tryCleanup() //throw FileError
{
- if (deletionType == MOVE_TO_RECYCLE_BIN) //clean-up temporary directory (recycle bin)
- zen::moveToRecycleBin(sessionDelDirLeft.BeforeLast(common::FILE_NAME_SEPARATOR)); //throw (FileError)
-}
-
-
-void DeletionHandling::tryCleanupRight() const //throw (FileError)
-{
- if (deletionType == MOVE_TO_RECYCLE_BIN) //clean-up temporary directory (recycle bin)
- zen::moveToRecycleBin(sessionDelDirRight.BeforeLast(common::FILE_NAME_SEPARATOR)); //throw (FileError)
-}
-
-
-inline
-const Zstring& DeletionHandling::getTxtRemovingFile() const
-{
- return txtRemovingFile;
-}
-
-
-inline
-const Zstring& DeletionHandling::getTxtRemovingDir() const
-{
- return txtRemovingDirectory;
-}
-
-
-inline
-const Zstring& DeletionHandling::getTxtRemovingSymLink() const
-{
- return txtRemovingSymlink;
-}
-
-
-template <>
-inline
-const Zstring& DeletionHandling::getSessionDir<LEFT_SIDE>() const
-{
- return sessionDelDirLeft;
-}
+ if (!cleanedUp)
+ {
+ if (deletionType == MOVE_TO_RECYCLE_BIN) //clean-up temporary directory (recycle bin)
+ zen::moveToRecycleBin(sessionDelDir.BeforeLast(FILE_NAME_SEPARATOR)); //throw FileError
-template <>
-inline
-const Zstring& DeletionHandling::getSessionDir<RIGHT_SIDE>() const
-{
- return sessionDelDirRight;
+ cleanedUp = true;
+ }
}
@@ -677,16 +503,9 @@ class CallbackMoveFileImpl : public CallbackMoveFile //callback functionality
public:
CallbackMoveFileImpl(ProcessCallback& handler) : statusHandler_(handler) {}
- virtual Response requestUiRefresh(const Zstring& currentObject) //DON'T throw exceptions here, at least in Windows build!
+ virtual void requestUiRefresh(const Zstring& currentObject) //DON'T throw exceptions here, at least in Windows build!
{
-#ifdef FFS_WIN
- statusHandler_.requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this
- if (statusHandler_.abortIsRequested())
- return CallbackMoveFile::CANCEL;
-#elif defined FFS_LINUX
statusHandler_.requestUiRefresh(); //exceptions may be thrown here!
-#endif
- return CallbackMoveFile::CONTINUE;
}
private:
@@ -709,110 +528,213 @@ private:
}
-template <zen::SelectedSide side>
-void DeletionHandling::removeFile(const FileSystemObject& fileObj) const
+void DeletionHandling::removeFile(const Zstring& relativeName) const
{
- using namespace zen;
+ const Zstring fullName = baseDir_ + relativeName;
switch (deletionType)
{
case DELETE_PERMANENTLY:
- zen::removeFile(fileObj.getFullName<side>());
+ zen::removeFile(fullName);
break;
case MOVE_TO_RECYCLE_BIN:
- if (fileExists(fileObj.getFullName<side>()))
+ if (fileExists(fullName))
{
- const Zstring targetFile = getSessionDir<side>() + fileObj.getRelativeName<side>(); //altDeletionDir ends with path separator
- const Zstring targetDir = targetFile.BeforeLast(common::FILE_NAME_SEPARATOR);
-
- if (!dirExists(targetDir))
- createDirectory(targetDir); //throw (FileError)
+ const Zstring targetFile = sessionDelDir + relativeName; //altDeletionDir ends with path separator
+ const Zstring targetDir = targetFile.BeforeLast(FILE_NAME_SEPARATOR);
try //rename file: no copying!!!
{
+ if (!dirExists(targetDir))
+ createDirectory(targetDir); //throw FileError -> may legitimately fail on Linux if permissions are missing
+
//performance optimization!! Instead of moving each object into recycle bin separately, we rename them ony by one into a
//temporary directory and delete this directory only ONCE!
- renameFile(fileObj.getFullName<side>(), targetFile); //throw (FileError);
+ renameFile(fullName, targetFile); //throw (FileError);
}
catch (...)
{
//if anything went wrong, move to recycle bin the standard way (single file processing: slow)
- moveToRecycleBin(fileObj.getFullName<side>()); //throw (FileError)
+ moveToRecycleBin(fullName); //throw (FileError)
}
}
break;
case MOVE_TO_CUSTOM_DIRECTORY:
- if (fileExists(fileObj.getFullName<side>()))
+ if (fileExists(fullName))
{
- const Zstring targetFile = getSessionDir<side>() + fileObj.getRelativeName<side>(); //altDeletionDir ends with path separator
- const Zstring targetDir = targetFile.BeforeLast(common::FILE_NAME_SEPARATOR);
+ const Zstring targetFile = sessionDelDir + relativeName; //altDeletionDir ends with path separator
+ const Zstring targetDir = targetFile.BeforeLast(FILE_NAME_SEPARATOR);
if (!dirExists(targetDir))
createDirectory(targetDir); //throw (FileError)
- CallbackMoveFileImpl callBack(procCallback_); //if file needs to be copied we need callback functionality to update screen and offer abort
- moveFile(fileObj.getFullName<side>(), targetFile, true, &callBack);
+ CallbackMoveFileImpl callBack(*procCallback_); //if file needs to be copied we need callback functionality to update screen and offer abort
+ moveFile(fullName, targetFile, true, &callBack);
}
break;
}
}
-template <zen::SelectedSide side>
-void DeletionHandling::removeFolder(const FileSystemObject& dirObj) const
+void DeletionHandling::removeFolder(const Zstring& relativeName) const
{
- using namespace zen;
+ const Zstring fullName = baseDir_ + relativeName;
switch (deletionType)
{
case DELETE_PERMANENTLY:
{
- CallbackRemoveDirImpl remDirCallback(procCallback_);
- removeDirectory(dirObj.getFullName<side>(), &remDirCallback);
+ CallbackRemoveDirImpl remDirCallback(*procCallback_);
+ removeDirectory(fullName, &remDirCallback);
}
break;
case MOVE_TO_RECYCLE_BIN:
- if (dirExists(dirObj.getFullName<side>()))
+ if (dirExists(fullName))
{
- const Zstring targetDir = getSessionDir<side>() + dirObj.getRelativeName<side>();
- const Zstring targetSuperDir = targetDir.BeforeLast(common::FILE_NAME_SEPARATOR);
-
- if (!dirExists(targetSuperDir))
- createDirectory(targetSuperDir); //throw (FileError)
+ const Zstring targetDir = sessionDelDir + relativeName;
+ const Zstring targetSuperDir = targetDir.BeforeLast(FILE_NAME_SEPARATOR);
try //rename directory: no copying!!!
{
+ if (!dirExists(targetSuperDir))
+ createDirectory(targetSuperDir); //throw FileError -> may legitimately fail on Linux if permissions are missing
+
//performance optimization!! Instead of moving each object into recycle bin separately, we rename them ony by one into a
//temporary directory and delete this directory only ONCE!
- renameFile(dirObj.getFullName<side>(), targetDir); //throw (FileError);
+ renameFile(fullName, targetDir); //throw (FileError);
}
catch (...)
{
//if anything went wrong, move to recycle bin the standard way (single file processing: slow)
- moveToRecycleBin(dirObj.getFullName<side>()); //throw (FileError)
+ moveToRecycleBin(fullName); //throw (FileError)
}
}
break;
case MOVE_TO_CUSTOM_DIRECTORY:
- if (dirExists(dirObj.getFullName<side>()))
+ if (dirExists(fullName))
{
- const Zstring targetDir = getSessionDir<side>() + dirObj.getRelativeName<side>();
- const Zstring targetSuperDir = targetDir.BeforeLast(common::FILE_NAME_SEPARATOR);
+ const Zstring targetDir = sessionDelDir + relativeName;
+ const Zstring targetSuperDir = targetDir.BeforeLast(FILE_NAME_SEPARATOR);
if (!dirExists(targetSuperDir))
createDirectory(targetSuperDir); //throw (FileError)
- CallbackMoveFileImpl callBack(procCallback_); //if files need to be copied, we need callback functionality to update screen and offer abort
- moveDirectory(dirObj.getFullName<side>(), targetDir, true, &callBack);
+ CallbackMoveFileImpl callBack(*procCallback_); //if files need to be copied, we need callback functionality to update screen and offer abort
+ moveDirectory(fullName, targetDir, true, &callBack);
}
break;
}
}
+
+//evaluate whether a deletion will actually free space within a volume
+bool DeletionHandling::deletionFreesSpace() const
+{
+ switch (deletionType)
+ {
+ case DELETE_PERMANENTLY:
+ return true;
+ case MOVE_TO_RECYCLE_BIN:
+ return false; //in general... (unless Recycle Bin is full)
+ case MOVE_TO_CUSTOM_DIRECTORY:
+ switch (zen::onSameVolume(baseDir_, sessionDelDir))
+ {
+ case VOLUME_SAME:
+ return false;
+ case VOLUME_DIFFERENT:
+ return true; //but other volume (sessionDelDir) may become full...
+ case VOLUME_CANT_SAY:
+ return true; //a rough guess!
+ }
+ }
+ assert(false);
+ return true;
+}
+//------------------------------------------------------------------------------------------------------------
+
+namespace
+{
+
+class DiskSpaceNeeded
+{
+public:
+ DiskSpaceNeeded(const BaseDirMapping& baseObj, bool freeSpaceDelLeft, bool freeSpaceDelRight) :
+ freeSpaceDelLeft_(freeSpaceDelLeft),
+ freeSpaceDelRight_(freeSpaceDelRight)
+ {
+ processRecursively(baseObj);
+ }
+
+ std::pair<zen::Int64, zen::Int64> getSpaceTotal() const
+ {
+ return std::make_pair(spaceNeededLeft, spaceNeededRight);
+ }
+
+private:
+ void processRecursively(const HierarchyObject& hierObj)
+ {
+ //don't process directories
+
+ //process files
+ for (HierarchyObject::SubFileMapping::const_iterator i = hierObj.useSubFiles().begin(); i != hierObj.useSubFiles().end(); ++i)
+ switch (i->getSyncOperation()) //evaluate comparison result and sync direction
+ {
+ case SO_CREATE_NEW_LEFT:
+ spaceNeededLeft += to<zen::Int64>(i->getFileSize<RIGHT_SIDE>());
+ break;
+
+ case SO_CREATE_NEW_RIGHT:
+ spaceNeededRight += to<zen::Int64>(i->getFileSize<LEFT_SIDE>());
+ break;
+
+ case SO_DELETE_LEFT:
+ if (freeSpaceDelLeft_)
+ spaceNeededLeft -= to<zen::Int64>(i->getFileSize<LEFT_SIDE>());
+ break;
+
+ case SO_DELETE_RIGHT:
+ if (freeSpaceDelRight_)
+ spaceNeededRight -= to<zen::Int64>(i->getFileSize<RIGHT_SIDE>());
+ break;
+
+ case SO_OVERWRITE_LEFT:
+ if (freeSpaceDelLeft_)
+ spaceNeededLeft -= to<zen::Int64>(i->getFileSize<LEFT_SIDE>());
+ spaceNeededLeft += to<zen::Int64>(i->getFileSize<RIGHT_SIDE>());
+ break;
+
+ case SO_OVERWRITE_RIGHT:
+ if (freeSpaceDelRight_)
+ spaceNeededRight -= to<zen::Int64>(i->getFileSize<RIGHT_SIDE>());
+ spaceNeededRight += to<zen::Int64>(i->getFileSize<LEFT_SIDE>());
+ break;
+
+ case SO_DO_NOTHING:
+ case SO_EQUAL:
+ case SO_UNRESOLVED_CONFLICT:
+ case SO_COPY_METADATA_TO_LEFT:
+ case SO_COPY_METADATA_TO_RIGHT:
+ break;
+ }
+
+ //symbolic links
+ //[...]
+
+ //recurse into sub-dirs
+ std::for_each(hierObj.useSubDirs().begin(), hierObj.useSubDirs().end(), boost::bind(&DiskSpaceNeeded::processRecursively, this, _1));
+ }
+
+ const bool freeSpaceDelLeft_;
+ const bool freeSpaceDelRight_;
+
+ zen::Int64 spaceNeededLeft;
+ zen::Int64 spaceNeededRight;
+};
+}
//----------------------------------------------------------------------------------------
//test if current sync-line will result in deletion of files or
@@ -841,9 +763,11 @@ bool diskSpaceIsReduced(const FileMapping& fileObj)
case SO_COPY_METADATA_TO_RIGHT:
return false;
}
+ assert(false);
return false; //dummy
}
+
inline
bool diskSpaceIsReduced(const DirMapping& dirObj)
{
@@ -856,15 +780,16 @@ bool diskSpaceIsReduced(const DirMapping& dirObj)
case SO_OVERWRITE_LEFT:
case SO_OVERWRITE_RIGHT:
assert(false);
- case SO_UNRESOLVED_CONFLICT:
case SO_CREATE_NEW_LEFT:
case SO_CREATE_NEW_RIGHT:
case SO_DO_NOTHING:
case SO_EQUAL:
+ case SO_UNRESOLVED_CONFLICT:
case SO_COPY_METADATA_TO_LEFT:
case SO_COPY_METADATA_TO_RIGHT:
return false;
}
+ assert(false);
return false; //dummy
}
//---------------------------------------------------------------------------------------------------------------
@@ -877,36 +802,45 @@ public:
#ifdef FFS_WIN
shadow::ShadowCopy* shadowCopyHandler,
#endif
- const DeletionHandling& delHandling) :
+ const DeletionHandling& delHandlingLeft,
+ const DeletionHandling& delHandlingRight) :
procCallback_(syncProc.procCallback),
#ifdef FFS_WIN
shadowCopyHandler_(shadowCopyHandler),
#endif
- delHandling_(delHandling),
+ delHandlingLeft_(delHandlingLeft),
+ delHandlingRight_(delHandlingRight),
verifyCopiedFiles_(syncProc.verifyCopiedFiles_),
copyFilePermissions_(syncProc.copyFilePermissions_),
- txtCopyingFile (wxToZ(_("Copying new file %x to %y")). Replace(Zstr("%x"), Zstr("\"%x\""), false).Replace(Zstr("%y"), Zstr("\n\"%y\""), false)),
- txtCopyingLink (wxToZ(_("Copying new Symbolic Link %x to %y")).Replace(Zstr("%x"), Zstr("\"%x\""), false).Replace(Zstr("%y"), Zstr("\n\"%y\""), false)),
- txtOverwritingFile(wxToZ(_("Overwriting file %x in %y")). Replace(Zstr("%x"), Zstr("\"%x\""), false).Replace(Zstr("%y"), Zstr("\n\"%y\""), false)),
- txtOverwritingLink(wxToZ(_("Overwriting Symbolic Link %x in %y")).Replace(Zstr("%x"), Zstr("\"%x\""), false).Replace(Zstr("%y"), Zstr("\n\"%y\""), false)),
- txtCreatingFolder (wxToZ(_("Creating folder %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false)),
- txtVerifying (wxToZ(_("Verifying file %x")). Replace(Zstr("%x"), Zstr("\n\"%x\""), false)),
- txtWritingAttributes(wxToZ(_("Updating attributes of %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false)) {}
+ txtCopyingFile (toZ(_("Copying new file %x to %y")). Replace(Zstr("%x"), Zstr("\"%x\""), false).Replace(Zstr("%y"), Zstr("\n\"%y\""), false)),
+ txtCopyingLink (toZ(_("Copying new Symbolic Link %x to %y")).Replace(Zstr("%x"), Zstr("\"%x\""), false).Replace(Zstr("%y"), Zstr("\n\"%y\""), false)),
+ txtOverwritingFile(toZ(_("Overwriting file %x in %y")). Replace(Zstr("%x"), Zstr("\"%x\""), false).Replace(Zstr("%y"), Zstr("\n\"%y\""), false)),
+ txtOverwritingLink(toZ(_("Overwriting Symbolic Link %x in %y")).Replace(Zstr("%x"), Zstr("\"%x\""), false).Replace(Zstr("%y"), Zstr("\n\"%y\""), false)),
+ txtCreatingFolder (toZ(_("Creating folder %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false)),
+ txtVerifying (toZ(_("Verifying file %x")). Replace(Zstr("%x"), Zstr("\n\"%x\""), false)),
+ txtWritingAttributes(toZ(_("Updating attributes of %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false)) {}
- template <bool reduceDiskSpace> //"true" if files deletion shall happen only
void startSync(BaseDirMapping& baseMap)
{
- execute<reduceDiskSpace>(baseMap);
+ //loop through all files twice; reason: first delete files (or overwrite big ones with smaller ones), then copy rest
+ execute<FIRST_PASS >(baseMap);
+ execute<SECOND_PASS>(baseMap);
}
private:
- template <bool reduceDiskSpace> //"true" if files deletion shall happen only
+ enum PassId
+ {
+ FIRST_PASS, //delete files
+ SECOND_PASS //create, modify
+ };
+
+ template <PassId pass>
void execute(HierarchyObject& hierObj);
- void synchronizeFile(FileMapping& fileObj) const;
- void synchronizeLink(SymLinkMapping& linkObj) const;
- void synchronizeFolder(DirMapping& dirObj) const;
+ void synchronizeFile (FileMapping& fileObj) const;
+ void synchronizeLink (SymLinkMapping& linkObj) const;
+ void synchronizeFolder(DirMapping& dirObj) const;
//more low level helper
template <zen::SelectedSide side>
@@ -920,7 +854,8 @@ private:
#ifdef FFS_WIN
shadow::ShadowCopy* shadowCopyHandler_; //optional!
#endif
- const DeletionHandling& delHandling_;
+ const DeletionHandling& delHandlingLeft_;
+ const DeletionHandling& delHandlingRight_;
const bool verifyCopiedFiles_;
const bool copyFilePermissions_;
@@ -936,63 +871,34 @@ private:
};
-template <bool reduceDiskSpace> //"true" if file deletion happens only
+template <SynchronizeFolderPair::PassId pass>
void SynchronizeFolderPair::execute(HierarchyObject& hierObj)
{
//synchronize files:
- for (HierarchyObject::SubFileMapping::iterator i = hierObj.useSubFiles().begin(); i != hierObj.useSubFiles().end(); ++i)
+ std::for_each(hierObj.useSubFiles().begin(), hierObj.useSubFiles().end(),
+ [&](FileMapping& fileObj)
{
- const bool letsDoThis = reduceDiskSpace == diskSpaceIsReduced(*i);
+ const bool letsDoThis = (pass == FIRST_PASS) == diskSpaceIsReduced(fileObj); //to be deleted files on first pass, rest on second!
if (letsDoThis)
- tryReportingError(procCallback_, boost::bind(&SynchronizeFolderPair::synchronizeFile, this, boost::ref(*i)));
- }
+ tryReportingError(procCallback_, [&]() { synchronizeFile(fileObj); });
+ });
//synchronize symbolic links: (process in second step only)
- if (!reduceDiskSpace)
- for (HierarchyObject::SubLinkMapping::iterator i = hierObj.useSubLinks().begin(); i != hierObj.useSubLinks().end(); ++i)
- tryReportingError(procCallback_, boost::bind(&SynchronizeFolderPair::synchronizeLink, this, boost::ref(*i)));
+ if (pass == SECOND_PASS)
+ std::for_each(hierObj.useSubLinks().begin(), hierObj.useSubLinks().end(),
+ [&](SymLinkMapping& linkObj) { tryReportingError(procCallback_, [&]() { synchronizeLink(linkObj); }); });
//synchronize folders:
- for (HierarchyObject::SubDirMapping::iterator i = hierObj.useSubDirs().begin(); i != hierObj.useSubDirs().end(); ++i)
+ std::for_each(hierObj.useSubDirs().begin(), hierObj.useSubDirs().end(),
+ [&](DirMapping& dirObj)
{
- const SyncOperation syncOp = i->getSyncOperation();
-
- //ensure folder creation happens in second pass, to enable time adaption below
- const bool letsDoThis = reduceDiskSpace == diskSpaceIsReduced(*i);
-
- if (letsDoThis)
- tryReportingError(procCallback_, boost::bind(&SynchronizeFolderPair::synchronizeFolder, this, boost::ref(*i)));
-
- //recursive synchronization:
- execute<reduceDiskSpace>(*i);
+ const bool letsDoThis = (pass == FIRST_PASS) == diskSpaceIsReduced(dirObj); //to be deleted files on first pass, rest on second!
+ if (letsDoThis) //running folder creation on first pass only, works, but looks strange for initial mirror sync when all folders are created at once
+ tryReportingError(procCallback_, [&]() { synchronizeFolder(dirObj); });
- //adapt folder modification dates: apply AFTER all subobjects have been synced to preserve folder modification date!
- if (letsDoThis)
- try
- {
- switch (syncOp)
- {
- case SO_CREATE_NEW_LEFT:
- case SO_COPY_METADATA_TO_LEFT:
- copyFileTimes(i->getFullName<RIGHT_SIDE>(), i->getFullName<LEFT_SIDE>(), true); //deref symlinks; throw (FileError)
- break;
- case SO_CREATE_NEW_RIGHT:
- case SO_COPY_METADATA_TO_RIGHT:
- copyFileTimes(i->getFullName<LEFT_SIDE>(), i->getFullName<RIGHT_SIDE>(), true); //deref symlinks; throw (FileError)
- break;
- case SO_OVERWRITE_RIGHT:
- case SO_OVERWRITE_LEFT:
- assert(false);
- case SO_UNRESOLVED_CONFLICT:
- case SO_DELETE_LEFT:
- case SO_DELETE_RIGHT:
- case SO_DO_NOTHING:
- case SO_EQUAL:
- break;
- }
- }
- catch (...) {}
- }
+ //recursion!
+ this->execute<pass>(dirObj);
+ });
}
@@ -1013,7 +919,7 @@ public:
void operator()() const
{
//delete target and copy source
- delHandling_.removeFile<side>(fileObj_); //throw (FileError)
+ delHandling_.removeFile(fileObj_.getObjRelativeName()); //throw (FileError)
fileObj_.removeObject<side>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!)
}
@@ -1036,9 +942,8 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
statusText = txtCopyingFile;
statusText.Replace(Zstr("%x"), fileObj.getShortName<RIGHT_SIDE>(), false);
- statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ statusText.Replace(Zstr("%y"), target.BeforeLast(FILE_NAME_SEPARATOR), false);
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target,
NullCommand(), //no target to delete
@@ -1050,9 +955,8 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
statusText = txtCopyingFile;
statusText.Replace(Zstr("%x"), fileObj.getShortName<LEFT_SIDE>(), false);
- statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ statusText.Replace(Zstr("%y"), target.BeforeLast(FILE_NAME_SEPARATOR), false);
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target,
NullCommand(), //no target to delete
@@ -1060,21 +964,19 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
break;
case SO_DELETE_LEFT:
- statusText = delHandling_.getTxtRemovingFile();
+ statusText = delHandlingLeft_.getTxtRemovingFile();
statusText.Replace(Zstr("%x"), fileObj.getFullName<LEFT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
- delHandling_.removeFile<LEFT_SIDE>(fileObj); //throw (FileError)
+ delHandlingLeft_.removeFile(fileObj.getObjRelativeName()); //throw (FileError)
break;
case SO_DELETE_RIGHT:
- statusText = delHandling_.getTxtRemovingFile();
+ statusText = delHandlingRight_.getTxtRemovingFile();
statusText.Replace(Zstr("%x"), fileObj.getFullName<RIGHT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
- delHandling_.removeFile<RIGHT_SIDE>(fileObj); //throw (FileError)
+ delHandlingRight_.removeFile(fileObj.getObjRelativeName()); //throw (FileError)
break;
case SO_OVERWRITE_LEFT:
@@ -1082,12 +984,11 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
statusText = txtOverwritingFile;
statusText.Replace(Zstr("%x"), fileObj.getShortName<RIGHT_SIDE>(), false);
- statusText.Replace(Zstr("%y"), fileObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ statusText.Replace(Zstr("%y"), fileObj.getFullName<LEFT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR), false);
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target,
- DelTargetCommand<LEFT_SIDE>(fileObj, delHandling_), //delete target at appropriate time
+ DelTargetCommand<LEFT_SIDE>(fileObj, delHandlingLeft_), //delete target at appropriate time
fileObj.getFileSize<RIGHT_SIDE>());
break;
@@ -1096,24 +997,22 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
statusText = txtOverwritingFile;
statusText.Replace(Zstr("%x"), fileObj.getShortName<LEFT_SIDE>(), false);
- statusText.Replace(Zstr("%y"), fileObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ statusText.Replace(Zstr("%y"), fileObj.getFullName<RIGHT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR), false);
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target,
- DelTargetCommand<RIGHT_SIDE>(fileObj, delHandling_), //delete target at appropriate time
+ DelTargetCommand<RIGHT_SIDE>(fileObj, delHandlingRight_), //delete target at appropriate time
fileObj.getFileSize<LEFT_SIDE>());
break;
case SO_COPY_METADATA_TO_LEFT:
statusText = txtWritingAttributes;
statusText.Replace(Zstr("%x"), fileObj.getFullName<LEFT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
if (fileObj.getShortName<LEFT_SIDE>() != fileObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only)
renameFile(fileObj.getFullName<LEFT_SIDE>(),
- fileObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + fileObj.getShortName<RIGHT_SIDE>()); //throw (FileError);
+ fileObj.getFullName<LEFT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + fileObj.getShortName<RIGHT_SIDE>()); //throw (FileError);
if (!sameFileTime(fileObj.getLastWriteTime<LEFT_SIDE>(), fileObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision
copyFileTimes(fileObj.getFullName<RIGHT_SIDE>(), fileObj.getFullName<LEFT_SIDE>(), true); //deref symlinks; throw (FileError)
@@ -1122,12 +1021,11 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
case SO_COPY_METADATA_TO_RIGHT:
statusText = txtWritingAttributes;
statusText.Replace(Zstr("%x"), fileObj.getFullName<RIGHT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
if (fileObj.getShortName<LEFT_SIDE>() != fileObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only)
renameFile(fileObj.getFullName<RIGHT_SIDE>(),
- fileObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + fileObj.getShortName<LEFT_SIDE>()); //throw (FileError);
+ fileObj.getFullName<RIGHT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + fileObj.getShortName<LEFT_SIDE>()); //throw (FileError);
if (!sameFileTime(fileObj.getLastWriteTime<LEFT_SIDE>(), fileObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision
copyFileTimes(fileObj.getFullName<LEFT_SIDE>(), fileObj.getFullName<RIGHT_SIDE>(), true); //deref symlinks; throw (FileError)
@@ -1145,6 +1043,7 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
//progress indicator update
//indicator is updated only if file is sync'ed correctly (and if some sync was done)!
procCallback_.updateProcessedData(1, 0); //processed data is communicated in subfunctions!
+ procCallback_.requestUiRefresh(); //may throw
}
@@ -1160,9 +1059,8 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
statusText = txtCopyingLink;
statusText.Replace(Zstr("%x"), linkObj.getShortName<RIGHT_SIDE>(), false);
- statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ statusText.Replace(Zstr("%y"), target.BeforeLast(FILE_NAME_SEPARATOR), false);
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
copySymlink(linkObj.getFullName<RIGHT_SIDE>(), target, linkObj.getLinkType<RIGHT_SIDE>());
break;
@@ -1172,27 +1070,24 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
statusText = txtCopyingLink;
statusText.Replace(Zstr("%x"), linkObj.getShortName<LEFT_SIDE>(), false);
- statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ statusText.Replace(Zstr("%y"), target.BeforeLast(FILE_NAME_SEPARATOR), false);
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
copySymlink(linkObj.getFullName<LEFT_SIDE>(), target, linkObj.getLinkType<LEFT_SIDE>());
break;
case SO_DELETE_LEFT:
- statusText = delHandling_.getTxtRemovingSymLink();
+ statusText = delHandlingLeft_.getTxtRemovingSymLink();
statusText.Replace(Zstr("%x"), linkObj.getFullName<LEFT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
deleteSymlink<LEFT_SIDE>(linkObj); //throw (FileError)
break;
case SO_DELETE_RIGHT:
- statusText = delHandling_.getTxtRemovingSymLink();
+ statusText = delHandlingRight_.getTxtRemovingSymLink();
statusText.Replace(Zstr("%x"), linkObj.getFullName<RIGHT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
deleteSymlink<RIGHT_SIDE>(linkObj); //throw (FileError)
break;
@@ -1202,9 +1097,8 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
statusText = txtOverwritingLink;
statusText.Replace(Zstr("%x"), linkObj.getShortName<RIGHT_SIDE>(), false);
- statusText.Replace(Zstr("%y"), linkObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ statusText.Replace(Zstr("%y"), linkObj.getFullName<LEFT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR), false);
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
deleteSymlink<LEFT_SIDE>(linkObj); //throw (FileError)
linkObj.removeObject<LEFT_SIDE>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!)
@@ -1217,9 +1111,8 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
statusText = txtOverwritingLink;
statusText.Replace(Zstr("%x"), linkObj.getShortName<LEFT_SIDE>(), false);
- statusText.Replace(Zstr("%y"), linkObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ statusText.Replace(Zstr("%y"), linkObj.getFullName<RIGHT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR), false);
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
deleteSymlink<RIGHT_SIDE>(linkObj); //throw (FileError)
linkObj.removeObject<RIGHT_SIDE>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!)
@@ -1230,12 +1123,11 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
case SO_COPY_METADATA_TO_LEFT:
statusText = txtWritingAttributes;
statusText.Replace(Zstr("%x"), linkObj.getFullName<LEFT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
if (linkObj.getShortName<LEFT_SIDE>() != linkObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only)
renameFile(linkObj.getFullName<LEFT_SIDE>(),
- linkObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + linkObj.getShortName<RIGHT_SIDE>()); //throw (FileError);
+ linkObj.getFullName<LEFT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + linkObj.getShortName<RIGHT_SIDE>()); //throw (FileError);
if (!sameFileTime(linkObj.getLastWriteTime<LEFT_SIDE>(), linkObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision
copyFileTimes(linkObj.getFullName<RIGHT_SIDE>(), linkObj.getFullName<LEFT_SIDE>(), false); //don't deref symlinks; throw (FileError)
@@ -1244,12 +1136,11 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
case SO_COPY_METADATA_TO_RIGHT:
statusText = txtWritingAttributes;
statusText.Replace(Zstr("%x"), linkObj.getFullName<RIGHT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
if (linkObj.getShortName<LEFT_SIDE>() != linkObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only)
renameFile(linkObj.getFullName<RIGHT_SIDE>(),
- linkObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + linkObj.getShortName<LEFT_SIDE>()); //throw (FileError);
+ linkObj.getFullName<RIGHT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + linkObj.getShortName<LEFT_SIDE>()); //throw (FileError);
if (!sameFileTime(linkObj.getLastWriteTime<LEFT_SIDE>(), linkObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision
copyFileTimes(linkObj.getFullName<LEFT_SIDE>(), linkObj.getFullName<RIGHT_SIDE>(), false); //don't deref symlinks; throw (FileError)
@@ -1267,6 +1158,7 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
//progress indicator update
//indicator is updated only if file is sync'ed correctly (and if some sync was done)!
procCallback_.updateProcessedData(1, 0); //processed data is communicated in subfunctions!
+ procCallback_.requestUiRefresh(); //may throw
}
@@ -1283,12 +1175,11 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const
statusText = txtCreatingFolder;
statusText.Replace(Zstr("%x"), target, false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
//some check to catch the error that directory on source has been deleted externally after "compare"...
if (!zen::dirExists(dirObj.getFullName<RIGHT_SIDE>()))
- throw FileError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(dirObj.getFullName<RIGHT_SIDE>()) + wxT("\""));
+ throw FileError(_("Source directory does not exist anymore:") + "\n\"" + dirObj.getFullName<RIGHT_SIDE>() + "\"");
createDirectory(target, dirObj.getFullName<RIGHT_SIDE>(), copyFilePermissions_); //no symlink copying!
break;
@@ -1297,77 +1188,72 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const
statusText = txtCreatingFolder;
statusText.Replace(Zstr("%x"), target, false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
//some check to catch the error that directory on source has been deleted externally after "compare"...
if (!zen::dirExists(dirObj.getFullName<LEFT_SIDE>()))
- throw FileError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(dirObj.getFullName<LEFT_SIDE>()) + wxT("\""));
+ throw FileError(_("Source directory does not exist anymore:") + "\n\"" + dirObj.getFullName<LEFT_SIDE>() + "\"");
createDirectory(target, dirObj.getFullName<LEFT_SIDE>(), copyFilePermissions_); //no symlink copying!
break;
+ case SO_COPY_METADATA_TO_LEFT:
+ statusText = txtWritingAttributes;
+ statusText.Replace(Zstr("%x"), dirObj.getFullName<LEFT_SIDE>(), false);
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
+
+ if (dirObj.getShortName<LEFT_SIDE>() != dirObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only)
+ renameFile(dirObj.getFullName<LEFT_SIDE>(),
+ dirObj.getFullName<LEFT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + dirObj.getShortName<RIGHT_SIDE>()); //throw (FileError);
+ //copyFileTimes(dirObj.getFullName<RIGHT_SIDE>(), dirObj.getFullName<LEFT_SIDE>(), true); //throw (FileError) -> is executed after sub-objects have finished synchronization
+ break;
+
+ case SO_COPY_METADATA_TO_RIGHT:
+ statusText = txtWritingAttributes;
+ statusText.Replace(Zstr("%x"), dirObj.getFullName<RIGHT_SIDE>(), false);
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
+
+ if (dirObj.getShortName<LEFT_SIDE>() != dirObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only)
+ renameFile(dirObj.getFullName<RIGHT_SIDE>(),
+ dirObj.getFullName<RIGHT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + dirObj.getShortName<LEFT_SIDE>()); //throw (FileError);
+ //copyFileTimes(dirObj.getFullName<LEFT_SIDE>(), dirObj.getFullName<RIGHT_SIDE>(), true); //throw (FileError) -> is executed after sub-objects have finished synchronization
+ break;
+
case SO_DELETE_LEFT:
//status information
- statusText = delHandling_.getTxtRemovingDir();
+ statusText = delHandlingLeft_.getTxtRemovingDir();
statusText.Replace(Zstr("%x"), dirObj.getFullName<LEFT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
- delHandling_.removeFolder<LEFT_SIDE>(dirObj); //throw (FileError)
+ delHandlingLeft_.removeFolder(dirObj.getObjRelativeName()); //throw (FileError)
{
//progress indicator update: DON'T forget to notify about implicitly deleted objects!
const SyncStatistics subObjects(dirObj);
//...then remove everything
dirObj.useSubFiles().clear();
dirObj.useSubLinks().clear();
- dirObj.useSubDirs().clear();
+ dirObj.useSubDirs ().clear();
procCallback_.updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), to<zen::Int64>(subObjects.getDataToProcess()));
}
break;
case SO_DELETE_RIGHT:
//status information
- statusText = delHandling_.getTxtRemovingDir();
+ statusText = delHandlingRight_.getTxtRemovingDir();
statusText.Replace(Zstr("%x"), dirObj.getFullName<RIGHT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
- delHandling_.removeFolder<RIGHT_SIDE>(dirObj); //throw (FileError)
+ delHandlingRight_.removeFolder(dirObj.getObjRelativeName()); //throw (FileError)
{
//progress indicator update: DON'T forget to notify about implicitly deleted objects!
const SyncStatistics subObjects(dirObj);
//...then remove everything
dirObj.useSubFiles().clear();
dirObj.useSubLinks().clear();
- dirObj.useSubDirs().clear();
+ dirObj.useSubDirs ().clear();
procCallback_.updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), to<zen::Int64>(subObjects.getDataToProcess()));
}
break;
- case SO_COPY_METADATA_TO_LEFT:
- statusText = txtWritingAttributes;
- statusText.Replace(Zstr("%x"), dirObj.getFullName<LEFT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
-
- if (dirObj.getShortName<LEFT_SIDE>() != dirObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only)
- renameFile(dirObj.getFullName<LEFT_SIDE>(),
- dirObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + dirObj.getShortName<RIGHT_SIDE>()); //throw (FileError);
- //copyFileTimes(dirObj.getFullName<RIGHT_SIDE>(), dirObj.getFullName<LEFT_SIDE>(), true); //throw (FileError) -> is executed after sub-objects have finished synchronization
- break;
-
- case SO_COPY_METADATA_TO_RIGHT:
- statusText = txtWritingAttributes;
- statusText.Replace(Zstr("%x"), dirObj.getFullName<RIGHT_SIDE>(), false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
-
- if (dirObj.getShortName<LEFT_SIDE>() != dirObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only)
- renameFile(dirObj.getFullName<RIGHT_SIDE>(),
- dirObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + dirObj.getShortName<LEFT_SIDE>()); //throw (FileError);
- //copyFileTimes(dirObj.getFullName<LEFT_SIDE>(), dirObj.getFullName<RIGHT_SIDE>(), true); //throw (FileError) -> is executed after sub-objects have finished synchronization
- break;
-
case SO_OVERWRITE_RIGHT:
case SO_OVERWRITE_LEFT:
assert(false);
@@ -1383,6 +1269,7 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const
//progress indicator update
//indicator is updated only if directory is sync'ed correctly (and if some work was done)!
procCallback_.updateProcessedData(1, 0); //each call represents one processed file
+ procCallback_.requestUiRefresh(); //may throw
}
@@ -1397,11 +1284,11 @@ bool dataLossPossible(const Zstring& dirName, const SyncStatistics& folderPairSt
namespace
{
-void makeSameLength(wxString& first, wxString& second)
+void makeSameLength(std::wstring& first, std::wstring& second)
{
const size_t maxPref = std::max(first.length(), second.length());
- first.Pad(maxPref - first.length(), wxT(' '), true);
- second.Pad(maxPref - second.length(), wxT(' '), true);
+ first .resize(maxPref, L' ');
+ second.resize(maxPref, L' ');
}
struct LessDependentDirectory : public std::binary_function<Zstring, Zstring, bool>
@@ -1426,27 +1313,24 @@ struct EqualDependentDirectory : public std::binary_function<Zstring, Zstring, b
class EnforceUpdateDatabase
{
public:
- EnforceUpdateDatabase(const BaseDirMapping& baseMap, ProcessCallback& procCallback) :
+ EnforceUpdateDatabase(const BaseDirMapping& baseMap) :
dbWasWritten(false),
- baseMap_(baseMap),
- procCallback_(procCallback) {}
+ baseMap_(baseMap) {}
~EnforceUpdateDatabase()
{
try
{
- tryWriteDB();
+ tryWriteDB(); //throw FileError, keep non-blocking!!!
}
catch (...) {}
}
- void tryWriteDB() //(try to gracefully) write db file; may throw in error-callback!
+ void tryWriteDB() //(try to gracefully) write db file; throw FileError
{
if (!dbWasWritten)
{
- procCallback_.reportInfo(wxToZ(_("Generating database...")));
- procCallback_.forceUiRefresh();
- tryReportingError(procCallback_, boost::bind(zen::saveToDisk, boost::cref(baseMap_))); //may throw in error-callback!
+ zen::saveToDisk(baseMap_); //throw FileError
dbWasWritten = true;
}
}
@@ -1454,7 +1338,6 @@ public:
private:
bool dbWasWritten;
const BaseDirMapping& baseMap_;
- ProcessCallback& procCallback_;
};
}
@@ -1480,11 +1363,30 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
ProcessCallback::PROCESS_SYNCHRONIZING);
if (!synchronizationNeeded(statisticsTotal))
- procCallback.reportInfo(wxToZ(_("Nothing to synchronize according to configuration!"))); //inform about this special case
+ procCallback.reportInfo(_("Nothing to synchronize according to configuration!")); //inform about this special case
std::deque<bool> skipFolderPair(folderCmp.size()); //folder pairs may be skipped after fatal errors were found
+
+ //initialize deletion handling: already required when checking for warnings
+ std::vector<std::pair<DeletionHandling, DeletionHandling>> delHandler;
+ for (FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j)
+ {
+ const size_t folderIndex = j - folderCmp.begin();
+ const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex];
+
+ delHandler.push_back(std::make_pair(DeletionHandling(folderPairCfg.handleDeletion,
+ folderPairCfg.custDelFolder,
+ j->getBaseDir<LEFT_SIDE>(),
+ procCallback),
+
+ DeletionHandling(folderPairCfg.handleDeletion,
+ folderPairCfg.custDelFolder,
+ j->getBaseDir<RIGHT_SIDE>(),
+ procCallback)));
+ }
+
//-------------------some basic checks:------------------------------------------
@@ -1500,6 +1402,8 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
typedef std::vector<std::pair<Zstring, std::pair<zen::Int64, zen::Int64> > > DirSpaceRequAvailList; //dirname / space required / space available
DirSpaceRequAvailList diskSpaceMissing;
+ typedef std::set<Zstring, LessDependentDirectory> DirRecyclerMissing;
+ DirRecyclerMissing recyclMissing;
//start checking folder pairs
for (FolderComparison::const_iterator j = folderCmp.begin(); j != folderCmp.end(); ++j)
@@ -1511,6 +1415,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
continue;
const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex];
+ const std::pair<DeletionHandling, DeletionHandling>& delHandlerFp = delHandler[folderIndex];
const SyncStatistics statisticsFolderPair(*j);
@@ -1567,14 +1472,6 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
if (statisticsFolderPair.getOverwrite() + statisticsFolderPair.getDelete() > 0)
{
- //test existence of Recycle Bin
- if (folderPairCfg.handleDeletion == zen::MOVE_TO_RECYCLE_BIN && !zen::recycleBinExists())
- {
- procCallback.reportFatalError(_("Recycle Bin not yet supported for this system!"));
- skipFolderPair[folderIndex] = true;
- continue;
- }
-
if (folderPairCfg.handleDeletion == zen::MOVE_TO_CUSTOM_DIRECTORY)
{
//check if user-defined directory for deletion was specified
@@ -1590,13 +1487,13 @@ 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))
{
- procCallback.reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\""));
+ procCallback.reportFatalError(_("Source directory does not exist anymore:") + "\n\"" + j->getBaseDir<LEFT_SIDE>() + "\"");
skipFolderPair[folderIndex] = true;
continue;
}
if (dataLossPossible(j->getBaseDir<RIGHT_SIDE>(), statisticsFolderPair))
{
- procCallback.reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\"") );
+ procCallback.reportFatalError(_("Source directory does not exist anymore:") + "\n\"" + j->getBaseDir<RIGHT_SIDE>() + "\"");
skipFolderPair[folderIndex] = true;
continue;
}
@@ -1606,10 +1503,12 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
significantDiff.push_back(std::make_pair(j->getBaseDir<LEFT_SIDE>(), j->getBaseDir<RIGHT_SIDE>()));
//check for sufficient free diskspace in left directory
- const std::pair<zen::Int64, zen::Int64> spaceNeeded = freeDiskSpaceNeeded(*j, folderPairCfg.handleDeletion, folderPairCfg.custDelFolder);
+ const std::pair<zen::Int64, zen::Int64> spaceNeeded = DiskSpaceNeeded(*j,
+ delHandlerFp.first.deletionFreesSpace(),
+ delHandlerFp.second.deletionFreesSpace()).getSpaceTotal();
wxLongLong freeDiskSpaceLeft;
- if (wxGetDiskSpace(zToWx(j->getBaseDir<LEFT_SIDE>()), NULL, &freeDiskSpaceLeft))
+ if (wxGetDiskSpace(toWx(j->getBaseDir<LEFT_SIDE>()), NULL, &freeDiskSpaceLeft))
{
if (0 < freeDiskSpaceLeft && //zero disk space is either an error or not: in both cases this warning message is obsolete (WebDav seems to report 0)
freeDiskSpaceLeft.ToDouble() < to<double>(spaceNeeded.first))
@@ -1618,12 +1517,30 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
//check for sufficient free diskspace in right directory
wxLongLong freeDiskSpaceRight;
- if (wxGetDiskSpace(zToWx(j->getBaseDir<RIGHT_SIDE>()), NULL, &freeDiskSpaceRight))
+ if (wxGetDiskSpace(toWx(j->getBaseDir<RIGHT_SIDE>()), NULL, &freeDiskSpaceRight))
{
if (0 < freeDiskSpaceRight && //zero disk space is either an error or not: in both cases this warning message is obsolete (WebDav seems to report 0)
freeDiskSpaceRight.ToDouble() < to<double>(spaceNeeded.second))
diskSpaceMissing.push_back(std::make_pair(j->getBaseDir<RIGHT_SIDE>(), std::make_pair(spaceNeeded.second, freeDiskSpaceRight.ToDouble())));
}
+
+ //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong
+#ifdef FFS_WIN
+ if (folderPairCfg.handleDeletion == MOVE_TO_RECYCLE_BIN)
+ {
+ if (statisticsFolderPair.getOverwrite<LEFT_SIDE>() +
+ statisticsFolderPair.getDelete <LEFT_SIDE>() > 0 &&
+
+ recycleBinExists(j->getBaseDir<LEFT_SIDE>()) != STATUS_REC_EXISTS)
+ recyclMissing.insert(j->getBaseDir<LEFT_SIDE>());
+
+ if (statisticsFolderPair.getOverwrite<RIGHT_SIDE>() +
+ statisticsFolderPair.getDelete <RIGHT_SIDE>() > 0 &&
+
+ recycleBinExists(j->getBaseDir<RIGHT_SIDE>()) != STATUS_REC_EXISTS)
+ recyclMissing.insert(j->getBaseDir<RIGHT_SIDE>());
+ }
+#endif
}
//check if unresolved conflicts exist
@@ -1639,7 +1556,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
wxString conflictDescription = i->second;
//conflictDescription.Replace(wxT("\n"), wxT(" ")); //remove line-breaks
- warningMessage += wxString(wxT("\"")) + zToWx(i->first) + wxT("\": ") + conflictDescription + wxT("\n\n");
+ warningMessage += wxString(wxT("\"")) + i->first + "\": " + conflictDescription + "\n\n";
}
if (statisticsTotal.getConflict() > static_cast<int>(firstConflicts.size()))
@@ -1658,8 +1575,8 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
for (DirPairList::const_iterator i = significantDiff.begin(); i != significantDiff.end(); ++i)
warningMessage += wxString(wxT("\n\n")) +
- zToWx(i->first) + wxT(" <-> ") + wxT("\n") +
- zToWx(i->second);
+ i->first + " <-> " + "\n" +
+ i->second;
warningMessage += wxString(wxT("\n\n")) +_("More than 50% of the total number of files will be copied or deleted!");
procCallback.reportWarning(warningMessage, m_warnings.warningSignificantDifference);
@@ -1671,9 +1588,9 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
{
wxString warningMessage = _("Not enough free disk space available in:");
- for (DirSpaceRequAvailList::const_iterator i = diskSpaceMissing.begin(); i != diskSpaceMissing.end(); ++i)
+ for (auto i = diskSpaceMissing.begin(); i != diskSpaceMissing.end(); ++i)
warningMessage += wxString(wxT("\n\n")) +
- wxT("\"") + zToWx(i->first) + wxT("\"\n") +
+ "\"" + i->first + "\"\n" +
_("Free disk space required:") + wxT(" ") + formatFilesizeToShortString(to<zen::UInt64>(i->second.first)) + wxT("\n") +
_("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(to<zen::UInt64>(i->second.second));
@@ -1681,6 +1598,20 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
}
+ //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong
+#ifdef FFS_WIN
+ if (!recyclMissing.empty())
+ {
+ wxString warningMessage = _("Recycle Bin is not available for the following paths! Files will be deleted permanently instead:");
+ warningMessage += L"\n";
+
+ std::for_each(recyclMissing.begin(), recyclMissing.end(),
+ [&](const Zstring& path) { warningMessage += L"\n" + toWx(path); });
+
+ procCallback.reportWarning(warningMessage, m_warnings.warningRecyclerMissing);
+ }
+#endif
+
//check if folders are used by multiple pairs in read/write access
std::vector<Zstring> conflictDirs;
for (DirWriteMap::const_iterator i = dirWriteCount.begin(); i != dirWriteCount.end(); ++i)
@@ -1693,30 +1624,15 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
wxString warningMessage = wxString(_("A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!")) + wxT("\n");
for (std::vector<Zstring>::const_iterator i = conflictDirs.begin(); i != conflictDirs.end(); ++i)
warningMessage += wxString(wxT("\n")) +
- wxT("\"") + zToWx(*i) + wxT("\"");
+ "\"" + *i + "\"";
procCallback.reportWarning(warningMessage, m_warnings.warningMultiFolderWriteAccess);
}
//-------------------end of basic checks------------------------------------------
#ifdef FFS_WIN
- struct ShadowCallback : public shadow::WaitingForShadow
- {
- ShadowCallback(ProcessCallback& updater) : procCallback_(updater) {}
- virtual void requestUiRefresh() //allowed to throw exceptions
- {
- procCallback_.requestUiRefresh();
- }
- virtual void reportInfo(const Zstring& text)
- {
- procCallback_.reportInfo(text);
- }
- private:
- ProcessCallback& procCallback_;
- } shadowCb(procCallback);
-
//shadow copy buffer: per sync-instance, not folder pair
- boost::scoped_ptr<shadow::ShadowCopy> shadowCopyHandler(copyLockedFiles_ ? new shadow::ShadowCopy(&shadowCb) : NULL);
+ boost::scoped_ptr<shadow::ShadowCopy> shadowCopyHandler(copyLockedFiles_ ? new shadow::ShadowCopy : NULL);
#endif
try
@@ -1726,12 +1642,12 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
(void)dummy;
//loop through all directory pairs
- assert(syncConfig.size() == folderCmp.size());
for (FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j)
{
const size_t folderIndex = j - folderCmp.begin();
const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex];
+ std::pair<DeletionHandling, DeletionHandling>& delHandlerFp = delHandler[folderIndex];
if (skipFolderPair[folderIndex]) //folder pairs may be skipped after fatal errors were found
continue;
@@ -1742,46 +1658,37 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
//------------------------------------------------------------------------------------------
//info about folder pair to be processed (useful for logfile)
- wxString left = wxString(_("Left")) + wxT(": ");
- wxString right = wxString(_("Right")) + wxT(": ");
+ std::wstring left = _("Left") + ": ";
+ std::wstring right = _("Right") + ": ";
makeSameLength(left, right);
- const wxString statusTxt = wxString(_("Processing folder pair:")) + wxT(" \n") +
- wxT("\t") + left + wxT("\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\"")+ wxT(" \n") +
- wxT("\t") + right + wxT("\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\"");
- procCallback.reportInfo(wxToZ(statusTxt));
+ const std::wstring statusTxt = _("Processing folder pair:") + " \n" +
+ "\t" + left + "\"" + j->getBaseDir<LEFT_SIDE>() + "\"" + " \n" +
+ "\t" + right + "\"" + j->getBaseDir<RIGHT_SIDE>() + "\"";
+ procCallback.reportInfo(statusTxt);
//------------------------------------------------------------------------------------------
//create base directories first (if not yet existing) -> no symlink or attribute copying! -> single error message instead of one per file (e.g. unplugged network drive)
- const Zstring dirnameLeft = j->getBaseDir<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR);
+ const Zstring dirnameLeft = j->getBaseDir<LEFT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR);
if (!dirnameLeft.empty() && !zen::dirExists(dirnameLeft))
{
if (!tryReportingError(procCallback, boost::bind(zen::createDirectory, boost::cref(dirnameLeft)))) //may throw in error-callback!
continue; //skip this folder pair
}
- const Zstring dirnameRight = j->getBaseDir<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR);
+ const Zstring dirnameRight = j->getBaseDir<RIGHT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR);
if (!dirnameRight.empty() && !zen::dirExists(dirnameRight))
{
if (!tryReportingError(procCallback, boost::bind(zen::createDirectory, boost::cref(dirnameRight)))) //may throw in error-callback!
continue; //skip this folder pair
}
//------------------------------------------------------------------------------------------
-
- //generate name of alternate deletion directory (unique for session AND folder pair)
- const DeletionHandling currentDelHandling(
- folderPairCfg.handleDeletion,
- folderPairCfg.custDelFolder,
- j->getBaseDir<LEFT_SIDE>(), j->getBaseDir<RIGHT_SIDE>(),
- procCallback);
-
- //------------------------------------------------------------------------------------------
//execute synchronization recursively
//update synchronization database (automatic sync only)
- std::auto_ptr<EnforceUpdateDatabase> dummyUpdateDb;
+ std::auto_ptr<EnforceUpdateDatabase> guardUpdateDb;
if (folderPairCfg.inAutomaticMode)
- dummyUpdateDb.reset(new EnforceUpdateDatabase(*j, procCallback));
+ guardUpdateDb.reset(new EnforceUpdateDatabase(*j));
//enforce removal of invalid entries (where element on both sides is empty)
struct RemoveInvalid
@@ -1799,19 +1706,21 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
#ifdef FFS_WIN
shadowCopyHandler.get(),
#endif
- currentDelHandling);
-
- //loop through all files twice; reason: first delete files (or overwrite big ones with smaller ones), then copy rest
- syncFP.startSync<true>(*j);
- syncFP.startSync<false>(*j);
+ delHandlerFp.first, delHandlerFp.second);
+ syncFP.startSync(*j);
//(try to gracefully) cleanup temporary folders (Recycle bin optimization) -> will be done in ~DeletionHandling anyway...
- currentDelHandling.tryCleanup(); //show error dialog if necessary
+ tryReportingError(procCallback, [&]() { delHandlerFp.first .tryCleanup(); }); //show error dialog if necessary
+ tryReportingError(procCallback, [&]() { delHandlerFp.second.tryCleanup(); }); //
//(try to gracefully) write database file (will be done in ~EnforceUpdateDatabase anyway...)
- if (dummyUpdateDb.get())
- dummyUpdateDb->tryWriteDB();
+ if (guardUpdateDb.get())
+ {
+ procCallback.reportInfo(_("Generating database..."));
+ procCallback.forceUiRefresh();
+ tryReportingError(procCallback, [&]() { guardUpdateDb->tryWriteDB(); });
+ }
}
}
catch (const std::exception& e)
@@ -1828,74 +1737,46 @@ template <class DelTargetCommand>
class WhileCopying : public zen::CallbackCopyFile //callback functionality
{
public:
- WhileCopying(zen::Int64& bytesTransferredLast,
+ WhileCopying(UInt64& bytesReported,
ProcessCallback& statusHandler,
const DelTargetCommand& cmd) :
- bytesTransferredLast_(bytesTransferredLast),
+ bytesReported_(bytesReported),
statusHandler_(statusHandler),
cmd_(cmd) {}
virtual void deleteTargetFile(const Zstring& targetFile) { cmd_(); }
- virtual Response updateCopyStatus(zen::UInt64 totalBytesTransferred)
+ virtual void updateCopyStatus(UInt64 totalBytesTransferred)
{
- const zen::Int64 totalBytes = to<zen::Int64>(totalBytesTransferred); //convert to signed
-
//inform about the (differential) processed amount of data
- statusHandler_.updateProcessedData(0, totalBytes - bytesTransferredLast_);
- bytesTransferredLast_ = totalBytes;
+ statusHandler_.updateProcessedData(0, to<Int64>(totalBytesTransferred) - to<Int64>(bytesReported_)); //throw()! -> this ensures client and service provider are in sync!
+ bytesReported_ = totalBytesTransferred; //
-#ifdef FFS_WIN
- statusHandler_.requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this
- if (statusHandler_.abortIsRequested())
- return CallbackCopyFile::CANCEL;
-#elif defined FFS_LINUX
- statusHandler_.requestUiRefresh(); //exceptions may be thrown here!
-#endif
- return CallbackCopyFile::CONTINUE;
+ statusHandler_.requestUiRefresh(); //may throw
}
private:
- zen::Int64& bytesTransferredLast_;
- ProcessCallback& statusHandler_;
+ UInt64& bytesReported_;
+ ProcessCallback& statusHandler_;
DelTargetCommand cmd_;
};
-class CleanUpStats //lambdas coming soon... unfortunately Loki::ScopeGuard is no option because of lazy function argument evaluation (-1 * ...)
-{
- bool dismissed;
- ProcessCallback& procCallback_;
- zen::Int64 bytesTransferred_;
-
-public:
- CleanUpStats(ProcessCallback& procCallback, zen::Int64 bytesTransferred) : dismissed(false), procCallback_(procCallback), bytesTransferred_(bytesTransferred) {}
- ~CleanUpStats()
- {
- if (!dismissed)
- try
- {
- procCallback_.updateProcessedData(0, bytesTransferred_ * -1); //throw ?
- }
- catch (...) {}
- }
- void dismiss() { dismissed = true; }
-};
-
-
-//copy file while executing procCallback->requestUiRefresh() calls
+//copy file while refreshing UI
template <class DelTargetCommand>
-void SynchronizeFolderPair::copyFileUpdating(const Zstring& source, const Zstring& target, const DelTargetCommand& cmd, zen::UInt64 totalBytesToCpy, int recursionLvl) const
+void SynchronizeFolderPair::copyFileUpdating(const Zstring& source, const Zstring& target, const DelTargetCommand& cmd, UInt64 totalBytesToCpy, int recursionLvl) const
{
const int exceptionPaths = 2;
+ //start of (possibly) long-running copy process: ensure status updates are performed regularly
+ UInt64 bytesReported;
+
+ //in error situation: undo communication of processed amount of data
+ Loki::ScopeGuard guardStatistics = Loki::MakeGuard([&]() { procCallback_.updateProcessedData(0, -1 * to<Int64>(bytesReported)); });
+
try
{
- //start of (possibly) long-running copy process: ensure status updates are performed regularly
- zen::Int64 totalBytesTransferred;
- CleanUpStats dummy(procCallback_, totalBytesTransferred); //error situation: undo communication of processed amount of data
-
- WhileCopying<DelTargetCommand> callback(totalBytesTransferred, procCallback_, cmd);
+ WhileCopying<DelTargetCommand> callback(bytesReported, procCallback_, cmd);
zen::copyFile(source, //type File implicitly means symlinks need to be dereferenced!
target,
@@ -1903,8 +1784,8 @@ void SynchronizeFolderPair::copyFileUpdating(const Zstring& source, const Zstrin
&callback); //throw (FileError, ErrorFileLocked);
//inform about the (remaining) processed amount of data
- dummy.dismiss();
- procCallback_.updateProcessedData(0, to<zen::Int64>(totalBytesToCpy) - totalBytesTransferred);
+ procCallback_.updateProcessedData(0, to<Int64>(totalBytesToCpy) - to<Int64>(bytesReported));
+ bytesReported = totalBytesToCpy;
}
#ifdef FFS_WIN
catch (ErrorFileLocked&)
@@ -1922,10 +1803,9 @@ void SynchronizeFolderPair::copyFileUpdating(const Zstring& source, const Zstrin
}
catch (const FileError& e)
{
- wxString errorMsg = _("Error copying locked file %x!");
- errorMsg.Replace(wxT("%x"), wxString(wxT("\"")) + zToWx(source) + wxT("\""));
- errorMsg += wxT("\n\n") + e.msg();
- throw FileError(errorMsg);
+ std::wstring errorMsg = _("Error copying locked file %x!");
+ replace(errorMsg, L"%x", std::wstring(L"\"") + source + "\"");
+ throw FileError(errorMsg + "\n\n" + e.msg());
}
//now try again
@@ -1938,8 +1818,8 @@ void SynchronizeFolderPair::copyFileUpdating(const Zstring& source, const Zstrin
//create folders "first" (see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080)
//using optimistic strategy: assume everything goes well, but cover up on error -> minimize file accesses
- const Zstring targetDir = target.BeforeLast(common::FILE_NAME_SEPARATOR);
- const Zstring templateDir = source.BeforeLast(common::FILE_NAME_SEPARATOR);
+ const Zstring targetDir = target.BeforeLast(FILE_NAME_SEPARATOR);
+ const Zstring templateDir = source.BeforeLast(FILE_NAME_SEPARATOR);
if (!targetDir.empty())
{
@@ -1960,6 +1840,8 @@ void SynchronizeFolderPair::copyFileUpdating(const Zstring& source, const Zstrin
if (verifyCopiedFiles_) //verify if data was copied correctly
verifyFileCopy(source, target); //throw (FileError)
+
+ guardStatistics.Dismiss();
}
@@ -1984,8 +1866,8 @@ void SynchronizeFolderPair::copySymlink(const Zstring& source, const Zstring& ta
//create folders "first" (see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080)
//using optimistic strategy: assume everything goes well, but cover up on error -> minimize file accesses
- const Zstring targetDir = target.BeforeLast(common::FILE_NAME_SEPARATOR);
- const Zstring templateDir = source.BeforeLast(common::FILE_NAME_SEPARATOR);
+ const Zstring targetDir = target.BeforeLast(FILE_NAME_SEPARATOR);
+ const Zstring templateDir = source.BeforeLast(FILE_NAME_SEPARATOR);
if (!targetDir.empty() && !zen::dirExists(targetDir))
{
@@ -2008,14 +1890,16 @@ void SynchronizeFolderPair::copySymlink(const Zstring& source, const Zstring& ta
template <zen::SelectedSide side>
void SynchronizeFolderPair::deleteSymlink(const SymLinkMapping& linkObj) const
{
+ const DeletionHandling& delHandling = side == LEFT_SIDE ? delHandlingLeft_ : delHandlingRight_;
+
switch (linkObj.getLinkType<side>())
{
case LinkDescriptor::TYPE_DIR:
- delHandling_.removeFolder<side>(linkObj); //throw (FileError)
+ delHandling.removeFolder(linkObj.getObjRelativeName()); //throw (FileError)
break;
case LinkDescriptor::TYPE_FILE: //Windows: true file symlink; Linux: file-link or broken link
- delHandling_.removeFile<side>(linkObj); //throw (FileError)
+ delHandling.removeFile(linkObj.getObjRelativeName()); //throw (FileError)
break;
}
}
@@ -2031,7 +1915,7 @@ struct VerifyCallback
};
-void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback* callback) // throw (FileError)
+void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback& callback) // throw (FileError)
{
static std::vector<char> memory1(1024 * 1024); //1024 kb seems to be a reasonable buffer size
static std::vector<char> memory2(1024 * 1024);
@@ -2042,7 +1926,7 @@ void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback* c
wxFile file1(::open(source.c_str(), O_RDONLY)); //utilize UTF-8 filename
#endif
if (!file1.IsOpened())
- throw FileError(wxString(_("Error opening file:")) + wxT(" \"") + zToWx(source) + wxT("\""));
+ throw FileError(_("Error opening file:") + " \"" + source + "\"");
#ifdef FFS_WIN
wxFile file2(zen::applyLongPathPrefix(target).c_str(), wxFile::read); //don't use buffered file input for verification!
@@ -2050,31 +1934,27 @@ void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback* c
wxFile file2(::open(target.c_str(), O_RDONLY)); //utilize UTF-8 filename
#endif
if (!file2.IsOpened()) //NO cleanup necessary for (wxFile) file1
- throw FileError(wxString(_("Error opening file:")) + wxT(" \"") + zToWx(target) + wxT("\""));
+ throw FileError(_("Error opening file:") + " \"" + target + "\"");
do
{
const size_t length1 = file1.Read(&memory1[0], memory1.size());
- if (file1.Error()) throw FileError(wxString(_("Error reading file:")) + wxT(" \"") + zToWx(source) + wxT("\""));
- callback->updateStatus(); //send progress updates
+ if (file1.Error())
+ throw FileError(_("Error reading file:") + " \"" + source + "\"");
+ callback.updateStatus(); //send progress updates
const size_t length2 = file2.Read(&memory2[0], memory2.size());
- if (file2.Error()) throw FileError(wxString(_("Error reading file:")) + wxT(" \"") + zToWx(target) + wxT("\""));
- callback->updateStatus(); //send progress updates
+ if (file2.Error())
+ throw FileError(_("Error reading file:") + " \"" + target + "\"");
+ callback.updateStatus(); //send progress updates
if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0)
- {
- const wxString errorMsg = wxString(_("Data verification error: Source and target file have different content!")) + wxT("\n");
- throw FileError(errorMsg + wxT("\"") + zToWx(source) + wxT("\" -> \n\"") + zToWx(target) + wxT("\""));
- }
+ throw FileError(_("Data verification error: Source and target file have different content!") + "\n" + "\"" + source + "\" -> \n\"" + target + "\"");
}
while (!file1.Eof());
if (!file2.Eof())
- {
- const wxString errorMsg = wxString(_("Data verification error: Source and target file have different content!")) + wxT("\n");
- throw FileError(errorMsg + wxT("\"") + zToWx(source) + wxT("\" -> \n\"") + zToWx(target) + wxT("\""));
- }
+ throw FileError(_("Data verification error: Source and target file have different content!") + "\n" + "\"" + source + "\" -> \n\"" + target + "\"");
}
@@ -2083,10 +1963,7 @@ class VerifyStatusUpdater : public VerifyCallback
public:
VerifyStatusUpdater(ProcessCallback& statusHandler) : statusHandler_(statusHandler) {}
- virtual void updateStatus()
- {
- statusHandler_.requestUiRefresh(); //trigger display refresh
- }
+ virtual void updateStatus() { statusHandler_.requestUiRefresh(); } //trigger display refresh
private:
ProcessCallback& statusHandler_;
@@ -2097,10 +1974,9 @@ void SynchronizeFolderPair::verifyFileCopy(const Zstring& source, const Zstring&
{
Zstring statusText = txtVerifying;
statusText.Replace(Zstr("%x"), target, false);
- procCallback_.reportInfo(statusText);
- procCallback_.requestUiRefresh(); //trigger display refresh
+ procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText));
VerifyStatusUpdater callback(procCallback_);
- tryReportingError(procCallback_, boost::bind(&::verifyFiles, boost::ref(source), boost::ref(target), &callback));
+ tryReportingError(procCallback_, [&]() { ::verifyFiles(source, target, callback);});
}
bgstack15