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