QtcProcess: Introduce a QtcProcess::Arguments class

This is used to get a platform-agnostic handle on "command line
arguments". It essentially wraps a single QString on Windows,
and a QStringList everywhere else.

As a consequence, several occurrences of #ifdef Q_OS_*
can be removed from the codebase.

Change-Id: Ic93118c1bd0bce0ebb58f416d395dbaebb861772
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
This commit is contained in:
hjk
2014-02-05 10:43:21 +01:00
committed by Eike Ziller
parent a0d29eeb03
commit c68ebeed2e
14 changed files with 1257 additions and 1196 deletions

View File

@@ -75,7 +75,8 @@ bool ConsoleProcess::start(const QString &program, const QString &args)
return false;
QtcProcess::SplitError perr;
QStringList pargs = QtcProcess::prepareArgs(args, &perr, &d->m_environment, &d->m_workingDir);
QtcProcess::Arguments pargs = QtcProcess::prepareArgs(args, &perr, HostOsInfo::hostOs(),
&d->m_environment, &d->m_workingDir);
QString pcmd;
if (perr == QtcProcess::SplitOk) {
pcmd = program;
@@ -91,11 +92,14 @@ bool ConsoleProcess::start(const QString &program, const QString &args)
return false;
}
pcmd = QLatin1String("/bin/sh");
pargs << QLatin1String("-c") << (QtcProcess::quoteArg(program) + QLatin1Char(' ') + args);
pargs = QtcProcess::Arguments::createUnixArgs(QStringList()
<< QLatin1String("-c")
<< (QtcProcess::quoteArg(program) + QLatin1Char(' ') + args));
}
QtcProcess::SplitError qerr;
QStringList xtermArgs = QtcProcess::prepareArgs(terminalEmulator(d->m_settings), &qerr,
QtcProcess::Arguments xtermArgs = QtcProcess::prepareArgs(terminalEmulator(d->m_settings), &qerr,
HostOsInfo::hostOs(),
&d->m_environment, &d->m_workingDir);
if (qerr != QtcProcess::SplitOk) {
emit processError(qerr == QtcProcess::BadQuoting
@@ -134,23 +138,23 @@ bool ConsoleProcess::start(const QString &program, const QString &args)
}
}
if (Utils::HostOsInfo::isMacHost()) {
xtermArgs << (QCoreApplication::applicationDirPath()
+ QLatin1String("/../Resources/qtcreator_process_stub"));
} else {
xtermArgs << (QCoreApplication::applicationDirPath()
+ QLatin1String("/qtcreator_process_stub"));
}
xtermArgs
QString stubPath = QCoreApplication::applicationDirPath();
if (Utils::HostOsInfo::isMacHost())
stubPath.append(QLatin1String("/../Resources/qtcreator_process_stub"));
else
stubPath.append(QLatin1String("/qtcreator_process_stub"));
QStringList allArgs = xtermArgs.toUnixArgs();
allArgs << stubPath
<< modeOption(d->m_mode)
<< d->m_stubServer.fullServerName()
<< msgPromptToClose()
<< workingDirectory()
<< (d->m_tempFile ? d->m_tempFile->fileName() : QString())
<< pcmd << pargs;
<< pcmd << pargs.toUnixArgs();
QString xterm = xtermArgs.takeFirst();
d->m_process.start(xterm, xtermArgs);
QString xterm = allArgs.takeFirst();
d->m_process.start(xterm, allArgs);
if (!d->m_process.waitForStarted()) {
stubServerShutdown();
emit processError(tr("Cannot start the terminal emulator '%1', change the setting in the "

View File

@@ -75,7 +75,10 @@ bool ConsoleProcess::start(const QString &program, const QString &args)
pcmd = program;
pargs = args;
} else {
QtcProcess::prepareCommand(program, args, &pcmd, &pargs, &d->m_environment, &d->m_workingDir);
QtcProcess::Arguments outArgs;
QtcProcess::prepareCommand(program, args, &pcmd, &outArgs, OsTypeWindows,
&d->m_environment, &d->m_workingDir);
pargs = outArgs.toWindowsArgs();
}
const QString err = stubServerListen();

View File

@@ -31,16 +31,43 @@
#include "stringutils.h"
#include <utils/qtcassert.h>
#include <utils/hostosinfo.h>
#include <QDir>
#include <QDebug>
#include <QCoreApplication>
#include <QStack>
#ifdef Q_OS_WIN
#include <qt_windows.h>
#endif
using namespace Utils;
// The main state of the Unix shell parser
enum MxQuoting { MxBasic, MxSingleQuote, MxDoubleQuote, MxParen, MxSubst, MxGroup, MxMath };
struct MxState
{
MxQuoting current;
// Bizarrely enough, double quoting has an impact on the behavior of some
// complex expressions within the quoted string.
bool dquote;
};
QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(MxState, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
// Pushed state for the case where a $(()) expansion turns out bogus
struct MxSave
{
QString str;
int pos, varPos;
};
QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(MxSave, Q_MOVABLE_TYPE);
QT_END_NAMESPACE
namespace Utils {
/*!
\class Utils::QtcProcess
@@ -49,9 +76,7 @@ using namespace Utils;
shell-quoted process arguments.
*/
#ifdef Q_OS_WIN
inline static bool isMetaChar(ushort c)
inline static bool isMetaCharWin(ushort c)
{
static const uchar iqm[] = {
0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, 0x50,
@@ -61,7 +86,7 @@ inline static bool isMetaChar(ushort c)
return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
}
static void envExpand(QString &args, const Environment *env, const QString *pwd)
static void envExpandWin(QString &args, const Environment *env, const QString *pwd)
{
static const QString cdName = QLatin1String("CD");
int off = 0;
@@ -82,18 +107,18 @@ static void envExpand(QString &args, const Environment *env, const QString *pwd)
}
}
QString QtcProcess::prepareArgs(const QString &_args, SplitError *err,
static QtcProcess::Arguments prepareArgsWin(const QString &_args, QtcProcess::SplitError *err,
const Environment *env, const QString *pwd)
{
QString args(_args);
if (env) {
envExpand(args, env, pwd);
envExpandWin(args, env, pwd);
} else {
if (args.indexOf(QLatin1Char('%')) >= 0) {
if (err)
*err = FoundMeta;
return QString();
*err = QtcProcess::FoundMeta;
return QtcProcess::Arguments::createWindowsArgs(QString());
}
}
@@ -109,24 +134,24 @@ QString QtcProcess::prepareArgs(const QString &_args, SplitError *err,
if (++p == args.length())
break; // For cmd, this is no error.
} while (args.unicode()[p].unicode() != '"');
} else if (isMetaChar(c)) {
} else if (isMetaCharWin(c)) {
if (err)
*err = FoundMeta;
return QString();
*err = QtcProcess::FoundMeta;
return QtcProcess::Arguments::createWindowsArgs(QString());
}
}
if (err)
*err = SplitOk;
return args;
*err = QtcProcess::SplitOk;
return QtcProcess::Arguments::createWindowsArgs(args);
}
inline static bool isWhiteSpace(ushort c)
inline static bool isWhiteSpaceWin(ushort c)
{
return c == ' ' || c == '\t';
}
static QStringList doSplitArgs(const QString &args, QtcProcess::SplitError *err)
static QStringList doSplitArgsWin(const QString &args, QtcProcess::SplitError *err)
{
QStringList ret;
@@ -139,7 +164,7 @@ static QStringList doSplitArgs(const QString &args, QtcProcess::SplitError *err)
forever {
if (p == length)
return ret;
if (!isWhiteSpace(args.unicode()[p].unicode()))
if (!isWhiteSpaceWin(args.unicode()[p].unicode()))
break;
++p;
}
@@ -181,7 +206,7 @@ static QStringList doSplitArgs(const QString &args, QtcProcess::SplitError *err)
while (--bslashes >= 0)
arg.append(QLatin1Char('\\'));
if (p == length || (!inquote && isWhiteSpace(args.unicode()[p].unicode()))) {
if (p == length || (!inquote && isWhiteSpaceWin(args.unicode()[p].unicode()))) {
ret.append(arg);
if (inquote) {
if (err)
@@ -258,28 +283,29 @@ static QStringList doSplitArgs(const QString &args, QtcProcess::SplitError *err)
\c{foo " bar}.
*/
QStringList QtcProcess::splitArgs(const QString &_args, bool abortOnMeta, SplitError *err,
static QStringList splitArgsWin(const QString &_args, bool abortOnMeta,
QtcProcess::SplitError *err,
const Environment *env, const QString *pwd)
{
if (abortOnMeta) {
SplitError perr;
QtcProcess::SplitError perr;
if (!err)
err = &perr;
QString args = prepareArgs(_args, &perr, env, pwd);
if (*err != SplitOk)
QString args = prepareArgsWin(_args, &perr, env, pwd).toWindowsArgs();
if (*err != QtcProcess::SplitOk)
return QStringList();
return doSplitArgs(args, err);
return doSplitArgsWin(args, err);
} else {
QString args = _args;
if (env)
envExpand(args, env, pwd);
return doSplitArgs(args, err);
envExpandWin(args, env, pwd);
return doSplitArgsWin(args, err);
}
}
#else // Q_OS_WIN
inline static bool isMeta(QChar cUnicode)
static bool isMetaUnix(QChar cUnicode)
{
static const uchar iqm[] = {
0x00, 0x00, 0x00, 0x00, 0xdc, 0x07, 0x00, 0xd8,
@@ -291,7 +317,8 @@ inline static bool isMeta(QChar cUnicode)
return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
}
QStringList QtcProcess::splitArgs(const QString &args, bool abortOnMeta, SplitError *err,
static QStringList splitArgsUnix(const QString &args, bool abortOnMeta,
QtcProcess::SplitError *err,
const Environment *env, const QString *pwd)
{
static const QString pwdName = QLatin1String("PWD");
@@ -448,7 +475,7 @@ QStringList QtcProcess::splitArgs(const QString &args, bool abortOnMeta, SplitEr
if (pos >= args.length())
goto quoteerr;
c = args.unicode()[pos++];
} else if (abortOnMeta && isMeta(c)) {
} else if (abortOnMeta && isMetaUnix(c)) {
goto metaerr;
}
cret += c;
@@ -466,22 +493,20 @@ QStringList QtcProcess::splitArgs(const QString &args, bool abortOnMeta, SplitEr
okret:
if (err)
*err = SplitOk;
*err = QtcProcess::SplitOk;
return ret;
quoteerr:
if (err)
*err = BadQuoting;
*err = QtcProcess::BadQuoting;
return QStringList();
metaerr:
if (err)
*err = FoundMeta;
*err = QtcProcess::FoundMeta;
return QStringList();
}
#endif // Q_OS_WIN
inline static bool isSpecialCharUnix(ushort c)
{
// Chars that should be quoted (TM). This includes:
@@ -501,6 +526,16 @@ inline static bool hasSpecialCharsUnix(const QString &arg)
return false;
}
QStringList QtcProcess::splitArgs(const QString &args, OsType osType,
bool abortOnMeta, QtcProcess::SplitError *err,
const Environment *env, const QString *pwd)
{
if (osType == OsTypeWindows)
return splitArgsWin(args, abortOnMeta, err, env, pwd);
else
return splitArgsUnix(args, abortOnMeta, err, env, pwd);
}
QString QtcProcess::quoteArgUnix(const QString &arg)
{
if (!arg.length())
@@ -515,23 +550,7 @@ QString QtcProcess::quoteArgUnix(const QString &arg)
return ret;
}
void QtcProcess::addArgUnix(QString *args, const QString &arg)
{
if (!args->isEmpty())
*args += QLatin1Char(' ');
*args += quoteArgUnix(arg);
}
QString QtcProcess::joinArgsUnix(const QStringList &args)
{
QString ret;
foreach (const QString &arg, args)
addArgUnix(&ret, arg);
return ret;
}
#ifdef Q_OS_WIN
inline static bool isSpecialChar(ushort c)
static bool isSpecialCharWin(ushort c)
{
// Chars that should be quoted (TM). This includes:
// - control chars & space
@@ -545,21 +564,21 @@ inline static bool isSpecialChar(ushort c)
return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
}
inline static bool hasSpecialChars(const QString &arg)
static bool hasSpecialCharsWin(const QString &arg)
{
for (int x = arg.length() - 1; x >= 0; --x)
if (isSpecialChar(arg.unicode()[x].unicode()))
if (isSpecialCharWin(arg.unicode()[x].unicode()))
return true;
return false;
}
QString QtcProcess::quoteArg(const QString &arg)
static QString quoteArgWin(const QString &arg)
{
if (!arg.length())
return QString::fromLatin1("\"\"");
QString ret(arg);
if (hasSpecialChars(ret)) {
if (hasSpecialCharsWin(ret)) {
// Quotes are escaped and their preceding backslashes are doubled.
// It's impossible to escape anything inside a quoted string on cmd
// level, so the outer quoting must be "suspended".
@@ -578,21 +597,38 @@ QString QtcProcess::quoteArg(const QString &arg)
return ret;
}
void QtcProcess::addArg(QString *args, const QString &arg)
QtcProcess::Arguments QtcProcess::prepareArgs(const QString &cmd, SplitError *err, OsType osType,
const Environment *env, const QString *pwd)
{
if (osType == OsTypeWindows)
return prepareArgsWin(cmd, err, env, pwd);
else
return Arguments::createUnixArgs(splitArgs(cmd, osType, true, err, env, pwd));
}
QString QtcProcess::quoteArg(const QString &arg, OsType osType)
{
if (osType == OsTypeWindows)
return quoteArgWin(arg);
else
return quoteArgUnix(arg);
}
void QtcProcess::addArg(QString *args, const QString &arg, OsType osType)
{
if (!args->isEmpty())
*args += QLatin1Char(' ');
*args += quoteArg(arg);
*args += quoteArg(arg, osType);
}
QString QtcProcess::joinArgs(const QStringList &args)
QString QtcProcess::joinArgs(const QStringList &args, OsType osType)
{
QString ret;
foreach (const QString &arg, args)
addArg(&ret, arg);
addArg(&ret, arg, osType);
return ret;
}
#endif
void QtcProcess::addArgs(QString *args, const QString &inArgs)
{
@@ -609,44 +645,36 @@ void QtcProcess::addArgs(QString *args, const QStringList &inArgs)
addArg(args, arg);
}
#ifdef Q_OS_WIN
void QtcProcess::prepareCommand(const QString &command, const QString &arguments,
QString *outCmd, QString *outArgs,
bool QtcProcess::prepareCommand(const QString &command, const QString &arguments,
QString *outCmd, Arguments *outArgs, OsType osType,
const Environment *env, const QString *pwd)
{
QtcProcess::SplitError err;
*outArgs = QtcProcess::prepareArgs(arguments, &err, env, pwd);
*outArgs = QtcProcess::prepareArgs(arguments, &err, osType, env, pwd);
if (err == QtcProcess::SplitOk) {
*outCmd = command;
} else {
if (osType == OsTypeWindows) {
*outCmd = QString::fromLatin1(qgetenv("COMSPEC"));
*outArgs = QLatin1String("/v:off /s /c \"")
*outArgs = Arguments::createWindowsArgs(QLatin1String("/v:off /s /c \"")
+ quoteArg(QDir::toNativeSeparators(command)) + QLatin1Char(' ') + arguments
+ QLatin1Char('"');
}
}
#else
bool QtcProcess::prepareCommand(const QString &command, const QString &arguments,
QString *outCmd, QStringList *outArgs,
const Environment *env, const QString *pwd)
{
QtcProcess::SplitError err;
*outArgs = QtcProcess::prepareArgs(arguments, &err, env, pwd);
if (err == QtcProcess::SplitOk) {
*outCmd = command;
+ QLatin1Char('"'));
} else {
if (err != QtcProcess::FoundMeta)
return false;
*outCmd = QLatin1String("/bin/sh");
*outArgs << QLatin1String("-c") << (quoteArg(command) + QLatin1Char(' ') + arguments);
*outArgs = Arguments::createUnixArgs(QStringList()
<< QLatin1String("-c")
<< (quoteArg(command) + QLatin1Char(' ') + arguments));
}
}
return true;
}
#endif
void QtcProcess::start()
{
Environment env;
const OsType osType = HostOsInfo::hostOs();
if (m_haveEnv) {
if (m_environment.size() == 0)
qWarning("QtcProcess::start: Empty environment set when running '%s'.", qPrintable(m_command));
@@ -655,16 +683,12 @@ void QtcProcess::start()
// If the process environemnt has no libraryPath,
// Qt will copy creator's libraryPath into the process environment.
// That's brain dead, and we work around it
#if defined(Q_OS_UNIX)
# if defined(Q_OS_MAC)
static const char libraryPathC[] = "DYLD_LIBRARY_PATH";
# else
static const char libraryPathC[] = "LD_LIBRARY_PATH";
# endif
const QString libraryPath = QLatin1String(libraryPathC);
if (osType != OsTypeWindows) { // a.k.a "Unixoid"
const QString libraryPath =
QLatin1String(osType == OsTypeMac ? "DYLD_LIBRARY_PATH" : "LD_LIBRARY_PATH");
if (env.constFind(libraryPath) == env.constEnd())
env.set(libraryPath, QString());
#endif
}
QProcess::setEnvironment(env.toStringList());
} else {
env = Environment::systemEnvironment();
@@ -672,27 +696,28 @@ void QtcProcess::start()
const QString &workDir = workingDirectory();
QString command;
QtcProcess::Arguments arguments;
bool success = prepareCommand(m_command, m_arguments, &command, &arguments, osType, &env, &workDir);
if (osType == OsTypeWindows) {
QString args = arguments.toWindowsArgs();
#ifdef Q_OS_WIN
QString arguments;
QStringList argList;
prepareCommand(m_command, m_arguments, &command, &arguments, &env, &workDir);
setNativeArguments(arguments);
setNativeArguments(args);
#endif
if (m_useCtrlCStub) {
argList << QDir::toNativeSeparators(command);
args = QDir::toNativeSeparators(command);
command = QCoreApplication::applicationDirPath() + QLatin1String("/qtcreator_ctrlc_stub.exe");
}
QProcess::start(command, argList);
#else
QStringList arguments;
if (!prepareCommand(m_command, m_arguments, &command, &arguments, &env, &workDir)) {
QProcess::start(command, QStringList(args));
} else {
if (!success) {
setErrorString(tr("Error in command line."));
// Should be FailedToStart, but we cannot set the process error from the outside,
// so it would be inconsistent.
emit error(QProcess::UnknownError);
return;
}
QProcess::start(command, arguments);
#endif
QProcess::start(command, arguments.toUnixArgs());
}
}
#ifdef Q_OS_WIN
@@ -738,10 +763,9 @@ void QtcProcess::interrupt()
#endif
}
#ifdef Q_OS_WIN
// This function assumes that the resulting string will be quoted.
// That's irrelevant if it does not contain quotes itself.
static int quoteArgInternal(QString &ret, int bslashes)
static int quoteArgInternalWin(QString &ret, int bslashes)
{
// Quotes are escaped and their preceding backslashes are doubled.
// It's impossible to escape anything inside a quoted string on cmd
@@ -765,32 +789,6 @@ static int quoteArgInternal(QString &ret, int bslashes)
return bslashes;
}
#else
// The main state of the Unix shell parser
enum MxQuoting { MxBasic, MxSingleQuote, MxDoubleQuote, MxParen, MxSubst, MxGroup, MxMath };
typedef struct {
MxQuoting current;
// Bizarrely enough, double quoting has an impact on the behavior of some
// complex expressions within the quoted string.
bool dquote;
} MxState;
QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(MxState, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
// Pushed state for the case where a $(()) expansion turns out bogus
typedef struct {
QString str;
int pos, varPos;
} MxSave;
QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(MxSave, Q_MOVABLE_TYPE);
QT_END_NAMESPACE
#include <QStack>
#endif
// TODO: This documentation is relevant for end-users. Where to put it?
/**
@@ -864,7 +862,7 @@ QT_END_NAMESPACE
* \return false if the string could not be parsed and therefore no safe
* substitution was possible
*/
bool QtcProcess::expandMacros(QString *cmd, AbstractMacroExpander *mx)
bool QtcProcess::expandMacros(QString *cmd, AbstractMacroExpander *mx, OsType osType)
{
QString str = *cmd;
if (str.isEmpty())
@@ -878,7 +876,7 @@ bool QtcProcess::expandMacros(QString *cmd, AbstractMacroExpander *mx)
int pos = 0;
#ifdef Q_OS_WIN
if (osType == OsTypeWindows) {
enum { // cmd.exe parsing state
ShellBasic, // initial state
ShellQuoted, // double-quoted state => *no* other meta chars are interpreted
@@ -913,12 +911,12 @@ bool QtcProcess::expandMacros(QString *cmd, AbstractMacroExpander *mx)
crtState = CrtNeedWord;
}
} else {
if (hasSpecialChars(rsts)) {
if (hasSpecialCharsWin(rsts)) {
if (crtState == CrtClosed) {
// Quoted expando right after closing quote. Can't do that.
return false;
}
int tbslashes = quoteArgInternal(rsts, 0);
int tbslashes = quoteArgInternalWin(rsts, 0);
rsts.prepend(QLatin1Char('"'));
if (rbslashes)
rsts.prepend(QString(rbslashes, QLatin1Char('\\')));
@@ -928,11 +926,11 @@ bool QtcProcess::expandMacros(QString *cmd, AbstractMacroExpander *mx)
crtState = CrtInWord; // We know that this string contains no spaces.
// We know that this string contains no quotes,
// so the function won't make a mess.
rbslashes = quoteArgInternal(rsts, rbslashes);
rbslashes = quoteArgInternalWin(rsts, rbslashes);
}
}
} else {
rbslashes = quoteArgInternal(rsts, rbslashes);
rbslashes = quoteArgInternalWin(rsts, rbslashes);
}
str.replace(pos, varLen, rsts);
pos += rsts.length();
@@ -1026,7 +1024,8 @@ bool QtcProcess::expandMacros(QString *cmd, AbstractMacroExpander *mx)
}
pos++;
}
#else
} else {
// !Windows
MxState state = { MxBasic, false };
QStack<MxState> sstack;
QStack<MxSave> ostack;
@@ -1181,16 +1180,16 @@ bool QtcProcess::expandMacros(QString *cmd, AbstractMacroExpander *mx)
pos++;
}
// FIXME? May complain if (!sstack.empty()), but we don't really care anyway.
#endif
}
*cmd = str;
return true;
}
QString QtcProcess::expandMacros(const QString &str, AbstractMacroExpander *mx)
QString QtcProcess::expandMacros(const QString &str, AbstractMacroExpander *mx, OsType osType)
{
QString ret = str;
expandMacros(&ret, mx);
expandMacros(&ret, mx, osType);
return ret;
}
@@ -1203,7 +1202,7 @@ bool QtcProcess::ArgIterator::next()
m_simple = true;
m_value.clear();
#ifdef Q_OS_WIN
if (m_osType == OsTypeWindows) {
enum { // cmd.exe parsing state
ShellBasic, // initial state
ShellQuoted, // double-quoted state => *no* other meta chars are interpreted
@@ -1223,7 +1222,7 @@ bool QtcProcess::ArgIterator::next()
if (shellState == ShellBasic && cc == '^') {
varState = NoVar;
shellState = ShellEscaped;
} else if ((shellState == ShellBasic && isMetaChar(cc)) || !cc) { // A "bit" simplistic ...
} else if ((shellState == ShellBasic && isMetaCharWin(cc)) || !cc) { // A "bit" simplistic ...
// We ignore crtQuote state here. Whatever ...
doReturn:
if (m_simple)
@@ -1302,7 +1301,7 @@ bool QtcProcess::ArgIterator::next()
shellState = ShellBasic;
}
}
#else
} else {
MxState state = { MxBasic, false };
QStack<MxState> sstack;
QStack<int> ostack;
@@ -1448,7 +1447,7 @@ bool QtcProcess::ArgIterator::next()
return true;
}
return false;
#endif
}
}
void QtcProcess::ArgIterator::deleteArg()
@@ -1470,8 +1469,6 @@ void QtcProcess::ArgIterator::appendArg(const QString &str)
m_pos += qstr.length() + 1;
}
namespace Utils {
QTCREATOR_UTILS_EXPORT unsigned long qPidToPid(const Q_PID qpid)
{
#ifdef Q_OS_WIN
@@ -1482,4 +1479,40 @@ QTCREATOR_UTILS_EXPORT unsigned long qPidToPid(const Q_PID qpid)
#endif
}
QtcProcess::Arguments QtcProcess::Arguments::createWindowsArgs(const QString &args)
{
Arguments result;
result.m_windowsArgs = args;
result.m_isWindows = true;
return result;
}
QtcProcess::Arguments QtcProcess::Arguments::createUnixArgs(const QStringList &args)
{
Arguments result;
result.m_unixArgs = args;
result.m_isWindows = false;
return result;
}
QString QtcProcess::Arguments::toWindowsArgs() const
{
QTC_CHECK(m_isWindows);
return m_windowsArgs;
}
QStringList QtcProcess::Arguments::toUnixArgs() const
{
QTC_CHECK(!m_isWindows);
return m_unixArgs;
}
QString QtcProcess::Arguments::toString() const
{
if (m_isWindows)
return m_windowsArgs;
else
return QtcProcess::joinArgs(m_unixArgs, OsTypeLinux);
}
} // namespace Utils

View File

@@ -56,6 +56,22 @@ public:
void terminate();
void interrupt();
class QTCREATOR_UTILS_EXPORT Arguments
{
public:
static Arguments createWindowsArgs(const QString &args);
static Arguments createUnixArgs(const QStringList &args);
QString toWindowsArgs() const;
QStringList toUnixArgs() const;
QString toString() const;
private:
QString m_windowsArgs;
QStringList m_unixArgs;
bool m_isWindows;
};
enum SplitError {
SplitOk = 0, //! All went just fine
BadQuoting, //! Command contains quoting errors
@@ -64,45 +80,33 @@ public:
//! Quote a single argument for usage in a unix shell command
static QString quoteArgUnix(const QString &arg);
//! Quote a single argument and append it to a unix shell command
static void addArgUnix(QString *args, const QString &arg);
//! Join an argument list into a unix shell command
static QString joinArgsUnix(const QStringList &args);
#ifdef Q_OS_WIN
//! Quote a single argument for usage in a shell command
static QString quoteArg(const QString &arg);
static QString quoteArg(const QString &arg, OsType osType = HostOsInfo::hostOs());
//! Quote a single argument and append it to a shell command
static void addArg(QString *args, const QString &arg);
static void addArg(QString *args, const QString &arg, OsType osType = HostOsInfo::hostOs());
//! Join an argument list into a shell command
static QString joinArgs(const QStringList &args);
static QString joinArgs(const QStringList &args, OsType osType = HostOsInfo::hostOs());
//! Prepare argument of a shell command for feeding into QProcess
static QString prepareArgs(const QString &cmd, SplitError *err,
static Arguments prepareArgs(const QString &cmd, SplitError *err,
OsType osType = HostOsInfo::hostOs(),
const Environment *env = 0, const QString *pwd = 0);
//! Prepare a shell command for feeding into QProcess
static void prepareCommand(const QString &command, const QString &arguments,
QString *outCmd, QString *outArgs,
const Environment *env = 0, const QString *pwd = 0);
#else
static QString quoteArg(const QString &arg) { return quoteArgUnix(arg); }
static void addArg(QString *args, const QString &arg) { addArgUnix(args, arg); }
static QString joinArgs(const QStringList &args) { return joinArgsUnix(args); }
static QStringList prepareArgs(const QString &cmd, SplitError *err,
const Environment *env = 0, const QString *pwd = 0)
{ return splitArgs(cmd, true, err, env, pwd); }
static bool prepareCommand(const QString &command, const QString &arguments,
QString *outCmd, QStringList *outArgs,
QString *outCmd, Arguments *outArgs, OsType osType = HostOsInfo::hostOs(),
const Environment *env = 0, const QString *pwd = 0);
#endif
//! Quote and append each argument to a shell command
static void addArgs(QString *args, const QStringList &inArgs);
//! Append already quoted arguments to a shell command
static void addArgs(QString *args, const QString &inArgs);
//! Split a shell command into separate arguments. ArgIterator is usually a better choice.
static QStringList splitArgs(const QString &cmd, bool abortOnMeta = false, SplitError *err = 0,
static QStringList splitArgs(const QString &cmd, OsType osType = HostOsInfo::hostOs(),
bool abortOnMeta = false, SplitError *err = 0,
const Environment *env = 0, const QString *pwd = 0);
//! Safely replace the expandos in a shell command
static bool expandMacros(QString *cmd, AbstractMacroExpander *mx);
static QString expandMacros(const QString &str, AbstractMacroExpander *mx);
static bool expandMacros(QString *cmd, AbstractMacroExpander *mx,
OsType osType = HostOsInfo::hostOs());
static QString expandMacros(const QString &str, AbstractMacroExpander *mx,
OsType osType = HostOsInfo::hostOs());
/*! Iterate over arguments from a command line.
* Assumes that the name of the actual command is *not* part of the line.
@@ -110,7 +114,9 @@ public:
*/
class QTCREATOR_UTILS_EXPORT ArgIterator {
public:
ArgIterator(QString *str) : m_str(str), m_pos(0), m_prev(-1) {}
ArgIterator(QString *str, OsType osType = HostOsInfo::hostOs())
: m_str(str), m_pos(0), m_prev(-1), m_osType(osType)
{}
//! Get the next argument. Returns false on encountering end of first command.
bool next();
//! True iff the argument is a plain string, possibly after unquoting.
@@ -126,11 +132,14 @@ public:
QString *m_str, m_value;
int m_pos, m_prev;
bool m_simple;
OsType m_osType;
};
class QTCREATOR_UTILS_EXPORT ConstArgIterator {
public:
ConstArgIterator(const QString &str) : m_str(str), m_ait(&m_str) {}
ConstArgIterator(const QString &str, OsType osType = HostOsInfo::hostOs())
: m_str(str), m_ait(&m_str, osType)
{}
bool next() { return m_ait.next(); }
bool isSimple() const { return m_ait.isSimple(); }
QString value() const { return m_ait.value(); }

View File

@@ -121,13 +121,14 @@ void FileUtils::openTerminal(const QString &path)
// Get terminal application
QString terminalEmulator;
QStringList args;
if (HostOsInfo::isWindowsHost()) {
const OsType hostOs = HostOsInfo::hostOs();
if (hostOs == OsTypeWindows) {
terminalEmulator = ConsoleProcess::defaultTerminalEmulator();
} else if (HostOsInfo::isMacHost()) {
} else if (hostOs == OsTypeMac) {
terminalEmulator = ICore::resourcePath()
+ QLatin1String("/scripts/openTerminal.command");
} else {
args = QtcProcess::splitArgs(ConsoleProcess::terminalEmulator(ICore::settings()));
args = QtcProcess::splitArgs(ConsoleProcess::terminalEmulator(ICore::settings()), hostOs);
terminalEmulator = args.takeFirst();
args.append(QString::fromLocal8Bit(qgetenv("SHELL")));
}

View File

@@ -4771,11 +4771,12 @@ void GdbEngine::write(const QByteArray &data)
bool GdbEngine::prepareCommand()
{
#ifdef Q_OS_WIN
Utils::QtcProcess::SplitError perr;
startParameters().processArgs = Utils::QtcProcess::prepareArgs(
startParameters().processArgs, &perr,
&startParameters().environment, &startParameters().workingDirectory);
if (HostOsInfo::isWindowsHost()) {
DebuggerStartParameters &sp = startParameters();
QtcProcess::SplitError perr;
sp.processArgs = QtcProcess::prepareArgs(sp.processArgs, &perr,
Utils::HostOsInfo::hostOs(),
&sp.environment, &sp.workingDirectory).toWindowsArgs();
if (perr != Utils::QtcProcess::SplitOk) {
// perr == BadQuoting is never returned on Windows
// FIXME? QTCREATORBUG-2809
@@ -4783,7 +4784,7 @@ bool GdbEngine::prepareCommand()
"Debugging complex command lines is currently not supported on Windows."), Core::Id());
return false;
}
#endif
}
return true;
}

View File

@@ -201,11 +201,7 @@ void LldbEngine::setupInferior()
const DebuggerStartParameters &sp = startParameters();
QString executable;
#ifdef Q_OS_WIN
QString args;
#else
QStringList args;
#endif
Utils::QtcProcess::Arguments args;
Utils::QtcProcess::prepareCommand(QFileInfo(sp.executable).absoluteFilePath(),
sp.processArgs, &executable, &args);
@@ -213,12 +209,8 @@ void LldbEngine::setupInferior()
cmd.arg("executable", executable);
cmd.arg("startMode", sp.startMode); // directly relying on this is brittle wrt. insertions, so check it here
cmd.beginList("processArgs");
#ifdef Q_OS_WIN
// fixme?
#else
foreach (const QString &arg, args)
foreach (const QString &arg, args.toUnixArgs())
cmd.arg(arg.toUtf8().toHex());
#endif
cmd.endList();
// it is better not to check the start mode on the python sid (as we would have to duplicate the

View File

@@ -2990,7 +2990,7 @@ bool GitClient::tryLauchingGitK(const QProcessEnvironment &env,
VcsBase::VcsBaseOutputWindow *outwin = VcsBase::VcsBaseOutputWindow::instance();
const QString gitkOpts = settings()->stringValue(GitSettings::gitkOptionsKey);
if (!gitkOpts.isEmpty())
arguments.append(Utils::QtcProcess::splitArgs(gitkOpts));
arguments.append(Utils::QtcProcess::splitArgs(gitkOpts, Utils::HostOsInfo::hostOs()));
if (!fileName.isEmpty())
arguments << QLatin1String("--") << fileName;
outwin->appendCommand(workingDirectory, binary, arguments);

View File

@@ -51,6 +51,7 @@
using namespace ProjectExplorer;
using namespace QmakeProjectManager;
using namespace Utils;
namespace Ios {
namespace Internal {
@@ -341,27 +342,27 @@ QString IosRunConfigurationWidget::argListToString(const QStringList &args) cons
QStringList IosRunConfigurationWidget::stringToArgList(const QString &args) const
{
Utils::QtcProcess::SplitError err;
QStringList res = Utils::QtcProcess::splitArgs(args, false, &err);
QtcProcess::SplitError err;
QStringList res = QtcProcess::splitArgs(args, OsTypeMac, false, &err);
switch (err) {
case Utils::QtcProcess::SplitOk:
case QtcProcess::SplitOk:
break;
case Utils::QtcProcess::BadQuoting:
case QtcProcess::BadQuoting:
if (args.at(args.size()-1) == QLatin1Char('\\')) {
res = Utils::QtcProcess::splitArgs(args + QLatin1Char('\\'), false, &err);
if (err != Utils::QtcProcess::SplitOk)
res = Utils::QtcProcess::splitArgs(args + QLatin1Char('\\') + QLatin1Char('\''),
false, &err);
if (err != Utils::QtcProcess::SplitOk)
res = Utils::QtcProcess::splitArgs(args + QLatin1Char('\\') + QLatin1Char('\"'),
false, &err);
res = QtcProcess::splitArgs(args + QLatin1Char('\\'), OsTypeMac, false, &err);
if (err != QtcProcess::SplitOk)
res = QtcProcess::splitArgs(args + QLatin1Char('\\') + QLatin1Char('\''),
OsTypeMac, false, &err);
if (err != QtcProcess::SplitOk)
res = QtcProcess::splitArgs(args + QLatin1Char('\\') + QLatin1Char('\"'),
OsTypeMac, false, &err);
}
if (err != Utils::QtcProcess::SplitOk)
res = Utils::QtcProcess::splitArgs(args + QLatin1Char('\''), false, &err);
if (err != Utils::QtcProcess::SplitOk)
res = Utils::QtcProcess::splitArgs(args + QLatin1Char('\"'), false, &err);
if (err != QtcProcess::SplitOk)
res = QtcProcess::splitArgs(args + QLatin1Char('\''), OsTypeMac, false, &err);
if (err != QtcProcess::SplitOk)
res = QtcProcess::splitArgs(args + QLatin1Char('\"'), OsTypeMac, false, &err);
break;
case Utils::QtcProcess::FoundMeta:
case QtcProcess::FoundMeta:
qDebug() << "IosRunConfigurationWidget FoundMeta (should not happen)";
break;
}

View File

@@ -928,13 +928,14 @@ void GccToolChainConfigWidget::makeReadOnlyImpl()
QStringList GccToolChainConfigWidget::splitString(const QString &s)
{
QtcProcess::SplitError splitError;
QStringList res = QtcProcess::splitArgs(s, false, &splitError);
const OsType osType = HostOsInfo::hostOs();
QStringList res = QtcProcess::splitArgs(s, osType, false, &splitError);
if (splitError != QtcProcess::SplitOk){
res = QtcProcess::splitArgs(s + QLatin1Char('\\'), false, &splitError);
res = QtcProcess::splitArgs(s + QLatin1Char('\\'), osType, false, &splitError);
if (splitError != QtcProcess::SplitOk){
res = QtcProcess::splitArgs(s + QLatin1Char('"'), false, &splitError);
res = QtcProcess::splitArgs(s + QLatin1Char('"'), osType, false, &splitError);
if (splitError != QtcProcess::SplitOk)
res = QtcProcess::splitArgs(s + QLatin1Char('\''), false, &splitError);
res = QtcProcess::splitArgs(s + QLatin1Char('\''), osType, false, &splitError);
}
}
return res;

View File

@@ -169,20 +169,12 @@ QString ProcessParameters::prettyArguments() const
{
QString margs = effectiveArguments();
QString workDir = effectiveWorkingDirectory();
#ifdef Q_OS_WIN
QString args;
#else
QStringList args;
#endif
Utils::QtcProcess::SplitError err;
args = Utils::QtcProcess::prepareArgs(margs, &err, &m_environment, &workDir);
Utils::QtcProcess::Arguments args =
Utils::QtcProcess::prepareArgs(margs, &err, Utils::HostOsInfo::hostOs(), &m_environment, &workDir);
if (err != Utils::QtcProcess::SplitOk)
return margs; // Sorry, too complex - just fall back.
#ifdef Q_OS_WIN
return args;
#else
return Utils::QtcProcess::joinArgs(args);
#endif
return args.toString();
}
QString ProcessParameters::summary(const QString &displayName) const

View File

@@ -69,7 +69,7 @@ QString LinuxDeviceProcess::fullCommandLine() const
fullCommandLine.append(quote(executable()));
if (!arguments().isEmpty()) {
fullCommandLine.append(QLatin1Char(' '));
fullCommandLine.append(Utils::QtcProcess::joinArgsUnix(arguments()));
fullCommandLine.append(Utils::QtcProcess::joinArgs(arguments(), Utils::OsTypeLinux));
}
return fullCommandLine;
}

View File

@@ -207,7 +207,7 @@ QString RemoteLinuxRunConfiguration::remoteExecutableFilePath() const
void RemoteLinuxRunConfiguration::setArguments(const QString &args)
{
d->arguments = QtcProcess::splitArgs(args); // TODO: Widget should be list-based.
d->arguments = QtcProcess::splitArgs(args, OsTypeLinux); // TODO: Widget should be list-based.
}
QString RemoteLinuxRunConfiguration::workingDirectory() const

File diff suppressed because it is too large Load Diff