HCoreBase/src/HShellLex.c
coffee 507c72cb98 1. 完善RPC
2. 修复协议调度的重叠问题
2025-11-25 11:17:01 +08:00

500 lines
14 KiB
C

#include <HShellLex.h>
#include <HVector.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef LogD
#if 0
#include <stdio.h>
#define LogD(format, ...) printf("[%s:%s:%d]" format "\r\n", __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define LogD(...)
#endif
#endif
#ifndef stdout
#define NOT_SUPPORT_STDOUT
#endif
#define CTRL_KEY(k) ((k) & 0x1f)
typedef struct __attribute__((packed)) ShellCmd {
const HShellMatch *match; ///< 匹配表
shellCall call; ///< 回调
uint8_t matchLen : 7; ///< 匹配表长度
uint8_t ignoreCase : 1; ///< 忽略大小写
} ShellCmd;
struct __attribute__((packed)) ShellInfo {
ShellCmd shellCmd[HSHELL_CALL_MAX]; ///< 回调注册表
HShellCmdToken cmdTokens[HSHELL_CMD_TOKEN_MAX]; ///< 解析参数
uint8_t cursorPos; ///< 光标位置
uint8_t escStatus : 3; ///< ESC转义状态, 最多7状态
uint8_t run : 1; ///< 可执行, 当前不能继续加入缓存区
uint8_t needPrint : 1; ///< 需要重绘当前行
uint8_t password : 1; ///< 密码模式
uint8_t userName : 1; ///< 用户名是否正确
};
// 命令行缓存数据
static HVECTOR_DEFINE(cmdBuffer_, HSHELL_CMD_LINE_BUFFER);
#ifdef HSHELL_USE_BACK_BUFFER
static HVECTOR_DEFINE(prevBuffer_, HSHELL_CMD_LINE_BUFFER);
#endif
static struct ShellInfo shellInfo_;
static void PrintAllCmd() {
uint16_t len = 0;
uint16_t pos = 10;
for (int i = 0; i < HSHELL_CALL_MAX; ++i) {
if (shellInfo_.shellCmd[i].match == NULL) {
continue;
}
for (int j = 0; j < shellInfo_.shellCmd[i].matchLen; ++j) {
if (shellInfo_.shellCmd[i].matchLen <= 0) {
continue;
}
pos = pos > shellInfo_.shellCmd[i].match[j].matchLen ? pos : shellInfo_.shellCmd[i].match[j].matchLen;
HSHELL_PRINTF("%-*s ", pos, (const char *)shellInfo_.shellCmd[i].match[j].match);
len += pos;
if (len >= 80) {
HSHELL_PRINTF("\r\n");
pos = 10;
len = 0;
}
}
}
if (len > 0) {
HSHELL_PRINTF("\r\n");
}
}
static void ShellParse()
{
if (HVectorEmpty(cmdBuffer_)) {
return ;
}
uint8_t len = HShellLex((const uint8_t *)HVectorGetByteDataPtr(cmdBuffer_, 0), HVectorGetUseByteLen(cmdBuffer_), shellInfo_.cmdTokens, HSHELL_CMD_TOKEN_MAX);
if (len <= 0) {
LogD("lex parse len[%d] empty", len);
return ;
}
#ifdef HSHELL_USE_PASSWORD
if (shellInfo_.password == 0 || shellInfo_.userName == 0) {
shellInfo_.needPrint = 1;
LogD("password[%d] userName[%d], data[%s]", shellInfo_.password, shellInfo_.userName, shellInfo_.cmdTokens[0].str);
// 这个状态下说明用户名输入错误, 无论输入什么都是失败
if (shellInfo_.password == 1 && shellInfo_.userName == 0) {
shellInfo_.password = 0;
return ;
}
#define _ICONV(name) name, sizeof(name) - 1
#define ICONV(name) _ICONV(name)
// 输入用户名
if (shellInfo_.userName == 0) {
if (HShellDiffString(shellInfo_.cmdTokens, len, 0, ICONV(HSHELL_USER), 0) == 0) {
// 用户名错误, 给输入假的密码机会
shellInfo_.password = 1;
return ;
}
shellInfo_.userName = 1;
return ;
}
if (shellInfo_.password == 0) {
if (HShellDiffString(shellInfo_.cmdTokens, len, 0, ICONV(HSHELL_PASSWORD), 0) == 0) {
shellInfo_.userName = 0;
return ;
}
shellInfo_.password = 1;
return ;
}
}
#undef _ICONV
#undef ICONV
#endif
if (shellInfo_.cmdTokens[0].len == 1 && shellInfo_.cmdTokens[0].str[0] == '?') {
PrintAllCmd();
return ;
}
for (int i = 0; i < HSHELL_CALL_MAX; ++i) {
if (shellInfo_.shellCmd[i].match == NULL) {
continue;
}
HShellLenType key = HShellMatchToken(shellInfo_.cmdTokens, shellInfo_.shellCmd[i].match, shellInfo_.shellCmd[i].matchLen, shellInfo_.shellCmd[i].ignoreCase);
if (key != HSHELL_ERROR_INDEX) {
shellInfo_.shellCmd[i].call(key, shellInfo_.cmdTokens, len);
return ;
}
}
}
static void PrintCmdLine() {
HVectorLenType len = HVectorGetUseLen(cmdBuffer_);
HVectorSetData(cmdBuffer_, len, '\0');
HSHELL_PRINTF("\r\033[K\033[1;32m > ");
#ifdef HSHELL_USE_PASSWORD
if (shellInfo_.password == 0 || shellInfo_.userName == 0) {
// 用户已经输入
if (shellInfo_.password == 1 && shellInfo_.userName == 0) {
HSHELL_PRINTF("password: \033[0m");
#ifndef NOT_SUPPORT_STDOUT
fflush(stdout);
#endif
return ;
}
// 输入用户名
if (shellInfo_.userName == 0) {
HSHELL_PRINTF("username: %s\033[0m", (const char *)HVectorGetByteDataPtr(cmdBuffer_, 0));
if (shellInfo_.cursorPos < len) {
HSHELL_PRINTF("\033[%dD", len - shellInfo_.cursorPos);
}
} else if (shellInfo_.password == 0) {
HSHELL_PRINTF("password: \033[0m");
}
#ifndef NOT_SUPPORT_STDOUT
fflush(stdout);
#endif
return ;
}
#endif
HSHELL_PRINTF("%s\033[0m", (const char *)HVectorGetByteDataPtr(cmdBuffer_, 0));
if (shellInfo_.cursorPos < len) {
HSHELL_PRINTF("\033[%dD", len - shellInfo_.cursorPos);
}
#ifndef NOT_SUPPORT_STDOUT
fflush(stdout);
#endif
}
static void DeleteBeginByte() {
if (shellInfo_.cursorPos <= 0) {
return ;
}
HVectorRemoveData(cmdBuffer_, shellInfo_.cursorPos - 1, 1);
--shellInfo_.cursorPos;
shellInfo_.needPrint = 1;
}
static void DeleteAfterByte() {
if (shellInfo_.cursorPos <= 0) {
return ;
}
if (shellInfo_.cursorPos >= HVectorGetUseLen(cmdBuffer_)) {
return ;
}
HVectorRemoveData(cmdBuffer_, shellInfo_.cursorPos, 1);
shellInfo_.needPrint = 1;
}
static void SwapPrevBuffer() {
#ifdef HSHELL_USE_BACK_BUFFER
int len = HVectorGetUseByteLen(cmdBuffer_);
char *swapBuf[HSHELL_CMD_LINE_BUFFER];
memcpy(swapBuf, HVectorGetByteDataPtr(cmdBuffer_, 0), len);
HVectorClear(cmdBuffer_);
HVectorAddBytes(cmdBuffer_, (const uint8_t *)HVectorGetByteDataPtr(prevBuffer_, 0), HVectorGetUseByteLen(prevBuffer_));
HVectorClear(prevBuffer_);
HVectorAddBytes(prevBuffer_, (const uint8_t *)swapBuf, len);
shellInfo_.cursorPos = HVectorGetUseLen(cmdBuffer_);
shellInfo_.needPrint = 1;
#endif
}
static void AddCmdData(uint8_t data) {
enum eEscStaus {
kEscNone,
kEsc,
kEscCsi,
kEscCsiDel,
};
if (shellInfo_.run) {
return ;
}
switch (shellInfo_.escStatus) {
case kEsc: {
if (data == '[') {
shellInfo_.escStatus = kEscCsi;
return ;
}
} break;
case kEscCsi: {
switch (data) {
case 'D': {
// 左键
if (shellInfo_.cursorPos > 0) {
--shellInfo_.cursorPos;
}
shellInfo_.needPrint = 1;
shellInfo_.escStatus = kEscNone;
} return ;
case 'C': {
// 右键
if (shellInfo_.cursorPos < HVectorGetUseLen(cmdBuffer_)) {
++shellInfo_.cursorPos;
}
shellInfo_.needPrint = 1;
shellInfo_.escStatus = kEscNone;
} return ;
case 'A': // 上键
case 'B': // 下键
SwapPrevBuffer();
shellInfo_.escStatus = kEscNone;
return ;
case '3': {
shellInfo_.escStatus = kEscCsiDel;
} return ;
default: break;
}
} break;
case kEscCsiDel: {
shellInfo_.escStatus = kEscNone;
if (data == '~') {
DeleteAfterByte();
return ;
}
} break;
default: break;
}
shellInfo_.escStatus = kEscNone;
switch (data) {
case 0x1b: shellInfo_.escStatus = kEsc; return ;
case '\t': return ;
case '\r':
case '\n': {
shellInfo_.run = 1;
} return ;
case '\b':
case 0x7f: {
DeleteBeginByte();
} return ;
default: break;
}
if (isprint(data) == 0) {
LogD("not print char[%x]", data);
return ;
}
if (HVectorInsertData(cmdBuffer_, shellInfo_.cursorPos, data) == 0) {
LogD("buffer is full");
return ;
}
shellInfo_.cursorPos++;
shellInfo_.needPrint = 1;
}
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;
}
uint8_t HShellDiffString(const HShellCmdToken *token, uint8_t tokenLen, uint8_t index, const char *str, uint8_t strLen, uint8_t ignoreCase) {
if (token == NULL || tokenLen <= 0 || index >= tokenLen || str == NULL || strLen <= 0) {
LogD("token[%p] or tokenLen[%d] or index[%d] or str[%p] or strLen[%d] is nullptr", token, tokenLen, index, str, strLen);
return 0;
}
if (token[index].str == NULL || token[index].len == 0) {
LogD("token[%d] is empty or str[%p] is nullptr", index, token[index].str);
return 0;
}
if (token[index].len != strLen) {
LogD("token[%d] len[%d] is not equal strLen[%d]", index, token[index].len, strLen);
return 0;
}
int (*cmpFunc)(const char *, const char *, size_t) = ignoreCase ? strncasecmp : strncmp;
return cmpFunc((const char *)token[index].str, str, strLen) == 0 ? 1 : 0;
}
uint8_t HSHellToUint32(const HShellCmdToken *token, uint8_t tokenLen, uint8_t index, uint32_t *value) {
if (token == NULL || tokenLen <= 0 || value == NULL) {
LogD("token[%p] or tokenLen[%d] or value[%p] is nullptr", token, tokenLen, value);
return 0;
}
if (index >= tokenLen) {
LogD("index[%d] is out of range", index);
return 0;
}
if (token[index].str == NULL || token[index].len == 0) {
LogD("token[%d] is empty or str[%p] is nullptr", index, token[index].str);
return 0;
}
char *endPtr = NULL;
*value = strtol((const char *)token[index].str, &endPtr, 0);
if (endPtr == (const char *)token[index].str || *endPtr != '\0') {
LogD("token[%d] is not uint32", index);
return 0;
}
return 1;
}
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 (shellInfo_.shellCmd[i].match == matches) {
LogD("call[%p] is exist, override write index[%d]", call, i);
shellInfo_.shellCmd[i].call = call;
shellInfo_.shellCmd[i].matchLen = matchLen;
shellInfo_.shellCmd[i].ignoreCase = ignoreCase ? 1 : 0;
return i;
}
if (shellInfo_.shellCmd[i].match == NULL) {
shellInfo_.shellCmd[i].match = matches;
shellInfo_.shellCmd[i].call = call;
shellInfo_.shellCmd[i].matchLen = matchLen;
shellInfo_.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;
}
shellInfo_.shellCmd[index].match = NULL;
shellInfo_.shellCmd[index].call = NULL;
shellInfo_.shellCmd[index].matchLen = 0;
}
void HShellAddCmdData(uint8_t data) {
AddCmdData(data);
}
void HSHellAddCmdDatas(const void *data, int len) {
if (shellInfo_.run || data == NULL) {
return ;
}
const uint8_t *d = (const uint8_t *)data;
for (int i = 0; i < len; ++i) {
AddCmdData(d[i]);
}
}
void HShellRun() {
if (shellInfo_.needPrint) {
PrintCmdLine();
shellInfo_.needPrint = 0;
}
if (shellInfo_.run == 0) {
return ;
}
HSHELL_PRINTFL("");
ShellParse();
#ifdef HSHELL_USE_BACK_BUFFER
if (HVectorEmpty(cmdBuffer_) == 0) {
HVectorClear(prevBuffer_);
HVectorAddBytes(prevBuffer_, (const uint8_t *)HVectorGetByteDataPtr(cmdBuffer_, 0), HVectorGetUseByteLen(cmdBuffer_));
}
#endif
HVectorClear(cmdBuffer_);
HVectorSetData(cmdBuffer_, 0, '\0');
PrintCmdLine();
shellInfo_.cursorPos = 0;
shellInfo_.run = 0;
}