boot中的行编辑功能

一 行编辑的由来

在和程序交互的过程中,用户输入难免会出错,如果不小心键入失误而不能修改,那该是一种很不友好的体验,于是行编辑出现了,用户在输入过程中可以删除,粘贴,移动光标,在光标处插入,可以翻看历史命令,只有当用户确认输入是正确无误,才按回车将输入结果提交给程序。

如果行编辑功能由用户程序来实现,那么将增加用户程序编程的难度,而且很多行编辑功能基本是相似的,于是linux就把它放在了内核中,也就是常说的行规程(line discipline), 终端类型不同,对应的line discipline也不一样,用户进程每次通过tty读取line discipline处理后的一行数据,另外用户也可以通过termios配置line discipline的行为,当然了这种配置是有限的,如果用户程序希望定制自己特有的行编辑功能,如一些编辑软件vi,shell等等,可以将tty设置为raw模式,这将跳过line discipline处理直接读取输入的原始值,而将行编辑功能交由特定的用户程序来处理,如readline库就是其中一个使用非常广泛的处理行编辑的开源库。

本文主要讲解boot中行编辑功能的实现,功能较简单。


二 行编辑的实现

#define CTRL(x) ((x)-'@')

#define VKEY(x)		(0x100|(x))
#define VKEY_UP		VKEY(1)
#define VKEY_DOWN	VKEY(2)
#define VKEY_LEFT	VKEY(3)
#define VKEY_RIGHT	VKEY(4)
#define VKEY_PGUP	VKEY(5)
#define VKEY_PGDN	VKEY(6)
#define VKEY_HOME	VKEY(7)
#define VKEY_END	VKEY(8)
#define VKEY_F1		VKEY(0x10)
#define VKEY_F2		VKEY(0x11)
#define VKEY_F3		VKEY(0x12)
#define VKEY_F4		VKEY(0x13)
#define VKEY_F5		VKEY(0x14)
#define VKEY_F6		VKEY(0x15)
#define VKEY_F7		VKEY(0x16)
#define VKEY_F8		VKEY(0x17)
#define VKEY_F9		VKEY(0x18)
#define VKEY_F10	VKEY(0x19)
#define VKEY_F11	VKEY(0x1A)
#define VKEY_F12	VKEY(0x1B)
#define VKEY_ESC	27

int console_readline(char *prompt,char *str,int maxlen)
{
    int reading = 1;
    int ch;
    int idx = 0;
    int len = 0;
    int t;
    int klen;
    int recall;
    int nosave = 0;
    char *x;
    char env[10];

    console_inreadline++;
    recall = console_nextsave;

    if (console_savedlines[console_nextsave]) {
	KFREE(console_savedlines[console_nextsave]);
	console_savedlines[console_nextsave] = NULL;
	}
    console_savedlines[console_nextsave] = strdup("");

    if (prompt && *prompt) console_write(prompt,strlen(prompt));

    POLL();
    while (reading) {

	/*
	 * If someone used console_log (above) or hit Control-C (below),
	 * redisplay the prompt and the string we've got so far.
	 */

	if (console_redisplay) {
	    if (prompt && *prompt) console_write(prompt,strlen(prompt));
	    console_write(str,idx);
	    console_redisplay = 0;
	    continue;
	    }

	/*
	 * if nobody's typed anything, keep polling
	 */

	if (console_status() == 0) {
	    POLL();
	    continue;
	    }

	/*
	 * Get the char from the keyboard
	 */

	ch = console_readkey();
	if (ch < 0) break;
	if (ch == 0) continue;

	/*
	 * And dispatch it.  Lots of yucky character manipulation follows
	 */

	switch (ch) {
	    case CTRL('C'):			/* Ctrl-C - cancel line */
		console_write("^C\r\n",4);
		console_redisplay = 1;
		nosave = 1;
		idx = 0;
		len = 0;
		break;

	    case 0x7f:				/* Backspace, Delete */
	    case CTRL('H'):
		if (idx > 0) {
		    nosave = 0;
		    len--;
		    idx--;
		    console_write("\b",1);
		    if (len != idx) {
			for (t = idx; t < len; t++) str[t] = str[t+1];
			console_write(&str[idx],len-idx);
			console_whiteout(1);
			console_backspace(len-idx);
			}
		    else {
			console_whiteout(1);
			}
		    }
		break;

	    case CTRL('D'):			/* Ctrl-D */
		if ((idx > 0) && (len != idx)) {
		    nosave = 0;
		    len--;
		    for (t = idx; t < len; t++) str[t] = str[t+1];
		    console_write(&str[idx],len-idx);
		    console_whiteout(1);
		    console_backspace(len-idx);
		    }
		break;

	    case CTRL('B'):			/* cursor left */
	    case VKEY_LEFT:
		if (idx > 0) {
		    idx--;
		    console_backspace(1);
		    }
		break;

	    case CTRL('F'):			/* cursor right */
	    case VKEY_RIGHT:
		if (idx < len) {
		    console_write(&str[idx],1);
		    idx++;
		    }
		break;

	    case CTRL('A'):			/* cursor to BOL */
		console_backspace(idx);
		idx = 0;
		break;

	    case CTRL('E'):			/* cursor to EOL */
		if (len-idx > 0) console_write(&str[idx],len-idx);
		idx = len;
		break;

	    case CTRL('K'):			/* Kill to EOL */
		if (idx != len) {
		    str[len] = '\0';
		    if (console_killbuffer) KFREE(console_killbuffer);
		    console_killbuffer = strdup(&str[idx]);
		    console_whiteout(len-idx);
		    len = idx;
		    nosave = 0;
		    }
		break;

	    case CTRL('Y'):			/* Yank killed data */
		if (console_killbuffer == NULL) break;
		klen = strlen(console_killbuffer);
		if (klen == 0) break;
		if (len + klen > maxlen) break;
		nosave = 0;
		for (t = len + klen; t > idx; t--) {
		    str[t-1] = str[t-klen-1];
		    }
		for (t = 0; t < klen; t++) str[t+idx] = console_killbuffer[t];
		len += klen;
		console_write(&str[idx],len-idx);
		idx += klen;
		console_backspace(len-idx-1);
		break;

	    case CTRL('R'):			/* Redisplay line */
		str[len] = 0;
		console_crlf();
		if (prompt && *prompt) console_write(prompt,strlen(prompt));
		console_write(str,len);
		console_backspace(len-idx);
		break;

	    case CTRL('U'):			/* Cancel line */
		console_backspace(idx);
		console_eraseeol();
		if (len > 0) nosave = 1;
		idx = 0;
		len = 0;
		break;

	    case CTRL('M'):			/* terminate */
	    case CTRL('J'):
		console_crlf();
		reading = 0;
		break;

	    case CTRL('P'):
	    case VKEY_UP:			/* recall previous line */
		t = recall;
		t--;
		if (t < 0) t = MAXSAVELINES-1;
		if (console_savedlines[t] == NULL) break;
		recall = t;
		console_backspace(idx);
		strcpy(str,console_savedlines[recall]);
		len = idx = strlen(console_savedlines[recall]);
		console_eraseeol();
		console_write(str,len);
		nosave = 1;
		break;
		
	    case CTRL('N'):
	    case VKEY_DOWN:			/* Recall next line */
		t = recall; 
		t++;
		if (t == MAXSAVELINES) t = 0;
		if (console_savedlines[t] == NULL) break;
		recall = t;
		console_backspace(idx);
		strcpy(str,console_savedlines[recall]);
		len = idx = strlen(console_savedlines[recall]);
		console_eraseeol();
		console_write(str,len);
		nosave = 1;
		break;

	    case VKEY_F1:
	    case VKEY_F2:
	    case VKEY_F3:
	    case VKEY_F4:
	    case VKEY_F5:
	    case VKEY_F6:
	    case VKEY_F7:
	    case VKEY_F8:
	    case VKEY_F9:
	    case VKEY_F10:
	    case VKEY_F11:
	    case VKEY_F12:
		sprintf(env,"F%d",ch-VKEY_F1+1);
		x = env_getenv(env);
		if (x) {
		    console_backspace(idx);
		    strcpy(str,x);
		    idx = len = strlen(str);
		    console_eraseeol();
		    console_write(str,len);
		    console_crlf();
		    reading = 0;
		    nosave = 1;
		    }
		/*
		 * If F12 is undefined, it means "repeat last command"
		 */
		if (ch == VKEY_F12) {
		    t = recall;
		    t--;
		    if (t < 0) t = MAXSAVELINES-1;
		    if (console_savedlines[t] == NULL) break;
		    recall = t;
		    console_backspace(idx);
		    strcpy(str,console_savedlines[recall]);
		    len = idx = strlen(console_savedlines[recall]);
		    console_eraseeol();
		    console_write(str,len);
		    console_crlf();
		    reading = 0;
		    nosave = 1;
		    }
		break;

	    default:				/* insert character */
		if (ch >= ' ') {
		    if (idx < (maxlen-1)) {
			nosave = 0;
			for (t = len; t > idx; t--) {
			    str[t] = str[t-1];
			    }
			str[idx] = ch;
			len++;
			if (len != idx) {
			    if(g_enable_printf) /*------skyworth add*/
			    {
			    console_write(&str[idx],len-idx);
			    }
			    console_backspace(len-idx-1);
			    }
			idx++;
			}
		    }
		break;
	    }
	}
    POLL();

    console_inreadline--;

    str[len] = 0;

    if ((len != 0) && !nosave) {
	if (console_savedlines[console_nextsave]) {
	    KFREE(console_savedlines[console_nextsave]);
	    }
	console_savedlines[console_nextsave] = strdup(str);
	console_nextsave++;
	if (console_nextsave == MAXSAVELINES) console_nextsave = 0;
	}

    return len;
}

int console_readkey(void)
{
    unsigned char ch;
    int num;

    GETCHAR(ch);

    switch (ch) {
	case VKEY_ESC:
	    GETCHAR(ch);
	    switch (ch) {
		case 'O':
		    GETCHAR(ch);
		    switch (ch) {
			case 'P':
			    return VKEY_F1;
			case 'Q':
			    return VKEY_F2;
			case 'R':
			    return VKEY_F3;
			case 'S':
			    return VKEY_F4;
			}
		    return (int)ch;

		case '[':
		    GETCHAR(ch);
		    if ((ch >= '0') && (ch <= '9')) {
			console_readnum(&num,&ch);
			if (ch == '~') {
			    switch (num) {
				case 2:
				    return VKEY_HOME;
				case 3:
				    return VKEY_PGUP;
				case 5:
				    if (console_mode == XTERM) return VKEY_PGUP;
				    return VKEY_END;
				case 6:
				    if (console_mode == XTERM) return VKEY_PGDN;
				    return VKEY_PGDN;
				case 11:
				    return VKEY_F1;
				case 12:
				    return VKEY_F2;
				case 13:
				    return VKEY_F3;
				case 14:
				    return VKEY_F4;
				case 15:
				    return VKEY_F5;
				case 17:
				    return VKEY_F6;
				case 18:
				    return VKEY_F7;
				case 19:
				    return VKEY_F8;
				case 20:
				    return VKEY_F9;
				case 21:
				    return VKEY_F10;
				case 23:
				    return VKEY_F11;
				case 24:
				    return VKEY_F12;
				}
			    return (int)ch;
			    }
			}
		    else {
			switch (ch) {
			    case 'A':
				return VKEY_UP;
			    case 'B':
				return VKEY_DOWN;
			    case 'C':
				return VKEY_RIGHT;
			    case 'D':
				return VKEY_LEFT;
			    case 'F':
				return VKEY_HOME;
			    case 'H':
				return VKEY_END;
			    default:
				return (int) ch;
			    }
			}
		default:
		    return (int)ch;
	
		}
	default:
	    return (int) ch;
	}
}

static void console_readnum(int *num,unsigned char *ch)
{
    int total = 0;

    for (;;) {
	total = (total * 10) + (*ch - '0');
	while (console_read(ch,1) != 1) { POLL(); }
	if (!((*ch >= '0') && (*ch <= '9'))) break;
	}

    *num = total;
}

static void console_backspace(int n)
{
    int t;

    for (t = 0; t < n; t++) console_write("\b",1);
}

static void console_whiteout(int n)
{
    int t;

    for (t = 0; t < n; t++) console_write(" ",1);
    for (t = 0; t < n; t++) console_write("\b",1);
}


static void console_eraseeol(void)
{
    console_write("\033[K",3);
}

static void console_crlf(void)
{
    console_write("\r\n",2);
}

上述代码我就不做过多解释了,一看就会明白,这里主要提一下console_readkey(), 为啥返回的是Int型而不是char型呢,这主要涉及到转义序列, 普通字符直接转化为int返回了

len为输入的总的字符数,idx为当前光标所在的位置,console_write()用于回显和光标的移动
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值