1 /* $Header: /src/pub/tcsh/sh.dol.c,v 3.39 2000/01/14 22:57:27 christos Exp $ */ 2 /* 3 * sh.dol.c: Variable substitutions 4 */ 5 /*- 6 * Copyright (c) 1980, 1991 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 #include "sh.h" 38 39 RCSID("$Id: sh.dol.c,v 3.39 2000/01/14 22:57:27 christos Exp $") 40 41 /* 42 * C shell 43 */ 44 45 /* 46 * These routines perform variable substitution and quoting via ' and ". 47 * To this point these constructs have been preserved in the divided 48 * input words. Here we expand variables and turn quoting via ' and " into 49 * QUOTE bits on characters (which prevent further interpretation). 50 * If the `:q' modifier was applied during history expansion, then 51 * some QUOTEing may have occurred already, so we dont "trim()" here. 52 */ 53 54 static int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */ 55 static Char *Dcp, **Dvp; /* Input vector for Dreadc */ 56 57 #define DEOF -1 58 59 #define unDgetC(c) Dpeekc = c 60 61 #define QUOTES (_QF|_QB|_ESC) /* \ ' " ` */ 62 63 /* 64 * The following variables give the information about the current 65 * $ expansion, recording the current word position, the remaining 66 * words within this expansion, the count of remaining words, and the 67 * information about any : modifier which is being applied. 68 */ 69 #define MAXWLEN (BUFSIZE - 4) 70 #ifndef COMPAT 71 #define MAXMOD MAXWLEN /* This cannot overflow */ 72 #endif /* COMPAT */ 73 static Char *dolp; /* Remaining chars from this word */ 74 static Char **dolnxt; /* Further words */ 75 static int dolcnt; /* Count of further words */ 76 #ifdef COMPAT 77 static Char dolmod; /* : modifier character */ 78 #else 79 static Char dolmod[MAXMOD]; /* : modifier character */ 80 static int dolnmod; /* Number of modifiers */ 81 #endif /* COMPAT */ 82 static int dolmcnt; /* :gx -> 10000, else 1 */ 83 static int dolwcnt; /* :ax -> 10000, else 1 */ 84 85 static void Dfix2 __P((Char **)); 86 static Char *Dpack __P((Char *, Char *)); 87 static int Dword __P((void)); 88 static void dolerror __P((Char *)); 89 static int DgetC __P((int)); 90 static void Dgetdol __P((void)); 91 static void fixDolMod __P((void)); 92 static void setDolp __P((Char *)); 93 static void unDredc __P((int)); 94 static int Dredc __P((void)); 95 static void Dtestq __P((int)); 96 97 /* 98 * Fix up the $ expansions and quotations in the 99 * argument list to command t. 100 */ 101 void 102 Dfix(t) 103 register struct command *t; 104 { 105 register Char **pp; 106 register Char *p; 107 108 if (noexec) 109 return; 110 /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */ 111 for (pp = t->t_dcom; (p = *pp++) != NULL;) { 112 for (; *p; p++) { 113 #ifdef DSPMBYTE 114 if (Ismbyte1(*p) && *(p + 1)) 115 p ++; 116 else 117 #endif DSPMBYTE 118 if (cmap(*p, _DOL | QUOTES)) { /* $, \, ', ", ` */ 119 Dfix2(t->t_dcom); /* found one */ 120 blkfree(t->t_dcom); 121 t->t_dcom = gargv; 122 gargv = 0; 123 return; 124 } 125 } 126 } 127 } 128 129 /* 130 * $ substitute one word, for i/o redirection 131 */ 132 Char * 133 Dfix1(cp) 134 register Char *cp; 135 { 136 Char *Dv[2]; 137 138 if (noexec) 139 return (0); 140 Dv[0] = cp; 141 Dv[1] = NULL; 142 Dfix2(Dv); 143 if (gargc != 1) { 144 setname(short2str(cp)); 145 stderror(ERR_NAME | ERR_AMBIG); 146 } 147 cp = Strsave(gargv[0]); 148 blkfree(gargv), gargv = 0; 149 return (cp); 150 } 151 152 /* 153 * Subroutine to do actual fixing after state initialization. 154 */ 155 static void 156 Dfix2(v) 157 Char **v; 158 { 159 ginit(); /* Initialize glob's area pointers */ 160 Dvp = v; 161 Dcp = STRNULL; /* Setup input vector for Dreadc */ 162 unDgetC(0); 163 unDredc(0); /* Clear out any old peeks (at error) */ 164 dolp = 0; 165 dolcnt = 0; /* Clear out residual $ expands (...) */ 166 while (Dword()) 167 continue; 168 } 169 170 /* 171 * Pack up more characters in this word 172 */ 173 static Char * 174 Dpack(wbuf, wp) 175 Char *wbuf, *wp; 176 { 177 register int c; 178 register int i = MAXWLEN - (int) (wp - wbuf); 179 #if defined(DSPMBYTE) 180 int mbytepos = 1; 181 #endif /* DSPMBYTE */ 182 183 for (;;) { 184 c = DgetC(DODOL); 185 #if defined(DSPMBYTE) 186 if (mbytepos == 1 && Ismbyte1(c)) { 187 /* An MB1 byte that may be followed by a MB2 byte */ 188 mbytepos = 2; 189 } 190 else { 191 /* check if MB1 byte followed by an MB2 byte */ 192 if (mbytepos == 2 && Ismbyte2(c)) { 193 /* MB1 + MB2 make the character */ 194 mbytepos = 1; /* reset */ 195 goto mbyteskip; 196 } 197 mbytepos = 1; /* reset */ 198 /* wasn't followed, so the two bytes make two characters */ 199 } 200 #endif /* DSPMBYTE */ 201 if (c == '\\') { 202 c = DgetC(0); 203 if (c == DEOF) { 204 unDredc(c); 205 *wp = 0; 206 Gcat(STRNULL, wbuf); 207 return (NULL); 208 } 209 if (c == '\n') 210 c = ' '; 211 else 212 c |= QUOTE; 213 } 214 if (c == DEOF) { 215 unDredc(c); 216 *wp = 0; 217 Gcat(STRNULL, wbuf); 218 return (NULL); 219 } 220 if (cmap(c, _SP | _NL | _QF | _QB)) { /* sp \t\n'"` */ 221 unDgetC(c); 222 if (cmap(c, QUOTES)) 223 return (wp); 224 *wp++ = 0; 225 Gcat(STRNULL, wbuf); 226 return (NULL); 227 } 228 mbyteskip: 229 if (--i <= 0) 230 stderror(ERR_WTOOLONG); 231 *wp++ = (Char) c; 232 } 233 } 234 235 /* 236 * Get a word. This routine is analogous to the routine 237 * word() in sh.lex.c for the main lexical input. One difference 238 * here is that we don't get a newline to terminate our expansion. 239 * Rather, DgetC will return a DEOF when we hit the end-of-input. 240 */ 241 static int 242 Dword() 243 { 244 register int c, c1; 245 Char wbuf[BUFSIZE]; 246 register Char *wp = wbuf; 247 register int i = MAXWLEN; 248 register bool dolflg; 249 bool sofar = 0, done = 0; 250 251 while (!done) { 252 done = 1; 253 c = DgetC(DODOL); 254 switch (c) { 255 256 case DEOF: 257 if (sofar == 0) 258 return (0); 259 /* finish this word and catch the code above the next time */ 260 unDredc(c); 261 /*FALLTHROUGH*/ 262 263 case '\n': 264 *wp = 0; 265 Gcat(STRNULL, wbuf); 266 return (1); 267 268 case ' ': 269 case '\t': 270 done = 0; 271 break; 272 273 case '`': 274 /* We preserve ` quotations which are done yet later */ 275 *wp++ = (Char) c, --i; 276 /*FALLTHROUGH*/ 277 case '\'': 278 case '"': 279 /* 280 * Note that DgetC never returns a QUOTES character from an 281 * expansion, so only true input quotes will get us here or out. 282 */ 283 c1 = c; 284 dolflg = c1 == '"' ? DODOL : 0; 285 for (;;) { 286 c = DgetC(dolflg); 287 if (c == c1) 288 break; 289 if (c == '\n' || c == DEOF) 290 stderror(ERR_UNMATCHED, c1); 291 if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) { 292 if ((wp[-1] & TRIM) == '\\') 293 --wp; 294 ++i; 295 } 296 if (--i <= 0) 297 stderror(ERR_WTOOLONG); 298 switch (c1) { 299 300 case '"': 301 /* 302 * Leave any `s alone for later. Other chars are all 303 * quoted, thus `...` can tell it was within "...". 304 */ 305 *wp++ = c == '`' ? '`' : c | QUOTE; 306 break; 307 308 case '\'': 309 /* Prevent all further interpretation */ 310 *wp++ = c | QUOTE; 311 break; 312 313 case '`': 314 /* Leave all text alone for later */ 315 *wp++ = (Char) c; 316 break; 317 318 default: 319 break; 320 } 321 } 322 if (c1 == '`') 323 *wp++ = '`' /* i--; eliminated */; 324 sofar = 1; 325 if ((wp = Dpack(wbuf, wp)) == NULL) 326 return (1); 327 else { 328 #ifdef masscomp 329 /* 330 * Avoid a nasty message from the RTU 4.1A & RTU 5.0 compiler concerning 331 * the "overuse of registers". According to the compiler release notes, 332 * incorrect code may be produced unless the offending expression is 333 * rewritten. Therefore, we can't just ignore it, DAS DEC-90. 334 */ 335 i = MAXWLEN; 336 i -= (int) (wp - wbuf); 337 #else /* !masscomp */ 338 i = MAXWLEN - (int) (wp - wbuf); 339 #endif /* masscomp */ 340 done = 0; 341 } 342 break; 343 344 case '\\': 345 c = DgetC(0); /* No $ subst! */ 346 if (c == '\n' || c == DEOF) { 347 done = 0; 348 break; 349 } 350 c |= QUOTE; 351 break; 352 353 default: 354 break; 355 } 356 if (done) { 357 unDgetC(c); 358 sofar = 1; 359 if ((wp = Dpack(wbuf, wp)) == NULL) 360 return (1); 361 else { 362 #ifdef masscomp 363 /* 364 * Avoid a nasty message from the RTU 4.1A & RTU 5.0 compiler concerning 365 * the "overuse of registers". According to the compiler release notes, 366 * incorrect code may be produced unless the offending expression is 367 * rewritten. Therefore, we can't just ignore it, DAS DEC-90. 368 */ 369 i = MAXWLEN; 370 i -= (int) (wp - wbuf); 371 #else /* !masscomp */ 372 i = MAXWLEN - (int) (wp - wbuf); 373 #endif /* masscomp */ 374 done = 0; 375 } 376 } 377 } 378 /* Really NOTREACHED */ 379 return (0); 380 } 381 382 383 /* 384 * Get a character, performing $ substitution unless flag is 0. 385 * Any QUOTES character which is returned from a $ expansion is 386 * QUOTEd so that it will not be recognized above. 387 */ 388 static int 389 DgetC(flag) 390 register int flag; 391 { 392 register int c; 393 394 top: 395 if ((c = Dpeekc) != 0) { 396 Dpeekc = 0; 397 return (c); 398 } 399 if (lap) { 400 c = *lap++ & (QUOTE | TRIM); 401 if (c == 0) { 402 lap = 0; 403 goto top; 404 } 405 quotspec: 406 if (cmap(c, QUOTES)) 407 return (c | QUOTE); 408 return (c); 409 } 410 if (dolp) { 411 if ((c = *dolp++ & (QUOTE | TRIM)) != 0) 412 goto quotspec; 413 if (dolcnt > 0) { 414 setDolp(*dolnxt++); 415 --dolcnt; 416 return (' '); 417 } 418 dolp = 0; 419 } 420 if (dolcnt > 0) { 421 setDolp(*dolnxt++); 422 --dolcnt; 423 goto top; 424 } 425 c = Dredc(); 426 if (c == '$' && flag) { 427 Dgetdol(); 428 goto top; 429 } 430 return (c); 431 } 432 433 static Char *nulvec[] = { NULL }; 434 static struct varent nulargv = {nulvec, STRargv, VAR_READWRITE, 435 { NULL, NULL, NULL }, 0 }; 436 437 static void 438 dolerror(s) 439 Char *s; 440 { 441 setname(short2str(s)); 442 stderror(ERR_NAME | ERR_RANGE); 443 } 444 445 /* 446 * Handle the multitudinous $ expansion forms. 447 * Ugh. 448 */ 449 static void 450 Dgetdol() 451 { 452 register Char *np; 453 register struct varent *vp = NULL; 454 Char name[4 * MAXVARLEN + 1]; 455 int c, sc; 456 int subscr = 0, lwb = 1, upb = 0; 457 bool dimen = 0, bitset = 0, length = 0; 458 char tnp; 459 Char wbuf[BUFSIZE]; 460 static Char *dolbang = NULL; 461 462 #ifdef COMPAT 463 dolmod = dolmcnt = dolwcnt = 0; 464 #else 465 dolnmod = dolmcnt = dolwcnt = 0; 466 #endif /* COMPAT */ 467 c = sc = DgetC(0); 468 if (c == '{') 469 c = DgetC(0); /* sc is { to take } later */ 470 if ((c & TRIM) == '#') 471 dimen++, c = DgetC(0); /* $# takes dimension */ 472 else if (c == '?') 473 bitset++, c = DgetC(0); /* $? tests existence */ 474 else if (c == '%') 475 length++, c = DgetC(0); /* $% returns length in chars */ 476 switch (c) { 477 478 case '!': 479 if (dimen || bitset || length) 480 stderror(ERR_SYNTAX); 481 if (backpid != 0) { 482 if (dolbang) 483 xfree((ptr_t) dolbang); 484 setDolp(dolbang = putn(backpid)); 485 } 486 goto eatbrac; 487 488 case '$': 489 if (dimen || bitset || length) 490 stderror(ERR_SYNTAX); 491 setDolp(doldol); 492 goto eatbrac; 493 494 #ifdef COHERENT 495 /* Coherent compiler doesn't allow case-labels that are not 496 constant-expressions */ 497 #ifdef SHORT_STRINGS 498 case 0100074: 499 #else /* !SHORT_STRINGS */ 500 case 0274: 501 #endif 502 #else /* !COHERENT */ 503 case '<'|QUOTE: 504 #endif 505 if (bitset) 506 stderror(ERR_NOTALLOWED, "$?<"); 507 if (dimen) 508 stderror(ERR_NOTALLOWED, "$#<"); 509 if (length) 510 stderror(ERR_NOTALLOWED, "$%<"); 511 { 512 #ifdef BSDSIGS 513 sigmask_t omask = sigsetmask(sigblock(0) & ~sigmask(SIGINT)); 514 #else /* !BSDSIGS */ 515 (void) sigrelse(SIGINT); 516 #endif /* BSDSIGS */ 517 for (np = wbuf; force_read(OLDSTD, &tnp, 1) == 1; np++) { 518 *np = (unsigned char) tnp; 519 if (np >= &wbuf[BUFSIZE - 1]) 520 stderror(ERR_LTOOLONG); 521 if (tnp == '\n') 522 break; 523 } 524 *np = 0; 525 #ifdef BSDSIGS 526 (void) sigsetmask(omask); 527 #else /* !BSDSIGS */ 528 (void) sighold(SIGINT); 529 #endif /* BSDSIGS */ 530 } 531 532 #ifdef COMPAT 533 /* 534 * KLUDGE: dolmod is set here because it will cause setDolp to call 535 * domod and thus to copy wbuf. Otherwise setDolp would use it 536 * directly. If we saved it ourselves, no one would know when to free 537 * it. The actual function of the 'q' causes filename expansion not to 538 * be done on the interpolated value. 539 */ 540 /* 541 * If we do that, then other modifiers don't work. 542 * in addition, let the user specify :q if wanted 543 * [christos] 544 */ 545 /*old*/ dolmod = 'q'; 546 /*new*/ dolmod[dolnmod++] = 'q'; 547 dolmcnt = 10000; 548 #endif /* COMPAT */ 549 550 fixDolMod(); 551 setDolp(wbuf); 552 goto eatbrac; 553 554 case '*': 555 (void) Strcpy(name, STRargv); 556 vp = adrof(STRargv); 557 subscr = -1; /* Prevent eating [...] */ 558 break; 559 560 case DEOF: 561 case '\n': 562 np = dimen ? STRargv : (bitset ? STRstatus : NULL); 563 if (np) { 564 bitset = 0; 565 (void) Strcpy(name, np); 566 vp = adrof(np); 567 subscr = -1; /* Prevent eating [...] */ 568 unDredc(c); 569 break; 570 } 571 else 572 stderror(ERR_SYNTAX); 573 /*NOTREACHED*/ 574 575 default: 576 np = name; 577 if (Isdigit(c)) { 578 if (dimen) 579 stderror(ERR_NOTALLOWED, "$#<num>"); 580 subscr = 0; 581 do { 582 subscr = subscr * 10 + c - '0'; 583 c = DgetC(0); 584 } while (Isdigit(c)); 585 unDredc(c); 586 if (subscr < 0) { 587 dolerror(vp->v_name); 588 return; 589 } 590 if (subscr == 0) { 591 if (bitset) { 592 dolp = dolzero ? STR1 : STR0; 593 goto eatbrac; 594 } 595 if (ffile == 0) 596 stderror(ERR_DOLZERO); 597 if (length) { 598 Char *cp; 599 length = Strlen(ffile); 600 cp = putn(length); 601 addla(cp); 602 xfree((ptr_t) cp); 603 } 604 else { 605 fixDolMod(); 606 setDolp(ffile); 607 } 608 goto eatbrac; 609 } 610 #if 0 611 if (bitset) 612 stderror(ERR_NOTALLOWED, "$?<num>"); 613 if (length) 614 stderror(ERR_NOTALLOWED, "$%<num>"); 615 #endif 616 vp = adrof(STRargv); 617 if (vp == 0) { 618 vp = &nulargv; 619 goto eatmod; 620 } 621 break; 622 } 623 if (!alnum(c)) { 624 np = dimen ? STRargv : (bitset ? STRstatus : NULL); 625 if (np) { 626 bitset = 0; 627 (void) Strcpy(name, np); 628 vp = adrof(np); 629 subscr = -1; /* Prevent eating [...] */ 630 unDredc(c); 631 break; 632 } 633 else 634 stderror(ERR_VARALNUM); 635 } 636 for (;;) { 637 *np++ = (Char) c; 638 c = DgetC(0); 639 if (!alnum(c)) 640 break; 641 if (np >= &name[MAXVARLEN]) 642 stderror(ERR_VARTOOLONG); 643 } 644 *np++ = 0; 645 unDredc(c); 646 vp = adrof(name); 647 } 648 if (bitset) { 649 dolp = (vp || getenv(short2str(name))) ? STR1 : STR0; 650 goto eatbrac; 651 } 652 if (vp == 0) { 653 np = str2short(getenv(short2str(name))); 654 if (np) { 655 fixDolMod(); 656 setDolp(np); 657 goto eatbrac; 658 } 659 udvar(name); 660 /* NOTREACHED */ 661 } 662 c = DgetC(0); 663 upb = blklen(vp->vec); 664 if (dimen == 0 && subscr == 0 && c == '[') { 665 np = name; 666 for (;;) { 667 c = DgetC(DODOL); /* Allow $ expand within [ ] */ 668 if (c == ']') 669 break; 670 if (c == '\n' || c == DEOF) 671 stderror(ERR_INCBR); 672 if (np >= &name[sizeof(name) / sizeof(Char) - 2]) 673 stderror(ERR_VARTOOLONG); 674 *np++ = (Char) c; 675 } 676 *np = 0, np = name; 677 if (dolp || dolcnt) /* $ exp must end before ] */ 678 stderror(ERR_EXPORD); 679 if (!*np) 680 stderror(ERR_SYNTAX); 681 if (Isdigit(*np)) { 682 int i; 683 684 for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0') 685 continue; 686 if ((i < 0 || i > upb) && !any("-*", *np)) { 687 dolerror(vp->v_name); 688 return; 689 } 690 lwb = i; 691 if (!*np) 692 upb = lwb, np = STRstar; 693 } 694 if (*np == '*') 695 np++; 696 else if (*np != '-') 697 stderror(ERR_MISSING, '-'); 698 else { 699 register int i = upb; 700 701 np++; 702 if (Isdigit(*np)) { 703 i = 0; 704 while (Isdigit(*np)) 705 i = i * 10 + *np++ - '0'; 706 if (i < 0 || i > upb) { 707 dolerror(vp->v_name); 708 return; 709 } 710 } 711 if (i < lwb) 712 upb = lwb - 1; 713 else 714 upb = i; 715 } 716 if (lwb == 0) { 717 if (upb != 0) { 718 dolerror(vp->v_name); 719 return; 720 } 721 upb = -1; 722 } 723 if (*np) 724 stderror(ERR_SYNTAX); 725 } 726 else { 727 if (subscr > 0) { 728 if (subscr > upb) 729 lwb = 1, upb = 0; 730 else 731 lwb = upb = subscr; 732 } 733 unDredc(c); 734 } 735 if (dimen) { 736 Char *cp = putn(upb - lwb + 1); 737 738 /* this is a kludge. It prevents Dgetdol() from */ 739 /* pushing erroneous ${#<error> values into the labuf. */ 740 if (sc == '{') { 741 c = Dredc(); 742 if (c != '}') 743 { 744 xfree((ptr_t) cp); 745 stderror(ERR_MISSING, '}'); 746 return; 747 } 748 unDredc(c); 749 } 750 addla(cp); 751 xfree((ptr_t) cp); 752 } 753 else if (length) { 754 int i; 755 Char *cp; 756 for (i = lwb - 1, length = 0; i < upb; i++) 757 length += Strlen(vp->vec[i]); 758 #ifdef notdef 759 /* We don't want that, since we can always compute it by adding $#xxx */ 760 length += i - 1; /* Add the number of spaces in */ 761 #endif 762 cp = putn(length); 763 addla(cp); 764 xfree((ptr_t) cp); 765 } 766 else { 767 eatmod: 768 fixDolMod(); 769 dolnxt = &vp->vec[lwb - 1]; 770 dolcnt = upb - lwb + 1; 771 } 772 eatbrac: 773 if (sc == '{') { 774 c = Dredc(); 775 if (c != '}') 776 stderror(ERR_MISSING, '}'); 777 } 778 } 779 780 static void 781 fixDolMod() 782 { 783 register int c; 784 785 c = DgetC(0); 786 if (c == ':') { 787 #ifndef COMPAT 788 do { 789 #endif /* COMPAT */ 790 c = DgetC(0), dolmcnt = 1, dolwcnt = 1; 791 if (c == 'g' || c == 'a') { 792 if (c == 'g') 793 dolmcnt = 10000; 794 else 795 dolwcnt = 10000; 796 c = DgetC(0); 797 } 798 if ((c == 'g' && dolmcnt != 10000) || 799 (c == 'a' && dolwcnt != 10000)) { 800 if (c == 'g') 801 dolmcnt = 10000; 802 else 803 dolwcnt = 10000; 804 c = DgetC(0); 805 } 806 807 if (c == 's') { /* [eichin:19910926.0755EST] */ 808 int delimcnt = 2; 809 int delim = DgetC(0); 810 dolmod[dolnmod++] = (Char) c; 811 dolmod[dolnmod++] = (Char) delim; 812 813 if (!delim || letter(delim) 814 || Isdigit(delim) || any(" \t\n", delim)) { 815 seterror(ERR_BADSUBST); 816 break; 817 } 818 while ((c = DgetC(0)) != (-1)) { 819 dolmod[dolnmod++] = (Char) c; 820 if(c == delim) delimcnt--; 821 if(!delimcnt) break; 822 } 823 if(delimcnt) { 824 seterror(ERR_BADSUBST); 825 break; 826 } 827 continue; 828 } 829 if (!any("luhtrqxes", c)) 830 stderror(ERR_BADMOD, c); 831 #ifndef COMPAT 832 dolmod[dolnmod++] = (Char) c; 833 #else 834 dolmod = (Char) c; 835 #endif /* COMPAT */ 836 if (c == 'q') 837 dolmcnt = 10000; 838 #ifndef COMPAT 839 } 840 while ((c = DgetC(0)) == ':'); 841 unDredc(c); 842 #endif /* COMPAT */ 843 } 844 else 845 unDredc(c); 846 } 847 848 static void 849 setDolp(cp) 850 register Char *cp; 851 { 852 register Char *dp; 853 #ifndef COMPAT 854 int i; 855 #endif /* COMPAT */ 856 857 #ifdef COMPAT 858 if (dolmod == 0 || dolmcnt == 0) { 859 #else 860 if (dolnmod == 0 || dolmcnt == 0) { 861 #endif /* COMPAT */ 862 dolp = cp; 863 return; 864 } 865 #ifdef COMPAT 866 dp = domod(cp, dolmod); 867 #else 868 dp = cp = Strsave(cp); 869 for (i = 0; i < dolnmod; i++) { 870 /* handle s// [eichin:19910926.0510EST] */ 871 if(dolmod[i] == 's') { 872 int delim; 873 Char *lhsub, *rhsub, *np; 874 size_t lhlen = 0, rhlen = 0; 875 int didmod = 0; 876 877 delim = dolmod[++i]; 878 if (!delim || letter(delim) 879 || Isdigit(delim) || any(" \t\n", delim)) { 880 seterror(ERR_BADSUBST); 881 break; 882 } 883 lhsub = &dolmod[++i]; 884 while(dolmod[i] != delim && dolmod[++i]) { 885 lhlen++; 886 } 887 dolmod[i] = 0; 888 rhsub = &dolmod[++i]; 889 while(dolmod[i] != delim && dolmod[++i]) { 890 rhlen++; 891 } 892 dolmod[i] = 0; 893 894 do { 895 strip(lhsub); 896 strip(cp); 897 dp = Strstr(cp, lhsub); 898 if (dp) { 899 np = (Char *) xmalloc((size_t) 900 ((Strlen(cp) + 1 - lhlen + rhlen) * 901 sizeof(Char))); 902 (void) Strncpy(np, cp, (size_t) (dp - cp)); 903 (void) Strcpy(np + (dp - cp), rhsub); 904 (void) Strcpy(np + (dp - cp) + rhlen, dp + lhlen); 905 906 xfree((ptr_t) cp); 907 dp = cp = np; 908 didmod = 1; 909 } else { 910 /* should this do a seterror? */ 911 break; 912 } 913 } 914 while (dolwcnt == 10000); 915 /* 916 * restore dolmod for additional words 917 */ 918 dolmod[i] = rhsub[-1] = (Char) delim; 919 if (didmod) 920 dolmcnt--; 921 #ifdef notdef 922 else 923 break; 924 #endif 925 } else { 926 int didmod = 0; 927 928 do { 929 if ((dp = domod(cp, dolmod[i])) != NULL) { 930 didmod = 1; 931 if (Strcmp(cp, dp) == 0) { 932 xfree((ptr_t) cp); 933 cp = dp; 934 break; 935 } 936 else { 937 xfree((ptr_t) cp); 938 cp = dp; 939 } 940 } 941 else 942 break; 943 } 944 while (dolwcnt == 10000); 945 dp = cp; 946 if (didmod) 947 dolmcnt--; 948 #ifdef notdef 949 else 950 break; 951 #endif 952 } 953 } 954 #endif /* COMPAT */ 955 956 if (dp) { 957 #ifdef COMPAT 958 dolmcnt--; 959 #endif /* COMPAT */ 960 addla(dp); 961 xfree((ptr_t) dp); 962 } 963 #ifndef COMPAT 964 else 965 addla(cp); 966 #endif /* COMPAT */ 967 968 dolp = STRNULL; 969 if (seterr) 970 stderror(ERR_OLD); 971 } 972 973 static void 974 unDredc(c) 975 int c; 976 { 977 978 Dpeekrd = c; 979 } 980 981 static int 982 Dredc() 983 { 984 register int c; 985 986 if ((c = Dpeekrd) != 0) { 987 Dpeekrd = 0; 988 return (c); 989 } 990 if (Dcp && (c = *Dcp++)) 991 return (c & (QUOTE | TRIM)); 992 if (*Dvp == 0) { 993 Dcp = 0; 994 return (DEOF); 995 } 996 Dcp = *Dvp++; 997 return (' '); 998 } 999 1000 static void 1001 Dtestq(c) 1002 register int c; 1003 { 1004 1005 if (cmap(c, QUOTES)) 1006 gflag = 1; 1007 } 1008 1009 /* 1010 * Form a shell temporary file (in unit 0) from the words 1011 * of the shell input up to EOF or a line the same as "term". 1012 * Unit 0 should have been closed before this call. 1013 */ 1014 void 1015 heredoc(term) 1016 Char *term; 1017 { 1018 register int c; 1019 Char *Dv[2]; 1020 Char obuf[BUFSIZE], lbuf[BUFSIZE], mbuf[BUFSIZE]; 1021 int ocnt, lcnt, mcnt; 1022 register Char *lbp, *obp, *mbp; 1023 Char **vp; 1024 bool quoted; 1025 char *tmp; 1026 1027 tmp = short2str(shtemp); 1028 #ifndef O_CREAT 1029 # define O_CREAT 0 1030 if (creat(tmp, 0600) < 0) 1031 stderror(ERR_SYSTEM, tmp, strerror(errno)); 1032 #endif 1033 (void) close(0); 1034 #ifndef O_TEMPORARY 1035 # define O_TEMPORARY 0 1036 #endif 1037 if (open(tmp, O_RDWR|O_CREAT|O_TEMPORARY) < 0) { 1038 int oerrno = errno; 1039 1040 (void) unlink(tmp); 1041 errno = oerrno; 1042 stderror(ERR_SYSTEM, tmp, strerror(errno)); 1043 } 1044 (void) unlink(tmp); /* 0 0 inode! */ 1045 Dv[0] = term; 1046 Dv[1] = NULL; 1047 gflag = 0; 1048 trim(Dv); 1049 rscan(Dv, Dtestq); 1050 quoted = gflag; 1051 ocnt = BUFSIZE; 1052 obp = obuf; 1053 inheredoc = 1; 1054 #ifdef WINNT 1055 __dup_stdin = 1; 1056 #endif /* WINNT */ 1057 for (;;) { 1058 /* 1059 * Read up a line 1060 */ 1061 lbp = lbuf; 1062 lcnt = BUFSIZE - 4; 1063 for (;;) { 1064 c = readc(1); /* 1 -> Want EOF returns */ 1065 if (c < 0 || c == '\n') 1066 break; 1067 if ((c &= TRIM) != 0) { 1068 *lbp++ = (Char) c; 1069 if (--lcnt < 0) { 1070 setname("<<"); 1071 stderror(ERR_NAME | ERR_OVERFLOW); 1072 } 1073 } 1074 } 1075 *lbp = 0; 1076 1077 /* 1078 * Check for EOF or compare to terminator -- before expansion 1079 */ 1080 if (c < 0 || eq(lbuf, term)) { 1081 (void) write(0, short2str(obuf), (size_t) (BUFSIZE - ocnt)); 1082 (void) lseek(0, (off_t) 0, L_SET); 1083 inheredoc = 0; 1084 return; 1085 } 1086 1087 /* 1088 * If term was quoted or -n just pass it on 1089 */ 1090 if (quoted || noexec) { 1091 *lbp++ = '\n'; 1092 *lbp = 0; 1093 for (lbp = lbuf; (c = *lbp++) != 0;) { 1094 *obp++ = (Char) c; 1095 if (--ocnt == 0) { 1096 (void) write(0, short2str(obuf), BUFSIZE); 1097 obp = obuf; 1098 ocnt = BUFSIZE; 1099 } 1100 } 1101 continue; 1102 } 1103 1104 /* 1105 * Term wasn't quoted so variable and then command expand the input 1106 * line 1107 */ 1108 Dcp = lbuf; 1109 Dvp = Dv + 1; 1110 mbp = mbuf; 1111 mcnt = BUFSIZE - 4; 1112 for (;;) { 1113 c = DgetC(DODOL); 1114 if (c == DEOF) 1115 break; 1116 if ((c &= TRIM) == 0) 1117 continue; 1118 /* \ quotes \ $ ` here */ 1119 if (c == '\\') { 1120 c = DgetC(0); 1121 if (!any("$\\`", c)) 1122 unDgetC(c | QUOTE), c = '\\'; 1123 else 1124 c |= QUOTE; 1125 } 1126 *mbp++ = (Char) c; 1127 if (--mcnt == 0) { 1128 setname("<<"); 1129 stderror(ERR_NAME | ERR_OVERFLOW); 1130 } 1131 } 1132 *mbp++ = 0; 1133 1134 /* 1135 * If any ` in line do command substitution 1136 */ 1137 mbp = mbuf; 1138 if (Strchr(mbp, '`') != NULL) { 1139 /* 1140 * 1 arg to dobackp causes substitution to be literal. Words are 1141 * broken only at newlines so that all blanks and tabs are 1142 * preserved. Blank lines (null words) are not discarded. 1143 */ 1144 vp = dobackp(mbuf, 1); 1145 } 1146 else 1147 /* Setup trivial vector similar to return of dobackp */ 1148 Dv[0] = mbp, Dv[1] = NULL, vp = Dv; 1149 1150 /* 1151 * Resurrect the words from the command substitution each separated by 1152 * a newline. Note that the last newline of a command substitution 1153 * will have been discarded, but we put a newline after the last word 1154 * because this represents the newline after the last input line! 1155 */ 1156 for (; *vp; vp++) { 1157 for (mbp = *vp; *mbp; mbp++) { 1158 *obp++ = *mbp & TRIM; 1159 if (--ocnt == 0) { 1160 (void) write(0, short2str(obuf), BUFSIZE); 1161 obp = obuf; 1162 ocnt = BUFSIZE; 1163 } 1164 } 1165 *obp++ = '\n'; 1166 if (--ocnt == 0) { 1167 (void) write(0, short2str(obuf), BUFSIZE); 1168 obp = obuf; 1169 ocnt = BUFSIZE; 1170 } 1171 } 1172 if (pargv) 1173 blkfree(pargv), pargv = 0; 1174 } 1175 } 1176