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. These message boxes can be omitted by setting
200 the \c QT_COMMAND_LINE_PARSER_NO_GUI_MESSAGE_BOXES environment variable.
201
202 For other platforms, it is recommended to display help texts and error messages
203 using a QMessageBox. To preserve the formatting of the help text, rich text
204 with \c <pre> elements should be used:
205
206 \code
207
208 switch (parseResult.statusCode) {
209 case Status::Ok:
210 break;
211 case Status::Error: {
212 QString errorMessage = parseResult.errorString.value_or(u"Unknown error occurred"_qs);
213 QMessageBox::warning(0, QGuiApplication::applicationDisplayName(),
214 "<html><head/><body><h2>" + errorMessage + "</h2><pre>"
215 + parser.helpText() + "</pre></body></html>");
216 return 1;
217 }
218 case Status::VersionRequested:
219 QMessageBox::information(0, QGuiApplication::applicationDisplayName(),
220 QGuiApplication::applicationDisplayName() + ' '
221 + QCoreApplication::applicationVersion());
222 return 0;
223 case Status::HelpRequested:
224 QMessageBox::warning(0, QGuiApplication::applicationDisplayName(),
225 "<html><head/><body><pre>"
226 + parser.helpText() + "</pre></body></html>");
227 return 0;
228 }
229 \endcode
230
231 However, this does not apply to the dnslookup example, because it is a
232 console application.
233
234 \sa QCommandLineOption, QCoreApplication
235*/
236
237/*!
238 Constructs a command line parser object.
239*/
240QCommandLineParser::QCommandLineParser()
241 : d(new QCommandLineParserPrivate)
242{
243}
244
245/*!
246 Destroys the command line parser object.
247*/
248QCommandLineParser::~QCommandLineParser()
249{
250 delete d;
251}
252
253/*!
254 \enum QCommandLineParser::SingleDashWordOptionMode
255
256 This enum describes the way the parser interprets command-line
257 options that use a single dash followed by multiple letters, as \c{-abc}.
258
259 \value ParseAsCompactedShortOptions \c{-abc} is interpreted as \c{-a -b -c},
260 i.e. as three short options that have been compacted on the command-line,
261 if none of the options take a value. If \c{a} takes a value, then it
262 is interpreted as \c{-a bc}, i.e. the short option \c{a} followed by the value \c{bc}.
263 This is typically used in tools that behave like compilers, in order
264 to handle options such as \c{-DDEFINE=VALUE} or \c{-I/include/path}.
265 This is the default parsing mode. New applications are recommended to
266 use this mode.
267
268 \value ParseAsLongOptions \c{-abc} is interpreted as \c{--abc},
269 i.e. as the long option named \c{abc}. This is how Qt's own tools
270 (uic, rcc...) have always been parsing arguments. This mode should be
271 used for preserving compatibility in applications that were parsing
272 arguments in such a way. There is an exception if the \c{a} option has the
273 QCommandLineOption::ShortOptionStyle flag set, in which case it is still
274 interpreted as \c{-a bc}.
275
276 \sa setSingleDashWordOptionMode()
277*/
278
279/*!
280 Sets the parsing mode to \a singleDashWordOptionMode.
281 This must be called before process() or parse().
282*/
283void QCommandLineParser::setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode)
284{
285 d->singleDashWordOptionMode = singleDashWordOptionMode;
286}
287
288/*!
289 \enum QCommandLineParser::OptionsAfterPositionalArgumentsMode
290
291 This enum describes the way the parser interprets options that
292 occur after positional arguments.
293
294 \value ParseAsOptions \c{application argument --opt -t} is interpreted as setting
295 the options \c{opt} and \c{t}, just like \c{application --opt -t argument} would do.
296 This is the default parsing mode. In order to specify that \c{--opt} and \c{-t}
297 are positional arguments instead, the user can use \c{--}, as in
298 \c{application argument -- --opt -t}.
299
300 \value ParseAsPositionalArguments \c{application argument --opt} is interpreted as
301 having two positional arguments, \c{argument} and \c{--opt}.
302 This mode is useful for executables that aim to launch other executables
303 (e.g. wrappers, debugging tools, etc.) or that support internal commands
304 followed by options for the command. \c{argument} is the name of the command,
305 and all options occurring after it can be collected and parsed by another
306 command line parser, possibly in another executable.
307
308 \sa setOptionsAfterPositionalArgumentsMode()
309
310 \since 5.6
311*/
312
313/*!
314 Sets the parsing mode to \a parsingMode.
315 This must be called before process() or parse().
316 \since 5.6
317*/
318void QCommandLineParser::setOptionsAfterPositionalArgumentsMode(QCommandLineParser::OptionsAfterPositionalArgumentsMode parsingMode)
319{
320 d->optionsAfterPositionalArgumentsMode = parsingMode;
321}
322
323/*!
324 Adds the option \a option to look for while parsing.
325
326 Returns \c true if adding the option was successful; otherwise returns \c false.
327
328 Adding the option fails if there is no name attached to the option, or
329 the option has a name that clashes with an option name added before.
330 */
331bool QCommandLineParser::addOption(const QCommandLineOption &option)
332{
333 const QStringList optionNames = option.names();
334
335 if (!optionNames.isEmpty()) {
336 for (const QString &name : optionNames) {
337 if (d->nameHash.contains(key: name)) {
338 qWarning() << "QCommandLineParser: already having an option named" << name;
339 return false;
340 }
341 }
342
343 d->commandLineOptionList.append(t: option);
344
345 const qsizetype offset = d->commandLineOptionList.size() - 1;
346 for (const QString &name : optionNames)
347 d->nameHash.insert(key: name, value: offset);
348
349 return true;
350 }
351
352 return false;
353}
354
355/*!
356 \since 5.4
357
358 Adds the options to look for while parsing. The options are specified by
359 the parameter \a options.
360
361 Returns \c true if adding all of the options was successful; otherwise
362 returns \c false.
363
364 See the documentation for addOption() for when this function may fail.
365*/
366bool QCommandLineParser::addOptions(const QList<QCommandLineOption> &options)
367{
368 // should be optimized (but it's no worse than what was possible before)
369 bool result = true;
370 for (QList<QCommandLineOption>::const_iterator it = options.begin(), end = options.end(); it != end; ++it)
371 result &= addOption(option: *it);
372 return result;
373}
374
375/*!
376 Adds the \c{-v} / \c{--version} option, which displays the version string of the application.
377
378 This option is handled automatically by QCommandLineParser.
379
380 You can set the actual version string by using QCoreApplication::setApplicationVersion().
381
382 Returns the option instance, which can be used to call isSet().
383*/
384QCommandLineOption QCommandLineParser::addVersionOption()
385{
386 QCommandLineOption opt(QStringList() << QStringLiteral("v") << QStringLiteral("version"), tr(sourceText: "Displays version information."));
387 addOption(option: opt);
388 d->builtinVersionOption = true;
389 return opt;
390}
391
392/*!
393 Adds help options to the command-line parser.
394
395 The options specified for this command-line are described by \c{-h} or
396 \c{--help}. On Windows, the alternative \c{-?} is also supported. The option
397 \c{--help-all} extends that to include generic Qt options, not defined by
398 this command, in the output.
399
400 These options are handled automatically by QCommandLineParser.
401
402 Remember to use setApplicationDescription() to set the application
403 description, which will be displayed when this option is used.
404
405 Example:
406 \snippet code/src_corelib_tools_qcommandlineparser_main.cpp 0
407
408 Returns the option instance, which can be used to call isSet().
409*/
410QCommandLineOption QCommandLineParser::addHelpOption()
411{
412 QCommandLineOption opt(QStringList()
413#ifdef Q_OS_WIN
414 << QStringLiteral("?")
415#endif
416 << QStringLiteral("h")
417 << QStringLiteral("help"), tr(sourceText: "Displays help on commandline options."));
418 addOption(option: opt);
419 QCommandLineOption optHelpAll(QStringLiteral("help-all"),
420 tr(sourceText: "Displays help, including generic Qt options."));
421 addOption(option: optHelpAll);
422 d->builtinHelpOption = true;
423 return opt;
424}
425
426/*!
427 Sets the application \a description shown by helpText().
428*/
429void QCommandLineParser::setApplicationDescription(const QString &description)
430{
431 d->description = description;
432}
433
434/*!
435 Returns the application description set in setApplicationDescription().
436*/
437QString QCommandLineParser::applicationDescription() const
438{
439 return d->description;
440}
441
442/*!
443 Defines an additional argument to the application, for the benefit of the help text.
444
445 The argument \a name and \a description will appear under the \c{Arguments:} section
446 of the help. If \a syntax is specified, it will be appended to the Usage line, otherwise
447 the \a name will be appended.
448
449 Example:
450 \snippet code/src_corelib_tools_qcommandlineparser.cpp 2
451
452 \sa addHelpOption(), helpText()
453*/
454void QCommandLineParser::addPositionalArgument(const QString &name, const QString &description, const QString &syntax)
455{
456 QCommandLineParserPrivate::PositionalArgumentDefinition arg;
457 arg.name = name;
458 arg.description = description;
459 arg.syntax = syntax.isEmpty() ? name : syntax;
460 d->positionalArgumentDefinitions.append(t: arg);
461}
462
463/*!
464 Clears the definitions of additional arguments from the help text.
465
466 This is only needed for the special case of tools which support multiple commands
467 with different options. Once the actual command has been identified, the options
468 for this command can be defined, and the help text for the command can be adjusted
469 accordingly.
470
471 Example:
472 \snippet code/src_corelib_tools_qcommandlineparser.cpp 3
473*/
474void QCommandLineParser::clearPositionalArguments()
475{
476 d->positionalArgumentDefinitions.clear();
477}
478
479/*!
480 Parses the command line \a arguments.
481
482 Most programs don't need to call this, a simple call to process() is enough.
483
484 parse() is more low-level, and only does the parsing. The application will have to
485 take care of the error handling, using errorText() if parse() returns \c false.
486 This can be useful for instance to show a graphical error message in graphical programs.
487
488 Calling parse() instead of process() can also be useful in order to ignore unknown
489 options temporarily, because more option definitions will be provided later on
490 (depending on one of the arguments), before calling process().
491
492 Don't forget that \a arguments must start with the name of the executable (ignored, though).
493
494 Returns \c false in case of a parse error (unknown option or missing value); returns \c true otherwise.
495
496 \sa process()
497*/
498bool QCommandLineParser::parse(const QStringList &arguments)
499{
500 return d->parse(args: arguments);
501}
502
503/*!
504 Returns a translated error text for the user.
505 This should only be called when parse() returns \c false.
506*/
507QString QCommandLineParser::errorText() const
508{
509 if (!d->errorText.isEmpty())
510 return d->errorText;
511 if (d->unknownOptionNames.size() == 1)
512 return tr(sourceText: "Unknown option '%1'.").arg(a: d->unknownOptionNames.constFirst());
513 if (d->unknownOptionNames.size() > 1)
514 return tr(sourceText: "Unknown options: %1.").arg(a: d->unknownOptionNames.join(QStringLiteral(", ")));
515 return QString();
516}
517
518enum MessageType { UsageMessage, ErrorMessage };
519
520#if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED)
521// Return whether to use a message box. Use handles if a console can be obtained
522// or we are run with redirected handles (for example, by QProcess).
523static inline bool displayMessageBox()
524{
525 if (GetConsoleWindow()
526 || qEnvironmentVariableIsSet("QT_COMMAND_LINE_PARSER_NO_GUI_MESSAGE_BOXES"))
527 return false;
528 STARTUPINFO startupInfo;
529 startupInfo.cb = sizeof(STARTUPINFO);
530 GetStartupInfo(&startupInfo);
531 return !(startupInfo.dwFlags & STARTF_USESTDHANDLES);
532}
533#endif // Q_OS_WIN && !QT_BOOTSTRAPPED
534
535static void showParserMessage(const QString &message, MessageType type)
536{
537#if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED)
538 if (displayMessageBox()) {
539 const UINT flags = MB_OK | MB_TOPMOST | MB_SETFOREGROUND
540 | (type == UsageMessage ? MB_ICONINFORMATION : MB_ICONERROR);
541 QString title;
542 if (QCoreApplication::instance())
543 title = QCoreApplication::instance()->property("applicationDisplayName").toString();
544 if (title.isEmpty())
545 title = QCoreApplication::applicationName();
546 MessageBoxW(0, reinterpret_cast<const wchar_t *>(message.utf16()),
547 reinterpret_cast<const wchar_t *>(title.utf16()), flags);
548 return;
549 }
550#endif // Q_OS_WIN && !QT_BOOTSTRAPPED
551 fputs(qPrintable(message), stream: type == UsageMessage ? stdout : stderr);
552}
553
554/*!
555 Processes the command line \a arguments.
556
557 In addition to parsing the options (like parse()), this function also handles the builtin
558 options and handles errors.
559
560 The builtin options are \c{--version} if addVersionOption was called and
561 \c{--help} / \c{--help-all} if addHelpOption was called.
562
563 When invoking one of these options, or when an error happens (for instance an unknown option was
564 passed), the current process will then stop, using the exit() function.
565
566 \sa QCoreApplication::arguments(), parse()
567 */
568void QCommandLineParser::process(const QStringList &arguments)
569{
570 if (!d->parse(args: arguments)) {
571 showParserMessage(message: QCoreApplication::applicationName() + ": "_L1 + errorText() + u'\n', type: ErrorMessage);
572 qt_call_post_routines();
573 ::exit(EXIT_FAILURE);
574 }
575
576 if (d->builtinVersionOption && isSet(QStringLiteral("version")))
577 showVersion();
578
579 if (d->builtinHelpOption && isSet(QStringLiteral("help")))
580 d->showHelp(EXIT_SUCCESS, includeQtOptions: false);
581
582 if (d->builtinHelpOption && isSet(QStringLiteral("help-all")))
583 d->showHelp(EXIT_SUCCESS, includeQtOptions: true);
584}
585
586/*!
587 \overload
588
589 The command line is obtained from the QCoreApplication instance \a app.
590 */
591void QCommandLineParser::process(const QCoreApplication &app)
592{
593 // QCoreApplication::arguments() is static, but the app instance must exist so we require it as parameter
594 Q_UNUSED(app);
595 process(arguments: QCoreApplication::arguments());
596}
597
598void QCommandLineParserPrivate::checkParsed(const char *method)
599{
600 if (needsParsing)
601 qWarning(msg: "QCommandLineParser: call process() or parse() before %s", method);
602}
603
604/*!
605 \internal
606 Looks up the option \a optionName (found on the command line) and register it as found.
607 Returns \c true on success.
608 */
609bool QCommandLineParserPrivate::registerFoundOption(const QString &optionName)
610{
611 if (nameHash.contains(key: optionName)) {
612 optionNames.append(t: optionName);
613 return true;
614 } else {
615 unknownOptionNames.append(t: optionName);
616 return false;
617 }
618}
619
620/*!
621 \internal
622 \brief Parse the value for a given option, if it was defined to expect one.
623
624 The value is taken from the next argument, or after the equal sign in \a argument.
625
626 \param optionName the short option name
627 \param argument the argument from the command line currently parsed. Only used for -k=value parsing.
628 \param argumentIterator iterator to the currently parsed argument. Incremented if the next argument contains the value.
629 \param argsEnd args.end(), to check if ++argumentIterator goes out of bounds
630 Returns \c true on success.
631 */
632bool QCommandLineParserPrivate::parseOptionValue(const QString &optionName, const QString &argument,
633 QStringList::const_iterator *argumentIterator, QStringList::const_iterator argsEnd)
634{
635 const QLatin1Char assignChar('=');
636 const NameHash_t::const_iterator nameHashIt = nameHash.constFind(key: optionName);
637 if (nameHashIt != nameHash.constEnd()) {
638 const qsizetype assignPos = argument.indexOf(c: assignChar);
639 const NameHash_t::mapped_type optionOffset = *nameHashIt;
640 const bool withValue = !commandLineOptionList.at(i: optionOffset).valueName().isEmpty();
641 if (withValue) {
642 if (assignPos == -1) {
643 ++(*argumentIterator);
644 if (*argumentIterator == argsEnd) {
645 errorText = QCommandLineParser::tr(sourceText: "Missing value after '%1'.").arg(a: argument);
646 return false;
647 }
648 optionValuesHash[optionOffset].append(t: *(*argumentIterator));
649 } else {
650 optionValuesHash[optionOffset].append(t: argument.mid(position: assignPos + 1));
651 }
652 } else {
653 if (assignPos != -1) {
654 errorText = QCommandLineParser::tr(sourceText: "Unexpected value after '%1'.").arg(a: argument.left(n: assignPos));
655 return false;
656 }
657 }
658 }
659 return true;
660}
661
662/*!
663 \internal
664
665 Parse the list of arguments \a args, and fills in
666 optionNames, optionValuesHash, unknownOptionNames, positionalArguments, and errorText.
667
668 Any results from a previous parse operation are removed.
669
670 The parser will not look for further options once it encounters the option
671 \c{--}; this does not include when \c{--} follows an option that requires a value.
672 */
673bool QCommandLineParserPrivate::parse(const QStringList &args)
674{
675 needsParsing = false;
676 bool error = false;
677
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: "--"_L1)) {
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 If the option does not take a value, a warning is printed, and an empty string is returned.
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 const auto &option = d->commandLineOptionList.at(i: optionOffset);
867 if (option.valueName().isEmpty()) {
868 qWarning(msg: "QCommandLineParser: option not expecting values: \"%ls\"",
869 qUtf16Printable(optionName));
870 }
871 values = option.defaultValues();
872 }
873 return values;
874 }
875
876 qWarning(msg: "QCommandLineParser: option not defined: \"%ls\"", qUtf16Printable(optionName));
877 return QStringList();
878}
879
880/*!
881 \overload
882 Checks whether the \a option was passed to the application.
883
884 Returns \c true if the \a option was set, false otherwise.
885
886 This is the recommended way to check for options with no values.
887
888 Example:
889 \snippet code/src_corelib_tools_qcommandlineparser.cpp 1
890*/
891bool QCommandLineParser::isSet(const QCommandLineOption &option) const
892{
893 // option.names() might be empty if the constructor failed
894 const auto names = option.names();
895 return !names.isEmpty() && isSet(name: names.first());
896}
897
898/*!
899 \overload
900 Returns the option value found for the given \a option, or
901 an empty string if not found.
902
903 For options found by the parser, the last value found for
904 that option is returned. If the option wasn't specified on the command line,
905 the default value is returned.
906
907 An empty string is returned if the option does not take a value.
908
909 \sa values(), QCommandLineOption::setDefaultValue(), QCommandLineOption::setDefaultValues()
910*/
911QString QCommandLineParser::value(const QCommandLineOption &option) const
912{
913 return value(optionName: option.names().constFirst());
914}
915
916/*!
917 \overload
918 Returns a list of option values found for the given \a option,
919 or an empty list if not found.
920
921 For options found by the parser, the list will contain an entry for
922 each time the option was encountered by the parser. If the option wasn't
923 specified on the command line, the default values are returned.
924
925 An empty list is returned if the option does not take a value.
926
927 \sa value(), QCommandLineOption::setDefaultValue(), QCommandLineOption::setDefaultValues()
928*/
929QStringList QCommandLineParser::values(const QCommandLineOption &option) const
930{
931 return values(optionName: option.names().constFirst());
932}
933
934/*!
935 Returns a list of positional arguments.
936
937 These are all of the arguments that were not recognized as part of an
938 option.
939 */
940
941QStringList QCommandLineParser::positionalArguments() const
942{
943 d->checkParsed(method: "positionalArguments");
944 return d->positionalArgumentList;
945}
946
947/*!
948 Returns a list of option names that were found.
949
950 This returns a list of all the recognized option names found by the
951 parser, in the order in which they were found. For any long options
952 that were in the form {--option=value}, the value part will have been
953 dropped.
954
955 The names in this list do not include the preceding dash characters.
956 Names may appear more than once in this list if they were encountered
957 more than once by the parser.
958
959 Any entry in the list can be used with value() or with
960 values() to get any relevant option values.
961 */
962
963QStringList QCommandLineParser::optionNames() const
964{
965 d->checkParsed(method: "optionNames");
966 return d->optionNames;
967}
968
969/*!
970 Returns a list of unknown option names.
971
972 This list will include both long an short name options that were not
973 recognized. For any long options that were in the form {--option=value},
974 the value part will have been dropped and only the long name is added.
975
976 The names in this list do not include the preceding dash characters.
977 Names may appear more than once in this list if they were encountered
978 more than once by the parser.
979
980 \sa optionNames()
981 */
982
983QStringList QCommandLineParser::unknownOptionNames() const
984{
985 d->checkParsed(method: "unknownOptionNames");
986 return d->unknownOptionNames;
987}
988
989/*!
990 Displays the version information from QCoreApplication::applicationVersion(),
991 and exits the application.
992 This is automatically triggered by the --version option, but can also
993 be used to display the version when not using process().
994 The exit code is set to EXIT_SUCCESS (0).
995
996 \sa addVersionOption()
997 \since 5.4
998*/
999Q_NORETURN void QCommandLineParser::showVersion()
1000{
1001 showParserMessage(message: QCoreApplication::applicationName() + u' '
1002 + QCoreApplication::applicationVersion() + u'\n',
1003 type: UsageMessage);
1004 qt_call_post_routines();
1005 ::exit(EXIT_SUCCESS);
1006}
1007
1008/*!
1009 Displays the help information, and exits the application.
1010 This is automatically triggered by the --help option, but can also
1011 be used to display the help when the user is not invoking the
1012 application correctly.
1013 The exit code is set to \a exitCode. It should be set to 0 if the
1014 user requested to see the help, and to any other value in case of
1015 an error.
1016
1017 \sa helpText()
1018*/
1019Q_NORETURN void QCommandLineParser::showHelp(int exitCode)
1020{
1021 d->showHelp(exitCode, includeQtOptions: false);
1022}
1023
1024Q_NORETURN void QCommandLineParserPrivate::showHelp(int exitCode, bool includeQtOptions)
1025{
1026 showParserMessage(message: helpText(includeQtOptions), type: UsageMessage);
1027 qt_call_post_routines();
1028 ::exit(status: exitCode);
1029}
1030
1031/*!
1032 Returns a string containing the complete help information.
1033
1034 \sa showHelp()
1035*/
1036QString QCommandLineParser::helpText() const
1037{
1038 return d->helpText(includeQtOptions: false);
1039}
1040
1041static QString wrapText(const QString &names, int optionNameMaxWidth, const QString &description)
1042{
1043 const auto nl = u'\n';
1044 const auto indentation = " "_L1;
1045
1046 // In case the list of option names is very long, wrap it as well
1047 int nameIndex = 0;
1048 auto nextNameSection = [&]() {
1049 QString section = names.mid(position: nameIndex, n: optionNameMaxWidth);
1050 nameIndex += section.size();
1051 return section;
1052 };
1053
1054 QString text;
1055 qsizetype lineStart = 0;
1056 qsizetype lastBreakable = -1;
1057 const int max = 79 - (indentation.size() + optionNameMaxWidth + 1);
1058 int x = 0;
1059 const qsizetype len = description.size();
1060
1061 for (qsizetype i = 0; i < len; ++i) {
1062 ++x;
1063 const QChar c = description.at(i);
1064 if (c.isSpace())
1065 lastBreakable = i;
1066
1067 qsizetype breakAt = -1;
1068 qsizetype nextLineStart = -1;
1069 if (x > max && lastBreakable != -1) {
1070 // time to break and we know where
1071 breakAt = lastBreakable;
1072 nextLineStart = lastBreakable + 1;
1073 } else if ((x > max - 1 && lastBreakable == -1) || i == len - 1) {
1074 // time to break but found nowhere [-> break here], or end of last line
1075 breakAt = i + 1;
1076 nextLineStart = breakAt;
1077 } else if (c == nl) {
1078 // forced break
1079 breakAt = i;
1080 nextLineStart = i + 1;
1081 }
1082
1083 if (breakAt != -1) {
1084 const qsizetype numChars = breakAt - lineStart;
1085 //qDebug() << "breakAt=" << description.at(breakAt) << "breakAtSpace=" << breakAtSpace << lineStart << "to" << breakAt << description.mid(lineStart, numChars);
1086 text += indentation + nextNameSection().leftJustified(width: optionNameMaxWidth) + u' ';
1087 text += QStringView{description}.mid(pos: lineStart, n: numChars) + nl;
1088 x = 0;
1089 lastBreakable = -1;
1090 lineStart = nextLineStart;
1091 if (lineStart < len && description.at(i: lineStart).isSpace())
1092 ++lineStart; // don't start a line with a space
1093 i = lineStart;
1094 }
1095 }
1096
1097 while (nameIndex < names.size()) {
1098 text += indentation + nextNameSection() + nl;
1099 }
1100
1101 return text;
1102}
1103
1104QString QCommandLineParserPrivate::helpText(bool includeQtOptions) const
1105{
1106 const QLatin1Char nl('\n');
1107 QString text;
1108 QString usage;
1109 // executable name
1110 usage += qApp ? QStringView(QCoreApplication::arguments().constFirst())
1111 : QStringView(u"<executable_name>");
1112 QList<QCommandLineOption> options = commandLineOptionList;
1113 if (includeQtOptions && qApp)
1114 qApp->d_func()->addQtOptions(options: &options);
1115 if (!options.isEmpty())
1116 usage += u' ' + QCommandLineParser::tr(sourceText: "[options]");
1117 for (const PositionalArgumentDefinition &arg : positionalArgumentDefinitions)
1118 usage += u' ' + arg.syntax;
1119 text += QCommandLineParser::tr(sourceText: "Usage: %1").arg(a: usage) + nl;
1120 if (!description.isEmpty())
1121 text += description + nl;
1122 text += nl;
1123 if (!options.isEmpty())
1124 text += QCommandLineParser::tr(sourceText: "Options:") + nl;
1125 QStringList optionNameList;
1126 optionNameList.reserve(asize: options.size());
1127 qsizetype longestOptionNameString = 0;
1128 for (const QCommandLineOption &option : std::as_const(t&: options)) {
1129 if (option.flags() & QCommandLineOption::HiddenFromHelp)
1130 continue;
1131 const QStringList optionNames = option.names();
1132 QString optionNamesString;
1133 for (const QString &optionName : optionNames) {
1134 const int numDashes = optionName.size() == 1 ? 1 : 2;
1135 optionNamesString += QLatin1StringView("--", numDashes) + optionName + ", "_L1;
1136 }
1137 if (!optionNames.isEmpty())
1138 optionNamesString.chop(n: 2); // remove trailing ", "
1139 const auto valueName = option.valueName();
1140 if (!valueName.isEmpty())
1141 optionNamesString += " <"_L1 + valueName + u'>';
1142 optionNameList.append(t: optionNamesString);
1143 longestOptionNameString = qMax(a: longestOptionNameString, b: optionNamesString.size());
1144 }
1145 ++longestOptionNameString;
1146 const int optionNameMaxWidth = qMin(a: 50, b: int(longestOptionNameString));
1147 auto optionNameIterator = optionNameList.cbegin();
1148 for (const QCommandLineOption &option : std::as_const(t&: options)) {
1149 if (option.flags() & QCommandLineOption::HiddenFromHelp)
1150 continue;
1151 text += wrapText(names: *optionNameIterator, optionNameMaxWidth, description: option.description());
1152 ++optionNameIterator;
1153 }
1154 if (!positionalArgumentDefinitions.isEmpty()) {
1155 if (!options.isEmpty())
1156 text += nl;
1157 text += QCommandLineParser::tr(sourceText: "Arguments:") + nl;
1158 for (const PositionalArgumentDefinition &arg : positionalArgumentDefinitions)
1159 text += wrapText(names: arg.name, optionNameMaxWidth, description: arg.description);
1160 }
1161 return text;
1162}
1163
1164QT_END_NAMESPACE
1165

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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