xref: /inferno-os/utils/rcsh/exec.c (revision 6e425a9de8c003b5a733621a6b6730ec3cc902b8)
1 #include "rc.h"
2 
3 extern	char	*argv0;
4 
5 int	ifnot;
6 int	eflagok;
7 
8 /*
9  * Opcode routines
10  * Arguments on stack (...)
11  * Arguments in line [...]
12  * Code in line with jump around {...}
13  *
14  * Xappend(file)[fd]			open file to append
15  * Xassign(name, val)			assign val to name
16  * Xasync{... Xexit}			make thread for {}, no wait
17  * Xbackq{... Xreturn}			make thread for {}, push stdout
18  * Xbang				complement condition
19  * Xcase(pat, value){...}		exec code on match, leave (value) on
20  * 					stack
21  * Xclose[i]				close file descriptor
22  * Xconc(left, right)			concatenate, push results
23  * Xcount(name)				push var count
24  * Xdelfn(name)				delete function definition
25  * Xdeltraps(names)			delete named traps
26  * Xdol(name)				get variable value
27  * Xqdol(name)				concatenate variable components
28  * Xdup[i j]				dup file descriptor
29  * Xexit				rc exits with status
30  * Xfalse{...}				execute {} if false
31  * Xfn(name){... Xreturn}			define function
32  * Xfor(var, list){... Xreturn}		for loop
33  * Xjump[addr]				goto
34  * Xlocal(name, val)			create local variable, assign value
35  * Xmark				mark stack
36  * Xmatch(pat, str)			match pattern, set status
37  * Xpipe[i j]{... Xreturn}{... Xreturn}	construct a pipe between 2 new threads,
38  * 					wait for both
39  * Xpipefd[type]{... Xreturn}		connect {} to pipe (input or output,
40  * 					depending on type), push /dev/fd/??
41  * Xpopm(value)				pop value from stack
42  * Xread(file)[fd]			open file to read
43  * Xsettraps(names){... Xreturn}		define trap functions
44  * Xshowtraps				print trap list
45  * Xsimple(args)			run command and wait
46  * Xreturn				kill thread
47  * Xsubshell{... Xexit}			execute {} in a subshell and wait
48  * Xtrue{...}				execute {} if true
49  * Xunlocal				delete local variable
50  * Xword[string]			push string
51  * Xwrite(file)[fd]			open file to write
52  */
53 
54 static char **
55 rcargv(char *s)
56 {
57 	char *flags;
58 
59 	if(flag['e'])
60 		flags = "-Se";
61 	else
62 		flags = "-S";
63 	return procargv(argv0, flags, "-c", s, vlook("*")->val);
64 }
65 
66 void
67 Xappend(void)
68 {
69 	char *file;
70 	int f;
71 
72 	switch(count(runq->argv->words)){
73 	default: Xerror(">> requires singleton"); return;
74 	case 0: Xerror(">> requires file"); return;
75 	case 1: break;
76 	}
77 	file=runq->argv->words->word;
78 	if((f=open(file, 1))<0 && (f=create(file, 1, 0666))<0){
79 		Xperror(file);
80 		return;
81 	}
82 	seek(f, 0L, 2);
83 	pushredir(ROPEN, f, runq->code[runq->pc].i);
84 	runq->pc++;
85 	poplist();
86 }
87 
88 void
89 Xassign(void)
90 {
91 	Var *v;
92 
93 	if(count(runq->argv->words)!=1){
94 		Xerror("variable name not singleton!");
95 		return;
96 	}
97 	deglob(runq->argv->words->word);
98 	v=vlook(runq->argv->words->word);
99 	poplist();
100 	globlist();
101 	freewords(v->val);
102 	v->val=runq->argv->words;
103 	v->changed=1;
104 	runq->argv->words=0;
105 	poplist();
106 }
107 
108 void
109 Xasync(void)
110 {
111 	uint pid;
112 	char buf[20], **argv;
113 
114 	updenv();
115 
116 	argv = rcargv(runq->code[runq->pc].s);
117 	pid = proc(argv, -1, 1, 2);
118 	free(argv);
119 
120 	if(pid == 0) {
121 		Xerror("proc failed");
122 		return;
123 	}
124 
125 	runq->pc++;
126 	sprint(buf, "%d", pid);
127 	setvar("apid", newword(buf, (Word *)0));
128 }
129 
130 void
131 Xbackq(void)
132 {
133 	char wd[8193], **argv;
134 	int c;
135 	char *s, *ewd=&wd[8192], *stop;
136 	Io *f;
137 	Var *ifs=vlook("ifs");
138 	Word *v, *nextv;
139 	int pfd[2];
140 	int pid;
141 
142 	stop = ifs->val?ifs->val->word:"";
143 	if(pipe(pfd)<0){
144 		Xerror("can't make pipe");
145 		return;
146 	}
147 
148 	updenv();
149 
150 	argv = rcargv(runq->code[runq->pc].s);
151 	pid = proc(argv, -1, pfd[1], 2);
152 	free(argv);
153 
154 	close(pfd[1]);
155 
156 	if(pid == 0) {
157 		Xerror("proc failed");
158 		close(pfd[0]);
159 		return;
160 	}
161 
162 	f = openfd(pfd[0]);
163 	s = wd;
164 	v = 0;
165 	while((c=rchr(f))!=EOF){
166 		if(strchr(stop, c) || s==ewd){
167 			if(s!=wd){
168 				*s='\0';
169 				v=newword(wd, v);
170 				s=wd;
171 			}
172 		}
173 		else *s++=c;
174 	}
175 	if(s!=wd){
176 		*s='\0';
177 		v=newword(wd, v);
178 	}
179 	closeio(f);
180 	waitfor(pid);
181 	/* v points to reversed arglist -- reverse it onto argv */
182 	while(v){
183 		nextv=v->next;
184 		v->next=runq->argv->words;
185 		runq->argv->words=v;
186 		v=nextv;
187 	}
188 	runq->pc++;
189 }
190 
191 void
192 Xbang(void)
193 {
194 	setstatus(truestatus()?"false":"");
195 }
196 
197 void
198 Xcase(void)
199 {
200 	Word *p;
201 	char *s;
202 	int ok=0;
203 
204 	s=list2str(runq->argv->next->words);
205 	for(p=runq->argv->words;p;p=p->next){
206 		if(match(s, p->word, '\0')){
207 			ok=1;
208 			break;
209 		}
210 	}
211 	free(s);
212 	if(ok)
213 		runq->pc++;
214 	else
215 		runq->pc=runq->code[runq->pc].i;
216 	poplist();
217 }
218 
219 void
220 Xclose(void)
221 {
222 	pushredir(RCLOSE, runq->code[runq->pc].i, 0);
223 	runq->pc++;
224 }
225 
226 void
227 Xconc(void)
228 {
229 	Word *lp=runq->argv->words;
230 	Word *rp=runq->argv->next->words;
231 	Word *vp=runq->argv->next->next->words;
232 	int lc=count(lp), rc=count(rp);
233 
234 	if(lc!=0 || rc!=0){
235 		if(lc==0 || rc==0){
236 			Xerror("null list in concatenation");
237 			return;
238 		}
239 		if(lc!=1 && rc!=1 && lc!=rc){
240 			Xerror("mismatched list lengths in concatenation");
241 			return;
242 		}
243 		vp=conclist(lp, rp, vp);
244 	}
245 	poplist();
246 	poplist();
247 	runq->argv->words=vp;
248 }
249 
250 void
251 Xcount(void)
252 {
253 	Word *a;
254 	char *s, *t;
255 	int n;
256 	char num[12];
257 
258 	if(count(runq->argv->words)!=1){
259 		Xerror("variable name not singleton!");
260 		return;
261 	}
262 	s=runq->argv->words->word;
263 	deglob(s);
264 	n=0;
265 	for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
266 	if(n==0 || *t){
267 		a=vlook(s)->val;
268 		sprint(num, "%d", count(a));
269 	}
270 	else{
271 		a=vlook("*")->val;
272 		sprint(num, "%d", a && 1<=n && n<=count(a)?1:0);
273 	}
274 	poplist();
275 	pushword(num);
276 }
277 
278 void
279 Xdelfn(void)
280 {
281 	Var *v;
282 	Word *a;
283 
284 	for(a=runq->argv->words;a;a=a->next){
285 		v=gvlook(a->word);
286 		if(v->fn)
287 			codefree(v->fn);
288 		v->fn=0;
289 		v->fnchanged=1;
290 	}
291 	poplist();
292 }
293 
294 void
295 Xdelhere(void)
296 {
297 	Var *v;
298 	Word *a;
299 
300 	for(a=runq->argv->words;a;a=a->next){
301 		v=gvlook(a->word);
302 		if(v->fn) codefree(v->fn);
303 		v->fn=0;
304 		v->fnchanged=1;
305 	}
306 	poplist();
307 }
308 
309 void
310 Xdol(void)
311 {
312 	Word *a, *star;
313 	char *s, *t;
314 	int n;
315 
316 	if(count(runq->argv->words)!=1){
317 		Xerror("variable name not singleton!");
318 		return;
319 	}
320 	s=runq->argv->words->word;
321 	deglob(s);
322 	n=0;
323 	for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
324 	a=runq->argv->next->words;
325 	if(n==0 || *t)
326 		a=copywords(vlook(s)->val, a);
327 	else{
328 		star=vlook("*")->val;
329 		if(star && 1<=n && n<=count(star)){
330 			while(--n) star=star->next;
331 			a=newword(star->word, a);
332 		}
333 	}
334 	poplist();
335 	runq->argv->words=a;
336 }
337 
338 void
339 Xdup(void)
340 {
341 	pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
342 	runq->pc+=2;
343 }
344 
345 void
346 Xeflag(void)
347 {
348 	if(eflagok && !truestatus())
349 		Xexit();
350 }
351 
352 void
353 Xexit(void)
354 {
355 	Var *trapreq;
356 	Word *starval;
357 	char *c;
358 	static int beenhere=0;
359 
360 	if(truestatus())
361 		c = "";
362 	else
363 		c = getstatus();
364 
365 	if(flag['S'] || beenhere)
366 		exits(c);
367 
368 	trapreq=vlook("sigexit");
369 	if(trapreq->fn){
370 		beenhere=1;
371 		--runq->pc;
372 		starval=vlook("*")->val;
373 		start(trapreq->fn, trapreq->pc, (Var*)0);
374 		runq->local=newvar(strdup("*"), runq->local);
375 		runq->local->val=copywords(starval, (Word*)0);
376 		runq->local->changed=1;
377 		runq->redir=runq->startredir=0;
378 	}
379 
380 	exits(c);
381 }
382 
383 void
384 Xfalse(void)
385 {
386 	if(truestatus())
387 		runq->pc=runq->code[runq->pc].i;
388 	else
389 		runq->pc++;
390 }
391 
392 void
393 Xfor(void)
394 {
395 	if(runq->argv->words==0) {
396 		poplist();
397 		runq->pc=runq->code[runq->pc].i;
398 	} else {
399 		freelist(runq->local->val);
400 		runq->local->val=runq->argv->words;
401 		runq->local->changed=1;
402 		runq->argv->words=runq->argv->words->next;
403 		runq->local->val->next=0;
404 		runq->pc++;
405 	}
406 }
407 
408 void
409 Xfn(void)
410 {
411 	Var *v;
412 	Word *a;
413 	int end;
414 
415 	end=runq->code[runq->pc].i;
416 	for(a=runq->argv->words;a;a=a->next){
417 		v=gvlook(a->word);
418 		if(v->fn)
419 			codefree(v->fn);
420 		v->fn=codecopy(runq->code);
421 		v->pc=runq->pc+2;
422 		v->fnchanged=1;
423 	}
424 	runq->pc=end;
425 	poplist();
426 }
427 
428 void
429 Xglob(void)
430 {
431 	globlist();
432 }
433 
434 void
435 Xif(void)
436 {
437 	ifnot=1;
438 	if(truestatus()) runq->pc++;
439 	else runq->pc=runq->code[runq->pc].i;
440 }
441 
442 void
443 Xifnot(void)
444 {
445 	if(ifnot)
446 		runq->pc++;
447 	else
448 		runq->pc=runq->code[runq->pc].i;
449 }
450 
451 void
452 Xjump(void)
453 {
454 	runq->pc=runq->code[runq->pc].i;
455 }
456 
457 
458 void
459 Xlocal(void)
460 {
461 	if(count(runq->argv->words)!=1){
462 		Xerror("variable name must be singleton\n");
463 		return;
464 	}
465 	deglob(runq->argv->words->word);
466 	runq->local=newvar(strdup(runq->argv->words->word), runq->local);
467 	runq->local->val=copywords(runq->argv->next->words, 0);
468 	runq->local->changed=1;
469 	poplist();
470 	poplist();
471 }
472 
473 
474 void
475 Xmark(void)
476 {
477 	pushlist();
478 }
479 
480 void
481 Xmatch(void)
482 {
483 	Word *p;
484 	char *subject;
485 
486 	subject=list2str(runq->argv->words);
487 	setstatus("no match");
488 	for(p=runq->argv->next->words;p;p=p->next) {
489 		if(match(subject, p->word, '\0')){
490 			setstatus("");
491 			break;
492 		}
493 	}
494 	free(subject);
495 	poplist();
496 	poplist();
497 }
498 
499 void
500 Xpipe(void)
501 {
502 	Thread *p=runq;
503 	int pc=p->pc, pid;
504 	int lfd=p->code[pc].i;
505 	int rfd=p->code[pc+1].i;
506 	int pfd[2];
507 	char **argv;
508 
509 	if(pipe(pfd)<0){
510 		Xperror("can't get pipe");
511 		return;
512 	}
513 
514 	updenv();
515 
516 	argv = rcargv(runq->code[pc+2].s);
517 	pid = proc(argv, 0, pfd[1], 2);
518 	free(argv);
519 	close(pfd[1]);
520 
521 	if(pid == 0) {
522 		Xerror("proc failed");
523 		close(pfd[0]);
524 		return;
525 	}
526 
527 	start(p->code, pc+4, runq->local);
528 	pushredir(ROPEN, pfd[0], rfd);
529 	p->pc=p->code[pc+3].i;
530 	p->pid=pid;
531 }
532 
533 void
534 Xpipefd(void)
535 {
536 	fatal("Xpipefd");
537 }
538 
539 void
540 Xpipewait(void)
541 {
542 	char status[NSTATUS+1];
543 	if(runq->pid==-1)
544 		setstatus(concstatus(runq->status, getstatus()));
545 	else{
546 		strncpy(status, getstatus(), NSTATUS);
547 		status[NSTATUS]='\0';
548 		waitfor(runq->pid);
549 		runq->pid=-1;
550 		setstatus(concstatus(getstatus(), status));
551 	}
552 }
553 
554 void
555 Xpopm(void)
556 {
557 	poplist();
558 }
559 
560 void
561 Xpopredir(void)
562 {
563 	Redir *rp=runq->redir;
564 
565 	if(rp==0)
566 		panic("turfredir null!", 0);
567 	runq->redir=rp->next;
568 	if(rp->type==ROPEN)
569 		close(rp->from);
570 	free((char *)rp);
571 }
572 
573 void
574 Xqdol(void)
575 {
576 	Word *a, *p;
577 	char *s;
578 	int n;
579 
580 	if(count(runq->argv->words)!=1){
581 		Xerror("variable name not singleton!");
582 		return;
583 	}
584 	s=runq->argv->words->word;
585 	deglob(s);
586 	a=vlook(s)->val;
587 	poplist();
588 	n=count(a);
589 	if(n==0){
590 		pushword("");
591 		return;
592 	}
593 	for(p=a;p;p=p->next) n+=strlen(p->word);
594 	s=malloc(n);
595 	if(a){
596 		strcpy(s, a->word);
597 		for(p=a->next;p;p=p->next){
598 			strcat(s, " ");
599 			strcat(s, p->word);
600 		}
601 	}
602 	else
603 		s[0]='\0';
604 	pushword(s);
605 	free(s);
606 }
607 
608 void
609 Xrdcmds(void)
610 {
611 	Thread *p=runq;
612 	Word *prompt;
613 
614 	flush(err);
615 	nerror=0;
616 	if(flag['s'] && !truestatus())
617 		pfmt(err, "status=%v\n", vlook("status")->val);
618 	if(runq->iflag){
619 		prompt=vlook("prompt")->val;
620 		if(prompt)
621 			promptstr=prompt->word;
622 		else
623 			promptstr="% ";
624 	}
625 	interrupted=0;
626 	if(yyparse()) {
627 		if(!p->iflag || p->eof /* && !Eintr() */) {
628 			if(p->cmdfile)
629 				free(p->cmdfile);
630 			closeio(p->cmdfd);
631 			Xreturn();	/* should this be omitted? */
632 		} else {
633 			if(interrupted){
634 				pchr(err, '\n');
635 				p->eof=0;
636 			}
637 			--p->pc;	/* go back for next command */
638 		}
639 	} else {
640 		--p->pc;	/* re-execute Xrdcmds after codebuf runs */
641 		start(codebuf, 1, runq->local);
642 	}
643 	freenodes();
644 }
645 
646 void
647 Xread(void)
648 {
649 	char *file;
650 	int f;
651 
652 	switch(count(runq->argv->words)){
653 	default: Xerror("< requires singleton\n"); return;
654 	case 0: Xerror("< requires file\n"); return;
655 	case 1: break;
656 	}
657 	file=runq->argv->words->word;
658 	if((f=open(file, 0))<0){
659 		Xperror(file);
660 		return;
661 	}
662 	pushredir(ROPEN, f, runq->code[runq->pc].i);
663 	runq->pc++;
664 	poplist();
665 }
666 
667 void
668 Xreturn(void)
669 {
670 	Thread *p=runq;
671 
672 	turfredir();
673 	while(p->argv)
674 		poplist();
675 	codefree(p->code);
676 	runq=p->ret;
677 	free(p);
678 	if(runq==0)
679 		exits(truestatus()?"":getstatus());
680 }
681 
682 void
683 Xsettrue(void)
684 {
685 	setstatus("");
686 }
687 
688 
689 void
690 Xsub(void)
691 {
692 	Word *a, *v;
693 	char *s;
694 	if(count(runq->argv->next->words)!=1){
695 		Xerror("variable name not singleton!");
696 		return;
697 	}
698 	s=runq->argv->next->words->word;
699 	deglob(s);
700 	a=runq->argv->next->next->words;
701 	v=vlook(s)->val;
702 	a=subwords(v, count(v), runq->argv->words, a);
703 	poplist();
704 	poplist();
705 	runq->argv->words=a;
706 }
707 
708 void
709 Xsubshell(void)
710 {
711 	char **argv;
712 	uint pid;
713 
714 	updenv();
715 
716 	argv = rcargv(runq->code[runq->pc].s);
717 	pid = proc(argv, -1, 1, 2);
718 	free(argv);
719 
720 	if(pid == 0) {
721 		Xerror("proc failed");
722 		return;
723 	}
724 
725 	waitfor(pid);
726 	runq->pc++;
727 }
728 
729 void
730 Xtrue(void)
731 {
732 	if(truestatus())
733 		runq->pc++;
734 	else
735 		runq->pc=runq->code[runq->pc].i;
736 }
737 
738 void
739 Xunlocal(void)
740 {
741 	Var *v=runq->local, *hid;
742 
743 	if(v==0)
744 		panic("Xunlocal: no locals!", 0);
745 	runq->local=v->next;
746 	hid=vlook(v->name);
747 	hid->changed=1;
748 	free(v->name);
749 	freewords(v->val);
750 	free(v);
751 }
752 
753 void
754 Xwastrue(void)
755 {
756 	ifnot=0;
757 }
758 
759 void
760 Xwrite(void)
761 {
762 	char *file;
763 	int f;
764 
765 	switch(count(runq->argv->words)){
766 	default: Xerror("> requires singleton\n"); return;
767 	case 0: Xerror("> requires file\n"); return;
768 	case 1: break;
769 	}
770 	file=runq->argv->words->word;
771 	if((f = create(file, 1, 0666))<0){
772 		Xperror(file);
773 		return;
774 	}
775 	pushredir(ROPEN, f, runq->code[runq->pc].i);
776 	runq->pc++;
777 	poplist();
778 }
779 
780 void
781 Xword(void)
782 {
783 	pushword(runq->code[runq->pc++].s);
784 }
785 
786 void
787 Xerror(char *s)
788 {
789 	pfmt(err, "rcsh: %s\n", s);
790 	flush(err);
791 	while(!runq->iflag)
792 		Xreturn();
793 }
794 
795 void
796 Xperror(char *s)
797 {
798 	pfmt(err, "rcsh: %s: %r\n", s);
799 	flush(err);
800 	while(!runq->iflag)
801 		Xreturn();
802 }
803