xref: /netbsd-src/usr.bin/rdist/expand.c (revision 2bd8f802e402fd318180dfc11d6dd13f109f058b)
1*2bd8f802Smrg /*	$NetBSD: expand.c,v 1.19 2023/08/03 08:03:19 mrg Exp $	*/
23aab4f7dSthorpej 
361f28255Scgd /*
4a5bfdf78Scgd  * Copyright (c) 1983, 1993
5a5bfdf78Scgd  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
1589aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
323b3cf635Slukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
343aab4f7dSthorpej #if 0
353aab4f7dSthorpej static char sccsid[] = "@(#)expand.c	8.1 (Berkeley) 6/9/93";
363aab4f7dSthorpej #else
37*2bd8f802Smrg __RCSID("$NetBSD: expand.c,v 1.19 2023/08/03 08:03:19 mrg Exp $");
383aab4f7dSthorpej #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
410026c9e9Smrg #include <sys/types.h>
420026c9e9Smrg 
430026c9e9Smrg #include <errno.h>
440026c9e9Smrg #include <pwd.h>
450026c9e9Smrg 
4661f28255Scgd #include "defs.h"
4761f28255Scgd 
4861f28255Scgd #define	GAVSIZ	NCARGS / 6
4961f28255Scgd #define LC '{'
5061f28255Scgd #define RC '}'
5161f28255Scgd 
5261f28255Scgd static char	shchars[] = "${[*?";
5361f28255Scgd 
5461f28255Scgd int	which;		/* bit mask of types to expand */
5561f28255Scgd int	eargc;		/* expanded arg count */
5661f28255Scgd char	**eargv;	/* expanded arg vectors */
5761f28255Scgd char	*path;
5861f28255Scgd char	*pathp;
5961f28255Scgd char	*lastpathp;
604538623fSlukem const char *tilde;		/* "~user" if not expanding tilde, else "" */
6161f28255Scgd char	*tpathp;
6261f28255Scgd int	nleft;
6361f28255Scgd 
6461f28255Scgd int	expany;		/* any expansions done? */
6561f28255Scgd char	*entp;
6661f28255Scgd char	**sortbase;
6761f28255Scgd 
6861f28255Scgd #define sort()	qsort((char *)sortbase, &eargv[eargc] - sortbase, \
6961f28255Scgd 		      sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
7061f28255Scgd 
714538623fSlukem static void	Cat(const char *, const char *);
72a9b4ca62Swiz static void	addpath(int);
73a9b4ca62Swiz static int	amatch(char *, char *);
74a9b4ca62Swiz static int	argcmp(const void *, const void *);
75a9b4ca62Swiz static int	execbrc(char *, char *);
76a9b4ca62Swiz static void	expsh(char *);
77a9b4ca62Swiz static void	expstr(char *);
78a9b4ca62Swiz static int	match(char *, char *);
79a9b4ca62Swiz static void	matchdir(char *);
80a5bfdf78Scgd 
8161f28255Scgd /*
8261f28255Scgd  * Take a list of names and expand any macros, etc.
8361f28255Scgd  * wh = E_VARS if expanding variables.
8461f28255Scgd  * wh = E_SHELL if expanding shell characters.
8561f28255Scgd  * wh = E_TILDE if expanding `~'.
8661f28255Scgd  * or any of these or'ed together.
8761f28255Scgd  *
8861f28255Scgd  * Major portions of this were snarfed from csh/sh.glob.c.
8961f28255Scgd  */
9061f28255Scgd struct namelist *
expand(struct namelist * list,int wh)91a9b4ca62Swiz expand(struct namelist *list, int wh)
9261f28255Scgd {
933b3cf635Slukem 	struct namelist *nl, *prev;
943b3cf635Slukem 	int n;
9561f28255Scgd 	char pathbuf[BUFSIZ];
9661f28255Scgd 	char *argvbuf[GAVSIZ];
9761f28255Scgd 
9861f28255Scgd 	if (debug) {
993b3cf635Slukem 		printf("expand(%lx, %d)\nlist = ", (long)list, wh);
10061f28255Scgd 		prnames(list);
10161f28255Scgd 	}
10261f28255Scgd 
10361f28255Scgd 	if (wh == 0) {
1043b3cf635Slukem 		char *cp;
10561f28255Scgd 
10661f28255Scgd 		for (nl = list; nl != NULL; nl = nl->n_next)
10761f28255Scgd 			for (cp = nl->n_name; *cp; cp++)
10861f28255Scgd 				*cp = *cp & TRIM;
10961f28255Scgd 		return(list);
11061f28255Scgd 	}
11161f28255Scgd 
11261f28255Scgd 	which = wh;
11361f28255Scgd 	path = tpathp = pathp = pathbuf;
11461f28255Scgd 	*pathp = '\0';
11561f28255Scgd 	lastpathp = &path[sizeof pathbuf - 2];
11661f28255Scgd 	tilde = "";
11761f28255Scgd 	eargc = 0;
11861f28255Scgd 	eargv = sortbase = argvbuf;
11961f28255Scgd 	*eargv = 0;
12061f28255Scgd 	nleft = NCARGS - 4;
12161f28255Scgd 	/*
12261f28255Scgd 	 * Walk the name list and expand names into eargv[];
12361f28255Scgd 	 */
12461f28255Scgd 	for (nl = list; nl != NULL; nl = nl->n_next)
12561f28255Scgd 		expstr(nl->n_name);
12661f28255Scgd 	/*
12761f28255Scgd 	 * Take expanded list of names from eargv[] and build a new list.
12861f28255Scgd 	 */
12961f28255Scgd 	list = prev = NULL;
13061f28255Scgd 	for (n = 0; n < eargc; n++) {
13161f28255Scgd 		nl = makenl(NULL);
13261f28255Scgd 		nl->n_name = eargv[n];
13361f28255Scgd 		if (prev == NULL)
13461f28255Scgd 			list = prev = nl;
13561f28255Scgd 		else {
13661f28255Scgd 			prev->n_next = nl;
13761f28255Scgd 			prev = nl;
13861f28255Scgd 		}
13961f28255Scgd 	}
14061f28255Scgd 	if (debug) {
14161f28255Scgd 		printf("expanded list = ");
14261f28255Scgd 		prnames(list);
14361f28255Scgd 	}
144*2bd8f802Smrg 	sortbase = NULL;
145*2bd8f802Smrg 	eargv = NULL;
146*2bd8f802Smrg 	path = tpathp = pathp = NULL;
147*2bd8f802Smrg 	lastpathp = NULL;
14861f28255Scgd 	return(list);
14961f28255Scgd }
15061f28255Scgd 
151a5bfdf78Scgd static void
expstr(char * s)152a9b4ca62Swiz expstr(char *s)
15361f28255Scgd {
1543b3cf635Slukem 	char *cp, *cp1;
1553b3cf635Slukem 	struct namelist *tp;
15661f28255Scgd 	char *tail;
1574538623fSlukem 	char expbuf[BUFSIZ];
15861f28255Scgd 	int savec, oeargc;
15961f28255Scgd 	extern char homedir[];
16061f28255Scgd 
16161f28255Scgd 	if (s == NULL || *s == '\0')
16261f28255Scgd 		return;
16361f28255Scgd 
1643b3cf635Slukem 	if ((which & E_VARS) && (cp = strchr(s, '$')) != NULL) {
16561f28255Scgd 		*cp++ = '\0';
16661f28255Scgd 		if (*cp == '\0') {
16761f28255Scgd 			yyerror("no variable name after '$'");
16861f28255Scgd 			return;
16961f28255Scgd 		}
17061f28255Scgd 		if (*cp == LC) {
17161f28255Scgd 			cp++;
1723b3cf635Slukem 			if ((tail = strchr(cp, RC)) == NULL) {
17361f28255Scgd 				yyerror("unmatched '{'");
17461f28255Scgd 				return;
17561f28255Scgd 			}
17661f28255Scgd 			*tail++ = savec = '\0';
17761f28255Scgd 			if (*cp == '\0') {
17861f28255Scgd 				yyerror("no variable name after '$'");
17961f28255Scgd 				return;
18061f28255Scgd 			}
18161f28255Scgd 		} else {
18261f28255Scgd 			tail = cp + 1;
18361f28255Scgd 			savec = *tail;
18461f28255Scgd 			*tail = '\0';
18561f28255Scgd 		}
186b55c3729Spk 		tp = lookup(cp, 0, 0);
18761f28255Scgd 		if (savec != '\0')
18861f28255Scgd 			*tail = savec;
18961f28255Scgd 		if (tp != NULL) {
19061f28255Scgd 			for (; tp != NULL; tp = tp->n_next) {
1914538623fSlukem 				snprintf(expbuf, sizeof(expbuf), "%s%s%s", s,
192336eeb5fSthorpej 				    tp->n_name, tail);
1934538623fSlukem 				expstr(expbuf);
19461f28255Scgd 			}
19561f28255Scgd 			return;
19661f28255Scgd 		}
1974538623fSlukem 		snprintf(expbuf, sizeof(expbuf), "%s%s", s, tail);
1984538623fSlukem 		expstr(expbuf);
19961f28255Scgd 		return;
20061f28255Scgd 	}
20161f28255Scgd 	if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
20261f28255Scgd 		Cat(s, "");
20361f28255Scgd 		sort();
20461f28255Scgd 		return;
20561f28255Scgd 	}
20661f28255Scgd 	if (*s == '~') {
20761f28255Scgd 		cp = ++s;
20861f28255Scgd 		if (*cp == '\0' || *cp == '/') {
20961f28255Scgd 			tilde = "~";
21061f28255Scgd 			cp1 = homedir;
21161f28255Scgd 		} else {
2124538623fSlukem 			tilde = cp1 = expbuf;
21361f28255Scgd 			*cp1++ = '~';
21461f28255Scgd 			do
21561f28255Scgd 				*cp1++ = *cp++;
21661f28255Scgd 			while (*cp && *cp != '/');
21761f28255Scgd 			*cp1 = '\0';
2184538623fSlukem 			if (pw == NULL || strcmp(pw->pw_name, expbuf+1) != 0) {
2194538623fSlukem 				if ((pw = getpwnam(expbuf+1)) == NULL) {
2204538623fSlukem 					strlcat(expbuf, ": unknown user name",
2214538623fSlukem 					    sizeof(expbuf));
2224538623fSlukem 					yyerror(expbuf+1);
22361f28255Scgd 					return;
22461f28255Scgd 				}
22561f28255Scgd 			}
22661f28255Scgd 			cp1 = pw->pw_dir;
22761f28255Scgd 			s = cp;
22861f28255Scgd 		}
2293b3cf635Slukem 		for (cp = path; (*cp++ = *cp1++) != 0; )
23061f28255Scgd 			;
23161f28255Scgd 		tpathp = pathp = cp - 1;
23261f28255Scgd 	} else {
23361f28255Scgd 		tpathp = pathp = path;
23461f28255Scgd 		tilde = "";
23561f28255Scgd 	}
23661f28255Scgd 	*pathp = '\0';
23761f28255Scgd 	if (!(which & E_SHELL)) {
23861f28255Scgd 		if (which & E_TILDE)
23961f28255Scgd 			Cat(path, s);
24061f28255Scgd 		else
24161f28255Scgd 			Cat(tilde, s);
24261f28255Scgd 		sort();
24361f28255Scgd 		return;
24461f28255Scgd 	}
24561f28255Scgd 	oeargc = eargc;
24661f28255Scgd 	expany = 0;
24761f28255Scgd 	expsh(s);
24861f28255Scgd 	if (eargc == oeargc)
24961f28255Scgd 		Cat(s, "");		/* "nonomatch" is set */
25061f28255Scgd 	sort();
25161f28255Scgd }
25261f28255Scgd 
253a5bfdf78Scgd static int
argcmp(const void * a1,const void * a2)254a9b4ca62Swiz argcmp(const void *a1, const void *a2)
25561f28255Scgd {
25661f28255Scgd 
2574538623fSlukem 	return (strcmp(*(const char * const *)a1, *(const char * const *)a2));
25861f28255Scgd }
25961f28255Scgd 
26061f28255Scgd /*
26161f28255Scgd  * If there are any Shell meta characters in the name,
26261f28255Scgd  * expand into a list, after searching directory
26361f28255Scgd  */
264a5bfdf78Scgd static void
expsh(char * s)265a9b4ca62Swiz expsh(char *s)
26661f28255Scgd {
2673b3cf635Slukem 	char *cp;
2683b3cf635Slukem 	char *spathp, *oldcp;
26961f28255Scgd 	struct stat stb;
27061f28255Scgd 
27161f28255Scgd 	spathp = pathp;
27261f28255Scgd 	cp = s;
27361f28255Scgd 	while (!any(*cp, shchars)) {
27461f28255Scgd 		if (*cp == '\0') {
27561f28255Scgd 			if (!expany || stat(path, &stb) >= 0) {
27661f28255Scgd 				if (which & E_TILDE)
27761f28255Scgd 					Cat(path, "");
27861f28255Scgd 				else
27961f28255Scgd 					Cat(tilde, tpathp);
28061f28255Scgd 			}
28161f28255Scgd 			goto endit;
28261f28255Scgd 		}
28361f28255Scgd 		addpath(*cp++);
28461f28255Scgd 	}
28561f28255Scgd 	oldcp = cp;
28661f28255Scgd 	while (cp > s && *cp != '/')
28761f28255Scgd 		cp--, pathp--;
28861f28255Scgd 	if (*cp == '/')
28961f28255Scgd 		cp++, pathp++;
29061f28255Scgd 	*pathp = '\0';
29161f28255Scgd 	if (*oldcp == '{') {
29261f28255Scgd 		execbrc(cp, NULL);
29361f28255Scgd 		return;
29461f28255Scgd 	}
29561f28255Scgd 	matchdir(cp);
29661f28255Scgd endit:
29761f28255Scgd 	pathp = spathp;
29861f28255Scgd 	*pathp = '\0';
29961f28255Scgd }
30061f28255Scgd 
301a5bfdf78Scgd static void
matchdir(char * pattern)302a9b4ca62Swiz matchdir(char *pattern)
30361f28255Scgd {
30461f28255Scgd 	struct stat stb;
305b751ad2cSchristos 	struct dirent *dp;
30661f28255Scgd 	DIR *dirp;
30761f28255Scgd 
30861f28255Scgd 	dirp = opendir(path);
30961f28255Scgd 	if (dirp == NULL) {
31061f28255Scgd 		if (expany)
31161f28255Scgd 			return;
31261f28255Scgd 		goto patherr2;
31361f28255Scgd 	}
31461f28255Scgd 	if (fstat(dirp->dd_fd, &stb) < 0)
31561f28255Scgd 		goto patherr1;
316004f2550Smycroft 	if (!S_ISDIR(stb.st_mode)) {
31761f28255Scgd 		errno = ENOTDIR;
31861f28255Scgd 		goto patherr1;
31961f28255Scgd 	}
32061f28255Scgd 	while ((dp = readdir(dirp)) != NULL)
32161f28255Scgd 		if (match(dp->d_name, pattern)) {
32261f28255Scgd 			if (which & E_TILDE)
32361f28255Scgd 				Cat(path, dp->d_name);
32461f28255Scgd 			else {
32561f28255Scgd 				strcpy(pathp, dp->d_name);
32661f28255Scgd 				Cat(tilde, tpathp);
32761f28255Scgd 				*pathp = '\0';
32861f28255Scgd 			}
32961f28255Scgd 		}
33061f28255Scgd 	closedir(dirp);
33161f28255Scgd 	return;
33261f28255Scgd 
33361f28255Scgd patherr1:
33461f28255Scgd 	closedir(dirp);
33561f28255Scgd patherr2:
33661f28255Scgd 	strcat(path, ": ");
33761f28255Scgd 	strcat(path, strerror(errno));
33861f28255Scgd 	yyerror(path);
33961f28255Scgd }
34061f28255Scgd 
341a5bfdf78Scgd static int
execbrc(char * p,char * s)342a9b4ca62Swiz execbrc(char *p, char *s)
34361f28255Scgd {
34461f28255Scgd 	char restbuf[BUFSIZ + 2];
3453b3cf635Slukem 	char *pe, *pm, *pl;
34661f28255Scgd 	int brclev = 0;
34761f28255Scgd 	char *lm, savec, *spathp;
34861f28255Scgd 
34961f28255Scgd 	for (lm = restbuf; *p != '{'; *lm++ = *p++)
35061f28255Scgd 		continue;
35161f28255Scgd 	for (pe = ++p; *pe; pe++)
35261f28255Scgd 		switch (*pe) {
35361f28255Scgd 
35461f28255Scgd 		case '{':
35561f28255Scgd 			brclev++;
35661f28255Scgd 			continue;
35761f28255Scgd 
35861f28255Scgd 		case '}':
35961f28255Scgd 			if (brclev == 0)
36061f28255Scgd 				goto pend;
36161f28255Scgd 			brclev--;
36261f28255Scgd 			continue;
36361f28255Scgd 
36461f28255Scgd 		case '[':
36561f28255Scgd 			for (pe++; *pe && *pe != ']'; pe++)
36661f28255Scgd 				continue;
36761f28255Scgd 			if (!*pe)
36861f28255Scgd 				yyerror("Missing ']'");
36961f28255Scgd 			continue;
37061f28255Scgd 		}
37161f28255Scgd pend:
37261f28255Scgd 	if (brclev || !*pe) {
37361f28255Scgd 		yyerror("Missing '}'");
37461f28255Scgd 		return (0);
37561f28255Scgd 	}
37661f28255Scgd 	for (pl = pm = p; pm <= pe; pm++)
37761f28255Scgd 		switch (*pm & (QUOTE|TRIM)) {
37861f28255Scgd 
37961f28255Scgd 		case '{':
38061f28255Scgd 			brclev++;
38161f28255Scgd 			continue;
38261f28255Scgd 
38361f28255Scgd 		case '}':
38461f28255Scgd 			if (brclev) {
38561f28255Scgd 				brclev--;
38661f28255Scgd 				continue;
38761f28255Scgd 			}
38861f28255Scgd 			goto doit;
38961f28255Scgd 
39061f28255Scgd 		case ',':
39161f28255Scgd 			if (brclev)
39261f28255Scgd 				continue;
39361f28255Scgd doit:
39461f28255Scgd 			savec = *pm;
39561f28255Scgd 			*pm = 0;
396e97c220eSitojun 			strlcpy(lm, pl, sizeof(restbuf) - (lm - restbuf));
397e97c220eSitojun 			strlcat(restbuf, pe + 1, sizeof(restbuf));
39861f28255Scgd 			*pm = savec;
39961f28255Scgd 			if (s == 0) {
40061f28255Scgd 				spathp = pathp;
40161f28255Scgd 				expsh(restbuf);
40261f28255Scgd 				pathp = spathp;
40361f28255Scgd 				*pathp = 0;
40461f28255Scgd 			} else if (amatch(s, restbuf))
40561f28255Scgd 				return (1);
40661f28255Scgd 			sort();
40761f28255Scgd 			pl = pm + 1;
40861f28255Scgd 			continue;
40961f28255Scgd 
41061f28255Scgd 		case '[':
41161f28255Scgd 			for (pm++; *pm && *pm != ']'; pm++)
41261f28255Scgd 				continue;
41361f28255Scgd 			if (!*pm)
41461f28255Scgd 				yyerror("Missing ']'");
41561f28255Scgd 			continue;
41661f28255Scgd 		}
41761f28255Scgd 	return (0);
41861f28255Scgd }
41961f28255Scgd 
420a5bfdf78Scgd static int
match(char * s,char * p)421a9b4ca62Swiz match(char *s, char *p)
42261f28255Scgd {
4233b3cf635Slukem 	int c;
4243b3cf635Slukem 	char *sentp;
42561f28255Scgd 	char sexpany = expany;
42661f28255Scgd 
42761f28255Scgd 	if (*s == '.' && *p != '.')
42861f28255Scgd 		return (0);
42961f28255Scgd 	sentp = entp;
43061f28255Scgd 	entp = s;
43161f28255Scgd 	c = amatch(s, p);
43261f28255Scgd 	entp = sentp;
43361f28255Scgd 	expany = sexpany;
43461f28255Scgd 	return (c);
43561f28255Scgd }
43661f28255Scgd 
437a5bfdf78Scgd static int
amatch(char * s,char * p)438a9b4ca62Swiz amatch(char *s, char *p)
43961f28255Scgd {
4403b3cf635Slukem 	int scc;
44161f28255Scgd 	int ok, lc;
44261f28255Scgd 	char *spathp;
44361f28255Scgd 	struct stat stb;
44461f28255Scgd 	int c, cc;
44561f28255Scgd 
44661f28255Scgd 	expany = 1;
44761f28255Scgd 	for (;;) {
44861f28255Scgd 		scc = *s++ & TRIM;
44961f28255Scgd 		switch (c = *p++) {
45061f28255Scgd 
45161f28255Scgd 		case '{':
45261f28255Scgd 			return (execbrc(p - 1, s - 1));
45361f28255Scgd 
45461f28255Scgd 		case '[':
45561f28255Scgd 			ok = 0;
45661f28255Scgd 			lc = 077777;
4573b3cf635Slukem 			while ((cc = *p++) != 0) {
45861f28255Scgd 				if (cc == ']') {
45961f28255Scgd 					if (ok)
46061f28255Scgd 						break;
46161f28255Scgd 					return (0);
46261f28255Scgd 				}
46361f28255Scgd 				if (cc == '-') {
46461f28255Scgd 					if (lc <= scc && scc <= *p++)
46561f28255Scgd 						ok++;
46661f28255Scgd 				} else
46761f28255Scgd 					if (scc == (lc = cc))
46861f28255Scgd 						ok++;
46961f28255Scgd 			}
47061f28255Scgd 			if (cc == 0) {
47161f28255Scgd 				yyerror("Missing ']'");
47261f28255Scgd 				return (0);
47361f28255Scgd 			}
47461f28255Scgd 			continue;
47561f28255Scgd 
47661f28255Scgd 		case '*':
47761f28255Scgd 			if (!*p)
47861f28255Scgd 				return (1);
47961f28255Scgd 			if (*p == '/') {
48061f28255Scgd 				p++;
48161f28255Scgd 				goto slash;
48261f28255Scgd 			}
48361f28255Scgd 			for (s--; *s; s++)
48461f28255Scgd 				if (amatch(s, p))
48561f28255Scgd 					return (1);
48661f28255Scgd 			return (0);
48761f28255Scgd 
48861f28255Scgd 		case '\0':
48961f28255Scgd 			return (scc == '\0');
49061f28255Scgd 
49161f28255Scgd 		default:
49261f28255Scgd 			if ((c & TRIM) != scc)
49361f28255Scgd 				return (0);
49461f28255Scgd 			continue;
49561f28255Scgd 
49661f28255Scgd 		case '?':
49761f28255Scgd 			if (scc == '\0')
49861f28255Scgd 				return (0);
49961f28255Scgd 			continue;
50061f28255Scgd 
50161f28255Scgd 		case '/':
50261f28255Scgd 			if (scc)
50361f28255Scgd 				return (0);
50461f28255Scgd slash:
50561f28255Scgd 			s = entp;
50661f28255Scgd 			spathp = pathp;
50761f28255Scgd 			while (*s)
50861f28255Scgd 				addpath(*s++);
50961f28255Scgd 			addpath('/');
510f670fa10Sross 			if (stat(path, &stb) == 0 && S_ISDIR(stb.st_mode)) {
51161f28255Scgd 				if (*p == '\0') {
51261f28255Scgd 					if (which & E_TILDE)
51361f28255Scgd 						Cat(path, "");
51461f28255Scgd 					else
51561f28255Scgd 						Cat(tilde, tpathp);
51661f28255Scgd 				} else
51761f28255Scgd 					expsh(p);
518f670fa10Sross 			}
51961f28255Scgd 			pathp = spathp;
52061f28255Scgd 			*pathp = '\0';
52161f28255Scgd 			return (0);
52261f28255Scgd 		}
52361f28255Scgd 	}
52461f28255Scgd }
52561f28255Scgd 
526a5bfdf78Scgd static void
Cat(const char * s1,const char * s2)5274538623fSlukem Cat(const char *s1, const char *s2)
52861f28255Scgd {
52961f28255Scgd 	int len = strlen(s1) + strlen(s2) + 1;
5303b3cf635Slukem 	char *s;
53161f28255Scgd 
53261f28255Scgd 	nleft -= len;
53361f28255Scgd 	if (nleft <= 0 || ++eargc >= GAVSIZ)
53461f28255Scgd 		yyerror("Arguments too long");
53561f28255Scgd 	eargv[eargc] = 0;
53661f28255Scgd 	eargv[eargc - 1] = s = malloc(len);
53761f28255Scgd 	if (s == NULL)
53861f28255Scgd 		fatal("ran out of memory\n");
5393b3cf635Slukem 	while ((*s++ = *s1++ & TRIM) != 0)
54061f28255Scgd 		;
54161f28255Scgd 	s--;
5423b3cf635Slukem 	while ((*s++ = *s2++ & TRIM) != 0)
54361f28255Scgd 		;
54461f28255Scgd }
54561f28255Scgd 
546a5bfdf78Scgd static void
addpath(int c)547a9b4ca62Swiz addpath(int c)
54861f28255Scgd {
54961f28255Scgd 
55061f28255Scgd 	if (pathp >= lastpathp)
55161f28255Scgd 		yyerror("Pathname too long");
55261f28255Scgd 	else {
55361f28255Scgd 		*pathp++ = c & TRIM;
55461f28255Scgd 		*pathp = '\0';
55561f28255Scgd 	}
55661f28255Scgd }
55761f28255Scgd 
55861f28255Scgd /*
55961f28255Scgd  * Expand file names beginning with `~' into the
56061f28255Scgd  * user's home directory path name. Return a pointer in buf to the
56161f28255Scgd  * part corresponding to `file'.
56261f28255Scgd  */
56361f28255Scgd char *
exptilde(char * expbuf,char * file)5644538623fSlukem exptilde(char *expbuf, char *file)
56561f28255Scgd {
5663b3cf635Slukem 	char *s1, *s2, *s3;
56761f28255Scgd 	extern char homedir[];
56861f28255Scgd 
56961f28255Scgd 	if (*file != '~') {
5704538623fSlukem 		strcpy(expbuf, file);
5714538623fSlukem 		return(expbuf);
57261f28255Scgd 	}
57361f28255Scgd 	if (*++file == '\0') {
57461f28255Scgd 		s2 = homedir;
57561f28255Scgd 		s3 = NULL;
57661f28255Scgd 	} else if (*file == '/') {
57761f28255Scgd 		s2 = homedir;
57861f28255Scgd 		s3 = file;
57961f28255Scgd 	} else {
58061f28255Scgd 		s3 = file;
58161f28255Scgd 		while (*s3 && *s3 != '/')
58261f28255Scgd 			s3++;
58361f28255Scgd 		if (*s3 == '/')
58461f28255Scgd 			*s3 = '\0';
58561f28255Scgd 		else
58661f28255Scgd 			s3 = NULL;
58761f28255Scgd 		if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
58861f28255Scgd 			if ((pw = getpwnam(file)) == NULL) {
58961f28255Scgd 				error("%s: unknown user name\n", file);
59061f28255Scgd 				if (s3 != NULL)
59161f28255Scgd 					*s3 = '/';
59261f28255Scgd 				return(NULL);
59361f28255Scgd 			}
59461f28255Scgd 		}
59561f28255Scgd 		if (s3 != NULL)
59661f28255Scgd 			*s3 = '/';
59761f28255Scgd 		s2 = pw->pw_dir;
59861f28255Scgd 	}
5994538623fSlukem 	for (s1 = expbuf; (*s1++ = *s2++) != 0; )
60061f28255Scgd 		;
60161f28255Scgd 	s2 = --s1;
60261f28255Scgd 	if (s3 != NULL) {
60361f28255Scgd 		s2++;
6043b3cf635Slukem 		while ((*s1++ = *s3++) != 0)
60561f28255Scgd 			;
60661f28255Scgd 	}
60761f28255Scgd 	return(s2);
60861f28255Scgd }
609