Code for our FYDP -only one IMU works right now -RTOS is working

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers tinysh.c Source File

tinysh.c

00001 /*
00002  * tinysh.c
00003  *
00004  * Minimal portable shell
00005  *
00006  * Copyright (C) 2001 Michel Gutierrez <mig@nerim.net>
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free
00020  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021  */
00022 
00023 #include "tinysh.h"
00024 
00025 #ifndef BUFFER_SIZE
00026 #define BUFFER_SIZE 64
00027 #endif
00028 #ifndef HISTORY_DEPTH
00029 #define HISTORY_DEPTH 3
00030 #endif
00031 #ifndef MAX_ARGS
00032 #define MAX_ARGS 4
00033 #endif
00034 #ifndef PROMPT_SIZE
00035 #define PROMPT_SIZE 4
00036 #endif
00037 #ifndef TOPCHAR
00038 #define TOPCHAR '/'
00039 #endif
00040 
00041 
00042 typedef unsigned char uchar;
00043 /* redefine some useful and maybe missing utilities to avoid conflicts */
00044 #define strlen tinysh_strlen
00045 #define puts tinysh_puts
00046 #define putchar tinysh_char_out
00047 
00048 static void help_fnt(int argc, char **argv);
00049 
00050 static tinysh_cmd_t help_cmd={ 
00051   0,"help","display help","<cr>",help_fnt,0,0,0 };
00052 
00053 static uchar input_buffers[HISTORY_DEPTH][BUFFER_SIZE+1]={0};
00054 static uchar trash_buffer[BUFFER_SIZE+1]={0};
00055 static int cur_buf_index=0;
00056 static uchar context_buffer[BUFFER_SIZE+1]={0};
00057 static int cur_context=0;
00058 static int cur_index=0;
00059 int echo=1;
00060 static char prompt[PROMPT_SIZE+1]="$ ";
00061 static tinysh_cmd_t *root_cmd=&help_cmd;
00062 static tinysh_cmd_t *cur_cmd_ctx=0;
00063 static void *tinysh_arg=0;
00064 
00065 /* few useful utilities that may be missing */
00066 
00067 static int strlen(uchar *s)
00068 {
00069   int i;
00070   for(i=0;*s;s++,i++);
00071   return i;
00072 }
00073 
00074 static void puts(char *s)
00075 {
00076   while(*s)
00077     putchar(*s++);
00078 }
00079 
00080 /* callback for help function
00081  */
00082 static void help_fnt(int argc, char **argv)
00083 {
00084   puts("?            display help on given or available commands\r\n");
00085   puts("<TAB>        auto-completion\r\n");
00086   puts("<cr>         execute command line\r\n");
00087   puts("CTRL-P       recall previous input line\r\n");
00088   puts("CTRL-N       recall next input line\r\n");
00089   puts("<any>        treat as input character\r\n");
00090 }
00091 
00092 /*
00093  */
00094 
00095 enum { NULLMATCH,FULLMATCH,PARTMATCH,UNMATCH,MATCH,AMBIG };
00096 
00097 /* verify if the non-spaced part of s2 is included at the begining
00098  * of s1.
00099  * return FULLMATCH if s2 equal to s1, PARTMATCH if s1 starts with s2
00100  * but there are remaining chars in s1, UNMATCH if s1 does not start with
00101  * s2
00102  */
00103 int strstart(uchar *s1, uchar *s2)
00104 {
00105   while(*s1 && *s1==*s2) { s1++; s2++; }
00106 
00107   if(*s2==' ' || *s2==0)
00108     {
00109       if(*s1==0)
00110         return FULLMATCH; /* full match */
00111       else
00112         return PARTMATCH; /* partial match */ 
00113     }
00114   else
00115     return UNMATCH;     /* no match */
00116 }
00117 
00118 /*
00119  * check commands at given level with input string.
00120  * _cmd: point to first command at this level, return matched cmd
00121  * _str: point to current unprocessed input, return next unprocessed
00122  */
00123 static int parse_command(tinysh_cmd_t **_cmd, uchar **_str)
00124 {
00125   uchar *str=*_str;
00126   tinysh_cmd_t *cmd;
00127   int matched_len=0;
00128   tinysh_cmd_t *matched_cmd=0;
00129 
00130   /* first eliminate first blanks */
00131   while(*str==' ') str++;
00132   if(!*str)
00133     {
00134       *_str=str;
00135       return NULLMATCH; /* end of input */
00136     }
00137   
00138   /* first pass: count matches */
00139   for(cmd=*_cmd;cmd;cmd=cmd->next)
00140     {
00141       int ret=strstart(cmd->name,str);
00142 
00143       if(ret==FULLMATCH)
00144         {
00145           /* found full match */
00146           while(*str && *str!=' ') str++; 
00147           while(*str==' ') str++;
00148           *_str=str;
00149           *_cmd=cmd;
00150           return MATCH;
00151         }
00152       else if (ret==PARTMATCH)
00153         {
00154           if(matched_cmd)
00155             {
00156               *_cmd=matched_cmd;
00157               return AMBIG;
00158             }
00159           else
00160             {
00161               matched_cmd=cmd;
00162             }
00163         }
00164       else /* UNMATCH */
00165         {
00166         }
00167     }
00168   if(matched_cmd)
00169     {
00170       while(*str && *str!=' ') str++; 
00171       while(*str==' ') str++;
00172       *_cmd=matched_cmd;
00173       *_str=str;
00174       return MATCH;
00175     }
00176   else
00177     return UNMATCH;
00178 }
00179 
00180 /* create a context from current input line
00181  */
00182 static void do_context(tinysh_cmd_t *cmd, uchar *str)
00183 {
00184   while(*str) 
00185     context_buffer[cur_context++]=*str++;
00186   context_buffer[cur_context]=0;
00187   cur_cmd_ctx=cmd;
00188 }
00189 
00190 /* execute the given command by calling callback with appropriate 
00191  * arguments
00192  */
00193 static void exec_command(tinysh_cmd_t *cmd, uchar *str)
00194 {
00195   char *argv[MAX_ARGS];
00196   int argc=0;
00197   int i;
00198 
00199 /* copy command line to preserve it for history */
00200   for(i=0;i<BUFFER_SIZE;i++)
00201     trash_buffer[i]=str[i];
00202   str=trash_buffer;
00203   
00204 /* cut into arguments */
00205   argv[argc++]=cmd->name;
00206   while(*str && argc<MAX_ARGS)
00207     {
00208       while(*str==' ') str++;
00209       if(*str==0)
00210         break;
00211       argv[argc++]=str;
00212       while(*str!=' ' && *str) str++;
00213       if(!*str) break;
00214       *str++=0;
00215     }
00216 /* call command function if present */
00217   if(cmd->function)
00218     {
00219       tinysh_arg=cmd->arg;
00220       cmd->function(argc,&argv[0]);
00221     }
00222 }
00223 
00224 /* try to execute the current command line
00225  */
00226 static int exec_command_line(tinysh_cmd_t *cmd, uchar *_str)
00227 {
00228   uchar *str=_str;
00229 
00230   while(1)
00231     {
00232       int ret;
00233       ret=parse_command(&cmd,&str);
00234       if(ret==MATCH) /* found unique match */
00235         {
00236           if(cmd)
00237             {
00238               if(!cmd->child) /* no sub-command, execute */
00239                 {
00240                   exec_command(cmd,str);
00241                   return 0;
00242                 }
00243               else
00244                 {
00245                   if(*str==0) /* no more input, this is a context */
00246                     {
00247                       do_context(cmd,_str);
00248                       return 0;
00249                     }
00250                   else /* process next command word */
00251                     {
00252                       cmd=cmd->child;
00253                     }
00254                 }
00255             } 
00256           else /* cmd == 0 */
00257             {
00258               return 0;
00259             }
00260         }
00261       else if(ret==AMBIG)
00262         {
00263           puts("ambiguity: ");
00264           puts(str);
00265           puts("\r\n");
00266           return 0;
00267         }
00268       else if(ret==UNMATCH) /* UNMATCH */
00269         {
00270           puts("no match: ");
00271           puts(str);
00272           puts("\r\n");
00273           return 0;
00274         }
00275       else /* NULLMATCH */
00276         return 0;
00277     }
00278 }
00279 
00280 /* display help for list of commands 
00281 */
00282 static void display_child_help(tinysh_cmd_t *cmd)
00283 {
00284   tinysh_cmd_t *cm;
00285   int len=0;
00286 
00287   puts("\r\n");
00288   for(cm=cmd;cm;cm=cm->next)
00289     if(len<strlen(cm->name))
00290       len=strlen(cm->name);
00291   for(cm=cmd;cm;cm=cm->next)
00292     if(cm->help)
00293       {
00294         int i;
00295         puts(cm->name);
00296         for(i=strlen(cm->name);i<len+2;i++)
00297           putchar(' ');
00298         puts(cm->help);
00299         puts("\r\n");
00300       }
00301 }
00302 
00303 /* try to display help for current comand line
00304  */
00305 static int help_command_line(tinysh_cmd_t *cmd, uchar *_str)
00306 {
00307   uchar *str=_str;
00308 
00309   while(1)
00310     {
00311       int ret;
00312       ret=parse_command(&cmd,&str);
00313       if(ret==MATCH && *str==0) /* found unique match or empty line */
00314         {
00315           tinysh_cmd_t *cm;
00316           int len=0;
00317               
00318           if(cmd->child) /* display sub-commands help */
00319             {
00320               display_child_help(cmd->child);
00321               return 0;
00322             }
00323           else  /* no sub-command, show single help */
00324             {
00325               if(*(str-1)!=' ')
00326                 putchar(' ');
00327               if(cmd->usage)
00328                 puts(cmd->usage);
00329               puts(": ");
00330               if(cmd->help)
00331                 puts(cmd->help);
00332               else
00333                 puts("no help available");
00334               puts("\r\n");
00335             }
00336           return 0;
00337         }
00338       else if(ret==MATCH && *str)
00339         { /* continue processing the line */
00340           cmd=cmd->child;
00341         }
00342       else if(ret==AMBIG)
00343         {
00344           puts("\r\nambiguity: ");
00345           puts(str);
00346           puts("\r\n");
00347           return 0;
00348         }
00349       else if(ret==UNMATCH)
00350         {
00351           puts("\r\nno match: ");
00352           puts(str);
00353           puts("\r\n");
00354           return 0;
00355         }
00356       else /* NULLMATCH */
00357         {
00358           if(cur_cmd_ctx)
00359             display_child_help(cur_cmd_ctx->child);
00360           else
00361             display_child_help(root_cmd);
00362           return 0;
00363         }
00364     }
00365 }
00366 
00367 /* try to complete current command line
00368  */
00369 static int complete_command_line(tinysh_cmd_t *cmd, uchar *_str)
00370 {
00371   uchar *str=_str;
00372 
00373   while(1)
00374     {
00375       int ret;
00376       int common_len=BUFFER_SIZE;
00377       int _str_len;
00378       int i;
00379       uchar *__str=str;
00380 
00381       tinysh_cmd_t *_cmd=cmd;
00382       ret=parse_command(&cmd,&str);
00383       for(_str_len=0;__str[_str_len]&&__str[_str_len]!=' ';_str_len++);
00384       if(ret==MATCH && *str)
00385         {
00386           cmd=cmd->child;
00387         }
00388       else if(ret==AMBIG || ret==MATCH || ret==NULLMATCH)
00389         {
00390           tinysh_cmd_t *cm;
00391           tinysh_cmd_t *matched_cmd=0;
00392           int nb_match=0;
00393               
00394           for(cm=cmd;cm;cm=cm->next)
00395             {
00396               int r=strstart(cm->name,__str);
00397               if(r==FULLMATCH)
00398                 {
00399                   for(i=_str_len;cmd->name[i];i++)
00400                     tinysh_char_in(cmd->name[i]);
00401                   if(*(str-1)!=' ')
00402                     tinysh_char_in(' ');
00403                   if(!cmd->child)
00404                     {
00405                       if(cmd->usage)
00406                         {
00407                           puts(cmd->usage);
00408                           putchar('\n');
00409                           return 1;
00410                         }
00411                       else
00412                         return 0;
00413                     }
00414                   else
00415                     {
00416                       cmd=cmd->child;
00417                       break;
00418                     }
00419                 }
00420               else if(r==PARTMATCH)
00421                 {
00422                   nb_match++;
00423                   if(!matched_cmd)
00424                     {
00425                       matched_cmd=cm;
00426                       common_len=strlen(cm->name);
00427                     }
00428                   else
00429                     {
00430                       for(i=_str_len;cm->name[i] && i<common_len &&
00431                             cm->name[i]==matched_cmd->name[i];i++);
00432                       if(i<common_len)
00433                         common_len=i;
00434                     }
00435                 }
00436             }
00437           if(cm)
00438             continue;
00439           if(matched_cmd)
00440             {
00441               if(_str_len==common_len)
00442                 {
00443                   puts("\r\n");
00444                   for(cm=cmd;cm;cm=cm->next)
00445                     {
00446                       int r=strstart(cm->name,__str);
00447                       if(r==FULLMATCH || r==PARTMATCH)
00448                         {
00449                           puts(cm->name);
00450                           puts("\r\n");
00451                         }
00452                     }
00453                   return 1;
00454                 }
00455               else
00456                 {
00457                   for(i=_str_len;i<common_len;i++)
00458                     tinysh_char_in(matched_cmd->name[i]);
00459                   if(nb_match==1)
00460                     tinysh_char_in(' ');
00461                 }
00462             }
00463           return 0;
00464         }
00465       else /* UNMATCH */
00466         { 
00467           return 0;
00468         }
00469     }
00470 }
00471 
00472 /* start a new line 
00473  */
00474 static void start_of_line()
00475 {
00476   /* display start of new line */
00477   puts(prompt);
00478   if(cur_context)
00479     {
00480       puts(context_buffer);
00481       puts("> ");
00482     }
00483   cur_index=0;
00484 }
00485 
00486 /* character input 
00487  */
00488 static void _tinysh_char_in(uchar c)
00489 {
00490   uchar *line=input_buffers[cur_buf_index];
00491 
00492   if(c=='\n' || c=='\r') /* validate command */
00493     {
00494       tinysh_cmd_t *cmd;
00495       int context=0;
00496       
00497 /* first, echo the newline */
00498       if(echo)
00499         putchar(c);
00500 
00501       while(*line && *line==' ') line++;
00502       if(*line) /* not empty line */
00503         {
00504           cmd=cur_cmd_ctx?cur_cmd_ctx->child:root_cmd;
00505           exec_command_line(cmd,line);
00506           cur_buf_index=(cur_buf_index+1)%HISTORY_DEPTH;
00507           cur_index=0;
00508           input_buffers[cur_buf_index][0]=0;
00509         }
00510       start_of_line();
00511     }
00512   else if(c==TOPCHAR) /* return to top level */
00513     {
00514       if(echo)
00515         putchar(c);
00516 
00517       cur_context=0;
00518       cur_cmd_ctx=0;
00519       
00520     }
00521   else if(c==8 || c==127) /* backspace */
00522     {
00523       if(cur_index>0)
00524         {
00525           puts("\b \b");
00526           cur_index--;
00527           line[cur_index]=0;
00528         }
00529     }
00530   else if(c==16) /* CTRL-P: back in history */
00531     {
00532       int prevline=(cur_buf_index+HISTORY_DEPTH-1)%HISTORY_DEPTH;
00533 
00534       if(input_buffers[prevline][0])
00535         {
00536           line=input_buffers[prevline];
00537           /* fill the rest of the line with spaces */
00538           while(cur_index-->strlen(line))
00539             puts("\b \b");
00540           putchar('\r');
00541           start_of_line();
00542           puts(line);
00543           cur_index=strlen(line);
00544           cur_buf_index=prevline;
00545         }
00546     }
00547   else if(c==14) /* CTRL-N: next in history */
00548     {
00549       int nextline=(cur_buf_index+1)%HISTORY_DEPTH;
00550 
00551       if(input_buffers[nextline][0])
00552         {
00553           line=input_buffers[nextline];
00554           /* fill the rest of the line with spaces */
00555           while(cur_index-->strlen(line))
00556             puts("\b \b");
00557           putchar('\r');
00558           start_of_line();
00559           puts(line);
00560           cur_index=strlen(line);
00561           cur_buf_index=nextline;
00562         }
00563     }
00564   else if(c=='?') /* display help */
00565     {
00566       tinysh_cmd_t *cmd;
00567       cmd=cur_cmd_ctx?cur_cmd_ctx->child:root_cmd;
00568       help_command_line(cmd,line);
00569       start_of_line();
00570       puts(line);
00571       cur_index=strlen(line);
00572     }
00573   else if(c==9 || c=='!') /* TAB: autocompletion */
00574     {
00575       tinysh_cmd_t *cmd;
00576       cmd=cur_cmd_ctx?cur_cmd_ctx->child:root_cmd;
00577       if(complete_command_line(cmd,line))
00578         {
00579           start_of_line();
00580           puts(line);
00581         }
00582       cur_index=strlen(line);
00583     }      
00584   else /* any input character */
00585     {
00586       if(cur_index<BUFFER_SIZE)
00587         {
00588           if(echo)
00589             putchar(c);
00590           line[cur_index++]=c;
00591           line[cur_index]=0;
00592         }
00593     }
00594 }
00595 
00596 /* new character input */
00597 void tinysh_char_in(uchar c)
00598 {
00599   /*
00600    * filter characters here
00601    */
00602   _tinysh_char_in(c);
00603 }
00604 
00605 /* add a new command */
00606 void tinysh_add_command(tinysh_cmd_t *cmd)
00607 {
00608   tinysh_cmd_t *cm;
00609 
00610   if(cmd->parent)
00611     {
00612       cm=cmd->parent->child;
00613       if(!cm)
00614         {
00615           cmd->parent->child=cmd;
00616         }
00617       else
00618         {
00619           while(cm->next) cm=cm->next;
00620           cm->next=cmd;
00621         }
00622     }
00623   else if(!root_cmd)
00624     {
00625       root_cmd=cmd;
00626     }
00627   else
00628     {
00629       cm=root_cmd;
00630       while(cm->next) cm=cm->next;
00631       cm->next=cmd;      
00632     }
00633 }
00634 
00635 /* modify shell prompt
00636  */
00637 void tinysh_set_prompt(char *str)
00638 {
00639   int i;
00640   for(i=0;str[i] && i<PROMPT_SIZE;i++)
00641     prompt[i]=str[i];
00642   prompt[i]=0;
00643   /* force prompt display by generating empty command */
00644   tinysh_char_in('\r');
00645 }
00646 
00647 /* return current command argument
00648  */
00649 void *tinysh_get_arg()
00650 {
00651   return tinysh_arg;
00652 }
00653 
00654 /* string to decimal/hexadecimal conversion
00655  */
00656 unsigned long tinysh_atoxi(char *s)
00657 {
00658   int ishex=0;
00659   unsigned long res=0;
00660   int sign = 1;
00661 
00662   if(*s==0) return 0;
00663 
00664   if(*s=='0' && *(s+1)=='x')
00665     {
00666       ishex=1;
00667       s+=2;
00668     }
00669 
00670   while(*s)
00671     {
00672       if(ishex)
00673     res*=16;
00674       else
00675     res*=10;
00676 
00677       if(*s>='0' && *s<='9')
00678     res+=*s-'0';
00679       else if(ishex && *s>='a' && *s<='f')
00680     res+=*s+10-'a';
00681       else if(ishex && *s>='A' && *s<='F')
00682     res+=*s+10-'A';
00683       else if(*s=='-')
00684     sign=-1;
00685       else
00686     break;
00687       
00688       s++;
00689     }
00690 
00691   return sign*res;
00692 }
00693