xref: /titanic_50/usr/src/lib/libshell/common/sh/name.c (revision 7257d1b4d25bfac0c802847390e98a464fd787ac)
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  * AT&T Labs
23  *
24  */
25 
26 #define putenv	___putenv
27 
28 #include	"defs.h"
29 #include	<ctype.h>
30 #include	"variables.h"
31 #include	"path.h"
32 #include	"lexstates.h"
33 #include	"timeout.h"
34 #include	"FEATURE/externs"
35 #include	"streval.h"
36 
37 static char	*savesub = 0;
38 
39 #if !_lib_pathnative && _lib_uwin_path
40 
41 #define _lib_pathnative		1
42 
43 extern int	uwin_path(const char*, char*, int);
44 
45 size_t
46 pathnative(const char* path, char* buf, size_t siz)
47 {
48 	return uwin_path(path, buf, siz);
49 }
50 
51 #endif /* _lib_pathnative */
52 
53 static void	attstore(Namval_t*,void*);
54 #ifndef _ENV_H
55 static void	pushnam(Namval_t*,void*);
56 static char	*staknam(Namval_t*, char*);
57 #endif
58 static void	ltou(const char*,char*);
59 static void	rightjust(char*, int, int);
60 
61 struct adata
62 {
63 	char    **argnam;
64 	int     attsize;
65 	char    *attval;
66 };
67 
68 char		nv_local = 0;
69 #ifndef _ENV_H
70 static void(*nullscan)(Namval_t*,void*);
71 #endif
72 
73 #if ( SFIO_VERSION  <= 20010201L )
74 #   define _data        data
75 #endif
76 
77 #if !SHOPT_MULTIBYTE
78 #   define mbchar(p)       (*(unsigned char*)p++)
79 #endif /* SHOPT_MULTIBYTE */
80 
81 /* ========	name value pair routines	======== */
82 
83 #include	"shnodes.h"
84 #include	"builtins.h"
85 
86 static char *getbuf(size_t len)
87 {
88 	static char *buf;
89 	static size_t buflen;
90 	if(buflen < len)
91 	{
92 		if(buflen==0)
93 			buf = (char*)malloc(len);
94 		else
95 			buf = (char*)realloc(buf,len);
96 		buflen = len;
97 	}
98 	return(buf);
99 }
100 
101 #ifdef _ENV_H
102 void sh_envput(Env_t* ep,Namval_t *np)
103 {
104 	int offset = staktell();
105 	Namarr_t *ap = nv_arrayptr(np);
106 	char *val;
107 	if(ap)
108 	{
109 		if(ap->nelem&ARRAY_UNDEF)
110 			nv_putsub(np,"0",0L);
111 		else if(!(val=nv_getsub(np)) || strcmp(val,"0"))
112 			return;
113 	}
114 	if(!(val = nv_getval(np)))
115 		return;
116 	stakputs(nv_name(np));
117 	stakputc('=');
118 	stakputs(val);
119 	stakseek(offset);
120 	env_add(ep,stakptr(offset),ENV_STRDUP);
121 }
122 #endif
123 
124 /*
125  * output variable name in format for re-input
126  */
127 void nv_outname(Sfio_t *out, char *name, int len)
128 {
129 	const char *cp=name, *sp;
130 	int c, offset = staktell();
131 	while(sp= strchr(cp,'['))
132 	{
133 		if(len>0 && cp+len <= sp)
134 			break;
135 		sfwrite(out,cp,++sp-cp);
136 		stakseek(offset);
137 		for(; c= *sp; sp++)
138 		{
139 			if(c==']')
140 				break;
141 			else if(c=='\\')
142 			{
143 				if(*sp=='[' || *sp==']' || *sp=='\\')
144 					c = *sp++;
145 			}
146 			stakputc(c);
147 		}
148 		stakputc(0);
149 		sfputr(out,sh_fmtq(stakptr(offset)),-1);
150 		if(len>0)
151 		{
152 			sfputc(out,']');
153 			return;
154 		}
155 		cp = sp;
156 	}
157 	if(*cp)
158 	{
159 		if(len>0)
160 			sfwrite(out,cp,len);
161 		else
162 			sfputr(out,cp,-1);
163 	}
164 	stakseek(offset);
165 }
166 
167 /*
168  * Perform parameter assignment for a linked list of parameters
169  * <flags> contains attributes for the parameters
170  */
171 void nv_setlist(register struct argnod *arg,register int flags)
172 {
173 	register char	*cp;
174 	register Namval_t *np;
175 	char		*trap=sh.st.trap[SH_DEBUGTRAP];
176 	int		traceon = (sh_isoption(SH_XTRACE)!=0);
177 	int		array = (flags&(NV_ARRAY|NV_IARRAY));
178 	flags &= ~(NV_TYPE|NV_ARRAY);
179 	if(sh_isoption(SH_ALLEXPORT))
180 		flags |= NV_EXPORT;
181 	if(sh.prefix)
182 	{
183 		flags &= ~(NV_IDENT|NV_EXPORT);
184 		flags |= NV_VARNAME;
185 	}
186 	for(;arg; arg=arg->argnxt.ap)
187 	{
188 		sh.used_pos = 0;
189 		if(arg->argflag&ARG_MAC)
190 			cp = sh_mactrim(arg->argval,-1);
191 		else
192 		{
193 			Namval_t *mp;
194 			stakseek(0);
195 			if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED)))
196 			{
197 				int flag = (NV_VARNAME|NV_ARRAY|NV_ASSIGN);
198 				struct fornod *fp=(struct fornod*)arg->argchn.ap;
199 				register Shnode_t *tp=fp->fortre;
200 				char *prefix = sh.prefix;
201 				flag |= (flags&NV_NOSCOPE);
202 				if(arg->argflag&ARG_QUOTED)
203 					cp = sh_mactrim(fp->fornam,-1);
204 				else
205 					cp = fp->fornam;
206 				error_info.line = fp->fortyp-sh.st.firstline;
207 				if(sh.fn_depth && (Namval_t*)tp->com.comnamp==SYSTYPESET)
208 			                flag |= NV_NOSCOPE;
209 				if(prefix && tp->com.comset && *cp=='[')
210 				{
211 					sh.prefix = 0;
212 					np = nv_open(prefix,sh.var_tree,flag);
213 					sh.prefix = prefix;
214 					if(np)
215 					{
216 						if(!nv_isarray(np))
217 						{
218 							stakputc('.');
219 							stakputs(cp);
220 							cp = stakfreeze(1);
221 						}
222 						nv_close(np);
223 					}
224 				}
225 				np = nv_open(cp,sh.var_tree,flag);
226 				if(array)
227 				{
228 					if(!(flags&NV_APPEND))
229 						nv_unset(np);
230 					if(array&NV_ARRAY)
231 					{
232 						nv_setarray(np,nv_associative);
233 					}
234 					else
235 					{
236 						nv_onattr(np,NV_ARRAY);
237 					}
238 				}
239 				/* check for array assignment */
240 				if(tp->tre.tretyp!=TLST && tp->com.comarg && !tp->com.comset && !((mp=tp->com.comnamp) && nv_isattr(mp,BLT_DCL)))
241 				{
242 					int argc;
243 					char **argv = sh_argbuild(&argc,&tp->com,0);
244 					if(!(arg->argflag&ARG_APPEND))
245 					{
246 						nv_unset(np);
247 					}
248 					nv_setvec(np,(arg->argflag&ARG_APPEND),argc,argv);
249 					if(traceon || trap)
250 					{
251 						int n = -1;
252 						char *name = nv_name(np);
253 						if(arg->argflag&ARG_APPEND)
254 							n = '+';
255 						if(trap)
256 							sh_debug(trap,name,(char*)0,argv,(arg->argflag&ARG_APPEND)|ARG_ASSIGN);
257 						if(traceon)
258 						{
259 							sh_trace(NIL(char**),0);
260 							sfputr(sfstderr,name,n);
261 							sfwrite(sfstderr,"=( ",3);
262 							while(cp= *argv++)
263 								sfputr(sfstderr,sh_fmtq(cp),' ');
264 							sfwrite(sfstderr,")\n",2);
265 						}
266 					}
267 					continue;
268 				}
269 				if(tp->tre.tretyp==TLST || !tp->com.comset || tp->com.comset->argval[0]!='[')
270 				{
271 					if(*cp!='.' && *cp!='[' && strchr(cp,'['))
272 					{
273 						nv_close(np);
274 						np = nv_open(cp,sh.var_tree,flag);
275 					}
276 					if((arg->argflag&ARG_APPEND) && !nv_isarray(np))
277 						nv_unset(np);
278 				}
279 				else
280 				{
281 					if(sh_isoption(SH_BASH) || (array&NV_IARRAY))
282 					{
283 						if(!(arg->argflag&ARG_APPEND))
284 							nv_unset(np);
285 					}
286 					else if((arg->argflag&ARG_APPEND) && (!nv_isarray(np) || (nv_aindex(np)>=0)))
287 					{
288 						nv_unset(np);
289 						nv_setarray(np,nv_associative);
290 					}
291 					else
292 						nv_setarray(np,nv_associative);
293 				}
294 				if(prefix)
295 					cp = stakcopy(nv_name(np));
296 				sh.prefix = cp;
297 				sh_exec(tp,sh_isstate(SH_ERREXIT));
298 				sh.prefix = prefix;
299 				if(nv_isarray(np) && (mp=nv_opensub(np)))
300 					np = mp;
301 				nv_setvtree(np);
302 				continue;
303 			}
304 			cp = arg->argval;
305 		}
306 		if(sh.prefix && *cp=='.' && cp[1]=='=')
307 			cp++;
308 		np = nv_open(cp,sh.var_tree,flags);
309 		if(!np->nvfun)
310 		{
311 			if(sh.used_pos)
312 				nv_onattr(np,NV_PARAM);
313 			else
314 				nv_offattr(np,NV_PARAM);
315 		}
316 		if(traceon || trap)
317 		{
318 			register char *sp=cp;
319 			char *name=nv_name(np);
320 			char *sub=0;
321 			int append = 0;
322 			if(nv_isarray(np))
323 				sub = savesub;
324 			if(cp=strchr(sp,'='))
325 			{
326 				if(cp[-1]=='+')
327 					append = ARG_APPEND;
328 				cp++;
329 			}
330 			if(traceon)
331 			{
332 				sh_trace(NIL(char**),0);
333 				nv_outname(sfstderr,name,-1);
334 				if(sub)
335 					sfprintf(sfstderr,"[%s]",sh_fmtq(sub));
336 				if(cp)
337 				{
338 					if(append)
339 						sfputc(sfstderr,'+');
340 					sfprintf(sfstderr,"=%s\n",sh_fmtq(cp));
341 				}
342 			}
343 			if(trap)
344 			{
345 					char *av[2];
346 					av[0] = cp;
347 					av[1] = 0;
348 					sh_debug(trap,name,sub,av,append);
349 			}
350 		}
351 	}
352 }
353 
354 /*
355  * copy the subscript onto the stack
356  */
357 static void stak_subscript(const char *sub, int last)
358 {
359 	register int c;
360 	stakputc('[');
361 	while(c= *sub++)
362 	{
363 		if(c=='[' || c==']' || c=='\\')
364 			stakputc('\\');
365 		stakputc(c);
366 	}
367 	stakputc(last);
368 }
369 
370 /*
371  * construct a new name from a prefix and base name on the stack
372  */
373 static char *copystack(const char *prefix, register const char *name, const char *sub)
374 {
375 	register int last=0,offset = staktell();
376 	if(prefix)
377 	{
378 		stakputs(prefix);
379 		if(*stakptr(staktell()-1)=='.')
380 			stakseek(staktell()-1);
381 		if(*name=='.' && name[1]=='[')
382 			last = staktell()+2;
383 		if(*name!='[' && *name!='.' && *name!='=' && *name!='+')
384 			stakputc('.');
385 	}
386 	if(last)
387 	{
388 		stakputs(name);
389 		if(sh_checkid(stakptr(last),(char*)0))
390 			stakseek(staktell()-2);
391 	}
392 	if(sub)
393 		stak_subscript(sub,']');
394 	if(!last)
395 		stakputs(name);
396 	stakputc(0);
397 	return(stakptr(offset));
398 }
399 
400 /*
401  * grow this stack string <name> by <n> bytes and move from cp-1 to end
402  * right by <n>.  Returns beginning of string on the stack
403  */
404 static char *stack_extend(const char *cname, char *cp, int n)
405 {
406 	register char *name = (char*)cname;
407 	int offset = name - stakptr(0);
408 	int m = cp-name;
409 	stakseek(strlen(name)+n+1);
410 	name = stakptr(offset);
411 	cp =  name + m;
412 	m = strlen(cp)+1;
413 	while(m-->0)
414 		cp[n+m]=cp[m];
415 	return((char*)name);
416 }
417 
418 Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp)
419 {
420 	char			*cp=(char*)name, *sp, *xp;
421 	register int		c;
422 	register Namval_t	*np=0, *nq=0;
423 	Namfun_t		*fp=0;
424 	long			mode, add=0;
425 	int			copy=1,isref,top=0,noscope=(flags&NV_NOSCOPE);
426 	if(root==sh.var_tree)
427 	{
428 		if(dtvnext(root))
429 			top = 1;
430 		else
431 			flags &= ~NV_NOSCOPE;
432 	}
433 	if(!dp->disc)
434 		copy = dp->nofree;
435 	if(*cp=='.')
436 		cp++;
437 	while(1)
438 	{
439 		switch(c = *(unsigned char*)(sp = cp))
440 		{
441 		    case '[':
442 			if(flags&NV_NOARRAY)
443 			{
444 				dp->last = cp;
445 				return(np);
446 			}
447 			cp = nv_endsubscript((Namval_t*)0,sp,0);
448 			if(sp==name || sp[-1]=='.')
449 				c = *(sp = cp);
450 			goto skip;
451 		    case '.':
452 			if(flags&NV_IDENT)
453 				return(0);
454 			if(root==sh.var_tree)
455 				flags &= ~NV_EXPORT;
456 			if(!copy && !(flags&NV_NOREF))
457 			{
458 				c = sp-name;
459 				copy = cp-name;
460 				dp->nofree = 1;
461 				name = copystack((const char*)0, name,(const char*)0);
462 				cp = (char*)name+copy;
463 				sp = (char*)name+c;
464 				c = '.';
465 			}
466 		skip:
467 		    case '+':
468 		    case '=':
469 			*sp = 0;
470 		    case 0:
471 			isref = 0;
472 			dp->last = cp;
473 			mode =  (c=='.' || (flags&NV_NOADD))?add:NV_ADD;
474 			if(flags&NV_NOSCOPE)
475 				mode |= HASH_NOSCOPE;
476 			if(top)
477 				nq = nv_search(name,sh.var_base,0);
478 			if(np = nv_search(name,root,mode))
479 			{
480 				isref = nv_isref(np);
481 				if(top)
482 				{
483 					if(nq==np)
484 						flags &= ~NV_NOSCOPE;
485 					else if(nq)
486 					{
487 						if(nv_isnull(np) && c!='.' && (np->nvfun=nv_cover(nq)))
488 							np->nvname = nq->nvname;
489 						flags |= NV_NOSCOPE;
490 					}
491 				}
492 				else if(add && nv_isnull(np) && c=='.')
493 					nv_setvtree(np);
494 			}
495 			if(c)
496 				*sp = c;
497 			top = 0;
498 			if(isref)
499 			{
500 				char *sub=0;
501 				if(c=='.') /* don't optimize */
502 					sh.argaddr = 0;
503 				else if(flags&NV_NOREF)
504 				{
505 					if(c)
506 						nv_unref(np);
507 					return(np);
508 				}
509 				while(nv_isref(np))
510 				{
511 					root = nv_reftree(np);
512 					sh.last_table = nv_reftable(np);
513 					sub = nv_refsub(np);
514 					np = nv_refnode(np);
515 					if(sub && c!='.')
516 						nv_putsub(np,sub,0L);
517 					flags |= NV_NOSCOPE;
518 				}
519 				if(sub && c==0)
520 					return(np);
521 				if(np==nq)
522 					flags &= ~(noscope?0:NV_NOSCOPE);
523 				else if(c)
524 				{
525 					c = (cp-sp);
526 					copy = strlen(cp=nv_name(np));
527 					dp->nofree = 1;
528 					name = copystack(cp,sp,sub);
529 					sp = (char*)name + copy;
530 					cp = sp+c;
531 					c = *sp;
532 					if(!noscope)
533 						flags &= ~NV_NOSCOPE;
534 				}
535 				flags |= NV_NOREF;
536 			}
537 			sh.last_root = root;
538 			do
539 			{
540 				if(!np)
541 				{
542 					if(*sp=='[' && *cp==0 && cp[-1]==']')
543 					{
544 						/*
545 						 * for backward compatibility
546 						 * evaluate subscript for
547 						 * possible side effects
548 						 */
549 						cp[-1] = 0;
550 						sh_arith(sp+1);
551 						cp[-1] = ']';
552 					}
553 					return(np);
554 				}
555 				if(c=='[' || (c=='.' && nv_isarray(np)))
556 				{
557 					int n = 0;
558 					if(c=='[')
559 					{
560 						n = mode|nv_isarray(np);
561 						if(!mode && (flags&NV_ARRAY) && ((c=sp[1])=='*' || c=='@') && sp[2]==']')
562 						{
563 							/* not implemented yet */
564 							dp->last = cp;
565 							return(np);
566 						}
567 						if(n&&(flags&NV_ARRAY))
568 							n |= ARRAY_FILL;
569 						cp = nv_endsubscript(np,sp,n);
570 					}
571 					else
572 						cp = sp;
573 					if((c = *cp)=='.' || c=='[' || (n&ARRAY_FILL))
574 
575 					{
576 						int m = cp-sp;
577 						char *sub = m?nv_getsub(np):0;
578 						if(!sub)
579 							sub = "0";
580 						n = strlen(sub)+2;
581 						if(!copy)
582 						{
583 							copy = cp-name;
584 							dp->nofree = 1;
585 							name = copystack((const char*)0, name,(const char*)0);
586 							cp = (char*)name+copy;
587 							sp = cp-m;
588 						}
589 						if(n <= m)
590 						{
591 							if(n)
592 							{
593 								memcpy(sp+1,sub,n-2);
594 								sp[n-1] = ']';
595 							}
596 							if(n < m)
597 								cp=strcpy(sp+n,cp);
598 						}
599 						else
600 						{
601 							int r = n-m;
602 							m = sp-name;
603 							name = stack_extend(name, cp-1, r);
604 							sp = (char*)name + m;
605 							*sp = '[';
606 							memcpy(sp+1,sub,n-2);
607 							sp[n-1] = ']';
608 							cp = sp+n;
609 
610 						}
611 					}
612 					else if(c==0 && mode && (n=nv_aindex(np))>0)
613 						nv_putsub(np,(char*)0,n|ARRAY_FILL);
614 					else if(n==0 && c==0)
615 					{
616 						/* subscript must be 0*/
617 						cp[-1] = 0;
618 						c = sh_arith(sp+1);
619 						cp[-1] = ']';
620 						if(c)
621 							return(0);
622 					}
623 					dp->last = cp;
624 					if(nv_isarray(np) && (c=='[' || c=='.' || (flags&NV_ARRAY)))
625 					{
626 						*(sp=cp) = 0;
627 						nq = nv_search(name,root,mode);
628 						*sp = c;
629 						if(nq && nv_isnull(nq))
630 							nq = nv_arraychild(np,nq,c);
631 						if(!(np=nq))
632 							return(np);
633 					}
634 				}
635 				else if(nv_isarray(np))
636 					nv_putsub(np,NIL(char*),ARRAY_UNDEF);
637 				if(c=='.' && (fp=np->nvfun))
638 				{
639 					for(; fp; fp=fp->next)
640 					{
641 						if(fp->disc && fp->disc->createf)
642 							break;
643 					}
644 					if(fp)
645 					{
646 						if((nq = (*fp->disc->createf)(np,cp+1,flags,fp)) == np)
647 						{
648 							add = NV_ADD;
649 							break;
650 						}
651 						else if((np=nq) && (c = *(cp=dp->last=fp->last))==0)
652 							return(np);
653 					}
654 				}
655 			}
656 			while(c=='[');
657 			if(c!='.')
658 				return(np);
659 			cp++;
660 			break;
661 		    default:
662 			dp->last = cp;
663 			if((c = mbchar(cp)) && !isaletter(c))
664 				return(np);
665 			while(xp=cp, c=mbchar(cp), isaname(c));
666 			cp = xp;
667 		}
668 	}
669 	return(np);
670 }
671 
672 /*
673  * Put <arg> into associative memory.
674  * If <flags> & NV_ARRAY then follow array to next subscript
675  * If <flags> & NV_NOARRAY then subscript is not allowed
676  * If <flags> & NV_NOSCOPE then use the current scope only
677  * If <flags> & NV_ASSIGN then assignment is allowed
678  * If <flags> & NV_IDENT then name must be an identifier
679  * If <flags> & NV_VARNAME then name must be a valid variable name
680  * If <flags> & NV_NOADD then node will not be added if not found
681  * If <flags> & NV_NOREF then don't follow reference
682  * If <flags> & NV_NOFAIL then don't generate an error message on failure
683  * SH_INIT is only set while initializing the environment
684  */
685 Namval_t *nv_open(const char *name, Dt_t *root, int flags)
686 {
687 	register char		*cp=(char*)name;
688 	register int		c;
689 	register Namval_t	*np;
690 	Namfun_t		fun;
691 	int			append=0;
692 	const char		*msg = e_varname;
693 	char			*fname = 0;
694 	int			offset = staktell();
695 	Dt_t			*funroot;
696 
697 	memset(&fun,0,sizeof(fun));
698 	sh.last_table = sh.namespace;
699 	if(!root)
700 		root = sh.var_tree;
701 	sh.last_root = root;
702 	if(root==sh_subfuntree(1))
703 	{
704 		flags |= NV_NOREF;
705 		msg = e_badfun;
706 		if((np=sh.namespace) || strchr(name,'.'))
707 		{
708 			name = cp = copystack(np?nv_name(np):0,name,(const char*)0);
709 			fname = strrchr(cp,'.');
710 			*fname = 0;
711 			fun.nofree = 1;
712 			flags &=  ~NV_IDENT;
713 			funroot = root;
714 			root = sh.var_tree;
715 		}
716 	}
717 	else if(!(flags&(NV_IDENT|NV_VARNAME|NV_ASSIGN)))
718 	{
719 		long mode = ((flags&NV_NOADD)?0:NV_ADD);
720 		if(flags&NV_NOSCOPE)
721 			mode |= HASH_SCOPE|HASH_NOSCOPE;
722 		np = nv_search(name,root,mode);
723 		if(np && !(flags&NV_REF))
724 		{
725 			while(nv_isref(np))
726 			{
727 				sh.last_table = nv_reftable(np);
728 				np = nv_refnode(np);
729 			}
730 		}
731 		return(np);
732 	}
733 	else if(sh.prefix && /**name!='.' &&*/ (flags&NV_ASSIGN))
734 	{
735 		name = cp = copystack(sh.prefix,name,(const char*)0);
736 		fun.nofree = 1;
737 	}
738 	c = *(unsigned char*)cp;
739 	if(root==sh.alias_tree)
740 	{
741 		msg = e_aliname;
742 		while((c= *(unsigned char*)cp++) && (c!='=') && (c!='/') &&
743 			(c>=0x200 || !(c=sh_lexstates[ST_NORM][c]) || c==S_EPAT));
744 		if(sh.subshell && c=='=')
745 			root = sh_subaliastree(1);
746 		if(c= *--cp)
747 			*cp = 0;
748 		np = nv_search(name, root, (flags&NV_NOADD)?0:NV_ADD);
749 		if(c)
750 			*cp = c;
751 		goto skip;
752 	}
753 	else if(flags&NV_IDENT)
754 		msg = e_ident;
755 	else if(c=='.')
756 	{
757 		c = *++cp;
758 		flags |= NV_NOREF;
759 		if(root==sh.var_tree)
760 			root = sh.var_base;
761 		sh.last_table = 0;
762 	}
763 	if(c= !isaletter(c))
764 		goto skip;
765 	np = nv_create(name, root, flags, &fun);
766 	cp = fun.last;
767 	if(fname)
768 	{
769 		c = ((flags&NV_NOSCOPE)?HASH_NOSCOPE:0)|((flags&NV_NOADD)?0:NV_ADD);
770 		*fname = '.';
771 		np = nv_search(name, funroot, c);
772 		*fname = 0;
773 	}
774 	else if(*cp=='+' && cp[1]=='=')
775 	{
776 		append=NV_APPEND;
777 		cp++;
778 	}
779 	c = *cp;
780 skip:
781 	if(c=='=' && np && (flags&NV_ASSIGN))
782 	{
783 		cp++;
784 		if(sh_isstate(SH_INIT))
785 		{
786 			nv_putval(np, cp, NV_RDONLY);
787 			if(np==PWDNOD)
788 				nv_onattr(np,NV_TAGGED);
789 		}
790 		else
791 		{
792 			char *sub=0;
793 			if(sh_isoption(SH_XTRACE) && nv_isarray(np))
794 				sub = nv_getsub(np);
795 			c = msg==e_aliname? 0: (append | (flags&NV_EXPORT));
796 			nv_putval(np, cp, c);
797 			savesub = sub;
798 		}
799 		nv_onattr(np, flags&NV_ATTRIBUTES);
800 	}
801 	else if(c)
802 	{
803 		if(flags&NV_NOFAIL)
804 			return(0);
805 		if(c=='.')
806 			msg = e_noparent;
807 		else if(c=='[')
808 			msg = e_noarray;
809 		errormsg(SH_DICT,ERROR_exit(1),msg,name);
810 	}
811 	if(fun.nofree)
812 		stakseek(offset);
813 	return(np);
814 }
815 
816 #if SHOPT_MULTIBYTE
817     static int ja_size(char*, int, int);
818     static void ja_restore(void);
819     static char *savep;
820     static char savechars[8+1];
821 #endif /* SHOPT_MULTIBYTE */
822 
823 /*
824  * put value <string> into name-value node <np>.
825  * If <np> is an array, then the element given by the
826  *   current index is assigned to.
827  * If <flags> contains NV_RDONLY, readonly attribute is ignored
828  * If <flags> contains NV_INTEGER, string is a pointer to a number
829  * If <flags> contains NV_NOFREE, previous value is freed, and <string>
830  * becomes value of node and <flags> becomes attributes
831  */
832 void nv_putval(register Namval_t *np, const char *string, int flags)
833 {
834 	register const char *sp=string;
835 	register union Value *up;
836 	register char *cp;
837 	register int size = 0;
838 	register int dot;
839 	int	was_local = nv_local;
840 	if(!(flags&NV_RDONLY) && nv_isattr (np, NV_RDONLY))
841 		errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np));
842 	/* The following could cause the shell to fork if assignment
843 	 * would cause a side effect
844 	 */
845 	sh.argaddr = 0;
846 	if(sh.subshell && !nv_local)
847 		np = sh_assignok(np,1);
848 	if(np->nvfun && !nv_isattr(np,NV_REF))
849 	{
850 		/* This function contains disc */
851 		if(!nv_local)
852 		{
853 			nv_local=1;
854 			nv_putv(np,sp,flags,np->nvfun);
855 			if(sp && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT)))
856 				sh_envput(sh.env,np);
857 			return;
858 		}
859 		/* called from disc, assign the actual value */
860 	}
861 	flags &= ~NV_NODISC;
862 	if(flags&(NV_NOREF|NV_NOFREE))
863 	{
864 		if(!nv_isnull(np) && np->nvalue.cp!=sp)
865 			nv_unset(np);
866 		nv_local=0;
867 		np->nvalue.cp = (char*)sp;
868 		nv_setattr(np,(flags&~NV_RDONLY)|NV_NOFREE);
869 		return;
870 	}
871 	nv_local=0;
872 	up= &np->nvalue;
873 #if !SHOPT_BSH
874 	if(nv_isattr(np,NV_EXPORT))
875 		nv_offattr(np,NV_IMPORT);
876 	else if(!nv_isattr(np,NV_MINIMAL))
877 		np->nvenv = 0;
878 #endif /* SHOPT_BSH */
879 	if(nv_isattr (np, NV_INTEGER))
880 	{
881 		if(nv_isattr(np, NV_DOUBLE))
882 		{
883 			if(nv_isattr(np, NV_LONG) && sizeof(double)<sizeof(Sfdouble_t))
884 			{
885 				Sfdouble_t ld, old=0;
886 				if(flags&NV_INTEGER)
887 				{
888 					if(flags&NV_LONG)
889 						ld = *((Sfdouble_t*)sp);
890 					else if(flags&NV_SHORT)
891 						ld = *((float*)sp);
892 					else
893 						ld = *((double*)sp);
894 				}
895 				else
896 					ld = sh_arith(sp);
897 				if(!up->ldp)
898 					up->ldp = new_of(Sfdouble_t,0);
899 				else if(flags&NV_APPEND)
900 					old = *(up->ldp);
901 				*(up->ldp) = ld+old;
902 			}
903 			else
904 			{
905 				double d,od=0;
906 				if(flags&NV_INTEGER)
907 				{
908 					if(flags&NV_LONG)
909 						d = (double)(*(Sfdouble_t*)sp);
910 					else if(flags&NV_SHORT)
911 						d = (double)(*(float*)sp);
912 					else
913 						d = *(double*)sp;
914 				}
915 				else
916 					d = sh_arith(sp);
917 				if(!up->dp)
918 					up->dp = new_of(double,0);
919 				else if(flags&NV_APPEND)
920 					od = *(up->dp);
921 				*(up->dp) = d+od;
922 			}
923 		}
924 		else
925 		{
926 			if(nv_isattr(np, NV_LONG) && sizeof(int32_t)<sizeof(Sflong_t))
927 			{
928 				Sflong_t ll=0,oll=0;
929 				if(flags&NV_INTEGER)
930 				{
931 					if(flags&NV_DOUBLE)
932 					{
933 						if(flags&NV_LONG)
934 							ll = *((Sfdouble_t*)sp);
935 						else if(flags&NV_SHORT)
936 							ll = *((float*)sp);
937 						else
938 							ll = *((double*)sp);
939 					}
940 					else if(nv_isattr(np,NV_UNSIGN))
941 					{
942 						if(flags&NV_LONG)
943 							ll = *((Sfulong_t*)sp);
944 						else if(flags&NV_SHORT)
945 							ll = *((uint16_t*)sp);
946 						else
947 							ll = *((uint32_t*)sp);
948 					}
949 					else
950 					{
951 						if(flags&NV_LONG)
952 							ll = *((Sflong_t*)sp);
953 						else if(flags&NV_SHORT)
954 							ll = *((uint16_t*)sp);
955 						else
956 							ll = *((uint32_t*)sp);
957 					}
958 				}
959 				else if(sp)
960 					ll = (Sflong_t)sh_arith(sp);
961 				if(!up->llp)
962 					up->llp = new_of(Sflong_t,0);
963 				else if(flags&NV_APPEND)
964 					oll = *(up->llp);
965 				*(up->llp) = ll+oll;
966 			}
967 			else
968 			{
969 				int32_t l=0,ol=0;
970 				if(flags&NV_INTEGER)
971 				{
972 					if(flags&NV_DOUBLE)
973 					{
974 						Sflong_t ll;
975 						if(flags&NV_LONG)
976 							ll = *((Sfdouble_t*)sp);
977 						else if(flags&NV_SHORT)
978 							ll = *((float*)sp);
979 						else
980 							ll = *((double*)sp);
981 						l = (int32_t)ll;
982 					}
983 					else if(nv_isattr(np,NV_UNSIGN))
984 					{
985 						if(flags&NV_LONG)
986 							l = *((Sfulong_t*)sp);
987 						else if(flags&NV_SHORT)
988 							l = *((uint16_t*)sp);
989 						else
990 							l = *(uint32_t*)sp;
991 					}
992 					else
993 					{
994 						if(flags&NV_LONG)
995 							l = *((Sflong_t*)sp);
996 						else if(flags&NV_SHORT)
997 							l = *((int16_t*)sp);
998 						else
999 							l = *(int32_t*)sp;
1000 					}
1001 				}
1002 				else if(sp)
1003 				{
1004 					Sfdouble_t ld = sh_arith(sp);
1005 					if(ld<0)
1006 						l = (int32_t)ld;
1007 					else
1008 						l = (uint32_t)ld;
1009 				}
1010 				if(nv_size(np) <= 1)
1011 					nv_setsize(np,10);
1012 				if(nv_isattr (np, NV_SHORT))
1013 				{
1014 					int16_t s=0;
1015 					if(flags&NV_APPEND)
1016 						s = up->s;
1017 					up->s = s+(int16_t)l;
1018 					nv_onattr(np,NV_NOFREE);
1019 				}
1020 				else
1021 				{
1022 					if(!up->lp)
1023 						up->lp = new_of(int32_t,0);
1024 					else if(flags&NV_APPEND)
1025 						ol =  *(up->lp);
1026 					*(up->lp) = l+ol;
1027 				}
1028 			}
1029 		}
1030 	}
1031 	else
1032 	{
1033 		const char *tofree=0;
1034 		int offset;
1035 #if _lib_pathnative
1036 		char buff[PATH_MAX];
1037 #endif /* _lib_pathnative */
1038 		if(flags&NV_INTEGER)
1039 		{
1040 			if(flags&NV_DOUBLE)
1041 			{
1042 				if(flags&NV_LONG)
1043 					sfprintf(sh.strbuf,"%.*Lg",LDBL_DIG,*((Sfdouble_t*)sp));
1044 				else
1045 					sfprintf(sh.strbuf,"%.*g",DBL_DIG,*((double*)sp));
1046 			}
1047 			else if(flags&NV_LONG)
1048 				sfprintf(sh.strbuf,"%lld\0",*((Sflong_t*)sp));
1049 			else
1050 				sfprintf(sh.strbuf,"%ld\0",*((int32_t*)sp));
1051 			sp = sfstruse(sh.strbuf);
1052 		}
1053 		if(nv_isattr(np, NV_HOST)==NV_HOST && sp)
1054 		{
1055 #ifdef _lib_pathnative
1056 			/*
1057 			 * return the host file name given the UNIX name
1058 			 */
1059 			pathnative(sp,buff,sizeof(buff));
1060 			if(buff[1]==':' && buff[2]=='/')
1061 			{
1062 				buff[2] = '\\';
1063 				if(*buff>='A' &&  *buff<='Z')
1064 					*buff += 'a'-'A';
1065 			}
1066 			sp = buff;
1067 #else
1068 			;
1069 #endif /* _lib_pathnative */
1070 		}
1071 		else if((nv_isattr(np, NV_RJUST|NV_ZFILL|NV_LJUST)) && sp)
1072 		{
1073 			for(;*sp == ' '|| *sp=='\t';sp++);
1074 	        	if((nv_isattr(np,NV_ZFILL)) && (nv_isattr(np,NV_LJUST)))
1075 				for(;*sp=='0';sp++);
1076 			size = nv_size(np);
1077 #if SHOPT_MULTIBYTE
1078 			if(size)
1079 				size = ja_size((char*)sp,size,nv_isattr(np,NV_RJUST|NV_ZFILL));
1080 #endif /* SHOPT_MULTIBYTE */
1081 		}
1082 		if(!up->cp)
1083 			flags &= ~NV_APPEND;
1084 		if((flags&NV_APPEND) && !nv_isattr(np,NV_BINARY))
1085 		{
1086 			offset = staktell();
1087 			stakputs(up->cp);
1088 			stakputs(sp);
1089 			stakputc(0);
1090 			sp = stakptr(offset);
1091 		}
1092 		if(!nv_isattr(np, NV_NOFREE))
1093 		{
1094 			/* delay free in case <sp> points into free region */
1095 			tofree = up->cp;
1096 		}
1097 		nv_offattr(np,NV_NOFREE);
1098        	 	if (sp)
1099 		{
1100 			dot = strlen(sp);
1101 #if (_AST_VERSION>=20030127L)
1102 			if(nv_isattr(np,NV_BINARY))
1103 			{
1104 				int oldsize = (flags&NV_APPEND)?nv_size(np):0;
1105 				if(flags&NV_RAW)
1106 				{
1107 					if(tofree)
1108 						free((void*)tofree);
1109 					up->cp = sp;
1110 					return;
1111 				}
1112 				size = 0;
1113 				if(nv_isattr(np,NV_ZFILL))
1114 					size = nv_size(np);
1115 				if(size==0)
1116 					size = oldsize + (3*dot/4);
1117 				cp = (char*)malloc(size+1);
1118 				if(oldsize)
1119 					memcpy((void*)cp,(void*)up->cp,oldsize);
1120 				up->cp = cp;
1121 				if(size <= oldsize)
1122 					return;
1123 				dot = base64decode(sp,dot, (void**)0, cp+oldsize, size-oldsize,(void**)0);
1124 				dot += oldsize;
1125 				if(!nv_isattr(np,NV_ZFILL) || nv_size(np)==0)
1126 					nv_setsize(np,dot);
1127 				else if(nv_isattr(np,NV_ZFILL) && (size>dot))
1128 					memset((void*)&cp[dot],0,size-dot);
1129 				return;
1130 			}
1131 			else
1132 #endif
1133 			if(size==0 && nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL))
1134 				nv_setsize(np,size=dot);
1135 			else if(size > dot)
1136 				dot = size;
1137 			cp = (char*)malloc(((unsigned)dot+1));
1138 		}
1139 		else
1140 			cp = 0;
1141 		up->cp = cp;
1142 		if(sp)
1143 		{
1144 			if(nv_isattr(np, NV_LTOU))
1145 				ltou(sp,cp);
1146 			else if(nv_isattr (np, NV_UTOL))
1147 				sh_utol(sp,cp);
1148 			else
1149        			 	strcpy(cp, sp);
1150 			if(nv_isattr(np, NV_RJUST) && nv_isattr(np, NV_ZFILL))
1151 				rightjust(cp,size,'0');
1152 			else if(nv_isattr(np, NV_RJUST))
1153 				rightjust(cp,size,' ');
1154 			else if(nv_isattr(np, NV_LJUST))
1155 			{
1156 				register char *dp;
1157 				dp = strlen (cp) + cp;
1158 				*(cp = (cp + size)) = 0;
1159 				for (; dp < cp; *dp++ = ' ');
1160 			 }
1161 #if SHOPT_MULTIBYTE
1162 			/* restore original string */
1163 			if(savep)
1164 				ja_restore();
1165 #endif /* SHOPT_MULTIBYTE */
1166 		}
1167 		if(flags&NV_APPEND)
1168 			stakseek(offset);
1169 		if(tofree)
1170 			free((void*)tofree);
1171 	}
1172 	if(!was_local && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT)))
1173 		sh_envput(sh.env,np);
1174 	return;
1175 }
1176 
1177 /*
1178  *
1179  *   Right-justify <str> so that it contains no more than
1180  *   <size> characters.  If <str> contains fewer than <size>
1181  *   characters, left-pad with <fill>.  Trailing blanks
1182  *   in <str> will be ignored.
1183  *
1184  *   If the leftmost digit in <str> is not a digit, <fill>
1185  *   will default to a blank.
1186  */
1187 static void rightjust(char *str, int size, int fill)
1188 {
1189 	register int n;
1190 	register char *cp,*sp;
1191 	n = strlen(str);
1192 
1193 	/* ignore trailing blanks */
1194 	for(cp=str+n;n && *--cp == ' ';n--);
1195 	if (n == size)
1196 		return;
1197 	if(n > size)
1198         {
1199         	*(str+n) = 0;
1200         	for (sp = str, cp = str+n-size; sp <= str+size; *sp++ = *cp++);
1201         	return;
1202         }
1203 	else *(sp = str+size) = 0;
1204 	if (n == 0)
1205         {
1206         	while (sp > str)
1207                		*--sp = ' ';
1208         	return;
1209         }
1210 	while(n--)
1211 	{
1212 		sp--;
1213 		*sp = *cp--;
1214 	}
1215 	if(!isdigit(*str))
1216 		fill = ' ';
1217 	while(sp>str)
1218 		*--sp = fill;
1219 	return;
1220 }
1221 
1222 #if SHOPT_MULTIBYTE
1223     /*
1224      * handle left and right justified fields for multi-byte chars
1225      * given physical size, return a logical size which reflects the
1226      * screen width of multi-byte characters
1227      * Multi-width characters replaced by spaces if they cross the boundary
1228      * <type> is non-zero for right justified  fields
1229      */
1230 
1231     static int ja_size(char *str,int size,int type)
1232     {
1233 	register char *cp = str;
1234 	register int c, n=size;
1235 	register int outsize;
1236 	register char *oldcp=cp;
1237 	int oldn;
1238 	wchar_t w;
1239 	while(*cp)
1240 	{
1241 		oldn = n;
1242 		w = mbchar(cp);
1243 		outsize = mbwidth(w);
1244 		size -= outsize;
1245 		c = cp-oldcp;
1246 		n += (c-outsize);
1247 		oldcp = cp;
1248 		if(size<=0 && type==0)
1249 			break;
1250 	}
1251 	/* check for right justified fields that need truncating */
1252 	if(size <0)
1253 	{
1254 		if(type==0)
1255 		{
1256 			/* left justified and character crosses field boundary */
1257 			n = oldn;
1258 			/* save boundary char and replace with spaces */
1259 			size = c;
1260 			savechars[size] = 0;
1261 			while(size--)
1262 			{
1263 				savechars[size] = cp[size];
1264 				cp[size] = ' ';
1265 			}
1266 			savep = cp;
1267 		}
1268 		size = -size;
1269 		if(type)
1270 			n -= (ja_size(str,size,0)-size);
1271 	}
1272 	return(n);
1273     }
1274 
1275     static void ja_restore(void)
1276     {
1277 	register char *cp = savechars;
1278 	while(*cp)
1279 		*savep++ = *cp++;
1280 	savep = 0;
1281     }
1282 #endif /* SHOPT_MULTIBYTE */
1283 
1284 #ifndef _ENV_H
1285 static char *staknam(register Namval_t *np, char *value)
1286 {
1287 	register char *p,*q;
1288 	q = stakalloc(strlen(nv_name(np))+(value?strlen(value):0)+2);
1289 	p=strcopy(q,nv_name(np));
1290 	if(value)
1291 	{
1292 		*p++ = '=';
1293 		strcpy(p,value);
1294 	}
1295 	return(q);
1296 }
1297 #endif
1298 
1299 /*
1300  * put the name and attribute into value of attributes variable
1301  */
1302 #ifdef _ENV_H
1303 static void attstore(register Namval_t *np, void *data)
1304 {
1305 	register int flag, c = ' ';
1306 	NOT_USED(data);
1307 	if(!(nv_isattr(np,NV_EXPORT)))
1308 		return;
1309 	flag = nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER);
1310 	stakputc('=');
1311 	if((flag&NV_DOUBLE) && (flag&NV_INTEGER))
1312 	{
1313 		/* export doubles as integers for ksh88 compatibility */
1314 		stakputc(c+(flag&~(NV_DOUBLE|NV_EXPNOTE)));
1315 	}
1316 	else
1317 	{
1318 		stakputc(c+flag);
1319 		if(flag&NV_INTEGER)
1320 			c +=  nv_size(np);
1321 	}
1322 	stakputc(c);
1323 	stakputs(nv_name(np));
1324 }
1325 #else
1326 static void attstore(register Namval_t *np, void *data)
1327 {
1328 	register int flag = np->nvflag;
1329 	register struct adata *ap = (struct adata*)data;
1330 	if(!(flag&NV_EXPORT) || (flag&NV_FUNCT))
1331 		return;
1332 	flag &= (NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER);
1333 	*ap->attval++ = '=';
1334 	if((flag&NV_DOUBLE) && (flag&NV_INTEGER))
1335 	{
1336 		/* export doubles as integers for ksh88 compatibility */
1337 		*ap->attval++ = ' '+(flag&~(NV_DOUBLE|NV_EXPNOTE));
1338 		*ap->attval = ' ';
1339 	}
1340 	else
1341 	{
1342 		*ap->attval++ = ' '+flag;
1343 		if(flag&NV_INTEGER)
1344 			*ap->attval = ' ' + nv_size(np);
1345 		else
1346 			*ap->attval = ' ';
1347 	}
1348 	ap->attval = strcopy(++ap->attval,nv_name(np));
1349 }
1350 #endif
1351 
1352 #ifndef _ENV_H
1353 static void pushnam(Namval_t *np, void *data)
1354 {
1355 	register char *value;
1356 	register struct adata *ap = (struct adata*)data;
1357 	if(nv_isattr(np,NV_IMPORT))
1358 	{
1359 		if(np->nvenv)
1360 			*ap->argnam++ = np->nvenv;
1361 	}
1362 	else if(value=nv_getval(np))
1363 		*ap->argnam++ = staknam(np,value);
1364 	if(nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER))
1365 		ap->attsize += (strlen(nv_name(np))+4);
1366 }
1367 #endif
1368 
1369 /*
1370  * Generate the environment list for the child.
1371  */
1372 
1373 #ifdef _ENV_H
1374 char **sh_envgen(void)
1375 {
1376 	int offset,tell;
1377 	register char **er;
1378 	env_delete(sh.env,"_");
1379 	er = env_get(sh.env);
1380 	offset = staktell();
1381 	stakputs(e_envmarker);
1382 	tell = staktell();
1383 	nv_scan(sh.var_tree, attstore,(void*)0,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER));
1384 	if(tell ==staktell())
1385 		stakseek(offset);
1386 	else
1387 		*--er = stakfreeze(1)+offset;
1388 	return(er);
1389 }
1390 #else
1391 char **sh_envgen(void)
1392 {
1393 	register char **er;
1394 	register int namec;
1395 	register char *cp;
1396 	struct adata data;
1397 	/* L_ARGNOD gets generated automatically as full path name of command */
1398 	nv_offattr(L_ARGNOD,NV_EXPORT);
1399 	data.attsize = 6;
1400 	namec = nv_scan(sh.var_tree,nullscan,(void*)0,NV_EXPORT,NV_EXPORT);
1401 	er = (char**)stakalloc((namec+4)*sizeof(char*));
1402 	data.argnam = (er+=2);
1403 	nv_scan(sh.var_tree, pushnam,&data,NV_EXPORT, NV_EXPORT);
1404 	*data.argnam = (char*)stakalloc(data.attsize);
1405 	cp = data.attval = strcopy(*data.argnam,e_envmarker);
1406 	nv_scan(sh.var_tree, attstore,&data,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER));
1407 	*data.attval = 0;
1408 	if(cp!=data.attval)
1409 		data.argnam++;
1410 	*data.argnam = 0;
1411 	return(er);
1412 }
1413 #endif
1414 
1415 struct scan
1416 {
1417 	void    (*scanfn)(Namval_t*, void*);
1418 	int     scanmask;
1419 	int     scanflags;
1420 	int     scancount;
1421 	void    *scandata;
1422 };
1423 
1424 
1425 static int scanfilter(Dt_t *dict, void *arg, void *data)
1426 {
1427 	register Namval_t *np = (Namval_t*)arg;
1428 	register int k=np->nvflag;
1429 	register struct scan *sp = (struct scan*)data;
1430 	NOT_USED(dict);
1431 	if(sp->scanmask?(k&sp->scanmask)==sp->scanflags:(!sp->scanflags || (k&sp->scanflags)))
1432 	{
1433 		if(!np->nvalue.cp && !nv_isattr(np,~NV_DEFAULT))
1434 			return(0);
1435 		if(sp->scanfn)
1436 		{
1437 			if(nv_isarray(np))
1438 				nv_putsub(np,NIL(char*),0L);
1439 			(*sp->scanfn)(np,sp->scandata);
1440 		}
1441 		sp->scancount++;
1442 	}
1443 	return(0);
1444 }
1445 
1446 /*
1447  * Walk through the name-value pairs
1448  * if <mask> is non-zero, then only nodes with (nvflags&mask)==flags
1449  *	are visited
1450  * If <mask> is zero, and <flags> non-zero, then nodes with one or
1451  *	more of <flags> is visited
1452  * If <mask> and <flags> are zero, then all nodes are visted
1453  */
1454 int nv_scan(Dt_t *root, void (*fn)(Namval_t*,void*), void *data,int mask, int flags)
1455 {
1456 	Dt_t *base=0;
1457 	struct scan sdata;
1458 	int (*hashfn)(Dt_t*, void*, void*);
1459 	sdata.scanmask = mask;
1460 	sdata.scanflags = flags&~NV_NOSCOPE;
1461 	sdata.scanfn = fn;
1462 	sdata.scancount = 0;
1463 	sdata.scandata = data;
1464 	hashfn = scanfilter;
1465 	if(flags&NV_NOSCOPE)
1466 		base = dtview((Dt_t*)root,0);
1467 	dtwalk(root, hashfn,&sdata);
1468 	if(base)
1469 		 dtview((Dt_t*)root,base);
1470 	return(sdata.scancount);
1471 }
1472 
1473 /*
1474  * create a new environment scope
1475  */
1476 void nv_scope(struct argnod *envlist)
1477 {
1478 	register Dt_t *newscope;
1479 	newscope = dtopen(&_Nvdisc,Dtoset);
1480 	dtview(newscope,(Dt_t*)sh.var_tree);
1481 	sh.var_tree = (Dt_t*)newscope;
1482 	if(envlist)
1483 		nv_setlist(envlist,NV_EXPORT|NV_NOSCOPE|NV_IDENT|NV_ASSIGN);
1484 }
1485 
1486 /*
1487  * Remove freeable local space associated with the nvalue field
1488  * of nnod. This includes any strings representing the value(s) of the
1489  * node, as well as its dope vector, if it is an array.
1490  */
1491 
1492 void	sh_envnolocal (register Namval_t *np, void *data)
1493 {
1494 	char *cp=0;
1495 	NOT_USED(data);
1496 	if(nv_isattr(np,NV_EXPORT) && nv_isarray(np))
1497 	{
1498 		nv_putsub(np,NIL(char*),0);
1499 		if(cp = nv_getval(np))
1500 			cp = strdup(cp);
1501 	}
1502 	if(nv_isattr(np,NV_EXPORT|NV_NOFREE))
1503 	{
1504 		if(nv_isref(np))
1505 		{
1506 			nv_offattr(np,NV_NOFREE|NV_REF);
1507 			free((void*)np->nvalue.nrp);
1508 			np->nvalue.cp = 0;
1509 		}
1510 		if(!cp)
1511 			return;
1512 	}
1513 	if(nv_isarray(np))
1514 		nv_putsub(np,NIL(char*),ARRAY_UNDEF);
1515 	_nv_unset(np,NV_RDONLY);
1516 	nv_setattr(np,0);
1517 	if(cp)
1518 	{
1519 		nv_putval(np,cp,0);
1520 		free((void*)cp);
1521 	}
1522 }
1523 
1524 /*
1525  * Currently this is a dummy, but someday will be needed
1526  * for reference counting
1527  */
1528 void	nv_close(Namval_t *np)
1529 {
1530 	NOT_USED(np);
1531 }
1532 
1533 static void table_unset(register Dt_t *root, int flags, Dt_t *oroot)
1534 {
1535 	register Namval_t *np,*nq;
1536 	for(np=(Namval_t*)dtfirst(root);np;np=nq)
1537 	{
1538 		_nv_unset(np,flags);
1539 		if(oroot && (nq=nv_search(nv_name(np),oroot,0)) && nv_isattr(nq,NV_EXPORT))
1540 			sh_envput(sh.env,nq);
1541 		nq = (Namval_t*)dtnext(root,np);
1542 		dtdelete(root,np);
1543 		free((void*)np);
1544 	}
1545 }
1546 
1547 /*
1548  *
1549  *   Set the value of <np> to 0, and nullify any attributes
1550  *   that <np> may have had.  Free any freeable space occupied
1551  *   by the value of <np>.  If <np> denotes an array member, it
1552  *   will retain its attributes.
1553  *   <flags> can contain NV_RDONLY to override the readonly attribute
1554  *	being cleared.
1555  */
1556 void	_nv_unset(register Namval_t *np,int flags)
1557 {
1558 	register union Value *up;
1559 	if(!(flags&NV_RDONLY) && nv_isattr (np,NV_RDONLY))
1560 		errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np));
1561 	if(is_afunction(np) && np->nvalue.ip)
1562 	{
1563 		register struct slnod *slp = (struct slnod*)(np->nvenv);
1564 		if(slp && !nv_isattr(np,NV_NOFREE))
1565 		{
1566 			/* free function definition */
1567 			register char *name=nv_name(np),*cp= strrchr(name,'.');
1568 			if(cp)
1569 			{
1570 				Namval_t *npv;
1571 				*cp = 0;
1572 				 npv = nv_open(name,sh.var_tree,NV_NOARRAY|NV_VARNAME|NV_NOADD);
1573 				*cp++ = '.';
1574 				if(npv)
1575 					nv_setdisc(npv,cp,NIL(Namval_t*),(Namfun_t*)npv);
1576 			}
1577 			stakdelete(slp->slptr);
1578 			free((void*)np->nvalue.ip);
1579 			np->nvalue.ip = 0;
1580 		}
1581 		goto done;
1582 	}
1583 	if(sh.subshell && !nv_isnull(np))
1584 		np = sh_assignok(np,0);
1585 	nv_offattr(np,NV_NODISC);
1586 	if(np->nvfun && !nv_isref(np))
1587 	{
1588 		/* This function contains disc */
1589 		if(!nv_local)
1590 		{
1591 			nv_local=1;
1592 			nv_putv(np,NIL(char*),flags,np->nvfun);
1593 			return;
1594 		}
1595 		/* called from disc, assign the actual value */
1596 		nv_local=0;
1597 	}
1598 	up = &np->nvalue;
1599 	if(up->cp)
1600 	{
1601 		if(!nv_isattr (np, NV_NOFREE))
1602 			free((void*)up->cp);
1603 		up->cp = 0;
1604 	}
1605 done:
1606 	if(!nv_isarray(np) || !nv_arrayptr(np))
1607 	{
1608 		if(nv_isref(np))
1609 			free((void*)np->nvalue.nrp);
1610 		nv_setsize(np,0);
1611 		if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT))
1612 		{
1613 			if(nv_isattr(np,NV_EXPORT) && !strchr(np->nvname,'['))
1614 				env_delete(sh.env,nv_name(np));
1615 			np->nvenv = 0;
1616 			nv_setattr(np,0);
1617 		}
1618 		else
1619 			nv_setattr(np,NV_MINIMAL);
1620 	}
1621 }
1622 
1623 void	nv_unset(register Namval_t *np)
1624 {
1625 	_nv_unset(np,0);
1626 }
1627 
1628 /*
1629  * return the node pointer in the highest level scope
1630  */
1631 Namval_t *nv_scoped(register Namval_t *np)
1632 {
1633 	if(!dtvnext(sh.var_tree))
1634 		return(np);
1635 	return(dtsearch(sh.var_tree,np));
1636 }
1637 
1638 #if 1
1639 /*
1640  * return space separated list of names of variables in given tree
1641  */
1642 static char *tableval(Dt_t *root)
1643 {
1644 	static Sfio_t *out;
1645 	register Namval_t *np;
1646 	register int first=1;
1647 	register Dt_t *base = dtview(root,0);
1648         if(out)
1649                 sfseek(out,(Sfoff_t)0,SEEK_SET);
1650         else
1651                 out =  sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
1652 	for(np=(Namval_t*)dtfirst(root);np;np=(Namval_t*)dtnext(root,np))
1653 	{
1654                 if(!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE))
1655 		{
1656 			if(!first)
1657 				sfputc(out,' ');
1658 			else
1659 				first = 0;
1660 			sfputr(out,np->nvname,-1);
1661 		}
1662 	}
1663 	sfputc(out,0);
1664 	if(base)
1665 		dtview(root,base);
1666 	return((char*)out->_data);
1667 }
1668 #endif
1669 
1670 #if SHOPT_OPTIMIZE
1671 struct optimize
1672 {
1673 	Namfun_t	hdr;
1674 	char		**ptr;
1675 	struct optimize	*next;
1676 	Namval_t	*np;
1677 };
1678 
1679 static struct optimize *opt_free;
1680 
1681 static void optimize_clear(Namval_t* np, Namfun_t *fp)
1682 {
1683 	struct optimize *op = (struct optimize*)fp;
1684 	nv_stack(np,fp);
1685 	nv_stack(np,(Namfun_t*)0);
1686 	for(;op && op->np==np; op=op->next)
1687 	{
1688 		if(op->ptr)
1689 		{
1690 			*op->ptr = 0;
1691 			op->ptr = 0;
1692 		}
1693 	}
1694 }
1695 
1696 static void put_optimize(Namval_t* np,const char *val,int flags,Namfun_t *fp)
1697 {
1698 	nv_putv(np,val,flags,fp);
1699 	optimize_clear(np,fp);
1700 }
1701 
1702 static const Namdisc_t optimize_disc  = {sizeof(struct optimize),put_optimize};
1703 
1704 void nv_optimize(Namval_t *np)
1705 {
1706 	register Namfun_t *fp;
1707 	register struct optimize *op, *xp;
1708 	if(sh.argaddr)
1709 	{
1710 		for(fp=np->nvfun; fp; fp = fp->next)
1711 		{
1712 			if(fp->disc->getnum || fp->disc->getval)
1713 			{
1714 				sh.argaddr = 0;
1715 				return;
1716 			}
1717 			if(fp->disc== &optimize_disc)
1718 				break;
1719 		}
1720 		if((xp= (struct optimize*)fp) && xp->ptr==sh.argaddr)
1721 			return;
1722 		if(op = opt_free)
1723 			opt_free = op->next;
1724 		else
1725 			op=(struct optimize*)calloc(1,sizeof(struct optimize));
1726 		op->ptr = sh.argaddr;
1727 		op->np = np;
1728 		if(xp)
1729 		{
1730 			op->hdr.disc = 0;
1731 			op->next = xp->next;
1732 			xp->next = op;
1733 		}
1734 		else
1735 		{
1736 			op->hdr.disc = &optimize_disc;
1737 			op->next = (struct optimize*)sh.optlist;
1738 			sh.optlist = (void*)op;
1739 			nv_stack(np,&op->hdr);
1740 		}
1741 	}
1742 }
1743 
1744 void sh_optclear(Shell_t *shp, void *old)
1745 {
1746 	register struct optimize *op,*opnext;
1747 	for(op=(struct optimize*)shp->optlist; op; op = opnext)
1748 	{
1749 		opnext = op->next;
1750 		if(op->ptr && op->hdr.disc)
1751 		{
1752 			nv_stack(op->np,&op->hdr);
1753 			nv_stack(op->np,(Namfun_t*)0);
1754 		}
1755 		op->next = opt_free;
1756 		opt_free = op;
1757 	}
1758 	shp->optlist = old;
1759 }
1760 
1761 #else
1762 #   define	optimize_clear(np,fp)
1763 #endif /* SHOPT_OPTIMIZE */
1764 
1765 /*
1766  *   Return a pointer to a character string that denotes the value
1767  *   of <np>.  If <np> refers to an array,  return a pointer to
1768  *   the value associated with the current index.
1769  *
1770  *   If the value of <np> is an integer, the string returned will
1771  *   be overwritten by the next call to nv_getval.
1772  *
1773  *   If <np> has no value, 0 is returned.
1774  */
1775 
1776 char *nv_getval(register Namval_t *np)
1777 {
1778 	register union Value *up= &np->nvalue;
1779 	register int numeric;
1780 #if SHOPT_OPTIMIZE
1781 	if(!nv_local && sh.argaddr)
1782 		nv_optimize(np);
1783 #endif /* SHOPT_OPTIMIZE */
1784 	if(!np->nvfun && !nv_isattr(np,NV_ARRAY|NV_INTEGER|NV_FUNCT|NV_REF|NV_TABLE))
1785 		goto done;
1786 	if(nv_isref(np))
1787 	{
1788 		sh.last_table = nv_reftable(np);
1789 		return(nv_name(nv_refnode(np)));
1790 	}
1791 	if(np->nvfun)
1792 	{
1793 		if(!nv_local)
1794 		{
1795 			nv_local=1;
1796 			return(nv_getv(np, np->nvfun));
1797 		}
1798 		nv_local=0;
1799 	}
1800 	numeric = ((nv_isattr (np, NV_INTEGER)) != 0);
1801 	if(numeric)
1802 	{
1803 		Sflong_t  ll;
1804 		if(!up->cp)
1805 			return("0");
1806 		if(nv_isattr (np,NV_DOUBLE))
1807 		{
1808 			Sfdouble_t ld;
1809 			double d;
1810 			char *format;
1811 			if(nv_isattr(np,NV_LONG))
1812 			{
1813 				ld = *up->ldp;
1814 				if(nv_isattr (np,NV_EXPNOTE))
1815 					format = "%.*Lg";
1816 				else
1817 					format = "%.*Lf";
1818 				sfprintf(sh.strbuf,format,nv_size(np),ld);
1819 			}
1820 			else
1821 			{
1822 				d = *up->dp;
1823 				if(nv_isattr (np,NV_EXPNOTE))
1824 					format = "%.*g";
1825 				else
1826 					format = "%.*f";
1827 				sfprintf(sh.strbuf,format,nv_size(np),d);
1828 			}
1829 			return(sfstruse(sh.strbuf));
1830 		}
1831 		else if(nv_isattr(np,NV_UNSIGN))
1832 		{
1833 	        	if(nv_isattr (np,NV_LONG))
1834 				ll = *(Sfulong_t*)up->llp;
1835 			else if(nv_isattr (np,NV_SHORT))
1836 				ll = (uint16_t)up->s;
1837 			else
1838 				ll = *(uint32_t*)(up->lp);
1839 		}
1840         	else if(nv_isattr (np,NV_LONG))
1841 			ll = *up->llp;
1842         	else if(nv_isattr (np,NV_SHORT))
1843 			ll = up->s;
1844         	else
1845 			ll = *(up->lp);
1846 		if((numeric=nv_size(np))==10)
1847 		{
1848 			if(nv_isattr(np,NV_UNSIGN))
1849 			{
1850 				sfprintf(sh.strbuf,"%I*u",sizeof(ll),ll);
1851 				return(sfstruse(sh.strbuf));
1852 			}
1853 			numeric = 0;
1854 		}
1855 		return(fmtbasell(ll,numeric, numeric&&numeric!=10));
1856 	}
1857 done:
1858 #if (_AST_VERSION>=20030127L)
1859 	/*
1860 	 * if NV_RAW flag is on, return pointer to binary data
1861 	 * otherwise, base64 encode the data and return this string
1862 	 */
1863 	if(up->cp && nv_isattr(np,NV_BINARY) && !nv_isattr(np,NV_RAW))
1864 	{
1865 		char *cp;
1866 		int size= nv_size(np), insize=(4*size)/3+size/45+8;
1867 		base64encode(up->cp, size, (void**)0, cp=getbuf(insize), insize, (void**)0);
1868 		return(cp);
1869 	}
1870 #endif
1871 	if((numeric=nv_size(np)) && up->cp && up->cp[numeric])
1872 	{
1873 		char *cp = getbuf(numeric+1);
1874 		memcpy(cp,up->cp,numeric);
1875 		cp[numeric]=0;
1876 		return(cp);
1877 	}
1878 	return ((char*)up->cp);
1879 }
1880 
1881 Sfdouble_t nv_getnum(register Namval_t *np)
1882 {
1883 	register union Value *up;
1884 	register Sfdouble_t r=0;
1885 	register char *str;
1886 #if SHOPT_OPTIMIZE
1887 	if(!nv_local && sh.argaddr)
1888 		nv_optimize(np);
1889 #endif /* SHOPT_OPTIMIZE */
1890 	if(nv_istable(np))
1891 		errormsg(SH_DICT,ERROR_exit(1),e_number,nv_name(np));
1892      	if(np->nvfun)
1893 	{
1894 		if(!nv_local)
1895 		{
1896 			nv_local=1;
1897 			return(nv_getn(np, np->nvfun));
1898 		}
1899 		nv_local=0;
1900 	}
1901      	if(nv_isattr (np, NV_INTEGER))
1902 	{
1903 		up= &np->nvalue;
1904 		if(!up->lp)
1905 			r = 0;
1906 		else if(nv_isattr(np, NV_DOUBLE))
1907 		{
1908 			if(nv_isattr(np, NV_LONG))
1909 	                       	r = *up->ldp;
1910 			else
1911        	                	r = *up->dp;
1912 		}
1913 		else if(nv_isattr(np, NV_UNSIGN))
1914 		{
1915 			if(nv_isattr(np, NV_LONG))
1916 				r = (Sflong_t)*((Sfulong_t*)up->llp);
1917 			else if(nv_isattr(np, NV_SHORT))
1918 				r = (Sflong_t)((uint16_t)up->s);
1919 			else
1920 				r = *((uint32_t*)up->lp);
1921 		}
1922 		else
1923 		{
1924 			if(nv_isattr(np, NV_LONG))
1925 				r = *up->llp;
1926 			else if(nv_isattr(np, NV_SHORT))
1927 				r = up->s;
1928 			else
1929 				r = *up->lp;
1930 		}
1931 	}
1932 	else if((str=nv_getval(np)) && *str!=0)
1933 	{
1934 		if(np->nvfun ||  nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL))
1935 		{
1936 			while(*str=='0')
1937 				str++;
1938 		}
1939 		r = sh_arith(str);
1940 	}
1941 	return(r);
1942 }
1943 /*
1944  *   Give <np> the attributes <newatts,> and change its current
1945  *   value to conform to <newatts>.  The <size> of left and right
1946  *   justified fields may be given.
1947  */
1948 void nv_newattr (register Namval_t *np, unsigned newatts, int size)
1949 {
1950 	register char *sp;
1951 	register char *cp = 0;
1952 	register unsigned int n;
1953 	Namarr_t *ap = 0;
1954 	int oldsize,oldatts;
1955 
1956 	/* check for restrictions */
1957 	if(sh_isoption(SH_RESTRICTED) && ((sp=nv_name(np))==nv_name(PATHNOD) || sp==nv_name(SHELLNOD) || sp==nv_name(ENVNOD) || sp==nv_name(FPATHNOD)))
1958 		errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np));
1959 	/* handle attributes that do not change data separately */
1960 	n = np->nvflag;
1961 #if SHOPT_BSH
1962 	if(newatts&NV_EXPORT)
1963 		nv_offattr(np,NV_IMPORT);
1964 #endif /* SHOPT_BSH */
1965 	if(((n^newatts)&NV_EXPORT))
1966 	{
1967 		/* record changes to the environment */
1968 		if(n&NV_EXPORT)
1969 			env_delete(sh.env,nv_name(np));
1970 		else
1971 			sh_envput(sh.env,np);
1972 	}
1973 	if((size==0||(n&NV_INTEGER)) && ((n^newatts)&~NV_NOCHANGE)==0)
1974 	{
1975 		if(size)
1976 			nv_setsize(np,size);
1977 		nv_offattr(np, ~NV_NOFREE);
1978 		nv_onattr(np, newatts);
1979 		return;
1980 	}
1981 	/* for an array, change all the elements */
1982 	if((ap=nv_arrayptr(np)) && ap->nelem>0)
1983 		nv_putsub(np,NIL(char*),ARRAY_SCAN);
1984 	oldsize = nv_size(np);
1985 	oldatts = np->nvflag;
1986 	if(ap) /* add element to prevent array deletion */
1987 		ap->nelem++;
1988 	do
1989 	{
1990 		nv_setsize(np,oldsize);
1991 		np->nvflag = oldatts;
1992 		if (sp = nv_getval(np))
1993  		{
1994 			if(nv_isattr(np,NV_ZFILL))
1995 				while(*sp=='0') sp++;
1996 			cp = (char*)malloc((n=strlen (sp)) + 1);
1997 			strcpy(cp, sp);
1998 			if(ap)
1999 			{
2000 				Namval_t *mp;
2001 				ap->nelem &= ~ARRAY_SCAN;
2002 				if(mp=nv_opensub(np))
2003 					nv_onattr(mp,NV_NOFREE);
2004 			}
2005 			nv_unset(np);
2006 			if(ap)
2007 				ap->nelem |= ARRAY_SCAN;
2008 			if(size==0 && (newatts&(NV_LJUST|NV_RJUST|NV_ZFILL)))
2009 				size = n;
2010 		}
2011 		else
2012 			nv_unset(np);
2013 		nv_setsize(np,size);
2014 		np->nvflag &= NV_ARRAY;
2015 		np->nvflag |= newatts;
2016 		if (cp)
2017 		{
2018 			nv_putval (np, cp, NV_RDONLY);
2019 			free(cp);
2020 		}
2021 	}
2022 	while(ap && nv_nextsub(np));
2023 	if(ap)
2024 		ap->nelem--;
2025 	return;
2026 }
2027 
2028 #ifndef _NEXT_SOURCE
2029 static char *oldgetenv(const char *string)
2030 {
2031 	register char c0,c1;
2032 	register const char *cp, *sp;
2033 	register char **av = environ;
2034 	if(!string || (c0= *string)==0)
2035 		return(0);
2036 	if((c1=*++string)==0)
2037 		c1= '=';
2038 	while(cp = *av++)
2039 	{
2040 		if(cp[0]!=c0 || cp[1]!=c1)
2041 			continue;
2042 		sp = string;
2043 		while(*sp && *sp++ == *++cp);
2044 		if(*sp==0 && *++cp=='=')
2045 			return((char*)(cp+1));
2046 	}
2047 	return(0);
2048 }
2049 
2050 /*
2051  * This version of getenv uses the hash storage to access environment values
2052  */
2053 char *getenv(const char *name)
2054 /*@
2055 	assume name!=0;
2056 @*/
2057 {
2058 	register Namval_t *np;
2059 	if(!sh.var_tree)
2060 		return(oldgetenv(name));
2061 	if((np = nv_search(name,sh.var_tree,0)) && nv_isattr(np,NV_EXPORT))
2062 		return(nv_getval(np));
2063 	if(name[0] == 'P' && name[1] == 'A' && name[2] == 'T' && name[3] == 'H' && name[4] == 0)
2064 		return(oldgetenv(name));
2065 	return(0);
2066 }
2067 #endif /* _NEXT_SOURCE */
2068 
2069 #undef putenv
2070 /*
2071  * This version of putenv uses the hash storage to assign environment values
2072  */
2073 int putenv(const char *name)
2074 {
2075 	register Namval_t *np;
2076 	if(name)
2077 	{
2078 		np = nv_open(name,sh.var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN);
2079 		if(!strchr(name,'='))
2080 			nv_unset(np);
2081 	}
2082 	return(0);
2083 }
2084 
2085 
2086 /*
2087  * Override libast setenv()
2088  */
2089 char* setenviron(const char *name)
2090 {
2091 	register Namval_t *np;
2092 	if(name)
2093 	{
2094 		np = nv_open(name,sh.var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN);
2095 		if(strchr(name,'='))
2096 			return(nv_getval(np));
2097 		nv_unset(np);
2098 	}
2099 	return("");
2100 }
2101 
2102 /*
2103  * copy <str1> to <str2> changing lower case to upper case
2104  * <str2> must be big enough to hold <str1>
2105  * <str1> and <str2> may point to the same place.
2106  */
2107 
2108 static void ltou(register char const *str1,register char *str2)
2109 /*@
2110 	assume str1!=0 && str2!=0;
2111 	return x satisfying strlen(in str1)==strlen(in str2);
2112 @*/
2113 {
2114 	register int c;
2115 	for(; c= *((unsigned char*)str1); str1++,str2++)
2116 	{
2117 		if(islower(c))
2118 			*str2 = toupper(c);
2119 		else
2120 			*str2 = c;
2121 	}
2122 	*str2 = 0;
2123 }
2124 
2125 /*
2126  * normalize <cp> and return pointer to subscript if any
2127  */
2128 static char *lastdot(register char *cp)
2129 {
2130 	register char *dp=cp, *ep=0;
2131 	register int c;
2132 	while(c= *cp++)
2133 	{
2134 		*dp++ = c;
2135 		if(c=='[')
2136 			ep = cp;
2137 		else if(c=='.')
2138 		{
2139 			if(*cp=='[')
2140 			{
2141 				ep = nv_endsubscript((Namval_t*)0,cp,0);
2142 				c = ep-cp;
2143 				memcpy(dp,cp,c);
2144 				dp = sh_checkid(dp+1,dp+c);
2145 				cp = ep;
2146 			}
2147 			ep = 0;
2148 		}
2149 	}
2150 	*dp = 0;
2151 	return(ep);
2152 }
2153 
2154 /*
2155  * Create a reference node from <np> to $np in dictionary <hp>
2156  */
2157 void nv_setref(register Namval_t *np, Dt_t *hp, int flags)
2158 {
2159 	register Namval_t *nq, *nr;
2160 	register char *ep,*cp;
2161 	if(nv_isref(np))
2162 		return;
2163 	if(nv_isarray(np))
2164 		errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np));
2165 	if(!(cp=nv_getval(np)))
2166 		errormsg(SH_DICT,ERROR_exit(1),e_noref,nv_name(np));
2167 	if((ep = lastdot(cp)) && nv_isattr(np,NV_MINIMAL))
2168 		errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np));
2169 	if(!hp)
2170 		hp = sh.var_tree;
2171 	nr= nq = nv_open(cp, hp, flags|NV_NOREF);
2172 	while(nv_isref(nr))
2173 	{
2174 		sh.last_table = nv_reftable(nr);
2175 		hp = nv_reftree(nr);
2176 		nr = nv_refnode(nr);
2177 	}
2178 	if(nr==np)
2179 	{
2180 		if(sh.namespace && nv_dict(sh.namespace)==hp)
2181 			errormsg(SH_DICT,ERROR_exit(1),e_selfref,nv_name(np));
2182 		/* bind to earlier scope, or add to global scope */
2183 		if(!(hp=dtvnext(hp)) || (nq=nv_search((char*)np,hp,NV_ADD|HASH_BUCKET))==np)
2184 			errormsg(SH_DICT,ERROR_exit(1),e_selfref,nv_name(np));
2185 	}
2186 	if(ep)
2187 	{
2188 		/* cause subscript evaluation and return result */
2189 #if 0
2190 		nv_endsubscript(nq,ep,NV_ADD);
2191 #endif
2192 		ep = nv_getsub(nq);
2193 	}
2194 	nv_unset(np);
2195 	np->nvalue.nrp = newof(0,struct Namref,1,0);
2196 	np->nvalue.nrp->np = nq;
2197 	np->nvalue.nrp->root = hp;
2198 	if(ep)
2199 		np->nvalue.nrp->sub = strdup(ep);
2200 	np->nvalue.nrp->table = sh.last_table;
2201 	nv_onattr(np,NV_REF|NV_NOFREE);
2202 }
2203 
2204 /*
2205  * get the scope corresponding to <index>
2206  * whence uses the same values as lseeek()
2207  */
2208 Shscope_t *sh_getscope(int index, int whence)
2209 {
2210 	register struct sh_scoped *sp, *topmost;
2211 	if(whence==SEEK_CUR)
2212 		sp = &sh.st;
2213 	else
2214 	{
2215 		if ((struct sh_scoped*)sh.topscope != sh.st.self)
2216 			topmost = (struct sh_scoped*)sh.topscope;
2217 		else
2218 			topmost = &(sh.st);
2219 		sp = topmost;
2220 		if(whence==SEEK_SET)
2221 		{
2222 			int n =0;
2223 			while(sp = sp->prevst)
2224 				n++;
2225 			index = n - index;
2226 			sp = topmost;
2227 		}
2228 	}
2229 	if(index < 0)
2230 		return((Shscope_t*)0);
2231 	while(index-- && (sp = sp->prevst));
2232 	return((Shscope_t*)sp);
2233 }
2234 
2235 /*
2236  * make <scoped> the top scope and return previous scope
2237  */
2238 Shscope_t *sh_setscope(Shscope_t *scope)
2239 {
2240 	Shscope_t *old = (Shscope_t*)sh.st.self;
2241 	*sh.st.self = sh.st;
2242 	sh.st = *((struct sh_scoped*)scope);
2243 	sh.var_tree = scope->var_tree;
2244 	return(old);
2245 }
2246 
2247 void nv_unscope(void)
2248 {
2249 	register Dt_t *root = sh.var_tree;
2250 	register Dt_t *dp = dtview(root,(Dt_t*)0);
2251 	table_unset(root,NV_RDONLY|NV_NOSCOPE,dp);
2252 	sh.var_tree=dp;
2253 	dtclose(root);
2254 }
2255 
2256 /*
2257  * The inverse of creating a reference node
2258  */
2259 void nv_unref(register Namval_t *np)
2260 {
2261 	Namval_t *nq;
2262 	if(!nv_isref(np))
2263 		return;
2264 	nq = nv_refnode(np);
2265 	nv_offattr(np,NV_NOFREE|NV_REF);
2266 	free((void*)np->nvalue.nrp);
2267 	np->nvalue.cp = strdup(nv_name(nq));
2268 #if SHOPT_OPTIMIZE
2269 	{
2270 		Namfun_t *fp;
2271 		for(fp=nq->nvfun; fp; fp = fp->next)
2272 		{
2273 			if(fp->disc== &optimize_disc)
2274 			{
2275 				optimize_clear(nq,fp);
2276 				return;
2277 			}
2278 		}
2279 	}
2280 #endif
2281 }
2282 
2283 /*
2284  * These following are for binary compatibility with the old hash library
2285  * They will be removed someday
2286  */
2287 
2288 #if defined(__IMPORT__) && defined(__EXPORT__)
2289 #   define extern __EXPORT__
2290 #endif
2291 
2292 #undef	hashscope
2293 
2294 extern Dt_t *hashscope(Dt_t *root)
2295 {
2296 	return(dtvnext(root));
2297 }
2298 
2299 #undef	hashfree
2300 
2301 extern Dt_t	*hashfree(Dt_t *root)
2302 {
2303 	Dt_t *dp = dtvnext(root);
2304 	dtclose(root);
2305 	return(dp);
2306 }
2307 
2308 #undef	hashname
2309 
2310 extern char	*hashname(void *obj)
2311 {
2312 	Namval_t *np = (Namval_t*)obj;
2313 	return(np->nvname);
2314 }
2315 
2316 #undef	hashlook
2317 
2318 extern void *hashlook(Dt_t *root, const char *name, int mode,int size)
2319 {
2320 	NOT_USED(size);
2321 	return((void*)nv_search(name,root,mode));
2322 }
2323 
2324 char *nv_name(register Namval_t *np)
2325 {
2326 	register Namval_t *table;
2327 	register Namfun_t *fp;
2328 	char *cp;
2329 	if(is_abuiltin(np) || is_afunction(np))
2330 		return(np->nvname);
2331 	if(nv_istable(np))
2332 #if 1
2333 		sh.last_table = nv_parent(np);
2334 #else
2335 		sh.last_table = nv_create(np,0, NV_LAST,(Namfun_t*)0);
2336 #endif
2337 	else if(!nv_isref(np))
2338 	{
2339 		for(fp= np->nvfun ; fp; fp=fp->next)
2340 		if(fp->disc && fp->disc->namef)
2341 		{
2342 			if(np==sh.last_table)
2343 				sh.last_table = 0;
2344 			return((*fp->disc->namef)(np,fp));
2345 		}
2346 	}
2347 	if(!(table=sh.last_table) || *np->nvname=='.' || table==sh.namespace || np==table)
2348 		return(np->nvname);
2349 	cp = nv_name(table);
2350 	sfprintf(sh.strbuf,"%s.%s",cp,np->nvname);
2351 	return(sfstruse(sh.strbuf));
2352 }
2353 
2354 Namval_t *nv_lastdict(void)
2355 {
2356 	return(sh.last_table);
2357 }
2358 
2359 #undef nv_context
2360 /*
2361  * returns the data context for a builtin
2362  */
2363 void *nv_context(Namval_t *np)
2364 {
2365 	return((void*)np->nvfun);
2366 }
2367 
2368 #define DISABLE /* proto workaround */
2369 
2370 int nv_isnull DISABLE (register Namval_t *np)
2371 {
2372 	return(nv_isnull(np));
2373 }
2374 
2375 #undef nv_setsize
2376 int nv_setsize(register Namval_t *np, int size)
2377 {
2378 	int oldsize = nv_size(np);
2379 	if(size>=0)
2380 		np->nvsize = size;
2381 	return(oldsize);
2382 }
2383