From 4b72c3f4347bfb8493e877d26f172f2e18c69a83 Mon Sep 17 00:00:00 2001 From: David Faure Date: Fri, 21 Oct 2011 20:57:31 +0200 Subject: [PATCH] Add QStandardPaths::findExecutable. Change-Id: If30a83622e2ac5af48e47a38b8f70fce73044d74 Reviewed-by: Thiago Macieira (Intel) --- src/corelib/io/qstandardpaths.cpp | 87 +++++++++++++++++++++++++++++++++ src/corelib/io/qstandardpaths.h | 4 +- src/corelib/kernel/qcoreapplication.cpp | 15 +----- 3 files changed, 92 insertions(+), 14 deletions(-) diff --git a/src/corelib/io/qstandardpaths.cpp b/src/corelib/io/qstandardpaths.cpp index 58a6ab1..86d0f74 100644 --- a/src/corelib/io/qstandardpaths.cpp +++ b/src/corelib/io/qstandardpaths.cpp @@ -176,6 +176,93 @@ QStringList QStandardPaths::locateAll(StandardLocation type, const QString &file return result; } +#ifdef Q_OS_WIN +static QStringList executableExtensions() +{ + QStringList ret = QString::fromLocal8Bit(qgetenv("PATHEXT")).split(QLatin1Char(';')); + if (!ret.contains(QLatin1String(".exe"), Qt::CaseInsensitive)) { + // If %PATHEXT% does not contain .exe, it is either empty, malformed, or distorted in ways that we cannot support, anyway. + ret.clear(); + ret << QLatin1String(".exe") + << QLatin1String(".com") + << QLatin1String(".bat") + << QLatin1String(".cmd"); + } + return ret; +} +#endif + +static QString checkExecutable(const QString &path) +{ + const QFileInfo info(path); + if (info.isBundle()) + return info.bundleName(); + if (info.isFile() && info.isExecutable()) + return QDir::cleanPath(path); + return QString(); +} + +/*! + Finds the executable named \a executableName in the paths specified by \a paths, + or the system paths if \a paths is empty. + + On most operating systems the system path is determined by the PATH environment variable. + + The directories where to search for the executable can be set in the \a paths argument. + To search in both your own paths and the system paths, call findExecutable twice, once with + \a paths set and once with \a paths empty. + + Symlinks are not resolved, in order to preserve behavior for the case of executables + whose behavior depends on the name they are invoked with. + + \note On Windows, the usual executable extensions (from the PATHEXT environment variable) + are automatically appended, so that for instance findExecutable("foo") will find foo.exe + or foo.bat if present. + + Returns the absolute file path to the executable, or an empty string if not found. + */ +QString QStandardPaths::findExecutable(const QString &executableName, const QStringList &paths) +{ + if (QFileInfo(executableName).isAbsolute()) + return checkExecutable(executableName); + + QStringList searchPaths = paths; + if (paths.isEmpty()) { + QByteArray pEnv = qgetenv("PATH"); +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + const QLatin1Char pathSep(';'); +#else + const QLatin1Char pathSep(':'); +#endif + searchPaths = QString::fromLocal8Bit(pEnv.constData()).split(pathSep, QString::SkipEmptyParts); + } + + QDir currentDir = QDir::current(); + QString absPath; +#ifdef Q_OS_WIN + static QStringList executable_extensions = executableExtensions(); +#endif + + for (QStringList::const_iterator p = searchPaths.constBegin(); p != searchPaths.constEnd(); ++p) { + const QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + executableName); +#ifdef Q_OS_WIN + const QString extension = QLatin1Char('.') + QFileInfo(executableName).suffix(); + if (!executable_extensions.contains(extension, Qt::CaseInsensitive)) { + foreach (const QString &extension, executable_extensions) { + absPath = checkExecutable(candidate + extension.toLower()); + if (!absPath.isEmpty()) + break; + } + } +#endif + absPath = checkExecutable(candidate); + if (!absPath.isEmpty()) { + break; + } + } + return absPath; +} + /*! \fn QString QStandardPaths::displayName(StandardLocation type) diff --git a/src/corelib/io/qstandardpaths.h b/src/corelib/io/qstandardpaths.h index 1688e16..d0293ab 100644 --- a/src/corelib/io/qstandardpaths.h +++ b/src/corelib/io/qstandardpaths.h @@ -42,7 +42,7 @@ #ifndef QSTANDARDPATHS_H #define QSTANDARDPATHS_H -#include +#include QT_BEGIN_HEADER @@ -87,6 +87,8 @@ public: static QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options = LocateFile); static QString displayName(StandardLocation type); + static QString findExecutable(const QString &executableName, const QStringList &paths = QStringList()); + private: // prevent construction QStandardPaths(); diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 9acf1f2..24d0cee 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -2019,19 +2020,7 @@ QString QCoreApplication::applicationFilePath() Otherwise, the file path has to be determined using the PATH environment variable. */ - QByteArray pEnv = qgetenv("PATH"); - QDir currentDir = QDir::current(); - QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1Char(':')); - for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) { - if ((*p).isEmpty()) - continue; - QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + argv0); - QFileInfo candidate_fi(candidate); - if (candidate_fi.exists() && !candidate_fi.isDir()) { - absPath = candidate; - break; - } - } + absPath = QStandardPaths::findExecutable(argv0); } absPath = QDir::cleanPath(absPath); -- 2.7.4