xref: /netbsd-src/bin/ksh/eval.c (revision d47295cc35d0f14450e9b401e5c838084cc9e0d2)
1*d47295ccSrillig /*	$NetBSD: eval.c,v 1.27 2024/10/03 20:14:01 rillig Exp $	*/
22ab2e20cStls 
3e1b2664cSjtc /*
4e1b2664cSjtc  * Expansion - quoting, separation, substitution, globbing
5e1b2664cSjtc  */
66377cac7Sagc #include <sys/cdefs.h>
76377cac7Sagc 
86377cac7Sagc #ifndef lint
9*d47295ccSrillig __RCSID("$NetBSD: eval.c,v 1.27 2024/10/03 20:14:01 rillig Exp $");
106377cac7Sagc #endif
116377cac7Sagc 
12fac4b394Skamil #include <sys/stat.h>
130a54ac30Sdholland #include <stdint.h>
140a54ac30Sdholland #include <pwd.h>
15e1b2664cSjtc 
16e1b2664cSjtc #include "sh.h"
17e1b2664cSjtc #include "ksh_dir.h"
18e1b2664cSjtc 
19e1b2664cSjtc /*
20e1b2664cSjtc  * string expansion
21e1b2664cSjtc  *
22e1b2664cSjtc  * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
23e1b2664cSjtc  * second pass: alternation ({,}), filename expansion (*?[]).
24e1b2664cSjtc  */
25e1b2664cSjtc 
26e1b2664cSjtc /* expansion generator state */
27e1b2664cSjtc typedef struct Expand {
28e1b2664cSjtc 	/* int  type; */	/* see expand() */
29e1b2664cSjtc 	const char *str;	/* string */
30e1b2664cSjtc 	union {
31e1b2664cSjtc 		const char **strv;/* string[] */
32e1b2664cSjtc 		struct shf *shf;/* file */
33e1b2664cSjtc 	} u;			/* source */
34e1b2664cSjtc 	struct tbl *var;	/* variable in ${var..} */
35e1b2664cSjtc 	short	split;		/* split "$@" / call waitlast $() */
36e1b2664cSjtc } Expand;
37e1b2664cSjtc 
38e1b2664cSjtc #define	XBASE		0	/* scanning original */
39e1b2664cSjtc #define	XSUB		1	/* expanding ${} string */
40e1b2664cSjtc #define	XARGSEP		2	/* ifs0 between "$*" */
41e1b2664cSjtc #define	XARG		3	/* expanding $*, $@ */
42e1b2664cSjtc #define	XCOM		4	/* expanding $() */
43e1b2664cSjtc #define XNULLSUB	5	/* "$@" when $# is 0 (don't generate word) */
44e1b2664cSjtc 
45e1b2664cSjtc /* States used for field splitting */
46e1b2664cSjtc #define IFS_WORD	0	/* word has chars (or quotes) */
47e1b2664cSjtc #define IFS_WS		1	/* have seen IFS white-space */
48e1b2664cSjtc #define IFS_NWS		2	/* have seen IFS non-white-space */
49e1b2664cSjtc 
5048ee8d12Shubertf static	int	varsub ARGS((Expand *xp, char *sp, char *word, int *stypep, int *slenp));
51e1b2664cSjtc static	int	comsub ARGS((Expand *xp, char *cp));
52e1b2664cSjtc static	char   *trimsub ARGS((char *str, char *pat, int how));
53b9cf72acSkamil static	void	ksh_glob ARGS((char *cp, XPtrV *wp, int markdirs));
54e1b2664cSjtc static	void	globit ARGS((XString *xs, char **xpp, char *sp, XPtrV *wp,
55e1b2664cSjtc 			     int check));
56e1b2664cSjtc static char	*maybe_expand_tilde ARGS((char *p, XString *dsp, char **dpp,
57e1b2664cSjtc 					  int isassign));
58e1b2664cSjtc static	char   *tilde ARGS((char *acp));
59e1b2664cSjtc static	char   *homedir ARGS((char *name));
60e1b2664cSjtc #ifdef BRACE_EXPAND
61e1b2664cSjtc static void	alt_expand ARGS((XPtrV *wp, char *start, char *exp_start,
62e1b2664cSjtc 				 char *end, int fdo));
63e1b2664cSjtc #endif
64e1b2664cSjtc 
65e1b2664cSjtc /* compile and expand word */
66e1b2664cSjtc char *
67e1b2664cSjtc substitute(cp, f)
68e1b2664cSjtc 	const char *cp;
69e1b2664cSjtc 	int f;
70e1b2664cSjtc {
71e1b2664cSjtc 	struct source *s, *sold;
72e1b2664cSjtc 
73e1b2664cSjtc 	sold = source;
74e1b2664cSjtc 	s = pushs(SWSTR, ATEMP);
75e1b2664cSjtc 	s->start = s->str = cp;
76e1b2664cSjtc 	source = s;
77e1b2664cSjtc 	if (yylex(ONEWORD) != LWORD)
78e1b2664cSjtc 		internal_errorf(1, "substitute");
79e1b2664cSjtc 	source = sold;
80e1b2664cSjtc 	afree(s, ATEMP);
81e1b2664cSjtc 	return evalstr(yylval.cp, f);
82e1b2664cSjtc }
83e1b2664cSjtc 
84e1b2664cSjtc /*
85e1b2664cSjtc  * expand arg-list
86e1b2664cSjtc  */
87e1b2664cSjtc char **
88e1b2664cSjtc eval(ap, f)
890f2b5450Skamil 	char **ap;
90e1b2664cSjtc 	int f;
91e1b2664cSjtc {
92e1b2664cSjtc 	XPtrV w;
93e1b2664cSjtc 
94e1b2664cSjtc 	if (*ap == NULL)
95e1b2664cSjtc 		return ap;
96e1b2664cSjtc 	XPinit(w, 32);
97e1b2664cSjtc 	XPput(w, NULL);		/* space for shell name */
98e1b2664cSjtc 	while (*ap != NULL)
99e1b2664cSjtc 		expand(*ap++, &w, f);
100e1b2664cSjtc 	XPput(w, NULL);
101e1b2664cSjtc 	return (char **) XPclose(w) + 1;
102e1b2664cSjtc }
103e1b2664cSjtc 
104e1b2664cSjtc /*
105e1b2664cSjtc  * expand string
106e1b2664cSjtc  */
107e1b2664cSjtc char *
108e1b2664cSjtc evalstr(cp, f)
109e1b2664cSjtc 	char *cp;
110e1b2664cSjtc 	int f;
111e1b2664cSjtc {
112e1b2664cSjtc 	XPtrV w;
113e1b2664cSjtc 
114e1b2664cSjtc 	XPinit(w, 1);
115e1b2664cSjtc 	expand(cp, &w, f);
116e1b2664cSjtc 	cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w);
117e1b2664cSjtc 	XPfree(w);
118e1b2664cSjtc 	return cp;
119e1b2664cSjtc }
120e1b2664cSjtc 
121e1b2664cSjtc /*
122e1b2664cSjtc  * expand string - return only one component
123e1b2664cSjtc  * used from iosetup to expand redirection files
124e1b2664cSjtc  */
125e1b2664cSjtc char *
126e1b2664cSjtc evalonestr(cp, f)
1270f2b5450Skamil 	char *cp;
128e1b2664cSjtc 	int f;
129e1b2664cSjtc {
130e1b2664cSjtc 	XPtrV w;
131e1b2664cSjtc 
132e1b2664cSjtc 	XPinit(w, 1);
133e1b2664cSjtc 	expand(cp, &w, f);
134e1b2664cSjtc 	switch (XPsize(w)) {
135e1b2664cSjtc 	case 0:
136e1b2664cSjtc 		cp = null;
137e1b2664cSjtc 		break;
138e1b2664cSjtc 	case 1:
139e1b2664cSjtc 		cp = (char*) *XPptrv(w);
140e1b2664cSjtc 		break;
141e1b2664cSjtc 	default:
142e1b2664cSjtc 		cp = evalstr(cp, f&~DOGLOB);
143e1b2664cSjtc 		break;
144e1b2664cSjtc 	}
145e1b2664cSjtc 	XPfree(w);
146e1b2664cSjtc 	return cp;
147e1b2664cSjtc }
148e1b2664cSjtc 
149e1b2664cSjtc /* for nested substitution: ${var:=$var2} */
150e1b2664cSjtc typedef struct SubType {
151e1b2664cSjtc 	short	stype;		/* [=+-?%#] action after expanded word */
152e1b2664cSjtc 	short	base;		/* begin position of expanded word */
153e1b2664cSjtc 	short	f;		/* saved value of f (DOPAT, etc) */
154e1b2664cSjtc 	struct tbl *var;	/* variable for ${var..} */
155e1b2664cSjtc 	short	quote;		/* saved value of quote (for ${..[%#]..}) */
156e1b2664cSjtc 	struct SubType *prev;	/* old type */
157e1b2664cSjtc 	struct SubType *next;	/* poped type (to avoid re-allocating) */
158e1b2664cSjtc } SubType;
159e1b2664cSjtc 
160e1b2664cSjtc void
161e1b2664cSjtc expand(cp, wp, f)
162e1b2664cSjtc 	char *cp;		/* input word */
1630f2b5450Skamil 	XPtrV *wp;		/* output words */
164e1b2664cSjtc 	int f;			/* DO* flags */
165e1b2664cSjtc {
1660f2b5450Skamil 	int UNINITIALIZED(c);
1670f2b5450Skamil 	int type;		/* expansion type */
1680f2b5450Skamil 	int quote = 0;		/* quoted */
169e1b2664cSjtc 	XString ds;		/* destination string */
1700f2b5450Skamil 	char *dp, *sp;		/* dest., source */
171e1b2664cSjtc 	int fdo, word;		/* second pass flags; have word */
172f662a744Smycroft 	int doblank;		/* field splitting of parameter/command subst */
173e1b2664cSjtc 	Expand x;		/* expansion variables */
174e1b2664cSjtc 	SubType st_head, *st;
175e1b2664cSjtc 	int UNINITIALIZED(newlines); /* For trailing newlines in COMSUB */
176d6d5f49cSkamil 	int saw_eq;
177d6d5f49cSkamil 	unsigned int tilde_ok;
178e1b2664cSjtc 	int make_magic;
179f662a744Smycroft 	size_t len;
180e1b2664cSjtc 
181084c0528Smrg 	x.split = 0;	/* XXX gcc */
182084c0528Smrg 	x.str = NULL;	/* XXX gcc */
1830fa13341Schristos 	x.u.strv = NULL;/* XXX gcc */
184e1b2664cSjtc 	if (cp == NULL)
185e1b2664cSjtc 		internal_errorf(1, "expand(NULL)");
186e1b2664cSjtc 	/* for alias, readonly, set, typeset commands */
187e1b2664cSjtc 	if ((f & DOVACHECK) && is_wdvarassign(cp)) {
188e1b2664cSjtc 		f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE);
189e1b2664cSjtc 		f |= DOASNTILDE;
190e1b2664cSjtc 	}
191e1b2664cSjtc 	if (Flag(FNOGLOB))
192e1b2664cSjtc 		f &= ~DOGLOB;
193e1b2664cSjtc 	if (Flag(FMARKDIRS))
194e1b2664cSjtc 		f |= DOMARKDIRS;
195e1b2664cSjtc #ifdef BRACE_EXPAND
196e1b2664cSjtc 	if (Flag(FBRACEEXPAND) && (f & DOGLOB))
197e1b2664cSjtc 		f |= DOBRACE_;
198e1b2664cSjtc #endif /* BRACE_EXPAND */
199e1b2664cSjtc 
200e1b2664cSjtc 	Xinit(ds, dp, 128, ATEMP);	/* init dest. string */
201e1b2664cSjtc 	type = XBASE;
202e1b2664cSjtc 	sp = cp;
203e1b2664cSjtc 	fdo = 0;
204e1b2664cSjtc 	saw_eq = 0;
205e1b2664cSjtc 	tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */
206e1b2664cSjtc 	doblank = 0;
207e1b2664cSjtc 	make_magic = 0;
208e1b2664cSjtc 	word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
209e1b2664cSjtc 	st_head.next = (SubType *) 0;
210e1b2664cSjtc 	st = &st_head;
211e1b2664cSjtc 
212e1b2664cSjtc 	while (1) {
213e1b2664cSjtc 		Xcheck(ds, dp);
214e1b2664cSjtc 
215e1b2664cSjtc 		switch (type) {
216e1b2664cSjtc 		  case XBASE:	/* original prefixed string */
217e1b2664cSjtc 			c = *sp++;
218e1b2664cSjtc 			switch (c) {
219e1b2664cSjtc 			  case EOS:
220e1b2664cSjtc 				c = 0;
221e1b2664cSjtc 				break;
222e1b2664cSjtc 			  case CHAR:
223e1b2664cSjtc 				c = *sp++;
224e1b2664cSjtc 				break;
225e1b2664cSjtc 			  case QCHAR:
226e1b2664cSjtc 				quote |= 2; /* temporary quote */
227e1b2664cSjtc 				c = *sp++;
228e1b2664cSjtc 				break;
229e1b2664cSjtc 			  case OQUOTE:
230e1b2664cSjtc 				word = IFS_WORD;
231e1b2664cSjtc 				tilde_ok = 0;
232e1b2664cSjtc 				quote = 1;
233e1b2664cSjtc 				continue;
234e1b2664cSjtc 			  case CQUOTE:
235e1b2664cSjtc 				quote = 0;
236e1b2664cSjtc 				continue;
237e1b2664cSjtc 			  case COMSUB:
238e1b2664cSjtc 				tilde_ok = 0;
239e1b2664cSjtc 				if (f & DONTRUNCOMMAND) {
240e1b2664cSjtc 					word = IFS_WORD;
241e1b2664cSjtc 					*dp++ = '$'; *dp++ = '(';
242e1b2664cSjtc 					while (*sp != '\0') {
243e1b2664cSjtc 						Xcheck(ds, dp);
244e1b2664cSjtc 						*dp++ = *sp++;
245e1b2664cSjtc 					}
246e1b2664cSjtc 					*dp++ = ')';
247e1b2664cSjtc 				} else {
248e1b2664cSjtc 					type = comsub(&x, sp);
249e1b2664cSjtc 					if (type == XCOM && (f&DOBLANK))
250e1b2664cSjtc 						doblank++;
251e1b2664cSjtc 					sp = strchr(sp, 0) + 1;
252e1b2664cSjtc 					newlines = 0;
253e1b2664cSjtc 				}
254e1b2664cSjtc 				continue;
255e1b2664cSjtc 			  case EXPRSUB:
256e1b2664cSjtc 				word = IFS_WORD;
257e1b2664cSjtc 				tilde_ok = 0;
258e1b2664cSjtc 				if (f & DONTRUNCOMMAND) {
259e1b2664cSjtc 					*dp++ = '$'; *dp++ = '('; *dp++ = '(';
260e1b2664cSjtc 					while (*sp != '\0') {
261e1b2664cSjtc 						Xcheck(ds, dp);
262e1b2664cSjtc 						*dp++ = *sp++;
263e1b2664cSjtc 					}
264e1b2664cSjtc 					*dp++ = ')'; *dp++ = ')';
265e1b2664cSjtc 				} else {
266e1b2664cSjtc 					struct tbl v;
267e1b2664cSjtc 					char *p;
268e1b2664cSjtc 
269e1b2664cSjtc 					v.flag = DEFINED|ISSET|INTEGER;
270e1b2664cSjtc 					v.type = 10; /* not default */
271e1b2664cSjtc 					v.name[0] = '\0';
272e1b2664cSjtc 					v_evaluate(&v, substitute(sp, 0),
27348ee8d12Shubertf 						KSH_UNWIND_ERROR);
274e1b2664cSjtc 					sp = strchr(sp, 0) + 1;
275e1b2664cSjtc 					for (p = str_val(&v); *p; ) {
276e1b2664cSjtc 						Xcheck(ds, dp);
277e1b2664cSjtc 						*dp++ = *p++;
278e1b2664cSjtc 					}
279e1b2664cSjtc 				}
280e1b2664cSjtc 				continue;
281e1b2664cSjtc 			  case OSUBST: /* ${{#}var{:}[=+-?#%]word} */
282e1b2664cSjtc 			  /* format is:
28348ee8d12Shubertf 			   *   OSUBST [{x] plain-variable-part \0
28448ee8d12Shubertf 			   *     compiled-word-part CSUBST [}x]
285e1b2664cSjtc 			   * This is were all syntax checking gets done...
286e1b2664cSjtc 			   */
287e1b2664cSjtc 			  {
28848ee8d12Shubertf 				char *varname = ++sp; /* skip the { or x (}) */
289e1b2664cSjtc 				int stype;
29048ee8d12Shubertf 				int slen;
291e1b2664cSjtc 
292084c0528Smrg 				slen = -1;	/* XXX gcc */
293e1b2664cSjtc 				sp = strchr(sp, '\0') + 1; /* skip variable */
29448ee8d12Shubertf 				type = varsub(&x, varname, sp, &stype, &slen);
295e1b2664cSjtc 				if (type < 0) {
296e1b2664cSjtc 					char endc;
297e1b2664cSjtc 					char *str, *end;
298e1b2664cSjtc 
299e1b2664cSjtc 					end = (char *) wdscan(sp, CSUBST);
30048ee8d12Shubertf 					/* ({) the } or x is already skipped */
301e1b2664cSjtc 					endc = *end;
302e1b2664cSjtc 					*end = EOS;
303e1b2664cSjtc 					str = snptreef((char *) 0, 64, "%S",
304e1b2664cSjtc 							varname - 1);
305e1b2664cSjtc 					*end = endc;
306e1b2664cSjtc 					errorf("%s: bad substitution", str);
307e1b2664cSjtc 				}
308e1b2664cSjtc 				if (f&DOBLANK)
309e1b2664cSjtc 					doblank++;
310e1b2664cSjtc 				tilde_ok = 0;
311e1b2664cSjtc 				if (type == XBASE) {	/* expand? */
312e1b2664cSjtc 					if (!st->next) {
313e1b2664cSjtc 						SubType *newst;
314e1b2664cSjtc 
315e1b2664cSjtc 						newst = (SubType *) alloc(
316e1b2664cSjtc 							sizeof(SubType), ATEMP);
317e1b2664cSjtc 						newst->next = (SubType *) 0;
318e1b2664cSjtc 						newst->prev = st;
319e1b2664cSjtc 						st->next = newst;
320e1b2664cSjtc 					}
321e1b2664cSjtc 					st = st->next;
322e1b2664cSjtc 					st->stype = stype;
323e1b2664cSjtc 					st->base = Xsavepos(ds, dp);
324e1b2664cSjtc 					st->f = f;
325e1b2664cSjtc 					st->var = x.var;
326e1b2664cSjtc 					st->quote = quote;
327e1b2664cSjtc 					/* skip qualifier(s) */
32848ee8d12Shubertf 					if (stype)
32948ee8d12Shubertf 						sp += slen;
330e1b2664cSjtc 					switch (stype & 0x7f) {
331e1b2664cSjtc 					  case '#':
332e1b2664cSjtc 					  case '%':
333e1b2664cSjtc 						/* ! DOBLANK,DOBRACE_,DOTILDE */
334e1b2664cSjtc 						f = DOPAT | (f&DONTRUNCOMMAND)
335e1b2664cSjtc 						    | DOTEMP_;
336e1b2664cSjtc 						quote = 0;
33748ee8d12Shubertf 						/* Prepend open pattern (so |
33848ee8d12Shubertf 						 * in a trim will work as
33948ee8d12Shubertf 						 * expected)
34048ee8d12Shubertf 						 */
34148ee8d12Shubertf 						*dp++ = MAGIC;
342acfe9a77Sjoerg 						*dp++ = (char)('@' + 0x80);
343e1b2664cSjtc 						break;
344e1b2664cSjtc 					  case '=':
345e1b2664cSjtc 						/* Enabling tilde expansion
346e1b2664cSjtc 						 * after :'s here is
347e1b2664cSjtc 						 * non-standard ksh, but is
348e1b2664cSjtc 						 * consistent with rules for
349e1b2664cSjtc 						 * other assignments.  Not
350e1b2664cSjtc 						 * sure what POSIX thinks of
351e1b2664cSjtc 						 * this.
352e1b2664cSjtc 						 * Not doing tilde expansion
353e1b2664cSjtc 						 * for integer variables is a
354e1b2664cSjtc 						 * non-POSIX thing - makes
355e1b2664cSjtc 						 * sense though, since ~ is
356e1b2664cSjtc 						 * a arithmetic operator.
357e1b2664cSjtc 						 */
358e1b2664cSjtc 						if (!(x.var->flag & INTEGER))
359e1b2664cSjtc 							f |= DOASNTILDE|DOTILDE;
360e1b2664cSjtc 						f |= DOTEMP_;
361e1b2664cSjtc 						/* These will be done after the
362e1b2664cSjtc 						 * value has been assigned.
363e1b2664cSjtc 						 */
364e1b2664cSjtc 						f &= ~(DOBLANK|DOGLOB|DOBRACE_);
365e1b2664cSjtc 						tilde_ok = 1;
366e1b2664cSjtc 						break;
367e1b2664cSjtc 					  case '?':
368e1b2664cSjtc 						f &= ~DOBLANK;
369e1b2664cSjtc 						f |= DOTEMP_;
370e1b2664cSjtc 						/* fall through */
371e1b2664cSjtc 					  default:
372e1b2664cSjtc 						/* Enable tilde expansion */
373e1b2664cSjtc 						tilde_ok = 1;
374e1b2664cSjtc 						f |= DOTILDE;
375e1b2664cSjtc 					}
376e1b2664cSjtc 				} else
377e1b2664cSjtc 					/* skip word */
378e1b2664cSjtc 					sp = (char *) wdscan(sp, CSUBST);
379e1b2664cSjtc 				continue;
380e1b2664cSjtc 			  }
381e1b2664cSjtc 			  case CSUBST: /* only get here if expanding word */
38248ee8d12Shubertf 				sp++; /* ({) skip the } or x */
383e1b2664cSjtc 				tilde_ok = 0;	/* in case of ${unset:-} */
384e1b2664cSjtc 				*dp = '\0';
385e1b2664cSjtc 				quote = st->quote;
386e1b2664cSjtc 				f = st->f;
387e1b2664cSjtc 				if (f&DOBLANK)
388e1b2664cSjtc 					doblank--;
389e1b2664cSjtc 				switch (st->stype&0x7f) {
390e1b2664cSjtc 				  case '#':
391e1b2664cSjtc 				  case '%':
39248ee8d12Shubertf 					/* Append end-pattern */
39348ee8d12Shubertf 					*dp++ = MAGIC; *dp++ = ')'; *dp = '\0';
394e1b2664cSjtc 					dp = Xrestpos(ds, dp, st->base);
395e1b2664cSjtc 					/* Must use st->var since calling
396e1b2664cSjtc 					 * global would break things
397e1b2664cSjtc 					 * like x[i+=1].
398e1b2664cSjtc 					 */
399e1b2664cSjtc 					x.str = trimsub(str_val(st->var),
400e1b2664cSjtc 						dp, st->stype);
401e1b2664cSjtc 					type = XSUB;
402e1b2664cSjtc 					if (f&DOBLANK)
403e1b2664cSjtc 						doblank++;
404e1b2664cSjtc 					st = st->prev;
405e1b2664cSjtc 					continue;
406e1b2664cSjtc 				  case '=':
407e1b2664cSjtc 					/* Restore our position and substitute
408e1b2664cSjtc 					 * the value of st->var (may not be
409e1b2664cSjtc 					 * the assigned value in the presence
410e1b2664cSjtc 					 * of integer/right-adj/etc attributes).
411e1b2664cSjtc 					 */
412e1b2664cSjtc 					dp = Xrestpos(ds, dp, st->base);
413e1b2664cSjtc 					/* Must use st->var since calling
414e1b2664cSjtc 					 * global would cause with things
415e1b2664cSjtc 					 * like x[i+=1] to be evaluated twice.
416e1b2664cSjtc 					 */
41748ee8d12Shubertf 					/* Note: not exported by FEXPORT
41848ee8d12Shubertf 					 * in at&t ksh.
41948ee8d12Shubertf 					 */
42048ee8d12Shubertf 					/* XXX POSIX says readonly is only
42148ee8d12Shubertf 					 * fatal for special builtins (setstr
42248ee8d12Shubertf 					 * does readonly check).
42348ee8d12Shubertf 					 */
424f662a744Smycroft 					len = strlen(dp) + 1;
425f662a744Smycroft 					setstr(st->var,
426f662a744Smycroft 					    debunk((char *) alloc(len, ATEMP),
427f662a744Smycroft 						dp, len),
42848ee8d12Shubertf 					    KSH_UNWIND_ERROR);
429e1b2664cSjtc 					x.str = str_val(st->var);
430e1b2664cSjtc 					type = XSUB;
431e1b2664cSjtc 					if (f&DOBLANK)
432e1b2664cSjtc 						doblank++;
433e1b2664cSjtc 					st = st->prev;
434e1b2664cSjtc 					continue;
435e1b2664cSjtc 				  case '?':
436e1b2664cSjtc 				    {
437e1b2664cSjtc 					char *s = Xrestpos(ds, dp, st->base);
438e1b2664cSjtc 
439e1b2664cSjtc 					errorf("%s: %s", st->var->name,
440e1b2664cSjtc 					    dp == s ?
441e1b2664cSjtc 					      "parameter null or not set"
442f662a744Smycroft 					    : (debunk(s, s, strlen(s) + 1), s));
443e1b2664cSjtc 				    }
444e1b2664cSjtc 				}
445e1b2664cSjtc 				st = st->prev;
446e1b2664cSjtc 				type = XBASE;
447e1b2664cSjtc 				continue;
448e1b2664cSjtc 
449e1b2664cSjtc 			  case OPAT: /* open pattern: *(foo|bar) */
450e1b2664cSjtc 				/* Next char is the type of pattern */
451e1b2664cSjtc 				make_magic = 1;
452e1b2664cSjtc 				c = *sp++ + 0x80;
453e1b2664cSjtc 				break;
454e1b2664cSjtc 
455f662a744Smycroft 			  case SPAT: /* pattern separator (|) */
456e1b2664cSjtc 				make_magic = 1;
457e1b2664cSjtc 				c = '|';
458e1b2664cSjtc 				break;
459e1b2664cSjtc 
460e1b2664cSjtc 			  case CPAT: /* close pattern */
461e1b2664cSjtc 				make_magic = 1;
462e1b2664cSjtc 				c = /*(*/ ')';
463e1b2664cSjtc 				break;
464e1b2664cSjtc 			}
465e1b2664cSjtc 			break;
466e1b2664cSjtc 
467e1b2664cSjtc 		  case XNULLSUB:
468e1b2664cSjtc 			/* Special case for "$@" (and "${foo[@]}") - no
469e1b2664cSjtc 			 * word is generated if $# is 0 (unless there is
470e1b2664cSjtc 			 * other stuff inside the quotes).
471e1b2664cSjtc 			 */
472e1b2664cSjtc 			type = XBASE;
473e1b2664cSjtc 			if (f&DOBLANK) {
474e1b2664cSjtc 				doblank--;
475e1b2664cSjtc 				/* not really correct: x=; "$x$@" should
476e1b2664cSjtc 				 * generate a null argument and
477e1b2664cSjtc 				 * set A; "${@:+}" shouldn't.
478e1b2664cSjtc 				 */
479e1b2664cSjtc 				if (dp == Xstring(ds, dp))
480e1b2664cSjtc 					word = IFS_WS;
481e1b2664cSjtc 			}
482e1b2664cSjtc 			continue;
483e1b2664cSjtc 
484e1b2664cSjtc 		  case XSUB:
485e1b2664cSjtc 			if ((c = *x.str++) == 0) {
486e1b2664cSjtc 				type = XBASE;
487e1b2664cSjtc 				if (f&DOBLANK)
488e1b2664cSjtc 					doblank--;
489e1b2664cSjtc 				continue;
490e1b2664cSjtc 			}
491e1b2664cSjtc 			break;
492e1b2664cSjtc 
493e1b2664cSjtc 		  case XARGSEP:
494e1b2664cSjtc 			type = XARG;
495e1b2664cSjtc 			quote = 1;
4966883e45bSmlelstv 			/* FALLTHROUGH */
497e1b2664cSjtc 		  case XARG:
498e1b2664cSjtc 			if ((c = *x.str++) == '\0') {
499e1b2664cSjtc 				/* force null words to be created so
500e1b2664cSjtc 				 * set -- '' 2 ''; foo "$@" will do
501e1b2664cSjtc 				 * the right thing
502e1b2664cSjtc 				 */
503e1b2664cSjtc 				if (quote && x.split)
504e1b2664cSjtc 					word = IFS_WORD;
505e1b2664cSjtc 				if ((x.str = *x.u.strv++) == NULL) {
506e1b2664cSjtc 					type = XBASE;
507e1b2664cSjtc 					if (f&DOBLANK)
508e1b2664cSjtc 						doblank--;
509e1b2664cSjtc 					continue;
510e1b2664cSjtc 				}
511e1b2664cSjtc 				c = ifs0;
512e1b2664cSjtc 				if (c == 0) {
513e1b2664cSjtc 					if (quote && !x.split)
514e1b2664cSjtc 						continue;
515e1b2664cSjtc 					c = ' ';
516e1b2664cSjtc 				}
517e1b2664cSjtc 				if (quote && x.split) {
518e1b2664cSjtc 					/* terminate word for "$@" */
519e1b2664cSjtc 					type = XARGSEP;
520e1b2664cSjtc 					quote = 0;
521e1b2664cSjtc 				}
522e1b2664cSjtc 			}
523e1b2664cSjtc 			break;
524e1b2664cSjtc 
525e1b2664cSjtc 		  case XCOM:
526e1b2664cSjtc 			if (newlines) {		/* Spit out saved nl's */
527e1b2664cSjtc 				c = '\n';
528e1b2664cSjtc 				--newlines;
529e1b2664cSjtc 			} else {
530e1b2664cSjtc 				while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
531e1b2664cSjtc 				    if (c == '\n')
532e1b2664cSjtc 					    newlines++;	/* Save newlines */
533e1b2664cSjtc 				if (newlines && c != EOF) {
534e1b2664cSjtc 					shf_ungetc(c, x.u.shf);
535e1b2664cSjtc 					c = '\n';
536e1b2664cSjtc 					--newlines;
537e1b2664cSjtc 				}
538e1b2664cSjtc 			}
539e1b2664cSjtc 			if (c == EOF) {
540e1b2664cSjtc 				newlines = 0;
541e1b2664cSjtc 				shf_close(x.u.shf);
542e1b2664cSjtc 				if (x.split)
543e1b2664cSjtc 					subst_exstat = waitlast();
544e1b2664cSjtc 				type = XBASE;
545e1b2664cSjtc 				if (f&DOBLANK)
546e1b2664cSjtc 					doblank--;
547e1b2664cSjtc 				continue;
548e1b2664cSjtc 			}
549e1b2664cSjtc 			break;
550e1b2664cSjtc 		}
551e1b2664cSjtc 
552e1b2664cSjtc 		/* check for end of word or IFS separation */
553e1b2664cSjtc 		if (c == 0 || (!quote && (f & DOBLANK) && doblank && !make_magic
554e1b2664cSjtc 			       && ctype(c, C_IFS)))
555e1b2664cSjtc 		{
556e1b2664cSjtc 			/* How words are broken up:
557e1b2664cSjtc 			 *		   |       value of c
558e1b2664cSjtc 			 *	  word	   |	ws	nws	0
559e1b2664cSjtc 			 *	-----------------------------------
560e1b2664cSjtc 			 *	IFS_WORD	w/WS	w/NWS	w
561e1b2664cSjtc 			 *	IFS_WS		-/WS	w/NWS	-
562e1b2664cSjtc 			 *	IFS_NWS		-/NWS	w/NWS	w
563e1b2664cSjtc 			 *   (w means generate a word)
564e1b2664cSjtc 			 * Note that IFS_NWS/0 generates a word (at&t ksh
565e1b2664cSjtc 			 * doesn't do this, but POSIX does).
566e1b2664cSjtc 			 */
567e1b2664cSjtc 			if (word == IFS_WORD
568e1b2664cSjtc 			    || (!ctype(c, C_IFSWS) && (c || word == IFS_NWS)))
569e1b2664cSjtc 			{
570e1b2664cSjtc 				char *p;
571e1b2664cSjtc 
572e1b2664cSjtc 				*dp++ = '\0';
573e1b2664cSjtc 				p = Xclose(ds, dp);
574e1b2664cSjtc #ifdef BRACE_EXPAND
575e1b2664cSjtc 				if (fdo & DOBRACE_)
576e1b2664cSjtc 					/* also does globbing */
577e1b2664cSjtc 					alt_expand(wp, p, p,
578e1b2664cSjtc 						   p + Xlength(ds, (dp - 1)),
579e1b2664cSjtc 						   fdo | (f & DOMARKDIRS));
580e1b2664cSjtc 				else
581e1b2664cSjtc #endif /* BRACE_EXPAND */
582e1b2664cSjtc 				if (fdo & DOGLOB)
583b9cf72acSkamil 					ksh_glob(p, wp, f & DOMARKDIRS);
584e1b2664cSjtc 				else if ((f & DOPAT) || !(fdo & DOMAGIC_))
585e1b2664cSjtc 					XPput(*wp, p);
586e1b2664cSjtc 				else
587f662a744Smycroft 					XPput(*wp, debunk(p, p, strlen(p) + 1));
588e1b2664cSjtc 				fdo = 0;
589e1b2664cSjtc 				saw_eq = 0;
590e1b2664cSjtc 				tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
591e1b2664cSjtc 				if (c != 0)
592e1b2664cSjtc 					Xinit(ds, dp, 128, ATEMP);
593e1b2664cSjtc 			}
594e1b2664cSjtc 			if (c == 0)
595e1b2664cSjtc 				return;
596e1b2664cSjtc 			if (word != IFS_NWS)
597e1b2664cSjtc 				word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
598e1b2664cSjtc 		} else {
599e1b2664cSjtc 			/* age tilde_ok info - ~ code tests second bit */
600e1b2664cSjtc 			tilde_ok <<= 1;
601e1b2664cSjtc 			/* mark any special second pass chars */
602e1b2664cSjtc 			if (!quote)
603e1b2664cSjtc 				switch (c) {
604e1b2664cSjtc 				  case '[':
605e1b2664cSjtc 				  case NOT:
606e1b2664cSjtc 				  case '-':
607e1b2664cSjtc 				  case ']':
608e1b2664cSjtc 					/* For character classes - doesn't hurt
609e1b2664cSjtc 					 * to have magic !,-,]'s outside of
610e1b2664cSjtc 					 * [...] expressions.
611e1b2664cSjtc 					 */
612e1b2664cSjtc 					if (f & (DOPAT | DOGLOB)) {
613e1b2664cSjtc 						fdo |= DOMAGIC_;
614e1b2664cSjtc 						if (c == '[')
615e1b2664cSjtc 							fdo |= f & DOGLOB;
616e1b2664cSjtc 						*dp++ = MAGIC;
617e1b2664cSjtc 					}
618e1b2664cSjtc 					break;
619e1b2664cSjtc 				  case '*':
620e1b2664cSjtc 				  case '?':
621e1b2664cSjtc 					if (f & (DOPAT | DOGLOB)) {
622e1b2664cSjtc 						fdo |= DOMAGIC_ | (f & DOGLOB);
623e1b2664cSjtc 						*dp++ = MAGIC;
624e1b2664cSjtc 					}
625e1b2664cSjtc 					break;
626e1b2664cSjtc #ifdef BRACE_EXPAND
627e1b2664cSjtc 				  case OBRACE:
628e1b2664cSjtc 				  case ',':
629e1b2664cSjtc 				  case CBRACE:
630e1b2664cSjtc 					if ((f & DOBRACE_) && (c == OBRACE
631e1b2664cSjtc 						|| (fdo & DOBRACE_)))
632e1b2664cSjtc 					{
633e1b2664cSjtc 						fdo |= DOBRACE_|DOMAGIC_;
634e1b2664cSjtc 						*dp++ = MAGIC;
635e1b2664cSjtc 					}
636e1b2664cSjtc 					break;
637e1b2664cSjtc #endif /* BRACE_EXPAND */
638e1b2664cSjtc 				  case '=':
639e1b2664cSjtc 					/* Note first unquoted = for ~ */
640e1b2664cSjtc 					if (!(f & DOTEMP_) && !saw_eq) {
641e1b2664cSjtc 						saw_eq = 1;
642e1b2664cSjtc 						tilde_ok = 1;
643e1b2664cSjtc 					}
644e1b2664cSjtc 					break;
645e1b2664cSjtc 				  case PATHSEP: /* : */
646e1b2664cSjtc 					/* Note unquoted : for ~ */
647e1b2664cSjtc 					if (!(f & DOTEMP_) && (f & DOASNTILDE))
648e1b2664cSjtc 						tilde_ok = 1;
649e1b2664cSjtc 					break;
650e1b2664cSjtc 				  case '~':
651e1b2664cSjtc 					/* tilde_ok is reset whenever
652e1b2664cSjtc 					 * any of ' " $( $(( ${ } are seen.
653e1b2664cSjtc 					 * Note that tilde_ok must be preserved
654e1b2664cSjtc 					 * through the sequence ${A=a=}~
655e1b2664cSjtc 					 */
656e1b2664cSjtc 					if (type == XBASE
657e1b2664cSjtc 					    && (f & (DOTILDE|DOASNTILDE))
658e1b2664cSjtc 					    && (tilde_ok & 2))
659e1b2664cSjtc 					{
660e1b2664cSjtc 						char *p, *dp_x;
661e1b2664cSjtc 
662e1b2664cSjtc 						dp_x = dp;
663e1b2664cSjtc 						p = maybe_expand_tilde(sp,
664e1b2664cSjtc 							&ds, &dp_x,
665e1b2664cSjtc 							f & DOASNTILDE);
666e1b2664cSjtc 						if (p) {
667e1b2664cSjtc 							if (dp != dp_x)
668e1b2664cSjtc 								word = IFS_WORD;
669e1b2664cSjtc 							dp = dp_x;
670e1b2664cSjtc 							sp = p;
671e1b2664cSjtc 							continue;
672e1b2664cSjtc 						}
673e1b2664cSjtc 					}
674e1b2664cSjtc 					break;
675e1b2664cSjtc 				}
676e1b2664cSjtc 			else
677e1b2664cSjtc 				quote &= ~2; /* undo temporary */
678e1b2664cSjtc 
679e1b2664cSjtc 			if (make_magic) {
680e1b2664cSjtc 				make_magic = 0;
681e1b2664cSjtc 				fdo |= DOMAGIC_ | (f & DOGLOB);
682e1b2664cSjtc 				*dp++ = MAGIC;
683e1b2664cSjtc 			} else if (ISMAGIC(c)) {
684e1b2664cSjtc 				fdo |= DOMAGIC_;
685e1b2664cSjtc 				*dp++ = MAGIC;
686e1b2664cSjtc 			}
687e1b2664cSjtc 			*dp++ = c; /* save output char */
688e1b2664cSjtc 			word = IFS_WORD;
689e1b2664cSjtc 		}
690e1b2664cSjtc 	}
691e1b2664cSjtc }
692e1b2664cSjtc 
693e1b2664cSjtc /*
694e1b2664cSjtc  * Prepare to generate the string returned by ${} substitution.
695e1b2664cSjtc  */
696e1b2664cSjtc static int
69748ee8d12Shubertf varsub(xp, sp, word, stypep, slenp)
698e1b2664cSjtc 	Expand *xp;
699e1b2664cSjtc 	char *sp;
700e1b2664cSjtc 	char *word;
70148ee8d12Shubertf 	int *stypep;	/* becomes qualifier type */
70248ee8d12Shubertf 	int *slenp;	/* " " len (=, :=, etc.) valid iff *stypep != 0 */
703e1b2664cSjtc {
704e1b2664cSjtc 	int c;
705e1b2664cSjtc 	int state;	/* next state: XBASE, XARG, XSUB, XNULLSUB */
706e1b2664cSjtc 	int stype;	/* substitution type */
70748ee8d12Shubertf 	int slen;
708e1b2664cSjtc 	char *p;
709e1b2664cSjtc 	struct tbl *vp;
710e1b2664cSjtc 
711e1b2664cSjtc 	if (sp[0] == '\0')	/* Bad variable name */
712e1b2664cSjtc 		return -1;
713e1b2664cSjtc 
7140fa13341Schristos 	xp->var = NULL;
715e1b2664cSjtc 
716e1b2664cSjtc 	/* ${#var}, string length or array size */
717e1b2664cSjtc 	if (sp[0] == '#' && (c = sp[1]) != '\0') {
718e1b2664cSjtc 		int zero_ok = 0;
719e1b2664cSjtc 
720e1b2664cSjtc 		/* Can't have any modifiers for ${#...} */
721e1b2664cSjtc 		if (*word != CSUBST)
722e1b2664cSjtc 			return -1;
723e1b2664cSjtc 		sp++;
724e1b2664cSjtc 		/* Check for size of array */
725e1b2664cSjtc 		if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
72648ee8d12Shubertf 			int n = 0;
727e1b2664cSjtc 			vp = global(arrayname(sp));
728e1b2664cSjtc 			if (vp->flag & (ISSET|ARRAY))
729e1b2664cSjtc 				zero_ok = 1;
730e1b2664cSjtc 			for (; vp; vp = vp->u.array)
73148ee8d12Shubertf 				if (vp->flag & ISSET) {
73248ee8d12Shubertf 					n++;
73348ee8d12Shubertf 				}
73448ee8d12Shubertf 			c = n; /* ksh88/ksh93 go for number, not max index */
735e1b2664cSjtc 		} else if (c == '*' || c == '@')
736e1b2664cSjtc 			c = e->loc->argc;
737e1b2664cSjtc 		else {
738e1b2664cSjtc 			p = str_val(global(sp));
739e1b2664cSjtc 			zero_ok = p != null;
740e1b2664cSjtc 			c = strlen(p);
741e1b2664cSjtc 		}
742e1b2664cSjtc 		if (Flag(FNOUNSET) && c == 0 && !zero_ok)
743e1b2664cSjtc 			errorf("%s: parameter not set", sp);
744e1b2664cSjtc 		*stypep = 0; /* unqualified variable/string substitution */
745e1b2664cSjtc 		xp->str = str_save(ulton((unsigned long)c, 10), ATEMP);
746e1b2664cSjtc 		return XSUB;
747e1b2664cSjtc 	}
748e1b2664cSjtc 
749e1b2664cSjtc 	/* Check for qualifiers in word part */
750e1b2664cSjtc 	stype = 0;
75148ee8d12Shubertf 	c = word[slen = 0] == CHAR ? word[1] : 0;
752e1b2664cSjtc 	if (c == ':') {
75348ee8d12Shubertf 		slen += 2;
754e1b2664cSjtc 		stype = 0x80;
75548ee8d12Shubertf 		c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
756e1b2664cSjtc 	}
75748ee8d12Shubertf 	if (ctype(c, C_SUBOP1)) {
75848ee8d12Shubertf 		slen += 2;
759e1b2664cSjtc 		stype |= c;
76048ee8d12Shubertf 	} else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */
76148ee8d12Shubertf 		slen += 2;
762e1b2664cSjtc 		stype = c;
76348ee8d12Shubertf 		if (word[slen + 0] == CHAR && c == word[slen + 1]) {
764e1b2664cSjtc 			stype |= 0x80;
76548ee8d12Shubertf 			slen += 2;
766e1b2664cSjtc 		}
76748ee8d12Shubertf 	} else if (stype)	/* : is not ok */
76848ee8d12Shubertf 		return -1;
769e1b2664cSjtc 	if (!stype && *word != CSUBST)
770e1b2664cSjtc 		return -1;
771e1b2664cSjtc 	*stypep = stype;
77248ee8d12Shubertf 	*slenp = slen;
773e1b2664cSjtc 
774e1b2664cSjtc 	c = sp[0];
775e1b2664cSjtc 	if (c == '*' || c == '@') {
776e1b2664cSjtc 		switch (stype & 0x7f) {
777e1b2664cSjtc 		  case '=':	/* can't assign to a vector */
77848ee8d12Shubertf 		  case '%':	/* can't trim a vector (yet) */
779e1b2664cSjtc 		  case '#':
780e1b2664cSjtc 			return -1;
781e1b2664cSjtc 		}
782e1b2664cSjtc 		if (e->loc->argc == 0) {
7830fa13341Schristos 			xp->u.strv = NULL;
784e1b2664cSjtc 			xp->str = null;
785e1b2664cSjtc 			state = c == '@' ? XNULLSUB : XSUB;
786e1b2664cSjtc 		} else {
7870fa13341Schristos 			char **t = &e->loc->argv[1];
7880fa13341Schristos 			xp->u.strv = (void *)(uintptr_t)t;
789e1b2664cSjtc 			xp->str = *xp->u.strv++;
790e1b2664cSjtc 			xp->split = c == '@'; /* $@ */
791e1b2664cSjtc 			state = XARG;
792e1b2664cSjtc 		}
793e1b2664cSjtc 	} else {
794e1b2664cSjtc 		if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
795e1b2664cSjtc 			XPtrV wv;
796e1b2664cSjtc 
797e1b2664cSjtc 			switch (stype & 0x7f) {
798e1b2664cSjtc 			  case '=':	/* can't assign to a vector */
79948ee8d12Shubertf 			  case '%':	/* can't trim a vector (yet) */
800e1b2664cSjtc 			  case '#':
801e1b2664cSjtc 				return -1;
802e1b2664cSjtc 			}
803e1b2664cSjtc 			XPinit(wv, 32);
804e1b2664cSjtc 			vp = global(arrayname(sp));
805e1b2664cSjtc 			for (; vp; vp = vp->u.array) {
806e1b2664cSjtc 				if (!(vp->flag&ISSET))
807e1b2664cSjtc 					continue;
808e1b2664cSjtc 				XPput(wv, str_val(vp));
809e1b2664cSjtc 			}
810e1b2664cSjtc 			if (XPsize(wv) == 0) {
811e1b2664cSjtc 				xp->str = null;
812e1b2664cSjtc 				state = p[1] == '@' ? XNULLSUB : XSUB;
813e1b2664cSjtc 				XPfree(wv);
814e1b2664cSjtc 			} else {
815e1b2664cSjtc 				XPput(wv, 0);
816e1b2664cSjtc 				xp->u.strv = (const char **) XPptrv(wv);
817e1b2664cSjtc 				xp->str = *xp->u.strv++;
818e1b2664cSjtc 				xp->split = p[1] == '@'; /* ${foo[@]} */
819e1b2664cSjtc 				state = XARG;
820e1b2664cSjtc 			}
821e1b2664cSjtc 		} else {
822e1b2664cSjtc 			/* Can't assign things like $! or $1 */
823e1b2664cSjtc 			if ((stype & 0x7f) == '='
824e1b2664cSjtc 			    && (ctype(*sp, C_VAR1) || digit(*sp)))
825e1b2664cSjtc 				return -1;
826e1b2664cSjtc 			xp->var = global(sp);
827e1b2664cSjtc 			xp->str = str_val(xp->var);
828e1b2664cSjtc 			state = XSUB;
829e1b2664cSjtc 		}
830e1b2664cSjtc 	}
831e1b2664cSjtc 
832e1b2664cSjtc 	c = stype&0x7f;
833e1b2664cSjtc 	/* test the compiler's code generator */
834e1b2664cSjtc 	if (ctype(c, C_SUBOP2) ||
835e1b2664cSjtc 	    (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
836e1b2664cSjtc 	     c == '=' || c == '-' || c == '?' : c == '+'))
837e1b2664cSjtc 		state = XBASE;	/* expand word instead of variable value */
838e1b2664cSjtc 	if (Flag(FNOUNSET) && xp->str == null
839e1b2664cSjtc 	    && (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
840e1b2664cSjtc 		errorf("%s: parameter not set", sp);
841e1b2664cSjtc 	return state;
842e1b2664cSjtc }
843e1b2664cSjtc 
844e1b2664cSjtc /*
845e1b2664cSjtc  * Run the command in $(...) and read its output.
846e1b2664cSjtc  */
847e1b2664cSjtc static int
848e1b2664cSjtc comsub(xp, cp)
8490f2b5450Skamil 	Expand *xp;
850e1b2664cSjtc 	char *cp;
851e1b2664cSjtc {
852e1b2664cSjtc 	Source *s, *sold;
8530f2b5450Skamil 	struct op *t;
854e1b2664cSjtc 	struct shf *shf;
855e1b2664cSjtc 
856e1b2664cSjtc 	s = pushs(SSTRING, ATEMP);
857e1b2664cSjtc 	s->start = s->str = cp;
858e1b2664cSjtc 	sold = source;
859e1b2664cSjtc 	t = compile(s);
8604f633ea0Schristos 	afree(s, ATEMP);
861e1b2664cSjtc 	source = sold;
862e1b2664cSjtc 
863e1b2664cSjtc 	if (t == NULL)
864e1b2664cSjtc 		return XBASE;
865e1b2664cSjtc 
866e1b2664cSjtc 	if (t != NULL && t->type == TCOM && /* $(<file) */
867e1b2664cSjtc 	    *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
8680f2b5450Skamil 		struct ioword *io = *t->ioact;
869e1b2664cSjtc 		char *name;
870e1b2664cSjtc 
871e1b2664cSjtc 		if ((io->flag&IOTYPE) != IOREAD)
872e1b2664cSjtc 			errorf("funny $() command: %s",
873e1b2664cSjtc 				snptreef((char *) 0, 32, "%R", io));
874e1b2664cSjtc 		shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
875e1b2664cSjtc 			SHF_MAPHI|SHF_CLEXEC);
876e1b2664cSjtc 		if (shf == NULL)
877e1b2664cSjtc 			errorf("%s: cannot open $() input", name);
878e1b2664cSjtc 		xp->split = 0;	/* no waitlast() */
879e1b2664cSjtc 	} else {
880e1b2664cSjtc 		int ofd1, pv[2];
881e1b2664cSjtc 		openpipe(pv);
882e1b2664cSjtc 		shf = shf_fdopen(pv[0], SHF_RD, (struct shf *) 0);
883e1b2664cSjtc 		ofd1 = savefd(1, 0);	/* fd 1 may be closed... */
884f662a744Smycroft 		if (pv[1] != 1) {
885dd8a75d5Skamil 			ksh_dup2(pv[1], 1, false);
886e1b2664cSjtc 			close(pv[1]);
887f662a744Smycroft 		}
888e1b2664cSjtc 		execute(t, XFORK|XXCOM|XPIPEO);
889e1b2664cSjtc 		restfd(1, ofd1);
890e1b2664cSjtc 		startlast();
891e1b2664cSjtc 		xp->split = 1;	/* waitlast() */
892e1b2664cSjtc 	}
893e1b2664cSjtc 
894e1b2664cSjtc 	xp->u.shf = shf;
895e1b2664cSjtc 	return XCOM;
896e1b2664cSjtc }
897e1b2664cSjtc 
898e1b2664cSjtc /*
899e1b2664cSjtc  * perform #pattern and %pattern substitution in ${}
900e1b2664cSjtc  */
901e1b2664cSjtc 
902e1b2664cSjtc static char *
903e1b2664cSjtc trimsub(str, pat, how)
9040f2b5450Skamil 	char *str;
905e1b2664cSjtc 	char *pat;
906e1b2664cSjtc 	int how;
907e1b2664cSjtc {
9080f2b5450Skamil 	char *end = strchr(str, 0);
9090f2b5450Skamil 	char *p, c;
910e1b2664cSjtc 
911e1b2664cSjtc 	switch (how&0xff) {	/* UCHAR_MAX maybe? */
912f662a744Smycroft 	  case '#':		/* shortest at beginning */
913e1b2664cSjtc 		for (p = str; p <= end; p++) {
914e1b2664cSjtc 			c = *p; *p = '\0';
915dd8a75d5Skamil 			if (gmatch(str, pat, false)) {
916e1b2664cSjtc 				*p = c;
917e1b2664cSjtc 				return p;
918e1b2664cSjtc 			}
919e1b2664cSjtc 			*p = c;
920e1b2664cSjtc 		}
921e1b2664cSjtc 		break;
922f662a744Smycroft 	  case '#'|0x80:	/* longest match at beginning */
923e1b2664cSjtc 		for (p = end; p >= str; p--) {
924e1b2664cSjtc 			c = *p; *p = '\0';
925dd8a75d5Skamil 			if (gmatch(str, pat, false)) {
926e1b2664cSjtc 				*p = c;
927e1b2664cSjtc 				return p;
928e1b2664cSjtc 			}
929e1b2664cSjtc 			*p = c;
930e1b2664cSjtc 		}
931e1b2664cSjtc 		break;
932e1b2664cSjtc 	  case '%':		/* shortest match at end */
933e1b2664cSjtc 		for (p = end; p >= str; p--) {
934dd8a75d5Skamil 			if (gmatch(p, pat, false))
935e1b2664cSjtc 				return str_nsave(str, p - str, ATEMP);
936e1b2664cSjtc 		}
937e1b2664cSjtc 		break;
938e1b2664cSjtc 	  case '%'|0x80:	/* longest match at end */
939e1b2664cSjtc 		for (p = str; p <= end; p++) {
940dd8a75d5Skamil 			if (gmatch(p, pat, false))
941e1b2664cSjtc 				return str_nsave(str, p - str, ATEMP);
942e1b2664cSjtc 		}
943e1b2664cSjtc 		break;
944e1b2664cSjtc 	}
945e1b2664cSjtc 
946e1b2664cSjtc 	return str;		/* no match, return string */
947e1b2664cSjtc }
948e1b2664cSjtc 
949e1b2664cSjtc /*
950b9cf72acSkamil  * ksh_glob
951e1b2664cSjtc  * Name derived from V6's /etc/glob, the program that expanded filenames.
952e1b2664cSjtc  */
953e1b2664cSjtc 
954e1b2664cSjtc /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */
955e1b2664cSjtc static void
956b9cf72acSkamil ksh_glob(cp, wp, markdirs)
957e1b2664cSjtc 	char *cp;
9580f2b5450Skamil 	XPtrV *wp;
959e1b2664cSjtc 	int markdirs;
960e1b2664cSjtc {
961e1b2664cSjtc 	int oldsize = XPsize(*wp);
962e1b2664cSjtc 
963e1b2664cSjtc 	if (glob_str(cp, wp, markdirs) == 0)
964f662a744Smycroft 		XPput(*wp, debunk(cp, cp, strlen(cp) + 1));
965e1b2664cSjtc 	else
966e1b2664cSjtc 		qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize),
967e1b2664cSjtc 			xstrcmp);
968e1b2664cSjtc }
969e1b2664cSjtc 
970e1b2664cSjtc #define GF_NONE		0
971f662a744Smycroft #define GF_EXCHECK	BIT(0)		/* do existence check on file */
972e1b2664cSjtc #define GF_GLOBBED	BIT(1)		/* some globbing has been done */
973e1b2664cSjtc #define GF_MARKDIR	BIT(2)		/* add trailing / to directories */
974e1b2664cSjtc 
975e1b2664cSjtc /* Apply file globbing to cp and store the matching files in wp.  Returns
976e1b2664cSjtc  * the number of matches found.
977e1b2664cSjtc  */
978e1b2664cSjtc int
979e1b2664cSjtc glob_str(cp, wp, markdirs)
980e1b2664cSjtc 	char *cp;
981e1b2664cSjtc 	XPtrV *wp;
982e1b2664cSjtc 	int markdirs;
983e1b2664cSjtc {
984e1b2664cSjtc 	int oldsize = XPsize(*wp);
985e1b2664cSjtc 	XString xs;
986e1b2664cSjtc 	char *xp;
987e1b2664cSjtc 
988e1b2664cSjtc 	Xinit(xs, xp, 256, ATEMP);
989e1b2664cSjtc 	globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE);
990e1b2664cSjtc 	Xfree(xs, xp);
991e1b2664cSjtc 
992e1b2664cSjtc 	return XPsize(*wp) - oldsize;
993e1b2664cSjtc }
994e1b2664cSjtc 
995e1b2664cSjtc static void
996e1b2664cSjtc globit(xs, xpp, sp, wp, check)
997e1b2664cSjtc 	XString *xs;		/* dest string */
998e1b2664cSjtc 	char **xpp;		/* ptr to dest end */
999e1b2664cSjtc 	char *sp;		/* source path */
10000f2b5450Skamil 	XPtrV *wp;		/* output list */
1001e1b2664cSjtc 	int check;		/* GF_* flags */
1002e1b2664cSjtc {
10030f2b5450Skamil 	char *np;		/* next source component */
1004e1b2664cSjtc 	char *xp = *xpp;
1005e1b2664cSjtc 	char *se;
1006e1b2664cSjtc 	char odirsep;
1007e1b2664cSjtc 
1008e1b2664cSjtc 	/* This to allow long expansions to be interrupted */
1009e1b2664cSjtc 	intrcheck();
1010e1b2664cSjtc 
1011e1b2664cSjtc 	if (sp == NULL) {	/* end of source path */
1012e1b2664cSjtc 		/* We only need to check if the file exists if a pattern
1013e1b2664cSjtc 		 * is followed by a non-pattern (eg, foo*x/bar; no check
1014e1b2664cSjtc 		 * is needed for foo* since the match must exist) or if
1015e1b2664cSjtc 		 * any patterns were expanded and the markdirs option is set.
1016e1b2664cSjtc 		 * Symlinks make things a bit tricky...
1017e1b2664cSjtc 		 */
1018e1b2664cSjtc 		if ((check & GF_EXCHECK)
1019e1b2664cSjtc 		    || ((check & GF_MARKDIR) && (check & GF_GLOBBED)))
1020e1b2664cSjtc 		{
1021e1b2664cSjtc #define stat_check()	(stat_done ? stat_done : \
1022e1b2664cSjtc 			    (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
1023e1b2664cSjtc 				? -1 : 1))
1024e1b2664cSjtc 			struct stat lstatb, statb;
1025e1b2664cSjtc 			int stat_done = 0;	 /* -1: failed, 1 ok */
1026e1b2664cSjtc 
1027e1b2664cSjtc 			if (lstat(Xstring(*xs, xp), &lstatb) < 0)
1028e1b2664cSjtc 				return;
1029e1b2664cSjtc 			/* special case for systems which strip trailing
1030e1b2664cSjtc 			 * slashes from regular files (eg, /etc/passwd/).
1031e1b2664cSjtc 			 * SunOS 4.1.3 does this...
1032e1b2664cSjtc 			 */
1033e1b2664cSjtc 			if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp)
1034e1b2664cSjtc 			    && ISDIRSEP(xp[-1]) && !S_ISDIR(lstatb.st_mode)
1035e1b2664cSjtc #ifdef S_ISLNK
1036e1b2664cSjtc 			    && (!S_ISLNK(lstatb.st_mode)
1037e1b2664cSjtc 				|| stat_check() < 0
1038e1b2664cSjtc 				|| !S_ISDIR(statb.st_mode))
1039e1b2664cSjtc #endif /* S_ISLNK */
1040e1b2664cSjtc 				)
1041e1b2664cSjtc 				return;
1042e1b2664cSjtc 			/* Possibly tack on a trailing / if there isn't already
1043e1b2664cSjtc 			 * one and if the file is a directory or a symlink to a
1044e1b2664cSjtc 			 * directory
1045e1b2664cSjtc 			 */
1046e1b2664cSjtc 			if (((check & GF_MARKDIR) && (check & GF_GLOBBED))
1047e1b2664cSjtc 			    && xp > Xstring(*xs, xp) && !ISDIRSEP(xp[-1])
1048e1b2664cSjtc 			    && (S_ISDIR(lstatb.st_mode)
1049e1b2664cSjtc #ifdef S_ISLNK
1050e1b2664cSjtc 				|| (S_ISLNK(lstatb.st_mode)
1051e1b2664cSjtc 				    && stat_check() > 0
1052e1b2664cSjtc 				    && S_ISDIR(statb.st_mode))
1053e1b2664cSjtc #endif /* S_ISLNK */
1054e1b2664cSjtc 				    ))
1055e1b2664cSjtc 			{
1056e1b2664cSjtc 				*xp++ = DIRSEP;
1057e1b2664cSjtc 				*xp = '\0';
1058e1b2664cSjtc 			}
1059e1b2664cSjtc 		}
1060e1b2664cSjtc # define KLUDGE_VAL	0
1061e1b2664cSjtc 		XPput(*wp, str_nsave(Xstring(*xs, xp), Xlength(*xs, xp)
1062e1b2664cSjtc 			+ KLUDGE_VAL, ATEMP));
1063e1b2664cSjtc 		return;
1064e1b2664cSjtc 	}
1065e1b2664cSjtc 
1066e1b2664cSjtc 	if (xp > Xstring(*xs, xp))
1067e1b2664cSjtc 		*xp++ = DIRSEP;
1068e1b2664cSjtc 	while (ISDIRSEP(*sp)) {
1069e1b2664cSjtc 		Xcheck(*xs, xp);
1070e1b2664cSjtc 		*xp++ = *sp++;
1071e1b2664cSjtc 	}
1072e1b2664cSjtc 	np = ksh_strchr_dirsep(sp);
1073e1b2664cSjtc 	if (np != NULL) {
1074e1b2664cSjtc 		se = np;
1075e1b2664cSjtc 		odirsep = *np;	/* don't assume DIRSEP, can be multiple kinds */
1076e1b2664cSjtc 		*np++ = '\0';
1077e1b2664cSjtc 	} else {
1078e1b2664cSjtc 		odirsep = '\0'; /* keep gcc quiet */
1079e1b2664cSjtc 		se = sp + strlen(sp);
1080e1b2664cSjtc 	}
1081e1b2664cSjtc 
1082e1b2664cSjtc 
1083e1b2664cSjtc 	/* Check if sp needs globbing - done to avoid pattern checks for strings
1084e1b2664cSjtc 	 * containing MAGIC characters, open ['s without the matching close ],
1085e1b2664cSjtc 	 * etc. (otherwise opendir() will be called which may fail because the
1086e1b2664cSjtc 	 * directory isn't readable - if no globbing is needed, only execute
1087e1b2664cSjtc 	 * permission should be required (as per POSIX)).
1088e1b2664cSjtc 	 */
1089e1b2664cSjtc 	if (!has_globbing(sp, se)) {
1090e1b2664cSjtc 		XcheckN(*xs, xp, se - sp + 1);
1091f662a744Smycroft 		debunk(xp, sp, Xnleft(*xs, xp));
1092e1b2664cSjtc 		xp += strlen(xp);
1093e1b2664cSjtc 		*xpp = xp;
1094e1b2664cSjtc 		globit(xs, xpp, np, wp, check);
1095e1b2664cSjtc 	} else {
1096e1b2664cSjtc 		DIR *dirp;
1097e1b2664cSjtc 		struct dirent *d;
1098e1b2664cSjtc 		char *name;
1099e1b2664cSjtc 		int len;
1100e1b2664cSjtc 		int prefix_len;
1101e1b2664cSjtc 
1102e1b2664cSjtc 		/* xp = *xpp;	   copy_non_glob() may have re-alloc'd xs */
1103e1b2664cSjtc 		*xp = '\0';
1104e1b2664cSjtc 		prefix_len = Xlength(*xs, xp);
1105e1b2664cSjtc 		dirp = ksh_opendir(prefix_len ? Xstring(*xs, xp) : ".");
1106e1b2664cSjtc 		if (dirp == NULL)
1107e1b2664cSjtc 			goto Nodir;
1108e1b2664cSjtc 		while ((d = readdir(dirp)) != NULL) {
1109e1b2664cSjtc 			name = d->d_name;
1110e1b2664cSjtc 			if ((*name == '.' && *sp != '.')
1111dd8a75d5Skamil 			    || !gmatch(name, sp, true))
1112e1b2664cSjtc 				continue;
1113e1b2664cSjtc 
1114e1b2664cSjtc 			len = NLENGTH(d) + 1;
1115e1b2664cSjtc 			XcheckN(*xs, xp, len);
1116e1b2664cSjtc 			memcpy(xp, name, len);
1117e1b2664cSjtc 			*xpp = xp + len - 1;
1118e1b2664cSjtc 			globit(xs, xpp, np, wp,
1119e1b2664cSjtc 				(check & GF_MARKDIR) | GF_GLOBBED
1120e1b2664cSjtc 				| (np ? GF_EXCHECK : GF_NONE));
1121e1b2664cSjtc 			xp = Xstring(*xs, xp) + prefix_len;
1122e1b2664cSjtc 		}
1123e1b2664cSjtc 		closedir(dirp);
1124e1b2664cSjtc 	  Nodir:;
1125e1b2664cSjtc 	}
1126e1b2664cSjtc 
1127e1b2664cSjtc 	if (np != NULL)
1128e1b2664cSjtc 		*--np = odirsep;
1129e1b2664cSjtc }
1130e1b2664cSjtc 
1131e1b2664cSjtc #if 0
1132e1b2664cSjtc /* Check if p contains something that needs globbing; if it does, 0 is
1133e1b2664cSjtc  * returned; if not, p is copied into xs/xp after stripping any MAGICs
1134e1b2664cSjtc  */
1135e1b2664cSjtc static int	copy_non_glob ARGS((XString *xs, char **xpp, char *p));
1136e1b2664cSjtc static int
1137e1b2664cSjtc copy_non_glob(xs, xpp, p)
1138e1b2664cSjtc 	XString *xs;
1139e1b2664cSjtc 	char **xpp;
1140e1b2664cSjtc 	char *p;
1141e1b2664cSjtc {
1142e1b2664cSjtc 	char *xp;
1143e1b2664cSjtc 	int len = strlen(p);
1144e1b2664cSjtc 
1145e1b2664cSjtc 	XcheckN(*xs, *xpp, len);
1146e1b2664cSjtc 	xp = *xpp;
1147e1b2664cSjtc 	for (; *p; p++) {
1148e1b2664cSjtc 		if (ISMAGIC(*p)) {
1149e1b2664cSjtc 			int c = *++p;
1150e1b2664cSjtc 
1151e1b2664cSjtc 			if (c == '*' || c == '?')
1152e1b2664cSjtc 				return 0;
1153e1b2664cSjtc 			if (*p == '[') {
1154e1b2664cSjtc 				char *q = p + 1;
1155e1b2664cSjtc 
1156e1b2664cSjtc 				if (ISMAGIC(*q) && q[1] == NOT)
1157e1b2664cSjtc 					q += 2;
1158e1b2664cSjtc 				if (ISMAGIC(*q) && q[1] == ']')
1159e1b2664cSjtc 					q += 2;
1160e1b2664cSjtc 				for (; *q; q++)
1161e1b2664cSjtc 					if (ISMAGIC(*q) && *++q == ']')
1162e1b2664cSjtc 						return 0;
1163e1b2664cSjtc 				/* pass a literal [ through */
1164e1b2664cSjtc 			}
1165e1b2664cSjtc 			/* must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, etc. */
1166e1b2664cSjtc 		}
1167e1b2664cSjtc 		*xp++ = *p;
1168e1b2664cSjtc 	}
1169e1b2664cSjtc 	*xp = '\0';
1170e1b2664cSjtc 	*xpp = xp;
1171e1b2664cSjtc 	return 1;
1172e1b2664cSjtc }
1173e1b2664cSjtc #endif /* 0 */
1174e1b2664cSjtc 
1175e1b2664cSjtc /* remove MAGIC from string */
1176e1b2664cSjtc char *
1177f662a744Smycroft debunk(dp, sp, dlen)
1178e1b2664cSjtc 	char *dp;
1179e1b2664cSjtc 	const char *sp;
1180f662a744Smycroft 	size_t dlen;
1181e1b2664cSjtc {
1182*d47295ccSrillig 	char *d;
1183*d47295ccSrillig 	const char *s;
1184e1b2664cSjtc 
1185e1b2664cSjtc 	if ((s = strchr(sp, MAGIC))) {
11867ca13b8bSlukem 		if (s - sp >= (ptrdiff_t)dlen)
1187f662a744Smycroft 			return dp;
1188e1b2664cSjtc 		memcpy(dp, sp, s - sp);
11897ca13b8bSlukem 		for (d = dp + (s - sp); *s && (d - dp < (ptrdiff_t)dlen); s++)
1190e1b2664cSjtc 			if (!ISMAGIC(*s) || !(*++s & 0x80)
1191e1b2664cSjtc 			    || !strchr("*+?@! ", *s & 0x7f))
1192e1b2664cSjtc 				*d++ = *s;
1193e1b2664cSjtc 			else {
1194e1b2664cSjtc 				/* extended pattern operators: *+?@! */
119548ee8d12Shubertf 				if ((*s & 0x7f) != ' ')
1196e1b2664cSjtc 					*d++ = *s & 0x7f;
11977ca13b8bSlukem 				if (d - dp < (ptrdiff_t)dlen)
1198e1b2664cSjtc 					*d++ = '(';
1199e1b2664cSjtc 			}
1200e1b2664cSjtc 		*d = '\0';
1201e1b2664cSjtc 	} else if (dp != sp)
1202f662a744Smycroft 		strlcpy(dp, sp, dlen);
1203e1b2664cSjtc 	return dp;
1204e1b2664cSjtc }
1205e1b2664cSjtc 
1206e1b2664cSjtc /* Check if p is an unquoted name, possibly followed by a / or :.  If so
1207e1b2664cSjtc  * puts the expanded version in *dcp,dp and returns a pointer in p just
1208e1b2664cSjtc  * past the name, otherwise returns 0.
1209e1b2664cSjtc  */
1210e1b2664cSjtc static char *
1211e1b2664cSjtc maybe_expand_tilde(p, dsp, dpp, isassign)
1212e1b2664cSjtc 	char *p;
1213e1b2664cSjtc 	XString *dsp;
1214e1b2664cSjtc 	char **dpp;
1215e1b2664cSjtc 	int isassign;
1216e1b2664cSjtc {
1217e1b2664cSjtc 	XString ts;
1218e1b2664cSjtc 	char *dp = *dpp;
1219e1b2664cSjtc 	char *tp, *r;
1220e1b2664cSjtc 
1221e1b2664cSjtc 	Xinit(ts, tp, 16, ATEMP);
1222e1b2664cSjtc 	/* : only for DOASNTILDE form */
1223e1b2664cSjtc 	while (p[0] == CHAR && !ISDIRSEP(p[1])
1224e1b2664cSjtc 	       && (!isassign || p[1] != PATHSEP))
1225e1b2664cSjtc 	{
1226e1b2664cSjtc 		Xcheck(ts, tp);
1227e1b2664cSjtc 		*tp++ = p[1];
1228e1b2664cSjtc 		p += 2;
1229e1b2664cSjtc 	}
1230e1b2664cSjtc 	*tp = '\0';
1231e1b2664cSjtc 	r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? tilde(Xstring(ts, tp)) : (char *) 0;
1232e1b2664cSjtc 	Xfree(ts, tp);
1233e1b2664cSjtc 	if (r) {
1234e1b2664cSjtc 		while (*r) {
1235e1b2664cSjtc 			Xcheck(*dsp, dp);
1236e1b2664cSjtc 			if (ISMAGIC(*r))
1237e1b2664cSjtc 				*dp++ = MAGIC;
1238e1b2664cSjtc 			*dp++ = *r++;
1239e1b2664cSjtc 		}
1240e1b2664cSjtc 		*dpp = dp;
1241e1b2664cSjtc 		r = p;
1242e1b2664cSjtc 	}
1243e1b2664cSjtc 	return r;
1244e1b2664cSjtc }
1245e1b2664cSjtc 
1246e1b2664cSjtc /*
1247e1b2664cSjtc  * tilde expansion
1248e1b2664cSjtc  *
1249e1b2664cSjtc  * based on a version by Arnold Robbins
1250e1b2664cSjtc  */
1251e1b2664cSjtc 
1252e1b2664cSjtc static char *
1253e1b2664cSjtc tilde(cp)
1254e1b2664cSjtc 	char *cp;
1255e1b2664cSjtc {
1256e1b2664cSjtc 	char *dp;
1257e1b2664cSjtc 
1258e1b2664cSjtc 	if (cp[0] == '\0')
1259e1b2664cSjtc 		dp = str_val(global("HOME"));
1260e1b2664cSjtc 	else if (cp[0] == '+' && cp[1] == '\0')
1261e1b2664cSjtc 		dp = str_val(global("PWD"));
1262e1b2664cSjtc 	else if (cp[0] == '-' && cp[1] == '\0')
1263e1b2664cSjtc 		dp = str_val(global("OLDPWD"));
1264e1b2664cSjtc 	else
1265e1b2664cSjtc 		dp = homedir(cp);
1266e1b2664cSjtc 	/* If HOME, PWD or OLDPWD are not set, don't expand ~ */
1267e1b2664cSjtc 	if (dp == null)
1268e1b2664cSjtc 		dp = (char *) 0;
1269e1b2664cSjtc 	return dp;
1270e1b2664cSjtc }
1271e1b2664cSjtc 
1272e1b2664cSjtc /*
1273e1b2664cSjtc  * map userid to user's home directory.
1274e1b2664cSjtc  * note that 4.3's getpw adds more than 6K to the shell,
1275e1b2664cSjtc  * and the YP version probably adds much more.
1276e1b2664cSjtc  * we might consider our own version of getpwnam() to keep the size down.
1277e1b2664cSjtc  */
1278e1b2664cSjtc 
1279e1b2664cSjtc static char *
1280e1b2664cSjtc homedir(name)
1281e1b2664cSjtc 	char *name;
1282e1b2664cSjtc {
12830f2b5450Skamil 	struct tbl *ap;
1284e1b2664cSjtc 
1285e1b2664cSjtc 	ap = tenter(&homedirs, name, hash(name));
1286e1b2664cSjtc 	if (!(ap->flag & ISSET)) {
1287e1b2664cSjtc 		struct passwd *pw;
1288ecc8aad2Scbiere 		size_t n;
1289e1b2664cSjtc 
1290e1b2664cSjtc 		pw = getpwnam(name);
1291e1b2664cSjtc 		if (pw == NULL)
1292e1b2664cSjtc 			return NULL;
1293ecc8aad2Scbiere 		n = strlen(pw->pw_dir);
1294ecc8aad2Scbiere 		if (n > 0 && '/' != pw->pw_dir[n - 1]) {
1295ecc8aad2Scbiere 			ap->val.s = str_nsave(pw->pw_dir, n + 1, APERM);
1296ecc8aad2Scbiere 			ap->val.s[n] = '/';
1297ecc8aad2Scbiere 			ap->val.s[n + 1] = '\0';
1298ecc8aad2Scbiere 		} else {
1299e1b2664cSjtc 			ap->val.s = str_save(pw->pw_dir, APERM);
1300ecc8aad2Scbiere 		}
1301e1b2664cSjtc 		ap->flag |= DEFINED|ISSET|ALLOC;
1302e1b2664cSjtc 	}
1303e1b2664cSjtc 	return ap->val.s;
1304e1b2664cSjtc }
1305e1b2664cSjtc 
1306e1b2664cSjtc #ifdef BRACE_EXPAND
1307e1b2664cSjtc static void
1308e1b2664cSjtc alt_expand(wp, start, exp_start, end, fdo)
1309e1b2664cSjtc 	XPtrV *wp;
1310e1b2664cSjtc 	char *start, *exp_start;
1311e1b2664cSjtc 	char *end;
1312e1b2664cSjtc 	int fdo;
1313e1b2664cSjtc {
1314e1b2664cSjtc 	int UNINITIALIZED(count);
1315e1b2664cSjtc 	char *brace_start, *brace_end, *UNINITIALIZED(comma);
1316e1b2664cSjtc 	char *field_start;
1317e1b2664cSjtc 	char *p;
1318e1b2664cSjtc 
1319e1b2664cSjtc 	/* search for open brace */
1320e1b2664cSjtc 	for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2)
1321e1b2664cSjtc 		;
1322e1b2664cSjtc 	brace_start = p;
1323e1b2664cSjtc 
1324e1b2664cSjtc 	/* find matching close brace, if any */
1325e1b2664cSjtc 	if (p) {
1326e1b2664cSjtc 		comma = (char *) 0;
1327e1b2664cSjtc 		count = 1;
1328e1b2664cSjtc 		for (p += 2; *p && count; p++) {
1329e1b2664cSjtc 			if (ISMAGIC(*p)) {
1330e1b2664cSjtc 				if (*++p == OBRACE)
1331e1b2664cSjtc 					count++;
1332e1b2664cSjtc 				else if (*p == CBRACE)
1333e1b2664cSjtc 					--count;
1334e1b2664cSjtc 				else if (*p == ',' && count == 1)
1335e1b2664cSjtc 					comma = p;
1336e1b2664cSjtc 			}
1337e1b2664cSjtc 		}
1338e1b2664cSjtc 	}
1339e1b2664cSjtc 	/* no valid expansions... */
1340e1b2664cSjtc 	if (!p || count != 0) {
1341e1b2664cSjtc 		/* Note that given a{{b,c} we do not expand anything (this is
1342e1b2664cSjtc 		 * what at&t ksh does.  This may be changed to do the {b,c}
1343e1b2664cSjtc 		 * expansion. }
1344e1b2664cSjtc 		 */
1345e1b2664cSjtc 		if (fdo & DOGLOB)
1346b9cf72acSkamil 			ksh_glob(start, wp, fdo & DOMARKDIRS);
1347e1b2664cSjtc 		else
1348f662a744Smycroft 			XPput(*wp, debunk(start, start, end - start));
1349e1b2664cSjtc 		return;
1350e1b2664cSjtc 	}
1351e1b2664cSjtc 	brace_end = p;
1352e1b2664cSjtc 	if (!comma) {
1353e1b2664cSjtc 		alt_expand(wp, start, brace_end, end, fdo);
1354e1b2664cSjtc 		return;
1355e1b2664cSjtc 	}
1356e1b2664cSjtc 
1357e1b2664cSjtc 	/* expand expression */
1358e1b2664cSjtc 	field_start = brace_start + 2;
1359e1b2664cSjtc 	count = 1;
1360e1b2664cSjtc 	for (p = brace_start + 2; p != brace_end; p++) {
1361e1b2664cSjtc 		if (ISMAGIC(*p)) {
1362e1b2664cSjtc 			if (*++p == OBRACE)
1363e1b2664cSjtc 				count++;
1364e1b2664cSjtc 			else if ((*p == CBRACE && --count == 0)
1365e1b2664cSjtc 				 || (*p == ',' && count == 1))
1366e1b2664cSjtc 			{
1367e1b2664cSjtc 				char *new;
1368e1b2664cSjtc 				int l1, l2, l3;
1369e1b2664cSjtc 
1370e1b2664cSjtc 				l1 = brace_start - start;
1371e1b2664cSjtc 				l2 = (p - 1) - field_start;
1372e1b2664cSjtc 				l3 = end - brace_end;
1373e1b2664cSjtc 				new = (char *) alloc(l1 + l2 + l3 + 1, ATEMP);
1374e1b2664cSjtc 				memcpy(new, start, l1);
1375e1b2664cSjtc 				memcpy(new + l1, field_start, l2);
1376e1b2664cSjtc 				memcpy(new + l1 + l2, brace_end, l3);
1377e1b2664cSjtc 				new[l1 + l2 + l3] = '\0';
1378e1b2664cSjtc 				alt_expand(wp, new, new + l1,
1379e1b2664cSjtc 					   new + l1 + l2 + l3, fdo);
1380e1b2664cSjtc 				field_start = p + 1;
1381e1b2664cSjtc 			}
1382e1b2664cSjtc 		}
1383e1b2664cSjtc 	}
1384e1b2664cSjtc 	return;
1385e1b2664cSjtc }
1386e1b2664cSjtc #endif /* BRACE_EXPAND */
1387