|
|
1.1 root 1: static char *sccsid = "@(#)sh.dol.c 4.1 10/9/80";
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: return;
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.