10#include <qtextstream.h>
11#if QT_CONFIG(regularexpression)
12#include <qregularexpression.h>
14#include <private/qfilesystemengine_p.h>
18#ifndef QT_BOOTSTRAPPED
19#include <qcoreapplication.h>
22#ifndef QT_NO_STANDARDPATHS
26using namespace Qt::StringLiterals;
30#ifndef QT_BOOTSTRAPPED
31 const QString org = QCoreApplication::organizationName();
34 const QString appName = QCoreApplication::applicationName();
35 if (!appName.isEmpty())
36 path += u'/' + appName;
42#if QT_CONFIG(regularexpression)
43static QLatin1StringView xdg_key_name(QStandardPaths::StandardLocation type)
46 case QStandardPaths::DesktopLocation:
48 case QStandardPaths::DocumentsLocation:
49 return "DOCUMENTS"_L1;
50 case QStandardPaths::PicturesLocation:
52 case QStandardPaths::MusicLocation:
54 case QStandardPaths::MoviesLocation:
56 case QStandardPaths::DownloadLocation:
58 case QStandardPaths::PublicShareLocation:
59 return "PUBLICSHARE"_L1;
60 case QStandardPaths::TemplatesLocation:
61 return "TEMPLATES"_L1;
71 if (permissions & QFile::ReadOwner)
73 if (permissions & QFile::WriteOwner)
75 if (permissions & QFile::ExeOwner)
77 if (permissions & QFile::ReadGroup)
79 if (permissions & QFile::WriteGroup)
81 if (permissions & QFile::ExeGroup)
83 if (permissions & QFile::ReadOther)
85 if (permissions & QFile::WriteOther)
87 if (permissions & QFile::ExeOther)
89 return '0' + QByteArray::number(perms, 8);
94 auto describeMetaData = [](
const QFileSystemMetaData &metaData) -> QByteArray {
95 if (!metaData.exists())
96 return "a broken symlink";
98 QByteArray description;
99 if (metaData.isLink())
100 description =
"a symbolic link to ";
102 if (metaData.isFile())
103 description +=
"a regular file";
104 else if (metaData.isDirectory())
105 description +=
"a directory";
106 else if (metaData.isSequential())
107 description +=
"a character device, socket or FIFO";
109 description +=
"a block device";
111 description +=
" permissions " + unixPermissionsText(metaData.permissions());
114 +
" owned by UID " + QByteArray::number(metaData.userId())
115 +
" GID " + QByteArray::number(metaData.groupId());
119 const uint myUid = uint(geteuid());
120 const QFile::Permissions wantedPerms = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
121 const QFileSystemMetaData::MetaDataFlags statFlags = QFileSystemMetaData::PosixStatFlags
122 | QFileSystemMetaData::LinkType;
123 QFileSystemMetaData metaData;
124 QFileSystemEntry entry(xdgRuntimeDir);
130 if (QT_MKDIR(entry.nativeFilePath(), 0700) == 0)
132 if (errno != EEXIST) {
133 qErrnoWarning(
"QStandardPaths: error creating runtime directory '%ls'",
134 qUtf16Printable(xdgRuntimeDir));
140 if (!QFileSystemEngine::fillMetaData(entry, metaData, statFlags) && !metaData.isLink()) {
141 qErrnoWarning(
"QStandardPaths: error obtaining permissions of runtime directory '%ls'",
142 qUtf16Printable(xdgRuntimeDir));
149 if (metaData.isLink() || !metaData.isDirectory()) {
150 qWarning(
"QStandardPaths: runtime directory '%ls' is not a directory, but %s",
151 qUtf16Printable(xdgRuntimeDir), describeMetaData(metaData).constData());
156 if (metaData.userId() != myUid) {
157 qWarning(
"QStandardPaths: runtime directory '%ls' is not owned by UID %d, but %s",
158 qUtf16Printable(xdgRuntimeDir), myUid, describeMetaData(metaData).constData());
163 if (metaData.permissions() != wantedPerms) {
164 qWarning(
"QStandardPaths: wrong permissions on runtime directory %ls, %s instead of %s",
165 qUtf16Printable(xdgRuntimeDir),
166 unixPermissionsText(metaData.permissions()).constData(),
167 unixPermissionsText(wantedPerms).constData());
174QString QStandardPaths::writableLocation(StandardLocation type)
178 return QDir::homePath();
180 return QDir::tempPath();
182 case GenericCacheLocation:
184 QString xdgCacheHome;
185 if (isTestModeEnabled()) {
186 xdgCacheHome = QDir::homePath() +
"/.qttest/cache"_L1;
189 xdgCacheHome = qEnvironmentVariable(
"XDG_CACHE_HOME");
190 if (!xdgCacheHome.startsWith(u'/'))
191 xdgCacheHome.clear();
193 if (xdgCacheHome.isEmpty())
194 xdgCacheHome = QDir::homePath() +
"/.cache"_L1;
196 if (type == QStandardPaths::CacheLocation)
197 appendOrganizationAndApp(xdgCacheHome);
201 case GenericStateLocation:
203 QString xdgStateHome;
204 if (isTestModeEnabled()) {
205 xdgStateHome = QDir::homePath() +
"/.qttest/state"_L1;
208 xdgStateHome = qEnvironmentVariable(
"XDG_STATE_HOME");
209 if (!xdgStateHome.startsWith(u'/'))
210 xdgStateHome.clear();
212 if (xdgStateHome.isEmpty())
213 xdgStateHome = QDir::homePath() +
"/.local/state"_L1;
215 if (type == QStandardPaths::StateLocation)
216 appendOrganizationAndApp(xdgStateHome);
219 case AppDataLocation:
220 case AppLocalDataLocation:
221 case GenericDataLocation:
224 if (isTestModeEnabled()) {
225 xdgDataHome = QDir::homePath() +
"/.qttest/share"_L1;
227 xdgDataHome = qEnvironmentVariable(
"XDG_DATA_HOME");
228 if (!xdgDataHome.startsWith(u'/'))
231 if (xdgDataHome.isEmpty())
232 xdgDataHome = QDir::homePath() +
"/.local/share"_L1;
234 if (type == AppDataLocation || type == AppLocalDataLocation)
235 appendOrganizationAndApp(xdgDataHome);
239 case GenericConfigLocation:
240 case AppConfigLocation:
242 QString xdgConfigHome;
243 if (isTestModeEnabled()) {
244 xdgConfigHome = QDir::homePath() +
"/.qttest/config"_L1;
247 xdgConfigHome = qEnvironmentVariable(
"XDG_CONFIG_HOME");
248 if (!xdgConfigHome.startsWith(u'/'))
249 xdgConfigHome.clear();
251 if (xdgConfigHome.isEmpty())
252 xdgConfigHome = QDir::homePath() +
"/.config"_L1;
254 if (type == AppConfigLocation)
255 appendOrganizationAndApp(xdgConfigHome);
256 return xdgConfigHome;
258 case RuntimeLocation:
260 QString xdgRuntimeDir = qEnvironmentVariable(
"XDG_RUNTIME_DIR");
261 if (!xdgRuntimeDir.startsWith(u'/'))
262 xdgRuntimeDir.clear();
264 bool fromEnv = !xdgRuntimeDir.isEmpty();
265 if (xdgRuntimeDir.isEmpty() || !checkXdgRuntimeDir(xdgRuntimeDir)) {
267 const uint myUid = uint(geteuid());
268 const QString userName = QFileSystemEngine::resolveUserName(myUid);
269 xdgRuntimeDir = QDir::tempPath() +
"/runtime-"_L1 + userName;
273 qWarning(
"QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '%ls'", qUtf16Printable(xdgRuntimeDir));
277 if (!checkXdgRuntimeDir(xdgRuntimeDir))
278 xdgRuntimeDir.clear();
281 return xdgRuntimeDir;
287#if QT_CONFIG(regularexpression)
289 QString xdgConfigHome = qEnvironmentVariable(
"XDG_CONFIG_HOME");
290 if (!xdgConfigHome.startsWith(u'/'))
291 xdgConfigHome.clear();
293 if (xdgConfigHome.isEmpty())
294 xdgConfigHome = QDir::homePath() +
"/.config"_L1;
295 QFile file(xdgConfigHome +
"/user-dirs.dirs"_L1);
296 const QLatin1StringView key = xdg_key_name(type);
297 if (!key.isEmpty() && !isTestModeEnabled() && file.open(QIODevice::ReadOnly)) {
298 QTextStream stream(&file);
300 static const QRegularExpression exp(u"^XDG_(.*)_DIR=(.*)$"_s);
302 while (!stream.atEnd()) {
303 const QString &line = stream.readLine();
304 QRegularExpressionMatch match = exp.match(line);
305 if (match.hasMatch() && match.capturedView(1) == key) {
306 QStringView value = match.capturedView(2);
308 && value.startsWith(u'\"')
309 && value.endsWith(u'\"'))
310 value = value.mid(1, value.size() - 2);
312 if (value.startsWith(
"$HOME"_L1))
313 result = QDir::homePath() + value.mid(5);
315 result = value.toString();
316 if (result.size() > 1 && result.endsWith(u'/'))
320 if (!result.isNull())
327 case DesktopLocation:
328 path = QDir::homePath() +
"/Desktop"_L1;
330 case DocumentsLocation:
331 path = QDir::homePath() +
"/Documents"_L1;
333 case PicturesLocation:
334 path = QDir::homePath() +
"/Pictures"_L1;
338 path = writableLocation(GenericDataLocation) +
"/fonts"_L1;
342 path = QDir::homePath() +
"/Music"_L1;
346 path = QDir::homePath() +
"/Videos"_L1;
348 case DownloadLocation:
349 path = QDir::homePath() +
"/Downloads"_L1;
351 case ApplicationsLocation:
352 path = writableLocation(GenericDataLocation) +
"/applications"_L1;
355 case PublicShareLocation:
356 path = QDir::homePath() +
"/Public"_L1;
359 case TemplatesLocation:
360 path = QDir::homePath() +
"/Templates"_L1;
376 for (
const auto dir : qTokenize(xdgEnvVar, u':'))
377 if (dir.startsWith(u'/'))
378 dirs.push_back(QDir::cleanPath(dir.toString()));
386 dirs.removeDuplicates();
394 QString xdgDataDirsEnv = qEnvironmentVariable(
"XDG_DATA_DIRS");
396 QStringList dirs = dirsList(xdgDataDirsEnv);
398 dirs = QStringList{u"/usr/local/share"_s, u"/usr/share"_s};
406 const QString xdgConfigDirs = qEnvironmentVariable(
"XDG_CONFIG_DIRS");
408 QStringList dirs = dirsList(xdgConfigDirs);
410 dirs.push_back(u"/etc/xdg"_s);
415QStringList QStandardPaths::standardLocations(StandardLocation type)
420 case GenericConfigLocation:
421 dirs = xdgConfigDirs();
423 case AppConfigLocation:
424 dirs = xdgConfigDirs();
425 for (
int i = 0; i < dirs.size(); ++i)
426 appendOrganizationAndApp(dirs[i]);
428 case GenericDataLocation:
429 dirs = xdgDataDirs();
431 case ApplicationsLocation:
432 dirs = xdgDataDirs();
433 for (
int i = 0; i < dirs.size(); ++i)
434 dirs[i].append(
"/applications"_L1);
436 case AppDataLocation:
437 case AppLocalDataLocation:
438 dirs = xdgDataDirs();
439 for (
int i = 0; i < dirs.size(); ++i)
440 appendOrganizationAndApp(dirs[i]);
443 dirs += QDir::homePath() +
"/.fonts"_L1;
444 dirs += xdgDataDirs();
445 for (
int i = 1; i < dirs.size(); ++i)
446 dirs[i].append(
"/fonts"_L1);
451 const QString localDir = writableLocation(type);
452 dirs.prepend(localDir);
static QByteArray unixPermissionsText(QFile::Permissions permissions)
static QStringList dirsList(const QString &xdgEnvVar)
static bool checkXdgRuntimeDir(const QString &xdgRuntimeDir)
static QStringList xdgConfigDirs()
static void appendOrganizationAndApp(QString &path)
static QStringList xdgDataDirs()