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