1
0
mirror of https://github.com/NevermindZZT/letter-shell.git synced 2025-01-21 10:02:54 +08:00

新增 函数签名

## 函数签名

之前的版本里,如果声明的命令是 `SHELL_TYPE_CMD_FUNC`,shell 会自动进行参数的转换,但是参数转换后的类型是猜出来的,无法保证转换后的数据类型是正确的,一旦猜错了,就容易导致程序挂掉

由此,借鉴 Java 等语言的函数签名,新版也引入了函数签名的概念,在声明命令时,可以给定最终执行命令的函数的签名,shell 根据这个签名进行参数转换,使用此功能时,需要打开宏 `SHELL_USING_FUNC_SIGNATURE`

函数签名是一个字符串,通过这个字符串声明表达函数的参数类型,返回值不声明,比如一个函数`int func(int a, char *b, char c)`,它的函数签名就是 `ics`

基本类型的参数签名定义如下:

| 类型                 | 签名 |
| -------------------- | ---- |
| char(字符)           | c    |
| int/short/char(数字) | i    |
| char * (字符串)      | s    |
| pointer              | p    |

声明命令时,在最后添加一个参数 `.data.cmd.signature = "ics"` 即可,比如:

```c
void shellFuncSignatureTest(int a, char *b, char c)
{
    printf("a = %d, b = %s, c = %c\r\n", a, b, c);
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
funcSignatureTest, shellFuncSignatureTest, test function signature, .data.cmd.signature = "isc");
```
This commit is contained in:
Letter 2023-04-15 16:30:44 +08:00
parent c4dad4c43a
commit acd9dceb4e
13 changed files with 237 additions and 38 deletions

View File

@ -1,8 +1,8 @@
# letter shell 3.x
![version](https://img.shields.io/badge/version-3.1.2-brightgreen.svg)
![version](https://img.shields.io/badge/version-3.2.0-brightgreen.svg)
![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg)
![build](https://img.shields.io/badge/build-2021.10.17-brightgreen.svg)
![build](https://img.shields.io/badge/build-2023.04.15-brightgreen.svg)
![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)
一个功能强大的嵌入式shell
@ -25,6 +25,7 @@
- [定义宏说明](#定义宏说明)
- [命令属性字段说明](#命令属性字段说明)
- [代理函数和代理参数解析](#代理函数和代理参数解析)
- [函数签名](#函数签名)
- [权限系统说明](#权限系统说明)
- [锁说明](#锁说明)
- [伴生对象](#伴生对象)
@ -178,6 +179,8 @@
| SHELL_DEFAULT_USER | shell默认用户 |
| SHELL_DEFAULT_USER_PASSWORD | 默认用户密码 |
| SHELL_LOCK_TIMEOUT | shell自动锁定超时 |
| SHELL_USING_FUNC_SIGNATURE | 使用函数签名 |
| SHELL_COMMAND_FILL_BYTES | 命令结构体填充字节数 |
## 使用方式
@ -485,6 +488,36 @@ p1, SHELL_PARAM_FLOAT(p2), p3, SHELL_PARAM_FLOAT(p4));
相比常规的命令导出代理函数命令导出前4个参数和常规形式的命令导出一致之后的参数即传递至目标函数的参数letter shell默认实现的代理函数定义支持最多7个参数p1~p7对于不需要代理参数解析的参数只需要对应写入`px(x为1~7)`即可,比如上方示例的`p1``p3`,而需要代理参数解析的参数,则需要使用对应的参数解析器,比如上方示例的`p2``p4`
## 函数签名
letter shell 3.2.x 之后,引入了函数签名的概念,以便于参数自动解析
之前的版本里,如果声明的命令是 `SHELL_TYPE_CMD_FUNC`shell 会自动进行参数的转换,但是参数转换后的类型是猜出来的,无法保证转换后的数据类型是正确的,一旦猜错了,就容易导致程序挂掉
由此,借鉴 Java 等语言的函数签名新版也引入了函数签名的概念在声明命令时可以给定最终执行命令的函数的签名shell 根据这个签名进行参数转换,使用此功能时,需要打开宏 `SHELL_USING_FUNC_SIGNATURE`
函数签名是一个字符串,通过这个字符串声明表达函数的参数类型,返回值不声明,比如一个函数`int func(int a, char *b, char c)`,它的函数签名就是 `ics`
基本类型的参数签名定义如下:
| 类型 | 签名 |
| -------------------- | ---- |
| char(字符) | c |
| int/short/char(数字) | i |
| char * (字符串) | s |
| pointer | p |
声明命令时,在最后添加一个参数 `.data.cmd.signature = "ics"` 即可,比如:
```c
void shellFuncSignatureTest(int a, char *b, char c)
{
printf("a = %d, b = %s, c = %c\r\n", a, b, c);
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
funcSignatureTest, shellFuncSignatureTest, test function signature, .data.cmd.signature = "isc");
```
## 权限系统说明
letter shell 3.x的权限管理同用户定义紧密相关letter shell 3.x使用8个bit位表示命令权限当用户和命令的权限按位与为真或者命令权限为0时表示该用户拥有此命令的权限可以调用该命令
@ -566,7 +599,6 @@ python shellTools.py project
letter shell 3.x提供了一个x86的demo可以直接编译运行其中包含了一条按键键值测试命令可以测试按键键值用于快捷键的定义编译运行方法如下
```sh
mv src/shell_cfg.h src/shell_cfg.h.bak
cd demo/x86-gcc/
cmake .
make

View File

@ -1,10 +1,15 @@
add_definitions(-DSHELL_CFG_USER="shell_cfg_user.h")
idf_component_register(
SRCS "src/shell.c"
"src/shell_ext.c"
"shell_port.c"
INCLUDE_DIRS "./"
"./src"
REQUIRES
driver
LDFRAGMENTS "shell.lf"
)
target_compile_options(${COMPONENT_LIB} PRIVATE -DSHELL_CFG_USER="shell_cfg_user.h")

View File

@ -11,3 +11,5 @@
- `esp-idf` 编译系统会忽略 `__attribute__((used))` 声明,所以仅仅作为命令定义的函数不会被包含在编译出来的固件里面,只有被代码引用的函数会被编译进去
- 此 demo 包含链接使用的 `.lf` 文件,在使用这个文件的情况下不需要修改 `esp-idf` 中的 `ld` 文件
- 如果使用 overlay 的方式配置 shell建议在主 CMakeList.txt 后面添加 `idf_build_set_property(COMPILE_OPTIONS "-DSHELL_CFG_USER=\"shell_cfg_user.h\"" APPEND)`

View File

@ -34,5 +34,7 @@ target_include_directories(LetterShell PUBLIC
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T shell.lds")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} \
-T \"${CMAKE_CURRENT_SOURCE_DIR}/shell.lds\" \
-Wl,-Map=LetterShell.map")
set(CMAKE_CXX_FLAGS "${CAMKE_CXX_FLAGS} -std=c++11 -pthread")

View File

@ -77,6 +77,13 @@ SECTIONS
. = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)));
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
. = ALIGN(32);
.shell_command ALIGN(4) :
{
_shell_command_start = .;
KEEP (*(shellCommand))
_shell_command_end = .;
}
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
@ -183,12 +190,7 @@ SECTIONS
. = ALIGN(64 / 8);
. = SEGMENT_START("ldata-segment", .);
.lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
{
*(.lrodata .lrodata.* .gnu.linkonce.lr.*)
_shell_command_start = .;
KEEP (*(shellCommand))
_shell_command_end = .;
}
{ *(.lrodata .lrodata.* .gnu.linkonce.lr.*) }
.ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
{
*(.ldata .ldata.* .gnu.linkonce.l.*)

View File

@ -66,4 +66,21 @@ unsigned int userGetTick();
*/
#define SHELL_FREE(obj) free(obj)
/**
* @brief 使
* 使shell
*
*/
#define SHELL_USING_FUNC_SIGNATURE 1
/**
* @brief
* `SHELL_COMMAND_FILL_BYTES`
*
* 使 shell
* sizeof
* 使 shell
*/
#define SHELL_COMMAND_FILL_BYTES 24
#endif

View File

@ -272,3 +272,12 @@ void systemPassthrough(char *data, unsigned short len)
system(data);
}
SHELL_EXPORT_PASSTROUGH(SHELL_CMD_PERMISSION(0), system, system>>\x20, systemPassthrough, passthrough for system command);
#if SHELL_USING_FUNC_SIGNATURE == 1
void shellFuncSignatureTest(int a, char *b, char c)
{
printf("a = %d, b = %s, c = %c\r\n", a, b, c);
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
funcSignatureTest, shellFuncSignatureTest, test function signature, .data.cmd.signature = "isc");
#endif

View File

@ -25,6 +25,12 @@ typedef struct shell_command_cpp_cmd
const char *name; /**< 命令名 */
int (*function)(); /**< 命令执行函数 */
const char *desc; /**< 命令描述 */
#if SHELL_USING_FUNC_SIGNATURE == 1
const char *signature; /**< 函数签名 */
#endif
#if SHELL_COMMAND_FILL_BYTES != 0
char fill[SHELL_COMMAND_FILL_BYTES]; /**< 填充字节 */
#endif
} ShellCommandCppCmd;
/**
@ -36,6 +42,12 @@ typedef struct shell_command_cpp_var
const char *name; /**< 变量名 */
void *value; /**< 变量值 */
const char *desc; /**< 变量描述 */
#if SHELL_USING_FUNC_SIGNATURE == 1
void *unused; /**< 未使用成员,需要保持和 ShellCommandCppCmd 大小一致 */
#endif
#if SHELL_COMMAND_FILL_BYTES != 0
char fill[SHELL_COMMAND_FILL_BYTES]; /**< 填充字节 */
#endif
} ShellCommandCppVar;
/**
@ -47,6 +59,12 @@ typedef struct shell_command_cpp_user
const char *name; /**< 用户名 */
const char *password; /**< 用户密码 */
const char *desc; /**< 用户描述 */
#if SHELL_USING_FUNC_SIGNATURE == 1
void *unused; /**< 未使用成员,需要保持和 ShellCommandCppCmd 大小一致 */
#endif
#if SHELL_COMMAND_FILL_BYTES != 0
char fill[SHELL_COMMAND_FILL_BYTES]; /**< 填充字节 */
#endif
} ShellCommandCppUser;
/**
@ -58,6 +76,12 @@ typedef struct shell_command_cpp_key
int value; /**< 按键键值 */
void (*function)(Shell *); /**< 按键执行函数 */
const char *desc; /**< 按键描述 */
#if SHELL_USING_FUNC_SIGNATURE == 1
void *unused; /**< 未使用成员,需要保持和 ShellCommandCppCmd 大小一致 */
#endif
#if SHELL_COMMAND_FILL_BYTES != 0
char fill[SHELL_COMMAND_FILL_BYTES]; /**< 填充字节 */
#endif
} ShellCommandCppKey;
#if SHELL_USING_CMD_EXPORT == 1
@ -70,8 +94,9 @@ typedef struct shell_command_cpp_key
* @param _name
* @param _func
* @param _desc
* @param ...
*/
#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc) \
#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc, ...) \
const char shellCmd##_name[] = #_name; \
const char shellDesc##_name[] = #_desc; \
extern "C" SHELL_USED const ShellCommandCppCmd \
@ -80,7 +105,8 @@ typedef struct shell_command_cpp_key
_attr, \
shellCmd##_name, \
(int (*)())_func, \
shellDesc##_name \
shellDesc##_name, \
##__VA_ARGS__ \
}
#undef SHELL_EXPORT_VAR

View File

@ -1957,7 +1957,7 @@ int shellExecute(int argc, char *argv[])
Shell *shell = shellGetCurrent();
if (shell && argc >= 2)
{
int (*func)() = (int (*)())shellExtParsePara(shell, argv[1]);
int (*func)() = (int (*)())shellExtParsePara(shell, argv[1], NULL);
ShellCommand command = {
.attr.value = SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)
|SHELL_CMD_DISABLE_RETURN,

View File

@ -14,7 +14,7 @@
#include "shell_cfg.h"
#define SHELL_VERSION "3.1.2" /**< 版本号 */
#define SHELL_VERSION "3.2.0" /**< 版本号 */
/**
@ -128,8 +128,9 @@
* @param _name
* @param _func
* @param _desc
* @param ...
*/
#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc) \
#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc, ...) \
const char shellCmd##_name[] = #_name; \
const char shellDesc##_name[] = #_desc; \
SHELL_USED const ShellCommand \
@ -138,7 +139,8 @@
.attr.value = _attr, \
.data.cmd.name = shellCmd##_name, \
.data.cmd.function = (int (*)())_func, \
.data.cmd.desc = shellDesc##_name \
.data.cmd.desc = shellDesc##_name, \
##__VA_ARGS__ \
}
/**
@ -399,26 +401,41 @@ typedef struct shell_command
const char *name; /**< 命令名 */
int (*function)(); /**< 命令执行函数 */
const char *desc; /**< 命令描述 */
#if SHELL_USING_FUNC_SIGNATURE == 1
const char *signature; /**< 函数签名 */
#endif
} cmd; /**< 命令定义 */
struct
{
const char *name; /**< 变量名 */
void *value; /**< 变量值 */
const char *desc; /**< 变量描述 */
#if SHELL_USING_FUNC_SIGNATURE == 1
const char *unsued; /**< 未使用成员 */
#endif
} var; /**< 变量定义 */
struct
{
const char *name; /**< 用户名 */
const char *password; /**< 用户密码 */
const char *desc; /**< 用户描述 */
#if SHELL_USING_FUNC_SIGNATURE == 1
const char *unsued; /**< 未使用成员 */
#endif
} user; /**< 用户定义 */
struct
{
int value; /**< 按键键值 */
void (*function)(Shell *); /**< 按键执行函数 */
const char *desc; /**< 按键描述 */
#if SHELL_USING_FUNC_SIGNATURE == 1
const char *unsued; /**< 未使用成员 */
#endif
} key; /**< 按键定义 */
} data;
#if SHELL_COMMAND_FILL_BYTES != 0
char fill[SHELL_COMMAND_FILL_BYTES]; /**< 填充字节 */
#endif
} ShellCommand;
/**

View File

@ -246,4 +246,25 @@
#define SHELL_LOCK_TIMEOUT 0 * 60 * 1000
#endif /** SHELL_LOCK_TIMEOUT */
#ifndef SHELL_USING_FUNC_SIGNATURE
/**
* @brief 使
* 使shell
*
*/
#define SHELL_USING_FUNC_SIGNATURE 0
#endif /** SHELL_USING_FUNC_SIGNATURE */
#ifndef SHELL_COMMAND_FILL_BYTES
/**
* @brief
* `SHELL_COMMAND_FILL_BYTES`
*
* 使 shell
* sizeof
* 使 shell
*/
#define SHELL_COMMAND_FILL_BYTES 0
#endif /** SHELL_COMMAND_FILL_BYTES */
#endif

View File

@ -12,7 +12,7 @@
#include "shell_cfg.h"
#include "shell.h"
#include "shell_ext.h"
#include "string.h"
extern ShellCommand* shellSeekCommand(Shell *shell,
const char *cmd,
@ -20,6 +20,37 @@ extern ShellCommand* shellSeekCommand(Shell *shell,
unsigned short compareLength);
extern int shellGetVarValue(Shell *shell, ShellCommand *command);
#if SHELL_USING_FUNC_SIGNATURE == 1
/**
* @brief
*
* @param signature
* @param index
* @param type
*
* @return int
*/
static int shellGetNextArgType(const char *signature, int index, char *type)
{
char *p = signature + index;
if (*p == 'L')
{
while (*p != ';' && *p != 0)
{
*type++ = *p++;
index++;
}
}
else
{
*type++ = *p;
index++;
}
*type = '\0';
return index;
}
#endif
/**
* @brief
*
@ -92,7 +123,7 @@ static char shellExtToNum(char code)
*/
static char shellExtParseChar(char *string)
{
char *p = string + 1;
char *p = (*string == '\'') ? (string + 1) : string;
char value = 0;
if (*p == '\\')
@ -268,8 +299,10 @@ static unsigned int shellExtParseVar(Shell *shell, char *var)
* @param string
* @return unsigned int
*/
unsigned int shellExtParsePara(Shell *shell, char *string)
unsigned int shellExtParsePara(Shell *shell, char *string, char *type)
{
if (type == NULL || (*string == '$' && *(string + 1)))
{
if (*string == '\'' && *(string + 1))
{
return (unsigned int)shellExtParseChar(string);
@ -286,6 +319,24 @@ unsigned int shellExtParsePara(Shell *shell, char *string)
{
return (unsigned int)shellExtParseString(string);
}
}
else
{
if (strcmp("c", type) == 0)
{
return (unsigned int)shellExtParseChar(string);
}
else if (strcmp("i", type) == 0
|| strcmp("f", type) == 0
|| strcmp("p", type) == 0)
{
return (unsigned int)shellExtParseNumber(string);
}
else if (strcmp("s", type) == 0)
{
return (unsigned int)shellExtParseString(string);
}
}
return 0;
}
@ -304,9 +355,24 @@ int shellExtRun(Shell *shell, ShellCommand *command, int argc, char *argv[])
unsigned int params[SHELL_PARAMETER_MAX_NUMBER] = {0};
int paramNum = command->attr.attrs.paramNum > (argc - 1) ?
command->attr.attrs.paramNum : (argc - 1);
#if SHELL_USING_FUNC_SIGNATURE == 1
char type[8];
int index = 0;
#endif
for (int i = 0; i < argc - 1; i++)
{
params[i] = shellExtParsePara(shell, argv[i + 1]);
#if SHELL_USING_FUNC_SIGNATURE == 1
if (command->data.cmd.signature != NULL) {
index = shellGetNextArgType(command->data.cmd.signature, index, type);
params[i] = shellExtParsePara(shell, argv[i + 1], type);
}
else
{
params[i] = shellExtParsePara(shell, argv[i + 1], NULL);
}
#else
params[i] = shellExtParsePara(shell, argv[i + 1], NULL);
#endif
}
switch (paramNum)
{

View File

@ -27,7 +27,7 @@ typedef enum
NUM_TYPE_FLOAT /**< 浮点型 */
} ShellNumType;
unsigned int shellExtParsePara(Shell *shell, char *string);
unsigned int shellExtParsePara(Shell *shell, char *string, char *type);
int shellExtRun(Shell *shell, ShellCommand *command, int argc, char *argv[]);
#endif