From 529c1aacb482aeef5f79f85bf648aff02e0405f6 Mon Sep 17 00:00:00 2001 From: Paul Asmuth Date: Sat, 9 May 2020 18:35:01 +0200 Subject: [PATCH] switch to getopt for option parsing --- src/cli.cc | 37 ++--- src/utils/flagparser.cc | 321 +++++++++++++--------------------------- src/utils/flagparser.h | 106 +++---------- 3 files changed, 142 insertions(+), 322 deletions(-) diff --git a/src/cli.cc b/src/cli.cc index 393af9e6d..874aa4b7d 100644 --- a/src/cli.cc +++ b/src/cli.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include "config.h" #include "context.h" @@ -32,45 +33,40 @@ void printError(const ReturnCode& rc) { std::cerr << fmt::format("ERROR: {}", rc.message) << std::endl; } -int main(int argc, const char** argv) { - FlagParser flag_parser; +int main(int argc, char** argv) { + FlagList flags; std::string flag_in; - flag_parser.defineString("in", false, &flag_in); + flags_add_string(&flags, 'i', "in", &flag_in); std::string flag_out; - flag_parser.defineString("out", false, &flag_out); + flags_add_string(&flags, 'e', "out", &flag_out); bool flag_stdin = false; - flag_parser.defineSwitch("stdin", &flag_stdin); + flags_add_switch(&flags, 0, "stdin", &flag_stdin); bool flag_stdout = false; - flag_parser.defineSwitch("stdout", &flag_stdout); + flags_add_switch(&flags, 0, "stdout", &flag_stdout); std::string flag_format; - flag_parser.defineString("format", false, &flag_format); + flags_add_string(&flags, 'f', "format", &flag_format); bool flag_help = false; - flag_parser.defineSwitch("help", &flag_help); + flags_add_switch(&flags, 'h', "help", &flag_help); bool flag_version = false; - flag_parser.defineSwitch("version", &flag_version); + flags_add_switch(&flags, 'v', "version", &flag_version); bool flag_debug = true; - flag_parser.defineSwitch("debug", &flag_debug); - - bool flag_font_defaults = true; - flag_parser.defineBool("font-defaults", &flag_font_defaults); + flags_add_switch(&flags, 'd', "debug", &flag_debug); std::vector flag_font_load; - flag_parser.defineStringV("font-load", &flag_font_load); + flags_add_stringv(&flags, 0, "font-load", &flag_font_load); - { - auto rc = flag_parser.parseArgv(argc - 1, argv + 1); - if (!rc) { - std::cerr << "ERROR: " << rc.message << std::endl; - return -1; - } + std::vector args; + if (auto rc = flags_parse(flags, argc, argv, &args); !rc) { + std::cerr << "ERROR: " << rc.message << std::endl; + return EXIT_FAILURE; } if (flag_version) { @@ -156,7 +152,6 @@ int main(int argc, const char** argv) { /* set up the context */ Context ctx; - ctx.font_defaults = flag_font_defaults; ctx.font_load = flag_font_load; if (auto rc = context_setup_defaults(&ctx); !rc) { diff --git a/src/utils/flagparser.cc b/src/utils/flagparser.cc index 173ce80f2..1bae15c64 100644 --- a/src/utils/flagparser.cc +++ b/src/utils/flagparser.cc @@ -11,252 +11,135 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "flagparser.h" + #include #include #include -#include "flagparser.h" +#include namespace clip { -FlagParser::FlagParser() {} - -void FlagParser::defineString( - const char* longopt, - bool required, - std::string* value) { - FlagState flag_state; - flag_state.type = T_STRING; - flag_state.required = required; - flag_state.longopt = longopt; - flag_state.value = static_cast(value); - flag_state.has_value = false; - flags_.emplace_back(flag_state); +void flags_add_string( + FlagList* flags, + char shortopt, + const std::string& longopt, + std::string* value) { + FlagInfo flag; + flag.opt_short = shortopt; + flag.opt_long = longopt; + flag.has_arg = true; + + flag.callback = [value] (auto v) { + *value = v ? std::string(v) : std::string(); + }; + + flags->push_back(flag); } -void FlagParser::defineStringV( - const char* longopt, - std::vector* value) { - FlagState flag_state; - flag_state.type = T_STRING_V; - flag_state.required = false; - flag_state.longopt = longopt; - flag_state.value = static_cast(value); - flag_state.has_value = false; - flags_.emplace_back(flag_state); +void flags_add_stringv( + FlagList* flags, + char shortopt, + const std::string& longopt, + std::vector* values) { + FlagInfo flag; + flag.opt_short = shortopt; + flag.opt_long = longopt; + flag.has_arg = true; + + flag.callback = [values] (auto v) { + values->push_back(v ? std::string(v) : std::string()); + }; + + flags->push_back(flag); } -void FlagParser::defineBool( - const char* longopt, - bool* value) { - FlagState flag_state; - flag_state.type = T_BOOL; - flag_state.required = false; - flag_state.longopt = longopt; - flag_state.value = static_cast(value); - flag_state.has_value = false; - flags_.emplace_back(flag_state); +void flags_add_switch( + FlagList* flags, + char shortopt, + const std::string& longopt, + bool* value) { + FlagInfo flag; + flag.opt_short = shortopt; + flag.opt_long = longopt; + flag.has_arg = false; + + flag.callback = [value] (auto _) { + *value = true; + }; + + flags->push_back(flag); } -void FlagParser::defineSwitch( - const char* longopt, - bool* value) { - FlagState flag_state; - flag_state.type = T_SWITCH; - flag_state.required = false; - flag_state.longopt = longopt; - flag_state.value = static_cast(value); - flag_state.has_value = false; - flags_.emplace_back(flag_state); -} - -void FlagParser::defineInt64( - const char* longopt, - bool required, - int64_t* value) { - FlagState flag_state; - flag_state.type = T_INT64; - flag_state.required = required; - flag_state.longopt = longopt; - flag_state.value = static_cast(value); - flag_state.has_value = false; - flags_.emplace_back(flag_state); -} - -void FlagParser::defineUInt64( - const char* longopt, - bool required, - uint64_t* value) { - FlagState flag_state; - flag_state.type = T_UINT64; - flag_state.required = required; - flag_state.longopt = longopt; - flag_state.value = static_cast(value); - flag_state.has_value = false; - flags_.emplace_back(flag_state); -} - -void FlagParser::defineFloat64( - const char* longopt, - bool required, - double* value) { - FlagState flag_state; - flag_state.type = T_FLOAT64; - flag_state.required = required; - flag_state.longopt = longopt; - flag_state.value = static_cast(value); - flag_state.has_value = false; - flags_.emplace_back(flag_state); -} - -ReturnCode parseValue(FlagState* flag, const std::string& value) { - switch (flag->type) { - case T_STRING: - *static_cast(flag->value) = value; - break; - - case T_STRING_V: - static_cast*>(flag->value)->push_back(value); - break; - - case T_SWITCH: - case T_BOOL: - if (value == "on") { - *static_cast(flag->value) = true; - } else if (value == "off") { - *static_cast(flag->value) = false; - } else { - return errorf( - ERROR, - "flag --{}=... invalid value", - flag->longopt); - } - break; - - case T_INT64: - try { - *static_cast(flag->value) = std::stoll(value); - } catch (std::exception e) { - return errorf( - ERROR, - "flag --{}=... invalid value", - flag->longopt); - } - break; - - case T_UINT64: - try { - *static_cast(flag->value) = std::stoull(value); - } catch (std::exception e) { - return errorf( - ERROR, - "flag --{}=... invalid value", - flag->longopt); - } - break; - - case T_FLOAT64: - try { - *static_cast(flag->value) = std::stod(value); - } catch (std::exception e) { - return errorf( - ERROR, - "flag --{}=... invalid value", - flag->longopt); - } - break; - - default: - return errorf( - ERROR, - "flag --{}=... invalid type", - flag->longopt); - } - - flag->has_value = true; - return OK; -} - -ReturnCode FlagParser::parseArgv(int argc, const char** argv) { - std::vector args; - for (int i = 0; i < argc; ++i) { - args.emplace_back(argv[i]); +ReturnCode flags_parse( + const FlagList& flags, + int argc, + char** argv, + std::vector* args_out) { + // prepapre long opts + std::vector opts_long(flags.size() + 1); + opts_long[flags.size()] = {0, 0, 0, 0}; + + for (size_t i = 0; i < flags.size(); ++i) { + opts_long[i] = { + flags[i].opt_long.c_str(), + flags[i].has_arg ? 1 : 0, + 0, + 0 + }; } - return parseArgv(args); -} -ReturnCode FlagParser::parseArgv(const std::vector& argv) { - for (int i = 0; i < argv.size(); i++) { - int eq_len = -1; - FlagState* flag_ptr = nullptr; - auto& arg = argv[i]; - - if (arg.size() == 0) { + // prepare short opts + std::string opts_short; + for (const auto& f : flags) { + if (f.opt_short == 0) { continue; } - for (auto& flag : flags_) { - auto longopt = std::string("--") + flag.longopt; - auto longopt_eq = std::string("--") + flag.longopt + "="; - - if (arg.size() == longopt.size() && - arg.compare(0, longopt.size(), longopt) == 0) { - flag_ptr = &flag; - } - - else if (arg.size() == longopt_eq.size() && - arg.compare(0, longopt_eq.size(), longopt_eq) == 0) { - flag_ptr = &flag; - eq_len = longopt_eq.size(); - } + opts_short.push_back(f.opt_short); - if (flag_ptr != nullptr) { - break; - } + if (f.has_arg) { + opts_short.push_back(':'); } + } - if (flag_ptr == nullptr) { - return errorf( - ERROR, - "invalid flag: {}", - arg); - } else if (eq_len > 0) { - if (arg.size() == eq_len) { - return errorf( - ERROR, - "flag --{}=... has no value", - flag_ptr->longopt); - } + // call getopt + for (;;) { + int opt_long = 0; + int opt_short = getopt_long( + argc, + argv, + opts_short.c_str(), + opts_long.data(), + &opt_long); + + if (opt_short == -1) { + break; + } - auto rc = parseValue(flag_ptr, arg.substr(eq_len)); - if (!rc) { - return rc; - } - } else if (flag_ptr->type == T_SWITCH) { - auto rc = parseValue(flag_ptr, "on"); - if (!rc) { - return rc; - } + const FlagInfo* flag = nullptr; + if (opt_short == 0) { + flag = &flags[opt_long]; } else { - if (i + 1 < argv.size()) { - auto rc = parseValue(flag_ptr, argv[++i]); - if (!rc) { - return rc; + for (const auto& f : flags) { + if (opt_short == f.opt_short) { + flag = &f; + break; } - } else if (flag_ptr->required) { - return errorf( - ERROR, - "flag --{} has no value", - flag_ptr->longopt); } } + + if (!flag) { + return error(ERROR, "invalid option"); + } + + flag->callback(optarg); } - for (const auto& flag : flags_) { - if (flag.required == true && !flag.has_value) { - return errorf( - ERROR, - "flag --{} is required", - flag.longopt); + // copy unparsed arguments + if (args_out) { + for (size_t i = optind; i < argc; ++i) { + args_out->push_back(argv[i]); } } diff --git a/src/utils/flagparser.h b/src/utils/flagparser.h index 629751a65..3c6307e89 100644 --- a/src/utils/flagparser.h +++ b/src/utils/flagparser.h @@ -18,95 +18,37 @@ namespace clip { -enum kFlagType { - T_BOOL, - T_SWITCH, - T_STRING, - T_STRING_V, - T_INT64, - T_UINT64, - T_FLOAT64, +struct FlagInfo { + char opt_short; + std::string opt_long; + bool has_arg; + std::function callback; }; -struct FlagState { - kFlagType type; - bool required; - const char* longopt; - std::vector values; - void* value; - bool has_value; -}; - -class FlagParser { -public: - - FlagParser(); +using FlagList = std::vector; - /** - * Define a string flag - */ - void defineString( - const char* longopt, - bool required, +void flags_add_string( + FlagList* flags, + char shortopt, + const std::string& longopt, std::string* value); - /** - * Define a vec flag - */ - void defineStringV( - const char* longopt, - std::vector* value); +void flags_add_stringv( + FlagList* flags, + char shortopt, + const std::string& longopt, + std::vector* values); - /** - * Define a boolean flag - */ - void defineBool( - const char* longopt, +void flags_add_switch( + FlagList* flags, + char shortopt, + const std::string& longopt, bool* value); - /** - * Define a boolean flag - */ - void defineSwitch( - const char* longopt, - bool* value); - - /** - * Define a int64 flag - */ - void defineInt64( - const char* longopt, - bool required, - int64_t* value); - - /** - * Define a uint64 flag - */ - void defineUInt64( - const char* longopt, - bool required, - uint64_t* value); - - /** - * Define a float64 flag - */ - void defineFloat64( - const char* longopt, - bool required, - double* value); - - /** - * Parse an argv array. This may throw an exception. - */ - ReturnCode parseArgv(int argc, const char** argv); - - /** - * Parse an argv array. This may throw an exception. - */ - ReturnCode parseArgv(const std::vector& argv); - -protected: - std::vector flags_; -}; +ReturnCode flags_parse( + const FlagList& flags, + int argc, + char** argv, + std::vector* args_out); }