1 /* sh - simple shell - great for early stages of porting */
2 #include "u.h"
3 #include "libc.h"
4
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))
12
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 */
19 };
20
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};
34 int ignored;
35
36 Node *alloc(int (*op)(Node *));
37 int builtin(Node *np);
38 Node *command(void);
39 int getch(void);
40 int gettoken(void);
41 Node *list(void);
42 void error(char *s, char *t);
43 Node *pipeline(void);
44 void redirect(Node *np);
45 int setio(Node *np);
46 Node *simple(void);
47 int xpipeline(Node *np);
48 int xsimple(Node *np);
49 int xsubshell(Node *np);
50 int xnowait(Node *np);
51 int xwait(Node *np);
52
53 void
main(int argc,char * argv[])54 main(int argc, char *argv[])
55 {
56 Node *np;
57
58 if(argc>1 && strcmp(argv[1], "-t")==0)
59 tflag++;
60 else if(argc>2 && strcmp(argv[1], "-c")==0){
61 cflag++;
62 cflagp = argv[2];
63 }else if(argc>1){
64 close(0);
65 if(open(argv[1], 0) != 0){
66 error(": can't open", argv[1]);
67 exits("argument");
68 }
69 }else
70 interactive = 1;
71 for(;;){
72 if(interactive)
73 fprint(2, "%d$ ", getpid());
74 nfree = nodes;
75 sfree = strspace;
76 if((t=gettoken()) == EOF)
77 break;
78 if(t != '\n')
79 if(np = list())
80 execute(np);
81 else
82 error("syntax error", "");
83 while(t!=EOF && t!='\n') /* flush syntax errors */
84 t = gettoken();
85 }
86 exits(status);
87 }
88
89 /* alloc - allocate for op and return a node */
90 Node*
alloc(int (* op)(Node *))91 alloc(int (*op)(Node *))
92 {
93 if(nfree < nodes+sizeof(nodes)){
94 nfree->op = op;
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;
98 return nfree++;
99 }
100 error("node storage overflow", "");
101 exits("node storage overflow");
102 return nil;
103 }
104
105 /* builtin - check np for builtin command and, if found, execute it */
106 int
builtin(Node * np)107 builtin(Node *np)
108 {
109 int n = 0;
110 char name[MAXLINE];
111 Waitmsg *wmsg;
112
113 if(np->argv[1])
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]);
118 return 1;
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", "");
126 return 1;
127 #ifdef asdf
128 }else if(strcmp(np->argv[0], "unmount") == 0){
129 if(np->argv[1] == 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", "");
136 return 1;
137 #endif
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){
142 n = 0;
143 free(wmsg);
144 break;
145 }
146 free(wmsg);
147 }
148 if(n)
149 error("wait error", "");
150 return 1;
151 }else if(strcmp(np->argv[0], "rfork") == 0){
152 char *p;
153 int mask;
154
155 p = np->argv[1];
156 if(p == 0 || *p == 0)
157 p = "ens";
158 mask = 0;
159
160 while(*p)
161 switch(*p++){
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");
171 }
172 rfork(mask);
173
174 return 1;
175 }else if(strcmp(np->argv[0], "exec") == 0){
176 redirect(np);
177 if(np->argv[1] == (char *) 0)
178 return 1;
179 exec(np->argv[1], &np->argv[1]);
180 n = np->argv[1][0];
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]);
185 }
186 error(": not found", np->argv[1]);
187 return 1;
188 }
189 return 0;
190 }
191
192 /* command - ( list ) [ ( < | > | >> ) word ]* | simple */
193 Node*
command(void)194 command(void)
195 {
196 Node *np;
197
198 if(t != '(')
199 return simple();
200 np = alloc(xsubshell);
201 t = gettoken();
202 if((np->args[0]=list())==0 || t!=')')
203 return 0;
204 while((t=gettoken())=='<' || t=='>')
205 if(!setio(np))
206 return 0;
207 return np;
208 }
209
210 /* getch - get next, possibly pushed back, input character */
211 int
getch(void)212 getch(void)
213 {
214 unsigned char c;
215 static done=0;
216
217 if(putback){
218 c = putback;
219 putback = 0;
220 }else if(tflag){
221 if(done || read(0, &c, 1)!=1){
222 done = 1;
223 return EOF;
224 }
225 if(c == '\n')
226 done = 1;
227 }else if(cflag){
228 if(done)
229 return EOF;
230 if((c=*cflagp++) == 0){
231 done = 1;
232 c = '\n';
233 }
234 }else if(read(0, &c, 1) != 1)
235 return EOF;
236 return c;
237 }
238
239 /* gettoken - get next token into string space, return token code */
240 int
gettoken(void)241 gettoken(void)
242 {
243 int c;
244
245 while((c = getch()) != EOF)
246 if(!isspace(c))
247 break;
248 if(c==EOF || ispunct(c))
249 return c;
250 token = sfree;
251 do{
252 if(sfree >= strspace+sizeof(strspace) - 1){
253 error("string storage overflow", "");
254 exits("string storage overflow");
255 }
256 *sfree++ = c;
257 }while((c=getch()) != EOF && !ispunct(c) && !isspace(c));
258 *sfree++ = 0;
259 putback = c;
260 return WORD;
261 }
262
263 /* list - pipeline ( ( ; | & ) pipeline )* [ ; | & ] (not LL(1), but ok) */
264 Node*
list(void)265 list(void)
266 {
267 Node *np, *np1;
268
269 np = alloc(0);
270 if((np->args[1]=pipeline()) == 0)
271 return 0;
272 while(t==';' || t=='&'){
273 np->op = (t==';')? xwait : xnowait;
274 t = gettoken();
275 if(t==')' || t=='\n') /* tests ~first(pipeline) */
276 break;
277 np1 = alloc(0);
278 np1->args[0] = np;
279 if((np1->args[1]=pipeline()) == 0)
280 return 0;
281 np = np1;
282 }
283 if(np->op == 0)
284 np->op = xwait;
285 return np;
286 }
287
288 /* error - print error message s, prefixed by t */
289 void
error(char * s,char * t)290 error(char *s, char *t)
291 {
292 char buf[256];
293
294 fprint(2, "%s%s", t, s);
295 errstr(buf, sizeof buf);
296 fprint(2, ": %s\n", buf);
297 }
298
299 /* pipeline - command ( | command )* */
300 Node*
pipeline(void)301 pipeline(void)
302 {
303 Node *np, *np1;
304
305 if((np=command()) == 0)
306 return 0;
307 while(t == '|'){
308 np1 = alloc(xpipeline);
309 np1->args[0] = np;
310 t = gettoken();
311 if((np1->args[1]=command()) == 0)
312 return 0;
313 np = np1;
314 }
315 return np;
316 }
317
318 /* redirect - redirect i/o according to np->io[] values */
319 void
redirect(Node * np)320 redirect(Node *np)
321 {
322 int fd;
323
324 if(np->io[0]){
325 if((fd = open(np->io[0], 0)) < 0){
326 error(": can't open", np->io[0]);
327 exits("open");
328 }
329 dup(fd, 0);
330 close(fd);
331 }
332 if(np->io[1]){
333 if((fd = create(np->io[1], 1, 0666L)) < 0){
334 error(": can't create", np->io[1]);
335 exits("create");
336 }
337 dup(fd, 1);
338 close(fd);
339 }
340 if(np->io[2]){
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]);
343 exits("write");
344 }
345 dup(fd, 1);
346 close(fd);
347 seek(1, 0, 2);
348 }
349 }
350
351 /* setio - ( < | > | >> ) word; fill in np->io[] */
352 int
setio(Node * np)353 setio(Node *np)
354 {
355 if(t == '<'){
356 t = gettoken();
357 np->io[0] = token;
358 }else if(t == '>'){
359 t = gettoken();
360 if(t == '>'){
361 t = gettoken();
362 np->io[2] = token;
363 }else
364 np->io[1] = token;
365 }else
366 return 0;
367 if(t != WORD)
368 return 0;
369 return 1;
370 }
371
372 /* simple - word ( [ < | > | >> ] word )* */
373 Node*
simple(void)374 simple(void)
375 {
376 Node *np;
377 int n = 1;
378
379 if(t != WORD)
380 return 0;
381 np = alloc(xsimple);
382 np->argv[0] = token;
383 while((t = gettoken())==WORD || t=='<' || t=='>')
384 if(t == WORD)
385 np->argv[n++] = token;
386 else if(!setio(np))
387 return 0;
388 np->argv[n] = 0;
389 return np;
390 }
391
392 /* xpipeline - execute cmd | cmd */
393 int
xpipeline(Node * np)394 xpipeline(Node *np)
395 {
396 int pid, fd[2];
397
398 if(pipe(fd) < 0){
399 error("can't create pipe", "");
400 return 0;
401 }
402 if((pid=fork()) == 0){ /* left side; redirect stdout */
403 dup(fd[1], 1);
404 close(fd[0]);
405 close(fd[1]);
406 execute(np->args[0]);
407 exits(status);
408 }else if(pid == -1){
409 error("can't create process", "");
410 return 0;
411 }
412 if((pid=fork()) == 0){ /* right side; redirect stdin */
413 dup(fd[0], 0);
414 close(fd[0]);
415 close(fd[1]);
416 pid = execute(np->args[1]); /*BUG: this is wrong sometimes*/
417 if(pid > 0)
418 while(waitpid()!=pid)
419 ;
420 exits(0);
421 }else if(pid == -1){
422 error("can't create process", "");
423 return 0;
424 }
425 close(fd[0]); /* avoid using up fd's */
426 close(fd[1]);
427 return pid;
428 }
429
430 /* xsimple - execute a simple command */
431 int
xsimple(Node * np)432 xsimple(Node *np)
433 {
434 char name[MAXLINE];
435 int pid, i;
436
437 if(builtin(np))
438 return 0;
439 if(pid = fork()){
440 if(pid == -1)
441 error(": can't create process", np->argv[0]);
442 return pid;
443 }
444 redirect(np); /* child process */
445 exec(np->argv[0], &np->argv[0]);
446 i = np->argv[0][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]);
451 }
452 error(": not found", np->argv[0]);
453 exits("not found");
454 return -1; // suppress compiler warnings
455 }
456
457 /* xsubshell - execute (cmd) */
458 int
xsubshell(Node * np)459 xsubshell(Node *np)
460 {
461 int pid;
462
463 if(pid = fork()){
464 if(pid == -1)
465 error("can't create process", "");
466 return pid;
467 }
468 redirect(np); /* child process */
469 execute(np->args[0]);
470 exits(status);
471 return -1; // suppress compiler warnings
472 }
473
474 /* xnowait - execute cmd & */
475 int
xnowait(Node * np)476 xnowait(Node *np)
477 {
478 int pid;
479
480 execute(np->args[0]);
481 pid = execute(np->args[1]);
482 if(interactive)
483 fprint(2, "%d\n", pid);
484 return 0;
485 }
486
487 /* xwait - execute cmd ; */
xwait(Node * np)488 int xwait(Node *np)
489 {
490 int pid;
491 Waitmsg *wmsg;
492
493 execute(np->args[0]);
494 pid = execute(np->args[1]);
495 if(pid > 0){
496 while((wmsg = wait()) != nil){
497 if(wmsg->pid == pid)
498 break;
499 free(wmsg);
500 }
501 if(wmsg == nil)
502 error("wait error", "");
503 else {
504 strncpy(status, wmsg->msg, sizeof(status)-1);
505 free(wmsg);
506 }
507 }
508 return 0;
509 }
510