Add QStandardPaths::findExecutable.
authorDavid Faure <faure@kde.org>
Fri, 21 Oct 2011 18:57:31 +0000 (20:57 +0200)
committerQt by Nokia <qt-info@nokia.com>
Sat, 22 Oct 2011 22:56:51 +0000 (00:56 +0200)
Change-Id: If30a83622e2ac5af48e47a38b8f70fce73044d74
Reviewed-by: Thiago Macieira (Intel) <thiago.macieira@intel.com>
src/corelib/io/qstandardpaths.cpp
src/corelib/io/qstandardpaths.h
src/corelib/kernel/qcoreapplication.cpp

index 58a6ab1..86d0f74 100644 (file)
@@ -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)
 
index 1688e16..d0293ab 100644 (file)
@@ -42,7 +42,7 @@
 #ifndef QSTANDARDPATHS_H
 #define QSTANDARDPATHS_H
 
-#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
 
 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();
index 9acf1f2..24d0cee 100644 (file)
@@ -53,6 +53,7 @@
 #include <qfileinfo.h>
 #include <qhash.h>
 #include <private/qprocess_p.h>
+#include <qstandardpaths.h>
 #include <qtextcodec.h>
 #include <qthread.h>
 #include <qthreadpool.h>
@@ -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);