Files
QtAV/examples/common/qoptions.cpp
T
wang-bin ce21c55b6e qt6 support
no qml. examples are wip.
fix #1381, #1294, #1389
2022-01-08 17:09:43 +08:00

379 lines
11 KiB
C++

/******************************************************************************
QOptions: make command line options easy. https://github.com/wang-bin/qoptions
Copyright (C) 2011-2022 Wang Bin <wbsecg1@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
******************************************************************************/
#include "qoptions.h"
#include <QtCore/QStringList>
#include <QtCore/QtDebug>
QOption::QOption()
{}
QOption::QOption(const char *name, const QVariant &defaultValue, Type type, const QString &description)
:mType(type),mDescription(description),mDefaultValue(defaultValue)
{
setName(QLatin1String(name));
}
QOption::QOption(const char *name, Type type, const QString &description)
:mType(type),mDescription(description),mDefaultValue(QVariant())
{
//if (mType==QOption::NoToken)
// mValue = false;
setName(QLatin1String(name));
}
/*
QOption::QOption(const char *name, const QVariant& value, Type type, const QString &description)
:mType(type),mDescription(description),mDefaultValue(*value),mValue(value)
{qDebug("%s %s %d", __FILE__, __FUNCTION__, __LINE__);
setName(name);
}
*/
QString QOption::shortName() const
{
return mShortName;
}
QString QOption::longName() const
{
return mLongName;
}
QString QOption::formatName() const
{
if (mLongName.isEmpty())
return QLatin1String("-") + mShortName;
if (mShortName.isEmpty())
return QLatin1String("--") + mLongName;
return QString::fromLatin1("-%1 [--%2]").arg(mShortName).arg(mLongName);
}
QString QOption::description() const
{
return mDescription;
}
QVariant QOption::value() const
{
if (mValue.isValid())
return mValue;
return mDefaultValue;
}
void QOption::setValue(const QVariant &value)
{
mValue = value;
}
bool QOption::isSet() const
{
return mValue.isValid();
}
bool QOption::isValid() const
{
return !shortName().isEmpty() || !longName().isEmpty();
}
void QOption::setType(QOption::Type type)
{
mType = type;
if (mType==NoToken)
mValue = false;
}
QOption::Type QOption::type() const
{
return mType;
}
QString QOption::help() const
{
QString message = formatName();
if (type()==QOption::SingleToken)
message.append(QLatin1String(" value"));
else if (type()==QOption::MultiToken)
message.append(QLatin1String(" value1 ... valueN"));
message = QString::fromLatin1("%1").arg(message, -33);
message.append(mDescription);
if (mDefaultValue.isValid() && !mDefaultValue.toString().isEmpty())
message.append(QLatin1String(" (default: ") + mDefaultValue.toString() + QLatin1String(")"));
return message;
}
bool QOption::operator <(const QOption& o) const
{
return mType < o.type() || mShortName < o.shortName() || mLongName < o.longName() || mDescription < o.description();
}
static QString get_short(const QString& name)
{
if (name.startsWith(QLatin1String("--")))
return QString();
if (name.startsWith(QLatin1String("-")))
return name.mid(1);
return name;
}
static QString get_long(const QString& name)
{
if (name.startsWith(QLatin1String("--")))
return name.mid(2);
if (name.startsWith(QLatin1String("-")))
return QString();
return name;
}
void QOption::setName(const QString &name)
{
int comma = name.indexOf(QLatin1Char(','));
if (comma>0) {
QString name1 = name.left(comma);
QString name2 = name.mid(comma+1);
if (name1.startsWith(QLatin1String("--"))) {
mLongName = name1.mid(2);
mShortName = get_short(name2);
} else if (name1.startsWith(QLatin1String("-"))) {
mShortName = name1.mid(1);
mLongName = get_long(name2);
} else {
if (name2.startsWith(QLatin1String("--"))) {
mLongName = name2.mid(2);
mShortName = name1;
} else if (name2.startsWith(QLatin1String("-"))) {
mShortName = name2.mid(1);
mLongName = name1;
} else {
mShortName = name2;
mLongName = name1;
}
}
} else {
if (name.startsWith(QLatin1String("--")))
mLongName = name.mid(2);
else if (name.startsWith(QLatin1String("-")))
mShortName = name.mid(1);
else
mShortName = name;
}
}
QOptions::QOptions()
{
}
QOptions::~QOptions()
{
mOptionGroupMap.clear();
mOptions.clear();
}
bool QOptions::parse(int argc, const char *const*argv)
{
if (mOptionGroupMap.isEmpty())
return false;
if (argc==1)
return true;
bool result = true;
QStringList args;
for (int i=1;i<argc;++i) {
args.append(QString::fromLocal8Bit(argv[i]));
}
QStringList::Iterator it = args.begin();
QList<QOption>::Iterator it_list;
mOptions = mOptionGroupMap.keys();
while (it != args.end()) {
if (it->startsWith(QLatin1String("--"))) {
int e = it->indexOf(QLatin1Char('='));
for (it_list = mOptions.begin(); it_list != mOptions.end(); ++it_list) {
if (it_list->longName() == it->mid(2,e-2)) {
if (it_list->type()==QOption::NoToken) {
it_list->setValue(true);
//qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString()));
it = args.erase(it);
break;
}
if (e>0) { //
it_list->setValue(it->mid(e+1));
//qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString()));
} else {
it = args.erase(it);
if (it == args.end())
break;
it_list->setValue(*it);
//qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString()));
}
it = args.erase(it);
break;
}
}
if (it_list == mOptions.end()) {
qWarning() << "unknown option: " << *it;
result = false;
++it;
}
//handle unknown option
} else if (it->startsWith(QLatin1Char('-'))) {
for (it_list = mOptions.begin(); it_list != mOptions.end(); ++it_list) {
QString sname = it_list->shortName();
int sname_len = sname.length(); //usally is 1
//TODO: startsWith(-height,-h) Not endsWith, -oabco
if (it->mid(1).compare(sname) == 0) {
if (it_list->type() == QOption::NoToken) {
it_list->setValue(true);
it = args.erase(it);
break;
}
if (it->length() == sname_len+1) {//-o abco
it = args.erase(it);
if (it == args.end())
break;
it_list->setValue(*it);
//qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString()));
} else {
it_list->setValue(it->mid(sname_len+1));
//qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString()));
}
it = args.erase(it);
break;
}
}
if (it_list==mOptions.end()) {
qWarning() << "unknown option: " << *it;
result = false;
++it;
}
//handle unknown option
} else {
qWarning() << "unknown option: " << *it;
++it;
}
}
if (!result) {
print();
}
return result;
}
QOptions& QOptions::add(const QString &group_description)
{
mCurrentDescription = group_description;
return *this;
}
QOptions& QOptions::addDescription(const QString &description)
{
mDescription = description;
return *this;
}
QOptions& QOptions::operator ()(const char* name, const QString& description)
{
QOption op(name, QOption::NoToken, description);
mOptions.append(op);
mOptionGroupMap.insert(op, mCurrentDescription);
return *this;
}
QOptions& QOptions::operator ()(const char* name, QOption::Type type, const QString& description)
{
QOption op(name, type, description);
mOptions.append(op);
mOptionGroupMap.insert(op, mCurrentDescription);
return *this;
}
QOptions& QOptions::operator ()(const char* name, const QVariant& defaultValue, const QString& description)
{
QOption op(name, defaultValue, QOption::SingleToken, description);
mOptions.append(op);
mOptionGroupMap.insert(op, mCurrentDescription);
return *this;
}
QOptions& QOptions::operator ()(const char* name, const QVariant& defaultValue, QOption::Type type, const QString& description)
{
QOption op(name, defaultValue, type, description);
mOptions.append(op);
mOptionGroupMap.insert(op, mCurrentDescription);
return *this;
}
/*
QOptions& QOptions::operator ()(const char* name, const QVariant& value, QOption::Type type, const QString& description)
{
QOption op(name, value, type, description);
mOptions.append(op);
mOptionGroupMap.insert(op, mCurrentDescription);
return *this;
}
*/
QOption QOptions::option(const QString &name) const
{
if (mOptions.isEmpty())
return QOption();
QList<QOption>::ConstIterator it_list;
for (it_list=mOptions.constBegin(); it_list!=mOptions.constEnd(); ++it_list) {
if (it_list->shortName()==name || it_list->longName()==name) {
return *it_list;
}
}
return QOption();
}
QVariant QOptions::value(const QString& name) const
{
return option(name).value();
}
QVariant QOptions::operator [](const QString& name) const
{
return value(name);
}
QString QOptions::help() const
{
QString message = mDescription;
QStringList groups = mOptionGroupMap.values();
groups.removeDuplicates();
QList<QString>::ConstIterator it;
QList<QOption>::ConstIterator it_op;
for (it=groups.constBegin(); it!=groups.constEnd(); ++it) {
message.append(QLatin1String("\n")).append(*it);
QList<QOption> options = mOptionGroupMap.keys(*it);
for (it_op=options.constBegin();it_op!=options.constEnd();++it_op)
message.append(QLatin1String("\n ")).append(it_op->help());
}
return message;
}
void QOptions::print() const
{
qDebug("%s", help().toUtf8().constData());
}