mirror of
https://github.com/aportelli/LatAnalyze.git
synced 2024-11-10 08:55:37 +00:00
300 lines
8.6 KiB
C++
300 lines
8.6 KiB
C++
/*
|
|
* OptParser.cpp, part of LatAnalyze
|
|
*
|
|
* Copyright (C) 2016 Antonin Portelli
|
|
*
|
|
* LatAnalyze 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.
|
|
*
|
|
* LatAnalyze 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 LatAnalyze. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <LatAnalyze/Core/OptParser.hpp>
|
|
#include <LatAnalyze/includes.hpp>
|
|
|
|
using namespace std;
|
|
using namespace Latan;
|
|
|
|
static char optRegex[] = "(-([a-zA-Z])(.+)?)|(--([a-zA-Z_-]+)=?(.+)?)";
|
|
|
|
/******************************************************************************
|
|
* OptParser implementation *
|
|
******************************************************************************/
|
|
// regular expressions /////////////////////////////////////////////////////////
|
|
const regex OptParser::optRegex_(optRegex);
|
|
|
|
// access //////////////////////////////////////////////////////////////////////
|
|
void OptParser::addOption(const std::string shortName,
|
|
const std::string longName,
|
|
const OptType type, const bool optional,
|
|
const std::string helpMessage,
|
|
const std::string defaultVal)
|
|
{
|
|
OptPar par;
|
|
|
|
par.shortName = shortName;
|
|
par.longName = longName;
|
|
par.defaultVal = defaultVal;
|
|
par.helpMessage = helpMessage;
|
|
par.type = type;
|
|
par.optional = optional;
|
|
auto it = std::find_if(opt_.begin(), opt_.end(), [&par](const OptPar & p)
|
|
{
|
|
bool match = false;
|
|
|
|
match |= (par.shortName == p.shortName) and !par.shortName.empty();
|
|
match |= (par.longName == p.longName) and !par.longName.empty();
|
|
|
|
return match;
|
|
});
|
|
if (it != opt_.end())
|
|
{
|
|
string opt;
|
|
|
|
if (!it->shortName.empty())
|
|
{
|
|
opt += "-" + it->shortName;
|
|
}
|
|
if (!opt.empty())
|
|
{
|
|
opt += "/";
|
|
}
|
|
if (!it->longName.empty())
|
|
{
|
|
opt += "--" + it->longName;
|
|
}
|
|
throw(logic_error("duplicate option " + opt + " (in the code, not in the command line)"));
|
|
}
|
|
opt_.push_back(par);
|
|
}
|
|
|
|
bool OptParser::gotOption(const std::string name) const
|
|
{
|
|
int i = optIndex(name);
|
|
|
|
if (result_.size() != opt_.size())
|
|
{
|
|
throw(runtime_error("options not parsed"));
|
|
}
|
|
if (i >= 0)
|
|
{
|
|
return result_[i].present;
|
|
}
|
|
else
|
|
{
|
|
throw(out_of_range("no option with name '" + name + "'"));
|
|
}
|
|
}
|
|
|
|
const vector<string> & OptParser::getArgs(void) const
|
|
{
|
|
return arg_;
|
|
}
|
|
|
|
// parse ///////////////////////////////////////////////////////////////////////
|
|
bool OptParser::parse(int argc, char *argv[])
|
|
{
|
|
smatch sm;
|
|
queue<string> arg;
|
|
int expectVal = -1;
|
|
bool isCorrect = true;
|
|
|
|
for (int i = 1; i < argc; ++i)
|
|
{
|
|
arg.push(argv[i]);
|
|
}
|
|
result_.clear();
|
|
result_.resize(opt_.size());
|
|
arg_.clear();
|
|
for (unsigned int i = 0; i < opt_.size(); ++i)
|
|
{
|
|
result_[i].value = opt_[i].defaultVal;
|
|
}
|
|
while (!arg.empty())
|
|
{
|
|
// option
|
|
if (regex_match(arg.front(), sm, optRegex_))
|
|
{
|
|
// should it be a value?
|
|
if (expectVal >= 0)
|
|
{
|
|
cerr << "warning: expected value for option ";
|
|
cerr << optName(opt_[expectVal]);
|
|
cerr << ", got option '" << arg.front() << "' instead" << endl;
|
|
expectVal = -1;
|
|
isCorrect = false;
|
|
}
|
|
// short option
|
|
if (sm[1].matched)
|
|
{
|
|
string optName = sm[2].str();
|
|
|
|
// find option
|
|
auto it = find_if(opt_.begin(), opt_.end(),
|
|
[&optName](const OptPar &p)
|
|
{
|
|
return (p.shortName == optName);
|
|
});
|
|
|
|
// parse if found
|
|
if (it != opt_.end())
|
|
{
|
|
unsigned int i = it - opt_.begin();
|
|
|
|
result_[i].present = true;
|
|
if (opt_[i].type == OptType::value)
|
|
{
|
|
if (sm[3].matched)
|
|
{
|
|
result_[i].value = sm[3].str();
|
|
}
|
|
else
|
|
{
|
|
expectVal = i;
|
|
}
|
|
}
|
|
}
|
|
// warning if not found
|
|
else
|
|
{
|
|
cerr << "warning: unknown option '" << arg.front() << "'";
|
|
cerr << endl;
|
|
}
|
|
}
|
|
// long option
|
|
else if (sm[4].matched)
|
|
{
|
|
string optName = sm[5].str();
|
|
|
|
// find option
|
|
auto it = find_if(opt_.begin(), opt_.end(),
|
|
[&optName](const OptPar &p)
|
|
{
|
|
return (p.longName == optName);
|
|
});
|
|
|
|
// parse if found
|
|
if (it != opt_.end())
|
|
{
|
|
unsigned int i = it - opt_.begin();
|
|
|
|
result_[i].present = true;
|
|
if (opt_[i].type == OptType::value)
|
|
{
|
|
if (sm[6].matched)
|
|
{
|
|
result_[i].value = sm[6].str();
|
|
}
|
|
else
|
|
{
|
|
expectVal = i;
|
|
}
|
|
}
|
|
}
|
|
// warning if not found
|
|
else
|
|
{
|
|
cerr << "warning: unknown option '" << arg.front() << "'";
|
|
cerr << endl;
|
|
}
|
|
}
|
|
}
|
|
else if (expectVal >= 0)
|
|
{
|
|
result_[expectVal].value = arg.front();
|
|
expectVal = -1;
|
|
}
|
|
else
|
|
{
|
|
arg_.push_back(arg.front());
|
|
}
|
|
arg.pop();
|
|
}
|
|
if (expectVal >= 0)
|
|
{
|
|
cerr << "warning: expected value for option ";
|
|
cerr << optName(opt_[expectVal]) << endl;
|
|
expectVal = -1;
|
|
isCorrect = false;
|
|
}
|
|
for (unsigned int i = 0; i < opt_.size(); ++i)
|
|
{
|
|
if (!opt_[i].optional and !result_[i].present)
|
|
{
|
|
cerr << "warning: mandatory option " << optName(opt_[i]);
|
|
cerr << " is missing" << endl;
|
|
isCorrect = false;
|
|
}
|
|
}
|
|
|
|
return isCorrect;
|
|
}
|
|
|
|
// find option index ///////////////////////////////////////////////////////////
|
|
int OptParser::optIndex(const string name) const
|
|
{
|
|
auto it = find_if(opt_.begin(), opt_.end(), [&name](const OptPar &p)
|
|
{
|
|
return (p.shortName == name) or (p.longName == name);
|
|
});
|
|
|
|
if (it != opt_.end())
|
|
{
|
|
return static_cast<int>(it - opt_.begin());
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// option name for messages ////////////////////////////////////////////////////
|
|
std::string OptParser::optName(const OptPar &opt)
|
|
{
|
|
std::string res = "";
|
|
|
|
if (!opt.shortName.empty())
|
|
{
|
|
res += "-" + opt.shortName;
|
|
if (!opt.longName.empty())
|
|
{
|
|
res += "/";
|
|
}
|
|
}
|
|
if (!opt.longName.empty())
|
|
{
|
|
res += "--" + opt.longName;
|
|
if (opt.type == OptParser::OptType::value)
|
|
{
|
|
res += "=";
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
// print option list ///////////////////////////////////////////////////////////
|
|
std::ostream & Latan::operator<<(std::ostream &out, const OptParser &parser)
|
|
{
|
|
for (auto &o: parser.opt_)
|
|
{
|
|
out << setw(20) << OptParser::optName(o);
|
|
out << ": " << o.helpMessage;
|
|
if (!o.defaultVal.empty())
|
|
{
|
|
out << " (default: " << o.defaultVal << ")";
|
|
}
|
|
out << endl;
|
|
}
|
|
|
|
return out;
|
|
}
|