1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1980 Regents of the University of California. 11 * All rights reserved. The Berkeley Software License Agreement 12 * specifies the terms and conditions for redistribution. 13 */ 14 15 #pragma ident "%Z%%M% %I% %E% SMI" 16 17 #include <unistd.h> /* for lseek prototype */ 18 #include "sh.h" 19 #include "sh.tconst.h" 20 21 /* 22 * C shell 23 */ 24 25 /* 26 * These routines perform variable substitution and quoting via ' and ". 27 * To this point these constructs have been preserved in the divided 28 * input words. Here we expand variables and turn quoting via ' and " into 29 * QUOTE bits on characters (which prevent further interpretation). 30 * If the `:q' modifier was applied during history expansion, then 31 * some QUOTEing may have occurred already, so we dont "trim()" here. 32 */ 33 34 int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */ 35 tchar *Dcp, **Dvp; /* Input vector for Dreadc */ 36 37 #define DEOF -1 38 39 #define unDgetC(c) Dpeekc = c 40 41 #define QUOTES (_Q|_Q1|_ESC) /* \ ' " ` */ 42 43 /* 44 * The following variables give the information about the current 45 * $ expansion, recording the current word position, the remaining 46 * words within this expansion, the count of remaining words, and the 47 * information about any : modifier which is being applied. 48 */ 49 tchar *dolp; /* Remaining chars from this word */ 50 tchar **dolnxt; /* Further words */ 51 int dolcnt; /* Count of further words */ 52 tchar dolmod; /* : modifier character */ 53 int dolmcnt; /* :gx -> 10000, else 1 */ 54 55 void Dfix2(tchar **); 56 void Dgetdol(void); 57 void setDolp(tchar *); 58 void unDredc(int); 59 60 /* 61 * Fix up the $ expansions and quotations in the 62 * argument list to command t. 63 */ 64 void 65 Dfix(struct command *t) 66 { 67 tchar **pp; 68 tchar *p; 69 70 #ifdef TRACE 71 tprintf("TRACE- Dfix()\n"); 72 #endif 73 if (noexec) 74 return; 75 /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */ 76 for (pp = t->t_dcom; p = *pp++;) 77 while (*p) 78 if (cmap(*p++, _DOL|QUOTES)) { /* $, \, ', ", ` */ 79 Dfix2(t->t_dcom); /* found one */ 80 blkfree(t->t_dcom); 81 t->t_dcom = gargv; 82 gargv = 0; 83 return; 84 } 85 } 86 87 /* 88 * $ substitute one word, for i/o redirection 89 */ 90 tchar * 91 Dfix1(tchar *cp) 92 { 93 tchar *Dv[2]; 94 95 #ifdef TRACE 96 tprintf("TRACE- Dfix1()\n"); 97 #endif 98 if (noexec) 99 return (0); 100 Dv[0] = cp; Dv[1] = NOSTR; 101 Dfix2(Dv); 102 if (gargc != 1) { 103 setname(cp); 104 bferr("Ambiguous"); 105 } 106 cp = savestr(gargv[0]); 107 blkfree(gargv), gargv = 0; 108 return (cp); 109 } 110 111 /* 112 * Subroutine to do actual fixing after state initialization. 113 */ 114 void 115 Dfix2(tchar **v) 116 { 117 tchar *agargv[GAVSIZ]; 118 119 #ifdef TRACE 120 tprintf("TRACE- Dfix2()\n"); 121 #endif 122 ginit(agargv); /* Initialize glob's area pointers */ 123 Dvp = v; Dcp = S_ /* "" */;/* Setup input vector for Dreadc */ 124 unDgetC(0); unDredc(0); /* Clear out any old peeks (at error) */ 125 dolp = 0; dolcnt = 0; /* Clear out residual $ expands (...) */ 126 while (Dword()) 127 continue; 128 gargv = copyblk(gargv); 129 } 130 131 /* 132 * Get a word. This routine is analogous to the routine 133 * word() in sh.lex.c for the main lexical input. One difference 134 * here is that we don't get a newline to terminate our expansion. 135 * Rather, DgetC will return a DEOF when we hit the end-of-input. 136 */ 137 int 138 Dword(void) 139 { 140 int c, c1; 141 static tchar *wbuf = NULL; 142 static int wbufsiz = BUFSIZ; 143 int wp = 0; 144 bool dolflg; 145 bool sofar = 0; 146 #define DYNAMICBUFFER() \ 147 do { \ 148 if (wp >= wbufsiz) { \ 149 wbufsiz += BUFSIZ; \ 150 wbuf = xrealloc(wbuf, (wbufsiz+1) * sizeof (tchar)); \ 151 } \ 152 } while (0) 153 154 #ifdef TRACE 155 tprintf("TRACE- Dword()\n"); 156 #endif 157 if (wbuf == NULL) 158 wbuf = xalloc((wbufsiz+1) * sizeof (tchar)); 159 loop: 160 c = DgetC(DODOL); 161 switch (c) { 162 163 case DEOF: 164 deof: 165 if (sofar == 0) 166 return (0); 167 /* finish this word and catch the code above the next time */ 168 unDredc(c); 169 /* fall into ... */ 170 171 case '\n': 172 wbuf[wp] = 0; 173 goto ret; 174 175 case ' ': 176 case '\t': 177 goto loop; 178 179 case '`': 180 /* We preserve ` quotations which are done yet later */ 181 wbuf[wp++] = c; 182 case '\'': 183 case '"': 184 /* 185 * Note that DgetC never returns a QUOTES character 186 * from an expansion, so only true input quotes will 187 * get us here or out. 188 */ 189 c1 = c; 190 dolflg = c1 == '"' ? DODOL : 0; 191 for (;;) { 192 c = DgetC(dolflg); 193 if (c == c1) 194 break; 195 if (c == '\n' || c == DEOF) 196 error("Unmatched %c", (tchar) c1); 197 if ((c & (QUOTE|TRIM)) == ('\n' | QUOTE)) 198 --wp; 199 DYNAMICBUFFER(); 200 switch (c1) { 201 202 case '"': 203 /* 204 * Leave any `s alone for later. 205 * Other chars are all quoted, thus `...` 206 * can tell it was within "...". 207 */ 208 wbuf[wp++] = c == '`' ? '`' : c | QUOTE; 209 break; 210 211 case '\'': 212 /* Prevent all further interpretation */ 213 wbuf[wp++] = c | QUOTE; 214 break; 215 216 case '`': 217 /* Leave all text alone for later */ 218 wbuf[wp++] = c; 219 break; 220 } 221 } 222 if (c1 == '`') { 223 DYNAMICBUFFER(); 224 wbuf[wp++] = '`'; 225 } 226 goto pack; /* continue the word */ 227 228 case '\\': 229 c = DgetC(0); /* No $ subst! */ 230 if (c == '\n' || c == DEOF) 231 goto loop; 232 c |= QUOTE; 233 break; 234 #ifdef MBCHAR /* Could be a space char from aux. codeset. */ 235 default: 236 if (isauxsp(c)) goto loop; 237 #endif /* MBCHAR */ 238 } 239 unDgetC(c); 240 pack: 241 sofar = 1; 242 /* pack up more characters in this word */ 243 for (;;) { 244 c = DgetC(DODOL); 245 if (c == '\\') { 246 c = DgetC(0); 247 if (c == DEOF) 248 goto deof; 249 if (c == '\n') 250 c = ' '; 251 else 252 c |= QUOTE; 253 } 254 if (c == DEOF) 255 goto deof; 256 if (cmap(c, _SP|_NL|_Q|_Q1) || 257 isauxsp(c)) { /* sp \t\n'"` or aux. sp */ 258 unDgetC(c); 259 if (cmap(c, QUOTES)) 260 goto loop; 261 DYNAMICBUFFER(); 262 wbuf[wp++] = 0; 263 goto ret; 264 } 265 DYNAMICBUFFER(); 266 wbuf[wp++] = c; 267 } 268 ret: 269 Gcat(S_ /* "" */, wbuf); 270 return (1); 271 } 272 273 /* 274 * Get a character, performing $ substitution unless flag is 0. 275 * Any QUOTES character which is returned from a $ expansion is 276 * QUOTEd so that it will not be recognized above. 277 */ 278 int 279 DgetC(int flag) 280 { 281 int c; 282 283 top: 284 if (c = Dpeekc) { 285 Dpeekc = 0; 286 return (c); 287 } 288 if (lap) { 289 c = *lap++ & (QUOTE|TRIM); 290 if (c == 0) { 291 lap = 0; 292 goto top; 293 } 294 quotspec: 295 /* 296 * don't quote things if there was an error (err!=0) 297 * the input is original, not from a substitution and 298 * therefore should not be quoted 299 */ 300 if (!err && cmap(c, QUOTES)) 301 return (c | QUOTE); 302 return (c); 303 } 304 if (dolp) { 305 if (c = *dolp++ & (QUOTE|TRIM)) 306 goto quotspec; 307 if (dolcnt > 0) { 308 setDolp(*dolnxt++); 309 --dolcnt; 310 return (' '); 311 } 312 dolp = 0; 313 } 314 if (dolcnt > 0) { 315 setDolp(*dolnxt++); 316 --dolcnt; 317 goto top; 318 } 319 c = Dredc(); 320 if (c == '$' && flag) { 321 Dgetdol(); 322 goto top; 323 } 324 return (c); 325 } 326 327 tchar *nulvec[] = { 0 }; 328 struct varent nulargv = { nulvec, S_argv, 0 }; 329 330 /* 331 * Handle the multitudinous $ expansion forms. 332 * Ugh. 333 */ 334 void 335 Dgetdol(void) 336 { 337 tchar *np; 338 struct varent *vp; 339 tchar name[MAX_VREF_LEN]; 340 int c, sc; 341 int subscr = 0, lwb = 1, upb = 0; 342 bool dimen = 0, bitset = 0; 343 tchar wbuf[BUFSIZ]; 344 345 #ifdef TRACE 346 tprintf("TRACE- Dgetdol()\n"); 347 #endif 348 dolmod = dolmcnt = 0; 349 c = sc = DgetC(0); 350 if (c == '{') 351 c = DgetC(0); /* sc is { to take } later */ 352 if ((c & TRIM) == '#') 353 dimen++, c = DgetC(0); /* $# takes dimension */ 354 else if (c == '?') 355 bitset++, c = DgetC(0); /* $? tests existence */ 356 switch (c) { 357 358 case '$': 359 if (dimen || bitset) 360 syntax: 361 error("Variable syntax"); /* No $?$, $#$ */ 362 setDolp(doldol); 363 goto eatbrac; 364 365 case '<'|QUOTE: 366 if (dimen || bitset) 367 goto syntax; /* No $?<, $#< */ 368 for (np = wbuf; read_(OLDSTD, np, 1) == 1; np++) { 369 if (np >= &wbuf[BUFSIZ-1]) 370 error("$< line too long"); 371 if (*np <= 0 || *np == '\n') 372 break; 373 } 374 *np = 0; 375 /* 376 * KLUDGE: dolmod is set here because it will 377 * cause setDolp to call domod and thus to copy wbuf. 378 * Otherwise setDolp would use it directly. If we saved 379 * it ourselves, no one would know when to free it. 380 * The actual function of the 'q' causes filename 381 * expansion not to be done on the interpolated value. 382 */ 383 dolmod = 'q'; 384 dolmcnt = 10000; 385 setDolp(wbuf); 386 goto eatbrac; 387 388 case DEOF: 389 case '\n': 390 goto syntax; 391 392 case '*': 393 (void) strcpy_(name, S_argv); 394 vp = adrof(S_argv); 395 subscr = -1; /* Prevent eating [...] */ 396 break; 397 398 default: 399 np = name; 400 if (digit(c)) { 401 if (dimen) 402 goto syntax; /* No $#1, e.g. */ 403 subscr = 0; 404 do { 405 subscr = subscr * 10 + c - '0'; 406 c = DgetC(0); 407 } while (digit(c)); 408 unDredc(c); 409 if (subscr < 0) 410 error("Subscript out of range"); 411 if (subscr == 0) { 412 if (bitset) { 413 dolp = file ? S_1/*"1"*/ : S_0/*"0"*/; 414 goto eatbrac; 415 } 416 if (file == 0) 417 error("No file for $0"); 418 setDolp(file); 419 goto eatbrac; 420 } 421 if (bitset) 422 goto syntax; 423 vp = adrof(S_argv); 424 if (vp == 0) { 425 vp = &nulargv; 426 goto eatmod; 427 } 428 break; 429 } 430 if (!alnum(c)) 431 goto syntax; 432 for (;;) { 433 *np++ = c; 434 c = DgetC(0); 435 if (!alnum(c)) 436 break; 437 /* if variable name is > 20, complain */ 438 if (np >= &name[MAX_VAR_LEN]) 439 error("Variable name too long"); 440 441 } 442 *np++ = 0; 443 unDredc(c); 444 vp = adrof(name); 445 } 446 if (bitset) { 447 /* getenv() to getenv_(), because 'name''s type is now tchar * */ 448 /* no need to xalloc */ 449 dolp = (vp || getenv_(name)) ? S_1 /*"1"*/ : S_0/*"0"*/; 450 goto eatbrac; 451 } 452 if (vp == 0) { 453 /* getenv() to getenv_(), because 'name''s type is now tchar * */ 454 /* no need to xalloc */ 455 np = getenv_(name); 456 if (np) { 457 addla(np); 458 goto eatbrac; 459 } 460 udvar(name); 461 /*NOTREACHED*/ 462 } 463 c = DgetC(0); 464 upb = blklen(vp->vec); 465 if (dimen == 0 && subscr == 0 && c == '[') { 466 np = name; 467 for (;;) { 468 c = DgetC(DODOL); /* Allow $ expand within [ ] */ 469 if (c == ']') 470 break; 471 if (c == '\n' || c == DEOF) 472 goto syntax; 473 if (np >= &name[MAX_VREF_LEN]) 474 error("Variable reference too long"); 475 *np++ = c; 476 } 477 *np = 0, np = name; 478 if (dolp || dolcnt) /* $ exp must end before ] */ 479 goto syntax; 480 if (!*np) 481 goto syntax; 482 if (digit(*np)) { 483 int i = 0; 484 485 while (digit(*np)) 486 i = i * 10 + *np++ - '0'; 487 /* if ((i < 0 || i > upb) && !any(*np, "-*")) {*/ 488 if ((i < 0 || i > upb) && (*np!='-') && (*np!='*')) { 489 oob: 490 setname(vp->v_name); 491 error("Subscript out of range"); 492 } 493 lwb = i; 494 if (!*np) 495 upb = lwb, np = S_AST/*"*"*/; 496 } 497 if (*np == '*') 498 np++; 499 else if (*np != '-') 500 goto syntax; 501 else { 502 int i = upb; 503 504 np++; 505 if (digit(*np)) { 506 i = 0; 507 while (digit(*np)) 508 i = i * 10 + *np++ - '0'; 509 if (i < 0 || i > upb) 510 goto oob; 511 } 512 if (i < lwb) 513 upb = lwb - 1; 514 else 515 upb = i; 516 } 517 if (lwb == 0) { 518 if (upb != 0) 519 goto oob; 520 upb = -1; 521 } 522 if (*np) 523 goto syntax; 524 } else { 525 if (subscr > 0) 526 if (subscr > upb) 527 lwb = 1, upb = 0; 528 else 529 lwb = upb = subscr; 530 unDredc(c); 531 } 532 if (dimen) { 533 tchar *cp = putn(upb - lwb + 1); 534 535 addla(cp); 536 xfree(cp); 537 } else { 538 eatmod: 539 c = DgetC(0); 540 if (c == ':') { 541 c = DgetC(0), dolmcnt = 1; 542 if (c == 'g') 543 c = DgetC(0), dolmcnt = 10000; 544 if (!any(c, S_htrqxe)) 545 error("Bad : mod in $"); 546 dolmod = c; 547 if (c == 'q') 548 dolmcnt = 10000; 549 } else 550 unDredc(c); 551 dolnxt = &vp->vec[lwb - 1]; 552 dolcnt = upb - lwb + 1; 553 } 554 eatbrac: 555 if (sc == '{') { 556 c = Dredc(); 557 if (c != '}') 558 goto syntax; 559 } 560 } 561 562 void 563 setDolp(tchar *cp) 564 { 565 tchar *dp; 566 567 #ifdef TRACE 568 tprintf("TRACE- setDolp()\n"); 569 #endif 570 if (dolmod == 0 || dolmcnt == 0) { 571 dolp = cp; 572 return; 573 } 574 dp = domod(cp, dolmod); 575 if (dp) { 576 dolmcnt--; 577 addla(dp); 578 xfree(dp); 579 } else 580 addla(cp); 581 dolp = S_/*""*/; 582 } 583 584 void 585 unDredc(int c) 586 { 587 588 Dpeekrd = c; 589 } 590 591 int 592 Dredc() 593 { 594 int c; 595 596 if (c = Dpeekrd) { 597 Dpeekrd = 0; 598 return (c); 599 } 600 if (Dcp && (c = *Dcp++)) 601 return (c&(QUOTE|TRIM)); 602 if (*Dvp == 0) { 603 Dcp = 0; 604 return (DEOF); 605 } 606 Dcp = *Dvp++; 607 return (' '); 608 } 609 610 void 611 Dtestq(int c) 612 { 613 614 if (cmap(c, QUOTES)) 615 gflag = 1; 616 } 617 618 /* 619 * Form a shell temporary file (in unit 0) from the words 620 * of the shell input up to a line the same as "term". 621 * Unit 0 should have been closed before this call. 622 */ 623 void 624 heredoc(tchar *term) 625 { 626 int c; 627 tchar *Dv[2]; 628 tchar obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ]; 629 int ocnt, lcnt, mcnt; 630 tchar *lbp, *obp, *mbp; 631 tchar **vp; 632 bool quoted; 633 tchar shtemp[] = {'/', 't', 'm', 'p', '/', 's', 'h', 'X', 'X', 'X', 634 'X', 'X', 'X', 0}; 635 int fd1; 636 637 #ifdef TRACE 638 tprintf("TRACE- heredoc()\n"); 639 #endif 640 if ((fd1 = mkstemp_(shtemp)) < 0) 641 Perror(shtemp); 642 (void) unlink_(shtemp); /* 0 0 inode! */ 643 unsetfd(fd1); 644 Dv[0] = term; Dv[1] = NOSTR; gflag = 0; 645 trim(Dv); rscan(Dv, Dtestq); quoted = gflag; 646 ocnt = BUFSIZ; obp = obuf; 647 for (;;) { 648 /* 649 * Read up a line 650 */ 651 lbp = lbuf; lcnt = BUFSIZ - 4; 652 for (;;) { 653 c = readc(1); /* 1 -> Want EOF returns */ 654 if (c < 0) { 655 setname(term); 656 bferr("<< terminator not found"); 657 } 658 if (c == '\n') 659 break; 660 if (c &= TRIM) { 661 *lbp++ = c; 662 if (--lcnt < 0) { 663 setname(S_LESLES/*"<<"*/); 664 error("Line overflow"); 665 } 666 } 667 } 668 *lbp = 0; 669 670 /* 671 * Compare to terminator -- before expansion 672 */ 673 if (eq(lbuf, term)) { 674 (void) write_(0, obuf, BUFSIZ - ocnt); 675 (void) lseek(0, (off_t)0, 0); 676 return; 677 } 678 679 /* 680 * If term was quoted or -n just pass it on 681 */ 682 if (quoted || noexec) { 683 *lbp++ = '\n'; *lbp = 0; 684 for (lbp = lbuf; c = *lbp++;) { 685 *obp++ = c; 686 if (--ocnt == 0) { 687 (void) write_(0, obuf, BUFSIZ); 688 obp = obuf; ocnt = BUFSIZ; 689 } 690 } 691 continue; 692 } 693 694 /* 695 * Term wasn't quoted so variable and then command 696 * expand the input line 697 */ 698 Dcp = lbuf; Dvp = Dv + 1; mbp = mbuf; mcnt = BUFSIZ - 4; 699 for (;;) { 700 c = DgetC(DODOL); 701 if (c == DEOF) 702 break; 703 if ((c &= TRIM) == 0) 704 continue; 705 /* \ quotes \ $ ` here */ 706 if (c =='\\') { 707 c = DgetC(0); 708 /* if (!any(c, "$\\`"))*/ 709 if ((c!='$')&&(c!='\\')&&(c!='`')) 710 unDgetC(c | QUOTE), c = '\\'; 711 else 712 c |= QUOTE; 713 } 714 *mbp++ = c; 715 if (--mcnt == 0) { 716 setname(S_LESLES/*"<<"*/); 717 bferr("Line overflow"); 718 } 719 } 720 *mbp++ = 0; 721 722 /* 723 * If any ` in line do command substitution 724 */ 725 mbp = mbuf; 726 if (any('`', mbp)) { 727 /* 728 * 1 arg to dobackp causes substitution to be literal. 729 * Words are broken only at newlines so that all blanks 730 * and tabs are preserved. Blank lines (null words) 731 * are not discarded. 732 */ 733 vp = dobackp(mbuf, 1); 734 } else 735 /* Setup trivial vector similar to return of dobackp */ 736 Dv[0] = mbp, Dv[1] = NOSTR, vp = Dv; 737 738 /* 739 * Resurrect the words from the command substitution 740 * each separated by a newline. Note that the last 741 * newline of a command substitution will have been 742 * discarded, but we put a newline after the last word 743 * because this represents the newline after the last 744 * input line! 745 */ 746 for (; *vp; vp++) { 747 for (mbp = *vp; *mbp; mbp++) { 748 *obp++ = *mbp & TRIM; 749 if (--ocnt == 0) { 750 (void) write_(0, obuf, BUFSIZ); 751 obp = obuf; ocnt = BUFSIZ; 752 } 753 } 754 *obp++ = '\n'; 755 if (--ocnt == 0) { 756 (void) write_(0, obuf, BUFSIZ); 757 obp = obuf; ocnt = BUFSIZ; 758 } 759 } 760 if (pargv) 761 blkfree(pargv), pargv = 0; 762 } 763 } 764