xref: /titanic_50/usr/src/lib/libshell/common/sh/macro.c (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*3e14f97fSRoger A. Faulkner *          Copyright (c) 1982-2010 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6da2e3ebdSchin *                  Common Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10da2e3ebdSchin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11da2e3ebdSchin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12da2e3ebdSchin *                                                                      *
13da2e3ebdSchin *              Information and Software Systems Research               *
14da2e3ebdSchin *                            AT&T Research                             *
15da2e3ebdSchin *                           Florham Park NJ                            *
16da2e3ebdSchin *                                                                      *
17da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
18da2e3ebdSchin *                                                                      *
19da2e3ebdSchin ***********************************************************************/
20da2e3ebdSchin #pragma prototyped
21da2e3ebdSchin /*
22da2e3ebdSchin  * Shell macro expander
23da2e3ebdSchin  * expands ~
24da2e3ebdSchin  * expands ${...}
25da2e3ebdSchin  * expands $(...)
26da2e3ebdSchin  * expands $((...))
27da2e3ebdSchin  * expands `...`
28da2e3ebdSchin  *
29da2e3ebdSchin  *   David Korn
30da2e3ebdSchin  *   AT&T Labs
31da2e3ebdSchin  *
32da2e3ebdSchin  */
33da2e3ebdSchin 
34da2e3ebdSchin #include	"defs.h"
35da2e3ebdSchin #include	<fcin.h>
36da2e3ebdSchin #include	<pwd.h>
37da2e3ebdSchin #include	"name.h"
38da2e3ebdSchin #include	"variables.h"
39da2e3ebdSchin #include	"shlex.h"
40da2e3ebdSchin #include	"io.h"
4134f9b3eeSRoland Mainz #include	"jobs.h"
42da2e3ebdSchin #include	"shnodes.h"
43da2e3ebdSchin #include	"path.h"
44da2e3ebdSchin #include	"national.h"
45da2e3ebdSchin #include	"streval.h"
46da2e3ebdSchin 
47da2e3ebdSchin #undef STR_GROUP
48da2e3ebdSchin #ifndef STR_GROUP
49da2e3ebdSchin #   define STR_GROUP	0
50da2e3ebdSchin #endif
51da2e3ebdSchin 
52da2e3ebdSchin #if !SHOPT_MULTIBYTE
53da2e3ebdSchin #define mbchar(p)       (*(unsigned char*)p++)
54da2e3ebdSchin #endif
55da2e3ebdSchin 
56da2e3ebdSchin static int	_c_;
57da2e3ebdSchin typedef struct  _mac_
58da2e3ebdSchin {
59da2e3ebdSchin 	Shell_t		*shp;		/* pointer to shell interpreter */
60da2e3ebdSchin 	Sfio_t		*sp;		/* stream pointer for here-document */
61da2e3ebdSchin 	struct argnod	**arghead;	/* address of head of argument list */
62da2e3ebdSchin 	char		*ifsp;		/* pointer to IFS value */
63da2e3ebdSchin 	int		fields;		/* number of fields */
64da2e3ebdSchin 	short		quoted;		/* set when word has quotes */
65da2e3ebdSchin 	unsigned char	ifs;		/* first char of IFS */
66da2e3ebdSchin 	char		quote;		/* set within double quoted contexts */
67da2e3ebdSchin 	char		lit;		/* set within single quotes */
68da2e3ebdSchin 	char		split;		/* set when word splittin is possible */
69da2e3ebdSchin 	char		pattern;	/* set when file expansion follows */
70da2e3ebdSchin 	char		patfound;	/* set if pattern character found */
71da2e3ebdSchin 	char		assign;		/* set for assignments */
72da2e3ebdSchin 	char		arith;		/* set for ((...)) */
73da2e3ebdSchin 	char		let;		/* set when expanding let arguments */
74da2e3ebdSchin 	char		zeros;		/* strip leading zeros when set */
757c2fbfb3SApril Chin 	char		arrayok;	/* $x[] ok for arrays */
767c2fbfb3SApril Chin 	char		subcopy;	/* set when copying subscript */
777c2fbfb3SApril Chin 	int		dotdot;		/* set for .. in subscript */
78da2e3ebdSchin 	void		*nvwalk;	/* for name space walking*/
79da2e3ebdSchin } Mac_t;
80da2e3ebdSchin 
81da2e3ebdSchin #undef ESCAPE
82da2e3ebdSchin #define ESCAPE		'\\'
83da2e3ebdSchin #define isescchar(s)	((s)>S_QUOTE)
84da2e3ebdSchin #define isqescchar(s)	((s)>=S_QUOTE)
85da2e3ebdSchin #define isbracechar(c)	((c)==RBRACE || (_c_=sh_lexstates[ST_BRACE][c])==S_MOD1 ||_c_==S_MOD2)
86da2e3ebdSchin #define ltos(x)		fmtbase((long)(x),0,0)
87da2e3ebdSchin 
88da2e3ebdSchin /* type of macro expansions */
89da2e3ebdSchin #define M_BRACE		1	/* ${var}	*/
90da2e3ebdSchin #define M_TREE		2	/* ${var.}	*/
91da2e3ebdSchin #define M_SIZE		3	/* ${#var}	*/
92da2e3ebdSchin #define M_VNAME		4	/* ${!var}	*/
93da2e3ebdSchin #define M_SUBNAME	5	/* ${!var[sub]}	*/
94da2e3ebdSchin #define M_NAMESCAN	6	/* ${!var*}	*/
95da2e3ebdSchin #define M_NAMECOUNT	7	/* ${#var*}	*/
96da2e3ebdSchin #define M_TYPE		8	/* ${@var}	*/
97da2e3ebdSchin 
98da2e3ebdSchin static int	substring(const char*, const char*, int[], int);
99da2e3ebdSchin static void	copyto(Mac_t*, int, int);
1007c2fbfb3SApril Chin static void	comsubst(Mac_t*, Shnode_t*, int);
101da2e3ebdSchin static int	varsub(Mac_t*);
102da2e3ebdSchin static void	mac_copy(Mac_t*,const char*, int);
1037c2fbfb3SApril Chin static void	tilde_expand2(Shell_t*,int);
1047c2fbfb3SApril Chin static char 	*sh_tilde(Shell_t*,const char*);
1057c2fbfb3SApril Chin static char	*special(Shell_t *,int);
106da2e3ebdSchin static void	endfield(Mac_t*,int);
107da2e3ebdSchin static void	mac_error(Namval_t*);
108da2e3ebdSchin static char	*mac_getstring(char*);
109da2e3ebdSchin static int	charlen(const char*,int);
110da2e3ebdSchin #if SHOPT_MULTIBYTE
111da2e3ebdSchin     static char	*lastchar(const char*,const char*);
112da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
113da2e3ebdSchin 
sh_macopen(Shell_t * shp)114da2e3ebdSchin void *sh_macopen(Shell_t *shp)
115da2e3ebdSchin {
116da2e3ebdSchin 	void *addr = newof(0,Mac_t,1,0);
117da2e3ebdSchin 	Mac_t *mp = (Mac_t*)addr;
118da2e3ebdSchin 	mp->shp = shp;
119da2e3ebdSchin 	return(addr);
120da2e3ebdSchin }
121da2e3ebdSchin 
122da2e3ebdSchin /*
123da2e3ebdSchin  * perform only parameter substitution and catch failures
124da2e3ebdSchin  */
sh_mactry(Shell_t * shp,register char * string)1257c2fbfb3SApril Chin char *sh_mactry(Shell_t *shp,register char *string)
126da2e3ebdSchin {
127da2e3ebdSchin 	if(string)
128da2e3ebdSchin 	{
129da2e3ebdSchin 		int		jmp_val;
1307c2fbfb3SApril Chin 		int		savexit = shp->savexit;
131da2e3ebdSchin 		struct checkpt	buff;
132da2e3ebdSchin 		sh_pushcontext(&buff,SH_JMPSUB);
133da2e3ebdSchin 		jmp_val = sigsetjmp(buff.buff,0);
134da2e3ebdSchin 		if(jmp_val == 0)
1357c2fbfb3SApril Chin 			string = sh_mactrim(shp,string,0);
136da2e3ebdSchin 		sh_popcontext(&buff);
1377c2fbfb3SApril Chin 		shp->savexit = savexit;
138da2e3ebdSchin 		return(string);
139da2e3ebdSchin 	}
140da2e3ebdSchin 	return("");
141da2e3ebdSchin }
142da2e3ebdSchin 
143da2e3ebdSchin /*
144da2e3ebdSchin  * Perform parameter expansion, command substitution, and arithmetic
145da2e3ebdSchin  * expansion on <str>.
146da2e3ebdSchin  * If <mode> greater than 1 file expansion is performed if the result
147da2e3ebdSchin  * yields a single pathname.
148da2e3ebdSchin  * If <mode> negative, than expansion rules for assignment are applied.
149da2e3ebdSchin  */
sh_mactrim(Shell_t * shp,char * str,register int mode)1507c2fbfb3SApril Chin char *sh_mactrim(Shell_t *shp, char *str, register int mode)
151da2e3ebdSchin {
1527c2fbfb3SApril Chin 	register Mac_t	*mp = (Mac_t*)shp->mac_context;
1537c2fbfb3SApril Chin 	Stk_t		*stkp = shp->stk;
154da2e3ebdSchin 	Mac_t		savemac;
155da2e3ebdSchin 	savemac = *mp;
1567c2fbfb3SApril Chin 	stkseek(stkp,0);
157da2e3ebdSchin 	mp->arith = (mode==3);
158da2e3ebdSchin 	mp->let = 0;
1597c2fbfb3SApril Chin 	shp->argaddr = 0;
160da2e3ebdSchin 	mp->pattern = (mode==1||mode==2);
161da2e3ebdSchin 	mp->patfound = 0;
1627c2fbfb3SApril Chin 	mp->assign = 0;
1637c2fbfb3SApril Chin 	if(mode<0)
1647c2fbfb3SApril Chin 		mp->assign = -mode;
165da2e3ebdSchin 	mp->quoted = mp->lit = mp->split = mp->quote = 0;
166da2e3ebdSchin 	mp->sp = 0;
1677c2fbfb3SApril Chin 	if(mp->ifsp=nv_getval(sh_scoped(shp,IFSNOD)))
168da2e3ebdSchin 		mp->ifs = *mp->ifsp;
169da2e3ebdSchin 	else
170da2e3ebdSchin 		mp->ifs = ' ';
1717c2fbfb3SApril Chin 	stkseek(stkp,0);
172da2e3ebdSchin 	fcsopen(str);
173da2e3ebdSchin 	copyto(mp,0,mp->arith);
1747c2fbfb3SApril Chin 	str = stkfreeze(stkp,1);
175da2e3ebdSchin 	if(mode==2)
176da2e3ebdSchin 	{
177da2e3ebdSchin 		/* expand only if unique */
178da2e3ebdSchin 		struct argnod *arglist=0;
179da2e3ebdSchin 		if((mode=path_expand(str,&arglist))==1)
180da2e3ebdSchin 			str = arglist->argval;
181da2e3ebdSchin 		else if(mode>1)
182da2e3ebdSchin 			errormsg(SH_DICT,ERROR_exit(1),e_ambiguous,str);
183da2e3ebdSchin 		sh_trim(str);
184da2e3ebdSchin 	}
185da2e3ebdSchin 	*mp = savemac;
186da2e3ebdSchin 	return(str);
187da2e3ebdSchin }
188da2e3ebdSchin 
189da2e3ebdSchin /*
190da2e3ebdSchin  * Perform all the expansions on the argument <argp>
191da2e3ebdSchin  */
sh_macexpand(Shell_t * shp,register struct argnod * argp,struct argnod ** arghead,int flag)1927c2fbfb3SApril Chin int sh_macexpand(Shell_t* shp, register struct argnod *argp, struct argnod **arghead,int flag)
193da2e3ebdSchin {
194da2e3ebdSchin 	register int	flags = argp->argflag;
195da2e3ebdSchin 	register char	*str = argp->argval;
1967c2fbfb3SApril Chin 	register Mac_t  *mp = (Mac_t*)shp->mac_context;
1977c2fbfb3SApril Chin 	char		**saveargaddr = shp->argaddr;
198da2e3ebdSchin 	Mac_t		savemac;
1997c2fbfb3SApril Chin 	Stk_t		*stkp = shp->stk;
200da2e3ebdSchin 	savemac = *mp;
201da2e3ebdSchin 	mp->sp = 0;
2027c2fbfb3SApril Chin 	if(mp->ifsp=nv_getval(sh_scoped(shp,IFSNOD)))
203da2e3ebdSchin 		mp->ifs = *mp->ifsp;
204da2e3ebdSchin 	else
205da2e3ebdSchin 		mp->ifs = ' ';
2067c2fbfb3SApril Chin 	if((flag&ARG_OPTIMIZE) && !shp->indebug)
2077c2fbfb3SApril Chin 		shp->argaddr = (char**)&argp->argchn.ap;
208da2e3ebdSchin 	else
2097c2fbfb3SApril Chin 		shp->argaddr = 0;
210da2e3ebdSchin 	mp->arghead = arghead;
211da2e3ebdSchin 	mp->quoted = mp->lit = mp->quote = 0;
212da2e3ebdSchin 	mp->arith = ((flag&ARG_ARITH)!=0);
213da2e3ebdSchin 	mp->let = ((flag&ARG_LET)!=0);
214da2e3ebdSchin 	mp->split = !(flag&ARG_ASSIGN);
215da2e3ebdSchin 	mp->assign = !mp->split;
216da2e3ebdSchin 	mp->pattern = mp->split && !(flag&ARG_NOGLOB) && !sh_isoption(SH_NOGLOB);
2177c2fbfb3SApril Chin 	mp->arrayok = mp->arith || (flag&ARG_ARRAYOK);
218da2e3ebdSchin 	str = argp->argval;
219da2e3ebdSchin 	fcsopen(str);
220da2e3ebdSchin 	mp->fields = 0;
221da2e3ebdSchin 	if(!arghead)
222da2e3ebdSchin 	{
223da2e3ebdSchin 		mp->split = 0;
224da2e3ebdSchin 		mp->pattern = ((flag&ARG_EXP)!=0);
2257c2fbfb3SApril Chin 		stkseek(stkp,0);
226da2e3ebdSchin 	}
227da2e3ebdSchin 	else
228da2e3ebdSchin 	{
2297c2fbfb3SApril Chin 		stkseek(stkp,ARGVAL);
2307c2fbfb3SApril Chin 		*stkptr(stkp,ARGVAL-1) = 0;
231da2e3ebdSchin 	}
232da2e3ebdSchin 	mp->patfound = 0;
2337c2fbfb3SApril Chin 	if(mp->pattern)
2347c2fbfb3SApril Chin 		mp->arrayok = 0;
235da2e3ebdSchin 	copyto(mp,0,mp->arith);
236da2e3ebdSchin 	if(!arghead)
237da2e3ebdSchin 	{
2387c2fbfb3SApril Chin 		argp->argchn.cp = stkfreeze(stkp,1);
2397c2fbfb3SApril Chin 		if(shp->argaddr)
240da2e3ebdSchin 			argp->argflag |= ARG_MAKE;
241da2e3ebdSchin 	}
242da2e3ebdSchin 	else
243da2e3ebdSchin 	{
244da2e3ebdSchin 		endfield(mp,mp->quoted);
245da2e3ebdSchin 		flags = mp->fields;
2467c2fbfb3SApril Chin 		if(flags==1 && shp->argaddr)
247da2e3ebdSchin 			argp->argchn.ap = *arghead;
248da2e3ebdSchin 	}
2497c2fbfb3SApril Chin 	shp->argaddr = saveargaddr;
250da2e3ebdSchin 	*mp = savemac;
251da2e3ebdSchin 	return(flags);
252da2e3ebdSchin }
253da2e3ebdSchin 
254da2e3ebdSchin /*
255da2e3ebdSchin  * Expand here document which is stored in <infile> or <string>
256da2e3ebdSchin  * The result is written to <outfile>
257da2e3ebdSchin  */
sh_machere(Shell_t * shp,Sfio_t * infile,Sfio_t * outfile,char * string)2587c2fbfb3SApril Chin void sh_machere(Shell_t *shp,Sfio_t *infile, Sfio_t *outfile, char *string)
259da2e3ebdSchin {
260da2e3ebdSchin 	register int	c,n;
261da2e3ebdSchin 	register const char	*state = sh_lexstates[ST_QUOTE];
262da2e3ebdSchin 	register char	*cp;
2637c2fbfb3SApril Chin 	register Mac_t	*mp = (Mac_t*)shp->mac_context;
2647c2fbfb3SApril Chin 	Lex_t		*lp = (Lex_t*)mp->shp->lex_context;
265da2e3ebdSchin 	Fcin_t		save;
266da2e3ebdSchin 	Mac_t		savemac;
2677c2fbfb3SApril Chin 	Stk_t		*stkp = shp->stk;
268da2e3ebdSchin 	savemac = *mp;
2697c2fbfb3SApril Chin 	stkseek(stkp,0);
2707c2fbfb3SApril Chin 	shp->argaddr = 0;
271da2e3ebdSchin 	mp->sp = outfile;
272da2e3ebdSchin 	mp->split = mp->assign = mp->pattern = mp->patfound = mp->lit = mp->arith = mp->let = 0;
273da2e3ebdSchin 	mp->quote = 1;
2747c2fbfb3SApril Chin 	mp->ifsp = nv_getval(sh_scoped(shp,IFSNOD));
275da2e3ebdSchin 	mp->ifs = ' ';
276da2e3ebdSchin 	fcsave(&save);
277da2e3ebdSchin 	if(infile)
278da2e3ebdSchin 		fcfopen(infile);
279da2e3ebdSchin 	else
280da2e3ebdSchin 		fcsopen(string);
2817c2fbfb3SApril Chin 	fcnotify(0,lp);
282da2e3ebdSchin 	cp = fcseek(0);
283da2e3ebdSchin 	while(1)
284da2e3ebdSchin 	{
285da2e3ebdSchin #if SHOPT_MULTIBYTE
286da2e3ebdSchin 		if(mbwide())
287da2e3ebdSchin 		{
288da2e3ebdSchin 			do
289da2e3ebdSchin 			{
290da2e3ebdSchin 				ssize_t len;
291da2e3ebdSchin 				switch(len = mbsize(cp))
292da2e3ebdSchin 				{
293da2e3ebdSchin 				    case -1:	/* illegal multi-byte char */
294da2e3ebdSchin 				    case 0:
295da2e3ebdSchin 				    case 1:
296da2e3ebdSchin 					n=state[*(unsigned char*)cp++];
297da2e3ebdSchin 					break;
298da2e3ebdSchin 				    default:
2997c2fbfb3SApril Chin 					/* use state of alpha character */
300da2e3ebdSchin 					n=state['a'];
301da2e3ebdSchin 					cp += len;
302da2e3ebdSchin 				}
303da2e3ebdSchin 			}
304da2e3ebdSchin 			while(n == 0);
305da2e3ebdSchin 		}
306da2e3ebdSchin 		else
307da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
308da2e3ebdSchin 		while((n=state[*(unsigned char*)cp++])==0);
309da2e3ebdSchin 		if(n==S_NL || n==S_QUOTE || n==S_RBRA)
310da2e3ebdSchin 			continue;
311da2e3ebdSchin 		if(c=(cp-1)-fcseek(0))
312da2e3ebdSchin 			sfwrite(outfile,fcseek(0),c);
313da2e3ebdSchin 		cp = fcseek(c+1);
314da2e3ebdSchin 		switch(n)
315da2e3ebdSchin 		{
316da2e3ebdSchin 		    case S_EOF:
317da2e3ebdSchin 			if((n=fcfill()) <=0)
318da2e3ebdSchin 			{
319da2e3ebdSchin 				/* ignore 0 byte when reading from file */
320da2e3ebdSchin 				if(n==0 && fcfile())
321da2e3ebdSchin 					continue;
322da2e3ebdSchin 				fcrestore(&save);
323da2e3ebdSchin 				*mp = savemac;
324da2e3ebdSchin 				return;
325da2e3ebdSchin 			}
326da2e3ebdSchin 			cp = fcseek(-1);
327da2e3ebdSchin 			continue;
328da2e3ebdSchin 		    case S_ESC:
329da2e3ebdSchin 			fcgetc(c);
330da2e3ebdSchin 			cp=fcseek(-1);
331da2e3ebdSchin 			if(c>0)
332da2e3ebdSchin 				cp++;
333da2e3ebdSchin 			if(!isescchar(state[c]))
334da2e3ebdSchin 				sfputc(outfile,ESCAPE);
335da2e3ebdSchin 			continue;
336da2e3ebdSchin 		    case S_GRAVE:
3377c2fbfb3SApril Chin 			comsubst(mp,(Shnode_t*)0,0);
338da2e3ebdSchin 			break;
339da2e3ebdSchin 		    case S_DOL:
340da2e3ebdSchin 			c = fcget();
341da2e3ebdSchin 			if(c=='.')
342da2e3ebdSchin 				goto regular;
343da2e3ebdSchin 		    again:
344da2e3ebdSchin 			switch(n=sh_lexstates[ST_DOL][c])
345da2e3ebdSchin 			{
346da2e3ebdSchin 			    case S_ALP: case S_SPC1: case S_SPC2:
347da2e3ebdSchin 			    case S_DIG: case S_LBRA:
348da2e3ebdSchin 			    {
349da2e3ebdSchin 				Fcin_t	save2;
3507c2fbfb3SApril Chin 				int	offset = stktell(stkp);
351da2e3ebdSchin 				int	offset2;
3527c2fbfb3SApril Chin 				sfputc(stkp,c);
353da2e3ebdSchin 				if(n==S_LBRA)
3547c2fbfb3SApril Chin 				{
3557c2fbfb3SApril Chin 					c = fcget();
3567c2fbfb3SApril Chin 					fcseek(-1);
3577c2fbfb3SApril Chin 					if(sh_lexstates[ST_NORM][c]==S_BREAK)
3587c2fbfb3SApril Chin 					{
3597c2fbfb3SApril Chin 						comsubst(mp,(Shnode_t*)0,2);
3607c2fbfb3SApril Chin 						break;
3617c2fbfb3SApril Chin 					}
3627c2fbfb3SApril Chin 					sh_lexskip(lp,RBRACE,1,ST_BRACE);
3637c2fbfb3SApril Chin 				}
364da2e3ebdSchin 				else if(n==S_ALP)
365da2e3ebdSchin 				{
366da2e3ebdSchin 					while(fcgetc(c),isaname(c))
3677c2fbfb3SApril Chin 						sfputc(stkp,c);
368da2e3ebdSchin 					fcseek(-1);
369da2e3ebdSchin 				}
3707c2fbfb3SApril Chin 				sfputc(stkp,0);
3717c2fbfb3SApril Chin 				offset2 = stktell(stkp);
372da2e3ebdSchin 				fcsave(&save2);
3737c2fbfb3SApril Chin 				fcsopen(stkptr(stkp,offset));
374da2e3ebdSchin 				varsub(mp);
3757c2fbfb3SApril Chin 				if(c=stktell(stkp)-offset2)
3767c2fbfb3SApril Chin 					sfwrite(outfile,(char*)stkptr(stkp,offset2),c);
377da2e3ebdSchin 				fcrestore(&save2);
3787c2fbfb3SApril Chin 				stkseek(stkp,offset);
379da2e3ebdSchin 				break;
380da2e3ebdSchin 			    }
381da2e3ebdSchin 			    case S_PAR:
3827c2fbfb3SApril Chin 				comsubst(mp,(Shnode_t*)0,1);
383da2e3ebdSchin 				break;
384da2e3ebdSchin 			    case S_EOF:
385da2e3ebdSchin 				if((c=fcfill()) > 0)
386da2e3ebdSchin 					goto again;
387da2e3ebdSchin 				/* FALL THRU */
388da2e3ebdSchin 			    default:
389da2e3ebdSchin 			    regular:
390da2e3ebdSchin 				sfputc(outfile,'$');
391da2e3ebdSchin 				fcseek(-1);
392da2e3ebdSchin 				break;
393da2e3ebdSchin 			}
394da2e3ebdSchin 		}
395da2e3ebdSchin 		cp = fcseek(0);
396da2e3ebdSchin 	}
397da2e3ebdSchin }
398da2e3ebdSchin 
399da2e3ebdSchin /*
400da2e3ebdSchin  * expand argument but do not trim pattern characters
401da2e3ebdSchin  */
sh_macpat(Shell_t * shp,register struct argnod * arg,int flags)4027c2fbfb3SApril Chin char *sh_macpat(Shell_t *shp,register struct argnod *arg, int flags)
403da2e3ebdSchin {
404da2e3ebdSchin 	register char *sp = arg->argval;
405da2e3ebdSchin 	if((arg->argflag&ARG_RAW))
406da2e3ebdSchin 		return(sp);
4077c2fbfb3SApril Chin 	sh_stats(STAT_ARGEXPAND);
408da2e3ebdSchin 	if(flags&ARG_OPTIMIZE)
409da2e3ebdSchin 		arg->argchn.ap=0;
410da2e3ebdSchin 	if(!(sp=arg->argchn.cp))
411da2e3ebdSchin 	{
4127c2fbfb3SApril Chin 		sh_macexpand(shp,arg,NIL(struct argnod**),flags|ARG_ARRAYOK);
413da2e3ebdSchin 		sp = arg->argchn.cp;
414da2e3ebdSchin 		if(!(flags&ARG_OPTIMIZE) || !(arg->argflag&ARG_MAKE))
415da2e3ebdSchin 			arg->argchn.cp = 0;
416da2e3ebdSchin 		arg->argflag &= ~ARG_MAKE;
417da2e3ebdSchin 	}
418da2e3ebdSchin 	else
4197c2fbfb3SApril Chin 		sh_stats(STAT_ARGHITS);
420da2e3ebdSchin 	return(sp);
421da2e3ebdSchin }
422da2e3ebdSchin 
423da2e3ebdSchin /*
424da2e3ebdSchin  * Process the characters up to <endch> or end of input string
425da2e3ebdSchin  */
copyto(register Mac_t * mp,int endch,int newquote)426da2e3ebdSchin static void copyto(register Mac_t *mp,int endch, int newquote)
427da2e3ebdSchin {
428da2e3ebdSchin 	register int	c,n;
429da2e3ebdSchin 	register const char	*state = sh_lexstates[ST_MACRO];
430da2e3ebdSchin 	register char	*cp,*first;
4317c2fbfb3SApril Chin 	Lex_t		*lp = (Lex_t*)mp->shp->lex_context;
432da2e3ebdSchin 	int		tilde = -1;
433da2e3ebdSchin 	int		oldquote = mp->quote;
434da2e3ebdSchin 	int		ansi_c = 0;
435da2e3ebdSchin 	int		paren = 0;
436da2e3ebdSchin 	int		ere = 0;
437da2e3ebdSchin 	int		brace = 0;
438da2e3ebdSchin 	Sfio_t		*sp = mp->sp;
4397c2fbfb3SApril Chin 	Stk_t		*stkp = mp->shp->stk;
440da2e3ebdSchin 	mp->sp = NIL(Sfio_t*);
441da2e3ebdSchin 	mp->quote = newquote;
442da2e3ebdSchin 	first = cp = fcseek(0);
4439a6f360eSCasper H.S. Dik 	if(!mp->quote && *cp=='~' && cp[1]!=LPAREN)
4447c2fbfb3SApril Chin 		tilde = stktell(stkp);
445da2e3ebdSchin 	/* handle // operator specially */
446da2e3ebdSchin 	if(mp->pattern==2 && *cp=='/')
447da2e3ebdSchin 		cp++;
448da2e3ebdSchin 	while(1)
449da2e3ebdSchin 	{
450da2e3ebdSchin #if SHOPT_MULTIBYTE
451da2e3ebdSchin 		if(mbwide())
452da2e3ebdSchin 		{
453da2e3ebdSchin 			ssize_t len;
454da2e3ebdSchin 			do
455da2e3ebdSchin 			{
456da2e3ebdSchin 				switch(len = mbsize(cp))
457da2e3ebdSchin 				{
458da2e3ebdSchin 				    case -1:	/* illegal multi-byte char */
459da2e3ebdSchin 				    case 0:
460da2e3ebdSchin 					len = 1;
461da2e3ebdSchin 				    case 1:
462da2e3ebdSchin 					n = state[*(unsigned char*)cp++];
463da2e3ebdSchin 					break;
464da2e3ebdSchin 				    default:
465da2e3ebdSchin 					/* treat as if alpha */
466da2e3ebdSchin 					cp += len;
467da2e3ebdSchin 					n=state['a'];
468da2e3ebdSchin 				}
469da2e3ebdSchin 			}
470da2e3ebdSchin 			while(n == 0);
471da2e3ebdSchin 			c = (cp-len) - first;
472da2e3ebdSchin 		}
473da2e3ebdSchin 		else
474da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
475da2e3ebdSchin 		{
476da2e3ebdSchin 			while((n=state[*(unsigned char*)cp++])==0);
477da2e3ebdSchin 			c = (cp-1) - first;
478da2e3ebdSchin 		}
479da2e3ebdSchin 		switch(n)
480da2e3ebdSchin 		{
481da2e3ebdSchin 		    case S_ESC:
482da2e3ebdSchin 			if(ansi_c)
483da2e3ebdSchin 			{
484da2e3ebdSchin 				/* process ANSI-C escape character */
485da2e3ebdSchin 				char *addr= --cp;
486da2e3ebdSchin 				if(c)
4877c2fbfb3SApril Chin 					sfwrite(stkp,first,c);
488da2e3ebdSchin 				c = chresc(cp,&addr);
489da2e3ebdSchin 				cp = addr;
490da2e3ebdSchin 				first = fcseek(cp-first);
491da2e3ebdSchin #if SHOPT_MULTIBYTE
492da2e3ebdSchin 				if(c > UCHAR_MAX && mbwide())
493da2e3ebdSchin 				{
494da2e3ebdSchin 					int		i;
495da2e3ebdSchin 					unsigned char	mb[8];
496da2e3ebdSchin 
497da2e3ebdSchin 					n = wctomb((char*)mb, c);
498da2e3ebdSchin 					for(i=0;i<n;i++)
4997c2fbfb3SApril Chin 						sfputc(stkp,mb[i]);
500da2e3ebdSchin 				}
501da2e3ebdSchin 				else
502da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
5037c2fbfb3SApril Chin 				sfputc(stkp,c);
504da2e3ebdSchin 				if(c==ESCAPE && mp->pattern)
5057c2fbfb3SApril Chin 					sfputc(stkp,ESCAPE);
506da2e3ebdSchin 				break;
507da2e3ebdSchin 			}
508da2e3ebdSchin 			else if(sh_isoption(SH_BRACEEXPAND) && mp->pattern==4 && (*cp==',' || *cp==LBRACE || *cp==RBRACE || *cp=='.'))
509da2e3ebdSchin 				break;
510da2e3ebdSchin 			else if(mp->split && endch && !mp->quote && !mp->lit)
511da2e3ebdSchin 			{
512da2e3ebdSchin 				if(c)
513da2e3ebdSchin 					mac_copy(mp,first,c);
514da2e3ebdSchin 				cp = fcseek(c+2);
515da2e3ebdSchin 				if(c= cp[-1])
516da2e3ebdSchin 				{
5177c2fbfb3SApril Chin 					sfputc(stkp,c);
518da2e3ebdSchin 					if(c==ESCAPE)
5197c2fbfb3SApril Chin 						sfputc(stkp,ESCAPE);
520da2e3ebdSchin 				}
521da2e3ebdSchin 				else
522da2e3ebdSchin 					cp--;
523da2e3ebdSchin 				first = cp;
524da2e3ebdSchin 				break;
525da2e3ebdSchin 			}
526da2e3ebdSchin 			n = state[*(unsigned char*)cp];
527da2e3ebdSchin 			if(n==S_ENDCH && *cp!=endch)
528da2e3ebdSchin 				n = S_PAT;
529da2e3ebdSchin 			if(mp->pattern)
530da2e3ebdSchin 			{
531da2e3ebdSchin 				/* preserve \digit for pattern matching */
532da2e3ebdSchin 				/* also \alpha for extended patterns */
53334f9b3eeSRoland Mainz 				if(!mp->lit && !mp->quote)
53434f9b3eeSRoland Mainz 				{
53534f9b3eeSRoland Mainz 					if((n==S_DIG || ((paren+ere) && sh_lexstates[ST_DOL][*(unsigned char*)cp]==S_ALP)))
536da2e3ebdSchin 						break;
53734f9b3eeSRoland Mainz 					if(ere && mp->pattern==1 && strchr(".[()*+?{|^$&!",*cp))
53834f9b3eeSRoland Mainz 						break;
53934f9b3eeSRoland Mainz 				}
540da2e3ebdSchin 				/* followed by file expansion */
541da2e3ebdSchin 				if(!mp->lit && (n==S_ESC || (!mp->quote &&
542da2e3ebdSchin 					(n==S_PAT||n==S_ENDCH||n==S_SLASH||n==S_BRACT||*cp=='-'))))
543da2e3ebdSchin 				{
544da2e3ebdSchin 					cp += (n!=S_EOF);
5457c2fbfb3SApril Chin 					if(ere && n==S_ESC && *cp =='\\' && cp[1]=='$')
5467c2fbfb3SApril Chin 					{
5477c2fbfb3SApril Chin 						/* convert \\\$ into \$' */
5487c2fbfb3SApril Chin 						sfwrite(stkp,first,c+1);
5497c2fbfb3SApril Chin 						cp = first = fcseek(c+3);
5507c2fbfb3SApril Chin 					}
551da2e3ebdSchin 					break;
552da2e3ebdSchin 				}
5537c2fbfb3SApril Chin 				if(!(ere && *cp=='$') && (mp->lit || (mp->quote && !isqescchar(n) && n!=S_ENDCH)))
554da2e3ebdSchin 				{
555da2e3ebdSchin 					/* add \ for file expansion */
5567c2fbfb3SApril Chin 					sfwrite(stkp,first,c+1);
557da2e3ebdSchin 					first = fcseek(c);
558da2e3ebdSchin 					break;
559da2e3ebdSchin 				}
560da2e3ebdSchin 			}
561da2e3ebdSchin 			if(mp->lit)
562da2e3ebdSchin 				break;
563da2e3ebdSchin 			if(!mp->quote || isqescchar(n) || n==S_ENDCH)
564da2e3ebdSchin 			{
565da2e3ebdSchin 				/* eliminate \ */
566da2e3ebdSchin 				if(c)
5677c2fbfb3SApril Chin 					sfwrite(stkp,first,c);
568da2e3ebdSchin 				/* check new-line joining */
569da2e3ebdSchin 				first = fcseek(c+1);
570da2e3ebdSchin 			}
571da2e3ebdSchin 			cp += (n!=S_EOF);
572da2e3ebdSchin 			break;
573da2e3ebdSchin 		    case S_GRAVE: case S_DOL:
574da2e3ebdSchin 			if(mp->lit)
575da2e3ebdSchin 				break;
576da2e3ebdSchin 			if(c)
577da2e3ebdSchin 			{
578da2e3ebdSchin 				if(mp->split && !mp->quote && endch)
579da2e3ebdSchin 					mac_copy(mp,first,c);
580da2e3ebdSchin 				else
5817c2fbfb3SApril Chin 					sfwrite(stkp,first,c);
582da2e3ebdSchin 			}
583da2e3ebdSchin 			first = fcseek(c+1);
584da2e3ebdSchin 			c = mp->pattern;
585da2e3ebdSchin 			if(n==S_GRAVE)
5867c2fbfb3SApril Chin 				comsubst(mp,(Shnode_t*)0,0);
587da2e3ebdSchin 			else if((n= *cp)==0 || !varsub(mp))
588da2e3ebdSchin 			{
589da2e3ebdSchin 				if(n=='\'' && !mp->quote)
590da2e3ebdSchin 					ansi_c = 1;
591da2e3ebdSchin 				else if(mp->quote || n!='"')
5927c2fbfb3SApril Chin 					sfputc(stkp,'$');
593da2e3ebdSchin 			}
594da2e3ebdSchin 			cp = first = fcseek(0);
595da2e3ebdSchin 			if(*cp)
596da2e3ebdSchin 				mp->pattern = c;
597da2e3ebdSchin 			break;
598da2e3ebdSchin 		    case S_ENDCH:
599da2e3ebdSchin 			if((mp->lit || cp[-1]!=endch || mp->quote!=newquote))
600da2e3ebdSchin 				goto pattern;
601da2e3ebdSchin 			if(endch==RBRACE && *cp==LPAREN && mp->pattern && brace)
602da2e3ebdSchin 				goto pattern;
603da2e3ebdSchin 		    case S_EOF:
604da2e3ebdSchin 			if(c)
605da2e3ebdSchin 			{
606da2e3ebdSchin 				if(mp->split && !mp->quote && !mp->lit && endch)
607da2e3ebdSchin 					mac_copy(mp,first,c);
608da2e3ebdSchin 				else
6097c2fbfb3SApril Chin 					sfwrite(stkp,first,c);
610da2e3ebdSchin 			}
611da2e3ebdSchin 			c += (n!=S_EOF);
612da2e3ebdSchin 			first = fcseek(c);
613da2e3ebdSchin 			if(tilde>=0)
6147c2fbfb3SApril Chin 				tilde_expand2(mp->shp,tilde);
615da2e3ebdSchin 			goto done;
616da2e3ebdSchin 		    case S_QUOTE:
617da2e3ebdSchin 			if(mp->lit || mp->arith)
618da2e3ebdSchin 				break;
619da2e3ebdSchin 		    case S_LIT:
620da2e3ebdSchin 			if(mp->arith)
621da2e3ebdSchin 			{
622da2e3ebdSchin 				if((*cp=='`' || *cp=='[') && cp[1]=='\'')
623da2e3ebdSchin 					cp +=2;
624da2e3ebdSchin 				break;
625da2e3ebdSchin 			}
626da2e3ebdSchin 			if(n==S_LIT && mp->quote)
627da2e3ebdSchin 				break;
628da2e3ebdSchin 			if(c)
629da2e3ebdSchin 			{
630da2e3ebdSchin 				if(mp->split && endch && !mp->quote && !mp->lit)
631da2e3ebdSchin 					mac_copy(mp,first,c);
632da2e3ebdSchin 				else
6337c2fbfb3SApril Chin 					sfwrite(stkp,first,c);
634da2e3ebdSchin 			}
635da2e3ebdSchin 			first = fcseek(c+1);
636da2e3ebdSchin 			if(n==S_LIT)
637da2e3ebdSchin 			{
638da2e3ebdSchin 				if(mp->quote)
639da2e3ebdSchin 					continue;
640da2e3ebdSchin 				if(mp->lit)
641da2e3ebdSchin 					mp->lit = ansi_c = 0;
642da2e3ebdSchin 				else
643da2e3ebdSchin 					mp->lit = 1;
644da2e3ebdSchin 			}
645da2e3ebdSchin 			else
646da2e3ebdSchin 				mp->quote = !mp->quote;
647da2e3ebdSchin 			mp->quoted++;
648da2e3ebdSchin 			break;
649da2e3ebdSchin 		    case S_BRACT:
6507c2fbfb3SApril Chin 			if(mp->arith || (((mp->assign&1) || endch==RBRACT) &&
651da2e3ebdSchin 				!(mp->quote || mp->lit)))
652da2e3ebdSchin 			{
653da2e3ebdSchin 				int offset=0,oldpat = mp->pattern;
6547c2fbfb3SApril Chin 				int oldarith = mp->arith, oldsub=mp->subcopy;
6557c2fbfb3SApril Chin 				sfwrite(stkp,first,++c);
65634f9b3eeSRoland Mainz 				if(mp->assign&1)
65734f9b3eeSRoland Mainz 				{
65834f9b3eeSRoland Mainz 					if(first[c-2]=='.')
6597c2fbfb3SApril Chin 						offset = stktell(stkp);
66034f9b3eeSRoland Mainz 					if(isastchar(*cp) && cp[1]==']')
66134f9b3eeSRoland Mainz 						errormsg(SH_DICT,ERROR_exit(1),
66234f9b3eeSRoland Mainz e_badsubscript,*cp);
66334f9b3eeSRoland Mainz 				}
664da2e3ebdSchin 				first = fcseek(c);
665da2e3ebdSchin 				mp->pattern = 4;
666da2e3ebdSchin 				mp->arith = 0;
6677c2fbfb3SApril Chin 				mp->subcopy = 0;
668da2e3ebdSchin 				copyto(mp,RBRACT,0);
6697c2fbfb3SApril Chin 				mp->subcopy = oldsub;
670da2e3ebdSchin 				mp->arith = oldarith;
671da2e3ebdSchin 				mp->pattern = oldpat;
6727c2fbfb3SApril Chin 				sfputc(stkp,RBRACT);
673da2e3ebdSchin 				if(offset)
674da2e3ebdSchin 				{
6757c2fbfb3SApril Chin 					cp = stkptr(stkp,stktell(stkp));
6767c2fbfb3SApril Chin 					if(sh_checkid(stkptr(stkp,offset),cp)!=cp)
6777c2fbfb3SApril Chin 						stkseek(stkp,stktell(stkp)-2);
678da2e3ebdSchin 				}
679da2e3ebdSchin 				cp = first = fcseek(0);
680da2e3ebdSchin 				break;
681da2e3ebdSchin 			}
682da2e3ebdSchin 		    case S_PAT:
683da2e3ebdSchin 			if(mp->pattern && !(mp->quote || mp->lit))
684da2e3ebdSchin 			{
685da2e3ebdSchin 				mp->patfound = mp->pattern;
686da2e3ebdSchin 				if((n=cp[-1])==LPAREN)
687da2e3ebdSchin 				{
688da2e3ebdSchin 					paren++;
689da2e3ebdSchin 					if((cp-first)>1 && cp[-2]=='~')
690da2e3ebdSchin 					{
691da2e3ebdSchin 						char *p = cp;
692da2e3ebdSchin 						while((c=mbchar(p)) && c!=RPAREN && c!='E');
69334f9b3eeSRoland Mainz 						ere = (c=='E'||c=='A');
694da2e3ebdSchin 					}
695da2e3ebdSchin 				}
696da2e3ebdSchin 				else if(n==RPAREN)
697da2e3ebdSchin 					--paren;
698da2e3ebdSchin 			}
699da2e3ebdSchin 			goto pattern;
7007c2fbfb3SApril Chin 		    case S_COM:
7017c2fbfb3SApril Chin 			if(mp->pattern==4 && (mp->quote || mp->lit))
7027c2fbfb3SApril Chin 			{
7037c2fbfb3SApril Chin 				if(c)
7047c2fbfb3SApril Chin 				{
7057c2fbfb3SApril Chin 					sfwrite(stkp,first,c);
7067c2fbfb3SApril Chin 					first = fcseek(c);
7077c2fbfb3SApril Chin 				}
7087c2fbfb3SApril Chin 				sfputc(stkp,ESCAPE);
7097c2fbfb3SApril Chin 			}
7107c2fbfb3SApril Chin 			break;
711da2e3ebdSchin 		    case S_BRACE:
712da2e3ebdSchin 			if(!(mp->quote || mp->lit))
713da2e3ebdSchin 			{
714da2e3ebdSchin 				mp->patfound = mp->split && sh_isoption(SH_BRACEEXPAND);
715da2e3ebdSchin 				brace = 1;
716da2e3ebdSchin 			}
717da2e3ebdSchin 		    pattern:
718da2e3ebdSchin 			if(!mp->pattern || !(mp->quote || mp->lit))
719da2e3ebdSchin 			{
720da2e3ebdSchin 				/* mark beginning of {a,b} */
721da2e3ebdSchin 				if(n==S_BRACE && endch==0 && mp->pattern)
722da2e3ebdSchin 					mp->pattern=4;
723da2e3ebdSchin 				if(n==S_SLASH && mp->pattern==2)
724da2e3ebdSchin 					mp->pattern=3;
725da2e3ebdSchin 				break;
726da2e3ebdSchin 			}
727da2e3ebdSchin 			if(mp->pattern==3)
728da2e3ebdSchin 				break;
729da2e3ebdSchin 			if(c)
7307c2fbfb3SApril Chin 				sfwrite(stkp,first,c);
731da2e3ebdSchin 			first = fcseek(c);
7327c2fbfb3SApril Chin 			sfputc(stkp,ESCAPE);
733da2e3ebdSchin 			break;
734da2e3ebdSchin 		    case S_EQ:
735da2e3ebdSchin 			if(mp->assign==1)
736da2e3ebdSchin 			{
737da2e3ebdSchin 				if(*cp=='~' && !endch && !mp->quote && !mp->lit)
7387c2fbfb3SApril Chin 					tilde = stktell(stkp)+(c+1);
739da2e3ebdSchin 				mp->assign = 2;
740da2e3ebdSchin 			}
741da2e3ebdSchin 			break;
742da2e3ebdSchin 		    case S_SLASH:
743da2e3ebdSchin 		    case S_COLON:
744da2e3ebdSchin 			if(tilde >=0)
745da2e3ebdSchin 			{
746da2e3ebdSchin 				if(c)
7477c2fbfb3SApril Chin 					sfwrite(stkp,first,c);
748da2e3ebdSchin 				first = fcseek(c);
7497c2fbfb3SApril Chin 				tilde_expand2(mp->shp,tilde);
750da2e3ebdSchin 				tilde = -1;
751da2e3ebdSchin 				c=0;
752da2e3ebdSchin 			}
753da2e3ebdSchin 			if(n==S_COLON && mp->assign==2 && *cp=='~' && endch==0 && !mp->quote &&!mp->lit)
7547c2fbfb3SApril Chin 				tilde = stktell(stkp)+(c+1);
755da2e3ebdSchin 			else if(n==S_SLASH && mp->pattern==2)
756da2e3ebdSchin #if 0
757da2e3ebdSchin 				goto pattern;
758da2e3ebdSchin #else
759da2e3ebdSchin 			{
760da2e3ebdSchin 				if(mp->quote || mp->lit)
761da2e3ebdSchin 					goto pattern;
7627c2fbfb3SApril Chin 				sfwrite(stkp,first,c+1);
763da2e3ebdSchin 				first = fcseek(c+1);
7647c2fbfb3SApril Chin 				c = stktell(stkp);
7657c2fbfb3SApril Chin 				sh_lexskip(lp,RBRACE,0,ST_NESTED);
7667c2fbfb3SApril Chin 				stkseek(stkp,c);
767da2e3ebdSchin 				cp = fcseek(-1);
7687c2fbfb3SApril Chin 				sfwrite(stkp,first,cp-first);
769da2e3ebdSchin 				first=cp;
770da2e3ebdSchin 			}
771da2e3ebdSchin #endif
772da2e3ebdSchin 			break;
7737c2fbfb3SApril Chin 		    case S_DOT:
7747c2fbfb3SApril Chin 			if(*cp=='.' && mp->subcopy==1)
7757c2fbfb3SApril Chin 			{
7767c2fbfb3SApril Chin 				sfwrite(stkp,first,c);
7777c2fbfb3SApril Chin 				sfputc(stkp,0);
7787c2fbfb3SApril Chin 				mp->dotdot = stktell(stkp);
7797c2fbfb3SApril Chin 				cp = first = fcseek(c+2);
7807c2fbfb3SApril Chin 			}
7817c2fbfb3SApril Chin 			break;
782da2e3ebdSchin 		}
783da2e3ebdSchin 	}
784da2e3ebdSchin done:
785da2e3ebdSchin 	mp->sp = sp;
786da2e3ebdSchin 	mp->quote = oldquote;
787da2e3ebdSchin }
788da2e3ebdSchin 
789da2e3ebdSchin /*
790da2e3ebdSchin  * copy <str> to stack performing sub-expression substitutions
791da2e3ebdSchin  */
mac_substitute(Mac_t * mp,register char * cp,char * str,register int subexp[],int subsize)792da2e3ebdSchin static void mac_substitute(Mac_t *mp, register char *cp,char *str,register int subexp[],int subsize)
793da2e3ebdSchin {
794da2e3ebdSchin 	register int	c,n;
795da2e3ebdSchin 	register char *first=fcseek(0);
796da2e3ebdSchin 	char		*ptr;
797da2e3ebdSchin 	Mac_t		savemac;
7987c2fbfb3SApril Chin 	Stk_t		*stkp = mp->shp->stk;
7997c2fbfb3SApril Chin 	n = stktell(stkp);
800da2e3ebdSchin 	savemac = *mp;
801da2e3ebdSchin 	mp->pattern = 3;
802da2e3ebdSchin 	mp->split = 0;
803da2e3ebdSchin 	fcsopen(cp);
804da2e3ebdSchin 	copyto(mp,0,0);
8057c2fbfb3SApril Chin 	sfputc(stkp,0);
8067c2fbfb3SApril Chin 	ptr = cp = strdup(stkptr(stkp,n));
8077c2fbfb3SApril Chin 	stkseek(stkp,n);
808da2e3ebdSchin 	*mp = savemac;
809da2e3ebdSchin 	fcsopen(first);
810da2e3ebdSchin 	first = cp;
811da2e3ebdSchin 	while(1)
812da2e3ebdSchin 	{
813da2e3ebdSchin 		while((c= *cp++) && c!=ESCAPE);
814da2e3ebdSchin 		if(c==0)
815da2e3ebdSchin 			break;
816da2e3ebdSchin 		if((n= *cp++)=='\\' || n==RBRACE || (n>='0' && n<='9' && (n-='0')<subsize))
817da2e3ebdSchin 		{
818da2e3ebdSchin 			c = cp-first-2;
819da2e3ebdSchin 			if(c)
820da2e3ebdSchin 				mac_copy(mp,first,c);
821da2e3ebdSchin 			first=cp;
822da2e3ebdSchin 			if(n=='\\' || n==RBRACE)
823da2e3ebdSchin 			{
824da2e3ebdSchin 				first--;
825da2e3ebdSchin 				continue;
826da2e3ebdSchin 			}
827da2e3ebdSchin 			if((c=subexp[2*n])>=0)
828da2e3ebdSchin 			{
829da2e3ebdSchin 				if((n=subexp[2*n+1]-c)>0)
830da2e3ebdSchin 					mac_copy(mp,str+c,n);
831da2e3ebdSchin 			}
832da2e3ebdSchin 		}
833da2e3ebdSchin 		else if(n==0)
834da2e3ebdSchin 			break;
835da2e3ebdSchin 	}
836da2e3ebdSchin 	if(n=cp-first-1)
837da2e3ebdSchin 		mac_copy(mp,first,n);
838da2e3ebdSchin 	free(ptr);
839da2e3ebdSchin }
840da2e3ebdSchin 
841da2e3ebdSchin #if  SHOPT_FILESCAN
842da2e3ebdSchin #define	MAX_OFFSETS	 (sizeof(shp->offsets)/sizeof(shp->offsets[0]))
843da2e3ebdSchin #define MAX_ARGN	(32*1024)
844da2e3ebdSchin 
845da2e3ebdSchin /*
846da2e3ebdSchin  * compute the arguments $1 ... $n and $# from the current line as needed
847da2e3ebdSchin  * save line offsets in the offsets array.
848da2e3ebdSchin  */
getdolarg(Shell_t * shp,int n,int * size)849da2e3ebdSchin static char *getdolarg(Shell_t *shp, int n, int *size)
850da2e3ebdSchin {
851da2e3ebdSchin 	register int c=S_DELIM, d=shp->ifstable['\\'];
852da2e3ebdSchin 	register unsigned char *first,*last,*cp = (unsigned char*)shp->cur_line;
853da2e3ebdSchin 	register int m=shp->offsets[0],delim=0;
854da2e3ebdSchin 	if(m==0)
855da2e3ebdSchin 		return(0);
856da2e3ebdSchin 	if(m<0)
857da2e3ebdSchin 		m = 0;
858da2e3ebdSchin 	else if(n<=m)
859da2e3ebdSchin 		m = n-1;
860da2e3ebdSchin 	else
861da2e3ebdSchin 		m--;
862da2e3ebdSchin 	if(m >= MAX_OFFSETS-1)
863da2e3ebdSchin 		m =  MAX_OFFSETS-2;
864da2e3ebdSchin 	cp += shp->offsets[m+1];
865da2e3ebdSchin 	n -= m;
866da2e3ebdSchin 	shp->ifstable['\\'] = 0;
867da2e3ebdSchin 	shp->ifstable[0] = S_EOF;
868da2e3ebdSchin 	while(1)
869da2e3ebdSchin 	{
870da2e3ebdSchin 		if(c==S_DELIM)
871da2e3ebdSchin 			while(shp->ifstable[*cp++]==S_SPACE);
872da2e3ebdSchin 		first = --cp;
873da2e3ebdSchin 		if(++m < MAX_OFFSETS)
874da2e3ebdSchin 			shp->offsets[m] = (first-(unsigned char*)shp->cur_line);
875da2e3ebdSchin 		while((c=shp->ifstable[*cp++])==0);
876da2e3ebdSchin 		last = cp-1;
877da2e3ebdSchin 		if(c==S_SPACE)
878da2e3ebdSchin 			while((c=shp->ifstable[*cp++])==S_SPACE);
879da2e3ebdSchin 		if(--n==0 || c==S_EOF)
880da2e3ebdSchin 		{
881da2e3ebdSchin 			if(last==first && c==S_EOF && (!delim || (m>1)))
882da2e3ebdSchin 			{
883da2e3ebdSchin 				n++;
884da2e3ebdSchin 				m--;
885da2e3ebdSchin 			}
886da2e3ebdSchin 			break;
887da2e3ebdSchin 		}
888da2e3ebdSchin 		delim = (c==S_DELIM);
889da2e3ebdSchin 	}
890da2e3ebdSchin 	shp->ifstable['\\'] = d;
891da2e3ebdSchin 	if(m > shp->offsets[0])
892da2e3ebdSchin 		shp->offsets[0] = m;
893da2e3ebdSchin 	if(n)
894da2e3ebdSchin 		first = last = 0;
895da2e3ebdSchin 	if(size)
896da2e3ebdSchin 		*size = last-first;
897da2e3ebdSchin 	return((char*)first);
898da2e3ebdSchin }
899da2e3ebdSchin #endif /* SHOPT_FILESCAN */
900da2e3ebdSchin 
901da2e3ebdSchin /*
902da2e3ebdSchin  * get the prefix after name reference resolution
903da2e3ebdSchin  */
prefix(Shell_t * shp,char * id)9047c2fbfb3SApril Chin static char *prefix(Shell_t *shp, char *id)
905da2e3ebdSchin {
906da2e3ebdSchin 	Namval_t *np;
90734f9b3eeSRoland Mainz 	register char *sub=0, *cp = strchr(id,'.');
908da2e3ebdSchin 	if(cp)
909da2e3ebdSchin 	{
910da2e3ebdSchin 		*cp = 0;
9117c2fbfb3SApril Chin 		np = nv_search(id, shp->var_tree,0);
912da2e3ebdSchin 		*cp = '.';
913da2e3ebdSchin 		if(isastchar(cp[1]))
914da2e3ebdSchin 			cp[1] = 0;
915da2e3ebdSchin 		if(np && nv_isref(np))
916da2e3ebdSchin 		{
917da2e3ebdSchin 			int n;
918da2e3ebdSchin 			char *sp;
9197c2fbfb3SApril Chin 			shp->argaddr = 0;
92034f9b3eeSRoland Mainz 			while(nv_isref(np) && np->nvalue.cp)
92134f9b3eeSRoland Mainz 			{
92234f9b3eeSRoland Mainz 				sub = nv_refsub(np);
923da2e3ebdSchin 				np = nv_refnode(np);
92434f9b3eeSRoland Mainz 				if(sub)
92534f9b3eeSRoland Mainz 					nv_putsub(np,sub,0L);
92634f9b3eeSRoland Mainz 			}
92734f9b3eeSRoland Mainz 			id = (char*)malloc(strlen(cp)+1+(n=strlen(sp=nv_name(np)))+ (sub?strlen(sub)+3:1));
928da2e3ebdSchin 			memcpy(id,sp,n);
92934f9b3eeSRoland Mainz 			if(sub)
93034f9b3eeSRoland Mainz 			{
93134f9b3eeSRoland Mainz 				id[n++] = '[';
93234f9b3eeSRoland Mainz 				strcpy(&id[n],sub);
93334f9b3eeSRoland Mainz 				n+= strlen(sub)+1;
93434f9b3eeSRoland Mainz 				id[n-1] = ']';
93534f9b3eeSRoland Mainz 			}
93634f9b3eeSRoland Mainz 			strcpy(&id[n],cp);
937da2e3ebdSchin 			return(id);
938da2e3ebdSchin 		}
939da2e3ebdSchin 	}
940da2e3ebdSchin 	return(strdup(id));
941da2e3ebdSchin }
942da2e3ebdSchin 
943da2e3ebdSchin /*
944da2e3ebdSchin  * copy to ']' onto the stack and return offset to it
945da2e3ebdSchin  */
subcopy(Mac_t * mp,int flag)946da2e3ebdSchin static int subcopy(Mac_t *mp, int flag)
947da2e3ebdSchin {
948da2e3ebdSchin 	int split = mp->split;
949da2e3ebdSchin 	int xpattern = mp->pattern;
9507c2fbfb3SApril Chin 	int loc = stktell(mp->shp->stk);
951da2e3ebdSchin 	int xarith = mp->arith;
9527c2fbfb3SApril Chin 	int arrayok = mp->arrayok;
953da2e3ebdSchin 	mp->split = 0;
954da2e3ebdSchin 	mp->arith = 0;
955da2e3ebdSchin 	mp->pattern = flag?4:0;
9567c2fbfb3SApril Chin 	mp->arrayok=1;
9577c2fbfb3SApril Chin 	mp->subcopy++;
9587c2fbfb3SApril Chin 	mp->dotdot = 0;
959da2e3ebdSchin 	copyto(mp,RBRACT,0);
9607c2fbfb3SApril Chin 	mp->subcopy = 0;
961da2e3ebdSchin 	mp->pattern = xpattern;
962da2e3ebdSchin 	mp->split = split;
963da2e3ebdSchin 	mp->arith = xarith;
9647c2fbfb3SApril Chin 	mp->arrayok = arrayok;
965da2e3ebdSchin 	return(loc);
966da2e3ebdSchin }
967da2e3ebdSchin 
9687c2fbfb3SApril Chin /*
9697c2fbfb3SApril Chin  * if name is a discipline function, run the function and put the results
9707c2fbfb3SApril Chin  * on the stack so that ${x.foo} behaves like ${ x.foo;}
9717c2fbfb3SApril Chin  */
sh_macfun(Shell_t * shp,const char * name,int offset)9727c2fbfb3SApril Chin int sh_macfun(Shell_t *shp, const char *name, int offset)
9737c2fbfb3SApril Chin {
9747c2fbfb3SApril Chin 	Namval_t	*np, *nq;
9757c2fbfb3SApril Chin 	np = nv_bfsearch(name,shp->fun_tree,&nq,(char**)0);
9767c2fbfb3SApril Chin 	if(np)
9777c2fbfb3SApril Chin 	{
9787c2fbfb3SApril Chin 		/* treat ${x.foo} as ${x.foo;} */
9797c2fbfb3SApril Chin 		Shnode_t *tp;
9807c2fbfb3SApril Chin 		char buff[sizeof(struct dolnod)+sizeof(char*)];
9817c2fbfb3SApril Chin 		struct comnod node;
9827c2fbfb3SApril Chin 		struct dolnod *dp = (struct dolnod*)buff;
9837c2fbfb3SApril Chin 		memset(&node,0,sizeof(node));
9847c2fbfb3SApril Chin 		memset(&buff,0,sizeof(buff));
9857c2fbfb3SApril Chin 		tp = (Shnode_t*)&node;
9867c2fbfb3SApril Chin 		tp->com.comarg = (struct argnod*)dp;
9877c2fbfb3SApril Chin 		tp->com.comline = shp->inlineno;
98834f9b3eeSRoland Mainz 		dp->dolnum = 1;
9897c2fbfb3SApril Chin 		dp->dolval[0] = strdup(name);
9907c2fbfb3SApril Chin 		stkseek(shp->stk,offset);
9917c2fbfb3SApril Chin 		comsubst((Mac_t*)shp->mac_context,tp,2);
9927c2fbfb3SApril Chin 		free(dp->dolval[0]);
9937c2fbfb3SApril Chin 		return(1);
9947c2fbfb3SApril Chin 	}
9957c2fbfb3SApril Chin 	return(0);
9967c2fbfb3SApril Chin }
9977c2fbfb3SApril Chin 
namecount(Mac_t * mp,const char * prefix)998da2e3ebdSchin static int namecount(Mac_t *mp,const char *prefix)
999da2e3ebdSchin {
1000da2e3ebdSchin 	int count = 0;
10017c2fbfb3SApril Chin 	mp->nvwalk = nv_diropen((Namval_t*)0,prefix);
1002da2e3ebdSchin 	while(nv_dirnext(mp->nvwalk))
1003da2e3ebdSchin 		count++;
1004da2e3ebdSchin 	nv_dirclose(mp->nvwalk);
1005da2e3ebdSchin 	return(count);
1006da2e3ebdSchin }
1007da2e3ebdSchin 
nextname(Mac_t * mp,const char * prefix,int len)1008da2e3ebdSchin static char *nextname(Mac_t *mp,const char *prefix, int len)
1009da2e3ebdSchin {
1010da2e3ebdSchin 	char *cp;
1011da2e3ebdSchin 	if(len==0)
1012da2e3ebdSchin 	{
10137c2fbfb3SApril Chin 		mp->nvwalk = nv_diropen((Namval_t*)0,prefix);
1014da2e3ebdSchin 		return((char*)mp->nvwalk);
1015da2e3ebdSchin 	}
1016da2e3ebdSchin 	if(!(cp=nv_dirnext(mp->nvwalk)))
1017da2e3ebdSchin 		nv_dirclose(mp->nvwalk);
1018da2e3ebdSchin 	return(cp);
1019da2e3ebdSchin }
1020da2e3ebdSchin 
1021da2e3ebdSchin /*
1022da2e3ebdSchin  * This routine handles $param,  ${parm}, and ${param op word}
1023da2e3ebdSchin  * The input stream is assumed to be a string
1024da2e3ebdSchin  */
varsub(Mac_t * mp)1025da2e3ebdSchin static int varsub(Mac_t *mp)
1026da2e3ebdSchin {
1027da2e3ebdSchin 	register int	c;
1028da2e3ebdSchin 	register int	type=0; /* M_xxx */
1029da2e3ebdSchin 	register char	*v,*argp=0;
1030da2e3ebdSchin 	register Namval_t	*np = NIL(Namval_t*);
1031da2e3ebdSchin 	register int 	dolg=0, mode=0;
10327c2fbfb3SApril Chin 	Lex_t		*lp = (Lex_t*)mp->shp->lex_context;
1033da2e3ebdSchin 	Namarr_t	*ap=0;
1034da2e3ebdSchin 	int		dolmax=0, vsize= -1, offset= -1, nulflg, replen=0, bysub=0;
10357c2fbfb3SApril Chin 	char		idbuff[3], *id = idbuff, *pattern=0, *repstr, *arrmax=0;
103634f9b3eeSRoland Mainz 	int		var=1,addsub=0,oldpat=mp->pattern,idnum=0,flag=0,d;
10377c2fbfb3SApril Chin 	Stk_t		*stkp = mp->shp->stk;
1038da2e3ebdSchin retry1:
1039da2e3ebdSchin 	mp->zeros = 0;
1040da2e3ebdSchin 	idbuff[0] = 0;
1041da2e3ebdSchin 	idbuff[1] = 0;
1042da2e3ebdSchin 	c = fcget();
104334f9b3eeSRoland Mainz 	switch(isascii(c)?sh_lexstates[ST_DOL][c]:S_ALP)
1044da2e3ebdSchin 	{
1045da2e3ebdSchin 	    case S_RBRA:
1046da2e3ebdSchin 		if(type<M_SIZE)
1047da2e3ebdSchin 			goto nosub;
1048da2e3ebdSchin 		/* This code handles ${#} */
1049da2e3ebdSchin 		c = mode;
1050da2e3ebdSchin 		mode = type = 0;
1051da2e3ebdSchin 		/* FALL THRU */
1052da2e3ebdSchin 	    case S_SPC1:
1053da2e3ebdSchin 		if(type==M_BRACE)
1054da2e3ebdSchin 		{
1055da2e3ebdSchin 			if(isaletter(mode=fcpeek(0)) || mode=='.')
1056da2e3ebdSchin 			{
1057da2e3ebdSchin 				if(c=='#')
1058da2e3ebdSchin 					type = M_SIZE;
1059da2e3ebdSchin #ifdef SHOPT_TYPEDEF
1060da2e3ebdSchin 				else if(c=='@')
1061da2e3ebdSchin 				{
1062da2e3ebdSchin 					type = M_TYPE;
1063da2e3ebdSchin 					goto retry1;
1064da2e3ebdSchin 				}
1065da2e3ebdSchin #endif /* SHOPT_TYPEDEF */
1066da2e3ebdSchin 				else
1067da2e3ebdSchin 					type = M_VNAME;
1068da2e3ebdSchin 				mode = c;
1069da2e3ebdSchin 				goto retry1;
1070da2e3ebdSchin 			}
1071da2e3ebdSchin 			else if(c=='#' && (isadigit(mode)||fcpeek(1)==RBRACE))
1072da2e3ebdSchin 			{
1073da2e3ebdSchin 				type = M_SIZE;
1074da2e3ebdSchin 				mode = c;
1075da2e3ebdSchin 				goto retry1;
1076da2e3ebdSchin 			}
1077da2e3ebdSchin 		}
1078da2e3ebdSchin 		/* FALL THRU */
1079da2e3ebdSchin 	    case S_SPC2:
108034f9b3eeSRoland Mainz 		var = 0;
1081da2e3ebdSchin 		*id = c;
10827c2fbfb3SApril Chin 		v = special(mp->shp,c);
1083da2e3ebdSchin 		if(isastchar(c))
1084da2e3ebdSchin 		{
1085da2e3ebdSchin 			mode = c;
1086da2e3ebdSchin #if  SHOPT_FILESCAN
10877c2fbfb3SApril Chin 			if(mp->shp->cur_line)
1088da2e3ebdSchin 			{
1089da2e3ebdSchin 				v = getdolarg(&sh,1,(int*)0);
1090da2e3ebdSchin 				dolmax = MAX_ARGN;
1091da2e3ebdSchin 			}
1092da2e3ebdSchin 			else
1093da2e3ebdSchin #endif  /* SHOPT_FILESCAN */
10947c2fbfb3SApril Chin 			dolmax = mp->shp->st.dolc+1;
1095da2e3ebdSchin 			dolg = (v!=0);
1096da2e3ebdSchin 		}
1097da2e3ebdSchin 		break;
1098da2e3ebdSchin 	    case S_LBRA:
1099da2e3ebdSchin 		if(type)
1100da2e3ebdSchin 			goto nosub;
1101da2e3ebdSchin 		type = M_BRACE;
1102da2e3ebdSchin 		goto retry1;
1103da2e3ebdSchin 	    case S_PAR:
1104da2e3ebdSchin 		if(type)
1105da2e3ebdSchin 			goto nosub;
11067c2fbfb3SApril Chin 		comsubst(mp,(Shnode_t*)0,1);
1107da2e3ebdSchin 		return(1);
1108da2e3ebdSchin 	    case S_DIG:
110934f9b3eeSRoland Mainz 		var = 0;
1110da2e3ebdSchin 		c -= '0';
11117c2fbfb3SApril Chin 		mp->shp->argaddr = 0;
1112da2e3ebdSchin 		if(type)
1113da2e3ebdSchin 		{
1114da2e3ebdSchin 			register int d;
1115da2e3ebdSchin 			while((d=fcget()),isadigit(d))
1116da2e3ebdSchin 				c = 10*c + (d-'0');
1117da2e3ebdSchin 			fcseek(-1);
1118da2e3ebdSchin 		}
1119da2e3ebdSchin 		idnum = c;
1120da2e3ebdSchin 		if(c==0)
11217c2fbfb3SApril Chin 			v = special(mp->shp,c);
1122da2e3ebdSchin #if  SHOPT_FILESCAN
11237c2fbfb3SApril Chin 		else if(mp->shp->cur_line)
1124da2e3ebdSchin 		{
11257c2fbfb3SApril Chin 			mp->shp->used_pos = 1;
1126da2e3ebdSchin 			v = getdolarg(&sh,c,&vsize);
1127da2e3ebdSchin 		}
1128da2e3ebdSchin #endif  /* SHOPT_FILESCAN */
11297c2fbfb3SApril Chin 		else if(c <= mp->shp->st.dolc)
1130da2e3ebdSchin 		{
11317c2fbfb3SApril Chin 			mp->shp->used_pos = 1;
11327c2fbfb3SApril Chin 			v = mp->shp->st.dolv[c];
1133da2e3ebdSchin 		}
1134da2e3ebdSchin 		else
1135da2e3ebdSchin 			v = 0;
1136da2e3ebdSchin 		break;
1137da2e3ebdSchin 	    case S_ALP:
1138da2e3ebdSchin 		if(c=='.' && type==0)
1139da2e3ebdSchin 			goto nosub;
11407c2fbfb3SApril Chin 		offset = stktell(stkp);
1141da2e3ebdSchin 		do
1142da2e3ebdSchin 		{
1143da2e3ebdSchin 			np = 0;
1144da2e3ebdSchin 			do
11457c2fbfb3SApril Chin 				sfputc(stkp,c);
114634f9b3eeSRoland Mainz 			while(((c=fcget()),(!isascii(c)||isaname(c)))||type && c=='.');
11477c2fbfb3SApril Chin 			while(c==LBRACT && (type||mp->arrayok))
1148da2e3ebdSchin 			{
11497c2fbfb3SApril Chin 				mp->shp->argaddr=0;
1150da2e3ebdSchin 				if((c=fcget(),isastchar(c)) && fcpeek(0)==RBRACT)
1151da2e3ebdSchin 				{
1152da2e3ebdSchin 					if(type==M_VNAME)
1153da2e3ebdSchin 						type = M_SUBNAME;
1154da2e3ebdSchin 					idbuff[0] = mode = c;
1155da2e3ebdSchin 					fcget();
1156da2e3ebdSchin 					c = fcget();
1157da2e3ebdSchin 					if(c=='.' || c==LBRACT)
1158da2e3ebdSchin 					{
11597c2fbfb3SApril Chin 						sfputc(stkp,LBRACT);
11607c2fbfb3SApril Chin 						sfputc(stkp,mode);
11617c2fbfb3SApril Chin 						sfputc(stkp,RBRACT);
1162da2e3ebdSchin 					}
1163da2e3ebdSchin 					else
1164da2e3ebdSchin 						flag = NV_ARRAY;
1165da2e3ebdSchin 					break;
1166da2e3ebdSchin 				}
1167da2e3ebdSchin 				else
1168da2e3ebdSchin 				{
1169da2e3ebdSchin 					fcseek(-1);
11707c2fbfb3SApril Chin 					c = stktell(stkp);
11717c2fbfb3SApril Chin 					sfputc(stkp,LBRACT);
11727c2fbfb3SApril Chin 					v = stkptr(stkp,subcopy(mp,1));
11737c2fbfb3SApril Chin 					if(type && mp->dotdot)
11747c2fbfb3SApril Chin 					{
11757c2fbfb3SApril Chin 						mode = '@';
11767c2fbfb3SApril Chin 						v[-1] = 0;
1177da2e3ebdSchin 						if(type==M_VNAME)
1178da2e3ebdSchin 							type = M_SUBNAME;
11797c2fbfb3SApril Chin 						else if(type==M_SIZE)
11807c2fbfb3SApril Chin 							goto nosub;
11817c2fbfb3SApril Chin 					}
11827c2fbfb3SApril Chin 					else
11837c2fbfb3SApril Chin 						sfputc(stkp,RBRACT);
1184da2e3ebdSchin 					c = fcget();
11857c2fbfb3SApril Chin 					if(c==0 && type==M_VNAME)
11867c2fbfb3SApril Chin 						type = M_SUBNAME;
1187da2e3ebdSchin 				}
1188da2e3ebdSchin 			}
1189da2e3ebdSchin 		}
1190da2e3ebdSchin 		while(type && c=='.');
1191da2e3ebdSchin 		if(c==RBRACE && type &&  fcpeek(-2)=='.')
1192da2e3ebdSchin 		{
11937c2fbfb3SApril Chin 			/* ${x.} or ${x..} */
11947c2fbfb3SApril Chin 			if(fcpeek(-3) == '.')
11957c2fbfb3SApril Chin 			{
11967c2fbfb3SApril Chin 				stkseek(stkp,stktell(stkp)-2);
11977c2fbfb3SApril Chin 				nv_local = 1;
11987c2fbfb3SApril Chin 			}
11997c2fbfb3SApril Chin 			else
12007c2fbfb3SApril Chin 			{
12017c2fbfb3SApril Chin 				stkseek(stkp,stktell(stkp)-1);
1202da2e3ebdSchin 				type = M_TREE;
1203da2e3ebdSchin 			}
12047c2fbfb3SApril Chin 		}
12057c2fbfb3SApril Chin 		sfputc(stkp,0);
12067c2fbfb3SApril Chin 		id=stkptr(stkp,offset);
1207da2e3ebdSchin 		if(isastchar(c) && type)
1208da2e3ebdSchin 		{
1209da2e3ebdSchin 			if(type==M_VNAME || type==M_SIZE)
1210da2e3ebdSchin 			{
1211da2e3ebdSchin 				idbuff[0] = mode = c;
1212da2e3ebdSchin 				if((d=fcpeek(0))==c)
1213da2e3ebdSchin 					idbuff[1] = fcget();
1214da2e3ebdSchin 				if(type==M_VNAME)
1215da2e3ebdSchin 					type = M_NAMESCAN;
1216da2e3ebdSchin 				else
1217da2e3ebdSchin 					type = M_NAMECOUNT;
1218da2e3ebdSchin 				break;
1219da2e3ebdSchin 			}
1220da2e3ebdSchin 			goto nosub;
1221da2e3ebdSchin 		}
1222da2e3ebdSchin 		flag |= NV_NOASSIGN|NV_VARNAME|NV_NOADD;
1223da2e3ebdSchin 		if(c=='=' || c=='?' || (c==':' && ((d=fcpeek(0))=='=' || d=='?')))
1224da2e3ebdSchin 			flag &= ~NV_NOADD;
1225da2e3ebdSchin #if  SHOPT_FILESCAN
12267c2fbfb3SApril Chin 		if(mp->shp->cur_line && *id=='R' && strcmp(id,"REPLY")==0)
1227da2e3ebdSchin 		{
12287c2fbfb3SApril Chin 			mp->shp->argaddr=0;
1229da2e3ebdSchin 			np = REPLYNOD;
1230da2e3ebdSchin 		}
1231da2e3ebdSchin 		else
1232da2e3ebdSchin #endif  /* SHOPT_FILESCAN */
123334f9b3eeSRoland Mainz 		{
12347c2fbfb3SApril Chin 			if(mp->shp->argaddr)
1235da2e3ebdSchin 				flag &= ~NV_NOADD;
12367c2fbfb3SApril Chin 			np = nv_open(id,mp->shp->var_tree,flag|NV_NOFAIL);
123734f9b3eeSRoland Mainz 		}
123834f9b3eeSRoland Mainz 		if(isastchar(mode))
123934f9b3eeSRoland Mainz 			var = 0;
124034f9b3eeSRoland Mainz 		if((!np || nv_isnull(np)) && type==M_BRACE && c==RBRACE && !(flag&NV_ARRAY) && strchr(id,'.'))
12417c2fbfb3SApril Chin 		{
12427c2fbfb3SApril Chin 			if(sh_macfun(mp->shp,id,offset))
12437c2fbfb3SApril Chin 			{
12447c2fbfb3SApril Chin 				fcget();
12457c2fbfb3SApril Chin 				return(1);
12467c2fbfb3SApril Chin 			}
12477c2fbfb3SApril Chin 		}
124834f9b3eeSRoland Mainz 		if(np && (flag&NV_NOADD) && nv_isnull(np))
124934f9b3eeSRoland Mainz 		{
125034f9b3eeSRoland Mainz 			if(nv_isattr(np,NV_NOFREE))
125134f9b3eeSRoland Mainz 				nv_offattr(np,NV_NOFREE);
125234f9b3eeSRoland Mainz 			else
125334f9b3eeSRoland Mainz 				np = 0;
125434f9b3eeSRoland Mainz 		}
1255da2e3ebdSchin 		ap = np?nv_arrayptr(np):0;
1256da2e3ebdSchin 		if(type)
1257da2e3ebdSchin 		{
12587c2fbfb3SApril Chin 			if(mp->dotdot)
12597c2fbfb3SApril Chin 			{
12607c2fbfb3SApril Chin 				if(ap)
12617c2fbfb3SApril Chin 				{
12627c2fbfb3SApril Chin 					nv_putsub(np,v,ARRAY_SCAN);
12637c2fbfb3SApril Chin 					v = stkptr(stkp,mp->dotdot);
12647c2fbfb3SApril Chin 					dolmax =1;
12657c2fbfb3SApril Chin 					if(array_assoc(ap))
12667c2fbfb3SApril Chin 						arrmax = strdup(v);
12677c2fbfb3SApril Chin 					else if((dolmax = (int)sh_arith(v))<0)
12687c2fbfb3SApril Chin 						dolmax += array_maxindex(np);
12697c2fbfb3SApril Chin 					if(type==M_SUBNAME)
12707c2fbfb3SApril Chin 						bysub = 1;
12717c2fbfb3SApril Chin 				}
12727c2fbfb3SApril Chin 				else
12737c2fbfb3SApril Chin 				{
12747c2fbfb3SApril Chin 					if((int)sh_arith(v))
12757c2fbfb3SApril Chin 						np = 0;
12767c2fbfb3SApril Chin 				}
12777c2fbfb3SApril Chin 			}
12787c2fbfb3SApril Chin 			else if(ap && (isastchar(mode)||type==M_TREE)  && !(ap->nelem&ARRAY_SCAN) && type!=M_SIZE)
1279da2e3ebdSchin 				nv_putsub(np,NIL(char*),ARRAY_SCAN);
1280da2e3ebdSchin 			if(!isbracechar(c))
1281da2e3ebdSchin 				goto nosub;
1282da2e3ebdSchin 			else
1283da2e3ebdSchin 				fcseek(-1);
1284da2e3ebdSchin 		}
1285da2e3ebdSchin 		else
1286da2e3ebdSchin 			fcseek(-1);
12877c2fbfb3SApril Chin 		if(type<=1 && np && nv_isvtree(np) && mp->pattern==1 && !mp->split)
1288da2e3ebdSchin 		{
12897c2fbfb3SApril Chin 			int peek=1,cc=fcget();
12907c2fbfb3SApril Chin 			if(type && cc=='}')
12917c2fbfb3SApril Chin 			{
12927c2fbfb3SApril Chin 				cc = fcget();
12937c2fbfb3SApril Chin 				peek = 2;
12947c2fbfb3SApril Chin 			}
12957c2fbfb3SApril Chin 			if(mp->quote && cc=='"')
12967c2fbfb3SApril Chin 			{
12977c2fbfb3SApril Chin 				cc = fcget();
12987c2fbfb3SApril Chin 				peek++;
12997c2fbfb3SApril Chin 			}
13007c2fbfb3SApril Chin 			fcseek(-peek);
13017c2fbfb3SApril Chin 			if(cc==0)
13027c2fbfb3SApril Chin 				mp->assign = 1;
13037c2fbfb3SApril Chin 		}
13047c2fbfb3SApril Chin 		if((type==M_VNAME||type==M_SUBNAME)  && mp->shp->argaddr && strcmp(nv_name(np),id))
13057c2fbfb3SApril Chin 			mp->shp->argaddr = 0;
13067c2fbfb3SApril Chin 		c = (type>M_BRACE && isastchar(mode));
13077c2fbfb3SApril Chin 		if(np && (type==M_TREE || !c || !ap))
13087c2fbfb3SApril Chin 		{
13097c2fbfb3SApril Chin 			char *savptr;
13107c2fbfb3SApril Chin 			c = *((unsigned char*)stkptr(stkp,offset-1));
13117c2fbfb3SApril Chin 			savptr = stkfreeze(stkp,0);
13127c2fbfb3SApril Chin 			if(type==M_VNAME || (type==M_SUBNAME && ap))
1313da2e3ebdSchin 			{
1314da2e3ebdSchin 				type = M_BRACE;
1315da2e3ebdSchin 				v = nv_name(np);
13167c2fbfb3SApril Chin 				if(ap && !mp->dotdot && !(ap->nelem&ARRAY_UNDEF))
13177c2fbfb3SApril Chin 					addsub = 1;
1318da2e3ebdSchin 			}
1319da2e3ebdSchin #ifdef SHOPT_TYPEDEF
1320da2e3ebdSchin 			else if(type==M_TYPE)
1321da2e3ebdSchin 			{
1322da2e3ebdSchin 				Namval_t *nq = nv_type(np);
1323da2e3ebdSchin 				type = M_BRACE;
1324da2e3ebdSchin 				if(nq)
13257c2fbfb3SApril Chin 					nv_typename(nq,mp->shp->strbuf);
1326da2e3ebdSchin 				else
13277c2fbfb3SApril Chin 					nv_attribute(np,mp->shp->strbuf,"typeset",1);
13287c2fbfb3SApril Chin 				v = sfstruse(mp->shp->strbuf);
1329da2e3ebdSchin 			}
1330da2e3ebdSchin #endif /* SHOPT_TYPEDEF */
1331da2e3ebdSchin #if  SHOPT_FILESCAN
13327c2fbfb3SApril Chin 			else if(mp->shp->cur_line && np==REPLYNOD)
13337c2fbfb3SApril Chin 				v = mp->shp->cur_line;
1334da2e3ebdSchin #endif  /* SHOPT_FILESCAN */
1335da2e3ebdSchin 			else if(type==M_TREE)
1336da2e3ebdSchin 				v = nv_getvtree(np,(Namfun_t*)0);
1337da2e3ebdSchin 			else
1338da2e3ebdSchin 			{
133934f9b3eeSRoland Mainz 				if(type && fcpeek(0)=='+')
134034f9b3eeSRoland Mainz 				{
134134f9b3eeSRoland Mainz 					if(ap)
134234f9b3eeSRoland Mainz 						v = nv_arrayisset(np,ap)?(char*)"x":0;
134334f9b3eeSRoland Mainz 					else
134434f9b3eeSRoland Mainz 						v = nv_isnull(np)?0:(char*)"x";
134534f9b3eeSRoland Mainz 				}
134634f9b3eeSRoland Mainz 				else
1347da2e3ebdSchin 					v = nv_getval(np);
1348da2e3ebdSchin 				/* special case --- ignore leading zeros */
13497c2fbfb3SApril Chin 				if( (mp->arith||mp->let) && (np->nvfun || nv_isattr(np,(NV_LJUST|NV_RJUST|NV_ZFILL))) && (offset==0 || !isalnum(c)))
1350da2e3ebdSchin 					mp->zeros = 1;
1351da2e3ebdSchin 			}
13527c2fbfb3SApril Chin 			if(savptr==stakptr(0))
13537c2fbfb3SApril Chin 				stkseek(stkp,offset);
13547c2fbfb3SApril Chin 			else
13557c2fbfb3SApril Chin 				stkset(stkp,savptr,offset);
1356da2e3ebdSchin 		}
1357da2e3ebdSchin 		else
13587c2fbfb3SApril Chin 		{
1359da2e3ebdSchin 			v = 0;
13607c2fbfb3SApril Chin 			if(type==M_VNAME)
13617c2fbfb3SApril Chin 			{
13627c2fbfb3SApril Chin 				v = id;
13637c2fbfb3SApril Chin 				type = M_BRACE;
13647c2fbfb3SApril Chin 			}
13657c2fbfb3SApril Chin 			else if(type==M_TYPE)
13667c2fbfb3SApril Chin 				type = M_BRACE;
13677c2fbfb3SApril Chin 		}
13687c2fbfb3SApril Chin 		stkseek(stkp,offset);
1369da2e3ebdSchin 		if(ap)
1370da2e3ebdSchin 		{
1371da2e3ebdSchin #if SHOPT_OPTIMIZE
13727c2fbfb3SApril Chin 			if(mp->shp->argaddr)
1373da2e3ebdSchin 				nv_optimize(np);
1374da2e3ebdSchin #endif
1375da2e3ebdSchin 			if(isastchar(mode) && array_elem(ap)> !c)
1376da2e3ebdSchin 				dolg = -1;
1377da2e3ebdSchin 			else
1378da2e3ebdSchin 				dolg = 0;
1379da2e3ebdSchin 		}
1380da2e3ebdSchin 		break;
1381da2e3ebdSchin 	    case S_EOF:
1382da2e3ebdSchin 		fcseek(-1);
1383da2e3ebdSchin 	    default:
1384da2e3ebdSchin 		goto nosub;
1385da2e3ebdSchin 	}
1386da2e3ebdSchin 	c = fcget();
1387da2e3ebdSchin 	if(type>M_TREE)
1388da2e3ebdSchin 	{
1389da2e3ebdSchin 		if(c!=RBRACE)
1390da2e3ebdSchin 			mac_error(np);
1391da2e3ebdSchin 		if(type==M_NAMESCAN || type==M_NAMECOUNT)
1392da2e3ebdSchin 		{
139334f9b3eeSRoland Mainz 			mp->shp->last_root = mp->shp->var_tree;
13947c2fbfb3SApril Chin 			id = prefix(mp->shp,id);
13957c2fbfb3SApril Chin 			stkseek(stkp,offset);
1396da2e3ebdSchin 			if(type==M_NAMECOUNT)
1397da2e3ebdSchin 			{
1398da2e3ebdSchin 				c = namecount(mp,id);
1399da2e3ebdSchin 				v = ltos(c);
1400da2e3ebdSchin 			}
1401da2e3ebdSchin 			else
1402da2e3ebdSchin 			{
1403da2e3ebdSchin 				dolmax = strlen(id);
1404da2e3ebdSchin 				dolg = -1;
1405da2e3ebdSchin 				nextname(mp,id,0);
1406da2e3ebdSchin 				v = nextname(mp,id,dolmax);
1407da2e3ebdSchin 			}
1408da2e3ebdSchin 		}
1409da2e3ebdSchin 		else if(type==M_SUBNAME)
1410da2e3ebdSchin 		{
1411da2e3ebdSchin 			if(dolg<0)
1412da2e3ebdSchin 			{
1413da2e3ebdSchin 				v = nv_getsub(np);
1414da2e3ebdSchin 				bysub=1;
1415da2e3ebdSchin 			}
1416da2e3ebdSchin 			else if(v)
1417da2e3ebdSchin 			{
1418da2e3ebdSchin 				if(!ap || isastchar(mode))
1419da2e3ebdSchin 					v = "0";
1420da2e3ebdSchin 				else
1421da2e3ebdSchin 					v = nv_getsub(np);
1422da2e3ebdSchin 			}
1423da2e3ebdSchin 		}
1424da2e3ebdSchin 		else
1425da2e3ebdSchin 		{
1426da2e3ebdSchin 			if(!isastchar(mode))
1427da2e3ebdSchin 				c = charlen(v,vsize);
1428da2e3ebdSchin 			else if(dolg>0)
1429da2e3ebdSchin 			{
1430da2e3ebdSchin #if  SHOPT_FILESCAN
14317c2fbfb3SApril Chin 				if(mp->shp->cur_line)
1432da2e3ebdSchin 				{
1433da2e3ebdSchin 					getdolarg(&sh,MAX_ARGN,(int*)0);
14347c2fbfb3SApril Chin 					c = mp->shp->offsets[0];
1435da2e3ebdSchin 				}
1436da2e3ebdSchin 				else
1437da2e3ebdSchin #endif  /* SHOPT_FILESCAN */
14387c2fbfb3SApril Chin 				c = mp->shp->st.dolc;
1439da2e3ebdSchin 			}
1440da2e3ebdSchin 			else if(dolg<0)
1441da2e3ebdSchin 				c = array_elem(ap);
1442da2e3ebdSchin 			else
1443da2e3ebdSchin 				c = (v!=0);
1444da2e3ebdSchin 			dolg = dolmax = 0;
1445da2e3ebdSchin 			v = ltos(c);
1446da2e3ebdSchin 		}
1447da2e3ebdSchin 		c = RBRACE;
1448da2e3ebdSchin 	}
1449da2e3ebdSchin 	nulflg = 0;
1450da2e3ebdSchin 	if(type && c==':')
1451da2e3ebdSchin 	{
1452da2e3ebdSchin 		c = fcget();
1453da2e3ebdSchin 		if(sh_lexstates[ST_BRACE][c]==S_MOD1 && c!='*' && c!= ':')
1454da2e3ebdSchin 			nulflg=1;
1455da2e3ebdSchin 		else if(c!='%' && c!='#')
1456da2e3ebdSchin 		{
1457da2e3ebdSchin 			fcseek(-1);
1458da2e3ebdSchin 			c = ':';
1459da2e3ebdSchin 		}
1460da2e3ebdSchin 	}
1461da2e3ebdSchin 	if(type)
1462da2e3ebdSchin 	{
1463da2e3ebdSchin 		if(!isbracechar(c))
1464da2e3ebdSchin 		{
1465da2e3ebdSchin 			if(!nulflg)
1466da2e3ebdSchin 				mac_error(np);
1467da2e3ebdSchin 			fcseek(-1);
1468da2e3ebdSchin 			c = ':';
1469da2e3ebdSchin 		}
1470da2e3ebdSchin 		if(c!=RBRACE)
1471da2e3ebdSchin 		{
1472da2e3ebdSchin 			int newops = (c=='#' || c == '%' || c=='/');
14737c2fbfb3SApril Chin 			offset = stktell(stkp);
1474da2e3ebdSchin 			if(c=='/' ||c==':' || ((!v || (nulflg && *v==0)) ^ (c=='+'||c=='#'||c=='%')))
1475da2e3ebdSchin 			{
1476da2e3ebdSchin 				int newquote = mp->quote;
1477da2e3ebdSchin 				int split = mp->split;
1478da2e3ebdSchin 				int quoted = mp->quoted;
1479da2e3ebdSchin 				int arith = mp->arith;
1480da2e3ebdSchin 				int zeros = mp->zeros;
148134f9b3eeSRoland Mainz 				int assign = mp->assign;
1482da2e3ebdSchin 				if(newops)
1483da2e3ebdSchin 				{
1484da2e3ebdSchin 					type = fcget();
1485da2e3ebdSchin 					if(type=='%' || type=='#')
1486da2e3ebdSchin 					{
1487da2e3ebdSchin 						int d = fcget();
1488da2e3ebdSchin 						fcseek(-1);
1489da2e3ebdSchin 						if(d=='(')
1490da2e3ebdSchin 							type = 0;
1491da2e3ebdSchin 					}
1492da2e3ebdSchin 					fcseek(-1);
1493da2e3ebdSchin 					mp->pattern = 1+(c=='/');
1494da2e3ebdSchin 					mp->split = 0;
1495da2e3ebdSchin 					mp->quoted = 0;
149634f9b3eeSRoland Mainz 					mp->assign &= ~1;
1497da2e3ebdSchin 					mp->arith = mp->zeros = 0;
1498da2e3ebdSchin 					newquote = 0;
1499da2e3ebdSchin 				}
1500da2e3ebdSchin 				else if(c=='?' || c=='=')
1501da2e3ebdSchin 					mp->split = mp->pattern = 0;
1502da2e3ebdSchin 				copyto(mp,RBRACE,newquote);
1503da2e3ebdSchin 				if(!oldpat)
1504da2e3ebdSchin 					mp->patfound = 0;
1505da2e3ebdSchin 				mp->pattern = oldpat;
1506da2e3ebdSchin 				mp->split = split;
1507da2e3ebdSchin 				mp->quoted = quoted;
1508da2e3ebdSchin 				mp->arith = arith;
1509da2e3ebdSchin 				mp->zeros = zeros;
151034f9b3eeSRoland Mainz 				mp->assign = assign;
1511da2e3ebdSchin 				/* add null byte */
15127c2fbfb3SApril Chin 				sfputc(stkp,0);
15137c2fbfb3SApril Chin 				stkseek(stkp,stktell(stkp)-1);
1514da2e3ebdSchin 			}
1515da2e3ebdSchin 			else
1516da2e3ebdSchin 			{
15177c2fbfb3SApril Chin 				sh_lexskip(lp,RBRACE,0,(!newops&&mp->quote)?ST_QUOTE:ST_NESTED);
15187c2fbfb3SApril Chin 				stkseek(stkp,offset);
1519da2e3ebdSchin 			}
15207c2fbfb3SApril Chin 			argp=stkptr(stkp,offset);
1521da2e3ebdSchin 		}
1522da2e3ebdSchin 	}
1523da2e3ebdSchin 	else
1524da2e3ebdSchin 	{
1525da2e3ebdSchin 		fcseek(-1);
1526da2e3ebdSchin 		c=0;
1527da2e3ebdSchin 	}
1528da2e3ebdSchin 	if(c==':')  /* ${name:expr1[:expr2]} */
1529da2e3ebdSchin 	{
1530da2e3ebdSchin 		char *ptr;
1531da2e3ebdSchin 		type = (int)sh_strnum(argp,&ptr,1);
1532da2e3ebdSchin 		if(isastchar(mode))
1533da2e3ebdSchin 		{
1534da2e3ebdSchin 			if(id==idbuff)  /* ${@} or ${*} */
1535da2e3ebdSchin 			{
1536da2e3ebdSchin 				if(type<0 && (type+= dolmax)<0)
1537da2e3ebdSchin 					type = 0;
1538da2e3ebdSchin 				if(type==0)
15397c2fbfb3SApril Chin 					v = special(mp->shp,dolg=0);
1540da2e3ebdSchin #if  SHOPT_FILESCAN
15417c2fbfb3SApril Chin 				else if(mp->shp->cur_line)
1542da2e3ebdSchin 				{
1543da2e3ebdSchin 					v = getdolarg(&sh,dolg=type,&vsize);
1544da2e3ebdSchin 					if(!v)
1545da2e3ebdSchin 						dolmax = type;
1546da2e3ebdSchin 				}
1547da2e3ebdSchin #endif  /* SHOPT_FILESCAN */
1548da2e3ebdSchin 				else if(type < dolmax)
15497c2fbfb3SApril Chin 					v = mp->shp->st.dolv[dolg=type];
1550da2e3ebdSchin 				else
1551da2e3ebdSchin 					v =  0;
1552da2e3ebdSchin 			}
1553da2e3ebdSchin 			else if(ap)
1554da2e3ebdSchin 			{
1555da2e3ebdSchin 				if(type<0)
1556da2e3ebdSchin 				{
1557da2e3ebdSchin 					if(array_assoc(ap))
1558da2e3ebdSchin 						type = -type;
1559da2e3ebdSchin 					else
1560da2e3ebdSchin 						type += array_maxindex(np);
1561da2e3ebdSchin 				}
1562da2e3ebdSchin 				if(array_assoc(ap))
1563da2e3ebdSchin 				{
1564da2e3ebdSchin 					while(type-- >0 && (v=0,nv_nextsub(np)))
1565da2e3ebdSchin 						v = nv_getval(np);
1566da2e3ebdSchin 				}
1567da2e3ebdSchin 				else if(type > 0)
1568da2e3ebdSchin 				{
1569da2e3ebdSchin 					if(nv_putsub(np,NIL(char*),type|ARRAY_SCAN))
1570da2e3ebdSchin 						v = nv_getval(np);
1571da2e3ebdSchin 					else
1572da2e3ebdSchin 						v = 0;
1573da2e3ebdSchin 				}
1574da2e3ebdSchin 			}
1575da2e3ebdSchin 			else if(type>0)
1576da2e3ebdSchin 				v = 0;
1577da2e3ebdSchin 		}
1578da2e3ebdSchin 		else if(v)
1579da2e3ebdSchin 		{
1580da2e3ebdSchin 			vsize = charlen(v,vsize);
1581da2e3ebdSchin 			if(type<0 && (type += vsize)<0)
1582da2e3ebdSchin 				type = 0;
1583da2e3ebdSchin 			if(vsize < type)
1584da2e3ebdSchin 				v = 0;
1585da2e3ebdSchin #if SHOPT_MULTIBYTE
1586da2e3ebdSchin 			else if(mbwide())
1587da2e3ebdSchin 			{
1588da2e3ebdSchin 				mbinit();
1589da2e3ebdSchin 				while(type-->0)
1590da2e3ebdSchin 				{
1591da2e3ebdSchin 					if((c=mbsize(v))<1)
1592da2e3ebdSchin 						c = 1;
1593da2e3ebdSchin 					v += c;
1594da2e3ebdSchin 				}
1595da2e3ebdSchin 				c = ':';
1596da2e3ebdSchin 			}
1597da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
1598da2e3ebdSchin 			else
1599da2e3ebdSchin 				v += type;
1600da2e3ebdSchin 			vsize -= type;
1601da2e3ebdSchin 		}
1602da2e3ebdSchin 		if(*ptr==':')
1603da2e3ebdSchin 		{
1604da2e3ebdSchin 			if((type = (int)sh_strnum(ptr+1,&ptr,1)) <=0)
1605da2e3ebdSchin 				v = 0;
1606da2e3ebdSchin 			else if(isastchar(mode))
1607da2e3ebdSchin 			{
1608da2e3ebdSchin 				if(dolg>=0)
1609da2e3ebdSchin 				{
1610da2e3ebdSchin 					if(dolg+type < dolmax)
1611da2e3ebdSchin 						dolmax = dolg+type;
1612da2e3ebdSchin 				}
1613da2e3ebdSchin 				else
1614da2e3ebdSchin 					dolmax = type;
1615da2e3ebdSchin 			}
1616da2e3ebdSchin 			else if(type < vsize)
1617da2e3ebdSchin 			{
1618da2e3ebdSchin #if SHOPT_MULTIBYTE
1619da2e3ebdSchin 				if(mbwide())
1620da2e3ebdSchin 				{
1621da2e3ebdSchin 					char *vp = v;
1622da2e3ebdSchin 					mbinit();
1623da2e3ebdSchin 					while(type-->0)
1624da2e3ebdSchin 					{
1625da2e3ebdSchin 						if((c=mbsize(vp))<1)
1626da2e3ebdSchin 							c = 1;
1627da2e3ebdSchin 						vp += c;
1628da2e3ebdSchin 					}
1629da2e3ebdSchin 					type = vp-v;
1630da2e3ebdSchin 					c = ':';
1631da2e3ebdSchin 				}
1632da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
1633da2e3ebdSchin 				vsize = type;
1634da2e3ebdSchin 			}
1635da2e3ebdSchin 		}
1636da2e3ebdSchin 		if(*ptr)
1637da2e3ebdSchin 			mac_error(np);
16387c2fbfb3SApril Chin 		stkseek(stkp,offset);
1639da2e3ebdSchin 		argp = 0;
1640da2e3ebdSchin 	}
1641da2e3ebdSchin 	/* check for substring operations */
1642da2e3ebdSchin 	else if(c == '#' || c == '%' || c=='/')
1643da2e3ebdSchin 	{
1644da2e3ebdSchin 		if(c=='/')
1645da2e3ebdSchin 		{
1646da2e3ebdSchin 			if(type=='/' || type=='#' || type=='%')
1647da2e3ebdSchin 			{
1648da2e3ebdSchin 				c = type;
1649da2e3ebdSchin 				type = '/';
1650da2e3ebdSchin 				argp++;
1651da2e3ebdSchin 			}
1652da2e3ebdSchin 			else
1653da2e3ebdSchin 				type = 0;
1654da2e3ebdSchin 		}
1655da2e3ebdSchin 		else
1656da2e3ebdSchin 		{
1657da2e3ebdSchin 			if(type==c) /* ## or %% */
1658da2e3ebdSchin 				argp++;
1659da2e3ebdSchin 			else
1660da2e3ebdSchin 				type = 0;
1661da2e3ebdSchin 		}
1662da2e3ebdSchin 		pattern = strdup(argp);
1663da2e3ebdSchin 		if((type=='/' || c=='/') && (repstr = mac_getstring(pattern)))
1664da2e3ebdSchin 			replen = strlen(repstr);
1665da2e3ebdSchin 		if(v || c=='/' && offset>=0)
16667c2fbfb3SApril Chin 			stkseek(stkp,offset);
1667da2e3ebdSchin 	}
1668da2e3ebdSchin 	/* check for quoted @ */
1669da2e3ebdSchin 	if(mode=='@' && mp->quote && !v && c!='-')
1670da2e3ebdSchin 		mp->quoted-=2;
1671da2e3ebdSchin retry2:
1672da2e3ebdSchin 	if(v && (!nulflg || *v ) && c!='+')
1673da2e3ebdSchin 	{
1674da2e3ebdSchin 		register int d = (mode=='@'?' ':mp->ifs);
16757c2fbfb3SApril Chin 		int match[2*(MATCH_MAX+1)], nmatch, nmatch_prev, vsize_last;
1676da2e3ebdSchin 		char *vlast;
1677da2e3ebdSchin 		while(1)
1678da2e3ebdSchin 		{
1679da2e3ebdSchin 			if(!v)
1680da2e3ebdSchin 				v= "";
1681da2e3ebdSchin 			if(c=='/' || c=='#' || c== '%')
1682da2e3ebdSchin 			{
16837c2fbfb3SApril Chin 				flag = (type || c=='/')?(STR_GROUP|STR_MAXIMAL):STR_GROUP;
1684da2e3ebdSchin 				if(c!='/')
1685da2e3ebdSchin 					flag |= STR_LEFT;
16867c2fbfb3SApril Chin 				nmatch = 0;
1687da2e3ebdSchin 				while(1)
1688da2e3ebdSchin 				{
1689da2e3ebdSchin 					vsize = strlen(v);
16907c2fbfb3SApril Chin 					nmatch_prev = nmatch;
1691da2e3ebdSchin 					if(c=='%')
1692da2e3ebdSchin 						nmatch=substring(v,pattern,match,flag&STR_MAXIMAL);
1693da2e3ebdSchin 					else
1694da2e3ebdSchin 						nmatch=strgrpmatch(v,pattern,match,elementsof(match)/2,flag);
1695da2e3ebdSchin 					if(replen>0)
1696da2e3ebdSchin 						sh_setmatch(v,vsize,nmatch,match);
1697da2e3ebdSchin 					if(nmatch)
1698da2e3ebdSchin 					{
1699da2e3ebdSchin 						vlast = v;
1700da2e3ebdSchin 						vsize_last = vsize;
1701da2e3ebdSchin 						vsize = match[0];
1702da2e3ebdSchin 					}
1703da2e3ebdSchin 					else if(c=='#')
1704da2e3ebdSchin 						vsize = 0;
1705da2e3ebdSchin 					if(vsize)
1706da2e3ebdSchin 						mac_copy(mp,v,vsize);
17077c2fbfb3SApril Chin 					if(nmatch && replen>0 && (match[1] || !nmatch_prev))
1708da2e3ebdSchin 						mac_substitute(mp,repstr,v,match,nmatch);
1709da2e3ebdSchin 					if(nmatch==0)
1710da2e3ebdSchin 						v += vsize;
1711da2e3ebdSchin 					else
1712da2e3ebdSchin 						v += match[1];
1713da2e3ebdSchin 					if(*v &&  c=='/' && type)
1714da2e3ebdSchin 					{
1715da2e3ebdSchin 						/* avoid infinite loop */
1716da2e3ebdSchin 						if(nmatch && match[1]==0)
17177c2fbfb3SApril Chin 						{
17187c2fbfb3SApril Chin 							nmatch = 0;
17197c2fbfb3SApril Chin 							mac_copy(mp,v,1);
1720da2e3ebdSchin 							v++;
17217c2fbfb3SApril Chin 						}
1722da2e3ebdSchin 						continue;
1723da2e3ebdSchin 					}
1724da2e3ebdSchin 					vsize = -1;
1725da2e3ebdSchin 					break;
1726da2e3ebdSchin 				}
1727da2e3ebdSchin 				if(replen==0)
1728da2e3ebdSchin 					sh_setmatch(vlast,vsize_last,nmatch,match);
1729da2e3ebdSchin 			}
1730da2e3ebdSchin 			if(vsize)
1731da2e3ebdSchin 				mac_copy(mp,v,vsize>0?vsize:strlen(v));
17327c2fbfb3SApril Chin 			if(addsub)
17337c2fbfb3SApril Chin 			{
17347c2fbfb3SApril Chin 				sfprintf(mp->shp->strbuf,"[%s]",nv_getsub(np));
17357c2fbfb3SApril Chin 				v = sfstruse(mp->shp->strbuf);
17367c2fbfb3SApril Chin 				mac_copy(mp, v, strlen(v));
17377c2fbfb3SApril Chin 			}
1738da2e3ebdSchin 			if(dolg==0 && dolmax==0)
1739da2e3ebdSchin 				 break;
17407c2fbfb3SApril Chin 			if(mp->dotdot)
17417c2fbfb3SApril Chin 			{
17427c2fbfb3SApril Chin 				if(nv_nextsub(np) == 0)
17437c2fbfb3SApril Chin 					break;
17447c2fbfb3SApril Chin 				if(bysub)
17457c2fbfb3SApril Chin 					v = nv_getsub(np);
17467c2fbfb3SApril Chin 				else
17477c2fbfb3SApril Chin 					v = nv_getval(np);
17487c2fbfb3SApril Chin 				if(array_assoc(ap))
17497c2fbfb3SApril Chin 				{
17507c2fbfb3SApril Chin 					if(strcmp(bysub?v:nv_getsub(np),arrmax)>0)
17517c2fbfb3SApril Chin 						break;
17527c2fbfb3SApril Chin 				}
17537c2fbfb3SApril Chin 				else
17547c2fbfb3SApril Chin 				{
17557c2fbfb3SApril Chin 					if(nv_aindex(np) > dolmax)
17567c2fbfb3SApril Chin 						break;
17577c2fbfb3SApril Chin 				}
17587c2fbfb3SApril Chin 			}
17597c2fbfb3SApril Chin 			else if(dolg>=0)
1760da2e3ebdSchin 			{
1761da2e3ebdSchin 				if(++dolg >= dolmax)
1762da2e3ebdSchin 					break;
1763da2e3ebdSchin #if  SHOPT_FILESCAN
17647c2fbfb3SApril Chin 				if(mp->shp->cur_line)
1765da2e3ebdSchin 				{
1766da2e3ebdSchin 					if(dolmax==MAX_ARGN && isastchar(mode))
1767da2e3ebdSchin 						break;
1768da2e3ebdSchin 					if(!(v=getdolarg(&sh,dolg,&vsize)))
1769da2e3ebdSchin 					{
1770da2e3ebdSchin 						dolmax = dolg;
1771da2e3ebdSchin 						break;
1772da2e3ebdSchin 					}
1773da2e3ebdSchin 				}
1774da2e3ebdSchin 				else
1775da2e3ebdSchin #endif  /* SHOPT_FILESCAN */
17767c2fbfb3SApril Chin 				v = mp->shp->st.dolv[dolg];
1777da2e3ebdSchin 			}
1778da2e3ebdSchin 			else if(!np)
1779da2e3ebdSchin 			{
1780da2e3ebdSchin 				if(!(v = nextname(mp,id,dolmax)))
1781da2e3ebdSchin 					break;
1782da2e3ebdSchin 			}
1783da2e3ebdSchin 			else
1784da2e3ebdSchin 			{
1785da2e3ebdSchin 				if(dolmax &&  --dolmax <=0)
1786da2e3ebdSchin 				{
1787da2e3ebdSchin 					nv_putsub(np,NIL(char*),ARRAY_UNDEF);
1788da2e3ebdSchin 					break;
1789da2e3ebdSchin 				}
17907c2fbfb3SApril Chin 				if(ap)
17917c2fbfb3SApril Chin 					ap->nelem |= ARRAY_SCAN;
1792da2e3ebdSchin 				if(nv_nextsub(np) == 0)
1793da2e3ebdSchin 					break;
1794da2e3ebdSchin 				if(bysub)
1795da2e3ebdSchin 					v = nv_getsub(np);
1796da2e3ebdSchin 				else
1797da2e3ebdSchin 					v = nv_getval(np);
1798da2e3ebdSchin 			}
1799da2e3ebdSchin 			if(mp->split && (!mp->quote || mode=='@'))
1800da2e3ebdSchin 			{
1801da2e3ebdSchin 				if(!np)
1802da2e3ebdSchin 					mp->pattern = 0;
1803da2e3ebdSchin 				endfield(mp,mp->quoted);
1804da2e3ebdSchin 				mp->pattern = oldpat;
1805da2e3ebdSchin 			}
1806da2e3ebdSchin 			else if(d)
1807da2e3ebdSchin 			{
1808da2e3ebdSchin 				if(mp->sp)
1809da2e3ebdSchin 					sfputc(mp->sp,d);
1810da2e3ebdSchin 				else
18117c2fbfb3SApril Chin 					sfputc(stkp,d);
1812da2e3ebdSchin 			}
1813da2e3ebdSchin 		}
18147c2fbfb3SApril Chin 		if(arrmax)
18157c2fbfb3SApril Chin 			free((void*)arrmax);
1816da2e3ebdSchin 		if(pattern)
1817da2e3ebdSchin 			free((void*)pattern);
1818da2e3ebdSchin 	}
1819da2e3ebdSchin 	else if(argp)
1820da2e3ebdSchin 	{
1821da2e3ebdSchin 		if(c=='/' && replen>0 && pattern && strmatch("",pattern))
1822da2e3ebdSchin 			mac_substitute(mp,repstr,v,0,0);
1823da2e3ebdSchin 		if(c=='?')
1824da2e3ebdSchin 		{
1825da2e3ebdSchin 			if(np)
1826da2e3ebdSchin 				id = nv_name(np);
1827da2e3ebdSchin 			else if(idnum)
1828da2e3ebdSchin 				id = ltos(idnum);
1829da2e3ebdSchin 			if(*argp)
1830da2e3ebdSchin 			{
18317c2fbfb3SApril Chin 				sfputc(stkp,0);
1832da2e3ebdSchin 				errormsg(SH_DICT,ERROR_exit(1),"%s: %s",id,argp);
1833da2e3ebdSchin 			}
1834da2e3ebdSchin 			else if(v)
1835da2e3ebdSchin 				errormsg(SH_DICT,ERROR_exit(1),e_nullset,id);
1836da2e3ebdSchin 			else
1837da2e3ebdSchin 				errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
1838da2e3ebdSchin 		}
1839da2e3ebdSchin 		else if(c=='=')
1840da2e3ebdSchin 		{
1841da2e3ebdSchin 			if(np)
1842da2e3ebdSchin 			{
18437c2fbfb3SApril Chin 				if(mp->shp->subshell)
1844da2e3ebdSchin 					np = sh_assignok(np,1);
1845da2e3ebdSchin 				nv_putval(np,argp,0);
1846da2e3ebdSchin 				v = nv_getval(np);
1847da2e3ebdSchin 				nulflg = 0;
18487c2fbfb3SApril Chin 				stkseek(stkp,offset);
1849da2e3ebdSchin 				goto retry2;
1850da2e3ebdSchin 			}
1851da2e3ebdSchin 		else
1852da2e3ebdSchin 			mac_error(np);
1853da2e3ebdSchin 		}
1854da2e3ebdSchin 	}
1855*3e14f97fSRoger A. Faulkner 	else if(var && sh_isoption(SH_NOUNSET) && type<=M_TREE && (!np  || nv_isnull(np) || (nv_isarray(np) && !np->nvalue.cp)))
1856da2e3ebdSchin 	{
1857da2e3ebdSchin 		if(np)
1858da2e3ebdSchin 		{
1859da2e3ebdSchin 			if(nv_isarray(np))
1860da2e3ebdSchin 			{
18617c2fbfb3SApril Chin 				sfprintf(mp->shp->strbuf,"%s[%s]\0",nv_name(np),nv_getsub(np));
18627c2fbfb3SApril Chin 				id = sfstruse(mp->shp->strbuf);
1863da2e3ebdSchin 			}
1864da2e3ebdSchin 			else
1865da2e3ebdSchin 				id = nv_name(np);
1866da2e3ebdSchin 			nv_close(np);
1867da2e3ebdSchin 		}
1868da2e3ebdSchin 		errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
1869da2e3ebdSchin 	}
1870da2e3ebdSchin 	if(np)
1871da2e3ebdSchin 		nv_close(np);
1872da2e3ebdSchin 	return(1);
1873da2e3ebdSchin nosub:
18747c2fbfb3SApril Chin 	if(type==M_BRACE && sh_lexstates[ST_NORM][c]==S_BREAK)
18757c2fbfb3SApril Chin 	{
18767c2fbfb3SApril Chin 		fcseek(-1);
18777c2fbfb3SApril Chin 		comsubst(mp,(Shnode_t*)0,2);
18787c2fbfb3SApril Chin 		return(1);
18797c2fbfb3SApril Chin 	}
1880da2e3ebdSchin 	if(type)
1881da2e3ebdSchin 		mac_error(np);
1882da2e3ebdSchin 	fcseek(-1);
1883da2e3ebdSchin 	nv_close(np);
1884da2e3ebdSchin 	return(0);
1885da2e3ebdSchin }
1886da2e3ebdSchin 
1887da2e3ebdSchin /*
1888da2e3ebdSchin  * This routine handles command substitution
1889da2e3ebdSchin  * <type> is 0 for older `...` version
1890da2e3ebdSchin  */
comsubst(Mac_t * mp,register Shnode_t * t,int type)18917c2fbfb3SApril Chin static void comsubst(Mac_t *mp,register Shnode_t* t, int type)
1892da2e3ebdSchin {
1893da2e3ebdSchin 	Sfdouble_t		num;
1894da2e3ebdSchin 	register int		c;
1895da2e3ebdSchin 	register char		*str;
1896da2e3ebdSchin 	Sfio_t			*sp;
18977c2fbfb3SApril Chin 	Stk_t			*stkp = mp->shp->stk;
1898da2e3ebdSchin 	Fcin_t			save;
18997c2fbfb3SApril Chin 	struct slnod            *saveslp = mp->shp->st.staklist;
1900da2e3ebdSchin 	struct _mac_		savemac;
19017c2fbfb3SApril Chin 	int			savtop = stktell(stkp);
19027c2fbfb3SApril Chin 	char			lastc, *savptr = stkfreeze(stkp,0);
1903da2e3ebdSchin 	int			was_history = sh_isstate(SH_HISTORY);
1904da2e3ebdSchin 	int			was_verbose = sh_isstate(SH_VERBOSE);
19057c2fbfb3SApril Chin 	int			was_interactive = sh_isstate(SH_INTERACTIVE);
19067c2fbfb3SApril Chin 	int			newlines,bufsize,nextnewlines;
1907da2e3ebdSchin 	Namval_t		*np;
19087c2fbfb3SApril Chin 	mp->shp->argaddr = 0;
1909da2e3ebdSchin 	savemac = *mp;
19107c2fbfb3SApril Chin 	mp->shp->st.staklist=0;
1911da2e3ebdSchin 	if(type)
1912da2e3ebdSchin 	{
1913da2e3ebdSchin 		sp = 0;
1914da2e3ebdSchin 		fcseek(-1);
19157c2fbfb3SApril Chin 		if(!t)
19167c2fbfb3SApril Chin 			t = sh_dolparen((Lex_t*)mp->shp->lex_context);
1917da2e3ebdSchin 		if(t && t->tre.tretyp==TARITH)
1918da2e3ebdSchin 		{
1919da2e3ebdSchin 			fcsave(&save);
19207c2fbfb3SApril Chin 			if((t->ar.arexpr->argflag&ARG_RAW))
19217c2fbfb3SApril Chin 				num = arith_exec(t->ar.arcomp);
1922da2e3ebdSchin 			else
19237c2fbfb3SApril Chin 				num = sh_arith(sh_mactrim(mp->shp,t->ar.arexpr->argval,3));
19247c2fbfb3SApril Chin 		out_offset:
19257c2fbfb3SApril Chin 			stkset(stkp,savptr,savtop);
19267c2fbfb3SApril Chin 			*mp = savemac;
19277c2fbfb3SApril Chin 			if((Sflong_t)num!=num)
19287c2fbfb3SApril Chin 				sfprintf(mp->shp->strbuf,"%.*Lg",LDBL_DIG,num);
19297c2fbfb3SApril Chin 			else if(num)
19307c2fbfb3SApril Chin 				sfprintf(mp->shp->strbuf,"%lld",(Sflong_t)num);
19317c2fbfb3SApril Chin 			else
19327c2fbfb3SApril Chin 				sfprintf(mp->shp->strbuf,"%Lg",num);
19337c2fbfb3SApril Chin 			str = sfstruse(mp->shp->strbuf);
1934da2e3ebdSchin 			mac_copy(mp,str,strlen(str));
19357c2fbfb3SApril Chin 			mp->shp->st.staklist = saveslp;
1936da2e3ebdSchin 			fcrestore(&save);
1937da2e3ebdSchin 			return;
1938da2e3ebdSchin 		}
1939da2e3ebdSchin 	}
1940da2e3ebdSchin 	else
1941da2e3ebdSchin 	{
1942da2e3ebdSchin 		while(fcgetc(c)!='`' && c)
1943da2e3ebdSchin 		{
1944da2e3ebdSchin 			if(c==ESCAPE)
1945da2e3ebdSchin 			{
1946da2e3ebdSchin 				fcgetc(c);
1947da2e3ebdSchin 				if(!(isescchar(sh_lexstates[ST_QUOTE][c]) ||
19487c2fbfb3SApril Chin 				  (c=='"' && mp->quote)))
19497c2fbfb3SApril Chin 					sfputc(stkp,ESCAPE);
1950da2e3ebdSchin 			}
19517c2fbfb3SApril Chin 			sfputc(stkp,c);
1952da2e3ebdSchin 		}
19537c2fbfb3SApril Chin 		c = stktell(stkp);
19547c2fbfb3SApril Chin 		str=stkfreeze(stkp,1);
1955da2e3ebdSchin 		/* disable verbose and don't save in history file */
1956da2e3ebdSchin 		sh_offstate(SH_HISTORY);
1957da2e3ebdSchin 		sh_offstate(SH_VERBOSE);
1958da2e3ebdSchin 		if(mp->sp)
1959da2e3ebdSchin 			sfsync(mp->sp);	/* flush before executing command */
1960da2e3ebdSchin 		sp = sfnew(NIL(Sfio_t*),str,c,-1,SF_STRING|SF_READ);
19617c2fbfb3SApril Chin 		c = mp->shp->inlineno;
19627c2fbfb3SApril Chin 		mp->shp->inlineno = error_info.line+mp->shp->st.firstline;
1963da2e3ebdSchin 		t = (Shnode_t*)sh_parse(mp->shp, sp,SH_EOF|SH_NL);
19647c2fbfb3SApril Chin 		mp->shp->inlineno = c;
19657c2fbfb3SApril Chin 		type = 1;
1966da2e3ebdSchin 	}
1967da2e3ebdSchin #if KSHELL
1968da2e3ebdSchin 	if(t)
1969da2e3ebdSchin 	{
1970da2e3ebdSchin 		fcsave(&save);
1971da2e3ebdSchin 		sfclose(sp);
19727c2fbfb3SApril Chin 		if(t->tre.tretyp==0 && !t->com.comarg && !t->com.comset)
1973da2e3ebdSchin 		{
1974da2e3ebdSchin 			/* special case $(<file) and $(<#file) */
1975da2e3ebdSchin 			register int fd;
1976da2e3ebdSchin 			int r;
1977da2e3ebdSchin 			struct checkpt buff;
1978da2e3ebdSchin 			struct ionod *ip=0;
1979da2e3ebdSchin 			sh_pushcontext(&buff,SH_JMPIO);
1980da2e3ebdSchin 			if((ip=t->tre.treio) &&
1981da2e3ebdSchin 				((ip->iofile&IOLSEEK) || !(ip->iofile&IOUFD)) &&
1982da2e3ebdSchin 				(r=sigsetjmp(buff.buff,0))==0)
19837c2fbfb3SApril Chin 				fd = sh_redirect(mp->shp,ip,3);
1984da2e3ebdSchin 			else
1985da2e3ebdSchin 				fd = sh_chkopen(e_devnull);
1986da2e3ebdSchin 			sh_popcontext(&buff);
1987da2e3ebdSchin 			if(r==0 && ip && (ip->iofile&IOLSEEK))
1988da2e3ebdSchin 			{
19897c2fbfb3SApril Chin 				if(sp=mp->shp->sftable[fd])
1990da2e3ebdSchin 					num = sftell(sp);
1991da2e3ebdSchin 				else
1992da2e3ebdSchin 					num = lseek(fd, (off_t)0, SEEK_CUR);
1993da2e3ebdSchin 				goto out_offset;
1994da2e3ebdSchin 			}
1995da2e3ebdSchin 			sp = sfnew(NIL(Sfio_t*),(char*)malloc(IOBSIZE+1),IOBSIZE,fd,SF_READ|SF_MALLOC);
19967c2fbfb3SApril Chin 			type = 3;
1997da2e3ebdSchin 		}
1998da2e3ebdSchin 		else
19997c2fbfb3SApril Chin 			sp = sh_subshell(t,sh_isstate(SH_ERREXIT),type);
2000da2e3ebdSchin 		fcrestore(&save);
2001da2e3ebdSchin 	}
2002da2e3ebdSchin 	else
2003da2e3ebdSchin 		sp = sfopen(NIL(Sfio_t*),"","sr");
20047c2fbfb3SApril Chin 	sh_freeup(mp->shp);
20057c2fbfb3SApril Chin 	mp->shp->st.staklist = saveslp;
2006da2e3ebdSchin 	if(was_history)
2007da2e3ebdSchin 		sh_onstate(SH_HISTORY);
2008da2e3ebdSchin 	if(was_verbose)
2009da2e3ebdSchin 		sh_onstate(SH_VERBOSE);
2010da2e3ebdSchin #else
2011da2e3ebdSchin 	sp = sfpopen(NIL(Sfio_t*),str,"r");
2012da2e3ebdSchin #endif
2013da2e3ebdSchin 	*mp = savemac;
20147c2fbfb3SApril Chin 	np = sh_scoped(mp->shp,IFSNOD);
20157c2fbfb3SApril Chin 	nv_putval(np,mp->ifsp,NV_RDONLY);
2016da2e3ebdSchin 	mp->ifsp = nv_getval(np);
20177c2fbfb3SApril Chin 	stkset(stkp,savptr,savtop);
2018da2e3ebdSchin 	newlines = 0;
2019da2e3ebdSchin 	lastc = 0;
2020da2e3ebdSchin 	sfsetbuf(sp,(void*)sp,0);
2021da2e3ebdSchin 	bufsize = sfvalue(sp);
2022da2e3ebdSchin 	/* read command substitution output and put on stack or here-doc */
2023da2e3ebdSchin 	sfpool(sp, NIL(Sfio_t*), SF_WRITE);
20247c2fbfb3SApril Chin 	sh_offstate(SH_INTERACTIVE);
2025da2e3ebdSchin 	while((str=(char*)sfreserve(sp,SF_UNBOUND,0)) && (c = sfvalue(sp))>0)
2026da2e3ebdSchin 	{
2027da2e3ebdSchin #if SHOPT_CRNL
2028da2e3ebdSchin 		/* eliminate <cr> */
2029da2e3ebdSchin 		register char *dp;
2030da2e3ebdSchin 		char *buff = str;
2031da2e3ebdSchin 		while(c>1 && (*str !='\r'|| str[1]!='\n'))
2032da2e3ebdSchin 		{
2033da2e3ebdSchin 			c--;
2034da2e3ebdSchin 			str++;
2035da2e3ebdSchin 		}
2036da2e3ebdSchin 		dp = str;
2037da2e3ebdSchin 		while(c>1)
2038da2e3ebdSchin 		{
2039da2e3ebdSchin 			str++;
2040da2e3ebdSchin 			c--;
2041da2e3ebdSchin 			while(c>1 && (*str!='\r' || str[1]!='\n'))
2042da2e3ebdSchin 			{
2043da2e3ebdSchin 				c--;
2044da2e3ebdSchin 				*dp++ = *str++;
2045da2e3ebdSchin 			}
2046da2e3ebdSchin 		}
2047da2e3ebdSchin 		if(c)
2048da2e3ebdSchin 			*dp++ = *str++;
2049da2e3ebdSchin 		str = buff;
2050da2e3ebdSchin 		c = dp-str;
2051da2e3ebdSchin #endif /* SHOPT_CRNL */
20527c2fbfb3SApril Chin 		/* delay appending trailing new-lines */
20537c2fbfb3SApril Chin 		for(nextnewlines=0; c-->0 && str[c]=='\n'; nextnewlines++);
20547c2fbfb3SApril Chin 		if(c < 0)
20557c2fbfb3SApril Chin 		{
20567c2fbfb3SApril Chin 			newlines += nextnewlines;
20577c2fbfb3SApril Chin 			continue;
20587c2fbfb3SApril Chin 		}
2059da2e3ebdSchin 		if(newlines >0)
2060da2e3ebdSchin 		{
2061da2e3ebdSchin 			if(mp->sp)
2062da2e3ebdSchin 				sfnputc(mp->sp,'\n',newlines);
20637c2fbfb3SApril Chin 			else if(!mp->quote && mp->split && mp->shp->ifstable['\n'])
2064da2e3ebdSchin 				endfield(mp,0);
20657c2fbfb3SApril Chin 			else
20667c2fbfb3SApril Chin 				sfnputc(stkp,'\n',newlines);
2067da2e3ebdSchin 		}
2068da2e3ebdSchin 		else if(lastc)
2069da2e3ebdSchin 		{
2070da2e3ebdSchin 			mac_copy(mp,&lastc,1);
2071da2e3ebdSchin 			lastc = 0;
2072da2e3ebdSchin 		}
20737c2fbfb3SApril Chin 		newlines = nextnewlines;
2074da2e3ebdSchin 		if(++c < bufsize)
2075da2e3ebdSchin 			str[c] = 0;
2076da2e3ebdSchin 		else
2077da2e3ebdSchin 		{
2078da2e3ebdSchin 			/* can't write past buffer so save last character */
2079da2e3ebdSchin 			lastc = str[--c];
2080da2e3ebdSchin 			str[c] = 0;
2081da2e3ebdSchin 		}
2082da2e3ebdSchin 		mac_copy(mp,str,c);
2083da2e3ebdSchin 	}
20847c2fbfb3SApril Chin 	if(was_interactive)
20857c2fbfb3SApril Chin 		sh_onstate(SH_INTERACTIVE);
20867c2fbfb3SApril Chin 	if(mp->shp->spid)
20877c2fbfb3SApril Chin 		job_wait(mp->shp->spid);
20887c2fbfb3SApril Chin 	if(--newlines>0 && mp->shp->ifstable['\n']==S_DELIM)
2089da2e3ebdSchin 	{
2090da2e3ebdSchin 		if(mp->sp)
2091da2e3ebdSchin 			sfnputc(mp->sp,'\n',newlines);
20927c2fbfb3SApril Chin 		else if(!mp->quote && mp->split)
20937c2fbfb3SApril Chin 			while(newlines--)
20947c2fbfb3SApril Chin 				endfield(mp,1);
20957c2fbfb3SApril Chin 		else
20967c2fbfb3SApril Chin 			sfnputc(stkp,'\n',newlines);
2097da2e3ebdSchin 	}
2098da2e3ebdSchin 	if(lastc)
2099da2e3ebdSchin 		mac_copy(mp,&lastc,1);
2100da2e3ebdSchin 	sfclose(sp);
2101da2e3ebdSchin 	return;
2102da2e3ebdSchin }
2103da2e3ebdSchin 
2104da2e3ebdSchin /*
2105da2e3ebdSchin  * copy <str> onto the stack
2106da2e3ebdSchin  */
mac_copy(register Mac_t * mp,register const char * str,register int size)2107da2e3ebdSchin static void mac_copy(register Mac_t *mp,register const char *str, register int size)
2108da2e3ebdSchin {
2109da2e3ebdSchin 	register char		*state;
2110da2e3ebdSchin 	register const char	*cp=str;
21117c2fbfb3SApril Chin 	register int		c,n,nopat,len;
21127c2fbfb3SApril Chin 	Stk_t			*stkp=mp->shp->stk;
2113da2e3ebdSchin 	nopat = (mp->quote||mp->assign==1||mp->arith);
2114da2e3ebdSchin 	if(mp->zeros)
2115da2e3ebdSchin 	{
2116da2e3ebdSchin 		/* prevent leading 0's from becomming octal constants */
2117da2e3ebdSchin 		while(size>1 && *str=='0')
2118da2e3ebdSchin 			str++,size--;
2119da2e3ebdSchin 		mp->zeros = 0;
2120da2e3ebdSchin 		cp = str;
2121da2e3ebdSchin 	}
2122da2e3ebdSchin 	if(mp->sp)
2123da2e3ebdSchin 		sfwrite(mp->sp,str,size);
2124da2e3ebdSchin 	else if(mp->pattern>=2 || (mp->pattern && nopat))
2125da2e3ebdSchin 	{
2126da2e3ebdSchin 		state = sh_lexstates[ST_MACRO];
2127da2e3ebdSchin 		/* insert \ before file expansion characters */
2128da2e3ebdSchin 		while(size-->0)
2129da2e3ebdSchin 		{
21307c2fbfb3SApril Chin #if SHOPT_MULTIBYTE
21317c2fbfb3SApril Chin 			if(mbwide() && (len=mbsize(cp))>1)
21327c2fbfb3SApril Chin 			{
21337c2fbfb3SApril Chin 				cp += len;
21347c2fbfb3SApril Chin 				size -= (len-1);
21357c2fbfb3SApril Chin 				continue;
21367c2fbfb3SApril Chin 			}
21377c2fbfb3SApril Chin #endif
2138da2e3ebdSchin 			c = state[n= *(unsigned char*)cp++];
2139da2e3ebdSchin 			if(nopat&&(c==S_PAT||c==S_ESC||c==S_BRACT||c==S_ENDCH) && mp->pattern!=3)
2140da2e3ebdSchin 				c=1;
2141da2e3ebdSchin 			else if(mp->pattern==4 && (c==S_ESC||c==S_BRACT||c==S_ENDCH || isastchar(n)))
2142da2e3ebdSchin 				c=1;
2143da2e3ebdSchin 			else if(mp->pattern==2 && c==S_SLASH)
2144da2e3ebdSchin 				c=1;
2145da2e3ebdSchin 			else if(mp->pattern==3 && c==S_ESC && (state[*(unsigned char*)cp]==S_DIG||(*cp==ESCAPE)))
2146da2e3ebdSchin 			{
2147da2e3ebdSchin 				if(!(c=mp->quote))
2148da2e3ebdSchin 					cp++;
2149da2e3ebdSchin 			}
2150da2e3ebdSchin 			else
2151da2e3ebdSchin 				c=0;
2152da2e3ebdSchin 			if(c)
2153da2e3ebdSchin 			{
2154da2e3ebdSchin 				if(c = (cp-1) - str)
21557c2fbfb3SApril Chin 					sfwrite(stkp,str,c);
21567c2fbfb3SApril Chin 				sfputc(stkp,ESCAPE);
2157da2e3ebdSchin 				str = cp-1;
2158da2e3ebdSchin 			}
2159da2e3ebdSchin 		}
2160da2e3ebdSchin 		if(c = cp-str)
21617c2fbfb3SApril Chin 			sfwrite(stkp,str,c);
2162da2e3ebdSchin 	}
2163da2e3ebdSchin 	else if(!mp->quote && mp->split && (mp->ifs||mp->pattern))
2164da2e3ebdSchin 	{
2165da2e3ebdSchin 		/* split words at ifs characters */
21667c2fbfb3SApril Chin 		state = mp->shp->ifstable;
2167da2e3ebdSchin 		if(mp->pattern)
2168da2e3ebdSchin 		{
2169da2e3ebdSchin 			char *sp = "&|()";
2170da2e3ebdSchin 			while(c = *sp++)
2171da2e3ebdSchin 			{
2172da2e3ebdSchin 				if(state[c]==0)
2173da2e3ebdSchin 					state[c] = S_EPAT;
2174da2e3ebdSchin 			}
2175da2e3ebdSchin 			sp = "*?[{";
2176da2e3ebdSchin 			while(c = *sp++)
2177da2e3ebdSchin 			{
2178da2e3ebdSchin 				if(state[c]==0)
2179da2e3ebdSchin 					state[c] = S_PAT;
2180da2e3ebdSchin 			}
2181da2e3ebdSchin 			if(state[ESCAPE]==0)
2182da2e3ebdSchin 				state[ESCAPE] = S_ESC;
2183da2e3ebdSchin 		}
2184da2e3ebdSchin 		while(size-->0)
2185da2e3ebdSchin 		{
21867c2fbfb3SApril Chin 			n=state[c= *(unsigned char*)cp++];
21877c2fbfb3SApril Chin #if SHOPT_MULTIBYTE
21887c2fbfb3SApril Chin 			if(mbwide() && n!=S_MBYTE && (len=mbsize(cp-1))>1)
21897c2fbfb3SApril Chin 			{
21907c2fbfb3SApril Chin 				sfwrite(stkp,cp-1, len);
21917c2fbfb3SApril Chin 				cp += --len;
21927c2fbfb3SApril Chin 				size -= len;
21937c2fbfb3SApril Chin 				continue;
21947c2fbfb3SApril Chin 			}
21957c2fbfb3SApril Chin #endif
21967c2fbfb3SApril Chin 			if(n==S_ESC || n==S_EPAT)
2197da2e3ebdSchin 			{
2198da2e3ebdSchin 				/* don't allow extended patterns in this case */
2199da2e3ebdSchin 				mp->patfound = mp->pattern;
22007c2fbfb3SApril Chin 				sfputc(stkp,ESCAPE);
2201da2e3ebdSchin 			}
2202da2e3ebdSchin 			else if(n==S_PAT)
2203da2e3ebdSchin 				mp->patfound = mp->pattern;
2204da2e3ebdSchin 			else if(n && mp->ifs)
2205da2e3ebdSchin 			{
2206da2e3ebdSchin #if SHOPT_MULTIBYTE
2207da2e3ebdSchin 				if(n==S_MBYTE)
2208da2e3ebdSchin 				{
2209da2e3ebdSchin 					if(sh_strchr(mp->ifsp,cp-1)<0)
2210da2e3ebdSchin 						continue;
2211da2e3ebdSchin 					n = mbsize(cp-1) - 1;
2212da2e3ebdSchin 					if(n==-2)
2213da2e3ebdSchin 						n = 0;
2214da2e3ebdSchin 					cp += n;
2215da2e3ebdSchin 					size -= n;
2216da2e3ebdSchin 					n= S_DELIM;
2217da2e3ebdSchin 				}
2218da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
2219da2e3ebdSchin 				if(n==S_SPACE || n==S_NL)
2220da2e3ebdSchin 				{
2221da2e3ebdSchin 					while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL))
2222da2e3ebdSchin 						size--;
2223da2e3ebdSchin #if SHOPT_MULTIBYTE
2224da2e3ebdSchin 					if(n==S_MBYTE && sh_strchr(mp->ifsp,cp-1)>=0)
2225da2e3ebdSchin 					{
2226da2e3ebdSchin 						n = mbsize(cp-1) - 1;
2227da2e3ebdSchin 						if(n==-2)
2228da2e3ebdSchin 							n = 0;
2229da2e3ebdSchin 						cp += n;
2230da2e3ebdSchin 						size -= n;
2231da2e3ebdSchin 						n=S_DELIM;
2232da2e3ebdSchin 					}
2233da2e3ebdSchin 					else
2234da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
2235da2e3ebdSchin 					if(n==S_DELIM)
2236da2e3ebdSchin 						size--;
2237da2e3ebdSchin 				}
2238da2e3ebdSchin 				endfield(mp,n==S_DELIM||mp->quoted);
2239da2e3ebdSchin 				mp->patfound = 0;
2240da2e3ebdSchin 				if(n==S_DELIM)
2241da2e3ebdSchin 					while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL))
2242da2e3ebdSchin 						size--;
2243da2e3ebdSchin 				if(size<=0)
2244da2e3ebdSchin 					break;
2245da2e3ebdSchin 				cp--;
2246da2e3ebdSchin 				continue;
2247da2e3ebdSchin 
2248da2e3ebdSchin 			}
22497c2fbfb3SApril Chin 			sfputc(stkp,c);
2250da2e3ebdSchin 		}
2251da2e3ebdSchin 		if(mp->pattern)
2252da2e3ebdSchin 		{
2253da2e3ebdSchin 			cp = "&|()";
2254da2e3ebdSchin 			while(c = *cp++)
2255da2e3ebdSchin 			{
2256da2e3ebdSchin 				if(state[c]==S_EPAT)
2257da2e3ebdSchin 					state[c] = 0;
2258da2e3ebdSchin 			}
2259da2e3ebdSchin 			cp = "*?[{";
2260da2e3ebdSchin 			while(c = *cp++)
2261da2e3ebdSchin 			{
2262da2e3ebdSchin 				if(state[c]==S_PAT)
2263da2e3ebdSchin 					state[c] = 0;
2264da2e3ebdSchin 			}
22657c2fbfb3SApril Chin 			if(mp->shp->ifstable[ESCAPE]==S_ESC)
22667c2fbfb3SApril Chin 				mp->shp->ifstable[ESCAPE] = 0;
2267da2e3ebdSchin 		}
2268da2e3ebdSchin 	}
2269da2e3ebdSchin 	else
22707c2fbfb3SApril Chin 		sfwrite(stkp,str,size);
2271da2e3ebdSchin }
2272da2e3ebdSchin 
2273da2e3ebdSchin /*
2274da2e3ebdSchin  * Terminate field.
2275da2e3ebdSchin  * If field is null count field if <split> is non-zero
2276da2e3ebdSchin  * Do filename expansion of required
2277da2e3ebdSchin  */
endfield(register Mac_t * mp,int split)2278da2e3ebdSchin static void endfield(register Mac_t *mp,int split)
2279da2e3ebdSchin {
2280da2e3ebdSchin 	register struct argnod	*argp;
2281da2e3ebdSchin 	register int		count=0;
22827c2fbfb3SApril Chin 	Stk_t			*stkp = mp->shp->stk;
22837c2fbfb3SApril Chin 	if(stktell(stkp) > ARGVAL || split)
2284da2e3ebdSchin 	{
22857c2fbfb3SApril Chin 		argp = (struct argnod*)stkfreeze(stkp,1);
2286da2e3ebdSchin 		argp->argnxt.cp = 0;
2287da2e3ebdSchin 		argp->argflag = 0;
2288da2e3ebdSchin 		if(mp->patfound)
2289da2e3ebdSchin 		{
22907c2fbfb3SApril Chin 			mp->shp->argaddr = 0;
2291da2e3ebdSchin #if SHOPT_BRACEPAT
2292da2e3ebdSchin 			count = path_generate(argp,mp->arghead);
2293da2e3ebdSchin #else
2294da2e3ebdSchin 			count = path_expand(argp->argval,mp->arghead);
2295da2e3ebdSchin #endif /* SHOPT_BRACEPAT */
2296da2e3ebdSchin 			if(count)
2297da2e3ebdSchin 				mp->fields += count;
2298da2e3ebdSchin 			else if(split)	/* pattern is null string */
2299da2e3ebdSchin 				*argp->argval = 0;
2300da2e3ebdSchin 			else	/* pattern expands to nothing */
2301da2e3ebdSchin 				count = -1;
2302da2e3ebdSchin 		}
2303da2e3ebdSchin 		if(count==0)
2304da2e3ebdSchin 		{
2305da2e3ebdSchin 			argp->argchn.ap = *mp->arghead;
2306da2e3ebdSchin 			*mp->arghead = argp;
2307da2e3ebdSchin 			mp->fields++;
2308da2e3ebdSchin 		}
2309da2e3ebdSchin 		if(count>=0)
2310da2e3ebdSchin 		{
2311da2e3ebdSchin 			(*mp->arghead)->argflag |= ARG_MAKE;
2312da2e3ebdSchin 			if(mp->assign || sh_isoption(SH_NOGLOB))
2313da2e3ebdSchin 				argp->argflag |= ARG_RAW|ARG_EXP;
2314da2e3ebdSchin 		}
23157c2fbfb3SApril Chin 		stkseek(stkp,ARGVAL);
2316da2e3ebdSchin 	}
2317da2e3ebdSchin 	mp->quoted = mp->quote;
2318da2e3ebdSchin }
2319da2e3ebdSchin 
2320da2e3ebdSchin /*
2321da2e3ebdSchin  * Finds the right substring of STRING using the expression PAT
2322da2e3ebdSchin  * the longest substring is found when FLAG is set.
2323da2e3ebdSchin  */
substring(register const char * string,const char * pat,int match[],int flag)2324da2e3ebdSchin static int substring(register const char *string,const char *pat,int match[], int flag)
2325da2e3ebdSchin {
2326da2e3ebdSchin 	register const char *sp=string;
2327da2e3ebdSchin 	register int size,len,nmatch,n;
2328da2e3ebdSchin 	int smatch[2*(MATCH_MAX+1)];
2329da2e3ebdSchin 	if(flag)
2330da2e3ebdSchin 	{
2331da2e3ebdSchin 		if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_MAXIMAL))
2332da2e3ebdSchin 		{
2333da2e3ebdSchin 			memcpy(match,smatch,n*2*sizeof(smatch[0]));
2334da2e3ebdSchin 			return(n);
2335da2e3ebdSchin 		}
2336da2e3ebdSchin 		return(0);
2337da2e3ebdSchin 	}
2338da2e3ebdSchin 	size = len = strlen(sp);
2339da2e3ebdSchin 	sp += size;
2340da2e3ebdSchin 	while(sp>=string)
2341da2e3ebdSchin 	{
2342da2e3ebdSchin #if SHOPT_MULTIBYTE
2343da2e3ebdSchin 		if(mbwide())
2344da2e3ebdSchin 			sp = lastchar(string,sp);
2345da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
2346da2e3ebdSchin 		if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_LEFT|STR_MAXIMAL))
2347da2e3ebdSchin 		{
2348da2e3ebdSchin 			nmatch = n;
2349da2e3ebdSchin 			memcpy(match,smatch,n*2*sizeof(smatch[0]));
2350da2e3ebdSchin 			size = sp-string;
2351da2e3ebdSchin 			break;
2352da2e3ebdSchin 		}
2353da2e3ebdSchin 		sp--;
2354da2e3ebdSchin 	}
2355da2e3ebdSchin 	if(size==len)
2356da2e3ebdSchin 		return(0);
2357da2e3ebdSchin 	if(nmatch)
2358da2e3ebdSchin 	{
2359da2e3ebdSchin 		nmatch *=2;
2360da2e3ebdSchin 		while(--nmatch>=0)
2361da2e3ebdSchin 			match[nmatch] += size;
2362da2e3ebdSchin 	}
2363da2e3ebdSchin 	return(n);
2364da2e3ebdSchin }
2365da2e3ebdSchin 
2366da2e3ebdSchin #if SHOPT_MULTIBYTE
lastchar(const char * string,const char * endstring)2367da2e3ebdSchin 	static char	*lastchar(const char *string, const char *endstring)
2368da2e3ebdSchin 	{
2369da2e3ebdSchin 		register char *str = (char*)string;
2370da2e3ebdSchin 		register int c;
2371da2e3ebdSchin 		mbinit();
2372da2e3ebdSchin 		while(*str)
2373da2e3ebdSchin 		{
2374da2e3ebdSchin 			if((c=mbsize(str))<0)
2375da2e3ebdSchin 				c = 1;
2376da2e3ebdSchin 			if(str+c > endstring)
2377da2e3ebdSchin 				break;
2378da2e3ebdSchin 			str += c;
2379da2e3ebdSchin 		}
2380da2e3ebdSchin 		return(str);
2381da2e3ebdSchin 	}
2382da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
charlen(const char * string,int len)2383da2e3ebdSchin static int	charlen(const char *string,int len)
2384da2e3ebdSchin {
2385da2e3ebdSchin 	if(!string)
2386da2e3ebdSchin 		return(0);
2387da2e3ebdSchin #if SHOPT_MULTIBYTE
2388da2e3ebdSchin 	if(mbwide())
2389da2e3ebdSchin 	{
2390da2e3ebdSchin 		register const char *str = string, *strmax=string+len;
2391da2e3ebdSchin 		register int n=0;
2392da2e3ebdSchin 		mbinit();
2393da2e3ebdSchin 		if(len>0)
2394da2e3ebdSchin 		{
2395da2e3ebdSchin 			while(str<strmax && mbchar(str))
2396da2e3ebdSchin 				n++;
2397da2e3ebdSchin 		}
2398da2e3ebdSchin 		else while(mbchar(str))
2399da2e3ebdSchin 			n++;
2400da2e3ebdSchin 		return(n);
2401da2e3ebdSchin 	}
2402da2e3ebdSchin 	else
2403da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
2404da2e3ebdSchin 	{
2405da2e3ebdSchin 		if(len<0)
2406da2e3ebdSchin 			return(strlen(string));
2407da2e3ebdSchin 		return(len);
2408da2e3ebdSchin 	}
2409da2e3ebdSchin }
2410da2e3ebdSchin 
2411da2e3ebdSchin /*
2412da2e3ebdSchin  * This is the default tilde discipline function
2413da2e3ebdSchin  */
sh_btilde(int argc,char * argv[],void * context)2414da2e3ebdSchin static int sh_btilde(int argc, char *argv[], void *context)
2415da2e3ebdSchin {
24167c2fbfb3SApril Chin 	Shell_t *shp = ((Shbltin_t*)context)->shp;
24177c2fbfb3SApril Chin 	char *cp = sh_tilde(shp,argv[1]);
2418da2e3ebdSchin 	NOT_USED(argc);
2419da2e3ebdSchin 	if(!cp)
2420da2e3ebdSchin 		cp = argv[1];
2421da2e3ebdSchin 	sfputr(sfstdout, cp, '\n');
2422da2e3ebdSchin 	return(0);
2423da2e3ebdSchin }
2424da2e3ebdSchin 
2425da2e3ebdSchin /*
2426da2e3ebdSchin  * <offset> is byte offset for beginning of tilde string
2427da2e3ebdSchin  */
tilde_expand2(Shell_t * shp,register int offset)24287c2fbfb3SApril Chin static void tilde_expand2(Shell_t *shp, register int offset)
2429da2e3ebdSchin {
24307c2fbfb3SApril Chin 	char		shtilde[10], *av[3], *ptr=stkfreeze(shp->stk,1);
2431da2e3ebdSchin 	Sfio_t		*iop, *save=sfstdout;
2432da2e3ebdSchin 	Namval_t	*np;
2433da2e3ebdSchin 	static int	beenhere=0;
2434da2e3ebdSchin 	strcpy(shtilde,".sh.tilde");
24357c2fbfb3SApril Chin 	np = nv_open(shtilde,shp->fun_tree, NV_VARNAME|NV_NOARRAY|NV_NOASSIGN|NV_NOFAIL);
2436da2e3ebdSchin 	if(np && !beenhere)
2437da2e3ebdSchin 	{
2438da2e3ebdSchin 		beenhere = 1;
2439da2e3ebdSchin 		sh_addbuiltin(shtilde,sh_btilde,0);
24407c2fbfb3SApril Chin 		nv_onattr(np,NV_EXPORT);
2441da2e3ebdSchin 	}
2442da2e3ebdSchin 	av[0] = ".sh.tilde";
2443da2e3ebdSchin 	av[1] = &ptr[offset];
2444da2e3ebdSchin 	av[2] = 0;
2445da2e3ebdSchin 	iop = sftmp(IOBSIZE+1);;
2446da2e3ebdSchin 	sfset(iop,SF_READ,0);
2447da2e3ebdSchin 	sfstdout = iop;
2448da2e3ebdSchin 	if(np)
2449da2e3ebdSchin 		sh_fun(np, (Namval_t*)0, av);
2450da2e3ebdSchin 	else
2451da2e3ebdSchin 		sh_btilde(2, av, &sh);
2452da2e3ebdSchin 	sfstdout = save;
24537c2fbfb3SApril Chin 	stkset(shp->stk,ptr, offset);
2454da2e3ebdSchin 	sfseek(iop,(Sfoff_t)0,SEEK_SET);
2455da2e3ebdSchin 	sfset(iop,SF_READ,1);
2456da2e3ebdSchin 	if(ptr = sfreserve(iop, SF_UNBOUND, -1))
2457da2e3ebdSchin 	{
2458da2e3ebdSchin 		Sfoff_t n = sfvalue(iop);
2459da2e3ebdSchin 		while(ptr[n-1]=='\n')
2460da2e3ebdSchin 			n--;
2461da2e3ebdSchin 		if(n==1 && fcpeek(0)=='/' && ptr[n-1])
2462da2e3ebdSchin 			n--;
2463da2e3ebdSchin 		if(n)
24647c2fbfb3SApril Chin 			sfwrite(shp->stk,ptr,n);
2465da2e3ebdSchin 	}
2466da2e3ebdSchin 	else
24677c2fbfb3SApril Chin 		sfputr(shp->stk,av[1],0);
2468da2e3ebdSchin 	sfclose(iop);
2469da2e3ebdSchin }
2470da2e3ebdSchin 
2471da2e3ebdSchin /*
2472da2e3ebdSchin  * This routine is used to resolve ~ expansion.
2473da2e3ebdSchin  * A ~ by itself is replaced with the users login directory.
2474da2e3ebdSchin  * A ~- is replaced by the previous working directory in shell.
2475da2e3ebdSchin  * A ~+ is replaced by the present working directory in shell.
2476da2e3ebdSchin  * If ~name  is replaced with login directory of name.
2477da2e3ebdSchin  * If string doesn't start with ~ or ~... not found then 0 returned.
2478da2e3ebdSchin  */
2479da2e3ebdSchin 
sh_tilde(Shell_t * shp,register const char * string)24807c2fbfb3SApril Chin static char *sh_tilde(Shell_t *shp,register const char *string)
2481da2e3ebdSchin {
2482da2e3ebdSchin 	register char		*cp;
2483da2e3ebdSchin 	register int		c;
2484da2e3ebdSchin 	register struct passwd	*pw;
2485da2e3ebdSchin 	register Namval_t *np=0;
2486da2e3ebdSchin 	static Dt_t *logins_tree;
2487da2e3ebdSchin 	if(*string++!='~')
2488da2e3ebdSchin 		return(NIL(char*));
2489da2e3ebdSchin 	if((c = *string)==0)
2490da2e3ebdSchin 	{
24917c2fbfb3SApril Chin 		if(!(cp=nv_getval(sh_scoped(shp,HOME))))
2492da2e3ebdSchin 			cp = getlogin();
2493da2e3ebdSchin 		return(cp);
2494da2e3ebdSchin 	}
2495da2e3ebdSchin 	if((c=='-' || c=='+') && string[1]==0)
2496da2e3ebdSchin 	{
2497da2e3ebdSchin 		if(c=='+')
24987c2fbfb3SApril Chin 			cp = nv_getval(sh_scoped(shp,PWDNOD));
2499da2e3ebdSchin 		else
25007c2fbfb3SApril Chin 			cp = nv_getval(sh_scoped(shp,OLDPWDNOD));
2501da2e3ebdSchin 		return(cp);
2502da2e3ebdSchin 	}
2503da2e3ebdSchin 	if(logins_tree && (np=nv_search(string,logins_tree,0)))
2504da2e3ebdSchin 		return(nv_getval(np));
2505da2e3ebdSchin 	if(!(pw = getpwnam(string)))
2506da2e3ebdSchin 		return(NIL(char*));
2507da2e3ebdSchin 	if(!logins_tree)
2508da2e3ebdSchin 		logins_tree = dtopen(&_Nvdisc,Dtbag);
2509da2e3ebdSchin 	if(np=nv_search(string,logins_tree,NV_ADD))
2510da2e3ebdSchin 		nv_putval(np, pw->pw_dir,0);
2511da2e3ebdSchin 	return(pw->pw_dir);
2512da2e3ebdSchin }
2513da2e3ebdSchin 
2514da2e3ebdSchin /*
2515da2e3ebdSchin  * return values for special macros
2516da2e3ebdSchin  */
special(Shell_t * shp,register int c)25177c2fbfb3SApril Chin static char *special(Shell_t *shp,register int c)
2518da2e3ebdSchin {
2519da2e3ebdSchin 	if(c!='$')
25207c2fbfb3SApril Chin 		shp->argaddr = 0;
2521da2e3ebdSchin 	switch(c)
2522da2e3ebdSchin 	{
2523da2e3ebdSchin 	    case '@':
2524da2e3ebdSchin 	    case '*':
25257c2fbfb3SApril Chin 		return(shp->st.dolc>0?shp->st.dolv[1]:NIL(char*));
2526da2e3ebdSchin 	    case '#':
2527da2e3ebdSchin #if  SHOPT_FILESCAN
25287c2fbfb3SApril Chin 		if(shp->cur_line)
2529da2e3ebdSchin 		{
25307c2fbfb3SApril Chin 			getdolarg(shp,MAX_ARGN,(int*)0);
25317c2fbfb3SApril Chin 			return(ltos(shp->offsets[0]));
2532da2e3ebdSchin 		}
2533da2e3ebdSchin #endif  /* SHOPT_FILESCAN */
25347c2fbfb3SApril Chin 		return(ltos(shp->st.dolc));
2535da2e3ebdSchin 	    case '!':
25367c2fbfb3SApril Chin 		if(shp->bckpid)
25377c2fbfb3SApril Chin 			return(ltos(shp->bckpid));
2538da2e3ebdSchin 		break;
2539da2e3ebdSchin 	    case '$':
2540da2e3ebdSchin 		if(nv_isnull(SH_DOLLARNOD))
25417c2fbfb3SApril Chin 			return(ltos(shp->pid));
2542da2e3ebdSchin 		return(nv_getval(SH_DOLLARNOD));
2543da2e3ebdSchin 	    case '-':
25447c2fbfb3SApril Chin 		return(sh_argdolminus(shp->arg_context));
2545da2e3ebdSchin 	    case '?':
25467c2fbfb3SApril Chin 		return(ltos(shp->savexit));
2547da2e3ebdSchin 	    case 0:
254834f9b3eeSRoland Mainz 		if(sh_isstate(SH_PROFILE) || shp->fn_depth==0 || !shp->st.cmdname)
25497c2fbfb3SApril Chin 			return(shp->shname);
2550da2e3ebdSchin 		else
255134f9b3eeSRoland Mainz 			return(shp->st.cmdname);
2552da2e3ebdSchin 	}
2553da2e3ebdSchin 	return(NIL(char*));
2554da2e3ebdSchin }
2555da2e3ebdSchin 
2556da2e3ebdSchin /*
2557da2e3ebdSchin  * Handle macro expansion errors
2558da2e3ebdSchin  */
mac_error(Namval_t * np)2559da2e3ebdSchin static void mac_error(Namval_t *np)
2560da2e3ebdSchin {
2561da2e3ebdSchin 	if(np)
2562da2e3ebdSchin 		nv_close(np);
2563da2e3ebdSchin 	errormsg(SH_DICT,ERROR_exit(1),e_subst,fcfirst());
2564da2e3ebdSchin }
2565da2e3ebdSchin 
2566da2e3ebdSchin /*
2567da2e3ebdSchin  * Given pattern/string, replace / with 0 and return pointer to string
25687c2fbfb3SApril Chin  * \ characters are stripped from string.  The \ are stripped in the
25697c2fbfb3SApril Chin  * replacement string unless followed by a digit or \.
2570da2e3ebdSchin  */
mac_getstring(char * pattern)2571da2e3ebdSchin static char *mac_getstring(char *pattern)
2572da2e3ebdSchin {
25737c2fbfb3SApril Chin 	register char	*cp=pattern, *rep=0, *dp;
2574da2e3ebdSchin 	register int	c;
2575da2e3ebdSchin 	while(c = *cp++)
2576da2e3ebdSchin 	{
25777c2fbfb3SApril Chin 		if(c==ESCAPE && (!rep || (*cp && strchr("&|()[]*?",*cp))))
25787c2fbfb3SApril Chin 		{
25797c2fbfb3SApril Chin 			c = *cp++;
25807c2fbfb3SApril Chin 		}
25817c2fbfb3SApril Chin 		else if(!rep && c=='/')
2582da2e3ebdSchin 		{
2583da2e3ebdSchin 			cp[-1] = 0;
25847c2fbfb3SApril Chin 			rep = dp = cp;
25857c2fbfb3SApril Chin 			continue;
2586da2e3ebdSchin 		}
25877c2fbfb3SApril Chin 		if(rep)
25887c2fbfb3SApril Chin 			*dp++ = c;
2589da2e3ebdSchin 	}
25907c2fbfb3SApril Chin 	if(rep)
25917c2fbfb3SApril Chin 		*dp = 0;
25927c2fbfb3SApril Chin 	return(rep);
2593da2e3ebdSchin }
2594