|
|
1.1 root 1: /*
2: * Expansion - quoting, separation, substitution, globbing
3: */
4:
5: static char *RCSid = "$Header: /newbits/usr/bin/korn/RCS/eval.c,v 1.2 91/08/01 12:39:55 bin Exp Locker: bin $";
6:
7: #include <stddef.h>
8: #include <stdio.h>
9: #include <string.h>
10: #include <errno.h>
11: #include <setjmp.h>
12: #include <unistd.h>
13: #include <sys/types.h>
14: #include <dirent.h>
15: #include <pwd.h>
16: #include "sh.h"
17: #include "lex.h"
18: #include "tree.h"
19: #include "table.h"
20: #include "expand.h"
21:
22: #if COHERENT
23: struct dirent *readdir(); /* missing from headers */
24: DIR *opendir();
25: #endif
26:
27: /*
28: * string expansion
29: *
30: * first pass: quoting, IFS separation, ${} and $() substitution.
31: * second pass: filename expansion (*?[]~).
32: */
33:
34: /* expansion generator state */
35: typedef struct Expand {
36: /* int type; */ /* see expand() */
37: char *str; /* string */
38: union {
39: char **strv; /* string[] */
40: FILE *file; /* file */
41: } u; /* source */
42: short split; /* split "$@"*/
43: } Expand;
44:
45: #define XBASE 0 /* scanning original */
46: #define XSUB 1 /* expanding ${} string */
47: #define XARGSEP 2 /* ifs0 between "$@" */
48: #define XARG 3 /* expanding $*, $@ */
49: #define XCOM 4 /* expanding $() */
50:
51: static void expand ARGS((char *, XPtrV *, int));
52: static int comsub ARGS((Expand *, char *comm));
53: static int varsub ARGS((Expand *, char *name, int stype));
54: static void glob ARGS((char *cp, XPtrV *wp));
55: static void globit ARGS((char *ds, char *dp, char *sp, XPtrV *wp, int check));
56: static char *tilde ARGS((char *wp));
57: static char *trimsub ARGS((char *str, char *pat, int how));
58:
59: int ifs0 = ' '; /* todo: first char of $IFS */
60:
61: /* compile and expand word */
62: char *
63: substitute(cp, f)
64: char Const *cp;
65: int f;
66: {
67: struct source *s, *sold;
68:
69: sold = source;
70: s = pushs(SWSTR);
71: s->str = (char *) cp;
72: source = s;
73: if (yylex(ONEWORD) != LWORD)
74: errorf("eval:substitute error\n");
75: source = sold;
76: return evalstr(yylval.cp, f);
77: }
78:
79: /*
80: * expand arg-list
81: */
82: char **
83: eval(ap, f)
84: register char **ap;
85: {
86: XPtrV w;
87:
88: if (*ap == NULL)
89: return ap;
90: XPinit(w, 32);
91: XPput(w, NULL); /* space for shell name */
92: while (*ap != NULL)
93: expand(*ap++, &w, f);
94: XPput(w, NULL);
95: return (char **) XPclose(w) + 1;
96: }
97:
98: /*
99: * expand string
100: */
101: char *
102: evalstr(cp, f)
103: register char *cp;
104: int f;
105: {
106: XPtrV w;
107:
108: XPinit(w, 1);
109: expand(cp, &w, f);
110: cp = (XPsize(w) == 0) ? "" : (char*) *XPptrv(w);
111: XPfree(w);
112: return cp;
113: }
114:
115: /* for nested substitution: ${var:=$var2} */
116: typedef struct SubType {
117: short type; /* [=+-?%#] action after expanded word */
118: short base; /* begin position of expanded word */
119: char *name; /* name for ${var=word} */
120: } SubType;
121:
122: #if 1
123: mypr(cp)
124: char *cp;
125: {
126: fprintf(stderr, "%r", &cp);
127: }
128: #endif
129:
130: static void
131: expand(cp, wp, f)
132: char *cp; /* input word */
133: register XPtrV *wp; /* output words */
134: int f; /* DO* flags */
135: {
136: register int c;
137: register int type = XBASE; /* expansion type */
138: register int quote = 0; /* quoted */
139: XString ds; /* destination string */
140: register char *dp, *sp; /* dest., source */
141: int fdo, word, combase; /* second pass flags; have word */
142: Expand x; /* expansion variables */
143: SubType subtype [10]; /* substitution type stack */
144: register SubType *st = subtype + 10;
145:
146: if (cp == NULL)
147: errorf("eval:expand(NULL)\n");
148: if (flag[FNOGLOB]) {
149: f &= ~ DOGLOB;
150: }
151: Xinit(ds, dp, 128); /* init dest. string */
152: type = XBASE;
153: sp = cp;
154: fdo = 0;
155: word = !(f&DOBLANK);
156:
157: while (1) {
158: Xcheck(ds, dp);
159:
160: switch (type) {
161: case XBASE: /* original prefixed string */
162: c = *sp++;
163: switch (c) {
164: case EOS:
165: c = 0;
166: break;
167: case CHAR:
168: c = *sp++;
169: break;
170: case QCHAR:
171: quote |= 2; /* temporary quote */
172: c = *sp++;
173: break;
174: case OQUOTE:
175: word = quote = 1;
176: continue;
177: case CQUOTE:
178: quote = 0;
179: continue;
180: case COMSUB:
181: type = comsub(&x, sp);
182: sp = strchr(sp, 0) + 1;
183: combase = Xsavepos(ds, dp);
184: continue;
185: case OSUBST: /* ${var{:}[=+-?]word} */
186: cp = sp; /* variable */
187: sp = strchr(sp, 0) + 1; /* skip variable */
188: c = (*sp == CSUBST) ? 0 : *sp++;
189: if ((c&0x7F) == '#' || (c&0x7F) == '%')
190: f |= DOPAT;
191: type = varsub(&x, cp, c);
192: if (type == XBASE) { /* expand? */
193: if (st == subtype)
194: errorf("ridiculous ${} nesting\n");
195: --st;
196: st->type = c;
197: st->base = Xsavepos(ds, dp);
198: st->name = cp;
199: } else
200: /* todo: nested OSUBST/CSUBST */
201: sp = wdscan(sp, CSUBST); /* skip word */
202: continue;
203: case CSUBST: /* only get here if expanding word */
204: *dp = 0;
205: if (f&DOGLOB)
206: f &= ~DOPAT;
207: switch (st->type&0x7F) {
208: case '#':
209: case '%':
210: *dp = 0;
211: dp = Xrestpos(ds, dp, st->base);
212: x.str = trimsub(x.str, dp, st->type);
213: type = XSUB;
214: continue;
215: case '=':
216: #if 0
217: if ((x.u.vp->flag&RDONLY))
218: errorf("cannot set readonly %s\n", cp);
219: #endif
220: setstr(global(st->name), Xrestpos(ds, dp, st->base));
221: break;
222: case '?':
223: if (dp == Xrestpos(ds, dp, st->base))
224: errorf("missing value for %s\n", cp);
225: else
226: errorf("%s\n", Xrestpos(ds, dp, st->base));
227: }
228: st++;
229: type = XBASE;
230: continue;
231: }
232: break;
233:
234: case XSUB:
235: if ((c = *x.str++) == 0) {
236: type = XBASE;
237: continue;
238: }
239: break;
240:
241: case XARGSEP:
242: type = XARG;
243: quote = 1;
244: case XARG:
245: if ((c = *x.str++) == 0) {
246: if ((x.str = *x.u.strv++) == NULL) {
247: type = XBASE;
248: continue;
249: } else if (quote && x.split) {
250: /* terminate word for "$@" */
251: type = XARGSEP;
252: quote = 0;
253: }
254: c = ifs0;
255: }
256: break;
257:
258: case XCOM:
259: c = getc(x.u.file);
260: if (quote) {
261: if (c == EOF) {
262: cp = Xrestpos(ds, sp, combase);
263: for (dp--; dp >= cp && *dp == '\n'; dp--)
264: ;
265: dp++;
266: fclose(x.u.file);
267: if (x.split)
268: waitlast();
269: type = XBASE;
270: continue;
271: }
272: } else { /* this part is probably redundant */
273: if (c == EOF || c == '\n') {
274: while ((c = getc(x.u.file)) == '\n')
275: ;
276: if (c == EOF) {
277: fclose(x.u.file);
278: if (x.split)
279: waitlast();
280: type = XBASE;
281: continue;
282: }
283: ungetc(c, x.u.file);
284: c = ifs0;
285: }
286: }
287: break;
288: }
289:
290: /* check for end of word or IFS separation */
291: if (c == 0 || !quote && (f&DOBLANK) && ctype(c, C_IFS)) {
292: if (word) {
293: *dp++ = 0;
294: cp = Xclose(ds, dp);
295: if (fdo&DOTILDE)
296: cp = tilde(cp);
297: if (fdo&DOGLOB)
298: glob(cp, wp);
299: else
300: {XPput(*wp, cp);}
301: fdo = word = 0;
302: if (c != 0)
303: Xinit(ds, dp, 128);
304: } else
305: ; /* ignore IFS */
306: if (c == 0)
307: return;
308: } else {
309: /* mark any special second pass chars */
310: if (!quote)
311: switch (c) {
312: case '*':
313: case '?':
314: case '[':
315: if (f&(DOPAT|DOGLOB)) {
316: fdo |= (f&DOGLOB);
317: *dp++ = MAGIC;
318: }
319: break;
320: case '~':
321: if ((f&DOTILDE) && dp == Xstring(ds, dp) ||
322: !(f&DOBLANK) &&
323: (dp[-1] == '=' || dp[-1] == ':')) {
324: fdo |= DOTILDE;
325: *dp++ = MAGIC;
326: }
327: break;
328: }
329: else
330: quote &= ~2; /* undo temporary */
331:
332: word = 1;
333: *dp++ = c; /* save output char */
334: }
335: }
336: }
337:
338: /*
339: * Prepare to generate the string returned by ${} substitution.
340: */
341: static int
342: varsub(xp, sp, stype)
343: register Expand *xp;
344: register char *sp;
345: int stype;
346: {
347: register int c;
348: int type;
349:
350: /* ${#var}, string length or argc */
351: if (sp[0] == '#' && (c = sp[1]) != 0) {
352: c = (c == '*' || c == '@') ? e.loc->argc :
353: strlen(strval(global(sp+1)));
354: xp->str = strsave(ulton((unsigned long)c, 10), ATEMP);
355: return XSUB;
356: }
357:
358: c = sp[0];
359: if (c == '*' || c == '@') {
360: if (e.loc->argc == 0) {
361: xp->str = null;
362: type = XSUB;
363: } else {
364: xp->u.strv = e.loc->argv + 1;
365: xp->str = *xp->u.strv++;
366: xp->split = c == '@'; /* $@ */
367: type = XARG;
368: }
369: } else {
370: xp->str = strval(global(sp));
371: type = XSUB;
372: }
373:
374: c = stype&0x7F;
375: /* test the compiler's code generator */
376: if (c == '%' || c == '#' ||
377: (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
378: c == '=' || c == '-' || c == '?' : c == '+'))
379: type = XBASE; /* expand word instead of variable value */
380: if (type != XBASE && flag[FNOUNSET] && xp->str == null)
381: errorf("%s: unset variable\n", sp);
382: return type;
383: }
384:
385: /*
386: * Run the command in $(...) and read its output.
387: */
388: static int
389: comsub(xp, cp)
390: register Expand *xp;
391: char *cp;
392: {
393: Source *s;
394: register struct op *t;
395: FILE *fi;
396:
397: s = pushs(SSTRING);
398: s->str = cp;
399: t = compile(s);
400:
401: if (t != NULL && t->type == TCOM && /* $(<file) */
402: *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
403: register struct ioword *io = *t->ioact;
404:
405: if (io->flag != IOREAD)
406: errorf("funny $() command\n");
407: fi = fopen(evalstr(io->name, DOTILDE), "r");
408: if (fi != NULL)
409: fileno(fi) = savefd(fileno(fi));
410: xp->split = 0; /* no waitlast() */
411: } else {
412: int ofd1, pv[2];
413:
414: openpipe(pv);
415: fi = fdopen(pv[0], "r");
416: ofd1 = savefd(1);
417: dup2(pv[1], 1);
418: close(pv[1]);
419: #if 0
420: exchild(t, XXCOM|XPIPEO);
421: #else
422: execute(t, XFORK|XXCOM|XPIPEO);
423: #endif
424: dup2(ofd1, 1);
425: #if COHERENT
426: close(ofd1);
427: #endif
428: xp->split = 1; /* waitlast() */
429: }
430:
431: if (fi == NULL)
432: errorf("cannot open $() input\n");
433: setvbuf(fi, (char *)NULL, _IOFBF, BUFSIZ);
434: xp->u.file = fi;
435: return XCOM;
436: }
437:
438: /*
439: * perform #pattern and %pattern substitution in ${}
440: */
441:
442: static char *
443: trimsub(str, pat, how)
444: register char *str;
445: char *pat;
446: int how;
447: {
448: register char *end = strchr(str, 0);
449: register char *p, c;
450:
451: switch (how) {
452: case '#': /* shortest at begin */
453: for (p = str; p <= end; p++) {
454: c = *p; *p = '\0';
455: if (gmatch(str, pat)) {
456: *p = c;
457: return p;
458: }
459: *p = c;
460: }
461: break;
462: case '#'|0x80: /* longest match at begin */
463: for (p = end; p >= str; p--) {
464: c = *p; *p = '\0';
465: if (gmatch(str, pat)) {
466: *p = c;
467: return p;
468: }
469: *p = c;
470: }
471: break;
472: case '%': /* shortest match at end */
473: for (p = end; p >= str; p--) {
474: if (gmatch(p, pat)) {
475: *p = '\0';
476: return str;
477: }
478: }
479: break;
480: case '%'|0x80: /* longest match at end */
481: for (p = str; p <= end; p++) {
482: if (gmatch(p, pat)) {
483: *p = '\0';
484: return str;
485: }
486: }
487: break;
488: }
489:
490: return str; /* no match, return string */
491: }
492:
493: /*
494: * glob
495: * Name derived from V6's /etc/glob, the program that expanded filenames.
496: */
497:
498: static char *debunk();
499:
500: static void
501: glob(cp, wp)
502: char *cp;
503: register XPtrV *wp;
504: {
505: char path [PATH];
506: register char *sp = cp;
507: int oldsize;
508:
509: oldsize = XPsize(*wp);
510: globit(path, path, sp, wp, 0);
511:
512: if (XPsize(*wp) == oldsize)
513: {XPput(*wp, debunk(cp));}
514: else
515: qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), xstrcmp);
516: }
517:
518: static void
519: globit(ds, dp, sp, wp, check)
520: char *ds; /* dest path */
521: char *dp; /* dest end */
522: char *sp; /* source path */
523: register XPtrV *wp; /* output list */
524: int check; /* check dest existence */
525: {
526: register char *np; /* next source component */
527: register char *tsp, *tdp;
528:
529: if (sp == NULL) { /* end of source path */
530: if (check && access(ds, 0) < 0)
531: return;
532: XPput(*wp, strsave(ds, ATEMP));
533: return;
534: }
535:
536: if (dp > ds)
537: *dp++ = '/';
538: while (*sp == '/')
539: *dp++ = *sp++;
540: np = strchr(sp, '/');
541: if (np != NULL)
542: *np++ = 0;
543:
544: *dp = 0;
545: if (strchr(sp, MAGIC) == NULL) { /* contains no pattern? */
546: tdp = dp; tsp = sp;
547: while ((*tdp++ = *tsp++) != 0)
548: ;
549: --tdp;
550: globit(ds, tdp, np, wp, check);
551: } else {
552: DIR *dirp;
553: struct dirent *d;
554:
555: dirp = opendir(ds);
556: if (dirp == NULL)
557: goto Nodir;
558: while ((d = readdir(dirp)) != NULL) {
559: tsp = d->d_name;
560: if (tsp[0] == '.' &&
561: (tsp[1] == 0 || tsp[1] == '.' && tsp[2] == 0))
562: continue; /* always ignore . and .. */
563: if (*tsp == '.' && *sp != '.' || !gmatch(tsp, sp))
564: continue;
565:
566: tdp = dp;
567: while ((*tdp++ = *tsp++) != 0)
568: ;
569: --tdp;
570: globit(ds, tdp, np, wp, np != NULL);
571: }
572: closedir(dirp);
573: Nodir:;
574: }
575:
576: if (np != NULL)
577: *--np = '/';
578: }
579:
580: /* remove MAGIC from string */
581: static char *
582: debunk(cp)
583: char *cp;
584: {
585: register unsigned char *dp, *sp;
586:
587: for (dp = sp = cp; *sp != 0; sp++)
588: if (*sp != MAGIC)
589: *dp++ = *sp;
590: *dp = 0;
591: return cp;
592: }
593:
594: /*
595: * tilde expansion
596: *
597: * based on a version by Arnold Robbins
598: */
599:
600: static char *homedir();
601:
602: static char *
603: tilde(acp)
604: char *acp;
605: {
606: register unsigned int c;
607: char path[PATH+1];
608: register unsigned char *cp = acp, *wp = path, *dp;
609: char userid[16+1];
610:
611: Again:
612: while (1) {
613: if ((c = *cp++) == 0) {
614: *wp = 0;
615: afree((Void*)acp, ATEMP);
616: return strsave(path, ATEMP);
617: } else if (c == MAGIC && *cp == '~')
618: break;
619: else
620: *wp++ = c;
621: }
622:
623: dp = NULL; /* no output substitution */
624: if (cp[1] == 0 || cp[1] == '/' || cp[1] == ':') /* ~ or ~/ */
625: dp = strval(global("HOME")), cp += 1;
626: else if (cp[1] == '+' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0))
627: dp = strval(global("PWD")), cp += 2;
628: else if (cp[1] == '-' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0))
629: dp = strval(global("OLDPWD")), cp += 2;
630: else if (letter(cp[1])) {
631: char *save = cp;
632: for (dp = userid, cp++; letnum(*cp) && dp < userid+16; )
633: *dp++ = *cp++;
634: *dp = 0;
635: dp = homedir(userid);
636: if (dp == NULL)
637: cp = save;
638: }
639: /* substitute */
640: if (dp != NULL)
641: while (*dp != 0)
642: *wp++ = *dp++;
643: goto Again;
644: }
645:
646: /*
647: * map userid to user's home directory.
648: * todo: implement a cache with the "homedirs" table.
649: * note that 4.3's getpw adds more than 6K to the shell,
650: * and the YP version probably adds much more.
651: * we might consider our own version of getpwnam() to keep the size down.
652: */
653:
654: static char *
655: homedir(name)
656: char *name;
657: {
658: register struct tbl *ap;
659: register struct passwd *pw;
660:
661: ap = tsearch(&homedirs, name, hash(name));
662: if ((ap != NULL && (ap->flag&ISSET)))
663: return ap->val.s;
664: pw = getpwnam(name);
665: if (pw == NULL)
666: return NULL;
667: return pw->pw_dir;
668: }
669:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.