/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- runcmd
 - getcmd
 - main
 - panic
 - fork1
 - execcmd
 - redircmd
 - pipecmd
 - listcmd
 - backcmd
 - gettoken
 - peek
 - parsecmd
 - parseline
 - parsepipe
 - parseredirs
 - parseblock
 - parseexec
 - 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)
  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)
 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)
 146 {
 147   static char buf[100];
 148   int fd;
 149   
 150   // Assumes three file descriptors 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       // Clumsy but will have to do for now.
 162       // Chdir has no effect on the parent if run in the child.
 163       buf[strlen(buf)-1] = 0;  // chop \n
 164       if(chdir(buf+3) < 0)
 165         printf(2, "cannot cd %s\n", buf+3);
 166       continue;
 167     }
 168     if(fork1() == 0)
 169       runcmd(parsecmd(buf));
 170     wait();
 171   }
 172   exit();
 173 }
 174 
 175 void
 176 panic(char *s)
 177 {
 178   printf(2, "%s\n", s);
 179   exit();
 180 }
 181 
 182 int
 183 fork1(void)
 184 {
 185   int pid;
 186   
 187   pid = fork();
 188   if(pid == -1)
 189     panic("fork");
 190   return pid;
 191 }
 192 
 193 //PAGEBREAK!
 194 // Constructors
 195 
 196 struct cmd*
 197 execcmd(void)
 198 {
 199   struct execcmd *cmd;
 200 
 201   cmd = malloc(sizeof(*cmd));
 202   memset(cmd, 0, sizeof(*cmd));
 203   cmd->type = EXEC;
 204   return (struct cmd*)cmd;
 205 }
 206 
 207 struct cmd*
 208 redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
 209 {
 210   struct redircmd *cmd;
 211 
 212   cmd = malloc(sizeof(*cmd));
 213   memset(cmd, 0, sizeof(*cmd));
 214   cmd->type = REDIR;
 215   cmd->cmd = subcmd;
 216   cmd->file = file;
 217   cmd->efile = efile;
 218   cmd->mode = mode;
 219   cmd->fd = fd;
 220   return (struct cmd*)cmd;
 221 }
 222 
 223 struct cmd*
 224 pipecmd(struct cmd *left, struct cmd *right)
 225 {
 226   struct pipecmd *cmd;
 227 
 228   cmd = malloc(sizeof(*cmd));
 229   memset(cmd, 0, sizeof(*cmd));
 230   cmd->type = PIPE;
 231   cmd->left = left;
 232   cmd->right = right;
 233   return (struct cmd*)cmd;
 234 }
 235 
 236 struct cmd*
 237 listcmd(struct cmd *left, struct cmd *right)
 238 {
 239   struct listcmd *cmd;
 240 
 241   cmd = malloc(sizeof(*cmd));
 242   memset(cmd, 0, sizeof(*cmd));
 243   cmd->type = LIST;
 244   cmd->left = left;
 245   cmd->right = right;
 246   return (struct cmd*)cmd;
 247 }
 248 
 249 struct cmd*
 250 backcmd(struct cmd *subcmd)
 251 {
 252   struct backcmd *cmd;
 253 
 254   cmd = malloc(sizeof(*cmd));
 255   memset(cmd, 0, sizeof(*cmd));
 256   cmd->type = BACK;
 257   cmd->cmd = subcmd;
 258   return (struct cmd*)cmd;
 259 }
 260 //PAGEBREAK!
 261 // Parsing
 262 
 263 char whitespace[] = " \t\r\n\v";
 264 char symbols[] = "<|>&;()";
 265 
 266 int
 267 gettoken(char **ps, char *es, char **q, char **eq)
 268 {
 269   char *s;
 270   int ret;
 271   
 272   s = *ps;
 273   while(s < es && strchr(whitespace, *s))
 274     s++;
 275   if(q)
 276     *q = s;
 277   ret = *s;
 278   switch(*s){
 279   case 0:
 280     break;
 281   case '|':
 282   case '(':
 283   case ')':
 284   case ';':
 285   case '&':
 286   case '<':
 287     s++;
 288     break;
 289   case '>':
 290     s++;
 291     if(*s == '>'){
 292       ret = '+';
 293       s++;
 294     }
 295     break;
 296   default:
 297     ret = 'a';
 298     while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
 299       s++;
 300     break;
 301   }
 302   if(eq)
 303     *eq = s;
 304   
 305   while(s < es && strchr(whitespace, *s))
 306     s++;
 307   *ps = s;
 308   return ret;
 309 }
 310 
 311 int
 312 peek(char **ps, char *es, char *toks)
 313 {
 314   char *s;
 315   
 316   s = *ps;
 317   while(s < es && strchr(whitespace, *s))
 318     s++;
 319   *ps = s;
 320   return *s && strchr(toks, *s);
 321 }
 322 
 323 struct cmd *parseline(char**, char*);
 324 struct cmd *parsepipe(char**, char*);
 325 struct cmd *parseexec(char**, char*);
 326 struct cmd *nulterminate(struct cmd*);
 327 
 328 struct cmd*
 329 parsecmd(char *s)
 330 {
 331   char *es;
 332   struct cmd *cmd;
 333 
 334   es = s + strlen(s);
 335   cmd = parseline(&s, es);
 336   peek(&s, es, "");
 337   if(s != es){
 338     printf(2, "leftovers: %s\n", s);
 339     panic("syntax");
 340   }
 341   nulterminate(cmd);
 342   return cmd;
 343 }
 344 
 345 struct cmd*
 346 parseline(char **ps, char *es)
 347 {
 348   struct cmd *cmd;
 349 
 350   cmd = parsepipe(ps, es);
 351   while(peek(ps, es, "&")){
 352     gettoken(ps, es, 0, 0);
 353     cmd = backcmd(cmd);
 354   }
 355   if(peek(ps, es, ";")){
 356     gettoken(ps, es, 0, 0);
 357     cmd = listcmd(cmd, parseline(ps, es));
 358   }
 359   return cmd;
 360 }
 361 
 362 struct cmd*
 363 parsepipe(char **ps, char *es)
 364 {
 365   struct cmd *cmd;
 366 
 367   cmd = parseexec(ps, es);
 368   if(peek(ps, es, "|")){
 369     gettoken(ps, es, 0, 0);
 370     cmd = pipecmd(cmd, parsepipe(ps, es));
 371   }
 372   return cmd;
 373 }
 374 
 375 struct cmd*
 376 parseredirs(struct cmd *cmd, char **ps, char *es)
 377 {
 378   int tok;
 379   char *q, *eq;
 380 
 381   while(peek(ps, es, "<>")){
 382     tok = gettoken(ps, es, 0, 0);
 383     if(gettoken(ps, es, &q, &eq) != 'a')
 384       panic("missing file for redirection");
 385     switch(tok){
 386     case '<':
 387       cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
 388       break;
 389     case '>':
 390       cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
 391       break;
 392     case '+':  // >>
 393       cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
 394       break;
 395     }
 396   }
 397   return cmd;
 398 }
 399 
 400 struct cmd*
 401 parseblock(char **ps, char *es)
 402 {
 403   struct cmd *cmd;
 404 
 405   if(!peek(ps, es, "("))
 406     panic("parseblock");
 407   gettoken(ps, es, 0, 0);
 408   cmd = parseline(ps, es);
 409   if(!peek(ps, es, ")"))
 410     panic("syntax - missing )");
 411   gettoken(ps, es, 0, 0);
 412   cmd = parseredirs(cmd, ps, es);
 413   return cmd;
 414 }
 415 
 416 struct cmd*
 417 parseexec(char **ps, char *es)
 418 {
 419   char *q, *eq;
 420   int tok, argc;
 421   struct execcmd *cmd;
 422   struct cmd *ret;
 423   
 424   if(peek(ps, es, "("))
 425     return parseblock(ps, es);
 426 
 427   ret = execcmd();
 428   cmd = (struct execcmd*)ret;
 429 
 430   argc = 0;
 431   ret = parseredirs(ret, ps, es);
 432   while(!peek(ps, es, "|)&;")){
 433     if((tok=gettoken(ps, es, &q, &eq)) == 0)
 434       break;
 435     if(tok != 'a')
 436       panic("syntax");
 437     cmd->argv[argc] = q;
 438     cmd->eargv[argc] = eq;
 439     argc++;
 440     if(argc >= MAXARGS)
 441       panic("too many args");
 442     ret = parseredirs(ret, ps, es);
 443   }
 444   cmd->argv[argc] = 0;
 445   cmd->eargv[argc] = 0;
 446   return ret;
 447 }
 448 
 449 // NUL-terminate all the counted strings.
 450 struct cmd*
 451 nulterminate(struct cmd *cmd)
 452 {
 453   int i;
 454   struct backcmd *bcmd;
 455   struct execcmd *ecmd;
 456   struct listcmd *lcmd;
 457   struct pipecmd *pcmd;
 458   struct redircmd *rcmd;
 459 
 460   if(cmd == 0)
 461     return 0;
 462   
 463   switch(cmd->type){
 464   case EXEC:
 465     ecmd = (struct execcmd*)cmd;
 466     for(i=0; ecmd->argv[i]; i++)
 467       *ecmd->eargv[i] = 0;
 468     break;
 469 
 470   case REDIR:
 471     rcmd = (struct redircmd*)cmd;
 472     nulterminate(rcmd->cmd);
 473     *rcmd->efile = 0;
 474     break;
 475 
 476   case PIPE:
 477     pcmd = (struct pipecmd*)cmd;
 478     nulterminate(pcmd->left);
 479     nulterminate(pcmd->right);
 480     break;
 481     
 482   case LIST:
 483     lcmd = (struct listcmd*)cmd;
 484     nulterminate(lcmd->left);
 485     nulterminate(lcmd->right);
 486     break;
 487 
 488   case BACK:
 489     bcmd = (struct backcmd*)cmd;
 490     nulterminate(bcmd->cmd);
 491     break;
 492   }
 493   return cmd;
 494 }