]> Devi Nivas Git - cs3210-lab0.git/commitdiff
New shell.
authorrsc <rsc>
Tue, 28 Aug 2007 03:28:29 +0000 (03:28 +0000)
committerrsc <rsc>
Tue, 28 Aug 2007 03:28:29 +0000 (03:28 +0000)
sh.c

diff --git a/sh.c b/sh.c
index 205b4456b0c83647fac722b25bdb4d666d2a22c3..6fe8754f7a98ceacf2b7e01b2b827ab7a99a7179 100644 (file)
--- a/sh.c
+++ b/sh.c
+// 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;
+  }
+}