00001 #include <QtCore/QCoreApplication>
00002 #include <QtCore/QQueue>
00003 #include <QtCore/QVariant>
00004 #include <QtCore/QFileInfo>
00005 #include <QDebug>
00006 #include <iostream>
00007
00008 #include "qcommandline.h"
00009
00010 const QCommandLineConfigEntry QCommandLine::helpEntry = { QCommandLine::Switch, QLatin1Char('h'), QLatin1String("help"), tr("Display this help and exit"), QCommandLine::Optional };
00011
00012 const QCommandLineConfigEntry QCommandLine::versionEntry = { QCommandLine::Switch, QLatin1Char('V'), QLatin1String("version"), tr("Display version and exit"), QCommandLine::Optional };
00013
00014 QCommandLine::QCommandLine(QObject * parent)
00015 : QObject(parent)
00016 {
00017 setArguments(QCoreApplication::instance()->arguments());
00018 }
00019
00020 QCommandLine::QCommandLine(const QCoreApplication & app,
00021 const QCommandLineConfig & config,
00022 QObject * parent)
00023 : QObject(parent)
00024 {
00025 setArguments(app.arguments());
00026 setConfig(config);
00027 enableHelp(true);
00028 enableVersion(true);
00029 }
00030
00031 QCommandLine::QCommandLine(int argc, char *argv[],
00032 const QCommandLineConfig & config,
00033 QObject * parent)
00034 : QObject(parent)
00035 {
00036 setArguments(argc, argv);
00037 setConfig(config);
00038 enableHelp(true);
00039 enableVersion(true);
00040 }
00041
00042 QCommandLine::QCommandLine(const QStringList args,
00043 const QCommandLineConfig & config,
00044 QObject * parent)
00045 : QObject(parent)
00046 {
00047 setArguments(args);
00048 setConfig(config);
00049 enableHelp(true);
00050 enableVersion(true);
00051 }
00052
00053 QCommandLine::~QCommandLine()
00054 {
00055 }
00056
00057 void
00058 QCommandLine::setConfig(const QCommandLineConfig & config)
00059 {
00060 d.config = config;
00061 }
00062
00063 void
00064 QCommandLine::setConfig(const QCommandLineConfigEntry config[])
00065 {
00066 d.config.clear();
00067
00068 while (config->type) {
00069 d.config << *config;
00070 config++;
00071 }
00072 }
00073
00074 QCommandLineConfig
00075 QCommandLine::config()
00076 {
00077 return d.config;
00078 }
00079
00080 void
00081 QCommandLine::setArguments(int argc, char *argv[])
00082 {
00083 d.args.clear();
00084 for (int i = 0; i < argc; i++)
00085 d.args.append(QLatin1String(argv[i]));
00086 }
00087
00088 void
00089 QCommandLine::setArguments(QStringList args)
00090 {
00091 d.args = args;
00092 }
00093
00094 QStringList
00095 QCommandLine::arguments()
00096 {
00097 return d.args;
00098 }
00099
00100 void
00101 QCommandLine::enableHelp(bool enable)
00102 {
00103 d.help = enable;
00104 }
00105
00106 bool
00107 QCommandLine::helpEnabled()
00108 {
00109 return d.help;
00110 }
00111
00112 void
00113 QCommandLine::enableVersion(bool enable)
00114 {
00115 d.version = enable;
00116 }
00117
00118 bool
00119 QCommandLine::versionEnabled()
00120 {
00121 return d.version;
00122 }
00123
00124 bool
00125 QCommandLine::parse()
00126 {
00127 QMap < QString, QCommandLineConfigEntry > conf;
00128 QMap < QString, QCommandLineConfigEntry > confLong;
00129 QQueue < QCommandLineConfigEntry > params;
00130 QMap < QString, QList < QString > > optionsFound;
00131 QMap < QString, int > switchsFound;
00132 QStringList args = d.args;
00133
00134 bool allparam = false;
00135
00136 foreach (QCommandLineConfigEntry entry, d.config) {
00137 if (entry.type != QCommandLine::Param && entry.shortName == QLatin1Char('\0'))
00138 qWarning() << QLatin1String("QCommandLine: Empty shortname detected");
00139 if (entry.longName.isEmpty())
00140 qWarning() << QLatin1String("QCommandLine: Empty shortname detected");
00141 if (entry.type != QCommandLine::Param && conf.find(entry.shortName) != conf.end())
00142 qWarning() << QLatin1String("QCommandLine: Duplicated shortname detected ") << entry.shortName;
00143 if (conf.find(entry.longName) != conf.end())
00144 qWarning() << QLatin1String("QCommandLine: Duplicated longname detected ") << entry.shortName;
00145
00146 if (entry.type == QCommandLine::Param)
00147 params << entry;
00148 else
00149 conf[entry.shortName] = entry;
00150 confLong[entry.longName] = entry;
00151 }
00152
00153 if (d.help) {
00154 conf[helpEntry.shortName] = helpEntry;
00155 confLong[helpEntry.longName] = helpEntry;
00156 }
00157
00158 if (d.version) {
00159 conf[versionEntry.shortName] = versionEntry;
00160 confLong[versionEntry.longName] = versionEntry;
00161 }
00162
00163 for (int i = 1; i < args.size(); ++i) {
00164 QString arg = args[i];
00165 bool param = true, shrt = false, stay = false, forward = false;
00166
00167
00168 if (allparam)
00169 param = true;
00170 else if (arg.startsWith(QLatin1String("--"))) {
00171 param = false;
00172 shrt = false;
00173 arg = arg.mid(2);
00174 } else if (arg.startsWith(QLatin1Char('-')) || arg.startsWith(QLatin1Char('+'))) {
00175 if (arg.startsWith(QLatin1Char('+')))
00176 allparam = true;
00177 param = false;
00178 shrt = true;
00179
00180 if (arg.size() > 2) {
00181 args[i] = arg.mid(0, 1) + arg.mid(2);
00182 arg = arg.mid(1, 1);
00183 } else {
00184 arg = arg.mid(1);
00185 }
00186 }
00187
00188
00189 if (param) {
00190 if (!params.size()) {
00191 emit parseError(tr("Unknown param: %1").arg(arg));
00192 return false;
00193 }
00194
00195 QCommandLineConfigEntry & entry = params.first();
00196
00197 if (entry.flags & QCommandLine::Mandatory) {
00198 entry.flags = (QCommandLine::Flags) (entry.flags & ~QCommandLine::Mandatory);
00199 entry.flags = (QCommandLine::Flags) (entry.flags | QCommandLine::Optional);
00200 }
00201
00202 emit paramFound(entry.longName, arg);
00203
00204 if (!(entry.flags & QCommandLine::Multiple))
00205 params.dequeue();
00206
00207 } else {
00208 QString key;
00209 QString value;
00210 int idx = arg.indexOf(QLatin1Char('='));
00211
00212 if (idx != -1) {
00213 key = arg.mid(0, idx);
00214 value = arg.mid(idx + 1);
00215 } else {
00216 key = arg;
00217 }
00218
00219 QMap < QString, QCommandLineConfigEntry > & c = shrt ? conf : confLong;
00220
00221 if (c.find(key) == c.end()) {
00222 emit parseError(tr("Unknown option: %1").arg(key));
00223 return false;
00224 }
00225
00226 QCommandLineConfigEntry & entry = c[key];
00227
00228 if (entry.type == QCommandLine::Switch) {
00229 if (entry.flags & QCommandLine::Multiple)
00230 switchsFound[entry.longName]++;
00231 else
00232 switchsFound[entry.longName] = 1;
00233 } else {
00234 if (stay) {
00235 emit parseError(tr("Option %1 need a value").arg(key));
00236 return false;
00237 }
00238
00239 if (idx == -1) {
00240 if (i+1 < args.size() && !args[i+1].startsWith(QLatin1Char('-'))) {
00241 value = args[i+1];
00242 forward = true;
00243 } else {
00244 emit parseError(tr("Option %1 need a value").arg(key));
00245 return false;
00246 }
00247 }
00248
00249 if (!(entry.flags & QCommandLine::Multiple))
00250 optionsFound[entry.longName].clear();
00251 optionsFound[entry.longName].append(value);
00252 }
00253
00254 if (entry.flags & QCommandLine::Mandatory) {
00255 entry.flags = (QCommandLine::Flags) (entry.flags & ~QCommandLine::Mandatory);
00256 entry.flags = (QCommandLine::Flags) (entry.flags | QCommandLine::Optional);
00257 conf[entry.shortName] = entry;
00258 confLong[entry.shortName] = entry;
00259 }
00260 }
00261
00262 if (stay)
00263 i--;
00264 else if (forward)
00265 i++;
00266 }
00267
00268 foreach (QCommandLineConfigEntry entry, params) {
00269 if (entry.flags & QCommandLine::Mandatory) {
00270 emit parseError(tr("Param %1 is mandatory").arg(entry.longName));
00271 return false;
00272 }
00273 }
00274
00275 foreach (QCommandLineConfigEntry entry, conf.values()) {
00276 if (entry.flags & QCommandLine::Mandatory) {
00277 QString type;
00278
00279 if (entry.type == QCommandLine::Switch)
00280 type = tr("Switch");
00281 if (entry.type == QCommandLine::Option)
00282 type = tr("Option");
00283
00284 emit parseError(tr("%1 %2 is mandatory").arg(type).arg(entry.longName));
00285 return false;
00286 }
00287 }
00288
00289 foreach (QString key, switchsFound.keys()) {
00290 for (int i = 0; i < switchsFound[key]; i++) {
00291 if (d.help && key == helpEntry.longName)
00292 showHelp();
00293 if (d.version && key == versionEntry.longName)
00294 showVersion();
00295 emit switchFound(key);
00296 }
00297 }
00298
00299 foreach (QString key, optionsFound.keys()) {
00300 foreach (QString opt, optionsFound[key])
00301 emit optionFound(key, opt);
00302 } return true;
00303 }
00304
00305 void
00306 QCommandLine::addOption(const QChar & shortName,
00307 const QString & longName,
00308 const QString & descr,
00309 QCommandLine::Flags flags)
00310 {
00311 QCommandLineConfigEntry entry;
00312
00313 entry.type = QCommandLine::Option;
00314 entry.shortName = shortName;
00315 entry.longName = longName;
00316 entry.descr = descr;
00317 entry.flags = flags;
00318 d.config << entry;
00319 }
00320
00321 void
00322 QCommandLine::addSwitch(const QChar & shortName,
00323 const QString & longName,
00324 const QString & descr,
00325 QCommandLine::Flags flags)
00326 {
00327 QCommandLineConfigEntry entry;
00328
00329 entry.type = QCommandLine::Switch;
00330 entry.shortName = shortName;
00331 entry.longName = longName;
00332 entry.descr = descr;
00333 entry.flags = flags;
00334 d.config << entry;
00335 }
00336
00337 void
00338 QCommandLine::addParam(const QString & name,
00339 const QString & descr,
00340 QCommandLine::Flags flags)
00341 {
00342 QCommandLineConfigEntry entry;
00343
00344 entry.type = QCommandLine::Param;
00345 entry.longName = name;
00346 entry.descr = descr;
00347 entry.flags = flags;
00348 d.config << entry;
00349 }
00350
00351 void
00352 QCommandLine::removeOption(const QString & name)
00353 {
00354 int i;
00355
00356 for (i = 0; i < d.config.size(); ++i) {
00357 if (d.config[i].type == QCommandLine::Option &&
00358 (d.config[i].shortName == name.at(0) || d.config[i].longName == name)) {
00359 d.config.removeAt(i);
00360 return ;
00361 }
00362 }
00363 }
00364
00365 void
00366 QCommandLine::removeSwitch(const QString & name)
00367 {
00368 int i;
00369
00370 for (i = 0; i < d.config.size(); ++i) {
00371 if (d.config[i].type == QCommandLine::Switch &&
00372 (d.config[i].shortName == name.at(0) || d.config[i].longName == name)) {
00373 d.config.removeAt(i);
00374 return ;
00375 }
00376 }
00377 }
00378
00379 void
00380 QCommandLine::removeParam(const QString & name)
00381 {
00382 int i;
00383
00384 for (i = 0; i < d.config.size(); ++i) {
00385 if (d.config[i].type == QCommandLine::Param &&
00386 (d.config[i].shortName == name.at(0) || d.config[i].longName == name)) {
00387 d.config.removeAt(i);
00388 return ;
00389 }
00390 }
00391 }
00392
00393
00394 QString
00395 QCommandLine::help(bool logo)
00396 {
00397 QString h;
00398
00399 if (logo)
00400 h = version() + QLatin1String("\n");
00401 h = QLatin1String("Usage:\n ");
00402
00403 if (!d.args.isEmpty())
00404 h += QFileInfo(d.args[0]).baseName();
00405 else
00406 h += QCoreApplication::applicationName();
00407 h.append(QLatin1String(" [switchs] [options]"));
00408
00409 foreach (QCommandLineConfigEntry entry, d.config) {
00410 if (entry.type == QCommandLine::Option) {
00411 if (entry.flags & QCommandLine::Mandatory)
00412 h.append(QLatin1String(" --") + entry.longName + QLatin1String("=<val>"));
00413 }
00414 if (entry.type == QCommandLine::Param) {
00415 h.append(QLatin1String(" "));
00416 if (entry.flags & QCommandLine::Optional)
00417 h.append(QLatin1String("["));
00418 h.append(entry.longName);
00419 if (entry.flags & QCommandLine::Multiple)
00420 h.append(QLatin1String(" [") + entry.longName + QLatin1String(" [...]]"));
00421 if (entry.flags & QCommandLine::Optional)
00422 h.append(QLatin1String("]"));
00423 }
00424 }
00425 h.append(QLatin1String("\n\n"));
00426
00427 h.append(QLatin1String("Options:\n"));
00428
00429 QStringList vals;
00430 QStringList descrs;
00431 int max = 0;
00432
00433 foreach (QCommandLineConfigEntry entry, d.config) {
00434 QString val;
00435
00436 if (entry.type == QCommandLine::Option)
00437 val = QLatin1String("-") + QString(entry.shortName) +
00438 QLatin1String(",--") + entry.longName + QLatin1String("=<val>");
00439 if (entry.type == QCommandLine::Switch)
00440 val = QLatin1String("-") + QString(entry.shortName) + QLatin1String(",--") + entry.longName;
00441 if (entry.type == QCommandLine::Param)
00442 val = entry.longName;
00443
00444 if (val.size() > max)
00445 max = val.size();
00446
00447 vals.append(val);
00448 descrs.append(entry.descr + QLatin1String("\n"));
00449 }
00450
00451 for (int i = 0; i < vals.size(); ++i) {
00452 h.append(QLatin1String(" "));
00453 h.append(vals[i]);
00454 h.append(QString(QLatin1String(" ")).repeated(max - vals[i].size() + 2));
00455 h.append(descrs[i]);
00456 }
00457
00458 h.append(tr("\nMandatory arguments to long options are mandatory for short options too.\n"));
00459
00460 return h;
00461 }
00462
00463 QString
00464 QCommandLine::version()
00465 {
00466 QString v;
00467
00468 v = QCoreApplication::applicationName() + QLatin1Char(' ');
00469 v += QCoreApplication::applicationVersion();
00470 if (!QCoreApplication::organizationDomain().isEmpty()
00471 || !QCoreApplication::organizationName().isEmpty())
00472 v = v + QLatin1String(" - ") +
00473 QCoreApplication::organizationDomain() + QLatin1String(" ") +
00474 QCoreApplication::organizationDomain();
00475 return v + QLatin1Char('\n');
00476 }
00477
00478 void
00479 QCommandLine::showHelp(bool quit, int returnCode)
00480 {
00481 std::cerr << qPrintable(help());
00482 if (quit) {
00483
00484 exit(returnCode);
00485 }
00486 }
00487
00488 void
00489 QCommandLine::showVersion(bool quit, int returnCode)
00490 {
00491 std::cerr << qPrintable(version());
00492 if (quit) {
00493 exit(returnCode);
00494 }
00495 }
00496