00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <stdlib.h>
00025 #include <assert.h>
00026 #include <QtCore/QVariant>
00027 #include "qgetopt.h"
00028
00029 #include <iostream>
00030 using namespace std;
00031
00032 GetOpt::GetOpt(int argc, char *argv[] ) {
00033 appname = argv[0];
00034 for(int i = 1; i < argc; i++)
00035 args.push_back(argv[i]);
00036 }
00037
00038 GetOpt::GetOpt(const QStringList &a): args(a) {
00039 appname = a[0];
00040 args = a;
00041 args.pop_front();
00042 }
00043
00044
00045 void GetOpt::addSwitch(char s, const QString &name, const QString &description, bool *b ) {
00046 Option option;
00047 assert(!findOption(s, option));
00048 assert(!findArg(name, option));
00049 option.type = Option::SWITCH;
00050 option.o = s;
00051 option.name = name;
00052 option.description = description;
00053 option.boolean_value = b;
00054 options.push_back(option);
00055 }
00056
00057
00058 void GetOpt::addOption(char s, const QString &name, const QString &description, QVariant *v ) {
00059 Option option(Option::OPTION, s, name, description);
00060 option.value = v;
00061
00062 assert(!findOption(s, option));
00063 assert(!findArg(name, option));
00064
00065 options.push_back(option);
00066
00067
00068 }
00069
00070 void GetOpt::addArgument(const QString &name, const QString &description, QVariant *v) {
00071 Option option;
00072 option.value = v;
00073 addArgument(name, description, option);
00074 }
00075
00076 void GetOpt::addArgument(const QString &name, const QString &description, QString *v) {
00077 Option option;
00078 option.string_value = v;
00079 addArgument(name, description, option);
00080 }
00081
00082 void GetOpt::addArgument(const QString &name, const QString &description, float *v) {
00083 Option option;
00084 option.float_value = v;
00085 addArgument(name, description, option);
00086 }
00087
00088 void GetOpt::addArgument(const QString &name, const QString &description, double *v) {
00089 Option option;
00090 option.double_value = v;
00091 addArgument(name, description, option);
00092 }
00093
00094 void GetOpt::addArgument(const QString &name, const QString &description, int *v) {
00095 Option option;
00096 option.int_value = v;
00097 addArgument(name, description, option);
00098 }
00099
00100 void GetOpt::addArgument(const QString &name, const QString &description, bool *v) {
00101 Option option;
00102 option.boolean_value = v;
00103 addArgument(name, description, option);
00104 }
00105
00106 void GetOpt::addArgument(const QString &name, const QString &description, Option option) {
00107 assert(!findArg(name, option));
00108 option.type = Option::ARGUMENT;
00109 option.name = name;
00110 option.description = description;
00111 options.push_back(option);
00112 }
00113
00114 void GetOpt::addOption(char s, const QString &longname, const QString &description, QString *v) {
00115 Option option(Option::OPTION, s, longname, description);
00116 option.string_value = v;
00117 options.push_back(option);
00118 }
00119 void GetOpt::addOption(char s, const QString &longname, const QString &description, float *v) {
00120 Option option(Option::OPTION, s, longname, description);
00121 option.float_value = v;
00122 options.push_back(option);
00123 }
00124 void GetOpt::addOption(char s, const QString &longname, const QString &description, double *v) {
00125 Option option(Option::OPTION, s, longname, description);
00126 option.double_value = v;
00127 options.push_back(option);
00128 }
00129 void GetOpt::addOption(char s, const QString &longname, const QString &description, int *v) {
00130 Option option(Option::OPTION, s, longname, description);
00131 option.int_value = v;
00132 options.push_back(option);
00133 }
00134 void GetOpt::addOption(char s, const QString &longname, const QString &description, bool *v) {
00135 Option option(Option::OPTION, s, longname, description);
00136 option.boolean_value = v;
00137 options.push_back(option);
00138 }
00139
00140
00141 void GetOpt::addOptionalArgument(const QString &name, const QString &description, QVariant *v) {
00142 Option option;
00143 assert(!findArg(name, option));
00144 option.type = Option::OPTIONAL;
00145 option.name = name;
00146 option.description = description;
00147 option.value = v;
00148 options.push_back(option);
00149 }
00150
00151 QString &GetOpt::applicationName() {
00152 return appname;
00153 }
00154
00155
00156 QString GetOpt::usage() {
00157 QString u = "Usage: " + appname;
00158
00159 bool has_optionals = false;
00160 bool has_options = false;
00161 for(int i = 0; i < options.size(); i++) {
00162 if(options[i].type == Option::OPTION) has_options = true;
00163 if(options[i].type == Option::OPTIONAL) has_optionals = true;
00164 if(options[i].type != Option::ARGUMENT) continue;
00165 u += " <" + options[i].name + ">";
00166 }
00167
00168 if(has_optionals) {
00169 u += " [";
00170 for(int i = 0; i < options.size(); i++) {
00171 if(options[i].type != Option::OPTIONAL) continue;
00172 u += "<" + options[i].name + ">";
00173 }
00174 u += "]";
00175 }
00176 if(has_options) {
00177 u += " [-";
00178 for(int i = 0; i < options.size(); i++) {
00179 if(options[i].type != Option::OPTION) continue;
00180 u += options[i].o;
00181 }
00182 u += "]";
00183 }
00184 u += "\n\n";
00185
00186 int maxlen = 0;
00187 for(int i = 0; i < options.size(); i++) {
00188 Option &o = options[i];
00189 int len = o.name.size() + 2;
00190 switch(o.type) {
00191 case Option::ARGUMENT:
00192 case Option::OPTIONAL: break;
00193 case Option::SWITCH: len += 5; break;
00194 case Option::OPTION: len += 16; break;
00195 default: break;
00196 }
00197 if(len > maxlen) maxlen = len;
00198 }
00199
00200 for(int i = 0; i < options.size(); i++) {
00201 Option &o = options[i];
00202 QString line = "";
00203 switch(o.type) {
00204 case Option::ARGUMENT:
00205 case Option::OPTIONAL: line += o.name; break;
00206 case Option::SWITCH: line += "-" + QString(o.o) + " --" + o.name; break;
00207 case Option::OPTION: line += "-" + QString(o.o) + " <val> --" + o.name + " <val>"; break;
00208 default: break;
00209 }
00210 QString blank = "";
00211 blank.resize(maxlen - line.size());
00212 blank.fill(' ');
00213 line += blank + formatDesc(o.description, maxlen) + "\n";
00214 u += line;
00215 }
00216 return u;
00217 }
00218
00219 void GetOpt::parse() {
00220 QString error;
00221 if(!parse(error)) {
00222 cerr << qPrintable(error) << endl << endl << qPrintable(usage()) << endl << endl;
00223 exit(0);
00224 }
00225 }
00226
00227 bool GetOpt::assignOption(Option &o, QString arg, QString &error) {
00228 QVariant::Type type;
00229 if(o.value) type = o.value->type();
00230 if(o.string_value) type = QVariant::String;
00231 if(o.float_value) type = QVariant::Double;
00232 if(o.double_value) type = QVariant::Double;
00233 if(o.int_value) type = QVariant::Int;
00234 if(o.boolean_value) type = QVariant::Bool;
00235 QVariant v(arg);
00236
00237 if(!v.canConvert(type) || !v.convert(type)) {
00238 error = "Error while parsing option " + o.name + ": cannot convert " +
00239 arg + " to: " + v.typeName();
00240 return false;
00241 }
00242 if(o.value) *(o.value) = v;
00243 if(o.string_value) *(o.string_value) = v.toString();
00244 if(o.float_value) *(o.float_value) = v.toFloat();
00245 if(o.double_value) *(o.double_value) = v.toDouble();
00246 if(o.int_value) *(o.int_value) = v.toInt();
00247 if(o.boolean_value) *(o.boolean_value) = v.toBool();
00248 return true;
00249 }
00250
00251 bool GetOpt::parse(QString &error) {
00252 for(int i = 0; i < args.size(); i++) {
00253 QString arg = args[i];
00254 if(args[i] == "-h" || args[i] == "--help") {
00255 cout << qPrintable(usage()) << endl << qPrintable(help) << endl;
00256 exit(0);
00257 }
00258
00259 if(arg.startsWith( QString::fromLatin1( "--" ) ) ) {
00260 arg = arg.mid( 2 );
00261 if(arg.isEmpty()) {
00262 error = "'--' feature not supported, yet";
00263 return false;
00264 }
00265 Option o;
00266 if(!findArg(arg, o)) {
00267 error = "Unknown option: '" + arg + "'";
00268 return false;
00269 }
00270 if(o.type == Option::SWITCH) {
00271 *(o.boolean_value) = true;
00272 } else {
00273 i++;
00274 if(args.size() <= i) {
00275 error = "Missing value for option: " + arg;
00276 return false;
00277 }
00278 arg = args[i];
00279 if(i == args.size() || arg[0] == '-') {
00280 error = "Missing argument after option '" + arg + "'";
00281 return false;
00282 }
00283 if(!assignOption(o, arg, error))
00284 return false;
00285 }
00286
00287
00288 } else if( arg[0] == '-' ) {
00289 if(arg.size() != 2) {
00290 error = "Invalid option: " + arg;
00291 return false;
00292 }
00293 Option o;
00294 if(!findOption(arg[1].toLatin1(), o)) {
00295 error = "Unknown option: '" + arg + "'";
00296 return false;
00297 }
00298 if(o.type == Option::SWITCH) {
00299 *(o.boolean_value) = true;
00300 } else {
00301 i++;
00302 if(args.size() <= i) {
00303 error = "Missing value for option: " + arg;
00304 return false;
00305 }
00306 arg = args[i];
00307 if(i == args.size()) {
00308 error = "Missing argument after option '" + arg + "'";
00309 return false;
00310 }
00311 if(!assignOption(o, arg, error))
00312 return false;
00313
00314
00315
00316
00317
00318
00319
00320
00321 }
00322
00323 } else {
00324 arguments.push_back(arg);
00325 }
00326 }
00327
00328
00329 for(int i = 0; i < options.size(); i++) {
00330 Option &o = options[i];
00331 if(o.type != Option::ARGUMENT) continue;
00332 if(arguments.isEmpty()) {
00333 error = "Too few arguments, could not parse argument '" + o.name + "'";
00334 return false;
00335 }
00336 if(!assignOption(o, arguments.front(), error))
00337 return false;
00338 arguments.pop_front();
00339 }
00340
00341 for(int i = 0; i < options.size(); i++) {
00342 Option &o = options[i];
00343 if(o.type != Option::OPTIONAL) continue;
00344 if(arguments.isEmpty()) break;
00345 if(!assignOption(o, arguments.front(), error))
00346 return false;
00347 arguments.pop_front();
00348 }
00349 if(!arguments.isEmpty() && !unlimitedArgs) {
00350 error = "Too many arguments";
00351 return false;
00352 }
00353 return true;
00354 }
00355
00356 bool GetOpt::findOption(char c, Option &option) {
00357 for(int i = 0; i < options.size(); i++) {
00358 Option &o = options[i];
00359 if(o.type != Option::OPTION && o.type != Option::SWITCH) continue;
00360 if(o.o == c) {
00361 option = o;
00362 return true;
00363 }
00364 }
00365 return false;
00366 }
00367
00368 bool GetOpt::findArg(const QString &name, Option &option) {
00369 for(int i = 0; i < options.size(); i++) {
00370 Option &o = options[i];
00371 if(o.name == name) {
00372 option = o;
00373 return true;
00374 }
00375 }
00376 return false;
00377 }
00378
00379 QString GetOpt::formatDesc(QString desc, int len) {
00380 QString output;
00381
00382 while(!desc.isEmpty()) {
00383 int pos = desc.lastIndexOf(" ", 79 - len);
00384 if(pos == -1) {
00385 output += desc;
00386 break;
00387 }
00388 output += desc.left(pos) + "\n";
00389 QString blank;
00390 blank.resize(len);
00391 blank.fill(' ');
00392 output += blank;
00393 desc = desc.mid(pos+1);
00394 }
00395 return output;
00396 }