• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDECore

kstandarddirs.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 1999 Sirtaj Singh Kang <taj@kde.org>
00003    Copyright (C) 1999,2007 Stephan Kulow <coolo@kde.org>
00004    Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
00005    Copyright (C) 2009 David Faure <faure@kde.org>
00006 
00007    This library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Library General Public
00009    License version 2 as published by the Free Software Foundation.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019    Boston, MA 02110-1301, USA.
00020 */
00021 
00022 /*
00023  * Author: Stephan Kulow <coolo@kde.org> and Sirtaj Singh Kang <taj@kde.org>
00024  * Generated: Thu Mar  5 16:05:28 EST 1998
00025  */
00026 
00027 #include "kstandarddirs.h"
00028 #include "kconfig.h"
00029 #include "kconfiggroup.h"
00030 #include "kdebug.h"
00031 #include "kcomponentdata.h"
00032 #include "kshell.h"
00033 #include "kuser.h"
00034 #include "kde_file.h"
00035 #include "kkernel_win.h"
00036 #include "kkernel_mac.h"
00037 #include "klocale.h"
00038 
00039 #include <config.h>
00040 #include <config-prefix.h>
00041 #include <config-kstandarddirs.h>
00042 
00043 #include <stdlib.h>
00044 #include <assert.h>
00045 #include <errno.h>
00046 #ifdef HAVE_SYS_STAT_H
00047 #include <sys/stat.h>
00048 #endif
00049 #ifdef HAVE_UNISTD_H
00050 #include <unistd.h>
00051 #endif
00052 #include <sys/param.h>
00053 #include <sys/types.h>
00054 #include <dirent.h>
00055 #include <pwd.h>
00056 #include <grp.h>
00057 #ifdef Q_WS_WIN
00058 #include <windows.h>
00059 #include <shlobj.h>
00060 #include <QtCore/QVarLengthArray>
00061 #endif
00062 
00063 #include <QtCore/QMutex>
00064 #include <QtCore/QRegExp>
00065 #include <QtCore/QDir>
00066 #include <QtCore/QFileInfo>
00067 #include <QtCore/QSettings>
00068 
00069 class KStandardDirs::KStandardDirsPrivate
00070 {
00071 public:
00072     KStandardDirsPrivate(KStandardDirs* qq)
00073         : m_restrictionsActive(false),
00074           m_checkRestrictions(true),
00075           m_cacheMutex(QMutex::Recursive), // resourceDirs is recursive
00076           q(qq)
00077     { }
00078 
00079     bool hasDataRestrictions(const QString &relPath) const;
00080     QStringList resourceDirs(const char* type, const QString& subdirForRestrictions);
00081     void createSpecialResource(const char*);
00082 
00083     bool m_restrictionsActive : 1;
00084     bool m_checkRestrictions : 1;
00085     QMap<QByteArray, bool> m_restrictions;
00086 
00087     QStringList xdgdata_prefixes;
00088     QStringList xdgconf_prefixes;
00089     QStringList m_prefixes;
00090 
00091     // Directory dictionaries
00092     QMap<QByteArray, QStringList> m_absolutes;
00093     QMap<QByteArray, QStringList> m_relatives;
00094 
00095     // Caches (protected by mutex in const methods, cf ctor docu)
00096     QMap<QByteArray, QStringList> m_dircache;
00097     QMap<QByteArray, QString> m_savelocations;
00098     QMutex m_cacheMutex;
00099 
00100     KStandardDirs* q;
00101 };
00102 
00103 /* If you add a new resource type here, make sure to
00104  * 1) regenerate using "kdesdk/scripts/generate_string_table.pl types < tmpfile" with the data below in tmpfile.
00105  * 2) update the KStandardDirs class documentation
00106  * 3) update the list in kde-config.cpp
00107 
00108 data
00109 share/apps
00110 html
00111 share/doc/HTML
00112 icon
00113 share/icons
00114 config
00115 share/config
00116 pixmap
00117 share/pixmaps
00118 apps
00119 share/applnk
00120 sound
00121 share/sounds
00122 locale
00123 share/locale
00124 services
00125 share/kde4/services
00126 servicetypes
00127 share/kde4/servicetypes
00128 mime
00129 share/mimelnk
00130 cgi
00131 cgi-bin
00132 wallpaper
00133 share/wallpapers
00134 templates
00135 share/templates
00136 exe
00137 bin
00138 module
00139 %lib/kde4
00140 qtplugins
00141 %lib/kde4/plugins
00142 kcfg
00143 share/config.kcfg
00144 emoticons
00145 share/emoticons
00146 xdgdata-apps
00147 applications
00148 xdgdata-icon
00149 icons
00150 xdgdata-pixmap
00151 pixmaps
00152 xdgdata-dirs
00153 desktop-directories
00154 xdgdata-mime
00155 mime
00156 xdgconf-menu
00157 menus
00158 xdgconf-autostart
00159 autostart
00160 */
00161 
00162 static const char types_string[] =
00163     "data\0"
00164     "share/apps\0"
00165     "html\0"
00166     "share/doc/HTML\0"
00167     "icon\0"
00168     "share/icons\0"
00169     "config\0"
00170     "share/config\0"
00171     "pixmap\0"
00172     "share/pixmaps\0"
00173     "apps\0"
00174     "share/applnk\0"
00175     "sound\0"
00176     "share/sounds\0"
00177     "locale\0"
00178     "share/locale\0"
00179     "services\0"
00180     "share/kde4/services\0"
00181     "servicetypes\0"
00182     "share/kde4/servicetypes\0"
00183     "mime\0"
00184     "share/mimelnk\0"
00185     "cgi\0"
00186     "cgi-bin\0"
00187     "wallpaper\0"
00188     "share/wallpapers\0"
00189     "templates\0"
00190     "share/templates\0"
00191     "exe\0"
00192     "bin\0"
00193     "module\0"
00194     "%lib/kde4\0"
00195     "qtplugins\0"
00196     "%lib/kde4/plugins\0"
00197     "kcfg\0"
00198     "share/config.kcfg\0"
00199     "emoticons\0"
00200     "share/emoticons\0"
00201     "xdgdata-apps\0"
00202     "applications\0"
00203     "xdgdata-icon\0"
00204     "icons\0"
00205     "xdgdata-pixmap\0"
00206     "pixmaps\0"
00207     "xdgdata-dirs\0"
00208     "desktop-directories\0"
00209     "xdgdata-mime\0"
00210     "xdgconf-menu\0"
00211     "menus\0"
00212     "xdgconf-autostart\0"
00213     "autostart\0"
00214     "\0";
00215 
00216 static const int types_indices[] = {
00217     0,    5,   16,   21,   36,   41,   53,   60,
00218     73,   80,   94,   99,  112,  118,  131,  138,
00219     151,  160,  180,  193,  217,  222,  236,  240,
00220     248,  258,  275,  285,  301,  305,  309,  316,
00221     326,  336,  354,  359,  377,  387,  403,  416,
00222     429,  442,  448,  463,  471,  484,  504,  217,
00223     517,  530,  536,  554,  -1
00224 };
00225 
00226 static int tokenize( QStringList& token, const QString& str,
00227                      const QString& delim );
00228 
00229 KStandardDirs::KStandardDirs()
00230     : d(new KStandardDirsPrivate(this))
00231 {
00232     addKDEDefaults();
00233 }
00234 
00235 KStandardDirs::~KStandardDirs()
00236 {
00237     delete d;
00238 }
00239 
00240 bool KStandardDirs::isRestrictedResource(const char *type, const QString& relPath) const
00241 {
00242     if (!d->m_restrictionsActive)
00243         return false;
00244 
00245     if (d->m_restrictions.value(type, false))
00246         return true;
00247 
00248     if (strcmp(type, "data")==0 && d->hasDataRestrictions(relPath))
00249         return true;
00250 
00251     return false;
00252 }
00253 
00254 bool KStandardDirs::KStandardDirsPrivate::hasDataRestrictions(const QString &relPath) const
00255 {
00256     QString key;
00257     const int i = relPath.indexOf('/');
00258     if (i != -1)
00259         key = "data_" + relPath.left(i);
00260     else
00261         key = "data_" + relPath;
00262 
00263     return m_restrictions.value(key.toLatin1(), false);
00264 }
00265 
00266 
00267 QStringList KStandardDirs::allTypes() const
00268 {
00269     QStringList list;
00270     for (int i = 0; types_indices[i] != -1; i += 2)
00271         list.append(QLatin1String(types_string + types_indices[i]));
00272     // Those are added manually by addKDEDefaults
00273     list.append("lib");
00274     //list.append("home"); // undocumented on purpose, said Waldo in r113855.
00275 
00276     // Those are handled by resourceDirs() itself
00277     list.append("socket");
00278     list.append("tmp");
00279     list.append("cache");
00280     // Those are handled by installPath()
00281     list.append("include");
00282 
00283     // If you add anything here, make sure kde-config.cpp has a description for it.
00284 
00285     return list;
00286 }
00287 
00288 static void priorityAdd(QStringList &prefixes, const QString& dir, bool priority)
00289 {
00290     if (priority && !prefixes.isEmpty())
00291     {
00292         // Add in front but behind $KDEHOME
00293         QStringList::iterator it = prefixes.begin();
00294         it++;
00295         prefixes.insert(it, dir);
00296     }
00297     else
00298     {
00299         prefixes.append(dir);
00300     }
00301 }
00302 
00303 void KStandardDirs::addPrefix( const QString& _dir )
00304 {
00305     addPrefix(_dir, false);
00306 }
00307 
00308 void KStandardDirs::addPrefix( const QString& _dir, bool priority )
00309 {
00310     if (_dir.isEmpty())
00311         return;
00312 
00313     QString dir = _dir;
00314     if (dir.at(dir.length() - 1) != '/')
00315         dir += '/';
00316 
00317     if (!d->m_prefixes.contains(dir)) {
00318         priorityAdd(d->m_prefixes, dir, priority);
00319         d->m_dircache.clear();
00320     }
00321 }
00322 
00323 void KStandardDirs::addXdgConfigPrefix( const QString& _dir )
00324 {
00325     addXdgConfigPrefix(_dir, false);
00326 }
00327 
00328 void KStandardDirs::addXdgConfigPrefix( const QString& _dir, bool priority )
00329 {
00330     if (_dir.isEmpty())
00331         return;
00332 
00333     QString dir = _dir;
00334     if (dir.at(dir.length() - 1) != '/')
00335         dir += '/';
00336 
00337     if (!d->xdgconf_prefixes.contains(dir)) {
00338         priorityAdd(d->xdgconf_prefixes, dir, priority);
00339         d->m_dircache.clear();
00340     }
00341 }
00342 
00343 void KStandardDirs::addXdgDataPrefix( const QString& _dir )
00344 {
00345     addXdgDataPrefix(_dir, false);
00346 }
00347 
00348 void KStandardDirs::addXdgDataPrefix( const QString& _dir, bool priority )
00349 {
00350     if (_dir.isEmpty())
00351         return;
00352 
00353     QString dir = _dir;
00354     if (dir.at(dir.length() - 1) != '/')
00355         dir += '/';
00356 
00357     if (!d->xdgdata_prefixes.contains(dir)) {
00358         priorityAdd(d->xdgdata_prefixes, dir, priority);
00359         d->m_dircache.clear();
00360     }
00361 }
00362 
00363 QString KStandardDirs::kfsstnd_prefixes()
00364 {
00365     return d->m_prefixes.join(QString(QChar(KPATH_SEPARATOR)));
00366 }
00367 
00368 QString KStandardDirs::kfsstnd_xdg_conf_prefixes()
00369 {
00370     return d->xdgconf_prefixes.join(QString(QChar(KPATH_SEPARATOR)));
00371 }
00372 
00373 QString KStandardDirs::kfsstnd_xdg_data_prefixes()
00374 {
00375     return d->xdgdata_prefixes.join(QString(QChar(KPATH_SEPARATOR)));
00376 }
00377 
00378 bool KStandardDirs::addResourceType( const char *type,
00379                                      const QString& relativename,
00380                                      bool priority )
00381 {
00382     return addResourceType( type, 0, relativename, priority);
00383 }
00384 
00385 bool KStandardDirs::addResourceType( const char *type,
00386                                      const char *basetype,
00387                                      const QString& relativename,
00388                                      bool priority )
00389 {
00390     if (relativename.isEmpty())
00391         return false;
00392 
00393     QString copy = relativename;
00394     if (basetype)
00395         copy = QString('%') + basetype + '/' + relativename;
00396 
00397     if (!copy.endsWith('/'))
00398         copy += '/';
00399 
00400     QStringList& rels = d->m_relatives[type]; // find or insert
00401 
00402     if (!rels.contains(copy)) {
00403         if (priority)
00404             rels.prepend(copy);
00405         else
00406             rels.append(copy);
00407         d->m_dircache.remove(type); // clean the cache
00408         return true;
00409     }
00410     return false;
00411 }
00412 
00413 bool KStandardDirs::addResourceDir( const char *type,
00414                                     const QString& absdir,
00415                                     bool priority)
00416 {
00417     if (absdir.isEmpty() || !type)
00418       return false;
00419     // find or insert entry in the map
00420     QString copy = absdir;
00421     if (copy.at(copy.length() - 1) != '/')
00422         copy += '/';
00423 
00424     QStringList &paths = d->m_absolutes[type];
00425     if (!paths.contains(copy)) {
00426         if (priority)
00427             paths.prepend(copy);
00428         else
00429             paths.append(copy);
00430         d->m_dircache.remove(type); // clean the cache
00431         return true;
00432     }
00433     return false;
00434 }
00435 
00436 QString KStandardDirs::findResource( const char *type,
00437                                      const QString& _filename ) const
00438 {
00439     if (!QDir::isRelativePath(_filename))
00440       return !KGlobal::hasLocale() ? _filename // absolute dirs are absolute dirs, right? :-/
00441                                    : KGlobal::locale()->localizedFilePath(_filename); // -- almost.
00442 
00443 #if 0
00444     kDebug(180) << "Find resource: " << type;
00445     for (QStringList::ConstIterator pit = m_prefixes.begin();
00446          pit != m_prefixes.end();
00447          ++pit)
00448     {
00449         kDebug(180) << "Prefix: " << *pit;
00450     }
00451 #endif
00452 
00453     QString filename(_filename);
00454 #ifdef Q_OS_WIN
00455     if(strcmp(type, "exe") == 0) {
00456       if(!filename.endsWith(QLatin1String(".exe")))
00457         filename += QLatin1String(".exe");
00458     }
00459 #endif
00460     const QString dir = findResourceDir(type, filename);
00461     if (dir.isEmpty())
00462       return dir;
00463     else
00464       return !KGlobal::hasLocale() ? dir + filename
00465                                    : KGlobal::locale()->localizedFilePath(dir + filename);
00466 }
00467 
00468 static quint32 updateHash(const QString &file, quint32 hash)
00469 {
00470     KDE_struct_stat buff;
00471     if ((KDE::access(file, R_OK) == 0) && (KDE::stat(file, &buff) == 0) && (S_ISREG(buff.st_mode))) {
00472         hash = hash + static_cast<quint32>(buff.st_ctime);
00473     }
00474     return hash;
00475 }
00476 
00477 quint32 KStandardDirs::calcResourceHash( const char *type,
00478                                          const QString& filename,
00479                                          SearchOptions options ) const
00480 {
00481     quint32 hash = 0;
00482 
00483     if (!QDir::isRelativePath(filename))
00484     {
00485         // absolute dirs are absolute dirs, right? :-/
00486         return updateHash(filename, hash);
00487     }
00488     QStringList candidates = d->resourceDirs(type, filename);
00489 
00490     foreach ( const QString& candidate, candidates )
00491     {
00492         hash = updateHash(candidate + filename, hash);
00493         if (  !( options & Recursive ) && hash ) {
00494             return hash;
00495         }
00496     }
00497     return hash;
00498 }
00499 
00500 
00501 QStringList KStandardDirs::findDirs( const char *type,
00502                                      const QString& reldir ) const
00503 {
00504     QDir testdir;
00505     QStringList list;
00506     if (!QDir::isRelativePath(reldir))
00507     {
00508         testdir.setPath(reldir);
00509         if (testdir.exists())
00510         {
00511             if (reldir.endsWith('/'))
00512                 list.append(reldir);
00513             else
00514                 list.append(reldir+'/');
00515         }
00516         return list;
00517     }
00518 
00519     const QStringList candidates = d->resourceDirs(type, reldir);
00520 
00521     for (QStringList::ConstIterator it = candidates.begin();
00522          it != candidates.end(); ++it) {
00523         testdir.setPath(*it + reldir);
00524         if (testdir.exists())
00525             list.append(testdir.absolutePath() + '/');
00526     }
00527 
00528     return list;
00529 }
00530 
00531 QString KStandardDirs::findResourceDir( const char *type,
00532                                         const QString& _filename) const
00533 {
00534 #ifndef NDEBUG
00535     if (_filename.isEmpty()) {
00536         kWarning() << "filename for type " << type << " in KStandardDirs::findResourceDir is not supposed to be empty!!";
00537         return QString();
00538     }
00539 #endif
00540 
00541     QString filename(_filename);
00542 #ifdef Q_OS_WIN
00543     if(strcmp(type, "exe") == 0) {
00544       if(!filename.endsWith(QLatin1String(".exe")))
00545         filename += QLatin1String(".exe");
00546     }
00547 #endif
00548     const QStringList candidates = d->resourceDirs(type, filename);
00549 
00550     for (QStringList::ConstIterator it = candidates.begin();
00551          it != candidates.end(); ++it) {
00552         if (exists(*it + filename)) {
00553             return *it;
00554         }
00555     }
00556 
00557 #ifndef NDEBUG
00558     if(false && strcmp(type, "locale"))
00559         kDebug(180) << "KStdDirs::findResDir(): can't find \"" << filename << "\" in type \"" << type << "\".";
00560 #endif
00561 
00562     return QString();
00563 }
00564 
00565 bool KStandardDirs::exists(const QString &fullPath)
00566 {
00567 #ifdef Q_OS_WIN
00568     // access() and stat() give a stupid error message to the user
00569     // if the path is not accessible at all (e.g. no disk in A:/ and
00570     // we do stat("A:/.directory")
00571     if (fullPath.endsWith('/'))
00572         return QDir(fullPath).exists();
00573     return QFileInfo(fullPath).exists();
00574 #else
00575     KDE_struct_stat buff;
00576     QByteArray cFullPath = QFile::encodeName(fullPath);
00577     if (access(cFullPath, R_OK) == 0 && KDE_stat( cFullPath, &buff ) == 0) {
00578         if (!fullPath.endsWith('/')) {
00579             if (S_ISREG( buff.st_mode ))
00580                 return true;
00581         } else
00582             if (S_ISDIR( buff.st_mode ))
00583                 return true;
00584     }
00585     return false;
00586 #endif
00587 }
00588 
00589 static void lookupDirectory(const QString& path, const QString &relPart,
00590                             const QRegExp &regexp,
00591                             QStringList& list,
00592                             QStringList& relList,
00593                             bool recursive, bool unique)
00594 {
00595     const QString pattern = regexp.pattern();
00596     if (recursive || pattern.contains('?') || pattern.contains('*'))
00597     {
00598         if (path.isEmpty()) //for sanity
00599             return;
00600 #ifdef Q_WS_WIN
00601         QString path_ = path + QLatin1String( "*.*" );
00602         WIN32_FIND_DATA findData;
00603         HANDLE hFile = FindFirstFile( (LPWSTR)path_.utf16(), &findData );
00604         if( hFile == INVALID_HANDLE_VALUE )
00605             return;
00606         do {
00607             const int len = wcslen( findData.cFileName );
00608             if (!( findData.cFileName[0] == '.' &&
00609                    findData.cFileName[1] == '\0' ) &&
00610                 !( findData.cFileName[0] == '.' &&
00611                    findData.cFileName[1] == '.' &&
00612                    findData.cFileName[2] == '\0' ) &&
00613                  ( findData.cFileName[len-1] != '~' ) ) {
00614                 QString fn = QString::fromUtf16( (const unsigned short*)findData.cFileName );
00615                 if (!recursive && !regexp.exactMatch(fn))
00616                     continue; // No match
00617                 QString pathfn = path + fn;
00618                 bool bIsDir = ( ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY );
00619                 if ( recursive ) {
00620                     if ( bIsDir ) {
00621                         lookupDirectory(pathfn + '/', relPart + fn + '/', regexp, list, relList, recursive, unique);
00622                     }
00623                     if (!regexp.exactMatch(fn))
00624                         continue; // No match
00625                 }
00626                 if ( !bIsDir )
00627                 {
00628                     if ( !unique || !relList.contains(relPart + fn) )
00629                     {
00630                         list.append( pathfn );
00631                         relList.append( relPart + fn );
00632                     }
00633                 }
00634             }
00635         } while( FindNextFile( hFile, &findData ) != 0 );
00636         FindClose( hFile );
00637 #else
00638         // We look for a set of files.
00639         DIR *dp = opendir( QFile::encodeName(path));
00640         if (!dp)
00641             return;
00642 
00643         assert(path.at(path.length() - 1) == '/');
00644 
00645         struct dirent *ep;
00646 
00647         while( ( ep = readdir( dp ) ) != 0L )
00648         {
00649             QString fn( QFile::decodeName(ep->d_name));
00650             if (fn == "." || fn == ".." || fn.at(fn.length() - 1).toLatin1() == '~')
00651                 continue;
00652 
00653             if (!recursive && !regexp.exactMatch(fn))
00654                 continue; // No match
00655 
00656             bool isDir;
00657             bool isReg;
00658 
00659             QString pathfn = path + fn;
00660 #ifdef HAVE_DIRENT_D_TYPE
00661             isDir = ep->d_type == DT_DIR;
00662             isReg = ep->d_type == DT_REG;
00663 
00664             if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK)
00665 #endif
00666             {
00667                 KDE_struct_stat buff;
00668                 if ( KDE::stat( pathfn, &buff ) != 0 ) {
00669                     kDebug(180) << "Error stat'ing " << pathfn << " : " << perror;
00670                     continue; // Couldn't stat (e.g. no read permissions)
00671                 }
00672                 isReg = S_ISREG (buff.st_mode);
00673                 isDir = S_ISDIR (buff.st_mode);
00674             }
00675 
00676             if ( recursive ) {
00677                 if ( isDir ) {
00678                     lookupDirectory(pathfn + '/', relPart + fn + '/', regexp, list, relList, recursive, unique);
00679                 }
00680                 if (!regexp.exactMatch(fn))
00681                     continue; // No match
00682             }
00683             if ( isReg )
00684             {
00685                 if (!unique || !relList.contains(relPart + fn))
00686                 {
00687                     list.append( pathfn );
00688                     relList.append( relPart + fn );
00689                 }
00690             }
00691         }
00692         closedir( dp );
00693 #endif
00694     }
00695     else
00696     {
00697         // We look for a single file.
00698         QString fn = pattern;
00699         QString pathfn = path + fn;
00700         KDE_struct_stat buff;
00701         if ( KDE::stat( pathfn, &buff ) != 0 )
00702             return; // File not found
00703         if ( S_ISREG( buff.st_mode))
00704         {
00705             if (!unique || !relList.contains(relPart + fn))
00706             {
00707                 list.append( pathfn );
00708                 relList.append( relPart + fn );
00709             }
00710         }
00711     }
00712 }
00713 
00714 static void lookupPrefix(const QString& prefix, const QString& relpath,
00715                          const QString& relPart,
00716                          const QRegExp &regexp,
00717                          QStringList& list,
00718                          QStringList& relList,
00719                          bool recursive, bool unique)
00720 {
00721     if (relpath.isEmpty()) {
00722         if (recursive)
00723             Q_ASSERT(prefix != "/"); // we don't want to recursively list the whole disk!
00724         lookupDirectory(prefix, relPart, regexp, list,
00725                         relList, recursive, unique);
00726         return;
00727     }
00728     QString path;
00729     QString rest;
00730 
00731     int slash = relpath.indexOf('/');
00732     if (slash < 0)
00733         rest = relpath.left(relpath.length() - 1);
00734     else {
00735         path = relpath.left(slash);
00736         rest = relpath.mid(slash + 1);
00737     }
00738 
00739     if (prefix.isEmpty()) //for sanity
00740         return;
00741     assert(prefix.at(prefix.length() - 1) == '/');
00742     if (path.contains('*') || path.contains('?')) {
00743 
00744         QRegExp pathExp(path, Qt::CaseSensitive, QRegExp::Wildcard);
00745 
00746 #ifdef Q_WS_WIN
00747         QString prefix_ = prefix + QLatin1String( "*.*" );
00748         WIN32_FIND_DATA findData;
00749         HANDLE hFile = FindFirstFile( (LPWSTR)prefix_.utf16(), &findData );
00750         if( hFile == INVALID_HANDLE_VALUE )
00751             return;
00752         do {
00753             const int len = wcslen( findData.cFileName );
00754             if (!( findData.cFileName[0] == '.' &&
00755                    findData.cFileName[1] == '\0' ) &&
00756                 !( findData.cFileName[0] == '.' &&
00757                    findData.cFileName[1] == '.' &&
00758                    findData.cFileName[2] == '\0' ) &&
00759                  ( findData.cFileName[len-1] != '~' ) ) {
00760                 const QString fn = QString::fromUtf16( (const unsigned short*)findData.cFileName );
00761                 if ( !pathExp.exactMatch(fn) )
00762                     continue; // No match
00763                 if ( ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY )
00764                     lookupPrefix(prefix + fn + '/', rest, relPart + fn + '/',
00765                                  regexp, list, relList, recursive, unique);
00766             }
00767         } while( FindNextFile( hFile, &findData ) != 0 );
00768         FindClose( hFile );
00769 #else
00770         DIR *dp = opendir( QFile::encodeName(prefix) );
00771         if (!dp) {
00772             return;
00773         }
00774 
00775         struct dirent *ep;
00776 
00777         while( ( ep = readdir( dp ) ) != 0L )
00778         {
00779             QString fn( QFile::decodeName(ep->d_name));
00780             if (fn == "." || fn == ".." || fn.at(fn.length() - 1) == '~')
00781                 continue;
00782 
00783             if ( !pathExp.exactMatch(fn) )
00784                 continue; // No match
00785             QString rfn = relPart+fn;
00786             fn = prefix + fn;
00787 
00788             bool isDir;
00789 
00790 #ifdef HAVE_DIRENT_D_TYPE
00791             isDir = ep->d_type == DT_DIR;
00792 
00793             if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK)
00794 #endif
00795             {
00796                 QString pathfn = path + fn;
00797                 KDE_struct_stat buff;
00798                 if ( KDE::stat( fn, &buff ) != 0 ) {
00799                     kDebug(180) << "Error stat'ing " << fn << " : " << perror;
00800                     continue; // Couldn't stat (e.g. no read permissions)
00801                 }
00802                 isDir = S_ISDIR (buff.st_mode);
00803             }
00804             if ( isDir )
00805                 lookupPrefix(fn + '/', rest, rfn + '/', regexp, list, relList, recursive, unique);
00806         }
00807 
00808         closedir( dp );
00809 #endif
00810     } else {
00811         // Don't stat, if the dir doesn't exist we will find out
00812         // when we try to open it.
00813         lookupPrefix(prefix + path + '/', rest,
00814                      relPart + path + '/', regexp, list,
00815                      relList, recursive, unique);
00816     }
00817 }
00818 
00819 QStringList
00820 KStandardDirs::findAllResources( const char *type,
00821                                  const QString& filter,
00822                                  SearchOptions options,
00823                                  QStringList &relList) const
00824 {
00825     QString filterPath;
00826     QString filterFile;
00827 
00828     if ( !filter.isEmpty() )
00829     {
00830         int slash = filter.lastIndexOf('/');
00831         if (slash < 0) {
00832             filterFile = filter;
00833         } else {
00834             filterPath = filter.left(slash + 1);
00835             filterFile = filter.mid(slash + 1);
00836         }
00837     }
00838 
00839     QStringList candidates;
00840     if ( !QDir::isRelativePath(filter) ) // absolute path
00841     {
00842 #ifdef Q_OS_WIN
00843         candidates << filterPath.left(3); //e.g. "C:\"
00844         filterPath = filterPath.mid(3);
00845 #else
00846         candidates << "/";
00847         filterPath = filterPath.mid(1);
00848 #endif
00849     }
00850     else
00851     {
00852         candidates = d->resourceDirs(type, filter);
00853     }
00854 
00855     if (filterFile.isEmpty()) {
00856         filterFile = "*";
00857     }
00858 
00859     QRegExp regExp(filterFile, Qt::CaseSensitive, QRegExp::Wildcard);
00860 
00861     QStringList list;
00862     foreach ( const QString& candidate, candidates )
00863     {
00864         lookupPrefix(candidate, filterPath, "", regExp, list,
00865                      relList, options & Recursive, options & NoDuplicates);
00866     }
00867 
00868     return list;
00869 }
00870 
00871 QStringList
00872 KStandardDirs::findAllResources( const char *type,
00873                                  const QString& filter,
00874                                  SearchOptions options ) const
00875 {
00876     QStringList relList;
00877     return findAllResources(type, filter, options, relList);
00878 }
00879 
00880 // ####### KDE4: should this be removed, in favor of QDir::canonicalPath()?
00881 // aseigo: QDir::canonicalPath returns QString() if the dir doesn't exist
00882 //         and this method is often used with the expectation for it to work
00883 //         even if the directory doesn't exist. so ... no, we can't drop this
00884 //         yet
00885 QString
00886 KStandardDirs::realPath(const QString &dirname)
00887 {
00888 #ifdef Q_WS_WIN
00889     const QString strRet = realFilePath(dirname);
00890     if ( !strRet.endsWith('/') )
00891         return strRet + '/';
00892     return strRet;
00893 #else
00894     char realpath_buffer[MAXPATHLEN + 1];
00895     memset(realpath_buffer, 0, MAXPATHLEN + 1);
00896 
00897     /* If the path contains symlinks, get the real name */
00898     if (realpath( QFile::encodeName(dirname).constData(), realpath_buffer) != 0) {
00899         // success, use result from realpath
00900         int len = strlen(realpath_buffer);
00901         realpath_buffer[len] = '/';
00902         realpath_buffer[len+1] = 0;
00903         return QFile::decodeName(realpath_buffer);
00904     }
00905 
00906     if ( !dirname.endsWith('/') )
00907         return dirname + '/';
00908     return dirname;
00909 #endif
00910 }
00911 
00912 // ####### KDE4: should this be removed, in favor of QDir::canonicalPath()?
00913 // aseigo: QDir::canonicalPath returns QString() if the dir doesn't exist
00914 //         and this method is often used with the expectation for it to work
00915 //         even if the directory doesn't exist. so ... no, we can't drop this
00916 //         yet
00917 QString
00918 KStandardDirs::realFilePath(const QString &filename)
00919 {
00920 #ifdef Q_WS_WIN
00921     LPCWSTR lpIn = (LPCWSTR)filename.utf16();
00922     QVarLengthArray<WCHAR, MAX_PATH> buf(MAX_PATH);
00923     DWORD len = GetFullPathNameW(lpIn, buf.size(), buf.data(), NULL);
00924     if (len > (DWORD)buf.size()) {
00925         buf.resize(len);
00926         len = GetFullPathNameW(lpIn, buf.size(), buf.data(), NULL);
00927     }
00928     if (len == 0)
00929         return QString();
00930     return QString::fromUtf16((const unsigned short*)buf.data()).replace('\\','/').toLower();
00931 #else
00932     char realpath_buffer[MAXPATHLEN + 1];
00933     memset(realpath_buffer, 0, MAXPATHLEN + 1);
00934 
00935     /* If the path contains symlinks, get the real name */
00936     if (realpath( QFile::encodeName(filename).constData(), realpath_buffer) != 0) {
00937         // success, use result from realpath
00938         return QFile::decodeName(realpath_buffer);
00939     }
00940 
00941     return filename;
00942 #endif
00943 }
00944 
00945 
00946 void KStandardDirs::KStandardDirsPrivate::createSpecialResource(const char *type)
00947 {
00948     char hostname[256];
00949     hostname[0] = 0;
00950     gethostname(hostname, 255);
00951     const QString localkdedir = m_prefixes.first();
00952     QString dir = QString("%1%2-%3").arg(localkdedir).arg(type).arg(hostname);
00953     char link[1024];
00954     link[1023] = 0;
00955     int result = readlink(QFile::encodeName(dir).constData(), link, 1023);
00956     bool relink = (result == -1) && (errno == ENOENT);
00957     if (result > 0)
00958     {
00959         link[result] = 0;
00960         if (!QDir::isRelativePath(link))
00961         {
00962             KDE_struct_stat stat_buf;
00963             int res = KDE::lstat(link, &stat_buf);
00964             if ((res == -1) && (errno == ENOENT))
00965             {
00966                 relink = true;
00967             }
00968             else if ((res == -1) || (!S_ISDIR(stat_buf.st_mode)))
00969             {
00970                 fprintf(stderr, "Error: \"%s\" is not a directory.\n", link);
00971                 relink = true;
00972             }
00973             else if (stat_buf.st_uid != getuid())
00974             {
00975                 fprintf(stderr, "Error: \"%s\" is owned by uid %d instead of uid %d.\n", link, stat_buf.st_uid, getuid());
00976                 relink = true;
00977             }
00978         }
00979     }
00980 #ifdef Q_WS_WIN
00981     if (relink)
00982     {
00983         if (!makeDir(dir, 0700))
00984             fprintf(stderr, "failed to create \"%s\"", qPrintable(dir));
00985         else
00986             result = readlink(QFile::encodeName(dir).data(), link, 1023);
00987     }
00988 #else //UNIX
00989     if (relink)
00990     {
00991         QString srv = findExe(QLatin1String("lnusertemp"), installPath("libexec"));
00992         if (srv.isEmpty())
00993             srv = findExe(QLatin1String("lnusertemp"));
00994         if (!srv.isEmpty())
00995         {
00996             if (system(QFile::encodeName(srv) + ' ' + type) == -1) {
00997                 fprintf(stderr, "Error: unable to launch lnusertemp command" );
00998             }
00999             result = readlink(QFile::encodeName(dir).constData(), link, 1023);
01000         }
01001     }
01002     if (result > 0)
01003     {
01004         link[result] = 0;
01005         if (link[0] == '/')
01006             dir = QFile::decodeName(link);
01007         else
01008             dir = QDir::cleanPath(dir+QFile::decodeName(link));
01009     }
01010 #endif
01011     q->addResourceDir(type, dir+'/', false);
01012 }
01013 
01014 QStringList KStandardDirs::resourceDirs(const char *type) const
01015 {
01016     return d->resourceDirs(type, QString());
01017 }
01018 
01019 QStringList KStandardDirs::KStandardDirsPrivate::resourceDirs(const char* type, const QString& subdirForRestrictions)
01020 {
01021     QMutexLocker lock(&m_cacheMutex);
01022     const bool dataRestrictionActive = m_restrictionsActive
01023                                        && (strcmp(type, "data") == 0)
01024                                        && hasDataRestrictions(subdirForRestrictions);
01025 
01026     QMap<QByteArray, QStringList>::const_iterator dirCacheIt = m_dircache.constFind(type);
01027 
01028     QStringList candidates;
01029 
01030     if (dirCacheIt != m_dircache.constEnd() && !dataRestrictionActive) {
01031         //qDebug() << this << "resourceDirs(" << type << "), in cache already";
01032         candidates = *dirCacheIt;
01033     }
01034     else // filling cache
01035     {
01036         //qDebug() << this << "resourceDirs(" << type << "), not in cache";
01037         if (strcmp(type, "socket") == 0)
01038             createSpecialResource(type);
01039         else if (strcmp(type, "tmp") == 0)
01040             createSpecialResource(type);
01041         else if (strcmp(type, "cache") == 0)
01042             createSpecialResource(type);
01043 
01044         QDir testdir;
01045 
01046         bool restrictionActive = false;
01047         if (m_restrictionsActive) {
01048             if (dataRestrictionActive)
01049                 restrictionActive = true;
01050             if (m_restrictions.value("all", false))
01051                 restrictionActive = true;
01052             else if (m_restrictions.value(type, false))
01053                 restrictionActive = true;
01054         }
01055 
01056         QStringList dirs;
01057         dirs = m_relatives.value(type);
01058         const QString typeInstallPath = installPath(type); // could be empty
01059 // better #ifdef incasesensitive_filesystem
01060 #ifdef Q_WS_WIN
01061         const QString installdir = typeInstallPath.isEmpty() ? QString() : realPath(typeInstallPath).toLower();
01062         const QString installprefix = installPath("kdedir").toLower();
01063 #else
01064         const QString installdir = typeInstallPath.isEmpty() ? QString() : realPath(typeInstallPath);
01065         const QString installprefix = installPath("kdedir");
01066 #endif
01067         if (!dirs.isEmpty())
01068         {
01069             bool local = true;
01070 
01071             for (QStringList::ConstIterator it = dirs.constBegin();
01072                  it != dirs.constEnd(); ++it)
01073             {
01074                 if ( (*it).startsWith('%'))
01075                 {
01076                     // grab the "data" from "%data/apps"
01077                     const int pos = (*it).indexOf('/');
01078                     QString rel = (*it).mid(1, pos - 1);
01079                     QString rest = (*it).mid(pos + 1);
01080                     const QStringList basedirs = resourceDirs(rel.toUtf8().constData(), subdirForRestrictions);
01081                     for (QStringList::ConstIterator it2 = basedirs.begin();
01082                          it2 != basedirs.end(); ++it2)
01083                     {
01084 #ifdef Q_WS_WIN
01085                         const QString path = realPath( *it2 + rest ).toLower();
01086 #else
01087                         const QString path = realPath( *it2 + rest );
01088 #endif
01089                         testdir.setPath(path);
01090                         if ((local || testdir.exists()) && !candidates.contains(path))
01091                             candidates.append(path);
01092                         local = false;
01093                     }
01094                 }
01095             }
01096 
01097             const QStringList *prefixList = 0;
01098             if (strncmp(type, "xdgdata-", 8) == 0)
01099                 prefixList = &(xdgdata_prefixes);
01100             else if (strncmp(type, "xdgconf-", 8) == 0)
01101                 prefixList = &(xdgconf_prefixes);
01102             else
01103                 prefixList = &m_prefixes;
01104 
01105             for (QStringList::ConstIterator pit = prefixList->begin();
01106                  pit != prefixList->end();
01107                  ++pit)
01108             {
01109                 // "exe" never has a custom install path, and the check triggers
01110                 // a false positive due to the libexecdir patch
01111             if((*pit)!=installprefix||installdir.isEmpty()||!strcmp("exe", type))
01112             {
01113                     for (QStringList::ConstIterator it = dirs.constBegin();
01114                          it != dirs.constEnd(); ++it)
01115                     {
01116                         if ( (*it).startsWith('%'))
01117                             continue;
01118 #ifdef Q_WS_WIN
01119                         const QString path = realPath( *pit + *it ).toLower();
01120 #else
01121                         const QString path = realPath( *pit + *it );
01122 #endif
01123                         testdir.setPath(path);
01124                         if (local && restrictionActive)
01125                             continue;
01126                         if ((local || testdir.exists()) && !candidates.contains(path))
01127                             candidates.append(path);
01128                     }
01129                     // special-case "config" (forward porting Chris Cheney's
01130                     // hack) - we want /etc/kde after the local config paths
01131                     // and before the ones in /usr (including kde-profile)
01132                     if (local && !strcmp("config", type))
01133                        candidates.append("/etc/kde/");
01134                     local = false;
01135                 }
01136             else
01137             {
01138                     // we have a custom install path, so use this instead of <installprefix>/<relative dir>
01139                 testdir.setPath(installdir);
01140                     if(testdir.exists() && ! candidates.contains(installdir))
01141                         candidates.append(installdir);
01142             }
01143         }
01144         }
01145 
01146         // make sure we find the path where it's installed
01147         if (!installdir.isEmpty()) {
01148             bool ok = true;
01149             foreach (const QString &s, candidates) {
01150                 if (installdir.startsWith(s)) {
01151                     ok = false;
01152                     break;
01153                 }
01154             }
01155             if (ok)
01156                 candidates.append(installdir);
01157         }
01158 
01159         dirs = m_absolutes.value(type);
01160         if (!dirs.isEmpty())
01161             for (QStringList::ConstIterator it = dirs.constBegin();
01162                  it != dirs.constEnd(); ++it)
01163             {
01164                 testdir.setPath(*it);
01165                 if (testdir.exists()) {
01166 #ifdef Q_WS_WIN
01167                     const QString filename = realPath( *it ).toLower();
01168 #else
01169                     const QString filename = realPath( *it );
01170 #endif
01171                     if (!candidates.contains(filename)) {
01172                         candidates.append(filename);
01173                     }
01174                 }
01175             }
01176 
01177         // Insert result into the cache for next time.
01178         // Exception: data_subdir restrictions are per-subdir, so we can't store such results
01179         if (!dataRestrictionActive) {
01180             //kDebug() << this << "Inserting" << type << candidates << "into dircache";
01181             m_dircache.insert(type, candidates);
01182         }
01183     }
01184 
01185 #if 0
01186     kDebug(180) << "found dirs for resource" << type << ":" << candidates;
01187 #endif
01188 
01189     return candidates;
01190 }
01191 
01192 QStringList KStandardDirs::systemPaths( const QString& pstr )
01193 {
01194     QStringList tokens;
01195     QString p = pstr;
01196 
01197     if( p.isEmpty() )
01198     {
01199         p = QString::fromLocal8Bit( qgetenv( "PATH" ) );
01200     }
01201 
01202     QString delimiters(QChar(KPATH_SEPARATOR));
01203     delimiters += "\b";
01204     tokenize( tokens, p, delimiters );
01205 
01206     QStringList exePaths;
01207 
01208     // split path using : or \b as delimiters
01209     for( int i = 0; i < tokens.count(); i++ )
01210     {
01211         exePaths << KShell::tildeExpand( tokens[ i ] );
01212     }
01213 
01214     return exePaths;
01215 }
01216 
01217 #ifdef Q_WS_MAC
01218 static QString getBundle( const QString& path, bool ignore )
01219 {
01220     kDebug(180) << "getBundle(" << path << ", " << ignore << ") called";
01221     QFileInfo info;
01222     QString bundle = path;
01223     bundle += ".app/Contents/MacOS/" + bundle.section('/', -1);
01224     info.setFile( bundle );
01225     if ( info.exists() && ( ignore || info.isExecutable() )
01226          && ( info.isFile() || info.isSymLink() ) ) {
01227         kDebug(180) << "getBundle(): returning " << bundle;
01228         return bundle;
01229     }
01230     return QString();
01231 }
01232 #endif
01233 
01234 static QString checkExecutable( const QString& path, bool ignoreExecBit )
01235 {
01236 #ifdef Q_WS_MAC
01237     QString bundle = getBundle( path, ignoreExecBit );
01238     if ( !bundle.isEmpty() ) {
01239         //kDebug(180) << "findExe(): returning " << bundle;
01240         return bundle;
01241     }
01242 #endif
01243     QFileInfo info( path );
01244     QFileInfo orig = info;
01245     if( info.exists() && info.isSymLink() )
01246         info = QFileInfo( info.canonicalFilePath() );
01247     if( info.exists() && ( ignoreExecBit || info.isExecutable() ) && info.isFile() ) {
01248         // return absolute path, but without symlinks resolved in order to prevent
01249         // problems with executables that work differently depending on name they are
01250         // run as (for example gunzip)
01251         orig.makeAbsolute();
01252         return orig.filePath();
01253     }
01254     //kDebug(180) << "checkExecutable(): failed, returning empty string";
01255     return QString();
01256 }
01257 
01258 QString KStandardDirs::findExe( const QString& appname,
01259                                 const QString& pstr,
01260                                 SearchOptions options )
01261 {
01262     //kDebug(180) << "findExe(" << appname << ", pstr, " << ignoreExecBit << ") called";
01263 
01264 #ifdef Q_WS_WIN
01265     QString real_appname = appname + QLatin1String(".exe");
01266 #else
01267     QString real_appname = appname;
01268 #endif
01269     QFileInfo info;
01270 
01271     // absolute or relative path?
01272     if (real_appname.contains(QDir::separator()))
01273     {
01274         //kDebug(180) << "findExe(): absolute path given";
01275         QString path = checkExecutable(real_appname, options & IgnoreExecBit);
01276         return path;
01277     }
01278 
01279     //kDebug(180) << "findExe(): relative path given";
01280 
01281     // Look in the default bin and libexec dirs. Maybe we should use the "exe" resource instead?
01282 
01283     QString p = installPath("libexec") + real_appname;
01284     QString result = checkExecutable(p, options & IgnoreExecBit);
01285     if (!result.isEmpty()) {
01286         //kDebug(180) << "findExe(): returning " << result;
01287         return result;
01288     }
01289 
01290     p = installPath("exe") + real_appname;
01291     result = checkExecutable(p, options & IgnoreExecBit);
01292     if (!result.isEmpty()) {
01293         //kDebug(180) << "findExe(): returning " << result;
01294         return result;
01295     }
01296 
01297     //kDebug(180) << "findExe(): checking system paths";
01298     const QStringList exePaths = systemPaths( pstr );
01299     for (QStringList::ConstIterator it = exePaths.begin(); it != exePaths.end(); ++it)
01300     {
01301         p = (*it) + '/';
01302         p += real_appname;
01303 
01304         // Check for executable in this tokenized path
01305         result = checkExecutable(p, options & IgnoreExecBit);
01306         if (!result.isEmpty()) {
01307             //kDebug(180) << "findExe(): returning " << result;
01308             return result;
01309         }
01310     }
01311 
01312     // If we reach here, the executable wasn't found.
01313     // So return empty string.
01314 
01315     //kDebug(180) << "findExe(): failed, nothing matched";
01316     return QString();
01317 }
01318 
01319 int KStandardDirs::findAllExe( QStringList& list, const QString& appname,
01320                                const QString& pstr, SearchOptions options )
01321 {
01322 #ifdef Q_WS_WIN
01323     QString real_appname = appname + ".exe";
01324 #else
01325     QString real_appname = appname;
01326 #endif
01327     QFileInfo info;
01328     QString p;
01329     list.clear();
01330 
01331     const QStringList exePaths = systemPaths( pstr );
01332     for (QStringList::ConstIterator it = exePaths.begin(); it != exePaths.end(); ++it)
01333     {
01334         p = (*it) + '/';
01335         p += real_appname;
01336 
01337 #ifdef Q_WS_MAC
01338         QString bundle = getBundle( p, (options & IgnoreExecBit) );
01339         if ( !bundle.isEmpty() ) {
01340             //kDebug(180) << "findExe(): returning " << bundle;
01341             list.append( bundle );
01342         }
01343 #endif
01344 
01345         info.setFile( p );
01346 
01347         if( info.exists() && ( ( options & IgnoreExecBit ) || info.isExecutable())
01348             && info.isFile() ) {
01349             list.append( p );
01350         }
01351     }
01352 
01353     return list.count();
01354 }
01355 
01356 static inline QString equalizePath(QString &str)
01357 {
01358 #ifdef Q_WS_WIN
01359     // filter pathes through QFileInfo to have always
01360     // the same case for drive letters
01361     QFileInfo f(str);
01362     if (f.isAbsolute())
01363         return f.absoluteFilePath();
01364     else
01365 #endif
01366         return str;
01367 }
01368 
01369 static int tokenize( QStringList& tokens, const QString& str,
01370                      const QString& delim )
01371 {
01372     int len = str.length();
01373     QString token;
01374 
01375     for( int index = 0; index < len; index++)
01376     {
01377         if ( delim.contains( str[ index ] ) )
01378         {
01379             tokens.append( equalizePath(token) );
01380             token.clear();
01381         }
01382         else
01383         {
01384             token += str[ index ];
01385         }
01386     }
01387     if ( !token.isEmpty() )
01388     {
01389         tokens.append( equalizePath(token) );
01390     }
01391 
01392     return tokens.count();
01393 }
01394 
01395 QString KStandardDirs::kde_default(const char *type)
01396 {
01397     return QString('%') + type + '/';
01398 }
01399 
01400 QString KStandardDirs::saveLocation(const char *type,
01401                                     const QString& suffix,
01402                                     bool create) const
01403 {
01404     QMutexLocker lock(&d->m_cacheMutex);
01405     QString path = d->m_savelocations.value(type);
01406     if (path.isEmpty())
01407     {
01408         QStringList dirs = d->m_relatives.value(type);
01409         if (dirs.isEmpty() && (
01410                 (strcmp(type, "socket") == 0) ||
01411                 (strcmp(type, "tmp") == 0) ||
01412                 (strcmp(type, "cache") == 0) ))
01413         {
01414             (void) resourceDirs(type); // Generate socket|tmp|cache resource.
01415             dirs = d->m_relatives.value(type); // Search again.
01416         }
01417         if (!dirs.isEmpty())
01418         {
01419             path = dirs.last();
01420 
01421             if ( path.startsWith('%'))
01422             {
01423                 // grab the "data" from "%data/apps"
01424                 const int pos = path.indexOf('/');
01425                 QString rel = path.mid(1, pos - 1);
01426                 QString rest = path.mid(pos + 1);
01427                 QString basepath = saveLocation(rel.toUtf8().constData());
01428                 path = basepath + rest;
01429             } else
01430 
01431                 // Check for existence of typed directory + suffix
01432                 if (strncmp(type, "xdgdata-", 8) == 0) {
01433                     path = realPath( localxdgdatadir() + path ) ;
01434                 } else if (strncmp(type, "xdgconf-", 8) == 0) {
01435                     path = realPath( localxdgconfdir() + path );
01436                 } else {
01437                     path = realPath( localkdedir() + path );
01438                 }
01439         }
01440         else {
01441             dirs = d->m_absolutes.value(type);
01442             if (dirs.isEmpty()) {
01443                 qFatal("KStandardDirs: The resource type %s is not registered", type);
01444             }
01445             path = realPath(dirs.last());
01446         }
01447 
01448         d->m_savelocations.insert(type, path.endsWith('/') ? path : path + '/');
01449     }
01450     QString fullPath = path + suffix;
01451 
01452     KDE_struct_stat st;
01453     if (KDE::stat(fullPath, &st) != 0 || !(S_ISDIR(st.st_mode))) {
01454         if(!create) {
01455 #ifndef NDEBUG
01456             // Too much noise from kbuildsycoca4 -- it's fine if this happens from KConfig
01457             // when parsing global files without a local equivalent.
01458             //kDebug(180) << QString("save location %1 doesn't exist").arg(fullPath);
01459 #endif
01460             return fullPath;
01461         }
01462         if(!makeDir(fullPath, 0700)) {
01463             return fullPath;
01464         }
01465         d->m_dircache.remove(type);
01466     }
01467     if (!fullPath.endsWith('/'))
01468         fullPath += '/';
01469     return fullPath;
01470 }
01471 
01472 // KDE5: make the method const
01473 QString KStandardDirs::relativeLocation(const char *type, const QString &absPath)
01474 {
01475     QString fullPath = absPath;
01476     int i = absPath.lastIndexOf('/');
01477     if (i != -1) {
01478         fullPath = realFilePath(absPath); // Normalize
01479     }
01480 
01481     const QStringList candidates = resourceDirs(type);
01482 
01483     for (QStringList::ConstIterator it = candidates.begin();
01484          it != candidates.end(); ++it) {
01485         if (fullPath.startsWith(*it)) {
01486             return fullPath.mid((*it).length());
01487         }
01488     }
01489     return absPath;
01490 }
01491 
01492 
01493 bool KStandardDirs::makeDir(const QString& dir, int mode)
01494 {
01495     // we want an absolute path
01496     if (QDir::isRelativePath(dir))
01497         return false;
01498 
01499 #ifdef Q_WS_WIN
01500     return QDir().mkpath(dir);
01501 #else
01502     QString target = dir;
01503     uint len = target.length();
01504 
01505     // append trailing slash if missing
01506     if (dir.at(len - 1) != '/')
01507         target += '/';
01508 
01509     QString base;
01510     uint i = 1;
01511 
01512     while( i < len )
01513     {
01514         KDE_struct_stat st;
01515         int pos = target.indexOf('/', i);
01516         base += target.mid(i - 1, pos - i + 1);
01517         QByteArray baseEncoded = QFile::encodeName(base);
01518         // bail out if we encountered a problem
01519         if (KDE_stat(baseEncoded, &st) != 0)
01520         {
01521             // Directory does not exist....
01522             // Or maybe a dangling symlink ?
01523             if (KDE_lstat(baseEncoded, &st) == 0)
01524                 (void)unlink(baseEncoded); // try removing
01525 
01526             if (KDE_mkdir(baseEncoded, static_cast<mode_t>(mode)) != 0) {
01527                 baseEncoded.prepend( "trying to create local folder " );
01528                 perror(baseEncoded.constData());
01529                 return false; // Couldn't create it :-(
01530             }
01531         }
01532         i = pos + 1;
01533     }
01534     return true;
01535 #endif
01536 }
01537 
01538 static QString readEnvPath(const char *env)
01539 {
01540     QByteArray c_path = qgetenv(env);
01541     if (c_path.isEmpty())
01542         return QString();
01543     return QDir::fromNativeSeparators(QFile::decodeName(c_path));
01544 }
01545 
01546 #ifdef __linux__
01547 static QString executablePrefix()
01548 {
01549     char path_buffer[MAXPATHLEN + 1];
01550     path_buffer[MAXPATHLEN] = 0;
01551     int length = readlink ("/proc/self/exe", path_buffer, MAXPATHLEN);
01552     if (length == -1)
01553         return QString();
01554 
01555     path_buffer[length] = '\0';
01556 
01557     QString path = QFile::decodeName(path_buffer);
01558 
01559     if(path.isEmpty())
01560         return QString();
01561 
01562     int pos = path.lastIndexOf('/'); // Skip filename
01563     if(pos <= 0)
01564         return QString();
01565     pos = path.lastIndexOf('/', pos - 1); // Skip last directory
01566     if(pos <= 0)
01567         return QString();
01568 
01569     return path.left(pos);
01570 }
01571 #endif
01572 
01573 void KStandardDirs::addResourcesFrom_krcdirs()
01574 {
01575     QString localFile = QDir::currentPath() + "/.krcdirs";
01576     if (!QFile::exists(localFile))
01577         return;
01578 
01579     QSettings iniFile(localFile, QSettings::IniFormat);
01580     iniFile.beginGroup("KStandardDirs");
01581     const QStringList resources = iniFile.allKeys();
01582     foreach(const QString &key, resources)
01583     {
01584         QDir path(iniFile.value(key).toString());
01585         if (!path.exists())
01586             continue;
01587 
01588         if(path.makeAbsolute())
01589             addResourceDir(key.toAscii(), path.path(), false);
01590     }
01591 }
01592 
01593 void KStandardDirs::addKDEDefaults()
01594 {
01595     addResourcesFrom_krcdirs();
01596 
01597     QStringList kdedirList;
01598 
01599     // begin KDEDIRS
01600     QString kdedirs = readEnvPath("KDEDIRS");
01601     if (!kdedirs.isEmpty())
01602     {
01603         tokenize(kdedirList, kdedirs, QString(QChar(KPATH_SEPARATOR)));
01604     }
01605     kdedirList.append(installPath("kdedir"));
01606 
01607     QString execPrefix(EXEC_INSTALL_PREFIX);
01608     if (!execPrefix.isEmpty() && !kdedirList.contains(execPrefix))
01609         kdedirList.append(execPrefix);
01610 #ifdef __linux__
01611     const QString linuxExecPrefix = executablePrefix();
01612     if ( !linuxExecPrefix.isEmpty() )
01613         kdedirList.append( linuxExecPrefix );
01614 #endif
01615 
01616     // We treat root differently to prevent a "su" shell messing up the
01617     // file permissions in the user's home directory.
01618     QString localKdeDir = readEnvPath(getuid() ? "KDEHOME" : "KDEROOTHOME");
01619     if (!localKdeDir.isEmpty())
01620     {
01621         if (localKdeDir[localKdeDir.length()-1] != '/')
01622             localKdeDir += '/';
01623     }
01624     else
01625     {
01626 #if defined(Q_WS_MACX)
01627         localKdeDir =  QDir::homePath() + QLatin1String("/Library/Preferences/KDE/");
01628 #elif defined(Q_WS_WIN)
01629         WCHAR wPath[MAX_PATH+1];
01630         if ( SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wPath) == S_OK) {
01631           localKdeDir = QDir::fromNativeSeparators(QString::fromUtf16((const ushort *) wPath)) + QLatin1Char('/') + KDE_DEFAULT_HOME + QLatin1Char('/');
01632         } else {
01633           localKdeDir =  QDir::homePath() + QLatin1Char('/') + KDE_DEFAULT_HOME + QLatin1Char('/');
01634         }
01635 #else
01636         localKdeDir =  QDir::homePath() + QLatin1Char('/') + KDE_DEFAULT_HOME + QLatin1Char('/');
01637 #endif
01638     }
01639 
01640     if (localKdeDir != "-/")
01641     {
01642         localKdeDir = KShell::tildeExpand(localKdeDir);
01643         addPrefix(localKdeDir);
01644     }
01645 
01646 #ifdef Q_WS_MACX
01647     // Adds the "Contents" directory of the current application bundle to
01648     // the search path. This way bundled resources can be found.
01649     QDir bundleDir(mac_app_filename());
01650     if (bundleDir.dirName() == "MacOS") { // just to be sure we're in a bundle
01651         bundleDir.cdUp();
01652         // now dirName should be "Contents". In there we can find our normal
01653         // dir-structure, beginning with "share"
01654         addPrefix(bundleDir.absolutePath());
01655     }
01656 #endif
01657 
01658     QStringList::ConstIterator end(kdedirList.end());
01659     for (QStringList::ConstIterator it = kdedirList.constBegin();
01660          it != kdedirList.constEnd(); ++it)
01661     {
01662         const QString dir = KShell::tildeExpand(*it);
01663         addPrefix(dir);
01664     }
01665     // end KDEDIRS
01666 
01667     // begin XDG_CONFIG_XXX
01668     QStringList xdgdirList;
01669     QString xdgdirs = readEnvPath("XDG_CONFIG_DIRS");
01670     if (!xdgdirs.isEmpty())
01671     {
01672         tokenize(xdgdirList, xdgdirs, QString(QChar(KPATH_SEPARATOR)));
01673     }
01674     else
01675     {
01676         xdgdirList.clear();
01677         xdgdirList.append("/etc/xdg");
01678 #ifdef Q_WS_WIN
01679         xdgdirList.append(installPath("kdedir") + "etc/xdg");
01680 #else
01681         xdgdirList.append(KDESYSCONFDIR "/xdg");
01682 #endif
01683     }
01684 
01685     QString localXdgDir = readEnvPath("XDG_CONFIG_HOME");
01686     if (!localXdgDir.isEmpty())
01687     {
01688         if (localXdgDir[localXdgDir.length()-1] != '/')
01689             localXdgDir += '/';
01690     }
01691     else
01692     {
01693 #ifdef Q_WS_MACX
01694         localXdgDir =  QDir::homePath() + "/Library/Preferences/XDG/";
01695 #else
01696         localXdgDir =  QDir::homePath() + "/.config/";
01697 #endif
01698     }
01699 
01700     localXdgDir = KShell::tildeExpand(localXdgDir);
01701     addXdgConfigPrefix(localXdgDir);
01702 
01703     for (QStringList::ConstIterator it = xdgdirList.constBegin();
01704          it != xdgdirList.constEnd(); ++it)
01705     {
01706         QString dir = KShell::tildeExpand(*it);
01707         addXdgConfigPrefix(dir);
01708     }
01709     // end XDG_CONFIG_XXX
01710 
01711     // begin XDG_DATA_XXX
01712     QStringList kdedirDataDirs;
01713     for (QStringList::ConstIterator it = kdedirList.constBegin();
01714          it != kdedirList.constEnd(); ++it) {
01715         QString dir = *it;
01716         if (!dir.endsWith('/'))
01717             dir += '/';
01718         kdedirDataDirs.append(dir+"share/");
01719     }
01720 
01721     xdgdirs = readEnvPath("XDG_DATA_DIRS");
01722     if (!xdgdirs.isEmpty()) {
01723         tokenize(xdgdirList, xdgdirs, QString(QChar(KPATH_SEPARATOR)));
01724         // Ensure the kdedirDataDirs are in there too,
01725         // otherwise resourceDirs() will add kdedir/share/applications/kde4
01726         // as returned by installPath(), and that's incorrect.
01727         Q_FOREACH(const QString& dir, kdedirDataDirs) {
01728             if (!xdgdirList.contains(dir))
01729                 xdgdirList.append(dir);
01730         }
01731     } else {
01732         xdgdirList = kdedirDataDirs;
01733 #ifndef Q_WS_WIN
01734         xdgdirList.append("/usr/local/share/");
01735         xdgdirList.append("/usr/share/");
01736 #endif
01737     }
01738 
01739     localXdgDir = readEnvPath("XDG_DATA_HOME");
01740     if (!localXdgDir.isEmpty())
01741     {
01742         if (localXdgDir[localXdgDir.length()-1] != '/')
01743             localXdgDir += '/';
01744     }
01745     else
01746     {
01747         localXdgDir = QDir::homePath() + "/.local/share/";
01748     }
01749 
01750     localXdgDir = KShell::tildeExpand(localXdgDir);
01751     addXdgDataPrefix(localXdgDir);
01752 
01753     for (QStringList::ConstIterator it = xdgdirList.constBegin();
01754          it != xdgdirList.constEnd(); ++it)
01755     {
01756         QString dir = KShell::tildeExpand(*it);
01757         addXdgDataPrefix(dir);
01758     }
01759     // end XDG_DATA_XXX
01760 
01761 
01762     addResourceType("lib", 0, "lib" KDELIBSUFF "/");
01763 
01764     uint index = 0;
01765     while (types_indices[index] != -1) {
01766         addResourceType(types_string + types_indices[index], 0, types_string + types_indices[index+1], true);
01767         index+=2;
01768     }
01769     addResourceType("exe", 0, "libexec/kde4", true );
01770 
01771     addResourceDir("home", QDir::homePath(), false);
01772 
01773     addResourceType("autostart", "xdgconf-autostart", "/"); // merge them, start with xdg autostart
01774     addResourceType("autostart", NULL, "share/autostart"); // KDE ones are higher priority
01775 }
01776 
01777 static QStringList lookupProfiles(const QString &mapFile)
01778 {
01779     QStringList profiles;
01780 
01781     if (mapFile.isEmpty() || !QFile::exists(mapFile))
01782     {
01783         profiles << "default";
01784         return profiles;
01785     }
01786 
01787     struct passwd *pw = getpwuid(geteuid());
01788     if (!pw)
01789     {
01790         profiles << "default";
01791         return profiles; // Not good
01792     }
01793 
01794     QByteArray user = pw->pw_name;
01795 
01796     gid_t sup_gids[512];
01797     int sup_gids_nr = getgroups(512, sup_gids);
01798 
01799     KConfig mapCfgFile(mapFile);
01800     KConfigGroup mapCfg(&mapCfgFile, "Users");
01801     if (mapCfg.hasKey(user.constData()))
01802     {
01803         profiles = mapCfg.readEntry(user.constData(), QStringList());
01804         return profiles;
01805     }
01806 
01807     const KConfigGroup generalGrp(&mapCfgFile, "General");
01808     const QStringList groups = generalGrp.readEntry("groups", QStringList());
01809 
01810     const KConfigGroup groupsGrp(&mapCfgFile, "Groups");
01811 
01812     for( QStringList::ConstIterator it = groups.begin();
01813          it != groups.end(); ++it )
01814     {
01815         QByteArray grp = (*it).toUtf8();
01816         // Check if user is in this group
01817         struct group *grp_ent = getgrnam(grp);
01818         if (!grp_ent) continue;
01819         gid_t gid = grp_ent->gr_gid;
01820         if (pw->pw_gid == gid)
01821         {
01822             // User is in this group --> add profiles
01823             profiles += groupsGrp.readEntry(*it, QStringList());
01824         }
01825         else
01826         {
01827             for(int i = 0; i < sup_gids_nr; i++)
01828             {
01829                 if (sup_gids[i] == gid)
01830                 {
01831                     // User is in this group --> add profiles
01832                     profiles += groupsGrp.readEntry(*it, QStringList());
01833                     break;
01834                 }
01835             }
01836         }
01837     }
01838 
01839     if (profiles.isEmpty())
01840         profiles << "default";
01841     return profiles;
01842 }
01843 
01844 extern bool kde_kiosk_admin;
01845 
01846 bool KStandardDirs::addCustomized(KConfig *config)
01847 {
01848     if (!d->m_checkRestrictions) // there are already customized entries
01849         return false; // we just quit and hope they are the right ones
01850 
01851     // save the numbers of config directories. If this changes,
01852     // we will return true to give KConfig a chance to reparse
01853     int configdirs = resourceDirs("config").count();
01854 
01855     if (true)
01856     {
01857         // reading the prefixes in
01858         QString group = QLatin1String("Directories");
01859         KConfigGroup cg(config, group);
01860 
01861         QString kioskAdmin = cg.readEntry("kioskAdmin");
01862         if (!kioskAdmin.isEmpty() && !kde_kiosk_admin)
01863         {
01864             int i = kioskAdmin.indexOf(':');
01865             QString user = kioskAdmin.left(i);
01866             QString host = kioskAdmin.mid(i+1);
01867 
01868             KUser thisUser;
01869             char hostname[ 256 ];
01870             hostname[ 0 ] = '\0';
01871             if (!gethostname( hostname, 255 ))
01872                 hostname[sizeof(hostname)-1] = '\0';
01873 
01874             if ((user == thisUser.loginName()) &&
01875                 (host.isEmpty() || (host == hostname)))
01876             {
01877                 kde_kiosk_admin = true;
01878             }
01879         }
01880 
01881         bool readProfiles = true;
01882 
01883         if (kde_kiosk_admin && !qgetenv("KDE_KIOSK_NO_PROFILES").isEmpty())
01884             readProfiles = false;
01885 
01886         QString userMapFile = cg.readEntry("userProfileMapFile");
01887         QString profileDirsPrefix = cg.readEntry("profileDirsPrefix");
01888         if (!profileDirsPrefix.isEmpty() && !profileDirsPrefix.endsWith('/'))
01889             profileDirsPrefix.append("/");
01890 
01891         QStringList profiles;
01892         if (readProfiles)
01893             profiles = lookupProfiles(userMapFile);
01894         QString profile;
01895 
01896         bool priority = false;
01897         while(true)
01898         {
01899             KConfigGroup cg(config, group);
01900             const QStringList list = cg.readEntry("prefixes", QStringList());
01901             for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
01902             {
01903                 addPrefix(*it, priority);
01904                 addXdgConfigPrefix(*it+"/etc/xdg", priority);
01905                 addXdgDataPrefix(*it+"/share", priority);
01906             }
01907             // If there are no prefixes defined, check if there is a directory
01908             // for this profile under <profileDirsPrefix>
01909             if (list.isEmpty() && !profile.isEmpty() && !profileDirsPrefix.isEmpty())
01910             {
01911                 QString dir = profileDirsPrefix + profile;
01912                 addPrefix(dir, priority);
01913                 addXdgConfigPrefix(dir+"/etc/xdg", priority);
01914                 addXdgDataPrefix(dir+"/share", priority);
01915             }
01916 
01917             // iterating over all entries in the group Directories
01918             // to find entries that start with dir_$type
01919             const QMap<QString, QString> entries = config->entryMap(group);
01920             for (QMap<QString, QString>::ConstIterator it2 = entries.begin();
01921                  it2 != entries.end(); ++it2)
01922             {
01923                 const QString key = it2.key();
01924                 if (key.startsWith("dir_")) {
01925                     // generate directory list, there may be more than 1.
01926                     QStringList dirs = (*it2).split(',');
01927                     QStringList::Iterator sIt(dirs.begin());
01928                     QString resType = key.mid(4);
01929                     for (; sIt != dirs.end(); ++sIt)
01930                     {
01931                         addResourceDir(resType.toLatin1(), *sIt, priority);
01932                     }
01933                 }
01934             }
01935             if (profiles.isEmpty())
01936                 break;
01937             profile = profiles.back();
01938             group = QString::fromLatin1("Directories-%1").arg(profile);
01939             profiles.pop_back();
01940             priority = true;
01941         }
01942     }
01943 
01944     // Process KIOSK restrictions.
01945     if (!kde_kiosk_admin || qgetenv("KDE_KIOSK_NO_RESTRICTIONS").isEmpty())
01946     {
01947         KConfigGroup cg(config, "KDE Resource Restrictions");
01948         const QMap<QString, QString> entries = cg.entryMap();
01949         for (QMap<QString, QString>::ConstIterator it2 = entries.begin();
01950              it2 != entries.end(); ++it2)
01951         {
01952             const QString key = it2.key();
01953             if (!cg.readEntry(key, true))
01954             {
01955                 d->m_restrictionsActive = true;
01956                 d->m_restrictions.insert(key.toLatin1(), true);
01957                 d->m_dircache.remove(key.toLatin1());
01958             }
01959         }
01960     }
01961 
01962     // check if the number of config dirs changed
01963     bool configDirsChanged = (resourceDirs("config").count() != configdirs);
01964     // If the config dirs changed, we check kiosk restrictions again.
01965     d->m_checkRestrictions = configDirsChanged;
01966     // return true if the number of config dirs changed: reparse config file
01967     return configDirsChanged;
01968 }
01969 
01970 QString KStandardDirs::localkdedir() const
01971 {
01972     // Return the prefix to use for saving
01973     return d->m_prefixes.first();
01974 }
01975 
01976 QString KStandardDirs::localxdgdatadir() const
01977 {
01978     // Return the prefix to use for saving
01979     return d->xdgdata_prefixes.first();
01980 }
01981 
01982 QString KStandardDirs::localxdgconfdir() const
01983 {
01984     // Return the prefix to use for saving
01985     return d->xdgconf_prefixes.first();
01986 }
01987 
01988 
01989 // just to make code more readable without macros
01990 QString KStandardDirs::locate( const char *type,
01991                                const QString& filename, const KComponentData &cData)
01992 {
01993     return cData.dirs()->findResource(type, filename);
01994 }
01995 
01996 QString KStandardDirs::locateLocal( const char *type,
01997                                     const QString& filename, const KComponentData &cData)
01998 {
01999     return locateLocal(type, filename, true, cData);
02000 }
02001 
02002 QString KStandardDirs::locateLocal( const char *type,
02003                                     const QString& filename, bool createDir,
02004                                     const KComponentData &cData)
02005 {
02006     // try to find slashes. If there are some, we have to
02007     // create the subdir first
02008     int slash = filename.lastIndexOf('/')+1;
02009     if (!slash) { // only one filename
02010         return cData.dirs()->saveLocation(type, QString(), createDir) + filename;
02011     }
02012 
02013     // split path from filename
02014     QString dir = filename.left(slash);
02015     QString file = filename.mid(slash);
02016     return cData.dirs()->saveLocation(type, dir, createDir) + file;
02017 }
02018 
02019 bool KStandardDirs::checkAccess(const QString& pathname, int mode)
02020 {
02021     int accessOK = KDE::access( pathname, mode );
02022     if ( accessOK == 0 )
02023         return true;  // OK, I can really access the file
02024 
02025     // else
02026     // if we want to write the file would be created. Check, if the
02027     // user may write to the directory to create the file.
02028     if ( (mode & W_OK) == 0 )
02029         return false;   // Check for write access is not part of mode => bail out
02030 
02031 
02032     if (!KDE::access( pathname, F_OK)) // if it already exists
02033         return false;
02034 
02035     //strip the filename (everything until '/' from the end
02036     QString dirName(pathname);
02037     int pos = dirName.lastIndexOf('/');
02038     if ( pos == -1 )
02039         return false;   // No path in argument. This is evil, we won't allow this
02040     else if ( pos == 0 ) // don't turn e.g. /root into an empty string
02041         pos = 1;
02042 
02043     dirName.truncate(pos); // strip everything starting from the last '/'
02044 
02045     accessOK = KDE::access( dirName, W_OK );
02046     // -?- Can I write to the accessed diretory
02047     if ( accessOK == 0 )
02048         return true;  // Yes
02049     else
02050         return false; // No
02051 }
02052 

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal