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