Obsolete command-string parsing QProcess::start/execute overloads

The command string parsing covers only simple quoting
patterns, while users tend to expect something that is in line with
their shell.
The overloads that take a QStringList are the recommended APIs to use
anyway, so exposing the splitting  method as a static function for
which we document the exact behavior allows callers to post-process
the QStringList, before calling the preferred overloads.

[ChangeLog][QtCore][QProcess] Overloads of start/execute/startDatached
that parse a single command string into program and arguments have
been marked as deprecated. A static helper splitCommand has
been added to construct a QStringList from a command string.

Change-Id: Ie91fcfb5eae6a52e5065efc60d2d9e068d20869d
Fixes: QTBUG-80640
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
This commit is contained in:
Volker Hilsheimer 2020-02-07 14:12:57 +01:00
parent adfd0914e3
commit 92eea63349
2 changed files with 41 additions and 11 deletions

View File

@ -2246,8 +2246,16 @@ void QProcessPrivate::start(QIODevice::OpenMode mode)
startProcess(); startProcess();
} }
/*!
\since 5.15
static QStringList parseCombinedArgString(const QString &program) Splits the string \a command into a list of tokens, and returns
the list.
Tokens with spaces can be surrounded by double quotes; three
consecutive double quotes represent the quote character itself.
*/
QStringList QProcess::splitCommand(const QString &command)
{ {
QStringList args; QStringList args;
QString tmp; QString tmp;
@ -2257,13 +2265,13 @@ static QStringList parseCombinedArgString(const QString &program)
// handle quoting. tokens can be surrounded by double quotes // handle quoting. tokens can be surrounded by double quotes
// "hello world". three consecutive double quotes represent // "hello world". three consecutive double quotes represent
// the quote character itself. // the quote character itself.
for (int i = 0; i < program.size(); ++i) { for (int i = 0; i < command.size(); ++i) {
if (program.at(i) == QLatin1Char('"')) { if (command.at(i) == QLatin1Char('"')) {
++quoteCount; ++quoteCount;
if (quoteCount == 3) { if (quoteCount == 3) {
// third consecutive quote // third consecutive quote
quoteCount = 0; quoteCount = 0;
tmp += program.at(i); tmp += command.at(i);
} }
continue; continue;
} }
@ -2272,13 +2280,13 @@ static QStringList parseCombinedArgString(const QString &program)
inQuote = !inQuote; inQuote = !inQuote;
quoteCount = 0; quoteCount = 0;
} }
if (!inQuote && program.at(i).isSpace()) { if (!inQuote && command.at(i).isSpace()) {
if (!tmp.isEmpty()) { if (!tmp.isEmpty()) {
args += tmp; args += tmp;
tmp.clear(); tmp.clear();
} }
} else { } else {
tmp += program.at(i); tmp += command.at(i);
} }
} }
if (!tmp.isEmpty()) if (!tmp.isEmpty())
@ -2288,6 +2296,7 @@ static QStringList parseCombinedArgString(const QString &program)
} }
/*! /*!
\obsolete
\overload \overload
Starts the command \a command in a new process. Starts the command \a command in a new process.
@ -2324,11 +2333,13 @@ static QStringList parseCombinedArgString(const QString &program)
list-based API. In these rare cases you need to use setProgram() and list-based API. In these rare cases you need to use setProgram() and
setNativeArguments() instead of this function. setNativeArguments() instead of this function.
\sa splitCommand()
*/ */
#if !defined(QT_NO_PROCESS_COMBINED_ARGUMENT_START) #if !defined(QT_NO_PROCESS_COMBINED_ARGUMENT_START)
void QProcess::start(const QString &command, OpenMode mode) void QProcess::start(const QString &command, OpenMode mode)
{ {
QStringList args = parseCombinedArgString(command); QStringList args = splitCommand(command);
if (args.isEmpty()) { if (args.isEmpty()) {
Q_D(QProcess); Q_D(QProcess);
d->setErrorAndEmit(QProcess::FailedToStart, tr("No program defined")); d->setErrorAndEmit(QProcess::FailedToStart, tr("No program defined"));
@ -2493,6 +2504,7 @@ int QProcess::execute(const QString &program, const QStringList &arguments)
} }
/*! /*!
\obsolete
\overload \overload
Starts the program \a command in a new process, waits for it to finish, Starts the program \a command in a new process, waits for it to finish,
@ -2503,7 +2515,7 @@ int QProcess::execute(const QString &program, const QStringList &arguments)
After the \a command string has been split and unquoted, this function After the \a command string has been split and unquoted, this function
behaves like the overload which takes the arguments as a string list. behaves like the overload which takes the arguments as a string list.
\sa start() \sa start(), splitCommand()
*/ */
int QProcess::execute(const QString &command) int QProcess::execute(const QString &command)
{ {
@ -2559,6 +2571,7 @@ bool QProcess::startDetached(const QString &program,
} }
/*! /*!
\obsolete
\overload startDetached() \overload startDetached()
Starts the command \a command in a new process, and detaches from it. Starts the command \a command in a new process, and detaches from it.
@ -2569,11 +2582,11 @@ bool QProcess::startDetached(const QString &program,
After the \a command string has been split and unquoted, this function After the \a command string has been split and unquoted, this function
behaves like the overload which takes the arguments as a string list. behaves like the overload which takes the arguments as a string list.
\sa start(const QString &command, QIODevice::OpenMode mode) \sa start(const QString &command, QIODevice::OpenMode mode), splitCommand()
*/ */
bool QProcess::startDetached(const QString &command) bool QProcess::startDetached(const QString &command)
{ {
QStringList args = parseCombinedArgString(command); QStringList args = splitCommand(command);
if (args.isEmpty()) if (args.isEmpty())
return false; return false;

View File

@ -160,7 +160,13 @@ public:
void start(const QString &program, const QStringList &arguments, OpenMode mode = ReadWrite); void start(const QString &program, const QStringList &arguments, OpenMode mode = ReadWrite);
#if !defined(QT_NO_PROCESS_COMBINED_ARGUMENT_START) #if !defined(QT_NO_PROCESS_COMBINED_ARGUMENT_START)
#if QT_DEPRECATED_SINCE(5, 15)
QT_DEPRECATED_X(
"Use QProcess::start(const QString &program, const QStringList &arguments,"
"OpenMode mode = ReadWrite) instead"
)
void start(const QString &command, OpenMode mode = ReadWrite); void start(const QString &command, OpenMode mode = ReadWrite);
#endif
#endif #endif
void start(OpenMode mode = ReadWrite); void start(OpenMode mode = ReadWrite);
bool startDetached(qint64 *pid = nullptr); bool startDetached(qint64 *pid = nullptr);
@ -250,8 +256,12 @@ public:
bool atEnd() const override; // ### Qt6: remove trivial override bool atEnd() const override; // ### Qt6: remove trivial override
static int execute(const QString &program, const QStringList &arguments); static int execute(const QString &program, const QStringList &arguments);
#if QT_DEPRECATED_SINCE(5, 15)
QT_DEPRECATED_X(
"Use QProcess::execute(const QString &program, const QStringList &arguments) instead"
)
static int execute(const QString &command); static int execute(const QString &command);
#endif
static bool startDetached(const QString &program, const QStringList &arguments, static bool startDetached(const QString &program, const QStringList &arguments,
const QString &workingDirectory const QString &workingDirectory
#if defined(Q_QDOC) #if defined(Q_QDOC)
@ -261,12 +271,19 @@ public:
#if !defined(Q_QDOC) #if !defined(Q_QDOC)
static bool startDetached(const QString &program, const QStringList &arguments); // ### Qt6: merge overloads static bool startDetached(const QString &program, const QStringList &arguments); // ### Qt6: merge overloads
#endif #endif
#if QT_DEPRECATED_SINCE(5, 15)
QT_DEPRECATED_X(
"Use QProcess::startDetached(const QString &program, const QStringList &arguments) instead"
)
static bool startDetached(const QString &command); static bool startDetached(const QString &command);
#endif
static QStringList systemEnvironment(); static QStringList systemEnvironment();
static QString nullDevice(); static QString nullDevice();
static QStringList splitCommand(const QString &command);
public Q_SLOTS: public Q_SLOTS:
void terminate(); void terminate();
void kill(); void kill();