xref: /plan9-contrib/sys/src/cmd/rc/simple.c (revision c6df144405f586b73992827d584728dc975dff14)
1 /*
2  * Maybe `simple' is a misnomer.
3  */
4 #include "rc.h"
5 #include "exec.h"
6 #include "io.h"
7 #include "fns.h"
8 /*
9  * Search through the following code to see if we're just going to exit.
10  */
11 int
exitnext(void)12 exitnext(void)
13 {
14 	union code *c=&runq->code[runq->pc];
15 	while(c->f==Xpopredir) c++;
16 	return c->f==Xexit;
17 }
18 
19 void
Xsimple(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
doredir(redir * rp)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*
searchpath(char * w)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
execexec(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
execfunc(var * func)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, runq->local);
134 	runq->local = newvar(strdup("*"), runq->local);
135 	runq->local->val = starval;
136 	runq->local->changed = 1;
137 }
138 
139 int
dochdir(char * word)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 			unixclsexec(wdirfd);
150 			write(wdirfd, word, strlen(word));
151 		}
152 	}
153 	return 1;
154 }
155 
156 static char *
appfile(char * dir,char * comp)157 appfile(char *dir, char *comp)
158 {
159 	int dirlen, complen;
160 	char *s, *p;
161 
162 	dirlen = strlen(dir);
163 	complen = strlen(comp);
164 	s = emalloc(dirlen + 1 + complen + 1);
165 	memmove(s, dir, dirlen);
166 	p = s + dirlen;
167 	*p++ = '/';
168 	memmove(p, comp, complen);
169 	p[complen] = '\0';
170 	return s;
171 }
172 
173 void
execcd(void)174 execcd(void)
175 {
176 	word *a = runq->argv->words;
177 	word *cdpath;
178 	char *dir;
179 
180 	setstatus("can't cd");
181 	cdpath = vlook("cdpath")->val;
182 	switch(count(a)){
183 	default:
184 		pfmt(err, "Usage: cd [directory]\n");
185 		break;
186 	case 2:
187 		if(a->next->word[0]=='/' || cdpath==0)
188 			cdpath = &nullpath;
189 		for(; cdpath; cdpath = cdpath->next){
190 			if(cdpath->word[0] != '\0')
191 				dir = appfile(cdpath->word, a->next->word);
192 			else
193 				dir = strdup(a->next->word);
194 
195 			if(dochdir(dir) >= 0){
196 				if(cdpath->word[0] != '\0' &&
197 				    strcmp(cdpath->word, ".") != 0)
198 					pfmt(err, "%s\n", dir);
199 				free(dir);
200 				setstatus("");
201 				break;
202 			}
203 			free(dir);
204 		}
205 		if(cdpath==0)
206 			pfmt(err, "Can't cd %s: %r\n", a->next->word);
207 		break;
208 	case 1:
209 		a = vlook("home")->val;
210 		if(count(a)>=1){
211 			if(dochdir(a->word)>=0)
212 				setstatus("");
213 			else
214 				pfmt(err, "Can't cd %s: %r\n", a->word);
215 		}
216 		else
217 			pfmt(err, "Can't cd -- $home empty\n");
218 		break;
219 	}
220 	poplist();
221 }
222 
223 void
execexit(void)224 execexit(void)
225 {
226 	switch(count(runq->argv->words)){
227 	default:
228 		pfmt(err, "Usage: exit [status]\nExiting anyway\n");
229 	case 2:
230 		setstatus(runq->argv->words->next->word);
231 	case 1:	Xexit();
232 	}
233 }
234 
235 void
execshift(void)236 execshift(void)
237 {
238 	int n;
239 	word *a;
240 	var *star;
241 	switch(count(runq->argv->words)){
242 	default:
243 		pfmt(err, "Usage: shift [n]\n");
244 		setstatus("shift usage");
245 		poplist();
246 		return;
247 	case 2:
248 		n = atoi(runq->argv->words->next->word);
249 		break;
250 	case 1:
251 		n = 1;
252 		break;
253 	}
254 	star = vlook("*");
255 	for(;n && star->val;--n){
256 		a = star->val->next;
257 		efree(star->val->word);
258 		efree((char *)star->val);
259 		star->val = a;
260 		star->changed = 1;
261 	}
262 	setstatus("");
263 	poplist();
264 }
265 
266 int
mapfd(int fd)267 mapfd(int fd)
268 {
269 	redir *rp;
270 	for(rp = runq->redir;rp;rp = rp->next){
271 		switch(rp->type){
272 		case RCLOSE:
273 			if(rp->from==fd)
274 				fd=-1;
275 			break;
276 		case RDUP:
277 		case ROPEN:
278 			if(rp->to==fd)
279 				fd = rp->from;
280 			break;
281 		}
282 	}
283 	return fd;
284 }
285 union code rdcmds[4];
286 
287 void
execcmds(io * f)288 execcmds(io *f)
289 {
290 	static int first = 1;
291 	if(first){
292 		rdcmds[0].i = 1;
293 		rdcmds[1].f = Xrdcmds;
294 		rdcmds[2].f = Xreturn;
295 		first = 0;
296 	}
297 	start(rdcmds, 1, runq->local);
298 	runq->cmdfd = f;
299 	runq->iflast = 0;
300 }
301 
302 void
execeval(void)303 execeval(void)
304 {
305 	char *cmdline, *s, *t;
306 	int len = 0;
307 	word *ap;
308 	if(count(runq->argv->words)<=1){
309 		Xerror1("Usage: eval cmd ...");
310 		return;
311 	}
312 	eflagok = 1;
313 	for(ap = runq->argv->words->next;ap;ap = ap->next)
314 		len+=1+strlen(ap->word);
315 	cmdline = emalloc(len);
316 	s = cmdline;
317 	for(ap = runq->argv->words->next;ap;ap = ap->next){
318 		for(t = ap->word;*t;) *s++=*t++;
319 		*s++=' ';
320 	}
321 	s[-1]='\n';
322 	poplist();
323 	execcmds(opencore(cmdline, len));
324 	efree(cmdline);
325 }
326 union code dotcmds[14];
327 
328 void
execdot(void)329 execdot(void)
330 {
331 	int iflag = 0;
332 	int fd;
333 	list *av;
334 	thread *p = runq;
335 	char *zero, *file;
336 	word *path;
337 	static int first = 1;
338 
339 	if(first){
340 		dotcmds[0].i = 1;
341 		dotcmds[1].f = Xmark;
342 		dotcmds[2].f = Xword;
343 		dotcmds[3].s="0";
344 		dotcmds[4].f = Xlocal;
345 		dotcmds[5].f = Xmark;
346 		dotcmds[6].f = Xword;
347 		dotcmds[7].s="*";
348 		dotcmds[8].f = Xlocal;
349 		dotcmds[9].f = Xrdcmds;
350 		dotcmds[10].f = Xunlocal;
351 		dotcmds[11].f = Xunlocal;
352 		dotcmds[12].f = Xreturn;
353 		first = 0;
354 	}
355 	else
356 		eflagok = 1;
357 	popword();
358 	if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){
359 		iflag = 1;
360 		popword();
361 	}
362 	/* get input file */
363 	if(p->argv->words==0){
364 		Xerror1("Usage: . [-i] file [arg ...]");
365 		return;
366 	}
367 	zero = strdup(p->argv->words->word);
368 	popword();
369 	fd = -1;
370 	for(path = searchpath(zero); path; path = path->next){
371 		if(path->word[0] != '\0')
372 			file = appfile(path->word, zero);
373 		else
374 			file = strdup(zero);
375 
376 		fd = open(file, 0);
377 		free(file);
378 		if(fd >= 0)
379 			break;
380 		if(strcmp(file, "/dev/stdin")==0){	/* for sun & ucb */
381 			fd = Dup1(0);
382 			if(fd>=0)
383 				break;
384 		}
385 	}
386 	if(fd<0){
387 		pfmt(err, "%s: ", zero);
388 		setstatus("can't open");
389 		Xerror(".: can't open");
390 		return;
391 	}
392 	/* set up for a new command loop */
393 	start(dotcmds, 1, (struct var *)0);
394 	pushredir(RCLOSE, fd, 0);
395 	runq->cmdfile = zero;
396 	runq->cmdfd = openfd(fd);
397 	runq->iflag = iflag;
398 	runq->iflast = 0;
399 	/* push $* value */
400 	pushlist();
401 	runq->argv->words = p->argv->words;
402 	/* free caller's copy of $* */
403 	av = p->argv;
404 	p->argv = av->next;
405 	efree((char *)av);
406 	/* push $0 value */
407 	pushlist();
408 	pushword(zero);
409 	ndot++;
410 }
411 
412 void
execflag(void)413 execflag(void)
414 {
415 	char *letter, *val;
416 	switch(count(runq->argv->words)){
417 	case 2:
418 		setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set");
419 		break;
420 	case 3:
421 		letter = runq->argv->words->next->word;
422 		val = runq->argv->words->next->next->word;
423 		if(strlen(letter)==1){
424 			if(strcmp(val, "+")==0){
425 				flag[(uchar)letter[0]] = flagset;
426 				break;
427 			}
428 			if(strcmp(val, "-")==0){
429 				flag[(uchar)letter[0]] = 0;
430 				break;
431 			}
432 		}
433 	default:
434 		Xerror1("Usage: flag [letter] [+-]");
435 		return;
436 	}
437 	poplist();
438 }
439 
440 void
execwhatis(void)441 execwhatis(void)	/* mildly wrong -- should fork before writing */
442 {
443 	word *a, *b, *path;
444 	var *v;
445 	struct builtin *bp;
446 	char *file;
447 	struct io out[1];
448 	int found, sep;
449 	a = runq->argv->words->next;
450 	if(a==0){
451 		Xerror1("Usage: whatis name ...");
452 		return;
453 	}
454 	setstatus("");
455 	memset(out, 0, sizeof out);
456 	out->fd = mapfd(1);
457 	out->bufp = out->buf;
458 	out->ebuf = &out->buf[NBUF];
459 	out->strp = 0;
460 	for(;a;a = a->next){
461 		v = vlook(a->word);
462 		if(v->val){
463 			pfmt(out, "%s=", a->word);
464 			if(v->val->next==0)
465 				pfmt(out, "%q\n", v->val->word);
466 			else{
467 				sep='(';
468 				for(b = v->val;b && b->word;b = b->next){
469 					pfmt(out, "%c%q", sep, b->word);
470 					sep=' ';
471 				}
472 				pfmt(out, ")\n");
473 			}
474 			found = 1;
475 		}
476 		else
477 			found = 0;
478 		v = gvlook(a->word);
479 		if(v->fn)
480 			pfmt(out, "fn %q %s\n", v->name, v->fn[v->pc-1].s);
481 		else{
482 			for(bp = Builtin;bp->name;bp++)
483 				if(strcmp(a->word, bp->name)==0){
484 					pfmt(out, "builtin %s\n", a->word);
485 					break;
486 				}
487 			if(!bp->name){
488 				for(path = searchpath(a->word); path;
489 				    path = path->next){
490 					if(path->word[0] != '\0')
491 						file = appfile(path->word,
492 							a->word);
493 					else
494 						file = strdup(a->word);
495 					if(Executable(file)){
496 						pfmt(out, "%s\n", file);
497 						free(file);
498 						break;
499 					}
500 					free(file);
501 				}
502 				if(!path && !found){
503 					pfmt(err, "%s: not found\n", a->word);
504 					setstatus("not found");
505 				}
506 			}
507 		}
508 	}
509 	poplist();
510 	flush(err);
511 }
512 
513 void
execwait(void)514 execwait(void)
515 {
516 	switch(count(runq->argv->words)){
517 	default:
518 		Xerror1("Usage: wait [pid]");
519 		return;
520 	case 2:
521 		Waitfor(atoi(runq->argv->words->next->word), 0);
522 		break;
523 	case 1:
524 		Waitfor(-1, 0);
525 		break;
526 	}
527 	poplist();
528 }
529