diff options
author | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-11-06 15:56:40 -0600 |
---|---|---|
committer | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-11-06 15:56:40 -0600 |
commit | e16866e072f94410321d70daedbcb855ea878cac (patch) | |
tree | ee3f52eabde7da1a0e6ca845fb9c2813cf1558cf /tdecore/kstandarddirs.cpp | |
parent | a58c20c1a7593631a1b50213c805507ebc16adaf (diff) | |
download | tdelibs-e16866e072f94410321d70daedbcb855ea878cac.tar.gz tdelibs-e16866e072f94410321d70daedbcb855ea878cac.zip |
Actually move the kde files that were renamed in the last commit
Diffstat (limited to 'tdecore/kstandarddirs.cpp')
-rw-r--r-- | tdecore/kstandarddirs.cpp | 1705 |
1 files changed, 1705 insertions, 0 deletions
diff --git a/tdecore/kstandarddirs.cpp b/tdecore/kstandarddirs.cpp new file mode 100644 index 000000000..87a509e13 --- /dev/null +++ b/tdecore/kstandarddirs.cpp @@ -0,0 +1,1705 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Sirtaj Singh Kang <taj@kde.org> + Copyright (C) 1999 Stephan Kulow <coolo@kde.org> + Copyright (C) 1999 Waldo Bastian <bastian@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* + * Author: Stephan Kulow <coolo@kde.org> and Sirtaj Singh Kang <taj@kde.org> + * Version: $Id$ + * Generated: Thu Mar 5 16:05:28 EST 1998 + */ + +#include "config.h" + +#include <stdlib.h> +#include <assert.h> +#include <errno.h> +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#include <sys/param.h> +#include <sys/types.h> +#include <dirent.h> +#include <pwd.h> +#include <grp.h> + +#include <tqregexp.h> +#include <tqasciidict.h> +#include <tqdict.h> +#include <tqdir.h> +#include <tqfileinfo.h> +#include <tqstring.h> +#include <tqstringlist.h> + +#include "kstandarddirs.h" +#include "kconfig.h" +#include "kdebug.h" +#include "kinstance.h" +#include "kshell.h" +#include "ksimpleconfig.h" +#include "kuser.h" +#include "kstaticdeleter.h" +#include <kde_file.h> + +template class TQDict<TQStringList>; + +class KStandardDirs::KStandardDirsPrivate +{ +public: + KStandardDirsPrivate() + : restrictionsActive(false), + dataRestrictionActive(false), + checkRestrictions(true) + { } + + bool restrictionsActive; + bool dataRestrictionActive; + bool checkRestrictions; + TQAsciiDict<bool> restrictions; + TQStringList xdgdata_prefixes; + TQStringList xdgconf_prefixes; +}; + +// Singleton, with data shared by all kstandarddirs instances. +// Used in static methods like findExe() +class KStandardDirsSingleton +{ +public: + TQString defaultprefix; + TQString defaultbindir; + static KStandardDirsSingleton* self(); +private: + static KStandardDirsSingleton* s_self; +}; +static KStaticDeleter<KStandardDirsSingleton> kstds_sd; +KStandardDirsSingleton* KStandardDirsSingleton::s_self = 0; +KStandardDirsSingleton* KStandardDirsSingleton::self() { + if ( !s_self ) + kstds_sd.setObject( s_self, new KStandardDirsSingleton ); + return s_self; +} + +static const char* const types[] = {"html", "html-bundle", "icon", "apps", "sound", + "data", "locale", "locale-bundle", "services", "mime", + "servicetypes", "config", "exe", + "wallpaper", "lib", "pixmap", "templates", + "module", "qtplugins", + "xdgdata-apps", "xdgdata-dirs", "xdgconf-menu", + "xdgdata-icon", "xdgdata-pixmap", "xdgconf-autostart", + "kcfg", "emoticons", 0 }; + +static int tokenize( TQStringList& token, const TQString& str, + const TQString& delim ); + +KStandardDirs::KStandardDirs( ) : addedCustoms(false) +{ + d = new KStandardDirsPrivate; + dircache.setAutoDelete(true); + relatives.setAutoDelete(true); + absolutes.setAutoDelete(true); + savelocations.setAutoDelete(true); + addKDEDefaults(); +} + +KStandardDirs::~KStandardDirs() +{ + delete d; +} + +bool KStandardDirs::isRestrictedResource(const char *type, const TQString& relPath) const +{ + if (!d || !d->restrictionsActive) + return false; + + if (d->restrictions[type]) + return true; + + if (strcmp(type, "data")==0) + { + applyDataRestrictions(relPath); + if (d->dataRestrictionActive) + { + d->dataRestrictionActive = false; + return true; + } + } + return false; +} + +void KStandardDirs::applyDataRestrictions(const TQString &relPath) const +{ + TQString key; + int i = relPath.find(QChar('/')); + if (i != -1) + key = "data_"+relPath.left(i); + else + key = "data_"+relPath; + + if (d && d->restrictions[key.latin1()]) + d->dataRestrictionActive = true; +} + + +TQStringList KStandardDirs::allTypes() const +{ + TQStringList list; + for (int i = 0; types[i] != 0; ++i) + list.append(TQString::tqfromLatin1(types[i])); + return list; +} + +static void priorityAdd(TQStringList &prefixes, const TQString& dir, bool priority) +{ + if (priority && !prefixes.isEmpty()) + { + // Add in front but behind $KDEHOME + TQStringList::iterator it = prefixes.begin(); + it++; + prefixes.insert(it, 1, dir); + } + else + { + prefixes.append(dir); + } +} + +void KStandardDirs::addPrefix( const TQString& _dir ) +{ + addPrefix(_dir, false); +} + +void KStandardDirs::addPrefix( const TQString& _dir, bool priority ) +{ + if (_dir.isEmpty()) + return; + + TQString dir = _dir; + if (dir.tqat(dir.length() - 1) != QChar('/')) + dir += QChar('/'); + + if (!prefixes.contains(dir)) { + priorityAdd(prefixes, dir, priority); + dircache.clear(); + } +} + +void KStandardDirs::addXdgConfigPrefix( const TQString& _dir ) +{ + addXdgConfigPrefix(_dir, false); +} + +void KStandardDirs::addXdgConfigPrefix( const TQString& _dir, bool priority ) +{ + if (_dir.isEmpty()) + return; + + TQString dir = _dir; + if (dir.tqat(dir.length() - 1) != QChar('/')) + dir += QChar('/'); + + if (!d->xdgconf_prefixes.contains(dir)) { + priorityAdd(d->xdgconf_prefixes, dir, priority); + dircache.clear(); + } +} + +void KStandardDirs::addXdgDataPrefix( const TQString& _dir ) +{ + addXdgDataPrefix(_dir, false); +} + +void KStandardDirs::addXdgDataPrefix( const TQString& _dir, bool priority ) +{ + if (_dir.isEmpty()) + return; + + TQString dir = _dir; + if (dir.tqat(dir.length() - 1) != QChar('/')) + dir += QChar('/'); + + if (!d->xdgdata_prefixes.contains(dir)) { + priorityAdd(d->xdgdata_prefixes, dir, priority); + dircache.clear(); + } +} + +TQString KStandardDirs::kfsstnd_prefixes() +{ + return prefixes.join(TQChar(KPATH_SEPARATOR)); +} + +TQString KStandardDirs::kfsstnd_xdg_conf_prefixes() +{ + return d->xdgconf_prefixes.join(TQChar(KPATH_SEPARATOR)); +} + +TQString KStandardDirs::kfsstnd_xdg_data_prefixes() +{ + return d->xdgdata_prefixes.join(TQChar(KPATH_SEPARATOR)); +} + +bool KStandardDirs::addResourceType( const char *type, + const TQString& relativename ) +{ + return addResourceType(type, relativename, true); +} +bool KStandardDirs::addResourceType( const char *type, + const TQString& relativename, + bool priority ) +{ + if (relativename.isEmpty()) + return false; + + TQStringList *rels = relatives.find(type); + if (!rels) { + rels = new TQStringList(); + relatives.insert(type, rels); + } + TQString copy = relativename; + if (copy.tqat(copy.length() - 1) != QChar('/')) + copy += QChar('/'); + if (!rels->contains(copy)) { + if (priority) + rels->prepend(copy); + else + rels->append(copy); + dircache.remove(type); // clean the cache + return true; + } + return false; +} + +bool KStandardDirs::addResourceDir( const char *type, + const TQString& absdir) +{ + // KDE4: change priority to bring in line with addResourceType + return addResourceDir(type, absdir, false); +} + +bool KStandardDirs::addResourceDir( const char *type, + const TQString& absdir, + bool priority) +{ + TQStringList *paths = absolutes.find(type); + if (!paths) { + paths = new TQStringList(); + absolutes.insert(type, paths); + } + TQString copy = absdir; + if (copy.tqat(copy.length() - 1) != QChar('/')) + copy += QChar('/'); + + if (!paths->contains(copy)) { + if (priority) + paths->prepend(copy); + else + paths->append(copy); + dircache.remove(type); // clean the cache + return true; + } + return false; +} + +TQString KStandardDirs::findResource( const char *type, + const TQString& filename ) const +{ + if (!TQDir::isRelativePath(filename)) + return filename; // absolute dirs are absolute dirs, right? :-/ + +#if 0 +kdDebug() << "Find resource: " << type << endl; +for (TQStringList::ConstIterator pit = prefixes.begin(); + pit != prefixes.end(); + pit++) +{ + kdDebug() << "Prefix: " << *pit << endl; +} +#endif + + TQString dir = findResourceDir(type, filename); + if (dir.isEmpty()) + return dir; + else return dir + filename; +} + +static TQ_UINT32 updateHash(const TQString &file, TQ_UINT32 hash) +{ + TQCString cFile = TQFile::encodeName(file); + KDE_struct_stat buff; + if ((access(cFile, R_OK) == 0) && + (KDE_stat( cFile, &buff ) == 0) && + (S_ISREG( buff.st_mode ))) + { + hash = hash + (TQ_UINT32) buff.st_ctime; + } + return hash; +} + +TQ_UINT32 KStandardDirs::calcResourceHash( const char *type, + const TQString& filename, bool deep) const +{ + TQ_UINT32 hash = 0; + + if (!TQDir::isRelativePath(filename)) + { + // absolute dirs are absolute dirs, right? :-/ + return updateHash(filename, hash); + } + if (d && d->restrictionsActive && (strcmp(type, "data")==0)) + applyDataRestrictions(filename); + TQStringList candidates = resourceDirs(type); + TQString fullPath; + + for (TQStringList::ConstIterator it = candidates.begin(); + it != candidates.end(); ++it) + { + hash = updateHash(*it + filename, hash); + if (!deep && hash) + return hash; + } + return hash; +} + + +TQStringList KStandardDirs::findDirs( const char *type, + const TQString& reldir ) const +{ + TQDir testdir; + TQStringList list; + if (!TQDir::isRelativePath(reldir)) + { + testdir.setPath(reldir); + if (testdir.exists()) + { + if (reldir.endsWith("/")) + list.append(reldir); + else + list.append(reldir+QChar('/')); + } + return list; + } + + checkConfig(); + + if (d && d->restrictionsActive && (strcmp(type, "data")==0)) + applyDataRestrictions(reldir); + TQStringList candidates = resourceDirs(type); + + for (TQStringList::ConstIterator it = candidates.begin(); + it != candidates.end(); ++it) { + testdir.setPath(*it + reldir); + if (testdir.exists()) + list.append(testdir.absPath() + QChar('/')); + } + + return list; +} + +TQString KStandardDirs::findResourceDir( const char *type, + const TQString& filename) const +{ +#ifndef NDEBUG + if (filename.isEmpty()) { + kdWarning() << "filename for type " << type << " in KStandardDirs::findResourceDir is not supposed to be empty!!" << endl; + return TQString::null; + } +#endif + + if (d && d->restrictionsActive && (strcmp(type, "data")==0)) + applyDataRestrictions(filename); + TQStringList candidates = resourceDirs(type); + TQString fullPath; + + for (TQStringList::ConstIterator it = candidates.begin(); + it != candidates.end(); ++it) { + if (exists(*it + filename)) { +#ifdef Q_WS_WIN //this ensures we're using installed .la files + if ((*it).isEmpty() && filename.right(3)==".la") { +#ifndef NDEBUG + kdDebug() << "KStandardDirs::findResourceDir() found .la in cwd: skipping. (fname=" << filename << ")" << endl; +#endif + continue; + } +#endif //Q_WS_WIN + return *it; + } + } + +#ifndef NDEBUG + if(false && strcmp(type, "locale")) + kdDebug() << "KStdDirs::findResDir(): can't find \"" << filename << "\" in type \"" << type << "\"." << endl; +#endif + + return TQString::null; +} + +bool KStandardDirs::exists(const TQString &fullPath) +{ + KDE_struct_stat buff; + if (access(TQFile::encodeName(fullPath), R_OK) == 0 && KDE_stat( TQFile::encodeName(fullPath), &buff ) == 0) + if (fullPath.tqat(fullPath.length() - 1) != QChar('/')) { + if (S_ISREG( buff.st_mode )) + return true; + } else + if (S_ISDIR( buff.st_mode )) + return true; + return false; +} + +static void lookupDirectory(const TQString& path, const TQString &relPart, + const TQRegExp ®exp, + TQStringList& list, + TQStringList& relList, + bool recursive, bool unique) +{ + TQString pattern = regexp.pattern(); + if (recursive || pattern.contains('?') || pattern.contains('*')) + { + if (path.isEmpty()) //for sanity + return; + // We look for a set of files. + DIR *dp = opendir( TQFile::encodeName(path)); + if (!dp) + return; + +#ifdef Q_WS_WIN + assert(path.tqat(path.length() - 1) == QChar('/') || path.tqat(path.length() - 1) == QChar('\\')); +#else + assert(path.tqat(path.length() - 1) == QChar('/')); +#endif + + struct dirent *ep; + KDE_struct_stat buff; + + TQString _dot("."); + TQString _dotdot(".."); + + while( ( ep = readdir( dp ) ) != 0L ) + { + TQString fn( TQFile::decodeName(ep->d_name)); + if (fn == _dot || fn == _dotdot || TQChar(fn.tqat(fn.length() - 1)).latin1() == TQChar('~').latin1()) + continue; + + if (!recursive && !regexp.exactMatch(fn)) + continue; // No match + + TQString pathfn = path + fn; + if ( KDE_stat( TQFile::encodeName(pathfn), &buff ) != 0 ) { + kdDebug() << "Error stat'ing " << pathfn << " : " << perror << endl; + continue; // Couldn't stat (e.g. no read permissions) + } + if ( recursive ) { + if ( S_ISDIR( buff.st_mode )) { + lookupDirectory(pathfn + QChar('/'), relPart + fn + QChar('/'), regexp, list, relList, recursive, unique); + } + if (!regexp.exactMatch(fn)) + continue; // No match + } + if ( S_ISREG( buff.st_mode)) + { + if (!unique || !relList.contains(relPart + fn)) + { + list.append( pathfn ); + relList.append( relPart + fn ); + } + } + } + closedir( dp ); + } + else + { + // We look for a single file. + TQString fn = pattern; + TQString pathfn = path + fn; + KDE_struct_stat buff; + if ( KDE_stat( TQFile::encodeName(pathfn), &buff ) != 0 ) + return; // File not found + if ( S_ISREG( buff.st_mode)) + { + if (!unique || !relList.contains(relPart + fn)) + { + list.append( pathfn ); + relList.append( relPart + fn ); + } + } + } +} + +static void lookupPrefix(const TQString& prefix, const TQString& relpath, + const TQString& relPart, + const TQRegExp ®exp, + TQStringList& list, + TQStringList& relList, + bool recursive, bool unique) +{ + if (relpath.isEmpty()) { + lookupDirectory(prefix, relPart, regexp, list, + relList, recursive, unique); + return; + } + TQString path; + TQString rest; + + if (relpath.length()) + { + int slash = relpath.find(QChar('/')); + if (slash < 0) + rest = relpath.left(relpath.length() - 1); + else { + path = relpath.left(slash); + rest = relpath.mid(slash + 1); + } + } + + if (prefix.isEmpty()) //for sanity + return; +#ifdef Q_WS_WIN + assert(prefix.tqat(prefix.length() - 1) == QChar('/') || prefix.tqat(prefix.length() - 1) == QChar('\\')); +#else + assert(prefix.tqat(prefix.length() - 1) == QChar('/')); +#endif + KDE_struct_stat buff; + + if (path.contains('*') || path.contains('?')) { + + TQRegExp pathExp(path, true, true); + DIR *dp = opendir( TQFile::encodeName(prefix) ); + if (!dp) { + return; + } + + struct dirent *ep; + + TQString _dot("."); + TQString _dotdot(".."); + + while( ( ep = readdir( dp ) ) != 0L ) + { + TQString fn( TQFile::decodeName(ep->d_name)); + if (fn == _dot || fn == _dotdot || fn.tqat(fn.length() - 1) == QChar('~')) + continue; + + if ( !pathExp.exactMatch(fn) ) + continue; // No match + TQString rfn = relPart+fn; + fn = prefix + fn; + if ( KDE_stat( TQFile::encodeName(fn), &buff ) != 0 ) { + kdDebug() << "Error statting " << fn << " : " << perror << endl; + continue; // Couldn't stat (e.g. no permissions) + } + if ( S_ISDIR( buff.st_mode )) + lookupPrefix(fn + QChar('/'), rest, rfn + QChar('/'), regexp, list, relList, recursive, unique); + } + + closedir( dp ); + } else { + // Don't stat, if the dir doesn't exist we will find out + // when we try to open it. + lookupPrefix(prefix + path + QChar('/'), rest, + relPart + path + QChar('/'), regexp, list, + relList, recursive, unique); + } +} + +TQStringList +KStandardDirs::findAllResources( const char *type, + const TQString& filter, + bool recursive, + bool unique, + TQStringList &relList) const +{ + TQStringList list; + TQString filterPath; + TQString filterFile; + + if (filter.length()) + { + int slash = filter.findRev('/'); + if (slash < 0) + filterFile = filter; + else { + filterPath = filter.left(slash + 1); + filterFile = filter.mid(slash + 1); + } + } + + checkConfig(); + + TQStringList candidates; + if (!TQDir::isRelativePath(filter)) // absolute path + { +#ifdef Q_OS_WIN + candidates << filterPath.left(3); //e.g. "C:\" + filterPath = filterPath.mid(3); +#else + candidates << "/"; + filterPath = filterPath.mid(1); +#endif + } + else + { + if (d && d->restrictionsActive && (strcmp(type, "data")==0)) + applyDataRestrictions(filter); + candidates = resourceDirs(type); + } + if (filterFile.isEmpty()) + filterFile = "*"; + + TQRegExp regExp(filterFile, true, true); + + for (TQStringList::ConstIterator it = candidates.begin(); + it != candidates.end(); ++it) + { + lookupPrefix(*it, filterPath, "", regExp, list, + relList, recursive, unique); + } + + return list; +} + +TQStringList +KStandardDirs::findAllResources( const char *type, + const TQString& filter, + bool recursive, + bool unique) const +{ + TQStringList relList; + return findAllResources(type, filter, recursive, unique, relList); +} + +TQString +KStandardDirs::realPath(const TQString &dirname) +{ + char realpath_buffer[MAXPATHLEN + 1]; + memset(realpath_buffer, 0, MAXPATHLEN + 1); + + /* If the path contains symlinks, get the real name */ + if (realpath( TQFile::encodeName(dirname).data(), realpath_buffer) != 0) { + // success, use result from realpath + int len = strlen(realpath_buffer); + realpath_buffer[len] = TQChar('/'); + realpath_buffer[len+1] = 0; + return TQFile::decodeName(realpath_buffer); + } + + return dirname; +} + +TQString +KStandardDirs::realFilePath(const TQString &filename) +{ + char realpath_buffer[MAXPATHLEN + 1]; + memset(realpath_buffer, 0, MAXPATHLEN + 1); + + /* If the path contains symlinks, get the real name */ + if (realpath( TQFile::encodeName(filename).data(), realpath_buffer) != 0) { + // success, use result from realpath + return TQFile::decodeName(realpath_buffer); + } + + return filename; +} + +void KStandardDirs::createSpecialResource(const char *type) +{ + char hostname[256]; + hostname[0] = 0; + if( getenv("XAUTHLOCALHOSTNAME")) + strlcpy(hostname, getenv("XAUTHLOCALHOSTNAME"), 255 ); + else + gethostname(hostname, 255); + TQString dir = TQString("%1%2-%3").arg(localkdedir()).arg(type).arg(hostname); + char link[1024]; + link[1023] = 0; + int result = readlink(TQFile::encodeName(dir).data(), link, 1023); + bool relink = (result == -1) && (errno == ENOENT); + if (result > 0) + { + link[result] = 0; + if (!TQDir::isRelativePath(link)) + { + KDE_struct_stat stat_buf; + int res = KDE_lstat(link, &stat_buf); + if ((res == -1) && (errno == ENOENT)) + { + relink = true; + } + else if ((res == -1) || (!S_ISDIR(stat_buf.st_mode))) + { + fprintf(stderr, "Error: \"%s\" is not a directory.\n", link); + relink = true; + } + else if (stat_buf.st_uid != getuid()) + { + fprintf(stderr, "Error: \"%s\" is owned by uid %d instead of uid %d.\n", link, stat_buf.st_uid, getuid()); + relink = true; + } + } + } +#ifdef Q_WS_WIN + if (relink) + { + if (!makeDir(dir, 0700)) + fprintf(stderr, "failed to create \"%s\"", dir.latin1()); + else + result = readlink(TQFile::encodeName(dir).data(), link, 1023); + } +#else //UNIX + if (relink) + { + TQString srv = findExe(TQString::tqfromLatin1("lnusertemp"), kfsstnd_defaultbindir()); + if (srv.isEmpty()) + srv = findExe(TQString::tqfromLatin1("lnusertemp")); + if (!srv.isEmpty()) + { + system(TQFile::encodeName(srv)+" "+type); + result = readlink(TQFile::encodeName(dir).data(), link, 1023); + } + } + if (result > 0) + { + link[result] = 0; + if (link[0] == TQChar('/').latin1()) + dir = TQFile::decodeName(link); + else + dir = TQDir::cleanDirPath(dir+TQFile::decodeName(link)); + } +#endif + addResourceDir(type, dir+QChar('/')); +} + +TQStringList KStandardDirs::resourceDirs(const char *type) const +{ + TQStringList *candidates = dircache.find(type); + + if (!candidates) { // filling cache + if (strcmp(type, "socket") == 0) + const_cast<KStandardDirs *>(this)->createSpecialResource(type); + else if (strcmp(type, "tmp") == 0) + const_cast<KStandardDirs *>(this)->createSpecialResource(type); + else if (strcmp(type, "cache") == 0) + const_cast<KStandardDirs *>(this)->createSpecialResource(type); + + TQDir testdir; + + candidates = new TQStringList(); + TQStringList *dirs; + + bool restrictionActive = false; + if (d && d->restrictionsActive) + { + if (d->dataRestrictionActive) + restrictionActive = true; + else if (d->restrictions["all"]) + restrictionActive = true; + else if (d->restrictions[type]) + restrictionActive = true; + d->dataRestrictionActive = false; // Reset + } + + dirs = relatives.find(type); + if (dirs) + { + bool local = true; + const TQStringList *prefixList = 0; + if (strncmp(type, "xdgdata-", 8) == 0) + prefixList = &(d->xdgdata_prefixes); + else if (strncmp(type, "xdgconf-", 8) == 0) + prefixList = &(d->xdgconf_prefixes); + else + prefixList = &prefixes; + + for (TQStringList::ConstIterator pit = prefixList->begin(); + pit != prefixList->end(); + ++pit) + { + for (TQStringList::ConstIterator it = dirs->begin(); + it != dirs->end(); ++it) { + TQString path = realPath(*pit + *it); + testdir.setPath(path); + if (local && restrictionActive) + continue; + if ((local || testdir.exists()) && !candidates->contains(path)) + candidates->append(path); + } + // UGLY HACK - Chris CHeney + if (local && (!strcmp("config", type))) + candidates->append("/etc/trinity/"); + // + local = false; + } + } + dirs = absolutes.find(type); + if (dirs) + for (TQStringList::ConstIterator it = dirs->begin(); + it != dirs->end(); ++it) + { + testdir.setPath(*it); + if (testdir.exists()) + { + TQString filename = realPath(*it); + if (!candidates->contains(filename)) + candidates->append(filename); + } + } + dircache.insert(type, candidates); + } + +#if 0 + kdDebug() << "found dirs for resource " << type << ":" << endl; + for (TQStringList::ConstIterator pit = candidates->begin(); + pit != candidates->end(); + pit++) + { + fprintf(stderr, "%s\n", (*pit).latin1()); + } +#endif + + + return *candidates; +} + +TQStringList KStandardDirs::systemPaths( const TQString& pstr ) +{ + TQStringList tokens; + TQString p = pstr; + + if( p.isNull() ) + { + p = getenv( "PATH" ); + } + + TQString delimiters(TQChar(KPATH_SEPARATOR)); + delimiters += "\b"; + tokenize( tokens, p, delimiters ); + + TQStringList exePaths; + + // split path using : or \b as delimiters + for( unsigned i = 0; i < tokens.count(); i++ ) + { + p = tokens[ i ]; + + if ( p[ 0 ] == QChar('~') ) + { + int len = p.find( QChar('/') ); + if ( len == -1 ) + len = p.length(); + if ( len == 1 ) + { + p.replace( 0, 1, TQDir::homeDirPath() ); + } + else + { + TQString user = p.mid( 1, len - 1 ); + struct passwd *dir = getpwnam( user.local8Bit().data() ); + if ( dir && strlen( dir->pw_dir ) ) + p.replace( 0, len, TQString::fromLocal8Bit( dir->pw_dir ) ); + } + } + + exePaths << p; + } + + return exePaths; +} + + +TQString KStandardDirs::findExe( const TQString& appname, + const TQString& pstr, bool ignore) +{ +#ifdef Q_WS_WIN + TQString real_appname = appname + ".exe"; +#else + TQString real_appname = appname; +#endif + TQFileInfo info; + + // absolute or relative path given + if (real_appname.find(TQDir::separator()) >= 0) + { + info.setFile( real_appname ); + if( info.exists() && ( ignore || info.isExecutable() ) + && info.isFile() ) { + return info.absFilePath(); + } + return TQString::null; + } + + TQString p = TQString("%1/%2").arg(kfsstnd_defaultbindir()).arg(real_appname); + info.setFile( p ); + if( info.exists() && ( ignore || info.isExecutable() ) + && ( info.isFile() || info.isSymLink() ) ) { + return p; + } + + TQStringList exePaths = systemPaths( pstr ); + for (TQStringList::ConstIterator it = exePaths.begin(); it != exePaths.end(); ++it) + { + p = (*it) + "/"; + p += real_appname; + + // Check for executable in this tokenized path + info.setFile( p ); + + if( info.exists() && ( ignore || info.isExecutable() ) + && ( info.isFile() || info.isSymLink() ) ) { + return p; + } + } + + // If we reach here, the executable wasn't found. + // So return empty string. + + return TQString::null; +} + +int KStandardDirs::findAllExe( TQStringList& list, const TQString& appname, + const TQString& pstr, bool ignore ) +{ +#ifdef Q_WS_WIN + TQString real_appname = appname + ".exe"; +#else + TQString real_appname = appname; +#endif + TQFileInfo info; + TQString p; + list.clear(); + + TQStringList exePaths = systemPaths( pstr ); + for (TQStringList::ConstIterator it = exePaths.begin(); it != exePaths.end(); ++it) + { + p = (*it) + "/"; + p += real_appname; + + info.setFile( p ); + + if( info.exists() && (ignore || info.isExecutable()) + && info.isFile() ) { + list.append( p ); + } + } + + return list.count(); +} + +static int tokenize( TQStringList& tokens, const TQString& str, + const TQString& delim ) +{ + int len = str.length(); + TQString token = ""; + + for( int index = 0; index < len; index++) + { + if ( delim.find( str[ index ] ) >= 0 ) + { + tokens.append( token ); + token = ""; + } + else + { + token += str[ index ]; + } + } + if ( token.length() > 0 ) + { + tokens.append( token ); + } + + return tokens.count(); +} + +TQString KStandardDirs::kde_default(const char *type) { + if (!strcmp(type, "data")) + return "share/apps/"; + if (!strcmp(type, "html-bundle")) + return "share/doc-bundle/HTML/"; + if (!strcmp(type, "html")) + return "share/doc/kde/HTML/"; + if (!strcmp(type, "icon")) + return "share/icons/"; + if (!strcmp(type, "config")) + return "share/config/"; + if (!strcmp(type, "pixmap")) + return "share/pixmaps/"; + if (!strcmp(type, "apps")) + return "share/applnk/"; + if (!strcmp(type, "sound")) + return "share/sounds/"; + if (!strcmp(type, "locale-bundle")) + return "share/locale-bundle/"; + if (!strcmp(type, "locale")) + return "share/locale/"; + if (!strcmp(type, "services")) + return "share/services/"; + if (!strcmp(type, "servicetypes")) + return "share/servicetypes/"; + if (!strcmp(type, "mime")) + return "share/mimelnk/"; + if (!strcmp(type, "cgi")) + return "lib/cgi-bin/"; + if (!strcmp(type, "wallpaper")) + return "share/wallpapers/"; + if (!strcmp(type, "templates")) + return "share/templates/"; + if (!strcmp(type, "exe")) + return "bin/"; + if (!strcmp(type, "lib")) + return "lib" KDELIBSUFF "/"; + if (!strcmp(type, "module")) + return "lib" KDELIBSUFF "/trinity/"; + if (!strcmp(type, "qtplugins")) + return "lib" KDELIBSUFF "/trinity/plugins"; + if (!strcmp(type, "xdgdata-apps")) + return "applications/"; + if (!strcmp(type, "xdgdata-icon")) + return "icons/"; + if (!strcmp(type, "xdgdata-pixmap")) + return "pixmaps/"; + if (!strcmp(type, "xdgdata-dirs")) + return "desktop-directories/"; + if (!strcmp(type, "xdgconf-menu")) + return "menus/"; + if (!strcmp(type, "xdgconf-autostart")) + return "autostart/"; + if (!strcmp(type, "kcfg")) + return "share/config.kcfg"; + if (!strcmp(type, "emoticons")) + return "share/emoticons"; + + + qFatal("unknown resource type %s", type); + return TQString::null; +} + +TQString KStandardDirs::saveLocation(const char *type, + const TQString& suffix, + bool create) const +{ + checkConfig(); + + TQString *pPath = savelocations.find(type); + if (!pPath) + { + TQStringList *dirs = relatives.find(type); + if (!dirs && ( + (strcmp(type, "socket") == 0) || + (strcmp(type, "tmp") == 0) || + (strcmp(type, "cache") == 0) )) + { + (void) resourceDirs(type); // Generate socket|tmp|cache resource. + dirs = relatives.find(type); // Search again. + } + if (dirs) + { + // Check for existence of typed directory + suffix + if (strncmp(type, "xdgdata-", 8) == 0) + pPath = new TQString(realPath(localxdgdatadir() + dirs->last())); + else if (strncmp(type, "xdgconf-", 8) == 0) + pPath = new TQString(realPath(localxdgconfdir() + dirs->last())); + else + pPath = new TQString(realPath(localkdedir() + dirs->last())); + } + else { + dirs = absolutes.find(type); + if (!dirs) + qFatal("KStandardDirs: The resource type %s is not registered", type); + pPath = new TQString(realPath(dirs->last())); + } + + savelocations.insert(type, pPath); + } + TQString fullPath = *pPath + (pPath->endsWith("/") ? "" : "/") + suffix; + + KDE_struct_stat st; + if (KDE_stat(TQFile::encodeName(fullPath), &st) != 0 || !(S_ISDIR(st.st_mode))) { + if(!create) { +#ifndef NDEBUG + kdDebug() << TQString("save location %1 doesn't exist").tqarg(fullPath) << endl; +#endif + return fullPath; + } + if(!makeDir(fullPath, 0700)) { + return fullPath; + } + dircache.remove(type); + } + if (!fullPath.endsWith("/")) + fullPath += "/"; + return fullPath; +} + +TQString KStandardDirs::relativeLocation(const char *type, const TQString &absPath) +{ + TQString fullPath = absPath; + int i = absPath.findRev('/'); + if (i != -1) + { + fullPath = realPath(absPath.left(i+1))+absPath.mid(i+1); // Normalize + } + + TQStringList candidates = resourceDirs(type); + + for (TQStringList::ConstIterator it = candidates.begin(); + it != candidates.end(); ++it) + if (fullPath.startsWith(*it)) + { + return fullPath.mid((*it).length()); + } + + return absPath; +} + + +bool KStandardDirs::makeDir(const TQString& dir, int mode) +{ + // we want an absolute path + if (TQDir::isRelativePath(dir)) + return false; + + TQString target = dir; + uint len = target.length(); + + // append trailing slash if missing + if (dir.tqat(len - 1) != QChar('/')) + target += QChar('/'); + + TQString base(""); + uint i = 1; + + while( i < len ) + { + KDE_struct_stat st; + int pos = target.find(QChar('/'), i); + base += target.mid(i - 1, pos - i + 1); + TQCString baseEncoded = TQFile::encodeName(base); + // bail out if we encountered a problem + if (KDE_stat(baseEncoded, &st) != 0) + { + // Directory does not exist.... + // Or maybe a dangling symlink ? + if (KDE_lstat(baseEncoded, &st) == 0) + (void)unlink(baseEncoded); // try removing + + if ( KDE_mkdir(baseEncoded, (mode_t) mode) != 0) { + baseEncoded.prepend( "trying to create local folder " ); + perror(baseEncoded.data()); + return false; // Couldn't create it :-( + } + } + i = pos + 1; + } + return true; +} + +static TQString readEnvPath(const char *env) +{ + TQCString c_path = getenv(env); + if (c_path.isEmpty()) + return TQString::null; +#ifdef Q_OS_WIN + //win32 paths are case-insensitive: avoid duplicates on various dir lists + return TQFile::decodeName(c_path).lower(); +#else + return TQFile::decodeName(c_path); +#endif +} + +#ifdef __linux__ +static TQString executablePrefix() +{ + char path_buffer[MAXPATHLEN + 1]; + path_buffer[MAXPATHLEN] = 0; + int length = readlink ("/proc/self/exe", path_buffer, MAXPATHLEN); + if (length == -1) + return TQString::null; + + path_buffer[length] = TQChar('\0'); + + TQString path = TQFile::decodeName(path_buffer); + + if(path.isEmpty()) + return TQString::null; + + int pos = path.findRev('/'); // Skip filename + if(pos <= 0) + return TQString::null; + pos = path.findRev(TQChar('/'), pos - 1); // Skip last directory + if(pos <= 0) + return TQString::null; + + return path.left(pos); +} +#endif + +TQString KStandardDirs::kfsstnd_defaultprefix() +{ + KStandardDirsSingleton* s = KStandardDirsSingleton::self(); + if (!s->defaultprefix.isEmpty()) + return s->defaultprefix; +#ifdef Q_WS_WIN + s->defaultprefix = readEnvPath("KDEDIR"); + if (s->defaultprefix.isEmpty()) { + s->defaultprefix = TQFile::decodeName("c:\\kde"); + //TODO: find other location (the Registry?) + } +#else //UNIX + s->defaultprefix = KDEDIR; +#endif + if (s->defaultprefix.isEmpty()) + kdWarning() << "KStandardDirs::kfsstnd_defaultprefix(): default KDE prefix not found!" << endl; + return s->defaultprefix; +} + +TQString KStandardDirs::kfsstnd_defaultbindir() +{ + KStandardDirsSingleton* s = KStandardDirsSingleton::self(); + if (!s->defaultbindir.isEmpty()) + return s->defaultbindir; +#ifdef Q_WS_WIN + s->defaultbindir = kfsstnd_defaultprefix() + TQString::tqfromLatin1("/bin"); +#else //UNIX + s->defaultbindir = __KDE_BINDIR; + if (s->defaultbindir.isEmpty()) + s->defaultbindir = kfsstnd_defaultprefix() + TQString::tqfromLatin1("/bin"); +#endif + if (s->defaultbindir.isEmpty()) + kdWarning() << "KStandardDirs::kfsstnd_defaultbindir(): default binary KDE dir not found!" << endl; + return s->defaultbindir; +} + +void KStandardDirs::addKDEDefaults() +{ + TQStringList kdedirList; + + // begin KDEDIRS + TQString kdedirs = readEnvPath("KDEDIRS"); + if (!kdedirs.isEmpty()) + { + tokenize(kdedirList, kdedirs, TQChar(KPATH_SEPARATOR)); + } + else + { + TQString kdedir = readEnvPath("KDEDIR"); + if (!kdedir.isEmpty()) + { + kdedir = KShell::tildeExpand(kdedir); + kdedirList.append(kdedir); + } + } + +#ifndef Q_OS_WIN //no default KDEDIR on win32 defined + kdedirList.append(KDEDIR); +#endif + +#ifdef __KDE_EXECPREFIX + TQString execPrefix(__KDE_EXECPREFIX); + if (execPrefix!="NONE") + kdedirList.append(execPrefix); +#endif +#ifdef __linux__ + const TQString linuxExecPrefix = executablePrefix(); + if ( !linuxExecPrefix.isEmpty() ) + kdedirList.append( linuxExecPrefix ); +#endif + + // We treat root differently to prevent a "su" shell messing up the + // file permissions in the user's home directory. + TQString localKdeDir; + if (getuid() == 0) { + localKdeDir = readEnvPath("KDEROOTHOME"); + if (localKdeDir.isEmpty() == true) + localKdeDir = readEnvPath("KDEHOME"); + } + else { + localKdeDir = readEnvPath("KDEHOME"); + } + if (!localKdeDir.isEmpty()) + { + if (localKdeDir[localKdeDir.length()-1] != QChar('/')) + localKdeDir += QChar('/'); + } + else + { + localKdeDir = TQDir::homeDirPath() + "/.trinity/"; + } + + if (localKdeDir != QString("-/")) + { + localKdeDir = KShell::tildeExpand(localKdeDir); + addPrefix(localKdeDir); + } + + TQStringList::ConstIterator end(kdedirList.end()); + for (TQStringList::ConstIterator it = kdedirList.begin(); + it != end; ++it) + { + TQString dir = KShell::tildeExpand(*it); + addPrefix(dir); + } + // end KDEDIRS + + // begin XDG_CONFIG_XXX + TQStringList xdgdirList; + TQString xdgdirs = readEnvPath("XDG_CONFIG_DIRS"); + if (!xdgdirs.isEmpty()) + { + tokenize(xdgdirList, xdgdirs, TQChar(KPATH_SEPARATOR)); + } + else + { + xdgdirList.clear(); + xdgdirList.append("/etc/xdg"); +#ifdef Q_WS_WIN + xdgdirList.append(kfsstnd_defaultprefix() + "/etc/xdg"); +#else + xdgdirList.append(KDESYSCONFDIR "/xdg"); +#endif + } + + TQString localXdgDir = readEnvPath("XDG_CONFIG_HOME"); + if (!localXdgDir.isEmpty()) + { + if (localXdgDir[localXdgDir.length()-1] != QChar('/')) + localXdgDir += QChar('/'); + } + else + { + localXdgDir = TQDir::homeDirPath() + "/.config/"; + } + + localXdgDir = KShell::tildeExpand(localXdgDir); + addXdgConfigPrefix(localXdgDir); + + for (TQStringList::ConstIterator it = xdgdirList.begin(); + it != xdgdirList.end(); ++it) + { + TQString dir = KShell::tildeExpand(*it); + addXdgConfigPrefix(dir); + } + // end XDG_CONFIG_XXX + + // begin XDG_DATA_XXX + xdgdirs = readEnvPath("XDG_DATA_DIRS"); + if (!xdgdirs.isEmpty()) + { + tokenize(xdgdirList, xdgdirs, TQChar(KPATH_SEPARATOR)); + } + else + { + xdgdirList.clear(); + for (TQStringList::ConstIterator it = kdedirList.begin(); + it != kdedirList.end(); ++it) + { + TQString dir = *it; + if (dir[dir.length()-1] != QChar('/')) + dir += QChar('/'); + xdgdirList.append(dir+"share/"); + } + + xdgdirList.append("/usr/local/share/"); + xdgdirList.append("/usr/share/"); + } + + localXdgDir = readEnvPath("XDG_DATA_HOME"); + if (!localXdgDir.isEmpty()) + { + if (localXdgDir[localXdgDir.length()-1] != QChar('/')) + localXdgDir += QChar('/'); + } + else + { + localXdgDir = TQDir::homeDirPath() + "/.local/share/"; + } + + localXdgDir = KShell::tildeExpand(localXdgDir); + addXdgDataPrefix(localXdgDir); + + for (TQStringList::ConstIterator it = xdgdirList.begin(); + it != xdgdirList.end(); ++it) + { + TQString dir = KShell::tildeExpand(*it); + addXdgDataPrefix(dir); + } + // end XDG_DATA_XXX + + + uint index = 0; + while (types[index] != 0) { + addResourceType(types[index], kde_default(types[index])); + index++; + } + + addResourceDir("home", TQDir::homeDirPath()); + + addResourceDir("locale", "/usr/share/locale-langpack/", true); +} + +void KStandardDirs::checkConfig() const +{ + if (!addedCustoms && KGlobal::_instance && KGlobal::_instance->_config) + const_cast<KStandardDirs*>(this)->addCustomized(KGlobal::_instance->_config); +} + +static TQStringList lookupProfiles(const TQString &mapFile) +{ + TQStringList profiles; + + if (mapFile.isEmpty() || !TQFile::exists(mapFile)) + { + profiles << "default"; + return profiles; + } + + struct passwd *pw = getpwuid(geteuid()); + if (!pw) + { + profiles << "default"; + return profiles; // Not good + } + + TQCString user = pw->pw_name; + + gid_t sup_gids[512]; + int sup_gids_nr = getgroups(512, sup_gids); + + KSimpleConfig mapCfg(mapFile, true); + mapCfg.setGroup("Users"); + if (mapCfg.hasKey(user.data())) + { + profiles = mapCfg.readListEntry(user.data()); + return profiles; + } + + mapCfg.setGroup("General"); + TQStringList groups = mapCfg.readListEntry("groups"); + + mapCfg.setGroup("Groups"); + + for( TQStringList::ConstIterator it = groups.begin(); + it != groups.end(); ++it ) + { + TQCString grp = (*it).utf8(); + // Check if user is in this group + struct group *grp_ent = getgrnam(grp); + if (!grp_ent) continue; + gid_t gid = grp_ent->gr_gid; + if (pw->pw_gid == gid) + { + // User is in this group --> add profiles + profiles += mapCfg.readListEntry(*it); + } + else + { + for(int i = 0; i < sup_gids_nr; i++) + { + if (sup_gids[i] == gid) + { + // User is in this group --> add profiles + profiles += mapCfg.readListEntry(*it); + break; + } + } + } + } + + if (profiles.isEmpty()) + profiles << "default"; + return profiles; +} + +extern bool kde_kiosk_admin; + +bool KStandardDirs::addCustomized(KConfig *config) +{ + if (addedCustoms && !d->checkRestrictions) // there are already customized entries + return false; // we just quit and hope they are the right ones + + // save the numbers of config directories. If this changes, + // we will return true to give KConfig a chance to reparse + uint configdirs = resourceDirs("config").count(); + + // Remember original group + TQString oldGroup = config->group(); + + if (!addedCustoms) + { + // We only add custom entries once + addedCustoms = true; + + // reading the prefixes in + TQString group = TQString::tqfromLatin1("Directories"); + config->setGroup(group); + + TQString kioskAdmin = config->readEntry("kioskAdmin"); + if (!kioskAdmin.isEmpty() && !kde_kiosk_admin) + { + int i = kioskAdmin.find(':'); + TQString user = kioskAdmin.left(i); + TQString host = kioskAdmin.mid(i+1); + + KUser thisUser; + char hostname[ 256 ]; + hostname[ 0 ] = TQChar('\0'); + if (!gethostname( hostname, 255 )) + hostname[sizeof(hostname)-1] = TQChar('\0'); + + if ((user == thisUser.loginName()) && + (host.isEmpty() || (host == hostname))) + { + kde_kiosk_admin = true; + } + } + + bool readProfiles = true; + + if (kde_kiosk_admin && !TQCString(getenv("KDE_KIOSK_NO_PROFILES")).isEmpty()) + readProfiles = false; + + TQString userMapFile = config->readEntry("userProfileMapFile"); + TQString profileDirsPrefix = config->readEntry("profileDirsPrefix"); + if (!profileDirsPrefix.isEmpty() && !profileDirsPrefix.endsWith("/")) + profileDirsPrefix.append('/'); + + TQStringList profiles; + if (readProfiles) + profiles = lookupProfiles(userMapFile); + TQString profile; + + bool priority = false; + while(true) + { + config->setGroup(group); + TQStringList list = config->readListEntry("prefixes"); + for (TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + addPrefix(*it, priority); + addXdgConfigPrefix(*it+"/etc/xdg", priority); + addXdgDataPrefix(*it+"/share", priority); + } + // If there are no prefixes defined, check if there is a directory + // for this profile under <profileDirsPrefix> + if (list.isEmpty() && !profile.isEmpty() && !profileDirsPrefix.isEmpty()) + { + TQString dir = profileDirsPrefix + profile; + addPrefix(dir, priority); + addXdgConfigPrefix(dir+"/etc/xdg", priority); + addXdgDataPrefix(dir+"/share", priority); + } + + // iterating over all entries in the group Directories + // to find entries that start with dir_$type + TQMap<TQString, TQString> entries = config->entryMap(group); + for (TQMap<TQString, TQString>::ConstIterator it2 = entries.begin(); + it2 != entries.end(); it2++) + { + TQString key = it2.key(); + if (key.startsWith("dir_")) { + // generate directory list, there may be more than 1. + TQStringList dirs = TQStringList::split(',', *it2); + TQStringList::Iterator sIt(dirs.begin()); + TQString resType = key.mid(4, key.length()); + for (; sIt != dirs.end(); ++sIt) + { + addResourceDir(resType.latin1(), *sIt, priority); + } + } + } + if (profiles.isEmpty()) + break; + profile = profiles.back(); + group = TQString::tqfromLatin1("Directories-%1").arg(profile); + profiles.pop_back(); + priority = true; + } + } + + // Process KIOSK restrictions. + if (!kde_kiosk_admin || TQCString(getenv("KDE_KIOSK_NO_RESTRICTIONS")).isEmpty()) + { + config->setGroup("KDE Resource Restrictions"); + TQMap<TQString, TQString> entries = config->entryMap("KDE Resource Restrictions"); + for (TQMap<TQString, TQString>::ConstIterator it2 = entries.begin(); + it2 != entries.end(); it2++) + { + TQString key = it2.key(); + if (!config->readBoolEntry(key, true)) + { + d->restrictionsActive = true; + d->restrictions.insert(key.latin1(), &d->restrictionsActive); // Anything will do + dircache.remove(key.latin1()); + } + } + } + + config->setGroup(oldGroup); + + // check if the number of config dirs changed + bool configDirsChanged = (resourceDirs("config").count() != configdirs); + // If the config dirs changed, we check kiosk restrictions again. + d->checkRestrictions = configDirsChanged; + // return true if the number of config dirs changed: reparse config file + return configDirsChanged; +} + +TQString KStandardDirs::localkdedir() const +{ + // Return the prefix to use for saving + return prefixes.first(); +} + +TQString KStandardDirs::localxdgdatadir() const +{ + // Return the prefix to use for saving + return d->xdgdata_prefixes.first(); +} + +TQString KStandardDirs::localxdgconfdir() const +{ + // Return the prefix to use for saving + return d->xdgconf_prefixes.first(); +} + + +// just to make code more readable without macros +TQString locate( const char *type, + const TQString& filename, const KInstance* inst ) +{ + return inst->dirs()->findResource(type, filename); +} + +TQString locateLocal( const char *type, + const TQString& filename, const KInstance* inst ) +{ + return locateLocal(type, filename, true, inst); +} + +TQString locateLocal( const char *type, + const TQString& filename, bool createDir, const KInstance* inst ) +{ + // try to find slashes. If there are some, we have to + // create the subdir first + int slash = filename.findRev('/')+1; + if (!slash) // only one filename + return inst->dirs()->saveLocation(type, TQString::null, createDir) + filename; + + // split path from filename + TQString dir = filename.left(slash); + TQString file = filename.mid(slash); + return inst->dirs()->saveLocation(type, dir, createDir) + file; +} |