diff --git a/include/HShellLex.h b/include/HShellLex.h new file mode 100644 index 0000000..df6ed37 --- /dev/null +++ b/include/HShellLex.h @@ -0,0 +1,103 @@ + + +#ifndef _H_SHELL_LEX_ +#define _H_SHELL_LEX_ + + +#include + + +typedef uint8_t HShellLenType; +#define HSHELL_ERROR_INDEX (0xff) + +///< 可配置最多可注册回调多少个 +#ifndef HSHELL_CALL_MAX +#define HSHELL_CALL_MAX 5 +#endif + +///< 命令行缓存长度 +#ifndef HSHELL_CMD_LINE_BUFFER +#define HSHELL_CMD_LINE_BUFFER 32 +#endif + +///< 解析token参数的最大个数 +#ifndef HSHELL_CMD_TOKEN_MAX +#define HSHELL_CMD_TOKEN_MAX 5 +#endif + +///< 使用专门接口避免日志系统关闭日志打印导致命令不输出的问题 +#define HSHELL_PRINTF(format, ...) printf(format, ##__VA_ARGS__) + + +///< 处理命令行token +typedef struct __attribute__ ((__packed__)) HShellCmdToken +{ + const uint8_t* str; ///< 指向每项数据开头, 如 echo 123 会有2个token, 一个str指向echo, 一个str指向123 + uint8_t len; ///< 数据长度 +} HShellCmdToken; + +// 匹配映射表初始化每项的辅助宏 +#define HSHELL_MATCH_ITEM(token, str) {token, sizeof(str) - 1, (const uint8_t *)str} + +///< 匹配映射表 +typedef struct __attribute__ ((__packed__)) HShellMatch +{ + HShellLenType token; ///< 匹配后对应的映射值 + uint8_t matchLen; ///< 需要匹配的字符串长度 + const uint8_t *match; ///< 需要匹配的字符串 +} HShellMatch; + + +/** + * @brief 命令行解析回调 + * @param key 命令行解析的首个token + * @param tokens 原始token, 可自行处理后续命令的所需的参数 + * @param tokensLen 原始token个数 + **/ +typedef void (*shellCall)(HShellLenType key, const HShellCmdToken *tokens, int tokensLen); + +/** + * @brief 解析命令行数据, 将解析后的数据写入tokens, 内存还是str, tokens是指向tokens的指针 + * @brief str 命令行数据 + * @brief strLen 命令行数据长度 + * @brief tokens 命令行解析后写入的token + * @brief tokensLen toknes长度 + * @return 返回token个数 + **/ +uint8_t HShellLex(const uint8_t *str, int strLen, HShellCmdToken *tokens, int tokensLen); + +/** + * @brief 查找token匹配对应token枚举值, 判断是否忽略大小写 + * @param token 命令行解析的token + * @param matches 匹配映射表 + * @param matchLen 匹配映射表个数 + * @param ignoreCase 是否忽略大小写 + * @return 返回对应token映射值, 如果不存在返回 HSHELL_ERROR_INDEX + **/ +HShellLenType HShellMatchToken(const HShellCmdToken *token, const HShellMatch *matches, int matchLen, uint8_t ignoreCase); + +/** + * @brief 注册命令行解析回调, 如果存在匹配映射表则覆盖原有映射 + * @param matches 匹配映射表 + * @param matchLen 匹配映射表个数 + * @param call 命令行解析回调 + * @param ignoreCase 是否忽略大小写 + * @return 返回注册的索引, 错误返回 -1 + **/ +int16_t HSHellRegister(const HShellMatch *matches, int matchLen, shellCall call, uint8_t ignoreCase); + +/** + * @brief 注销命令行解析回调 + * @param index 注销的索引 + **/ +void HSHellUnregister(int16_t index); + +/** + * @brief 添加命令行数据, 直到换行'\n'才处理 + * @param str 命令行数据 + * @param strLen 命令行数据长度 + **/ +void HShellAddCmdData(uint8_t data); +void HSHellAddCmdDatas(uint8_t *data, int len); + +#endif // _H_SHELL_LEX_ diff --git a/include/HVector.h b/include/HVector.h index 9d3adb9..15c6099 100644 --- a/include/HVector.h +++ b/include/HVector.h @@ -100,10 +100,13 @@ typedef struct __attribute__ ((__packed__)) _HVector32 { // 添加数据, 成功返回1, 失败返回 0 uint8_t HVectorAddData(HVectorType *vector, HVectorDataType data); +uint8_t HVectorInsertData(HVectorType *vector, HVectorLenType index, HVectorDataType data); // 添加字节数据, 超出仅复制到最大长度, 返回复制长度 HVectorLenType HVectorAddBytes(HVectorType *vector, const uint8_t *datas, HVectorLenType byteLen); // 添加对应格式数据, 要求datas类型是同一对应位类型, 长度为字节长度, 超出仅复制到最大长度, 返回复制长度 HVectorLenType HVectorAddDatas(HVectorType *vector, const void *datas, HVectorLenType byteLen); +// 设置数据, 可超出使用长度, 用于配置空字符 +void HVectorSetData(HVectorType *vector, HVectorLenType index, HVectorDataType data); // 获取数据, 失败返回 HVECTOR_ERROR HVectorDataType HVectorGetData(HVectorType *vector, HVectorLenType index); diff --git a/src/HShellLex.c b/src/HShellLex.c new file mode 100644 index 0000000..67f962f --- /dev/null +++ b/src/HShellLex.c @@ -0,0 +1,249 @@ + + +#include +#include +#include +#include +#include + + +#ifndef LogD +#if 0 +#include +#define LogD(format, ...) printf("[%s:%s:%d]" format "\r\n", __FILE_NAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#else +#define LogD(...) +#endif +#endif + + +#define CTRL_KEY(k) ((k) & 0x1f) + + +struct __attribute__((packed)) HShellCmd { + const HShellMatch *match; ///< 匹配表 + shellCall call; ///< 回调 + uint8_t matchLen; ///< 匹配表长度 + uint8_t ignoreCase : 1; ///< 忽略大小写 +}; + +// 回调注册 +static struct HShellCmd shellCmd_[HSHELL_CALL_MAX]; +// 命令行数据 +static HVECTOR_DEFINE(cmdBuffer_, HSHELL_CMD_LINE_BUFFER); +// 解析参数 +static HShellCmdToken cmdTokens_[HSHELL_CMD_TOKEN_MAX]; +// 光标位置 +static uint8_t cursorPos; + + +uint8_t HShellLex(const uint8_t *str, int strLen, HShellCmdToken *tokens, int tokensLen) { + if (str == NULL || tokens == NULL || tokensLen <= 0) { + LogD("str[%p] is nullptr or Tokens[%p] is nullptr or len[%d]", str, tokens, tokensLen); + return 0; + } + + int tokenIndex = 0; + for (int i = 0; i < strLen; ++i) { + if (tokenIndex >= tokensLen) { + LogD("Tokens[%d] is full, index[%d], currParseIndex[%d]", tokensLen, tokenIndex, i); + break; + } + + // 跳过空白字符 + if (isspace(str[i])) { + continue; + } + + int j = i; + for (; j < strLen; ++j) { + if (isspace(str[j])) { + break; + } + } + + tokens[tokenIndex].str = str + i; + tokens[tokenIndex].len = j - i; + tokenIndex++; + i = j; + } + + return tokenIndex; +} + +HShellLenType HShellMatchToken(const HShellCmdToken *token, const HShellMatch *matches, int matchesLen, uint8_t ignoreCase) { + if (token == NULL || matches == NULL || matchesLen <= 0) { + LogD("Tokens[%p] or matches[%p] is nullptr or len[%d]", token, matches, matchesLen); + return HSHELL_ERROR_INDEX; + } + + int (*cmpFunc)(const char *, const char *, size_t) = ignoreCase ? strncasecmp : strncmp; + for (int i = 0; i < matchesLen; ++i) { + if (token->len != matches[i].matchLen) { + continue; + } + + if (cmpFunc((const char *)token->str, (const char *)matches[i].match, token->len) == 0) { + return matches[i].token; + } + } + + return HSHELL_ERROR_INDEX; +} + +int16_t HSHellRegister(const HShellMatch *matches, int matchLen, shellCall call, uint8_t ignoreCase) { + if (matches == NULL || matchLen <= 0 || call == NULL) { + LogD("matches[%p] or matchLen[%d] or call[%p] is nullptr", matches, matchLen, call); + return -1; + } + + for (int i = 0; i < HSHELL_CALL_MAX; ++i) { + if (shellCmd_[i].match == matches) { + LogD("call[%p] is exist, override write index[%d]", call, i); + shellCmd_[i].call = call; + shellCmd_[i].matchLen = matchLen; + shellCmd_[i].ignoreCase = ignoreCase ? 1 : 0; + return i; + } + + if (shellCmd_[i].match == NULL) { + shellCmd_[i].match = matches; + shellCmd_[i].call = call; + shellCmd_[i].matchLen = matchLen; + shellCmd_[i].ignoreCase = ignoreCase ? 1 : 0; + return i; + } + } + + LogD("call table is full"); + return -1; +} + +void HSHellUnregister(int16_t index) { + if (index < 0 || index >= HSHELL_CALL_MAX) { + LogD("error index[%d]", index); + return; + } + + shellCmd_[index].match = NULL; + shellCmd_[index].call = NULL; + shellCmd_[index].matchLen = 0; +} + +static void ShellParse() +{ + if (HVectorEmpty(cmdBuffer_)) { + return ; + } + + uint8_t len = HShellLex((const uint8_t *)HVectorGetByteDataPtr(cmdBuffer_, 0), HVectorGetUseByteLen(cmdBuffer_), cmdTokens_, HSHELL_CMD_TOKEN_MAX); + LogD("lex parse len[%d], dataLen[%d]", len, HVectorGetUseByteLen(cmdBuffer_)); + if (len <= 0) { + LogD("lex parse len[%d] empty", len); + return ; + } + + for (int i = 0; i < HSHELL_CALL_MAX; ++i) { + if (shellCmd_[i].match == NULL) { + continue; + } + + HShellLenType key = HShellMatchToken(cmdTokens_, shellCmd_[i].match, shellCmd_[i].matchLen, shellCmd_[i].ignoreCase); + if (key != HSHELL_ERROR_INDEX) { + shellCmd_[i].call(key, cmdTokens_, len); + return ; + } + } +} + +static void PrintCmdData() { + if (HVectorEmpty(cmdBuffer_)) { + return ; + } + + HVectorSetData(cmdBuffer_, HVectorGetUseLen(cmdBuffer_), '\0'); + HSHELL_PRINTF("\r\x1b[K%s", (const char *)HVectorGetByteDataPtr(cmdBuffer_, 0)); +} + +///< 返回1需要打印数据 +static uint8_t AddCmdData(uint8_t data) { + enum eEscStaus { + kEscNone, + kEsc, + kEscCsi, + }; + + static uint8_t escStatus = kEscNone; + switch (escStatus) { + case kEsc: { + if (data == '[') { + escStatus = kEscCsi; + return 0; + } + } break; + case kEscCsi: { + if (data == 'D') { + // 左键 + if (cursorPos > 0) { + --cursorPos; + } + return 0; + } else if (data == 'C') { + // 右键 + if (cursorPos < HVectorGetUseLen(cmdBuffer_)) { + ++cursorPos; + } + return 0; + } + } break; + } + + escStatus = kEscNone; + switch (data) { + case 0x1b: escStatus = kEsc; return 0; + case '\t': + case '\r': return 0; + case '\n': { + cursorPos = 0; + ShellParse(); + HVectorClear(cmdBuffer_); + } return 0; + case '\b': { + if (cursorPos > 0) { + HVectorRemoveData(cmdBuffer_, cursorPos - 1, 1); + cursorPos--; + } + } return 1; + default: break; + } + + if (isprint(data) == 0) { + LogD("not print char[%x]", data); + return 0; + } + + if (HVectorInsertData(cmdBuffer_, cursorPos, data) == 0) { + LogD("buffer is full"); + return 0; + } + + cursorPos++; + return 1; +} + +void HShellAddCmdData(uint8_t data) { + if (AddCmdData(data)) { + PrintCmdData(); + } +} + +void HSHellAddCmdDatas(uint8_t *data, int len) { + uint8_t needPrint = 0; + for (int i = 0; i < len; ++i) { + needPrint |= AddCmdData(data[i]); + } + + if (HVectorGetUseLen(cmdBuffer_) > 0 && needPrint) { + PrintCmdData(); + } +} diff --git a/src/HVector.c b/src/HVector.c index b9bc12a..2033aec 100644 --- a/src/HVector.c +++ b/src/HVector.c @@ -136,6 +136,21 @@ uint8_t HVectorAddData(HVectorType *vector, HVectorDataType data) { return 1; } +uint8_t HVectorInsertData(HVectorType *vector, HVectorLenType index, HVectorDataType data) { + HVectorLenType len = GetVectorUseLen(vector); + if (index > len) { + LogD("index[%d] error, len[%d]", index, len); + return 0; + } + + HVectorLenType typeSize = GetVectorTypeSize(vector); + uint8_t *dest = (uint8_t *)GetVectorDataBytePtr(vector, index * typeSize); + memmove(dest + typeSize, dest, (len - index) * typeSize); + SetVectorData(vector, index, data); + SetVectorUseLen(vector, len + 1); + return 1; +} + HVectorLenType HVectorAddBytes(HVectorType *vector, const uint8_t *datas, HVectorLenType byteLen) { HVectorLenType useLen = GetVectorUseLen(vector); HVectorLenType maxLen = GetVectorByteLen(vector) - useLen; @@ -158,6 +173,14 @@ HVectorLenType HVectorAddDatas(HVectorType *vector, const void *datas, HVectorLe return HVectorAddBytes(vector, (const uint8_t *)datas, byteLen * GetVectorTypeSize(vector)) / GetVectorTypeSize(vector); } +void HVectorSetData(HVectorType *vector, HVectorLenType index, HVectorDataType data) { + if (index >= GetVectorLen(vector)) { + return; + } + + SetVectorData(vector, index, data); +} + // 获取数据, 失败返回 HVECTOR_ERROR HVectorDataType HVectorGetData(HVectorType *vector, HVectorLenType index) { return GetVectorData(vector, index);