1 /****************************************************************************
2 * MeshLab o o *
3 * An extendible mesh processor o o *
4 * _ O _ *
5 * Copyright(C) 2005, 2009 \/)\/ *
6 * Visual Computing Lab /\/| *
7 * ISTI - Italian National Research Council | *
8 * \ *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
20 * for more details. *
21 * *
22 ****************************************************************************/
23
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <QtCore/QVariant>
27 #include "qgetopt.h"
28
29 #include <iostream>
30 using namespace std;
31
GetOpt(int argc,char * argv[])32 GetOpt::GetOpt(int argc, char *argv[] ) {
33 appname = argv[0];
34 for(int i = 1; i < argc; i++)
35 args.push_back(argv[i]);
36 }
37
GetOpt(const QStringList & a)38 GetOpt::GetOpt(const QStringList &a): args(a) {
39 appname = a[0];
40 args = a;
41 args.pop_front();
42 }
43
44 //add an option without a value
addSwitch(char s,const QString & name,const QString & description,bool * b)45 void GetOpt::addSwitch(char s, const QString &name, const QString &description, bool *b ) {
46 Option option;
47 assert(!findOption(s, option));
48 assert(!findArg(name, option));
49 option.type = Option::SWITCH;
50 option.o = s;
51 option.name = name;
52 option.description = description;
53 option.boolean_value = b;
54 options.push_back(option);
55 }
56
57 //add a valued option (v will be left untouched if the option is not given)
addOption(char s,const QString & name,const QString & description,QVariant * v)58 void GetOpt::addOption(char s, const QString &name, const QString &description, QVariant *v ) {
59 Option option(Option::OPTION, s, name, description);
60 option.value = v;
61
62 assert(!findOption(s, option)); //TODO make this check systematic
63 assert(!findArg(name, option));
64
65 options.push_back(option);
66
67
68 }
69 //add an argument
addArgument(const QString & name,const QString & description,QVariant * v)70 void GetOpt::addArgument(const QString &name, const QString &description, QVariant *v) {
71 Option option;
72 option.value = v;
73 addArgument(name, description, option);
74 }
75
addArgument(const QString & name,const QString & description,QString * v)76 void GetOpt::addArgument(const QString &name, const QString &description, QString *v) {
77 Option option;
78 option.string_value = v;
79 addArgument(name, description, option);
80 }
81
addArgument(const QString & name,const QString & description,float * v)82 void GetOpt::addArgument(const QString &name, const QString &description, float *v) {
83 Option option;
84 option.float_value = v;
85 addArgument(name, description, option);
86 }
87
addArgument(const QString & name,const QString & description,double * v)88 void GetOpt::addArgument(const QString &name, const QString &description, double *v) {
89 Option option;
90 option.double_value = v;
91 addArgument(name, description, option);
92 }
93
addArgument(const QString & name,const QString & description,int * v)94 void GetOpt::addArgument(const QString &name, const QString &description, int *v) {
95 Option option;
96 option.int_value = v;
97 addArgument(name, description, option);
98 }
99
addArgument(const QString & name,const QString & description,bool * v)100 void GetOpt::addArgument(const QString &name, const QString &description, bool *v) {
101 Option option;
102 option.boolean_value = v;
103 addArgument(name, description, option);
104 }
105
addArgument(const QString & name,const QString & description,Option option)106 void GetOpt::addArgument(const QString &name, const QString &description, Option option) {
107 assert(!findArg(name, option));
108 option.type = Option::ARGUMENT;
109 option.name = name;
110 option.description = description;
111 options.push_back(option);
112 }
113
addOption(char s,const QString & longname,const QString & description,QString * v)114 void GetOpt::addOption(char s, const QString &longname, const QString &description, QString *v) {
115 Option option(Option::OPTION, s, longname, description);
116 option.string_value = v;
117 options.push_back(option);
118 }
addOption(char s,const QString & longname,const QString & description,float * v)119 void GetOpt::addOption(char s, const QString &longname, const QString &description, float *v) {
120 Option option(Option::OPTION, s, longname, description);
121 option.float_value = v;
122 options.push_back(option);
123 }
addOption(char s,const QString & longname,const QString & description,double * v)124 void GetOpt::addOption(char s, const QString &longname, const QString &description, double *v) {
125 Option option(Option::OPTION, s, longname, description);
126 option.double_value = v;
127 options.push_back(option);
128 }
addOption(char s,const QString & longname,const QString & description,int * v)129 void GetOpt::addOption(char s, const QString &longname, const QString &description, int *v) {
130 Option option(Option::OPTION, s, longname, description);
131 option.int_value = v;
132 options.push_back(option);
133 }
addOption(char s,const QString & longname,const QString & description,bool * v)134 void GetOpt::addOption(char s, const QString &longname, const QString &description, bool *v) {
135 Option option(Option::OPTION, s, longname, description);
136 option.boolean_value = v;
137 options.push_back(option);
138 }
139
140 //add an optional agrument
addOptionalArgument(const QString & name,const QString & description,QVariant * v)141 void GetOpt::addOptionalArgument(const QString &name, const QString &description, QVariant *v) {
142 Option option;
143 assert(!findArg(name, option));
144 option.type = Option::OPTIONAL;
145 option.name = name;
146 option.description = description;
147 option.value = v;
148 options.push_back(option);
149 }
150 //return application name
applicationName()151 QString &GetOpt::applicationName() {
152 return appname;
153 }
154
155 //return usage string
usage()156 QString GetOpt::usage() {
157 QString u = "Usage: " + appname;
158 //arguments
159 bool has_optionals = false;
160 bool has_options = false;
161 for(int i = 0; i < options.size(); i++) {
162 if(options[i].type == Option::OPTION) has_options = true;
163 if(options[i].type == Option::OPTIONAL) has_optionals = true;
164 if(options[i].type != Option::ARGUMENT) continue;
165 u += " <" + options[i].name + ">";
166 }
167 //optional arguments
168 if(has_optionals) {
169 u += " [";
170 for(int i = 0; i < options.size(); i++) {
171 if(options[i].type != Option::OPTIONAL) continue;
172 u += "<" + options[i].name + ">";
173 }
174 u += "]";
175 }
176
177 if(unlimitedArgs) {
178 u += " [ARGS]";
179 }
180
181 if(has_options) {
182 u += " [-";
183 for(int i = 0; i < options.size(); i++) {
184 if(options[i].type != Option::OPTION) continue;
185 u += options[i].o;
186 }
187 u += "]";
188 }
189 u += "\n\n";
190 u += help;
191 u += "\n\n";
192 //compute maxlen:
193 int maxlen = 0;
194 for(int i = 0; i < options.size(); i++) {
195 Option &o = options[i];
196 int len = o.name.size() + 2;
197 switch(o.type) {
198 case Option::ARGUMENT:
199 case Option::OPTIONAL: break;
200 case Option::SWITCH: len += 5; break;
201 case Option::OPTION: len += 16; break;
202 default: break;
203 }
204 if(len > maxlen) maxlen = len;
205 }
206 //print options and arguments in the given order
207 for(int i = 0; i < options.size(); i++) {
208 Option &o = options[i];
209 QString line = "";
210 switch(o.type) {
211 case Option::ARGUMENT:
212 case Option::OPTIONAL: line += o.name; break;
213 case Option::SWITCH: line += "-" + QString(o.o) + " --" + o.name; break;
214 case Option::OPTION: line += "-" + QString(o.o) + " <val> --" + o.name + " <val>"; break;
215 default: break;
216 }
217 QString blank = "";
218 blank.resize(maxlen - line.size());
219 blank.fill(' ');
220 line += blank + formatDesc(o.description, maxlen) + "\n";
221 u += line;
222 }
223 return u;
224 }
225
parse()226 void GetOpt::parse() {
227 QString error;
228 if(!parse(error)) {
229 cerr << qPrintable(error) << endl << endl << qPrintable(usage()) << endl << endl;
230 exit(0);
231 }
232 }
233
assignOption(Option & o,QString arg,QString & error)234 bool GetOpt::assignOption(Option &o, QString arg, QString &error) {
235 QVariant::Type type;
236 if(o.value) type = o.value->type();
237 if(o.string_value) type = QVariant::String;
238 if(o.float_value) type = QVariant::Double;
239 if(o.double_value) type = QVariant::Double;
240 if(o.int_value) type = QVariant::Int;
241 if(o.boolean_value) type = QVariant::Bool;
242 QVariant v(arg);
243
244 if(!v.canConvert(type) || !v.convert(type)) {
245 error = "Error while parsing option " + o.name + ": cannot convert " +
246 arg + " to: " + v.typeName();
247 return false;
248 }
249 if(o.value) *(o.value) = v;
250 if(o.string_value) *(o.string_value) = v.toString();
251 if(o.float_value) *(o.float_value) = v.toFloat();
252 if(o.double_value) *(o.double_value) = v.toDouble();
253 if(o.int_value) *(o.int_value) = v.toInt();
254 if(o.boolean_value) *(o.boolean_value) = v.toBool();
255 return true;
256 }
257
parse(QString & error)258 bool GetOpt::parse(QString &error) {
259 for(int i = 0; i < args.size(); i++) {
260 QString arg = args[i];
261 if(args[i] == "-h" || args[i] == "--help") {
262 cout << qPrintable(usage()) << endl;
263 exit(0);
264 }
265 //long option
266 if(arg.startsWith( QString::fromLatin1( "--" ) ) ) {
267 arg = arg.mid( 2 );
268 if(arg.isEmpty()) {
269 error = "'--' feature not supported, yet";
270 return false;
271 }
272 Option o;
273 if(!findArg(arg, o)) {
274 error = "Unknown option: '" + arg + "'";
275 return false;
276 }
277 if(o.type == Option::SWITCH) {
278 *(o.boolean_value) = true;
279 } else { //OPTION
280 i++;
281 if(args.size() <= i) {
282 error = "Missing value for option: " + arg;
283 return false;
284 }
285 arg = args[i];
286 if(i == args.size() || arg[0] == '-') {
287 error = "Missing argument after option '" + arg + "'";
288 return false;
289 }
290 if(!assignOption(o, arg, error))
291 return false;
292 }
293
294 //option
295 } else if( arg[0] == '-' ) {
296 if(arg.size() != 2) {
297 error = "Invalid option: " + arg;
298 return false;
299 }
300 Option o;
301 if(!findOption(arg[1].toLatin1(), o)) {
302 error = "Unknown option: '" + arg + "'";
303 return false;
304 }
305 if(o.type == Option::SWITCH) {
306 *(o.boolean_value) = true;
307 } else { //OPTION
308 i++;
309 if(args.size() <= i) {
310 error = "Missing value for option: " + arg;
311 return false;
312 }
313 arg = args[i];
314 if(i == args.size()) {
315 error = "Missing argument after option '" + arg + "'";
316 return false;
317 }
318 if(!assignOption(o, arg, error))
319 return false;
320 /*
321 QVariant v(arg);
322 if(!v.canConvert(o.value->type()) || !v.convert(o.value->type())) {
323 error = "Error while parsing option " + o.name + ": cannot convert " +
324 arg + " to: " + o.value->typeName();
325 return false;
326 }
327 *(o.value) = v; */
328 }
329 //argument
330 } else {
331 arguments.push_back(arg);
332 }
333 }
334
335 //regular arguments
336 for(int i = 0; i < options.size(); i++) {
337 Option &o = options[i];
338 if(o.type != Option::ARGUMENT) continue;
339 if(arguments.isEmpty()) {
340 error = "Too few arguments, could not parse argument '" + o.name + "'";
341 return false;
342 }
343 if(!assignOption(o, arguments.front(), error))
344 return false;
345 arguments.pop_front();
346 }
347 //optional arguments
348 for(int i = 0; i < options.size(); i++) {
349 Option &o = options[i];
350 if(o.type != Option::OPTIONAL) continue;
351 if(arguments.isEmpty()) break;
352 if(!assignOption(o, arguments.front(), error))
353 return false;
354 arguments.pop_front();
355 }
356 if(!arguments.isEmpty() && !unlimitedArgs) {
357 error = "Too many arguments";
358 return false;
359 }
360 return true;
361 }
362
findOption(char c,Option & option)363 bool GetOpt::findOption(char c, Option &option) {
364 for(int i = 0; i < options.size(); i++) {
365 Option &o = options[i];
366 if(o.type != Option::OPTION && o.type != Option::SWITCH) continue;
367 if(o.o == c) {
368 option = o;
369 return true;
370 }
371 }
372 return false;
373 }
374
findArg(const QString & name,Option & option)375 bool GetOpt::findArg(const QString &name, Option &option) {
376 for(int i = 0; i < options.size(); i++) {
377 Option &o = options[i];
378 if(o.name == name) {
379 option = o;
380 return true;
381 }
382 }
383 return false;
384 }
385
formatDesc(QString desc,int len)386 QString GetOpt::formatDesc(QString desc, int len) {
387 QString output;
388 //search for last space before 79 - len characters
389 while(!desc.isEmpty()) {
390 int pos = desc.lastIndexOf(" ", 79 - len);
391 if(pos == -1) {
392 output += desc;
393 break;
394 }
395 output += desc.left(pos) + "\n";
396 QString blank;
397 blank.resize(len);
398 blank.fill(' ');
399 output += blank;
400 desc = desc.mid(pos+1);
401 }
402 return output;
403 }
404