diff options
author | hjk <hjk121@nokiamail.com> | 2014-02-05 10:43:21 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@digia.com> | 2014-02-19 09:19:28 +0100 |
commit | c68ebeed2e47c7a78d790bb732a080edaf07d9ba (patch) | |
tree | ab861674989f5b4aaf8f8f70275d53a2e075b0f2 | |
parent | a0d29eeb03cfe51ed929a83374c18e3301fefdaf (diff) | |
download | qt-creator-c68ebeed2e47c7a78d790bb732a080edaf07d9ba.tar.gz |
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>
-rw-r--r-- | src/libs/utils/consoleprocess_unix.cpp | 34 | ||||
-rw-r--r-- | src/libs/utils/consoleprocess_win.cpp | 5 | ||||
-rw-r--r-- | src/libs/utils/qtcprocess.cpp | 1311 | ||||
-rw-r--r-- | src/libs/utils/qtcprocess.h | 63 | ||||
-rw-r--r-- | src/plugins/coreplugin/fileutils.cpp | 7 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.cpp | 25 | ||||
-rw-r--r-- | src/plugins/debugger/lldb/lldbengine.cpp | 12 | ||||
-rw-r--r-- | src/plugins/git/gitclient.cpp | 2 | ||||
-rw-r--r-- | src/plugins/ios/iosrunconfiguration.cpp | 33 | ||||
-rw-r--r-- | src/plugins/projectexplorer/gcctoolchain.cpp | 9 | ||||
-rw-r--r-- | src/plugins/projectexplorer/processparameters.cpp | 14 | ||||
-rw-r--r-- | src/plugins/remotelinux/linuxdeviceprocess.cpp | 2 | ||||
-rw-r--r-- | src/plugins/remotelinux/remotelinuxrunconfiguration.cpp | 2 | ||||
-rw-r--r-- | tests/auto/qtcprocess/tst_qtcprocess.cpp | 912 |
14 files changed, 1246 insertions, 1185 deletions
diff --git a/src/libs/utils/consoleprocess_unix.cpp b/src/libs/utils/consoleprocess_unix.cpp index 331e65a0ae..cc9474dbb5 100644 --- a/src/libs/utils/consoleprocess_unix.cpp +++ b/src/libs/utils/consoleprocess_unix.cpp @@ -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,12 +92,15 @@ 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, - &d->m_environment, &d->m_workingDir); + 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 ? tr("Quoting error in terminal command.") @@ -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 " diff --git a/src/libs/utils/consoleprocess_win.cpp b/src/libs/utils/consoleprocess_win.cpp index 646cf11806..9c6df8104b 100644 --- a/src/libs/utils/consoleprocess_win.cpp +++ b/src/libs/utils/consoleprocess_win.cpp @@ -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(); diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index fed14afa90..b6e13b1cca 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -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, - const Environment *env, const QString *pwd) +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, - const Environment *env, const QString *pwd) + +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,8 +317,9 @@ 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, - const Environment *env, const QString *pwd) +static QStringList splitArgsUnix(const QString &args, bool abortOnMeta, + QtcProcess::SplitError *err, + const Environment *env, const QString *pwd) { static const QString pwdName = QLatin1String("PWD"); QStringList ret; @@ -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, - const Environment *env, const QString *pwd) -{ - QtcProcess::SplitError err; - *outArgs = QtcProcess::prepareArgs(arguments, &err, env, pwd); - if (err == QtcProcess::SplitOk) { - *outCmd = command; - } else { - *outCmd = QString::fromLatin1(qgetenv("COMSPEC")); - *outArgs = 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, + 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 (err != QtcProcess::FoundMeta) - return false; - *outCmd = QLatin1String("/bin/sh"); - *outArgs << QLatin1String("-c") << (quoteArg(command) + QLatin1Char(' ') + arguments); + if (osType == OsTypeWindows) { + *outCmd = QString::fromLatin1(qgetenv("COMSPEC")); + *outArgs = Arguments::createWindowsArgs(QLatin1String("/v:off /s /c \"") + + quoteArg(QDir::toNativeSeparators(command)) + QLatin1Char(' ') + arguments + + QLatin1Char('"')); + } else { + if (err != QtcProcess::FoundMeta) + return false; + *outCmd = QLatin1String("/bin/sh"); + *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 (env.constFind(libraryPath) == env.constEnd()) - env.set(libraryPath, QString()); -#endif + 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()); + } 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); - if (m_useCtrlCStub) { - argList << 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)) { - 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); + setNativeArguments(args); #endif + if (m_useCtrlCStub) { + args = QDir::toNativeSeparators(command); + command = QCoreApplication::applicationDirPath() + QLatin1String("/qtcreator_ctrlc_stub.exe"); + } + 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.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,319 +876,320 @@ bool QtcProcess::expandMacros(QString *cmd, AbstractMacroExpander *mx) int pos = 0; -#ifdef Q_OS_WIN - enum { // cmd.exe parsing state - ShellBasic, // initial state - ShellQuoted, // double-quoted state => *no* other meta chars are interpreted - ShellEscaped // circumflex-escaped state => next char is not interpreted - } shellState = ShellBasic; - enum { // CommandLineToArgv() parsing state and some more - CrtBasic, // initial state - CrtNeedWord, // after empty expando; insert empty argument if whitespace follows - CrtInWord, // in non-whitespace - CrtClosed, // previous char closed the double-quoting - CrtHadQuote, // closed double-quoting after an expando - // The remaining two need to be numerically higher - CrtQuoted, // double-quoted state => spaces don't split tokens - CrtNeedQuote // expando opened quote; close if no expando follows - } crtState = CrtBasic; - int bslashes = 0; // previous chars were manual backslashes - int rbslashes = 0; // trailing backslashes in replacement + if (osType == OsTypeWindows) { + enum { // cmd.exe parsing state + ShellBasic, // initial state + ShellQuoted, // double-quoted state => *no* other meta chars are interpreted + ShellEscaped // circumflex-escaped state => next char is not interpreted + } shellState = ShellBasic; + enum { // CommandLineToArgv() parsing state and some more + CrtBasic, // initial state + CrtNeedWord, // after empty expando; insert empty argument if whitespace follows + CrtInWord, // in non-whitespace + CrtClosed, // previous char closed the double-quoting + CrtHadQuote, // closed double-quoting after an expando + // The remaining two need to be numerically higher + CrtQuoted, // double-quoted state => spaces don't split tokens + CrtNeedQuote // expando opened quote; close if no expando follows + } crtState = CrtBasic; + int bslashes = 0; // previous chars were manual backslashes + int rbslashes = 0; // trailing backslashes in replacement - forever { - if (pos == varPos) { - if (shellState == ShellEscaped) - return false; // Circumflex'd quoted expando would be Bad (TM). - if ((shellState == ShellQuoted) != (crtState == CrtQuoted)) - return false; // CRT quoting out of sync with shell quoting. Ahoy to Redmond. - rbslashes += bslashes; - bslashes = 0; - if (crtState < CrtQuoted) { - if (rsts.isEmpty()) { - if (crtState == CrtBasic) { - // Outside any quoting and the string is empty, so put - // a pair of quotes. Delaying that is just pedantry. - crtState = CrtNeedWord; - } - } else { - if (hasSpecialChars(rsts)) { - if (crtState == CrtClosed) { - // Quoted expando right after closing quote. Can't do that. - return false; + forever { + if (pos == varPos) { + if (shellState == ShellEscaped) + return false; // Circumflex'd quoted expando would be Bad (TM). + if ((shellState == ShellQuoted) != (crtState == CrtQuoted)) + return false; // CRT quoting out of sync with shell quoting. Ahoy to Redmond. + rbslashes += bslashes; + bslashes = 0; + if (crtState < CrtQuoted) { + if (rsts.isEmpty()) { + if (crtState == CrtBasic) { + // Outside any quoting and the string is empty, so put + // a pair of quotes. Delaying that is just pedantry. + crtState = CrtNeedWord; } - int tbslashes = quoteArgInternal(rsts, 0); - rsts.prepend(QLatin1Char('"')); - if (rbslashes) - rsts.prepend(QString(rbslashes, QLatin1Char('\\'))); - crtState = CrtNeedQuote; - rbslashes = tbslashes; } else { - 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); + if (hasSpecialCharsWin(rsts)) { + if (crtState == CrtClosed) { + // Quoted expando right after closing quote. Can't do that. + return false; + } + int tbslashes = quoteArgInternalWin(rsts, 0); + rsts.prepend(QLatin1Char('"')); + if (rbslashes) + rsts.prepend(QString(rbslashes, QLatin1Char('\\'))); + crtState = CrtNeedQuote; + rbslashes = tbslashes; + } else { + 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 = quoteArgInternalWin(rsts, rbslashes); + } } + } else { + rbslashes = quoteArgInternalWin(rsts, rbslashes); } - } else { - rbslashes = quoteArgInternal(rsts, rbslashes); - } - str.replace(pos, varLen, rsts); - pos += rsts.length(); - varPos = pos; - if (!(varLen = mx->findMacro(str, &varPos, &rsts))) { - // Don't leave immediately, as we may be in CrtNeedWord state which could - // be still resolved, or we may have inserted trailing backslashes. - varPos = INT_MAX; - } - continue; - } - if (crtState == CrtNeedQuote) { - if (rbslashes) { - str.insert(pos, QString(rbslashes, QLatin1Char('\\'))); - pos += rbslashes; - varPos += rbslashes; - rbslashes = 0; + str.replace(pos, varLen, rsts); + pos += rsts.length(); + varPos = pos; + if (!(varLen = mx->findMacro(str, &varPos, &rsts))) { + // Don't leave immediately, as we may be in CrtNeedWord state which could + // be still resolved, or we may have inserted trailing backslashes. + varPos = INT_MAX; + } + continue; } - str.insert(pos, QLatin1Char('"')); - pos++; - varPos++; - crtState = CrtHadQuote; - } - ushort cc = str.unicode()[pos].unicode(); - if (shellState == ShellBasic && cc == '^') { - shellState = ShellEscaped; - } else { - if (!cc || cc == ' ' || cc == '\t') { - if (crtState < CrtQuoted) { - if (crtState == CrtNeedWord) { - str.insert(pos, QLatin1String("\"\"")); - pos += 2; - varPos += 2; - } - crtState = CrtBasic; + if (crtState == CrtNeedQuote) { + if (rbslashes) { + str.insert(pos, QString(rbslashes, QLatin1Char('\\'))); + pos += rbslashes; + varPos += rbslashes; + rbslashes = 0; } - if (!cc) - break; - bslashes = 0; - rbslashes = 0; + str.insert(pos, QLatin1Char('"')); + pos++; + varPos++; + crtState = CrtHadQuote; + } + ushort cc = str.unicode()[pos].unicode(); + if (shellState == ShellBasic && cc == '^') { + shellState = ShellEscaped; } else { - if (cc == '\\') { - bslashes++; - if (crtState < CrtQuoted) - crtState = CrtInWord; - } else { - if (cc == '"') { - if (shellState != ShellEscaped) - shellState = (shellState == ShellQuoted) ? ShellBasic : ShellQuoted; - if (rbslashes) { - // Offset -1: skip possible circumflex. We have at least - // one backslash, so a fixed offset is ok. - str.insert(pos - 1, QString(rbslashes, QLatin1Char('\\'))); - pos += rbslashes; - varPos += rbslashes; + if (!cc || cc == ' ' || cc == '\t') { + if (crtState < CrtQuoted) { + if (crtState == CrtNeedWord) { + str.insert(pos, QLatin1String("\"\"")); + pos += 2; + varPos += 2; } - if (!(bslashes & 1)) { - // Even number of backslashes, so the quote is not escaped. - switch (crtState) { - case CrtQuoted: - // Closing quote - crtState = CrtClosed; - break; - case CrtClosed: - // Two consecutive quotes make a literal quote - and - // still close quoting. See QtcProcess::quoteArg(). + crtState = CrtBasic; + } + if (!cc) + break; + bslashes = 0; + rbslashes = 0; + } else { + if (cc == '\\') { + bslashes++; + if (crtState < CrtQuoted) + crtState = CrtInWord; + } else { + if (cc == '"') { + if (shellState != ShellEscaped) + shellState = (shellState == ShellQuoted) ? ShellBasic : ShellQuoted; + if (rbslashes) { + // Offset -1: skip possible circumflex. We have at least + // one backslash, so a fixed offset is ok. + str.insert(pos - 1, QString(rbslashes, QLatin1Char('\\'))); + pos += rbslashes; + varPos += rbslashes; + } + if (!(bslashes & 1)) { + // Even number of backslashes, so the quote is not escaped. + switch (crtState) { + case CrtQuoted: + // Closing quote + crtState = CrtClosed; + break; + case CrtClosed: + // Two consecutive quotes make a literal quote - and + // still close quoting. See QtcProcess::quoteArg(). + crtState = CrtInWord; + break; + case CrtHadQuote: + // Opening quote right after quoted expando. Can't do that. + return false; + default: + // Opening quote + crtState = CrtQuoted; + break; + } + } else if (crtState < CrtQuoted) { crtState = CrtInWord; - break; - case CrtHadQuote: - // Opening quote right after quoted expando. Can't do that. - return false; - default: - // Opening quote - crtState = CrtQuoted; - break; } } else if (crtState < CrtQuoted) { crtState = CrtInWord; } - } else if (crtState < CrtQuoted) { - crtState = CrtInWord; + bslashes = 0; + rbslashes = 0; } - bslashes = 0; - rbslashes = 0; } + if (varPos == INT_MAX && !rbslashes) + break; + if (shellState == ShellEscaped) + shellState = ShellBasic; } - if (varPos == INT_MAX && !rbslashes) - break; - if (shellState == ShellEscaped) - shellState = ShellBasic; - } - pos++; - } -#else - MxState state = { MxBasic, false }; - QStack<MxState> sstack; - QStack<MxSave> ostack; - - while (pos < str.length()) { - if (pos == varPos) { - // Our expansion rules trigger in any context - if (state.dquote) { - // We are within a double-quoted string. Escape relevant meta characters. - rsts.replace(QRegExp(QLatin1String("([$`\"\\\\])")), QLatin1String("\\\\1")); - } else if (state.current == MxSingleQuote) { - // We are within a single-quoted string. "Suspend" single-quoting and put a - // single escaped quote for each single quote inside the string. - rsts.replace(QLatin1Char('\''), QLatin1String("'\\''")); - } else if (rsts.isEmpty() || hasSpecialCharsUnix(rsts)) { - // String contains "quote-worthy" characters. Use single quoting - but - // that choice is arbitrary. - rsts.replace(QLatin1Char('\''), QLatin1String("'\\''")); - rsts.prepend(QLatin1Char('\'')); - rsts.append(QLatin1Char('\'')); - } // Else just use the string verbatim. - str.replace(pos, varLen, rsts); - pos += rsts.length(); - varPos = pos; - if (!(varLen = mx->findMacro(str, &varPos, &rsts))) - break; - continue; + pos++; } - ushort cc = str.unicode()[pos].unicode(); - if (state.current == MxSingleQuote) { - // Single quoted context - only the single quote has any special meaning. - if (cc == '\'') - state = sstack.pop(); - } else if (cc == '\\') { - // In any other context, the backslash starts an escape. - pos += 2; - if (varPos < pos) - return false; // Backslash'd quoted expando would be Bad (TM). - continue; - } else if (cc == '$') { - cc = str.unicode()[++pos].unicode(); - if (cc == '(') { - sstack.push(state); - if (str.unicode()[pos + 1].unicode() == '(') { - // $(( starts a math expression. This may also be a $( ( in fact, - // so we push the current string and offset on a stack so we can retry. - MxSave sav = { str, pos + 2, varPos }; - ostack.push(sav); - state.current = MxMath; - pos += 2; - continue; - } else { - // $( starts a command substitution. This actually "opens a new context" - // which overrides surrounding double quoting. - state.current = MxParen; - state.dquote = false; - } - } else if (cc == '{') { - // ${ starts a "braced" variable substitution. - sstack.push(state); - state.current = MxSubst; - } // Else assume that a "bare" variable substitution has started - } else if (cc == '`') { - // Backticks are evil, as every shell interprets escapes within them differently, - // which is a danger for the quoting of our own expansions. - // So we just apply *our* rules (which match bash) and transform it into a POSIX - // command substitution which has clear semantics. - str.replace(pos, 1, QLatin1String("$( " )); // add space -> avoid creating $(( - varPos += 2; - int pos2 = pos += 3; - forever { - if (pos2 >= str.length()) - return false; // Syntax error - unterminated backtick expression. - cc = str.unicode()[pos2].unicode(); - if (cc == '`') + } else { + // !Windows + MxState state = { MxBasic, false }; + QStack<MxState> sstack; + QStack<MxSave> ostack; + + while (pos < str.length()) { + if (pos == varPos) { + // Our expansion rules trigger in any context + if (state.dquote) { + // We are within a double-quoted string. Escape relevant meta characters. + rsts.replace(QRegExp(QLatin1String("([$`\"\\\\])")), QLatin1String("\\\\1")); + } else if (state.current == MxSingleQuote) { + // We are within a single-quoted string. "Suspend" single-quoting and put a + // single escaped quote for each single quote inside the string. + rsts.replace(QLatin1Char('\''), QLatin1String("'\\''")); + } else if (rsts.isEmpty() || hasSpecialCharsUnix(rsts)) { + // String contains "quote-worthy" characters. Use single quoting - but + // that choice is arbitrary. + rsts.replace(QLatin1Char('\''), QLatin1String("'\\''")); + rsts.prepend(QLatin1Char('\'')); + rsts.append(QLatin1Char('\'')); + } // Else just use the string verbatim. + str.replace(pos, varLen, rsts); + pos += rsts.length(); + varPos = pos; + if (!(varLen = mx->findMacro(str, &varPos, &rsts))) break; - if (cc == '\\') { - cc = str.unicode()[++pos2].unicode(); - if (cc == '$' || cc == '`' || cc == '\\' || - (cc == '"' && state.dquote)) - { - str.remove(pos2 - 1, 1); - if (varPos >= pos2) - varPos--; + continue; + } + ushort cc = str.unicode()[pos].unicode(); + if (state.current == MxSingleQuote) { + // Single quoted context - only the single quote has any special meaning. + if (cc == '\'') + state = sstack.pop(); + } else if (cc == '\\') { + // In any other context, the backslash starts an escape. + pos += 2; + if (varPos < pos) + return false; // Backslash'd quoted expando would be Bad (TM). + continue; + } else if (cc == '$') { + cc = str.unicode()[++pos].unicode(); + if (cc == '(') { + sstack.push(state); + if (str.unicode()[pos + 1].unicode() == '(') { + // $(( starts a math expression. This may also be a $( ( in fact, + // so we push the current string and offset on a stack so we can retry. + MxSave sav = { str, pos + 2, varPos }; + ostack.push(sav); + state.current = MxMath; + pos += 2; continue; + } else { + // $( starts a command substitution. This actually "opens a new context" + // which overrides surrounding double quoting. + state.current = MxParen; + state.dquote = false; + } + } else if (cc == '{') { + // ${ starts a "braced" variable substitution. + sstack.push(state); + state.current = MxSubst; + } // Else assume that a "bare" variable substitution has started + } else if (cc == '`') { + // Backticks are evil, as every shell interprets escapes within them differently, + // which is a danger for the quoting of our own expansions. + // So we just apply *our* rules (which match bash) and transform it into a POSIX + // command substitution which has clear semantics. + str.replace(pos, 1, QLatin1String("$( " )); // add space -> avoid creating $(( + varPos += 2; + int pos2 = pos += 3; + forever { + if (pos2 >= str.length()) + return false; // Syntax error - unterminated backtick expression. + cc = str.unicode()[pos2].unicode(); + if (cc == '`') + break; + if (cc == '\\') { + cc = str.unicode()[++pos2].unicode(); + if (cc == '$' || cc == '`' || cc == '\\' || + (cc == '"' && state.dquote)) + { + str.remove(pos2 - 1, 1); + if (varPos >= pos2) + varPos--; + continue; + } } + pos2++; } - pos2++; - } - str[pos2] = QLatin1Char(')'); - sstack.push(state); - state.current = MxParen; - state.dquote = false; - continue; - } else if (state.current == MxDoubleQuote) { - // (Truly) double quoted context - only remaining special char is the closing quote. - if (cc == '"') - state = sstack.pop(); - } else if (cc == '\'') { - // Start single quote if we are not in "inherited" double quoted context. - if (!state.dquote) { - sstack.push(state); - state.current = MxSingleQuote; - } - } else if (cc == '"') { - // Same for double quoting. - if (!state.dquote) { + str[pos2] = QLatin1Char(')'); sstack.push(state); - state.current = MxDoubleQuote; - state.dquote = true; - } - } else if (state.current == MxSubst) { - // "Braced" substitution context - only remaining special char is the closing brace. - if (cc == '}') - state = sstack.pop(); - } else if (cc == ')') { - if (state.current == MxMath) { - if (str.unicode()[pos + 1].unicode() == ')') { + state.current = MxParen; + state.dquote = false; + continue; + } else if (state.current == MxDoubleQuote) { + // (Truly) double quoted context - only remaining special char is the closing quote. + if (cc == '"') state = sstack.pop(); - pos += 2; - } else { - // False hit: the $(( was a $( ( in fact. - // ash does not care (and will complain), but bash actually parses it. - varPos = ostack.top().varPos; - pos = ostack.top().pos; - str = ostack.top().str; - ostack.pop(); - state.current = MxParen; - state.dquote = false; + } else if (cc == '\'') { + // Start single quote if we are not in "inherited" double quoted context. + if (!state.dquote) { sstack.push(state); + state.current = MxSingleQuote; } - continue; - } else if (state.current == MxParen) { - state = sstack.pop(); - } else { - break; // Syntax error - excess closing parenthesis. + } else if (cc == '"') { + // Same for double quoting. + if (!state.dquote) { + sstack.push(state); + state.current = MxDoubleQuote; + state.dquote = true; + } + } else if (state.current == MxSubst) { + // "Braced" substitution context - only remaining special char is the closing brace. + if (cc == '}') + state = sstack.pop(); + } else if (cc == ')') { + if (state.current == MxMath) { + if (str.unicode()[pos + 1].unicode() == ')') { + state = sstack.pop(); + pos += 2; + } else { + // False hit: the $(( was a $( ( in fact. + // ash does not care (and will complain), but bash actually parses it. + varPos = ostack.top().varPos; + pos = ostack.top().pos; + str = ostack.top().str; + ostack.pop(); + state.current = MxParen; + state.dquote = false; + sstack.push(state); + } + continue; + } else if (state.current == MxParen) { + state = sstack.pop(); + } else { + break; // Syntax error - excess closing parenthesis. + } + } else if (cc == '}') { + if (state.current == MxGroup) + state = sstack.pop(); + else + break; // Syntax error - excess closing brace. + } else if (cc == '(') { + // Context-saving command grouping. + sstack.push(state); + state.current = MxParen; + } else if (cc == '{') { + // Plain command grouping. + sstack.push(state); + state.current = MxGroup; } - } else if (cc == '}') { - if (state.current == MxGroup) - state = sstack.pop(); - else - break; // Syntax error - excess closing brace. - } else if (cc == '(') { - // Context-saving command grouping. - sstack.push(state); - state.current = MxParen; - } else if (cc == '{') { - // Plain command grouping. - sstack.push(state); - state.current = MxGroup; + pos++; } - pos++; + // FIXME? May complain if (!sstack.empty()), but we don't really care anyway. } - // 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,252 +1202,252 @@ bool QtcProcess::ArgIterator::next() m_simple = true; m_value.clear(); -#ifdef Q_OS_WIN - enum { // cmd.exe parsing state - ShellBasic, // initial state - ShellQuoted, // double-quoted state => *no* other meta chars are interpreted - ShellEscaped // circumflex-escaped state => next char is not interpreted - } shellState = ShellBasic; - enum { // CommandLineToArgv() parsing state and some more - CrtBasic, // initial state - CrtInWord, // in non-whitespace - CrtClosed, // previous char closed the double-quoting - CrtQuoted // double-quoted state => spaces don't split tokens - } crtState = CrtBasic; - enum { NoVar, NewVar, FullVar } varState = NoVar; // inside a potential env variable expansion - int bslashes = 0; // number of preceding backslashes - - for (;; m_pos++) { - ushort cc = m_pos < m_str->length() ? m_str->unicode()[m_pos].unicode() : 0; - if (shellState == ShellBasic && cc == '^') { - varState = NoVar; - shellState = ShellEscaped; - } else if ((shellState == ShellBasic && isMetaChar(cc)) || !cc) { // A "bit" simplistic ... - // We ignore crtQuote state here. Whatever ... - doReturn: - if (m_simple) - while (--bslashes >= 0) - m_value += QLatin1Char('\\'); - else - m_value.clear(); - if (crtState != CrtBasic) { - m_prev = prev; - return true; - } - return false; - } else { - if (crtState != CrtQuoted && (cc == ' ' || cc == '\t')) { + if (m_osType == OsTypeWindows) { + enum { // cmd.exe parsing state + ShellBasic, // initial state + ShellQuoted, // double-quoted state => *no* other meta chars are interpreted + ShellEscaped // circumflex-escaped state => next char is not interpreted + } shellState = ShellBasic; + enum { // CommandLineToArgv() parsing state and some more + CrtBasic, // initial state + CrtInWord, // in non-whitespace + CrtClosed, // previous char closed the double-quoting + CrtQuoted // double-quoted state => spaces don't split tokens + } crtState = CrtBasic; + enum { NoVar, NewVar, FullVar } varState = NoVar; // inside a potential env variable expansion + int bslashes = 0; // number of preceding backslashes + + for (;; m_pos++) { + ushort cc = m_pos < m_str->length() ? m_str->unicode()[m_pos].unicode() : 0; + if (shellState == ShellBasic && cc == '^') { + varState = NoVar; + shellState = ShellEscaped; + } else if ((shellState == ShellBasic && isMetaCharWin(cc)) || !cc) { // A "bit" simplistic ... + // We ignore crtQuote state here. Whatever ... + doReturn: + if (m_simple) + while (--bslashes >= 0) + m_value += QLatin1Char('\\'); + else + m_value.clear(); if (crtState != CrtBasic) { - // We'll lose shellQuote state here. Whatever ... - goto doReturn; + m_prev = prev; + return true; } + return false; } else { - if (cc == '\\') { - bslashes++; - if (crtState != CrtQuoted) - crtState = CrtInWord; - varState = NoVar; + if (crtState != CrtQuoted && (cc == ' ' || cc == '\t')) { + if (crtState != CrtBasic) { + // We'll lose shellQuote state here. Whatever ... + goto doReturn; + } } else { - if (cc == '"') { - varState = NoVar; - if (shellState != ShellEscaped) - shellState = (shellState == ShellQuoted) ? ShellBasic : ShellQuoted; - int obslashes = bslashes; - bslashes >>= 1; - if (!(obslashes & 1)) { - // Even number of backslashes, so the quote is not escaped. - switch (crtState) { - case CrtQuoted: - // Closing quote - crtState = CrtClosed; - continue; - case CrtClosed: - // Two consecutive quotes make a literal quote - and - // still close quoting. See quoteArg(). - crtState = CrtInWord; - break; - default: - // Opening quote - crtState = CrtQuoted; - continue; - } - } else if (crtState != CrtQuoted) { + if (cc == '\\') { + bslashes++; + if (crtState != CrtQuoted) crtState = CrtInWord; - } + varState = NoVar; } else { - if (cc == '%') { - if (varState == FullVar) { - m_simple = false; - varState = NoVar; - } else { - varState = NewVar; + if (cc == '"') { + varState = NoVar; + if (shellState != ShellEscaped) + shellState = (shellState == ShellQuoted) ? ShellBasic : ShellQuoted; + int obslashes = bslashes; + bslashes >>= 1; + if (!(obslashes & 1)) { + // Even number of backslashes, so the quote is not escaped. + switch (crtState) { + case CrtQuoted: + // Closing quote + crtState = CrtClosed; + continue; + case CrtClosed: + // Two consecutive quotes make a literal quote - and + // still close quoting. See quoteArg(). + crtState = CrtInWord; + break; + default: + // Opening quote + crtState = CrtQuoted; + continue; + } + } else if (crtState != CrtQuoted) { + crtState = CrtInWord; } - } else if (varState != NoVar) { - // This check doesn't really reflect cmd reality, but it is an - // approximation of what would be sane. - varState = (cc == '_' || cc == '-' || cc == '.' - || QChar(cc).isLetterOrNumber()) ? FullVar : NoVar; + } else { + if (cc == '%') { + if (varState == FullVar) { + m_simple = false; + varState = NoVar; + } else { + varState = NewVar; + } + } else if (varState != NoVar) { + // This check doesn't really reflect cmd reality, but it is an + // approximation of what would be sane. + varState = (cc == '_' || cc == '-' || cc == '.' + || QChar(cc).isLetterOrNumber()) ? FullVar : NoVar; + } + if (crtState != CrtQuoted) + crtState = CrtInWord; } - if (crtState != CrtQuoted) - crtState = CrtInWord; + for (; bslashes > 0; bslashes--) + m_value += QLatin1Char('\\'); + m_value += QChar(cc); } - for (; bslashes > 0; bslashes--) - m_value += QLatin1Char('\\'); - m_value += QChar(cc); } + if (shellState == ShellEscaped) + shellState = ShellBasic; } - if (shellState == ShellEscaped) - shellState = ShellBasic; } - } -#else - MxState state = { MxBasic, false }; - QStack<MxState> sstack; - QStack<int> ostack; - bool hadWord = false; - - for (; m_pos < m_str->length(); m_pos++) { - ushort cc = m_str->unicode()[m_pos].unicode(); - if (state.current == MxSingleQuote) { - if (cc == '\'') { - state = sstack.pop(); - continue; - } - } else if (cc == '\\') { - if (++m_pos >= m_str->length()) - break; - cc = m_str->unicode()[m_pos].unicode(); - if (state.dquote && cc != '"' && cc != '\\' && cc != '$' && cc != '`') - m_value += QLatin1Char('\\'); - } else if (cc == '$') { - if (++m_pos >= m_str->length()) - break; - cc = m_str->unicode()[m_pos].unicode(); - if (cc == '(') { - sstack.push(state); + } else { + MxState state = { MxBasic, false }; + QStack<MxState> sstack; + QStack<int> ostack; + bool hadWord = false; + + for (; m_pos < m_str->length(); m_pos++) { + ushort cc = m_str->unicode()[m_pos].unicode(); + if (state.current == MxSingleQuote) { + if (cc == '\'') { + state = sstack.pop(); + continue; + } + } else if (cc == '\\') { + if (++m_pos >= m_str->length()) + break; + cc = m_str->unicode()[m_pos].unicode(); + if (state.dquote && cc != '"' && cc != '\\' && cc != '$' && cc != '`') + m_value += QLatin1Char('\\'); + } else if (cc == '$') { if (++m_pos >= m_str->length()) break; - if (m_str->unicode()[m_pos].unicode() == '(') { - ostack.push(m_pos); - state.current = MxMath; + cc = m_str->unicode()[m_pos].unicode(); + if (cc == '(') { + sstack.push(state); + if (++m_pos >= m_str->length()) + break; + if (m_str->unicode()[m_pos].unicode() == '(') { + ostack.push(m_pos); + state.current = MxMath; + } else { + state.dquote = false; + state.current = MxParen; + // m_pos too far by one now - whatever. + } + } else if (cc == '{') { + sstack.push(state); + state.current = MxSubst; } else { - state.dquote = false; - state.current = MxParen; // m_pos too far by one now - whatever. } - } else if (cc == '{') { - sstack.push(state); - state.current = MxSubst; - } else { - // m_pos too far by one now - whatever. - } - m_simple = false; - hadWord = true; - continue; - } else if (cc == '`') { - forever { - if (++m_pos >= m_str->length()) { - m_simple = false; - m_prev = prev; - return true; - } - cc = m_str->unicode()[m_pos].unicode(); - if (cc == '`') - break; - if (cc == '\\') - m_pos++; // m_pos may be too far by one now - whatever. - } - m_simple = false; - hadWord = true; - continue; - } else if (state.current == MxDoubleQuote) { - if (cc == '"') { - state = sstack.pop(); - continue; - } - } else if (cc == '\'') { - if (!state.dquote) { - sstack.push(state); - state.current = MxSingleQuote; + m_simple = false; hadWord = true; continue; - } - } else if (cc == '"') { - if (!state.dquote) { - sstack.push(state); - state.dquote = true; - state.current = MxDoubleQuote; + } else if (cc == '`') { + forever { + if (++m_pos >= m_str->length()) { + m_simple = false; + m_prev = prev; + return true; + } + cc = m_str->unicode()[m_pos].unicode(); + if (cc == '`') + break; + if (cc == '\\') + m_pos++; // m_pos may be too far by one now - whatever. + } + m_simple = false; hadWord = true; continue; - } - } else if (state.current == MxSubst) { - if (cc == '}') - state = sstack.pop(); - continue; // Not simple anyway - } else if (cc == ')') { - if (state.current == MxMath) { - if (++m_pos >= m_str->length()) - break; - if (m_str->unicode()[m_pos].unicode() == ')') { - ostack.pop(); + } else if (state.current == MxDoubleQuote) { + if (cc == '"') { state = sstack.pop(); - } else { - // false hit: the $(( was a $( ( in fact. - // ash does not care, but bash does. - m_pos = ostack.pop(); - state.current = MxParen; - state.dquote = false; + continue; + } + } else if (cc == '\'') { + if (!state.dquote) { sstack.push(state); + state.current = MxSingleQuote; + hadWord = true; + continue; + } + } else if (cc == '"') { + if (!state.dquote) { + sstack.push(state); + state.dquote = true; + state.current = MxDoubleQuote; + hadWord = true; + continue; + } + } else if (state.current == MxSubst) { + if (cc == '}') + state = sstack.pop(); + continue; // Not simple anyway + } else if (cc == ')') { + if (state.current == MxMath) { + if (++m_pos >= m_str->length()) + break; + if (m_str->unicode()[m_pos].unicode() == ')') { + ostack.pop(); + state = sstack.pop(); + } else { + // false hit: the $(( was a $( ( in fact. + // ash does not care, but bash does. + m_pos = ostack.pop(); + state.current = MxParen; + state.dquote = false; + sstack.push(state); + } + continue; + } else if (state.current == MxParen) { + state = sstack.pop(); + continue; + } else { + break; } - continue; - } else if (state.current == MxParen) { - state = sstack.pop(); - continue; - } else { - break; - } #if 0 // MxGroup is impossible, see below. - } else if (cc == '}') { - if (state.current == MxGroup) { - state = sstack.pop(); - continue; - } + } else if (cc == '}') { + if (state.current == MxGroup) { + state = sstack.pop(); + continue; + } #endif - } else if (cc == '(') { - sstack.push(state); - state.current = MxParen; - m_simple = false; - hadWord = true; + } else if (cc == '(') { + sstack.push(state); + state.current = MxParen; + m_simple = false; + hadWord = true; #if 0 // Should match only at the beginning of a command, which we never have currently. - } else if (cc == '{') { - sstack.push(state); - state.current = MxGroup; - m_simple = false; - hadWord = true; - continue; -#endif - } else if (cc == '<' || cc == '>' || cc == '&' || cc == '|' || cc == ';') { - if (sstack.isEmpty()) - break; - } else if (cc == ' ' || cc == '\t') { - if (!hadWord) + } else if (cc == '{') { + sstack.push(state); + state.current = MxGroup; + m_simple = false; + hadWord = true; continue; - if (sstack.isEmpty()) - break; +#endif + } else if (cc == '<' || cc == '>' || cc == '&' || cc == '|' || cc == ';') { + if (sstack.isEmpty()) + break; + } else if (cc == ' ' || cc == '\t') { + if (!hadWord) + continue; + if (sstack.isEmpty()) + break; + } + m_value += QChar(cc); + hadWord = true; } - m_value += QChar(cc); - hadWord = true; - } - // TODO: Possibly complain here if (!sstack.empty()) - if (!m_simple) - m_value.clear(); - if (hadWord) { - m_prev = prev; - return true; + // TODO: Possibly complain here if (!sstack.empty()) + if (!m_simple) + m_value.clear(); + if (hadWord) { + m_prev = prev; + return true; + } + return false; } - 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 diff --git a/src/libs/utils/qtcprocess.h b/src/libs/utils/qtcprocess.h index 66cbcbda5b..ca1dff19fc 100644 --- a/src/libs/utils/qtcprocess.h +++ b/src/libs/utils/qtcprocess.h @@ -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, - const Environment *env = 0, const QString *pwd = 0); + 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(); } diff --git a/src/plugins/coreplugin/fileutils.cpp b/src/plugins/coreplugin/fileutils.cpp index 296beb336d..214ad3a66d 100644 --- a/src/plugins/coreplugin/fileutils.cpp +++ b/src/plugins/coreplugin/fileutils.cpp @@ -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"))); } diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index f8ef1af82e..b828ceaac0 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -4771,19 +4771,20 @@ 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 (perr != Utils::QtcProcess::SplitOk) { - // perr == BadQuoting is never returned on Windows - // FIXME? QTCREATORBUG-2809 - handleAdapterStartFailed(QCoreApplication::translate("DebuggerEngine", // Same message in CdbEngine - "Debugging complex command lines is currently not supported on Windows."), Core::Id()); - return false; + 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 + handleAdapterStartFailed(QCoreApplication::translate("DebuggerEngine", // Same message in CdbEngine + "Debugging complex command lines is currently not supported on Windows."), Core::Id()); + return false; + } } -#endif return true; } diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 54ac32b28e..f4a7e1da21 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -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 diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 953033ca35..fbe39458f6 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -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); diff --git a/src/plugins/ios/iosrunconfiguration.cpp b/src/plugins/ios/iosrunconfiguration.cpp index b097c490df..4042c4b478 100644 --- a/src/plugins/ios/iosrunconfiguration.cpp +++ b/src/plugins/ios/iosrunconfiguration.cpp @@ -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; } diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index ca63fc800d..59647310cc 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -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; diff --git a/src/plugins/projectexplorer/processparameters.cpp b/src/plugins/projectexplorer/processparameters.cpp index e702292c5c..711209b872 100644 --- a/src/plugins/projectexplorer/processparameters.cpp +++ b/src/plugins/projectexplorer/processparameters.cpp @@ -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 diff --git a/src/plugins/remotelinux/linuxdeviceprocess.cpp b/src/plugins/remotelinux/linuxdeviceprocess.cpp index c6ec627708..cf60d6a2e0 100644 --- a/src/plugins/remotelinux/linuxdeviceprocess.cpp +++ b/src/plugins/remotelinux/linuxdeviceprocess.cpp @@ -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; } diff --git a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp index 2e5d1b7d37..90bfa3123e 100644 --- a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp @@ -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 diff --git a/tests/auto/qtcprocess/tst_qtcprocess.cpp b/tests/auto/qtcprocess/tst_qtcprocess.cpp index 797f08bdb2..a68b278d13 100644 --- a/tests/auto/qtcprocess/tst_qtcprocess.cpp +++ b/tests/auto/qtcprocess/tst_qtcprocess.cpp @@ -28,6 +28,7 @@ ****************************************************************************/ #include <utils/qtcprocess.h> +#include <utils/hostosinfo.h> #include <utils/stringutils.h> #include <utils/environment.h> @@ -57,6 +58,7 @@ class tst_QtcProcess : public QObject private slots: void initTestCase(); + void splitArgs_data(); void splitArgs(); void prepareArgs_data(); @@ -67,120 +69,123 @@ private slots: void expandMacros(); void iterations_data(); void iterations(); - void iteratorEdits(); + void iteratorEditsWindows(); + void iteratorEditsLinux(); private: - Environment env; - MacroMapExpander mx; -#ifdef Q_OS_UNIX + void iteratorEditsHelper(OsType osType); + + Environment envWindows; + Environment envLinux; + + MacroMapExpander mxWin; + MacroMapExpander mxUnix; QString homeStr; QString home; -#endif }; void tst_QtcProcess::initTestCase() { -#ifdef Q_OS_UNIX homeStr = QLatin1String("@HOME@"); home = QDir::homePath(); -#endif - - env.set("empty", ""); - env.set("word", "hi"); - env.set("words", "hi ho"); - env.set("spacedwords", " hi ho sucker "); - -#ifdef Q_OS_WIN - mx.insert("a", "hi"); - mx.insert("aa", "hi ho"); - - mx.insert("b", "h\\i"); - mx.insert("c", "\\hi"); - mx.insert("d", "hi\\"); - mx.insert("ba", "h\\i ho"); - mx.insert("ca", "\\hi ho"); - mx.insert("da", "hi ho\\"); - - mx.insert("e", "h\"i"); - mx.insert("f", "\"hi"); - mx.insert("g", "hi\""); - - mx.insert("h", "h\\\"i"); - mx.insert("i", "\\\"hi"); - mx.insert("j", "hi\\\""); - - mx.insert("k", "&special;"); - - mx.insert("x", "\\"); - mx.insert("y", "\""); -#else - mx.insert("a", "hi"); - mx.insert("b", "hi ho"); - mx.insert("c", "&special;"); - mx.insert("d", "h\\i"); - mx.insert("e", "h\"i"); - mx.insert("f", "h'i"); -#endif - mx.insert("z", ""); + + QStringList env; + env << "empty=" << "word=hi" << "words=hi ho" << "spacedwords= hi ho sucker "; + envWindows = Environment(env, OsTypeWindows); + envLinux = Environment(env, OsTypeLinux); + + mxWin.insert("a", "hi"); + mxWin.insert("aa", "hi ho"); + + mxWin.insert("b", "h\\i"); + mxWin.insert("c", "\\hi"); + mxWin.insert("d", "hi\\"); + mxWin.insert("ba", "h\\i ho"); + mxWin.insert("ca", "\\hi ho"); + mxWin.insert("da", "hi ho\\"); + + mxWin.insert("e", "h\"i"); + mxWin.insert("f", "\"hi"); + mxWin.insert("g", "hi\""); + + mxWin.insert("h", "h\\\"i"); + mxWin.insert("i", "\\\"hi"); + mxWin.insert("j", "hi\\\""); + + mxWin.insert("k", "&special;"); + + mxWin.insert("x", "\\"); + mxWin.insert("y", "\""); + mxWin.insert("z", ""); + + mxUnix.insert("a", "hi"); + mxUnix.insert("b", "hi ho"); + mxUnix.insert("c", "&special;"); + mxUnix.insert("d", "h\\i"); + mxUnix.insert("e", "h\"i"); + mxUnix.insert("f", "h'i"); + mxUnix.insert("z", ""); } + Q_DECLARE_METATYPE(QtcProcess::SplitError) +Q_DECLARE_METATYPE(Utils::OsType) void tst_QtcProcess::splitArgs_data() { QTest::addColumn<QString>("in"); QTest::addColumn<QString>("out"); QTest::addColumn<QtcProcess::SplitError>("err"); + QTest::addColumn<Utils::OsType>("os"); static const struct { const char * const in; const char * const out; const QtcProcess::SplitError err; + const OsType os; } vals[] = { -#ifdef Q_OS_WIN - { "", "", QtcProcess::SplitOk }, - { " ", "", QtcProcess::SplitOk }, - { "hi", "hi", QtcProcess::SplitOk }, - { "hi ho", "hi ho", QtcProcess::SplitOk }, - { " hi ho ", "hi ho", QtcProcess::SplitOk }, - { "\"hi ho\" \"hi\" ho ", "\"hi ho\" hi ho", QtcProcess::SplitOk }, - { "\\", "\\", QtcProcess::SplitOk }, - { "\\\"", "\"\"\\^\"\"\"", QtcProcess::SplitOk }, - { "\"hi\"\"\"ho\"", "\"hi\"\\^\"\"ho\"", QtcProcess::SplitOk }, - { "\\\\\\\"", "\"\"\\\\\\^\"\"\"", QtcProcess::SplitOk }, - { " ^^ ", "\"^^\"", QtcProcess::SplitOk }, - { "hi\"", "", QtcProcess::BadQuoting }, - { "hi\"dood", "", QtcProcess::BadQuoting }, - { "%var%", "%var%", QtcProcess::SplitOk }, -#else - { "", "", QtcProcess::SplitOk }, - { " ", "", QtcProcess::SplitOk }, - { "hi", "hi", QtcProcess::SplitOk }, - { "hi ho", "hi ho", QtcProcess::SplitOk }, - { " hi ho ", "hi ho", QtcProcess::SplitOk }, - { "'hi ho' \"hi\" ho ", "'hi ho' hi ho", QtcProcess::SplitOk }, - { " \\ ", "' '", QtcProcess::SplitOk }, - { " \\\" ", "'\"'", QtcProcess::SplitOk }, - { " '\"' ", "'\"'", QtcProcess::SplitOk }, - { " \"\\\"\" ", "'\"'", QtcProcess::SplitOk }, - { "hi'", "", QtcProcess::BadQuoting }, - { "hi\"dood", "", QtcProcess::BadQuoting }, - { "$var", "'$var'", QtcProcess::SplitOk }, - { "~", "@HOME@", QtcProcess::SplitOk }, - { "~ foo", "@HOME@ foo", QtcProcess::SplitOk }, - { "foo ~", "foo @HOME@", QtcProcess::SplitOk }, - { "~/foo", "@HOME@/foo", QtcProcess::SplitOk }, - { "~foo", "'~foo'", QtcProcess::SplitOk }, -#endif + { "", "", QtcProcess::SplitOk, OsTypeWindows }, + { " ", "", QtcProcess::SplitOk, OsTypeWindows }, + { "hi", "hi", QtcProcess::SplitOk, OsTypeWindows }, + { "hi ho", "hi ho", QtcProcess::SplitOk, OsTypeWindows }, + { " hi ho ", "hi ho", QtcProcess::SplitOk, OsTypeWindows }, + { "\"hi ho\" \"hi\" ho ", "\"hi ho\" hi ho", QtcProcess::SplitOk, OsTypeWindows }, + { "\\", "\\", QtcProcess::SplitOk, OsTypeWindows }, + { "\\\"", "\"\"\\^\"\"\"", QtcProcess::SplitOk, OsTypeWindows }, + { "\"hi\"\"\"ho\"", "\"hi\"\\^\"\"ho\"", QtcProcess::SplitOk, OsTypeWindows }, + { "\\\\\\\"", "\"\"\\\\\\^\"\"\"", QtcProcess::SplitOk, OsTypeWindows }, + { " ^^ ", "\"^^\"", QtcProcess::SplitOk, OsTypeWindows }, + { "hi\"", "", QtcProcess::BadQuoting, OsTypeWindows }, + { "hi\"dood", "", QtcProcess::BadQuoting, OsTypeWindows }, + { "%var%", "%var%", QtcProcess::SplitOk, OsTypeWindows }, + + { "", "", QtcProcess::SplitOk, OsTypeLinux }, + { " ", "", QtcProcess::SplitOk, OsTypeLinux }, + { "hi", "hi", QtcProcess::SplitOk, OsTypeLinux }, + { "hi ho", "hi ho", QtcProcess::SplitOk, OsTypeLinux }, + { " hi ho ", "hi ho", QtcProcess::SplitOk, OsTypeLinux }, + { "'hi ho' \"hi\" ho ", "'hi ho' hi ho", QtcProcess::SplitOk, OsTypeLinux }, + { " \\ ", "' '", QtcProcess::SplitOk, OsTypeLinux }, + { " \\\" ", "'\"'", QtcProcess::SplitOk, OsTypeLinux }, + { " '\"' ", "'\"'", QtcProcess::SplitOk, OsTypeLinux }, + { " \"\\\"\" ", "'\"'", QtcProcess::SplitOk, OsTypeLinux }, + { "hi'", "", QtcProcess::BadQuoting, OsTypeLinux }, + { "hi\"dood", "", QtcProcess::BadQuoting, OsTypeLinux }, + { "$var", "'$var'", QtcProcess::SplitOk, OsTypeLinux }, + { "~", "@HOME@", QtcProcess::SplitOk, OsTypeLinux }, + { "~ foo", "@HOME@ foo", QtcProcess::SplitOk, OsTypeLinux }, + { "foo ~", "foo @HOME@", QtcProcess::SplitOk, OsTypeLinux }, + { "~/foo", "@HOME@/foo", QtcProcess::SplitOk, OsTypeLinux }, + { "~foo", "'~foo'", QtcProcess::SplitOk, OsTypeLinux } }; - for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) + for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) { + QString out = QString::fromLatin1(vals[i].out); + if (vals[i].os == OsTypeLinux) + out.replace(homeStr, home); QTest::newRow(vals[i].in) << QString::fromLatin1(vals[i].in) - << QString::fromLatin1(vals[i].out) -#ifdef Q_OS_UNIX - .replace(homeStr, home) -#endif - << vals[i].err; + << out << vals[i].err << vals[i].os; + } } void tst_QtcProcess::splitArgs() @@ -188,9 +193,10 @@ void tst_QtcProcess::splitArgs() QFETCH(QString, in); QFETCH(QString, out); QFETCH(QtcProcess::SplitError, err); + QFETCH(Utils::OsType, os); QtcProcess::SplitError outerr; - QString outstr = QtcProcess::joinArgs(QtcProcess::splitArgs(in, false, &outerr)); + QString outstr = QtcProcess::joinArgs(QtcProcess::splitArgs(in, os, false, &outerr), os); QCOMPARE(outerr, err); if (err == QtcProcess::SplitOk) QCOMPARE(outstr, out); @@ -201,53 +207,53 @@ void tst_QtcProcess::prepareArgs_data() QTest::addColumn<QString>("in"); QTest::addColumn<QString>("out"); QTest::addColumn<QtcProcess::SplitError>("err"); + QTest::addColumn<OsType>("os"); static const struct { const char * const in; const char * const out; const QtcProcess::SplitError err; + const OsType os; } vals[] = { -#ifdef Q_OS_WIN - { "", "", QtcProcess::SplitOk }, - { " ", " ", QtcProcess::SplitOk }, - { "hi", "hi", QtcProcess::SplitOk }, - { "hi ho", "hi ho", QtcProcess::SplitOk }, - { " hi ho ", " hi ho ", QtcProcess::SplitOk }, - { "\"hi ho\" \"hi\" ho ", "\"hi ho\" \"hi\" ho ", QtcProcess::SplitOk }, - { "\\", "\\", QtcProcess::SplitOk }, - { "\\\"", "\\\"", QtcProcess::SplitOk }, - { "\"hi\"\"ho\"", "\"hi\"\"ho\"", QtcProcess::SplitOk }, - { "\\\\\\\"", "\\\\\\\"", QtcProcess::SplitOk }, - { "^^", "^", QtcProcess::SplitOk }, - { "hi\"", "hi\"", QtcProcess::SplitOk }, - { "hi\"dood", "hi\"dood", QtcProcess::SplitOk }, - { "%var%", "", QtcProcess::FoundMeta }, - { "echo hi > file", "", QtcProcess::FoundMeta }, -#else - { "", "", QtcProcess::SplitOk }, - { " ", "", QtcProcess::SplitOk }, - { "hi", "hi", QtcProcess::SplitOk }, - { "hi ho", "hi ho", QtcProcess::SplitOk }, - { " hi ho ", "hi ho", QtcProcess::SplitOk }, - { "'hi ho' \"hi\" ho ", "'hi ho' hi ho", QtcProcess::SplitOk }, - { " \\ ", "' '", QtcProcess::SplitOk }, - { "hi'", "", QtcProcess::BadQuoting }, - { "hi\"dood", "", QtcProcess::BadQuoting }, - { "$var", "", QtcProcess::FoundMeta }, - { "~", "@HOME@", QtcProcess::SplitOk }, - { "~ foo", "@HOME@ foo", QtcProcess::SplitOk }, - { "~/foo", "@HOME@/foo", QtcProcess::SplitOk }, - { "~foo", "", QtcProcess::FoundMeta }, -#endif + { " ", " ", QtcProcess::SplitOk, OsTypeWindows }, + { "", "", QtcProcess::SplitOk, OsTypeWindows }, + { "hi", "hi", QtcProcess::SplitOk, OsTypeWindows }, + { "hi ho", "hi ho", QtcProcess::SplitOk, OsTypeWindows }, + { " hi ho ", " hi ho ", QtcProcess::SplitOk, OsTypeWindows }, + { "\"hi ho\" \"hi\" ho ", "\"hi ho\" \"hi\" ho ", QtcProcess::SplitOk, OsTypeWindows }, + { "\\", "\\", QtcProcess::SplitOk, OsTypeWindows }, + { "\\\"", "\\\"", QtcProcess::SplitOk, OsTypeWindows }, + { "\"hi\"\"ho\"", "\"hi\"\"ho\"", QtcProcess::SplitOk, OsTypeWindows }, + { "\\\\\\\"", "\\\\\\\"", QtcProcess::SplitOk, OsTypeWindows }, + { "^^", "^", QtcProcess::SplitOk, OsTypeWindows }, + { "hi\"", "hi\"", QtcProcess::SplitOk, OsTypeWindows }, + { "hi\"dood", "hi\"dood", QtcProcess::SplitOk, OsTypeWindows }, + { "%var%", "", QtcProcess::FoundMeta, OsTypeWindows }, + { "echo hi > file", "", QtcProcess::FoundMeta, OsTypeWindows }, + + { "", "", QtcProcess::SplitOk, OsTypeLinux }, + { " ", "", QtcProcess::SplitOk, OsTypeLinux }, + { "hi", "hi", QtcProcess::SplitOk, OsTypeLinux }, + { "hi ho", "hi ho", QtcProcess::SplitOk, OsTypeLinux }, + { " hi ho ", "hi ho", QtcProcess::SplitOk, OsTypeLinux }, + { "'hi ho' \"hi\" ho ", "'hi ho' hi ho", QtcProcess::SplitOk, OsTypeLinux }, + { " \\ ", "' '", QtcProcess::SplitOk, OsTypeLinux }, + { "hi'", "", QtcProcess::BadQuoting, OsTypeLinux }, + { "hi\"dood", "", QtcProcess::BadQuoting, OsTypeLinux }, + { "$var", "", QtcProcess::FoundMeta, OsTypeLinux }, + { "~", "@HOME@", QtcProcess::SplitOk, OsTypeLinux }, + { "~ foo", "@HOME@ foo", QtcProcess::SplitOk, OsTypeLinux }, + { "~/foo", "@HOME@/foo", QtcProcess::SplitOk, OsTypeLinux }, + { "~foo", "", QtcProcess::FoundMeta, OsTypeLinux } }; - for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) + for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) { + QString out = QString::fromLatin1(vals[i].out); + if (vals[i].os == OsTypeLinux) + out.replace(homeStr, home); QTest::newRow(vals[i].in) << QString::fromLatin1(vals[i].in) - << QString::fromLatin1(vals[i].out) -#ifdef Q_OS_UNIX - .replace(homeStr, home) -#endif - << vals[i].err; + << out << vals[i].err << vals[i].os; + } } void tst_QtcProcess::prepareArgs() @@ -255,14 +261,12 @@ void tst_QtcProcess::prepareArgs() QFETCH(QString, in); QFETCH(QString, out); QFETCH(QtcProcess::SplitError, err); + QFETCH(OsType, os); QtcProcess::SplitError outerr; - QString outstr; -#ifdef Q_OS_WIN - outstr = QtcProcess::prepareArgs(in, &outerr); -#else - outstr = QtcProcess::joinArgs(QtcProcess::prepareArgs(in, &outerr)); -#endif + QtcProcess::Arguments args = QtcProcess::prepareArgs(in, &outerr, os); + QString outstr = args.toString(); + QCOMPARE(outerr, err); if (err == QtcProcess::SplitOk) QCOMPARE(outstr, out); @@ -273,70 +277,73 @@ void tst_QtcProcess::prepareArgsEnv_data() QTest::addColumn<QString>("in"); QTest::addColumn<QString>("out"); QTest::addColumn<QtcProcess::SplitError>("err"); + QTest::addColumn<OsType>("os"); static const struct { const char * const in; const char * const out; const QtcProcess::SplitError err; + const OsType os; } vals[] = { -#ifdef Q_OS_WIN - { "", "", QtcProcess::SplitOk }, - { " ", " ", QtcProcess::SplitOk }, - { "hi", "hi", QtcProcess::SplitOk }, - { "hi ho", "hi ho", QtcProcess::SplitOk }, - { " hi ho ", " hi ho ", QtcProcess::SplitOk }, - { "\"hi ho\" \"hi\" ho ", "\"hi ho\" \"hi\" ho ", QtcProcess::SplitOk }, - { "\\", "\\", QtcProcess::SplitOk }, - { "\\\"", "\\\"", QtcProcess::SplitOk }, - { "\"hi\"\"ho\"", "\"hi\"\"ho\"", QtcProcess::SplitOk }, - { "\\\\\\\"", "\\\\\\\"", QtcProcess::SplitOk }, - { "^^", "^", QtcProcess::SplitOk }, - { "hi\"", "hi\"", QtcProcess::SplitOk }, - { "hi\"dood", "hi\"dood", QtcProcess::SplitOk }, - { "%empty%", "%empty%", QtcProcess::SplitOk }, // Yep, no empty variables on Windows. - { "%word%", "hi", QtcProcess::SplitOk }, - { " %word% ", " hi ", QtcProcess::SplitOk }, - { "%words%", "hi ho", QtcProcess::SplitOk }, - { "%nonsense%words%", "%nonsensehi ho", QtcProcess::SplitOk }, - { "fail%nonsense%words%", "fail%nonsensehi ho", QtcProcess::SplitOk }, - { "%words%words%", "hi howords%", QtcProcess::SplitOk }, - { "%words%%words%", "hi hohi ho", QtcProcess::SplitOk }, - { "echo hi > file", "", QtcProcess::FoundMeta }, -#else - { "", "", QtcProcess::SplitOk }, - { " ", "", QtcProcess::SplitOk }, - { "hi", "hi", QtcProcess::SplitOk }, - { "hi ho", "hi ho", QtcProcess::SplitOk }, - { " hi ho ", "hi ho", QtcProcess::SplitOk }, - { "'hi ho' \"hi\" ho ", "'hi ho' hi ho", QtcProcess::SplitOk }, - { " \\ ", "' '", QtcProcess::SplitOk }, - { "hi'", "", QtcProcess::BadQuoting }, - { "hi\"dood", "", QtcProcess::BadQuoting }, - { "$empty", "", QtcProcess::SplitOk }, - { "$word", "hi", QtcProcess::SplitOk }, - { " $word ", "hi", QtcProcess::SplitOk }, - { "${word}", "hi", QtcProcess::SplitOk }, - { " ${word} ", "hi", QtcProcess::SplitOk }, - { "$words", "hi ho", QtcProcess::SplitOk }, - { "$spacedwords", "hi ho sucker", QtcProcess::SplitOk }, - { "hi${empty}ho", "hiho", QtcProcess::SplitOk }, - { "hi${words}ho", "hihi hoho", QtcProcess::SplitOk }, - { "hi${spacedwords}ho", "hi hi ho sucker ho", QtcProcess::SplitOk }, - { "${", "", QtcProcess::BadQuoting }, - { "${var", "", QtcProcess::BadQuoting }, - { "${var ", "", QtcProcess::FoundMeta }, - { "\"hi${words}ho\"", "'hihi hoho'", QtcProcess::SplitOk }, - { "\"hi${spacedwords}ho\"", "'hi hi ho sucker ho'", QtcProcess::SplitOk }, - { "\"${", "", QtcProcess::BadQuoting }, - { "\"${var", "", QtcProcess::BadQuoting }, - { "\"${var ", "", QtcProcess::FoundMeta }, -#endif + { " ", " ", QtcProcess::SplitOk, OsTypeWindows }, + { "", "", QtcProcess::SplitOk, OsTypeWindows }, + { "hi", "hi", QtcProcess::SplitOk, OsTypeWindows }, + { "hi ho", "hi ho", QtcProcess::SplitOk, OsTypeWindows }, + { " hi ho ", " hi ho ", QtcProcess::SplitOk, OsTypeWindows }, + { "\"hi ho\" \"hi\" ho ", "\"hi ho\" \"hi\" ho ", QtcProcess::SplitOk, OsTypeWindows }, + { "\\", "\\", QtcProcess::SplitOk, OsTypeWindows }, + { "\\\"", "\\\"", QtcProcess::SplitOk, OsTypeWindows }, + { "\"hi\"\"ho\"", "\"hi\"\"ho\"", QtcProcess::SplitOk, OsTypeWindows }, + { "\\\\\\\"", "\\\\\\\"", QtcProcess::SplitOk, OsTypeWindows }, + { "^^", "^", QtcProcess::SplitOk, OsTypeWindows }, + { "hi\"", "hi\"", QtcProcess::SplitOk, OsTypeWindows }, + { "hi\"dood", "hi\"dood", QtcProcess::SplitOk, OsTypeWindows }, + { "%empty%", "%empty%", QtcProcess::SplitOk, OsTypeWindows }, // Yep, no empty variables on Windows. + { "%word%", "hi", QtcProcess::SplitOk, OsTypeWindows }, + { " %word% ", " hi ", QtcProcess::SplitOk, OsTypeWindows }, + { "%words%", "hi ho", QtcProcess::SplitOk, OsTypeWindows }, + { "%nonsense%words%", "%nonsensehi ho", QtcProcess::SplitOk, OsTypeWindows }, + { "fail%nonsense%words%", "fail%nonsensehi ho", QtcProcess::SplitOk, OsTypeWindows }, + { "%words%words%", "hi howords%", QtcProcess::SplitOk, OsTypeWindows }, + { "%words%%words%", "hi hohi ho", QtcProcess::SplitOk, OsTypeWindows }, + { "echo hi > file", "", QtcProcess::FoundMeta, OsTypeWindows }, + + { "", "", QtcProcess::SplitOk, OsTypeLinux }, + { " ", "", QtcProcess::SplitOk, OsTypeLinux }, + { "hi", "hi", QtcProcess::SplitOk, OsTypeLinux }, + { "hi ho", "hi ho", QtcProcess::SplitOk, OsTypeLinux }, + { " hi ho ", "hi ho", QtcProcess::SplitOk, OsTypeLinux }, + { "'hi ho' \"hi\" ho ", "'hi ho' hi ho", QtcProcess::SplitOk, OsTypeLinux }, + { " \\ ", "' '", QtcProcess::SplitOk, OsTypeLinux }, + { "hi'", "", QtcProcess::BadQuoting, OsTypeLinux }, + { "hi\"dood", "", QtcProcess::BadQuoting, OsTypeLinux }, + { "$empty", "", QtcProcess::SplitOk, OsTypeLinux }, + { "$word", "hi", QtcProcess::SplitOk, OsTypeLinux }, + { " $word ", "hi", QtcProcess::SplitOk, OsTypeLinux }, + { "${word}", "hi", QtcProcess::SplitOk, OsTypeLinux }, + { " ${word} ", "hi", QtcProcess::SplitOk, OsTypeLinux }, + { "$words", "hi ho", QtcProcess::SplitOk, OsTypeLinux }, + { "$spacedwords", "hi ho sucker", QtcProcess::SplitOk, OsTypeLinux }, + { "hi${empty}ho", "hiho", QtcProcess::SplitOk, OsTypeLinux }, + { "hi${words}ho", "hihi hoho", QtcProcess::SplitOk, OsTypeLinux }, + { "hi${spacedwords}ho", "hi hi ho sucker ho", QtcProcess::SplitOk, OsTypeLinux }, + { "${", "", QtcProcess::BadQuoting, OsTypeLinux }, + { "${var", "", QtcProcess::BadQuoting, OsTypeLinux }, + { "${var ", "", QtcProcess::FoundMeta, OsTypeLinux }, + { "\"hi${words}ho\"", "'hihi hoho'", QtcProcess::SplitOk, OsTypeLinux }, + { "\"hi${spacedwords}ho\"", "'hi hi ho sucker ho'", QtcProcess::SplitOk, OsTypeLinux }, + { "\"${", "", QtcProcess::BadQuoting, OsTypeLinux }, + { "\"${var", "", QtcProcess::BadQuoting, OsTypeLinux }, + { "\"${var ", "", QtcProcess::FoundMeta, OsTypeLinux }, }; - for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) + for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) { + QString out = QString::fromLatin1(vals[i].out); + if (vals[i].os == OsTypeLinux) + out.replace(homeStr, home); QTest::newRow(vals[i].in) << QString::fromLatin1(vals[i].in) - << QString::fromLatin1(vals[i].out) - << vals[i].err; + << out << vals[i].err << vals[i].os; + } } void tst_QtcProcess::prepareArgsEnv() @@ -344,14 +351,12 @@ void tst_QtcProcess::prepareArgsEnv() QFETCH(QString, in); QFETCH(QString, out); QFETCH(QtcProcess::SplitError, err); + QFETCH(OsType, os); QtcProcess::SplitError outerr; - QString outstr; -#ifdef Q_OS_WIN - outstr = QtcProcess::prepareArgs(in, &outerr, &env); -#else - outstr = QtcProcess::joinArgs(QtcProcess::prepareArgs(in, &outerr, &env)); -#endif + QtcProcess::Arguments args = QtcProcess::prepareArgs(in, &outerr, os, os == OsTypeLinux ? &envLinux : &envWindows); + QString outstr = args.toString(); + QCOMPARE(outerr, err); if (err == QtcProcess::SplitOk) QCOMPARE(outstr, out); @@ -362,215 +367,215 @@ void tst_QtcProcess::expandMacros_data() { QTest::addColumn<QString>("in"); QTest::addColumn<QString>("out"); + QTest::addColumn<OsType>("os"); QChar sp(QLatin1Char(' ')); static const struct { const char * const in; const char * const out; + OsType os; } vals[] = { -#ifdef Q_OS_WIN - { "plain", 0 }, - { "%{a}", "hi" }, - { "%{aa}", "\"hi ho\"" }, - { "%{b}", "h\\i" }, - { "%{c}", "\\hi" }, - { "%{d}", "hi\\" }, - { "%{ba}", "\"h\\i ho\"" }, - { "%{ca}", "\"\\hi ho\"" }, - { "%{da}", "\"hi ho\\\\\"" }, // or "\"hi ho\"\\" - { "%{e}", "\"h\"\\^\"\"i\"" }, - { "%{f}", "\"\"\\^\"\"hi\"" }, - { "%{g}", "\"hi\"\\^\"\"\"" }, - { "%{h}", "\"h\\\\\"\\^\"\"i\"" }, - { "%{i}", "\"\\\\\"\\^\"\"hi\"" }, - { "%{j}", "\"hi\\\\\"\\^\"\"\"" }, - { "%{k}", "\"&special;\"" }, - { "%{x}", "\\" }, - { "%{y}", "\"\"\\^\"\"\"" }, - { "%{z}", "\"\"" }, - { "^%{z}%{z}", "^%{z}%{z}" }, // stupid user check - - { "quoted", 0 }, - { "\"%{a}\"", "\"hi\"" }, - { "\"%{aa}\"", "\"hi ho\"" }, - { "\"%{b}\"", "\"h\\i\"" }, - { "\"%{c}\"", "\"\\hi\"" }, - { "\"%{d}\"", "\"hi\\\\\"" }, - { "\"%{ba}\"", "\"h\\i ho\"" }, - { "\"%{ca}\"", "\"\\hi ho\"" }, - { "\"%{da}\"", "\"hi ho\\\\\"" }, - { "\"%{e}\"", "\"h\"\\^\"\"i\"" }, - { "\"%{f}\"", "\"\"\\^\"\"hi\"" }, - { "\"%{g}\"", "\"hi\"\\^\"\"\"" }, - { "\"%{h}\"", "\"h\\\\\"\\^\"\"i\"" }, - { "\"%{i}\"", "\"\\\\\"\\^\"\"hi\"" }, - { "\"%{j}\"", "\"hi\\\\\"\\^\"\"\"" }, - { "\"%{k}\"", "\"&special;\"" }, - { "\"%{x}\"", "\"\\\\\"" }, - { "\"%{y}\"", "\"\"\\^\"\"\"" }, - { "\"%{z}\"", "\"\"" }, - - { "leading bs", 0 }, - { "\\%{a}", "\\hi" }, - { "\\%{aa}", "\\\\\"hi ho\"" }, - { "\\%{b}", "\\h\\i" }, - { "\\%{c}", "\\\\hi" }, - { "\\%{d}", "\\hi\\" }, - { "\\%{ba}", "\\\\\"h\\i ho\"" }, - { "\\%{ca}", "\\\\\"\\hi ho\"" }, - { "\\%{da}", "\\\\\"hi ho\\\\\"" }, - { "\\%{e}", "\\\\\"h\"\\^\"\"i\"" }, - { "\\%{f}", "\\\\\"\"\\^\"\"hi\"" }, - { "\\%{g}", "\\\\\"hi\"\\^\"\"\"" }, - { "\\%{h}", "\\\\\"h\\\\\"\\^\"\"i\"" }, - { "\\%{i}", "\\\\\"\\\\\"\\^\"\"hi\"" }, - { "\\%{j}", "\\\\\"hi\\\\\"\\^\"\"\"" }, - { "\\%{x}", "\\\\" }, - { "\\%{y}", "\\\\\"\"\\^\"\"\"" }, - { "\\%{z}", "\\" }, - - { "trailing bs", 0 }, - { "%{a}\\", "hi\\" }, - { "%{aa}\\", "\"hi ho\"\\" }, - { "%{b}\\", "h\\i\\" }, - { "%{c}\\", "\\hi\\" }, - { "%{d}\\", "hi\\\\" }, - { "%{ba}\\", "\"h\\i ho\"\\" }, - { "%{ca}\\", "\"\\hi ho\"\\" }, - { "%{da}\\", "\"hi ho\\\\\"\\" }, - { "%{e}\\", "\"h\"\\^\"\"i\"\\" }, - { "%{f}\\", "\"\"\\^\"\"hi\"\\" }, - { "%{g}\\", "\"hi\"\\^\"\"\"\\" }, - { "%{h}\\", "\"h\\\\\"\\^\"\"i\"\\" }, - { "%{i}\\", "\"\\\\\"\\^\"\"hi\"\\" }, - { "%{j}\\", "\"hi\\\\\"\\^\"\"\"\\" }, - { "%{x}\\", "\\\\" }, - { "%{y}\\", "\"\"\\^\"\"\"\\" }, - { "%{z}\\", "\\" }, - - { "bs-enclosed", 0 }, - { "\\%{a}\\", "\\hi\\" }, - { "\\%{aa}\\", "\\\\\"hi ho\"\\" }, - { "\\%{b}\\", "\\h\\i\\" }, - { "\\%{c}\\", "\\\\hi\\" }, - { "\\%{d}\\", "\\hi\\\\" }, - { "\\%{ba}\\", "\\\\\"h\\i ho\"\\" }, - { "\\%{ca}\\", "\\\\\"\\hi ho\"\\" }, - { "\\%{da}\\", "\\\\\"hi ho\\\\\"\\" }, - { "\\%{e}\\", "\\\\\"h\"\\^\"\"i\"\\" }, - { "\\%{f}\\", "\\\\\"\"\\^\"\"hi\"\\" }, - { "\\%{g}\\", "\\\\\"hi\"\\^\"\"\"\\" }, - { "\\%{h}\\", "\\\\\"h\\\\\"\\^\"\"i\"\\" }, - { "\\%{i}\\", "\\\\\"\\\\\"\\^\"\"hi\"\\" }, - { "\\%{j}\\", "\\\\\"hi\\\\\"\\^\"\"\"\\" }, - { "\\%{x}\\", "\\\\\\" }, - { "\\%{y}\\", "\\\\\"\"\\^\"\"\"\\" }, - { "\\%{z}\\", "\\\\" }, - - { "bs-enclosed and trailing literal quote", 0 }, - { "\\%{a}\\\\\\^\"", "\\hi\\\\\\^\"" }, - { "\\%{aa}\\\\\\^\"", "\\\\\"hi ho\"\\\\\\^\"" }, - { "\\%{b}\\\\\\^\"", "\\h\\i\\\\\\^\"" }, - { "\\%{c}\\\\\\^\"", "\\\\hi\\\\\\^\"" }, - { "\\%{d}\\\\\\^\"", "\\hi\\\\\\\\\\^\"" }, - { "\\%{ba}\\\\\\^\"", "\\\\\"h\\i ho\"\\\\\\^\"" }, - { "\\%{ca}\\\\\\^\"", "\\\\\"\\hi ho\"\\\\\\^\"" }, - { "\\%{da}\\\\\\^\"", "\\\\\"hi ho\\\\\"\\\\\\^\"" }, - { "\\%{e}\\\\\\^\"", "\\\\\"h\"\\^\"\"i\"\\\\\\^\"" }, - { "\\%{f}\\\\\\^\"", "\\\\\"\"\\^\"\"hi\"\\\\\\^\"" }, - { "\\%{g}\\\\\\^\"", "\\\\\"hi\"\\^\"\"\"\\\\\\^\"" }, - { "\\%{h}\\\\\\^\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\\^\"" }, - { "\\%{i}\\\\\\^\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\\^\"" }, - { "\\%{j}\\\\\\^\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\\^\"" }, - { "\\%{x}\\\\\\^\"", "\\\\\\\\\\\\\\^\"" }, - { "\\%{y}\\\\\\^\"", "\\\\\"\"\\^\"\"\"\\\\\\^\"" }, - { "\\%{z}\\\\\\^\"", "\\\\\\\\\\^\"" }, - - { "bs-enclosed and trailing unclosed quote", 0 }, - { "\\%{a}\\\\\"", "\\hi\\\\\"" }, - { "\\%{aa}\\\\\"", "\\\\\"hi ho\"\\\\\"" }, - { "\\%{b}\\\\\"", "\\h\\i\\\\\"" }, - { "\\%{c}\\\\\"", "\\\\hi\\\\\"" }, - { "\\%{d}\\\\\"", "\\hi\\\\\\\\\"" }, - { "\\%{ba}\\\\\"", "\\\\\"h\\i ho\"\\\\\"" }, - { "\\%{ca}\\\\\"", "\\\\\"\\hi ho\"\\\\\"" }, - { "\\%{da}\\\\\"", "\\\\\"hi ho\\\\\"\\\\\"" }, - { "\\%{e}\\\\\"", "\\\\\"h\"\\^\"\"i\"\\\\\"" }, - { "\\%{f}\\\\\"", "\\\\\"\"\\^\"\"hi\"\\\\\"" }, - { "\\%{g}\\\\\"", "\\\\\"hi\"\\^\"\"\"\\\\\"" }, - { "\\%{h}\\\\\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\"" }, - { "\\%{i}\\\\\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\"" }, - { "\\%{j}\\\\\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\"" }, - { "\\%{x}\\\\\"", "\\\\\\\\\\\\\"" }, - { "\\%{y}\\\\\"", "\\\\\"\"\\^\"\"\"\\\\\"" }, - { "\\%{z}\\\\\"", "\\\\\\\\\"" }, - - { "multi-var", 0 }, - { "%{x}%{y}%{z}", "\\\\\"\"\\^\"\"\"" }, - { "%{x}%{z}%{y}%{z}", "\\\\\"\"\\^\"\"\"" }, - { "%{x}%{z}%{y}", "\\\\\"\"\\^\"\"\"" }, - { "%{x}\\^\"%{z}", "\\\\\\^\"" }, - { "%{x}%{z}\\^\"%{z}", "\\\\\\^\"" }, - { "%{x}%{z}\\^\"", "\\\\\\^\"" }, - { "%{x}\\%{z}", "\\\\" }, - { "%{x}%{z}\\%{z}", "\\\\" }, - { "%{x}%{z}\\", "\\\\" }, - { "%{aa}%{a}", "\"hi hohi\"" }, - { "%{aa}%{aa}", "\"hi hohi ho\"" }, - { "%{aa}:%{aa}", "\"hi ho\":\"hi ho\"" }, - { "hallo ^|%{aa}^|", "hallo ^|\"hi ho\"^|" }, - - { "quoted multi-var", 0 }, - { "\"%{x}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"" }, - { "\"%{x}%{z}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"" }, - { "\"%{x}%{z}%{y}\"", "\"\\\\\"\\^\"\"\"" }, - { "\"%{x}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"" }, - { "\"%{x}%{z}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"" }, - { "\"%{x}%{z}\"^\"\"\"", "\"\\\\\"^\"\"\"" }, - { "\"%{x}\\%{z}\"", "\"\\\\\\\\\"" }, - { "\"%{x}%{z}\\%{z}\"", "\"\\\\\\\\\"" }, - { "\"%{x}%{z}\\\\\"", "\"\\\\\\\\\"" }, - { "\"%{aa}%{a}\"", "\"hi hohi\"" }, - { "\"%{aa}%{aa}\"", "\"hi hohi ho\"" }, - { "\"%{aa}:%{aa}\"", "\"hi ho:hi ho\"" }, -#else - { "plain", 0 }, - { "%{a}", "hi" }, - { "%{b}", "'hi ho'" }, - { "%{c}", "'&special;'" }, - { "%{d}", "'h\\i'" }, - { "%{e}", "'h\"i'" }, - { "%{f}", "'h'\\''i'" }, - { "%{z}", "''" }, - { "\\%{z}%{z}", "\\%{z}%{z}" }, // stupid user check - - { "single-quoted", 0 }, - { "'%{a}'", "'hi'" }, - { "'%{b}'", "'hi ho'" }, - { "'%{c}'", "'&special;'" }, - { "'%{d}'", "'h\\i'" }, - { "'%{e}'", "'h\"i'" }, - { "'%{f}'", "'h'\\''i'" }, - { "'%{z}'", "''" }, - - { "double-quoted", 0 }, - { "\"%{a}\"", "\"hi\"" }, - { "\"%{b}\"", "\"hi ho\"" }, - { "\"%{c}\"", "\"&special;\"" }, - { "\"%{d}\"", "\"h\\\\i\"" }, - { "\"%{e}\"", "\"h\\\"i\"" }, - { "\"%{f}\"", "\"h'i\"" }, - { "\"%{z}\"", "\"\"" }, - - { "complex", 0 }, - { "echo \"$(echo %{a})\"", "echo \"$(echo hi)\"" }, - { "echo \"$(echo %{b})\"", "echo \"$(echo 'hi ho')\"" }, - { "echo \"$(echo \"%{a}\")\"", "echo \"$(echo \"hi\")\"" }, + { "plain", 0, OsTypeWindows }, + { "%{a}", "hi", OsTypeWindows }, + { "%{aa}", "\"hi ho\"", OsTypeWindows }, + { "%{b}", "h\\i", OsTypeWindows }, + { "%{c}", "\\hi", OsTypeWindows }, + { "%{d}", "hi\\", OsTypeWindows }, + { "%{ba}", "\"h\\i ho\"", OsTypeWindows }, + { "%{ca}", "\"\\hi ho\"", OsTypeWindows }, + { "%{da}", "\"hi ho\\\\\"", OsTypeWindows }, // or "\"hi ho\"\\" + { "%{e}", "\"h\"\\^\"\"i\"", OsTypeWindows }, + { "%{f}", "\"\"\\^\"\"hi\"", OsTypeWindows }, + { "%{g}", "\"hi\"\\^\"\"\"", OsTypeWindows }, + { "%{h}", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows }, + { "%{i}", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows }, + { "%{j}", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows }, + { "%{k}", "\"&special;\"", OsTypeWindows }, + { "%{x}", "\\", OsTypeWindows }, + { "%{y}", "\"\"\\^\"\"\"", OsTypeWindows }, + { "%{z}", "\"\"", OsTypeWindows }, + { "^%{z}%{z}", "^%{z}%{z}", OsTypeWindows }, // stupid user check + + { "quoted", 0, OsTypeWindows }, + { "\"%{a}\"", "\"hi\"", OsTypeWindows }, + { "\"%{aa}\"", "\"hi ho\"", OsTypeWindows }, + { "\"%{b}\"", "\"h\\i\"", OsTypeWindows }, + { "\"%{c}\"", "\"\\hi\"", OsTypeWindows }, + { "\"%{d}\"", "\"hi\\\\\"", OsTypeWindows }, + { "\"%{ba}\"", "\"h\\i ho\"", OsTypeWindows }, + { "\"%{ca}\"", "\"\\hi ho\"", OsTypeWindows }, + { "\"%{da}\"", "\"hi ho\\\\\"", OsTypeWindows }, + { "\"%{e}\"", "\"h\"\\^\"\"i\"", OsTypeWindows }, + { "\"%{f}\"", "\"\"\\^\"\"hi\"", OsTypeWindows }, + { "\"%{g}\"", "\"hi\"\\^\"\"\"", OsTypeWindows }, + { "\"%{h}\"", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows }, + { "\"%{i}\"", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows }, + { "\"%{j}\"", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows }, + { "\"%{k}\"", "\"&special;\"", OsTypeWindows }, + { "\"%{x}\"", "\"\\\\\"", OsTypeWindows }, + { "\"%{y}\"", "\"\"\\^\"\"\"", OsTypeWindows }, + { "\"%{z}\"", "\"\"", OsTypeWindows }, + + { "leading bs", 0, OsTypeWindows }, + { "\\%{a}", "\\hi", OsTypeWindows }, + { "\\%{aa}", "\\\\\"hi ho\"", OsTypeWindows }, + { "\\%{b}", "\\h\\i", OsTypeWindows }, + { "\\%{c}", "\\\\hi", OsTypeWindows }, + { "\\%{d}", "\\hi\\", OsTypeWindows }, + { "\\%{ba}", "\\\\\"h\\i ho\"", OsTypeWindows }, + { "\\%{ca}", "\\\\\"\\hi ho\"", OsTypeWindows }, + { "\\%{da}", "\\\\\"hi ho\\\\\"", OsTypeWindows }, + { "\\%{e}", "\\\\\"h\"\\^\"\"i\"", OsTypeWindows }, + { "\\%{f}", "\\\\\"\"\\^\"\"hi\"", OsTypeWindows }, + { "\\%{g}", "\\\\\"hi\"\\^\"\"\"", OsTypeWindows }, + { "\\%{h}", "\\\\\"h\\\\\"\\^\"\"i\"", OsTypeWindows }, + { "\\%{i}", "\\\\\"\\\\\"\\^\"\"hi\"", OsTypeWindows }, + { "\\%{j}", "\\\\\"hi\\\\\"\\^\"\"\"", OsTypeWindows }, + { "\\%{x}", "\\\\", OsTypeWindows }, + { "\\%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows }, + { "\\%{z}", "\\", OsTypeWindows }, + + { "trailing bs", 0, OsTypeWindows }, + { "%{a}\\", "hi\\", OsTypeWindows }, + { "%{aa}\\", "\"hi ho\"\\", OsTypeWindows }, + { "%{b}\\", "h\\i\\", OsTypeWindows }, + { "%{c}\\", "\\hi\\", OsTypeWindows }, + { "%{d}\\", "hi\\\\", OsTypeWindows }, + { "%{ba}\\", "\"h\\i ho\"\\", OsTypeWindows }, + { "%{ca}\\", "\"\\hi ho\"\\", OsTypeWindows }, + { "%{da}\\", "\"hi ho\\\\\"\\", OsTypeWindows }, + { "%{e}\\", "\"h\"\\^\"\"i\"\\", OsTypeWindows }, + { "%{f}\\", "\"\"\\^\"\"hi\"\\", OsTypeWindows }, + { "%{g}\\", "\"hi\"\\^\"\"\"\\", OsTypeWindows }, + { "%{h}\\", "\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows }, + { "%{i}\\", "\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows }, + { "%{j}\\", "\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows }, + { "%{x}\\", "\\\\", OsTypeWindows }, + { "%{y}\\", "\"\"\\^\"\"\"\\", OsTypeWindows }, + { "%{z}\\", "\\", OsTypeWindows }, + + { "bs-enclosed", 0, OsTypeWindows }, + { "\\%{a}\\", "\\hi\\", OsTypeWindows }, + { "\\%{aa}\\", "\\\\\"hi ho\"\\", OsTypeWindows }, + { "\\%{b}\\", "\\h\\i\\", OsTypeWindows }, + { "\\%{c}\\", "\\\\hi\\", OsTypeWindows }, + { "\\%{d}\\", "\\hi\\\\", OsTypeWindows }, + { "\\%{ba}\\", "\\\\\"h\\i ho\"\\", OsTypeWindows }, + { "\\%{ca}\\", "\\\\\"\\hi ho\"\\", OsTypeWindows }, + { "\\%{da}\\", "\\\\\"hi ho\\\\\"\\", OsTypeWindows }, + { "\\%{e}\\", "\\\\\"h\"\\^\"\"i\"\\", OsTypeWindows }, + { "\\%{f}\\", "\\\\\"\"\\^\"\"hi\"\\", OsTypeWindows }, + { "\\%{g}\\", "\\\\\"hi\"\\^\"\"\"\\", OsTypeWindows }, + { "\\%{h}\\", "\\\\\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows }, + { "\\%{i}\\", "\\\\\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows }, + { "\\%{j}\\", "\\\\\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows }, + { "\\%{x}\\", "\\\\\\", OsTypeWindows }, + { "\\%{y}\\", "\\\\\"\"\\^\"\"\"\\", OsTypeWindows }, + { "\\%{z}\\", "\\\\", OsTypeWindows }, + + { "bs-enclosed and trailing literal quote", 0, OsTypeWindows }, + { "\\%{a}\\\\\\^\"", "\\hi\\\\\\^\"", OsTypeWindows }, + { "\\%{aa}\\\\\\^\"", "\\\\\"hi ho\"\\\\\\^\"", OsTypeWindows }, + { "\\%{b}\\\\\\^\"", "\\h\\i\\\\\\^\"", OsTypeWindows }, + { "\\%{c}\\\\\\^\"", "\\\\hi\\\\\\^\"", OsTypeWindows }, + { "\\%{d}\\\\\\^\"", "\\hi\\\\\\\\\\^\"", OsTypeWindows }, + { "\\%{ba}\\\\\\^\"", "\\\\\"h\\i ho\"\\\\\\^\"", OsTypeWindows }, + { "\\%{ca}\\\\\\^\"", "\\\\\"\\hi ho\"\\\\\\^\"", OsTypeWindows }, + { "\\%{da}\\\\\\^\"", "\\\\\"hi ho\\\\\"\\\\\\^\"", OsTypeWindows }, + { "\\%{e}\\\\\\^\"", "\\\\\"h\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows }, + { "\\%{f}\\\\\\^\"", "\\\\\"\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows }, + { "\\%{g}\\\\\\^\"", "\\\\\"hi\"\\^\"\"\"\\\\\\^\"", OsTypeWindows }, + { "\\%{h}\\\\\\^\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows }, + { "\\%{i}\\\\\\^\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows }, + { "\\%{j}\\\\\\^\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\\^\"", OsTypeWindows }, + { "\\%{x}\\\\\\^\"", "\\\\\\\\\\\\\\^\"", OsTypeWindows }, + { "\\%{y}\\\\\\^\"", "\\\\\"\"\\^\"\"\"\\\\\\^\"", OsTypeWindows }, + { "\\%{z}\\\\\\^\"", "\\\\\\\\\\^\"", OsTypeWindows }, + + { "bs-enclosed and trailing unclosed quote", 0, OsTypeWindows }, + { "\\%{a}\\\\\"", "\\hi\\\\\"", OsTypeWindows }, + { "\\%{aa}\\\\\"", "\\\\\"hi ho\"\\\\\"", OsTypeWindows }, + { "\\%{b}\\\\\"", "\\h\\i\\\\\"", OsTypeWindows }, + { "\\%{c}\\\\\"", "\\\\hi\\\\\"", OsTypeWindows }, + { "\\%{d}\\\\\"", "\\hi\\\\\\\\\"", OsTypeWindows }, + { "\\%{ba}\\\\\"", "\\\\\"h\\i ho\"\\\\\"", OsTypeWindows }, + { "\\%{ca}\\\\\"", "\\\\\"\\hi ho\"\\\\\"", OsTypeWindows }, + { "\\%{da}\\\\\"", "\\\\\"hi ho\\\\\"\\\\\"", OsTypeWindows }, + { "\\%{e}\\\\\"", "\\\\\"h\"\\^\"\"i\"\\\\\"", OsTypeWindows }, + { "\\%{f}\\\\\"", "\\\\\"\"\\^\"\"hi\"\\\\\"", OsTypeWindows }, + { "\\%{g}\\\\\"", "\\\\\"hi\"\\^\"\"\"\\\\\"", OsTypeWindows }, + { "\\%{h}\\\\\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\"", OsTypeWindows }, + { "\\%{i}\\\\\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\"", OsTypeWindows }, + { "\\%{j}\\\\\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\"", OsTypeWindows }, + { "\\%{x}\\\\\"", "\\\\\\\\\\\\\"", OsTypeWindows }, + { "\\%{y}\\\\\"", "\\\\\"\"\\^\"\"\"\\\\\"", OsTypeWindows }, + { "\\%{z}\\\\\"", "\\\\\\\\\"", OsTypeWindows }, + + { "multi-var", 0, OsTypeWindows }, + { "%{x}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows }, + { "%{x}%{z}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows }, + { "%{x}%{z}%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows }, + { "%{x}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows }, + { "%{x}%{z}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows }, + { "%{x}%{z}\\^\"", "\\\\\\^\"", OsTypeWindows }, + { "%{x}\\%{z}", "\\\\", OsTypeWindows }, + { "%{x}%{z}\\%{z}", "\\\\", OsTypeWindows }, + { "%{x}%{z}\\", "\\\\", OsTypeWindows }, + { "%{aa}%{a}", "\"hi hohi\"", OsTypeWindows }, + { "%{aa}%{aa}", "\"hi hohi ho\"", OsTypeWindows }, + { "%{aa}:%{aa}", "\"hi ho\":\"hi ho\"", OsTypeWindows }, + { "hallo ^|%{aa}^|", "hallo ^|\"hi ho\"^|", OsTypeWindows }, + + { "quoted multi-var", 0, OsTypeWindows }, + { "\"%{x}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows }, + { "\"%{x}%{z}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows }, + { "\"%{x}%{z}%{y}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows }, + { "\"%{x}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows }, + { "\"%{x}%{z}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows }, + { "\"%{x}%{z}\"^\"\"\"", "\"\\\\\"^\"\"\"", OsTypeWindows }, + { "\"%{x}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows }, + { "\"%{x}%{z}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows }, + { "\"%{x}%{z}\\\\\"", "\"\\\\\\\\\"", OsTypeWindows }, + { "\"%{aa}%{a}\"", "\"hi hohi\"", OsTypeWindows }, + { "\"%{aa}%{aa}\"", "\"hi hohi ho\"", OsTypeWindows }, + { "\"%{aa}:%{aa}\"", "\"hi ho:hi ho\"", OsTypeWindows }, + + { "plain", 0, OsTypeLinux }, + { "%{a}", "hi", OsTypeLinux }, + { "%{b}", "'hi ho'", OsTypeLinux }, + { "%{c}", "'&special;'", OsTypeLinux }, + { "%{d}", "'h\\i'", OsTypeLinux }, + { "%{e}", "'h\"i'", OsTypeLinux }, + { "%{f}", "'h'\\''i'", OsTypeLinux }, + { "%{z}", "''", OsTypeLinux }, + { "\\%{z}%{z}", "\\%{z}%{z}", OsTypeLinux }, // stupid user check + + { "single-quoted", 0, OsTypeLinux }, + { "'%{a}'", "'hi'", OsTypeLinux }, + { "'%{b}'", "'hi ho'", OsTypeLinux }, + { "'%{c}'", "'&special;'", OsTypeLinux }, + { "'%{d}'", "'h\\i'", OsTypeLinux }, + { "'%{e}'", "'h\"i'", OsTypeLinux }, + { "'%{f}'", "'h'\\''i'", OsTypeLinux }, + { "'%{z}'", "''", OsTypeLinux }, + + { "double-quoted", 0, OsTypeLinux }, + { "\"%{a}\"", "\"hi\"", OsTypeLinux }, + { "\"%{b}\"", "\"hi ho\"", OsTypeLinux }, + { "\"%{c}\"", "\"&special;\"", OsTypeLinux }, + { "\"%{d}\"", "\"h\\\\i\"", OsTypeLinux }, + { "\"%{e}\"", "\"h\\\"i\"", OsTypeLinux }, + { "\"%{f}\"", "\"h'i\"", OsTypeLinux }, + { "\"%{z}\"", "\"\"", OsTypeLinux }, + + { "complex", 0, OsTypeLinux }, + { "echo \"$(echo %{a})\"", "echo \"$(echo hi)\"", OsTypeLinux }, + { "echo \"$(echo %{b})\"", "echo \"$(echo 'hi ho')\"", OsTypeLinux }, + { "echo \"$(echo \"%{a}\")\"", "echo \"$(echo \"hi\")\"", OsTypeLinux }, // These make no sense shell-wise, but they test expando nesting - { "echo \"%{echo %{a}}\"", "echo \"%{echo hi}\"" }, - { "echo \"%{echo %{b}}\"", "echo \"%{echo hi ho}\"" }, - { "echo \"%{echo \"%{a}\"}\"", "echo \"%{echo \"hi\"}\"" }, -#endif + { "echo \"%{echo %{a}}\"", "echo \"%{echo hi}\"", OsTypeLinux }, + { "echo \"%{echo %{b}}\"", "echo \"%{echo hi ho}\"", OsTypeLinux }, + { "echo \"%{echo \"%{a}\"}\"", "echo \"%{echo \"hi\"}\"", OsTypeLinux }, }; const char *title = 0; @@ -581,10 +586,12 @@ void tst_QtcProcess::expandMacros_data() char buf[80]; sprintf(buf, "%s: %s", title, vals[i].in); QTest::newRow(buf) << QString::fromLatin1(vals[i].in) - << QString::fromLatin1(vals[i].out); + << QString::fromLatin1(vals[i].out) + << vals[i].os; sprintf(buf, "padded %s: %s", title, vals[i].in); QTest::newRow(buf) << (sp + QString::fromLatin1(vals[i].in) + sp) - << (sp + QString::fromLatin1(vals[i].out) + sp); + << (sp + QString::fromLatin1(vals[i].out) + sp) + << vals[i].os; } } } @@ -593,8 +600,12 @@ void tst_QtcProcess::expandMacros() { QFETCH(QString, in); QFETCH(QString, out); + QFETCH(OsType, os); - QtcProcess::expandMacros(&in, &mx); + if (os == OsTypeWindows) + QtcProcess::expandMacros(&in, &mxWin, os); + else + QtcProcess::expandMacros(&in, &mxUnix, os); QCOMPARE(in, out); } @@ -602,75 +613,78 @@ void tst_QtcProcess::iterations_data() { QTest::addColumn<QString>("in"); QTest::addColumn<QString>("out"); + QTest::addColumn<OsType>("os"); static const struct { const char * const in; const char * const out; + OsType os; } vals[] = { -#ifdef Q_OS_WIN - { "", "" }, - { "hi", "hi" }, - { " hi ", "hi" }, - { "hi ho", "hi ho" }, - { "\"hi ho\" sucker", "\"hi ho\" sucker" }, - { "\"hi\"^\"\"ho\" sucker", "\"hi\"\\^\"\"ho\" sucker" }, - { "\"hi\"\\^\"\"ho\" sucker", "\"hi\"\\^\"\"ho\" sucker" }, - { "hi^|ho", "\"hi|ho\"" }, - { "c:\\", "c:\\" }, - { "\"c:\\\\\"", "c:\\" }, - { "\\hi\\ho", "\\hi\\ho" }, - { "hi null%", "hi null%" }, - { "hi null% ho", "hi null% ho" }, - { "hi null%here ho", "hi null%here ho" }, - { "hi null%here%too ho", "hi {} ho" }, - { "echo hello | more", "echo hello" }, - { "echo hello| more", "echo hello" }, -#else - { "", "" }, - { " ", "" }, - { "hi", "hi" }, - { " hi ", "hi" }, - { "'hi'", "hi" }, - { "hi ho", "hi ho" }, - { "\"hi ho\" sucker", "'hi ho' sucker" }, - { "\"hi\\\"ho\" sucker", "'hi\"ho' sucker" }, - { "\"hi'ho\" sucker", "'hi'\\''ho' sucker" }, - { "'hi ho' sucker", "'hi ho' sucker" }, - { "\\\\", "'\\'" }, - { "'\\'", "'\\'" }, - { "hi 'null${here}too' ho", "hi 'null${here}too' ho" }, - { "hi null${here}too ho", "hi {} ho" }, - { "hi $(echo $dollar cent) ho", "hi {} ho" }, - { "hi `echo $dollar \\`echo cent\\` | cat` ho", "hi {} ho" }, - { "echo hello | more", "echo hello" }, - { "echo hello| more", "echo hello" }, -#endif + { "", "", OsTypeWindows }, + { "hi", "hi", OsTypeWindows }, + { " hi ", "hi", OsTypeWindows }, + { "hi ho", "hi ho", OsTypeWindows }, + { "\"hi ho\" sucker", "\"hi ho\" sucker", OsTypeWindows }, + { "\"hi\"^\"\"ho\" sucker", "\"hi\"\\^\"\"ho\" sucker", OsTypeWindows }, + { "\"hi\"\\^\"\"ho\" sucker", "\"hi\"\\^\"\"ho\" sucker", OsTypeWindows }, + { "hi^|ho", "\"hi|ho\"", OsTypeWindows }, + { "c:\\", "c:\\", OsTypeWindows }, + { "\"c:\\\\\"", "c:\\", OsTypeWindows }, + { "\\hi\\ho", "\\hi\\ho", OsTypeWindows }, + { "hi null%", "hi null%", OsTypeWindows }, + { "hi null% ho", "hi null% ho", OsTypeWindows }, + { "hi null%here ho", "hi null%here ho", OsTypeWindows }, + { "hi null%here%too ho", "hi {} ho", OsTypeWindows }, + { "echo hello | more", "echo hello", OsTypeWindows }, + { "echo hello| more", "echo hello", OsTypeWindows }, + + { "", "", OsTypeLinux }, + { " ", "", OsTypeLinux }, + { "hi", "hi", OsTypeLinux }, + { " hi ", "hi", OsTypeLinux }, + { "'hi'", "hi", OsTypeLinux }, + { "hi ho", "hi ho", OsTypeLinux }, + { "\"hi ho\" sucker", "'hi ho' sucker", OsTypeLinux }, + { "\"hi\\\"ho\" sucker", "'hi\"ho' sucker", OsTypeLinux }, + { "\"hi'ho\" sucker", "'hi'\\''ho' sucker", OsTypeLinux }, + { "'hi ho' sucker", "'hi ho' sucker", OsTypeLinux }, + { "\\\\", "'\\'", OsTypeLinux }, + { "'\\'", "'\\'", OsTypeLinux }, + { "hi 'null${here}too' ho", "hi 'null${here}too' ho", OsTypeLinux }, + { "hi null${here}too ho", "hi {} ho", OsTypeLinux }, + { "hi $(echo $dollar cent) ho", "hi {} ho", OsTypeLinux }, + { "hi `echo $dollar \\`echo cent\\` | cat` ho", "hi {} ho", OsTypeLinux }, + { "echo hello | more", "echo hello", OsTypeLinux }, + { "echo hello| more", "echo hello", OsTypeLinux }, }; for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) QTest::newRow(vals[i].in) << QString::fromLatin1(vals[i].in) - << QString::fromLatin1(vals[i].out); + << QString::fromLatin1(vals[i].out) + << vals[i].os; } void tst_QtcProcess::iterations() { QFETCH(QString, in); QFETCH(QString, out); + QFETCH(OsType, os); QString outstr; - for (QtcProcess::ArgIterator ait(&in); ait.next(); ) + for (QtcProcess::ArgIterator ait(&in, os); ait.next(); ) { if (ait.isSimple()) - QtcProcess::addArg(&outstr, ait.value()); + QtcProcess::addArg(&outstr, ait.value(), os); else QtcProcess::addArgs(&outstr, "{}"); + } QCOMPARE(outstr, out); } -void tst_QtcProcess::iteratorEdits() +void tst_QtcProcess::iteratorEditsHelper(OsType osType) { QString in1 = "one two three", in2 = in1, in3 = in1, in4 = in1, in5 = in1; - QtcProcess::ArgIterator ait1(&in1); + QtcProcess::ArgIterator ait1(&in1, osType); QVERIFY(ait1.next()); ait1.deleteArg(); QVERIFY(ait1.next()); @@ -680,7 +694,7 @@ void tst_QtcProcess::iteratorEdits() ait1.appendArg("four"); QCOMPARE(in1, QString::fromLatin1("two three four")); - QtcProcess::ArgIterator ait2(&in2); + QtcProcess::ArgIterator ait2(&in2, osType); QVERIFY(ait2.next()); QVERIFY(ait2.next()); ait2.deleteArg(); @@ -689,7 +703,7 @@ void tst_QtcProcess::iteratorEdits() QVERIFY(!ait2.next()); QCOMPARE(in2, QString::fromLatin1("one three four")); - QtcProcess::ArgIterator ait3(&in3); + QtcProcess::ArgIterator ait3(&in3, osType); QVERIFY(ait3.next()); ait3.appendArg("one-b"); QVERIFY(ait3.next()); @@ -698,7 +712,7 @@ void tst_QtcProcess::iteratorEdits() QVERIFY(!ait3.next()); QCOMPARE(in3, QString::fromLatin1("one one-b two")); - QtcProcess::ArgIterator ait4(&in4); + QtcProcess::ArgIterator ait4(&in4, osType); ait4.appendArg("pre-one"); QVERIFY(ait4.next()); QVERIFY(ait4.next()); @@ -707,7 +721,7 @@ void tst_QtcProcess::iteratorEdits() QVERIFY(!ait4.next()); QCOMPARE(in4, QString::fromLatin1("pre-one one two")); - QtcProcess::ArgIterator ait5(&in5); + QtcProcess::ArgIterator ait5(&in5, osType); QVERIFY(ait5.next()); QVERIFY(ait5.next()); QVERIFY(ait5.next()); @@ -717,6 +731,16 @@ void tst_QtcProcess::iteratorEdits() QCOMPARE(in5, QString::fromLatin1("one two")); } +void tst_QtcProcess::iteratorEditsWindows() +{ + iteratorEditsHelper(OsTypeWindows); +} + +void tst_QtcProcess::iteratorEditsLinux() +{ + iteratorEditsHelper(OsTypeLinux); +} + QTEST_MAIN(tst_QtcProcess) #include "tst_qtcprocess.moc" |