From 1b25f3b057fb61f0f099922058b52478fb1c98db Mon Sep 17 00:00:00 2001 From: rsc Date: Tue, 28 Aug 2007 03:28:29 +0000 Subject: [PATCH] New shell. --- sh.c | 679 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 407 insertions(+), 272 deletions(-) diff --git a/sh.c b/sh.c index 205b445..6fe8754 100644 --- a/sh.c +++ b/sh.c @@ -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 - -// some day a real parse tree; for now ad-hoc -struct ionode { - int token; - char *s; -}; +#define MAXARGS 10 struct cmd { - char *argv[MAXARGS]; - char argv0buf[BUFSIZ]; - int argc; - int token; - struct ionode iolist[MAXIO]; - struct ionode *io; + int type; }; -struct cmd cmdlist[MAXCMD]; -struct cmd *cmd; -char buf[BUFSIZ]; -int debug; +struct execcmd { + int type; + char *argv[MAXARGS]; + char *eargv[MAXARGS]; +}; -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 redircmd { + int type; + struct cmd *cmd; + char *file; + char *efile; + int mode; + int fd; +}; + +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; + static char buf[100]; - gettoken(s, 0); - - cmd = &cmdlist[0]; - for(i = 0; i < MAXCMD; i++) { - cmdlist[i].argc = 0; - cmdlist[i].token = 0; - cmdlist[i].io = cmdlist[i].iolist; - } - 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; - - 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; - - case ';': // command sequence - case '|': // pipe - if(cmd->io - cmd->iolist >= MAXIO) { - printf(2, "too many redirections\n"); - return -1; - } - cmd->token = c; - cmd++; - break; - - case 0: // String is complete - return 0; - - default: - printf(2, "syntax error: bad return %d from gettoken", c); - return -1; - - } + while(getcmd(buf, sizeof(buf)) >= 0) { + if(fork1() == 0) + runcmd(parsecmd(buf)); + wait(); } + exit(); } void -runcmd(void) +panic(char *s) { - int i, r, pid, tfd; - int fdarray[2]; - struct cmd *c; - struct ionode *io; + printf(2, "%s\n", s); + exit(); +} - // Return immediately if command line was empty. - if(cmdlist[0].argc == 0) { - if(debug) - printf(2, "EMPTY COMMAND\n"); - return; - } +// Constructors - 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; +struct cmd* +execcmd(void) +{ + struct execcmd *cmd; - // 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"); - } + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = EXEC; + 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* +redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd) +{ + struct redircmd *cmd; - if(c->token == '|') - if(pipe(fdarray) < 0) - printf(2, "cmd %d pipe failed\n", c); + 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; +} - 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); +struct cmd* +pipecmd(struct cmd *left, struct cmd *right) +{ + struct pipecmd *cmd; - 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 = PIPE; + cmd->left = left; + cmd->right = right; + return (struct cmd*)cmd; +} + +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; +} + +struct cmd* +backcmd(struct cmd *subcmd) +{ + struct backcmd *cmd; + + 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 +peek(char **ps, char *es, char *toks) +{ + char *s; + + s = *ps; + while(s < es && strchr(whitespace, *s)) + s++; + *ps = s; + return *s && strchr(toks, *s); } int -ioredirection(struct ionode *iolist, int nio) +gettoken(char **ps, char *es, char **q, char **eq) { - int fd; - struct ionode *io; + 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; + } + if(eq) + *eq = s; + + while(s < es && strchr(whitespace, *s)) + s++; + *ps = s; + return ret; +} - for(io = iolist; io < &iolist[nio]; io++) { - switch(io->token) { +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*); + +struct cmd* +parsecmd(char *s) +{ + 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"); + } + nulterminate(cmd); + return cmd; +} + +struct cmd* +parseline(char **ps, char *es) +{ + struct cmd *cmd; + + 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; +} + +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; +} + +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; +} + +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 '<': - 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); + cmd = redircmd(cmd, q, eq, O_RDONLY, 0); 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); + cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); + break; + case '+': // >> + cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); break; } } - return 0; + return cmd; } -// 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. - -int -gettoken(char *s, char **p1) +struct cmd* +parseexec(char **ps, char *es) { - static int c, nc; - static char *np1, *np2; + char *q, *eq; + int tok, argc; + struct execcmd *cmd; + struct cmd *ret; + + if(peek(ps, es, "(")) + return parseblock(ps, es); - if(s) { - nc = _gettoken(s, &np1, &np2); - return 0; + 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); } - c = nc; - *p1 = np1; - nc = _gettoken(np2, &np1, &np2); - return c; + cmd->argv[argc] = 0; + cmd->eargv[argc] = 0; + return ret; } - -// 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) +// NUL-terminate all the counted strings. +void +nulterminate(struct cmd *cmd) { - int t; + int i; + struct backcmd *bcmd; + struct execcmd *ecmd; + struct listcmd *lcmd; + struct pipecmd *pcmd; + struct redircmd *rcmd; - if(s == 0) { - if(debug > 1) - printf(2, "GETTOKEN 0\n"); - return 0; - } + 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; - if(debug > 1) - printf(2, "GETTOKEN: %s\n", s); + case REDIR: + rcmd = (struct redircmd*)cmd; + nulterminate(rcmd->cmd); + *rcmd->efile = 0; + break; - *p1 = 0; - *p2 = 0; + 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; - while(strchr(WHITESPACE, *s)) - *s++ = 0; - if(*s == 0) { - if(debug > 1) - printf(2, "EOL\n"); - return 0; + case BACK: + bcmd = (struct backcmd*)cmd; + nulterminate(bcmd->cmd); + break; } - if(strchr(SYMBOLS, *s)) { - t = *s; - *p1 = s; - *s++ = 0; - *p2 = s; - if(debug > 1) - printf(2, "TOK %c\n", t); - return t; - } - *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 'w'; } -