xref: /plan9/sys/src/cmd/sh.C (revision dc5a79c1208f0704eeb474acc990728f8b4854f5)
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