/* * 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 . */ #include #include 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 & OptParser::getArgs(void) const { return arg_; } // parse /////////////////////////////////////////////////////////////////////// bool OptParser::parse(int argc, char *argv[]) { smatch sm; queue 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(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; }