user/sh.c

499 lines
1// Shell.23#include "kernel/types.h"4#include "user/user.h"5#include "kernel/fcntl.h"67// Parsed command representation8#define EXEC  19#define REDIR 210#define PIPE  311#define LIST  412#define BACK  51314#define MAXARGS 101516struct cmd {17  int type;18};1920struct execcmd {21  int type;22  char *argv[MAXARGS];23  char *eargv[MAXARGS];24};2526struct redircmd {27  int type;28  struct cmd *cmd;29  char *file;30  char *efile;31  int mode;32  int fd;33};3435struct pipecmd {36  int type;37  struct cmd *left;38  struct cmd *right;39};4041struct listcmd {42  int type;43  struct cmd *left;44  struct cmd *right;45};4647struct backcmd {48  int type;49  struct cmd *cmd;50};5152int fork1(void);  // Fork but panics on failure.53void panic(char*);54struct cmd *parsecmd(char*);55void runcmd(struct cmd*) __attribute__((noreturn));5657// Execute cmd.  Never returns.58void59runcmd(struct cmd *cmd)60{61  int p[2];62  struct backcmd *bcmd;63  struct execcmd *ecmd;64  struct listcmd *lcmd;65  struct pipecmd *pcmd;66  struct redircmd *rcmd;6768  if(cmd == 0)69    exit(1);7071  switch(cmd->type){72  default:73    panic("runcmd");7475  case EXEC:76    ecmd = (struct execcmd*)cmd;77    if(ecmd->argv[0] == 0)78      exit(1);79    exec(ecmd->argv[0], ecmd->argv);80    fprintf(2, "exec %s failed\n", ecmd->argv[0]);81    break;8283  case REDIR:84    rcmd = (struct redircmd*)cmd;85    close(rcmd->fd);86    if(open(rcmd->file, rcmd->mode) < 0){87      fprintf(2, "open %s failed\n", rcmd->file);88      exit(1);89    }90    runcmd(rcmd->cmd);91    break;9293  case LIST:94    lcmd = (struct listcmd*)cmd;95    if(fork1() == 0)96      runcmd(lcmd->left);97    wait(0);98    runcmd(lcmd->right);99    break;100101  case PIPE:102    pcmd = (struct pipecmd*)cmd;103    if(pipe(p) < 0)104      panic("pipe");105    if(fork1() == 0){106      close(1);107      dup(p[1]);108      close(p[0]);109      close(p[1]);110      runcmd(pcmd->left);111    }112    if(fork1() == 0){113      close(0);114      dup(p[0]);115      close(p[0]);116      close(p[1]);117      runcmd(pcmd->right);118    }119    close(p[0]);120    close(p[1]);121    wait(0);122    wait(0);123    break;124125  case BACK:126    bcmd = (struct backcmd*)cmd;127    if(fork1() == 0)128      runcmd(bcmd->cmd);129    break;130  }131  exit(0);132}133134int135getcmd(char *buf, int nbuf)136{137  write(2, "$ ", 2);138  memset(buf, 0, nbuf);139  gets(buf, nbuf);140  if(buf[0] == 0) // EOF141    return -1;142  return 0;143}144145int146main(void)147{148  static char buf[100];149  int fd;150151  // Ensure that three file descriptors are open.152  while((fd = open("console", O_RDWR)) >= 0){153    if(fd >= 3){154      close(fd);155      break;156    }157  }158159  // Read and run input commands.160  while(getcmd(buf, sizeof(buf)) >= 0){161    char *cmd = buf;162    while (*cmd == ' ' || *cmd == '\t')163      cmd++;164    if (*cmd == '\n') // is a blank command165      continue;166    if(cmd[0] == 'c' && cmd[1] == 'd' && cmd[2] == ' '){167      // Chdir must be called by the parent, not the child.168      cmd[strlen(cmd)-1] = 0;  // chop \n169      if(chdir(cmd+3) < 0)170        fprintf(2, "cannot cd %s\n", cmd+3);171    } else {172      if(fork1() == 0)173        runcmd(parsecmd(cmd));174      wait(0);175    }176  }177  exit(0);178}179180void181panic(char *s)182{183  fprintf(2, "%s\n", s);184  exit(1);185}186187int188fork1(void)189{190  int pid;191192  pid = fork();193  if(pid == -1)194    panic("fork");195  return pid;196}197198//PAGEBREAK!199// Constructors200201struct cmd*202execcmd(void)203{204  struct execcmd *cmd;205206  cmd = malloc(sizeof(*cmd));207  memset(cmd, 0, sizeof(*cmd));208  cmd->type = EXEC;209  return (struct cmd*)cmd;210}211212struct cmd*213redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)214{215  struct redircmd *cmd;216217  cmd = malloc(sizeof(*cmd));218  memset(cmd, 0, sizeof(*cmd));219  cmd->type = REDIR;220  cmd->cmd = subcmd;221  cmd->file = file;222  cmd->efile = efile;223  cmd->mode = mode;224  cmd->fd = fd;225  return (struct cmd*)cmd;226}227228struct cmd*229pipecmd(struct cmd *left, struct cmd *right)230{231  struct pipecmd *cmd;232233  cmd = malloc(sizeof(*cmd));234  memset(cmd, 0, sizeof(*cmd));235  cmd->type = PIPE;236  cmd->left = left;237  cmd->right = right;238  return (struct cmd*)cmd;239}240241struct cmd*242listcmd(struct cmd *left, struct cmd *right)243{244  struct listcmd *cmd;245246  cmd = malloc(sizeof(*cmd));247  memset(cmd, 0, sizeof(*cmd));248  cmd->type = LIST;249  cmd->left = left;250  cmd->right = right;251  return (struct cmd*)cmd;252}253254struct cmd*255backcmd(struct cmd *subcmd)256{257  struct backcmd *cmd;258259  cmd = malloc(sizeof(*cmd));260  memset(cmd, 0, sizeof(*cmd));261  cmd->type = BACK;262  cmd->cmd = subcmd;263  return (struct cmd*)cmd;264}265//PAGEBREAK!266// Parsing267268char whitespace[] = " \t\r\n\v";269char symbols[] = "<|>&;()";270271int272gettoken(char **ps, char *es, char **q, char **eq)273{274  char *s;275  int ret;276277  s = *ps;278  while(s < es && strchr(whitespace, *s))279    s++;280  if(q)281    *q = s;282  ret = *s;283  switch(*s){284  case 0:285    break;286  case '|':287  case '(':288  case ')':289  case ';':290  case '&':291  case '<':292    s++;293    break;294  case '>':295    s++;296    if(*s == '>'){297      ret = '+';298      s++;299    }300    break;301  default:302    ret = 'a';303    while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))304      s++;305    break;306  }307  if(eq)308    *eq = s;309310  while(s < es && strchr(whitespace, *s))311    s++;312  *ps = s;313  return ret;314}315316int317peek(char **ps, char *es, char *toks)318{319  char *s;320321  s = *ps;322  while(s < es && strchr(whitespace, *s))323    s++;324  *ps = s;325  return *s && strchr(toks, *s);326}327328struct cmd *parseline(char**, char*);329struct cmd *parsepipe(char**, char*);330struct cmd *parseexec(char**, char*);331struct cmd *nulterminate(struct cmd*);332333struct cmd*334parsecmd(char *s)335{336  char *es;337  struct cmd *cmd;338339  es = s + strlen(s);340  cmd = parseline(&s, es);341  peek(&s, es, "");342  if(s != es){343    fprintf(2, "leftovers: %s\n", s);344    panic("syntax");345  }346  nulterminate(cmd);347  return cmd;348}349350struct cmd*351parseline(char **ps, char *es)352{353  struct cmd *cmd;354355  cmd = parsepipe(ps, es);356  while(peek(ps, es, "&")){357    gettoken(ps, es, 0, 0);358    cmd = backcmd(cmd);359  }360  if(peek(ps, es, ";")){361    gettoken(ps, es, 0, 0);362    cmd = listcmd(cmd, parseline(ps, es));363  }364  return cmd;365}366367struct cmd*368parsepipe(char **ps, char *es)369{370  struct cmd *cmd;371372  cmd = parseexec(ps, es);373  if(peek(ps, es, "|")){374    gettoken(ps, es, 0, 0);375    cmd = pipecmd(cmd, parsepipe(ps, es));376  }377  return cmd;378}379380struct cmd*381parseredirs(struct cmd *cmd, char **ps, char *es)382{383  int tok;384  char *q, *eq;385386  while(peek(ps, es, "<>")){387    tok = gettoken(ps, es, 0, 0);388    if(gettoken(ps, es, &q, &eq) != 'a')389      panic("missing file for redirection");390    switch(tok){391    case '<':392      cmd = redircmd(cmd, q, eq, O_RDONLY, 0);393      break;394    case '>':395      cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE|O_TRUNC, 1);396      break;397    case '+':  // >>398      cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);399      break;400    }401  }402  return cmd;403}404405struct cmd*406parseblock(char **ps, char *es)407{408  struct cmd *cmd;409410  if(!peek(ps, es, "("))411    panic("parseblock");412  gettoken(ps, es, 0, 0);413  cmd = parseline(ps, es);414  if(!peek(ps, es, ")"))415    panic("syntax - missing )");416  gettoken(ps, es, 0, 0);417  cmd = parseredirs(cmd, ps, es);418  return cmd;419}420421struct cmd*422parseexec(char **ps, char *es)423{424  char *q, *eq;425  int tok, argc;426  struct execcmd *cmd;427  struct cmd *ret;428429  if(peek(ps, es, "("))430    return parseblock(ps, es);431432  ret = execcmd();433  cmd = (struct execcmd*)ret;434435  argc = 0;436  ret = parseredirs(ret, ps, es);437  while(!peek(ps, es, "|)&;")){438    if((tok=gettoken(ps, es, &q, &eq)) == 0)439      break;440    if(tok != 'a')441      panic("syntax");442    cmd->argv[argc] = q;443    cmd->eargv[argc] = eq;444    argc++;445    if(argc >= MAXARGS)446      panic("too many args");447    ret = parseredirs(ret, ps, es);448  }449  cmd->argv[argc] = 0;450  cmd->eargv[argc] = 0;451  return ret;452}453454// NUL-terminate all the counted strings.455struct cmd*456nulterminate(struct cmd *cmd)457{458  int i;459  struct backcmd *bcmd;460  struct execcmd *ecmd;461  struct listcmd *lcmd;462  struct pipecmd *pcmd;463  struct redircmd *rcmd;464465  if(cmd == 0)466    return 0;467468  switch(cmd->type){469  case EXEC:470    ecmd = (struct execcmd*)cmd;471    for(i=0; ecmd->argv[i]; i++)472      *ecmd->eargv[i] = 0;473    break;474475  case REDIR:476    rcmd = (struct redircmd*)cmd;477    nulterminate(rcmd->cmd);478    *rcmd->efile = 0;479    break;480481  case PIPE:482    pcmd = (struct pipecmd*)cmd;483    nulterminate(pcmd->left);484    nulterminate(pcmd->right);485    break;486487  case LIST:488    lcmd = (struct listcmd*)cmd;489    nulterminate(lcmd->left);490    nulterminate(lcmd->right);491    break;492493  case BACK:494    bcmd = (struct backcmd*)cmd;495    nulterminate(bcmd->cmd);496    break;497  }498  return cmd;499}500