//============================================================================// // A class to simplify the argument parsing procedure // // // // Chao Peng // // 07/26/2017 // //============================================================================// #include "ConfigOption.h" #include "ConfigParser.h" #include <iostream> #include <cstring> ConfigOption::ConfigOption() { // place holder } ConfigOption::~ConfigOption() { // place holder } void ConfigOption::AddOpt(OptType type, char s_term) { AddOpt(type, s_term, s_term); } void ConfigOption::AddLongOpt(OptType type, const char *l_term) { AddLongOpt(type, l_term, *l_term); } void ConfigOption::AddOpts(OptType type, char s_term, const char *l_term) { AddOpts(type, s_term, l_term, s_term); } void ConfigOption::AddOpt(OptType type, char s_term, char mark) { if(s_opt_map.find(s_term) != s_opt_map.end()) { std::cerr << "ConfigOption: Short option " << s_term << " has already been added, abort AddOpt." << std::endl; return; } s_opt_map[s_term] = Opt(mark, type); } void ConfigOption::AddLongOpt(OptType type, const char *l_term, char mark) { if(l_opt_map.find(l_term) != l_opt_map.end()) { std::cerr << "ConfigOption: Long option " << l_term << " has already been added, abort AddOpt." << std::endl; return; } l_opt_map[l_term] = Opt(mark, type); } void ConfigOption::AddOpts(OptType type, char s_term, const char *l_term, char mark) { AddOpt(type, s_term, mark); AddLongOpt(type, l_term, mark); } bool ConfigOption::parseLongOpt(const char *arg) { auto vars = ConfigParser::split(arg, strlen(arg), "="); std::string key = std::move(vars.front()); vars.pop_front(); auto it = l_opt_map.find(key); if(it == l_opt_map.end()) { std::cerr << "Unknown option --" << key << std::endl; return false; } switch(it->second.type) { case arg_require: if(vars.empty()) { std::cerr << "Lack of argument for option --" << key << std::endl; return false; } opt_pack.emplace_back(it->second.mark, it->second.type, std::move(vars.front())); break; case arg_none: opt_pack.emplace_back(it->second.mark, it->second.type); break; case help_message: return false; } return true; } bool ConfigOption::parseShortOpt(char key, int argc, char *argv[], int &idx) { auto it = s_opt_map.find(key); if(it == s_opt_map.end()) { std::cerr << "Unknown option -" << key << std::endl; return false; } switch(it->second.type) { case arg_require: if((idx + 1) >= argc || *argv[idx + 1] == '-') { std::cerr << "Lack of argument for option -" << key << std::endl; return false; } opt_pack.emplace_back(it->second.mark, it->second.type, ConfigValue(argv[++idx])); break; case arg_none: opt_pack.emplace_back(it->second.mark, it->second.type); break; case help_message: return false; } return true; } bool ConfigOption::ParseArgs(int argc, char *argv[]) { if (argc < 1) return false; argv0 = argv[0]; bool success = true; arg_pack.clear(); opt_pack.clear(); for(int i = 1; i < argc; ++i) { char* ptr = argv[i]; // option if(*ptr == '-') { // long option if(*(ptr + 1) == '-') { success &= parseLongOpt(ptr + 2); // short option } else { success &= parseShortOpt(*(ptr + 1), argc, argv, i); } // arguments } else { arg_pack.emplace_back(ptr); } } return success; } void ConfigOption::SetDesc(const char *desc) { base_desc = desc; } void ConfigOption::SetDesc(char mark, const char *desc) { std::string option; bool found_mark = false; for(auto &it : s_opt_map) { if(it.second.mark != mark) continue; option = " - "; option.back() = it.first; if(it.second.type == arg_require) option += " <arg>"; option += ","; found_mark = true; } for(auto &it : l_opt_map) { if(it.second.mark != mark) continue; option += " --" + it.first; if(it.second.type == arg_require) option += "=<arg>"; option += ","; found_mark = true; } if(!found_mark) { std::cerr << "Config Option: Cannot find any option with mark <" << mark << ">, abort adding description." << std::endl; return; } option.back() = ':'; option += " "; option += desc; option_desc.emplace_back(option); } std::string ConfigOption::GetInstruction() { std::string res = base_desc + "\noptions:\n"; auto pvar = res.find("%0"); if (pvar != std::string::npos) { res.replace(pvar, 2, argv0); } for(auto &option : option_desc) { res += "\t" + option + "\n"; } return res; }