2 * This file is part of the vng project
3 * Copyright (C) 2008 Thomas Zander <tzander@trolltech.com>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "CommandLineParser.h"
22 #include <QTextStream>
23 #include <QStringList>
27 CommandLineParser
*CommandLineParser::self
= 0;
29 class CommandLineParser::Private
32 Private(int argc
, char **argv
);
35 void addDefinitions(const CommandLineOption
* options
);
36 void setArgumentDefinition(const char *definition
);
40 const int argumentCount
;
41 char ** const argumentStrings
;
43 int requiredArguments
;
44 QString argumentDefinition
;
46 struct OptionDefinition
{
47 OptionDefinition() : optionalParameters(0), requiredParameters(0) { }
51 int optionalParameters
;
52 int requiredParameters
;
55 // result of what the user typed
58 QList
<QString
> parameters
;
61 QList
<OptionDefinition
> definitions
;
62 QHash
<QString
, ParsedOption
> options
;
63 QList
<QString
> arguments
;
64 QList
<QString
> undefinedOptions
;
65 QList
<QString
> errors
;
69 CommandLineParser::Private::Private(int argc
, char **argv
)
70 : argumentCount(argc
), argumentStrings(argv
), dirty(true),
75 void CommandLineParser::Private::addDefinitions(const CommandLineOption
* options
)
77 for (int i
=0; options
[i
].specification
!= 0; i
++) {
78 OptionDefinition definition
;
79 QString option
= QString::fromLatin1(options
[i
].specification
);
80 if (option
.indexOf(QLatin1Char(',')) >= 0) {
81 QStringList optionParts
= option
.split(QLatin1Char(','), QString::SkipEmptyParts
);
82 if (optionParts
.count() != 2) {
83 qWarning() << "option definition '" << option
<< "' is faulty; only one ',' allowed";
86 foreach(QString s
, optionParts
) {
88 if (s
.startsWith(QLatin1String("--")) && s
.length() > 2)
89 definition
.name
= s
.mid(2);
90 else if (s
.startsWith(QLatin1String("-")) && s
.length() > 1)
91 definition
.shortName
= s
.at(1);
93 qWarning() << "option definition '" << option
<< "' is faulty; the option should start with a -";
98 else if (option
.startsWith(QLatin1String("--")) && option
.length() > 2)
99 definition
.name
= option
.mid(2);
101 qWarning() << "option definition '" << option
<< "' has unrecognized format. See the api docs for CommandLineParser for a howto";
103 if(definition
.name
.isEmpty())
105 if (option
.indexOf(QLatin1Char(' ')) > 0) {
106 QStringList optionParts
= definition
.name
.split(QLatin1Char(' '), QString::SkipEmptyParts
);
107 definition
.name
= optionParts
[0];
109 foreach(QString s
, optionParts
) {
115 if (s
[0].unicode() == '[' && s
.endsWith(QLatin1Char(']')))
116 definition
.optionalParameters
++;
118 definition
.requiredParameters
++;
122 definition
.comment
= QString::fromLatin1(options
[i
].description
);
123 definitions
<< definition
;
126 foreach (OptionDefinition def, definitions) {
127 qDebug() << "definition:" << (def.shortName != 0 ? def.shortName : QChar(32)) << "|" << def.name << "|" << def.comment
128 << "|" << def.requiredParameters << "+" << def.optionalParameters;
135 void CommandLineParser::Private::setArgumentDefinition(const char *defs
)
137 requiredArguments
= 0;
138 argumentDefinition
= QString::fromLatin1(defs
);
139 QStringList optionParts
= argumentDefinition
.split(QLatin1Char(' '), QString::SkipEmptyParts
);
141 foreach(QString s
, optionParts
) {
143 if (s
[0].unicode() == '<') {
147 else if (s
[0].unicode() == '[')
149 if (s
.endsWith(QLatin1Char('>')))
156 void CommandLineParser::Private::parse()
163 undefinedOptions
.clear();
166 class OptionProcessor
{
168 OptionProcessor(Private
*d
) : clp(d
) { }
170 void next(Private::ParsedOption
&option
) {
171 if (! option
.option
.isEmpty()) {
172 // find the definition to match.
173 OptionDefinition def
;
174 foreach(Private::OptionDefinition definition
, clp
->definitions
) {
175 if (definition
.name
== option
.option
) {
180 if (! def
.name
.isEmpty() && def
.requiredParameters
>= option
.parameters
.count() &&
181 def
.requiredParameters
+ def
.optionalParameters
<= option
.parameters
.count())
182 clp
->options
.insert(option
.option
, option
);
183 else if (!clp
->undefinedOptions
.contains(option
.option
))
184 clp
->undefinedOptions
<< option
.option
;
186 clp
->errors
.append(QLatin1String("Not enough arguments passed for option `")
187 + option
.option
+QLatin1Char('\''));
189 option
.option
.clear();
190 option
.parameters
.clear();
194 CommandLineParser::Private
*clp
;
196 OptionProcessor
processor(this);
198 bool optionsAllowed
= true;
200 OptionDefinition currentDefinition
;
201 for(int i
= 1; i
< argumentCount
; i
++) {
202 QString arg
= QString::fromLocal8Bit(argumentStrings
[i
]);
203 if (optionsAllowed
) {
204 if (arg
== QLatin1String("--")) {
205 optionsAllowed
= false;
208 if (arg
.startsWith(QLatin1String("--"))) {
209 processor
.next(option
);
210 int end
= arg
.indexOf(QLatin1Char('='));
211 option
.option
= arg
.mid(2, end
- 2);
213 option
.parameters
<< arg
.mid(end
+1);
216 if (arg
[0].unicode() == '-' && arg
.length() > 1) {
217 for(int x
= 1; x
< arg
.length(); x
++) {
218 foreach(OptionDefinition definition
, definitions
) {
219 if (definition
.shortName
== arg
[x
]) {
220 processor
.next(option
);
221 currentDefinition
= definition
;
222 option
.option
= definition
.name
;
224 if (definition
.requiredParameters
== 1 && arg
.length() >= x
+2) {
225 option
.parameters
<< arg
.mid(x
+1, arg
.length());
231 if (option
.option
.isEmpty()) { // nothing found; copy char so it ends up in unrecognized
232 option
.option
= arg
[x
];
233 processor
.next(option
);
239 if (! option
.option
.isEmpty()) {
240 if (currentDefinition
.name
!= option
.option
) {
241 // find the definition to match.
242 foreach(OptionDefinition definition
, definitions
) {
243 if (definition
.name
== option
.option
) {
244 currentDefinition
= definition
;
249 if (currentDefinition
.requiredParameters
+ currentDefinition
.optionalParameters
<= option
.parameters
.count())
250 processor
.next(option
);
252 if (option
.option
.isEmpty())
255 option
.parameters
<< arg
;
257 processor
.next(option
);
259 if (requiredArguments
> arguments
.count())
260 errors
.append(QLatin1String("Not enough arguments, usage: ") + QString::fromLocal8Bit(argumentStrings
[0])
261 + QLatin1Char(' ') + argumentDefinition
);
264 foreach(QString key, options.keys()) {
265 ParsedOption p = options[key];
266 qDebug() << "-> " << p.option;
267 foreach(QString v, p.parameters)
268 qDebug() << " +" << v;
271 foreach(QString arg, arguments) {
277 // -----------------------------------
281 void CommandLineParser::init(int argc
, char **argv
)
285 self
= new CommandLineParser(argc
, argv
);
289 void CommandLineParser::addOptionDefinitions(const CommandLineOption
* optionList
)
292 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
295 self
->d
->addDefinitions(optionList
);
299 CommandLineParser
*CommandLineParser::instance()
305 void CommandLineParser::setArgumentDefinition(const char *definition
)
308 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
311 self
->d
->setArgumentDefinition(definition
);
315 CommandLineParser::CommandLineParser(int argc
, char **argv
)
316 : d(new Private(argc
, argv
))
320 CommandLineParser::~CommandLineParser()
325 void CommandLineParser::usage(const QString
&name
, const QString
&argumentDescription
)
327 QTextStream
cout(stdout
, QIODevice::WriteOnly
);
328 cout
<< "Usage: " << d
->argumentStrings
[0];
329 if (! name
.isEmpty())
331 if (d
->definitions
.count())
333 if (! argumentDescription
.isEmpty())
334 cout
<< " " << argumentDescription
;
335 cout
<< endl
<< endl
;
337 if (d
->definitions
.count() > 0)
338 cout
<< "Options:" << endl
;
339 int commandLength
= 0;
340 foreach (Private::OptionDefinition definition
, d
->definitions
)
341 commandLength
= qMax(definition
.name
.length(), commandLength
);
343 foreach (Private::OptionDefinition definition
, d
->definitions
) {
345 if (definition
.shortName
== 0)
348 cout
<< "-" << definition
.shortName
<< " --";
349 cout
<< definition
.name
;
350 for (int i
= definition
.name
.length(); i
<= commandLength
; i
++)
352 cout
<< definition
.comment
<<endl
;
356 QStringList
CommandLineParser::options() const
359 return d
->options
.keys();
362 bool CommandLineParser::contains(const QString
& key
) const
365 return d
->options
.contains(key
);
368 QStringList
CommandLineParser::arguments() const
374 QStringList
CommandLineParser::undefinedOptions() const
377 return d
->undefinedOptions
;
380 QString
CommandLineParser::optionArgument(const QString
&optionName
, const QString
&defaultValue
) const
382 QStringList answer
= optionArguments(optionName
);
383 if (answer
.isEmpty())
385 return answer
.first();
388 QStringList
CommandLineParser::optionArguments(const QString
&optionName
) const
390 if (! contains(optionName
))
391 return QStringList();
392 Private::ParsedOption po
= d
->options
[optionName
];
393 return po
.parameters
;
396 QStringList
CommandLineParser::parseErrors() const