/* sh - simple shell */ #define MAXLINE 200 /* maximum line length */ #define WORD 256 /* token code for words */ #define EOF -1 /* token code for end of file */ #define ispunct(c) (c=='|' || c=='&' || c==';' || c=='<' || \ c=='>' || c=='(' || c==')' || c=='\n') #define isspace(c) (c==' ' || c=='\t') #define execute(np) (np ? (*(np)->op)(np) : 0) struct node { /* parse tree node */ int (*op)(); /* operator function */ struct node *args[2]; /* argument nodes */ char *argv[100]; /* argument pointers */ char *io[3]; /* i/o redirection */ } nodes[100]; /* node pool */ struct node *nfree; /* next available node */ char strspace[10*MAXLINE]; /* string storage */ char *sfree; /* next free character in strspace */ int t; /* current token code */ char *token; /* current token text (in strspace) */ int putback = 0; /* lookahead */ int status = 0; /* exit status of most recent command */ char **env; /* environment of caller */ char *path[] = {".", "/bin", "/usr/bin", "/usr/ucb", 0}; extern int errno; struct node *alloc(), *list(), *command(), *simple(), *pipeline(); int xsimple(), xpipeline(), xwait(), xnowait(), xsubshell(); main(argc, argv, envp) int argc; char *argv[], *envp[]; { struct node *np; env = envp; for (;;) { write(2, "$ ", 2); nfree = nodes; sfree = strspace; if ((t = gettoken()) == EOF) break; if (t != '\n') if (np = list()) execute(np); else perror("syntax error", ""); while (t != EOF && t != '\n') /* flush syntax errors */ t = gettoken(); } exit(status); } /* command - ( list ) [ ( < | > | >> ) word ]* | simple */ struct node *command() { struct node *np; if (t != '(') return simple(); np = alloc(xsubshell); t = gettoken(); if (!(np->args[0] = list()) || t != ')') return 0; while ((t = gettoken()) == '<' || t == '>') if (!setio(np)) return 0; return np; } /* list - pipeline ( ( ; | & ) pipeline )* [ ; | & ] (not LL(1), but ok) */ struct node *list() { struct node *np, *np1; np = alloc((int (*)()) 0); if (!(np->args[1] = pipeline())) return 0; while (t == ';' || t == '&') { np->op = (t == ';') ? xwait : xnowait; t = gettoken(); if (t == ')' || t == '\n') /* tests ~first(pipeline) */ break; np1 = alloc((int (*)()) 0); np1->args[0] = np; if (!(np1->args[1] = pipeline())) return 0; np = np1; } if (np->op == 0) np->op = xwait; return np; } /* pipeline - command ( | command )* */ struct node *pipeline() { struct node *np, *np1; if (!(np = command())) return 0; while (t == '|') { np1 = alloc(xpipeline); np1->args[0] = np; t = gettoken(); if (!(np1->args[1] = command())) return 0; np = np1; } return np; } /* redirect - redirect i/o according to np->io[] values */ redirect(np) struct node *np; { if (np->io[0]) { close(0); if (open(np->io[0], 0) != 0) { perror(": can't open", np->io[0]); exit(1); } } if (np->io[1]) { close(1); if (creat(np->io[1], 0644) != 1) { perror(": can't create", np->io[1]); exit(1); } } if (np->io[2]) { close(1); if (open(np->io[2], 1) != 1 && creat(np->io[2], 0644) != 1) { perror(": can't write", np->io[2]); exit(1); } lseek(1, 0L, 2); } } /* setio - ( < | > | >> ) word; fill in np->io[] */ int setio(np) struct node *np; { if (t == '<') { t = gettoken(); np->io[0] = token; } else if (t == '>') { t = gettoken(); if (t == '>') { t = gettoken(); np->io[2] = token; } else np->io[1] = token; } else return 0; if (t != WORD) return 0; return 1; } /* simple - word ( [ < | > | >> ] word )* */ struct node *simple() { struct node *np; int n = 1; if (t != WORD) return 0; np = alloc(xsimple); np->argv[0] = token; while ((t = gettoken()) == WORD || t == '<' || t == '>') if (t == WORD) np->argv[n++] = token; else if (!setio(np)) return 0; np->argv[n] = 0; return np; } /* strcmp - compare s1 and s2 */ int strcmp(s1, s2) char *s1, *s2; { while (*s1 == *s2++) if (*s1++ == 0) return 0; return *s1 - *--s2; } /* builtin - check np for builtin command and, if found, execute it */ int builtin(np) struct node *np; { int pid, n = 0; char *s, name[MAXLINE]; if (s = np->argv[1]) while (*s >= '0' && *s <= '9') n = 10*n + *s++ - '0'; if (strcmp(np->argv[0], "cd") == 0) { if (np->argv[1] && chdir(np->argv[1]) == -1) perror(": bad directory", np->argv[0]); return 1; } else if (strcmp(np->argv[0], "exit") == 0) exit(np->argv[1] ? n : status); else if (strcmp(np->argv[0], "wait") == 0) { if (n > 0) while ((pid = wait(&status)) > 0 && pid != n) ; else wait(0); return 1; } else if (strcmp(np->argv[0], "exec") == 0) { redirect(np); if (np->argv[1] == (char *) 0) return 1; for (n = 0; path[n]; n++) { sprintf(name, "%s/%s", path[n], np->argv[1]); execve(name, &np->argv[1], env); } perror(": not found", np->argv[1]); return 1; } return 0; } /* xpipeline - execute cmd | cmd */ int xpipeline(np) struct node *np; { int pid, n, fd[2]; if (pipe(fd) < 0) { perror("can't create pipe", ""); return 0; } if ((pid = fork()) == 0) { /* left side; redirect stdout */ close(1); dup(fd[1]); close(fd[0]); close(fd[1]); execute(np->args[0]); exit(status); } else if (pid == -1) { perror("can't create process", ""); return 0; } if ((pid = fork()) == 0) { /* right side; redirect stdin */ close(0); dup(fd[0]); close(fd[0]); close(fd[1]); if ((pid = execute(np->args[1])) > 0) { while ((n = wait(&status)) > 0 && pid != n) ; if (n == -1) perror("wait error", ""); } exit(status); } else if (pid == -1) { perror("can't create process", ""); return 0; } close(fd[0]); /* avoid using up fd's */ close(fd[1]); return pid; } /* xsimple - execute a simple command */ int xsimple(np) struct node *np; { char name[MAXLINE]; int pid, i; if (builtin(np)) return 0; if (pid = fork()) { if (pid == -1) perror(": can't create process", np->argv[0]); return pid; } redirect(np); /* child process */ if (*np->argv[0] == '/' || *np->argv[0] == '.') execve(np->argv[0], np->argv, env); else for (i = 0; path[i]; i++) { sprintf(name, "%s/%s", path[i], np->argv[0]); execve(name, np->argv, env); } perror(": not found", np->argv[0]); exit(2); } /* xsubshell - execute (cmd) */ int xsubshell(np) struct node *np; { int pid; if (pid = fork()) { if (pid == -1) perror("can't create process", ""); return pid; } redirect(np); /* child process */ execute(np->args[0]); exit(status); } /* xnowait - execute cmd & */ int xnowait(np) struct node *np; { char *s, buf[MAXLINE]; execute(np->args[0]); sprintf(s = buf, "%5d\n", execute(np->args[1])); while (*s == ' ') s++; write(2, s, buf + 6 - s); return 0; } /* xwait - execute cmd ; */ int xwait(np) struct node *np; { int n, pid; execute(np->args[0]); if ((pid = execute(np->args[1])) > 0) { while ((n = wait(&status)) > 0 && n != pid) ; if (n == -1) perror("wait error", ""); } return 0; } /* getc - get next, possibly pushed back, input character */ int getc() { char c; if (putback) { c = putback; putback = 0; } else if (read(0, &c, 1) != 1) return EOF; return c; } /* gettoken - get next token into string space, return token code */ int gettoken() { int c; while ((c = getc()) != EOF) if (!isspace(c)) break; if (c == EOF || ispunct(c)) return c; token = sfree; do { if (sfree >= strspace + sizeof(strspace) - 1) { perror("string storage overflow", ""); exit(2); } *sfree++ = c; } while ((c = getc()) != EOF && !ispunct(c) && !isspace(c)); *sfree++ = 0; putback = c; return WORD; } /* alloc - allocate for op and return a node */ struct node *alloc(op) int (*op)(); { if (nfree < nodes + sizeof(nodes)) { nfree->op = op; nfree->args[0] = nfree->args[1] = 0; nfree->argv[0] = nfree->argv[1] = 0; nfree->io[0] = nfree->io[1] = nfree->io[2] = 0; return nfree++; } perror("node storage overflow", ""); exit(2); } /* perror - print error message s, prefixed by t */ perror(s, t) char *s, *t; { char buf[MAXLINE]; if (errno > 0) sprintf(buf, "%s%s (%d)\n", t, s, errno); else sprintf(buf, "%s%s\n", t, s); for (s = buf; *s; s++) ; write(2, buf, s - buf); errno = 0; }