micro_climate/Drivers/Shell/shell.c

710 lines
16 KiB
C

/**
******************************************************************************
* @file shell.c
* @author YANDLD
* @version V2.5
* @date 2014.3.24
* @brief www.beyondcore.net http://upcmcu.taobao.com
******************************************************************************
*/
#include <stdio.h>
#include <stdarg.h>
#include "shell.h"
#include "shell_uart.h"
#if __ICCARM__
#include <yfuns.h>
#endif
#define CTL_CH(c) ((c) - 'a' + 1)
#define CTL_BACKSPACE ('\b')
#define DEL ((char)255)
#define DEL7 ((char)127)
#define CREAD_HIST_CHAR ('!')
#define putstr(str) do {while (* str != '\0') {sputc(*str++);}} while (0)
#ifdef SHELL_CONFIG_USE_STDIO
#define sgetc() getchar()
#define sputc(ch) putchar(ch)
#else
#define sgetc() dev_uart_in_char()
#define sputc(ch) dev_uart_out_char(ch)
#endif
#define BEGINNING_OF_LINE() { \
while (num) { \
sputc(CTL_BACKSPACE); \
num--; \
} \
}
#define ERASE_TO_EOL() { \
if (num < eol_num) { \
shell_printf("%*s", (int)(eol_num - num), ""); \
do { \
sputc(CTL_BACKSPACE); \
} while (--eol_num > num); \
} \
}
#define REFRESH_TO_EOL() { \
if (num < eol_num) { \
wlen = eol_num - num; \
putnstr(buf + num, wlen); \
num = eol_num; \
} \
}
/*******************************************************************************
* Prototypes
******************************************************************************/
extern int cmd_auto_complete(const char * const prompt, char * buf, uint8_t * np, uint8_t * colp);
static void putnstr(char * str, uint8_t n);
/********************************************************************************
* Variables
******************************************************************************/
cmd_tbl_t *_syscall_table_begin = NULL;
cmd_tbl_t *_syscall_table_end = NULL;
#ifndef SHELL_CONFIG_USE_STDIO
static shell_io_install_t * gpIOInstallStruct;
#endif
static char console_buffer[SHELL_CB_SIZE + 1]; /* console I/O buffer */
/* hist support */
#ifdef SHELL_CONFIG_USE_HIST
static int8_t hist_max;
static int8_t hist_add_idx;
static int8_t hist_cur = -1;
static int8_t hist_num;
/* hist buffer */
static char *hist_list[HIST_MAX];
static char hist_lines[HIST_MAX][SHELL_CB_SIZE + 1]; /* Save room for NULL */
static char *hist_list[HIST_MAX];
static char hist_lines[HIST_MAX][SHELL_CB_SIZE + 1]; /* Save room for NULL */
#endif /* SHELL_CONFIG_USE_HIST */
/*******************************************************************************
* Code
******************************************************************************/
#ifdef SHELL_CONFIG_USE_STDIO
#else
/*!
* @brief use vsprintf for format.
*/
int shell_printf(const char * format,...)
{
int chars;
va_list ap;
static char printbuffer[SHELL_CB_SIZE];
memset(printbuffer, '0', SHELL_CB_SIZE);
va_start(ap, format);
chars = vsnprintf(printbuffer, SHELL_CB_SIZE, format, ap);
//va_end(ap); /* follow MISRA... */
putnstr(printbuffer, chars);
return chars ;
}
#endif
/*!
* @brief install shell io function.
*/
void shell_io_install(shell_io_install_t * IOInstallStruct)
{
#ifndef SHELL_CONFIG_USE_STDIO
if(IOInstallStruct != NULL)
{
gpIOInstallStruct = IOInstallStruct;
}
#endif
}
/*!
* @brief beep consult
*/
void shell_beep(void)
{
sputc('\a');
}
/*!
* @brief send n chars
*/
static void putnstr(char * str, uint8_t n)
{
while (n--)
{
sputc(*str++);
}
}
#ifdef SHELL_CONFIG_USE_HIST
/*!
* @brief init history buffer and vars
*/
static void hist_init(void)
{
uint8_t i;
hist_max = 0;
hist_add_idx = 0;
hist_cur = -1;
hist_num = 0;
for (i = 0; i < HIST_MAX; i++)
{
hist_list[i] = hist_lines[i];
hist_list[i][0] = '\0';
}
}
/*!
* @brief and line string to history buffer
*/
static void cread_add_to_hist(char * line)
{
strcpy(hist_list[hist_add_idx], line);
if (++hist_add_idx >= HIST_MAX)
{
hist_add_idx = 0;
}
if (hist_add_idx > hist_max)
{
hist_max = hist_add_idx;
}
hist_num++;
}
/*!
* @brief get history data list and also get number of the list
*/
char ** shell_get_hist_data_list(uint8_t * num, uint8_t * cur_index)
{
*num = hist_max + 1 ;
*cur_index = hist_cur;
return &hist_list[0];
}
/*!
* @brief return previous history string
*/
static char * hist_prev(void)
{
char * ret;
uint8_t old_cur;
if (hist_cur < 0)
{
return NULL;
}
old_cur = hist_cur;
if (--hist_cur < 0)
{
hist_cur = hist_max;
}
if (hist_cur == hist_add_idx)
{
hist_cur = old_cur;
ret = NULL;
}
else
{
ret = hist_list[hist_cur];
}
return (ret);
}
/*!
* @brief return next history string
*/
static char * hist_next(void)
{
char * ret;
if (hist_cur < 0)
{
return NULL;
}
if (hist_cur == hist_add_idx)
{
return NULL;
}
if (++hist_cur > hist_max)
{
hist_cur = 0;
}
if (hist_cur == hist_add_idx)
{
ret = "";
}
else
{
ret = hist_list[hist_cur];
}
return (ret);
}
#endif
/*!
* @brief add a char to conslt buffer
*/
static void cread_add_char(char ichar, uint8_t insert, uint8_t * num,
uint8_t * eol_num, char * buf, uint8_t len)
{
unsigned long wlen;
/* room ??? */
if (insert || (*num == *eol_num))
{
if (*eol_num > len - 1)
{
shell_beep();
return;
}
(*eol_num)++;
}
if (insert)
{
wlen = *eol_num - *num;
if (wlen > 1)
{
memmove(&buf[*num+1], &buf[*num], wlen-1);
}
buf[*num] = ichar;
putnstr(buf + *num, wlen);
(*num)++;
while (--wlen)
{
sputc(CTL_BACKSPACE);
}
}
else
{
/* echo the character */
wlen = 1;
buf[*num] = ichar;
putnstr(buf + *num, wlen);
(*num)++;
}
}
/*!
* @brief add string to conslt buffer
*/
static void cread_add_str(char * str, uint8_t strsize, uint8_t insert, uint8_t * num,
uint8_t * eol_num, char * buf, uint8_t len)
{
while (strsize--)
{
cread_add_char(*str, insert, num, eol_num, buf, len);
str++;
}
}
/*!
* @brief read line into buffer
*/
static int cread_line(const char * const prompt, char * buf, uint8_t * len)
{
uint8_t num = 0;
uint8_t eol_num = 0;
uint8_t wlen;
char ichar = 0;
uint8_t insert = 1;
uint8_t esc_len = 0;
char esc_save[8];
uint8_t init_len = strlen(buf);
if (init_len)
{
cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
}
while (1)
{
do
{
ichar = sgetc();
}while(!ichar);
if ((ichar == '\n') || (ichar == '\r'))
{
sputc('\r');sputc('\n');
break;
}
/* handle standard linux xterm esc sequences for arrow key, etc.*/
if (esc_len != 0)
{
if (esc_len == 1)
{
if (ichar == '[')
{
esc_save[esc_len] = ichar;
esc_len = 2;
}
else
{
cread_add_str(esc_save, esc_len, insert, &num, &eol_num, buf, *len);
esc_len = 0;
}
continue;
}
switch (ichar)
{
case 'D': /* <- key */
ichar = CTL_CH('b');
esc_len = 0;
break;
case 'C': /* -> key */
ichar = CTL_CH('f');
esc_len = 0;
break; /* pass off to ^F handler */
case 'H': /* Home key */
ichar = CTL_CH('a');
esc_len = 0;
break; /* pass off to ^A handler */
case 'A': /* up arrow */
ichar = CTL_CH('p');
esc_len = 0;
break; /* pass off to ^P handler */
case 'B': /* down arrow */
ichar = CTL_CH('n');
esc_len = 0;
break; /* pass off to ^N handler */
default:
esc_save[esc_len++] = ichar;
cread_add_str(esc_save, esc_len, insert,
&num, &eol_num, buf, *len);
esc_len = 0;
continue;
}
}
switch (ichar)
{
case 0x1b:
if (esc_len == 0)
{
esc_save[esc_len] = ichar;
esc_len = 1;
}
else
{
shell_printf("impossible condition #876\n");
esc_len = 0;
}
break;
case CTL_CH('a'):
BEGINNING_OF_LINE()
break;
case CTL_CH('c'): /* ^C - break */
*buf = '\0'; /* discard input */
return (-1);
//break; /* have to follow MISRA */
case CTL_CH('f'):
if (num < eol_num)
{
sputc(buf[num]);
num++;
}
break;
case CTL_CH('b'):
if (num)
{
sputc(CTL_BACKSPACE);
num--;
}
break;
case CTL_CH('d'):
if (num < eol_num)
{
wlen = eol_num - num - 1;
if (wlen)
{
memmove(&buf[num], &buf[num+1], wlen);
putnstr(buf + num, wlen);
}
sputc(' ');
do
{
sputc(CTL_BACKSPACE);
} while (wlen--);
eol_num--;
}
break;
case CTL_CH('k'):
ERASE_TO_EOL()
break;
case CTL_CH('e'):
REFRESH_TO_EOL()
break;
case CTL_CH('o'):
insert = !insert;
break;
case CTL_CH('x'):
case CTL_CH('u'):
BEGINNING_OF_LINE()
ERASE_TO_EOL()
break;
case DEL:
case DEL7:
case 8:
if (num)
{
wlen = eol_num - num;
num--;
memmove(&buf[num], &buf[num+1], wlen);
sputc(CTL_BACKSPACE);
putnstr(buf + num, wlen);
sputc(' ');
do
{
sputc(CTL_BACKSPACE);
} while (wlen--);
eol_num--;
}
break;
case CTL_CH('p'):
case CTL_CH('n'):
#ifdef SHELL_CONFIG_USE_HIST
{
char * hline;
esc_len = 0;
if (ichar == CTL_CH('p'))
{
hline = hist_prev();
}
else
{
hline = hist_next();
}
if (!hline)
{
shell_beep();
continue;
}
/* nuke the current line */
/* first, go home */
BEGINNING_OF_LINE()
ERASE_TO_EOL()
/* copy new line into place and display */
strcpy(buf, hline);
eol_num = strlen(buf);
REFRESH_TO_EOL()
continue;
//break; /* have to follow MISRA */
}
#else
{
continue;
}
#endif /* SHELL_CONFIG_USE_HIST */
case '\t':
#ifdef SHELL_CONFIG_AUTO_COMPLETE
{
uint8_t num2, col;
/* do not autocomplete when in the middle */
if (num < eol_num)
{
shell_beep();
break;
}
buf[num] = '\0';
col = strlen(prompt) + eol_num;
num2 = num;
if (cmd_auto_complete(prompt, buf, &num2, &col))
{
col = num2 - num;
num += col;
eol_num += col;
}
break;
}
#else
{
continue;
}
#endif /* SHELL_CONFIG_AUTO_COMPLETE */
default:
cread_add_char(ichar, insert, &num, &eol_num, buf, *len);
break;
}
}
*len = eol_num;
buf[eol_num] = '\0'; /* lose the newline */
#ifdef SHELL_CONFIG_USE_HIST
if ((buf[0]) && (buf[0] != CREAD_HIST_CHAR))
{
cread_add_to_hist(buf);
}
hist_cur = hist_add_idx;
#endif
return 0;
}
/*!
* @brief read line from consult
*/
int readline (char * prompt)
{
uint8_t len = SHELL_CB_SIZE;
int8_t rc;
char * p = console_buffer;
static uint8_t initted = 0;
/* break console_buffer so that is will not repeatable */
console_buffer[0] = '\0';
if (!initted)
{
#ifdef SHELL_CONFIG_USE_HIST
hist_init();
#endif
initted = 1;
//printf("\r\nSHELL (build: %s)\r\n", __DATE__);
}
if (prompt)
{
putstr (prompt);
}
rc = cread_line(prompt, p, &len);
return rc < 0 ? rc : len;
}
/*!
* @brief extract from readline
*/
static int parse_line (char * line, char * argv[])
{
uint8_t nargs = 0;
while (nargs < SHELL_MAX_ARGS)
{
/* skip any white space */
while (isblank(*line))
{
++line;
}
if (*line == '\0')
{ /* end of line, no more args */
argv[nargs] = NULL;
return nargs;
}
argv[nargs++] = line; /* begin of argument string */
/* find end of string */
while ((*line) && (!isblank(*line)))
{
++line;
}
if (*line == '\0')
{ /* end of line, no more args */
argv[nargs] = NULL;
return nargs;
}
*line++ = '\0'; /* terminate current arg */
}
shell_printf ("** Too many args (max. %d) **\r\n", SHELL_MAX_ARGS);
return (nargs);
}
/*!
* @brief find command form command struct's name
*/
const cmd_tbl_t * shell_find_command (const char * cmd)
{
const cmd_tbl_t *cmdtp_temp = NULL;
if (cmd == NULL)
{
return NULL;
}
for(cmdtp_temp = _syscall_table_begin; cmdtp_temp < _syscall_table_end; cmdtp_temp++)
{
if (!strcmp(cmd, cmdtp_temp->name))
{
return cmdtp_temp;
}
}
return NULL;
}
static void _system_function_section_init(const void* begin, const void* end)
{
_syscall_table_begin = ( cmd_tbl_t*) begin;
_syscall_table_end = ( cmd_tbl_t*) end;
}
void shell_init(void)
{
#ifdef __CC_ARM /* ARM C Compiler */
extern const int FSymTab$$Base;
extern const int FSymTab$$Limit;
_system_function_section_init(&FSymTab$$Base, &FSymTab$$Limit);
#elif defined (__ICCARM__) /* for IAR Compiler */
#pragma section="FSymTab"
_system_function_section_init(__section_begin("FSymTab"),
__section_end("FSymTab"));
#endif
}
void shell_main_loop(char * prompt)
{
int8_t len;
uint8_t argc;
int8_t result;
const cmd_tbl_t * cmdtp;
char * argv[SHELL_MAX_ARGS]; /* NULL terminated */
uint8_t debug_level_temp;
for (;;)
{
len = readline(prompt);
if (len > 0)
{
//debug_level_temp = debug_level;
// debug_level = 3;
if ((argc = parse_line (console_buffer, argv)) == 0)
{
continue;
}
cmdtp = shell_find_command(argv[0]);
if ((cmdtp != NULL) && (cmdtp->cmd != NULL))
{
if (argc > cmdtp->maxargs)
{
result = CMD_RET_USAGE;
}
else
{
result = (cmdtp->cmd)(argc, argv);
}
}
else
{
shell_printf("Unknown command '%s' - try 'help'\r\n", argv[0]);
}
if (result == CMD_RET_USAGE)
{
if (cmdtp->usage != NULL)
{
shell_printf("%s - %s\r\n", cmdtp->name, cmdtp->usage);
}
if (cmdtp->help != NULL)
{
shell_printf("Usage:\r\n%s ", cmdtp->name);
shell_printf("%s\r\n", cmdtp->help);
}
else
{
shell_printf ("- No additional help available.\r\n");
}
}
//debug_level = debug_level_temp;
}
else if (len == -1)
{
shell_printf("<INTERRUPT>\r\n");
}
}
}