static int cread_line()

in common/cli_readline.c [247:512]


static int cread_line(const char *const prompt, char *buf, unsigned int *len,
		int timeout)
{
	unsigned long num = 0;
	unsigned long eol_num = 0;
	unsigned long wlen;
	char ichar;
	int insert = 1;
	int esc_len = 0;
	char esc_save[8];
	int init_len = strlen(buf);
	int first = 1;

	if (init_len)
		cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);

	while (1) {
		if (bootretry_tstc_timeout())
			return -2;	/* timed out */
		if (first && timeout) {
			uint64_t etime = endtick(timeout);

			while (!tstc()) {	/* while no incoming data */
				if (get_ticks() >= etime)
					return -2;	/* timed out */
				WATCHDOG_RESET();
			}
			first = 0;
		}

		ichar = getcmd_getch();

		/* ichar=0x0 when error occurs in U-Boot getc */
		if (!ichar)
			continue;

		if ((ichar == '\n') || (ichar == '\r')) {
			putc('\n');
			break;
		}

		/*
		 * handle standard linux xterm esc sequences for arrow key, etc.
		 */
		if (esc_len != 0) {
			enum { ESC_REJECT, ESC_SAVE, ESC_CONVERTED } act = ESC_REJECT;

			if (esc_len == 1) {
				if (ichar == '[' || ichar == 'O')
					act = ESC_SAVE;
			} else if (esc_len == 2) {
				switch (ichar) {
				case 'D':	/* <- key */
					ichar = CTL_CH('b');
					act = ESC_CONVERTED;
					break;	/* pass off to ^B handler */
				case 'C':	/* -> key */
					ichar = CTL_CH('f');
					act = ESC_CONVERTED;
					break;	/* pass off to ^F handler */
				case 'H':	/* Home key */
					ichar = CTL_CH('a');
					act = ESC_CONVERTED;
					break;	/* pass off to ^A handler */
				case 'F':	/* End key */
					ichar = CTL_CH('e');
					act = ESC_CONVERTED;
					break;	/* pass off to ^E handler */
				case 'A':	/* up arrow */
					ichar = CTL_CH('p');
					act = ESC_CONVERTED;
					break;	/* pass off to ^P handler */
				case 'B':	/* down arrow */
					ichar = CTL_CH('n');
					act = ESC_CONVERTED;
					break;	/* pass off to ^N handler */
				case '1':
				case '3':
				case '4':
				case '7':
				case '8':
					if (esc_save[1] == '[') {
						/* see if next character is ~ */
						act = ESC_SAVE;
					}
					break;
				}
			} else if (esc_len == 3) {
				if (ichar == '~') {
					switch (esc_save[2]) {
					case '3':	/* Delete key */
						ichar = CTL_CH('d');
						act = ESC_CONVERTED;
						break;	/* pass to ^D handler */
					case '1':	/* Home key */
					case '7':
						ichar = CTL_CH('a');
						act = ESC_CONVERTED;
						break;	/* pass to ^A handler */
					case '4':	/* End key */
					case '8':
						ichar = CTL_CH('e');
						act = ESC_CONVERTED;
						break;	/* pass to ^E handler */
					}
				}
			}

			switch (act) {
			case ESC_SAVE:
				esc_save[esc_len++] = ichar;
				continue;
			case ESC_REJECT:
				esc_save[esc_len++] = ichar;
				cread_add_str(esc_save, esc_len, insert,
					      &num, &eol_num, buf, *len);
				esc_len = 0;
				continue;
			case ESC_CONVERTED:
				esc_len = 0;
				break;
			}
		}

		switch (ichar) {
		case 0x1b:
			if (esc_len == 0) {
				esc_save[esc_len] = ichar;
				esc_len = 1;
			} else {
				puts("impossible condition #876\n");
				esc_len = 0;
			}
			break;

		case CTL_CH('a'):
			BEGINNING_OF_LINE();
			break;
		case CTL_CH('c'):	/* ^C - break */
			*buf = '\0';	/* discard input */
			return -1;
		case CTL_CH('f'):
			if (num < eol_num) {
				getcmd_putch(buf[num]);
				num++;
			}
			break;
		case CTL_CH('b'):
			if (num) {
				getcmd_putch(CTL_BACKSPACE);
				num--;
			}
			break;
		case CTL_CH('d'):
			if (num < eol_num) {
				wlen = eol_num - num - 1;
				if (wlen) {
					memmove(&buf[num], &buf[num+1], wlen);
					putnstr(buf + num, wlen);
				}

				getcmd_putch(' ');
				do {
					getcmd_putch(CTL_BACKSPACE);
				} while (wlen--);
				eol_num--;
			}
			break;
		case CTL_CH('k'):
			ERASE_TO_EOL();
			break;
		case CTL_CH('e'):
			REFRESH_TO_EOL();
			break;
		case CTL_CH('o'):
			insert = !insert;
			break;
		case CTL_CH('x'):
		case CTL_CH('u'):
			BEGINNING_OF_LINE();
			ERASE_TO_EOL();
			break;
		case DEL:
		case DEL7:
		case 8:
			if (num) {
				wlen = eol_num - num;
				num--;
				memmove(&buf[num], &buf[num+1], wlen);
				getcmd_putch(CTL_BACKSPACE);
				putnstr(buf + num, wlen);
				getcmd_putch(' ');
				do {
					getcmd_putch(CTL_BACKSPACE);
				} while (wlen--);
				eol_num--;
			}
			break;
		case CTL_CH('p'):
		case CTL_CH('n'):
		{
			char *hline;

			esc_len = 0;

			if (ichar == CTL_CH('p'))
				hline = hist_prev();
			else
				hline = hist_next();

			if (!hline) {
				getcmd_cbeep();
				continue;
			}

			/* nuke the current line */
			/* first, go home */
			BEGINNING_OF_LINE();

			/* erase to end of line */
			ERASE_TO_EOL();

			/* copy new line into place and display */
			strcpy(buf, hline);
			eol_num = strlen(buf);
			REFRESH_TO_EOL();
			continue;
		}
#ifdef CONFIG_AUTO_COMPLETE
		case '\t': {
			int num2, col;

			/* do not autocomplete when in the middle */
			if (num < eol_num) {
				getcmd_cbeep();
				break;
			}

			buf[num] = '\0';
			col = strlen(prompt) + eol_num;
			num2 = num;
			if (cmd_auto_complete(prompt, buf, &num2, &col)) {
				col = num2 - num;
				num += col;
				eol_num += col;
			}
			break;
		}
#endif
		default:
			if (ichar >= ' ' && ichar <= '~') {
				cread_add_char(ichar, insert, &num, &eol_num,
					       buf, *len);
			}
			break;
		}
	}
	*len = eol_num;
	buf[eol_num] = '\0';	/* lose the newline */

	if (buf[0] && buf[0] != CREAD_HIST_CHAR)
		cread_add_to_hist(buf);
	hist_cur = hist_add_idx;

	return 0;
}