#include void scc_optparse_init(scc_optparse_t *parser, int argc, const char **argv) { parser->argc = argc; parser->argv = argv; parser->handle_positional = 0; parser->opts = 0; parser->prev = 0; parser->current_arg_pos = 1; parser->short_opt_pos = 0; } static inline const char *str_chr(const char *s, char c) { while (*s && *s != c) s++; return *s ? s : 0; } scc_optparse_opt_t *scc_optparse_get_long_name(scc_optparse_opt_t *opts, const char *name, const char end) { if (*name == '\0') return 0; for (scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) { if (!opt->long_name) { continue; } int i; for (i = 0; opt->long_name[i]; ++i) { if (name[i] != opt->long_name[i]) { break; } } if (name[i] == '\0' || name[i] == end) return opt; } return 0; } scc_optparse_opt_t *scc_optparse_get_short_name(scc_optparse_opt_t *opts, const char short_name) { for (scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) { if (opt->short_name == short_name) { return opt; } } return 0; } static inline void scc_optparse_parse_long_name(scc_optparse_opt_t *opts, scc_optparse_result_t *res, const char *arg) { // caller MUST use `--` start arg scc_optparse_opt_t *ret = 0; const char *chr = str_chr(arg, '='); if (chr) { // --file=a.txt ret = scc_optparse_get_long_name(opts, arg + 2, '='); if (ret == 0) { res->error = SCC_OPT_ERROR_NOT_FOUND_LONG_ARG; } else { res->opt = ret; if (chr[1] == '\0') { // TODO maybe can empty like -file= res->error = SCC_OPT_ERROR_NOT_ENOUGH_ARGS; } res->value = chr + 1; } } else { // --file a.txt ret = scc_optparse_get_long_name(opts, arg + 2, '\0'); if (ret == 0) { res->error = SCC_OPT_ERROR_NOT_FOUND_LONG_ARG; } else { res->opt = ret; } } } static inline int scc_optparse_parse_short_name(scc_optparse_opt_t *opts, scc_optparse_result_t *res, const char *arg, int arg_start) { for (int i = arg_start; arg[i]; ++i) { scc_optparse_opt_t *ret = scc_optparse_get_short_name(opts, arg[i]); if (ret == 0) { res->error = SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG; return 0; } res->opt = ret; if (res->opt->parsed_args < res->opt->max_args && arg[i + 1] != '\0') { res->value = arg + i + 1; res->opt->parsed_args++; return 0; } return arg[i + 1] ? i + 1 : 0; } return 0; } static inline void scc_optparse_parse_position_arg(scc_optparse_t *parser, const char *arg, scc_optparse_result_t *res) { parser->current_arg_pos++; res->value = arg; } void scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res) { scc_optparse_opt_t *opts = parser->opts; res->opt = 0; res->value = 0; res->error = SCC_OPT_ERROR_NONE; if (parser->short_opt_pos != 0) { parser->short_opt_pos = scc_optparse_parse_short_name( opts, res, parser->argv[parser->current_arg_pos], parser->short_opt_pos); if (parser->short_opt_pos == 0) { parser->short_opt_pos = 0; parser->current_arg_pos += 1; } goto RETURN; } while (parser->current_arg_pos < parser->argc) { const char *arg = parser->argv[parser->current_arg_pos]; if (arg[0] == '\0' || parser->handle_positional) { return scc_optparse_parse_position_arg(parser, arg, res); } for (const scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) { if (arg[0] != opt->prefix) { continue; } if (arg[1] == opt->prefix) { // long option like -- if (arg[2] == '\0') { // the `--` res->value = arg; parser->handle_positional = 1; parser->current_arg_pos++; goto RETURN; } scc_optparse_parse_long_name(opts, res, arg); if (!res->opt || res->error) { goto RETURN; } parser->prev = 0; if (res->value) res->opt->parsed_args++; parser->current_arg_pos++; if (res->opt->parsed_args < res->opt->max_args && res->opt->max_args > 0) { parser->prev = res->opt; } if (res->opt->parsed_args < res->opt->min_args && !res->value) { goto NEXT; } goto RETURN; } else { // short option like - if (arg[1] == '\0') { // the `-` res->value = arg; parser->handle_positional = 1; parser->current_arg_pos++; goto RETURN; } parser->short_opt_pos = scc_optparse_parse_short_name(opts, res, arg, 1); if (!res->opt || res->error) { goto RETURN; } if (parser->short_opt_pos == 0) { parser->current_arg_pos++; } parser->prev = 0; if (res->opt->parsed_args < res->opt->max_args && res->opt->max_args > 0) { parser->prev = res->opt; } if (res->opt->parsed_args < res->opt->min_args) { goto NEXT; } goto RETURN; } } // position arg if (parser->prev && parser->prev->parsed_args < parser->prev->max_args) { res->opt = parser->prev; res->value = arg; res->opt->parsed_args++; parser->current_arg_pos++; goto RETURN; } else { scc_optparse_parse_position_arg(parser, arg, res); goto RETURN; } NEXT: continue; } RETURN: if (res->opt && res->opt->max_args > 0 && res->opt->parsed_args > res->opt->max_args) { res->error = SCC_OPT_ERROR_TOO_MANY_ARGS; } return; }