diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/sh.C |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/sh.C')
-rwxr-xr-x | sys/src/cmd/sh.C | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/sys/src/cmd/sh.C b/sys/src/cmd/sh.C new file mode 100755 index 000000000..0a7eb6ba7 --- /dev/null +++ b/sys/src/cmd/sh.C @@ -0,0 +1,509 @@ +/* sh - simple shell - great for early stages of porting */ +#include "u.h" +#include "libc.h" + +#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) (ignored = (np? (*(np)->op)(np) : 0)) + +typedef struct Node Node; +struct Node{ /* parse tree node */ + int (*op)(Node *); /* operator function */ + Node *args[2]; /* argument nodes */ + char *argv[100]; /* argument pointers */ + char *io[3]; /* i/o redirection */ +}; + +Node nodes[25]; /* node pool */ +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 */ +char status[256]; /* exit status of most recent command */ +int cflag = 0; /* command is argument to sh */ +int tflag = 0; /* read only one line */ +int interactive = 0; /* prompt */ +char *cflagp; /* command line for cflag */ +char *path[] ={"/bin", 0}; +int ignored; + +Node *alloc(int (*op)(Node *)); +int builtin(Node *np); +Node *command(void); +int getch(void); +int gettoken(void); +Node *list(void); +void error(char *s, char *t); +Node *pipeline(void); +void redirect(Node *np); +int setio(Node *np); +Node *simple(void); +int xpipeline(Node *np); +int xsimple(Node *np); +int xsubshell(Node *np); +int xnowait(Node *np); +int xwait(Node *np); + +void +main(int argc, char *argv[]) +{ + Node *np; + + if(argc>1 && strcmp(argv[1], "-t")==0) + tflag++; + else if(argc>2 && strcmp(argv[1], "-c")==0){ + cflag++; + cflagp = argv[2]; + }else if(argc>1){ + close(0); + if(open(argv[1], 0) != 0){ + error(": can't open", argv[1]); + exits("argument"); + } + }else + interactive = 1; + for(;;){ + if(interactive) + fprint(2, "%d$ ", getpid()); + nfree = nodes; + sfree = strspace; + if((t=gettoken()) == EOF) + break; + if(t != '\n') + if(np = list()) + execute(np); + else + error("syntax error", ""); + while(t!=EOF && t!='\n') /* flush syntax errors */ + t = gettoken(); + } + exits(status); +} + +/* alloc - allocate for op and return a node */ +Node* +alloc(int (*op)(Node *)) +{ + 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++; + } + error("node storage overflow", ""); + exits("node storage overflow"); + return nil; +} + +/* builtin - check np for builtin command and, if found, execute it */ +int +builtin(Node *np) +{ + int n = 0; + char name[MAXLINE]; + Waitmsg *wmsg; + + if(np->argv[1]) + n = strtoul(np->argv[1], 0, 0); + if(strcmp(np->argv[0], "cd") == 0){ + if(chdir(np->argv[1]? np->argv[1] : "/") == -1) + error(": bad directory", np->argv[0]); + return 1; + }else if(strcmp(np->argv[0], "exit") == 0) + exits(np->argv[1]? np->argv[1] : status); + else if(strcmp(np->argv[0], "bind") == 0){ + if(np->argv[1]==0 || np->argv[2]==0) + error("usage: bind new old", ""); + else if(bind(np->argv[1], np->argv[2], 0)==-1) + error("bind failed", ""); + return 1; +#ifdef asdf + }else if(strcmp(np->argv[0], "unmount") == 0){ + if(np->argv[1] == 0) + error("usage: unmount [new] old", ""); + else if(np->argv[2] == 0){ + if(unmount((char *)0, np->argv[1]) == -1) + error("unmount:", ""); + }else if(unmount(np->argv[1], np->argv[2]) == -1) + error("unmount", ""); + return 1; +#endif + }else if(strcmp(np->argv[0], "wait") == 0){ + while((wmsg = wait()) != nil){ + strncpy(status, wmsg->msg, sizeof(status)-1); + if(n && wmsg->pid==n){ + n = 0; + free(wmsg); + break; + } + free(wmsg); + } + if(n) + error("wait error", ""); + return 1; + }else if(strcmp(np->argv[0], "rfork") == 0){ + char *p; + int mask; + + p = np->argv[1]; + if(p == 0 || *p == 0) + p = "ens"; + mask = 0; + + while(*p) + switch(*p++){ + case 'n': mask |= RFNAMEG; break; + case 'N': mask |= RFCNAMEG; break; + case 'e': mask |= RFENVG; break; + case 'E': mask |= RFCENVG; break; + case 's': mask |= RFNOTEG; break; + case 'f': mask |= RFFDG; break; + case 'F': mask |= RFCFDG; break; + case 'm': mask |= RFNOMNT; break; + default: error(np->argv[1], "bad rfork flag"); + } + rfork(mask); + + return 1; + }else if(strcmp(np->argv[0], "exec") == 0){ + redirect(np); + if(np->argv[1] == (char *) 0) + return 1; + exec(np->argv[1], &np->argv[1]); + n = np->argv[1][0]; + if(n!='/' && n!='#' && (n!='.' || np->argv[1][1]!='/')) + for(n = 0; path[n]; n++){ + sprint(name, "%s/%s", path[n], np->argv[1]); + exec(name, &np->argv[1]); + } + error(": not found", np->argv[1]); + return 1; + } + return 0; +} + +/* command - ( list ) [ ( < | > | >> ) word ]* | simple */ +Node* +command(void) +{ + Node *np; + + if(t != '(') + return simple(); + np = alloc(xsubshell); + t = gettoken(); + if((np->args[0]=list())==0 || t!=')') + return 0; + while((t=gettoken())=='<' || t=='>') + if(!setio(np)) + return 0; + return np; +} + +/* getch - get next, possibly pushed back, input character */ +int +getch(void) +{ + unsigned char c; + static done=0; + + if(putback){ + c = putback; + putback = 0; + }else if(tflag){ + if(done || read(0, &c, 1)!=1){ + done = 1; + return EOF; + } + if(c == '\n') + done = 1; + }else if(cflag){ + if(done) + return EOF; + if((c=*cflagp++) == 0){ + done = 1; + c = '\n'; + } + }else if(read(0, &c, 1) != 1) + return EOF; + return c; +} + +/* gettoken - get next token into string space, return token code */ +int +gettoken(void) +{ + int c; + + while((c = getch()) != EOF) + if(!isspace(c)) + break; + if(c==EOF || ispunct(c)) + return c; + token = sfree; + do{ + if(sfree >= strspace+sizeof(strspace) - 1){ + error("string storage overflow", ""); + exits("string storage overflow"); + } + *sfree++ = c; + }while((c=getch()) != EOF && !ispunct(c) && !isspace(c)); + *sfree++ = 0; + putback = c; + return WORD; +} + +/* list - pipeline ( ( ; | & ) pipeline )* [ ; | & ] (not LL(1), but ok) */ +Node* +list(void) +{ + Node *np, *np1; + + np = alloc(0); + if((np->args[1]=pipeline()) == 0) + return 0; + while(t==';' || t=='&'){ + np->op = (t==';')? xwait : xnowait; + t = gettoken(); + if(t==')' || t=='\n') /* tests ~first(pipeline) */ + break; + np1 = alloc(0); + np1->args[0] = np; + if((np1->args[1]=pipeline()) == 0) + return 0; + np = np1; + } + if(np->op == 0) + np->op = xwait; + return np; +} + +/* error - print error message s, prefixed by t */ +void +error(char *s, char *t) +{ + char buf[256]; + + fprint(2, "%s%s", t, s); + errstr(buf, sizeof buf); + fprint(2, ": %s\n", buf); +} + +/* pipeline - command ( | command )* */ +Node* +pipeline(void) +{ + Node *np, *np1; + + if((np=command()) == 0) + return 0; + while(t == '|'){ + np1 = alloc(xpipeline); + np1->args[0] = np; + t = gettoken(); + if((np1->args[1]=command()) == 0) + return 0; + np = np1; + } + return np; +} + +/* redirect - redirect i/o according to np->io[] values */ +void +redirect(Node *np) +{ + int fd; + + if(np->io[0]){ + if((fd = open(np->io[0], 0)) < 0){ + error(": can't open", np->io[0]); + exits("open"); + } + dup(fd, 0); + close(fd); + } + if(np->io[1]){ + if((fd = create(np->io[1], 1, 0666L)) < 0){ + error(": can't create", np->io[1]); + exits("create"); + } + dup(fd, 1); + close(fd); + } + if(np->io[2]){ + if((fd = open(np->io[2], 1)) < 0 && (fd = create(np->io[2], 1, 0666L)) < 0){ + error(": can't write", np->io[2]); + exits("write"); + } + dup(fd, 1); + close(fd); + seek(1, 0, 2); + } +} + +/* setio - ( < | > | >> ) word; fill in np->io[] */ +int +setio(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 )* */ +Node* +simple(void) +{ + 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; +} + +/* xpipeline - execute cmd | cmd */ +int +xpipeline(Node *np) +{ + int pid, fd[2]; + + if(pipe(fd) < 0){ + error("can't create pipe", ""); + return 0; + } + if((pid=fork()) == 0){ /* left side; redirect stdout */ + dup(fd[1], 1); + close(fd[0]); + close(fd[1]); + execute(np->args[0]); + exits(status); + }else if(pid == -1){ + error("can't create process", ""); + return 0; + } + if((pid=fork()) == 0){ /* right side; redirect stdin */ + dup(fd[0], 0); + close(fd[0]); + close(fd[1]); + pid = execute(np->args[1]); /*BUG: this is wrong sometimes*/ + if(pid > 0) + while(waitpid()!=pid) + ; + exits(0); + }else if(pid == -1){ + error("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(Node *np) +{ + char name[MAXLINE]; + int pid, i; + + if(builtin(np)) + return 0; + if(pid = fork()){ + if(pid == -1) + error(": can't create process", np->argv[0]); + return pid; + } + redirect(np); /* child process */ + exec(np->argv[0], &np->argv[0]); + i = np->argv[0][0]; + if(i!='/' && i!='#' && (i!='.' || np->argv[0][1]!='/')) + for(i = 0; path[i]; i++){ + sprint(name, "%s/%s", path[i], np->argv[0]); + exec(name, &np->argv[0]); + } + error(": not found", np->argv[0]); + exits("not found"); + return -1; // suppress compiler warnings +} + +/* xsubshell - execute (cmd) */ +int +xsubshell(Node *np) +{ + int pid; + + if(pid = fork()){ + if(pid == -1) + error("can't create process", ""); + return pid; + } + redirect(np); /* child process */ + execute(np->args[0]); + exits(status); + return -1; // suppress compiler warnings +} + +/* xnowait - execute cmd & */ +int +xnowait(Node *np) +{ + int pid; + + execute(np->args[0]); + pid = execute(np->args[1]); + if(interactive) + fprint(2, "%d\n", pid); + return 0; +} + +/* xwait - execute cmd ; */ +int xwait(Node *np) +{ + int pid; + Waitmsg *wmsg; + + execute(np->args[0]); + pid = execute(np->args[1]); + if(pid > 0){ + while((wmsg = wait()) != nil){ + if(wmsg->pid == pid) + break; + free(wmsg); + } + if(wmsg == nil) + error("wait error", ""); + else { + strncpy(status, wmsg->msg, sizeof(status)-1); + free(wmsg); + } + } + return 0; +} |