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