implement commandline parsing
This commit is contained in:
parent
cf0e78b46d
commit
c523a2c752
@ -117,6 +117,7 @@ gui/browserdialog.cpp
|
|||||||
util/pathutils.cpp
|
util/pathutils.cpp
|
||||||
util/osutils.cpp
|
util/osutils.cpp
|
||||||
util/userutil.cpp
|
util/userutil.cpp
|
||||||
|
util/cmdutils.cpp
|
||||||
|
|
||||||
java/javautils.cpp
|
java/javautils.cpp
|
||||||
java/annotations.cpp
|
java/annotations.cpp
|
||||||
@ -147,6 +148,7 @@ util/apputils.h
|
|||||||
util/pathutils.h
|
util/pathutils.h
|
||||||
util/osutils.h
|
util/osutils.h
|
||||||
util/userutil.h
|
util/userutil.h
|
||||||
|
util/cmdutils.h
|
||||||
|
|
||||||
multimc_pragma.h
|
multimc_pragma.h
|
||||||
|
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
/* Copyright 2013 MultiMC Contributors
|
/* Copyright 2013 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Authors: Andrew Okin
|
||||||
|
* Peterix
|
||||||
|
* Orochimarufan <orochimarufan.x3@gmail.com>
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -19,9 +23,10 @@
|
|||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
|
#include <QApplication>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
#include "util/osutils.h"
|
#include "util/osutils.h"
|
||||||
#include "util/userutil.h"
|
#include "util/userutil.h"
|
||||||
@ -168,9 +173,9 @@ void MainWindow::on_actionMakeDesktopShortcut_triggered()
|
|||||||
QString name("Test");
|
QString name("Test");
|
||||||
name = QInputDialog::getText(this, tr("MultiMC Shortcut"), tr("Enter a Shortcut Name."), QLineEdit::Normal, name);
|
name = QInputDialog::getText(this, tr("MultiMC Shortcut"), tr("Enter a Shortcut Name."), QLineEdit::Normal, name);
|
||||||
|
|
||||||
Util::createShortCut(Util::getDesktopDir(), "test", QStringList() << "-d" << "lol", name, "application-x-octet-stream");
|
Util::createShortCut(Util::getDesktopDir(), QApplication::instance()->applicationFilePath(), QStringList() << "-dl" << QDir::currentPath() << "test", name, "application-x-octet-stream");
|
||||||
|
|
||||||
QMessageBox::warning(this, "Stupidness", "A Dummy Shortcut was created. the current instance model doesnt allow for anything more");
|
QMessageBox::warning(this, "Not useful", "A Dummy Shortcut was created. it will not do anything productive");
|
||||||
}
|
}
|
||||||
|
|
||||||
// BrowserDialog
|
// BrowserDialog
|
||||||
|
86
main.cpp
86
main.cpp
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
/* Copyright 2013 MultiMC Contributors
|
/* Copyright 2013 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,26 +15,105 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gui/mainwindow.h"
|
#include <iostream>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
#include "gui/mainwindow.h"
|
||||||
|
|
||||||
#include "data/appsettings.h"
|
#include "data/appsettings.h"
|
||||||
|
|
||||||
#include "data/loginresponse.h"
|
#include "data/loginresponse.h"
|
||||||
|
|
||||||
|
#include "util/cmdutils.h"
|
||||||
|
|
||||||
|
using namespace Util::Commandline;
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
// initialize Qt
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
app.setOrganizationName("Forkk");
|
app.setOrganizationName("Forkk");
|
||||||
app.setApplicationName("MultiMC 5");
|
app.setApplicationName("MultiMC 5");
|
||||||
|
|
||||||
|
// Print app header
|
||||||
|
std::cout << "MultiMC 5" << std::endl;
|
||||||
|
std::cout << "(c) 2013 MultiMC contributors" << std::endl << std::endl;
|
||||||
|
|
||||||
|
// Commandline parsing
|
||||||
|
Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
|
||||||
|
|
||||||
|
// --help
|
||||||
|
parser.addSwitch("help");
|
||||||
|
parser.addShortOpt("help", 'h');
|
||||||
|
parser.addDocumentation("help", "displays help on command line parameters");
|
||||||
|
// --dir
|
||||||
|
parser.addOption("dir", app.applicationDirPath());
|
||||||
|
parser.addShortOpt("dir", 'd');
|
||||||
|
parser.addDocumentation("dir", "use the supplied directory as MultiMC root instead of the binary location (use '.' for current)");
|
||||||
|
// --update
|
||||||
|
parser.addOption("update");
|
||||||
|
parser.addShortOpt("update", 'u');
|
||||||
|
parser.addDocumentation("update", "replaces the given file with the running executable", "<path>");
|
||||||
|
// --quietupdate
|
||||||
|
parser.addSwitch("quietupdate");
|
||||||
|
parser.addShortOpt("quietupdate", 'U');
|
||||||
|
parser.addDocumentation("quietupdate", "doesn't restart MultiMC after installing updates");
|
||||||
|
// --launch
|
||||||
|
parser.addOption("launch");
|
||||||
|
parser.addShortOpt("launch", 'l');
|
||||||
|
parser.addDocumentation("launch", "tries to launch the given instance", "<inst>");
|
||||||
|
|
||||||
|
// parse the arguments
|
||||||
|
QHash<QString, QVariant> args;
|
||||||
|
try {
|
||||||
|
args = parser.parse(app.arguments());
|
||||||
|
} catch(ParsingError e) {
|
||||||
|
std::cerr << "CommandLineError: " << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// display help and exit
|
||||||
|
if (args["help"].toBool()) {
|
||||||
|
std::cout << qPrintable(parser.compileHelp(app.arguments()[0]));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update
|
||||||
|
// Note: cwd is always the current executable path!
|
||||||
|
if (!args["update"].isNull())
|
||||||
|
{
|
||||||
|
std::cout << "Performing MultiMC update: " << qPrintable(args["update"].toString()) << std::endl;
|
||||||
|
QDir::setCurrent(app.applicationDirPath());
|
||||||
|
QFile file(app.applicationFilePath());
|
||||||
|
file.copy(args["update"].toString());
|
||||||
|
if(args["quietupdate"].toBool())
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// change directory
|
||||||
|
QDir::setCurrent(args["dir"].toString());
|
||||||
|
|
||||||
|
// launch instance.
|
||||||
|
if (!args["launch"].isNull())
|
||||||
|
{
|
||||||
|
std::cout << "Launching instance: " << qPrintable(args["launch"].toString()) << std::endl;
|
||||||
|
// TODO: make it launch the an instance.
|
||||||
|
// needs the new instance model to be complete
|
||||||
|
std::cerr << "Launching Instances is not implemented yet!" << std::endl;
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load settings
|
||||||
settings = new AppSettings(&app);
|
settings = new AppSettings(&app);
|
||||||
|
|
||||||
// Register meta types.
|
// Register meta types.
|
||||||
qRegisterMetaType<LoginResponse>("LoginResponse");
|
qRegisterMetaType<LoginResponse>("LoginResponse");
|
||||||
|
|
||||||
|
// show window
|
||||||
MainWindow mainWin;
|
MainWindow mainWin;
|
||||||
mainWin.show();
|
mainWin.show();
|
||||||
|
|
||||||
|
// loop
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
433
util/cmdutils.cpp
Normal file
433
util/cmdutils.cpp
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
/* Copyright 2013 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cmdutils.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file utils/cmdutils.cpp
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Util {
|
||||||
|
namespace Commandline {
|
||||||
|
|
||||||
|
Parser::Parser(FlagStyle flagStyle, ArgumentStyle argStyle)
|
||||||
|
{
|
||||||
|
m_flagStyle = flagStyle;
|
||||||
|
m_argStyle = argStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// styles setter/getter
|
||||||
|
void Parser::setArgumentStyle(ArgumentStyle style)
|
||||||
|
{
|
||||||
|
m_argStyle = style;
|
||||||
|
}
|
||||||
|
ArgumentStyle Parser::argumentStyle()
|
||||||
|
{
|
||||||
|
return m_argStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::setFlagStyle(FlagStyle style)
|
||||||
|
{
|
||||||
|
m_flagStyle = style;
|
||||||
|
}
|
||||||
|
FlagStyle Parser::flagStyle()
|
||||||
|
{
|
||||||
|
return m_flagStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup methods
|
||||||
|
void Parser::addSwitch(QString name, bool def) throw (const char *)
|
||||||
|
{
|
||||||
|
if (m_params.contains(name))
|
||||||
|
throw "Name not unique";
|
||||||
|
|
||||||
|
OptionDef *param = new OptionDef;
|
||||||
|
param->type = OptionType::Switch;
|
||||||
|
param->name = name;
|
||||||
|
param->metavar = QString("<%1>").arg(name);
|
||||||
|
param->def = def;
|
||||||
|
|
||||||
|
m_options[name] = param;
|
||||||
|
m_params[name] = (CommonDef *)param;
|
||||||
|
m_optionList.append(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::addOption(QString name, QVariant def) throw (const char *)
|
||||||
|
{
|
||||||
|
if (m_params.contains(name))
|
||||||
|
throw "Name not unique";
|
||||||
|
|
||||||
|
OptionDef *param = new OptionDef;
|
||||||
|
param->type = OptionType::Option;
|
||||||
|
param->name = name;
|
||||||
|
param->metavar = QString("<%1>").arg(name);
|
||||||
|
param->def = def;
|
||||||
|
|
||||||
|
m_options[name] = param;
|
||||||
|
m_params[name] = (CommonDef *)param;
|
||||||
|
m_optionList.append(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::addArgument(QString name, bool required, QVariant def) throw (const char *)
|
||||||
|
{
|
||||||
|
if (m_params.contains(name))
|
||||||
|
throw "Name not unique";
|
||||||
|
|
||||||
|
PositionalDef *param = new PositionalDef;
|
||||||
|
param->name = name;
|
||||||
|
param->def = def;
|
||||||
|
param->required = required;
|
||||||
|
|
||||||
|
m_positionals.append(param);
|
||||||
|
m_params[name] = (CommonDef *)param;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::addDocumentation(QString name, QString doc, QString metavar) throw (const char *)
|
||||||
|
{
|
||||||
|
if (!m_params.contains(name))
|
||||||
|
throw "Name does not exist";
|
||||||
|
|
||||||
|
CommonDef *param = m_params[name];
|
||||||
|
param->doc = doc;
|
||||||
|
if (!metavar.isNull())
|
||||||
|
param->metavar = metavar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::addShortOpt(QString name, QChar flag) throw (const char *)
|
||||||
|
{
|
||||||
|
if (!m_params.contains(name))
|
||||||
|
throw "Name does not exist";
|
||||||
|
if (!m_options.contains(name))
|
||||||
|
throw "Name is not an Option or Swtich";
|
||||||
|
|
||||||
|
OptionDef *param = m_options[name];
|
||||||
|
m_flags[flag] = param;
|
||||||
|
param->flag = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// help methods
|
||||||
|
QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags)
|
||||||
|
{
|
||||||
|
QStringList help;
|
||||||
|
help << compileUsage(progName, useFlags) << "\r\n";
|
||||||
|
|
||||||
|
// positionals
|
||||||
|
if (!m_positionals.isEmpty())
|
||||||
|
{
|
||||||
|
help << "\r\n";
|
||||||
|
help << "Positional arguments:\r\n";
|
||||||
|
QListIterator<PositionalDef *> it2(m_positionals);
|
||||||
|
while(it2.hasNext())
|
||||||
|
{
|
||||||
|
PositionalDef *param = it2.next();
|
||||||
|
help << " " << param->metavar;
|
||||||
|
help << " " << QString(helpIndent - param->name.length() - 1, ' ');
|
||||||
|
help << param->doc << "\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options
|
||||||
|
if (!m_optionList.isEmpty())
|
||||||
|
{
|
||||||
|
help << "\r\n";
|
||||||
|
QString optPrefix, flagPrefix;
|
||||||
|
getPrefix(optPrefix, flagPrefix);
|
||||||
|
|
||||||
|
help << "Options & Switches:\r\n";
|
||||||
|
QListIterator<OptionDef *> it(m_optionList);
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
OptionDef *option = it.next();
|
||||||
|
help << " ";
|
||||||
|
int nameLength = optPrefix.length() + option->name.length();
|
||||||
|
if (!option->flag.isNull())
|
||||||
|
{
|
||||||
|
nameLength += 3 + flagPrefix.length();
|
||||||
|
help << flagPrefix << option->flag << ", ";
|
||||||
|
}
|
||||||
|
help << optPrefix << option->name;
|
||||||
|
if (option->type == OptionType::Option)
|
||||||
|
{
|
||||||
|
QString arg = QString("%1%2").arg(((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
|
||||||
|
nameLength += arg.length();
|
||||||
|
help << arg;
|
||||||
|
}
|
||||||
|
help << " " << QString(helpIndent - nameLength - 1, ' ');
|
||||||
|
help << option->doc << "\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return help.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Parser::compileUsage(QString progName, bool useFlags)
|
||||||
|
{
|
||||||
|
QStringList usage;
|
||||||
|
usage << "Usage: " << progName;
|
||||||
|
|
||||||
|
QString optPrefix, flagPrefix;
|
||||||
|
getPrefix(optPrefix, flagPrefix);
|
||||||
|
|
||||||
|
// options
|
||||||
|
QListIterator<OptionDef *> it(m_optionList);
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
OptionDef *option = it.next();
|
||||||
|
usage << " [";
|
||||||
|
if (!option->flag.isNull() && useFlags)
|
||||||
|
usage << flagPrefix << option->flag;
|
||||||
|
else
|
||||||
|
usage << optPrefix << option->name;
|
||||||
|
if (option->type == OptionType::Option)
|
||||||
|
usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
|
||||||
|
usage << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// arguments
|
||||||
|
QListIterator<PositionalDef *> it2(m_positionals);
|
||||||
|
while(it2.hasNext())
|
||||||
|
{
|
||||||
|
PositionalDef *param = it2.next();
|
||||||
|
usage << " " << (param->required ? "<" : "[");
|
||||||
|
usage << param->name;
|
||||||
|
usage << (param->required ? ">" : "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
return usage.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsing
|
||||||
|
QHash<QString, QVariant> Parser::parse(QStringList argv) throw (ParsingError)
|
||||||
|
{
|
||||||
|
QHash<QString, QVariant> map;
|
||||||
|
|
||||||
|
QStringListIterator it(argv);
|
||||||
|
QString programName = it.next();
|
||||||
|
|
||||||
|
QString optionPrefix;
|
||||||
|
QString flagPrefix;
|
||||||
|
QListIterator<PositionalDef *> positionals(m_positionals);
|
||||||
|
QStringList expecting;
|
||||||
|
|
||||||
|
getPrefix(optionPrefix, flagPrefix);
|
||||||
|
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
QString arg = it.next();
|
||||||
|
|
||||||
|
if (!expecting.isEmpty())
|
||||||
|
// we were expecting an argument
|
||||||
|
{
|
||||||
|
QString name = expecting.first();
|
||||||
|
|
||||||
|
if (map.contains(name))
|
||||||
|
throw ParsingError(QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
|
||||||
|
|
||||||
|
map[name] = QVariant(arg);
|
||||||
|
|
||||||
|
expecting.removeFirst();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith(optionPrefix))
|
||||||
|
// we have an option
|
||||||
|
{
|
||||||
|
//qDebug("Found option %s", qPrintable(arg));
|
||||||
|
|
||||||
|
QString name = arg.mid(optionPrefix.length());
|
||||||
|
QString equals;
|
||||||
|
|
||||||
|
if ((m_argStyle == ArgumentStyle::Equals || m_argStyle == ArgumentStyle::SpaceAndEquals) && name.contains("="))
|
||||||
|
{
|
||||||
|
int i = name.indexOf("=");
|
||||||
|
equals = name.mid(i+1);
|
||||||
|
name = name.left(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_options.contains(name))
|
||||||
|
{
|
||||||
|
if (map.contains(name))
|
||||||
|
throw ParsingError(QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
|
||||||
|
|
||||||
|
OptionDef *option = m_options[name];
|
||||||
|
if (option->type == OptionType::Switch)
|
||||||
|
map[name] = true;
|
||||||
|
else //if (option->type == OptionType::Option)
|
||||||
|
{
|
||||||
|
if (m_argStyle == ArgumentStyle::Space)
|
||||||
|
expecting.append(name);
|
||||||
|
else if (!equals.isNull())
|
||||||
|
map[name] = equals;
|
||||||
|
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
|
||||||
|
expecting.append(name);
|
||||||
|
else
|
||||||
|
throw ParsingError(QString("Option %2%1 reqires an argument.").arg(name, optionPrefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith(flagPrefix))
|
||||||
|
// we have (a) flag(s)
|
||||||
|
{
|
||||||
|
//qDebug("Found flags %s", qPrintable(arg));
|
||||||
|
|
||||||
|
QString flags = arg.mid(flagPrefix.length());
|
||||||
|
QString equals;
|
||||||
|
|
||||||
|
if ((m_argStyle == ArgumentStyle::Equals || m_argStyle == ArgumentStyle::SpaceAndEquals) && flags.contains("="))
|
||||||
|
{
|
||||||
|
int i = flags.indexOf("=");
|
||||||
|
equals = flags.mid(i+1);
|
||||||
|
flags = flags.left(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < flags.length(); i++)
|
||||||
|
{
|
||||||
|
QChar flag = flags.at(i);
|
||||||
|
|
||||||
|
if (!m_flags.contains(flag))
|
||||||
|
throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix));
|
||||||
|
|
||||||
|
OptionDef *option = m_flags[flag];
|
||||||
|
|
||||||
|
if (map.contains(option->name))
|
||||||
|
throw ParsingError(QString("Option %2%1 was given multiple times").arg(option->name, optionPrefix));
|
||||||
|
|
||||||
|
if (option->type == OptionType::Switch)
|
||||||
|
map[option->name] = true;
|
||||||
|
else //if (option->type == OptionType::Option)
|
||||||
|
{
|
||||||
|
if (m_argStyle == ArgumentStyle::Space)
|
||||||
|
expecting.append(option->name);
|
||||||
|
else if (!equals.isNull())
|
||||||
|
if (i == flags.length()-1)
|
||||||
|
map[option->name] = equals;
|
||||||
|
else
|
||||||
|
throw ParsingError(QString("Flag %4%2 of Argument-requiring Option %1 not last flag in %4%3").arg(option->name, flag, flags, flagPrefix));
|
||||||
|
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
|
||||||
|
expecting.append(option->name);
|
||||||
|
else
|
||||||
|
throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)").arg(option->name, flag, flagPrefix));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be a positional argument
|
||||||
|
if (!positionals.hasNext())
|
||||||
|
throw ParsingError(QString("Don't know what to do with '%1'").arg(arg));
|
||||||
|
|
||||||
|
PositionalDef *param = positionals.next();
|
||||||
|
|
||||||
|
map[param->name] = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we're missing something
|
||||||
|
if (!expecting.isEmpty())
|
||||||
|
throw ParsingError(QString("Was still expecting arguments for %2%1").arg(expecting.join(QString(", ")+optionPrefix), optionPrefix));
|
||||||
|
|
||||||
|
while (positionals.hasNext())
|
||||||
|
{
|
||||||
|
PositionalDef *param = positionals.next();
|
||||||
|
if (param->required)
|
||||||
|
throw ParsingError(QString("Missing required positional argument '%1'").arg(param->name));
|
||||||
|
else
|
||||||
|
map[param->name] = param->def;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill out gaps
|
||||||
|
QListIterator<OptionDef *> iter(m_optionList);
|
||||||
|
while (iter.hasNext())
|
||||||
|
{
|
||||||
|
OptionDef *option = iter.next();
|
||||||
|
if (!map.contains(option->name))
|
||||||
|
map[option->name] = option->def;
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
//clear defs
|
||||||
|
void Parser::clear()
|
||||||
|
{
|
||||||
|
m_flags.clear();
|
||||||
|
m_params.clear();
|
||||||
|
m_options.clear();
|
||||||
|
|
||||||
|
QMutableListIterator<OptionDef *> it(m_optionList);
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
OptionDef *option = it.next();
|
||||||
|
it.remove();
|
||||||
|
delete option;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutableListIterator<PositionalDef *> it2(m_positionals);
|
||||||
|
while(it2.hasNext())
|
||||||
|
{
|
||||||
|
PositionalDef *arg = it2.next();
|
||||||
|
it2.remove();
|
||||||
|
delete arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Destructor
|
||||||
|
Parser::~Parser()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
//getPrefix
|
||||||
|
void Parser::getPrefix(QString &opt, QString &flag)
|
||||||
|
{
|
||||||
|
if (m_flagStyle == FlagStyle::Windows)
|
||||||
|
opt = flag = "/";
|
||||||
|
else if (m_flagStyle == FlagStyle::Unix)
|
||||||
|
opt = flag = "-";
|
||||||
|
//else if (m_flagStyle == FlagStyle::GNU)
|
||||||
|
else {
|
||||||
|
opt = "--";
|
||||||
|
flag = "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsingError
|
||||||
|
ParsingError::ParsingError(const QString &what) throw()
|
||||||
|
{
|
||||||
|
m_what = what;
|
||||||
|
}
|
||||||
|
ParsingError::ParsingError(const ParsingError &e) throw()
|
||||||
|
{
|
||||||
|
m_what = e.m_what;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ParsingError::what() const throw()
|
||||||
|
{
|
||||||
|
return m_what.toLocal8Bit().data();
|
||||||
|
}
|
||||||
|
QString ParsingError::qwhat() const throw()
|
||||||
|
{
|
||||||
|
return m_what;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
237
util/cmdutils.h
Normal file
237
util/cmdutils.h
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
/* Copyright 2013 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CMDUTILS_H
|
||||||
|
#define CMDUTILS_H
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file utils/cmdutils.h
|
||||||
|
* @brief commandline parsing utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Util {
|
||||||
|
namespace Commandline {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The FlagStyle enum
|
||||||
|
* Specifies how flags are decorated
|
||||||
|
*/
|
||||||
|
enum class FlagStyle {
|
||||||
|
GNU, /**< --option and -o (GNU Style) */
|
||||||
|
Unix, /**< -option and -o (Unix Style) */
|
||||||
|
Windows, /**< /option and /o (Windows Style) */
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
Default = Windows
|
||||||
|
#else
|
||||||
|
Default = GNU
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The ArgumentStyle enum
|
||||||
|
*/
|
||||||
|
enum class ArgumentStyle {
|
||||||
|
Space, /**< --option=value */
|
||||||
|
Equals, /**< --option value */
|
||||||
|
SpaceAndEquals, /**< --option[= ]value */
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
Default = Equals
|
||||||
|
#else
|
||||||
|
Default = SpaceAndEquals
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The ParsingError class
|
||||||
|
*/
|
||||||
|
class ParsingError : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ParsingError(const QString &what) throw();
|
||||||
|
ParsingError(const ParsingError &e) throw();
|
||||||
|
~ParsingError() throw() {}
|
||||||
|
char *what() const throw();
|
||||||
|
QString qwhat() const throw();
|
||||||
|
private:
|
||||||
|
QString m_what;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The Parser class
|
||||||
|
*/
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Parser constructor
|
||||||
|
* @param flagStyle the FlagStyle to use in this Parser
|
||||||
|
* @param argStyle the ArgumentStyle to use in this Parser
|
||||||
|
*/
|
||||||
|
Parser(FlagStyle flagStyle=FlagStyle::Default, ArgumentStyle argStyle=ArgumentStyle::Default);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set the flag style
|
||||||
|
* @param style
|
||||||
|
*/
|
||||||
|
void setFlagStyle(FlagStyle style);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the flag style
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
FlagStyle flagStyle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set the argument style
|
||||||
|
* @param style
|
||||||
|
*/
|
||||||
|
void setArgumentStyle(ArgumentStyle style);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the argument style
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
ArgumentStyle argumentStyle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief define a boolean switch
|
||||||
|
* @param name the parameter name
|
||||||
|
* @param def the default value
|
||||||
|
*/
|
||||||
|
void addSwitch(QString name, bool def=false) throw (const char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief define an option that takes an additional argument
|
||||||
|
* @param name the parameter name
|
||||||
|
* @param def the default value
|
||||||
|
*/
|
||||||
|
void addOption(QString name, QVariant def=QVariant()) throw (const char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief define a positional argument
|
||||||
|
* @param name the parameter name
|
||||||
|
* @param required wether this argument is required
|
||||||
|
* @param def the default value
|
||||||
|
*/
|
||||||
|
void addArgument(QString name, bool required=true, QVariant def=QVariant()) throw (const char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief adds a flag to an existing parameter
|
||||||
|
* @param name the (existing) parameter name
|
||||||
|
* @param flag the flag character
|
||||||
|
* @see addSwitch addArgument addOption
|
||||||
|
* Note: any one parameter can only have one flag
|
||||||
|
*/
|
||||||
|
void addShortOpt(QString name, QChar flag) throw (const char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief adds documentation to a Parameter
|
||||||
|
* @param name the parameter name
|
||||||
|
* @param metavar a string to be displayed as placeholder for the value
|
||||||
|
* @param doc a QString containing the documentation
|
||||||
|
*/
|
||||||
|
void addDocumentation(QString name, QString doc, QString metavar=QString()) throw (const char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief generate a help message
|
||||||
|
* @param progName the program name to use in the help message
|
||||||
|
* @param helpIndent how much the parameter documentation should be indented
|
||||||
|
* @param flagsInUsage whether we should use flags instead of options in the usage
|
||||||
|
* @return a help message
|
||||||
|
*/
|
||||||
|
QString compileHelp(QString progName, int helpIndent=22, bool flagsInUsage=true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief generate a short usage message
|
||||||
|
* @param progName the program name to use in the usage message
|
||||||
|
* @param useFlags whether we should use flags instead of options
|
||||||
|
* @return a usage message
|
||||||
|
*/
|
||||||
|
QString compileUsage(QString progName, bool useFlags=true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief parse
|
||||||
|
* @param argv a QStringList containing the program ARGV
|
||||||
|
* @return a QHash mapping argument names to their values
|
||||||
|
*/
|
||||||
|
QHash<QString, QVariant> parse(QStringList argv) throw (ParsingError);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief clear all definitions
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
~Parser();
|
||||||
|
|
||||||
|
private:
|
||||||
|
FlagStyle m_flagStyle;
|
||||||
|
ArgumentStyle m_argStyle;
|
||||||
|
|
||||||
|
enum class OptionType {
|
||||||
|
Switch,
|
||||||
|
Option
|
||||||
|
};
|
||||||
|
|
||||||
|
// Important: the common part MUST BE COMMON ON ALL THREE structs
|
||||||
|
struct CommonDef {
|
||||||
|
QString name;
|
||||||
|
QString doc;
|
||||||
|
QString metavar;
|
||||||
|
QVariant def;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OptionDef {
|
||||||
|
// common
|
||||||
|
QString name;
|
||||||
|
QString doc;
|
||||||
|
QString metavar;
|
||||||
|
QVariant def;
|
||||||
|
// option
|
||||||
|
OptionType type;
|
||||||
|
QChar flag;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PositionalDef {
|
||||||
|
// common
|
||||||
|
QString name;
|
||||||
|
QString doc;
|
||||||
|
QString metavar;
|
||||||
|
QVariant def;
|
||||||
|
// positional
|
||||||
|
bool required;
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<QString, OptionDef *> m_options;
|
||||||
|
QHash<QChar, OptionDef *> m_flags;
|
||||||
|
QHash<QString, CommonDef *> m_params;
|
||||||
|
QList<PositionalDef *> m_positionals;
|
||||||
|
QList<OptionDef *> m_optionList;
|
||||||
|
|
||||||
|
void getPrefix(QString &opt, QString &flag);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CMDUTILS_H
|
Loading…
Reference in New Issue
Block a user