#include #include #include #include #include #include #ifndef LogD #if 0 #include #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; }