From 21c737d54de9c9ca806c7a0457ed0842d2e59d1c Mon Sep 17 00:00:00 2001 From: iorebuild Date: Sun, 8 Jun 2025 20:30:31 +0800 Subject: [PATCH] first commit --- Inc/shell.h | 472 ++++++++++ Inc/shell_cfg.h | 188 ++++ Inc/shell_ext.h | 33 + Src/shell.c | 1995 +++++++++++++++++++++++++++++++++++++++++ Src/shell_cmd_list.c | 103 +++ Src/shell_companion.c | 87 ++ Src/shell_ext.c | 447 +++++++++ 7 files changed, 3325 insertions(+) create mode 100644 Inc/shell.h create mode 100644 Inc/shell_cfg.h create mode 100644 Inc/shell_ext.h create mode 100644 Src/shell.c create mode 100644 Src/shell_cmd_list.c create mode 100644 Src/shell_companion.c create mode 100644 Src/shell_ext.c diff --git a/Inc/shell.h b/Inc/shell.h new file mode 100644 index 0000000..356182c --- /dev/null +++ b/Inc/shell.h @@ -0,0 +1,472 @@ +/** + * @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.1.2" /**< 版本号 */ + + +/** + * @brief shell 断言 + * + * @param expr 表达式 + * @param action 断言失败操作 + */ +#define SHELL_ASSERT(expr, action) \ + if (!(expr)) { \ + action; \ + } + +#if SHELL_USING_LOCK == 1 +#define SHELL_LOCK(shell) shell->lock(shell) +#define SHELL_UNLOCK(shell) shell->unlock(shell) +#else +#define SHELL_LOCK(shell) +#define SHELL_UNLOCK(shell) +#endif /** SHELL_USING_LOCK == 1 */ +/** + * @brief shell 命令权限 + * + * @param permission 权限级别 + */ +#define SHELL_CMD_PERMISSION(permission) \ + (permission & 0x000000FF) + +/** + * @brief shell 命令类型 + * + * @param type 类型 + */ +#define SHELL_CMD_TYPE(type) \ + ((type & 0x0000000F) << 8) + +/** + * @brief 使能命令在未校验密码的情况下使用 + */ +#define SHELL_CMD_ENABLE_UNCHECKED \ + (1 << 12) + +/** + * @brief 禁用返回值打印 + */ +#define SHELL_CMD_DISABLE_RETURN \ + (1 << 13) + +/** + * @brief 只读属性(仅对变量生效) + */ +#define SHELL_CMD_READ_ONLY \ + (1 << 14) + +/** + * @brief 命令参数数量 + */ +#define SHELL_CMD_PARAM_NUM(num) \ + ((num & 0x0000000F)) << 16 + +#ifndef SHELL_SECTION + #if defined(__CC_ARM) || defined(__CLANG_ARM) + #define SHELL_SECTION(x) __attribute__((section(x))) + #elif defined (__IAR_SYSTEMS_ICC__) + #define SHELL_SECTION(x) @ x + #elif defined(__GNUC__) + #define SHELL_SECTION(x) __attribute__((section(x))) + #else + #define SHELL_SECTION(x) + #endif +#endif + +#ifndef SHELL_USED + #if defined(__CC_ARM) || defined(__CLANG_ARM) + #define SHELL_USED __attribute__((used)) + #elif defined (__IAR_SYSTEMS_ICC__) + #define SHELL_USED __root + #elif defined(__GNUC__) + #define SHELL_USED __attribute__((used)) + #else + #define SHELL_USED + #endif +#endif + +/** + * @brief shell float型参数转换 + */ +#define SHELL_PARAM_FLOAT(x) (*(float *)(&x)) + +/** + * @brief shell 代理函数名 + */ +#define SHELL_AGENCY_FUNC_NAME(_func) agency##_func + +/** + * @brief shell代理函数定义 + * + * @param _func 被代理的函数 + * @param ... 代理参数 + */ +#define SHELL_AGENCY_FUNC(_func, ...) \ + void SHELL_AGENCY_FUNC_NAME(_func)(int p1, int p2, int p3, int p4, int p5, int p6, int p7) \ + { _func(__VA_ARGS__); } + +#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; \ + SHELL_USED const ShellCommand \ + shellCommand##_name SHELL_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 _func 命令函数 + * @param _desc 命令描述 + * @param ... 代理参数 + */ + #define SHELL_EXPORT_CMD_AGENCY(_attr, _name, _func, _desc, ...) \ + SHELL_AGENCY_FUNC(_func, ##__VA_ARGS__) \ + SHELL_EXPORT_CMD(_attr, _name, SHELL_AGENCY_FUNC_NAME(_func), _desc) + + /** + * @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; \ + SHELL_USED const ShellCommand \ + shellVar##_name SHELL_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; \ + SHELL_USED const ShellCommand \ + shellUser##_name SHELL_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; \ + SHELL_USED const ShellCommand \ + shellKey##_value SHELL_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 \ + } + + /** + * @brief shell 代理按键定义 + * + * @param _attr 按键属性 + * @param _value 按键键值 + * @param _func 按键函数 + * @param _desc 按键描述 + * @param ... 代理参数 + */ + #define SHELL_EXPORT_KEY_AGENCY(_attr, _value, _func, _desc, ...) \ + SHELL_AGENCY_FUNC(_func, ##__VA_ARGS__) \ + SHELL_EXPORT_KEY(_attr, _value, SHELL_AGENCY_FUNC_NAME(_func), _desc) +#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_CMD_AGENCY(_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) + #define SHELL_EXPORT_KEY_AGENCY(_attr, _name, _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_STRING, /**< string型变量 */ + SHELL_TYPE_VAR_POINT, /**< 指针型变量 */ + SHELL_TYPE_VAR_NODE, /**< 节点变量 */ + SHELL_TYPE_USER, /**< 用户 */ + SHELL_TYPE_KEY, /**< 按键 */ +} ShellCommandType; + + +/** + * @brief Shell定义 + */ +typedef struct shell_def +{ + struct + { + const struct shell_command *user; /**< 当前用户 */ + int activeTime; /**< shell激活时间 */ + char *path; /**< 当前shell路径 */ + #if SHELL_USING_COMPANION == 1 + struct shell_companion_object *companions; /**< 伴生对象 */ + #endif + #if SHELL_KEEP_RETURN_VALUE == 1 + int retVal; /**< 返回值 */ + #endif + } info; + struct + { + unsigned short length; /**< 输入数据长度 */ + unsigned short cursor; /**< 当前光标位置 */ + char *buffer; /**< 输入缓冲 */ + char *param[SHELL_PARAMETER_MAX_NUMBER]; /**< 参数 */ + unsigned short bufferSize; /**< 输入缓冲大小 */ + unsigned short paramCount; /**< 参数数量 */ + int keyValue; /**< 输入按键键值 */ + } parser; +#if SHELL_HISTORY_MAX_NUMBER > 0 + struct + { + char *item[SHELL_HISTORY_MAX_NUMBER]; /**< 历史记录 */ + unsigned short number; /**< 历史记录数 */ + unsigned short record; /**< 当前记录位置 */ + signed short offset; /**< 当前历史记录偏移 */ + } history; +#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */ + struct + { + void *base; /**< 命令表基址 */ + unsigned short count; /**< 命令数量 */ + } commandList; + struct + { + unsigned char isChecked : 1; /**< 密码校验通过 */ + unsigned char isActive : 1; /**< 当前活动Shell */ + unsigned char tabFlag : 1; /**< tab标志 */ + } status; + signed short (*read)(char *, unsigned short); /**< shell读函数 */ + signed short (*write)(char *, unsigned short); /**< shell写函数 */ +#if SHELL_USING_LOCK == 1 + int (*lock)(struct shell_def *); /**< shell 加锁 */ + int (*unlock)(struct shell_def *); /**< shell 解锁 */ +#endif +} 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; /**< 禁用返回值输出 */ + unsigned char readOnly : 1; /**< 只读 */ + unsigned char reserve : 1; /**< 保留 */ + unsigned char paramNum : 4; /**< 参数数量 */ + } 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; + +/** + * @brief shell节点变量属性 + */ +typedef struct +{ + void *var; /**< 变量引用 */ + int (*get)(); /**< 变量get方法 */ + int (*set)(); /**< 变量set方法 */ +} ShellNodeVarAttr; + + +#define shellSetPath(_shell, _path) (_shell)->info.path = _path +#define shellGetPath(_shell) ((_shell)->info.path) + +#define shellDeInit(shell) shellRemove(shell) + +void shellInit(Shell *shell, char *buffer, unsigned short size); +void shellRemove(Shell *shell); +unsigned short shellWriteString(Shell *shell, const char *string); +void shellPrint(Shell *shell, char *fmt, ...); +void shellScan(Shell *shell, char *fmt, ...); +Shell* shellGetCurrent(void); +void shellHandler(Shell *shell, char data); +void shellWriteEndLine(Shell *shell, char *buffer, int len); +void shellTask(void *param); +int shellRun(Shell *shell, const char *cmd); + + + +#if SHELL_USING_COMPANION == 1 +/** + * @brief shell伴生对象定义 + */ +typedef struct shell_companion_object +{ + int id; /**< 伴生对象ID */ + void *obj; /**< 伴生对象 */ + struct shell_companion_object *next; /**< 下一个伴生对象 */ +} ShellCompanionObj; + + +signed char shellCompanionAdd(Shell *shell, int id, void *object); +signed char shellCompanionDel(Shell *shell, int id); +void *shellCompanionGet(Shell *shell, int id); +#endif + +#endif + + diff --git a/Inc/shell_cfg.h b/Inc/shell_cfg.h new file mode 100644 index 0000000..874043d --- /dev/null +++ b/Inc/shell_cfg.h @@ -0,0 +1,188 @@ +/** + * @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__ + + +/** + * @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 是否使用shell伴生对象 + * 一些扩展的组件(文件系统支持,日志工具等)需要使用伴生对象 + */ +#define SHELL_USING_COMPANION 0 + +/** + * @brief 支持shell尾行模式 + */ +#define SHELL_SUPPORT_END_LINE 0 + +/** + * @brief 是否在输出命令列表中列出用户 + */ +#define SHELL_HELP_LIST_USER 0 + +/** + * @brief 是否在输出命令列表中列出变量 + */ +#define SHELL_HELP_LIST_VAR 0 + +/** + * @brief 是否在输出命令列表中列出按键 + */ +#define SHELL_HELP_LIST_KEY 0 + +/** + * @brief 是否在输出命令列表中展示命令权限 + */ +#define SHELL_HELP_SHOW_PERMISSION 1 + +/** + * @brief 使用LF作为命令行回车触发 + * 可以和SHELL_ENTER_CR同时开启 + */ +#define SHELL_ENTER_LF 1 + +/** + * @brief 使用CR作为命令行回车触发 + * 可以和SHELL_ENTER_LF同时开启 + */ +#define SHELL_ENTER_CR 1 + +/** + * @brief 使用CRLF作为命令行回车触发 + * 不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同时开启 + */ +#define SHELL_ENTER_CRLF 0 + +/** + * @brief 使用执行未导出函数的功能 + * 启用后,可以通过`exec [addr] [args]`直接执行对应地址的函数 + * @attention 如果地址错误,可能会直接引起程序崩溃 + */ +#define SHELL_EXEC_UNDEF_FUNC 0 + +/** + * @brief shell命令参数最大数量 + * 包含命令名在内,超过16个参数并且使用了参数自动转换的情况下,需要修改源码 + */ +#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 快速帮助 + * 作用于双击tab的场景,当使能此宏时,双击tab不会对命令进行help补全,而是直接显示对应命令的帮助信息 + */ +#define SHELL_QUICK_HELP 1 + +/** + * @brief 保存命令返回值 + * 开启后会默认定义一个`RETVAL`变量,会保存上一次命令执行的返回值,可以在随后的命令中进行调用 + * 如果命令的`SHELL_CMD_DISABLE_RETURN`标志被设置,则该命令不会更新`RETVAL` + */ +#define SHELL_KEEP_RETURN_VALUE 0 + +/** + * @brief 管理的最大shell数量 + */ +#define SHELL_MAX_NUMBER 5 + +/** + * @brief shell格式化输出的缓冲大小 + * 为0时不使用shell格式化输出 + */ +#define SHELL_PRINT_BUFFER 128 + +/** + * @brief shell格式化输入的缓冲大小 + * 为0时不使用shell格式化输入 + * @note shell格式化输入会阻塞shellTask, 仅适用于在有操作系统的情况下使用 + */ +#define SHELL_SCAN_BUFFER 0 + +/** + * @brief 获取系统时间(ms) + * 定义此宏为获取系统Tick,如`HAL_GetTick()` + * @note 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定 + */ +#define SHELL_GET_TICK() 0 + +/** + * @brief 使用锁 + * @note 使用shell锁时,需要对加锁和解锁进行实现 + */ +#define SHELL_USING_LOCK 0 + +/** + * @brief shell内存分配 + * shell本身不需要此接口,若使用shell伴生对象,需要进行定义 + */ +#define SHELL_MALLOC(size) 0 + +/** + * @brief shell内存释放 + * shell本身不需要此接口,若使用shell伴生对象,需要进行定义 + */ +#define SHELL_FREE(obj) 0 + +/** + * @brief 是否显示shell信息 + */ +#define SHELL_SHOW_INFO 1 + +/** + * @brief 是否在登录后清除命令行 + */ +#define SHELL_CLS_WHEN_LOGIN 1 + +/** + * @brief shell默认用户 + */ +#define SHELL_DEFAULT_USER "letter" + +/** + * @brief shell默认用户密码 + * 若默认用户不需要密码,设为"" + */ +#define SHELL_DEFAULT_USER_PASSWORD "" + +/** + * @brief shell自动锁定超时 + * shell当前用户密码有效的时候生效,超时后会自动重新锁定shell + * 设置为0时关闭自动锁定功能,时间单位为`SHELL_GET_TICK()`单位 + * @note 使用超时锁定必须保证`SHELL_GET_TICK()`有效 + */ +#define SHELL_LOCK_TIMEOUT 0 * 60 * 1000 + +#endif diff --git a/Inc/shell_ext.h b/Inc/shell_ext.h new file mode 100644 index 0000000..2e6158c --- /dev/null +++ b/Inc/shell_ext.h @@ -0,0 +1,33 @@ +/** + * @file shell_ext.h + * @author Letter (NevermindZZT@gmail.com) + * @brief shell extensions + * @version 3.0.0 + * @date 2019-12-31 + * + * @copyright (c) 2019 Letter + * + */ + +#ifndef __SHELL_EXT_H__ +#define __SHELL_EXT_H__ + +#include "shell.h" + +/** + * @brief 数字类型 + * + */ +typedef enum +{ + NUM_TYPE_DEC, /**< 十进制整型 */ + NUM_TYPE_BIN, /**< 二进制整型 */ + NUM_TYPE_OCT, /**< 八进制整型 */ + NUM_TYPE_HEX, /**< 十六进制整型 */ + NUM_TYPE_FLOAT /**< 浮点型 */ +} ShellNumType; + +unsigned int shellExtParsePara(Shell *shell, char *string); +int shellExtRun(Shell *shell, ShellCommand *command, int argc, char *argv[]); + +#endif diff --git a/Src/shell.c b/Src/shell.c new file mode 100644 index 0000000..a766751 --- /dev/null +++ b/Src/shell.c @@ -0,0 +1,1995 @@ +/** + * @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[] = "default user"; +SHELL_USED const ShellCommand shellUserDefault SHELL_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 + #if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000) + extern const unsigned int shellCommand$$Base; + extern const unsigned int shellCommand$$Limit; + #elif defined(__ICCARM__) || defined(__ICCRX__) + #pragma section="shellCommand" + #elif defined(__GNUC__) + extern const unsigned int _shell_command_start; + extern const unsigned int _shell_command_end; + #endif +#else + extern const ShellCommand shellCommandList[]; + extern const unsigned short shellCommandCount; +#endif + + +/** + * @brief shell 常量文本索引 + */ +enum +{ +#if SHELL_SHOW_INFO == 1 + SHELL_TEXT_INFO, /**< shell信息 */ +#endif + 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_VAR_READ_ONLY_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_CLEAR_LINE, /**< 清空当前行 */ + SHELL_TEXT_TYPE_CMD, /**< 命令类型 */ + SHELL_TEXT_TYPE_VAR, /**< 变量类型 */ + SHELL_TEXT_TYPE_USER, /**< 用户类型 */ + SHELL_TEXT_TYPE_KEY, /**< 按键类型 */ + SHELL_TEXT_TYPE_NONE, /**< 非法类型 */ +#if SHELL_EXEC_UNDEF_FUNC == 1 + SHELL_TEXT_PARAM_ERROR, /**< 参数错误 */ +#endif +}; + + +static const char *shellText[] = +{ +#if SHELL_SHOW_INFO == 1 + [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", +#endif + [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_VAR_READ_ONLY_CANNOT_MODIFY] = + "can't set read only var\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\npassword error\r\n", + [SHELL_TEXT_CLEAR_CONSOLE] = + "\033[2J\033[1H", + [SHELL_TEXT_CLEAR_LINE] = + "\033[2K\r", + [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", +#if SHELL_EXEC_UNDEF_FUNC == 1 + [SHELL_TEXT_PARAM_ERROR] = + "Parameter error\r\n", +#endif +}; + + +/** + * @brief shell对象表 + */ +static Shell *shellList[SHELL_MAX_NUMBER] = {NULL}; + + +static void shellAdd(Shell *shell); +static void shellWritePrompt(Shell *shell, unsigned char newline); +static void shellWriteReturnValue(Shell *shell, int value); +static int 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); +static void shellWriteCommandHelp(Shell *shell, char *cmd); + +/** + * @brief shell 初始化 + * + * @param shell shell对象 + */ +void shellInit(Shell *shell, char *buffer, unsigned short size) +{ + shell->parser.length = 0; + shell->parser.cursor = 0; + shell->info.user = NULL; + shell->status.isChecked = 1; + + shell->parser.buffer = buffer; + shell->parser.bufferSize = size / (SHELL_HISTORY_MAX_NUMBER + 1); + +#if SHELL_HISTORY_MAX_NUMBER > 0 + shell->history.offset = 0; + shell->history.number = 0; + shell->history.record = 0; + for (short i = 0; i < SHELL_HISTORY_MAX_NUMBER; i++) + { + shell->history.item[i] = buffer + shell->parser.bufferSize * (i + 1); + } +#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */ + +#if SHELL_USING_CMD_EXPORT == 1 + #if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000) + shell->commandList.base = (ShellCommand *)(&shellCommand$$Base); + shell->commandList.count = ((unsigned int)(&shellCommand$$Limit) + - (unsigned int)(&shellCommand$$Base)) + / sizeof(ShellCommand); + + #elif defined(__ICCARM__) || defined(__ICCRX__) + 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__) + 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)); + shellWritePrompt(shell, 1); +} + + +/** + * @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 + * + * @param shell shell对象 + * + */ +void shellRemove(Shell *shell) +{ + for (short i = 0; i < SHELL_MAX_NUMBER; i++) + { + if (shellList[i] == shell) + { + shellList[i] = NULL; + 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, char data) +{ + shell->write(&data, 1); +} + + +/** + * @brief shell 写字符串 + * + * @param shell shell对象 + * @param string 字符串数据 + * + * @return unsigned short 写入字符的数量 + */ +unsigned short shellWriteString(Shell *shell, const char *string) +{ + unsigned short count = 0; + const char *p = string; + SHELL_ASSERT(shell->write, return 0); + while(*p++) + { + count ++; + } + return shell->write((char *)string, count); +} + + +/** + * @brief shell 写命令描述字符串 + * + * @param shell shell对象 + * @param string 字符串数据 + * + * @return unsigned short 写入字符的数量 + */ +static unsigned short shellWriteCommandDesc(Shell *shell, const char *string) +{ + unsigned short count = 0; + const char *p = string; + SHELL_ASSERT(shell->write, return 0); + while (*p && *p != '\r' && *p != '\n') + { + p++; + count++; + } + + if (count > 36) + { + shell->write((char *)string, 36); + shell->write("...", 3); + } + else + { + shell->write((char *)string, count); + } + return count > 36 ? 36 : 39; +} + + +/** + * @brief shell写命令提示符 + * + * @param shell shell对象 + * @param newline 新行 + * + */ +static void shellWritePrompt(Shell *shell, unsigned char newline) +{ + if (shell->status.isChecked) + { + if (newline) + { + shellWriteString(shell, "\r\n"); + } + shellWriteString(shell, shell->info.user->data.user.name); + shellWriteString(shell, ":"); + shellWriteString(shell, shell->info.path ? shell->info.path : "/"); + shellWriteString(shell, "$ "); + } + else + { + shellWriteString(shell, shellText[SHELL_TEXT_PASSWORD_HINT]); + } +} + + +#if SHELL_PRINT_BUFFER > 0 +/** + * @brief shell格式化输出 + * + * @param shell shell对象 + * @param fmt 格式化字符串 + * @param ... 参数 + */ +void shellPrint(Shell *shell, char *fmt, ...) +{ + char buffer[SHELL_PRINT_BUFFER]; + va_list vargs; + + SHELL_ASSERT(shell, return); + + va_start(vargs, fmt); + vsnprintf(buffer, SHELL_PRINT_BUFFER - 1, fmt, vargs); + va_end(vargs); + + shellWriteString(shell, buffer); +} +#endif + + +#if SHELL_SCAN_BUFFER > 0 +/** + * @brief shell格式化输入 + * + * @param shell shell对象 + * @param fmt 格式化字符串 + * @param ... 参数 + */ +void shellScan(Shell *shell, char *fmt, ...) +{ + char buffer[SHELL_SCAN_BUFFER]; + va_list vargs; + short index = 0; + + SHELL_ASSERT(shell, return); + + if (shell->read) + { + do { + if (shell->read(&buffer[index], 1) == 1) + { + shell->write(&buffer[index], 1); + index++; + } + } while (buffer[index -1] != '\r' && buffer[index -1] != '\n' && index < SHELL_SCAN_BUFFER); + shellWriteString(shell, "\r\n"); + buffer[index] = '\0'; + } + + va_start(vargs, fmt); + vsscanf(buffer, fmt, vargs); + va_end(vargs); +} +#endif + + +/** + * @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.type == SHELL_TYPE_USER + || (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(unsigned int value, char *buffer) +{ + char byte; + unsigned 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) +{ + unsigned char i = 11; + int v = value; + if (value < 0) + { + v = -value; + } + buffer[11] = 0; + while (v) + { + buffer[--i] = v % 10 + 48; + v /= 10; + } + if (value < 0) + { + buffer[--i] = '-'; + } + if (value == 0) { + buffer[--i] = '0'; + } + return 11 - 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 (unsigned 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_VAR_NODE) + { + 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_VAR_NODE) + { + 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_VAR_NODE) + { + 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]); + } +#if SHELL_HELP_SHOW_PERMISSION == 1 + shellWriteString(shell, " "); + for (signed char i = 7; i >= 0; i--) + { + shellWriteByte(shell, item->attr.attrs.permission & (1 << i) ? 'x' : '-'); + } +#endif + 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_VAR_NODE + && 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_VAR_NODE + && base[i].attr.attrs.type <= SHELL_TYPE_USER + && shellCheckPermission(shell, &base[i]) == 0) + { + 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]); + shellWritePrompt(shell, 1); + 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; + + for (short i = 0; i < SHELL_PARAMETER_MAX_NUMBER; i++) + { + shell->parser.param[i] = NULL; + } + + 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 (shell->parser.buffer[i] == '\"') + { + quotes = quotes ? 0 : 1; + } + if (record == 1) + { + if (shell->parser.paramCount < SHELL_PARAMETER_MAX_NUMBER) + { + 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 + || shellCheckPermission(shell, &base[i]) != 0) + { + 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 = 0; + 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_STRING: + case SHELL_TYPE_VAR_POINT: + value = (int)(command->data.var.value); + break; + case SHELL_TYPE_VAR_NODE: + value = ((ShellNodeVarAttr *)command->data.var.value)->get ? + ((ShellNodeVarAttr *)command->data.var.value) + ->get(((ShellNodeVarAttr *)command->data.var.value)->var) : 0; + break; + default: + break; + } + return value; +} + + +/** + * @brief shell设置变量值 + * + * @param shell shell对象 + * @param command 命令 + * @param value 值 + * @return int 返回变量值 + */ +int shellSetVarValue(Shell *shell, ShellCommand *command, int value) +{ + if (command->attr.attrs.readOnly) + { + shellWriteString(shell, shellText[SHELL_TEXT_VAR_READ_ONLY_CANNOT_MODIFY]); + } + else + { + 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_STRING: + shellStringCopy(((char *)(command->data.var.value)), (char *)value); + break; + case SHELL_TYPE_VAR_POINT: + shellWriteString(shell, shellText[SHELL_TEXT_POINT_CANNOT_MODIFY]); + break; + case SHELL_TYPE_VAR_NODE: + if (((ShellNodeVarAttr *)command->data.var.value)->set) + { + if (((ShellNodeVarAttr *)command->data.var.value)->var) + { + ((ShellNodeVarAttr *)command->data.var.value) + ->set(((ShellNodeVarAttr *)command->data.var.value)->var, value); + } + else + { + ((ShellNodeVarAttr *)command->data.var.value)->set(value); + } + } + break; + default: + break; + } + } + return shellShowVar(shell, command); +} + + +/** + * @brief shell变量输出 + * + * @param shell shell对象 + * @param command 命令 + * @return int 返回变量值 + */ +static int shellShowVar(Shell *shell, ShellCommand *command) +{ + char buffer[12] = "00000000000"; + int value = shellGetVarValue(shell, command); + + shellWriteString(shell, command->data.var.name); + shellWriteString(shell, " = "); + + switch (command->attr.attrs.type) + { + case SHELL_TYPE_VAR_STRING: + shellWriteString(shell, "\""); + shellWriteString(shell, (char *)value); + shellWriteString(shell, "\""); + break; + // case SHELL_TYPE_VAR_INT: + // case SHELL_TYPE_VAR_SHORT: + // case SHELL_TYPE_VAR_CHAR: + // case SHELL_TYPE_VAR_POINT: + default: + shellWriteString(shell, &buffer[11 - shellToDec(value, buffer)]); + shellWriteString(shell, ", 0x"); + for (short i = 0; i < 11; i++) + { + buffer[i] = '0'; + } + shellToHex(value, buffer); + shellWriteString(shell, buffer); + break; + } + + shellWriteString(shell, "\r\n"); + return value; +} + + +/** + * @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_VAR_NODE) + { + shellWriteString(shell, name); + shellWriteString(shell, shellText[SHELL_TEXT_NOT_VAR]); + return 0; + } + return shellSetVarValue(shell, command, value); +} +SHELL_EXPORT_CMD( +SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, +setVar, shellSetVar, set var); + + +/** + * @brief shell运行命令 + * + * @param shell shell对象 + * @param command 命令 + * + * @return unsigned int 命令返回值 + */ +unsigned int shellRunCommand(Shell *shell, ShellCommand *command) +{ + int returnValue = 0; + 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) + { + shellWriteReturnValue(shell, returnValue); + } + } + else if (command->attr.attrs.type == SHELL_TYPE_CMD_FUNC) + { + returnValue = shellExtRun(shell, + command, + shell->parser.paramCount, + shell->parser.param); + if (!command->attr.attrs.disableReturn) + { + shellWriteReturnValue(shell, returnValue); + } + } + else if (command->attr.attrs.type >= SHELL_TYPE_VAR_INT + && command->attr.attrs.type <= SHELL_TYPE_VAR_NODE) + { + shellShowVar(shell, command); + } + else if (command->attr.attrs.type == SHELL_TYPE_USER) + { + shellSetUser(shell, command); + } + shell->status.isActive = 0; + + return returnValue; +} + + +/** + * @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; + #if SHELL_SHOW_INFO == 1 + shellWriteString(shell, shellText[SHELL_TEXT_INFO]); + #endif + } + 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) + && (shell->parser.paramCount < 2 + || strcmp(user->data.user.password, shell->parser.param[1]) != 0)) + ? 0 : 1; + +#if SHELL_CLS_WHEN_LOGIN == 1 + shellWriteString(shell, shellText[SHELL_TEXT_CLEAR_CONSOLE]); +#endif +#if SHELL_SHOW_INFO == 1 + if (shell->status.isChecked) + { + shellWriteString(shell, shellText[SHELL_TEXT_INFO]); + } +#endif +} + + +/** + * @brief shell写返回值 + * + * @param shell shell对象 + * @param value 返回值 + */ +static void shellWriteReturnValue(Shell *shell, int value) +{ + char buffer[12] = "00000000000"; + shellWriteString(shell, "Return: "); + shellWriteString(shell, &buffer[11 - shellToDec(value, buffer)]); + shellWriteString(shell, ", 0x"); + for (short i = 0; i < 11; i++) + { + buffer[i] = '0'; + } + shellToHex(value, buffer); + shellWriteString(shell, buffer); + shellWriteString(shell, "\r\n"); +#if SHELL_KEEP_RETURN_VALUE == 1 + shell->info.retVal = value; +#endif +} + + +#if SHELL_HISTORY_MAX_NUMBER > 0 +/** + * @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); + } + +} +#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */ + + +/** + * @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 shellExec(Shell *shell) +{ + + if (shell->parser.length == 0) + { + return; + } + + shell->parser.buffer[shell->parser.length] = 0; + + if (shell->status.isChecked) + { + #if SHELL_HISTORY_MAX_NUMBER > 0 + shellHistoryAdd(shell); + #endif /** SHELL_HISTORY_MAX_NUMBER > 0 */ + shellParserParam(shell); + shell->parser.length = shell->parser.cursor = 0; + if (shell->parser.paramCount == 0) + { + 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); + } +} + + +#if SHELL_HISTORY_MAX_NUMBER > 0 +/** + * @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); +#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */ + + +/** + * @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); + shellWritePrompt(shell, 1); + } + 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 (shellCheckPermission(shell, &base[i]) == 0 + && 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]); + shellWritePrompt(shell, 1); + 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) + { + #if SHELL_QUICK_HELP == 1 + shellWriteString(shell, "\r\n"); + shellWriteCommandHelp(shell, shell->parser.buffer); + shellWritePrompt(shell, 1); + shellWriteString(shell, shell->parser.buffer); + #else + 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); + #endif + } + 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); +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, +0x7F000000, 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, +0x1B5B337E, shellDelete, delete); + + +/** + * @brief shell 回车处理 + * + * @param shell shell对象 + */ +void shellEnter(Shell *shell) +{ + shellExec(shell); + shellWritePrompt(shell, 1); +} +#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 写命令帮助信息 + * + * @param shell shell对象 + * @param cmd 命令字符串 + */ +static void shellWriteCommandHelp(Shell *shell, char *cmd) +{ + ShellCommand *command = shellSeekCommand(shell, + cmd, + shell->commandList.base, + 0); + if (command) + { + shellWriteString(shell, shellText[SHELL_TEXT_HELP_HEADER]); + shellWriteString(shell, shellGetCommandName(command)); + shellWriteString(shell, "\r\n"); + shellWriteString(shell, shellGetCommandDesc(command)); + shellWriteString(shell, "\r\n"); + } + else + { + shellWriteString(shell, shellText[SHELL_TEXT_CMD_NOT_FOUND]); + } +} + +/** + * @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) + { + shellWriteCommandHelp(shell, argv[1]); + } +} +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); + SHELL_LOCK(shell); + +#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) + { + shell->parser.keyValue = 0x00000000; + shellNormalInput(shell, data); + } + + if (SHELL_GET_TICK()) + { + shell->info.activeTime = SHELL_GET_TICK(); + } + SHELL_UNLOCK(shell); +} + + +#if SHELL_SUPPORT_END_LINE == 1 +void shellWriteEndLine(Shell *shell, char *buffer, int len) +{ + SHELL_LOCK(shell); + if (!shell->status.isActive) + { + shellWriteString(shell, shellText[SHELL_TEXT_CLEAR_LINE]); + } + shell->write(buffer, len); + + if (!shell->status.isActive) + { + shellWritePrompt(shell, 0); + if (shell->parser.length > 0) + { + shellWriteString(shell, shell->parser.buffer); + for (short i = 0; i < shell->parser.length - shell->parser.cursor; i++) + { + shellWriteByte(shell, '\b'); + } + } + } + SHELL_UNLOCK(shell); +} +#endif /** SHELL_SUPPORT_END_LINE == 1 */ + + +/** + * @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, 1) == 1) + { + 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); + + +/** + * @brief shell执行命令 + * + * @param shell shell对象 + * @param cmd 命令字符串 + * @return int 返回值 + */ +int shellRun(Shell *shell, const char *cmd) +{ + SHELL_ASSERT(shell && cmd, return -1); + char active = shell->status.isActive; + if (strlen(cmd) > shell->parser.bufferSize - 1) + { + shellWriteString(shell, shellText[SHELL_TEXT_CMD_TOO_LONG]); + return -1; + } + else + { + shell->parser.length = shellStringCopy(shell->parser.buffer, (char *)cmd); + shellExec(shell); + shell->status.isActive = active; + return 0; + } +} + + +#if SHELL_EXEC_UNDEF_FUNC == 1 +/** + * @brief shell执行未定义函数 + * + * @param argc 参数个数 + * @param argv 参数 + * @return int 返回值 + */ +int shellExecute(int argc, char *argv[]) +{ + Shell *shell = shellGetCurrent(); + if (shell && argc >= 2) + { + int (*func)() = (int (*)())shellExtParsePara(shell, argv[1]); + ShellCommand command = { + .attr.value = SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC) + |SHELL_CMD_DISABLE_RETURN, + .data.cmd.function = func, + }; + return shellExtRun(shell, &command, argc - 1, &argv[1]); + } + else + { + shellWriteString(shell, shellText[SHELL_TEXT_PARAM_ERROR]); + return -1; + } +} +SHELL_EXPORT_CMD( +SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN, +exec, shellExecute, execute function undefined); +#endif + +#if SHELL_KEEP_RETURN_VALUE == 1 +/** + * @brief shell返回值获取 + * 获取上一次执行的命令的返回值 + * + * @return int 返回值 + */ +static int shellRetValGet() +{ + Shell *shell = shellGetCurrent(); + return shell ? shell->info.retVal : 0; +} +static ShellNodeVarAttr shellRetVal = { + .get = shellRetValGet +}; +SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_NODE)|SHELL_CMD_READ_ONLY, +RETVAL, &shellRetVal, return value of last command); +#endif /** SHELL_KEEP_RETURN_VALUE == 1 */ diff --git a/Src/shell_cmd_list.c b/Src/shell_cmd_list.c new file mode 100644 index 0000000..139a531 --- /dev/null +++ b/Src/shell_cmd_list.c @@ -0,0 +1,103 @@ +/** + * @file shell_cmd_list.c + * @author Letter (NevermindZZT@gmail.com) + * @brief shell cmd list + * @version 3.0.0 + * @date 2020-01-17 + * + * @copyright (c) 2020 Letter + * + */ + +#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); +#if SHELL_EXEC_UNDEF_FUNC == 1 +extern int shellExecute(int argc, char *argv[]); +#endif + +SHELL_AGENCY_FUNC(shellRun, shellGetCurrent(), (const char *)p1); + + +/** + * @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 = "default 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), + SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN, + sh, SHELL_AGENCY_FUNC_NAME(shellRun), run command directly), +#if SHELL_EXEC_UNDEF_FUNC == 1 + SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN, + exec, shellExecute, execute function undefined), +#endif +}; + + +/** + * @brief shell命令表大小 + * + */ +const unsigned short shellCommandCount + = sizeof(shellCommandList) / sizeof(ShellCommand); + +#endif diff --git a/Src/shell_companion.c b/Src/shell_companion.c new file mode 100644 index 0000000..eb494c7 --- /dev/null +++ b/Src/shell_companion.c @@ -0,0 +1,87 @@ +/** + * @file shell_companion.c + * @author Letter (nevermindzzt@gmail.com) + * @brief shell companion object support + * @version 3.0.3 + * @date 2020-07-22 + * + * @copyright (c) 2020 Letter + * + */ + #include "shell.h" + +#if SHELL_USING_COMPANION == 1 +/** + * @brief shell添加伴生对象 + * + * @param shell shell对象 + * @param id 伴生对象ID + * @param object 伴生对象 + * @return signed char 0 添加成功 -1 添加失败 + */ +signed char shellCompanionAdd(Shell *shell, int id, void *object) +{ + ShellCompanionObj *companions = shell->info.companions; + ShellCompanionObj *node = SHELL_MALLOC(sizeof(ShellCompanionObj)); + SHELL_ASSERT(node, return -1); + node->id = id; + node->obj = object; + node->next = companions; + shell->info.companions = node; + return 0; +} + +/** + * @brief shell删除伴生对象 + * + * @param shell shell对象 + * @param id 伴生对象ID + * @return signed char 0 删除成功 -1 无匹配对象 + */ +signed char shellCompanionDel(Shell *shell, int id) +{ + ShellCompanionObj *companions = shell->info.companions; + ShellCompanionObj *front = companions; + while (companions) + { + if (companions->id == id) + { + if (companions == shell->info.companions && !(companions->next)) + { + shell->info.companions = (void *)0; + } + else + { + front->next = companions->next; + } + SHELL_FREE(companions); + return 0; + } + front = companions; + companions = companions->next; + } + return -1; +} + +/** + * @brief shell获取伴生对象 + * + * @param shell shell对象 + * @param id 伴生对象ID + * @return void* 伴生对象,无匹配对象时返回NULL + */ +void *shellCompanionGet(Shell *shell, int id) +{ + SHELL_ASSERT(shell, return (void *)0); + ShellCompanionObj *companions = shell->info.companions; + while (companions) + { + if (companions->id == id) + { + return companions->obj; + } + companions = companions->next; + } + return (void *)0; +} +#endif /** SHELL_USING_COMPANION == 1 */ diff --git a/Src/shell_ext.c b/Src/shell_ext.c new file mode 100644 index 0000000..7fbbc61 --- /dev/null +++ b/Src/shell_ext.c @@ -0,0 +1,447 @@ +/** + * @file shell_ext.c + * @author Letter (NevermindZZT@gmail.com) + * @brief shell extensions + * @version 3.0.0 + * @date 2019-12-31 + * + * @copyright (c) 2019 Letter + * + */ + +#include "shell_cfg.h" +#include "shell.h" +#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 判断数字进制 + * + * @param string 参数字符串 + * @return ShellNumType 进制 + */ +static ShellNumType shellExtNumType(char *string) +{ + char *p = string; + ShellNumType type = NUM_TYPE_DEC; + + if ((*p == '0') && ((*(p + 1) == 'x') || (*(p + 1) == 'X'))) + { + type = NUM_TYPE_HEX; + } + else if ((*p == '0') && ((*(p + 1) == 'b') || (*(p + 1) == 'B'))) + { + type = NUM_TYPE_BIN; + } + else if (*p == '0') + { + type = NUM_TYPE_OCT; + } + + while (*p++) + { + if (*p == '.' && *(p + 1) != 0) + { + type = NUM_TYPE_FLOAT; + break; + } + } + + return type; +} + + +/** + * @brief 字符转数字 + * + * @param code 字符 + * @return char 数字 + */ +static char shellExtToNum(char code) +{ + if ((code >= '0') && (code <= '9')) + { + return code -'0'; + } + else if ((code >= 'a') && (code <= 'f')) + { + return code - 'a' + 10; + } + else if ((code >= 'A') && (code <= 'F')) + { + return code - 'A' + 10; + } + else + { + return 0; + } +} + + +/** + * @brief 解析字符参数 + * + * @param string 字符串参数 + * @return char 解析出的字符 + */ +static char shellExtParseChar(char *string) +{ + char *p = string + 1; + char value = 0; + + if (*p == '\\') + { + switch (*(p + 1)) + { + case 'b': + value = '\b'; + break; + case 'r': + value = '\r'; + break; + case 'n': + value = '\n'; + break; + case 't': + value = '\t'; + break; + case '0': + value = 0; + break; + default: + value = *(p + 1); + break; + } + } + else + { + value = *p; + } + return value; +} + + +/** + * @brief 解析字符串参数 + * + * @param string 字符串参数 + * @return char* 解析出的字符串 + */ +static char* shellExtParseString(char *string) +{ + char *p = string; + unsigned short index = 0; + + if (*string == '\"') + { + p = ++string; + } + + while (*p) + { + if (*p == '\\') + { + *(string + index) = shellExtParseChar(p - 1); + p++; + } + else if (*p == '\"') + { + *(string + index) = 0; + } + else + { + *(string + index) = *p; + } + p++; + index ++; + } + *(string + index) = 0; + return string; +} + + +/** + * @brief 解析数字参数 + * + * @param string 字符串参数 + * @return unsigned int 解析出的数字 + */ +static unsigned int shellExtParseNumber(char *string) +{ + ShellNumType type = NUM_TYPE_DEC; + char radix = 10; + char *p = string; + char offset = 0; + signed char sign = 1; + unsigned int valueInt = 0; + float valueFloat = 0.0; + unsigned int devide = 0; + + if (*string == '-') + { + sign = -1; + } + + type = shellExtNumType(string + ((sign == -1) ? 1 : 0)); + + switch ((char)type) + { + case NUM_TYPE_HEX: + radix = 16; + offset = 2; + break; + + case NUM_TYPE_OCT: + radix = 8; + offset = 1; + break; + + case NUM_TYPE_BIN: + radix = 2; + offset = 2; + break; + + default: + break; + } + + p = string + offset + ((sign == -1) ? 1 : 0); + + while (*p) + { + if (*p == '.') + { + devide = 1; + p++; + continue; + } + valueInt = valueInt * radix + shellExtToNum(*p); + devide *= 10; + p++; + } + if (type == NUM_TYPE_FLOAT && devide != 0) + { + valueFloat = (float)valueInt / devide * sign; + return *(unsigned int *)(&valueFloat); + } + else + { + return valueInt * sign; + } +} + + +/** + * @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(Shell *shell, char *string) +{ + if (*string == '\'' && *(string + 1)) + { + return (unsigned int)shellExtParseChar(string); + } + else if (*string == '-' || (*string >= '0' && *string <= '9')) + { + return (unsigned int)shellExtParseNumber(string); + } + else if (*string == '$' && *(string + 1)) + { + return shellExtParseVar(shell, string); + } + else if (*string) + { + return (unsigned int)shellExtParseString(string); + } + return 0; +} + + +/** + * @brief 执行命令 + * + * @param shell shell对象 + * @param command 命令 + * @param argc 参数个数 + * @param argv 参数 + * @return int 返回值 + */ +int shellExtRun(Shell *shell, ShellCommand *command, int argc, char *argv[]) +{ + unsigned int params[SHELL_PARAMETER_MAX_NUMBER] = {0}; + int paramNum = command->attr.attrs.paramNum > (argc - 1) ? + command->attr.attrs.paramNum : (argc - 1); + for (int i = 0; i < argc - 1; i++) + { + params[i] = shellExtParsePara(shell, argv[i + 1]); + } + switch (paramNum) + { +#if SHELL_PARAMETER_MAX_NUMBER >= 1 + case 0: + return command->data.cmd.function(); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 1 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 2 + case 1: + return command->data.cmd.function(params[0]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 2 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 3 + case 2: + return command->data.cmd.function(params[0], params[1]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 3 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 4 + case 3: + return command->data.cmd.function(params[0], params[1], + params[2]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 4 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 5 + case 4: + return command->data.cmd.function(params[0], params[1], + params[2], params[3]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 5 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 6 + case 5: + return command->data.cmd.function(params[0], params[1], + params[2], params[3], + params[4]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 6 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 7 + case 6: + return command->data.cmd.function(params[0], params[1], + params[2], params[3], + params[4], params[5]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 7 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 8 + case 7: + return command->data.cmd.function(params[0], params[1], + params[2], params[3], + params[4], params[5], + params[6]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 8 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 9 + case 8: + return command->data.cmd.function(params[0], params[1], + params[2], params[3], + params[4], params[5], + params[6], params[7]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 9 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 10 + case 9: + return command->data.cmd.function(params[0], params[1], + params[2], params[3], + params[4], params[5], + params[6], params[7], + params[8]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 10 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 11 + case 10: + return command->data.cmd.function(params[0], params[1], + params[2], params[3], + params[4], params[5], + params[6], params[7], + params[8], params[9]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 11 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 12 + case 11: + return command->data.cmd.function(params[0], params[1], + params[2], params[3], + params[4], params[5], + params[6], params[7], + params[8], params[9], + params[10]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 12 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 13 + case 12: + return command->data.cmd.function(params[0], params[1], + params[2], params[3], + params[4], params[5], + params[6], params[7], + params[8], params[9], + params[10], params[11]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 13 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 14 + case 13: + return command->data.cmd.function(params[0], params[1], + params[2], params[3], + params[4], params[5], + params[6], params[7], + params[8], params[9], + params[10], params[11], + params[12]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 14 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 15 + case 14: + return command->data.cmd.function(params[0], params[1], + params[2], params[3], + params[4], params[5], + params[6], params[7], + params[8], params[9], + params[10], params[11], + params[12], params[13]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 15 */ +#if SHELL_PARAMETER_MAX_NUMBER >= 16 + case 15: + return command->data.cmd.function(params[0], params[1], + params[2], params[3], + params[4], params[5], + params[6], params[7], + params[8], params[9], + params[10], params[11], + params[12], params[13], + params[14]); + // break; +#endif /** SHELL_PARAMETER_MAX_NUMBER >= 16 */ + default: + return -1; + // break; + } +} +