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