diff options
Diffstat (limited to 'usr.bin/rdist/expand.c')
-rw-r--r-- | usr.bin/rdist/expand.c | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/usr.bin/rdist/expand.c b/usr.bin/rdist/expand.c new file mode 100644 index 0000000..6b5fd17 --- /dev/null +++ b/usr.bin/rdist/expand.c @@ -0,0 +1,666 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)expand.c 8.1 (Berkeley) 6/9/93"; +#endif /* not lint */ + +#include "defs.h" + +#define GAVSIZ NCARGS / 6 +#define LC '{' +#define RC '}' + +static char shchars[] = "${[*?"; + +int which; /* bit mask of types to expand */ +int eargc; /* expanded arg count */ +char **eargv; /* expanded arg vectors */ +char *path; +char *pathp; +char *lastpathp; +char *tilde; /* "~user" if not expanding tilde, else "" */ +char *tpathp; +int nleft; + +int expany; /* any expansions done? */ +char *entp; +char **sortbase; + +#define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \ + sizeof(*sortbase), argcmp), sortbase = &eargv[eargc] + +static void Cat __P((char *, char *)); +static void addpath __P((int)); +static int amatch __P((char *, char *)); +static int argcmp __P((const void *, const void *)); +static int execbrc __P((char *, char *)); +static void expsh __P((char *)); +static void expstr __P((char *)); +static int match __P((char *, char *)); +static void matchdir __P((char *)); +static int smatch __P((char *, char *)); + +/* + * Take a list of names and expand any macros, etc. + * wh = E_VARS if expanding variables. + * wh = E_SHELL if expanding shell characters. + * wh = E_TILDE if expanding `~'. + * or any of these or'ed together. + * + * Major portions of this were snarfed from csh/sh.glob.c. + */ +struct namelist * +expand(list, wh) + struct namelist *list; + int wh; +{ + register struct namelist *nl, *prev; + register int n; + char pathbuf[BUFSIZ]; + char *argvbuf[GAVSIZ]; + + if (debug) { + printf("expand(%x, %d)\nlist = ", list, wh); + prnames(list); + } + + if (wh == 0) { + register char *cp; + + for (nl = list; nl != NULL; nl = nl->n_next) + for (cp = nl->n_name; *cp; cp++) + *cp = *cp & TRIM; + return(list); + } + + which = wh; + path = tpathp = pathp = pathbuf; + *pathp = '\0'; + lastpathp = &path[sizeof pathbuf - 2]; + tilde = ""; + eargc = 0; + eargv = sortbase = argvbuf; + *eargv = 0; + nleft = NCARGS - 4; + /* + * Walk the name list and expand names into eargv[]; + */ + for (nl = list; nl != NULL; nl = nl->n_next) + expstr(nl->n_name); + /* + * Take expanded list of names from eargv[] and build a new list. + */ + list = prev = NULL; + for (n = 0; n < eargc; n++) { + nl = makenl(NULL); + nl->n_name = eargv[n]; + if (prev == NULL) + list = prev = nl; + else { + prev->n_next = nl; + prev = nl; + } + } + if (debug) { + printf("expanded list = "); + prnames(list); + } + return(list); +} + +static void +expstr(s) + char *s; +{ + register char *cp, *cp1; + register struct namelist *tp; + char *tail; + char buf[BUFSIZ]; + int savec, oeargc; + extern char homedir[]; + + if (s == NULL || *s == '\0') + return; + + if ((which & E_VARS) && (cp = index(s, '$')) != NULL) { + *cp++ = '\0'; + if (*cp == '\0') { + yyerror("no variable name after '$'"); + return; + } + if (*cp == LC) { + cp++; + if ((tail = index(cp, RC)) == NULL) { + yyerror("unmatched '{'"); + return; + } + *tail++ = savec = '\0'; + if (*cp == '\0') { + yyerror("no variable name after '$'"); + return; + } + } else { + tail = cp + 1; + savec = *tail; + *tail = '\0'; + } + tp = lookup(cp, NULL, 0); + if (savec != '\0') + *tail = savec; + if (tp != NULL) { + for (; tp != NULL; tp = tp->n_next) { + sprintf(buf, "%s%s%s", s, tp->n_name, tail); + expstr(buf); + } + return; + } + sprintf(buf, "%s%s", s, tail); + expstr(buf); + return; + } + if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) { + Cat(s, ""); + sort(); + return; + } + if (*s == '~') { + cp = ++s; + if (*cp == '\0' || *cp == '/') { + tilde = "~"; + cp1 = homedir; + } else { + tilde = cp1 = buf; + *cp1++ = '~'; + do + *cp1++ = *cp++; + while (*cp && *cp != '/'); + *cp1 = '\0'; + if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) { + if ((pw = getpwnam(buf+1)) == NULL) { + strcat(buf, ": unknown user name"); + yyerror(buf+1); + return; + } + } + cp1 = pw->pw_dir; + s = cp; + } + for (cp = path; *cp++ = *cp1++; ) + ; + tpathp = pathp = cp - 1; + } else { + tpathp = pathp = path; + tilde = ""; + } + *pathp = '\0'; + if (!(which & E_SHELL)) { + if (which & E_TILDE) + Cat(path, s); + else + Cat(tilde, s); + sort(); + return; + } + oeargc = eargc; + expany = 0; + expsh(s); + if (eargc == oeargc) + Cat(s, ""); /* "nonomatch" is set */ + sort(); +} + +static int +argcmp(a1, a2) + const void *a1, *a2; +{ + + return (strcmp(*(char **)a1, *(char **)a2)); +} + +/* + * If there are any Shell meta characters in the name, + * expand into a list, after searching directory + */ +static void +expsh(s) + char *s; +{ + register char *cp; + register char *spathp, *oldcp; + struct stat stb; + + spathp = pathp; + cp = s; + while (!any(*cp, shchars)) { + if (*cp == '\0') { + if (!expany || stat(path, &stb) >= 0) { + if (which & E_TILDE) + Cat(path, ""); + else + Cat(tilde, tpathp); + } + goto endit; + } + addpath(*cp++); + } + oldcp = cp; + while (cp > s && *cp != '/') + cp--, pathp--; + if (*cp == '/') + cp++, pathp++; + *pathp = '\0'; + if (*oldcp == '{') { + execbrc(cp, NULL); + return; + } + matchdir(cp); +endit: + pathp = spathp; + *pathp = '\0'; +} + +static void +matchdir(pattern) + char *pattern; +{ + struct stat stb; + register struct direct *dp; + DIR *dirp; + + dirp = opendir(path); + if (dirp == NULL) { + if (expany) + return; + goto patherr2; + } + if (fstat(dirp->dd_fd, &stb) < 0) + goto patherr1; + if (!ISDIR(stb.st_mode)) { + errno = ENOTDIR; + goto patherr1; + } + while ((dp = readdir(dirp)) != NULL) + if (match(dp->d_name, pattern)) { + if (which & E_TILDE) + Cat(path, dp->d_name); + else { + strcpy(pathp, dp->d_name); + Cat(tilde, tpathp); + *pathp = '\0'; + } + } + closedir(dirp); + return; + +patherr1: + closedir(dirp); +patherr2: + strcat(path, ": "); + strcat(path, strerror(errno)); + yyerror(path); +} + +static int +execbrc(p, s) + char *p, *s; +{ + char restbuf[BUFSIZ + 2]; + register char *pe, *pm, *pl; + int brclev = 0; + char *lm, savec, *spathp; + + for (lm = restbuf; *p != '{'; *lm++ = *p++) + continue; + for (pe = ++p; *pe; pe++) + switch (*pe) { + + case '{': + brclev++; + continue; + + case '}': + if (brclev == 0) + goto pend; + brclev--; + continue; + + case '[': + for (pe++; *pe && *pe != ']'; pe++) + continue; + if (!*pe) + yyerror("Missing ']'"); + continue; + } +pend: + if (brclev || !*pe) { + yyerror("Missing '}'"); + return (0); + } + for (pl = pm = p; pm <= pe; pm++) + switch (*pm & (QUOTE|TRIM)) { + + case '{': + brclev++; + continue; + + case '}': + if (brclev) { + brclev--; + continue; + } + goto doit; + + case ',': + if (brclev) + continue; +doit: + savec = *pm; + *pm = 0; + strcpy(lm, pl); + strcat(restbuf, pe + 1); + *pm = savec; + if (s == 0) { + spathp = pathp; + expsh(restbuf); + pathp = spathp; + *pathp = 0; + } else if (amatch(s, restbuf)) + return (1); + sort(); + pl = pm + 1; + continue; + + case '[': + for (pm++; *pm && *pm != ']'; pm++) + continue; + if (!*pm) + yyerror("Missing ']'"); + continue; + } + return (0); +} + +static int +match(s, p) + char *s, *p; +{ + register int c; + register char *sentp; + char sexpany = expany; + + if (*s == '.' && *p != '.') + return (0); + sentp = entp; + entp = s; + c = amatch(s, p); + entp = sentp; + expany = sexpany; + return (c); +} + +static int +amatch(s, p) + register char *s, *p; +{ + register int scc; + int ok, lc; + char *spathp; + struct stat stb; + int c, cc; + + expany = 1; + for (;;) { + scc = *s++ & TRIM; + switch (c = *p++) { + + case '{': + return (execbrc(p - 1, s - 1)); + + case '[': + ok = 0; + lc = 077777; + while (cc = *p++) { + if (cc == ']') { + if (ok) + break; + return (0); + } + if (cc == '-') { + if (lc <= scc && scc <= *p++) + ok++; + } else + if (scc == (lc = cc)) + ok++; + } + if (cc == 0) { + yyerror("Missing ']'"); + return (0); + } + continue; + + case '*': + if (!*p) + return (1); + if (*p == '/') { + p++; + goto slash; + } + for (s--; *s; s++) + if (amatch(s, p)) + return (1); + return (0); + + case '\0': + return (scc == '\0'); + + default: + if ((c & TRIM) != scc) + return (0); + continue; + + case '?': + if (scc == '\0') + return (0); + continue; + + case '/': + if (scc) + return (0); +slash: + s = entp; + spathp = pathp; + while (*s) + addpath(*s++); + addpath('/'); + if (stat(path, &stb) == 0 && ISDIR(stb.st_mode)) + if (*p == '\0') { + if (which & E_TILDE) + Cat(path, ""); + else + Cat(tilde, tpathp); + } else + expsh(p); + pathp = spathp; + *pathp = '\0'; + return (0); + } + } +} + +static int +smatch(s, p) + register char *s, *p; +{ + register int scc; + int ok, lc; + int c, cc; + + for (;;) { + scc = *s++ & TRIM; + switch (c = *p++) { + + case '[': + ok = 0; + lc = 077777; + while (cc = *p++) { + if (cc == ']') { + if (ok) + break; + return (0); + } + if (cc == '-') { + if (lc <= scc && scc <= *p++) + ok++; + } else + if (scc == (lc = cc)) + ok++; + } + if (cc == 0) { + yyerror("Missing ']'"); + return (0); + } + continue; + + case '*': + if (!*p) + return (1); + for (s--; *s; s++) + if (smatch(s, p)) + return (1); + return (0); + + case '\0': + return (scc == '\0'); + + default: + if ((c & TRIM) != scc) + return (0); + continue; + + case '?': + if (scc == 0) + return (0); + continue; + + } + } +} + +static void +Cat(s1, s2) + register char *s1, *s2; +{ + int len = strlen(s1) + strlen(s2) + 1; + register char *s; + + nleft -= len; + if (nleft <= 0 || ++eargc >= GAVSIZ) + yyerror("Arguments too long"); + eargv[eargc] = 0; + eargv[eargc - 1] = s = malloc(len); + if (s == NULL) + fatal("ran out of memory\n"); + while (*s++ = *s1++ & TRIM) + ; + s--; + while (*s++ = *s2++ & TRIM) + ; +} + +static void +addpath(c) + int c; +{ + + if (pathp >= lastpathp) + yyerror("Pathname too long"); + else { + *pathp++ = c & TRIM; + *pathp = '\0'; + } +} + +/* + * Expand file names beginning with `~' into the + * user's home directory path name. Return a pointer in buf to the + * part corresponding to `file'. + */ +char * +exptilde(buf, file) + char buf[]; + register char *file; +{ + register char *s1, *s2, *s3; + extern char homedir[]; + + if (*file != '~') { + strcpy(buf, file); + return(buf); + } + if (*++file == '\0') { + s2 = homedir; + s3 = NULL; + } else if (*file == '/') { + s2 = homedir; + s3 = file; + } else { + s3 = file; + while (*s3 && *s3 != '/') + s3++; + if (*s3 == '/') + *s3 = '\0'; + else + s3 = NULL; + if (pw == NULL || strcmp(pw->pw_name, file) != 0) { + if ((pw = getpwnam(file)) == NULL) { + error("%s: unknown user name\n", file); + if (s3 != NULL) + *s3 = '/'; + return(NULL); + } + } + if (s3 != NULL) + *s3 = '/'; + s2 = pw->pw_dir; + } + for (s1 = buf; *s1++ = *s2++; ) + ; + s2 = --s1; + if (s3 != NULL) { + s2++; + while (*s1++ = *s3++) + ; + } + return(s2); +} |