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