Annotation of src/usr.bin/rdist/expand.c, Revision 1.4
1.4 ! deraadt 1: /* $OpenBSD$ */
! 2:
1.1 deraadt 3: /*
1.2 dm 4: * Copyright (c) 1983 Regents of the University of California.
5: * All rights reserved.
1.1 deraadt 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
1.2 dm 37: static char RCSid[] =
1.4 ! deraadt 38: "$OpenBSD: expand.c,v 1.3 1996/03/05 03:16:04 dm Exp $";
1.2 dm 39:
40: static char sccsid[] = "@(#)expand.c 5.2 (Berkeley) 3/28/86";
41:
42: char copyright[] =
43: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
44: All rights reserved.\n";
1.1 deraadt 45: #endif /* not lint */
46:
47: #include "defs.h"
48:
1.2 dm 49: #define MAXEARGS 2048
50: #define LC '{'
51: #define RC '}'
1.1 deraadt 52:
53: static char shchars[] = "${[*?";
54:
1.2 dm 55: int which; /* bit mask of types to expand */
56: int eargc; /* expanded arg count */
57: char **eargv; /* expanded arg vectors */
58: char *path;
59: char *pathp;
60: char *lastpathp;
61: char *tilde; /* "~user" if not expanding tilde, else "" */
62: char *tpathp;
63:
64: int expany; /* any expansions done? */
65: char *entp;
66: char **sortbase;
67: char *argvbuf[MAXEARGS];
68:
69: static int argcmp();
70: void expstr();
71: void expsh();
72: void matchdir();
1.1 deraadt 73:
74: #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
75: sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
76:
1.2 dm 77: static void Cat(s1, s2) /* quote in s1 and s2 */
78: register u_char *s1, *s2;
79: {
80: register char *cp;
81: int len = strlen((char *)s1) + strlen((char *)s2) + 2;
82:
83: if ((eargc + 1) >= MAXEARGS) {
84: yyerror("Too many names");
85: return;
86: }
87:
88: eargv[++eargc] = (char *) NULL;
89: eargv[eargc - 1] = cp = xmalloc(len);
90:
91: do {
92: if (*s1 == QUOTECHAR)
93: s1++;
94: } while (*cp++ = *s1++);
95: cp--;
96: do {
97: if (*s2 == QUOTECHAR)
98: s2++;
99: } while (*cp++ = *s2++);
100: }
101:
102: static void addpath(c)
103: char c;
104: {
105: if (pathp >= lastpathp) {
106: yyerror("Pathname too long");
107: return;
108: } else {
109: *pathp++ = c;
110: *pathp = CNULL;
111: }
112: }
1.1 deraadt 113:
114: /*
115: * Take a list of names and expand any macros, etc.
116: * wh = E_VARS if expanding variables.
117: * wh = E_SHELL if expanding shell characters.
118: * wh = E_TILDE if expanding `~'.
119: * or any of these or'ed together.
120: *
121: * Major portions of this were snarfed from csh/sh.glob.c.
122: */
123: struct namelist *
1.2 dm 124: expand(list, wh) /* quote in list->n_name */
1.1 deraadt 125: struct namelist *list;
126: int wh;
127: {
128: register struct namelist *nl, *prev;
129: register int n;
130: char pathbuf[BUFSIZ];
131:
1.2 dm 132: if (debug)
133: debugmsg(DM_CALL, "expand(%x, %d) start, list = %s",
134: list, wh, getnlstr(list));
1.1 deraadt 135:
1.2 dm 136: if (wh == 0)
137: fatalerr("expand() contains invalid 'wh' argument.");
1.1 deraadt 138:
139: which = wh;
140: path = tpathp = pathp = pathbuf;
1.2 dm 141: *pathp = CNULL;
1.1 deraadt 142: lastpathp = &path[sizeof pathbuf - 2];
143: tilde = "";
144: eargc = 0;
145: eargv = sortbase = argvbuf;
1.2 dm 146: *eargv = (char *) NULL;
147:
1.1 deraadt 148: /*
149: * Walk the name list and expand names into eargv[];
150: */
151: for (nl = list; nl != NULL; nl = nl->n_next)
1.2 dm 152: expstr((u_char *)nl->n_name);
1.1 deraadt 153: /*
154: * Take expanded list of names from eargv[] and build a new list.
155: */
156: list = prev = NULL;
157: for (n = 0; n < eargc; n++) {
1.2 dm 158: nl = makenl((char *)NULL);
1.1 deraadt 159: nl->n_name = eargv[n];
160: if (prev == NULL)
161: list = prev = nl;
162: else {
163: prev->n_next = nl;
164: prev = nl;
165: }
166: }
1.2 dm 167:
1.1 deraadt 168: return(list);
169: }
170:
1.2 dm 171: /*
172: * xstrchr() is a version of strchr() that
173: * handles u_char buffers.
174: */
175: u_char *xstrchr(str, ch)
176: u_char *str;
177: int ch;
1.1 deraadt 178: {
1.2 dm 179: register u_char *cp;
180:
181: for (cp = str; cp && *cp != CNULL; ++cp)
182: if (ch == *cp)
183: return(cp);
184:
185: return((u_char *)NULL);
186: }
187:
188: void expstr(s)
189: u_char *s;
190: {
191: register u_char *cp, *cp1;
1.1 deraadt 192: register struct namelist *tp;
1.2 dm 193: u_char *tail;
194: u_char ebuf[BUFSIZ];
195: u_char varbuff[BUFSIZ];
1.1 deraadt 196: int savec, oeargc;
1.2 dm 197: extern char *homedir;
1.1 deraadt 198:
1.2 dm 199: if (s == NULL || *s == CNULL)
1.1 deraadt 200: return;
201:
1.2 dm 202: /*
203: * Remove quoted characters
204: */
205: if (IS_ON(which, E_VARS)) {
206: if ((int)strlen((char *)s) > sizeof(varbuff)) {
207: yyerror("Variable is too large.");
208: return;
209: }
210: for (cp = s, cp1 = varbuff; cp && *cp; ++cp) {
211: /*
212: * remove quoted character if the next
213: * character is not $
214: */
215: if (*cp == QUOTECHAR && *(cp+1) != '$')
216: ++cp;
217: else
218: *cp1++ = *cp;
219: }
220: *cp1 = CNULL;
221: s = varbuff;
222: }
223:
224: /*
225: * Consider string 's' a variable that should be expanded if
226: * there is a '$' in 's' that is not quoted.
227: */
228: if (IS_ON(which, E_VARS) &&
229: ((cp = xstrchr(s, '$')) && !(cp > s && *(cp-1) == QUOTECHAR))) {
230: *cp++ = CNULL;
231: if (*cp == CNULL) {
1.1 deraadt 232: yyerror("no variable name after '$'");
233: return;
234: }
235: if (*cp == LC) {
236: cp++;
1.2 dm 237: for (cp1 = cp; ; cp1 = tail + 1) {
238: if ((tail = xstrchr(cp1, RC)) == NULL) {
239: yyerror("unmatched '{'");
240: return;
241: }
242: if (tail[-1] != QUOTECHAR) break;
1.1 deraadt 243: }
1.2 dm 244: *tail++ = savec = CNULL;
245: if (*cp == CNULL) {
1.1 deraadt 246: yyerror("no variable name after '$'");
247: return;
248: }
249: } else {
250: tail = cp + 1;
251: savec = *tail;
1.2 dm 252: *tail = CNULL;
1.1 deraadt 253: }
1.2 dm 254: tp = lookup((char *)cp, LOOKUP, (struct namelist *)NULL);
255: if (savec != CNULL)
1.1 deraadt 256: *tail = savec;
257: if (tp != NULL) {
258: for (; tp != NULL; tp = tp->n_next) {
1.2 dm 259: (void) sprintf((char *)ebuf,
260: "%s%s%s", s, tp->n_name, tail);
261: expstr(ebuf);
1.1 deraadt 262: }
263: return;
264: }
1.2 dm 265: (void) sprintf((char *)ebuf, "%s%s", s, tail);
266: expstr(ebuf);
1.1 deraadt 267: return;
268: }
1.2 dm 269: if ((which & ~E_VARS) == 0 || !strcmp((char *)s, "{") ||
270: !strcmp((char *)s, "{}")) {
271: Cat(s, (u_char *)"");
1.1 deraadt 272: sort();
273: return;
274: }
275: if (*s == '~') {
276: cp = ++s;
1.2 dm 277: if (*cp == CNULL || *cp == '/') {
1.1 deraadt 278: tilde = "~";
1.2 dm 279: cp1 = (u_char *)homedir;
1.1 deraadt 280: } else {
1.2 dm 281: tilde = (char *)(cp1 = ebuf);
1.1 deraadt 282: *cp1++ = '~';
283: do
284: *cp1++ = *cp++;
285: while (*cp && *cp != '/');
1.2 dm 286: *cp1 = CNULL;
287: if (pw == NULL || strcmp(pw->pw_name,
288: (char *)ebuf+1) != 0) {
289: if ((pw = getpwnam((char *)ebuf+1)) == NULL) {
290: strcat((char *)ebuf,
291: ": unknown user name");
292: yyerror((char *)ebuf+1);
1.1 deraadt 293: return;
294: }
295: }
1.2 dm 296: cp1 = (u_char *)pw->pw_dir;
1.1 deraadt 297: s = cp;
298: }
1.2 dm 299: for (cp = (u_char *)path; *cp++ = *cp1++; )
1.1 deraadt 300: ;
1.2 dm 301: tpathp = pathp = (char *)cp - 1;
1.1 deraadt 302: } else {
303: tpathp = pathp = path;
304: tilde = "";
305: }
1.2 dm 306: *pathp = CNULL;
1.1 deraadt 307: if (!(which & E_SHELL)) {
308: if (which & E_TILDE)
1.2 dm 309: Cat((u_char *)path, s);
1.1 deraadt 310: else
1.2 dm 311: Cat((u_char *)tilde, s);
1.1 deraadt 312: sort();
313: return;
314: }
315: oeargc = eargc;
316: expany = 0;
317: expsh(s);
318: if (eargc == oeargc)
1.2 dm 319: Cat(s, (u_char *)""); /* "nonomatch" is set */
1.1 deraadt 320: sort();
321: }
322:
1.2 dm 323: static
1.1 deraadt 324: argcmp(a1, a2)
1.2 dm 325: char **a1, **a2;
1.1 deraadt 326: {
327:
1.2 dm 328: return (strcmp(*a1, *a2));
1.1 deraadt 329: }
330:
331: /*
332: * If there are any Shell meta characters in the name,
333: * expand into a list, after searching directory
334: */
1.2 dm 335: void expsh(s) /* quote in s */
336: u_char *s;
1.1 deraadt 337: {
1.2 dm 338: register u_char *cp, *oldcp;
339: register char *spathp;
1.1 deraadt 340: struct stat stb;
341:
342: spathp = pathp;
343: cp = s;
344: while (!any(*cp, shchars)) {
1.2 dm 345: if (*cp == CNULL) {
1.1 deraadt 346: if (!expany || stat(path, &stb) >= 0) {
347: if (which & E_TILDE)
1.2 dm 348: Cat((u_char *)path, (u_char *)"");
1.1 deraadt 349: else
1.2 dm 350: Cat((u_char *)tilde, (u_char *)tpathp);
1.1 deraadt 351: }
352: goto endit;
353: }
1.2 dm 354: if (*cp == QUOTECHAR) cp++;
1.1 deraadt 355: addpath(*cp++);
356: }
357: oldcp = cp;
358: while (cp > s && *cp != '/')
359: cp--, pathp--;
360: if (*cp == '/')
361: cp++, pathp++;
1.2 dm 362: *pathp = CNULL;
1.1 deraadt 363: if (*oldcp == '{') {
1.2 dm 364: (void) execbrc(cp, (u_char *)NULL);
1.1 deraadt 365: return;
366: }
1.2 dm 367: matchdir((char *)cp);
1.1 deraadt 368: endit:
369: pathp = spathp;
1.2 dm 370: *pathp = CNULL;
1.1 deraadt 371: }
372:
1.2 dm 373: void matchdir(pattern) /* quote in pattern */
1.1 deraadt 374: char *pattern;
375: {
376: struct stat stb;
1.2 dm 377: register DIRENTRY *dp;
1.1 deraadt 378: DIR *dirp;
379:
380: dirp = opendir(path);
381: if (dirp == NULL) {
382: if (expany)
383: return;
384: goto patherr2;
385: }
386: if (fstat(dirp->dd_fd, &stb) < 0)
387: goto patherr1;
1.2 dm 388: if (!S_ISDIR(stb.st_mode)) {
1.1 deraadt 389: errno = ENOTDIR;
390: goto patherr1;
391: }
392: while ((dp = readdir(dirp)) != NULL)
393: if (match(dp->d_name, pattern)) {
394: if (which & E_TILDE)
1.2 dm 395: Cat((u_char *)path, (u_char *)dp->d_name);
1.1 deraadt 396: else {
1.2 dm 397: (void) strcpy(pathp, dp->d_name);
398: Cat((u_char *)tilde, (u_char *)tpathp);
399: *pathp = CNULL;
1.1 deraadt 400: }
401: }
402: closedir(dirp);
403: return;
404:
405: patherr1:
406: closedir(dirp);
407: patherr2:
1.2 dm 408: (void) strcat(path, ": ");
409: (void) strcat(path, SYSERR);
1.1 deraadt 410: yyerror(path);
411: }
412:
1.2 dm 413: execbrc(p, s) /* quote in p */
414: u_char *p, *s;
1.1 deraadt 415: {
1.2 dm 416: u_char restbuf[BUFSIZ + 2];
417: register u_char *pe, *pm, *pl;
1.1 deraadt 418: int brclev = 0;
1.2 dm 419: u_char *lm, savec;
420: char *spathp;
1.1 deraadt 421:
422: for (lm = restbuf; *p != '{'; *lm++ = *p++)
1.2 dm 423: if (*p == QUOTECHAR) *lm++ = *p++;
424:
1.1 deraadt 425: for (pe = ++p; *pe; pe++)
426: switch (*pe) {
427:
428: case '{':
429: brclev++;
430: continue;
431:
432: case '}':
433: if (brclev == 0)
434: goto pend;
435: brclev--;
436: continue;
437:
438: case '[':
439: for (pe++; *pe && *pe != ']'; pe++)
1.2 dm 440: if (*p == QUOTECHAR) pe++;
1.1 deraadt 441: if (!*pe)
442: yyerror("Missing ']'");
443: continue;
1.2 dm 444:
445: case QUOTECHAR: /* skip this character */
446: pe++;
447: continue;
1.1 deraadt 448: }
449: pend:
450: if (brclev || !*pe) {
451: yyerror("Missing '}'");
452: return (0);
453: }
454: for (pl = pm = p; pm <= pe; pm++)
1.2 dm 455: /* the strip code was a noop */
456: switch (*pm) {
1.1 deraadt 457:
458: case '{':
459: brclev++;
460: continue;
461:
462: case '}':
463: if (brclev) {
464: brclev--;
465: continue;
466: }
467: goto doit;
468:
469: case ',':
470: if (brclev)
471: continue;
472: doit:
473: savec = *pm;
474: *pm = 0;
1.2 dm 475: (void) strcpy((char *)lm, (char *)pl);
476: (void) strcat((char *)restbuf, (char *)pe + 1);
1.1 deraadt 477: *pm = savec;
478: if (s == 0) {
479: spathp = pathp;
480: expsh(restbuf);
481: pathp = spathp;
482: *pathp = 0;
1.2 dm 483: } else if (amatch((char *)s, restbuf))
1.1 deraadt 484: return (1);
485: sort();
486: pl = pm + 1;
487: continue;
488:
489: case '[':
490: for (pm++; *pm && *pm != ']'; pm++)
1.2 dm 491: if (*pm == QUOTECHAR) pm++;
1.1 deraadt 492: if (!*pm)
493: yyerror("Missing ']'");
494: continue;
1.2 dm 495:
496: case QUOTECHAR: /* skip one character */
497: pm++;
498: continue;
1.1 deraadt 499: }
500: return (0);
501: }
502:
1.2 dm 503: match(s, p) /* quote in p */
1.1 deraadt 504: char *s, *p;
505: {
506: register int c;
507: register char *sentp;
508: char sexpany = expany;
509:
510: if (*s == '.' && *p != '.')
511: return (0);
512: sentp = entp;
513: entp = s;
514: c = amatch(s, p);
515: entp = sentp;
516: expany = sexpany;
517: return (c);
518: }
519:
1.2 dm 520: amatch(s, p) /* quote in p */
521: register char *s;
522: register u_char *p;
1.1 deraadt 523: {
524: register int scc;
525: int ok, lc;
526: char *spathp;
527: struct stat stb;
528: int c, cc;
529:
530: expany = 1;
531: for (;;) {
1.2 dm 532: scc = *s++;
1.1 deraadt 533: switch (c = *p++) {
534:
535: case '{':
1.2 dm 536: return (execbrc((u_char *)p - 1, (u_char *)s - 1));
1.1 deraadt 537:
538: case '[':
539: ok = 0;
540: lc = 077777;
541: while (cc = *p++) {
542: if (cc == ']') {
543: if (ok)
544: break;
545: return (0);
546: }
1.2 dm 547: if (cc == QUOTECHAR) cc = *p++;
1.1 deraadt 548: if (cc == '-') {
1.2 dm 549: if (lc <= scc && scc <= (int)*p++)
1.1 deraadt 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: if (*p == '/') {
565: p++;
566: goto slash;
567: }
568: for (s--; *s; s++)
569: if (amatch(s, p))
570: return (1);
571: return (0);
572:
1.2 dm 573: case CNULL:
574: return (scc == CNULL);
1.1 deraadt 575:
576: default:
1.2 dm 577: if (c != scc)
1.1 deraadt 578: return (0);
579: continue;
580:
581: case '?':
1.2 dm 582: if (scc == CNULL)
1.1 deraadt 583: return (0);
584: continue;
585:
586: case '/':
587: if (scc)
588: return (0);
589: slash:
590: s = entp;
591: spathp = pathp;
592: while (*s)
593: addpath(*s++);
594: addpath('/');
1.2 dm 595: if (stat(path, &stb) == 0 && S_ISDIR(stb.st_mode))
596: if (*p == CNULL) {
1.1 deraadt 597: if (which & E_TILDE)
1.2 dm 598: Cat((u_char *)path,
599: (u_char *)"");
1.1 deraadt 600: else
1.2 dm 601: Cat((u_char *)tilde,
602: (u_char *)tpathp);
1.1 deraadt 603: } else
604: expsh(p);
605: pathp = spathp;
1.2 dm 606: *pathp = CNULL;
1.1 deraadt 607: return (0);
608: }
609: }
610: }