diff options
author | Oswald Buddenhagen <oswald.buddenhagen@nokia.com> | 2010-10-19 11:14:03 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@nokia.com> | 2010-11-17 13:19:07 +0100 |
commit | 1e362b0f8b0dfd712337df35cd26c5dc98dfc294 (patch) | |
tree | dbb61c65fc1c3e84b507e03c2b3f62a36acd6488 /src/libs/utils | |
parent | 531c70f05bfc8355f856f2af41be533fb13b85e6 (diff) | |
download | qt-creator-1e362b0f8b0dfd712337df35cd26c5dc98dfc294.tar.gz |
overhaul process argument handling
get away from argument stringlists. instead, use native shell command
lines which support quoting/splitting, environment variable expansion
and redirections with well-understood semantics.
Task-number: QTCREATORBUG-542
Task-number: QTCREATORBUG-1564
Diffstat (limited to 'src/libs/utils')
-rw-r--r-- | src/libs/utils/abstractprocess.h | 13 | ||||
-rw-r--r-- | src/libs/utils/abstractprocess_win.cpp | 78 | ||||
-rw-r--r-- | src/libs/utils/consoleprocess.h | 2 | ||||
-rw-r--r-- | src/libs/utils/consoleprocess_unix.cpp | 44 | ||||
-rw-r--r-- | src/libs/utils/consoleprocess_win.cpp | 20 | ||||
-rw-r--r-- | src/libs/utils/environment.cpp | 56 | ||||
-rw-r--r-- | src/libs/utils/environment.h | 3 |
7 files changed, 107 insertions, 109 deletions
diff --git a/src/libs/utils/abstractprocess.h b/src/libs/utils/abstractprocess.h index 28a8a59e06..49564e4fd0 100644 --- a/src/libs/utils/abstractprocess.h +++ b/src/libs/utils/abstractprocess.h @@ -32,6 +32,8 @@ #include "utils_global.h" +#include "environment.h" + #include <QtCore/QStringList> namespace Utils { @@ -45,10 +47,10 @@ public: QString workingDirectory() const { return m_workingDir; } void setWorkingDirectory(const QString &dir) { m_workingDir = dir; } - QStringList environment() const { return m_environment; } - void setEnvironment(const QStringList &env) { m_environment = env; } + void setEnvironment(const Environment &env) { m_environment = env; } + Environment environment() const { return m_environment; } - virtual bool start(const QString &program, const QStringList &args) = 0; + virtual bool start(const QString &program, const QString &args) = 0; virtual void stop() = 0; virtual bool isRunning() const = 0; @@ -63,14 +65,15 @@ public: static QStringList fixWinEnvironment(const QStringList &env); // Quote a Windows command line correctly for the "CreateProcess" API static QString createWinCommandline(const QString &program, const QStringList &args); + static QString createWinCommandline(const QString &program, const QString &args); // Create a bytearray suitable to be passed on as environment // to the "CreateProcess" API (0-terminated UTF 16 strings). static QByteArray createWinEnvironment(const QStringList &env); #endif -private: +protected: QString m_workingDir; - QStringList m_environment; + Environment m_environment; }; } //namespace Utils diff --git a/src/libs/utils/abstractprocess_win.cpp b/src/libs/utils/abstractprocess_win.cpp index be99c70bf0..8c63b5e403 100644 --- a/src/libs/utils/abstractprocess_win.cpp +++ b/src/libs/utils/abstractprocess_win.cpp @@ -51,49 +51,57 @@ QStringList AbstractProcess::fixWinEnvironment(const QStringList &env) return envStrings; } -QString AbstractProcess::createWinCommandline(const QString &program, const QStringList &args) +static QString quoteWinCommand(const QString &program) { const QChar doubleQuote = QLatin1Char('"'); - const QChar blank = QLatin1Char(' '); - const QChar backSlash = QLatin1Char('\\'); + // add the programm as the first arg ... it works better QString programName = program; - if (!programName.startsWith(doubleQuote) && !programName.endsWith(doubleQuote) && programName.contains(blank)) { - programName.insert(0, doubleQuote); + programName.replace(QLatin1Char('/'), QLatin1Char('\\')); + if (!programName.startsWith(doubleQuote) && !programName.endsWith(doubleQuote) + && programName.contains(QLatin1Char(' '))) { + programName.prepend(doubleQuote); programName.append(doubleQuote); } - // add the prgram as the first arrg ... it works better - programName.replace(QLatin1Char('/'), backSlash); - QString cmdLine = programName; - if (args.empty()) - return cmdLine; + return programName; +} + +static QString quoteWinArgument(const QString &arg) +{ + if (!arg.length()) + return QString::fromLatin1("\"\""); + + QString ret(arg); + // Quotes are escaped and their preceding backslashes are doubled. + ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\"")); + if (ret.contains(QRegExp(QLatin1String("\\s")))) { + // The argument must not end with a \ since this would be interpreted + // as escaping the quote -- rather put the \ behind the quote: e.g. + // rather use "foo"\ than "foo\" + ret.replace(QRegExp(QLatin1String("(\\\\*)$")), QLatin1String("\"\\1")); + ret.prepend(QLatin1Char('"')); + } + return ret; +} - cmdLine += blank; - for (int i = 0; i < args.size(); ++i) { - QString tmp = args.at(i); - // in the case of \" already being in the string the \ must also be escaped - tmp.replace(QLatin1String("\\\""), QLatin1String("\\\\\"")); - // escape a single " because the arguments will be parsed - tmp.replace(QString(doubleQuote), QLatin1String("\\\"")); - if (tmp.isEmpty() || tmp.contains(blank) || tmp.contains('\t')) { - // The argument must not end with a \ since this would be interpreted - // as escaping the quote -- rather put the \ behind the quote: e.g. - // rather use "foo"\ than "foo\" - QString endQuote(doubleQuote); - int i = tmp.length(); - while (i > 0 && tmp.at(i - 1) == backSlash) { - --i; - endQuote += backSlash; - } - cmdLine += QLatin1String(" \""); - cmdLine += tmp.left(i); - cmdLine += endQuote; - } else { - cmdLine += blank; - cmdLine += tmp; - } +QString AbstractProcess::createWinCommandline(const QString &program, const QStringList &args) +{ + QString programName = quoteWinCommand(program); + foreach (const QString &arg, args) { + programName += QLatin1Char(' '); + programName += quoteWinArgument(arg); + } + return programName; +} + +QString AbstractProcess::createWinCommandline(const QString &program, const QString &args) +{ + QString programName = quoteWinCommand(program); + if (!args.isEmpty()) { + programName += QLatin1Char(' '); + programName += args; } - return cmdLine; + return programName; } QByteArray AbstractProcess::createWinEnvironment(const QStringList &env) diff --git a/src/libs/utils/consoleprocess.h b/src/libs/utils/consoleprocess.h index ced9aa352e..a64132e586 100644 --- a/src/libs/utils/consoleprocess.h +++ b/src/libs/utils/consoleprocess.h @@ -54,7 +54,7 @@ public: ConsoleProcess(QObject *parent = 0); ~ConsoleProcess(); - bool start(const QString &program, const QStringList &args); + bool start(const QString &program, const QString &args); void stop(); void setMode(Mode m); diff --git a/src/libs/utils/consoleprocess_unix.cpp b/src/libs/utils/consoleprocess_unix.cpp index 32528d44af..6a3d8e8a9b 100644 --- a/src/libs/utils/consoleprocess_unix.cpp +++ b/src/libs/utils/consoleprocess_unix.cpp @@ -29,6 +29,9 @@ #include "consoleprocess.h" +#include "environment.h" +#include "qtcprocess.h" + #include <QtCore/QCoreApplication> #include <QtCore/QDir> #include <QtCore/QSettings> @@ -66,6 +69,7 @@ ConsoleProcessPrivate::ConsoleProcessPrivate() : m_mode(ConsoleProcess::Run), m_appPid(0), m_stubSocket(0), + m_tempFile(0), m_settings(0) { } @@ -114,18 +118,49 @@ void ConsoleProcess::setSettings(QSettings *settings) d->m_settings = settings; } -bool ConsoleProcess::start(const QString &program, const QStringList &args) +bool ConsoleProcess::start(const QString &program, const QString &args) { if (isRunning()) return false; + QtcProcess::SplitError perr; + QStringList pargs = QtcProcess::prepareArgs(args, &perr, &m_environment, &m_workingDir); + QString pcmd; + if (perr == QtcProcess::SplitOk) { + pcmd = program; + } else { + if (perr != QtcProcess::FoundMeta) { + emit processMessage(tr("Quoting error in command."), true); + return false; + } + if (d->m_mode == Debug) { + // FIXME: QTCREATORBUG-2809 + emit processMessage(tr("Debugging complex shell commands in a terminal" + " is currently not supported."), true); + return false; + } + pcmd = QLatin1String("/bin/sh"); + pargs << QLatin1String("-c") << (QtcProcess::quoteArg(program) + QLatin1Char(' ') + args); + } + + QtcProcess::SplitError qerr; + QStringList xtermArgs = QtcProcess::prepareArgs(terminalEmulator(d->m_settings), &qerr, + &m_environment, &m_workingDir); + if (qerr != QtcProcess::SplitOk) { + emit processMessage(qerr == QtcProcess::BadQuoting + ? tr("Quoting error in terminal command.") + : tr("Terminal command may not be a shell command."), true); + return false; + } + const QString err = stubServerListen(); if (!err.isEmpty()) { emit processMessage(msgCommChannelFailed(err), true); return false; } - if (!environment().isEmpty()) { + QStringList env = m_environment.toStringList(); + if (!env.isEmpty()) { d->m_tempFile = new QTemporaryFile(); if (!d->m_tempFile->open()) { stubServerShutdown(); @@ -134,14 +169,13 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args) d->m_tempFile = 0; return false; } - foreach (const QString &var, environment()) { + foreach (const QString &var, env) { d->m_tempFile->write(var.toLocal8Bit()); d->m_tempFile->write("", 1); } d->m_tempFile->flush(); } - QStringList xtermArgs = terminalEmulator(d->m_settings).split(QLatin1Char(' ')); // FIXME: quoting xtermArgs #ifdef Q_OS_MAC << (QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/qtcreator_process_stub")) @@ -153,7 +187,7 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args) << msgPromptToClose() << workingDirectory() << (d->m_tempFile ? d->m_tempFile->fileName() : QString()) - << program << args; + << pcmd << pargs; QString xterm = xtermArgs.takeFirst(); d->m_process.start(xterm, xtermArgs); diff --git a/src/libs/utils/consoleprocess_win.cpp b/src/libs/utils/consoleprocess_win.cpp index eeaa333604..d93175f637 100644 --- a/src/libs/utils/consoleprocess_win.cpp +++ b/src/libs/utils/consoleprocess_win.cpp @@ -28,6 +28,8 @@ **************************************************************************/ #include "consoleprocess.h" +#include "environment.h" +#include "qtcprocess.h" #include "winutils.h" #include <windows.h> @@ -115,18 +117,28 @@ QProcess::ExitStatus ConsoleProcess::exitStatus() const return d->m_appStatus; } -bool ConsoleProcess::start(const QString &program, const QStringList &args) +bool ConsoleProcess::start(const QString &program, const QString &args) { if (isRunning()) return false; + QString pcmd; + QString pargs; + if (d->m_mode != Run) { // The debugger engines already pre-process the arguments. + pcmd = program; + pargs = args; + } else { + QtcProcess::prepareCommand(program, args, &pcmd, &pargs, &m_environment, &m_workingDir); + } + const QString err = stubServerListen(); if (!err.isEmpty()) { emit processMessage(msgCommChannelFailed(err), true); return false; } - if (!environment().isEmpty()) { + QStringList env = m_environment.toStringList(); + if (!env.isEmpty()) { d->m_tempFile = new QTemporaryFile(); if (!d->m_tempFile->open()) { stubServerShutdown(); @@ -138,7 +150,7 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args) QTextStream out(d->m_tempFile); out.setCodec("UTF-16LE"); out.setGenerateByteOrderMark(false); - foreach (const QString &var, fixWinEnvironment(environment())) + foreach (const QString &var, fixWinEnvironment(env)) out << var << QChar(0); out << QChar(0); } @@ -159,7 +171,7 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args) << d->m_stubServer.fullServerName() << workDir << (d->m_tempFile ? d->m_tempFile->fileName() : 0) - << createWinCommandline(program, args) + << createWinCommandline(pcmd, pargs) << msgPromptToClose(); const QString cmdLine = createWinCommandline( diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp index 03b7343592..34a8fa3aba 100644 --- a/src/libs/utils/environment.cpp +++ b/src/libs/utils/environment.cpp @@ -332,62 +332,6 @@ bool Environment::operator==(const Environment &other) const return m_values == other.m_values; } -QStringList Environment::parseCombinedArgString(const QString &program) -{ - QStringList args; - QString tmp; - int quoteCount = 0; - bool inQuote = false; - - // handle quoting. tokens can be surrounded by double quotes - // "hello world". three consecutive double quotes represent - // the quote character itself. - for (int i = 0; i < program.size(); ++i) { - if (program.at(i) == QLatin1Char('"')) { - ++quoteCount; - if (quoteCount == 3) { - // third consecutive quote - quoteCount = 0; - tmp += program.at(i); - } - continue; - } - if (quoteCount) { - if (quoteCount == 1) - inQuote = !inQuote; - quoteCount = 0; - } - if (!inQuote && program.at(i).isSpace()) { - if (!tmp.isEmpty()) { - args += tmp; - tmp.clear(); - } - } else { - tmp += program.at(i); - } - } - if (!tmp.isEmpty()) - args += tmp; - return args; -} - -QString Environment::joinArgumentList(const QStringList &arguments) -{ - QString result; - const QChar doubleQuote = QLatin1Char('"'); - foreach (QString arg, arguments) { - if (!result.isEmpty()) - result += QLatin1Char(' '); - arg.replace(QString(doubleQuote), QLatin1String("\"\"\"")); - if (arg.contains(QLatin1Char(' '))) { - arg.insert(0, doubleQuote); - arg += doubleQuote; - } - result += arg; - } - return result; -} - /** Expand environment variables in a string. * * Environment variables are accepted in the following forms: diff --git a/src/libs/utils/environment.h b/src/libs/utils/environment.h index 5e505656d0..fe2ddc701d 100644 --- a/src/libs/utils/environment.h +++ b/src/libs/utils/environment.h @@ -94,9 +94,6 @@ public: const QStringList & additionalDirs = QStringList()) const; QStringList path() const; - static QStringList parseCombinedArgString(const QString &program); - static QString joinArgumentList(const QStringList &arguments); - QString expandVariables(const QString &) const; QStringList expandVariables(const QStringList &) const; |