Ignore files according to ignore set on whatsnew --look-for-adds
[vng.git] / CommandLineParser.cpp
blob1297ebad6943a050f959523346640982c37fc4ad
1 /*
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"
21 #include <QDebug>
22 #include <QTextStream>
23 #include <QStringList>
24 #include <QList>
25 #include <QHash>
27 CommandLineParser *CommandLineParser::self = 0;
29 class CommandLineParser::Private
31 public:
32 Private(int argc, char **argv);
34 // functions
35 void addDefinitions(const CommandLineOption * options);
36 void setArgumentDefinition(const char *definition);
37 void parse();
39 // variables;
40 const int argumentCount;
41 char ** const argumentStrings;
42 bool dirty;
43 int requiredArguments;
44 QString argumentDefinition;
46 struct OptionDefinition {
47 OptionDefinition() : optionalParameters(0), requiredParameters(0) { }
48 QString name;
49 QString comment;
50 QChar shortName;
51 int optionalParameters;
52 int requiredParameters;
55 // result of what the user typed
56 struct ParsedOption {
57 QString option;
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),
71 requiredArguments(0)
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";
84 continue;
86 foreach(QString s, optionParts) {
87 s = s.trimmed();
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);
92 else {
93 qWarning() << "option definition '" << option << "' is faulty; the option should start with a -";
94 break;
98 else if (option.startsWith("--") && option.length() > 2)
99 definition.name = option.mid(2);
100 else
101 qWarning() << "option definition '" << option << "' has unrecognized format. See the api docs for CommandLineParser for a howto";
103 if(definition.name.isEmpty())
104 continue;
105 if (option.indexOf(' ') > 0) {
106 QStringList optionParts = definition.name.split(QChar(' '), QString::SkipEmptyParts);
107 definition.name = optionParts[0];
108 bool first = true;
109 foreach(QString s, optionParts) {
110 if (first) {
111 first = false;
112 continue;
114 s = s.trimmed();
115 if (s[0] == '[' && s.endsWith(']'))
116 definition.optionalParameters++;
117 else
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;
132 dirty = true;
135 void CommandLineParser::Private::setArgumentDefinition(const char *defs)
137 requiredArguments = 0;
138 argumentDefinition = QString(defs);
139 QStringList optionParts = argumentDefinition.split(QChar(' '), QString::SkipEmptyParts);
140 bool inArg = false;
141 foreach(QString s, optionParts) {
142 s = s.trimmed();
143 if (s[0] == '<') {
144 inArg = true;
145 requiredArguments++;
147 else if (s[0] == '[')
148 inArg = true;
149 if (s.endsWith('>'))
150 inArg = false;
151 else if (!inArg)
152 requiredArguments++;
156 void CommandLineParser::Private::parse()
158 if (dirty == false)
159 return;
160 errors.clear();
161 options.clear();
162 arguments.clear();
163 undefinedOptions.clear();
164 dirty = false;
166 class OptionProcessor {
167 public:
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) {
176 def = definition;
177 break;
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;
185 else
186 clp->errors.append(QString("Not enough arguments passed for option `")+ option.option +"'");
188 option.option.clear();
189 option.parameters.clear();
192 private:
193 CommandLineParser::Private *clp;
195 OptionProcessor processor(this);
197 bool optionsAllowed = true;
198 ParsedOption option;
199 OptionDefinition currentDefinition;
200 for(int i = 1; i < argumentCount; i++) {
201 QString arg = QString::fromLocal8Bit(argumentStrings[i]);
202 if (optionsAllowed) {
203 if (arg == "--") {
204 optionsAllowed = false;
205 continue;
207 if (arg.startsWith("--")) {
208 processor.next(option);
209 int end = arg.indexOf('=');
210 option.option = arg.mid(2, end - 2);
211 if (end > 0)
212 option.parameters << arg.mid(end+1);
213 continue;
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());
225 x = arg.length();
227 break;
230 if (option.option.isEmpty()) { // nothing found; copy char so it ends up in unrecognized
231 option.option = arg[x];
232 processor.next(option);
235 continue;
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;
244 break;
248 if (currentDefinition.requiredParameters + currentDefinition.optionalParameters <= option.parameters.count())
249 processor.next(option);
251 if (option.option.isEmpty())
252 arguments << arg;
253 else
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;
268 qDebug() << "---";
269 foreach(QString arg, arguments) {
270 qDebug() << arg;
275 // -----------------------------------
278 // static
279 void CommandLineParser::init(int argc, char **argv)
281 if (self)
282 delete self;
283 self = new CommandLineParser(argc, argv);
286 // static
287 void CommandLineParser::addOptionDefinitions(const CommandLineOption * optionList)
289 if (!self) {
290 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
291 return;
293 self->d->addDefinitions(optionList);
296 // static
297 CommandLineParser *CommandLineParser::instance()
299 return self;
302 // static
303 void CommandLineParser::setArgumentDefinition(const char *definition)
305 if (!self) {
306 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
307 return;
309 self->d->setArgumentDefinition(definition);
313 CommandLineParser::CommandLineParser(int argc, char **argv)
314 : d(new Private(argc, argv))
318 CommandLineParser::~CommandLineParser()
320 delete d;
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())
328 cout << " " << name;
329 if (d->definitions.count())
330 cout << " [OPTION]";
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) {
342 cout << " ";
343 if (definition.shortName == 0)
344 cout << " --";
345 else
346 cout << "-" << definition.shortName << " --";
347 cout << definition.name;
348 for (int i = definition.name.length(); i <= commandLength; i++)
349 cout << ' ';
350 cout << definition.comment <<endl;
354 QStringList CommandLineParser::options() const
356 d->parse();
357 return d->options.keys();
360 bool CommandLineParser::contains(const QString & key) const
362 d->parse();
363 return d->options.contains(key);
366 QStringList CommandLineParser::arguments() const
368 d->parse();
369 return d->arguments;
372 QStringList CommandLineParser::undefinedOptions() const
374 d->parse();
375 return d->undefinedOptions;
378 QString CommandLineParser::optionArgument(const QString &optionName, const QString &defaultValue) const
380 QStringList answer = optionArguments(optionName);
381 if (answer.isEmpty())
382 return defaultValue;
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
396 d->parse();
397 return d->errors;