1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtDebug> |
30 | #include <QTextBoundaryFinder> |
31 | #include <QCoreApplication> |
32 | #include <QHash> |
33 | #include <QPair> |
34 | #include <QStringList> |
35 | #include <QTextStream> |
36 | #include <QUrl> |
37 | #include <QRegExp> |
38 | |
39 | #include "qapplicationargument_p.h" |
40 | |
41 | #include "qapplicationargumentparser_p.h" |
42 | |
43 | #include <algorithm> |
44 | |
45 | QT_BEGIN_NAMESPACE |
46 | |
47 | /*! |
48 | \class QApplicationArgumentParser |
49 | \brief The QApplicationArgumentParser class parses the command |
50 | line arguments for an application. |
51 | \reentrant |
52 | \internal |
53 | \since 4.4 |
54 | |
55 | QApplicationArgumentParser simplifies writing command line applications by taking care of: |
56 | |
57 | \list |
58 | \li Generating help and version arguments |
59 | \li Taking care of converting arguments to QVariant types, since each argument |
60 | has a type: QApplicationArgument::type() |
61 | \li Validates the command line such that the user operates on well-defined input. For instance, |
62 | that the argument is a valid integer if that is the case, that an argument does not |
63 | occur more times than allowed, and so on. |
64 | \li Allows customization through sub-classing. |
65 | \endlist |
66 | |
67 | The user declares what arguments that can be given to the application with QApplicationArgument. Provided |
68 | with that information, QApplicationArgumentParser takes care of parsing the actual |
69 | command line, appropriately flag errors, generate help messages, and provide |
70 | convenient access to the values of the arguments. |
71 | |
72 | The way to use it is to create a set of QApplicationArgument by ones choosing, call |
73 | addArgument() for each, and subsequently call parse(). If parse() returns \c false, |
74 | the caller should exit and return exitCode(). |
75 | |
76 | If parse() returns \c true the command line was successfully parsed, its |
77 | values are well-defined, and they can be spectated with count(), |
78 | has(), value() and values(). |
79 | |
80 | \snippet doc/src/snippets/code/tools_patternist_qapplicationargumentparser.cpp 0 |
81 | |
82 | For arguments without a name(such as filename passed to the \c ls utility on Linux) add a |
83 | QApplicationArgument that does not have a name. The minimum and maximum occurrences will be |
84 | respected as usual and the type applies too. |
85 | |
86 | QApplicationArgumentParser always has two options builtin: \c version and \c help. |
87 | |
88 | \section1 Changing Parsing Convention |
89 | |
90 | QApplicationArgumentParser by default parses the command line in the style |
91 | of Qt's utilities, where arguments are preceded by a single dash, and identified |
92 | by a single name. However, in some cases it might be of interest to parse |
93 | another style, such as the well-established UNIX \c getopt convention(\c -l |
94 | and \c --long). |
95 | |
96 | This can be achieved by sub-classing QApplicationArgumentParser and reimplementing |
97 | parse(). It would do the following: |
98 | |
99 | \list |
100 | \li Call input() to retrieve the strings the user specified on the command line. |
101 | \li Call declaredArguments() to retrieve the arguments that the implementor has |
102 | decided can be specified. |
103 | \li Parse and validate the input. Salt and pepper as per taste. |
104 | \li If an error occurred, call setExitCode() and return \c false. |
105 | \li Otherwise, call setExitCode(Success), provide access to the |
106 | arguments by calling setUsedArguments(), and return \c true. If a |
107 | help message was requested, call setExitCode(Success) and return \c false. |
108 | \endlist |
109 | |
110 | \sa QApplicationArgument, QCoreApplication |
111 | */ |
112 | class QApplicationArgumentParserPrivate |
113 | { |
114 | Q_DECLARE_TR_FUNCTIONS(QApplicationArgumentParserPrivate) |
115 | public: |
116 | // TODO Isn't it like ten times better with QHash<QApplicationArgument, QList<QVariant> >? |
117 | // TODO test QApplicationArgument::nameless() |
118 | typedef QList<QPair<QApplicationArgument, QVariant> > UsedList; |
119 | |
120 | /*! |
121 | We initialize exitCode to ParseError such that we consciously flag success. |
122 | */ |
123 | inline QApplicationArgumentParserPrivate(QApplicationArgumentParser *const master, |
124 | const QStringList &aInput) : exitCode(QApplicationArgumentParser::ParseError) |
125 | , input(aInput) |
126 | , q_ptr(master) |
127 | { |
128 | Q_ASSERT(!aInput.isEmpty()); |
129 | } |
130 | |
131 | QApplicationArgument nextNamelessArgument() const; |
132 | static QStringList argumentsFromLocal(const int argc, const char *const *const argv); |
133 | |
134 | bool error(const QString &message); |
135 | static bool errorMessage(const QString &message); |
136 | static inline bool isSwitch(const QApplicationArgument &arg); |
137 | static inline QVariant conversionError(const QString &typeName, |
138 | const QString &input); |
139 | int count(const QApplicationArgument &arg) const; |
140 | bool contains(const QApplicationArgument &arg) const; |
141 | static inline bool isBuiltinVariant(const int type); |
142 | void displayVersion() const; |
143 | void displayHelp() const; |
144 | void parseNameless(); |
145 | bool parseNamelessArguments(const QString &in); |
146 | |
147 | QApplicationArgumentParser::ExitCode exitCode; |
148 | const QStringList input; |
149 | |
150 | /*! |
151 | Since the QString is QApplicationArgument::name() anyway, why |
152 | not use a QSet? |
153 | */ |
154 | QHash<QString, QApplicationArgument> declaredArguments; |
155 | |
156 | QList<QApplicationArgument> declaredNamelessArguments; |
157 | |
158 | UsedList usedArguments; |
159 | QString applicationDescription; |
160 | QString applicationVersion; |
161 | |
162 | private: |
163 | QApplicationArgumentParser *const q_ptr; |
164 | Q_DECLARE_PUBLIC(QApplicationArgumentParser) |
165 | |
166 | static QString lineWrap(const QString &input, |
167 | const int leftIndent, |
168 | const int width); |
169 | static QList<QApplicationArgument> builtinArguments(); |
170 | }; |
171 | |
172 | QApplicationArgument QApplicationArgumentParserPrivate::nextNamelessArgument() const |
173 | { |
174 | /* Count how many nameless arguments we have so far. */ |
175 | int count = 0; |
176 | |
177 | for(int i = 0; i < usedArguments.count(); ++i) |
178 | { |
179 | if(usedArguments.at(i).first.isNameless()) |
180 | ++count; |
181 | } |
182 | |
183 | /* TODO this doesn't work for arguments that have more than one |
184 | * mandatory value(e.g nameless ones), since several values should |
185 | * then only count for one argument. */ |
186 | for(int i = 0; i < declaredNamelessArguments.count(); ++i) |
187 | { |
188 | if(count) |
189 | { |
190 | /* Skip the ones we already have processed. */ |
191 | --count; |
192 | continue; |
193 | } |
194 | |
195 | if(declaredNamelessArguments.at(i).isNameless()) |
196 | return declaredNamelessArguments.at(i); |
197 | } |
198 | |
199 | return QApplicationArgument(); |
200 | } |
201 | |
202 | int QApplicationArgumentParserPrivate::count(const QApplicationArgument &arg) const |
203 | { |
204 | const int len = usedArguments.count(); |
205 | int count = 0; |
206 | |
207 | for(int i = 0; i < len; ++i) |
208 | { |
209 | if(usedArguments.at(i).first == arg) |
210 | ++count; |
211 | } |
212 | |
213 | return count; |
214 | } |
215 | |
216 | /*! |
217 | Returns \c true if \a arg has appeared on the command line, not whether it has been declared. |
218 | */ |
219 | bool QApplicationArgumentParserPrivate::contains(const QApplicationArgument &arg) const |
220 | { |
221 | const int len = usedArguments.count(); |
222 | |
223 | for(int i = 0; i < len; ++i) |
224 | { |
225 | if(usedArguments.at(i).first == arg) |
226 | return true; |
227 | } |
228 | |
229 | return false; |
230 | } |
231 | |
232 | /*! |
233 | Returns always \c false. |
234 | */ |
235 | bool QApplicationArgumentParserPrivate::error(const QString &message) |
236 | { |
237 | exitCode = QApplicationArgumentParser::ParseError; |
238 | errorMessage(message); |
239 | return errorMessage(message: tr(sourceText: "Pass -help for information about the command line." )); |
240 | } |
241 | |
242 | /*! |
243 | Returns always \c false. |
244 | */ |
245 | bool QApplicationArgumentParserPrivate::errorMessage(const QString &message) |
246 | { |
247 | QTextStream out(stderr, QIODevice::WriteOnly); |
248 | out << message << Qt::endl; |
249 | return false; |
250 | } |
251 | |
252 | /*! |
253 | \internal |
254 | Determines whether \a arg carries a value or is on/off. |
255 | */ |
256 | bool QApplicationArgumentParserPrivate::isSwitch(const QApplicationArgument &arg) |
257 | { |
258 | return arg.type() == QVariant::Invalid; |
259 | } |
260 | |
261 | QVariant QApplicationArgumentParserPrivate::conversionError(const QString &typeName, |
262 | const QString &input) |
263 | { |
264 | errorMessage(message: tr(sourceText: "Cannot convert %1 to type %2." ).arg(a1: input, a2: typeName)); |
265 | return QVariant(); |
266 | } |
267 | |
268 | bool QApplicationArgumentParserPrivate::isBuiltinVariant(const int type) |
269 | { |
270 | return type < int(QVariant::UserType); |
271 | } |
272 | |
273 | /*! |
274 | TODO Temporary, replace with a function in QCoreApplication. |
275 | */ |
276 | QStringList QApplicationArgumentParserPrivate::argumentsFromLocal(const int argc, const char *const *const argv) |
277 | { |
278 | Q_ASSERT(argc >= 1); |
279 | Q_ASSERT(argv); |
280 | QStringList result; |
281 | |
282 | for(int i = 0; i < argc; ++i) |
283 | result.append(t: QString::fromLocal8Bit(str: argv[i])); |
284 | |
285 | return result; |
286 | } |
287 | |
288 | void QApplicationArgumentParserPrivate::displayVersion() const |
289 | { |
290 | QTextStream out(stderr); |
291 | |
292 | out << tr(sourceText: "%1 version %2 using Qt %3" ).arg(args: QCoreApplication::applicationName(), args: applicationVersion, args: QString::fromLatin1(str: qVersion())) |
293 | << Qt::endl; |
294 | } |
295 | |
296 | void QApplicationArgumentParserPrivate::displayHelp() const |
297 | { |
298 | enum Constants |
299 | { |
300 | /** |
301 | * When we want to line wrap, 80 minus a couple of characters. This should |
302 | * be suitable for vt100 compatible terminals. |
303 | */ |
304 | LineWrapAt = 78, |
305 | |
306 | /** |
307 | * The initial " -" for each option. |
308 | */ |
309 | IndentPadding = 3, |
310 | |
311 | /** |
312 | * Pad for the brackets and space we use when we have a type. |
313 | */ |
314 | ValueArgumentPadding = 4 |
315 | }; |
316 | |
317 | QList<QApplicationArgument> args(declaredArguments.values()); |
318 | args += builtinArguments(); |
319 | |
320 | /* Sort them, such that we get the nameless options at the end, and it |
321 | * generally looks tidy. */ |
322 | std::sort(first: args.begin(), last: args.end()); |
323 | |
324 | /* This is the basic approach: |
325 | * Switches: |
326 | * -name description |
327 | * Value arguments: |
328 | * -name <name-of-value-type> description |
329 | * |
330 | * Nameless arguments |
331 | * name <type> description |
332 | * |
333 | * It all line-wraps at OutputWidth and the description is indented, |
334 | * where the highest indent is the length of the name plus length of the name |
335 | * of the type. */ |
336 | |
337 | /* First we find the name with the largest width. */ |
338 | int maxWidth = 0; |
339 | |
340 | QList<QApplicationArgument> nameless(declaredNamelessArguments); |
341 | std::sort(first: nameless.begin(), last: nameless.end()); |
342 | |
343 | /* Note, here the nameless arguments appear last, but are sorted |
344 | * with themselves. */ |
345 | QList<QApplicationArgument> allArgs(args + nameless); |
346 | const int allArgsCount = allArgs.count(); |
347 | |
348 | for(int i = 0; i < allArgsCount; ++i) |
349 | { |
350 | const QApplicationArgument &at = allArgs.at(i); |
351 | const int nameLength = at.name().length(); |
352 | const QString typeName(q_ptr->typeToName(argument: at)); |
353 | const int typeNameLength = typeName.length(); |
354 | const int padding = at.type() == QVariant::Invalid ? 0 : ValueArgumentPadding; |
355 | maxWidth = qMax(a: maxWidth, b: nameLength + typeNameLength + padding); |
356 | } |
357 | |
358 | QTextStream out(stderr); |
359 | out << Qt::endl |
360 | << QString(IndentPadding, QLatin1Char(' ')) |
361 | << QCoreApplication::applicationName() |
362 | << QLatin1String(" -- " ) |
363 | << applicationDescription |
364 | << Qt::endl; |
365 | // TODO synopsis |
366 | |
367 | /* One extra so we get some space between the overview and the options. */ |
368 | out << Qt::endl; |
369 | |
370 | const int indentWidth = maxWidth + 3; |
371 | |
372 | /* Ok, print them out. */ |
373 | for(int i = 0; i < allArgsCount; ++i) |
374 | { |
375 | const QApplicationArgument &at = allArgs.at(i); |
376 | /* " -name ". Indent a bit first, inspired by Qt's moc. */ |
377 | const QString &name = at.name(); |
378 | QString prolog(QLatin1String(" " )); |
379 | |
380 | /* We have a special case for the single dash. */ |
381 | if(name == QChar::fromLatin1(c: '-')) |
382 | prolog.append(s: name); |
383 | else |
384 | { |
385 | if(!at.isNameless()) |
386 | prolog.append(c: QLatin1Char('-')); |
387 | |
388 | prolog.append(s: name + QLatin1Char(' ')); |
389 | } |
390 | |
391 | if(at.type() != QVariant::Invalid) |
392 | { |
393 | /* It's not a switch, it has a value. */ |
394 | |
395 | /* Do we have a default value? If so, the argument is optional. */ |
396 | const QString typeName(q_ptr->typeToName(argument: at)); |
397 | |
398 | if(at.defaultValue().isValid()) |
399 | prolog.append(s: QLatin1Char('[') + typeName + QLatin1Char(']')); |
400 | else |
401 | prolog.append(s: QLatin1Char('<') + typeName + QLatin1Char('>')); |
402 | // TODO Don't we want to display the default value? |
403 | |
404 | prolog.append(c: QLatin1Char(' ')); |
405 | } |
406 | |
407 | prolog = prolog.leftJustified(width: indentWidth); |
408 | |
409 | out << prolog |
410 | << lineWrap(input: at.description(), leftIndent: indentWidth, width: LineWrapAt) |
411 | << Qt::endl; |
412 | } |
413 | } |
414 | |
415 | /*! |
416 | Line wraps \a input and indents each line with \a leftIndent spaces, such that |
417 | the width does not go beyond \a maxWidth. |
418 | |
419 | The addition of line endings is accounted for by the caller. |
420 | |
421 | With QTextBoundaryFinder our line wrapping is relatively fancy, since it |
422 | does it the Unicode-way. |
423 | */ |
424 | QString QApplicationArgumentParserPrivate::lineWrap(const QString &input, |
425 | const int leftIndent, |
426 | const int maxWidth) |
427 | { |
428 | const QString indent(QString(leftIndent, QLatin1Char(' '))); |
429 | const int len = input.length(); |
430 | const int textWidth = maxWidth - leftIndent; |
431 | |
432 | QString output; |
433 | QTextBoundaryFinder wrapFinder(QTextBoundaryFinder::Line, input); |
434 | wrapFinder.setPosition(textWidth); |
435 | |
436 | if(input.length() + leftIndent <= maxWidth) |
437 | return input; |
438 | |
439 | int from = wrapFinder.toPreviousBoundary(); |
440 | output.append(s: input.leftRef(n: from)); |
441 | |
442 | while(true) |
443 | { |
444 | if((len - from) + leftIndent > maxWidth) |
445 | { |
446 | /* We need to line wrap. */ |
447 | wrapFinder.setPosition(from + textWidth); |
448 | const int currentWidthPos = wrapFinder.toPreviousBoundary(); |
449 | |
450 | output.append(c: QLatin1Char('\n')); |
451 | output.append(s: indent); |
452 | output.append(s: input.midRef(position: from, n: currentWidthPos - from).trimmed().toString()); |
453 | from += (currentWidthPos - from); |
454 | } |
455 | else |
456 | { |
457 | /* Append the remains. */ |
458 | output.append(c: QLatin1Char('\n')); |
459 | output.append(s: indent); |
460 | output.append(s: input.midRef(position: from).trimmed().toString()); |
461 | break; |
462 | } |
463 | } |
464 | |
465 | return output; |
466 | } |
467 | |
468 | /*! |
469 | Returns a list with the builtin options that the parser has |
470 | */ |
471 | QList<QApplicationArgument> QApplicationArgumentParserPrivate::builtinArguments() |
472 | { |
473 | QList<QApplicationArgument> result; |
474 | |
475 | result.append(t: QApplicationArgument(QLatin1String("help" ), |
476 | QLatin1String("Displays this help." ))); |
477 | result.append(t: QApplicationArgument(QLatin1String("version" ), |
478 | QLatin1String("Displays version information." ))); |
479 | |
480 | result.append(t: QApplicationArgument(QLatin1String("-" ), |
481 | QLatin1String("When appearing, any following options are not interpreted as switches." ))); |
482 | return result; |
483 | } |
484 | |
485 | /* TODO, I don't think we want this function in a public API. Add it first when there is a demand. */ |
486 | |
487 | /*! |
488 | Creates a QApplicationArgumentParser that will parse the input in \a argc and \a argv. |
489 | These arguments should be passed directly from the \c main() function, and the decoding |
490 | of the input will be taken care of appropriately, depending on platform. |
491 | |
492 | It is preferred to use the QStringList overload, in case the input is in the form of QStrings. |
493 | */ |
494 | QApplicationArgumentParser::QApplicationArgumentParser(int argc, char **argv) : d(new QApplicationArgumentParserPrivate(this, QApplicationArgumentParserPrivate::argumentsFromLocal(argc, argv))) |
495 | { |
496 | Q_ASSERT_X(argv, Q_FUNC_INFO, "Argv cannot be null." ); |
497 | Q_ASSERT_X(argc >= 1, Q_FUNC_INFO, |
498 | "argc must at least contain the application name. " |
499 | "Use the QStringList overload instead." ); |
500 | } |
501 | |
502 | /*! |
503 | \overload |
504 | |
505 | Creates a QApplicationArgumentParser that will parse \a input. That is, instead of passing in \c argc |
506 | and \c argv, one can pass in a QStringList. |
507 | |
508 | The caller guarantees that the first string in \a input is the name of the application. |
509 | */ |
510 | QApplicationArgumentParser::QApplicationArgumentParser(const QStringList &input) : d(new QApplicationArgumentParserPrivate(this, input)) |
511 | { |
512 | Q_ASSERT_X(input.count() >= 1, Q_FUNC_INFO, |
513 | "The input must at least contain the application name." ); |
514 | } |
515 | |
516 | /*! |
517 | This function is only of interest when subclassing. |
518 | |
519 | Returns the strings that the user specified when starting the application. The first string |
520 | in the list is always the application name. |
521 | */ |
522 | QStringList QApplicationArgumentParser::input() const |
523 | { |
524 | Q_ASSERT_X(d->input.count() >= 1, Q_FUNC_INFO, "Internal error, this should always hold true" ); |
525 | return d->input; |
526 | } |
527 | |
528 | /*! |
529 | This function is only of interest when subclassing. |
530 | |
531 | Sets the arguments that the user actually used on the command line to \a arguments. |
532 | The parse() function should call this, such that the result afterwards can be inspected |
533 | with for instance has() or count(). |
534 | |
535 | \sa usedArguments() |
536 | */ |
537 | void QApplicationArgumentParser::setUsedArguments(const QList<QPair<QApplicationArgument, QVariant> > &arguments) |
538 | { |
539 | d->usedArguments = arguments; |
540 | } |
541 | |
542 | /*! |
543 | This function is only of interest when subclassing. |
544 | |
545 | Returns the arguments that the user used on the command line. |
546 | |
547 | \sa setUsedArguments() |
548 | */ |
549 | QList<QPair<QApplicationArgument, QVariant> > QApplicationArgumentParser::usedArguments() const |
550 | { |
551 | return d->usedArguments; |
552 | } |
553 | |
554 | /*! |
555 | Destructs this QApplicationArgumentParser instance. |
556 | */ |
557 | QApplicationArgumentParser::~QApplicationArgumentParser() |
558 | { |
559 | delete d; |
560 | } |
561 | |
562 | /*! |
563 | Adds \a argument to this parser. |
564 | |
565 | This function is provided for convenience. It is equivalent to creating a QList |
566 | containing \a argument, append the existing arguments, and then call setDeclaredArguments() with the list. |
567 | |
568 | \sa setDeclaredArguments() |
569 | */ |
570 | void QApplicationArgumentParser::addArgument(const QApplicationArgument &argument) |
571 | { |
572 | if(argument.isNameless()) |
573 | d->declaredNamelessArguments.append(t: argument); |
574 | else |
575 | d->declaredArguments.insert(akey: argument.name(), avalue: argument); |
576 | } |
577 | |
578 | /*! |
579 | Makes the parser recognize all arguments in \a arguments. |
580 | |
581 | Any arguments previously set, are discarded. |
582 | |
583 | \sa addArgument(), declaredArguments() |
584 | */ |
585 | void QApplicationArgumentParser::setDeclaredArguments(const QList<QApplicationArgument> &arguments) |
586 | { |
587 | // TODO If we have a QHash internally, why not use it in the public API too? |
588 | const int len = arguments.count(); |
589 | |
590 | for(int i = 0; i < len; ++i) |
591 | d->declaredArguments.insert(akey: arguments.at(i).name(), avalue: arguments.at(i)); |
592 | } |
593 | |
594 | /*! |
595 | Returns the arguments that this parser recognizes. |
596 | |
597 | \sa addArgument(), setDeclaredArguments() |
598 | */ |
599 | QList<QApplicationArgument> QApplicationArgumentParser::declaredArguments() const |
600 | { |
601 | return d->declaredArguments.values(); |
602 | } |
603 | |
604 | bool QApplicationArgumentParserPrivate::parseNamelessArguments(const QString &in) |
605 | { |
606 | /* It's a nameless options, such as simply "value". */ |
607 | const QApplicationArgument nameless(nextNamelessArgument()); |
608 | |
609 | const QVariant val(q_ptr->convertToValue(argument: nameless, value: in)); |
610 | if(val.isValid()) |
611 | { |
612 | usedArguments.append(t: qMakePair(x: nameless, y: val)); |
613 | return true; |
614 | } |
615 | else |
616 | return false; // TODO error msg? |
617 | } |
618 | |
619 | /*! |
620 | Parses input() together with declaredArguments() and returns \c false if the caller |
621 | should exit immediately, which is the case of which an error was encountered or |
622 | help or the version was requested. |
623 | |
624 | In the case of \c true was returned, valid arguments were supplied, and they can |
625 | be requested with functions like value(), values(), count() and has(). |
626 | |
627 | parse() must only be called once per QApplicationArgumentParser instance. The |
628 | second time it's called, the effects and return value are undefined. |
629 | |
630 | \sa convertToValue(), typeToName() |
631 | */ |
632 | bool QApplicationArgumentParser::parse() |
633 | { |
634 | const QChar sep(QLatin1Char('-')); |
635 | const int inputCount = d->input.count(); |
636 | |
637 | /* We skip the first entry, which is the application name. */ |
638 | int i = 1; |
639 | |
640 | for(; i < inputCount; ++i) |
641 | { |
642 | const QString &in = d->input.at(i); |
643 | |
644 | /* We have a single '-', signalling that the succeeding are not options. */ |
645 | if(in == sep) |
646 | { |
647 | ++i; |
648 | |
649 | for(; i < inputCount; ++i) |
650 | { |
651 | if(!d->parseNamelessArguments(in: d->input.at(i))) |
652 | return false; |
653 | /* Process nameless options. Have code for this elsewhere, factor it out. */ |
654 | } |
655 | |
656 | break; |
657 | } |
658 | |
659 | if(in.startsWith(c: sep)) /* It is "-name". */ |
660 | { |
661 | const QString name(in.mid(position: 1)); |
662 | |
663 | if(name == QLatin1String("help" )) |
664 | { |
665 | setExitCode(Success); |
666 | d->displayHelp(); |
667 | return false; |
668 | } |
669 | else if(name == QLatin1String("version" )) |
670 | { |
671 | setExitCode(Success); |
672 | d->displayVersion(); |
673 | return false; |
674 | } |
675 | |
676 | if(!d->declaredArguments.contains(akey: name)) |
677 | return d->error(message: QApplicationArgumentParserPrivate::tr(sourceText: "\"%1\" is an unknown argument." ).arg(a: name)); |
678 | |
679 | const QApplicationArgument &arg = d->declaredArguments.value(akey: name); |
680 | const int argCount = d->count(arg) + 1; |
681 | const int max = arg.maximumOccurrence(); |
682 | |
683 | if(argCount > max && max != -1) |
684 | { |
685 | /* Let's tailor the message for a common case. */ |
686 | if(max == 1) |
687 | return d->error(message: QApplicationArgumentParserPrivate::tr(sourceText: "\"%1\" can only be used once." ).arg(a: name)); |
688 | else |
689 | return d->error(message: QApplicationArgumentParserPrivate::tr(sourceText: "\"%1\" can only be used %2 times." ).arg(args: name, args: QString::number(max))); |
690 | } |
691 | |
692 | if(QApplicationArgumentParserPrivate::isSwitch(arg)) |
693 | { |
694 | d->usedArguments.append(t: qMakePair(x: arg, y: QVariant())); |
695 | continue; |
696 | } |
697 | else |
698 | { |
699 | ++i; |
700 | |
701 | if(i == inputCount) |
702 | return d->error(message: QApplicationArgumentParserPrivate::tr(sourceText: "\"%1\" must be followed by a value." ).arg(a: name)); |
703 | |
704 | /* Okidoki, got a value, always something. Let's |
705 | * see if it validates. */ |
706 | const QString &value = d->input.at(i); |
707 | |
708 | const QVariant val(convertToValue(argument: arg, value)); |
709 | if(val.isValid()) |
710 | { |
711 | d->usedArguments.append(t: qMakePair(x: arg, y: val)); |
712 | continue; |
713 | } |
714 | else |
715 | return false; // TODO error msg? |
716 | } |
717 | } |
718 | else |
719 | { |
720 | if(!d->parseNamelessArguments(in)) |
721 | return false; |
722 | } |
723 | } |
724 | |
725 | /* Check that all arguments that have been declared as mandatory, are actually |
726 | * specified. */ |
727 | const QList<QApplicationArgument> declaredArguments(d->declaredArguments.values() + d->declaredNamelessArguments); |
728 | const int len = declaredArguments.count(); |
729 | for(int i = 0; i < len; ++i) |
730 | { |
731 | const QApplicationArgument &at = declaredArguments.at(i); |
732 | const int min = at.minimumOccurrence(); |
733 | const int max = at.maximumOccurrence(); // TODO What about infinite? -1 |
734 | if(min == 0) |
735 | continue; |
736 | else |
737 | { |
738 | const int usedLen = d->usedArguments.count(); |
739 | int useCount = 0; |
740 | |
741 | for(int u = 0; u < usedLen; ++u) |
742 | { |
743 | const QPair<QApplicationArgument, QVariant> &used = d->usedArguments.at(i: u); |
744 | if(used.first == at) |
745 | ++useCount; |
746 | } |
747 | |
748 | const QString originalName(at.name()); |
749 | const QString effectiveName(originalName.isEmpty() ? QLatin1Char('<') + typeToName(argument: at) + QLatin1Char('>') : originalName); |
750 | |
751 | if(useCount < min) |
752 | { |
753 | /* For nameless options, we use the type as the name. Looks better. */ |
754 | return d->error(message: QApplicationArgumentParserPrivate::tr(sourceText: "%1 must occur at least %2 times, therefore %3 times is insufficient." , disambiguation: "The number is for %2." , n: min) |
755 | .arg(args: effectiveName, args: QString::number(min), args: QString::number(useCount))); |
756 | } |
757 | else if(useCount > max) |
758 | return d->error(message: QApplicationArgumentParserPrivate::tr(sourceText: "%1 can occur at most %2 times" , disambiguation: "" , n: max).arg(args: effectiveName, args: QString::number(max))); |
759 | } |
760 | } |
761 | |
762 | d->exitCode = Success; |
763 | return true; |
764 | } |
765 | |
766 | /*! |
767 | This function is only of interest when subclassing. |
768 | |
769 | parse() calls this function each time a value, that is \a input, on the command line needs to be |
770 | validated and subsequently converted to the type of \a argument. A descriptive error message will |
771 | be outputted if \a input cannot be converted to the required type. |
772 | |
773 | The default implementation uses QVariant::canConvert() and QVariant::convert() for doing conversions. |
774 | |
775 | QApplicationArgumentParser can be subclassed and this function subsequently overridden, to handle custom types. |
776 | |
777 | If \a input isn't valid input for \a argument, this function returns a default constructed |
778 | QVariant. |
779 | |
780 | \sa typeToName(), parse() |
781 | */ |
782 | QVariant QApplicationArgumentParser::convertToValue(const QApplicationArgument &argument, |
783 | const QString &input) const |
784 | { |
785 | const int type = argument.type(); |
786 | |
787 | switch(type) |
788 | { |
789 | case QVariant::Bool: |
790 | { |
791 | if(input == QLatin1String("true" ) || input == QChar::fromLatin1(c: '1')) |
792 | return QVariant(true); |
793 | else if(input == QLatin1String("false" ) || input == QChar::fromLatin1(c: '0')) |
794 | return QVariant(false); |
795 | else |
796 | return QApplicationArgumentParserPrivate::conversionError(typeName: typeToName(argument), input); |
797 | } |
798 | case QVariant::RegExp: |
799 | { |
800 | QRegExp exp(input); |
801 | |
802 | if(exp.isValid()) |
803 | return QVariant(exp); |
804 | else |
805 | return QApplicationArgumentParserPrivate::conversionError(typeName: typeToName(argument), input); |
806 | } |
807 | case QVariant::Url: |
808 | { |
809 | const QUrl result(input); |
810 | |
811 | if(result.isValid()) |
812 | return QVariant(result); |
813 | else |
814 | return QApplicationArgumentParserPrivate::conversionError(typeName: typeToName(argument), input); |
815 | } |
816 | default: |
817 | { |
818 | QVariant result(input); |
819 | |
820 | if(QApplicationArgumentParserPrivate::isBuiltinVariant(type) && |
821 | result.convert(targetTypeId: type)) |
822 | return result; |
823 | else |
824 | return QApplicationArgumentParserPrivate::conversionError(typeName: typeToName(argument), input); |
825 | } |
826 | } |
827 | } |
828 | |
829 | /*! |
830 | This function is only of interest when subclassing. |
831 | |
832 | convertToValue() calls this function when requiring a string for referring to \a type, |
833 | when generating user messages. |
834 | |
835 | The implementation uses QMetaType::typeName() for most types, but special handles |
836 | some types, in order to let the message be better tailored for humans. |
837 | |
838 | \sa convertToValue() |
839 | */ |
840 | QString QApplicationArgumentParser::typeToName(const QApplicationArgument &argument) const |
841 | { |
842 | /* Personally I think nameForType() would be a better name but this is consistent |
843 | * with QVariant's function of the same name. */ |
844 | const int type = argument.type(); |
845 | |
846 | switch(type) |
847 | { |
848 | case QVariant::RegExp: |
849 | return QApplicationArgumentParserPrivate::tr(sourceText: "regular expression" ); |
850 | case QVariant::Url: |
851 | return QLatin1String("URI" ); |
852 | case QVariant::String: |
853 | return QLatin1String("string" ); |
854 | default: |
855 | { |
856 | return QString::fromLatin1(str: QMetaType::typeName(type)); |
857 | } |
858 | } |
859 | } |
860 | |
861 | /*! |
862 | Returns the default value for \a argument. The default implementation returns |
863 | QApplicationArgument::defaultValue(), if \a argument has been added to this parser. |
864 | |
865 | Overriding this function can be useful if creating the default value is resource |
866 | consuming, such as opening a file. |
867 | */ |
868 | QVariant QApplicationArgumentParser::defaultValue(const QApplicationArgument &argument) const |
869 | { |
870 | return d->declaredArguments.value(akey: argument.name()).defaultValue(); |
871 | } |
872 | |
873 | /*! |
874 | Returns the count of how many times \a argument was used on the command line. |
875 | |
876 | \sa has() |
877 | */ |
878 | int QApplicationArgumentParser::count(const QApplicationArgument &argument) const |
879 | { |
880 | Q_ASSERT_X(d->declaredArguments.contains(argument.name()) || |
881 | d->declaredNamelessArguments.contains(argument), Q_FUNC_INFO, |
882 | "The argument isn't known to the parser. Has addArgument() been called?" ); |
883 | return d->count(arg: argument); |
884 | } |
885 | |
886 | /*! |
887 | Returns \c true if \a argument has been |
888 | specified one or more times on the command line, otherwise \a false. |
889 | |
890 | \sa count() |
891 | */ |
892 | bool QApplicationArgumentParser::has(const QApplicationArgument &argument) const |
893 | { |
894 | Q_ASSERT_X(d->declaredArguments.contains(argument.name()) || |
895 | d->declaredNamelessArguments.contains(argument), Q_FUNC_INFO, |
896 | "The argument isn't known to the parser. Has addArgument() been called?" ); |
897 | return d->contains(arg: argument); |
898 | } |
899 | |
900 | /*! |
901 | // TODO docs |
902 | |
903 | \sa values() |
904 | */ |
905 | QVariant QApplicationArgumentParser::value(const QApplicationArgument &argument) const |
906 | { |
907 | Q_ASSERT_X(d->declaredArguments.contains(argument.name()) || |
908 | d->declaredNamelessArguments.contains(argument), Q_FUNC_INFO, |
909 | "The argument isn't known to the parser. Has addArgument() been called?" ); |
910 | |
911 | const int len = d->usedArguments.count(); |
912 | |
913 | for(int i = 0; i < len; ++i) |
914 | { |
915 | if(d->usedArguments.at(i).first == argument) |
916 | return d->usedArguments.at(i).second; |
917 | } |
918 | |
919 | return defaultValue(argument); |
920 | } |
921 | |
922 | /*! |
923 | // TODO docs |
924 | \sa value() |
925 | */ |
926 | QVariantList QApplicationArgumentParser::values(const QApplicationArgument &argument) const |
927 | { |
928 | Q_ASSERT_X(d->declaredArguments.contains(argument.name()) || |
929 | d->declaredNamelessArguments.contains(argument), |
930 | Q_FUNC_INFO, |
931 | "The argument isn't known to the parser. Has addArgument() been called?" ); |
932 | |
933 | const int len = d->usedArguments.count(); |
934 | |
935 | QVariantList result; |
936 | for(int i = 0; i < len; ++i) |
937 | { |
938 | if(d->usedArguments.at(i).first == argument) |
939 | result.append(t: d->usedArguments.at(i).second); |
940 | } |
941 | |
942 | // TODO how do we handle default values? |
943 | return result; |
944 | } |
945 | |
946 | /*! |
947 | After parse() has been called, this function returns a code that can be used to |
948 | exit \c main() with. It returns zero upon success or if help was requested, and |
949 | otherwise a value signalling failure. |
950 | */ |
951 | QApplicationArgumentParser::ExitCode QApplicationArgumentParser::exitCode() const |
952 | { |
953 | return d->exitCode; |
954 | } |
955 | |
956 | /*! |
957 | This function is only of interest when subclassing. |
958 | |
959 | Makes exitCode() return \a code. |
960 | */ |
961 | void QApplicationArgumentParser::setExitCode(ExitCode code) |
962 | { |
963 | d->exitCode = code; |
964 | } |
965 | |
966 | /*! |
967 | Sets the application description to \a description. |
968 | |
969 | The application description is a sentence or two used for help and version |
970 | messages, that briefly describes the application. |
971 | |
972 | The default is the empty string. |
973 | */ |
974 | void QApplicationArgumentParser::setApplicationDescription(const QString &description) |
975 | { |
976 | d->applicationDescription = description; |
977 | } |
978 | |
979 | /*! |
980 | Sets the application version to \a version. |
981 | |
982 | This string, which is arbitrary but typically is "1.0" or so, is used when |
983 | generating a version statement. |
984 | */ |
985 | void QApplicationArgumentParser::setApplicationVersion(const QString &version) |
986 | { |
987 | d->applicationVersion = version; |
988 | } |
989 | |
990 | /*! |
991 | Writes out \a message to \c stderr. |
992 | */ |
993 | void QApplicationArgumentParser::message(const QString &message) const |
994 | { |
995 | d->errorMessage(message); |
996 | } |
997 | |
998 | QT_END_NAMESPACE |
999 | |