Skip to content
Snippets Groups Projects
ConfigOption.cpp 5.26 KiB
Newer Older
  • Learn to ignore specific revisions
  • Chao Peng's avatar
    Chao Peng committed
    //============================================================================//
    // 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;
    }