1*4887Schin /***********************************************************************
2*4887Schin *                                                                      *
3*4887Schin *               This software is part of the ast package               *
4*4887Schin *           Copyright (c) 1982-2007 AT&T Knowledge Ventures            *
5*4887Schin *                      and is licensed under the                       *
6*4887Schin *                  Common Public License, Version 1.0                  *
7*4887Schin *                      by AT&T Knowledge Ventures                      *
8*4887Schin *                                                                      *
9*4887Schin *                A copy of the License is available at                 *
10*4887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*4887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*4887Schin *                                                                      *
13*4887Schin *              Information and Software Systems Research               *
14*4887Schin *                            AT&T Research                             *
15*4887Schin *                           Florham Park NJ                            *
16*4887Schin *                                                                      *
17*4887Schin *                  David Korn <dgk@research.att.com>                   *
18*4887Schin *                                                                      *
19*4887Schin ***********************************************************************/
20*4887Schin #pragma prototyped
21*4887Schin /*
22*4887Schin  * Shell macro expander
23*4887Schin  * expands ~
24*4887Schin  * expands ${...}
25*4887Schin  * expands $(...)
26*4887Schin  * expands $((...))
27*4887Schin  * expands `...`
28*4887Schin  *
29*4887Schin  *   David Korn
30*4887Schin  *   AT&T Labs
31*4887Schin  *
32*4887Schin  */
33*4887Schin 
34*4887Schin #include	"defs.h"
35*4887Schin #include	<fcin.h>
36*4887Schin #include	<pwd.h>
37*4887Schin #include	"name.h"
38*4887Schin #include	"variables.h"
39*4887Schin #include	"shlex.h"
40*4887Schin #include	"io.h"
41*4887Schin #include	"shnodes.h"
42*4887Schin #include	"path.h"
43*4887Schin #include	"national.h"
44*4887Schin #include	"streval.h"
45*4887Schin 
46*4887Schin #undef STR_GROUP
47*4887Schin #ifndef STR_GROUP
48*4887Schin #   define STR_GROUP	0
49*4887Schin #endif
50*4887Schin 
51*4887Schin #if !SHOPT_MULTIBYTE
52*4887Schin #define mbchar(p)       (*(unsigned char*)p++)
53*4887Schin #endif
54*4887Schin 
55*4887Schin static int	_c_;
56*4887Schin typedef struct  _mac_
57*4887Schin {
58*4887Schin 	Shell_t		*shp;		/* pointer to shell interpreter */
59*4887Schin 	Sfio_t		*sp;		/* stream pointer for here-document */
60*4887Schin 	struct argnod	**arghead;	/* address of head of argument list */
61*4887Schin 	char		*ifsp;		/* pointer to IFS value */
62*4887Schin 	int		fields;		/* number of fields */
63*4887Schin 	short		quoted;		/* set when word has quotes */
64*4887Schin 	unsigned char	ifs;		/* first char of IFS */
65*4887Schin 	char		quote;		/* set within double quoted contexts */
66*4887Schin 	char		lit;		/* set within single quotes */
67*4887Schin 	char		split;		/* set when word splittin is possible */
68*4887Schin 	char		pattern;	/* set when file expansion follows */
69*4887Schin 	char		patfound;	/* set if pattern character found */
70*4887Schin 	char		assign;		/* set for assignments */
71*4887Schin 	char		arith;		/* set for ((...)) */
72*4887Schin 	char		let;		/* set when expanding let arguments */
73*4887Schin 	char		zeros;		/* strip leading zeros when set */
74*4887Schin 	void		*nvwalk;	/* for name space walking*/
75*4887Schin } Mac_t;
76*4887Schin 
77*4887Schin #define mac	(*((Mac_t*)(sh.mac_context)))
78*4887Schin 
79*4887Schin #undef ESCAPE
80*4887Schin #define ESCAPE		'\\'
81*4887Schin #define isescchar(s)	((s)>S_QUOTE)
82*4887Schin #define isqescchar(s)	((s)>=S_QUOTE)
83*4887Schin #define isbracechar(c)	((c)==RBRACE || (_c_=sh_lexstates[ST_BRACE][c])==S_MOD1 ||_c_==S_MOD2)
84*4887Schin #define ltos(x)		fmtbase((long)(x),0,0)
85*4887Schin 
86*4887Schin /* type of macro expansions */
87*4887Schin #define M_BRACE		1	/* ${var}	*/
88*4887Schin #define M_TREE		2	/* ${var.}	*/
89*4887Schin #define M_SIZE		3	/* ${#var}	*/
90*4887Schin #define M_VNAME		4	/* ${!var}	*/
91*4887Schin #define M_SUBNAME	5	/* ${!var[sub]}	*/
92*4887Schin #define M_NAMESCAN	6	/* ${!var*}	*/
93*4887Schin #define M_NAMECOUNT	7	/* ${#var*}	*/
94*4887Schin #define M_TYPE		8	/* ${@var}	*/
95*4887Schin 
96*4887Schin static int	substring(const char*, const char*, int[], int);
97*4887Schin static void	copyto(Mac_t*, int, int);
98*4887Schin static void	comsubst(Mac_t*,int);
99*4887Schin static int	varsub(Mac_t*);
100*4887Schin static void	mac_copy(Mac_t*,const char*, int);
101*4887Schin static void	tilde_expand2(int);
102*4887Schin static char 	*sh_tilde(const char*);
103*4887Schin static char	*special(int);
104*4887Schin static void	endfield(Mac_t*,int);
105*4887Schin static void	mac_error(Namval_t*);
106*4887Schin static char	*mac_getstring(char*);
107*4887Schin static int	charlen(const char*,int);
108*4887Schin #if SHOPT_MULTIBYTE
109*4887Schin     static char	*lastchar(const char*,const char*);
110*4887Schin #endif /* SHOPT_MULTIBYTE */
111*4887Schin 
112*4887Schin void *sh_macopen(Shell_t *shp)
113*4887Schin {
114*4887Schin 	void *addr = newof(0,Mac_t,1,0);
115*4887Schin 	Mac_t *mp = (Mac_t*)addr;
116*4887Schin 	mp->shp = shp;
117*4887Schin 	return(addr);
118*4887Schin }
119*4887Schin 
120*4887Schin /*
121*4887Schin  * perform only parameter substitution and catch failures
122*4887Schin  */
123*4887Schin char *sh_mactry(register char *string)
124*4887Schin {
125*4887Schin 	if(string)
126*4887Schin 	{
127*4887Schin 		int		jmp_val;
128*4887Schin 		int		savexit = sh.savexit;
129*4887Schin 		struct checkpt	buff;
130*4887Schin 		sh_pushcontext(&buff,SH_JMPSUB);
131*4887Schin 		jmp_val = sigsetjmp(buff.buff,0);
132*4887Schin 		if(jmp_val == 0)
133*4887Schin 			string = sh_mactrim(string,0);
134*4887Schin 		sh_popcontext(&buff);
135*4887Schin 		sh.savexit = savexit;
136*4887Schin 		return(string);
137*4887Schin 	}
138*4887Schin 	return("");
139*4887Schin }
140*4887Schin 
141*4887Schin /*
142*4887Schin  * Perform parameter expansion, command substitution, and arithmetic
143*4887Schin  * expansion on <str>.
144*4887Schin  * If <mode> greater than 1 file expansion is performed if the result
145*4887Schin  * yields a single pathname.
146*4887Schin  * If <mode> negative, than expansion rules for assignment are applied.
147*4887Schin  */
148*4887Schin char *sh_mactrim(char *str, register int mode)
149*4887Schin {
150*4887Schin 	register Mac_t *mp = (Mac_t*)sh.mac_context;
151*4887Schin 	Mac_t	savemac;
152*4887Schin 	savemac = *mp;
153*4887Schin 	stakseek(0);
154*4887Schin 	mp->arith = (mode==3);
155*4887Schin 	mp->let = 0;
156*4887Schin 	sh.argaddr = 0;
157*4887Schin 	mp->pattern = (mode==1||mode==2);
158*4887Schin 	mp->patfound = 0;
159*4887Schin 	mp->assign = (mode<0);
160*4887Schin 	mp->quoted = mp->lit = mp->split = mp->quote = 0;
161*4887Schin 	mp->sp = 0;
162*4887Schin 	if(mp->ifsp=nv_getval(nv_scoped(IFSNOD)))
163*4887Schin 		mp->ifs = *mp->ifsp;
164*4887Schin 	else
165*4887Schin 		mp->ifs = ' ';
166*4887Schin 	stakseek(0);
167*4887Schin 	fcsopen(str);
168*4887Schin 	copyto(mp,0,mp->arith);
169*4887Schin 	str = stakfreeze(1);
170*4887Schin 	if(mode==2)
171*4887Schin 	{
172*4887Schin 		/* expand only if unique */
173*4887Schin 		struct argnod *arglist=0;
174*4887Schin 		if((mode=path_expand(str,&arglist))==1)
175*4887Schin 			str = arglist->argval;
176*4887Schin 		else if(mode>1)
177*4887Schin 			errormsg(SH_DICT,ERROR_exit(1),e_ambiguous,str);
178*4887Schin 		sh_trim(str);
179*4887Schin 	}
180*4887Schin 	*mp = savemac;
181*4887Schin 	return(str);
182*4887Schin }
183*4887Schin 
184*4887Schin /*
185*4887Schin  * Perform all the expansions on the argument <argp>
186*4887Schin  */
187*4887Schin int sh_macexpand(register struct argnod *argp, struct argnod **arghead,int flag)
188*4887Schin {
189*4887Schin 	register int flags = argp->argflag;
190*4887Schin 	register char *str = argp->argval;
191*4887Schin 	register Mac_t  *mp = (Mac_t*)sh.mac_context;
192*4887Schin 	char **saveargaddr = sh.argaddr;
193*4887Schin 	Mac_t savemac;
194*4887Schin 	savemac = *mp;
195*4887Schin 	mp->sp = 0;
196*4887Schin 	if(mp->ifsp=nv_getval(nv_scoped(IFSNOD)))
197*4887Schin 		mp->ifs = *mp->ifsp;
198*4887Schin 	else
199*4887Schin 		mp->ifs = ' ';
200*4887Schin 	if(flag&ARG_OPTIMIZE)
201*4887Schin 		sh.argaddr = (char**)&argp->argchn.ap;
202*4887Schin 	else
203*4887Schin 		sh.argaddr = 0;
204*4887Schin 	mp->arghead = arghead;
205*4887Schin 	mp->quoted = mp->lit = mp->quote = 0;
206*4887Schin 	mp->arith = ((flag&ARG_ARITH)!=0);
207*4887Schin 	mp->let = ((flag&ARG_LET)!=0);
208*4887Schin 	mp->split = !(flag&ARG_ASSIGN);
209*4887Schin 	mp->assign = !mp->split;
210*4887Schin 	mp->pattern = mp->split && !(flag&ARG_NOGLOB) && !sh_isoption(SH_NOGLOB);
211*4887Schin 	str = argp->argval;
212*4887Schin 	fcsopen(str);
213*4887Schin 	mp->fields = 0;
214*4887Schin 	if(!arghead)
215*4887Schin 	{
216*4887Schin 		mp->split = 0;
217*4887Schin 		mp->pattern = ((flag&ARG_EXP)!=0);
218*4887Schin 		stakseek(0);
219*4887Schin 	}
220*4887Schin 	else
221*4887Schin 	{
222*4887Schin 		stakseek(ARGVAL);
223*4887Schin 		*stakptr(ARGVAL-1) = 0;
224*4887Schin 	}
225*4887Schin 	mp->patfound = 0;
226*4887Schin 	copyto(mp,0,mp->arith);
227*4887Schin 	if(!arghead)
228*4887Schin 	{
229*4887Schin 		argp->argchn.cp = stakfreeze(1);
230*4887Schin 		if(sh.argaddr)
231*4887Schin 			argp->argflag |= ARG_MAKE;
232*4887Schin 	}
233*4887Schin 	else
234*4887Schin 	{
235*4887Schin 		endfield(mp,mp->quoted);
236*4887Schin 		flags = mp->fields;
237*4887Schin 		if(flags==1 && sh.argaddr)
238*4887Schin 			argp->argchn.ap = *arghead;
239*4887Schin 	}
240*4887Schin 	sh.argaddr = saveargaddr;
241*4887Schin 	*mp = savemac;
242*4887Schin 	return(flags);
243*4887Schin }
244*4887Schin 
245*4887Schin /*
246*4887Schin  * Expand here document which is stored in <infile> or <string>
247*4887Schin  * The result is written to <outfile>
248*4887Schin  */
249*4887Schin void sh_machere(Sfio_t *infile, Sfio_t *outfile, char *string)
250*4887Schin {
251*4887Schin 	register int	c,n;
252*4887Schin 	register const char	*state = sh_lexstates[ST_QUOTE];
253*4887Schin 	register char	*cp;
254*4887Schin 	register Mac_t	*mp = (Mac_t*)sh.mac_context;
255*4887Schin 	Fcin_t		save;
256*4887Schin 	Mac_t		savemac;
257*4887Schin 	savemac = *mp;
258*4887Schin 	stakseek(0);
259*4887Schin 	sh.argaddr = 0;
260*4887Schin 	mp->sp = outfile;
261*4887Schin 	mp->split = mp->assign = mp->pattern = mp->patfound = mp->lit = mp->arith = mp->let = 0;
262*4887Schin 	mp->quote = 1;
263*4887Schin 	mp->ifsp = nv_getval(nv_scoped(IFSNOD));
264*4887Schin 	mp->ifs = ' ';
265*4887Schin 	fcsave(&save);
266*4887Schin 	if(infile)
267*4887Schin 		fcfopen(infile);
268*4887Schin 	else
269*4887Schin 		fcsopen(string);
270*4887Schin 	fcnotify(0);
271*4887Schin 	cp = fcseek(0);
272*4887Schin 	while(1)
273*4887Schin 	{
274*4887Schin #if SHOPT_MULTIBYTE
275*4887Schin 		if(mbwide())
276*4887Schin 		{
277*4887Schin 			do
278*4887Schin 			{
279*4887Schin 				ssize_t len;
280*4887Schin 				switch(len = mbsize(cp))
281*4887Schin 				{
282*4887Schin 				    case -1:	/* illegal multi-byte char */
283*4887Schin 				    case 0:
284*4887Schin 				    case 1:
285*4887Schin 					n=state[*(unsigned char*)cp++];
286*4887Schin 					break;
287*4887Schin 				    default:
288*4887Schin 					/* use state of alpah character */
289*4887Schin 					n=state['a'];
290*4887Schin 					cp += len;
291*4887Schin 				}
292*4887Schin 			}
293*4887Schin 			while(n == 0);
294*4887Schin 		}
295*4887Schin 		else
296*4887Schin #endif /* SHOPT_MULTIBYTE */
297*4887Schin 		while((n=state[*(unsigned char*)cp++])==0);
298*4887Schin 		if(n==S_NL || n==S_QUOTE || n==S_RBRA)
299*4887Schin 			continue;
300*4887Schin 		if(c=(cp-1)-fcseek(0))
301*4887Schin 			sfwrite(outfile,fcseek(0),c);
302*4887Schin 		cp = fcseek(c+1);
303*4887Schin 		switch(n)
304*4887Schin 		{
305*4887Schin 		    case S_EOF:
306*4887Schin 			if((n=fcfill()) <=0)
307*4887Schin 			{
308*4887Schin 				/* ignore 0 byte when reading from file */
309*4887Schin 				if(n==0 && fcfile())
310*4887Schin 					continue;
311*4887Schin 				fcrestore(&save);
312*4887Schin 				*mp = savemac;
313*4887Schin 				return;
314*4887Schin 			}
315*4887Schin 			cp = fcseek(-1);
316*4887Schin 			continue;
317*4887Schin 		    case S_ESC:
318*4887Schin 			fcgetc(c);
319*4887Schin 			cp=fcseek(-1);
320*4887Schin 			if(c>0)
321*4887Schin 				cp++;
322*4887Schin 			if(!isescchar(state[c]))
323*4887Schin 				sfputc(outfile,ESCAPE);
324*4887Schin 			continue;
325*4887Schin 		    case S_GRAVE:
326*4887Schin 			comsubst(mp,0);
327*4887Schin 			break;
328*4887Schin 		    case S_DOL:
329*4887Schin 			c = fcget();
330*4887Schin 			if(c=='.')
331*4887Schin 				goto regular;
332*4887Schin 		    again:
333*4887Schin 			switch(n=sh_lexstates[ST_DOL][c])
334*4887Schin 			{
335*4887Schin 			    case S_ALP: case S_SPC1: case S_SPC2:
336*4887Schin 			    case S_DIG: case S_LBRA:
337*4887Schin 			    {
338*4887Schin 				Fcin_t	save2;
339*4887Schin 				int	offset = staktell();
340*4887Schin 				int	offset2;
341*4887Schin 				stakputc(c);
342*4887Schin 				if(n==S_LBRA)
343*4887Schin 					sh_lexskip(RBRACE,1,ST_BRACE);
344*4887Schin 				else if(n==S_ALP)
345*4887Schin 				{
346*4887Schin 					while(fcgetc(c),isaname(c))
347*4887Schin 						stakputc(c);
348*4887Schin 					fcseek(-1);
349*4887Schin 				}
350*4887Schin 				stakputc(0);
351*4887Schin 				offset2 = staktell();
352*4887Schin 				fcsave(&save2);
353*4887Schin 				fcsopen(stakptr(offset));
354*4887Schin 				varsub(mp);
355*4887Schin 				if(c=staktell()-offset2)
356*4887Schin 					sfwrite(outfile,(char*)stakptr(offset2),c);
357*4887Schin 				fcrestore(&save2);
358*4887Schin 				stakseek(offset);
359*4887Schin 				break;
360*4887Schin 			    }
361*4887Schin 			    case S_PAR:
362*4887Schin 				comsubst(mp,1);
363*4887Schin 				break;
364*4887Schin 			    case S_EOF:
365*4887Schin 				if((c=fcfill()) > 0)
366*4887Schin 					goto again;
367*4887Schin 				/* FALL THRU */
368*4887Schin 			    default:
369*4887Schin 			    regular:
370*4887Schin 				sfputc(outfile,'$');
371*4887Schin 				fcseek(-1);
372*4887Schin 				break;
373*4887Schin 			}
374*4887Schin 		}
375*4887Schin 		cp = fcseek(0);
376*4887Schin 	}
377*4887Schin }
378*4887Schin 
379*4887Schin /*
380*4887Schin  * expand argument but do not trim pattern characters
381*4887Schin  */
382*4887Schin char *sh_macpat(register struct argnod *arg, int flags)
383*4887Schin {
384*4887Schin 	register char *sp = arg->argval;
385*4887Schin 	if((arg->argflag&ARG_RAW))
386*4887Schin 		return(sp);
387*4887Schin 	if(flags&ARG_OPTIMIZE)
388*4887Schin 		arg->argchn.ap=0;
389*4887Schin 	if(!(sp=arg->argchn.cp))
390*4887Schin 	{
391*4887Schin 		sh_macexpand(arg,NIL(struct argnod**),flags);
392*4887Schin 		sp = arg->argchn.cp;
393*4887Schin 		if(!(flags&ARG_OPTIMIZE) || !(arg->argflag&ARG_MAKE))
394*4887Schin 			arg->argchn.cp = 0;
395*4887Schin 		arg->argflag &= ~ARG_MAKE;
396*4887Schin 	}
397*4887Schin 	else
398*4887Schin 		sh.optcount++;
399*4887Schin 	return(sp);
400*4887Schin }
401*4887Schin 
402*4887Schin /*
403*4887Schin  * Process the characters up to <endch> or end of input string
404*4887Schin  */
405*4887Schin static void copyto(register Mac_t *mp,int endch, int newquote)
406*4887Schin {
407*4887Schin 	register int	c,n;
408*4887Schin 	register const char	*state = sh_lexstates[ST_MACRO];
409*4887Schin 	register char	*cp,*first;
410*4887Schin 	int		tilde = -1;
411*4887Schin 	int		oldquote = mp->quote;
412*4887Schin 	int		ansi_c = 0;
413*4887Schin 	int		paren = 0;
414*4887Schin 	int		ere = 0;
415*4887Schin 	int		brace = 0;
416*4887Schin 	Sfio_t		*sp = mp->sp;
417*4887Schin 	mp->sp = NIL(Sfio_t*);
418*4887Schin 	mp->quote = newquote;
419*4887Schin 	first = cp = fcseek(0);
420*4887Schin 	if(!mp->quote && *cp=='~')
421*4887Schin 		tilde = staktell();
422*4887Schin 	/* handle // operator specially */
423*4887Schin 	if(mp->pattern==2 && *cp=='/')
424*4887Schin 		cp++;
425*4887Schin 	while(1)
426*4887Schin 	{
427*4887Schin #if SHOPT_MULTIBYTE
428*4887Schin 		if(mbwide())
429*4887Schin 		{
430*4887Schin 			ssize_t len;
431*4887Schin 			do
432*4887Schin 			{
433*4887Schin 				switch(len = mbsize(cp))
434*4887Schin 				{
435*4887Schin 				    case -1:	/* illegal multi-byte char */
436*4887Schin 				    case 0:
437*4887Schin 					len = 1;
438*4887Schin 				    case 1:
439*4887Schin 					n = state[*(unsigned char*)cp++];
440*4887Schin 					break;
441*4887Schin 				    default:
442*4887Schin 					/* treat as if alpha */
443*4887Schin 					cp += len;
444*4887Schin 					n=state['a'];
445*4887Schin 				}
446*4887Schin 			}
447*4887Schin 			while(n == 0);
448*4887Schin 			c = (cp-len) - first;
449*4887Schin 		}
450*4887Schin 		else
451*4887Schin #endif /* SHOPT_MULTIBYTE */
452*4887Schin 		{
453*4887Schin 			while((n=state[*(unsigned char*)cp++])==0);
454*4887Schin 			c = (cp-1) - first;
455*4887Schin 		}
456*4887Schin 		switch(n)
457*4887Schin 		{
458*4887Schin 		    case S_ESC:
459*4887Schin 			if(ansi_c)
460*4887Schin 			{
461*4887Schin 				/* process ANSI-C escape character */
462*4887Schin 				char *addr= --cp;
463*4887Schin 				if(c)
464*4887Schin 					stakwrite(first,c);
465*4887Schin 				c = chresc(cp,&addr);
466*4887Schin 				cp = addr;
467*4887Schin 				first = fcseek(cp-first);
468*4887Schin #if SHOPT_MULTIBYTE
469*4887Schin 				if(c > UCHAR_MAX && mbwide())
470*4887Schin 				{
471*4887Schin 					int		i;
472*4887Schin 					unsigned char	mb[8];
473*4887Schin 
474*4887Schin 					n = wctomb((char*)mb, c);
475*4887Schin 					for(i=0;i<n;i++)
476*4887Schin 						stakputc(mb[i]);
477*4887Schin 				}
478*4887Schin 				else
479*4887Schin #endif /* SHOPT_MULTIBYTE */
480*4887Schin 				stakputc(c);
481*4887Schin 				if(c==ESCAPE && mp->pattern)
482*4887Schin 					stakputc(ESCAPE);
483*4887Schin 				break;
484*4887Schin 			}
485*4887Schin 			else if(sh_isoption(SH_BRACEEXPAND) && mp->pattern==4 && (*cp==',' || *cp==LBRACE || *cp==RBRACE || *cp=='.'))
486*4887Schin 				break;
487*4887Schin 			else if(mp->split && endch && !mp->quote && !mp->lit)
488*4887Schin 			{
489*4887Schin 				if(c)
490*4887Schin 					mac_copy(mp,first,c);
491*4887Schin 				cp = fcseek(c+2);
492*4887Schin 				if(c= cp[-1])
493*4887Schin 				{
494*4887Schin 					stakputc(c);
495*4887Schin 					if(c==ESCAPE)
496*4887Schin 						stakputc(ESCAPE);
497*4887Schin 				}
498*4887Schin 				else
499*4887Schin 					cp--;
500*4887Schin 				first = cp;
501*4887Schin 				break;
502*4887Schin 			}
503*4887Schin 			n = state[*(unsigned char*)cp];
504*4887Schin 			if(n==S_ENDCH && *cp!=endch)
505*4887Schin 				n = S_PAT;
506*4887Schin 			if(mp->pattern)
507*4887Schin 			{
508*4887Schin 				/* preserve \digit for pattern matching */
509*4887Schin 				/* also \alpha for extended patterns */
510*4887Schin 				if(!mp->lit && !mp->quote && (n==S_DIG || ((paren+ere) && sh_lexstates[ST_DOL][*(unsigned char*)cp]==S_ALP)))
511*4887Schin 					break;
512*4887Schin 				/* followed by file expansion */
513*4887Schin 				if(!mp->lit && (n==S_ESC || (!mp->quote &&
514*4887Schin 					(n==S_PAT||n==S_ENDCH||n==S_SLASH||n==S_BRACT||*cp=='-'))))
515*4887Schin 				{
516*4887Schin 					cp += (n!=S_EOF);
517*4887Schin 					break;
518*4887Schin 				}
519*4887Schin 				if(mp->lit || (mp->quote && !isqescchar(n) && n!=S_ENDCH))
520*4887Schin 				{
521*4887Schin 					/* add \ for file expansion */
522*4887Schin 					stakwrite(first,c+1);
523*4887Schin 					first = fcseek(c);
524*4887Schin 					break;
525*4887Schin 				}
526*4887Schin 			}
527*4887Schin 			if(mp->lit)
528*4887Schin 				break;
529*4887Schin 			if(!mp->quote || isqescchar(n) || n==S_ENDCH)
530*4887Schin 			{
531*4887Schin 				/* eliminate \ */
532*4887Schin 				if(c)
533*4887Schin 					stakwrite(first,c);
534*4887Schin 				/* check new-line joining */
535*4887Schin 				first = fcseek(c+1);
536*4887Schin 			}
537*4887Schin 			cp += (n!=S_EOF);
538*4887Schin 			break;
539*4887Schin 		    case S_GRAVE: case S_DOL:
540*4887Schin 			if(mp->lit)
541*4887Schin 				break;
542*4887Schin 			if(c)
543*4887Schin 			{
544*4887Schin 				if(mp->split && !mp->quote && endch)
545*4887Schin 					mac_copy(mp,first,c);
546*4887Schin 				else
547*4887Schin 					stakwrite(first,c);
548*4887Schin 			}
549*4887Schin 			first = fcseek(c+1);
550*4887Schin 			c = mp->pattern;
551*4887Schin 			if(n==S_GRAVE)
552*4887Schin 				comsubst(mp,0);
553*4887Schin 			else if((n= *cp)==0 || !varsub(mp))
554*4887Schin 			{
555*4887Schin 				if(n=='\'' && !mp->quote)
556*4887Schin 					ansi_c = 1;
557*4887Schin 				else if(mp->quote || n!='"')
558*4887Schin 					stakputc('$');
559*4887Schin 			}
560*4887Schin 			cp = first = fcseek(0);
561*4887Schin 			if(*cp)
562*4887Schin 				mp->pattern = c;
563*4887Schin 			break;
564*4887Schin 		    case S_ENDCH:
565*4887Schin 			if((mp->lit || cp[-1]!=endch || mp->quote!=newquote))
566*4887Schin 				goto pattern;
567*4887Schin 			if(endch==RBRACE && *cp==LPAREN && mp->pattern && brace)
568*4887Schin 				goto pattern;
569*4887Schin 		    case S_EOF:
570*4887Schin 			if(c)
571*4887Schin 			{
572*4887Schin 				if(mp->split && !mp->quote && !mp->lit && endch)
573*4887Schin 					mac_copy(mp,first,c);
574*4887Schin 				else
575*4887Schin 					stakwrite(first,c);
576*4887Schin 			}
577*4887Schin 			c += (n!=S_EOF);
578*4887Schin 			first = fcseek(c);
579*4887Schin 			if(tilde>=0)
580*4887Schin 				tilde_expand2(tilde);
581*4887Schin 			goto done;
582*4887Schin 		    case S_QUOTE:
583*4887Schin 			if(mp->lit || mp->arith)
584*4887Schin 				break;
585*4887Schin 		    case S_LIT:
586*4887Schin 			if(mp->arith)
587*4887Schin 			{
588*4887Schin 				if((*cp=='`' || *cp=='[') && cp[1]=='\'')
589*4887Schin 					cp +=2;
590*4887Schin 				break;
591*4887Schin 			}
592*4887Schin 			if(n==S_LIT && mp->quote)
593*4887Schin 				break;
594*4887Schin 			if(c)
595*4887Schin 			{
596*4887Schin 				if(mp->split && endch && !mp->quote && !mp->lit)
597*4887Schin 					mac_copy(mp,first,c);
598*4887Schin 				else
599*4887Schin 					stakwrite(first,c);
600*4887Schin 			}
601*4887Schin 			first = fcseek(c+1);
602*4887Schin 			if(n==S_LIT)
603*4887Schin 			{
604*4887Schin 				if(mp->quote)
605*4887Schin 					continue;
606*4887Schin 				if(mp->lit)
607*4887Schin 					mp->lit = ansi_c = 0;
608*4887Schin 				else
609*4887Schin 					mp->lit = 1;
610*4887Schin 			}
611*4887Schin 			else
612*4887Schin 				mp->quote = !mp->quote;
613*4887Schin 			mp->quoted++;
614*4887Schin 			break;
615*4887Schin 		    case S_BRACT:
616*4887Schin 			if(mp->arith || ((mp->assign==1 || endch==RBRACT) &&
617*4887Schin 				!(mp->quote || mp->lit)))
618*4887Schin 			{
619*4887Schin 				int offset=0,oldpat = mp->pattern;
620*4887Schin 				int oldarith = mp->arith;
621*4887Schin 				stakwrite(first,++c);
622*4887Schin 				if(mp->assign==1 && first[c-2]=='.')
623*4887Schin 					offset = staktell();
624*4887Schin 				first = fcseek(c);
625*4887Schin 				mp->pattern = 4;
626*4887Schin 				mp->arith = 0;
627*4887Schin 				copyto(mp,RBRACT,0);
628*4887Schin 				mp->arith = oldarith;
629*4887Schin 				mp->pattern = oldpat;
630*4887Schin 				stakputc(RBRACT);
631*4887Schin 				if(offset)
632*4887Schin 				{
633*4887Schin 					cp = stakptr(staktell());
634*4887Schin 					if(sh_checkid(stakptr(offset),cp)!=cp)
635*4887Schin 						stakseek(staktell()-2);
636*4887Schin 				}
637*4887Schin 				cp = first = fcseek(0);
638*4887Schin 				break;
639*4887Schin 			}
640*4887Schin 		    case S_PAT:
641*4887Schin 			if(mp->pattern && !(mp->quote || mp->lit))
642*4887Schin 			{
643*4887Schin 				mp->patfound = mp->pattern;
644*4887Schin 				if((n=cp[-1])==LPAREN)
645*4887Schin 				{
646*4887Schin 					paren++;
647*4887Schin 					if((cp-first)>1 && cp[-2]=='~')
648*4887Schin 					{
649*4887Schin 						char *p = cp;
650*4887Schin 						while((c=mbchar(p)) && c!=RPAREN && c!='E');
651*4887Schin 						ere = c=='E';
652*4887Schin 					}
653*4887Schin 				}
654*4887Schin 				else if(n==RPAREN)
655*4887Schin 					--paren;
656*4887Schin 			}
657*4887Schin 			goto pattern;
658*4887Schin 		    case S_BRACE:
659*4887Schin 			if(!(mp->quote || mp->lit))
660*4887Schin 			{
661*4887Schin 				mp->patfound = mp->split && sh_isoption(SH_BRACEEXPAND);
662*4887Schin 				brace = 1;
663*4887Schin 			}
664*4887Schin 		    pattern:
665*4887Schin 			if(!mp->pattern || !(mp->quote || mp->lit))
666*4887Schin 			{
667*4887Schin 				/* mark beginning of {a,b} */
668*4887Schin 				if(n==S_BRACE && endch==0 && mp->pattern)
669*4887Schin 					mp->pattern=4;
670*4887Schin 				if(n==S_SLASH && mp->pattern==2)
671*4887Schin 					mp->pattern=3;
672*4887Schin 				break;
673*4887Schin 			}
674*4887Schin 			if(mp->pattern==3)
675*4887Schin 				break;
676*4887Schin 			if(c)
677*4887Schin 				stakwrite(first,c);
678*4887Schin 			first = fcseek(c);
679*4887Schin 			stakputc(ESCAPE);
680*4887Schin 			break;
681*4887Schin 		    case S_EQ:
682*4887Schin 			if(mp->assign==1)
683*4887Schin 			{
684*4887Schin 				if(*cp=='~' && !endch && !mp->quote && !mp->lit)
685*4887Schin 					tilde = staktell()+(c+1);
686*4887Schin 				mp->assign = 2;
687*4887Schin 			}
688*4887Schin 			break;
689*4887Schin 		    case S_SLASH:
690*4887Schin 		    case S_COLON:
691*4887Schin 			if(tilde >=0)
692*4887Schin 			{
693*4887Schin 				if(c)
694*4887Schin 					stakwrite(first,c);
695*4887Schin 				first = fcseek(c);
696*4887Schin 				tilde_expand2(tilde);
697*4887Schin 				tilde = -1;
698*4887Schin 				c=0;
699*4887Schin 			}
700*4887Schin 			if(n==S_COLON && mp->assign==2 && *cp=='~' && endch==0 && !mp->quote &&!mp->lit)
701*4887Schin 				tilde = staktell()+(c+1);
702*4887Schin 			else if(n==S_SLASH && mp->pattern==2)
703*4887Schin #if 0
704*4887Schin 				goto pattern;
705*4887Schin #else
706*4887Schin 			{
707*4887Schin 				if(mp->quote || mp->lit)
708*4887Schin 					goto pattern;
709*4887Schin 				stakwrite(first,c+1);
710*4887Schin 				first = fcseek(c+1);
711*4887Schin 				c = staktell();
712*4887Schin 				sh_lexskip(RBRACE,0,ST_NESTED);
713*4887Schin 				stakseek(c);
714*4887Schin 				cp = fcseek(-1);
715*4887Schin 				stakwrite(first,cp-first);
716*4887Schin 				first=cp;
717*4887Schin 			}
718*4887Schin #endif
719*4887Schin 			break;
720*4887Schin 		}
721*4887Schin 	}
722*4887Schin done:
723*4887Schin 	mp->sp = sp;
724*4887Schin 	mp->quote = oldquote;
725*4887Schin }
726*4887Schin 
727*4887Schin /*
728*4887Schin  * copy <str> to stack performing sub-expression substitutions
729*4887Schin  */
730*4887Schin static void mac_substitute(Mac_t *mp, register char *cp,char *str,register int subexp[],int subsize)
731*4887Schin {
732*4887Schin 	register int c,n;
733*4887Schin #if 0
734*4887Schin 	register char *first=cp;
735*4887Schin #else
736*4887Schin 	register char *first=fcseek(0);
737*4887Schin 	char *ptr;
738*4887Schin 	Mac_t savemac;
739*4887Schin 	n = staktell();
740*4887Schin 	savemac = *mp;
741*4887Schin 	mp->pattern = 3;
742*4887Schin 	mp->split = 0;
743*4887Schin 	fcsopen(cp);
744*4887Schin 	copyto(mp,0,0);
745*4887Schin 	stakputc(0);
746*4887Schin 	ptr = cp = strdup(stakptr(n));
747*4887Schin 	stakseek(n);
748*4887Schin 	*mp = savemac;
749*4887Schin 	fcsopen(first);
750*4887Schin 	first = cp;
751*4887Schin #endif
752*4887Schin 	while(1)
753*4887Schin 	{
754*4887Schin 		while((c= *cp++) && c!=ESCAPE);
755*4887Schin 		if(c==0)
756*4887Schin 			break;
757*4887Schin 		if((n= *cp++)=='\\' || n==RBRACE || (n>='0' && n<='9' && (n-='0')<subsize))
758*4887Schin 		{
759*4887Schin 			c = cp-first-2;
760*4887Schin 			if(c)
761*4887Schin 				mac_copy(mp,first,c);
762*4887Schin 			first=cp;
763*4887Schin 			if(n=='\\' || n==RBRACE)
764*4887Schin 			{
765*4887Schin 				first--;
766*4887Schin 				continue;
767*4887Schin 			}
768*4887Schin 			if((c=subexp[2*n])>=0)
769*4887Schin 			{
770*4887Schin 				if((n=subexp[2*n+1]-c)>0)
771*4887Schin 					mac_copy(mp,str+c,n);
772*4887Schin 			}
773*4887Schin 		}
774*4887Schin 		else if(n==0)
775*4887Schin 			break;
776*4887Schin 	}
777*4887Schin 	if(n=cp-first-1)
778*4887Schin 		mac_copy(mp,first,n);
779*4887Schin #if 1
780*4887Schin 	free(ptr);
781*4887Schin #endif
782*4887Schin }
783*4887Schin 
784*4887Schin #if  SHOPT_FILESCAN
785*4887Schin #define	MAX_OFFSETS	 (sizeof(shp->offsets)/sizeof(shp->offsets[0]))
786*4887Schin #define MAX_ARGN	(32*1024)
787*4887Schin 
788*4887Schin /*
789*4887Schin  * compute the arguments $1 ... $n and $# from the current line as needed
790*4887Schin  * save line offsets in the offsets array.
791*4887Schin  */
792*4887Schin static char *getdolarg(Shell_t *shp, int n, int *size)
793*4887Schin {
794*4887Schin 	register int c=S_DELIM, d=shp->ifstable['\\'];
795*4887Schin 	register unsigned char *first,*last,*cp = (unsigned char*)shp->cur_line;
796*4887Schin 	register int m=shp->offsets[0],delim=0;
797*4887Schin 	if(m==0)
798*4887Schin 		return(0);
799*4887Schin 	if(m<0)
800*4887Schin 		m = 0;
801*4887Schin 	else if(n<=m)
802*4887Schin 		m = n-1;
803*4887Schin 	else
804*4887Schin 		m--;
805*4887Schin 	if(m >= MAX_OFFSETS-1)
806*4887Schin 		m =  MAX_OFFSETS-2;
807*4887Schin 	cp += shp->offsets[m+1];
808*4887Schin 	n -= m;
809*4887Schin 	shp->ifstable['\\'] = 0;
810*4887Schin 	shp->ifstable[0] = S_EOF;
811*4887Schin 	while(1)
812*4887Schin 	{
813*4887Schin 		if(c==S_DELIM)
814*4887Schin 			while(shp->ifstable[*cp++]==S_SPACE);
815*4887Schin 		first = --cp;
816*4887Schin 		if(++m < MAX_OFFSETS)
817*4887Schin 			shp->offsets[m] = (first-(unsigned char*)shp->cur_line);
818*4887Schin 		while((c=shp->ifstable[*cp++])==0);
819*4887Schin 		last = cp-1;
820*4887Schin 		if(c==S_SPACE)
821*4887Schin 			while((c=shp->ifstable[*cp++])==S_SPACE);
822*4887Schin 		if(--n==0 || c==S_EOF)
823*4887Schin 		{
824*4887Schin 			if(last==first && c==S_EOF && (!delim || (m>1)))
825*4887Schin 			{
826*4887Schin 				n++;
827*4887Schin 				m--;
828*4887Schin 			}
829*4887Schin 			break;
830*4887Schin 		}
831*4887Schin 		delim = (c==S_DELIM);
832*4887Schin 	}
833*4887Schin 	shp->ifstable['\\'] = d;
834*4887Schin 	if(m > shp->offsets[0])
835*4887Schin 		shp->offsets[0] = m;
836*4887Schin 	if(n)
837*4887Schin 		first = last = 0;
838*4887Schin 	if(size)
839*4887Schin 		*size = last-first;
840*4887Schin 	return((char*)first);
841*4887Schin }
842*4887Schin #endif /* SHOPT_FILESCAN */
843*4887Schin 
844*4887Schin /*
845*4887Schin  * get the prefix after name reference resolution
846*4887Schin  */
847*4887Schin static char *prefix(char *id)
848*4887Schin {
849*4887Schin 	Namval_t *np;
850*4887Schin 	register char *cp = strchr(id,'.');
851*4887Schin 	if(cp)
852*4887Schin 	{
853*4887Schin 		*cp = 0;
854*4887Schin 		np = nv_search(id, sh.var_tree,0);
855*4887Schin 		*cp = '.';
856*4887Schin 		if(isastchar(cp[1]))
857*4887Schin 			cp[1] = 0;
858*4887Schin 		if(np && nv_isref(np))
859*4887Schin 		{
860*4887Schin 			int n;
861*4887Schin 			char *sp;
862*4887Schin 			sh.argaddr = 0;
863*4887Schin 			while(nv_isref(np))
864*4887Schin 				np = nv_refnode(np);
865*4887Schin 			id = (char*)malloc(strlen(cp)+1+(n=strlen(sp=nv_name(np)))+1);
866*4887Schin 			strcpy(&id[n],cp);
867*4887Schin 			memcpy(id,sp,n);
868*4887Schin 			return(id);
869*4887Schin 		}
870*4887Schin 	}
871*4887Schin 	return(strdup(id));
872*4887Schin }
873*4887Schin 
874*4887Schin /*
875*4887Schin  * copy to ']' onto the stack and return offset to it
876*4887Schin  */
877*4887Schin static int subcopy(Mac_t *mp, int flag)
878*4887Schin {
879*4887Schin 	int split = mp->split;
880*4887Schin 	int xpattern = mp->pattern;
881*4887Schin 	int loc = staktell();
882*4887Schin 	int xarith = mp->arith;
883*4887Schin 	mp->split = 0;
884*4887Schin 	mp->arith = 0;
885*4887Schin 	mp->pattern = flag?4:0;
886*4887Schin 	copyto(mp,RBRACT,0);
887*4887Schin 	mp->pattern = xpattern;
888*4887Schin 	mp->split = split;
889*4887Schin 	mp->arith = xarith;
890*4887Schin 	return(loc);
891*4887Schin }
892*4887Schin 
893*4887Schin static int namecount(Mac_t *mp,const char *prefix)
894*4887Schin {
895*4887Schin 	int count = 0;
896*4887Schin 	mp->nvwalk = nv_diropen(prefix);
897*4887Schin 	while(nv_dirnext(mp->nvwalk))
898*4887Schin 		count++;
899*4887Schin 	nv_dirclose(mp->nvwalk);
900*4887Schin 	return(count);
901*4887Schin }
902*4887Schin 
903*4887Schin static char *nextname(Mac_t *mp,const char *prefix, int len)
904*4887Schin {
905*4887Schin 	char *cp;
906*4887Schin 	if(len==0)
907*4887Schin 	{
908*4887Schin 		mp->nvwalk = nv_diropen(prefix);
909*4887Schin 		return((char*)mp->nvwalk);
910*4887Schin 	}
911*4887Schin 	if(!(cp=nv_dirnext(mp->nvwalk)))
912*4887Schin 		nv_dirclose(mp->nvwalk);
913*4887Schin 	return(cp);
914*4887Schin }
915*4887Schin 
916*4887Schin /*
917*4887Schin  * This routine handles $param,  ${parm}, and ${param op word}
918*4887Schin  * The input stream is assumed to be a string
919*4887Schin  */
920*4887Schin static int varsub(Mac_t *mp)
921*4887Schin {
922*4887Schin 	register int	c;
923*4887Schin 	register int	type=0; /* M_xxx */
924*4887Schin 	register char	*v,*argp=0;
925*4887Schin 	register Namval_t	*np = NIL(Namval_t*);
926*4887Schin 	register int 	dolg=0, mode=0;
927*4887Schin 	Namarr_t	*ap=0;
928*4887Schin 	int		dolmax=0, vsize= -1, offset= -1, nulflg, replen=0, bysub=0;
929*4887Schin 	char		idbuff[3], *id = idbuff, *pattern=0, *repstr;
930*4887Schin 	int		oldpat=mp->pattern,idnum=0,flag=0,d;
931*4887Schin retry1:
932*4887Schin 	mp->zeros = 0;
933*4887Schin 	idbuff[0] = 0;
934*4887Schin 	idbuff[1] = 0;
935*4887Schin 	c = fcget();
936*4887Schin 	switch(c>0x7f?S_ALP:sh_lexstates[ST_DOL][c])
937*4887Schin 	{
938*4887Schin 	    case S_RBRA:
939*4887Schin 		if(type<M_SIZE)
940*4887Schin 			goto nosub;
941*4887Schin 		/* This code handles ${#} */
942*4887Schin 		c = mode;
943*4887Schin 		mode = type = 0;
944*4887Schin 		/* FALL THRU */
945*4887Schin 	    case S_SPC1:
946*4887Schin 		if(type==M_BRACE)
947*4887Schin 		{
948*4887Schin 			if(isaletter(mode=fcpeek(0)) || mode=='.')
949*4887Schin 			{
950*4887Schin 				if(c=='#')
951*4887Schin 					type = M_SIZE;
952*4887Schin #ifdef SHOPT_TYPEDEF
953*4887Schin 				else if(c=='@')
954*4887Schin 				{
955*4887Schin 					type = M_TYPE;
956*4887Schin 					goto retry1;
957*4887Schin 				}
958*4887Schin #endif /* SHOPT_TYPEDEF */
959*4887Schin 				else
960*4887Schin 					type = M_VNAME;
961*4887Schin 				mode = c;
962*4887Schin 				goto retry1;
963*4887Schin 			}
964*4887Schin 			else if(c=='#' && (isadigit(mode)||fcpeek(1)==RBRACE))
965*4887Schin 			{
966*4887Schin 				type = M_SIZE;
967*4887Schin 				mode = c;
968*4887Schin 				goto retry1;
969*4887Schin 			}
970*4887Schin 		}
971*4887Schin 		/* FALL THRU */
972*4887Schin 	    case S_SPC2:
973*4887Schin 		*id = c;
974*4887Schin 		v = special(c);
975*4887Schin 		if(isastchar(c))
976*4887Schin 		{
977*4887Schin 			mode = c;
978*4887Schin #if  SHOPT_FILESCAN
979*4887Schin 			if(sh.cur_line)
980*4887Schin 			{
981*4887Schin 				v = getdolarg(&sh,1,(int*)0);
982*4887Schin 				dolmax = MAX_ARGN;
983*4887Schin 			}
984*4887Schin 			else
985*4887Schin #endif  /* SHOPT_FILESCAN */
986*4887Schin 			dolmax = sh.st.dolc+1;
987*4887Schin 			dolg = (v!=0);
988*4887Schin 		}
989*4887Schin 		break;
990*4887Schin 	    case S_LBRA:
991*4887Schin 		if(type)
992*4887Schin 			goto nosub;
993*4887Schin 		type = M_BRACE;
994*4887Schin 		goto retry1;
995*4887Schin 	    case S_PAR:
996*4887Schin 		if(type)
997*4887Schin 			goto nosub;
998*4887Schin 		comsubst(mp,1);
999*4887Schin 		return(1);
1000*4887Schin 	    case S_DIG:
1001*4887Schin 		c -= '0';
1002*4887Schin 		sh.argaddr = 0;
1003*4887Schin 		if(type)
1004*4887Schin 		{
1005*4887Schin 			register int d;
1006*4887Schin 			while((d=fcget()),isadigit(d))
1007*4887Schin 				c = 10*c + (d-'0');
1008*4887Schin 			fcseek(-1);
1009*4887Schin 		}
1010*4887Schin 		idnum = c;
1011*4887Schin 		if(c==0)
1012*4887Schin 			v = special(c);
1013*4887Schin #if  SHOPT_FILESCAN
1014*4887Schin 		else if(sh.cur_line)
1015*4887Schin 		{
1016*4887Schin 			sh.used_pos = 1;
1017*4887Schin 			v = getdolarg(&sh,c,&vsize);
1018*4887Schin 		}
1019*4887Schin #endif  /* SHOPT_FILESCAN */
1020*4887Schin 		else if(c <= sh.st.dolc)
1021*4887Schin 		{
1022*4887Schin 			sh.used_pos = 1;
1023*4887Schin 			v = sh.st.dolv[c];
1024*4887Schin 		}
1025*4887Schin 		else
1026*4887Schin 			v = 0;
1027*4887Schin 		break;
1028*4887Schin 	    case S_ALP:
1029*4887Schin 		if(c=='.' && type==0)
1030*4887Schin 			goto nosub;
1031*4887Schin 		offset = staktell();
1032*4887Schin 		do
1033*4887Schin 		{
1034*4887Schin 			np = 0;
1035*4887Schin 			do
1036*4887Schin 				stakputc(c);
1037*4887Schin 			while(((c=fcget()),(c>0x7f||isaname(c)))||type && c=='.');
1038*4887Schin 			while(c==LBRACT && type)
1039*4887Schin 			{
1040*4887Schin 				sh.argaddr=0;
1041*4887Schin 				if((c=fcget(),isastchar(c)) && fcpeek(0)==RBRACT)
1042*4887Schin 				{
1043*4887Schin 					if(type==M_VNAME)
1044*4887Schin 						type = M_SUBNAME;
1045*4887Schin 					idbuff[0] = mode = c;
1046*4887Schin 					fcget();
1047*4887Schin 					c = fcget();
1048*4887Schin 					if(c=='.' || c==LBRACT)
1049*4887Schin 					{
1050*4887Schin 						stakputc(LBRACT);
1051*4887Schin 						stakputc(mode);
1052*4887Schin 						stakputc(RBRACT);
1053*4887Schin 					}
1054*4887Schin 					else
1055*4887Schin 						flag = NV_ARRAY;
1056*4887Schin 					break;
1057*4887Schin 				}
1058*4887Schin 				else
1059*4887Schin 				{
1060*4887Schin 					fcseek(-1);
1061*4887Schin 					if(type==M_VNAME)
1062*4887Schin 						type = M_SUBNAME;
1063*4887Schin 					stakputc(LBRACT);
1064*4887Schin 					v = stakptr(subcopy(mp,1));
1065*4887Schin 					stakputc(RBRACT);
1066*4887Schin 					c = fcget();
1067*4887Schin 				}
1068*4887Schin 			}
1069*4887Schin 		}
1070*4887Schin 		while(type && c=='.');
1071*4887Schin 		if(c==RBRACE && type &&  fcpeek(-2)=='.')
1072*4887Schin 		{
1073*4887Schin 			stakseek(staktell()-1);
1074*4887Schin 			type = M_TREE;
1075*4887Schin 		}
1076*4887Schin 		stakputc(0);
1077*4887Schin 		id=stakptr(offset);
1078*4887Schin 		if(isastchar(c) && type)
1079*4887Schin 		{
1080*4887Schin 			if(type==M_VNAME || type==M_SIZE)
1081*4887Schin 			{
1082*4887Schin 				idbuff[0] = mode = c;
1083*4887Schin 				if((d=fcpeek(0))==c)
1084*4887Schin 					idbuff[1] = fcget();
1085*4887Schin 				if(type==M_VNAME)
1086*4887Schin 					type = M_NAMESCAN;
1087*4887Schin 				else
1088*4887Schin 					type = M_NAMECOUNT;
1089*4887Schin 				break;
1090*4887Schin 			}
1091*4887Schin 			goto nosub;
1092*4887Schin 		}
1093*4887Schin 		flag |= NV_NOASSIGN|NV_VARNAME|NV_NOADD;
1094*4887Schin 		if(c=='=' || c=='?' || (c==':' && ((d=fcpeek(0))=='=' || d=='?')))
1095*4887Schin 			flag &= ~NV_NOADD;
1096*4887Schin #if  SHOPT_FILESCAN
1097*4887Schin 		if(sh.cur_line && *id=='R' && strcmp(id,"REPLY")==0)
1098*4887Schin 		{
1099*4887Schin 			sh.argaddr=0;
1100*4887Schin 			np = REPLYNOD;
1101*4887Schin 		}
1102*4887Schin 		else
1103*4887Schin #endif  /* SHOPT_FILESCAN */
1104*4887Schin 		if(sh.argaddr)
1105*4887Schin 			flag &= ~NV_NOADD;
1106*4887Schin 		np = nv_open(id,sh.var_tree,flag|NV_NOFAIL);
1107*4887Schin 		ap = np?nv_arrayptr(np):0;
1108*4887Schin 		if(type)
1109*4887Schin 		{
1110*4887Schin 			if(ap && isastchar(mode) && !(ap->nelem&ARRAY_SCAN))
1111*4887Schin 				nv_putsub(np,NIL(char*),ARRAY_SCAN);
1112*4887Schin 			if(!isbracechar(c))
1113*4887Schin 				goto nosub;
1114*4887Schin 			else
1115*4887Schin 				fcseek(-1);
1116*4887Schin 		}
1117*4887Schin 		else
1118*4887Schin 			fcseek(-1);
1119*4887Schin 		if((type==M_VNAME||type==M_SUBNAME)  && sh.argaddr && strcmp(nv_name(np),id))
1120*4887Schin 			sh.argaddr = 0;
1121*4887Schin 		c = (type>M_BRACE && isastchar(mode));
1122*4887Schin 		if(np && (!c || !ap))
1123*4887Schin 		{
1124*4887Schin 			if(type==M_VNAME)
1125*4887Schin 			{
1126*4887Schin 				type = M_BRACE;
1127*4887Schin 				v = nv_name(np);
1128*4887Schin 			}
1129*4887Schin #ifdef SHOPT_TYPEDEF
1130*4887Schin 			else if(type==M_TYPE)
1131*4887Schin 			{
1132*4887Schin #if 0
1133*4887Schin 				Namval_t *nq = nv_type(np);
1134*4887Schin #else
1135*4887Schin 				Namval_t *nq = 0;
1136*4887Schin #endif
1137*4887Schin 				type = M_BRACE;
1138*4887Schin 				if(nq)
1139*4887Schin 					v = nv_name(nq);
1140*4887Schin 				else
1141*4887Schin 				{
1142*4887Schin 					nv_attribute(np,sh.strbuf,"typeset",1);
1143*4887Schin 					v = sfstruse(sh.strbuf);
1144*4887Schin 				}
1145*4887Schin 			}
1146*4887Schin #endif /* SHOPT_TYPEDEF */
1147*4887Schin #if  SHOPT_FILESCAN
1148*4887Schin 			else if(sh.cur_line && np==REPLYNOD)
1149*4887Schin 				v = sh.cur_line;
1150*4887Schin #endif  /* SHOPT_FILESCAN */
1151*4887Schin 			else if(type==M_TREE)
1152*4887Schin 				v = nv_getvtree(np,(Namfun_t*)0);
1153*4887Schin 			else
1154*4887Schin 			{
1155*4887Schin 				v = nv_getval(np);
1156*4887Schin 				/* special case --- ignore leading zeros */
1157*4887Schin 				if( (mp->arith||mp->let) && (np->nvfun || nv_isattr(np,(NV_LJUST|NV_RJUST|NV_ZFILL))) && (offset==0 || !isalnum(*((unsigned char*)stakptr(offset-1)))))
1158*4887Schin 					mp->zeros = 1;
1159*4887Schin 			}
1160*4887Schin 		}
1161*4887Schin 		else
1162*4887Schin 			v = 0;
1163*4887Schin 		stakseek(offset);
1164*4887Schin 		if(ap)
1165*4887Schin 		{
1166*4887Schin #if SHOPT_OPTIMIZE
1167*4887Schin 			if(sh.argaddr)
1168*4887Schin 				nv_optimize(np);
1169*4887Schin #endif
1170*4887Schin 			if(isastchar(mode) && array_elem(ap)> !c)
1171*4887Schin 				dolg = -1;
1172*4887Schin 			else
1173*4887Schin 				dolg = 0;
1174*4887Schin 		}
1175*4887Schin 		break;
1176*4887Schin 	    case S_EOF:
1177*4887Schin 		fcseek(-1);
1178*4887Schin 	    default:
1179*4887Schin 		goto nosub;
1180*4887Schin 	}
1181*4887Schin 	c = fcget();
1182*4887Schin 	if(type>M_TREE)
1183*4887Schin 	{
1184*4887Schin 		if(c!=RBRACE)
1185*4887Schin 			mac_error(np);
1186*4887Schin 		if(type==M_NAMESCAN || type==M_NAMECOUNT)
1187*4887Schin 		{
1188*4887Schin 			id = prefix(id);
1189*4887Schin 			stakseek(offset);
1190*4887Schin 			if(type==M_NAMECOUNT)
1191*4887Schin 			{
1192*4887Schin 				c = namecount(mp,id);
1193*4887Schin 				v = ltos(c);
1194*4887Schin 			}
1195*4887Schin 			else
1196*4887Schin 			{
1197*4887Schin 				dolmax = strlen(id);
1198*4887Schin 				dolg = -1;
1199*4887Schin 				nextname(mp,id,0);
1200*4887Schin 				v = nextname(mp,id,dolmax);
1201*4887Schin 			}
1202*4887Schin 		}
1203*4887Schin 		else if(type==M_SUBNAME)
1204*4887Schin 		{
1205*4887Schin 			if(dolg<0)
1206*4887Schin 			{
1207*4887Schin 				v = nv_getsub(np);
1208*4887Schin 				bysub=1;
1209*4887Schin 			}
1210*4887Schin 			else if(v)
1211*4887Schin 			{
1212*4887Schin 				if(!ap || isastchar(mode))
1213*4887Schin 					v = "0";
1214*4887Schin 				else
1215*4887Schin 					v = nv_getsub(np);
1216*4887Schin 			}
1217*4887Schin 		}
1218*4887Schin 		else
1219*4887Schin 		{
1220*4887Schin 			if(!isastchar(mode))
1221*4887Schin 				c = charlen(v,vsize);
1222*4887Schin 			else if(dolg>0)
1223*4887Schin 			{
1224*4887Schin #if  SHOPT_FILESCAN
1225*4887Schin 				if(sh.cur_line)
1226*4887Schin 				{
1227*4887Schin 					getdolarg(&sh,MAX_ARGN,(int*)0);
1228*4887Schin 					c = sh.offsets[0];
1229*4887Schin 				}
1230*4887Schin 				else
1231*4887Schin #endif  /* SHOPT_FILESCAN */
1232*4887Schin 				c = sh.st.dolc;
1233*4887Schin 			}
1234*4887Schin 			else if(dolg<0)
1235*4887Schin 				c = array_elem(ap);
1236*4887Schin 			else
1237*4887Schin 				c = (v!=0);
1238*4887Schin 			dolg = dolmax = 0;
1239*4887Schin 			v = ltos(c);
1240*4887Schin 		}
1241*4887Schin 		c = RBRACE;
1242*4887Schin 	}
1243*4887Schin 	nulflg = 0;
1244*4887Schin 	if(type && c==':')
1245*4887Schin 	{
1246*4887Schin 		c = fcget();
1247*4887Schin 		if(sh_lexstates[ST_BRACE][c]==S_MOD1 && c!='*' && c!= ':')
1248*4887Schin 			nulflg=1;
1249*4887Schin 		else if(c!='%' && c!='#')
1250*4887Schin 		{
1251*4887Schin 			fcseek(-1);
1252*4887Schin 			c = ':';
1253*4887Schin 		}
1254*4887Schin 	}
1255*4887Schin 	if(type)
1256*4887Schin 	{
1257*4887Schin 		if(!isbracechar(c))
1258*4887Schin 		{
1259*4887Schin 			if(!nulflg)
1260*4887Schin 				mac_error(np);
1261*4887Schin 			fcseek(-1);
1262*4887Schin 			c = ':';
1263*4887Schin 		}
1264*4887Schin 		if(c!=RBRACE)
1265*4887Schin 		{
1266*4887Schin 			int newops = (c=='#' || c == '%' || c=='/');
1267*4887Schin 			offset = staktell();
1268*4887Schin 			if(c=='/' ||c==':' || ((!v || (nulflg && *v==0)) ^ (c=='+'||c=='#'||c=='%')))
1269*4887Schin 			{
1270*4887Schin 				int newquote = mp->quote;
1271*4887Schin 				int split = mp->split;
1272*4887Schin 				int quoted = mp->quoted;
1273*4887Schin 				int arith = mp->arith;
1274*4887Schin 				int zeros = mp->zeros;
1275*4887Schin 				if(newops)
1276*4887Schin 				{
1277*4887Schin 					type = fcget();
1278*4887Schin 					if(type=='%' || type=='#')
1279*4887Schin 					{
1280*4887Schin 						int d = fcget();
1281*4887Schin 						fcseek(-1);
1282*4887Schin 						if(d=='(')
1283*4887Schin 							type = 0;
1284*4887Schin 					}
1285*4887Schin 					fcseek(-1);
1286*4887Schin 					mp->pattern = 1+(c=='/');
1287*4887Schin 					mp->split = 0;
1288*4887Schin 					mp->quoted = 0;
1289*4887Schin 					mp->arith = mp->zeros = 0;
1290*4887Schin 					newquote = 0;
1291*4887Schin 				}
1292*4887Schin 				else if(c=='?' || c=='=')
1293*4887Schin 					mp->split = mp->pattern = 0;
1294*4887Schin 				copyto(mp,RBRACE,newquote);
1295*4887Schin 				if(!oldpat)
1296*4887Schin 					mp->patfound = 0;
1297*4887Schin 				mp->pattern = oldpat;
1298*4887Schin 				mp->split = split;
1299*4887Schin 				mp->quoted = quoted;
1300*4887Schin 				mp->arith = arith;
1301*4887Schin 				mp->zeros = zeros;
1302*4887Schin 				/* add null byte */
1303*4887Schin 				stakputc(0);
1304*4887Schin 				stakseek(staktell()-1);
1305*4887Schin 			}
1306*4887Schin 			else
1307*4887Schin 			{
1308*4887Schin 				sh_lexskip(RBRACE,0,(!newops&&mp->quote)?ST_QUOTE:ST_NESTED);
1309*4887Schin 				stakseek(offset);
1310*4887Schin 			}
1311*4887Schin 			argp=stakptr(offset);
1312*4887Schin 		}
1313*4887Schin 	}
1314*4887Schin 	else
1315*4887Schin 	{
1316*4887Schin 		fcseek(-1);
1317*4887Schin 		c=0;
1318*4887Schin 	}
1319*4887Schin 	if(c==':')  /* ${name:expr1[:expr2]} */
1320*4887Schin 	{
1321*4887Schin 		char *ptr;
1322*4887Schin 		type = (int)sh_strnum(argp,&ptr,1);
1323*4887Schin 		if(isastchar(mode))
1324*4887Schin 		{
1325*4887Schin 			if(id==idbuff)  /* ${@} or ${*} */
1326*4887Schin 			{
1327*4887Schin 				if(type<0 && (type+= dolmax)<0)
1328*4887Schin 					type = 0;
1329*4887Schin 				if(type==0)
1330*4887Schin 					v = special(dolg=0);
1331*4887Schin #if  SHOPT_FILESCAN
1332*4887Schin 				else if(sh.cur_line)
1333*4887Schin 				{
1334*4887Schin 					v = getdolarg(&sh,dolg=type,&vsize);
1335*4887Schin 					if(!v)
1336*4887Schin 						dolmax = type;
1337*4887Schin 				}
1338*4887Schin #endif  /* SHOPT_FILESCAN */
1339*4887Schin 				else if(type < dolmax)
1340*4887Schin 					v = sh.st.dolv[dolg=type];
1341*4887Schin 				else
1342*4887Schin 					v =  0;
1343*4887Schin 			}
1344*4887Schin 			else if(ap)
1345*4887Schin 			{
1346*4887Schin 				if(type<0)
1347*4887Schin 				{
1348*4887Schin 					if(array_assoc(ap))
1349*4887Schin 						type = -type;
1350*4887Schin 					else
1351*4887Schin 						type += array_maxindex(np);
1352*4887Schin 				}
1353*4887Schin 				if(array_assoc(ap))
1354*4887Schin 				{
1355*4887Schin 					while(type-- >0 && (v=0,nv_nextsub(np)))
1356*4887Schin 						v = nv_getval(np);
1357*4887Schin 				}
1358*4887Schin 				else if(type > 0)
1359*4887Schin 				{
1360*4887Schin 					if(nv_putsub(np,NIL(char*),type|ARRAY_SCAN))
1361*4887Schin 						v = nv_getval(np);
1362*4887Schin 					else
1363*4887Schin 						v = 0;
1364*4887Schin 				}
1365*4887Schin 			}
1366*4887Schin 			else if(type>0)
1367*4887Schin 				v = 0;
1368*4887Schin 		}
1369*4887Schin 		else if(v)
1370*4887Schin 		{
1371*4887Schin 			vsize = charlen(v,vsize);
1372*4887Schin 			if(type<0 && (type += vsize)<0)
1373*4887Schin 				type = 0;
1374*4887Schin 			if(vsize < type)
1375*4887Schin 				v = 0;
1376*4887Schin #if SHOPT_MULTIBYTE
1377*4887Schin 			else if(mbwide())
1378*4887Schin 			{
1379*4887Schin 				mbinit();
1380*4887Schin 				while(type-->0)
1381*4887Schin 				{
1382*4887Schin 					if((c=mbsize(v))<1)
1383*4887Schin 						c = 1;
1384*4887Schin 					v += c;
1385*4887Schin 				}
1386*4887Schin 				c = ':';
1387*4887Schin 			}
1388*4887Schin #endif /* SHOPT_MULTIBYTE */
1389*4887Schin 			else
1390*4887Schin 				v += type;
1391*4887Schin 			vsize -= type;
1392*4887Schin 		}
1393*4887Schin 		if(*ptr==':')
1394*4887Schin 		{
1395*4887Schin 			if((type = (int)sh_strnum(ptr+1,&ptr,1)) <=0)
1396*4887Schin 				v = 0;
1397*4887Schin 			else if(isastchar(mode))
1398*4887Schin 			{
1399*4887Schin 				if(dolg>=0)
1400*4887Schin 				{
1401*4887Schin 					if(dolg+type < dolmax)
1402*4887Schin 						dolmax = dolg+type;
1403*4887Schin 				}
1404*4887Schin 				else
1405*4887Schin 					dolmax = type;
1406*4887Schin 			}
1407*4887Schin 			else if(type < vsize)
1408*4887Schin 			{
1409*4887Schin #if SHOPT_MULTIBYTE
1410*4887Schin 				if(mbwide())
1411*4887Schin 				{
1412*4887Schin 					char *vp = v;
1413*4887Schin 					mbinit();
1414*4887Schin 					while(type-->0)
1415*4887Schin 					{
1416*4887Schin 						if((c=mbsize(vp))<1)
1417*4887Schin 							c = 1;
1418*4887Schin 						vp += c;
1419*4887Schin 					}
1420*4887Schin 					type = vp-v;
1421*4887Schin 					c = ':';
1422*4887Schin 				}
1423*4887Schin #endif /* SHOPT_MULTIBYTE */
1424*4887Schin 				vsize = type;
1425*4887Schin 			}
1426*4887Schin 		}
1427*4887Schin 		if(*ptr)
1428*4887Schin 			mac_error(np);
1429*4887Schin 		stakseek(offset);
1430*4887Schin 		argp = 0;
1431*4887Schin 	}
1432*4887Schin 	/* check for substring operations */
1433*4887Schin 	else if(c == '#' || c == '%' || c=='/')
1434*4887Schin 	{
1435*4887Schin 		if(c=='/')
1436*4887Schin 		{
1437*4887Schin 			if(type=='/' || type=='#' || type=='%')
1438*4887Schin 			{
1439*4887Schin 				c = type;
1440*4887Schin 				type = '/';
1441*4887Schin 				argp++;
1442*4887Schin 			}
1443*4887Schin 			else
1444*4887Schin 				type = 0;
1445*4887Schin 		}
1446*4887Schin 		else
1447*4887Schin 		{
1448*4887Schin 			if(type==c) /* ## or %% */
1449*4887Schin 				argp++;
1450*4887Schin 			else
1451*4887Schin 				type = 0;
1452*4887Schin 		}
1453*4887Schin 		pattern = strdup(argp);
1454*4887Schin 		if((type=='/' || c=='/') && (repstr = mac_getstring(pattern)))
1455*4887Schin 			replen = strlen(repstr);
1456*4887Schin 		if(v || c=='/' && offset>=0)
1457*4887Schin 			stakseek(offset);
1458*4887Schin 	}
1459*4887Schin 	/* check for quoted @ */
1460*4887Schin 	if(mode=='@' && mp->quote && !v && c!='-')
1461*4887Schin 		mp->quoted-=2;
1462*4887Schin retry2:
1463*4887Schin 	if(v && (!nulflg || *v ) && c!='+')
1464*4887Schin 	{
1465*4887Schin 		register int d = (mode=='@'?' ':mp->ifs);
1466*4887Schin 		int match[2*(MATCH_MAX+1)], nmatch, vsize_last;
1467*4887Schin 		char *vlast;
1468*4887Schin 		while(1)
1469*4887Schin 		{
1470*4887Schin 			if(!v)
1471*4887Schin 				v= "";
1472*4887Schin 			if(c=='/' || c=='#' || c== '%')
1473*4887Schin 			{
1474*4887Schin 				flag = (type || c=='/')?STR_GROUP|STR_MAXIMAL:STR_GROUP;
1475*4887Schin 				if(c!='/')
1476*4887Schin 					flag |= STR_LEFT;
1477*4887Schin 				while(1)
1478*4887Schin 				{
1479*4887Schin 					vsize = strlen(v);
1480*4887Schin 					if(c=='%')
1481*4887Schin 						nmatch=substring(v,pattern,match,flag&STR_MAXIMAL);
1482*4887Schin 					else
1483*4887Schin 						nmatch=strgrpmatch(v,pattern,match,elementsof(match)/2,flag);
1484*4887Schin 					if(replen>0)
1485*4887Schin 						sh_setmatch(v,vsize,nmatch,match);
1486*4887Schin 					if(nmatch)
1487*4887Schin 					{
1488*4887Schin 						vlast = v;
1489*4887Schin 						vsize_last = vsize;
1490*4887Schin 						vsize = match[0];
1491*4887Schin 					}
1492*4887Schin 					else if(c=='#')
1493*4887Schin 						vsize = 0;
1494*4887Schin 					if(vsize)
1495*4887Schin 						mac_copy(mp,v,vsize);
1496*4887Schin 					if(nmatch && replen>0)
1497*4887Schin 						mac_substitute(mp,repstr,v,match,nmatch);
1498*4887Schin 					if(nmatch==0)
1499*4887Schin 						v += vsize;
1500*4887Schin 					else
1501*4887Schin 						v += match[1];
1502*4887Schin 					if(*v &&  c=='/' && type)
1503*4887Schin 					{
1504*4887Schin 						/* avoid infinite loop */
1505*4887Schin 						if(nmatch && match[1]==0)
1506*4887Schin 							v++;
1507*4887Schin 						continue;
1508*4887Schin 					}
1509*4887Schin 					vsize = -1;
1510*4887Schin 					break;
1511*4887Schin 				}
1512*4887Schin 				if(replen==0)
1513*4887Schin 					sh_setmatch(vlast,vsize_last,nmatch,match);
1514*4887Schin 			}
1515*4887Schin 			if(vsize)
1516*4887Schin 				mac_copy(mp,v,vsize>0?vsize:strlen(v));
1517*4887Schin 			if(dolg==0 && dolmax==0)
1518*4887Schin 				 break;
1519*4887Schin 			if(dolg>=0)
1520*4887Schin 			{
1521*4887Schin 				if(++dolg >= dolmax)
1522*4887Schin 					break;
1523*4887Schin #if  SHOPT_FILESCAN
1524*4887Schin 				if(sh.cur_line)
1525*4887Schin 				{
1526*4887Schin 					if(dolmax==MAX_ARGN && isastchar(mode))
1527*4887Schin 						break;
1528*4887Schin 					if(!(v=getdolarg(&sh,dolg,&vsize)))
1529*4887Schin 					{
1530*4887Schin 						dolmax = dolg;
1531*4887Schin 						break;
1532*4887Schin 					}
1533*4887Schin 				}
1534*4887Schin 				else
1535*4887Schin #endif  /* SHOPT_FILESCAN */
1536*4887Schin 				v = sh.st.dolv[dolg];
1537*4887Schin 			}
1538*4887Schin 			else if(!np)
1539*4887Schin 			{
1540*4887Schin 				if(!(v = nextname(mp,id,dolmax)))
1541*4887Schin 					break;
1542*4887Schin 			}
1543*4887Schin 			else
1544*4887Schin 			{
1545*4887Schin 				if(dolmax &&  --dolmax <=0)
1546*4887Schin 				{
1547*4887Schin 					nv_putsub(np,NIL(char*),ARRAY_UNDEF);
1548*4887Schin 					break;
1549*4887Schin 				}
1550*4887Schin 				if(nv_nextsub(np) == 0)
1551*4887Schin 					break;
1552*4887Schin 				if(bysub)
1553*4887Schin 					v = nv_getsub(np);
1554*4887Schin 				else
1555*4887Schin 					v = nv_getval(np);
1556*4887Schin 			}
1557*4887Schin 			if(mp->split && (!mp->quote || mode=='@'))
1558*4887Schin 			{
1559*4887Schin 				if(!np)
1560*4887Schin 					mp->pattern = 0;
1561*4887Schin 				endfield(mp,mp->quoted);
1562*4887Schin 				mp->pattern = oldpat;
1563*4887Schin 			}
1564*4887Schin 			else if(d)
1565*4887Schin 			{
1566*4887Schin 				if(mp->sp)
1567*4887Schin 					sfputc(mp->sp,d);
1568*4887Schin 				else
1569*4887Schin 					stakputc(d);
1570*4887Schin 			}
1571*4887Schin 		}
1572*4887Schin 		if(pattern)
1573*4887Schin 			free((void*)pattern);
1574*4887Schin 	}
1575*4887Schin 	else if(argp)
1576*4887Schin 	{
1577*4887Schin 		if(c=='/' && replen>0 && pattern && strmatch("",pattern))
1578*4887Schin 			mac_substitute(mp,repstr,v,0,0);
1579*4887Schin 		if(c=='?')
1580*4887Schin 		{
1581*4887Schin 			if(np)
1582*4887Schin 				id = nv_name(np);
1583*4887Schin 			else if(idnum)
1584*4887Schin 				id = ltos(idnum);
1585*4887Schin 			if(*argp)
1586*4887Schin 			{
1587*4887Schin 				stakputc(0);
1588*4887Schin 				errormsg(SH_DICT,ERROR_exit(1),"%s: %s",id,argp);
1589*4887Schin 			}
1590*4887Schin 			else if(v)
1591*4887Schin 				errormsg(SH_DICT,ERROR_exit(1),e_nullset,id);
1592*4887Schin 			else
1593*4887Schin 				errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
1594*4887Schin 		}
1595*4887Schin 		else if(c=='=')
1596*4887Schin 		{
1597*4887Schin 			if(np)
1598*4887Schin 			{
1599*4887Schin 				if(sh.subshell)
1600*4887Schin 					np = sh_assignok(np,1);
1601*4887Schin 				nv_putval(np,argp,0);
1602*4887Schin 				v = nv_getval(np);
1603*4887Schin 				nulflg = 0;
1604*4887Schin 				stakseek(offset);
1605*4887Schin 				goto retry2;
1606*4887Schin 			}
1607*4887Schin 		else
1608*4887Schin 			mac_error(np);
1609*4887Schin 		}
1610*4887Schin 	}
1611*4887Schin 	else if(sh_isoption(SH_NOUNSET) && (!np  || nv_isnull(np) || (nv_isarray(np) && !np->nvalue.cp)))
1612*4887Schin 	{
1613*4887Schin 		if(np)
1614*4887Schin 		{
1615*4887Schin 			if(nv_isarray(np))
1616*4887Schin 			{
1617*4887Schin 				sfprintf(sh.strbuf,"%s[%s]\0",nv_name(np),nv_getsub(np));
1618*4887Schin 				id = nv_getsub(np);
1619*4887Schin 				id = sfstruse(sh.strbuf);
1620*4887Schin 			}
1621*4887Schin 			else
1622*4887Schin 				id = nv_name(np);
1623*4887Schin 			nv_close(np);
1624*4887Schin 		}
1625*4887Schin 		errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
1626*4887Schin 	}
1627*4887Schin 	if(np)
1628*4887Schin 		nv_close(np);
1629*4887Schin 	return(1);
1630*4887Schin nosub:
1631*4887Schin 	if(type)
1632*4887Schin 		mac_error(np);
1633*4887Schin 	fcseek(-1);
1634*4887Schin 	nv_close(np);
1635*4887Schin 	return(0);
1636*4887Schin }
1637*4887Schin 
1638*4887Schin /*
1639*4887Schin  * This routine handles command substitution
1640*4887Schin  * <type> is 0 for older `...` version
1641*4887Schin  */
1642*4887Schin static void comsubst(Mac_t *mp,int type)
1643*4887Schin {
1644*4887Schin 	Sfdouble_t		num;
1645*4887Schin 	register int		c;
1646*4887Schin 	register char		*str;
1647*4887Schin 	Sfio_t			*sp;
1648*4887Schin 	Fcin_t			save;
1649*4887Schin 	struct slnod            *saveslp = sh.st.staklist;
1650*4887Schin 	struct _mac_		savemac;
1651*4887Schin 	int			savtop = staktell();
1652*4887Schin 	char			lastc, *savptr = stakfreeze(0);
1653*4887Schin 	int			was_history = sh_isstate(SH_HISTORY);
1654*4887Schin 	int			was_verbose = sh_isstate(SH_VERBOSE);
1655*4887Schin 	int			newlines,bufsize;
1656*4887Schin 	register Shnode_t	*t;
1657*4887Schin 	Namval_t		*np;
1658*4887Schin 	sh.argaddr = 0;
1659*4887Schin 	savemac = *mp;
1660*4887Schin 	sh.st.staklist=0;
1661*4887Schin 	if(type)
1662*4887Schin 	{
1663*4887Schin 		sp = 0;
1664*4887Schin 		fcseek(-1);
1665*4887Schin 		t = sh_dolparen();
1666*4887Schin 		if(t && t->tre.tretyp==TARITH)
1667*4887Schin 		{
1668*4887Schin 			str =  t->ar.arexpr->argval;
1669*4887Schin 			fcsave(&save);
1670*4887Schin 			if(!(t->ar.arexpr->argflag&ARG_RAW))
1671*4887Schin 				str = sh_mactrim(str,3);
1672*4887Schin 			num = sh_arith(str);
1673*4887Schin 		out_offset:
1674*4887Schin 			stakset(savptr,savtop);
1675*4887Schin 			*mp = savemac;
1676*4887Schin 			if((Sflong_t)num==num)
1677*4887Schin 				sfprintf(sh.strbuf,"%lld",(Sflong_t)num);
1678*4887Schin 			else
1679*4887Schin 				sfprintf(sh.strbuf,"%.*Lg",LDBL_DIG,num);
1680*4887Schin 			str = sfstruse(sh.strbuf);
1681*4887Schin 			mac_copy(mp,str,strlen(str));
1682*4887Schin 			sh.st.staklist = saveslp;
1683*4887Schin 			fcrestore(&save);
1684*4887Schin 			return;
1685*4887Schin 		}
1686*4887Schin 	}
1687*4887Schin 	else
1688*4887Schin 	{
1689*4887Schin 		while(fcgetc(c)!='`' && c)
1690*4887Schin 		{
1691*4887Schin 			if(c==ESCAPE)
1692*4887Schin 			{
1693*4887Schin 				fcgetc(c);
1694*4887Schin 				if(!(isescchar(sh_lexstates[ST_QUOTE][c]) ||
1695*4887Schin 				  (c=='"' && mp->quote)) || (c=='$' && fcpeek(0)=='\''))
1696*4887Schin 					stakputc(ESCAPE);
1697*4887Schin 			}
1698*4887Schin 			stakputc(c);
1699*4887Schin 		}
1700*4887Schin 		c = staktell();
1701*4887Schin 		str=stakfreeze(1);
1702*4887Schin 		/* disable verbose and don't save in history file */
1703*4887Schin 		sh_offstate(SH_HISTORY);
1704*4887Schin 		sh_offstate(SH_VERBOSE);
1705*4887Schin 		if(mp->sp)
1706*4887Schin 			sfsync(mp->sp);	/* flush before executing command */
1707*4887Schin 		sp = sfnew(NIL(Sfio_t*),str,c,-1,SF_STRING|SF_READ);
1708*4887Schin 		c = sh.inlineno;
1709*4887Schin 		sh.inlineno = error_info.line+sh.st.firstline;
1710*4887Schin 		t = (Shnode_t*)sh_parse(mp->shp, sp,SH_EOF|SH_NL);
1711*4887Schin 		sh.inlineno = c;
1712*4887Schin 	}
1713*4887Schin #if KSHELL
1714*4887Schin 	if(t)
1715*4887Schin 	{
1716*4887Schin 		fcsave(&save);
1717*4887Schin 		sfclose(sp);
1718*4887Schin 		if(t->tre.tretyp==0 && !t->com.comarg)
1719*4887Schin 		{
1720*4887Schin 			/* special case $(<file) and $(<#file) */
1721*4887Schin 			register int fd;
1722*4887Schin 			int r;
1723*4887Schin 			struct checkpt buff;
1724*4887Schin 			struct ionod *ip=0;
1725*4887Schin 			sh_pushcontext(&buff,SH_JMPIO);
1726*4887Schin 			if((ip=t->tre.treio) &&
1727*4887Schin 				((ip->iofile&IOLSEEK) || !(ip->iofile&IOUFD)) &&
1728*4887Schin 				(r=sigsetjmp(buff.buff,0))==0)
1729*4887Schin 				fd = sh_redirect(ip,3);
1730*4887Schin 			else
1731*4887Schin 				fd = sh_chkopen(e_devnull);
1732*4887Schin 			sh_popcontext(&buff);
1733*4887Schin 			if(r==0 && ip && (ip->iofile&IOLSEEK))
1734*4887Schin 			{
1735*4887Schin 				if(sp=sh.sftable[fd])
1736*4887Schin 					num = sftell(sp);
1737*4887Schin 				else
1738*4887Schin 					num = lseek(fd, (off_t)0, SEEK_CUR);
1739*4887Schin 				goto out_offset;
1740*4887Schin 			}
1741*4887Schin 			sp = sfnew(NIL(Sfio_t*),(char*)malloc(IOBSIZE+1),IOBSIZE,fd,SF_READ|SF_MALLOC);
1742*4887Schin 		}
1743*4887Schin 		else
1744*4887Schin 			sp = sh_subshell(t,sh_isstate(SH_ERREXIT),1);
1745*4887Schin 		fcrestore(&save);
1746*4887Schin 	}
1747*4887Schin 	else
1748*4887Schin 		sp = sfopen(NIL(Sfio_t*),"","sr");
1749*4887Schin 	sh_freeup();
1750*4887Schin 	sh.st.staklist = saveslp;
1751*4887Schin 	if(was_history)
1752*4887Schin 		sh_onstate(SH_HISTORY);
1753*4887Schin 	if(was_verbose)
1754*4887Schin 		sh_onstate(SH_VERBOSE);
1755*4887Schin #else
1756*4887Schin 	sp = sfpopen(NIL(Sfio_t*),str,"r");
1757*4887Schin #endif
1758*4887Schin 	*mp = savemac;
1759*4887Schin 	np = nv_scoped(IFSNOD);
1760*4887Schin 	nv_putval(np,mp->ifsp,0);
1761*4887Schin 	mp->ifsp = nv_getval(np);
1762*4887Schin 	stakset(savptr,savtop);
1763*4887Schin 	newlines = 0;
1764*4887Schin 	lastc = 0;
1765*4887Schin 	sfsetbuf(sp,(void*)sp,0);
1766*4887Schin 	bufsize = sfvalue(sp);
1767*4887Schin 	/* read command substitution output and put on stack or here-doc */
1768*4887Schin 	sfpool(sp, NIL(Sfio_t*), SF_WRITE);
1769*4887Schin 	while((str=(char*)sfreserve(sp,SF_UNBOUND,0)) && (c = sfvalue(sp))>0)
1770*4887Schin 	{
1771*4887Schin #if SHOPT_CRNL
1772*4887Schin 		/* eliminate <cr> */
1773*4887Schin 		register char *dp;
1774*4887Schin 		char *buff = str;
1775*4887Schin 		while(c>1 && (*str !='\r'|| str[1]!='\n'))
1776*4887Schin 		{
1777*4887Schin 			c--;
1778*4887Schin 			str++;
1779*4887Schin 		}
1780*4887Schin 		dp = str;
1781*4887Schin 		while(c>1)
1782*4887Schin 		{
1783*4887Schin 			str++;
1784*4887Schin 			c--;
1785*4887Schin 			while(c>1 && (*str!='\r' || str[1]!='\n'))
1786*4887Schin 			{
1787*4887Schin 				c--;
1788*4887Schin 				*dp++ = *str++;
1789*4887Schin 			}
1790*4887Schin 		}
1791*4887Schin 		if(c)
1792*4887Schin 			*dp++ = *str++;
1793*4887Schin 		*dp = 0;
1794*4887Schin 		str = buff;
1795*4887Schin 		c = dp-str;
1796*4887Schin #endif /* SHOPT_CRNL */
1797*4887Schin 		if(newlines >0)
1798*4887Schin 		{
1799*4887Schin 			if(mp->sp)
1800*4887Schin 				sfnputc(mp->sp,'\n',newlines);
1801*4887Schin 			else if(!mp->quote && mp->split && sh.ifstable['\n'])
1802*4887Schin 				endfield(mp,0);
1803*4887Schin 			else	while(newlines--)
1804*4887Schin 					stakputc('\n');
1805*4887Schin 			newlines = 0;
1806*4887Schin 		}
1807*4887Schin 		else if(lastc)
1808*4887Schin 		{
1809*4887Schin 			mac_copy(mp,&lastc,1);
1810*4887Schin 			lastc = 0;
1811*4887Schin 		}
1812*4887Schin 		/* delay appending trailing new-lines */
1813*4887Schin 		while(str[--c]=='\n')
1814*4887Schin 			newlines++;
1815*4887Schin 		if(++c < bufsize)
1816*4887Schin 			str[c] = 0;
1817*4887Schin 		else
1818*4887Schin 		{
1819*4887Schin 			/* can't write past buffer so save last character */
1820*4887Schin 			lastc = str[--c];
1821*4887Schin 			str[c] = 0;
1822*4887Schin 		}
1823*4887Schin 		mac_copy(mp,str,c);
1824*4887Schin 	}
1825*4887Schin 	if(--newlines>0 && sh.ifstable['\n']==S_DELIM)
1826*4887Schin 	{
1827*4887Schin 		if(mp->sp)
1828*4887Schin 			sfnputc(mp->sp,'\n',newlines);
1829*4887Schin 		else if(!mp->quote && mp->split && sh.ifstable['\n'])
1830*4887Schin 			endfield(mp,0);
1831*4887Schin 		else	while(newlines--)
1832*4887Schin 				stakputc('\n');
1833*4887Schin 	}
1834*4887Schin 	if(lastc)
1835*4887Schin 		mac_copy(mp,&lastc,1);
1836*4887Schin 	sfclose(sp);
1837*4887Schin 	return;
1838*4887Schin }
1839*4887Schin 
1840*4887Schin /*
1841*4887Schin  * copy <str> onto the stack
1842*4887Schin  */
1843*4887Schin static void mac_copy(register Mac_t *mp,register const char *str, register int size)
1844*4887Schin {
1845*4887Schin 	register char		*state;
1846*4887Schin 	register const char	*cp=str;
1847*4887Schin 	register int		c,n,nopat;
1848*4887Schin 	nopat = (mp->quote||mp->assign==1||mp->arith);
1849*4887Schin 	if(mp->zeros)
1850*4887Schin 	{
1851*4887Schin 		/* prevent leading 0's from becomming octal constants */
1852*4887Schin 		while(size>1 && *str=='0')
1853*4887Schin 			str++,size--;
1854*4887Schin 		mp->zeros = 0;
1855*4887Schin 		cp = str;
1856*4887Schin 	}
1857*4887Schin 	if(mp->sp)
1858*4887Schin 		sfwrite(mp->sp,str,size);
1859*4887Schin 	else if(mp->pattern>=2 || (mp->pattern && nopat))
1860*4887Schin 	{
1861*4887Schin 		state = sh_lexstates[ST_MACRO];
1862*4887Schin 		/* insert \ before file expansion characters */
1863*4887Schin 		while(size-->0)
1864*4887Schin 		{
1865*4887Schin 			c = state[n= *(unsigned char*)cp++];
1866*4887Schin 			if(nopat&&(c==S_PAT||c==S_ESC||c==S_BRACT||c==S_ENDCH) && mp->pattern!=3)
1867*4887Schin 				c=1;
1868*4887Schin 			else if(mp->pattern==4 && (c==S_ESC||c==S_BRACT||c==S_ENDCH || isastchar(n)))
1869*4887Schin 				c=1;
1870*4887Schin 			else if(mp->pattern==2 && c==S_SLASH)
1871*4887Schin 				c=1;
1872*4887Schin 			else if(mp->pattern==3 && c==S_ESC && (state[*(unsigned char*)cp]==S_DIG||(*cp==ESCAPE)))
1873*4887Schin 			{
1874*4887Schin 				if(!(c=mp->quote))
1875*4887Schin 					cp++;
1876*4887Schin 			}
1877*4887Schin 			else
1878*4887Schin 				c=0;
1879*4887Schin 			if(c)
1880*4887Schin 			{
1881*4887Schin 				if(c = (cp-1) - str)
1882*4887Schin 					stakwrite(str,c);
1883*4887Schin 				stakputc(ESCAPE);
1884*4887Schin 				str = cp-1;
1885*4887Schin 			}
1886*4887Schin 		}
1887*4887Schin 		if(c = cp-str)
1888*4887Schin 			stakwrite(str,c);
1889*4887Schin 	}
1890*4887Schin 	else if(!mp->quote && mp->split && (mp->ifs||mp->pattern))
1891*4887Schin 	{
1892*4887Schin 		/* split words at ifs characters */
1893*4887Schin 		state = sh.ifstable;
1894*4887Schin 		if(mp->pattern)
1895*4887Schin 		{
1896*4887Schin 			char *sp = "&|()";
1897*4887Schin 			while(c = *sp++)
1898*4887Schin 			{
1899*4887Schin 				if(state[c]==0)
1900*4887Schin 					state[c] = S_EPAT;
1901*4887Schin 			}
1902*4887Schin 			sp = "*?[{";
1903*4887Schin 			while(c = *sp++)
1904*4887Schin 			{
1905*4887Schin 				if(state[c]==0)
1906*4887Schin 					state[c] = S_PAT;
1907*4887Schin 			}
1908*4887Schin 			if(state[ESCAPE]==0)
1909*4887Schin 				state[ESCAPE] = S_ESC;
1910*4887Schin 		}
1911*4887Schin 		while(size-->0)
1912*4887Schin 		{
1913*4887Schin 			if((n=state[c= *(unsigned char*)cp++])==S_ESC || n==S_EPAT)
1914*4887Schin 			{
1915*4887Schin 				/* don't allow extended patterns in this case */
1916*4887Schin 				mp->patfound = mp->pattern;
1917*4887Schin 				stakputc(ESCAPE);
1918*4887Schin 			}
1919*4887Schin 			else if(n==S_PAT)
1920*4887Schin 				mp->patfound = mp->pattern;
1921*4887Schin 			else if(n && mp->ifs)
1922*4887Schin 			{
1923*4887Schin #if SHOPT_MULTIBYTE
1924*4887Schin 				if(n==S_MBYTE)
1925*4887Schin 				{
1926*4887Schin 					if(sh_strchr(mp->ifsp,cp-1)<0)
1927*4887Schin 						continue;
1928*4887Schin 					n = mbsize(cp-1) - 1;
1929*4887Schin 					if(n==-2)
1930*4887Schin 						n = 0;
1931*4887Schin 					cp += n;
1932*4887Schin 					size -= n;
1933*4887Schin 					n= S_DELIM;
1934*4887Schin 				}
1935*4887Schin #endif /* SHOPT_MULTIBYTE */
1936*4887Schin 				if(n==S_SPACE || n==S_NL)
1937*4887Schin 				{
1938*4887Schin 					while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL))
1939*4887Schin 						size--;
1940*4887Schin #if SHOPT_MULTIBYTE
1941*4887Schin 					if(n==S_MBYTE && sh_strchr(mp->ifsp,cp-1)>=0)
1942*4887Schin 					{
1943*4887Schin 						n = mbsize(cp-1) - 1;
1944*4887Schin 						if(n==-2)
1945*4887Schin 							n = 0;
1946*4887Schin 						cp += n;
1947*4887Schin 						size -= n;
1948*4887Schin 						n=S_DELIM;
1949*4887Schin 					}
1950*4887Schin 					else
1951*4887Schin #endif /* SHOPT_MULTIBYTE */
1952*4887Schin 					if(n==S_DELIM)
1953*4887Schin 						size--;
1954*4887Schin 				}
1955*4887Schin 				endfield(mp,n==S_DELIM||mp->quoted);
1956*4887Schin 				mp->patfound = 0;
1957*4887Schin 				if(n==S_DELIM)
1958*4887Schin 					while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL))
1959*4887Schin 						size--;
1960*4887Schin 				if(size<=0)
1961*4887Schin 					break;
1962*4887Schin 				cp--;
1963*4887Schin 				continue;
1964*4887Schin 
1965*4887Schin 			}
1966*4887Schin 			stakputc(c);
1967*4887Schin 		}
1968*4887Schin 		if(mp->pattern)
1969*4887Schin 		{
1970*4887Schin 			cp = "&|()";
1971*4887Schin 			while(c = *cp++)
1972*4887Schin 			{
1973*4887Schin 				if(state[c]==S_EPAT)
1974*4887Schin 					state[c] = 0;
1975*4887Schin 			}
1976*4887Schin 			cp = "*?[{";
1977*4887Schin 			while(c = *cp++)
1978*4887Schin 			{
1979*4887Schin 				if(state[c]==S_PAT)
1980*4887Schin 					state[c] = 0;
1981*4887Schin 			}
1982*4887Schin 			if(sh.ifstable[ESCAPE]==S_ESC)
1983*4887Schin 				sh.ifstable[ESCAPE] = 0;
1984*4887Schin 		}
1985*4887Schin 	}
1986*4887Schin 	else
1987*4887Schin 		stakwrite(str,size);
1988*4887Schin }
1989*4887Schin 
1990*4887Schin /*
1991*4887Schin  * Terminate field.
1992*4887Schin  * If field is null count field if <split> is non-zero
1993*4887Schin  * Do filename expansion of required
1994*4887Schin  */
1995*4887Schin static void endfield(register Mac_t *mp,int split)
1996*4887Schin {
1997*4887Schin 	register struct argnod *argp;
1998*4887Schin 	register int count=0;
1999*4887Schin 	if(staktell() > ARGVAL || split)
2000*4887Schin 	{
2001*4887Schin 		argp = (struct argnod*)stakfreeze(1);
2002*4887Schin 		argp->argnxt.cp = 0;
2003*4887Schin 		argp->argflag = 0;
2004*4887Schin 		if(mp->patfound)
2005*4887Schin 		{
2006*4887Schin 			sh.argaddr = 0;
2007*4887Schin #if SHOPT_BRACEPAT
2008*4887Schin 			count = path_generate(argp,mp->arghead);
2009*4887Schin #else
2010*4887Schin 			count = path_expand(argp->argval,mp->arghead);
2011*4887Schin #endif /* SHOPT_BRACEPAT */
2012*4887Schin 			if(count)
2013*4887Schin 				mp->fields += count;
2014*4887Schin 			else if(split)	/* pattern is null string */
2015*4887Schin 				*argp->argval = 0;
2016*4887Schin 			else	/* pattern expands to nothing */
2017*4887Schin 				count = -1;
2018*4887Schin 		}
2019*4887Schin 		if(count==0)
2020*4887Schin 		{
2021*4887Schin 			argp->argchn.ap = *mp->arghead;
2022*4887Schin 			*mp->arghead = argp;
2023*4887Schin 			mp->fields++;
2024*4887Schin 		}
2025*4887Schin 		if(count>=0)
2026*4887Schin 		{
2027*4887Schin 			(*mp->arghead)->argflag |= ARG_MAKE;
2028*4887Schin 			if(mp->assign || sh_isoption(SH_NOGLOB))
2029*4887Schin 				argp->argflag |= ARG_RAW|ARG_EXP;
2030*4887Schin 		}
2031*4887Schin 		stakseek(ARGVAL);
2032*4887Schin 	}
2033*4887Schin 	mp->quoted = mp->quote;
2034*4887Schin }
2035*4887Schin 
2036*4887Schin /*
2037*4887Schin  * Finds the right substring of STRING using the expression PAT
2038*4887Schin  * the longest substring is found when FLAG is set.
2039*4887Schin  */
2040*4887Schin static int substring(register const char *string,const char *pat,int match[], int flag)
2041*4887Schin {
2042*4887Schin 	register const char *sp=string;
2043*4887Schin 	register int size,len,nmatch,n;
2044*4887Schin 	int smatch[2*(MATCH_MAX+1)];
2045*4887Schin 	if(flag)
2046*4887Schin 	{
2047*4887Schin 		if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_MAXIMAL))
2048*4887Schin 		{
2049*4887Schin 			memcpy(match,smatch,n*2*sizeof(smatch[0]));
2050*4887Schin 			return(n);
2051*4887Schin 		}
2052*4887Schin 		return(0);
2053*4887Schin 	}
2054*4887Schin 	size = len = strlen(sp);
2055*4887Schin 	sp += size;
2056*4887Schin 	while(sp>=string)
2057*4887Schin 	{
2058*4887Schin #if SHOPT_MULTIBYTE
2059*4887Schin 		if(mbwide())
2060*4887Schin 			sp = lastchar(string,sp);
2061*4887Schin #endif /* SHOPT_MULTIBYTE */
2062*4887Schin 		if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_LEFT|STR_MAXIMAL))
2063*4887Schin 		{
2064*4887Schin 			nmatch = n;
2065*4887Schin 			memcpy(match,smatch,n*2*sizeof(smatch[0]));
2066*4887Schin 			size = sp-string;
2067*4887Schin 			break;
2068*4887Schin 		}
2069*4887Schin 		sp--;
2070*4887Schin 	}
2071*4887Schin 	if(size==len)
2072*4887Schin 		return(0);
2073*4887Schin 	if(nmatch)
2074*4887Schin 	{
2075*4887Schin 		nmatch *=2;
2076*4887Schin 		while(--nmatch>=0)
2077*4887Schin 			match[nmatch] += size;
2078*4887Schin 	}
2079*4887Schin 	return(n);
2080*4887Schin }
2081*4887Schin 
2082*4887Schin #if SHOPT_MULTIBYTE
2083*4887Schin 	static char	*lastchar(const char *string, const char *endstring)
2084*4887Schin 	{
2085*4887Schin 		register char *str = (char*)string;
2086*4887Schin 		register int c;
2087*4887Schin 		mbinit();
2088*4887Schin 		while(*str)
2089*4887Schin 		{
2090*4887Schin 			if((c=mbsize(str))<0)
2091*4887Schin 				c = 1;
2092*4887Schin 			if(str+c > endstring)
2093*4887Schin 				break;
2094*4887Schin 			str += c;
2095*4887Schin 		}
2096*4887Schin 		return(str);
2097*4887Schin 	}
2098*4887Schin #endif /* SHOPT_MULTIBYTE */
2099*4887Schin static int	charlen(const char *string,int len)
2100*4887Schin {
2101*4887Schin 	if(!string)
2102*4887Schin 		return(0);
2103*4887Schin #if SHOPT_MULTIBYTE
2104*4887Schin 	if(mbwide())
2105*4887Schin 	{
2106*4887Schin 		register const char *str = string, *strmax=string+len;
2107*4887Schin 		register int n=0;
2108*4887Schin 		mbinit();
2109*4887Schin 		if(len>0)
2110*4887Schin 		{
2111*4887Schin 			while(str<strmax && mbchar(str))
2112*4887Schin 				n++;
2113*4887Schin 		}
2114*4887Schin 		else while(mbchar(str))
2115*4887Schin 			n++;
2116*4887Schin 		return(n);
2117*4887Schin 	}
2118*4887Schin 	else
2119*4887Schin #endif /* SHOPT_MULTIBYTE */
2120*4887Schin 	{
2121*4887Schin 		if(len<0)
2122*4887Schin 			return(strlen(string));
2123*4887Schin 		return(len);
2124*4887Schin 	}
2125*4887Schin }
2126*4887Schin 
2127*4887Schin /*
2128*4887Schin  * This is the default tilde discipline function
2129*4887Schin  */
2130*4887Schin static int sh_btilde(int argc, char *argv[], void *context)
2131*4887Schin {
2132*4887Schin 	char *cp = sh_tilde(argv[1]);
2133*4887Schin 	NOT_USED(argc);
2134*4887Schin 	NOT_USED(context);
2135*4887Schin 	if(!cp)
2136*4887Schin 		cp = argv[1];
2137*4887Schin 	sfputr(sfstdout, cp, '\n');
2138*4887Schin 	return(0);
2139*4887Schin }
2140*4887Schin 
2141*4887Schin /*
2142*4887Schin  * <offset> is byte offset for beginning of tilde string
2143*4887Schin  */
2144*4887Schin static void tilde_expand2(register int offset)
2145*4887Schin {
2146*4887Schin 	char		shtilde[10], *av[3], *ptr=stakfreeze(1);
2147*4887Schin 	Sfio_t		*iop, *save=sfstdout;
2148*4887Schin 	Namval_t	*np;
2149*4887Schin 	static int	beenhere=0;
2150*4887Schin 	strcpy(shtilde,".sh.tilde");
2151*4887Schin 	np = nv_open(shtilde,sh.fun_tree, NV_VARNAME|NV_NOARRAY|NV_NOASSIGN|NV_NOFAIL);
2152*4887Schin 	if(np && !beenhere)
2153*4887Schin 	{
2154*4887Schin 		beenhere = 1;
2155*4887Schin 		sh_addbuiltin(shtilde,sh_btilde,0);
2156*4887Schin 	}
2157*4887Schin 	av[0] = ".sh.tilde";
2158*4887Schin 	av[1] = &ptr[offset];
2159*4887Schin 	av[2] = 0;
2160*4887Schin 	iop = sftmp(IOBSIZE+1);;
2161*4887Schin 	sfset(iop,SF_READ,0);
2162*4887Schin 	sfstdout = iop;
2163*4887Schin 	if(np)
2164*4887Schin 		sh_fun(np, (Namval_t*)0, av);
2165*4887Schin 	else
2166*4887Schin 		sh_btilde(2, av, &sh);
2167*4887Schin 	sfstdout = save;
2168*4887Schin 	stakset(ptr, offset);
2169*4887Schin 	sfseek(iop,(Sfoff_t)0,SEEK_SET);
2170*4887Schin 	sfset(iop,SF_READ,1);
2171*4887Schin 	if(ptr = sfreserve(iop, SF_UNBOUND, -1))
2172*4887Schin 	{
2173*4887Schin 		Sfoff_t n = sfvalue(iop);
2174*4887Schin 		while(ptr[n-1]=='\n')
2175*4887Schin 			n--;
2176*4887Schin 		if(n==1 && fcpeek(0)=='/' && ptr[n-1])
2177*4887Schin 			n--;
2178*4887Schin 		if(n)
2179*4887Schin 			stakwrite(ptr,n);
2180*4887Schin 	}
2181*4887Schin 	else
2182*4887Schin 		stakputs(av[1]);
2183*4887Schin 	sfclose(iop);
2184*4887Schin }
2185*4887Schin 
2186*4887Schin /*
2187*4887Schin  * This routine is used to resolve ~ expansion.
2188*4887Schin  * A ~ by itself is replaced with the users login directory.
2189*4887Schin  * A ~- is replaced by the previous working directory in shell.
2190*4887Schin  * A ~+ is replaced by the present working directory in shell.
2191*4887Schin  * If ~name  is replaced with login directory of name.
2192*4887Schin  * If string doesn't start with ~ or ~... not found then 0 returned.
2193*4887Schin  */
2194*4887Schin 
2195*4887Schin static char *sh_tilde(register const char *string)
2196*4887Schin {
2197*4887Schin 	register char		*cp;
2198*4887Schin 	register int		c;
2199*4887Schin 	register struct passwd	*pw;
2200*4887Schin 	register Namval_t *np=0;
2201*4887Schin 	static Dt_t *logins_tree;
2202*4887Schin 	if(*string++!='~')
2203*4887Schin 		return(NIL(char*));
2204*4887Schin 	if((c = *string)==0)
2205*4887Schin 	{
2206*4887Schin 		if(!(cp=nv_getval(nv_scoped(HOME))))
2207*4887Schin 			cp = getlogin();
2208*4887Schin 		return(cp);
2209*4887Schin 	}
2210*4887Schin 	if((c=='-' || c=='+') && string[1]==0)
2211*4887Schin 	{
2212*4887Schin 		if(c=='+')
2213*4887Schin 			cp = nv_getval(nv_scoped(PWDNOD));
2214*4887Schin 		else
2215*4887Schin 			cp = nv_getval(nv_scoped(OLDPWDNOD));
2216*4887Schin 		return(cp);
2217*4887Schin 	}
2218*4887Schin 	if(logins_tree && (np=nv_search(string,logins_tree,0)))
2219*4887Schin 		return(nv_getval(np));
2220*4887Schin 	if(!(pw = getpwnam(string)))
2221*4887Schin 		return(NIL(char*));
2222*4887Schin 	if(!logins_tree)
2223*4887Schin 		logins_tree = dtopen(&_Nvdisc,Dtbag);
2224*4887Schin 	if(np=nv_search(string,logins_tree,NV_ADD))
2225*4887Schin 		nv_putval(np, pw->pw_dir,0);
2226*4887Schin 	return(pw->pw_dir);
2227*4887Schin }
2228*4887Schin 
2229*4887Schin /*
2230*4887Schin  * return values for special macros
2231*4887Schin  */
2232*4887Schin static char *special(register int c)
2233*4887Schin {
2234*4887Schin 	register Namval_t *np;
2235*4887Schin 	if(c!='$')
2236*4887Schin 		sh.argaddr = 0;
2237*4887Schin 	switch(c)
2238*4887Schin 	{
2239*4887Schin 	    case '@':
2240*4887Schin 	    case '*':
2241*4887Schin 		return(sh.st.dolc>0?sh.st.dolv[1]:NIL(char*));
2242*4887Schin 	    case '#':
2243*4887Schin #if  SHOPT_FILESCAN
2244*4887Schin 		if(sh.cur_line)
2245*4887Schin 		{
2246*4887Schin 			getdolarg(&sh,MAX_ARGN,(int*)0);
2247*4887Schin 			return(ltos(sh.offsets[0]));
2248*4887Schin 		}
2249*4887Schin #endif  /* SHOPT_FILESCAN */
2250*4887Schin 		return(ltos(sh.st.dolc));
2251*4887Schin 	    case '!':
2252*4887Schin 		if(sh.bckpid)
2253*4887Schin 			return(ltos(sh.bckpid));
2254*4887Schin 		break;
2255*4887Schin 	    case '$':
2256*4887Schin 		if(nv_isnull(SH_DOLLARNOD))
2257*4887Schin 			return(ltos(sh.pid));
2258*4887Schin 		return(nv_getval(SH_DOLLARNOD));
2259*4887Schin 	    case '-':
2260*4887Schin 		return(sh_argdolminus());
2261*4887Schin 	    case '?':
2262*4887Schin 		return(ltos(sh.savexit));
2263*4887Schin 	    case 0:
2264*4887Schin 		if(sh_isstate(SH_PROFILE) || !error_info.id || ((np=nv_search(error_info.id,sh.bltin_tree,0)) && nv_isattr(np,BLT_SPC)))
2265*4887Schin 			return(sh.shname);
2266*4887Schin 		else
2267*4887Schin 			return(error_info.id);
2268*4887Schin 	}
2269*4887Schin 	return(NIL(char*));
2270*4887Schin }
2271*4887Schin 
2272*4887Schin /*
2273*4887Schin  * Handle macro expansion errors
2274*4887Schin  */
2275*4887Schin static void mac_error(Namval_t *np)
2276*4887Schin {
2277*4887Schin 	if(np)
2278*4887Schin 		nv_close(np);
2279*4887Schin 	errormsg(SH_DICT,ERROR_exit(1),e_subst,fcfirst());
2280*4887Schin }
2281*4887Schin 
2282*4887Schin /*
2283*4887Schin  * Given pattern/string, replace / with 0 and return pointer to string
2284*4887Schin  * \ characters are stripped from string.
2285*4887Schin  */
2286*4887Schin static char *mac_getstring(char *pattern)
2287*4887Schin {
2288*4887Schin 	register char *cp = pattern;
2289*4887Schin 	register int c;
2290*4887Schin 	while(c = *cp++)
2291*4887Schin 	{
2292*4887Schin 		if(c==ESCAPE)
2293*4887Schin 			cp++;
2294*4887Schin 		else if(c=='/')
2295*4887Schin 		{
2296*4887Schin 			cp[-1] = 0;
2297*4887Schin 			return(cp);
2298*4887Schin 		}
2299*4887Schin 	}
2300*4887Schin 	return(NIL(char*));
2301*4887Schin }
2302