summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Faure <faure@kde.org>2013-08-24 11:15:19 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-08-24 21:33:39 +0200
commit404598b61366e681100893052fdb394702d3bcbf (patch)
treeb8741268b3960d6f47fd9a562a362e6503cab75c
parent1411a6f1acfcbea3f31ac461c27cd3e1be87ee1c (diff)
downloadqtbase-404598b61366e681100893052fdb394702d3bcbf.tar.gz
Long live QCommandLineParser!
The QCommandLineParser class provides a means for handling the command line options. QCoreApplication provides the command-line arguments as a simple list of strings. QCommandLineParser provides the ability to define a set of options, parse the command-line arguments, and store which options have actually been used, as well as option values. Done-with: Laszlo Papp <lpapp@kde.org> Change-Id: Ic7bebc10b3f8d8dd06ad0f4bb897c51d566e3b7c Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r--.gitignore1
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_tools_qcommandlineoption.cpp44
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_tools_qcommandlineparser.cpp141
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp4
-rw-r--r--src/corelib/tools/qcommandlineoption.cpp308
-rw-r--r--src/corelib/tools/qcommandlineoption.h92
-rw-r--r--src/corelib/tools/qcommandlineparser.cpp896
-rw-r--r--src/corelib/tools/qcommandlineparser.h106
-rw-r--r--src/corelib/tools/tools.pri4
-rw-r--r--tests/auto/corelib/tools/qcommandlineparser/qcommandlineparser.pro3
-rw-r--r--tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp93
-rw-r--r--tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.pro6
-rw-r--r--tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp531
-rw-r--r--tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.pro4
-rw-r--r--tests/auto/corelib/tools/tools.pro1
15 files changed, 2233 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 9e965e9c12..cc6102b64e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -311,6 +311,7 @@ tests/auto/corelib/io/qresourceengine/qresourceengine
tests/auto/corelib/codecs/qtextcodec/echo/echo
tests/auto/corelib/plugin/quuid/testProcessUniqueness/testProcessUniqueness
tests/auto/corelib/io/qlockfile/qlockfiletesthelper/qlockfile_test_helper
+tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper
tests/auto/dbus/qdbusabstractadaptor/qmyserver/qmyserver
tests/auto/dbus/qdbusabstractinterface/qpinger/qpinger
tests/auto/dbus/qdbusabstractinterface/test/pinger_interface.*
diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qcommandlineoption.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qcommandlineoption.cpp
new file mode 100644
index 0000000000..d4c745215f
--- /dev/null
+++ b/src/corelib/doc/snippets/code/src_corelib_tools_qcommandlineoption.cpp
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 David Faure <faure@kde.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+QCommandLineOption verboseOption("verbose", "Verbose mode. Prints out more information.");
+QCommandLineOption outputOption(QStringList() << "o" << "output", "Write generated data into <file>.", "file");
+//! [0]
diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qcommandlineparser.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qcommandlineparser.cpp
new file mode 100644
index 0000000000..569cb6af80
--- /dev/null
+++ b/src/corelib/doc/snippets/code/src_corelib_tools_qcommandlineparser.cpp
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 David Faure <faure@kde.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+bool verbose = parser.isSet("verbose");
+//! [0]
+
+//! [1]
+// Usage: image-editor file
+//
+// Arguments:
+// file The file to open.
+parser.addPositionalArgument("file", QCoreApplication::translate("main", "The file to open."));
+
+// Usage: web-browser [urls...]
+//
+// Arguments:
+// urls URLs to open, optionally.
+parser.addPositionalArgument("urls", QCoreApplication::translate("main", "URLs to open, optionally."), "[urls...]");
+
+// Usage: cp source destination
+//
+// Arguments:
+// source Source file to copy.
+// destination Destination directory.
+parser.addPositionalArgument("source", QCoreApplication::translate("main", "Source file to copy."));
+parser.addPositionalArgument("destination", QCoreApplication::translate("main", "Destination directory."));
+//! [1]
+
+//! [2]
+parser.addPositionalArgument("command", "The command to execute.");
+
+// Call parse() to find out the positional arguments.
+parser.parse(QCoreApplication::arguments());
+
+const QStringList args = parser.positionalArguments();
+const QString command = args.isEmpty() ? QString() : args.first();
+if (command == "resize") {
+ parser.clearPositionalArguments();
+ parser.addPositionalArgument("resize", "Resize the object to a new size.", "resize [resize_options]");
+ parser.addOption(QCommandLineOption("size", "New size.", "new_size"));
+ parser.process(app);
+ // ...
+}
+
+This code results in context-dependent help:
+
+$ tool --help
+Usage: tool command
+
+Arguments:
+ command The command to execute.
+
+$ tool resize --help
+Usage: tool resize [resize_options]
+
+Options:
+ --size <size> New size.
+
+Arguments:
+ resize Resize the object to a new size.
+
+//! [2]
+
+//! [3]
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ app.setApplicationName("my-copy-program");
+ app.setApplicationVersion("1.0");
+
+ QCommandLineParser parser;
+ parser.addHelpOption("Test helper");
+ parser.addVersionOption();
+ parser.addRemainingArgument("source", QCoreApplication::translate("main", "Source file to copy."));
+ parser.addRemainingArgument("destination", QCoreApplication::translate("main", "Destination directory."));
+
+ // A boolean option with a single name (-p)
+ QCommandLineOption showProgressOption("p", QCoreApplication::translate("main", "Show progress during copy"));
+ parser.addOption(showProgressOption);
+
+ // A boolean option with multiple names (-f, --force)
+ QCommandLineOption forceOption(QStringList() << "f" << "force", "Overwrite existing files.");
+ parser.addOption(forceOption);
+
+ // An option with a value
+ QCommandLineOption targetDirectoryOption(QStringList() << "t" << "target-directory",
+ QCoreApplication::translate("main", "Copy all source files into <directory>."),
+ QCoreApplication::translate("main", "directory"));
+ parser.addOption(targetDirectoryOption);
+
+ // Process the actual command line arguments given by the user
+ parser.process(app);
+
+ const QStringList args = parser.remainingArguments();
+ // source is args.at(0), destination is args.at(1)
+
+ bool showProgress = parser.isSet(showProgressOption);
+ bool force = parser.isSet(forceOption);
+ QString targetDir = parser.value(targetDirectoryOption);
+ // ...
+}
+
+//! [3]
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index 3460b9e228..bba878d2eb 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -589,6 +589,8 @@ void QCoreApplicationPrivate::initLocale()
Note that some arguments supplied by the user may have been
processed and removed by QCoreApplication.
+ For more advanced command line option handling, create a QCommandLineParser.
+
\section1 Locale Settings
On Unix/Linux Qt is configured to use the system locale settings by
@@ -2060,7 +2062,7 @@ qint64 QCoreApplication::applicationPid()
As a result of this, the string given by arguments().at(0) might not be
the program name on Windows, depending on how the application was started.
- \sa applicationFilePath()
+ \sa applicationFilePath(), QCommandLineParser
*/
QStringList QCoreApplication::arguments()
diff --git a/src/corelib/tools/qcommandlineoption.cpp b/src/corelib/tools/qcommandlineoption.cpp
new file mode 100644
index 0000000000..4f9e166587
--- /dev/null
+++ b/src/corelib/tools/qcommandlineoption.cpp
@@ -0,0 +1,308 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
+** Copyright (C) 2013 David Faure <faure@kde.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcommandlineoption.h"
+
+#include "qset.h"
+
+QT_BEGIN_NAMESPACE
+
+class QCommandLineOptionPrivate : public QSharedData
+{
+public:
+ inline QCommandLineOptionPrivate()
+ { }
+
+ void setNames(const QStringList &nameList);
+
+ //! The list of names used for this option.
+ QStringList names;
+
+ //! The documentation name for the value, if one is expected
+ //! Example: "-o <file>" means valueName == "file"
+ QString valueName;
+
+ //! The description used for this option.
+ QString description;
+
+ //! The list of default values used for this option.
+ QStringList defaultValues;
+};
+
+/*!
+ \since 5.2
+ \class QCommandLineOption
+ \brief The QCommandLineOption class defines a possible command-line option.
+ \inmodule QtCore
+ \ingroup shared
+ \ingroup tools
+
+ This class is used to describe an option on the command line. It allows
+ different ways of defining the same option with multiple aliases possible.
+ It is also used to describe how the option is used - it may be a flag (e.g. \c{-v})
+ or take an argument (e.g. \c{-o file}).
+
+ Examples:
+ \snippet code/src_corelib_tools_qcommandlineoption.cpp 0
+
+ \sa QCommandLineParser
+*/
+
+/*!
+ Constructs a command line option object with the given arguments.
+
+ The name of the option is set to \a name.
+ The name can be either short or long. If the name is one character in
+ length, it is considered a short name. Option names must not be empty,
+ must not start with a dash or a slash character, must not contain a \c{=}
+ and cannot be repeated.
+
+ The description is set to \a description. It is customary to add a "."
+ at the end of the description.
+
+ In addition, the \a valueName can be set if the option expects a value.
+ The default value for the option is set to \a defaultValue.
+
+ \sa setDescription(), setValueName(), setDefaultValues()
+*/
+QCommandLineOption::QCommandLineOption(const QString &name, const QString &description,
+ const QString &valueName,
+ const QString &defaultValue)
+ : d(new QCommandLineOptionPrivate)
+{
+ d->setNames(QStringList(name));
+ setValueName(valueName);
+ setDescription(description);
+ setDefaultValue(defaultValue);
+}
+
+/*!
+ Constructs a command line option object with the given arguments.
+
+ This overload allows to set multiple names for the option, for instance
+ \c{o} and \c{output}.
+
+ The names of the option are set to \a names.
+ The names can be either short or long. Any name in the list that is one
+ character in length is a short name. Option names must not be empty,
+ must not start with a dash or a slash character, must not contain a \c{=}
+ and cannot be repeated.
+
+ The description is set to \a description. It is customary to add a "."
+ at the end of the description.
+
+ In addition, the \a valueName can be set if the option expects a value.
+ The default value for the option is set to \a defaultValue.
+
+ \sa setDescription(), setValueName(), setDefaultValues()
+*/
+QCommandLineOption::QCommandLineOption(const QStringList &names, const QString &description,
+ const QString &valueName,
+ const QString &defaultValue)
+ : d(new QCommandLineOptionPrivate)
+{
+ d->setNames(names);
+ setValueName(valueName);
+ setDescription(description);
+ setDefaultValue(defaultValue);
+}
+
+/*!
+ Constructs a QCommandLineOption object that is a copy of the QCommandLineOption
+ object \a other.
+
+ \sa operator=()
+*/
+QCommandLineOption::QCommandLineOption(const QCommandLineOption &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys the command line option object.
+*/
+QCommandLineOption::~QCommandLineOption()
+{
+}
+
+/*!
+ Makes a copy of the \a other object and assigns it to this QCommandLineOption
+ object.
+*/
+QCommandLineOption &QCommandLineOption::operator=(const QCommandLineOption &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \fn void QCommandLineOption::swap(QCommandLineOption &other)
+
+ Swaps option \a other with this option. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ Returns the names set for this option.
+ */
+QStringList QCommandLineOption::names() const
+{
+ return d->names;
+}
+
+void QCommandLineOptionPrivate::setNames(const QStringList &nameList)
+{
+ names.clear();
+ if (nameList.isEmpty())
+ qWarning("Options must have at least one name");
+ foreach (const QString &name, nameList) {
+ if (name.isEmpty())
+ qWarning("Option names cannot be empty");
+ else if (name.startsWith(QLatin1Char('-')))
+ qWarning("Option names cannot start with a '-'");
+ else if (name.startsWith(QLatin1Char('/')))
+ qWarning("Option names cannot start with a '/'");
+ else if (name.contains(QLatin1Char('=')))
+ qWarning("Option names cannot contain a '='");
+ else
+ names.append(name);
+ }
+}
+
+/*!
+ Sets the name of the expected value, for the documentation, to \a valueName.
+
+ Options without a value assigned have a boolean-like behavior:
+ either the user specifies --option or they don't.
+
+ Options with a value assigned need to set a name for the expected value,
+ for the documentation of the option in the help output. An option with names \c{o} and \c{output},
+ and a value name of \c{file} will appear as \c{-o, --output <file>}.
+
+ Call QCommandLineParser::argument() if you expect the option to be present
+ only once, and QCommandLineParser::arguments() if you expect that option
+ to be present multiple times.
+
+ \sa valueName()
+ */
+void QCommandLineOption::setValueName(const QString &valueName)
+{
+ d->valueName = valueName;
+}
+
+/*!
+ Returns the name of the expected value.
+
+ If empty, the option doesn't take a value.
+
+ \sa setValueName()
+ */
+QString QCommandLineOption::valueName() const
+{
+ return d->valueName;
+}
+
+/*!
+ Sets the description used for this option to \a description.
+
+ It is customary to add a "." at the end of the description.
+
+ The description is used by QCommandLineParser::showHelp().
+
+ \sa description()
+ */
+void QCommandLineOption::setDescription(const QString &description)
+{
+ d->description = description;
+}
+
+/*!
+ Returns the description set for this option.
+
+ \sa setDescription()
+ */
+QString QCommandLineOption::description() const
+{
+ return d->description;
+}
+
+/*!
+ Sets the default value used for this option to \a defaultValue.
+
+ The default value is used if the user of the application does not specify
+ the option on the command line.
+
+ If \a defaultValue is empty, the option has no default values.
+
+ \sa defaultValues() setDefaultValues()
+ */
+void QCommandLineOption::setDefaultValue(const QString &defaultValue)
+{
+ d->defaultValues.clear();
+ if (!defaultValue.isEmpty())
+ d->defaultValues << defaultValue;
+}
+
+/*!
+ Sets the list of default values used for this option to \a defaultValues.
+
+ The default values are used if the user of the application does not specify
+ the option on the command line.
+
+ \sa defaultValues() setDefaultValue()
+ */
+void QCommandLineOption::setDefaultValues(const QStringList &defaultValues)
+{
+ d->defaultValues = defaultValues;
+}
+
+/*!
+ Returns the default values set for this option.
+
+ \sa setDefaultValues()
+ */
+QStringList QCommandLineOption::defaultValues() const
+{
+ return d->defaultValues;
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/tools/qcommandlineoption.h b/src/corelib/tools/qcommandlineoption.h
new file mode 100644
index 0000000000..7775aae5b6
--- /dev/null
+++ b/src/corelib/tools/qcommandlineoption.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOMMANDLINEOPTION_H
+#define QCOMMANDLINEOPTION_H
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCommandLineOptionPrivate;
+
+class Q_CORE_EXPORT QCommandLineOption
+{
+public:
+ explicit QCommandLineOption(const QString &name, const QString &description = QString(),
+ const QString &valueName = QString(),
+ const QString &defaultValue = QString());
+ explicit QCommandLineOption(const QStringList &names, const QString &description = QString(),
+ const QString &valueName = QString(),
+ const QString &defaultValue = QString());
+ QCommandLineOption(const QCommandLineOption &other);
+
+ ~QCommandLineOption();
+
+ QCommandLineOption &operator=(const QCommandLineOption &other);
+#ifdef Q_COMPILER_RVALUE_REFS
+ inline QCommandLineOption &operator=(QCommandLineOption &&other)
+ { qSwap(d, other.d); return *this; }
+#endif
+
+ inline void swap(QCommandLineOption &other)
+ { qSwap(d, other.d); }
+
+ QStringList names() const;
+
+ void setValueName(const QString &name);
+ QString valueName() const;
+
+ void setDescription(const QString &description);
+ QString description() const;
+
+ void setDefaultValue(const QString &defaultValue);
+ void setDefaultValues(const QStringList &defaultValues);
+ QStringList defaultValues() const;
+
+private:
+ QSharedDataPointer<QCommandLineOptionPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOMMANDLINEOPTION_H
diff --git a/src/corelib/tools/qcommandlineparser.cpp b/src/corelib/tools/qcommandlineparser.cpp
new file mode 100644
index 0000000000..ae6079ab0b
--- /dev/null
+++ b/src/corelib/tools/qcommandlineparser.cpp
@@ -0,0 +1,896 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
+** Copyright (C) 2013 David Faure <faure@kde.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcommandlineparser.h"
+
+#include <qcoreapplication.h>
+#include <qhash.h>
+#include <qvector.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef QHash<QString, int> NameHash_t;
+
+// Special value for "not found" when doing hash lookups.
+static const NameHash_t::mapped_type optionNotFound = ~0;
+
+class QCommandLineParserPrivate
+{
+public:
+ inline QCommandLineParserPrivate()
+ : singleDashWordOptionMode(QCommandLineParser::ParseAsCompactedShortOptions),
+ builtinVersionOption(false),
+ builtinHelpOption(false),
+ needsParsing(true)
+ { }
+
+ bool parse(const QStringList &args);
+ void checkParsed(const char *method);
+ QStringList aliases(const QString &name) const;
+ QString helpText() const;
+ bool registerFoundOption(const QString &optionName);
+ bool parseOptionValue(const QString &optionName, const QString &argument,
+ QStringList::const_iterator *argumentIterator,
+ QStringList::const_iterator argsEnd);
+
+ //! Error text set when parse() returns false
+ QString errorText;
+
+ //! The command line options used for parsing
+ QList<QCommandLineOption> commandLineOptionList;
+
+ //! Hash mapping option names to their offsets in commandLineOptionList and optionArgumentList.
+ NameHash_t nameHash;
+
+ //! Option values found (only for options with a value)
+ QHash<int, QStringList> optionValuesHash;
+
+ //! Names of options found on the command line.
+ QStringList optionNames;
+
+ //! Arguments which did not belong to any option.
+ QStringList positionalArgumentList;
+
+ //! Names of options which were unknown.
+ QStringList unknownOptionNames;
+
+ //! Application description
+ QString description;
+
+ //! Documentation for positional arguments
+ struct PositionalArgumentDefinition
+ {
+ QString name;
+ QString description;
+ QString syntax;
+ };
+ QVector<PositionalArgumentDefinition> positionalArgumentDefinitions;
+
+ //! The parsing mode for "-abc"
+ QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode;
+
+ //! Whether addVersionOption was called
+ bool builtinVersionOption;
+
+ //! Whether addHelpOption was called
+ bool builtinHelpOption;
+
+ //! True if parse() needs to be called
+ bool needsParsing;
+};
+
+QStringList QCommandLineParserPrivate::aliases(const QString &optionName) const
+{
+ const NameHash_t::mapped_type optionOffset = nameHash.value(optionName, optionNotFound);
+ if (optionOffset == optionNotFound) {
+ qWarning("QCommandLineParser: option not defined: \"%s\"", qPrintable(optionName));
+ return QStringList();
+ }
+ return commandLineOptionList.at(optionOffset).names();
+}
+
+/*!
+ \since 5.2
+ \class QCommandLineParser
+ \inmodule QtCore
+ \ingroup tools
+
+ \brief The QCommandLineParser class provides a means for handling the
+ command line options.
+
+ QCoreApplication provides the command-line arguments as a simple list of strings.
+ QCommandLineParser provides the ability to define a set of options, parse the
+ command-line arguments, and store which options have actually been used, as
+ well as option values.
+
+ Any argument that isn't an option (i.e. doesn't start with a \c{-}) is stored
+ as a "positional argument".
+
+ The parser handles short names, long names, more than one name for the same
+ option, and option values.
+
+ Options on the command line are recognized as starting with a single or
+ double \c{-} character(s).
+ The option \c{-} (single dash alone) is a special case, often meaning standard
+ input, and not treated as an option. The parser will treat everything after the
+ option \c{--} (double dash) as positional arguments.
+
+ Short options are single letters. The option \c{v} would be specified by
+ passing \c{-v} on the command line. In the default parsing mode, short options
+ can be written in a compact form, for instance \c{-abc} is equivalent to \c{-a -b -c}.
+ The parsing mode for can be set to ParseAsLongOptions, in which case \c{-abc}
+ will be parsed as the long option \a{abc}.
+
+ Long options are more than one letter long and cannot be compacted together.
+ The long option \c{verbose} would be passed as \c{--verbose} or \c{-verbose}.
+
+ Passing values to options can be done using the assignment operator: \c{-v=value}
+ \c{--verbose=value}, or a space: \c{-v value} \c{--verbose value}, i.e. the next
+ argument is used as value (even if it starts with a \c{-}).
+
+ The parser does not support optional values - if an option is set to
+ require a value, one must be present. If such an option is placed last
+ and has no value, the option will be treated as if it had not been
+ specified.
+
+ The parser does not automatically support negating or disabling long options
+ by using the format \c{--disable-option} or \c{--no-option}. However, it is
+ possible to handle this case explicitly by making an option with \c{no-option}
+ as one of its names, and handling the option explicitly.
+
+ Example:
+ \snippet code/src_corelib_tools_qcommandlineparser.cpp 3
+
+ Known limitation: the parsing of Qt options inside QCoreApplication and subclasses
+ happens before QCommandLineParser exists, so it can't take it into account. This
+ means any option value that looks like a builtin Qt option, will be treated by
+ QCoreApplication as a builtin Qt option. Example: \c{--profile -reverse} will
+ lead to QGuiApplication seeing the -reverse option set, and removing it from
+ QCoreApplication::arguments() before QCommandLineParser defines the \c{profile}
+ option and parses the command line.
+
+ \sa QCommandLineOption, QCoreApplication
+*/
+
+/*!
+ Constructs a command line parser object.
+*/
+QCommandLineParser::QCommandLineParser()
+ : d(new QCommandLineParserPrivate)
+{
+}
+
+/*!
+ Destroys the command line parser object.
+*/
+QCommandLineParser::~QCommandLineParser()
+{
+ delete d;
+}
+
+/*!
+ \enum QCommandLineParser::SingleDashWordOptionMode
+
+ This enum describes the way the parser interprets command-line
+ options that use a single dash followed by multiple letters, as as \c{-abc}.
+
+ \value ParseAsCompactedShortOptions \c{-abc} is interpreted as \c{-a -b -c},
+ i.e. as three short options that have been compacted on the command-line,
+ if none of the options take a value. If \c{a} takes a value, then it
+ is interpreted as \c{-a bc}, i.e. the short option \c{a} followed by the value \c{bc}.
+ This is typically used in tools that behave like compilers, in order
+ to handle options such as \c{-DDEFINE=VALUE} or \c{-I/include/path}.
+ This is the default parsing mode. New applications are recommended to
+ use this mode.
+
+ \value ParseAsLongOptions \c{-abc} is interpreted as \c{--abc},
+ i.e. as the long option named \c{abc}. This is how Qt's own tools
+ (uic, rcc...) have always been parsing arguments. This mode should be
+ used for preserving compatibility in applications that were parsing
+ arguments in such a way.
+
+ \sa setSingleDashWordOptionMode()
+*/
+
+/*!
+ Sets the parsing mode to \a singleDashWordOptionMode.
+ This must be called before process() or parse().
+*/
+void QCommandLineParser::setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode)
+{
+ d->singleDashWordOptionMode = singleDashWordOptionMode;
+}
+
+/*!
+ Adds the option \a option to look for while parsing.
+
+ Returns true if adding the option was successful; otherwise returns false.
+
+ Adding the option fails if there is no name attached to the option, or
+ the option has a name that clashes with an option name added before.
+ */
+bool QCommandLineParser::addOption(const QCommandLineOption &option)
+{
+ QStringList optionNames = option.names();
+
+ if (!optionNames.isEmpty()) {
+ foreach (const QString &name, optionNames) {
+ if (d->nameHash.contains(name))
+ return false;
+ }
+
+ d->commandLineOptionList.append(option);
+
+ const int offset = d->commandLineOptionList.size() - 1;
+ foreach (const QString &name, optionNames)
+ d->nameHash.insert(name, offset);
+
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Adds the \c{-v} / \c{--version} option, which displays the version string of the application.
+
+ This option is handled automatically by QCommandLineParser.
+
+ You can set the actual version string by using QCoreApplication::setApplicationVersion().
+
+ Returns the option instance, which can be used to call isSet().
+*/
+QCommandLineOption QCommandLineParser::addVersionOption()
+{
+ d->builtinVersionOption = true;
+ QCommandLineOption opt(QStringList() << QStringLiteral("v") << QStringLiteral("version"), tr("Displays version information."));
+ addOption(opt);
+ return opt;
+}
+
+/*!
+ Adds the help option (\c{-h}, \c{--help} and \c{-?} on Windows)
+ This option is handled automatically by QCommandLineParser.
+
+ Remember to use setApplicationDescription to set the application description,
+ which will be displayed when this option is used.
+
+ Example:
+ \code
+ setApplicationDescription(QCoreApplication::translate("main", "The best application in the world"));
+ addHelpOption();
+ \endcode
+
+ Returns the option instance, which can be used to call isSet().
+*/
+QCommandLineOption QCommandLineParser::addHelpOption()
+{
+ d->builtinHelpOption = true;
+ QCommandLineOption opt(QStringList()
+#ifdef Q_OS_WIN
+ << QStringLiteral("?")
+#endif
+ << QStringLiteral("h")
+ << QStringLiteral("help"), tr("Displays this help."));
+ addOption(opt);
+ return opt;
+}
+
+/*!
+ Sets the application \a description shown by helpText().
+ Most applications don't need to call this directly, addHelpOption()
+ also sets the application description.
+*/
+void QCommandLineParser::setApplicationDescription(const QString &description)
+{
+ d->description = description;
+}
+
+/*!
+ Returns the application description set in setApplicationDescription()
+ or addHelpOption().
+*/
+QString QCommandLineParser::applicationDescription() const
+{
+ return d->description;
+}
+
+/*!
+ Defines an additional argument to the application, for the benefit of the help text.
+
+ The argument \a name and \a description will appear under the \c{Arguments:} section
+ of the help. If \a syntax is specified, it will be appended to the Usage line, otherwise
+ the \a name will be appended.
+
+ Example:
+ \snippet code/src_corelib_tools_qcommandlineparser.cpp 1
+
+ \sa addHelpOption(), helpText()
+*/
+void QCommandLineParser::addPositionalArgument(const QString &name, const QString &description, const QString &syntax)
+{
+ QCommandLineParserPrivate::PositionalArgumentDefinition arg;
+ arg.name = name;
+ arg.description = description;
+ arg.syntax = syntax.isEmpty() ? name : syntax;
+ d->positionalArgumentDefinitions.append(arg);
+}
+
+/*!
+ Clears the definitions of additional arguments from the help text.
+
+ This is only needed for the special case of tools which support multiple commands
+ with different options. Once the actual command has been identified, the options
+ for this command can be defined, and the help text for the command can be adjusted
+ accordingly.
+
+ Example:
+ \snippet code/src_corelib_tools_qcommandlineparser.cpp 2
+*/
+void QCommandLineParser::clearPositionalArguments()
+{
+ d->positionalArgumentDefinitions.clear();
+}
+
+/*!
+ Parses the command line \a arguments.
+
+ Most programs don't need to call this, a simple call to process(app) is enough.
+
+ parse() is more low-level, and only does the parsing. The application will have to
+ take care of the error handling, using errorText() if parse() returns false.
+ This can be useful for instance to show a graphical error message in graphical programs.
+
+ Calling parse() instead of process() can also be useful in order to ignore unknown
+ options temporarily, because more option definitions will be provided later on
+ (depending on one of the arguments), before calling process().
+
+ Don't forget that \a arguments must start with the name of the executable (ignored, though).
+
+ Return false in case of a parse error (unknown option or missing value); returns true otherwise.
+
+ \sa process()
+*/
+bool QCommandLineParser::parse(const QStringList &arguments)
+{
+ return d->parse(arguments);
+}
+
+/*!
+ Returns a translated error text for the user.
+ This should only be called when parse() returns false.
+*/
+QString QCommandLineParser::errorText() const
+{
+ if (!d->errorText.isEmpty())
+ return d->errorText;
+ if (d->unknownOptionNames.count() == 1)
+ return tr("Unknown option '%1'.").arg(d->unknownOptionNames.first());
+ if (d->unknownOptionNames.count() > 1)
+ return tr("Unknown options: %1.").arg(d->unknownOptionNames.join(QStringLiteral(", ")));
+ return QString();
+}
+
+/*!
+ Processes the command line \a arguments.
+
+ This means both parsing them, and handling the builtin options,
+ \c{--version} if addVersionOption was called, \c{--help} if addHelpOption was called,
+ as well as giving an error on unknown option names.
+ In each of these three cases, the current process will then stop, using the exit() function.
+
+ \sa QCoreApplication::arguments(), parse()
+ */
+void QCommandLineParser::process(const QStringList &arguments)
+{
+ if (!d->parse(arguments)) {
+ fprintf(stderr, "%s\n", qPrintable(errorText()));
+ ::exit(1);
+ }
+
+ if (d->builtinVersionOption && isSet(QStringLiteral("version"))) {
+ printf("%s %s\n", qPrintable(QCoreApplication::applicationName()), qPrintable(QCoreApplication::applicationVersion()));
+ ::exit(0);
+ }
+
+ if (d->builtinHelpOption && isSet(QStringLiteral("help")))
+ showHelp(0);
+}
+
+/*!
+ \overload
+
+ The command line is obtained from the QCoreApplication instance \a app.
+ */
+void QCommandLineParser::process(const QCoreApplication &app)
+{
+ // QCoreApplication::arguments() is static, but the app instance must exist so we require it as parameter
+ Q_UNUSED(app);
+ process(QCoreApplication::arguments());
+}
+
+void QCommandLineParserPrivate::checkParsed(const char *method)
+{
+ if (needsParsing)
+ qWarning("QCommandLineParser: call process() or parse() before %s", method);
+}
+
+/*!
+ \internal
+ Looks up the option \a optionName (found on the command line) and register it as found.
+ Returns true on success.
+ */
+bool QCommandLineParserPrivate::registerFoundOption(const QString &optionName)
+{
+ if (nameHash.contains(optionName)) {
+ optionNames.append(optionName);
+ return true;
+ } else {
+ unknownOptionNames.append(optionName);
+ return false;
+ }
+}
+
+/*!
+ \internal
+ \brief Parse the value for a given option, if it was defined to expect one.
+
+ The value is taken from the next argument, or after the equal sign in \a argument.
+
+ \param optionName the short option name
+ \param argument the argument from the command line currently parsed. Only used for -k=value parsing.
+ \param argumentIterator iterator to the currently parsed argument. Incremented if the next argument contains the value.
+ \param argsEnd args.end(), to check if ++argumentIterator goes out of bounds
+ Returns true on success.
+ */
+bool QCommandLineParserPrivate::parseOptionValue(const QString &optionName, const QString &argument,
+ QStringList::const_iterator *argumentIterator, QStringList::const_iterator argsEnd)
+{
+ const QLatin1Char assignChar('=');
+ const NameHash_t::const_iterator nameHashIt = nameHash.constFind(optionName);
+ if (nameHashIt != nameHash.constEnd()) {
+ const int assignPos = argument.indexOf(assignChar);
+ const NameHash_t::mapped_type optionOffset = *nameHashIt;
+ const bool withValue = !commandLineOptionList.at(optionOffset).valueName().isEmpty();
+ if (withValue) {
+ if (assignPos == -1) {
+ ++(*argumentIterator);
+ if (*argumentIterator == argsEnd) {
+ errorText = QCommandLineParser::tr("Missing value after '%1'.").arg(argument);
+ return false;
+ }
+ optionValuesHash[optionOffset].append(*(*argumentIterator));
+ } else {
+ optionValuesHash[optionOffset].append(argument.mid(assignPos + 1));
+ }
+ } else {
+ if (assignPos != -1) {
+ errorText = QCommandLineParser::tr("Unexpected value after '%1'.").arg(argument.left(assignPos));
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*!
+ \internal
+
+ Parse the list of arguments \a arguments.
+
+ Any results from a previous parse operation are removed.
+ The parser will not look for further options once it encounters the option
+ \c{--}; this does not include when \c{--} follows an option that requires a value.
+
+ Options that were successfully recognized, and their values, are
+ removed from the input list. If \c m_bRemoveUnknownLongNames is
+ \c true, unrecognized options are removed and placed into a list of
+ unknown option names. Anything left over is placed into a list of
+ leftover arguments.
+ */
+bool QCommandLineParserPrivate::parse(const QStringList &args)
+{
+ needsParsing = false;
+ bool error = false;
+
+ const QString doubleDashString(QStringLiteral("--"));
+ const QLatin1Char dashChar('-');
+ const QLatin1Char assignChar('=');
+
+ bool doubleDashFound = false;
+ errorText.clear();
+ positionalArgumentList.clear();
+ optionNames.clear();
+ unknownOptionNames.clear();
+ optionValuesHash.clear();
+
+ if (args.isEmpty()) {
+ qWarning("QCommandLineParser: argument list cannot be empty, it should contain at least the executable name");
+ return false;
+ }
+
+ QStringList::const_iterator argumentIterator = args.begin();
+ ++argumentIterator; // skip executable name
+
+ for (; argumentIterator != args.end() ; ++argumentIterator) {
+ QString argument = *argumentIterator;
+
+ if (doubleDashFound) {
+ positionalArgumentList.append(argument);
+ } else if (argument.startsWith(doubleDashString)) {
+ if (argument.length() > 2) {
+ QString optionName = argument.mid(2).section(assignChar, 0, 0);
+ if (registerFoundOption(optionName)) {
+ if (!parseOptionValue(optionName, argument, &argumentIterator, args.end()))
+ error = true;
+ } else {
+ error = true;
+ }
+ } else {
+ doubleDashFound = true;
+ }
+ } else if (argument.startsWith(dashChar)) {
+ if (argument.size() == 1) { // single dash ("stdin")
+ positionalArgumentList.append(argument);
+ continue;
+ }
+ switch (singleDashWordOptionMode) {
+ case QCommandLineParser::ParseAsCompactedShortOptions:
+ {
+ QString optionName;
+ bool valueFound = false;
+ for (int pos = 1 ; pos < argument.size(); ++pos) {
+ optionName = argument.mid(pos, 1);
+ if (!registerFoundOption(optionName)) {
+ error = true;
+ } else {
+ const NameHash_t::const_iterator nameHashIt = nameHash.constFind(optionName);
+ Q_ASSERT(nameHashIt != nameHash.constEnd()); // checked by registerFoundOption
+ const NameHash_t::mapped_type optionOffset = *nameHashIt;
+ const bool withValue = !commandLineOptionList.at(optionOffset).valueName().isEmpty();
+ if (withValue) {
+ if (pos + 1 < argument.size()) {
+ if (argument.at(pos + 1) == assignChar)
+ ++pos;
+ optionValuesHash[optionOffset].append(argument.mid(pos + 1));
+ valueFound = true;
+ }
+ break;
+ }
+ if (pos + 1 < argument.size() && argument.at(pos + 1) == assignChar)
+ break;
+ }
+ }
+ if (!valueFound && !parseOptionValue(optionName, argument, &argumentIterator, args.end()))
+ error = true;
+ break;
+ }
+ case QCommandLineParser::ParseAsLongOptions:
+ {
+ const QString optionName = argument.mid(1).section(assignChar, 0, 0);
+ if (registerFoundOption(optionName)) {
+ if (!parseOptionValue(optionName, argument, &argumentIterator, args.end()))
+ error = true;
+ } else {
+ error = true;
+ }
+ break;
+ }
+ }
+ } else {
+ positionalArgumentList.append(argument);
+ }
+ if (argumentIterator == args.end())
+ break;
+ }
+ return !error;
+}
+
+/*!
+ Checks whether the option \a name was passed to the application.
+
+ Returns true if the option \a name was set, false otherwise.
+
+ This is the recommended way to check for options with no values.
+
+ The name provided can be any long or short name of any option that was
+ added with \c addOption(). All the options names are treated as being
+ equivalent. If the name is not recognized or that option was not present,
+ false is returned.
+
+ Example:
+ \snippet code/src_corelib_tools_qcommandlineparser.cpp 0
+ */
+
+bool QCommandLineParser::isSet(const QString &name) const
+{
+ d->checkParsed("isSet");
+ if (d->optionNames.contains(name))
+ return true;
+ const QStringList aliases = d->aliases(name);
+ foreach (const QString &optionName, d->optionNames) {
+ if (aliases.contains(optionName))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns the option value found for the given option name \a optionName, or
+ an empty string if not found.
+
+ The name provided can be any long or short name of any option that was
+ added with \c addOption(). All the option names are treated as being
+ equivalent. If the name is not recognized or that option was not present, an
+ empty string is returned.
+
+ For options found by the parser, the last value found for
+ that option is returned. If the option wasn't specified on the command line,
+ the default value is returned.
+
+ An empty string is returned if the option does not take a value.
+
+ \sa values()
+ */
+
+QString QCommandLineParser::value(const QString &optionName) const
+{
+ d->checkParsed("value");
+ const QStringList valueList = values(optionName);
+
+ if (!valueList.isEmpty())
+ return valueList.last();
+
+ return QString();
+}
+
+/*!
+ Returns a list of option values found for the given option name \a
+ optionName, or an empty list if not found.
+
+ The name provided can be any long or short name of any option that was
+ added with \c addOption(). All the options names are treated as being
+ equivalent. If the name is not recognized or that option was not present, an
+ empty list is returned.
+
+ For options found by the parser, the list will contain an entry for
+ each time the option was encountered by the parser. If the option wasn't
+ specified on the command line, the default values are returned.
+
+ An empty list is returned if the option does not take a value.
+
+ \sa value()
+ */
+
+QStringList QCommandLineParser::values(const QString &optionName) const
+{
+ d->checkParsed("values");
+ const NameHash_t::mapped_type optionOffset = d->nameHash.value(optionName, optionNotFound);
+ if (optionOffset != optionNotFound) {
+ QStringList values = d->optionValuesHash.value(optionOffset);
+ if (values.isEmpty())
+ values = d->commandLineOptionList.at(optionOffset).defaultValues();
+ return values;
+ }
+
+ qWarning("QCommandLineParser: option not defined: \"%s\"", qPrintable(optionName));
+ return QStringList();
+}
+
+/*!
+ \overload
+ Returns true if the \a option was set, false otherwise.
+*/
+bool QCommandLineParser::isSet(const QCommandLineOption &option) const
+{
+ return isSet(option.names().first());
+}
+
+/*!
+ \overload
+ Returns the option value found for the given \a option, or
+ an empty string if not found.
+*/
+QString QCommandLineParser::value(const QCommandLineOption &option) const
+{
+ return value(option.names().first());
+}
+
+/*!
+ \overload
+ Returns a list of option values found for the given \a option,
+ or an empty list if not found.
+*/
+QStringList QCommandLineParser::values(const QCommandLineOption &option) const
+{
+ return values(option.names().first());
+}
+
+/*!
+ Returns a list of positional arguments.
+
+ These are all of the arguments that were not recognized as part of an
+ option.
+ */
+
+QStringList QCommandLineParser::positionalArguments() const
+{
+ d->checkParsed("positionalArguments");
+ return d->positionalArgumentList;
+}
+
+/*!
+ Returns a list of option names that were found.
+
+ This returns a list of all the recognized option names found by the
+ parser, in the order in which they were found. For any long options
+ that were in the form {--option=value}, the value part will have been
+ dropped.
+
+ The names in this list do not include the preceding dash characters.
+ Names may appear more than once in this list if they were encountered
+ more than once by the parser.
+
+ Any entry in the list can be used with \c value() or with
+ \c values() to get any relevant option values.
+ */
+
+QStringList QCommandLineParser::optionNames() const
+{
+ d->checkParsed("optionNames");
+ return d->optionNames;
+}
+
+/*!
+ Returns a list of unknown option names.
+
+ This list will include both long an short name options that were not
+ recognized. For any long options that were in the form {--option=value},
+ the value part will have been dropped and only the long name is added.
+
+ The names in this list do not include the preceding dash characters.
+ Names may appear more than once in this list if they were encountered
+ more than once by the parser.
+
+ \sa optionNames()
+ */
+
+QStringList QCommandLineParser::unknownOptionNames() const
+{
+ d->checkParsed("unknownOptionNames");
+ return d->unknownOptionNames;
+}
+
+/*!
+ Displays the help information, and exits the application.
+ This is automatically triggered by the --help option, but can also
+ be used to display the help when the user is not invoking the
+ application correctly.
+ The exit code is set to \a exitCode. It should be set to 0 if the
+ user requested to see the help, and to any other value in case of
+ an error.
+
+ \sa helpText()
+*/
+Q_NORETURN void QCommandLineParser::showHelp(int exitCode)
+{
+ fprintf(stdout, "%s", qPrintable(d->helpText()));
+ ::exit(exitCode);
+}
+
+/*!
+ Returns a string containing the complete help information.
+
+ \sa showHelp()
+*/
+QString QCommandLineParser::helpText() const
+{
+ return d->helpText();
+}
+
+static QString wrapText(const QString &names, int longestOptionNameString, const QString &description)
+{
+ const QLatin1Char nl('\n');
+ QString text = QStringLiteral(" ") + names.leftJustified(longestOptionNameString) + QLatin1Char(' ');
+ const int leftColumnWidth = text.length();
+ const int rightColumnWidth = 79 - leftColumnWidth;
+ text += description.left(rightColumnWidth) + nl;
+ for (int n = rightColumnWidth; n < description.length(); n += rightColumnWidth)
+ text += QStringLiteral(" ").repeated(leftColumnWidth) + description.mid(n, rightColumnWidth) + nl;
+ return text;
+}
+
+QString QCommandLineParserPrivate::helpText() const
+{
+ const QLatin1Char nl('\n');
+ QString text;
+ const QString exeName = QCoreApplication::instance()->arguments().first();
+ QString usage = exeName;
+ if (!commandLineOptionList.isEmpty()) {
+ usage += QLatin1Char(' ');
+ usage += QCommandLineParser::tr("[options]");
+ }
+ foreach (const PositionalArgumentDefinition &arg, positionalArgumentDefinitions) {
+ usage += QLatin1Char(' ');
+ usage += arg.syntax;
+ }
+ text += QCommandLineParser::tr("Usage: %1").arg(usage) + nl;
+ if (!description.isEmpty())
+ text += description + nl;
+ text += nl;
+ if (!commandLineOptionList.isEmpty())
+ text += QCommandLineParser::tr("Options:") + nl;
+ QStringList optionNameList;
+ int longestOptionNameString = 0;
+ foreach (const QCommandLineOption &option, commandLineOptionList) {
+ QStringList optionNames;
+ foreach (const QString &optionName, option.names()) {
+ if (optionName.length() == 1)
+ optionNames.append(QLatin1Char('-') + optionName);
+ else
+ optionNames.append(QStringLiteral("--") + optionName);
+ }
+ QString optionNamesString = optionNames.join(QStringLiteral(", "));
+ if (!option.valueName().isEmpty())
+ optionNamesString += QStringLiteral(" <") + option.valueName() + QLatin1Char('>');
+ optionNameList.append(optionNamesString);
+ longestOptionNameString = qMax(longestOptionNameString, optionNamesString.length());
+ }
+ ++longestOptionNameString;
+ for (int i = 0; i < commandLineOptionList.count(); ++i) {
+ const QCommandLineOption &option = commandLineOptionList.at(i);
+ text += wrapText(optionNameList.at(i), longestOptionNameString, option.description());
+ }
+ if (!positionalArgumentDefinitions.isEmpty()) {
+ if (!commandLineOptionList.isEmpty())
+ text += nl;
+ text += QCommandLineParser::tr("Arguments:") + nl;
+ foreach (const PositionalArgumentDefinition &arg, positionalArgumentDefinitions) {
+ text += wrapText(arg.name, longestOptionNameString, arg.description);
+ }
+ }
+ return text;
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/tools/qcommandlineparser.h b/src/corelib/tools/qcommandlineparser.h
new file mode 100644
index 0000000000..5a7061f031
--- /dev/null
+++ b/src/corelib/tools/qcommandlineparser.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOMMANDLINEPARSER_H
+#define QCOMMANDLINEPARSER_H
+
+#include <QtCore/qstringlist.h>
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qcommandlineoption.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCommandLineParserPrivate;
+class QCoreApplication;
+
+class Q_CORE_EXPORT QCommandLineParser
+{
+ Q_DECLARE_TR_FUNCTIONS(QCommandLineParser)
+public:
+ QCommandLineParser();
+ ~QCommandLineParser();
+
+ enum SingleDashWordOptionMode {
+ ParseAsCompactedShortOptions,
+ ParseAsLongOptions
+ };
+ void setSingleDashWordOptionMode(SingleDashWordOptionMode parsingMode);
+
+ bool addOption(const QCommandLineOption &commandLineOption);
+
+ QCommandLineOption addVersionOption();
+ QCommandLineOption addHelpOption();
+ void setApplicationDescription(const QString &description);
+ QString applicationDescription() const;
+ void addPositionalArgument(const QString &name, const QString &description, const QString &syntax = QString());
+ void clearPositionalArguments();
+
+ void process(const QStringList &arguments);
+ void process(const QCoreApplication &app);
+
+ bool parse(const QStringList &arguments);
+ QString errorText() const;
+
+ bool isSet(const QString &name) const;
+ QString value(const QString &name) const;
+ QStringList values(const QString &name) const;
+
+ bool isSet(const QCommandLineOption &option) const;
+ QString value(const QCommandLineOption &option) const;
+ QStringList values(const QCommandLineOption &option) const;
+
+ QStringList positionalArguments() const;
+ QStringList optionNames() const;
+ QStringList unknownOptionNames() const;
+
+ Q_NORETURN void showHelp(int exitCode = 0);
+ QString helpText() const;
+
+private:
+ Q_DISABLE_COPY(QCommandLineParser)
+
+ QCommandLineParserPrivate * const d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOMMANDLINEPARSER_H
diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri
index 40858e5336..8aab53998b 100644
--- a/src/corelib/tools/tools.pri
+++ b/src/corelib/tools/tools.pri
@@ -12,6 +12,8 @@ HEADERS += \
tools/qcache.h \
tools/qchar.h \
tools/qcollator_p.h \
+ tools/qcommandlineoption.h \
+ tools/qcommandlineparser.h \
tools/qcontainerfwd.h \
tools/qcryptographichash.h \
tools/qdatetime.h \
@@ -69,6 +71,8 @@ SOURCES += \
tools/qbytearray.cpp \
tools/qbytearraymatcher.cpp \
tools/qcollator.cpp \
+ tools/qcommandlineoption.cpp \
+ tools/qcommandlineparser.cpp \
tools/qcryptographichash.cpp \
tools/qdatetime.cpp \
tools/qeasingcurve.cpp \
diff --git a/tests/auto/corelib/tools/qcommandlineparser/qcommandlineparser.pro b/tests/auto/corelib/tools/qcommandlineparser/qcommandlineparser.pro
new file mode 100644
index 0000000000..a9aedc4c0d
--- /dev/null
+++ b/tests/auto/corelib/tools/qcommandlineparser/qcommandlineparser.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+SUBDIRS += tst_qcommandlineparser.pro testhelper/qcommandlineparser_test_helper.pro
diff --git a/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp
new file mode 100644
index 0000000000..c7bd2a5dc9
--- /dev/null
+++ b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 David Faure <faure@kde.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QDebug>
+#include <QCoreApplication>
+#include <QCommandLineParser>
+
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ app.setApplicationVersion("1.0");
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Test helper");
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.addPositionalArgument("parsingMode", "The parsing mode to test.");
+ parser.addPositionalArgument("command", "The command to execute.");
+ parser.addOption(QCommandLineOption("load", "Load file from URL.", "url"));
+ parser.addOption(QCommandLineOption(QStringList() << "o" << "output", "Set output file.", "file"));
+ parser.addOption(QCommandLineOption("D", "Define macro.", "key=value"));
+
+ // An option with a longer description, to test wrapping
+ QCommandLineOption noImplicitIncludesOption(QStringList() << QStringLiteral("n") << QStringLiteral("no-implicit-includes"));
+ noImplicitIncludesOption.setDescription(QStringLiteral("Disable automatic generation of implicit #include-directives."));
+ parser.addOption(noImplicitIncludesOption);
+
+ // This program supports different options depending on the "command" (first argument).
+ // Call parse() to find out the positional arguments.
+ parser.parse(QCoreApplication::arguments());
+
+ QStringList args = parser.positionalArguments();
+ if (args.isEmpty())
+ parser.showHelp(1);
+ parser.setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode(args.takeFirst().toInt()));
+ const QString command = args.isEmpty() ? QString() : args.first();
+ if (command == "resize") {
+ parser.clearPositionalArguments();
+ parser.addPositionalArgument("resize", "Resize the object to a new size.", "resize [resize_options]");
+ parser.addOption(QCommandLineOption("size", "New size.", "size"));
+ parser.process(app);
+ const QString size = parser.value("size");
+ printf("Resizing %s to %s and saving to %s\n", qPrintable(parser.value("load")), qPrintable(size), qPrintable(parser.value("o")));
+ } else {
+ // Call process again, to handle unknown options this time.
+ parser.process(app);
+ }
+
+ printf("Positional arguments: %s\n", qPrintable(parser.positionalArguments().join(",")));
+
+ return 0;
+}
+
diff --git a/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.pro b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.pro
new file mode 100644
index 0000000000..dce1ac0d37
--- /dev/null
+++ b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.pro
@@ -0,0 +1,6 @@
+CONFIG += console
+CONFIG -= app_bundle
+QT = core
+DESTDIR = ./
+
+SOURCES += qcommandlineparser_test_helper.cpp
diff --git a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp
new file mode 100644
index 0000000000..79ab212d47
--- /dev/null
+++ b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp
@@ -0,0 +1,531 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 David Faure <faure@kde.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtCore/QCommandLineParser>
+
+Q_DECLARE_METATYPE(char**)
+
+class tst_QCommandLineParser : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void parsingModes_data();
+
+ // In-process tests
+ void testInvalidOptions();
+ void testPositionalArguments();
+ void testBooleanOption_data();
+ void testBooleanOption();
+ void testMultipleNames_data();
+ void testMultipleNames();
+ void testSingleValueOption_data();
+ void testSingleValueOption();
+ void testValueNotSet();
+ void testMultipleValuesOption();
+ void testUnknownOptionErrorHandling_data();
+ void testUnknownOptionErrorHandling();
+ void testDoubleDash_data();
+ void testDoubleDash();
+ void testProcessNotCalled();
+ void testEmptyArgsList();
+ void testMissingOptionValue();
+ void testStdinArgument_data();
+ void testStdinArgument();
+ void testSingleDashWordOptionModes_data();
+ void testSingleDashWordOptionModes();
+
+ // QProcess-based tests using qcommandlineparser_test_helper
+ void testVersionOption();
+ void testHelpOption_data();
+ void testHelpOption();
+};
+
+static char *empty_argv[] = { const_cast<char*>("tst_qcommandlineparser") };
+static int empty_argc = 1;
+
+Q_DECLARE_METATYPE(QCommandLineParser::SingleDashWordOptionMode)
+
+void tst_QCommandLineParser::parsingModes_data()
+{
+ QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>("parsingMode");
+
+ QTest::newRow("collapsed") << QCommandLineParser::ParseAsCompactedShortOptions;
+ QTest::newRow("implicitlylong") << QCommandLineParser::ParseAsLongOptions;
+}
+
+void tst_QCommandLineParser::testInvalidOptions()
+{
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineParser parser;
+ QTest::ignoreMessage(QtWarningMsg, "Option names cannot start with a '-'");
+ parser.addOption(QCommandLineOption(QStringLiteral("-v"), QStringLiteral("Displays version information.")));
+}
+
+void tst_QCommandLineParser::testPositionalArguments()
+{
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineParser parser;
+ QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "file.txt"));
+ QCOMPARE(parser.positionalArguments(), QStringList() << QStringLiteral("file.txt"));
+}
+
+void tst_QCommandLineParser::testBooleanOption_data()
+{
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<QStringList>("expectedOptionNames");
+ QTest::addColumn<bool>("expectedIsSet");
+
+ QTest::newRow("set") << (QStringList() << "tst_qcommandlineparser" << "-b") << (QStringList() << "b") << true;
+ QTest::newRow("unset") << (QStringList() << "tst_qcommandlineparser") << QStringList() << false;
+}
+
+void tst_QCommandLineParser::testBooleanOption()
+{
+ QFETCH(QStringList, args);
+ QFETCH(QStringList, expectedOptionNames);
+ QFETCH(bool, expectedIsSet);
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineParser parser;
+ 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(), QStringList());
+ // Should warn on typos
+ QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: option not defined: \"c\"");
+ QVERIFY(!parser.isSet("c"));
+}
+
+void tst_QCommandLineParser::testMultipleNames_data()
+{
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<QStringList>("expectedOptionNames");
+
+ QTest::newRow("short") << (QStringList() << "tst_qcommandlineparser" << "-v") << (QStringList() << "v");
+ QTest::newRow("long") << (QStringList() << "tst_qcommandlineparser" << "--version") << (QStringList() << "version");
+ QTest::newRow("not_set") << (QStringList() << "tst_qcommandlineparser") << QStringList();
+}
+
+void tst_QCommandLineParser::testMultipleNames()
+{
+ QFETCH(QStringList, args);
+ QFETCH(QStringList, expectedOptionNames);
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineOption option(QStringList() << "v" << "version", QStringLiteral("Show version information"));
+ QCOMPARE(option.names(), QStringList() << "v" << "version");
+ QCommandLineParser parser;
+ QVERIFY(parser.addOption(option));
+ QVERIFY(parser.parse(args));
+ QCOMPARE(parser.optionNames(), expectedOptionNames);
+ const bool expectedIsSet = !expectedOptionNames.isEmpty();
+ QCOMPARE(parser.isSet("v"), expectedIsSet);
+ QCOMPARE(parser.isSet("version"), expectedIsSet);
+}
+
+void tst_QCommandLineParser::testSingleValueOption_data()
+{
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<QStringList>("defaults");
+ QTest::addColumn<bool>("expectedIsSet");
+
+ QTest::newRow("short") << (QStringList() << "tst" << "-s" << "oxygen") << QStringList() << true;
+ QTest::newRow("long") << (QStringList() << "tst" << "--style" << "oxygen") << QStringList() << true;
+ QTest::newRow("longequal") << (QStringList() << "tst" << "--style=oxygen") << QStringList() << true;
+ QTest::newRow("default") << (QStringList() << "tst") << (QStringList() << "oxygen") << false;
+}
+
+void tst_QCommandLineParser::testSingleValueOption()
+{
+ QFETCH(QStringList, args);
+ QFETCH(QStringList, defaults);
+ QFETCH(bool, expectedIsSet);
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineParser parser;
+ QCommandLineOption option(QStringList() << "s" << "style", QStringLiteral("style name"), "styleName");
+ option.setDefaultValues(defaults);
+ QVERIFY(parser.addOption(option));
+ for (int mode = 0; mode < 2; ++mode) {
+ parser.setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode(mode));
+ QVERIFY(parser.parse(args));
+ QCOMPARE(parser.isSet("s"), expectedIsSet);
+ QCOMPARE(parser.isSet("style"), expectedIsSet);
+ QCOMPARE(parser.isSet(option), expectedIsSet);
+ QCOMPARE(parser.value("s"), QString("oxygen"));
+ QCOMPARE(parser.value("style"), QString("oxygen"));
+ QCOMPARE(parser.values("s"), QStringList() << "oxygen");
+ QCOMPARE(parser.values("style"), QStringList() << "oxygen");
+ QCOMPARE(parser.values(option), QStringList() << "oxygen");
+ QCOMPARE(parser.positionalArguments(), QStringList());
+ }
+ // Should warn on typos
+ QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: option not defined: \"c\"");
+ QVERIFY(parser.values("c").isEmpty());
+}
+
+void tst_QCommandLineParser::testValueNotSet()
+{
+ QCoreApplication app(empty_argc, empty_argv);
+ // Not set, no default value
+ QCommandLineParser parser;
+ QCommandLineOption option(QStringList() << "s" << "style", QStringLiteral("style name"));
+ option.setValueName("styleName");
+ QVERIFY(parser.addOption(option));
+ QVERIFY(parser.parse(QStringList() << "tst"));
+ QCOMPARE(parser.optionNames(), QStringList());
+ QVERIFY(!parser.isSet("s"));
+ QVERIFY(!parser.isSet("style"));
+ QCOMPARE(parser.value("s"), QString());
+ QCOMPARE(parser.value("style"), QString());
+ QCOMPARE(parser.values("s"), QStringList());
+ QCOMPARE(parser.values("style"), QStringList());
+}
+
+void tst_QCommandLineParser::testMultipleValuesOption()
+{
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineOption option(QStringLiteral("param"), QStringLiteral("Pass parameter to the backend."));
+ option.setValueName("key=value");
+ QCommandLineParser parser;
+ QVERIFY(parser.addOption(option));
+ {
+ QVERIFY(parser.parse(QStringList() << "tst" << "--param" << "key1=value1"));
+ QVERIFY(parser.isSet("param"));
+ QCOMPARE(parser.values("param"), QStringList() << "key1=value1");
+ QCOMPARE(parser.value("param"), QString("key1=value1"));
+ }
+ {
+ QVERIFY(parser.parse(QStringList() << "tst" << "--param" << "key1=value1" << "--param" << "key2=value2"));
+ QVERIFY(parser.isSet("param"));
+ QCOMPARE(parser.values("param"), QStringList() << "key1=value1" << "key2=value2");
+ QCOMPARE(parser.value("param"), QString("key2=value2"));
+ }
+
+ QString expected =
+ "Usage: tst_qcommandlineparser [options]\n"
+ "\n"
+ "Options:\n"
+ " --param <key=value> Pass parameter to the backend.\n";
+
+ const QString exeName = QCoreApplication::instance()->arguments().first(); // e.g. debug\tst_qcommandlineparser.exe on Windows
+ expected.replace(QStringLiteral("tst_qcommandlineparser"), exeName);
+ QCOMPARE(parser.helpText(), expected);
+}
+
+void tst_QCommandLineParser::testUnknownOptionErrorHandling_data()
+{
+ QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>("parsingMode");
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<QStringList>("expectedUnknownOptionNames");
+ QTest::addColumn<QString>("expectedErrorText");
+
+ const QStringList args_hello = QStringList() << "tst_qcommandlineparser" << "--hello";
+ const QString error_hello("Unknown option 'hello'.");
+ QTest::newRow("unknown_name_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_hello << QStringList("hello") << error_hello;
+ QTest::newRow("unknown_name_long") << QCommandLineParser::ParseAsLongOptions << args_hello << QStringList("hello") << error_hello;
+
+ const QStringList args_value = QStringList() << "tst_qcommandlineparser" << "-b=1";
+ QTest::newRow("bool_with_value_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_value << QStringList() << QString("Unexpected value after '-b'.");
+ QTest::newRow("bool_with_value_long") << QCommandLineParser::ParseAsLongOptions << args_value << QStringList() << QString("Unexpected value after '-b'.");
+
+ const QStringList args_dash_long = QStringList() << "tst_qcommandlineparser" << "-bool";
+ const QString error_bool("Unknown options: o, o, l.");
+ QTest::newRow("unknown_name_long_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_dash_long << (QStringList() << "o" << "o" << "l") << error_bool;
+}
+
+void tst_QCommandLineParser::testUnknownOptionErrorHandling()
+{
+ QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
+ QFETCH(QStringList, args);
+ QFETCH(QStringList, expectedUnknownOptionNames);
+ QFETCH(QString, expectedErrorText);
+
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineParser parser;
+ parser.setSingleDashWordOptionMode(parsingMode);
+ QVERIFY(parser.addOption(QCommandLineOption(QStringList() << "b" << "bool", QStringLiteral("a boolean option"))));
+ QCOMPARE(parser.parse(args), expectedErrorText.isEmpty());
+ QCOMPARE(parser.unknownOptionNames(), expectedUnknownOptionNames);
+ QCOMPARE(parser.errorText(), expectedErrorText);
+}
+
+void tst_QCommandLineParser::testDoubleDash_data()
+{
+ parsingModes_data();
+}
+
+void tst_QCommandLineParser::testDoubleDash()
+{
+ QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
+
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineParser parser;
+ parser.addOption(QCommandLineOption(QStringList() << "o" << "output", QStringLiteral("Output file"), QStringLiteral("filename")));
+ parser.setSingleDashWordOptionMode(parsingMode);
+ QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--output" << "foo"));
+ QCOMPARE(parser.value("output"), QString("foo"));
+ QCOMPARE(parser.positionalArguments(), QStringList());
+ QCOMPARE(parser.unknownOptionNames(), QStringList());
+ QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--" << "--output" << "bar" << "-b" << "bleh"));
+ QCOMPARE(parser.value("output"), QString());
+ QCOMPARE(parser.positionalArguments(), QStringList() << "--output" << "bar" << "-b" << "bleh");
+ QCOMPARE(parser.unknownOptionNames(), QStringList());
+}
+
+void tst_QCommandLineParser::testProcessNotCalled()
+{
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineParser parser;
+ QVERIFY(parser.addOption(QCommandLineOption(QStringLiteral("b"), QStringLiteral("a boolean option"))));
+ QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: call process() or parse() before isSet");
+ QVERIFY(!parser.isSet("b"));
+ QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: call process() or parse() before values");
+ QCOMPARE(parser.values("b"), QStringList());
+}
+
+void tst_QCommandLineParser::testEmptyArgsList()
+{
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineParser parser;
+ QTest::ignoreMessage(QtWarningMsg, "QCommandLineParser: argument list cannot be empty, it should contain at least the executable name");
+ QVERIFY(!parser.parse(QStringList())); // invalid call, argv[0] is missing
+}
+
+void tst_QCommandLineParser::testMissingOptionValue()
+{
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineParser parser;
+ parser.addOption(QCommandLineOption(QStringLiteral("option"), QStringLiteral("An option"), "value"));
+ QVERIFY(!parser.parse(QStringList() << "argv0" << "--option")); // the user forgot to pass a value for --option
+ QCOMPARE(parser.value("option"), QString());
+ QCOMPARE(parser.errorText(), QString("Missing value after '--option'."));
+}
+
+void tst_QCommandLineParser::testStdinArgument_data()
+{
+ parsingModes_data();
+}
+
+void tst_QCommandLineParser::testStdinArgument()
+{
+ QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
+
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineParser parser;
+ parser.setSingleDashWordOptionMode(parsingMode);
+ parser.addOption(QCommandLineOption(QStringList() << "i" << "input", QStringLiteral("Input file."), QStringLiteral("filename")));
+ parser.addOption(QCommandLineOption("b", QStringLiteral("Boolean option.")));
+ QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--input" << "-"));
+ QCOMPARE(parser.value("input"), QString("-"));
+ QCOMPARE(parser.positionalArguments(), QStringList());
+ QCOMPARE(parser.unknownOptionNames(), QStringList());
+
+ QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--input" << "-" << "-b" << "arg"));
+ QCOMPARE(parser.value("input"), QString("-"));
+ QVERIFY(parser.isSet("b"));
+ QCOMPARE(parser.positionalArguments(), QStringList() << "arg");
+ QCOMPARE(parser.unknownOptionNames(), QStringList());
+
+ QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "-"));
+ QCOMPARE(parser.value("input"), QString());
+ QVERIFY(!parser.isSet("b"));
+ QCOMPARE(parser.positionalArguments(), QStringList() << "-");
+ QCOMPARE(parser.unknownOptionNames(), QStringList());
+}
+
+void tst_QCommandLineParser::testSingleDashWordOptionModes_data()
+{
+ QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>("parsingMode");
+ QTest::addColumn<QStringList>("commandLine");
+ QTest::addColumn<QStringList>("expectedOptionNames");
+ QTest::addColumn<QStringList>("expectedOptionValues");
+
+ QTest::newRow("collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-abc" << "val")
+ << (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val");
+ QTest::newRow("collapsed_with_equalsign_value") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-abc=val")
+ << (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val");
+ QTest::newRow("collapsed_explicit_longoption") << QCommandLineParser::ParseAsCompactedShortOptions << QStringList("--nn")
+ << QStringList("nn") << QStringList();
+ QTest::newRow("collapsed_longoption_value") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "--abc" << "val")
+ << QStringList("abc") << QStringList("val");
+ QTest::newRow("compiler") << QCommandLineParser::ParseAsCompactedShortOptions << QStringList("-cab")
+ << QStringList("c") << QStringList("ab");
+ QTest::newRow("compiler_with_space") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-c" << "val")
+ << QStringList("c") << QStringList("val");
+
+ QTest::newRow("implicitlylong") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-abc" << "val")
+ << QStringList("abc") << QStringList("val");
+ QTest::newRow("implicitlylong_equal") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-abc=val")
+ << QStringList("abc") << QStringList("val");
+ QTest::newRow("implicitlylong_longoption") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "--nn")
+ << QStringList("nn") << QStringList();
+ QTest::newRow("implicitlylong_longoption_value") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "--abc" << "val")
+ << QStringList("abc") << QStringList("val");
+ QTest::newRow("implicitlylong_with_space") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-c" << "val")
+ << QStringList("c") << QStringList("val");
+}
+
+void tst_QCommandLineParser::testSingleDashWordOptionModes()
+{
+ QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
+ QFETCH(QStringList, commandLine);
+ QFETCH(QStringList, expectedOptionNames);
+ QFETCH(QStringList, expectedOptionValues);
+
+ commandLine.prepend("tst_QCommandLineParser");
+
+ QCoreApplication app(empty_argc, empty_argv);
+ QCommandLineParser parser;
+ parser.setSingleDashWordOptionMode(parsingMode);
+ parser.addOption(QCommandLineOption("a", QStringLiteral("a option.")));
+ parser.addOption(QCommandLineOption("b", QStringLiteral("b option.")));
+ parser.addOption(QCommandLineOption(QStringList() << "c" << "abc", QStringLiteral("c option."), QStringLiteral("value")));
+ parser.addOption(QCommandLineOption("nn", QStringLiteral("nn option.")));
+ QVERIFY(parser.parse(commandLine));
+ QCOMPARE(parser.optionNames(), expectedOptionNames);
+ for (int i = 0; i < expectedOptionValues.count(); ++i)
+ QCOMPARE(parser.value(parser.optionNames().at(i)), expectedOptionValues.at(i));
+ QCOMPARE(parser.unknownOptionNames(), QStringList());
+}
+
+void tst_QCommandLineParser::testVersionOption()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE");
+#endif
+ QCoreApplication app(empty_argc, empty_argv);
+ QProcess process;
+ process.start("testhelper/qcommandlineparser_test_helper", QStringList() << "0" << "--version");
+ QVERIFY(process.waitForFinished(5000));
+ QCOMPARE(process.exitStatus(), QProcess::NormalExit);
+ QString output = process.readAll();
+#ifdef Q_OS_WIN
+ output.replace(QStringLiteral("\r\n"), QStringLiteral("\n"));
+#endif
+ QCOMPARE(output, QString("qcommandlineparser_test_helper 1.0\n"));
+}
+
+void tst_QCommandLineParser::testHelpOption_data()
+{
+ QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>("parsingMode");
+ QTest::addColumn<QString>("expectedHelpOutput");
+
+ QString expectedOutput =
+ "Usage: testhelper/qcommandlineparser_test_helper [options] parsingMode command\n"
+ "Test helper\n"
+ "\n"
+ "Options:\n"
+ " -h, --help Displays this help.\n"
+ " -v, --version Displays version information.\n"
+ " --load <url> Load file from URL.\n"
+ " -o, --output <file> Set output file.\n"
+ " -D <key=value> Define macro.\n"
+ " -n, --no-implicit-includes Disable automatic generation of implicit #include\n"
+ " -directives.\n"
+ "\n"
+ "Arguments:\n"
+ " parsingMode The parsing mode to test.\n"
+ " command The command to execute.\n";
+#ifdef Q_OS_WIN
+ expectedOutput.replace(" -h, --help Displays this help.\n",
+ " -?, -h, --help Displays this help.\n");
+ expectedOutput.replace("testhelper/", "testhelper\\");
+#endif
+
+ QTest::newRow("collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << expectedOutput;
+ QTest::newRow("long") << QCommandLineParser::ParseAsLongOptions << expectedOutput;
+}
+
+void tst_QCommandLineParser::testHelpOption()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE");
+#endif
+
+ QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
+ QFETCH(QString, expectedHelpOutput);
+ QCoreApplication app(empty_argc, empty_argv);
+ QProcess process;
+ process.start("testhelper/qcommandlineparser_test_helper", QStringList() << QString::number(parsingMode) << "--help");
+ QVERIFY(process.waitForFinished(5000));
+ QCOMPARE(process.exitStatus(), QProcess::NormalExit);
+ QString output = process.readAll();
+#ifdef Q_OS_WIN
+ output.replace(QStringLiteral("\r\n"), QStringLiteral("\n"));
+#endif
+ QCOMPARE(output, expectedHelpOutput);
+
+ process.start("testhelper/qcommandlineparser_test_helper", QStringList() << "0" << "resize" << "--help");
+ QVERIFY(process.waitForFinished(5000));
+ QCOMPARE(process.exitStatus(), QProcess::NormalExit);
+ output = process.readAll();
+#ifdef Q_OS_WIN
+ output.replace(QStringLiteral("\r\n"), QStringLiteral("\n"));
+#endif
+ QByteArray expectedResizeHelp =
+ "Usage: testhelper/qcommandlineparser_test_helper [options] resize [resize_options]\n"
+ "Test helper\n"
+ "\n"
+ "Options:\n"
+ " -h, --help Displays this help.\n"
+ " -v, --version Displays version information.\n"
+ " --load <url> Load file from URL.\n"
+ " -o, --output <file> Set output file.\n"
+ " -D <key=value> Define macro.\n"
+ " -n, --no-implicit-includes Disable automatic generation of implicit #include\n"
+ " -directives.\n"
+ " --size <size> New size.\n"
+ "\n"
+ "Arguments:\n"
+ " resize Resize the object to a new size.\n";
+#ifdef Q_OS_WIN
+ expectedResizeHelp.replace(" -h, --help Displays this help.\n",
+ " -?, -h, --help Displays this help.\n");
+ expectedResizeHelp.replace("testhelper/", "testhelper\\");
+#endif
+ QCOMPARE(output, QString(expectedResizeHelp));
+}
+
+QTEST_APPLESS_MAIN(tst_QCommandLineParser)
+#include "tst_qcommandlineparser.moc"
+
diff --git a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.pro b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.pro
new file mode 100644
index 0000000000..6d3e6d677f
--- /dev/null
+++ b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.pro
@@ -0,0 +1,4 @@
+CONFIG += testcase parallel_test
+TARGET = tst_qcommandlineparser
+QT = core testlib
+SOURCES = tst_qcommandlineparser.cpp
diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro
index afa5e5b613..fbc1b996f7 100644
--- a/tests/auto/corelib/tools/tools.pro
+++ b/tests/auto/corelib/tools/tools.pro
@@ -8,6 +8,7 @@ SUBDIRS=\
qbytedatabuffer \
qcache \
qchar \
+ qcommandlineparser \
qcontiguouscache \
qcryptographichash \
qdate \