Annotation of src/usr.bin/rdist/expand.c, Revision 1.16
1.16 ! millert 1: /* $OpenBSD: expand.c,v 1.15 2015/01/20 09:00:16 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>
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;
55:
56: int expany; /* any expansions done? */
57: char *entp;
58: char **sortbase;
59: char *argvbuf[MAXEARGS];
60:
1.1 deraadt 61: #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
62: sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
63:
1.10 millert 64: static void Cat(u_char *, u_char *);
65: static void addpath(int);
66: static int argcmp(const void *, const void *);
67:
68: static void
69: Cat(u_char *s1, u_char *s2) /* quote in s1 and s2 */
1.2 dm 70: {
1.7 mpech 71: char *cp;
1.2 dm 72: int len = strlen((char *)s1) + strlen((char *)s2) + 2;
73:
74: if ((eargc + 1) >= MAXEARGS) {
75: yyerror("Too many names");
76: return;
77: }
78:
1.5 millert 79: eargv[++eargc] = NULL;
1.2 dm 80: eargv[eargc - 1] = cp = xmalloc(len);
81:
82: do {
83: if (*s1 == QUOTECHAR)
84: s1++;
1.10 millert 85: } while ((*cp++ = *s1++) != '\0');
1.2 dm 86: cp--;
87: do {
88: if (*s2 == QUOTECHAR)
89: s2++;
1.10 millert 90: } while ((*cp++ = *s2++) != '\0');
1.2 dm 91: }
92:
1.10 millert 93: static void
94: addpath(int c)
1.2 dm 95: {
96: if (pathp >= lastpathp) {
97: yyerror("Pathname too long");
98: return;
99: } else {
100: *pathp++ = c;
101: *pathp = CNULL;
102: }
103: }
1.1 deraadt 104:
105: /*
106: * Take a list of names and expand any macros, etc.
107: * wh = E_VARS if expanding variables.
108: * wh = E_SHELL if expanding shell characters.
109: * wh = E_TILDE if expanding `~'.
110: * or any of these or'ed together.
111: *
112: * Major portions of this were snarfed from csh/sh.glob.c.
113: */
114: struct namelist *
1.10 millert 115: expand(struct namelist *list, int wh) /* quote in list->n_name */
1.1 deraadt 116: {
1.7 mpech 117: struct namelist *nl, *prev;
118: int n;
1.1 deraadt 119: char pathbuf[BUFSIZ];
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.16 ! millert 263: struct passwd *pw;
! 264:
1.1 deraadt 265: cp = ++s;
1.2 dm 266: if (*cp == CNULL || *cp == '/') {
1.1 deraadt 267: tilde = "~";
1.2 dm 268: cp1 = (u_char *)homedir;
1.1 deraadt 269: } else {
1.2 dm 270: tilde = (char *)(cp1 = ebuf);
1.1 deraadt 271: *cp1++ = '~';
272: do
273: *cp1++ = *cp++;
274: while (*cp && *cp != '/');
1.2 dm 275: *cp1 = CNULL;
1.16 ! millert 276: if (strcmp(locuser, (char *)ebuf+1) != 0) {
1.2 dm 277: if ((pw = getpwnam((char *)ebuf+1)) == NULL) {
1.10 millert 278: strlcat((char *)ebuf,
279: ": unknown user name",
280: sizeof(ebuf));
1.2 dm 281: yyerror((char *)ebuf+1);
1.1 deraadt 282: return;
283: }
1.16 ! millert 284: cp1 = (u_char *)pw->pw_dir;
! 285: } else {
! 286: cp1 = (u_char *)homedir;
1.1 deraadt 287: }
288: s = cp;
289: }
1.10 millert 290: for (cp = (u_char *)path; (*cp++ = *cp1++) != '\0'; )
291: continue;
1.2 dm 292: tpathp = pathp = (char *)cp - 1;
1.1 deraadt 293: } else {
294: tpathp = pathp = path;
295: tilde = "";
296: }
1.2 dm 297: *pathp = CNULL;
1.1 deraadt 298: if (!(which & E_SHELL)) {
299: if (which & E_TILDE)
1.2 dm 300: Cat((u_char *)path, s);
1.1 deraadt 301: else
1.2 dm 302: Cat((u_char *)tilde, s);
1.1 deraadt 303: sort();
304: return;
305: }
306: oeargc = eargc;
307: expany = 0;
308: expsh(s);
309: if (eargc == oeargc)
1.2 dm 310: Cat(s, (u_char *)""); /* "nonomatch" is set */
1.1 deraadt 311: sort();
312: }
313:
1.5 millert 314: static int
1.10 millert 315: argcmp(const void *v1, const void *v2)
1.1 deraadt 316: {
1.10 millert 317: const char *const *a1 = v1, *const *a2 = v2;
1.1 deraadt 318:
1.2 dm 319: return (strcmp(*a1, *a2));
1.1 deraadt 320: }
321:
322: /*
323: * If there are any Shell meta characters in the name,
324: * expand into a list, after searching directory
325: */
1.10 millert 326: void
327: expsh(u_char *s) /* quote in s */
1.1 deraadt 328: {
1.7 mpech 329: u_char *cp, *oldcp;
330: char *spathp;
1.1 deraadt 331: struct stat stb;
332:
333: spathp = pathp;
334: cp = s;
335: while (!any(*cp, shchars)) {
1.2 dm 336: if (*cp == CNULL) {
1.1 deraadt 337: if (!expany || stat(path, &stb) >= 0) {
338: if (which & E_TILDE)
1.2 dm 339: Cat((u_char *)path, (u_char *)"");
1.1 deraadt 340: else
1.2 dm 341: Cat((u_char *)tilde, (u_char *)tpathp);
1.1 deraadt 342: }
343: goto endit;
344: }
1.2 dm 345: if (*cp == QUOTECHAR) cp++;
1.1 deraadt 346: addpath(*cp++);
347: }
348: oldcp = cp;
349: while (cp > s && *cp != '/')
350: cp--, pathp--;
351: if (*cp == '/')
352: cp++, pathp++;
1.2 dm 353: *pathp = CNULL;
1.1 deraadt 354: if (*oldcp == '{') {
1.5 millert 355: (void) execbrc(cp, NULL);
1.1 deraadt 356: return;
357: }
1.2 dm 358: matchdir((char *)cp);
1.1 deraadt 359: endit:
360: pathp = spathp;
1.2 dm 361: *pathp = CNULL;
1.1 deraadt 362: }
363:
1.10 millert 364: void
365: matchdir(char *pattern) /* quote in pattern */
1.1 deraadt 366: {
367: struct stat stb;
1.14 guenther 368: struct dirent *dp;
1.1 deraadt 369: DIR *dirp;
370:
371: dirp = opendir(path);
372: if (dirp == NULL) {
373: if (expany)
374: return;
375: goto patherr2;
376: }
1.10 millert 377: if (fstat(dirfd(dirp), &stb) < 0)
1.1 deraadt 378: goto patherr1;
1.2 dm 379: if (!S_ISDIR(stb.st_mode)) {
1.1 deraadt 380: errno = ENOTDIR;
381: goto patherr1;
382: }
383: while ((dp = readdir(dirp)) != NULL)
384: if (match(dp->d_name, pattern)) {
385: if (which & E_TILDE)
1.2 dm 386: Cat((u_char *)path, (u_char *)dp->d_name);
1.1 deraadt 387: else {
1.9 millert 388: (void) strlcpy(pathp, dp->d_name,
389: lastpathp - pathp + 2);
1.2 dm 390: Cat((u_char *)tilde, (u_char *)tpathp);
391: *pathp = CNULL;
1.1 deraadt 392: }
393: }
394: closedir(dirp);
395: return;
396:
397: patherr1:
398: closedir(dirp);
399: patherr2:
1.9 millert 400: (void) strlcat(path, ": ", lastpathp - path + 2);
401: (void) strlcat(path, SYSERR, lastpathp - path + 2);
1.1 deraadt 402: yyerror(path);
403: }
404:
1.5 millert 405: int
1.10 millert 406: execbrc(u_char *p, u_char *s) /* quote in p */
1.1 deraadt 407: {
1.2 dm 408: u_char restbuf[BUFSIZ + 2];
1.7 mpech 409: u_char *pe, *pm, *pl;
1.1 deraadt 410: int brclev = 0;
1.2 dm 411: u_char *lm, savec;
412: char *spathp;
1.1 deraadt 413:
414: for (lm = restbuf; *p != '{'; *lm++ = *p++)
1.2 dm 415: if (*p == QUOTECHAR) *lm++ = *p++;
416:
1.1 deraadt 417: for (pe = ++p; *pe; pe++)
418: switch (*pe) {
419:
420: case '{':
421: brclev++;
422: continue;
423:
424: case '}':
425: if (brclev == 0)
426: goto pend;
427: brclev--;
428: continue;
429:
430: case '[':
431: for (pe++; *pe && *pe != ']'; pe++)
1.2 dm 432: if (*p == QUOTECHAR) pe++;
1.1 deraadt 433: if (!*pe)
434: yyerror("Missing ']'");
435: continue;
1.2 dm 436:
437: case QUOTECHAR: /* skip this character */
438: pe++;
439: continue;
1.1 deraadt 440: }
441: pend:
442: if (brclev || !*pe) {
443: yyerror("Missing '}'");
444: return (0);
445: }
446: for (pl = pm = p; pm <= pe; pm++)
1.2 dm 447: /* the strip code was a noop */
448: switch (*pm) {
1.1 deraadt 449:
450: case '{':
451: brclev++;
452: continue;
453:
454: case '}':
455: if (brclev) {
456: brclev--;
457: continue;
458: }
459: goto doit;
460:
461: case ',':
462: if (brclev)
463: continue;
464: doit:
465: savec = *pm;
466: *pm = 0;
1.9 millert 467: *lm = 0;
468: (void) strlcat((char *)restbuf, (char *)pl,
1.10 millert 469: sizeof(restbuf));
1.8 deraadt 470: (void) strlcat((char *)restbuf, (char *)pe + 1,
1.10 millert 471: sizeof(restbuf));
1.1 deraadt 472: *pm = savec;
473: if (s == 0) {
474: spathp = pathp;
475: expsh(restbuf);
476: pathp = spathp;
477: *pathp = 0;
1.2 dm 478: } else if (amatch((char *)s, restbuf))
1.1 deraadt 479: return (1);
480: sort();
481: pl = pm + 1;
482: continue;
483:
484: case '[':
485: for (pm++; *pm && *pm != ']'; pm++)
1.2 dm 486: if (*pm == QUOTECHAR) pm++;
1.1 deraadt 487: if (!*pm)
488: yyerror("Missing ']'");
489: continue;
1.2 dm 490:
491: case QUOTECHAR: /* skip one character */
492: pm++;
493: continue;
1.1 deraadt 494: }
495: return (0);
496: }
497:
1.5 millert 498: int
1.10 millert 499: match(char *s, char *p) /* quote in p */
1.1 deraadt 500: {
1.7 mpech 501: int c;
502: char *sentp;
1.1 deraadt 503: char sexpany = expany;
504:
505: if (*s == '.' && *p != '.')
506: return (0);
507: sentp = entp;
508: entp = s;
509: c = amatch(s, p);
510: entp = sentp;
511: expany = sexpany;
512: return (c);
513: }
514:
1.5 millert 515: int
1.10 millert 516: amatch(char *s, u_char *p) /* quote in p */
1.1 deraadt 517: {
1.7 mpech 518: int scc;
1.1 deraadt 519: int ok, lc;
520: char *spathp;
521: struct stat stb;
522: int c, cc;
523:
524: expany = 1;
525: for (;;) {
1.2 dm 526: scc = *s++;
1.1 deraadt 527: switch (c = *p++) {
528:
529: case '{':
1.2 dm 530: return (execbrc((u_char *)p - 1, (u_char *)s - 1));
1.1 deraadt 531:
532: case '[':
533: ok = 0;
534: lc = 077777;
1.10 millert 535: while ((cc = *p++) != '\0') {
1.1 deraadt 536: if (cc == ']') {
537: if (ok)
538: break;
539: return (0);
540: }
1.2 dm 541: if (cc == QUOTECHAR) cc = *p++;
1.1 deraadt 542: if (cc == '-') {
1.2 dm 543: if (lc <= scc && scc <= (int)*p++)
1.1 deraadt 544: ok++;
545: } else
546: if (scc == (lc = cc))
547: ok++;
548: }
549: if (cc == 0) {
550: yyerror("Missing ']'");
551: return (0);
552: }
553: continue;
554:
555: case '*':
556: if (!*p)
557: return (1);
558: if (*p == '/') {
559: p++;
560: goto slash;
561: }
562: for (s--; *s; s++)
563: if (amatch(s, p))
564: return (1);
565: return (0);
566:
1.2 dm 567: case CNULL:
568: return (scc == CNULL);
1.1 deraadt 569:
570: default:
1.2 dm 571: if (c != scc)
1.1 deraadt 572: return (0);
573: continue;
574:
575: case '?':
1.2 dm 576: if (scc == CNULL)
1.1 deraadt 577: return (0);
578: continue;
579:
580: case '/':
581: if (scc)
582: return (0);
583: slash:
584: s = entp;
585: spathp = pathp;
586: while (*s)
587: addpath(*s++);
588: addpath('/');
1.10 millert 589: if (stat(path, &stb) == 0 && S_ISDIR(stb.st_mode)) {
1.2 dm 590: if (*p == CNULL) {
1.10 millert 591: if (which & E_TILDE) {
1.2 dm 592: Cat((u_char *)path,
593: (u_char *)"");
1.10 millert 594: } else {
1.2 dm 595: Cat((u_char *)tilde,
596: (u_char *)tpathp);
1.10 millert 597: }
598: } else
1.1 deraadt 599: expsh(p);
1.10 millert 600: }
1.1 deraadt 601: pathp = spathp;
1.2 dm 602: *pathp = CNULL;
1.1 deraadt 603: return (0);
604: }
605: }
606: }