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