feat(argparse): 实现高级命令行参数解析库

- 添加完整的参数解析API,支持子命令、选项和参数定义
- 实现多种数据类型支持:字符串、布尔值、整数、浮点数、枚举等
- 添加约束规范结构体,支持必填项、多值、隐藏选项等功能
- 实现国际化支持,包含中英文错误提示和帮助信息
- 添加模糊匹配功能,当用户输入错误参数时提供相似建议
- 实现详细的帮助信息打印功能,包括使用方法、选项说明等
- 修改底层optparse库,优化选项处理和错误报告机制
- 添加向量类型支持用于管理参数、选项和子命令集合
This commit is contained in:
zzy
2026-02-12 21:41:57 +08:00
parent 34d7eb3c42
commit 191cdcef53
6 changed files with 777 additions and 67 deletions

View File

@@ -2,30 +2,219 @@
#define __SCC_ARGPARSE_H__
#include "optparse.h"
#include <scc_core.h>
typedef enum {
SCC_ARGPARSE_ERROR_NONE,
} scc_argparse_error_t;
// TODO: 实现更多高级功能
// 1. 支持子命令嵌套层级限制
// 2. 支持选项分组显示Grouping
// 3. 支持自动补全脚本生成Bash/Zsh/Fish
// 4. 支持配置文件解析
// 5. 支持国际化i18n
typedef struct {
} scc_argparse_cmd_t;
typedef struct scc_argparse_spec scc_argparse_spec_t;
typedef struct scc_argparse_arg scc_argparse_arg_t;
typedef struct scc_argparse_opt scc_argparse_opt_t;
typedef struct scc_argparse_cmd scc_argparse_cmd_t;
typedef struct {
} scc_argparse_arg_t;
typedef SCC_VEC(scc_argparse_arg_t) scc_argparse_arg_vec_t;
typedef SCC_VEC(scc_argparse_opt_t) scc_argparse_opt_vec_t;
typedef SCC_VEC(scc_argparse_cmd_t) scc_argparse_cmd_vec_t;
typedef struct {
typedef enum scc_argparse_val_type {
SCC_ARGPARSE_VAL_TYPE_UNKNOWN,
SCC_ARGPARSE_VAL_TYPE_STRING,
SCC_ARGPARSE_VAL_TYPE_BOOL,
SCC_ARGPARSE_VAL_TYPE_INT,
SCC_ARGPARSE_VAL_TYPE_FLOAT,
SCC_ARGPARSE_VAL_TYPE_ENUM,
SCC_ARGPARSE_VAL_TYPE_LIST,
SCC_ARGPARSE_VAL_TYPE_COUNT,
} scc_argparse_val_type_t;
typedef enum scc_argparse_err {
SCC_ARGPARSE_ERR_NONE,
SCC_ARGPARSE_ERR_UNKNOWN_ERR,
SCC_ARGPARSE_ERR_INVALID_ARG,
SCC_ARGPARSE_ERR_INVALID_VALUE,
SCC_ARGPARSE_ERR_MISSING_ARG,
SCC_ARGPARSE_ERR_MISSING_VALUE,
SCC_ARGPARSE_ERR_UNKNOWN_ARG,
SCC_ARGPARSE_ERR_UNKNOWN_VALUE,
} scc_argparse_err_t;
// 约束规范结构体
struct scc_argparse_spec {
scc_argparse_val_type_t value_type; // 值类型
const char *raw_value;
// 存储位置
union {
cbool *bool_store; // 布尔值存储
int *int_store; // 整数存储
float *float_store; // 浮点数存储
const char **str_store; // 字符串存储
char **str_alloc_store; // 字符串存储使用alloc需要free
void **ptr_store; // 通用指针存储
} store;
// 枚举值约束
struct {
const char **values; // 枚举值数组
int count; // 枚举值数量
} choices;
// 其他约束标志
cbool flag_required; // 是否必须
cbool flag_store_as_count; // 是否存储为计数(如 -vvv 返回3
cbool flag_allow_empty; // 是否允许空字符串
cbool flag_hidden; // 是否隐藏(不显示在帮助)
cbool flag_takes_multiple; // 是否接受多个值(如 -I dir1 -I dir2
cbool flag_parse_complex; // 是否进行复杂解析(如 1K, 2M, 3G
};
struct scc_argparse_arg {
scc_argparse_spec_t spec;
const char *name;
const char *description;
};
struct scc_argparse_opt {
scc_argparse_spec_t spec;
char short_name;
const char *long_name;
const char *description;
};
struct scc_argparse_cmd {
const char *name;
const char *description;
scc_argparse_arg_vec_t args;
scc_argparse_opt_vec_t opts;
scc_argparse_cmd_vec_t subcmds;
};
typedef enum {
SCC_ARGPARSE_LANG_EN,
SCC_ARGPARSE_LANG_ZH,
} scc_argparse_lang_t;
typedef struct {
const char *prog_name;
const char *version;
const char *description;
const char *epilog;
const char *usage;
const char *help;
scc_optparse_t optparse;
int usage_margin;
int usage_max_width;
int help_vertical_space;
int help_usage_margin;
int help_description_margin;
int help_max_width;
scc_argparse_cmd_t root_cmd;
scc_argparse_lang_t lang;
cbool need_help;
cbool need_version;
cbool need_debug;
} scc_argparse_t;
typedef SCC_VEC(scc_optparse_opt_t) scc_optparse_opt_vec_t;
typedef struct {
scc_argparse_cmd_t *current_cmd; // 当前正在解析的命令
scc_optparse_t optparse; // 底层解析器
scc_optparse_opt_vec_t opts; // 当前命令的选项列表
scc_optparse_result_t result;
int positional_index; // 当前处理的位置参数索引
cbool parsing_done; // 是否已完成解析
scc_argparse_lang_t lang;
} scc_argparse_context_t;
void scc_argparse_init(scc_argparse_t *parser, const char *program_name,
const char *description);
scc_argparse_cmd_t *scc_argparse_get_root(scc_argparse_t *parser);
void scc_argparse_drop(scc_argparse_t *parser);
int scc_argparse_parse(scc_argparse_t *parser, int argc, const char **argv);
void scc_argparse_cmd_init(scc_argparse_cmd_t *cmd, const char *name,
const char *description);
void scc_argparse_cmd_drop(scc_argparse_cmd_t *cmd);
void scc_argparse_cmd_add_arg(scc_argparse_cmd_t *cmd,
const scc_argparse_arg_t *arg);
void scc_argparse_cmd_add_opt(scc_argparse_cmd_t *cmd,
const scc_argparse_opt_t *opt);
void scc_argparse_cmd_add_subcmd(scc_argparse_cmd_t *cmd,
const scc_argparse_cmd_t *subcmd);
void scc_argparse_print_help(scc_argparse_t *parser, scc_argparse_cmd_t *cmd);
void scc_argparse_print_error(scc_argparse_context_t *ctx,
scc_argparse_err_t err);
static inline void scc_argparse_spec_init(scc_argparse_spec_t *spec) {
spec->value_type = SCC_ARGPARSE_VAL_TYPE_STRING;
spec->raw_value = null;
spec->store.ptr_store = null;
spec->choices.count = 0;
spec->choices.values = null;
spec->flag_required = false;
spec->flag_store_as_count = false;
spec->flag_allow_empty = false;
spec->flag_hidden = false;
spec->flag_takes_multiple = false;
spec->flag_parse_complex = false;
}
static inline void scc_argparse_opt_init(scc_argparse_opt_t *opt,
char short_name, const char *long_name,
const char *desc) {
opt->short_name = short_name;
opt->long_name = long_name;
opt->description = desc;
scc_argparse_spec_init(&opt->spec);
}
static inline void scc_argparse_arg_init(scc_argparse_arg_t *arg,
const char *name, const char *desc) {
arg->name = name;
arg->description = desc;
scc_argparse_spec_init(&arg->spec);
}
static inline void scc_argparse_spec_setup_bool(scc_argparse_spec_t *spec,
cbool *store) {
spec->value_type = SCC_ARGPARSE_VAL_TYPE_BOOL;
spec->store.bool_store = store;
}
static inline void scc_argparse_spec_setup_count(scc_argparse_spec_t *spec,
int *store) {
spec->value_type = SCC_ARGPARSE_VAL_TYPE_COUNT;
spec->flag_store_as_count = true; // 自动设为计数模式
spec->store.int_store = store;
}
static inline void scc_argparse_spec_setup_string(scc_argparse_spec_t *spec,
const char **store) {
spec->value_type = SCC_ARGPARSE_VAL_TYPE_STRING;
spec->store.str_store = store;
}
static inline void scc_argparse_spec_setup_int(scc_argparse_spec_t *spec,
int *store) {
spec->value_type = SCC_ARGPARSE_VAL_TYPE_INT;
spec->store.int_store = store;
}
static inline void scc_argparse_spec_setup_float(scc_argparse_spec_t *spec,
float *store) {
spec->value_type = SCC_ARGPARSE_VAL_TYPE_FLOAT;
spec->store.float_store = store;
}
#define SCC_ARGPARSE_MACRO_SETTER(attr) \
static inline void scc_argparse_spec_set_##attr(scc_argparse_spec_t *spec, \
cbool flag) { \
spec->flag_##attr = flag; \
}
SCC_ARGPARSE_MACRO_SETTER(required)
SCC_ARGPARSE_MACRO_SETTER(allow_empty)
SCC_ARGPARSE_MACRO_SETTER(hidden)
SCC_ARGPARSE_MACRO_SETTER(takes_multiple)
SCC_ARGPARSE_MACRO_SETTER(parse_complex)
#endif /* __SCC_ARGPARSE_H__ */

View File

@@ -5,15 +5,14 @@ typedef struct scc_optparse_opt {
char prefix;
char short_name;
const char *long_name;
const char *default_value;
void (*invoke)(void *value);
void (*handle)(void *user_data);
void *user_data;
int min_args;
int max_args;
} scc_optparse_opt_t;
#define SCC_OPTPARSE_OPT(prefix, short_name, long_name, min_args, max_args, \
default_value, invoke) \
{prefix, short_name, long_name, default_value, invoke, min_args, max_args}
#define SCC_OPTPARSE_OPT(prefix, short_name, long_name, min_args, max_args) \
{prefix, short_name, long_name, 0, 0, min_args, max_args}
#define SCC_OPTPARSE_OPT_END() {0}
typedef enum scc_optparse_error {
@@ -24,10 +23,18 @@ typedef enum scc_optparse_error {
SCC_OPT_ERROR_TOO_MANY_ARGS,
} scc_optparse_error_t;
typedef struct {
const scc_optparse_opt_t *opt;
int count; // check for min_args <= count <= max_args
int arg_pos; // for argv pos
int opt_pos; // for short pos
} scc_optparse_state_t;
typedef struct scc_optparse_result {
const scc_optparse_opt_t *opt;
const char *value;
int error;
const char *raw_arg;
} scc_optparse_result_t;
typedef struct {
@@ -36,12 +43,7 @@ typedef struct {
const scc_optparse_opt_t *opts;
int handle_positional;
int greedy_mode;
struct {
const scc_optparse_opt_t *opt;
int count; // check for min_args <= count <= max_args
int arg_pos; // for argv pos
int opt_pos; // for short pos
} current;
scc_optparse_state_t current;
} scc_optparse_t;
void scc_optparse_init(scc_optparse_t *parser, int argc, const char **argv);

View File

@@ -1 +1,297 @@
#include <argparse.h>
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 = null;
parser->lang = SCC_ARGPARSE_LANG_EN;
parser->need_help = true;
parser->need_version = true;
parser->need_debug = false;
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 null;
}
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 null;
}
static inline void parse_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;
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 == null) {
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);
parse_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
ctx->positional_index = 0;
ctx->parsing_done = false;
ctx->lang = parser->lang;
}
static int handle_parse_error(scc_argparse_t *parser,
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;
}
scc_argparse_print_error(ctx, error);
return error;
}
static void validate_and_cleanup(scc_argparse_context_t *ctx,
scc_argparse_t *parser) {
// 检查必需参数是否都已提供
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 && *arg->spec.store.str_store == NULL) {
scc_argparse_print_error(ctx, SCC_ARGPARSE_ERR_MISSING_ARG);
break;
}
}
// 清理资源
scc_vec_free(ctx->opts);
scc_optparse_drop(&ctx->optparse);
}
static void 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;
}
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;
}
if (ctx->result.value) {
opt->spec.raw_value = ctx->result.value;
*opt->spec.store.str_store = ctx->result.value;
}
// // opt value == null or value != null
// scc_argparse_opt_t *org_opt =
// (scc_argparse_opt_t *)opt_res.opt->user_data;
// if (parser->need_help) {
// if (scc_strcmp(org_opt->long_name, "help") == 0) {
// scc_argparse_print_help(parser, current_cmd);
// break;
// }
// }
// if (org_opt->spec.store_as_count) {
// *org_opt->spec.store.int_store += 1;
// }
// if (org_opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_BOOL) {
// *org_opt->spec.store.bool_store = true;
// }
// if (opt_res.value) {
// org_opt->spec.raw_value = opt_res.value;
// // TODO
// *org_opt->spec.store.str_store = opt_res.value;
// }
}
static void handle_positional_arg(scc_argparse_context_t *ctx,
scc_argparse_t *parser) {
scc_argparse_cmd_t *subcmd =
is_subcommand(ctx->current_cmd, ctx->result.value);
if (subcmd != NULL) {
ctx->current_cmd = subcmd;
parse_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
return;
}
if (ctx->positional_index < 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 {
scc_argparse_print_error(ctx, SCC_ARGPARSE_ERR_UNKNOWN_ARG);
ctx->parsing_done = true;
}
// // position arg
// scc_argparse_cmd_t *cmd = is_subcommand(current_cmd,
// opt_res.value);
// if (cmd != null) {
// current_cmd = cmd;
// parse_cmd(&optparse, &opts, current_cmd);
// } else {
// // TODO argument
// scc_vec_foreach(current_cmd->args, i) {
// scc_argparse_arg_t *arg = &scc_vec_at(current_cmd->args,
// i); if (*arg->spec.store.str_store == 0) {
// *arg->spec.store.str_store = opt_res.value;
// arg->spec.raw_value = opt_res.value;
// break;
// }
// }
// }
}
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); // 初始化上下文
while (!ctx.parsing_done &&
scc_optparse_parse(&ctx.optparse, &ctx.result)) {
if (parser->need_debug) {
scc_printf("[%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);
}
if (ctx.result.error) {
handle_parse_error(parser, &ctx);
return 1;
}
if (ctx.result.opt != null) {
handle_option(&ctx, parser);
} else if (ctx.result.value != null) {
handle_positional_arg(&ctx, parser);
} else {
UNREACHABLE(); // 不应到达此处
}
}
validate_and_cleanup(&ctx, parser);
return 0;
}
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);
}

View File

@@ -0,0 +1,221 @@
#include <argparse.h>
// 帮助信息处理函数
enum {
ARGPARSE_USAGE,
ARGPARSE_OPTION,
ARGPARSE_COMMAND,
ARGPARSE_ARGUMENT,
ARGPARSE_SHOW_ARG,
ARGPARSE_SHOW_OPT,
ARGPARSE_SHOW_CMD,
ARGPARSE_SHOW_HELP_MSG,
ARGPARSE_UNKNOWN_ARGUMENT,
ARGPARSE_INVALID_VALUE_FOR_OPTION,
ARGPARSE_OPTION_MISSING_VALUE,
ARGPARSE_DID_YOU_MEAN,
};
static const char *fmt_en[] = {
[ARGPARSE_USAGE] = "Usage: %s",
[ARGPARSE_OPTION] = " [Options]",
[ARGPARSE_COMMAND] = " [Commands]",
[ARGPARSE_SHOW_ARG] = "Arguments:\n",
[ARGPARSE_SHOW_OPT] = "Options:\n",
[ARGPARSE_SHOW_CMD] = "Commands:\n",
[ARGPARSE_SHOW_HELP_MSG] = "Show this help message and exit",
[ARGPARSE_UNKNOWN_ARGUMENT] = "Unknown argument '%s'",
[ARGPARSE_INVALID_VALUE_FOR_OPTION] = "Invalid value for option '%s'\n",
[ARGPARSE_OPTION_MISSING_VALUE] = "Option '%s' missing value\n",
[ARGPARSE_DID_YOU_MEAN] = "; Did you mean: '%s'?\n",
};
static const char *fmt_zh[] = {
[ARGPARSE_USAGE] = "用法: %s",
[ARGPARSE_OPTION] = " [选项]",
[ARGPARSE_COMMAND] = " [命令]",
[ARGPARSE_SHOW_ARG] = "参数:\n",
[ARGPARSE_SHOW_OPT] = "选项:\n",
[ARGPARSE_SHOW_CMD] = "命令:\n",
[ARGPARSE_SHOW_HELP_MSG] = "显示帮助信息并退出",
[ARGPARSE_UNKNOWN_ARGUMENT] = "未知的参数 '%s'",
[ARGPARSE_INVALID_VALUE_FOR_OPTION] = "无效的选项值 '%s'\n",
[ARGPARSE_OPTION_MISSING_VALUE] = "选项 '%s' 缺少值\n",
[ARGPARSE_DID_YOU_MEAN] = "; 您是不是想要输入: '%s'?\n",
};
/**
* @brief 计算两个字符串的编辑距离Levenshtein距离
* @param s1 字符串1非空以'\0'结尾)
* @param s2 字符串2非空以'\0'结尾)
* @return 编辑距离,内存分配失败返回 -1
*/
static int scc_levenshtein(const char *s1, const char *s2) {
usize len1 = scc_strlen(s1);
usize len2 = scc_strlen(s2);
// 保证 len1 >= len2 以减少内存占用
if (len1 < len2) {
const char *tmp = s1;
s1 = s2;
s2 = tmp;
usize tlen = len1;
len1 = len2;
len2 = tlen;
}
// 分配滚动数组(只需 len2+1 个 int
int *dp = (int *)scc_malloc(sizeof(int) * (len2 + 1));
if (!dp)
return -1;
// 初始化第0行
for (usize j = 0; j <= len2; ++j)
dp[j] = (int)j;
for (usize i = 1; i <= len1; ++i) {
int prev = dp[0];
dp[0] = (int)i;
for (usize j = 1; j <= len2; ++j) {
int temp = dp[j];
int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
dp[j] = scc_min(scc_min(dp[j] + 1, dp[j - 1] + 1), prev + cost);
prev = temp;
}
}
int dist = dp[len2];
scc_free(dp);
return dist;
}
void scc_argparse_print_help(scc_argparse_t *parser, scc_argparse_cmd_t *cmd) {
const char **lines =
(parser->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
scc_printf(lines[ARGPARSE_USAGE],
cmd->name ? cmd->name : parser->prog_name);
if (scc_vec_size(cmd->opts) > 0)
scc_printf(lines[ARGPARSE_OPTION]);
scc_vec_foreach(cmd->args, i) {
scc_argparse_arg_t *arg = &scc_vec_at(cmd->args, i);
scc_printf(arg->spec.flag_required ? " <%s>" : " [%s]", arg->name);
}
if (scc_vec_size(cmd->subcmds) > 0)
scc_printf(lines[ARGPARSE_COMMAND]);
scc_printf("\n\n");
if (cmd->description)
scc_printf("%s\n\n", cmd->description);
if (scc_vec_size(cmd->args) > 0) {
scc_printf(lines[ARGPARSE_SHOW_ARG]);
scc_vec_foreach(cmd->args, i) {
scc_argparse_arg_t *arg = &scc_vec_at(cmd->args, i);
scc_printf(" %-20s %s", arg->name,
arg->description ? arg->description : "");
// if (!arg->spec.required && arg->spec.flags.has_default)
// scc_printf(" (default: %s)", arg->spec.default_value);
scc_printf("\n");
}
scc_printf("\n");
}
if (scc_vec_size(cmd->opts) > 0) {
scc_printf(lines[ARGPARSE_SHOW_OPT]);
scc_vec_foreach(cmd->opts, i) {
scc_argparse_opt_t *opt = &scc_vec_at(cmd->opts, i);
if (opt->spec.flag_hidden)
continue;
if (opt->short_name == 'h')
opt->description = lines[ARGPARSE_SHOW_HELP_MSG];
char buf[64];
int pos = 0;
if (opt->short_name) {
buf[pos++] = '-';
buf[pos++] = opt->short_name;
if (opt->long_name) {
buf[pos++] = ',';
buf[pos++] = ' ';
}
}
if (opt->long_name) {
buf[pos++] = '-';
buf[pos++] = '-';
for (const char *p = opt->long_name; *p; ++p)
buf[pos++] = *p;
}
buf[pos] = '\0';
scc_printf(" %-25s %s", buf,
opt->description ? opt->description : "");
// if (opt->spec.default_value)
// scc_printf(" (default: %s)", opt->spec.default_value);
scc_printf("\n");
}
scc_printf("\n");
}
if (scc_vec_size(cmd->subcmds) > 0) {
scc_printf(lines[ARGPARSE_SHOW_CMD]);
scc_vec_foreach(cmd->subcmds, i) {
scc_argparse_cmd_t *sub = &scc_vec_at(cmd->subcmds, i);
scc_printf(" %-20s %s\n", sub->name,
sub->description ? sub->description : "");
}
scc_printf("\n");
}
if (parser->epilog)
scc_printf("%s\n", parser->epilog);
}
const char *scc_argparse_find_similar_arg(scc_argparse_cmd_t *cmd,
const char *arg) {
if (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0') {
// opt arg
scc_vec_foreach(cmd->opts, i) {
if (scc_levenshtein(arg + 2, scc_vec_at(cmd->opts, i).long_name) <=
2) {
return scc_vec_at(cmd->opts, i).long_name;
}
}
} else {
// subcmd arg
scc_vec_foreach(cmd->subcmds, i) {
if (scc_levenshtein(arg, scc_vec_at(cmd->subcmds, i).name) <= 2) {
return scc_vec_at(cmd->subcmds, i).name;
}
}
}
return 0;
}
void scc_argparse_print_error(scc_argparse_context_t *ctx,
scc_argparse_err_t err) {
const char **lines = (ctx->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
const char *optname = ctx->result.raw_arg;
switch (err) {
case SCC_ARGPARSE_ERR_INVALID_ARG:
case SCC_ARGPARSE_ERR_INVALID_VALUE:
scc_printf(lines[ARGPARSE_INVALID_VALUE_FOR_OPTION], optname);
break;
case SCC_ARGPARSE_ERR_MISSING_ARG:
case SCC_ARGPARSE_ERR_MISSING_VALUE:
scc_printf(lines[ARGPARSE_OPTION_MISSING_VALUE], optname);
break;
case SCC_ARGPARSE_ERR_UNKNOWN_ARG:
case SCC_ARGPARSE_ERR_UNKNOWN_VALUE:
scc_printf(lines[ARGPARSE_UNKNOWN_ARGUMENT], optname);
const char *similar_arg =
scc_argparse_find_similar_arg(ctx->current_cmd, optname);
if (similar_arg != 0) {
scc_printf(lines[ARGPARSE_DID_YOU_MEAN], similar_arg);
}
break;
default:
scc_printf("Unknown error: %d\n", err);
}
}

View File

@@ -145,7 +145,7 @@ int scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res) {
goto RETURN;
}
for (const scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) {
for (const scc_optparse_opt_t *opt = opts; opt && opt->prefix; ++opt) {
if (arg[0] != opt->prefix) {
continue;
}
@@ -216,5 +216,6 @@ RETURN:
parser->current.count > res->opt->max_args) {
res->error = SCC_OPT_ERROR_TOO_MANY_ARGS;
}
return res->opt != 0 || res->value != 0;
// res->raw_arg = arg;
return res->opt != 0 || res->value != 0 || res->error != SCC_OPT_ERROR_NONE;
}

View File

@@ -6,13 +6,13 @@
// 测试用例配置
static scc_optparse_opt_t test_opts[] = {
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0, NULL, NULL),
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0, NULL, NULL),
SCC_OPTPARSE_OPT('-', 'v', "verbose", 0, 0, NULL, NULL),
SCC_OPTPARSE_OPT('-', 'f', "file", 1, 1, NULL, NULL),
SCC_OPTPARSE_OPT('-', 'o', "output", 1, 1, NULL, NULL),
SCC_OPTPARSE_OPT('-', 'l', "list", 0, 10, NULL, NULL),
SCC_OPTPARSE_OPT('-', 'c', "count", 1, 3, NULL, NULL),
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0),
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0),
SCC_OPTPARSE_OPT('-', 'v', "verbose", 0, 0),
SCC_OPTPARSE_OPT('-', 'f', "file", 1, 1),
SCC_OPTPARSE_OPT('-', 'o', "output", 1, 1),
SCC_OPTPARSE_OPT('-', 'l', "list", 0, 10),
SCC_OPTPARSE_OPT('-', 'c', "count", 1, 3),
SCC_OPTPARSE_OPT_END(),
};
@@ -528,7 +528,7 @@ void test_non_dash_prefix(void) {
scc_optparse_result_t res;
static scc_optparse_opt_t custom_opts[] = {
SCC_OPTPARSE_OPT('/', 'h', "help", 0, 0, NULL, NULL),
SCC_OPTPARSE_OPT('/', 'h', "help", 0, 0),
SCC_OPTPARSE_OPT_END(),
};
@@ -544,33 +544,34 @@ void test_non_dash_prefix(void) {
}
// 测试22: 默认值功能测试
// TODO
void test_default_value(void) {
scc_optparse_t parser;
scc_optparse_result_t res;
// scc_optparse_t parser;
// scc_optparse_result_t res;
static scc_optparse_opt_t default_opts[] = {
SCC_OPTPARSE_OPT('-', 'o', "output", 0, 1, "default.txt", NULL),
SCC_OPTPARSE_OPT_END(),
};
// static scc_optparse_opt_t default_opts[] = {
// SCC_OPTPARSE_OPT('-', 'o', "output", 0, 1, "default.txt", NULL),
// SCC_OPTPARSE_OPT_END(),
// };
// 测试1: 不提供选项,应该有默认值
const char *argv1[] = {"program"};
INIT_OPTPARSE(argv1);
parser.opts = default_opts;
// // 测试1: 不提供选项,应该有默认值
// const char *argv1[] = {"program"};
// INIT_OPTPARSE(argv1);
// parser.opts = default_opts;
scc_optparse_parse(&parser, &res);
// 注意:这个测试需要修改解析器以支持默认值
// 当前实现不支持,所以只是演示
// scc_optparse_parse(&parser, &res);
// // 注意:这个测试需要修改解析器以支持默认值
// // 当前实现不支持,所以只是演示
// 测试2: 提供选项但没有值,应该使用默认值
const char *argv2[] = {"program", "-o"};
INIT_OPTPARSE(argv2);
parser.opts = default_opts;
// // 测试2: 提供选项但没有值,应该使用默认值
// const char *argv2[] = {"program", "-o"};
// INIT_OPTPARSE(argv2);
// parser.opts = default_opts;
scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt && res.opt->short_name == 'o');
// 这里可以检查默认值,但当前实现不支持
// scc_optparse_parse(&parser, &res);
// TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
// TEST_CHECK(res.opt && res.opt->short_name == 'o');
// // 这里可以检查默认值,但当前实现不支持
}
// 测试23: 回调函数测试
@@ -579,13 +580,13 @@ static void test_callback(void *value) {
(void)value;
callback_called = 1;
}
// TODO
void test_callback_function(void) {
scc_optparse_t parser;
scc_optparse_result_t res;
static scc_optparse_opt_t callback_opts[] = {
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0, NULL, test_callback),
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0),
SCC_OPTPARSE_OPT_END(),
};
@@ -599,11 +600,11 @@ void test_callback_function(void) {
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "help") == 0);
// 调用回调函数
if (res.opt->invoke) {
res.opt->invoke(NULL);
TEST_CHECK(callback_called == 1);
}
// // 调用回调函数
// if (res.opt->invoke) {
// res.opt->invoke(NULL);
// TEST_CHECK(callback_called == 1);
// }
}
// 测试24: 复杂的短选项组合