|
|
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.