Annotation of src/usr.bin/oldrdist/expand.c, Revision 1.2
1.2 ! deraadt 1: /* $OpenBSD$ */
! 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.2 ! deraadt 38: static char *rcsid = "$OpenBSD: expand.c,v 1.1 1996/02/03 12:11:56 dm 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) {
97: printf("expand(%x, %d)\nlist = ", list, wh);
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:
159: if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
160: *cp++ = '\0';
161: if (*cp == '\0') {
162: yyerror("no variable name after '$'");
163: return;
164: }
165: if (*cp == LC) {
166: cp++;
167: if ((tail = index(cp, RC)) == NULL) {
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) {
186: sprintf(buf, "%s%s%s", s, tp->n_name, tail);
187: expstr(buf);
188: }
189: return;
190: }
191: sprintf(buf, "%s%s", s, tail);
192: expstr(buf);
193: return;
194: }
195: if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
196: Cat(s, "");
197: sort();
198: return;
199: }
200: if (*s == '~') {
201: cp = ++s;
202: if (*cp == '\0' || *cp == '/') {
203: tilde = "~";
204: cp1 = homedir;
205: } else {
206: tilde = cp1 = buf;
207: *cp1++ = '~';
208: do
209: *cp1++ = *cp++;
210: while (*cp && *cp != '/');
211: *cp1 = '\0';
212: if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
213: if ((pw = getpwnam(buf+1)) == NULL) {
214: strcat(buf, ": unknown user name");
215: yyerror(buf+1);
216: return;
217: }
218: }
219: cp1 = pw->pw_dir;
220: s = cp;
221: }
222: for (cp = path; *cp++ = *cp1++; )
223: ;
224: tpathp = pathp = cp - 1;
225: } else {
226: tpathp = pathp = path;
227: tilde = "";
228: }
229: *pathp = '\0';
230: if (!(which & E_SHELL)) {
231: if (which & E_TILDE)
232: Cat(path, s);
233: else
234: Cat(tilde, s);
235: sort();
236: return;
237: }
238: oeargc = eargc;
239: expany = 0;
240: expsh(s);
241: if (eargc == oeargc)
242: Cat(s, ""); /* "nonomatch" is set */
243: sort();
244: }
245:
246: static int
247: argcmp(a1, a2)
248: const void *a1, *a2;
249: {
250:
251: return (strcmp(*(char **)a1, *(char **)a2));
252: }
253:
254: /*
255: * If there are any Shell meta characters in the name,
256: * expand into a list, after searching directory
257: */
258: static void
259: expsh(s)
260: char *s;
261: {
262: register char *cp;
263: register char *spathp, *oldcp;
264: struct stat stb;
265:
266: spathp = pathp;
267: cp = s;
268: while (!any(*cp, shchars)) {
269: if (*cp == '\0') {
270: if (!expany || stat(path, &stb) >= 0) {
271: if (which & E_TILDE)
272: Cat(path, "");
273: else
274: Cat(tilde, tpathp);
275: }
276: goto endit;
277: }
278: addpath(*cp++);
279: }
280: oldcp = cp;
281: while (cp > s && *cp != '/')
282: cp--, pathp--;
283: if (*cp == '/')
284: cp++, pathp++;
285: *pathp = '\0';
286: if (*oldcp == '{') {
287: execbrc(cp, NULL);
288: return;
289: }
290: matchdir(cp);
291: endit:
292: pathp = spathp;
293: *pathp = '\0';
294: }
295:
296: static void
297: matchdir(pattern)
298: char *pattern;
299: {
300: struct stat stb;
301: register struct direct *dp;
302: DIR *dirp;
303:
304: dirp = opendir(path);
305: if (dirp == NULL) {
306: if (expany)
307: return;
308: goto patherr2;
309: }
310: if (fstat(dirp->dd_fd, &stb) < 0)
311: goto patherr1;
312: if (!ISDIR(stb.st_mode)) {
313: errno = ENOTDIR;
314: goto patherr1;
315: }
316: while ((dp = readdir(dirp)) != NULL)
317: if (match(dp->d_name, pattern)) {
318: if (which & E_TILDE)
319: Cat(path, dp->d_name);
320: else {
321: strcpy(pathp, dp->d_name);
322: Cat(tilde, tpathp);
323: *pathp = '\0';
324: }
325: }
326: closedir(dirp);
327: return;
328:
329: patherr1:
330: closedir(dirp);
331: patherr2:
332: strcat(path, ": ");
333: strcat(path, strerror(errno));
334: yyerror(path);
335: }
336:
337: static int
338: execbrc(p, s)
339: char *p, *s;
340: {
341: char restbuf[BUFSIZ + 2];
342: register char *pe, *pm, *pl;
343: int brclev = 0;
344: char *lm, savec, *spathp;
345:
346: for (lm = restbuf; *p != '{'; *lm++ = *p++)
347: continue;
348: for (pe = ++p; *pe; pe++)
349: switch (*pe) {
350:
351: case '{':
352: brclev++;
353: continue;
354:
355: case '}':
356: if (brclev == 0)
357: goto pend;
358: brclev--;
359: continue;
360:
361: case '[':
362: for (pe++; *pe && *pe != ']'; pe++)
363: continue;
364: if (!*pe)
365: yyerror("Missing ']'");
366: continue;
367: }
368: pend:
369: if (brclev || !*pe) {
370: yyerror("Missing '}'");
371: return (0);
372: }
373: for (pl = pm = p; pm <= pe; pm++)
374: switch (*pm & (QUOTE|TRIM)) {
375:
376: case '{':
377: brclev++;
378: continue;
379:
380: case '}':
381: if (brclev) {
382: brclev--;
383: continue;
384: }
385: goto doit;
386:
387: case ',':
388: if (brclev)
389: continue;
390: doit:
391: savec = *pm;
392: *pm = 0;
393: strcpy(lm, pl);
394: strcat(restbuf, pe + 1);
395: *pm = savec;
396: if (s == 0) {
397: spathp = pathp;
398: expsh(restbuf);
399: pathp = spathp;
400: *pathp = 0;
401: } else if (amatch(s, restbuf))
402: return (1);
403: sort();
404: pl = pm + 1;
405: continue;
406:
407: case '[':
408: for (pm++; *pm && *pm != ']'; pm++)
409: continue;
410: if (!*pm)
411: yyerror("Missing ']'");
412: continue;
413: }
414: return (0);
415: }
416:
417: static int
418: match(s, p)
419: char *s, *p;
420: {
421: register int c;
422: register char *sentp;
423: char sexpany = expany;
424:
425: if (*s == '.' && *p != '.')
426: return (0);
427: sentp = entp;
428: entp = s;
429: c = amatch(s, p);
430: entp = sentp;
431: expany = sexpany;
432: return (c);
433: }
434:
435: static int
436: amatch(s, p)
437: register char *s, *p;
438: {
439: register int scc;
440: int ok, lc;
441: char *spathp;
442: struct stat stb;
443: int c, cc;
444:
445: expany = 1;
446: for (;;) {
447: scc = *s++ & TRIM;
448: switch (c = *p++) {
449:
450: case '{':
451: return (execbrc(p - 1, s - 1));
452:
453: case '[':
454: ok = 0;
455: lc = 077777;
456: while (cc = *p++) {
457: if (cc == ']') {
458: if (ok)
459: break;
460: return (0);
461: }
462: if (cc == '-') {
463: if (lc <= scc && scc <= *p++)
464: ok++;
465: } else
466: if (scc == (lc = cc))
467: ok++;
468: }
469: if (cc == 0) {
470: yyerror("Missing ']'");
471: return (0);
472: }
473: continue;
474:
475: case '*':
476: if (!*p)
477: return (1);
478: if (*p == '/') {
479: p++;
480: goto slash;
481: }
482: for (s--; *s; s++)
483: if (amatch(s, p))
484: return (1);
485: return (0);
486:
487: case '\0':
488: return (scc == '\0');
489:
490: default:
491: if ((c & TRIM) != scc)
492: return (0);
493: continue;
494:
495: case '?':
496: if (scc == '\0')
497: return (0);
498: continue;
499:
500: case '/':
501: if (scc)
502: return (0);
503: slash:
504: s = entp;
505: spathp = pathp;
506: while (*s)
507: addpath(*s++);
508: addpath('/');
509: if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
510: if (*p == '\0') {
511: if (which & E_TILDE)
512: Cat(path, "");
513: else
514: Cat(tilde, tpathp);
515: } else
516: expsh(p);
517: pathp = spathp;
518: *pathp = '\0';
519: return (0);
520: }
521: }
522: }
523:
524: static int
525: smatch(s, p)
526: register char *s, *p;
527: {
528: register int scc;
529: int ok, lc;
530: int c, cc;
531:
532: for (;;) {
533: scc = *s++ & TRIM;
534: switch (c = *p++) {
535:
536: case '[':
537: ok = 0;
538: lc = 077777;
539: while (cc = *p++) {
540: if (cc == ']') {
541: if (ok)
542: break;
543: return (0);
544: }
545: if (cc == '-') {
546: if (lc <= scc && scc <= *p++)
547: ok++;
548: } else
549: if (scc == (lc = cc))
550: ok++;
551: }
552: if (cc == 0) {
553: yyerror("Missing ']'");
554: return (0);
555: }
556: continue;
557:
558: case '*':
559: if (!*p)
560: return (1);
561: for (s--; *s; s++)
562: if (smatch(s, p))
563: return (1);
564: return (0);
565:
566: case '\0':
567: return (scc == '\0');
568:
569: default:
570: if ((c & TRIM) != scc)
571: return (0);
572: continue;
573:
574: case '?':
575: if (scc == 0)
576: return (0);
577: continue;
578:
579: }
580: }
581: }
582:
583: static void
584: Cat(s1, s2)
585: register char *s1, *s2;
586: {
587: int len = strlen(s1) + strlen(s2) + 1;
588: register char *s;
589:
590: nleft -= len;
591: if (nleft <= 0 || ++eargc >= GAVSIZ)
592: yyerror("Arguments too long");
593: eargv[eargc] = 0;
594: eargv[eargc - 1] = s = malloc(len);
595: if (s == NULL)
596: fatal("ran out of memory\n");
597: while (*s++ = *s1++ & TRIM)
598: ;
599: s--;
600: while (*s++ = *s2++ & TRIM)
601: ;
602: }
603:
604: static void
605: addpath(c)
606: int c;
607: {
608:
609: if (pathp >= lastpathp)
610: yyerror("Pathname too long");
611: else {
612: *pathp++ = c & TRIM;
613: *pathp = '\0';
614: }
615: }
616:
617: /*
618: * Expand file names beginning with `~' into the
619: * user's home directory path name. Return a pointer in buf to the
620: * part corresponding to `file'.
621: */
622: char *
623: exptilde(buf, file)
624: char buf[];
625: register char *file;
626: {
627: register char *s1, *s2, *s3;
628: extern char homedir[];
629:
630: if (*file != '~') {
631: strcpy(buf, file);
632: return(buf);
633: }
634: if (*++file == '\0') {
635: s2 = homedir;
636: s3 = NULL;
637: } else if (*file == '/') {
638: s2 = homedir;
639: s3 = file;
640: } else {
641: s3 = file;
642: while (*s3 && *s3 != '/')
643: s3++;
644: if (*s3 == '/')
645: *s3 = '\0';
646: else
647: s3 = NULL;
648: if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
649: if ((pw = getpwnam(file)) == NULL) {
650: error("%s: unknown user name\n", file);
651: if (s3 != NULL)
652: *s3 = '/';
653: return(NULL);
654: }
655: }
656: if (s3 != NULL)
657: *s3 = '/';
658: s2 = pw->pw_dir;
659: }
660: for (s1 = buf; *s1++ = *s2++; )
661: ;
662: s2 = --s1;
663: if (s3 != NULL) {
664: s2++;
665: while (*s1++ = *s3++)
666: ;
667: }
668: return(s2);
669: }