feat: v0.2.0
This commit is contained in:
874
src/ArgParse.c
Normal file
874
src/ArgParse.c
Normal file
@@ -0,0 +1,874 @@
|
||||
#include "ArgParse.h"
|
||||
#include "ArgParseTools.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool _AutoHelp = true; // 是否自动添加帮助信息
|
||||
static bool _COLOR = true; // 是否启用颜色
|
||||
|
||||
void argParseDisableAutoHelp() { _AutoHelp = false; }
|
||||
|
||||
ArgParse *argParseInit(char *documentation) {
|
||||
ArgParse *argParse = malloc(sizeof(ArgParse));
|
||||
if (argParse == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
argParse->commands = NULL;
|
||||
argParse->commands_len = 0;
|
||||
argParse->current_command = NULL;
|
||||
argParse->global_args = NULL;
|
||||
argParse->global_args_len = 0;
|
||||
argParse->argc = 0;
|
||||
argParse->argv = NULL;
|
||||
argParse->documentation = stringNewCopy(documentation);
|
||||
|
||||
argParseAutoHelp(argParse);
|
||||
|
||||
return argParse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 自动帮助信息回调函数
|
||||
*/
|
||||
int __helpCallback(ArgParse *argParse, char **val, int val_len) {
|
||||
if (argParse == NULL) {
|
||||
return -1;
|
||||
}
|
||||
char *help_doc = argParseGenerateHelp(argParse);
|
||||
printf("%s\n", help_doc);
|
||||
free(help_doc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 自动添加帮助信息
|
||||
* @param argParse ArgParse结构体指针
|
||||
*/
|
||||
void argParseAutoHelp(ArgParse *argParse) {
|
||||
if (!_AutoHelp) {
|
||||
return;
|
||||
}
|
||||
if (argParse == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
argParseAddGlobalArg(argParse,
|
||||
"-h",
|
||||
"--help",
|
||||
"show help",
|
||||
NULL,
|
||||
__helpCallback,
|
||||
false,
|
||||
NOVALUE);
|
||||
}
|
||||
|
||||
int __commandHelpCallback(ArgParse *argParse, char **val, int val_len) {
|
||||
if (argParse == NULL) {
|
||||
return -1;
|
||||
}
|
||||
char *help_doc = argParseGenerateHelpForCommand(argParse->current_command);
|
||||
printf("%s", help_doc);
|
||||
free(help_doc);
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void argParseCommandAutoHelp(Command *command) {
|
||||
if (!_AutoHelp) {
|
||||
return;
|
||||
}
|
||||
if (command == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
argParseAddArg(command,
|
||||
"-h",
|
||||
"--help",
|
||||
"show help",
|
||||
NULL,
|
||||
__commandHelpCallback,
|
||||
false,
|
||||
NOVALUE);
|
||||
}
|
||||
|
||||
Command *argParseAddCommand(ArgParse *argParse,
|
||||
char *name,
|
||||
char *help,
|
||||
char *default_val,
|
||||
ArgParseCallback callback,
|
||||
CommandGroup *group,
|
||||
ArgParseValueType value_type) {
|
||||
|
||||
if (argParse == NULL || name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// 构造命令结构
|
||||
Command *command =
|
||||
createCommand(name, help, default_val, callback, group, value_type);
|
||||
|
||||
// 将命令结构添加到argParse中
|
||||
argParse->commands = realloc(
|
||||
argParse->commands, sizeof(Command *) * (argParse->commands_len + 1));
|
||||
if (argParse->commands == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
argParse->commands[argParse->commands_len] = command;
|
||||
argParse->commands_len++;
|
||||
|
||||
argParseCommandAutoHelp(command);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加子命令
|
||||
* @param Parent 父命令
|
||||
* @param name 子命令名称
|
||||
* @param help 子命令帮助信息
|
||||
* @param callback 子命令回调函数
|
||||
* @param group 子命令组
|
||||
* @param value_type 子命令值类型
|
||||
* @return
|
||||
*/
|
||||
Command *argParseAddSubCommand(Command *Parent,
|
||||
char *name,
|
||||
char *help,
|
||||
char *default_val,
|
||||
ArgParseCallback callback,
|
||||
CommandGroup *group,
|
||||
ArgParseValueType value_type) {
|
||||
|
||||
if (Parent == NULL || name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// 构造命令结构
|
||||
Command *command =
|
||||
createCommand(name, help, default_val, callback, group, value_type);
|
||||
if (command == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// 将命令结构添加到Parent中
|
||||
Parent->sub_commands =
|
||||
realloc(Parent->sub_commands,
|
||||
sizeof(Command *) * (Parent->sub_commands_len + 1));
|
||||
if (Parent->sub_commands == NULL)
|
||||
return NULL;
|
||||
Parent->sub_commands[Parent->sub_commands_len] = command;
|
||||
Parent->sub_commands_len++;
|
||||
|
||||
argParseCommandAutoHelp(command);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
CommandArgs *argParseAddArg(Command *command,
|
||||
char *short_opt,
|
||||
char *long_opt,
|
||||
char *help,
|
||||
char *default_val,
|
||||
ArgParseCallback callback,
|
||||
bool required,
|
||||
ArgParseValueType value_type) {
|
||||
|
||||
if (command == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// 构造参数结构
|
||||
CommandArgs *arg = createCommandArgs(
|
||||
short_opt, long_opt, default_val, help, callback, required, value_type);
|
||||
if (arg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// 将参数结构添加到command中
|
||||
command->args =
|
||||
realloc(command->args, sizeof(CommandArgs *) * (command->args_len + 1));
|
||||
if (command->args == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
command->args[command->args_len] = arg;
|
||||
command->args_len++;
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
CommandArgs *argParseAddGlobalArg(ArgParse *argParse,
|
||||
char *short_opt,
|
||||
char *long_opt,
|
||||
char *help,
|
||||
char *default_val,
|
||||
ArgParseCallback callback,
|
||||
bool required,
|
||||
ArgParseValueType value_type) {
|
||||
|
||||
if (argParse == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// 构造参数结构
|
||||
CommandArgs *arg = createCommandArgs(
|
||||
short_opt, long_opt, default_val, help, callback, required, value_type);
|
||||
if (arg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// 将参数结构添加到argParse中
|
||||
argParse->global_args =
|
||||
realloc(argParse->global_args,
|
||||
sizeof(CommandArgs *) * (argParse->global_args_len + 1));
|
||||
if (argParse->global_args == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
argParse->global_args[argParse->global_args_len] = arg;
|
||||
argParse->global_args_len++;
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
/** Start----------------内存释放API---------------- */
|
||||
void __freeCommandArgs(CommandArgs *arg) {
|
||||
if (arg == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
free(arg->short_opt);
|
||||
free(arg->long_opt);
|
||||
free(arg->help);
|
||||
free(arg->default_val);
|
||||
for (size_t i = 0; i < arg->val_len; i++) {
|
||||
free(arg->val[i]);
|
||||
}
|
||||
free(arg->val);
|
||||
free(arg);
|
||||
}
|
||||
|
||||
void __freeCommand(Command *command) {
|
||||
if (command == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < command->args_len; i++) {
|
||||
__freeCommandArgs(command->args[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < command->sub_commands_len; i++) {
|
||||
__freeCommand(command->sub_commands[i]);
|
||||
}
|
||||
free(command->sub_commands);
|
||||
free(command->name);
|
||||
free(command->help);
|
||||
free(command->default_val);
|
||||
free(command);
|
||||
}
|
||||
|
||||
void argParseFree(ArgParse *argParse) {
|
||||
// 释放命令及其子命令与参数
|
||||
for (size_t i = 0; i < argParse->commands_len; i++) {
|
||||
__freeCommand(argParse->commands[i]);
|
||||
}
|
||||
free(argParse->commands);
|
||||
|
||||
// 释放全局参数
|
||||
for (size_t i = 0; i < argParse->global_args_len; i++) {
|
||||
__freeCommandArgs(argParse->global_args[i]);
|
||||
}
|
||||
free(argParse->global_args);
|
||||
free(argParse);
|
||||
}
|
||||
/** End----------------内存释放API---------------- */
|
||||
|
||||
/** Start----------------解析API---------------- */
|
||||
|
||||
/**
|
||||
* @brief 解析命令行参数
|
||||
* @param argParse ArgParse结构体指针
|
||||
* @param command 命令结构体指针
|
||||
* @param arg 参数结构体指针
|
||||
* @param arg_index 参数索引
|
||||
* @return int 返回解析到的参数索引
|
||||
*/
|
||||
int __processArgs(ArgParse *argParse, CommandArgs *arg, int arg_index) {
|
||||
arg->is_trigged = true; // 标记参数被触发
|
||||
|
||||
int current_index = arg_index;
|
||||
|
||||
if (arg->value_type == MULTIVALUE) {
|
||||
for (int i = arg_index + 1; i < argParse->argc; i++) {
|
||||
if (checkArgType(argParse->argv[i]) ==
|
||||
COMMAND) { // COMMAND是无--或-开头的字符串,也可认定为参数值
|
||||
argParseSetArgVal(arg, argParse->argv[i]);
|
||||
current_index = i;
|
||||
} else {
|
||||
current_index = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (arg->value_type == SINGLEVALUE) {
|
||||
if (arg_index + 1 < argParse->argc) {
|
||||
argParseSetArgVal(arg, argParse->argv[arg_index + 1]);
|
||||
current_index = arg_index + 1;
|
||||
}
|
||||
} else if (arg->value_type == NOVALUE) {
|
||||
current_index = arg_index;
|
||||
}
|
||||
|
||||
if (arg->callback != NULL) {
|
||||
arg->callback(argParse, arg->val, arg->val_len);
|
||||
}
|
||||
|
||||
return current_index;
|
||||
}
|
||||
|
||||
// 处理子命令参数
|
||||
int __processSubCommand(ArgParse *argParse,
|
||||
Command *ParentCommand,
|
||||
char *name,
|
||||
int command_index) {
|
||||
|
||||
Command *sub_command = argParseFindSubCommand(ParentCommand, name);
|
||||
if (sub_command == NULL) {
|
||||
argParseError(argParse, argParse->current_command, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
CommandArgs *arg = NULL;
|
||||
|
||||
argParse->current_command = sub_command;
|
||||
|
||||
for (int i = command_index + 1; i < argParse->argc; i++) {
|
||||
ArgType argType = checkArgType(argParse->argv[i]);
|
||||
switch (argType) {
|
||||
case COMMAND:
|
||||
return __processSubCommand(
|
||||
argParse, sub_command, argParse->argv[i], i); // 递归处理子命令
|
||||
case LONG_ARG:
|
||||
arg =
|
||||
argParseFindCommandArgs(sub_command, argParse->argv[i], false);
|
||||
if (arg != NULL) {
|
||||
i = __processArgs(argParse, arg, i);
|
||||
}
|
||||
return i;
|
||||
case SHORT_ARG:
|
||||
arg = argParseFindCommandArgs(sub_command, argParse->argv[i], true);
|
||||
if (arg != NULL) {
|
||||
i = __processArgs(argParse,
|
||||
arg,
|
||||
i); // 解析参数值并返回以解析到的索引位置
|
||||
return i;
|
||||
} else {
|
||||
argParseError(argParse, argParse->current_command, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
default:
|
||||
argParseError(argParse, argParse->current_command, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
argParseError(argParse, argParse->current_command, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 处理命令参数
|
||||
|
||||
int __processCommand(ArgParse *argParse, char *name, int command_index) {
|
||||
Command *command = argParseFindCommand(argParse, name);
|
||||
if (command == NULL) {
|
||||
|
||||
argParseError(argParse, command, "Command not found", NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
command->is_trigged = true; // 标记命令被触发
|
||||
|
||||
CommandArgs *arg = NULL;
|
||||
argParse->current_command = command;
|
||||
|
||||
for (int i = command_index + 1; i < argParse->argc; i++) {
|
||||
ArgType argType = checkArgType(argParse->argv[i]);
|
||||
switch (argType) {
|
||||
case COMMAND:
|
||||
// 命令无值则处理子命令
|
||||
if (command->value_type == NOVALUE) {
|
||||
__processSubCommand(argParse, command, argParse->argv[i], i);
|
||||
return argParse->argc - 1;
|
||||
} else {
|
||||
// 命令有值,则认为该值是命令值
|
||||
argParseSetCommandVal(command, argParse->argv[i]);
|
||||
}
|
||||
break;
|
||||
case LONG_ARG:
|
||||
// 处理命令长选项
|
||||
arg = argParseFindCommandArgs(command, argParse->argv[i], false);
|
||||
if (arg == NULL) {
|
||||
// 可能全局参数被放在了命令参数之前
|
||||
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;
|
||||
}
|
||||
i = __processArgs(argParse, arg, i);
|
||||
break;
|
||||
case SHORT_ARG:
|
||||
// 处理命令短选项
|
||||
arg = argParseFindCommandArgs(command, argParse->argv[i], true);
|
||||
if (arg == NULL) {
|
||||
// 可能全局参数被放在了命令参数之前
|
||||
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;
|
||||
}
|
||||
i = __processArgs(argParse,
|
||||
arg,
|
||||
i); // 解析参数值并返回以解析到的索引位置
|
||||
break;
|
||||
default:
|
||||
argParseError(argParse, argParse->current_command, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return argParse->argc - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析命令行参数
|
||||
* @param argParse 解析器指针
|
||||
* @param argc 参数个数
|
||||
* @param argv 参数列表
|
||||
*/
|
||||
void argParseParse(ArgParse *argParse, int argc, char *argv[]) {
|
||||
argParse->argc = argc;
|
||||
argParse->argv = argv;
|
||||
|
||||
CommandArgs *arg = NULL;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
ArgType argType = checkArgType(argv[i]);
|
||||
switch (argType) {
|
||||
case COMMAND:
|
||||
i = __processCommand(argParse, argv[i], i);
|
||||
break;
|
||||
case LONG_ARG: // 处理全局长选项
|
||||
arg = argParseFindGlobalArgs(argParse, argv[i], false);
|
||||
if (arg == NULL) {
|
||||
char *msg =
|
||||
argParseGenerateArgErrorMsg(argParse, argv[i], false);
|
||||
argParseError(argParse, argParse->current_command, msg, NULL);
|
||||
return; // 错误处理
|
||||
}
|
||||
i = __processArgs(argParse, arg, i);
|
||||
break;
|
||||
case SHORT_ARG: // 处理全局短选项
|
||||
arg = argParseFindGlobalArgs(argParse, argv[i], true);
|
||||
if (arg == NULL) {
|
||||
char *msg =
|
||||
argParseGenerateArgErrorMsg(argParse, argv[i], true);
|
||||
argParseError(argParse, argParse->current_command, msg, NULL);
|
||||
return; // 错误处理
|
||||
}
|
||||
i = __processArgs(
|
||||
argParse, arg, i); // 解析参数值并返回以解析到的索引位置
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行当前命令的回调函数
|
||||
if (argParse->current_command != NULL &&
|
||||
argParse->current_command->is_trigged &&
|
||||
argParse->current_command->callback != NULL) {
|
||||
argParse->current_command->callback(argParse,
|
||||
argParse->current_command->val,
|
||||
argParse->current_command->val_len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前解析到的命令名,会返回最后一个解析到的命令名(子命令)
|
||||
* @param argParse 解析器指针
|
||||
* @return char* 命令名
|
||||
*/
|
||||
char *argParseGetCurCommandName(ArgParse *argParse) {
|
||||
if (argParse == NULL)
|
||||
return NULL;
|
||||
|
||||
if (argParse->current_command == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return argParse->current_command->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前解析到的命令参数
|
||||
* @param argParse 解析器指针
|
||||
* @param opt 选项名
|
||||
* @return char* 选项值
|
||||
* @return
|
||||
*/
|
||||
char *argParseGetCurArg(ArgParse *argParse, char *opt) {
|
||||
CommandArgs *arg = NULL;
|
||||
// 尝试获取当前命令的长选项参数
|
||||
arg = argParseFindCommandArgs(argParse->current_command, opt, false);
|
||||
|
||||
if (arg == NULL) {
|
||||
// 尝试获取当前命令的短选项参数
|
||||
arg = argParseFindCommandArgs(argParse->current_command, opt, true);
|
||||
}
|
||||
|
||||
if (arg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (arg->val_len >= 1) {
|
||||
return arg->val[0];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前解析到的命令参数列表,仅适用于多值参数
|
||||
* @param argParse 解析器指针
|
||||
* @param opt 选项名
|
||||
* @param len 参数个数buffer
|
||||
* @return char** 参数列表
|
||||
*/
|
||||
char **argParseGetCurArgList(ArgParse *argParse, char *opt, int *len) {
|
||||
CommandArgs *arg = NULL;
|
||||
arg = argParseFindCommandArgs(argParse->current_command, opt, false);
|
||||
|
||||
if (arg == NULL) {
|
||||
arg = argParseFindCommandArgs(argParse->current_command, opt, true);
|
||||
}
|
||||
|
||||
if (arg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
*len = arg->val_len;
|
||||
return arg->val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取全局参数
|
||||
* @param argParse 解析器指针
|
||||
* @param opt 选项名
|
||||
* @return char* 选项值
|
||||
*/
|
||||
char *argParseGetGlobalArg(ArgParse *argParse, char *opt) {
|
||||
CommandArgs *arg = NULL;
|
||||
arg = argParseFindGlobalArgs(argParse, opt, false);
|
||||
|
||||
if (arg == NULL) {
|
||||
arg = argParseFindGlobalArgs(argParse, opt, true);
|
||||
}
|
||||
|
||||
if (arg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (arg->val_len >= 1) {
|
||||
return arg->val[0];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前解析到的命令参数值
|
||||
* @return char* 参数值
|
||||
*/
|
||||
char *argParseGetCurCommandValue(ArgParse *argParse) {
|
||||
Command *command = argParse->current_command;
|
||||
if (command == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (command->val_len >= 1) {
|
||||
return command->val[0];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/**
|
||||
* @brief 获取当前解析到的命令参数列表,仅适用于多值参数
|
||||
* @return char** 参数列表
|
||||
*/
|
||||
char **argParseGetCurCommandValues(ArgParse *argParse, int *len) {
|
||||
Command *command = argParse->current_command;
|
||||
if (command == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (command->val_len >= 1) {
|
||||
*len = command->val_len;
|
||||
return command->val;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取全局参数列表,仅适用于多值参数
|
||||
* @param argParse 解析器指针
|
||||
* @param opt 选项名
|
||||
* @param len 参数个数buffer
|
||||
* @return char** 参数列表
|
||||
*/
|
||||
char **argParseGetGlobalArgList(ArgParse *argParse, char *opt, int *len) {
|
||||
CommandArgs *arg = NULL;
|
||||
arg = argParseFindGlobalArgs(argParse, opt, false);
|
||||
|
||||
if (arg == NULL) {
|
||||
arg = argParseFindGlobalArgs(argParse, opt, true);
|
||||
}
|
||||
|
||||
if (arg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
*len = arg->val_len;
|
||||
return arg->val;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
char *argParseGenerateHelpForCommand(Command *command) {
|
||||
if (command == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *help_msg = stringNewCopy(command->help);
|
||||
if (help_msg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__catStr(&help_msg, 1, "\n\n");
|
||||
__catStr(&help_msg, 2, "\033[1;33mUsage\033[0m: ", command->name);
|
||||
|
||||
switch (command->value_type) {
|
||||
case NOVALUE:
|
||||
break;
|
||||
case SINGLEVALUE:
|
||||
__catStr(&help_msg, 1, " <value>");
|
||||
break;
|
||||
case MULTIVALUE:
|
||||
__catStr(&help_msg, 1, " <value>...");
|
||||
}
|
||||
|
||||
if (command->args != NULL) {
|
||||
__catStr(&help_msg, 1, " [Options]");
|
||||
__catStr(&help_msg, 1, "\n\n\033[1;34mOptions\033[0m:");
|
||||
for (int i = 0; i < command->args_len; i++) {
|
||||
CommandArgs *arg = command->args[i];
|
||||
|
||||
__catStr(&help_msg,
|
||||
6,
|
||||
"\n \033[1;32m",
|
||||
arg->short_opt,
|
||||
"\033[0m , \033[1;32m",
|
||||
arg->long_opt,
|
||||
"\033[0m ",
|
||||
arg->help);
|
||||
}
|
||||
}
|
||||
__catStr(&help_msg, 1, "\n");
|
||||
|
||||
return help_msg;
|
||||
}
|
||||
|
||||
// 检查全局参数是否被触发
|
||||
bool argParseCheckGlobalTriggered(ArgParse *argParse, char *opt) {
|
||||
CommandArgs *arg = NULL;
|
||||
arg = argParseFindGlobalArgs(argParse, opt, false);
|
||||
|
||||
if (arg == NULL) {
|
||||
arg = argParseFindGlobalArgs(argParse, opt, true);
|
||||
}
|
||||
|
||||
if (arg == NULL) {
|
||||
return false;
|
||||
}
|
||||
return arg->is_trigged;
|
||||
}
|
||||
// 检测当前命令的某项参数是否被触发
|
||||
bool argParseCheckCurArgTriggered(ArgParse *argParse, char *opt) {
|
||||
CommandArgs *arg = NULL;
|
||||
arg = argParseFindCommandArgs(argParse->current_command, opt, false);
|
||||
|
||||
if (arg == NULL) {
|
||||
arg = argParseFindCommandArgs(argParse->current_command, opt, true);
|
||||
}
|
||||
|
||||
if (arg == NULL) {
|
||||
return false;
|
||||
}
|
||||
return arg->is_trigged;
|
||||
}
|
||||
// 检测指定命令是否被触发
|
||||
bool argParseCheckCommandTriggered(ArgParse *argParse, char *command_name) {
|
||||
Command *command = argParseFindCommand(argParse, command_name);
|
||||
if (command == NULL) {
|
||||
return false;
|
||||
}
|
||||
return command->is_trigged;
|
||||
}
|
||||
|
||||
_Noreturn void argParseError(ArgParse *argParse,
|
||||
Command *lastCommand,
|
||||
char *prefix,
|
||||
char *suffix) {
|
||||
if (argParse == NULL) {
|
||||
printf("ERROR: Parse is NULL\n");
|
||||
exit(1);
|
||||
}
|
||||
if (lastCommand == NULL) {
|
||||
char *help = argParseGenerateHelp(argParse);
|
||||
printf("\033[1;31mERROR\033[0m: Last command is unknown\n");
|
||||
printf("%s\n", help);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *ErrorMsg = NULL;
|
||||
|
||||
if (prefix != NULL) {
|
||||
ErrorMsg = stringNewCopy(prefix);
|
||||
}
|
||||
|
||||
char *command_help_msg = argParseGenerateHelpForCommand(lastCommand);
|
||||
int command_help_msg_len = strlen(command_help_msg);
|
||||
|
||||
if (command_help_msg != NULL) {
|
||||
__catStr(&ErrorMsg, 2, "\n", command_help_msg);
|
||||
}
|
||||
|
||||
if (suffix != NULL) {
|
||||
__catStr(&ErrorMsg, 2, "\n", suffix);
|
||||
}
|
||||
|
||||
printf("%s\n", ErrorMsg);
|
||||
free(ErrorMsg);
|
||||
free(command_help_msg);
|
||||
free(prefix);
|
||||
free(suffix);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *
|
||||
argParseGenerateArgErrorMsg(ArgParse *argParse, char *name, bool short_flag) {
|
||||
if (argParse == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *ErrorMsg = NULL;
|
||||
|
||||
if (_COLOR) {
|
||||
ErrorMsg = stringNewCopy("\033[1;31mERROR\033[0m: Invalid argument ");
|
||||
} else {
|
||||
ErrorMsg = stringNewCopy("ERROR: Invalid argument ");
|
||||
}
|
||||
|
||||
if (short_flag) {
|
||||
__catStr(&ErrorMsg, 1, name);
|
||||
} else {
|
||||
__catStr(&ErrorMsg, 1, name);
|
||||
}
|
||||
return ErrorMsg;
|
||||
}
|
||||
|
||||
char *argParseGenerateHelp(ArgParse *argParse) {
|
||||
if (argParse == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *help_msg = stringNewCopy(argParse->documentation);
|
||||
|
||||
if (help_msg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__catStr(&help_msg, 1, "\n\n\033[1;34mCommands\033[0m:");
|
||||
for (int i = 0; i < argParse->commands_len; i++) {
|
||||
Command *command = argParse->commands[i];
|
||||
|
||||
__catStr(&help_msg,
|
||||
4,
|
||||
"\n \033[1;32m",
|
||||
command->name,
|
||||
"\033[0m ",
|
||||
command->help);
|
||||
}
|
||||
|
||||
if (argParse->global_args_len > 0) {
|
||||
__catStr(&help_msg, 1, "\n\n\033[1;34mGlobal Options\033[0m:");
|
||||
for (int i = 0; i < argParse->global_args_len; i++) {
|
||||
CommandArgs *arg = argParse->global_args[i];
|
||||
|
||||
__catStr(&help_msg,
|
||||
6,
|
||||
"\n \033[1;32m",
|
||||
arg->short_opt,
|
||||
"\033[0m , \033[1;32m",
|
||||
arg->long_opt,
|
||||
"\033[0m ",
|
||||
arg->help);
|
||||
}
|
||||
}
|
||||
|
||||
return help_msg;
|
||||
}
|
||||
|
||||
_Noreturn void argParseHelp(ArgParse *argParse) {
|
||||
char *help_msg = argParseGenerateHelp(argParse);
|
||||
printf("%s\n", help_msg);
|
||||
free(help_msg);
|
||||
exit(0);
|
||||
}
|
||||
245
src/ArgParseTools.c
Normal file
245
src/ArgParseTools.c
Normal file
@@ -0,0 +1,245 @@
|
||||
#include "ArgParseTools.h"
|
||||
#include "ArgParse.h"
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @brief 分配新内存复制字符串
|
||||
* @param str 字符串
|
||||
* @return 复制后的字符串内存指针
|
||||
*/
|
||||
char *stringNewCopy(char *str) {
|
||||
if (str == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
char *mem = malloc(strlen(str) + 1);
|
||||
if (mem == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
strcpy(mem, str);
|
||||
return mem;
|
||||
}
|
||||
|
||||
// 构造命令
|
||||
Command *createCommand(char *name,
|
||||
char *help,
|
||||
char *default_val,
|
||||
ArgParseCallback callback,
|
||||
CommandGroup *group,
|
||||
ArgParseValueType value_typ) {
|
||||
// 构造命令结构
|
||||
Command *command = malloc(sizeof(Command));
|
||||
if (command == NULL || name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
command->name = stringNewCopy(name);
|
||||
if (command->name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
command->help = stringNewCopy(help);
|
||||
command->default_val = stringNewCopy(default_val);
|
||||
command->callback = callback;
|
||||
|
||||
command->sub_commands = NULL;
|
||||
command->sub_commands_len = 0;
|
||||
command->args = NULL;
|
||||
command->args_len = 0;
|
||||
command->value_type = value_typ;
|
||||
|
||||
if (group != NULL) {
|
||||
command->group = group;
|
||||
} else {
|
||||
command->group = NULL;
|
||||
}
|
||||
|
||||
command->val = NULL;
|
||||
command->val_len = 0;
|
||||
command->is_trigged = false; // 是否被触发
|
||||
return command;
|
||||
}
|
||||
|
||||
ArgType checkArgType(char *arg) {
|
||||
size_t len = strlen(arg);
|
||||
if (len == 0) {
|
||||
return BAD;
|
||||
}
|
||||
if (arg[0] == '-') {
|
||||
if (len >= 2) {
|
||||
if (arg[1] == '-') {
|
||||
return LONG_ARG;
|
||||
} else {
|
||||
return SHORT_ARG;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return COMMAND;
|
||||
}
|
||||
return BAD;
|
||||
}
|
||||
|
||||
// 寻找命令
|
||||
Command *argParseFindCommand(ArgParse *argParse, char *name) {
|
||||
for (size_t i = 0; i < argParse->commands_len; i++) {
|
||||
if (strcmp(argParse->commands[i]->name, name) == 0) {
|
||||
return argParse->commands[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Command *argParseFindSubCommand(Command *command, char *name) {
|
||||
for (size_t i = 0; i < command->sub_commands_len; i++) {
|
||||
if (strcmp(command->sub_commands[i]->name, name) == 0) {
|
||||
return command->sub_commands[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CommandArgs *createCommandArgs(char *short_opt,
|
||||
char *long_opt,
|
||||
char *default_val,
|
||||
char *help,
|
||||
ArgParseCallback callback,
|
||||
bool required,
|
||||
ArgParseValueType value_type) {
|
||||
CommandArgs *args = malloc(sizeof(CommandArgs));
|
||||
|
||||
if (short_opt == NULL && long_opt == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 分配内存
|
||||
args->short_opt = stringNewCopy(short_opt);
|
||||
args->long_opt = stringNewCopy(long_opt);
|
||||
args->default_val = stringNewCopy(default_val);
|
||||
args->help = stringNewCopy(help);
|
||||
|
||||
args->callback = callback;
|
||||
args->required = required;
|
||||
args->value_type = value_type;
|
||||
|
||||
// 解析属性初始化
|
||||
args->val = NULL;
|
||||
args->val_len = 0;
|
||||
args->is_trigged = false; // 是否被触发
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
CommandArgs *
|
||||
argParseFindCommandArgs(Command *command, char *name, bool short_flag) {
|
||||
if (command == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < command->args_len; i++) {
|
||||
if (short_flag) {
|
||||
if (command->args[i]->short_opt != NULL &&
|
||||
strcmp(command->args[i]->short_opt, name) == 0) {
|
||||
return command->args[i];
|
||||
}
|
||||
} else {
|
||||
if (command->args[i]->long_opt != NULL &&
|
||||
strcmp(command->args[i]->long_opt, name) == 0) {
|
||||
return command->args[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CommandArgs *
|
||||
argParseFindGlobalArgs(ArgParse *argParse, char *name, bool short_flag) {
|
||||
for (size_t i = 0; i < argParse->global_args_len; i++) {
|
||||
if (short_flag) {
|
||||
if (argParse->global_args[i]->short_opt != NULL &&
|
||||
strcmp(argParse->global_args[i]->short_opt, name) == 0) {
|
||||
return argParse->global_args[i];
|
||||
}
|
||||
} else {
|
||||
if (argParse->global_args[i]->long_opt != NULL &&
|
||||
strcmp(argParse->global_args[i]->long_opt, name) == 0) {
|
||||
return argParse->global_args[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置命令参数值
|
||||
* @param args 参数
|
||||
* @param val 值
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
bool argParseSetArgVal(CommandArgs *args, char *val) {
|
||||
if (args->value_type == MULTIVALUE) { // 多值
|
||||
args->val = realloc(args->val, (args->val_len + 1) * sizeof(char *));
|
||||
if (args->val == NULL) {
|
||||
return false;
|
||||
}
|
||||
args->val[args->val_len] = stringNewCopy(val);
|
||||
if (args->val[args->val_len] == NULL) {
|
||||
return false;
|
||||
}
|
||||
args->val_len++;
|
||||
return true;
|
||||
} else if (args->value_type == SINGLEVALUE) { // 单值
|
||||
if (args->val != NULL) {
|
||||
free(args->val);
|
||||
}
|
||||
args->val = malloc(sizeof(char *));
|
||||
|
||||
if (args->val == NULL) {
|
||||
return false;
|
||||
}
|
||||
args->val[0] = stringNewCopy(val); // 分配内存
|
||||
if (args->val[0] == NULL) {
|
||||
return false;
|
||||
}
|
||||
args->val_len = 1;
|
||||
return true;
|
||||
} else if (args->value_type == NOVALUE) { // 无值
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool argParseSetCommandVal(Command *command, char *val) {
|
||||
if (command->value_type == MULTIVALUE) { // 多值
|
||||
command->val =
|
||||
realloc(command->val, (command->val_len + 1) * sizeof(char *));
|
||||
if (command->val == NULL) {
|
||||
return false;
|
||||
}
|
||||
command->val[command->val_len] = stringNewCopy(val);
|
||||
if (command->val[command->val_len] == NULL) {
|
||||
return false;
|
||||
}
|
||||
command->val_len++;
|
||||
return true;
|
||||
} else if (command->value_type == SINGLEVALUE) { // 单值
|
||||
if (command->val != NULL) {
|
||||
free(command->val);
|
||||
}
|
||||
command->val = malloc(sizeof(char *));
|
||||
|
||||
if (command->val == NULL) {
|
||||
return false;
|
||||
}
|
||||
command->val[0] = stringNewCopy(val); // 分配内存
|
||||
if (command->val[0] == NULL) {
|
||||
return false;
|
||||
}
|
||||
command->val_len = 1;
|
||||
return true;
|
||||
} else if (command->value_type == NOVALUE) { // 无值
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
67
src/ArgParseTools.h
Normal file
67
src/ArgParseTools.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef ARGPARSETOOLS_H
|
||||
#define ARGPARSETOOLS_H
|
||||
|
||||
#include "ArgParse.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
LONG_ARG, // 长选项
|
||||
SHORT_ARG, // 短选项
|
||||
COMMAND, // 命令
|
||||
BAD, // 错误
|
||||
} ArgType;
|
||||
|
||||
Command *createCommand(char *name,
|
||||
char *help,
|
||||
char *default_val,
|
||||
ArgParseCallback callback,
|
||||
CommandGroup *group,
|
||||
ArgParseValueType value_type); // 创建命令
|
||||
|
||||
CommandGroup *createCommandGroup(char *name, char *help); // 创建命令组
|
||||
|
||||
CommandGroup *addCommandToGroup(CommandGroup *group,
|
||||
Command *command); // 添加命令到命令组
|
||||
|
||||
CommandArgs *createCommandArgs(char *short_opt,
|
||||
char *long_opt,
|
||||
char *default_val,
|
||||
char *help,
|
||||
ArgParseCallback callback,
|
||||
bool required,
|
||||
ArgParseValueType value_type); // 创建命令参数
|
||||
|
||||
/**
|
||||
* @brief 设置命令参数值
|
||||
* @param args 参数
|
||||
* @param val 值
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
bool argParseSetArgVal(CommandArgs *args, char *val); // 设置命令参数值
|
||||
|
||||
bool argParseSetCommandVal(Command *command, char *val); // 设置命令值
|
||||
|
||||
ArgType checkArgType(char *arg); // 检查参数类型
|
||||
|
||||
Command *argParseFindCommand(ArgParse *argParse, char *name); // 查找命令
|
||||
|
||||
Command *argParseFindSubCommand(Command *command, char *name); // 查找子命令
|
||||
|
||||
CommandArgs *argParseFindCommandArgs(Command *command,
|
||||
char *name,
|
||||
bool short_flag); // 查找命令参数
|
||||
|
||||
CommandArgs *argParseFindGlobalArgs(ArgParse *argParse,
|
||||
char *name,
|
||||
bool short_flag); // 查找全局参数
|
||||
|
||||
char *stringNewCopy(char *str); // 创建字符串副本
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
#endif // ARGPARSETOOLS_H
|
||||
Reference in New Issue
Block a user