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