feat: 添加程序值

This commit is contained in:
2025-07-16 13:59:23 +08:00
parent 924f9662b5
commit f6ee49f85e
8 changed files with 358 additions and 60 deletions

View File

@@ -4,7 +4,7 @@
#include <string.h> #include <string.h>
ArgParse *Init() { ArgParse *Init() {
ArgParse *ap = argParseInit("简单的命令行工具示例"); ArgParse *ap = argParseInit("简单的命令行工具示例",NOVALUE);
// 添加第一个命令 // 添加第一个命令
Command *cmd = argParseAddCommand( Command *cmd = argParseAddCommand(

View File

@@ -69,19 +69,25 @@ typedef struct ArgParse {
struct CommandArgs **global_args; // 全局参数 struct CommandArgs **global_args; // 全局参数
int global_args_len; // 全局参数个数 int global_args_len; // 全局参数个数
char *documentation; // 帮助文档 char *documentation; // 帮助文档
ArgParseValueType value_type; // 值类型 程序默认需要的值例如 gcc main.c
/* 解析所用到的属性*/ /* 解析所用到的属性*/
struct Command *current_command; // 当前解析到的命令 struct Command *current_command; // 当前解析到的命令
int argc; // 参数个数 char **val;
char **argv; // 参数列表 int val_len;
int argc; // 参数个数
char **argv; // 参数列表
} ArgParse; } ArgParse;
/** Start---------------构造API---------------- */ /** Start---------------构造API---------------- */
/** /**
* @brief 初始化解析器 * @brief 初始化解析器
* @param documentation 帮助文档
* @param value_type 值类型,程序默认需要的值例如 gcc main.c
* @return ArgParse* 解析器指针 * @return ArgParse* 解析器指针
*/ */
ArgParse *argParseInit(char *documentation); ArgParse *argParseInit(char *documentation, ArgParseValueType value_type);
/** /**
* @brief 释放解析器 * @brief 释放解析器
@@ -268,6 +274,9 @@ bool argParseCheckCommandTriggered(ArgParse *argParse, char *command_name);
*/ */
bool argParseCheckGlobalTriggered(ArgParse *argParse, char *opt); bool argParseCheckGlobalTriggered(ArgParse *argParse, char *opt);
char *argParseGetVal(ArgParse *argParse);
char **argParseGetValList(ArgParse *argParse, int *len);
/** End----------------解析API---------------- */ /** End----------------解析API---------------- */
/** /**
@@ -294,10 +303,10 @@ char *argParseGenerateHelp(ArgParse *argParse);
char * char *
argParseGenerateArgErrorMsg(ArgParse *argParse, char *name, bool short_flag); argParseGenerateArgErrorMsg(ArgParse *argParse, char *name, bool short_flag);
_Noreturn void argParseError(ArgParse *argParse, _Noreturn void argParseError(ArgParse *argParse,
Command *lastCommand, Command *lastCommand,
char *prefix, const char *prefix,
char *suffix); const char *suffix);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -12,7 +12,7 @@ static bool _COLOR = true; // 是否启用颜色
void argParseDisableAutoHelp() { _AutoHelp = false; } void argParseDisableAutoHelp() { _AutoHelp = false; }
ArgParse *argParseInit(char *documentation) { ArgParse *argParseInit(char *documentation, ArgParseValueType value_type) {
ArgParse *argParse = malloc(sizeof(ArgParse)); ArgParse *argParse = malloc(sizeof(ArgParse));
if (argParse == NULL) { if (argParse == NULL) {
return NULL; return NULL;
@@ -26,6 +26,9 @@ ArgParse *argParseInit(char *documentation) {
argParse->argc = 0; argParse->argc = 0;
argParse->argv = NULL; argParse->argv = NULL;
argParse->documentation = stringNewCopy(documentation); argParse->documentation = stringNewCopy(documentation);
argParse->value_type = value_type;
argParse->val = NULL;
argParse->val_len = 0;
argParseAutoHelp(argParse); argParseAutoHelp(argParse);
@@ -259,7 +262,16 @@ void __freeCommand(Command *command) {
free(command->sub_commands); free(command->sub_commands);
free(command->name); free(command->name);
free(command->help); free(command->help);
free(command->args);
free(command->default_val); 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); free(command);
} }
@@ -274,7 +286,15 @@ void argParseFree(ArgParse *argParse) {
for (size_t i = 0; i < argParse->global_args_len; i++) { for (size_t i = 0; i < argParse->global_args_len; i++) {
__freeCommandArgs(argParse->global_args[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->global_args);
free(argParse->documentation);
free(argParse); free(argParse);
} }
/** End----------------内存释放API---------------- */ /** End----------------内存释放API---------------- */
@@ -369,25 +389,79 @@ int __processSubCommand(ArgParse *argParse,
return -1; 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) { int __processCommand(ArgParse *argParse, char *name, int command_index) {
Command *command = argParseFindCommand(argParse, name); CommandArgs *arg = NULL;
if (command == 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; return -1;
} }
command->is_trigged = true; // 标记命令被触发 if (command == NULL && argParse->value_type != NOVALUE) {
return __processVal(argParse, command_index);
}
CommandArgs *arg = NULL; if (command != NULL) {
argParse->current_command = command; command->is_trigged = true; // 标记命令被触发
argParse->current_command = command;
}
for (int i = command_index + 1; i < argParse->argc; i++) { for (int i = command_index + 1; i < argParse->argc; i++) {
ArgType argType = checkArgType(argParse->argv[i]); ArgType argType = checkArgType(argParse->argv[i]);
switch (argType) { switch (argType) {
case COMMAND: case COMMAND: {
// 命令无值则处理子命令 // 命令无值则处理子命令
if (command->value_type == NOVALUE) { if (command->value_type == NOVALUE) {
__processSubCommand(argParse, command, argParse->argv[i], i); __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]); argParseSetCommandVal(command, argParse->argv[i]);
} }
break; break;
}
case LONG_ARG: case LONG_ARG:
// 处理命令长选项 // 处理命令长选项
arg = argParseFindCommandArgs(command, argParse->argv[i], false); arg = argParseFindCommandArgs(command, argParse->argv[i], false);
@@ -462,6 +537,7 @@ void argParseParse(ArgParse *argParse, int argc, char *argv[]) {
ArgType argType = checkArgType(argv[i]); ArgType argType = checkArgType(argv[i]);
switch (argType) { switch (argType) {
case COMMAND: case COMMAND:
// 处理命令
i = __processCommand(argParse, argv[i], i); i = __processCommand(argParse, argv[i], i);
break; break;
case LONG_ARG: // 处理全局长选项 case LONG_ARG: // 处理全局长选项
@@ -638,44 +714,27 @@ char **argParseGetGlobalArgList(ArgParse *argParse, char *opt, int *len) {
return arg->val; return arg->val;
} }
size_t __getStrlen(char *str) { char **argParseGetValList(ArgParse *argParse, int *len) {
if (str == NULL) { if (argParse == NULL) {
return 0; 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, ...) { char *argParseGetVal(ArgParse *argParse) {
va_list args; if (argParse == NULL) {
va_start(args, count); return NULL;
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; // 处理内存分配失败
} }
// 拼接字符串 if (argParse->val_len == 0) {
va_start(args, count); return NULL;
temp = NULL;
for (int i = 0; i < count; i++) {
temp = va_arg(args, char *);
strcat(*dst, temp);
} }
return argParse->val[0];
va_end(args);
} }
char *argParseGenerateHelpForCommand(Command *command) { char *argParseGenerateHelpForCommand(Command *command) {
@@ -759,25 +818,41 @@ bool argParseCheckCommandTriggered(ArgParse *argParse, char *command_name) {
return command->is_trigged; return command->is_trigged;
} }
_Noreturn void argParseError(ArgParse *argParse, _Noreturn void argParseError(ArgParse *argParse,
Command *lastCommand, Command *lastCommand,
char *prefix, const char *prefix,
char *suffix) { const char *suffix) {
if (argParse == NULL) { if (argParse == NULL) {
printf("ERROR: Parse is NULL\n"); printf("ERROR: Parse is NULL\n");
exit(1); exit(1);
} }
if (lastCommand == NULL) { if (lastCommand == NULL) {
char *mgs = stringNewCopy("");
if (prefix != NULL) {
__catStr(&mgs, 1, prefix);
}
char *help = argParseGenerateHelp(argParse); 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); exit(1);
} }
char *ErrorMsg = NULL; char *ErrorMsg = NULL;
if (prefix != NULL) { if (prefix != NULL) {
ErrorMsg = stringNewCopy(prefix); ErrorMsg = stringNewCopy((char *)prefix);
} }
char *command_help_msg = argParseGenerateHelpForCommand(lastCommand); char *command_help_msg = argParseGenerateHelpForCommand(lastCommand);
@@ -794,8 +869,7 @@ _Noreturn void argParseError(ArgParse *argParse,
printf("%s\n", ErrorMsg); printf("%s\n", ErrorMsg);
free(ErrorMsg); free(ErrorMsg);
free(command_help_msg); free(command_help_msg);
free(prefix); argParseFree(argParse);
free(suffix);
exit(1); exit(1);
} }

View File

@@ -1,5 +1,7 @@
#include "ArgParseTools.h" #include "ArgParseTools.h"
#include "ArgParse.h" #include "ArgParse.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -242,4 +244,85 @@ bool argParseSetCommandVal(Command *command, char *val) {
return true; return true;
} }
return false; 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);
} }

View File

@@ -3,6 +3,7 @@
#include "ArgParse.h" #include "ArgParse.h"
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -45,6 +46,8 @@ bool argParseSetArgVal(CommandArgs *args, char *val); // 设置命令参数值
bool argParseSetCommandVal(Command *command, char *val); // 设置命令值 bool argParseSetCommandVal(Command *command, char *val); // 设置命令值
bool argParseSetVal(ArgParse *argParse, char *val); // 设置值
ArgType checkArgType(char *arg); // 检查参数类型 ArgType checkArgType(char *arg); // 检查参数类型
Command *argParseFindCommand(ArgParse *argParse, char *name); // 查找命令 Command *argParseFindCommand(ArgParse *argParse, char *name); // 查找命令
@@ -59,7 +62,9 @@ CommandArgs *argParseFindGlobalArgs(ArgParse *argParse,
char *name, char *name,
bool short_flag); // 查找全局参数 bool short_flag); // 查找全局参数
char *stringNewCopy(char *str); // 创建字符串副本 char *stringNewCopy(char *str); // 创建字符串副本
void __catStr(char **dst, int count, ...); // 字符串拼接
size_t __getStrlen(char *str); // 获取字符串长度
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -32,3 +32,8 @@ add_executable(${PROJECT_NAME}unknow_command_arg test_unknow_command.c)
target_link_libraries(${PROJECT_NAME}unknow_command_arg CArgParse) target_link_libraries(${PROJECT_NAME}unknow_command_arg CArgParse)
add_test(${PROJECT_NAME}unknow_command_arg ${PROJECT_NAME}unknow_command_arg install --unknow) 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) 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)

View File

@@ -4,7 +4,7 @@
#include <string.h> #include <string.h>
ArgParse *initArgParse() { ArgParse *initArgParse() {
ArgParse *argparse = argParseInit("测试程序"); ArgParse *argparse = argParseInit("测试程序",NOVALUE);
Command *command = NULL; Command *command = NULL;
Command *sub_command = NULL; Command *sub_command = NULL;

122
tests/test_val.c Normal file
View File

@@ -0,0 +1,122 @@
#include "ArgParse.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
}