xref: /minix/bin/ksh/eval.c (revision 84d9c625)
1*84d9c625SLionel Sambuc /*	$NetBSD: eval.c,v 1.15 2013/10/18 19:53:34 christos Exp $	*/
22718b568SThomas Cort 
32718b568SThomas Cort /*
42718b568SThomas Cort  * Expansion - quoting, separation, substitution, globbing
52718b568SThomas Cort  */
62718b568SThomas Cort #include <sys/cdefs.h>
72718b568SThomas Cort 
82718b568SThomas Cort #ifndef lint
9*84d9c625SLionel Sambuc __RCSID("$NetBSD: eval.c,v 1.15 2013/10/18 19:53:34 christos Exp $");
102718b568SThomas Cort #endif
112718b568SThomas Cort 
122718b568SThomas Cort #include <stdint.h>
132718b568SThomas Cort #include <pwd.h>
142718b568SThomas Cort 
152718b568SThomas Cort #include "sh.h"
162718b568SThomas Cort #include "ksh_dir.h"
172718b568SThomas Cort #include "ksh_stat.h"
182718b568SThomas Cort 
192718b568SThomas Cort /*
202718b568SThomas Cort  * string expansion
212718b568SThomas Cort  *
222718b568SThomas Cort  * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
232718b568SThomas Cort  * second pass: alternation ({,}), filename expansion (*?[]).
242718b568SThomas Cort  */
252718b568SThomas Cort 
262718b568SThomas Cort /* expansion generator state */
272718b568SThomas Cort typedef struct Expand {
282718b568SThomas Cort 	/* int  type; */	/* see expand() */
292718b568SThomas Cort 	const char *str;	/* string */
302718b568SThomas Cort 	union {
312718b568SThomas Cort 		const char **strv;/* string[] */
322718b568SThomas Cort 		struct shf *shf;/* file */
332718b568SThomas Cort 	} u;			/* source */
342718b568SThomas Cort 	struct tbl *var;	/* variable in ${var..} */
352718b568SThomas Cort 	short	split;		/* split "$@" / call waitlast $() */
362718b568SThomas Cort } Expand;
372718b568SThomas Cort 
382718b568SThomas Cort #define	XBASE		0	/* scanning original */
392718b568SThomas Cort #define	XSUB		1	/* expanding ${} string */
402718b568SThomas Cort #define	XARGSEP		2	/* ifs0 between "$*" */
412718b568SThomas Cort #define	XARG		3	/* expanding $*, $@ */
422718b568SThomas Cort #define	XCOM		4	/* expanding $() */
432718b568SThomas Cort #define XNULLSUB	5	/* "$@" when $# is 0 (don't generate word) */
442718b568SThomas Cort 
452718b568SThomas Cort /* States used for field splitting */
462718b568SThomas Cort #define IFS_WORD	0	/* word has chars (or quotes) */
472718b568SThomas Cort #define IFS_WS		1	/* have seen IFS white-space */
482718b568SThomas Cort #define IFS_NWS		2	/* have seen IFS non-white-space */
492718b568SThomas Cort 
502718b568SThomas Cort static	int	varsub ARGS((Expand *xp, char *sp, char *word, int *stypep, int *slenp));
512718b568SThomas Cort static	int	comsub ARGS((Expand *xp, char *cp));
522718b568SThomas Cort static	char   *trimsub ARGS((char *str, char *pat, int how));
532718b568SThomas Cort static	void	glob ARGS((char *cp, XPtrV *wp, int markdirs));
542718b568SThomas Cort static	void	globit ARGS((XString *xs, char **xpp, char *sp, XPtrV *wp,
552718b568SThomas Cort 			     int check));
562718b568SThomas Cort static char	*maybe_expand_tilde ARGS((char *p, XString *dsp, char **dpp,
572718b568SThomas Cort 					  int isassign));
582718b568SThomas Cort static	char   *tilde ARGS((char *acp));
592718b568SThomas Cort static	char   *homedir ARGS((char *name));
602718b568SThomas Cort #ifdef BRACE_EXPAND
612718b568SThomas Cort static void	alt_expand ARGS((XPtrV *wp, char *start, char *exp_start,
622718b568SThomas Cort 				 char *end, int fdo));
632718b568SThomas Cort #endif
642718b568SThomas Cort 
652718b568SThomas Cort /* compile and expand word */
662718b568SThomas Cort char *
substitute(cp,f)672718b568SThomas Cort substitute(cp, f)
682718b568SThomas Cort 	const char *cp;
692718b568SThomas Cort 	int f;
702718b568SThomas Cort {
712718b568SThomas Cort 	struct source *s, *sold;
722718b568SThomas Cort 
732718b568SThomas Cort 	sold = source;
742718b568SThomas Cort 	s = pushs(SWSTR, ATEMP);
752718b568SThomas Cort 	s->start = s->str = cp;
762718b568SThomas Cort 	source = s;
772718b568SThomas Cort 	if (yylex(ONEWORD) != LWORD)
782718b568SThomas Cort 		internal_errorf(1, "substitute");
792718b568SThomas Cort 	source = sold;
802718b568SThomas Cort 	afree(s, ATEMP);
812718b568SThomas Cort 	return evalstr(yylval.cp, f);
822718b568SThomas Cort }
832718b568SThomas Cort 
842718b568SThomas Cort /*
852718b568SThomas Cort  * expand arg-list
862718b568SThomas Cort  */
872718b568SThomas Cort char **
eval(ap,f)882718b568SThomas Cort eval(ap, f)
892718b568SThomas Cort 	register char **ap;
902718b568SThomas Cort 	int f;
912718b568SThomas Cort {
922718b568SThomas Cort 	XPtrV w;
932718b568SThomas Cort 
942718b568SThomas Cort 	if (*ap == NULL)
952718b568SThomas Cort 		return ap;
962718b568SThomas Cort 	XPinit(w, 32);
972718b568SThomas Cort 	XPput(w, NULL);		/* space for shell name */
982718b568SThomas Cort #ifdef	SHARPBANG
992718b568SThomas Cort 	XPput(w, NULL);		/* and space for one arg */
1002718b568SThomas Cort #endif
1012718b568SThomas Cort 	while (*ap != NULL)
1022718b568SThomas Cort 		expand(*ap++, &w, f);
1032718b568SThomas Cort 	XPput(w, NULL);
1042718b568SThomas Cort #ifdef	SHARPBANG
1052718b568SThomas Cort 	return (char **) XPclose(w) + 2;
1062718b568SThomas Cort #else
1072718b568SThomas Cort 	return (char **) XPclose(w) + 1;
1082718b568SThomas Cort #endif
1092718b568SThomas Cort }
1102718b568SThomas Cort 
1112718b568SThomas Cort /*
1122718b568SThomas Cort  * expand string
1132718b568SThomas Cort  */
1142718b568SThomas Cort char *
evalstr(cp,f)1152718b568SThomas Cort evalstr(cp, f)
1162718b568SThomas Cort 	char *cp;
1172718b568SThomas Cort 	int f;
1182718b568SThomas Cort {
1192718b568SThomas Cort 	XPtrV w;
1202718b568SThomas Cort 
1212718b568SThomas Cort 	XPinit(w, 1);
1222718b568SThomas Cort 	expand(cp, &w, f);
1232718b568SThomas Cort 	cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w);
1242718b568SThomas Cort 	XPfree(w);
1252718b568SThomas Cort 	return cp;
1262718b568SThomas Cort }
1272718b568SThomas Cort 
1282718b568SThomas Cort /*
1292718b568SThomas Cort  * expand string - return only one component
1302718b568SThomas Cort  * used from iosetup to expand redirection files
1312718b568SThomas Cort  */
1322718b568SThomas Cort char *
evalonestr(cp,f)1332718b568SThomas Cort evalonestr(cp, f)
1342718b568SThomas Cort 	register char *cp;
1352718b568SThomas Cort 	int f;
1362718b568SThomas Cort {
1372718b568SThomas Cort 	XPtrV w;
1382718b568SThomas Cort 
1392718b568SThomas Cort 	XPinit(w, 1);
1402718b568SThomas Cort 	expand(cp, &w, f);
1412718b568SThomas Cort 	switch (XPsize(w)) {
1422718b568SThomas Cort 	case 0:
1432718b568SThomas Cort 		cp = null;
1442718b568SThomas Cort 		break;
1452718b568SThomas Cort 	case 1:
1462718b568SThomas Cort 		cp = (char*) *XPptrv(w);
1472718b568SThomas Cort 		break;
1482718b568SThomas Cort 	default:
1492718b568SThomas Cort 		cp = evalstr(cp, f&~DOGLOB);
1502718b568SThomas Cort 		break;
1512718b568SThomas Cort 	}
1522718b568SThomas Cort 	XPfree(w);
1532718b568SThomas Cort 	return cp;
1542718b568SThomas Cort }
1552718b568SThomas Cort 
1562718b568SThomas Cort /* for nested substitution: ${var:=$var2} */
1572718b568SThomas Cort typedef struct SubType {
1582718b568SThomas Cort 	short	stype;		/* [=+-?%#] action after expanded word */
1592718b568SThomas Cort 	short	base;		/* begin position of expanded word */
1602718b568SThomas Cort 	short	f;		/* saved value of f (DOPAT, etc) */
1612718b568SThomas Cort 	struct tbl *var;	/* variable for ${var..} */
1622718b568SThomas Cort 	short	quote;		/* saved value of quote (for ${..[%#]..}) */
1632718b568SThomas Cort 	struct SubType *prev;	/* old type */
1642718b568SThomas Cort 	struct SubType *next;	/* poped type (to avoid re-allocating) */
1652718b568SThomas Cort } SubType;
1662718b568SThomas Cort 
1672718b568SThomas Cort void
expand(cp,wp,f)1682718b568SThomas Cort expand(cp, wp, f)
1692718b568SThomas Cort 	char *cp;		/* input word */
1702718b568SThomas Cort 	register XPtrV *wp;	/* output words */
1712718b568SThomas Cort 	int f;			/* DO* flags */
1722718b568SThomas Cort {
1732718b568SThomas Cort 	register int UNINITIALIZED(c);
1742718b568SThomas Cort 	register int type;	/* expansion type */
1752718b568SThomas Cort 	register int quote = 0;	/* quoted */
1762718b568SThomas Cort 	XString ds;		/* destination string */
1772718b568SThomas Cort 	register char *dp, *sp;	/* dest., source */
1782718b568SThomas Cort 	int fdo, word;		/* second pass flags; have word */
1792718b568SThomas Cort 	int doblank;		/* field splitting of parameter/command subst */
1802718b568SThomas Cort 	Expand x;		/* expansion variables */
1812718b568SThomas Cort 	SubType st_head, *st;
1822718b568SThomas Cort 	int UNINITIALIZED(newlines); /* For trailing newlines in COMSUB */
1832718b568SThomas Cort 	int saw_eq, tilde_ok;
1842718b568SThomas Cort 	int make_magic;
1852718b568SThomas Cort 	size_t len;
1862718b568SThomas Cort 
1872718b568SThomas Cort 	x.split = 0;	/* XXX gcc */
1882718b568SThomas Cort 	x.str = NULL;	/* XXX gcc */
1892718b568SThomas Cort 	x.u.strv = NULL;/* XXX gcc */
1902718b568SThomas Cort 	if (cp == NULL)
1912718b568SThomas Cort 		internal_errorf(1, "expand(NULL)");
1922718b568SThomas Cort 	/* for alias, readonly, set, typeset commands */
1932718b568SThomas Cort 	if ((f & DOVACHECK) && is_wdvarassign(cp)) {
1942718b568SThomas Cort 		f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE);
1952718b568SThomas Cort 		f |= DOASNTILDE;
1962718b568SThomas Cort 	}
1972718b568SThomas Cort 	if (Flag(FNOGLOB))
1982718b568SThomas Cort 		f &= ~DOGLOB;
1992718b568SThomas Cort 	if (Flag(FMARKDIRS))
2002718b568SThomas Cort 		f |= DOMARKDIRS;
2012718b568SThomas Cort #ifdef BRACE_EXPAND
2022718b568SThomas Cort 	if (Flag(FBRACEEXPAND) && (f & DOGLOB))
2032718b568SThomas Cort 		f |= DOBRACE_;
2042718b568SThomas Cort #endif /* BRACE_EXPAND */
2052718b568SThomas Cort 
2062718b568SThomas Cort 	Xinit(ds, dp, 128, ATEMP);	/* init dest. string */
2072718b568SThomas Cort 	type = XBASE;
2082718b568SThomas Cort 	sp = cp;
2092718b568SThomas Cort 	fdo = 0;
2102718b568SThomas Cort 	saw_eq = 0;
2112718b568SThomas Cort 	tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */
2122718b568SThomas Cort 	doblank = 0;
2132718b568SThomas Cort 	make_magic = 0;
2142718b568SThomas Cort 	word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
2152718b568SThomas Cort 	st_head.next = (SubType *) 0;
2162718b568SThomas Cort 	st = &st_head;
2172718b568SThomas Cort 
2182718b568SThomas Cort 	while (1) {
2192718b568SThomas Cort 		Xcheck(ds, dp);
2202718b568SThomas Cort 
2212718b568SThomas Cort 		switch (type) {
2222718b568SThomas Cort 		  case XBASE:	/* original prefixed string */
2232718b568SThomas Cort 			c = *sp++;
2242718b568SThomas Cort 			switch (c) {
2252718b568SThomas Cort 			  case EOS:
2262718b568SThomas Cort 				c = 0;
2272718b568SThomas Cort 				break;
2282718b568SThomas Cort 			  case CHAR:
2292718b568SThomas Cort 				c = *sp++;
2302718b568SThomas Cort 				break;
2312718b568SThomas Cort 			  case QCHAR:
2322718b568SThomas Cort 				quote |= 2; /* temporary quote */
2332718b568SThomas Cort 				c = *sp++;
2342718b568SThomas Cort 				break;
2352718b568SThomas Cort 			  case OQUOTE:
2362718b568SThomas Cort 				word = IFS_WORD;
2372718b568SThomas Cort 				tilde_ok = 0;
2382718b568SThomas Cort 				quote = 1;
2392718b568SThomas Cort 				continue;
2402718b568SThomas Cort 			  case CQUOTE:
2412718b568SThomas Cort 				quote = 0;
2422718b568SThomas Cort 				continue;
2432718b568SThomas Cort 			  case COMSUB:
2442718b568SThomas Cort 				tilde_ok = 0;
2452718b568SThomas Cort 				if (f & DONTRUNCOMMAND) {
2462718b568SThomas Cort 					word = IFS_WORD;
2472718b568SThomas Cort 					*dp++ = '$'; *dp++ = '(';
2482718b568SThomas Cort 					while (*sp != '\0') {
2492718b568SThomas Cort 						Xcheck(ds, dp);
2502718b568SThomas Cort 						*dp++ = *sp++;
2512718b568SThomas Cort 					}
2522718b568SThomas Cort 					*dp++ = ')';
2532718b568SThomas Cort 				} else {
2542718b568SThomas Cort 					type = comsub(&x, sp);
2552718b568SThomas Cort 					if (type == XCOM && (f&DOBLANK))
2562718b568SThomas Cort 						doblank++;
2572718b568SThomas Cort 					sp = strchr(sp, 0) + 1;
2582718b568SThomas Cort 					newlines = 0;
2592718b568SThomas Cort 				}
2602718b568SThomas Cort 				continue;
2612718b568SThomas Cort 			  case EXPRSUB:
2622718b568SThomas Cort 				word = IFS_WORD;
2632718b568SThomas Cort 				tilde_ok = 0;
2642718b568SThomas Cort 				if (f & DONTRUNCOMMAND) {
2652718b568SThomas Cort 					*dp++ = '$'; *dp++ = '('; *dp++ = '(';
2662718b568SThomas Cort 					while (*sp != '\0') {
2672718b568SThomas Cort 						Xcheck(ds, dp);
2682718b568SThomas Cort 						*dp++ = *sp++;
2692718b568SThomas Cort 					}
2702718b568SThomas Cort 					*dp++ = ')'; *dp++ = ')';
2712718b568SThomas Cort 				} else {
2722718b568SThomas Cort 					struct tbl v;
2732718b568SThomas Cort 					char *p;
2742718b568SThomas Cort 
2752718b568SThomas Cort 					v.flag = DEFINED|ISSET|INTEGER;
2762718b568SThomas Cort 					v.type = 10; /* not default */
2772718b568SThomas Cort 					v.name[0] = '\0';
2782718b568SThomas Cort 					v_evaluate(&v, substitute(sp, 0),
2792718b568SThomas Cort 						KSH_UNWIND_ERROR);
2802718b568SThomas Cort 					sp = strchr(sp, 0) + 1;
2812718b568SThomas Cort 					for (p = str_val(&v); *p; ) {
2822718b568SThomas Cort 						Xcheck(ds, dp);
2832718b568SThomas Cort 						*dp++ = *p++;
2842718b568SThomas Cort 					}
2852718b568SThomas Cort 				}
2862718b568SThomas Cort 				continue;
2872718b568SThomas Cort 			  case OSUBST: /* ${{#}var{:}[=+-?#%]word} */
2882718b568SThomas Cort 			  /* format is:
2892718b568SThomas Cort 			   *   OSUBST [{x] plain-variable-part \0
2902718b568SThomas Cort 			   *     compiled-word-part CSUBST [}x]
2912718b568SThomas Cort 			   * This is were all syntax checking gets done...
2922718b568SThomas Cort 			   */
2932718b568SThomas Cort 			  {
2942718b568SThomas Cort 				char *varname = ++sp; /* skip the { or x (}) */
2952718b568SThomas Cort 				int stype;
2962718b568SThomas Cort 				int slen;
2972718b568SThomas Cort 
2982718b568SThomas Cort 				slen = -1;	/* XXX gcc */
2992718b568SThomas Cort 				sp = strchr(sp, '\0') + 1; /* skip variable */
3002718b568SThomas Cort 				type = varsub(&x, varname, sp, &stype, &slen);
3012718b568SThomas Cort 				if (type < 0) {
3022718b568SThomas Cort 					char endc;
3032718b568SThomas Cort 					char *str, *end;
3042718b568SThomas Cort 
3052718b568SThomas Cort 					end = (char *) wdscan(sp, CSUBST);
3062718b568SThomas Cort 					/* ({) the } or x is already skipped */
3072718b568SThomas Cort 					endc = *end;
3082718b568SThomas Cort 					*end = EOS;
3092718b568SThomas Cort 					str = snptreef((char *) 0, 64, "%S",
3102718b568SThomas Cort 							varname - 1);
3112718b568SThomas Cort 					*end = endc;
3122718b568SThomas Cort 					errorf("%s: bad substitution", str);
3132718b568SThomas Cort 				}
3142718b568SThomas Cort 				if (f&DOBLANK)
3152718b568SThomas Cort 					doblank++;
3162718b568SThomas Cort 				tilde_ok = 0;
3172718b568SThomas Cort 				if (type == XBASE) {	/* expand? */
3182718b568SThomas Cort 					if (!st->next) {
3192718b568SThomas Cort 						SubType *newst;
3202718b568SThomas Cort 
3212718b568SThomas Cort 						newst = (SubType *) alloc(
3222718b568SThomas Cort 							sizeof(SubType), ATEMP);
3232718b568SThomas Cort 						newst->next = (SubType *) 0;
3242718b568SThomas Cort 						newst->prev = st;
3252718b568SThomas Cort 						st->next = newst;
3262718b568SThomas Cort 					}
3272718b568SThomas Cort 					st = st->next;
3282718b568SThomas Cort 					st->stype = stype;
3292718b568SThomas Cort 					st->base = Xsavepos(ds, dp);
3302718b568SThomas Cort 					st->f = f;
3312718b568SThomas Cort 					st->var = x.var;
3322718b568SThomas Cort 					st->quote = quote;
3332718b568SThomas Cort 					/* skip qualifier(s) */
3342718b568SThomas Cort 					if (stype)
3352718b568SThomas Cort 						sp += slen;
3362718b568SThomas Cort 					switch (stype & 0x7f) {
3372718b568SThomas Cort 					  case '#':
3382718b568SThomas Cort 					  case '%':
3392718b568SThomas Cort 						/* ! DOBLANK,DOBRACE_,DOTILDE */
3402718b568SThomas Cort 						f = DOPAT | (f&DONTRUNCOMMAND)
3412718b568SThomas Cort 						    | DOTEMP_;
3422718b568SThomas Cort 						quote = 0;
3432718b568SThomas Cort 						/* Prepend open pattern (so |
3442718b568SThomas Cort 						 * in a trim will work as
3452718b568SThomas Cort 						 * expected)
3462718b568SThomas Cort 						 */
3472718b568SThomas Cort 						*dp++ = MAGIC;
3482718b568SThomas Cort 						*dp++ = '@' + 0x80;
3492718b568SThomas Cort 						break;
3502718b568SThomas Cort 					  case '=':
3512718b568SThomas Cort 						/* Enabling tilde expansion
3522718b568SThomas Cort 						 * after :'s here is
3532718b568SThomas Cort 						 * non-standard ksh, but is
3542718b568SThomas Cort 						 * consistent with rules for
3552718b568SThomas Cort 						 * other assignments.  Not
3562718b568SThomas Cort 						 * sure what POSIX thinks of
3572718b568SThomas Cort 						 * this.
3582718b568SThomas Cort 						 * Not doing tilde expansion
3592718b568SThomas Cort 						 * for integer variables is a
3602718b568SThomas Cort 						 * non-POSIX thing - makes
3612718b568SThomas Cort 						 * sense though, since ~ is
3622718b568SThomas Cort 						 * a arithmetic operator.
3632718b568SThomas Cort 						 */
3642718b568SThomas Cort 						if (!(x.var->flag & INTEGER))
3652718b568SThomas Cort 							f |= DOASNTILDE|DOTILDE;
3662718b568SThomas Cort 						f |= DOTEMP_;
3672718b568SThomas Cort 						/* These will be done after the
3682718b568SThomas Cort 						 * value has been assigned.
3692718b568SThomas Cort 						 */
3702718b568SThomas Cort 						f &= ~(DOBLANK|DOGLOB|DOBRACE_);
3712718b568SThomas Cort 						tilde_ok = 1;
3722718b568SThomas Cort 						break;
3732718b568SThomas Cort 					  case '?':
3742718b568SThomas Cort 						f &= ~DOBLANK;
3752718b568SThomas Cort 						f |= DOTEMP_;
3762718b568SThomas Cort 						/* fall through */
3772718b568SThomas Cort 					  default:
3782718b568SThomas Cort 						/* Enable tilde expansion */
3792718b568SThomas Cort 						tilde_ok = 1;
3802718b568SThomas Cort 						f |= DOTILDE;
3812718b568SThomas Cort 					}
3822718b568SThomas Cort 				} else
3832718b568SThomas Cort 					/* skip word */
3842718b568SThomas Cort 					sp = (char *) wdscan(sp, CSUBST);
3852718b568SThomas Cort 				continue;
3862718b568SThomas Cort 			  }
3872718b568SThomas Cort 			  case CSUBST: /* only get here if expanding word */
3882718b568SThomas Cort 				sp++; /* ({) skip the } or x */
3892718b568SThomas Cort 				tilde_ok = 0;	/* in case of ${unset:-} */
3902718b568SThomas Cort 				*dp = '\0';
3912718b568SThomas Cort 				quote = st->quote;
3922718b568SThomas Cort 				f = st->f;
3932718b568SThomas Cort 				if (f&DOBLANK)
3942718b568SThomas Cort 					doblank--;
3952718b568SThomas Cort 				switch (st->stype&0x7f) {
3962718b568SThomas Cort 				  case '#':
3972718b568SThomas Cort 				  case '%':
3982718b568SThomas Cort 					/* Append end-pattern */
3992718b568SThomas Cort 					*dp++ = MAGIC; *dp++ = ')'; *dp = '\0';
4002718b568SThomas Cort 					dp = Xrestpos(ds, dp, st->base);
4012718b568SThomas Cort 					/* Must use st->var since calling
4022718b568SThomas Cort 					 * global would break things
4032718b568SThomas Cort 					 * like x[i+=1].
4042718b568SThomas Cort 					 */
4052718b568SThomas Cort 					x.str = trimsub(str_val(st->var),
4062718b568SThomas Cort 						dp, st->stype);
4072718b568SThomas Cort 					type = XSUB;
4082718b568SThomas Cort 					if (f&DOBLANK)
4092718b568SThomas Cort 						doblank++;
4102718b568SThomas Cort 					st = st->prev;
4112718b568SThomas Cort 					continue;
4122718b568SThomas Cort 				  case '=':
4132718b568SThomas Cort 					/* Restore our position and substitute
4142718b568SThomas Cort 					 * the value of st->var (may not be
4152718b568SThomas Cort 					 * the assigned value in the presence
4162718b568SThomas Cort 					 * of integer/right-adj/etc attributes).
4172718b568SThomas Cort 					 */
4182718b568SThomas Cort 					dp = Xrestpos(ds, dp, st->base);
4192718b568SThomas Cort 					/* Must use st->var since calling
4202718b568SThomas Cort 					 * global would cause with things
4212718b568SThomas Cort 					 * like x[i+=1] to be evaluated twice.
4222718b568SThomas Cort 					 */
4232718b568SThomas Cort 					/* Note: not exported by FEXPORT
4242718b568SThomas Cort 					 * in at&t ksh.
4252718b568SThomas Cort 					 */
4262718b568SThomas Cort 					/* XXX POSIX says readonly is only
4272718b568SThomas Cort 					 * fatal for special builtins (setstr
4282718b568SThomas Cort 					 * does readonly check).
4292718b568SThomas Cort 					 */
4302718b568SThomas Cort 					len = strlen(dp) + 1;
4312718b568SThomas Cort 					setstr(st->var,
4322718b568SThomas Cort 					    debunk((char *) alloc(len, ATEMP),
4332718b568SThomas Cort 						dp, len),
4342718b568SThomas Cort 					    KSH_UNWIND_ERROR);
4352718b568SThomas Cort 					x.str = str_val(st->var);
4362718b568SThomas Cort 					type = XSUB;
4372718b568SThomas Cort 					if (f&DOBLANK)
4382718b568SThomas Cort 						doblank++;
4392718b568SThomas Cort 					st = st->prev;
4402718b568SThomas Cort 					continue;
4412718b568SThomas Cort 				  case '?':
4422718b568SThomas Cort 				    {
4432718b568SThomas Cort 					char *s = Xrestpos(ds, dp, st->base);
4442718b568SThomas Cort 
4452718b568SThomas Cort 					errorf("%s: %s", st->var->name,
4462718b568SThomas Cort 					    dp == s ?
4472718b568SThomas Cort 					      "parameter null or not set"
4482718b568SThomas Cort 					    : (debunk(s, s, strlen(s) + 1), s));
4492718b568SThomas Cort 				    }
4502718b568SThomas Cort 				}
4512718b568SThomas Cort 				st = st->prev;
4522718b568SThomas Cort 				type = XBASE;
4532718b568SThomas Cort 				continue;
4542718b568SThomas Cort 
4552718b568SThomas Cort 			  case OPAT: /* open pattern: *(foo|bar) */
4562718b568SThomas Cort 				/* Next char is the type of pattern */
4572718b568SThomas Cort 				make_magic = 1;
4582718b568SThomas Cort 				c = *sp++ + 0x80;
4592718b568SThomas Cort 				break;
4602718b568SThomas Cort 
4612718b568SThomas Cort 			  case SPAT: /* pattern separator (|) */
4622718b568SThomas Cort 				make_magic = 1;
4632718b568SThomas Cort 				c = '|';
4642718b568SThomas Cort 				break;
4652718b568SThomas Cort 
4662718b568SThomas Cort 			  case CPAT: /* close pattern */
4672718b568SThomas Cort 				make_magic = 1;
4682718b568SThomas Cort 				c = /*(*/ ')';
4692718b568SThomas Cort 				break;
4702718b568SThomas Cort 			}
4712718b568SThomas Cort 			break;
4722718b568SThomas Cort 
4732718b568SThomas Cort 		  case XNULLSUB:
4742718b568SThomas Cort 			/* Special case for "$@" (and "${foo[@]}") - no
4752718b568SThomas Cort 			 * word is generated if $# is 0 (unless there is
4762718b568SThomas Cort 			 * other stuff inside the quotes).
4772718b568SThomas Cort 			 */
4782718b568SThomas Cort 			type = XBASE;
4792718b568SThomas Cort 			if (f&DOBLANK) {
4802718b568SThomas Cort 				doblank--;
4812718b568SThomas Cort 				/* not really correct: x=; "$x$@" should
4822718b568SThomas Cort 				 * generate a null argument and
4832718b568SThomas Cort 				 * set A; "${@:+}" shouldn't.
4842718b568SThomas Cort 				 */
4852718b568SThomas Cort 				if (dp == Xstring(ds, dp))
4862718b568SThomas Cort 					word = IFS_WS;
4872718b568SThomas Cort 			}
4882718b568SThomas Cort 			continue;
4892718b568SThomas Cort 
4902718b568SThomas Cort 		  case XSUB:
4912718b568SThomas Cort 			if ((c = *x.str++) == 0) {
4922718b568SThomas Cort 				type = XBASE;
4932718b568SThomas Cort 				if (f&DOBLANK)
4942718b568SThomas Cort 					doblank--;
4952718b568SThomas Cort 				continue;
4962718b568SThomas Cort 			}
4972718b568SThomas Cort 			break;
4982718b568SThomas Cort 
4992718b568SThomas Cort 		  case XARGSEP:
5002718b568SThomas Cort 			type = XARG;
5012718b568SThomas Cort 			quote = 1;
5022718b568SThomas Cort 		  case XARG:
5032718b568SThomas Cort 			if ((c = *x.str++) == '\0') {
5042718b568SThomas Cort 				/* force null words to be created so
5052718b568SThomas Cort 				 * set -- '' 2 ''; foo "$@" will do
5062718b568SThomas Cort 				 * the right thing
5072718b568SThomas Cort 				 */
5082718b568SThomas Cort 				if (quote && x.split)
5092718b568SThomas Cort 					word = IFS_WORD;
5102718b568SThomas Cort 				if ((x.str = *x.u.strv++) == NULL) {
5112718b568SThomas Cort 					type = XBASE;
5122718b568SThomas Cort 					if (f&DOBLANK)
5132718b568SThomas Cort 						doblank--;
5142718b568SThomas Cort 					continue;
5152718b568SThomas Cort 				}
5162718b568SThomas Cort 				c = ifs0;
5172718b568SThomas Cort 				if (c == 0) {
5182718b568SThomas Cort 					if (quote && !x.split)
5192718b568SThomas Cort 						continue;
5202718b568SThomas Cort 					c = ' ';
5212718b568SThomas Cort 				}
5222718b568SThomas Cort 				if (quote && x.split) {
5232718b568SThomas Cort 					/* terminate word for "$@" */
5242718b568SThomas Cort 					type = XARGSEP;
5252718b568SThomas Cort 					quote = 0;
5262718b568SThomas Cort 				}
5272718b568SThomas Cort 			}
5282718b568SThomas Cort 			break;
5292718b568SThomas Cort 
5302718b568SThomas Cort 		  case XCOM:
5312718b568SThomas Cort 			if (newlines) {		/* Spit out saved nl's */
5322718b568SThomas Cort 				c = '\n';
5332718b568SThomas Cort 				--newlines;
5342718b568SThomas Cort 			} else {
5352718b568SThomas Cort 				while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
5362718b568SThomas Cort 				    if (c == '\n')
5372718b568SThomas Cort 					    newlines++;	/* Save newlines */
5382718b568SThomas Cort 				if (newlines && c != EOF) {
5392718b568SThomas Cort 					shf_ungetc(c, x.u.shf);
5402718b568SThomas Cort 					c = '\n';
5412718b568SThomas Cort 					--newlines;
5422718b568SThomas Cort 				}
5432718b568SThomas Cort 			}
5442718b568SThomas Cort 			if (c == EOF) {
5452718b568SThomas Cort 				newlines = 0;
5462718b568SThomas Cort 				shf_close(x.u.shf);
5472718b568SThomas Cort 				if (x.split)
5482718b568SThomas Cort 					subst_exstat = waitlast();
5492718b568SThomas Cort 				type = XBASE;
5502718b568SThomas Cort 				if (f&DOBLANK)
5512718b568SThomas Cort 					doblank--;
5522718b568SThomas Cort 				continue;
5532718b568SThomas Cort 			}
5542718b568SThomas Cort 			break;
5552718b568SThomas Cort 		}
5562718b568SThomas Cort 
5572718b568SThomas Cort 		/* check for end of word or IFS separation */
5582718b568SThomas Cort 		if (c == 0 || (!quote && (f & DOBLANK) && doblank && !make_magic
5592718b568SThomas Cort 			       && ctype(c, C_IFS)))
5602718b568SThomas Cort 		{
5612718b568SThomas Cort 			/* How words are broken up:
5622718b568SThomas Cort 			 *		   |       value of c
5632718b568SThomas Cort 			 *	  word	   |	ws	nws	0
5642718b568SThomas Cort 			 *	-----------------------------------
5652718b568SThomas Cort 			 *	IFS_WORD	w/WS	w/NWS	w
5662718b568SThomas Cort 			 *	IFS_WS		-/WS	w/NWS	-
5672718b568SThomas Cort 			 *	IFS_NWS		-/NWS	w/NWS	w
5682718b568SThomas Cort 			 *   (w means generate a word)
5692718b568SThomas Cort 			 * Note that IFS_NWS/0 generates a word (at&t ksh
5702718b568SThomas Cort 			 * doesn't do this, but POSIX does).
5712718b568SThomas Cort 			 */
5722718b568SThomas Cort 			if (word == IFS_WORD
5732718b568SThomas Cort 			    || (!ctype(c, C_IFSWS) && (c || word == IFS_NWS)))
5742718b568SThomas Cort 			{
5752718b568SThomas Cort 				char *p;
5762718b568SThomas Cort 
5772718b568SThomas Cort 				*dp++ = '\0';
5782718b568SThomas Cort 				p = Xclose(ds, dp);
5792718b568SThomas Cort #ifdef BRACE_EXPAND
5802718b568SThomas Cort 				if (fdo & DOBRACE_)
5812718b568SThomas Cort 					/* also does globbing */
5822718b568SThomas Cort 					alt_expand(wp, p, p,
5832718b568SThomas Cort 						   p + Xlength(ds, (dp - 1)),
5842718b568SThomas Cort 						   fdo | (f & DOMARKDIRS));
5852718b568SThomas Cort 				else
5862718b568SThomas Cort #endif /* BRACE_EXPAND */
5872718b568SThomas Cort 				if (fdo & DOGLOB)
5882718b568SThomas Cort 					glob(p, wp, f & DOMARKDIRS);
5892718b568SThomas Cort 				else if ((f & DOPAT) || !(fdo & DOMAGIC_))
5902718b568SThomas Cort 					XPput(*wp, p);
5912718b568SThomas Cort 				else
5922718b568SThomas Cort 					XPput(*wp, debunk(p, p, strlen(p) + 1));
5932718b568SThomas Cort 				fdo = 0;
5942718b568SThomas Cort 				saw_eq = 0;
5952718b568SThomas Cort 				tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
5962718b568SThomas Cort 				if (c != 0)
5972718b568SThomas Cort 					Xinit(ds, dp, 128, ATEMP);
5982718b568SThomas Cort 			}
5992718b568SThomas Cort 			if (c == 0)
6002718b568SThomas Cort 				return;
6012718b568SThomas Cort 			if (word != IFS_NWS)
6022718b568SThomas Cort 				word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
6032718b568SThomas Cort 		} else {
6042718b568SThomas Cort 			/* age tilde_ok info - ~ code tests second bit */
6052718b568SThomas Cort 			tilde_ok <<= 1;
6062718b568SThomas Cort 			/* mark any special second pass chars */
6072718b568SThomas Cort 			if (!quote)
6082718b568SThomas Cort 				switch (c) {
6092718b568SThomas Cort 				  case '[':
6102718b568SThomas Cort 				  case NOT:
6112718b568SThomas Cort 				  case '-':
6122718b568SThomas Cort 				  case ']':
6132718b568SThomas Cort 					/* For character classes - doesn't hurt
6142718b568SThomas Cort 					 * to have magic !,-,]'s outside of
6152718b568SThomas Cort 					 * [...] expressions.
6162718b568SThomas Cort 					 */
6172718b568SThomas Cort 					if (f & (DOPAT | DOGLOB)) {
6182718b568SThomas Cort 						fdo |= DOMAGIC_;
6192718b568SThomas Cort 						if (c == '[')
6202718b568SThomas Cort 							fdo |= f & DOGLOB;
6212718b568SThomas Cort 						*dp++ = MAGIC;
6222718b568SThomas Cort 					}
6232718b568SThomas Cort 					break;
6242718b568SThomas Cort 				  case '*':
6252718b568SThomas Cort 				  case '?':
6262718b568SThomas Cort 					if (f & (DOPAT | DOGLOB)) {
6272718b568SThomas Cort 						fdo |= DOMAGIC_ | (f & DOGLOB);
6282718b568SThomas Cort 						*dp++ = MAGIC;
6292718b568SThomas Cort 					}
6302718b568SThomas Cort 					break;
6312718b568SThomas Cort #ifdef BRACE_EXPAND
6322718b568SThomas Cort 				  case OBRACE:
6332718b568SThomas Cort 				  case ',':
6342718b568SThomas Cort 				  case CBRACE:
6352718b568SThomas Cort 					if ((f & DOBRACE_) && (c == OBRACE
6362718b568SThomas Cort 						|| (fdo & DOBRACE_)))
6372718b568SThomas Cort 					{
6382718b568SThomas Cort 						fdo |= DOBRACE_|DOMAGIC_;
6392718b568SThomas Cort 						*dp++ = MAGIC;
6402718b568SThomas Cort 					}
6412718b568SThomas Cort 					break;
6422718b568SThomas Cort #endif /* BRACE_EXPAND */
6432718b568SThomas Cort 				  case '=':
6442718b568SThomas Cort 					/* Note first unquoted = for ~ */
6452718b568SThomas Cort 					if (!(f & DOTEMP_) && !saw_eq) {
6462718b568SThomas Cort 						saw_eq = 1;
6472718b568SThomas Cort 						tilde_ok = 1;
6482718b568SThomas Cort 					}
6492718b568SThomas Cort 					break;
6502718b568SThomas Cort 				  case PATHSEP: /* : */
6512718b568SThomas Cort 					/* Note unquoted : for ~ */
6522718b568SThomas Cort 					if (!(f & DOTEMP_) && (f & DOASNTILDE))
6532718b568SThomas Cort 						tilde_ok = 1;
6542718b568SThomas Cort 					break;
6552718b568SThomas Cort 				  case '~':
6562718b568SThomas Cort 					/* tilde_ok is reset whenever
6572718b568SThomas Cort 					 * any of ' " $( $(( ${ } are seen.
6582718b568SThomas Cort 					 * Note that tilde_ok must be preserved
6592718b568SThomas Cort 					 * through the sequence ${A=a=}~
6602718b568SThomas Cort 					 */
6612718b568SThomas Cort 					if (type == XBASE
6622718b568SThomas Cort 					    && (f & (DOTILDE|DOASNTILDE))
6632718b568SThomas Cort 					    && (tilde_ok & 2))
6642718b568SThomas Cort 					{
6652718b568SThomas Cort 						char *p, *dp_x;
6662718b568SThomas Cort 
6672718b568SThomas Cort 						dp_x = dp;
6682718b568SThomas Cort 						p = maybe_expand_tilde(sp,
6692718b568SThomas Cort 							&ds, &dp_x,
6702718b568SThomas Cort 							f & DOASNTILDE);
6712718b568SThomas Cort 						if (p) {
6722718b568SThomas Cort 							if (dp != dp_x)
6732718b568SThomas Cort 								word = IFS_WORD;
6742718b568SThomas Cort 							dp = dp_x;
6752718b568SThomas Cort 							sp = p;
6762718b568SThomas Cort 							continue;
6772718b568SThomas Cort 						}
6782718b568SThomas Cort 					}
6792718b568SThomas Cort 					break;
6802718b568SThomas Cort 				}
6812718b568SThomas Cort 			else
6822718b568SThomas Cort 				quote &= ~2; /* undo temporary */
6832718b568SThomas Cort 
6842718b568SThomas Cort 			if (make_magic) {
6852718b568SThomas Cort 				make_magic = 0;
6862718b568SThomas Cort 				fdo |= DOMAGIC_ | (f & DOGLOB);
6872718b568SThomas Cort 				*dp++ = MAGIC;
6882718b568SThomas Cort 			} else if (ISMAGIC(c)) {
6892718b568SThomas Cort 				fdo |= DOMAGIC_;
6902718b568SThomas Cort 				*dp++ = MAGIC;
6912718b568SThomas Cort 			}
6922718b568SThomas Cort 			*dp++ = c; /* save output char */
6932718b568SThomas Cort 			word = IFS_WORD;
6942718b568SThomas Cort 		}
6952718b568SThomas Cort 	}
6962718b568SThomas Cort }
6972718b568SThomas Cort 
6982718b568SThomas Cort /*
6992718b568SThomas Cort  * Prepare to generate the string returned by ${} substitution.
7002718b568SThomas Cort  */
7012718b568SThomas Cort static int
varsub(xp,sp,word,stypep,slenp)7022718b568SThomas Cort varsub(xp, sp, word, stypep, slenp)
7032718b568SThomas Cort 	Expand *xp;
7042718b568SThomas Cort 	char *sp;
7052718b568SThomas Cort 	char *word;
7062718b568SThomas Cort 	int *stypep;	/* becomes qualifier type */
7072718b568SThomas Cort 	int *slenp;	/* " " len (=, :=, etc.) valid iff *stypep != 0 */
7082718b568SThomas Cort {
7092718b568SThomas Cort 	int c;
7102718b568SThomas Cort 	int state;	/* next state: XBASE, XARG, XSUB, XNULLSUB */
7112718b568SThomas Cort 	int stype;	/* substitution type */
7122718b568SThomas Cort 	int slen;
7132718b568SThomas Cort 	char *p;
7142718b568SThomas Cort 	struct tbl *vp;
7152718b568SThomas Cort 
7162718b568SThomas Cort 	if (sp[0] == '\0')	/* Bad variable name */
7172718b568SThomas Cort 		return -1;
7182718b568SThomas Cort 
7192718b568SThomas Cort 	xp->var = NULL;
7202718b568SThomas Cort 
7212718b568SThomas Cort 	/* ${#var}, string length or array size */
7222718b568SThomas Cort 	if (sp[0] == '#' && (c = sp[1]) != '\0') {
7232718b568SThomas Cort 		int zero_ok = 0;
7242718b568SThomas Cort 
7252718b568SThomas Cort 		/* Can't have any modifiers for ${#...} */
7262718b568SThomas Cort 		if (*word != CSUBST)
7272718b568SThomas Cort 			return -1;
7282718b568SThomas Cort 		sp++;
7292718b568SThomas Cort 		/* Check for size of array */
7302718b568SThomas Cort 		if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
7312718b568SThomas Cort 			int n = 0;
7322718b568SThomas Cort 			vp = global(arrayname(sp));
7332718b568SThomas Cort 			if (vp->flag & (ISSET|ARRAY))
7342718b568SThomas Cort 				zero_ok = 1;
7352718b568SThomas Cort 			for (; vp; vp = vp->u.array)
7362718b568SThomas Cort 				if (vp->flag & ISSET) {
7372718b568SThomas Cort 					n++;
7382718b568SThomas Cort 				}
7392718b568SThomas Cort 			c = n; /* ksh88/ksh93 go for number, not max index */
7402718b568SThomas Cort 		} else if (c == '*' || c == '@')
7412718b568SThomas Cort 			c = e->loc->argc;
7422718b568SThomas Cort 		else {
7432718b568SThomas Cort 			p = str_val(global(sp));
7442718b568SThomas Cort 			zero_ok = p != null;
7452718b568SThomas Cort 			c = strlen(p);
7462718b568SThomas Cort 		}
7472718b568SThomas Cort 		if (Flag(FNOUNSET) && c == 0 && !zero_ok)
7482718b568SThomas Cort 			errorf("%s: parameter not set", sp);
7492718b568SThomas Cort 		*stypep = 0; /* unqualified variable/string substitution */
7502718b568SThomas Cort 		xp->str = str_save(ulton((unsigned long)c, 10), ATEMP);
7512718b568SThomas Cort 		return XSUB;
7522718b568SThomas Cort 	}
7532718b568SThomas Cort 
7542718b568SThomas Cort 	/* Check for qualifiers in word part */
7552718b568SThomas Cort 	stype = 0;
7562718b568SThomas Cort 	c = word[slen = 0] == CHAR ? word[1] : 0;
7572718b568SThomas Cort 	if (c == ':') {
7582718b568SThomas Cort 		slen += 2;
7592718b568SThomas Cort 		stype = 0x80;
7602718b568SThomas Cort 		c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
7612718b568SThomas Cort 	}
7622718b568SThomas Cort 	if (ctype(c, C_SUBOP1)) {
7632718b568SThomas Cort 		slen += 2;
7642718b568SThomas Cort 		stype |= c;
7652718b568SThomas Cort 	} else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */
7662718b568SThomas Cort 		slen += 2;
7672718b568SThomas Cort 		stype = c;
7682718b568SThomas Cort 		if (word[slen + 0] == CHAR && c == word[slen + 1]) {
7692718b568SThomas Cort 			stype |= 0x80;
7702718b568SThomas Cort 			slen += 2;
7712718b568SThomas Cort 		}
7722718b568SThomas Cort 	} else if (stype)	/* : is not ok */
7732718b568SThomas Cort 		return -1;
7742718b568SThomas Cort 	if (!stype && *word != CSUBST)
7752718b568SThomas Cort 		return -1;
7762718b568SThomas Cort 	*stypep = stype;
7772718b568SThomas Cort 	*slenp = slen;
7782718b568SThomas Cort 
7792718b568SThomas Cort 	c = sp[0];
7802718b568SThomas Cort 	if (c == '*' || c == '@') {
7812718b568SThomas Cort 		switch (stype & 0x7f) {
7822718b568SThomas Cort 		  case '=':	/* can't assign to a vector */
7832718b568SThomas Cort 		  case '%':	/* can't trim a vector (yet) */
7842718b568SThomas Cort 		  case '#':
7852718b568SThomas Cort 			return -1;
7862718b568SThomas Cort 		}
7872718b568SThomas Cort 		if (e->loc->argc == 0) {
7882718b568SThomas Cort 			xp->u.strv = NULL;
7892718b568SThomas Cort 			xp->str = null;
7902718b568SThomas Cort 			state = c == '@' ? XNULLSUB : XSUB;
7912718b568SThomas Cort 		} else {
7922718b568SThomas Cort 			char **t = &e->loc->argv[1];
7932718b568SThomas Cort 			xp->u.strv = (void *)(uintptr_t)t;
7942718b568SThomas Cort 			xp->str = *xp->u.strv++;
7952718b568SThomas Cort 			xp->split = c == '@'; /* $@ */
7962718b568SThomas Cort 			state = XARG;
7972718b568SThomas Cort 		}
7982718b568SThomas Cort 	} else {
7992718b568SThomas Cort 		if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
8002718b568SThomas Cort 			XPtrV wv;
8012718b568SThomas Cort 
8022718b568SThomas Cort 			switch (stype & 0x7f) {
8032718b568SThomas Cort 			  case '=':	/* can't assign to a vector */
8042718b568SThomas Cort 			  case '%':	/* can't trim a vector (yet) */
8052718b568SThomas Cort 			  case '#':
8062718b568SThomas Cort 				return -1;
8072718b568SThomas Cort 			}
8082718b568SThomas Cort 			XPinit(wv, 32);
8092718b568SThomas Cort 			vp = global(arrayname(sp));
8102718b568SThomas Cort 			for (; vp; vp = vp->u.array) {
8112718b568SThomas Cort 				if (!(vp->flag&ISSET))
8122718b568SThomas Cort 					continue;
8132718b568SThomas Cort 				XPput(wv, str_val(vp));
8142718b568SThomas Cort 			}
8152718b568SThomas Cort 			if (XPsize(wv) == 0) {
8162718b568SThomas Cort 				xp->str = null;
8172718b568SThomas Cort 				state = p[1] == '@' ? XNULLSUB : XSUB;
8182718b568SThomas Cort 				XPfree(wv);
8192718b568SThomas Cort 			} else {
8202718b568SThomas Cort 				XPput(wv, 0);
8212718b568SThomas Cort 				xp->u.strv = (const char **) XPptrv(wv);
8222718b568SThomas Cort 				xp->str = *xp->u.strv++;
8232718b568SThomas Cort 				xp->split = p[1] == '@'; /* ${foo[@]} */
8242718b568SThomas Cort 				state = XARG;
8252718b568SThomas Cort 			}
8262718b568SThomas Cort 		} else {
8272718b568SThomas Cort 			/* Can't assign things like $! or $1 */
8282718b568SThomas Cort 			if ((stype & 0x7f) == '='
8292718b568SThomas Cort 			    && (ctype(*sp, C_VAR1) || digit(*sp)))
8302718b568SThomas Cort 				return -1;
8312718b568SThomas Cort 			xp->var = global(sp);
8322718b568SThomas Cort 			xp->str = str_val(xp->var);
8332718b568SThomas Cort 			state = XSUB;
8342718b568SThomas Cort 		}
8352718b568SThomas Cort 	}
8362718b568SThomas Cort 
8372718b568SThomas Cort 	c = stype&0x7f;
8382718b568SThomas Cort 	/* test the compiler's code generator */
8392718b568SThomas Cort 	if (ctype(c, C_SUBOP2) ||
8402718b568SThomas Cort 	    (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
8412718b568SThomas Cort 	     c == '=' || c == '-' || c == '?' : c == '+'))
8422718b568SThomas Cort 		state = XBASE;	/* expand word instead of variable value */
8432718b568SThomas Cort 	if (Flag(FNOUNSET) && xp->str == null
8442718b568SThomas Cort 	    && (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
8452718b568SThomas Cort 		errorf("%s: parameter not set", sp);
8462718b568SThomas Cort 	return state;
8472718b568SThomas Cort }
8482718b568SThomas Cort 
8492718b568SThomas Cort /*
8502718b568SThomas Cort  * Run the command in $(...) and read its output.
8512718b568SThomas Cort  */
8522718b568SThomas Cort static int
comsub(xp,cp)8532718b568SThomas Cort comsub(xp, cp)
8542718b568SThomas Cort 	register Expand *xp;
8552718b568SThomas Cort 	char *cp;
8562718b568SThomas Cort {
8572718b568SThomas Cort 	Source *s, *sold;
8582718b568SThomas Cort 	register struct op *t;
8592718b568SThomas Cort 	struct shf *shf;
8602718b568SThomas Cort 
8612718b568SThomas Cort 	s = pushs(SSTRING, ATEMP);
8622718b568SThomas Cort 	s->start = s->str = cp;
8632718b568SThomas Cort 	sold = source;
8642718b568SThomas Cort 	t = compile(s);
8652718b568SThomas Cort 	afree(s, ATEMP);
8662718b568SThomas Cort 	source = sold;
8672718b568SThomas Cort 
8682718b568SThomas Cort 	if (t == NULL)
8692718b568SThomas Cort 		return XBASE;
8702718b568SThomas Cort 
8712718b568SThomas Cort 	if (t != NULL && t->type == TCOM && /* $(<file) */
8722718b568SThomas Cort 	    *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
8732718b568SThomas Cort 		register struct ioword *io = *t->ioact;
8742718b568SThomas Cort 		char *name;
8752718b568SThomas Cort 
8762718b568SThomas Cort 		if ((io->flag&IOTYPE) != IOREAD)
8772718b568SThomas Cort 			errorf("funny $() command: %s",
8782718b568SThomas Cort 				snptreef((char *) 0, 32, "%R", io));
8792718b568SThomas Cort 		shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
8802718b568SThomas Cort 			SHF_MAPHI|SHF_CLEXEC);
8812718b568SThomas Cort 		if (shf == NULL)
8822718b568SThomas Cort 			errorf("%s: cannot open $() input", name);
8832718b568SThomas Cort 		xp->split = 0;	/* no waitlast() */
8842718b568SThomas Cort 	} else {
8852718b568SThomas Cort 		int ofd1, pv[2];
8862718b568SThomas Cort 		openpipe(pv);
8872718b568SThomas Cort 		shf = shf_fdopen(pv[0], SHF_RD, (struct shf *) 0);
8882718b568SThomas Cort 		ofd1 = savefd(1, 0);	/* fd 1 may be closed... */
8892718b568SThomas Cort 		if (pv[1] != 1) {
8902718b568SThomas Cort 			ksh_dup2(pv[1], 1, FALSE);
8912718b568SThomas Cort 			close(pv[1]);
8922718b568SThomas Cort 		}
8932718b568SThomas Cort 		execute(t, XFORK|XXCOM|XPIPEO);
8942718b568SThomas Cort 		restfd(1, ofd1);
8952718b568SThomas Cort 		startlast();
8962718b568SThomas Cort 		xp->split = 1;	/* waitlast() */
8972718b568SThomas Cort 	}
8982718b568SThomas Cort 
8992718b568SThomas Cort 	xp->u.shf = shf;
9002718b568SThomas Cort 	return XCOM;
9012718b568SThomas Cort }
9022718b568SThomas Cort 
9032718b568SThomas Cort /*
9042718b568SThomas Cort  * perform #pattern and %pattern substitution in ${}
9052718b568SThomas Cort  */
9062718b568SThomas Cort 
9072718b568SThomas Cort static char *
trimsub(str,pat,how)9082718b568SThomas Cort trimsub(str, pat, how)
9092718b568SThomas Cort 	register char *str;
9102718b568SThomas Cort 	char *pat;
9112718b568SThomas Cort 	int how;
9122718b568SThomas Cort {
9132718b568SThomas Cort 	register char *end = strchr(str, 0);
9142718b568SThomas Cort 	register char *p, c;
9152718b568SThomas Cort 
9162718b568SThomas Cort 	switch (how&0xff) {	/* UCHAR_MAX maybe? */
9172718b568SThomas Cort 	  case '#':		/* shortest at beginning */
9182718b568SThomas Cort 		for (p = str; p <= end; p++) {
9192718b568SThomas Cort 			c = *p; *p = '\0';
9202718b568SThomas Cort 			if (gmatch(str, pat, FALSE)) {
9212718b568SThomas Cort 				*p = c;
9222718b568SThomas Cort 				return p;
9232718b568SThomas Cort 			}
9242718b568SThomas Cort 			*p = c;
9252718b568SThomas Cort 		}
9262718b568SThomas Cort 		break;
9272718b568SThomas Cort 	  case '#'|0x80:	/* longest match at beginning */
9282718b568SThomas Cort 		for (p = end; p >= str; p--) {
9292718b568SThomas Cort 			c = *p; *p = '\0';
9302718b568SThomas Cort 			if (gmatch(str, pat, FALSE)) {
9312718b568SThomas Cort 				*p = c;
9322718b568SThomas Cort 				return p;
9332718b568SThomas Cort 			}
9342718b568SThomas Cort 			*p = c;
9352718b568SThomas Cort 		}
9362718b568SThomas Cort 		break;
9372718b568SThomas Cort 	  case '%':		/* shortest match at end */
9382718b568SThomas Cort 		for (p = end; p >= str; p--) {
9392718b568SThomas Cort 			if (gmatch(p, pat, FALSE))
9402718b568SThomas Cort 				return str_nsave(str, p - str, ATEMP);
9412718b568SThomas Cort 		}
9422718b568SThomas Cort 		break;
9432718b568SThomas Cort 	  case '%'|0x80:	/* longest match at end */
9442718b568SThomas Cort 		for (p = str; p <= end; p++) {
9452718b568SThomas Cort 			if (gmatch(p, pat, FALSE))
9462718b568SThomas Cort 				return str_nsave(str, p - str, ATEMP);
9472718b568SThomas Cort 		}
9482718b568SThomas Cort 		break;
9492718b568SThomas Cort 	}
9502718b568SThomas Cort 
9512718b568SThomas Cort 	return str;		/* no match, return string */
9522718b568SThomas Cort }
9532718b568SThomas Cort 
9542718b568SThomas Cort /*
9552718b568SThomas Cort  * glob
9562718b568SThomas Cort  * Name derived from V6's /etc/glob, the program that expanded filenames.
9572718b568SThomas Cort  */
9582718b568SThomas Cort 
9592718b568SThomas Cort /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */
9602718b568SThomas Cort static void
glob(cp,wp,markdirs)9612718b568SThomas Cort glob(cp, wp, markdirs)
9622718b568SThomas Cort 	char *cp;
9632718b568SThomas Cort 	register XPtrV *wp;
9642718b568SThomas Cort 	int markdirs;
9652718b568SThomas Cort {
9662718b568SThomas Cort 	int oldsize = XPsize(*wp);
9672718b568SThomas Cort 
9682718b568SThomas Cort 	if (glob_str(cp, wp, markdirs) == 0)
9692718b568SThomas Cort 		XPput(*wp, debunk(cp, cp, strlen(cp) + 1));
9702718b568SThomas Cort 	else
9712718b568SThomas Cort 		qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize),
9722718b568SThomas Cort 			xstrcmp);
9732718b568SThomas Cort }
9742718b568SThomas Cort 
9752718b568SThomas Cort #define GF_NONE		0
9762718b568SThomas Cort #define GF_EXCHECK	BIT(0)		/* do existence check on file */
9772718b568SThomas Cort #define GF_GLOBBED	BIT(1)		/* some globbing has been done */
9782718b568SThomas Cort #define GF_MARKDIR	BIT(2)		/* add trailing / to directories */
9792718b568SThomas Cort 
9802718b568SThomas Cort /* Apply file globbing to cp and store the matching files in wp.  Returns
9812718b568SThomas Cort  * the number of matches found.
9822718b568SThomas Cort  */
9832718b568SThomas Cort int
glob_str(cp,wp,markdirs)9842718b568SThomas Cort glob_str(cp, wp, markdirs)
9852718b568SThomas Cort 	char *cp;
9862718b568SThomas Cort 	XPtrV *wp;
9872718b568SThomas Cort 	int markdirs;
9882718b568SThomas Cort {
9892718b568SThomas Cort 	int oldsize = XPsize(*wp);
9902718b568SThomas Cort 	XString xs;
9912718b568SThomas Cort 	char *xp;
9922718b568SThomas Cort 
9932718b568SThomas Cort 	Xinit(xs, xp, 256, ATEMP);
9942718b568SThomas Cort 	globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE);
9952718b568SThomas Cort 	Xfree(xs, xp);
9962718b568SThomas Cort 
9972718b568SThomas Cort 	return XPsize(*wp) - oldsize;
9982718b568SThomas Cort }
9992718b568SThomas Cort 
10002718b568SThomas Cort static void
globit(xs,xpp,sp,wp,check)10012718b568SThomas Cort globit(xs, xpp, sp, wp, check)
10022718b568SThomas Cort 	XString *xs;		/* dest string */
10032718b568SThomas Cort 	char **xpp;		/* ptr to dest end */
10042718b568SThomas Cort 	char *sp;		/* source path */
10052718b568SThomas Cort 	register XPtrV *wp;	/* output list */
10062718b568SThomas Cort 	int check;		/* GF_* flags */
10072718b568SThomas Cort {
10082718b568SThomas Cort 	register char *np;	/* next source component */
10092718b568SThomas Cort 	char *xp = *xpp;
10102718b568SThomas Cort 	char *se;
10112718b568SThomas Cort 	char odirsep;
10122718b568SThomas Cort 
10132718b568SThomas Cort 	/* This to allow long expansions to be interrupted */
10142718b568SThomas Cort 	intrcheck();
10152718b568SThomas Cort 
10162718b568SThomas Cort 	if (sp == NULL) {	/* end of source path */
10172718b568SThomas Cort 		/* We only need to check if the file exists if a pattern
10182718b568SThomas Cort 		 * is followed by a non-pattern (eg, foo*x/bar; no check
10192718b568SThomas Cort 		 * is needed for foo* since the match must exist) or if
10202718b568SThomas Cort 		 * any patterns were expanded and the markdirs option is set.
10212718b568SThomas Cort 		 * Symlinks make things a bit tricky...
10222718b568SThomas Cort 		 */
10232718b568SThomas Cort 		if ((check & GF_EXCHECK)
10242718b568SThomas Cort 		    || ((check & GF_MARKDIR) && (check & GF_GLOBBED)))
10252718b568SThomas Cort 		{
10262718b568SThomas Cort #define stat_check()	(stat_done ? stat_done : \
10272718b568SThomas Cort 			    (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
10282718b568SThomas Cort 				? -1 : 1))
10292718b568SThomas Cort 			struct stat lstatb, statb;
10302718b568SThomas Cort 			int stat_done = 0;	 /* -1: failed, 1 ok */
10312718b568SThomas Cort 
10322718b568SThomas Cort 			if (lstat(Xstring(*xs, xp), &lstatb) < 0)
10332718b568SThomas Cort 				return;
10342718b568SThomas Cort 			/* special case for systems which strip trailing
10352718b568SThomas Cort 			 * slashes from regular files (eg, /etc/passwd/).
10362718b568SThomas Cort 			 * SunOS 4.1.3 does this...
10372718b568SThomas Cort 			 */
10382718b568SThomas Cort 			if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp)
10392718b568SThomas Cort 			    && ISDIRSEP(xp[-1]) && !S_ISDIR(lstatb.st_mode)
10402718b568SThomas Cort #ifdef S_ISLNK
10412718b568SThomas Cort 			    && (!S_ISLNK(lstatb.st_mode)
10422718b568SThomas Cort 				|| stat_check() < 0
10432718b568SThomas Cort 				|| !S_ISDIR(statb.st_mode))
10442718b568SThomas Cort #endif /* S_ISLNK */
10452718b568SThomas Cort 				)
10462718b568SThomas Cort 				return;
10472718b568SThomas Cort 			/* Possibly tack on a trailing / if there isn't already
10482718b568SThomas Cort 			 * one and if the file is a directory or a symlink to a
10492718b568SThomas Cort 			 * directory
10502718b568SThomas Cort 			 */
10512718b568SThomas Cort 			if (((check & GF_MARKDIR) && (check & GF_GLOBBED))
10522718b568SThomas Cort 			    && xp > Xstring(*xs, xp) && !ISDIRSEP(xp[-1])
10532718b568SThomas Cort 			    && (S_ISDIR(lstatb.st_mode)
10542718b568SThomas Cort #ifdef S_ISLNK
10552718b568SThomas Cort 				|| (S_ISLNK(lstatb.st_mode)
10562718b568SThomas Cort 				    && stat_check() > 0
10572718b568SThomas Cort 				    && S_ISDIR(statb.st_mode))
10582718b568SThomas Cort #endif /* S_ISLNK */
10592718b568SThomas Cort 				    ))
10602718b568SThomas Cort 			{
10612718b568SThomas Cort 				*xp++ = DIRSEP;
10622718b568SThomas Cort 				*xp = '\0';
10632718b568SThomas Cort 			}
10642718b568SThomas Cort 		}
10652718b568SThomas Cort #ifdef OS2 /* Done this way to avoid bug in gcc 2.7.2... */
10662718b568SThomas Cort     /* Ugly kludge required for command
10672718b568SThomas Cort      * completion - see how search_access()
10682718b568SThomas Cort      * is implemented for OS/2...
10692718b568SThomas Cort      */
10702718b568SThomas Cort # define KLUDGE_VAL	4
10712718b568SThomas Cort #else /* OS2 */
10722718b568SThomas Cort # define KLUDGE_VAL	0
10732718b568SThomas Cort #endif /* OS2 */
10742718b568SThomas Cort 		XPput(*wp, str_nsave(Xstring(*xs, xp), Xlength(*xs, xp)
10752718b568SThomas Cort 			+ KLUDGE_VAL, ATEMP));
10762718b568SThomas Cort 		return;
10772718b568SThomas Cort 	}
10782718b568SThomas Cort 
10792718b568SThomas Cort 	if (xp > Xstring(*xs, xp))
10802718b568SThomas Cort 		*xp++ = DIRSEP;
10812718b568SThomas Cort 	while (ISDIRSEP(*sp)) {
10822718b568SThomas Cort 		Xcheck(*xs, xp);
10832718b568SThomas Cort 		*xp++ = *sp++;
10842718b568SThomas Cort 	}
10852718b568SThomas Cort 	np = ksh_strchr_dirsep(sp);
10862718b568SThomas Cort 	if (np != NULL) {
10872718b568SThomas Cort 		se = np;
10882718b568SThomas Cort 		odirsep = *np;	/* don't assume DIRSEP, can be multiple kinds */
10892718b568SThomas Cort 		*np++ = '\0';
10902718b568SThomas Cort 	} else {
10912718b568SThomas Cort 		odirsep = '\0'; /* keep gcc quiet */
10922718b568SThomas Cort 		se = sp + strlen(sp);
10932718b568SThomas Cort 	}
10942718b568SThomas Cort 
10952718b568SThomas Cort 
10962718b568SThomas Cort 	/* Check if sp needs globbing - done to avoid pattern checks for strings
10972718b568SThomas Cort 	 * containing MAGIC characters, open ['s without the matching close ],
10982718b568SThomas Cort 	 * etc. (otherwise opendir() will be called which may fail because the
10992718b568SThomas Cort 	 * directory isn't readable - if no globbing is needed, only execute
11002718b568SThomas Cort 	 * permission should be required (as per POSIX)).
11012718b568SThomas Cort 	 */
11022718b568SThomas Cort 	if (!has_globbing(sp, se)) {
11032718b568SThomas Cort 		XcheckN(*xs, xp, se - sp + 1);
11042718b568SThomas Cort 		debunk(xp, sp, Xnleft(*xs, xp));
11052718b568SThomas Cort 		xp += strlen(xp);
11062718b568SThomas Cort 		*xpp = xp;
11072718b568SThomas Cort 		globit(xs, xpp, np, wp, check);
11082718b568SThomas Cort 	} else {
11092718b568SThomas Cort 		DIR *dirp;
11102718b568SThomas Cort 		struct dirent *d;
11112718b568SThomas Cort 		char *name;
11122718b568SThomas Cort 		int len;
11132718b568SThomas Cort 		int prefix_len;
11142718b568SThomas Cort 
11152718b568SThomas Cort 		/* xp = *xpp;	   copy_non_glob() may have re-alloc'd xs */
11162718b568SThomas Cort 		*xp = '\0';
11172718b568SThomas Cort 		prefix_len = Xlength(*xs, xp);
11182718b568SThomas Cort 		dirp = ksh_opendir(prefix_len ? Xstring(*xs, xp) : ".");
11192718b568SThomas Cort 		if (dirp == NULL)
11202718b568SThomas Cort 			goto Nodir;
11212718b568SThomas Cort 		while ((d = readdir(dirp)) != NULL) {
11222718b568SThomas Cort 			name = d->d_name;
11232718b568SThomas Cort 			if ((*name == '.' && *sp != '.')
11242718b568SThomas Cort 			    || !gmatch(name, sp, TRUE))
11252718b568SThomas Cort 				continue;
11262718b568SThomas Cort 
11272718b568SThomas Cort 			len = NLENGTH(d) + 1;
11282718b568SThomas Cort 			XcheckN(*xs, xp, len);
11292718b568SThomas Cort 			memcpy(xp, name, len);
11302718b568SThomas Cort 			*xpp = xp + len - 1;
11312718b568SThomas Cort 			globit(xs, xpp, np, wp,
11322718b568SThomas Cort 				(check & GF_MARKDIR) | GF_GLOBBED
11332718b568SThomas Cort 				| (np ? GF_EXCHECK : GF_NONE));
11342718b568SThomas Cort 			xp = Xstring(*xs, xp) + prefix_len;
11352718b568SThomas Cort 		}
11362718b568SThomas Cort 		closedir(dirp);
11372718b568SThomas Cort 	  Nodir:;
11382718b568SThomas Cort 	}
11392718b568SThomas Cort 
11402718b568SThomas Cort 	if (np != NULL)
11412718b568SThomas Cort 		*--np = odirsep;
11422718b568SThomas Cort }
11432718b568SThomas Cort 
11442718b568SThomas Cort #if 0
11452718b568SThomas Cort /* Check if p contains something that needs globbing; if it does, 0 is
11462718b568SThomas Cort  * returned; if not, p is copied into xs/xp after stripping any MAGICs
11472718b568SThomas Cort  */
11482718b568SThomas Cort static int	copy_non_glob ARGS((XString *xs, char **xpp, char *p));
11492718b568SThomas Cort static int
11502718b568SThomas Cort copy_non_glob(xs, xpp, p)
11512718b568SThomas Cort 	XString *xs;
11522718b568SThomas Cort 	char **xpp;
11532718b568SThomas Cort 	char *p;
11542718b568SThomas Cort {
11552718b568SThomas Cort 	char *xp;
11562718b568SThomas Cort 	int len = strlen(p);
11572718b568SThomas Cort 
11582718b568SThomas Cort 	XcheckN(*xs, *xpp, len);
11592718b568SThomas Cort 	xp = *xpp;
11602718b568SThomas Cort 	for (; *p; p++) {
11612718b568SThomas Cort 		if (ISMAGIC(*p)) {
11622718b568SThomas Cort 			int c = *++p;
11632718b568SThomas Cort 
11642718b568SThomas Cort 			if (c == '*' || c == '?')
11652718b568SThomas Cort 				return 0;
11662718b568SThomas Cort 			if (*p == '[') {
11672718b568SThomas Cort 				char *q = p + 1;
11682718b568SThomas Cort 
11692718b568SThomas Cort 				if (ISMAGIC(*q) && q[1] == NOT)
11702718b568SThomas Cort 					q += 2;
11712718b568SThomas Cort 				if (ISMAGIC(*q) && q[1] == ']')
11722718b568SThomas Cort 					q += 2;
11732718b568SThomas Cort 				for (; *q; q++)
11742718b568SThomas Cort 					if (ISMAGIC(*q) && *++q == ']')
11752718b568SThomas Cort 						return 0;
11762718b568SThomas Cort 				/* pass a literal [ through */
11772718b568SThomas Cort 			}
11782718b568SThomas Cort 			/* must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, etc. */
11792718b568SThomas Cort 		}
11802718b568SThomas Cort 		*xp++ = *p;
11812718b568SThomas Cort 	}
11822718b568SThomas Cort 	*xp = '\0';
11832718b568SThomas Cort 	*xpp = xp;
11842718b568SThomas Cort 	return 1;
11852718b568SThomas Cort }
11862718b568SThomas Cort #endif /* 0 */
11872718b568SThomas Cort 
11882718b568SThomas Cort /* remove MAGIC from string */
11892718b568SThomas Cort char *
debunk(dp,sp,dlen)11902718b568SThomas Cort debunk(dp, sp, dlen)
11912718b568SThomas Cort 	char *dp;
11922718b568SThomas Cort 	const char *sp;
11932718b568SThomas Cort 	size_t dlen;
11942718b568SThomas Cort {
11952718b568SThomas Cort 	char *d, *s;
11962718b568SThomas Cort 
11972718b568SThomas Cort 	if ((s = strchr(sp, MAGIC))) {
11982718b568SThomas Cort 		if (s - sp >= (ptrdiff_t)dlen)
11992718b568SThomas Cort 			return dp;
12002718b568SThomas Cort 		memcpy(dp, sp, s - sp);
12012718b568SThomas Cort 		for (d = dp + (s - sp); *s && (d - dp < (ptrdiff_t)dlen); s++)
12022718b568SThomas Cort 			if (!ISMAGIC(*s) || !(*++s & 0x80)
12032718b568SThomas Cort 			    || !strchr("*+?@! ", *s & 0x7f))
12042718b568SThomas Cort 				*d++ = *s;
12052718b568SThomas Cort 			else {
12062718b568SThomas Cort 				/* extended pattern operators: *+?@! */
12072718b568SThomas Cort 				if ((*s & 0x7f) != ' ')
12082718b568SThomas Cort 					*d++ = *s & 0x7f;
12092718b568SThomas Cort 				if (d - dp < (ptrdiff_t)dlen)
12102718b568SThomas Cort 					*d++ = '(';
12112718b568SThomas Cort 			}
12122718b568SThomas Cort 		*d = '\0';
12132718b568SThomas Cort 	} else if (dp != sp)
12142718b568SThomas Cort 		strlcpy(dp, sp, dlen);
12152718b568SThomas Cort 	return dp;
12162718b568SThomas Cort }
12172718b568SThomas Cort 
12182718b568SThomas Cort /* Check if p is an unquoted name, possibly followed by a / or :.  If so
12192718b568SThomas Cort  * puts the expanded version in *dcp,dp and returns a pointer in p just
12202718b568SThomas Cort  * past the name, otherwise returns 0.
12212718b568SThomas Cort  */
12222718b568SThomas Cort static char *
maybe_expand_tilde(p,dsp,dpp,isassign)12232718b568SThomas Cort maybe_expand_tilde(p, dsp, dpp, isassign)
12242718b568SThomas Cort 	char *p;
12252718b568SThomas Cort 	XString *dsp;
12262718b568SThomas Cort 	char **dpp;
12272718b568SThomas Cort 	int isassign;
12282718b568SThomas Cort {
12292718b568SThomas Cort 	XString ts;
12302718b568SThomas Cort 	char *dp = *dpp;
12312718b568SThomas Cort 	char *tp, *r;
12322718b568SThomas Cort 
12332718b568SThomas Cort 	Xinit(ts, tp, 16, ATEMP);
12342718b568SThomas Cort 	/* : only for DOASNTILDE form */
12352718b568SThomas Cort 	while (p[0] == CHAR && !ISDIRSEP(p[1])
12362718b568SThomas Cort 	       && (!isassign || p[1] != PATHSEP))
12372718b568SThomas Cort 	{
12382718b568SThomas Cort 		Xcheck(ts, tp);
12392718b568SThomas Cort 		*tp++ = p[1];
12402718b568SThomas Cort 		p += 2;
12412718b568SThomas Cort 	}
12422718b568SThomas Cort 	*tp = '\0';
12432718b568SThomas Cort 	r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? tilde(Xstring(ts, tp)) : (char *) 0;
12442718b568SThomas Cort 	Xfree(ts, tp);
12452718b568SThomas Cort 	if (r) {
12462718b568SThomas Cort 		while (*r) {
12472718b568SThomas Cort 			Xcheck(*dsp, dp);
12482718b568SThomas Cort 			if (ISMAGIC(*r))
12492718b568SThomas Cort 				*dp++ = MAGIC;
12502718b568SThomas Cort 			*dp++ = *r++;
12512718b568SThomas Cort 		}
12522718b568SThomas Cort 		*dpp = dp;
12532718b568SThomas Cort 		r = p;
12542718b568SThomas Cort 	}
12552718b568SThomas Cort 	return r;
12562718b568SThomas Cort }
12572718b568SThomas Cort 
12582718b568SThomas Cort /*
12592718b568SThomas Cort  * tilde expansion
12602718b568SThomas Cort  *
12612718b568SThomas Cort  * based on a version by Arnold Robbins
12622718b568SThomas Cort  */
12632718b568SThomas Cort 
12642718b568SThomas Cort static char *
tilde(cp)12652718b568SThomas Cort tilde(cp)
12662718b568SThomas Cort 	char *cp;
12672718b568SThomas Cort {
12682718b568SThomas Cort 	char *dp;
12692718b568SThomas Cort 
12702718b568SThomas Cort 	if (cp[0] == '\0')
12712718b568SThomas Cort 		dp = str_val(global("HOME"));
12722718b568SThomas Cort 	else if (cp[0] == '+' && cp[1] == '\0')
12732718b568SThomas Cort 		dp = str_val(global("PWD"));
12742718b568SThomas Cort 	else if (cp[0] == '-' && cp[1] == '\0')
12752718b568SThomas Cort 		dp = str_val(global("OLDPWD"));
12762718b568SThomas Cort 	else
12772718b568SThomas Cort 		dp = homedir(cp);
12782718b568SThomas Cort 	/* If HOME, PWD or OLDPWD are not set, don't expand ~ */
12792718b568SThomas Cort 	if (dp == null)
12802718b568SThomas Cort 		dp = (char *) 0;
12812718b568SThomas Cort 	return dp;
12822718b568SThomas Cort }
12832718b568SThomas Cort 
12842718b568SThomas Cort /*
12852718b568SThomas Cort  * map userid to user's home directory.
12862718b568SThomas Cort  * note that 4.3's getpw adds more than 6K to the shell,
12872718b568SThomas Cort  * and the YP version probably adds much more.
12882718b568SThomas Cort  * we might consider our own version of getpwnam() to keep the size down.
12892718b568SThomas Cort  */
12902718b568SThomas Cort 
12912718b568SThomas Cort static char *
homedir(name)12922718b568SThomas Cort homedir(name)
12932718b568SThomas Cort 	char *name;
12942718b568SThomas Cort {
12952718b568SThomas Cort 	register struct tbl *ap;
12962718b568SThomas Cort 
12972718b568SThomas Cort 	ap = tenter(&homedirs, name, hash(name));
12982718b568SThomas Cort 	if (!(ap->flag & ISSET)) {
12992718b568SThomas Cort #ifdef OS2
13002718b568SThomas Cort 		/* No usernames in OS2 - punt */
13012718b568SThomas Cort 		return NULL;
13022718b568SThomas Cort #else /* OS2 */
13032718b568SThomas Cort 		struct passwd *pw;
13042718b568SThomas Cort 		size_t n;
13052718b568SThomas Cort 
13062718b568SThomas Cort 		pw = getpwnam(name);
13072718b568SThomas Cort 		if (pw == NULL)
13082718b568SThomas Cort 			return NULL;
13092718b568SThomas Cort 		n = strlen(pw->pw_dir);
13102718b568SThomas Cort 		if (n > 0 && '/' != pw->pw_dir[n - 1]) {
13112718b568SThomas Cort 			ap->val.s = str_nsave(pw->pw_dir, n + 1, APERM);
13122718b568SThomas Cort 			ap->val.s[n] = '/';
13132718b568SThomas Cort 			ap->val.s[n + 1] = '\0';
13142718b568SThomas Cort 		} else {
13152718b568SThomas Cort 			ap->val.s = str_save(pw->pw_dir, APERM);
13162718b568SThomas Cort 		}
13172718b568SThomas Cort 		ap->flag |= DEFINED|ISSET|ALLOC;
13182718b568SThomas Cort #endif /* OS2 */
13192718b568SThomas Cort 	}
13202718b568SThomas Cort 	return ap->val.s;
13212718b568SThomas Cort }
13222718b568SThomas Cort 
13232718b568SThomas Cort #ifdef BRACE_EXPAND
13242718b568SThomas Cort static void
alt_expand(wp,start,exp_start,end,fdo)13252718b568SThomas Cort alt_expand(wp, start, exp_start, end, fdo)
13262718b568SThomas Cort 	XPtrV *wp;
13272718b568SThomas Cort 	char *start, *exp_start;
13282718b568SThomas Cort 	char *end;
13292718b568SThomas Cort 	int fdo;
13302718b568SThomas Cort {
13312718b568SThomas Cort 	int UNINITIALIZED(count);
13322718b568SThomas Cort 	char *brace_start, *brace_end, *UNINITIALIZED(comma);
13332718b568SThomas Cort 	char *field_start;
13342718b568SThomas Cort 	char *p;
13352718b568SThomas Cort 
13362718b568SThomas Cort 	/* search for open brace */
13372718b568SThomas Cort 	for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2)
13382718b568SThomas Cort 		;
13392718b568SThomas Cort 	brace_start = p;
13402718b568SThomas Cort 
13412718b568SThomas Cort 	/* find matching close brace, if any */
13422718b568SThomas Cort 	if (p) {
13432718b568SThomas Cort 		comma = (char *) 0;
13442718b568SThomas Cort 		count = 1;
13452718b568SThomas Cort 		for (p += 2; *p && count; p++) {
13462718b568SThomas Cort 			if (ISMAGIC(*p)) {
13472718b568SThomas Cort 				if (*++p == OBRACE)
13482718b568SThomas Cort 					count++;
13492718b568SThomas Cort 				else if (*p == CBRACE)
13502718b568SThomas Cort 					--count;
13512718b568SThomas Cort 				else if (*p == ',' && count == 1)
13522718b568SThomas Cort 					comma = p;
13532718b568SThomas Cort 			}
13542718b568SThomas Cort 		}
13552718b568SThomas Cort 	}
13562718b568SThomas Cort 	/* no valid expansions... */
13572718b568SThomas Cort 	if (!p || count != 0) {
13582718b568SThomas Cort 		/* Note that given a{{b,c} we do not expand anything (this is
13592718b568SThomas Cort 		 * what at&t ksh does.  This may be changed to do the {b,c}
13602718b568SThomas Cort 		 * expansion. }
13612718b568SThomas Cort 		 */
13622718b568SThomas Cort 		if (fdo & DOGLOB)
13632718b568SThomas Cort 			glob(start, wp, fdo & DOMARKDIRS);
13642718b568SThomas Cort 		else
13652718b568SThomas Cort 			XPput(*wp, debunk(start, start, end - start));
13662718b568SThomas Cort 		return;
13672718b568SThomas Cort 	}
13682718b568SThomas Cort 	brace_end = p;
13692718b568SThomas Cort 	if (!comma) {
13702718b568SThomas Cort 		alt_expand(wp, start, brace_end, end, fdo);
13712718b568SThomas Cort 		return;
13722718b568SThomas Cort 	}
13732718b568SThomas Cort 
13742718b568SThomas Cort 	/* expand expression */
13752718b568SThomas Cort 	field_start = brace_start + 2;
13762718b568SThomas Cort 	count = 1;
13772718b568SThomas Cort 	for (p = brace_start + 2; p != brace_end; p++) {
13782718b568SThomas Cort 		if (ISMAGIC(*p)) {
13792718b568SThomas Cort 			if (*++p == OBRACE)
13802718b568SThomas Cort 				count++;
13812718b568SThomas Cort 			else if ((*p == CBRACE && --count == 0)
13822718b568SThomas Cort 				 || (*p == ',' && count == 1))
13832718b568SThomas Cort 			{
13842718b568SThomas Cort 				char *new;
13852718b568SThomas Cort 				int l1, l2, l3;
13862718b568SThomas Cort 
13872718b568SThomas Cort 				l1 = brace_start - start;
13882718b568SThomas Cort 				l2 = (p - 1) - field_start;
13892718b568SThomas Cort 				l3 = end - brace_end;
13902718b568SThomas Cort 				new = (char *) alloc(l1 + l2 + l3 + 1, ATEMP);
13912718b568SThomas Cort 				memcpy(new, start, l1);
13922718b568SThomas Cort 				memcpy(new + l1, field_start, l2);
13932718b568SThomas Cort 				memcpy(new + l1 + l2, brace_end, l3);
13942718b568SThomas Cort 				new[l1 + l2 + l3] = '\0';
13952718b568SThomas Cort 				alt_expand(wp, new, new + l1,
13962718b568SThomas Cort 					   new + l1 + l2 + l3, fdo);
13972718b568SThomas Cort 				field_start = p + 1;
13982718b568SThomas Cort 			}
13992718b568SThomas Cort 		}
14002718b568SThomas Cort 	}
14012718b568SThomas Cort 	return;
14022718b568SThomas Cort }
14032718b568SThomas Cort #endif /* BRACE_EXPAND */
1404