feat(argparse): 重构命令行参数解析器以支持更灵活的参数处理

将optparse库的API进行了重大改进,包括:
- 修改结构体字段为const类型以提高安全性
- 添加了宏定义SCC_OPTPARSE_OPT和SCC_OPTPARSE_OPT_END用于简化选项定义
- 重构了解析逻辑,引入greedy_mode和current状态跟踪
- 改进了短选项和长选项的解析机制
- 添加了参数计数验证功能(min_args/max_args检查)
- 调整了函数返回值,parse函数现在返回int类型表示是否还有更多参数
- 完善了错误处理和边界条件检查
This commit is contained in:
zzy
2026-02-06 17:33:43 +08:00
parent d1b215861c
commit 34d7eb3c42
3 changed files with 242 additions and 203 deletions

View File

@@ -2,16 +2,20 @@
#define __SCC_OPTPARSER_H__ #define __SCC_OPTPARSER_H__
typedef struct scc_optparse_opt { typedef struct scc_optparse_opt {
const char prefix; char prefix;
const char short_name; char short_name;
const char *long_name; const char *long_name;
int min_args;
int max_args;
int parsed_args;
const char *default_value; const char *default_value;
void (*invoke)(void *value); void (*invoke)(void *value);
int min_args;
int max_args;
} scc_optparse_opt_t; } 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 { typedef enum scc_optparse_error {
SCC_OPT_ERROR_NONE, SCC_OPT_ERROR_NONE,
SCC_OPT_ERROR_NOT_FOUND_LONG_ARG, SCC_OPT_ERROR_NOT_FOUND_LONG_ARG,
@@ -21,7 +25,7 @@ typedef enum scc_optparse_error {
} scc_optparse_error_t; } scc_optparse_error_t;
typedef struct scc_optparse_result { typedef struct scc_optparse_result {
scc_optparse_opt_t *opt; const scc_optparse_opt_t *opt;
const char *value; const char *value;
int error; int error;
} scc_optparse_result_t; } scc_optparse_result_t;
@@ -29,17 +33,21 @@ typedef struct scc_optparse_result {
typedef struct { typedef struct {
int argc; int argc;
const char **argv; const char **argv;
scc_optparse_opt_t *opts; const scc_optparse_opt_t *opts;
scc_optparse_opt_t *prev;
int handle_positional; int handle_positional;
int current_arg_pos; int greedy_mode;
int short_opt_pos; 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; } scc_optparse_t;
void scc_optparse_init(scc_optparse_t *parser, int argc, const char **argv); void scc_optparse_init(scc_optparse_t *parser, int argc, const char **argv);
void scc_optparse_drop(scc_optparse_t *parser); 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_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__ */ #endif /* __SCC_OPTPARSER_H__ */

View File

@@ -3,11 +3,23 @@
void scc_optparse_init(scc_optparse_t *parser, int argc, const char **argv) { void scc_optparse_init(scc_optparse_t *parser, int argc, const char **argv) {
parser->argc = argc; parser->argc = argc;
parser->argv = argv; parser->argv = argv;
parser->handle_positional = 0;
parser->opts = 0; parser->opts = 0;
parser->prev = 0; scc_optparse_reset(parser);
parser->current_arg_pos = 1; }
parser->short_opt_pos = 0;
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) { 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; return *s ? s : 0;
} }
scc_optparse_opt_t *scc_optparse_get_long_name(scc_optparse_opt_t *opts, const scc_optparse_opt_t *
const char *name, scc_optparse_get_long_name(const scc_optparse_opt_t *opts, const char *name,
const char end) { const char end) {
if (*name == '\0') if (*name == '\0')
return 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) { if (!opt->long_name) {
continue; continue;
} }
@@ -37,9 +49,10 @@ scc_optparse_opt_t *scc_optparse_get_long_name(scc_optparse_opt_t *opts,
return 0; return 0;
} }
scc_optparse_opt_t *scc_optparse_get_short_name(scc_optparse_opt_t *opts, const scc_optparse_opt_t *
const char short_name) { scc_optparse_get_short_name(const scc_optparse_opt_t *opts,
for (scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) { const char short_name) {
for (const scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) {
if (opt->short_name == short_name) { if (opt->short_name == short_name) {
return opt; return opt;
} }
@@ -47,11 +60,11 @@ scc_optparse_opt_t *scc_optparse_get_short_name(scc_optparse_opt_t *opts,
return 0; 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, scc_optparse_result_t *res,
const char *arg) { const char *arg) {
// caller MUST use `--` start 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, '='); const char *chr = str_chr(arg, '=');
if (chr) { if (chr) {
// --file=a.txt // --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, static inline void scc_optparse_parse_short_name(const scc_optparse_opt_t *opts,
scc_optparse_result_t *res, scc_optparse_result_t *res,
const char *arg, const char *arg,
int arg_start) { int arg_start) {
for (int i = arg_start; arg[i]; ++i) { // const char *chr =
scc_optparse_opt_t *ret = scc_optparse_get_short_name(opts, arg[i]); // str_chr(arg + arg_start, '='); // TODO maybe short can have -I=/usr
if (ret == 0) { const scc_optparse_opt_t *ret =
res->error = SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG; scc_optparse_get_short_name(opts, arg[arg_start]);
return 0; if (ret == 0) {
} res->error = SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG;
} else {
res->opt = ret; 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, static inline void scc_optparse_parse_position_arg(scc_optparse_t *parser,
const char *arg, const char *arg,
scc_optparse_result_t *res) { scc_optparse_result_t *res) {
parser->current_arg_pos++; parser->current.arg_pos++;
res->value = arg; res->value = arg;
} }
void scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res) { static inline int scc_optparse_check(scc_optparse_t *parser,
scc_optparse_opt_t *opts = parser->opts; 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->opt = 0;
res->value = 0; res->value = 0;
res->error = SCC_OPT_ERROR_NONE; res->error = SCC_OPT_ERROR_NONE;
if (parser->short_opt_pos != 0) { if (parser->current.opt_pos != 0) {
parser->short_opt_pos = scc_optparse_parse_short_name( arg = parser->argv[parser->current.arg_pos];
opts, res, parser->argv[parser->current_arg_pos], goto CONTINUE_SHORT_OPTION;
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) { while (parser->current.arg_pos < parser->argc) {
const char *arg = parser->argv[parser->current_arg_pos]; arg = parser->argv[parser->current.arg_pos];
if (arg[0] == '\0' || parser->handle_positional) { 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) { for (const scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) {
if (arg[0] != opt->prefix) { if (arg[0] != opt->prefix) {
continue; continue;
} }
parser->current.opt = 0;
parser->current.count = 0;
if (arg[1] == opt->prefix) { if (arg[1] == opt->prefix) {
// long option like -- // long option like --
if (arg[2] == '\0') { if (arg[2] == '\0') {
// the `--` // the `--`
res->value = arg; res->value = arg;
parser->handle_positional = 1; parser->handle_positional = 1;
parser->current_arg_pos++; parser->current.arg_pos++;
goto RETURN; goto RETURN;
} }
scc_optparse_parse_long_name(opts, res, arg); scc_optparse_parse_long_name(opts, res, arg);
if (!res->opt || res->error) { parser->current.arg_pos += 1;
goto RETURN; if (scc_optparse_check(parser, res)) {
}
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 NEXT;
} }
goto RETURN; goto RETURN;
@@ -165,37 +172,37 @@ void scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res) {
if (arg[1] == '\0') { if (arg[1] == '\0') {
// the `-` // the `-`
res->value = arg; res->value = arg;
parser->handle_positional = 1; parser->current.arg_pos++;
parser->current_arg_pos++;
goto RETURN; goto RETURN;
} }
parser->short_opt_pos = parser->current.opt_pos = 1;
scc_optparse_parse_short_name(opts, res, arg, 1); CONTINUE_SHORT_OPTION:
if (!res->opt || res->error) { scc_optparse_parse_short_name(opts, res, arg,
goto RETURN; parser->current.opt_pos++);
} int have_next = arg[parser->current.opt_pos];
if (parser->short_opt_pos == 0) {
parser->current_arg_pos++;
}
parser->prev = 0;
if (res->opt->parsed_args < res->opt->max_args && parser->current.opt_pos =
res->opt->max_args > 0) { have_next ? parser->current.opt_pos : 0;
parser->prev = res->opt; parser->current.arg_pos += have_next ? 0 : 1;
} if (scc_optparse_check(parser, res)) {
if (res->opt->parsed_args < res->opt->min_args) { 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 NEXT;
} }
goto RETURN; goto RETURN;
} }
} }
// position arg // position arg
if (parser->prev && if (parser->current.opt &&
parser->prev->parsed_args < parser->prev->max_args) { parser->current.count < parser->current.opt->max_args) {
res->opt = parser->prev; res->opt = parser->current.opt;
res->value = arg; res->value = arg;
res->opt->parsed_args++; parser->current.count++;
parser->current_arg_pos++; parser->current.arg_pos++;
goto RETURN; goto RETURN;
} else { } else {
scc_optparse_parse_position_arg(parser, arg, res); 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: RETURN:
if (res->opt && res->opt->max_args > 0 && 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; res->error = SCC_OPT_ERROR_TOO_MANY_ARGS;
} }
return; return res->opt != 0 || res->value != 0;
} }

View File

@@ -6,39 +6,33 @@
// 测试用例配置 // 测试用例配置
static scc_optparse_opt_t test_opts[] = { static scc_optparse_opt_t test_opts[] = {
{'-', 'h', "help", 0, 0, 0, NULL, NULL}, SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0, NULL, NULL),
{'-', 'v', "verbose", 0, 0, 0, NULL, NULL}, SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0, NULL, NULL),
{'-', 'f', "file", 1, 1, 0, NULL, NULL}, SCC_OPTPARSE_OPT('-', 'v', "verbose", 0, 0, NULL, NULL),
{'-', 'o', "output", 1, 1, 0, NULL, NULL}, SCC_OPTPARSE_OPT('-', 'f', "file", 1, 1, NULL, NULL),
{'-', 'l', "list", 0, 10, 0, NULL, NULL}, // 0到10个参数 SCC_OPTPARSE_OPT('-', 'o', "output", 1, 1, NULL, NULL),
{'-', 'c', "count", 1, 3, 0, NULL, NULL}, // 1到3个参数 SCC_OPTPARSE_OPT('-', 'l', "list", 0, 10, NULL, NULL),
{'-', 0, NULL, 0, 0, 0, NULL, NULL} // 终止标记 SCC_OPTPARSE_OPT('-', 'c', "count", 1, 3, NULL, NULL),
SCC_OPTPARSE_OPT_END(),
}; };
// 辅助函数:重置解析器 #define INIT_OPTPARSE(argv) \
static void reset_parser(scc_optparse_t *parser, int argc, const char **argv) { scc_optparse_init(&parser, sizeof(argv) / sizeof(argv[0]), argv); \
scc_optparse_init(parser, argc, argv); scc_optparse_set(&parser, test_opts);
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;
}
}
// 测试1: 基础初始化 // 测试1: 基础初始化
void test_init(void) { void test_init(void) {
scc_optparse_t parser; scc_optparse_t parser;
const char *argv[] = {"program", "-h"}; const char *argv[] = {"program", "-h"};
INIT_OPTPARSE(argv);
scc_optparse_init(&parser, 2, argv); scc_optparse_set(&parser, 0);
TEST_CHECK(parser.argc == 2); TEST_CHECK(parser.argc == 2);
TEST_CHECK(parser.argv == argv); TEST_CHECK(parser.argv == argv);
TEST_CHECK(parser.handle_positional == 0); TEST_CHECK(parser.handle_positional == 0);
TEST_CHECK(parser.current_arg_pos == 1); TEST_CHECK(parser.current.arg_pos == 1);
TEST_CHECK(parser.short_opt_pos == 0); TEST_CHECK(parser.current.opt_pos == 0);
TEST_CHECK(parser.opts == 0); TEST_CHECK(parser.opts == 0);
TEST_CHECK(parser.prev == 0); TEST_CHECK(parser.current.opt == 0);
} }
// 测试2: 简单短选项 // 测试2: 简单短选项
@@ -46,25 +40,47 @@ void test_simple_short_options(void) {
scc_optparse_t parser; scc_optparse_t parser;
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-h", "-v"}; const char *argv[] = {"program", "-h", "-v"};
INIT_OPTPARSE(argv);
reset_parser(&parser, 3, argv);
// 解析第一个选项 -h // 解析第一个选项 -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.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt != NULL); 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); TEST_CHECK(res.value == NULL);
// 解析第二个选项 -v // 解析第二个选项 -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.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt != NULL); 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); 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.opt == NULL);
TEST_CHECK(res.value == NULL); TEST_CHECK(res.value == NULL);
} }
@@ -74,21 +90,20 @@ void test_short_options_with_args(void) {
scc_optparse_t parser; scc_optparse_t parser;
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-f", "file.txt", "-o", "output.txt"}; const char *argv[] = {"program", "-f", "file.txt", "-o", "output.txt"};
INIT_OPTPARSE(argv);
reset_parser(&parser, 5, argv);
// 解析 -f file.txt // 解析 -f file.txt
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt != NULL); 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); TEST_CHECK(strcmp(res.value, "file.txt") == 0);
// 解析 -o output.txt // 解析 -o output.txt
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt != NULL); 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); 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", const char *argv[] = {"program", "--help", "--file=test.txt", "--output",
"out.txt"}; "out.txt"};
reset_parser(&parser, 5, argv); INIT_OPTPARSE(argv);
// 解析 --help // 解析 --help
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt != NULL); 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 // 解析 --file=test.txt
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt != NULL); 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); TEST_CHECK(strcmp(res.value, "test.txt") == 0);
// 解析 --output out.txt // 解析 --output out.txt
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt != NULL); 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); TEST_CHECK(strcmp(res.value, "out.txt") == 0);
} }
@@ -129,17 +144,17 @@ void test_mixed_options_positional(void) {
const char *argv[] = {"program", "-h", "-f", const char *argv[] = {"program", "-h", "-f",
"input.txt", "positional1", "positional2"}; "input.txt", "positional1", "positional2"};
reset_parser(&parser, 6, argv); INIT_OPTPARSE(argv);
// 解析 -h // 解析 -h
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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 // 解析 -f input.txt
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "input.txt") == 0);
// 解析第一个位置参数 // 解析第一个位置参数
@@ -161,17 +176,17 @@ void test_combined_short_options(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-vh"}; const char *argv[] = {"program", "-vh"};
reset_parser(&parser, 2, argv); INIT_OPTPARSE(argv);
// 解析 -v // 解析 -v
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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 // 解析 -h
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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: 错误处理 - 未知选项 // 测试7: 错误处理 - 未知选项
@@ -180,7 +195,7 @@ void test_unknown_options(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-x", "--unknown"}; const char *argv[] = {"program", "-x", "--unknown"};
reset_parser(&parser, 3, argv); INIT_OPTPARSE(argv);
// 解析 -x (未知短选项) // 解析 -x (未知短选项)
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
@@ -188,7 +203,7 @@ void test_unknown_options(void) {
// 重置解析器测试长选项 // 重置解析器测试长选项
const char *argv2[] = {"program", "--unknown"}; const char *argv2[] = {"program", "--unknown"};
reset_parser(&parser, 2, argv2); INIT_OPTPARSE(argv2);
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_FOUND_LONG_ARG); 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; scc_optparse_result_t res;
const char *argv[] = {"program", "-finput.txt", "-ooutput.txt"}; const char *argv[] = {"program", "-finput.txt", "-ooutput.txt"};
reset_parser(&parser, 3, argv); INIT_OPTPARSE(argv);
// 解析 -finput.txt // 解析 -finput.txt
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "input.txt") == 0);
// 解析 -ooutput.txt // 解析 -ooutput.txt
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "output.txt") == 0);
} }
@@ -221,12 +236,12 @@ void test_option_terminator(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-h", "--", "-f", "file.txt"}; const char *argv[] = {"program", "-h", "--", "-f", "file.txt"};
reset_parser(&parser, 5, argv); INIT_OPTPARSE(argv);
// 解析 -h // 解析 -h
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); scc_optparse_parse(&parser, &res);
@@ -252,7 +267,7 @@ void test_edge_cases(void) {
// 测试空参数数组 // 测试空参数数组
const char *argv1[] = {"program"}; const char *argv1[] = {"program"};
reset_parser(&parser, 1, argv1); INIT_OPTPARSE(argv1);
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.opt == NULL); TEST_CHECK(res.opt == NULL);
@@ -260,18 +275,18 @@ void test_edge_cases(void) {
// 测试空字符串参数 // 测试空字符串参数
const char *argv2[] = {"program", "-f", "", "-o", " "}; const char *argv2[] = {"program", "-f", "", "-o", " "};
reset_parser(&parser, 5, argv2); INIT_OPTPARSE(argv2);
// 解析 -f "" // 解析 -f ""
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "") == 0);
// 解析 -o " " // 解析 -o " "
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, " ") == 0);
} }
@@ -281,12 +296,12 @@ void test_multi_argument_option(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-l", "item1", "item2", "item3"}; const char *argv[] = {"program", "-l", "item1", "item2", "item3"};
reset_parser(&parser, 5, argv); INIT_OPTPARSE(argv);
// 解析 -l // 解析 -l
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(res.value == NULL);
// 由于 -l 可以接受多个参数,后续的参数应该作为 -l 的值 // 由于 -l 可以接受多个参数,后续的参数应该作为 -l 的值
@@ -313,7 +328,7 @@ void test_insufficient_arguments(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-f"}; const char *argv[] = {"program", "-f"};
reset_parser(&parser, 2, argv); INIT_OPTPARSE(argv);
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
// 注意:当前实现可能需要额外检查 // 注意:当前实现可能需要额外检查
@@ -325,13 +340,12 @@ void test_long_option_with_equal_no_value(void) {
scc_optparse_t parser; scc_optparse_t parser;
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "--file="}; const char *argv[] = {"program", "--file="};
INIT_OPTPARSE(argv);
reset_parser(&parser, 2, argv);
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_ENOUGH_ARGS); TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_ENOUGH_ARGS);
TEST_CHECK(res.opt != NULL); 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: 重置和重用解析器 // 测试14: 重置和重用解析器
@@ -341,20 +355,20 @@ void test_parser_reuse(void) {
// 第一次使用 // 第一次使用
const char *argv1[] = {"program", "-h", "-v"}; const char *argv1[] = {"program", "-h", "-v"};
reset_parser(&parser, 3, argv1); INIT_OPTPARSE(argv1);
scc_optparse_parse(&parser, &res); 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); 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"}; const char *argv2[] = {"program", "-f", "test.txt"};
reset_parser(&parser, 3, argv2); INIT_OPTPARSE(argv2);
scc_optparse_parse(&parser, &res); 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); TEST_CHECK(strcmp(res.value, "test.txt") == 0);
} }
// 测试15: 短选项连写中带参数的情况 // 测试15: 短选项连写中带参数的情况
@@ -363,20 +377,20 @@ void test_combined_short_with_arg(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-vfinput.txt"}; const char *argv[] = {"program", "-vfinput.txt"};
reset_parser(&parser, 2, argv); INIT_OPTPARSE(argv);
// 解析 -v // 解析 -v
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt != NULL); 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); TEST_CHECK(res.value == NULL);
// 解析 -finput.txt // 解析 -finput.txt
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt != NULL); 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); TEST_CHECK(strcmp(res.value, "input.txt") == 0);
} }
@@ -386,13 +400,13 @@ void test_too_many_arguments(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-f", "file1", "file2"}; const char *argv[] = {"program", "-f", "file1", "file2"};
reset_parser(&parser, 4, argv); INIT_OPTPARSE(argv);
// 解析 -f file1 // 解析 -f file1
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt != NULL); 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); TEST_CHECK(strcmp(res.value, "file1") == 0);
// 尝试给 -f 第二个参数,应该返回错误 // 尝试给 -f 第二个参数,应该返回错误
@@ -408,17 +422,17 @@ void test_mixed_short_and_positional(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-v", "-f", "input.txt", "positional"}; const char *argv[] = {"program", "-v", "-f", "input.txt", "positional"};
reset_parser(&parser, 5, argv); INIT_OPTPARSE(argv);
// 解析 -v // 解析 -v
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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 // 解析 -f input.txt
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "input.txt") == 0);
// 解析位置参数 // 解析位置参数
@@ -434,24 +448,24 @@ void test_complex_multi_argument(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-c", "1", "2", "3", "extra"}; const char *argv[] = {"program", "-c", "1", "2", "3", "extra"};
reset_parser(&parser, 6, argv); INIT_OPTPARSE(argv);
// 解析 -c 1 // 解析 -c 1
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "1") == 0);
// 解析 -c 2 // 解析 -c 2
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "2") == 0);
// 解析 -c 3 // 解析 -c 3
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "3") == 0);
// 第4个参数应该是位置参数 // 第4个参数应该是位置参数
@@ -467,30 +481,30 @@ void test_long_option_multi_args(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "--list", "item1", "item2", "item3"}; const char *argv[] = {"program", "--list", "item1", "item2", "item3"};
reset_parser(&parser, 5, argv); INIT_OPTPARSE(argv);
// 解析 --list // 解析 --list
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(res.value == NULL);
// 解析 item1 // 解析 item1
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "item1") == 0);
// 解析 item2 // 解析 item2
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "item2") == 0);
// 解析 item3 // 解析 item3
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "item3") == 0);
} }
@@ -500,7 +514,7 @@ void test_empty_long_name(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "--"}; const char *argv[] = {"program", "--"};
reset_parser(&parser, 2, argv); INIT_OPTPARSE(argv);
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(parser.handle_positional == 1); TEST_CHECK(parser.handle_positional == 1);
@@ -514,18 +528,19 @@ void test_non_dash_prefix(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
static scc_optparse_opt_t custom_opts[] = { static scc_optparse_opt_t custom_opts[] = {
{'/', 'h', "help", 0, 0, 0, NULL, NULL}, SCC_OPTPARSE_OPT('/', 'h', "help", 0, 0, NULL, NULL),
{'/', 0, NULL, 0, 0, 0, NULL, NULL}}; SCC_OPTPARSE_OPT_END(),
};
const char *argv[] = {"program", "/h"}; const char *argv[] = {"program", "/h"};
scc_optparse_init(&parser, 2, argv); INIT_OPTPARSE(argv);
parser.opts = custom_opts; parser.opts = custom_opts;
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
TEST_CHECK(res.opt != NULL); TEST_CHECK(res.opt != NULL);
TEST_CHECK(res.opt->short_name == 'h'); TEST_CHECK(res.opt && res.opt->short_name == 'h');
TEST_CHECK(res.opt->prefix == '/'); TEST_CHECK(res.opt && res.opt->prefix == '/');
} }
// 测试22: 默认值功能测试 // 测试22: 默认值功能测试
@@ -534,12 +549,13 @@ void test_default_value(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
static scc_optparse_opt_t default_opts[] = { static scc_optparse_opt_t default_opts[] = {
{'-', 'o', "output", 0, 1, 0, "default.txt", NULL}, SCC_OPTPARSE_OPT('-', 'o', "output", 0, 1, "default.txt", NULL),
{'-', 0, NULL, 0, 0, 0, NULL, NULL}}; SCC_OPTPARSE_OPT_END(),
};
// 测试1: 不提供选项,应该有默认值 // 测试1: 不提供选项,应该有默认值
const char *argv1[] = {"program"}; const char *argv1[] = {"program"};
scc_optparse_init(&parser, 1, argv1); INIT_OPTPARSE(argv1);
parser.opts = default_opts; parser.opts = default_opts;
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
@@ -548,36 +564,40 @@ void test_default_value(void) {
// 测试2: 提供选项但没有值,应该使用默认值 // 测试2: 提供选项但没有值,应该使用默认值
const char *argv2[] = {"program", "-o"}; const char *argv2[] = {"program", "-o"};
scc_optparse_init(&parser, 2, argv2); INIT_OPTPARSE(argv2);
parser.opts = default_opts; parser.opts = default_opts;
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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: 回调函数测试 // 测试23: 回调函数测试
static int callback_called = 0; 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) { void test_callback_function(void) {
scc_optparse_t parser; scc_optparse_t parser;
scc_optparse_result_t res; scc_optparse_result_t res;
static scc_optparse_opt_t callback_opts[] = { static scc_optparse_opt_t callback_opts[] = {
{'-', 'h', "help", 0, 0, 0, NULL, test_callback}, SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0, NULL, test_callback),
{'-', 0, NULL, 0, 0, 0, NULL, NULL}}; SCC_OPTPARSE_OPT_END(),
};
const char *argv[] = {"program", "--help"}; const char *argv[] = {"program", "--help"};
scc_optparse_init(&parser, 2, argv); INIT_OPTPARSE(argv);
parser.opts = callback_opts; parser.opts = callback_opts;
callback_called = 0; callback_called = 0;
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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) { if (res.opt->invoke) {
@@ -592,22 +612,23 @@ void test_complex_short_combination(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-vhfinput.txt"}; const char *argv[] = {"program", "-vhfinput.txt"};
reset_parser(&parser, 2, argv); INIT_OPTPARSE(argv);
scc_optparse_set(&parser, test_opts);
// 解析 -v // 解析 -v
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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 // 解析 -h
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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 // 解析 -finput.txt
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE); 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); TEST_CHECK(strcmp(res.value, "input.txt") == 0);
} }
@@ -617,7 +638,8 @@ void test_single_dash(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-", "--", "arg"}; const char *argv[] = {"program", "-", "--", "arg"};
reset_parser(&parser, 4, argv); INIT_OPTPARSE(argv);
scc_optparse_set(&parser, test_opts);
// 解析单个横杠(通常表示标准输入) // 解析单个横杠(通常表示标准输入)
scc_optparse_parse(&parser, &res); scc_optparse_parse(&parser, &res);
@@ -639,11 +661,12 @@ void test_parser_state_reset(void) {
scc_optparse_result_t res; scc_optparse_result_t res;
const char *argv[] = {"program", "-f", "file1", "--", "arg1", "arg2"}; const char *argv[] = {"program", "-f", "file1", "--", "arg1", "arg2"};
reset_parser(&parser, 6, argv); INIT_OPTPARSE(argv);
scc_optparse_set(&parser, test_opts);
// 解析 -f file1 // 解析 -f file1
scc_optparse_parse(&parser, &res); 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); scc_optparse_parse(&parser, &res);
@@ -659,20 +682,21 @@ void test_parser_state_reset(void) {
// 重置解析器 // 重置解析器
const char *argv2[] = {"program", "-v", "-h"}; const char *argv2[] = {"program", "-v", "-h"};
reset_parser(&parser, 3, argv2); INIT_OPTPARSE(argv2);
// 应该能正确解析新的参数 // 应该能正确解析新的参数
scc_optparse_parse(&parser, &res); 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); scc_optparse_parse(&parser, &res);
TEST_CHECK(res.opt->short_name == 'h'); TEST_CHECK(res.opt && res.opt->short_name == 'h');
} }
// 更新测试列表 // 更新测试列表
TEST_LIST = { TEST_LIST = {
{"test_init", test_init}, {"test_init", test_init},
{"test_simple_short_options", test_simple_short_options}, {"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_short_options_with_args", test_short_options_with_args},
{"test_long_options", test_long_options}, {"test_long_options", test_long_options},
{"test_mixed_options_positional", test_mixed_options_positional}, {"test_mixed_options_positional", test_mixed_options_positional},