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