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 **
rcargv(char * s)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
Xappend(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
Xassign(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
Xasync(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
Xbackq(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
Xbang(void)192 Xbang(void)
193 {
194 setstatus(truestatus()?"false":"");
195 }
196
197 void
Xcase(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
Xclose(void)220 Xclose(void)
221 {
222 pushredir(RCLOSE, runq->code[runq->pc].i, 0);
223 runq->pc++;
224 }
225
226 void
Xconc(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
Xcount(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
Xdelfn(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
Xdelhere(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
Xdol(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
Xdup(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
Xeflag(void)346 Xeflag(void)
347 {
348 if(eflagok && !truestatus())
349 Xexit();
350 }
351
352 void
Xexit(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
Xfalse(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
Xfor(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
Xfn(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
Xglob(void)429 Xglob(void)
430 {
431 globlist();
432 }
433
434 void
Xif(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
Xifnot(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
Xjump(void)452 Xjump(void)
453 {
454 runq->pc=runq->code[runq->pc].i;
455 }
456
457
458 void
Xlocal(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
Xmark(void)475 Xmark(void)
476 {
477 pushlist();
478 }
479
480 void
Xmatch(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
Xpipe(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
Xpipefd(void)534 Xpipefd(void)
535 {
536 fatal("Xpipefd");
537 }
538
539 void
Xpipewait(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
Xpopm(void)555 Xpopm(void)
556 {
557 poplist();
558 }
559
560 void
Xpopredir(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
Xqdol(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
Xrdcmds(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
Xread(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
Xreturn(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
Xsettrue(void)683 Xsettrue(void)
684 {
685 setstatus("");
686 }
687
688
689 void
Xsub(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
Xsubshell(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
Xtrue(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
Xunlocal(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
Xwastrue(void)754 Xwastrue(void)
755 {
756 ifnot=0;
757 }
758
759 void
Xwrite(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
Xword(void)781 Xword(void)
782 {
783 pushword(runq->code[runq->pc++].s);
784 }
785
786 void
Xerror(char * s)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
Xperror(char * s)796 Xperror(char *s)
797 {
798 pfmt(err, "rcsh: %s: %r\n", s);
799 flush(err);
800 while(!runq->iflag)
801 Xreturn();
802 }
803