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