diff options
Diffstat (limited to 'algorithm.cpp')
-rw-r--r-- | algorithm.cpp | 249 |
1 files changed, 154 insertions, 95 deletions
diff --git a/algorithm.cpp b/algorithm.cpp index affc5bd1..0c47410a 100644 --- a/algorithm.cpp +++ b/algorithm.cpp @@ -12,12 +12,13 @@ #include <zen/recycler.h> #include <zen/stl_tools.h> #include <zen/scope_guard.h> -//#include <wx/msgdlg.h> +#include <zen/thread.h> #include "lib/resources.h" #include "lib/norm_filter.h" #include "lib/db_file.h" #include "lib/cmp_filetime.h" #include "lib/norm_filter.h" +#include "process_callback.h" //for UI_UPDATE_INTERVAL using namespace zen; using namespace std::rel_ops; @@ -53,19 +54,22 @@ private: void operator()(FileMapping& fileObj) const { - switch (fileObj.getCategory()) + const CompareFilesResult cat = fileObj.getCategory(); + + //##################### schedule old temporary files for deletion #################### + if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getShortName<LEFT_SIDE>(), TEMP_FILE_ENDING)) + return fileObj.setSyncDir(SYNC_DIR_LEFT); + else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getShortName<RIGHT_SIDE>(), TEMP_FILE_ENDING)) + return fileObj.setSyncDir(SYNC_DIR_RIGHT); + //#################################################################################### + + switch (cat) { case FILE_LEFT_SIDE_ONLY: - if (endsWith(fileObj.getShortName<LEFT_SIDE>(), zen::TEMP_FILE_ENDING)) - fileObj.setSyncDir(SYNC_DIR_LEFT); //schedule potentially existing temporary files for deletion - else - fileObj.setSyncDir(dirCfg.exLeftSideOnly); + fileObj.setSyncDir(dirCfg.exLeftSideOnly); break; case FILE_RIGHT_SIDE_ONLY: - if (endsWith(fileObj.getShortName<RIGHT_SIDE>(), zen::TEMP_FILE_ENDING)) - fileObj.setSyncDir(SYNC_DIR_RIGHT); //schedule potentially existing temporary files for deletion - else - fileObj.setSyncDir(dirCfg.exRightSideOnly); + fileObj.setSyncDir(dirCfg.exRightSideOnly); break; case FILE_RIGHT_NEWER: fileObj.setSyncDir(dirCfg.rightNewer); @@ -123,7 +127,16 @@ private: void operator()(DirMapping& dirObj) const { - switch (dirObj.getDirCategory()) + const CompareDirResult cat = dirObj.getDirCategory(); + + //########### schedule abandoned temporary recycle bin directory for deletion ########## + if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getShortName<LEFT_SIDE>(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SYNC_DIR_LEFT, dirObj); // + else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName<RIGHT_SIDE>(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SYNC_DIR_RIGHT, dirObj); //don't recurse below! + //####################################################################################### + + switch (cat) { case DIR_LEFT_SIDE_ONLY: dirObj.setSyncDir(dirCfg.exLeftSideOnly); @@ -148,8 +161,8 @@ private: const DirectionSet dirCfg; }; - //--------------------------------------------------------------------------------------------------------------- + struct AllEqual //test if non-equal items exist in scanned data { bool operator()(const HierarchyObject& hierObj) const @@ -385,15 +398,9 @@ private: //##################### schedule old temporary files for deletion #################### if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getShortName<LEFT_SIDE>(), TEMP_FILE_ENDING)) - { - fileObj.setSyncDir(SYNC_DIR_LEFT); - return; - } + return fileObj.setSyncDir(SYNC_DIR_LEFT); else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getShortName<RIGHT_SIDE>(), TEMP_FILE_ENDING)) - { - fileObj.setSyncDir(SYNC_DIR_RIGHT); - return; - } + return fileObj.setSyncDir(SYNC_DIR_RIGHT); //#################################################################################### //try to find corresponding database entry @@ -466,6 +473,13 @@ private: { const CompareDirResult cat = dirObj.getDirCategory(); + //########### schedule abandoned temporary recycle bin directory for deletion ########## + if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getShortName<LEFT_SIDE>(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SYNC_DIR_LEFT, dirObj); // + else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName<RIGHT_SIDE>(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SYNC_DIR_RIGHT, dirObj); //don't recurse below! + //####################################################################################### + //try to find corresponding database entry const InSyncDir::DirList::value_type* dbEntry = nullptr; if (dbContainer) @@ -527,9 +541,10 @@ private: { auto iterPair = cnt.equal_range(key); //since file id is already unique, we expect a single-element range at most auto it = std::find_if(iterPair.first, iterPair.second, - [&](const typename Container::value_type& item) + [&](const typename Container::value_type& item) { - return sameFileTime(std::get<0>(item.first), std::get<0>(key), 2); //respect 2 second FAT/FAT32 precision + return sameFileTime(std::get<0>(item.first), std::get<0>(key), 2); //respect 2 second FAT/FAT32 precision! + //the file time could be inferred from the source side after a file copy while a slightly different time is stored on a FAT32 disk! }); return it == iterPair.second ? cnt.end() : it; } @@ -754,7 +769,6 @@ void zen::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsOb fsObj.accept(recurse); } - //--------------- functions related to filtering ------------------------------------------------------------------------------------ template <bool include> @@ -1192,51 +1206,139 @@ bool tryReportingError(Function cmd, DeleteFilesHandler& handler) //return "true } } +#ifdef FFS_WIN +//recycleBinStatus() blocks seriously if recycle bin is really full and drive is slow +StatusRecycler recycleBinStatusUpdating(const Zstring& dirname, DeleteFilesHandler& callback) +{ + const std::wstring msg = replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", fmtFileName(dirname), false); + + auto ft = async([=] { return recycleBinStatus(dirname); }); + while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) + callback.reportStatus(msg); //may throw! + return ft.get(); +} +#endif + -struct RemoveCallbackImpl : public zen::CallbackRemoveDir +template <SelectedSide side> +void categorize(const std::set<FileSystemObject*>& rowsIn, + std::vector<FileSystemObject*>& deletePermanent, + std::vector<FileSystemObject*>& deleteRecyler, + bool useRecycleBin, + std::map<Zstring, bool, LessFilename>& hasRecyclerBuffer, + DeleteFilesHandler& callback) { - RemoveCallbackImpl(DeleteFilesHandler& handler) : handler_(handler) {} + auto hasRecycler = [&](const FileSystemObject& fsObj) -> bool + { +#ifdef FFS_WIN + const Zstring& baseDirPf = fsObj.root().getBaseDirPf<side>(); - virtual void notifyFileDeletion(const Zstring& filename) { handler_.notifyDeletion(filename); } - virtual void notifyDirDeletion (const Zstring& dirname) { handler_.notifyDeletion(dirname); } + auto it = hasRecyclerBuffer.find(baseDirPf); + if (it != hasRecyclerBuffer.end()) + return it->second; + return hasRecyclerBuffer.insert(std::make_pair(baseDirPf, recycleBinStatusUpdating(baseDirPf, callback) == STATUS_REC_EXISTS)).first->second; +#else + return true; +#endif + }; -private: - DeleteFilesHandler& handler_; -}; + for (auto it = rowsIn.begin(); it != rowsIn.end(); ++it) + if (!(*it)->isEmpty<side>()) + { + if (useRecycleBin && hasRecycler(**it)) //Windows' ::SHFileOperation() will delete permanently anyway, but we have a superior deletion routine + deleteRecyler.push_back(*it); + else + deletePermanent.push_back(*it); + } +} template <SelectedSide side> -struct PermanentDeleter : public FSObjectVisitor //throw FileError +struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow constructor!!! { - PermanentDeleter(DeleteFilesHandler& handler) : remCallback(handler) {} + ItemDeleter(bool useRecycleBin, DeleteFilesHandler& handler) : + handler_(handler), useRecycleBin_(useRecycleBin), remCallback(*this) + { + if (useRecycleBin_) + { + txtRemovingFile = _("Moving file %x to recycle bin" ); + txtRemovingDirectory = _("Moving folder %x to recycle bin" ); + txtRemovingSymlink = _("Moving symbolic link %x to recycle bin"); + } + else + { + txtRemovingFile = _("Deleting file %x" ); + txtRemovingDirectory = _("Deleting folder %x" ); + txtRemovingSymlink = _("Deleting symbolic link %x"); + } + } virtual void visit(const FileMapping& fileObj) { - if (zen::removeFile(fileObj.getFullName<side>())) //throw FileError - remCallback.notifyFileDeletion(fileObj.getFullName<side>()); + notifyFileDeletion(fileObj.getFullName<side>()); + + if (useRecycleBin_) + zen::recycleOrDelete(fileObj.getFullName<side>()); //throw FileError + else + zen::removeFile(fileObj.getFullName<side>()); //throw FileError } virtual void visit(const SymLinkMapping& linkObj) { - switch (linkObj.getLinkType<side>()) - { - case LinkDescriptor::TYPE_DIR: - zen::removeDirectory(linkObj.getFullName<side>(), &remCallback); //throw FileError - break; - case LinkDescriptor::TYPE_FILE: - if (zen::removeFile(linkObj.getFullName<side>())) //throw FileError - remCallback.notifyFileDeletion(linkObj.getFullName<side>()); - break; - } + notifySymlinkDeletion(linkObj.getFullName<side>()); + + if (useRecycleBin_) + zen::recycleOrDelete(linkObj.getFullName<side>()); //throw FileError + else + switch (linkObj.getLinkType<side>()) + { + case LinkDescriptor::TYPE_DIR: + zen::removeDirectory(linkObj.getFullName<side>()); //throw FileError + break; + case LinkDescriptor::TYPE_FILE: + zen::removeFile(linkObj.getFullName<side>()); //throw FileError + break; + } } virtual void visit(const DirMapping& dirObj) { - zen::removeDirectory(dirObj.getFullName<side>(), &remCallback); //throw FileError + notifyDirectoryDeletion(dirObj.getFullName<side>()); //notfied twice! see RemoveCallbackImpl -> no big deal + + if (useRecycleBin_) + zen::recycleOrDelete(dirObj.getFullName<side>()); //throw FileError + else + zen::removeDirectory(dirObj.getFullName<side>(), &remCallback); //throw FileError } private: + struct RemoveCallbackImpl : public zen::CallbackRemoveDir + { + RemoveCallbackImpl(ItemDeleter& itemDeleter) : itemDeleter_(itemDeleter) {} + + virtual void onBeforeFileDeletion(const Zstring& filename) { itemDeleter_.notifyFileDeletion (filename); } + virtual void onBeforeDirDeletion (const Zstring& dirname) { itemDeleter_.notifyDirectoryDeletion(dirname ); } + + private: + ItemDeleter& itemDeleter_; + }; + + void notifyFileDeletion (const Zstring& objName) { notifyItemDeletion(txtRemovingFile , objName); } + void notifyDirectoryDeletion(const Zstring& objName) { notifyItemDeletion(txtRemovingDirectory, objName); } + void notifySymlinkDeletion (const Zstring& objName) { notifyItemDeletion(txtRemovingSymlink , objName); } + + void notifyItemDeletion(const std::wstring& statusText, const Zstring& objName) + { + handler_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(objName))); + } + + DeleteFilesHandler& handler_; + const bool useRecycleBin_; RemoveCallbackImpl remCallback; + + std::wstring txtRemovingFile; + std::wstring txtRemovingDirectory; + std::wstring txtRemovingSymlink; }; @@ -1245,64 +1347,21 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& ptrList, bool useRecycleBin, DeleteFilesHandler& handler) { + ItemDeleter<side> deleter(useRecycleBin, handler); + for (auto it = ptrList.begin(); it != ptrList.end(); ++it) //VS 2010 bug prevents replacing this by std::for_each + lamba { FileSystemObject& fsObj = **it; //all pointers are required(!) to be bound - if (fsObj.isEmpty<side>()) //element may be implicitly deleted, e.g. if parent folder was deleted first - continue; - - tryReportingError([&] + if (!fsObj.isEmpty<side>()) //element may be implicitly deleted, e.g. if parent folder was deleted first + tryReportingError([&] { - if (useRecycleBin) - { - if (zen::recycleOrDelete(fsObj.getFullName<side>())) //throw FileError - handler.notifyDeletion(fsObj.getFullName<side>()); - } - else - { - PermanentDeleter<side> delPerm(handler); //throw FileError - fsObj.accept(delPerm); - } - + fsObj.accept(deleter); //throw FileError fsObj.removeObject<side>(); //if directory: removes recursively! }, handler); } } - - -template <SelectedSide side> -void categorize(const std::set<FileSystemObject*>& rowsIn, - std::vector<FileSystemObject*>& deletePermanent, - std::vector<FileSystemObject*>& deleteRecyler, - bool useRecycleBin, - std::map<Zstring, bool, LessFilename>& hasRecyclerBuffer) -{ - auto hasRecycler = [&](const FileSystemObject& fsObj) -> bool - { -#ifdef FFS_WIN - const Zstring& baseDirPf = fsObj.root().getBaseDirPf<side>(); - - auto it = hasRecyclerBuffer.find(baseDirPf); - if (it != hasRecyclerBuffer.end()) - return it->second; - return hasRecyclerBuffer.insert(std::make_pair(baseDirPf, recycleBinStatus(baseDirPf) == STATUS_REC_EXISTS)).first->second; -#else - return true; -#endif - }; - - for (auto it = rowsIn.begin(); it != rowsIn.end(); ++it) - if (!(*it)->isEmpty<side>()) - { - if (useRecycleBin && hasRecycler(**it)) //Windows' ::SHFileOperation() will delete permanently anyway, but we have a superior deletion routine - deleteRecyler.push_back(*it); - else - deletePermanent.push_back(*it); - } -} } - void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDeleteOnLeft, //refresh GUI grid after deletion to remove invalid rows const std::vector<FileSystemObject*>& rowsToDeleteOnRight, //all pointers need to be bound! FolderComparison& folderCmp, //attention: rows will be physically deleted! @@ -1378,8 +1437,8 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete std::vector<FileSystemObject*> deleteRecylerRight; std::map<Zstring, bool, LessFilename> hasRecyclerBuffer; - categorize<LEFT_SIDE >(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, hasRecyclerBuffer); - categorize<RIGHT_SIDE>(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, hasRecyclerBuffer); + categorize<LEFT_SIDE >(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, hasRecyclerBuffer, statusHandler); + categorize<RIGHT_SIDE>(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, hasRecyclerBuffer, statusHandler); //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong if (useRecycleBin && |