500 lines
14 KiB
C
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;
|
|
}
|