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
) {
147 else if (s
[0] == '[')
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(QString("Not enough arguments passed for option `")+ option
.option
+"'");
188 option
.option
.clear();
189 option
.parameters
.clear();
193 CommandLineParser::Private
*clp
;
195 OptionProcessor
processor(this);
197 bool optionsAllowed
= true;
199 OptionDefinition currentDefinition
;
200 for(int i
= 1; i
< argumentCount
; i
++) {
201 QString arg
= QString::fromLocal8Bit(argumentStrings
[i
]);
202 if (optionsAllowed
) {
204 optionsAllowed
= false;
207 if (arg
.startsWith("--")) {
208 processor
.next(option
);
209 int end
= arg
.indexOf('=');
210 option
.option
= arg
.mid(2, end
- 2);
212 option
.parameters
<< arg
.mid(end
+1);
215 if (arg
[0] == '-' && arg
.length() > 1) {
216 for(int x
= 1; x
< arg
.length(); x
++) {
217 foreach(OptionDefinition definition
, definitions
) {
218 if (definition
.shortName
== arg
[x
]) {
219 processor
.next(option
);
220 currentDefinition
= definition
;
221 option
.option
= definition
.name
;
223 if (definition
.requiredParameters
== 1 && arg
.length() >= x
+2) {
224 option
.parameters
<< arg
.mid(x
+1, arg
.length());
230 if (option
.option
.isEmpty()) { // nothing found; copy char so it ends up in unrecognized
231 option
.option
= arg
[x
];
232 processor
.next(option
);
238 if (! option
.option
.isEmpty()) {
239 if (currentDefinition
.name
!= option
.option
) {
240 // find the definition to match.
241 foreach(OptionDefinition definition
, definitions
) {
242 if (definition
.name
== option
.option
) {
243 currentDefinition
= definition
;
248 if (currentDefinition
.requiredParameters
+ currentDefinition
.optionalParameters
<= option
.parameters
.count())
249 processor
.next(option
);
251 if (option
.option
.isEmpty())
254 option
.parameters
<< arg
;
256 processor
.next(option
);
258 if (requiredArguments
> arguments
.count())
259 errors
.append(QString("Not enough arguments, usage: ") + argumentStrings
[0] +" "+argumentDefinition
);
262 foreach(QString key, options.keys()) {
263 ParsedOption p = options[key];
264 qDebug() << "-> " << p.option;
265 foreach(QString v, p.parameters)
266 qDebug() << " +" << v;
269 foreach(QString arg, arguments) {
275 // -----------------------------------
279 void CommandLineParser::init(int argc
, char **argv
)
283 self
= new CommandLineParser(argc
, argv
);
287 void CommandLineParser::addOptionDefinitions(const CommandLineOption
* optionList
)
290 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
293 self
->d
->addDefinitions(optionList
);
297 CommandLineParser
*CommandLineParser::instance()
303 void CommandLineParser::setArgumentDefinition(const char *definition
)
306 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
309 self
->d
->setArgumentDefinition(definition
);
313 CommandLineParser::CommandLineParser(int argc
, char **argv
)
314 : d(new Private(argc
, argv
))
318 CommandLineParser::~CommandLineParser()
323 void CommandLineParser::usage(const QString
&name
, const QString
&argumentDescription
)
325 QTextStream
cout(stdout
, QIODevice::WriteOnly
);
326 cout
<< "Usage: " << d
->argumentStrings
[0];
327 if (! name
.isEmpty())
329 if (d
->definitions
.count())
331 if (! argumentDescription
.isEmpty())
332 cout
<< " " << argumentDescription
;
333 cout
<< endl
<< endl
;
335 if (d
->definitions
.count() > 0)
336 cout
<< "Options:" << endl
;
337 int commandLength
= 0;
338 foreach (Private::OptionDefinition definition
, d
->definitions
)
339 commandLength
= qMax(definition
.name
.length(), commandLength
);
341 foreach (Private::OptionDefinition definition
, d
->definitions
) {
343 if (definition
.shortName
== 0)
346 cout
<< "-" << definition
.shortName
<< " --";
347 cout
<< definition
.name
;
348 for (int i
= definition
.name
.length(); i
<= commandLength
; i
++)
350 cout
<< definition
.comment
<<endl
;
354 QStringList
CommandLineParser::options() const
357 return d
->options
.keys();
360 bool CommandLineParser::contains(const QString
& key
) const
363 return d
->options
.contains(key
);
366 QStringList
CommandLineParser::arguments() const
372 QStringList
CommandLineParser::undefinedOptions() const
375 return d
->undefinedOptions
;
378 QString
CommandLineParser::optionArgument(const QString
&optionName
, const QString
&defaultValue
) const
380 QStringList answer
= optionArguments(optionName
);
381 if (answer
.isEmpty())
383 return answer
.first();
386 QStringList
CommandLineParser::optionArguments(const QString
&optionName
) const
388 if (! contains(optionName
))
389 return QStringList();
390 Private::ParsedOption po
= d
->options
[optionName
];
391 return po
.parameters
;
394 QStringList
CommandLineParser::parseErrors() const