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

/*
* 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;
}