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
= options
[i
].specification
;
80 if (option
.indexOf(',') >= 0) {
81 QStringList optionParts
= option
.split(QChar(','), 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("--") && s
.length() > 2)
89 definition
.name
= s
.mid(2);
90 else if (s
.startsWith("-") && 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("--") && 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(' ') > 0) {
106 QStringList optionParts
= definition
.name
.split(QChar(' '), QString::SkipEmptyParts
);
107 definition
.name
= optionParts
[0];
109 foreach(QString s
, optionParts
) {
115 if (s
[0] == '[' && s
.endsWith(']'))
116 definition
.optionalParameters
++;
118 definition
.requiredParameters
++;
122 definition
.comment
= 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(defs
);
139 QStringList optionParts
= argumentDefinition
.split(QChar(' '), QString::SkipEmptyParts
);
141 foreach(QString s
, optionParts
) {
154 void CommandLineParser::Private::parse()
161 undefinedOptions
.clear();
164 class OptionProcessor
{
166 OptionProcessor(Private
*d
) : clp(d
) { }
168 void next(Private::ParsedOption
&option
) {
169 if (! option
.option
.isEmpty()) {
170 // find the definition to match.
171 OptionDefinition def
;
172 foreach(Private::OptionDefinition definition
, clp
->definitions
) {
173 if (definition
.name
== option
.option
) {
178 if (! def
.name
.isEmpty() && def
.requiredParameters
>= option
.parameters
.count() &&
179 def
.requiredParameters
+ def
.optionalParameters
<= option
.parameters
.count())
180 clp
->options
.insert(option
.option
, option
);
181 else if (!clp
->undefinedOptions
.contains(option
.option
))
182 clp
->undefinedOptions
<< option
.option
;
184 clp
->errors
.append(QString("Not enough arguments passed for option `")+ option
.option
+"'");
186 option
.option
.clear();
187 option
.parameters
.clear();
191 CommandLineParser::Private
*clp
;
193 OptionProcessor
processor(this);
195 bool optionsAllowed
= true;
197 OptionDefinition currentDefinition
;
198 for(int i
= 1; i
< argumentCount
; i
++) {
199 QString arg
= argumentStrings
[i
];
200 if (optionsAllowed
) {
202 optionsAllowed
= false;
205 if (arg
.startsWith("--")) {
206 processor
.next(option
);
207 int end
= arg
.indexOf('=');
208 option
.option
= arg
.mid(2, end
- 2);
210 option
.parameters
<< arg
.mid(end
+1);
213 if (arg
[0] == '-' && arg
.length() > 1) {
214 for(int x
= 1; x
< arg
.length(); x
++) {
215 foreach(OptionDefinition definition
, definitions
) {
216 if (definition
.shortName
== arg
[x
]) {
217 processor
.next(option
);
218 currentDefinition
= definition
;
219 option
.option
= definition
.name
;
223 if (option
.option
.isEmpty()) { // nothing found; copy char so it ends up in unrecognized
224 option
.option
= arg
[x
];
225 processor
.next(option
);
231 if (! option
.option
.isEmpty()) {
232 if (currentDefinition
.name
!= option
.option
) {
233 // find the definition to match.
234 foreach(OptionDefinition definition
, definitions
) {
235 if (definition
.name
== option
.option
) {
236 currentDefinition
= definition
;
241 if (currentDefinition
.requiredParameters
+ currentDefinition
.optionalParameters
<= option
.parameters
.count())
242 processor
.next(option
);
244 if (option
.option
.isEmpty())
247 option
.parameters
<< arg
;
249 processor
.next(option
);
251 if (requiredArguments
> arguments
.count())
252 errors
.append(QString("Not enough arguments, usage: ") + argumentStrings
[0] +" "+argumentDefinition
);
255 foreach(QString key, options.keys()) {
256 ParsedOption p = options[key];
257 qDebug() << "-> " << p.option;
258 foreach(QString v, p.parameters)
259 qDebug() << " +" << v;
262 foreach(QString arg, arguments) {
268 // -----------------------------------
272 void CommandLineParser::init(int argc
, char **argv
)
276 self
= new CommandLineParser(argc
, argv
);
280 void CommandLineParser::addOptionDefinitions(const CommandLineOption
* optionList
)
283 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
286 self
->d
->addDefinitions(optionList
);
290 CommandLineParser
*CommandLineParser::instance()
296 void CommandLineParser::setArgumentDefinition(const char *definition
)
299 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
302 self
->d
->setArgumentDefinition(definition
);
306 CommandLineParser::CommandLineParser(int argc
, char **argv
)
307 : d(new Private(argc
, argv
))
311 CommandLineParser::~CommandLineParser()
316 void CommandLineParser::usage(const QString
&name
, const QString
&argumentDescription
)
318 QTextStream
cout(stdout
, QIODevice::WriteOnly
);
319 cout
<< "Usage: " << d
->argumentStrings
[0];
320 if (! name
.isEmpty())
322 if (d
->definitions
.count())
324 if (! argumentDescription
.isEmpty())
325 cout
<< " " << argumentDescription
;
326 cout
<< endl
<< endl
;
328 if (d
->definitions
.count() > 0)
329 cout
<< "Options:" << endl
;
330 int commandLenght
= 0;
331 foreach (Private::OptionDefinition definition
, d
->definitions
)
332 commandLenght
= qMax(definition
.name
.length(), commandLenght
);
334 foreach (Private::OptionDefinition definition
, d
->definitions
) {
336 if (definition
.shortName
== 0)
339 cout
<< "-" << definition
.shortName
<< " --";
340 cout
<< definition
.name
;
341 for (int i
= definition
.name
.length(); i
<= commandLenght
; i
++)
343 cout
<< definition
.comment
<<endl
;
347 QStringList
CommandLineParser::options() const
350 return d
->options
.keys();
353 bool CommandLineParser::contains(const QString
& key
) const
356 return d
->options
.contains(key
);
359 QStringList
CommandLineParser::arguments() const
365 QStringList
CommandLineParser::undefinedOptions() const
368 return d
->undefinedOptions
;
371 QString
CommandLineParser::optionArgument(const QString
&optionName
, const QString
&defaultValue
) const
373 QStringList answer
= optionArguments(optionName
);
374 if (answer
.isEmpty())
376 return answer
.first();
379 QStringList
CommandLineParser::optionArguments(const QString
&optionName
) const
381 if (! contains(optionName
))
382 return QStringList();
383 Private::ParsedOption po
= d
->options
[optionName
];
384 return po
.parameters
;
387 QStringList
CommandLineParser::parseErrors() const