source: rtems/cpukit/libmisc/shell/shell.c @ dd74e612

4.104.114.84.95
Last change on this file since dd74e612 was dd74e612, checked in by Joel Sherrill <joel.sherrill@…>, on Apr 20, 2001 at 8:35:45 PM

2001-04-20 Correo Fernando-ruiz <correo@…>

  • Added initial shell functionality.
  • Makefile.am, configure.in, wrapup/Makefile.am:
  • shell/.cvsignore, shell/Makefile.am, shell/README, shell/cmds.c, shell/shell.c, shell/shell.h: New files.
  • Property mode set to 100644
File size: 13.3 KB
Line 
1/*
2 *
3 *  Instantatiate a new terminal shell.
4 *
5 *  Author:
6 *
7 *   WORK: fernando.ruiz@ctv.es
8 *   HOME: correo@fernando-ruiz.com
9 *
10 *   Thanks at:
11 *    Chris John
12 *
13 *  $Id$
14 */
15
16#undef  __STRICT_ANSI__  /* fileno() */
17#include <stdio.h>
18
19#include <rtems.h>
20#include <rtems/error.h>
21#include <rtems/libio.h>
22#include <rtems/libio_.h> 
23
24#include <termios.h>
25#include <string.h>
26#include <stdlib.h>
27#include <ctype.h>
28#include <unistd.h>
29#include <errno.h>
30
31#include <rtems/shell.h>
32/* ----------------------------------------------- *
33 * This is a stupidity but is cute.
34 * ----------------------------------------------- */
35rtems_unsigned32 new_rtems_name(char * rtems_name) {
36        static char b[5];
37        sprintf(b,"%-4.4s",rtems_name);
38        return rtems_build_name(b[0],b[1],b[2],b[3]);
39}
40/* **************************************************************
41 * common linked list of shell commands.
42 * Because the help report is very long
43 * I have a topic for each command.
44 * Help list the topics
45 * help [topic] list the commands for the topic
46 * help [command] help for the command
47 * Can you see help rtems monitor report?
48 * ************************************************************** */
49
50struct shell_topic_tt;
51typedef struct shell_topic_tt shell_topic_t;
52
53struct shell_topic_tt {
54        char * topic;
55        shell_topic_t * next;
56};
57
58
59static shell_cmd_t   * shell_first_cmd;
60static shell_topic_t * shell_first_topic;
61/* ----------------------------------------------- *
62 * Using Chain I can reuse the rtems code.
63 * I am more comfortable with this, sorry.
64 * ----------------------------------------------- */
65shell_topic_t * shell_lookup_topic(char * topic) {
66  shell_topic_t * shell_topic;
67  shell_topic=shell_first_topic;
68  while (shell_topic) {
69   if (!strcmp(shell_topic->topic,topic)) return shell_topic;
70   shell_topic=shell_topic->next;
71  };
72  return (shell_topic_t *) NULL;
73}
74/* ----------------------------------------------- */
75shell_topic_t * shell_add_topic(char * topic) {
76 shell_topic_t * current,*aux;
77 if (!shell_first_topic) {
78  aux=malloc(sizeof(shell_topic_t));   
79  aux->topic=topic;
80  aux->next=(shell_topic_t*)NULL;
81  return shell_first_topic=aux;
82 } else {
83  current=shell_first_topic;
84  if (!strcmp(topic,current->topic)) return current;
85  while (current->next) {
86   if (!strcmp(topic,current->next->topic)) return current->next;
87   current=current->next;
88  }; 
89  aux=malloc(sizeof(shell_topic_t));   
90  aux->topic=topic;
91  aux->next=(shell_topic_t*)NULL;
92  current->next=aux;
93  return aux;
94 };
95}
96/* ----------------------------------------------- */
97shell_cmd_t * shell_lookup_cmd(char * cmd) {
98  shell_cmd_t * shell_cmd;
99  shell_cmd=shell_first_cmd;
100  while (shell_cmd) {
101   if (!strcmp(shell_cmd->name,cmd)) return shell_cmd;
102   shell_cmd=shell_cmd->next;
103  };
104  return (shell_cmd_t *) NULL;
105}
106/* ----------------------------------------------- */
107shell_cmd_t * shell_add_cmd(char * cmd,
108                      char * topic,
109                      char * usage,
110                      shell_command_t command) {
111  int shell_help(int argc,char * argv[]);
112  shell_cmd_t * shell_cmd,*shell_pvt;
113  if (!shell_first_cmd) {
114   shell_first_cmd=(shell_cmd_t *) malloc(sizeof(shell_cmd_t));
115   shell_first_cmd->name   ="help";
116   shell_first_cmd->topic  ="help";
117   shell_first_cmd->usage  ="help [topic] # list of usage of commands";
118   shell_first_cmd->command=shell_help;
119   shell_first_cmd->alias  =(shell_cmd_t *) NULL;
120   shell_first_cmd->next   =(shell_cmd_t *) NULL;
121   shell_add_topic(shell_first_cmd->topic);
122   register_cmds();
123  };
124  if (!cmd)     return (shell_cmd_t *) NULL;
125  if (!command) return (shell_cmd_t *) NULL;
126  shell_cmd=(shell_cmd_t *) malloc(sizeof(shell_cmd_t));
127  shell_cmd->name   =cmd;
128  shell_cmd->topic  =topic;
129  shell_cmd->usage  =usage;
130  shell_cmd->command=command;
131  shell_cmd->alias  =(shell_cmd_t *) NULL;
132  shell_cmd->next   =(shell_cmd_t *) NULL;
133  shell_add_topic(shell_cmd->topic);
134  shell_pvt=shell_first_cmd;
135  while (shell_pvt->next) shell_pvt=shell_pvt->next;
136  return shell_pvt->next=shell_cmd;
137}
138/* ----------------------------------------------- *
139 * you can make an alias for every command.
140 * ----------------------------------------------- */
141shell_cmd_t * shell_alias_cmd(char * cmd, char * alias) {
142  shell_cmd_t * shell_cmd,* shell_aux;
143  shell_aux=(shell_cmd_t *) NULL;
144  if (alias) {
145   if ((shell_aux=shell_lookup_cmd(alias))!=NULL) {
146    return NULL;
147   };
148   if ((shell_cmd=shell_lookup_cmd(cmd))!=NULL) {
149    shell_aux=shell_add_cmd(alias,shell_cmd->topic,
150                            shell_cmd->usage,shell_cmd->command);
151    if (shell_aux) shell_aux->alias=shell_cmd;
152   };
153  };
154  return shell_aux;
155}
156/* ----------------------------------------------- *
157 * Poor but enough..
158 * TODO: Redirection capture. "" evaluate, ... C&S welcome.
159 * ----------------------------------------------- */
160int shell_make_args(char * cmd,
161                 int  * pargc,
162                 char * argv[]) {
163  int argc=0;
164  while ((cmd=strtok(cmd," \t\r\n"))!=NULL) {
165    argv[argc++]=cmd;
166    cmd=(char*)NULL;
167   };
168  argv[argc]=(char*)NULL;
169  return *pargc=argc;
170}
171/* ----------------------------------------------- *
172 * show the help for one command.
173 * ----------------------------------------------- */
174int shell_help_cmd(shell_cmd_t * shell_cmd) {
175  char * pc;
176  int    col,line;
177  printf("%-10.10s -",shell_cmd->name);
178  col=12;
179  line=1;
180  if (shell_cmd->alias) {
181   printf("is an <alias> for command '%s'",shell_cmd->alias->name);
182  } else 
183  if (shell_cmd->usage) {
184   pc=shell_cmd->usage;
185   while (*pc) {
186    switch(*pc) {
187     case '\r':break;
188     case '\n':putchar('\n');
189               col=0;
190               break;
191     default  :putchar(*pc);
192               col++;
193               break;
194    };
195    pc++;           
196    if(col>78) { /* What daring... 78?*/
197     if (*pc) {
198      putchar('\n');
199      col=0;
200     };
201    };
202    if (!col && *pc) {
203      printf("            ");
204      col=12;line++;
205    }; 
206   };
207  };
208  puts("");
209  return line;
210}
211/* ----------------------------------------------- *
212 * show the help. The first command implemented.
213 * Can you see the header of routine? Known?
214 * The same with all the commands....
215 * ----------------------------------------------- */
216int shell_help(int argc,char * argv[]) {
217  int col,line,arg;     
218  shell_topic_t *topic;
219  shell_cmd_t * shell_cmd=shell_first_cmd;
220  if (argc<2) {
221   printf("help: TOPIC? The topics are\n");       
222   topic=shell_first_topic;
223   col=0;
224   while (topic) {
225    if (!col){
226     col=printf("   %s",topic->topic);
227    } else {
228     if ((col+strlen(topic->topic)+2)>78){
229      printf("\n");
230      col=printf("   %s",topic->topic);
231     } else {
232      col+=printf(", %s",topic->topic);
233     }; 
234    };
235    topic=topic->next;
236   };
237   printf("\n");
238   return 1;
239  };
240  line=0;
241  for (arg=1;arg<argc;arg++) {
242   if (line>16) {
243    printf("Press any key to continue...");getchar();
244    printf("\n");
245    line=0;
246   };
247   topic=shell_lookup_topic(argv[arg]);
248   if (!topic){
249    if ((shell_cmd=shell_lookup_cmd(argv[arg]))==NULL) {
250     printf("help: topic or cmd '%s' not found. Try <help> alone for a list\n",argv[arg]);
251     line++;
252    } else {
253     line+=shell_help_cmd(shell_cmd);
254    }
255    continue;
256   };
257   printf("help: list for the topic '%s'\n",argv[arg]);
258   line++;
259   while (shell_cmd) {
260    if (!strcmp(topic->topic,shell_cmd->topic)) 
261     line+=shell_help_cmd(shell_cmd);
262    if (line>16) {
263     printf("Press any key to continue...");getchar();
264     printf("\n");
265     line=0;
266    };
267    shell_cmd=shell_cmd->next;
268   };
269  }; 
270  puts("");
271  return 0;
272}
273/* ----------------------------------------------- *
274 * TODO:Change to bash readline() better.
275 * ----------------------------------------------- */
276int shell_scanline(char * line,int size,FILE * in,FILE * out) {
277  int c,col;
278  col=0;
279  for (;;) {
280   line[col]=0;   
281   c=fgetc(in);
282   switch (c) {
283    case EOF :return 0;
284    case '\r':if (out) fputc('\n',out);
285              return 1;
286    case '\b':if (col) {
287               if (out) {
288                fputc('\b',out);
289                fputc(' ',out);
290                fputc('\b',out);
291               };
292               col--;
293              } else {
294               if (out) fputc('\a',out);
295              };
296              break;
297    default  :if (!iscntrl(c)) {
298                if (col<size-1) {
299                 line[col++]=c; 
300                 if (out) fputc(c,out);
301                } else {
302                  if (out) fputc('\a',out);
303                };
304              } else {
305                if (out) fputc('\a',out);
306              };
307              break;
308   };
309  };
310}
311/* ----------------------------------------------- *
312 * - The shell TASK                               
313 * Poor but enough..
314 * TODO: Redirection. Tty Signals. ENVVARs. Shell language.
315 * ----------------------------------------------- */
316shell_env_t global_shell_env ,
317          * current_shell_env=&global_shell_env; 
318
319extern char **environ;   
320
321rtems_task shell_shell(rtems_task_argument task_argument) {
322
323  shell_env_t * shell_env =(shell_env_t*) task_argument;
324  shell_cmd_t * shell_cmd;
325
326  rtems_status_code sc;
327
328  struct termios term; 
329  char * devname;
330  char * curdir;
331
332  char cmd[256];
333  char last_cmd[256]; /* to repeat 'r' */
334  int  argc;
335  char * argv[128];
336
337  sc=rtems_libio_set_private_env();
338  if (sc!=RTEMS_SUCCESSFUL) {
339   rtems_error(sc,"rtems_libio_set_private_env():");     
340   printk("rtems_libio_set_private_env():%d",sc);       
341   rtems_task_delete(RTEMS_SELF);
342  }; 
343
344  sc=rtems_task_variable_add(RTEMS_SELF,(void*)&current_shell_env,free);
345  if (sc!=RTEMS_SUCCESSFUL) {
346   rtems_error(sc,"rtems_task_variable_add():");         
347   printk("rtems_task_variable_add():%d",sc);   
348   rtems_task_delete(RTEMS_SELF);
349  }; 
350
351  current_shell_env=shell_env; /* Set the task var */
352
353  devname=shell_env->devname;
354
355  stdin =fopen(devname,"r+");
356   
357  if (!stdin) {
358   fprintf(stderr,"shell:unable to open stdin.%s:%s\n",devname,strerror(errno));
359   printk("shell:unable to open stdin.(%s)",strerror(errno));   
360   rtems_task_delete(RTEMS_SELF);
361  };
362  setvbuf(stdin,NULL,_IONBF,0); /* Not buffered*/
363  /* make a raw terminal,Linux MANuals */
364  if (tcgetattr (fileno(stdin), &term)>=0) {
365   term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
366   term.c_oflag &= ~OPOST;
367   term.c_oflag |= (OPOST|ONLCR);
368   term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
369   term.c_cflag  = CLOCAL | CREAD |(shell_env->tcflag);
370   term.c_cc[VMIN]  = 1;
371   term.c_cc[VTIME] = 0;
372   if (tcsetattr (fileno(stdin), TCSADRAIN, &term) < 0) {
373     fprintf(stderr,"shell:cannot set terminal attributes(%s)\n",devname);
374   };
375   stdout=fopen(devname,"r+");
376   if (!stdout) {
377    fprintf(stderr,"shell:unable to open stdout.%s:%s\n",devname,strerror(errno));
378   };
379   stderr=fopen(devname,"r+");
380   if (!stderr) {
381    printf("shell:unable to open stderr.%s:%s\n",devname,strerror(errno));
382   };
383   /* when the future user environment runs ok
384    * a freopen() reopens the terminals. Now this don't work
385    * (sorry but you can't use because FILENO_STDIN!=0. Better fileno(stdin))
386    */
387  };
388  shell_add_cmd(NULL,NULL,NULL,NULL); /* init the chain list*/
389  strcpy(cmd,"");
390  printf("\n"
391         "RTEMS-SHELL:%s. "__DATE__". 'help' to list commands.\n",devname);
392  curdir=malloc(1024); 
393  chdir("/");
394  for (;;) {
395   /* Prompt section */   
396   /* XXX: show_prompt user adjustable */
397   getcwd(curdir,1024);
398   printf("%s [%s] # ",devname,curdir);
399   /* getcmd section */   
400   if (!shell_scanline(cmd,sizeof(cmd),stdin,stdout)) {
401    printf("shell:unable scanline(%s)\n",devname);     
402    break;
403   };
404   /* evaluate cmd section */     
405   if (!strcmp(cmd,"r")) {  /* repeat last command, forced, not automatic */
406    strcpy(cmd,last_cmd);
407   } else 
408   if (strcmp(cmd,"")) { /* only for get a new prompt */
409    strcpy(last_cmd,cmd);
410   }; 
411   /* exec cmd section */         
412   /* TODO:
413    *  To avoid user crash catch the signals.
414    *  Open a new stdio files with posibility of redirection *
415    *  Run in a new shell task background. (unix &)
416    *  Resuming. A little bash.
417    */   
418   if (shell_make_args(cmd,&argc,argv)) {
419    if ((shell_cmd=shell_lookup_cmd(argv[0]))!=NULL) {
420     current_shell_env->errorlevel=shell_cmd->command(argc,argv);
421    } else {
422     printf("shell:%s command not found\n",argv[0]);
423     current_shell_env->errorlevel=-1;
424    };
425   };
426   /* end exec cmd section */     
427  };
428  free(curdir);
429  rtems_task_delete(RTEMS_SELF);
430}
431/* ----------------------------------------------- */
432rtems_status_code   shell_init (char * task_name,
433                                rtems_unsigned32    task_stacksize,
434                                rtems_task_priority task_priority,
435                                char * devname,
436                                tcflag_t tcflag) {
437 rtems_id task_id;
438 rtems_status_code sc;
439 shell_env_t * shell_env;
440 sc=rtems_task_create(new_rtems_name(task_name),
441                     task_priority,
442                     task_stacksize?task_stacksize:RTEMS_MINIMUM_STACK_SIZE,
443                     (RTEMS_DEFAULT_MODES&~RTEMS_ASR_MASK)|RTEMS_ASR,
444                     RTEMS_DEFAULT_ATTRIBUTES,
445                     &task_id);
446 if (sc!=RTEMS_SUCCESSFUL) {
447  rtems_error(sc,"creating task %s in shell_init()",task_name); 
448  return sc;
449 }; 
450 shell_env=malloc(sizeof(shell_env_t));
451 if (!shell_env) {
452  rtems_task_delete(task_id);
453  sc=RTEMS_NO_MEMORY;   
454  rtems_error(sc,"allocating shell_env %s in shell_init()",task_name);   
455  return sc;
456 };
457 if (global_shell_env.magic!=new_rtems_name("SENV")) {
458  global_shell_env.magic   =new_rtems_name("SENV");
459  global_shell_env.devname ="/dev/console";
460  global_shell_env.taskname="GLOBAL";
461  global_shell_env.tcflag   =0;
462 }; 
463 shell_env->magic   =global_shell_env.magic; 
464 shell_env->devname =devname;
465 shell_env->taskname=task_name;
466 shell_env->tcflag  =tcflag;
467 return rtems_task_start(task_id,shell_shell,(rtems_task_argument) shell_env);
468}
469/* ----------------------------------------------- */
470
Note: See TracBrowser for help on using the repository browser.