18 Commits
v0.2.0 ... main

Author SHA1 Message Date
5879273771 Merge pull request '0.3.2版本' (#4) from dev into main
Reviewed-on: #4
2025-09-30 14:02:57 +08:00
f58d83b4ee docs: update version to 0.3.2 2025-09-30 13:50:10 +08:00
85a219126d fix: 修复必填参数未做检测 2025-09-30 13:35:19 +08:00
1345a0514e style: 优化代码格式和测试用例 2025-09-30 11:20:33 +08:00
0a7179380a style: 格式化代码对齐和参数布局 2025-09-30 10:16:42 +08:00
56a50201fe fix: 修正枚举值命名规范 2025-09-30 10:10:41 +08:00
7b2acabfd6 style: 统一代码格式和命名规范 2025-09-30 10:06:53 +08:00
ad655041dd Merge pull request 'dev' (#3) from dev into main
Reviewed-on: #3
2025-07-30 16:41:41 +08:00
e9dd39f53b fix: 修复msvc不支持_Noreturn 2025-07-30 16:40:27 +08:00
cffe8bfe4a docs: update 2025-07-16 14:25:06 +08:00
b4663a01e8 Merge pull request 'dev' (#2) from dev into main
Reviewed-on: #2
2025-07-16 14:10:30 +08:00
b1eaae793e docs: 0.3.0 2025-07-16 14:06:11 +08:00
ce4d0fc423 docs: update 2025-07-16 14:02:02 +08:00
f6ee49f85e feat: 添加程序值 2025-07-16 13:59:23 +08:00
924f9662b5 Merge pull request 'main' (#1) from main into dev
Reviewed-on: #1
2025-07-16 09:57:02 +08:00
5df2e31973 docs: 添加MIT许可证文件 2025-07-16 09:56:00 +08:00
c71c9b6d15 docs: 更新文档 2025-07-15 11:14:31 +08:00
3ade1fd1aa docs: 更新特性介绍 2025-07-15 10:42:23 +08:00
16 changed files with 873 additions and 128 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 youmetme
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

191
README.md
View File

@@ -5,8 +5,13 @@ C语言易用的命令参数分析
# 特性
- 自动生成帮助信息
- 命令自动帮助信息
- 默认值支持
- 子命令支持
- 命令参数支持
- 全局参数
- 互斥参数(即将)
# 安装
@@ -29,7 +34,7 @@ conan create .
## 示例程序
构建简单的命令行程序
构建简单的命令行程序,更多示例请参考`example`目录
```c
#include "ArgParse.h"
#include <stdbool.h>
@@ -37,11 +42,11 @@ conan create .
#include <string.h>
ArgParse *Init() {
ArgParse *ap = argParseInit("简单的命令行工具示例");
ArgParse *ap = argParseInit("简单的命令行工具示例",ArgParseNOVALUE);
// 添加第一个命令
Command *cmd = argParseAddCommand(
ap, "list", "列出文件列表", NULL, NULL, NULL, SINGLEVALUE);
ap, "list", "列出文件列表", NULL, NULL, NULL, ArgParseSINGLEVALUE);
// 添加第一个命令的参数
argParseAddArg(cmd,
@@ -51,7 +56,7 @@ ArgParse *Init() {
NULL,
NULL,
false,
NOVALUE);
ArgParseNOVALUE);
return ap;
}
@@ -77,4 +82,180 @@ int main(int argc, char *argv[]) {
```bash
example -h
```
![帮助信息](docs/images/help.png)
![帮助信息](docs/images/help.png)
# API
该API将分为两类一类是用于构造解析器另一类将用于解析参数
## 构造解析器
1. 初始化解析器
```c
ArgParse *argParseInit(char *documentationArgParseValueType value_type);
```
- `documentation` 解析器的文档信息
- `valueType` 程序本身需要的值类型
2. 添加命令
```c
Command *argParseAddCommand(ArgParse *argParse,
char *name,
char *help,
char *default_val,
ArgParseCallback callback,
CommandGroup *group,
ArgParseValueType value_type);
```
- `argParse` 解析器
- `name` 命令名称
- `help` 命令帮助信息
- `defaultVal` 默认值
- `callback` 命令回调函数(可选)
- `group` 命令组(可选)
- `valueType` 命令参数类型
3. 添加命令参数
```c
CommandArgs *argParseAddArg(Command *command,
char *short_opt,
char *long_opt,
char *help,
char *default_val,
ArgParseCallback callback,
bool required,
ArgParseValueType value_type);
```
- `command` 命令
- `shortOpt` 短选项
- `longOpt` 长选项
- `help` 帮助信息
- `defaultVal` 默认值
- `callback` 回调函数(可选)
- `required` 是否必须
- `valueType` 参数类型
4. 添加子命令
```c
Command *argParseAddSubCommand(Command *Parent,
char *name,
char *help,
char *default_val,
ArgParseCallback callback,
CommandGroup *group,
ArgParseValueType value_type);
```
- `Parent` 父命令
- `name` 命令名称
- `help` 命令帮助信息
- `defaultVal` 默认值
- `callback` 回调函数(可选)
- `group` 命令组(可选)
- `valueType` 命令参数类型
5. 添加全局参数
```c
CommandArgs *argParseAddGlobalArg(ArgParse *argParse,
char *short_opt,
char *long_opt,
char *help,
char *default_val,
ArgParseCallback callback,
bool required,
ArgParseValueType value_type);
```
- `argParse` 解析器
- `shortOpt` 短选项
- `longOpt` 长选项
- `help` 帮助信息
- `defaultVal` 默认值
- `callback` 回调函数(可选)
- `required` 是否必须
- `valueType` 参数类型
## 解析参数API
1. 解析参数
```c
void argParseParse(ArgParse *argParse, int argc, char *argv[]);
```
- `argParse` 解析器
- `argc` 参数个数
- `argv` 参数列表
2. 获取当前解析到的命令名字
```c
char *argParseGetCurCommandName(ArgParse *argParse);
```
- `argParse` 解析器
> 注意,解析到的命令为最后一次出现的命令,也可能是子命令
3. 获取当前命令的值
```c
char *argParseGetCurCommandValue(ArgParse *argParse);
```
- `argParse` 解析器
> 注意,解析到的命令为最后一次出现的命令,也可能是子命令
4. 获取当前命令的某个参数的值
```c
char *argParseGetCurArg(ArgParse *argParse, char *opt);
```
- `argParse` 解析器
- `opt` 参数选项
5. 获取当前命令的某个选项的值列表(当为多值类型时使用该函数)
```c
char **argParseGetCurArgList(ArgParse *argParse, char *opt, int *len);
```
- `argParse` 解析器
- `opt` 参数选项
- `len` 参数个数
6. 获取全局选项的值
```c
char *argParseGetGlobalArg(ArgParse *argParse, char *opt);
```
- `argParse` 解析器
- `opt` 参数选项
7. 获取全局选项的值列表
```c
char **argParseGetGlobalArgList(ArgParse *argParse, char *opt, int *len);
```
- `argParse` 解析器
- `opt` 参数选项
- `len` 参数个数
8. 获取程序值
该值为非命令的值,类似`gcc main.c`,该类程序没有使用命令,因此该值就是`main.c`
```c
char *argParseGetVal(ArgParse *argParse);
char **argParseGetValList(ArgParse *argParse, int *len);
```
- `argParse` 解析器
- `len` 参数个数
### 触发检测API
1. 检测当前检测的命令的某个选项是否触发
```c
bool argParseCheckCurArgTriggered(ArgParse *argParse, char *opt);
```
- `argParse` 解析器
- `opt` 参数选项
2. 检测某个命令是否被触发
```c
bool argParseCheckCommandTriggered(ArgParse *argParse, char *command_name);
```
- `argParse` 解析器
- `command_name` 命令名称
3. 检测某个全局选项是否被触发
```c
bool argParseCheckGlobalTriggered(ArgParse *argParse, char *opt);
```
- `argParse` 解析器
- `opt` 参数选项

View File

@@ -6,7 +6,7 @@ import os
class loggingRecipe(ConanFile):
name = "cargparse"
version = "0.2.0"
version = "0.3.2"
license = "MIT"
author = "321640253@qq.com"
url = "https://gitea.youmetme.wang/youmetme/logging"

View File

@@ -4,11 +4,11 @@
#include <string.h>
ArgParse *Init() {
ArgParse *ap = argParseInit("简单的命令行工具示例");
ArgParse *ap = argParseInit("简单的命令行工具示例",ArgParseNOVALUE);
// 添加第一个命令
Command *cmd = argParseAddCommand(
ap, "list", "列出文件列表", NULL, NULL, NULL, SINGLEVALUE);
ap, "list", "列出文件列表", NULL, NULL, NULL, ArgParseSINGLEVALUE);
// 添加第一个命令的参数
argParseAddArg(cmd,
@@ -18,7 +18,7 @@ ArgParse *Init() {
NULL,
NULL,
false,
NOVALUE);
ArgParseNOVALUE);
return ap;
}

View File

@@ -7,6 +7,12 @@
extern "C" {
#endif
#ifdef _MSC_VER
#define NORETURN __declspec(noreturn)
#else
#define NORETURN _Noreturn
#endif
#define ARG_DEFAULT_HELP_FLAG "--help"
typedef struct ArgParse ArgParse; // 解析器
@@ -16,10 +22,10 @@ typedef int (*ArgParseCallback)(ArgParse *argParse,
int val_len); // 回调函数
typedef enum {
NOVALUE = 0, // 无值
SINGLEVALUE, // 单值 例如: -i https://www.baidu.com
MULTIVALUE, // 多值 例如: -s a b c 或 -s a -s b -s c等
} ArgParseValueType; // 值类型
ArgParseNOVALUE = 0, // 无值
ArgParseSINGLEVALUE, // 单值 例如: -i https://www.baidu.com
ArgParseMULTIVALUE, // 多值 例如: -s a b c 或 -s a -s b -s c等
} ArgParseValueType; // 值类型
typedef struct CommandArgs {
/* 构造属性 */
@@ -69,19 +75,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 +280,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 +309,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
}

View File

@@ -7,12 +7,16 @@
#include <stdlib.h>
#include <string.h>
#define RED "\033[0;31m"
#define BLUE "\033[0;34m"
#define RESET "\033[0m"
static bool _AutoHelp = true; // 是否自动添加帮助信息
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 +30,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);
@@ -34,15 +41,20 @@ ArgParse *argParseInit(char *documentation) {
/**
* @brief 自动帮助信息回调函数
* @param argParse ArgParse结构体指针
* @param val 参数值
* @param val_len 参数值长度
* @return 无返回值,将直接结束程序
*/
int __helpCallback(ArgParse *argParse, char **val, int val_len) {
NORETURN int __helpCallback(ArgParse *argParse, char **val, int val_len) {
if (argParse == NULL) {
return -1;
exit(1);
}
char *help_doc = argParseGenerateHelp(argParse);
printf("%s\n", help_doc);
free(help_doc);
return 0;
argParseFree(argParse);
exit(0);
}
/**
@@ -64,7 +76,7 @@ void argParseAutoHelp(ArgParse *argParse) {
NULL,
__helpCallback,
false,
NOVALUE);
ArgParseNOVALUE);
}
int __commandHelpCallback(ArgParse *argParse, char **val, int val_len) {
@@ -93,7 +105,7 @@ void argParseCommandAutoHelp(Command *command) {
NULL,
__commandHelpCallback,
false,
NOVALUE);
ArgParseNOVALUE);
}
Command *argParseAddCommand(ArgParse *argParse,
@@ -259,7 +271,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 +295,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---------------- */
@@ -294,7 +323,7 @@ int __processArgs(ArgParse *argParse, CommandArgs *arg, int arg_index) {
int current_index = arg_index;
if (arg->value_type == MULTIVALUE) {
if (arg->value_type == ArgParseMULTIVALUE) {
for (int i = arg_index + 1; i < argParse->argc; i++) {
if (checkArgType(argParse->argv[i]) ==
COMMAND) { // COMMAND是无--或-开头的字符串,也可认定为参数值
@@ -305,12 +334,12 @@ int __processArgs(ArgParse *argParse, CommandArgs *arg, int arg_index) {
break;
}
}
} else if (arg->value_type == SINGLEVALUE) {
} else if (arg->value_type == ArgParseSINGLEVALUE) {
if (arg_index + 1 < argParse->argc) {
argParseSetArgVal(arg, argParse->argv[arg_index + 1]);
current_index = arg_index + 1;
}
} else if (arg->value_type == NOVALUE) {
} else if (arg->value_type == ArgParseNOVALUE) {
current_index = arg_index;
}
@@ -369,27 +398,81 @@ 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 == ArgParseNOVALUE) {
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 != ArgParseNOVALUE) {
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) {
if (command->value_type == ArgParseNOVALUE) {
__processSubCommand(argParse, command, argParse->argv[i], i);
return argParse->argc - 1;
} else {
@@ -397,6 +480,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);
@@ -448,6 +532,7 @@ int __processCommand(ArgParse *argParse, char *name, int command_index) {
/**
* @brief 解析命令行参数
* @errors: 错误信息字符串统一又调用方申请,处理函数释放
* @param argParse 解析器指针
* @param argc 参数个数
* @param argv 参数列表
@@ -462,6 +547,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: // 处理全局长选项
@@ -498,6 +584,47 @@ void argParseParse(ArgParse *argParse, int argc, char *argv[]) {
argParse->current_command->val,
argParse->current_command->val_len);
}
// 检查全局参数必填参数是否已设置
for (int i = 0; i < argParse->global_args_len; i++) {
if (argParse->global_args[i]->required &&
argParse->global_args[i]->is_trigged == false) {
// 错误处理,必填全局参数未设置
char *msg =
stringNewCopy(RED "ERROR" RESET ": Global Option " BLUE);
if (argParse->global_args[i]->short_opt != NULL) {
__catStr(&msg, 1, argParse->global_args[i]->short_opt);
} else {
__catStr(&msg, 1, argParse->global_args[i]->long_opt);
}
__catStr(&msg, 1, RESET " is required");
argParseError(argParse, NULL, msg,
NULL); // 错误处理
}
}
// 检查当前命令的必填参数是否已设置
if (argParse->current_command != NULL) {
for (int i = 0; i < argParse->current_command->args_len; i++) {
if (argParse->current_command->args[i]->required &&
argParse->current_command->args[i]->is_trigged == false) {
// 错误处理,必填参数未设置
char *msg = stringNewCopy(RED "ERROR" RESET ": Command " BLUE);
__catStr(&msg, 1, argParse->current_command->name);
__catStr(&msg, 1, RESET " Option " BLUE);
if (argParse->current_command->args[i]->short_opt != NULL) {
__catStr(
&msg, 1, argParse->current_command->args[i]->short_opt);
} else {
__catStr(
&msg, 1, argParse->current_command->args[i]->long_opt);
}
__catStr(&msg, 1, RESET " is required");
argParseError(
argParse, argParse->current_command, msg, NULL); // 错误处理
}
}
}
}
/**
@@ -638,44 +765,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) {
@@ -692,12 +802,12 @@ char *argParseGenerateHelpForCommand(Command *command) {
__catStr(&help_msg, 2, "\033[1;33mUsage\033[0m: ", command->name);
switch (command->value_type) {
case NOVALUE:
case ArgParseNOVALUE:
break;
case SINGLEVALUE:
case ArgParseSINGLEVALUE:
__catStr(&help_msg, 1, " <value>");
break;
case MULTIVALUE:
case ArgParseMULTIVALUE:
__catStr(&help_msg, 1, " <value>...");
}
@@ -759,25 +869,40 @@ 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("%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 +919,7 @@ _Noreturn void argParseError(ArgParse *argParse,
printf("%s\n", ErrorMsg);
free(ErrorMsg);
free(command_help_msg);
free(prefix);
free(suffix);
argParseFree(argParse);
exit(1);
}

View File

@@ -1,5 +1,7 @@
#include "ArgParseTools.h"
#include "ArgParse.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
@@ -177,7 +179,7 @@ argParseFindGlobalArgs(ArgParse *argParse, char *name, bool short_flag) {
* @return 成功返回true失败返回false
*/
bool argParseSetArgVal(CommandArgs *args, char *val) {
if (args->value_type == MULTIVALUE) { // 多值
if (args->value_type == ArgParseMULTIVALUE) { // 多值
args->val = realloc(args->val, (args->val_len + 1) * sizeof(char *));
if (args->val == NULL) {
return false;
@@ -188,7 +190,7 @@ bool argParseSetArgVal(CommandArgs *args, char *val) {
}
args->val_len++;
return true;
} else if (args->value_type == SINGLEVALUE) { // 单值
} else if (args->value_type == ArgParseSINGLEVALUE) { // 单值
if (args->val != NULL) {
free(args->val);
}
@@ -203,7 +205,7 @@ bool argParseSetArgVal(CommandArgs *args, char *val) {
}
args->val_len = 1;
return true;
} else if (args->value_type == NOVALUE) { // 无值
} else if (args->value_type == ArgParseNOVALUE) { // 无值
return true;
}
@@ -211,7 +213,7 @@ bool argParseSetArgVal(CommandArgs *args, char *val) {
}
bool argParseSetCommandVal(Command *command, char *val) {
if (command->value_type == MULTIVALUE) { // 多值
if (command->value_type == ArgParseMULTIVALUE) { // 多值
command->val =
realloc(command->val, (command->val_len + 1) * sizeof(char *));
if (command->val == NULL) {
@@ -223,7 +225,7 @@ bool argParseSetCommandVal(Command *command, char *val) {
}
command->val_len++;
return true;
} else if (command->value_type == SINGLEVALUE) { // 单值
} else if (command->value_type == ArgParseSINGLEVALUE) { // 单值
if (command->val != NULL) {
free(command->val);
}
@@ -238,8 +240,89 @@ bool argParseSetCommandVal(Command *command, char *val) {
}
command->val_len = 1;
return true;
} else if (command->value_type == NOVALUE) { // 无值
} else if (command->value_type == ArgParseNOVALUE) { // 无值
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 == ArgParseMULTIVALUE) { // 多值
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 == ArgParseSINGLEVALUE) { // 单值
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 == ArgParseNOVALUE) { // 无值
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 <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
@@ -25,14 +26,14 @@ Command *createCommand(char *name,
CommandGroup *createCommandGroup(char *name, char *help); // 创建命令组
CommandGroup *addCommandToGroup(CommandGroup *group,
Command *command); // 添加命令到命令组
Command *command); // 添加命令到命令组
CommandArgs *createCommandArgs(char *short_opt,
char *long_opt,
char *default_val,
char *help,
ArgParseCallback callback,
bool required,
CommandArgs *createCommandArgs(char *short_opt,
char *long_opt,
char *default_val,
char *help,
ArgParseCallback callback,
bool required,
ArgParseValueType value_type); // 创建命令参数
/**
@@ -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
}

View File

@@ -32,3 +32,17 @@ 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)
# 必填参数测试
add_executable(${PROJECT_NAME}required test_required.c)
target_link_libraries(${PROJECT_NAME}required CArgParse)
add_test(${PROJECT_NAME}required_1 ${PROJECT_NAME}required -f)
add_test(${PROJECT_NAME}required_2 ${PROJECT_NAME}required)
set_tests_properties(${PROJECT_NAME}required_2 PROPERTIES WILL_FAIL TRUE)
add_test(${PROJECT_NAME}required_3 ${PROJECT_NAME}required -h)
add_test(${PROJECT_NAME}required_4 ${PROJECT_NAME}required -f install -h)

View File

@@ -4,7 +4,7 @@
#include <string.h>
ArgParse *initArgParse() {
ArgParse *argparse = argParseInit("测试程序");
ArgParse *argparse = argParseInit("测试程序", ArgParseNOVALUE);
Command *command = NULL;
Command *sub_command = NULL;
@@ -16,13 +16,24 @@ ArgParse *initArgParse() {
NULL,
NULL,
false,
NOVALUE);
argParseAddGlobalArg(
argparse, "-q", "--quiet", "Quiet mode", NULL, NULL, false, NOVALUE);
ArgParseNOVALUE);
argParseAddGlobalArg(argparse,
"-q",
"--quiet",
"Quiet mode",
NULL,
NULL,
false,
ArgParseNOVALUE);
// add arguments
command = argParseAddCommand(
argparse, "install", "Install the package", NULL, NULL, NULL, NOVALUE);
command = argParseAddCommand(argparse,
"install",
"Install the package",
NULL,
NULL,
NULL,
ArgParseNOVALUE);
argParseAddArg(command,
"-i",
"--index",
@@ -30,7 +41,7 @@ ArgParse *initArgParse() {
"https://example.com",
NULL,
false,
SINGLEVALUE);
ArgParseSINGLEVALUE);
argParseAddArg(command,
"-f",
"--file",
@@ -38,7 +49,7 @@ ArgParse *initArgParse() {
"package.json",
NULL,
false,
MULTIVALUE);
ArgParseMULTIVALUE);
argParseAddArg(command,
"-p",
"--package",
@@ -46,10 +57,15 @@ ArgParse *initArgParse() {
"package.json",
NULL,
false,
MULTIVALUE);
ArgParseMULTIVALUE);
sub_command = argParseAddSubCommand(
command, "tools", "Install tools", NULL, NULL, NULL, MULTIVALUE);
sub_command = argParseAddSubCommand(command,
"tools",
"Install tools",
NULL,
NULL,
NULL,
ArgParseMULTIVALUE);
argParseAddArg(sub_command,
"-t",
@@ -58,9 +74,14 @@ ArgParse *initArgParse() {
"Tool name",
NULL,
true,
MULTIVALUE);
sub_command = argParseAddSubCommand(
command, "tools_sub", "Install tools", NULL, NULL, NULL, MULTIVALUE);
ArgParseMULTIVALUE);
sub_command = argParseAddSubCommand(command,
"tools_sub",
"Install tools",
NULL,
NULL,
NULL,
ArgParseMULTIVALUE);
argParseAddArg(sub_command,
"-s",
@@ -69,7 +90,7 @@ ArgParse *initArgParse() {
"tools subcommand test",
NULL,
true,
MULTIVALUE);
ArgParseMULTIVALUE);
command = argParseAddCommand(argparse,
"uninstall",
@@ -77,7 +98,7 @@ ArgParse *initArgParse() {
NULL,
NULL,
NULL,
SINGLEVALUE);
ArgParseSINGLEVALUE);
argParseAddArg(command,
"-p",
"--package",
@@ -85,7 +106,7 @@ ArgParse *initArgParse() {
"Package name",
NULL,
true,
MULTIVALUE);
ArgParseMULTIVALUE);
return argparse;
}

View File

@@ -10,12 +10,11 @@ int main(int argc, char *argv[]) {
const char *testv[3] = {"testpackge1", "testpackge2", "testpackge3"};
// Test -p
char *val = argParseGetCurArg(argparse, "-p");
char *val = argParseGetCurArg(argparse, "-p");
int len = 0;
char **vals = argParseGetCurArgList(argparse, "-p", &len);
int len = 0;
char **vals = argParseGetCurArgList(argparse, "-p", &len);
for (int i = 0; i < len; i++) {
printf("-p Value: %s\n", vals[i]);
@@ -27,18 +26,15 @@ int main(int argc, char *argv[]) {
printf("-i Value: %s\n", val_i);
assert(strcmp(val_i, "www.test.com") == 0);
// Test -f
len = 0;
len = 0;
const char *testf[2] = {"file1.txt", "file2.txt"};
char **val_f = argParseGetCurArgList(argparse, "-f", &len);
char **val_f = argParseGetCurArgList(argparse, "-f", &len);
for (int i = 0; i < len; i++) {
printf("-f Value: %s\n", val_f[i]);
assert(strcmp(val_f[i], testf[i]) == 0);
}
argParseFree(argparse);
return 0;

144
tests/test_required.c Normal file
View File

@@ -0,0 +1,144 @@
#include "ArgParse.h"
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
ArgParse *initArgParse() {
ArgParse *argparse = argParseInit("测试程序", ArgParseNOVALUE);
Command *command = NULL;
Command *sub_command = NULL;
// add global arguments
argParseAddGlobalArg(argparse,
"-v",
"--version",
"Show version",
NULL,
NULL,
false,
ArgParseNOVALUE);
argParseAddGlobalArg(argparse,
"-f",
"--file",
"File name",
NULL,
NULL,
true,
ArgParseNOVALUE);
// add arguments
command = argParseAddCommand(argparse,
"install",
"Install the package",
NULL,
NULL,
NULL,
ArgParseNOVALUE);
argParseAddArg(command,
"-i",
"--index",
"Index URL",
"https://example.com",
NULL,
false,
ArgParseSINGLEVALUE);
argParseAddArg(command,
"-f",
"--file",
"Package file",
"package.json",
NULL,
true,
ArgParseMULTIVALUE);
argParseAddArg(command,
"-p",
"--package",
"Package file",
"package.json",
NULL,
false,
ArgParseMULTIVALUE);
sub_command = argParseAddSubCommand(command,
"tools",
"Install tools",
NULL,
NULL,
NULL,
ArgParseMULTIVALUE);
argParseAddArg(sub_command,
"-t",
"--tool",
"Tool name",
"Tool name",
NULL,
true,
ArgParseMULTIVALUE);
sub_command = argParseAddSubCommand(command,
"tools_sub",
"Install tools",
NULL,
NULL,
NULL,
ArgParseMULTIVALUE);
argParseAddArg(sub_command,
"-s",
"--source",
"test_source",
"tools subcommand test",
NULL,
true,
ArgParseMULTIVALUE);
command = argParseAddCommand(argparse,
"uninstall",
"Uninstall the package",
NULL,
NULL,
NULL,
ArgParseSINGLEVALUE);
argParseAddArg(command,
"-p",
"--package",
"Package name",
"Package name",
NULL,
true,
ArgParseMULTIVALUE);
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, "-f"));
argParseFree(argparse);
return 0;
}

View File

@@ -10,10 +10,10 @@ int main(int argc, char *argv[]) {
const char *testv[3] = {"testpackge1", "testpackge2", "testpackge3"};
char *val = argParseGetCurArg(argparse, "-p");
char *val = argParseGetCurArg(argparse, "-p");
int len = 0;
char **vals = argParseGetCurArgList(argparse, "-p", &len);
int len = 0;
char **vals = argParseGetCurArgList(argparse, "-p", &len);
for (int i = 0; i < len; i++) {
printf("-p Value: %s\n", vals[i]);

View File

@@ -8,12 +8,11 @@ int main(int argc, char *argv[]) {
argParseParse(argparse, argc, argv);
char *command_name = argParseGetCurCommandName(argparse);
printf("command name: %s\n", command_name);
assert(strcmp(command_name, "tools") == 0);
char * val = argParseGetCurArg(argparse, "-t");
char *val = argParseGetCurArg(argparse, "-t");
printf("tools -t: %s\n", val);
argParseFree(argparse);

View File

@@ -7,7 +7,6 @@ int main(int argc, char *argv[]) {
argParseParse(argparse, argc, argv);
argParseFree(argparse);
return 0;

143
tests/test_val.c Normal file
View File

@@ -0,0 +1,143 @@
#include "ArgParse.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
ArgParse *initArgParse() {
ArgParse *argparse = argParseInit("测试程序", ArgParseMULTIVALUE);
Command *command = NULL;
Command *sub_command = NULL;
// add global arguments
argParseAddGlobalArg(argparse,
"-v",
"--version",
"Show version",
NULL,
NULL,
false,
ArgParseNOVALUE);
argParseAddGlobalArg(argparse,
"-q",
"--quiet",
"Quiet mode",
NULL,
NULL,
false,
ArgParseNOVALUE);
// add arguments
command = argParseAddCommand(argparse,
"install",
"Install the package",
NULL,
NULL,
NULL,
ArgParseNOVALUE);
argParseAddArg(command,
"-i",
"--index",
"Index URL",
"https://example.com",
NULL,
false,
ArgParseSINGLEVALUE);
argParseAddArg(command,
"-f",
"--file",
"Package file",
"package.json",
NULL,
false,
ArgParseMULTIVALUE);
argParseAddArg(command,
"-p",
"--package",
"Package file",
"package.json",
NULL,
false,
ArgParseMULTIVALUE);
sub_command = argParseAddSubCommand(command,
"tools",
"Install tools",
NULL,
NULL,
NULL,
ArgParseMULTIVALUE);
argParseAddArg(sub_command,
"-t",
"--tool",
"Tool name",
"Tool name",
NULL,
true,
ArgParseMULTIVALUE);
sub_command = argParseAddSubCommand(command,
"tools_sub",
"Install tools",
NULL,
NULL,
NULL,
ArgParseMULTIVALUE);
argParseAddArg(sub_command,
"-s",
"--source",
"test_source",
"tools subcommand test",
NULL,
true,
ArgParseMULTIVALUE);
command = argParseAddCommand(argparse,
"uninstall",
"Uninstall the package",
NULL,
NULL,
NULL,
ArgParseSINGLEVALUE);
argParseAddArg(command,
"-p",
"--package",
"Package name",
"Package name",
NULL,
true,
ArgParseMULTIVALUE);
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;
}