1
0
mirror of https://github.com/NevermindZZT/letter-shell.git synced 2025-01-21 10:02:54 +08:00
letter-shell/shell.c
2019-03-02 11:30:52 +08:00

772 lines
19 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file shell.c
* @author Letter (NevermindZZT@gmail.cn)
* @brief letter shell
* @version 2.0.0
* @date 2018-12-29
*
* @Copyright (c) 2018 Letter
*
*/
#include "shell.h"
#include "string.h"
#if SHELL_AUTO_PRASE == 1
#include "shell_ext.h"
#endif
static unsigned short shellDisplay(SHELL_TypeDef *shell, const char *string);
static void shellDisplayItem(SHELL_TypeDef *shell, unsigned short index);
static void shellHelp(SHELL_TypeDef *shell, int argc, char *argv[]);
#if SHELL_USING_CMD_EXPORT != 1
/**
* @brief shell默认命令表
*
* @note 当使用命令表方式定义命令的时候,此表才会生效
* @note 添加命令时可使用SHELL_CMD_ITEM宏如SHELL_CMD_ITEM(help, shellHelp, command help)
* @note 可不使用默认命令表初始化完成之后可调用shellSetCommandList接口设置命令表
*/
const SHELL_CommandTypeDef shellDefaultCommandList[] =
{
SHELL_CMD_ITEM_EX(help, shellHelp, command help, help [command] --show help info of command),
};
#endif
/**
* @brief shell初始化
*
* @param shell shell对象
*/
void shellInit(SHELL_TypeDef *shell)
{
shellDisplay(shell, "\r\n\r\n");
shellDisplay(shell, "+=========================================================+\r\n");
shellDisplay(shell, "| (C) COPYRIGHT 2018 Letter |\r\n");
shellDisplay(shell, "| Letter shell v"SHELL_VERSION" |\r\n");
shellDisplay(shell, "| Build: "__DATE__" "__TIME__" |\r\n");
shellDisplay(shell, "+=========================================================+\r\n");
shell->length = 0;
shell->cursor = 0;
shell->historyCount = 0;
shell->historyFlag = 0;
shell->historyOffset = 0;
shell->status = SHELL_IN_NORMAL;
shell->command = SHELL_DEFAULT_COMMAND;
shellDisplay(shell, shell->command);
#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;
shell->commandBase = (SHELL_CommandTypeDef *)(&shellCommand$$Base);
shell->commandNumber = ((unsigned int)(&shellCommand$$Limit)
- (unsigned int)(&shellCommand$$Base))
/ sizeof(SHELL_CommandTypeDef);
#elif defined(__ICCARM__)
shell->commandBase = (SHELL_CommandTypeDef *)(__section_begin("shellCommand"));
shell->commandNumber = ((unsigned int)(__section_end("shellCommand"))
- (unsigned int)(__section_begin("shellCommand")))
/ sizeof(SHELL_CommandTypeDef);
#elif defined(__GNUC__)
extern const unsigned int _shell_command_start;
extern const unsigned int _shell_command_end;
shell->commandBase = (SHELL_CommandTypeDef *)(&_shell_command_start);
shell->commandNumber = ((unsigned int)(&_shell_command_end)
- (unsigned int)(&_shell_command_start))
/ sizeof(SHELL_CommandTypeDef);
#else
#error not supported compiler, please use command table mode
#endif
#else
shell->commandBase = (SHELL_CommandTypeDef *)shellDefaultCommandList;
shell->commandNumber = sizeof(shellDefaultCommandList) / sizeof(SHELL_CommandTypeDef);
#endif
}
/**
* @brief shell设置命令表
*
* @param shell shell对象
* @param base 命令表基址
* @param size 命令数量
*
* @note 此接口不可在shellInit之前调用
* @note 不调用此接口,则使用默认命令表或命令导出形成的命令表(取决于命令定义方式)
*/
void shellSetCommandList(SHELL_TypeDef *shell, SHELL_CommandTypeDef *base, unsigned short size)
{
shell->commandBase = base;
shell->commandNumber = size;
}
/**
* @brief shell显示字符串
*
* @param shell shell对象
* @param string 字符串
* @return unsigned short 字符串长度
*/
static unsigned short shellDisplay(SHELL_TypeDef *shell, const char *string)
{
unsigned short count = 0;
if (shell->write == NULL)
{
return 0;
}
while(*string)
{
shell->write(*string++);
count ++;
}
return count;
}
/**
* @brief shell显示字符
*
* @param data 字符
*/
static void shellDisplayByte(SHELL_TypeDef *shell, char data)
{
if (shell->write == NULL)
{
return;
}
shell->write(data);
}
/**
* @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 shell shell对象
* @param length 删除的长度
*/
static void shellDelete(SHELL_TypeDef *shell, unsigned short length)
{
while (length--)
{
shellDisplay(shell, "\b \b");
}
}
/**
* @brief shell清除输入
*
* @param shell shell对象
*/
static void shellClearLine(SHELL_TypeDef *shell)
{
for (short i = shell->length - shell->cursor; i > 0; i--)
{
shellDisplayByte(shell, ' ');
}
shellDelete(shell, shell->length);
}
/**
* @brief shell历史记录添加
*
* @param shell shell对象
*/
static void shellHistoryAdd(SHELL_TypeDef *shell)
{
if (strcmp(shell->history[shell->historyFlag - 1], shell->buffer) == 0)
{
return;
}
if (shellStringCopy(shell->history[shell->historyFlag], shell->buffer) != 0)
{
shell->historyFlag++;
}
if (++shell->historyCount > SHELL_HISTORY_MAX_NUMBER)
{
shell->historyCount = SHELL_HISTORY_MAX_NUMBER;
}
if (shell->historyFlag >= SHELL_HISTORY_MAX_NUMBER)
{
shell->historyFlag = 0;
}
shell->historyOffset = 0;
}
/**
* @brief shell历史记录查找
*
* @param shell shell对象
* @param dir 查找方向
*/
static void shellHistory(SHELL_TypeDef *shell, unsigned char dir)
{
if (dir == 0)
{
if (shell->historyOffset--
<= -((shell->historyCount > shell->historyFlag)
? shell->historyCount : shell->historyFlag))
{
shell->historyOffset = -((shell->historyCount > shell->historyFlag)
? shell->historyCount : shell->historyFlag);
}
}
else if (dir == 1)
{
if (++shell->historyOffset > 0)
{
shell->historyOffset = 0;
return;
}
}
else
{
return;
}
shellClearLine(shell);
if (shell->historyOffset == 0)
{
shell->cursor = shell->length = 0;
}
else
{
if ((shell->length = shellStringCopy(shell->buffer,
shell->history[(shell->historyFlag + SHELL_HISTORY_MAX_NUMBER
+ shell->historyOffset) % SHELL_HISTORY_MAX_NUMBER])) == 0)
{
return;
}
shell->cursor = shell->length;
shellDisplay(shell, shell->buffer);
}
}
/**
* @brief shell回车输入处理
*
* @param shell shell对象
*/
static void shellEnter(SHELL_TypeDef *shell)
{
unsigned char paramCount = 0;
unsigned char quotes = 0;
unsigned char record = 1;
SHELL_CommandTypeDef *base;
unsigned char runFlag = 0;
if (shell->length == 0)
{
shellDisplay(shell, shell->command);
return;
}
*(shell->buffer + shell->length++) = 0;
shellHistoryAdd(shell);
for (unsigned short i = 0; i < shell->length; i++)
{
if ((quotes != 0 ||
(*(shell->buffer + i) != ' ' &&
*(shell->buffer + i) != '\t' &&
*(shell->buffer + i) != ',')) &&
*(shell->buffer + i) != 0)
{
if (*(shell->buffer + i) == '\"')
{
quotes = quotes ? 0 : 1;
#if SHELL_AUTO_PRASE == 0
*(shell->buffer + i) = 0;
continue;
#endif
}
if (record == 1)
{
shell->param[paramCount++] = shell->buffer + i;
record = 0;
}
if (*(shell->buffer + i) == '\\' &&
*(shell->buffer + i) != 0)
{
i++;
}
}
else
{
*(shell->buffer + i) = 0;
record = 1;
}
}
shell->length = 0;
shell->cursor = 0;
if (paramCount == 0)
{
shellDisplay(shell, shell->command);
return;
}
shellDisplay(shell, "\r\n");
base = shell->commandBase;
if (strcmp((const char *)shell->param[0], "help") == 0)
{
shellHelp(shell, paramCount, shell->param);
shellDisplay(shell, shell->command);
return;
}
for (unsigned char i = 0; i < shell->commandNumber; i++)
{
if (strcmp((const char *)shell->param[0], (base + i)->name) == 0)
{
runFlag = 1;
#if SHELL_AUTO_PRASE == 0
(base + i)->function(paramCount, shell->param);
#else
shellExtRun((base + i)->function, paramCount, shell->param);
#endif
}
}
if (runFlag == 0)
{
shellDisplay(shell, "Command not found\r\n");
}
shellDisplay(shell, shell->command);
}
/**
* @brief shell退格输入处理
*
* @param shell shell对象
*/
static void shellBackspace(SHELL_TypeDef *shell)
{
if (shell->length == 0)
{
return;
}
if (shell->cursor == shell->length)
{
shell->length--;
shell->cursor--;
shell->buffer[shell->length] = 0;
shellDelete(shell, 1);
}
else if (shell->cursor > 0)
{
for (short i = 0; i < shell->length - shell->cursor; i++)
{
shell->buffer[shell->cursor + i - 1] = shell->buffer[shell->cursor + i];
}
shell->length--;
shell->cursor--;
shell->buffer[shell->length] = 0;
shellDisplayByte(shell, '\b');
for (short i = shell->cursor; i < shell->length; i++)
{
shellDisplayByte(shell, shell->buffer[i]);
}
shellDisplayByte(shell, ' ');
for (short i = shell->length - shell->cursor + 1; i > 0; i--)
{
shellDisplayByte(shell, '\b');
}
}
}
/**
* @brief shell Tab键输入处理
*
* @param shell shell对象
*/
static void shellTab(SHELL_TypeDef *shell)
{
unsigned short maxMatch = SHELL_COMMAND_MAX_LENGTH;
unsigned short lastMatchIndex = 0;
unsigned short matchNum = 0;
unsigned short length;
SHELL_CommandTypeDef *base = shell->commandBase;
if (shell->length != 0)
{
shell->buffer[shell->length] = 0;
for (short i = 0; i < shell->commandNumber; i++)
{
if (shellStringCompare(shell->buffer,
(char *)(base + i)->name)
== shell->length)
{
if (matchNum != 0)
{
if (matchNum == 1)
{
shellDisplay(shell, "\r\n");
}
shellDisplayItem(shell, lastMatchIndex);
length = shellStringCompare((char *)(base + lastMatchIndex)->name,
(char *)(base +i)->name);
maxMatch = (maxMatch > length) ? length : maxMatch;
}
lastMatchIndex = i;
matchNum ++;
}
}
if (matchNum == 0)
{
return;
}
if (matchNum == 1)
{
shellClearLine(shell);
}
if (matchNum != 0)
{
shell->length = shellStringCopy(shell->buffer,
(char *)(base + lastMatchIndex)->name);
}
if (matchNum > 1)
{
shellDisplayItem(shell, lastMatchIndex);
shellDisplay(shell, shell->command);
shell->length = maxMatch;
}
shell->buffer[shell->length] = 0;
shell->cursor = shell->length;
shellDisplay(shell, shell->buffer);
}
else
{
shellHelp(shell, 1, (void *)0);
shellDisplay(shell, shell->command);
}
#if SHELL_LONG_HELP == 1
static int time = 0;
if (SHELL_GET_TICK())
{
if (matchNum == 1 && SHELL_GET_TICK() - time < SHELL_DOUBLE_CLICK_TIME)
{
shellClearLine(shell);
for (short i = shell->length; i >= 0; i--)
{
shell->buffer[i + 5] = shell->buffer[i];
}
shellStringCopy(shell->buffer, "help");
shell->buffer[4] = ' ';
shell->length += 5;
shell->cursor = shell->length;
shellDisplay(shell, shell->buffer);
}
time = SHELL_GET_TICK();
}
#endif /** SHELL_LONG_HELP == 1 */
}
/**
* @brief shell正常按键处理
*
* @param shell shell对象
* @param data 输入的数据
*/
static void shellNormal(SHELL_TypeDef *shell, char data)
{
if (shell->length < SHELL_COMMAND_MAX_LENGTH - 1)
{
if (shell->length == shell->cursor)
{
shell->buffer[shell->length++] = data;
shell->cursor++;
shellDisplayByte(shell, data);
}
else
{
for (short i = shell->length - shell->cursor; i > 0; i--)
{
shell->buffer[shell->cursor + i] = shell->buffer[shell->cursor + i - 1];
}
shell->buffer[shell->cursor++] = data;
shell->buffer[++shell->length] = 0;
for (short i = shell->cursor - 1; i < shell->length; i++)
{
shellDisplayByte(shell, shell->buffer[i]);
}
for (short i = shell->length - shell->cursor; i > 0; i--)
{
shellDisplayByte(shell, '\b');
}
}
}
else
{
shellDisplay(shell, "\r\nWarnig: Command is too long\r\n");
shellDisplay(shell, shell->command);
shellDisplay(shell, shell->buffer);
shell->cursor = shell->length;
}
}
/**
* @brief shell ansi控制系列处理
*
* @param shell shell对象
* @param data 输入的数据
*/
void shellAnsi(SHELL_TypeDef *shell, char data)
{
switch ((unsigned char)(shell->status))
{
case SHELL_ANSI_CSI:
switch (data)
{
case 0x41: /** 方向上键 */
shellHistory(shell, 0);
break;
case 0x42: /** 方向下键 */
shellHistory(shell, 1);
break;
case 0x43: /** 方向右键 */
if (shell->cursor < shell->length)
{
shellDisplayByte(shell, shell->buffer[shell->cursor]);
shell->cursor++;
}
break;
case 0x44: /** 方向左键 */
if (shell->cursor > 0)
{
shellDisplayByte(shell, '\b');
shell->cursor--;
}
break;
default:
break;
}
shell->status = SHELL_IN_NORMAL;
break;
case SHELL_ANSI_ESC:
if (data == 0x5B)
{
shell->status = SHELL_ANSI_CSI;
}
else
{
shell->status = SHELL_IN_NORMAL;
}
break;
default:
break;
}
}
/**
* @brief shell处理
*
* @param shell shell对象
* @param data 输入数据
*/
void shellHandler(SHELL_TypeDef *shell, char data)
{
if (shell->status == SHELL_IN_NORMAL)
{
switch (data)
{
case '\r':
case '\n':
shellEnter(shell);
break;
case '\b':
case 0x7F:
shellBackspace(shell);
break;
case '\t':
shellTab(shell);
break;
case 0x1B:
shell->status = SHELL_ANSI_ESC;
break;
default:
shellNormal(shell, data);
break;
}
}
else
{
shellAnsi(shell, data);
}
}
#if SHELL_USING_TASK == 1
/**
* @brief shell 任务
*
* @param param shell对象
*
* @note 使用操作系统时定义的shell read函数必须是阻塞式的
* @note 不使用操作系统时可以通过不断查询的方式使用shell修改宏SHELL_TASK_WHILE
* 为0然后在主循环中不断调用此函数
*/
void shellTask(void *param)
{
SHELL_TypeDef *shell = (SHELL_TypeDef *)param;
char data;
if (shell->read == NULL)
{
shellDisplay(shell, "error: shell.read() must be defined\r\n");
while (1) ;
}
#if SHELL_TASK_WHILE == 1
while (1)
{
#endif
if (shell->read(&data) == 0)
{
shellHandler(shell, data);
}
#if SHELL_TASK_WHILE == 1
}
#endif
}
#endif
/**
* @brief shell显示一条命令信息
*
* @param shell shell对象
* @param index 要显示的命令索引
*/
static void shellDisplayItem(SHELL_TypeDef *shell, unsigned short index)
{
unsigned short spaceLength;
SHELL_CommandTypeDef *base = shell->commandBase;
spaceLength = 22 - shellDisplay(shell, (base + index)->name);
spaceLength = (spaceLength > 0) ? spaceLength : 4;
do {
shellDisplay(shell, " ");
} while (--spaceLength);
shellDisplay(shell, "--");
shellDisplay(shell, (base + index)->desc);
shellDisplay(shell, "\r\n");
}
/**
* @brief shell帮助
*
* @param shell shell对象
* @param argc 参数个数
* @param argv 参数
*/
static void shellHelp(SHELL_TypeDef *shell, int argc, char *argv[])
{
#if SHELL_LONG_HELP == 1
if (argc == 1)
{
#endif /** SHELL_LONG_HELP == 1 */
shellDisplay(shell, "\r\nCOMMAND LIST:\r\n\r\n");
for(unsigned short i = 0; i < shell->commandNumber; i++)
{
shellDisplayItem(shell, i);
}
#if SHELL_LONG_HELP == 1
}
else if (argc == 2) {
SHELL_CommandTypeDef *base = shell->commandBase;
for (unsigned char i = 0; i < shell->commandNumber; i++)
{
if (strcmp((const char *)argv[1], (base + i)->name) == 0)
{
shellDisplay(shell, "command help --");
shellDisplay(shell, (base + i)->name);
shellDisplay(shell, ":\r\n");
shellDisplay(shell, (base + i)->desc);
shellDisplay(shell, "\r\n");
if ((base + i)->help)
{
shellDisplay(shell, (base + i)->help);
shellDisplay(shell, "\r\n");
}
return;
}
}
shellDisplay(shell, "command not found\r\n");
}
#endif /** SHELL_LONG_HELP == 1 */
}
SHELL_EXPORT_CMD_EX(help, shellHelp, command help, help [command] --show help info of command);