|
|
@ -6,16 +6,10 @@ |
|
|
|
#endif |
|
|
|
#include <ctype.h> |
|
|
|
#include "cmd.h" |
|
|
|
#include "tick.h" |
|
|
|
#include "config.h" |
|
|
|
|
|
|
|
struct cmd_handler { |
|
|
|
char buffer[TARGET_CMD_BUFSIZE + 2]; |
|
|
|
int recved; |
|
|
|
void *priv; |
|
|
|
void (*putstring)(struct cmd_handler *, const char *s, int len); |
|
|
|
int (*getchar)(struct cmd_handler *); |
|
|
|
}; |
|
|
|
|
|
|
|
#define PROMPT_MAX_LEN (8) |
|
|
|
enum KEY_ACTION { |
|
|
|
KEY_NULL = 0, /* NULL */ |
|
|
|
CTRL_A = 1, /* Ctrl+a */ |
|
|
@ -38,67 +32,117 @@ enum KEY_ACTION { |
|
|
|
BACKSPACE = 127 /* Backspace */ |
|
|
|
}; |
|
|
|
|
|
|
|
struct terminal { |
|
|
|
char *buf; /* edited line buffer */ |
|
|
|
int buflen; /* edited line buffer size */ |
|
|
|
int len; /* current edited line length */ |
|
|
|
int mlmode; /* mulitline mode flag */ |
|
|
|
char *prompt; /* prompt to display */ |
|
|
|
uint16_t plen; /* prompt length */ |
|
|
|
uint16_t pos; /* current cursor poistion */ |
|
|
|
uint16_t oldpos; /* previouse refresh cursor position */ |
|
|
|
uint16_t cols; /* number of columns in terminal */ |
|
|
|
uint16_t maxrows; /* maximum num of rows used so for (mulitline mode) */ |
|
|
|
int16_t hindex; /* the history index we are currently editing */ |
|
|
|
}; |
|
|
|
|
|
|
|
struct linebuf { |
|
|
|
char *buf; |
|
|
|
int buflen; |
|
|
|
int len; |
|
|
|
}; |
|
|
|
|
|
|
|
static char __editline_buf[TARGET_CMD_BUFSIZE + (PROMPT_MAX_LEN + 1)] __attribute__((section(".rxbuf.editline,\"aw\",%nobits@"))); |
|
|
|
static struct terminal G; |
|
|
|
static char *history[TARGET_CMD_HISTORY]; |
|
|
|
static int history_len = 0; |
|
|
|
|
|
|
|
#if defined(__CC_ARM) |
|
|
|
extern struct cmd_tbl_s Image$$CMDLINE$$Base, Image$$CMDLINE$$Limit; |
|
|
|
#else |
|
|
|
extern struct cmd_tbl_s __cmdline_cmd_start, __cmdline_cmd_end; |
|
|
|
#endif |
|
|
|
|
|
|
|
static struct cmd_handler _G; |
|
|
|
|
|
|
|
#define G_BUF (_G.buffer) |
|
|
|
#define G_N (_G.recved) |
|
|
|
#define G_GETCH() _G.getchar(&_G) |
|
|
|
|
|
|
|
#ifdef __CC_ARM /* for keil */ |
|
|
|
static void do_puts(struct cmd_handler *hc, const char *s, int len) |
|
|
|
static int tputs(const char *s, int len) |
|
|
|
{ |
|
|
|
if (len < 0) { |
|
|
|
len = strlen(s); |
|
|
|
} |
|
|
|
fwrite(s, len, 1, stdout); |
|
|
|
fflush(stdout); |
|
|
|
} |
|
|
|
static int do_getch(struct cmd_handler *hc) |
|
|
|
{ |
|
|
|
return fgetc(stdin); |
|
|
|
return (len); |
|
|
|
} |
|
|
|
#else |
|
|
|
static void do_puts(struct cmd_handler *hc, const char *s, int len) |
|
|
|
|
|
|
|
static int tgets(char *buf, int len, int blocking /* in millis */) |
|
|
|
{ |
|
|
|
char *buf_save = buf; |
|
|
|
int n; |
|
|
|
|
|
|
|
if (len < 0) { |
|
|
|
len = strlen(s); |
|
|
|
if (blocking <= 0) { |
|
|
|
return fread(buf, 1, len, stdin); |
|
|
|
} |
|
|
|
|
|
|
|
while (len > 0) { |
|
|
|
n = write(STDOUT_FILENO, s, len); |
|
|
|
n = fread(buf, 1, len, stdin); |
|
|
|
if (n > 0) { |
|
|
|
buf += n; |
|
|
|
len -= n; |
|
|
|
s += n; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return (buf - buf_save); |
|
|
|
} |
|
|
|
#else |
|
|
|
static int tputs(const char *s, int len) |
|
|
|
{ |
|
|
|
write(STDOUT_FILENO, s, len); |
|
|
|
return (len); |
|
|
|
} |
|
|
|
|
|
|
|
static int do_getch(struct cmd_handler *hc) |
|
|
|
static int tgets(char *buf, int len, int blocking /* in millis */) |
|
|
|
{ |
|
|
|
char c = 0; |
|
|
|
char *buf_save = buf; |
|
|
|
int n; |
|
|
|
uint64_t end; |
|
|
|
|
|
|
|
if (read(STDIN_FILENO, &c, 1) == 1) { |
|
|
|
return (c); |
|
|
|
if (blocking <= 0) { |
|
|
|
return read(STDIN_FILENO, buf, len); |
|
|
|
} |
|
|
|
return -1; |
|
|
|
|
|
|
|
end = millis() + blocking; |
|
|
|
while ((len > 0) && (end > millis())) { |
|
|
|
n = read(STDIN_FILENO, buf, len); |
|
|
|
if (n > 0) { |
|
|
|
buf += n; |
|
|
|
len -= n; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return (buf - buf_save); |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
static void do_writen(const char *s, int len) |
|
|
|
static void linebuf_init(struct linebuf *ln, char *buf, int len) |
|
|
|
{ |
|
|
|
_G.putstring(&_G, s, len); |
|
|
|
ln->buf = buf; |
|
|
|
ln->buflen = len; |
|
|
|
ln->len = 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void do_printf(const char *s) |
|
|
|
static int linebuf_len(struct linebuf *ln) |
|
|
|
{ |
|
|
|
do_writen(s, -1); |
|
|
|
return ln->len; |
|
|
|
} |
|
|
|
|
|
|
|
static int linebuf_append(struct linebuf *ln, const char *s, int len) |
|
|
|
{ |
|
|
|
if (len + ln->len > ln->buflen) { |
|
|
|
len = ln->buflen - ln->len; |
|
|
|
} |
|
|
|
|
|
|
|
if (len > 0) { |
|
|
|
memcpy(ln->buf + ln->len, s, len); |
|
|
|
ln->len += len; |
|
|
|
} |
|
|
|
|
|
|
|
return (len); |
|
|
|
} |
|
|
|
|
|
|
|
static int do_help(cmd_tbl_t s, int argc, char *argv[]) |
|
|
@ -108,34 +152,20 @@ static int do_help(cmd_tbl_t s, int argc, char *argv[]) |
|
|
|
#else |
|
|
|
cmd_tbl_t it = &__cmdline_cmd_start; |
|
|
|
#endif |
|
|
|
do_printf("support commands:\r\n\r\n"); |
|
|
|
printf("support commands:\r\n"); |
|
|
|
#if defined(__CC_ARM) |
|
|
|
while (it < &Image$$CMDLINE$$Limit) { |
|
|
|
#else |
|
|
|
while (it < &__cmdline_cmd_end) { |
|
|
|
while (it < &__cmdline_cmd_end) { |
|
|
|
#endif |
|
|
|
do_printf(it->name); |
|
|
|
do_printf("\t"); |
|
|
|
do_printf(it->help); |
|
|
|
do_printf("\r\n"); |
|
|
|
++it; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
printf("%s\t%s\r\n", it->name, it->help); |
|
|
|
++it; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
CON_CMD(help, "commands help", NULL, do_help) |
|
|
|
|
|
|
|
int cmd_init() |
|
|
|
{ |
|
|
|
G_N = 0 ; |
|
|
|
_G.putstring = do_puts; |
|
|
|
_G.getchar = do_getch; |
|
|
|
setvbuf(stdout, NULL, _IONBF, 0); |
|
|
|
setvbuf(stderr, NULL, _IONBF, 0); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int parse_line(char *cmd, int len, char *argv[], int _maxargs) |
|
|
|
{ |
|
|
|
int argc; |
|
|
@ -183,16 +213,16 @@ static int parse_line(char *cmd, int len, char *argv[], int _maxargs) |
|
|
|
|
|
|
|
int cmd_process(char *line) |
|
|
|
{ |
|
|
|
int i, argc, namelen; |
|
|
|
int size = strlen(line); |
|
|
|
char *argv[TARGET_CMD_MAXARGS + 1]; |
|
|
|
|
|
|
|
#if defined(__CC_ARM) |
|
|
|
cmd_tbl_t it = &Image$$CMDLINE$$Base; |
|
|
|
#else |
|
|
|
cmd_tbl_t it = &__cmdline_cmd_start; |
|
|
|
#endif |
|
|
|
|
|
|
|
int i, argc, namelen; |
|
|
|
int size = strlen(line); |
|
|
|
char *argv[TARGET_CMD_MAXARGS + 1]; |
|
|
|
|
|
|
|
/* split line */ |
|
|
|
for (i = 0; i < TARGET_CMD_MAXARGS + 1; ++i) { |
|
|
|
argv[i] = NULL; |
|
|
@ -226,59 +256,696 @@ recovery: |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
void cmd_loop() |
|
|
|
/*
|
|
|
|
* Use the ESC [6n escape sequence to query the horizontal cursor position |
|
|
|
* and return it. On error -1 is returned, on success the position of the |
|
|
|
* cursor. |
|
|
|
*/ |
|
|
|
static int get_cursor_position() |
|
|
|
{ |
|
|
|
int c; |
|
|
|
char buf[32]; |
|
|
|
int cols, rows; |
|
|
|
unsigned int i = 0; |
|
|
|
|
|
|
|
/* Report cursor location */ |
|
|
|
if (tputs("\x1b[6n", 4) != 4) return -1; |
|
|
|
|
|
|
|
/* Read the response: ESC [ rows ; cols R */ |
|
|
|
while (i < sizeof(buf)-1) { |
|
|
|
if (tgets(buf + i, 1, 1000) != 1) break; |
|
|
|
if (buf[i] == 'R') break; |
|
|
|
i++; |
|
|
|
} |
|
|
|
buf[i] = '\0'; |
|
|
|
|
|
|
|
do { |
|
|
|
c = G_GETCH(); |
|
|
|
if (c <= 0) |
|
|
|
break; |
|
|
|
if (G_N >= TARGET_CMD_BUFSIZE) { |
|
|
|
do_printf("too long\r\n"); |
|
|
|
G_N = 0; |
|
|
|
do_writen("\r\n> ", 4); |
|
|
|
} |
|
|
|
/* Parse it. */ |
|
|
|
if (buf[0] != ESC || buf[1] != '[') return -1; |
|
|
|
if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; |
|
|
|
return cols; |
|
|
|
} |
|
|
|
|
|
|
|
switch (c) { |
|
|
|
case CTRL_C: |
|
|
|
G_N = 0; |
|
|
|
do_writen("\r\n> ", 4); |
|
|
|
return; |
|
|
|
case BACKSPACE: |
|
|
|
case CTRL_H: |
|
|
|
if (G_N > 0) { |
|
|
|
do_writen("\b \b", 3); |
|
|
|
G_N--; |
|
|
|
G_BUF[G_N] = '\0'; |
|
|
|
} |
|
|
|
return; |
|
|
|
case CTRL_L: |
|
|
|
do_writen("\x1b[H\x1b[2J",7); |
|
|
|
do_writen("> ", 2); |
|
|
|
if (G_N > 0) |
|
|
|
do_writen(G_BUF, G_N); |
|
|
|
return; |
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to get the number of columns in the current terminal, or assume 80 |
|
|
|
* if it fails. |
|
|
|
*/ |
|
|
|
static int get_columns() |
|
|
|
{ |
|
|
|
char seq[32]; |
|
|
|
int start, cols; |
|
|
|
|
|
|
|
/* Get the initial position so we can restore it later. */ |
|
|
|
start = get_cursor_position(); |
|
|
|
if (start == -1) goto failed; |
|
|
|
|
|
|
|
/* Go to right margin and get position. */ |
|
|
|
if (tputs("\x1b[999C", 6) != 6) goto failed; |
|
|
|
cols = get_cursor_position(); |
|
|
|
if (cols == -1) goto failed; |
|
|
|
|
|
|
|
/* Restore position. */ |
|
|
|
if (cols > start) { |
|
|
|
snprintf(seq,32,"\x1b[%dD",cols-start); |
|
|
|
if (tputs(seq, strlen(seq)) == -1) { |
|
|
|
/* Can't recover... */ |
|
|
|
} |
|
|
|
} |
|
|
|
return cols; |
|
|
|
failed: |
|
|
|
return 80; |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Multi line low level line /efresh. |
|
|
|
* |
|
|
|
* Rewrite the currently edited line accordingly to the buffer content, |
|
|
|
* cursor position, and number of columns of the terminal. |
|
|
|
*/ |
|
|
|
static void refresh_multi_line(struct terminal *l) |
|
|
|
{ |
|
|
|
char seq[64]; |
|
|
|
int rows = (l->plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */ |
|
|
|
int rpos = (l->plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */ |
|
|
|
int rpos2; /* rpos after refresh. */ |
|
|
|
int j, col; /* colum position, zero-based. */ |
|
|
|
int old_rows = l->maxrows; |
|
|
|
char line[512]; |
|
|
|
struct linebuf lb; |
|
|
|
|
|
|
|
/* Update maxrows if needed. */ |
|
|
|
if (rows > (int)l->maxrows) |
|
|
|
l->maxrows = rows; |
|
|
|
|
|
|
|
linebuf_init(&lb, line, sizeof line); |
|
|
|
/*
|
|
|
|
* First step: clear all the lines used before. To do so start by |
|
|
|
* going to the last row. |
|
|
|
*/ |
|
|
|
if (old_rows-rpos > 0) { |
|
|
|
snprintf(seq,64,"\x1b[%dB", old_rows-rpos); |
|
|
|
linebuf_append(&lb, seq, strlen(seq)); |
|
|
|
} |
|
|
|
|
|
|
|
/* Now for every row clear it, go up. */ |
|
|
|
for (j = 0; j < old_rows - 1; j++) { |
|
|
|
snprintf(seq,64,"\r\x1b[0K\x1b[1A"); |
|
|
|
linebuf_append(&lb, seq, strlen(seq)); |
|
|
|
} |
|
|
|
|
|
|
|
/* Clean the top line. */ |
|
|
|
snprintf(seq,64,"\r\x1b[0K"); |
|
|
|
linebuf_append(&lb, seq, strlen(seq)); |
|
|
|
|
|
|
|
/* Write the prompt and the current buffer content */ |
|
|
|
linebuf_append(&lb, l->prompt, l->plen); |
|
|
|
linebuf_append(&lb, l->buf, l->len); |
|
|
|
|
|
|
|
/* If we are at the very end of the screen with our prompt, we need to
|
|
|
|
* emit a newline and move the prompt to the first column. */ |
|
|
|
if (l->pos && l->pos == l->len && (l->pos+ l->plen) % l->cols == 0) { |
|
|
|
linebuf_append(&lb, "\r\n", 2); |
|
|
|
rows++; |
|
|
|
if (rows > (int)l->maxrows) |
|
|
|
l->maxrows = rows; |
|
|
|
} |
|
|
|
|
|
|
|
/* Move cursor to right position. */ |
|
|
|
rpos2 = (l->plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */ |
|
|
|
|
|
|
|
/* Go up till we reach the expected positon. */ |
|
|
|
if (rows-rpos2 > 0) { |
|
|
|
snprintf(seq,64,"\x1b[%dA", rows-rpos2); |
|
|
|
linebuf_append(&lb, seq, strlen(seq)); |
|
|
|
} |
|
|
|
|
|
|
|
/* Set column. */ |
|
|
|
col = (l->plen + (int)l->pos) % (int)l->cols; |
|
|
|
if (col) |
|
|
|
snprintf(seq,64,"\r\x1b[%dC", col); |
|
|
|
else |
|
|
|
snprintf(seq,64,"\r"); |
|
|
|
linebuf_append(&lb, seq, strlen(seq)); |
|
|
|
|
|
|
|
l->oldpos = l->pos; |
|
|
|
|
|
|
|
tputs(line, linebuf_len(&lb)); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Single line low level line refresh. |
|
|
|
* |
|
|
|
* Rewrite the currently edited line accordingly to the buffer content, |
|
|
|
* cursor position, and number of columns of the terminal. |
|
|
|
*/ |
|
|
|
static void refresh_single_line(struct terminal *l) |
|
|
|
{ |
|
|
|
struct linebuf lb; |
|
|
|
char line[512]; |
|
|
|
char seq[64] = {0}; |
|
|
|
char *buf = l->buf; |
|
|
|
size_t len = l->len; |
|
|
|
size_t pos = l->pos; |
|
|
|
|
|
|
|
while((l->plen + pos) >= l->cols) { |
|
|
|
buf++; |
|
|
|
len--; |
|
|
|
pos--; |
|
|
|
} |
|
|
|
|
|
|
|
while (l->plen+len > l->cols) { |
|
|
|
len--; |
|
|
|
} |
|
|
|
|
|
|
|
linebuf_init(&lb, line, sizeof line); |
|
|
|
|
|
|
|
/* Cursor to left edge */ |
|
|
|
linebuf_append(&lb, "\r", 1); |
|
|
|
linebuf_append(&lb, l->prompt, l->plen); |
|
|
|
linebuf_append(&lb, buf, len); |
|
|
|
|
|
|
|
/* end of line */ |
|
|
|
if (c == '\r' || c == '\n') { |
|
|
|
do_writen("\r\n", 2); |
|
|
|
if (G_N > 0) { |
|
|
|
G_BUF[G_N] = '\0'; |
|
|
|
cmd_process(G_BUF); |
|
|
|
do_writen("\r\n", 2); |
|
|
|
G_N = 0; |
|
|
|
/* Erase to right */ |
|
|
|
linebuf_append(&lb, "\x1b[0K", 4); |
|
|
|
|
|
|
|
/* Move cursor to original position. */ |
|
|
|
snprintf(seq,64,"\r\x1b[%dC", (int)(pos+l->plen)); |
|
|
|
linebuf_append(&lb, seq, strlen(seq)); |
|
|
|
tputs(line, linebuf_len(&lb)); |
|
|
|
} |
|
|
|
|
|
|
|
static void refresh_line(struct terminal *l) |
|
|
|
{ |
|
|
|
if (l->mlmode) { |
|
|
|
refresh_multi_line(l); |
|
|
|
} else { |
|
|
|
refresh_single_line(l); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete the character at the right of the cursor without altering the cursor |
|
|
|
* position. Basically this is what happens with the "Delete" keyboard key. |
|
|
|
*/ |
|
|
|
static void edit_delete(struct terminal *l) |
|
|
|
{ |
|
|
|
if (l->len > 0 && l->pos < l->len) { |
|
|
|
memmove(l->buf + l->pos, l->buf + l->pos + 1, l->len - l->pos - 1); |
|
|
|
l->len--; |
|
|
|
l->buf[l->len] = '\0'; |
|
|
|
refresh_line(l); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Backspace implementation. */ |
|
|
|
static void edit_backspace(struct terminal *l) |
|
|
|
{ |
|
|
|
if (l->pos > 0 && l->len > 0) { |
|
|
|
memmove(l->buf + l->pos - 1, l->buf + l->pos, l->len - l->pos); |
|
|
|
l->pos--; |
|
|
|
l->len--; |
|
|
|
l->buf[l->len] = '\0'; |
|
|
|
refresh_line(l); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete the previosu word, maintaining the cursor at the start of the |
|
|
|
* current word. |
|
|
|
*/ |
|
|
|
static void edit_delete_prev(struct terminal *l) |
|
|
|
{ |
|
|
|
size_t old_pos = l->pos; |
|
|
|
size_t diff; |
|
|
|
|
|
|
|
while (l->pos > 0 && l->buf[l->pos - 1] == ' ') |
|
|
|
l->pos--; |
|
|
|
while (l->pos > 0 && l->buf[l->pos - 1] != ' ') |
|
|
|
l->pos--; |
|
|
|
diff = old_pos - l->pos; |
|
|
|
memmove(l->buf + l->pos, l->buf + old_pos, l->len - old_pos + 1); |
|
|
|
l->len -= diff; |
|
|
|
refresh_line(l); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert the character 'c' at cursor current position. |
|
|
|
* |
|
|
|
* On error writing to the terminal -1 is returned, otherwise 0. |
|
|
|
*/ |
|
|
|
static void edit_insert(struct terminal *l, char c) |
|
|
|
{ |
|
|
|
if (l->len < l->buflen) { |
|
|
|
if (l->len == l->pos) { |
|
|
|
l->buf[l->pos] = c; |
|
|
|
l->pos++; |
|
|
|
l->len++; |
|
|
|
l->buf[l->len] = '\0'; |
|
|
|
if (!l->mlmode && (l->plen + l->len) < l->cols) { |
|
|
|
/* Avoid a full update of the line in the
|
|
|
|
* trivial case. */ |
|
|
|
tputs(&c, 1); |
|
|
|
} else { |
|
|
|
refresh_line(l); |
|
|
|
} |
|
|
|
do_writen("> ", 2); |
|
|
|
continue; |
|
|
|
} else { |
|
|
|
memmove(l->buf + l->pos + 1, l->buf + l->pos, l->len - l->pos); |
|
|
|
l->buf[l->pos] = c; |
|
|
|
l->len++; |
|
|
|
l->pos++; |
|
|
|
l->buf[l->len] = '\0'; |
|
|
|
refresh_line(l); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void move_right(struct terminal *l) |
|
|
|
{ |
|
|
|
if (l->pos != l->len) { |
|
|
|
++l->pos; |
|
|
|
refresh_line(l); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void move_left(struct terminal *l) |
|
|
|
{ |
|
|
|
if (l->pos > 0) { |
|
|
|
l->pos--; |
|
|
|
refresh_line(l); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void move_home(struct terminal *l) |
|
|
|
{ |
|
|
|
if (l->pos != 0) { |
|
|
|
l->pos = 0; |
|
|
|
refresh_line(l); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void move_end(struct terminal *l) |
|
|
|
{ |
|
|
|
if (l->pos != l->len) { |
|
|
|
l->pos = l->len; |
|
|
|
refresh_line(l); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static int history_add(const char *line) |
|
|
|
{ |
|
|
|
char *copy; |
|
|
|
|
|
|
|
if (history_len && !strcmp(history[history_len - 1], line)) |
|
|
|
return 0; |
|
|
|
copy = strdup(line); |
|
|
|
if (!copy) |
|
|
|
return 0; |
|
|
|
if (history_len == TARGET_CMD_HISTORY) { |
|
|
|
free(history[0]); |
|
|
|
memmove(history, history + 1, sizeof (char *) * (history_len - 1)); |
|
|
|
--history_len; |
|
|
|
} |
|
|
|
history[history_len++] = copy; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Substitute the currently edited line with the next or previous history |
|
|
|
* entry as specified by 'dir' |
|
|
|
*/ |
|
|
|
static void edit_history(struct terminal *l, int dir) |
|
|
|
{ |
|
|
|
if (history_len > 0) { |
|
|
|
l->hindex += dir; |
|
|
|
if (l->hindex < 0) { |
|
|
|
l->hindex = history_len - 1; |
|
|
|
} else if (l->hindex >= history_len) { |
|
|
|
l->hindex = 0; |
|
|
|
} |
|
|
|
strncpy(l->buf, history[l->hindex], l->buflen); |
|
|
|
l->buf[l->buflen - 1] = '\0'; |
|
|
|
l->len = l->pos = strlen(l->buf); |
|
|
|
refresh_line(l); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void history_prev(struct terminal *l) |
|
|
|
{ |
|
|
|
edit_history(l, -1); |
|
|
|
} |
|
|
|
|
|
|
|
static void history_next(struct terminal *l) |
|
|
|
{ |
|
|
|
edit_history(l, 1); |
|
|
|
} |
|
|
|
|
|
|
|
static void reset_linestate(struct terminal *l) |
|
|
|
{ |
|
|
|
memset(l->buf, 0, l->buflen); |
|
|
|
l->cols = get_columns(); |
|
|
|
l->oldpos = 0; |
|
|
|
l->pos = 0; |
|
|
|
l->len = 0; |
|
|
|
l->hindex = history_len; |
|
|
|
|
|
|
|
tputs(l->prompt, l->plen); |
|
|
|
} |
|
|
|
|
|
|
|
static int get_mached(const char *buf, int len) |
|
|
|
{ |
|
|
|
#if defined(__CC_ARM) |
|
|
|
cmd_tbl_t it = &Image$$CMDLINE$$Base; |
|
|
|
#else |
|
|
|
cmd_tbl_t it = &__cmdline_cmd_start; |
|
|
|
#endif |
|
|
|
int n, mached = 0; |
|
|
|
|
|
|
|
#if defined(__CC_ARM) |
|
|
|
while (it < &Image$$CMDLINE$$Limit) { |
|
|
|
#else |
|
|
|
while (it < &__cmdline_cmd_end) { |
|
|
|
#endif |
|
|
|
n = strlen(it->name); |
|
|
|
if (n > len) |
|
|
|
n = len; |
|
|
|
if (memcmp(it->name, buf, n) == 0) |
|
|
|
++mached; |
|
|
|
++it; |
|
|
|
} |
|
|
|
|
|
|
|
return (mached); |
|
|
|
} |
|
|
|
|
|
|
|
static int fill_mached(int mached, const char *vec[], const char *buf, int len) |
|
|
|
{ |
|
|
|
#if defined(__CC_ARM) |
|
|
|
cmd_tbl_t it = &Image$$CMDLINE$$Base; |
|
|
|
#else |
|
|
|
cmd_tbl_t it = &__cmdline_cmd_start; |
|
|
|
#endif |
|
|
|
int i = 0, n; |
|
|
|
|
|
|
|
#if defined(__CC_ARM) |
|
|
|
while (it < &Image$$CMDLINE$$Limit) { |
|
|
|
#else |
|
|
|
while (it < &__cmdline_cmd_end) { |
|
|
|
#endif |
|
|
|
n = strlen(it->name); |
|
|
|
if (n > len) |
|
|
|
n = len; |
|
|
|
if (memcmp(it->name, buf, n) == 0) { |
|
|
|
vec[i++] = it->name; |
|
|
|
if (i >= mached) |
|
|
|
break; |
|
|
|
} |
|
|
|
++it; |
|
|
|
} |
|
|
|
|
|
|
|
return (i); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* This is an helper function for cmd_loop() and is called when the |
|
|
|
* user types the <tab> key in order to complete the string currently in the |
|
|
|
* input. |
|
|
|
* |
|
|
|
* The state of the editing is encapsulated into the pointed linenoiseState |
|
|
|
* structure as described in the structure definition. |
|
|
|
*/ |
|
|
|
static int do_complete_line(struct terminal *ls, int mached) |
|
|
|
{ |
|
|
|
const char *cvec[mached]; |
|
|
|
int nread, nwritten; |
|
|
|
size_t stop = 0, i = 0; |
|
|
|
char c = 0; |
|
|
|
|
|
|
|
if (fill_mached(mached, cvec, ls->buf, ls->len) != mached) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
while(!stop) { |
|
|
|
/* Show completion or original buffer */ |
|
|
|
if (i < mached) { |
|
|
|
struct terminal saved = *ls; |
|
|
|
|
|
|
|
ls->len = ls->pos = strlen(cvec[i]); |
|
|
|
ls->buf = (char *)cvec[i]; |
|
|
|
refresh_line(ls); |
|
|
|
|
|
|
|
ls->len = saved.len; |
|
|
|
ls->pos = saved.pos; |
|
|
|
ls->buf = saved.buf; |
|
|
|
} else { |
|
|
|
refresh_line(ls); |
|
|
|
} |
|
|
|
|
|
|
|
nread = tgets(&c, 1, 1000); |
|
|
|
if (nread <= 0) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
switch(c) { |
|
|
|
case TAB: /* tab */ |
|
|
|
i = (i+1) % (mached + 1); |
|
|
|
if (i == mached) { |
|
|
|
tputs("\x7", 1); /* beep */ |
|
|
|
} |
|
|
|
break; |
|
|
|
case ESC: /* escape */ |
|
|
|
/* Re-show original buffer */ |
|
|
|
if (i < mached) |
|
|
|
refresh_line(ls); |
|
|
|
stop = 1; |
|
|
|
break; |
|
|
|
default: |
|
|
|
/* Update buffer and return */ |
|
|
|
if (i < mached) { |
|
|
|
nwritten = snprintf(ls->buf,ls->buflen,"%s",cvec[i]); |
|
|
|
ls->len = ls->pos = nwritten; |
|
|
|
} |
|
|
|
stop = 1; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return c; /* Return last read character */ |
|
|
|
} |
|
|
|
|
|
|
|
static int complete_line(struct terminal *l) |
|
|
|
{ |
|
|
|
int mached = get_mached(l->buf, l->len); |
|
|
|
|
|
|
|
if (isprint(c)) { |
|
|
|
G_BUF[G_N] = c; |
|
|
|
G_N++; |
|
|
|
do_writen((char *)&c, 1); |
|
|
|
if (mached <= 0) { |
|
|
|
tputs("\x7", 1); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
return do_complete_line(l, mached); |
|
|
|
} |
|
|
|
|
|
|
|
static int do_clear(cmd_tbl_t s, int argc, char *argv[]) |
|
|
|
{ |
|
|
|
tputs("\x1b[H\x1b[2J",7); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
CON_CMD(clear, "clear the screen", NULL, do_clear); |
|
|
|
|
|
|
|
static int do_mode(cmd_tbl_t s, int argc, char *argv[]) |
|
|
|
{ |
|
|
|
int mode; |
|
|
|
|
|
|
|
if (argc >= 2) { |
|
|
|
mode = atoi(argv[1]); |
|
|
|
G.mlmode = mode > 0 ? 1 : 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
CON_CMD(mlmode, "set multi-line cmdline mode (0 or 1)", NULL, do_mode); |
|
|
|
|
|
|
|
int cmd_init(const char *prompt) |
|
|
|
{ |
|
|
|
struct terminal *l = &G; |
|
|
|
|
|
|
|
#if defined(__CC_ARM) |
|
|
|
setvbuf(stdin, NULL, _IONBF, 0); |
|
|
|
setvbuf(stdout, NULL, _IONBF, 0); |
|
|
|
setvbuf(stderr, NULL, _IONBF, 0); |
|
|
|
#endif |
|
|
|
|
|
|
|
l->buf = __editline_buf; |
|
|
|
l->buflen = TARGET_CMD_BUFSIZE; |
|
|
|
l->prompt = l->buf + TARGET_CMD_BUFSIZE; |
|
|
|
l->maxrows = 0; |
|
|
|
l->mlmode = TARGET_CMD_MULTIMODE; |
|
|
|
|
|
|
|
memset(l->buf, 0, l->buflen); |
|
|
|
l->buflen--; /* make sure there is always space hold `NUL' */ |
|
|
|
|
|
|
|
if (prompt) { |
|
|
|
strncpy(l->prompt, prompt, PROMPT_MAX_LEN); |
|
|
|
} else { |
|
|
|
strcpy(l->prompt, "> "); |
|
|
|
} |
|
|
|
l->plen = strlen(l->prompt); |
|
|
|
|
|
|
|
reset_linestate(l); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
void cmd_refresh() |
|
|
|
{ |
|
|
|
reset_linestate(&G); |
|
|
|
} |
|
|
|
|
|
|
|
static void do_command(struct terminal *l) |
|
|
|
{ |
|
|
|
tputs("\r\n", 2); |
|
|
|
if (l->len > 0) { |
|
|
|
history_add(l->buf); |
|
|
|
cmd_process(l->buf); |
|
|
|
} |
|
|
|
reset_linestate(l); |
|
|
|
} |
|
|
|
|
|
|
|
void cmd_loop() |
|
|
|
{ |
|
|
|
struct terminal *l = &G; |
|
|
|
int nread; |
|
|
|
char c, seq[3]; |
|
|
|
|
|
|
|
nread = tgets(&c, 1, -1); |
|
|
|
if (nread <= 0) |
|
|
|
return; |
|
|
|
|
|
|
|
/* if c == TAB, do completion? */ |
|
|
|
if (c == TAB) { |
|
|
|
c = complete_line(l); |
|
|
|
if (c <= 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
} while (1) ; |
|
|
|
} |
|
|
|
|
|
|
|
switch (c) { |
|
|
|
case ENTER: |
|
|
|
case '\n': |
|
|
|
do_command(l); |
|
|
|
break; |
|
|
|
case CTRL_C: /* ctrl-c */ |
|
|
|
reset_linestate(l); |
|
|
|
break; |
|
|
|
case BACKSPACE: /* backspace */ |
|
|
|
case CTRL_H: |
|
|
|
edit_backspace(l); |
|
|
|
break; |
|
|
|
case CTRL_D: /* ctrl-d, remove the right of cursor. */ |
|
|
|
if (l->len > 0) { |
|
|
|
edit_delete(l); |
|
|
|
} else { |
|
|
|
reset_linestate(l); |
|
|
|
} |
|
|
|
break; |
|
|
|
case CTRL_T: /* ctrl-t, swaps current char with previous */ |
|
|
|
if (l->pos > 0 && l->pos < l->len) { |
|
|
|
int aux = l->buf[l->pos - 1]; |
|
|
|
l->buf[l->pos-1] = l->buf[l->pos]; |
|
|
|
l->buf[l->pos] = aux; |
|
|
|
if (l->pos != l->len - 1) l->pos++; |
|
|
|
refresh_line(l); |
|
|
|
} |
|
|
|
break; |
|
|
|
case CTRL_B: /* ctrl -b */ |
|
|
|
move_left(l); |
|
|
|
break; |
|
|
|
case CTRL_F: /* ctrl-f */ |
|
|
|
move_right(l); |
|
|
|
break; |
|
|
|
case CTRL_L: |
|
|
|
tputs("\x1b[H\x1b[2J",7); |
|
|
|
refresh_line(l); |
|
|
|
break; |
|
|
|
case CTRL_U: /* ctrl-u, delete the whole line */ |
|
|
|
l->buf[0] = '\0'; |
|
|
|
l->pos = l->len = 0; |
|
|
|
refresh_line(l); |
|
|
|
break; |
|
|
|
case CTRL_K: /* ctrl-k, delete from current to end of line */ |
|
|
|
l->buf[l->pos] = '\0'; |
|
|
|
l->len = l->pos; |
|
|
|
refresh_line(l); |
|
|
|
break; |
|
|
|
case CTRL_A: /* ctrl-a, goto start of the line */ |
|
|
|
move_home(l); |
|
|
|
break; |
|
|
|
case CTRL_E: /* ctrl-e, goto end of the line */ |
|
|
|
move_end(l); |
|
|
|
break; |
|
|
|
case CTRL_W: |
|
|
|
edit_delete_prev(l); |
|
|
|
break; |
|
|
|
case CTRL_P: |
|
|
|
history_prev(l); |
|
|
|
break; |
|
|
|
case CTRL_N: |
|
|
|
history_next(l); |
|
|
|
break; |
|
|
|
case ESC: /* escape sequence */ |
|
|
|
/* Read the next two bytes representing the escape sequence.
|
|
|
|
* Use two calls to handle slow terminals returning the two |
|
|
|
* chars at different times. */ |
|
|
|
if (tgets(seq, 1, 1000) <= 0) break; |
|
|
|
if (tgets(seq + 1 ,1, 1000) <= 0) break; |
|
|
|
|
|
|
|
/* ESC [ sequences. */ |
|
|
|
if (seq[0] == '[') { |
|
|
|
if (seq[1] >= '0' && seq[1] <= '9') { |
|
|
|
/* Extended escape, read additional byte. */ |
|
|
|
if (tgets(seq + 2, 1, 1000) <= 0) break; |
|
|
|
if (seq[2] == '~') { |
|
|
|
switch(seq[1]) { |
|
|
|
case '3': /* Delete key. */ |
|
|
|
edit_delete(l); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
switch(seq[1]) { |
|
|
|
case 'A': /* Up */ |
|
|
|
history_prev(l); |
|
|
|
break; |
|
|
|
case 'B': /* Down */ |
|
|
|
history_next(l); |
|
|
|
break; |
|
|
|
case 'C': /* Right */ |
|
|
|
move_right(l); |
|
|
|
break; |
|
|
|
case 'D': /* Left */ |
|
|
|
move_left(l); |
|
|
|
break; |
|
|
|
case 'H': /* Home */ |
|
|
|
move_home(l); |
|
|
|
break; |
|
|
|
case 'F': /* End*/ |
|
|
|
move_end(l); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* ESC O sequences. */ |
|
|
|
else if (seq[0] == 'O') { |
|
|
|
switch(seq[1]) { |
|
|
|
case 'H': /* Home */ |
|
|
|
move_home(l); |
|
|
|
break; |
|
|
|
case 'F': /* End*/ |
|
|
|
move_end(l); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
break; |
|
|
|
default: |
|
|
|
edit_insert(l, c); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|