File indexing completed on 2024-03-24 15:27:27

0001 /* This file is part of the KDE libraries
0002    Copyright (C) 1999 Sirtaj Singh Kang <taj@kde.org>
0003    Copyright (C) 1999,2007 Stephan Kulow <coolo@kde.org>
0004    Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
0005    Copyright (C) 2009 David Faure <faure@kde.org>
0006 
0007    This library is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU Library General Public
0009    License version 2 as published by the Free Software Foundation.
0010 
0011    This library is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this library; see the file COPYING.LIB.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019    Boston, MA 02110-1301, USA.
0020 */
0021 
0022 /*
0023  * Author: Stephan Kulow <coolo@kde.org> and Sirtaj Singh Kang <taj@kde.org>
0024  * Generated: Thu Mar  5 16:05:28 EST 1998
0025  */
0026 
0027 #include "kstandarddirs.h"
0028 #include "kconfig.h"
0029 #include "kconfiggroup.h"
0030 #include "kdebug.h"
0031 #include "kglobal.h"
0032 #include "kshell.h"
0033 #include "kuser.h"
0034 #include "kde_file.h"
0035 #include "kkernel_win.h"
0036 #include "kkernel_mac.h"
0037 #include "klocalizedstring.h"
0038 #include <kservice_export.h>
0039 
0040 #include <config-kstandarddirs.h>
0041 #include <config-kdelibs4support.h>
0042 #include <stdlib.h>
0043 #include <assert.h>
0044 #include <errno.h>
0045 #if HAVE_SYS_STAT_H
0046 #include <sys/stat.h>
0047 #endif
0048 #if HAVE_UNISTD_H
0049 #include <unistd.h>
0050 #endif
0051 #include <sys/param.h>
0052 #include <sys/types.h>
0053 #include <dirent.h>
0054 #include <pwd.h>
0055 #include <grp.h>
0056 #ifdef Q_OS_WIN
0057 #include <windows.h>
0058 #ifdef _WIN32_WCE
0059 #include <basetyps.h>
0060 #endif
0061 #ifdef Q_OS_WIN64
0062 // FIXME: did not find a reliable way to fix with kdewin mingw header
0063 #define interface struct
0064 #endif
0065 #include <shlobj.h>
0066 #include <QVarLengthArray>
0067 #endif
0068 
0069 #include <QMutex>
0070 #include <QRegExp>
0071 #include <QDir>
0072 #include <QFileInfo>
0073 #include <QSettings>
0074 #include <QCoreApplication>
0075 
0076 #ifndef MAXPATHLEN
0077 # define MAXPATHLEN 4096
0078 #endif
0079 
0080 #ifdef Q_OS_WIN
0081 static const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
0082 #else
0083 static const Qt::CaseSensitivity cs = Qt::CaseSensitive;
0084 #endif
0085 
0086 class Q_DECL_HIDDEN KStandardDirs::KStandardDirsPrivate
0087 {
0088 public:
0089     KStandardDirsPrivate(KStandardDirs *qq)
0090         : m_restrictionsActive(false),
0091           m_checkRestrictions(true),
0092           m_cacheMutex(QMutex::Recursive), // resourceDirs is recursive
0093           q(qq)
0094     { }
0095 
0096     bool hasDataRestrictions(const QString &relPath) const;
0097     QStringList resourceDirs(const char *type, const QString &subdirForRestrictions);
0098 
0099     bool m_restrictionsActive : 1;
0100     bool m_checkRestrictions : 1;
0101     QMap<QByteArray, bool> m_restrictions;
0102 
0103     QStringList xdgdata_prefixes;
0104     QStringList xdgconf_prefixes;
0105     QStringList m_prefixes;
0106 
0107     // Directory dictionaries
0108     QMap<QByteArray, QStringList> m_absolutes; // For each resource type, the list of absolute paths, from most local (most priority) to most global
0109     QMap<QByteArray, QStringList> m_relatives; // Same with relative paths
0110     // The search path is "all relative paths" < "all absolute paths", from most priority to least priority.
0111 
0112     // Caches (protected by mutex in const methods, cf ctor docu)
0113     QMap<QByteArray, QStringList> m_dircache;
0114     QMap<QByteArray, QString> m_savelocations;
0115     QMutex m_cacheMutex;
0116 
0117     KStandardDirs *q;
0118 };
0119 
0120 /* If you add a new resource type here, make sure to
0121  * 1) regenerate using "kdesdk/scripts/generate_string_table.pl types < tmpfile" with the data below
0122  *        in tmpfile (no empty line at the beginning or at the end!)
0123  * 2) update the KStandardDirs class documentation
0124  * 3) update the list in kde-config.cpp
0125  * 4) update the installPath() function below
0126 
0127 data
0128 .
0129 html
0130 doc/HTML
0131 icon
0132 icons
0133 config
0134 config
0135 pixmap
0136 pixmaps
0137 sound
0138 sounds
0139 locale
0140 locale
0141 services
0142 kservices5
0143 servicetypes
0144 kservicetypes5
0145 wallpaper
0146 wallpapers
0147 templates
0148 templates
0149 exe
0150 bin
0151 module
0152 %lib/plugins/kf5
0153 qtplugins
0154 %lib/plugins
0155 kcfg
0156 config.kcfg
0157 emoticons
0158 emoticons
0159 xdgdata
0160 .
0161 xdgdata-apps
0162 applications
0163 xdgdata-icon
0164 icons
0165 xdgdata-pixmap
0166 pixmaps
0167 xdgdata-dirs
0168 desktop-directories
0169 xdgdata-mime
0170 mime
0171 xdgconf
0172 .
0173 xdgconf-menu
0174 menus
0175 xdgconf-autostart
0176 autostart
0177 */
0178 
0179 static const char types_string[] =
0180     "data\0"
0181     ".\0"
0182     "html\0"
0183     "doc/HTML\0"
0184     "icon\0"
0185     "icons\0"
0186     "config\0"
0187     "pixmap\0"
0188     "pixmaps\0"
0189     "sound\0"
0190     "sounds\0"
0191     "locale\0"
0192     "services\0"
0193     "kservices5\0"
0194     "servicetypes\0"
0195     "kservicetypes5\0"
0196     "wallpaper\0"
0197     "wallpapers\0"
0198     "templates\0"
0199     "exe\0"
0200     "bin\0"
0201     "module\0"
0202     "%lib/plugins/kf5\0"
0203     "qtplugins\0"
0204     "%lib/plugins\0"
0205     "kcfg\0"
0206     "config.kcfg\0"
0207     "emoticons\0"
0208     "xdgdata\0"
0209     "xdgdata-apps\0"
0210     "applications\0"
0211     "xdgdata-icon\0"
0212     "xdgdata-pixmap\0"
0213     "xdgdata-dirs\0"
0214     "desktop-directories\0"
0215     "xdgdata-mime\0"
0216     "mime\0"
0217     "xdgconf\0"
0218     "xdgconf-menu\0"
0219     "menus\0"
0220     "xdgconf-autostart\0"
0221     "autostart\0"
0222     "\0";
0223 
0224 static const int types_indices[] = {
0225        0,    5,    7,   12,   21,   26,   32,   32,
0226       39,   46,   54,   60,   67,   67,   74,   83,
0227       94,  107,  122,  132,  143,  143,  153,  157,
0228      161,  168,  185,  195,  208,  213,  225,  225,
0229      235,    5,  243,  256,  269,   26,  282,   46,
0230      297,  310,  330,  343,  348,    5,  356,  369,
0231      375,  393,   -1
0232 };
0233 
0234 static void tokenize(QStringList &token, const QString &str,
0235                      const QString &delim);
0236 
0237 enum BasePrefix { XdgConf, XdgData, KdePrefixes };
0238 static BasePrefix basePrefixForResource(const char *type)
0239 {
0240     // KF5: We now use xdgdata_prefixes for every resource in share/*,
0241     // i.e. everything except exe, lib, config and xdgconf...
0242 
0243     // TODO: exe: replaced with $PATH
0244     //       lib: unused as is, right?
0245     //     module: based on lib, but mostly replaced with QT_PLUGIN_PATH (+"/kf5")
0246 
0247     const QByteArray typeBa(type);
0248     if (typeBa.startsWith("xdgconf") || typeBa == "config") {
0249         return XdgConf;
0250     } else if (typeBa == "exe" || typeBa == "lib") {
0251         return KdePrefixes;
0252     } else { // was: if (typeBa.startsWith("xdgdata") || typeBa == "data")
0253         return XdgData;
0254     }
0255 }
0256 
0257 #ifdef Q_OS_WIN
0258 QString getKde4Prefix();
0259 #endif
0260 
0261 static QString relativeInstallPath(const char *type)
0262 {
0263     Q_ASSERT(type != nullptr);
0264 
0265     switch (type[0]) {
0266     case 'c':
0267         if (strcmp("config", type) == 0) {
0268             return QFile::decodeName(CONFIG_INSTALL_DIR "/");
0269         }
0270         break;
0271     case 'k':
0272         if (strcmp("kcfg", type) == 0) {
0273             return QFile::decodeName(KCFG_INSTALL_DIR "/");
0274         }
0275         if (strcmp("kdedir", type) == 0) {
0276             return QString::fromLatin1("");    // not null!
0277         }
0278         break;
0279     case 'd':
0280         if (strcmp("data", type) == 0) {
0281             return QFile::decodeName(DATA_INSTALL_DIR "/");
0282         }
0283         break;
0284     case 'e':
0285         if (strcmp("exe", type) == 0) {
0286             return QFile::decodeName(BIN_INSTALL_DIR "/");
0287         }
0288         break;
0289     case 'h':
0290         if (strcmp("html", type) == 0) {
0291             return QFile::decodeName(HTML_INSTALL_DIR "/");
0292         }
0293         break;
0294     case 'i':
0295         if (strcmp("icon", type) == 0) {
0296             return QFile::decodeName(ICON_INSTALL_DIR "/");
0297         }
0298         if (strcmp("include", type) == 0) {
0299             return QFile::decodeName(INCLUDE_INSTALL_DIR "/");
0300         }
0301         break;
0302     case 'l':
0303         if (strcmp("lib", type) == 0) {
0304             return QFile::decodeName(LIB_INSTALL_DIR "/");
0305         }
0306         if (strcmp("libexec", type) == 0) {
0307             return QFile::decodeName(LIBEXEC_INSTALL_DIR "/");
0308         }
0309         if (strcmp("locale", type) == 0) {
0310             return QFile::decodeName(LOCALE_INSTALL_DIR "/");
0311         }
0312         break;
0313     case 'm':
0314         if (strcmp("module", type) == 0) {
0315             return QFile::decodeName(PLUGIN_INSTALL_DIR "/");
0316         }
0317         break;
0318     case 'q':
0319         if (strcmp("qtplugins", type) == 0) {
0320             return QFile::decodeName(PLUGIN_INSTALL_DIR "/");
0321         }
0322         break;
0323     case 's':
0324         if (strcmp("services", type) == 0) {
0325             return QFile::decodeName(SERVICES_INSTALL_DIR "/");
0326         }
0327         if (strcmp("servicetypes", type) == 0) {
0328             return QFile::decodeName(SERVICETYPES_INSTALL_DIR "/");
0329         }
0330         if (strcmp("sound", type) == 0) {
0331             return QFile::decodeName(SOUND_INSTALL_DIR "/");
0332         }
0333         break;
0334     case 't':
0335         if (strcmp("templates", type) == 0) {
0336             return QFile::decodeName(TEMPLATES_INSTALL_DIR "/");
0337         }
0338         break;
0339     case 'w':
0340         if (strcmp("wallpaper", type) == 0) {
0341             return QFile::decodeName(WALLPAPER_INSTALL_DIR "/");
0342         }
0343         break;
0344     case 'x':
0345         if (strcmp("xdgconf-menu", type) == 0) {
0346             return QFile::decodeName(SYSCONF_INSTALL_DIR "/xdg/menus/");
0347         }
0348         if (strcmp("xdgdata-apps", type) == 0) {
0349             return QFile::decodeName(XDG_APPS_INSTALL_DIR "/");
0350         }
0351         if (strcmp("xdgdata-dirs", type) == 0) {
0352             return QFile::decodeName(XDG_DIRECTORY_INSTALL_DIR "/");
0353         }
0354         break;
0355     }
0356     return QString();
0357 }
0358 
0359 QString KStandardDirs::installPath(const char *type)
0360 {
0361     const QString relPath = relativeInstallPath(type);
0362     if (relPath.isNull()) {
0363         return QString();
0364     } else if (QDir::isAbsolutePath(relPath)) {
0365         return relPath;
0366     } else {
0367 #ifdef Q_OS_WIN
0368         return getKde4Prefix() + relPath;
0369 #else
0370         return QFile::decodeName(CMAKE_INSTALL_PREFIX "/") + relPath;
0371 #endif
0372     }
0373 }
0374 
0375 KStandardDirs::KStandardDirs()
0376     : d(new KStandardDirsPrivate(this))
0377 {
0378     addKDEDefaults();
0379 }
0380 
0381 KStandardDirs::~KStandardDirs()
0382 {
0383     delete d;
0384 }
0385 
0386 bool KStandardDirs::isRestrictedResource(const char *type, const QString &relPath) const
0387 {
0388     if (!d->m_restrictionsActive) {
0389         return false;
0390     }
0391 
0392     if (d->m_restrictions.value(type, false)) {
0393         return true;
0394     }
0395 
0396     if (strcmp(type, "data") == 0 && d->hasDataRestrictions(relPath)) {
0397         return true;
0398     }
0399 
0400     return false;
0401 }
0402 
0403 bool KStandardDirs::KStandardDirsPrivate::hasDataRestrictions(const QString &relPath) const
0404 {
0405     QString key;
0406     const int i = relPath.indexOf(QLatin1Char('/'));
0407     if (i != -1) {
0408         key = QString::fromLatin1("data_") + relPath.left(i);
0409     } else {
0410         key = QString::fromLatin1("data_") + relPath;
0411     }
0412 
0413     return m_restrictions.value(key.toLatin1(), false);
0414 }
0415 
0416 QStringList KStandardDirs::allTypes() const
0417 {
0418     QStringList list;
0419     for (int i = 0; types_indices[i] != -1; i += 2) {
0420         list.append(QLatin1String(types_string + types_indices[i]));
0421     }
0422     // Those are added manually by addKDEDefaults
0423     list.append(QString::fromLatin1("lib"));
0424     //list.append(QString::fromLatin1("home")); // undocumented on purpose, said Waldo in r113855.
0425 
0426     // Those are handled by resourceDirs() itself
0427     list.append(QString::fromLatin1("socket"));
0428     list.append(QString::fromLatin1("tmp"));
0429     list.append(QString::fromLatin1("cache"));
0430     // Those are handled by installPath()
0431     list.append(QString::fromLatin1("include"));
0432 
0433     // If you add anything here, make sure kde-config.cpp has a description for it.
0434 
0435     return list;
0436 }
0437 
0438 static void priorityAdd(QStringList &prefixes, const QString &dir, bool priority)
0439 {
0440     if (priority && !prefixes.isEmpty()) {
0441         // Add in front but behind the most-local prefix
0442         QStringList::iterator it = prefixes.begin();
0443         ++it;
0444         prefixes.insert(it, dir);
0445     } else {
0446         prefixes.append(dir);
0447     }
0448 }
0449 
0450 void KStandardDirs::addPrefix(const QString &_dir)
0451 {
0452     addPrefix(_dir, false);
0453 }
0454 
0455 void KStandardDirs::addPrefix(const QString &_dir, bool priority)
0456 {
0457     if (_dir.isEmpty()) {
0458         return;
0459     }
0460 
0461     QString dir = _dir;
0462     if (dir.at(dir.length() - 1) != QLatin1Char('/')) {
0463         dir += QLatin1Char('/');
0464     }
0465 
0466     if (!d->m_prefixes.contains(dir, cs)) {
0467         priorityAdd(d->m_prefixes, dir, priority);
0468         d->m_dircache.clear();
0469     }
0470 }
0471 
0472 void KStandardDirs::addXdgConfigPrefix(const QString &_dir)
0473 {
0474     addXdgConfigPrefix(_dir, false);
0475 }
0476 
0477 void KStandardDirs::addXdgConfigPrefix(const QString &_dir, bool priority)
0478 {
0479     if (_dir.isEmpty()) {
0480         return;
0481     }
0482 
0483     QString dir = _dir;
0484     if (dir.at(dir.length() - 1) != QLatin1Char('/')) {
0485         dir += QLatin1Char('/');
0486     }
0487 
0488     if (!d->xdgconf_prefixes.contains(dir, cs)) {
0489         priorityAdd(d->xdgconf_prefixes, dir, priority);
0490         d->m_dircache.clear();
0491     }
0492 }
0493 
0494 void KStandardDirs::addXdgDataPrefix(const QString &_dir)
0495 {
0496     addXdgDataPrefix(_dir, false);
0497 }
0498 
0499 void KStandardDirs::addXdgDataPrefix(const QString &_dir, bool priority)
0500 {
0501     if (_dir.isEmpty()) {
0502         return;
0503     }
0504 
0505     QString dir = _dir;
0506     if (dir.at(dir.length() - 1) != QLatin1Char('/')) {
0507         dir += QLatin1Char('/');
0508     }
0509 
0510     if (!d->xdgdata_prefixes.contains(dir, cs)) {
0511         priorityAdd(d->xdgdata_prefixes, dir, priority);
0512         d->m_dircache.clear();
0513     }
0514 }
0515 
0516 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0517 QString KStandardDirs::kfsstnd_prefixes()
0518 {
0519     return d->m_prefixes.join(QString(QLatin1Char(':')));
0520 }
0521 
0522 bool KStandardDirs::addResourceType(const char *type,
0523                                     const QString &relativename,
0524                                     bool priority)
0525 {
0526     return addResourceType(type, nullptr, relativename, priority);
0527 }
0528 #endif
0529 
0530 bool KStandardDirs::addResourceType(const char *type,
0531                                     const char *basetype,
0532                                     const QString &relativename,
0533                                     bool priority)
0534 {
0535     if (relativename.isEmpty()) {
0536         return false;
0537     }
0538 
0539     QString copy = relativename;
0540     if (basetype) {
0541         copy = QLatin1Char('%') + QString::fromLatin1(basetype) + QLatin1Char('/');
0542         if (relativename != QLatin1String("/")) {
0543             copy += relativename;
0544         }
0545     }
0546 
0547     if (!copy.endsWith(QLatin1Char('/'))) {
0548         copy += QLatin1Char('/');
0549     }
0550 
0551     QByteArray typeBa = type;
0552     QStringList &rels = d->m_relatives[typeBa]; // find or insert
0553 
0554     if (!rels.contains(copy, cs)) {
0555         if (priority) {
0556             rels.prepend(copy);
0557         } else {
0558             rels.append(copy);
0559         }
0560         // clean the caches
0561         d->m_dircache.remove(typeBa);
0562         d->m_savelocations.remove(typeBa);
0563         return true;
0564     }
0565     return false;
0566 }
0567 
0568 bool KStandardDirs::addResourceDir(const char *type,
0569                                    const QString &absdir,
0570                                    bool priority)
0571 {
0572     if (absdir.isEmpty() || !type) {
0573         return false;
0574     }
0575     // find or insert entry in the map
0576     QString copy = absdir;
0577     if (copy.at(copy.length() - 1) != QLatin1Char('/')) {
0578         copy += QLatin1Char('/');
0579     }
0580 
0581     QByteArray typeBa = type;
0582     QStringList &paths = d->m_absolutes[typeBa];
0583     if (!paths.contains(copy, cs)) {
0584         if (priority) {
0585             paths.prepend(copy);
0586         } else {
0587             paths.append(copy);
0588         }
0589         // clean the caches
0590         d->m_dircache.remove(typeBa);
0591         d->m_savelocations.remove(typeBa);
0592         return true;
0593     }
0594     return false;
0595 }
0596 
0597 QString KStandardDirs::findResource(const char *type,
0598                                     const QString &_filename) const
0599 {
0600     if (!QDir::isRelativePath(_filename)) {
0601         // absolute dirs are absolute dirs, right? :-/
0602         return KLocalizedString::localizedFilePath(_filename); // -- almost.
0603     }
0604 
0605 #if 0
0606     kDebug(180) << "Find resource: " << type;
0607     for (QStringList::ConstIterator pit = m_prefixes.begin();
0608             pit != m_prefixes.end();
0609             ++pit) {
0610         kDebug(180) << "Prefix: " << *pit;
0611     }
0612 #endif
0613 
0614     QString filename(_filename);
0615 #ifdef Q_OS_WIN
0616     if (strcmp(type, "exe") == 0) {
0617         if (!filename.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive)) {
0618             filename += QLatin1String(".exe");
0619         }
0620     }
0621 #endif
0622     const QString dir = findResourceDir(type, filename);
0623     if (dir.isEmpty()) {
0624         return dir;
0625     } else {
0626         return KLocalizedString::localizedFilePath(dir + filename);
0627     }
0628 }
0629 
0630 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0631 static quint32 updateHash(const QString &file, quint32 hash)
0632 {
0633     KDE_struct_stat buff;
0634     if ((KDE::access(file, R_OK) == 0) && (KDE::stat(file, &buff) == 0) && (S_ISREG(buff.st_mode))) {
0635         hash = hash + static_cast<quint32>(buff.st_ctime);
0636     }
0637     return hash;
0638 }
0639 
0640 quint32 KStandardDirs::calcResourceHash(const char *type,
0641                                         const QString &filename,
0642                                         SearchOptions options) const
0643 {
0644     quint32 hash = 0;
0645 
0646     if (!QDir::isRelativePath(filename)) {
0647         // absolute dirs are absolute dirs, right? :-/
0648         return updateHash(filename, hash);
0649     }
0650     QStringList candidates = d->resourceDirs(type, filename);
0651 
0652     foreach (const QString &candidate, candidates) {
0653         hash = updateHash(candidate + filename, hash);
0654         if (!(options & Recursive) && hash) {
0655             return hash;
0656         }
0657     }
0658     return hash;
0659 }
0660 #endif
0661 
0662 QStringList KStandardDirs::findDirs(const char *type,
0663                                     const QString &reldir) const
0664 {
0665     QDir testdir;
0666     QStringList list;
0667     if (!QDir::isRelativePath(reldir)) {
0668         testdir.setPath(reldir);
0669         if (testdir.exists()) {
0670             if (reldir.endsWith(QLatin1Char('/'))) {
0671                 list.append(reldir);
0672             } else {
0673                 list.append(reldir + QLatin1Char('/'));
0674             }
0675         }
0676         return list;
0677     }
0678 
0679     const QStringList candidates = d->resourceDirs(type, reldir);
0680 
0681     for (QStringList::ConstIterator it = candidates.begin();
0682             it != candidates.end(); ++it) {
0683         testdir.setPath(*it + reldir);
0684         if (testdir.exists()) {
0685             list.append(testdir.absolutePath() + QLatin1Char('/'));
0686         }
0687     }
0688 
0689     return list;
0690 }
0691 
0692 QString KStandardDirs::findResourceDir(const char *type,
0693                                        const QString &_filename) const
0694 {
0695 #ifndef NDEBUG
0696     if (_filename.isEmpty()) {
0697         kWarning() << "filename for type " << type << " in KStandardDirs::findResourceDir is not supposed to be empty!!";
0698         return QString();
0699     }
0700 #endif
0701 
0702     QString filename(_filename);
0703 #ifdef Q_OS_WIN
0704     if (strcmp(type, "exe") == 0) {
0705         if (!filename.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive)) {
0706             filename += QLatin1String(".exe");
0707         }
0708     }
0709 #endif
0710     const QStringList candidates = d->resourceDirs(type, filename);
0711 
0712     for (QStringList::ConstIterator it = candidates.begin();
0713             it != candidates.end(); ++it) {
0714         if (exists(*it + filename)) {
0715             return *it;
0716         }
0717     }
0718 
0719 #ifndef NDEBUG
0720     if (false && strcmp(type, "locale")) {
0721         kDebug(180) << "KStdDirs::findResDir(): can't find \"" << filename << "\" in type \"" << type << "\".";
0722     }
0723 #endif
0724 
0725     return QString();
0726 }
0727 
0728 bool KStandardDirs::exists(const QString &fullPath)
0729 {
0730 #ifdef Q_OS_WIN
0731     // access() and stat() give a stupid error message to the user
0732     // if the path is not accessible at all (e.g. no disk in A:/ and
0733     // we do stat("A:/.directory")
0734     if (fullPath.endsWith(QLatin1Char('/'))) {
0735         return QDir(fullPath).exists();
0736     }
0737     return QFileInfo(fullPath).exists();
0738 #else
0739     KDE_struct_stat buff;
0740     QByteArray cFullPath = QFile::encodeName(fullPath);
0741     if (access(cFullPath, R_OK) == 0 && KDE_stat(cFullPath, &buff) == 0) {
0742         if (!fullPath.endsWith(QLatin1Char('/'))) {
0743             if (S_ISREG(buff.st_mode)) {
0744                 return true;
0745             }
0746         } else if (S_ISDIR(buff.st_mode)) {
0747             return true;
0748         }
0749     }
0750     return false;
0751 #endif
0752 }
0753 
0754 static void lookupDirectory(const QString &path, const QString &relPart,
0755                             const QRegExp &regexp,
0756                             QStringList &list,
0757                             QStringList &relList,
0758                             bool recursive, bool unique)
0759 {
0760     const QString pattern = regexp.pattern();
0761     if (recursive || pattern.contains(QLatin1Char('?')) || pattern.contains(QLatin1Char('*'))) {
0762         if (path.isEmpty()) { //for sanity
0763             return;
0764         }
0765 #ifdef Q_OS_WIN
0766         QString path_ = path + QLatin1String("*.*");
0767         WIN32_FIND_DATA findData;
0768         HANDLE hFile = FindFirstFile((LPWSTR)path_.utf16(), &findData);
0769         if (hFile == INVALID_HANDLE_VALUE) {
0770             return;
0771         }
0772         do {
0773             const int len = wcslen(findData.cFileName);
0774             if (!(findData.cFileName[0] == '.' &&
0775                     findData.cFileName[1] == '\0') &&
0776                     !(findData.cFileName[0] == '.' &&
0777                       findData.cFileName[1] == '.' &&
0778                       findData.cFileName[2] == '\0') &&
0779                     (findData.cFileName[len - 1] != '~')) {
0780                 QString fn = QString::fromUtf16((const unsigned short *)findData.cFileName);
0781                 if (!recursive && !regexp.exactMatch(fn)) {
0782                     continue;    // No match
0783                 }
0784                 QString pathfn = path + fn;
0785                 bool bIsDir = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY);
0786                 if (recursive) {
0787                     if (bIsDir) {
0788                         lookupDirectory(pathfn + QLatin1Char('/'),
0789                                         relPart + fn + QLatin1Char('/'),
0790                                         regexp, list, relList, recursive, unique);
0791                     }
0792                     if (!regexp.exactMatch(fn)) {
0793                         continue;    // No match
0794                     }
0795                 }
0796                 if (!bIsDir) {
0797                     if (!unique || !relList.contains(relPart + fn, cs)) {
0798                         list.append(pathfn);
0799                         relList.append(relPart + fn);
0800                     }
0801                 }
0802             }
0803         } while (FindNextFile(hFile, &findData) != 0);
0804         FindClose(hFile);
0805 #else
0806         // We look for a set of files.
0807         DIR *dp = opendir(QFile::encodeName(path));
0808         if (!dp) {
0809             return;
0810         }
0811 
0812         assert(path.endsWith(QLatin1Char('/')));
0813 
0814         struct dirent *ep;
0815 
0816         while ((ep = readdir(dp)) != nullptr) {
0817             QString fn(QFile::decodeName(ep->d_name));
0818             if (fn == QString::fromLatin1(".") || fn == QString::fromLatin1("..") || fn.at(fn.length() - 1) == QLatin1Char('~')) {
0819                 continue;
0820             }
0821 
0822             if (!recursive && !regexp.exactMatch(fn)) {
0823                 continue;    // No match
0824             }
0825 
0826             bool isDir;
0827             bool isReg;
0828 
0829             QString pathfn = path + fn;
0830 #if HAVE_DIRENT_D_TYPE
0831             isDir = ep->d_type == DT_DIR;
0832             isReg = ep->d_type == DT_REG;
0833 
0834             if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK)
0835 #endif
0836             {
0837                 KDE_struct_stat buff;
0838                 if (KDE::stat(pathfn, &buff) != 0) {
0839                     kDebug(180) << "Error stat'ing " << pathfn << " : " << perror;
0840                     continue; // Couldn't stat (e.g. no read permissions)
0841                 }
0842                 isReg = S_ISREG(buff.st_mode);
0843                 isDir = S_ISDIR(buff.st_mode);
0844             }
0845 
0846             if (recursive) {
0847                 if (isDir) {
0848                     lookupDirectory(pathfn + QLatin1Char('/'), relPart + fn + QLatin1Char('/'), regexp, list, relList, recursive, unique);
0849                 }
0850                 if (!regexp.exactMatch(fn)) {
0851                     continue;    // No match
0852                 }
0853             }
0854             if (isReg) {
0855                 if (!unique || !relList.contains(relPart + fn, cs)) {
0856                     list.append(pathfn);
0857                     relList.append(relPart + fn);
0858                 }
0859             }
0860         }
0861         closedir(dp);
0862 #endif
0863     } else {
0864         // We look for a single file.
0865         QString fn = pattern;
0866         QString pathfn = path + fn;
0867         KDE_struct_stat buff;
0868         if (KDE::stat(pathfn, &buff) != 0) {
0869             return;    // File not found
0870         }
0871         if (S_ISREG(buff.st_mode)) {
0872             if (!unique || !relList.contains(relPart + fn, cs)) {
0873                 list.append(pathfn);
0874                 relList.append(relPart + fn);
0875             }
0876         }
0877     }
0878 }
0879 
0880 static void lookupPrefix(const QString &prefix, const QString &relpath,
0881                          const QString &relPart,
0882                          const QRegExp &regexp,
0883                          QStringList &list,
0884                          QStringList &relList,
0885                          bool recursive, bool unique)
0886 {
0887     if (relpath.isEmpty()) {
0888         if (recursive) {
0889             Q_ASSERT(prefix != QLatin1String("/"));    // we don't want to recursively list the whole disk!
0890         }
0891         lookupDirectory(prefix, relPart, regexp, list,
0892                         relList, recursive, unique);
0893         return;
0894     }
0895     QString path;
0896     QString rest;
0897 
0898     int slash = relpath.indexOf(QLatin1Char('/'));
0899     if (slash < 0) {
0900         rest = relpath.left(relpath.length() - 1);
0901     } else {
0902         path = relpath.left(slash);
0903         rest = relpath.mid(slash + 1);
0904     }
0905 
0906     if (prefix.isEmpty()) { //for sanity
0907         return;
0908     }
0909 #ifndef Q_OS_WIN
0910     // what does this assert check ?
0911     assert(prefix.endsWith(QLatin1Char('/')));
0912 #endif
0913     if (path.contains(QLatin1Char('*')) || path.contains(QLatin1Char('?'))) {
0914 
0915         QRegExp pathExp(path, Qt::CaseSensitive, QRegExp::Wildcard);
0916 
0917 #ifdef Q_OS_WIN
0918         QString prefix_ = prefix + QLatin1String("*.*");
0919         WIN32_FIND_DATA findData;
0920         HANDLE hFile = FindFirstFile((LPWSTR)prefix_.utf16(), &findData);
0921         if (hFile == INVALID_HANDLE_VALUE) {
0922             return;
0923         }
0924         do {
0925             const int len = wcslen(findData.cFileName);
0926             if (!(findData.cFileName[0] == '.' &&
0927                     findData.cFileName[1] == '\0') &&
0928                     !(findData.cFileName[0] == '.' &&
0929                       findData.cFileName[1] == '.' &&
0930                       findData.cFileName[2] == '\0') &&
0931                     (findData.cFileName[len - 1] != '~')) {
0932                 const QString fn = QString::fromUtf16((const unsigned short *)findData.cFileName);
0933                 if (!pathExp.exactMatch(fn)) {
0934                     continue;    // No match
0935                 }
0936                 if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
0937                     lookupPrefix(prefix + fn + QLatin1Char('/'),
0938                                  rest, relPart + fn + QLatin1Char('/'),
0939                                  regexp, list, relList, recursive, unique);
0940             }
0941         } while (FindNextFile(hFile, &findData) != 0);
0942         FindClose(hFile);
0943 #else
0944         DIR *dp = opendir(QFile::encodeName(prefix));
0945         if (!dp) {
0946             return;
0947         }
0948 
0949         struct dirent *ep;
0950 
0951         while ((ep = readdir(dp)) != nullptr) {
0952             QString fn(QFile::decodeName(ep->d_name));
0953             if (fn == QLatin1String(".") || fn == QLatin1String("..") || fn.at(fn.length() - 1) == QLatin1Char('~')) {
0954                 continue;
0955             }
0956 
0957             if (!pathExp.exactMatch(fn)) {
0958                 continue;    // No match
0959             }
0960             QString rfn = relPart + fn;
0961             fn = prefix + fn;
0962 
0963             bool isDir;
0964 
0965 #if HAVE_DIRENT_D_TYPE
0966             isDir = ep->d_type == DT_DIR;
0967 
0968             if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK)
0969 #endif
0970             {
0971                 QString pathfn = path + fn;
0972                 KDE_struct_stat buff;
0973                 if (KDE::stat(fn, &buff) != 0) {
0974                     kDebug(180) << "Error stat'ing " << fn << " : " << perror;
0975                     continue; // Couldn't stat (e.g. no read permissions)
0976                 }
0977                 isDir = S_ISDIR(buff.st_mode);
0978             }
0979             if (isDir) {
0980                 lookupPrefix(fn + QLatin1Char('/'), rest, rfn + QLatin1Char('/'), regexp, list, relList, recursive, unique);
0981             }
0982         }
0983 
0984         closedir(dp);
0985 #endif
0986     } else {
0987         // Don't stat, if the dir doesn't exist we will find out
0988         // when we try to open it.
0989         lookupPrefix(prefix + path + QLatin1Char('/'), rest,
0990                      relPart + path + QLatin1Char('/'), regexp, list,
0991                      relList, recursive, unique);
0992     }
0993 }
0994 
0995 QStringList
0996 KStandardDirs::findAllResources(const char *type,
0997                                 const QString &filter,
0998                                 SearchOptions options,
0999                                 QStringList &relList) const
1000 {
1001     QString filterPath;
1002     QString filterFile;
1003 
1004     if (!filter.isEmpty()) {
1005         int slash = filter.lastIndexOf(QLatin1Char('/'));
1006         if (slash < 0) {
1007             filterFile = filter;
1008         } else {
1009             filterPath = filter.left(slash + 1);
1010             filterFile = filter.mid(slash + 1);
1011         }
1012     }
1013 
1014     QStringList candidates;
1015     if (!QDir::isRelativePath(filter)) { // absolute path
1016 #ifdef Q_OS_WIN
1017         candidates << filterPath.left(3); //e.g. "C:\"
1018         filterPath = filterPath.mid(3);
1019 #else
1020         candidates << QString::fromLatin1("/");
1021         filterPath = filterPath.mid(1);
1022 #endif
1023     } else {
1024         candidates = d->resourceDirs(type, filter);
1025     }
1026 
1027     if (filterFile.isEmpty()) {
1028         filterFile = QString(QLatin1Char('*'));
1029     }
1030 
1031     QRegExp regExp(filterFile, Qt::CaseSensitive, QRegExp::Wildcard);
1032 
1033     QStringList list;
1034     foreach (const QString &candidate, candidates) {
1035         lookupPrefix(candidate, filterPath, QString(), regExp, list,
1036                      relList, options & Recursive, options & NoDuplicates);
1037     }
1038 
1039     return list;
1040 }
1041 
1042 QStringList
1043 KStandardDirs::findAllResources(const char *type,
1044                                 const QString &filter,
1045                                 SearchOptions options) const
1046 {
1047     QStringList relList;
1048     return findAllResources(type, filter, options, relList);
1049 }
1050 
1051 // ####### KDE4: should this be removed, in favor of QDir::canonicalPath()?
1052 // aseigo: QDir::canonicalPath returns QString() if the dir doesn't exist
1053 //         and this method is often used with the expectation for it to work
1054 //         even if the directory doesn't exist. so ... no, we can't drop this
1055 //         yet
1056 QString
1057 KStandardDirs::realPath(const QString &dirname)
1058 {
1059 #ifdef Q_OS_WIN
1060     const QString strRet = realFilePath(dirname);
1061     if (!strRet.endsWith(QLatin1Char('/'))) {
1062         return strRet + QLatin1Char('/');
1063     }
1064     return strRet;
1065 #else
1066     if (dirname.isEmpty() || (dirname.size() == 1 && dirname.at(0) == QLatin1Char('/'))) {
1067         return dirname;
1068     }
1069 
1070     if (dirname.at(0) != QLatin1Char('/')) {
1071         qWarning("realPath called with a relative path '%s', please fix", qPrintable(dirname));
1072         return dirname;
1073     }
1074 
1075     char realpath_buffer[MAXPATHLEN + 1];
1076     memset(realpath_buffer, 0, MAXPATHLEN + 1);
1077 
1078     /* If the path contains symlinks, get the real name */
1079     if (realpath(QFile::encodeName(dirname).constData(), realpath_buffer) != nullptr) {
1080         // success, use result from realpath
1081         int len = strlen(realpath_buffer);
1082         realpath_buffer[len] = '/';
1083         realpath_buffer[len + 1] = 0;
1084         return QFile::decodeName(realpath_buffer);
1085     }
1086 
1087     // Does not exist yet; resolve symlinks in parent dirs then.
1088     // This ensures that once the directory exists, it will still be resolved
1089     // the same way, so that the general rule that KStandardDirs always returns
1090     // canonical paths stays true, and app code can compare paths more easily.
1091     QString dir = dirname;
1092     if (!dir.endsWith(QLatin1Char('/'))) {
1093         dir += QLatin1Char('/');
1094     }
1095     QString relative;
1096     while (!KStandardDirs::exists(dir)) {
1097         //qDebug() << "does not exist:" << dir;
1098         const int pos = dir.lastIndexOf(QLatin1Char('/'), -2);
1099         Q_ASSERT(pos >= 0); // what? even "/" doesn't exist?
1100         relative.prepend(dir.mid(pos + 1)); // keep "subdir/"
1101         dir = dir.left(pos + 1);
1102         Q_ASSERT(dir.endsWith(QLatin1Char('/')));
1103     }
1104     Q_ASSERT(!relative.isEmpty()); // infinite recursion ahead
1105     if (!relative.isEmpty()) {
1106         //qDebug() << "done, resolving" << dir << "and adding" << relative;
1107         dir = realPath(dir) + relative;
1108     }
1109     return dir;
1110 #endif
1111 }
1112 
1113 // ####### KDE4: should this be removed, in favor of QFileInfo::canonicalFilePath()?
1114 // aseigo: QDir::canonicalPath returns QString() if the dir doesn't exist
1115 //         and this method is often used with the expectation for it to work
1116 //         even if the directory doesn't exist. so ... no, we can't drop this
1117 //         yet
1118 QString
1119 KStandardDirs::realFilePath(const QString &filename)
1120 {
1121 #ifdef Q_OS_WIN
1122     LPCWSTR lpIn = (LPCWSTR)filename.utf16();
1123     QVarLengthArray<WCHAR, MAX_PATH> buf(MAX_PATH);
1124     DWORD len = GetFullPathNameW(lpIn, buf.size(), buf.data(), NULL);
1125     if (len > (DWORD)buf.size()) {
1126         buf.resize(len);
1127         len = GetFullPathNameW(lpIn, buf.size(), buf.data(), NULL);
1128     }
1129     if (len == 0) {
1130         return QString();
1131     }
1132     return QString::fromUtf16((const unsigned short *)buf.data()).replace(QLatin1Char('\\'), QLatin1Char('/'));
1133 #else
1134     char realpath_buffer[MAXPATHLEN + 1];
1135     memset(realpath_buffer, 0, MAXPATHLEN + 1);
1136 
1137     /* If the path contains symlinks, get the real name */
1138     if (realpath(QFile::encodeName(filename).constData(), realpath_buffer) != nullptr) {
1139         // success, use result from realpath
1140         return QFile::decodeName(realpath_buffer);
1141     }
1142 
1143     return filename;
1144 #endif
1145 }
1146 
1147 QStringList KStandardDirs::resourceDirs(const char *type) const
1148 {
1149     return d->resourceDirs(type, QString());
1150 }
1151 
1152 QStringList KStandardDirs::KStandardDirsPrivate::resourceDirs(const char *type, const QString &subdirForRestrictions)
1153 {
1154     QMutexLocker lock(&m_cacheMutex);
1155     const bool dataRestrictionActive = m_restrictionsActive
1156                                        && (strcmp(type, "data") == 0)
1157                                        && hasDataRestrictions(subdirForRestrictions);
1158 
1159     QMap<QByteArray, QStringList>::const_iterator dirCacheIt = m_dircache.constFind(type);
1160 
1161     QStringList candidates;
1162 
1163     if (dirCacheIt != m_dircache.constEnd() && !dataRestrictionActive) {
1164         //qDebug() << this << "resourceDirs(" << type << "), in cache already";
1165         candidates = *dirCacheIt;
1166     } else { // filling cache
1167         //qDebug() << this << "resourceDirs(" << type << "), not in cache";
1168         q->addResourceDir("socket", QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation));
1169         q->addResourceDir("tmp", QStandardPaths::writableLocation(QStandardPaths::TempLocation));
1170         q->addResourceDir("cache", QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation));
1171 
1172         QDir testdir;
1173 
1174         bool restrictionActive = false;
1175         if (m_restrictionsActive) {
1176             if (dataRestrictionActive) {
1177                 restrictionActive = true;
1178             }
1179             if (m_restrictions.value("all", false)) {
1180                 restrictionActive = true;
1181             } else if (m_restrictions.value(type, false)) {
1182                 restrictionActive = true;
1183             }
1184         }
1185 
1186         const QStringList dirs = m_relatives.value(type);
1187         QString typeInstallPath;
1188         if (strcmp(type, "xdgdata-apps") == 0) {
1189             // If the resource is xdgdata-apps, we never actually want the
1190             // install directory (which is probably a subdirectory of
1191             // CMAKE_INSTALL_PREFIX/share/applications/), but instead we want
1192             // CMAKE_INSTALL_PREFIX/share/applications/ itself.
1193 #ifdef Q_OS_WIN
1194             typeInstallPath = getKde4Prefix() + QLatin1String("share/applications/");
1195 #else
1196             typeInstallPath = QFile::decodeName(CMAKE_INSTALL_PREFIX "/") + QLatin1String("share/applications/");
1197 #endif
1198         } else {
1199             typeInstallPath = installPath(type); // could be empty
1200         }
1201         const QString installdir = typeInstallPath.isEmpty() ? QString() : realPath(typeInstallPath);
1202         const QString installprefix = installPath("kdedir");
1203         if (!dirs.isEmpty()) {
1204             bool local = true;
1205 
1206             for (QStringList::ConstIterator it = dirs.constBegin();
1207                     it != dirs.constEnd(); ++it) {
1208                 if ((*it).startsWith(QLatin1Char('%'))) {
1209                     // grab the "data" from "%data/apps"
1210                     const int pos = (*it).indexOf(QLatin1Char('/'));
1211                     QString rel = (*it).mid(1, pos - 1);
1212                     QString rest = (*it).mid(pos + 1);
1213                     const QStringList basedirs = resourceDirs(rel.toUtf8().constData(), subdirForRestrictions);
1214                     for (QStringList::ConstIterator it2 = basedirs.begin();
1215                             it2 != basedirs.end(); ++it2) {
1216                         const QString path = realPath(*it2 + rest);
1217                         testdir.setPath(path);
1218                         if ((local || testdir.exists()) && !candidates.contains(path, cs)) {
1219                             candidates.append(path);
1220                         }
1221                         local = false;
1222                     }
1223                 }
1224             }
1225 
1226             const QStringList *prefixList = nullptr;
1227             const BasePrefix basePrefix = basePrefixForResource(type);
1228             if (basePrefix == XdgConf) {
1229                 prefixList = &(xdgconf_prefixes);
1230             } else if (basePrefix == XdgData) {
1231                 prefixList = &(xdgdata_prefixes);
1232             } else if (basePrefix == KdePrefixes) {
1233                 prefixList = &m_prefixes;
1234             }
1235 
1236             for (QStringList::ConstIterator pit = prefixList->begin();
1237                     pit != prefixList->end();
1238                     ++pit) {
1239                 if ((*pit).compare(installprefix, cs) != 0 || installdir.isEmpty()) {
1240                     for (QStringList::ConstIterator it = dirs.constBegin();
1241                             it != dirs.constEnd(); ++it) {
1242                         if ((*it).startsWith(QLatin1Char('%'))) {
1243                             continue;
1244                         }
1245                         const QString path = realPath(*pit + *it);
1246                         testdir.setPath(path);
1247                         if (local && restrictionActive) {
1248                             continue;
1249                         }
1250                         if ((local || testdir.exists()) && !candidates.contains(path, cs)) {
1251                             candidates.append(path);
1252                         }
1253                     }
1254                     local = false;
1255                 } else {
1256                     // we have a custom install path, so use this instead of <installprefix>/<relative dir>
1257                     testdir.setPath(installdir);
1258                     if (testdir.exists() && ! candidates.contains(installdir, cs)) {
1259                         candidates.append(installdir);
1260                     }
1261                 }
1262             }
1263         }
1264 
1265         // make sure we find the path where it's installed
1266         if (!installdir.isEmpty()) {
1267             bool ok = true;
1268             foreach (const QString &s, candidates) {
1269                 if (installdir.startsWith(s, cs)) {
1270                     ok = false;
1271                     break;
1272                 }
1273             }
1274             if (ok) {
1275                 candidates.append(installdir);
1276             }
1277         }
1278 
1279         const QStringList absDirs = m_absolutes.value(type);
1280         for (QStringList::ConstIterator it = absDirs.constBegin();
1281                 it != absDirs.constEnd(); ++it) {
1282             testdir.setPath(*it);
1283             if (testdir.exists()) {
1284                 const QString filename = realPath(*it);
1285                 if (!candidates.contains(filename, cs)) {
1286                     candidates.append(filename);
1287                 }
1288             }
1289         }
1290 
1291         // Insert result into the cache for next time.
1292         // Exception: data_subdir restrictions are per-subdir, so we can't store such results
1293         if (!dataRestrictionActive) {
1294             //qDebug() << this << "Inserting" << type << candidates << "into dircache";
1295             m_dircache.insert(type, candidates);
1296         }
1297     }
1298 
1299 #if 0
1300     kDebug(180) << "found dirs for resource" << type << ":" << candidates;
1301 #endif
1302 
1303     return candidates;
1304 }
1305 
1306 #ifdef Q_OS_WIN
1307 static QStringList executableExtensions()
1308 {
1309     QStringList ret = QString::fromLocal8Bit(qgetenv("PATHEXT")).split(QLatin1Char(';'));
1310     if (!ret.contains(QLatin1String(".exe"), Qt::CaseInsensitive)) {
1311         // If %PATHEXT% does not contain .exe, it is either empty, malformed, or distorted in ways that we cannot support, anyway.
1312         ret.clear();
1313         ret << QLatin1String(".exe")
1314             << QLatin1String(".com")
1315             << QLatin1String(".bat")
1316             << QLatin1String(".cmd");
1317     }
1318     return ret;
1319 }
1320 #endif
1321 
1322 QStringList KStandardDirs::systemPaths(const QString &pstr)
1323 {
1324     QStringList tokens;
1325     QString p = pstr;
1326 
1327     if (p.isEmpty()) {
1328         p = QString::fromLocal8Bit(qgetenv("PATH"));
1329     }
1330 
1331     QString delimiters(QLatin1Char(KPATH_SEPARATOR));
1332     delimiters += QLatin1Char('\b');
1333     tokenize(tokens, p, delimiters);
1334 
1335     QStringList exePaths;
1336 
1337     // split path using : or \b as delimiters
1338     for (int i = 0; i < tokens.count(); i++) {
1339         exePaths << KShell::tildeExpand(tokens[ i ]);
1340     }
1341 
1342     return exePaths;
1343 }
1344 
1345 #ifdef Q_OS_MAC
1346 static QString getBundle(const QString &path, bool ignore)
1347 {
1348     //kDebug(180) << "getBundle(" << path << ", " << ignore << ") called";
1349     QFileInfo info;
1350     QString bundle = path;
1351     bundle += QLatin1String(".app/Contents/MacOS/") + bundle.section(QLatin1Char('/'), -1);
1352     info.setFile(bundle);
1353     FILE *file;
1354     if (file = fopen(info.absoluteFilePath().toUtf8().constData(), "r")) {
1355         fclose(file);
1356         struct stat _stat;
1357         if ((stat(info.absoluteFilePath().toUtf8().constData(), &_stat)) < 0) {
1358             return QString();
1359         }
1360         if (ignore || (_stat.st_mode & S_IXUSR)) {
1361             if (((_stat.st_mode & S_IFMT) == S_IFREG) || ((_stat.st_mode & S_IFMT) == S_IFLNK)) {
1362                 //kDebug(180) << "getBundle(): returning " << bundle;
1363                 return bundle;
1364             }
1365         }
1366     }
1367     return QString();
1368 }
1369 #endif
1370 
1371 static QString checkExecutable(const QString &path, bool ignoreExecBit)
1372 {
1373 #ifdef Q_OS_MAC
1374     QString bundle = getBundle(path, ignoreExecBit);
1375     if (!bundle.isEmpty()) {
1376         //kDebug(180) << "findExe(): returning " << bundle;
1377         return bundle;
1378     }
1379 #endif
1380     QFileInfo info(path);
1381     QFileInfo orig = info;
1382 #if defined(Q_OS_DARWIN) || defined(Q_OS_MAC)
1383     FILE *file;
1384     if (file = fopen(orig.absoluteFilePath().toUtf8().constData(), "r")) {
1385         fclose(file);
1386         struct stat _stat;
1387         if ((stat(orig.absoluteFilePath().toUtf8().constData(), &_stat)) < 0) {
1388             return QString();
1389         }
1390         if (ignoreExecBit || (_stat.st_mode & S_IXUSR)) {
1391             if (((_stat.st_mode & S_IFMT) == S_IFREG) || ((_stat.st_mode & S_IFMT) == S_IFLNK)) {
1392                 orig.makeAbsolute();
1393                 return orig.filePath();
1394             }
1395         }
1396     }
1397     return QString();
1398 #else
1399     if (info.exists() && info.isSymLink()) {
1400         info = QFileInfo(info.canonicalFilePath());
1401     }
1402     if (info.exists() && (ignoreExecBit || info.isExecutable()) && info.isFile()) {
1403         // return absolute path, but without symlinks resolved in order to prevent
1404         // problems with executables that work differently depending on name they are
1405         // run as (for example gunzip)
1406         orig.makeAbsolute();
1407         return QDir::cleanPath(orig.filePath());
1408     }
1409     //kDebug(180) << "checkExecutable(): failed, returning empty string";
1410     return QString();
1411 #endif
1412 }
1413 
1414 // KDE5 TODO: remove IgnoreExecBit almost unused
1415 QString KStandardDirs::findExe(const QString &appname,
1416                                const QString &pstr,
1417                                SearchOptions options)
1418 {
1419     //kDebug(180) << "findExe(" << appname << ", pstr, " << ignoreExecBit << ") called";
1420 
1421 #ifdef Q_OS_WIN
1422     QStringList executable_extensions = executableExtensions();
1423     if (!executable_extensions.contains(appname.section(QLatin1Char('.'), -1, -1, QString::SectionIncludeLeadingSep), Qt::CaseInsensitive)) {
1424         QString found_exe;
1425         foreach (const QString &extension, executable_extensions) {
1426             found_exe = findExe(appname + extension, pstr, options);
1427             if (!found_exe.isEmpty()) {
1428                 return found_exe;
1429             }
1430         }
1431         return QString();
1432     }
1433 #endif
1434     QFileInfo info;
1435 
1436     // absolute or relative path?
1437     if (QDir::isAbsolutePath(appname)) {
1438         //kDebug(180) << "findExe(): absolute path given";
1439         QString path = checkExecutable(appname, options & IgnoreExecBit);
1440         return path;
1441     }
1442 
1443     //kDebug(180) << "findExe(): relative path given";
1444 
1445     QString p = installPath("libexec") + appname;
1446     QString result = checkExecutable(p, options & IgnoreExecBit);
1447     if (!result.isEmpty()) {
1448         //kDebug(180) << "findExe(): returning " << result;
1449         return result;
1450     }
1451 
1452     //kDebug(180) << "findExe(): checking system paths";
1453     const QStringList exePaths = systemPaths(pstr);
1454     for (QStringList::ConstIterator it = exePaths.begin(); it != exePaths.end(); ++it) {
1455         p = (*it) + QLatin1Char('/');
1456         p += appname;
1457 
1458         // Check for executable in this tokenized path
1459         result = checkExecutable(p, options & IgnoreExecBit);
1460         if (!result.isEmpty()) {
1461             //kDebug(180) << "findExe(): returning " << result;
1462             return result;
1463         }
1464     }
1465 
1466     // Not found in PATH, look into the KDE-specific bin dir ("exe" resource)
1467     p = installPath("exe");
1468     p += appname;
1469     result = checkExecutable(p, options & IgnoreExecBit);
1470     if (!result.isEmpty()) {
1471         //kDebug(180) << "findExe(): returning " << result;
1472         return result;
1473     }
1474 
1475     // If we reach here, the executable wasn't found.
1476     // So return empty string.
1477 
1478     //kDebug(180) << "findExe(): failed, nothing matched";
1479     return QString();
1480 }
1481 
1482 // TODO: very rarely used. Only known example is kdevelop looking for all qmakes/cmakes in the path.
1483 int KStandardDirs::findAllExe(QStringList &list, const QString &appname,
1484                               const QString &pstr, SearchOptions options)
1485 {
1486 #ifdef Q_OS_WIN
1487     QStringList executable_extensions = executableExtensions();
1488     if (!executable_extensions.contains(appname.section(QLatin1Char('.'), -1, -1, QString::SectionIncludeLeadingSep), Qt::CaseInsensitive)) {
1489         int total = 0;
1490         foreach (const QString &extension, executable_extensions) {
1491             total += findAllExe(list, appname + extension, pstr, options);
1492         }
1493         return total;
1494     }
1495 #endif
1496     QFileInfo info;
1497     QString p;
1498     list.clear();
1499 
1500     const QStringList exePaths = systemPaths(pstr);
1501     for (QStringList::ConstIterator it = exePaths.begin(); it != exePaths.end(); ++it) {
1502         p = (*it) + QLatin1Char('/');
1503         p += appname;
1504 
1505 #ifdef Q_OS_MAC
1506         QString bundle = getBundle(p, (options & IgnoreExecBit));
1507         if (!bundle.isEmpty()) {
1508             //kDebug(180) << "findExe(): returning " << bundle;
1509             list.append(bundle);
1510         }
1511 #endif
1512 
1513         info.setFile(p);
1514 
1515         if (info.exists() && ((options & IgnoreExecBit) || info.isExecutable())
1516                 && info.isFile()) {
1517             list.append(p);
1518         }
1519     }
1520 
1521     return list.count();
1522 }
1523 
1524 static inline QString equalizePath(QString &str)
1525 {
1526 #ifdef Q_OS_WIN
1527     // filter pathes through QFileInfo to have always
1528     // the same case for drive letters
1529     QFileInfo f(str);
1530     if (f.isAbsolute()) {
1531         return f.absoluteFilePath();
1532     } else
1533 #endif
1534         return str;
1535 }
1536 
1537 static void tokenize(QStringList &tokens, const QString &str,
1538                      const QString &delim)
1539 {
1540     const int len = str.length();
1541     QString token;
1542 
1543     for (int index = 0; index < len; index++) {
1544         if (delim.contains(str[index])) {
1545             tokens.append(equalizePath(token));
1546             token.clear();
1547         } else {
1548             token += str[index];
1549         }
1550     }
1551     if (!token.isEmpty()) {
1552         tokens.append(equalizePath(token));
1553     }
1554 }
1555 
1556 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
1557 QString KStandardDirs::kde_default(const char *type)
1558 {
1559     return QString(QLatin1Char('%')) + QString::fromLatin1(type) + QLatin1Char('/');
1560 }
1561 #endif
1562 
1563 QString KStandardDirs::saveLocation(const char *type,
1564                                     const QString &suffix,
1565                                     bool create) const
1566 {
1567     QMutexLocker lock(&d->m_cacheMutex);
1568     QString path = d->m_savelocations.value(type);
1569     if (path.isEmpty()) {
1570         QStringList dirs = d->m_relatives.value(type);
1571         if (dirs.isEmpty() && (
1572                     (strcmp(type, "socket") == 0) ||
1573                     (strcmp(type, "tmp") == 0) ||
1574                     (strcmp(type, "cache") == 0))) {
1575             (void) resourceDirs(type); // Generate socket|tmp|cache resource.
1576             dirs = d->m_relatives.value(type); // Search again.
1577         }
1578         if (!dirs.isEmpty()) {
1579             path = dirs.first();
1580 
1581             if (path.startsWith(QLatin1Char('%'))) {
1582                 // grab the "data" from "%data/apps"
1583                 const int pos = path.indexOf(QLatin1Char('/'));
1584                 QString rel = path.mid(1, pos - 1);
1585                 QString rest = path.mid(pos + 1);
1586                 QString basepath = saveLocation(rel.toUtf8().constData(), QString(), create);
1587                 path = basepath + rest;
1588             } else {
1589                 if (path == QLatin1String("./")) {
1590                     path.clear();
1591                 }
1592                 // Check for existence of typed directory + suffix
1593                 const BasePrefix basePrefix = basePrefixForResource(type);
1594                 if (basePrefix == XdgConf) {
1595                     path = realPath(d->xdgconf_prefixes.first() + path);
1596                 } else if (basePrefix == XdgData) {
1597                     path = realPath(d->xdgdata_prefixes.first() + path);
1598                 } else {
1599                     path = realPath(d->m_prefixes.first() + path);
1600                 }
1601             }
1602         } else {
1603             dirs = d->m_absolutes.value(type);
1604             if (dirs.isEmpty()) {
1605                 qFatal("KStandardDirs: The resource type %s is not registered", type);
1606             } else {
1607                 path = realPath(dirs.first());
1608             }
1609         }
1610 
1611         d->m_savelocations.insert(type, path.endsWith(QLatin1Char('/')) ? path : path + QLatin1Char('/'));
1612     }
1613     QString fullPath = path + suffix;
1614 
1615     KDE_struct_stat st;
1616     if (KDE::stat(fullPath, &st) != 0 || !(S_ISDIR(st.st_mode))) {
1617         if (!create) {
1618 #ifndef NDEBUG
1619             // Too much noise from kbuildsycoca4 -- it's fine if this happens from KConfig
1620             // when parsing global files without a local equivalent.
1621             //kDebug(180) << QString("save location %1 doesn't exist").arg(fullPath);
1622 #endif
1623             return fullPath;
1624         }
1625         if (!makeDir(fullPath, 0700)) {
1626             return fullPath;
1627         }
1628         d->m_dircache.remove(type);
1629     }
1630     if (!fullPath.endsWith(QLatin1Char('/'))) {
1631         fullPath += QLatin1Char('/');
1632     }
1633     return fullPath;
1634 }
1635 
1636 QString KStandardDirs::relativeLocation(const char *type, const QString &absPath) const
1637 {
1638     QString fullPath = absPath;
1639     int i = absPath.lastIndexOf(QLatin1Char('/'));
1640     if (i != -1) {
1641         fullPath = realFilePath(absPath); // Normalize
1642     }
1643 
1644     const QStringList candidates = resourceDirs(type);
1645 
1646     for (QStringList::ConstIterator it = candidates.begin();
1647             it != candidates.end(); ++it) {
1648         if (fullPath.startsWith(*it, cs)) {
1649             return fullPath.mid((*it).length());
1650         }
1651     }
1652     return absPath;
1653 }
1654 
1655 bool KStandardDirs::makeDir(const QString &dir, int mode)
1656 {
1657     // we want an absolute path
1658     if (QDir::isRelativePath(dir)) {
1659         return false;
1660     }
1661 
1662 #ifdef Q_OS_WIN
1663     return QDir().mkpath(dir);
1664 #else
1665     QString target = dir;
1666     uint len = target.length();
1667 
1668     // append trailing slash if missing
1669     if (dir.at(len - 1) != QLatin1Char('/')) {
1670         target += QLatin1Char('/');
1671     }
1672 
1673     QString base;
1674     uint i = 1;
1675 
1676     while (i < len) {
1677         KDE_struct_stat st;
1678         int pos = target.indexOf(QLatin1Char('/'), i);
1679         base += target.mid(i - 1, pos - i + 1);
1680         QByteArray baseEncoded = QFile::encodeName(base);
1681         // bail out if we encountered a problem
1682         if (KDE_stat(baseEncoded, &st) != 0) {
1683             // Directory does not exist....
1684             // Or maybe a dangling symlink ?
1685             if (KDE_lstat(baseEncoded, &st) == 0) {
1686                 (void)unlink(baseEncoded);    // try removing
1687             }
1688 
1689             if (KDE_mkdir(baseEncoded, static_cast<mode_t>(mode)) != 0) {
1690                 baseEncoded.prepend("trying to create local folder ");
1691                 perror(baseEncoded.constData());
1692                 return false; // Couldn't create it :-(
1693             }
1694         }
1695         i = pos + 1;
1696     }
1697     return true;
1698 #endif
1699 }
1700 
1701 static QString readEnvPath(const char *env)
1702 {
1703     QByteArray c_path;
1704 #ifndef _WIN32_WCE
1705     c_path = qgetenv(env);
1706     if (c_path.isEmpty()) {
1707         return QString();
1708     }
1709 #else
1710     bool ok;
1711     QString retval = getWin32RegistryValue(HKEY_LOCAL_MACHINE, "Software\\kde", "KDEDIRS", &ok);
1712     if (!ok) {
1713         return QString();
1714     } else {
1715         c_path = retval.toLatin1();
1716     }
1717 #endif
1718     return QDir::fromNativeSeparators(QFile::decodeName(c_path));
1719 }
1720 
1721 #ifdef __linux__
1722 static QString executablePrefix()
1723 {
1724     char path_buffer[MAXPATHLEN + 1];
1725     path_buffer[MAXPATHLEN] = 0;
1726     int length = readlink("/proc/self/exe", path_buffer, MAXPATHLEN);
1727     if (length == -1) {
1728         return QString();
1729     }
1730 
1731     path_buffer[length] = '\0';
1732 
1733     QString path = QFile::decodeName(path_buffer);
1734 
1735     if (path.isEmpty()) {
1736         return QString();
1737     }
1738 
1739     int pos = path.lastIndexOf(QLatin1Char('/')); // Skip filename
1740     if (pos <= 0) {
1741         return QString();
1742     }
1743     pos = path.lastIndexOf(QLatin1Char('/'), pos - 1); // Skip last directory
1744     if (pos <= 0) {
1745         return QString();
1746     }
1747 
1748     return path.left(pos);
1749 }
1750 #endif
1751 
1752 void KStandardDirs::addResourcesFrom_krcdirs()
1753 {
1754     QString localFile = QDir::currentPath() + QLatin1String("/.krcdirs");
1755     if (!QFile::exists(localFile)) {
1756         return;
1757     }
1758 
1759     QSettings iniFile(localFile, QSettings::IniFormat);
1760     iniFile.beginGroup(QString::fromLatin1("KStandardDirs"));
1761     const QStringList resources = iniFile.allKeys();
1762     foreach (const QString &key, resources) {
1763         QDir path(iniFile.value(key).toString());
1764         if (!path.exists()) {
1765             continue;
1766         }
1767 
1768         if (path.makeAbsolute()) {
1769             addResourceDir(key.toLatin1(), path.path(), false);
1770         }
1771     }
1772 }
1773 
1774 void KStandardDirs::addKDEDefaults()
1775 {
1776     addResourcesFrom_krcdirs();
1777 
1778     QStringList kdedirList;
1779     // begin KDEDIRS
1780     QString kdedirs = readEnvPath("KDEDIRS");
1781 
1782     if (!kdedirs.isEmpty()) {
1783         tokenize(kdedirList, kdedirs, QString(QLatin1Char(KPATH_SEPARATOR)));
1784     }
1785     kdedirList.append(installPath("kdedir"));
1786 
1787     QString execPrefix(QFile::decodeName(EXEC_INSTALL_PREFIX));
1788     if (!execPrefix.isEmpty() && !kdedirList.contains(execPrefix, cs)) {
1789         kdedirList.append(execPrefix);
1790     }
1791 #ifdef __linux__
1792     const QString linuxExecPrefix = executablePrefix();
1793     if (!linuxExecPrefix.isEmpty()) {
1794         kdedirList.append(linuxExecPrefix);
1795     }
1796 #endif
1797 
1798 #if 0 // No longer applicable in KDE Frameworks 5
1799     // We treat root differently to prevent a "su" shell messing up the
1800     // file permissions in the user's home directory.
1801     QString localKdeDir = readEnvPath(getuid() ? "KDEHOME" : "KDEROOTHOME");
1802     if (!localKdeDir.isEmpty()) {
1803         if (!localKdeDir.endsWith(QLatin1Char('/'))) {
1804             localKdeDir += QLatin1Char('/');
1805         }
1806     } else {
1807 #if defined(Q_OS_MAC)
1808         localKdeDir =  QDir::homePath() + QLatin1String("/Library/Preferences/KDE/");
1809 #elif defined(Q_OS_WIN)
1810 #ifndef _WIN32_WCE
1811         WCHAR wPath[MAX_PATH + 1];
1812         if (SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wPath) == S_OK) {
1813             localKdeDir = QDir::fromNativeSeparators(QString::fromUtf16((const ushort *) wPath)) + QLatin1Char('/') + QString::fromLatin1(KDE_DEFAULT_HOME) + QLatin1Char('/');
1814         } else {
1815 #endif
1816             localKdeDir =  QDir::homePath() + QLatin1Char('/') + QString::fromLatin1(KDE_DEFAULT_HOME) + QLatin1Char('/');
1817 #ifndef _WIN32_WCE
1818         }
1819 #endif
1820 #else
1821         localKdeDir =  QDir::homePath() + QLatin1Char('/') + QString::fromLatin1(KDE_DEFAULT_HOME) + QLatin1Char('/');
1822 #endif
1823     }
1824 
1825     if (localKdeDir != QLatin1String("-/")) {
1826         localKdeDir = KShell::tildeExpand(localKdeDir);
1827         addPrefix(localKdeDir);
1828     }
1829 #endif
1830 
1831 #ifdef Q_OS_MAC
1832     // Adds the "Contents" directory of the current application bundle to
1833     // the search path. This way bundled resources can be found.
1834     QDir bundleDir(mac_app_filename());
1835     if (bundleDir.dirName() == QLatin1String("MacOS")) { // just to be sure we're in a bundle
1836         bundleDir.cdUp();
1837         // now dirName should be "Contents". In there we can find our normal
1838         // dir-structure, beginning with "share"
1839         addPrefix(bundleDir.absolutePath());
1840     }
1841 #endif
1842 
1843     QStringList::ConstIterator end(kdedirList.end());
1844     for (QStringList::ConstIterator it = kdedirList.constBegin();
1845             it != kdedirList.constEnd(); ++it) {
1846         const QString dir = KShell::tildeExpand(*it);
1847         addPrefix(dir);
1848     }
1849     // end KDEDIRS
1850 
1851     // begin XDG_CONFIG_XXX
1852     QStringList xdgdirList;
1853     QString xdgdirs = readEnvPath("XDG_CONFIG_DIRS");
1854     if (!xdgdirs.isEmpty()) {
1855         tokenize(xdgdirList, xdgdirs, QString(QLatin1Char(KPATH_SEPARATOR)));
1856     } else {
1857         xdgdirList.clear();
1858         xdgdirList.append(QString::fromLatin1("/etc/xdg"));
1859 #ifdef Q_OS_WIN
1860         xdgdirList.append(installPath("kdedir") + QString::fromLatin1("etc/xdg"));
1861 #else
1862         xdgdirList.append(QFile::decodeName(KDESYSCONFDIR "/xdg"));
1863 #endif
1864     }
1865 
1866     QString localXdgDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
1867     addXdgConfigPrefix(localXdgDir);
1868 
1869     for (QStringList::ConstIterator it = xdgdirList.constBegin();
1870             it != xdgdirList.constEnd(); ++it) {
1871         QString dir = KShell::tildeExpand(*it);
1872         addXdgConfigPrefix(dir);
1873     }
1874     // end XDG_CONFIG_XXX
1875 
1876     // begin XDG_DATA_XXX
1877     QStringList kdedirDataDirs;
1878     for (QStringList::ConstIterator it = kdedirList.constBegin();
1879             it != kdedirList.constEnd(); ++it) {
1880         QString dir = *it;
1881         if (!dir.endsWith(QLatin1Char('/'))) {
1882             dir += QLatin1Char('/');
1883         }
1884         kdedirDataDirs.append(dir + QLatin1String("share/"));
1885     }
1886 
1887     xdgdirList.clear();
1888     xdgdirs = readEnvPath("XDG_DATA_DIRS");
1889     if (!xdgdirs.isEmpty()) {
1890         tokenize(xdgdirList, xdgdirs, QString(QLatin1Char(KPATH_SEPARATOR)));
1891         // Ensure the kdedirDataDirs are in there too,
1892         // otherwise resourceDirs() will add kdedir/share/applications/kde5
1893         // as returned by installPath(), and that's incorrect.
1894         Q_FOREACH (const QString &dir, kdedirDataDirs) {
1895             if (!xdgdirList.contains(dir, cs)) {
1896                 xdgdirList.append(dir);
1897             }
1898         }
1899     } else {
1900         xdgdirList = kdedirDataDirs;
1901 #ifndef Q_OS_WIN
1902         xdgdirList.append(QString::fromLatin1("/usr/local/share/"));
1903         xdgdirList.append(QString::fromLatin1("/usr/share/"));
1904 #endif
1905     }
1906 
1907     localXdgDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
1908     addXdgDataPrefix(localXdgDir);
1909 
1910     for (QStringList::ConstIterator it = xdgdirList.constBegin();
1911             it != xdgdirList.constEnd(); ++it) {
1912         QString dir = KShell::tildeExpand(*it);
1913         addXdgDataPrefix(dir);
1914     }
1915     // end XDG_DATA_XXX
1916 
1917     addResourceType("lib", nullptr, LIB_INSTALL_DIR "/");
1918 
1919     addResourceType("qtplugins", "lib", "plugins");
1920 
1921     uint index = 0;
1922     while (types_indices[index] != -1) {
1923         addResourceType(types_string + types_indices[index], nullptr, types_string + types_indices[index + 1], true);
1924         index += 2;
1925     }
1926 
1927     // config resource: the XDG paths (xdg/config) have more priority than the KDE4 paths (share/config)
1928     addResourceType("config", "xdgconf", "/", true);
1929 
1930     addResourceType("exe", "lib", "libexec", true);
1931 
1932     addResourceDir("home", QDir::homePath(), false);
1933 
1934     addResourceType("autostart", "xdgconf-autostart", "/"); // merge them, start with xdg autostart
1935     addResourceType("autostart", nullptr, "share/autostart"); // KDE ones are higher priority - KDE 5: deprecated, use xdgconf-autostart
1936 
1937     QString appName = QCoreApplication::applicationName();
1938     addResourceType("appdata", "data", appName + QLatin1Char('/'), true);
1939 }
1940 
1941 static QStringList lookupProfiles(const QString &mapFile)
1942 {
1943     QStringList profiles;
1944 
1945     if (mapFile.isEmpty() || !QFile::exists(mapFile)) {
1946         profiles << QString::fromLatin1("default");
1947         return profiles;
1948     }
1949 
1950     struct passwd *pw = getpwuid(geteuid());
1951     if (!pw) {
1952         profiles << QString::fromLatin1("default");
1953         return profiles; // Not good
1954     }
1955 
1956     QByteArray user = pw->pw_name;
1957 
1958     gid_t sup_gids[512];
1959     int sup_gids_nr = getgroups(512, sup_gids);
1960 
1961     KConfig mapCfgFile(mapFile);
1962     KConfigGroup mapCfg(&mapCfgFile, "Users");
1963     if (mapCfg.hasKey(user.constData())) {
1964         profiles = mapCfg.readEntry(user.constData(), QStringList());
1965         return profiles;
1966     }
1967 
1968     const KConfigGroup generalGrp(&mapCfgFile, "General");
1969     const QStringList groups = generalGrp.readEntry("groups", QStringList());
1970 
1971     const KConfigGroup groupsGrp(&mapCfgFile, "Groups");
1972 
1973     for (QStringList::ConstIterator it = groups.begin();
1974             it != groups.end(); ++it) {
1975         QByteArray grp = (*it).toUtf8();
1976         // Check if user is in this group
1977         struct group *grp_ent = getgrnam(grp);
1978         if (!grp_ent) {
1979             continue;
1980         }
1981         gid_t gid = grp_ent->gr_gid;
1982         if (pw->pw_gid == gid) {
1983             // User is in this group --> add profiles
1984             profiles += groupsGrp.readEntry(*it, QStringList());
1985         } else {
1986             for (int i = 0; i < sup_gids_nr; i++) {
1987                 if (sup_gids[i] == gid) {
1988                     // User is in this group --> add profiles
1989                     profiles += groupsGrp.readEntry(*it, QStringList());
1990                     break;
1991                 }
1992             }
1993         }
1994     }
1995 
1996     if (profiles.isEmpty()) {
1997         profiles << QString::fromLatin1("default");
1998     }
1999     return profiles;
2000 }
2001 
2002 extern KDELIBS4SUPPORT_DEPRECATED_EXPORT bool kde_kiosk_admin;
2003 
2004 bool KStandardDirs::addCustomized(KConfig *config)
2005 {
2006     if (!d->m_checkRestrictions) { // there are already customized entries
2007         return false;    // we just quit and hope they are the right ones
2008     }
2009 
2010     // save the numbers of config directories. If this changes,
2011     // we will return true to give KConfig a chance to reparse
2012     int configdirs = resourceDirs("config").count();
2013 
2014     if (true) {
2015         // reading the prefixes in
2016         QString group = QLatin1String("Directories");
2017         KConfigGroup cg(config, group);
2018 
2019         QString kioskAdmin = cg.readEntry("kioskAdmin");
2020         if (!kioskAdmin.isEmpty() && !kde_kiosk_admin) {
2021             int i = kioskAdmin.indexOf(QLatin1Char(':'));
2022             QString user = kioskAdmin.left(i);
2023             QString host = kioskAdmin.mid(i + 1);
2024 
2025             KUser thisUser;
2026             char hostname[ 256 ];
2027             hostname[ 0 ] = '\0';
2028             if (!gethostname(hostname, 255)) {
2029                 hostname[sizeof(hostname) - 1] = '\0';
2030             }
2031 
2032             if ((user == thisUser.loginName()) &&
2033                     (host.isEmpty() || (host == QLatin1String(hostname)))) {
2034                 kde_kiosk_admin = true;
2035             }
2036         }
2037 
2038         bool readProfiles = true;
2039 
2040         if (kde_kiosk_admin && !qgetenv("KDE_KIOSK_NO_PROFILES").isEmpty()) {
2041             readProfiles = false;
2042         }
2043 
2044         QString userMapFile = cg.readEntry("userProfileMapFile");
2045         QString profileDirsPrefix = cg.readEntry("profileDirsPrefix");
2046         if (!profileDirsPrefix.isEmpty() && !profileDirsPrefix.endsWith(QLatin1Char('/'))) {
2047             profileDirsPrefix.append(QLatin1Char('/'));
2048         }
2049 
2050         QStringList profiles;
2051         if (readProfiles) {
2052             profiles = lookupProfiles(userMapFile);
2053         }
2054         QString profile;
2055 
2056         bool priority = false;
2057         while (true) {
2058             KConfigGroup cg(config, group);
2059             const QStringList list = cg.readEntry("prefixes", QStringList());
2060             for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
2061                 addPrefix(*it, priority);
2062                 addXdgConfigPrefix(*it + QLatin1String("/etc/xdg"), priority);
2063                 addXdgDataPrefix(*it + QLatin1String("/share"), priority);
2064             }
2065             // If there are no prefixes defined, check if there is a directory
2066             // for this profile under <profileDirsPrefix>
2067             if (list.isEmpty() && !profile.isEmpty() && !profileDirsPrefix.isEmpty()) {
2068                 QString dir = profileDirsPrefix + profile;
2069                 addPrefix(dir, priority);
2070                 addXdgConfigPrefix(dir + QLatin1String("/etc/xdg"), priority);
2071                 addXdgDataPrefix(dir + QLatin1String("/share"), priority);
2072             }
2073 
2074             // iterating over all entries in the group Directories
2075             // to find entries that start with dir_$type
2076             const QMap<QString, QString> entries = config->entryMap(group);
2077             for (QMap<QString, QString>::ConstIterator it2 = entries.begin();
2078                     it2 != entries.end(); ++it2) {
2079                 const QString key = it2.key();
2080                 if (key.startsWith(QLatin1String("dir_"))) {
2081                     // generate directory list, there may be more than 1.
2082                     const QStringList dirs = (*it2).split(QString(QLatin1Char(',')));
2083                     QStringList::ConstIterator sIt(dirs.begin());
2084                     QString resType = key.mid(4);
2085                     for (; sIt != dirs.end(); ++sIt) {
2086                         addResourceDir(resType.toLatin1(), *sIt, priority);
2087                     }
2088                 }
2089             }
2090             if (profiles.isEmpty()) {
2091                 break;
2092             }
2093             profile = profiles.back();
2094             group = QString::fromLatin1("Directories-%1").arg(profile);
2095             profiles.pop_back();
2096             priority = true;
2097         }
2098     }
2099 
2100     // Process KIOSK restrictions.
2101     if (!kde_kiosk_admin || qgetenv("KDE_KIOSK_NO_RESTRICTIONS").isEmpty()) {
2102         KConfigGroup cg(config, "KDE Resource Restrictions");
2103         const QMap<QString, QString> entries = cg.entryMap();
2104         for (QMap<QString, QString>::ConstIterator it2 = entries.begin();
2105                 it2 != entries.end(); ++it2) {
2106             const QString key = it2.key();
2107             if (!cg.readEntry(key, true)) {
2108                 d->m_restrictionsActive = true;
2109                 const QByteArray cKey = key.toLatin1();
2110                 d->m_restrictions.insert(cKey, true);
2111                 d->m_dircache.remove(cKey);
2112                 d->m_savelocations.remove(cKey);
2113             }
2114         }
2115     }
2116 
2117     // check if the number of config dirs changed
2118     bool configDirsChanged = (resourceDirs("config").count() != configdirs);
2119     // If the config dirs changed, we check kiosk restrictions again.
2120     d->m_checkRestrictions = configDirsChanged;
2121     // return true if the number of config dirs changed: reparse config file
2122     return configDirsChanged;
2123 }
2124 
2125 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
2126 QString KStandardDirs::localkdedir() const
2127 {
2128     // Return the prefix to use for saving
2129     return realPath(d->m_prefixes.first());
2130 }
2131 
2132 QString KStandardDirs::localxdgdatadir() const
2133 {
2134     // Return the prefix to use for saving
2135     return realPath(d->xdgdata_prefixes.first());
2136 }
2137 
2138 QString KStandardDirs::localxdgconfdir() const
2139 {
2140     // Return the prefix to use for saving
2141     return realPath(d->xdgconf_prefixes.first());
2142 }
2143 #endif // KDELIBS4SUPPORT_NO_DEPRECATED
2144 
2145 // just to make code more readable without macros
2146 QString KStandardDirs::locate(const char *type,
2147                               const QString &filename)
2148 {
2149     return KGlobal::dirs()->findResource(type, filename);
2150 }
2151 
2152 QString KStandardDirs::locateLocal(const char *type,
2153                                    const QString &filename)
2154 {
2155     return KGlobal::dirs()->locateLocal(type, filename, true);
2156 }
2157 
2158 QString KStandardDirs::locateLocal(const char *type,
2159                                    const QString &filename, bool createDir)
2160 {
2161     // try to find slashes. If there are some, we have to
2162     // create the subdir first
2163     int slash = filename.lastIndexOf(QLatin1Char('/')) + 1;
2164     if (!slash) { // only one filename
2165         return KGlobal::dirs()->saveLocation(type, QString(), createDir) + filename;
2166     }
2167 
2168     // split path from filename
2169     QString dir = filename.left(slash);
2170     QString file = filename.mid(slash);
2171     return KGlobal::dirs()->saveLocation(type, dir, createDir) + file;
2172 }
2173 
2174 bool KStandardDirs::checkAccess(const QString &pathname, int mode)
2175 {
2176     int accessOK = KDE::access(pathname, mode);
2177     if (accessOK == 0) {
2178         return true;    // OK, I can really access the file
2179     }
2180 
2181     // else
2182     // if we want to write the file would be created. Check, if the
2183     // user may write to the directory to create the file.
2184     if ((mode & W_OK) == 0) {
2185         return false;    // Check for write access is not part of mode => bail out
2186     }
2187 
2188     if (!KDE::access(pathname, F_OK)) { // if it already exists
2189         return false;
2190     }
2191 
2192     //strip the filename (everything until '/' from the end
2193     QString dirName(pathname);
2194     int pos = dirName.lastIndexOf(QLatin1Char('/'));
2195     if (pos == -1) {
2196         return false;    // No path in argument. This is evil, we won't allow this
2197     } else if (pos == 0) { // don't turn e.g. /root into an empty string
2198         pos = 1;
2199     }
2200 
2201     dirName.truncate(pos); // strip everything starting from the last '/'
2202 
2203     accessOK = KDE::access(dirName, W_OK);
2204     // -?- Can I write to the accessed diretory
2205     if (accessOK == 0) {
2206         return true;    // Yes
2207     } else {
2208         return false;    // No
2209     }
2210 }
2211