Improve data path handling
authorManuel Nickschas <sputnick@quassel-irc.org>
Sun, 8 Feb 2015 18:52:55 +0000 (19:52 +0100)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sun, 8 Feb 2015 18:55:54 +0000 (19:55 +0100)
Originally I intended to switch to QStandardPaths for Qt5, however this
would also require migration code. We'll postpone this until we can
get rid of Qt4 and KDE4, so there are less combinations to handle properly.

Instead, this commit improves the handling of data paths in general by
fixing search order, supporting XDG_DATA_HOME, and sanitizing the paths
properly. Also KDE builds now check their own install prefix, in case
Quassel is installed in a non-standard location.

src/common/quassel.cpp
src/qtui/qtuiapplication.cpp

index c9da049..f43423c 100644 (file)
@@ -427,43 +427,50 @@ QStringList Quassel::dataDirPaths()
 
 QStringList Quassel::findDataDirPaths() const
 {
 
 QStringList Quassel::findDataDirPaths() const
 {
-    QStringList dataDirNames = QString(qgetenv("XDG_DATA_DIRS")).split(':', QString::SkipEmptyParts);
+    // We don't use QStandardPaths for now, as we still need to provide fallbacks for Qt4 and
+    // want to stay consistent.
 
 
-    if (!dataDirNames.isEmpty()) {
-        for (int i = 0; i < dataDirNames.count(); i++)
-            dataDirNames[i].append("/apps/quassel/");
-    }
-    else {
-        // Provide a fallback
+    QStringList dataDirNames;
 #ifdef Q_OS_WIN
 #ifdef Q_OS_WIN
-        dataDirNames << qgetenv("APPDATA") + QCoreApplication::organizationDomain() + "/share/apps/quassel/"
-                     << qgetenv("APPDATA") + QCoreApplication::organizationDomain()
-                     << QCoreApplication::applicationDirPath();
-    }
+    dataDirNames << qgetenv("APPDATA") + QCoreApplication::organizationDomain() + "/share/apps/quassel/"
+                 << qgetenv("APPDATA") + QCoreApplication::organizationDomain()
+                 << QCoreApplication::applicationDirPath();
 #elif defined Q_OS_MAC
 #elif defined Q_OS_MAC
-        dataDirNames << QDir::homePath() + "/Library/Application Support/Quassel/"
-                     << QCoreApplication::applicationDirPath();
-    }
+    dataDirNames << QDir::homePath() + "/Library/Application Support/Quassel/"
+                 << QCoreApplication::applicationDirPath();
 #else
 #else
-        dataDirNames.append("/usr/share/apps/quassel/");
-    }
-    // on UNIX, we always check our install prefix
-    QString appDir = QCoreApplication::applicationDirPath();
-    int binpos = appDir.lastIndexOf("/bin");
-    if (binpos >= 0) {
-        appDir.replace(binpos, 4, "/share");
-        appDir.append("/apps/quassel/");
-        if (!dataDirNames.contains(appDir))
-            dataDirNames.append(appDir);
-    }
+    // Linux et al
+
+    // XDG_DATA_HOME is the location for users to override system-installed files, usually in .local/share
+    // This should thus come first.
+    QString xdgDataHome = QFile::decodeName(qgetenv("XDG_DATA_HOME"));
+    if (xdgDataHome.isEmpty())
+        xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
+    dataDirNames << xdgDataHome;
+
+    // Now whatever is configured through XDG_DATA_DIRS
+    QString xdgDataDirs = QFile::decodeName(qgetenv("XDG_DATA_DIRS"));
+    if (xdgDataDirs.isEmpty())
+        dataDirNames << "/usr/local/share" << "/usr/share";
+    else
+        dataDirNames << xdgDataDirs.split(':', QString::SkipEmptyParts);
+
+    // Just in case, also check our install prefix
+    dataDirNames << QCoreApplication::applicationDirPath() + "/../share";
+
+    // Normalize and append our application name
+    for (int i = 0; i < dataDirNames.count(); i++)
+        dataDirNames[i] = QDir::cleanPath(dataDirNames.at(i)) + "/quassel/";
+
 #endif
 
 #endif
 
-    // add resource path and workdir just in case
-    dataDirNames << QCoreApplication::applicationDirPath() + "/data/"
-                 << ":/data/";
+    // Add resource path and workdir just in case.
+    // Workdir should have precedence
+    dataDirNames.prepend(QCoreApplication::applicationDirPath() + "/data/");
+    dataDirNames.append(":/data/");
 
 
-    // append trailing '/' and check for existence
-    QStringList::Iterator iter = dataDirNames.begin();
+    // Append trailing '/' and check for existence
+    auto iter = dataDirNames.begin();
     while (iter != dataDirNames.end()) {
         if (!iter->endsWith(QDir::separator()) && !iter->endsWith('/'))
             iter->append(QDir::separator());
     while (iter != dataDirNames.end()) {
         if (!iter->endsWith(QDir::separator()) && !iter->endsWith('/'))
             iter->append(QDir::separator());
@@ -473,6 +480,8 @@ QStringList Quassel::findDataDirPaths() const
             ++iter;
     }
 
             ++iter;
     }
 
+    dataDirNames.removeDuplicates();
+
     return dataDirNames;
 }
 
     return dataDirNames;
 }
 
index a279442..88b8bdb 100644 (file)
 #include "qtui.h"
 #include "qtuisettings.h"
 
 #include "qtui.h"
 #include "qtuisettings.h"
 
+
 QtUiApplication::QtUiApplication(int &argc, char **argv)
 #ifdef HAVE_KDE4
 QtUiApplication::QtUiApplication(int &argc, char **argv)
 #ifdef HAVE_KDE4
-    : KApplication(),
+    : KApplication(),  // KApplication is deprecated in KF5
 #else
     : QApplication(argc, argv),
 #endif
 #else
     : QApplication(argc, argv),
 #endif
@@ -45,11 +46,34 @@ QtUiApplication::QtUiApplication(int &argc, char **argv)
 #ifdef HAVE_KDE4
     Q_UNUSED(argc); Q_UNUSED(argv);
 
 #ifdef HAVE_KDE4
     Q_UNUSED(argc); Q_UNUSED(argv);
 
-    // We need to setup KDE's data dirs
+    // Setup KDE's data dirs
+    // Because we can't use KDE stuff in (the class) Quassel directly, we need to do this here...
     QStringList dataDirs = KGlobal::dirs()->findDirs("data", "");
     QStringList dataDirs = KGlobal::dirs()->findDirs("data", "");
+
+    // Just in case, also check our install prefix
+    dataDirs << QCoreApplication::applicationDirPath() + "/../share/apps/";
+
+    // Normalize and append our application name
     for (int i = 0; i < dataDirs.count(); i++)
     for (int i = 0; i < dataDirs.count(); i++)
-        dataDirs[i].append("quassel/");
+        dataDirs[i] = QDir::cleanPath(dataDirs.at(i)) + "/quassel/";
+
+    // Add resource path and just in case.
+    // Workdir should have precedence
+    dataDirs.prepend(QCoreApplication::applicationDirPath() + "/data/");
     dataDirs.append(":/data/");
     dataDirs.append(":/data/");
+
+    // Append trailing '/' and check for existence
+    auto iter = dataDirs.begin();
+    while (iter != dataDirs.end()) {
+        if (!iter->endsWith(QDir::separator()) && !iter->endsWith('/'))
+            iter->append(QDir::separator());
+        if (!QFile::exists(*iter))
+            iter = dataDirs.erase(iter);
+        else
+            ++iter;
+    }
+
+    dataDirs.removeDuplicates();
     setDataDirPaths(dataDirs);
 
 #else /* HAVE_KDE4 */
     setDataDirPaths(dataDirs);
 
 #else /* HAVE_KDE4 */