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