1 /* $Header: /p/tcsh/cvsroot/tcsh/sh.dol.c,v 3.70 2006/09/14 18:30:16 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("$tcsh: sh.dol.c,v 3.70 2006/09/14 18:30:16 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 Char Dpeekc; /* Peek for DgetC */ 51 static eChar Dpeekrd; /* Peek for Dreadc */ 52 static Char *Dcp, *const *Dvp; /* Input vector for Dreadc */ 53 54 #define DEOF CHAR_ERR 55 56 #define unDgetC(c) Dpeekc = c 57 58 #define QUOTES (_QF|_QB|_ESC) /* \ ' " ` */ 59 60 /* 61 * The following variables give the information about the current 62 * $ expansion, recording the current word position, the remaining 63 * words within this expansion, the count of remaining words, and the 64 * information about any : modifier which is being applied. 65 */ 66 static Char *dolp; /* Remaining chars from this word */ 67 static Char **dolnxt; /* Further words */ 68 static int dolcnt; /* Count of further words */ 69 static struct Strbuf dolmod; /* = Strbuf_INIT; : modifier characters */ 70 static int dolmcnt; /* :gx -> INT_MAX, else 1 */ 71 static int dol_flag_a; /* :ax -> 1, else 0 */ 72 73 static Char **Dfix2 (Char *const *); 74 static int Dpack (struct Strbuf *); 75 static int Dword (struct blk_buf *); 76 static void dolerror (Char *); 77 static eChar DgetC (int); 78 static void Dgetdol (void); 79 static void fixDolMod (void); 80 static void setDolp (Char *); 81 static void unDredc (eChar); 82 static eChar Dredc (void); 83 static void Dtestq (Char); 84 85 /* 86 * Fix up the $ expansions and quotations in the 87 * argument list to command t. 88 */ 89 void 90 Dfix(struct command *t) 91 { 92 Char **pp; 93 Char *p; 94 95 if (noexec) 96 return; 97 /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */ 98 for (pp = t->t_dcom; (p = *pp++) != NULL;) { 99 for (; *p; p++) { 100 if (cmap(*p, _DOL | QUOTES)) { /* $, \, ', ", ` */ 101 Char **expanded; 102 103 expanded = Dfix2(t->t_dcom); /* found one */ 104 blkfree(t->t_dcom); 105 t->t_dcom = expanded; 106 return; 107 } 108 } 109 } 110 } 111 112 /* 113 * $ substitute one word, for i/o redirection 114 */ 115 Char * 116 Dfix1(Char *cp) 117 { 118 Char *Dv[2], **expanded; 119 120 if (noexec) 121 return (0); 122 Dv[0] = cp; 123 Dv[1] = NULL; 124 expanded = Dfix2(Dv); 125 if (expanded[0] == NULL || expanded[1] != NULL) { 126 blkfree(expanded); 127 setname(short2str(cp)); 128 stderror(ERR_NAME | ERR_AMBIG); 129 } 130 cp = Strsave(expanded[0]); 131 blkfree(expanded); 132 return (cp); 133 } 134 135 /* 136 * Subroutine to do actual fixing after state initialization. 137 */ 138 static Char ** 139 Dfix2(Char *const *v) 140 { 141 struct blk_buf bb = BLK_BUF_INIT; 142 143 Dvp = v; 144 Dcp = STRNULL; /* Setup input vector for Dreadc */ 145 unDgetC(0); 146 unDredc(0); /* Clear out any old peeks (at error) */ 147 dolp = 0; 148 dolcnt = 0; /* Clear out residual $ expands (...) */ 149 cleanup_push(&bb, bb_cleanup); 150 while (Dword(&bb)) 151 continue; 152 cleanup_ignore(&bb); 153 cleanup_until(&bb); 154 return bb_finish(&bb); 155 } 156 157 /* 158 * Pack up more characters in this word 159 */ 160 static int 161 Dpack(struct Strbuf *wbuf) 162 { 163 eChar c; 164 165 for (;;) { 166 c = DgetC(DODOL); 167 if (c == '\\') { 168 c = DgetC(0); 169 if (c == DEOF) { 170 unDredc(c); 171 return 1; 172 } 173 if (c == '\n') 174 c = ' '; 175 else 176 c |= QUOTE; 177 } 178 if (c == DEOF) { 179 unDredc(c); 180 return 1; 181 } 182 if (cmap(c, _SP | _NL | _QF | _QB)) { /* sp \t\n'"` */ 183 unDgetC(c); 184 if (cmap(c, QUOTES)) 185 return 0; 186 return 1; 187 } 188 Strbuf_append1(wbuf, (Char) c); 189 } 190 } 191 192 /* 193 * Get a word. This routine is analogous to the routine 194 * word() in sh.lex.c for the main lexical input. One difference 195 * here is that we don't get a newline to terminate our expansion. 196 * Rather, DgetC will return a DEOF when we hit the end-of-input. 197 */ 198 static int 199 Dword(struct blk_buf *bb) 200 { 201 eChar c, c1; 202 struct Strbuf wbuf = Strbuf_INIT; 203 int dolflg; 204 int sofar = 0; 205 206 cleanup_push(&wbuf, Strbuf_cleanup); 207 for (;;) { 208 c = DgetC(DODOL); 209 switch (c) { 210 211 case DEOF: 212 if (sofar == 0) { 213 cleanup_until(&wbuf); 214 return (0); 215 } 216 /* finish this word and catch the code above the next time */ 217 unDredc(c); 218 /*FALLTHROUGH*/ 219 220 case '\n': 221 goto end; 222 223 case ' ': 224 case '\t': 225 continue; 226 227 case '`': 228 /* We preserve ` quotations which are done yet later */ 229 Strbuf_append1(&wbuf, (Char) c); 230 /*FALLTHROUGH*/ 231 case '\'': 232 case '"': 233 /* 234 * Note that DgetC never returns a QUOTES character from an 235 * expansion, so only true input quotes will get us here or out. 236 */ 237 c1 = c; 238 dolflg = c1 == '"' ? DODOL : 0; 239 for (;;) { 240 c = DgetC(dolflg); 241 if (c == c1) 242 break; 243 if (c == '\n' || c == DEOF) 244 stderror(ERR_UNMATCHED, (int)c1); 245 if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) { 246 if (wbuf.len != 0 && (wbuf.s[wbuf.len - 1] & TRIM) == '\\') 247 wbuf.len--; 248 } 249 switch (c1) { 250 251 case '"': 252 /* 253 * Leave any `s alone for later. Other chars are all 254 * quoted, thus `...` can tell it was within "...". 255 */ 256 Strbuf_append1(&wbuf, c == '`' ? '`' : c | QUOTE); 257 break; 258 259 case '\'': 260 /* Prevent all further interpretation */ 261 Strbuf_append1(&wbuf, c | QUOTE); 262 break; 263 264 case '`': 265 /* Leave all text alone for later */ 266 Strbuf_append1(&wbuf, (Char) c); 267 break; 268 269 default: 270 break; 271 } 272 } 273 if (c1 == '`') 274 Strbuf_append1(&wbuf, '`'); 275 sofar = 1; 276 if (Dpack(&wbuf) != 0) 277 goto end; 278 continue; 279 280 case '\\': 281 c = DgetC(0); /* No $ subst! */ 282 if (c == '\n' || c == DEOF) 283 continue; 284 c |= QUOTE; 285 break; 286 287 default: 288 break; 289 } 290 unDgetC(c); 291 sofar = 1; 292 if (Dpack(&wbuf) != 0) 293 goto end; 294 } 295 296 end: 297 cleanup_ignore(&wbuf); 298 cleanup_until(&wbuf); 299 bb_append(bb, Strbuf_finish(&wbuf)); 300 return 1; 301 } 302 303 304 /* 305 * Get a character, performing $ substitution unless flag is 0. 306 * Any QUOTES character which is returned from a $ expansion is 307 * QUOTEd so that it will not be recognized above. 308 */ 309 static eChar 310 DgetC(int flag) 311 { 312 Char c; 313 314 top: 315 if ((c = Dpeekc) != 0) { 316 Dpeekc = 0; 317 return (c); 318 } 319 if (lap < labuf.len) { 320 c = labuf.s[lap++] & (QUOTE | TRIM); 321 quotspec: 322 if (cmap(c, QUOTES)) 323 return (c | QUOTE); 324 return (c); 325 } 326 if (dolp) { 327 if ((c = *dolp++ & (QUOTE | TRIM)) != 0) 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[] = { NULL }; 350 static struct varent nulargv = {nulvec, STRargv, VAR_READWRITE, 351 { NULL, NULL, NULL }, 0 }; 352 353 static void 354 dolerror(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(void) 366 { 367 Char *np; 368 struct varent *vp = NULL; 369 struct Strbuf name = Strbuf_INIT; 370 eChar c, sc; 371 int subscr = 0, lwb = 1, upb = 0; 372 int dimen = 0, bitset = 0, length = 0; 373 static Char *dolbang = NULL; 374 375 cleanup_push(&name, Strbuf_cleanup); 376 dolmod.len = dolmcnt = dol_flag_a = 0; 377 c = sc = DgetC(0); 378 if (c == DEOF) { 379 stderror(ERR_SYNTAX); 380 return; 381 } 382 if (c == '{') 383 c = DgetC(0); /* sc is { to take } later */ 384 if ((c & TRIM) == '#') 385 dimen++, c = DgetC(0); /* $# takes dimension */ 386 else if (c == '?') 387 bitset++, c = DgetC(0); /* $? tests existence */ 388 else if (c == '%') 389 length++, c = DgetC(0); /* $% returns length in chars */ 390 switch (c) { 391 392 case '!': 393 if (dimen || bitset || length) 394 stderror(ERR_SYNTAX); 395 if (backpid != 0) { 396 xfree(dolbang); 397 setDolp(dolbang = putn(backpid)); 398 } 399 cleanup_until(&name); 400 goto eatbrac; 401 402 case '$': 403 if (dimen || bitset || length) 404 stderror(ERR_SYNTAX); 405 setDolp(doldol); 406 cleanup_until(&name); 407 goto eatbrac; 408 409 case '<'|QUOTE: { 410 static struct Strbuf wbuf; /* = Strbuf_INIT; */ 411 412 if (bitset) 413 stderror(ERR_NOTALLOWED, "$?<"); 414 if (dimen) 415 stderror(ERR_NOTALLOWED, "$#<"); 416 if (length) 417 stderror(ERR_NOTALLOWED, "$%<"); 418 wbuf.len = 0; 419 { 420 char cbuf[MB_LEN_MAX]; 421 size_t cbp = 0; 422 int old_pintr_disabled; 423 424 for (;;) { 425 int len; 426 ssize_t res; 427 Char wc; 428 429 pintr_push_enable(&old_pintr_disabled); 430 res = force_read(OLDSTD, cbuf + cbp, 1); 431 cleanup_until(&old_pintr_disabled); 432 if (res != 1) 433 break; 434 cbp++; 435 len = normal_mbtowc(&wc, cbuf, cbp); 436 if (len == -1) { 437 reset_mbtowc(); 438 if (cbp < MB_LEN_MAX) 439 continue; /* Maybe a partial character */ 440 wc = (unsigned char)*cbuf | INVALID_BYTE; 441 } 442 if (len <= 0) 443 len = 1; 444 if (cbp != (size_t)len) 445 memmove(cbuf, cbuf + len, cbp - len); 446 cbp -= len; 447 if (wc == '\n') 448 break; 449 Strbuf_append1(&wbuf, wc); 450 } 451 while (cbp != 0) { 452 int len; 453 Char wc; 454 455 len = normal_mbtowc(&wc, cbuf, cbp); 456 if (len == -1) { 457 reset_mbtowc(); 458 wc = (unsigned char)*cbuf | INVALID_BYTE; 459 } 460 if (len <= 0) 461 len = 1; 462 if (cbp != (size_t)len) 463 memmove(cbuf, cbuf + len, cbp - len); 464 cbp -= len; 465 if (wc == '\n') 466 break; 467 Strbuf_append1(&wbuf, wc); 468 } 469 Strbuf_terminate(&wbuf); 470 } 471 472 fixDolMod(); 473 setDolp(wbuf.s); /* Kept allocated until next $< expansion */ 474 cleanup_until(&name); 475 goto eatbrac; 476 } 477 478 case '*': 479 Strbuf_append(&name, STRargv); 480 Strbuf_terminate(&name); 481 vp = adrof(STRargv); 482 subscr = -1; /* Prevent eating [...] */ 483 break; 484 485 case DEOF: 486 case '\n': 487 np = dimen ? STRargv : (bitset ? STRstatus : NULL); 488 if (np) { 489 bitset = 0; 490 Strbuf_append(&name, np); 491 Strbuf_terminate(&name); 492 vp = adrof(np); 493 subscr = -1; /* Prevent eating [...] */ 494 unDredc(c); 495 break; 496 } 497 else 498 stderror(ERR_SYNTAX); 499 /*NOTREACHED*/ 500 501 default: 502 if (Isdigit(c)) { 503 if (dimen) 504 stderror(ERR_NOTALLOWED, "$#<num>"); 505 subscr = 0; 506 do { 507 subscr = subscr * 10 + c - '0'; 508 c = DgetC(0); 509 } while (c != DEOF && Isdigit(c)); 510 unDredc(c); 511 if (subscr < 0) 512 stderror(ERR_RANGE); 513 if (subscr == 0) { 514 if (bitset) { 515 dolp = dolzero ? STR1 : STR0; 516 cleanup_until(&name); 517 goto eatbrac; 518 } 519 if (ffile == 0) 520 stderror(ERR_DOLZERO); 521 if (length) { 522 length = Strlen(ffile); 523 addla(putn(length)); 524 } 525 else { 526 fixDolMod(); 527 setDolp(ffile); 528 } 529 cleanup_until(&name); 530 goto eatbrac; 531 } 532 #if 0 533 if (bitset) 534 stderror(ERR_NOTALLOWED, "$?<num>"); 535 if (length) 536 stderror(ERR_NOTALLOWED, "$%<num>"); 537 #endif 538 vp = adrof(STRargv); 539 if (vp == 0) { 540 vp = &nulargv; 541 cleanup_until(&name); 542 goto eatmod; 543 } 544 break; 545 } 546 if (c == DEOF || !alnum(c)) { 547 np = dimen ? STRargv : (bitset ? STRstatus : NULL); 548 if (np) { 549 bitset = 0; 550 Strbuf_append(&name, np); 551 Strbuf_terminate(&name); 552 vp = adrof(np); 553 subscr = -1; /* Prevent eating [...] */ 554 unDredc(c); 555 break; 556 } 557 else 558 stderror(ERR_VARALNUM); 559 } 560 for (;;) { 561 Strbuf_append1(&name, (Char) c); 562 c = DgetC(0); 563 if (c == DEOF || !alnum(c)) 564 break; 565 } 566 Strbuf_terminate(&name); 567 unDredc(c); 568 vp = adrof(name.s); 569 } 570 if (bitset) { 571 dolp = (vp || getenv(short2str(name.s))) ? STR1 : STR0; 572 cleanup_until(&name); 573 goto eatbrac; 574 } 575 if (vp == NULL || vp->vec == NULL) { 576 np = str2short(getenv(short2str(name.s))); 577 if (np) { 578 static Char *env_val; /* = NULL; */ 579 580 cleanup_until(&name); 581 fixDolMod(); 582 xfree(env_val); 583 env_val = Strsave(np); 584 setDolp(env_val); 585 goto eatbrac; 586 } 587 udvar(name.s); 588 /* NOTREACHED */ 589 } 590 cleanup_until(&name); 591 c = DgetC(0); 592 upb = blklen(vp->vec); 593 if (dimen == 0 && subscr == 0 && c == '[') { 594 name = Strbuf_init; 595 cleanup_push(&name, Strbuf_cleanup); 596 np = name.s; 597 for (;;) { 598 c = DgetC(DODOL); /* Allow $ expand within [ ] */ 599 if (c == ']') 600 break; 601 if (c == '\n' || c == DEOF) 602 stderror(ERR_INCBR); 603 Strbuf_append1(&name, (Char) c); 604 } 605 Strbuf_terminate(&name); 606 np = name.s; 607 if (dolp || dolcnt) /* $ exp must end before ] */ 608 stderror(ERR_EXPORD); 609 if (!*np) 610 stderror(ERR_SYNTAX); 611 if (Isdigit(*np)) { 612 int i; 613 614 for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0') 615 continue; 616 if ((i < 0 || i > upb) && !any("-*", *np)) { 617 cleanup_until(&name); 618 dolerror(vp->v_name); 619 return; 620 } 621 lwb = i; 622 if (!*np) 623 upb = lwb, np = STRstar; 624 } 625 if (*np == '*') 626 np++; 627 else if (*np != '-') 628 stderror(ERR_MISSING, '-'); 629 else { 630 int i = upb; 631 632 np++; 633 if (Isdigit(*np)) { 634 i = 0; 635 while (Isdigit(*np)) 636 i = i * 10 + *np++ - '0'; 637 if (i < 0 || i > upb) { 638 cleanup_until(&name); 639 dolerror(vp->v_name); 640 return; 641 } 642 } 643 if (i < lwb) 644 upb = lwb - 1; 645 else 646 upb = i; 647 } 648 if (lwb == 0) { 649 if (upb != 0) { 650 cleanup_until(&name); 651 dolerror(vp->v_name); 652 return; 653 } 654 upb = -1; 655 } 656 if (*np) 657 stderror(ERR_SYNTAX); 658 cleanup_until(&name); 659 } 660 else { 661 if (subscr > 0) { 662 if (subscr > upb) 663 lwb = 1, upb = 0; 664 else 665 lwb = upb = subscr; 666 } 667 unDredc(c); 668 } 669 if (dimen) { 670 /* this is a kludge. It prevents Dgetdol() from */ 671 /* pushing erroneous ${#<error> values into the labuf. */ 672 if (sc == '{') { 673 c = Dredc(); 674 if (c != '}') 675 stderror(ERR_MISSING, '}'); 676 unDredc(c); 677 } 678 addla(putn(upb - lwb + 1)); 679 } 680 else if (length) { 681 int i; 682 683 for (i = lwb - 1, length = 0; i < upb; i++) 684 length += Strlen(vp->vec[i]); 685 #ifdef notdef 686 /* We don't want that, since we can always compute it by adding $#xxx */ 687 length += i - 1; /* Add the number of spaces in */ 688 #endif 689 addla(putn(length)); 690 } 691 else { 692 eatmod: 693 fixDolMod(); 694 dolnxt = &vp->vec[lwb - 1]; 695 dolcnt = upb - lwb + 1; 696 } 697 eatbrac: 698 if (sc == '{') { 699 c = Dredc(); 700 if (c != '}') 701 stderror(ERR_MISSING, '}'); 702 } 703 } 704 705 static void 706 fixDolMod(void) 707 { 708 eChar c; 709 710 c = DgetC(0); 711 if (c == ':') { 712 do { 713 c = DgetC(0), dolmcnt = 1, dol_flag_a = 0; 714 if (c == 'g' || c == 'a') { 715 if (c == 'g') 716 dolmcnt = INT_MAX; 717 else 718 dol_flag_a = 1; 719 c = DgetC(0); 720 } 721 if ((c == 'g' && dolmcnt != INT_MAX) || 722 (c == 'a' && dol_flag_a == 0)) { 723 if (c == 'g') 724 dolmcnt = INT_MAX; 725 else 726 dol_flag_a = 1; 727 c = DgetC(0); 728 } 729 730 if (c == 's') { /* [eichin:19910926.0755EST] */ 731 int delimcnt = 2; 732 eChar delim = DgetC(0); 733 Strbuf_append1(&dolmod, (Char) c); 734 Strbuf_append1(&dolmod, (Char) delim); 735 736 if (delim == DEOF || !delim || letter(delim) 737 || Isdigit(delim) || any(" \t\n", delim)) { 738 seterror(ERR_BADSUBST); 739 break; 740 } 741 while ((c = DgetC(0)) != DEOF) { 742 Strbuf_append1(&dolmod, (Char) c); 743 if(c == delim) delimcnt--; 744 if(!delimcnt) break; 745 } 746 if(delimcnt) { 747 seterror(ERR_BADSUBST); 748 break; 749 } 750 continue; 751 } 752 if (!any("luhtrqxes", c)) 753 stderror(ERR_BADMOD, (int)c); 754 Strbuf_append1(&dolmod, (Char) c); 755 if (c == 'q') 756 dolmcnt = INT_MAX; 757 } 758 while ((c = DgetC(0)) == ':'); 759 unDredc(c); 760 } 761 else 762 unDredc(c); 763 } 764 765 static void 766 setDolp(Char *cp) 767 { 768 Char *dp; 769 size_t i; 770 771 if (dolmod.len == 0 || dolmcnt == 0) { 772 dolp = cp; 773 return; 774 } 775 cp = Strsave(cp); 776 for (i = 0; i < dolmod.len; i++) { 777 int didmod = 0; 778 779 /* handle s// [eichin:19910926.0510EST] */ 780 if(dolmod.s[i] == 's') { 781 Char delim; 782 Char *lhsub, *rhsub, *np; 783 size_t lhlen = 0, rhlen = 0; 784 785 delim = dolmod.s[++i]; 786 if (!delim || letter(delim) 787 || Isdigit(delim) || any(" \t\n", delim)) { 788 seterror(ERR_BADSUBST); 789 break; 790 } 791 lhsub = &dolmod.s[++i]; 792 while(dolmod.s[i] != delim && dolmod.s[++i]) { 793 lhlen++; 794 } 795 dolmod.s[i] = 0; 796 rhsub = &dolmod.s[++i]; 797 while(dolmod.s[i] != delim && dolmod.s[++i]) { 798 rhlen++; 799 } 800 dolmod.s[i] = 0; 801 802 strip(lhsub); 803 strip(cp); 804 dp = cp; 805 do { 806 dp = Strstr(dp, lhsub); 807 if (dp) { 808 ptrdiff_t diff = dp - cp; 809 np = xmalloc((Strlen(cp) + 1 - lhlen + rhlen) * 810 sizeof(Char)); 811 (void) Strncpy(np, cp, diff); 812 (void) Strcpy(np + diff, rhsub); 813 (void) Strcpy(np + diff + rhlen, dp + lhlen); 814 815 dp = np + diff + 1; 816 xfree(cp); 817 cp = np; 818 didmod = 1; 819 } else { 820 /* should this do a seterror? */ 821 break; 822 } 823 } 824 while (dol_flag_a != 0); 825 /* 826 * restore dolmod for additional words 827 */ 828 dolmod.s[i] = rhsub[-1] = (Char) delim; 829 } else { 830 831 do { 832 if ((dp = domod(cp, dolmod.s[i])) != NULL) { 833 didmod = 1; 834 if (Strcmp(cp, dp) == 0) { 835 xfree(cp); 836 cp = dp; 837 break; 838 } 839 else { 840 xfree(cp); 841 cp = dp; 842 } 843 } 844 else 845 break; 846 } 847 while (dol_flag_a != 0); 848 } 849 if (didmod && dolmcnt != INT_MAX) 850 dolmcnt--; 851 #ifdef notdef 852 else 853 break; 854 #endif 855 } 856 857 addla(cp); 858 859 dolp = STRNULL; 860 if (seterr) 861 stderror(ERR_OLD); 862 } 863 864 static void 865 unDredc(eChar c) 866 { 867 868 Dpeekrd = c; 869 } 870 871 static eChar 872 Dredc(void) 873 { 874 Char c; 875 876 if ((c = Dpeekrd) != 0) { 877 Dpeekrd = 0; 878 return (c); 879 } 880 if (Dcp && (c = *Dcp++)) 881 return (c & (QUOTE | TRIM)); 882 if (*Dvp == 0) { 883 Dcp = 0; 884 return (DEOF); 885 } 886 Dcp = *Dvp++; 887 return (' '); 888 } 889 890 static int gflag; 891 892 static void 893 Dtestq(Char c) 894 { 895 896 if (cmap(c, QUOTES)) 897 gflag = 1; 898 } 899 900 static void 901 inheredoc_cleanup(void *dummy) 902 { 903 USE(dummy); 904 inheredoc = 0; 905 } 906 907 /* 908 * Form a shell temporary file (in unit 0) from the words 909 * of the shell input up to EOF or a line the same as "term". 910 * Unit 0 should have been closed before this call. 911 */ 912 void 913 heredoc(Char *term) 914 { 915 eChar c; 916 Char *Dv[2]; 917 struct Strbuf lbuf = Strbuf_INIT, mbuf = Strbuf_INIT; 918 Char obuf[BUFSIZE + 1]; 919 #define OBUF_END (obuf + sizeof(obuf) / sizeof (*obuf) - 1) 920 Char *lbp, *obp, *mbp; 921 Char **vp; 922 int quoted; 923 char *tmp; 924 #ifndef WINNT_NATIVE 925 struct timeval tv; 926 927 again: 928 #endif /* WINNT_NATIVE */ 929 tmp = short2str(shtemp); 930 #ifndef O_CREAT 931 # define O_CREAT 0 932 if (xcreat(tmp, 0600) < 0) 933 stderror(ERR_SYSTEM, tmp, strerror(errno)); 934 #endif 935 xclose(0); 936 #ifndef O_TEMPORARY 937 # define O_TEMPORARY 0 938 #endif 939 #ifndef O_EXCL 940 # define O_EXCL 0 941 #endif 942 if (xopen(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY|O_LARGEFILE, 0600) == 943 -1) { 944 int oerrno = errno; 945 #ifndef WINNT_NATIVE 946 if (errno == EEXIST) { 947 if (unlink(tmp) == -1) { 948 (void) gettimeofday(&tv, NULL); 949 xfree(shtemp); 950 mbp = putn((((int)tv.tv_sec) ^ 951 ((int)tv.tv_usec) ^ ((int)getpid())) & 0x00ffffff); 952 shtemp = Strspl(STRtmpsh, mbp); 953 xfree(mbp); 954 } 955 goto again; 956 } 957 #endif /* WINNT_NATIVE */ 958 (void) unlink(tmp); 959 errno = oerrno; 960 stderror(ERR_SYSTEM, tmp, strerror(errno)); 961 } 962 (void) unlink(tmp); /* 0 0 inode! */ 963 Dv[0] = term; 964 Dv[1] = NULL; 965 gflag = 0; 966 trim(Dv); 967 rscan(Dv, Dtestq); 968 quoted = gflag; 969 obp = obuf; 970 obuf[BUFSIZE] = 0; 971 inheredoc = 1; 972 cleanup_push(&inheredoc, inheredoc_cleanup); 973 #ifdef WINNT_NATIVE 974 __dup_stdin = 1; 975 #endif /* WINNT_NATIVE */ 976 #ifdef O_TEXT 977 setmode(1, O_TEXT); 978 #endif 979 #ifdef O_BINARY 980 setmode(0, O_BINARY); 981 #endif 982 cleanup_push(&lbuf, Strbuf_cleanup); 983 cleanup_push(&mbuf, Strbuf_cleanup); 984 for (;;) { 985 Char **words; 986 987 /* 988 * Read up a line 989 */ 990 lbuf.len = 0; 991 for (;;) { 992 c = readc(1); /* 1 -> Want EOF returns */ 993 if (c == CHAR_ERR || c == '\n') 994 break; 995 if ((c &= TRIM) != 0) 996 Strbuf_append1(&lbuf, (Char) c); 997 } 998 Strbuf_terminate(&lbuf); 999 1000 /* 1001 * Check for EOF or compare to terminator -- before expansion 1002 */ 1003 if (c == CHAR_ERR || eq(lbuf.s, term)) 1004 break; 1005 1006 /* 1007 * If term was quoted or -n just pass it on 1008 */ 1009 if (quoted || noexec) { 1010 Strbuf_append1(&lbuf, '\n'); 1011 Strbuf_terminate(&lbuf); 1012 for (lbp = lbuf.s; (c = *lbp++) != 0;) { 1013 *obp++ = (Char) c; 1014 if (obp == OBUF_END) { 1015 tmp = short2str(obuf); 1016 (void) xwrite(0, tmp, strlen (tmp)); 1017 obp = obuf; 1018 } 1019 } 1020 continue; 1021 } 1022 1023 /* 1024 * Term wasn't quoted so variable and then command expand the input 1025 * line 1026 */ 1027 Dcp = lbuf.s; 1028 Dvp = Dv + 1; 1029 mbuf.len = 0; 1030 for (;;) { 1031 c = DgetC(DODOL); 1032 if (c == DEOF) 1033 break; 1034 if ((c &= TRIM) == 0) 1035 continue; 1036 /* \ quotes \ $ ` here */ 1037 if (c == '\\') { 1038 c = DgetC(0); 1039 if (!any("$\\`", c)) 1040 unDgetC(c | QUOTE), c = '\\'; 1041 else 1042 c |= QUOTE; 1043 } 1044 Strbuf_append1(&mbuf, (Char) c); 1045 } 1046 Strbuf_terminate(&mbuf); 1047 1048 /* 1049 * If any ` in line do command substitution 1050 */ 1051 mbp = mbuf.s; 1052 if (Strchr(mbp, '`') != NULL) { 1053 /* 1054 * 1 arg to dobackp causes substitution to be literal. Words are 1055 * broken only at newlines so that all blanks and tabs are 1056 * preserved. Blank lines (null words) are not discarded. 1057 */ 1058 words = dobackp(mbp, 1); 1059 } 1060 else 1061 /* Setup trivial vector similar to return of dobackp */ 1062 Dv[0] = mbp, Dv[1] = NULL, words = Dv; 1063 1064 /* 1065 * Resurrect the words from the command substitution each separated by 1066 * a newline. Note that the last newline of a command substitution 1067 * will have been discarded, but we put a newline after the last word 1068 * because this represents the newline after the last input line! 1069 */ 1070 for (vp= words; *vp; vp++) { 1071 for (mbp = *vp; *mbp; mbp++) { 1072 *obp++ = *mbp & TRIM; 1073 if (obp == OBUF_END) { 1074 tmp = short2str(obuf); 1075 (void) xwrite(0, tmp, strlen (tmp)); 1076 obp = obuf; 1077 } 1078 } 1079 *obp++ = '\n'; 1080 if (obp == OBUF_END) { 1081 tmp = short2str(obuf); 1082 (void) xwrite(0, tmp, strlen (tmp)); 1083 obp = obuf; 1084 } 1085 } 1086 if (words != Dv) 1087 blkfree(words); 1088 } 1089 *obp = 0; 1090 tmp = short2str(obuf); 1091 (void) xwrite(0, tmp, strlen (tmp)); 1092 (void) lseek(0, (off_t) 0, L_SET); 1093 cleanup_until(&inheredoc); 1094 } 1095