mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-14 08:57:58 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
656 lines
12 KiB
C
656 lines
12 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2000
|
|
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
|
*
|
|
* Add to readline cmdline-editing by
|
|
* (C) Copyright 2005
|
|
* JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <bootretry.h>
|
|
#include <cli.h>
|
|
#include <watchdog.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
static const char erase_seq[] = "\b \b"; /* erase sequence */
|
|
static const char tab_seq[] = " "; /* used to expand TABs */
|
|
|
|
char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */
|
|
|
|
static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen)
|
|
{
|
|
char *s;
|
|
|
|
if (*np == 0)
|
|
return p;
|
|
|
|
if (*(--p) == '\t') { /* will retype the whole line */
|
|
while (*colp > plen) {
|
|
puts(erase_seq);
|
|
(*colp)--;
|
|
}
|
|
for (s = buffer; s < p; ++s) {
|
|
if (*s == '\t') {
|
|
puts(tab_seq + ((*colp) & 07));
|
|
*colp += 8 - ((*colp) & 07);
|
|
} else {
|
|
++(*colp);
|
|
putc(*s);
|
|
}
|
|
}
|
|
} else {
|
|
puts(erase_seq);
|
|
(*colp)--;
|
|
}
|
|
(*np)--;
|
|
|
|
return p;
|
|
}
|
|
|
|
#ifdef CONFIG_CMDLINE_EDITING
|
|
|
|
/*
|
|
* cmdline-editing related codes from vivi.
|
|
* Author: Janghoon Lyu <nandy@mizi.com>
|
|
*/
|
|
|
|
#define putnstr(str, n) printf("%.*s", (int)n, str)
|
|
|
|
#define CTL_CH(c) ((c) - 'a' + 1)
|
|
#define CTL_BACKSPACE ('\b')
|
|
#define DEL ((char)255)
|
|
#define DEL7 ((char)127)
|
|
#define CREAD_HIST_CHAR ('!')
|
|
|
|
#define getcmd_putch(ch) putc(ch)
|
|
#define getcmd_getch() getc()
|
|
#define getcmd_cbeep() getcmd_putch('\a')
|
|
|
|
#define HIST_MAX 20
|
|
#define HIST_SIZE CONFIG_SYS_CBSIZE
|
|
|
|
static int hist_max;
|
|
static int hist_add_idx;
|
|
static int hist_cur = -1;
|
|
static unsigned hist_num;
|
|
|
|
static char *hist_list[HIST_MAX];
|
|
static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */
|
|
|
|
#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1)
|
|
|
|
static void hist_init(void)
|
|
{
|
|
int i;
|
|
|
|
hist_max = 0;
|
|
hist_add_idx = 0;
|
|
hist_cur = -1;
|
|
hist_num = 0;
|
|
|
|
for (i = 0; i < HIST_MAX; i++) {
|
|
hist_list[i] = hist_lines[i];
|
|
hist_list[i][0] = '\0';
|
|
}
|
|
}
|
|
|
|
static void cread_add_to_hist(char *line)
|
|
{
|
|
strcpy(hist_list[hist_add_idx], line);
|
|
|
|
if (++hist_add_idx >= HIST_MAX)
|
|
hist_add_idx = 0;
|
|
|
|
if (hist_add_idx > hist_max)
|
|
hist_max = hist_add_idx;
|
|
|
|
hist_num++;
|
|
}
|
|
|
|
static char *hist_prev(void)
|
|
{
|
|
char *ret;
|
|
int old_cur;
|
|
|
|
if (hist_cur < 0)
|
|
return NULL;
|
|
|
|
old_cur = hist_cur;
|
|
if (--hist_cur < 0)
|
|
hist_cur = hist_max;
|
|
|
|
if (hist_cur == hist_add_idx) {
|
|
hist_cur = old_cur;
|
|
ret = NULL;
|
|
} else {
|
|
ret = hist_list[hist_cur];
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static char *hist_next(void)
|
|
{
|
|
char *ret;
|
|
|
|
if (hist_cur < 0)
|
|
return NULL;
|
|
|
|
if (hist_cur == hist_add_idx)
|
|
return NULL;
|
|
|
|
if (++hist_cur > hist_max)
|
|
hist_cur = 0;
|
|
|
|
if (hist_cur == hist_add_idx)
|
|
ret = "";
|
|
else
|
|
ret = hist_list[hist_cur];
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifndef CONFIG_CMDLINE_EDITING
|
|
static void cread_print_hist_list(void)
|
|
{
|
|
int i;
|
|
unsigned long n;
|
|
|
|
n = hist_num - hist_max;
|
|
|
|
i = hist_add_idx + 1;
|
|
while (1) {
|
|
if (i > hist_max)
|
|
i = 0;
|
|
if (i == hist_add_idx)
|
|
break;
|
|
printf("%s\n", hist_list[i]);
|
|
n++;
|
|
i++;
|
|
}
|
|
}
|
|
#endif /* CONFIG_CMDLINE_EDITING */
|
|
|
|
#define BEGINNING_OF_LINE() { \
|
|
while (num) { \
|
|
getcmd_putch(CTL_BACKSPACE); \
|
|
num--; \
|
|
} \
|
|
}
|
|
|
|
#define ERASE_TO_EOL() { \
|
|
if (num < eol_num) { \
|
|
printf("%*s", (int)(eol_num - num), ""); \
|
|
do { \
|
|
getcmd_putch(CTL_BACKSPACE); \
|
|
} while (--eol_num > num); \
|
|
} \
|
|
}
|
|
|
|
#define REFRESH_TO_EOL() { \
|
|
if (num < eol_num) { \
|
|
wlen = eol_num - num; \
|
|
putnstr(buf + num, wlen); \
|
|
num = eol_num; \
|
|
} \
|
|
}
|
|
|
|
static void cread_add_char(char ichar, int insert, unsigned long *num,
|
|
unsigned long *eol_num, char *buf, unsigned long len)
|
|
{
|
|
unsigned long wlen;
|
|
|
|
/* room ??? */
|
|
if (insert || *num == *eol_num) {
|
|
if (*eol_num > len - 1) {
|
|
getcmd_cbeep();
|
|
return;
|
|
}
|
|
(*eol_num)++;
|
|
}
|
|
|
|
if (insert) {
|
|
wlen = *eol_num - *num;
|
|
if (wlen > 1)
|
|
memmove(&buf[*num+1], &buf[*num], wlen-1);
|
|
|
|
buf[*num] = ichar;
|
|
putnstr(buf + *num, wlen);
|
|
(*num)++;
|
|
while (--wlen)
|
|
getcmd_putch(CTL_BACKSPACE);
|
|
} else {
|
|
/* echo the character */
|
|
wlen = 1;
|
|
buf[*num] = ichar;
|
|
putnstr(buf + *num, wlen);
|
|
(*num)++;
|
|
}
|
|
}
|
|
|
|
static void cread_add_str(char *str, int strsize, int insert,
|
|
unsigned long *num, unsigned long *eol_num,
|
|
char *buf, unsigned long len)
|
|
{
|
|
while (strsize--) {
|
|
cread_add_char(*str, insert, num, eol_num, buf, len);
|
|
str++;
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
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:
|
|
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;
|
|
}
|
|
|
|
#endif /* CONFIG_CMDLINE_EDITING */
|
|
|
|
/****************************************************************************/
|
|
|
|
int cli_readline(const char *const prompt)
|
|
{
|
|
/*
|
|
* If console_buffer isn't 0-length the user will be prompted to modify
|
|
* it instead of entering it from scratch as desired.
|
|
*/
|
|
console_buffer[0] = '\0';
|
|
|
|
return cli_readline_into_buffer(prompt, console_buffer, 0);
|
|
}
|
|
|
|
|
|
int cli_readline_into_buffer(const char *const prompt, char *buffer,
|
|
int timeout)
|
|
{
|
|
char *p = buffer;
|
|
#ifdef CONFIG_CMDLINE_EDITING
|
|
unsigned int len = CONFIG_SYS_CBSIZE;
|
|
int rc;
|
|
static int initted;
|
|
|
|
/*
|
|
* History uses a global array which is not
|
|
* writable until after relocation to RAM.
|
|
* Revert to non-history version if still
|
|
* running from flash.
|
|
*/
|
|
if (gd->flags & GD_FLG_RELOC) {
|
|
if (!initted) {
|
|
hist_init();
|
|
initted = 1;
|
|
}
|
|
|
|
if (prompt)
|
|
puts(prompt);
|
|
|
|
rc = cread_line(prompt, p, &len, timeout);
|
|
return rc < 0 ? rc : len;
|
|
|
|
} else {
|
|
#endif /* CONFIG_CMDLINE_EDITING */
|
|
char *p_buf = p;
|
|
int n = 0; /* buffer index */
|
|
int plen = 0; /* prompt length */
|
|
int col; /* output column cnt */
|
|
char c;
|
|
|
|
/* print prompt */
|
|
if (prompt) {
|
|
plen = strlen(prompt);
|
|
puts(prompt);
|
|
}
|
|
col = plen;
|
|
|
|
for (;;) {
|
|
if (bootretry_tstc_timeout())
|
|
return -2; /* timed out */
|
|
WATCHDOG_RESET(); /* Trigger watchdog, if needed */
|
|
|
|
#ifdef CONFIG_SHOW_ACTIVITY
|
|
while (!tstc()) {
|
|
show_activity(0);
|
|
WATCHDOG_RESET();
|
|
}
|
|
#endif
|
|
c = getc();
|
|
|
|
/*
|
|
* Special character handling
|
|
*/
|
|
switch (c) {
|
|
case '\r': /* Enter */
|
|
case '\n':
|
|
*p = '\0';
|
|
puts("\r\n");
|
|
return p - p_buf;
|
|
|
|
case '\0': /* nul */
|
|
continue;
|
|
|
|
case 0x03: /* ^C - break */
|
|
p_buf[0] = '\0'; /* discard input */
|
|
return -1;
|
|
|
|
case 0x15: /* ^U - erase line */
|
|
while (col > plen) {
|
|
puts(erase_seq);
|
|
--col;
|
|
}
|
|
p = p_buf;
|
|
n = 0;
|
|
continue;
|
|
|
|
case 0x17: /* ^W - erase word */
|
|
p = delete_char(p_buf, p, &col, &n, plen);
|
|
while ((n > 0) && (*p != ' '))
|
|
p = delete_char(p_buf, p, &col, &n, plen);
|
|
continue;
|
|
|
|
case 0x08: /* ^H - backspace */
|
|
case 0x7F: /* DEL - backspace */
|
|
p = delete_char(p_buf, p, &col, &n, plen);
|
|
continue;
|
|
|
|
default:
|
|
/*
|
|
* Must be a normal character then
|
|
*/
|
|
if (n < CONFIG_SYS_CBSIZE-2) {
|
|
if (c == '\t') { /* expand TABs */
|
|
#ifdef CONFIG_AUTO_COMPLETE
|
|
/*
|
|
* if auto completion triggered just
|
|
* continue
|
|
*/
|
|
*p = '\0';
|
|
if (cmd_auto_complete(prompt,
|
|
console_buffer,
|
|
&n, &col)) {
|
|
p = p_buf + n; /* reset */
|
|
continue;
|
|
}
|
|
#endif
|
|
puts(tab_seq + (col & 07));
|
|
col += 8 - (col & 07);
|
|
} else {
|
|
char __maybe_unused buf[2];
|
|
|
|
/*
|
|
* Echo input using puts() to force an
|
|
* LCD flush if we are using an LCD
|
|
*/
|
|
++col;
|
|
buf[0] = c;
|
|
buf[1] = '\0';
|
|
puts(buf);
|
|
}
|
|
*p++ = c;
|
|
++n;
|
|
} else { /* Buffer full */
|
|
putc('\a');
|
|
}
|
|
}
|
|
}
|
|
#ifdef CONFIG_CMDLINE_EDITING
|
|
}
|
|
#endif
|
|
}
|