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