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