Annotation of src/usr.bin/oldrdist/expand.c, Revision 1.11
1.11 ! millert 1: /* $OpenBSD: expand.c,v 1.10 2003/04/06 17:57:45 ho 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.
1.11 ! millert 15: * 3. Neither the name of the University nor the names of its contributors
1.1 dm 16: * may be used to endorse or promote products derived from this software
17: * without specific prior written permission.
18: *
19: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29: * SUCH DAMAGE.
30: */
31:
32: #ifndef lint
33: /* from: static char sccsid[] = "@(#)expand.c 8.1 (Berkeley) 6/9/93"; */
1.11 ! millert 34: static char *rcsid = "$OpenBSD: expand.c,v 1.10 2003/04/06 17:57:45 ho Exp $";
1.1 dm 35: #endif /* not lint */
36:
37: #include "defs.h"
38:
39: #define GAVSIZ NCARGS / 6
40: #define LC '{'
41: #define RC '}'
42:
43: static char shchars[] = "${[*?";
44:
45: int which; /* bit mask of types to expand */
46: int eargc; /* expanded arg count */
47: char **eargv; /* expanded arg vectors */
1.10 ho 48: char pathbuf[BUFSIZ];
1.1 dm 49: char *path;
50: char *pathp;
51: char *lastpathp;
52: char *tilde; /* "~user" if not expanding tilde, else "" */
53: char *tpathp;
54: int nleft;
55:
56: int expany; /* any expansions done? */
57: char *entp;
58: char **sortbase;
59:
60: #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
61: sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
62:
1.9 millert 63: static void Cat(char *, char *);
64: static void addpath(int);
65: static int amatch(char *, char *);
66: static int argcmp(const void *, const void *);
67: static int execbrc(char *, char *);
68: static void expsh(char *);
69: static void expstr(char *);
70: static int match(char *, char *);
71: static void matchdir(char *);
72: static int smatch(char *, char *);
1.1 dm 73:
74: /*
75: * Take a list of names and expand any macros, etc.
76: * wh = E_VARS if expanding variables.
77: * wh = E_SHELL if expanding shell characters.
78: * wh = E_TILDE if expanding `~'.
79: * or any of these or'ed together.
80: *
81: * Major portions of this were snarfed from csh/sh.glob.c.
82: */
83: struct namelist *
84: expand(list, wh)
85: struct namelist *list;
86: int wh;
87: {
1.8 mpech 88: struct namelist *nl, *prev;
89: int n;
1.1 dm 90: char *argvbuf[GAVSIZ];
91:
92: if (debug) {
1.7 pvalchev 93: printf("expand(%lx, %d)\nlist = ", (long)list, wh);
1.1 dm 94: prnames(list);
95: }
96:
97: if (wh == 0) {
1.8 mpech 98: char *cp;
1.1 dm 99:
100: for (nl = list; nl != NULL; nl = nl->n_next)
101: for (cp = nl->n_name; *cp; cp++)
102: *cp = *cp & TRIM;
103: return(list);
104: }
105:
106: which = wh;
107: path = tpathp = pathp = pathbuf;
108: *pathp = '\0';
109: lastpathp = &path[sizeof pathbuf - 2];
110: tilde = "";
111: eargc = 0;
112: eargv = sortbase = argvbuf;
113: *eargv = 0;
114: nleft = NCARGS - 4;
115: /*
116: * Walk the name list and expand names into eargv[];
117: */
118: for (nl = list; nl != NULL; nl = nl->n_next)
119: expstr(nl->n_name);
120: /*
121: * Take expanded list of names from eargv[] and build a new list.
122: */
123: list = prev = NULL;
124: for (n = 0; n < eargc; n++) {
125: nl = makenl(NULL);
126: nl->n_name = eargv[n];
127: if (prev == NULL)
128: list = prev = nl;
129: else {
130: prev->n_next = nl;
131: prev = nl;
132: }
133: }
134: if (debug) {
135: printf("expanded list = ");
136: prnames(list);
137: }
138: return(list);
139: }
140:
141: static void
142: expstr(s)
143: char *s;
144: {
1.8 mpech 145: char *cp, *cp1;
146: struct namelist *tp;
1.1 dm 147: char *tail;
148: char buf[BUFSIZ];
149: int savec, oeargc;
150: extern char homedir[];
151:
152: if (s == NULL || *s == '\0')
153: return;
154:
1.3 millert 155: if ((which & E_VARS) && (cp = strchr(s, '$')) != NULL) {
1.1 dm 156: *cp++ = '\0';
157: if (*cp == '\0') {
158: yyerror("no variable name after '$'");
159: return;
160: }
161: if (*cp == LC) {
162: cp++;
1.3 millert 163: if ((tail = strchr(cp, RC)) == NULL) {
1.1 dm 164: yyerror("unmatched '{'");
165: return;
166: }
167: *tail++ = savec = '\0';
168: if (*cp == '\0') {
169: yyerror("no variable name after '$'");
170: return;
171: }
172: } else {
173: tail = cp + 1;
174: savec = *tail;
175: *tail = '\0';
176: }
177: tp = lookup(cp, NULL, 0);
178: if (savec != '\0')
179: *tail = savec;
180: if (tp != NULL) {
181: for (; tp != NULL; tp = tp->n_next) {
1.5 millert 182: snprintf(buf, sizeof(buf), "%s%s%s", s,
1.4 millert 183: tp->n_name, tail);
1.1 dm 184: expstr(buf);
185: }
186: return;
187: }
1.5 millert 188: snprintf(buf, sizeof(buf), "%s%s", s, tail);
1.1 dm 189: expstr(buf);
190: return;
191: }
192: if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
193: Cat(s, "");
194: sort();
195: return;
196: }
197: if (*s == '~') {
198: cp = ++s;
199: if (*cp == '\0' || *cp == '/') {
200: tilde = "~";
201: cp1 = homedir;
202: } else {
203: tilde = cp1 = buf;
204: *cp1++ = '~';
205: do
206: *cp1++ = *cp++;
207: while (*cp && *cp != '/');
208: *cp1 = '\0';
209: if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
210: if ((pw = getpwnam(buf+1)) == NULL) {
1.10 ho 211: strlcat(buf, ": unknown user name",
212: sizeof buf);
1.1 dm 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: {
1.8 mpech 260: char *cp;
261: char *spathp, *oldcp;
1.1 dm 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;
1.8 mpech 299: struct direct *dp;
1.1 dm 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 {
1.10 ho 319: strlcpy(pathp, dp->d_name,
320: pathbuf + sizeof pathbuf - pathp);
1.1 dm 321: Cat(tilde, tpathp);
322: *pathp = '\0';
323: }
324: }
325: closedir(dirp);
326: return;
327:
328: patherr1:
329: closedir(dirp);
330: patherr2:
1.10 ho 331: strlcat(path, ": ", pathbuf + sizeof pathbuf - path);
332: strlcat(path, strerror(errno), pathbuf + sizeof pathbuf - path);
1.1 dm 333: yyerror(path);
334: }
335:
336: static int
337: execbrc(p, s)
338: char *p, *s;
339: {
340: char restbuf[BUFSIZ + 2];
1.8 mpech 341: char *pe, *pm, *pl;
1.1 dm 342: int brclev = 0;
343: char *lm, savec, *spathp;
344:
345: for (lm = restbuf; *p != '{'; *lm++ = *p++)
346: continue;
347: for (pe = ++p; *pe; pe++)
348: switch (*pe) {
349:
350: case '{':
351: brclev++;
352: continue;
353:
354: case '}':
355: if (brclev == 0)
356: goto pend;
357: brclev--;
358: continue;
359:
360: case '[':
361: for (pe++; *pe && *pe != ']'; pe++)
362: continue;
363: if (!*pe)
364: yyerror("Missing ']'");
365: continue;
366: }
367: pend:
368: if (brclev || !*pe) {
369: yyerror("Missing '}'");
370: return (0);
371: }
372: for (pl = pm = p; pm <= pe; pm++)
373: switch (*pm & (QUOTE|TRIM)) {
374:
375: case '{':
376: brclev++;
377: continue;
378:
379: case '}':
380: if (brclev) {
381: brclev--;
382: continue;
383: }
384: goto doit;
385:
386: case ',':
387: if (brclev)
388: continue;
389: doit:
390: savec = *pm;
391: *pm = 0;
1.10 ho 392: strlcpy(lm, pl, restbuf + sizeof restbuf - lm);
393: strlcat(restbuf, pe + 1, sizeof restbuf);
1.1 dm 394: *pm = savec;
395: if (s == 0) {
396: spathp = pathp;
397: expsh(restbuf);
398: pathp = spathp;
399: *pathp = 0;
400: } else if (amatch(s, restbuf))
401: return (1);
402: sort();
403: pl = pm + 1;
404: continue;
405:
406: case '[':
407: for (pm++; *pm && *pm != ']'; pm++)
408: continue;
409: if (!*pm)
410: yyerror("Missing ']'");
411: continue;
412: }
413: return (0);
414: }
415:
416: static int
417: match(s, p)
418: char *s, *p;
419: {
1.8 mpech 420: int c;
421: char *sentp;
1.1 dm 422: char sexpany = expany;
423:
424: if (*s == '.' && *p != '.')
425: return (0);
426: sentp = entp;
427: entp = s;
428: c = amatch(s, p);
429: entp = sentp;
430: expany = sexpany;
431: return (c);
432: }
433:
434: static int
435: amatch(s, p)
1.8 mpech 436: char *s, *p;
1.1 dm 437: {
1.8 mpech 438: int scc;
1.1 dm 439: int ok, lc;
440: char *spathp;
441: struct stat stb;
442: int c, cc;
443:
444: expany = 1;
445: for (;;) {
446: scc = *s++ & TRIM;
447: switch (c = *p++) {
448:
449: case '{':
450: return (execbrc(p - 1, s - 1));
451:
452: case '[':
453: ok = 0;
454: lc = 077777;
455: while (cc = *p++) {
456: if (cc == ']') {
457: if (ok)
458: break;
459: return (0);
460: }
461: if (cc == '-') {
462: if (lc <= scc && scc <= *p++)
463: ok++;
464: } else
465: if (scc == (lc = cc))
466: ok++;
467: }
468: if (cc == 0) {
469: yyerror("Missing ']'");
470: return (0);
471: }
472: continue;
473:
474: case '*':
475: if (!*p)
476: return (1);
477: if (*p == '/') {
478: p++;
479: goto slash;
480: }
481: for (s--; *s; s++)
482: if (amatch(s, p))
483: return (1);
484: return (0);
485:
486: case '\0':
487: return (scc == '\0');
488:
489: default:
490: if ((c & TRIM) != scc)
491: return (0);
492: continue;
493:
494: case '?':
495: if (scc == '\0')
496: return (0);
497: continue;
498:
499: case '/':
500: if (scc)
501: return (0);
502: slash:
503: s = entp;
504: spathp = pathp;
505: while (*s)
506: addpath(*s++);
507: addpath('/');
508: if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
509: if (*p == '\0') {
510: if (which & E_TILDE)
511: Cat(path, "");
512: else
513: Cat(tilde, tpathp);
514: } else
515: expsh(p);
516: pathp = spathp;
517: *pathp = '\0';
518: return (0);
519: }
520: }
521: }
522:
523: static int
524: smatch(s, p)
1.8 mpech 525: char *s, *p;
1.1 dm 526: {
1.8 mpech 527: int scc;
1.1 dm 528: int ok, lc;
529: int c, cc;
530:
531: for (;;) {
532: scc = *s++ & TRIM;
533: switch (c = *p++) {
534:
535: case '[':
536: ok = 0;
537: lc = 077777;
538: while (cc = *p++) {
539: if (cc == ']') {
540: if (ok)
541: break;
542: return (0);
543: }
544: if (cc == '-') {
545: if (lc <= scc && scc <= *p++)
546: ok++;
547: } else
548: if (scc == (lc = cc))
549: ok++;
550: }
551: if (cc == 0) {
552: yyerror("Missing ']'");
553: return (0);
554: }
555: continue;
556:
557: case '*':
558: if (!*p)
559: return (1);
560: for (s--; *s; s++)
561: if (smatch(s, p))
562: return (1);
563: return (0);
564:
565: case '\0':
566: return (scc == '\0');
567:
568: default:
569: if ((c & TRIM) != scc)
570: return (0);
571: continue;
572:
573: case '?':
574: if (scc == 0)
575: return (0);
576: continue;
577:
578: }
579: }
580: }
581:
582: static void
583: Cat(s1, s2)
1.8 mpech 584: char *s1, *s2;
1.1 dm 585: {
586: int len = strlen(s1) + strlen(s2) + 1;
1.8 mpech 587: char *s;
1.1 dm 588:
589: nleft -= len;
590: if (nleft <= 0 || ++eargc >= GAVSIZ)
591: yyerror("Arguments too long");
592: eargv[eargc] = 0;
593: eargv[eargc - 1] = s = malloc(len);
594: if (s == NULL)
595: fatal("ran out of memory\n");
596: while (*s++ = *s1++ & TRIM)
597: ;
598: s--;
599: while (*s++ = *s2++ & TRIM)
600: ;
601: }
602:
603: static void
604: addpath(c)
605: int c;
606: {
607:
608: if (pathp >= lastpathp)
609: yyerror("Pathname too long");
610: else {
611: *pathp++ = c & TRIM;
612: *pathp = '\0';
613: }
614: }
615:
616: /*
617: * Expand file names beginning with `~' into the
618: * user's home directory path name. Return a pointer in buf to the
619: * part corresponding to `file'.
620: */
621: char *
1.6 deraadt 622: exptilde(buf, file, maxlen)
1.1 dm 623: char buf[];
1.8 mpech 624: char *file;
1.6 deraadt 625: int maxlen;
1.1 dm 626: {
1.8 mpech 627: char *s1, *s2, *s3;
1.1 dm 628: extern char homedir[];
629:
630: if (*file != '~') {
1.10 ho 631: strlcpy(buf, file, maxlen);
1.1 dm 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: }
1.6 deraadt 660: for (s1 = buf; (*s1++ = *s2++) && s1 < buf+maxlen; )
1.1 dm 661: ;
662: s2 = --s1;
1.6 deraadt 663: if (s3 != NULL && s1 < buf+maxlen) {
1.1 dm 664: s2++;
1.6 deraadt 665: while ((*s1++ = *s3++) && s1 < buf+maxlen)
1.1 dm 666: ;
667: }
1.6 deraadt 668: if (s1 == buf+maxlen)
669: return (NULL);
1.1 dm 670: return(s2);
671: }