xref: /plan9/sys/src/cmd/rc/simple.c (revision f9e1cf08d3be51592e03e639fc848a68dc31a55e)
1 /*
2  * Maybe `simple' is a misnomer.
3  */
4 #include "rc.h"
5 #include "getflags.h"
6 #include "exec.h"
7 #include "io.h"
8 #include "fns.h"
9 /*
10  * Search through the following code to see if we're just going to exit.
11  */
12 int
13 exitnext(void){
14 	union code *c=&runq->code[runq->pc];
15 	while(c->f==Xpopredir) c++;
16 	return c->f==Xexit;
17 }
18 
19 void
20 Xsimple(void)
21 {
22 	word *a;
23 	thread *p = runq;
24 	var *v;
25 	struct builtin *bp;
26 	int pid;
27 	globlist();
28 	a = runq->argv->words;
29 	if(a==0){
30 		Xerror1("empty argument list");
31 		return;
32 	}
33 	if(flag['x'])
34 		pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */
35 	v = gvlook(a->word);
36 	if(v->fn)
37 		execfunc(v);
38 	else{
39 		if(strcmp(a->word, "builtin")==0){
40 			if(count(a)==1){
41 				pfmt(err, "builtin: empty argument list\n");
42 				setstatus("empty arg list");
43 				poplist();
44 				return;
45 			}
46 			a = a->next;
47 			popword();
48 		}
49 		for(bp = Builtin;bp->name;bp++)
50 			if(strcmp(a->word, bp->name)==0){
51 				(*bp->fnc)();
52 				return;
53 			}
54 		if(exitnext()){
55 			/* fork and wait is redundant */
56 			pushword("exec");
57 			execexec();
58 			Xexit();
59 		}
60 		else{
61 			flush(err);
62 			Updenv();	/* necessary so changes don't go out again */
63 			if((pid = execforkexec()) < 0){
64 				Xerror("try again");
65 				return;
66 			}
67 
68 			/* interrupts don't get us out */
69 			poplist();
70 			while(Waitfor(pid, 1) < 0)
71 				;
72 		}
73 	}
74 }
75 struct word nullpath = { "", 0};
76 
77 void
78 doredir(redir *rp)
79 {
80 	if(rp){
81 		doredir(rp->next);
82 		switch(rp->type){
83 		case ROPEN:
84 			if(rp->from!=rp->to){
85 				Dup(rp->from, rp->to);
86 				close(rp->from);
87 			}
88 			break;
89 		case RDUP:
90 			Dup(rp->from, rp->to);
91 			break;
92 		case RCLOSE:
93 			close(rp->from);
94 			break;
95 		}
96 	}
97 }
98 
99 word*
100 searchpath(char *w)
101 {
102 	word *path;
103 	if(strncmp(w, "/", 1)==0
104 	|| strncmp(w, "#", 1)==0
105 	|| strncmp(w, "./", 2)==0
106 	|| strncmp(w, "../", 3)==0
107 	|| (path = vlook("path")->val)==0)
108 		path=&nullpath;
109 	return path;
110 }
111 
112 void
113 execexec(void)
114 {
115 	popword();	/* "exec" */
116 	if(runq->argv->words==0){
117 		Xerror1("empty argument list");
118 		return;
119 	}
120 	doredir(runq->redir);
121 	Execute(runq->argv->words, searchpath(runq->argv->words->word));
122 	poplist();
123 }
124 
125 void
126 execfunc(var *func)
127 {
128 	word *starval;
129 	popword();
130 	starval = runq->argv->words;
131 	runq->argv->words = 0;
132 	poplist();
133 	start(func->fn, func->pc, (struct var *)0);
134 	runq->local = newvar(strdup("*"), runq->local);
135 	runq->local->val = starval;
136 	runq->local->changed = 1;
137 }
138 
139 int
140 dochdir(char *word)
141 {
142 	/* report to /dev/wdir if it exists and we're interactive */
143 	static int wdirfd = -2;
144 	if(chdir(word)<0) return -1;
145 	if(flag['i']!=0){
146 		if(wdirfd==-2)	/* try only once */
147 			wdirfd = open("/dev/wdir", OWRITE|OCEXEC);
148 		if(wdirfd>=0)
149 			write(wdirfd, word, strlen(word));
150 	}
151 	return 1;
152 }
153 
154 void
155 execcd(void)
156 {
157 	word *a = runq->argv->words;
158 	word *cdpath;
159 	char *dir;
160 
161 	setstatus("can't cd");
162 	cdpath = vlook("cdpath")->val;
163 	switch(count(a)){
164 	default:
165 		pfmt(err, "Usage: cd [directory]\n");
166 		break;
167 	case 2:
168 		if(a->next->word[0]=='/' || cdpath==0)
169 			cdpath = &nullpath;
170 		for(; cdpath; cdpath = cdpath->next){
171 			if(cdpath->word[0] != '\0')
172 				dir = smprint("%s/%s", cdpath->word,
173 					a->next->word);
174 			else
175 				dir = strdup(a->next->word);
176 
177 			if(dochdir(dir) >= 0){
178 				if(cdpath->word[0] != '\0' &&
179 				    strcmp(cdpath->word, ".") != 0)
180 					pfmt(err, "%s\n", dir);
181 				free(dir);
182 				setstatus("");
183 				break;
184 			}
185 			free(dir);
186 		}
187 		if(cdpath==0)
188 			pfmt(err, "Can't cd %s: %r\n", a->next->word);
189 		break;
190 	case 1:
191 		a = vlook("home")->val;
192 		if(count(a)>=1){
193 			if(dochdir(a->word)>=0)
194 				setstatus("");
195 			else
196 				pfmt(err, "Can't cd %s: %r\n", a->word);
197 		}
198 		else
199 			pfmt(err, "Can't cd -- $home empty\n");
200 		break;
201 	}
202 	poplist();
203 }
204 
205 void
206 execexit(void)
207 {
208 	switch(count(runq->argv->words)){
209 	default:
210 		pfmt(err, "Usage: exit [status]\nExiting anyway\n");
211 	case 2:
212 		setstatus(runq->argv->words->next->word);
213 	case 1:	Xexit();
214 	}
215 }
216 
217 void
218 execshift(void)
219 {
220 	int n;
221 	word *a;
222 	var *star;
223 	switch(count(runq->argv->words)){
224 	default:
225 		pfmt(err, "Usage: shift [n]\n");
226 		setstatus("shift usage");
227 		poplist();
228 		return;
229 	case 2:
230 		n = atoi(runq->argv->words->next->word);
231 		break;
232 	case 1:
233 		n = 1;
234 		break;
235 	}
236 	star = vlook("*");
237 	for(;n && star->val;--n){
238 		a = star->val->next;
239 		efree(star->val->word);
240 		efree((char *)star->val);
241 		star->val = a;
242 		star->changed = 1;
243 	}
244 	setstatus("");
245 	poplist();
246 }
247 
248 int
249 octal(char *s)
250 {
251 	int n = 0;
252 	while(*s==' ' || *s=='\t' || *s=='\n') s++;
253 	while('0'<=*s && *s<='7') n = n*8+*s++-'0';
254 	return n;
255 }
256 
257 int
258 mapfd(int fd)
259 {
260 	redir *rp;
261 	for(rp = runq->redir;rp;rp = rp->next){
262 		switch(rp->type){
263 		case RCLOSE:
264 			if(rp->from==fd)
265 				fd=-1;
266 			break;
267 		case RDUP:
268 		case ROPEN:
269 			if(rp->to==fd)
270 				fd = rp->from;
271 			break;
272 		}
273 	}
274 	return fd;
275 }
276 union code rdcmds[4];
277 
278 void
279 execcmds(io *f)
280 {
281 	static int first = 1;
282 	if(first){
283 		rdcmds[0].i = 1;
284 		rdcmds[1].f = Xrdcmds;
285 		rdcmds[2].f = Xreturn;
286 		first = 0;
287 	}
288 	start(rdcmds, 1, runq->local);
289 	runq->cmdfd = f;
290 	runq->iflast = 0;
291 }
292 
293 void
294 execeval(void)
295 {
296 	char *cmdline, *s, *t;
297 	int len = 0;
298 	word *ap;
299 	if(count(runq->argv->words)<=1){
300 		Xerror1("Usage: eval cmd ...");
301 		return;
302 	}
303 	eflagok = 1;
304 	for(ap = runq->argv->words->next;ap;ap = ap->next)
305 		len+=1+strlen(ap->word);
306 	cmdline = emalloc(len);
307 	s = cmdline;
308 	for(ap = runq->argv->words->next;ap;ap = ap->next){
309 		for(t = ap->word;*t;) *s++=*t++;
310 		*s++=' ';
311 	}
312 	s[-1]='\n';
313 	poplist();
314 	execcmds(opencore(cmdline, len));
315 	efree(cmdline);
316 }
317 union code dotcmds[14];
318 
319 void
320 execdot(void)
321 {
322 	int iflag = 0;
323 	int fd;
324 	list *av;
325 	thread *p = runq;
326 	char *zero, *file;
327 	word *path;
328 	static int first = 1;
329 
330 	if(first){
331 		dotcmds[0].i = 1;
332 		dotcmds[1].f = Xmark;
333 		dotcmds[2].f = Xword;
334 		dotcmds[3].s="0";
335 		dotcmds[4].f = Xlocal;
336 		dotcmds[5].f = Xmark;
337 		dotcmds[6].f = Xword;
338 		dotcmds[7].s="*";
339 		dotcmds[8].f = Xlocal;
340 		dotcmds[9].f = Xrdcmds;
341 		dotcmds[10].f = Xunlocal;
342 		dotcmds[11].f = Xunlocal;
343 		dotcmds[12].f = Xreturn;
344 		first = 0;
345 	}
346 	else
347 		eflagok = 1;
348 	popword();
349 	if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){
350 		iflag = 1;
351 		popword();
352 	}
353 	/* get input file */
354 	if(p->argv->words==0){
355 		Xerror1("Usage: . [-i] file [arg ...]");
356 		return;
357 	}
358 	zero = strdup(p->argv->words->word);
359 	popword();
360 	fd = -1;
361 	for(path = searchpath(zero); path; path = path->next){
362 		if(path->word[0] != '\0')
363 			file = smprint("%s/%s", path->word, zero);
364 		else
365 			file = strdup(zero);
366 
367 		fd = open(file, 0);
368 		free(file);
369 		if(fd >= 0)
370 			break;
371 		if(strcmp(file, "/dev/stdin")==0){	/* for sun & ucb */
372 			fd = Dup1(0);
373 			if(fd>=0)
374 				break;
375 		}
376 	}
377 	if(fd<0){
378 		pfmt(err, "%s: ", zero);
379 		setstatus("can't open");
380 		Xerror(".: can't open");
381 		return;
382 	}
383 	/* set up for a new command loop */
384 	start(dotcmds, 1, (struct var *)0);
385 	pushredir(RCLOSE, fd, 0);
386 	runq->cmdfile = zero;
387 	runq->cmdfd = openfd(fd);
388 	runq->iflag = iflag;
389 	runq->iflast = 0;
390 	/* push $* value */
391 	pushlist();
392 	runq->argv->words = p->argv->words;
393 	/* free caller's copy of $* */
394 	av = p->argv;
395 	p->argv = av->next;
396 	efree((char *)av);
397 	/* push $0 value */
398 	pushlist();
399 	pushword(zero);
400 	ndot++;
401 }
402 
403 void
404 execflag(void)
405 {
406 	char *letter, *val;
407 	switch(count(runq->argv->words)){
408 	case 2:
409 		setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set");
410 		break;
411 	case 3:
412 		letter = runq->argv->words->next->word;
413 		val = runq->argv->words->next->next->word;
414 		if(strlen(letter)==1){
415 			if(strcmp(val, "+")==0){
416 				flag[(uchar)letter[0]] = flagset;
417 				break;
418 			}
419 			if(strcmp(val, "-")==0){
420 				flag[(uchar)letter[0]] = 0;
421 				break;
422 			}
423 		}
424 	default:
425 		Xerror1("Usage: flag [letter] [+-]");
426 		return;
427 	}
428 	poplist();
429 }
430 
431 void
432 execwhatis(void){	/* mildly wrong -- should fork before writing */
433 	word *a, *b, *path;
434 	var *v;
435 	struct builtin *bp;
436 	char *file;
437 	struct io out[1];
438 	int found, sep;
439 	a = runq->argv->words->next;
440 	if(a==0){
441 		Xerror1("Usage: whatis name ...");
442 		return;
443 	}
444 	setstatus("");
445 	out->fd = mapfd(1);
446 	out->bufp = out->buf;
447 	out->ebuf = &out->buf[NBUF];
448 	out->strp = 0;
449 	for(;a;a = a->next){
450 		v = vlook(a->word);
451 		if(v->val){
452 			pfmt(out, "%s=", a->word);
453 			if(v->val->next==0)
454 				pfmt(out, "%q\n", v->val->word);
455 			else{
456 				sep='(';
457 				for(b = v->val;b && b->word;b = b->next){
458 					pfmt(out, "%c%q", sep, b->word);
459 					sep=' ';
460 				}
461 				pfmt(out, ")\n");
462 			}
463 			found = 1;
464 		}
465 		else
466 			found = 0;
467 		v = gvlook(a->word);
468 		if(v->fn)
469 			pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s);
470 		else{
471 			for(bp = Builtin;bp->name;bp++)
472 				if(strcmp(a->word, bp->name)==0){
473 					pfmt(out, "builtin %s\n", a->word);
474 					break;
475 				}
476 			if(!bp->name){
477 				for(path = searchpath(a->word); path;
478 				    path = path->next){
479 					if(path->word[0] != '\0')
480 						file = smprint("%s/%s",
481 							path->word, a->word);
482 					else
483 						file = strdup(a->word);
484 					if(Executable(file)){
485 						pfmt(out, "%s\n", file);
486 						free(file);
487 						break;
488 					}
489 					free(file);
490 				}
491 				if(!path && !found){
492 					pfmt(err, "%s: not found\n", a->word);
493 					setstatus("not found");
494 				}
495 			}
496 		}
497 	}
498 	poplist();
499 	flush(err);
500 }
501 
502 void
503 execwait(void)
504 {
505 	switch(count(runq->argv->words)){
506 	default:
507 		Xerror1("Usage: wait [pid]");
508 		return;
509 	case 2:
510 		Waitfor(atoi(runq->argv->words->next->word), 0);
511 		break;
512 	case 1:
513 		Waitfor(-1, 0);
514 		break;
515 	}
516 	poplist();
517 }
518