1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
4 ** Copyright (C) 2013 David Faure <faure@kde.org>
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qcommandlineparser.h"
42 
43 #include <qcoreapplication.h>
44 #include <private/qcoreapplication_p.h>
45 #include <qhash.h>
46 #include <qvector.h>
47 #include <qdebug.h>
48 #if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WINRT)
49 #  include <qt_windows.h>
50 #endif
51 #include <stdio.h>
52 #include <stdlib.h>
53 
54 QT_BEGIN_NAMESPACE
55 
56 extern void Q_CORE_EXPORT qt_call_post_routines();
57 
58 typedef QHash<QString, int> NameHash_t;
59 
60 class QCommandLineParserPrivate
61 {
62 public:
QCommandLineParserPrivate()63     inline QCommandLineParserPrivate()
64         : singleDashWordOptionMode(QCommandLineParser::ParseAsCompactedShortOptions),
65           optionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsOptions),
66           builtinVersionOption(false),
67           builtinHelpOption(false),
68           needsParsing(true)
69     { }
70 
71     bool parse(const QStringList &args);
72     void checkParsed(const char *method);
73     QStringList aliases(const QString &name) const;
74     QString helpText(bool includeQtOptions) const;
75     bool registerFoundOption(const QString &optionName);
76     bool parseOptionValue(const QString &optionName, const QString &argument,
77                           QStringList::const_iterator *argumentIterator,
78                           QStringList::const_iterator argsEnd);
79     Q_NORETURN void showHelp(int exitCode, bool includeQtOptions);
80 
81     //! Error text set when parse() returns false
82     QString errorText;
83 
84     //! The command line options used for parsing
85     QList<QCommandLineOption> commandLineOptionList;
86 
87     //! Hash mapping option names to their offsets in commandLineOptionList and optionArgumentList.
88     NameHash_t nameHash;
89 
90     //! Option values found (only for options with a value)
91     QHash<int, QStringList> optionValuesHash;
92 
93     //! Names of options found on the command line.
94     QStringList optionNames;
95 
96     //! Arguments which did not belong to any option.
97     QStringList positionalArgumentList;
98 
99     //! Names of options which were unknown.
100     QStringList unknownOptionNames;
101 
102     //! Application description
103     QString description;
104 
105     //! Documentation for positional arguments
106     struct PositionalArgumentDefinition
107     {
108         QString name;
109         QString description;
110         QString syntax;
111     };
112     QVector<PositionalArgumentDefinition> positionalArgumentDefinitions;
113 
114     //! The parsing mode for "-abc"
115     QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode;
116 
117     //! How to parse "arg -option"
118     QCommandLineParser::OptionsAfterPositionalArgumentsMode optionsAfterPositionalArgumentsMode;
119 
120     //! Whether addVersionOption was called
121     bool builtinVersionOption;
122 
123     //! Whether addHelpOption was called
124     bool builtinHelpOption;
125 
126     //! True if parse() needs to be called
127     bool needsParsing;
128 };
129 Q_DECLARE_TYPEINFO(QCommandLineParserPrivate::PositionalArgumentDefinition, Q_MOVABLE_TYPE);
130 
aliases(const QString & optionName) const131 QStringList QCommandLineParserPrivate::aliases(const QString &optionName) const
132 {
133     const NameHash_t::const_iterator it = nameHash.constFind(optionName);
134     if (it == nameHash.cend()) {
135         qWarning("QCommandLineParser: option not defined: \"%ls\"", qUtf16Printable(optionName));
136         return QStringList();
137     }
138     return commandLineOptionList.at(*it).names();
139 }
140 
141 /*!
142     \since 5.2
143     \class QCommandLineParser
144     \inmodule QtCore
145     \ingroup tools
146 
147     \brief The QCommandLineParser class provides a means for handling the
148     command line options.
149 
150     QCoreApplication provides the command-line arguments as a simple list of strings.
151     QCommandLineParser provides the ability to define a set of options, parse the
152     command-line arguments, and store which options have actually been used, as
153     well as option values.
154 
155     Any argument that isn't an option (i.e. doesn't start with a \c{-}) is stored
156     as a "positional argument".
157 
158     The parser handles short names, long names, more than one name for the same
159     option, and option values.
160 
161     Options on the command line are recognized as starting with a single or
162     double \c{-} character(s).
163     The option \c{-} (single dash alone) is a special case, often meaning standard
164     input, and not treated as an option. The parser will treat everything after the
165     option \c{--} (double dash) as positional arguments.
166 
167     Short options are single letters. The option \c{v} would be specified by
168     passing \c{-v} on the command line. In the default parsing mode, short options
169     can be written in a compact form, for instance \c{-abc} is equivalent to \c{-a -b -c}.
170     The parsing mode for can be set to ParseAsLongOptions, in which case \c{-abc}
171     will be parsed as the long option \c{abc}.
172 
173     Long options are more than one letter long and cannot be compacted together.
174     The long option \c{verbose} would be passed as \c{--verbose} or \c{-verbose}.
175 
176     Passing values to options can be done using the assignment operator: \c{-v=value}
177     \c{--verbose=value}, or a space: \c{-v value} \c{--verbose value}, i.e. the next
178     argument is used as value (even if it starts with a \c{-}).
179 
180     The parser does not support optional values - if an option is set to
181     require a value, one must be present. If such an option is placed last
182     and has no value, the option will be treated as if it had not been
183     specified.
184 
185     The parser does not automatically support negating or disabling long options
186     by using the format \c{--disable-option} or \c{--no-option}. However, it is
187     possible to handle this case explicitly by making an option with \c{no-option}
188     as one of its names, and handling the option explicitly.
189 
190     Example:
191     \snippet code/src_corelib_tools_qcommandlineparser_main.cpp 0
192 
193     If your compiler supports the C++11 standard, the three addOption() calls in
194     the above example can be simplified:
195     \snippet code/src_corelib_tools_qcommandlineparser_main.cpp cxx11
196 
197     Known limitation: the parsing of Qt options inside QCoreApplication and subclasses
198     happens before QCommandLineParser exists, so it can't take it into account. This
199     means any option value that looks like a builtin Qt option, will be treated by
200     QCoreApplication as a builtin Qt option. Example: \c{--profile -reverse} will
201     lead to QGuiApplication seeing the -reverse option set, and removing it from
202     QCoreApplication::arguments() before QCommandLineParser defines the \c{profile}
203     option and parses the command line.
204 
205     \section2 How to Use QCommandLineParser in Complex Applications
206 
207     In practice, additional error checking needs to be performed on the positional
208     arguments and option values. For example, ranges of numbers should be checked.
209 
210     It is then advisable to introduce a function to do the command line parsing
211     which takes a struct or class receiving the option values returning an
212     enumeration representing the result. The dnslookup example of the QtNetwork
213     module illustrates this:
214 
215     \snippet dnslookup.h 0
216 
217     \snippet dnslookup.cpp 0
218 
219     In the main function, help should be printed to the standard output if the help option
220     was passed and the application should return the exit code 0.
221 
222     If an error was detected, the error message should be printed to the standard
223     error output and the application should return an exit code other than 0.
224 
225     \snippet dnslookup.cpp 1
226 
227     A special case to consider here are GUI applications on Windows and mobile
228     platforms. These applications may not use the standard output or error channels
229     since the output is either discarded or not accessible.
230 
231     On Windows, QCommandLineParser uses message boxes to display usage information
232     and errors if no console window can be obtained.
233 
234     For other platforms, it is recommended to display help texts and error messages
235     using a QMessageBox. To preserve the formatting of the help text, rich text
236     with \c <pre> elements should be used:
237 
238     \code
239 
240     switch (parseCommandLine(parser, &query, &errorMessage)) {
241     case CommandLineOk:
242         break;
243     case CommandLineError:
244         QMessageBox::warning(0, QGuiApplication::applicationDisplayName(),
245                              "<html><head/><body><h2>" + errorMessage + "</h2><pre>"
246                              + parser.helpText() + "</pre></body></html>");
247         return 1;
248     case CommandLineVersionRequested:
249         QMessageBox::information(0, QGuiApplication::applicationDisplayName(),
250                                  QGuiApplication::applicationDisplayName() + ' '
251                                  + QCoreApplication::applicationVersion());
252         return 0;
253     case CommandLineHelpRequested:
254         QMessageBox::warning(0, QGuiApplication::applicationDisplayName(),
255                              "<html><head/><body><pre>"
256                              + parser.helpText() + "</pre></body></html>");
257         return 0;
258     }
259     \endcode
260 
261     However, this does not apply to the dnslookup example, because it is a
262     console application.
263 
264     \sa QCommandLineOption, QCoreApplication
265 */
266 
267 /*!
268     Constructs a command line parser object.
269 */
QCommandLineParser()270 QCommandLineParser::QCommandLineParser()
271     : d(new QCommandLineParserPrivate)
272 {
273 }
274 
275 /*!
276     Destroys the command line parser object.
277 */
~QCommandLineParser()278 QCommandLineParser::~QCommandLineParser()
279 {
280     delete d;
281 }
282 
283 /*!
284     \enum QCommandLineParser::SingleDashWordOptionMode
285 
286     This enum describes the way the parser interprets command-line
287     options that use a single dash followed by multiple letters, as as \c{-abc}.
288 
289     \value ParseAsCompactedShortOptions \c{-abc} is interpreted as \c{-a -b -c},
290     i.e. as three short options that have been compacted on the command-line,
291     if none of the options take a value. If \c{a} takes a value, then it
292     is interpreted as \c{-a bc}, i.e. the short option \c{a} followed by the value \c{bc}.
293     This is typically used in tools that behave like compilers, in order
294     to handle options such as \c{-DDEFINE=VALUE} or \c{-I/include/path}.
295     This is the default parsing mode. New applications are recommended to
296     use this mode.
297 
298     \value ParseAsLongOptions \c{-abc} is interpreted as \c{--abc},
299     i.e. as the long option named \c{abc}. This is how Qt's own tools
300     (uic, rcc...) have always been parsing arguments. This mode should be
301     used for preserving compatibility in applications that were parsing
302     arguments in such a way. There is an exception if the \c{a} option has the
303     QCommandLineOption::ShortOptionStyle flag set, in which case it is still
304     interpreted as \c{-a bc}.
305 
306     \sa setSingleDashWordOptionMode()
307 */
308 
309 /*!
310     Sets the parsing mode to \a singleDashWordOptionMode.
311     This must be called before process() or parse().
312 */
setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode)313 void QCommandLineParser::setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode)
314 {
315     d->singleDashWordOptionMode = singleDashWordOptionMode;
316 }
317 
318 /*!
319     \enum QCommandLineParser::OptionsAfterPositionalArgumentsMode
320 
321     This enum describes the way the parser interprets options that
322     occur after positional arguments.
323 
324     \value ParseAsOptions \c{application argument --opt -t} is interpreted as setting
325     the options \c{opt} and \c{t}, just like \c{application --opt -t argument} would do.
326     This is the default parsing mode. In order to specify that \c{--opt} and \c{-t}
327     are positional arguments instead, the user can use \c{--}, as in
328     \c{application argument -- --opt -t}.
329 
330     \value ParseAsPositionalArguments \c{application argument --opt} is interpreted as
331     having two positional arguments, \c{argument} and \c{--opt}.
332     This mode is useful for executables that aim to launch other executables
333     (e.g. wrappers, debugging tools, etc.) or that support internal commands
334     followed by options for the command. \c{argument} is the name of the command,
335     and all options occurring after it can be collected and parsed by another
336     command line parser, possibly in another executable.
337 
338     \sa setOptionsAfterPositionalArgumentsMode()
339 
340     \since 5.6
341 */
342 
343 /*!
344     Sets the parsing mode to \a parsingMode.
345     This must be called before process() or parse().
346     \since 5.6
347 */
setOptionsAfterPositionalArgumentsMode(QCommandLineParser::OptionsAfterPositionalArgumentsMode parsingMode)348 void QCommandLineParser::setOptionsAfterPositionalArgumentsMode(QCommandLineParser::OptionsAfterPositionalArgumentsMode parsingMode)
349 {
350     d->optionsAfterPositionalArgumentsMode = parsingMode;
351 }
352 
353 /*!
354     Adds the option \a option to look for while parsing.
355 
356     Returns \c true if adding the option was successful; otherwise returns \c false.
357 
358     Adding the option fails if there is no name attached to the option, or
359     the option has a name that clashes with an option name added before.
360  */
addOption(const QCommandLineOption & option)361 bool QCommandLineParser::addOption(const QCommandLineOption &option)
362 {
363     const QStringList optionNames = option.names();
364 
365     if (!optionNames.isEmpty()) {
366         for (const QString &name : optionNames) {
367             if (d->nameHash.contains(name)) {
368                 qWarning() << "QCommandLineParser: already having an option named" << name;
369                 return false;
370             }
371         }
372 
373         d->commandLineOptionList.append(option);
374 
375         const int offset = d->commandLineOptionList.size() - 1;
376         for (const QString &name : optionNames)
377             d->nameHash.insert(name, offset);
378 
379         return true;
380     }
381 
382     return false;
383 }
384 
385 /*!
386     \since 5.4
387 
388     Adds the options to look for while parsing. The options are specified by
389     the parameter \a options.
390 
391     Returns \c true if adding all of the options was successful; otherwise
392     returns \c false.
393 
394     See the documentation for addOption() for when this function may fail.
395 */
addOptions(const QList<QCommandLineOption> & options)396 bool QCommandLineParser::addOptions(const QList<QCommandLineOption> &options)
397 {
398     // should be optimized (but it's no worse than what was possible before)
399     bool result = true;
400     for (QList<QCommandLineOption>::const_iterator it = options.begin(), end = options.end(); it != end; ++it)
401         result &= addOption(*it);
402     return result;
403 }
404 
405 /*!
406     Adds the \c{-v} / \c{--version} option, which displays the version string of the application.
407 
408     This option is handled automatically by QCommandLineParser.
409 
410     You can set the actual version string by using QCoreApplication::setApplicationVersion().
411 
412     Returns the option instance, which can be used to call isSet().
413 */
addVersionOption()414 QCommandLineOption QCommandLineParser::addVersionOption()
415 {
416     QCommandLineOption opt(QStringList() << QStringLiteral("v") << QStringLiteral("version"), tr("Displays version information."));
417     addOption(opt);
418     d->builtinVersionOption = true;
419     return opt;
420 }
421 
422 /*!
423     Adds the help option (\c{-h}, \c{--help} and \c{-?} on Windows)
424     as well as an option \c{--help-all} to include Qt-specific options in the output.
425 
426     These options are handled automatically by QCommandLineParser.
427 
428     Remember to use setApplicationDescription to set the application description,
429     which will be displayed when this option is used.
430 
431     Example:
432     \snippet code/src_corelib_tools_qcommandlineparser_main.cpp 0
433 
434     Returns the option instance, which can be used to call isSet().
435 */
addHelpOption()436 QCommandLineOption QCommandLineParser::addHelpOption()
437 {
438     QCommandLineOption opt(QStringList()
439 #ifdef Q_OS_WIN
440                 << QStringLiteral("?")
441 #endif
442                 << QStringLiteral("h")
443                 << QStringLiteral("help"), tr("Displays help on commandline options."));
444     addOption(opt);
445     QCommandLineOption optHelpAll(QStringLiteral("help-all"), tr("Displays help including Qt specific options."));
446     addOption(optHelpAll);
447     d->builtinHelpOption = true;
448     return opt;
449 }
450 
451 /*!
452     Sets the application \a description shown by helpText().
453 */
setApplicationDescription(const QString & description)454 void QCommandLineParser::setApplicationDescription(const QString &description)
455 {
456     d->description = description;
457 }
458 
459 /*!
460     Returns the application description set in setApplicationDescription().
461 */
applicationDescription() const462 QString QCommandLineParser::applicationDescription() const
463 {
464     return d->description;
465 }
466 
467 /*!
468     Defines an additional argument to the application, for the benefit of the help text.
469 
470     The argument \a name and \a description will appear under the \c{Arguments:} section
471     of the help. If \a syntax is specified, it will be appended to the Usage line, otherwise
472     the \a name will be appended.
473 
474     Example:
475     \snippet code/src_corelib_tools_qcommandlineparser.cpp 2
476 
477     \sa addHelpOption(), helpText()
478 */
addPositionalArgument(const QString & name,const QString & description,const QString & syntax)479 void QCommandLineParser::addPositionalArgument(const QString &name, const QString &description, const QString &syntax)
480 {
481     QCommandLineParserPrivate::PositionalArgumentDefinition arg;
482     arg.name = name;
483     arg.description = description;
484     arg.syntax = syntax.isEmpty() ? name : syntax;
485     d->positionalArgumentDefinitions.append(arg);
486 }
487 
488 /*!
489     Clears the definitions of additional arguments from the help text.
490 
491     This is only needed for the special case of tools which support multiple commands
492     with different options. Once the actual command has been identified, the options
493     for this command can be defined, and the help text for the command can be adjusted
494     accordingly.
495 
496     Example:
497     \snippet code/src_corelib_tools_qcommandlineparser.cpp 3
498 */
clearPositionalArguments()499 void QCommandLineParser::clearPositionalArguments()
500 {
501     d->positionalArgumentDefinitions.clear();
502 }
503 
504 /*!
505     Parses the command line \a arguments.
506 
507     Most programs don't need to call this, a simple call to process() is enough.
508 
509     parse() is more low-level, and only does the parsing. The application will have to
510     take care of the error handling, using errorText() if parse() returns \c false.
511     This can be useful for instance to show a graphical error message in graphical programs.
512 
513     Calling parse() instead of process() can also be useful in order to ignore unknown
514     options temporarily, because more option definitions will be provided later on
515     (depending on one of the arguments), before calling process().
516 
517     Don't forget that \a arguments must start with the name of the executable (ignored, though).
518 
519     Returns \c false in case of a parse error (unknown option or missing value); returns \c true otherwise.
520 
521     \sa process()
522 */
parse(const QStringList & arguments)523 bool QCommandLineParser::parse(const QStringList &arguments)
524 {
525     return d->parse(arguments);
526 }
527 
528 /*!
529     Returns a translated error text for the user.
530     This should only be called when parse() returns \c false.
531 */
errorText() const532 QString QCommandLineParser::errorText() const
533 {
534     if (!d->errorText.isEmpty())
535         return d->errorText;
536     if (d->unknownOptionNames.count() == 1)
537         return tr("Unknown option '%1'.").arg(d->unknownOptionNames.first());
538     if (d->unknownOptionNames.count() > 1)
539         return tr("Unknown options: %1.").arg(d->unknownOptionNames.join(QStringLiteral(", ")));
540     return QString();
541 }
542 
543 enum MessageType { UsageMessage, ErrorMessage };
544 
545 #if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WINRT)
546 // Return whether to use a message box. Use handles if a console can be obtained
547 // or we are run with redirected handles (for example, by QProcess).
displayMessageBox()548 static inline bool displayMessageBox()
549 {
550     if (GetConsoleWindow())
551         return false;
552     STARTUPINFO startupInfo;
553     startupInfo.cb = sizeof(STARTUPINFO);
554     GetStartupInfo(&startupInfo);
555     return !(startupInfo.dwFlags & STARTF_USESTDHANDLES);
556 }
557 #endif // Q_OS_WIN && !QT_BOOTSTRAPPED && !Q_OS_WIN && !Q_OS_WINRT
558 
showParserMessage(const QString & message,MessageType type)559 static void showParserMessage(const QString &message, MessageType type)
560 {
561 #if defined(Q_OS_WINRT)
562     if (type == UsageMessage)
563         qInfo("%ls", qUtf16Printable(message));
564     else
565         qCritical("%ls", qUtf16Printable(message));
566     return;
567 #elif defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED)
568     if (displayMessageBox()) {
569         const UINT flags = MB_OK | MB_TOPMOST | MB_SETFOREGROUND
570             | (type == UsageMessage ? MB_ICONINFORMATION : MB_ICONERROR);
571         QString title;
572         if (QCoreApplication::instance())
573             title = QCoreApplication::instance()->property("applicationDisplayName").toString();
574         if (title.isEmpty())
575             title = QCoreApplication::applicationName();
576         MessageBoxW(0, reinterpret_cast<const wchar_t *>(message.utf16()),
577                     reinterpret_cast<const wchar_t *>(title.utf16()), flags);
578         return;
579     }
580 #endif // Q_OS_WIN && !QT_BOOTSTRAPPED
581     fputs(qPrintable(message), type == UsageMessage ? stdout : stderr);
582 }
583 
584 /*!
585     Processes the command line \a arguments.
586 
587     In addition to parsing the options (like parse()), this function also handles the builtin
588     options and handles errors.
589 
590     The builtin options are \c{--version} if addVersionOption was called and
591     \c{--help} / \c{--help-all} if addHelpOption was called.
592 
593     When invoking one of these options, or when an error happens (for instance an unknown option was
594     passed), the current process will then stop, using the exit() function.
595 
596     \sa QCoreApplication::arguments(), parse()
597  */
process(const QStringList & arguments)598 void QCommandLineParser::process(const QStringList &arguments)
599 {
600     if (!d->parse(arguments)) {
601         showParserMessage(QCoreApplication::applicationName() + QLatin1String(": ") + errorText() + QLatin1Char('\n'), ErrorMessage);
602         qt_call_post_routines();
603         ::exit(EXIT_FAILURE);
604     }
605 
606     if (d->builtinVersionOption && isSet(QStringLiteral("version")))
607         showVersion();
608 
609     if (d->builtinHelpOption && isSet(QStringLiteral("help")))
610         d->showHelp(EXIT_SUCCESS, false);
611 
612     if (d->builtinHelpOption && isSet(QStringLiteral("help-all")))
613         d->showHelp(EXIT_SUCCESS, true);
614 }
615 
616 /*!
617     \overload
618 
619     The command line is obtained from the QCoreApplication instance \a app.
620  */
process(const QCoreApplication & app)621 void QCommandLineParser::process(const QCoreApplication &app)
622 {
623     // QCoreApplication::arguments() is static, but the app instance must exist so we require it as parameter
624     Q_UNUSED(app);
625     process(QCoreApplication::arguments());
626 }
627 
checkParsed(const char * method)628 void QCommandLineParserPrivate::checkParsed(const char *method)
629 {
630     if (needsParsing)
631         qWarning("QCommandLineParser: call process() or parse() before %s", method);
632 }
633 
634 /*!
635     \internal
636     Looks up the option \a optionName (found on the command line) and register it as found.
637     Returns \c true on success.
638  */
registerFoundOption(const QString & optionName)639 bool QCommandLineParserPrivate::registerFoundOption(const QString &optionName)
640 {
641     if (nameHash.contains(optionName)) {
642         optionNames.append(optionName);
643         return true;
644     } else {
645         unknownOptionNames.append(optionName);
646         return false;
647     }
648 }
649 
650 /*!
651     \internal
652     \brief Parse the value for a given option, if it was defined to expect one.
653 
654     The value is taken from the next argument, or after the equal sign in \a argument.
655 
656     \param optionName the short option name
657     \param argument the argument from the command line currently parsed. Only used for -k=value parsing.
658     \param argumentIterator iterator to the currently parsed argument. Incremented if the next argument contains the value.
659     \param argsEnd args.end(), to check if ++argumentIterator goes out of bounds
660     Returns \c true on success.
661  */
parseOptionValue(const QString & optionName,const QString & argument,QStringList::const_iterator * argumentIterator,QStringList::const_iterator argsEnd)662 bool QCommandLineParserPrivate::parseOptionValue(const QString &optionName, const QString &argument,
663                                                  QStringList::const_iterator *argumentIterator, QStringList::const_iterator argsEnd)
664 {
665     const QLatin1Char assignChar('=');
666     const NameHash_t::const_iterator nameHashIt = nameHash.constFind(optionName);
667     if (nameHashIt != nameHash.constEnd()) {
668         const int assignPos = argument.indexOf(assignChar);
669         const NameHash_t::mapped_type optionOffset = *nameHashIt;
670         const bool withValue = !commandLineOptionList.at(optionOffset).valueName().isEmpty();
671         if (withValue) {
672             if (assignPos == -1) {
673                 ++(*argumentIterator);
674                 if (*argumentIterator == argsEnd) {
675                     errorText = QCommandLineParser::tr("Missing value after '%1'.").arg(argument);
676                     return false;
677                 }
678                 optionValuesHash[optionOffset].append(*(*argumentIterator));
679             } else {
680                 optionValuesHash[optionOffset].append(argument.mid(assignPos + 1));
681             }
682         } else {
683             if (assignPos != -1) {
684                 errorText = QCommandLineParser::tr("Unexpected value after '%1'.").arg(argument.left(assignPos));
685                 return false;
686             }
687         }
688     }
689     return true;
690 }
691 
692 /*!
693     \internal
694 
695     Parse the list of arguments \a args, and fills in
696     optionNames, optionValuesHash, unknownOptionNames, positionalArguments, and errorText.
697 
698     Any results from a previous parse operation are removed.
699 
700     The parser will not look for further options once it encounters the option
701     \c{--}; this does not include when \c{--} follows an option that requires a value.
702  */
parse(const QStringList & args)703 bool QCommandLineParserPrivate::parse(const QStringList &args)
704 {
705     needsParsing = false;
706     bool error = false;
707 
708     const QString     doubleDashString(QStringLiteral("--"));
709     const QLatin1Char dashChar('-');
710     const QLatin1Char assignChar('=');
711 
712     bool forcePositional = false;
713     errorText.clear();
714     positionalArgumentList.clear();
715     optionNames.clear();
716     unknownOptionNames.clear();
717     optionValuesHash.clear();
718 
719     if (args.isEmpty()) {
720         qWarning("QCommandLineParser: argument list cannot be empty, it should contain at least the executable name");
721         return false;
722     }
723 
724     QStringList::const_iterator argumentIterator = args.begin();
725     ++argumentIterator; // skip executable name
726 
727     for (; argumentIterator != args.end() ; ++argumentIterator) {
728         QString argument = *argumentIterator;
729 
730         if (forcePositional) {
731             positionalArgumentList.append(argument);
732         } else if (argument.startsWith(doubleDashString)) {
733             if (argument.length() > 2) {
734                 QString optionName = argument.mid(2).section(assignChar, 0, 0);
735                 if (registerFoundOption(optionName)) {
736                     if (!parseOptionValue(optionName, argument, &argumentIterator, args.end()))
737                         error = true;
738                 } else {
739                     error = true;
740                 }
741             } else {
742                 forcePositional = true;
743             }
744         } else if (argument.startsWith(dashChar)) {
745             if (argument.size() == 1) { // single dash ("stdin")
746                 positionalArgumentList.append(argument);
747                 continue;
748             }
749             switch (singleDashWordOptionMode) {
750             case QCommandLineParser::ParseAsCompactedShortOptions:
751             {
752                 QString optionName;
753                 bool valueFound = false;
754                 for (int pos = 1 ; pos < argument.size(); ++pos) {
755                     optionName = argument.mid(pos, 1);
756                     if (!registerFoundOption(optionName)) {
757                         error = true;
758                     } else {
759                         const NameHash_t::const_iterator nameHashIt = nameHash.constFind(optionName);
760                         Q_ASSERT(nameHashIt != nameHash.constEnd()); // checked by registerFoundOption
761                         const NameHash_t::mapped_type optionOffset = *nameHashIt;
762                         const bool withValue = !commandLineOptionList.at(optionOffset).valueName().isEmpty();
763                         if (withValue) {
764                             if (pos + 1 < argument.size()) {
765                                 if (argument.at(pos + 1) == assignChar)
766                                     ++pos;
767                                 optionValuesHash[optionOffset].append(argument.mid(pos + 1));
768                                 valueFound = true;
769                             }
770                             break;
771                         }
772                         if (pos + 1 < argument.size() && argument.at(pos + 1) == assignChar)
773                             break;
774                     }
775                 }
776                 if (!valueFound && !parseOptionValue(optionName, argument, &argumentIterator, args.end()))
777                     error = true;
778                 break;
779             }
780             case QCommandLineParser::ParseAsLongOptions:
781             {
782                 if (argument.size() > 2) {
783                     const QString possibleShortOptionStyleName = argument.mid(1, 1);
784                     const auto shortOptionIt = nameHash.constFind(possibleShortOptionStyleName);
785                     if (shortOptionIt != nameHash.constEnd()) {
786                         const auto &arg = commandLineOptionList.at(*shortOptionIt);
787                         if (arg.flags() & QCommandLineOption::ShortOptionStyle) {
788                             registerFoundOption(possibleShortOptionStyleName);
789                             optionValuesHash[*shortOptionIt].append(argument.mid(2));
790                             break;
791                         }
792                     }
793                 }
794                 const QString optionName = argument.mid(1).section(assignChar, 0, 0);
795                 if (registerFoundOption(optionName)) {
796                     if (!parseOptionValue(optionName, argument, &argumentIterator, args.end()))
797                         error = true;
798                 } else {
799                     error = true;
800                 }
801                 break;
802             }
803             }
804         } else {
805             positionalArgumentList.append(argument);
806             if (optionsAfterPositionalArgumentsMode == QCommandLineParser::ParseAsPositionalArguments)
807                 forcePositional = true;
808         }
809         if (argumentIterator == args.end())
810             break;
811     }
812     return !error;
813 }
814 
815 /*!
816     Checks whether the option \a name was passed to the application.
817 
818     Returns \c true if the option \a name was set, false otherwise.
819 
820     The name provided can be any long or short name of any option that was
821     added with \c addOption(). All the options names are treated as being
822     equivalent. If the name is not recognized or that option was not present,
823     false is returned.
824 
825     Example:
826     \snippet code/src_corelib_tools_qcommandlineparser.cpp 0
827  */
828 
isSet(const QString & name) const829 bool QCommandLineParser::isSet(const QString &name) const
830 {
831     d->checkParsed("isSet");
832     if (d->optionNames.contains(name))
833         return true;
834     const QStringList aliases = d->aliases(name);
835     for (const QString &optionName : qAsConst(d->optionNames)) {
836         if (aliases.contains(optionName))
837             return true;
838     }
839     return false;
840 }
841 
842 /*!
843     Returns the option value found for the given option name \a optionName, or
844     an empty string if not found.
845 
846     The name provided can be any long or short name of any option that was
847     added with \c addOption(). All the option names are treated as being
848     equivalent. If the name is not recognized or that option was not present, an
849     empty string is returned.
850 
851     For options found by the parser, the last value found for
852     that option is returned. If the option wasn't specified on the command line,
853     the default value is returned.
854 
855     An empty string is returned if the option does not take a value.
856 
857     \sa values(), QCommandLineOption::setDefaultValue(), QCommandLineOption::setDefaultValues()
858  */
859 
value(const QString & optionName) const860 QString QCommandLineParser::value(const QString &optionName) const
861 {
862     d->checkParsed("value");
863     const QStringList valueList = values(optionName);
864 
865     if (!valueList.isEmpty())
866         return valueList.last();
867 
868     return QString();
869 }
870 
871 /*!
872     Returns a list of option values found for the given option name \a
873     optionName, or an empty list if not found.
874 
875     The name provided can be any long or short name of any option that was
876     added with \c addOption(). All the options names are treated as being
877     equivalent. If the name is not recognized or that option was not present, an
878     empty list is returned.
879 
880     For options found by the parser, the list will contain an entry for
881     each time the option was encountered by the parser. If the option wasn't
882     specified on the command line, the default values are returned.
883 
884     An empty list is returned if the option does not take a value.
885 
886     \sa value(), QCommandLineOption::setDefaultValue(), QCommandLineOption::setDefaultValues()
887  */
888 
values(const QString & optionName) const889 QStringList QCommandLineParser::values(const QString &optionName) const
890 {
891     d->checkParsed("values");
892     const NameHash_t::const_iterator it = d->nameHash.constFind(optionName);
893     if (it != d->nameHash.cend()) {
894         const int optionOffset = *it;
895         QStringList values = d->optionValuesHash.value(optionOffset);
896         if (values.isEmpty())
897             values = d->commandLineOptionList.at(optionOffset).defaultValues();
898         return values;
899     }
900 
901     qWarning("QCommandLineParser: option not defined: \"%ls\"", qUtf16Printable(optionName));
902     return QStringList();
903 }
904 
905 /*!
906     \overload
907     Checks whether the \a option was passed to the application.
908 
909     Returns \c true if the \a option was set, false otherwise.
910 
911     This is the recommended way to check for options with no values.
912 
913     Example:
914     \snippet code/src_corelib_tools_qcommandlineparser.cpp 1
915 */
isSet(const QCommandLineOption & option) const916 bool QCommandLineParser::isSet(const QCommandLineOption &option) const
917 {
918     // option.names() might be empty if the constructor failed
919     const auto names = option.names();
920     return !names.isEmpty() && isSet(names.first());
921 }
922 
923 /*!
924     \overload
925     Returns the option value found for the given \a option, or
926     an empty string if not found.
927 
928     For options found by the parser, the last value found for
929     that option is returned. If the option wasn't specified on the command line,
930     the default value is returned.
931 
932     An empty string is returned if the option does not take a value.
933 
934     \sa values(), QCommandLineOption::setDefaultValue(), QCommandLineOption::setDefaultValues()
935 */
value(const QCommandLineOption & option) const936 QString QCommandLineParser::value(const QCommandLineOption &option) const
937 {
938     return value(option.names().constFirst());
939 }
940 
941 /*!
942     \overload
943     Returns a list of option values found for the given \a option,
944     or an empty list if not found.
945 
946     For options found by the parser, the list will contain an entry for
947     each time the option was encountered by the parser. If the option wasn't
948     specified on the command line, the default values are returned.
949 
950     An empty list is returned if the option does not take a value.
951 
952     \sa value(), QCommandLineOption::setDefaultValue(), QCommandLineOption::setDefaultValues()
953 */
values(const QCommandLineOption & option) const954 QStringList QCommandLineParser::values(const QCommandLineOption &option) const
955 {
956     return values(option.names().constFirst());
957 }
958 
959 /*!
960     Returns a list of positional arguments.
961 
962     These are all of the arguments that were not recognized as part of an
963     option.
964  */
965 
positionalArguments() const966 QStringList QCommandLineParser::positionalArguments() const
967 {
968     d->checkParsed("positionalArguments");
969     return d->positionalArgumentList;
970 }
971 
972 /*!
973     Returns a list of option names that were found.
974 
975     This returns a list of all the recognized option names found by the
976     parser, in the order in which they were found. For any long options
977     that were in the form {--option=value}, the value part will have been
978     dropped.
979 
980     The names in this list do not include the preceding dash characters.
981     Names may appear more than once in this list if they were encountered
982     more than once by the parser.
983 
984     Any entry in the list can be used with \c value() or with
985     \c values() to get any relevant option values.
986  */
987 
optionNames() const988 QStringList QCommandLineParser::optionNames() const
989 {
990     d->checkParsed("optionNames");
991     return d->optionNames;
992 }
993 
994 /*!
995     Returns a list of unknown option names.
996 
997     This list will include both long an short name options that were not
998     recognized. For any long options that were in the form {--option=value},
999     the value part will have been dropped and only the long name is added.
1000 
1001     The names in this list do not include the preceding dash characters.
1002     Names may appear more than once in this list if they were encountered
1003     more than once by the parser.
1004 
1005     \sa optionNames()
1006  */
1007 
unknownOptionNames() const1008 QStringList QCommandLineParser::unknownOptionNames() const
1009 {
1010     d->checkParsed("unknownOptionNames");
1011     return d->unknownOptionNames;
1012 }
1013 
1014 /*!
1015     Displays the version information from QCoreApplication::applicationVersion(),
1016     and exits the application.
1017     This is automatically triggered by the --version option, but can also
1018     be used to display the version when not using process().
1019     The exit code is set to EXIT_SUCCESS (0).
1020 
1021     \sa addVersionOption()
1022     \since 5.4
1023 */
showVersion()1024 Q_NORETURN void QCommandLineParser::showVersion()
1025 {
1026     showParserMessage(QCoreApplication::applicationName() + QLatin1Char(' ')
1027                       + QCoreApplication::applicationVersion() + QLatin1Char('\n'),
1028                       UsageMessage);
1029     qt_call_post_routines();
1030     ::exit(EXIT_SUCCESS);
1031 }
1032 
1033 /*!
1034     Displays the help information, and exits the application.
1035     This is automatically triggered by the --help option, but can also
1036     be used to display the help when the user is not invoking the
1037     application correctly.
1038     The exit code is set to \a exitCode. It should be set to 0 if the
1039     user requested to see the help, and to any other value in case of
1040     an error.
1041 
1042     \sa helpText()
1043 */
showHelp(int exitCode)1044 Q_NORETURN void QCommandLineParser::showHelp(int exitCode)
1045 {
1046     d->showHelp(exitCode, false);
1047 }
1048 
showHelp(int exitCode,bool includeQtOptions)1049 Q_NORETURN void QCommandLineParserPrivate::showHelp(int exitCode, bool includeQtOptions)
1050 {
1051     showParserMessage(helpText(includeQtOptions), UsageMessage);
1052     qt_call_post_routines();
1053     ::exit(exitCode);
1054 }
1055 
1056 /*!
1057     Returns a string containing the complete help information.
1058 
1059     \sa showHelp()
1060 */
helpText() const1061 QString QCommandLineParser::helpText() const
1062 {
1063     return d->helpText(false);
1064 }
1065 
wrapText(const QString & names,int optionNameMaxWidth,const QString & description)1066 static QString wrapText(const QString &names, int optionNameMaxWidth, const QString &description)
1067 {
1068     const QLatin1Char nl('\n');
1069     const QLatin1String indentation("  ");
1070 
1071     // In case the list of option names is very long, wrap it as well
1072     int nameIndex = 0;
1073     auto nextNameSection = [&]() {
1074         QString section = names.mid(nameIndex, optionNameMaxWidth);
1075         nameIndex += section.size();
1076         return section;
1077     };
1078 
1079     QString text;
1080     int lineStart = 0;
1081     int lastBreakable = -1;
1082     const int max = 79 - (indentation.size() + optionNameMaxWidth + 1);
1083     int x = 0;
1084     const int len = description.length();
1085 
1086     for (int i = 0; i < len; ++i) {
1087         ++x;
1088         const QChar c = description.at(i);
1089         if (c.isSpace())
1090             lastBreakable = i;
1091 
1092         int breakAt = -1;
1093         int nextLineStart = -1;
1094         if (x > max && lastBreakable != -1) {
1095             // time to break and we know where
1096             breakAt = lastBreakable;
1097             nextLineStart = lastBreakable + 1;
1098         } else if ((x > max - 1 && lastBreakable == -1) || i == len - 1) {
1099             // time to break but found nowhere [-> break here], or end of last line
1100             breakAt = i + 1;
1101             nextLineStart = breakAt;
1102         } else if (c == nl) {
1103             // forced break
1104             breakAt = i;
1105             nextLineStart = i + 1;
1106         }
1107 
1108         if (breakAt != -1) {
1109             const int numChars = breakAt - lineStart;
1110             //qDebug() << "breakAt=" << description.at(breakAt) << "breakAtSpace=" << breakAtSpace << lineStart << "to" << breakAt << description.mid(lineStart, numChars);
1111             text += indentation + nextNameSection().leftJustified(optionNameMaxWidth) + QLatin1Char(' ');
1112             text += description.midRef(lineStart, numChars) + nl;
1113             x = 0;
1114             lastBreakable = -1;
1115             lineStart = nextLineStart;
1116             if (lineStart < len && description.at(lineStart).isSpace())
1117                 ++lineStart; // don't start a line with a space
1118             i = lineStart;
1119         }
1120     }
1121 
1122     while (nameIndex < names.size()) {
1123         text += indentation + nextNameSection() + nl;
1124     }
1125 
1126     return text;
1127 }
1128 
helpText(bool includeQtOptions) const1129 QString QCommandLineParserPrivate::helpText(bool includeQtOptions) const
1130 {
1131     const QLatin1Char nl('\n');
1132     QString text;
1133     QString usage;
1134     usage += QCoreApplication::instance()->arguments().constFirst(); // executable name
1135     QList<QCommandLineOption> options = commandLineOptionList;
1136     if (includeQtOptions)
1137         QCoreApplication::instance()->d_func()->addQtOptions(&options);
1138     if (!options.isEmpty())
1139         usage += QLatin1Char(' ') + QCommandLineParser::tr("[options]");
1140     for (const PositionalArgumentDefinition &arg : positionalArgumentDefinitions)
1141         usage += QLatin1Char(' ') + arg.syntax;
1142     text += QCommandLineParser::tr("Usage: %1").arg(usage) + nl;
1143     if (!description.isEmpty())
1144        text += description + nl;
1145     text += nl;
1146     if (!options.isEmpty())
1147         text += QCommandLineParser::tr("Options:") + nl;
1148     QStringList optionNameList;
1149     optionNameList.reserve(options.size());
1150     int longestOptionNameString = 0;
1151     for (const QCommandLineOption &option : qAsConst(options)) {
1152         if (option.flags() & QCommandLineOption::HiddenFromHelp)
1153             continue;
1154         const QStringList optionNames = option.names();
1155         QString optionNamesString;
1156         for (const QString &optionName : optionNames) {
1157             const int numDashes = optionName.length() == 1 ? 1 : 2;
1158             optionNamesString += QLatin1String("--", numDashes) + optionName + QLatin1String(", ");
1159         }
1160         if (!optionNames.isEmpty())
1161             optionNamesString.chop(2); // remove trailing ", "
1162         const auto valueName = option.valueName();
1163         if (!valueName.isEmpty())
1164             optionNamesString += QLatin1String(" <") + valueName + QLatin1Char('>');
1165         optionNameList.append(optionNamesString);
1166         longestOptionNameString = qMax(longestOptionNameString, optionNamesString.length());
1167     }
1168     ++longestOptionNameString;
1169     const int optionNameMaxWidth = qMin(50, longestOptionNameString);
1170     auto optionNameIterator = optionNameList.cbegin();
1171     for (const QCommandLineOption &option : qAsConst(options)) {
1172         if (option.flags() & QCommandLineOption::HiddenFromHelp)
1173             continue;
1174         text += wrapText(*optionNameIterator, optionNameMaxWidth, option.description());
1175         ++optionNameIterator;
1176     }
1177     if (!positionalArgumentDefinitions.isEmpty()) {
1178         if (!options.isEmpty())
1179             text += nl;
1180         text += QCommandLineParser::tr("Arguments:") + nl;
1181         for (const PositionalArgumentDefinition &arg : positionalArgumentDefinitions)
1182             text += wrapText(arg.name, optionNameMaxWidth, arg.description);
1183     }
1184     return text;
1185 }
1186 
1187 QT_END_NAMESPACE
1188