xv6

port of xv6 to x86-64
git clone http://frotz.net/git/xv6.git
Log | Files | Refs | README | LICENSE

sh.c (8307B)


      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 }