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