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

source code of qtbase/src/corelib/tools/qcommandlineparser.cpp