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