2015-09-06 20:19:09 +02:00
|
|
|
#include "./argumentparser.h"
|
|
|
|
#include "./commandlineutils.h"
|
|
|
|
#include "./failure.h"
|
2015-05-08 23:20:47 +02:00
|
|
|
|
2015-09-06 20:19:09 +02:00
|
|
|
#include "../conversion/stringconversion.h"
|
|
|
|
#include "../misc/random.h"
|
2015-04-22 18:36:40 +02:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <vector>
|
|
|
|
#include <iostream>
|
|
|
|
#include <sstream>
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace std::placeholders;
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\namespace ApplicationUtilities
|
|
|
|
\brief Contains currently only ArgumentParser and related classes.
|
|
|
|
*/
|
|
|
|
namespace ApplicationUtilities {
|
|
|
|
|
2015-08-25 19:12:05 +02:00
|
|
|
const char *applicationName = nullptr;
|
|
|
|
const char *applicationAuthor = nullptr;
|
|
|
|
const char *applicationVersion = nullptr;
|
|
|
|
const char *applicationUrl = nullptr;
|
|
|
|
|
2015-04-22 18:36:40 +02:00
|
|
|
/*!
|
|
|
|
* \class ApplicationUtilities::Argument
|
|
|
|
* \brief The Argument class is a wrapper for command line argument information.
|
|
|
|
*
|
|
|
|
* Instaces of the Argument class are used as definition when parsing command line
|
|
|
|
* arguments. Arguments can be assigned to an ArgumentParser using
|
|
|
|
* ArgumentParser::setMainArguments() and to another Argument instance using
|
|
|
|
* Argument::setSecondaryArguments().
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Constructs an Argument with the given \a name, \a abbreviation and \a description.
|
|
|
|
*
|
|
|
|
* The \a name and the abbreviation mustn't contain any whitespaces.
|
|
|
|
* The \a name mustn't be empty. The \a abbreviation and the \a description might be empty.
|
|
|
|
*/
|
|
|
|
Argument::Argument(const std::string &name, const std::string abbreviation, const std::string &description) :
|
|
|
|
m_required(false),
|
|
|
|
m_combinable(false),
|
|
|
|
m_implicit(false),
|
2015-05-08 23:20:47 +02:00
|
|
|
m_denotesOperation(false),
|
2015-04-22 18:36:40 +02:00
|
|
|
m_requiredValueCount(0),
|
|
|
|
m_default(false),
|
|
|
|
m_present(false),
|
|
|
|
m_isMainArg(false)
|
|
|
|
{
|
|
|
|
setName(name);
|
|
|
|
setAbbreviation(abbreviation);
|
|
|
|
setDescription(description);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Constructs an Argument with the given \a name, \a abbreviation and \a description.
|
|
|
|
* The \a name and the abbreviation mustn't contain any whitespaces.
|
|
|
|
* The \a name mustn't be empty. The \a abbreviation and the \a description might be empty.
|
|
|
|
*/
|
|
|
|
Argument::Argument(const char *name, const char *abbreviation, const char *description) :
|
|
|
|
m_required(false),
|
|
|
|
m_combinable(false),
|
|
|
|
m_implicit(false),
|
2015-07-27 23:19:10 +02:00
|
|
|
m_denotesOperation(false),
|
2015-04-22 18:36:40 +02:00
|
|
|
m_requiredValueCount(0),
|
|
|
|
m_default(false),
|
|
|
|
m_present(false),
|
|
|
|
m_isMainArg(false)
|
|
|
|
{
|
|
|
|
setName(name);
|
|
|
|
setAbbreviation(abbreviation);
|
|
|
|
setDescription(description);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Destroys the Argument.
|
|
|
|
*/
|
|
|
|
Argument::~Argument()
|
|
|
|
{}
|
|
|
|
|
|
|
|
///*!
|
|
|
|
// * \brief Checks whether the argument is ambigious.
|
|
|
|
// * \returns Returns zero if the argument is not ambigious. Returns 1 if the name
|
|
|
|
// * is ambiguous and 2 if the abbreviation is ambiguous.
|
|
|
|
// */
|
|
|
|
//unsigned char Argument::isAmbiguous(const ArgumentParser &parser) const
|
|
|
|
//{
|
|
|
|
// function<unsigned char (const ArgumentVector &args, const Argument *toCheck)> check;
|
|
|
|
// check = [&check] (const ArgumentVector &args, const Argument *toCheck) -> unsigned char {
|
|
|
|
// for(const auto *arg : args) {
|
|
|
|
// if(arg != toCheck) {
|
|
|
|
// if(!toCheck->name().empty() && arg->name() == toCheck->name()) {
|
|
|
|
// return 1;
|
|
|
|
// } else if(!toCheck->abbreviation().empty() && arg->abbreviation() == toCheck->abbreviation()) {
|
|
|
|
// return 2;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// if(auto res = check(arg->parents(), toCheck)) {
|
|
|
|
// return res;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// return 0;
|
|
|
|
// };
|
|
|
|
// if(auto res = check(parser.mainArguments(), this)) {
|
|
|
|
// return res;
|
|
|
|
// }
|
|
|
|
// return check(parents(), this);
|
|
|
|
//}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Appends the name, the abbreviation and the description of the Argument
|
|
|
|
* to the give ostream.
|
|
|
|
*/
|
|
|
|
void Argument::printInfo(ostream &os, unsigned char indentionLevel) const
|
|
|
|
{
|
|
|
|
for(unsigned char i = 0; i < indentionLevel; ++i) os << " ";
|
|
|
|
if(!name().empty()) {
|
|
|
|
os << "--" << name();
|
|
|
|
}
|
|
|
|
if(!name().empty() && !abbreviation().empty()) {
|
|
|
|
os << ", ";
|
|
|
|
}
|
|
|
|
if(!abbreviation().empty()) {
|
|
|
|
os << "-" << abbreviation();
|
|
|
|
}
|
|
|
|
if(requiredValueCount() > 0) {
|
|
|
|
int valueNamesPrint = 0;
|
|
|
|
for(auto i = valueNames().cbegin(), end = valueNames().cend(); i != end && valueNamesPrint < requiredValueCount(); ++i) {
|
|
|
|
os << " [" << *i << "]";
|
|
|
|
++valueNamesPrint;
|
|
|
|
}
|
|
|
|
for(; valueNamesPrint < requiredValueCount(); ++valueNamesPrint) {
|
|
|
|
os << " [value " << (valueNamesPrint + 1) << "]";
|
|
|
|
}
|
|
|
|
} else if(requiredValueCount() < 0) {
|
|
|
|
for(auto i = valueNames().cbegin(), end = valueNames().cend(); i != end; ++i) {
|
|
|
|
os << " [" << *i << "]";
|
|
|
|
}
|
|
|
|
os << " ...";
|
|
|
|
}
|
|
|
|
++indentionLevel;
|
|
|
|
if(!description().empty()) {
|
|
|
|
os << endl;
|
|
|
|
for(unsigned char i = 0; i < indentionLevel; ++i) os << " ";
|
|
|
|
os << description();
|
|
|
|
}
|
|
|
|
if(isRequired()) {
|
|
|
|
os << endl;
|
|
|
|
for(unsigned char i = 0; i < indentionLevel; ++i) os << " ";
|
|
|
|
os << "This argument is required.";
|
|
|
|
}
|
|
|
|
os << endl;
|
|
|
|
for(const Argument *arg : secondaryArguments()) {
|
|
|
|
arg->printInfo(os, indentionLevel + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* This function return the first present and uncombinable argument of the given list of arguments.
|
|
|
|
* The Argument \a except will be ignored.
|
|
|
|
*/
|
|
|
|
Argument *firstPresentUncombinableArg(const ArgumentVector &args, const Argument *except)
|
|
|
|
{
|
|
|
|
for(Argument *arg : args) {
|
|
|
|
if(arg != except && arg->isPresent() && !arg->isCombinable()) {
|
|
|
|
return arg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Sets the secondary arguments for this arguments. The given arguments will be considered as
|
2015-07-27 23:19:10 +02:00
|
|
|
* secondary arguments of this argument by the argument parser. This means that the parser
|
|
|
|
* will complain if these arguments are given, but not this argument. If secondary arguments are
|
2015-04-22 18:36:40 +02:00
|
|
|
* labeled as mandatory their parent is also mandatory.
|
|
|
|
*
|
|
|
|
* The Argument does not take ownership. Do not destroy the given arguments as long as they are
|
|
|
|
* used as secondary arguments.
|
|
|
|
*
|
|
|
|
* \sa secondaryArguments()
|
2015-07-27 23:19:10 +02:00
|
|
|
* \sa addSecondaryArgument()
|
2015-04-22 18:36:40 +02:00
|
|
|
* \sa hasSecondaryArguments()
|
|
|
|
*/
|
|
|
|
void Argument::setSecondaryArguments(const ArgumentInitializerList &secondaryArguments)
|
|
|
|
{
|
|
|
|
// remove this argument from the parents list of the previous secondary arguments
|
|
|
|
for(Argument *arg : m_secondaryArgs) {
|
|
|
|
arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(), this), arg->m_parents.end());
|
|
|
|
}
|
|
|
|
// assign secondary arguments
|
|
|
|
m_secondaryArgs.assign(secondaryArguments);
|
|
|
|
// add this argument to the parents list of the assigned secondary arguments
|
|
|
|
// and set the parser
|
|
|
|
for(Argument *arg : m_secondaryArgs) {
|
|
|
|
if(find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
|
|
|
|
arg->m_parents.push_back(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-27 23:19:10 +02:00
|
|
|
/*!
|
|
|
|
* Adds \a arg as a secondary argument for this argument.
|
|
|
|
*
|
|
|
|
* \sa secondaryArguments()
|
|
|
|
* \sa setSecondaryArguments()
|
|
|
|
* \sa hasSecondaryArguments()
|
|
|
|
*/
|
|
|
|
void Argument::addSecondaryArgument(Argument *arg)
|
|
|
|
{
|
|
|
|
if(find(m_secondaryArgs.cbegin(), m_secondaryArgs.cend(), arg) == m_secondaryArgs.cend()) {
|
|
|
|
m_secondaryArgs.push_back(arg);
|
|
|
|
if(find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
|
|
|
|
arg->m_parents.push_back(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-22 18:36:40 +02:00
|
|
|
/*!
|
|
|
|
* Returns the names of the parents in the form "parent1", "parent2, "parent3", ...
|
|
|
|
* Returns an empty string if this Argument has no parents.
|
|
|
|
* \sa parents()
|
|
|
|
*/
|
|
|
|
string Argument::parentNames() const
|
|
|
|
{
|
|
|
|
string res;
|
|
|
|
if(m_parents.size()) {
|
|
|
|
vector<string> names;
|
|
|
|
names.reserve(m_parents.size());
|
|
|
|
for(const Argument *parent : m_parents) {
|
|
|
|
names.push_back(parent->name());
|
|
|
|
}
|
|
|
|
res.assign(ConversionUtilities::joinStrings(names, ", "));
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Returns true if at least one of the parents is present.
|
|
|
|
* Returns false if this argument has no parents or none of its parents is present.
|
|
|
|
*/
|
|
|
|
bool Argument::isParentPresent() const
|
|
|
|
{
|
|
|
|
for(Argument *parent : m_parents) {
|
|
|
|
if(parent->isPresent()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Checks if this arguments conflicts with other arguments. If the argument is in conflict
|
|
|
|
* with an other argument this argument will be returned. Otherwise nullptr will be returned.
|
|
|
|
*/
|
|
|
|
Argument *Argument::conflictsWithArgument() const
|
|
|
|
{
|
|
|
|
if(!isCombinable() && isPresent()) {
|
|
|
|
for(Argument *parent : m_parents) {
|
|
|
|
for(Argument *sibling : parent->secondaryArguments()) {
|
|
|
|
if(sibling != this && sibling->isPresent() && !sibling->isCombinable()) {
|
|
|
|
return sibling;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \class ApplicationUtilities::ArgumentParser
|
|
|
|
* \brief The ArgumentParser class provides a means for handling command line arguments.
|
|
|
|
*
|
|
|
|
* To setup the parser create instances of ApplicationUtilities::Argument to define a
|
|
|
|
* set of known arguments and assign these to the parser using setMainArguments().
|
|
|
|
*
|
|
|
|
* To invoke parsing call parseArgs(). The parser will verify the previously
|
|
|
|
* assigned definitions (and might throw std::invalid_argument) and then parse the
|
|
|
|
* given command line arguments according the definitions (and might throw
|
|
|
|
* ApplicationUtilities::Failure).
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Constructs a new ArgumentParser.
|
|
|
|
*/
|
|
|
|
ArgumentParser::ArgumentParser() :
|
|
|
|
m_actualArgc(0),
|
|
|
|
m_ignoreUnknownArgs(false)
|
|
|
|
{}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Sets the main arguments for the parser. The parser will use these argument definitions
|
|
|
|
* to when parsing the command line arguments and when printing help information.
|
|
|
|
*
|
|
|
|
* The parser does not take ownership. Do not destroy the arguments as long as they are used as
|
|
|
|
* main arguments.
|
|
|
|
*/
|
|
|
|
void ArgumentParser::setMainArguments(const ArgumentInitializerList &mainArguments)
|
|
|
|
{
|
|
|
|
for(Argument *arg : mainArguments) {
|
|
|
|
arg->m_isMainArg = true;
|
|
|
|
}
|
|
|
|
m_mainArgs.assign(mainArguments);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Prints help information for all main arguments which have been set using setMainArguments().
|
|
|
|
*/
|
|
|
|
void ArgumentParser::printHelp(ostream &os) const
|
|
|
|
{
|
2015-08-25 19:12:05 +02:00
|
|
|
if(applicationName && *applicationName) {
|
|
|
|
os << applicationName;
|
|
|
|
if(applicationVersion && *applicationVersion) {
|
|
|
|
os << ',' << ' ';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(applicationVersion && *applicationVersion) {
|
|
|
|
os << "version " << applicationVersion;
|
|
|
|
}
|
|
|
|
if((applicationName && *applicationName) || (applicationVersion && *applicationVersion)) {
|
|
|
|
os << '\n' << '\n';
|
|
|
|
}
|
|
|
|
if(!m_mainArgs.empty()) {
|
|
|
|
os << "Available arguments:\n";
|
|
|
|
for(const Argument *arg : m_mainArgs) {
|
|
|
|
arg->printInfo(os);
|
|
|
|
}
|
2015-04-22 18:36:40 +02:00
|
|
|
}
|
2015-08-25 19:12:05 +02:00
|
|
|
if(applicationUrl && *applicationUrl) {
|
|
|
|
os << "\nProject website: " << applicationUrl << endl;
|
2015-04-22 18:36:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Returns the first argument definition which matches the predicate.
|
|
|
|
* The search includes all assigned main argument definitions and their sub arguments.
|
|
|
|
*/
|
|
|
|
Argument *ArgumentParser::findArg(const ArgumentPredicate &predicate) const
|
|
|
|
{
|
|
|
|
return findArg(m_mainArgs, predicate);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Returns the first argument definition which matches the predicate.
|
|
|
|
* The search includes all provided \a arguments and their sub arguments.
|
|
|
|
*/
|
|
|
|
Argument *ArgumentParser::findArg(const ArgumentVector &arguments, const ArgumentPredicate &predicate)
|
|
|
|
{
|
|
|
|
for(Argument *arg : arguments) {
|
|
|
|
if(predicate(arg)) {
|
|
|
|
return arg; // argument matches
|
|
|
|
} else if(Argument *subarg = findArg(arg->secondaryArguments(), predicate)) {
|
|
|
|
return subarg; // a secondary argument matches
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr; // no argument matches
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* This method is used to verify the setup of the command line parser before parsing.
|
|
|
|
*
|
|
|
|
* This function will throw std::invalid_argument when a mismatch is detected:
|
|
|
|
* - An argument is used as main argument and as sub argument at the same time.
|
|
|
|
* - An argument abbreviation is used more then once.
|
|
|
|
* - An argument name is used more then once.
|
|
|
|
* If none of these conditions are met, this method will do nothing.
|
|
|
|
*
|
|
|
|
* \remarks Usually you don't need to call this function manually because it is called by
|
|
|
|
* parse() automatically befor the parsing is performed.
|
|
|
|
*/
|
|
|
|
void ArgumentParser::verifySetup() const
|
|
|
|
{
|
|
|
|
vector<Argument *> verifiedArgs;
|
|
|
|
vector<string> abbreviations;
|
|
|
|
vector<string> names;
|
2015-07-27 23:19:10 +02:00
|
|
|
const Argument *implicitArg = nullptr;
|
2015-04-22 18:36:40 +02:00
|
|
|
function<void (const ArgumentVector &args)> checkArguments;
|
2015-07-27 23:19:10 +02:00
|
|
|
checkArguments = [&verifiedArgs, &abbreviations, &names, &checkArguments, &implicitArg, this] (const ArgumentVector &args) {
|
2015-04-22 18:36:40 +02:00
|
|
|
for(Argument *arg : args) {
|
2015-07-27 23:19:10 +02:00
|
|
|
if(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) != verifiedArgs.cend()) {
|
2015-04-22 18:36:40 +02:00
|
|
|
continue; // do not verify the same argument twice
|
2015-07-27 23:19:10 +02:00
|
|
|
}
|
|
|
|
if(arg->isMainArgument() && arg->parents().size()) {
|
2015-04-22 18:36:40 +02:00
|
|
|
throw invalid_argument("Argument \"" + arg->name() + "\" can not be used as main argument and sub argument at the same time.");
|
2015-07-27 23:19:10 +02:00
|
|
|
}
|
|
|
|
if(!arg->abbreviation().empty() && find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) != abbreviations.cend()) {
|
2015-04-22 18:36:40 +02:00
|
|
|
throw invalid_argument("Abbreviation \"" + arg->abbreviation() + "\" has been used more then once.");
|
2015-07-27 23:19:10 +02:00
|
|
|
}
|
|
|
|
if(find(names.cbegin(), names.cend(), arg->name()) != names.cend()) {
|
2015-04-22 18:36:40 +02:00
|
|
|
throw invalid_argument("Name \"" + arg->name() + "\" has been used more then once.");
|
2015-07-27 23:19:10 +02:00
|
|
|
}
|
|
|
|
if(arg->isDefault() && arg->requiredValueCount() > 0 && arg->defaultValues().size() < static_cast<size_t>(arg->requiredValueCount())) {
|
2015-04-22 18:36:40 +02:00
|
|
|
throw invalid_argument("Default argument \"" + arg->name() + "\" doesn't provide the required number of default values.");
|
2015-07-27 23:19:10 +02:00
|
|
|
}
|
|
|
|
if(arg->isImplicit()) {
|
|
|
|
if(implicitArg) {
|
|
|
|
throw invalid_argument("The arguments \"" + implicitArg->name() + "\" and \"" + arg->name() + "\" can not be both implicit.");
|
|
|
|
} else {
|
|
|
|
implicitArg = arg;
|
|
|
|
}
|
|
|
|
}
|
2015-04-22 18:36:40 +02:00
|
|
|
abbreviations.push_back(arg->abbreviation());
|
|
|
|
names.push_back(arg->name());
|
|
|
|
verifiedArgs.push_back(arg);
|
|
|
|
checkArguments(arg->secondaryArguments());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
checkArguments(m_mainArgs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* This method invokes verifySetup() before parsing. See its do documentation for more
|
|
|
|
* information about execptions that might be thrown to indicate an invalid setup of the parser.
|
|
|
|
*
|
|
|
|
* If the parser is setup properly this method will parse the given command line arguments using
|
|
|
|
* the previsously set argument definitions.
|
|
|
|
* If one of the previsously defined arguments has been found, its present flag will be set to true
|
|
|
|
* and the parser reads all values tied to this argument.
|
|
|
|
* If an argument has been found which does not match any of the previous definitions it will be
|
|
|
|
* considered as unknown.
|
|
|
|
* If the given command line arguments are not valid according the defined arguments an
|
|
|
|
* Failure will be thrown.
|
|
|
|
*/
|
|
|
|
void ArgumentParser::parseArgs(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
// initiate parser
|
|
|
|
verifySetup();
|
|
|
|
m_actualArgc = 0; // reset actual agument count
|
|
|
|
unsigned int actualArgc = 0;
|
|
|
|
int valuesToRead = 0;
|
|
|
|
// read current directory
|
|
|
|
if(argc >= 1) {
|
|
|
|
m_currentDirectory = string(*argv);
|
|
|
|
} else {
|
|
|
|
m_currentDirectory.clear();
|
|
|
|
}
|
2015-07-27 23:19:10 +02:00
|
|
|
// function for iterating through all arguments
|
|
|
|
function<void(Argument *, const ArgumentVector &, const function<void (Argument *, Argument *)> &)> foreachArg;
|
|
|
|
foreachArg = [&foreachArg] (Argument *parent, const ArgumentVector &args, const function<void (Argument *, Argument *)> &proc) {
|
|
|
|
for(Argument *arg : args) {
|
|
|
|
proc(parent, arg);
|
|
|
|
foreachArg(arg, arg->secondaryArguments(), proc);
|
|
|
|
}
|
|
|
|
};
|
2015-04-22 18:36:40 +02:00
|
|
|
// parse given arguments
|
|
|
|
if(argc >= 2) {
|
|
|
|
Argument *currentArg = nullptr;
|
|
|
|
// iterate through given arguments
|
2015-07-27 23:19:10 +02:00
|
|
|
for(char **i = argv + 1, **end = argv + argc; i != end; ++i) {
|
2015-04-22 18:36:40 +02:00
|
|
|
string givenArg(*i); // convert argument to string
|
|
|
|
if(!givenArg.empty()) { // skip empty entries
|
|
|
|
if(valuesToRead <= 0 && givenArg.size() > 1 && givenArg.front() == '-') {
|
|
|
|
// we have no values left to read and the given arguments starts with '-'
|
|
|
|
// -> the next "value" is a main argument or a sub argument
|
|
|
|
ArgumentPredicate pred;
|
|
|
|
string givenId;
|
|
|
|
size_t equationPos = givenArg.find('=');
|
|
|
|
if(givenArg.size() > 2 && givenArg[1] == '-') {
|
|
|
|
// the argument starts with '--'
|
|
|
|
// -> the full argument name has been provided
|
|
|
|
givenId = givenArg.substr(2, equationPos - 2);
|
|
|
|
pred = [&givenId, equationPos] (Argument *arg) {
|
|
|
|
return arg->name() == givenId;
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
// the argument starts with a single '-'
|
|
|
|
// -> the abbreviation has been provided
|
|
|
|
givenId = givenArg.substr(1, equationPos - 1);
|
|
|
|
pred = [&givenId, equationPos] (Argument *arg) {
|
|
|
|
return arg->abbreviation() == givenId;
|
|
|
|
};
|
|
|
|
}
|
2015-07-27 23:19:10 +02:00
|
|
|
// find the corresponding instance of the Argument class
|
2015-04-22 18:36:40 +02:00
|
|
|
currentArg = findArg(pred);
|
|
|
|
if(currentArg) {
|
|
|
|
// the corresponding instance of Argument class has been found
|
|
|
|
if(currentArg->m_present) {
|
|
|
|
// the argument has been provided more then once
|
2015-07-27 23:19:10 +02:00
|
|
|
throw Failure("The argument \"" + currentArg->name() + "\" has been specified more than one time.");
|
2015-04-22 18:36:40 +02:00
|
|
|
} else {
|
|
|
|
// set present flag of argument
|
|
|
|
currentArg->m_present = true;
|
|
|
|
++actualArgc; // we actually found an argument
|
|
|
|
// now we might need to read values tied to that argument
|
|
|
|
valuesToRead = currentArg->requiredValueCount();
|
|
|
|
if(equationPos != string::npos) {
|
|
|
|
// a value has been specified using the --argument=value syntax
|
|
|
|
string value = givenArg.substr(equationPos + 1);
|
|
|
|
if(valuesToRead != 0) {
|
|
|
|
currentArg->m_values.push_back(value);
|
|
|
|
if(valuesToRead > 0) {
|
|
|
|
--valuesToRead;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw Failure("Invalid extra information \"" + value + "\" (specified using \"--argument=value\" syntax) for the argument \"" + currentArg->name() + "\" given.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// the given argument seems to be unknown
|
|
|
|
if(valuesToRead < 0) {
|
|
|
|
// we have a variable number of values to expect -> treat "unknown argument" as value
|
|
|
|
goto readValue;
|
|
|
|
} else {
|
|
|
|
// we have no more values to expect so we need to complain about the unknown argument
|
|
|
|
goto invalidArg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
readValue:
|
|
|
|
if(!currentArg) {
|
2015-05-08 23:20:47 +02:00
|
|
|
// we have not parsed an argument before
|
|
|
|
// -> check if an argument which denotes the operation is specified
|
|
|
|
if(i == argv + 1) {
|
|
|
|
for(Argument *arg : m_mainArgs) {
|
|
|
|
if(!arg->isPresent() && arg->denotesOperation()
|
|
|
|
&& (arg->name() == givenArg || arg->abbreviation() == givenArg)) {
|
|
|
|
currentArg = arg;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(currentArg) {
|
2015-06-24 00:48:45 +02:00
|
|
|
currentArg->m_present = true;
|
|
|
|
++actualArgc; // we actually found an argument
|
|
|
|
// now we might need to read values tied to that argument
|
|
|
|
valuesToRead = currentArg->requiredValueCount();
|
2015-05-08 23:20:47 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// -> check if there's an implicit argument definition
|
2015-07-27 23:19:10 +02:00
|
|
|
try {
|
|
|
|
foreachArg(nullptr, m_mainArgs, [&actualArgc, &valuesToRead, ¤tArg, this] (Argument *, Argument *arg) {
|
|
|
|
if(!arg->isPresent() && arg->isImplicit()) {
|
|
|
|
throw arg;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch(Argument *arg) {
|
|
|
|
// set present flag of argument
|
|
|
|
arg->m_present = true;
|
|
|
|
++actualArgc; // we actually found an argument
|
|
|
|
// now we might need to read values tied to that argument
|
|
|
|
valuesToRead = arg->requiredValueCount();
|
|
|
|
currentArg = arg;
|
2015-04-22 18:36:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if(currentArg) {
|
|
|
|
// check if the given value is tied to the most recently parsed argument
|
|
|
|
if(valuesToRead == 0) {
|
|
|
|
throw Failure("Invalid extra information \"" + givenArg + "\" for the argument \"" + currentArg->name() + "\" given.");
|
|
|
|
} else if(valuesToRead < 0) {
|
|
|
|
currentArg->m_values.push_back(givenArg);
|
|
|
|
} else {
|
|
|
|
currentArg->m_values.push_back(givenArg);
|
|
|
|
--valuesToRead; // one value less to be read
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// there is no implicit argument definition -> the "value" has to be an argument
|
|
|
|
// but does not start with '-' or '--'
|
|
|
|
invalidArg:
|
|
|
|
string msg("The given argument \"" + givenArg + "\" is unknown.");
|
|
|
|
if(m_ignoreUnknownArgs) {
|
|
|
|
cout << msg << " It will be ignored." << endl;
|
|
|
|
} else {
|
|
|
|
throw Failure(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// iterate actually through all arguments using previously defined function to check gathered arguments
|
|
|
|
foreachArg(nullptr, m_mainArgs, [this] (Argument *parent, Argument *arg) {
|
2015-07-27 23:19:10 +02:00
|
|
|
if(!arg->isPresent()) {
|
|
|
|
// the argument might be flagged as present if its a default argument
|
|
|
|
if(arg->isDefault() && (arg->isMainArgument() || (parent && parent->isPresent()))) {
|
|
|
|
arg->m_present = true;
|
|
|
|
if(firstPresentUncombinableArg(arg->isMainArgument() ? m_mainArgs : parent->secondaryArguments(), arg)) {
|
|
|
|
arg->m_present = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// throw an error if mandatory argument is not present
|
|
|
|
if(!arg->isPresent() && (arg->isRequired() && (arg->isMainArgument() || (parent && parent->isPresent())))) {
|
|
|
|
throw Failure("The argument \"" + arg->name() + "\" is required but not given.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
foreachArg(nullptr, m_mainArgs, [this] (Argument *, Argument *arg) {
|
2015-04-22 18:36:40 +02:00
|
|
|
if(arg->isPresent()) {
|
|
|
|
if(!arg->isMainArgument() && arg->parents().size() && !arg->isParentPresent()) {
|
|
|
|
if(arg->parents().size() > 1) {
|
|
|
|
throw Failure("The argument \"" + arg->name() + "\" needs to be used together with one the following arguments: " + arg->parentNames());
|
|
|
|
} else {
|
|
|
|
throw Failure("The argument \"" + arg->name() + "\" needs to be used together with the argument \"" + arg->parents().front()->name() + "\".");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Argument *conflictingArgument = nullptr;
|
|
|
|
if(arg->isMainArgument()) {
|
|
|
|
if(!arg->isCombinable() && arg->isPresent()) {
|
|
|
|
conflictingArgument = firstPresentUncombinableArg(m_mainArgs, arg);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
conflictingArgument = arg->conflictsWithArgument();
|
|
|
|
}
|
|
|
|
if(conflictingArgument) {
|
|
|
|
throw Failure("The argument \"" + conflictingArgument->name() + "\" can not be combined with \"" + arg->name() + "\".");
|
|
|
|
}
|
|
|
|
if(!arg->allRequiredValuesPresent()) {
|
|
|
|
stringstream ss(stringstream::in | stringstream::out);
|
|
|
|
ss << "Not all required information for the given argument \"" << arg->name() << "\" provided. You have to give the following information:";
|
|
|
|
int valueNamesPrint = 0;
|
|
|
|
for(const auto &name : arg->m_valueNames) {
|
|
|
|
ss << "\n" << name;
|
|
|
|
++valueNamesPrint;
|
|
|
|
}
|
|
|
|
while(valueNamesPrint < arg->m_requiredValueCount) {
|
|
|
|
ss << "\nvalue " << (++valueNamesPrint);
|
|
|
|
}
|
|
|
|
throw Failure(ss.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// set actual argument count
|
|
|
|
m_actualArgc = actualArgc;
|
|
|
|
// interate through all arguments again to invoke callback functions
|
|
|
|
foreachArg(nullptr, m_mainArgs, [] (Argument *parent, Argument *arg) {
|
|
|
|
if(arg->m_callbackFunction) {
|
|
|
|
if(arg->isMainArgument() || (parent && parent->isPresent())) {
|
|
|
|
// only invoke if its a main argument or the parent is present
|
|
|
|
if(arg->isPresent()) {
|
|
|
|
if(arg->isDefault() && !arg->values().size()) {
|
|
|
|
arg->m_callbackFunction(arg->defaultValues());
|
|
|
|
} else {
|
|
|
|
arg->m_callbackFunction(arg->values());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief The HelpArgument class prints help information for an argument parser
|
|
|
|
* when present (--help, -h).
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Constructs a new help argument for the specified parser.
|
|
|
|
*/
|
|
|
|
HelpArgument::HelpArgument(ArgumentParser &parser) :
|
|
|
|
Argument("help", "h", "shows this information")
|
|
|
|
{
|
2015-09-01 20:05:38 +02:00
|
|
|
setCallback([&parser] (const StringVector &) {
|
|
|
|
CMD_UTILS_START_CONSOLE;
|
|
|
|
parser.printHelp(cout);
|
|
|
|
});
|
2015-04-22 18:36:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|