From 912bbf58517425eb97716264f532d10ec5cc610d Mon Sep 17 00:00:00 2001 From: Letter Date: Fri, 17 Jan 2020 17:51:32 +0800 Subject: [PATCH 1/2] shell 3.0 --- README.md | 493 +++++---- demo/stm32-freertos/shell_cfg.h | 119 +++ demo/stm32-freertos/shell_port.c | 62 ++ demo/stm32-freertos/shell_port.h | 21 + doc/img/shell_info.png | Bin 0 -> 5875 bytes shell.c | 1304 ----------------------- shell.h | 363 ------- src/shell.c | 1653 ++++++++++++++++++++++++++++++ src/shell.h | 327 ++++++ shell_cfg.h => src/shell_cfg.h | 63 +- src/shell_cmd_list.c | 98 ++ shell_ext.c => src/shell_ext.c | 76 +- shell_ext.h => src/shell_ext.h | 8 +- 13 files changed, 2594 insertions(+), 1993 deletions(-) create mode 100644 demo/stm32-freertos/shell_cfg.h create mode 100644 demo/stm32-freertos/shell_port.c create mode 100644 demo/stm32-freertos/shell_port.h create mode 100644 doc/img/shell_info.png delete mode 100644 shell.c delete mode 100644 shell.h create mode 100644 src/shell.c create mode 100644 src/shell.h rename shell_cfg.h => src/shell_cfg.h (57%) create mode 100644 src/shell_cmd_list.c rename shell_ext.c => src/shell_ext.c (66%) rename shell_ext.h => src/shell_ext.h (80%) diff --git a/README.md b/README.md index 8ff192d..93929c5 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,51 @@ -# letter shell +# letter shell 3.0 -![version](https://img.shields.io/badge/version-2.0.8-brightgreen.svg) -![build](https://img.shields.io/badge/build-2020.01.14-brightgreen.svg) +![version](https://img.shields.io/badge/version-3.0.0_beta1-brightgreen.svg) +![build](https://img.shields.io/badge/build-2020.01.17-brightgreen.svg) ![build](https://img.shields.io/badge/license-MIT-brightgreen.svg) -一个体积极小的嵌入式shell +一个功能强大的嵌入式shell -- [letter shell](#letter-shell) - - [功能](#%e5%8a%9f%e8%83%bd) - - [移植说明](#%e7%a7%bb%e6%a4%8d%e8%af%b4%e6%98%8e) - - [使用方式](#%e4%bd%bf%e7%94%a8%e6%96%b9%e5%bc%8f) - - [函数定义](#%e5%87%bd%e6%95%b0%e5%ae%9a%e4%b9%89) - - [main函数形式](#main%e5%87%bd%e6%95%b0%e5%bd%a2%e5%bc%8f) - - [普通C函数形式](#%e6%99%ae%e9%80%9ac%e5%87%bd%e6%95%b0%e5%bd%a2%e5%bc%8f) - - [在函数中获取当前shell对象](#%e5%9c%a8%e5%87%bd%e6%95%b0%e4%b8%ad%e8%8e%b7%e5%8f%96%e5%bd%93%e5%89%8dshell%e5%af%b9%e8%b1%a1) - - [命令定义](#%e5%91%bd%e4%bb%a4%e5%ae%9a%e4%b9%89) - - [命令导出方式](#%e5%91%bd%e4%bb%a4%e5%af%bc%e5%87%ba%e6%96%b9%e5%bc%8f) - - [命令表方式](#%e5%91%bd%e4%bb%a4%e8%a1%a8%e6%96%b9%e5%bc%8f) - - [组合按键](#%e7%bb%84%e5%90%88%e6%8c%89%e9%94%ae) - - [shell变量](#shell%e5%8f%98%e9%87%8f) - - [导出变量](#%e5%af%bc%e5%87%ba%e5%8f%98%e9%87%8f) - - [读取变量](#%e8%af%bb%e5%8f%96%e5%8f%98%e9%87%8f) - - [修改变量](#%e4%bf%ae%e6%94%b9%e5%8f%98%e9%87%8f) - - [变量作为命令参数](#%e5%8f%98%e9%87%8f%e4%bd%9c%e4%b8%ba%e5%91%bd%e4%bb%a4%e5%8f%82%e6%95%b0) - - [shell密码](#shell%e5%af%86%e7%a0%81) - - [建议终端软件](#%e5%bb%ba%e8%ae%ae%e7%bb%88%e7%ab%af%e8%bd%af%e4%bb%b6) - - [更新日志](#%e6%9b%b4%e6%96%b0%e6%97%a5%e5%bf%97) +![shell_info.png](doc/img/shell_info.png) + +- [letter shell 3.0](#letter-shell-30) + - [简介](#简介) + - [功能](#功能) + - [移植说明](#移植说明) + - [使用方式](#使用方式) + - [函数定义](#函数定义) + - [main函数形式](#main函数形式) + - [普通C函数形式](#普通c函数形式) + - [在函数中获取当前shell对象](#在函数中获取当前shell对象) + - [命令定义](#命令定义) + - [定义方式](#定义方式) + - [定义宏说明](#定义宏说明) + - [命令属性字段说明](#命令属性字段说明) + - [权限系统说明](#权限系统说明) + - [建议终端软件](#建议终端软件) + +## 简介 + +[letter shell 3.0](https://github.com/NevermindZZT/letter-shell/tree/shell3.0)是一个C语言编写的,可以嵌入在程序中的嵌入式shell,主要面向嵌入式设备,以C语言函数为运行单位,可以通过命令行调用,运行程序中的函数 + +相对2.x版本,letter shell 3.0增加了用户管理,权限管理,后续会增加对文件系统的支持 + +此外3.0版本修改了命令格式和定义,2.x版本的工程需要经过简单的修改才能完成迁移 ## 功能 -- 命令自动补全,使用tab键补全命令 -- 命令长帮助,使用help [command]显示命令长帮助 -- 长帮助补全,输入命令后双击tab键补全命令长帮助指令 -- 快捷键,支持使用Ctrl + A~Z组合按键直接调用函数 -- shell变量,支持在shell中查看和修改变量值,支持变量作为命令参数 -- 登录密码,支持在shell中使用登录密码,支持超时自动锁定 +- 命令自动补全 +- 快捷键功能定义 +- 命令权限管理 +- 用户管理 +- 变量支持 ## 移植说明 1. 定义shell对象 ```C - SHELL_TypeDef shell; + Shell shell; ``` 2. 定义shell读,写函数,函数原型如下 @@ -65,15 +69,21 @@ typedef void (*shellWrite)(const char); ``` -3. 调用shellInit进行初始化 +3. 申请一片缓冲区 + + ```C + char shellBuffer[512]; + ``` + +4. 调用shellInit进行初始化 ```C shell.read = shellRead; shell.write = shellWrite; - shellInit(&shell); + shellInit(&shell, shellBuffer, 512); ``` -4. 调用(建立)shell任务 +5. 调用(建立)shell任务 对于运行在操作系统的情况,建立`shellTask`任务(确保sell_cfg.h中的配置无误),任务参数为shell对象 @@ -81,54 +91,53 @@ OsTaskCreate(shellTask, &shell, ...); ``` - 对于裸机环境,在主循环中调用`shellTask`,或者在接收到数据时,调用`shellInput` + 对于裸机环境,在主循环中调用`shellTask`,或者在接收到数据时,调用`shellHandler` -5. 说明 +6. 说明 - - 对于中断方式使用shell,不用定义`shell->read`,但需要在中断中调用`shellInput` - - 对于在无操作系统环境下,可以使用查询的方式,使能```SHELL_UISNG_TASK```,然后在循环中不断调用shellTask - - 对于使用操作系统的情况,使能```SHELL_USING_TASK```和```SHEHLL_TASK_WHILE```宏,然后创建shellTask任务 - - 打印函数返回值,使能```SHELL_DISPLAY_RETURN```宏,返回值均作为整型数据打印 + - 对于中断方式使用shell,不用定义`shell->read`,但需要在中断中调用`shellHandler` + - 对于使用操作系统的情况,使能```SHEHLL_TASK_WHILE```宏,然后创建shellTask任务 -6. 其他配置 +7. 其他配置 - - 定义宏```SHELL_GET_TICK()```为获取系统tick函数,使能tab双击操作,用户长帮助补全 + - 定义宏`SHELL_GET_TICK()`为获取系统tick函数,使能tab双击操作,用户长帮助补全 -7. 配置宏 +8. 配置宏 shell_cfg.h文件中包含了所有用于配置shell的宏,在使用前,需要根据需要进行配置 - | 宏 | 意义 | - | -------------------------- | ------------------------------ | - | SHELL_USING_TASK | 是否使用默认shell任务 | - | SHELL_USING_CMD_EXPORT | 是否使用命令导出方式 | - | SHELL_DISPLAY_RETURN | 是否显示命令调用函数返回值 | - | SHELL_TASK_WHILE | 是否使用默认shell任务while循环 | - | SHELL_AUTO_PRASE | 是否使用shell参数自动解析 | - | SHELL_LONG_HELP | 是否使用shell长帮助 | - | SHELL_COMMAND_MAX_LENGTH | shell命令最大长度 | - | SHELL_PARAMETER_MAX_NUMBER | shell命令参数最大数量 | - | SHELL_HISTORY_MAX_NUMBER | 历史命令记录数量 | - | SHELL_DOUBLE_CLICK_TIME | 双击间隔(ms) | - | SHELL_GET_TICK() | 获取系统时间(ms) | - | SHELL_DEFAULT_COMMAND | shell默认提示符 | - | SHELL_MAX_NUMBER | 管理的最大shell数量 | - | SHELL_USING_AUTH | 是否使用密码功能 | - | SHELL_USER_PASSWORD | 用户密码 | - | SHELL_LOCK_TIMEOUT | shell自动锁定超时 | + | 宏 | 意义 | + | --------------------------- | ------------------------------ | + | SHELL_TASK_WHILE | 是否使用默认shell任务while循环 | + | SHELL_USING_CMD_EXPORT | 是否使用命令导出方式 | + | SHELL_HELP_LIST_USER | 是否在输入命令列表中列出用户 | + | SHELL_HELP_LIST_VAR | 是否在输入命令列表中列出变量 | + | SHELL_HELP_LIST_KEY | 是否在输入命令列表中列出按键 | + | SHELL_ENTER_LF | 使用LF作为命令行回车触发 | + | SHELL_ENTER_CR | 使用CR作为命令行回车触发 | + | SHELL_ENTER_CRLF | 使用CRLF作为命令行回车触发 | + | SHELL_COMMAND_MAX_LENGTH | shell命令最大长度 | + | SHELL_PARAMETER_MAX_NUMBER | shell命令参数最大数量 | + | SHELL_HISTORY_MAX_NUMBER | 历史命令记录数量 | + | SHELL_DOUBLE_CLICK_TIME | 双击间隔(ms) | + | SHELL_MAX_NUMBER | 管理的最大shell数量 | + | SHELL_GET_TICK() | 获取系统时间(ms) | + | SHELL_DEFAULT_USER | shell默认用户 | + | SHELL_DEFAULT_USER_PASSWORD | 默认用户密码 | + | SHELL_LOCK_TIMEOUT | shell自动锁定超时 | ## 使用方式 ### 函数定义 -letter shell 支持两种形式的函数定义方式,形如main函数定义的```func(int argc, char *agrv[])```以及形如普通C函数的定义```func(int i, char *str, ...)```,这两种方式目前不可共存,只能选择其中的一种,通过宏```SHELL_AUTO_PRASE```选择 +letter shell 3.0同时支持两种形式的函数定义方式,形如main函数定义的`func(int argc, char *agrv[])`以及形如普通C函数的定义`func(int i, char *str, ...)`,两种函数定义方式适用于不同的场景 #### main函数形式 使用此方式,一个函数定义的例子如下: ```C -func(int argc, char *agrv[]) +int func(int argc, char *agrv[]) { printf("%dparameter(s)\r\n", argc); for (char i = 1; i < argc; i++) @@ -136,13 +145,13 @@ func(int argc, char *agrv[]) printf("%s\r\n", argv[i]); } } -SHELL_EXPORT_CMD(func, func, test) +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), func, func, test); ``` 终端调用 ```sh -letter>>func "hello world" +letter:/$ func "hello world" 2 parameter(s) hello world ``` @@ -152,239 +161,195 @@ hello world 使用此方式,shell会自动对参数进行转化处理,目前支持二进制,八进制,十进制,十六进制整形,字符,字符串的自动处理,如果需要其他类型的参数,请使用字符串的方式作为参数,自行进行处理,例子如下: ```C -func(int i, char ch, char *str) +int func(int i, char ch, char *str) { printf("input int: %d, char: %c, string: %s\r\n", i, ch, str); } -SHELL_EXPORT_CMD(func, func, test) +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), func, func, test); ``` 终端调用 ```sh -letter>>func 666 'A' "hello world" +letter:/$ func 666 'A' "hello world" input int: 666, char: A, string: hello world ``` #### 在函数中获取当前shell对象 -shell采取一个静态数组对定义的多个shell进行管理,shell数量可以修改宏```SHELL_MAX_NUMBER```定义(为了不使用动态内存分配,此处通过数据进行管理),从而,在shell执行的函数中,可以调用```shellGetCurrent()```获得当前活动的shell对象,从而可以实现某一个函数在不同的shell对象中发生不同的行为,也可以通过这种方式获得shell对象后,调用```shellDisplay(shell, string)```进行shell的输出 +shell采取一个静态数组对定义的多个shell进行管理,shell数量可以修改宏`SHELL_MAX_NUMBER`定义(为了不使用动态内存分配,此处通过数据进行管理),从而,在shell执行的函数中,可以调用`shellGetCurrent()`获得当前活动的shell对象,从而可以实现某一个函数在不同的shell对象中发生不同的行为,也可以通过这种方式获得shell对象后,调用`shellWriteString(shell, string)`进行shell的输出 -### 命令定义 +## 命令定义 + +letter shell 3.0将可执行的函数命令定义,用户定义,按键定义以及变量定义统一归为命令定义,使用相同的结构储存,查找和执行 + +### 定义方式 letter shell 支持使用命令导出方式和命令表方式进行命令的添加,定义,通过宏```SHELL_USING_CMD_EXPORT```控制 命令导出方式支持keil,IAR(未测试)以及GCC -### 命令导出方式 +1. 命令导出方式 -letter shell 支持在函数体外部,采用定义常量的方式定义命令,例如```SHELL_EXPORT_CMD_EX(help, shellHelp, command help, help [command] --show help info of command);```,或者```SHELL_EXPORT_CMD(help, shellHelp, command help);``` + letter shell 支持在函数体外部,采用定义常量的方式定义命令,例如`SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE (SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,help, shellHelp, show command info\r\nhelp [cmd]);` -对于使用keil进行编译,需要在keil的target option中增加--keep shellCommand*,防止定义的命令被优化掉 + 对于使用keil进行编译,需要在keil的target option中增加--keep shellCommand*,防止定义的命令被优化掉 -使用GCC编译时,需要在ld文件中的只读数据区(建议)添加: + 使用GCC编译时,需要在ld文件中的只读数据区(建议)添加: -```ld -_shell_command_start = .; -KEEP (*(shellCommand)) -_shell_command_end = .; -``` - -### 命令表方式 - -- 当使用其他编译器时,暂时不支持使用类似keil中命令导出的方式,需要在命令表中添加 - - ```C - const SHELL_CommandTypeDef shellDefaultCommandList[] = - { - SHELL_CMD_ITEM_EX(help, shellHelp, command help, help [command] --show help info of command), - }; + ```ld + _shell_command_start = .; + KEEP (*(shellCommand)) + _shell_command_end = .; ``` -- 其中,带有EX的命令导出宏最后一个参数为命令的长帮助信息,在shell中使用help [command]可查看帮助信息,通过shell.h中的SHELL_LONG_HELP宏可设置是否使用此功能 +2. 命令表方式 -### 组合按键 + - 当使用其他暂时不支持使用命令导出方式的编译器时,需要在`shell_cmd_list.c`文件的命令表中添加 -letter shell支持使用Ctrl键加任意字母键的组合按键一键执行操作,使用时,在任意文件定义按键命令表 + ```C + const SHELL_CommandTypeDef shellDefaultCommandList[] = + { + SHELL_CMD_ITEM( + SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN, + help, shellHelp, show command info\r\nhelp [cmd]), + }; + ``` + +### 定义宏说明 + +letter shell 3.0对可执行命令,按键,用户以及变量分别提供了一个宏,用于进行命令定义 + +1. 可执行命令定义 + + 使用宏`SHELL_EXPORT_CMD`定义可执行命令,定义如下 + + ```C + /** + * @brief shell 命令定义 + * + * @param _attr 命令属性 + * @param _name 命令名 + * @param _func 命令函数 + * @param _desc 命令描述 + */ + #define SHELL_EXPORT_CMD(_attr, _name, _func, _desc) \ + const char shellCmd##_name[] = #_name; \ + const char shellDesc##_name[] = #_desc; \ + const ShellCommand \ + shellCommand##_name SECTION("shellCommand") = \ + { \ + .attr.value = _attr, \ + .data.cmd.name = shellCmd##_name, \ + .data.cmd.function = (int (*)())_func, \ + .data.cmd.desc = shellDesc##_name \ + } + ``` + +2. 变量定义 + + 使用宏`SHELL_EXPORT_VAR`定义变量,定义如下 + + ```C + /** + * @brief shell 变量定义 + * + * @param _attr 变量属性 + * @param _name 变量名 + * @param _value 变量值 + * @param _desc 变量描述 + */ + #define SHELL_EXPORT_VAR(_attr, _name, _value, _desc) \ + const char shellCmd##_name[] = #_name; \ + const char shellDesc##_name[] = #_desc; \ + const ShellCommand \ + shellVar##_name SECTION("shellCommand") = \ + { \ + .attr.value = _attr, \ + .data.var.name = shellCmd##_name, \ + .data.var.value = (void *)_value, \ + .data.var.desc = shellDesc##_name \ + } + ``` + +3. 用户定义 + + 使用宏`SHELL_EXPORT_USER`定义用户,定义如下 + + ```C + /** + * @brief shell 用户定义 + * + * @param _attr 用户属性 + * @param _name 用户名 + * @param _password 用户密码 + * @param _desc 用户描述 + */ + #define SHELL_EXPORT_USER(_attr, _name, _password, _desc) \ + const char shellCmd##_name[] = #_name; \ + const char shellPassword##_name[] = #_password; \ + const char shellDesc##_name[] = #_desc; \ + const ShellCommand \ + shellUser##_name SECTION("shellCommand") = \ + { \ + .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \ + .data.user.name = shellCmd##_name, \ + .data.user.password = shellPassword##_name, \ + .data.user.desc = shellDesc##_name \ + } + ``` + +4. 按键定义 + + 使用宏`SHELL_EXPORT_KEY`定义按键,定义如下 + + ```C + /** + * @brief shell 按键定义 + * + * @param _attr 按键属性 + * @param _value 按键键值 + * @param _func 按键函数 + * @param _desc 按键描述 + */ + #define SHELL_EXPORT_KEY(_attr, _value, _func, _desc) \ + const char shellDesc##_value[] = #_desc; \ + const ShellCommand \ + shellKey##_value SECTION("shellCommand") = \ + { \ + .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \ + .data.key.value = _value, \ + .data.key.function = (void (*)(Shell *))_func, \ + .data.key.desc = shellDesc##_value \ + } + ``` + + 按键键值为在终端输入按键会发送的字符串序列,以大端模式表示,比如在SecureCRT中断,按下Tab键,会发送0x0B,则这个按键的键值为0x0B000000,如果按下方向上,会依次发送0x1B, 0x5B, 0x41, 则这个键的键值为0x1B5B4100 + +### 命令属性字段说明 + +在命令定义中,有一个`attr`字段,表示该命令的属性,具体定义为 ```C -SHELL_KeyFunctionDef keyFuncList[] = +union { - {SHELL_KEY_CTRL_T, switchUlog} -}; + struct + { + unsigned char permission : 8; /**< command权限 */ + ShellCommandType type : 4; /**< command类型 */ + unsigned char enableUnchecked : 1; /**< 在未校验密码的情况下可用 */ + unsigned char disableReturn : 1; /**< 禁用返回值输出 */ + } attrs; + int value; +} attr; ``` -然后使用`shellSetKeyFuncList`进行注册 +在定义命令时,需要给定这些值,可以通过宏`SHELL_CMD_PERMISSION(permission)`, `SHELL_CMD_TYPE(type)`, `SHELL_CMD_ENABLE_UNCHECKED`, `SHELL_CMD_DISABLE_RETURN`快速声明 -```C - shellSetKeyFuncList(&shell, keyFuncList, sizeof(keyFuncList) / sizeof(SHELL_KeyFunctionDef)); -``` +### 权限系统说明 -### shell变量 - -letter shell支持shell变量,通过导出变量,将变量进行注册,可以在shell中读取,修改变量的值,可以将变量作为参数传递给shell命令 - -使用时,在shell_cfg.h文件中将`SHELL_USING_VAR`修改为1 - -执行`vars`命令查看所有变量 - -#### 导出变量 - -使用变量导出方式时,通过宏`SHELL_EXPORT_VAR_INT`,`SHELL_EXPORT_VAR_SHORT`,`SHELL_EXPORT_VAR_CHAR`,`SHELL_EXPORT_VAR_POINTER`导出变量,例如: - -```C -SHELL_EXPORT_VAR_INT(testVar1, testVar1, var for test); -``` - -使用变量表方式时,定义一个命令表,并调用`shellSetVariableList`进行注册,参考命令导出 - -#### 读取变量 - -shell变量使用`$[var]`的方式读取,直接在命令行输入即可,例如: - -```sh -letter>>$testVar1 -testVar1 = 100, 0x00000064 -``` - -#### 修改变量 - -使用`setVar`命令修改变量,例如: - -```sh -letter>>setVar testVar1 200 -testVar1 = 200, 0x000000c8 -``` - -#### 变量作为命令参数 - -直接使用`$[var]`即可将变量的值作为参数传递给shell命令,例如: - -```sh -letter>>getVar $testVar1 -``` - -### shell密码 - -letter shell支持shell密码,支持在一定时间shell无操作时自动锁定 - -使能宏`SHELL_USING_AUTH`开启shell密码功能,同时修改宏`SHELL_USER_PASSWORD`定义shell密码 - -在使用密码,并且宏`SHELL_GET_TICK()`有有效定义后,可以使用shell超时自动锁定,通过宏`SHELL_LOCK_TIMEOUT`设置超时时长,当此宏设为0时,禁用超时自动锁定功能 +letter shell 3.0的权限管理同用户定义紧密相关,letter shell 3.0使用8个bit位表示命令权限,当用户和命令的权限按位与为真,或者命令权限为0时,表示该用户拥有此命令的权限,可以调用改命令 ### 建议终端软件 -- 对于基于串口移植,letter shell建议使用secureCRT软件,letter shell中的相关按键映射都是按照secureCRT进行设计的,使用其他串口软件可能会出现某些功能无法使用的情况 - -## 更新日志 - -- 2018/4/20 v1.0 - - - 第一版 - -- 2018/4/23 v1.1 - - - 加入对带参命令的支持 - -- 2018/4/25 v1.2 - - - 合并带参函数和不带参函数命令表 - -- 2018/7/10 v1.3 - - - 修复带参命令最后输入空格时传入参数数量出错的问题 - - 修复不带参数调用带参命令时程序跑死的问题,指令处理相关代码优化 - - 加入对于输入超出命令长度的情况的处理措施 - -- 2018/7/11 v1.4 - - - 新增对于方向键的处理,暂时未对方向键添加具体功能 - - 修复单独的空格,制表符等无效操作被识别为指令进行解析,造成程序死机的问题 - - 取消制表符作为参数分隔符的用途 - -- 2018/7/12 v1.5 - - - 新增历史命令的功能,使用上下方向键操作 - - 新增tab键输入最近一条命令 - - 无历史记录时,tab键输入help命令 - - 新增一条用于清屏的默认指令 - -- 2018/7/18 v1.6 - - - 修改tab键功能,加入自动补全 - - 无输入情况下,按下tab输入help命令 - - 有输入情况下,进行自动补全 - -- 2018/7/24 v1.7 - - - 增加SHELL_TypeDef结构体 - - 采用新的命令添加方式,现在可以在任意文件的函数 外部采用宏SHELL_EXPORT_CMD进行命令定义 - -- 2018/7/26 v1.7.1 - - - 修复不使用带参函数(SHELL_USE_PARAMETER = 0)的情况下,无法匹配命令的问题 - - 修复不使用历史命令(SHELL_USE_HISTORY = 0)的情况下,无法使用命令补全的问题 - -- 2018/8/9 v1.8 - - - 新增左右键移动光标功能,方便对输错的命令进行修改使用宏SHELL_ALLOW_SHIFT开启 - - 优化结构体成员命名 - - 对开启移动光标功能后,输入参数过长的情况采用新的处理方式 - -- 2018/11/19 v1.8.3 - - - 修复不使用光标移动功能的时候,输入命令过长时无法正常删除的问题 - - 针对不使用MDK编译,重新加入命令表定义的方式 - - 新增对双引号的识别处理,支持带空格的参数 - -- 2019/01/07 2.0.0 - - - 重构代码,优化逻辑结构,减少内存开销 - - 新增shell扩展模块,支持函数参数自动转化 - - 精简shell可选项 - - 新增多shell支持 - -- 2019/02/20 2.0.1 - - - 新增命令导出方式对于IAR,GCC的支持 - - 新增命令长帮助 - - 新增二进制参数支持 - - 修复bug以及一些优化 - -- 2019/03/29 2.0.2 - - - 新增函数返回值打印 - - 新增shell对象管理,支持在shell调用的函数中获取当前shell对象 - - bug修复 - -- 2019/08/16 2.0.3 - - - 新增一个shell格式化输出函数 - -- 2019/08/20 2.0.4 - - - 新增组合按键功能自定义 - - 新增shell变量 - -- 2019/08/21 2.0.5 - - - 新增shell常量 - - 修复bug以及一些优化 - -- 2019/11/16 2.0.6 - - - 修复历史命令概率性异常的问题 - - 新增shell密码 - - 一些细节优化 - -- 2019/11/30 2.0.7 - - - 新增shell超时自动锁定 - - 修复未验证密码的情况下,仍能查看历史命令,使用tab查看命令表的问题 - - 修复双击tab补全,可能在使用多shell的时候有冲突的问题 - - 细节优化 - -- 2020/01/14 2.0.8 - - - 修复命令名过长时,显示命令列表异常的问题 - - 修复命令被限制在256个的问题 +- 对于基于串口移植,letter shell建议使用secureCRT软件,letter shell中的相关按键映射都是按照secureCRT进行设计的,使用其他串口软件时,可能需要修改键值 diff --git a/demo/stm32-freertos/shell_cfg.h b/demo/stm32-freertos/shell_cfg.h new file mode 100644 index 0000000..30d0f77 --- /dev/null +++ b/demo/stm32-freertos/shell_cfg.h @@ -0,0 +1,119 @@ +/** + * @file shell_cfg.h + * @author Letter (nevermindzzt@gmail.com) + * @brief shell config + * @version 3.0.0 + * @date 2019-12-31 + * + * @copyright (c) 2019 Letter + * + */ + +#ifndef __SHELL_CFG_H__ +#define __SHELL_CFG_H__ + +#include "stm32f4xx_hal.h" + + +/** + * @brief 是否使用默认shell任务while循环,使能宏`SHELL_USING_TASK`后此宏有意义 + * 使能此宏,则`shellTask()`函数会一直循环读取输入,一般使用操作系统建立shell + * 任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用`shellTask()` + */ +#define SHELL_TASK_WHILE 1 + +/** + * @brief 是否使用命令导出方式 + * 使能此宏后,可以使用`SHELL_EXPORT_CMD()`等导出命令 + * 定义shell命令,关闭此宏的情况下,需要使用命令表的方式 + */ +#define SHELL_USING_CMD_EXPORT 1 + +/** + * @brief 是否在输入命令列表中列出用户 + */ +#define SHELL_HELP_LIST_USER 0 + +/** + * @brief 是否在输入命令列表中列出变量 + */ +#define SHELL_HELP_LIST_VAR 0 + +/** + * @brief 是否在输入命令列表中列出按键 + */ +#define SHELL_HELP_LIST_KEY 0 + +/** + * @brief 使用LF作为命令行回车触发 + * 可以和SHELL_ENTER_CR同时开启 + */ +#define SHELL_ENTER_LF 0 + +/** + * @brief 使用CR作为命令行回车触发 + * 可以和SHELL_ENTER_LF同时开启 + */ +#define SHELL_ENTER_CR 0 + +/** + * @brief 使用CRLF作为命令行回车触发 + * 不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同时开启 + */ +#define SHELL_ENTER_CRLF 1 + +/** + * @brief shell命令参数最大数量 + * 包含命令名在内,超过8个参数并且使用了参数自动转换的情况下,需要修改源码 + */ +#define SHELL_PARAMETER_MAX_NUMBER 8 + +/** + * @brief 历史命令记录数量 + */ +#define SHELL_HISTORY_MAX_NUMBER 5 + +/** + * @brief 双击间隔(ms) + * 使能宏`SHELL_LONG_HELP`后此宏生效,定义双击tab补全help的时间间隔 + */ +#define SHELL_DOUBLE_CLICK_TIME 200 + +/** + * @brief 管理的最大shell数量 + */ +#define SHELL_MAX_NUMBER 5 + +/** + * @brief shell格式化输出的缓冲大小 + * 为0时不使用shell格式化输出 + */ +#define SHELL_PRINT_BUFFER 128 + +/** + * @brief 获取系统时间(ms) + * 定义此宏为获取系统Tick,如`HAL_GetTick()` + * @note 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定 + */ +#define SHELL_GET_TICK() HAL_GetTick() + +/** + * @brief shell默认用户 + */ +#define SHELL_DEFAULT_USER "letter" + +/** + * @brief shell默认用户密码 + * 若默认用户不需要密码,设为"" + */ +#define SHELL_DEFAULT_USER_PASSWORD "" + +/** + * @brief shell自动锁定超时 + * 使能`SHELL_USING_AUTH`的情况下生效,超时后会自动重新锁定shell + * 设置为0时关闭自动锁定功能,时间单位为`SHELL_GET_TICK()`单位 + * @note 使用超时锁定必须保证`SHELL_GET_TICK()`有效 + */ +#define SHELL_LOCK_TIMEOUT 0 * 60 * 1000 + +#endif diff --git a/demo/stm32-freertos/shell_port.c b/demo/stm32-freertos/shell_port.c new file mode 100644 index 0000000..261bfbd --- /dev/null +++ b/demo/stm32-freertos/shell_port.c @@ -0,0 +1,62 @@ +/** + * @file shell_port.c + * @author Letter (NevermindZZT@gmail.com) + * @brief + * @version 0.1 + * @date 2019-02-22 + * + * @Copyright (c) 2019 Unicook + * + */ + +#include "shell.h" +#include "serial.h" +#include "stm32f4xx_hal.h" +#include "usart.h" + + +Shell shell; +char shellBuffer[512]; + +/** + * @brief 用户shell写 + * + * @param data 数据 + */ +void userShellWrite(char data) +{ + serialTransmit(&debugSerial, (uint8_t *)&data, 1, 0xFF); +} + + +/** + * @brief 用户shell读 + * + * @param data 数据 + * @return char 状态 + */ +signed char userShellRead(char *data) +{ + if (serialReceive(&debugSerial, (uint8_t *)data, 1, 0) == 1) + { + return 0; + } + else + { + return -1; + } + +} + + +/** + * @brief 用户shell初始化 + * + */ +void userShellInit(void) +{ + shell.write = userShellWrite; + shell.read = userShellRead; + shellInit(&shell, shellBuffer, 512); +} + diff --git a/demo/stm32-freertos/shell_port.h b/demo/stm32-freertos/shell_port.h new file mode 100644 index 0000000..8daa33c --- /dev/null +++ b/demo/stm32-freertos/shell_port.h @@ -0,0 +1,21 @@ +/** + * @file shell_port.h + * @author Letter (NevermindZZT@gmail.com) + * @brief + * @version 0.1 + * @date 2019-02-22 + * + * @Copyright (c) 2019 Unicook + * + */ + +#ifndef __SHELL_PORT_H__ +#define __SHELL_PORT_H__ + +#include "serial.h" +#include "shell.h" + +extern Shell shell; + +void userShellInit(void); +#endif diff --git a/doc/img/shell_info.png b/doc/img/shell_info.png new file mode 100644 index 0000000000000000000000000000000000000000..a5f9369c5bccebccc85437bcecac2d5e8aabe3a3 GIT binary patch literal 5875 zcmc(jcQl-Rl*fle2vH(R5JZhG2%?MLh3IAU5~Bn|%qWu(op|wT(V`2{qW4ZjZ-dbi z6Qau)eT>kmN+KLr#1&%0n%Gw=&9XYgw~A1A;I&zG<4 ze8EoE^kd5a09}-($}^*Yti9!N&>8|ah(e$H;BNXwToTc08=U&7-70LNZPvKkLQC2o zo~dLS%0%yE`*HED9Z#C*qT#!>C4Wes7DEl}nd_{=TI+AqBsZNY2iZY+){%UN59wJC zR(194fw*!bK0?65SsSIO6D%wfNDi3i!!>Tv0^SI!cn|^3ODg~mS?g&5KMNWN-vrH= z0RBoqkOB@3ED03l-S`1t)&HMFt2Qekg2D4VdjeUa?lxIAnbdO{H|!d23F5{k31ms( z&q3}hIAgvP{6l$}5dylRMF{>0UGP(d+Et;!FoQfjox9L+_yS|yrHnf9Z;kVD`oaI{ z(&A`MHNe#PcTd7wX3YjB}$U#}YG{ zUqL(!!N4CZwCtlG5MLwf9yYMHvQ@ODanImMP5G5)5ZU_TtE2n@Ye|r2X=C!#bLxw> zLpi<&&^h=|PkZp(cI5&2$PNrsbCz@1iXwMaPJpjGj|o6u<*UbUSy{$kmYjXWHLW0{ z_H%)p$=a32auOOSi6Dl!KHD?);P^|jqP);##B;B0bj)cf)l2*x?J9+^MT^&72X*_A z(JF4E2VdCA8uK975R%Y9RtM!5Zy zkA&F$$eT;pgK0({d9vk^%aJzc(cHC#O%}KxeXdXwWg$QAQ95mqi#gzx_ur@V*fiKA zFCtmR#IiV*ICLxwi2>bxZ znPUW&yij9;)+I(5zN^BjCiqNMH82v}_($ydpV8AvC+JVQo9r#h%jT2IAKpAhO8*gL~Z^S~eRfoRT4+uKXRJBNtflSM`-o6A* zCG&-LrXf7;yNko`-ehmA<)tqE@*Z1sR^lV}6-)p)uVcE0KWm;}=V85=b=_DaNiDhc z1~k&+jDmM+VMe`}4B#4%*v{@rWI;@PChv`-oa8D~5!pWBaVvNErVe#gY?QboCa;oK zN?p&hR8U$0Y){ij@SCdoBjqep;pIEUOC@9Q(r4p;q_L&c;ib!XX-+B2R^AQk#m6IO zRLuEWGkF0C`3+Fi3cDQq+3Fu@SShtr=?Y%T($=X#c@FmMeT-C|N#HXGKa`6>v`k^* zj{ed<{Ubdtr51Ocz)Q>9<}@fXj=Zj=G~^ zM3yh^>P!&~todN0Xu_jSgBcUa1Gy>Xh`pD847}%Ccxnx$sh=fEWbta$^|iGV8^|5Z{?345sCBZyX5oUVJrAgtN231?8mbB3w z6ViLii4uK=r0OXGDhW{CPDGj3Q8@f1YfK$m>T{_wM-k`@KO@}4DPOow5A(c`yygwU zfNOZ7;6dHziZT@&*X)x#4W>2P{sOJkeD#Gbb4%lYHTo`W8?VIg@h27krtDEWFQukG z;^DUjm5cssOzD#k%lc^L-1Jueop)|<6rZJvy9Wu|J8lCm{)vKO zaOJ<*RNACA{z59}rnG}7)A=X(AFUWx!2$ttGhX@4lj@nJgZ2GHV1BErLimIKbSo*BG;AZX^7W+$J0)U ze$v!^ktQ76-Q1=6@~+=yFu6n;ZqGt)$iu)FodV9N>si|)T{)Z>%SXZ?B|5AUT91gw zfq|EWarV6EVu-)k`NGpuV^L6WqEY3y-Z0P#>bGpi(sIk*wA7AArVzTZp zW3!BO&==dDR6{?LywdsS{UOUgj}P0db1Ye;eVDz(4|CVnw%ooPFeCFCa8MQ_zb_*^ z@2G+?7}goSjPy<-w^0ORYHyKa^ZCnwE3Z0fx!RK_NfTHD^kanWPx(hGxT$H#{*)6Z za`G82cZ)qPNaWrPJC**7a+of{r)+rdxKQ@iRDYjK?Q04 zs^P+LSx?i6)JP$d_l+hylj7E``qxFQym?{gHM_zwZuCn1% zQTSG>_>3~!P(4M*9sJre6Qz9h^C=Spj^Od3x4ExoeYN@mP=|Rob~KqX+0G_SQ;3Yl ze%;@Dq6NEj&<#C<)SC+LbM=Q^f))k_qo_ExVrSD6%S}!TQt2rkIv}fiwNuf z`G~``Vn6nze=8`?Z<&L0NO<9EHFj_=FF8?xmpiLbGXC}Y?)K=}5^O$GSZdxX)2=V| zYV>u6;}5&m&^lQ#G9*{=qB{Nla{T~tMc5C;zSDs;ne4{yTd0Yh*z9qLG6`Fpm0vd@ ze!I3$%IdVE;%V$3JXnuIC2YBKOKiXP$gNt~d-|zBQu5%N@enK~wq!MAUr4n0SM!)i ze&osW!z8IJ_YaA}&h}_7;!Kd7$q5hg`>Ta|On5S#;Hz1GB3Fp3Yk&Rcl5)h{*7tUZ zFXnGvTE@{(s(cv4`?N?@?rnVv!oC)ml|b2|vwhX1^F#|tMpV#>j=i$$hpM&3k7N$I z7nrl+N?;HP)@CtFTpe<7I+e8nwya9#1tMPzK0pRbcb;~A#b(FfSu~XwN@Hs7ti5Zu z!%{0lHr6+jJcMwOzNx(QvoO)Ohs(C~nnE2>p6yHHLg+}v#S1@%a| z3w+i`nB_1n)G7C-j?V2D6C`wskQ0%&W60#F{!@XHU!U5UCwJO25%gta$Z6T{eX7;F z$#0^0?F>0WiOk3bYb{uySVOj)C!Qli$@~8I@?la6!?h9b&Ggp{)sM%+V$r{LpA1Yv z8$b$o8{(vzxVz^!4)49n*QSp1{_W&4-(l9A^n<%tXU+qDcCukTL-0A!?U09ckg6}~ zGo#lhu{Wt%sq%r9>?1ZVcCNv_PiY~bvnMX4}XI$s+*Pr+`*^5 z7egYJoq?DJjZg@<^?aVmi8@ueP=%eVWhH_A5ku7KuY+TKnGo6XGk!un8hjL0If)8V zo&$0~d(zb4FW|}}yWS)QWB}D_8>Au-;zVrLBUgTF-mofnRhpG9YT?#Rd9?y|>I%3R z+-BcAgoR>*lB4|!3Y9x=Mq_umU_MKq>BE0}*CTS?+Q}#MMHp_w2irPYw2cHf9>)?5 z%bSbL^^U(dP=Ajc&g7Tr63ug&1(2HkcHiBq?(&@xefXH}6_u}vTd-Bd`vtyFDDND~ z7FGL6eBxHR#@a4C6213g1164r{f69e7QknAwEPyeObCP3tg`){=wRBVNmv2rE#Mg$ z7_xV<+ELw>mWYvI+FFI)>7U`g&FyCtB_pPkw7ya9W%zka{6p)T^DARqr#5sSW!a`gVh4n^$IQCudmarOidUb%_{N<&*nTq;#LEHm$=ZaW zsj9wz|I$f=Na?b#0^xU!0bSEIYD z=(DB==wd0%mrnc^97Q`UtpP(viMqPIm+*Z`|7KbSjVBt^qyI?+L?Y8a6=RHgrqLKJ}1+8Y!$W;JZup zFuT2hk(EO!4oC+xk^S}AEL8EjTU_Y*)i#-7@?-hkrp;L7N-9^kKtf;!ckGkFw<5>( zJtu4#zY+^6nTpn)o$kapvx7OU1|m!EpWGrVeyZ+32VtR5y$m~mI`49AP<9FX2>c%2$Px z0#U=Y)6SqD%7bB}>%C7oi=D`$QSxhdUEg-dFj39-U~Z`zxJ*jh>r+{AaGIZ@91sWQKl*9!`N zr_|b$6W&!KnS8k%>)z1Th?Giml5&t_32Z6L&epp+KYQ5x?)|%ro70dWi#o>T#S@+n zf-Nuu_IejegtwE9GB6u?2cLO(A8X`nBC(H#qL>Le&C_LU$;=v9Naoo!1y~gVjfST9 zF=KOSHda0Y6DR&~vhEYjA)nfZyC5!i@g8Uj#NqUIoTcNkuwgy(=`S6%-O_n3#D9Es{lM?lDXGi>s5l9ZhZe(t#A6HdY@|YoMtJF+Lxn zPaRd*iYS@5?65|i9i!Se@W{=(CaxD0vklnf`1{vwji<0I!EJ{=R1c8-u$Z5Z3H2LY zMvpoU0&u_#b5sHJi@6Lw^nRCbeU(%Ar8|Z0^3&XdJx-nCo-TQZ#^60 z-veZW;0d4cAQ#py;!zb66*F1T?9L7N>xZ$&UXa2L3ENa;d{QS@(fe&x)Xq`h++5+Y zCLY||ZGNHoJ_tyXp->4$(1OBq8w!9{ps{76s*j{di%hOD+OgR@s!mw1%3JnmUmQtTAg?s{wE)Z z*xpg$$*D@Rc$hPdNDqjUZF&kDlhBe;iZTuBL~;qsR7svtCti7ggQij)@sfRNaxV|?y&6z{gh~CmO7c(l z(l0)ZpNWv?hCjv~5o0pUbV%Iz-}wEO=l-XVrcw~L-BDvt)PUsHxxKB#GYlS#gO2@& zgv~L=0-0vuAUnbA(LNQN$PC7VpzYB=@aT+dT(VVgY7X*h-#O2AJaxJd;AzB6ijT?3{O51v00%>uICcyqi^d_Zx( z6*XVF>Q|AKy5j(-maz0&A_c_E7b8so&x!u?uotV4ora0T{EFGY53B*2s(LDwO17c@ E0&e|vpa1{> literal 0 HcmV?d00001 diff --git a/shell.c b/shell.c deleted file mode 100644 index 1a38e05..0000000 --- a/shell.c +++ /dev/null @@ -1,1304 +0,0 @@ -/** - * @file shell.c - * @author Letter (NevermindZZT@gmail.com) - * @brief letter shell - * @version 2.0.0 - * @date 2018-12-29 - * - * @Copyright (c) 2018 Letter - * - */ - -#include "shell.h" -#include "string.h" -#include "stdio.h" -#include "stdarg.h" - -#if SHELL_AUTO_PRASE == 1 -#include "shell_ext.h" -#endif - -/** - * @brief shell提示信息文本索引 - */ -enum -{ - TEXT_INFO, - TEXT_PWD_HINT, - TEXT_PWD_RIGHT, - TEXT_PWD_ERROR, - TEXT_FUN_LIST, - TEXT_VAR_LIST, - TEXT_CMD_NONE, - TEXT_CMD_TOO_LONG, - TEXT_READ_NOT_DEF, -}; - -/** - * @brief shell提示信息文本 - */ -static const char *shellText[] = -{ - [TEXT_INFO] = "\r\n\r\n" - "+=========================================================+\r\n" - "| (C) COPYRIGHT 2019 Letter |\r\n" - "| Letter shell v"SHELL_VERSION" |\r\n" - "| Build: "__DATE__" "__TIME__" |\r\n" - "+=========================================================+\r\n", - [TEXT_PWD_HINT] = "\r\nPlease input password:", - [TEXT_PWD_RIGHT] = "\r\npassword confirm success.\r\n", - [TEXT_PWD_ERROR] = "\r\npassword confirm failed.\r\n", - [TEXT_FUN_LIST] = "\r\nCOMMAND LIST:\r\n\r\n", - [TEXT_VAR_LIST] = "\r\nVARIABLE LIST:\r\n\r\n", - [TEXT_CMD_NONE] = "Command not found\r\n", - [TEXT_CMD_TOO_LONG] = "\r\nWarnig: Command is too long\r\n", - [TEXT_READ_NOT_DEF] = "error: shell.read must be defined\r\n", -}; - - -static SHELL_TypeDef *shellList[SHELL_MAX_NUMBER] = {NULL}; /**< shell列表 */ - -static void shellAdd(SHELL_TypeDef *shell); -static void shellDisplayItem(SHELL_TypeDef *shell, unsigned short index); - -static void shellEnter(SHELL_TypeDef *shell); -static void shellTab(SHELL_TypeDef *shell); -static void shellBackspace(SHELL_TypeDef *shell); -static void shellAnsiStart(SHELL_TypeDef *shell); - -#if SHELL_USING_VAR == 1 -static void shellDisplayVariable(SHELL_TypeDef *shell, char *var); -void shellListVariables(void); -#endif /** SHELL_USING_VAR == 1 */ - -#if SHELL_USING_CMD_EXPORT != 1 -/** - * @brief shell默认命令表 - * - * @note 当使用命令表方式定义命令的时候,此表才会生效 - * @note 添加命令时,可使用SHELL_CMD_ITEM宏,如SHELL_CMD_ITEM(help, shellHelp, command help) - * @note 可不使用默认命令表,初始化完成之后,可调用shellSetCommandList接口设置命令表 - */ -const SHELL_CommandTypeDef shellDefaultCommandList[] = -{ - SHELL_CMD_ITEM_EX(help, shellHelp, command help, help [command] --show help info of command), -#if SHELL_USING_VAR == 1 - SHELL_CMD_ITEM(vars, shellListVariables, show vars), - SHELL_CMD_ITEM_EX(setVar, shellSetVariable, set var, setVar $[var] [value]), -#endif /** SHELL_USING_VAR == 1 */ - SHELL_CMD_ITEM(cls, shellClear, clear command line), -}; - -#if SHELL_USING_VAR == 1 -/** - * @brief shell默认变量表 - * - */ -const SHELL_VaribaleTypeDef shellDefaultVariableList[] = -{ - -}; -#endif /** SHELL_USING_VAR == 1 */ -#endif /** SHELL_USING_CMD_EXPORT != 1 */ - -/** - * @brief 默认按键响应映射函数表 - * - */ -const SHELL_KeyFunctionDef shellDefaultKeyFunctionList[] = -{ - {SHELL_KEY_LF, shellEnter}, - {SHELL_KEY_CR, shellEnter}, - {SHELL_KEY_TAB, shellTab}, - {SHELL_KEY_BACKSPACE, shellBackspace}, - {SHELL_KEY_DELETE, shellBackspace}, - {SHELL_KEY_ESC, shellAnsiStart}, -}; - - -/** - * @brief shell初始化 - * - * @param shell shell对象 - */ -void shellInit(SHELL_TypeDef *shell) -{ - shell->length = 0; - shell->cursor = 0; - shell->historyCount = 0; - shell->historyFlag = 0; - shell->historyOffset = 0; - shell->status.inputMode = SHELL_IN_NORMAL; - shell->status.isActive = 0; - shell->status.tabFlag = 0; - shell->command = SHELL_DEFAULT_COMMAND; - shellAdd(shell); - -#if SHELL_USING_AUTH == 1 - shell->status.authFlag = 0; - shellDisplay(shell, shellText[TEXT_PWD_HINT]); -#else - shellDisplay(shell, shellText[TEXT_INFO]); - shellDisplay(shell, shell->command); -#endif - -#if SHELL_USING_CMD_EXPORT == 1 - #if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000) - extern const unsigned int shellCommand$$Base; - extern const unsigned int shellCommand$$Limit; - extern const unsigned int shellVariable$$Base; - extern const unsigned int shellVariable$$Limit; - - shell->commandBase = (SHELL_CommandTypeDef *)(&shellCommand$$Base); - shell->commandNumber = ((unsigned int)(&shellCommand$$Limit) - - (unsigned int)(&shellCommand$$Base)) - / sizeof(SHELL_CommandTypeDef); - #if SHELL_USING_VAR == 1 - shell->variableBase = (SHELL_VaribaleTypeDef *)(&shellVariable$$Base); - shell->variableNumber = ((unsigned int)(&shellVariable$$Limit) - - (unsigned int)(&shellVariable$$Base)) - / sizeof(SHELL_VaribaleTypeDef); - #endif /** SHELL_USING_VAR == 1 */ - - #elif defined(__ICCARM__) - shell->commandBase = (SHELL_CommandTypeDef *)(__section_begin("shellCommand")); - shell->commandNumber = ((unsigned int)(__section_end("shellCommand")) - - (unsigned int)(__section_begin("shellCommand"))) - / sizeof(SHELL_CommandTypeDef); - #if SHELL_USING_VAR == 1 - shell->variableBase = (SHELL_VaribaleTypeDef *)(__section_begin("shellVariable")); - shell->variableNumber = ((unsigned int)(__section_end("shellVariable")) - - (unsigned int)(__section_begin("shellVariable"))) - / sizeof(SHELL_VaribaleTypeDef); - #endif /** SHELL_USING_VAR == 1 */ - #elif defined(__GNUC__) - extern const unsigned int _shell_command_start; - extern const unsigned int _shell_command_end; - - shell->commandBase = (SHELL_CommandTypeDef *)(&_shell_command_start); - shell->commandNumber = ((unsigned int)(&_shell_command_end) - - (unsigned int)(&_shell_command_start)) - / sizeof(SHELL_CommandTypeDef); - #if SHELL_USING_VAR == 1 - extern const unsigned int _shell_variable_start; - extern const unsigned int _shell_variable_end; - shell->variableBase = (SHELL_VaribaleTypeDef *)(&_shell_variable_start); - shell->variableNumber = ((unsigned int)(&_shell_variable_end) - - (unsigned int)(&_shell_variable_start)) - / sizeof(SHELL_VaribaleTypeDef); - #endif /** SHELL_USING_VAR == 1 */ - #else - #error not supported compiler, please use command table mode - #endif -#else - shell->commandBase = (SHELL_CommandTypeDef *)shellDefaultCommandList; - shell->commandNumber = sizeof(shellDefaultCommandList) / sizeof(SHELL_CommandTypeDef); - #if SHELL_USING_VAR == 1 - shell->variableBase = (SHELL_VaribaleTypeDef *)shellDefaultVariableList; - shell->variableNumber = sizeof(shellDefaultVariableList) / sizeof(SHELL_VaribaleTypeDef); - #endif /** SHELL_USING_VAR == 1 */ -#endif -} - - -#if SHELL_USING_CMD_EXPORT != 1 -/** - * @brief shell设置命令表 - * - * @param shell shell对象 - * @param base 命令表基址 - * @param size 命令数量 - * - * @note 此接口不可在shellInit之前调用 - * @note 不调用此接口,则使用默认命令表或命令导出形成的命令表(取决于命令定义方式) - */ -void shellSetCommandList(SHELL_TypeDef *shell, SHELL_CommandTypeDef *base, unsigned short size) -{ - shell->commandBase = base; - shell->commandNumber = size; -} - - -#if SHELL_USING_VAR == 1 -/** - * @brief shell设置变量表 - * - * @param shell shell对象 - * @param base 变量表基址 - * @param size 变量数量 - * - * @note 此接口不可在shellInit之前调用 - * @note 不调用此接口,则使用默认命令表或命令导出形成的命令表(取决于命令定义方式) - */ -void shellSetVariableList(SHELL_TypeDef *shell, SHELL_VaribaleTypeDef *base, unsigned short size) -{ - shell->variableBase = base; - shell->variableNumber = size; -} -#endif /** SHELL_USING_VAR == 1 */ -#endif /** SHELL_USING_CMD_EXPORT != 1 */ - - -/** - * @brief shell设置按键响应 - * - * @param shell shell对象 - * @param base 按键响应表基址 - * @param size 按键响应数量 - */ -void shellSetKeyFuncList(SHELL_TypeDef *shell, SHELL_KeyFunctionDef *base, unsigned short size) -{ - shell->keyFuncBase = (int)base; - shell->keyFuncNumber = size; -} - - -/** - * @brief 添加shell到shell列表 - * - * @param shell shell对象 - */ -static void shellAdd(SHELL_TypeDef *shell) -{ - for (short i = 0; i < SHELL_MAX_NUMBER; i++) - { - if (shellList[i] == NULL) - { - shellList[i] = shell; - return; - } - } -} - - -/** - * @brief 获取当前活动shell - * - * @return SHELL_TypeDef* 当前活动shell对象 - */ -SHELL_TypeDef *shellGetCurrent(void) -{ - for (short i = 0; i < SHELL_MAX_NUMBER; i++) - { - if (shellList[i] != NULL && shellList[i]->status.isActive == 1) - { - return shellList[i]; - } - } - return NULL; -} - - -#if SHELL_PRINT_BUFFER > 0 -/** - * @brief shell格式化输出 - * - * @param shell shell对象 - * @param fmt 格式化字符串 - * @param ... 参数 - */ -void shellPrint(SHELL_TypeDef *shell, char *fmt, ...) -{ - char buffer[SHELL_PRINT_BUFFER]; - va_list vargs; - - if (!shell) - { - return; - } - - va_start(vargs, fmt); - vsnprintf(buffer, SHELL_PRINT_BUFFER - 1, fmt, vargs); - va_end(vargs); - - shellDisplay(shell, buffer); -} -#endif - - -/** - * @brief shell显示字符串 - * - * @param shell shell对象 - * @param string 字符串 - * @return unsigned short 字符串长度 - */ -unsigned short shellDisplay(SHELL_TypeDef *shell, const char *string) -{ - unsigned short count = 0; - if (shell->write == NULL) - { - return 0; - } - while(*string) - { - shell->write(*string++); - count ++; - } - return count; -} - - -/** - * @brief shell显示字符 - * - * @param shell shel对象 - * @param data 字符 - */ -static void shellDisplayByte(SHELL_TypeDef *shell, char data) -{ - if (shell->write == NULL) - { - return; - } - shell->write(data); -} - - -#if SHELL_USING_VAR == 1 || SHELL_DISPLAY_RETURN == 1 -/** - * @brief shell显示值 - * - * @param shell shell对象 - * @param value 值 - */ -static void shellDisplayValue(SHELL_TypeDef *shell, int value) -{ - char str[11] = "0000000000"; - unsigned int v = value; - char i = 10; - char tmp; - - if (value < 0) - { - shellDisplay(shell, "-"); - v = -v; - } - while (v) - { - str[--i] = v % 10 + 48; - v /= 10; - } - shellDisplay(shell, str + i - (value == 0)); - v = value; - if (value < 0) - { - v = (unsigned int)value; - } - i = 8; - str[8] = 0; - while (v) - { - tmp = v & 0x0000000F; - str[--i] = (tmp > 9) ? (tmp + 87) : (tmp + 48); - v >>= 4; - } - shellDisplay(shell, ", 0x"); - shellDisplay(shell, str); - shellDisplay(shell, "\r\n"); -} -#endif - - -#if SHELL_DISPLAY_RETURN == 1 -/** - * @brief shell显示函数调用返回值 - * - * @param shell shel对象 - * @param value 值 - */ -static void shellDisplayReturn(SHELL_TypeDef *shell, int value) -{ - shellDisplay(shell, "Return: "); - shellDisplayValue(shell, value); -} -#endif /** SHELL_DISPLAY_RETURN == 1 */ - - -/** - * @brief shell字符串复制 - * - * @param dest 目标字符串 - * @param src 源字符串 - * @return unsigned short 字符串长度 - */ -static unsigned short shellStringCopy(char *dest, char* src) -{ - unsigned short count = 0; - while (*(src + count)) - { - *(dest + count) = *(src + count); - count++; - } - *(dest + count) = 0; - return count; -} - - -/** - * @brief shell字符串比较 - * - * @param dest 目标字符串 - * @param src 源字符串 - * @return unsigned short 匹配长度 - */ -static unsigned short shellStringCompare(char* dest, char *src) -{ - unsigned short match = 0; - unsigned short i = 0; - - while (*(dest +i) && *(src + i)) - { - if (*(dest + i) != *(src +i)) - { - break; - } - match ++; - i++; - } - return match; -} - - -/** - * @brief shell删除 - * - * @param shell shell对象 - * @param length 删除的长度 - */ -static void shellDelete(SHELL_TypeDef *shell, unsigned short length) -{ - while (length--) - { - shellDisplay(shell, "\b \b"); - } -} - - -/** - * @brief shell清除输入 - * - * @param shell shell对象 - */ -static void shellClearLine(SHELL_TypeDef *shell) -{ - for (short i = shell->length - shell->cursor; i > 0; i--) - { - shellDisplayByte(shell, ' '); - } - shellDelete(shell, shell->length); -} - - -/** - * @brief shell历史记录添加 - * - * @param shell shell对象 - */ -static void shellHistoryAdd(SHELL_TypeDef *shell) -{ - shell->historyOffset = 0; - if (strcmp(shell->history[shell->historyFlag - 1], shell->buffer) == 0) - { - return; - } - if (shellStringCopy(shell->history[shell->historyFlag], shell->buffer) != 0) - { - shell->historyFlag++; - } - if (++shell->historyCount > SHELL_HISTORY_MAX_NUMBER) - { - shell->historyCount = SHELL_HISTORY_MAX_NUMBER; - } - if (shell->historyFlag >= SHELL_HISTORY_MAX_NUMBER) - { - shell->historyFlag = 0; - } -} - - -/** - * @brief shell历史记录查找 - * - * @param shell shell对象 - * @param dir 查找方向 - */ -static void shellHistory(SHELL_TypeDef *shell, unsigned char dir) -{ -#if SHELL_USING_AUTH == 1 - if (!shell->status.authFlag) - { - return; - } -#endif - if (dir == 0) - { - if (shell->historyOffset-- - <= -((shell->historyCount > shell->historyFlag) - ? shell->historyCount : shell->historyFlag)) - { - shell->historyOffset = -((shell->historyCount > shell->historyFlag) - ? shell->historyCount : shell->historyFlag); - } - } - else if (dir == 1) - { - if (++shell->historyOffset > 0) - { - shell->historyOffset = 0; - return; - } - } - else - { - return; - } - shellClearLine(shell); - if (shell->historyOffset == 0) - { - shell->cursor = shell->length = 0; - } - else - { - if ((shell->length = shellStringCopy(shell->buffer, - shell->history[(shell->historyFlag + SHELL_HISTORY_MAX_NUMBER - + shell->historyOffset) % SHELL_HISTORY_MAX_NUMBER])) == 0) - { - return; - } - shell->cursor = shell->length; - shellDisplay(shell, shell->buffer); - } -} - - -/** - * @brief shell回车输入处理 - * - * @param shell shell对象 - */ -static void shellEnter(SHELL_TypeDef *shell) -{ - unsigned char paramCount = 0; - unsigned char quotes = 0; - unsigned char record = 1; - SHELL_CommandTypeDef *base; - unsigned char runFlag = 0; - int returnValue; - (void) returnValue; - -#if SHELL_USING_AUTH == 1 - if(shell->status.authFlag == 0) - { - if((shell->length != strlen(SHELL_USER_PASSWORD)) - ||(strncmp(shell->buffer, SHELL_USER_PASSWORD, strlen(SHELL_USER_PASSWORD)) != 0)) - { - shellDisplay(shell, shellText[TEXT_PWD_ERROR]); - shellDisplay(shell, shellText[TEXT_PWD_HINT]); - } - else - { - shell->status.authFlag = 1; - shellDisplay(shell, shellText[TEXT_PWD_RIGHT]); - shellDisplay(shell, shellText[TEXT_INFO]); - shellDisplay(shell, shell->command); - } - - shell->length = 0; - shell->cursor = 0; - return; - } -#endif - - if (shell->length == 0) - { - shellDisplay(shell, shell->command); - return; - } - - *(shell->buffer + shell->length++) = 0; - shellHistoryAdd(shell); - - for (unsigned short i = 0; i < shell->length; i++) - { - if ((quotes != 0 || - (*(shell->buffer + i) != ' ' && - *(shell->buffer + i) != '\t' && - *(shell->buffer + i) != ',')) && - *(shell->buffer + i) != 0) - { - if (*(shell->buffer + i) == '\"') - { - quotes = quotes ? 0 : 1; - #if SHELL_AUTO_PRASE == 0 - *(shell->buffer + i) = 0; - continue; - #endif - } - if (record == 1) - { - shell->param[paramCount++] = shell->buffer + i; - record = 0; - } - if (*(shell->buffer + i) == '\\' && - *(shell->buffer + i + 1) != 0) - { - i++; - } - } - else - { - *(shell->buffer + i) = 0; - record = 1; - } - } - shell->length = 0; - shell->cursor = 0; - if (paramCount == 0) - { - shellDisplay(shell, shell->command); - return; - } - - shellDisplay(shell, "\r\n"); - base = shell->commandBase; - if (strcmp((const char *)shell->param[0], "help") == 0) - { - shell->status.isActive = 1; - shellHelp(paramCount, shell->param); - shell->status.isActive = 0; - shellDisplay(shell, shell->command); - return; - } -#if SHELL_USING_VAR == 1 - if (shell->param[0][0] == '$') - { - shellDisplayVariable(shell, shell->param[0]); - shellDisplay(shell, shell->command); - return; - } -#endif /** SHELL_USING_VAR == 1 */ - for (unsigned short i = 0; i < shell->commandNumber; i++) - { - if (strcmp((const char *)shell->param[0], (base + i)->name) == 0) - { - runFlag = 1; - shell->status.isActive = 1; - #if SHELL_AUTO_PRASE == 0 - returnValue = (base + i)->function(paramCount, shell->param); - #else - returnValue = shellExtRun((base + i)->function, paramCount, shell->param); - #endif /** SHELL_AUTO_PRASE == 0 */ - shell->status.isActive = 0; - #if SHELL_DISPLAY_RETURN == 1 - shellDisplayReturn(shell, returnValue); - #endif /** SHELL_DISPLAY_RETURN == 1 */ - break; - } - } - if (runFlag == 0) - { - shellDisplay(shell, shellText[TEXT_CMD_NONE]); - } - shellDisplay(shell, shell->command); -} - - -/** - * @brief shell退格输入处理 - * - * @param shell shell对象 - */ -static void shellBackspace(SHELL_TypeDef *shell) -{ - if (shell->length == 0) - { - return; - } - if (shell->cursor == shell->length) - { - shell->length--; - shell->cursor--; - shell->buffer[shell->length] = 0; - shellDelete(shell, 1); - } - else if (shell->cursor > 0) - { - for (short i = 0; i < shell->length - shell->cursor; i++) - { - shell->buffer[shell->cursor + i - 1] = shell->buffer[shell->cursor + i]; - } - shell->length--; - shell->cursor--; - shell->buffer[shell->length] = 0; - shellDisplayByte(shell, '\b'); - for (short i = shell->cursor; i < shell->length; i++) - { - shellDisplayByte(shell, shell->buffer[i]); - } - shellDisplayByte(shell, ' '); - for (short i = shell->length - shell->cursor + 1; i > 0; i--) - { - shellDisplayByte(shell, '\b'); - } - } -} - - -/** - * @brief shell Tab键输入处理 - * - * @param shell shell对象 - */ -static void shellTab(SHELL_TypeDef *shell) -{ - unsigned short maxMatch = SHELL_COMMAND_MAX_LENGTH; - unsigned short lastMatchIndex = 0; - unsigned short matchNum = 0; - unsigned short length; - SHELL_CommandTypeDef *base = shell->commandBase; - -#if SHELL_USING_AUTH == 1 - if (!shell->status.authFlag) - { - return; - } -#endif - - if (shell->length != 0) - { - shell->buffer[shell->length] = 0; - for (short i = 0; i < shell->commandNumber; i++) - { - if (shellStringCompare(shell->buffer, - (char *)(base + i)->name) - == shell->length) - { - if (matchNum != 0) - { - if (matchNum == 1) - { - shellDisplay(shell, "\r\n"); - } - shellDisplayItem(shell, lastMatchIndex); - length = shellStringCompare((char *)(base + lastMatchIndex)->name, - (char *)(base +i)->name); - maxMatch = (maxMatch > length) ? length : maxMatch; - } - lastMatchIndex = i; - matchNum ++; - } - } - - if (matchNum == 0) - { - return; - } - if (matchNum == 1) - { - shellClearLine(shell); - } - if (matchNum != 0) - { - shell->length = shellStringCopy(shell->buffer, - (char *)(base + lastMatchIndex)->name); - } - if (matchNum > 1) - { - shellDisplayItem(shell, lastMatchIndex); - shellDisplay(shell, shell->command); - shell->length = maxMatch; - } - shell->buffer[shell->length] = 0; - shell->cursor = shell->length; - shellDisplay(shell, shell->buffer); - } - else - { - shell->status.isActive = 1; - shellHelp(1, (void *)0); - shell->status.isActive = 0; - shellDisplay(shell, shell->command); - } - -#if SHELL_LONG_HELP == 1 - if (SHELL_GET_TICK()) - { - if (matchNum == 1 - && shell->status.tabFlag == 1 - && SHELL_GET_TICK() - shell->activeTime < SHELL_DOUBLE_CLICK_TIME) - { - shellClearLine(shell); - for (short i = shell->length; i >= 0; i--) - { - shell->buffer[i + 5] = shell->buffer[i]; - } - shellStringCopy(shell->buffer, "help"); - shell->buffer[4] = ' '; - shell->length += 5; - shell->cursor = shell->length; - shellDisplay(shell, shell->buffer); - } - } -#endif /** SHELL_LONG_HELP == 1 */ -} - - -/** - * @brief shell正常按键处理 - * - * @param shell shell对象 - * @param data 输入的数据 - */ -static void shellNormal(SHELL_TypeDef *shell, char data) -{ - if (data == 0) - { - return; - } - if (shell->length < SHELL_COMMAND_MAX_LENGTH - 1) - { - if (shell->length == shell->cursor) - { - shell->buffer[shell->length++] = data; - shell->cursor++; - shellDisplayByte(shell, data); - } - else - { - for (short i = shell->length - shell->cursor; i > 0; i--) - { - shell->buffer[shell->cursor + i] = shell->buffer[shell->cursor + i - 1]; - } - shell->buffer[shell->cursor++] = data; - shell->buffer[++shell->length] = 0; - for (short i = shell->cursor - 1; i < shell->length; i++) - { - shellDisplayByte(shell, shell->buffer[i]); - } - for (short i = shell->length - shell->cursor; i > 0; i--) - { - shellDisplayByte(shell, '\b'); - } - } - } - else - { - shellDisplay(shell, shellText[TEXT_CMD_TOO_LONG]); - shellDisplay(shell, shell->command); - shellDisplay(shell, shell->buffer); - shell->cursor = shell->length; - } -} - - -/** - * @brief shell开始ansi控制序列 - * - * @param shell shell对象 - */ -static void shellAnsiStart(SHELL_TypeDef *shell) -{ - shell->status.inputMode = SHELL_ANSI_ESC; -} - - -/** - * @brief shell ansi控制序列处理 - * - * @param shell shell对象 - * @param data 输入的数据 - */ -void shellAnsi(SHELL_TypeDef *shell, char data) -{ - switch ((unsigned char)(shell->status.inputMode)) - { - case SHELL_ANSI_CSI: - switch (data) - { - case 0x41: /** 方向上键 */ - shellHistory(shell, 0); - break; - - case 0x42: /** 方向下键 */ - shellHistory(shell, 1); - break; - - case 0x43: /** 方向右键 */ - if (shell->cursor < shell->length) - { - shellDisplayByte(shell, shell->buffer[shell->cursor]); - shell->cursor++; - } - break; - - case 0x44: /** 方向左键 */ - if (shell->cursor > 0) - { - shellDisplayByte(shell, '\b'); - shell->cursor--; - } - break; - - default: - break; - } - shell->status.inputMode = SHELL_IN_NORMAL; - break; - - case SHELL_ANSI_ESC: - if (data == 0x5B) - { - shell->status.inputMode = SHELL_ANSI_CSI; - } - else - { - shell->status.inputMode = SHELL_IN_NORMAL; - } - break; - - default: - break; - } -} - - -/** - * @brief shell处理 - * - * @param shell shell对象 - * @param data 输入数据 - */ -void shellHandler(SHELL_TypeDef *shell, char data) -{ -#if SHELL_USING_AUTH == 1 && SHELL_LOCK_TIMEOUT > 0 - if (SHELL_GET_TICK()) - { - if (SHELL_GET_TICK() - shell->activeTime > SHELL_LOCK_TIMEOUT) - { - shell->status.authFlag = 0; - } - } -#endif - if (shell->status.inputMode == SHELL_IN_NORMAL) - { - char keyDefFind = 0; - SHELL_KeyFunctionDef *base = (SHELL_KeyFunctionDef *)shell->keyFuncBase; - - #if SHELL_USING_AUTH == 1 - if (shell->status.authFlag == 1) - { - #endif - for (short i = 0; i < shell->keyFuncNumber; i++) - { - if (base[i].keyCode == data) { - if (base[i].keyFunction) { - base[i].keyFunction(shell); - } - keyDefFind = 1; - } - } - #if SHELL_USING_AUTH == 1 - } - #endif - if (keyDefFind == 0) - { - for (short i = 0; - i < sizeof(shellDefaultKeyFunctionList) / sizeof(SHELL_KeyFunctionDef); - i++) - { - if (shellDefaultKeyFunctionList[i].keyCode == data) { - if (shellDefaultKeyFunctionList[i].keyFunction) { - shellDefaultKeyFunctionList[i].keyFunction(shell); - } - keyDefFind = 1; - } - } - } - if (keyDefFind == 0) - { - shellNormal(shell, data); - } - } - else - { - shellAnsi(shell, data); - } -#if SHELL_LONG_HELP == 1 || (SHELL_USING_AUTH && SHELL_LOCK_TIMEOUT > 0) - if (SHELL_GET_TICK()) - { - shell->activeTime = SHELL_GET_TICK(); - } -#endif -#if SHELL_LONG_HELP == 1 - shell->status.tabFlag = data == '\t' ? 1 : 0; -#endif -} - - -#if SHELL_USING_TASK == 1 -/** - * @brief shell 任务 - * - * @param param shell对象 - * - * @note 使用操作系统时,定义的shell read函数必须是阻塞式的 - * @note 不使用操作系统时,可以通过不断查询的方式使用shell,修改宏SHELL_TASK_WHILE - * 为0,然后在主循环中不断调用此函数 - */ -void shellTask(void *param) -{ - SHELL_TypeDef *shell = (SHELL_TypeDef *)param; - char data; - if (shell->read == NULL) - { - shellDisplay(shell, shellText[TEXT_READ_NOT_DEF]); - while (1) ; - } -#if SHELL_TASK_WHILE == 1 - while (1) - { -#endif - if (shell->read(&data) == 0) - { - shellInput(shell, data); - } -#if SHELL_TASK_WHILE == 1 - } -#endif -} -#endif - - -#if SHELL_USING_VAR == 1 -/** - * @brief shell获取变量 - * - * @param shell shell对象 - * @param var 参数 - * @return int 变量值 - */ -int shellGetVariable(SHELL_TypeDef *shell, char *var) -{ - SHELL_VaribaleTypeDef *base = shell->variableBase; - int value = 0; - - if (var[0] == '$') - { - var++; - } - - for (short i = 0; i < shell->variableNumber; i++) - { - if (strcmp((const char *)var, (const char *)(base + i)->name) == 0) - { - switch ((base + i)->type) - { - case SHELL_VAR_INT: - value = *((int *)((base + i)->value)); - break; - case SHELL_VAR_SHORT: - value = *((short *)((base + i)->value)); - break; - case SHELL_VAR_CHAR: - value = *((char *)((base + i)->value)); - break; - case SHELL_VAR_POINTER: - case SHELL_VAL: - value = (int)((base + i)->value); - default: - break; - } - } - } - return value; -} - - -/** - * @brief shell设置变量值 - * - * @param var 变量 - * @param value 值 - */ -void shellSetVariable(char *var, int value) -{ - SHELL_TypeDef *shell = shellGetCurrent(); - char isVarFind = 0; - if (!shell) - { - return; - } - SHELL_VaribaleTypeDef *base = shell->variableBase; - - for (short i = 0; i < shell->variableNumber; i++) - { - if (strcmp((const char *)var, (const char *)(base + i)->name) == 0) - { - switch ((base + i)->type) - { - case SHELL_VAR_INT: - *((int *)((base + i)->value)) = value; - break; - case SHELL_VAR_SHORT: - *((short *)((base + i)->value)) = value; - break; - case SHELL_VAR_CHAR: - *((char *)((base + i)->value)) = value; - break; - case SHELL_VAL: - shellDisplay(shell, "can't set val\r\n"); - break; - default: - break; - } - isVarFind = 1; - } - } - if (!isVarFind) - { - shellDisplay(shell, "var not found\r\n"); - } - else - { - shellDisplayVariable(shell, var); - } -} -SHELL_EXPORT_CMD_EX(setVar, shellSetVariable, set var, setVar $[var] [value]); - - -/** - * @brief shell显示变量 - * - * @param shell shell对象 - * @param var 变量 - */ -static void shellDisplayVariable(SHELL_TypeDef *shell, char *var) -{ - shellDisplay(shell, var[0] == '$' ? var + 1 : var); - shellDisplay(shell, " = "); - shellDisplayValue(shell, shellGetVariable(shell, var)); -} - - -/** - * @brief shell显示所有变量 - * - */ -void shellListVariables(void) -{ - SHELL_TypeDef *shell = shellGetCurrent(); - if (!shell) - { - return; - } - - SHELL_VaribaleTypeDef *base = shell->variableBase; - - unsigned short spaceLength; - - shellDisplay(shell, shellText[TEXT_VAR_LIST]); - - for (short i = 0; i < shell->variableNumber; i++) - { - spaceLength = 22 - shellDisplay(shell, (base + i)->name); - spaceLength = (spaceLength > 0) ? spaceLength : 4; - do { - shellDisplay(shell, " "); - } while (--spaceLength); - shellDisplay(shell, "--"); - shellDisplay(shell, (base + i)->desc); - shellDisplay(shell, "\r\n"); - } -} -SHELL_EXPORT_CMD(vars, shellListVariables, show vars); -#endif /** SHELL_USING_VAR == 1 */ - -/** - * @brief shell显示一条命令信息 - * - * @param shell shell对象 - * @param index 要显示的命令索引 - */ -static void shellDisplayItem(SHELL_TypeDef *shell, unsigned short index) -{ - short spaceLength; - SHELL_CommandTypeDef *base = shell->commandBase; - - spaceLength = 22 - shellDisplay(shell, (base + index)->name); - spaceLength = (spaceLength > 0) ? spaceLength : 4; - do { - shellDisplay(shell, " "); - } while (--spaceLength); - shellDisplay(shell, "--"); - shellDisplay(shell, (base + index)->desc); - shellDisplay(shell, "\r\n"); - -} - - -/** - * @brief shell帮助 - * - * @param argc 参数个数 - * @param argv 参数 - */ -void shellHelp(int argc, char *argv[]) -{ - SHELL_TypeDef *shell = shellGetCurrent(); - if (!shell) - { - return; - } -#if SHELL_LONG_HELP == 1 - if (argc == 1) - { -#endif /** SHELL_LONG_HELP == 1 */ - shellDisplay(shell, shellText[TEXT_FUN_LIST]); - for(unsigned short i = 0; i < shell->commandNumber; i++) - { - shellDisplayItem(shell, i); - } -#if SHELL_LONG_HELP == 1 - } - else if (argc == 2) { - SHELL_CommandTypeDef *base = shell->commandBase; - for (unsigned char i = 0; i < shell->commandNumber; i++) - { - if (strcmp((const char *)argv[1], (base + i)->name) == 0) - { - - shellDisplay(shell, "command help --"); - shellDisplay(shell, (base + i)->name); - shellDisplay(shell, ":\r\n"); - shellDisplay(shell, (base + i)->desc); - shellDisplay(shell, "\r\n"); - if ((base + i)->help) - { - shellDisplay(shell, (base + i)->help); - shellDisplay(shell, "\r\n"); - } - return; - } - } - shellDisplay(shell, shellText[TEXT_CMD_NONE]); - } -#endif /** SHELL_LONG_HELP == 1 */ -} -SHELL_EXPORT_CMD_EX(help, shellHelp, command help, help [command] --show help info of command); - - -/** - * @brief 清空命令行 - * - */ -void shellClear(void) -{ - SHELL_TypeDef *shell = shellGetCurrent(); - if (!shell) - { - return; - } - shellDisplay(shell, "\033[2J\033[1H"); -} -SHELL_EXPORT_CMD(cls, shellClear, clear command line); diff --git a/shell.h b/shell.h deleted file mode 100644 index 533c8f5..0000000 --- a/shell.h +++ /dev/null @@ -1,363 +0,0 @@ -/** - * @file shell.h - * @author Letter (NevermindZZT@gmail.com) - * @brief letter shell - * @version 2.0.0 - * @date 2018-12-29 - * - * @Copyright (c) 2018 Letter - * - */ - -#ifndef __SHELL_H__ -#define __SHELL_H__ - -#include "shell_cfg.h" - -#if SHELL_USING_AUTH == 1 - #if !defined(SHELL_USER_PASSWORD) - #error "please config shell user password (int shell_cfg.h) " - #endif -#endif - -#define SHELL_VERSION "2.0.8" /**< 版本号 */ - -/** - * @brief shell键值定义 - * - */ -#define SHELL_KEY_LF 0x0A -#define SHELL_KEY_CR 0x0D -#define SHELL_KEY_TAB 0x09 -#define SHELL_KEY_BACKSPACE 0x08 -#define SHELL_KEY_DELETE 0x7F -#define SHELL_KEY_ESC 0x1B - -#define SHELL_KEY_CTRL_T 0x14 -#define SHELL_KEY_CTRL_A 0x01 -#define SHELL_KEY_CTRL_B 0x02 -#define SHELL_KEY_CTRL_C 0x03 -#define SHELL_KEY_CTRL_D 0x04 -#define SHELL_KEY_CTRL_E 0x05 -#define SHELL_KEY_CTRL_F 0x06 -#define SHELL_KEY_CTRL_G 0x07 -#define SHELL_KEY_CTRL_H 0x08 -#define SHELL_KEY_CTRL_I 0x09 -#define SHELL_KEY_CTRL_J 0x0A -#define SHELL_KEY_CTRL_K 0x0B -#define SHELL_KEY_CTRL_L 0x0C -#define SHELL_KEY_CTRL_M 0x0D -#define SHELL_KEY_CTRL_N 0x0E -#define SHELL_KEY_CTRL_O 0x0F -#define SHELL_KEY_CTRL_P 0x10 -#define SHELL_KEY_CTRL_Q 0x11 -#define SHELL_KEY_CTRL_R 0x12 -#define SHELL_KEY_CTRL_S 0x13 -#define SHELL_KEY_CTRL_T 0x14 -#define SHELL_KEY_CTRL_U 0x15 -#define SHELL_KEY_CTRL_V 0x16 -#define SHELL_KEY_CTRL_W 0x17 -#define SHELL_KEY_CTRL_X 0x18 -#define SHELL_KEY_CTRL_Y 0x19 -#define SHELL_KEY_CTRL_Z 0x1A - -/** - * @brief shell变量类型定义 - * - */ -#define SHELL_VAR_INT 0 -#define SHELL_VAR_SHORT 1 -#define SHELL_VAR_CHAR 2 -#define SHELL_VAR_POINTER 3 -#define SHELL_VAL 4 - -#if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000) - #define SECTION(x) __attribute__((section(x))) -#elif defined(__ICCARM__) - #define SECTION(x) @ x -#elif defined(__GNUC__) - #define SECTION(x) __attribute__((section(x))) -#else - #define SECTION(x) -#endif - -/** - * @brief shell命令导出 - * - * @attention 命令导出方式支持keil,iar的编译器以及gcc,具体参考readme - */ -#if SHELL_USING_CMD_EXPORT == 1 -#if SHELL_LONG_HELP == 1 -#define SHELL_EXPORT_CMD(cmd, func, desc) \ - const char shellCmd##cmd[] = #cmd; \ - const char shellDesc##cmd[] = #desc; \ - const SHELL_CommandTypeDef \ - shellCommand##cmd SECTION("shellCommand") = \ - { \ - shellCmd##cmd, \ - (int (*)())func, \ - shellDesc##cmd, \ - (void *)0 \ - } -#define SHELL_EXPORT_CMD_EX(cmd, func, desc, help) \ - const char shellCmd##cmd[] = #cmd; \ - const char shellDesc##cmd[] = #desc; \ - const char shellHelp##cmd[] = #help; \ - const SHELL_CommandTypeDef \ - shellCommand##cmd SECTION("shellCommand") = \ - { \ - shellCmd##cmd, \ - (int (*)())func, \ - shellDesc##cmd, \ - shellHelp##cmd \ - } -#else /** SHELL_LONG_HELP == 1 */ -#define SHELL_EXPORT_CMD(cmd, func, desc) \ - const char shellCmd##cmd[] = #cmd; \ - const char shellDesc##cmd[] = #desc; \ - const SHELL_CommandTypeDef \ - shellCommand##cmd SECTION("shellCommand") = \ - { \ - shellCmd##cmd, \ - (int (*)())func, \ - shellDesc##cmd \ - } -#define SHELL_EXPORT_CMD_EX(cmd, func, desc, help) \ - const char shellCmd##cmd[] = #cmd; \ - const char shellDesc##cmd[] = #desc; \ - const SHELL_CommandTypeDef \ - shellCommand##cmd SECTION("shellCommand") = \ - { \ - shellCmd##cmd, \ - (int (*)())func, \ - shellDesc##cmd \ - } -#endif /** SHELL_LONG_HELP == 1 */ - -#if SHELL_USING_VAR == 1 - #define SHELL_EXPORT_VAR(var, variable, desc, type) \ - const char shellVar##var[] = #var; \ - const char shellDesc##var[] = #desc; \ - const SHELL_VaribaleTypeDef \ - shellVariable##var SECTION("shellVariable") = \ - { \ - shellVar##var, \ - (void *)(variable), \ - shellDesc##var, \ - type \ - } -#else - #define SHELL_EXPORT_VAR(var, variable, desc, type) -#endif /** SHELL_USING_VAR == 1 */ - -#else -#define SHELL_EXPORT_CMD(cmd, func, desc) -#define SHELL_EXPORT_CMD_EX(cmd, func, desc, help) -#define SHELL_EXPORT_VAR(var, variable, desc, type) -#endif /** SHELL_USING_CMD_EXPORT == 1 */ - -#define SHELL_EXPORT_VAR_INT(var, variable, desc) \ - SHELL_EXPORT_VAR(var, &variable, desc, SHELL_VAR_INT) -#define SHELL_EXPORT_VAR_SHORT(var, variable, desc) \ - SHELL_EXPORT_VAR(var, &variable, desc, SHELL_VAR_SHORT) -#define SHELL_EXPORT_VAR_CHAR(var, variable, desc) \ - SHELL_EXPORT_VAR(var, &variable, desc, SHELL_VAR_CHAR) -#define SHELL_EXPORT_VAR_POINTER(var, variable, desc) \ - SHELL_EXPORT_VAR(var, variable, desc, SHELL_VAR_POINTER) -#define SHELL_EXPORT_VAL(val, value, desc) \ - SHELL_EXPORT_VAR(val, value, desc, SHELL_VAL) - - -/** - * @brief shell命令条目 - * - * @note 用于shell命令通过命令表的方式定义 - */ -#if SHELL_USING_CMD_EXPORT == 0 -#if SHELL_LONG_HELP == 1 -#define SHELL_CMD_ITEM(cmd, func, desc) \ - { \ - #cmd, \ - (int (*)())func, \ - #desc, \ - (void *)0 \ - } -#define SHELL_CMD_ITEM_EX(cmd, func, desc, help) \ - { \ - #cmd, \ - (int (*)())func, \ - #desc, \ - #help \ - } -#else /** SHELL_LONG_HELP == 1 */ -#define SHELL_CMD_ITEM(cmd, func, desc) \ - { \ - #cmd, \ - (int (*)())func, \ - #desc \ - } -#define SHELL_CMD_ITEM_EX(cmd, func, desc, help) \ - { \ - #cmd, \ - (int (*)())func, \ - #desc, \ - } -#endif /** SHELL_LONG_HELP == 1 */ - -#define SHELL_VAR_ITEM(var, variable, desc, type) \ - { \ - #var, \ - varialbe, \ - #desc, \ - type, \ - } -#define SHELL_VAR_ITEM_INT(var, variable, desc) \ - SHELL_VAR_ITEM(var, &variable, desc, SHELL_VAR_INT) -#define SHELL_VAR_ITEM_SHORT(var, variable, desc) \ - SHELL_VAR_ITEM(var, &variable, desc, SHELL_VAR_SHORT) -#define SHELL_VAR_ITEM_CHAR(var, variable, desc) \ - SHELL_VAR_ITEM(var, &variable, desc, SHELL_VAR_CHAR) -#define SHELL_VAR_ITEM_POINTER(var, variable, desc) \ - SHELL_VAR_ITEM(var, variable, desc, SHELL_VAR_POINTER) - -#endif /** SHELL_USING_CMD_EXPORT == 0 */ - -/** - * @brief shell读取数据函数原型 - * - * @param char shell读取的字符 - * - * @return char 0 读取数据成功 - * @return char -1 读取数据失败 - */ -typedef signed char (*shellRead)(char *); - -/** - * @brief shell写数据函数原型 - * - * @param const char 需写的字符 - */ -typedef void (*shellWrite)(const char); - -/** - * @brief shell指令执行函数原型 - * - */ -typedef int (*shellFunction)(); - - -/** - * @brief shell输入状态 - * - */ -typedef enum -{ - SHELL_IN_NORMAL = 0, - SHELL_ANSI_ESC, - SHELL_ANSI_CSI, -}SHELL_InputMode; - - -/** - * @brief shell 命令定义 - * - */ -typedef struct -{ - const char *name; /**< shell命令名称 */ - shellFunction function; /**< shell命令函数 */ - const char *desc; /**< shell命令描述 */ -#if SHELL_LONG_HELP == 1 - const char *help; /**< shell长帮助信息 */ -#endif -}SHELL_CommandTypeDef; - - -#if SHELL_USING_VAR == 1 -/** - * @brief shell 变量定义 - * - */ -typedef struct -{ - const char *name; /**< shell变量名称 */ - const void *value; /**< shell变量值 */ - const char *desc; /**< shell变量描述 */ - const int type; /**< shell变量类型 */ -} SHELL_VaribaleTypeDef; -#endif /** SHELL_USING_VAR == 1 */ - - -/** - * @brief shell对象定义 - * - */ -typedef struct -{ - char *command; /**< shell命令提示符 */ - char buffer[SHELL_COMMAND_MAX_LENGTH]; /**< shell命令缓冲 */ - unsigned short length; /**< shell命令长度 */ - unsigned short cursor; /**< shell光标位置 */ - char *param[SHELL_PARAMETER_MAX_NUMBER]; /**< shell参数 */ - char history[SHELL_HISTORY_MAX_NUMBER][SHELL_COMMAND_MAX_LENGTH]; /**< 历史记录 */ - unsigned short historyCount; /**< 历史记录数量 */ - short historyFlag; /**< 当前记录位置 */ - short historyOffset; /**< 历史记录偏移 */ - SHELL_CommandTypeDef *commandBase; /**< 命令表基址 */ - unsigned short commandNumber; /**< 命令数量 */ -#if SHELL_USING_VAR == 1 - SHELL_VaribaleTypeDef *variableBase; /**< 变量表基址 */ - unsigned short variableNumber; /**< 变量数量 */ -#endif - int keyFuncBase; /**< 按键响应表基址 */ - unsigned short keyFuncNumber; /**< 按键响应数量 */ - struct - { - unsigned char inputMode : 2; /**< 输入模式 */ - unsigned char isActive: 1; /**< 是否是当前活动shell */ - unsigned char tabFlag : 1; /**< tab标志 */ - unsigned char authFlag : 1; /**< 密码标志 */ - } status; /**< shell状态 */ - shellRead read; /**< shell读字符 */ - shellWrite write; /**< shell写字符 */ -#if SHELL_LONG_HELP == 1 || (SHELL_USING_AUTH && SHELL_LOCK_TIMEOUT > 0) - int activeTime; /**< shell激活时间戳 */ -#endif -}SHELL_TypeDef; - - -/** - * @brief shell按键功能定义 - * - */ -typedef struct -{ - unsigned char keyCode; /**< shell按键键值 */ - void (*keyFunction)(SHELL_TypeDef *); /**< 按键响应函数 */ -} SHELL_KeyFunctionDef; - - -void shellInit(SHELL_TypeDef *shell); -void shellSetCommandList(SHELL_TypeDef *shell, SHELL_CommandTypeDef *base, unsigned short size); - -#if SHELL_USING_VAR == 1 -void shellSetVariableList(SHELL_TypeDef *shell, SHELL_VaribaleTypeDef *base, unsigned short size); -int shellGetVariable(SHELL_TypeDef *shell, char *var); -#endif /** SHELL_USING_VAR == 1 */ - -void shellSetKeyFuncList(SHELL_TypeDef *shell, SHELL_KeyFunctionDef *base, unsigned short size); -SHELL_TypeDef *shellGetCurrent(void); -void shellPrint(SHELL_TypeDef *shell, char *fmt, ...); -unsigned short shellDisplay(SHELL_TypeDef *shell, const char *string); -void shellHandler(SHELL_TypeDef *shell, char data); -#define shellInput shellHandler - -void shellHelp(int argc, char *argv[]); -void shellClear(void); - -#if SHELL_USING_TASK == 1 -void shellTask(void *param); -#endif - -#endif - diff --git a/src/shell.c b/src/shell.c new file mode 100644 index 0000000..680e4e8 --- /dev/null +++ b/src/shell.c @@ -0,0 +1,1653 @@ +/** + * @file shell.c + * @author Letter (NevermindZZT@gmail.com) + * @version 3.0.0 + * @date 2019-12-30 + * + * @Copyright (c) 2020 Letter + * + */ + +#include "shell.h" +#include "string.h" +#include "stdio.h" +#include "stdarg.h" +#include "shell_ext.h" + + +#if SHELL_USING_CMD_EXPORT == 1 +/** + * @brief 默认用户 + */ +const char shellCmdDefaultUser[] = SHELL_DEFAULT_USER; +const char shellPasswordDefaultUser[] = SHELL_DEFAULT_USER_PASSWORD; +const char shellDesDefaultUser[] = "defalut user"; +const ShellCommand shellUserDefault SECTION("shellCommand") = +{ + .attr.value = SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_USER), + .data.user.name = shellCmdDefaultUser, + .data.user.password = shellPasswordDefaultUser, + .data.user.desc = shellDesDefaultUser +}; +#endif + +#if SHELL_USING_CMD_EXPORT != 1 + extern const ShellCommand shellCommandList[]; + extern const unsigned short shellCommandCount; +#endif + + +/** + * @brief shell 常量文本索引 + */ +enum +{ + SHELL_TEXT_INFO, /**< shell信息 */ + SHELL_TEXT_CMD_TOO_LONG, /**< 命令过长 */ + SHELL_TEXT_CMD_LIST, /**< 可执行命令列表标题 */ + SHELL_TEXT_VAR_LIST, /**< 变量列表标题 */ + SHELL_TEXT_USER_LIST, /**< 用户列表标题 */ + SHELL_TEXT_KEY_LIST, /**< 按键列表标题 */ + SHELL_TEXT_CMD_NOT_FOUND, /**< 命令未找到 */ + SHELL_TEXT_POINT_CANNOT_MODIFY, /**< 指针变量不允许修改 */ + SHELL_TEXT_VAL_CANNOT_MODIFY, /**< 常量不允许修改 */ + SHELL_TEXT_NOT_VAR, /**< 命令不是变量 */ + SHELL_TEXT_VAR_NOT_FOUND, /**< 变量未找到 */ + SHELL_TEXT_HELP_HEADER, /**< help头 */ + SHELL_TEXT_PASSWORD_HINT, /**< 密码输入提示 */ + SHELL_TEXT_PASSWORD_ERROR, /**< 密码错误 */ + SHELL_TEXT_CLEAR_CONSOLE, /**< 清空控制台 */ + SHELL_TEXT_TYPE_CMD, /**< 命令类型 */ + SHELL_TEXT_TYPE_VAR, /**< 变量类型 */ + SHELL_TEXT_TYPE_USER, /**< 用户类型 */ + SHELL_TEXT_TYPE_KEY, /**< 按键类型 */ + SHELL_TEXT_TYPE_NONE, /**< 非法类型 */ +}; + + +static const char *shellText[] = +{ + [SHELL_TEXT_INFO] = + "\r\n" + " _ _ _ _ _ _ \r\n" + "| | ___| |_| |_ ___ _ __ ___| |__ ___| | |\r\n" + "| | / _ \\ __| __/ _ \\ '__| / __| '_ \\ / _ \\ | |\r\n" + "| |__| __/ |_| || __/ | \\__ \\ | | | __/ | |\r\n" + "|_____\\___|\\__|\\__\\___|_| |___/_| |_|\\___|_|_|\r\n" + "\r\n" + "Build: "__DATE__" "__TIME__"\r\n" + "Version: "SHELL_VERSION"\r\n" + "Copyright: (c) 2020 Letter\r\n", + [SHELL_TEXT_CMD_TOO_LONG] = + "\r\nWarning: Command is too long\r\n", + [SHELL_TEXT_CMD_LIST] = + "\r\nCommand List:\r\n", + [SHELL_TEXT_VAR_LIST] = + "\r\nVar List:\r\n", + [SHELL_TEXT_USER_LIST] = + "\r\nUser List:\r\n", + [SHELL_TEXT_KEY_LIST] = + "\r\nKey List:\r\n", + [SHELL_TEXT_CMD_NOT_FOUND] = + "Command not Found\r\n", + [SHELL_TEXT_POINT_CANNOT_MODIFY] = + "can't set pointer\r\n", + [SHELL_TEXT_VAL_CANNOT_MODIFY] = + "can't set val\r\n", + [SHELL_TEXT_NOT_VAR] = + " is not a var\r\n", + [SHELL_TEXT_VAR_NOT_FOUND] = + "Var not Fount\r\n", + [SHELL_TEXT_HELP_HEADER] = + "command help of ", + [SHELL_TEXT_PASSWORD_HINT] = + "\r\nPlease input password:", + [SHELL_TEXT_PASSWORD_ERROR] = + "\r\npasswrod error\r\n", + [SHELL_TEXT_CLEAR_CONSOLE] = + "\033[2J\033[1H", + [SHELL_TEXT_TYPE_CMD] = + "CMD ", + [SHELL_TEXT_TYPE_VAR] = + "VAR ", + [SHELL_TEXT_TYPE_USER] = + "USER", + [SHELL_TEXT_TYPE_KEY] = + "KEY ", + [SHELL_TEXT_TYPE_NONE] = + "NONE", +}; + + +/** + * @brief shell对象表 + */ +static Shell *shellList[SHELL_MAX_NUMBER] = {NULL}; + + +static void shellAdd(Shell *shell); +static unsigned short shellWriteString(Shell *shell, const char *string); +static void shellWriteCommandLine(Shell *shell); +static void shellWirteReturnValue(Shell *shell, int value); +static void shellShowVar(Shell *shell, ShellCommand *command); +static void shellSetUser(Shell *shell, const ShellCommand *user); +ShellCommand* shellSeekCommand(Shell *shell, + const char *cmd, + ShellCommand *base, + unsigned short compareLength); + +/** + * @brief shell 初始化 + * + * @param shell shell对象 + */ +void shellInit(Shell *shell, char *buffer, unsigned short size) +{ + shell->parser.length = 0; + shell->parser.cursor = 0; + shell->history.offset = 0; + shell->history.number = 0; + shell->history.record = 0; + + shell->parser.buffer = buffer; + shell->parser.bufferSize = size / (SHELL_HISTORY_MAX_NUMBER + 1); + for (short i = 0; i < SHELL_HISTORY_MAX_NUMBER; i++) + { + shell->history.item[i] = buffer + shell->parser.bufferSize * (i + 1); + } + +#if SHELL_USING_CMD_EXPORT == 1 + #if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000) + extern const unsigned int shellCommand$$Base; + extern const unsigned int shellCommand$$Limit; + + shell->commandList.base = (ShellCommand *)(&shellCommand$$Base); + shell->commandList.count = ((unsigned int)(&shellCommand$$Limit) + - (unsigned int)(&shellCommand$$Base)) + / sizeof(ShellCommand); + + #elif defined(__ICCARM__) + shell->commandList.base = (ShellCommand *)(__section_begin("shellCommand")); + shell->commandList.count = ((unsigned int)(__section_end("shellCommand")) + - (unsigned int)(__section_begin("shellCommand"))) + / sizeof(ShellCommand); + #elif defined(__GNUC__) + extern const unsigned int _shell_command_start; + extern const unsigned int _shell_command_end; + + shell->commandList.base = (ShellCommand *)(&_shell_command_start); + shell->commandList.count = ((unsigned int)(&_shell_command_end) + - (unsigned int)(&_shell_command_start)) + / sizeof(ShellCommand); + #else + #error not supported compiler, please use command table mode + #endif +#else + shell->commandList.base = (ShellCommand *)shellCommandList; + shell->commandList.count = shellCommandCount; +#endif + + shellAdd(shell); + + shellSetUser(shell, shellSeekCommand(shell, + SHELL_DEFAULT_USER, + shell->commandList.base, + 0)); + shellWriteCommandLine(shell); +} + + +/** + * @brief 添加shell + * + * @param shell shell对象 + */ +static void shellAdd(Shell *shell) +{ + for (short i = 0; i < SHELL_MAX_NUMBER; i++) + { + if (shellList[i] == NULL) + { + shellList[i] = shell; + return; + } + } +} + + +/** + * @brief 获取当前活动shell + * + * @return Shell* 当前活动shell对象 + */ +Shell* shellGetCurrent(void) +{ + for (short i = 0; i < SHELL_MAX_NUMBER; i++) + { + if (shellList[i] && shellList[i]->status.isActive) + { + return shellList[i]; + } + } + return NULL; +} + + +/** + * @brief shell写字符 + * + * @param shell shell对象 + * @param data 字符数据 + */ +static void shellWriteByte(Shell *shell, const char data) +{ + shell->write(data); +} + + +/** + * @brief shell 写字符串 + * + * @param shell shell对象 + * @param string 字符串数据 + * + * @return unsigned short 写入字符的数量 + */ +static unsigned short shellWriteString(Shell *shell, const char *string) +{ + unsigned short count = 0; + SHELL_ASSERT(shell->write, return 0); + while(*string) + { + shell->write(*string ++); + count ++; + } + return count; +} + + +/** + * @brief shell 写命令描述字符串 + * + * @param shell shell对象 + * @param string 字符串数据 + * + * @return unsigned short 写入字符的数量 + */ +static unsigned short shellWriteCommandDesc(Shell *shell, const char *string) +{ + unsigned short count = 0; + SHELL_ASSERT(shell->write, return 0); + while(*string + && *string != '\r' + && *string != '\n' + && count < 36) + { + shell->write(*string ++); + count ++; + if (count >= 36 && *(string + 1)) + { + shell->write('.'); + shell->write('.'); + shell->write('.'); + } + } + return count; +} + + +/** + * @brief shell写命令提示符 + * + * @param shell shell对象 + * + */ +static void shellWriteCommandLine(Shell *shell) +{ + if (shell->status.isChecked) + { + shellWriteString(shell, "\r\n"); + shellWriteString(shell, shell->info.user->data.user.name); + shellWriteString(shell, ":/$ "); + } + else + { + shellWriteString(shell, shellText[SHELL_TEXT_PASSWORD_HINT]); + } +} + + +/** + * @brief shell 检查命令权限 + * + * @param shell shell对象 + * @param command ShellCommand + * + * @return signed char 0 当前用户具有该命令权限 + * @return signec char -1 当前用户不具有该命令权限 + */ +signed char shellCheckPermission(Shell *shell, ShellCommand *command) +{ + return ((!command->attr.attrs.permission + || (command->attr.attrs.permission + & shell->info.user->attr.attrs.permission)) + && (shell->status.isChecked + || command->attr.attrs.enableUnchecked)) + ? 0 : -1; +} + + +/** + * @brief int转16进制字符串 + * + * @param value 数值 + * @param buffer 缓冲 + * + * @return signed char 转换后有效数据长度 + */ +signed char shellToHex(int value, char *buffer) +{ + char byte; + char i = 8; + buffer[8] = 0; + while (value) + { + byte = value & 0x0000000F; + buffer[--i] = (byte > 9) ? (byte + 87) : (byte + 48); + value >>= 4; + } + return 8 - i; +} + + +/** +* @brief int转10进制字符串 + * + * @param value 数值 + * @param buffer 缓冲 + * + * @return signed char 转换后有效数据长度 + */ +signed char shellToDec(int value, char *buffer) +{ + char i = 10; + int v = value; + if (value < 0) + { + v = -value; + } + buffer[10] = 0; + while (v) + { + buffer[--i] = v % 10 + 48; + v /= 10; + } + if (value < 0) + { + buffer[--i] = '-'; + } + return 10 - i; +} + + +/** + * @brief shell字符串复制 + * + * @param dest 目标字符串 + * @param src 源字符串 + * @return unsigned short 字符串长度 + */ +static unsigned short shellStringCopy(char *dest, char* src) +{ + unsigned short count = 0; + while (*(src + count)) + { + *(dest + count) = *(src + count); + count++; + } + *(dest + count) = 0; + return count; +} + + +/** + * @brief shell字符串比较 + * + * @param dest 目标字符串 + * @param src 源字符串 + * @return unsigned short 匹配长度 + */ +static unsigned short shellStringCompare(char* dest, char *src) +{ + unsigned short match = 0; + unsigned short i = 0; + + while (*(dest +i) && *(src + i)) + { + if (*(dest + i) != *(src +i)) + { + break; + } + match ++; + i++; + } + return match; +} + + +/** + * @brief shell获取命令名 + * + * @param command 命令 + * @return const char* 命令名 + */ +static const char* shellGetCommandName(ShellCommand *command) +{ + static char buffer[9]; + for (char i = 0; i < 9; i++) + { + buffer[i] = '0'; + } + if (command->attr.attrs.type <= SHELL_TYPE_CMD_FUNC) + { + return command->data.cmd.name; + } + else if (command->attr.attrs.type <= SHELL_TYPE_VAL) + { + return command->data.var.name; + } + else if (command->attr.attrs.type <= SHELL_TYPE_USER) + { + return command->data.user.name; + } + else + { + shellToHex(command->data.key.value, buffer); + return buffer; + } +} + + +/** + * @brief shell获取命令描述 + * + * @param command 命令 + * @return const char* 命令描述 + */ +static const char* shellGetCommandDesc(ShellCommand *command) +{ + if (command->attr.attrs.type <= SHELL_TYPE_CMD_FUNC) + { + return command->data.cmd.desc; + } + else if (command->attr.attrs.type <= SHELL_TYPE_VAL) + { + return command->data.var.desc; + } + else if (command->attr.attrs.type <= SHELL_TYPE_USER) + { + return command->data.user.desc; + } + else + { + return command->data.key.desc; + } +} + +/** + * @brief shell 列出命令条目 + * + * @param shell shell对象 + * @param item 命令条目 + */ +void shellListItem(Shell *shell, ShellCommand *item) +{ + short spaceLength; + + spaceLength = 22 - shellWriteString(shell, shellGetCommandName(item)); + spaceLength = (spaceLength > 0) ? spaceLength : 4; + do { + shellWriteByte(shell, ' '); + } while (--spaceLength); + if (item->attr.attrs.type <= SHELL_TYPE_CMD_FUNC) + { + shellWriteString(shell, shellText[SHELL_TEXT_TYPE_CMD]); + } + else if (item->attr.attrs.type <= SHELL_TYPE_VAL) + { + shellWriteString(shell, shellText[SHELL_TEXT_TYPE_VAR]); + } + else if (item->attr.attrs.type <= SHELL_TYPE_USER) + { + shellWriteString(shell, shellText[SHELL_TEXT_TYPE_USER]); + } + else if (item->attr.attrs.type <= SHELL_TYPE_KEY) + { + shellWriteString(shell, shellText[SHELL_TEXT_TYPE_KEY]); + } + else + { + shellWriteString(shell, shellText[SHELL_TEXT_TYPE_NONE]); + } + shellWriteString(shell, " --"); + shellWriteCommandDesc(shell, shellGetCommandDesc(item)); + shellWriteString(shell, "\r\n"); +} + + +/** + * @brief shell列出可执行命令 + * + * @param shell shell对象 + */ +void shellListCommand(Shell *shell) +{ + ShellCommand *base = (ShellCommand *)shell->commandList.base; + shellWriteString(shell, shellText[SHELL_TEXT_CMD_LIST]); + for (short i = 0; i < shell->commandList.count; i++) + { + if (base[i].attr.attrs.type <= SHELL_TYPE_CMD_FUNC + && shellCheckPermission(shell, &base[i]) == 0) + { + shellListItem(shell, &base[i]); + } + } +} + + +/** + * @brief shell列出变量 + * + * @param shell shell对象 + */ +void shellListVar(Shell *shell) +{ + ShellCommand *base = (ShellCommand *)shell->commandList.base; + shellWriteString(shell, shellText[SHELL_TEXT_VAR_LIST]); + for (short i = 0; i < shell->commandList.count; i++) + { + if (base[i].attr.attrs.type > SHELL_TYPE_CMD_FUNC + && base[i].attr.attrs.type <= SHELL_TYPE_VAL + && shellCheckPermission(shell, &base[i]) == 0) + { + shellListItem(shell, &base[i]); + } + } +} + + +/** + * @brief shell列出用户 + * + * @param shell shell对象 + */ +void shellListUser(Shell *shell) +{ + ShellCommand *base = (ShellCommand *)shell->commandList.base; + shellWriteString(shell, shellText[SHELL_TEXT_USER_LIST]); + for (short i = 0; i < shell->commandList.count; i++) + { + if (base[i].attr.attrs.type > SHELL_TYPE_VAL + && base[i].attr.attrs.type <= SHELL_TYPE_USER) + { + shellListItem(shell, &base[i]); + } + } +} + + +/** + * @brief shell列出按键 + * + * @param shell shell对象 + */ +void shellListKey(Shell *shell) +{ + ShellCommand *base = (ShellCommand *)shell->commandList.base; + shellWriteString(shell, shellText[SHELL_TEXT_KEY_LIST]); + for (short i = 0; i < shell->commandList.count; i++) + { + if (base[i].attr.attrs.type > SHELL_TYPE_USER + && base[i].attr.attrs.type <= SHELL_TYPE_KEY + && shellCheckPermission(shell, &base[i]) == 0) + { + shellListItem(shell, &base[i]); + } + } +} + + +/** + * @brief shell列出所有命令 + * + * @param shell shell对象 + */ +void shellListAll(Shell *shell) +{ +#if SHELL_HELP_LIST_USER == 1 + shellListUser(shell); +#endif + shellListCommand(shell); +#if SHELL_HELP_LIST_VAR == 1 + shellListVar(shell); +#endif +#if SHELL_HELP_LIST_KEY == 1 + shellListKey(shell); +#endif +} + + +/** + * @brief shell删除命令行数据 + * + * @param shell shell对象 + * @param length 删除长度 + */ +void shellDeleteCommandLine(Shell *shell, unsigned char length) +{ + while (length--) + { + shellWriteString(shell, "\b \b"); + } +} + + +/** + * @brief shell 清空命令行输入 + * + * @param shell shell对象 + */ +void shellClearCommandLine(Shell *shell) +{ + for (short i = shell->parser.length - shell->parser.cursor; i > 0; i--) + { + shellWriteByte(shell, ' '); + } + shellDeleteCommandLine(shell, shell->parser.length); +} + + +/** + * @brief shell插入一个字符到光标位置 + * + * @param shell shell对象 + * @param data 字符数据 + */ +void shellInsertByte(Shell *shell, char data) +{ + /* 判断输入数据是否过长 */ + if (shell->parser.length >= shell->parser.bufferSize - 1) + { + shellWriteString(shell, shellText[SHELL_TEXT_CMD_TOO_LONG]); + shellWriteCommandLine(shell); + shellWriteString(shell, shell->parser.buffer); + return; + } + + /* 插入数据 */ + if (shell->parser.cursor == shell->parser.length) + { + shell->parser.buffer[shell->parser.length++] = data; + shell->parser.buffer[shell->parser.length] = 0; + shell->parser.cursor++; + shellWriteByte(shell, data); + } + else if (shell->parser.cursor < shell->parser.length) + { + for (short i = shell->parser.length - shell->parser.cursor; i > 0; i--) + { + shell->parser.buffer[shell->parser.cursor + i] = + shell->parser.buffer[shell->parser.cursor + i - 1]; + } + shell->parser.buffer[shell->parser.cursor++] = data; + shell->parser.buffer[++shell->parser.length] = 0; + for (short i = shell->parser.cursor - 1; i < shell->parser.length; i++) + { + shellWriteByte(shell, shell->parser.buffer[i]); + } + for (short i = shell->parser.length - shell->parser.cursor; i > 0; i--) + { + shellWriteByte(shell, '\b'); + } + } +} + + +/** + * @brief shell 删除字节 + * + * @param shell shell对象 + * @param direction 删除方向 {@code 1}删除光标前字符 {@code -1}删除光标处字符 + */ +void shellDeleteByte(Shell *shell, signed char direction) +{ + char offset = (direction == -1) ? 1 : 0; + + if ((shell->parser.cursor == 0 && direction == 1) + || (shell->parser.cursor == shell->parser.length && direction == -1)) + { + return; + } + if (shell->parser.cursor == shell->parser.length && direction == 1) + { + shell->parser.cursor--; + shell->parser.length--; + shell->parser.buffer[shell->parser.length] = 0; + shellDeleteCommandLine(shell, 1); + } + else + { + for (short i = offset; i < shell->parser.length - shell->parser.cursor; i++) + { + shell->parser.buffer[shell->parser.cursor + i - 1] = + shell->parser.buffer[shell->parser.cursor + i]; + } + shell->parser.length--; + if (!offset) + { + shell->parser.cursor--; + shellWriteByte(shell, '\b'); + } + shell->parser.buffer[shell->parser.length] = 0; + for (short i = shell->parser.cursor; i < shell->parser.length; i++) + { + shellWriteByte(shell, shell->parser.buffer[i]); + } + shellWriteByte(shell, ' '); + for (short i = shell->parser.length - shell->parser.cursor + 1; i > 0; i--) + { + shellWriteByte(shell, '\b'); + } + } +} + + +/** + * @brief shell 解析参数 + * + * @param shell shell对象 + */ +static void shellParserParam(Shell *shell) +{ + unsigned char quotes = 0; + unsigned char record = 1; + + shell->parser.paramCount = 0; + for (unsigned short i = 0; i < shell->parser.length; i++) + { + if (quotes != 0 + || (shell->parser.buffer[i] != ' ' + && shell->parser.buffer[i] != 0)) + { + if (record == 1) + { + shell->parser.param[shell->parser.paramCount ++] = + &(shell->parser.buffer[i]); + record = 0; + } + if (shell->parser.buffer[i] == '\\' + && shell->parser.buffer[i + 1] != 0) + { + i++; + } + } + else + { + shell->parser.buffer[i] = 0; + record = 1; + } + } +} + + +/** + * @brief shell去除字符串参数头尾的双引号 + * + * @param shell shell对象 + */ +static void shellRemoveParamQuotes(Shell *shell) +{ + unsigned short paramLength; + for (unsigned short i = 0; i < shell->parser.paramCount; i++) + { + if (shell->parser.param[i][0] == '\"') + { + shell->parser.param[i][0] = 0; + shell->parser.param[i] = &shell->parser.param[i][1]; + } + paramLength = strlen(shell->parser.param[i]); + if (shell->parser.param[i][paramLength - 1] == '\"') + { + shell->parser.param[i][paramLength - 1] = 0; + } + } +} + + +/** + * @brief shell匹配命令 + * + * @param shell shell对象 + * @param cmd 命令 + * @param base 匹配命令表基址 + * @param compareLength 匹配字符串长度 + * @return ShellCommand* 匹配到的命令 + */ +ShellCommand* shellSeekCommand(Shell *shell, + const char *cmd, + ShellCommand *base, + unsigned short compareLength) +{ + const char *name; + unsigned short count = shell->commandList.count - + ((int)base - (int)shell->commandList.base) / sizeof(ShellCommand); + for (unsigned short i = 0; i < count; i++) + { + if (base[i].attr.attrs.type == SHELL_TYPE_KEY) + { + continue; + } + name = shellGetCommandName(&base[i]); + if (!compareLength) + { + if (strcmp(cmd, name) == 0) + { + return &base[i]; + } + } + else + { + if (strncmp(cmd, name, compareLength) == 0) + { + return &base[i]; + } + } + } + return NULL; +} + + +/** + * @brief shell 获取变量值 + * + * @param shell shell对象 + * @param command 命令 + * @return int 变量值 + */ +int shellGetVarValue(Shell *shell, ShellCommand *command) +{ + int value; + switch (command->attr.attrs.type) + { + case SHELL_TYPE_VAR_INT: + value = *((int *)(command->data.var.value)); + break; + case SHELL_TYPE_VAR_SHORT: + value = *((short *)(command->data.var.value)); + break; + case SHELL_TYPE_VAR_CHAR: + value = *((char *)(command->data.var.value)); + break; + case SHELL_TYPE_VAR_POINT: + case SHELL_TYPE_VAL: + value = (int)(command->data.var.value); + break; + default: + break; + } + return value; +} + + +/** + * @brief shell设置变量值 + * + * @param shell shell对象 + * @param command 命令 + * @param value 值 + */ +void shellSetVarValue(Shell *shell, ShellCommand *command, int value) +{ + switch (command->attr.attrs.type) + { + case SHELL_TYPE_VAR_INT: + *((int *)(command->data.var.value)) = value; + break; + case SHELL_TYPE_VAR_SHORT: + *((short *)(command->data.var.value)) = value; + break; + case SHELL_TYPE_VAR_CHAR: + *((char *)(command->data.var.value)) = value; + break; + case SHELL_TYPE_VAR_POINT: + shellWriteString(shell, shellText[SHELL_TEXT_POINT_CANNOT_MODIFY]); + break; + case SHELL_TYPE_VAL: + shellWriteString(shell, shellText[SHELL_TEXT_VAL_CANNOT_MODIFY]); + break; + default: + break; + } + shellShowVar(shell, command); +} + + +/** + * @brief shell变量输出 + * + * @param shell shell对象 + * @param command 命令 + */ +static void shellShowVar(Shell *shell, ShellCommand *command) +{ + char buffer[11] = "0000000000"; + int value = shellGetVarValue(shell, command); + shellWriteString(shell, command->data.var.name); + shellWriteString(shell, " = "); + shellToDec(value, buffer); + shellWriteString(shell, buffer); + shellWriteString(shell, ", 0x"); + for (short i = 0; i < 11; i++) + { + buffer[i] = '0'; + } + shellToHex(value, buffer); + shellWriteString(shell, buffer); + shellWriteString(shell, "\r\n"); +} + + +/** + * @brief shell设置变量 + * + * @param name 变量名 + * @param value 变量值 + * @return int 返回变量值 + */ +int shellSetVar(char *name, int value) +{ + Shell *shell = shellGetCurrent(); + if (shell == NULL) + { + return 0; + } + ShellCommand *command = shellSeekCommand(shell, + name, + shell->commandList.base, + 0); + if (!command) + { + shellWriteString(shell, shellText[SHELL_TEXT_VAR_NOT_FOUND]); + return 0; + } + if (command->attr.attrs.type < SHELL_TYPE_VAR_INT + || command->attr.attrs.type > SHELL_TYPE_VAL) + { + shellWriteString(shell, name); + shellWriteString(shell, shellText[SHELL_TEXT_NOT_VAR]); + return 0; + } + shellSetVarValue(shell, command, value); + return shellGetVarValue(shell, command); +} +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), +setVar, shellSetVar, set var); + + +/** + * @brief shell运行命令 + * + * @param shell shell对象 + * @param command 命令 + */ +static void shellRunCommand(Shell *shell, ShellCommand *command) +{ + int returnValue; + shell->status.isActive = 1; + if (command->attr.attrs.type == SHELL_TYPE_CMD_MAIN) + { + shellRemoveParamQuotes(shell); + returnValue = command->data.cmd.function(shell->parser.paramCount, + shell->parser.param); + if (!command->attr.attrs.disableReturn) + { + shellWirteReturnValue(shell, returnValue); + } + } + else if (command->attr.attrs.type == SHELL_TYPE_CMD_FUNC) + { + returnValue = shellExtRun(shell, + command->data.cmd.function, + shell->parser.paramCount, + shell->parser.param); + if (!command->attr.attrs.disableReturn) + { + shellWirteReturnValue(shell, returnValue); + } + } + else if (command->attr.attrs.type >= SHELL_TYPE_VAR_INT + && command->attr.attrs.type <= SHELL_TYPE_VAL) + { + shellShowVar(shell, command); + } + else if (command->attr.attrs.type == SHELL_TYPE_USER) + { + shellSetUser(shell, command); + } + shell->status.isActive = 0; +} + + +/** + * @brief shell校验密码 + * + * @param shell shell对象 + */ +static void shellCheckPassword(Shell *shell) +{ + if (strcmp(shell->parser.buffer, shell->info.user->data.user.password) == 0) + { + shell->status.isChecked = 1; + shellWriteString(shell, shellText[SHELL_TEXT_INFO]); + } + else + { + shellWriteString(shell, shellText[SHELL_TEXT_PASSWORD_ERROR]); + } + shell->parser.length = 0; + shell->parser.cursor = 0; +} + + +/** + * @brief shell设置用户 + * + * @param shell shell对象 + * @param user 用户 + */ +static void shellSetUser(Shell *shell, const ShellCommand *user) +{ + shell->info.user = user; + shell->status.isChecked = + (user->data.user.password && strlen(user->data.user.password) != 0) ? 0 : 1; + + shellWriteString(shell, shellText[SHELL_TEXT_CLEAR_CONSOLE]); + if (shell->status.isChecked) + { + shellWriteString(shell, shellText[SHELL_TEXT_INFO]); + } +} + + +/** + * @brief shell写返回值 + * + * @param shell shell对象 + * @param value 返回值 + */ +static void shellWirteReturnValue(Shell *shell, int value) +{ + char buffer[11] = "0000000000"; + shellWriteString(shell, "Return: "); + shellToDec(value, buffer); + shellWriteString(shell, buffer); + shellWriteString(shell, ", 0x"); + for (short i = 0; i < 11; i++) + { + buffer[i] = '0'; + } + shellToHex(value, buffer); + shellWriteString(shell, buffer); + shellWriteString(shell, "\r\n"); +} + + +/** + * @brief shell历史记录添加 + * + * @param shell shell对象 + */ +static void shellHistoryAdd(Shell *shell) +{ + shell->history.offset = 0; + if (shell->history.number > 0 + && strcmp(shell->history.item[(shell->history.record == 0 ? + SHELL_HISTORY_MAX_NUMBER : shell->history.record) - 1], + shell->parser.buffer) == 0) + { + return; + } + if (shellStringCopy(shell->history.item[shell->history.record], + shell->parser.buffer) != 0) + { + shell->history.record++; + } + if (++shell->history.number > SHELL_HISTORY_MAX_NUMBER) + { + shell->history.number = SHELL_HISTORY_MAX_NUMBER; + } + if (shell->history.record >= SHELL_HISTORY_MAX_NUMBER) + { + shell->history.record = 0; + } +} + + +/** + * @brief shell历史记录查找 + * + * @param shell shell对象 + * @param dir 方向 {@code <0}往上查找 {@code >0}往下查找 + */ +static void shellHistory(Shell *shell, signed char dir) +{ + if (dir > 0) + { + if (shell->history.offset-- <= + -((shell->history.number > shell->history.record) ? + shell->history.number : shell->history.record)) + { + shell->history.offset = -((shell->history.number > shell->history.record) + ? shell->history.number : shell->history.record); + } + } + else if (dir < 0) + { + if (++shell->history.offset > 0) + { + shell->history.offset = 0; + return; + } + } + else + { + return; + } + shellClearCommandLine(shell); + if (shell->history.offset == 0) + { + shell->parser.cursor = shell->parser.length = 0; + } + else + { + if ((shell->parser.length = shellStringCopy(shell->parser.buffer, + shell->history.item[(shell->history.record + SHELL_HISTORY_MAX_NUMBER + + shell->history.offset) % SHELL_HISTORY_MAX_NUMBER])) == 0) + { + return; + } + shell->parser.cursor = shell->parser.length; + shellWriteString(shell, shell->parser.buffer); + } + +} + + +/** + * @brief shell 常规输入 + * + * @param shell shell 对象 + * @param data 输入字符 + */ +void shellNormalInput(Shell *shell, char data) +{ + shell->status.tabFlag = 0; + shellInsertByte(shell, data); +} + + +/** + * @brief shell上方向键输入 + * + * @param shell shell对象 + */ +void shellUp(Shell *shell) +{ + shellHistory(shell, 1); +} +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x1B5B4100, shellUp, up); + + +/** + * @brief shell下方向键输入 + * + * @param shell shell对象 + */ +void shellDown(Shell *shell) +{ + shellHistory(shell, -1); +} +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x1B5B4200, shellDown, down); + + +/** + * @brief shell右方向键输入 + * + * @param shell shell对象 + */ +void shellRight(Shell *shell) +{ + if (shell->parser.cursor < shell->parser.length) + { + shellWriteByte(shell, shell->parser.buffer[shell->parser.cursor++]); + } +} +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, +0x1B5B4300, shellRight, right); + + +/** + * @brief shell左方向键输入 + * + * @param shell shell对象 + */ +void shellLeft(Shell *shell) +{ + if (shell->parser.cursor > 0) + { + shellWriteByte(shell, '\b'); + shell->parser.cursor--; + } +} +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, +0x1B5B4400, shellLeft, left); + + +/** + * @brief shell Tab按键处理 + * + * @param shell shell对象 + */ +void shellTab(Shell *shell) +{ + unsigned short maxMatch = shell->parser.bufferSize; + unsigned short lastMatchIndex = 0; + unsigned short matchNum = 0; + unsigned short length; + + if (shell->parser.length == 0) + { + shellListAll(shell); + shellWriteCommandLine(shell); + } + else if (shell->parser.length > 0) + { + shell->parser.buffer[shell->parser.length] = 0; + ShellCommand *base = (ShellCommand *)shell->commandList.base; + for (short i = 0; i < shell->commandList.count; i++) + { + if (shellStringCompare(shell->parser.buffer, + (char *)shellGetCommandName(&base[i])) + == shell->parser.length) + { + if (matchNum != 0) + { + if (matchNum == 1) + { + shellWriteString(shell, "\r\n"); + } + shellListItem(shell, &base[lastMatchIndex]); + length = + shellStringCompare((char *)shellGetCommandName(&base[lastMatchIndex]), + (char *)shellGetCommandName(&base[i])); + maxMatch = (maxMatch > length) ? length : maxMatch; + } + lastMatchIndex = i; + matchNum++; + } + } + if (matchNum == 0) + { + return; + } + if (matchNum == 1) + { + shellClearCommandLine(shell); + } + if (matchNum != 0) + { + shell->parser.length = + shellStringCopy(shell->parser.buffer, + (char *)shellGetCommandName(&base[lastMatchIndex])); + } + if (matchNum > 1) + { + shellListItem(shell, &base[lastMatchIndex]); + shellWriteCommandLine(shell); + shell->parser.length = maxMatch; + } + shell->parser.buffer[shell->parser.length] = 0; + shell->parser.cursor = shell->parser.length; + shellWriteString(shell, shell->parser.buffer); + } + + if (SHELL_GET_TICK()) + { + if (matchNum == 1 + && shell->status.tabFlag + && SHELL_GET_TICK() - shell->info.activeTime < SHELL_DOUBLE_CLICK_TIME) + { + shellClearCommandLine(shell); + for (short i = shell->parser.length; i >= 0; i--) + { + shell->parser.buffer[i + 5] = shell->parser.buffer[i]; + } + shellStringCopy(shell->parser.buffer, "help"); + shell->parser.buffer[4] = ' '; + shell->parser.length += 5; + shell->parser.cursor = shell->parser.length; + shellWriteString(shell, shell->parser.buffer); + } + else + { + shell->status.tabFlag = 1; + } + } +} +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x09000000, shellTab, tab); + + +/** + * @brief shell 退格 + * + * @param shell shell对象 + */ +void shellBackspace(Shell *shell) +{ + shellDeleteByte(shell, 1); +} +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, +0x08000000, shellBackspace, backspace); + + +/** + * @brief shell 删除 + * + * @param shell shell对象 + */ +void shellDelete(Shell *shell) +{ + shellDeleteByte(shell, -1); +} +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, +0x7F000000, shellDelete, delete); +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, +0x1B5B337E, shellDelete, delete); + +/** + * @brief shell 回车处理 + * + * @param shell shell对象 + */ +void shellEnter(Shell *shell) +{ + if (shell->parser.length == 0) + { + shellWriteCommandLine(shell); + return; + } + + shell->parser.buffer[shell->parser.length] = 0; + + if (shell->status.isChecked) + { + shellHistoryAdd(shell); + shellParserParam(shell); + shell->parser.length = shell->parser.cursor = 0; + if (shell->parser.paramCount == 0) + { + shellWriteCommandLine(shell); + return; + } + shellWriteString(shell, "\r\n"); + + ShellCommand *command = shellSeekCommand(shell, + shell->parser.param[0], + shell->commandList.base, + 0); + if (command != NULL) + { + shellRunCommand(shell, command); + } + else + { + shellWriteString(shell, shellText[SHELL_TEXT_CMD_NOT_FOUND]); + } + } + else + { + shellCheckPassword(shell); + } + shellWriteCommandLine(shell); +} +#if SHELL_ENTER_LF == 1 +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, +0x0A000000, shellEnter, enter); +#endif +#if SHELL_ENTER_CR == 1 +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, +0x0D000000, shellEnter, enter); +#endif +#if SHELL_ENTER_CRLF == 1 +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, +0x0D0A0000, shellEnter, enter); +#endif + + +/** + * @brief shell help + * + * @param argc 参数个数 + * @param argv 参数 + */ +void shellHelp(int argc, char *argv[]) +{ + Shell *shell = shellGetCurrent(); + SHELL_ASSERT(shell, return); + if (argc == 1) + { + shellListAll(shell); + } + else if (argc > 1) + { + ShellCommand *command = shellSeekCommand(shell, + argv[1], + shell->commandList.base, + 0); + shellWriteString(shell, shellText[SHELL_TEXT_HELP_HEADER]); + shellWriteString(shell, shellGetCommandName(command)); + shellWriteString(shell, "\r\n"); + shellWriteString(shell, shellGetCommandDesc(command)); + shellWriteString(shell, "\r\n"); + } +} +SHELL_EXPORT_CMD( +SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN, +help, shellHelp, show command info\r\nhelp [cmd]); + +/** + * @brief shell 输入处理 + * + * @param shell shell对象 + * @param data 输入数据 + */ +void shellHandler(Shell *shell, char data) +{ + SHELL_ASSERT(data, return); + +#if SHELL_LOCK_TIMEOUT > 0 + if (shell->info.user->data.user.password + && strlen(shell->info.user->data.user.password) != 0 + && SHELL_GET_TICK()) + { + if (SHELL_GET_TICK() - shell->info.activeTime > SHELL_LOCK_TIMEOUT) + { + shell->status.isChecked = 0; + } + } +#endif + + /* 根据记录的按键键值计算当前字节在按键键值中的偏移 */ + char keyByteOffset = 24; + int keyFilter = 0x00000000; + if ((shell->parser.keyValue & 0x0000FF00) != 0x00000000) + { + keyByteOffset = 0; + keyFilter = 0xFFFFFF00; + } + else if ((shell->parser.keyValue & 0x00FF0000) != 0x00000000) + { + keyByteOffset = 8; + keyFilter = 0xFFFF0000; + } + else if ((shell->parser.keyValue & 0xFF000000) != 0x00000000) + { + keyByteOffset = 16; + keyFilter = 0xFF000000; + } + + /* 遍历ShellCommand列表,尝试进行按键键值匹配 */ + ShellCommand *base = (ShellCommand *)shell->commandList.base; + for (short i = 0; i < shell->commandList.count; i++) + { + /* 判断是否是按键定义并验证权限 */ + if (base[i].attr.attrs.type == SHELL_TYPE_KEY + && shellCheckPermission(shell, &(base[i])) == 0) + { + /* 对输入的字节同按键键值进行匹配 */ + if ((base[i].data.key.value & keyFilter) == shell->parser.keyValue + && (base[i].data.key.value & (0xFF << keyByteOffset)) + == (data << keyByteOffset)) + { + shell->parser.keyValue |= data << keyByteOffset; + data = 0x00; + if (keyByteOffset == 0 + || (base[i].data.key.value & (0xFF << keyByteOffset - 8)) + == 0x00000000) + { + if (base[i].data.key.function) + { + base[i].data.key.function(shell); + } + shell->parser.keyValue = 0x00000000; + break; + } + } + } + } + + if (data != 0x00) + { + shellNormalInput(shell, data); + } + + if (SHELL_GET_TICK()) + { + shell->info.activeTime = SHELL_GET_TICK(); + } +} + + +/** + * @brief shell 任务 + * + * @param param 参数(shell对象) + * + */ +void shellTask(void *param) +{ + Shell *shell = (Shell *)param; + char data; +#if SHELL_TASK_WHILE == 1 + while(1) + { +#endif + if (shell->read && shell->read(&data) == 0) + { + shellHandler(shell, data); + } +#if SHELL_TASK_WHILE == 1 + } +#endif +} + + +/** + * @brief shell 输出用户列表(shell调用) + */ +void shellUsers(void) +{ + Shell *shell = shellGetCurrent(); + if (shell) + { + shellListUser(shell); + } +} +SHELL_EXPORT_CMD( +SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, +users, shellUsers, list all user); + + +/** + * @brief shell 输出命令列表(shell调用) + */ +void shellCmds(void) +{ + Shell *shell = shellGetCurrent(); + if (shell) + { + shellListCommand(shell); + } +} +SHELL_EXPORT_CMD( +SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, +cmds, shellCmds, list all cmd); + + +/** + * @brief shell 输出变量列表(shell调用) + */ +void shellVars(void) +{ + Shell *shell = shellGetCurrent(); + if (shell) + { + shellListVar(shell); + } +} +SHELL_EXPORT_CMD( +SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, +vars, shellVars, list all var); + + +/** + * @brief shell 输出按键列表(shell调用) + */ +void shellKeys(void) +{ + Shell *shell = shellGetCurrent(); + if (shell) + { + shellListKey(shell); + } +} +SHELL_EXPORT_CMD( +SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, +keys, shellKeys, list all key); + + +/** + * @brief shell 清空控制台(shell调用) + */ +void shellClear(void) +{ + Shell *shell = shellGetCurrent(); + if (shell) + { + shellWriteString(shell, shellText[SHELL_TEXT_CLEAR_CONSOLE]); + } +} +SHELL_EXPORT_CMD( +SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, +clear, shellClear, clear console); + diff --git a/src/shell.h b/src/shell.h new file mode 100644 index 0000000..05448fc --- /dev/null +++ b/src/shell.h @@ -0,0 +1,327 @@ +/** + * @file shell.h + * @author Letter (NevermindZZT@gmail.com) + * @brief letter shell + * @version 3.0.0 + * @date 2019-12-30 + * + * @Copyright (c) 2020 Letter + * + */ + +#ifndef __SHELL_H__ +#define __SHELL_H__ + +#include "shell_cfg.h" + +#define SHELL_VERSION "3.0.0-beta1" /**< 版本号 */ + + +/** + * @brief shell 断言 + * + * @param expr 表达式 + * @param action 断言失败操作 + */ +#define SHELL_ASSERT(expr, action) \ + if (!(expr)) { \ + action; \ + } + +/** + * @brief shell 命令权限 + * + * @param permission 权限级别 + */ +#define SHELL_CMD_PERMISSION(permission) \ + (permission & 0x000000FF) + +/** + * @brief shell 命令类型 + * + * @param type 类型 + */ +#define SHELL_CMD_TYPE(type) \ + ((type & 0x0000000F) << 8) + +#define SHELL_CMD_ENABLE_UNCHECKED \ + (1 << 12) + +#define SHELL_CMD_DISABLE_RETURN \ + (1 << 13) + + +#if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000) + #define SECTION(x) __attribute__((section(x))) +#elif defined(__ICCARM__) + #define SECTION(x) @ x +#elif defined(__GNUC__) + #define SECTION(x) __attribute__((section(x))) +#else + #define SECTION(x) +#endif + +#if SHELL_USING_CMD_EXPORT == 1 + + /** + * @brief shell 命令定义 + * + * @param _attr 命令属性 + * @param _name 命令名 + * @param _func 命令函数 + * @param _desc 命令描述 + */ + #define SHELL_EXPORT_CMD(_attr, _name, _func, _desc) \ + const char shellCmd##_name[] = #_name; \ + const char shellDesc##_name[] = #_desc; \ + const ShellCommand \ + shellCommand##_name SECTION("shellCommand") = \ + { \ + .attr.value = _attr, \ + .data.cmd.name = shellCmd##_name, \ + .data.cmd.function = (int (*)())_func, \ + .data.cmd.desc = shellDesc##_name \ + } + + /** + * @brief shell 变量定义 + * + * @param _attr 变量属性 + * @param _name 变量名 + * @param _value 变量值 + * @param _desc 变量描述 + */ + #define SHELL_EXPORT_VAR(_attr, _name, _value, _desc) \ + const char shellCmd##_name[] = #_name; \ + const char shellDesc##_name[] = #_desc; \ + const ShellCommand \ + shellVar##_name SECTION("shellCommand") = \ + { \ + .attr.value = _attr, \ + .data.var.name = shellCmd##_name, \ + .data.var.value = (void *)_value, \ + .data.var.desc = shellDesc##_name \ + } + + /** + * @brief shell 用户定义 + * + * @param _attr 用户属性 + * @param _name 用户名 + * @param _password 用户密码 + * @param _desc 用户描述 + */ + #define SHELL_EXPORT_USER(_attr, _name, _password, _desc) \ + const char shellCmd##_name[] = #_name; \ + const char shellPassword##_name[] = #_password; \ + const char shellDesc##_name[] = #_desc; \ + const ShellCommand \ + shellUser##_name SECTION("shellCommand") = \ + { \ + .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \ + .data.user.name = shellCmd##_name, \ + .data.user.password = shellPassword##_name, \ + .data.user.desc = shellDesc##_name \ + } + + /** + * @brief shell 按键定义 + * + * @param _attr 按键属性 + * @param _value 按键键值 + * @param _func 按键函数 + * @param _desc 按键描述 + */ + #define SHELL_EXPORT_KEY(_attr, _value, _func, _desc) \ + const char shellDesc##_value[] = #_desc; \ + const ShellCommand \ + shellKey##_value SECTION("shellCommand") = \ + { \ + .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \ + .data.key.value = _value, \ + .data.key.function = (void (*)(Shell *))_func, \ + .data.key.desc = shellDesc##_value \ + } +#else + /** + * @brief shell 命令item定义 + * + * @param _attr 命令属性 + * @param _name 命令名 + * @param _func 命令函数 + * @param _desc 命令描述 + */ + #define SHELL_CMD_ITEM(_attr, _name, _func, _desc) \ + { \ + .attr.value = _attr, \ + .data.cmd.name = #_name, \ + .data.cmd.function = (int (*)())_func, \ + .data.cmd.desc = #_desc \ + } + + /** + * @brief shell 变量item定义 + * + * @param _attr 变量属性 + * @param _name 变量名 + * @param _value 变量值 + * @param _desc 变量描述 + */ + #define SHELL_VAR_ITEM(_attr, _name, _value, _desc) \ + { \ + .attr.value = _attr, \ + .data.var.name = #_name, \ + .data.var.value = (void *)_value, \ + .data.var.desc = #_desc \ + } + + /** + * @brief shell 用户item定义 + * + * @param _attr 用户属性 + * @param _name 用户名 + * @param _password 用户密码 + * @param _desc 用户描述 + */ + #define SHELL_USER_ITEM(_attr, _name, _password, _desc) \ + { \ + .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \ + .data.user.name = #_name, \ + .data.user.password = #_password, \ + .data.user.desc = #_desc \ + } + + /** + * @brief shell 按键item定义 + * + * @param _attr 按键属性 + * @param _value 按键键值 + * @param _func 按键函数 + * @param _desc 按键描述 + */ + #define SHELL_KEY_ITEM(_attr, _value, _func, _desc) \ + { \ + .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \ + .data.key.value = _value, \ + .data.key.function = (void (*)(Shell *))_func, \ + .data.key.desc = #_desc \ + } + + #define SHELL_EXPORT_CMD(_attr, _name, _func, _desc) + #define SHELL_EXPORT_VAR(_attr, _name, _value, _desc) + #define SHELL_EXPORT_USER(_attr, _name, _password, _desc) + #define SHELL_EXPORT_KEY(_attr, _value, _func, _desc) +#endif /** SHELL_USING_CMD_EXPORT == 1 */ + +/** + * @brief shell command类型 + */ +typedef enum +{ + SHELL_TYPE_CMD_MAIN = 0, /**< main形式命令 */ + SHELL_TYPE_CMD_FUNC, /**< C函数形式命令 */ + SHELL_TYPE_VAR_INT, /**< int型变量 */ + SHELL_TYPE_VAR_SHORT, /**< short型变量 */ + SHELL_TYPE_VAR_CHAR, /**< char型变量 */ + SHELL_TYPE_VAR_POINT, /**< 指针型变量 */ + SHELL_TYPE_VAL, /**< 常量 */ + SHELL_TYPE_USER, /**< 用户 */ + SHELL_TYPE_KEY, /**< 按键 */ +} ShellCommandType; + + +/** + * @brief Shell定义 + */ +typedef struct +{ + struct + { + const struct shell_command *user; /**< 当前用户 */ + int activeTime; /**< shell激活时间 */ + } info; + struct + { + unsigned short length; /**< 输入数据长度 */ + unsigned short cursor; /**< 当前光标位置 */ + char *buffer; /**< 输入缓冲 */ + unsigned short bufferSize; /**< 输入缓冲大小 */ + char *param[SHELL_PARAMETER_MAX_NUMBER]; /**< 参数 */ + unsigned short paramCount; /**< 参数数量 */ + int keyValue; /**< 输入按键键值 */ + } parser; + struct + { + char *item[SHELL_HISTORY_MAX_NUMBER]; /**< 历史记录 */ + unsigned short number; /**< 历史记录数 */ + unsigned short record; /**< 当前记录位置 */ + signed short offset; /**< 当前历史记录偏移 */ + } history; + struct + { + void *base; /**< 命令表基址 */ + unsigned short count; /**< 命令数量 */ + } commandList; + struct + { + unsigned char isChecked : 1; /**< 密码校验通过 */ + unsigned char isActive : 1; /**< 当前活动Shell */ + unsigned char tabFlag : 1; /**< tab标志 */ + } status; + signed char (*read)(char *); /**< shell读函数 */ + void (*write)(const char); /**< shell写函数 */ +} Shell; + + +/** + * @brief shell command定义 + */ +typedef struct shell_command +{ + union + { + struct + { + unsigned char permission : 8; /**< command权限 */ + ShellCommandType type : 4; /**< command类型 */ + unsigned char enableUnchecked : 1; /**< 在未校验密码的情况下可用 */ + unsigned char disableReturn : 1; /**< 禁用返回值输出 */ + } attrs; + int value; + } attr; /**< 属性 */ + union + { + struct + { + const char *name; /**< 命令名 */ + int (*function)(); /**< 命令执行函数 */ + const char *desc; /**< 命令描述 */ + } cmd; /**< 命令定义 */ + struct + { + const char *name; /**< 变量名 */ + void *value; /**< 变量值 */ + const char *desc; /**< 变量描述 */ + } var; /**< 变量定义 */ + struct + { + const char *name; /**< 用户名 */ + const char *password; /**< 用户密码 */ + const char *desc; /**< 用户描述 */ + } user; /**< 用户定义 */ + struct + { + int value; /**< 按键键值 */ + void (*function)(Shell *); /**< 按键执行函数 */ + const char *desc; /**< 按键描述 */ + } key; /**< 按键定义 */ + } data; +} ShellCommand; + + +void shellInit(Shell *shell, char *buffer, unsigned short size); +void shellHandler(Shell *shell, char data); +void shellTask(void *param); + +#endif + diff --git a/shell_cfg.h b/src/shell_cfg.h similarity index 57% rename from shell_cfg.h rename to src/shell_cfg.h index 2a11352..df5a3cd 100644 --- a/shell_cfg.h +++ b/src/shell_cfg.h @@ -1,9 +1,9 @@ /** * @file shell_cfg.h - * @author Letter (NevermindZZT@gmail.com) + * @author Letter (nevermindzzt@gmail.com) * @brief shell config - * @version 0.1 - * @date 2019-04-11 + * @version 3.0.0 + * @date 2019-12-31 * * @copyright (c) 2019 Letter * @@ -12,11 +12,6 @@ #ifndef __SHELL_CFG_H__ #define __SHELL_CFG_H__ -/** - * @brief 是否使用默认shell任务 - * 使能之后可以使用`shellTask()`建立shell任务,或者使用`shellTask()`进行轮询 - */ -#define SHELL_USING_TASK 0 /** * @brief 是否使用默认shell任务while循环,使能宏`SHELL_USING_TASK`后此宏有意义 @@ -27,41 +22,43 @@ /** * @brief 是否使用命令导出方式 - * 使能此宏后,可以使用`SHELL_EXPORT_CMD()`或者`SHELL_EXPORT_CMD_EX()` + * 使能此宏后,可以使用`SHELL_EXPORT_CMD()`等导出命令 * 定义shell命令,关闭此宏的情况下,需要使用命令表的方式 */ #define SHELL_USING_CMD_EXPORT 1 /** - * @brief 是否使用变量功能 - * + * @brief 是否在输入命令列表中列出用户 */ -#define SHELL_USING_VAR 0 +#define SHELL_HELP_LIST_USER 0 /** - * @brief 是否显示命令调用函数返回值 - * 使能此宏,则每次调用shell命令之后会以整形和十六进制的方式打印函数的返回值 + * @brief 是否在输入命令列表中列出变量 */ -#define SHELL_DISPLAY_RETURN 1 +#define SHELL_HELP_LIST_VAR 0 /** - * @brief 是否使用shell参数自动解析 - * 使能此宏以支持常规C函数形式的命令,shell会自动转换参数 - * 关闭此宏则支持main函数形式的命令,需要自行在函数中处理参数 + * @brief 是否在输入命令列表中列出按键 */ -#define SHELL_AUTO_PRASE 1 +#define SHELL_HELP_LIST_KEY 0 /** - * @brief 是否使用shell长帮助 - * 使能此宏以支持命令的长帮助信息 + * @brief 使用LF作为命令行回车触发 + * 可以和SHELL_ENTER_CR同时开启 */ -#define SHELL_LONG_HELP 1 +#define SHELL_ENTER_LF 1 /** - * @brief shell命令最大长度 - * 命令行可输入的最大字符长度 + * @brief 使用CR作为命令行回车触发 + * 可以和SHELL_ENTER_LF同时开启 */ -#define SHELL_COMMAND_MAX_LENGTH 50 +#define SHELL_ENTER_CR 1 + +/** + * @brief 使用CRLF作为命令行回车触发 + * 不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同时开启 + */ +#define SHELL_ENTER_CRLF 0 /** * @brief shell命令参数最大数量 @@ -99,19 +96,15 @@ #define SHELL_GET_TICK() 0 /** - * @brief shell默认提示符 + * @brief shell默认用户 */ -#define SHELL_DEFAULT_COMMAND "\r\nletter>>" +#define SHELL_DEFAULT_USER "letter" /** - * @brief 是否使用密码功能 + * @brief shell默认用户密码 + * 若默认用户不需要密码,设为"" */ -#define SHELL_USING_AUTH 0 - -/** - * @brief shell用户密码 - */ -#define SHELL_USER_PASSWORD "letter" +#define SHELL_DEFAULT_USER_PASSWORD "" /** * @brief shell自动锁定超时 @@ -119,6 +112,6 @@ * 设置为0时关闭自动锁定功能,时间单位为`SHELL_GET_TICK()`单位 * @note 使用超时锁定必须保证`SHELL_GET_TICK()`有效 */ -#define SHELL_LOCK_TIMEOUT 5 * 60 * 1000 +#define SHELL_LOCK_TIMEOUT 0 * 60 * 1000 #endif diff --git a/src/shell_cmd_list.c b/src/shell_cmd_list.c new file mode 100644 index 0000000..38f29a6 --- /dev/null +++ b/src/shell_cmd_list.c @@ -0,0 +1,98 @@ +/** + * @file shell_cmd_list.c + * @author Letter (zhengkeqiang@ut.cn) + * @brief shell cmd list + * @version 0.1 + * @date 2020-01-17 + * + * @copyright (c) 2020 Unicook + * + */ + +#include "shell.h" + +#if SHELL_USING_CMD_EXPORT != 1 + +extern int shellSetVar(char *name, int value); +extern void shellUp(Shell *shell); +extern void shellDown(Shell *shell); +extern void shellRight(Shell *shell); +extern void shellLeft(Shell *shell); +extern void shellTab(Shell *shell); +extern void shellBackspace(Shell *shell); +extern void shellDelete(Shell *shell); +extern void shellEnter(Shell *shell); +extern void shellHelp(int argc, char *argv[]); +extern void shellUsers(void); +extern void shellCmds(void); +extern void shellVars(void); +extern void shellKeys(void); +extern void shellClear(void); + + +/** + * @brief shell命令表 + * + */ +const ShellCommand shellCommandList[] = +{ + {.attr.value=SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_USER), + .data.user.name = SHELL_DEFAULT_USER, + .data.user.password = SHELL_DEFAULT_USER_PASSWORD, + .data.user.desc = "defalut user"}, + SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), + setVar, shellSetVar, set var), + SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x1B5B4100, shellUp, up), + SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x1B5B4200, shellDown, down), + SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, + 0x1B5B4300, shellRight, right), + SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, + 0x1B5B4400, shellLeft, left), + SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x09000000, shellTab, tab), + SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, + 0x08000000, shellBackspace, backspace), + SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, + 0x7F000000, shellDelete, delete), + SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, + 0x1B5B337E, shellDelete, delete), +#if SHELL_ENTER_LF == 1 + SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, + 0x0A000000, shellEnter, enter), +#endif +#if SHELL_ENTER_CR == 1 + SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, + 0x0D000000, shellEnter, enter), +#endif +#if SHELL_ENTER_CRLF == 1 + SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, + 0x0D0A0000, shellEnter, enter), +#endif + SHELL_CMD_ITEM( + SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN, + help, shellHelp, show command info\r\nhelp [cmd]), + SHELL_CMD_ITEM( + SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, + users, shellUsers, list all user), + SHELL_CMD_ITEM( + SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, + cmds, shellCmds, list all cmd), + SHELL_CMD_ITEM( + SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, + vars, shellVars, list all var), + SHELL_CMD_ITEM( + SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, + keys, shellKeys, list all key), + SHELL_CMD_ITEM( + SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, + clear, shellClear, clear console), +}; + + +/** + * @brief shell命令表大小 + * + */ +const unsigned short shellCommandCount + = sizeof(shellCommandList) / sizeof(ShellCommand); + +#endif diff --git a/shell_ext.c b/src/shell_ext.c similarity index 66% rename from shell_ext.c rename to src/shell_ext.c index bd10746..2455d44 100644 --- a/shell_ext.c +++ b/src/shell_ext.c @@ -2,8 +2,8 @@ * @file shell_ext.c * @author Letter (NevermindZZT@gmail.com) * @brief shell extensions - * @version 1.0.0 - * @date 2019-01-05 + * @version 3.0.0 + * @date 2019-12-31 * * @Copyright (c) 2019 Letter * @@ -14,6 +14,12 @@ #include "shell_ext.h" +extern ShellCommand* shellSeekCommand(Shell *shell, + const char *cmd, + ShellCommand *base, + unsigned short compareLength); +extern int shellGetVarValue(Shell *shell, ShellCommand *command); + /** * @brief 判断数字进制 * @@ -231,13 +237,38 @@ static unsigned int shellExtParseNumber(char *string) } +/** + * @brief 解析变量参数 + * + * @param shell shell对象 + * @param var 变量 + * @return unsigned int 变量值 + */ +static unsigned int shellExtParseVar(Shell *shell, char *var) +{ + ShellCommand *command = shellSeekCommand(shell, + var + 1, + shell->commandList.base, + 0); + if (command) + { + return shellGetVarValue(shell, command); + } + else + { + return 0; + } +} + + /** * @brief 解析参数 * + * @param shell shell对象 * @param string 参数 * @return unsigned int 解析结果 */ -unsigned int shellExtParsePara(char *string) +unsigned int shellExtParsePara(Shell *shell, char *string) { if (*string == '\'' && *(string + 1)) { @@ -247,12 +278,10 @@ unsigned int shellExtParsePara(char *string) { return (unsigned int)shellExtParseNumber(string); } -#if SHELL_USING_VAR == 1 else if (*string == '$' && *(string + 1)) { - return (unsigned int )shellGetVariable(shellGetCurrent(), string); + return shellExtParseVar(shell, string); } -#endif /** SHELL_USING_VAR == 1 */ else if (*string) { return (unsigned int)shellExtParseString(string); @@ -264,12 +293,13 @@ unsigned int shellExtParsePara(char *string) /** * @brief 执行命令 * + * @param shell shell对象 * @param function 执行命令的函数 * @param argc 参数个数 * @param argv 参数 * @return int 返回值 */ -int shellExtRun(shellFunction function, int argc, char *argv[]) +int shellExtRun(Shell *shell, int (*function)(), int argc, char *argv[]) { switch (argc) { @@ -277,34 +307,34 @@ int shellExtRun(shellFunction function, int argc, char *argv[]) return function(); // break; case 2: - return function(shellExtParsePara(argv[1])); + return function(shellExtParsePara(shell, argv[1])); // break; case 3: - return function(shellExtParsePara(argv[1]), shellExtParsePara(argv[2])); + return function(shellExtParsePara(shell, argv[1]), shellExtParsePara(shell, argv[2])); // break; case 4: - return function(shellExtParsePara(argv[1]), shellExtParsePara(argv[2]), - shellExtParsePara(argv[3])); + return function(shellExtParsePara(shell, argv[1]), shellExtParsePara(shell, argv[2]), + shellExtParsePara(shell, argv[3])); // break; case 5: - return function(shellExtParsePara(argv[1]), shellExtParsePara(argv[2]), - shellExtParsePara(argv[3]), shellExtParsePara(argv[4])); + return function(shellExtParsePara(shell, argv[1]), shellExtParsePara(shell, argv[2]), + shellExtParsePara(shell, argv[3]), shellExtParsePara(shell, argv[4])); // break; case 6: - return function(shellExtParsePara(argv[1]), shellExtParsePara(argv[2]), - shellExtParsePara(argv[3]), shellExtParsePara(argv[4]), - shellExtParsePara(argv[5])); + return function(shellExtParsePara(shell, argv[1]), shellExtParsePara(shell, argv[2]), + shellExtParsePara(shell, argv[3]), shellExtParsePara(shell, argv[4]), + shellExtParsePara(shell, argv[5])); // break; case 7: - return function(shellExtParsePara(argv[1]), shellExtParsePara(argv[2]), - shellExtParsePara(argv[3]), shellExtParsePara(argv[4]), - shellExtParsePara(argv[5]), shellExtParsePara(argv[6])); + return function(shellExtParsePara(shell, argv[1]), shellExtParsePara(shell, argv[2]), + shellExtParsePara(shell, argv[3]), shellExtParsePara(shell, argv[4]), + shellExtParsePara(shell, argv[5]), shellExtParsePara(shell, argv[6])); // break; case 8: - return function(shellExtParsePara(argv[1]), shellExtParsePara(argv[2]), - shellExtParsePara(argv[3]), shellExtParsePara(argv[4]), - shellExtParsePara(argv[5]), shellExtParsePara(argv[6]), - shellExtParsePara(argv[7])); + return function(shellExtParsePara(shell, argv[1]), shellExtParsePara(shell, argv[2]), + shellExtParsePara(shell, argv[3]), shellExtParsePara(shell, argv[4]), + shellExtParsePara(shell, argv[5]), shellExtParsePara(shell, argv[6]), + shellExtParsePara(shell, argv[7])); // break; default: return -1; diff --git a/shell_ext.h b/src/shell_ext.h similarity index 80% rename from shell_ext.h rename to src/shell_ext.h index 86647cf..ab954fe 100644 --- a/shell_ext.h +++ b/src/shell_ext.h @@ -2,8 +2,8 @@ * @file shell_ext.h * @author Letter (NevermindZZT@gmail.com) * @brief shell extensions - * @version 1.0.0 - * @date 2019-01-05 + * @version 3.0.0 + * @date 2019-12-31 * * @Copyright (c) 2019 Letter * @@ -27,7 +27,7 @@ typedef enum NUM_TYPE_FLOAT /**< 浮点型 */ } NUM_Type; -unsigned int shellExtParsePara(char *string); -int shellExtRun(shellFunction function, int argc, char *argv[]); +unsigned int shellExtParsePara(Shell *shell, char *string); +int shellExtRun(Shell *shell, int (*function)(), int argc, char *argv[]); #endif From 07932c5576ab5666519e6fcc36b88972eab62e4d Mon Sep 17 00:00:00 2001 From: Letter Date: Sat, 18 Jan 2020 11:53:16 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9D=83=E9=99=90?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复用户可以使用无权限命令的问题 修复打印有符号数引发异常的问题 --- README.md | 6 ++++-- src/shell.c | 38 +++++++++++++++++++++----------------- src/shell.h | 4 +++- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 93929c5..e06a876 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # letter shell 3.0 -![version](https://img.shields.io/badge/version-3.0.0_beta1-brightgreen.svg) -![build](https://img.shields.io/badge/build-2020.01.17-brightgreen.svg) +![version](https://img.shields.io/badge/version-3.0.0_beta2-brightgreen.svg) +![build](https://img.shields.io/badge/build-2020.01.18-brightgreen.svg) ![build](https://img.shields.io/badge/license-MIT-brightgreen.svg) 一个功能强大的嵌入式shell @@ -32,6 +32,8 @@ 此外3.0版本修改了命令格式和定义,2.x版本的工程需要经过简单的修改才能完成迁移 +若只需要使用基础功能,可以使用[letter shell 2.x](https://github.com/NevermindZZT/letter-shell/tree/shell2.x)版本 + ## 功能 - 命令自动补全 diff --git a/src/shell.c b/src/shell.c index 680e4e8..37c89b6 100644 --- a/src/shell.c +++ b/src/shell.c @@ -126,7 +126,6 @@ static Shell *shellList[SHELL_MAX_NUMBER] = {NULL}; static void shellAdd(Shell *shell); -static unsigned short shellWriteString(Shell *shell, const char *string); static void shellWriteCommandLine(Shell *shell); static void shellWirteReturnValue(Shell *shell, int value); static void shellShowVar(Shell *shell, ShellCommand *command); @@ -148,6 +147,8 @@ void shellInit(Shell *shell, char *buffer, unsigned short size) shell->history.offset = 0; shell->history.number = 0; shell->history.record = 0; + shell->info.user = NULL; + shell->status.isChecked = 1; shell->parser.buffer = buffer; shell->parser.bufferSize = size / (SHELL_HISTORY_MAX_NUMBER + 1); @@ -253,7 +254,7 @@ static void shellWriteByte(Shell *shell, const char data) * * @return unsigned short 写入字符的数量 */ -static unsigned short shellWriteString(Shell *shell, const char *string) +unsigned short shellWriteString(Shell *shell, const char *string) { unsigned short count = 0; SHELL_ASSERT(shell->write, return 0); @@ -329,6 +330,7 @@ static void shellWriteCommandLine(Shell *shell) signed char shellCheckPermission(Shell *shell, ShellCommand *command) { return ((!command->attr.attrs.permission + || command->attr.attrs.type == SHELL_TYPE_USER || (command->attr.attrs.permission & shell->info.user->attr.attrs.permission)) && (shell->status.isChecked @@ -345,7 +347,7 @@ signed char shellCheckPermission(Shell *shell, ShellCommand *command) * * @return signed char 转换后有效数据长度 */ -signed char shellToHex(int value, char *buffer) +signed char shellToHex(unsigned int value, char *buffer) { char byte; char i = 8; @@ -370,13 +372,13 @@ signed char shellToHex(int value, char *buffer) */ signed char shellToDec(int value, char *buffer) { - char i = 10; + char i = 11; int v = value; if (value < 0) { v = -value; } - buffer[10] = 0; + buffer[11] = 0; while (v) { buffer[--i] = v % 10 + 48; @@ -386,7 +388,7 @@ signed char shellToDec(int value, char *buffer) { buffer[--i] = '-'; } - return 10 - i; + return 11 - i; } @@ -588,7 +590,8 @@ void shellListUser(Shell *shell) for (short i = 0; i < shell->commandList.count; i++) { if (base[i].attr.attrs.type > SHELL_TYPE_VAL - && base[i].attr.attrs.type <= SHELL_TYPE_USER) + && base[i].attr.attrs.type <= SHELL_TYPE_USER + && shellCheckPermission(shell, &base[i]) == 0) { shellListItem(shell, &base[i]); } @@ -843,7 +846,8 @@ ShellCommand* shellSeekCommand(Shell *shell, ((int)base - (int)shell->commandList.base) / sizeof(ShellCommand); for (unsigned short i = 0; i < count; i++) { - if (base[i].attr.attrs.type == SHELL_TYPE_KEY) + if (base[i].attr.attrs.type == SHELL_TYPE_KEY + || shellCheckPermission(shell, &base[i]) != 0) { continue; } @@ -940,12 +944,11 @@ void shellSetVarValue(Shell *shell, ShellCommand *command, int value) */ static void shellShowVar(Shell *shell, ShellCommand *command) { - char buffer[11] = "0000000000"; + char buffer[12] = "00000000000"; int value = shellGetVarValue(shell, command); shellWriteString(shell, command->data.var.name); shellWriteString(shell, " = "); - shellToDec(value, buffer); - shellWriteString(shell, buffer); + shellWriteString(shell, &buffer[11 - shellToDec(value, buffer)]); shellWriteString(shell, ", 0x"); for (short i = 0; i < 11; i++) { @@ -990,7 +993,8 @@ int shellSetVar(char *name, int value) shellSetVarValue(shell, command, value); return shellGetVarValue(shell, command); } -SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), +SHELL_EXPORT_CMD( +SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, setVar, shellSetVar, set var); @@ -1087,10 +1091,9 @@ static void shellSetUser(Shell *shell, const ShellCommand *user) */ static void shellWirteReturnValue(Shell *shell, int value) { - char buffer[11] = "0000000000"; + char buffer[12] = "00000000000"; shellWriteString(shell, "Return: "); - shellToDec(value, buffer); - shellWriteString(shell, buffer); + shellWriteString(shell, &buffer[11 - shellToDec(value, buffer)]); shellWriteString(shell, ", 0x"); for (short i = 0; i < 11; i++) { @@ -1276,9 +1279,10 @@ void shellTab(Shell *shell) ShellCommand *base = (ShellCommand *)shell->commandList.base; for (short i = 0; i < shell->commandList.count; i++) { - if (shellStringCompare(shell->parser.buffer, + if (shellCheckPermission(shell, &base[i]) == 0 + && shellStringCompare(shell->parser.buffer, (char *)shellGetCommandName(&base[i])) - == shell->parser.length) + == shell->parser.length) { if (matchNum != 0) { diff --git a/src/shell.h b/src/shell.h index 05448fc..474cfca 100644 --- a/src/shell.h +++ b/src/shell.h @@ -14,7 +14,7 @@ #include "shell_cfg.h" -#define SHELL_VERSION "3.0.0-beta1" /**< 版本号 */ +#define SHELL_VERSION "3.0.0-beta2" /**< 版本号 */ /** @@ -320,6 +320,8 @@ typedef struct shell_command void shellInit(Shell *shell, char *buffer, unsigned short size); +unsigned short shellWriteString(Shell *shell, const char *string); +Shell* shellGetCurrent(void); void shellHandler(Shell *shell, char data); void shellTask(void *param);