Code for our FYDP -only one IMU works right now -RTOS is working
Diff: bt_shell/shell/tinyshell/tinysh.c
- Revision:
- 0:964eb6a2ef00
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bt_shell/shell/tinyshell/tinysh.c Wed Mar 18 22:23:48 2015 +0000 @@ -0,0 +1,693 @@ +/* + * tinysh.c + * + * Minimal portable shell + * + * Copyright (C) 2001 Michel Gutierrez <mig@nerim.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "tinysh.h" + +#ifndef BUFFER_SIZE +#define BUFFER_SIZE 64 +#endif +#ifndef HISTORY_DEPTH +#define HISTORY_DEPTH 3 +#endif +#ifndef MAX_ARGS +#define MAX_ARGS 4 +#endif +#ifndef PROMPT_SIZE +#define PROMPT_SIZE 4 +#endif +#ifndef TOPCHAR +#define TOPCHAR '/' +#endif + + +typedef unsigned char uchar; +/* redefine some useful and maybe missing utilities to avoid conflicts */ +#define strlen tinysh_strlen +#define puts tinysh_puts +#define putchar tinysh_char_out + +static void help_fnt(int argc, char **argv); + +static tinysh_cmd_t help_cmd={ + 0,"help","display help","<cr>",help_fnt,0,0,0 }; + +static uchar input_buffers[HISTORY_DEPTH][BUFFER_SIZE+1]={0}; +static uchar trash_buffer[BUFFER_SIZE+1]={0}; +static int cur_buf_index=0; +static uchar context_buffer[BUFFER_SIZE+1]={0}; +static int cur_context=0; +static int cur_index=0; +int echo=1; +static char prompt[PROMPT_SIZE+1]="$ "; +static tinysh_cmd_t *root_cmd=&help_cmd; +static tinysh_cmd_t *cur_cmd_ctx=0; +static void *tinysh_arg=0; + +/* few useful utilities that may be missing */ + +static int strlen(uchar *s) +{ + int i; + for(i=0;*s;s++,i++); + return i; +} + +static void puts(char *s) +{ + while(*s) + putchar(*s++); +} + +/* callback for help function + */ +static void help_fnt(int argc, char **argv) +{ + puts("? display help on given or available commands\r\n"); + puts("<TAB> auto-completion\r\n"); + puts("<cr> execute command line\r\n"); + puts("CTRL-P recall previous input line\r\n"); + puts("CTRL-N recall next input line\r\n"); + puts("<any> treat as input character\r\n"); +} + +/* + */ + +enum { NULLMATCH,FULLMATCH,PARTMATCH,UNMATCH,MATCH,AMBIG }; + +/* verify if the non-spaced part of s2 is included at the begining + * of s1. + * return FULLMATCH if s2 equal to s1, PARTMATCH if s1 starts with s2 + * but there are remaining chars in s1, UNMATCH if s1 does not start with + * s2 + */ +int strstart(uchar *s1, uchar *s2) +{ + while(*s1 && *s1==*s2) { s1++; s2++; } + + if(*s2==' ' || *s2==0) + { + if(*s1==0) + return FULLMATCH; /* full match */ + else + return PARTMATCH; /* partial match */ + } + else + return UNMATCH; /* no match */ +} + +/* + * check commands at given level with input string. + * _cmd: point to first command at this level, return matched cmd + * _str: point to current unprocessed input, return next unprocessed + */ +static int parse_command(tinysh_cmd_t **_cmd, uchar **_str) +{ + uchar *str=*_str; + tinysh_cmd_t *cmd; + int matched_len=0; + tinysh_cmd_t *matched_cmd=0; + + /* first eliminate first blanks */ + while(*str==' ') str++; + if(!*str) + { + *_str=str; + return NULLMATCH; /* end of input */ + } + + /* first pass: count matches */ + for(cmd=*_cmd;cmd;cmd=cmd->next) + { + int ret=strstart(cmd->name,str); + + if(ret==FULLMATCH) + { + /* found full match */ + while(*str && *str!=' ') str++; + while(*str==' ') str++; + *_str=str; + *_cmd=cmd; + return MATCH; + } + else if (ret==PARTMATCH) + { + if(matched_cmd) + { + *_cmd=matched_cmd; + return AMBIG; + } + else + { + matched_cmd=cmd; + } + } + else /* UNMATCH */ + { + } + } + if(matched_cmd) + { + while(*str && *str!=' ') str++; + while(*str==' ') str++; + *_cmd=matched_cmd; + *_str=str; + return MATCH; + } + else + return UNMATCH; +} + +/* create a context from current input line + */ +static void do_context(tinysh_cmd_t *cmd, uchar *str) +{ + while(*str) + context_buffer[cur_context++]=*str++; + context_buffer[cur_context]=0; + cur_cmd_ctx=cmd; +} + +/* execute the given command by calling callback with appropriate + * arguments + */ +static void exec_command(tinysh_cmd_t *cmd, uchar *str) +{ + char *argv[MAX_ARGS]; + int argc=0; + int i; + +/* copy command line to preserve it for history */ + for(i=0;i<BUFFER_SIZE;i++) + trash_buffer[i]=str[i]; + str=trash_buffer; + +/* cut into arguments */ + argv[argc++]=cmd->name; + while(*str && argc<MAX_ARGS) + { + while(*str==' ') str++; + if(*str==0) + break; + argv[argc++]=str; + while(*str!=' ' && *str) str++; + if(!*str) break; + *str++=0; + } +/* call command function if present */ + if(cmd->function) + { + tinysh_arg=cmd->arg; + cmd->function(argc,&argv[0]); + } +} + +/* try to execute the current command line + */ +static int exec_command_line(tinysh_cmd_t *cmd, uchar *_str) +{ + uchar *str=_str; + + while(1) + { + int ret; + ret=parse_command(&cmd,&str); + if(ret==MATCH) /* found unique match */ + { + if(cmd) + { + if(!cmd->child) /* no sub-command, execute */ + { + exec_command(cmd,str); + return 0; + } + else + { + if(*str==0) /* no more input, this is a context */ + { + do_context(cmd,_str); + return 0; + } + else /* process next command word */ + { + cmd=cmd->child; + } + } + } + else /* cmd == 0 */ + { + return 0; + } + } + else if(ret==AMBIG) + { + puts("ambiguity: "); + puts(str); + puts("\r\n"); + return 0; + } + else if(ret==UNMATCH) /* UNMATCH */ + { + puts("no match: "); + puts(str); + puts("\r\n"); + return 0; + } + else /* NULLMATCH */ + return 0; + } +} + +/* display help for list of commands +*/ +static void display_child_help(tinysh_cmd_t *cmd) +{ + tinysh_cmd_t *cm; + int len=0; + + puts("\r\n"); + for(cm=cmd;cm;cm=cm->next) + if(len<strlen(cm->name)) + len=strlen(cm->name); + for(cm=cmd;cm;cm=cm->next) + if(cm->help) + { + int i; + puts(cm->name); + for(i=strlen(cm->name);i<len+2;i++) + putchar(' '); + puts(cm->help); + puts("\r\n"); + } +} + +/* try to display help for current comand line + */ +static int help_command_line(tinysh_cmd_t *cmd, uchar *_str) +{ + uchar *str=_str; + + while(1) + { + int ret; + ret=parse_command(&cmd,&str); + if(ret==MATCH && *str==0) /* found unique match or empty line */ + { + tinysh_cmd_t *cm; + int len=0; + + if(cmd->child) /* display sub-commands help */ + { + display_child_help(cmd->child); + return 0; + } + else /* no sub-command, show single help */ + { + if(*(str-1)!=' ') + putchar(' '); + if(cmd->usage) + puts(cmd->usage); + puts(": "); + if(cmd->help) + puts(cmd->help); + else + puts("no help available"); + puts("\r\n"); + } + return 0; + } + else if(ret==MATCH && *str) + { /* continue processing the line */ + cmd=cmd->child; + } + else if(ret==AMBIG) + { + puts("\r\nambiguity: "); + puts(str); + puts("\r\n"); + return 0; + } + else if(ret==UNMATCH) + { + puts("\r\nno match: "); + puts(str); + puts("\r\n"); + return 0; + } + else /* NULLMATCH */ + { + if(cur_cmd_ctx) + display_child_help(cur_cmd_ctx->child); + else + display_child_help(root_cmd); + return 0; + } + } +} + +/* try to complete current command line + */ +static int complete_command_line(tinysh_cmd_t *cmd, uchar *_str) +{ + uchar *str=_str; + + while(1) + { + int ret; + int common_len=BUFFER_SIZE; + int _str_len; + int i; + uchar *__str=str; + + tinysh_cmd_t *_cmd=cmd; + ret=parse_command(&cmd,&str); + for(_str_len=0;__str[_str_len]&&__str[_str_len]!=' ';_str_len++); + if(ret==MATCH && *str) + { + cmd=cmd->child; + } + else if(ret==AMBIG || ret==MATCH || ret==NULLMATCH) + { + tinysh_cmd_t *cm; + tinysh_cmd_t *matched_cmd=0; + int nb_match=0; + + for(cm=cmd;cm;cm=cm->next) + { + int r=strstart(cm->name,__str); + if(r==FULLMATCH) + { + for(i=_str_len;cmd->name[i];i++) + tinysh_char_in(cmd->name[i]); + if(*(str-1)!=' ') + tinysh_char_in(' '); + if(!cmd->child) + { + if(cmd->usage) + { + puts(cmd->usage); + putchar('\n'); + return 1; + } + else + return 0; + } + else + { + cmd=cmd->child; + break; + } + } + else if(r==PARTMATCH) + { + nb_match++; + if(!matched_cmd) + { + matched_cmd=cm; + common_len=strlen(cm->name); + } + else + { + for(i=_str_len;cm->name[i] && i<common_len && + cm->name[i]==matched_cmd->name[i];i++); + if(i<common_len) + common_len=i; + } + } + } + if(cm) + continue; + if(matched_cmd) + { + if(_str_len==common_len) + { + puts("\r\n"); + for(cm=cmd;cm;cm=cm->next) + { + int r=strstart(cm->name,__str); + if(r==FULLMATCH || r==PARTMATCH) + { + puts(cm->name); + puts("\r\n"); + } + } + return 1; + } + else + { + for(i=_str_len;i<common_len;i++) + tinysh_char_in(matched_cmd->name[i]); + if(nb_match==1) + tinysh_char_in(' '); + } + } + return 0; + } + else /* UNMATCH */ + { + return 0; + } + } +} + +/* start a new line + */ +static void start_of_line() +{ + /* display start of new line */ + puts(prompt); + if(cur_context) + { + puts(context_buffer); + puts("> "); + } + cur_index=0; +} + +/* character input + */ +static void _tinysh_char_in(uchar c) +{ + uchar *line=input_buffers[cur_buf_index]; + + if(c=='\n' || c=='\r') /* validate command */ + { + tinysh_cmd_t *cmd; + int context=0; + +/* first, echo the newline */ + if(echo) + putchar(c); + + while(*line && *line==' ') line++; + if(*line) /* not empty line */ + { + cmd=cur_cmd_ctx?cur_cmd_ctx->child:root_cmd; + exec_command_line(cmd,line); + cur_buf_index=(cur_buf_index+1)%HISTORY_DEPTH; + cur_index=0; + input_buffers[cur_buf_index][0]=0; + } + start_of_line(); + } + else if(c==TOPCHAR) /* return to top level */ + { + if(echo) + putchar(c); + + cur_context=0; + cur_cmd_ctx=0; + + } + else if(c==8 || c==127) /* backspace */ + { + if(cur_index>0) + { + puts("\b \b"); + cur_index--; + line[cur_index]=0; + } + } + else if(c==16) /* CTRL-P: back in history */ + { + int prevline=(cur_buf_index+HISTORY_DEPTH-1)%HISTORY_DEPTH; + + if(input_buffers[prevline][0]) + { + line=input_buffers[prevline]; + /* fill the rest of the line with spaces */ + while(cur_index-->strlen(line)) + puts("\b \b"); + putchar('\r'); + start_of_line(); + puts(line); + cur_index=strlen(line); + cur_buf_index=prevline; + } + } + else if(c==14) /* CTRL-N: next in history */ + { + int nextline=(cur_buf_index+1)%HISTORY_DEPTH; + + if(input_buffers[nextline][0]) + { + line=input_buffers[nextline]; + /* fill the rest of the line with spaces */ + while(cur_index-->strlen(line)) + puts("\b \b"); + putchar('\r'); + start_of_line(); + puts(line); + cur_index=strlen(line); + cur_buf_index=nextline; + } + } + else if(c=='?') /* display help */ + { + tinysh_cmd_t *cmd; + cmd=cur_cmd_ctx?cur_cmd_ctx->child:root_cmd; + help_command_line(cmd,line); + start_of_line(); + puts(line); + cur_index=strlen(line); + } + else if(c==9 || c=='!') /* TAB: autocompletion */ + { + tinysh_cmd_t *cmd; + cmd=cur_cmd_ctx?cur_cmd_ctx->child:root_cmd; + if(complete_command_line(cmd,line)) + { + start_of_line(); + puts(line); + } + cur_index=strlen(line); + } + else /* any input character */ + { + if(cur_index<BUFFER_SIZE) + { + if(echo) + putchar(c); + line[cur_index++]=c; + line[cur_index]=0; + } + } +} + +/* new character input */ +void tinysh_char_in(uchar c) +{ + /* + * filter characters here + */ + _tinysh_char_in(c); +} + +/* add a new command */ +void tinysh_add_command(tinysh_cmd_t *cmd) +{ + tinysh_cmd_t *cm; + + if(cmd->parent) + { + cm=cmd->parent->child; + if(!cm) + { + cmd->parent->child=cmd; + } + else + { + while(cm->next) cm=cm->next; + cm->next=cmd; + } + } + else if(!root_cmd) + { + root_cmd=cmd; + } + else + { + cm=root_cmd; + while(cm->next) cm=cm->next; + cm->next=cmd; + } +} + +/* modify shell prompt + */ +void tinysh_set_prompt(char *str) +{ + int i; + for(i=0;str[i] && i<PROMPT_SIZE;i++) + prompt[i]=str[i]; + prompt[i]=0; + /* force prompt display by generating empty command */ + tinysh_char_in('\r'); +} + +/* return current command argument + */ +void *tinysh_get_arg() +{ + return tinysh_arg; +} + +/* string to decimal/hexadecimal conversion + */ +unsigned long tinysh_atoxi(char *s) +{ + int ishex=0; + unsigned long res=0; + int sign = 1; + + if(*s==0) return 0; + + if(*s=='0' && *(s+1)=='x') + { + ishex=1; + s+=2; + } + + while(*s) + { + if(ishex) + res*=16; + else + res*=10; + + if(*s>='0' && *s<='9') + res+=*s-'0'; + else if(ishex && *s>='a' && *s<='f') + res+=*s+10-'a'; + else if(ishex && *s>='A' && *s<='F') + res+=*s+10-'A'; + else if(*s=='-') + sign=-1; + else + break; + + s++; + } + + return sign*res; +} +