qt6-bb10/src/corelib/io/qstandardpaths_unix.cpp

324 lines
12 KiB
C++

/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qstandardpaths.h"
#include <qdir.h>
#include <qfile.h>
#include <qhash.h>
#include <qtextstream.h>
#include <private/qfilesystemengine_p.h>
#include <errno.h>
#include <stdlib.h>
#ifndef QT_BOOTSTRAPPED
#include <qcoreapplication.h>
#endif
#ifndef QT_NO_STANDARDPATHS
QT_BEGIN_NAMESPACE
static void appendOrganizationAndApp(QString &path)
{
#ifndef QT_BOOTSTRAPPED
const QString org = QCoreApplication::organizationName();
if (!org.isEmpty())
path += QLatin1Char('/') + org;
const QString appName = QCoreApplication::applicationName();
if (!appName.isEmpty())
path += QLatin1Char('/') + appName;
#else
Q_UNUSED(path);
#endif
}
QString QStandardPaths::writableLocation(StandardLocation type)
{
switch (type) {
case HomeLocation:
return QDir::homePath();
case TempLocation:
return QDir::tempPath();
case CacheLocation:
case GenericCacheLocation:
{
// http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
QString xdgCacheHome = QFile::decodeName(qgetenv("XDG_CACHE_HOME"));
if (isTestModeEnabled())
xdgCacheHome = QDir::homePath() + QLatin1String("/.qttest/cache");
if (xdgCacheHome.isEmpty())
xdgCacheHome = QDir::homePath() + QLatin1String("/.cache");
if (type == QStandardPaths::CacheLocation)
appendOrganizationAndApp(xdgCacheHome);
return xdgCacheHome;
}
case DataLocation:
case GenericDataLocation:
{
QString xdgDataHome = QFile::decodeName(qgetenv("XDG_DATA_HOME"));
if (isTestModeEnabled())
xdgDataHome = QDir::homePath() + QLatin1String("/.qttest/share");
if (xdgDataHome.isEmpty())
xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
if (type == QStandardPaths::DataLocation)
appendOrganizationAndApp(xdgDataHome);
return xdgDataHome;
}
case ConfigLocation:
case GenericConfigLocation:
{
// http://standards.freedesktop.org/basedir-spec/latest/
QString xdgConfigHome = QFile::decodeName(qgetenv("XDG_CONFIG_HOME"));
if (isTestModeEnabled())
xdgConfigHome = QDir::homePath() + QLatin1String("/.qttest/config");
if (xdgConfigHome.isEmpty())
xdgConfigHome = QDir::homePath() + QLatin1String("/.config");
return xdgConfigHome;
}
case RuntimeLocation:
{
const uid_t myUid = geteuid();
// http://standards.freedesktop.org/basedir-spec/latest/
QString xdgRuntimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR"));
if (xdgRuntimeDir.isEmpty()) {
const QString userName = QFileSystemEngine::resolveUserName(myUid);
xdgRuntimeDir = QDir::tempPath() + QLatin1String("/runtime-") + userName;
QDir dir(xdgRuntimeDir);
if (!dir.exists()) {
if (!QDir().mkdir(xdgRuntimeDir)) {
qWarning("QStandardPaths: error creating runtime directory %s: %s", qPrintable(xdgRuntimeDir), qPrintable(qt_error_string(errno)));
return QString();
}
}
qWarning("QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '%s'", qPrintable(xdgRuntimeDir));
}
// "The directory MUST be owned by the user"
QFileInfo fileInfo(xdgRuntimeDir);
if (fileInfo.ownerId() != myUid) {
qWarning("QStandardPaths: wrong ownership on runtime directory %s, %d instead of %d", qPrintable(xdgRuntimeDir),
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."
QFile file(xdgRuntimeDir);
const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser;
if (file.permissions() != wantedPerms && !file.setPermissions(wantedPerms)) {
qWarning("QStandardPaths: wrong permissions on runtime directory %s", qPrintable(xdgRuntimeDir));
return QString();
}
return xdgRuntimeDir;
}
default:
break;
}
#ifndef QT_BOOTSTRAPPED
// http://www.freedesktop.org/wiki/Software/xdg-user-dirs
QString xdgConfigHome = QFile::decodeName(qgetenv("XDG_CONFIG_HOME"));
if (xdgConfigHome.isEmpty())
xdgConfigHome = QDir::homePath() + QLatin1String("/.config");
QFile file(xdgConfigHome + QLatin1String("/user-dirs.dirs"));
if (!isTestModeEnabled() && file.open(QIODevice::ReadOnly)) {
QHash<QString, QString> lines;
QTextStream stream(&file);
// Only look for lines like: XDG_DESKTOP_DIR="$HOME/Desktop"
QRegExp exp(QLatin1String("^XDG_(.*)_DIR=(.*)$"));
while (!stream.atEnd()) {
const QString &line = stream.readLine();
if (exp.indexIn(line) != -1) {
const QStringList lst = exp.capturedTexts();
const QString key = lst.at(1);
QString value = lst.at(2);
if (value.length() > 2
&& value.startsWith(QLatin1Char('\"'))
&& value.endsWith(QLatin1Char('\"')))
value = value.mid(1, value.length() - 2);
// Store the key and value: "DESKTOP", "$HOME/Desktop"
lines[key] = value;
}
}
QString key;
switch (type) {
case DesktopLocation:
key = QLatin1String("DESKTOP");
break;
case DocumentsLocation:
key = QLatin1String("DOCUMENTS");
break;
case PicturesLocation:
key = QLatin1String("PICTURES");
break;
case MusicLocation:
key = QLatin1String("MUSIC");
break;
case MoviesLocation:
key = QLatin1String("VIDEOS");
break;
case DownloadLocation:
key = QLatin1String("DOWNLOAD");
break;
default:
break;
}
if (!key.isEmpty()) {
QString value = lines.value(key);
if (!value.isEmpty()) {
// value can start with $HOME
if (value.startsWith(QLatin1String("$HOME")))
value = QDir::homePath() + value.mid(5);
if (value.length() > 1 && value.endsWith(QLatin1Char('/')))
value.chop(1);
return value;
}
}
}
#endif
QString path;
switch (type) {
case DesktopLocation:
path = QDir::homePath() + QLatin1String("/Desktop");
break;
case DocumentsLocation:
path = QDir::homePath() + QLatin1String("/Documents");
break;
case PicturesLocation:
path = QDir::homePath() + QLatin1String("/Pictures");
break;
case FontsLocation:
path = QDir::homePath() + QLatin1String("/.fonts");
break;
case MusicLocation:
path = QDir::homePath() + QLatin1String("/Music");
break;
case MoviesLocation:
path = QDir::homePath() + QLatin1String("/Videos");
break;
case DownloadLocation:
path = QDir::homePath() + QLatin1String("/Downloads");
break;
case ApplicationsLocation:
path = writableLocation(GenericDataLocation) + QLatin1String("/applications");
break;
default:
break;
}
return path;
}
static QStringList xdgDataDirs()
{
QStringList dirs;
// http://standards.freedesktop.org/basedir-spec/latest/
QString xdgDataDirsEnv = QFile::decodeName(qgetenv("XDG_DATA_DIRS"));
if (xdgDataDirsEnv.isEmpty()) {
dirs.append(QString::fromLatin1("/usr/local/share"));
dirs.append(QString::fromLatin1("/usr/share"));
} else {
dirs = xdgDataDirsEnv.split(QLatin1Char(':'), QString::SkipEmptyParts);
// Normalize 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_DATA_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();
}
return dirs;
}
QStringList QStandardPaths::standardLocations(StandardLocation type)
{
QStringList dirs;
switch (type) {
case ConfigLocation:
case GenericConfigLocation:
{
// http://standards.freedesktop.org/basedir-spec/latest/
const QString xdgConfigDirs = QFile::decodeName(qgetenv("XDG_CONFIG_DIRS"));
if (xdgConfigDirs.isEmpty())
dirs.append(QString::fromLatin1("/etc/xdg"));
else
dirs = xdgConfigDirs.split(QLatin1Char(':'));
}
break;
case GenericDataLocation:
dirs = xdgDataDirs();
break;
case ApplicationsLocation:
dirs = xdgDataDirs();
for (int i = 0; i < dirs.count(); ++i)
dirs[i].append(QLatin1String("/applications"));
break;
case DataLocation:
dirs = xdgDataDirs();
for (int i = 0; i < dirs.count(); ++i)
appendOrganizationAndApp(dirs[i]);
break;
default:
break;
}
const QString localDir = writableLocation(type);
dirs.prepend(localDir);
return dirs;
}
QT_END_NAMESPACE
#endif // QT_NO_STANDARDPATHS