// ----------------------------------------------------------------------
// File: ConsoleCliCommand.cc
// Author: Joaquim Rocha - CERN
// ----------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2013 CERN/Switzerland *
* *
* 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 3 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, see .*
************************************************************************/
#include
#include
#include
#include
#include
#include "ConsoleCliCommand.hh"
#include "common/StringTokenizer.hh"
#define HELP_PADDING 50
#define DESC_LINE_LENGTH 70
bool isFloatEvalFunc(const CliOptionWithArgs* option,
std::vector& args,
std::string** error,
void* userData)
{
for (size_t i = 0; i < args.size(); i++) {
for (size_t s = 0; s < args[i].length(); s++) {
istringstream ss(args[i]);
float number;
if (!(ss >> number)) {
if (error) {
*error = new std::string("Error: Option " + option->repr() +
" needs a float.");
}
return false;
}
}
}
return true;
}
bool isIntegerEvalFunc(const CliOptionWithArgs* option,
std::vector& args,
std::string** error,
void* userData)
{
for (size_t i = 0; i < args.size(); i++) {
size_t s = 0;
if (args[i][0] == '-') {
s++;
}
for (; s < args[i].length(); s++) {
if (!std::isdigit(args[i][s])) {
if (error) {
*error = new std::string("Error: Option " + option->repr() +
" needs an integer.");
}
return false;
}
}
}
return true;
}
bool isNumberInRangeEvalFunc(const CliOptionWithArgs* option,
std::vector& args,
std::string** error,
const std::pair* range)
{
for (size_t i = 0; i < args.size(); i++) {
for (size_t s = 0; s < args[i].length(); s++) {
istringstream ss(args[i]);
float number;
if (!(ss >> number) || (range->first > number || number > range->second)) {
ostringstream limit;
if (error) {
*error = new std::string("Error: Option " + option->repr() +
" needs to be between ");
limit << range->first << " and " << range->second;
(*error)->append(limit.str());
}
return false;
}
}
}
return true;
}
bool isPositiveNumberEvalFunc(const CliOptionWithArgs* option,
std::vector& args,
std::string** error,
void* data)
{
std::pair groupSizeRange = {0.0, (float) INT_MAX};
if (!isNumberInRangeEvalFunc(option, args, 0, &groupSizeRange)) {
*error = new std::string("Error: Option " + option->repr() + " needs to be "
"a positive number.");
return false;
}
return true;
}
bool isNegativeNumberEvalFunc(const CliOptionWithArgs* option,
std::vector& args,
std::string** error,
void* data)
{
std::pair groupSizeRange = {(float) - INT_MAX, 0};
if (!isNumberInRangeEvalFunc(option, args, 0, &groupSizeRange)) {
*error = new std::string("Error: Option " + option->repr() + " needs to be "
"a negative number.");
return false;
}
return true;
}
bool isChoiceEvalFunc(const CliOptionWithArgs* option,
std::vector& args,
std::string** error,
const std::vector* choices)
{
assert(choices->size() != 0);
for (size_t i = 0; i < args.size(); i++) {
if (std::find(choices->begin(), choices->end(), args[i]) == choices->end()) {
if (error) {
*error = new std::string("Error: Option " + option->repr() + " needs to be");
(*error)->append(" " + choices->at(0));
for (size_t c = 1; c < choices->size(); c++) {
(*error)->append((c == choices->size() - 1 ? " or " : ", ") + choices->at(c));
}
}
return false;
}
}
return true;
}
std::vector*
splitKeywords(std::string keywords, char delimiter)
{
std::vector* splitKeywords = new std::vector;
eos::common::StringTokenizer tokenizer(keywords, delimiter);
tokenizer.GetLine();
const char* token = tokenizer.GetToken();
while (token) {
splitKeywords->push_back(token);
token = tokenizer.GetToken();
}
return splitKeywords;
}
ParseError::ParseError(CliBaseOption* option, std::string message)
: mOption(option),
mMessage(message)
{}
CliBaseOption::CliBaseOption(std::string name, std::string desc)
: mName(name),
mDescription(desc),
mRequired(false),
mHidden(false)
{};
CliBaseOption::~CliBaseOption() {};
CliOption::CliOption(std::string name, std::string desc, std::string keywords)
: CliBaseOption(name, desc),
mKeywords(splitKeywords(keywords, ',')),
mGroup(0)
{
};
CliOption::CliOption():
mKeywords(nullptr), mGroup(nullptr)
{
CliOption("", "", "");
}
CliOption::CliOption(const CliOption& option)
: CliBaseOption(option.mName, option.mDescription),
mGroup(nullptr)
{
CliOption(option.mName, option.mDescription, "");
mRequired = option.required();
mHidden = option.hidden();
if (option.mKeywords) {
mKeywords = new std::vector;
std::vector::const_iterator it;
for (it = option.mKeywords->cbegin(); it != option.mKeywords->cend(); it++) {
mKeywords->push_back(*it);
}
}
}
CliOption::~CliOption()
{
delete mKeywords;
mKeywords = nullptr;
}
void
CliOption::setGroup(OptionsGroup* group)
{
if (mGroup == group) {
return;
}
if (mGroup) {
mGroup->removeOption(this);
}
mGroup = group;
}
std::string
CliOption::hasKeyword(std::string keyword)
{
if (!mKeywords) {
return "";
}
std::vector::const_iterator it = mKeywords->cbegin();
for (; it != mKeywords->cend(); it++) {
if (keyword.compare(*it) == 0) {
return *it;
}
}
return "";
}
AnalysisResult*
CliOption::analyse(std::vector& cliArgs)
{
std::pair> ret;
std::vector::iterator it = cliArgs.begin();
AnalysisResult* res = new AnalysisResult;
res->start = res->end = it;
for (; it != cliArgs.end(); it++) {
if (hasKeyword(*it) != "") {
ret.first = mName;
res->start = it;
res->end = it + 1;
break;
}
}
res->values = ret;
res->errorMsg = "";
return res;
}
std::string
CliOption::joinKeywords()
{
std::string keyword("");
if (!mKeywords) {
return "";
}
for (size_t i = 0; i < mKeywords->size(); i++) {
keyword += mKeywords->at(i);
if (i < mKeywords->size() - 1) {
keyword += std::string("|");
}
}
return keyword;
}
char*
CliOption::keywordsRepr()
{
char* repr = NULL;
std::string keyword = joinKeywords();
if (keyword != "") {
if (!mRequired) {
keyword = "[" + keyword + "]";
}
repr = strdup(keyword.c_str());
}
return repr;
}
std::string
CliOption::repr() const
{
return mKeywords ? mKeywords->at(0) : "";
}
static std::string
truncateDescString(const std::string& description, const std::string& prefix)
{
std::string desc("");
size_t lineStart = 0, lineEnd = 0;
for (size_t i = 0; i < description.length(); i++) {
if (i == description.length() - 1) {
lineEnd = i + 1;
}
if (lineEnd != lineStart &&
(i - lineStart > DESC_LINE_LENGTH || i == description.length() - 1 ||
description[lineEnd] == '\n')) {
if (lineEnd > DESC_LINE_LENGTH || desc[desc.length() - 1] == '\n') {
desc += "\n" + std::string(HELP_PADDING, ' ') + prefix;
}
desc += std::string(description, lineStart, lineEnd - lineStart);
lineStart = lineEnd + 1;
lineEnd = lineStart;
continue;
}
if (description[i] == ' ' || description[i] == '\n') {
lineEnd = i;
}
}
desc += "\n";
return desc;
}
char*
CliOption::helpString()
{
if (mDescription == "" || !mKeywords) {
return NULL;
}
char* helpStr = 0;
std::string keyword("");
for (size_t i = 0; i < mKeywords->size(); i++) {
keyword += mKeywords->at(i);
if (i < mKeywords->size() - 1) {
keyword += std::string("|");
}
}
if (keyword != "") {
int strSize = keyword.length() + HELP_PADDING + 10;
helpStr = new char[strSize];
}
sprintf(helpStr, "%*s\t- ", HELP_PADDING, keyword.c_str());
std::string help(helpStr);
help += truncateDescString(mDescription, "\t ");
delete[] helpStr;
return strdup(help.c_str());
}
void
CliOptionWithArgs::init(int numArgs,
std::string repr,
bool required)
{
mNumArgs = numArgs;
mRepr = repr;
mEvalFunctions = 0;
mUserData = 0;
mRequired = required;
}
CliOptionWithArgs::CliOptionWithArgs(std::string name,
std::string desc,
std::string keywords,
int numArgs,
std::string repr,
bool required)
: CliOption::CliOption(name, desc, keywords)
{
init(numArgs, repr, required);
}
CliOptionWithArgs::CliOptionWithArgs(std::string name,
std::string desc,
std::string keywords,
std::string repr,
bool required)
: CliOption::CliOption(name, desc, keywords)
{
init(1, repr, required);
}
CliOptionWithArgs::CliOptionWithArgs()
: CliOption::CliOption("", "", "")
{
init(1, "", false);
}
CliOptionWithArgs::CliOptionWithArgs(const CliOptionWithArgs& otherOption)
: CliOption::CliOption(otherOption.mName, otherOption.mDescription, "")
{
init(otherOption.mNumArgs, otherOption.mRepr, otherOption.mRequired);
mHidden = otherOption.hidden();
if (otherOption.mKeywords) {
std::vector::const_iterator it;
for (it = otherOption.mKeywords->cbegin();
it != otherOption.mKeywords->cend(); it++) {
mKeywords->push_back(*it);
}
}
if (otherOption.mEvalFunctions) {
mEvalFunctions = new std::vector(*otherOption.mEvalFunctions);
if (otherOption.mUserData) {
mUserData = new std::vector(*otherOption.mUserData);
}
}
}
CliOptionWithArgs::~CliOptionWithArgs()
{
if (mEvalFunctions) {
delete mEvalFunctions;
}
mEvalFunctions = 0;
if (mUserData) {
delete mUserData;
}
mUserData = 0;
}
char*
CliOptionWithArgs::helpString()
{
if (mDescription == "" || !mKeywords) {
return NULL;
}
char* helpStr = 0;
std::string keyword("");
ostringstream helpRepr(mRepr);
if (helpRepr.str() == "") {
if (mNumArgs == -1) {
helpRepr << " ...";
} else {
for (int i = 1; i <= mNumArgs; i++) {
helpRepr << "" << (i == mNumArgs ? "" : " ");
}
helpRepr << "...";
}
}
for (size_t i = 0; i < mKeywords->size(); i++) {
keyword += mKeywords->at(i);
if (keyword[keyword.length() - 1] == '=') {
keyword += helpRepr.str();
} else {
keyword += " " + helpRepr.str();
}
if (i < mKeywords->size() - 1) {
keyword += std::string("|");
}
}
if (keyword != "") {
int strSize = keyword.length() + HELP_PADDING + 10;
helpStr = new char[strSize];
}
sprintf(helpStr, "%*s\t- ", HELP_PADDING, keyword.c_str());
std::string help(helpStr);
help += truncateDescString(mDescription, "\t ");
delete[] helpStr;
return strdup(help.c_str());
}
void
CliOptionWithArgs::addEvalFunction(evalFuncCb func, void* userData)
{
if (!mEvalFunctions) {
mEvalFunctions = new std::vector;
mUserData = new std::vector;
}
mEvalFunctions->push_back(func);
mUserData->push_back(userData);
}
std::string
CliOptionWithArgs::hasKeyword(std::string keyword)
{
if (!mKeywords) {
return "";
}
std::vector::const_iterator it = mKeywords->cbegin();
for (; it != mKeywords->cend(); it++) {
if (keyword.compare(*it) == 0) {
return *it;
}
const std::string& kw = *it;
// If the current keyword ends up in an '=' we check
// if its a prefix of the given arguemtn
if (kw[kw.length() - 1] == '=' &&
keyword.compare(0, kw.length(), kw) == 0) {
return *it;
}
}
return "";
}
std::string
CliOptionWithArgs::repr() const
{
std::string reprStr = mRepr;
if (reprStr != "" && mKeywords) {
const std::string& firstKw = mKeywords->front();
reprStr = firstKw + (firstKw[firstKw.length() - 1] == '=' ? "" : " ") + reprStr;
}
return reprStr;
}
AnalysisResult*
CliOptionWithArgs::commonAnalysis(std::vector& cliArgs,
int initPos, const std::string& firstArg)
{
AnalysisResult* res;
std::vector optionArgs;
int numArgs = mNumArgs == -1 ? max((int) cliArgs.size(), 1) : mNumArgs;
if (firstArg != "") {
optionArgs.insert(optionArgs.begin(), firstArg);
numArgs--;
}
if (initPos + numArgs <= (int) cliArgs.size())
optionArgs.insert(optionArgs.end(), cliArgs.cbegin() + initPos,
cliArgs.cbegin() + initPos + numArgs);
if (numArgs > (int) optionArgs.size()) {
if (!mRequired) {
return NULL;
}
res = new AnalysisResult;
res->values.first = mName;
res->start = cliArgs.begin() + initPos;
if (optionArgs.size() == 0) {
res->end = res->start;
res->errorMsg = "Error: Please specify " + repr() + ".";
} else {
res->end = res->start + min((int) optionArgs.size(), numArgs);
res->errorMsg = "Error: Too few arguments for " + repr() + ".";
}
return res;
}
res = new AnalysisResult;
res->values.first = mName;
res->start = cliArgs.begin() + initPos;
res->errorMsg = "";
std::string* evalErrorMsg = 0;
if (shouldEvaluate()) {
for (size_t f = 0; f < mEvalFunctions->size(); f++) {
if (!mEvalFunctions->at(f)(this, optionArgs, &evalErrorMsg, mUserData->at(f))) {
goto bailout;
}
}
}
int i;
for (i = 0; i < (int) optionArgs.size(); i++) {
res->values.second.push_back(optionArgs.at(i));
}
bailout:
res->end = res->start + numArgs;
if (evalErrorMsg) {
res->errorMsg = std::string(evalErrorMsg->c_str());
}
delete evalErrorMsg;
return res;
}
AnalysisResult*
CliOptionWithArgs::analyse(std::vector& cliArgs)
{
AnalysisResult* res;
size_t initPos;
std::string firstArg("");
for (initPos = 0; initPos < cliArgs.size(); initPos++) {
const std::string& foundKw = hasKeyword(cliArgs[initPos]);
if (foundKw != "") {
if (foundKw[foundKw.length() - 1] == '=') {
firstArg = cliArgs[initPos].substr(foundKw.length());
}
break;
}
}
initPos++;
res = commonAnalysis(cliArgs, initPos, firstArg);
// we decrease the start of our options because we need to include
// the keyword used.
if (res && initPos <= cliArgs.size()) {
res->start = res->start - 1;
}
return res;
}
CliPositionalOption::CliPositionalOption(std::string name, std::string desc,
int position, int numArgs, std::string repr)
: CliOptionWithArgs(name, desc, "", numArgs, repr, false),
mPosition(position)
{
assert(mPosition > 0 || mPosition == -1);
}
CliPositionalOption::CliPositionalOption(std::string name, std::string desc,
int position, int numArgs,
std::string repr, bool required)
: CliOptionWithArgs(name, desc, "", numArgs, repr, required),
mPosition(position)
{
assert(mPosition > 0 || mPosition == -1);
}
CliPositionalOption::CliPositionalOption(std::string name, std::string desc,
int position, std::string repr)
: CliOptionWithArgs(name, desc, "", 1, repr, false),
mPosition(position)
{
assert(mPosition > 0 || mPosition == -1);
}
CliPositionalOption::CliPositionalOption(const CliPositionalOption& option)
: CliOptionWithArgs(option.name(), option.description(), "",
option.mNumArgs, option.mRepr, option.mRequired),
mPosition(option.mPosition)
{
if (option.mEvalFunctions) {
mEvalFunctions = new std::vector(*option.mEvalFunctions);
}
if (option.mUserData) {
mUserData = new std::vector(*option.mUserData);
}
}
CliPositionalOption::~CliPositionalOption() {};
char*
CliPositionalOption::helpString()
{
if (mDescription == "") {
return NULL;
}
char* helpStr;
std::string repr = mRepr;
int strLength = repr.length() + HELP_PADDING + 10;
helpStr = new char[strLength];
sprintf(helpStr, "%*s\t- ", HELP_PADDING, repr.c_str());
std::string help(helpStr);
help += truncateDescString(mDescription, "\t ");
delete[] helpStr;
return strdup(help.c_str());
}
AnalysisResult*
CliPositionalOption::analyse(std::vector& cliArgs)
{
return commonAnalysis(cliArgs, mPosition - 1, "");
}
std::string
CliPositionalOption::repr() const
{
return mRepr;
}
void
ConsoleCliCommand::init(const std::string& name,
const std::string& description)
{
mName = name;
mDescription = description;
mSubcommands = 0;
mMainGroup = 0;
mPositionalOptions = 0;
mParentCommand = 0;
mErrors = 0;
mGroups = 0;
mStandalone = true;
}
ConsoleCliCommand::ConsoleCliCommand(const std::string& name,
const std::string& description)
{
init(name, description);
}
ConsoleCliCommand::ConsoleCliCommand(const ConsoleCliCommand& otherCmd)
{
init(otherCmd.mName, otherCmd.mDescription);
if (otherCmd.mMainGroup) {
mMainGroup = new OptionsGroup(*otherCmd.mMainGroup);
}
if (otherCmd.mPositionalOptions) {
std::map::const_iterator it;
for (it = otherCmd.mPositionalOptions->cbegin();
it != otherCmd.mPositionalOptions->cend(); it++) {
addOption(*(*it).second);
}
}
if (otherCmd.mGroups) {
for (size_t i = 0; i < otherCmd.mGroups->size(); i++) {
OptionsGroup* newGroup = new OptionsGroup(*otherCmd.mGroups->at(i));
addGroup(newGroup);
}
}
if (otherCmd.mSubcommands) {
mSubcommands = new std::vector(*otherCmd.mSubcommands);
}
if (otherCmd.mParentCommand) {
const_cast(otherCmd.mParentCommand)->addSubcommand(this);
}
}
ConsoleCliCommand::~ConsoleCliCommand()
{
size_t i;
delete mMainGroup;
mMainGroup = 0;
if (mGroups) {
for (i = 0; i < mGroups->size(); i++) {
delete mGroups->at(i);
}
delete mGroups;
mGroups = 0;
}
if (mPositionalOptions) {
std::map::const_iterator it;
for (it = mPositionalOptions->cbegin(); it != mPositionalOptions->cend();
it++) {
delete(*it).second;
}
delete mPositionalOptions;
mPositionalOptions = 0;
}
if (mSubcommands) {
for (i = 0; i < mSubcommands->size(); i++) {
delete mSubcommands->at(i);
}
delete mSubcommands;
mSubcommands = 0;
}
clean();
mErrors = 0;
}
void
ConsoleCliCommand::clean()
{
if (mErrors) {
for (size_t i = 0; i < mErrors->size(); i++) {
delete mErrors->at(i);
}
mErrors->clear();
}
mOptionsMap.clear();
}
void
ConsoleCliCommand::addOption(CliOption* option)
{
if (mMainGroup == 0) {
mMainGroup = new OptionsGroup;
}
mMainGroup->addOption(option);
}
void
ConsoleCliCommand::addOption(CliPositionalOption* option)
{
if (mPositionalOptions == 0) {
mPositionalOptions = new std::map;
}
int pos = option->position();
if (mPositionalOptions->count(pos) != 0) {
delete mPositionalOptions->at(pos);
mPositionalOptions->erase(pos);
}
mPositionalOptions->insert({pos, option});
}
void
ConsoleCliCommand::addSubcommand(ConsoleCliCommand* subcommand)
{
assert(subcommand != this);
if (mSubcommands == 0) {
mSubcommands = new std::vector;
mStandalone = false;
}
if (std::find(mSubcommands->begin(), mSubcommands->end(),
subcommand) == mSubcommands->end()) {
mSubcommands->push_back(subcommand);
}
subcommand->setParent(this);
}
void
ConsoleCliCommand::addOption(const CliOption& option)
{
CliOption* newObj = new CliOption(option);
addOption(newObj);
}
void
ConsoleCliCommand::addOption(const CliOptionWithArgs& option)
{
CliOptionWithArgs* newObj = new CliOptionWithArgs(option);
addOption(newObj);
}
void
ConsoleCliCommand::addOption(const CliPositionalOption& option)
{
CliPositionalOption* newObj = new CliPositionalOption(option);
addOption(newObj);
}
void
ConsoleCliCommand::addOptions(std::vector options)
{
std::vector::const_iterator it = options.cbegin();
for (; it != options.cend(); it++) {
addOption(*it);
}
}
void
ConsoleCliCommand::addOptions(std::vector options)
{
std::vector::const_iterator it = options.cbegin();
for (; it != options.cend(); it++) {
addOption(*it);
}
}
void
ConsoleCliCommand::addOptions(std::vector options)
{
std::vector::const_iterator it = options.cbegin();
for (; it != options.cend(); it++) {
addOption(*it);
}
}
CliOption*
ConsoleCliCommand::getOption(const std::string& name) const
{
CliOption* ret = 0;
if (mMainGroup) {
ret = mMainGroup->getOption(name);
}
if (!ret && mPositionalOptions) {
std::map::iterator pos_it =
mPositionalOptions->begin();
for (; pos_it != mPositionalOptions->end(); pos_it++) {
if ((*pos_it).second->name() == name) {
return (*pos_it).second;
}
}
}
std::vector::const_iterator it;
for (it = mGroups->cbegin(); !ret && it != mGroups->cend(); it++) {
ret = (*it)->getOption(name);
}
return ret;
}
OptionsGroup*
ConsoleCliCommand::addGroupedOptions(std::vector options)
{
if (options.size() == 0) {
return 0;
}
OptionsGroup* group = new OptionsGroup;
addGroup(group);
group->addOptions(options);
return group;
}
OptionsGroup*
ConsoleCliCommand::addGroupedOptions(std::vector options)
{
if (options.size() == 0) {
return 0;
}
OptionsGroup* group = new OptionsGroup;
addGroup(group);
group->addOptions(options);
return group;
}
void
ConsoleCliCommand::addGroup(OptionsGroup* group)
{
if (!mGroups) {
mGroups = new std::vector;
}
if (std::find(mGroups->begin(), mGroups->end(), group) == mGroups->end()) {
mGroups->push_back(group);
}
}
void
ConsoleCliCommand::addError(const ParseError* error)
{
if (!mErrors) {
mErrors = new std::vector;
}
mErrors->push_back(error);
}
bool
ConsoleCliCommand::hasErrors()
{
return mErrors && !mErrors->empty();
}
ConsoleCliCommand*
ConsoleCliCommand::isSubcommand(std::vector& cliArgs)
{
if (cliArgs.size() == 0) {
return 0;
}
std::vector::const_iterator it = mSubcommands->cbegin();
for (; it != mSubcommands->cend(); it++) {
if ((*it)->name().compare(cliArgs[0]) == 0) {
return *it;
}
}
return 0;
}
void
ConsoleCliCommand::analyseGroup(OptionsGroup* group,
std::vector& cliArgs)
{
std::vector* options = group->options();
if (options) {
bool optionFound = false;
std::vector::iterator it = options->begin();
for (; it != options->end(); it++) {
AnalysisResult* res = (*it)->analyse(cliArgs);
if (!res) {
continue;
}
if (res->values.first != "") {
if (optionFound && group != mMainGroup) {
addError(new ParseError(0,
"Error: Use only one option: " + group->optionsRepr()));
delete res;
return;
}
if (res->errorMsg == "") {
mOptionsMap.insert(res->values);
} else {
addError(new ParseError(*it, res->errorMsg));
}
cliArgs.erase(res->start, res->end);
optionFound = true;
}
delete res;
}
if (!optionFound && group->required())
addError(new ParseError(0, "Error: You have to use at least one "
"of these options: " + group->optionsRepr()));
}
}
ConsoleCliCommand*
ConsoleCliCommand::parse(std::vector& cliArgs)
{
clean();
if (mSubcommands) {
ConsoleCliCommand* subcommand = isSubcommand(cliArgs);
if (subcommand) {
std::vector subcommandArgs(cliArgs);
subcommandArgs.erase(subcommandArgs.begin());
return subcommand->parse(subcommandArgs);
}
}
if (mMainGroup) {
analyseGroup(mMainGroup, cliArgs);
}
if (mGroups) {
std::vector::const_iterator it;
for (it = mGroups->cbegin(); it != mGroups->cend(); it++) {
analyseGroup(*it, cliArgs);
}
}
int numArgsProcessed = (int) cliArgs.size();
if (mPositionalOptions) {
std::map::iterator pos_it =
mPositionalOptions->begin();
for (; pos_it != mPositionalOptions->end(); pos_it++) {
AnalysisResult* res = (*pos_it).second->analyse(cliArgs);
if (!res) { // not required and not found
continue;
}
if (res->errorMsg == "") {
mOptionsMap.insert(res->values);
} else {
addError(new ParseError((*pos_it).second, res->errorMsg));
}
numArgsProcessed -= (res->end - res->start);
delete res;
}
}
if (numArgsProcessed > 0) {
addError(new ParseError(0, "Error: Unknown arguments found."));
}
return this;
}
ConsoleCliCommand*
ConsoleCliCommand::parse(const std::string& cliArgs)
{
std::vector* cliArgsVector = splitKeywords(cliArgs, ' ');
ConsoleCliCommand* cmd = parse(*cliArgsVector);
delete cliArgsVector;
return cmd;
}
bool
ConsoleCliCommand::hasValue(std::string optionName)
{
return mOptionsMap.count(optionName);
}
bool
ConsoleCliCommand::hasValues()
{
return !mOptionsMap.empty();
}
std::string
ConsoleCliCommand::getValue(std::string optionName)
{
return mOptionsMap[optionName][0];
}
std::vector
ConsoleCliCommand::getValues(std::string optionName)
{
return mOptionsMap[optionName];
}
void
ConsoleCliCommand::printHelpForOptions(std::vector* options) const
{
std::vector::const_iterator it;
for (it = options->cbegin(); it != options->cend(); it++) {
char* str = (*it)->helpString();
if (str != NULL) {
fprintf(stdout, "%s", str);
free(str);
}
}
}
void
ConsoleCliCommand::printHelp() const
{
if (mMainGroup) {
printHelpForOptions(mMainGroup->options());
}
if (mGroups) {
for (size_t i = 0; i < mGroups->size(); i++) {
printHelpForOptions(mGroups->at(i)->options());
}
}
if (mPositionalOptions) {
std::map::const_iterator it;
for (it = mPositionalOptions->cbegin(); it != mPositionalOptions->cend();
it++) {
char* str = (*it).second->helpString();
if (str != NULL) {
fprintf(stdout, "%s", str);
free(str);
}
}
}
}
void
ConsoleCliCommand::printUsage() const
{
std::string subcommRepr = subcommandsRepr();
std::string kwRepr = keywordsRepr();
std::string posOptionsRepr = positionalOptionsRepr();
std::string commandAndOptions = "";
std::string fullCommandName = mName;
const ConsoleCliCommand* parent;
if (subcommRepr != "") {
commandAndOptions += " " + subcommRepr;
}
if (kwRepr != "") {
commandAndOptions += " " + kwRepr;
}
if (mGroups) {
std::vector::const_iterator it;
for (it = mGroups->cbegin(); it != mGroups->cend(); it++) {
std::string groupRepr((*it)->name());
if (groupRepr == "") {
groupRepr = (*it)->optionsRepr();
}
commandAndOptions += " " + ((*it)->required() ? groupRepr : "[" + groupRepr +
"]");
}
}
if (posOptionsRepr != "") {
commandAndOptions += " " + posOptionsRepr;
}
for (parent = mParentCommand; parent; parent = parent->parent()) {
fullCommandName = parent->name() + " " + fullCommandName;
}
commandAndOptions = fullCommandName + commandAndOptions;
fprintf(stdout, "Usage: %s", commandAndOptions.c_str());
if (mDescription != "") {
fprintf(stdout, " : %s", mDescription.c_str());
}
fprintf(stdout, "\n");
printHelp();
}
void
ConsoleCliCommand::printErrors() const
{
if (!mErrors) {
return;
}
std::string errorsStr("");
std::vector::const_iterator it;
for (it = mErrors->cbegin(); it != mErrors->cend(); it++) {
errorsStr += (*it)->message() + "\n";
}
fprintf(stdout, "%s", errorsStr.c_str());
}
void
ConsoleCliCommand::setParent(const ConsoleCliCommand* parent)
{
mParentCommand = parent;
}
std::string
ConsoleCliCommand::keywordsRepr() const
{
std::vector* options;
std::string repr("");
if (!mMainGroup) {
return repr;
}
options = mMainGroup->options();
if (!options) {
return repr;
}
for (size_t i = 0; i < options->size(); i++) {
if (options->at(i)->hidden()) {
continue;
}
std::string optionRepr = options->at(i)->repr();
if (!options->at(i)->required()) {
optionRepr = "[" + optionRepr + "]";
}
if (repr != "") {
repr += " ";
}
repr += optionRepr;
}
return repr;
}
std::string
ConsoleCliCommand::subcommandsRepr() const
{
std::string repr("");
if (!mSubcommands) {
return repr;
}
for (size_t i = 0; i < mSubcommands->size(); i++) {
repr += mSubcommands->at(i)->name();
if (i < mSubcommands->size() - 1) {
repr += "|";
}
}
if (mStandalone) {
repr = "[" + repr + "]";
}
return repr;
}
std::string
ConsoleCliCommand::positionalOptionsRepr() const
{
std::string repr("");
if (!mPositionalOptions) {
return repr;
}
std::map::const_iterator it;
for (it = mPositionalOptions->cbegin(); it != mPositionalOptions->cend();
it++) {
const std::string& currentRepr = (*it).second->repr();
repr += (*it).second->required() ? currentRepr : "[" + currentRepr + "]";
if (it == --mPositionalOptions->cend()) {
break;
}
repr += " ";
}
return repr;
}
OptionsGroup::OptionsGroup(std::string name)
: mName(name),
mOptions(0),
mRequired(false)
{}
OptionsGroup::OptionsGroup()
: mName(""),
mOptions(0),
mRequired(false)
{
}
OptionsGroup::OptionsGroup(const OptionsGroup& otherGroup)
: mName(otherGroup.mName),
mOptions(0),
mRequired(otherGroup.mRequired)
{
if (otherGroup.mOptions) {
for (size_t i = 0; i < otherGroup.mOptions->size(); i++) {
CliPositionalOption* positionalOption =
dynamic_cast(otherGroup.mOptions->at(i));
if (positionalOption) {
addOption(*positionalOption);
continue;
}
CliOptionWithArgs* optionWithArgs =
dynamic_cast(otherGroup.mOptions->at(i));
if (optionWithArgs) {
addOption(*optionWithArgs);
continue;
}
addOption(*otherGroup.mOptions->at(i));
}
}
}
OptionsGroup::~OptionsGroup()
{
if (mOptions) {
size_t i;
for (i = 0; i < mOptions->size(); i++) {
delete mOptions->at(i);
}
delete mOptions;
mOptions = 0;
}
}
void
OptionsGroup::addOption(CliOption* option)
{
if (!mOptions) {
mOptions = new std::vector;
}
if (option->group() != this) {
option->setGroup(this);
mOptions->push_back(option);
}
}
void
OptionsGroup::addOption(const CliOption& option)
{
CliOption* optionPtr = new CliOption(option);
optionPtr->setRequired(false);
addOption(optionPtr);
}
void
OptionsGroup::addOption(const CliOptionWithArgs& option)
{
CliOptionWithArgs* optionPtr = new CliOptionWithArgs(option);
optionPtr->setRequired(false);
addOption(optionPtr);
}
void
OptionsGroup::addOptions(std::vector options)
{
std::vector::const_iterator it = options.cbegin();
for (; it != options.cend(); it++) {
addOption(*it);
}
}
void
OptionsGroup::addOptions(std::vector options)
{
std::vector::const_iterator it = options.cbegin();
for (; it != options.cend(); it++) {
addOption(*it);
}
}
CliOption*
OptionsGroup::getOption(const std::string& name) const
{
std::vector::iterator it;
for (it = mOptions->begin(); it != mOptions->end(); it++) {
if ((*it)->name() == name) {
return *it;
}
}
return 0;
}
void
OptionsGroup::removeOption(CliOption* option)
{
option->setGroup(0);
if (!mOptions) {
return;
}
std::vector::iterator it;
for (it = mOptions->begin(); it != mOptions->end(); it++) {
if (*it == option) {
mOptions->erase(it);
return;
}
}
}
std::string
OptionsGroup::optionsRepr()
{
std::string repr = "";
if (!mOptions) {
return repr;
}
size_t i;
std::vector::iterator it;
for (i = 0; i < mOptions->size(); i++) {
if (mOptions->at(i)->hidden()) {
continue;
}
repr += mOptions->at(i)->repr();
if (i != mOptions->size() - 1 &&
!mOptions->at(i + 1)->hidden()) {
repr += "|";
}
}
return repr;
}