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