mirror of https://github.com/vitalif/phantomjs
536 lines
13 KiB
C++
536 lines
13 KiB
C++
/* 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(" [switchs] [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);
|
|
}
|
|
}
|
|
|