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