QCommandLineParser: add parsing mode for options after arguments

The new mode, ParseAsPositionalArguments, allows to interpret
"application argument --opt" as having two positional arguments,
"argument" and "--opt".
This mode is useful for executables that aim to launch other executables
(e.g. wrappers, debugging tools, etc.) or who support internal commands
followed by options for the command. "argument" is the name of the command,
and all options occurring after it can be collected and parsed by another
command line parser, possibly in another executable.

[ChangeLog][QtCore][QCommandLineParser] Add parsing mode for options
after arguments, to allow treating them as more arguments.

Change-Id: I48d5fcf90f2f59deda8422538b8ebf2680fae828
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
David Faure 2015-02-06 11:18:55 +01:00
parent aabbb87f4a
commit 74117b5100
3 changed files with 87 additions and 3 deletions

View File

@ -53,6 +53,7 @@ class QCommandLineParserPrivate
public:
inline QCommandLineParserPrivate()
: singleDashWordOptionMode(QCommandLineParser::ParseAsCompactedShortOptions),
optionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsOptions),
builtinVersionOption(false),
builtinHelpOption(false),
needsParsing(true)
@ -103,6 +104,9 @@ public:
//! The parsing mode for "-abc"
QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode;
//! How to parse "arg -option"
QCommandLineParser::OptionsAfterPositionalArgumentsMode optionsAfterPositionalArgumentsMode;
//! Whether addVersionOption was called
bool builtinVersionOption;
@ -298,6 +302,41 @@ void QCommandLineParser::setSingleDashWordOptionMode(QCommandLineParser::SingleD
d->singleDashWordOptionMode = singleDashWordOptionMode;
}
/*!
\enum QCommandLineParser::OptionsAfterPositionalArgumentsMode
This enum describes the way the parser interprets options that
occur after positional arguments.
\value ParseAsOptions \c{application argument --opt -t} is interpreted as setting
the options \c{opt} and \c{t}, just like \c{application --opt -t argument} would do.
This is the default parsing mode. In order to specify that \c{--opt} and \c{-t}
are positional arguments instead, the user can use \c{--}, as in
\c{application argument -- --opt -t}.
\value ParseAsPositionalArguments \c{application argument --opt} is interpreted as
having two positional arguments, \c{argument} and \c{--opt}.
This mode is useful for executables that aim to launch other executables
(e.g. wrappers, debugging tools, etc.) or that support internal commands
followed by options for the command. \c{argument} is the name of the command,
and all options occurring after it can be collected and parsed by another
command line parser, possibly in another executable.
\sa setOptionsAfterPositionalArgumentsMode()
\since 5.5
*/
/*!
Sets the parsing mode to \a parsingMode.
This must be called before process() or parse().
\since 5.5
*/
void QCommandLineParser::setOptionsAfterPositionalArgumentsMode(QCommandLineParser::OptionsAfterPositionalArgumentsMode parsingMode)
{
d->optionsAfterPositionalArgumentsMode = parsingMode;
}
/*!
Adds the option \a option to look for while parsing.
@ -640,7 +679,7 @@ bool QCommandLineParserPrivate::parse(const QStringList &args)
const QLatin1Char dashChar('-');
const QLatin1Char assignChar('=');
bool doubleDashFound = false;
bool forcePositional = false;
errorText.clear();
positionalArgumentList.clear();
optionNames.clear();
@ -658,7 +697,7 @@ bool QCommandLineParserPrivate::parse(const QStringList &args)
for (; argumentIterator != args.end() ; ++argumentIterator) {
QString argument = *argumentIterator;
if (doubleDashFound) {
if (forcePositional) {
positionalArgumentList.append(argument);
} else if (argument.startsWith(doubleDashString)) {
if (argument.length() > 2) {
@ -670,7 +709,7 @@ bool QCommandLineParserPrivate::parse(const QStringList &args)
error = true;
}
} else {
doubleDashFound = true;
forcePositional = true;
}
} else if (argument.startsWith(dashChar)) {
if (argument.size() == 1) { // single dash ("stdin")
@ -722,6 +761,8 @@ bool QCommandLineParserPrivate::parse(const QStringList &args)
}
} else {
positionalArgumentList.append(argument);
if (optionsAfterPositionalArgumentsMode == QCommandLineParser::ParseAsPositionalArguments)
forcePositional = true;
}
if (argumentIterator == args.end())
break;

View File

@ -57,6 +57,12 @@ public:
};
void setSingleDashWordOptionMode(SingleDashWordOptionMode parsingMode);
enum OptionsAfterPositionalArgumentsMode {
ParseAsOptions,
ParseAsPositionalArguments
};
void setOptionsAfterPositionalArgumentsMode(OptionsAfterPositionalArgumentsMode mode);
bool addOption(const QCommandLineOption &commandLineOption);
bool addOptions(const QList<QCommandLineOption> &options);

View File

@ -35,6 +35,7 @@
#include <QtCore/QCommandLineParser>
Q_DECLARE_METATYPE(char**)
Q_DECLARE_METATYPE(QCommandLineParser::OptionsAfterPositionalArgumentsMode)
class tst_QCommandLineParser : public QObject
{
@ -51,6 +52,8 @@ private slots:
void testPositionalArguments();
void testBooleanOption_data();
void testBooleanOption();
void testOptionsAndPositional_data();
void testOptionsAndPositional();
void testMultipleNames_data();
void testMultipleNames();
void testSingleValueOption_data();
@ -141,6 +144,40 @@ void tst_QCommandLineParser::testBooleanOption()
QVERIFY(!parser.isSet("c"));
}
void tst_QCommandLineParser::testOptionsAndPositional_data()
{
QTest::addColumn<QStringList>("args");
QTest::addColumn<QStringList>("expectedOptionNames");
QTest::addColumn<bool>("expectedIsSet");
QTest::addColumn<QStringList>("expectedPositionalArguments");
QTest::addColumn<QCommandLineParser::OptionsAfterPositionalArgumentsMode>("parsingMode");
const QStringList arg = QStringList() << "arg";
QTest::newRow("before_positional_default") << (QStringList() << "tst_qcommandlineparser" << "-b" << "arg") << (QStringList() << "b") << true << arg << QCommandLineParser::ParseAsOptions;
QTest::newRow("after_positional_default") << (QStringList() << "tst_qcommandlineparser" << "arg" << "-b") << (QStringList() << "b") << true << arg << QCommandLineParser::ParseAsOptions;
QTest::newRow("before_positional_parseAsArg") << (QStringList() << "tst_qcommandlineparser" << "-b" << "arg") << (QStringList() << "b") << true << arg << QCommandLineParser::ParseAsPositionalArguments;
QTest::newRow("after_positional_parseAsArg") << (QStringList() << "tst_qcommandlineparser" << "arg" << "-b") << (QStringList()) << false << (QStringList() << "arg" << "-b") << QCommandLineParser::ParseAsPositionalArguments;
}
void tst_QCommandLineParser::testOptionsAndPositional()
{
QFETCH(QStringList, args);
QFETCH(QStringList, expectedOptionNames);
QFETCH(bool, expectedIsSet);
QFETCH(QStringList, expectedPositionalArguments);
QFETCH(QCommandLineParser::OptionsAfterPositionalArgumentsMode, parsingMode);
QCoreApplication app(empty_argc, empty_argv);
QCommandLineParser parser;
parser.setOptionsAfterPositionalArgumentsMode(parsingMode);
QVERIFY(parser.addOption(QCommandLineOption(QStringLiteral("b"), QStringLiteral("a boolean option"))));
QVERIFY(parser.parse(args));
QCOMPARE(parser.optionNames(), expectedOptionNames);
QCOMPARE(parser.isSet("b"), expectedIsSet);
QCOMPARE(parser.values("b"), QStringList());
QCOMPARE(parser.positionalArguments(), expectedPositionalArguments);
}
void tst_QCommandLineParser::testMultipleNames_data()
{
QTest::addColumn<QStringList>("args");