1.shell主要原理
shell代码部分在components/finsh/shell.c 下,其主要原理是创建一个tshell线程,在空闲时获取字符,如果输入/n 字符,则调用msh_exec 来解析指令,在msh_exec 中,代码会寻找是否有相对应的指令,如果没有,则打印报错,输入的指令出错,如果有则调用cmd_func()来执行指令,cmd_func是一个声明,定义如下
typedef int (cmd_function_t)(int argc, char *argv);
在调用之前,代码会提取出我们输入的指令和参数,也就说,我们最后的指令调用,都会变成函数的调用
2.有部分操作会使用ansi转义字符,定义如下
ANSI转义序列是一种带内信号的转义序列标准,用于控制视频文本终端上的光标位置、颜色和其他选项。在文本中嵌入确定的字节序列,大部分以ESC转义字符和”[“字符开始,终端会把这些字节序列解释为相应的指令,而不是普通的字符编码。
在rt-thread中,ansi主要是在输出是对相应的内容进行控制,比如clear命令,简单了解即可
3.代码解析
1.重要结构认识
比较重要的结构体有finsh_shell,这个结构体代表着我们输入的字符串,里面重要的成员有
char line[FINSH_CMD_SIZE + 1]
rt_uint16_t line_position;
rt_uint16_t line_curpos;
第一个是我们输入的字符存放的地方,第二个为字符的长度,第三个为光标在的地方,因为有时候我们会按左右键,导致光标的位置和字符的长度不一样,这一点很值得注意
普通字符处理
首先代码544行,通过finsh_getchar获取了字符,如果这个字符不是特殊含义的字符,最终就会当作普通字符处理。
普通字符的处理在shell.c 的729-750行,729行判断当前的光标在是否在最后(因为你可以通过左右键改变光标位置),如果当前光标不在最后,733行rt-memmove 函数,肉眼可见的就是将当前指针后面的字符向后移1位,736行插入字符,如果光标就在字符串最后,746行直接放入line中
对 上下左右键 的处理
之前我们提到rt-thread使用了ANSI转义字符,在这里就可以体现,在我们点击键盘上的 上键 是,计算机实际上是识别为了 0x1b 0x5b 0x41这三个ascii码,0x1b是esc的ascii码,0x5b是[的ascii码,0x41是A的ascii码
一下是上下左右键的ascii码
代码557-574 行,就是判断我们输入的是不是上下左右键,如果是 shell->stat 的表示就会被置为WAIT_FUNC_KEY ,之后在判断具体是哪个键值
对上键的处理
576行判断输入的字符是行上键,578行,如果打开了FINSH_USING_HISTORY 功能,580-587判断shell->current_history 的值,589行,将对应的历史指令赋值到当前指令上,
591行设置光标位置和代码长度,592行调用shell_handle_history,代码如下
可以看到,这个部分的主要内容是清楚上一行,并且输出刚刚处理的内容,清楚上一行的指令在452行
对下键的处理
这个部分和对上键的处理大同小异,不在赘述
对左键的处理
这部分代码比较简单,判断如果当前光标位置大于0,将齐减1,退一格
对右键的处理
对右键的处理比较好玩,因为没有字符让光标前进一个,所以干脆就打印一边原来位置的字符,并且让光标位置加一,这样看起来就好像只有光标动,其他没动
对错误字符的处理
不理他,直接不处理
对tab的处理
647将光标退到最前面,651补全代码并且输出,653将光标位置和指令长度重新复制
对backspace 和 del 的处理
这边的处理分两种情况,分别是光标是否在最后,如果在最后,就比较好处理,684行直接把原来的字符用空格覆盖掉,即可,如果光标不在最后一行,671行调用rt_memmove把前面一个位置的字符覆盖,其他代码和插入字符的代码类似
对/n 和/r 的处理
shell_push_history 是将当前的指令放到cmd_history 这样的话,我们才可以用上键对之前的代码进行操作,699-704则是打印换行,然后调用msh_exec执行我们的命令,718-721行将指令清空。
原作者:KIMI_7569