xref: /plan9/sys/src/cmd/rc/simple.c (revision 4e3613ab15c331a9ada113286cc0f2a35bc0373d)
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
exitnext(void)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
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 			fcntl(wdirfd, F_SETFD, FD_CLOEXEC);
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
octal(char * s)267 octal(char *s)
268 {
269 	int n = 0;
270 	while(*s==' ' || *s=='\t' || *s=='\n') s++;
271 	while('0'<=*s && *s<='7') n = n*8+*s++-'0';
272 	return n;
273 }
274 
275 int
mapfd(int fd)276 mapfd(int fd)
277 {
278 	redir *rp;
279 	for(rp = runq->redir;rp;rp = rp->next){
280 		switch(rp->type){
281 		case RCLOSE:
282 			if(rp->from==fd)
283 				fd=-1;
284 			break;
285 		case RDUP:
286 		case ROPEN:
287 			if(rp->to==fd)
288 				fd = rp->from;
289 			break;
290 		}
291 	}
292 	return fd;
293 }
294 union code rdcmds[4];
295 
296 void
execcmds(io * f)297 execcmds(io *f)
298 {
299 	static int first = 1;
300 	if(first){
301 		rdcmds[0].i = 1;
302 		rdcmds[1].f = Xrdcmds;
303 		rdcmds[2].f = Xreturn;
304 		first = 0;
305 	}
306 	start(rdcmds, 1, runq->local);
307 	runq->cmdfd = f;
308 	runq->iflast = 0;
309 }
310 
311 void
execeval(void)312 execeval(void)
313 {
314 	char *cmdline, *s, *t;
315 	int len = 0;
316 	word *ap;
317 	if(count(runq->argv->words)<=1){
318 		Xerror1("Usage: eval cmd ...");
319 		return;
320 	}
321 	eflagok = 1;
322 	for(ap = runq->argv->words->next;ap;ap = ap->next)
323 		len+=1+strlen(ap->word);
324 	cmdline = emalloc(len);
325 	s = cmdline;
326 	for(ap = runq->argv->words->next;ap;ap = ap->next){
327 		for(t = ap->word;*t;) *s++=*t++;
328 		*s++=' ';
329 	}
330 	s[-1]='\n';
331 	poplist();
332 	execcmds(opencore(cmdline, len));
333 	efree(cmdline);
334 }
335 union code dotcmds[14];
336 
337 void
execdot(void)338 execdot(void)
339 {
340 	int iflag = 0;
341 	int fd;
342 	list *av;
343 	thread *p = runq;
344 	char *zero, *file;
345 	word *path;
346 	static int first = 1;
347 
348 	if(first){
349 		dotcmds[0].i = 1;
350 		dotcmds[1].f = Xmark;
351 		dotcmds[2].f = Xword;
352 		dotcmds[3].s="0";
353 		dotcmds[4].f = Xlocal;
354 		dotcmds[5].f = Xmark;
355 		dotcmds[6].f = Xword;
356 		dotcmds[7].s="*";
357 		dotcmds[8].f = Xlocal;
358 		dotcmds[9].f = Xrdcmds;
359 		dotcmds[10].f = Xunlocal;
360 		dotcmds[11].f = Xunlocal;
361 		dotcmds[12].f = Xreturn;
362 		first = 0;
363 	}
364 	else
365 		eflagok = 1;
366 	popword();
367 	if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){
368 		iflag = 1;
369 		popword();
370 	}
371 	/* get input file */
372 	if(p->argv->words==0){
373 		Xerror1("Usage: . [-i] file [arg ...]");
374 		return;
375 	}
376 	zero = strdup(p->argv->words->word);
377 	popword();
378 	fd = -1;
379 	for(path = searchpath(zero); path; path = path->next){
380 		if(path->word[0] != '\0')
381 			file = appfile(path->word, zero);
382 		else
383 			file = strdup(zero);
384 
385 		fd = open(file, 0);
386 		free(file);
387 		if(fd >= 0)
388 			break;
389 		if(strcmp(file, "/dev/stdin")==0){	/* for sun & ucb */
390 			fd = Dup1(0);
391 			if(fd>=0)
392 				break;
393 		}
394 	}
395 	if(fd<0){
396 		pfmt(err, "%s: ", zero);
397 		setstatus("can't open");
398 		Xerror(".: can't open");
399 		return;
400 	}
401 	/* set up for a new command loop */
402 	start(dotcmds, 1, (struct var *)0);
403 	pushredir(RCLOSE, fd, 0);
404 	runq->cmdfile = zero;
405 	runq->cmdfd = openfd(fd);
406 	runq->iflag = iflag;
407 	runq->iflast = 0;
408 	/* push $* value */
409 	pushlist();
410 	runq->argv->words = p->argv->words;
411 	/* free caller's copy of $* */
412 	av = p->argv;
413 	p->argv = av->next;
414 	efree((char *)av);
415 	/* push $0 value */
416 	pushlist();
417 	pushword(zero);
418 	ndot++;
419 }
420 
421 void
execflag(void)422 execflag(void)
423 {
424 	char *letter, *val;
425 	switch(count(runq->argv->words)){
426 	case 2:
427 		setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set");
428 		break;
429 	case 3:
430 		letter = runq->argv->words->next->word;
431 		val = runq->argv->words->next->next->word;
432 		if(strlen(letter)==1){
433 			if(strcmp(val, "+")==0){
434 				flag[(uchar)letter[0]] = flagset;
435 				break;
436 			}
437 			if(strcmp(val, "-")==0){
438 				flag[(uchar)letter[0]] = 0;
439 				break;
440 			}
441 		}
442 	default:
443 		Xerror1("Usage: flag [letter] [+-]");
444 		return;
445 	}
446 	poplist();
447 }
448 
449 void
execwhatis(void)450 execwhatis(void){	/* mildly wrong -- should fork before writing */
451 	word *a, *b, *path;
452 	var *v;
453 	struct builtin *bp;
454 	char *file;
455 	struct io out[1];
456 	int found, sep;
457 	a = runq->argv->words->next;
458 	if(a==0){
459 		Xerror1("Usage: whatis name ...");
460 		return;
461 	}
462 	setstatus("");
463 	memset(out, 0, sizeof out);
464 	out->fd = mapfd(1);
465 	out->bufp = out->buf;
466 	out->ebuf = &out->buf[NBUF];
467 	out->strp = 0;
468 	for(;a;a = a->next){
469 		v = vlook(a->word);
470 		if(v->val){
471 			pfmt(out, "%s=", a->word);
472 			if(v->val->next==0)
473 				pfmt(out, "%q\n", v->val->word);
474 			else{
475 				sep='(';
476 				for(b = v->val;b && b->word;b = b->next){
477 					pfmt(out, "%c%q", sep, b->word);
478 					sep=' ';
479 				}
480 				pfmt(out, ")\n");
481 			}
482 			found = 1;
483 		}
484 		else
485 			found = 0;
486 		v = gvlook(a->word);
487 		if(v->fn)
488 			pfmt(out, "fn %q %s\n", v->name, v->fn[v->pc-1].s);
489 		else{
490 			for(bp = Builtin;bp->name;bp++)
491 				if(strcmp(a->word, bp->name)==0){
492 					pfmt(out, "builtin %s\n", a->word);
493 					break;
494 				}
495 			if(!bp->name){
496 				for(path = searchpath(a->word); path;
497 				    path = path->next){
498 					if(path->word[0] != '\0')
499 						file = appfile(path->word,
500 							a->word);
501 					else
502 						file = strdup(a->word);
503 					if(Executable(file)){
504 						pfmt(out, "%s\n", file);
505 						free(file);
506 						break;
507 					}
508 					free(file);
509 				}
510 				if(!path && !found){
511 					pfmt(err, "%s: not found\n", a->word);
512 					setstatus("not found");
513 				}
514 			}
515 		}
516 	}
517 	poplist();
518 	flush(err);
519 }
520 
521 void
execwait(void)522 execwait(void)
523 {
524 	switch(count(runq->argv->words)){
525 	default:
526 		Xerror1("Usage: wait [pid]");
527 		return;
528 	case 2:
529 		Waitfor(atoi(runq->argv->words->next->word), 0);
530 		break;
531 	case 1:
532 		Waitfor(-1, 0);
533 		break;
534 	}
535 	poplist();
536 }
537