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