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