From 888375d294991160ab6ce3e2faa65ffda195c8e5 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Wed, 22 Feb 2017 05:10:24 +0100 Subject: [PATCH] refs #887, directories available via CDirectoryUtils/relative paths * relative cache/settings paths are available relativeFilePath * directories build in CDirectoryUtils * directory comparison in CDirectoryUtils * using CFileUtils to concatenate dir paths * using URL encoded string for normalized directory (as it can be decoded) --- src/blackcore/application.cpp | 13 +-- src/blackmisc/datacache.cpp | 10 +- src/blackmisc/datacache.h | 3 + src/blackmisc/directoryutils.cpp | 154 ++++++++++++++++++++++++++++--- src/blackmisc/directoryutils.h | 52 ++++++++++- src/blackmisc/settingscache.cpp | 10 +- src/blackmisc/settingscache.h | 3 + src/blackmisc/valuecache.cpp | 3 +- 8 files changed, 220 insertions(+), 28 deletions(-) diff --git a/src/blackcore/application.cpp b/src/blackcore/application.cpp index 820d908a5..c801eda8d 100644 --- a/src/blackcore/application.cpp +++ b/src/blackcore/application.cpp @@ -1121,14 +1121,11 @@ namespace BlackCore // No crash handling for unit tests if (isUnitTest()) { return CStatusMessage(this).info("No crash handler for unit tests"); } - static const QString extension = CBuildConfig::isRunningOnWindowsNtPlatform() ? ".exe" : QString(); - static const QString handler = CDirectoryUtils::applicationDirectoryPath() % QLatin1Char('/') % "swift_crashpad_handler" + extension; - static const QString crashpadPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % - QLatin1String("/org.swift-project/") % - CDirectoryUtils::normalizedApplicationDirectory() % - QLatin1String("/crashpad"); - static const QString database = crashpadPath % QLatin1String("/database"); - static const QString metrics = crashpadPath % QLatin1String("/metrics"); + static const QString crashpadHandler(CBuildConfig::isRunningOnWindowsNtPlatform() ? "swift_crashpad_handler.exe" : "swift_crashpad_handler"); + static const QString handler = CFileUtils::appendFilePaths(CDirectoryUtils::applicationDirectoryPath(), crashpadHandler); + static const QString crashpadPath = CDirectoryUtils::getCrashpadDirectory(); + static const QString database = CFileUtils::appendFilePaths(crashpadPath, "/database"); + static const QString metrics = CFileUtils::appendFilePaths(crashpadPath, "/metrics"); if (!QFileInfo::exists(handler)) { diff --git a/src/blackmisc/datacache.cpp b/src/blackmisc/datacache.cpp index 4c97f97bc..9871e34b0 100644 --- a/src/blackmisc/datacache.cpp +++ b/src/blackmisc/datacache.cpp @@ -103,13 +103,13 @@ namespace BlackMisc const QString &CDataCache::persistentStore() { - static const QString dir = getCacheRootDirectory() + "/data/cache/core"; + static const QString dir = CFileUtils::appendFilePaths(getCacheRootDirectory(), relativeFilePath()); return dir; } QString CDataCache::filenameForKey(const QString &key) { - return persistentStore() + "/" + CValueCache::filenameForKey(key); + return CFileUtils::appendFilePaths(persistentStore(), CValueCache::filenameForKey(key)); } QStringList CDataCache::enumerateStore() const @@ -174,6 +174,12 @@ namespace BlackMisc singleShot(0, &m_serializer, [this, key] { m_revision.sessionValue(key); }); } + const QString CDataCache::relativeFilePath() + { + static const QString p("/data/cache/core"); + return p; + } + QString lockFileError(const QLockFile &lock) { switch (lock.error()) diff --git a/src/blackmisc/datacache.h b/src/blackmisc/datacache.h index 21cb165a7..b032df213 100644 --- a/src/blackmisc/datacache.h +++ b/src/blackmisc/datacache.h @@ -285,6 +285,9 @@ namespace BlackMisc //! Method used for implementing session values. void sessionValue(const QString &key); + //! Relative file path in application data directory + static const QString relativeFilePath(); + private: CDataCache(); diff --git a/src/blackmisc/directoryutils.cpp b/src/blackmisc/directoryutils.cpp index 9e0248295..d065aec8c 100644 --- a/src/blackmisc/directoryutils.cpp +++ b/src/blackmisc/directoryutils.cpp @@ -10,9 +10,12 @@ //! \cond PRIVATE #include "blackmisc/directoryutils.h" - +#include "blackmisc/fileutils.h" #include #include +#include +#include +#include #include #include @@ -34,13 +37,11 @@ namespace BlackMisc QString normalizedApplicationDirectoryImpl() { - QString appDir = CDirectoryUtils::applicationDirectoryPath().toLower(); + QString appDir = CDirectoryUtils::applicationDirectoryPath(); Q_ASSERT(appDir.size() > 0); // Remove leading '/' on Unix if (appDir.at(0) == '/') { appDir.remove(0, 1); } - const QRegularExpression re("[:\\\\\\/]"); - appDir = appDir.replace(re, "_"); - return appDir; + return QUrl::toPercentEncoding(appDir); } const QString &CDirectoryUtils::normalizedApplicationDirectory() @@ -49,15 +50,146 @@ namespace BlackMisc return appDir; } - const QString &CDirectoryUtils::getLogDirectory() + const QString &CDirectoryUtils::swiftApplicationDataDirectory() { - static const QString logPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + - "/org.swift-project/" + - CDirectoryUtils::normalizedApplicationDirectory() + - "/logs"; - return logPath; + static const QString p = CFileUtils::appendFilePaths(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation), "/org.swift-project/"); + return p; } + QFileInfoList CDirectoryUtils::swiftApplicationDataDirectories() + { + QDir swiftAppData(CDirectoryUtils::swiftApplicationDataDirectory()); // contains 1..n subdirs + if (!swiftAppData.isReadable()) return QFileInfoList(); + return swiftAppData.entryInfoList({}, QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time); + } + + QStringList CDirectoryUtils::swiftApplicationDataDirectoryList(bool withoutCurrent, bool beautify) + { + QStringList dirs; + const QFileInfoList directories(CDirectoryUtils::swiftApplicationDataDirectories()); + for (const QFileInfo &info : directories) + { + if (withoutCurrent && info.filePath().contains(normalizedApplicationDirectory(), Qt::CaseInsensitive)) continue; + dirs.append(beautify ? + CDirectoryUtils::decodeNormalizedDirectory(info.fileName()) : + info.fileName()); + } + return dirs; + } + + const QString &CDirectoryUtils::swiftNormalizedApplicationDataDirectory() + { + static const QString p = CFileUtils::appendFilePaths(swiftApplicationDataDirectory(), normalizedApplicationDirectory()); + return p; + } + + const QString &CDirectoryUtils::getLogDirectory() + { + static const QString p = CFileUtils::appendFilePaths(swiftNormalizedApplicationDataDirectory(), "/logs"); + return p; + } + + const QString &CDirectoryUtils::getCrashpadDirectory() + { + static const QString p = CFileUtils::appendFilePaths(swiftNormalizedApplicationDataDirectory(), "/crashpad"); + return p; + } + + QString CDirectoryUtils::decodeNormalizedDirectory(const QString &directory) + { + return QUrl::fromPercentEncoding(directory.toUtf8()); + } + + QSet CDirectoryUtils::fileNamesToQSet(const QFileInfoList &fileInfoList) + { + QSet sl; + for (const QFileInfo &info : fileInfoList) + { + sl.insert(info.fileName()); + } + return sl; + } + + QSet CDirectoryUtils::canonicalFileNamesToQSet(const QFileInfoList &fileInfoList) + { + QSet sl; + for (const QFileInfo &info : fileInfoList) + { + sl.insert(info.canonicalFilePath()); + } + return sl; + } + + const QSet CDirectoryUtils::filesToCanonicalNames(const QSet &fileNames, const QSet &canonicalFileNames) + { + QSet found; + if (fileNames.isEmpty()) return found; + for (const QString &canonical : canonicalFileNames) + { + if (canonical.endsWith('/')) continue; + const QString c = canonical.mid(1 + canonical.lastIndexOf('/')); + if (fileNames.contains(c)) + { + found.insert(canonical); + } + } + return found; + } + + CDirectoryUtils::DirComparison CDirectoryUtils::compareTwoDirectories(const QString &dirSource, const QString &dirTarget) + { + DirComparison comp; + const QDir d1(dirSource); + const QDir d2(dirTarget); + + if (!d1.exists() || !d2.exists()) { return comp; } + const QFileInfoList dSourceList = d1.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name); + const QFileInfoList dTargetList = d2.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name); + + // only names + const QSet sSourceFiles = CDirectoryUtils::fileNamesToQSet(dSourceList); + const QSet sTargetFiles = CDirectoryUtils::fileNamesToQSet(dTargetList); + + // full paths + const QSet sSourceCanonicalFiles = CDirectoryUtils::canonicalFileNamesToQSet(dSourceList); + const QSet sTargetCanonicalFiles = CDirectoryUtils::canonicalFileNamesToQSet(dTargetList); + + QSet missingInTarget(sSourceFiles); + QSet missingInSource(sTargetFiles); + QSet sameNames(sSourceFiles); + missingInSource.subtract(sSourceFiles); + missingInTarget.subtract(sTargetFiles); + sameNames.intersect(sTargetFiles); + + comp.source = sSourceCanonicalFiles; + comp.missingInSource = CDirectoryUtils::filesToCanonicalNames(missingInSource, sSourceCanonicalFiles); + comp.missingInTarget = CDirectoryUtils::filesToCanonicalNames(missingInTarget, sSourceCanonicalFiles); + comp.sameNameInSource = CDirectoryUtils::filesToCanonicalNames(sameNames, sSourceCanonicalFiles); + comp.sameNameInTarget = CDirectoryUtils::filesToCanonicalNames(sameNames, sTargetCanonicalFiles); + + Q_ASSERT_X(comp.sameNameInSource.size() == comp.sameNameInTarget.size(), Q_FUNC_INFO, "Same sets require same size"); + QSet::const_iterator targetIt = comp.sameNameInTarget.begin(); + for (const QString &sourceFile : comp.sameNameInSource) + { + const QFileInfo source(sourceFile); + const QFileInfo target(*targetIt++); + if (source.lastModified() == target.lastModified() && source.size() == target.size()) + { + // same + } + else if (source.lastModified() < target.lastModified()) + { + comp.newerInTarget.insert(target.canonicalFilePath()); + } + else + { + // source.lastModified() >= target.lastModified() + comp.newerInSource.insert(source.canonicalFilePath()); + } + } + comp.ok = true; + return comp; + } } // ns //! \endcond diff --git a/src/blackmisc/directoryutils.h b/src/blackmisc/directoryutils.h index 9c017da74..a6d174fbf 100644 --- a/src/blackmisc/directoryutils.h +++ b/src/blackmisc/directoryutils.h @@ -13,7 +13,9 @@ #define BLACKMISC_DIRECTORYUTILS_H #include "blackmisc/blackmiscexport.h" +#include #include +#include namespace BlackMisc { @@ -27,14 +29,58 @@ namespace BlackMisc //! it takes Mac OS X app bundles into account and returns the directory of the bundle. static QString applicationDirectoryPath(); + //! swift application data directory, contains 0..n swift installation directories + static const QString &swiftApplicationDataDirectory(); + + //! swift application data sub directories + static QFileInfoList swiftApplicationDataDirectories(); + + //! swift application data sub directories + static QStringList swiftApplicationDataDirectoryList(bool withoutCurrent = false, bool beautify = false); + + //! swift application data directory for one specific installation (a version) + static const QString &swiftNormalizedApplicationDataDirectory(); + + //! Directory for log files + //! \remark In BlackMisc so it can also be used from BlackMisc classes + static const QString &getLogDirectory(); + + //! Directory for log files + static const QString &getCrashpadDirectory(); + + //! Virtually the inverse operation of CDirectoryUtils::normalizedApplicationDirectory + static QString decodeNormalizedDirectory(const QString &directory); + + //! Result of directory comparison + struct DirComparison + { + bool ok = false; //!< comparison ok + QSet source; //!< all source files + QSet missingInSource; //!< files not in source, but in target + QSet missingInTarget; //!< files not in target, but in source + QSet newerInSource; //!< file exists in target, but source is newer + QSet newerInTarget; //!< file in target is newer + QSet sameNameInSource; //!< file exists in source and target, source name + QSet sameNameInTarget; //!< file exists in source and target, target name + }; + + //! Compare 2 directories (only files, not subdirectories + static DirComparison compareTwoDirectories(const QString &dirSource, const QString &dirTarget); + + private: //! Returns the application directory of the calling executable as normalized string. //! \note There is no trailing '/'. //! \warning The normalization rules are implementation specific and could change over time. static const QString &normalizedApplicationDirectory(); - //! Directory for log files - //! \remark In BlackMisc so it can also be used from BlackMisc classes - static const QString &getLogDirectory(); + //! Convert filenames to set + static QSet fileNamesToQSet(const QFileInfoList &fileInfoList); + + //! Convert canoncial filenames to set + static QSet canonicalFileNamesToQSet(const QFileInfoList &fileInfoList); + + //! File to canonical names + static const QSet filesToCanonicalNames(const QSet &fileNames, const QSet &canonicalFileNames); }; } // ns diff --git a/src/blackmisc/settingscache.cpp b/src/blackmisc/settingscache.cpp index 81366eb9f..41504b86f 100644 --- a/src/blackmisc/settingscache.cpp +++ b/src/blackmisc/settingscache.cpp @@ -26,7 +26,7 @@ namespace BlackMisc const QString &CSettingsCache::persistentStore() { - static const QString dir = getCacheRootDirectory() + "/settings/core"; + static const QString dir = CFileUtils::appendFilePaths(getCacheRootDirectory(), relativeFilePath()); return dir; } @@ -58,7 +58,13 @@ namespace BlackMisc QString CSettingsCache::filenameForKey(const QString &key) { - return persistentStore() + "/" + CValueCache::filenameForKey(key); + return CFileUtils::appendFilePaths(persistentStore(), CValueCache::filenameForKey(key)); + } + + const QString CSettingsCache::relativeFilePath() + { + static const QString p("/settings/core"); + return p; } QStringList CSettingsCache::enumerateStore() const diff --git a/src/blackmisc/settingscache.h b/src/blackmisc/settingscache.h index fdf689fcf..dacc52885 100644 --- a/src/blackmisc/settingscache.h +++ b/src/blackmisc/settingscache.h @@ -53,6 +53,9 @@ namespace BlackMisc //! Return the filename where the value with the given key may be stored. static QString filenameForKey(const QString &key); + //! Relative file path in application data directory + static const QString relativeFilePath(); + //! Return all files where settings may be stored. QStringList enumerateStore() const; diff --git a/src/blackmisc/valuecache.cpp b/src/blackmisc/valuecache.cpp index 5aff549b9..d4e1e3c4e 100644 --- a/src/blackmisc/valuecache.cpp +++ b/src/blackmisc/valuecache.cpp @@ -50,8 +50,7 @@ namespace BlackMisc //! \private std::pair &> getCacheRootDirectoryMutable() { - static QString dir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + - "/org.swift-project/" + CDirectoryUtils::normalizedApplicationDirectory(); + static QString dir = CDirectoryUtils::swiftNormalizedApplicationDataDirectory(); static std::atomic frozen { false }; return { dir, frozen }; }