123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083 |
-
-
- #include "qcommandlineparser.h"
-
- #include <qcoreapplication.h>
- #include <qhash.h>
- #include <qvector.h>
- #include <qdebug.h>
- #if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
- # include <qt_windows.h>
- #endif
- #include <stdio.h>
- #include <stdlib.h>
-
- QT_BEGIN_NAMESPACE
-
- typedef QHash<QString, int> NameHash_t;
-
- class QCommandLineParserPrivate
- {
- public:
- inline QCommandLineParserPrivate()
- : singleDashWordOptionMode(QCommandLineParser::ParseAsCompactedShortOptions),
- builtinVersionOption(false),
- builtinHelpOption(false),
- needsParsing(true)
- { }
-
- bool parse(const QStringList &args);
- void checkParsed(const char *method);
- QStringList aliases(const QString &name) const;
- QString helpText() const;
- bool registerFoundOption(const QString &optionName);
- bool parseOptionValue(const QString &optionName, const QString &argument,
- QStringList::const_iterator *argumentIterator,
- QStringList::const_iterator argsEnd);
-
-
- QString errorText;
-
-
- QList<QCommandLineOption> commandLineOptionList;
-
-
- NameHash_t nameHash;
-
-
- QHash<int, QStringList> optionValuesHash;
-
-
- QStringList optionNames;
-
-
- QStringList positionalArgumentList;
-
-
- QStringList unknownOptionNames;
-
-
- QString description;
-
-
- struct PositionalArgumentDefinition
- {
- QString name;
- QString description;
- QString syntax;
- };
- QVector<PositionalArgumentDefinition> positionalArgumentDefinitions;
-
-
- QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode;
-
-
- bool builtinVersionOption;
-
-
- bool builtinHelpOption;
-
-
- bool needsParsing;
- };
-
- QStringList QCommandLineParserPrivate::aliases(const QString &optionName) const
- {
- const NameHash_t::const_iterator it = nameHash.constFind(optionName);
- if (it == nameHash.constEnd()) {
- qWarning("QCommandLineParser: option not defined: \"%s\"", qPrintable(optionName));
- return QStringList();
- }
- return commandLineOptionList.at(*it).names();
- }
-
-
-
-
- QCommandLineParser::QCommandLineParser()
- : d(new QCommandLineParserPrivate)
- {
- }
-
-
- QCommandLineParser::~QCommandLineParser()
- {
- delete d;
- }
-
-
-
-
- void QCommandLineParser::setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode)
- {
- d->singleDashWordOptionMode = singleDashWordOptionMode;
- }
-
-
- bool QCommandLineParser::addOption(const QCommandLineOption &option)
- {
- QStringList optionNames = option.names();
-
- if (!optionNames.isEmpty()) {
- foreach (const QString &name, optionNames) {
- if (d->nameHash.contains(name))
- return false;
- }
-
- d->commandLineOptionList.append(option);
-
- const int offset = d->commandLineOptionList.size() - 1;
- foreach (const QString &name, optionNames)
- d->nameHash.insert(name, offset);
-
- return true;
- }
-
- return false;
- }
-
-
- bool QCommandLineParser::addOptions(const QList<QCommandLineOption> &options)
- {
-
- bool result = true;
- for (QList<QCommandLineOption>::const_iterator it = options.begin(), end = options.end(); it != end; ++it)
- result &= addOption(*it);
- return result;
- }
-
-
- QCommandLineOption QCommandLineParser::addVersionOption()
- {
- QCommandLineOption opt(QStringList() << QString("v") << QString("version"), tr("Displays version information."));
- addOption(opt);
- d->builtinVersionOption = true;
- return opt;
- }
-
-
- QCommandLineOption QCommandLineParser::addHelpOption()
- {
- QCommandLineOption opt(QStringList()
- #ifdef Q_OS_WIN
- << QString("?")
- #endif
- << QString("h")
- << QString("help"), tr("Displays this help."));
- addOption(opt);
- d->builtinHelpOption = true;
- return opt;
- }
-
-
- void QCommandLineParser::setApplicationDescription(const QString &description)
- {
- d->description = description;
- }
-
-
- QString QCommandLineParser::applicationDescription() const
- {
- return d->description;
- }
-
-
- void QCommandLineParser::addPositionalArgument(const QString &name, const QString &description, const QString &syntax)
- {
- QCommandLineParserPrivate::PositionalArgumentDefinition arg;
- arg.name = name;
- arg.description = description;
- arg.syntax = syntax.isEmpty() ? name : syntax;
- d->positionalArgumentDefinitions.append(arg);
- }
-
-
- void QCommandLineParser::clearPositionalArguments()
- {
- d->positionalArgumentDefinitions.clear();
- }
-
-
- bool QCommandLineParser::parse(const QStringList &arguments)
- {
- return d->parse(arguments);
- }
-
-
- QString QCommandLineParser::errorText() const
- {
- if (!d->errorText.isEmpty())
- return d->errorText;
- if (d->unknownOptionNames.count() == 1)
- return tr("Unknown option '%1'.").arg(d->unknownOptionNames.first());
- if (d->unknownOptionNames.count() > 1)
- return tr("Unknown options: %1.").arg(d->unknownOptionNames.join(QString(", ")));
- return QString();
- }
-
- enum MessageType { UsageMessage, ErrorMessage };
-
- #if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
-
-
- static inline bool displayMessageBox()
- {
- if (GetConsoleWindow())
- return false;
- STARTUPINFO startupInfo;
- startupInfo.cb = sizeof(STARTUPINFO);
- GetStartupInfo(&startupInfo);
- return !(startupInfo.dwFlags & STARTF_USESTDHANDLES);
- }
- #endif
-
- static void showParserMessage(const QString &message, MessageType type)
- {
- #if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
- if (displayMessageBox()) {
- const UINT flags = MB_OK | MB_TOPMOST | MB_SETFOREGROUND
- | (type == UsageMessage ? MB_ICONINFORMATION : MB_ICONERROR);
- QString title;
- if (QCoreApplication::instance())
- title = QCoreApplication::instance()->property("applicationDisplayName").toString();
- if (title.isEmpty())
- title = QCoreApplication::applicationName();
- MessageBoxW(0, reinterpret_cast<const wchar_t *>(message.utf16()),
- reinterpret_cast<const wchar_t *>(title.utf16()), flags);
- return;
- }
- #endif
- fputs(qPrintable(message), type == UsageMessage ? stdout : stderr);
- }
-
-
- void QCommandLineParser::process(const QStringList &arguments)
- {
- if (!d->parse(arguments)) {
- showParserMessage(errorText() + QLatin1Char('\n'), ErrorMessage);
- ::exit(EXIT_FAILURE);
- }
-
- if (d->builtinVersionOption && isSet(QString("version")))
- showVersion();
-
- if (d->builtinHelpOption && isSet(QString("help")))
- showHelp(EXIT_SUCCESS);
- }
-
-
- void QCommandLineParser::process(const QCoreApplication &app)
- {
-
- Q_UNUSED(app);
- process(QCoreApplication::arguments());
- }
-
- void QCommandLineParserPrivate::checkParsed(const char *method)
- {
- if (needsParsing)
- qWarning("QCommandLineParser: call process() or parse() before %s", method);
- }
-
-
- bool QCommandLineParserPrivate::registerFoundOption(const QString &optionName)
- {
- if (nameHash.contains(optionName)) {
- optionNames.append(optionName);
- return true;
- } else {
- unknownOptionNames.append(optionName);
- return false;
- }
- }
-
-
- bool QCommandLineParserPrivate::parseOptionValue(const QString &optionName, const QString &argument,
- QStringList::const_iterator *argumentIterator, QStringList::const_iterator argsEnd)
- {
- const QLatin1Char assignChar('=');
- const NameHash_t::const_iterator nameHashIt = nameHash.constFind(optionName);
- if (nameHashIt != nameHash.constEnd()) {
- const int assignPos = argument.indexOf(assignChar);
- const NameHash_t::mapped_type optionOffset = *nameHashIt;
- const bool withValue = !commandLineOptionList.at(optionOffset).valueName().isEmpty();
- if (withValue) {
- if (assignPos == -1) {
- ++(*argumentIterator);
- if (*argumentIterator == argsEnd) {
- errorText = QCommandLineParser::tr("Missing value after '%1'.").arg(argument);
- return false;
- }
- optionValuesHash[optionOffset].append(*(*argumentIterator));
- } else {
- optionValuesHash[optionOffset].append(argument.mid(assignPos + 1));
- }
- } else {
- if (assignPos != -1) {
- errorText = QCommandLineParser::tr("Unexpected value after '%1'.").arg(argument.left(assignPos));
- return false;
- }
- }
- }
- return true;
- }
-
-
- bool QCommandLineParserPrivate::parse(const QStringList &args)
- {
- needsParsing = false;
- bool error = false;
-
- const QString doubleDashString(QString("--"));
- const QLatin1Char dashChar('-');
- const QLatin1Char assignChar('=');
-
- bool doubleDashFound = false;
- errorText.clear();
- positionalArgumentList.clear();
- optionNames.clear();
- unknownOptionNames.clear();
- optionValuesHash.clear();
-
- if (args.isEmpty()) {
- qWarning("QCommandLineParser: argument list cannot be empty, it should contain at least the executable name");
- return false;
- }
-
- QStringList::const_iterator argumentIterator = args.begin();
- ++argumentIterator;
-
- for (; argumentIterator != args.end() ; ++argumentIterator) {
- QString argument = *argumentIterator;
-
- if (doubleDashFound) {
- positionalArgumentList.append(argument);
- } else if (argument.startsWith(doubleDashString)) {
- if (argument.length() > 2) {
- QString optionName = argument.mid(2).section(assignChar, 0, 0);
- if (registerFoundOption(optionName)) {
- if (!parseOptionValue(optionName, argument, &argumentIterator, args.end()))
- error = true;
- } else {
- error = true;
- }
- } else {
- doubleDashFound = true;
- }
- } else if (argument.startsWith(dashChar)) {
- if (argument.size() == 1) {
- positionalArgumentList.append(argument);
- continue;
- }
- switch (singleDashWordOptionMode) {
- case QCommandLineParser::ParseAsCompactedShortOptions:
- {
- QString optionName;
- bool valueFound = false;
- for (int pos = 1 ; pos < argument.size(); ++pos) {
- optionName = argument.mid(pos, 1);
- if (!registerFoundOption(optionName)) {
- error = true;
- } else {
- const NameHash_t::const_iterator nameHashIt = nameHash.constFind(optionName);
- Q_ASSERT(nameHashIt != nameHash.constEnd());
- const NameHash_t::mapped_type optionOffset = *nameHashIt;
- const bool withValue = !commandLineOptionList.at(optionOffset).valueName().isEmpty();
- if (withValue) {
- if (pos + 1 < argument.size()) {
- if (argument.at(pos + 1) == assignChar)
- ++pos;
- optionValuesHash[optionOffset].append(argument.mid(pos + 1));
- valueFound = true;
- }
- break;
- }
- if (pos + 1 < argument.size() && argument.at(pos + 1) == assignChar)
- break;
- }
- }
- if (!valueFound && !parseOptionValue(optionName, argument, &argumentIterator, args.end()))
- error = true;
- break;
- }
- case QCommandLineParser::ParseAsLongOptions:
- {
- const QString optionName = argument.mid(1).section(assignChar, 0, 0);
- if (registerFoundOption(optionName)) {
- if (!parseOptionValue(optionName, argument, &argumentIterator, args.end()))
- error = true;
- } else {
- error = true;
- }
- break;
- }
- }
- } else {
- positionalArgumentList.append(argument);
- }
- if (argumentIterator == args.end())
- break;
- }
- return !error;
- }
-
-
-
- bool QCommandLineParser::isSet(const QString &name) const
- {
- d->checkParsed("isSet");
- if (d->optionNames.contains(name))
- return true;
- const QStringList aliases = d->aliases(name);
- foreach (const QString &optionName, d->optionNames) {
- if (aliases.contains(optionName))
- return true;
- }
- return false;
- }
-
-
-
- QString QCommandLineParser::value(const QString &optionName) const
- {
- d->checkParsed("value");
- const QStringList valueList = values(optionName);
-
- if (!valueList.isEmpty())
- return valueList.last();
-
- return QString();
- }
-
-
-
- QStringList QCommandLineParser::values(const QString &optionName) const
- {
- d->checkParsed("values");
- const NameHash_t::const_iterator it = d->nameHash.constFind(optionName);
- if (it != d->nameHash.constEnd()) {
- const int optionOffset = *it;
- QStringList values = d->optionValuesHash.value(optionOffset);
- if (values.isEmpty())
- values = d->commandLineOptionList.at(optionOffset).defaultValues();
- return values;
- }
-
- qWarning("QCommandLineParser: option not defined: \"%s\"", qPrintable(optionName));
- return QStringList();
- }
-
-
- bool QCommandLineParser::isSet(const QCommandLineOption &option) const
- {
-
- return !option.names().isEmpty() && isSet(option.names().first());
- }
-
-
- QString QCommandLineParser::value(const QCommandLineOption &option) const
- {
- return value(option.names().first());
- }
-
-
- QStringList QCommandLineParser::values(const QCommandLineOption &option) const
- {
- return values(option.names().first());
- }
-
-
-
- QStringList QCommandLineParser::positionalArguments() const
- {
- d->checkParsed("positionalArguments");
- return d->positionalArgumentList;
- }
-
-
-
- QStringList QCommandLineParser::optionNames() const
- {
- d->checkParsed("optionNames");
- return d->optionNames;
- }
-
-
-
- QStringList QCommandLineParser::unknownOptionNames() const
- {
- d->checkParsed("unknownOptionNames");
- return d->unknownOptionNames;
- }
-
-
- void QCommandLineParser::showVersion()
- {
- showParserMessage(QCoreApplication::applicationName() + QLatin1Char(' ')
- + QCoreApplication::applicationVersion() + QLatin1Char('\n'),
- UsageMessage);
- ::exit(EXIT_SUCCESS);
- }
-
-
- void QCommandLineParser::showHelp(int exitCode)
- {
- showParserMessage(d->helpText(), UsageMessage);
- ::exit(exitCode);
- }
-
-
- QString QCommandLineParser::helpText() const
- {
- return d->helpText();
- }
-
- static QString wrapText(const QString &names, int longestOptionNameString, const QString &description)
- {
- const QLatin1Char nl('\n');
- QString text = QString(" ") + names.leftJustified(longestOptionNameString) + QLatin1Char(' ');
- const int indent = text.length();
- int lineStart = 0;
- int lastBreakable = -1;
- const int max = 79 - indent;
- int x = 0;
- const int len = description.length();
-
- for (int i = 0; i < len; ++i) {
- ++x;
- const QChar c = description.at(i);
- if (c.isSpace())
- lastBreakable = i;
-
- int breakAt = -1;
- int nextLineStart = -1;
- if (x > max && lastBreakable != -1) {
-
- breakAt = lastBreakable;
- nextLineStart = lastBreakable + 1;
- } else if ((x > max - 1 && lastBreakable == -1) || i == len - 1) {
-
- breakAt = i + 1;
- nextLineStart = breakAt;
- } else if (c == nl) {
-
- breakAt = i;
- nextLineStart = i + 1;
- }
-
- if (breakAt != -1) {
- const int numChars = breakAt - lineStart;
-
- if (lineStart > 0)
- text += QString(indent, QLatin1Char(' '));
- text += description.midRef(lineStart, numChars).toString() + nl;
- x = 0;
- lastBreakable = -1;
- lineStart = nextLineStart;
- if (lineStart < len && description.at(lineStart).isSpace())
- ++lineStart;
- i = lineStart;
- }
- }
-
- return text;
- }
-
- QString QCommandLineParserPrivate::helpText() const
- {
- const QLatin1Char nl('\n');
- QString text;
- const QString exeName = QCoreApplication::instance()->arguments().first();
- QString usage = exeName;
- if (!commandLineOptionList.isEmpty()) {
- usage += QLatin1Char(' ');
- usage += QCommandLineParser::tr("[options]");
- }
- foreach (const PositionalArgumentDefinition &arg, positionalArgumentDefinitions) {
- usage += QLatin1Char(' ');
- usage += arg.syntax;
- }
- text += QCommandLineParser::tr("Usage: %1").arg(usage) + nl;
- if (!description.isEmpty())
- text += description + nl;
- text += nl;
- if (!commandLineOptionList.isEmpty())
- text += QCommandLineParser::tr("Options:") + nl;
- QStringList optionNameList;
- int longestOptionNameString = 0;
- foreach (const QCommandLineOption &option, commandLineOptionList) {
- QStringList optionNames;
- foreach (const QString &optionName, option.names()) {
- if (optionName.length() == 1)
- optionNames.append(QLatin1Char('-') + optionName);
- else
- optionNames.append(QString("--") + optionName);
- }
- QString optionNamesString = optionNames.join(QString(", "));
- if (!option.valueName().isEmpty())
- {
- optionNamesString += QString(" <") + option.valueName();
- if (option.defaultValues().size() != 0)
- optionNamesString += "=" + option.defaultValues()[0];
- optionNamesString += QLatin1Char('>');
- }
- optionNameList.append(optionNamesString);
- longestOptionNameString = qMax(longestOptionNameString, optionNamesString.length());
- }
- ++longestOptionNameString;
- for (int i = 0; i < commandLineOptionList.count(); ++i) {
- const QCommandLineOption &option = commandLineOptionList.at(i);
- text += wrapText(optionNameList.at(i), longestOptionNameString, option.description());
- }
- if (!positionalArgumentDefinitions.isEmpty()) {
- if (!commandLineOptionList.isEmpty())
- text += nl;
- text += QCommandLineParser::tr("Arguments:") + nl;
- foreach (const PositionalArgumentDefinition &arg, positionalArgumentDefinitions) {
- text += wrapText(arg.name, longestOptionNameString, arg.description);
- }
- }
- return text;
- }
-
- QT_END_NAMESPACE
|