summaryrefslogtreecommitdiff
path: root/src/libs/utils
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@nokia.com>2010-10-19 11:14:03 +0200
committerOswald Buddenhagen <oswald.buddenhagen@nokia.com>2010-11-17 13:19:07 +0100
commit1e362b0f8b0dfd712337df35cd26c5dc98dfc294 (patch)
treedbb61c65fc1c3e84b507e03c2b3f62a36acd6488 /src/libs/utils
parent531c70f05bfc8355f856f2af41be533fb13b85e6 (diff)
downloadqt-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.h13
-rw-r--r--src/libs/utils/abstractprocess_win.cpp78
-rw-r--r--src/libs/utils/consoleprocess.h2
-rw-r--r--src/libs/utils/consoleprocess_unix.cpp44
-rw-r--r--src/libs/utils/consoleprocess_win.cpp20
-rw-r--r--src/libs/utils/environment.cpp56
-rw-r--r--src/libs/utils/environment.h3
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;