diff options
| -rw-r--r-- | sh.c | 677 | 
1 files changed, 406 insertions, 271 deletions
| @@ -1,48 +1,141 @@ +// Shell. +  #include "types.h" -#include "stat.h"  #include "user.h"  #include "fcntl.h" -#define BUFSIZ  512 -#define MAXARGS  10 -#define MAXIO 2 -#define MAXCMD  2 +// Parsed command representation +#define EXEC  1 +#define REDIR 2 +#define PIPE  3 +#define LIST  4 +#define BACK  5 -// an embarrassingly naive shell +#define MAXARGS 10 -// some day a real parse tree; for now ad-hoc -struct ionode { -  int token; -  char *s; +struct cmd { +  int type;  }; -struct cmd { +struct execcmd { +  int type;    char *argv[MAXARGS]; -  char argv0buf[BUFSIZ]; -  int argc; -  int token; -  struct ionode iolist[MAXIO]; -  struct ionode *io; +  char *eargv[MAXARGS];  }; -struct cmd cmdlist[MAXCMD]; -struct cmd *cmd; -char buf[BUFSIZ]; -int debug; +struct redircmd { +  int type; +  struct cmd *cmd; +  char *file; +  char *efile; +  int mode; +  int fd; +}; -int parse(char *s); -void runcmd(void); -int getcmd(char *buf, int nbuf); -int ioredirection(struct ionode *iolist, int nio); -int gettoken(char *s, char **token); -int _gettoken(char *s, char **p1, char **p2); +struct pipecmd { +  int type; +  struct cmd *left; +  struct cmd *right; +}; + +struct listcmd { +  int type; +  struct cmd *left; +  struct cmd *right; +}; + +struct backcmd { +  int type; +  struct cmd *cmd; +}; + +struct cmd *parsecmd(char*); +void panic(char*);  int -main(void) +fork1(void)  { -  while(getcmd(buf, sizeof(buf)) >= 0) { -    if(parse(buf) >= 0) -      runcmd(); +  int pid; +   +  pid = fork(); +  if(pid == -1) +    panic("fork"); +  return pid; +} + +// Execute cmd.  Never returns. +void +runcmd(struct cmd *cmd) +{ +  int p[2]; +  struct backcmd *bcmd; +  struct execcmd *ecmd; +  struct listcmd *lcmd; +  struct pipecmd *pcmd; +  struct redircmd *rcmd; + +  if(cmd == 0) +    return; +   +  switch(cmd->type){ +  default: +    panic("runcmd"); + +  case EXEC: +    ecmd = (struct execcmd*)cmd; +    if(ecmd->argv[0] == 0) +      exit(); +    exec(ecmd->argv[0], ecmd->argv); +    printf(2, "exec %s failed\n", ecmd->argv[0]); +    break; + +  case REDIR: +    rcmd = (struct redircmd*)cmd; +    close(rcmd->fd); +    if(open(rcmd->file, rcmd->mode) < 0){ +      printf(2, "open %s failed\n", rcmd->file); +      exit(); +    } +    runcmd(rcmd->cmd); +    break; + +  case PIPE: +    pcmd = (struct pipecmd*)cmd; +    if(pipe(p) < 0) +      panic("pipe"); +    if(fork1() == 0){ +      close(1); +      dup(p[1]); +      close(p[0]); +      close(p[1]); +      runcmd(pcmd->left); +    } +    if(fork1() == 0){ +      close(0); +      dup(p[0]); +      close(p[0]); +      close(p[1]); +      runcmd(pcmd->right); +    } +    close(p[0]); +    close(p[1]); +    wait(); +    wait(); +    break; +     +  case LIST: +    lcmd = (struct listcmd*)cmd; +    if(fork1() == 0) +      runcmd(lcmd->left); +    wait(); +    runcmd(lcmd->right); +    break; + +  case BACK: +    bcmd = (struct backcmd*)cmd; +    if(fork1() == 0) +      runcmd(bcmd->cmd); +    break;    }    exit();  } @@ -59,282 +152,324 @@ getcmd(char *buf, int nbuf)  }  int -parse(char *s) +main(void)  { -  char *t; -  int c, i; - -  gettoken(s, 0); +  static char buf[100]; -  cmd = &cmdlist[0]; -  for(i = 0; i < MAXCMD; i++) { -    cmdlist[i].argc = 0; -    cmdlist[i].token = 0; -    cmdlist[i].io = cmdlist[i].iolist; +  while(getcmd(buf, sizeof(buf)) >= 0) { +    if(fork1() == 0) +      runcmd(parsecmd(buf)); +    wait();    } -  for(;;){ -    switch((c = gettoken(0, &t))) { - -    case 'w':   // Add an argument -      if(cmd->argc >= MAXARGS) { -        printf(2, "too many arguments\n"); -        return -1; -      } -      cmd->argv[cmd->argc++] = t; -      break; +  exit(); +} -    case '>':   // Input and output redirection -    case '<': -      // Grab the filename from the argument list -      if(gettoken(0, &t) != 'w') { -        printf(2, "syntax error: > not followed by word\n"); -        return -1; -      } -      if(cmd->io - cmd->iolist >= MAXIO) { -        printf(2, "too many redirections\n"); -        return -1; -      } -      cmd->io->token = c; -      cmd->io->s = t; -      cmd->io++; -      break; +void +panic(char *s) +{ +  printf(2, "%s\n", s); +  exit(); +} -    case ';':  // command sequence -    case '|':  // pipe -      if(cmd->io - cmd->iolist >= MAXIO) { -        printf(2, "too many redirections\n"); -        return -1; -      } -      cmd->token = c; -      cmd++; -      break; +// Constructors -    case 0:             // String is complete -      return 0; +struct cmd* +execcmd(void) +{ +  struct execcmd *cmd; -    default: -      printf(2, "syntax error: bad return %d from gettoken", c); -      return -1; +  cmd = malloc(sizeof(*cmd)); +  memset(cmd, 0, sizeof(*cmd)); +  cmd->type = EXEC; +  return (struct cmd*)cmd; +} -    } -  } +struct cmd* +redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd) +{ +  struct redircmd *cmd; + +  cmd = malloc(sizeof(*cmd)); +  memset(cmd, 0, sizeof(*cmd)); +  cmd->type = REDIR; +  cmd->cmd = subcmd; +  cmd->file = file; +  cmd->efile = efile; +  cmd->mode = mode; +  cmd->fd = fd; +  return (struct cmd*)cmd;  } -void -runcmd(void) +struct cmd* +pipecmd(struct cmd *left, struct cmd *right)  { -  int i, r, pid, tfd; -  int fdarray[2]; -  struct cmd *c; -  struct ionode *io; - -  // Return immediately if command line was empty. -  if(cmdlist[0].argc == 0) { -    if(debug) -      printf(2, "EMPTY COMMAND\n"); -    return; -  } +  struct pipecmd *cmd; + +  cmd = malloc(sizeof(*cmd)); +  memset(cmd, 0, sizeof(*cmd)); +  cmd->type = PIPE; +  cmd->left = left; +  cmd->right = right; +  return (struct cmd*)cmd; +} -  for(c = &cmdlist[0]; c <= cmd; c++) { -    // Clean up command line. -    // Read all commands from the filesystem: add an initial '/' to -    // the command name. -    // This essentially acts like 'PATH=/'. -    if(c->argv[0][0] != '/') { -      c->argv0buf[0] = '/'; -      strcpy(c->argv0buf + 1, c->argv[0]); -      c->argv[0] = c->argv0buf; -    } -    c->argv[c->argc] = 0; - -    // Print the command. -    if(debug) { -      printf(2, "[%d] SPAWN:", getpid()); -      for(i = 0; c->argv[i]; i++) -        printf(2, " %s", c->argv[i]); -      for(io = c->iolist; io <= c->io; io++) { -        printf(2, "%c %s", io->token, io->s); -      } -      printf(2, "\n"); -    } +struct cmd* +listcmd(struct cmd *left, struct cmd *right) +{ +  struct listcmd *cmd; + +  cmd = malloc(sizeof(*cmd)); +  memset(cmd, 0, sizeof(*cmd)); +  cmd->type = LIST; +  cmd->left = left; +  cmd->right = right; +  return (struct cmd*)cmd; +} -    if(strcmp(c->argv[0], "/cd") == 0) { -      if(debug) -        printf (2, "/cd %s is build in\n", c->argv[1]); -      chdir(c->argv[1]); -      return; -    } +struct cmd* +backcmd(struct cmd *subcmd) +{ +  struct backcmd *cmd; -    if(c->token == '|') -      if(pipe(fdarray) < 0) -        printf(2, "cmd %d pipe failed\n", c); - -    pid = fork(); -    if(pid == 0) { -      if(c->token == '|') { -        if(close(1) < 0) -          printf(2, "close 1 failed\n"); -        if((tfd = dup(fdarray[1])) < 0) -          printf(2, "dup failed\n"); -        if(close(fdarray[0]) < 0) -          printf(2, "close fdarray[0] failed\n"); -        if(close(fdarray[1]) < 0) -          printf(2, "close fdarray[1] failed\n"); -      } -      if(c > cmdlist && (c-1)->token == '|') { -        if(close(0) < 0) -          printf(2, "close 0 failed\n"); -        if((tfd = dup(fdarray[0])) < 0) -          printf(2, "dup failed\n"); -        if(close(fdarray[0]) < 0) -          printf(2, "close fdarray[0] failed\n"); -        if(close(fdarray[1]) < 0) -          printf(2, "close fdarray[1] failed\n"); -      } -      if(ioredirection(c->iolist, c->io - c->iolist) < 0) -        exit(); -      if((r = exec(c->argv0buf, (char**) c->argv)) < 0) { -        printf(2, "exec %s: %d\n", c->argv[0], r); -        exit(); -      } -    } else if(pid > 0) { -      int p; -      if(debug) -        printf(2, "[%d] FORKED child %d\n", getpid(), pid); - -      if(c > cmdlist && (c-1)->token == '|') { -        close(fdarray[0]); -        close(fdarray[1]); -      } -      if(c->token != '|') { -        if(debug) -          printf(2, "[%d] WAIT for children\n", getpid()); -        do { -          p = wait(); -          if(debug) -            printf(2, "[%d] WAIT child %d finished\n", getpid(), p); -        } while(p > 0); -        if(debug) -          printf(2, "[%d] wait finished\n", getpid()); -      } -    } -  } +  cmd = malloc(sizeof(*cmd)); +  memset(cmd, 0, sizeof(*cmd)); +  cmd->type = BACK; +  cmd->cmd = subcmd; +  return (struct cmd*)cmd;  } +// Parsing + +char whitespace[] = " \t\r\n\v"; +char symbols[] = "<|>&;()"; +  int -ioredirection(struct ionode *iolist, int nio) +peek(char **ps, char *es, char *toks)  { -  int fd; -  struct ionode *io; +  char *s; +   +  s = *ps; +  while(s < es && strchr(whitespace, *s)) +    s++; +  *ps = s; +  return *s && strchr(toks, *s); +} -  for(io = iolist; io < &iolist[nio]; io++) { -    switch(io->token) { -    case '<': -      if(close(0) < 0) -        printf(2, "close 0 failed\n"); -      if((fd = open(io->s, O_RDONLY)) < 0) { -        printf(2, "failed to open %s for read: %d", io->s, fd); -        return -1; -      } -      if(debug) -        printf(2, "redirect 0 from %s\n", io->s); -      break; -    case '>': -      if(close(1) < 0) -        printf(2, "close 1 failed\n"); -      if((fd = open(io->s, O_WRONLY|O_CREATE)) < 0) { -        printf(2, "failed to open %s for write: %d", io->s, fd); -        exit(); -      } -      if(debug) -        printf(2, "redirect 1 to %s\n", io->s); -      break; +int +gettoken(char **ps, char *es, char **q, char **eq) +{ +  char *s; +  int ret; +   +  s = *ps; +  while(s < es && strchr(whitespace, *s)) +    s++; +  if(q) +    *q = s; +  ret = *s; +  switch(*s){ +  case 0: +    break; +  case '|': +  case '(': +  case ')': +  case ';': +  case '&': +  case '<': +    s++; +    break; +  case '>': +    s++; +    if(*s == '>'){ +      ret = '+'; +      s++;      } +    break; +  default: +    ret = 'a'; +    while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) +      s++; +    break;    } -  return 0; +  if(eq) +    *eq = s; +   +  while(s < es && strchr(whitespace, *s)) +    s++; +  *ps = s; +  return ret;  } -// gettoken(s, 0) prepares gettoken for subsequent calls and returns 0. -// gettoken(0, token) parses a shell token from the previously set string, -// null-terminates that token, stores the token pointer in '*token', -// and returns a token ID (0, '<', '>', '|', or 'w'). -// Subsequent calls to 'gettoken(0, token)' will return subsequent -// tokens from the string. +void nulterminate(struct cmd*); +struct cmd *parseline(char**, char*); +struct cmd *parsepipe(char**, char*); +struct cmd *parseredirs(struct cmd*, char**, char*); +struct cmd *parseblock(char**, char*); +struct cmd *parseexec(char**, char*); -int -gettoken(char *s, char **p1) +struct cmd* +parsecmd(char *s)  { -  static int c, nc; -  static char *np1, *np2; - -  if(s) { -    nc = _gettoken(s, &np1, &np2); -    return 0; +  char *es; +  struct cmd *cmd; +   +  es = s + strlen(s); +  cmd = parseline(&s, es); +  peek(&s, es, ""); +  if(s != es){ +    printf(2, "leftovers: %s\n", s); +    panic("syntax");    } -  c = nc; -  *p1 = np1; -  nc = _gettoken(np2, &np1, &np2); -  return c; +  nulterminate(cmd); +  return cmd;  } - -// Get the next token from string s. -// Set *p1 to the beginning of the token and *p2 just past the token. -// Returns -//      0 for end-of-string; -//      < for <; -//      > for >; -//      | for |; -//      w for a word. -// -// Eventually (once we parse the space where the \0 will go), -// words get nul-terminated. -#define WHITESPACE " \t\r\n" -#define SYMBOLS "<|>&;()" - -int -_gettoken(char *s, char **p1, char **p2) +struct cmd* +parseline(char **ps, char *es)  { -  int t; +  struct cmd *cmd; -  if(s == 0) { -    if(debug > 1) -      printf(2, "GETTOKEN 0\n"); -    return 0; +  cmd = parsepipe(ps, es); +  while(peek(ps, es, "&")){ +    gettoken(ps, es, 0, 0); +    cmd = backcmd(cmd);    } +  if(peek(ps, es, ";")){ +    gettoken(ps, es, 0, 0); +    cmd = listcmd(cmd, parseline(ps, es)); +  } +  return cmd; +} -  if(debug > 1) -    printf(2, "GETTOKEN: %s\n", s); +struct cmd* +parsepipe(char **ps, char *es) +{ +  struct cmd *cmd; +   +  cmd = parseexec(ps, es); +  if(peek(ps, es, "|")){ +    gettoken(ps, es, 0, 0); +    cmd = pipecmd(cmd, parsepipe(ps, es)); +  } +  return cmd; +} -  *p1 = 0; -  *p2 = 0; +struct cmd* +parseblock(char **ps, char *es) +{ +  struct cmd *cmd; + +  if(!peek(ps, es, "(")) +    panic("parseblock"); +  gettoken(ps, es, 0, 0); +  cmd = parseline(ps, es); +  if(!peek(ps, es, ")")) +    panic("syntax - missing )"); +  gettoken(ps, es, 0, 0); +  cmd = parseredirs(cmd, ps, es); +  return cmd; +} -  while(strchr(WHITESPACE, *s)) -    *s++ = 0; -  if(*s == 0) { -    if(debug > 1) -      printf(2, "EOL\n"); -    return 0; -  } -  if(strchr(SYMBOLS, *s)) { -    t = *s; -    *p1 = s; -    *s++ = 0; -    *p2 = s; -    if(debug > 1) -      printf(2, "TOK %c\n", t); -    return t; +struct cmd* +parseredirs(struct cmd *cmd, char **ps, char *es) +{ +  int tok; +  char *q, *eq; + +  while(peek(ps, es, "<>")){ +    tok = gettoken(ps, es, 0, 0); +    if(gettoken(ps, es, &q, &eq) != 'a') +      panic("missing file for redirection"); +    switch(tok){ +    case '<': +      cmd = redircmd(cmd, q, eq, O_RDONLY, 0); +      break; +    case '>': +      cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); +      break; +    case '+':  // >> +      cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); +      break; +    }    } -  *p1 = s; -  while(*s && !strchr(WHITESPACE SYMBOLS, *s)) -    s++; -  *p2 = s; -  if(debug > 1) { -    t = **p2; -    **p2 = 0; -    printf(2, "WORD: %s\n", *p1); -    **p2 = t; +  return cmd; +} + +struct cmd* +parseexec(char **ps, char *es) +{ +  char *q, *eq; +  int tok, argc; +  struct execcmd *cmd; +  struct cmd *ret; +   +  if(peek(ps, es, "(")) +    return parseblock(ps, es); + +  ret = execcmd(); +  cmd = (struct execcmd*)ret; + +  argc = 0; +  ret = parseredirs(ret, ps, es); +  while(!peek(ps, es, "|)&;")){ +    if((tok=gettoken(ps, es, &q, &eq)) == 0) +      break; +    if(tok != 'a') +      panic("syntax"); +    cmd->argv[argc] = q; +    cmd->eargv[argc] = eq; +    argc++; +    if(argc >= MAXARGS) +      panic("too many args"); +    ret = parseredirs(ret, ps, es);    } -  return 'w'; +  cmd->argv[argc] = 0; +  cmd->eargv[argc] = 0; +  return ret;  } +// NUL-terminate all the counted strings. +void +nulterminate(struct cmd *cmd) +{ +  int i; +  struct backcmd *bcmd; +  struct execcmd *ecmd; +  struct listcmd *lcmd; +  struct pipecmd *pcmd; +  struct redircmd *rcmd; + +  if(cmd == 0) +    return; +   +  switch(cmd->type){ +  case EXEC: +    ecmd = (struct execcmd*)cmd; +    for(i=0; ecmd->argv[i]; i++) +      *ecmd->eargv[i] = 0; +    break; + +  case REDIR: +    rcmd = (struct redircmd*)cmd; +    nulterminate(rcmd->cmd); +    *rcmd->efile = 0; +    break; + +  case PIPE: +    pcmd = (struct pipecmd*)cmd; +    nulterminate(pcmd->left); +    nulterminate(pcmd->right); +    break; +     +  case LIST: +    lcmd = (struct listcmd*)cmd; +    nulterminate(lcmd->left); +    nulterminate(lcmd->right); +    break; + +  case BACK: +    bcmd = (struct backcmd*)cmd; +    nulterminate(bcmd->cmd); +    break; +  } +} | 
