Don't allow to unrecord past the branch point
[vng.git] / CommandLineParser.cpp
blobe31eb1eca7e946cd22e4614b25767e724ff4a925
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 if (s.endsWith('>'))
148 inArg = false;
149 else if (!inArg)
150 requiredArguments++;
154 void CommandLineParser::Private::parse()
156 if (dirty == false)
157 return;
158 errors.clear();
159 options.clear();
160 arguments.clear();
161 undefinedOptions.clear();
162 dirty = false;
164 class OptionProcessor {
165 public:
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) {
174 def = definition;
175 break;
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;
183 else
184 clp->errors.append(QString("Not enough arguments passed for option `")+ option.option +"'");
186 option.option.clear();
187 option.parameters.clear();
190 private:
191 CommandLineParser::Private *clp;
193 OptionProcessor processor(this);
195 bool optionsAllowed = true;
196 ParsedOption option;
197 OptionDefinition currentDefinition;
198 for(int i = 1; i < argumentCount; i++) {
199 QString arg = argumentStrings[i];
200 if (optionsAllowed) {
201 if (arg == "--") {
202 optionsAllowed = false;
203 continue;
205 if (arg.startsWith("--")) {
206 processor.next(option);
207 int end = arg.indexOf('=');
208 option.option = arg.mid(2, end - 2);
209 if (end > 0)
210 option.parameters << arg.mid(end+1);
211 continue;
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;
220 break;
223 if (option.option.isEmpty()) { // nothing found; copy char so it ends up in unrecognized
224 option.option = arg[x];
225 processor.next(option);
228 continue;
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;
237 break;
241 if (currentDefinition.requiredParameters + currentDefinition.optionalParameters <= option.parameters.count())
242 processor.next(option);
244 if (option.option.isEmpty())
245 arguments << arg;
246 else
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;
261 qDebug() << "---";
262 foreach(QString arg, arguments) {
263 qDebug() << arg;
268 // -----------------------------------
271 // static
272 void CommandLineParser::init(int argc, char **argv)
274 if (self)
275 delete self;
276 self = new CommandLineParser(argc, argv);
279 // static
280 void CommandLineParser::addOptionDefinitions(const CommandLineOption * optionList)
282 if (!self) {
283 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
284 return;
286 self->d->addDefinitions(optionList);
289 // static
290 CommandLineParser *CommandLineParser::instance()
292 return self;
295 // static
296 void CommandLineParser::setArgumentDefinition(const char *definition)
298 if (!self) {
299 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
300 return;
302 self->d->setArgumentDefinition(definition);
306 CommandLineParser::CommandLineParser(int argc, char **argv)
307 : d(new Private(argc, argv))
311 CommandLineParser::~CommandLineParser()
313 delete d;
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())
321 cout << " " << name;
322 if (d->definitions.count())
323 cout << " [OPTION]";
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) {
335 cout << " ";
336 if (definition.shortName == 0)
337 cout << " --";
338 else
339 cout << "-" << definition.shortName << " --";
340 cout << definition.name;
341 for (int i = definition.name.length(); i <= commandLenght; i++)
342 cout << ' ';
343 cout << definition.comment <<endl;
347 QStringList CommandLineParser::options() const
349 d->parse();
350 return d->options.keys();
353 bool CommandLineParser::contains(const QString & key) const
355 d->parse();
356 return d->options.contains(key);
359 QStringList CommandLineParser::arguments() const
361 d->parse();
362 return d->arguments;
365 QStringList CommandLineParser::undefinedOptions() const
367 d->parse();
368 return d->undefinedOptions;
371 QString CommandLineParser::optionArgument(const QString &optionName, const QString &defaultValue) const
373 QStringList answer = optionArguments(optionName);
374 if (answer.isEmpty())
375 return defaultValue;
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
389 d->parse();
390 return d->errors;