summaryrefslogtreecommitdiff
path: root/algorithm.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:27:42 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:27:42 +0200
commitb916407a2a06f8452e82b74dc44c54acbcc572b0 (patch)
tree46358e0bb035fca0f42edb4b5b8aa5f1613814af /algorithm.cpp
parent5.20 (diff)
downloadFreeFileSync-b916407a2a06f8452e82b74dc44c54acbcc572b0.tar.gz
FreeFileSync-b916407a2a06f8452e82b74dc44c54acbcc572b0.tar.bz2
FreeFileSync-b916407a2a06f8452e82b74dc44c54acbcc572b0.zip
5.21
Diffstat (limited to 'algorithm.cpp')
-rw-r--r--algorithm.cpp629
1 files changed, 309 insertions, 320 deletions
diff --git a/algorithm.cpp b/algorithm.cpp
index 2415cd18..dd068e0d 100644
--- a/algorithm.cpp
+++ b/algorithm.cpp
@@ -25,7 +25,7 @@ using namespace std::rel_ops;
void zen::swapGrids(const MainConfiguration& config, FolderComparison& folderCmp)
{
- std::for_each(begin(folderCmp), end(folderCmp), std::mem_fun_ref(&BaseDirPair::flip));
+ std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseObj) { baseObj.flip(); });
redetermineSyncDirection(config, folderCmp, [](const std::wstring&) {});
}
@@ -36,22 +36,22 @@ namespace
class Redetermine
{
public:
- static void execute(const DirectionSet& dirCfgIn, HierarchyObject& hierObj)
- {
- Redetermine(dirCfgIn).recurse(hierObj);
- }
+ static void execute(const DirectionSet& dirCfgIn, HierarchyObject& hierObj) { Redetermine(dirCfgIn).recurse(hierObj); }
private:
Redetermine(const DirectionSet& dirCfgIn) : dirCfg(dirCfgIn) {}
void recurse(HierarchyObject& hierObj) const
{
- std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](FilePair& fileObj) { (*this)(fileObj); });
- std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), [&](SymlinkPair& linkObj) { (*this)(linkObj); });
- std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](DirPair& dirObj) { (*this)(dirObj); });
+ for (FilePair& fileObj : hierObj.refSubFiles())
+ processFile(fileObj);
+ for (SymlinkPair& linkObj : hierObj.refSubLinks())
+ processLink(linkObj);
+ for (DirPair& dirObj : hierObj.refSubDirs())
+ processDir(dirObj);
}
- void operator()(FilePair& fileObj) const
+ void processFile(FilePair& fileObj) const
{
const CompareFilesResult cat = fileObj.getCategory();
@@ -92,7 +92,7 @@ private:
}
}
- void operator()(SymlinkPair& linkObj) const
+ void processLink(SymlinkPair& linkObj) const
{
switch (linkObj.getLinkCategory())
{
@@ -124,7 +124,7 @@ private:
}
}
- void operator()(DirPair& dirObj) const
+ void processDir(DirPair& dirObj) const
{
const CompareDirResult cat = dirObj.getDirCategory();
@@ -162,28 +162,26 @@ private:
//---------------------------------------------------------------------------------------------------------------
-struct AllEqual //test if non-equal items exist in scanned data
+//test if non-equal items exist in scanned data
+bool allItemsCategoryEqual(const HierarchyObject& hierObj)
{
- bool operator()(const HierarchyObject& hierObj) const
- {
- return std::all_of(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(),
- [](const FilePair& fileObj) { return fileObj.getCategory() == FILE_EQUAL; })&& //files
+ return std::all_of(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(),
+ [](const FilePair& fileObj) { return fileObj.getCategory() == FILE_EQUAL; })&& //files
- std::all_of(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(),
- [](const SymlinkPair& linkObj) { return linkObj.getLinkCategory() == SYMLINK_EQUAL; })&& //symlinks
+ std::all_of(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(),
+ [](const SymlinkPair& linkObj) { return linkObj.getLinkCategory() == SYMLINK_EQUAL; })&& //symlinks
- std::all_of(hierObj.refSubDirs(). begin(), hierObj.refSubDirs(). end(),
- [](const DirPair& dirObj)
- {
- return dirObj.getDirCategory() == DIR_EQUAL && AllEqual()(dirObj); //short circuit-behavior!
- }); //directories
- }
-};
+ std::all_of(hierObj.refSubDirs(). begin(), hierObj.refSubDirs().end(),
+ [](const DirPair& dirObj)
+ {
+ return dirObj.getDirCategory() == DIR_EQUAL && allItemsCategoryEqual(dirObj); //short circuit-behavior!
+ }); //directories
+}
}
bool zen::allElementsEqual(const FolderComparison& folderCmp)
{
- return std::all_of(begin(folderCmp), end(folderCmp), AllEqual());
+ return std::all_of(begin(folderCmp), end(folderCmp), [](const BaseDirPair& baseObj) { return allItemsCategoryEqual(baseObj); });
}
//---------------------------------------------------------------------------------------------------------------
@@ -224,7 +222,7 @@ bool stillInSync(const InSyncFile& dbFile, CompareVariant compareVar, size_t fil
switch (compareVar)
{
case CMP_BY_TIME_SIZE:
- if (dbFile.inSyncType == IN_SYNC_BINARY_EQUAL) return true; //special rule: this is already "good enough" for CMP_BY_TIME_SIZE!
+ if (dbFile.cmpVar == CMP_BY_CONTENT) return true; //special rule: this is already "good enough" for CMP_BY_TIME_SIZE!
return //case-sensitive short name match is a database invariant!
CmpFileTime::getResult(dbFile.left.lastWriteTimeRaw, dbFile.right.lastWriteTimeRaw, fileTimeTolerance) == CmpFileTime::TIME_EQUAL;
@@ -232,7 +230,7 @@ bool stillInSync(const InSyncFile& dbFile, CompareVariant compareVar, size_t fil
case CMP_BY_CONTENT:
//case-sensitive short name match is a database invariant!
- return dbFile.inSyncType == IN_SYNC_BINARY_EQUAL;
+ return dbFile.cmpVar == CMP_BY_CONTENT;
//in contrast to comparison, we don't care about modification time here!
}
assert(false);
@@ -273,14 +271,14 @@ bool stillInSync(const InSyncSymlink& dbLink, CompareVariant compareVar, size_t
switch (compareVar)
{
case CMP_BY_TIME_SIZE:
- if (dbLink.inSyncType == IN_SYNC_BINARY_EQUAL) return true; //special rule: this is already "good enough" for CMP_BY_TIME_SIZE!
+ if (dbLink.cmpVar == CMP_BY_CONTENT) return true; //special rule: this is already "good enough" for CMP_BY_TIME_SIZE!
return //case-sensitive short name match is a database invariant!
CmpFileTime::getResult(dbLink.left.lastWriteTimeRaw, dbLink.right.lastWriteTimeRaw, fileTimeTolerance) == CmpFileTime::TIME_EQUAL;
case CMP_BY_CONTENT:
//case-sensitive short name match is a database invariant!
- return dbLink.inSyncType == IN_SYNC_BINARY_EQUAL;
+ return dbLink.cmpVar == CMP_BY_CONTENT;
//in contrast to comparison, we don't care about modification time here!
}
assert(false);
@@ -314,98 +312,175 @@ bool stillInSync(const InSyncDir& dbDir)
//----------------------------------------------------------------------------------------------
-class RedetermineAuto
+class DetectMovedFiles
{
public:
- static void execute(BaseDirPair& baseDirectory, std::function<void(const std::wstring&)> reportWarning)
- {
- RedetermineAuto(baseDirectory, reportWarning);
- }
+ static void execute(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) { DetectMovedFiles(baseDirectory, dbContainer); }
private:
- RedetermineAuto(BaseDirPair& baseDirectory, std::function<void(const std::wstring&)> reportWarning) :
- txtBothSidesChanged(_("Both sides have changed since last synchronization.")),
- txtNoSideChanged(_("Cannot determine sync-direction:") + L" \n" + _("No change since last synchronization.")),
- txtDbNotInSync(_("Cannot determine sync-direction:") + L" \n" + _("The database entry is not in sync considering current settings.")),
+ DetectMovedFiles(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) :
cmpVar(baseDirectory.getCompVariant()),
- fileTimeTolerance(baseDirectory.getFileTimeTolerance()),
- reportWarning_(reportWarning)
+ fileTimeTolerance(baseDirectory.getFileTimeTolerance())
{
- if (AllEqual()(baseDirectory)) //nothing to do: abort and don't show any nag-screens
- return;
+ recurse(baseDirectory);
+
+ if (!exLeftOnly.empty() && !exRightOnly.empty())
+ detectFilePairs(dbContainer);
+ }
- //try to load sync-database files
- std::shared_ptr<InSyncDir> lastSyncState = loadDBFile(baseDirectory);
- if (!lastSyncState)
+ void recurse(HierarchyObject& hierObj)
+ {
+ for (FilePair& fileObj : hierObj.refSubFiles())
{
- //set conservative "two-way" directions
- DirectionSet twoWayCfg = getTwoWaySet();
+ const CompareFilesResult cat = fileObj.getCategory();
- Redetermine::execute(twoWayCfg, baseDirectory);
- return;
+ if (cat == FILE_LEFT_SIDE_ONLY)
+ {
+ if (fileObj.getFileId<LEFT_SIDE>() != FileId())
+ {
+ auto rv = exLeftOnly.insert(std::make_pair(fileObj.getFileId<LEFT_SIDE>(), &fileObj));
+ assert(rv.second);
+ if (!rv.second) //duplicate file ID!
+ rv.first->second = nullptr;
+ }
+ }
+ else if (cat == FILE_RIGHT_SIDE_ONLY)
+ {
+ if (fileObj.getFileId<RIGHT_SIDE>() != FileId())
+ {
+ auto rv = exRightOnly.insert(std::make_pair(fileObj.getFileId<RIGHT_SIDE>(), &fileObj));
+ assert(rv.second);
+ if (!rv.second) //duplicate file ID!
+ rv.first->second = nullptr;
+ }
+ }
}
+ for (DirPair& dirObj : hierObj.refSubDirs())
+ recurse(dirObj);
+ }
- //-> considering filter not relevant:
- //if narrowing filter: all ok; if widening filter (if file ex on both sides -> conflict, fine; if file ex. on one side: copy to other side: fine)
+ void detectFilePairs(const InSyncDir& container) const
+ {
+ for (auto& dbFile : container.files)
+ findAndSetMovePair(dbFile.second);
- recurse(baseDirectory, &*lastSyncState);
+ for (auto& dbDir : container.dirs)
+ detectFilePairs(dbDir.second);
+ }
- //----------- detect renamed files -----------------
- if (!exLeftOnly.empty() && !exRightOnly.empty())
- detectRenamedFiles(*lastSyncState);
+ static bool sameSizeAndDateLeft(const FilePair& fsObj, const InSyncFile& dbEntry)
+ {
+ return fsObj.getFileSize<LEFT_SIDE>() == dbEntry.fileSize &&
+ sameFileTime(fsObj.getLastWriteTime<LEFT_SIDE>(), dbEntry.left.lastWriteTimeRaw, 2); //respect 2 second FAT/FAT32 precision!
+ //PS: *never* allow 2 sec tolerance as container predicate!!
+ // => no strict weak ordering relation! reason: no transitivity of equivalence!
+ }
+ static bool sameSizeAndDateRight(const FilePair& fsObj, const InSyncFile& dbEntry)
+ {
+ return fsObj.getFileSize<RIGHT_SIDE>() == dbEntry.fileSize &&
+ sameFileTime(fsObj.getLastWriteTime<RIGHT_SIDE>(), dbEntry.right.lastWriteTimeRaw, 2);
}
- std::shared_ptr<InSyncDir> loadDBFile(const BaseDirPair& baseDirObj) //return nullptr on failure
+ void findAndSetMovePair(const InSyncFile& dbEntry) const
{
- try
- {
- return loadLastSynchronousState(baseDirObj); //throw FileError, FileErrorDatabaseNotExisting
- }
- catch (FileErrorDatabaseNotExisting&) {} //let's ignore this error, it seems there's no value in reporting it other than confuse users
- catch (FileError& error) //e.g. incompatible database version
+ const FileId idLeft = dbEntry.left .fileId;
+ const FileId idRight = dbEntry.right.fileId;
+
+ if (idLeft != FileId() &&
+ idRight != FileId() &&
+ stillInSync(dbEntry, cmpVar, fileTimeTolerance))
{
- reportWarning_(error.toString() + L" \n\n" +
- _("Setting default synchronization directions: Old files will be overwritten with newer files."));
+ auto itL = exLeftOnly.find(idLeft);
+ if (itL != exLeftOnly.end())
+ if (FilePair* fileLeftOnly = itL->second) //= nullptr, if duplicate ID!
+ if (sameSizeAndDateLeft(*fileLeftOnly, dbEntry))
+ {
+ auto itR = exRightOnly.find(idRight);
+ if (itR != exRightOnly.end())
+ if (FilePair* fileRightOnly = itR->second) //= nullptr, if duplicate ID!
+ if (sameSizeAndDateRight(*fileRightOnly, dbEntry))
+ if (fileLeftOnly ->getMoveRef() == nullptr && //the db may contain duplicate file ids on left or right side: e.g. consider aliasing through symlinks
+ fileRightOnly->getMoveRef() == nullptr) //=> should not be a problem (same id, size, date => alias!) but don't let a row participate in two move pairs!
+ {
+ fileLeftOnly ->setMoveRef(fileRightOnly->getId()); //found a pair, mark it!
+ fileRightOnly->setMoveRef(fileLeftOnly ->getId()); //
+ }
+ }
}
- return nullptr;
}
- void recurse(HierarchyObject& hierObj, const InSyncDir* dbContainer)
+ const CompareVariant cmpVar;
+ const size_t fileTimeTolerance;
+
+ std::map<FileId, FilePair*> exLeftOnly; //FilePair* == nullptr for duplicate ids! => consider aliasing through symlinks!
+ std::map<FileId, FilePair*> exRightOnly; //=> avoid ambiguity for mixtures of files/symlinks on one side and allow 1-1 mapping only!
+
+ /*
+ detect renamed files:
+
+ X -> |_| Create right
+ |_| -> Y Delete right
+
+ is detected as:
+
+ Rename Y to X on right
+
+ Algorithm:
+ ----------
+ DB-file left <--- (name, size, date) ---> DB-file right
+ | |
+ | (file ID, size, date) | (file ID, size, date)
+ \|/ \|/
+ file left only file right only
+
+ FAT caveat: File Ids are generally not stable when file is either moved or renamed!
+ => 1. Move/rename operations on FAT cannot be detected reliably.
+ => 2. database generally contains wrong file ID on FAT after renaming from .ffs_tmp files => correct file Ids in database only after next sync
+ => 3. even exFAT screws up (but less than FAT) and changes IDs after file move. Did they learn nothing from the past?
+
+ Possible refinement
+ -------------------
+ If the file ID is wrong (FAT) or not available, we could at least allow direct association by name, instead of breaking the chain completely: support NTFS -> FAT
+ */
+};
+
+//----------------------------------------------------------------------------------------------
+
+class RedetermineTwoWay
+{
+public:
+ static void execute(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) { RedetermineTwoWay(baseDirectory, dbContainer); }
+
+private:
+ RedetermineTwoWay(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) :
+ txtBothSidesChanged(_("Both sides have changed since last synchronization.")),
+ txtNoSideChanged(_("Cannot determine sync-direction:") + L" \n" + _("No change since last synchronization.")),
+ txtDbNotInSync(_("Cannot determine sync-direction:") + L" \n" + _("The database entry is not in sync considering current settings.")),
+ cmpVar(baseDirectory.getCompVariant()),
+ fileTimeTolerance(baseDirectory.getFileTimeTolerance())
+ {
+ //-> considering filter not relevant:
+ //if narrowing filter: all ok; if widening filter (if file ex on both sides -> conflict, fine; if file ex. on one side: copy to other side: fine)
+
+ recurse(baseDirectory, &dbContainer);
+ }
+
+ void recurse(HierarchyObject& hierObj, const InSyncDir* dbContainer) const
{
- std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](FilePair& fileObj) { processFile (fileObj, dbContainer); });
- std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), [&](SymlinkPair& linkObj) { processSymlink(linkObj, dbContainer); });
- std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](DirPair& dirObj) { processDir (dirObj, dbContainer); });
+ for (FilePair& fileObj : hierObj.refSubFiles())
+ processFile(fileObj, dbContainer);
+ for (SymlinkPair& linkObj : hierObj.refSubLinks())
+ processSymlink(linkObj, dbContainer);
+ for (DirPair& dirObj : hierObj.refSubDirs())
+ processDir(dirObj, dbContainer);
}
- void processFile(FilePair& fileObj, const InSyncDir* dbContainer)
+ void processFile(FilePair& fileObj, const InSyncDir* dbContainer) const
{
const CompareFilesResult cat = fileObj.getCategory();
if (cat == FILE_EQUAL)
return;
- //----------------- prepare detection of renamed files -----------------
- if (cat == FILE_LEFT_SIDE_ONLY)
- {
- if (fileObj.getFileId<LEFT_SIDE>() != FileId())
- {
- auto rv = exLeftOnly.insert(std::make_pair(fileObj.getFileId<LEFT_SIDE>(), &fileObj));
- assert(rv.second);
- if (!rv.second) //duplicate file ID!
- rv.first->second = nullptr;
- }
- }
- else if (cat == FILE_RIGHT_SIDE_ONLY)
- {
- if (fileObj.getFileId<RIGHT_SIDE>() != FileId())
- {
- auto rv = exRightOnly.insert(std::make_pair(fileObj.getFileId<RIGHT_SIDE>(), &fileObj));
- assert(rv.second);
- if (!rv.second) //duplicate file ID!
- rv.first->second = nullptr;
- }
- }
- //----------------------------------------------------------------------
-
//##################### schedule old temporary files for deletion ####################
if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getShortName<LEFT_SIDE>(), TEMP_FILE_ENDING))
return fileObj.setSyncDir(SyncDirection::LEFT);
@@ -443,7 +518,7 @@ private:
}
}
- void processSymlink(SymlinkPair& linkObj, const InSyncDir* dbContainer)
+ void processSymlink(SymlinkPair& linkObj, const InSyncDir* dbContainer) const
{
const CompareSymlinkResult cat = linkObj.getLinkCategory();
if (cat == SYMLINK_EQUAL)
@@ -479,7 +554,7 @@ private:
}
}
- void processDir(DirPair& dirObj, const InSyncDir* dbContainer)
+ void processDir(DirPair& dirObj, const InSyncDir* dbContainer) const
{
const CompareDirResult cat = dirObj.getDirCategory();
@@ -522,58 +597,7 @@ private:
}
}
- recurse(dirObj, dbEntry ? &dbEntry->second : nullptr); //recursion
- }
-
- //note: - we cannot integrate this traversal into "recurse()" since it may take a *slightly* different path: e.g. file renamed on both sides
- void detectRenamedFiles(InSyncDir& container)
- {
- std::for_each(container.files.begin(), container.files.end(),
- [&](std::pair<const Zstring, InSyncFile>& dbFile) { findAndSetMovePair(dbFile.second); });
-
- std::for_each(container.dirs.begin(), container.dirs.end(),
- [&](std::pair<const Zstring, InSyncDir>& dbDir) { detectRenamedFiles(dbDir.second); });
- }
-
- static bool sameSizeAndDateLeft(const FilePair& fsObj, const InSyncFile& dbEntry)
- {
- return fsObj.getFileSize<LEFT_SIDE>() == dbEntry.fileSize &&
- sameFileTime(fsObj.getLastWriteTime<LEFT_SIDE>(), dbEntry.left.lastWriteTimeRaw, 2); //respect 2 second FAT/FAT32 precision!
- //PS: *never* allow 2 sec tolerance as container predicate!!
- // => no strict weak ordering relation! reason: no transitivity of equivalence!
- }
- static bool sameSizeAndDateRight(const FilePair& fsObj, const InSyncFile& dbEntry)
- {
- return fsObj.getFileSize<RIGHT_SIDE>() == dbEntry.fileSize &&
- sameFileTime(fsObj.getLastWriteTime<RIGHT_SIDE>(), dbEntry.right.lastWriteTimeRaw, 2);
- }
-
- void findAndSetMovePair(const InSyncFile& dbEntry) const
- {
- const FileId idLeft = dbEntry.left .fileId;
- const FileId idRight = dbEntry.right.fileId;
-
- if (idLeft != FileId() &&
- idRight != FileId() &&
- stillInSync(dbEntry, cmpVar, fileTimeTolerance))
- {
- auto itL = exLeftOnly.find(idLeft);
- if (itL != exLeftOnly.end())
- if (FilePair* fileLeftOnly = itL->second) //= nullptr, if duplicate ID!
- if (sameSizeAndDateLeft(*fileLeftOnly, dbEntry))
- {
- auto itR = exRightOnly.find(idRight);
- if (itR != exRightOnly.end())
- if (FilePair* fileRightOnly = itR->second) //= nullptr, if duplicate ID!
- if (sameSizeAndDateRight(*fileRightOnly, dbEntry))
- if (fileLeftOnly ->getMoveRef() == nullptr && //the db may contain duplicate file ids on left or right side: e.g. consider aliasing through symlinks
- fileRightOnly->getMoveRef() == nullptr) //=> should not be a problem (same id, size, date => alias!) but don't let a row participate in two move pairs!
- {
- fileLeftOnly ->setMoveRef(fileRightOnly->getId()); //found a pair, mark it!
- fileRightOnly->setMoveRef(fileLeftOnly ->getId()); //
- }
- }
- }
+ recurse(dirObj, dbEntry ? &dbEntry->second : nullptr);
}
const std::wstring txtBothSidesChanged;
@@ -582,51 +606,6 @@ private:
const CompareVariant cmpVar;
const size_t fileTimeTolerance;
- std::function<void(const std::wstring&)> reportWarning_;
-
- std::map<FileId, FilePair*> exLeftOnly; //FilePair* == nullptr for duplicate ids! => consider aliasing through symlinks!
- std::map<FileId, FilePair*> exRightOnly; //=> avoid ambiguity for mixtures of files/symlinks on one side and allow 1-1 mapping only!
-
- /*
- detect renamed files
-
- X -> |_| Create right
- |_| -> Y Delete right
-
- is detected as:
-
- Rename Y to X on right
-
- Algorithm:
- ----------
- DB-file left <--- (name, size, date) ---> DB-file right
- | |
- | (file ID, size, date) | (file ID, size, date)
- \|/ \|/
- file left only file right only
-
- FAT caveat: File Ids are generally not stable when file is either moved or renamed!
- => 1. Move/rename operations on FAT cannot be detected reliably.
- => 2. database generally contains wrong file ID on FAT after renaming from .ffs_tmp files => correct file Ids in database only after next sync
- => 3. even exFAT screws up (but less than FAT) and changes IDs after file move. Did they learn nothing from the past?
-
- Possible refinement
- -------------------
- If the file ID is wrong (FAT) or not available, we could at least allow direct association by name, instead of breaking the chain completely: support NTFS -> FAT
-
- 1. find equal entries in database:
- std::hash_map: DB* |-> DB* onceEqual
-
- 2. build alternative mappings if file Id is available for database entries:
- std::map: FielId |-> DB* leftIdToDbRight
- std::map: FielId |-> DB* rightIdToDbRight
-
- 3. collect files on one side during determination of sync directions:
- std::vector<FilePair*, DB*> exLeftOnlyToDbRight -> first try to use file Id, if failed associate via file name instead
- std::hash_map<DB*, FilePair*> dbRightToexRightOnly ->
-
- 4. find renamed pairs
- */
};
}
@@ -652,15 +631,40 @@ std::vector<DirectionConfig> zen::extractDirectionCfg(const MainConfiguration& m
}
-void zen::redetermineSyncDirection(const DirectionConfig& directConfig, BaseDirPair& baseDirectory, std::function<void(const std::wstring&)> reportWarning)
+void zen::redetermineSyncDirection(const DirectionConfig& dirCfg, BaseDirPair& baseDirectory, std::function<void(const std::wstring&)> reportWarning)
{
- if (directConfig.var == DirectionConfig::AUTOMATIC)
- RedetermineAuto::execute(baseDirectory, reportWarning);
- else
+ //try to load sync-database files
+ std::shared_ptr<InSyncDir> lastSyncState;
+ if (dirCfg.var == DirectionConfig::TWOWAY || detectMovedFilesEnabled(dirCfg))
+ try
+ {
+ if (allItemsCategoryEqual(baseDirectory))
+ return; //nothing to do: abort and don't even try to open db files
+
+ lastSyncState = loadLastSynchronousState(baseDirectory); //throw FileError, FileErrorDatabaseNotExisting
+ }
+ catch (FileErrorDatabaseNotExisting&) {} //let's ignore this error, there's no value in reporting it other than confuse users
+ catch (FileError& error) //e.g. incompatible database version
+ {
+ reportWarning(error.toString() +
+ (dirCfg.var == DirectionConfig::TWOWAY ?
+ L" \n\n" + _("Setting default synchronization directions: Old files will be overwritten with newer files.") : std::wstring()));
+ }
+
+ //set sync directions
+ if (dirCfg.var == DirectionConfig::TWOWAY)
{
- DirectionSet dirCfg = extractDirections(directConfig);
- Redetermine::execute(dirCfg, baseDirectory);
+ if (lastSyncState)
+ RedetermineTwoWay::execute(baseDirectory, *lastSyncState);
+ else //default fallback
+ Redetermine::execute(getTwoWayUpdateSet(), baseDirectory);
}
+ else
+ Redetermine::execute(extractDirections(dirCfg), baseDirectory);
+
+ //detect renamed files
+ if (lastSyncState)
+ DetectMovedFiles::execute(baseDirectory, *lastSyncState);
}
@@ -681,113 +685,88 @@ void zen::redetermineSyncDirection(const MainConfiguration& mainCfg, FolderCompa
}
}
-
//---------------------------------------------------------------------------------------------------------------
-class SetNewDirection
-{
-public:
- SetNewDirection(SyncDirection newDirection) : newDirection_(newDirection) {}
- void operator()(FilePair& fileObj) const
+struct SetNewDirection
+{
+ static void execute(FilePair& fileObj, SyncDirection newDirection)
{
if (fileObj.getCategory() != FILE_EQUAL)
- fileObj.setSyncDir(newDirection_);
+ fileObj.setSyncDir(newDirection);
}
- void operator()(SymlinkPair& linkObj) const
+ static void execute(SymlinkPair& linkObj, SyncDirection newDirection)
{
if (linkObj.getLinkCategory() != SYMLINK_EQUAL)
- linkObj.setSyncDir(newDirection_);
+ linkObj.setSyncDir(newDirection);
}
- void operator()(DirPair& dirObj) const
+ static void execute(DirPair& dirObj, SyncDirection newDirection)
{
if (dirObj.getDirCategory() != DIR_EQUAL)
- dirObj.setSyncDir(newDirection_);
- execute(dirObj); //recursion
+ dirObj.setSyncDir(newDirection);
+
+ //recurse:
+ for (FilePair& fileObj : dirObj.refSubFiles())
+ execute(fileObj, newDirection);
+ for (SymlinkPair& linkObj : dirObj.refSubLinks())
+ execute(linkObj, newDirection);
+ for (DirPair& dirObj2 : dirObj.refSubDirs())
+ execute(dirObj2, newDirection);
}
-
-private:
- void execute(HierarchyObject& hierObj) const
- {
- std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](FilePair& fileObj) { (*this)(fileObj); });
- std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), [&](SymlinkPair& linkObj) { (*this)(linkObj); });
- std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](DirPair& dirObj) { (*this)(dirObj); });
- }
-
- const SyncDirection newDirection_;
};
void zen::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsObj)
{
- SetNewDirection dirSetter(newDirection);
-
//process subdirectories also!
struct Recurse: public FSObjectVisitor
{
- Recurse(const SetNewDirection& ds) : dirSetter_(ds) {}
+ Recurse(SyncDirection newDir) : newDir_(newDir) {}
virtual void visit(const FilePair& fileObj)
{
- dirSetter_(const_cast<FilePair&>(fileObj)); //phyiscal object is not const in this method anyway
+ SetNewDirection::execute(const_cast<FilePair&>(fileObj), newDir_); //phyiscal object is not const in this method anyway
}
virtual void visit(const SymlinkPair& linkObj)
{
- dirSetter_(const_cast<SymlinkPair&>(linkObj)); //
+ SetNewDirection::execute(const_cast<SymlinkPair&>(linkObj), newDir_); //
}
virtual void visit(const DirPair& dirObj)
{
- dirSetter_(const_cast<DirPair&>(dirObj)); //
+ SetNewDirection::execute(const_cast<DirPair&>(dirObj), newDir_); //
}
private:
- const SetNewDirection& dirSetter_;
- } recurse(dirSetter);
- fsObj.accept(recurse);
+ SyncDirection newDir_;
+ } setDirVisitor(newDirection);
+ fsObj.accept(setDirVisitor);
}
//--------------- functions related to filtering ------------------------------------------------------------------------------------
+namespace
+{
template <bool include>
-class InOrExcludeAllRows
+void inOrExcludeAllRows(zen::HierarchyObject& hierObj)
{
-public:
- void operator()(zen::BaseDirPair& baseDirectory) const //be careful with operator() to no get called by std::for_each!
- {
- execute(baseDirectory);
- }
-
- void execute(zen::HierarchyObject& hierObj) const //don't create ambiguity by replacing with operator()
- {
- std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](FilePair& fileObj) { (*this)(fileObj); });
- std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), [&](SymlinkPair& linkObj) { (*this)(linkObj); });
- std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](DirPair& dirObj) { (*this)(dirObj); });
- }
-
-private:
- void operator()(zen::FilePair& fileObj) const
- {
+ for (FilePair& fileObj : hierObj.refSubFiles())
fileObj.setActive(include);
- }
-
- void operator()(zen::SymlinkPair& linkObj) const
- {
+ for (SymlinkPair& linkObj : hierObj.refSubLinks())
linkObj.setActive(include);
- }
-
- void operator()(zen::DirPair& dirObj) const
+ for (DirPair& dirObj : hierObj.refSubDirs())
{
dirObj.setActive(include);
- execute(dirObj); //recursion
+ inOrExcludeAllRows<include>(dirObj); //recurse
}
-};
+}
+}
void zen::setActiveStatus(bool newStatus, zen::FolderComparison& folderCmp)
{
if (newStatus)
- std::for_each(begin(folderCmp), end(folderCmp), InOrExcludeAllRows<true>()); //include all rows
+ std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseDirObj) { inOrExcludeAllRows<true>(baseDirObj); }); //include all rows
else
- std::for_each(begin(folderCmp), end(folderCmp), InOrExcludeAllRows<false>()); //exclude all rows
+ std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseDirObj) { inOrExcludeAllRows<false>(baseDirObj); }); //exclude all rows
}
@@ -804,9 +783,9 @@ void zen::setActiveStatus(bool newStatus, zen::FileSystemObject& fsObj)
virtual void visit(const DirPair& dirObj)
{
if (newStatus_)
- InOrExcludeAllRows<true>().execute(const_cast<DirPair&>(dirObj)); //object is not physically const here anyway
+ inOrExcludeAllRows<true>(const_cast<DirPair&>(dirObj)); //object is not physically const here anyway
else
- InOrExcludeAllRows<false>().execute(const_cast<DirPair&>(dirObj)); //
+ inOrExcludeAllRows<false>(const_cast<DirPair&>(dirObj)); //
}
private:
const bool newStatus_;
@@ -851,29 +830,34 @@ template <FilterStrategy strategy>
class ApplyHardFilter
{
public:
- ApplyHardFilter(const HardFilter& filterProcIn) : filterProc(filterProcIn) {}
+ static void execute(HierarchyObject& hierObj, const HardFilter& filterProcIn) { ApplyHardFilter(hierObj, filterProcIn); }
+
+private:
+ ApplyHardFilter(HierarchyObject& hierObj, const HardFilter& filterProcIn) : filterProc(filterProcIn) { recurse(hierObj); }
- void execute(zen::HierarchyObject& hierObj) const
+ void recurse(HierarchyObject& hierObj) const
{
- std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](FilePair& fileObj) { (*this)(fileObj); });
- std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), [&](SymlinkPair& linkObj) { (*this)(linkObj); });
- std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](DirPair& dirObj) { (*this)(dirObj); });
+ for (FilePair& fileObj : hierObj.refSubFiles())
+ processFile(fileObj);
+ for (SymlinkPair& linkObj : hierObj.refSubLinks())
+ processLink(linkObj);
+ for (DirPair& dirObj : hierObj.refSubDirs())
+ processDir(dirObj);
};
-private:
- void operator()(zen::FilePair& fileObj) const
+ void processFile(FilePair& fileObj) const
{
if (Eval<strategy>().process(fileObj))
fileObj.setActive(filterProc.passFileFilter(fileObj.getObjRelativeName()));
}
- void operator()(zen::SymlinkPair& linkObj) const
+ void processLink(SymlinkPair& linkObj) const
{
if (Eval<strategy>().process(linkObj))
linkObj.setActive(filterProc.passFileFilter(linkObj.getObjRelativeName()));
}
- void operator()(zen::DirPair& dirObj) const
+ void processDir(DirPair& dirObj) const
{
bool subObjMightMatch = true;
const bool filterPassed = filterProc.passDirFilter(dirObj.getObjRelativeName(), &subObjMightMatch);
@@ -883,11 +867,11 @@ private:
if (!subObjMightMatch) //use same logic like directory traversing here: evaluate filter in subdirs only if objects could match
{
- InOrExcludeAllRows<false>().execute(dirObj); //exclude all files dirs in subfolders
+ inOrExcludeAllRows<false>(dirObj); //exclude all files dirs in subfolders
return;
}
- execute(dirObj); //recursion
+ recurse(dirObj);
}
const HardFilter& filterProc;
@@ -901,17 +885,22 @@ template <FilterStrategy strategy>
class ApplySoftFilter //falsify only! -> can run directly after "hard/base filter"
{
public:
- ApplySoftFilter(const SoftFilter& timeSizeFilter) : timeSizeFilter_(timeSizeFilter) {}
+ static void execute(HierarchyObject& hierObj, const SoftFilter& timeSizeFilter) { ApplySoftFilter(hierObj, timeSizeFilter); }
- void execute(zen::HierarchyObject& hierObj) const
+private:
+ ApplySoftFilter(HierarchyObject& hierObj, const SoftFilter& timeSizeFilter) : timeSizeFilter_(timeSizeFilter) { recurse(hierObj); }
+
+ void recurse(zen::HierarchyObject& hierObj) const
{
- std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](FilePair& fileObj) { (*this)(fileObj); });
- std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), [&](SymlinkPair& linkObj) { (*this)(linkObj); });
- std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](DirPair& dirObj) { (*this)(dirObj); });
+ for (FilePair& fileObj : hierObj.refSubFiles())
+ processFile(fileObj);
+ for (SymlinkPair& linkObj : hierObj.refSubLinks())
+ processLink(linkObj);
+ for (DirPair& dirObj : hierObj.refSubDirs())
+ processDir(dirObj);
};
-private:
- void operator()(zen::FilePair& fileObj) const
+ void processFile(FilePair& fileObj) const
{
if (Eval<strategy>().process(fileObj))
{
@@ -926,14 +915,14 @@ private:
//the only case with partially unclear semantics:
//file and time filters may match or not match on each side, leaving a total of 16 combinations for both sides!
/*
- ST S T - ST := match size and time
- --------- S := match size only
- ST |X|X|X|X| T := match time only
- ------------ - := no match
+ ST S T - ST := match size and time
+ --------- S := match size only
+ ST |X|X|X|X| T := match time only
+ ------------ - := no match
S |X|O|?|O|
- ------------ X := include row
- T |X|?|O|O| O := exclude row
- ------------ ? := unclear
+ ------------ X := include row
+ T |X|?|O|O| O := exclude row
+ ------------ ? := unclear
- |X|O|O|O|
------------
*/
@@ -946,7 +935,7 @@ private:
}
}
- void operator()(zen::SymlinkPair& linkObj) const
+ void processLink(SymlinkPair& linkObj) const
{
if (Eval<strategy>().process(linkObj))
{
@@ -960,12 +949,12 @@ private:
}
}
- void operator()(zen::DirPair& dirObj) const
+ void processDir(DirPair& dirObj) const
{
if (Eval<strategy>().process(dirObj))
dirObj.setActive(timeSizeFilter_.matchFolder()); //if date filter is active we deactivate all folders: effectively gets rid of empty folders!
- execute(dirObj); //recursion
+ recurse(dirObj);
}
template <SelectedSide side, class T>
@@ -987,14 +976,14 @@ private:
void zen::addHardFiltering(BaseDirPair& baseDirObj, const Zstring& excludeFilter)
{
- ApplyHardFilter<STRATEGY_AND>(NameFilter(FilterConfig().includeFilter, excludeFilter)).execute(baseDirObj);
+ ApplyHardFilter<STRATEGY_AND>::execute(baseDirObj, NameFilter(FilterConfig().includeFilter, excludeFilter));
}
void zen::addSoftFiltering(BaseDirPair& baseDirObj, const SoftFilter& timeSizeFilter)
{
if (!timeSizeFilter.isNull()) //since we use STRATEGY_AND, we may skip a "null" filter
- ApplySoftFilter<STRATEGY_AND>(timeSizeFilter).execute(baseDirObj);
+ ApplySoftFilter<STRATEGY_AND>::execute(baseDirObj, timeSizeFilter);
}
@@ -1019,7 +1008,7 @@ void zen::applyFiltering(FolderComparison& folderCmp, const MainConfiguration& m
const NormalizedFilter normFilter = normalizeFilters(mainCfg.globalFilter, it->localFilter);
//"set" hard filter
- ApplyHardFilter<STRATEGY_SET>(*normFilter.nameFilter).execute(baseDirectory);
+ ApplyHardFilter<STRATEGY_SET>::execute(baseDirectory, *normFilter.nameFilter);
//"and" soft filter
addSoftFiltering(baseDirectory, normFilter.timeSizeFilter);
@@ -1030,20 +1019,26 @@ void zen::applyFiltering(FolderComparison& folderCmp, const MainConfiguration& m
class FilterByTimeSpan
{
public:
- FilterByTimeSpan(const Int64& timeFrom,
+ static void execute(HierarchyObject& hierObj, const Int64& timeFrom, const Int64& timeTo) { FilterByTimeSpan(hierObj, timeFrom, timeTo); }
+
+private:
+ FilterByTimeSpan(HierarchyObject& hierObj,
+ const Int64& timeFrom,
const Int64& timeTo) :
timeFrom_(timeFrom),
- timeTo_(timeTo) {}
+ timeTo_(timeTo) { recurse(hierObj); }
- void execute(zen::HierarchyObject& hierObj) const
+ void recurse(HierarchyObject& hierObj) const
{
- std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](FilePair& fileObj) { (*this)(fileObj); });
- std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), [&](SymlinkPair& linkObj) { (*this)(linkObj); });
- std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](DirPair& dirObj) { (*this)(dirObj); });
+ for (FilePair& fileObj : hierObj.refSubFiles())
+ processFile(fileObj);
+ for (SymlinkPair& linkObj : hierObj.refSubLinks())
+ processLink(linkObj);
+ for (DirPair& dirObj : hierObj.refSubDirs())
+ processDir(dirObj);
};
-private:
- void operator()(zen::FilePair& fileObj) const
+ void processFile(FilePair& fileObj) const
{
if (fileObj.isEmpty<LEFT_SIDE>())
fileObj.setActive(matchTime<RIGHT_SIDE>(fileObj));
@@ -1054,7 +1049,7 @@ private:
matchTime<LEFT_SIDE>(fileObj));
}
- void operator()(zen::SymlinkPair& linkObj) const
+ void processLink(SymlinkPair& linkObj) const
{
if (linkObj.isEmpty<LEFT_SIDE>())
linkObj.setActive(matchTime<RIGHT_SIDE>(linkObj));
@@ -1065,10 +1060,10 @@ private:
matchTime<LEFT_SIDE> (linkObj));
}
- void operator()(zen::DirPair& dirObj) const
+ void processDir(DirPair& dirObj) const
{
dirObj.setActive(false);
- execute(dirObj); //recursion
+ recurse(dirObj);
}
template <SelectedSide side, class T>
@@ -1085,8 +1080,7 @@ private:
void zen::applyTimeSpanFilter(FolderComparison& folderCmp, const Int64& timeFrom, const Int64& timeTo)
{
- FilterByTimeSpan spanFilter(timeFrom, timeTo);
- std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { spanFilter.execute(baseDirObj); });
+ std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { FilterByTimeSpan::execute(baseDirObj, timeFrom, timeTo); });
}
@@ -1235,9 +1229,9 @@ struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow cons
{
if (useRecycleBin_)
{
- txtRemovingFile = _("Moving file %x to recycle bin" );
- txtRemovingDirectory = _("Moving folder %x to recycle bin" );
- txtRemovingSymlink = _("Moving symbolic link %x to recycle bin");
+ txtRemovingFile = _("Moving file %x to the recycle bin" );
+ txtRemovingDirectory = _("Moving folder %x to the recycle bin" );
+ txtRemovingSymlink = _("Moving symbolic link %x to the recycle bin");
}
else
{
@@ -1264,17 +1258,12 @@ struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow cons
if (useRecycleBin_)
zen::recycleOrDelete(linkObj.getFullName<side>()); //throw FileError
else
- switch (getSymlinkType(linkObj.getFullName<side>()))
- {
- case SYMLINK_TYPE_DIR:
- zen::removeDirectory(linkObj.getFullName<side>()); //throw FileError
- break;
-
- case SYMLINK_TYPE_FILE:
- case SYMLINK_TYPE_UNKNOWN:
- zen::removeFile(linkObj.getFullName<side>()); //throw FileError
- break;
- }
+ {
+ if (dirExists(linkObj.getFullName<side>())) //dir symlink
+ zen::removeDirectory(linkObj.getFullName<side>()); //throw FileError
+ else //file symlink, broken symlink
+ zen::removeFile(linkObj.getFullName<side>()); //throw FileError
+ }
}
virtual void visit(const DirPair& dirObj)
@@ -1386,7 +1375,7 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete
{
SyncDirection newDir = SyncDirection::NONE;
- if (cfgIter->second.var == DirectionConfig::AUTOMATIC)
+ if (cfgIter->second.var == DirectionConfig::TWOWAY)
newDir = fsObj.isEmpty<LEFT_SIDE>() ? SyncDirection::RIGHT : SyncDirection::LEFT;
else
{
@@ -1420,7 +1409,7 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete
if (useRecycleBin &&
std::any_of(hasRecyclerBuffer.begin(), hasRecyclerBuffer.end(), [](std::pair<Zstring, bool> item) { return !item.second; }))
{
- std::wstring msg = _("The Recycle Bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n";
+ std::wstring msg = _("The recycle bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n";
for (auto it = hasRecyclerBuffer.begin(); it != hasRecyclerBuffer.end(); ++it)
if (!it->second)
bgstack15