ConfigOption.cpp 5.26 KiB
//============================================================================//
// 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;
}