diff --git a/libs/argparse/include/optparse.h b/libs/argparse/include/optparse.h index 6013c83..16ce8c3 100644 --- a/libs/argparse/include/optparse.h +++ b/libs/argparse/include/optparse.h @@ -2,16 +2,20 @@ #define __SCC_OPTPARSER_H__ typedef struct scc_optparse_opt { - const char prefix; - const char short_name; + char prefix; + char short_name; const char *long_name; - int min_args; - int max_args; - int parsed_args; const char *default_value; void (*invoke)(void *value); + 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_END() {0} + typedef enum scc_optparse_error { SCC_OPT_ERROR_NONE, SCC_OPT_ERROR_NOT_FOUND_LONG_ARG, @@ -21,7 +25,7 @@ typedef enum scc_optparse_error { } scc_optparse_error_t; typedef struct scc_optparse_result { - scc_optparse_opt_t *opt; + const scc_optparse_opt_t *opt; const char *value; int error; } scc_optparse_result_t; @@ -29,17 +33,21 @@ typedef struct scc_optparse_result { typedef struct { int argc; const char **argv; - scc_optparse_opt_t *opts; - scc_optparse_opt_t *prev; + const scc_optparse_opt_t *opts; int handle_positional; - int current_arg_pos; - int short_opt_pos; + 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_t; void scc_optparse_init(scc_optparse_t *parser, int argc, const char **argv); void scc_optparse_drop(scc_optparse_t *parser); -void scc_optparse_set(scc_optparse_t *parser, scc_optparse_opt_t *opts); +void scc_optparse_set(scc_optparse_t *parser, const scc_optparse_opt_t *opts); void scc_optparse_reset(scc_optparse_t *parser); -void scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res); +int scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res); #endif /* __SCC_OPTPARSER_H__ */ diff --git a/libs/argparse/src/optparse.c b/libs/argparse/src/optparse.c index cf9bacf..b0d236f 100644 --- a/libs/argparse/src/optparse.c +++ b/libs/argparse/src/optparse.c @@ -3,11 +3,23 @@ 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; + scc_optparse_reset(parser); +} + +void scc_optparse_drop(scc_optparse_t *parser) { (void)parser; } + +void scc_optparse_set(scc_optparse_t *parser, const scc_optparse_opt_t *opts) { + parser->opts = opts; + scc_optparse_reset(parser); +} + +void scc_optparse_reset(scc_optparse_t *parser) { + parser->handle_positional = 0; + parser->current.opt = 0; + parser->current.count = 0; + parser->current.arg_pos = 1; + parser->current.opt_pos = 0; } static inline const char *str_chr(const char *s, char c) { @@ -16,12 +28,12 @@ static inline const char *str_chr(const char *s, char c) { return *s ? s : 0; } -scc_optparse_opt_t *scc_optparse_get_long_name(scc_optparse_opt_t *opts, - const char *name, - const char end) { +const scc_optparse_opt_t * +scc_optparse_get_long_name(const 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) { + for (const scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) { if (!opt->long_name) { continue; } @@ -37,9 +49,10 @@ scc_optparse_opt_t *scc_optparse_get_long_name(scc_optparse_opt_t *opts, 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) { +const scc_optparse_opt_t * +scc_optparse_get_short_name(const scc_optparse_opt_t *opts, + const char short_name) { + for (const scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) { if (opt->short_name == short_name) { return opt; } @@ -47,11 +60,11 @@ scc_optparse_opt_t *scc_optparse_get_short_name(scc_optparse_opt_t *opts, return 0; } -static inline void scc_optparse_parse_long_name(scc_optparse_opt_t *opts, +static inline void scc_optparse_parse_long_name(const 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 scc_optparse_opt_t *ret = 0; const char *chr = str_chr(arg, '='); if (chr) { // --file=a.txt @@ -77,86 +90,80 @@ static inline void scc_optparse_parse_long_name(scc_optparse_opt_t *opts, } } -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; - } +static inline void scc_optparse_parse_short_name(const scc_optparse_opt_t *opts, + scc_optparse_result_t *res, + const char *arg, + int arg_start) { + // const char *chr = + // str_chr(arg + arg_start, '='); // TODO maybe short can have -I=/usr + const scc_optparse_opt_t *ret = + scc_optparse_get_short_name(opts, arg[arg_start]); + if (ret == 0) { + res->error = SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG; + } else { 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++; + 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; +static inline int scc_optparse_check(scc_optparse_t *parser, + scc_optparse_result_t *res) { + if (!res->opt || res->error) + return 0; + if (res->value) + parser->current.count += 1; + if (parser->current.count < res->opt->max_args && res->opt->max_args > 0) + parser->current.opt = res->opt; + if (parser->current.count < res->opt->min_args && !res->value) + return 1; + return 0; +} + +int scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res) { + const scc_optparse_opt_t *opts = parser->opts; + const char *arg = 0; 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; + if (parser->current.opt_pos != 0) { + arg = parser->argv[parser->current.arg_pos]; + goto CONTINUE_SHORT_OPTION; } - while (parser->current_arg_pos < parser->argc) { - const char *arg = parser->argv[parser->current_arg_pos]; + while (parser->current.arg_pos < parser->argc) { + arg = parser->argv[parser->current.arg_pos]; if (arg[0] == '\0' || parser->handle_positional) { - return scc_optparse_parse_position_arg(parser, arg, res); + scc_optparse_parse_position_arg(parser, arg, res); + goto RETURN; } for (const scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) { if (arg[0] != opt->prefix) { continue; } + + parser->current.opt = 0; + parser->current.count = 0; if (arg[1] == opt->prefix) { // long option like -- if (arg[2] == '\0') { // the `--` res->value = arg; parser->handle_positional = 1; - parser->current_arg_pos++; + 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) { + parser->current.arg_pos += 1; + if (scc_optparse_check(parser, res)) { goto NEXT; } goto RETURN; @@ -165,37 +172,37 @@ void scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res) { if (arg[1] == '\0') { // the `-` res->value = arg; - parser->handle_positional = 1; - parser->current_arg_pos++; + 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; + parser->current.opt_pos = 1; + CONTINUE_SHORT_OPTION: + scc_optparse_parse_short_name(opts, res, arg, + parser->current.opt_pos++); + int have_next = arg[parser->current.opt_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) { + parser->current.opt_pos = + have_next ? parser->current.opt_pos : 0; + parser->current.arg_pos += have_next ? 0 : 1; + if (scc_optparse_check(parser, res)) { + if (have_next) { + res->value = arg + parser->current.opt_pos; + parser->current.opt_pos = 0; + parser->current.arg_pos += 1; + goto RETURN; + } goto NEXT; } goto RETURN; } } // position arg - if (parser->prev && - parser->prev->parsed_args < parser->prev->max_args) { - res->opt = parser->prev; + if (parser->current.opt && + parser->current.count < parser->current.opt->max_args) { + res->opt = parser->current.opt; res->value = arg; - res->opt->parsed_args++; - parser->current_arg_pos++; + parser->current.count++; + parser->current.arg_pos++; goto RETURN; } else { scc_optparse_parse_position_arg(parser, arg, res); @@ -206,8 +213,8 @@ void scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res) { } RETURN: if (res->opt && res->opt->max_args > 0 && - res->opt->parsed_args > res->opt->max_args) { + parser->current.count > res->opt->max_args) { res->error = SCC_OPT_ERROR_TOO_MANY_ARGS; } - return; + return res->opt != 0 || res->value != 0; } diff --git a/libs/argparse/tests/test_optparse.c b/libs/argparse/tests/test_optparse.c index 4321fd6..173a8e6 100644 --- a/libs/argparse/tests/test_optparse.c +++ b/libs/argparse/tests/test_optparse.c @@ -6,39 +6,33 @@ // 测试用例配置 static scc_optparse_opt_t test_opts[] = { - {'-', 'h', "help", 0, 0, 0, NULL, NULL}, - {'-', 'v', "verbose", 0, 0, 0, NULL, NULL}, - {'-', 'f', "file", 1, 1, 0, NULL, NULL}, - {'-', 'o', "output", 1, 1, 0, NULL, NULL}, - {'-', 'l', "list", 0, 10, 0, NULL, NULL}, // 0到10个参数 - {'-', 'c', "count", 1, 3, 0, NULL, NULL}, // 1到3个参数 - {'-', 0, NULL, 0, 0, 0, NULL, NULL} // 终止标记 + 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_END(), }; -// 辅助函数:重置解析器 -static void reset_parser(scc_optparse_t *parser, int argc, const char **argv) { - scc_optparse_init(parser, argc, argv); - parser->opts = test_opts; - for (size_t i = 0; i < sizeof(test_opts) / sizeof(scc_optparse_opt_t); - i++) { - test_opts[i].parsed_args = 0; - } -} +#define INIT_OPTPARSE(argv) \ + scc_optparse_init(&parser, sizeof(argv) / sizeof(argv[0]), argv); \ + scc_optparse_set(&parser, test_opts); // 测试1: 基础初始化 void test_init(void) { scc_optparse_t parser; const char *argv[] = {"program", "-h"}; - - scc_optparse_init(&parser, 2, argv); - + INIT_OPTPARSE(argv); + scc_optparse_set(&parser, 0); TEST_CHECK(parser.argc == 2); TEST_CHECK(parser.argv == argv); TEST_CHECK(parser.handle_positional == 0); - TEST_CHECK(parser.current_arg_pos == 1); - TEST_CHECK(parser.short_opt_pos == 0); + TEST_CHECK(parser.current.arg_pos == 1); + TEST_CHECK(parser.current.opt_pos == 0); TEST_CHECK(parser.opts == 0); - TEST_CHECK(parser.prev == 0); + TEST_CHECK(parser.current.opt == 0); } // 测试2: 简单短选项 @@ -46,25 +40,47 @@ void test_simple_short_options(void) { scc_optparse_t parser; scc_optparse_result_t res; const char *argv[] = {"program", "-h", "-v"}; - - reset_parser(&parser, 3, argv); + INIT_OPTPARSE(argv); // 解析第一个选项 -h - scc_optparse_parse(&parser, &res); + TEST_CHECK(scc_optparse_parse(&parser, &res) != 0); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.opt != NULL); - TEST_CHECK(res.opt->short_name == 'h'); + TEST_CHECK(res.opt && res.opt->short_name == 'h'); TEST_CHECK(res.value == NULL); // 解析第二个选项 -v - scc_optparse_parse(&parser, &res); + TEST_CHECK(scc_optparse_parse(&parser, &res) != 0); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.opt != NULL); - TEST_CHECK(res.opt->short_name == 'v'); + TEST_CHECK(res.opt && res.opt->short_name == 'v'); TEST_CHECK(res.value == NULL); // 没有更多选项 - scc_optparse_parse(&parser, &res); + TEST_CHECK(scc_optparse_parse(&parser, &res) == 0); + TEST_CHECK(res.opt == NULL); + TEST_CHECK(res.value == NULL); +} + +void test_simple_position_arg(void) { + scc_optparse_t parser; + scc_optparse_result_t res; + const char *argv[] = {"program", "a", "b"}; + INIT_OPTPARSE(argv); + // 解析第一个选项 + TEST_CHECK(scc_optparse_parse(&parser, &res) != 0); + TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); + TEST_CHECK(res.opt == NULL); + TEST_CHECK(strcmp(res.value, "a") == 0); + + // 解析第二个选项 + TEST_CHECK(scc_optparse_parse(&parser, &res) != 0); + TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); + TEST_CHECK(res.opt == NULL); + TEST_CHECK(strcmp(res.value, "b") == 0); + + // 没有更多选项 + TEST_CHECK(scc_optparse_parse(&parser, &res) == 0); TEST_CHECK(res.opt == NULL); TEST_CHECK(res.value == NULL); } @@ -74,21 +90,20 @@ void test_short_options_with_args(void) { scc_optparse_t parser; scc_optparse_result_t res; const char *argv[] = {"program", "-f", "file.txt", "-o", "output.txt"}; - - reset_parser(&parser, 5, argv); + INIT_OPTPARSE(argv); // 解析 -f file.txt scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.opt != NULL); - TEST_CHECK(res.opt->short_name == 'f'); + TEST_CHECK(res.opt && res.opt->short_name == 'f'); TEST_CHECK(strcmp(res.value, "file.txt") == 0); // 解析 -o output.txt scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.opt != NULL); - TEST_CHECK(res.opt->short_name == 'o'); + TEST_CHECK(res.opt && res.opt->short_name == 'o'); TEST_CHECK(strcmp(res.value, "output.txt") == 0); } @@ -99,26 +114,26 @@ void test_long_options(void) { const char *argv[] = {"program", "--help", "--file=test.txt", "--output", "out.txt"}; - reset_parser(&parser, 5, argv); + INIT_OPTPARSE(argv); // 解析 --help scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.opt != NULL); - TEST_CHECK(strcmp(res.opt->long_name, "help") == 0); + TEST_CHECK(res.opt && strcmp(res.opt->long_name, "help") == 0); // 解析 --file=test.txt scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.opt != NULL); - TEST_CHECK(strcmp(res.opt->long_name, "file") == 0); + TEST_CHECK(res.opt && strcmp(res.opt->long_name, "file") == 0); TEST_CHECK(strcmp(res.value, "test.txt") == 0); // 解析 --output out.txt scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.opt != NULL); - TEST_CHECK(strcmp(res.opt->long_name, "output") == 0); + TEST_CHECK(res.opt && strcmp(res.opt->long_name, "output") == 0); TEST_CHECK(strcmp(res.value, "out.txt") == 0); } @@ -129,17 +144,17 @@ void test_mixed_options_positional(void) { const char *argv[] = {"program", "-h", "-f", "input.txt", "positional1", "positional2"}; - reset_parser(&parser, 6, argv); + INIT_OPTPARSE(argv); // 解析 -h scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'h'); + TEST_CHECK(res.opt && res.opt->short_name == 'h'); // 解析 -f input.txt scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'f'); + TEST_CHECK(res.opt && res.opt->short_name == 'f'); TEST_CHECK(strcmp(res.value, "input.txt") == 0); // 解析第一个位置参数 @@ -161,17 +176,17 @@ void test_combined_short_options(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-vh"}; - reset_parser(&parser, 2, argv); + INIT_OPTPARSE(argv); // 解析 -v scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'v'); + TEST_CHECK(res.opt && res.opt->short_name == 'v'); // 解析 -h scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'h'); + TEST_CHECK(res.opt && res.opt->short_name == 'h'); } // 测试7: 错误处理 - 未知选项 @@ -180,7 +195,7 @@ void test_unknown_options(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-x", "--unknown"}; - reset_parser(&parser, 3, argv); + INIT_OPTPARSE(argv); // 解析 -x (未知短选项) scc_optparse_parse(&parser, &res); @@ -188,7 +203,7 @@ void test_unknown_options(void) { // 重置解析器测试长选项 const char *argv2[] = {"program", "--unknown"}; - reset_parser(&parser, 2, argv2); + INIT_OPTPARSE(argv2); scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_FOUND_LONG_ARG); @@ -200,18 +215,18 @@ void test_short_option_with_attached_value(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-finput.txt", "-ooutput.txt"}; - reset_parser(&parser, 3, argv); + INIT_OPTPARSE(argv); // 解析 -finput.txt scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'f'); + TEST_CHECK(res.opt && res.opt->short_name == 'f'); TEST_CHECK(strcmp(res.value, "input.txt") == 0); // 解析 -ooutput.txt scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'o'); + TEST_CHECK(res.opt && res.opt->short_name == 'o'); TEST_CHECK(strcmp(res.value, "output.txt") == 0); } @@ -221,12 +236,12 @@ void test_option_terminator(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-h", "--", "-f", "file.txt"}; - reset_parser(&parser, 5, argv); + INIT_OPTPARSE(argv); // 解析 -h scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'h'); + TEST_CHECK(res.opt && res.opt->short_name == 'h'); // 解析 -- (应该触发位置参数模式) scc_optparse_parse(&parser, &res); @@ -252,7 +267,7 @@ void test_edge_cases(void) { // 测试空参数数组 const char *argv1[] = {"program"}; - reset_parser(&parser, 1, argv1); + INIT_OPTPARSE(argv1); scc_optparse_parse(&parser, &res); TEST_CHECK(res.opt == NULL); @@ -260,18 +275,18 @@ void test_edge_cases(void) { // 测试空字符串参数 const char *argv2[] = {"program", "-f", "", "-o", " "}; - reset_parser(&parser, 5, argv2); + INIT_OPTPARSE(argv2); // 解析 -f "" scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'f'); + TEST_CHECK(res.opt && res.opt->short_name == 'f'); TEST_CHECK(strcmp(res.value, "") == 0); // 解析 -o " " scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'o'); + TEST_CHECK(res.opt && res.opt->short_name == 'o'); TEST_CHECK(strcmp(res.value, " ") == 0); } @@ -281,12 +296,12 @@ void test_multi_argument_option(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-l", "item1", "item2", "item3"}; - reset_parser(&parser, 5, argv); + INIT_OPTPARSE(argv); // 解析 -l scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'l'); + TEST_CHECK(res.opt && res.opt->short_name == 'l'); TEST_CHECK(res.value == NULL); // 由于 -l 可以接受多个参数,后续的参数应该作为 -l 的值 @@ -313,7 +328,7 @@ void test_insufficient_arguments(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-f"}; - reset_parser(&parser, 2, argv); + INIT_OPTPARSE(argv); scc_optparse_parse(&parser, &res); // 注意:当前实现可能需要额外检查 @@ -325,13 +340,12 @@ void test_long_option_with_equal_no_value(void) { scc_optparse_t parser; scc_optparse_result_t res; const char *argv[] = {"program", "--file="}; - - reset_parser(&parser, 2, argv); + INIT_OPTPARSE(argv); scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_ENOUGH_ARGS); TEST_CHECK(res.opt != NULL); - TEST_CHECK(strcmp(res.opt->long_name, "file") == 0); + TEST_CHECK(res.opt && strcmp(res.opt->long_name, "file") == 0); } // 测试14: 重置和重用解析器 @@ -341,20 +355,20 @@ void test_parser_reuse(void) { // 第一次使用 const char *argv1[] = {"program", "-h", "-v"}; - reset_parser(&parser, 3, argv1); + INIT_OPTPARSE(argv1); scc_optparse_parse(&parser, &res); - TEST_CHECK(res.opt->short_name == 'h'); + TEST_CHECK(res.opt && res.opt->short_name == 'h'); scc_optparse_parse(&parser, &res); - TEST_CHECK(res.opt->short_name == 'v'); + TEST_CHECK(res.opt && res.opt->short_name == 'v'); // 重置并重用 const char *argv2[] = {"program", "-f", "test.txt"}; - reset_parser(&parser, 3, argv2); + INIT_OPTPARSE(argv2); scc_optparse_parse(&parser, &res); - TEST_CHECK(res.opt->short_name == 'f'); + TEST_CHECK(res.opt && res.opt->short_name == 'f'); TEST_CHECK(strcmp(res.value, "test.txt") == 0); } // 测试15: 短选项连写中带参数的情况 @@ -363,20 +377,20 @@ void test_combined_short_with_arg(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-vfinput.txt"}; - reset_parser(&parser, 2, argv); + INIT_OPTPARSE(argv); // 解析 -v scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.opt != NULL); - TEST_CHECK(res.opt->short_name == 'v'); + TEST_CHECK(res.opt && res.opt->short_name == 'v'); TEST_CHECK(res.value == NULL); // 解析 -finput.txt scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.opt != NULL); - TEST_CHECK(res.opt->short_name == 'f'); + TEST_CHECK(res.opt && res.opt->short_name == 'f'); TEST_CHECK(strcmp(res.value, "input.txt") == 0); } @@ -386,13 +400,13 @@ void test_too_many_arguments(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-f", "file1", "file2"}; - reset_parser(&parser, 4, argv); + INIT_OPTPARSE(argv); // 解析 -f file1 scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.opt != NULL); - TEST_CHECK(res.opt->short_name == 'f'); + TEST_CHECK(res.opt && res.opt->short_name == 'f'); TEST_CHECK(strcmp(res.value, "file1") == 0); // 尝试给 -f 第二个参数,应该返回错误 @@ -408,17 +422,17 @@ void test_mixed_short_and_positional(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-v", "-f", "input.txt", "positional"}; - reset_parser(&parser, 5, argv); + INIT_OPTPARSE(argv); // 解析 -v scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'v'); + TEST_CHECK(res.opt && res.opt->short_name == 'v'); // 解析 -f input.txt scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'f'); + TEST_CHECK(res.opt && res.opt->short_name == 'f'); TEST_CHECK(strcmp(res.value, "input.txt") == 0); // 解析位置参数 @@ -434,24 +448,24 @@ void test_complex_multi_argument(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-c", "1", "2", "3", "extra"}; - reset_parser(&parser, 6, argv); + INIT_OPTPARSE(argv); // 解析 -c 1 scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'c'); + TEST_CHECK(res.opt && res.opt->short_name == 'c'); TEST_CHECK(strcmp(res.value, "1") == 0); // 解析 -c 2 scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'c'); + TEST_CHECK(res.opt && res.opt->short_name == 'c'); TEST_CHECK(strcmp(res.value, "2") == 0); // 解析 -c 3 scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'c'); + TEST_CHECK(res.opt && res.opt->short_name == 'c'); TEST_CHECK(strcmp(res.value, "3") == 0); // 第4个参数应该是位置参数 @@ -467,30 +481,30 @@ void test_long_option_multi_args(void) { scc_optparse_result_t res; const char *argv[] = {"program", "--list", "item1", "item2", "item3"}; - reset_parser(&parser, 5, argv); + INIT_OPTPARSE(argv); // 解析 --list scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(strcmp(res.opt->long_name, "list") == 0); + TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0); TEST_CHECK(res.value == NULL); // 解析 item1 scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(strcmp(res.opt->long_name, "list") == 0); + TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0); TEST_CHECK(strcmp(res.value, "item1") == 0); // 解析 item2 scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(strcmp(res.opt->long_name, "list") == 0); + TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0); TEST_CHECK(strcmp(res.value, "item2") == 0); // 解析 item3 scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(strcmp(res.opt->long_name, "list") == 0); + TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0); TEST_CHECK(strcmp(res.value, "item3") == 0); } @@ -500,7 +514,7 @@ void test_empty_long_name(void) { scc_optparse_result_t res; const char *argv[] = {"program", "--"}; - reset_parser(&parser, 2, argv); + INIT_OPTPARSE(argv); scc_optparse_parse(&parser, &res); TEST_CHECK(parser.handle_positional == 1); @@ -514,18 +528,19 @@ void test_non_dash_prefix(void) { scc_optparse_result_t res; static scc_optparse_opt_t custom_opts[] = { - {'/', 'h', "help", 0, 0, 0, NULL, NULL}, - {'/', 0, NULL, 0, 0, 0, NULL, NULL}}; + SCC_OPTPARSE_OPT('/', 'h', "help", 0, 0, NULL, NULL), + SCC_OPTPARSE_OPT_END(), + }; const char *argv[] = {"program", "/h"}; - scc_optparse_init(&parser, 2, argv); + INIT_OPTPARSE(argv); parser.opts = custom_opts; scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.opt != NULL); - TEST_CHECK(res.opt->short_name == 'h'); - TEST_CHECK(res.opt->prefix == '/'); + TEST_CHECK(res.opt && res.opt->short_name == 'h'); + TEST_CHECK(res.opt && res.opt->prefix == '/'); } // 测试22: 默认值功能测试 @@ -534,12 +549,13 @@ void test_default_value(void) { scc_optparse_result_t res; static scc_optparse_opt_t default_opts[] = { - {'-', 'o', "output", 0, 1, 0, "default.txt", NULL}, - {'-', 0, NULL, 0, 0, 0, NULL, NULL}}; + SCC_OPTPARSE_OPT('-', 'o', "output", 0, 1, "default.txt", NULL), + SCC_OPTPARSE_OPT_END(), + }; // 测试1: 不提供选项,应该有默认值 const char *argv1[] = {"program"}; - scc_optparse_init(&parser, 1, argv1); + INIT_OPTPARSE(argv1); parser.opts = default_opts; scc_optparse_parse(&parser, &res); @@ -548,36 +564,40 @@ void test_default_value(void) { // 测试2: 提供选项但没有值,应该使用默认值 const char *argv2[] = {"program", "-o"}; - scc_optparse_init(&parser, 2, argv2); + INIT_OPTPARSE(argv2); parser.opts = default_opts; scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'o'); + TEST_CHECK(res.opt && res.opt->short_name == 'o'); // 这里可以检查默认值,但当前实现不支持 } // 测试23: 回调函数测试 static int callback_called = 0; -static void test_callback(void *value) { callback_called = 1; } +static void test_callback(void *value) { + (void)value; + callback_called = 1; +} void test_callback_function(void) { scc_optparse_t parser; scc_optparse_result_t res; static scc_optparse_opt_t callback_opts[] = { - {'-', 'h', "help", 0, 0, 0, NULL, test_callback}, - {'-', 0, NULL, 0, 0, 0, NULL, NULL}}; + SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0, NULL, test_callback), + SCC_OPTPARSE_OPT_END(), + }; const char *argv[] = {"program", "--help"}; - scc_optparse_init(&parser, 2, argv); + INIT_OPTPARSE(argv); parser.opts = callback_opts; callback_called = 0; scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(strcmp(res.opt->long_name, "help") == 0); + TEST_CHECK(res.opt && strcmp(res.opt->long_name, "help") == 0); // 调用回调函数 if (res.opt->invoke) { @@ -592,22 +612,23 @@ void test_complex_short_combination(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-vhfinput.txt"}; - reset_parser(&parser, 2, argv); + INIT_OPTPARSE(argv); + scc_optparse_set(&parser, test_opts); // 解析 -v scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'v'); + TEST_CHECK(res.opt && res.opt->short_name == 'v'); // 解析 -h scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'h'); + TEST_CHECK(res.opt && res.opt->short_name == 'h'); // 解析 -finput.txt scc_optparse_parse(&parser, &res); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); - TEST_CHECK(res.opt->short_name == 'f'); + TEST_CHECK(res.opt && res.opt->short_name == 'f'); TEST_CHECK(strcmp(res.value, "input.txt") == 0); } @@ -617,7 +638,8 @@ void test_single_dash(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-", "--", "arg"}; - reset_parser(&parser, 4, argv); + INIT_OPTPARSE(argv); + scc_optparse_set(&parser, test_opts); // 解析单个横杠(通常表示标准输入) scc_optparse_parse(&parser, &res); @@ -639,11 +661,12 @@ void test_parser_state_reset(void) { scc_optparse_result_t res; const char *argv[] = {"program", "-f", "file1", "--", "arg1", "arg2"}; - reset_parser(&parser, 6, argv); + INIT_OPTPARSE(argv); + scc_optparse_set(&parser, test_opts); // 解析 -f file1 scc_optparse_parse(&parser, &res); - TEST_CHECK(res.opt->short_name == 'f'); + TEST_CHECK(res.opt && res.opt->short_name == 'f'); // 解析 -- scc_optparse_parse(&parser, &res); @@ -659,20 +682,21 @@ void test_parser_state_reset(void) { // 重置解析器 const char *argv2[] = {"program", "-v", "-h"}; - reset_parser(&parser, 3, argv2); + INIT_OPTPARSE(argv2); // 应该能正确解析新的参数 scc_optparse_parse(&parser, &res); - TEST_CHECK(res.opt->short_name == 'v'); + TEST_CHECK(res.opt && res.opt->short_name == 'v'); scc_optparse_parse(&parser, &res); - TEST_CHECK(res.opt->short_name == 'h'); + TEST_CHECK(res.opt && res.opt->short_name == 'h'); } // 更新测试列表 TEST_LIST = { {"test_init", test_init}, {"test_simple_short_options", test_simple_short_options}, + {"test_simple_position_arg", test_simple_position_arg}, {"test_short_options_with_args", test_short_options_with_args}, {"test_long_options", test_long_options}, {"test_mixed_options_positional", test_mixed_options_positional},