1 /* 2 3 * Copyright (c) 1984, 1985, 1986 AT&T 4 * All Rights Reserved 5 6 * THIS IS UNPUBLISHED PROPRIETARY SOURCE 7 * CODE OF AT&T. 8 * The copyright notice above does not 9 * evidence any actual or intended 10 * publication of such source code. 11 12 */ 13 /* @(#)cmd.c 1.1 */ 14 /* 15 * UNIX shell 16 * 17 * S. R. Bourne 18 * Rewritten by David Korn 19 * AT&T Bell Laboratories 20 * 21 */ 22 23 #include "defs.h" 24 #include "sym.h" 25 #include "flags.h" 26 #include "name.h" 27 #include "io.h" 28 #include "history.h" 29 #include "mode.h" 30 #include "stak.h" 31 #include "shtype.h" 32 #include "brkincr.h" 33 #include "builtins.h" 34 35 36 /* These routines are defined by this module */ 37 void synbad(); 38 TREPTR cmd(); 39 TREPTR makefork(); 40 41 /* These routines are referenced by this module */ 42 extern void addblok(); 43 extern void chkpr(); 44 extern void exitsh(); 45 extern void free(); 46 extern STKPTR getstak(); 47 extern void hist_cancel(); 48 extern void hist_flush(); 49 extern long hist_position(); 50 extern char *malloc(); 51 extern char *movstr(); 52 extern void p_setout(); 53 extern void p_str(); 54 extern void p_prp(); 55 extern void p_num(); 56 57 static TREPTR makelist(); 58 static ARGPTR qscan(); 59 static IOPTR inout(); 60 static void chkword(); 61 static void chkflags(); 62 static void chksym(); 63 static TREPTR term(); 64 static TREPTR list(); 65 static REGPTR syncase(); 66 static TREPTR item(); 67 static int skipnl(); 68 static void prsym(); 69 70 71 static int heredoc; 72 73 /* 74 * ======== command line decoding ======== 75 * 76 * This is the parser for a shell command line 77 */ 78 79 80 81 82 /* 83 * Make a node which will cause the shell to fork 84 */ 85 86 TREPTR makefork(flgs, i) 87 int flgs; 88 TREPTR i; 89 { 90 register FORKPTR t; 91 t=(FORKPTR) getstak(FORKTYPE); 92 t->forktyp = flgs|TFORK; 93 t->forktre = i; 94 t->forkio = 0; 95 return((TREPTR)t); 96 } 97 98 /* 99 * Make a node corresponding to a command list 100 */ 101 102 static TREPTR makelist(type,i,r) 103 int type; 104 TREPTR i, r; 105 { 106 register LSTPTR t; 107 if(i==0 || r==0) 108 synbad(); 109 else 110 { 111 t = (LSTPTR) getstak(LSTTYPE); 112 t->lsttyp = type; 113 t->lstlef = i; 114 t->lstrit = r; 115 } 116 return((TREPTR)t); 117 } 118 119 /* 120 * cmd 121 * empty 122 * list 123 * list & [ cmd ] 124 * list [ ; cmd ] 125 */ 126 127 TREPTR cmd(sym,flg) 128 register int sym; 129 int flg; 130 { 131 register int flag = FINT|FPRS|FAMP; 132 register TREPTR i, e; 133 IOPTR saviotemp = iotemp; 134 /* parser output goes on standard error */ 135 p_setout(stderr); 136 i = list(flg); 137 if(wdval==NL) 138 { 139 if(flg&NLFLG) 140 { 141 wdval=';'; 142 chkpr(0); 143 } 144 } 145 else if(i==0 && (flg&MTFLG)==0) 146 synbad(); 147 switch(wdval) 148 { 149 case COOPSYM: /* set up a cooperating process */ 150 flag |= FPIN|FPOU; 151 case '&': 152 if(i) 153 { 154 if(saviotemp!=iotemp || heredoc) 155 flag |= FTMP; 156 i = (TREPTR)makefork(flag, i); 157 } 158 else 159 synbad(); 160 161 case ';': 162 if(e=cmd(sym,flg|MTFLG)) 163 i=(TREPTR)makelist(TLST, i, e); 164 break; 165 166 case EOFSYM: 167 if(sym==NL) 168 break; 169 170 default: 171 if(sym) 172 chksym(sym); 173 174 } 175 /* restore output stream */ 176 return(i); 177 } 178 179 /* 180 * list 181 * term 182 * list && term 183 * list || term 184 */ 185 186 static TREPTR list(flg) 187 { 188 register TREPTR r; 189 register int b; 190 r = term(flg); 191 while(r && ((b=(wdval==ANDFSYM)) || wdval==ORFSYM)) 192 { 193 r = makelist((b ? TAND : TORF), r, term(NLFLG)); 194 } 195 return(r); 196 } 197 198 /* 199 * term 200 * item 201 * item |^ term 202 */ 203 204 static TREPTR term(flg) 205 register int flg; 206 { 207 register TREPTR t; 208 register PARPTR p = NULL; 209 heredoc = 0; 210 reserv++; 211 if(flg&NLFLG) 212 skipnl(); 213 else 214 word(); 215 /* check to see if pipeline is to be timed */ 216 if(wdval==TIMSYM) 217 { 218 p=(PARPTR) getstak(PARTYPE); 219 p->partyp=TTIME; 220 reserv++; 221 word(); 222 } 223 if((t=item(NLFLG|MTFLG)) && wdval=='|') 224 { 225 flg = heredoc|FPOU; 226 t=makelist(TFIL,makefork(flg,t),makefork(FPIN|FPCL,term(NLFLG))); 227 } 228 if(p) 229 { 230 p->partre= t; 231 return((TREPTR)p); 232 } 233 else 234 return(t); 235 } 236 237 /* 238 * case statement 239 */ 240 241 static REGPTR syncase(esym) 242 register int esym; 243 { 244 wdset |= E_FLAG; /* set to avoid aliasing expressions */ 245 skipnl(); 246 if(wdval==esym) 247 { 248 wdset &= ~E_FLAG; 249 return(0); 250 } 251 else 252 { 253 register REGPTR r=(REGPTR) getstak(REGTYPE); 254 r->regptr=0; 255 while(1) 256 { 257 chkflags(wdarg,1); 258 wdarg->argnxt=r->regptr; 259 r->regptr=wdarg; 260 if(wdval==')' || wdval=='|' || ( word()!=')' && wdval!='|' )) 261 synbad(); 262 if(wdval=='|') 263 word(); 264 else 265 break; 266 } 267 wdset &= ~E_FLAG; 268 r->regcom=cmd(0,NLFLG|MTFLG); 269 if(wdval==ECSYM) 270 r->regnxt=syncase(esym); 271 else 272 { 273 chksym(esym); 274 r->regnxt=0; 275 } 276 return(r); 277 } 278 } 279 280 /* 281 * item 282 * 283 * ( cmd ) [ < in ] [ > out ] 284 * word word* [ < in ] [ > out ] 285 * if ... then ... else ... fi 286 * for ... while ... do ... done 287 * case ... in ... esac 288 * begin ... end 289 */ 290 291 static TREPTR item(flag) 292 BOOL flag; 293 { 294 register TREPTR t; 295 register IOPTR io; 296 if(flag) 297 io=inout((IOPTR)0,1); 298 else 299 io=0; 300 switch(wdval) 301 { 302 /* case statement */ 303 case CASYM: 304 { 305 t=(TREPTR) getstak(SWTYPE); 306 chkword(); 307 ((SWPTR) t)->swarg=wdarg->argval; 308 skipnl(); 309 chksym(INSYM|BRSYM); 310 ((SWPTR) t)->swlst=syncase(wdval==INSYM?ESSYM:KTSYM); 311 ((SWPTR) t)->swtyp=TSW; 312 break; 313 } 314 315 /* if statement */ 316 case IFSYM: 317 { 318 register int w; 319 t=(TREPTR) getstak(IFTYPE); 320 ((IFPTR) t)->iftyp=TIF; 321 ((IFPTR) t)->iftre=cmd(THSYM,NLFLG); 322 ((IFPTR) t)->thtre=cmd(ELSYM|FISYM|EFSYM,NLFLG); 323 ((IFPTR) t)->eltre=((w=wdval)==ELSYM?cmd(FISYM,NLFLG): 324 (w==EFSYM?(wdval=IFSYM, item(0)):0)); 325 if(w==EFSYM) 326 return(t); 327 break; 328 } 329 330 /* for and select statement */ 331 case FORSYM: 332 case SELSYM: 333 { 334 t=(TREPTR) getstak(FORTYPE); 335 ((FORPTR) t)->fortyp=(wdval==FORSYM?TFOR:TSELECT); 336 ((FORPTR) t)->forlst=0; 337 chkword(); 338 ((FORPTR) t)->fornam=(char*) wdarg->argval; 339 if(skipnl()==INSYM) 340 { 341 chkword(); 342 ((FORPTR) t)->forlst=(COMPTR) item(0); 343 if(wdval!=NL && wdval!=';') 344 synbad(); 345 if(wdval==NL) 346 chkpr(0); 347 skipnl(); 348 } 349 /* 'for i;do cmd' is valid syntax */ 350 else if(wdval==';') 351 { 352 reserv = 1; 353 word(); 354 } 355 chksym(DOSYM|BRSYM); 356 ((FORPTR) t)->fortre=cmd(wdval==DOSYM?ODSYM:KTSYM,NLFLG); 357 break; 358 } 359 360 /* This is the code for parsing function definitions */ 361 case PROCSYM: 362 funct_5_2: 363 { 364 TREPTR cmdptr; 365 BLKPTR blokptr; 366 int savstates = states; 367 int saveline = firstline; 368 register FILE *fd = NULL; 369 IOPTR saviotemp = iotemp; 370 t=(TREPTR) getstak(PROCTYPE); 371 ((PROCPTR) t)->proctyp=TPROC; 372 ((PROCPTR) t)->procloc = -1; 373 firstline = standin->flin; 374 if(wdval == PROCSYM) 375 chkword(); 376 ((PROCPTR) t)->procnam=(char *) wdarg->argval; 377 skipnl(); 378 chksym(BRSYM); 379 /* force a new stak frame to compile the command */ 380 addblok(-1); 381 if(is_option(INTFLG)) 382 { 383 /* just in case history file not open yet */ 384 hist_open(); 385 if(fc_fix) 386 { 387 fd = fc_fix->fixfd; 388 states |= FIXFLG; 389 ((PROCPTR)t)->procloc = 390 hist_position(fc_fix->fixind) + 391 fd->_ptr - fd->_base; 392 } 393 } 394 cmdptr = cmd(KTSYM,NLFLG); 395 /* force another stak frame to save the command */ 396 addblok(-1); 397 blokptr = stakbsy; 398 stakbsy = stakbsy->word; 399 /* save the entry point in block */ 400 blokptr->word = BLK(cmdptr); 401 ((PROCPTR) t)->proctre = blokptr; 402 if(iotemp != saviotemp) 403 { 404 iotemp = saviotemp; 405 states |= RM_TMP; 406 } 407 if(fd && (savstates&FIXFLG)==0) 408 { 409 hist_flush(); 410 hist_cancel(); 411 states &= ~FIXFLG; 412 } 413 firstline = saveline; 414 break; 415 } 416 417 /* while and until */ 418 case WHSYM: 419 case UNSYM: 420 { 421 t=(TREPTR) getstak(WHTYPE); 422 ((WHPTR) t)->whtyp=(wdval==WHSYM ? TWH : TUN); 423 ((WHPTR) t)->whtre = cmd(DOSYM,NLFLG); 424 ((WHPTR) t)->dotre = cmd(ODSYM,NLFLG); 425 break; 426 } 427 428 /* command group with { */ 429 case BRSYM: 430 t=cmd(KTSYM,NLFLG); 431 break; 432 433 case '(': 434 { 435 register PARPTR p; 436 p=(PARPTR) getstak(PARTYPE); 437 p->partre=cmd(')',NLFLG); 438 p->partyp=TPAR; 439 t=makefork(0,(TREPTR)p); 440 break; 441 } 442 443 default: 444 if(io==0) 445 return(0); 446 447 /* simple command */ 448 case 0: 449 { 450 register ARGPTR argp; 451 register ARGPTR *argtail; 452 register ARGPTR *argset=0; 453 int keywd=KEYFLG; 454 int argno = 0; 455 int bltin = 0; 456 t=(TREPTR) getstak(COMTYPE); 457 ((COMPTR)t)->comio=io; /*initial io chain*/ 458 /* set command line number for error messages */ 459 ((COMPTR)t)->comline = (exec_flag?cmdline: 460 standin->flin-firstline-1); 461 argtail = &(((COMPTR)t)->comarg); 462 while(wdval==0) 463 { 464 argp = wdarg; 465 argp->argchn = 0; 466 /* test for keyword argument */ 467 if(wdset&keywd) 468 { 469 chkflags(argp,0); 470 argp->argnxt=(ARGPTR) argset; 471 argset=(ARGPTR *) argp; 472 /* alias substitutions allowed */ 473 wdset |= (KEYFLG|S_FLAG); 474 } 475 else 476 { 477 wdset = 0; /* don't hunt for aliases*/ 478 chkflags(argp,1); 479 if((argp->argflag&A_RAW) == 0) 480 argno = -1; 481 if(argno>=0 && argno++==0) 482 { 483 /* check for builtin command */ 484 bltin=syslook(argp->argval,commands); 485 } 486 *argtail = argp; 487 argtail = &(argp->argnxt); 488 wdset = keywd=is_option(KEYFLG); 489 } 490 #ifdef DEVFD 491 retry: 492 word(); 493 if((wdval&STRIP)=='(') 494 { 495 TREPTR t; 496 int flag = (wdval==OPROC); 497 t = cmd(')',NLFLG|(argno==1&&wdval=='('?MTFLG:0)); 498 if(t == NULL) 499 { 500 wdarg = argp; 501 goto funct_5_2; 502 } 503 argp = (ARGPTR)locstak(); 504 argno = -1; 505 *argtail = argp; 506 argtail = &(argp->argnxt); 507 endstak(movstr(nullstr,argp->argval)); 508 argp->argchn = (ARGPTR)makefork(flag?FPIN|FAMP|FPCL:FPOU,t); 509 argp->argflag = (A_EXP|flag); 510 goto retry; 511 } 512 #else 513 word(); 514 if(argno==1 && argset==NULL && wdval== '(') 515 { 516 /* SVR2 style function */ 517 word(); 518 if(wdval == ')') 519 { 520 wdarg = argp; 521 goto funct_5_2; 522 } 523 wdval = '('; 524 } 525 #endif /* DEVFD */ 526 if(flag) 527 { 528 if(io) 529 { 530 while(io->ionxt) 531 io = io->ionxt; 532 io->ionxt = inout((IOPTR)0,0); 533 } 534 else 535 ((COMPTR)t)->comio = io = inout((IOPTR)0,0); 536 } 537 } 538 *argtail = 0; 539 ((COMPTR)t)->comtyp = (TCOM|(bltin<<(COMBITS+1))); 540 /* expand argument list if possible */ 541 if(argno>0) 542 ((COMPTR)t)->comarg = qscan(t,argno); 543 else if(((COMPTR)t)->comarg) 544 ((COMPTR)t)->comtyp |= COMSCAN; 545 ((COMPTR)t)->comset=(ARGPTR) argset; 546 wdset &= ~S_FLAG; 547 return(t); 548 } 549 } 550 reserv++; 551 word(); 552 if(io=inout(io,0)) 553 { 554 int type = t->tretyp&COMMSK; 555 t=makefork(0,t); 556 t->treio=io; 557 if(type != TFORK) 558 t->tretyp = TSETIO; 559 } 560 return(t); 561 } 562 563 564 /* 565 * skip past newlines but issue prompt if interactive 566 */ 567 568 static int skipnl() 569 { 570 while((reserv++, word()==NL)) 571 chkpr(0); 572 return(wdval); 573 } 574 575 /* 576 * check for and process and i/o redirections 577 * if flag is set then an alias can be in the next word 578 */ 579 580 static IOPTR inout(lastio,flag) 581 IOPTR lastio; 582 { 583 register int iof; 584 register IOPTR iop; 585 register int c; 586 iof=wdnum; 587 switch(wdval) 588 { 589 case DOCSYM: /* << */ 590 iof |= IODOC; 591 heredoc = FTMP; 592 break; 593 594 case APPSYM: /* >> */ 595 case '>': 596 if(wdnum==0) 597 iof |= 1; 598 iof |= IOPUT; 599 if(wdval==APPSYM) 600 { 601 iof |= IOAPP; 602 break; 603 } 604 605 case '<': 606 if((c=nextc())=='&') 607 iof |= IOMOV; 608 else if(c=='>') 609 /* <> is open for read and write */ 610 /* unadvertised feature */ 611 iof |= IORDW; 612 else 613 peekn=c|MARK; 614 break; 615 616 default: 617 return(lastio); 618 } 619 chkword(); 620 iop=(IOPTR) getstak(IOTYPE); 621 iop->ioname=wdarg->argval; 622 iop->iofile=iof; 623 if(iof&IODOC) 624 { 625 iop->iolst=iopend; 626 iopend=iop; 627 } 628 word(); 629 iop->ionxt=inout(lastio,0); 630 /* allow alias substitutions */ 631 if(flag) 632 wdset |= S_FLAG; 633 return(iop); 634 } 635 636 /* 637 * get next token and make sure that it is not a keyword or meta-character 638 */ 639 640 static void chkword() 641 { 642 if(word()) 643 synbad(); 644 } 645 646 /* 647 * see if this token is syntactically correct 648 */ 649 650 static void chksym(sym) 651 register int sym; 652 { 653 register int x = sym&wdval; 654 if(((x&SYMFLG) ? x : sym) != wdval) 655 synbad(); 656 } 657 658 /* 659 * print the name of a syntactic token 660 */ 661 662 static void prsym(sym) 663 register int sym; 664 { 665 if(sym&SYMFLG) 666 { 667 register SYSPTR sp=reserved; 668 while(sp->sysval && sp->sysval!=sym) 669 sp++; 670 fputs(sp->sysnam,output); 671 } 672 else if(sym==EOFSYM) 673 fputs(endoffile,output); 674 else 675 { 676 if(sym&SYMREP) 677 putc(sym,output); 678 if(sym==NL) 679 fputs("newline or ;",output); 680 else 681 putc(sym,output); 682 } 683 putc('\'',output); 684 } 685 686 /* 687 * print a bad syntax message 688 */ 689 690 void synbad() 691 { 692 register char *cp = unexpected; 693 register int w = wdval; 694 p_setout(stderr); 695 p_prp(synmsg,0); 696 if((states&TTYFLG)==0) 697 { 698 fputs(atline,output); 699 p_num((int)standin->flin,SP); 700 } 701 p_str(colon,'`'); 702 if(w) 703 prsym(w); 704 else 705 p_str(wdarg->argval,'\''); 706 if((w&EOFSYM) && w!=EOFSYM) 707 cp = unmatched; 708 p_str(cp,NL); 709 hist_flush(); 710 exitsh(SYNBAD); 711 } 712 713 /* 714 * check argument for possible optimizations 715 * in many cases we can skip macro and file name expansion 716 * The fexp flag is set when file expansion is possible 717 */ 718 719 #define EXP_MACRO 2 /* macro expansion needed */ 720 #define EXP_TRIM 4 /* quoted characters in string */ 721 #define EXP_FILE 8 /* file expansion characters*/ 722 #define EXP_QUOTE 16 /* string contains " character */ 723 724 static void chkflags(argp,fexp) 725 register ARGPTR argp; 726 { 727 register int c; 728 argp->argflag = 0; 729 { 730 register int flag = 0; 731 char nquote = 0; 732 char *sp=argp->argval; 733 while(c= *sp++) 734 { 735 if(c==ESCAPE) 736 { 737 flag |= EXP_TRIM; 738 sp++; 739 } 740 else if(isexp(c)) 741 { 742 if(c == '$' || c == '`') 743 { 744 flag |= EXP_MACRO; 745 if(c=='`') 746 subflag++; 747 } 748 else if(nquote==0) 749 { 750 /* special case of '[' */ 751 if(*sp || c!='[') 752 flag |= EXP_FILE; 753 } 754 } 755 else if(c == '"') 756 { 757 /* toggle the quote count */ 758 nquote = 1 - nquote; 759 flag |= EXP_QUOTE; 760 } 761 } 762 if(fexp==0) 763 flag &= ~EXP_FILE; 764 /* return if no macro expansion, file expansion or trimming required */ 765 if(flag==0) 766 { 767 argp->argflag |= A_RAW; 768 return; 769 } 770 /* return if macro or command substitution needed */ 771 if(flag&EXP_MACRO) 772 { 773 argp->argflag |= (A_MAC|A_EXP); 774 return; 775 } 776 /* check to see if file expansion is required */ 777 if(flag&EXP_FILE) 778 { 779 argp->argflag|= A_EXP; 780 /* return if no quotes otherwise don't optimize */ 781 if(flag&(EXP_QUOTE|EXP_TRIM)) 782 { 783 argp->argflag= A_MAC; 784 return; 785 } 786 return; 787 } 788 argp->argflag |= A_RAW; 789 } 790 /* just get rid of quoting stuff and consider argument as expanded */ 791 { 792 register char *dp,*sp; 793 char nquote = 0; /* set within quoted string */ 794 dp = sp = argp->argval; 795 while(c= *sp++) 796 { 797 if(c != '"') 798 { 799 if(c==ESCAPE) 800 { 801 /* strip escchar's in double quotes */ 802 c = *sp++; 803 if(nquote && !escchar(c) && c!='"') 804 *dp++ = ESCAPE; 805 } 806 *dp++ = c; 807 } 808 else /* toggle quote marker */ 809 nquote = 1-nquote; 810 } 811 *dp = 0; 812 } 813 } 814 815 /* 816 * convert argument chain to argument list when no special arguments 817 */ 818 819 static ARGPTR qscan(ac,argn) 820 COMPTR ac; 821 int argn; 822 { 823 register char **cp; 824 register ARGPTR ap; 825 register DOLPTR dp; 826 /* leave space for an extra argument at the front */ 827 dp = (DOLPTR)getstak((unsigned)DOLTYPE + sizeof(char*) + argn*sizeof(char*)); 828 cp = dp->dolarg+1; 829 dp->doluse = argn; 830 ap = ac->comarg; 831 while(ap) 832 { 833 *cp++ = ap->argval; 834 ap = ap->argnxt; 835 } 836 *cp = NULL; 837 return((ARGPTR)dp); 838 } 839 840