1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1983 Regents of the University of California.
3*7c478bd9Sstevel@tonic-gate  * All rights reserved.
4*7c478bd9Sstevel@tonic-gate  *
5*7c478bd9Sstevel@tonic-gate  * Redistribution and use in source and binary forms are permitted
6*7c478bd9Sstevel@tonic-gate  * provided that the above copyright notice and this paragraph are
7*7c478bd9Sstevel@tonic-gate  * duplicated in all such forms and that any documentation,
8*7c478bd9Sstevel@tonic-gate  * advertising materials, and other materials related to such
9*7c478bd9Sstevel@tonic-gate  * distribution and use acknowledge that the software was developed
10*7c478bd9Sstevel@tonic-gate  * by the University of California, Berkeley.  The name of the
11*7c478bd9Sstevel@tonic-gate  * University may not be used to endorse or promote products derived
12*7c478bd9Sstevel@tonic-gate  * from this software without specific prior written permission.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1998, by Sun Microsystems, Inc.
15*7c478bd9Sstevel@tonic-gate  * All rights reserved.
16*7c478bd9Sstevel@tonic-gate  */
17*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
18*7c478bd9Sstevel@tonic-gate 
19*7c478bd9Sstevel@tonic-gate #include "defs.h"
20*7c478bd9Sstevel@tonic-gate #include <string.h>
21*7c478bd9Sstevel@tonic-gate 
22*7c478bd9Sstevel@tonic-gate #define	GAVSIZ	NCARGS / 6
23*7c478bd9Sstevel@tonic-gate #define	LC '{'
24*7c478bd9Sstevel@tonic-gate #define	RC '}'
25*7c478bd9Sstevel@tonic-gate 
26*7c478bd9Sstevel@tonic-gate static char	shchars[] = "${[*?";
27*7c478bd9Sstevel@tonic-gate 
28*7c478bd9Sstevel@tonic-gate int	which;		/* bit mask of types to expand */
29*7c478bd9Sstevel@tonic-gate int	eargc;		/* expanded arg count */
30*7c478bd9Sstevel@tonic-gate char	**eargv;	/* expanded arg vectors */
31*7c478bd9Sstevel@tonic-gate char	*path;
32*7c478bd9Sstevel@tonic-gate char	*pathp;
33*7c478bd9Sstevel@tonic-gate char	*lastpathp;
34*7c478bd9Sstevel@tonic-gate char	*tilde;		/* "~user" if not expanding tilde, else "" */
35*7c478bd9Sstevel@tonic-gate char	*tpathp;
36*7c478bd9Sstevel@tonic-gate int	nleft;
37*7c478bd9Sstevel@tonic-gate 
38*7c478bd9Sstevel@tonic-gate int	expany;		/* any expansions done? */
39*7c478bd9Sstevel@tonic-gate char	*entp;
40*7c478bd9Sstevel@tonic-gate char	**sortbase;
41*7c478bd9Sstevel@tonic-gate 
42*7c478bd9Sstevel@tonic-gate char	*index();
43*7c478bd9Sstevel@tonic-gate int	argcmp();
44*7c478bd9Sstevel@tonic-gate 
45*7c478bd9Sstevel@tonic-gate #define	sort()	qsort((char *)sortbase, &eargv[eargc] - sortbase, \
46*7c478bd9Sstevel@tonic-gate 		sizeof (*sortbase), argcmp), sortbase = &eargv[eargc]
47*7c478bd9Sstevel@tonic-gate 
48*7c478bd9Sstevel@tonic-gate #define	MIN(a, b)	((a) < (b) ? (a) : (b))
49*7c478bd9Sstevel@tonic-gate 
50*7c478bd9Sstevel@tonic-gate /*
51*7c478bd9Sstevel@tonic-gate  * Take a list of names and expand any macros, etc.
52*7c478bd9Sstevel@tonic-gate  * wh = E_VARS if expanding variables.
53*7c478bd9Sstevel@tonic-gate  * wh = E_SHELL if expanding shell characters.
54*7c478bd9Sstevel@tonic-gate  * wh = E_TILDE if expanding `~'.
55*7c478bd9Sstevel@tonic-gate  * or any of these or'ed together.
56*7c478bd9Sstevel@tonic-gate  *
57*7c478bd9Sstevel@tonic-gate  * Major portions of this were snarfed from csh/sh.glob.c.
58*7c478bd9Sstevel@tonic-gate  */
59*7c478bd9Sstevel@tonic-gate struct namelist *
60*7c478bd9Sstevel@tonic-gate expand(list, wh)
61*7c478bd9Sstevel@tonic-gate 	struct namelist *list;
62*7c478bd9Sstevel@tonic-gate 	int wh;
63*7c478bd9Sstevel@tonic-gate {
64*7c478bd9Sstevel@tonic-gate 	register struct namelist *nl, *prev;
65*7c478bd9Sstevel@tonic-gate 	register int n;
66*7c478bd9Sstevel@tonic-gate 	char pathbuf[LINESIZE];
67*7c478bd9Sstevel@tonic-gate 	char *argvbuf[GAVSIZ];
68*7c478bd9Sstevel@tonic-gate 
69*7c478bd9Sstevel@tonic-gate 	if (debug) {
70*7c478bd9Sstevel@tonic-gate 		printf("expand(%x, %d)\nlist = ", list, wh);
71*7c478bd9Sstevel@tonic-gate 		prnames(list);
72*7c478bd9Sstevel@tonic-gate 	}
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate 	if (wh == 0) {
75*7c478bd9Sstevel@tonic-gate 		register char *cp;
76*7c478bd9Sstevel@tonic-gate 
77*7c478bd9Sstevel@tonic-gate 		for (nl = list; nl != NULL; nl = nl->n_next)
78*7c478bd9Sstevel@tonic-gate 			for (cp = nl->n_name; *cp; cp++)
79*7c478bd9Sstevel@tonic-gate 				*cp = *cp & TRIM;
80*7c478bd9Sstevel@tonic-gate 		return (list);
81*7c478bd9Sstevel@tonic-gate 	}
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate 	which = wh;
84*7c478bd9Sstevel@tonic-gate 	path = tpathp = pathp = pathbuf;
85*7c478bd9Sstevel@tonic-gate 	*pathp = '\0';
86*7c478bd9Sstevel@tonic-gate 	lastpathp = &path[sizeof pathbuf - 2];
87*7c478bd9Sstevel@tonic-gate 	tilde = "";
88*7c478bd9Sstevel@tonic-gate 	eargc = 0;
89*7c478bd9Sstevel@tonic-gate 	eargv = sortbase = argvbuf;
90*7c478bd9Sstevel@tonic-gate 	*eargv = 0;
91*7c478bd9Sstevel@tonic-gate 	nleft = NCARGS - 4;
92*7c478bd9Sstevel@tonic-gate 	/*
93*7c478bd9Sstevel@tonic-gate 	 * Walk the name list and expand names into eargv[];
94*7c478bd9Sstevel@tonic-gate 	 */
95*7c478bd9Sstevel@tonic-gate 	for (nl = list; nl != NULL; nl = nl->n_next)
96*7c478bd9Sstevel@tonic-gate 		expstr(nl->n_name);
97*7c478bd9Sstevel@tonic-gate 	/*
98*7c478bd9Sstevel@tonic-gate 	 * Take expanded list of names from eargv[] and build a new list.
99*7c478bd9Sstevel@tonic-gate 	 */
100*7c478bd9Sstevel@tonic-gate 	list = prev = NULL;
101*7c478bd9Sstevel@tonic-gate 	for (n = 0; n < eargc; n++) {
102*7c478bd9Sstevel@tonic-gate 		nl = makenl(NULL);
103*7c478bd9Sstevel@tonic-gate 		nl->n_name = eargv[n];
104*7c478bd9Sstevel@tonic-gate 		if (prev == NULL)
105*7c478bd9Sstevel@tonic-gate 			list = prev = nl;
106*7c478bd9Sstevel@tonic-gate 		else {
107*7c478bd9Sstevel@tonic-gate 			prev->n_next = nl;
108*7c478bd9Sstevel@tonic-gate 			prev = nl;
109*7c478bd9Sstevel@tonic-gate 		}
110*7c478bd9Sstevel@tonic-gate 	}
111*7c478bd9Sstevel@tonic-gate 	if (debug) {
112*7c478bd9Sstevel@tonic-gate 		printf("expanded list = ");
113*7c478bd9Sstevel@tonic-gate 		prnames(list);
114*7c478bd9Sstevel@tonic-gate 	}
115*7c478bd9Sstevel@tonic-gate 	return (list);
116*7c478bd9Sstevel@tonic-gate }
117*7c478bd9Sstevel@tonic-gate 
118*7c478bd9Sstevel@tonic-gate expstr(s)
119*7c478bd9Sstevel@tonic-gate 	char *s;
120*7c478bd9Sstevel@tonic-gate {
121*7c478bd9Sstevel@tonic-gate 	register char *cp, *cp1;
122*7c478bd9Sstevel@tonic-gate 	register struct namelist *tp;
123*7c478bd9Sstevel@tonic-gate 	char *tail;
124*7c478bd9Sstevel@tonic-gate 	char buf[LINESIZE];
125*7c478bd9Sstevel@tonic-gate 	int savec, oeargc;
126*7c478bd9Sstevel@tonic-gate 	extern char homedir[];
127*7c478bd9Sstevel@tonic-gate 
128*7c478bd9Sstevel@tonic-gate 	if (s == NULL || *s == '\0')
129*7c478bd9Sstevel@tonic-gate 		return;
130*7c478bd9Sstevel@tonic-gate 
131*7c478bd9Sstevel@tonic-gate 	if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
132*7c478bd9Sstevel@tonic-gate 		*cp++ = '\0';
133*7c478bd9Sstevel@tonic-gate 		if (*cp == '\0') {
134*7c478bd9Sstevel@tonic-gate 			yyerror("no variable name after '$'");
135*7c478bd9Sstevel@tonic-gate 			return;
136*7c478bd9Sstevel@tonic-gate 		}
137*7c478bd9Sstevel@tonic-gate 		if (*cp == LC) {
138*7c478bd9Sstevel@tonic-gate 			cp++;
139*7c478bd9Sstevel@tonic-gate 			if ((tail = index(cp, RC)) == NULL) {
140*7c478bd9Sstevel@tonic-gate 				yyerror("unmatched '{'");
141*7c478bd9Sstevel@tonic-gate 				return;
142*7c478bd9Sstevel@tonic-gate 			}
143*7c478bd9Sstevel@tonic-gate 			*tail++ = savec = '\0';
144*7c478bd9Sstevel@tonic-gate 			if (*cp == '\0') {
145*7c478bd9Sstevel@tonic-gate 				yyerror("no variable name after '$'");
146*7c478bd9Sstevel@tonic-gate 				return;
147*7c478bd9Sstevel@tonic-gate 			}
148*7c478bd9Sstevel@tonic-gate 		} else {
149*7c478bd9Sstevel@tonic-gate 			tail = cp + 1;
150*7c478bd9Sstevel@tonic-gate 			savec = *tail;
151*7c478bd9Sstevel@tonic-gate 			*tail = '\0';
152*7c478bd9Sstevel@tonic-gate 		}
153*7c478bd9Sstevel@tonic-gate 		tp = lookup(cp, NULL, 0);
154*7c478bd9Sstevel@tonic-gate 		if (savec != '\0')
155*7c478bd9Sstevel@tonic-gate 			*tail = savec;
156*7c478bd9Sstevel@tonic-gate 		if (tp != NULL) {
157*7c478bd9Sstevel@tonic-gate 			for (; tp != NULL; tp = tp->n_next) {
158*7c478bd9Sstevel@tonic-gate 				(void) snprintf(buf, sizeof (buf), "%s%s%s", s,
159*7c478bd9Sstevel@tonic-gate 				    tp->n_name, tail);
160*7c478bd9Sstevel@tonic-gate 				expstr(buf);
161*7c478bd9Sstevel@tonic-gate 			}
162*7c478bd9Sstevel@tonic-gate 			return;
163*7c478bd9Sstevel@tonic-gate 		}
164*7c478bd9Sstevel@tonic-gate 		(void) snprintf(buf, sizeof (buf), "%s%s", s, tail);
165*7c478bd9Sstevel@tonic-gate 		expstr(buf);
166*7c478bd9Sstevel@tonic-gate 		return;
167*7c478bd9Sstevel@tonic-gate 	}
168*7c478bd9Sstevel@tonic-gate 	if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
169*7c478bd9Sstevel@tonic-gate 		Cat(s, "");
170*7c478bd9Sstevel@tonic-gate 		sort();
171*7c478bd9Sstevel@tonic-gate 		return;
172*7c478bd9Sstevel@tonic-gate 	}
173*7c478bd9Sstevel@tonic-gate 	if (*s == '~') {
174*7c478bd9Sstevel@tonic-gate 		cp = ++s;
175*7c478bd9Sstevel@tonic-gate 		if (*cp == '\0' || *cp == '/') {
176*7c478bd9Sstevel@tonic-gate 			tilde = "~";
177*7c478bd9Sstevel@tonic-gate 			cp1 = homedir;
178*7c478bd9Sstevel@tonic-gate 		} else {
179*7c478bd9Sstevel@tonic-gate 			tilde = cp1 = buf;
180*7c478bd9Sstevel@tonic-gate 			*cp1++ = '~';
181*7c478bd9Sstevel@tonic-gate 			do {
182*7c478bd9Sstevel@tonic-gate 				if (cp1 >= &buf[sizeof (buf)]) {
183*7c478bd9Sstevel@tonic-gate 					yyerror("User name too long");
184*7c478bd9Sstevel@tonic-gate 					return;
185*7c478bd9Sstevel@tonic-gate 				}
186*7c478bd9Sstevel@tonic-gate 				*cp1++ = *cp++;
187*7c478bd9Sstevel@tonic-gate 			} while (*cp && *cp != '/');
188*7c478bd9Sstevel@tonic-gate 			*cp1 = '\0';
189*7c478bd9Sstevel@tonic-gate 			if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
190*7c478bd9Sstevel@tonic-gate 				if ((pw = getpwnam(buf+1)) == NULL) {
191*7c478bd9Sstevel@tonic-gate 					static char unknown_user[] =
192*7c478bd9Sstevel@tonic-gate 					    ": unknown user name";
193*7c478bd9Sstevel@tonic-gate 
194*7c478bd9Sstevel@tonic-gate 					cp1 = MIN(cp1,
195*7c478bd9Sstevel@tonic-gate 					    &buf[sizeof (buf)] -
196*7c478bd9Sstevel@tonic-gate 					    sizeof (unknown_user));
197*7c478bd9Sstevel@tonic-gate 					strcpy(cp1, unknown_user);
198*7c478bd9Sstevel@tonic-gate 					yyerror(buf+1);
199*7c478bd9Sstevel@tonic-gate 					return;
200*7c478bd9Sstevel@tonic-gate 				}
201*7c478bd9Sstevel@tonic-gate 			}
202*7c478bd9Sstevel@tonic-gate 			cp1 = pw->pw_dir;
203*7c478bd9Sstevel@tonic-gate 			s = cp;
204*7c478bd9Sstevel@tonic-gate 		}
205*7c478bd9Sstevel@tonic-gate 		for (cp = path; cp <= lastpathp + 1 && (*cp++ = *cp1++); )
206*7c478bd9Sstevel@tonic-gate 			;
207*7c478bd9Sstevel@tonic-gate 		tpathp = pathp = cp - 1;
208*7c478bd9Sstevel@tonic-gate 	} else {
209*7c478bd9Sstevel@tonic-gate 		tpathp = pathp = path;
210*7c478bd9Sstevel@tonic-gate 		tilde = "";
211*7c478bd9Sstevel@tonic-gate 	}
212*7c478bd9Sstevel@tonic-gate 	*pathp = '\0';
213*7c478bd9Sstevel@tonic-gate 	if (!(which & E_SHELL)) {
214*7c478bd9Sstevel@tonic-gate 		if (which & E_TILDE)
215*7c478bd9Sstevel@tonic-gate 			Cat(path, s);
216*7c478bd9Sstevel@tonic-gate 		else
217*7c478bd9Sstevel@tonic-gate 			Cat(tilde, s);
218*7c478bd9Sstevel@tonic-gate 		sort();
219*7c478bd9Sstevel@tonic-gate 		return;
220*7c478bd9Sstevel@tonic-gate 	}
221*7c478bd9Sstevel@tonic-gate 	oeargc = eargc;
222*7c478bd9Sstevel@tonic-gate 	expany = 0;
223*7c478bd9Sstevel@tonic-gate 	expsh(s);
224*7c478bd9Sstevel@tonic-gate 	if (eargc == oeargc)
225*7c478bd9Sstevel@tonic-gate 		Cat(s, "");		/* "nonomatch" is set */
226*7c478bd9Sstevel@tonic-gate 	sort();
227*7c478bd9Sstevel@tonic-gate }
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate static
230*7c478bd9Sstevel@tonic-gate argcmp(a1, a2)
231*7c478bd9Sstevel@tonic-gate 	char **a1, **a2;
232*7c478bd9Sstevel@tonic-gate {
233*7c478bd9Sstevel@tonic-gate 
234*7c478bd9Sstevel@tonic-gate 	return (strcmp(*a1, *a2));
235*7c478bd9Sstevel@tonic-gate }
236*7c478bd9Sstevel@tonic-gate 
237*7c478bd9Sstevel@tonic-gate /*
238*7c478bd9Sstevel@tonic-gate  * If there are any Shell meta characters in the name,
239*7c478bd9Sstevel@tonic-gate  * expand into a list, after searching directory
240*7c478bd9Sstevel@tonic-gate  */
241*7c478bd9Sstevel@tonic-gate expsh(s)
242*7c478bd9Sstevel@tonic-gate 	char *s;
243*7c478bd9Sstevel@tonic-gate {
244*7c478bd9Sstevel@tonic-gate 	register char *cp;
245*7c478bd9Sstevel@tonic-gate 	register char *spathp, *oldcp;
246*7c478bd9Sstevel@tonic-gate 	struct stat stb;
247*7c478bd9Sstevel@tonic-gate 
248*7c478bd9Sstevel@tonic-gate 	spathp = pathp;
249*7c478bd9Sstevel@tonic-gate 	cp = s;
250*7c478bd9Sstevel@tonic-gate 	while (!any(*cp, shchars)) {
251*7c478bd9Sstevel@tonic-gate 		if (*cp == '\0') {
252*7c478bd9Sstevel@tonic-gate 			if (!expany || stat(path, &stb) >= 0) {
253*7c478bd9Sstevel@tonic-gate 				if (which & E_TILDE)
254*7c478bd9Sstevel@tonic-gate 					Cat(path, "");
255*7c478bd9Sstevel@tonic-gate 				else
256*7c478bd9Sstevel@tonic-gate 					Cat(tilde, tpathp);
257*7c478bd9Sstevel@tonic-gate 			}
258*7c478bd9Sstevel@tonic-gate 			goto endit;
259*7c478bd9Sstevel@tonic-gate 		}
260*7c478bd9Sstevel@tonic-gate 		addpath(*cp++);
261*7c478bd9Sstevel@tonic-gate 	}
262*7c478bd9Sstevel@tonic-gate 	oldcp = cp;
263*7c478bd9Sstevel@tonic-gate 	while (cp > s && *cp != '/')
264*7c478bd9Sstevel@tonic-gate 		cp--, pathp--;
265*7c478bd9Sstevel@tonic-gate 	if (*cp == '/')
266*7c478bd9Sstevel@tonic-gate 		cp++, pathp++;
267*7c478bd9Sstevel@tonic-gate 	*pathp = '\0';
268*7c478bd9Sstevel@tonic-gate 	if (*oldcp == '{') {
269*7c478bd9Sstevel@tonic-gate 		execbrc(cp, NULL);
270*7c478bd9Sstevel@tonic-gate 		return;
271*7c478bd9Sstevel@tonic-gate 	}
272*7c478bd9Sstevel@tonic-gate 	matchdir(cp);
273*7c478bd9Sstevel@tonic-gate endit:
274*7c478bd9Sstevel@tonic-gate 	pathp = spathp;
275*7c478bd9Sstevel@tonic-gate 	*pathp = '\0';
276*7c478bd9Sstevel@tonic-gate }
277*7c478bd9Sstevel@tonic-gate 
278*7c478bd9Sstevel@tonic-gate matchdir(pattern)
279*7c478bd9Sstevel@tonic-gate 	char *pattern;
280*7c478bd9Sstevel@tonic-gate {
281*7c478bd9Sstevel@tonic-gate 	struct stat stb;
282*7c478bd9Sstevel@tonic-gate 	register struct dirent *dp;
283*7c478bd9Sstevel@tonic-gate 	DIR *dirp;
284*7c478bd9Sstevel@tonic-gate 
285*7c478bd9Sstevel@tonic-gate 	dirp = opendir(path);
286*7c478bd9Sstevel@tonic-gate 	if (dirp == NULL) {
287*7c478bd9Sstevel@tonic-gate 		if (expany)
288*7c478bd9Sstevel@tonic-gate 			return;
289*7c478bd9Sstevel@tonic-gate 		goto patherr2;
290*7c478bd9Sstevel@tonic-gate 	}
291*7c478bd9Sstevel@tonic-gate 	if (fstat(dirp->dd_fd, &stb) < 0)
292*7c478bd9Sstevel@tonic-gate 		goto patherr1;
293*7c478bd9Sstevel@tonic-gate 	if (!ISDIR(stb.st_mode)) {
294*7c478bd9Sstevel@tonic-gate 		errno = ENOTDIR;
295*7c478bd9Sstevel@tonic-gate 		goto patherr1;
296*7c478bd9Sstevel@tonic-gate 	}
297*7c478bd9Sstevel@tonic-gate 	while ((dp = readdir(dirp)) != NULL)
298*7c478bd9Sstevel@tonic-gate 		if (match(dp->d_name, pattern)) {
299*7c478bd9Sstevel@tonic-gate 			if (which & E_TILDE)
300*7c478bd9Sstevel@tonic-gate 				Cat(path, dp->d_name);
301*7c478bd9Sstevel@tonic-gate 			else {
302*7c478bd9Sstevel@tonic-gate 				if (pathp + strlen(dp->d_name) - 1 >
303*7c478bd9Sstevel@tonic-gate 				    lastpathp) {
304*7c478bd9Sstevel@tonic-gate 					errno = ENAMETOOLONG;
305*7c478bd9Sstevel@tonic-gate 					goto patherr1;
306*7c478bd9Sstevel@tonic-gate 				}
307*7c478bd9Sstevel@tonic-gate 				strcpy(pathp, dp->d_name);
308*7c478bd9Sstevel@tonic-gate 				Cat(tilde, tpathp);
309*7c478bd9Sstevel@tonic-gate 				*pathp = '\0';
310*7c478bd9Sstevel@tonic-gate 			}
311*7c478bd9Sstevel@tonic-gate 		}
312*7c478bd9Sstevel@tonic-gate 	closedir(dirp);
313*7c478bd9Sstevel@tonic-gate 	return;
314*7c478bd9Sstevel@tonic-gate 
315*7c478bd9Sstevel@tonic-gate patherr1:
316*7c478bd9Sstevel@tonic-gate 	closedir(dirp);
317*7c478bd9Sstevel@tonic-gate patherr2:
318*7c478bd9Sstevel@tonic-gate 	{
319*7c478bd9Sstevel@tonic-gate 		char *strerr = strerror(errno);
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 		if (path + strlen(path) + strlen(strerr) + 1 > lastpathp)
322*7c478bd9Sstevel@tonic-gate 			strcpy(lastpathp - strlen(strerr) - 1, ": ");
323*7c478bd9Sstevel@tonic-gate 		else
324*7c478bd9Sstevel@tonic-gate 			strcat(path, ": ");
325*7c478bd9Sstevel@tonic-gate 		strcat(path, strerr);
326*7c478bd9Sstevel@tonic-gate 	}
327*7c478bd9Sstevel@tonic-gate 	yyerror(path);
328*7c478bd9Sstevel@tonic-gate }
329*7c478bd9Sstevel@tonic-gate 
330*7c478bd9Sstevel@tonic-gate execbrc(p, s)
331*7c478bd9Sstevel@tonic-gate 	char *p, *s;
332*7c478bd9Sstevel@tonic-gate {
333*7c478bd9Sstevel@tonic-gate 	char restbuf[LINESIZE + 2];
334*7c478bd9Sstevel@tonic-gate 	register char *pe, *pm, *pl;
335*7c478bd9Sstevel@tonic-gate 	int brclev = 0;
336*7c478bd9Sstevel@tonic-gate 	char *lm, savec, *spathp;
337*7c478bd9Sstevel@tonic-gate 
338*7c478bd9Sstevel@tonic-gate 	for (lm = restbuf; *p != '{'; *lm++ = *p++) {
339*7c478bd9Sstevel@tonic-gate 		if (lm >= &restbuf[sizeof (restbuf)]) {
340*7c478bd9Sstevel@tonic-gate 			yyerror("Pathname too long");
341*7c478bd9Sstevel@tonic-gate 			return (0);
342*7c478bd9Sstevel@tonic-gate 		}
343*7c478bd9Sstevel@tonic-gate 	}
344*7c478bd9Sstevel@tonic-gate 	for (pe = ++p; *pe; pe++)
345*7c478bd9Sstevel@tonic-gate 		switch (*pe) {
346*7c478bd9Sstevel@tonic-gate 
347*7c478bd9Sstevel@tonic-gate 		case '{':
348*7c478bd9Sstevel@tonic-gate 			brclev++;
349*7c478bd9Sstevel@tonic-gate 			continue;
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate 		case '}':
352*7c478bd9Sstevel@tonic-gate 			if (brclev == 0)
353*7c478bd9Sstevel@tonic-gate 				goto pend;
354*7c478bd9Sstevel@tonic-gate 			brclev--;
355*7c478bd9Sstevel@tonic-gate 			continue;
356*7c478bd9Sstevel@tonic-gate 
357*7c478bd9Sstevel@tonic-gate 		case '[':
358*7c478bd9Sstevel@tonic-gate 			for (pe++; *pe && *pe != ']'; pe++)
359*7c478bd9Sstevel@tonic-gate 				continue;
360*7c478bd9Sstevel@tonic-gate 			if (!*pe)
361*7c478bd9Sstevel@tonic-gate 				yyerror("Missing ']'");
362*7c478bd9Sstevel@tonic-gate 			continue;
363*7c478bd9Sstevel@tonic-gate 		}
364*7c478bd9Sstevel@tonic-gate pend:
365*7c478bd9Sstevel@tonic-gate 	if (brclev || !*pe) {
366*7c478bd9Sstevel@tonic-gate 		yyerror("Missing '}'");
367*7c478bd9Sstevel@tonic-gate 		return (0);
368*7c478bd9Sstevel@tonic-gate 	}
369*7c478bd9Sstevel@tonic-gate 	for (pl = pm = p; pm <= pe; pm++)
370*7c478bd9Sstevel@tonic-gate 		switch (*pm & (QUOTE|TRIM)) {
371*7c478bd9Sstevel@tonic-gate 
372*7c478bd9Sstevel@tonic-gate 		case '{':
373*7c478bd9Sstevel@tonic-gate 			brclev++;
374*7c478bd9Sstevel@tonic-gate 			continue;
375*7c478bd9Sstevel@tonic-gate 
376*7c478bd9Sstevel@tonic-gate 		case '}':
377*7c478bd9Sstevel@tonic-gate 			if (brclev) {
378*7c478bd9Sstevel@tonic-gate 				brclev--;
379*7c478bd9Sstevel@tonic-gate 				continue;
380*7c478bd9Sstevel@tonic-gate 			}
381*7c478bd9Sstevel@tonic-gate 			goto doit;
382*7c478bd9Sstevel@tonic-gate 
383*7c478bd9Sstevel@tonic-gate 		case ',':
384*7c478bd9Sstevel@tonic-gate 			if (brclev)
385*7c478bd9Sstevel@tonic-gate 				continue;
386*7c478bd9Sstevel@tonic-gate doit:
387*7c478bd9Sstevel@tonic-gate 			savec = *pm;
388*7c478bd9Sstevel@tonic-gate 			*pm = 0;
389*7c478bd9Sstevel@tonic-gate 			if (lm + strlen(pl) + strlen(pe + 1) >=
390*7c478bd9Sstevel@tonic-gate 			    &restbuf[sizeof (restbuf)]) {
391*7c478bd9Sstevel@tonic-gate 				yyerror("Pathname too long");
392*7c478bd9Sstevel@tonic-gate 				return (0);
393*7c478bd9Sstevel@tonic-gate 			}
394*7c478bd9Sstevel@tonic-gate 			strcpy(lm, pl);
395*7c478bd9Sstevel@tonic-gate 			strcat(restbuf, pe + 1);
396*7c478bd9Sstevel@tonic-gate 			*pm = savec;
397*7c478bd9Sstevel@tonic-gate 			if (s == 0) {
398*7c478bd9Sstevel@tonic-gate 				spathp = pathp;
399*7c478bd9Sstevel@tonic-gate 				expsh(restbuf);
400*7c478bd9Sstevel@tonic-gate 				pathp = spathp;
401*7c478bd9Sstevel@tonic-gate 				*pathp = 0;
402*7c478bd9Sstevel@tonic-gate 			} else if (amatch(s, restbuf))
403*7c478bd9Sstevel@tonic-gate 				return (1);
404*7c478bd9Sstevel@tonic-gate 			sort();
405*7c478bd9Sstevel@tonic-gate 			pl = pm + 1;
406*7c478bd9Sstevel@tonic-gate 			continue;
407*7c478bd9Sstevel@tonic-gate 
408*7c478bd9Sstevel@tonic-gate 		case '[':
409*7c478bd9Sstevel@tonic-gate 			for (pm++; *pm && *pm != ']'; pm++)
410*7c478bd9Sstevel@tonic-gate 				continue;
411*7c478bd9Sstevel@tonic-gate 			if (!*pm)
412*7c478bd9Sstevel@tonic-gate 				yyerror("Missing ']'");
413*7c478bd9Sstevel@tonic-gate 			continue;
414*7c478bd9Sstevel@tonic-gate 		}
415*7c478bd9Sstevel@tonic-gate 	return (0);
416*7c478bd9Sstevel@tonic-gate }
417*7c478bd9Sstevel@tonic-gate 
418*7c478bd9Sstevel@tonic-gate match(s, p)
419*7c478bd9Sstevel@tonic-gate 	char *s, *p;
420*7c478bd9Sstevel@tonic-gate {
421*7c478bd9Sstevel@tonic-gate 	register int c;
422*7c478bd9Sstevel@tonic-gate 	register char *sentp;
423*7c478bd9Sstevel@tonic-gate 	char sexpany = expany;
424*7c478bd9Sstevel@tonic-gate 
425*7c478bd9Sstevel@tonic-gate 	if (*s == '.' && *p != '.')
426*7c478bd9Sstevel@tonic-gate 		return (0);
427*7c478bd9Sstevel@tonic-gate 	sentp = entp;
428*7c478bd9Sstevel@tonic-gate 	entp = s;
429*7c478bd9Sstevel@tonic-gate 	c = amatch(s, p);
430*7c478bd9Sstevel@tonic-gate 	entp = sentp;
431*7c478bd9Sstevel@tonic-gate 	expany = sexpany;
432*7c478bd9Sstevel@tonic-gate 	return (c);
433*7c478bd9Sstevel@tonic-gate }
434*7c478bd9Sstevel@tonic-gate 
435*7c478bd9Sstevel@tonic-gate amatch(s, p)
436*7c478bd9Sstevel@tonic-gate 	register char *s, *p;
437*7c478bd9Sstevel@tonic-gate {
438*7c478bd9Sstevel@tonic-gate 	register int scc;
439*7c478bd9Sstevel@tonic-gate 	int ok, lc;
440*7c478bd9Sstevel@tonic-gate 	char *spathp;
441*7c478bd9Sstevel@tonic-gate 	struct stat stb;
442*7c478bd9Sstevel@tonic-gate 	int c, cc;
443*7c478bd9Sstevel@tonic-gate 
444*7c478bd9Sstevel@tonic-gate 	expany = 1;
445*7c478bd9Sstevel@tonic-gate 	for (;;) {
446*7c478bd9Sstevel@tonic-gate 		scc = *s++ & TRIM;
447*7c478bd9Sstevel@tonic-gate 		switch (c = *p++) {
448*7c478bd9Sstevel@tonic-gate 
449*7c478bd9Sstevel@tonic-gate 		case '{':
450*7c478bd9Sstevel@tonic-gate 			return (execbrc(p - 1, s - 1));
451*7c478bd9Sstevel@tonic-gate 
452*7c478bd9Sstevel@tonic-gate 		case '[':
453*7c478bd9Sstevel@tonic-gate 			ok = 0;
454*7c478bd9Sstevel@tonic-gate 			lc = 077777;
455*7c478bd9Sstevel@tonic-gate 			while (cc = *p++) {
456*7c478bd9Sstevel@tonic-gate 				if (cc == ']') {
457*7c478bd9Sstevel@tonic-gate 					if (ok)
458*7c478bd9Sstevel@tonic-gate 						break;
459*7c478bd9Sstevel@tonic-gate 					return (0);
460*7c478bd9Sstevel@tonic-gate 				}
461*7c478bd9Sstevel@tonic-gate 				if (cc == '-') {
462*7c478bd9Sstevel@tonic-gate 					if (lc <= scc && scc <= *p++)
463*7c478bd9Sstevel@tonic-gate 						ok++;
464*7c478bd9Sstevel@tonic-gate 				} else
465*7c478bd9Sstevel@tonic-gate 					if (scc == (lc = cc))
466*7c478bd9Sstevel@tonic-gate 						ok++;
467*7c478bd9Sstevel@tonic-gate 			}
468*7c478bd9Sstevel@tonic-gate 			if (cc == 0) {
469*7c478bd9Sstevel@tonic-gate 				yyerror("Missing ']'");
470*7c478bd9Sstevel@tonic-gate 				return (0);
471*7c478bd9Sstevel@tonic-gate 			}
472*7c478bd9Sstevel@tonic-gate 			continue;
473*7c478bd9Sstevel@tonic-gate 
474*7c478bd9Sstevel@tonic-gate 		case '*':
475*7c478bd9Sstevel@tonic-gate 			if (!*p)
476*7c478bd9Sstevel@tonic-gate 				return (1);
477*7c478bd9Sstevel@tonic-gate 			if (*p == '/') {
478*7c478bd9Sstevel@tonic-gate 				p++;
479*7c478bd9Sstevel@tonic-gate 				goto slash;
480*7c478bd9Sstevel@tonic-gate 			}
481*7c478bd9Sstevel@tonic-gate 			for (s--; *s; s++)
482*7c478bd9Sstevel@tonic-gate 				if (amatch(s, p))
483*7c478bd9Sstevel@tonic-gate 					return (1);
484*7c478bd9Sstevel@tonic-gate 			return (0);
485*7c478bd9Sstevel@tonic-gate 
486*7c478bd9Sstevel@tonic-gate 		case '\0':
487*7c478bd9Sstevel@tonic-gate 			return (scc == '\0');
488*7c478bd9Sstevel@tonic-gate 
489*7c478bd9Sstevel@tonic-gate 		default:
490*7c478bd9Sstevel@tonic-gate 			if ((c & TRIM) != scc)
491*7c478bd9Sstevel@tonic-gate 				return (0);
492*7c478bd9Sstevel@tonic-gate 			continue;
493*7c478bd9Sstevel@tonic-gate 
494*7c478bd9Sstevel@tonic-gate 		case '?':
495*7c478bd9Sstevel@tonic-gate 			if (scc == '\0')
496*7c478bd9Sstevel@tonic-gate 				return (0);
497*7c478bd9Sstevel@tonic-gate 			continue;
498*7c478bd9Sstevel@tonic-gate 
499*7c478bd9Sstevel@tonic-gate 		case '/':
500*7c478bd9Sstevel@tonic-gate 			if (scc)
501*7c478bd9Sstevel@tonic-gate 				return (0);
502*7c478bd9Sstevel@tonic-gate slash:
503*7c478bd9Sstevel@tonic-gate 			s = entp;
504*7c478bd9Sstevel@tonic-gate 			spathp = pathp;
505*7c478bd9Sstevel@tonic-gate 			while (*s)
506*7c478bd9Sstevel@tonic-gate 				addpath(*s++);
507*7c478bd9Sstevel@tonic-gate 			addpath('/');
508*7c478bd9Sstevel@tonic-gate 			if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
509*7c478bd9Sstevel@tonic-gate 				if (*p == '\0') {
510*7c478bd9Sstevel@tonic-gate 					if (which & E_TILDE)
511*7c478bd9Sstevel@tonic-gate 						Cat(path, "");
512*7c478bd9Sstevel@tonic-gate 					else
513*7c478bd9Sstevel@tonic-gate 						Cat(tilde, tpathp);
514*7c478bd9Sstevel@tonic-gate 				} else
515*7c478bd9Sstevel@tonic-gate 					expsh(p);
516*7c478bd9Sstevel@tonic-gate 			pathp = spathp;
517*7c478bd9Sstevel@tonic-gate 			*pathp = '\0';
518*7c478bd9Sstevel@tonic-gate 			return (0);
519*7c478bd9Sstevel@tonic-gate 		}
520*7c478bd9Sstevel@tonic-gate 	}
521*7c478bd9Sstevel@tonic-gate }
522*7c478bd9Sstevel@tonic-gate 
523*7c478bd9Sstevel@tonic-gate smatch(s, p)
524*7c478bd9Sstevel@tonic-gate 	register char *s, *p;
525*7c478bd9Sstevel@tonic-gate {
526*7c478bd9Sstevel@tonic-gate 	register int scc;
527*7c478bd9Sstevel@tonic-gate 	int ok, lc;
528*7c478bd9Sstevel@tonic-gate 	int c, cc;
529*7c478bd9Sstevel@tonic-gate 
530*7c478bd9Sstevel@tonic-gate 	for (;;) {
531*7c478bd9Sstevel@tonic-gate 		scc = *s++ & TRIM;
532*7c478bd9Sstevel@tonic-gate 		switch (c = *p++) {
533*7c478bd9Sstevel@tonic-gate 
534*7c478bd9Sstevel@tonic-gate 		case '[':
535*7c478bd9Sstevel@tonic-gate 			ok = 0;
536*7c478bd9Sstevel@tonic-gate 			lc = 077777;
537*7c478bd9Sstevel@tonic-gate 			while (cc = *p++) {
538*7c478bd9Sstevel@tonic-gate 				if (cc == ']') {
539*7c478bd9Sstevel@tonic-gate 					if (ok)
540*7c478bd9Sstevel@tonic-gate 						break;
541*7c478bd9Sstevel@tonic-gate 					return (0);
542*7c478bd9Sstevel@tonic-gate 				}
543*7c478bd9Sstevel@tonic-gate 				if (cc == '-') {
544*7c478bd9Sstevel@tonic-gate 					if (lc <= scc && scc <= *p++)
545*7c478bd9Sstevel@tonic-gate 						ok++;
546*7c478bd9Sstevel@tonic-gate 				} else
547*7c478bd9Sstevel@tonic-gate 					if (scc == (lc = cc))
548*7c478bd9Sstevel@tonic-gate 						ok++;
549*7c478bd9Sstevel@tonic-gate 			}
550*7c478bd9Sstevel@tonic-gate 			if (cc == 0) {
551*7c478bd9Sstevel@tonic-gate 				yyerror("Missing ']'");
552*7c478bd9Sstevel@tonic-gate 				return (0);
553*7c478bd9Sstevel@tonic-gate 			}
554*7c478bd9Sstevel@tonic-gate 			continue;
555*7c478bd9Sstevel@tonic-gate 
556*7c478bd9Sstevel@tonic-gate 		case '*':
557*7c478bd9Sstevel@tonic-gate 			if (!*p)
558*7c478bd9Sstevel@tonic-gate 				return (1);
559*7c478bd9Sstevel@tonic-gate 			for (s--; *s; s++)
560*7c478bd9Sstevel@tonic-gate 				if (smatch(s, p))
561*7c478bd9Sstevel@tonic-gate 					return (1);
562*7c478bd9Sstevel@tonic-gate 			return (0);
563*7c478bd9Sstevel@tonic-gate 
564*7c478bd9Sstevel@tonic-gate 		case '\0':
565*7c478bd9Sstevel@tonic-gate 			return (scc == '\0');
566*7c478bd9Sstevel@tonic-gate 
567*7c478bd9Sstevel@tonic-gate 		default:
568*7c478bd9Sstevel@tonic-gate 			if ((c & TRIM) != scc)
569*7c478bd9Sstevel@tonic-gate 				return (0);
570*7c478bd9Sstevel@tonic-gate 			continue;
571*7c478bd9Sstevel@tonic-gate 
572*7c478bd9Sstevel@tonic-gate 		case '?':
573*7c478bd9Sstevel@tonic-gate 			if (scc == 0)
574*7c478bd9Sstevel@tonic-gate 				return (0);
575*7c478bd9Sstevel@tonic-gate 			continue;
576*7c478bd9Sstevel@tonic-gate 
577*7c478bd9Sstevel@tonic-gate 		}
578*7c478bd9Sstevel@tonic-gate 	}
579*7c478bd9Sstevel@tonic-gate }
580*7c478bd9Sstevel@tonic-gate 
581*7c478bd9Sstevel@tonic-gate Cat(s1, s2)
582*7c478bd9Sstevel@tonic-gate 	register char *s1, *s2;
583*7c478bd9Sstevel@tonic-gate {
584*7c478bd9Sstevel@tonic-gate 	int len = strlen(s1) + strlen(s2) + 1;
585*7c478bd9Sstevel@tonic-gate 	register char *s;
586*7c478bd9Sstevel@tonic-gate 
587*7c478bd9Sstevel@tonic-gate 	nleft -= len;
588*7c478bd9Sstevel@tonic-gate 	if (nleft <= 0 || ++eargc >= GAVSIZ)
589*7c478bd9Sstevel@tonic-gate 		fatal("Arguments too long\n");
590*7c478bd9Sstevel@tonic-gate 	eargv[eargc] = 0;
591*7c478bd9Sstevel@tonic-gate 	eargv[eargc - 1] = s = (char *)malloc(len);
592*7c478bd9Sstevel@tonic-gate 	if (s == NULL)
593*7c478bd9Sstevel@tonic-gate 		fatal("ran out of memory\n");
594*7c478bd9Sstevel@tonic-gate 	while (*s++ = *s1++ & TRIM)
595*7c478bd9Sstevel@tonic-gate 		;
596*7c478bd9Sstevel@tonic-gate 	s--;
597*7c478bd9Sstevel@tonic-gate 	while (*s++ = *s2++ & TRIM)
598*7c478bd9Sstevel@tonic-gate 		;
599*7c478bd9Sstevel@tonic-gate }
600*7c478bd9Sstevel@tonic-gate 
601*7c478bd9Sstevel@tonic-gate addpath(c)
602*7c478bd9Sstevel@tonic-gate 	char c;
603*7c478bd9Sstevel@tonic-gate {
604*7c478bd9Sstevel@tonic-gate 
605*7c478bd9Sstevel@tonic-gate 	if (pathp > lastpathp)
606*7c478bd9Sstevel@tonic-gate 		yyerror("Pathname too long");
607*7c478bd9Sstevel@tonic-gate 	else {
608*7c478bd9Sstevel@tonic-gate 		*pathp++ = c & TRIM;
609*7c478bd9Sstevel@tonic-gate 		*pathp = '\0';
610*7c478bd9Sstevel@tonic-gate 	}
611*7c478bd9Sstevel@tonic-gate }
612*7c478bd9Sstevel@tonic-gate 
613*7c478bd9Sstevel@tonic-gate /*
614*7c478bd9Sstevel@tonic-gate  * Expand file names beginning with `~' into the
615*7c478bd9Sstevel@tonic-gate  * user's home directory path name. Return a pointer in buf to the
616*7c478bd9Sstevel@tonic-gate  * part corresponding to `file'.
617*7c478bd9Sstevel@tonic-gate  */
618*7c478bd9Sstevel@tonic-gate char *
619*7c478bd9Sstevel@tonic-gate exptilde(buf, len, file)
620*7c478bd9Sstevel@tonic-gate 	char buf[];
621*7c478bd9Sstevel@tonic-gate 	unsigned int len;
622*7c478bd9Sstevel@tonic-gate 	register char *file;
623*7c478bd9Sstevel@tonic-gate {
624*7c478bd9Sstevel@tonic-gate 	register char *s1, *s2, *s3;
625*7c478bd9Sstevel@tonic-gate 	extern char homedir[];
626*7c478bd9Sstevel@tonic-gate 
627*7c478bd9Sstevel@tonic-gate 	if (*file != '~') {
628*7c478bd9Sstevel@tonic-gate 		if (strlen(file) + 1 > len) {
629*7c478bd9Sstevel@tonic-gate 			error("pathname too long: %s\n", file);
630*7c478bd9Sstevel@tonic-gate 			return (NULL);
631*7c478bd9Sstevel@tonic-gate 		}
632*7c478bd9Sstevel@tonic-gate 		strcpy(buf, file);
633*7c478bd9Sstevel@tonic-gate 		return (buf);
634*7c478bd9Sstevel@tonic-gate 	}
635*7c478bd9Sstevel@tonic-gate 	if (*++file == '\0') {
636*7c478bd9Sstevel@tonic-gate 		s2 = homedir;
637*7c478bd9Sstevel@tonic-gate 		s3 = NULL;
638*7c478bd9Sstevel@tonic-gate 	} else if (*file == '/') {
639*7c478bd9Sstevel@tonic-gate 		s2 = homedir;
640*7c478bd9Sstevel@tonic-gate 		s3 = file;
641*7c478bd9Sstevel@tonic-gate 	} else {
642*7c478bd9Sstevel@tonic-gate 		s3 = file;
643*7c478bd9Sstevel@tonic-gate 		while (*s3 && *s3 != '/')
644*7c478bd9Sstevel@tonic-gate 			s3++;
645*7c478bd9Sstevel@tonic-gate 		if (*s3 == '/')
646*7c478bd9Sstevel@tonic-gate 			*s3 = '\0';
647*7c478bd9Sstevel@tonic-gate 		else
648*7c478bd9Sstevel@tonic-gate 			s3 = NULL;
649*7c478bd9Sstevel@tonic-gate 		if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
650*7c478bd9Sstevel@tonic-gate 			if ((pw = getpwnam(file)) == NULL) {
651*7c478bd9Sstevel@tonic-gate 				error("%s: unknown user name\n", file);
652*7c478bd9Sstevel@tonic-gate 				if (s3 != NULL)
653*7c478bd9Sstevel@tonic-gate 					*s3 = '/';
654*7c478bd9Sstevel@tonic-gate 				return (NULL);
655*7c478bd9Sstevel@tonic-gate 			}
656*7c478bd9Sstevel@tonic-gate 		}
657*7c478bd9Sstevel@tonic-gate 		if (s3 != NULL)
658*7c478bd9Sstevel@tonic-gate 			*s3 = '/';
659*7c478bd9Sstevel@tonic-gate 		s2 = pw->pw_dir;
660*7c478bd9Sstevel@tonic-gate 	}
661*7c478bd9Sstevel@tonic-gate 	for (s1 = buf; s1 < &buf[len] && (*s1++ = *s2++); )
662*7c478bd9Sstevel@tonic-gate 		;
663*7c478bd9Sstevel@tonic-gate 	s2 = --s1;
664*7c478bd9Sstevel@tonic-gate 	if (s3 != NULL) {
665*7c478bd9Sstevel@tonic-gate 		s2++;
666*7c478bd9Sstevel@tonic-gate 		while (s1 < &buf[len] && (*s1++ = *s3++))
667*7c478bd9Sstevel@tonic-gate 			;
668*7c478bd9Sstevel@tonic-gate 	}
669*7c478bd9Sstevel@tonic-gate 	if (s1 == &buf[len]) {
670*7c478bd9Sstevel@tonic-gate 		error("pathname too long: %s\n", file - 1);
671*7c478bd9Sstevel@tonic-gate 		return (NULL);
672*7c478bd9Sstevel@tonic-gate 	}
673*7c478bd9Sstevel@tonic-gate 	return (s2);
674*7c478bd9Sstevel@tonic-gate }
675