#include void scc_argparse_init(scc_argparse_t *parser, const char *program_name, const char *description) { parser->prog_name = program_name; parser->version = "0.1.0"; parser->description = description; parser->epilog = nullptr; parser->lang = SCC_ARGPARSE_LANG_EN; parser->need_help = true; parser->need_version = true; parser->need_debug = false; parser->need_error_add_help = false; parser->need_error_add_usage = true; scc_argparse_cmd_init(&parser->root_cmd, parser->prog_name, parser->description); } scc_argparse_cmd_t *scc_argparse_get_root(scc_argparse_t *parser) { return &parser->root_cmd; } void scc_argparse_drop(scc_argparse_t *parser) { scc_argparse_cmd_drop(&parser->root_cmd); } static inline scc_argparse_cmd_t *is_subcommand(scc_argparse_cmd_t *cmd, const char *name) { if (!scc_vec_size(cmd->subcmds)) { return nullptr; } scc_vec_foreach(cmd->subcmds, i) { scc_argparse_cmd_t *subcmd = &scc_vec_at(cmd->subcmds, i); if (scc_strcmp(subcmd->name, name) == 0) { return subcmd; } } return nullptr; } static inline void prepare_cmd(scc_optparse_t *optparse, scc_optparse_opt_vec_t *opts, scc_argparse_cmd_t *cmd) { scc_vec_free(*opts); scc_vec_init(*opts); scc_vec_foreach(cmd->opts, i) { scc_argparse_opt_t *opt = &scc_vec_at(cmd->opts, i); int min_args = 0; int max_args = 0; // 根据选项类型推断 min_args 和 max_args switch (opt->spec.value_type) { case SCC_ARGPARSE_VAL_TYPE_STRING: case SCC_ARGPARSE_VAL_TYPE_INT: case SCC_ARGPARSE_VAL_TYPE_FLOAT: min_args = 1; max_args = 1; break; case SCC_ARGPARSE_VAL_TYPE_BOOL: min_args = 0; max_args = 0; break; case SCC_ARGPARSE_VAL_TYPE_COUNT: min_args = 0; max_args = 0; break; case SCC_ARGPARSE_VAL_TYPE_LIST: // 列表类型 min_args = 1; max_args = 65535; // FIXME maybe INT_MAX ? break; case SCC_ARGPARSE_VAL_TYPE_ENUM: min_args = 1; max_args = 1; break; default: min_args = 0; max_args = 0; break; } scc_vec_push(*opts, ((scc_optparse_opt_t){ .prefix = '-', .short_name = opt->short_name, .long_name = opt->long_name, .min_args = min_args, .max_args = max_args, .user_data = opt, })); } scc_vec_push(*opts, (scc_optparse_opt_t)SCC_OPTPARSE_OPT_END()); scc_optparse_set(optparse, opts->data); } static void push_help(scc_argparse_cmd_t *cmd) { if (cmd == nullptr) { return; } scc_vec_push(cmd->opts, ((scc_argparse_opt_t){ .short_name = 'h', .long_name = "help", .description = 0, .spec.value_type = SCC_ARGPARSE_VAL_TYPE_BOOL, })); scc_vec_foreach(cmd->subcmds, i) { push_help(&scc_vec_at(cmd->subcmds, i)); } } static void init_context(scc_argparse_context_t *ctx, scc_argparse_t *parser, int argc, const char **argv) { ctx->current_cmd = scc_argparse_get_root(parser); if (parser->need_help) { push_help(ctx->current_cmd); } scc_optparse_init(&ctx->optparse, argc, argv); scc_vec_init(ctx->opts); prepare_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd); ctx->positional_index = 0; ctx->parsing_done = false; } static int transite_error(scc_argparse_context_t *ctx) { scc_argparse_err_t error = SCC_ARGPARSE_ERR_NONE; switch (ctx->result.error) { case SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG: case SCC_OPT_ERROR_NOT_FOUND_LONG_ARG: error = SCC_ARGPARSE_ERR_UNKNOWN_ARG; break; case SCC_OPT_ERROR_NOT_ENOUGH_ARGS: error = SCC_ARGPARSE_ERR_MISSING_VALUE; break; case SCC_OPT_ERROR_TOO_MANY_ARGS: error = SCC_ARGPARSE_ERR_INVALID_ARG; break; default: error = SCC_ARGPARSE_ERR_UNKNOWN_ERR; break; } return error; } static int check_choices(const scc_argparse_spec_t *spec, const char *value, int *out_index) { if (!spec->choices.values || spec->choices.count == 0) { return SCC_ARGPARSE_ERR_NONE; } if (value == nullptr) { return SCC_ARGPARSE_ERR_MISSING_VALUE; } if (out_index) *out_index = -1; for (int i = 0; i < spec->choices.count; i += 1) { if (scc_strcmp(value, spec->choices.values[i]) == 0) { if (out_index) *out_index = i; return SCC_ARGPARSE_ERR_NONE; } } return SCC_ARGPARSE_ERR_INVALID_VALUE; } static int validate_and_cleanup(scc_argparse_context_t *ctx, scc_argparse_t *parser, int errcode) { if (errcode != SCC_ARGPARSE_ERR_NONE) { goto END; } // 检查必需的位置参数 scc_vec_foreach(ctx->current_cmd->args, i) { scc_argparse_arg_t *arg = &scc_vec_at(ctx->current_cmd->args, i); if (arg->spec.flag_required) { // 检查是否已存储(非空) if (arg->spec.store.str_store == nullptr || *arg->spec.store.str_store == nullptr) { errcode = SCC_ARGPARSE_ERR_MISSING_ARG; ctx->err_ctx = arg->name; goto END; } } } scc_vec_foreach(ctx->current_cmd->opts, i) { scc_argparse_opt_t *opt = &scc_vec_at(ctx->current_cmd->opts, i); if (opt->spec.flag_required) { int provided = 0; if (opt->spec.flag_store_as_count) { provided = (opt->spec.store.int_store && *opt->spec.store.int_store > 0); } else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_BOOL) { provided = (opt->spec.store.bool_store && *opt->spec.store.bool_store); } else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_LIST) { provided = (opt->spec.store.vec_store && scc_vec_size(*opt->spec.store.vec_store) > 0); } else { provided = (opt->spec.store.str_store && *opt->spec.store.str_store != nullptr); } if (!provided) { errcode = SCC_ARGPARSE_ERR_MISSING_ARG; goto END; } } } END: if (errcode != SCC_ARGPARSE_ERR_NONE) { scc_argparse_print_error(ctx, parser, errcode); } scc_vec_free(ctx->opts); scc_optparse_drop(&ctx->optparse); return errcode; } static int handle_option(scc_argparse_context_t *ctx, scc_argparse_t *parser) { scc_argparse_opt_t *opt = (scc_argparse_opt_t *)ctx->result.opt->user_data; if (parser->need_help && scc_strcmp(opt->long_name, "help") == 0) { scc_argparse_print_help(parser, ctx->current_cmd); ctx->parsing_done = true; return SCC_ARGPARSE_ERR_PNT_DEFAULT; } if (parser->need_version && scc_strcmp(opt->long_name, "version") == 0) { // TODO default version print } if (opt->spec.flag_store_as_count) { (*opt->spec.store.int_store)++; } if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_BOOL) { *opt->spec.store.bool_store = true; } else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_ENUM) { return check_choices(&opt->spec, ctx->result.value, opt->spec.store.int_store); } if (!ctx->result.value) { return SCC_ARGPARSE_ERR_NONE; } opt->spec.raw_value = ctx->result.value; if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_LIST) { scc_vec_push(*opt->spec.store.vec_store, ctx->result.value); } else { *opt->spec.store.str_store = ctx->result.value; } return SCC_ARGPARSE_ERR_NONE; } static int handle_positional_arg(scc_argparse_context_t *ctx, scc_argparse_t *parser) { (void)parser; // TODO scc_argparse_cmd_t *subcmd = is_subcommand(ctx->current_cmd, ctx->result.value); if (subcmd != nullptr) { ctx->current_cmd = subcmd; prepare_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd); return SCC_ARGPARSE_ERR_NONE; } if (ctx->positional_index < (int)scc_vec_size(ctx->current_cmd->args)) { scc_argparse_arg_t *arg = &scc_vec_at(ctx->current_cmd->args, ctx->positional_index); *arg->spec.store.str_store = ctx->result.value; arg->spec.raw_value = ctx->result.value; ctx->positional_index++; } else { ctx->parsing_done = true; return SCC_ARGPARSE_ERR_UNKNOWN_ARG; } return SCC_ARGPARSE_ERR_NONE; } int scc_argparse_parse(scc_argparse_t *parser, int argc, const char **argv) { scc_argparse_context_t ctx = {0}; init_context(&ctx, parser, argc, argv); // 初始化上下文 int errcode = SCC_ARGPARSE_ERR_NONE; while (!ctx.parsing_done && errcode == SCC_ARGPARSE_ERR_NONE && scc_optparse_parse(&ctx.optparse, &ctx.result)) { if (parser->need_debug) { scc_printf("debug:[%c:%s:%d] %s\n", ctx.result.opt ? ctx.result.opt->short_name : '-', ctx.result.opt ? ctx.result.opt->long_name : "--", ctx.result.error, ctx.result.value ? ctx.result.value : ""); } if (ctx.result.error) { errcode = transite_error(&ctx); continue; } if (ctx.result.opt != nullptr) { errcode = handle_option(&ctx, parser); } else if (ctx.result.value != nullptr) { errcode = handle_positional_arg(&ctx, parser); } else { UNREACHABLE(); // 不应到达此处 } } return validate_and_cleanup(&ctx, parser, errcode); } void scc_argparse_cmd_init(scc_argparse_cmd_t *cmd, const char *name, const char *description) { cmd->name = name ? name : "cmd_name"; cmd->description = description ? description : "cmd_description"; scc_vec_init(cmd->args); scc_vec_init(cmd->opts); scc_vec_init(cmd->subcmds); } void scc_argparse_cmd_drop(scc_argparse_cmd_t *cmd) { scc_vec_free(cmd->args); scc_vec_free(cmd->opts); scc_vec_foreach(cmd->subcmds, i) { scc_argparse_cmd_drop(&scc_vec_at(cmd->subcmds, i)); } scc_vec_free(cmd->subcmds); } void scc_argparse_cmd_add_arg(scc_argparse_cmd_t *cmd, const scc_argparse_arg_t *arg) { scc_vec_push(cmd->args, *arg); } void scc_argparse_cmd_add_opt(scc_argparse_cmd_t *cmd, const scc_argparse_opt_t *opt) { scc_vec_push(cmd->opts, *opt); } void scc_argparse_cmd_add_subcmd(scc_argparse_cmd_t *cmd, const scc_argparse_cmd_t *subcmd) { scc_vec_push(cmd->subcmds, *subcmd); }