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