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