Annotation of src/usr.bin/rdist/expand.c, Revision 1.2
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: }