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