Annotation of src/usr.bin/rdist/expand.c, Revision 1.3
1.1 deraadt 1: /*
1.2 dm 2: * Copyright (c) 1983 Regents of the University of California.
3: * All rights reserved.
1.1 deraadt 4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. All advertising materials mentioning features or use of this software
14: * must display the following acknowledgement:
15: * This product includes software developed by the University of
16: * California, Berkeley and its contributors.
17: * 4. Neither the name of the University nor the names of its contributors
18: * may be used to endorse or promote products derived from this software
19: * without specific prior written permission.
20: *
21: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31: * SUCH DAMAGE.
32: */
33:
34: #ifndef lint
1.2 dm 35: static char RCSid[] =
36: "$Id: expand.c,v 6.17 1994/03/14 23:25:24 mcooper Exp $";
37:
38: static char sccsid[] = "@(#)expand.c 5.2 (Berkeley) 3/28/86";
39:
40: char copyright[] =
41: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
42: All rights reserved.\n";
1.1 deraadt 43: #endif /* not lint */
44:
45: #include "defs.h"
46:
1.2 dm 47: #define MAXEARGS 2048
48: #define LC '{'
49: #define RC '}'
1.1 deraadt 50:
51: static char shchars[] = "${[*?";
52:
1.2 dm 53: int which; /* bit mask of types to expand */
54: int eargc; /* expanded arg count */
55: char **eargv; /* expanded arg vectors */
56: char *path;
57: char *pathp;
58: char *lastpathp;
59: char *tilde; /* "~user" if not expanding tilde, else "" */
60: char *tpathp;
61:
62: int expany; /* any expansions done? */
63: char *entp;
64: char **sortbase;
65: char *argvbuf[MAXEARGS];
66:
67: static int argcmp();
68: void expstr();
69: void expsh();
70: void matchdir();
1.1 deraadt 71:
72: #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
73: sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
74:
1.2 dm 75: static void Cat(s1, s2) /* quote in s1 and s2 */
76: register u_char *s1, *s2;
77: {
78: register char *cp;
79: int len = strlen((char *)s1) + strlen((char *)s2) + 2;
80:
81: if ((eargc + 1) >= MAXEARGS) {
82: yyerror("Too many names");
83: return;
84: }
85:
86: eargv[++eargc] = (char *) NULL;
87: eargv[eargc - 1] = cp = xmalloc(len);
88:
89: do {
90: if (*s1 == QUOTECHAR)
91: s1++;
92: } while (*cp++ = *s1++);
93: cp--;
94: do {
95: if (*s2 == QUOTECHAR)
96: s2++;
97: } while (*cp++ = *s2++);
98: }
99:
100: static void addpath(c)
101: char c;
102: {
103: if (pathp >= lastpathp) {
104: yyerror("Pathname too long");
105: return;
106: } else {
107: *pathp++ = c;
108: *pathp = CNULL;
109: }
110: }
1.1 deraadt 111:
112: /*
113: * Take a list of names and expand any macros, etc.
114: * wh = E_VARS if expanding variables.
115: * wh = E_SHELL if expanding shell characters.
116: * wh = E_TILDE if expanding `~'.
117: * or any of these or'ed together.
118: *
119: * Major portions of this were snarfed from csh/sh.glob.c.
120: */
121: struct namelist *
1.2 dm 122: expand(list, wh) /* quote in list->n_name */
1.1 deraadt 123: struct namelist *list;
124: int wh;
125: {
126: register struct namelist *nl, *prev;
127: register int n;
128: char pathbuf[BUFSIZ];
129:
1.2 dm 130: if (debug)
131: debugmsg(DM_CALL, "expand(%x, %d) start, list = %s",
132: list, wh, getnlstr(list));
1.1 deraadt 133:
1.2 dm 134: if (wh == 0)
135: fatalerr("expand() contains invalid 'wh' argument.");
1.1 deraadt 136:
137: which = wh;
138: path = tpathp = pathp = pathbuf;
1.2 dm 139: *pathp = CNULL;
1.1 deraadt 140: lastpathp = &path[sizeof pathbuf - 2];
141: tilde = "";
142: eargc = 0;
143: eargv = sortbase = argvbuf;
1.2 dm 144: *eargv = (char *) NULL;
145:
1.1 deraadt 146: /*
147: * Walk the name list and expand names into eargv[];
148: */
149: for (nl = list; nl != NULL; nl = nl->n_next)
1.2 dm 150: expstr((u_char *)nl->n_name);
1.1 deraadt 151: /*
152: * Take expanded list of names from eargv[] and build a new list.
153: */
154: list = prev = NULL;
155: for (n = 0; n < eargc; n++) {
1.2 dm 156: nl = makenl((char *)NULL);
1.1 deraadt 157: nl->n_name = eargv[n];
158: if (prev == NULL)
159: list = prev = nl;
160: else {
161: prev->n_next = nl;
162: prev = nl;
163: }
164: }
1.2 dm 165:
1.1 deraadt 166: return(list);
167: }
168:
1.2 dm 169: /*
170: * xstrchr() is a version of strchr() that
171: * handles u_char buffers.
172: */
173: u_char *xstrchr(str, ch)
174: u_char *str;
175: int ch;
1.1 deraadt 176: {
1.2 dm 177: register u_char *cp;
178:
179: for (cp = str; cp && *cp != CNULL; ++cp)
180: if (ch == *cp)
181: return(cp);
182:
183: return((u_char *)NULL);
184: }
185:
186: void expstr(s)
187: u_char *s;
188: {
189: register u_char *cp, *cp1;
1.1 deraadt 190: register 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)) {
204: if ((int)strlen((char *)s) > sizeof(varbuff)) {
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.2 dm 252: tp = lookup((char *)cp, LOOKUP, (struct namelist *)NULL);
253: if (savec != CNULL)
1.1 deraadt 254: *tail = savec;
255: if (tp != NULL) {
256: for (; tp != NULL; tp = tp->n_next) {
1.2 dm 257: (void) sprintf((char *)ebuf,
258: "%s%s%s", s, tp->n_name, tail);
259: expstr(ebuf);
1.1 deraadt 260: }
261: return;
262: }
1.2 dm 263: (void) sprintf((char *)ebuf, "%s%s", s, tail);
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) {
288: strcat((char *)ebuf,
289: ": unknown user name");
290: yyerror((char *)ebuf+1);
1.1 deraadt 291: return;
292: }
293: }
1.2 dm 294: cp1 = (u_char *)pw->pw_dir;
1.1 deraadt 295: s = cp;
296: }
1.2 dm 297: for (cp = (u_char *)path; *cp++ = *cp1++; )
1.1 deraadt 298: ;
1.2 dm 299: tpathp = pathp = (char *)cp - 1;
1.1 deraadt 300: } else {
301: tpathp = pathp = path;
302: tilde = "";
303: }
1.2 dm 304: *pathp = CNULL;
1.1 deraadt 305: if (!(which & E_SHELL)) {
306: if (which & E_TILDE)
1.2 dm 307: Cat((u_char *)path, s);
1.1 deraadt 308: else
1.2 dm 309: Cat((u_char *)tilde, s);
1.1 deraadt 310: sort();
311: return;
312: }
313: oeargc = eargc;
314: expany = 0;
315: expsh(s);
316: if (eargc == oeargc)
1.2 dm 317: Cat(s, (u_char *)""); /* "nonomatch" is set */
1.1 deraadt 318: sort();
319: }
320:
1.2 dm 321: static
1.1 deraadt 322: argcmp(a1, a2)
1.2 dm 323: char **a1, **a2;
1.1 deraadt 324: {
325:
1.2 dm 326: return (strcmp(*a1, *a2));
1.1 deraadt 327: }
328:
329: /*
330: * If there are any Shell meta characters in the name,
331: * expand into a list, after searching directory
332: */
1.2 dm 333: void expsh(s) /* quote in s */
334: u_char *s;
1.1 deraadt 335: {
1.2 dm 336: register u_char *cp, *oldcp;
337: register char *spathp;
1.1 deraadt 338: struct stat stb;
339:
340: spathp = pathp;
341: cp = s;
342: while (!any(*cp, shchars)) {
1.2 dm 343: if (*cp == CNULL) {
1.1 deraadt 344: if (!expany || stat(path, &stb) >= 0) {
345: if (which & E_TILDE)
1.2 dm 346: Cat((u_char *)path, (u_char *)"");
1.1 deraadt 347: else
1.2 dm 348: Cat((u_char *)tilde, (u_char *)tpathp);
1.1 deraadt 349: }
350: goto endit;
351: }
1.2 dm 352: if (*cp == QUOTECHAR) cp++;
1.1 deraadt 353: addpath(*cp++);
354: }
355: oldcp = cp;
356: while (cp > s && *cp != '/')
357: cp--, pathp--;
358: if (*cp == '/')
359: cp++, pathp++;
1.2 dm 360: *pathp = CNULL;
1.1 deraadt 361: if (*oldcp == '{') {
1.2 dm 362: (void) execbrc(cp, (u_char *)NULL);
1.1 deraadt 363: return;
364: }
1.2 dm 365: matchdir((char *)cp);
1.1 deraadt 366: endit:
367: pathp = spathp;
1.2 dm 368: *pathp = CNULL;
1.1 deraadt 369: }
370:
1.2 dm 371: void matchdir(pattern) /* quote in pattern */
1.1 deraadt 372: char *pattern;
373: {
374: struct stat stb;
1.2 dm 375: register DIRENTRY *dp;
1.1 deraadt 376: DIR *dirp;
377:
378: dirp = opendir(path);
379: if (dirp == NULL) {
380: if (expany)
381: return;
382: goto patherr2;
383: }
384: if (fstat(dirp->dd_fd, &stb) < 0)
385: goto patherr1;
1.2 dm 386: if (!S_ISDIR(stb.st_mode)) {
1.1 deraadt 387: errno = ENOTDIR;
388: goto patherr1;
389: }
390: while ((dp = readdir(dirp)) != NULL)
391: if (match(dp->d_name, pattern)) {
392: if (which & E_TILDE)
1.2 dm 393: Cat((u_char *)path, (u_char *)dp->d_name);
1.1 deraadt 394: else {
1.2 dm 395: (void) strcpy(pathp, dp->d_name);
396: Cat((u_char *)tilde, (u_char *)tpathp);
397: *pathp = CNULL;
1.1 deraadt 398: }
399: }
400: closedir(dirp);
401: return;
402:
403: patherr1:
404: closedir(dirp);
405: patherr2:
1.2 dm 406: (void) strcat(path, ": ");
407: (void) strcat(path, SYSERR);
1.1 deraadt 408: yyerror(path);
409: }
410:
1.2 dm 411: execbrc(p, s) /* quote in p */
412: u_char *p, *s;
1.1 deraadt 413: {
1.2 dm 414: u_char restbuf[BUFSIZ + 2];
415: register u_char *pe, *pm, *pl;
1.1 deraadt 416: int brclev = 0;
1.2 dm 417: u_char *lm, savec;
418: char *spathp;
1.1 deraadt 419:
420: for (lm = restbuf; *p != '{'; *lm++ = *p++)
1.2 dm 421: if (*p == QUOTECHAR) *lm++ = *p++;
422:
1.1 deraadt 423: for (pe = ++p; *pe; pe++)
424: switch (*pe) {
425:
426: case '{':
427: brclev++;
428: continue;
429:
430: case '}':
431: if (brclev == 0)
432: goto pend;
433: brclev--;
434: continue;
435:
436: case '[':
437: for (pe++; *pe && *pe != ']'; pe++)
1.2 dm 438: if (*p == QUOTECHAR) pe++;
1.1 deraadt 439: if (!*pe)
440: yyerror("Missing ']'");
441: continue;
1.2 dm 442:
443: case QUOTECHAR: /* skip this character */
444: pe++;
445: continue;
1.1 deraadt 446: }
447: pend:
448: if (brclev || !*pe) {
449: yyerror("Missing '}'");
450: return (0);
451: }
452: for (pl = pm = p; pm <= pe; pm++)
1.2 dm 453: /* the strip code was a noop */
454: switch (*pm) {
1.1 deraadt 455:
456: case '{':
457: brclev++;
458: continue;
459:
460: case '}':
461: if (brclev) {
462: brclev--;
463: continue;
464: }
465: goto doit;
466:
467: case ',':
468: if (brclev)
469: continue;
470: doit:
471: savec = *pm;
472: *pm = 0;
1.2 dm 473: (void) strcpy((char *)lm, (char *)pl);
474: (void) strcat((char *)restbuf, (char *)pe + 1);
1.1 deraadt 475: *pm = savec;
476: if (s == 0) {
477: spathp = pathp;
478: expsh(restbuf);
479: pathp = spathp;
480: *pathp = 0;
1.2 dm 481: } else if (amatch((char *)s, restbuf))
1.1 deraadt 482: return (1);
483: sort();
484: pl = pm + 1;
485: continue;
486:
487: case '[':
488: for (pm++; *pm && *pm != ']'; pm++)
1.2 dm 489: if (*pm == QUOTECHAR) pm++;
1.1 deraadt 490: if (!*pm)
491: yyerror("Missing ']'");
492: continue;
1.2 dm 493:
494: case QUOTECHAR: /* skip one character */
495: pm++;
496: continue;
1.1 deraadt 497: }
498: return (0);
499: }
500:
1.2 dm 501: match(s, p) /* quote in p */
1.1 deraadt 502: char *s, *p;
503: {
504: register int c;
505: register char *sentp;
506: char sexpany = expany;
507:
508: if (*s == '.' && *p != '.')
509: return (0);
510: sentp = entp;
511: entp = s;
512: c = amatch(s, p);
513: entp = sentp;
514: expany = sexpany;
515: return (c);
516: }
517:
1.2 dm 518: amatch(s, p) /* quote in p */
519: register char *s;
520: register u_char *p;
1.1 deraadt 521: {
522: register int scc;
523: int ok, lc;
524: char *spathp;
525: struct stat stb;
526: int c, cc;
527:
528: expany = 1;
529: for (;;) {
1.2 dm 530: scc = *s++;
1.1 deraadt 531: switch (c = *p++) {
532:
533: case '{':
1.2 dm 534: return (execbrc((u_char *)p - 1, (u_char *)s - 1));
1.1 deraadt 535:
536: case '[':
537: ok = 0;
538: lc = 077777;
539: while (cc = *p++) {
540: if (cc == ']') {
541: if (ok)
542: break;
543: return (0);
544: }
1.2 dm 545: if (cc == QUOTECHAR) cc = *p++;
1.1 deraadt 546: if (cc == '-') {
1.2 dm 547: if (lc <= scc && scc <= (int)*p++)
1.1 deraadt 548: ok++;
549: } else
550: if (scc == (lc = cc))
551: ok++;
552: }
553: if (cc == 0) {
554: yyerror("Missing ']'");
555: return (0);
556: }
557: continue;
558:
559: case '*':
560: if (!*p)
561: return (1);
562: if (*p == '/') {
563: p++;
564: goto slash;
565: }
566: for (s--; *s; s++)
567: if (amatch(s, p))
568: return (1);
569: return (0);
570:
1.2 dm 571: case CNULL:
572: return (scc == CNULL);
1.1 deraadt 573:
574: default:
1.2 dm 575: if (c != scc)
1.1 deraadt 576: return (0);
577: continue;
578:
579: case '?':
1.2 dm 580: if (scc == CNULL)
1.1 deraadt 581: return (0);
582: continue;
583:
584: case '/':
585: if (scc)
586: return (0);
587: slash:
588: s = entp;
589: spathp = pathp;
590: while (*s)
591: addpath(*s++);
592: addpath('/');
1.2 dm 593: if (stat(path, &stb) == 0 && S_ISDIR(stb.st_mode))
594: if (*p == CNULL) {
1.1 deraadt 595: if (which & E_TILDE)
1.2 dm 596: Cat((u_char *)path,
597: (u_char *)"");
1.1 deraadt 598: else
1.2 dm 599: Cat((u_char *)tilde,
600: (u_char *)tpathp);
1.1 deraadt 601: } else
602: expsh(p);
603: pathp = spathp;
1.2 dm 604: *pathp = CNULL;
1.1 deraadt 605: return (0);
606: }
607: }
608: }