summaryrefslogtreecommitdiff
path: root/sh.c
diff options
context:
space:
mode:
authorrsc <rsc>2007-08-28 03:28:29 +0000
committerrsc <rsc>2007-08-28 03:28:29 +0000
commit1b25f3b057fb61f0f099922058b52478fb1c98db (patch)
tree4f92e5f7f159399f1e2df54bae303ce48c390cad /sh.c
parentf0d11fea8251ef959cf1197b62e523922855df3a (diff)
downloadxv6-labs-1b25f3b057fb61f0f099922058b52478fb1c98db.tar.gz
xv6-labs-1b25f3b057fb61f0f099922058b52478fb1c98db.tar.bz2
xv6-labs-1b25f3b057fb61f0f099922058b52478fb1c98db.zip
New shell.
Diffstat (limited to 'sh.c')
-rw-r--r--sh.c677
1 files changed, 406 insertions, 271 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
+#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;
+ }
+}