Annotation of src/usr.bin/rdist/expand.c, Revision 1.9
1.9 ! millert 1: /* $OpenBSD: expand.c,v 1.8 2003/04/05 20:31:58 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.9 ! millert 42: "$OpenBSD: expand.c,v 1.8 2003/04/05 20:31:58 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.9 ! millert 147: lastpathp = &pathbuf[sizeof pathbuf - 2];
1.1 deraadt 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) {
1.9 ! millert 295: strlcat((char *)ebuf,
! 296: ": unknown user name",
! 297: sizeof(ebuf));
1.2 dm 298: yyerror((char *)ebuf+1);
1.1 deraadt 299: return;
300: }
301: }
1.2 dm 302: cp1 = (u_char *)pw->pw_dir;
1.1 deraadt 303: s = cp;
304: }
1.5 millert 305: for (cp = (u_char *)path; (*cp++ = *cp1++); )
1.1 deraadt 306: ;
1.2 dm 307: tpathp = pathp = (char *)cp - 1;
1.1 deraadt 308: } else {
309: tpathp = pathp = path;
310: tilde = "";
311: }
1.2 dm 312: *pathp = CNULL;
1.1 deraadt 313: if (!(which & E_SHELL)) {
314: if (which & E_TILDE)
1.2 dm 315: Cat((u_char *)path, s);
1.1 deraadt 316: else
1.2 dm 317: Cat((u_char *)tilde, s);
1.1 deraadt 318: sort();
319: return;
320: }
321: oeargc = eargc;
322: expany = 0;
323: expsh(s);
324: if (eargc == oeargc)
1.2 dm 325: Cat(s, (u_char *)""); /* "nonomatch" is set */
1.1 deraadt 326: sort();
327: }
328:
1.5 millert 329: static int
1.1 deraadt 330: argcmp(a1, a2)
1.2 dm 331: char **a1, **a2;
1.1 deraadt 332: {
333:
1.2 dm 334: return (strcmp(*a1, *a2));
1.1 deraadt 335: }
336:
337: /*
338: * If there are any Shell meta characters in the name,
339: * expand into a list, after searching directory
340: */
1.2 dm 341: void expsh(s) /* quote in s */
342: u_char *s;
1.1 deraadt 343: {
1.7 mpech 344: u_char *cp, *oldcp;
345: char *spathp;
1.1 deraadt 346: struct stat stb;
347:
348: spathp = pathp;
349: cp = s;
350: while (!any(*cp, shchars)) {
1.2 dm 351: if (*cp == CNULL) {
1.1 deraadt 352: if (!expany || stat(path, &stb) >= 0) {
353: if (which & E_TILDE)
1.2 dm 354: Cat((u_char *)path, (u_char *)"");
1.1 deraadt 355: else
1.2 dm 356: Cat((u_char *)tilde, (u_char *)tpathp);
1.1 deraadt 357: }
358: goto endit;
359: }
1.2 dm 360: if (*cp == QUOTECHAR) cp++;
1.1 deraadt 361: addpath(*cp++);
362: }
363: oldcp = cp;
364: while (cp > s && *cp != '/')
365: cp--, pathp--;
366: if (*cp == '/')
367: cp++, pathp++;
1.2 dm 368: *pathp = CNULL;
1.1 deraadt 369: if (*oldcp == '{') {
1.5 millert 370: (void) execbrc(cp, NULL);
1.1 deraadt 371: return;
372: }
1.2 dm 373: matchdir((char *)cp);
1.1 deraadt 374: endit:
375: pathp = spathp;
1.2 dm 376: *pathp = CNULL;
1.1 deraadt 377: }
378:
1.2 dm 379: void matchdir(pattern) /* quote in pattern */
1.1 deraadt 380: char *pattern;
381: {
382: struct stat stb;
1.7 mpech 383: DIRENTRY *dp;
1.1 deraadt 384: DIR *dirp;
385:
386: dirp = opendir(path);
387: if (dirp == NULL) {
388: if (expany)
389: return;
390: goto patherr2;
391: }
392: if (fstat(dirp->dd_fd, &stb) < 0)
393: goto patherr1;
1.2 dm 394: if (!S_ISDIR(stb.st_mode)) {
1.1 deraadt 395: errno = ENOTDIR;
396: goto patherr1;
397: }
398: while ((dp = readdir(dirp)) != NULL)
399: if (match(dp->d_name, pattern)) {
400: if (which & E_TILDE)
1.2 dm 401: Cat((u_char *)path, (u_char *)dp->d_name);
1.1 deraadt 402: else {
1.9 ! millert 403: (void) strlcpy(pathp, dp->d_name,
! 404: lastpathp - pathp + 2);
1.2 dm 405: Cat((u_char *)tilde, (u_char *)tpathp);
406: *pathp = CNULL;
1.1 deraadt 407: }
408: }
409: closedir(dirp);
410: return;
411:
412: patherr1:
413: closedir(dirp);
414: patherr2:
1.9 ! millert 415: (void) strlcat(path, ": ", lastpathp - path + 2);
! 416: (void) strlcat(path, SYSERR, lastpathp - path + 2);
1.1 deraadt 417: yyerror(path);
418: }
419:
1.5 millert 420: int
1.2 dm 421: execbrc(p, s) /* quote in p */
422: u_char *p, *s;
1.1 deraadt 423: {
1.2 dm 424: u_char restbuf[BUFSIZ + 2];
1.7 mpech 425: u_char *pe, *pm, *pl;
1.1 deraadt 426: int brclev = 0;
1.2 dm 427: u_char *lm, savec;
428: char *spathp;
1.1 deraadt 429:
430: for (lm = restbuf; *p != '{'; *lm++ = *p++)
1.2 dm 431: if (*p == QUOTECHAR) *lm++ = *p++;
432:
1.1 deraadt 433: for (pe = ++p; *pe; pe++)
434: switch (*pe) {
435:
436: case '{':
437: brclev++;
438: continue;
439:
440: case '}':
441: if (brclev == 0)
442: goto pend;
443: brclev--;
444: continue;
445:
446: case '[':
447: for (pe++; *pe && *pe != ']'; pe++)
1.2 dm 448: if (*p == QUOTECHAR) pe++;
1.1 deraadt 449: if (!*pe)
450: yyerror("Missing ']'");
451: continue;
1.2 dm 452:
453: case QUOTECHAR: /* skip this character */
454: pe++;
455: continue;
1.1 deraadt 456: }
457: pend:
458: if (brclev || !*pe) {
459: yyerror("Missing '}'");
460: return (0);
461: }
462: for (pl = pm = p; pm <= pe; pm++)
1.2 dm 463: /* the strip code was a noop */
464: switch (*pm) {
1.1 deraadt 465:
466: case '{':
467: brclev++;
468: continue;
469:
470: case '}':
471: if (brclev) {
472: brclev--;
473: continue;
474: }
475: goto doit;
476:
477: case ',':
478: if (brclev)
479: continue;
480: doit:
481: savec = *pm;
482: *pm = 0;
1.9 ! millert 483: *lm = 0;
! 484: (void) strlcat((char *)restbuf, (char *)pl,
! 485: sizeof restbuf);
1.8 deraadt 486: (void) strlcat((char *)restbuf, (char *)pe + 1,
487: sizeof restbuf);
1.1 deraadt 488: *pm = savec;
489: if (s == 0) {
490: spathp = pathp;
491: expsh(restbuf);
492: pathp = spathp;
493: *pathp = 0;
1.2 dm 494: } else if (amatch((char *)s, restbuf))
1.1 deraadt 495: return (1);
496: sort();
497: pl = pm + 1;
498: continue;
499:
500: case '[':
501: for (pm++; *pm && *pm != ']'; pm++)
1.2 dm 502: if (*pm == QUOTECHAR) pm++;
1.1 deraadt 503: if (!*pm)
504: yyerror("Missing ']'");
505: continue;
1.2 dm 506:
507: case QUOTECHAR: /* skip one character */
508: pm++;
509: continue;
1.1 deraadt 510: }
511: return (0);
512: }
513:
1.5 millert 514: int
1.2 dm 515: match(s, p) /* quote in p */
1.1 deraadt 516: char *s, *p;
517: {
1.7 mpech 518: int c;
519: char *sentp;
1.1 deraadt 520: char sexpany = expany;
521:
522: if (*s == '.' && *p != '.')
523: return (0);
524: sentp = entp;
525: entp = s;
526: c = amatch(s, p);
527: entp = sentp;
528: expany = sexpany;
529: return (c);
530: }
531:
1.5 millert 532: int
1.2 dm 533: amatch(s, p) /* quote in p */
1.7 mpech 534: char *s;
535: u_char *p;
1.1 deraadt 536: {
1.7 mpech 537: int scc;
1.1 deraadt 538: int ok, lc;
539: char *spathp;
540: struct stat stb;
541: int c, cc;
542:
543: expany = 1;
544: for (;;) {
1.2 dm 545: scc = *s++;
1.1 deraadt 546: switch (c = *p++) {
547:
548: case '{':
1.2 dm 549: return (execbrc((u_char *)p - 1, (u_char *)s - 1));
1.1 deraadt 550:
551: case '[':
552: ok = 0;
553: lc = 077777;
1.5 millert 554: while ((cc = *p++)) {
1.1 deraadt 555: if (cc == ']') {
556: if (ok)
557: break;
558: return (0);
559: }
1.2 dm 560: if (cc == QUOTECHAR) cc = *p++;
1.1 deraadt 561: if (cc == '-') {
1.2 dm 562: if (lc <= scc && scc <= (int)*p++)
1.1 deraadt 563: ok++;
564: } else
565: if (scc == (lc = cc))
566: ok++;
567: }
568: if (cc == 0) {
569: yyerror("Missing ']'");
570: return (0);
571: }
572: continue;
573:
574: case '*':
575: if (!*p)
576: return (1);
577: if (*p == '/') {
578: p++;
579: goto slash;
580: }
581: for (s--; *s; s++)
582: if (amatch(s, p))
583: return (1);
584: return (0);
585:
1.2 dm 586: case CNULL:
587: return (scc == CNULL);
1.1 deraadt 588:
589: default:
1.2 dm 590: if (c != scc)
1.1 deraadt 591: return (0);
592: continue;
593:
594: case '?':
1.2 dm 595: if (scc == CNULL)
1.1 deraadt 596: return (0);
597: continue;
598:
599: case '/':
600: if (scc)
601: return (0);
602: slash:
603: s = entp;
604: spathp = pathp;
605: while (*s)
606: addpath(*s++);
607: addpath('/');
1.2 dm 608: if (stat(path, &stb) == 0 && S_ISDIR(stb.st_mode))
609: if (*p == CNULL) {
1.1 deraadt 610: if (which & E_TILDE)
1.2 dm 611: Cat((u_char *)path,
612: (u_char *)"");
1.1 deraadt 613: else
1.2 dm 614: Cat((u_char *)tilde,
615: (u_char *)tpathp);
1.5 millert 616: } else {
1.1 deraadt 617: expsh(p);
1.5 millert 618: }
1.1 deraadt 619: pathp = spathp;
1.2 dm 620: *pathp = CNULL;
1.1 deraadt 621: return (0);
622: }
623: }
624: }