1 /* sh - simple shell - great for early stages of porting */
5 #define MAXLINE 200 /* maximum line length */
6 #define WORD 256 /* token code for words */
7 #define EOF -1 /* token code for end of file */
8 #define ispunct(c) (c=='|' || c=='&' || c==';' || c=='<' || \
9 c=='>' || c=='(' || c==')' || c=='\n')
10 #define isspace(c) (c==' ' || c=='\t')
11 #define execute(np) (ignored = (np? (*(np)->op)(np) : 0))
13 typedef struct Node Node;
14 struct Node{ /* parse tree node */
15 int (*op)(Node *); /* operator function */
16 Node *args[2]; /* argument nodes */
17 char *argv[100]; /* argument pointers */
18 char *io[3]; /* i/o redirection */
21 Node nodes[25]; /* node pool */
22 Node *nfree; /* next available node */
23 char strspace[10*MAXLINE]; /* string storage */
24 char *sfree; /* next free character in strspace */
25 int t; /* current token code */
26 char *token; /* current token text (in strspace) */
27 int putback = 0; /* lookahead */
28 char status[256]; /* exit status of most recent command */
29 int cflag = 0; /* command is argument to sh */
30 int tflag = 0; /* read only one line */
31 int interactive = 0; /* prompt */
32 char *cflagp; /* command line for cflag */
33 char *path[] ={"/bin", 0};
36 Node *alloc(int (*op)(Node *));
37 int builtin(Node *np);
42 void error(char *s, char *t);
44 void redirect(Node *np);
47 int xpipeline(Node *np);
48 int xsimple(Node *np);
49 int xsubshell(Node *np);
50 int xnowait(Node *np);
54 main(int argc, char *argv[])
58 if(argc>1 && strcmp(argv[1], "-t")==0)
60 else if(argc>2 && strcmp(argv[1], "-c")==0){
65 if(open(argv[1], 0) != 0){
66 error(": can't open", argv[1]);
73 fprint(2, "%d$ ", getpid());
76 if((t=gettoken()) == EOF)
82 error("syntax error", "");
83 while(t!=EOF && t!='\n') /* flush syntax errors */
89 /* alloc - allocate for op and return a node */
91 alloc(int (*op)(Node *))
93 if(nfree < nodes+sizeof(nodes)){
95 nfree->args[0] = nfree->args[1] = 0;
96 nfree->argv[0] = nfree->argv[1] = 0;
97 nfree->io[0] = nfree->io[1] = nfree->io[2] = 0;
100 error("node storage overflow", "");
101 exits("node storage overflow");
105 /* builtin - check np for builtin command and, if found, execute it */
114 n = strtoul(np->argv[1], 0, 0);
115 if(strcmp(np->argv[0], "cd") == 0){
116 if(chdir(np->argv[1]? np->argv[1] : "/") == -1)
117 error(": bad directory", np->argv[0]);
119 }else if(strcmp(np->argv[0], "exit") == 0)
120 exits(np->argv[1]? np->argv[1] : status);
121 else if(strcmp(np->argv[0], "bind") == 0){
122 if(np->argv[1]==0 || np->argv[2]==0)
123 error("usage: bind new old", "");
124 else if(bind(np->argv[1], np->argv[2], 0)==-1)
125 error("bind failed", "");
128 }else if(strcmp(np->argv[0], "unmount") == 0){
130 error("usage: unmount [new] old", "");
131 else if(np->argv[2] == 0){
132 if(unmount((char *)0, np->argv[1]) == -1)
133 error("unmount:", "");
134 }else if(unmount(np->argv[1], np->argv[2]) == -1)
135 error("unmount", "");
138 }else if(strcmp(np->argv[0], "wait") == 0){
139 while((wmsg = wait()) != nil){
140 strncpy(status, wmsg->msg, sizeof(status)-1);
141 if(n && wmsg->pid==n){
149 error("wait error", "");
151 }else if(strcmp(np->argv[0], "rfork") == 0){
156 if(p == 0 || *p == 0)
162 case 'n': mask |= RFNAMEG; break;
163 case 'N': mask |= RFCNAMEG; break;
164 case 'e': mask |= RFENVG; break;
165 case 'E': mask |= RFCENVG; break;
166 case 's': mask |= RFNOTEG; break;
167 case 'f': mask |= RFFDG; break;
168 case 'F': mask |= RFCFDG; break;
169 case 'm': mask |= RFNOMNT; break;
170 default: error(np->argv[1], "bad rfork flag");
175 }else if(strcmp(np->argv[0], "exec") == 0){
177 if(np->argv[1] == (char *) 0)
179 exec(np->argv[1], &np->argv[1]);
181 if(n!='/' && n!='#' && (n!='.' || np->argv[1][1]!='/'))
182 for(n = 0; path[n]; n++){
183 sprint(name, "%s/%s", path[n], np->argv[1]);
184 exec(name, &np->argv[1]);
186 error(": not found", np->argv[1]);
192 /* command - ( list ) [ ( < | > | >> ) word ]* | simple */
200 np = alloc(xsubshell);
202 if((np->args[0]=list())==0 || t!=')')
204 while((t=gettoken())=='<' || t=='>')
210 /* getch - get next, possibly pushed back, input character */
221 if(done || read(0, &c, 1)!=1){
230 if((c=*cflagp++) == 0){
234 }else if(read(0, &c, 1) != 1)
239 /* gettoken - get next token into string space, return token code */
245 while((c = getch()) != EOF)
248 if(c==EOF || ispunct(c))
252 if(sfree >= strspace+sizeof(strspace) - 1){
253 error("string storage overflow", "");
254 exits("string storage overflow");
257 }while((c=getch()) != EOF && !ispunct(c) && !isspace(c));
263 /* list - pipeline ( ( ; | & ) pipeline )* [ ; | & ] (not LL(1), but ok) */
270 if((np->args[1]=pipeline()) == 0)
272 while(t==';' || t=='&'){
273 np->op = (t==';')? xwait : xnowait;
275 if(t==')' || t=='\n') /* tests ~first(pipeline) */
279 if((np1->args[1]=pipeline()) == 0)
288 /* error - print error message s, prefixed by t */
290 error(char *s, char *t)
294 fprint(2, "%s%s", t, s);
295 errstr(buf, sizeof buf);
296 fprint(2, ": %s\n", buf);
299 /* pipeline - command ( | command )* */
305 if((np=command()) == 0)
308 np1 = alloc(xpipeline);
311 if((np1->args[1]=command()) == 0)
318 /* redirect - redirect i/o according to np->io[] values */
325 if((fd = open(np->io[0], 0)) < 0){
326 error(": can't open", np->io[0]);
333 if((fd = create(np->io[1], 1, 0666L)) < 0){
334 error(": can't create", np->io[1]);
341 if((fd = open(np->io[2], 1)) < 0 && (fd = create(np->io[2], 1, 0666L)) < 0){
342 error(": can't write", np->io[2]);
351 /* setio - ( < | > | >> ) word; fill in np->io[] */
372 /* simple - word ( [ < | > | >> ] word )* */
383 while((t = gettoken())==WORD || t=='<' || t=='>')
385 np->argv[n++] = token;
392 /* xpipeline - execute cmd | cmd */
399 error("can't create pipe", "");
402 if((pid=fork()) == 0){ /* left side; redirect stdout */
406 execute(np->args[0]);
409 error("can't create process", "");
412 if((pid=fork()) == 0){ /* right side; redirect stdin */
416 pid = execute(np->args[1]); /*BUG: this is wrong sometimes*/
418 while(waitpid()!=pid)
422 error("can't create process", "");
425 close(fd[0]); /* avoid using up fd's */
430 /* xsimple - execute a simple command */
441 error(": can't create process", np->argv[0]);
444 redirect(np); /* child process */
445 exec(np->argv[0], &np->argv[0]);
447 if(i!='/' && i!='#' && (i!='.' || np->argv[0][1]!='/'))
448 for(i = 0; path[i]; i++){
449 sprint(name, "%s/%s", path[i], np->argv[0]);
450 exec(name, &np->argv[0]);
452 error(": not found", np->argv[0]);
454 return -1; // suppress compiler warnings
457 /* xsubshell - execute (cmd) */
465 error("can't create process", "");
468 redirect(np); /* child process */
469 execute(np->args[0]);
471 return -1; // suppress compiler warnings
474 /* xnowait - execute cmd & */
480 execute(np->args[0]);
481 pid = execute(np->args[1]);
483 fprint(2, "%d\n", pid);
487 /* xwait - execute cmd ; */
493 execute(np->args[0]);
494 pid = execute(np->args[1]);
496 while((wmsg = wait()) != nil){
502 error("wait error", "");
504 strncpy(status, wmsg->msg, sizeof(status)-1);