root/sh.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. runcmd
  2. getcmd
  3. main
  4. panic
  5. fork1
  6. execcmd
  7. redircmd
  8. pipecmd
  9. listcmd
  10. backcmd
  11. gettoken
  12. peek
  13. parsecmd
  14. parseline
  15. parsepipe
  16. parseredirs
  17. parseblock
  18. parseexec
  19. nulterminate

   1 // Shell.
   2 
   3 #include "types.h"
   4 #include "user.h"
   5 #include "fcntl.h"
   6 
   7 // Parsed command representation
   8 #define EXEC  1
   9 #define REDIR 2
  10 #define PIPE  3
  11 #define LIST  4
  12 #define BACK  5
  13 
  14 #define MAXARGS 10
  15 
  16 struct cmd {
  17   int type;
  18 };
  19 
  20 struct execcmd {
  21   int type;
  22   char *argv[MAXARGS];
  23   char *eargv[MAXARGS];
  24 };
  25 
  26 struct redircmd {
  27   int type;
  28   struct cmd *cmd;
  29   char *file;
  30   char *efile;
  31   int mode;
  32   int fd;
  33 };
  34 
  35 struct pipecmd {
  36   int type;
  37   struct cmd *left;
  38   struct cmd *right;
  39 };
  40 
  41 struct listcmd {
  42   int type;
  43   struct cmd *left;
  44   struct cmd *right;
  45 };
  46 
  47 struct backcmd {
  48   int type;
  49   struct cmd *cmd;
  50 };
  51 
  52 int fork1(void);  // Fork but panics on failure.
  53 void panic(char*);
  54 struct cmd *parsecmd(char*);
  55 
  56 // Execute cmd.  Never returns.
  57 void
  58 runcmd(struct cmd *cmd)
     /* [previous][next][first][last][top][bottom][index][help]  */
  59 {
  60   int p[2];
  61   struct backcmd *bcmd;
  62   struct execcmd *ecmd;
  63   struct listcmd *lcmd;
  64   struct pipecmd *pcmd;
  65   struct redircmd *rcmd;
  66 
  67   if(cmd == 0)
  68     exit();
  69 
  70   switch(cmd->type){
  71   default:
  72     panic("runcmd");
  73 
  74   case EXEC:
  75     ecmd = (struct execcmd*)cmd;
  76     if(ecmd->argv[0] == 0)
  77       exit();
  78     exec(ecmd->argv[0], ecmd->argv);
  79     printf(2, "exec %s failed\n", ecmd->argv[0]);
  80     break;
  81 
  82   case REDIR:
  83     rcmd = (struct redircmd*)cmd;
  84     close(rcmd->fd);
  85     if(open(rcmd->file, rcmd->mode) < 0){
  86       printf(2, "open %s failed\n", rcmd->file);
  87       exit();
  88     }
  89     runcmd(rcmd->cmd);
  90     break;
  91 
  92   case LIST:
  93     lcmd = (struct listcmd*)cmd;
  94     if(fork1() == 0)
  95       runcmd(lcmd->left);
  96     wait();
  97     runcmd(lcmd->right);
  98     break;
  99 
 100   case PIPE:
 101     pcmd = (struct pipecmd*)cmd;
 102     if(pipe(p) < 0)
 103       panic("pipe");
 104     if(fork1() == 0){
 105       close(1);
 106       dup(p[1]);
 107       close(p[0]);
 108       close(p[1]);
 109       runcmd(pcmd->left);
 110     }
 111     if(fork1() == 0){
 112       close(0);
 113       dup(p[0]);
 114       close(p[0]);
 115       close(p[1]);
 116       runcmd(pcmd->right);
 117     }
 118     close(p[0]);
 119     close(p[1]);
 120     wait();
 121     wait();
 122     break;
 123 
 124   case BACK:
 125     bcmd = (struct backcmd*)cmd;
 126     if(fork1() == 0)
 127       runcmd(bcmd->cmd);
 128     break;
 129   }
 130   exit();
 131 }
 132 
 133 int
 134 getcmd(char *buf, int nbuf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 135 {
 136   printf(2, "$ ");
 137   memset(buf, 0, nbuf);
 138   gets(buf, nbuf);
 139   if(buf[0] == 0) // EOF
 140     return -1;
 141   return 0;
 142 }
 143 
 144 int
 145 main(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 146 {
 147   static char buf[100];
 148   int fd;
 149 
 150   // Ensure that three file descriptors are open.
 151   while((fd = open("console", O_RDWR)) >= 0){
 152     if(fd >= 3){
 153       close(fd);
 154       break;
 155     }
 156   }
 157 
 158   // Read and run input commands.
 159   while(getcmd(buf, sizeof(buf)) >= 0){
 160     if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){
 161       // Chdir must be called by the parent, not the child.
 162       buf[strlen(buf)-1] = 0;  // chop \n
 163       if(chdir(buf+3) < 0)
 164         printf(2, "cannot cd %s\n", buf+3);
 165       continue;
 166     }
 167     if(fork1() == 0)
 168       runcmd(parsecmd(buf));
 169     wait();
 170   }
 171   exit();
 172 }
 173 
 174 void
 175 panic(char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 176 {
 177   printf(2, "%s\n", s);
 178   exit();
 179 }
 180 
 181 int
 182 fork1(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 183 {
 184   int pid;
 185 
 186   pid = fork();
 187   if(pid == -1)
 188     panic("fork");
 189   return pid;
 190 }
 191 
 192 //PAGEBREAK!
 193 // Constructors
 194 
 195 struct cmd*
 196 execcmd(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 197 {
 198   struct execcmd *cmd;
 199 
 200   cmd = malloc(sizeof(*cmd));
 201   memset(cmd, 0, sizeof(*cmd));
 202   cmd->type = EXEC;
 203   return (struct cmd*)cmd;
 204 }
 205 
 206 struct cmd*
 207 redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 208 {
 209   struct redircmd *cmd;
 210 
 211   cmd = malloc(sizeof(*cmd));
 212   memset(cmd, 0, sizeof(*cmd));
 213   cmd->type = REDIR;
 214   cmd->cmd = subcmd;
 215   cmd->file = file;
 216   cmd->efile = efile;
 217   cmd->mode = mode;
 218   cmd->fd = fd;
 219   return (struct cmd*)cmd;
 220 }
 221 
 222 struct cmd*
 223 pipecmd(struct cmd *left, struct cmd *right)
     /* [previous][next][first][last][top][bottom][index][help]  */
 224 {
 225   struct pipecmd *cmd;
 226 
 227   cmd = malloc(sizeof(*cmd));
 228   memset(cmd, 0, sizeof(*cmd));
 229   cmd->type = PIPE;
 230   cmd->left = left;
 231   cmd->right = right;
 232   return (struct cmd*)cmd;
 233 }
 234 
 235 struct cmd*
 236 listcmd(struct cmd *left, struct cmd *right)
     /* [previous][next][first][last][top][bottom][index][help]  */
 237 {
 238   struct listcmd *cmd;
 239 
 240   cmd = malloc(sizeof(*cmd));
 241   memset(cmd, 0, sizeof(*cmd));
 242   cmd->type = LIST;
 243   cmd->left = left;
 244   cmd->right = right;
 245   return (struct cmd*)cmd;
 246 }
 247 
 248 struct cmd*
 249 backcmd(struct cmd *subcmd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 250 {
 251   struct backcmd *cmd;
 252 
 253   cmd = malloc(sizeof(*cmd));
 254   memset(cmd, 0, sizeof(*cmd));
 255   cmd->type = BACK;
 256   cmd->cmd = subcmd;
 257   return (struct cmd*)cmd;
 258 }
 259 //PAGEBREAK!
 260 // Parsing
 261 
 262 char whitespace[] = " \t\r\n\v";
 263 char symbols[] = "<|>&;()";
 264 
 265 int
 266 gettoken(char **ps, char *es, char **q, char **eq)
     /* [previous][next][first][last][top][bottom][index][help]  */
 267 {
 268   char *s;
 269   int ret;
 270 
 271   s = *ps;
 272   while(s < es && strchr(whitespace, *s))
 273     s++;
 274   if(q)
 275     *q = s;
 276   ret = *s;
 277   switch(*s){
 278   case 0:
 279     break;
 280   case '|':
 281   case '(':
 282   case ')':
 283   case ';':
 284   case '&':
 285   case '<':
 286     s++;
 287     break;
 288   case '>':
 289     s++;
 290     if(*s == '>'){
 291       ret = '+';
 292       s++;
 293     }
 294     break;
 295   default:
 296     ret = 'a';
 297     while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
 298       s++;
 299     break;
 300   }
 301   if(eq)
 302     *eq = s;
 303 
 304   while(s < es && strchr(whitespace, *s))
 305     s++;
 306   *ps = s;
 307   return ret;
 308 }
 309 
 310 int
 311 peek(char **ps, char *es, char *toks)
     /* [previous][next][first][last][top][bottom][index][help]  */
 312 {
 313   char *s;
 314 
 315   s = *ps;
 316   while(s < es && strchr(whitespace, *s))
 317     s++;
 318   *ps = s;
 319   return *s && strchr(toks, *s);
 320 }
 321 
 322 struct cmd *parseline(char**, char*);
 323 struct cmd *parsepipe(char**, char*);
 324 struct cmd *parseexec(char**, char*);
 325 struct cmd *nulterminate(struct cmd*);
 326 
 327 struct cmd*
 328 parsecmd(char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 329 {
 330   char *es;
 331   struct cmd *cmd;
 332 
 333   es = s + strlen(s);
 334   cmd = parseline(&s, es);
 335   peek(&s, es, "");
 336   if(s != es){
 337     printf(2, "leftovers: %s\n", s);
 338     panic("syntax");
 339   }
 340   nulterminate(cmd);
 341   return cmd;
 342 }
 343 
 344 struct cmd*
 345 parseline(char **ps, char *es)
     /* [previous][next][first][last][top][bottom][index][help]  */
 346 {
 347   struct cmd *cmd;
 348 
 349   cmd = parsepipe(ps, es);
 350   while(peek(ps, es, "&")){
 351     gettoken(ps, es, 0, 0);
 352     cmd = backcmd(cmd);
 353   }
 354   if(peek(ps, es, ";")){
 355     gettoken(ps, es, 0, 0);
 356     cmd = listcmd(cmd, parseline(ps, es));
 357   }
 358   return cmd;
 359 }
 360 
 361 struct cmd*
 362 parsepipe(char **ps, char *es)
     /* [previous][next][first][last][top][bottom][index][help]  */
 363 {
 364   struct cmd *cmd;
 365 
 366   cmd = parseexec(ps, es);
 367   if(peek(ps, es, "|")){
 368     gettoken(ps, es, 0, 0);
 369     cmd = pipecmd(cmd, parsepipe(ps, es));
 370   }
 371   return cmd;
 372 }
 373 
 374 struct cmd*
 375 parseredirs(struct cmd *cmd, char **ps, char *es)
     /* [previous][next][first][last][top][bottom][index][help]  */
 376 {
 377   int tok;
 378   char *q, *eq;
 379 
 380   while(peek(ps, es, "<>")){
 381     tok = gettoken(ps, es, 0, 0);
 382     if(gettoken(ps, es, &q, &eq) != 'a')
 383       panic("missing file for redirection");
 384     switch(tok){
 385     case '<':
 386       cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
 387       break;
 388     case '>':
 389       cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
 390       break;
 391     case '+':  // >>
 392       cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
 393       break;
 394     }
 395   }
 396   return cmd;
 397 }
 398 
 399 struct cmd*
 400 parseblock(char **ps, char *es)
     /* [previous][next][first][last][top][bottom][index][help]  */
 401 {
 402   struct cmd *cmd;
 403 
 404   if(!peek(ps, es, "("))
 405     panic("parseblock");
 406   gettoken(ps, es, 0, 0);
 407   cmd = parseline(ps, es);
 408   if(!peek(ps, es, ")"))
 409     panic("syntax - missing )");
 410   gettoken(ps, es, 0, 0);
 411   cmd = parseredirs(cmd, ps, es);
 412   return cmd;
 413 }
 414 
 415 struct cmd*
 416 parseexec(char **ps, char *es)
     /* [previous][next][first][last][top][bottom][index][help]  */
 417 {
 418   char *q, *eq;
 419   int tok, argc;
 420   struct execcmd *cmd;
 421   struct cmd *ret;
 422 
 423   if(peek(ps, es, "("))
 424     return parseblock(ps, es);
 425 
 426   ret = execcmd();
 427   cmd = (struct execcmd*)ret;
 428 
 429   argc = 0;
 430   ret = parseredirs(ret, ps, es);
 431   while(!peek(ps, es, "|)&;")){
 432     if((tok=gettoken(ps, es, &q, &eq)) == 0)
 433       break;
 434     if(tok != 'a')
 435       panic("syntax");
 436     cmd->argv[argc] = q;
 437     cmd->eargv[argc] = eq;
 438     argc++;
 439     if(argc >= MAXARGS)
 440       panic("too many args");
 441     ret = parseredirs(ret, ps, es);
 442   }
 443   cmd->argv[argc] = 0;
 444   cmd->eargv[argc] = 0;
 445   return ret;
 446 }
 447 
 448 // NUL-terminate all the counted strings.
 449 struct cmd*
 450 nulterminate(struct cmd *cmd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 451 {
 452   int i;
 453   struct backcmd *bcmd;
 454   struct execcmd *ecmd;
 455   struct listcmd *lcmd;
 456   struct pipecmd *pcmd;
 457   struct redircmd *rcmd;
 458 
 459   if(cmd == 0)
 460     return 0;
 461 
 462   switch(cmd->type){
 463   case EXEC:
 464     ecmd = (struct execcmd*)cmd;
 465     for(i=0; ecmd->argv[i]; i++)
 466       *ecmd->eargv[i] = 0;
 467     break;
 468 
 469   case REDIR:
 470     rcmd = (struct redircmd*)cmd;
 471     nulterminate(rcmd->cmd);
 472     *rcmd->efile = 0;
 473     break;
 474 
 475   case PIPE:
 476     pcmd = (struct pipecmd*)cmd;
 477     nulterminate(pcmd->left);
 478     nulterminate(pcmd->right);
 479     break;
 480 
 481   case LIST:
 482     lcmd = (struct listcmd*)cmd;
 483     nulterminate(lcmd->left);
 484     nulterminate(lcmd->right);
 485     break;
 486 
 487   case BACK:
 488     bcmd = (struct backcmd*)cmd;
 489     nulterminate(bcmd->cmd);
 490     break;
 491   }
 492   return cmd;
 493 }

/* [previous][next][first][last][top][bottom][index][help]  */