Make sure we don't ignore hidden files.
[vng.git] / src / CommandLineParser.cpp
blob3221c5863e2d0fa7e0217f4a6c20e2e48b5ca93a
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 = QString::fromLatin1(options[i].specification);
80 if (option.indexOf(QLatin1Char(',')) >= 0) {
81 QStringList optionParts = option.split(QLatin1Char(','), 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(QLatin1String("--")) && s.length() > 2)
89 definition.name = s.mid(2);
90 else if (s.startsWith(QLatin1String("-")) && 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(QLatin1String("--")) && 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(QLatin1Char(' ')) > 0) {
106 QStringList optionParts = definition.name.split(QLatin1Char(' '), 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].unicode() == '[' && s.endsWith(QLatin1Char(']')))
116 definition.optionalParameters++;
117 else
118 definition.requiredParameters++;
122 definition.comment = QString::fromLatin1(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::fromLatin1(defs);
139 QStringList optionParts = argumentDefinition.split(QLatin1Char(' '), QString::SkipEmptyParts);
140 bool inArg = false;
141 foreach(QString s, optionParts) {
142 s = s.trimmed();
143 if (s[0].unicode() == '<') {
144 inArg = true;
145 requiredArguments++;
147 else if (s[0].unicode() == '[')
148 inArg = true;
149 if (s.endsWith(QLatin1Char('>')))
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(QLatin1String("Not enough arguments passed for option `")
187 + option.option +QLatin1Char('\''));
189 option.option.clear();
190 option.parameters.clear();
193 private:
194 CommandLineParser::Private *clp;
196 OptionProcessor processor(this);
198 bool optionsAllowed = true;
199 ParsedOption option;
200 OptionDefinition currentDefinition;
201 for(int i = 1; i < argumentCount; i++) {
202 QString arg = QString::fromLocal8Bit(argumentStrings[i]);
203 if (optionsAllowed) {
204 if (arg == QLatin1String("--")) {
205 optionsAllowed = false;
206 continue;
208 if (arg.startsWith(QLatin1String("--"))) {
209 processor.next(option);
210 int end = arg.indexOf(QLatin1Char('='));
211 option.option = arg.mid(2, end - 2);
212 if (end > 0)
213 option.parameters << arg.mid(end+1);
214 continue;
216 if (arg[0].unicode() == '-' && arg.length() > 1) {
217 for(int x = 1; x < arg.length(); x++) {
218 foreach(OptionDefinition definition, definitions) {
219 if (definition.shortName == arg[x]) {
220 processor.next(option);
221 currentDefinition = definition;
222 option.option = definition.name;
224 if (definition.requiredParameters == 1 && arg.length() >= x+2) {
225 option.parameters << arg.mid(x+1, arg.length());
226 x = arg.length();
228 break;
231 if (option.option.isEmpty()) { // nothing found; copy char so it ends up in unrecognized
232 option.option = arg[x];
233 processor.next(option);
236 continue;
239 if (! option.option.isEmpty()) {
240 if (currentDefinition.name != option.option) {
241 // find the definition to match.
242 foreach(OptionDefinition definition, definitions) {
243 if (definition.name == option.option) {
244 currentDefinition = definition;
245 break;
249 if (currentDefinition.requiredParameters + currentDefinition.optionalParameters <= option.parameters.count())
250 processor.next(option);
252 if (option.option.isEmpty())
253 arguments << arg;
254 else
255 option.parameters << arg;
257 processor.next(option);
259 if (requiredArguments > arguments.count())
260 errors.append(QLatin1String("Not enough arguments, usage: ") + QString::fromLocal8Bit(argumentStrings[0])
261 + QLatin1Char(' ') + argumentDefinition);
264 foreach(QString key, options.keys()) {
265 ParsedOption p = options[key];
266 qDebug() << "-> " << p.option;
267 foreach(QString v, p.parameters)
268 qDebug() << " +" << v;
270 qDebug() << "---";
271 foreach(QString arg, arguments) {
272 qDebug() << arg;
277 // -----------------------------------
280 // static
281 void CommandLineParser::init(int argc, char **argv)
283 if (self)
284 delete self;
285 self = new CommandLineParser(argc, argv);
288 // static
289 void CommandLineParser::addOptionDefinitions(const CommandLineOption * optionList)
291 if (!self) {
292 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
293 return;
295 self->d->addDefinitions(optionList);
298 // static
299 CommandLineParser *CommandLineParser::instance()
301 return self;
304 // static
305 void CommandLineParser::setArgumentDefinition(const char *definition)
307 if (!self) {
308 qWarning() << "CommandLineParser:: Use init before addOptionDefinitions!";
309 return;
311 self->d->setArgumentDefinition(definition);
315 CommandLineParser::CommandLineParser(int argc, char **argv)
316 : d(new Private(argc, argv))
320 CommandLineParser::~CommandLineParser()
322 delete d;
325 void CommandLineParser::usage(const QString &name, const QString &argumentDescription)
327 QTextStream cout(stdout, QIODevice::WriteOnly);
328 cout << "Usage: " << d->argumentStrings[0];
329 if (! name.isEmpty())
330 cout << " " << name;
331 if (d->definitions.count())
332 cout << " [OPTION]";
333 if (! argumentDescription.isEmpty())
334 cout << " " << argumentDescription;
335 cout << endl << endl;
337 if (d->definitions.count() > 0)
338 cout << "Options:" << endl;
339 int commandLength = 0;
340 foreach (Private::OptionDefinition definition, d->definitions)
341 commandLength = qMax(definition.name.length(), commandLength);
343 foreach (Private::OptionDefinition definition, d->definitions) {
344 cout << " ";
345 if (definition.shortName == 0)
346 cout << " --";
347 else
348 cout << "-" << definition.shortName << " --";
349 cout << definition.name;
350 for (int i = definition.name.length(); i <= commandLength; i++)
351 cout << ' ';
352 cout << definition.comment <<endl;
356 QStringList CommandLineParser::options() const
358 d->parse();
359 return d->options.keys();
362 bool CommandLineParser::contains(const QString & key) const
364 d->parse();
365 return d->options.contains(key);
368 QStringList CommandLineParser::arguments() const
370 d->parse();
371 return d->arguments;
374 QStringList CommandLineParser::undefinedOptions() const
376 d->parse();
377 return d->undefinedOptions;
380 QString CommandLineParser::optionArgument(const QString &optionName, const QString &defaultValue) const
382 QStringList answer = optionArguments(optionName);
383 if (answer.isEmpty())
384 return defaultValue;
385 return answer.first();
388 QStringList CommandLineParser::optionArguments(const QString &optionName) const
390 if (! contains(optionName))
391 return QStringList();
392 Private::ParsedOption po = d->options[optionName];
393 return po.parameters;
396 QStringList CommandLineParser::parseErrors() const
398 d->parse();
399 return d->errors;