1 /*- 2 * Copyright (c) 1980, 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)dol.c 5.15 (Berkeley) 07/22/91"; 10 #endif /* not lint */ 11 12 #include <sys/types.h> 13 #include <fcntl.h> 14 #include <errno.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <unistd.h> 18 #if __STDC__ 19 # include <stdarg.h> 20 #else 21 # include <varargs.h> 22 #endif 23 24 #include "csh.h" 25 #include "extern.h" 26 27 /* 28 * These routines perform variable substitution and quoting via ' and ". 29 * To this point these constructs have been preserved in the divided 30 * input words. Here we expand variables and turn quoting via ' and " into 31 * QUOTE bits on characters (which prevent further interpretation). 32 * If the `:q' modifier was applied during history expansion, then 33 * some QUOTEing may have occurred already, so we dont "trim()" here. 34 */ 35 36 static int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */ 37 static Char *Dcp, **Dvp; /* Input vector for Dreadc */ 38 39 #define DEOF -1 40 41 #define unDgetC(c) Dpeekc = c 42 43 #define QUOTES (_Q|_Q1|_ESC) /* \ ' " ` */ 44 45 /* 46 * The following variables give the information about the current 47 * $ expansion, recording the current word position, the remaining 48 * words within this expansion, the count of remaining words, and the 49 * information about any : modifier which is being applied. 50 */ 51 #define MAXWLEN (BUFSIZ - 4) 52 #define MAXMOD MAXWLEN /* This cannot overflow */ 53 static Char *dolp; /* Remaining chars from this word */ 54 static Char **dolnxt; /* Further words */ 55 static int dolcnt; /* Count of further words */ 56 static Char dolmod[MAXMOD]; /* : modifier character */ 57 static int dolnmod; /* Number of modifiers */ 58 static int dolmcnt; /* :gx -> 10000, else 1 */ 59 60 static void Dfix2 __P((Char **)); 61 static Char *Dpack __P((Char *, Char *)); 62 static int Dword __P((void)); 63 static void dolerror __P((Char *)); 64 static int DgetC __P((int)); 65 static void Dgetdol __P((void)); 66 static void fixDolMod __P((void)); 67 static void setDolp __P((Char *)); 68 static void unDredc __P((int)); 69 static int Dredc __P((void)); 70 static void Dtestq __P((int)); 71 72 73 /* 74 * Fix up the $ expansions and quotations in the 75 * argument list to command t. 76 */ 77 void 78 Dfix(t) 79 register struct command *t; 80 { 81 register Char **pp; 82 register Char *p; 83 84 if (noexec) 85 return; 86 /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */ 87 for (pp = t->t_dcom; p = *pp++;) 88 for (; *p; p++) { 89 if (cmap(*p, _DOL | QUOTES)) { /* $, \, ', ", ` */ 90 Dfix2(t->t_dcom); /* found one */ 91 blkfree(t->t_dcom); 92 t->t_dcom = gargv; 93 gargv = 0; 94 return; 95 } 96 } 97 } 98 99 /* 100 * $ substitute one word, for i/o redirection 101 */ 102 Char * 103 Dfix1(cp) 104 register Char *cp; 105 { 106 Char *Dv[2]; 107 108 if (noexec) 109 return (0); 110 Dv[0] = cp; 111 Dv[1] = NULL; 112 Dfix2(Dv); 113 if (gargc != 1) { 114 setname(short2str(cp)); 115 stderror(ERR_NAME | ERR_AMBIG); 116 } 117 cp = Strsave(gargv[0]); 118 blkfree(gargv), gargv = 0; 119 return (cp); 120 } 121 122 /* 123 * Subroutine to do actual fixing after state initialization. 124 */ 125 static void 126 Dfix2(v) 127 Char **v; 128 { 129 ginit(); /* Initialize glob's area pointers */ 130 Dvp = v; 131 Dcp = STRNULL; /* Setup input vector for Dreadc */ 132 unDgetC(0); 133 unDredc(0); /* Clear out any old peeks (at error) */ 134 dolp = 0; 135 dolcnt = 0; /* Clear out residual $ expands (...) */ 136 while (Dword()) 137 continue; 138 } 139 140 /* 141 * Pack up more characters in this word 142 */ 143 static Char * 144 Dpack(wbuf, wp) 145 Char *wbuf, *wp; 146 { 147 register int c; 148 register int i = MAXWLEN - (wp - wbuf); 149 150 for (;;) { 151 c = DgetC(DODOL); 152 if (c == '\\') { 153 c = DgetC(0); 154 if (c == DEOF) { 155 unDredc(c); 156 *wp = 0; 157 Gcat(STRNULL, wbuf); 158 return (NULL); 159 } 160 if (c == '\n') 161 c = ' '; 162 else 163 c |= QUOTE; 164 } 165 if (c == DEOF) { 166 unDredc(c); 167 *wp = 0; 168 Gcat(STRNULL, wbuf); 169 return (NULL); 170 } 171 if (cmap(c, _SP | _NL | _Q | _Q1)) { /* sp \t\n'"` */ 172 unDgetC(c); 173 if (cmap(c, QUOTES)) 174 return (wp); 175 *wp++ = 0; 176 Gcat(STRNULL, wbuf); 177 return (NULL); 178 } 179 if (--i <= 0) 180 stderror(ERR_WTOOLONG); 181 *wp++ = c; 182 } 183 } 184 185 /* 186 * Get a word. This routine is analogous to the routine 187 * word() in sh.lex.c for the main lexical input. One difference 188 * here is that we don't get a newline to terminate our expansion. 189 * Rather, DgetC will return a DEOF when we hit the end-of-input. 190 */ 191 static int 192 Dword() 193 { 194 register int c, c1; 195 Char wbuf[BUFSIZ]; 196 register Char *wp = wbuf; 197 register int i = MAXWLEN; 198 register bool dolflg; 199 bool sofar = 0, done = 0; 200 201 while (!done) { 202 done = 1; 203 c = DgetC(DODOL); 204 switch (c) { 205 206 case DEOF: 207 if (sofar == 0) 208 return (0); 209 /* finish this word and catch the code above the next time */ 210 unDredc(c); 211 /* fall into ... */ 212 213 case '\n': 214 *wp = 0; 215 Gcat(STRNULL, wbuf); 216 return (1); 217 218 case ' ': 219 case '\t': 220 done = 0; 221 break; 222 223 case '`': 224 /* We preserve ` quotations which are done yet later */ 225 *wp++ = c, --i; 226 case '\'': 227 case '"': 228 /* 229 * Note that DgetC never returns a QUOTES character from an 230 * expansion, so only true input quotes will get us here or out. 231 */ 232 c1 = c; 233 dolflg = c1 == '"' ? DODOL : 0; 234 for (;;) { 235 c = DgetC(dolflg); 236 if (c == c1) 237 break; 238 if (c == '\n' || c == DEOF) 239 stderror(ERR_UNMATCHED, c1); 240 if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) 241 --wp, ++i; 242 if (--i <= 0) 243 stderror(ERR_WTOOLONG); 244 switch (c1) { 245 246 case '"': 247 /* 248 * Leave any `s alone for later. Other chars are all 249 * quoted, thus `...` can tell it was within "...". 250 */ 251 *wp++ = c == '`' ? '`' : c | QUOTE; 252 break; 253 254 case '\'': 255 /* Prevent all further interpretation */ 256 *wp++ = c | QUOTE; 257 break; 258 259 case '`': 260 /* Leave all text alone for later */ 261 *wp++ = c; 262 break; 263 } 264 } 265 if (c1 == '`') 266 *wp++ = '`', --i; 267 sofar = 1; 268 if ((wp = Dpack(wbuf, wp)) == NULL) 269 return (1); 270 else { 271 i = MAXWLEN - (wp - wbuf); 272 done = 0; 273 } 274 break; 275 276 case '\\': 277 c = DgetC(0); /* No $ subst! */ 278 if (c == '\n' || c == DEOF) { 279 done = 0; 280 break; 281 } 282 c |= QUOTE; 283 break; 284 } 285 if (done) { 286 unDgetC(c); 287 sofar = 1; 288 if ((wp = Dpack(wbuf, wp)) == NULL) 289 return (1); 290 else { 291 i = MAXWLEN - (wp - wbuf); 292 done = 0; 293 } 294 } 295 } 296 /* Really NOTREACHED */ 297 return (0); 298 } 299 300 301 /* 302 * Get a character, performing $ substitution unless flag is 0. 303 * Any QUOTES character which is returned from a $ expansion is 304 * QUOTEd so that it will not be recognized above. 305 */ 306 static int 307 DgetC(flag) 308 register int flag; 309 { 310 register int c; 311 312 top: 313 if (c = Dpeekc) { 314 Dpeekc = 0; 315 return (c); 316 } 317 if (lap) { 318 c = *lap++ & (QUOTE | TRIM); 319 if (c == 0) { 320 lap = 0; 321 goto top; 322 } 323 quotspec: 324 if (cmap(c, QUOTES)) 325 return (c | QUOTE); 326 return (c); 327 } 328 if (dolp) { 329 if (c = *dolp++ & (QUOTE | TRIM)) 330 goto quotspec; 331 if (dolcnt > 0) { 332 setDolp(*dolnxt++); 333 --dolcnt; 334 return (' '); 335 } 336 dolp = 0; 337 } 338 if (dolcnt > 0) { 339 setDolp(*dolnxt++); 340 --dolcnt; 341 goto top; 342 } 343 c = Dredc(); 344 if (c == '$' && flag) { 345 Dgetdol(); 346 goto top; 347 } 348 return (c); 349 } 350 351 static Char *nulvec[] = {0}; 352 static struct varent nulargv = {nulvec, STRargv, 0}; 353 354 static void 355 dolerror(s) 356 Char *s; 357 { 358 setname(short2str(s)); 359 stderror(ERR_NAME | ERR_RANGE); 360 } 361 362 /* 363 * Handle the multitudinous $ expansion forms. 364 * Ugh. 365 */ 366 static void 367 Dgetdol() 368 { 369 register Char *np; 370 register struct varent *vp = NULL; 371 Char name[4 * MAXVARLEN + 1]; 372 int c, sc; 373 int subscr = 0, lwb = 1, upb = 0; 374 bool dimen = 0, bitset = 0; 375 char tnp; 376 Char wbuf[BUFSIZ]; 377 378 dolnmod = dolmcnt = 0; 379 c = sc = DgetC(0); 380 if (c == '{') 381 c = DgetC(0); /* sc is { to take } later */ 382 if ((c & TRIM) == '#') 383 dimen++, c = DgetC(0); /* $# takes dimension */ 384 else if (c == '?') 385 bitset++, c = DgetC(0); /* $? tests existence */ 386 switch (c) { 387 388 case '$': 389 if (dimen || bitset) 390 stderror(ERR_SYNTAX); 391 setDolp(doldol); 392 goto eatbrac; 393 394 case '<' | QUOTE: 395 if (bitset) 396 stderror(ERR_NOTALLOWED, "$?<"); 397 if (dimen) 398 stderror(ERR_NOTALLOWED, "$?#"); 399 for (np = wbuf; read(OLDSTD, &tnp, 1) == 1; np++) { 400 *np = tnp; 401 if (np >= &wbuf[BUFSIZ - 1]) 402 stderror(ERR_LTOOLONG); 403 if (SIGN_EXTEND_CHAR(tnp) <= 0 || tnp == '\n') 404 break; 405 } 406 *np = 0; 407 /* 408 * KLUDGE: dolmod is set here because it will cause setDolp to call 409 * domod and thus to copy wbuf. Otherwise setDolp would use it 410 * directly. If we saved it ourselves, no one would know when to free 411 * it. The actual function of the 'q' causes filename expansion not to 412 * be done on the interpolated value. 413 */ 414 dolmod[dolnmod++] = 'q'; 415 dolmcnt = 10000; 416 setDolp(wbuf); 417 goto eatbrac; 418 419 case DEOF: 420 case '\n': 421 stderror(ERR_SYNTAX); 422 /* NOTREACHED */ 423 break; 424 425 case '*': 426 (void) Strcpy(name, STRargv); 427 vp = adrof(STRargv); 428 subscr = -1; /* Prevent eating [...] */ 429 break; 430 431 default: 432 np = name; 433 if (Isdigit(c)) { 434 if (dimen) 435 stderror(ERR_NOTALLOWED, "$#<num>"); 436 subscr = 0; 437 do { 438 subscr = subscr * 10 + c - '0'; 439 c = DgetC(0); 440 } while (Isdigit(c)); 441 unDredc(c); 442 if (subscr < 0) { 443 dolerror(vp->v_name); 444 return; 445 } 446 if (subscr == 0) { 447 if (bitset) { 448 dolp = ffile ? STR1 : STR0; 449 goto eatbrac; 450 } 451 if (ffile == 0) 452 stderror(ERR_DOLZERO); 453 fixDolMod(); 454 setDolp(ffile); 455 goto eatbrac; 456 } 457 if (bitset) 458 stderror(ERR_DOLQUEST); 459 vp = adrof(STRargv); 460 if (vp == 0) { 461 vp = &nulargv; 462 goto eatmod; 463 } 464 break; 465 } 466 if (!alnum(c)) 467 stderror(ERR_VARALNUM); 468 for (;;) { 469 *np++ = c; 470 c = DgetC(0); 471 if (!alnum(c)) 472 break; 473 if (np >= &name[MAXVARLEN]) 474 stderror(ERR_VARTOOLONG); 475 } 476 *np++ = 0; 477 unDredc(c); 478 vp = adrof(name); 479 } 480 if (bitset) { 481 dolp = (vp || getenv(short2str(name))) ? STR1 : STR0; 482 goto eatbrac; 483 } 484 if (vp == 0) { 485 np = str2short(getenv(short2str(name))); 486 if (np) { 487 fixDolMod(); 488 setDolp(np); 489 goto eatbrac; 490 } 491 udvar(name); 492 /* NOTREACHED */ 493 } 494 c = DgetC(0); 495 upb = blklen(vp->vec); 496 if (dimen == 0 && subscr == 0 && c == '[') { 497 np = name; 498 for (;;) { 499 c = DgetC(DODOL); /* Allow $ expand within [ ] */ 500 if (c == ']') 501 break; 502 if (c == '\n' || c == DEOF) 503 stderror(ERR_INCBR); 504 if (np >= &name[sizeof(name) / sizeof(Char) - 2]) 505 stderror(ERR_VARTOOLONG); 506 *np++ = c; 507 } 508 *np = 0, np = name; 509 if (dolp || dolcnt) /* $ exp must end before ] */ 510 stderror(ERR_EXPORD); 511 if (!*np) 512 stderror(ERR_SYNTAX); 513 if (Isdigit(*np)) { 514 int i; 515 516 for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0'); 517 if ((i < 0 || i > upb) && !any("-*", *np)) { 518 dolerror(vp->v_name); 519 return; 520 } 521 lwb = i; 522 if (!*np) 523 upb = lwb, np = STRstar; 524 } 525 if (*np == '*') 526 np++; 527 else if (*np != '-') 528 stderror(ERR_MISSING, '-'); 529 else { 530 register int i = upb; 531 532 np++; 533 if (Isdigit(*np)) { 534 i = 0; 535 while (Isdigit(*np)) 536 i = i * 10 + *np++ - '0'; 537 if (i < 0 || i > upb) { 538 dolerror(vp->v_name); 539 return; 540 } 541 } 542 if (i < lwb) 543 upb = lwb - 1; 544 else 545 upb = i; 546 } 547 if (lwb == 0) { 548 if (upb != 0) { 549 dolerror(vp->v_name); 550 return; 551 } 552 upb = -1; 553 } 554 if (*np) 555 stderror(ERR_SYNTAX); 556 } 557 else { 558 if (subscr > 0) 559 if (subscr > upb) 560 lwb = 1, upb = 0; 561 else 562 lwb = upb = subscr; 563 unDredc(c); 564 } 565 if (dimen) { 566 Char *cp = putn(upb - lwb + 1); 567 568 addla(cp); 569 xfree((ptr_t) cp); 570 } 571 else { 572 eatmod: 573 fixDolMod(); 574 dolnxt = &vp->vec[lwb - 1]; 575 dolcnt = upb - lwb + 1; 576 } 577 eatbrac: 578 if (sc == '{') { 579 c = Dredc(); 580 if (c != '}') 581 stderror(ERR_MISSING, '}'); 582 } 583 } 584 585 static void 586 fixDolMod() 587 { 588 register int c; 589 590 c = DgetC(0); 591 if (c == ':') { 592 do { 593 c = DgetC(0), dolmcnt = 1; 594 if (c == 'g') 595 c = DgetC(0), dolmcnt = 10000; 596 if (!any("htrqxe", c)) 597 stderror(ERR_BADMOD, c); 598 dolmod[dolnmod++] = c; 599 if (c == 'q') 600 dolmcnt = 10000; 601 } 602 while ((c = DgetC(0)) == ':'); 603 unDredc(c); 604 } 605 else 606 unDredc(c); 607 } 608 609 static void 610 setDolp(cp) 611 register Char *cp; 612 { 613 register Char *dp; 614 int i; 615 616 if (dolnmod == 0 || dolmcnt == 0) { 617 dolp = cp; 618 return; 619 } 620 dp = cp = Strsave(cp); 621 for (i = 0; i < dolnmod; i++) 622 if ((dp = domod(cp, dolmod[i]))) { 623 xfree((ptr_t) cp); 624 cp = dp; 625 dolmcnt--; 626 } 627 else { 628 dp = cp; 629 break; 630 } 631 632 if (dp) { 633 addla(dp); 634 xfree((ptr_t) dp); 635 } 636 dolp = STRNULL; 637 if (seterr) 638 stderror(ERR_OLD); 639 } 640 641 static void 642 unDredc(c) 643 int c; 644 { 645 646 Dpeekrd = c; 647 } 648 649 static int 650 Dredc() 651 { 652 register int c; 653 654 if (c = Dpeekrd) { 655 Dpeekrd = 0; 656 return (c); 657 } 658 if (Dcp && (c = *Dcp++)) 659 return (c & (QUOTE | TRIM)); 660 if (*Dvp == 0) { 661 Dcp = 0; 662 return (DEOF); 663 } 664 Dcp = *Dvp++; 665 return (' '); 666 } 667 668 static void 669 Dtestq(c) 670 register int c; 671 { 672 673 if (cmap(c, QUOTES)) 674 gflag = 1; 675 } 676 677 /* 678 * Form a shell temporary file (in unit 0) from the words 679 * of the shell input up to EOF or a line the same as "term". 680 * Unit 0 should have been closed before this call. 681 */ 682 void 683 /*ARGSUSED*/ 684 heredoc(term) 685 Char *term; 686 { 687 register int c; 688 Char *Dv[2]; 689 Char obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ]; 690 int ocnt, lcnt, mcnt; 691 register Char *lbp, *obp, *mbp; 692 Char **vp; 693 bool quoted; 694 char *tmp; 695 696 if (creat(tmp = short2str(shtemp), 0600) < 0) 697 stderror(ERR_SYSTEM, tmp, strerror(errno)); 698 (void) close(0); 699 if (open(tmp, O_RDWR) < 0) { 700 int oerrno = errno; 701 702 (void) unlink(tmp); 703 errno = oerrno; 704 stderror(ERR_SYSTEM, tmp, strerror(errno)); 705 } 706 (void) unlink(tmp); /* 0 0 inode! */ 707 Dv[0] = term; 708 Dv[1] = NULL; 709 gflag = 0; 710 trim(Dv); 711 rscan(Dv, Dtestq); 712 quoted = gflag; 713 ocnt = BUFSIZ; 714 obp = obuf; 715 for (;;) { 716 /* 717 * Read up a line 718 */ 719 lbp = lbuf; 720 lcnt = BUFSIZ - 4; 721 for (;;) { 722 c = readc(1); /* 1 -> Want EOF returns */ 723 if (c < 0 || c == '\n') 724 break; 725 if (c &= TRIM) { 726 *lbp++ = c; 727 if (--lcnt < 0) { 728 setname("<<"); 729 stderror(ERR_NAME | ERR_OVERFLOW); 730 } 731 } 732 } 733 *lbp = 0; 734 735 /* 736 * Check for EOF or compare to terminator -- before expansion 737 */ 738 if (c < 0 || eq(lbuf, term)) { 739 (void) write(0, short2str(obuf), (size_t) (BUFSIZ - ocnt)); 740 (void) lseek(0, 0l, L_SET); 741 return; 742 } 743 744 /* 745 * If term was quoted or -n just pass it on 746 */ 747 if (quoted || noexec) { 748 *lbp++ = '\n'; 749 *lbp = 0; 750 for (lbp = lbuf; c = *lbp++;) { 751 *obp++ = c; 752 if (--ocnt == 0) { 753 (void) write(0, short2str(obuf), BUFSIZ); 754 obp = obuf; 755 ocnt = BUFSIZ; 756 } 757 } 758 continue; 759 } 760 761 /* 762 * Term wasn't quoted so variable and then command expand the input 763 * line 764 */ 765 Dcp = lbuf; 766 Dvp = Dv + 1; 767 mbp = mbuf; 768 mcnt = BUFSIZ - 4; 769 for (;;) { 770 c = DgetC(DODOL); 771 if (c == DEOF) 772 break; 773 if ((c &= TRIM) == 0) 774 continue; 775 /* \ quotes \ $ ` here */ 776 if (c == '\\') { 777 c = DgetC(0); 778 if (!any("$\\`", c)) 779 unDgetC(c | QUOTE), c = '\\'; 780 else 781 c |= QUOTE; 782 } 783 *mbp++ = c; 784 if (--mcnt == 0) { 785 setname("<<"); 786 stderror(ERR_NAME | ERR_OVERFLOW); 787 } 788 } 789 *mbp++ = 0; 790 791 /* 792 * If any ` in line do command substitution 793 */ 794 mbp = mbuf; 795 if (any(short2str(mbp), '`')) { 796 /* 797 * 1 arg to dobackp causes substitution to be literal. Words are 798 * broken only at newlines so that all blanks and tabs are 799 * preserved. Blank lines (null words) are not discarded. 800 */ 801 vp = dobackp(mbuf, 1); 802 } 803 else 804 /* Setup trivial vector similar to return of dobackp */ 805 Dv[0] = mbp, Dv[1] = NULL, vp = Dv; 806 807 /* 808 * Resurrect the words from the command substitution each separated by 809 * a newline. Note that the last newline of a command substitution 810 * will have been discarded, but we put a newline after the last word 811 * because this represents the newline after the last input line! 812 */ 813 for (; *vp; vp++) { 814 for (mbp = *vp; *mbp; mbp++) { 815 *obp++ = *mbp & TRIM; 816 if (--ocnt == 0) { 817 (void) write(0, short2str(obuf), BUFSIZ); 818 obp = obuf; 819 ocnt = BUFSIZ; 820 } 821 } 822 *obp++ = '\n'; 823 if (--ocnt == 0) { 824 (void) write(0, short2str(obuf), BUFSIZ); 825 obp = obuf; 826 ocnt = BUFSIZ; 827 } 828 } 829 if (pargv) 830 blkfree(pargv), pargv = 0; 831 } 832 } 833