/src/qcommandline/qcommandline.cpp
C++ | 534 lines | 432 code | 76 blank | 26 comment | 111 complexity | 46ac3a204c0e8d349116aae228255c1a MD5 | raw file
- /* This file is part of QCommandLine
- *
- * Copyright (C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
- #include <QtCore/QCoreApplication>
- #include <QtCore/QQueue>
- #include <QtCore/QVariant>
- #include <QtCore/QFileInfo>
- #include <QDebug>
- #include <iostream>
- #include "qcommandline.h"
- const QCommandLineConfigEntry QCommandLine::helpEntry = { QCommandLine::Switch, QLatin1Char('h'), QLatin1String("help"), tr("Display this help and exit"), QCommandLine::Optional };
- const QCommandLineConfigEntry QCommandLine::versionEntry = { QCommandLine::Switch, QLatin1Char('V'), QLatin1String("version"), tr("Display version and exit"), QCommandLine::Optional };
- QCommandLine::QCommandLine(QObject * parent)
- : QObject(parent), d(new QCommandLinePrivate)
- {
- setArguments(QCoreApplication::instance()->arguments());
- }
- QCommandLine::QCommandLine(const QCoreApplication & app,
- const QCommandLineConfig & config,
- QObject * parent)
- : QObject(parent), d(new QCommandLinePrivate)
- {
- setArguments(app.arguments());
- setConfig(config);
- enableHelp(true);
- enableVersion(true);
- }
- QCommandLine::QCommandLine(int argc, char *argv[],
- const QCommandLineConfig & config,
- QObject * parent)
- : QObject(parent), d(new QCommandLinePrivate)
- {
- setArguments(argc, argv);
- setConfig(config);
- enableHelp(true);
- enableVersion(true);
- }
- QCommandLine::QCommandLine(const QStringList args,
- const QCommandLineConfig & config,
- QObject * parent)
- : QObject(parent), d(new QCommandLinePrivate)
- {
- setArguments(args);
- setConfig(config);
- enableHelp(true);
- enableVersion(true);
- }
- QCommandLine::~QCommandLine()
- {
- delete d;
- }
- void
- QCommandLine::setConfig(const QCommandLineConfig & config)
- {
- d->config = config;
- }
- void
- QCommandLine::setConfig(const QCommandLineConfigEntry config[])
- {
- d->config.clear();
- while (config->type) {
- d->config << *config;
- config++;
- }
- }
- QCommandLineConfig
- QCommandLine::config()
- {
- return d->config;
- }
- void
- QCommandLine::setArguments(int argc, char *argv[])
- {
- d->args.clear();
- for (int i = 0; i < argc; i++)
- d->args.append(QLatin1String(argv[i]));
- }
- void
- QCommandLine::setArguments(QStringList args)
- {
- d->args = args;
- }
- QStringList
- QCommandLine::arguments()
- {
- return d->args;
- }
- void
- QCommandLine::enableHelp(bool enable)
- {
- d->help = enable;
- }
- bool
- QCommandLine::helpEnabled()
- {
- return d->help;
- }
- void
- QCommandLine::enableVersion(bool enable)
- {
- d->version = enable;
- }
- bool
- QCommandLine::versionEnabled()
- {
- return d->version;
- }
- bool
- QCommandLine::parse()
- {
- QMap < QString, QCommandLineConfigEntry > conf;
- QMap < QString, QCommandLineConfigEntry > confLong;
- QQueue < QCommandLineConfigEntry > params;
- QMap < QString, QList < QString > > optionsFound;
- QMap < QString, int > switchsFound;
- QStringList options, switchs;
- QStringList args = d->args;
- bool allparam = false;
- foreach (QCommandLineConfigEntry entry, d->config) {
- #if 0
- if (entry.type != QCommandLine::Param && entry.shortName == QLatin1Char('\0'))
- qWarning() << QLatin1String("QCommandLine: Empty shortname detected");
- if (entry.longName.isEmpty())
- qWarning() << QLatin1String("QCommandLine: Empty shortname detected");
- if (entry.type != QCommandLine::Param && conf.find(entry.shortName) != conf.end())
- qWarning() << QLatin1String("QCommandLine: Duplicated shortname detected ") << entry.shortName;
- #endif
- if (conf.find(entry.longName) != conf.end())
- qWarning() << QLatin1String("QCommandLine: Duplicated longname detected ") << entry.shortName;
- if (entry.type == QCommandLine::Param)
- params << entry;
- else
- conf[entry.shortName] = entry;
- confLong[entry.longName] = entry;
- }
- if (d->help) {
- conf[helpEntry.shortName] = helpEntry;
- confLong[helpEntry.longName] = helpEntry;
- }
- if (d->version) {
- conf[versionEntry.shortName] = versionEntry;
- confLong[versionEntry.longName] = versionEntry;
- }
- for (int i = 1; i < args.size(); ++i) {
- QString arg = args[i];
- bool param = true, shrt = false, stay = false, forward = false;
- /* A '+' was found, all remaining options are params */
- if (allparam)
- param = true;
- else if (arg.startsWith(QLatin1String("--"))) {
- param = false;
- shrt = false;
- arg = arg.mid(2);
- } else if (arg.startsWith(QLatin1Char('-')) || arg.startsWith(QLatin1Char('+'))) {
- if (arg.startsWith(QLatin1Char('+')))
- allparam = true;
- param = false;
- shrt = true;
- /* Handle stacked args like `tar -xzf` */
- if (arg.size() > 2) {
- args[i] = arg.mid(0, 2);
- args.insert(i + 1, arg.mid(0, 1) + arg.mid(2));
- arg = arg.mid(1, 1);
- } else {
- arg = arg.mid(1);
- }
- }
- /* Handle params */
- if (param) {
- if (!params.size()) {
- emit parseError(tr("Unknown param: %1").arg(arg));
- return false;
- }
- QCommandLineConfigEntry & entry = params.first();
- if (entry.flags & QCommandLine::Mandatory) {
- entry.flags = (QCommandLine::Flags) (entry.flags & ~QCommandLine::Mandatory);
- entry.flags = (QCommandLine::Flags) (entry.flags | QCommandLine::Optional);
- }
- if (entry.flags & QCommandLine::ParameterFence) {
- allparam = true;
- }
- emit paramFound(entry.longName, arg);
- if (!(entry.flags & QCommandLine::Multiple))
- params.dequeue();
- } else { /* Options and switchs* */
- QString key;
- QString value;
- int idx = arg.indexOf(QLatin1Char('='));
- if (idx != -1) {
- key = arg.mid(0, idx);
- value = arg.mid(idx + 1);
- } else {
- key = arg;
- }
- QMap < QString, QCommandLineConfigEntry > & c = shrt ? conf : confLong;
- if (c.find(key) == c.end()) {
- emit parseError(tr("Unknown option: %1").arg(key));
- return false;
- }
- QCommandLineConfigEntry & entry = c[key];
- if (entry.type == QCommandLine::Switch) {
- if (!switchsFound.contains(entry.longName))
- switchs << entry.longName;
- if (entry.flags & QCommandLine::Multiple)
- switchsFound[entry.longName]++;
- else
- switchsFound[entry.longName] = 1;
- } else {
- if (stay) {
- emit parseError(tr("Option %1 need a value").arg(key));
- return false;
- }
- if (idx == -1) {
- if (i+1 < args.size() && !args[i+1].startsWith(QLatin1Char('-'))) {
- value = args[i+1];
- forward = true;
- } else {
- emit parseError(tr("Option %1 need a value").arg(key));
- return false;
- }
- }
- if (!optionsFound.contains(entry.longName))
- options << entry.longName;
- if (!(entry.flags & QCommandLine::Multiple))
- optionsFound[entry.longName].clear();
- optionsFound[entry.longName].append(value);
- }
- if (entry.flags & QCommandLine::Mandatory) {
- entry.flags = (QCommandLine::Flags) (entry.flags & ~QCommandLine::Mandatory);
- entry.flags = (QCommandLine::Flags) (entry.flags | QCommandLine::Optional);
- conf[entry.shortName] = entry;
- confLong[entry.shortName] = entry;
- }
- }
- /* Stay here, stacked args */
- if (stay)
- i--;
- else if (forward)
- i++;
- }
- foreach (QCommandLineConfigEntry entry, params) {
- if (entry.flags & QCommandLine::Mandatory) {
- emit parseError(tr("Param %1 is mandatory").arg(entry.longName));
- return false;
- }
- }
- foreach (QCommandLineConfigEntry entry, conf.values()) {
- if (entry.flags & QCommandLine::Mandatory) {
- QString type;
- if (entry.type == QCommandLine::Switch)
- type = tr("Switch");
- if (entry.type == QCommandLine::Option)
- type = tr("Option");
- emit parseError(tr("%1 %2 is mandatory").arg(type).arg(entry.longName));
- return false;
- }
- }
- foreach (QString key, switchs) {
- for (int i = 0; i < switchsFound[key]; i++) {
- if (d->help && key == helpEntry.longName)
- showHelp();
- if (d->version && key == versionEntry.longName)
- showVersion();
- emit switchFound(key);
- }
- }
- foreach (QString key, options) {
- foreach (QString opt, optionsFound[key])
- emit optionFound(key, opt);
- } return true;
- }
- void
- QCommandLine::addOption(const QChar & shortName,
- const QString & longName,
- const QString & descr,
- QCommandLine::Flags flags)
- {
- QCommandLineConfigEntry entry;
- entry.type = QCommandLine::Option;
- entry.shortName = shortName;
- entry.longName = longName;
- entry.descr = descr;
- entry.flags = flags;
- d->config << entry;
- }
- void
- QCommandLine::addSwitch(const QChar & shortName,
- const QString & longName,
- const QString & descr,
- QCommandLine::Flags flags)
- {
- QCommandLineConfigEntry entry;
- entry.type = QCommandLine::Switch;
- entry.shortName = shortName;
- entry.longName = longName;
- entry.descr = descr;
- entry.flags = flags;
- d->config << entry;
- }
- void
- QCommandLine::addParam(const QString & name,
- const QString & descr,
- QCommandLine::Flags flags)
- {
- QCommandLineConfigEntry entry;
- entry.type = QCommandLine::Param;
- entry.longName = name;
- entry.descr = descr;
- entry.flags = flags;
- d->config << entry;
- }
- void
- QCommandLine::removeOption(const QString & name)
- {
- int i;
- for (i = 0; i < d->config.size(); ++i) {
- if (d->config[i].type == QCommandLine::Option &&
- (d->config[i].shortName == name.at(0) || d->config[i].longName == name)) {
- d->config.removeAt(i);
- return ;
- }
- }
- }
- void
- QCommandLine::removeSwitch(const QString & name)
- {
- int i;
- for (i = 0; i < d->config.size(); ++i) {
- if (d->config[i].type == QCommandLine::Switch &&
- (d->config[i].shortName == name.at(0) || d->config[i].longName == name)) {
- d->config.removeAt(i);
- return ;
- }
- }
- }
- void
- QCommandLine::removeParam(const QString & name)
- {
- int i;
- for (i = 0; i < d->config.size(); ++i) {
- if (d->config[i].type == QCommandLine::Param &&
- (d->config[i].shortName == name.at(0) || d->config[i].longName == name)) {
- d->config.removeAt(i);
- return ;
- }
- }
- }
- QString
- QCommandLine::help(bool logo)
- {
- QString h;
- if (logo)
- h = version() + QLatin1String("\n");
- h = QLatin1String("Usage:\n ");
- /* Executable name */
- if (!d->args.isEmpty())
- h += QFileInfo(d->args[0]).baseName();
- else
- h += QCoreApplication::applicationName();
- h.append(QLatin1String(" [switches] [options]"));
- /* Arguments, short */
- foreach (QCommandLineConfigEntry entry, d->config) {
- if (entry.type == QCommandLine::Option) {
- if (entry.flags & QCommandLine::Mandatory)
- h.append(QLatin1String(" --") + entry.longName + QLatin1String("=<val>"));
- }
- if (entry.type == QCommandLine::Param) {
- h.append(QLatin1String(" "));
- if (entry.flags & QCommandLine::Optional)
- h.append(QLatin1String("["));
- h.append(entry.longName);
- if (entry.flags & QCommandLine::Multiple)
- h.append(QLatin1String(" [") + entry.longName + QLatin1String(" [...]]"));
- if (entry.flags & QCommandLine::Optional)
- h.append(QLatin1String("]"));
- }
- }
- h.append(QLatin1String("\n\n"));
- h.append(QLatin1String("Options:\n"));
- QStringList vals;
- QStringList descrs;
- int max = 0;
- foreach (QCommandLineConfigEntry entry, d->config) {
- QString val;
- if (entry.type == QCommandLine::Option) {
- if (entry.shortName != QLatin1Char('\0'))
- val = QLatin1String("-") + QString(entry.shortName) + QLatin1Char(',');
- val += QLatin1String("--") + entry.longName + QLatin1String("=<val>");
- }
- if (entry.type == QCommandLine::Switch)
- val = QLatin1String("-") + QString(entry.shortName) + QLatin1String(",--") + entry.longName;
- #if 0
- if (entry.type == QCommandLine::Param)
- val = entry.longName;
- #endif
- if (val.size() > max)
- max = val.size();
- vals.append(val);
- descrs.append(entry.descr + QLatin1String("\n"));
- }
- for (int i = 0; i < vals.size(); ++i) {
- if (vals[i].isEmpty()) continue;
- h.append(QLatin1String(" "));
- h.append(vals[i]);
- h.append(QString(QLatin1String(" ")).repeated(max - vals[i].size() + 2));
- h.append(descrs[i]);
- }
- #if 0
- h.append(tr("\nMandatory arguments to long options are mandatory for short options too.\n"));
- #endif
- return h;
- }
- QString
- QCommandLine::version()
- {
- QString v;
- v = QCoreApplication::applicationName() + QLatin1Char(' ');
- v += QCoreApplication::applicationVersion();
- if (!QCoreApplication::organizationDomain().isEmpty()
- || !QCoreApplication::organizationName().isEmpty())
- v = v + QLatin1String(" - ") +
- QCoreApplication::organizationDomain() + QLatin1String(" ") +
- QCoreApplication::organizationDomain();
- return v + QLatin1Char('\n');
- }
- void
- QCommandLine::showHelp(bool quit, int returnCode)
- {
- std::cerr << qPrintable(help());
- if (quit) {
- // Can't call QApplication::exit() here, because we may be called before app.exec()
- exit(returnCode);
- }
- }
- void
- QCommandLine::showVersion(bool quit, int returnCode)
- {
- std::cerr << qPrintable(version());
- if (quit) {
- exit(returnCode);
- }
- }