diff options
Diffstat (limited to 'algorithm.cpp')
-rw-r--r-- | algorithm.cpp | 248 |
1 files changed, 154 insertions, 94 deletions
diff --git a/algorithm.cpp b/algorithm.cpp index 6de28baa..6ca1f73a 100644 --- a/algorithm.cpp +++ b/algorithm.cpp @@ -6,7 +6,7 @@ #include "algorithm.h" #include <set> -#include <iterator> +//#include <iterator> #include <stdexcept> #include <tuple> #include "lib/resources.h" @@ -80,7 +80,7 @@ private: case FILE_CONFLICT: case FILE_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" if (dirCfg.conflict == SYNC_DIR_NONE) - fileObj.setSyncDirConflict(getCategoryDescription(fileObj)); //take over category conflict + fileObj.setSyncDirConflict(fileObj.getCatExtraDescription()); //take over category conflict else fileObj.setSyncDir(dirCfg.conflict); break; @@ -109,7 +109,7 @@ private: case SYMLINK_CONFLICT: case SYMLINK_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" if (dirCfg.conflict == SYNC_DIR_NONE) - linkObj.setSyncDirConflict(getCategoryDescription(linkObj)); //take over category conflict + linkObj.setSyncDirConflict(linkObj.getCatExtraDescription()); //take over category conflict else linkObj.setSyncDir(dirCfg.conflict); break; @@ -137,7 +137,7 @@ private: break; case DIR_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" if (dirCfg.conflict == SYNC_DIR_NONE) - dirObj.setSyncDirConflict(getCategoryDescription(dirObj)); //take over category conflict + dirObj.setSyncDirConflict(dirObj.getCatExtraDescription()); //take over category conflict else dirObj.setSyncDir(dirCfg.conflict); break; @@ -162,11 +162,9 @@ struct AllEqual //test if non-equal items exist in scanned data [](const SymLinkMapping& linkObj) { return linkObj.getLinkCategory() == SYMLINK_EQUAL; }) && //symlinks std::all_of(hierObj.refSubDirs(). begin(), hierObj.refSubDirs(). end(), - [](const DirMapping& dirObj) -> bool + [](const DirMapping& dirObj) { - if (dirObj.getDirCategory() != DIR_EQUAL) - return false; - return AllEqual()(dirObj); //recurse + return dirObj.getDirCategory() == DIR_EQUAL && AllEqual()(dirObj); //short circuit-behavior! }); //directories } }; @@ -351,7 +349,7 @@ private: { return loadLastSynchronousState(baseMap); //throw FileError, FileErrorDatabaseNotExisting } - catch (FileErrorDatabaseNotExisting&) {} //let's ignore these errors for now... + 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 { reportWarning_(error.toString() + L" \n\n" + @@ -1111,8 +1109,7 @@ std::pair<wxString, int> zen::deleteFromGridAndHDPreview(const std::vector<FileS const std::vector<FileSystemObject*>& selectionRight, bool deleteOnBothSides) { - //fast replacement for wxString modelling exponential growth - typedef Zbase<wchar_t> zxString; //for use with UI texts + typedef Zbase<wchar_t> zxString; //fast replacement for wxString modelling exponential growth zxString filesToDelete; int totalDelCount = 0; @@ -1174,97 +1171,138 @@ std::pair<wxString, int> zen::deleteFromGridAndHDPreview(const std::vector<FileS namespace { +template <typename Function> inline +bool tryReportingError(Function cmd, DeleteFilesHandler& handler) //return "true" on success, "false" if error was ignored +{ + for (;;) + try + { + cmd(); //throw FileError + return true; + } + catch (FileError& error) + { + switch (handler.reportError(error.toString())) //may throw! + { + case DeleteFilesHandler::IGNORE_ERROR: + return false; + case DeleteFilesHandler::RETRY: + break; //continue with loop + default: + assert(false); + break; + } + } +} + + struct RemoveCallbackImpl : public zen::CallbackRemoveDir { - RemoveCallbackImpl(DeleteFilesHandler& deleteCallback) : deleteCallback_(deleteCallback) {} + RemoveCallbackImpl(DeleteFilesHandler& handler) : handler_(handler) {} - virtual void notifyFileDeletion(const Zstring& filename) { deleteCallback_.notifyDeletion(filename); } - virtual void notifyDirDeletion (const Zstring& dirname) { deleteCallback_.notifyDeletion(dirname); } + virtual void notifyFileDeletion(const Zstring& filename) { handler_.notifyDeletion(filename); } + virtual void notifyDirDeletion (const Zstring& dirname) { handler_.notifyDeletion(dirname); } private: - DeleteFilesHandler& deleteCallback_; + DeleteFilesHandler& handler_; }; -} -template <SelectedSide side, class InputIterator> -void deleteFromGridAndHDOneSide(InputIterator first, InputIterator last, +template <SelectedSide side> +struct PermanentDeleter : public FSObjectVisitor //throw FileError +{ + PermanentDeleter(DeleteFilesHandler& handler) : remCallback(handler) {} + + virtual void visit(const FileMapping& fileObj) + { + if (zen::removeFile(fileObj.getFullName<side>())) //throw FileError + remCallback.notifyFileDeletion(fileObj.getFullName<side>()); + } + + 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; + } + } + + virtual void visit(const DirMapping& dirObj) + { + zen::removeDirectory(dirObj.getFullName<side>(), &remCallback); //throw FileError + } + +private: + RemoveCallbackImpl remCallback; +}; + + +template <SelectedSide side> +void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& ptrList, bool useRecycleBin, - DeleteFilesHandler& statusHandler) + DeleteFilesHandler& handler) { - for (auto iter = first; iter != last; ++iter) //VS 2010 bug prevents replacing this by std::for_each + lamba + for (auto iter = ptrList.begin(); iter != ptrList.end(); ++iter) //VS 2010 bug prevents replacing this by std::for_each + lamba { FileSystemObject& fsObj = **iter; //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; - while (true) + tryReportingError([&] { - try + if (useRecycleBin) { - if (!fsObj.isEmpty<side>()) //element may become implicitly delted, e.g. if parent folder was deleted first - { - if (useRecycleBin) - { - if (zen::recycleOrDelete(fsObj.getFullName<side>())) //throw FileError - statusHandler.notifyDeletion(fsObj.getFullName<side>()); - } - else - { - RemoveCallbackImpl removeCallback(statusHandler); - - //del directories and symlinks - struct DeletePermanently : public FSObjectVisitor - { - DeletePermanently(RemoveCallbackImpl& remCallback) : remCallback_(remCallback) {} - - virtual void visit(const FileMapping& fileObj) - { - if (zen::removeFile(fileObj.getFullName<side>())) - remCallback_.notifyFileDeletion(fileObj.getFullName<side>()); - } - - virtual void visit(const SymLinkMapping& linkObj) - { - switch (linkObj.getLinkType<side>()) - { - case LinkDescriptor::TYPE_DIR: - zen::removeDirectory(linkObj.getFullName<side>(), &remCallback_); - break; - case LinkDescriptor::TYPE_FILE: - if (zen::removeFile(linkObj.getFullName<side>())) - remCallback_.notifyFileDeletion(linkObj.getFullName<side>()); - break; - } - } - - virtual void visit(const DirMapping& dirObj) - { - zen::removeDirectory(dirObj.getFullName<side>(), &remCallback_); - } - - private: - RemoveCallbackImpl& remCallback_; - } delPerm(removeCallback); - fsObj.accept(delPerm); - } - - fsObj.removeObject<side>(); //if directory: removes recursively! - } - break; + if (zen::recycleOrDelete(fsObj.getFullName<side>())) //throw FileError + handler.notifyDeletion(fsObj.getFullName<side>()); } - catch (const FileError& error) + else { - DeleteFilesHandler::Response rv = statusHandler.reportError(error.toString()); + PermanentDeleter<side> delPerm(handler); //throw FileError + fsObj.accept(delPerm); + } - if (rv == DeleteFilesHandler::IGNORE_ERROR) - break; + fsObj.removeObject<side>(); //if directory: removes recursively! + }, handler); + } +} - else if (rv == DeleteFilesHandler::RETRY) - ; //continue in loop - else - assert (false); - } + +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 iter = hasRecyclerBuffer.find(baseDirPf); + if (iter != hasRecyclerBuffer.end()) + return iter->second; + return hasRecyclerBuffer.insert(std::make_pair(baseDirPf, recycleBinStatus(baseDirPf) == STATUS_REC_EXISTS)).first->second; +#else + return true; +#endif + }; + + for (auto iter = rowsIn.begin(); iter != rowsIn.end(); ++iter) + if (!(*iter)->isEmpty<side>()) + { + if (useRecycleBin && hasRecycler(**iter)) //Windows' ::SHFileOperation() will delete permanently anyway, but we have a superior deletion routine + deleteRecyler.push_back(*iter); + else + deletePermanent.push_back(*iter); } - } +} } @@ -1274,7 +1312,8 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete const std::vector<DirectionConfig>& directCfgs, bool deleteOnBothSides, bool useRecycleBin, - DeleteFilesHandler& statusHandler) + DeleteFilesHandler& statusHandler, + bool& warningRecyclerMissing) { if (folderCmp.empty()) return; @@ -1282,21 +1321,20 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete throw std::logic_error("Programming Error: Contract violation!"); //build up mapping from base directory to corresponding direction config - std::map<const BaseDirMapping*, DirectionConfig> baseDirCfgs; + hash_map<const BaseDirMapping*, DirectionConfig> baseDirCfgs; for (auto iter = folderCmp.begin(); iter != folderCmp.end(); ++iter) baseDirCfgs[&** iter] = directCfgs[iter - folderCmp.begin()]; std::set<FileSystemObject*> deleteLeft (rowsToDeleteOnLeft .begin(), rowsToDeleteOnLeft .end()); std::set<FileSystemObject*> deleteRight(rowsToDeleteOnRight.begin(), rowsToDeleteOnRight.end()); - if (deleteOnBothSides) { deleteLeft.insert(deleteRight.begin(), deleteRight.end()); deleteRight = deleteLeft; } - set_remove_if(deleteLeft, std::mem_fun(&FileSystemObject::isEmpty<LEFT_SIDE>)); //remove empty rows to ensure correct statistics - set_remove_if(deleteRight, std::mem_fun(&FileSystemObject::isEmpty<RIGHT_SIDE>)); // + set_remove_if(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<LEFT_SIDE >(); }); //still needed? + set_remove_if(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); // //ensure cleanup: redetermination of sync-directions and removal of invalid rows auto updateDirection = [&]() @@ -1320,7 +1358,7 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete newDir = fsObj.isEmpty<LEFT_SIDE>() ? SYNC_DIR_RIGHT : SYNC_DIR_LEFT; else { - DirectionSet dirCfg = extractDirections(cfgIter->second); + const DirectionSet& dirCfg = extractDirections(cfgIter->second); newDir = fsObj.isEmpty<LEFT_SIDE>() ? dirCfg.exRightSideOnly : dirCfg.exLeftSideOnly; } @@ -1336,11 +1374,33 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete }; ZEN_ON_SCOPE_EXIT(updateDirection()); //MSVC: assert is a macro and it doesn't play nice with ZEN_ON_SCOPE_EXIT, surprise... wasn't there something about macros being "evil"? - deleteFromGridAndHDOneSide<LEFT_SIDE>(deleteLeft.begin(), deleteLeft.end(), - useRecycleBin, - statusHandler); + //categorize rows into permanent deletion and recycle bin + std::vector<FileSystemObject*> deletePermanentLeft; + std::vector<FileSystemObject*> deletePermanentRight; + std::vector<FileSystemObject*> deleteRecylerLeft; + 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); + + //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong + if (useRecycleBin && + std::any_of(hasRecyclerBuffer.begin(), hasRecyclerBuffer.end(), [](std::pair<Zstring, bool> item) { return !item.second; })) + { + std::wstring warningMessage = _("Recycle Bin is not available for the following paths! Files will be deleted permanently instead:"); + warningMessage += L"\n"; + + for (auto iter = hasRecyclerBuffer.begin(); iter != hasRecyclerBuffer.end(); ++iter) + if (!iter->second) + warningMessage += L"\n" + utfCvrtTo<std::wstring>(iter->first); + + statusHandler.reportWarning(warningMessage, warningRecyclerMissing); + } + + deleteFromGridAndHDOneSide<LEFT_SIDE>(deleteRecylerLeft, true, statusHandler); + deleteFromGridAndHDOneSide<LEFT_SIDE>(deletePermanentLeft, false, statusHandler); - deleteFromGridAndHDOneSide<RIGHT_SIDE>(deleteRight.begin(), deleteRight.end(), - useRecycleBin, - statusHandler); + deleteFromGridAndHDOneSide<RIGHT_SIDE>(deleteRecylerRight, true, statusHandler); + deleteFromGridAndHDOneSide<RIGHT_SIDE>(deletePermanentRight, false, statusHandler); } |