diff --git a/examples/simple.c b/examples/simple.c index 8071733..ad6d7f9 100644 --- a/examples/simple.c +++ b/examples/simple.c @@ -4,7 +4,7 @@ #include ArgParse *Init() { - ArgParse *ap = argParseInit("简单的命令行工具示例"); + ArgParse *ap = argParseInit("简单的命令行工具示例",NOVALUE); // 添加第一个命令 Command *cmd = argParseAddCommand( diff --git a/include/ArgParse.h b/include/ArgParse.h index 48143ca..ba42540 100644 --- a/include/ArgParse.h +++ b/include/ArgParse.h @@ -69,19 +69,25 @@ typedef struct ArgParse { struct CommandArgs **global_args; // 全局参数 int global_args_len; // 全局参数个数 char *documentation; // 帮助文档 + ArgParseValueType value_type; // 值类型 程序默认需要的值例如 gcc main.c + /* 解析所用到的属性*/ struct Command *current_command; // 当前解析到的命令 - int argc; // 参数个数 - char **argv; // 参数列表 + char **val; + int val_len; + int argc; // 参数个数 + char **argv; // 参数列表 } ArgParse; /** Start---------------构造API---------------- */ /** * @brief 初始化解析器 + * @param documentation 帮助文档 + * @param value_type 值类型,程序默认需要的值例如 gcc main.c * @return ArgParse* 解析器指针 */ -ArgParse *argParseInit(char *documentation); +ArgParse *argParseInit(char *documentation, ArgParseValueType value_type); /** * @brief 释放解析器 @@ -268,6 +274,9 @@ bool argParseCheckCommandTriggered(ArgParse *argParse, char *command_name); */ bool argParseCheckGlobalTriggered(ArgParse *argParse, char *opt); +char *argParseGetVal(ArgParse *argParse); +char **argParseGetValList(ArgParse *argParse, int *len); + /** End----------------解析API---------------- */ /** @@ -294,10 +303,10 @@ char *argParseGenerateHelp(ArgParse *argParse); char * argParseGenerateArgErrorMsg(ArgParse *argParse, char *name, bool short_flag); -_Noreturn void argParseError(ArgParse *argParse, - Command *lastCommand, - char *prefix, - char *suffix); +_Noreturn void argParseError(ArgParse *argParse, + Command *lastCommand, + const char *prefix, + const char *suffix); #ifdef __cplusplus } diff --git a/src/ArgParse.c b/src/ArgParse.c index a1a1f5a..1142849 100644 --- a/src/ArgParse.c +++ b/src/ArgParse.c @@ -12,7 +12,7 @@ static bool _COLOR = true; // 是否启用颜色 void argParseDisableAutoHelp() { _AutoHelp = false; } -ArgParse *argParseInit(char *documentation) { +ArgParse *argParseInit(char *documentation, ArgParseValueType value_type) { ArgParse *argParse = malloc(sizeof(ArgParse)); if (argParse == NULL) { return NULL; @@ -26,6 +26,9 @@ ArgParse *argParseInit(char *documentation) { argParse->argc = 0; argParse->argv = NULL; argParse->documentation = stringNewCopy(documentation); + argParse->value_type = value_type; + argParse->val = NULL; + argParse->val_len = 0; argParseAutoHelp(argParse); @@ -259,7 +262,16 @@ void __freeCommand(Command *command) { free(command->sub_commands); free(command->name); free(command->help); + free(command->args); free(command->default_val); + + if (command->val_len > 0) { + for (size_t i = 0; i < command->val_len; i++) { + free(command->val[i]); + } + free(command->val); + } + free(command); } @@ -274,7 +286,15 @@ void argParseFree(ArgParse *argParse) { for (size_t i = 0; i < argParse->global_args_len; i++) { __freeCommandArgs(argParse->global_args[i]); } + if (argParse->val_len > 0) { + for (size_t i = 0; i < argParse->val_len; i++) { + free(argParse->val[i]); + } + free(argParse->val); + } + free(argParse->global_args); + free(argParse->documentation); free(argParse); } /** End----------------内存释放API---------------- */ @@ -369,25 +389,79 @@ int __processSubCommand(ArgParse *argParse, return -1; } +int __processVal(ArgParse *argParse, int index) { + CommandArgs *arg = NULL; + + for (int i = index; i < argParse->argc; i++) { + ArgType argType = checkArgType(argParse->argv[i]); + switch (argType) { + case COMMAND: + argParseSetVal(argParse, argParse->argv[i]); + break; + case LONG_ARG: + // 处理全局命令长选项 + arg = argParseFindGlobalArgs(argParse, argParse->argv[i], false); + if (arg == NULL) { + char *msg = argParseGenerateArgErrorMsg( + argParse, argParse->argv[i], false); + argParseError(argParse, argParse->current_command, msg, NULL); + return -1; + } + i = __processArgs(argParse, arg, i); + break; + case SHORT_ARG: + // 处理全局命令短选项 + arg = argParseFindGlobalArgs(argParse, argParse->argv[i], true); + if (arg == NULL) { + char *msg = argParseGenerateArgErrorMsg( + argParse, argParse->argv[i], true); + argParseError(argParse, argParse->current_command, msg, NULL); + return -1; // 错误处理 + } + i = __processArgs( + argParse, arg, i); // 解析参数值并返回以解析到的索引位置 + break; + default: + argParseError( + argParse, argParse->current_command, "unknown error", NULL); + return -1; + } + } + return argParse->argc - 1; +} + // 处理命令参数 - int __processCommand(ArgParse *argParse, char *name, int command_index) { - Command *command = argParseFindCommand(argParse, name); - if (command == NULL) { + CommandArgs *arg = NULL; + Command *command = NULL; - argParseError(argParse, command, "Command not found", NULL); + command = argParseFindCommand(argParse, name); // 查找命令 + + if (command == NULL && argParse->value_type == NOVALUE) { + char *msg = NULL; + if (name != NULL) { + msg = stringNewCopy("\033[1;31mERROR\033[0m:"); + __catStr(&msg, 1, name); + __catStr(&msg, 1, " is not a valid command"); + } + + argParseError(argParse, command, msg, NULL); return -1; } - command->is_trigged = true; // 标记命令被触发 + if (command == NULL && argParse->value_type != NOVALUE) { + return __processVal(argParse, command_index); + } - CommandArgs *arg = NULL; - argParse->current_command = command; + if (command != NULL) { + command->is_trigged = true; // 标记命令被触发 + argParse->current_command = command; + } for (int i = command_index + 1; i < argParse->argc; i++) { ArgType argType = checkArgType(argParse->argv[i]); switch (argType) { - case COMMAND: + case COMMAND: { // 命令无值则处理子命令 if (command->value_type == NOVALUE) { __processSubCommand(argParse, command, argParse->argv[i], i); @@ -397,6 +471,7 @@ int __processCommand(ArgParse *argParse, char *name, int command_index) { argParseSetCommandVal(command, argParse->argv[i]); } break; + } case LONG_ARG: // 处理命令长选项 arg = argParseFindCommandArgs(command, argParse->argv[i], false); @@ -462,6 +537,7 @@ void argParseParse(ArgParse *argParse, int argc, char *argv[]) { ArgType argType = checkArgType(argv[i]); switch (argType) { case COMMAND: + // 处理命令 i = __processCommand(argParse, argv[i], i); break; case LONG_ARG: // 处理全局长选项 @@ -638,44 +714,27 @@ char **argParseGetGlobalArgList(ArgParse *argParse, char *opt, int *len) { return arg->val; } -size_t __getStrlen(char *str) { - if (str == NULL) { - return 0; +char **argParseGetValList(ArgParse *argParse, int *len) { + if (argParse == NULL) { + return NULL; } - return strlen(str); + + if (argParse->val_len == 0) { + return NULL; + } + *len = argParse->val_len; + return argParse->val; } -void __catStr(char **dst, int count, ...) { - va_list args; - va_start(args, count); - - size_t total_len = 0; - total_len = __getStrlen(*dst); - - // 计算总长度 - char *temp = NULL; - for (int i = 0; i < count; i++) { - temp = va_arg(args, char *); - total_len += __getStrlen(temp); - } - va_end(args); - - // 分配内存 - *dst = realloc(*dst, total_len + 1); // +1 是为了存储字符串结束符 '\0' - if (*dst == NULL) { - va_end(args); - return; // 处理内存分配失败 +char *argParseGetVal(ArgParse *argParse) { + if (argParse == NULL) { + return NULL; } - // 拼接字符串 - va_start(args, count); - temp = NULL; - for (int i = 0; i < count; i++) { - temp = va_arg(args, char *); - strcat(*dst, temp); + if (argParse->val_len == 0) { + return NULL; } - - va_end(args); + return argParse->val[0]; } char *argParseGenerateHelpForCommand(Command *command) { @@ -759,25 +818,41 @@ bool argParseCheckCommandTriggered(ArgParse *argParse, char *command_name) { return command->is_trigged; } -_Noreturn void argParseError(ArgParse *argParse, - Command *lastCommand, - char *prefix, - char *suffix) { +_Noreturn void argParseError(ArgParse *argParse, + Command *lastCommand, + const char *prefix, + const char *suffix) { if (argParse == NULL) { printf("ERROR: Parse is NULL\n"); exit(1); } if (lastCommand == NULL) { + char *mgs = stringNewCopy(""); + if (prefix != NULL) { + __catStr(&mgs, 1, prefix); + } char *help = argParseGenerateHelp(argParse); - printf("\033[1;31mERROR\033[0m: Last command is unknown\n"); - printf("%s\n", help); + + if (help != NULL) { + __catStr(&mgs, 2, "\n", help); + } + + if (suffix != NULL) { + __catStr(&mgs, 2, "\n", suffix); + } + + // printf("\033[1;31mERROR\033[0m: Last command is unknown\n"); + printf("%s\n", mgs); + free(mgs); + free(help); + argParseFree(argParse); exit(1); } char *ErrorMsg = NULL; if (prefix != NULL) { - ErrorMsg = stringNewCopy(prefix); + ErrorMsg = stringNewCopy((char *)prefix); } char *command_help_msg = argParseGenerateHelpForCommand(lastCommand); @@ -794,8 +869,7 @@ _Noreturn void argParseError(ArgParse *argParse, printf("%s\n", ErrorMsg); free(ErrorMsg); free(command_help_msg); - free(prefix); - free(suffix); + argParseFree(argParse); exit(1); } diff --git a/src/ArgParseTools.c b/src/ArgParseTools.c index 59f4a30..159f0bb 100644 --- a/src/ArgParseTools.c +++ b/src/ArgParseTools.c @@ -1,5 +1,7 @@ #include "ArgParseTools.h" #include "ArgParse.h" +#include +#include #include #include #include @@ -242,4 +244,85 @@ bool argParseSetCommandVal(Command *command, char *val) { return true; } return false; +} + +/** + * @brief 设置解析值,该值为程序所需的无命令值例如`gcc mian.c`的`main.c` + * @param argParse 解析器 + * @param val 值 + * @return 成功返回true,失败返回false + */ +bool argParseSetVal(ArgParse *argParse, char *val) { + + if (argParse->value_type == MULTIVALUE) { // 多值 + argParse->val = + realloc(argParse->val, (argParse->val_len + 1) * sizeof(char *)); + if (argParse->val == NULL) { + return false; + } + argParse->val[argParse->val_len] = stringNewCopy(val); + if (argParse->val[argParse->val_len] == NULL) { + return false; + } + argParse->val_len++; + return true; + } else if (argParse->value_type == SINGLEVALUE) { // 单值 + if (argParse->val != NULL) { + free(argParse->val); + } + argParse->val = malloc(sizeof(char *)); + + if (argParse->val == NULL) { + return false; + } + argParse->val[0] = stringNewCopy(val); // 分配内存 + if (argParse->val[0] == NULL) { + return false; + } + argParse->val_len = 1; + return true; + } else if (argParse->value_type == NOVALUE) { // 无值 + return true; + } + return false; +} + +size_t __getStrlen(char *str) { + if (str == NULL) { + return 0; + } + return strlen(str); +} + +void __catStr(char **dst, int count, ...) { + va_list args; + va_start(args, count); + + size_t total_len = 0; + total_len = __getStrlen(*dst); + + // 计算总长度 + char *temp = NULL; + for (int i = 0; i < count; i++) { + temp = va_arg(args, char *); + total_len += __getStrlen(temp); + } + va_end(args); + + // 分配内存 + *dst = realloc(*dst, total_len + 1); // +1 是为了存储字符串结束符 '\0' + if (*dst == NULL) { + va_end(args); + return; // 处理内存分配失败 + } + + // 拼接字符串 + va_start(args, count); + temp = NULL; + for (int i = 0; i < count; i++) { + temp = va_arg(args, char *); + strcat(*dst, temp); + } + + va_end(args); } \ No newline at end of file diff --git a/src/ArgParseTools.h b/src/ArgParseTools.h index d5a9d74..df1bcde 100644 --- a/src/ArgParseTools.h +++ b/src/ArgParseTools.h @@ -3,6 +3,7 @@ #include "ArgParse.h" #include +#include #ifdef __cplusplus extern "C" { @@ -45,6 +46,8 @@ bool argParseSetArgVal(CommandArgs *args, char *val); // 设置命令参数值 bool argParseSetCommandVal(Command *command, char *val); // 设置命令值 +bool argParseSetVal(ArgParse *argParse, char *val); // 设置值 + ArgType checkArgType(char *arg); // 检查参数类型 Command *argParseFindCommand(ArgParse *argParse, char *name); // 查找命令 @@ -59,7 +62,9 @@ CommandArgs *argParseFindGlobalArgs(ArgParse *argParse, char *name, bool short_flag); // 查找全局参数 -char *stringNewCopy(char *str); // 创建字符串副本 +char *stringNewCopy(char *str); // 创建字符串副本 +void __catStr(char **dst, int count, ...); // 字符串拼接 +size_t __getStrlen(char *str); // 获取字符串长度 #ifdef __cplusplus } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fd33004..5d87e04 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -32,3 +32,8 @@ add_executable(${PROJECT_NAME}unknow_command_arg test_unknow_command.c) target_link_libraries(${PROJECT_NAME}unknow_command_arg CArgParse) add_test(${PROJECT_NAME}unknow_command_arg ${PROJECT_NAME}unknow_command_arg install --unknow) set_tests_properties(${PROJECT_NAME}unknow_command_arg PROPERTIES WILL_FAIL TRUE) + +# 未知命令选项测试,预期打印该命令的帮助信息 +add_executable(${PROJECT_NAME}val test_val.c) +target_link_libraries(${PROJECT_NAME}val CArgParse) +add_test(${PROJECT_NAME}val ${PROJECT_NAME}val file1.txt file2.txt file3.txt -v -q) \ No newline at end of file diff --git a/tests/initArgParse.h b/tests/initArgParse.h index 2589732..0561878 100644 --- a/tests/initArgParse.h +++ b/tests/initArgParse.h @@ -4,7 +4,7 @@ #include ArgParse *initArgParse() { - ArgParse *argparse = argParseInit("测试程序"); + ArgParse *argparse = argParseInit("测试程序",NOVALUE); Command *command = NULL; Command *sub_command = NULL; diff --git a/tests/test_val.c b/tests/test_val.c new file mode 100644 index 0000000..17fbc05 --- /dev/null +++ b/tests/test_val.c @@ -0,0 +1,122 @@ +#include "ArgParse.h" +#include +#include +#include +#include + +ArgParse *initArgParse() { + ArgParse *argparse = argParseInit("测试程序", MULTIVALUE); + Command *command = NULL; + Command *sub_command = NULL; + + // add global arguments + argParseAddGlobalArg(argparse, + "-v", + "--version", + "Show version", + NULL, + NULL, + false, + NOVALUE); + argParseAddGlobalArg( + argparse, "-q", "--quiet", "Quiet mode", NULL, NULL, false, NOVALUE); + + // add arguments + command = argParseAddCommand( + argparse, "install", "Install the package", NULL, NULL, NULL, NOVALUE); + argParseAddArg(command, + "-i", + "--index", + "Index URL", + "https://example.com", + NULL, + false, + SINGLEVALUE); + argParseAddArg(command, + "-f", + "--file", + "Package file", + "package.json", + NULL, + false, + MULTIVALUE); + argParseAddArg(command, + "-p", + "--package", + "Package file", + "package.json", + NULL, + false, + MULTIVALUE); + + sub_command = argParseAddSubCommand( + command, "tools", "Install tools", NULL, NULL, NULL, MULTIVALUE); + + argParseAddArg(sub_command, + "-t", + "--tool", + "Tool name", + "Tool name", + NULL, + true, + MULTIVALUE); + sub_command = argParseAddSubCommand( + command, "tools_sub", "Install tools", NULL, NULL, NULL, MULTIVALUE); + + argParseAddArg(sub_command, + "-s", + "--source", + "test_source", + "tools subcommand test", + NULL, + true, + MULTIVALUE); + + command = argParseAddCommand(argparse, + "uninstall", + "Uninstall the package", + NULL, + NULL, + NULL, + SINGLEVALUE); + argParseAddArg(command, + "-p", + "--package", + "Package name", + "Package name", + NULL, + true, + MULTIVALUE); + + return argparse; +} + +int main(int argc, char *argv[]) { + ArgParse *argparse = initArgParse(); + + argParseParse(argparse, argc, argv); + + char *val = argParseGetVal(argparse); + if (val) { + printf("val: %s\n", val); + } + + int len = 0; + char **vals = argParseGetValList(argparse, &len); + + char *test_val[3] = {"file1.txt", "file2.txt", "file3.txt"}; + + if (vals) { + for (int i = 0; i < len; i++) { + printf("vals: %s\n", vals[i]); + assert(strcmp(vals[i], test_val[i]) == 0); + } + } + + assert(argParseCheckGlobalTriggered(argparse, "-v")); + assert(argParseCheckGlobalTriggered(argparse, "-q")); + + argParseFree(argparse); + + return 0; +} \ No newline at end of file