2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
33 * @(#)expand.c 8.1 (Berkeley) 6/9/93
34 * $FreeBSD: src/usr.bin/rdist/expand.c,v 1.8 1999/08/28 01:05:06 peter Exp $
35 * $DragonFly: src/usr.bin/rdist/expand.c,v 1.3 2003/11/03 19:31:31 eirikn Exp $
40 #define GAVSIZ NCARGS / 6
44 static char shchars[] = "${[*?";
46 int which; /* bit mask of types to expand */
47 int eargc; /* expanded arg count */
48 char **eargv; /* expanded arg vectors */
52 char *tilde; /* "~user" if not expanding tilde, else "" */
56 int expany; /* any expansions done? */
60 #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
61 sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
63 static void Cat(char *, char *);
64 static void addpath(int);
65 static int amatch(char *, char *);
66 static int argcmp(const void *, const void *);
67 static int execbrc(char *, char *);
68 static void expsh(char *);
69 static void expstr(char *);
70 static int match(char *, char *);
71 static void matchdir(char *);
72 static int smatch(char *, char *);
75 * Take a list of names and expand any macros, etc.
76 * wh = E_VARS if expanding variables.
77 * wh = E_SHELL if expanding shell characters.
78 * wh = E_TILDE if expanding `~'.
79 * or any of these or'ed together.
81 * Major portions of this were snarfed from csh/sh.glob.c.
85 struct namelist *list;
88 register struct namelist *nl, *prev;
91 char *argvbuf[GAVSIZ];
94 printf("expand(%p, %d)\nlist = ", list, wh);
101 for (nl = list; nl != NULL; nl = nl->n_next)
102 for (cp = nl->n_name; *cp; cp++)
108 path = tpathp = pathp = pathbuf;
110 lastpathp = &path[sizeof pathbuf - 2];
113 eargv = sortbase = argvbuf;
117 * Walk the name list and expand names into eargv[];
119 for (nl = list; nl != NULL; nl = nl->n_next)
122 * Take expanded list of names from eargv[] and build a new list.
125 for (n = 0; n < eargc; n++) {
127 nl->n_name = eargv[n];
136 printf("expanded list = ");
146 register char *cp, *cp1;
147 register struct namelist *tp;
151 extern char homedir[];
153 if (s == NULL || *s == '\0')
156 if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
159 yyerror("no variable name after '$'");
164 if ((tail = index(cp, RC)) == NULL) {
165 yyerror("unmatched '{'");
168 *tail++ = savec = '\0';
170 yyerror("no variable name after '$'");
178 tp = lookup(cp, NULL, 0);
182 for (; tp != NULL; tp = tp->n_next) {
183 snprintf(buf, sizeof(buf),
184 "%s%s%s", s, tp->n_name, tail);
189 snprintf(buf, sizeof(buf), "%s%s", s, tail);
193 if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
200 if (*cp == '\0' || *cp == '/') {
208 while (*cp && *cp != '/');
210 if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
211 if ((pw = getpwnam(buf+1)) == NULL) {
212 strcat(buf, ": unknown user name");
220 for (cp = path; (*cp++ = *cp1++); )
222 tpathp = pathp = cp - 1;
224 tpathp = pathp = path;
228 if (!(which & E_SHELL)) {
240 Cat(s, ""); /* "nonomatch" is set */
249 return (strcmp(*(char **)a1, *(char **)a2));
253 * If there are any Shell meta characters in the name,
254 * expand into a list, after searching directory
261 register char *spathp, *oldcp;
266 while (!any(*cp, shchars)) {
268 if (!expany || stat(path, &stb) >= 0) {
279 while (cp > s && *cp != '/')
299 register struct dirent *dp;
302 dirp = opendir(path);
308 if (fstat(dirp->dd_fd, &stb) < 0)
310 if (!ISDIR(stb.st_mode)) {
314 while ((dp = readdir(dirp)) != NULL)
315 if (match(dp->d_name, pattern)) {
317 Cat(path, dp->d_name);
319 strcpy(pathp, dp->d_name);
331 strcat(path, strerror(errno));
339 char restbuf[BUFSIZ + 2];
340 register char *pe, *pm, *pl;
342 char *lm, savec, *spathp;
344 for (lm = restbuf; *p != '{'; *lm++ = *p++)
346 for (pe = ++p; *pe; pe++)
360 for (pe++; *pe && *pe != ']'; pe++)
363 yyerror("Missing ']'");
367 if (brclev || !*pe) {
368 yyerror("Missing '}'");
371 for (pl = pm = p; pm <= pe; pm++)
372 switch (*pm & (QUOTE|TRIM)) {
392 strcat(restbuf, pe + 1);
399 } else if (amatch(s, restbuf))
406 for (pm++; *pm && *pm != ']'; pm++)
409 yyerror("Missing ']'");
420 register char *sentp;
421 char sexpany = expany;
423 if (*s == '.' && *p != '.')
435 register char *s, *p;
449 return (execbrc(p - 1, s - 1));
454 while ((cc = *p++)) {
461 if (lc <= scc && scc <= *p++)
464 if (scc == (lc = cc))
468 yyerror("Missing ']'");
486 return (scc == '\0');
489 if ((c & TRIM) != scc)
507 if (stat(path, &stb) == 0 && ISDIR(stb.st_mode)) {
525 register char *s, *p;
538 while ((cc = *p++)) {
545 if (lc <= scc && scc <= *p++)
548 if (scc == (lc = cc))
552 yyerror("Missing ']'");
566 return (scc == '\0');
569 if ((c & TRIM) != scc)
584 register char *s1, *s2;
586 int len = strlen(s1) + strlen(s2) + 1;
590 if (nleft <= 0 || ++eargc >= GAVSIZ)
591 yyerror("Arguments too long");
593 eargv[eargc - 1] = s = malloc(len);
595 fatal("ran out of memory\n");
596 while ((*s++ = *s1++ & TRIM))
599 while ((*s++ = *s2++ & TRIM))
608 if (pathp >= lastpathp)
609 yyerror("Pathname too long");
617 * Expand file names beginning with `~' into the
618 * user's home directory path name. Return a pointer in buf to the
619 * part corresponding to `file'.
622 exptilde(buf, file, maxlen)
627 register char *s1, *s2, *s3;
628 extern char homedir[];
630 if (strlen(file) >= maxlen)
636 if (*++file == '\0') {
639 } else if (*file == '/') {
644 while (*s3 && *s3 != '/')
650 if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
651 if ((pw = getpwnam(file)) == NULL) {
652 error("%s: unknown user name\n", file);
662 for (s1 = buf; (*s1++ = *s2++) && s1 < buf+maxlen; )
665 if (s3 != NULL && s1 < buf+maxlen) {
667 while ((*s1++ = *s3++) && s1 < buf+maxlen)
670 if (s1 == buf+maxlen)