port http://sourceforge.net/projects/tinysh to mbed enviroment
Dependents: kl25z-tinyshell-demo HelloWorld_IHM02A1
This library is a port of tiny shell library to mbed enviroment.
Features
- Autocomplete
- Command history
- Linux like
Tiny Shell minimal example
#include "mbed.h" #include "tinysh.h" //serial port to use Serial pc(USBTX, USBRX); //custom function void foo_fnt(int argc, char **argv) { printf("foo command called\r\n"); for(int i=0; i<argc; i++) { printf("argv[%d]=\"%s\"\r\n",i,argv[i]); } } //custom command tinysh_cmd_t myfoocmd= {0,"foo","foo command","[args]",foo_fnt,0,0,0}; //mandatory tiny shell output function void tinysh_char_out(unsigned char c) { pc.putc(c); } void main(void){ //configure serial baudrate pc.baud(115200); //print build date pc.printf("tiny shell build %s %s\r\n",__DATE__,__TIME__); //set prompt tinysh_set_prompt("$ "); //add custom commands here tinysh_add_command(&myfoocmd); //run command parser loop foverer while(true) { tinysh_char_in( pc.getc() ); } }
tinysh.c
- Committer:
- murilopontes
- Date:
- 2014-03-11
- Revision:
- 0:78b46c0d5246
- Child:
- 1:71580bf962fe
File content as of revision 0:78b46c0d5246:
/* * 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; static 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) { putchar('\n'); for(cm=cmd;cm;cm=cm->next) { int r=strstart(cm->name,__str); if(r==FULLMATCH || r==PARTMATCH) { puts(cm->name); putchar('\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; 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 break; s++; } return res; }