Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of FYDP_Final2 by
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
Generated on Tue Jul 12 2022 16:56:36 by
