前言
本文作为《守护进程接收终端输入的一种变通性方法》的补充版,主要讨论不使用第三方库时,如何支持字符终端命令行的退格和历史记录。文中涉及的代码运行环境如下:
一 退格键
术语“退格”(BS,BackSpace)本意指删除光标左侧的一个字符。最初的打字机中,退格键将机架(carriage)回退一个位置;而在现代计算机系统中,退格键将显示器光标左移一个位置,并删除该处的字符,然后将该处之后的文字左移一个位置。
删除(DEL,Delete)键可追溯到计算机使用打孔磁带的年代。当时,纠正一个字符打孔错误的唯一办法是在磁带上将额外的比特位置打孔。所有比特位被打孔的字符被视为已删除,其对应ASCII控制码DEL(0x7f)。要删除最后输入的字符,必须先点击BS键回移一个位置,再点击DEL键删除该字符。在较新的计算机中,对BS键或DEL键的一次点击将同时完成回移和删除。
在现代计算机系统中,退格键常被映射到DEL符(0x7f),但保留退格键删除光标之前字符的功能。
在计算机终端上点击退格键(如[<—])或Ctrl+H组合键会产生ASCII控制码BS。若终端未将退格键映射为回移光标并删除字符的功能,则点击退格键时显示^H符号。即使终端将退格键解释为删除前置字符,接收文本的系统也不一定如此。这样,屏幕将显示未删除的文本,并包含可见的删除码。如:
You want to delete^H^H^H |
在Linux系统中,通过'man ascii'命令可查看ASCII码字符集。如:
字符 |
8进制 |
10进制 |
16进制 |
BS '' (backspace) |
010 |
8 |
0x08 |
DEL |
177 |
127 |
0x7f |
使用SecureCRT终端时,可重新设置码值映射关系。例如,若想使退格键实现回移和删除(而非打印^H),可在Options->Session Options->Terminal->Emulation->Mapped Keys页的Other mappings选框中,勾选“退格发送删除”,即:
若不做该修改,也可使用Ctrl+退格组合键达到相同的效果。
注意,SecureCRT在VT100模式下时,删除键和退格键均删除光标前面的字符。若要删除光标处(向后删除)的字符,需在Emulation页的Terminal类型中选择Linux或Xterm模式。
其他终端下删除键和退格键的适配可参考《Consistent BackSpace and Delete Configuration》。
二 命令历史
命令历史模拟Linux Shell风格,即保存已输入的命令行,用户通过上下键调取前条或后条命令(不同于Shell的history命令)。
上移键和下移键对应由转义符开头的三个ASCII码。上移键对应{0x1b, 0x5b, 0x41},其打印形式为^[[A;下移键对应{0x1b, 0x5b, 0x42},其打印形式为^[[B。
本实现首先需要增加一组宏定义和数据结构,如下:
1 /* 方向控制键字符编码 */ 2 //上移键:{0x1b, 0x5b, 0x41} 3 //下移键:{0x1b, 0x5b, 0x42} 4 //右移键:{0x1b, 0x5b, 0x43} 5 //左移键:{0x1b, 0x5b, 0x44} 6 #define LINUX_KEY1 0x1b 7 #define LINUX_KEY2 0x5b 8 #define LINUX_KEY_UP 0x41 9 #define LINUX_KEY_DN 0x42 10 #define LINUX_KEY_LT 0x43 11 #define LINUX_KEY_RT 0x44 12 #define LINUX_KEY_BS 0x08 13 14 /* 命令历史环形列表结构 */ 15 typedef struct{ 16 INT32U dwTail; //命令列表的尾端,即最后一条命令的下个位置 17 INT32U dwSelCmdIdx; //上下键翻转时当前选择的命令索引 18 CHAR szCmd[CMD_MAX_NUM][CMD_MAX_SIZE]; //历史命令 19 }T_CMD_HIST; 20 21 //前条或后条命令索引 22 #define PREV_CMD_IDX(curIdx) ((curIdx+CMD_MAX_NUM-1)%CMD_MAX_NUM) 23 #define NEXT_CMD_IDX(curIdx) ((curIdx+1)%CMD_MAX_NUM)
命令历史以环形列表存储,即表满时最新存入的命令会覆盖最老的命令。
然后定义全局的命令历史列表:
1 /* 命令历史列表 */ 2 static T_CMD_HIST gCmdHist = {0, 0, {{0}}};
其判空函数为:
1 /* 判断命令历史列表是否为空 */ 2 static BOOL IsCmdsEmpty(T_CMD_HIST *ptCmds) 3 { 4 return '