/** ****************************************************************************** * @file shell.c * @author YANDLD * @version V2.5 * @date 2014.3.24 * @brief www.beyondcore.net http://upcmcu.taobao.com ****************************************************************************** */ #include #include #include "shell.h" #include "shell_uart.h" #if __ICCARM__ #include #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("\r\n"); } } }