|
|
1.1 root 1: /*
2: * sh/eval.c
3: * Bourne shell.
4: * Evaluation of parameter substitution, command substitution,
5: * blank interpretation, and file name generation.
6: */
7:
8: #include <sys/compat.h>
9: #include <sys/stat.h>
10: #include <fcntl.h>
11: #include "sh.h"
12:
13: struct eval_context {
14: char * arcp; /* character position in argument */
15: int argf; /* First character of argument flag */
16: int argg; /* Glob seek, escape quoted glob chars */
17: int argq; /* Quotation flag, no blanks or glob */
18: } _eval [1];
19:
20: LOCAL void variable ();
21: LOCAL void special ();
22: LOCAL void graves ();
23:
24: LOCAL void add_char ();
25: LOCAL void add_quoted ();
26: LOCAL void add_arg ();
27: LOCAL void end_arg ();
28:
29: #define EVAL_NO_QUOTE 0
30: #define EVAL_QUOTED 1
31: #define EVAL_FORCE_QUOTE 2
32:
33: #define EVAL_INPUT_SOURCE(eval,cp) (void) ((eval)->arcp = (cp))
34: #define EVAL_NEXT_CHAR(eval) (* (eval)->arcp ++)
35: #define EVAL_UNGET_CHAR(eval,ch) (void) ((eval)->arcp --)
36:
37: #define EVAL_SET_QUOTING(eval,q) (void) ((eval)->argq = (q))
38: #define EVAL_ADD_QUOTE(eval,q) \
39: (void) ((eval)->argq = ((eval)->argq & ~ EVAL_QUOTED) | (q))
40: #define EVAL_IS_QUOTED(eval) ((eval)->argq)
41:
42: #define EVAL_SET_GLOB(eval,g) (void) ((eval)->argg = (g))
43: #define EVAL_IS_GLOBSEEK(eval) ((eval)->argg != 0)
44:
45: #define EVAL_MADE_ARGUMENT(eval) (void) ((eval)->argf = 0)
46: #define EVAL_ARGUMENT_IF_QUOTED(eval) (void) ((eval)->argf &= \
47: ~ (eval)->argq)
48: #define EVAL_NO_ARGUMENT(eval) (void) ((eval)->argf = 1, strp = strt)
49: #define EVAL_IS_NO_ARGUMENT(eval) ((eval)->argf != 0)
50:
51:
52: /*
53: * Evaluate a string.
54: */
55:
56: void
57: eval(cp, f)
58: char *cp;
59: {
60: register int m, c;
61:
62: EVAL_INPUT_SOURCE (_eval, cp);
63: EVAL_NO_ARGUMENT (_eval);
64:
65: if (f == EHERE) {
66: m = MHERE;
67: EVAL_SET_QUOTING (_eval, EVAL_FORCE_QUOTE);
68: } else if (f == EWORD) {
69: m = MNQUO;
70: EVAL_SET_QUOTING (_eval, EVAL_FORCE_QUOTE);
71: } else {
72: m = MNQUO;
73: EVAL_SET_QUOTING (_eval, EVAL_NO_QUOTE);
74: }
75: EVAL_SET_GLOB (_eval, f == EARGS || f == EPATT);
76:
77: while ((c = EVAL_NEXT_CHAR (_eval)) != '\0') {
78: if (! class (c, m)) {
79: add_char (c);
80: continue;
81: }
82: switch (c) {
83: case '"': /* m == MNQUO || m == MDQUO */
84: if ((m ^= (MDQUO ^ MNQUO)) == MDQUO) {
85:
86: EVAL_ADD_QUOTE (_eval, EVAL_QUOTED);
87:
88: if ((c = EVAL_NEXT_CHAR (_eval)) == '\"')
89: EVAL_MADE_ARGUMENT (_eval);
90:
91: EVAL_UNGET_CHAR (_eval, c);
92: } else
93: EVAL_ADD_QUOTE (_eval, 0);
94: continue;
95:
96: case '\'': /* m == MNQUO */
97: while ((c = EVAL_NEXT_CHAR (_eval)) != '\'' && c != 0)
98: add_quoted (c);
99: EVAL_MADE_ARGUMENT (_eval);
100: continue;
101:
102: case '\\': /* m == MDQUO || m == MNQUO */
103: c = EVAL_NEXT_CHAR (_eval);
104: if (m != MNQUO && ! class (c, m)) {
105: add_char ('\\');
106: add_char (c);
107: } else
108: add_quoted (c);
109: EVAL_MADE_ARGUMENT (_eval);
110: continue;
111:
112: case '$': /* m == MNQUO || m = MDQUO */
113: variable ();
114: continue;
115:
116: case '`': /* m == MNQUO || m = MDQUO */
117: graves (m);
118: continue;
119:
120: default:
121: add_char (c);
122: continue;
123: }
124: }
125:
126: if (f == EARGS)
127: end_arg ();
128: else
129: * strp ++ = '\0';
130: }
131:
132:
133: /*
134: * Read the name of a shell variable and perform the appropriate substitution.
135: */
136:
137: LOCAL void
138: variable()
139: {
140: VAR *vp;
141: int s;
142: char *sav;
143: register int c, count, quote;
144: register char * pp;
145: char * name_start = strp;
146: char * alternate_value;
147: int colon_test = 0;
148:
149: s = '\0';
150: c = EVAL_NEXT_CHAR (_eval);
151:
152: if (strchr (SPECIAL_VAR_CHARS, c) != 0) {
153: special (c);
154: return;
155: } else if (class (c, MRVAR)) {
156: do {
157: add_arg (c);
158: c = EVAL_NEXT_CHAR (_eval);
159: } while (class (c, MRVAR));
160:
161: EVAL_UNGET_CHAR (_eval, c);
162: } else if (c != '{') {
163: /* Not a legal variable name, put it back. */
164: add_char ('$');
165: add_char (c);
166: return;
167: } else {
168: /* c == '{' */
169:
170: c = EVAL_NEXT_CHAR (_eval);
171:
172: if (strchr (SPECIAL_VAR_CHARS, c) != NULL) {
173: int peek;
174:
175: if ((peek = EVAL_NEXT_CHAR (_eval)) == '}') {
176:
177: /* Allow specials of the form "${?}" etc. */
178: special (c);
179: return;
180: }
181: EVAL_UNGET_CHAR (_eval, peek);
182: }
183:
184: for (;;) {
185: if (! class (c, MRVAR))
186: break;
187: add_arg (c);
188: c = EVAL_NEXT_CHAR (_eval);
189: }
190:
191: if ((colon_test = c == ':') != 0)
192: c = EVAL_NEXT_CHAR (_eval);
193:
194: if (strchr ("-=?+", c) != NULL) {
195: /*
196: * ${VAR [-=?+] word}
197: * Stash away the value of the intemediate character
198: * and store a '=' character to stroke the variable-
199: * storage machinery.
200: */
201:
202: s = c;
203: add_arg ('=');
204: alternate_value = strp;
205: if ((quote = EVAL_NEXT_CHAR (_eval)) != '"' &&
206: quote != '\'') {
207:
208: EVAL_UNGET_CHAR (_eval, quote);
209: quote = 0;
210: }
211:
212: for (count = 1; ; ) {
213: c = EVAL_NEXT_CHAR (_eval);
214: if (c == '}' && count -- == 1)
215: break;
216: else if (c == '$' && quote != '\'') {
217: /*
218: * steve 6/24/92
219: * This truly sleazy hack handles e.g. "${V1-$V2}", oy.
220: * It doesn't do it very well, paying no attention to quotes (for example).
221: */
222: variable ();
223: continue;
224: } else if (c == '{')
225: ++ count;
226: else if (quote != 0 && c == quote) {
227: quote = 0;
228: continue;
229: }
230: add_arg (c);
231: }
232: } else if (colon_test || c != '}') {
233: eillvar (strp = name_start);
234: return;
235: }
236: }
237: add_arg ('\0');
238:
239: c = * name_start;
240:
241: if (class (c, MDIGI)) {
242: if ((c -= '1') >= sargc)
243: pp = NULL;
244: else
245: pp = sargp [c];
246: } else if (namevar (name_start) == 0) {
247: eillvar (strp = name_start);
248: return;
249: } else {
250: pp = NULL;
251: if ((vp = findvar (name_start)) != NULL) {
252: pp = convvar (vp);
253: if (* pp == '\0' && colon_test)
254: pp = NULL; /* regard value "" as not set */
255: }
256: }
257:
258: switch (s) {
259: case '\0':
260: if (uflag != 0 && pp == NULL)
261: enotdef (name_start);
262: break;
263:
264: case '-':
265: if (pp == NULL)
266: pp = alternate_value;
267: break;
268:
269: case '=':
270: if (pp == NULL) {
271: if (class (* name_start, MDIGI) ||
272: strchr (SPECIAL_VAR_CHARS,
273: * name_start) != NULL) {
274:
275: printe ("Illegal substitution");
276: strp = name_start;
277: return;
278: }
279: setsvar (name_start);
280: pp = alternate_value;
281: }
282: break;
283:
284: case '?':
285: if (pp != NULL)
286: break;
287: if (* alternate_value != '\0')
288: prints ("%s\n", alternate_value);
289: else {
290: alternate_value [-1] = '\0';
291: enotdef (name_start);
292: }
293: reset (RUABORT);
294: NOTREACHED;
295:
296: case '+':
297: if (pp != NULL)
298: pp = alternate_value;
299: break;
300: }
301:
302: strp = name_start;
303:
304: if (pp != NULL)
305: while ((c = * pp ++) != '\0')
306: add_char (c);
307:
308: EVAL_ARGUMENT_IF_QUOTED (_eval);
309: }
310:
311:
312: /*
313: * Return the value of the special shell variables.
314: * No check for end of buffer.
315: */
316:
317: LOCAL void
318: special(n)
319: int n;
320: {
321: int flag;
322: char * sp;
323:
324: switch (n) {
325: case '#':
326: n = sargc;
327: goto maked;
328:
329: case '?':
330: n = slret;
331: goto maked;
332:
333: case '$':
334: n = shpid;
335: goto maked;
336:
337: case '!':
338: n = sback;
339: goto maked;
340:
341: maked:
342: sprintf (strp, "%d", n);
343: sp = strp;
344: break;
345:
346: case '-':
347:
348: for (sp = & eflag; sp <= & xflag ; sp ++)
349: if (* sp)
350: add_char (* sp);
351:
352: EVAL_ARGUMENT_IF_QUOTED (_eval);
353: return;
354:
355: case '@':
356: case '*':
357: flag = EVAL_IS_QUOTED (_eval) == EVAL_QUOTED && n == '@';
358: for (n = 0; n < sargc; n++) {
359: if (n) {
360: if (flag)
361: end_arg ();
362: else
363: add_char (' ');
364: }
365:
366: sp = sargp [n];
367: while (* sp)
368: add_char (* sp ++);
369:
370: /*
371: * Make sure that arguments like "" get handled
372: * properly when expanding "$@"
373: */
374:
375: if (flag)
376: EVAL_ARGUMENT_IF_QUOTED (_eval);
377: }
378: return;
379:
380: case '0':
381: sp = sarg0;
382: break;
383:
384: default:
385: if (n - '1' >= sargc) {
386: if (uflag)
387: printe ("Unset parameter: %c", n);
388: EVAL_ARGUMENT_IF_QUOTED (_eval);
389: return;
390: }
391: sp = sargp [n - '1'];
392: break;
393: }
394:
395: while (* sp)
396: add_char (* sp ++);
397:
398: EVAL_ARGUMENT_IF_QUOTED (_eval);
399: }
400:
401:
402: /*
403: * Read and evaluate a command found between graves.
404: *
405: * NB : Backslash-quoting inside graves was not supported properly before,
406: * and I'm not sure that I've got it right. The idea is that before passing
407: * things off to session (), we process the appropriate backslash-escapes.
408: * What consists of an "appropriate" escape depends on whether the graves
409: * appeared within a double-quoted section (in which case we recognise the
410: * specials appropriate to that) or not (in which case we recognised the
411: * characters $`\ as specials. We also process '$'-expansions in graves now,
412: * but not globs.
413: *
414: * Since we are removing some bashslashes here, we should be building into
415: * a temporary buffer. We append our work onto the global "strp" buffer and
416: * cut it back once we have finished.
417: */
418:
419: LOCAL void
420: graves (quotemode)
421: int quotemode;
422: {
423: int pipev[2], f, oslret;
424: char * ostrp;
425: register FILE *fp;
426: register int c;
427: register int nnl;
428:
429: ostrp = strp;
430: oslret = slret;
431:
432: while ((c = EVAL_NEXT_CHAR (_eval)) != '`') {
433: if (c != '\\') {
434: add_arg (c);
435: continue;
436: }
437: c = EVAL_NEXT_CHAR (_eval);
438:
439: if (! (quotemode == MDQUO && class (c, MDQUO)) &&
440: (c != '$' && c != '\\' && c != '`'))
441: add_arg ('\\');
442:
443: add_arg (c);
444: }
445: * strp = 0;
446:
447: if ((f = pipeline (pipev)) == 0) {
448: slret = oslret; /* in case grave command uses $? */
449: dup2 (pipev [1], 1);
450: close (pipev [0]);
451: close (pipev [1]);
452: exit (session (SARGS, ostrp));
453: NOTREACHED;
454: }
455:
456: close (pipev [1]);
457: if ((fp = fdopen (pipev [0], "r")) == NULL) {
458: close (pipev [0]);
459: ecantfdop ();
460: return;
461: }
462: strp = ostrp;
463: nnl = 0;
464:
465: while ((c = getc (fp)) != EOF) {
466: if (! recover (IEVAL)) {
467: #ifdef VERBOSE
468: if (xflag) prints ("Interrupt in eval\n");
469: #endif
470: errflag ++;
471: break;
472: }
473: if (c == '\n')
474: ++ nnl;
475: else {
476: while (nnl) {
477: nnl --;
478: add_char ('\n');
479: }
480: add_char (c);
481: }
482: }
483:
484: /*
485: * If we expanded to something, we have an arg. If we are in double-
486: * quotes, we have an arg. Otherwise, we have an arg if we had an arg
487: * before.
488: */
489:
490: EVAL_ARGUMENT_IF_QUOTED (_eval);
491: fclose (fp);
492: waitc (f);
493: }
494:
495:
496: /*
497: * Add a character to the current argument.
498: * If no quotation is set, pick off blanks and globs.
499: */
500:
501: LOCAL void
502: add_char(c)
503: register int c;
504: {
505: if (EVAL_IS_QUOTED (_eval) == 0) {
506: if (strchr (vifs, c) != NULL) {
507: end_arg ();
508: return;
509: }
510: if (EVAL_IS_GLOBSEEK (_eval) && class (c, MGLOB)) {
511: add_arg (c);
512: EVAL_MADE_ARGUMENT (_eval);
513: return;
514: }
515: }
516: add_quoted (c);
517: }
518:
519:
520: /*
521: * Add a quoted character to the current argument.
522: * if argg is set, then glob characters are quoted with a \,
523: * as well as \ itself.
524: */
525:
526: LOCAL void
527: add_quoted(c) register int c;
528: {
529: if (EVAL_IS_GLOBSEEK (_eval) && (class (c, MGLOB) || c == '\\'))
530: add_arg ('\\');
531:
532: add_arg (c);
533: EVAL_MADE_ARGUMENT (_eval);
534: }
535:
536:
537: /*
538: * Add a character to the current argument
539: * and check for end of buffer.
540: */
541:
542: LOCAL void
543: add_arg (c) register int c;
544: {
545: if (strp >= strt + STRSIZE) /* Should do more */
546: etoolong ("in add_arg ()");
547: else
548: * strp ++ = c;
549: }
550:
551:
552: /*
553: * Terminate the current argument if it is non-empty.
554: * If argg is set, then glob the argument to expand globs
555: * or to simply remove any quotes.
556: */
557:
558: LOCAL void
559: end_arg ()
560: {
561: if (EVAL_IS_NO_ARGUMENT (_eval))
562: return;
563:
564: * strp ++ = '\0';
565: if (EVAL_IS_GLOBSEEK (_eval))
566: glob1 (duplstr (strt, 0));
567: else {
568: nargv = addargl (nargv, duplstr (strt, 0));
569: nargc += 1;
570: }
571:
572: EVAL_NO_ARGUMENT (_eval);
573: }
574:
575:
576: /*
577: * Evaluate a here document.
578: */
579:
580: int
581: evalhere(u2)
582: {
583: register int u1;
584: register FILE *f2;
585: char buf[128];
586: char *tmp;
587:
588: tmp = shtmp ();
589: if ((u1 = creat (tmp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
590: ecantmake (tmp);
591: return -1;
592: }
593: if ((f2 = fdopen (u2, "r")) == NULL) {
594: ecantfdop ();
595: close (u1);
596: close (u2);
597: return -1;
598: }
599: while (fgets (buf, sizeof (buf), f2) != NULL) {
600: eval (buf, EHERE);
601: write (u1, strt, strp - 1 - strt);
602: }
603: close (u1);
604: fclose (f2);
605: if ((u2 = open (tmp, O_RDONLY)) < 0) {
606: ecantopen (tmp);
607: u2 = -1;
608: }
609: unlink (tmp);
610: return u2;
611: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.