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