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