/** * @file argparser.cc * @brief Implementation of the command line argument parser * @author Andreas Aardal Hanssen * @date 2005 */ #include "argparser.h" #include "convert.h" #include #include using namespace Binc; using std::string; CommandLineArgs::CommandLineArgs() { errString = "Unknown error for args"; ac = 0; } bool CommandLineArgs::parse(int argc, char *argv[]) { ac = -1; head = argv[0]; head += " --\n"; if (argc > 1) { string lastKey; bool lastIsBoolean = false; for (int i = 1; i < argc; ++i) { string s = argv[i]; if (s.length() < 2) { unqualified.push_back(s); continue; } if (s[0] != '-') { // read value of last argument if (lastKey == "") { unqualified.push_back(s); continue; } if (lastIsBoolean && (s != "yes" && s != "no")) { errString = "syntax error: " + s; errString += " (expected yes or no)"; return false; } args[lastKey] = s; passedArgs[lastKey] = true; lastKey = ""; lastIsBoolean = false; } else if (s[1] == '-') { if (lastKey != "") { if (lastIsBoolean) { args[lastKey] = "yes"; passedArgs[lastKey] = true; lastKey = ""; lastIsBoolean = false; } else { errString = "expected value of "; errString += lastKey; return false; } } // break if '--' is detected if (s.length() == 2) { ac = i + 1; break; } // parse --argument string arg = s.substr(2); string val; auto epos = arg.find('='); if (epos != string::npos) { val = arg.substr(epos + 1); arg = arg.substr(0, epos); } if (reg.find(arg) == reg.end()) { errString = "unrecognized argument: --" + arg; return false; } if (reg.find(arg)->second.b) { if (val != "" && val != "yes" && val != "no") { errString = "syntax error: " + val; errString += " (expected yes or no)"; return false; } else if (val == "") { val = "yes"; } } if (val == "") { errString = "syntax error: " + arg; errString += " (expected --" + arg + "=)"; return false; } args[arg] = val; passedArgs[arg] = true; lastKey = ""; lastIsBoolean = false; } else { if (lastKey != "") { if (lastIsBoolean) { args[lastKey] = "yes"; passedArgs[lastKey] = true; lastKey = ""; lastIsBoolean = false; } else { errString = "expected value of "; errString += lastKey; return false; } } // parse -argument string arg = s.substr(1); if (arg.length() == 1) { bool match = false; for (const auto &[first, second] : reg) { if (second.c.find(arg[0]) != string::npos) { lastKey = first; if (second.b) lastIsBoolean = true; match = true; break; } } if (!match) { errString = "unrecognized argument: -"; errString += arg[0]; return false; } } else { for (auto its : arg) { bool match = false; for (const auto &[first, second] : reg) { if (second.c.find(its) != string::npos) { if (!second.b) { errString = "argument is not a boolean: "; errString += "--" + first; errString += " / -"; errString += second.c; return false; } match = true; args[first] = "yes"; passedArgs[first] = true; lastKey = ""; lastIsBoolean = false; break; } } if (!match) { errString = "unrecognized argument: "; errString += s; return false; } } } } } if (lastKey != "") { if (lastIsBoolean) { args[lastKey] = "yes"; passedArgs[lastKey] = true; } else { errString = "expected value of "; errString += lastKey; return false; } } } // assign default "no" values for arguments that were not passed. for (const auto &[first, second] : reg) { if (args.find(first) == args.end()) { if (!second.o) { errString = "missing argument: "; errString += first; return false; } if (second.b) args[first] = "no"; } } if (ac == -1) ac = argc; return true; } string CommandLineArgs::errorString() const { return errString; } const string CommandLineArgs::operator[](const string &arg) const { if (args.find(arg) == args.end()) return ""; return args.find(arg)->second; } void CommandLineArgs::addOptional(const string &arg, const string &desc, bool boolean) { registerArg(arg, desc, boolean, true); } void CommandLineArgs::addRequired(const string &arg, const string &desc, bool boolean) { registerArg(arg, desc, boolean, false); } void CommandLineArgs::registerArg(const string &arg, const string &desc, bool boolean, bool optional) { string name = arg; string shorts; while (name.size() > 1 && name[1] == '|') { shorts += name[0]; name = name.substr(2); } reg.insert(make_pair(name, ArgOpts{shorts, boolean, optional, desc})); } bool CommandLineArgs::hasArg(const string &arg) const { string tmp = arg; lowercase(tmp); return passedArgs.find(tmp) != passedArgs.end(); } string CommandLineArgs::usageString() const { string tmp = head; tmp += '\n'; for (const auto &[first, second] : reg) { if (!second.c.empty()) { for (auto sit = second.c.cbegin(); sit != second.c.cend(); ++sit) { if (sit != second.c.begin()) tmp += '\n'; tmp += " -"; tmp += *sit; } tmp += ", "; } else { tmp += " "; } tmp += "--" + first; if (!second.b) tmp += "="; if (!second.o) tmp += " (required)"; auto lineStart = tmp.rfind('\n'); if (lineStart == string::npos) lineStart = 0; int pad = 21 - (tmp.length() - lineStart); if (pad < 0) { tmp += '\n'; pad = 20; } tmp += string(pad, ' ') + second.desc + '\n'; } tmp += '\n' + tail + '\n'; return tmp; } int CommandLineArgs::argc() const { return ac; } void CommandLineArgs::setTail(const string &str) { tail = str; } const std::vector &CommandLineArgs::getUnqualifiedArgs() const { return unqualified; }