1 /* $NetBSD: dol.c,v 1.29 2013/07/16 17:47:43 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1980, 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)dol.c 8.1 (Berkeley) 5/31/93"; 36 #else 37 __RCSID("$NetBSD: dol.c,v 1.29 2013/07/16 17:47:43 christos Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/types.h> 42 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <stdarg.h> 46 #include <stddef.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 51 #include "csh.h" 52 #include "extern.h" 53 54 /* 55 * These routines perform variable substitution and quoting via ' and ". 56 * To this point these constructs have been preserved in the divided 57 * input words. Here we expand variables and turn quoting via ' and " into 58 * QUOTE bits on characters (which prevent further interpretation). 59 * If the `:q' modifier was applied during history expansion, then 60 * some QUOTEing may have occurred already, so we dont "trim()" here. 61 */ 62 63 static int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */ 64 static Char *Dcp, **Dvp; /* Input vector for Dreadc */ 65 66 #define DEOF -1 67 #define unDgetC(c) Dpeekc = c 68 #define QUOTES (_QF|_QB|_ESC) /* \ ' " ` */ 69 70 /* 71 * The following variables give the information about the current 72 * $ expansion, recording the current word position, the remaining 73 * words within this expansion, the count of remaining words, and the 74 * information about any : modifier which is being applied. 75 */ 76 #define MAXWLEN (BUFSIZE - 4) 77 #define MAXMOD MAXWLEN /* This cannot overflow */ 78 static Char dolmod[MAXMOD]; /* : modifier character */ 79 static Char *dolp; /* Remaining chars from this word */ 80 static Char **dolnxt; /* Further words */ 81 static int dolcnt; /* Count of further words */ 82 static int dolnmod; /* Number of modifiers */ 83 static int dolmcnt; /* :gx -> 10000, else 1 */ 84 static int dolwcnt; /* :wx -> 10000, else 1 */ 85 86 static void Dfix2(Char **); 87 static Char *Dpack(Char *, Char *); 88 static int Dword(void); 89 __dead static void dolerror(Char *); 90 static int DgetC(int); 91 static void Dgetdol(void); 92 static void fixDolMod(void); 93 static void setDolp(Char *); 94 static void unDredc(int); 95 static int Dredc(void); 96 static void Dtestq(int); 97 98 99 /* 100 * Fix up the $ expansions and quotations in the 101 * argument list to command t. 102 */ 103 void 104 Dfix(struct command *t) 105 { 106 Char *p, **pp; 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 * $ substitute one word, for i/o redirection 125 */ 126 Char * 127 Dfix1(Char *cp) 128 { 129 Char *Dv[2]; 130 131 if (noexec) 132 return (0); 133 Dv[0] = cp; 134 Dv[1] = NULL; 135 Dfix2(Dv); 136 if (gargc != 1) { 137 setname(vis_str(cp)); 138 stderror(ERR_NAME | ERR_AMBIG); 139 } 140 cp = Strsave(gargv[0]); 141 blkfree(gargv), gargv = 0; 142 return (cp); 143 } 144 145 /* 146 * Subroutine to do actual fixing after state initialization. 147 */ 148 static void 149 Dfix2(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(Char *wbuf, Char *wp) 167 { 168 int c; 169 ptrdiff_t i; 170 171 i = MAXWLEN - (wp - wbuf); 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(void) 215 { 216 Char wbuf[BUFSIZE], *wp; 217 int c, c1; 218 ptrdiff_t i; 219 int dolflg, done, sofar; 220 221 done = 0; 222 i = MAXWLEN; 223 sofar = 0; 224 wp = wbuf; 225 226 while (!done) { 227 done = 1; 228 c = DgetC(DODOL); 229 switch (c) { 230 case DEOF: 231 if (sofar == 0) 232 return (0); 233 /* finish this word and catch the code above the next time */ 234 unDredc(c); 235 /* FALLTHROUGH */ 236 case '\n': 237 *wp = 0; 238 Gcat(STRNULL, wbuf); 239 return (1); 240 case ' ': 241 case '\t': 242 done = 0; 243 break; 244 case '`': 245 /* We preserve ` quotations which are done yet later */ 246 *wp++ = (Char)c, --i; 247 /* FALLTHROUGH */ 248 case '\'': 249 case '"': 250 /* 251 * Note that DgetC never returns a QUOTES character from an 252 * expansion, so only true input quotes will get us here or out. 253 */ 254 c1 = c; 255 dolflg = c1 == '"' ? DODOL : 0; 256 for (;;) { 257 c = DgetC(dolflg); 258 if (c == c1) 259 break; 260 if (c == '\n' || c == DEOF) 261 stderror(ERR_UNMATCHED, c1); 262 if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) 263 --wp, ++i; 264 if (--i <= 0) 265 stderror(ERR_WTOOLONG); 266 switch (c1) { 267 case '"': 268 /* 269 * Leave any `s alone for later. Other chars are all 270 * quoted, thus `...` can tell it was within "...". 271 */ 272 *wp++ = (Char)(c == '`' ? '`' : (c | QUOTE)); 273 break; 274 case '\'': 275 /* Prevent all further interpretation */ 276 *wp++ = (Char)(c | QUOTE); 277 break; 278 case '`': 279 /* Leave all text alone for later */ 280 *wp++ = (Char)c; 281 break; 282 default: 283 break; 284 } 285 } 286 if (c1 == '`') 287 *wp++ = '`' /* i--; eliminated */; 288 sofar = 1; 289 if ((wp = Dpack(wbuf, wp)) == NULL) 290 return (1); 291 else { 292 i = MAXWLEN - (wp - wbuf); 293 done = 0; 294 } 295 break; 296 case '\\': 297 c = DgetC(0); /* No $ subst! */ 298 if (c == '\n' || c == DEOF) { 299 done = 0; 300 break; 301 } 302 c |= QUOTE; 303 break; 304 default: 305 break; 306 } 307 if (done) { 308 unDgetC(c); 309 sofar = 1; 310 if ((wp = Dpack(wbuf, wp)) == NULL) 311 return (1); 312 else { 313 i = MAXWLEN - (wp - wbuf); 314 done = 0; 315 } 316 } 317 } 318 /* Really NOTREACHED */ 319 return (0); 320 } 321 322 323 /* 324 * Get a character, performing $ substitution unless flag is 0. 325 * Any QUOTES character which is returned from a $ expansion is 326 * QUOTEd so that it will not be recognized above. 327 */ 328 static int 329 DgetC(int flag) 330 { 331 int c; 332 top: 333 if ((c = Dpeekc) != '\0') { 334 Dpeekc = 0; 335 return (c); 336 } 337 if (lap) { 338 c = *lap++ & (QUOTE | TRIM); 339 if (c == 0) { 340 lap = 0; 341 goto top; 342 } 343 quotspec: 344 if (cmap(c, QUOTES)) 345 return (c | QUOTE); 346 return (c); 347 } 348 if (dolp) { 349 if ((c = *dolp++ & (QUOTE | TRIM)) != '\0') 350 goto quotspec; 351 if (dolcnt > 0) { 352 setDolp(*dolnxt++); 353 --dolcnt; 354 return (' '); 355 } 356 dolp = 0; 357 } 358 if (dolcnt > 0) { 359 setDolp(*dolnxt++); 360 --dolcnt; 361 goto top; 362 } 363 c = Dredc(); 364 if (c == '$' && flag) { 365 Dgetdol(); 366 goto top; 367 } 368 return (c); 369 } 370 371 static Char *nulvec[] = {0}; 372 static struct varent nulargv = {nulvec, STRargv, { NULL, NULL, NULL }, 0}; 373 374 static void 375 dolerror(Char *s) 376 { 377 setname(vis_str(s)); 378 stderror(ERR_NAME | ERR_RANGE); 379 /* NOTREACHED */ 380 } 381 382 /* 383 * Handle the multitudinous $ expansion forms. 384 * Ugh. 385 */ 386 static void 387 Dgetdol(void) 388 { 389 static Char *dolbang = NULL; 390 Char name[4*MAXVARLEN+1]; 391 Char wbuf[BUFSIZE]; 392 struct varent *vp; 393 Char *np; 394 int c, lwb, sc, subscr, upb; 395 int dimen, bitset; 396 char tnp; 397 398 bitset = 0; 399 dimen = 0; 400 lwb = 1; 401 upb = 0; 402 subscr = 0; 403 vp = NULL; 404 405 dolnmod = dolmcnt = dolwcnt = 0; 406 c = sc = DgetC(0); 407 if (c == '{') 408 c = DgetC(0); /* sc is { to take } later */ 409 if ((c & TRIM) == '#') 410 dimen++, c = DgetC(0); /* $# takes dimension */ 411 else if (c == '?') 412 bitset++, c = DgetC(0); /* $? tests existence */ 413 switch (c) { 414 case '!': 415 if (dimen || bitset) 416 stderror(ERR_SYNTAX); 417 if (backpid != 0) { 418 if (dolbang) 419 xfree((ptr_t)dolbang); 420 setDolp(dolbang = putn(backpid)); 421 } 422 goto eatbrac; 423 case '$': 424 if (dimen || bitset) 425 stderror(ERR_SYNTAX); 426 setDolp(doldol); 427 goto eatbrac; 428 case '<' | QUOTE: 429 if (bitset) 430 stderror(ERR_NOTALLOWED, "$?<"); 431 if (dimen) 432 stderror(ERR_NOTALLOWED, "$?#"); 433 for (np = wbuf; read(OLDSTD, &tnp, 1) == 1; np++) { 434 *np = (unsigned char)tnp; 435 if (np >= &wbuf[BUFSIZE - 1]) 436 stderror(ERR_LTOOLONG); 437 if (tnp == '\n') 438 break; 439 } 440 *np = 0; 441 /* 442 * KLUDGE: dolmod is set here because it will cause setDolp to call 443 * domod and thus to copy wbuf. Otherwise setDolp would use it 444 * directly. If we saved it ourselves, no one would know when to free 445 * it. The actual function of the 'q' causes filename expansion not to 446 * be done on the interpolated value. 447 */ 448 dolmod[dolnmod++] = 'q'; 449 dolmcnt = 10000; 450 setDolp(wbuf); 451 goto eatbrac; 452 case DEOF: 453 case '\n': 454 stderror(ERR_SYNTAX); 455 /* NOTREACHED */ 456 case '*': 457 (void) Strcpy(name, STRargv); 458 vp = adrof(STRargv); 459 subscr = -1; /* Prevent eating [...] */ 460 break; 461 default: 462 np = name; 463 if (Isdigit(c)) { 464 if (dimen) 465 stderror(ERR_NOTALLOWED, "$#<num>"); 466 subscr = 0; 467 do { 468 subscr = subscr * 10 + c - '0'; 469 c = DgetC(0); 470 } while (Isdigit(c)); 471 unDredc(c); 472 if (subscr < 0) 473 stderror(ERR_RANGE); 474 if (subscr == 0) { 475 if (bitset) { 476 dolp = ffile ? STR1 : STR0; 477 goto eatbrac; 478 } 479 if (ffile == 0) 480 stderror(ERR_DOLZERO); 481 fixDolMod(); 482 setDolp(ffile); 483 goto eatbrac; 484 } 485 if (bitset) 486 stderror(ERR_DOLQUEST); 487 vp = adrof(STRargv); 488 if (vp == 0) { 489 vp = &nulargv; 490 goto eatmod; 491 } 492 break; 493 } 494 if (!alnum(c)) 495 stderror(ERR_VARALNUM); 496 for (;;) { 497 *np++ = (Char)c; 498 c = DgetC(0); 499 if (!alnum(c)) 500 break; 501 if (np >= &name[MAXVARLEN]) 502 stderror(ERR_VARTOOLONG); 503 } 504 *np++ = 0; 505 unDredc(c); 506 vp = adrof(name); 507 } 508 if (bitset) { 509 dolp = (vp || getenv(short2str(name))) ? STR1 : STR0; 510 goto eatbrac; 511 } 512 if (vp == 0) { 513 np = str2short(getenv(short2str(name))); 514 if (np) { 515 fixDolMod(); 516 setDolp(np); 517 goto eatbrac; 518 } 519 udvar(name); 520 } 521 c = DgetC(0); 522 upb = blklen(vp->vec); 523 if (dimen == 0 && subscr == 0 && c == '[') { 524 np = name; 525 for (;;) { 526 c = DgetC(DODOL); /* Allow $ expand within [ ] */ 527 if (c == ']') 528 break; 529 if (c == '\n' || c == DEOF) 530 stderror(ERR_INCBR); 531 if (np >= &name[sizeof(name) / sizeof(Char) - 2]) 532 stderror(ERR_VARTOOLONG); 533 *np++ = (Char)c; 534 } 535 *np = 0, np = name; 536 if (dolp || dolcnt) /* $ exp must end before ] */ 537 stderror(ERR_EXPORD); 538 if (!*np) 539 stderror(ERR_SYNTAX); 540 if (Isdigit(*np)) { 541 int i; 542 543 for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0') 544 continue; 545 if ((i < 0 || i > upb) && !any("-*", *np)) { 546 dolerror(vp->v_name); 547 return; 548 } 549 lwb = i; 550 if (!*np) 551 upb = lwb, np = STRstar; 552 } 553 if (*np == '*') 554 np++; 555 else if (*np != '-') 556 stderror(ERR_MISSING, '-'); 557 else { 558 int i = upb; 559 560 np++; 561 if (Isdigit(*np)) { 562 i = 0; 563 while (Isdigit(*np)) 564 i = i * 10 + *np++ - '0'; 565 if (i < 0 || i > upb) { 566 dolerror(vp->v_name); 567 return; 568 } 569 } 570 if (i < lwb) 571 upb = lwb - 1; 572 else 573 upb = i; 574 } 575 if (lwb == 0) { 576 if (upb != 0) { 577 dolerror(vp->v_name); 578 return; 579 } 580 upb = -1; 581 } 582 if (*np) 583 stderror(ERR_SYNTAX); 584 } 585 else { 586 if (subscr > 0) { 587 if (subscr > upb) 588 lwb = 1, upb = 0; 589 else 590 lwb = upb = subscr; 591 } 592 unDredc(c); 593 } 594 if (dimen) { 595 Char *cp = putn(upb - lwb + 1); 596 597 addla(cp); 598 xfree((ptr_t) cp); 599 } 600 else { 601 eatmod: 602 fixDolMod(); 603 dolnxt = &vp->vec[lwb - 1]; 604 dolcnt = upb - lwb + 1; 605 } 606 eatbrac: 607 if (sc == '{') { 608 c = Dredc(); 609 if (c != '}') 610 stderror(ERR_MISSING, '}'); 611 } 612 } 613 614 static void 615 fixDolMod(void) 616 { 617 int c; 618 619 c = DgetC(0); 620 if (c == ':') { 621 do { 622 c = DgetC(0), dolmcnt = 1, dolwcnt = 1; 623 if (c == 'g' || c == 'a') { 624 if (c == 'g') 625 dolmcnt = 10000; 626 else 627 dolwcnt = 10000; 628 c = DgetC(0); 629 } 630 if ((c == 'g' && dolmcnt != 10000) || 631 (c == 'a' && dolwcnt != 10000)) { 632 if (c == 'g') 633 dolmcnt = 10000; 634 else 635 dolwcnt = 10000; 636 c = DgetC(0); 637 } 638 639 if (c == 's') { /* [eichin:19910926.0755EST] */ 640 int delimcnt = 2; 641 int delim = DgetC(0); 642 dolmod[dolnmod++] = (Char)c; 643 dolmod[dolnmod++] = (Char)delim; 644 645 if (!delim || letter(delim) 646 || Isdigit(delim) || any(" \t\n", delim)) { 647 seterror(ERR_BADSUBST); 648 break; 649 } 650 while ((c = DgetC(0)) != (-1)) { 651 dolmod[dolnmod++] = (Char)c; 652 if(c == delim) delimcnt--; 653 if(!delimcnt) break; 654 } 655 if(delimcnt) { 656 seterror(ERR_BADSUBST); 657 break; 658 } 659 continue; 660 } 661 if (!any("htrqxes", c)) 662 stderror(ERR_BADMOD, c); 663 dolmod[dolnmod++] = (Char)c; 664 if (c == 'q') 665 dolmcnt = 10000; 666 } 667 while ((c = DgetC(0)) == ':'); 668 unDredc(c); 669 } 670 else 671 unDredc(c); 672 } 673 674 static void 675 setDolp(Char *cp) 676 { 677 Char *dp; 678 int i; 679 680 if (dolnmod == 0 || dolmcnt == 0) { 681 dolp = cp; 682 return; 683 } 684 dp = cp = Strsave(cp); 685 for (i = 0; i < dolnmod; i++) { 686 /* handle s// [eichin:19910926.0510EST] */ 687 if(dolmod[i] == 's') { 688 int delim; 689 Char *lhsub, *rhsub, *np; 690 size_t lhlen = 0, rhlen = 0; 691 int didmod = 0; 692 693 delim = dolmod[++i]; 694 if (!delim || letter(delim) 695 || Isdigit(delim) || any(" \t\n", delim)) { 696 seterror(ERR_BADSUBST); 697 break; 698 } 699 lhsub = &dolmod[++i]; 700 while(dolmod[i] != delim && dolmod[++i]) { 701 lhlen++; 702 } 703 dolmod[i] = 0; 704 rhsub = &dolmod[++i]; 705 while(dolmod[i] != delim && dolmod[++i]) { 706 rhlen++; 707 } 708 dolmod[i] = 0; 709 710 do { 711 dp = Strstr(cp, lhsub); 712 if (dp) { 713 np = xmalloc( 714 (size_t)((Strlen(cp) + 1 - lhlen + rhlen) * 715 sizeof(*np))); 716 (void)Strncpy(np, cp, (size_t)(dp - cp)); 717 (void)Strcpy(np + (dp - cp), rhsub); 718 (void)Strcpy(np + (dp - cp) + rhlen, dp + lhlen); 719 720 xfree((ptr_t) cp); 721 dp = cp = np; 722 didmod = 1; 723 } else { 724 /* should this do a seterror? */ 725 break; 726 } 727 } 728 while (dolwcnt == 10000); 729 /* 730 * restore dolmod for additional words 731 */ 732 dolmod[i] = rhsub[-1] = (Char)delim; 733 if (didmod) 734 dolmcnt--; 735 else 736 break; 737 } else { 738 int didmod = 0; 739 740 do { 741 if ((dp = domod(cp, dolmod[i]))) { 742 didmod = 1; 743 if (Strcmp(cp, dp) == 0) { 744 xfree((ptr_t) cp); 745 cp = dp; 746 break; 747 } 748 else { 749 xfree((ptr_t) cp); 750 cp = dp; 751 } 752 } 753 else 754 break; 755 } 756 while (dolwcnt == 10000); 757 dp = cp; 758 if (didmod) 759 dolmcnt--; 760 else 761 break; 762 } 763 } 764 765 if (dp) { 766 addla(dp); 767 xfree((ptr_t) dp); 768 } 769 else { 770 addla(cp); 771 xfree((ptr_t) cp); 772 } 773 774 dolp = STRNULL; 775 if (seterr) 776 stderror(ERR_OLD); 777 } 778 779 static void 780 unDredc(int c) 781 { 782 Dpeekrd = c; 783 } 784 785 static int 786 Dredc(void) 787 { 788 int c; 789 790 if ((c = Dpeekrd) != '\0') { 791 Dpeekrd = 0; 792 return (c); 793 } 794 if (Dcp && (c = *Dcp++)) 795 return (c & (QUOTE | TRIM)); 796 if (*Dvp == 0) { 797 Dcp = 0; 798 return (DEOF); 799 } 800 Dcp = *Dvp++; 801 return (' '); 802 } 803 804 static void 805 Dtestq(int c) 806 { 807 if (cmap(c, QUOTES)) 808 gflag = 1; 809 } 810 811 /* 812 * Form a shell temporary file (in unit 0) from the words 813 * of the shell input up to EOF or a line the same as "term". 814 * Unit 0 should have been closed before this call. 815 */ 816 void 817 /*ARGSUSED*/ 818 heredoc(Char *term) 819 { 820 Char obuf[BUFSIZE], lbuf[BUFSIZE], mbuf[BUFSIZE]; 821 struct timespec tv; 822 Char *Dv[2], *lbp, *obp, *mbp, **vp; 823 char *tmp; 824 int c, ocnt, lcnt, mcnt; 825 int quoted; 826 827 again: 828 tmp = short2str(shtemp); 829 if (open(tmp, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600) < 0) { 830 if (errno == EEXIST) { 831 if (unlink(tmp) == -1) { 832 (void)clock_gettime(CLOCK_MONOTONIC, &tv); 833 mbp = putn((((int)tv.tv_sec) ^ 834 ((int)tv.tv_nsec) ^ ((int)getpid())) & 0x00ffffff); 835 shtemp = Strspl(STRtmpsh, mbp); 836 xfree((ptr_t)mbp); 837 } 838 goto again; 839 } 840 stderror(ERR_SYSTEM, tmp, strerror(errno)); 841 } 842 (void)unlink(tmp); /* 0 0 inode! */ 843 Dv[0] = term; 844 Dv[1] = NULL; 845 gflag = 0; 846 trim(Dv); 847 rscan(Dv, Dtestq); 848 quoted = gflag; 849 ocnt = BUFSIZE; 850 obp = obuf; 851 for (;;) { 852 /* 853 * Read up a line 854 */ 855 lbp = lbuf; 856 lcnt = BUFSIZE - 4; 857 for (;;) { 858 c = readc(1); /* 1 -> Want EOF returns */ 859 if (c < 0 || c == '\n') 860 break; 861 if ((c &= TRIM) != '\0') { 862 *lbp++ = (Char)c; 863 if (--lcnt < 0) { 864 setname("<<"); 865 stderror(ERR_NAME | ERR_OVERFLOW); 866 } 867 } 868 } 869 *lbp = 0; 870 871 /* 872 * Check for EOF or compare to terminator -- before expansion 873 */ 874 if (c < 0 || eq(lbuf, term)) { 875 (void)write(0, short2str(obuf), (size_t)(BUFSIZE - ocnt)); 876 (void)lseek(0, (off_t)0, SEEK_SET); 877 return; 878 } 879 880 /* 881 * If term was quoted or -n just pass it on 882 */ 883 if (quoted || noexec) { 884 *lbp++ = '\n'; 885 *lbp = 0; 886 for (lbp = lbuf; (c = *lbp++) != '\0';) { 887 *obp++ = (Char)c; 888 if (--ocnt == 0) { 889 (void) write(0, short2str(obuf), BUFSIZE); 890 obp = obuf; 891 ocnt = BUFSIZE; 892 } 893 } 894 continue; 895 } 896 897 /* 898 * Term wasn't quoted so variable and then command expand the input 899 * line 900 */ 901 Dcp = lbuf; 902 Dvp = Dv + 1; 903 mbp = mbuf; 904 mcnt = BUFSIZE - 4; 905 for (;;) { 906 c = DgetC(DODOL); 907 if (c == DEOF) 908 break; 909 if ((c &= TRIM) == 0) 910 continue; 911 /* \ quotes \ $ ` here */ 912 if (c == '\\') { 913 c = DgetC(0); 914 if (!any("$\\`", c)) 915 unDgetC(c | QUOTE), c = '\\'; 916 else 917 c |= QUOTE; 918 } 919 *mbp++ = (Char)c; 920 if (--mcnt == 0) { 921 setname("<<"); 922 stderror(ERR_NAME | ERR_OVERFLOW); 923 } 924 } 925 *mbp++ = 0; 926 927 /* 928 * If any ` in line do command substitution 929 */ 930 mbp = mbuf; 931 if (any(short2str(mbp), '`')) { 932 /* 933 * 1 arg to dobackp causes substitution to be literal. Words are 934 * broken only at newlines so that all blanks and tabs are 935 * preserved. Blank lines (null words) are not discarded. 936 */ 937 vp = dobackp(mbuf, 1); 938 } 939 else 940 /* Setup trivial vector similar to return of dobackp */ 941 Dv[0] = mbp, Dv[1] = NULL, vp = Dv; 942 943 /* 944 * Resurrect the words from the command substitution each separated by 945 * a newline. Note that the last newline of a command substitution 946 * will have been discarded, but we put a newline after the last word 947 * because this represents the newline after the last input line! 948 */ 949 for (; *vp; vp++) { 950 for (mbp = *vp; *mbp; mbp++) { 951 *obp++ = *mbp & TRIM; 952 if (--ocnt == 0) { 953 (void)write(0, short2str(obuf), BUFSIZE); 954 obp = obuf; 955 ocnt = BUFSIZE; 956 } 957 } 958 *obp++ = '\n'; 959 if (--ocnt == 0) { 960 (void)write(0, short2str(obuf), BUFSIZE); 961 obp = obuf; 962 ocnt = BUFSIZE; 963 } 964 } 965 if (pargv) 966 blkfree(pargv), pargv = 0; 967 } 968 } 969