Annotation of src/usr.bin/oldrdist/expand.c, Revision 1.7
1.7 ! pvalchev 1: /* $OpenBSD: expand.c,v 1.6 1997/02/09 19:24:57 deraadt Exp $ */
1.2 deraadt 2:
1.1 dm 3: /*
4: * Copyright (c) 1983, 1993
5: * The Regents of the University of California. All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: * 3. All advertising materials mentioning features or use of this software
16: * must display the following acknowledgement:
17: * This product includes software developed by the University of
18: * California, Berkeley and its contributors.
19: * 4. Neither the name of the University nor the names of its contributors
20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
36: #ifndef lint
37: /* from: static char sccsid[] = "@(#)expand.c 8.1 (Berkeley) 6/9/93"; */
1.7 ! pvalchev 38: static char *rcsid = "$OpenBSD: expand.c,v 1.6 1997/02/09 19:24:57 deraadt Exp $";
1.1 dm 39: #endif /* not lint */
40:
41: #include "defs.h"
42:
43: #define GAVSIZ NCARGS / 6
44: #define LC '{'
45: #define RC '}'
46:
47: static char shchars[] = "${[*?";
48:
49: int which; /* bit mask of types to expand */
50: int eargc; /* expanded arg count */
51: char **eargv; /* expanded arg vectors */
52: char *path;
53: char *pathp;
54: char *lastpathp;
55: char *tilde; /* "~user" if not expanding tilde, else "" */
56: char *tpathp;
57: int nleft;
58:
59: int expany; /* any expansions done? */
60: char *entp;
61: char **sortbase;
62:
63: #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
64: sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
65:
66: static void Cat __P((char *, char *));
67: static void addpath __P((int));
68: static int amatch __P((char *, char *));
69: static int argcmp __P((const void *, const void *));
70: static int execbrc __P((char *, char *));
71: static void expsh __P((char *));
72: static void expstr __P((char *));
73: static int match __P((char *, char *));
74: static void matchdir __P((char *));
75: static int smatch __P((char *, char *));
76:
77: /*
78: * Take a list of names and expand any macros, etc.
79: * wh = E_VARS if expanding variables.
80: * wh = E_SHELL if expanding shell characters.
81: * wh = E_TILDE if expanding `~'.
82: * or any of these or'ed together.
83: *
84: * Major portions of this were snarfed from csh/sh.glob.c.
85: */
86: struct namelist *
87: expand(list, wh)
88: struct namelist *list;
89: int wh;
90: {
91: register struct namelist *nl, *prev;
92: register int n;
93: char pathbuf[BUFSIZ];
94: char *argvbuf[GAVSIZ];
95:
96: if (debug) {
1.7 ! pvalchev 97: printf("expand(%lx, %d)\nlist = ", (long)list, wh);
1.1 dm 98: prnames(list);
99: }
100:
101: if (wh == 0) {
102: register char *cp;
103:
104: for (nl = list; nl != NULL; nl = nl->n_next)
105: for (cp = nl->n_name; *cp; cp++)
106: *cp = *cp & TRIM;
107: return(list);
108: }
109:
110: which = wh;
111: path = tpathp = pathp = pathbuf;
112: *pathp = '\0';
113: lastpathp = &path[sizeof pathbuf - 2];
114: tilde = "";
115: eargc = 0;
116: eargv = sortbase = argvbuf;
117: *eargv = 0;
118: nleft = NCARGS - 4;
119: /*
120: * Walk the name list and expand names into eargv[];
121: */
122: for (nl = list; nl != NULL; nl = nl->n_next)
123: expstr(nl->n_name);
124: /*
125: * Take expanded list of names from eargv[] and build a new list.
126: */
127: list = prev = NULL;
128: for (n = 0; n < eargc; n++) {
129: nl = makenl(NULL);
130: nl->n_name = eargv[n];
131: if (prev == NULL)
132: list = prev = nl;
133: else {
134: prev->n_next = nl;
135: prev = nl;
136: }
137: }
138: if (debug) {
139: printf("expanded list = ");
140: prnames(list);
141: }
142: return(list);
143: }
144:
145: static void
146: expstr(s)
147: char *s;
148: {
149: register char *cp, *cp1;
150: register struct namelist *tp;
151: char *tail;
152: char buf[BUFSIZ];
153: int savec, oeargc;
154: extern char homedir[];
155:
156: if (s == NULL || *s == '\0')
157: return;
158:
1.3 millert 159: if ((which & E_VARS) && (cp = strchr(s, '$')) != NULL) {
1.1 dm 160: *cp++ = '\0';
161: if (*cp == '\0') {
162: yyerror("no variable name after '$'");
163: return;
164: }
165: if (*cp == LC) {
166: cp++;
1.3 millert 167: if ((tail = strchr(cp, RC)) == NULL) {
1.1 dm 168: yyerror("unmatched '{'");
169: return;
170: }
171: *tail++ = savec = '\0';
172: if (*cp == '\0') {
173: yyerror("no variable name after '$'");
174: return;
175: }
176: } else {
177: tail = cp + 1;
178: savec = *tail;
179: *tail = '\0';
180: }
181: tp = lookup(cp, NULL, 0);
182: if (savec != '\0')
183: *tail = savec;
184: if (tp != NULL) {
185: for (; tp != NULL; tp = tp->n_next) {
1.5 millert 186: snprintf(buf, sizeof(buf), "%s%s%s", s,
1.4 millert 187: tp->n_name, tail);
1.1 dm 188: expstr(buf);
189: }
190: return;
191: }
1.5 millert 192: snprintf(buf, sizeof(buf), "%s%s", s, tail);
1.1 dm 193: expstr(buf);
194: return;
195: }
196: if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
197: Cat(s, "");
198: sort();
199: return;
200: }
201: if (*s == '~') {
202: cp = ++s;
203: if (*cp == '\0' || *cp == '/') {
204: tilde = "~";
205: cp1 = homedir;
206: } else {
207: tilde = cp1 = buf;
208: *cp1++ = '~';
209: do
210: *cp1++ = *cp++;
211: while (*cp && *cp != '/');
212: *cp1 = '\0';
213: if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
214: if ((pw = getpwnam(buf+1)) == NULL) {
215: strcat(buf, ": unknown user name");
216: yyerror(buf+1);
217: return;
218: }
219: }
220: cp1 = pw->pw_dir;
221: s = cp;
222: }
223: for (cp = path; *cp++ = *cp1++; )
224: ;
225: tpathp = pathp = cp - 1;
226: } else {
227: tpathp = pathp = path;
228: tilde = "";
229: }
230: *pathp = '\0';
231: if (!(which & E_SHELL)) {
232: if (which & E_TILDE)
233: Cat(path, s);
234: else
235: Cat(tilde, s);
236: sort();
237: return;
238: }
239: oeargc = eargc;
240: expany = 0;
241: expsh(s);
242: if (eargc == oeargc)
243: Cat(s, ""); /* "nonomatch" is set */
244: sort();
245: }
246:
247: static int
248: argcmp(a1, a2)
249: const void *a1, *a2;
250: {
251:
252: return (strcmp(*(char **)a1, *(char **)a2));
253: }
254:
255: /*
256: * If there are any Shell meta characters in the name,
257: * expand into a list, after searching directory
258: */
259: static void
260: expsh(s)
261: char *s;
262: {
263: register char *cp;
264: register char *spathp, *oldcp;
265: struct stat stb;
266:
267: spathp = pathp;
268: cp = s;
269: while (!any(*cp, shchars)) {
270: if (*cp == '\0') {
271: if (!expany || stat(path, &stb) >= 0) {
272: if (which & E_TILDE)
273: Cat(path, "");
274: else
275: Cat(tilde, tpathp);
276: }
277: goto endit;
278: }
279: addpath(*cp++);
280: }
281: oldcp = cp;
282: while (cp > s && *cp != '/')
283: cp--, pathp--;
284: if (*cp == '/')
285: cp++, pathp++;
286: *pathp = '\0';
287: if (*oldcp == '{') {
288: execbrc(cp, NULL);
289: return;
290: }
291: matchdir(cp);
292: endit:
293: pathp = spathp;
294: *pathp = '\0';
295: }
296:
297: static void
298: matchdir(pattern)
299: char *pattern;
300: {
301: struct stat stb;
302: register struct direct *dp;
303: DIR *dirp;
304:
305: dirp = opendir(path);
306: if (dirp == NULL) {
307: if (expany)
308: return;
309: goto patherr2;
310: }
311: if (fstat(dirp->dd_fd, &stb) < 0)
312: goto patherr1;
313: if (!ISDIR(stb.st_mode)) {
314: errno = ENOTDIR;
315: goto patherr1;
316: }
317: while ((dp = readdir(dirp)) != NULL)
318: if (match(dp->d_name, pattern)) {
319: if (which & E_TILDE)
320: Cat(path, dp->d_name);
321: else {
322: strcpy(pathp, dp->d_name);
323: Cat(tilde, tpathp);
324: *pathp = '\0';
325: }
326: }
327: closedir(dirp);
328: return;
329:
330: patherr1:
331: closedir(dirp);
332: patherr2:
333: strcat(path, ": ");
334: strcat(path, strerror(errno));
335: yyerror(path);
336: }
337:
338: static int
339: execbrc(p, s)
340: char *p, *s;
341: {
342: char restbuf[BUFSIZ + 2];
343: register char *pe, *pm, *pl;
344: int brclev = 0;
345: char *lm, savec, *spathp;
346:
347: for (lm = restbuf; *p != '{'; *lm++ = *p++)
348: continue;
349: for (pe = ++p; *pe; pe++)
350: switch (*pe) {
351:
352: case '{':
353: brclev++;
354: continue;
355:
356: case '}':
357: if (brclev == 0)
358: goto pend;
359: brclev--;
360: continue;
361:
362: case '[':
363: for (pe++; *pe && *pe != ']'; pe++)
364: continue;
365: if (!*pe)
366: yyerror("Missing ']'");
367: continue;
368: }
369: pend:
370: if (brclev || !*pe) {
371: yyerror("Missing '}'");
372: return (0);
373: }
374: for (pl = pm = p; pm <= pe; pm++)
375: switch (*pm & (QUOTE|TRIM)) {
376:
377: case '{':
378: brclev++;
379: continue;
380:
381: case '}':
382: if (brclev) {
383: brclev--;
384: continue;
385: }
386: goto doit;
387:
388: case ',':
389: if (brclev)
390: continue;
391: doit:
392: savec = *pm;
393: *pm = 0;
394: strcpy(lm, pl);
395: strcat(restbuf, pe + 1);
396: *pm = savec;
397: if (s == 0) {
398: spathp = pathp;
399: expsh(restbuf);
400: pathp = spathp;
401: *pathp = 0;
402: } else if (amatch(s, restbuf))
403: return (1);
404: sort();
405: pl = pm + 1;
406: continue;
407:
408: case '[':
409: for (pm++; *pm && *pm != ']'; pm++)
410: continue;
411: if (!*pm)
412: yyerror("Missing ']'");
413: continue;
414: }
415: return (0);
416: }
417:
418: static int
419: match(s, p)
420: char *s, *p;
421: {
422: register int c;
423: register char *sentp;
424: char sexpany = expany;
425:
426: if (*s == '.' && *p != '.')
427: return (0);
428: sentp = entp;
429: entp = s;
430: c = amatch(s, p);
431: entp = sentp;
432: expany = sexpany;
433: return (c);
434: }
435:
436: static int
437: amatch(s, p)
438: register char *s, *p;
439: {
440: register int scc;
441: int ok, lc;
442: char *spathp;
443: struct stat stb;
444: int c, cc;
445:
446: expany = 1;
447: for (;;) {
448: scc = *s++ & TRIM;
449: switch (c = *p++) {
450:
451: case '{':
452: return (execbrc(p - 1, s - 1));
453:
454: case '[':
455: ok = 0;
456: lc = 077777;
457: while (cc = *p++) {
458: if (cc == ']') {
459: if (ok)
460: break;
461: return (0);
462: }
463: if (cc == '-') {
464: if (lc <= scc && scc <= *p++)
465: ok++;
466: } else
467: if (scc == (lc = cc))
468: ok++;
469: }
470: if (cc == 0) {
471: yyerror("Missing ']'");
472: return (0);
473: }
474: continue;
475:
476: case '*':
477: if (!*p)
478: return (1);
479: if (*p == '/') {
480: p++;
481: goto slash;
482: }
483: for (s--; *s; s++)
484: if (amatch(s, p))
485: return (1);
486: return (0);
487:
488: case '\0':
489: return (scc == '\0');
490:
491: default:
492: if ((c & TRIM) != scc)
493: return (0);
494: continue;
495:
496: case '?':
497: if (scc == '\0')
498: return (0);
499: continue;
500:
501: case '/':
502: if (scc)
503: return (0);
504: slash:
505: s = entp;
506: spathp = pathp;
507: while (*s)
508: addpath(*s++);
509: addpath('/');
510: if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
511: if (*p == '\0') {
512: if (which & E_TILDE)
513: Cat(path, "");
514: else
515: Cat(tilde, tpathp);
516: } else
517: expsh(p);
518: pathp = spathp;
519: *pathp = '\0';
520: return (0);
521: }
522: }
523: }
524:
525: static int
526: smatch(s, p)
527: register char *s, *p;
528: {
529: register int scc;
530: int ok, lc;
531: int c, cc;
532:
533: for (;;) {
534: scc = *s++ & TRIM;
535: switch (c = *p++) {
536:
537: case '[':
538: ok = 0;
539: lc = 077777;
540: while (cc = *p++) {
541: if (cc == ']') {
542: if (ok)
543: break;
544: return (0);
545: }
546: if (cc == '-') {
547: if (lc <= scc && scc <= *p++)
548: ok++;
549: } else
550: if (scc == (lc = cc))
551: ok++;
552: }
553: if (cc == 0) {
554: yyerror("Missing ']'");
555: return (0);
556: }
557: continue;
558:
559: case '*':
560: if (!*p)
561: return (1);
562: for (s--; *s; s++)
563: if (smatch(s, p))
564: return (1);
565: return (0);
566:
567: case '\0':
568: return (scc == '\0');
569:
570: default:
571: if ((c & TRIM) != scc)
572: return (0);
573: continue;
574:
575: case '?':
576: if (scc == 0)
577: return (0);
578: continue;
579:
580: }
581: }
582: }
583:
584: static void
585: Cat(s1, s2)
586: register char *s1, *s2;
587: {
588: int len = strlen(s1) + strlen(s2) + 1;
589: register char *s;
590:
591: nleft -= len;
592: if (nleft <= 0 || ++eargc >= GAVSIZ)
593: yyerror("Arguments too long");
594: eargv[eargc] = 0;
595: eargv[eargc - 1] = s = malloc(len);
596: if (s == NULL)
597: fatal("ran out of memory\n");
598: while (*s++ = *s1++ & TRIM)
599: ;
600: s--;
601: while (*s++ = *s2++ & TRIM)
602: ;
603: }
604:
605: static void
606: addpath(c)
607: int c;
608: {
609:
610: if (pathp >= lastpathp)
611: yyerror("Pathname too long");
612: else {
613: *pathp++ = c & TRIM;
614: *pathp = '\0';
615: }
616: }
617:
618: /*
619: * Expand file names beginning with `~' into the
620: * user's home directory path name. Return a pointer in buf to the
621: * part corresponding to `file'.
622: */
623: char *
1.6 deraadt 624: exptilde(buf, file, maxlen)
1.1 dm 625: char buf[];
626: register char *file;
1.6 deraadt 627: int maxlen;
1.1 dm 628: {
629: register char *s1, *s2, *s3;
630: extern char homedir[];
631:
632: if (*file != '~') {
633: strcpy(buf, file);
634: return(buf);
635: }
636: if (*++file == '\0') {
637: s2 = homedir;
638: s3 = NULL;
639: } else if (*file == '/') {
640: s2 = homedir;
641: s3 = file;
642: } else {
643: s3 = file;
644: while (*s3 && *s3 != '/')
645: s3++;
646: if (*s3 == '/')
647: *s3 = '\0';
648: else
649: s3 = NULL;
650: if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
651: if ((pw = getpwnam(file)) == NULL) {
652: error("%s: unknown user name\n", file);
653: if (s3 != NULL)
654: *s3 = '/';
655: return(NULL);
656: }
657: }
658: if (s3 != NULL)
659: *s3 = '/';
660: s2 = pw->pw_dir;
661: }
1.6 deraadt 662: for (s1 = buf; (*s1++ = *s2++) && s1 < buf+maxlen; )
1.1 dm 663: ;
664: s2 = --s1;
1.6 deraadt 665: if (s3 != NULL && s1 < buf+maxlen) {
1.1 dm 666: s2++;
1.6 deraadt 667: while ((*s1++ = *s3++) && s1 < buf+maxlen)
1.1 dm 668: ;
669: }
1.6 deraadt 670: if (s1 == buf+maxlen)
671: return (NULL);
1.1 dm 672: return(s2);
673: }