xref: /onnv-gate/usr/src/lib/libshell/common/sh/macro.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1982-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                  David Korn <dgk@research.att.com>                   *
184887Schin *                                                                      *
194887Schin ***********************************************************************/
204887Schin #pragma prototyped
214887Schin /*
224887Schin  * Shell macro expander
234887Schin  * expands ~
244887Schin  * expands ${...}
254887Schin  * expands $(...)
264887Schin  * expands $((...))
274887Schin  * expands `...`
284887Schin  *
294887Schin  *   David Korn
304887Schin  *   AT&T Labs
314887Schin  *
324887Schin  */
334887Schin 
344887Schin #include	"defs.h"
354887Schin #include	<fcin.h>
364887Schin #include	<pwd.h>
374887Schin #include	"name.h"
384887Schin #include	"variables.h"
394887Schin #include	"shlex.h"
404887Schin #include	"io.h"
4110898Sroland.mainz@nrubsig.org #include	"jobs.h"
424887Schin #include	"shnodes.h"
434887Schin #include	"path.h"
444887Schin #include	"national.h"
454887Schin #include	"streval.h"
464887Schin 
474887Schin #undef STR_GROUP
484887Schin #ifndef STR_GROUP
494887Schin #   define STR_GROUP	0
504887Schin #endif
514887Schin 
524887Schin #if !SHOPT_MULTIBYTE
534887Schin #define mbchar(p)       (*(unsigned char*)p++)
544887Schin #endif
554887Schin 
564887Schin static int	_c_;
574887Schin typedef struct  _mac_
584887Schin {
594887Schin 	Shell_t		*shp;		/* pointer to shell interpreter */
604887Schin 	Sfio_t		*sp;		/* stream pointer for here-document */
614887Schin 	struct argnod	**arghead;	/* address of head of argument list */
624887Schin 	char		*ifsp;		/* pointer to IFS value */
634887Schin 	int		fields;		/* number of fields */
644887Schin 	short		quoted;		/* set when word has quotes */
654887Schin 	unsigned char	ifs;		/* first char of IFS */
664887Schin 	char		quote;		/* set within double quoted contexts */
674887Schin 	char		lit;		/* set within single quotes */
684887Schin 	char		split;		/* set when word splittin is possible */
694887Schin 	char		pattern;	/* set when file expansion follows */
704887Schin 	char		patfound;	/* set if pattern character found */
714887Schin 	char		assign;		/* set for assignments */
724887Schin 	char		arith;		/* set for ((...)) */
734887Schin 	char		let;		/* set when expanding let arguments */
744887Schin 	char		zeros;		/* strip leading zeros when set */
758462SApril.Chin@Sun.COM 	char		arrayok;	/* $x[] ok for arrays */
768462SApril.Chin@Sun.COM 	char		subcopy;	/* set when copying subscript */
778462SApril.Chin@Sun.COM 	int		dotdot;		/* set for .. in subscript */
784887Schin 	void		*nvwalk;	/* for name space walking*/
794887Schin } Mac_t;
804887Schin 
814887Schin #undef ESCAPE
824887Schin #define ESCAPE		'\\'
834887Schin #define isescchar(s)	((s)>S_QUOTE)
844887Schin #define isqescchar(s)	((s)>=S_QUOTE)
854887Schin #define isbracechar(c)	((c)==RBRACE || (_c_=sh_lexstates[ST_BRACE][c])==S_MOD1 ||_c_==S_MOD2)
864887Schin #define ltos(x)		fmtbase((long)(x),0,0)
874887Schin 
884887Schin /* type of macro expansions */
894887Schin #define M_BRACE		1	/* ${var}	*/
904887Schin #define M_TREE		2	/* ${var.}	*/
914887Schin #define M_SIZE		3	/* ${#var}	*/
924887Schin #define M_VNAME		4	/* ${!var}	*/
934887Schin #define M_SUBNAME	5	/* ${!var[sub]}	*/
944887Schin #define M_NAMESCAN	6	/* ${!var*}	*/
954887Schin #define M_NAMECOUNT	7	/* ${#var*}	*/
964887Schin #define M_TYPE		8	/* ${@var}	*/
974887Schin 
984887Schin static int	substring(const char*, const char*, int[], int);
994887Schin static void	copyto(Mac_t*, int, int);
1008462SApril.Chin@Sun.COM static void	comsubst(Mac_t*, Shnode_t*, int);
1014887Schin static int	varsub(Mac_t*);
1024887Schin static void	mac_copy(Mac_t*,const char*, int);
1038462SApril.Chin@Sun.COM static void	tilde_expand2(Shell_t*,int);
1048462SApril.Chin@Sun.COM static char 	*sh_tilde(Shell_t*,const char*);
1058462SApril.Chin@Sun.COM static char	*special(Shell_t *,int);
1064887Schin static void	endfield(Mac_t*,int);
1074887Schin static void	mac_error(Namval_t*);
1084887Schin static char	*mac_getstring(char*);
1094887Schin static int	charlen(const char*,int);
1104887Schin #if SHOPT_MULTIBYTE
1114887Schin     static char	*lastchar(const char*,const char*);
1124887Schin #endif /* SHOPT_MULTIBYTE */
1134887Schin 
sh_macopen(Shell_t * shp)1144887Schin void *sh_macopen(Shell_t *shp)
1154887Schin {
1164887Schin 	void *addr = newof(0,Mac_t,1,0);
1174887Schin 	Mac_t *mp = (Mac_t*)addr;
1184887Schin 	mp->shp = shp;
1194887Schin 	return(addr);
1204887Schin }
1214887Schin 
1224887Schin /*
1234887Schin  * perform only parameter substitution and catch failures
1244887Schin  */
sh_mactry(Shell_t * shp,register char * string)1258462SApril.Chin@Sun.COM char *sh_mactry(Shell_t *shp,register char *string)
1264887Schin {
1274887Schin 	if(string)
1284887Schin 	{
1294887Schin 		int		jmp_val;
1308462SApril.Chin@Sun.COM 		int		savexit = shp->savexit;
1314887Schin 		struct checkpt	buff;
1324887Schin 		sh_pushcontext(&buff,SH_JMPSUB);
1334887Schin 		jmp_val = sigsetjmp(buff.buff,0);
1344887Schin 		if(jmp_val == 0)
1358462SApril.Chin@Sun.COM 			string = sh_mactrim(shp,string,0);
1364887Schin 		sh_popcontext(&buff);
1378462SApril.Chin@Sun.COM 		shp->savexit = savexit;
1384887Schin 		return(string);
1394887Schin 	}
1404887Schin 	return("");
1414887Schin }
1424887Schin 
1434887Schin /*
1444887Schin  * Perform parameter expansion, command substitution, and arithmetic
1454887Schin  * expansion on <str>.
1464887Schin  * If <mode> greater than 1 file expansion is performed if the result
1474887Schin  * yields a single pathname.
1484887Schin  * If <mode> negative, than expansion rules for assignment are applied.
1494887Schin  */
sh_mactrim(Shell_t * shp,char * str,register int mode)1508462SApril.Chin@Sun.COM char *sh_mactrim(Shell_t *shp, char *str, register int mode)
1514887Schin {
1528462SApril.Chin@Sun.COM 	register Mac_t	*mp = (Mac_t*)shp->mac_context;
1538462SApril.Chin@Sun.COM 	Stk_t		*stkp = shp->stk;
1548462SApril.Chin@Sun.COM 	Mac_t		savemac;
1554887Schin 	savemac = *mp;
1568462SApril.Chin@Sun.COM 	stkseek(stkp,0);
1574887Schin 	mp->arith = (mode==3);
1584887Schin 	mp->let = 0;
1598462SApril.Chin@Sun.COM 	shp->argaddr = 0;
1604887Schin 	mp->pattern = (mode==1||mode==2);
1614887Schin 	mp->patfound = 0;
1628462SApril.Chin@Sun.COM 	mp->assign = 0;
1638462SApril.Chin@Sun.COM 	if(mode<0)
1648462SApril.Chin@Sun.COM 		mp->assign = -mode;
1654887Schin 	mp->quoted = mp->lit = mp->split = mp->quote = 0;
1664887Schin 	mp->sp = 0;
1678462SApril.Chin@Sun.COM 	if(mp->ifsp=nv_getval(sh_scoped(shp,IFSNOD)))
1684887Schin 		mp->ifs = *mp->ifsp;
1694887Schin 	else
1704887Schin 		mp->ifs = ' ';
1718462SApril.Chin@Sun.COM 	stkseek(stkp,0);
1724887Schin 	fcsopen(str);
1734887Schin 	copyto(mp,0,mp->arith);
1748462SApril.Chin@Sun.COM 	str = stkfreeze(stkp,1);
1754887Schin 	if(mode==2)
1764887Schin 	{
1774887Schin 		/* expand only if unique */
1784887Schin 		struct argnod *arglist=0;
1794887Schin 		if((mode=path_expand(str,&arglist))==1)
1804887Schin 			str = arglist->argval;
1814887Schin 		else if(mode>1)
1824887Schin 			errormsg(SH_DICT,ERROR_exit(1),e_ambiguous,str);
1834887Schin 		sh_trim(str);
1844887Schin 	}
1854887Schin 	*mp = savemac;
1864887Schin 	return(str);
1874887Schin }
1884887Schin 
1894887Schin /*
1904887Schin  * Perform all the expansions on the argument <argp>
1914887Schin  */
sh_macexpand(Shell_t * shp,register struct argnod * argp,struct argnod ** arghead,int flag)1928462SApril.Chin@Sun.COM int sh_macexpand(Shell_t* shp, register struct argnod *argp, struct argnod **arghead,int flag)
1934887Schin {
1948462SApril.Chin@Sun.COM 	register int	flags = argp->argflag;
1958462SApril.Chin@Sun.COM 	register char	*str = argp->argval;
1968462SApril.Chin@Sun.COM 	register Mac_t  *mp = (Mac_t*)shp->mac_context;
1978462SApril.Chin@Sun.COM 	char		**saveargaddr = shp->argaddr;
1988462SApril.Chin@Sun.COM 	Mac_t		savemac;
1998462SApril.Chin@Sun.COM 	Stk_t		*stkp = shp->stk;
2004887Schin 	savemac = *mp;
2014887Schin 	mp->sp = 0;
2028462SApril.Chin@Sun.COM 	if(mp->ifsp=nv_getval(sh_scoped(shp,IFSNOD)))
2034887Schin 		mp->ifs = *mp->ifsp;
2044887Schin 	else
2054887Schin 		mp->ifs = ' ';
2068462SApril.Chin@Sun.COM 	if((flag&ARG_OPTIMIZE) && !shp->indebug)
2078462SApril.Chin@Sun.COM 		shp->argaddr = (char**)&argp->argchn.ap;
2084887Schin 	else
2098462SApril.Chin@Sun.COM 		shp->argaddr = 0;
2104887Schin 	mp->arghead = arghead;
2114887Schin 	mp->quoted = mp->lit = mp->quote = 0;
2124887Schin 	mp->arith = ((flag&ARG_ARITH)!=0);
2134887Schin 	mp->let = ((flag&ARG_LET)!=0);
2144887Schin 	mp->split = !(flag&ARG_ASSIGN);
2154887Schin 	mp->assign = !mp->split;
2164887Schin 	mp->pattern = mp->split && !(flag&ARG_NOGLOB) && !sh_isoption(SH_NOGLOB);
2178462SApril.Chin@Sun.COM 	mp->arrayok = mp->arith || (flag&ARG_ARRAYOK);
2184887Schin 	str = argp->argval;
2194887Schin 	fcsopen(str);
2204887Schin 	mp->fields = 0;
2214887Schin 	if(!arghead)
2224887Schin 	{
2234887Schin 		mp->split = 0;
2244887Schin 		mp->pattern = ((flag&ARG_EXP)!=0);
2258462SApril.Chin@Sun.COM 		stkseek(stkp,0);
2264887Schin 	}
2274887Schin 	else
2284887Schin 	{
2298462SApril.Chin@Sun.COM 		stkseek(stkp,ARGVAL);
2308462SApril.Chin@Sun.COM 		*stkptr(stkp,ARGVAL-1) = 0;
2314887Schin 	}
2324887Schin 	mp->patfound = 0;
2338462SApril.Chin@Sun.COM 	if(mp->pattern)
2348462SApril.Chin@Sun.COM 		mp->arrayok = 0;
2354887Schin 	copyto(mp,0,mp->arith);
2364887Schin 	if(!arghead)
2374887Schin 	{
2388462SApril.Chin@Sun.COM 		argp->argchn.cp = stkfreeze(stkp,1);
2398462SApril.Chin@Sun.COM 		if(shp->argaddr)
2404887Schin 			argp->argflag |= ARG_MAKE;
2414887Schin 	}
2424887Schin 	else
2434887Schin 	{
2444887Schin 		endfield(mp,mp->quoted);
2454887Schin 		flags = mp->fields;
2468462SApril.Chin@Sun.COM 		if(flags==1 && shp->argaddr)
2474887Schin 			argp->argchn.ap = *arghead;
2484887Schin 	}
2498462SApril.Chin@Sun.COM 	shp->argaddr = saveargaddr;
2504887Schin 	*mp = savemac;
2514887Schin 	return(flags);
2524887Schin }
2534887Schin 
2544887Schin /*
2554887Schin  * Expand here document which is stored in <infile> or <string>
2564887Schin  * The result is written to <outfile>
2574887Schin  */
sh_machere(Shell_t * shp,Sfio_t * infile,Sfio_t * outfile,char * string)2588462SApril.Chin@Sun.COM void sh_machere(Shell_t *shp,Sfio_t *infile, Sfio_t *outfile, char *string)
2594887Schin {
2604887Schin 	register int	c,n;
2614887Schin 	register const char	*state = sh_lexstates[ST_QUOTE];
2624887Schin 	register char	*cp;
2638462SApril.Chin@Sun.COM 	register Mac_t	*mp = (Mac_t*)shp->mac_context;
2648462SApril.Chin@Sun.COM 	Lex_t		*lp = (Lex_t*)mp->shp->lex_context;
2654887Schin 	Fcin_t		save;
2664887Schin 	Mac_t		savemac;
2678462SApril.Chin@Sun.COM 	Stk_t		*stkp = shp->stk;
2684887Schin 	savemac = *mp;
2698462SApril.Chin@Sun.COM 	stkseek(stkp,0);
2708462SApril.Chin@Sun.COM 	shp->argaddr = 0;
2714887Schin 	mp->sp = outfile;
2724887Schin 	mp->split = mp->assign = mp->pattern = mp->patfound = mp->lit = mp->arith = mp->let = 0;
2734887Schin 	mp->quote = 1;
2748462SApril.Chin@Sun.COM 	mp->ifsp = nv_getval(sh_scoped(shp,IFSNOD));
2754887Schin 	mp->ifs = ' ';
2764887Schin 	fcsave(&save);
2774887Schin 	if(infile)
2784887Schin 		fcfopen(infile);
2794887Schin 	else
2804887Schin 		fcsopen(string);
2818462SApril.Chin@Sun.COM 	fcnotify(0,lp);
2824887Schin 	cp = fcseek(0);
2834887Schin 	while(1)
2844887Schin 	{
2854887Schin #if SHOPT_MULTIBYTE
2864887Schin 		if(mbwide())
2874887Schin 		{
2884887Schin 			do
2894887Schin 			{
2904887Schin 				ssize_t len;
2914887Schin 				switch(len = mbsize(cp))
2924887Schin 				{
2934887Schin 				    case -1:	/* illegal multi-byte char */
2944887Schin 				    case 0:
2954887Schin 				    case 1:
2964887Schin 					n=state[*(unsigned char*)cp++];
2974887Schin 					break;
2984887Schin 				    default:
2998462SApril.Chin@Sun.COM 					/* use state of alpha character */
3004887Schin 					n=state['a'];
3014887Schin 					cp += len;
3024887Schin 				}
3034887Schin 			}
3044887Schin 			while(n == 0);
3054887Schin 		}
3064887Schin 		else
3074887Schin #endif /* SHOPT_MULTIBYTE */
3084887Schin 		while((n=state[*(unsigned char*)cp++])==0);
3094887Schin 		if(n==S_NL || n==S_QUOTE || n==S_RBRA)
3104887Schin 			continue;
3114887Schin 		if(c=(cp-1)-fcseek(0))
3124887Schin 			sfwrite(outfile,fcseek(0),c);
3134887Schin 		cp = fcseek(c+1);
3144887Schin 		switch(n)
3154887Schin 		{
3164887Schin 		    case S_EOF:
3174887Schin 			if((n=fcfill()) <=0)
3184887Schin 			{
3194887Schin 				/* ignore 0 byte when reading from file */
3204887Schin 				if(n==0 && fcfile())
3214887Schin 					continue;
3224887Schin 				fcrestore(&save);
3234887Schin 				*mp = savemac;
3244887Schin 				return;
3254887Schin 			}
3264887Schin 			cp = fcseek(-1);
3274887Schin 			continue;
3284887Schin 		    case S_ESC:
3294887Schin 			fcgetc(c);
3304887Schin 			cp=fcseek(-1);
3314887Schin 			if(c>0)
3324887Schin 				cp++;
3334887Schin 			if(!isescchar(state[c]))
3344887Schin 				sfputc(outfile,ESCAPE);
3354887Schin 			continue;
3364887Schin 		    case S_GRAVE:
3378462SApril.Chin@Sun.COM 			comsubst(mp,(Shnode_t*)0,0);
3384887Schin 			break;
3394887Schin 		    case S_DOL:
3404887Schin 			c = fcget();
3414887Schin 			if(c=='.')
3424887Schin 				goto regular;
3434887Schin 		    again:
3444887Schin 			switch(n=sh_lexstates[ST_DOL][c])
3454887Schin 			{
3464887Schin 			    case S_ALP: case S_SPC1: case S_SPC2:
3474887Schin 			    case S_DIG: case S_LBRA:
3484887Schin 			    {
3494887Schin 				Fcin_t	save2;
3508462SApril.Chin@Sun.COM 				int	offset = stktell(stkp);
3514887Schin 				int	offset2;
3528462SApril.Chin@Sun.COM 				sfputc(stkp,c);
3534887Schin 				if(n==S_LBRA)
3548462SApril.Chin@Sun.COM 				{
3558462SApril.Chin@Sun.COM 					c = fcget();
3568462SApril.Chin@Sun.COM 					fcseek(-1);
3578462SApril.Chin@Sun.COM 					if(sh_lexstates[ST_NORM][c]==S_BREAK)
3588462SApril.Chin@Sun.COM 					{
3598462SApril.Chin@Sun.COM 						comsubst(mp,(Shnode_t*)0,2);
3608462SApril.Chin@Sun.COM 						break;
3618462SApril.Chin@Sun.COM 					}
3628462SApril.Chin@Sun.COM 					sh_lexskip(lp,RBRACE,1,ST_BRACE);
3638462SApril.Chin@Sun.COM 				}
3644887Schin 				else if(n==S_ALP)
3654887Schin 				{
3664887Schin 					while(fcgetc(c),isaname(c))
3678462SApril.Chin@Sun.COM 						sfputc(stkp,c);
3684887Schin 					fcseek(-1);
3694887Schin 				}
3708462SApril.Chin@Sun.COM 				sfputc(stkp,0);
3718462SApril.Chin@Sun.COM 				offset2 = stktell(stkp);
3724887Schin 				fcsave(&save2);
3738462SApril.Chin@Sun.COM 				fcsopen(stkptr(stkp,offset));
3744887Schin 				varsub(mp);
3758462SApril.Chin@Sun.COM 				if(c=stktell(stkp)-offset2)
3768462SApril.Chin@Sun.COM 					sfwrite(outfile,(char*)stkptr(stkp,offset2),c);
3774887Schin 				fcrestore(&save2);
3788462SApril.Chin@Sun.COM 				stkseek(stkp,offset);
3794887Schin 				break;
3804887Schin 			    }
3814887Schin 			    case S_PAR:
3828462SApril.Chin@Sun.COM 				comsubst(mp,(Shnode_t*)0,1);
3834887Schin 				break;
3844887Schin 			    case S_EOF:
3854887Schin 				if((c=fcfill()) > 0)
3864887Schin 					goto again;
3874887Schin 				/* FALL THRU */
3884887Schin 			    default:
3894887Schin 			    regular:
3904887Schin 				sfputc(outfile,'$');
3914887Schin 				fcseek(-1);
3924887Schin 				break;
3934887Schin 			}
3944887Schin 		}
3954887Schin 		cp = fcseek(0);
3964887Schin 	}
3974887Schin }
3984887Schin 
3994887Schin /*
4004887Schin  * expand argument but do not trim pattern characters
4014887Schin  */
sh_macpat(Shell_t * shp,register struct argnod * arg,int flags)4028462SApril.Chin@Sun.COM char *sh_macpat(Shell_t *shp,register struct argnod *arg, int flags)
4034887Schin {
4044887Schin 	register char *sp = arg->argval;
4054887Schin 	if((arg->argflag&ARG_RAW))
4064887Schin 		return(sp);
4078462SApril.Chin@Sun.COM 	sh_stats(STAT_ARGEXPAND);
4084887Schin 	if(flags&ARG_OPTIMIZE)
4094887Schin 		arg->argchn.ap=0;
4104887Schin 	if(!(sp=arg->argchn.cp))
4114887Schin 	{
4128462SApril.Chin@Sun.COM 		sh_macexpand(shp,arg,NIL(struct argnod**),flags|ARG_ARRAYOK);
4134887Schin 		sp = arg->argchn.cp;
4144887Schin 		if(!(flags&ARG_OPTIMIZE) || !(arg->argflag&ARG_MAKE))
4154887Schin 			arg->argchn.cp = 0;
4164887Schin 		arg->argflag &= ~ARG_MAKE;
4174887Schin 	}
4184887Schin 	else
4198462SApril.Chin@Sun.COM 		sh_stats(STAT_ARGHITS);
4204887Schin 	return(sp);
4214887Schin }
4224887Schin 
4234887Schin /*
4244887Schin  * Process the characters up to <endch> or end of input string
4254887Schin  */
copyto(register Mac_t * mp,int endch,int newquote)4264887Schin static void copyto(register Mac_t *mp,int endch, int newquote)
4274887Schin {
4284887Schin 	register int	c,n;
4294887Schin 	register const char	*state = sh_lexstates[ST_MACRO];
4304887Schin 	register char	*cp,*first;
4318462SApril.Chin@Sun.COM 	Lex_t		*lp = (Lex_t*)mp->shp->lex_context;
4324887Schin 	int		tilde = -1;
4334887Schin 	int		oldquote = mp->quote;
4344887Schin 	int		ansi_c = 0;
4354887Schin 	int		paren = 0;
4364887Schin 	int		ere = 0;
4374887Schin 	int		brace = 0;
4384887Schin 	Sfio_t		*sp = mp->sp;
4398462SApril.Chin@Sun.COM 	Stk_t		*stkp = mp->shp->stk;
4404887Schin 	mp->sp = NIL(Sfio_t*);
4414887Schin 	mp->quote = newquote;
4424887Schin 	first = cp = fcseek(0);
4438900SCasper.Dik@Sun.COM 	if(!mp->quote && *cp=='~' && cp[1]!=LPAREN)
4448462SApril.Chin@Sun.COM 		tilde = stktell(stkp);
4454887Schin 	/* handle // operator specially */
4464887Schin 	if(mp->pattern==2 && *cp=='/')
4474887Schin 		cp++;
4484887Schin 	while(1)
4494887Schin 	{
4504887Schin #if SHOPT_MULTIBYTE
4514887Schin 		if(mbwide())
4524887Schin 		{
4534887Schin 			ssize_t len;
4544887Schin 			do
4554887Schin 			{
4564887Schin 				switch(len = mbsize(cp))
4574887Schin 				{
4584887Schin 				    case -1:	/* illegal multi-byte char */
4594887Schin 				    case 0:
4604887Schin 					len = 1;
4614887Schin 				    case 1:
4624887Schin 					n = state[*(unsigned char*)cp++];
4634887Schin 					break;
4644887Schin 				    default:
4654887Schin 					/* treat as if alpha */
4664887Schin 					cp += len;
4674887Schin 					n=state['a'];
4684887Schin 				}
4694887Schin 			}
4704887Schin 			while(n == 0);
4714887Schin 			c = (cp-len) - first;
4724887Schin 		}
4734887Schin 		else
4744887Schin #endif /* SHOPT_MULTIBYTE */
4754887Schin 		{
4764887Schin 			while((n=state[*(unsigned char*)cp++])==0);
4774887Schin 			c = (cp-1) - first;
4784887Schin 		}
4794887Schin 		switch(n)
4804887Schin 		{
4814887Schin 		    case S_ESC:
4824887Schin 			if(ansi_c)
4834887Schin 			{
4844887Schin 				/* process ANSI-C escape character */
4854887Schin 				char *addr= --cp;
4864887Schin 				if(c)
4878462SApril.Chin@Sun.COM 					sfwrite(stkp,first,c);
4884887Schin 				c = chresc(cp,&addr);
4894887Schin 				cp = addr;
4904887Schin 				first = fcseek(cp-first);
4914887Schin #if SHOPT_MULTIBYTE
4924887Schin 				if(c > UCHAR_MAX && mbwide())
4934887Schin 				{
4944887Schin 					int		i;
4954887Schin 					unsigned char	mb[8];
4964887Schin 
4974887Schin 					n = wctomb((char*)mb, c);
4984887Schin 					for(i=0;i<n;i++)
4998462SApril.Chin@Sun.COM 						sfputc(stkp,mb[i]);
5004887Schin 				}
5014887Schin 				else
5024887Schin #endif /* SHOPT_MULTIBYTE */
5038462SApril.Chin@Sun.COM 				sfputc(stkp,c);
5044887Schin 				if(c==ESCAPE && mp->pattern)
5058462SApril.Chin@Sun.COM 					sfputc(stkp,ESCAPE);
5064887Schin 				break;
5074887Schin 			}
5084887Schin 			else if(sh_isoption(SH_BRACEEXPAND) && mp->pattern==4 && (*cp==',' || *cp==LBRACE || *cp==RBRACE || *cp=='.'))
5094887Schin 				break;
5104887Schin 			else if(mp->split && endch && !mp->quote && !mp->lit)
5114887Schin 			{
5124887Schin 				if(c)
5134887Schin 					mac_copy(mp,first,c);
5144887Schin 				cp = fcseek(c+2);
5154887Schin 				if(c= cp[-1])
5164887Schin 				{
5178462SApril.Chin@Sun.COM 					sfputc(stkp,c);
5184887Schin 					if(c==ESCAPE)
5198462SApril.Chin@Sun.COM 						sfputc(stkp,ESCAPE);
5204887Schin 				}
5214887Schin 				else
5224887Schin 					cp--;
5234887Schin 				first = cp;
5244887Schin 				break;
5254887Schin 			}
5264887Schin 			n = state[*(unsigned char*)cp];
5274887Schin 			if(n==S_ENDCH && *cp!=endch)
5284887Schin 				n = S_PAT;
5294887Schin 			if(mp->pattern)
5304887Schin 			{
5314887Schin 				/* preserve \digit for pattern matching */
5324887Schin 				/* also \alpha for extended patterns */
53310898Sroland.mainz@nrubsig.org 				if(!mp->lit && !mp->quote)
53410898Sroland.mainz@nrubsig.org 				{
53510898Sroland.mainz@nrubsig.org 					if((n==S_DIG || ((paren+ere) && sh_lexstates[ST_DOL][*(unsigned char*)cp]==S_ALP)))
53610898Sroland.mainz@nrubsig.org 						break;
53710898Sroland.mainz@nrubsig.org 					if(ere && mp->pattern==1 && strchr(".[()*+?{|^$&!",*cp))
53810898Sroland.mainz@nrubsig.org 						break;
53910898Sroland.mainz@nrubsig.org 				}
5404887Schin 				/* followed by file expansion */
5414887Schin 				if(!mp->lit && (n==S_ESC || (!mp->quote &&
5424887Schin 					(n==S_PAT||n==S_ENDCH||n==S_SLASH||n==S_BRACT||*cp=='-'))))
5434887Schin 				{
5444887Schin 					cp += (n!=S_EOF);
5458462SApril.Chin@Sun.COM 					if(ere && n==S_ESC && *cp =='\\' && cp[1]=='$')
5468462SApril.Chin@Sun.COM 					{
5478462SApril.Chin@Sun.COM 						/* convert \\\$ into \$' */
5488462SApril.Chin@Sun.COM 						sfwrite(stkp,first,c+1);
5498462SApril.Chin@Sun.COM 						cp = first = fcseek(c+3);
5508462SApril.Chin@Sun.COM 					}
5514887Schin 					break;
5524887Schin 				}
5538462SApril.Chin@Sun.COM 				if(!(ere && *cp=='$') && (mp->lit || (mp->quote && !isqescchar(n) && n!=S_ENDCH)))
5544887Schin 				{
5554887Schin 					/* add \ for file expansion */
5568462SApril.Chin@Sun.COM 					sfwrite(stkp,first,c+1);
5574887Schin 					first = fcseek(c);
5584887Schin 					break;
5594887Schin 				}
5604887Schin 			}
5614887Schin 			if(mp->lit)
5624887Schin 				break;
5634887Schin 			if(!mp->quote || isqescchar(n) || n==S_ENDCH)
5644887Schin 			{
5654887Schin 				/* eliminate \ */
5664887Schin 				if(c)
5678462SApril.Chin@Sun.COM 					sfwrite(stkp,first,c);
5684887Schin 				/* check new-line joining */
5694887Schin 				first = fcseek(c+1);
5704887Schin 			}
5714887Schin 			cp += (n!=S_EOF);
5724887Schin 			break;
5734887Schin 		    case S_GRAVE: case S_DOL:
5744887Schin 			if(mp->lit)
5754887Schin 				break;
5764887Schin 			if(c)
5774887Schin 			{
5784887Schin 				if(mp->split && !mp->quote && endch)
5794887Schin 					mac_copy(mp,first,c);
5804887Schin 				else
5818462SApril.Chin@Sun.COM 					sfwrite(stkp,first,c);
5824887Schin 			}
5834887Schin 			first = fcseek(c+1);
5844887Schin 			c = mp->pattern;
5854887Schin 			if(n==S_GRAVE)
5868462SApril.Chin@Sun.COM 				comsubst(mp,(Shnode_t*)0,0);
5874887Schin 			else if((n= *cp)==0 || !varsub(mp))
5884887Schin 			{
5894887Schin 				if(n=='\'' && !mp->quote)
5904887Schin 					ansi_c = 1;
5914887Schin 				else if(mp->quote || n!='"')
5928462SApril.Chin@Sun.COM 					sfputc(stkp,'$');
5934887Schin 			}
5944887Schin 			cp = first = fcseek(0);
5954887Schin 			if(*cp)
5964887Schin 				mp->pattern = c;
5974887Schin 			break;
5984887Schin 		    case S_ENDCH:
5994887Schin 			if((mp->lit || cp[-1]!=endch || mp->quote!=newquote))
6004887Schin 				goto pattern;
6014887Schin 			if(endch==RBRACE && *cp==LPAREN && mp->pattern && brace)
6024887Schin 				goto pattern;
6034887Schin 		    case S_EOF:
6044887Schin 			if(c)
6054887Schin 			{
6064887Schin 				if(mp->split && !mp->quote && !mp->lit && endch)
6074887Schin 					mac_copy(mp,first,c);
6084887Schin 				else
6098462SApril.Chin@Sun.COM 					sfwrite(stkp,first,c);
6104887Schin 			}
6114887Schin 			c += (n!=S_EOF);
6124887Schin 			first = fcseek(c);
6134887Schin 			if(tilde>=0)
6148462SApril.Chin@Sun.COM 				tilde_expand2(mp->shp,tilde);
6154887Schin 			goto done;
6164887Schin 		    case S_QUOTE:
6174887Schin 			if(mp->lit || mp->arith)
6184887Schin 				break;
6194887Schin 		    case S_LIT:
6204887Schin 			if(mp->arith)
6214887Schin 			{
6224887Schin 				if((*cp=='`' || *cp=='[') && cp[1]=='\'')
6234887Schin 					cp +=2;
6244887Schin 				break;
6254887Schin 			}
6264887Schin 			if(n==S_LIT && mp->quote)
6274887Schin 				break;
6284887Schin 			if(c)
6294887Schin 			{
6304887Schin 				if(mp->split && endch && !mp->quote && !mp->lit)
6314887Schin 					mac_copy(mp,first,c);
6324887Schin 				else
6338462SApril.Chin@Sun.COM 					sfwrite(stkp,first,c);
6344887Schin 			}
6354887Schin 			first = fcseek(c+1);
6364887Schin 			if(n==S_LIT)
6374887Schin 			{
6384887Schin 				if(mp->quote)
6394887Schin 					continue;
6404887Schin 				if(mp->lit)
6414887Schin 					mp->lit = ansi_c = 0;
6424887Schin 				else
6434887Schin 					mp->lit = 1;
6444887Schin 			}
6454887Schin 			else
6464887Schin 				mp->quote = !mp->quote;
6474887Schin 			mp->quoted++;
6484887Schin 			break;
6494887Schin 		    case S_BRACT:
6508462SApril.Chin@Sun.COM 			if(mp->arith || (((mp->assign&1) || endch==RBRACT) &&
6514887Schin 				!(mp->quote || mp->lit)))
6524887Schin 			{
6534887Schin 				int offset=0,oldpat = mp->pattern;
6548462SApril.Chin@Sun.COM 				int oldarith = mp->arith, oldsub=mp->subcopy;
6558462SApril.Chin@Sun.COM 				sfwrite(stkp,first,++c);
65610898Sroland.mainz@nrubsig.org 				if(mp->assign&1)
65710898Sroland.mainz@nrubsig.org 				{
65810898Sroland.mainz@nrubsig.org 					if(first[c-2]=='.')
65910898Sroland.mainz@nrubsig.org 						offset = stktell(stkp);
66010898Sroland.mainz@nrubsig.org 					if(isastchar(*cp) && cp[1]==']')
66110898Sroland.mainz@nrubsig.org 						errormsg(SH_DICT,ERROR_exit(1),
66210898Sroland.mainz@nrubsig.org e_badsubscript,*cp);
66310898Sroland.mainz@nrubsig.org 				}
6644887Schin 				first = fcseek(c);
6654887Schin 				mp->pattern = 4;
6664887Schin 				mp->arith = 0;
6678462SApril.Chin@Sun.COM 				mp->subcopy = 0;
6684887Schin 				copyto(mp,RBRACT,0);
6698462SApril.Chin@Sun.COM 				mp->subcopy = oldsub;
6704887Schin 				mp->arith = oldarith;
6714887Schin 				mp->pattern = oldpat;
6728462SApril.Chin@Sun.COM 				sfputc(stkp,RBRACT);
6734887Schin 				if(offset)
6744887Schin 				{
6758462SApril.Chin@Sun.COM 					cp = stkptr(stkp,stktell(stkp));
6768462SApril.Chin@Sun.COM 					if(sh_checkid(stkptr(stkp,offset),cp)!=cp)
6778462SApril.Chin@Sun.COM 						stkseek(stkp,stktell(stkp)-2);
6784887Schin 				}
6794887Schin 				cp = first = fcseek(0);
6804887Schin 				break;
6814887Schin 			}
6824887Schin 		    case S_PAT:
6834887Schin 			if(mp->pattern && !(mp->quote || mp->lit))
6844887Schin 			{
6854887Schin 				mp->patfound = mp->pattern;
6864887Schin 				if((n=cp[-1])==LPAREN)
6874887Schin 				{
6884887Schin 					paren++;
6894887Schin 					if((cp-first)>1 && cp[-2]=='~')
6904887Schin 					{
6914887Schin 						char *p = cp;
6924887Schin 						while((c=mbchar(p)) && c!=RPAREN && c!='E');
69310898Sroland.mainz@nrubsig.org 						ere = (c=='E'||c=='A');
6944887Schin 					}
6954887Schin 				}
6964887Schin 				else if(n==RPAREN)
6974887Schin 					--paren;
6984887Schin 			}
6994887Schin 			goto pattern;
7008462SApril.Chin@Sun.COM 		    case S_COM:
7018462SApril.Chin@Sun.COM 			if(mp->pattern==4 && (mp->quote || mp->lit))
7028462SApril.Chin@Sun.COM 			{
7038462SApril.Chin@Sun.COM 				if(c)
7048462SApril.Chin@Sun.COM 				{
7058462SApril.Chin@Sun.COM 					sfwrite(stkp,first,c);
7068462SApril.Chin@Sun.COM 					first = fcseek(c);
7078462SApril.Chin@Sun.COM 				}
7088462SApril.Chin@Sun.COM 				sfputc(stkp,ESCAPE);
7098462SApril.Chin@Sun.COM 			}
7108462SApril.Chin@Sun.COM 			break;
7114887Schin 		    case S_BRACE:
7124887Schin 			if(!(mp->quote || mp->lit))
7134887Schin 			{
7144887Schin 				mp->patfound = mp->split && sh_isoption(SH_BRACEEXPAND);
7154887Schin 				brace = 1;
7164887Schin 			}
7174887Schin 		    pattern:
7184887Schin 			if(!mp->pattern || !(mp->quote || mp->lit))
7194887Schin 			{
7204887Schin 				/* mark beginning of {a,b} */
7214887Schin 				if(n==S_BRACE && endch==0 && mp->pattern)
7224887Schin 					mp->pattern=4;
7234887Schin 				if(n==S_SLASH && mp->pattern==2)
7244887Schin 					mp->pattern=3;
7254887Schin 				break;
7264887Schin 			}
7274887Schin 			if(mp->pattern==3)
7284887Schin 				break;
7294887Schin 			if(c)
7308462SApril.Chin@Sun.COM 				sfwrite(stkp,first,c);
7314887Schin 			first = fcseek(c);
7328462SApril.Chin@Sun.COM 			sfputc(stkp,ESCAPE);
7334887Schin 			break;
7344887Schin 		    case S_EQ:
7354887Schin 			if(mp->assign==1)
7364887Schin 			{
7374887Schin 				if(*cp=='~' && !endch && !mp->quote && !mp->lit)
7388462SApril.Chin@Sun.COM 					tilde = stktell(stkp)+(c+1);
7394887Schin 				mp->assign = 2;
7404887Schin 			}
7414887Schin 			break;
7424887Schin 		    case S_SLASH:
7434887Schin 		    case S_COLON:
7444887Schin 			if(tilde >=0)
7454887Schin 			{
7464887Schin 				if(c)
7478462SApril.Chin@Sun.COM 					sfwrite(stkp,first,c);
7484887Schin 				first = fcseek(c);
7498462SApril.Chin@Sun.COM 				tilde_expand2(mp->shp,tilde);
7504887Schin 				tilde = -1;
7514887Schin 				c=0;
7524887Schin 			}
7534887Schin 			if(n==S_COLON && mp->assign==2 && *cp=='~' && endch==0 && !mp->quote &&!mp->lit)
7548462SApril.Chin@Sun.COM 				tilde = stktell(stkp)+(c+1);
7554887Schin 			else if(n==S_SLASH && mp->pattern==2)
7564887Schin #if 0
7574887Schin 				goto pattern;
7584887Schin #else
7594887Schin 			{
7604887Schin 				if(mp->quote || mp->lit)
7614887Schin 					goto pattern;
7628462SApril.Chin@Sun.COM 				sfwrite(stkp,first,c+1);
7634887Schin 				first = fcseek(c+1);
7648462SApril.Chin@Sun.COM 				c = stktell(stkp);
7658462SApril.Chin@Sun.COM 				sh_lexskip(lp,RBRACE,0,ST_NESTED);
7668462SApril.Chin@Sun.COM 				stkseek(stkp,c);
7674887Schin 				cp = fcseek(-1);
7688462SApril.Chin@Sun.COM 				sfwrite(stkp,first,cp-first);
7694887Schin 				first=cp;
7704887Schin 			}
7714887Schin #endif
7724887Schin 			break;
7738462SApril.Chin@Sun.COM 		    case S_DOT:
7748462SApril.Chin@Sun.COM 			if(*cp=='.' && mp->subcopy==1)
7758462SApril.Chin@Sun.COM 			{
7768462SApril.Chin@Sun.COM 				sfwrite(stkp,first,c);
7778462SApril.Chin@Sun.COM 				sfputc(stkp,0);
7788462SApril.Chin@Sun.COM 				mp->dotdot = stktell(stkp);
7798462SApril.Chin@Sun.COM 				cp = first = fcseek(c+2);
7808462SApril.Chin@Sun.COM 			}
7818462SApril.Chin@Sun.COM 			break;
7824887Schin 		}
7834887Schin 	}
7844887Schin done:
7854887Schin 	mp->sp = sp;
7864887Schin 	mp->quote = oldquote;
7874887Schin }
7884887Schin 
7894887Schin /*
7904887Schin  * copy <str> to stack performing sub-expression substitutions
7914887Schin  */
mac_substitute(Mac_t * mp,register char * cp,char * str,register int subexp[],int subsize)7924887Schin static void mac_substitute(Mac_t *mp, register char *cp,char *str,register int subexp[],int subsize)
7934887Schin {
7948462SApril.Chin@Sun.COM 	register int	c,n;
7954887Schin 	register char *first=fcseek(0);
7968462SApril.Chin@Sun.COM 	char		*ptr;
7978462SApril.Chin@Sun.COM 	Mac_t		savemac;
7988462SApril.Chin@Sun.COM 	Stk_t		*stkp = mp->shp->stk;
7998462SApril.Chin@Sun.COM 	n = stktell(stkp);
8004887Schin 	savemac = *mp;
8014887Schin 	mp->pattern = 3;
8024887Schin 	mp->split = 0;
8034887Schin 	fcsopen(cp);
8044887Schin 	copyto(mp,0,0);
8058462SApril.Chin@Sun.COM 	sfputc(stkp,0);
8068462SApril.Chin@Sun.COM 	ptr = cp = strdup(stkptr(stkp,n));
8078462SApril.Chin@Sun.COM 	stkseek(stkp,n);
8084887Schin 	*mp = savemac;
8094887Schin 	fcsopen(first);
8104887Schin 	first = cp;
8114887Schin 	while(1)
8124887Schin 	{
8134887Schin 		while((c= *cp++) && c!=ESCAPE);
8144887Schin 		if(c==0)
8154887Schin 			break;
8164887Schin 		if((n= *cp++)=='\\' || n==RBRACE || (n>='0' && n<='9' && (n-='0')<subsize))
8174887Schin 		{
8184887Schin 			c = cp-first-2;
8194887Schin 			if(c)
8204887Schin 				mac_copy(mp,first,c);
8214887Schin 			first=cp;
8224887Schin 			if(n=='\\' || n==RBRACE)
8234887Schin 			{
8244887Schin 				first--;
8254887Schin 				continue;
8264887Schin 			}
8274887Schin 			if((c=subexp[2*n])>=0)
8284887Schin 			{
8294887Schin 				if((n=subexp[2*n+1]-c)>0)
8304887Schin 					mac_copy(mp,str+c,n);
8314887Schin 			}
8324887Schin 		}
8334887Schin 		else if(n==0)
8344887Schin 			break;
8354887Schin 	}
8364887Schin 	if(n=cp-first-1)
8374887Schin 		mac_copy(mp,first,n);
8384887Schin 	free(ptr);
8394887Schin }
8404887Schin 
8414887Schin #if  SHOPT_FILESCAN
8424887Schin #define	MAX_OFFSETS	 (sizeof(shp->offsets)/sizeof(shp->offsets[0]))
8434887Schin #define MAX_ARGN	(32*1024)
8444887Schin 
8454887Schin /*
8464887Schin  * compute the arguments $1 ... $n and $# from the current line as needed
8474887Schin  * save line offsets in the offsets array.
8484887Schin  */
getdolarg(Shell_t * shp,int n,int * size)8494887Schin static char *getdolarg(Shell_t *shp, int n, int *size)
8504887Schin {
8514887Schin 	register int c=S_DELIM, d=shp->ifstable['\\'];
8524887Schin 	register unsigned char *first,*last,*cp = (unsigned char*)shp->cur_line;
8534887Schin 	register int m=shp->offsets[0],delim=0;
8544887Schin 	if(m==0)
8554887Schin 		return(0);
8564887Schin 	if(m<0)
8574887Schin 		m = 0;
8584887Schin 	else if(n<=m)
8594887Schin 		m = n-1;
8604887Schin 	else
8614887Schin 		m--;
8624887Schin 	if(m >= MAX_OFFSETS-1)
8634887Schin 		m =  MAX_OFFSETS-2;
8644887Schin 	cp += shp->offsets[m+1];
8654887Schin 	n -= m;
8664887Schin 	shp->ifstable['\\'] = 0;
8674887Schin 	shp->ifstable[0] = S_EOF;
8684887Schin 	while(1)
8694887Schin 	{
8704887Schin 		if(c==S_DELIM)
8714887Schin 			while(shp->ifstable[*cp++]==S_SPACE);
8724887Schin 		first = --cp;
8734887Schin 		if(++m < MAX_OFFSETS)
8744887Schin 			shp->offsets[m] = (first-(unsigned char*)shp->cur_line);
8754887Schin 		while((c=shp->ifstable[*cp++])==0);
8764887Schin 		last = cp-1;
8774887Schin 		if(c==S_SPACE)
8784887Schin 			while((c=shp->ifstable[*cp++])==S_SPACE);
8794887Schin 		if(--n==0 || c==S_EOF)
8804887Schin 		{
8814887Schin 			if(last==first && c==S_EOF && (!delim || (m>1)))
8824887Schin 			{
8834887Schin 				n++;
8844887Schin 				m--;
8854887Schin 			}
8864887Schin 			break;
8874887Schin 		}
8884887Schin 		delim = (c==S_DELIM);
8894887Schin 	}
8904887Schin 	shp->ifstable['\\'] = d;
8914887Schin 	if(m > shp->offsets[0])
8924887Schin 		shp->offsets[0] = m;
8934887Schin 	if(n)
8944887Schin 		first = last = 0;
8954887Schin 	if(size)
8964887Schin 		*size = last-first;
8974887Schin 	return((char*)first);
8984887Schin }
8994887Schin #endif /* SHOPT_FILESCAN */
9004887Schin 
9014887Schin /*
9024887Schin  * get the prefix after name reference resolution
9034887Schin  */
prefix(Shell_t * shp,char * id)9048462SApril.Chin@Sun.COM static char *prefix(Shell_t *shp, char *id)
9054887Schin {
9064887Schin 	Namval_t *np;
90710898Sroland.mainz@nrubsig.org 	register char *sub=0, *cp = strchr(id,'.');
9084887Schin 	if(cp)
9094887Schin 	{
9104887Schin 		*cp = 0;
9118462SApril.Chin@Sun.COM 		np = nv_search(id, shp->var_tree,0);
9124887Schin 		*cp = '.';
9134887Schin 		if(isastchar(cp[1]))
9144887Schin 			cp[1] = 0;
9154887Schin 		if(np && nv_isref(np))
9164887Schin 		{
9174887Schin 			int n;
9184887Schin 			char *sp;
9198462SApril.Chin@Sun.COM 			shp->argaddr = 0;
92010898Sroland.mainz@nrubsig.org 			while(nv_isref(np) && np->nvalue.cp)
92110898Sroland.mainz@nrubsig.org 			{
92210898Sroland.mainz@nrubsig.org 				sub = nv_refsub(np);
9234887Schin 				np = nv_refnode(np);
92410898Sroland.mainz@nrubsig.org 				if(sub)
92510898Sroland.mainz@nrubsig.org 					nv_putsub(np,sub,0L);
92610898Sroland.mainz@nrubsig.org 			}
92710898Sroland.mainz@nrubsig.org 			id = (char*)malloc(strlen(cp)+1+(n=strlen(sp=nv_name(np)))+ (sub?strlen(sub)+3:1));
92810898Sroland.mainz@nrubsig.org 			memcpy(id,sp,n);
92910898Sroland.mainz@nrubsig.org 			if(sub)
93010898Sroland.mainz@nrubsig.org 			{
93110898Sroland.mainz@nrubsig.org 				id[n++] = '[';
93210898Sroland.mainz@nrubsig.org 				strcpy(&id[n],sub);
93310898Sroland.mainz@nrubsig.org 				n+= strlen(sub)+1;
93410898Sroland.mainz@nrubsig.org 				id[n-1] = ']';
93510898Sroland.mainz@nrubsig.org 			}
9364887Schin 			strcpy(&id[n],cp);
9374887Schin 			return(id);
9384887Schin 		}
9394887Schin 	}
9404887Schin 	return(strdup(id));
9414887Schin }
9424887Schin 
9434887Schin /*
9444887Schin  * copy to ']' onto the stack and return offset to it
9454887Schin  */
subcopy(Mac_t * mp,int flag)9464887Schin static int subcopy(Mac_t *mp, int flag)
9474887Schin {
9484887Schin 	int split = mp->split;
9494887Schin 	int xpattern = mp->pattern;
9508462SApril.Chin@Sun.COM 	int loc = stktell(mp->shp->stk);
9514887Schin 	int xarith = mp->arith;
9528462SApril.Chin@Sun.COM 	int arrayok = mp->arrayok;
9534887Schin 	mp->split = 0;
9544887Schin 	mp->arith = 0;
9554887Schin 	mp->pattern = flag?4:0;
9568462SApril.Chin@Sun.COM 	mp->arrayok=1;
9578462SApril.Chin@Sun.COM 	mp->subcopy++;
9588462SApril.Chin@Sun.COM 	mp->dotdot = 0;
9594887Schin 	copyto(mp,RBRACT,0);
9608462SApril.Chin@Sun.COM 	mp->subcopy = 0;
9614887Schin 	mp->pattern = xpattern;
9624887Schin 	mp->split = split;
9634887Schin 	mp->arith = xarith;
9648462SApril.Chin@Sun.COM 	mp->arrayok = arrayok;
9654887Schin 	return(loc);
9664887Schin }
9674887Schin 
9688462SApril.Chin@Sun.COM /*
9698462SApril.Chin@Sun.COM  * if name is a discipline function, run the function and put the results
9708462SApril.Chin@Sun.COM  * on the stack so that ${x.foo} behaves like ${ x.foo;}
9718462SApril.Chin@Sun.COM  */
sh_macfun(Shell_t * shp,const char * name,int offset)9728462SApril.Chin@Sun.COM int sh_macfun(Shell_t *shp, const char *name, int offset)
9738462SApril.Chin@Sun.COM {
9748462SApril.Chin@Sun.COM 	Namval_t	*np, *nq;
9758462SApril.Chin@Sun.COM 	np = nv_bfsearch(name,shp->fun_tree,&nq,(char**)0);
9768462SApril.Chin@Sun.COM 	if(np)
9778462SApril.Chin@Sun.COM 	{
9788462SApril.Chin@Sun.COM 		/* treat ${x.foo} as ${x.foo;} */
9798462SApril.Chin@Sun.COM 		Shnode_t *tp;
9808462SApril.Chin@Sun.COM 		char buff[sizeof(struct dolnod)+sizeof(char*)];
9818462SApril.Chin@Sun.COM 		struct comnod node;
9828462SApril.Chin@Sun.COM 		struct dolnod *dp = (struct dolnod*)buff;
9838462SApril.Chin@Sun.COM 		memset(&node,0,sizeof(node));
9848462SApril.Chin@Sun.COM 		memset(&buff,0,sizeof(buff));
9858462SApril.Chin@Sun.COM 		tp = (Shnode_t*)&node;
9868462SApril.Chin@Sun.COM 		tp->com.comarg = (struct argnod*)dp;
9878462SApril.Chin@Sun.COM 		tp->com.comline = shp->inlineno;
98810898Sroland.mainz@nrubsig.org 		dp->dolnum = 1;
9898462SApril.Chin@Sun.COM 		dp->dolval[0] = strdup(name);
9908462SApril.Chin@Sun.COM 		stkseek(shp->stk,offset);
9918462SApril.Chin@Sun.COM 		comsubst((Mac_t*)shp->mac_context,tp,2);
9928462SApril.Chin@Sun.COM 		free(dp->dolval[0]);
9938462SApril.Chin@Sun.COM 		return(1);
9948462SApril.Chin@Sun.COM 	}
9958462SApril.Chin@Sun.COM 	return(0);
9968462SApril.Chin@Sun.COM }
9978462SApril.Chin@Sun.COM 
namecount(Mac_t * mp,const char * prefix)9984887Schin static int namecount(Mac_t *mp,const char *prefix)
9994887Schin {
10004887Schin 	int count = 0;
10018462SApril.Chin@Sun.COM 	mp->nvwalk = nv_diropen((Namval_t*)0,prefix);
10024887Schin 	while(nv_dirnext(mp->nvwalk))
10034887Schin 		count++;
10044887Schin 	nv_dirclose(mp->nvwalk);
10054887Schin 	return(count);
10064887Schin }
10074887Schin 
nextname(Mac_t * mp,const char * prefix,int len)10084887Schin static char *nextname(Mac_t *mp,const char *prefix, int len)
10094887Schin {
10104887Schin 	char *cp;
10114887Schin 	if(len==0)
10124887Schin 	{
10138462SApril.Chin@Sun.COM 		mp->nvwalk = nv_diropen((Namval_t*)0,prefix);
10144887Schin 		return((char*)mp->nvwalk);
10154887Schin 	}
10164887Schin 	if(!(cp=nv_dirnext(mp->nvwalk)))
10174887Schin 		nv_dirclose(mp->nvwalk);
10184887Schin 	return(cp);
10194887Schin }
10204887Schin 
10214887Schin /*
10224887Schin  * This routine handles $param,  ${parm}, and ${param op word}
10234887Schin  * The input stream is assumed to be a string
10244887Schin  */
varsub(Mac_t * mp)10254887Schin static int varsub(Mac_t *mp)
10264887Schin {
10274887Schin 	register int	c;
10284887Schin 	register int	type=0; /* M_xxx */
10294887Schin 	register char	*v,*argp=0;
10304887Schin 	register Namval_t	*np = NIL(Namval_t*);
10314887Schin 	register int 	dolg=0, mode=0;
10328462SApril.Chin@Sun.COM 	Lex_t		*lp = (Lex_t*)mp->shp->lex_context;
10334887Schin 	Namarr_t	*ap=0;
10344887Schin 	int		dolmax=0, vsize= -1, offset= -1, nulflg, replen=0, bysub=0;
10358462SApril.Chin@Sun.COM 	char		idbuff[3], *id = idbuff, *pattern=0, *repstr, *arrmax=0;
103610898Sroland.mainz@nrubsig.org 	int		var=1,addsub=0,oldpat=mp->pattern,idnum=0,flag=0,d;
10378462SApril.Chin@Sun.COM 	Stk_t		*stkp = mp->shp->stk;
10384887Schin retry1:
10394887Schin 	mp->zeros = 0;
10404887Schin 	idbuff[0] = 0;
10414887Schin 	idbuff[1] = 0;
10424887Schin 	c = fcget();
104310898Sroland.mainz@nrubsig.org 	switch(isascii(c)?sh_lexstates[ST_DOL][c]:S_ALP)
10444887Schin 	{
10454887Schin 	    case S_RBRA:
10464887Schin 		if(type<M_SIZE)
10474887Schin 			goto nosub;
10484887Schin 		/* This code handles ${#} */
10494887Schin 		c = mode;
10504887Schin 		mode = type = 0;
10514887Schin 		/* FALL THRU */
10524887Schin 	    case S_SPC1:
10534887Schin 		if(type==M_BRACE)
10544887Schin 		{
10554887Schin 			if(isaletter(mode=fcpeek(0)) || mode=='.')
10564887Schin 			{
10574887Schin 				if(c=='#')
10584887Schin 					type = M_SIZE;
10594887Schin #ifdef SHOPT_TYPEDEF
10604887Schin 				else if(c=='@')
10614887Schin 				{
10624887Schin 					type = M_TYPE;
10634887Schin 					goto retry1;
10644887Schin 				}
10654887Schin #endif /* SHOPT_TYPEDEF */
10664887Schin 				else
10674887Schin 					type = M_VNAME;
10684887Schin 				mode = c;
10694887Schin 				goto retry1;
10704887Schin 			}
10714887Schin 			else if(c=='#' && (isadigit(mode)||fcpeek(1)==RBRACE))
10724887Schin 			{
10734887Schin 				type = M_SIZE;
10744887Schin 				mode = c;
10754887Schin 				goto retry1;
10764887Schin 			}
10774887Schin 		}
10784887Schin 		/* FALL THRU */
10794887Schin 	    case S_SPC2:
108010898Sroland.mainz@nrubsig.org 		var = 0;
10814887Schin 		*id = c;
10828462SApril.Chin@Sun.COM 		v = special(mp->shp,c);
10834887Schin 		if(isastchar(c))
10844887Schin 		{
10854887Schin 			mode = c;
10864887Schin #if  SHOPT_FILESCAN
10878462SApril.Chin@Sun.COM 			if(mp->shp->cur_line)
10884887Schin 			{
10894887Schin 				v = getdolarg(&sh,1,(int*)0);
10904887Schin 				dolmax = MAX_ARGN;
10914887Schin 			}
10924887Schin 			else
10934887Schin #endif  /* SHOPT_FILESCAN */
10948462SApril.Chin@Sun.COM 			dolmax = mp->shp->st.dolc+1;
10954887Schin 			dolg = (v!=0);
10964887Schin 		}
10974887Schin 		break;
10984887Schin 	    case S_LBRA:
10994887Schin 		if(type)
11004887Schin 			goto nosub;
11014887Schin 		type = M_BRACE;
11024887Schin 		goto retry1;
11034887Schin 	    case S_PAR:
11044887Schin 		if(type)
11054887Schin 			goto nosub;
11068462SApril.Chin@Sun.COM 		comsubst(mp,(Shnode_t*)0,1);
11074887Schin 		return(1);
11084887Schin 	    case S_DIG:
110910898Sroland.mainz@nrubsig.org 		var = 0;
11104887Schin 		c -= '0';
11118462SApril.Chin@Sun.COM 		mp->shp->argaddr = 0;
11124887Schin 		if(type)
11134887Schin 		{
11144887Schin 			register int d;
11154887Schin 			while((d=fcget()),isadigit(d))
11164887Schin 				c = 10*c + (d-'0');
11174887Schin 			fcseek(-1);
11184887Schin 		}
11194887Schin 		idnum = c;
11204887Schin 		if(c==0)
11218462SApril.Chin@Sun.COM 			v = special(mp->shp,c);
11224887Schin #if  SHOPT_FILESCAN
11238462SApril.Chin@Sun.COM 		else if(mp->shp->cur_line)
11244887Schin 		{
11258462SApril.Chin@Sun.COM 			mp->shp->used_pos = 1;
11264887Schin 			v = getdolarg(&sh,c,&vsize);
11274887Schin 		}
11284887Schin #endif  /* SHOPT_FILESCAN */
11298462SApril.Chin@Sun.COM 		else if(c <= mp->shp->st.dolc)
11304887Schin 		{
11318462SApril.Chin@Sun.COM 			mp->shp->used_pos = 1;
11328462SApril.Chin@Sun.COM 			v = mp->shp->st.dolv[c];
11334887Schin 		}
11344887Schin 		else
11354887Schin 			v = 0;
11364887Schin 		break;
11374887Schin 	    case S_ALP:
11384887Schin 		if(c=='.' && type==0)
11394887Schin 			goto nosub;
11408462SApril.Chin@Sun.COM 		offset = stktell(stkp);
11414887Schin 		do
11424887Schin 		{
11434887Schin 			np = 0;
11444887Schin 			do
11458462SApril.Chin@Sun.COM 				sfputc(stkp,c);
114610898Sroland.mainz@nrubsig.org 			while(((c=fcget()),(!isascii(c)||isaname(c)))||type && c=='.');
11478462SApril.Chin@Sun.COM 			while(c==LBRACT && (type||mp->arrayok))
11484887Schin 			{
11498462SApril.Chin@Sun.COM 				mp->shp->argaddr=0;
11504887Schin 				if((c=fcget(),isastchar(c)) && fcpeek(0)==RBRACT)
11514887Schin 				{
11524887Schin 					if(type==M_VNAME)
11534887Schin 						type = M_SUBNAME;
11544887Schin 					idbuff[0] = mode = c;
11554887Schin 					fcget();
11564887Schin 					c = fcget();
11574887Schin 					if(c=='.' || c==LBRACT)
11584887Schin 					{
11598462SApril.Chin@Sun.COM 						sfputc(stkp,LBRACT);
11608462SApril.Chin@Sun.COM 						sfputc(stkp,mode);
11618462SApril.Chin@Sun.COM 						sfputc(stkp,RBRACT);
11624887Schin 					}
11634887Schin 					else
11644887Schin 						flag = NV_ARRAY;
11654887Schin 					break;
11664887Schin 				}
11674887Schin 				else
11684887Schin 				{
11694887Schin 					fcseek(-1);
11708462SApril.Chin@Sun.COM 					c = stktell(stkp);
11718462SApril.Chin@Sun.COM 					sfputc(stkp,LBRACT);
11728462SApril.Chin@Sun.COM 					v = stkptr(stkp,subcopy(mp,1));
11738462SApril.Chin@Sun.COM 					if(type && mp->dotdot)
11748462SApril.Chin@Sun.COM 					{
11758462SApril.Chin@Sun.COM 						mode = '@';
11768462SApril.Chin@Sun.COM 						v[-1] = 0;
11778462SApril.Chin@Sun.COM 						if(type==M_VNAME)
11788462SApril.Chin@Sun.COM 							type = M_SUBNAME;
11798462SApril.Chin@Sun.COM 						else if(type==M_SIZE)
11808462SApril.Chin@Sun.COM 							goto nosub;
11818462SApril.Chin@Sun.COM 					}
11828462SApril.Chin@Sun.COM 					else
11838462SApril.Chin@Sun.COM 						sfputc(stkp,RBRACT);
11848462SApril.Chin@Sun.COM 					c = fcget();
11858462SApril.Chin@Sun.COM 					if(c==0 && type==M_VNAME)
11864887Schin 						type = M_SUBNAME;
11874887Schin 				}
11884887Schin 			}
11894887Schin 		}
11904887Schin 		while(type && c=='.');
11914887Schin 		if(c==RBRACE && type &&  fcpeek(-2)=='.')
11924887Schin 		{
11938462SApril.Chin@Sun.COM 			/* ${x.} or ${x..} */
11948462SApril.Chin@Sun.COM 			if(fcpeek(-3) == '.')
11958462SApril.Chin@Sun.COM 			{
11968462SApril.Chin@Sun.COM 				stkseek(stkp,stktell(stkp)-2);
11978462SApril.Chin@Sun.COM 				nv_local = 1;
11988462SApril.Chin@Sun.COM 			}
11998462SApril.Chin@Sun.COM 			else
12008462SApril.Chin@Sun.COM 			{
12018462SApril.Chin@Sun.COM 				stkseek(stkp,stktell(stkp)-1);
12028462SApril.Chin@Sun.COM 				type = M_TREE;
12038462SApril.Chin@Sun.COM 			}
12044887Schin 		}
12058462SApril.Chin@Sun.COM 		sfputc(stkp,0);
12068462SApril.Chin@Sun.COM 		id=stkptr(stkp,offset);
12074887Schin 		if(isastchar(c) && type)
12084887Schin 		{
12094887Schin 			if(type==M_VNAME || type==M_SIZE)
12104887Schin 			{
12114887Schin 				idbuff[0] = mode = c;
12124887Schin 				if((d=fcpeek(0))==c)
12134887Schin 					idbuff[1] = fcget();
12144887Schin 				if(type==M_VNAME)
12154887Schin 					type = M_NAMESCAN;
12164887Schin 				else
12174887Schin 					type = M_NAMECOUNT;
12184887Schin 				break;
12194887Schin 			}
12204887Schin 			goto nosub;
12214887Schin 		}
12224887Schin 		flag |= NV_NOASSIGN|NV_VARNAME|NV_NOADD;
12234887Schin 		if(c=='=' || c=='?' || (c==':' && ((d=fcpeek(0))=='=' || d=='?')))
12244887Schin 			flag &= ~NV_NOADD;
12254887Schin #if  SHOPT_FILESCAN
12268462SApril.Chin@Sun.COM 		if(mp->shp->cur_line && *id=='R' && strcmp(id,"REPLY")==0)
12274887Schin 		{
12288462SApril.Chin@Sun.COM 			mp->shp->argaddr=0;
12294887Schin 			np = REPLYNOD;
12304887Schin 		}
12314887Schin 		else
12324887Schin #endif  /* SHOPT_FILESCAN */
123310898Sroland.mainz@nrubsig.org 		{
123410898Sroland.mainz@nrubsig.org 			if(mp->shp->argaddr)
123510898Sroland.mainz@nrubsig.org 				flag &= ~NV_NOADD;
123610898Sroland.mainz@nrubsig.org 			np = nv_open(id,mp->shp->var_tree,flag|NV_NOFAIL);
123710898Sroland.mainz@nrubsig.org 		}
123810898Sroland.mainz@nrubsig.org 		if(isastchar(mode))
123910898Sroland.mainz@nrubsig.org 			var = 0;
124010898Sroland.mainz@nrubsig.org 		if((!np || nv_isnull(np)) && type==M_BRACE && c==RBRACE && !(flag&NV_ARRAY) && strchr(id,'.'))
12418462SApril.Chin@Sun.COM 		{
12428462SApril.Chin@Sun.COM 			if(sh_macfun(mp->shp,id,offset))
12438462SApril.Chin@Sun.COM 			{
12448462SApril.Chin@Sun.COM 				fcget();
12458462SApril.Chin@Sun.COM 				return(1);
12468462SApril.Chin@Sun.COM 			}
12478462SApril.Chin@Sun.COM 		}
124810898Sroland.mainz@nrubsig.org 		if(np && (flag&NV_NOADD) && nv_isnull(np))
124910898Sroland.mainz@nrubsig.org 		{
125010898Sroland.mainz@nrubsig.org 			if(nv_isattr(np,NV_NOFREE))
125110898Sroland.mainz@nrubsig.org 				nv_offattr(np,NV_NOFREE);
125210898Sroland.mainz@nrubsig.org 			else
125310898Sroland.mainz@nrubsig.org 				np = 0;
125410898Sroland.mainz@nrubsig.org 		}
12554887Schin 		ap = np?nv_arrayptr(np):0;
12564887Schin 		if(type)
12574887Schin 		{
12588462SApril.Chin@Sun.COM 			if(mp->dotdot)
12598462SApril.Chin@Sun.COM 			{
12608462SApril.Chin@Sun.COM 				if(ap)
12618462SApril.Chin@Sun.COM 				{
12628462SApril.Chin@Sun.COM 					nv_putsub(np,v,ARRAY_SCAN);
12638462SApril.Chin@Sun.COM 					v = stkptr(stkp,mp->dotdot);
12648462SApril.Chin@Sun.COM 					dolmax =1;
12658462SApril.Chin@Sun.COM 					if(array_assoc(ap))
12668462SApril.Chin@Sun.COM 						arrmax = strdup(v);
12678462SApril.Chin@Sun.COM 					else if((dolmax = (int)sh_arith(v))<0)
12688462SApril.Chin@Sun.COM 						dolmax += array_maxindex(np);
12698462SApril.Chin@Sun.COM 					if(type==M_SUBNAME)
12708462SApril.Chin@Sun.COM 						bysub = 1;
12718462SApril.Chin@Sun.COM 				}
12728462SApril.Chin@Sun.COM 				else
12738462SApril.Chin@Sun.COM 				{
12748462SApril.Chin@Sun.COM 					if((int)sh_arith(v))
12758462SApril.Chin@Sun.COM 						np = 0;
12768462SApril.Chin@Sun.COM 				}
12778462SApril.Chin@Sun.COM 			}
12788462SApril.Chin@Sun.COM 			else if(ap && (isastchar(mode)||type==M_TREE)  && !(ap->nelem&ARRAY_SCAN) && type!=M_SIZE)
12794887Schin 				nv_putsub(np,NIL(char*),ARRAY_SCAN);
12804887Schin 			if(!isbracechar(c))
12814887Schin 				goto nosub;
12824887Schin 			else
12834887Schin 				fcseek(-1);
12844887Schin 		}
12854887Schin 		else
12864887Schin 			fcseek(-1);
12878462SApril.Chin@Sun.COM 		if(type<=1 && np && nv_isvtree(np) && mp->pattern==1 && !mp->split)
12888462SApril.Chin@Sun.COM 		{
12898462SApril.Chin@Sun.COM 			int peek=1,cc=fcget();
12908462SApril.Chin@Sun.COM 			if(type && cc=='}')
12918462SApril.Chin@Sun.COM 			{
12928462SApril.Chin@Sun.COM 				cc = fcget();
12938462SApril.Chin@Sun.COM 				peek = 2;
12948462SApril.Chin@Sun.COM 			}
12958462SApril.Chin@Sun.COM 			if(mp->quote && cc=='"')
12968462SApril.Chin@Sun.COM 			{
12978462SApril.Chin@Sun.COM 				cc = fcget();
12988462SApril.Chin@Sun.COM 				peek++;
12998462SApril.Chin@Sun.COM 			}
13008462SApril.Chin@Sun.COM 			fcseek(-peek);
13018462SApril.Chin@Sun.COM 			if(cc==0)
13028462SApril.Chin@Sun.COM 				mp->assign = 1;
13038462SApril.Chin@Sun.COM 		}
13048462SApril.Chin@Sun.COM 		if((type==M_VNAME||type==M_SUBNAME)  && mp->shp->argaddr && strcmp(nv_name(np),id))
13058462SApril.Chin@Sun.COM 			mp->shp->argaddr = 0;
13064887Schin 		c = (type>M_BRACE && isastchar(mode));
13078462SApril.Chin@Sun.COM 		if(np && (type==M_TREE || !c || !ap))
13084887Schin 		{
13098462SApril.Chin@Sun.COM 			char *savptr;
13108462SApril.Chin@Sun.COM 			c = *((unsigned char*)stkptr(stkp,offset-1));
13118462SApril.Chin@Sun.COM 			savptr = stkfreeze(stkp,0);
13128462SApril.Chin@Sun.COM 			if(type==M_VNAME || (type==M_SUBNAME && ap))
13134887Schin 			{
13144887Schin 				type = M_BRACE;
13154887Schin 				v = nv_name(np);
13168462SApril.Chin@Sun.COM 				if(ap && !mp->dotdot && !(ap->nelem&ARRAY_UNDEF))
13178462SApril.Chin@Sun.COM 					addsub = 1;
13184887Schin 			}
13194887Schin #ifdef SHOPT_TYPEDEF
13204887Schin 			else if(type==M_TYPE)
13214887Schin 			{
13224887Schin 				Namval_t *nq = nv_type(np);
13234887Schin 				type = M_BRACE;
13244887Schin 				if(nq)
13258462SApril.Chin@Sun.COM 					nv_typename(nq,mp->shp->strbuf);
13264887Schin 				else
13278462SApril.Chin@Sun.COM 					nv_attribute(np,mp->shp->strbuf,"typeset",1);
13288462SApril.Chin@Sun.COM 				v = sfstruse(mp->shp->strbuf);
13294887Schin 			}
13304887Schin #endif /* SHOPT_TYPEDEF */
13314887Schin #if  SHOPT_FILESCAN
13328462SApril.Chin@Sun.COM 			else if(mp->shp->cur_line && np==REPLYNOD)
13338462SApril.Chin@Sun.COM 				v = mp->shp->cur_line;
13344887Schin #endif  /* SHOPT_FILESCAN */
13354887Schin 			else if(type==M_TREE)
13364887Schin 				v = nv_getvtree(np,(Namfun_t*)0);
13374887Schin 			else
13384887Schin 			{
133910898Sroland.mainz@nrubsig.org 				if(type && fcpeek(0)=='+')
134010898Sroland.mainz@nrubsig.org 				{
134110898Sroland.mainz@nrubsig.org 					if(ap)
134210898Sroland.mainz@nrubsig.org 						v = nv_arrayisset(np,ap)?(char*)"x":0;
134310898Sroland.mainz@nrubsig.org 					else
134410898Sroland.mainz@nrubsig.org 						v = nv_isnull(np)?0:(char*)"x";
134510898Sroland.mainz@nrubsig.org 				}
134610898Sroland.mainz@nrubsig.org 				else
134710898Sroland.mainz@nrubsig.org 					v = nv_getval(np);
13484887Schin 				/* special case --- ignore leading zeros */
13498462SApril.Chin@Sun.COM 				if( (mp->arith||mp->let) && (np->nvfun || nv_isattr(np,(NV_LJUST|NV_RJUST|NV_ZFILL))) && (offset==0 || !isalnum(c)))
13504887Schin 					mp->zeros = 1;
13514887Schin 			}
13528462SApril.Chin@Sun.COM 			if(savptr==stakptr(0))
13538462SApril.Chin@Sun.COM 				stkseek(stkp,offset);
13548462SApril.Chin@Sun.COM 			else
13558462SApril.Chin@Sun.COM 				stkset(stkp,savptr,offset);
13564887Schin 		}
13574887Schin 		else
13588462SApril.Chin@Sun.COM 		{
13594887Schin 			v = 0;
13608462SApril.Chin@Sun.COM 			if(type==M_VNAME)
13618462SApril.Chin@Sun.COM 			{
13628462SApril.Chin@Sun.COM 				v = id;
13638462SApril.Chin@Sun.COM 				type = M_BRACE;
13648462SApril.Chin@Sun.COM 			}
13658462SApril.Chin@Sun.COM 			else if(type==M_TYPE)
13668462SApril.Chin@Sun.COM 				type = M_BRACE;
13678462SApril.Chin@Sun.COM 		}
13688462SApril.Chin@Sun.COM 		stkseek(stkp,offset);
13694887Schin 		if(ap)
13704887Schin 		{
13714887Schin #if SHOPT_OPTIMIZE
13728462SApril.Chin@Sun.COM 			if(mp->shp->argaddr)
13734887Schin 				nv_optimize(np);
13744887Schin #endif
13754887Schin 			if(isastchar(mode) && array_elem(ap)> !c)
13764887Schin 				dolg = -1;
13774887Schin 			else
13784887Schin 				dolg = 0;
13794887Schin 		}
13804887Schin 		break;
13814887Schin 	    case S_EOF:
13824887Schin 		fcseek(-1);
13834887Schin 	    default:
13844887Schin 		goto nosub;
13854887Schin 	}
13864887Schin 	c = fcget();
13874887Schin 	if(type>M_TREE)
13884887Schin 	{
13894887Schin 		if(c!=RBRACE)
13904887Schin 			mac_error(np);
13914887Schin 		if(type==M_NAMESCAN || type==M_NAMECOUNT)
13924887Schin 		{
139310898Sroland.mainz@nrubsig.org 			mp->shp->last_root = mp->shp->var_tree;
13948462SApril.Chin@Sun.COM 			id = prefix(mp->shp,id);
13958462SApril.Chin@Sun.COM 			stkseek(stkp,offset);
13964887Schin 			if(type==M_NAMECOUNT)
13974887Schin 			{
13984887Schin 				c = namecount(mp,id);
13994887Schin 				v = ltos(c);
14004887Schin 			}
14014887Schin 			else
14024887Schin 			{
14034887Schin 				dolmax = strlen(id);
14044887Schin 				dolg = -1;
14054887Schin 				nextname(mp,id,0);
14064887Schin 				v = nextname(mp,id,dolmax);
14074887Schin 			}
14084887Schin 		}
14094887Schin 		else if(type==M_SUBNAME)
14104887Schin 		{
14114887Schin 			if(dolg<0)
14124887Schin 			{
14134887Schin 				v = nv_getsub(np);
14144887Schin 				bysub=1;
14154887Schin 			}
14164887Schin 			else if(v)
14174887Schin 			{
14184887Schin 				if(!ap || isastchar(mode))
14194887Schin 					v = "0";
14204887Schin 				else
14214887Schin 					v = nv_getsub(np);
14224887Schin 			}
14234887Schin 		}
14244887Schin 		else
14254887Schin 		{
14264887Schin 			if(!isastchar(mode))
14274887Schin 				c = charlen(v,vsize);
14284887Schin 			else if(dolg>0)
14294887Schin 			{
14304887Schin #if  SHOPT_FILESCAN
14318462SApril.Chin@Sun.COM 				if(mp->shp->cur_line)
14324887Schin 				{
14334887Schin 					getdolarg(&sh,MAX_ARGN,(int*)0);
14348462SApril.Chin@Sun.COM 					c = mp->shp->offsets[0];
14354887Schin 				}
14364887Schin 				else
14374887Schin #endif  /* SHOPT_FILESCAN */
14388462SApril.Chin@Sun.COM 				c = mp->shp->st.dolc;
14394887Schin 			}
14404887Schin 			else if(dolg<0)
14414887Schin 				c = array_elem(ap);
14424887Schin 			else
14434887Schin 				c = (v!=0);
14444887Schin 			dolg = dolmax = 0;
14454887Schin 			v = ltos(c);
14464887Schin 		}
14474887Schin 		c = RBRACE;
14484887Schin 	}
14494887Schin 	nulflg = 0;
14504887Schin 	if(type && c==':')
14514887Schin 	{
14524887Schin 		c = fcget();
14534887Schin 		if(sh_lexstates[ST_BRACE][c]==S_MOD1 && c!='*' && c!= ':')
14544887Schin 			nulflg=1;
14554887Schin 		else if(c!='%' && c!='#')
14564887Schin 		{
14574887Schin 			fcseek(-1);
14584887Schin 			c = ':';
14594887Schin 		}
14604887Schin 	}
14614887Schin 	if(type)
14624887Schin 	{
14634887Schin 		if(!isbracechar(c))
14644887Schin 		{
14654887Schin 			if(!nulflg)
14664887Schin 				mac_error(np);
14674887Schin 			fcseek(-1);
14684887Schin 			c = ':';
14694887Schin 		}
14704887Schin 		if(c!=RBRACE)
14714887Schin 		{
14724887Schin 			int newops = (c=='#' || c == '%' || c=='/');
14738462SApril.Chin@Sun.COM 			offset = stktell(stkp);
14744887Schin 			if(c=='/' ||c==':' || ((!v || (nulflg && *v==0)) ^ (c=='+'||c=='#'||c=='%')))
14754887Schin 			{
14764887Schin 				int newquote = mp->quote;
14774887Schin 				int split = mp->split;
14784887Schin 				int quoted = mp->quoted;
14794887Schin 				int arith = mp->arith;
14804887Schin 				int zeros = mp->zeros;
148110898Sroland.mainz@nrubsig.org 				int assign = mp->assign;
14824887Schin 				if(newops)
14834887Schin 				{
14844887Schin 					type = fcget();
14854887Schin 					if(type=='%' || type=='#')
14864887Schin 					{
14874887Schin 						int d = fcget();
14884887Schin 						fcseek(-1);
14894887Schin 						if(d=='(')
14904887Schin 							type = 0;
14914887Schin 					}
14924887Schin 					fcseek(-1);
14934887Schin 					mp->pattern = 1+(c=='/');
14944887Schin 					mp->split = 0;
14954887Schin 					mp->quoted = 0;
149610898Sroland.mainz@nrubsig.org 					mp->assign &= ~1;
14974887Schin 					mp->arith = mp->zeros = 0;
14984887Schin 					newquote = 0;
14994887Schin 				}
15004887Schin 				else if(c=='?' || c=='=')
15014887Schin 					mp->split = mp->pattern = 0;
15024887Schin 				copyto(mp,RBRACE,newquote);
15034887Schin 				if(!oldpat)
15044887Schin 					mp->patfound = 0;
15054887Schin 				mp->pattern = oldpat;
15064887Schin 				mp->split = split;
15074887Schin 				mp->quoted = quoted;
15084887Schin 				mp->arith = arith;
15094887Schin 				mp->zeros = zeros;
151010898Sroland.mainz@nrubsig.org 				mp->assign = assign;
15114887Schin 				/* add null byte */
15128462SApril.Chin@Sun.COM 				sfputc(stkp,0);
15138462SApril.Chin@Sun.COM 				stkseek(stkp,stktell(stkp)-1);
15144887Schin 			}
15154887Schin 			else
15164887Schin 			{
15178462SApril.Chin@Sun.COM 				sh_lexskip(lp,RBRACE,0,(!newops&&mp->quote)?ST_QUOTE:ST_NESTED);
15188462SApril.Chin@Sun.COM 				stkseek(stkp,offset);
15194887Schin 			}
15208462SApril.Chin@Sun.COM 			argp=stkptr(stkp,offset);
15214887Schin 		}
15224887Schin 	}
15234887Schin 	else
15244887Schin 	{
15254887Schin 		fcseek(-1);
15264887Schin 		c=0;
15274887Schin 	}
15284887Schin 	if(c==':')  /* ${name:expr1[:expr2]} */
15294887Schin 	{
15304887Schin 		char *ptr;
15314887Schin 		type = (int)sh_strnum(argp,&ptr,1);
15324887Schin 		if(isastchar(mode))
15334887Schin 		{
15344887Schin 			if(id==idbuff)  /* ${@} or ${*} */
15354887Schin 			{
15364887Schin 				if(type<0 && (type+= dolmax)<0)
15374887Schin 					type = 0;
15384887Schin 				if(type==0)
15398462SApril.Chin@Sun.COM 					v = special(mp->shp,dolg=0);
15404887Schin #if  SHOPT_FILESCAN
15418462SApril.Chin@Sun.COM 				else if(mp->shp->cur_line)
15424887Schin 				{
15434887Schin 					v = getdolarg(&sh,dolg=type,&vsize);
15444887Schin 					if(!v)
15454887Schin 						dolmax = type;
15464887Schin 				}
15474887Schin #endif  /* SHOPT_FILESCAN */
15484887Schin 				else if(type < dolmax)
15498462SApril.Chin@Sun.COM 					v = mp->shp->st.dolv[dolg=type];
15504887Schin 				else
15514887Schin 					v =  0;
15524887Schin 			}
15534887Schin 			else if(ap)
15544887Schin 			{
15554887Schin 				if(type<0)
15564887Schin 				{
15574887Schin 					if(array_assoc(ap))
15584887Schin 						type = -type;
15594887Schin 					else
15604887Schin 						type += array_maxindex(np);
15614887Schin 				}
15624887Schin 				if(array_assoc(ap))
15634887Schin 				{
15644887Schin 					while(type-- >0 && (v=0,nv_nextsub(np)))
15654887Schin 						v = nv_getval(np);
15664887Schin 				}
15674887Schin 				else if(type > 0)
15684887Schin 				{
15694887Schin 					if(nv_putsub(np,NIL(char*),type|ARRAY_SCAN))
15704887Schin 						v = nv_getval(np);
15714887Schin 					else
15724887Schin 						v = 0;
15734887Schin 				}
15744887Schin 			}
15754887Schin 			else if(type>0)
15764887Schin 				v = 0;
15774887Schin 		}
15784887Schin 		else if(v)
15794887Schin 		{
15804887Schin 			vsize = charlen(v,vsize);
15814887Schin 			if(type<0 && (type += vsize)<0)
15824887Schin 				type = 0;
15834887Schin 			if(vsize < type)
15844887Schin 				v = 0;
15854887Schin #if SHOPT_MULTIBYTE
15864887Schin 			else if(mbwide())
15874887Schin 			{
15884887Schin 				mbinit();
15894887Schin 				while(type-->0)
15904887Schin 				{
15914887Schin 					if((c=mbsize(v))<1)
15924887Schin 						c = 1;
15934887Schin 					v += c;
15944887Schin 				}
15954887Schin 				c = ':';
15964887Schin 			}
15974887Schin #endif /* SHOPT_MULTIBYTE */
15984887Schin 			else
15994887Schin 				v += type;
16004887Schin 			vsize -= type;
16014887Schin 		}
16024887Schin 		if(*ptr==':')
16034887Schin 		{
16044887Schin 			if((type = (int)sh_strnum(ptr+1,&ptr,1)) <=0)
16054887Schin 				v = 0;
16064887Schin 			else if(isastchar(mode))
16074887Schin 			{
16084887Schin 				if(dolg>=0)
16094887Schin 				{
16104887Schin 					if(dolg+type < dolmax)
16114887Schin 						dolmax = dolg+type;
16124887Schin 				}
16134887Schin 				else
16144887Schin 					dolmax = type;
16154887Schin 			}
16164887Schin 			else if(type < vsize)
16174887Schin 			{
16184887Schin #if SHOPT_MULTIBYTE
16194887Schin 				if(mbwide())
16204887Schin 				{
16214887Schin 					char *vp = v;
16224887Schin 					mbinit();
16234887Schin 					while(type-->0)
16244887Schin 					{
16254887Schin 						if((c=mbsize(vp))<1)
16264887Schin 							c = 1;
16274887Schin 						vp += c;
16284887Schin 					}
16294887Schin 					type = vp-v;
16304887Schin 					c = ':';
16314887Schin 				}
16324887Schin #endif /* SHOPT_MULTIBYTE */
16334887Schin 				vsize = type;
16344887Schin 			}
16354887Schin 		}
16364887Schin 		if(*ptr)
16374887Schin 			mac_error(np);
16388462SApril.Chin@Sun.COM 		stkseek(stkp,offset);
16394887Schin 		argp = 0;
16404887Schin 	}
16414887Schin 	/* check for substring operations */
16424887Schin 	else if(c == '#' || c == '%' || c=='/')
16434887Schin 	{
16444887Schin 		if(c=='/')
16454887Schin 		{
16464887Schin 			if(type=='/' || type=='#' || type=='%')
16474887Schin 			{
16484887Schin 				c = type;
16494887Schin 				type = '/';
16504887Schin 				argp++;
16514887Schin 			}
16524887Schin 			else
16534887Schin 				type = 0;
16544887Schin 		}
16554887Schin 		else
16564887Schin 		{
16574887Schin 			if(type==c) /* ## or %% */
16584887Schin 				argp++;
16594887Schin 			else
16604887Schin 				type = 0;
16614887Schin 		}
16624887Schin 		pattern = strdup(argp);
16634887Schin 		if((type=='/' || c=='/') && (repstr = mac_getstring(pattern)))
16644887Schin 			replen = strlen(repstr);
16654887Schin 		if(v || c=='/' && offset>=0)
16668462SApril.Chin@Sun.COM 			stkseek(stkp,offset);
16674887Schin 	}
16684887Schin 	/* check for quoted @ */
16694887Schin 	if(mode=='@' && mp->quote && !v && c!='-')
16704887Schin 		mp->quoted-=2;
16714887Schin retry2:
16724887Schin 	if(v && (!nulflg || *v ) && c!='+')
16734887Schin 	{
16744887Schin 		register int d = (mode=='@'?' ':mp->ifs);
16758462SApril.Chin@Sun.COM 		int match[2*(MATCH_MAX+1)], nmatch, nmatch_prev, vsize_last;
16764887Schin 		char *vlast;
16774887Schin 		while(1)
16784887Schin 		{
16794887Schin 			if(!v)
16804887Schin 				v= "";
16814887Schin 			if(c=='/' || c=='#' || c== '%')
16824887Schin 			{
16838462SApril.Chin@Sun.COM 				flag = (type || c=='/')?(STR_GROUP|STR_MAXIMAL):STR_GROUP;
16844887Schin 				if(c!='/')
16854887Schin 					flag |= STR_LEFT;
16868462SApril.Chin@Sun.COM 				nmatch = 0;
16874887Schin 				while(1)
16884887Schin 				{
16894887Schin 					vsize = strlen(v);
16908462SApril.Chin@Sun.COM 					nmatch_prev = nmatch;
16914887Schin 					if(c=='%')
16924887Schin 						nmatch=substring(v,pattern,match,flag&STR_MAXIMAL);
16934887Schin 					else
16944887Schin 						nmatch=strgrpmatch(v,pattern,match,elementsof(match)/2,flag);
16954887Schin 					if(replen>0)
16964887Schin 						sh_setmatch(v,vsize,nmatch,match);
16974887Schin 					if(nmatch)
16984887Schin 					{
16994887Schin 						vlast = v;
17004887Schin 						vsize_last = vsize;
17014887Schin 						vsize = match[0];
17024887Schin 					}
17034887Schin 					else if(c=='#')
17044887Schin 						vsize = 0;
17054887Schin 					if(vsize)
17064887Schin 						mac_copy(mp,v,vsize);
17078462SApril.Chin@Sun.COM 					if(nmatch && replen>0 && (match[1] || !nmatch_prev))
17084887Schin 						mac_substitute(mp,repstr,v,match,nmatch);
17094887Schin 					if(nmatch==0)
17104887Schin 						v += vsize;
17114887Schin 					else
17124887Schin 						v += match[1];
17134887Schin 					if(*v &&  c=='/' && type)
17144887Schin 					{
17154887Schin 						/* avoid infinite loop */
17164887Schin 						if(nmatch && match[1]==0)
17178462SApril.Chin@Sun.COM 						{
17188462SApril.Chin@Sun.COM 							nmatch = 0;
17198462SApril.Chin@Sun.COM 							mac_copy(mp,v,1);
17204887Schin 							v++;
17218462SApril.Chin@Sun.COM 						}
17224887Schin 						continue;
17234887Schin 					}
17244887Schin 					vsize = -1;
17254887Schin 					break;
17264887Schin 				}
17274887Schin 				if(replen==0)
17284887Schin 					sh_setmatch(vlast,vsize_last,nmatch,match);
17294887Schin 			}
17304887Schin 			if(vsize)
17314887Schin 				mac_copy(mp,v,vsize>0?vsize:strlen(v));
17328462SApril.Chin@Sun.COM 			if(addsub)
17338462SApril.Chin@Sun.COM 			{
17348462SApril.Chin@Sun.COM 				sfprintf(mp->shp->strbuf,"[%s]",nv_getsub(np));
17358462SApril.Chin@Sun.COM 				v = sfstruse(mp->shp->strbuf);
17368462SApril.Chin@Sun.COM 				mac_copy(mp, v, strlen(v));
17378462SApril.Chin@Sun.COM 			}
17384887Schin 			if(dolg==0 && dolmax==0)
17394887Schin 				 break;
17408462SApril.Chin@Sun.COM 			if(mp->dotdot)
17418462SApril.Chin@Sun.COM 			{
17428462SApril.Chin@Sun.COM 				if(nv_nextsub(np) == 0)
17438462SApril.Chin@Sun.COM 					break;
17448462SApril.Chin@Sun.COM 				if(bysub)
17458462SApril.Chin@Sun.COM 					v = nv_getsub(np);
17468462SApril.Chin@Sun.COM 				else
17478462SApril.Chin@Sun.COM 					v = nv_getval(np);
17488462SApril.Chin@Sun.COM 				if(array_assoc(ap))
17498462SApril.Chin@Sun.COM 				{
17508462SApril.Chin@Sun.COM 					if(strcmp(bysub?v:nv_getsub(np),arrmax)>0)
17518462SApril.Chin@Sun.COM 						break;
17528462SApril.Chin@Sun.COM 				}
17538462SApril.Chin@Sun.COM 				else
17548462SApril.Chin@Sun.COM 				{
17558462SApril.Chin@Sun.COM 					if(nv_aindex(np) > dolmax)
17568462SApril.Chin@Sun.COM 						break;
17578462SApril.Chin@Sun.COM 				}
17588462SApril.Chin@Sun.COM 			}
17598462SApril.Chin@Sun.COM 			else if(dolg>=0)
17604887Schin 			{
17614887Schin 				if(++dolg >= dolmax)
17624887Schin 					break;
17634887Schin #if  SHOPT_FILESCAN
17648462SApril.Chin@Sun.COM 				if(mp->shp->cur_line)
17654887Schin 				{
17664887Schin 					if(dolmax==MAX_ARGN && isastchar(mode))
17674887Schin 						break;
17684887Schin 					if(!(v=getdolarg(&sh,dolg,&vsize)))
17694887Schin 					{
17704887Schin 						dolmax = dolg;
17714887Schin 						break;
17724887Schin 					}
17734887Schin 				}
17744887Schin 				else
17754887Schin #endif  /* SHOPT_FILESCAN */
17768462SApril.Chin@Sun.COM 				v = mp->shp->st.dolv[dolg];
17774887Schin 			}
17784887Schin 			else if(!np)
17794887Schin 			{
17804887Schin 				if(!(v = nextname(mp,id,dolmax)))
17814887Schin 					break;
17824887Schin 			}
17834887Schin 			else
17844887Schin 			{
17854887Schin 				if(dolmax &&  --dolmax <=0)
17864887Schin 				{
17874887Schin 					nv_putsub(np,NIL(char*),ARRAY_UNDEF);
17884887Schin 					break;
17894887Schin 				}
17908462SApril.Chin@Sun.COM 				if(ap)
17918462SApril.Chin@Sun.COM 					ap->nelem |= ARRAY_SCAN;
17924887Schin 				if(nv_nextsub(np) == 0)
17934887Schin 					break;
17944887Schin 				if(bysub)
17954887Schin 					v = nv_getsub(np);
17964887Schin 				else
17974887Schin 					v = nv_getval(np);
17984887Schin 			}
17994887Schin 			if(mp->split && (!mp->quote || mode=='@'))
18004887Schin 			{
18014887Schin 				if(!np)
18024887Schin 					mp->pattern = 0;
18034887Schin 				endfield(mp,mp->quoted);
18044887Schin 				mp->pattern = oldpat;
18054887Schin 			}
18064887Schin 			else if(d)
18074887Schin 			{
18084887Schin 				if(mp->sp)
18094887Schin 					sfputc(mp->sp,d);
18104887Schin 				else
18118462SApril.Chin@Sun.COM 					sfputc(stkp,d);
18124887Schin 			}
18134887Schin 		}
18148462SApril.Chin@Sun.COM 		if(arrmax)
18158462SApril.Chin@Sun.COM 			free((void*)arrmax);
18164887Schin 		if(pattern)
18174887Schin 			free((void*)pattern);
18184887Schin 	}
18194887Schin 	else if(argp)
18204887Schin 	{
18214887Schin 		if(c=='/' && replen>0 && pattern && strmatch("",pattern))
18224887Schin 			mac_substitute(mp,repstr,v,0,0);
18234887Schin 		if(c=='?')
18244887Schin 		{
18254887Schin 			if(np)
18264887Schin 				id = nv_name(np);
18274887Schin 			else if(idnum)
18284887Schin 				id = ltos(idnum);
18294887Schin 			if(*argp)
18304887Schin 			{
18318462SApril.Chin@Sun.COM 				sfputc(stkp,0);
18324887Schin 				errormsg(SH_DICT,ERROR_exit(1),"%s: %s",id,argp);
18334887Schin 			}
18344887Schin 			else if(v)
18354887Schin 				errormsg(SH_DICT,ERROR_exit(1),e_nullset,id);
18364887Schin 			else
18374887Schin 				errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
18384887Schin 		}
18394887Schin 		else if(c=='=')
18404887Schin 		{
18414887Schin 			if(np)
18424887Schin 			{
18438462SApril.Chin@Sun.COM 				if(mp->shp->subshell)
18444887Schin 					np = sh_assignok(np,1);
18454887Schin 				nv_putval(np,argp,0);
18464887Schin 				v = nv_getval(np);
18474887Schin 				nulflg = 0;
18488462SApril.Chin@Sun.COM 				stkseek(stkp,offset);
18494887Schin 				goto retry2;
18504887Schin 			}
18514887Schin 		else
18524887Schin 			mac_error(np);
18534887Schin 		}
18544887Schin 	}
1855*12068SRoger.Faulkner@Oracle.COM 	else if(var && sh_isoption(SH_NOUNSET) && type<=M_TREE && (!np  || nv_isnull(np) || (nv_isarray(np) && !np->nvalue.cp)))
18564887Schin 	{
18574887Schin 		if(np)
18584887Schin 		{
18594887Schin 			if(nv_isarray(np))
18604887Schin 			{
18618462SApril.Chin@Sun.COM 				sfprintf(mp->shp->strbuf,"%s[%s]\0",nv_name(np),nv_getsub(np));
18628462SApril.Chin@Sun.COM 				id = sfstruse(mp->shp->strbuf);
18634887Schin 			}
18644887Schin 			else
18654887Schin 				id = nv_name(np);
18664887Schin 			nv_close(np);
18674887Schin 		}
18684887Schin 		errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
18694887Schin 	}
18704887Schin 	if(np)
18714887Schin 		nv_close(np);
18724887Schin 	return(1);
18734887Schin nosub:
18748462SApril.Chin@Sun.COM 	if(type==M_BRACE && sh_lexstates[ST_NORM][c]==S_BREAK)
18758462SApril.Chin@Sun.COM 	{
18768462SApril.Chin@Sun.COM 		fcseek(-1);
18778462SApril.Chin@Sun.COM 		comsubst(mp,(Shnode_t*)0,2);
18788462SApril.Chin@Sun.COM 		return(1);
18798462SApril.Chin@Sun.COM 	}
18804887Schin 	if(type)
18814887Schin 		mac_error(np);
18824887Schin 	fcseek(-1);
18834887Schin 	nv_close(np);
18844887Schin 	return(0);
18854887Schin }
18864887Schin 
18874887Schin /*
18884887Schin  * This routine handles command substitution
18894887Schin  * <type> is 0 for older `...` version
18904887Schin  */
comsubst(Mac_t * mp,register Shnode_t * t,int type)18918462SApril.Chin@Sun.COM static void comsubst(Mac_t *mp,register Shnode_t* t, int type)
18924887Schin {
18934887Schin 	Sfdouble_t		num;
18944887Schin 	register int		c;
18954887Schin 	register char		*str;
18964887Schin 	Sfio_t			*sp;
18978462SApril.Chin@Sun.COM 	Stk_t			*stkp = mp->shp->stk;
18984887Schin 	Fcin_t			save;
18998462SApril.Chin@Sun.COM 	struct slnod            *saveslp = mp->shp->st.staklist;
19004887Schin 	struct _mac_		savemac;
19018462SApril.Chin@Sun.COM 	int			savtop = stktell(stkp);
19028462SApril.Chin@Sun.COM 	char			lastc, *savptr = stkfreeze(stkp,0);
19034887Schin 	int			was_history = sh_isstate(SH_HISTORY);
19044887Schin 	int			was_verbose = sh_isstate(SH_VERBOSE);
19058462SApril.Chin@Sun.COM 	int			was_interactive = sh_isstate(SH_INTERACTIVE);
19068462SApril.Chin@Sun.COM 	int			newlines,bufsize,nextnewlines;
19074887Schin 	Namval_t		*np;
19088462SApril.Chin@Sun.COM 	mp->shp->argaddr = 0;
19094887Schin 	savemac = *mp;
19108462SApril.Chin@Sun.COM 	mp->shp->st.staklist=0;
19114887Schin 	if(type)
19124887Schin 	{
19134887Schin 		sp = 0;
19144887Schin 		fcseek(-1);
19158462SApril.Chin@Sun.COM 		if(!t)
19168462SApril.Chin@Sun.COM 			t = sh_dolparen((Lex_t*)mp->shp->lex_context);
19174887Schin 		if(t && t->tre.tretyp==TARITH)
19184887Schin 		{
19194887Schin 			fcsave(&save);
19208462SApril.Chin@Sun.COM 			if((t->ar.arexpr->argflag&ARG_RAW))
19218462SApril.Chin@Sun.COM 				num = arith_exec(t->ar.arcomp);
19228462SApril.Chin@Sun.COM 			else
19238462SApril.Chin@Sun.COM 				num = sh_arith(sh_mactrim(mp->shp,t->ar.arexpr->argval,3));
19244887Schin 		out_offset:
19258462SApril.Chin@Sun.COM 			stkset(stkp,savptr,savtop);
19264887Schin 			*mp = savemac;
19278462SApril.Chin@Sun.COM 			if((Sflong_t)num!=num)
19288462SApril.Chin@Sun.COM 				sfprintf(mp->shp->strbuf,"%.*Lg",LDBL_DIG,num);
19298462SApril.Chin@Sun.COM 			else if(num)
19308462SApril.Chin@Sun.COM 				sfprintf(mp->shp->strbuf,"%lld",(Sflong_t)num);
19314887Schin 			else
19328462SApril.Chin@Sun.COM 				sfprintf(mp->shp->strbuf,"%Lg",num);
19338462SApril.Chin@Sun.COM 			str = sfstruse(mp->shp->strbuf);
19344887Schin 			mac_copy(mp,str,strlen(str));
19358462SApril.Chin@Sun.COM 			mp->shp->st.staklist = saveslp;
19364887Schin 			fcrestore(&save);
19374887Schin 			return;
19384887Schin 		}
19394887Schin 	}
19404887Schin 	else
19414887Schin 	{
19424887Schin 		while(fcgetc(c)!='`' && c)
19434887Schin 		{
19444887Schin 			if(c==ESCAPE)
19454887Schin 			{
19464887Schin 				fcgetc(c);
19474887Schin 				if(!(isescchar(sh_lexstates[ST_QUOTE][c]) ||
19488462SApril.Chin@Sun.COM 				  (c=='"' && mp->quote)))
19498462SApril.Chin@Sun.COM 					sfputc(stkp,ESCAPE);
19504887Schin 			}
19518462SApril.Chin@Sun.COM 			sfputc(stkp,c);
19524887Schin 		}
19538462SApril.Chin@Sun.COM 		c = stktell(stkp);
19548462SApril.Chin@Sun.COM 		str=stkfreeze(stkp,1);
19554887Schin 		/* disable verbose and don't save in history file */
19564887Schin 		sh_offstate(SH_HISTORY);
19574887Schin 		sh_offstate(SH_VERBOSE);
19584887Schin 		if(mp->sp)
19594887Schin 			sfsync(mp->sp);	/* flush before executing command */
19604887Schin 		sp = sfnew(NIL(Sfio_t*),str,c,-1,SF_STRING|SF_READ);
19618462SApril.Chin@Sun.COM 		c = mp->shp->inlineno;
19628462SApril.Chin@Sun.COM 		mp->shp->inlineno = error_info.line+mp->shp->st.firstline;
19634887Schin 		t = (Shnode_t*)sh_parse(mp->shp, sp,SH_EOF|SH_NL);
19648462SApril.Chin@Sun.COM 		mp->shp->inlineno = c;
19658462SApril.Chin@Sun.COM 		type = 1;
19664887Schin 	}
19674887Schin #if KSHELL
19684887Schin 	if(t)
19694887Schin 	{
19704887Schin 		fcsave(&save);
19714887Schin 		sfclose(sp);
19728462SApril.Chin@Sun.COM 		if(t->tre.tretyp==0 && !t->com.comarg && !t->com.comset)
19734887Schin 		{
19744887Schin 			/* special case $(<file) and $(<#file) */
19754887Schin 			register int fd;
19764887Schin 			int r;
19774887Schin 			struct checkpt buff;
19784887Schin 			struct ionod *ip=0;
19794887Schin 			sh_pushcontext(&buff,SH_JMPIO);
19804887Schin 			if((ip=t->tre.treio) &&
19814887Schin 				((ip->iofile&IOLSEEK) || !(ip->iofile&IOUFD)) &&
19824887Schin 				(r=sigsetjmp(buff.buff,0))==0)
19838462SApril.Chin@Sun.COM 				fd = sh_redirect(mp->shp,ip,3);
19844887Schin 			else
19854887Schin 				fd = sh_chkopen(e_devnull);
19864887Schin 			sh_popcontext(&buff);
19874887Schin 			if(r==0 && ip && (ip->iofile&IOLSEEK))
19884887Schin 			{
19898462SApril.Chin@Sun.COM 				if(sp=mp->shp->sftable[fd])
19904887Schin 					num = sftell(sp);
19914887Schin 				else
19924887Schin 					num = lseek(fd, (off_t)0, SEEK_CUR);
19934887Schin 				goto out_offset;
19944887Schin 			}
19954887Schin 			sp = sfnew(NIL(Sfio_t*),(char*)malloc(IOBSIZE+1),IOBSIZE,fd,SF_READ|SF_MALLOC);
19968462SApril.Chin@Sun.COM 			type = 3;
19974887Schin 		}
19984887Schin 		else
19998462SApril.Chin@Sun.COM 			sp = sh_subshell(t,sh_isstate(SH_ERREXIT),type);
20004887Schin 		fcrestore(&save);
20014887Schin 	}
20024887Schin 	else
20034887Schin 		sp = sfopen(NIL(Sfio_t*),"","sr");
20048462SApril.Chin@Sun.COM 	sh_freeup(mp->shp);
20058462SApril.Chin@Sun.COM 	mp->shp->st.staklist = saveslp;
20064887Schin 	if(was_history)
20074887Schin 		sh_onstate(SH_HISTORY);
20084887Schin 	if(was_verbose)
20094887Schin 		sh_onstate(SH_VERBOSE);
20104887Schin #else
20114887Schin 	sp = sfpopen(NIL(Sfio_t*),str,"r");
20124887Schin #endif
20134887Schin 	*mp = savemac;
20148462SApril.Chin@Sun.COM 	np = sh_scoped(mp->shp,IFSNOD);
20158462SApril.Chin@Sun.COM 	nv_putval(np,mp->ifsp,NV_RDONLY);
20164887Schin 	mp->ifsp = nv_getval(np);
20178462SApril.Chin@Sun.COM 	stkset(stkp,savptr,savtop);
20184887Schin 	newlines = 0;
20194887Schin 	lastc = 0;
20204887Schin 	sfsetbuf(sp,(void*)sp,0);
20214887Schin 	bufsize = sfvalue(sp);
20224887Schin 	/* read command substitution output and put on stack or here-doc */
20234887Schin 	sfpool(sp, NIL(Sfio_t*), SF_WRITE);
20248462SApril.Chin@Sun.COM 	sh_offstate(SH_INTERACTIVE);
20254887Schin 	while((str=(char*)sfreserve(sp,SF_UNBOUND,0)) && (c = sfvalue(sp))>0)
20264887Schin 	{
20274887Schin #if SHOPT_CRNL
20284887Schin 		/* eliminate <cr> */
20294887Schin 		register char *dp;
20304887Schin 		char *buff = str;
20314887Schin 		while(c>1 && (*str !='\r'|| str[1]!='\n'))
20324887Schin 		{
20334887Schin 			c--;
20344887Schin 			str++;
20354887Schin 		}
20364887Schin 		dp = str;
20374887Schin 		while(c>1)
20384887Schin 		{
20394887Schin 			str++;
20404887Schin 			c--;
20414887Schin 			while(c>1 && (*str!='\r' || str[1]!='\n'))
20424887Schin 			{
20434887Schin 				c--;
20444887Schin 				*dp++ = *str++;
20454887Schin 			}
20464887Schin 		}
20474887Schin 		if(c)
20484887Schin 			*dp++ = *str++;
20494887Schin 		str = buff;
20504887Schin 		c = dp-str;
20514887Schin #endif /* SHOPT_CRNL */
20528462SApril.Chin@Sun.COM 		/* delay appending trailing new-lines */
20538462SApril.Chin@Sun.COM 		for(nextnewlines=0; c-->0 && str[c]=='\n'; nextnewlines++);
20548462SApril.Chin@Sun.COM 		if(c < 0)
20558462SApril.Chin@Sun.COM 		{
20568462SApril.Chin@Sun.COM 			newlines += nextnewlines;
20578462SApril.Chin@Sun.COM 			continue;
20588462SApril.Chin@Sun.COM 		}
20594887Schin 		if(newlines >0)
20604887Schin 		{
20614887Schin 			if(mp->sp)
20624887Schin 				sfnputc(mp->sp,'\n',newlines);
20638462SApril.Chin@Sun.COM 			else if(!mp->quote && mp->split && mp->shp->ifstable['\n'])
20644887Schin 				endfield(mp,0);
20658462SApril.Chin@Sun.COM 			else
20668462SApril.Chin@Sun.COM 				sfnputc(stkp,'\n',newlines);
20674887Schin 		}
20684887Schin 		else if(lastc)
20694887Schin 		{
20704887Schin 			mac_copy(mp,&lastc,1);
20714887Schin 			lastc = 0;
20724887Schin 		}
20738462SApril.Chin@Sun.COM 		newlines = nextnewlines;
20744887Schin 		if(++c < bufsize)
20754887Schin 			str[c] = 0;
20764887Schin 		else
20774887Schin 		{
20784887Schin 			/* can't write past buffer so save last character */
20794887Schin 			lastc = str[--c];
20804887Schin 			str[c] = 0;
20814887Schin 		}
20824887Schin 		mac_copy(mp,str,c);
20834887Schin 	}
20848462SApril.Chin@Sun.COM 	if(was_interactive)
20858462SApril.Chin@Sun.COM 		sh_onstate(SH_INTERACTIVE);
20868462SApril.Chin@Sun.COM 	if(mp->shp->spid)
20878462SApril.Chin@Sun.COM 		job_wait(mp->shp->spid);
20888462SApril.Chin@Sun.COM 	if(--newlines>0 && mp->shp->ifstable['\n']==S_DELIM)
20894887Schin 	{
20904887Schin 		if(mp->sp)
20914887Schin 			sfnputc(mp->sp,'\n',newlines);
20928462SApril.Chin@Sun.COM 		else if(!mp->quote && mp->split)
20938462SApril.Chin@Sun.COM 			while(newlines--)
20948462SApril.Chin@Sun.COM 				endfield(mp,1);
20958462SApril.Chin@Sun.COM 		else
20968462SApril.Chin@Sun.COM 			sfnputc(stkp,'\n',newlines);
20974887Schin 	}
20984887Schin 	if(lastc)
20994887Schin 		mac_copy(mp,&lastc,1);
21004887Schin 	sfclose(sp);
21014887Schin 	return;
21024887Schin }
21034887Schin 
21044887Schin /*
21054887Schin  * copy <str> onto the stack
21064887Schin  */
mac_copy(register Mac_t * mp,register const char * str,register int size)21074887Schin static void mac_copy(register Mac_t *mp,register const char *str, register int size)
21084887Schin {
21094887Schin 	register char		*state;
21104887Schin 	register const char	*cp=str;
21118462SApril.Chin@Sun.COM 	register int		c,n,nopat,len;
21128462SApril.Chin@Sun.COM 	Stk_t			*stkp=mp->shp->stk;
21134887Schin 	nopat = (mp->quote||mp->assign==1||mp->arith);
21144887Schin 	if(mp->zeros)
21154887Schin 	{
21164887Schin 		/* prevent leading 0's from becomming octal constants */
21174887Schin 		while(size>1 && *str=='0')
21184887Schin 			str++,size--;
21194887Schin 		mp->zeros = 0;
21204887Schin 		cp = str;
21214887Schin 	}
21224887Schin 	if(mp->sp)
21234887Schin 		sfwrite(mp->sp,str,size);
21244887Schin 	else if(mp->pattern>=2 || (mp->pattern && nopat))
21254887Schin 	{
21264887Schin 		state = sh_lexstates[ST_MACRO];
21274887Schin 		/* insert \ before file expansion characters */
21284887Schin 		while(size-->0)
21294887Schin 		{
21308462SApril.Chin@Sun.COM #if SHOPT_MULTIBYTE
21318462SApril.Chin@Sun.COM 			if(mbwide() && (len=mbsize(cp))>1)
21328462SApril.Chin@Sun.COM 			{
21338462SApril.Chin@Sun.COM 				cp += len;
21348462SApril.Chin@Sun.COM 				size -= (len-1);
21358462SApril.Chin@Sun.COM 				continue;
21368462SApril.Chin@Sun.COM 			}
21378462SApril.Chin@Sun.COM #endif
21384887Schin 			c = state[n= *(unsigned char*)cp++];
21394887Schin 			if(nopat&&(c==S_PAT||c==S_ESC||c==S_BRACT||c==S_ENDCH) && mp->pattern!=3)
21404887Schin 				c=1;
21414887Schin 			else if(mp->pattern==4 && (c==S_ESC||c==S_BRACT||c==S_ENDCH || isastchar(n)))
21424887Schin 				c=1;
21434887Schin 			else if(mp->pattern==2 && c==S_SLASH)
21444887Schin 				c=1;
21454887Schin 			else if(mp->pattern==3 && c==S_ESC && (state[*(unsigned char*)cp]==S_DIG||(*cp==ESCAPE)))
21464887Schin 			{
21474887Schin 				if(!(c=mp->quote))
21484887Schin 					cp++;
21494887Schin 			}
21504887Schin 			else
21514887Schin 				c=0;
21524887Schin 			if(c)
21534887Schin 			{
21544887Schin 				if(c = (cp-1) - str)
21558462SApril.Chin@Sun.COM 					sfwrite(stkp,str,c);
21568462SApril.Chin@Sun.COM 				sfputc(stkp,ESCAPE);
21574887Schin 				str = cp-1;
21584887Schin 			}
21594887Schin 		}
21604887Schin 		if(c = cp-str)
21618462SApril.Chin@Sun.COM 			sfwrite(stkp,str,c);
21624887Schin 	}
21634887Schin 	else if(!mp->quote && mp->split && (mp->ifs||mp->pattern))
21644887Schin 	{
21654887Schin 		/* split words at ifs characters */
21668462SApril.Chin@Sun.COM 		state = mp->shp->ifstable;
21674887Schin 		if(mp->pattern)
21684887Schin 		{
21694887Schin 			char *sp = "&|()";
21704887Schin 			while(c = *sp++)
21714887Schin 			{
21724887Schin 				if(state[c]==0)
21734887Schin 					state[c] = S_EPAT;
21744887Schin 			}
21754887Schin 			sp = "*?[{";
21764887Schin 			while(c = *sp++)
21774887Schin 			{
21784887Schin 				if(state[c]==0)
21794887Schin 					state[c] = S_PAT;
21804887Schin 			}
21814887Schin 			if(state[ESCAPE]==0)
21824887Schin 				state[ESCAPE] = S_ESC;
21834887Schin 		}
21844887Schin 		while(size-->0)
21854887Schin 		{
21868462SApril.Chin@Sun.COM 			n=state[c= *(unsigned char*)cp++];
21878462SApril.Chin@Sun.COM #if SHOPT_MULTIBYTE
21888462SApril.Chin@Sun.COM 			if(mbwide() && n!=S_MBYTE && (len=mbsize(cp-1))>1)
21898462SApril.Chin@Sun.COM 			{
21908462SApril.Chin@Sun.COM 				sfwrite(stkp,cp-1, len);
21918462SApril.Chin@Sun.COM 				cp += --len;
21928462SApril.Chin@Sun.COM 				size -= len;
21938462SApril.Chin@Sun.COM 				continue;
21948462SApril.Chin@Sun.COM 			}
21958462SApril.Chin@Sun.COM #endif
21968462SApril.Chin@Sun.COM 			if(n==S_ESC || n==S_EPAT)
21974887Schin 			{
21984887Schin 				/* don't allow extended patterns in this case */
21994887Schin 				mp->patfound = mp->pattern;
22008462SApril.Chin@Sun.COM 				sfputc(stkp,ESCAPE);
22014887Schin 			}
22024887Schin 			else if(n==S_PAT)
22034887Schin 				mp->patfound = mp->pattern;
22044887Schin 			else if(n && mp->ifs)
22054887Schin 			{
22064887Schin #if SHOPT_MULTIBYTE
22074887Schin 				if(n==S_MBYTE)
22084887Schin 				{
22094887Schin 					if(sh_strchr(mp->ifsp,cp-1)<0)
22104887Schin 						continue;
22114887Schin 					n = mbsize(cp-1) - 1;
22124887Schin 					if(n==-2)
22134887Schin 						n = 0;
22144887Schin 					cp += n;
22154887Schin 					size -= n;
22164887Schin 					n= S_DELIM;
22174887Schin 				}
22184887Schin #endif /* SHOPT_MULTIBYTE */
22194887Schin 				if(n==S_SPACE || n==S_NL)
22204887Schin 				{
22214887Schin 					while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL))
22224887Schin 						size--;
22234887Schin #if SHOPT_MULTIBYTE
22244887Schin 					if(n==S_MBYTE && sh_strchr(mp->ifsp,cp-1)>=0)
22254887Schin 					{
22264887Schin 						n = mbsize(cp-1) - 1;
22274887Schin 						if(n==-2)
22284887Schin 							n = 0;
22294887Schin 						cp += n;
22304887Schin 						size -= n;
22314887Schin 						n=S_DELIM;
22324887Schin 					}
22334887Schin 					else
22344887Schin #endif /* SHOPT_MULTIBYTE */
22354887Schin 					if(n==S_DELIM)
22364887Schin 						size--;
22374887Schin 				}
22384887Schin 				endfield(mp,n==S_DELIM||mp->quoted);
22394887Schin 				mp->patfound = 0;
22404887Schin 				if(n==S_DELIM)
22414887Schin 					while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL))
22424887Schin 						size--;
22434887Schin 				if(size<=0)
22444887Schin 					break;
22454887Schin 				cp--;
22464887Schin 				continue;
22474887Schin 
22484887Schin 			}
22498462SApril.Chin@Sun.COM 			sfputc(stkp,c);
22504887Schin 		}
22514887Schin 		if(mp->pattern)
22524887Schin 		{
22534887Schin 			cp = "&|()";
22544887Schin 			while(c = *cp++)
22554887Schin 			{
22564887Schin 				if(state[c]==S_EPAT)
22574887Schin 					state[c] = 0;
22584887Schin 			}
22594887Schin 			cp = "*?[{";
22604887Schin 			while(c = *cp++)
22614887Schin 			{
22624887Schin 				if(state[c]==S_PAT)
22634887Schin 					state[c] = 0;
22644887Schin 			}
22658462SApril.Chin@Sun.COM 			if(mp->shp->ifstable[ESCAPE]==S_ESC)
22668462SApril.Chin@Sun.COM 				mp->shp->ifstable[ESCAPE] = 0;
22674887Schin 		}
22684887Schin 	}
22694887Schin 	else
22708462SApril.Chin@Sun.COM 		sfwrite(stkp,str,size);
22714887Schin }
22724887Schin 
22734887Schin /*
22744887Schin  * Terminate field.
22754887Schin  * If field is null count field if <split> is non-zero
22764887Schin  * Do filename expansion of required
22774887Schin  */
endfield(register Mac_t * mp,int split)22784887Schin static void endfield(register Mac_t *mp,int split)
22794887Schin {
22808462SApril.Chin@Sun.COM 	register struct argnod	*argp;
22818462SApril.Chin@Sun.COM 	register int		count=0;
22828462SApril.Chin@Sun.COM 	Stk_t			*stkp = mp->shp->stk;
22838462SApril.Chin@Sun.COM 	if(stktell(stkp) > ARGVAL || split)
22844887Schin 	{
22858462SApril.Chin@Sun.COM 		argp = (struct argnod*)stkfreeze(stkp,1);
22864887Schin 		argp->argnxt.cp = 0;
22874887Schin 		argp->argflag = 0;
22884887Schin 		if(mp->patfound)
22894887Schin 		{
22908462SApril.Chin@Sun.COM 			mp->shp->argaddr = 0;
22914887Schin #if SHOPT_BRACEPAT
22924887Schin 			count = path_generate(argp,mp->arghead);
22934887Schin #else
22944887Schin 			count = path_expand(argp->argval,mp->arghead);
22954887Schin #endif /* SHOPT_BRACEPAT */
22964887Schin 			if(count)
22974887Schin 				mp->fields += count;
22984887Schin 			else if(split)	/* pattern is null string */
22994887Schin 				*argp->argval = 0;
23004887Schin 			else	/* pattern expands to nothing */
23014887Schin 				count = -1;
23024887Schin 		}
23034887Schin 		if(count==0)
23044887Schin 		{
23054887Schin 			argp->argchn.ap = *mp->arghead;
23064887Schin 			*mp->arghead = argp;
23074887Schin 			mp->fields++;
23084887Schin 		}
23094887Schin 		if(count>=0)
23104887Schin 		{
23114887Schin 			(*mp->arghead)->argflag |= ARG_MAKE;
23124887Schin 			if(mp->assign || sh_isoption(SH_NOGLOB))
23134887Schin 				argp->argflag |= ARG_RAW|ARG_EXP;
23144887Schin 		}
23158462SApril.Chin@Sun.COM 		stkseek(stkp,ARGVAL);
23164887Schin 	}
23174887Schin 	mp->quoted = mp->quote;
23184887Schin }
23194887Schin 
23204887Schin /*
23214887Schin  * Finds the right substring of STRING using the expression PAT
23224887Schin  * the longest substring is found when FLAG is set.
23234887Schin  */
substring(register const char * string,const char * pat,int match[],int flag)23244887Schin static int substring(register const char *string,const char *pat,int match[], int flag)
23254887Schin {
23264887Schin 	register const char *sp=string;
23274887Schin 	register int size,len,nmatch,n;
23284887Schin 	int smatch[2*(MATCH_MAX+1)];
23294887Schin 	if(flag)
23304887Schin 	{
23314887Schin 		if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_MAXIMAL))
23324887Schin 		{
23334887Schin 			memcpy(match,smatch,n*2*sizeof(smatch[0]));
23344887Schin 			return(n);
23354887Schin 		}
23364887Schin 		return(0);
23374887Schin 	}
23384887Schin 	size = len = strlen(sp);
23394887Schin 	sp += size;
23404887Schin 	while(sp>=string)
23414887Schin 	{
23424887Schin #if SHOPT_MULTIBYTE
23434887Schin 		if(mbwide())
23444887Schin 			sp = lastchar(string,sp);
23454887Schin #endif /* SHOPT_MULTIBYTE */
23464887Schin 		if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_LEFT|STR_MAXIMAL))
23474887Schin 		{
23484887Schin 			nmatch = n;
23494887Schin 			memcpy(match,smatch,n*2*sizeof(smatch[0]));
23504887Schin 			size = sp-string;
23514887Schin 			break;
23524887Schin 		}
23534887Schin 		sp--;
23544887Schin 	}
23554887Schin 	if(size==len)
23564887Schin 		return(0);
23574887Schin 	if(nmatch)
23584887Schin 	{
23594887Schin 		nmatch *=2;
23604887Schin 		while(--nmatch>=0)
23614887Schin 			match[nmatch] += size;
23624887Schin 	}
23634887Schin 	return(n);
23644887Schin }
23654887Schin 
23664887Schin #if SHOPT_MULTIBYTE
lastchar(const char * string,const char * endstring)23674887Schin 	static char	*lastchar(const char *string, const char *endstring)
23684887Schin 	{
23694887Schin 		register char *str = (char*)string;
23704887Schin 		register int c;
23714887Schin 		mbinit();
23724887Schin 		while(*str)
23734887Schin 		{
23744887Schin 			if((c=mbsize(str))<0)
23754887Schin 				c = 1;
23764887Schin 			if(str+c > endstring)
23774887Schin 				break;
23784887Schin 			str += c;
23794887Schin 		}
23804887Schin 		return(str);
23814887Schin 	}
23824887Schin #endif /* SHOPT_MULTIBYTE */
charlen(const char * string,int len)23834887Schin static int	charlen(const char *string,int len)
23844887Schin {
23854887Schin 	if(!string)
23864887Schin 		return(0);
23874887Schin #if SHOPT_MULTIBYTE
23884887Schin 	if(mbwide())
23894887Schin 	{
23904887Schin 		register const char *str = string, *strmax=string+len;
23914887Schin 		register int n=0;
23924887Schin 		mbinit();
23934887Schin 		if(len>0)
23944887Schin 		{
23954887Schin 			while(str<strmax && mbchar(str))
23964887Schin 				n++;
23974887Schin 		}
23984887Schin 		else while(mbchar(str))
23994887Schin 			n++;
24004887Schin 		return(n);
24014887Schin 	}
24024887Schin 	else
24034887Schin #endif /* SHOPT_MULTIBYTE */
24044887Schin 	{
24054887Schin 		if(len<0)
24064887Schin 			return(strlen(string));
24074887Schin 		return(len);
24084887Schin 	}
24094887Schin }
24104887Schin 
24114887Schin /*
24124887Schin  * This is the default tilde discipline function
24134887Schin  */
sh_btilde(int argc,char * argv[],void * context)24144887Schin static int sh_btilde(int argc, char *argv[], void *context)
24154887Schin {
24168462SApril.Chin@Sun.COM 	Shell_t *shp = ((Shbltin_t*)context)->shp;
24178462SApril.Chin@Sun.COM 	char *cp = sh_tilde(shp,argv[1]);
24184887Schin 	NOT_USED(argc);
24194887Schin 	if(!cp)
24204887Schin 		cp = argv[1];
24214887Schin 	sfputr(sfstdout, cp, '\n');
24224887Schin 	return(0);
24234887Schin }
24244887Schin 
24254887Schin /*
24264887Schin  * <offset> is byte offset for beginning of tilde string
24274887Schin  */
tilde_expand2(Shell_t * shp,register int offset)24288462SApril.Chin@Sun.COM static void tilde_expand2(Shell_t *shp, register int offset)
24294887Schin {
24308462SApril.Chin@Sun.COM 	char		shtilde[10], *av[3], *ptr=stkfreeze(shp->stk,1);
24314887Schin 	Sfio_t		*iop, *save=sfstdout;
24324887Schin 	Namval_t	*np;
24334887Schin 	static int	beenhere=0;
24344887Schin 	strcpy(shtilde,".sh.tilde");
24358462SApril.Chin@Sun.COM 	np = nv_open(shtilde,shp->fun_tree, NV_VARNAME|NV_NOARRAY|NV_NOASSIGN|NV_NOFAIL);
24364887Schin 	if(np && !beenhere)
24374887Schin 	{
24384887Schin 		beenhere = 1;
24394887Schin 		sh_addbuiltin(shtilde,sh_btilde,0);
24408462SApril.Chin@Sun.COM 		nv_onattr(np,NV_EXPORT);
24414887Schin 	}
24424887Schin 	av[0] = ".sh.tilde";
24434887Schin 	av[1] = &ptr[offset];
24444887Schin 	av[2] = 0;
24454887Schin 	iop = sftmp(IOBSIZE+1);;
24464887Schin 	sfset(iop,SF_READ,0);
24474887Schin 	sfstdout = iop;
24484887Schin 	if(np)
24494887Schin 		sh_fun(np, (Namval_t*)0, av);
24504887Schin 	else
24514887Schin 		sh_btilde(2, av, &sh);
24524887Schin 	sfstdout = save;
24538462SApril.Chin@Sun.COM 	stkset(shp->stk,ptr, offset);
24544887Schin 	sfseek(iop,(Sfoff_t)0,SEEK_SET);
24554887Schin 	sfset(iop,SF_READ,1);
24564887Schin 	if(ptr = sfreserve(iop, SF_UNBOUND, -1))
24574887Schin 	{
24584887Schin 		Sfoff_t n = sfvalue(iop);
24594887Schin 		while(ptr[n-1]=='\n')
24604887Schin 			n--;
24614887Schin 		if(n==1 && fcpeek(0)=='/' && ptr[n-1])
24624887Schin 			n--;
24634887Schin 		if(n)
24648462SApril.Chin@Sun.COM 			sfwrite(shp->stk,ptr,n);
24654887Schin 	}
24664887Schin 	else
24678462SApril.Chin@Sun.COM 		sfputr(shp->stk,av[1],0);
24684887Schin 	sfclose(iop);
24694887Schin }
24704887Schin 
24714887Schin /*
24724887Schin  * This routine is used to resolve ~ expansion.
24734887Schin  * A ~ by itself is replaced with the users login directory.
24744887Schin  * A ~- is replaced by the previous working directory in shell.
24754887Schin  * A ~+ is replaced by the present working directory in shell.
24764887Schin  * If ~name  is replaced with login directory of name.
24774887Schin  * If string doesn't start with ~ or ~... not found then 0 returned.
24784887Schin  */
24794887Schin 
sh_tilde(Shell_t * shp,register const char * string)24808462SApril.Chin@Sun.COM static char *sh_tilde(Shell_t *shp,register const char *string)
24814887Schin {
24824887Schin 	register char		*cp;
24834887Schin 	register int		c;
24844887Schin 	register struct passwd	*pw;
24854887Schin 	register Namval_t *np=0;
24864887Schin 	static Dt_t *logins_tree;
24874887Schin 	if(*string++!='~')
24884887Schin 		return(NIL(char*));
24894887Schin 	if((c = *string)==0)
24904887Schin 	{
24918462SApril.Chin@Sun.COM 		if(!(cp=nv_getval(sh_scoped(shp,HOME))))
24924887Schin 			cp = getlogin();
24934887Schin 		return(cp);
24944887Schin 	}
24954887Schin 	if((c=='-' || c=='+') && string[1]==0)
24964887Schin 	{
24974887Schin 		if(c=='+')
24988462SApril.Chin@Sun.COM 			cp = nv_getval(sh_scoped(shp,PWDNOD));
24994887Schin 		else
25008462SApril.Chin@Sun.COM 			cp = nv_getval(sh_scoped(shp,OLDPWDNOD));
25014887Schin 		return(cp);
25024887Schin 	}
25034887Schin 	if(logins_tree && (np=nv_search(string,logins_tree,0)))
25044887Schin 		return(nv_getval(np));
25054887Schin 	if(!(pw = getpwnam(string)))
25064887Schin 		return(NIL(char*));
25074887Schin 	if(!logins_tree)
25084887Schin 		logins_tree = dtopen(&_Nvdisc,Dtbag);
25094887Schin 	if(np=nv_search(string,logins_tree,NV_ADD))
25104887Schin 		nv_putval(np, pw->pw_dir,0);
25114887Schin 	return(pw->pw_dir);
25124887Schin }
25134887Schin 
25144887Schin /*
25154887Schin  * return values for special macros
25164887Schin  */
special(Shell_t * shp,register int c)25178462SApril.Chin@Sun.COM static char *special(Shell_t *shp,register int c)
25184887Schin {
25194887Schin 	if(c!='$')
25208462SApril.Chin@Sun.COM 		shp->argaddr = 0;
25214887Schin 	switch(c)
25224887Schin 	{
25234887Schin 	    case '@':
25244887Schin 	    case '*':
25258462SApril.Chin@Sun.COM 		return(shp->st.dolc>0?shp->st.dolv[1]:NIL(char*));
25264887Schin 	    case '#':
25274887Schin #if  SHOPT_FILESCAN
25288462SApril.Chin@Sun.COM 		if(shp->cur_line)
25294887Schin 		{
25308462SApril.Chin@Sun.COM 			getdolarg(shp,MAX_ARGN,(int*)0);
25318462SApril.Chin@Sun.COM 			return(ltos(shp->offsets[0]));
25324887Schin 		}
25334887Schin #endif  /* SHOPT_FILESCAN */
25348462SApril.Chin@Sun.COM 		return(ltos(shp->st.dolc));
25354887Schin 	    case '!':
25368462SApril.Chin@Sun.COM 		if(shp->bckpid)
25378462SApril.Chin@Sun.COM 			return(ltos(shp->bckpid));
25384887Schin 		break;
25394887Schin 	    case '$':
25404887Schin 		if(nv_isnull(SH_DOLLARNOD))
25418462SApril.Chin@Sun.COM 			return(ltos(shp->pid));
25424887Schin 		return(nv_getval(SH_DOLLARNOD));
25434887Schin 	    case '-':
25448462SApril.Chin@Sun.COM 		return(sh_argdolminus(shp->arg_context));
25454887Schin 	    case '?':
25468462SApril.Chin@Sun.COM 		return(ltos(shp->savexit));
25474887Schin 	    case 0:
254810898Sroland.mainz@nrubsig.org 		if(sh_isstate(SH_PROFILE) || shp->fn_depth==0 || !shp->st.cmdname)
25498462SApril.Chin@Sun.COM 			return(shp->shname);
25504887Schin 		else
255110898Sroland.mainz@nrubsig.org 			return(shp->st.cmdname);
25524887Schin 	}
25534887Schin 	return(NIL(char*));
25544887Schin }
25554887Schin 
25564887Schin /*
25574887Schin  * Handle macro expansion errors
25584887Schin  */
mac_error(Namval_t * np)25594887Schin static void mac_error(Namval_t *np)
25604887Schin {
25614887Schin 	if(np)
25624887Schin 		nv_close(np);
25634887Schin 	errormsg(SH_DICT,ERROR_exit(1),e_subst,fcfirst());
25644887Schin }
25654887Schin 
25664887Schin /*
25674887Schin  * Given pattern/string, replace / with 0 and return pointer to string
25688462SApril.Chin@Sun.COM  * \ characters are stripped from string.  The \ are stripped in the
25698462SApril.Chin@Sun.COM  * replacement string unless followed by a digit or \.
25704887Schin  */
mac_getstring(char * pattern)25714887Schin static char *mac_getstring(char *pattern)
25724887Schin {
25738462SApril.Chin@Sun.COM 	register char	*cp=pattern, *rep=0, *dp;
25748462SApril.Chin@Sun.COM 	register int	c;
25754887Schin 	while(c = *cp++)
25764887Schin 	{
25778462SApril.Chin@Sun.COM 		if(c==ESCAPE && (!rep || (*cp && strchr("&|()[]*?",*cp))))
25788462SApril.Chin@Sun.COM 		{
25798462SApril.Chin@Sun.COM 			c = *cp++;
25808462SApril.Chin@Sun.COM 		}
25818462SApril.Chin@Sun.COM 		else if(!rep && c=='/')
25824887Schin 		{
25834887Schin 			cp[-1] = 0;
25848462SApril.Chin@Sun.COM 			rep = dp = cp;
25858462SApril.Chin@Sun.COM 			continue;
25864887Schin 		}
25878462SApril.Chin@Sun.COM 		if(rep)
25888462SApril.Chin@Sun.COM 			*dp++ = c;
25894887Schin 	}
25908462SApril.Chin@Sun.COM 	if(rep)
25918462SApril.Chin@Sun.COM 		*dp = 0;
25928462SApril.Chin@Sun.COM 	return(rep);
25934887Schin }
2594