From 44db72ead568fe7ce22e1999aac02fd9aac9beea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 10 Oct 2015 05:55:55 +0200 Subject: [PATCH] GH-93 add an option to not copy saves on instance copy --- application/MainWindow.cpp | 2 +- logic/FileSystem.cpp | 116 ++++++++++++++++++------------ logic/FileSystem.h | 39 ++++++++-- logic/InstanceList.cpp | 12 +++- logic/minecraft/Mod.cpp | 2 +- logic/minecraft/ModList.cpp | 2 +- logic/minecraft/World.cpp | 4 +- logic/pathmatcher/IPathMatcher.h | 2 +- logic/pathmatcher/MultiMatcher.h | 2 +- logic/pathmatcher/RegexpMatcher.h | 17 ++++- 10 files changed, 138 insertions(+), 60 deletions(-) diff --git a/application/MainWindow.cpp b/application/MainWindow.cpp index 718888706..2469f9836 100644 --- a/application/MainWindow.cpp +++ b/application/MainWindow.cpp @@ -1090,7 +1090,7 @@ InstancePtr MainWindow::instanceFromZipPack(QString instName, QString instGroup, CustomMessageBox::selectable(this, tr("Error"), tr("Archive does not contain instance.cfg"))->show(); return nullptr; } - if (!FS::copyPath(instanceCfgFile.absoluteDir().absolutePath(), instDir)) + if (!FS::copy(instanceCfgFile.absoluteDir().absolutePath(), instDir)()) { CustomMessageBox::selectable(this, tr("Error"), tr("Unable to copy instance"))->show(); return nullptr; diff --git a/logic/FileSystem.cpp b/logic/FileSystem.cpp index ca4a3ba0a..006356b39 100644 --- a/logic/FileSystem.cpp +++ b/logic/FileSystem.cpp @@ -9,16 +9,18 @@ #include #include +namespace FS { + void ensureExists(const QDir &dir) { if (!QDir().mkpath(dir.absolutePath())) { - throw FS::FileSystemException("Unable to create directory " + dir.dirName() + " (" + + throw FileSystemException("Unable to create directory " + dir.dirName() + " (" + dir.absolutePath() + ")"); } } -void FS::write(const QString &filename, const QByteArray &data) +void write(const QString &filename, const QByteArray &data) { ensureExists(QFileInfo(filename).dir()); QSaveFile file(filename); @@ -39,7 +41,7 @@ void FS::write(const QString &filename, const QByteArray &data) } } -QByteArray FS::read(const QString &filename) +QByteArray read(const QString &filename) { QFile file(filename); if (!file.open(QFile::ReadOnly)) @@ -58,7 +60,7 @@ QByteArray FS::read(const QString &filename) return data; } -bool FS::ensureFilePathExists(QString filenamepath) +bool ensureFilePathExists(QString filenamepath) { QFileInfo a(filenamepath); QDir dir; @@ -67,7 +69,7 @@ bool FS::ensureFilePathExists(QString filenamepath) return success; } -bool FS::ensureFolderPathExists(QString foldernamepath) +bool ensureFolderPathExists(QString foldernamepath) { QFileInfo a(foldernamepath); QDir dir; @@ -76,56 +78,77 @@ bool FS::ensureFolderPathExists(QString foldernamepath) return success; } -bool FS::copyPath(const QString &src, const QString &dst, bool follow_symlinks) +bool copy::operator()(const QString &offset) { //NOTE always deep copy on windows. the alternatives are too messy. #if defined Q_OS_WIN32 - follow_symlinks = true; + m_followSymlinks = true; #endif - QDir dir(src); - if (!dir.exists()) - return false; - if (!ensureFolderPathExists(dst)) + auto src = PathCombine(m_src.absolutePath(), offset); + auto dst = PathCombine(m_dst.absolutePath(), offset); + + QFileInfo currentSrc(src); + if (!currentSrc.exists()) return false; - bool OK = true; - - qDebug() << "Looking at " << dir.absolutePath(); - foreach(QString f, dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) + if(!m_followSymlinks && currentSrc.isSymLink()) { - QString inner_src = src + QDir::separator() + f; - QString inner_dst = dst + QDir::separator() + f; - qDebug() << f << "translates to"<< inner_src << "to" << inner_dst; - QFileInfo fileInfo(inner_src); - if(!follow_symlinks && fileInfo.isSymLink()) + qDebug() << "creating symlink" << src << " - " << dst; + if (!ensureFilePathExists(dst)) { - qDebug() << "creating symlink" << inner_src << " - " << inner_dst; - OK &= QFile::link(fileInfo.symLinkTarget(),inner_dst); + qWarning() << "Cannot create path!"; + return false; } - else if (fileInfo.isDir()) + return QFile::link(currentSrc.symLinkTarget(), dst); + } + else if(currentSrc.isFile()) + { + qDebug() << "copying file" << src << " - " << dst; + if (!ensureFilePathExists(dst)) { - qDebug() << "recursing" << inner_src << " - " << inner_dst; - OK &= copyPath(inner_src, inner_dst, follow_symlinks); + qWarning() << "Cannot create path!"; + return false; } - else if (fileInfo.isFile()) + return QFile::copy(src, dst); + } + else if(currentSrc.isDir()) + { + qDebug() << "recursing" << offset; + if (!ensureFolderPathExists(dst)) { - qDebug() << "copying file" << inner_src << " - " << inner_dst; - OK &= QFile::copy(inner_src, inner_dst); + qWarning() << "Cannot create path!"; + return false; } - else + QDir currentDir(src); + for(auto & f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) { - OK = false; - qCritical() << "Copy ERROR: Unknown filesystem object:" << inner_src; + auto inner_offset = PathCombine(offset, f); + // ignore and skip stuff that matches the blacklist. + if(m_blacklist && m_blacklist->matches(inner_offset)) + { + continue; + } + if(!operator()(inner_offset)) + { + return false; + } } } - return OK; + else + { + qCritical() << "Copy ERROR: Unknown filesystem object:" << src; + return false; + } + return true; } + + #if defined Q_OS_WIN32 #include #include #endif -bool FS::deletePath(QString path) +bool deletePath(QString path) { bool OK = true; QDir dir(path); @@ -138,7 +161,7 @@ bool FS::deletePath(QString path) QDir::AllDirs | QDir::Files, QDir::DirsFirst); - for(QFileInfo info: allEntries) + for(auto & info: allEntries) { #if defined Q_OS_WIN32 QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath()); @@ -182,7 +205,7 @@ bool FS::deletePath(QString path) } -QString FS::PathCombine(QString path1, QString path2) +QString PathCombine(QString path1, QString path2) { if(!path1.size()) return path2; @@ -191,17 +214,17 @@ QString FS::PathCombine(QString path1, QString path2) return QDir::cleanPath(path1 + QDir::separator() + path2); } -QString FS::PathCombine(QString path1, QString path2, QString path3) +QString PathCombine(QString path1, QString path2, QString path3) { return PathCombine(PathCombine(path1, path2), path3); } -QString FS::AbsolutePath(QString path) +QString AbsolutePath(QString path) { return QFileInfo(path).absolutePath(); } -QString FS::ResolveExecutable(QString path) +QString ResolveExecutable(QString path) { if (path.isEmpty()) { @@ -225,7 +248,7 @@ QString FS::ResolveExecutable(QString path) * Any paths inside the current directory will be normalized to relative paths (to current) * Other paths will be made absolute */ -QString FS::NormalizePath(QString path) +QString NormalizePath(QString path) { QDir a = QDir::currentPath(); QString currentAbsolute = a.absolutePath(); @@ -245,7 +268,7 @@ QString FS::NormalizePath(QString path) QString badFilenameChars = "\"\\/?<>:*|!"; -QString FS::RemoveInvalidFilenameChars(QString string, QChar replaceWith) +QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) { for (int i = 0; i < string.length(); i++) { @@ -257,7 +280,7 @@ QString FS::RemoveInvalidFilenameChars(QString string, QChar replaceWith) return string; } -QString FS::DirNameFromString(QString string, QString inDir) +QString DirNameFromString(QString string, QString inDir) { int num = 0; QString baseName = RemoveInvalidFilenameChars(string, '-'); @@ -281,7 +304,7 @@ QString FS::DirNameFromString(QString string, QString inDir) return dirName; } -void FS::openDirInDefaultProgram(QString path, bool ensureExists) +void openDirInDefaultProgram(QString path, bool ensureExists) { QDir parentPath; QDir dir(path); @@ -292,14 +315,14 @@ void FS::openDirInDefaultProgram(QString path, bool ensureExists) QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); } -void FS::openFileInDefaultProgram(QString filename) +void openFileInDefaultProgram(QString filename) { QDesktopServices::openUrl(QUrl::fromLocalFile(filename)); } // Does the directory path contain any '!'? If yes, return true, otherwise false. // (This is a problem for Java) -bool FS::checkProblemticPathJava(QDir folder) +bool checkProblemticPathJava(QDir folder) { QString pathfoldername = folder.absolutePath(); return pathfoldername.contains("!", Qt::CaseInsensitive); @@ -366,13 +389,13 @@ HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args) #endif -QString FS::getDesktopDir() +QString getDesktopDir() { return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); } // Cross-platform Shortcut creation -bool FS::createShortCut(QString location, QString dest, QStringList args, QString name, +bool createShortCut(QString location, QString dest, QStringList args, QString name, QString icon) { #if defined Q_OS_LINUX @@ -426,3 +449,4 @@ bool FS::createShortCut(QString location, QString dest, QStringList args, QStrin return false; #endif } +} \ No newline at end of file diff --git a/logic/FileSystem.h b/logic/FileSystem.h index 5055eb6cf..5e21375b7 100644 --- a/logic/FileSystem.h +++ b/logic/FileSystem.h @@ -3,9 +3,11 @@ #pragma once #include "Exception.h" +#include "pathmatcher/IPathMatcher.h" #include "multimc_logic_export.h" #include +#include namespace FS { @@ -38,10 +40,39 @@ MULTIMC_LOGIC_EXPORT bool ensureFilePathExists(QString filenamepath); */ MULTIMC_LOGIC_EXPORT bool ensureFolderPathExists(QString filenamepath); -/** - * Copy a folder recursively - */ -MULTIMC_LOGIC_EXPORT bool copyPath(const QString &src, const QString &dst, bool follow_symlinks = true); +class MULTIMC_LOGIC_EXPORT copy +{ +public: + copy(const copy&) = delete; + copy(const QString & src, const QString & dst) + { + m_src = src; + m_dst = dst; + } + copy & followSymlinks(const bool follow) + { + m_followSymlinks = follow; + return *this; + } + copy & blacklist(const IPathMatcher * filter) + { + m_blacklist = filter; + return *this; + } + bool operator()() + { + return operator()(QString()); + } + +private: + bool operator()(const QString &offset); + +private: + bool m_followSymlinks = true; + const IPathMatcher * m_blacklist = nullptr; + QDir m_src; + QDir m_dst; +}; /** * Delete a folder recursively diff --git a/logic/InstanceList.cpp b/logic/InstanceList.cpp index b791cc322..a8acdf055 100644 --- a/logic/InstanceList.cpp +++ b/logic/InstanceList.cpp @@ -38,6 +38,7 @@ #include "ftb/FTBPlugin.h" #include "NullInstance.h" #include "FileSystem.h" +#include "pathmatcher/RegexpMatcher.h" const static int GROUP_FILE_FORMAT_VERSION = 1; @@ -486,9 +487,18 @@ InstanceList::InstCreateError InstanceList::copyInstance(InstancePtr &newInstance, InstancePtr &oldInstance, const QString &instDir, bool copySaves) { QDir rootDir(instDir); + std::unique_ptr matcher; + if(!copySaves) + { + auto matcherReal = new RegexpMatcher("[.]?minecraft/saves"); + matcherReal->caseSensitive(false); + matcher.reset(matcherReal); + } qDebug() << instDir.toUtf8(); - if (!FS::copyPath(oldInstance->instanceRoot(), instDir, false)) + FS::copy folderCopy(oldInstance->instanceRoot(), instDir); + folderCopy.followSymlinks(false).blacklist(matcher.get()); + if (!folderCopy()) { FS::deletePath(instDir); return InstanceList::CantCreateDir; diff --git a/logic/minecraft/Mod.cpp b/logic/minecraft/Mod.cpp index 879ca1956..9b9f76f9b 100644 --- a/logic/minecraft/Mod.cpp +++ b/logic/minecraft/Mod.cpp @@ -278,7 +278,7 @@ bool Mod::replace(Mod &with) } if (t == MOD_FOLDER) { - success = FS::copyPath(with.m_file.filePath(), m_file.path()); + success = FS::copy(with.m_file.filePath(), m_file.path())(); } if (success) { diff --git a/logic/minecraft/ModList.cpp b/logic/minecraft/ModList.cpp index 60f8b0126..07affb720 100644 --- a/logic/minecraft/ModList.cpp +++ b/logic/minecraft/ModList.cpp @@ -286,7 +286,7 @@ bool ModList::installMod(const QFileInfo &filename, int index) QString from = filename.filePath(); QString to = FS::PathCombine(m_dir.path(), filename.fileName()); - if (!FS::copyPath(from, to)) + if (!FS::copy(from, to)()) return false; m.repath(to); beginInsertRows(QModelIndex(), index, index); diff --git a/logic/minecraft/World.cpp b/logic/minecraft/World.cpp index e47345566..6081a8ecd 100644 --- a/logic/minecraft/World.cpp +++ b/logic/minecraft/World.cpp @@ -197,7 +197,7 @@ bool World::install(const QString &to, const QString &name) else if(m_containerFile.isDir()) { QString from = m_containerFile.filePath(); - ok = FS::copyPath(from, finalPath); + ok = FS::copy(from, finalPath)(); } if(ok && !name.isEmpty() && m_actualName != name) @@ -350,7 +350,7 @@ bool World::replace(World &with) { if (!destroy()) return false; - bool success = FS::copyPath(with.m_containerFile.filePath(), m_containerFile.path()); + bool success = FS::copy(with.m_containerFile.filePath(), m_containerFile.path())(); if (success) { m_folderName = with.m_folderName; diff --git a/logic/pathmatcher/IPathMatcher.h b/logic/pathmatcher/IPathMatcher.h index 806a750af..1d4109479 100644 --- a/logic/pathmatcher/IPathMatcher.h +++ b/logic/pathmatcher/IPathMatcher.h @@ -8,5 +8,5 @@ public: public: virtual ~IPathMatcher(){}; - virtual bool matches(const QString &string) = 0; + virtual bool matches(const QString &string) const = 0; }; diff --git a/logic/pathmatcher/MultiMatcher.h b/logic/pathmatcher/MultiMatcher.h index e018967cd..91f70aa43 100644 --- a/logic/pathmatcher/MultiMatcher.h +++ b/logic/pathmatcher/MultiMatcher.h @@ -15,7 +15,7 @@ public: return *this; } - virtual bool matches(const QString &string) override + virtual bool matches(const QString &string) const override { for(auto iter: m_matchers) { diff --git a/logic/pathmatcher/RegexpMatcher.h b/logic/pathmatcher/RegexpMatcher.h index f3cf90b15..da5521238 100644 --- a/logic/pathmatcher/RegexpMatcher.h +++ b/logic/pathmatcher/RegexpMatcher.h @@ -5,13 +5,26 @@ class RegexpMatcher : public IPathMatcher { public: virtual ~RegexpMatcher() {}; - RegexpMatcher(QString regexp) + RegexpMatcher(const QString ®exp) { m_regexp.setPattern(regexp); m_onlyFilenamePart = !regexp.contains('/'); } - virtual bool matches(const QString &string) override + RegexpMatcher &caseSensitive(bool cs = true) + { + if(cs) + { + m_regexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption); + } + else + { + m_regexp.setPatternOptions(QRegularExpression::NoPatternOption); + } + return *this; + } + + virtual bool matches(const QString &string) const override { if(m_onlyFilenamePart) {