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