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