You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1879 lines
46 KiB
1879 lines
46 KiB
/*
|
|
* vim: set et sw=4 ts=4 fdm=marker:
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include "cmd.h"
|
|
|
|
#undef putc
|
|
#undef getc
|
|
|
|
#define TARGET_BUFFER_SIZE (128)
|
|
#define TARGET_VARIABLES_MAX (64)
|
|
#define TARGET_CMD_HISTORY (32)
|
|
#define TARGET_CMD_MAXARGS (12)
|
|
|
|
#define TARGET_COMMANDS_MAX (256)
|
|
|
|
#define PROMPT_MAX_LEN (16)
|
|
|
|
#if defined(__CC_ARM)
|
|
extern CMD_TBL_S Image$$CMDLINE$$Base, Image$$CMDLINE$$Limit;
|
|
#else
|
|
extern CMD_TBL_S __console_cmd_start;
|
|
extern CMD_TBL_S __console_cmd_end;
|
|
#endif
|
|
|
|
/*
|
|
* VT100控制码
|
|
* \033
|
|
*/
|
|
enum KEY_ACTION {
|
|
KEY_NULL = 0, /* NULL */
|
|
CTRL_A = 1, /* Ctrl+a */
|
|
CTRL_B = 2, /* Ctrl-b */
|
|
CTRL_C = 3, /* Ctrl-c */
|
|
CTRL_D = 4, /* Ctrl-d */
|
|
CTRL_E = 5, /* Ctrl-e */
|
|
CTRL_F = 6, /* Ctrl-f */
|
|
CTRL_H = 8, /* Ctrl-h */
|
|
TAB = 9, /* Tab */
|
|
CTRL_K = 11, /* Ctrl+k */
|
|
CTRL_L = 12, /* Ctrl+l */
|
|
ENTER = 13, /* Enter */
|
|
CTRL_N = 14, /* Ctrl-n */
|
|
CTRL_P = 16, /* Ctrl-p */
|
|
CTRL_T = 20, /* Ctrl-t */
|
|
CTRL_U = 21, /* Ctrl+u */
|
|
CTRL_W = 23, /* Ctrl+w */
|
|
CTRL_Z = 26, /* Ctrl+z */
|
|
ESC = 27, /* Escape */
|
|
BACKSPACE = 127 /* Backspace */
|
|
};
|
|
|
|
#define STEP_MARK_DEL (1 << 1)
|
|
|
|
struct hash_table;
|
|
|
|
struct Listener {
|
|
struct Listener *next;
|
|
int flags;
|
|
void (*listen)(zcHandle, void *);
|
|
void *arg;
|
|
};
|
|
|
|
struct ConsoleHandle {
|
|
char buf[TARGET_BUFFER_SIZE]; /* edited line buffer */
|
|
int buflen;
|
|
int len; /* current edited line length */
|
|
char prompt[PROMPT_MAX_LEN]; /* prompt to display */
|
|
int plen; /* prompt length */
|
|
int pos; /* current cursor poistion */
|
|
int oldpos; /* previouse refresh cursor position */
|
|
int cols; /* number of columns in terminal */
|
|
int maxrows; /* maximum num of rows used so for (mulitline mode) */
|
|
int hindex; /* the history index we are currently editing */
|
|
|
|
/* line mode */
|
|
int is_raw;
|
|
int olast_ch;
|
|
int do_echo;
|
|
|
|
char *history[TARGET_CMD_HISTORY];
|
|
int history_len;
|
|
|
|
/*io interface func*/
|
|
void *param;
|
|
void (*putc)(void *param, int ch);
|
|
int (*getc)(void *param, char *ch);
|
|
int (*line)(zcHandle, void *param, char *sline); // line handler (surenyi926 2020/09/17)
|
|
void (*delayUs)(void *param, unsigned int us);
|
|
|
|
int debug_console;
|
|
int exit;
|
|
char last_ch;
|
|
int issue; /*0-no prompt, 1- have prompt*/
|
|
|
|
void (*free)(void *param);
|
|
|
|
/* listener hookup */
|
|
void (*hookup)(zcHandle, void *param);
|
|
|
|
/* Listener */
|
|
struct Listener *steps;
|
|
|
|
/* variables */
|
|
struct hash_table *variables;
|
|
|
|
/* commands */
|
|
struct hash_table *commands;
|
|
};
|
|
|
|
/* {{{ hash table : store commands and variables */
|
|
struct hash_elem {
|
|
struct hash_elem *next; /* Next element in case of a collision */
|
|
void *data; /* Pointer to the stored element */
|
|
const char *key; /* Key of the stored element */
|
|
};
|
|
|
|
struct hash_elem_op {
|
|
struct hash_elem * (*new )(const char *key, void *data);
|
|
int (*update)(struct hash_elem *, void *new_data);
|
|
void (*free )(struct hash_elem *);
|
|
};
|
|
|
|
struct hash_table {
|
|
struct hash_elem_op *eop;
|
|
size_t capacity; /* Hashtable capacity (in terms of hashed keys) */
|
|
struct hash_elem **table; /* The table containaing elements */
|
|
};
|
|
|
|
/*
|
|
* Hash function with djb2 by Dan Bernstein.
|
|
* http://www.cse.yorku.ca/~oz/hash.html
|
|
*/
|
|
static unsigned long hash(const char *str)
|
|
{
|
|
unsigned long hash = 5381;
|
|
int c;
|
|
|
|
while ((c = *str++) != 0)
|
|
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
|
|
|
return hash;
|
|
}
|
|
|
|
/*
|
|
* Create a hashtable with capacity 'capacity'
|
|
* and return a pointer to it
|
|
*/
|
|
struct hash_table *ht_create(size_t capacity, struct hash_elem_op *eop)
|
|
{
|
|
struct hash_table *hasht = calloc(1, sizeof *hasht);
|
|
|
|
if (!hasht)
|
|
return NULL;
|
|
|
|
hasht->table = calloc(capacity, sizeof (struct hash_elem));
|
|
if (!hasht->table) {
|
|
free(hasht);
|
|
return NULL;
|
|
}
|
|
|
|
hasht->eop = eop;
|
|
hasht->capacity = capacity;
|
|
return hasht;
|
|
}
|
|
|
|
/*
|
|
* store data to the hashtable.
|
|
*
|
|
* return: 1 updated, 0 allocated, -1 error.
|
|
*/
|
|
int ht_set(struct hash_table *ht, const char* key, void* data)
|
|
{
|
|
unsigned long h = hash(key) % ht->capacity;
|
|
struct hash_elem *e = ht->table[h];
|
|
|
|
while (e != NULL) {
|
|
if (strcmp(e->key, key) == 0) { /* matched */
|
|
ht->eop->update(e, data);
|
|
return 1;
|
|
}
|
|
e = e->next;
|
|
}
|
|
|
|
/* the key doesn't already exist */
|
|
e = ht->eop->new(key, data);
|
|
if (e == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
/* add the element at the beginning of the linked list */
|
|
e->next = ht->table[h];
|
|
ht->table[h] = e;
|
|
return 0;
|
|
}
|
|
|
|
/* retrieve data from the hashtable */
|
|
void *ht_get(struct hash_table *ht, const char* key)
|
|
{
|
|
unsigned long h = hash(key) % ht->capacity;
|
|
struct hash_elem* e =ht->table[h];
|
|
|
|
while(e != NULL) {
|
|
if (strcmp(e->key, key) == 0)
|
|
return e->data;
|
|
e = e->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* remove data from the hashtable.
|
|
*/
|
|
int ht_remove(struct hash_table *ht, const char* key)
|
|
{
|
|
unsigned long h = hash(key) % ht->capacity;
|
|
struct hash_elem *it, **e = &ht->table[h];
|
|
|
|
while((*e) != NULL) {
|
|
it = *e;
|
|
if (strcmp(it->key, key) == 0) {
|
|
*e = it->next;
|
|
ht->eop->free(it);
|
|
return 0;
|
|
}
|
|
e = &(*e)->next;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* dont call ht_xxx in func
|
|
*
|
|
* func() return > 0 , stop iteration.
|
|
*/
|
|
int ht_for_each(struct hash_table *ht, void *pdata, int (*func)(void *pdata, const char *key, void *data))
|
|
{
|
|
size_t i;
|
|
struct hash_elem *e;
|
|
|
|
for (i = 0; i < ht->capacity; ++i) {
|
|
e = ht->table[i];
|
|
while (e) {
|
|
if (func(pdata, e->key, e->data) > 0) /* skip others */
|
|
return 1;
|
|
e = e->next;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* destroy the hash table, and free memory.
|
|
*/
|
|
void ht_destroy(struct hash_table *ht)
|
|
{
|
|
size_t i;
|
|
struct hash_elem *e, *it;
|
|
|
|
/* free all elements */
|
|
for (i = 0; i < ht->capacity; ++i) {
|
|
it = ht->table[i];
|
|
while (it) {
|
|
e = it->next;
|
|
ht->eop->free(it);
|
|
it = e;
|
|
}
|
|
}
|
|
free(ht->table);
|
|
free(ht);
|
|
}
|
|
/* }}} */
|
|
|
|
struct linebuf {
|
|
char *buf;
|
|
int buflen;
|
|
int len;
|
|
};
|
|
|
|
void *vstrdup(const char *str)
|
|
{
|
|
void *m;
|
|
int len = strlen(str);
|
|
|
|
m = calloc(1, len + 1);
|
|
memcpy(m, str, len);
|
|
|
|
return m;
|
|
}
|
|
|
|
static void tputc_line_mode(zcHandle cs, int ch)
|
|
{
|
|
int och = ch;
|
|
|
|
if (ch == '\n') {
|
|
if (cs->olast_ch != '\r') {
|
|
cs->putc(cs->param, '\r');
|
|
} else {
|
|
cs->olast_ch = -1;
|
|
return;
|
|
}
|
|
}
|
|
if (ch == '\r') {
|
|
if (cs->olast_ch != '\n') {
|
|
cs->putc(cs->param, '\n');
|
|
} else {
|
|
cs->olast_ch = -1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
cs->putc(cs->param, ch);
|
|
cs->olast_ch = och;
|
|
}
|
|
|
|
static int tputs(zcHandle cs, const char *buf, int len)
|
|
{
|
|
int n = len;
|
|
|
|
if (cs->putc) {
|
|
if(n == -1) {
|
|
n = strlen(buf);
|
|
len = n;
|
|
}
|
|
/* append '\r', '\n', or '\r\n' */
|
|
while (n > 0) {
|
|
if (cs->is_raw) {
|
|
cs->putc(cs->param, *buf);
|
|
} else {
|
|
tputc_line_mode(cs, *buf);
|
|
}
|
|
++buf;
|
|
--n;
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
static int tgets(zcHandle cs, char *buf, int len, int blocking)
|
|
{
|
|
int readLen = 0;
|
|
|
|
if (!cs->getc)
|
|
goto skip;
|
|
|
|
while(len > 0) {
|
|
if (cs->getc(cs->param, &buf[readLen]) > 0) {
|
|
readLen++;
|
|
len--;
|
|
} else {
|
|
if (blocking == -1) { //block read
|
|
continue;
|
|
} else if (blocking == 0){ //non block read
|
|
break;
|
|
} else {
|
|
if (cs->delayUs)
|
|
cs->delayUs(cs->param, 1);
|
|
blocking--;
|
|
}
|
|
}
|
|
}
|
|
|
|
skip:
|
|
return readLen;
|
|
}
|
|
|
|
static void tprintf(zcHandle cs, const char *fmt, ...)
|
|
{
|
|
char _buf[128 + 4] = {0};
|
|
int len;
|
|
va_list args;
|
|
|
|
va_start (args, fmt);
|
|
len = vsnprintf(_buf, sizeof _buf - 1, fmt, args);
|
|
va_end (args);
|
|
|
|
tputs(cs, _buf, len);
|
|
}
|
|
|
|
static void linebuf_init(struct linebuf *ln, char *buf, int len)
|
|
{
|
|
ln->buf = buf;
|
|
ln->buflen = len;
|
|
ln->len = 0;
|
|
}
|
|
|
|
static int linebuf_len(struct linebuf *ln)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
#if 0
|
|
static int get_cursor_position(zcHandle cs)
|
|
{
|
|
char buf[32];
|
|
int cols, rows;
|
|
unsigned int i = 0;
|
|
|
|
/* Report cursor location */
|
|
if (tputs(cs, "\x1b[6n", 4) != 4)
|
|
return -1;
|
|
|
|
/* Read the response: ESC [ rows ; cols R */
|
|
while (i < sizeof(buf)-1) {
|
|
if (tgets(cs, buf + i, 1, 1000) != 1)
|
|
break;
|
|
if (buf[i] == 'R')
|
|
break;
|
|
i++;
|
|
}
|
|
buf[i] = '\0';
|
|
|
|
/* Parse it. */
|
|
if (buf[0] != ESC || buf[1] != '[')
|
|
return -1;
|
|
if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2)
|
|
return -1;
|
|
return cols;
|
|
}
|
|
#endif
|
|
/*
|
|
* Try to get the number of columns in the current terminal, or assume 80
|
|
* if it fails.
|
|
*/
|
|
static int get_columns(zcHandle cs)
|
|
{
|
|
#if 0
|
|
char seq[32];
|
|
int start, cols;
|
|
|
|
/* Get the initial position so we can restore it later. */
|
|
start = get_cursor_position(cs);
|
|
if (start == -1)
|
|
goto failed;
|
|
|
|
/* Go to right margin and get position. */
|
|
if (tputs(cs, "\x1b[999C", 6) != 6)
|
|
goto failed;
|
|
cols = get_cursor_position(cs);
|
|
if (cols == -1)
|
|
goto failed;
|
|
|
|
/* Restore position. */
|
|
if (cols > start) {
|
|
snprintf(seq,32,"\x1b[%dD",cols-start);
|
|
if (tputs(cs, seq, strlen(seq)) == -1) {
|
|
/* Can't recover... */
|
|
}
|
|
}
|
|
return cols;
|
|
failed:
|
|
#endif
|
|
return 80;
|
|
}
|
|
|
|
/*
|
|
* 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(zcHandle cs)
|
|
{
|
|
struct linebuf lb;
|
|
int mode;
|
|
char line[512];
|
|
char seq[64] = {0};
|
|
char *buf = cs->buf;
|
|
size_t len = cs->len;
|
|
size_t pos = cs->pos;
|
|
|
|
while((cs->plen + pos) >= cs->cols) {
|
|
buf++;
|
|
len--;
|
|
pos--;
|
|
}
|
|
|
|
while (cs->plen+len > cs->cols) {
|
|
len--;
|
|
}
|
|
|
|
linebuf_init(&lb, line, sizeof line);
|
|
|
|
/* Cursor to left edge */
|
|
linebuf_append(&lb, "\r", 1);
|
|
linebuf_append(&lb, cs->prompt, cs->plen);
|
|
linebuf_append(&lb, buf, len);
|
|
pos += cs->plen;
|
|
|
|
/* Erase to right */
|
|
linebuf_append(&lb, "\x1b[0K", 4);
|
|
|
|
/* Move cursor to original position. */
|
|
snprintf(seq, sizeof seq, "\r\x1b[%dC", (int)(pos));
|
|
linebuf_append(&lb, seq, strlen(seq));
|
|
mode = cs->is_raw;
|
|
cs->is_raw = 1;
|
|
tputs(cs, line, linebuf_len(&lb));
|
|
cs->is_raw = mode;
|
|
}
|
|
|
|
static void refresh_line(zcHandle cs)
|
|
{
|
|
refresh_single_line(cs);
|
|
}
|
|
|
|
/*
|
|
* 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(zcHandle cs)
|
|
{
|
|
if (cs->len > 0 && cs->pos < cs->len) {
|
|
memmove(cs->buf + cs->pos, cs->buf + cs->pos + 1, cs->len - cs->pos - 1);
|
|
cs->len--;
|
|
cs->buf[cs->len] = '\0';
|
|
refresh_line(cs);
|
|
}
|
|
}
|
|
|
|
/* Backspace implementation. */
|
|
static void edit_backspace(zcHandle cs)
|
|
{
|
|
if (cs->pos > 0 && cs->len > 0) {
|
|
memmove(cs->buf + cs->pos - 1, cs->buf + cs->pos, cs->len - cs->pos);
|
|
cs->pos--;
|
|
cs->len--;
|
|
cs->buf[cs->len] = '\0';
|
|
refresh_line(cs);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Delete the previosu word, maintaining the cursor at the start of the
|
|
* current word.
|
|
*/
|
|
static void edit_delete_prev(zcHandle cs)
|
|
{
|
|
size_t old_pos = cs->pos;
|
|
size_t diff;
|
|
|
|
while (cs->pos > 0 && cs->buf[cs->pos - 1] == ' ')
|
|
cs->pos--;
|
|
while (cs->pos > 0 && cs->buf[cs->pos - 1] != ' ')
|
|
cs->pos--;
|
|
diff = old_pos - cs->pos;
|
|
memmove(cs->buf + cs->pos, cs->buf + old_pos, cs->len - old_pos + 1);
|
|
cs->len -= diff;
|
|
refresh_line(cs);
|
|
}
|
|
|
|
/*
|
|
* Insert the character 'c' at cursor current position.
|
|
*
|
|
* On error writing to the terminal -1 is returned, otherwise 0.
|
|
*/
|
|
static void edit_insert(zcHandle cs, char c)
|
|
{
|
|
if (cs->len < sizeof cs->buf) {
|
|
if (cs->len == cs->pos) {
|
|
cs->buf[cs->pos] = c;
|
|
cs->pos++;
|
|
cs->len++;
|
|
cs->buf[cs->len] = '\0';
|
|
if ((cs->plen + cs->len) < cs->cols) {
|
|
/*
|
|
* Avoid a full update of the line in the
|
|
* trivial case.
|
|
*/
|
|
if (cs->do_echo)
|
|
tputs(cs, &c, 1);
|
|
} else {
|
|
refresh_line(cs);
|
|
}
|
|
} else {
|
|
memmove(cs->buf + cs->pos + 1, cs->buf + cs->pos, cs->len - cs->pos);
|
|
cs->buf[cs->pos] = c;
|
|
cs->len++;
|
|
cs->pos++;
|
|
cs->buf[cs->len] = '\0';
|
|
refresh_line(cs);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void move_right(zcHandle cs)
|
|
{
|
|
if (cs->pos != cs->len) {
|
|
++cs->pos;
|
|
refresh_line(cs);
|
|
}
|
|
}
|
|
|
|
static void move_left(zcHandle cs)
|
|
{
|
|
if (cs->pos > 0) {
|
|
cs->pos--;
|
|
refresh_line(cs);
|
|
}
|
|
}
|
|
|
|
static void move_home(zcHandle cs)
|
|
{
|
|
if (cs->pos != 0) {
|
|
cs->pos = 0;
|
|
refresh_line(cs);
|
|
}
|
|
}
|
|
|
|
static void move_end(zcHandle cs)
|
|
{
|
|
if (cs->pos != cs->len) {
|
|
cs->pos = cs->len;
|
|
refresh_line(cs);
|
|
}
|
|
}
|
|
|
|
static int history_add(zcHandle cs, const char *line)
|
|
{
|
|
char *copy;
|
|
|
|
if (cs->history_len && !strcmp(cs->history[cs->history_len - 1], line))
|
|
return 0;
|
|
|
|
copy = vstrdup(line);
|
|
if (!copy)
|
|
return 0;
|
|
if (cs->history_len == TARGET_CMD_HISTORY) {
|
|
free(cs->history[0]);
|
|
memmove(cs->history, cs->history + 1, sizeof (char *) * (cs->history_len - 1));
|
|
--cs->history_len;
|
|
}
|
|
cs->history[cs->history_len++] = copy;
|
|
return 1;
|
|
}
|
|
|
|
static int history_free(zcHandle cs)
|
|
{
|
|
while(cs->history_len > 0) {
|
|
--cs->history_len;
|
|
if (cs->history[cs->history_len]) {
|
|
free(cs->history[cs->history_len]);
|
|
cs->history[cs->history_len] = NULL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Substitute the currently edited line with the next or previous history
|
|
* entry as specified by 'dir'
|
|
*/
|
|
static void edit_history(zcHandle cs, int dir)
|
|
{
|
|
if (cs->history_len > 0) {
|
|
cs->hindex += dir;
|
|
if (cs->hindex < 0) {
|
|
cs->hindex = cs->history_len - 1;
|
|
} else if (cs->hindex >= cs->history_len) {
|
|
cs->hindex = 0;
|
|
}
|
|
strncpy(cs->buf, cs->history[cs->hindex], cs->buflen);
|
|
cs->buf[cs->buflen - 1] = '\0';
|
|
cs->len = cs->pos = strlen(cs->buf);
|
|
refresh_line(cs);
|
|
}
|
|
}
|
|
|
|
static void history_prev(zcHandle cs)
|
|
{
|
|
edit_history(cs, -1);
|
|
}
|
|
|
|
static void history_next(zcHandle cs)
|
|
{
|
|
edit_history(cs, 1);
|
|
}
|
|
|
|
static void reset_linestate(zcHandle cs)
|
|
{
|
|
memset(cs->buf, 0, sizeof cs->buf);
|
|
|
|
cs->oldpos = 0;
|
|
cs->pos = 0;
|
|
cs->len = 0;
|
|
cs->hindex = cs->history_len;
|
|
|
|
tputs(cs, cs->prompt, cs->plen);
|
|
}
|
|
|
|
struct match_entry {
|
|
CMD_TBL_S *last;
|
|
const char *cmd;
|
|
int cmdlen;
|
|
int matchlen;
|
|
int count;
|
|
};
|
|
|
|
static int __do_get_matching(void *user, const char *key, void *data)
|
|
{
|
|
struct match_entry *me = user;
|
|
int n = strlen(key);
|
|
|
|
if (n > me->cmdlen)
|
|
n = me->cmdlen;
|
|
|
|
if (n < me->matchlen)
|
|
return 0;
|
|
|
|
if (memcmp(me->cmd, key, n) == 0) {
|
|
if (n > me->matchlen) {
|
|
me->last = data;
|
|
me->matchlen = n;
|
|
me->count = 1;
|
|
} else {
|
|
me->count++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int get_matched(struct hash_table *ht, const char *cmd_name, int len, CMD_TBL_S **last_matched)
|
|
{
|
|
struct match_entry me = {NULL, cmd_name, len, 0, 0};
|
|
|
|
if (ht) {
|
|
ht_for_each(ht, &me, __do_get_matching);
|
|
if (last_matched)
|
|
*last_matched = me.last;
|
|
}
|
|
return me.count;
|
|
}
|
|
|
|
struct fill_entry {
|
|
const char **vec;
|
|
int veclen;
|
|
int matched;
|
|
const char *cmd;
|
|
int cmdlen;
|
|
};
|
|
|
|
static int __do_fill_matched(void *user, const char *key, void *data)
|
|
{
|
|
struct fill_entry *fe = user;
|
|
int n = strlen(key);
|
|
|
|
if (n > fe->cmdlen)
|
|
n = fe->cmdlen;
|
|
if (memcmp(key, fe->cmd, n) == 0) {
|
|
fe->vec[fe->matched++] = key;
|
|
if (fe->matched == fe->veclen)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int fill_matched(struct hash_table *ht, int veclen, const char *vec[], const char *buf, int len)
|
|
{
|
|
struct fill_entry fet = {vec, veclen, 0, buf, len};
|
|
|
|
if (ht)
|
|
ht_for_each(ht, &fet, __do_fill_matched);
|
|
|
|
return fet.matched;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static int do_complete_line(zcHandle cs, int mached)
|
|
{
|
|
const char *cvec[TARGET_CMD_HISTORY];
|
|
int nread, nwritten;
|
|
size_t stop = 0, i = 0;
|
|
char c = 0;
|
|
|
|
if (fill_matched(cs->commands, mached, cvec, cs->buf, cs->len) != mached)
|
|
return 0;
|
|
|
|
while(!stop) {
|
|
/* Show completion or original buffer */
|
|
if (i < mached) {
|
|
char orig_buf[TARGET_BUFFER_SIZE];
|
|
int orig_buflen, orig_len, orig_pos;
|
|
/* save original state */
|
|
orig_buflen = cs->buflen;
|
|
orig_len = cs->len;
|
|
orig_pos = cs->pos;
|
|
memcpy(orig_buf, cs->buf, cs->buflen);
|
|
|
|
cs->len = cs->pos = strlen(cvec[i]);
|
|
sprintf(cs->buf, "%s", (char *)cvec[i]);
|
|
refresh_line(cs);
|
|
|
|
/* recovery console state */
|
|
cs->len = orig_len;
|
|
cs->pos = orig_pos;
|
|
memcpy(cs->buf, orig_buf, orig_buflen);
|
|
} else {
|
|
refresh_line(cs);
|
|
}
|
|
|
|
/*等待按enter 或 tab 键, 时间足够 1s */
|
|
nread = tgets(cs, &c, 1, 1000 * 1000);
|
|
if (nread <= 0)
|
|
return 0;
|
|
|
|
switch(c) {
|
|
case TAB: /* tab */
|
|
i = (i+1) % (mached + 1);
|
|
if (i == mached) {
|
|
tputs(cs, "\x7", 1); /* beep */
|
|
}
|
|
break;
|
|
case ESC: /* escape */
|
|
/* Re-show original buffer */
|
|
if (i < mached)
|
|
refresh_line(cs);
|
|
stop = 1;
|
|
break;
|
|
default:
|
|
/* Update buffer and return */
|
|
if (i < mached) {
|
|
nwritten = snprintf(cs->buf, cs->buflen, "%s", cvec[i]);
|
|
cs->len = cs->pos = nwritten;
|
|
}
|
|
stop = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return c; /* Return last read character */
|
|
}
|
|
|
|
static int complete_line(zcHandle cs)
|
|
{
|
|
int mached = get_matched(cs->commands, cs->buf, cs->len, NULL);
|
|
|
|
if (mached <= 0) {
|
|
tputs(cs, "\x7", 1);
|
|
return 0;
|
|
}
|
|
return do_complete_line(cs, mached);
|
|
}
|
|
|
|
static int parse_line(char *cmd, int len, char *argv[], int _maxargs)
|
|
{
|
|
int argc;
|
|
char *p;
|
|
int position;
|
|
|
|
/* Init params */
|
|
p = cmd;
|
|
position = 0;
|
|
argc = 0;
|
|
|
|
while ((position < len) && (argc < _maxargs)) {
|
|
/* Skip all blanks */
|
|
while (((*p) == ' ') && (position < len)) {
|
|
*p = '\0';
|
|
p++;
|
|
position++;
|
|
}
|
|
/* Process begin of a string */
|
|
if (*p == '"') {
|
|
p++;
|
|
position++;
|
|
argv[argc] = p;
|
|
argc++;
|
|
/* Skip this string */
|
|
while ((*p != '"') && (position < len)) {
|
|
p++;
|
|
position++;
|
|
}
|
|
/* Skip '"' */
|
|
*p = '\0';
|
|
p++;
|
|
position++;
|
|
} else {/* Normal char */
|
|
argv[argc] = p;
|
|
argc++;
|
|
while (((*p) != ' ') && ((*p) != '\t') && (position < len)) {
|
|
p++;
|
|
position++;
|
|
}
|
|
}
|
|
}
|
|
return argc;
|
|
}
|
|
|
|
int parseToArgv(char *line, int len, char *argv[], int _maxargs)
|
|
{
|
|
if (len < 0)
|
|
len = strlen(line);
|
|
|
|
return parse_line(line, len, argv, _maxargs);
|
|
}
|
|
|
|
static void __on_idle(zcHandle vt)
|
|
{
|
|
struct Listener *it, **nxt;
|
|
|
|
/* command loop idle callbacks */
|
|
it = vt->steps;
|
|
while (it) {
|
|
if ((!(it->flags & STEP_MARK_DEL)) && it->listen)
|
|
it->listen(vt, it->arg);
|
|
it = it->next;
|
|
}
|
|
|
|
/* remove all deleted objects */
|
|
nxt = &vt->steps;
|
|
while (*nxt) {
|
|
it = *nxt;
|
|
if (it->flags & STEP_MARK_DEL) {
|
|
*nxt = it->next;
|
|
nxt = &it->next;
|
|
free(it);
|
|
} else {
|
|
nxt = &it->next;
|
|
}
|
|
}
|
|
if (vt->hookup)
|
|
vt->hookup(vt, vt->param);
|
|
}
|
|
|
|
static void __preproc_args(zcHandle cs, int flags, int argc, char *argv[])
|
|
{
|
|
int i;
|
|
char *ptr;
|
|
|
|
/* preprocess args */
|
|
if (((flags & CMD_FLAG_DOLLAR) == 0) && cs->variables) {
|
|
for (i = 1; i < argc; ++i) {
|
|
if (argv[i][0] == '$') { /* begin with dollar */
|
|
ptr = ht_get(cs->variables, argv[i] + 1);
|
|
if (ptr) {
|
|
argv[i] = ptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int cmd_process(zcHandle cs, char *line)
|
|
{
|
|
int i, argc, cmdlen, nmatch = -1;
|
|
int size = strlen(line);
|
|
char *argv[TARGET_CMD_MAXARGS + 1];
|
|
CMD_TBL_S *it;
|
|
|
|
/* split line */
|
|
for (i = 0; i < TARGET_CMD_MAXARGS + 1; ++i) {
|
|
argv[i] = NULL;
|
|
}
|
|
|
|
argc = parse_line(line, size, argv, TARGET_CMD_MAXARGS + 1);
|
|
|
|
if (argc <= 0)
|
|
goto recovery;
|
|
|
|
/* exact match */
|
|
if (cs->commands) {
|
|
it = ht_get(cs->commands, argv[0]);
|
|
if (it) {
|
|
__preproc_args(cs, it->flags, argc, argv);
|
|
it->cmd(it, cs, argc, argv);
|
|
return 1;
|
|
}
|
|
}
|
|
it = NULL;
|
|
cmdlen = strlen(argv[0]);
|
|
nmatch = get_matched(cs->commands, argv[0], cmdlen, &it);
|
|
if (nmatch == 1 && it != NULL) {
|
|
__preproc_args(cs, it->flags, argc, argv);
|
|
it->cmd(it, cs, argc, argv);
|
|
return 1;
|
|
}
|
|
|
|
if (nmatch > 1) {
|
|
consolePrintln(cs, "At least two commands matched: %s", argv[0]);
|
|
} else {
|
|
consolePrintln(cs, "Command not found: %s", argv[0]);
|
|
}
|
|
/* recovery line */
|
|
recovery:
|
|
for (i = 0; i < size; ++i) {
|
|
if (line[i] == '\0')
|
|
line[i] = ' ';
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int consoleRunCmd(zcHandle nvt, const char *line)
|
|
{
|
|
#define MAX_LINESZ (128)
|
|
char buf[MAX_LINESZ + 1], *p = buf;
|
|
int rtn, len = strlen(line);
|
|
if (len > MAX_LINESZ) {
|
|
p = calloc(1, len + 1);
|
|
if (p)
|
|
strcpy(p, line);
|
|
else
|
|
return -1;
|
|
} else {
|
|
strcpy(buf, line);
|
|
}
|
|
rtn = cmd_process(nvt, p);
|
|
if (p != buf) {
|
|
free(p);
|
|
}
|
|
return rtn ? 0 : -2;
|
|
#undef MAX_LINESZ
|
|
}
|
|
|
|
static void do_command(zcHandle cs)
|
|
{
|
|
if (cs->do_echo)
|
|
tputs(cs, "\r\n", 2);
|
|
|
|
if (cs->len > 0) {
|
|
/* add to history */
|
|
history_add(cs, cs->buf);
|
|
|
|
if (cs->line)
|
|
cs->line(cs, cs->param, cs->buf);
|
|
else {
|
|
char *xbuf = cs->buf;
|
|
/* remove the head of \0 s */
|
|
while (cs->len > 0) {
|
|
if (*xbuf != '\0')
|
|
break;
|
|
--cs->len;
|
|
++xbuf;
|
|
}
|
|
if ((xbuf != cs->buf) && (cs->len > 0))
|
|
memmove(cs->buf, xbuf, cs->len);
|
|
|
|
if (cs->len > 0)
|
|
cmd_process(cs, cs->buf);
|
|
}
|
|
}
|
|
|
|
/* start next cmd, print prompt... */
|
|
if (!cs->exit)
|
|
reset_linestate(cs);
|
|
}
|
|
|
|
static int do_show_help(void *user, const char *key, void *data)
|
|
{
|
|
zcHandle cs = user;
|
|
CMD_TBL_S *it = data;
|
|
char _name[16] = {0};
|
|
int n;
|
|
|
|
n = snprintf(_name, sizeof _name - 1, "%9s\t", it->name);
|
|
tputs(cs, _name, n);
|
|
tputs(cs, it->usage, -1);
|
|
tputs(cs, "\r\n", 2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_help(CMD_TBL_S *param, zcHandle cs, int argc, char *argv[])
|
|
{
|
|
if (cs->commands) {
|
|
tputs(cs, "\r\n", 2);
|
|
ht_for_each(cs->commands, cs, do_show_help);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
CON_CMD(help, 0, do_help, "show command list");
|
|
|
|
static int do_clear(CMD_TBL_S *param, zcHandle cs, int argc, char *argv[])
|
|
{
|
|
tputs(cs, "\x1b[H\x1b[2J",7);
|
|
return 0;
|
|
}
|
|
|
|
CON_CMD(clear, 0, do_clear, "clear screen");
|
|
|
|
static void mm_usage(zcHandle cs)
|
|
{
|
|
tprintf(cs, "memory or register operations:\r\n");
|
|
tprintf(cs, "\tmm[.b|.h] r addr : read memory or register <.b: 8, .h: 16 bits>\r\n");
|
|
tprintf(cs, "\tmm[.b|.h] w addr val : write memory or register\r\n");
|
|
tprintf(cs, "\tmm[.b|.h] s addr len : show memory block\r\n");
|
|
tprintf(cs, "\tmm[.b|.h] f addr val len [+-] : fill memory block with `val`\r\n");
|
|
}
|
|
|
|
static int do_mm(CMD_TBL_S *param, zcHandle cs, int argc, char *argv[])
|
|
{
|
|
unsigned int i, addr, val = 0, length, width = 4;
|
|
int fflag = 0;
|
|
|
|
if (strcmp(argv[0], "mm.b") == 0) {
|
|
width = 1;
|
|
} else if (strcmp(argv[0], "mm.h") == 0) {
|
|
width = 2;
|
|
}
|
|
if (argc == 2 && argv[1][0] == '?') {
|
|
mm_usage(cs);
|
|
} else if ((argc >= 3) && argv[1][0] == 'r') {
|
|
addr = strtoul(argv[2], NULL, 0);
|
|
if (width == 1) {
|
|
val = *(volatile unsigned char *)(addr);
|
|
tprintf(cs, "read mem addr: 0x%x, val: 0x%02x \r\n", addr, val);
|
|
} else if (width == 2) {
|
|
val = *(volatile unsigned short *)(addr);
|
|
tprintf(cs, "read mem addr: 0x%x, val: 0x%04x \r\n", addr, val);
|
|
} else {
|
|
val = *(volatile unsigned int *)(addr);
|
|
tprintf(cs, "read mem addr: 0x%x, val: 0x%08x \r\n", addr, val);
|
|
}
|
|
} else if ((argc >= 4) && (argv[1][0] == 'w')) {
|
|
addr = strtoul(argv[2], NULL, 0);
|
|
val = strtoul(argv[3], NULL, 0);
|
|
if (width == 1) {
|
|
*(volatile unsigned char *)(addr) = val & 0xff;
|
|
tprintf(cs, "write mem addr: 0x%x, val: 0x%02x \r\n", addr, val);
|
|
} else if (width == 2) {
|
|
*(volatile unsigned short *)(addr) = val & 0xffff;
|
|
tprintf(cs, "write mem addr: 0x%x, val: 0x%04x \r\n", addr, val);
|
|
} else {
|
|
*(volatile unsigned int *)(addr) = val;
|
|
tprintf(cs, "write mem addr: 0x%x, val: 0x%08x \r\n", addr, val);
|
|
}
|
|
} else if ((argc >= 5) && (argv[1][0] == 'f')) {
|
|
addr = strtoul(argv[2], NULL, 0);
|
|
val = strtoul(argv[3], NULL, 0);
|
|
length = strtoul(argv[4], NULL, 0);
|
|
fflag = 0;
|
|
if (argc >= 6) {
|
|
switch (argv[5][0]) {
|
|
case '+':
|
|
fflag = 1;
|
|
break;
|
|
case '-':
|
|
fflag = -1;
|
|
break;
|
|
default:
|
|
fflag = 0;
|
|
break;
|
|
}
|
|
}
|
|
switch (width) {
|
|
case 1:
|
|
for (i = 0; i < length; ++i) {
|
|
*(volatile uint8_t *)(addr +i) = (val + i * fflag) & 0xff;
|
|
}
|
|
break;
|
|
case 2:
|
|
for (i = 0; i < length / 2; ++i) {
|
|
*(volatile uint16_t *)(addr + i * 2) = (val + i * fflag) & 0xffff;
|
|
}
|
|
break;
|
|
case 4:
|
|
default:
|
|
for (i = 0; i < length / 4 ; ++i) {
|
|
*(volatile uint32_t *)(addr + i * 4) = (val + i * fflag);
|
|
}
|
|
break;
|
|
}
|
|
} else if ((argc >= 4) && (argv[1][0] == 's')) {
|
|
addr = strtoul(argv[2], NULL, 0);
|
|
length = strtoul(argv[3], NULL, 0);
|
|
|
|
if (width == 1) {
|
|
for (i = 0; i < length; ++i) {
|
|
if (i % 16 == 0)
|
|
tprintf(cs, "0x%08x: ", addr + i);
|
|
tprintf(cs, "%02x ", *(volatile uint8_t *)(addr + i));
|
|
if (i % 16 == 15)
|
|
tprintf(cs, "\r\n");
|
|
}
|
|
|
|
if (i % 16 != 15)
|
|
tprintf(cs, "\r\n");
|
|
} else if (width == 2) {
|
|
for (i = 0; i < length / 2; ++i) {
|
|
if (i % 8 == 0)
|
|
tprintf(cs, "0x%08x: ", addr + (i * 2));
|
|
tprintf(cs, "%04x ", *(volatile uint16_t *)(addr + 2 * i));
|
|
if (i % 8 == 7)
|
|
tprintf(cs, "\r\n");
|
|
}
|
|
|
|
if (i % 8 != 7)
|
|
tprintf(cs, "\r\n");
|
|
} else {
|
|
for (i = 0; i < length / 4; ++i) {
|
|
if (i % 4 == 0)
|
|
tprintf(cs, "0x%08x: ", addr + (i * 4));
|
|
tprintf(cs, "%08x ", *(volatile uint32_t *)(addr + 4 * i));
|
|
if (i % 4 == 3)
|
|
tprintf(cs, "\r\n");
|
|
}
|
|
|
|
if (i % 4 != 3)
|
|
tprintf(cs, "\r\n");
|
|
}
|
|
} else {
|
|
mm_usage(cs);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
CON_CMD(mm, 0, do_mm, "memory read/write");
|
|
|
|
static int __do_cmd_echo(CMD_TBL_S *param, zcHandle nvt, int argc, char *argv[])
|
|
{
|
|
int i;
|
|
char *ptr;
|
|
|
|
for (i = 1; i < argc; ++i) {
|
|
if (argv[i][0] == '$') {
|
|
if (nvt->variables) {
|
|
ptr = ht_get(nvt->variables, argv[i] + 1); /* skip '$' */
|
|
if (ptr) {
|
|
consolePrintf(nvt, "%s ", ptr);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
consolePrintf(nvt, "%s ", argv[i]);
|
|
}
|
|
consolePrintf(nvt, "\r\n");
|
|
return 0;
|
|
}
|
|
|
|
CON_CMD(echo, (CMD_FLAG_DOLLAR), __do_cmd_echo, "echo back string(s)");
|
|
|
|
static int do_sleep(CMD_TBL_S *param, zcHandle nvt, int argc, char *argv[])
|
|
{
|
|
unsigned int i, sec = 0, usec = 0;
|
|
double dt, udt;
|
|
|
|
if (!nvt->delayUs)
|
|
return -1;
|
|
if (argc >= 2) {
|
|
dt = strtod(argv[1], NULL);
|
|
sec = (unsigned int)dt;
|
|
udt = dt - sec;
|
|
udt *= 1000000;
|
|
usec = (unsigned int)(udt);
|
|
for (i = 0; i < sec; ++i)
|
|
nvt->delayUs(nvt->param, 1000000);
|
|
|
|
if (usec > 0)
|
|
nvt->delayUs(nvt->param, usec);
|
|
return 0;
|
|
}
|
|
return -2;
|
|
}
|
|
|
|
CON_CMD (sleep, 0, do_sleep, "sleep some time.");
|
|
|
|
static struct hash_elem * convar_new(const char *key, void *data)
|
|
{
|
|
struct hash_elem *e;
|
|
char *ptr;
|
|
int len = sizeof *e + strlen(key) + 1;
|
|
|
|
e = calloc(1, len);
|
|
if (e == NULL) {
|
|
return NULL;
|
|
}
|
|
len = strlen(data);
|
|
ptr = calloc(1, len + 1);
|
|
if (ptr == NULL) {
|
|
free(e);
|
|
return NULL;
|
|
}
|
|
e->key = (const char *)&e[1];
|
|
strcpy((char *)e->key, key);
|
|
strcpy(ptr, data);
|
|
e->data = ptr;
|
|
e->next = NULL;
|
|
return (e);
|
|
}
|
|
|
|
static int convar_update(struct hash_elem *e, void *new_data)
|
|
{
|
|
char *ptr = calloc(1, strlen(new_data) + 1);
|
|
|
|
if (ptr == NULL) {
|
|
return -1;
|
|
}
|
|
strcpy(ptr, new_data);
|
|
free(e->data);
|
|
e->data = ptr;
|
|
return 0;
|
|
}
|
|
|
|
static void convar_free(struct hash_elem *e)
|
|
{
|
|
free(e->data);
|
|
free(e);
|
|
}
|
|
|
|
static int do_builtin_set(CMD_TBL_S *param, zcHandle nvt, int argc, char *argv[])
|
|
{
|
|
static struct hash_elem_op eop = {
|
|
.new = convar_new,
|
|
.update = convar_update,
|
|
.free = convar_free
|
|
};
|
|
|
|
if (nvt->variables == NULL) {
|
|
nvt->variables = ht_create(TARGET_VARIABLES_MAX, &eop);
|
|
}
|
|
|
|
if (nvt->variables == NULL) {
|
|
consolePrintln(nvt, "out of memory");
|
|
return -1;
|
|
}
|
|
|
|
if (argc < 2) {
|
|
ht_remove(nvt->variables, argv[1]);
|
|
} else {
|
|
ht_set(nvt->variables, argv[1], argv[2]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
CON_CMD(set, 0, do_builtin_set, "set variable.");
|
|
|
|
static struct hash_elem * centry_new(const char *key, void *data)
|
|
{
|
|
struct hash_elem *e;
|
|
CMD_TBL_S *cts = data;
|
|
|
|
e = calloc(1, sizeof *e);
|
|
if (e == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
e->key = cts->name;
|
|
e->data = cts;
|
|
e->next = NULL;
|
|
return (e);
|
|
}
|
|
|
|
static int centry_update(struct hash_elem *e, void *new_data)
|
|
{
|
|
CMD_TBL_S *cts = new_data;
|
|
|
|
e->key = cts->name;
|
|
e->data = new_data;
|
|
return 0;
|
|
}
|
|
|
|
static void centry_free(struct hash_elem *e)
|
|
{
|
|
free(e);
|
|
}
|
|
|
|
#if defined(__CC_ARM)
|
|
static void add_default_commands(struct hash_table *htb)
|
|
{
|
|
CMD_TBL_S *it;
|
|
|
|
for (it = &Image$$CMDLINE$$Base; it != &Image$$CMDLINE$$Limit; it++) {
|
|
ht_set(htb, it->name, it);
|
|
}
|
|
}
|
|
#else
|
|
static void add_default_commands(struct hash_table *htb)
|
|
{
|
|
CMD_TBL_S *it;
|
|
|
|
for (it = &__console_cmd_start; it != &__console_cmd_end; it++) {
|
|
ht_set(htb, it->name, it);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int consoleAddCommand(zcHandle nvt, const CMD_TBL_S *cmd)
|
|
{
|
|
if (nvt->commands) {
|
|
ht_set(nvt->commands, cmd->name, (void *)cmd);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int consoleRemoveCommand(zcHandle nvt, const char *cmdname)
|
|
{
|
|
if (nvt->commands) {
|
|
return ht_remove(nvt->commands, cmdname);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#define DEFAULT_ISSUE "\rwelcome to console"
|
|
zcHandle consoleNewWithIssue(ConsoleCfg *cfg, const char *issue)
|
|
{
|
|
zcHandle cs;
|
|
|
|
if (NULL == (cs = calloc(1, sizeof *cs)))
|
|
return NULL;
|
|
|
|
cs->buflen = sizeof cs->buf;
|
|
if (cfg->prompt) {
|
|
cs->plen = strlen(cfg->prompt);
|
|
if (cs->plen >= PROMPT_MAX_LEN)
|
|
cs->plen = PROMPT_MAX_LEN - 1;
|
|
memcpy(cs->prompt, cfg->prompt, cs->plen);
|
|
} else {
|
|
strcpy(cs->prompt, "con> ");
|
|
}
|
|
|
|
if (issue == NULL)
|
|
issue = DEFAULT_ISSUE;
|
|
|
|
snprintf(cs->buf, cs->buflen - 1, "%s", issue);
|
|
cs->param = cfg->param;
|
|
cs->putc = cfg->putc;
|
|
cs->getc = cfg->getc;
|
|
cs->line = cfg->line;
|
|
cs->delayUs = cfg->delayUs;
|
|
cs->is_raw = 0;
|
|
cs->olast_ch = -1;
|
|
cs->do_echo = 1; /* default echo */
|
|
|
|
cs->cols = get_columns(cs);
|
|
|
|
/* commands to hash table */
|
|
if (cfg->line == NULL) {
|
|
static struct hash_elem_op eop = {
|
|
.new = centry_new,
|
|
.update = centry_update,
|
|
.free = centry_free
|
|
};
|
|
cs->commands = ht_create(TARGET_COMMANDS_MAX, &eop);
|
|
add_default_commands(cs->commands);
|
|
}
|
|
return cs;
|
|
}
|
|
|
|
zcHandle consoleNew(ConsoleCfg *cfg)
|
|
{
|
|
return consoleNewWithIssue(cfg, DEFAULT_ISSUE);
|
|
}
|
|
|
|
static void free_all_listeners(zcHandle nvt)
|
|
{
|
|
struct Listener *s, *n;
|
|
|
|
s = nvt->steps;
|
|
while (s) {
|
|
n = s->next;
|
|
free(s);
|
|
s = n;
|
|
}
|
|
}
|
|
|
|
void consoleFree(zcHandle handle)
|
|
{
|
|
if (handle) {
|
|
/* free variables */
|
|
if (handle->variables)
|
|
ht_destroy(handle->variables);
|
|
|
|
if (handle->free)
|
|
handle->free(handle->param);
|
|
|
|
/* free commands */
|
|
if (handle->commands)
|
|
ht_destroy(handle->commands);
|
|
|
|
history_free(handle);
|
|
free_all_listeners(handle);
|
|
free(handle);
|
|
}
|
|
}
|
|
|
|
int consoleAddListener(zcHandle nvt, void (*listen)(zcHandle, void *), void *arg)
|
|
{
|
|
struct Listener *step = NULL;
|
|
|
|
/* find duplication */
|
|
step = nvt->steps;
|
|
while (step) {
|
|
if ((step->listen == listen) && (step->arg == arg)) {
|
|
return -1;
|
|
}
|
|
step = step->next;
|
|
}
|
|
|
|
/* add to list */
|
|
step = calloc(1, sizeof *step);
|
|
if (step) {
|
|
step->listen = listen;
|
|
step->arg = arg;
|
|
step->next = nvt->steps;
|
|
nvt->steps = step;
|
|
return 0;
|
|
}
|
|
return -2;
|
|
}
|
|
|
|
int consoleRemoveListener(zcHandle nvt, void (*listen)(zcHandle, void *), void *arg)
|
|
{
|
|
struct Listener *it = nvt->steps;
|
|
|
|
while (it) {
|
|
if ((it->listen == listen) && (it->arg == arg)) {
|
|
it->flags |= STEP_MARK_DEL;
|
|
return 0;
|
|
}
|
|
it = it->next;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void consoleProcess(zcHandle handle)
|
|
{
|
|
int nread;
|
|
char c, seq[3];
|
|
zcHandle cs = handle;
|
|
|
|
//use first loop puts "abc> "
|
|
if (cs->issue == 0) { /* print issue message */
|
|
tputs(cs, cs->buf, -1);
|
|
tputs(cs, "\r\n", 2);
|
|
|
|
reset_linestate(cs);
|
|
cs->issue = 1;
|
|
}
|
|
|
|
nread = tgets(cs, &c, 1, 0);
|
|
if (nread <= 0) {
|
|
__on_idle(handle);
|
|
return;
|
|
}
|
|
|
|
/* if c == TAB, do completion? */
|
|
if (c == TAB) {
|
|
c = complete_line(cs);
|
|
if (c <= 0) {
|
|
__on_idle(handle);
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (c) {
|
|
case ENTER: //'\r'
|
|
do_command(cs);
|
|
break;
|
|
case '\n': // '\n'
|
|
if (cs->last_ch != ENTER) { //'\r\n' pre '\r' already handle
|
|
do_command(cs);
|
|
}
|
|
break;
|
|
case CTRL_C: /* ctrl-c */
|
|
reset_linestate(cs);
|
|
break;
|
|
case BACKSPACE: /* backspace */
|
|
case CTRL_H:
|
|
edit_backspace(cs);
|
|
break;
|
|
case CTRL_D: /* ctrl-d, remove the right of cursor. */
|
|
if (cs->len > 0) {
|
|
edit_delete(cs);
|
|
} else {
|
|
reset_linestate(cs);
|
|
}
|
|
break;
|
|
case CTRL_T: /* ctrl-t, swaps current char with previous */
|
|
if (cs->pos > 0 && cs->pos < cs->len) {
|
|
int aux = cs->buf[cs->pos - 1];
|
|
cs->buf[cs->pos-1] = cs->buf[cs->pos];
|
|
cs->buf[cs->pos] = aux;
|
|
if (cs->pos != cs->len - 1) cs->pos++;
|
|
refresh_line(cs);
|
|
}
|
|
break;
|
|
case CTRL_B: /* ctrl -b */
|
|
move_left(cs);
|
|
break;
|
|
case CTRL_F: /* ctrl-f */
|
|
move_right(cs);
|
|
break;
|
|
case CTRL_L:
|
|
tputs(cs, "\x1b[H\x1b[2J",7);
|
|
refresh_line(cs);
|
|
break;
|
|
case CTRL_U: /* ctrl-u, delete the whole line */
|
|
cs->buf[0] = '\0';
|
|
cs->pos = cs->len = 0;
|
|
refresh_line(cs);
|
|
break;
|
|
case CTRL_K: /* ctrl-k, delete from current to end of line */
|
|
cs->buf[cs->pos] = '\0';
|
|
cs->len = cs->pos;
|
|
refresh_line(cs);
|
|
break;
|
|
case CTRL_A: /* ctrl-a, goto start of the line */
|
|
move_home(cs);
|
|
break;
|
|
case CTRL_E: /* ctrl-e, goto end of the line */
|
|
move_end(cs);
|
|
break;
|
|
case CTRL_W:
|
|
edit_delete_prev(cs);
|
|
break;
|
|
case CTRL_P:
|
|
history_prev(cs);
|
|
break;
|
|
case CTRL_N:
|
|
history_next(cs);
|
|
break;
|
|
case CTRL_Z:
|
|
cs->exit = 1; /* exit console */
|
|
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(cs, seq, 1, 1000) <= 0)
|
|
break;
|
|
if (tgets(cs, 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(cs, seq + 2, 1, 1000) <= 0)
|
|
break;
|
|
if (seq[2] == '~') {
|
|
switch(seq[1]) {
|
|
case '3': /* Delete key. */
|
|
edit_delete(cs);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
switch(seq[1]) {
|
|
case 'A': /* Up */
|
|
history_prev(cs);
|
|
break;
|
|
case 'B': /* Down */
|
|
history_next(cs);
|
|
break;
|
|
case 'C': /* Right */
|
|
move_right(cs);
|
|
break;
|
|
case 'D': /* Left */
|
|
move_left(cs);
|
|
break;
|
|
case 'H': /* Home */
|
|
move_home(cs);
|
|
break;
|
|
case 'F': /* End*/
|
|
move_end(cs);
|
|
break;
|
|
}
|
|
}
|
|
} else if (seq[0] == 'O') { /* ESC O sequences. */
|
|
switch(seq[1]) {
|
|
case 'H': /* Home */
|
|
move_home(cs);
|
|
break;
|
|
case 'F': /* End*/
|
|
move_end(cs);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
edit_insert(cs, c);
|
|
break;
|
|
}
|
|
|
|
cs->last_ch = c;
|
|
__on_idle(handle);
|
|
}
|
|
|
|
static int do_va_printf(zcHandle vt, int withNewLine, const char *fmt, va_list va)
|
|
{
|
|
#define CON_LINE_MAX (256)
|
|
int n = 0;
|
|
char linebuf[CON_LINE_MAX] = {0};
|
|
|
|
/* reserve space for '\r\n\0' */
|
|
n = vsnprintf(linebuf, CON_LINE_MAX - 3, fmt, va);
|
|
if (n > (CON_LINE_MAX - 3)) {
|
|
n = (CON_LINE_MAX - 3);
|
|
linebuf[n] = '\0';
|
|
}
|
|
|
|
if (withNewLine) {
|
|
n = strlen(linebuf);
|
|
linebuf[n] = '\r'; /* '\0' replace with '\r' */
|
|
linebuf[n + 1] = '\n';
|
|
linebuf[n + 2] = '\0';
|
|
n += 2;
|
|
}
|
|
tputs(vt, linebuf, n);
|
|
#undef CON_LINE_MAX
|
|
return (n);
|
|
}
|
|
|
|
void consoleVPrintf(zcHandle nvt, const char *fmt, va_list va)
|
|
{
|
|
do_va_printf(nvt, 0, fmt, va);
|
|
}
|
|
|
|
void consolePrintf(zcHandle handle, const char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
do_va_printf(handle, 0, fmt, va);
|
|
va_end(va);
|
|
}
|
|
|
|
void consolePrintln(zcHandle handle, const char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
do_va_printf(handle, 1, fmt, va);
|
|
va_end(va);
|
|
}
|
|
|
|
void consoleDelayUs(zcHandle vt, unsigned int us)
|
|
{
|
|
if (vt->delayUs)
|
|
vt->delayUs(vt->param, us);
|
|
}
|
|
|
|
int consoleIsExit(zcHandle handle)
|
|
{
|
|
return handle->exit;
|
|
}
|
|
|
|
int consoleGetc(zcHandle handle, char *ch)
|
|
{
|
|
return handle->getc(handle->param, ch);
|
|
}
|
|
|
|
int consolePuts(zcHandle handle, const char *s, int len)
|
|
{
|
|
int i;
|
|
|
|
if (len < 0)
|
|
len = strlen(s);
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
handle->putc(handle->param, s[i]);
|
|
}
|
|
return (len);
|
|
}
|
|
|
|
void consoleLoop(zcHandle handle)
|
|
{
|
|
while (!consoleIsExit(handle)) {
|
|
consoleProcess(handle);
|
|
}
|
|
}
|
|
|
|
int consoleSetPrompt(zcHandle nvt, const char *prompt)
|
|
{
|
|
int plen = strlen(prompt);
|
|
|
|
if (plen >= PROMPT_MAX_LEN)
|
|
plen = PROMPT_MAX_LEN - 1;
|
|
memcpy(nvt->prompt, prompt, plen);
|
|
nvt->prompt[plen] = '\0';
|
|
nvt->plen = plen;
|
|
return 0;
|
|
}
|
|
|
|
struct SubConsole {
|
|
zcHandle nvt;
|
|
void *param;
|
|
int (*parser)(zcHandle, void *param, char *sline);
|
|
};
|
|
|
|
static void __delay_us(void *param, unsigned int us)
|
|
{
|
|
struct SubConsole *sdr = param;
|
|
sdr->nvt->delayUs(sdr->param, us);
|
|
}
|
|
|
|
static void __putc(void *param, int ch)
|
|
{
|
|
struct SubConsole *sdr = param;
|
|
sdr->nvt->putc(sdr->param, ch);
|
|
}
|
|
|
|
static int __getc(void *param, char *ch)
|
|
{
|
|
struct SubConsole *sdr = param;
|
|
return sdr->nvt->getc(sdr->param, ch);
|
|
}
|
|
|
|
static int __parser(zcHandle vt, void *param, char *line)
|
|
{
|
|
struct SubConsole *sbc = param;
|
|
return sbc->parser(vt, sbc->param, line);
|
|
}
|
|
|
|
static void __sub_hookup(zcHandle unused, void *param)
|
|
{
|
|
struct SubConsole *sbc = param;
|
|
__on_idle(sbc->nvt);
|
|
}
|
|
|
|
zcHandle consoleSubCreate(zcHandle nvt, void *param, const char *issue, int (*parser)(zcHandle, void *param, char *sline))
|
|
{
|
|
ConsoleCfg cfg;
|
|
zcHandle console;
|
|
struct SubConsole *scb = calloc(1, sizeof *scb);
|
|
|
|
if (!scb) { /* out of memory */
|
|
return NULL;
|
|
}
|
|
scb->nvt = nvt;
|
|
scb->param = param;
|
|
scb->parser = parser;
|
|
|
|
memset(&cfg, 0, sizeof cfg);
|
|
|
|
cfg.prompt = ">> ";
|
|
cfg.param = scb;
|
|
cfg.delayUs = __delay_us;
|
|
cfg.getc = __getc;
|
|
cfg.putc = __putc;
|
|
cfg.line = parser ? __parser : NULL;
|
|
cfg.free = free;
|
|
|
|
console = consoleNewWithIssue(&cfg, issue);
|
|
if (console) {
|
|
console->hookup = __sub_hookup;
|
|
}
|
|
return (console);
|
|
}
|
|
|
|
int consoleSetEchoMode(zcHandle nvt, int enable)
|
|
{
|
|
int em = nvt->do_echo;
|
|
|
|
nvt->do_echo = enable;
|
|
|
|
return em;
|
|
}
|
|
|