docs
[vng.git] / CommandLineParser.cpp
blobb5883de6737fd940eb3c4eac4c4390a3f0263e5b
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 = 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;
222 break;
225 if (option.option.isEmpty()) { // nothing found; copy char so it ends up in unrecognized
226 option.option = arg[x];
227 processor.next(option);
230 continue;
233 if (! option.option.isEmpty()) {
234 if (currentDefinition.name != option.option) {
235 // find the definition to match.
236 foreach(OptionDefinition definition, definitions) {
237 if (definition.name == option.option) {
238 currentDefinition = definition;
239 break;
243 if (currentDefinition.requiredParameters + currentDefinition.optionalParameters <= option.parameters.count())
244 processor.next(option);
246 if (option.option.isEmpty())
247 arguments << arg;
248 else
249 option.parameters << arg;
251 processor.next(option);
253 if (requiredArguments > arguments.count())
254 errors.append(QString("Not enough arguments, usage: ") + argumentStrings[0] +" "+argumentDefinition);
257 foreach(QString key, options.keys()) {
258 ParsedOption p = options[key];
259 qDebug() << "-> " << p.option;
260 foreach(QString v, p.parameters)
261 qDebug() << " +" << v;
263 qDebug() << "---";
264 foreach(QString arg, arguments) {
265 qDebug() << arg;
270 // -----------------------------------
273 // static
274 void CommandLineParser::init(int argc, char **argv)
276 if (self)
277 delete self;
278 self = new CommandLineParser(argc, argv);
281 // static
282 void CommandLineParser::addOptionDefinitions(const CommandLineOption * optionList)
284 if (!self) {
285 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
286 return;
288 self->d->addDefinitions(optionList);
291 // static
292 CommandLineParser *CommandLineParser::instance()
294 return self;
297 // static
298 void CommandLineParser::setArgumentDefinition(const char *definition)
300 if (!self) {
301 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
302 return;
304 self->d->setArgumentDefinition(definition);
308 CommandLineParser::CommandLineParser(int argc, char **argv)
309 : d(new Private(argc, argv))
313 CommandLineParser::~CommandLineParser()
315 delete d;
318 void CommandLineParser::usage(const QString &name, const QString &argumentDescription)
320 QTextStream cout(stdout, QIODevice::WriteOnly);
321 cout << "Usage: " << d->argumentStrings[0];
322 if (! name.isEmpty())
323 cout << " " << name;
324 if (d->definitions.count())
325 cout << " [OPTION]";
326 if (! argumentDescription.isEmpty())
327 cout << " " << argumentDescription;
328 cout << endl << endl;
330 if (d->definitions.count() > 0)
331 cout << "Options:" << endl;
332 int commandLength = 0;
333 foreach (Private::OptionDefinition definition, d->definitions)
334 commandLength = qMax(definition.name.length(), commandLength);
336 foreach (Private::OptionDefinition definition, d->definitions) {
337 cout << " ";
338 if (definition.shortName == 0)
339 cout << " --";
340 else
341 cout << "-" << definition.shortName << " --";
342 cout << definition.name;
343 for (int i = definition.name.length(); i <= commandLength; i++)
344 cout << ' ';
345 cout << definition.comment <<endl;
349 QStringList CommandLineParser::options() const
351 d->parse();
352 return d->options.keys();
355 bool CommandLineParser::contains(const QString & key) const
357 d->parse();
358 return d->options.contains(key);
361 QStringList CommandLineParser::arguments() const
363 d->parse();
364 return d->arguments;
367 QStringList CommandLineParser::undefinedOptions() const
369 d->parse();
370 return d->undefinedOptions;
373 QString CommandLineParser::optionArgument(const QString &optionName, const QString &defaultValue) const
375 QStringList answer = optionArguments(optionName);
376 if (answer.isEmpty())
377 return defaultValue;
378 return answer.first();
381 QStringList CommandLineParser::optionArguments(const QString &optionName) const
383 if (! contains(optionName))
384 return QStringList();
385 Private::ParsedOption po = d->options[optionName];
386 return po.parameters;
389 QStringList CommandLineParser::parseErrors() const
391 d->parse();
392 return d->errors;