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.b = 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;
00060 assert(!findOption(s, option));
00061 assert(!findArg(name, option));
00062 option.type = Option::OPTION;
00063 option.o = s;
00064 option.name = name;
00065 option.description = description;
00066 option.value = v;
00067 options.push_back(option);
00068 }
00069
00070 void GetOpt::addArgument(const QString &name, const QString &description, QVariant *v) {
00071 Option option;
00072 assert(!findArg(name, option));
00073 option.type = Option::ARGUMENT;
00074 option.name = name;
00075 option.description = description;
00076 option.value = v;
00077 options.push_back(option);
00078 }
00079
00080 void GetOpt::addOptionalArgument(const QString &name, const QString &description, QVariant *v) {
00081 Option option;
00082 assert(!findArg(name, option));
00083 option.type = Option::OPTIONAL;
00084 option.name = name;
00085 option.description = description;
00086 option.value = v;
00087 options.push_back(option);
00088 }
00089
00090 QString &GetOpt::applicationName() {
00091 return appname;
00092 }
00093
00094
00095 QString GetOpt::usage() {
00096 QString u = "Usage: " + appname;
00097
00098 bool has_optionals = false;
00099 bool has_options = false;
00100 for(int i = 0; i < options.size(); i++) {
00101 if(options[i].type == Option::OPTION) has_options = true;
00102 if(options[i].type == Option::OPTIONAL) has_optionals = true;
00103 if(options[i].type != Option::ARGUMENT) continue;
00104 u += " <" + options[i].name + ">";
00105 }
00106
00107 if(has_optionals) {
00108 u += " [";
00109 for(int i = 0; i < options.size(); i++) {
00110 if(options[i].type != Option::OPTIONAL) continue;
00111 u += "<" + options[i].name + ">";
00112 }
00113 u += "]";
00114 }
00115 if(has_options) {
00116 u += " [-";
00117 for(int i = 0; i < options.size(); i++) {
00118 if(options[i].type != Option::OPTION) continue;
00119 u += options[i].o;
00120 }
00121 u += "]";
00122 }
00123 u += "\n\n";
00124
00125 int maxlen = 0;
00126 for(int i = 0; i < options.size(); i++) {
00127 Option &o = options[i];
00128 int len = o.name.size() + 2;
00129 switch(o.type) {
00130 case Option::ARGUMENT:
00131 case Option::OPTIONAL: break;
00132 case Option::SWITCH: len += 5; break;
00133 case Option::OPTION: len += 16; break;
00134 default: break;
00135 }
00136 if(len > maxlen) maxlen = len;
00137 }
00138
00139 for(int i = 0; i < options.size(); i++) {
00140 Option &o = options[i];
00141 QString line = "";
00142 switch(o.type) {
00143 case Option::ARGUMENT:
00144 case Option::OPTIONAL: line += o.name; break;
00145 case Option::SWITCH: line += "-" + QString(o.o) + " --" + o.name; break;
00146 case Option::OPTION: line += "-" + QString(o.o) + " <val> --" + o.name + " <val>"; break;
00147 default: break;
00148 }
00149 QString blank = "";
00150 blank.resize(maxlen - line.size());
00151 blank.fill(' ');
00152 line += blank + formatDesc(o.description, maxlen) + "\n";
00153 u += line;
00154 }
00155 return u;
00156 }
00157
00158 void GetOpt::parse() {
00159 QString error;
00160 if(!parse(error)) {
00161 cerr << qPrintable(error) << endl << endl << qPrintable(usage()) << endl << endl;
00162 exit(0);
00163 }
00164 }
00165
00166 bool GetOpt::parse(QString &error) {
00167 for(int i = 0; i < args.size(); i++) {
00168 QString arg = args[i];
00169 if(args[i] == "-h" || args[i] == "--help") {
00170 cout << qPrintable(usage()) << endl << qPrintable(help) << endl;
00171 exit(0);
00172 }
00173
00174 if(arg.startsWith( QString::fromLatin1( "--" ) ) ) {
00175 arg = arg.mid( 2 );
00176 if(arg.isEmpty()) {
00177 error = "'--' feature not supported, yet";
00178 return false;
00179 }
00180 Option o;
00181 if(!findArg(arg, o)) {
00182 error = "Unknown option: '" + arg + "'";
00183 return false;
00184 }
00185 if(o.type == Option::SWITCH) {
00186 *(o.b) = true;
00187 } else {
00188 i++;
00189 if(args.size() <= i) {
00190 error = "Missing value for option: " + arg;
00191 return false;
00192 }
00193 arg = args[i];
00194 if(i == args.size() || arg[0] == '-') {
00195 error = "Missing argument after option '" + arg + "'";
00196 return false;
00197 }
00198 QVariant v(arg);
00199 if(!v.canConvert(o.value->type()) || !v.convert(o.value->type())) {
00200 error = "Error while parsing option " + o.name + ": cannot convert " +
00201 arg + " to: " + o.value->typeName();
00202 return false;
00203 }
00204 *(o.value) = v;
00205 }
00206
00207
00208 } else if( arg[0] == '-' ) {
00209 if(arg.size() != 2) {
00210 error = "Invalid option: " + arg;
00211 return false;
00212 }
00213 Option o;
00214 if(!findOption(arg[1].toAscii(), o)) {
00215 error = "Unknown option: '" + arg + "'";
00216 return false;
00217 }
00218 if(o.type == Option::SWITCH) {
00219 *(o.b) = true;
00220 } else {
00221 i++;
00222 if(args.size() <= i) {
00223 error = "Missing value for option: " + arg;
00224 return false;
00225 }
00226 arg = args[i];
00227 if(i == args.size() || arg[0] == '-') {
00228 error = "Missing argument after option '" + arg + "'";
00229 return false;
00230 }
00231 QVariant v(arg);
00232 if(!v.canConvert(o.value->type()) || !v.convert(o.value->type())) {
00233 error = "Error while parsing option " + o.name + ": cannot convert " +
00234 arg + " to: " + o.value->typeName();
00235 return false;
00236 }
00237 *(o.value) = v;
00238 }
00239
00240 } else {
00241 arguments.push_back(arg);
00242 }
00243 }
00244
00245 for(int i = 0; i < options.size(); i++) {
00246 Option &o = options[i];
00247 if(o.type != Option::ARGUMENT) continue;
00248 if(arguments.isEmpty()) {
00249 error = "Too few arguments, could not parse argument '" + o.name + "'";
00250 return false;
00251 }
00252 *(o.value) = arguments.front();
00253 arguments.pop_front();
00254 }
00255
00256 for(int i = 0; i < options.size(); i++) {
00257 Option &o = options[i];
00258 if(o.type != Option::OPTIONAL) continue;
00259 if(arguments.isEmpty()) break;
00260 *(o.value) = arguments.front();
00261 arguments.pop_front();
00262 }
00263
00264 for(int i = 0; i < options.size(); i++) {
00265 Option &o = options[i];
00266 if(o.type != Option::ARGUMENT) continue;
00267 if(arguments.isEmpty()) break;
00268 *(o.value) = arguments.front();
00269 arguments.pop_front();
00270 }
00271 if(!arguments.isEmpty() && !unlimitedArgs) {
00272 error = "Too many arguments";
00273 return false;
00274 }
00275 return true;
00276 }
00277
00278 bool GetOpt::findOption(char c, Option &option) {
00279 for(int i = 0; i < options.size(); i++) {
00280 Option &o = options[i];
00281 if(o.type != Option::OPTION && o.type != Option::SWITCH) continue;
00282 if(o.o == c) {
00283 option = o;
00284 return true;
00285 }
00286 }
00287 return false;
00288 }
00289
00290 bool GetOpt::findArg(const QString &name, Option &option) {
00291 for(int i = 0; i < options.size(); i++) {
00292 Option &o = options[i];
00293 if(o.name == name) {
00294 option = o;
00295 return true;
00296 }
00297 }
00298 return false;
00299 }
00300
00301 QString GetOpt::formatDesc(QString desc, int len) {
00302 QString output;
00303
00304 while(!desc.isEmpty()) {
00305 int pos = desc.lastIndexOf(" ", 79 - len);
00306 if(pos == -1) {
00307 output += desc;
00308 break;
00309 }
00310 output += desc.left(pos) + "\n";
00311 QString blank;
00312 blank.resize(len);
00313 blank.fill(' ');
00314 output += blank;
00315 desc = desc.mid(pos+1);
00316 }
00317 return output;
00318 }