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