1 /* $Header: /src/pub/tcsh/sh.dol.c,v 3.45 2000/11/19 20:50:43 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.45 2000/11/19 20:50:43 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 #if defined(DSPMBYTE) 229 mbyteskip: 230 #endif /* DSPMBYTE */ 231 if (--i <= 0) 232 stderror(ERR_WTOOLONG); 233 *wp++ = (Char) c; 234 } 235 } 236 237 /* 238 * Get a word. This routine is analogous to the routine 239 * word() in sh.lex.c for the main lexical input. One difference 240 * here is that we don't get a newline to terminate our expansion. 241 * Rather, DgetC will return a DEOF when we hit the end-of-input. 242 */ 243 static int 244 Dword() 245 { 246 register int c, c1; 247 Char wbuf[BUFSIZE]; 248 register Char *wp = wbuf; 249 register int i = MAXWLEN; 250 register bool dolflg; 251 bool sofar = 0, done = 0; 252 253 while (!done) { 254 done = 1; 255 c = DgetC(DODOL); 256 switch (c) { 257 258 case DEOF: 259 if (sofar == 0) 260 return (0); 261 /* finish this word and catch the code above the next time */ 262 unDredc(c); 263 /*FALLTHROUGH*/ 264 265 case '\n': 266 *wp = 0; 267 Gcat(STRNULL, wbuf); 268 return (1); 269 270 case ' ': 271 case '\t': 272 done = 0; 273 break; 274 275 case '`': 276 /* We preserve ` quotations which are done yet later */ 277 *wp++ = (Char) c, --i; 278 /*FALLTHROUGH*/ 279 case '\'': 280 case '"': 281 /* 282 * Note that DgetC never returns a QUOTES character from an 283 * expansion, so only true input quotes will get us here or out. 284 */ 285 c1 = c; 286 dolflg = c1 == '"' ? DODOL : 0; 287 for (;;) { 288 c = DgetC(dolflg); 289 if (c == c1) 290 break; 291 if (c == '\n' || c == DEOF) 292 stderror(ERR_UNMATCHED, c1); 293 if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) { 294 if ((wp[-1] & TRIM) == '\\') 295 --wp; 296 ++i; 297 } 298 if (--i <= 0) 299 stderror(ERR_WTOOLONG); 300 switch (c1) { 301 302 case '"': 303 /* 304 * Leave any `s alone for later. Other chars are all 305 * quoted, thus `...` can tell it was within "...". 306 */ 307 *wp++ = c == '`' ? '`' : c | QUOTE; 308 break; 309 310 case '\'': 311 /* Prevent all further interpretation */ 312 *wp++ = c | QUOTE; 313 break; 314 315 case '`': 316 /* Leave all text alone for later */ 317 *wp++ = (Char) c; 318 break; 319 320 default: 321 break; 322 } 323 } 324 if (c1 == '`') 325 *wp++ = '`' /* i--; eliminated */; 326 sofar = 1; 327 if ((wp = Dpack(wbuf, wp)) == NULL) 328 return (1); 329 else { 330 #ifdef masscomp 331 /* 332 * Avoid a nasty message from the RTU 4.1A & RTU 5.0 compiler concerning 333 * the "overuse of registers". According to the compiler release notes, 334 * incorrect code may be produced unless the offending expression is 335 * rewritten. Therefore, we can't just ignore it, DAS DEC-90. 336 */ 337 i = MAXWLEN; 338 i -= (int) (wp - wbuf); 339 #else /* !masscomp */ 340 i = MAXWLEN - (int) (wp - wbuf); 341 #endif /* masscomp */ 342 done = 0; 343 } 344 break; 345 346 case '\\': 347 c = DgetC(0); /* No $ subst! */ 348 if (c == '\n' || c == DEOF) { 349 done = 0; 350 break; 351 } 352 c |= QUOTE; 353 break; 354 355 default: 356 break; 357 } 358 if (done) { 359 unDgetC(c); 360 sofar = 1; 361 if ((wp = Dpack(wbuf, wp)) == NULL) 362 return (1); 363 else { 364 #ifdef masscomp 365 /* 366 * Avoid a nasty message from the RTU 4.1A & RTU 5.0 compiler concerning 367 * the "overuse of registers". According to the compiler release notes, 368 * incorrect code may be produced unless the offending expression is 369 * rewritten. Therefore, we can't just ignore it, DAS DEC-90. 370 */ 371 i = MAXWLEN; 372 i -= (int) (wp - wbuf); 373 #else /* !masscomp */ 374 i = MAXWLEN - (int) (wp - wbuf); 375 #endif /* masscomp */ 376 done = 0; 377 } 378 } 379 } 380 /* Really NOTREACHED */ 381 return (0); 382 } 383 384 385 /* 386 * Get a character, performing $ substitution unless flag is 0. 387 * Any QUOTES character which is returned from a $ expansion is 388 * QUOTEd so that it will not be recognized above. 389 */ 390 static int 391 DgetC(flag) 392 register int flag; 393 { 394 register int c; 395 396 top: 397 if ((c = Dpeekc) != 0) { 398 Dpeekc = 0; 399 return (c); 400 } 401 if (lap) { 402 c = *lap++ & (QUOTE | TRIM); 403 if (c == 0) { 404 lap = 0; 405 goto top; 406 } 407 quotspec: 408 if (cmap(c, QUOTES)) 409 return (c | QUOTE); 410 return (c); 411 } 412 if (dolp) { 413 if ((c = *dolp++ & (QUOTE | TRIM)) != 0) 414 goto quotspec; 415 if (dolcnt > 0) { 416 setDolp(*dolnxt++); 417 --dolcnt; 418 return (' '); 419 } 420 dolp = 0; 421 } 422 if (dolcnt > 0) { 423 setDolp(*dolnxt++); 424 --dolcnt; 425 goto top; 426 } 427 c = Dredc(); 428 if (c == '$' && flag) { 429 Dgetdol(); 430 goto top; 431 } 432 return (c); 433 } 434 435 static Char *nulvec[] = { NULL }; 436 static struct varent nulargv = {nulvec, STRargv, VAR_READWRITE, 437 { NULL, NULL, NULL }, 0 }; 438 439 static void 440 dolerror(s) 441 Char *s; 442 { 443 setname(short2str(s)); 444 stderror(ERR_NAME | ERR_RANGE); 445 } 446 447 /* 448 * Handle the multitudinous $ expansion forms. 449 * Ugh. 450 */ 451 static void 452 Dgetdol() 453 { 454 register Char *np; 455 register struct varent *vp = NULL; 456 Char name[4 * MAXVARLEN + 1]; 457 int c, sc; 458 int subscr = 0, lwb = 1, upb = 0; 459 bool dimen = 0, bitset = 0, length = 0; 460 char tnp; 461 Char wbuf[BUFSIZE]; 462 static Char *dolbang = NULL; 463 464 #ifdef COMPAT 465 dolmod = dolmcnt = dolwcnt = 0; 466 #else 467 dolnmod = dolmcnt = dolwcnt = 0; 468 #endif /* COMPAT */ 469 c = sc = DgetC(0); 470 if (c == '{') 471 c = DgetC(0); /* sc is { to take } later */ 472 if ((c & TRIM) == '#') 473 dimen++, c = DgetC(0); /* $# takes dimension */ 474 else if (c == '?') 475 bitset++, c = DgetC(0); /* $? tests existence */ 476 else if (c == '%') 477 length++, c = DgetC(0); /* $% returns length in chars */ 478 switch (c) { 479 480 case '!': 481 if (dimen || bitset || length) 482 stderror(ERR_SYNTAX); 483 if (backpid != 0) { 484 if (dolbang) 485 xfree((ptr_t) dolbang); 486 setDolp(dolbang = putn(backpid)); 487 } 488 goto eatbrac; 489 490 case '$': 491 if (dimen || bitset || length) 492 stderror(ERR_SYNTAX); 493 setDolp(doldol); 494 goto eatbrac; 495 496 #ifdef COHERENT 497 /* Coherent compiler doesn't allow case-labels that are not 498 constant-expressions */ 499 #ifdef SHORT_STRINGS 500 case 0100074: 501 #else /* !SHORT_STRINGS */ 502 case 0274: 503 #endif 504 #else /* !COHERENT */ 505 case '<'|QUOTE: 506 #endif 507 if (bitset) 508 stderror(ERR_NOTALLOWED, "$?<"); 509 if (dimen) 510 stderror(ERR_NOTALLOWED, "$#<"); 511 if (length) 512 stderror(ERR_NOTALLOWED, "$%<"); 513 { 514 #ifdef BSDSIGS 515 sigmask_t omask = sigsetmask(sigblock(0) & ~sigmask(SIGINT)); 516 #else /* !BSDSIGS */ 517 (void) sigrelse(SIGINT); 518 #endif /* BSDSIGS */ 519 for (np = wbuf; force_read(OLDSTD, &tnp, 1) == 1; np++) { 520 *np = (unsigned char) tnp; 521 if (np >= &wbuf[BUFSIZE - 1]) 522 stderror(ERR_LTOOLONG); 523 if (tnp == '\n') 524 break; 525 } 526 *np = 0; 527 #ifdef BSDSIGS 528 (void) sigsetmask(omask); 529 #else /* !BSDSIGS */ 530 (void) sighold(SIGINT); 531 #endif /* BSDSIGS */ 532 } 533 534 #ifdef COMPAT 535 /* 536 * KLUDGE: dolmod is set here because it will cause setDolp to call 537 * domod and thus to copy wbuf. Otherwise setDolp would use it 538 * directly. If we saved it ourselves, no one would know when to free 539 * it. The actual function of the 'q' causes filename expansion not to 540 * be done on the interpolated value. 541 */ 542 /* 543 * If we do that, then other modifiers don't work. 544 * in addition, let the user specify :q if wanted 545 * [christos] 546 */ 547 /*old*/ dolmod = 'q'; 548 /*new*/ dolmod[dolnmod++] = 'q'; 549 dolmcnt = 10000; 550 #endif /* COMPAT */ 551 552 fixDolMod(); 553 setDolp(wbuf); 554 goto eatbrac; 555 556 case '*': 557 (void) Strcpy(name, STRargv); 558 vp = adrof(STRargv); 559 subscr = -1; /* Prevent eating [...] */ 560 break; 561 562 case DEOF: 563 case '\n': 564 np = dimen ? STRargv : (bitset ? STRstatus : NULL); 565 if (np) { 566 bitset = 0; 567 (void) Strcpy(name, np); 568 vp = adrof(np); 569 subscr = -1; /* Prevent eating [...] */ 570 unDredc(c); 571 break; 572 } 573 else 574 stderror(ERR_SYNTAX); 575 /*NOTREACHED*/ 576 577 default: 578 np = name; 579 if (Isdigit(c)) { 580 if (dimen) 581 stderror(ERR_NOTALLOWED, "$#<num>"); 582 subscr = 0; 583 do { 584 subscr = subscr * 10 + c - '0'; 585 c = DgetC(0); 586 } while (Isdigit(c)); 587 unDredc(c); 588 if (subscr < 0) { 589 dolerror(vp->v_name); 590 return; 591 } 592 if (subscr == 0) { 593 if (bitset) { 594 dolp = dolzero ? STR1 : STR0; 595 goto eatbrac; 596 } 597 if (ffile == 0) 598 stderror(ERR_DOLZERO); 599 if (length) { 600 Char *cp; 601 length = Strlen(ffile); 602 cp = putn(length); 603 addla(cp); 604 xfree((ptr_t) cp); 605 } 606 else { 607 fixDolMod(); 608 setDolp(ffile); 609 } 610 goto eatbrac; 611 } 612 #if 0 613 if (bitset) 614 stderror(ERR_NOTALLOWED, "$?<num>"); 615 if (length) 616 stderror(ERR_NOTALLOWED, "$%<num>"); 617 #endif 618 vp = adrof(STRargv); 619 if (vp == 0) { 620 vp = &nulargv; 621 goto eatmod; 622 } 623 break; 624 } 625 if (!alnum(c)) { 626 np = dimen ? STRargv : (bitset ? STRstatus : NULL); 627 if (np) { 628 bitset = 0; 629 (void) Strcpy(name, np); 630 vp = adrof(np); 631 subscr = -1; /* Prevent eating [...] */ 632 unDredc(c); 633 break; 634 } 635 else 636 stderror(ERR_VARALNUM); 637 } 638 for (;;) { 639 *np++ = (Char) c; 640 c = DgetC(0); 641 if (!alnum(c)) 642 break; 643 if (np >= &name[MAXVARLEN]) 644 stderror(ERR_VARTOOLONG); 645 } 646 *np++ = 0; 647 unDredc(c); 648 vp = adrof(name); 649 } 650 if (bitset) { 651 dolp = (vp || getenv(short2str(name))) ? STR1 : STR0; 652 goto eatbrac; 653 } 654 if (vp == 0) { 655 np = str2short(getenv(short2str(name))); 656 if (np) { 657 fixDolMod(); 658 setDolp(np); 659 goto eatbrac; 660 } 661 udvar(name); 662 /* NOTREACHED */ 663 } 664 c = DgetC(0); 665 upb = blklen(vp->vec); 666 if (dimen == 0 && subscr == 0 && c == '[') { 667 np = name; 668 for (;;) { 669 c = DgetC(DODOL); /* Allow $ expand within [ ] */ 670 if (c == ']') 671 break; 672 if (c == '\n' || c == DEOF) 673 stderror(ERR_INCBR); 674 if (np >= &name[sizeof(name) / sizeof(Char) - 2]) 675 stderror(ERR_VARTOOLONG); 676 *np++ = (Char) c; 677 } 678 *np = 0, np = name; 679 if (dolp || dolcnt) /* $ exp must end before ] */ 680 stderror(ERR_EXPORD); 681 if (!*np) 682 stderror(ERR_SYNTAX); 683 if (Isdigit(*np)) { 684 int i; 685 686 for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0') 687 continue; 688 if ((i < 0 || i > upb) && !any("-*", *np)) { 689 dolerror(vp->v_name); 690 return; 691 } 692 lwb = i; 693 if (!*np) 694 upb = lwb, np = STRstar; 695 } 696 if (*np == '*') 697 np++; 698 else if (*np != '-') 699 stderror(ERR_MISSING, '-'); 700 else { 701 register int i = upb; 702 703 np++; 704 if (Isdigit(*np)) { 705 i = 0; 706 while (Isdigit(*np)) 707 i = i * 10 + *np++ - '0'; 708 if (i < 0 || i > upb) { 709 dolerror(vp->v_name); 710 return; 711 } 712 } 713 if (i < lwb) 714 upb = lwb - 1; 715 else 716 upb = i; 717 } 718 if (lwb == 0) { 719 if (upb != 0) { 720 dolerror(vp->v_name); 721 return; 722 } 723 upb = -1; 724 } 725 if (*np) 726 stderror(ERR_SYNTAX); 727 } 728 else { 729 if (subscr > 0) { 730 if (subscr > upb) 731 lwb = 1, upb = 0; 732 else 733 lwb = upb = subscr; 734 } 735 unDredc(c); 736 } 737 if (dimen) { 738 Char *cp = putn(upb - lwb + 1); 739 740 /* this is a kludge. It prevents Dgetdol() from */ 741 /* pushing erroneous ${#<error> values into the labuf. */ 742 if (sc == '{') { 743 c = Dredc(); 744 if (c != '}') 745 { 746 xfree((ptr_t) cp); 747 stderror(ERR_MISSING, '}'); 748 return; 749 } 750 unDredc(c); 751 } 752 addla(cp); 753 xfree((ptr_t) cp); 754 } 755 else if (length) { 756 int i; 757 Char *cp; 758 for (i = lwb - 1, length = 0; i < upb; i++) 759 length += Strlen(vp->vec[i]); 760 #ifdef notdef 761 /* We don't want that, since we can always compute it by adding $#xxx */ 762 length += i - 1; /* Add the number of spaces in */ 763 #endif 764 cp = putn(length); 765 addla(cp); 766 xfree((ptr_t) cp); 767 } 768 else { 769 eatmod: 770 fixDolMod(); 771 dolnxt = &vp->vec[lwb - 1]; 772 dolcnt = upb - lwb + 1; 773 } 774 eatbrac: 775 if (sc == '{') { 776 c = Dredc(); 777 if (c != '}') 778 stderror(ERR_MISSING, '}'); 779 } 780 } 781 782 static void 783 fixDolMod() 784 { 785 register int c; 786 787 c = DgetC(0); 788 if (c == ':') { 789 #ifndef COMPAT 790 do { 791 #endif /* COMPAT */ 792 c = DgetC(0), dolmcnt = 1, dolwcnt = 1; 793 if (c == 'g' || c == 'a') { 794 if (c == 'g') 795 dolmcnt = 10000; 796 else 797 dolwcnt = 10000; 798 c = DgetC(0); 799 } 800 if ((c == 'g' && dolmcnt != 10000) || 801 (c == 'a' && dolwcnt != 10000)) { 802 if (c == 'g') 803 dolmcnt = 10000; 804 else 805 dolwcnt = 10000; 806 c = DgetC(0); 807 } 808 809 if (c == 's') { /* [eichin:19910926.0755EST] */ 810 int delimcnt = 2; 811 int delim = DgetC(0); 812 dolmod[dolnmod++] = (Char) c; 813 dolmod[dolnmod++] = (Char) delim; 814 815 if (!delim || letter(delim) 816 || Isdigit(delim) || any(" \t\n", delim)) { 817 seterror(ERR_BADSUBST); 818 break; 819 } 820 while ((c = DgetC(0)) != (-1)) { 821 dolmod[dolnmod++] = (Char) c; 822 if(c == delim) delimcnt--; 823 if(!delimcnt) break; 824 } 825 if(delimcnt) { 826 seterror(ERR_BADSUBST); 827 break; 828 } 829 continue; 830 } 831 if (!any("luhtrqxes", c)) 832 stderror(ERR_BADMOD, c); 833 #ifndef COMPAT 834 dolmod[dolnmod++] = (Char) c; 835 #else 836 dolmod = (Char) c; 837 #endif /* COMPAT */ 838 if (c == 'q') 839 dolmcnt = 10000; 840 #ifndef COMPAT 841 } 842 while ((c = DgetC(0)) == ':'); 843 unDredc(c); 844 #endif /* COMPAT */ 845 } 846 else 847 unDredc(c); 848 } 849 850 static void 851 setDolp(cp) 852 register Char *cp; 853 { 854 register Char *dp; 855 #ifndef COMPAT 856 int i; 857 #endif /* COMPAT */ 858 859 #ifdef COMPAT 860 if (dolmod == 0 || dolmcnt == 0) { 861 #else 862 if (dolnmod == 0 || dolmcnt == 0) { 863 #endif /* COMPAT */ 864 dolp = cp; 865 return; 866 } 867 #ifdef COMPAT 868 dp = domod(cp, dolmod); 869 #else 870 dp = cp = Strsave(cp); 871 for (i = 0; i < dolnmod; i++) { 872 /* handle s// [eichin:19910926.0510EST] */ 873 if(dolmod[i] == 's') { 874 int delim; 875 Char *lhsub, *rhsub, *np; 876 size_t lhlen = 0, rhlen = 0; 877 int didmod = 0; 878 879 delim = dolmod[++i]; 880 if (!delim || letter(delim) 881 || Isdigit(delim) || any(" \t\n", delim)) { 882 seterror(ERR_BADSUBST); 883 break; 884 } 885 lhsub = &dolmod[++i]; 886 while(dolmod[i] != delim && dolmod[++i]) { 887 lhlen++; 888 } 889 dolmod[i] = 0; 890 rhsub = &dolmod[++i]; 891 while(dolmod[i] != delim && dolmod[++i]) { 892 rhlen++; 893 } 894 dolmod[i] = 0; 895 896 do { 897 strip(lhsub); 898 strip(cp); 899 dp = Strstr(cp, lhsub); 900 if (dp) { 901 np = (Char *) xmalloc((size_t) 902 ((Strlen(cp) + 1 - lhlen + rhlen) * 903 sizeof(Char))); 904 (void) Strncpy(np, cp, (size_t) (dp - cp)); 905 (void) Strcpy(np + (dp - cp), rhsub); 906 (void) Strcpy(np + (dp - cp) + rhlen, dp + lhlen); 907 908 xfree((ptr_t) cp); 909 dp = cp = np; 910 didmod = 1; 911 } else { 912 /* should this do a seterror? */ 913 break; 914 } 915 } 916 while (dolwcnt == 10000); 917 /* 918 * restore dolmod for additional words 919 */ 920 dolmod[i] = rhsub[-1] = (Char) delim; 921 if (didmod) 922 dolmcnt--; 923 #ifdef notdef 924 else 925 break; 926 #endif 927 } else { 928 int didmod = 0; 929 930 do { 931 if ((dp = domod(cp, dolmod[i])) != NULL) { 932 didmod = 1; 933 if (Strcmp(cp, dp) == 0) { 934 xfree((ptr_t) cp); 935 cp = dp; 936 break; 937 } 938 else { 939 xfree((ptr_t) cp); 940 cp = dp; 941 } 942 } 943 else 944 break; 945 } 946 while (dolwcnt == 10000); 947 dp = cp; 948 if (didmod) 949 dolmcnt--; 950 #ifdef notdef 951 else 952 break; 953 #endif 954 } 955 } 956 #endif /* COMPAT */ 957 958 if (dp) { 959 #ifdef COMPAT 960 dolmcnt--; 961 #endif /* COMPAT */ 962 addla(dp); 963 xfree((ptr_t) dp); 964 } 965 #ifndef COMPAT 966 else 967 addla(cp); 968 #endif /* COMPAT */ 969 970 dolp = STRNULL; 971 if (seterr) 972 stderror(ERR_OLD); 973 } 974 975 static void 976 unDredc(c) 977 int c; 978 { 979 980 Dpeekrd = c; 981 } 982 983 static int 984 Dredc() 985 { 986 register int c; 987 988 if ((c = Dpeekrd) != 0) { 989 Dpeekrd = 0; 990 return (c); 991 } 992 if (Dcp && (c = *Dcp++)) 993 return (c & (QUOTE | TRIM)); 994 if (*Dvp == 0) { 995 Dcp = 0; 996 return (DEOF); 997 } 998 Dcp = *Dvp++; 999 return (' '); 1000 } 1001 1002 static void 1003 Dtestq(c) 1004 register int c; 1005 { 1006 1007 if (cmap(c, QUOTES)) 1008 gflag = 1; 1009 } 1010 1011 /* 1012 * Form a shell temporary file (in unit 0) from the words 1013 * of the shell input up to EOF or a line the same as "term". 1014 * Unit 0 should have been closed before this call. 1015 */ 1016 void 1017 heredoc(term) 1018 Char *term; 1019 { 1020 int c; 1021 Char *Dv[2]; 1022 Char obuf[BUFSIZE], lbuf[BUFSIZE], mbuf[BUFSIZE]; 1023 int ocnt, lcnt, mcnt; 1024 register Char *lbp, *obp, *mbp; 1025 Char **vp; 1026 bool quoted; 1027 char *tmp; 1028 #ifndef WINNT_NATIVE 1029 struct timeval tv; 1030 1031 again: 1032 #endif /* WINNT_NATIVE */ 1033 tmp = short2str(shtemp); 1034 #ifndef O_CREAT 1035 # define O_CREAT 0 1036 if (creat(tmp, 0600) < 0) 1037 stderror(ERR_SYSTEM, tmp, strerror(errno)); 1038 #endif 1039 (void) close(0); 1040 #ifndef O_TEMPORARY 1041 # define O_TEMPORARY 0 1042 #endif 1043 #ifndef O_EXCL 1044 # define O_EXCL 0 1045 #endif 1046 if (open(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY) == -1) { 1047 int oerrno = errno; 1048 #ifndef WINNT_NATIVE 1049 if (errno == EEXIST) { 1050 if (unlink(tmp) == -1) { 1051 (void) gettimeofday(&tv, NULL); 1052 shtemp = Strspl(STRtmpsh, putn((((int)tv.tv_sec) ^ 1053 ((int)tv.tv_usec) ^ ((int)getpid())) & 0x00ffffff)); 1054 } 1055 goto again; 1056 } 1057 #endif /* WINNT_NATIVE */ 1058 (void) unlink(tmp); 1059 errno = oerrno; 1060 stderror(ERR_SYSTEM, tmp, strerror(errno)); 1061 } 1062 (void) unlink(tmp); /* 0 0 inode! */ 1063 Dv[0] = term; 1064 Dv[1] = NULL; 1065 gflag = 0; 1066 trim(Dv); 1067 rscan(Dv, Dtestq); 1068 quoted = gflag; 1069 ocnt = BUFSIZE; 1070 obp = obuf; 1071 inheredoc = 1; 1072 #ifdef WINNT_NATIVE 1073 __dup_stdin = 1; 1074 #endif /* WINNT_NATIVE */ 1075 for (;;) { 1076 /* 1077 * Read up a line 1078 */ 1079 lbp = lbuf; 1080 lcnt = BUFSIZE - 4; 1081 for (;;) { 1082 c = readc(1); /* 1 -> Want EOF returns */ 1083 if (c < 0 || c == '\n') 1084 break; 1085 if ((c &= TRIM) != 0) { 1086 *lbp++ = (Char) c; 1087 if (--lcnt < 0) { 1088 setname("<<"); 1089 stderror(ERR_NAME | ERR_OVERFLOW); 1090 } 1091 } 1092 } 1093 *lbp = 0; 1094 1095 /* 1096 * Check for EOF or compare to terminator -- before expansion 1097 */ 1098 if (c < 0 || eq(lbuf, term)) { 1099 (void) write(0, short2str(obuf), (size_t) (BUFSIZE - ocnt)); 1100 (void) lseek(0, (off_t) 0, L_SET); 1101 inheredoc = 0; 1102 return; 1103 } 1104 1105 /* 1106 * If term was quoted or -n just pass it on 1107 */ 1108 if (quoted || noexec) { 1109 *lbp++ = '\n'; 1110 *lbp = 0; 1111 for (lbp = lbuf; (c = *lbp++) != 0;) { 1112 *obp++ = (Char) c; 1113 if (--ocnt == 0) { 1114 (void) write(0, short2str(obuf), BUFSIZE); 1115 obp = obuf; 1116 ocnt = BUFSIZE; 1117 } 1118 } 1119 continue; 1120 } 1121 1122 /* 1123 * Term wasn't quoted so variable and then command expand the input 1124 * line 1125 */ 1126 Dcp = lbuf; 1127 Dvp = Dv + 1; 1128 mbp = mbuf; 1129 mcnt = BUFSIZE - 4; 1130 for (;;) { 1131 c = DgetC(DODOL); 1132 if (c == DEOF) 1133 break; 1134 if ((c &= TRIM) == 0) 1135 continue; 1136 /* \ quotes \ $ ` here */ 1137 if (c == '\\') { 1138 c = DgetC(0); 1139 if (!any("$\\`", c)) 1140 unDgetC(c | QUOTE), c = '\\'; 1141 else 1142 c |= QUOTE; 1143 } 1144 *mbp++ = (Char) c; 1145 if (--mcnt == 0) { 1146 setname("<<"); 1147 stderror(ERR_NAME | ERR_OVERFLOW); 1148 } 1149 } 1150 *mbp++ = 0; 1151 1152 /* 1153 * If any ` in line do command substitution 1154 */ 1155 mbp = mbuf; 1156 if (Strchr(mbp, '`') != NULL) { 1157 /* 1158 * 1 arg to dobackp causes substitution to be literal. Words are 1159 * broken only at newlines so that all blanks and tabs are 1160 * preserved. Blank lines (null words) are not discarded. 1161 */ 1162 vp = dobackp(mbuf, 1); 1163 } 1164 else 1165 /* Setup trivial vector similar to return of dobackp */ 1166 Dv[0] = mbp, Dv[1] = NULL, vp = Dv; 1167 1168 /* 1169 * Resurrect the words from the command substitution each separated by 1170 * a newline. Note that the last newline of a command substitution 1171 * will have been discarded, but we put a newline after the last word 1172 * because this represents the newline after the last input line! 1173 */ 1174 for (; *vp; vp++) { 1175 for (mbp = *vp; *mbp; mbp++) { 1176 *obp++ = *mbp & TRIM; 1177 if (--ocnt == 0) { 1178 (void) write(0, short2str(obuf), BUFSIZE); 1179 obp = obuf; 1180 ocnt = BUFSIZE; 1181 } 1182 } 1183 *obp++ = '\n'; 1184 if (--ocnt == 0) { 1185 (void) write(0, short2str(obuf), BUFSIZE); 1186 obp = obuf; 1187 ocnt = BUFSIZE; 1188 } 1189 } 1190 if (pargv) 1191 blkfree(pargv), pargv = 0; 1192 } 1193 } 1194