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 /* @(#)macro.c 1.1 */ 14 /* 15 * UNIX shell 16 * 17 * S. R. Bourne 18 * AT&T Bell Laboratories 19 * Rewritten by David Korn 20 * 21 */ 22 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 #include "flags.h" 26 #include "defs.h" 27 #include "io.h" 28 #include "sym.h" 29 #include "stak.h" 30 #include "name.h" 31 #include "shtype.h" 32 #include "mode.h" 33 #include "jobs.h" 34 #include "builtins.h" 35 #include "brkincr.h" 36 #ifdef MULTIBYTE 37 #include "national.h" 38 #endif /* MULTIBYTE */ 39 40 #define unreadc(c) (peekc = (c)|MARK) 41 #define blt_no(t) ((t)>>(COMBITS+1)) 42 #define RBRACE '}' 43 44 /* These routines are defined by this module */ 45 char *macro(); 46 char *mactry(); 47 char *mactrim(); 48 void mac_subst(); 49 50 /* These external routines are referenced by this module */ 51 extern char *arg_dolminus(); 52 extern void assign(); 53 extern void await(); 54 extern FILE *chkopen(); 55 extern void chkpipe(); 56 extern TREPTR cmd(); 57 extern void exfunct(); 58 extern void failed(); 59 extern void initf(); 60 extern char *itos(); 61 extern NAMPTR lookup(); 62 extern long lseek(); 63 extern TREPTR makefork(); 64 extern char *match_paren(); 65 extern char *movstr(); 66 extern void p_setout(); 67 extern int readc(); 68 extern char *strcpy(); 69 extern void tdystak(); 70 extern FILE *tmp_open(); 71 extern void trim(); 72 extern char *valup(); 73 74 #ifdef MULTIBYTE 75 static int charlen(); 76 #endif /* MULTIBYTE */ 77 static char *copyto(); 78 static char *substring(); 79 static void skipto(); 80 static int getch(); 81 static void comsubst(); 82 static void mac_error(); 83 84 static char quote; /* used locally */ 85 static char quoted; /* used locally */ 86 static char mflag; /* set for macro expansion, unset for here docs */ 87 static FILE *w_fd; 88 static int mac_try; 89 static jmp_buf mac_buf; 90 static char idb[2]; 91 92 93 static char *copyto(endch) 94 register char endch; 95 { 96 register int c; 97 98 while((c=getch(endch))!=endch && c) 99 { 100 if(quote || c==ESCAPE) 101 { 102 pushstak(ESCAPE); 103 if(c==ESCAPE) 104 { 105 c = readc(); 106 if(quote && !escchar(c) && c!= '"') 107 { 108 pushstak(ESCAPE); 109 pushstak(ESCAPE); 110 } 111 } 112 } 113 pushstak(c); 114 } 115 zerostak(); 116 if(c!=endch) 117 mac_error(); 118 } 119 120 /* skip chars up to } */ 121 static void skipto(endch) 122 register char endch; 123 { 124 register char c; 125 while((c=readc()) && c!=endch) 126 { 127 switch(c) 128 { 129 case SQUOTE: case DQUOTE: 130 skipto(c); 131 break; 132 133 case DOLLAR: 134 if(readc()==BRACE) 135 skipto(RBRACE); 136 } 137 } 138 if(c!=endch) 139 mac_error(); 140 } 141 142 static int getch(endch) 143 int endch; 144 { 145 register int c; 146 int atflag; /* set if $@ or ${array[@]} within double quotes */ 147 retry: 148 c = readc(); 149 if(!subchar(c)) 150 return(c); 151 if(c==DOLLAR) 152 { 153 register int bra = 0; /* {...} bra =1, {#...} bra=2 */ 154 register char *v; 155 register char *argp; 156 register NAMPTR n=(NAMPTR)NULL; 157 int dolg=0; 158 int dolmax = dolc+1; 159 BOOL nulflg; 160 char *id=idb; 161 *id = 0; 162 retry1: 163 c = readc(); 164 switch(c) 165 { 166 case DOLLAR: 167 v=pidadr; 168 break; 169 170 case '!': 171 v=pcsadr; 172 break; 173 174 case BRACE: 175 if(bra++ ==0) 176 goto retry1; 177 178 case LPAREN: 179 if(bra==0 && mac_try==0) 180 { 181 comsubst(1); 182 goto retry; 183 } 184 goto nosub; 185 186 case RBRACE: 187 if(bra!=2) 188 goto nosub; 189 bra = 0; 190 case '#': 191 if(bra ==1) 192 { 193 bra++; 194 goto retry1; 195 } 196 v=itos(dolc); 197 break; 198 199 case '?': 200 v=itos(savexit); 201 break; 202 203 case '-': 204 v=arg_dolminus(); 205 break; 206 207 default: 208 if(isalpha(c)) 209 { 210 argp=(char *) relstak(); 211 while(isalnum(c)) 212 { 213 pushstak(c); 214 c= readc(); 215 } 216 if(c=='[' && bra) 217 { 218 if((c=readc(),astchar(c))) 219 { 220 *id = c; 221 if(c=readc()!=']') 222 mac_error(); 223 dolmax = 0; 224 } 225 else 226 { 227 int savq = quote; 228 pushstak('['); 229 unreadc(c); 230 quote = 0; 231 copyto(']'); 232 quote = savq; 233 pushstak(']'); 234 } 235 } 236 else 237 unreadc(c); 238 zerostak(); 239 n=lookup(absstak(argp)); 240 setstak(argp); 241 v = valup(n); 242 id = n->namid; 243 if(dolmax == 0 && attest(n, ARRAY)) 244 { 245 dolg = -((int)(arayp(n)->maxi) + 1); 246 while(v==0) 247 { 248 arayp(n)->adot++; 249 if(++dolg == 0) 250 break; 251 v = valup(n); 252 } 253 } 254 goto cont1; 255 } 256 if(digchar(c)) 257 { 258 *id = c; 259 if(astchar(c)) 260 { 261 dolg=1; 262 c=1; 263 } 264 else 265 { 266 c -= '0'; 267 if(bra) 268 { 269 int d; 270 while((d=readc(),isdigit(d))) 271 c = 10*c + (d-'0'); 272 unreadc(d); 273 } 274 } 275 v=((c==0)?cmdadr:(c<=dolc)?dolv[c] : (char *)(dolg=0)); 276 goto cont1; 277 } 278 nosub: 279 if(bra) 280 mac_error(); 281 else 282 { 283 unreadc(c); 284 return(DOLLAR); 285 } 286 } 287 cont1: 288 c = readc(); 289 if(bra==2) 290 { 291 if(c!=RBRACE) 292 mac_error(); 293 if(dolg==0 && dolmax) 294 #ifdef MULTIBYTE 295 c = (v?charlen(v):0); 296 #else 297 c = (v?strlen(v):0); 298 #endif /* MULTIBYTE */ 299 else if(dolg>0) 300 c = dolc; 301 else if(dolg<0) 302 c = arayp(n)->maxi+1; 303 else 304 c = (v!=0); 305 v = itos(c); 306 dolg = 0; 307 c = RBRACE; 308 } 309 /* check for quotes @ */ 310 if(idb[0]=='@' && quote && !atflag) 311 { 312 quoted--; 313 atflag = 1; 314 } 315 if(c==':' && bra) /* null and unset fix */ 316 { 317 nulflg=1; 318 c=readc(); 319 } 320 else 321 nulflg=0; 322 if(!defchar(c) && bra) 323 mac_error(); 324 argp = 0; 325 if(bra) 326 { 327 if(c!=RBRACE) 328 { 329 argp=(char *)relstak(); 330 if((v==0 || (nulflg && *v==0)) ^ (setchar(c)!=0)) 331 copyto(RBRACE); 332 else 333 skipto(RBRACE); 334 argp=absstak(argp); 335 } 336 } 337 else 338 { 339 unreadc(c); 340 c=0; 341 } 342 /* check for substring operations */ 343 if(c == '#' || c == '%') 344 { 345 if(dolg != 0) 346 mac_error(); 347 if(v && *v) 348 { 349 /* allow room for escapes */ 350 staktop += strlen(v); 351 strcpy(staktop,v); 352 trim(argp); 353 if(*argp==c) 354 { 355 c |= MARK; 356 argp++; 357 } 358 v = substring(staktop,argp,c); 359 if(c&MARK) 360 argp--; 361 } 362 staktop = argp; 363 } 364 if(v && (!nulflg || *v ) && c!='+') 365 { 366 while(1) 367 { 368 BOOL no_ifs = 0; 369 int sep = SP; 370 argp = valup(IFSNOD); 371 if(argp==0 || *argp==0) 372 no_ifs++; 373 else 374 sep = *argp; 375 /* quoted null strings have to be marked */ 376 if(*v==0 && quote) 377 { 378 pushstak(ESCAPE); 379 pushstak(0); 380 } 381 while(c = *v++) 382 { 383 if(staktop >= brkend) 384 setbrk(BRKINCR); 385 if(quote || (c==ESCAPE&&mflag) 386 || (no_ifs&&isspace(c))) 387 pushstak(ESCAPE); 388 pushstak(c); 389 } 390 if(dolg==0 || (++dolg>=dolmax)) 391 break; 392 if(dolg>0) 393 v = dolv[dolg]; 394 else 395 { 396 arayp(n)->adot++; 397 while((v=valup(n))==0) 398 { 399 arayp(n)->adot++; 400 if(dolg++==0) 401 break; 402 } 403 if(v==0) 404 break; 405 } 406 if(quote && *id=='*') 407 { 408 if(no_ifs) 409 continue; 410 pushstak(ESCAPE); 411 } 412 pushstak(sep); 413 } 414 } 415 else if(argp) 416 { 417 if(c=='?') 418 { 419 if(mac_try) 420 mac_error(); 421 else 422 { 423 trim(argp); 424 failed(id,*argp?argp:badparam); 425 } 426 } 427 else if(c=='=') 428 { 429 if(n) 430 { 431 trim(argp); 432 assign(n,argp); 433 staktop = movstr(valup(n),argp); 434 } 435 else 436 mac_error(); 437 } 438 } 439 else if(is_option(NOSET)) 440 { 441 if(mac_try) 442 mac_error(); 443 else 444 failed(id,unset); 445 } 446 goto retry; 447 } 448 else if(c==endch) 449 return(c); 450 else if(c==SQUOTE && mac_try==0) 451 { 452 comsubst(0); 453 goto retry; 454 } 455 else if(c==DQUOTE) 456 { 457 if(quote ==0) 458 { 459 atflag = 0; 460 quoted++; 461 } 462 quote ^= 1; 463 goto retry; 464 } 465 return(c); 466 } 467 468 /* Strip "" and do $ substitution 469 * Leaves result on top of stack 470 */ 471 char *macro(as) 472 char *as; 473 { 474 register BOOL savqu =quoted; 475 register char savq = quote; 476 FILE fblk; 477 FILEBLK cb; 478 mflag = 1; 479 push(&cb); 480 estabf(as,&fblk); 481 usestak(); 482 quote=0; 483 quoted=0; 484 copyto(0); 485 pop(1); 486 if(quoted && (stakbot == staktop)) 487 { 488 pushstak(ESCAPE); 489 pushstak(0); 490 } 491 /* above is the fix for *'.c' bug */ 492 quote=savq; 493 quoted=savqu; 494 return(fixstak()); 495 } 496 497 /* 498 * command substitution 499 * type==0 for `` 500 * type==1 for $() 501 */ 502 503 static void comsubst(type) 504 int type; 505 { 506 FILEBLK cb; 507 register FILE *fd; 508 FILE *pv[2]; 509 FILE fblk; 510 char tmp_fname[TMPSIZ]; 511 register unsigned int d; 512 register TREPTR t; 513 register char *argc; 514 IOPTR saviotemp = iotemp; 515 int forkflag = FPOU|FCOMSUB; 516 STKPTR savtop = staktop; 517 STKPTR savptr = fixstak(); 518 char inbuff[BUFSIZ]; 519 int saveflag = states&FIXFLG; 520 register int waitflag = 0; 521 if(w_fd) 522 fflush(w_fd); /* flush before executing command */ 523 usestak(); 524 if(type) 525 { 526 staktop = (STKPTR)(match_paren((char*)stakbot,LPAREN,RPAREN,0)-1); 527 } 528 else 529 { 530 while((d=readc())!=SQUOTE && d) 531 { 532 if(d==ESCAPE) 533 { 534 d = readc(); 535 /* 536 * This is wrong but it preserves compatibility with 537 * the SVR2 shell 538 */ 539 if(!(escchar(d) || (d=='"' && quote))) 540 pushstak(ESCAPE); 541 } 542 pushstak(d); 543 } 544 } 545 argc=fixstak(); 546 states &= ~FIXFLG; /* do not save command subs in fc file */ 547 push(&cb); 548 estabf(argc,&fblk); 549 subflag = 0; 550 exec_flag++; 551 t = cmd(EOFSYM,MTFLG|NLFLG); 552 exec_flag--; 553 d = t->tretyp; 554 if(!subflag && !t->treio && (d&COMMSK)==TCOM && blt_no(d)>SYSSPECIAL) 555 { 556 /* nested command subs not handled specially */ 557 /* handle command substitution of most builtins separately */ 558 /* exec, login, cd, ., eval and shift not handled this way */ 559 /* put output into tmpfile */ 560 FILE *save1_out = standout; 561 if((states&IS_TMP)==0) 562 { 563 /* create and keep open a /tmp file for command subs */ 564 fd = tmp_open(tmp_fname); 565 fd = frenumber(fd,TMPIO); 566 states |= IS_TMP; 567 /* root cannot unlink because fsck could give bad ref count */ 568 if(userid) 569 unlink(tmp_fname); 570 else 571 states |= RM_TMP; 572 } 573 else 574 fd = file_fd(TMPIO); 575 standout = fd; 576 /* this will only flush the buffer if output is fd already */ 577 p_setout(fd); 578 #ifdef JOBS 579 states |= NONSTOP; 580 #endif /* JOBS */ 581 putc(0,fd); 582 exfunct(t,(char**)0,states&ERRFLG); 583 putc(0,fd); 584 #ifdef JOBS 585 states &= ~NONSTOP; 586 #endif /* JOBS */ 587 if(*_sobuf != 0) 588 { 589 /* file is larger than buffer, read from it */ 590 fflush(fd); 591 fseek(fd,1L,0); 592 initf(fd); 593 waitflag = -1; 594 } 595 else 596 { 597 /* The file is all in the buffer */ 598 setbuf(fd,NIL); 599 strcpy(inbuff,(char*)_sobuf+1); 600 setbuf(fd,(char*)_sobuf); 601 estabf(inbuff,(fd= &fblk)); 602 } 603 standout = save1_out; 604 goto readit; 605 } 606 else if(d==0 && ((COMPTR)t)->comarg==0) 607 { 608 if(((t->treio)->iofile) == 0) 609 argc = mactrim((t->treio)->ioname,1); 610 else 611 argc = devnull; 612 fd = chkopen(argc); 613 } 614 else 615 { 616 waitflag++; 617 if(iotemp!=saviotemp) 618 forkflag |= FTMP; 619 t = makefork(forkflag,t); 620 /* this is done like this so that the pipe 621 * is open only when needed 622 */ 623 chkpipe(pv); 624 #ifdef JOBS 625 jobstat.cur_pgrp = jobstat.mypid; 626 jobstat.j_flag++; 627 #endif /* JOBS */ 628 execute(t, states&ERRFLG, (FILE**)0, pv); 629 #ifdef JOBS 630 jobstat.j_flag = 0; 631 #endif /* JOBS */ 632 fd = pv[INPIPE]; 633 fclose(pv[OTPIPE]); 634 } 635 setbuf(fd,inbuff); 636 initf(fd); 637 638 readit: 639 tdystak(savptr); 640 d = savtop - savptr; 641 while(d--) 642 *staktop++ = *savptr++; 643 while(d=readc()) 644 { 645 if(quote || (d==ESCAPE&&mflag)) 646 pushstak(ESCAPE); 647 pushstak(d); 648 } 649 if(waitflag>0) 650 await(parent,0); 651 while(stakbot!=staktop) 652 { 653 if(*--staktop != NL) 654 { 655 *++staktop; 656 break; 657 } 658 else if(quote) 659 staktop--; 660 } 661 pop(waitflag>=0?0:1); 662 states |= saveflag; 663 } 664 665 666 void mac_subst(in,ot) 667 FILE *in; 668 register FILE *ot; 669 { 670 register char c; 671 register flag = is_option(EXECPR); 672 FILEBLK fb; 673 char inbuff[BUFSIZ]; 674 char otbuff[BUFSIZ]; 675 mflag = 0; 676 w_fd = ot; 677 push(&fb); 678 initf(in); 679 /* DQUOTE used to stop it from quoting */ 680 setbuf(in,inbuff); 681 setbuf(ot,otbuff); 682 if(flag) 683 p_setout(stderr); 684 usestak(); 685 while(1) 686 { 687 c=getch(DQUOTE); 688 if(c==ESCAPE) 689 { 690 c = readc(); 691 if(!escchar(c)) 692 pushstak(ESCAPE); 693 } 694 if(staktop!=stakbot) 695 { 696 *staktop = 0; 697 fputs(stakbot,ot); 698 if(flag) 699 fputs(stakbot,output); 700 staktop = stakbot; 701 } 702 if(c==0) 703 break; 704 putc(c,ot); 705 if(flag) 706 putc(c,output); 707 } 708 pop(0); 709 w_fd = NULL; 710 fflush(ot); 711 fseek(ot,0L,0); 712 setbuf(ot,NIL); 713 } 714 715 716 717 /* 718 * Computes the substring of STRING using the expression PAT 719 * depending on which FLAG is set. 720 */ 721 722 static char *substring(string,pat,flag) 723 char *string; 724 char *pat; 725 int flag; 726 { 727 register char *sp = string; 728 register char *cp; 729 switch(flag) 730 { 731 case '#': 732 case MARK|'#': 733 { 734 register int c; 735 cp = sp; 736 do 737 { 738 #ifdef MULTIBYTE 739 c = *sp; 740 c = echarset(c); 741 sp += (in_csize(c)+(c>=2)); 742 c = *sp; 743 #else 744 c= *++sp; 745 #endif /* MULTIBYTE */ 746 *sp=0; 747 if(gmatch(string,pat)) 748 { 749 cp = sp; 750 if(flag=='#') 751 break; 752 } 753 *sp = c; 754 } 755 while(c); 756 *sp = c; 757 return(cp); 758 } 759 760 case '%': 761 case MARK|'%': 762 { 763 sp += strlen(sp); 764 cp = sp; 765 while(sp>=string) 766 { 767 if(gmatch(sp,pat)) 768 { 769 cp = sp; 770 if(flag=='%') 771 break; 772 } 773 sp--; 774 #ifdef MULTIBYTE 775 if(*sp&HIGHBIT) 776 { 777 if(*(sp-in_csize(3))==ESS3) 778 sp -= in_csize(3); 779 else if(*(sp-in_csize(2))==ESS2) 780 sp -= in_csize(2); 781 else 782 sp -= (in_csize(1)-1); 783 } 784 #endif /* MULTIBYTE */ 785 } 786 *cp = 0; 787 return(string); 788 } 789 } 790 return(sp); 791 } 792 793 794 /* 795 * do parameter and command substitution and strip of quotes 796 * attempt file name expansion if <type> not zero 797 */ 798 799 char *mactrim(s,type) 800 char * s; 801 { 802 register char *t=macro(s); 803 ARGPTR schain = gchain; 804 if(type && f_complete(t,nullstr)==1) 805 t = gchain->argval; 806 gchain = schain; 807 trim(t); 808 return(t); 809 } 810 811 /* 812 * perform only parameter substitution and catch failures 813 */ 814 815 char *mactry(s) 816 register char *s; 817 { 818 mac_try++; 819 if(setjmp(mac_buf)==0) 820 s = mactrim(s,0); 821 mac_try = 0; 822 return(s); 823 } 824 825 static void mac_error() 826 { 827 if(mac_try) 828 longjmp(mac_buf,1); 829 error(badsub); 830 } 831 832 833 834 #ifdef MULTIBYTE 835 static int charlen(str) 836 register char *str; 837 { 838 register int n = 0; 839 register int c; 840 while(*str) 841 { 842 c = echarset(*str); /* find character set */ 843 str += (in_csize(c)+(c>=2)); /* move to next char */ 844 n += out_csize(c); /* add character size */ 845 } 846 return(n); 847 } 848 #endif /* MULTIBYTE */ 849