[KDE/Mac] QSP patch/activator

René J.V. Bertin rjvbertin at gmail.com
Sun Jan 31 15:28:57 UTC 2016


And here's the business part of the latest version of my QSP patch, by popular request (or not) :)

I've followed your (David's) remark that the actual QExtStandardPaths payload could be part of QStandardPaths; QExtStandardPaths is now purely header based - in qstandardpaths.h to be exact.

The idea is now that the user controls QSP at build time. When building with -DQT_USE_EXTSTANDARDPATHS QStandardPaths is replaced with QExtStandardPaths through a #define (except in the QSP implementation files of course). The member functions that take the *non-optional* mode argument are now part of QStandardPaths. I had to move the mode argument around a bit for the locate* versions in order to avoid ambiguity but that's not an issue at this point. 
QExtStandardPaths overloads all relevant QSP functions through its standardLocations(), writableLocation(), locate() and locateAll() methods with a mode argument that has a default value (QSPDEFAULTXDGMODE) -- at least I hope that's indeed what it does.

An additional preprocessor token determines this default behaviour (= when code doesn't use the mode argument explicitly):
- QT_EXTSTANDARDPATHS_XDG_DEFAULT undefined or set to false: the patch has no effect; native QSP locations are returned regardless of the global mode switch (QSPDEFAULTXDGMODE is set to false).
- QT_EXTSTANDARDPATHS_XDG_DEFAULT=runtime : behaviour depends on the global mode switch; #define QSPDEFAULTXDGMODE isXDGLocationsEnabled()
- QT_EXTSTANDARDPATHS_XDG_DEFAULT=true : XDG-compliant locations are always returned, regardless of the global mode switch.

I could imagine replacing the xdg triplet with the alt, for instance, or something that refers to a legacy/traditional Unix approach. Does XDG actually stand for something (I presume the D is for desktop)?

I think it would be preferable to provide built-in values for the additional/alternative locations, but the mechanism would arguably more flexible (and thus have a higher value) if those defaults can be controlled via, for instance, qt.conf .

Cheers,
R.
-------------- next part --------------
diff --git a/qtbase/src/corelib/io/qstandardpaths.cpp b/qtbase/src/corelib/io/qstandardpaths.cpp
index 74252d1..128d10e 100644
--- a/qtbase/src/corelib/io/qstandardpaths.cpp
+++ b/qtbase/src/corelib/io/qstandardpaths.cpp
@@ -31,8 +31,10 @@
 **
 ****************************************************************************/
 
+#undef QT_USE_EXTSTANDARDPATHS
 #include "qstandardpaths.h"
 
+#include <qglobal.h>
 #include <qdir.h>
 #include <qfileinfo.h>
 #include <qhash.h>
@@ -400,6 +402,85 @@ static bool existsAsSpecified(const QString &path, QStandardPaths::LocateOptions
     return QFileInfo(path).isFile();
 }
 
+#if defined(Q_OS_OSX)
+//#include <QDebug>
+/*!
+    This function enables or disables XDG locations on platforms where Qt can be configured
+    to use this kind of locations instead of locations following OS guidelines.
+    Currently this concerns only Mac OS X //and Cygwin?//
+ */
+void QStandardPaths::setXDGLocationsEnabled(bool xdgMode)
+{
+    usingXDGLocations = xdgMode;
+    isSetXDGLocations = true;
+    //qDebug() << Q_FUNC_INFO << "Setting usingXDGLocations=" << xdgMode;
+}
+
+/*!
+    This function returns true when XDG locations are used, unless QT_STANDARDPATHS_CANNOT_SWITCH
+    is set, in which case switching is disabled and the function always returns false.
+ */
+bool QStandardPaths::isXDGLocationsEnabled()
+{
+    if (qEnvironmentVariableIsSet("QT_STANDARDPATHS_CANNOT_SWITCH"))
+        return false;
+    else
+        return usingXDGLocations;
+}
+
+QString QStandardPaths::locate(StandardLocation type, bool xdgMode, const QString &fileName, LocateOptions options)
+{
+    const QStringList &dirs = standardLocations(type, xdgMode);
+    for (QStringList::const_iterator dir = dirs.constBegin(); dir != dirs.constEnd(); ++dir) {
+        const QString path = *dir + QLatin1Char('/') + fileName;
+        if (existsAsSpecified(path, options))
+            return path;
+    }
+    return QString();
+}
+
+QStringList QStandardPaths::locateAll(StandardLocation type, bool xdgMode, const QString &fileName, LocateOptions options)
+{
+    const QStringList &dirs = standardLocations(type, xdgMode);
+    QStringList result;
+    for (QStringList::const_iterator dir = dirs.constBegin(); dir != dirs.constEnd(); ++dir) {
+        const QString path = *dir + QLatin1Char('/') + fileName;
+        if (existsAsSpecified(path, options))
+            result.append(path);
+    }
+    //qDebug() << Q_FUNC_INFO << "search list=" << dirs;
+    return result;
+}
+
+/*!
+   Tries to find a file or directory called \a fileName in the standard locations
+   for \a type.
+
+   The full path to the first file or directory (depending on \a options) found is returned.
+   If no such file or directory can be found, an empty string is returned.
+ */
+QString QStandardPaths::locate(StandardLocation type, const QString &fileName, LocateOptions options)
+{
+    return QStandardPaths::locate(type, isXDGLocationsEnabled(), fileName, options);
+}
+
+/*!
+   Tries to find all files or directories called \a fileName in the standard locations
+   for \a type.
+
+   The \a options flag allows to specify whether to look for files or directories.
+
+   Returns the list of all the files that were found.
+ */
+QStringList QStandardPaths::locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
+{
+    QStringList ret = QStandardPaths::locateAll(type, isXDGLocationsEnabled(), fileName, options);
+    //qDebug() << Q_FUNC_INFO << "type=" << type << "name=" << fileName << "found" << ret;
+    return ret;
+}
+
+#else // !Q_OS_OSX
+
 /*!
    Tries to find a file or directory called \a fileName in the standard locations
    for \a type.
@@ -437,6 +518,7 @@ QStringList QStandardPaths::locateAll(StandardLocation type, const QString &file
     }
     return result;
 }
+#endif // Q_OS_OSX
 
 #ifdef Q_OS_WIN
 static QStringList executableExtensions()
diff --git a/qtbase/src/corelib/io/qstandardpaths.h b/qtbase/src/corelib/io/qstandardpaths.h
index 5c0e08b..921d0c5 100644
--- a/qtbase/src/corelib/io/qstandardpaths.h
+++ b/qtbase/src/corelib/io/qstandardpaths.h
@@ -41,6 +41,8 @@ QT_BEGIN_NAMESPACE
 
 #ifndef QT_NO_STANDARDPATHS
 
+class Q_CORE_EXPORT QStandardPathsConfiguration;
+
 class Q_CORE_EXPORT QStandardPaths
 {
 public:
@@ -68,6 +70,10 @@ public:
         AppLocalDataLocation = DataLocation
     };
 
+#if defined(Q_OS_OSX)
+    static bool isXDGLocationsEnabled();
+#endif // Q_OS_OSX
+
     static QString writableLocation(StandardLocation type);
     static QStringList standardLocations(StandardLocation type);
 
@@ -91,14 +97,93 @@ public:
     static void setTestModeEnabled(bool testMode);
     static bool isTestModeEnabled();
 
+#if defined(Q_OS_OSX)
+protected:
+    static void setXDGLocationsEnabled(bool xdgMode);
+
+    static QString writableLocation(StandardLocation type, bool xdgMode);
+    static QStringList standardLocations(StandardLocation type, bool xdgMode);
+
+    static QString locate(StandardLocation type, bool xdgMode, const QString &fileName, LocateOptions options = LocateFile);
+    static QStringList locateAll(StandardLocation type, bool xdgMode, const QString &fileName, LocateOptions options = LocateFile);
+#endif // Q_OS_OSX
+
 private:
     // prevent construction
     QStandardPaths();
     ~QStandardPaths();
+#if defined(Q_OS_OSX)
+    static bool usingXDGLocations, isSetXDGLocations;
+
+    friend class QStandardPathsConfiguration;
+    friend class QExtStandardPaths;
+#endif // Q_OS_OSX
+};
+
+/* **** Extended QStandardPaths **** */
+
+#ifdef QT_EXTSTANDARDPATHS_XDG_DEFAULT
+#if QT_EXTSTANDARDPATHS_XDG_DEFAULT == runtime
+#define QSPDEFAULTXDGMODE  isXDGLocationsEnabled()
+#else
+#define QSPDEFAULTXDGMODE  true
+#endif // "runtime"
+#else
+#define QSPDEFAULTXDGMODE  false
+#endif
+
+/*!
+    \class QExtStandardPaths
+    \inmodule QtCore
+    \brief The QExtStandardPaths class provides configurable methods for accessing standard paths.
+
+    This class inherits and elaborates on \class QStandardPaths, providing access to the support for
+    native vs. XDG-compliant standard locations that QStandardPaths has on certain platforms (currently
+    only Mac OS X).
+    When the QT_USE_EXTSTANDARDPATHS macro is defined, this class will replace QStandardPaths, and
+    in that case the QT_EXTSTANDARDPATHS_XDG_DEFAULT macro will define the behaviour of code that does
+    not use QExtStandardPaths explicitly itself. When undefined or QT_EXTSTANDARDPATHS_XDG_DEFAULT=false,
+    QExtStandardPaths will use native locations, even if QStandardPaths::isXDGLocationsEnabled() returns true.
+    When QT_EXTSTANDARDPATHS_XDG_DEFAULT=runtime, behaviour is controlled at runtime through
+    QStandardPaths::setXDGLocationsEnabled() and QStandardPaths::isXDGLocationsEnabled().
+    In all other cases QStandardPaths will use XDG-compliant locations.
+*/
+class Q_CORE_EXPORT QExtStandardPaths : public QStandardPaths
+{
+public:
+#if defined(Q_OS_OSX)
+    static QString writableLocation(StandardLocation type, bool xdgMode=QSPDEFAULTXDGMODE)
+    {
+        return QStandardPaths::writableLocation(type, xdgMode);
+    }
+    static QStringList standardLocations(StandardLocation type, bool xdgMode=QSPDEFAULTXDGMODE)
+    {
+        return QStandardPaths::standardLocations(type, xdgMode);
+    }
+
+    static QString locate(StandardLocation type, const QString &fileName, LocateOptions options = LocateFile, bool xdgMode=QSPDEFAULTXDGMODE)
+    {
+        return QStandardPaths::locate(type, xdgMode, fileName, options);
+    }
+    static QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options = LocateFile, bool xdgMode=QSPDEFAULTXDGMODE)
+    {
+        return QStandardPaths::locateAll(type, xdgMode, fileName, options);
+    }
+#endif
+
+private:
+    // prevent construction
+    QExtStandardPaths();
+    ~QExtStandardPaths();
 };
 
 #endif // QT_NO_STANDARDPATHS
 
 QT_END_NAMESPACE
 
+#ifdef QT_USE_EXTSTANDARDPATHS
+#define QStandardPaths  QExtStandardPaths
+#endif // QT_USE_EXTSTANDARDPATHS
+
+
 #endif // QSTANDARDPATHS_H
diff --git a/qtbase/src/corelib/io/qstandardpaths_mac.mm b/qtbase/src/corelib/io/qstandardpaths_mac.mm
index d6126ce..887c964 100644
--- a/qtbase/src/corelib/io/qstandardpaths_mac.mm
+++ b/qtbase/src/corelib/io/qstandardpaths_mac.mm
@@ -31,13 +31,16 @@
 **
 ****************************************************************************/
 
+#undef QT_USE_EXTSTANDARDPATHS
 #include "qstandardpaths.h"
 #include <qdir.h>
 #include <qurl.h>
 #include <private/qcore_mac_p.h>
+#include <private/qfilesystemengine_p.h>
 
 #ifndef QT_BOOTSTRAPPED
 #include <qcoreapplication.h>
+#include <qlibraryinfo.h>
 #endif
 
 #include <CoreFoundation/CoreFoundation.h>
@@ -136,8 +139,155 @@ static QString macLocation(QStandardPaths::StandardLocation type, short domain)
     return path;
 }
 
-QString QStandardPaths::writableLocation(StandardLocation type)
+bool QStandardPaths::usingXDGLocations = false;
+bool QStandardPaths::isSetXDGLocations = false;
+
+static void normaliseDirs(QStringList &dirs)
+{
+    // Normalise paths, skip relative paths
+    QMutableListIterator<QString> it(dirs);
+    while (it.hasNext()) {
+        const QString dir = it.next();
+        if (!dir.startsWith(QLatin1Char('/')))
+            it.remove();
+        else
+            it.setValue(QDir::cleanPath(dir));
+    }
+
+    // Remove duplicates from the list, there's no use for duplicated
+    // paths in XDG_CONFIG_DIRS - if it's not found in the given
+    // directory the first time, it won't be there the second time.
+    // Plus duplicate paths causes problems for example for mimetypes,
+    // where duplicate paths here lead to duplicated mime types returned
+    // for a file, eg "text/plain,text/plain" instead of "text/plain"
+    dirs.removeDuplicates();
+}
+
+static QStringList xdgCacheDirs()
 {
+    QStringList dirs;
+    // http://standards.freedesktop.org/basedir-spec/latest/
+    QString xdgConfigDirsEnv = QFile::decodeName(qgetenv("XDG_CACHE_HOME"));
+    if (xdgConfigDirsEnv.isEmpty()) {
+#ifndef QT_BOOTSTRAPPED
+        dirs.append(QDir::homePath() + QString::fromLatin1("/.cache"));
+#endif
+    } else {
+        dirs = xdgConfigDirsEnv.split(QLatin1Char(':'), QString::SkipEmptyParts);
+
+        normaliseDirs(dirs);
+    }
+    return dirs;
+}
+
+static QStringList xdgConfigDirs()
+{
+    QStringList dirs;
+    // http://standards.freedesktop.org/basedir-spec/latest/
+    QString xdgConfigDirsEnv = QFile::decodeName(qgetenv("XDG_CONFIG_DIRS"));
+    if (xdgConfigDirsEnv.isEmpty()) {
+#ifndef QT_BOOTSTRAPPED
+        dirs.append(QLibraryInfo::location(QLibraryInfo::PrefixPath) + QString::fromLatin1("/etc/xdg"));
+#endif
+    } else {
+        dirs = xdgConfigDirsEnv.split(QLatin1Char(':'), QString::SkipEmptyParts);
+
+        normaliseDirs(dirs);
+    }
+    return dirs;
+}
+
+static QStringList xdgDataDirs()
+{
+    QStringList dirs;
+    // http://standards.freedesktop.org/basedir-spec/latest/
+    QString xdgDataDirsEnv = QFile::decodeName(qgetenv("XDG_DATA_DIRS"));
+    if (xdgDataDirsEnv.isEmpty()) {
+#ifndef QT_BOOTSTRAPPED
+        dirs.append(QLibraryInfo::location(QLibraryInfo::PrefixPath) + QString::fromLatin1("/share"));
+#endif
+    } else {
+        dirs = xdgDataDirsEnv.split(QLatin1Char(':'), QString::SkipEmptyParts);
+
+        normaliseDirs(dirs);
+    }
+    return dirs;
+}
+
+static QString xdgRuntimeDir()
+{
+    const uid_t myUid = geteuid();
+    // http://standards.freedesktop.org/basedir-spec/latest/
+    QString xdgRTDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR"));
+    if (xdgRTDir.isEmpty()) {
+        const QString userName = QFileSystemEngine::resolveUserName(myUid);
+//         xdgRTDir = QDir::tempPath() + QLatin1String("/runtime-") + userName;
+        // NSTemporaryDirectory() returns the default $TMPDIR value, regardless of its current setting,
+        // which is more in line with XDG_RUNTIME_DIR requirements.
+        xdgRTDir = QString::fromNSString(NSTemporaryDirectory()) + QLatin1String("runtime-") + userName;
+        QDir dir(xdgRTDir);
+        if (!dir.exists()) {
+            if (!QDir().mkdir(xdgRTDir)) {
+                qWarning("QStandardPaths: error creating runtime directory %s: %s",
+                         qPrintable(xdgRTDir), qPrintable(qt_error_string(errno)));
+                return QString();
+            }
+        }
+    } else {
+        qWarning("QStandardPaths: XDG_RUNTIME_DIR is set, using '%s'", qPrintable(xdgRTDir));
+    }
+    // "The directory MUST be owned by the user"
+    QFileInfo fileInfo(xdgRTDir);
+    if (fileInfo.ownerId() != myUid) {
+        qWarning("QStandardPaths: wrong ownership on runtime directory %s, %d instead of %d", qPrintable(xdgRTDir),
+                 fileInfo.ownerId(), myUid);
+        return QString();
+    }
+    // "and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700."
+    // since the current user is the owner, set both xxxUser and xxxOwner
+    QFile file(xdgRTDir);
+    const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
+                                           | QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
+    if (file.permissions() != wantedPerms && !file.setPermissions(wantedPerms)) {
+        qWarning("QStandardPaths: could not set correct permissions on runtime directory %s: %s",
+                 qPrintable(xdgRTDir), qPrintable(file.errorString()));
+        return QString();
+    }
+    return xdgRTDir;
+}
+
+QString QStandardPaths::writableLocation(StandardLocation type, bool xdgMode)
+{
+    if (xdgMode) {
+        const QString prefix = (isTestModeEnabled())? QDir::homePath() + QLatin1String("/.qttest") : QDir::homePath();
+        QString path;
+        switch (type) {
+        case GenericDataLocation:
+        case AppDataLocation:
+        case AppLocalDataLocation:
+            path = prefix + QLatin1String("/.local/share");
+            if (type != GenericDataLocation)
+                appendOrganizationAndApp(path);
+            return path;
+        case GenericCacheLocation:
+        case CacheLocation:
+            path = prefix + QLatin1String("/.cache");
+            if (type == CacheLocation)
+                appendOrganizationAndApp(path);
+            return path;
+        case GenericConfigLocation:
+        case ConfigLocation:
+            return prefix + QLatin1String("/.config");
+        case ApplicationsLocation:
+            path = writableLocation(GenericDataLocation, xdgMode) + QLatin1String("/applications");
+            return path;
+        case RuntimeLocation:
+            return xdgRuntimeDir();
+        default:
+            break;
+        }
+    }
+
     if (isTestModeEnabled()) {
         const QString qttestDir = QDir::homePath() + QLatin1String("/.qttest");
         QString path;
@@ -162,6 +312,9 @@ QString QStandardPaths::writableLocation(StandardLocation type)
             if (type == AppConfigLocation)
                 appendOrganizationAndApp(path);
             return path;
+        case ApplicationsLocation:
+            path = qttestDir + QLatin1String("/Applications");
+            return path;
         default:
             break;
         }
@@ -178,16 +331,45 @@ QString QStandardPaths::writableLocation(StandardLocation type)
     case GenericCacheLocation:
     case CacheLocation:
     case RuntimeLocation:
+    case FontsLocation:
+        // the font location that is writable for all users is ~/Library/Fonts
         return macLocation(type, kUserDomain);
     default:
         return macLocation(type, kOnAppropriateDisk);
     }
 }
 
-QStringList QStandardPaths::standardLocations(StandardLocation type)
+QString QStandardPaths::writableLocation(StandardLocation type)
+{
+    return QStandardPaths::writableLocation(type, isXDGLocationsEnabled());
+}
+
+QStringList QStandardPaths::standardLocations(StandardLocation type, bool xdgMode)
 {
     QStringList dirs;
 
+    if (xdgMode) {
+        switch (type) {
+        case GenericDataLocation:
+            dirs.append(xdgDataDirs());
+            break;
+        case GenericConfigLocation:
+        case ConfigLocation:
+            dirs.append(xdgConfigDirs());
+            break;
+        case GenericCacheLocation:
+        case CacheLocation:
+            dirs.append(xdgCacheDirs());
+            break;
+        case ApplicationsLocation:
+            QStringList xdgDirs = xdgDataDirs();
+            for (int i = 0; i < xdgDirs.count(); ++i)
+                xdgDirs[i].append(QLatin1String("/applications"));
+            dirs.append(xdgDirs);
+            break;
+        }
+    }
+
     if (type == GenericDataLocation || type == AppDataLocation || type == AppLocalDataLocation || type == GenericCacheLocation || type == CacheLocation) {
         const QString path = macLocation(type, kOnAppropriateDisk);
         if (!path.isEmpty())
@@ -195,6 +377,14 @@ QStringList QStandardPaths::standardLocations(StandardLocation type)
     }
 
     if (type == AppDataLocation || type == AppLocalDataLocation) {
+        if (xdgMode) {
+            QStringList xdgDirs = xdgDataDirs();
+            for (int i = 0; i < xdgDirs.count(); ++i) {
+                appendOrganizationAndApp(xdgDirs[i]);
+            }
+            dirs.append(xdgDirs);
+        }
+
         CFBundleRef mainBundle = CFBundleGetMainBundle();
         if (mainBundle) {
             CFURLRef bundleUrl = CFBundleCopyBundleURL(mainBundle);
@@ -218,11 +408,22 @@ QStringList QStandardPaths::standardLocations(StandardLocation type)
                 dirs.append(bundlePath + resourcesPath);
         }
     }
-    const QString localDir = writableLocation(type);
+    if (type == FontsLocation) {
+        // /Library/Fonts
+        dirs.append(macLocation(type,kLocalDomain));
+        // /System/Library/Fonts
+        dirs.append(macLocation(type,kSystemDomain));
+    }
+    const QString localDir = writableLocation(type, xdgMode);
     dirs.prepend(localDir);
     return dirs;
 }
 
+QStringList QStandardPaths::standardLocations(StandardLocation type)
+{
+    return QStandardPaths::standardLocations(type, isXDGLocationsEnabled());
+}
+
 #ifndef QT_BOOTSTRAPPED
 QString QStandardPaths::displayName(StandardLocation type)
 {


More information about the kde-mac mailing list