xref: /titanic_52/usr/src/contrib/ast/src/cmd/ksh93/sh/name.c (revision 906afcb89d0412cc073b95c2d701a804a8cdb62c)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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  * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
27  */
28 
29 #define putenv	___putenv
30 
31 #include	"defs.h"
32 #include	"variables.h"
33 #include	"path.h"
34 #include	"lexstates.h"
35 #include	"timeout.h"
36 #include	"FEATURE/externs"
37 #include	"streval.h"
38 
39 #define NVCACHE		8	/* must be a power of 2 */
40 #define Empty	((char*)(e_sptbnl+3))
41 static char	*savesub = 0;
42 static char	Null[1];
43 static Namval_t	NullNode;
44 static Dt_t	*Refdict;
45 static Dtdisc_t	_Refdisc =
46 {
47 	offsetof(struct Namref,np),sizeof(struct Namval_t*),sizeof(struct Namref)
48 };
49 
50 #if !_lib_pathnative && _lib_uwin_path
51 
52 #define _lib_pathnative		1
53 
54 extern int	uwin_path(const char*, char*, int);
55 
56 size_t
57 pathnative(const char* path, char* buf, size_t siz)
58 {
59 	return uwin_path(path, buf, siz);
60 }
61 
62 #endif /* _lib_pathnative */
63 
64 static void	attstore(Namval_t*,void*);
65 #ifndef _ENV_H
66     static void	pushnam(Namval_t*,void*);
67     static char	*staknam(Namval_t*, char*);
68 #endif
69 static void	rightjust(char*, int, int);
70 static char	*lastdot(char*, int);
71 
72 struct adata
73 {
74 	Shell_t		*sh;
75 	Namval_t	*tp;
76 	char		*mapname;
77 	char		**argnam;
78 	int		attsize;
79 	char		*attval;
80 };
81 
82 #if SHOPT_TYPEDEF
83     struct sh_type
84     {
85 	void		*previous;
86 	Namval_t	**nodes;
87 	Namval_t	*rp;
88 	short		numnodes;
89 	short		maxnodes;
90     };
91 #endif /*SHOPT_TYPEDEF */
92 
93 #if NVCACHE
94     struct Namcache
95     {
96 	struct Cache_entry
97 	{
98 		Dt_t		*root;
99 		Dt_t		*last_root;
100 		char		*name;
101 		Namval_t	*np;
102 		Namval_t	*last_table;
103 		Namval_t	*namespace;
104 		int		flags;
105 		short		size;
106 		short		len;
107 	} entries[NVCACHE];
108 	short		index;
109 	short		ok;
110     };
111     static struct Namcache nvcache;
112 #endif
113 
114 char		nv_local = 0;
115 #ifndef _ENV_H
116 static void(*nullscan)(Namval_t*,void*);
117 #endif
118 
119 #if ( SFIO_VERSION  <= 20010201L )
120 #   define _data        data
121 #endif
122 
123 #if !SHOPT_MULTIBYTE
124 #   define mbchar(p)       (*(unsigned char*)p++)
125 #endif /* SHOPT_MULTIBYTE */
126 
127 /* ========	name value pair routines	======== */
128 
129 #include	"shnodes.h"
130 #include	"builtins.h"
131 
132 static char *getbuf(size_t len)
133 {
134 	static char *buf;
135 	static size_t buflen;
136 	if(buflen < len)
137 	{
138 		if(buflen==0)
139 			buf = (char*)malloc(len);
140 		else
141 			buf = (char*)realloc(buf,len);
142 		buflen = len;
143 	}
144 	return(buf);
145 }
146 
147 #ifdef _ENV_H
148 void sh_envput(Env_t* ep,Namval_t *np)
149 {
150 	int offset = staktell();
151 	Namarr_t *ap = nv_arrayptr(np);
152 	char *val;
153 	if(ap)
154 	{
155 		if(ap->nelem&ARRAY_UNDEF)
156 			nv_putsub(np,"0",0L);
157 		else if(!(val=nv_getsub(np)) || strcmp(val,"0"))
158 			return;
159 	}
160 	if(!(val = nv_getval(np)))
161 		return;
162 	stakputs(nv_name(np));
163 	stakputc('=');
164 	stakputs(val);
165 	stakseek(offset);
166 	env_add(ep,stakptr(offset),ENV_STRDUP);
167 }
168 #endif
169 
170 /*
171  * output variable name in format for re-input
172  */
173 void nv_outname(Sfio_t *out, char *name, int len)
174 {
175 	const char *cp=name, *sp;
176 	int c, offset = staktell();
177 	while(sp= strchr(cp,'['))
178 	{
179 		if(len>0 && cp+len <= sp)
180 			break;
181 		sfwrite(out,cp,++sp-cp);
182 		stakseek(offset);
183 		while(c= *sp++)
184 		{
185 			if(c==']')
186 				break;
187 			else if(c=='\\')
188 			{
189 				if(*sp=='[' || *sp==']' || *sp=='\\')
190 					c = *sp++;
191 			}
192 			stakputc(c);
193 		}
194 		stakputc(0);
195 		sfputr(out,sh_fmtq(stakptr(offset)),-1);
196 		if(len>0)
197 		{
198 			sfputc(out,']');
199 			return;
200 		}
201 		cp = sp-1;
202 	}
203 	if(*cp)
204 	{
205 		if(len>0)
206 			sfwrite(out,cp,len);
207 		else
208 			sfputr(out,cp,-1);
209 	}
210 	stakseek(offset);
211 }
212 
213 #if SHOPT_TYPEDEF
214 Namval_t *nv_addnode(Namval_t* np, int remove)
215 {
216 	Shell_t			*shp = sh_getinterp();
217 	register struct sh_type	*sp = (struct sh_type*)shp->mktype;
218 	register int		i;
219 	register char		*name=0;
220 	if(sp->numnodes==0 && !nv_isnull(np) && shp->last_table)
221 	{
222 		/* could be an redefine */
223 		Dt_t *root = nv_dict(shp->last_table);
224 		sp->rp = np;
225 		nv_delete(np,root,NV_NOFREE);
226 		np = nv_search(sp->rp->nvname,root,NV_ADD);
227 	}
228 	if(sp->numnodes && memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1))
229 	{
230 		name = (sp->nodes[0])->nvname;
231 		i = strlen(name);
232 		if(memcmp(np->nvname,name,i))
233 			return(np);
234 	}
235 	if(sp->rp && sp->numnodes)
236 	{
237 		/* check for a redefine */
238 		if(name && np->nvname[i]=='.' && np->nvname[i+1]=='_' && np->nvname[i+2]==0)
239 			sp->rp = 0;
240 		else
241 		{
242 			Dt_t *root = nv_dict(shp->last_table);
243 			nv_delete(sp->nodes[0],root,NV_NOFREE);
244 			dtinsert(root,sp->rp);
245 			errormsg(SH_DICT,ERROR_exit(1),e_redef,sp->nodes[0]->nvname);
246 		}
247 	}
248 	for(i=0; i < sp->numnodes; i++)
249 	{
250 		if(np == sp->nodes[i])
251 		{
252 			if(remove)
253 			{
254 				while(++i < sp->numnodes)
255 					sp->nodes[i-1] = sp->nodes[i];
256 				sp->numnodes--;
257 			}
258 			return(np);
259 		}
260 	}
261 	if(remove)
262 		return(np);
263 	if(sp->numnodes==sp->maxnodes)
264 	{
265 		sp->maxnodes += 20;
266 		sp->nodes = (Namval_t**)realloc(sp->nodes,sizeof(Namval_t*)*sp->maxnodes);
267 	}
268 	sp->nodes[sp->numnodes++] = np;
269 	return(np);
270 }
271 #endif /* SHOPT_TYPEDEF */
272 
273 /*
274  * given a list of assignments, determine <name> is on the list
275    returns a pointer to the argnod on the list or NULL
276  */
277 struct argnod *nv_onlist(struct argnod *arg, const char *name)
278 {
279 	char *cp;
280 	int len = strlen(name);
281 	for(;arg; arg=arg->argnxt.ap)
282 	{
283 		if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED|ARG_MESSAGE)))
284 			cp = ((struct fornod*)arg->argchn.ap)->fornam;
285 		else
286 			cp = arg->argval;
287 		if(memcmp(cp,name,len)==0 && (cp[len]==0 || cp[len]=='='))
288 			return(arg);
289 	}
290 	return(0);
291 }
292 
293 /*
294  * Perform parameter assignment for a linked list of parameters
295  * <flags> contains attributes for the parameters
296  */
297 void nv_setlist(register struct argnod *arg,register int flags, Namval_t *typ)
298 {
299 	Shell_t		*shp = sh_getinterp();
300 	register char	*cp;
301 	register Namval_t *np, *mp;
302 	char		*trap=shp->st.trap[SH_DEBUGTRAP];
303 	char		*prefix = shp->prefix;
304 	int		traceon = (sh_isoption(SH_XTRACE)!=0);
305 	int		array = (flags&(NV_ARRAY|NV_IARRAY));
306 	Namarr_t	*ap;
307 	Namval_t	node;
308 	struct Namref	nr;
309 #if SHOPT_TYPEDEF
310 	int		maketype = flags&NV_TYPE;
311 	struct sh_type	shtp;
312 	if(maketype)
313 	{
314 		shtp.previous = shp->mktype;
315 		shp->mktype=(void*)&shtp;
316 		shtp.numnodes=0;
317 		shtp.maxnodes = 20;
318 		shtp.rp = 0;
319 		shtp.nodes =(Namval_t**)malloc(shtp.maxnodes*sizeof(Namval_t*));
320 	}
321 #endif /* SHOPT_TYPEDEF*/
322 #if SHOPT_NAMESPACE
323 	if(shp->namespace && nv_dict(shp->namespace)==shp->var_tree)
324 		flags |= NV_NOSCOPE;
325 #endif /* SHOPT_NAMESPACE */
326 	flags &= ~(NV_TYPE|NV_ARRAY|NV_IARRAY);
327 	if(sh_isoption(SH_ALLEXPORT))
328 		flags |= NV_EXPORT;
329 	if(shp->prefix)
330 	{
331 		flags &= ~(NV_IDENT|NV_EXPORT);
332 		flags |= NV_VARNAME;
333 	}
334 	else
335 		shp->prefix_root = shp->first_root = 0;
336 	for(;arg; arg=arg->argnxt.ap)
337 	{
338 		shp->used_pos = 0;
339 		if(arg->argflag&ARG_MAC)
340 		{
341 			shp->prefix = 0;
342 			cp = sh_mactrim(shp,arg->argval,(flags&NV_NOREF)?-3:-1);
343 			shp->prefix = prefix;
344 		}
345 		else
346 		{
347 			stakseek(0);
348 			if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED|ARG_MESSAGE)))
349 			{
350 				int flag = (NV_VARNAME|NV_ARRAY|NV_ASSIGN);
351 				int sub=0;
352 				struct fornod *fp=(struct fornod*)arg->argchn.ap;
353 				register Shnode_t *tp=fp->fortre;
354 				flag |= (flags&(NV_NOSCOPE|NV_STATIC|NV_FARRAY));
355 				if(arg->argflag&ARG_QUOTED)
356 					cp = sh_mactrim(shp,fp->fornam,-1);
357 				else
358 					cp = fp->fornam;
359 				error_info.line = fp->fortyp-shp->st.firstline;
360 				if(!array && tp->tre.tretyp!=TLST && tp->com.comset && !tp->com.comarg && tp->com.comset->argval[0]==0 && tp->com.comset->argval[1]=='[')
361 					array |= (tp->com.comset->argflag&ARG_MESSAGE)?NV_IARRAY:NV_ARRAY;
362 				if(prefix && tp->com.comset && *cp=='[')
363 				{
364 					shp->prefix = 0;
365 					np = nv_open(prefix,shp->last_root,flag);
366 					shp->prefix = prefix;
367 					if(np)
368 					{
369 						if(nv_isvtree(np) && !nv_isarray(np))
370 						{
371 							stakputc('.');
372 							stakputs(cp);
373 							cp = stakfreeze(1);
374 						}
375 						nv_close(np);
376 					}
377 				}
378 				np = nv_open(cp,shp->var_tree,flag|NV_ASSIGN);
379 				if((arg->argflag&ARG_APPEND) && (tp->tre.tretyp&COMMSK)==TCOM && tp->com.comset && !nv_isvtree(np) && (((ap=nv_arrayptr(np)) && !ap->fun && !nv_opensub(np))  || (!ap && nv_isarray(np) && tp->com.comarg && !((mp=nv_search(tp->com.comarg->argval,shp->fun_tree,0)) && nv_isattr(mp,BLT_DCL)))))
380 				{
381 					if(tp->com.comarg)
382 					{
383 						struct argnod *ap = tp->com.comset;
384 						while(ap->argnxt.ap)
385 							ap = ap->argnxt.ap;
386 						ap->argnxt.ap = tp->com.comarg;
387 
388 					}
389 					tp->com.comarg = tp->com.comset;
390 					tp->com.comset = 0;
391 					tp->com.comtyp = COMSCAN;
392 				}
393 				if(nv_isattr(np,NV_RDONLY) && np->nvfun && !(flags&NV_RDONLY))
394 					errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np));
395 				if(nv_isattr(np,NV_NOFREE) && nv_isnull(np))
396 					nv_offattr(np,NV_NOFREE);
397 				if(nv_istable(np))
398 					_nv_unset(np,0);
399 				if(typ && !array  && (!shp->prefix || nv_isnull(np) || nv_isarray(np)))
400 				{
401 					if(!(nv_isnull(np)) && !nv_isarray(np))
402 						_nv_unset(np,0);
403 					 nv_settype(np,typ,0);
404 				}
405 				if((flags&NV_STATIC) && !nv_isattr(np,NV_EXPORT) && !nv_isnull(np))
406 #if SHOPT_TYPEDEF
407 					goto check_type;
408 #else
409 					continue;
410 #endif /* SHOPT_TYPEDEF */
411 				ap=nv_arrayptr(np);
412 #if SHOPT_FIXEDARRAY
413 				if(ap && ap->fixed)
414 					flags |= NV_FARRAY;
415 #endif /* SHOPT_FIXEDARRAY */
416 				if(array && (!ap || !ap->hdr.type))
417 				{
418 #if SHOPT_FIXEDARRAY
419 					if(!(arg->argflag&ARG_APPEND) && (!ap || !ap->fixed))
420 #else
421 					if(!(arg->argflag&ARG_APPEND))
422 #endif /* SHOPT_FIXEDARRAY */
423 						_nv_unset(np,NV_EXPORT);
424 					if(array&NV_ARRAY)
425 					{
426 						nv_setarray(np,nv_associative);
427 					}
428 					else
429 					{
430 						nv_onattr(np,NV_ARRAY);
431 					}
432 				}
433 				if(array && tp->tre.tretyp!=TLST && !tp->com.comset && !tp->com.comarg)
434 				{
435 #if SHOPT_TYPEDEF
436 						goto check_type;
437 #else
438 						continue;
439 #endif /* SHOPT_TYPEDEF */
440 				}
441 				/* check for array assignment */
442 				if(tp->tre.tretyp!=TLST && tp->com.comarg && !tp->com.comset && ((array&NV_IARRAY) || !((mp=tp->com.comnamp) && nv_isattr(mp,BLT_DCL))))
443 				{
444 					int argc;
445 					Dt_t	*last_root = shp->last_root;
446 					char **argv = sh_argbuild(shp,&argc,&tp->com,0);
447 					shp->last_root = last_root;
448 #if SHOPT_TYPEDEF
449 					if(shp->mktype && shp->dot_depth==0 && np==((struct sh_type*)shp->mktype)->nodes[0])
450 					{
451 						shp->mktype = 0;
452 						errormsg(SH_DICT,ERROR_exit(1),"%s: not a known type name",argv[0]);
453 					}
454 #endif /* SHOPT_TYPEDEF */
455 					if(!(arg->argflag&ARG_APPEND))
456 					{
457 #if SHOPT_FIXEDARRAY
458 						if(!nv_isarray(np) || ((ap=nv_arrayptr(np)) && !ap->fixed && (ap->nelem&ARRAY_MASK)))
459 #else
460 						if(!nv_isarray(np) || ((ap=nv_arrayptr(np)) && (ap->nelem&ARRAY_MASK)))
461 #endif /* SHOPT_FIXEDARRAY */
462 						{
463 							if(ap)
464 								ap->nelem |= ARRAY_UNDEF;
465 							_nv_unset(np,NV_EXPORT);
466 						}
467 					}
468 					nv_setvec(np,(arg->argflag&ARG_APPEND),argc,argv);
469 					if(traceon || trap)
470 					{
471 						int n = -1;
472 						char *name = nv_name(np);
473 						if(arg->argflag&ARG_APPEND)
474 							n = '+';
475 						if(trap)
476 							sh_debug(shp,trap,name,(char*)0,argv,(arg->argflag&ARG_APPEND)|ARG_ASSIGN);
477 						if(traceon)
478 						{
479 							sh_trace(shp,NIL(char**),0);
480 							sfputr(sfstderr,name,n);
481 							sfwrite(sfstderr,"=( ",3);
482 							while(cp= *argv++)
483 								sfputr(sfstderr,sh_fmtq(cp),' ');
484 							sfwrite(sfstderr,")\n",2);
485 						}
486 					}
487 #if SHOPT_TYPEDEF
488 					goto check_type;
489 #else
490 					continue;
491 #endif /* SHOPT_TYPEDEF */
492 				}
493 				if((tp->tre.tretyp&COMMSK)==TFUN)
494 					goto skip;
495 				if(tp->tre.tretyp==0 && !tp->com.comset && !tp->com.comarg)
496 				{
497 					if(!(arg->argflag&ARG_APPEND) && nv_isattr(np,NV_BINARY|NV_NOFREE|NV_RAW)!=(NV_BINARY|NV_NOFREE|NV_RAW))
498 						_nv_unset(np,NV_EXPORT);
499 					goto skip;
500 				}
501 				if(tp->tre.tretyp==TLST || !tp->com.comset || tp->com.comset->argval[0]!='[')
502 				{
503 					if(tp->tre.tretyp!=TLST && !tp->com.comnamp && tp->com.comset && tp->com.comset->argval[0]==0 && tp->com.comset->argchn.ap)
504 					{
505 						if(prefix || np)
506 							cp = stakcopy(nv_name(np));
507 						shp->prefix = cp;
508 						if(tp->com.comset->argval[1]=='[')
509 						{
510 							if((arg->argflag&ARG_APPEND) && (!nv_isarray(np) || (nv_aindex(np)>=0)))
511 								_nv_unset(np,0);
512 							if(!(array&NV_IARRAY) && !(tp->com.comset->argflag&ARG_MESSAGE))
513 								nv_setarray(np,nv_associative);
514 						}
515 						nv_setlist(tp->com.comset,flags&~NV_STATIC,0);
516 						shp->prefix = prefix;
517 						if(tp->com.comset->argval[1]!='[')
518 							 nv_setvtree(np);
519 						nv_close(np);
520 #if SHOPT_TYPEDEF
521 						goto check_type;
522 #else
523 						continue;
524 #endif /* SHOPT_TYPEDEF */
525 					}
526 					if(*cp!='.' && *cp!='[' && strchr(cp,'['))
527 					{
528 						cp = stakcopy(nv_name(np));
529 						nv_close(np);
530 						if(!(arg->argflag&ARG_APPEND))
531 							flag &= ~NV_ARRAY;
532 						shp->prefix_root = shp->first_root;
533 						np = nv_open(cp,shp->prefix_root?shp->prefix_root:shp->var_tree,flag);
534 					}
535 					if(arg->argflag&ARG_APPEND)
536 					{
537 						if(nv_isarray(np))
538 						{
539 							if((sub=nv_aimax(np)) < 0  && nv_arrayptr(np))
540 								errormsg(SH_DICT,ERROR_exit(1),e_badappend,nv_name(np));
541 							if(sub>=0)
542 								sub++;
543 						}
544 						if(!nv_isnull(np) && np->nvalue.cp!=Empty && !nv_isvtree(np))
545 							sub=1;
546 					}
547 					else if(((np->nvalue.cp && np->nvalue.cp!=Empty)||nv_isvtree(np)|| nv_arrayptr(np)) && !nv_type(np))
548 					{
549 						_nv_unset(np,NV_EXPORT);
550 						if(ap && ap->fun)
551 							 nv_setarray(np,nv_associative);
552 
553 					}
554 				}
555 				else
556 				{
557 					if(!(arg->argflag&ARG_APPEND))
558 						_nv_unset(np,NV_EXPORT);
559 					if(!sh_isoption(SH_BASH) && !(array&NV_IARRAY) && !nv_isarray(np))
560 						nv_setarray(np,nv_associative);
561 				}
562 			skip:
563 				if(sub>0)
564 				{
565 					sfprintf(stkstd,"%s[%d]",prefix?nv_name(np):cp,sub);
566 					shp->prefix = stakfreeze(1);
567 					nv_putsub(np,(char*)0,ARRAY_ADD|ARRAY_FILL|sub);
568 				}
569 				else if(prefix)
570 					shp->prefix = stakcopy(nv_name(np));
571 				else
572 					shp->prefix = cp;
573 				shp->last_table = 0;
574 				if(shp->prefix)
575 				{
576 					if(*shp->prefix=='_' && shp->prefix[1]=='.' && nv_isref(L_ARGNOD))
577 					{
578 						sfprintf(stkstd,"%s%s",nv_name(L_ARGNOD->nvalue.nrp->np),shp->prefix+1);
579 						shp->prefix = stkfreeze(stkstd,1);
580 					}
581 					memset(&nr,0,sizeof(nr));
582 					memcpy(&node,L_ARGNOD,sizeof(node));
583 					L_ARGNOD->nvalue.nrp = &nr;
584 					nr.np = np;
585 					nr.root = shp->last_root;
586 					nr.table = shp->last_table;
587 					L_ARGNOD->nvflag = NV_REF|NV_NOFREE;
588 					L_ARGNOD->nvfun = 0;
589 				}
590 				sh_exec(tp,sh_isstate(SH_ERREXIT));
591 #if SHOPT_TYPEDEF
592 				if(shp->prefix)
593 #endif
594 				{
595 					L_ARGNOD->nvalue.nrp = node.nvalue.nrp;
596 					L_ARGNOD->nvflag = node.nvflag;
597 					L_ARGNOD->nvfun = node.nvfun;
598 				}
599 				shp->prefix = prefix;
600 				if(nv_isarray(np) && (mp=nv_opensub(np)))
601 					np = mp;
602 				while(tp->tre.tretyp==TLST)
603 				{
604 					if(!tp->lst.lstlef || !tp->lst.lstlef->tre.tretyp==TCOM || tp->lst.lstlef->com.comarg || tp->lst.lstlef->com.comset && tp->lst.lstlef->com.comset->argval[0]!='[')
605 						break;
606 					tp = tp->lst.lstrit;
607 
608 				}
609 				if(!nv_isarray(np) && !typ && (tp->com.comarg || !tp->com.comset || tp->com.comset->argval[0]!='['))
610 				{
611 					nv_setvtree(np);
612 					if(tp->com.comarg || tp->com.comset)
613 						np->nvfun->dsize = 0;
614 				}
615 #if SHOPT_TYPEDEF
616 				goto check_type;
617 #else
618 				continue;
619 #endif /* SHOPT_TYPEDEF */
620 			}
621 			cp = arg->argval;
622 			mp = 0;
623 		}
624 		np = nv_open(cp,shp->prefix_root?shp->prefix_root:shp->var_tree,flags);
625 		if(!np->nvfun && (flags&NV_NOREF))
626 		{
627 			if(shp->used_pos)
628 				nv_onattr(np,NV_PARAM);
629 			else
630 				nv_offattr(np,NV_PARAM);
631 		}
632 		if(traceon || trap)
633 		{
634 			register char *sp=cp;
635 			char *name=nv_name(np);
636 			char *sub=0;
637 			int append = 0;
638 			if(nv_isarray(np))
639 				sub = savesub;
640 			if(cp=lastdot(sp,'='))
641 			{
642 				if(cp[-1]=='+')
643 					append = ARG_APPEND;
644 				cp++;
645 			}
646 			if(traceon)
647 			{
648 				sh_trace(shp,NIL(char**),0);
649 				nv_outname(sfstderr,name,-1);
650 				if(sub)
651 					sfprintf(sfstderr,"[%s]",sh_fmtq(sub));
652 				if(cp)
653 				{
654 					if(append)
655 						sfputc(sfstderr,'+');
656 					sfprintf(sfstderr,"=%s\n",sh_fmtq(cp));
657 				}
658 			}
659 			if(trap)
660 			{
661 					char *av[2];
662 					av[0] = cp;
663 					av[1] = 0;
664 					sh_debug(shp,trap,name,sub,av,append);
665 			}
666 		}
667 #if SHOPT_TYPEDEF
668 	check_type:
669 		if(maketype)
670 		{
671 			nv_open(shtp.nodes[0]->nvname,shp->var_tree,NV_ASSIGN|NV_VARNAME|NV_NOADD|NV_NOFAIL);
672 			np = nv_mktype(shtp.nodes,shtp.numnodes);
673 			free((void*)shtp.nodes);
674 			shp->mktype = shtp.previous;
675 			maketype = 0;
676 			if(shp->namespace)
677 				free(shp->prefix);
678 			shp->prefix = 0;
679 			if(nr.np == np)
680 			{
681 				L_ARGNOD->nvalue.nrp = node.nvalue.nrp;
682 				L_ARGNOD->nvflag = node.nvflag;
683 				L_ARGNOD->nvfun = node.nvfun;
684 			}
685 		}
686 #endif /* SHOPT_TYPEDEF */
687 	}
688 }
689 
690 /*
691  * copy the subscript onto the stack
692  */
693 static void stak_subscript(const char *sub, int last)
694 {
695 	register int c;
696 	stakputc('[');
697 	while(c= *sub++)
698 	{
699 		if(c=='[' || c==']' || c=='\\')
700 			stakputc('\\');
701 		stakputc(c);
702 	}
703 	stakputc(last);
704 }
705 
706 /*
707  * construct a new name from a prefix and base name on the stack
708  */
709 static char *copystack(const char *prefix, register const char *name, const char *sub)
710 {
711 	register int last=0,offset = staktell();
712 	if(prefix)
713 	{
714 		stakputs(prefix);
715 		if(*stakptr(staktell()-1)=='.')
716 			stakseek(staktell()-1);
717 		if(*name=='.' && name[1]=='[')
718 			last = staktell()+2;
719 		if(*name!='['  && *name!='.' && *name!='=' && *name!='+')
720 			stakputc('.');
721 		if(*name=='.' && (name[1]=='=' || name[1]==0))
722 			stakputc('.');
723 	}
724 	if(last)
725 	{
726 		stakputs(name);
727 		if(sh_checkid(stakptr(last),(char*)0))
728 			stakseek(staktell()-2);
729 	}
730 	if(sub)
731 		stak_subscript(sub,']');
732 	if(!last)
733 		stakputs(name);
734 	stakputc(0);
735 	return(stakptr(offset));
736 }
737 
738 /*
739  * grow this stack string <name> by <n> bytes and move from cp-1 to end
740  * right by <n>.  Returns beginning of string on the stack
741  */
742 static char *stack_extend(const char *cname, char *cp, int n)
743 {
744 	register char *name = (char*)cname;
745 	int offset = name - stakptr(0);
746 	int m = cp-name;
747 	stakseek(offset + strlen(name)+n+1);
748 	name = stakptr(offset);
749 	cp =  name + m;
750 	m = strlen(cp)+1;
751 	while(m-->0)
752 		cp[n+m]=cp[m];
753 	return((char*)name);
754 }
755 
756 Namval_t *nv_create(const char *name,  Dt_t *root, int flags, Namfun_t *dp)
757 {
758 	Shell_t			*shp = sh_getinterp();
759 	char			*sub=0, *cp=(char*)name, *sp, *xp;
760 	register int		c;
761 	register Namval_t	*np=0, *nq=0;
762 	Namfun_t		*fp=0;
763 	long			mode, add=0;
764 	int			copy=0,isref,top=0,noscope=(flags&NV_NOSCOPE);
765 	int			nofree=0, level=0;
766 #if SHOPT_FIXEDARRAY
767 	Namarr_t		*ap;
768 #endif /* SHOPT_FIXEDARRAY */
769 	if(root==shp->var_tree)
770 	{
771 		if(dtvnext(root))
772 			top = 1;
773 		else
774 			flags &= ~NV_NOSCOPE;
775 	}
776 	if(!dp->disc)
777 		copy = dp->nofree&1;
778 	if(*cp=='.')
779 		cp++;
780 	while(1)
781 	{
782 		switch(c = *(unsigned char*)(sp = cp))
783 		{
784 		    case '[':
785 			if(flags&NV_NOARRAY)
786 			{
787 				dp->last = cp;
788 				return(np);
789 			}
790 			cp = nv_endsubscript((Namval_t*)0,sp,0);
791 			if(sp==name || sp[-1]=='.')
792 				c = *(sp = cp);
793 			goto skip;
794 		    case '.':
795 			if(flags&NV_IDENT)
796 				return(0);
797 			if(root==shp->var_tree)
798 				flags &= ~NV_EXPORT;
799 			if(!copy && !(flags&NV_NOREF))
800 			{
801 				c = sp-name;
802 				copy = cp-name;
803 				dp->nofree |= 1;
804 				name = copystack((const char*)0, name,(const char*)0);
805 				cp = (char*)name+copy;
806 				sp = (char*)name+c;
807 				c = '.';
808 			}
809 			/* FALLTHROUGH */
810 		skip:
811 		    case '+':
812 		    case '=':
813 			*sp = 0;
814 			/* FALLTHROUGH */
815 		    case 0:
816 			isref = 0;
817 			dp->last = cp;
818 			mode =  (c=='.' || (flags&NV_NOADD))?add:NV_ADD;
819 			if(level++ || ((flags&NV_NOSCOPE) && c!='.'))
820 				mode |= HASH_NOSCOPE;
821 			np=0;
822 			if(top)
823 			{
824 				struct Ufunction *rp;
825 				if((rp=shp->st.real_fun) && !rp->sdict && (flags&NV_STATIC))
826 				{
827 					Dt_t *dp = dtview(shp->var_tree,(Dt_t*)0);
828 					rp->sdict = dtopen(&_Nvdisc,Dtoset);
829 					dtview(rp->sdict,dp);
830 					dtview(shp->var_tree,rp->sdict);
831 				}
832 				if(np = nv_search(name,shp->var_tree,0))
833 				{
834 #if SHOPT_NAMESPACE
835 					if(shp->var_tree->walk==shp->var_base || (shp->var_tree->walk!=shp->var_tree && shp->namespace &&  nv_dict(shp->namespace)==shp->var_tree->walk))
836 #else
837 					if(shp->var_tree->walk==shp->var_base)
838 #endif /* SHOPT_NAMESPACE */
839 					{
840 #if SHOPT_NAMESPACE
841 						if(!(nq = nv_search((char*)np,shp->var_base,HASH_BUCKET)))
842 #endif /* SHOPT_NAMESPACE */
843 						nq = np;
844 						shp->last_root = shp->var_tree->walk;
845 						if((flags&NV_NOSCOPE) && *cp!='.')
846 						{
847 							if(mode==0)
848 								root = shp->var_tree->walk;
849 							else
850 							{
851 								nv_delete(np,(Dt_t*)0,NV_NOFREE);
852 								np = 0;
853 							}
854 						}
855 					}
856 					else
857 					{
858 						if(shp->var_tree->walk)
859 							root = shp->var_tree->walk;
860 						flags |= NV_NOSCOPE;
861 						noscope = 1;
862 					}
863 				}
864 				if(rp && rp->sdict && (flags&NV_STATIC))
865 				{
866 					root = rp->sdict;
867 					if(np && shp->var_tree->walk==shp->var_tree)
868 					{
869 						_nv_unset(np,0);
870 						nv_delete(np,shp->var_tree,0);
871 						np = 0;
872 					}
873 					if(!np || shp->var_tree->walk!=root)
874 						np =  nv_search(name,root,HASH_NOSCOPE|NV_ADD);
875 				}
876 			}
877 #if SHOPT_NAMESPACE
878 			if(!np && !noscope && *name!='.' && shp->namespace && root==shp->var_tree)
879 				root = nv_dict(shp->namespace);
880 #endif /* SHOPT_NAMESPACE */
881 			if(np ||  (np = nv_search(name,root,mode)))
882 			{
883 				isref = nv_isref(np);
884 				shp->openmatch = root->walk?root->walk:root;
885 				if(top)
886 				{
887 					if(nq==np)
888 					{
889 						flags &= ~NV_NOSCOPE;
890 						root = shp->last_root;
891 					}
892 					else if(nq)
893 					{
894 						if(nv_isnull(np) && c!='.' && ((np->nvfun=nv_cover(nq)) || nq==OPTINDNOD))
895 						{
896 							np->nvname = nq->nvname;
897 #if SHOPT_NAMESPACE
898 							if(shp->namespace && nv_dict(shp->namespace)==shp->var_tree && nv_isattr(nq,NV_EXPORT))
899 								nv_onattr(np,NV_EXPORT);
900 #endif /* SHOPT_NAMESPACE */
901 							if(nq==OPTINDNOD)
902 							{
903 								np->nvfun = nq->nvfun;
904 								np->nvalue.lp = (&shp->st.optindex);
905 								nv_onattr(np,NV_INTEGER|NV_NOFREE);
906 							}
907 						}
908 						flags |= NV_NOSCOPE;
909 					}
910 				}
911 				else if(add && nv_isnull(np) && c=='.' && cp[1]!='.')
912 					nv_setvtree(np);
913 #if SHOPT_NAMESPACE
914 				if(shp->namespace && root==nv_dict(shp->namespace))
915 				{
916 					flags |= NV_NOSCOPE;
917 					shp->last_table = shp->namespace;
918 				}
919 #endif /* SHOPT_NAMESPACE */
920 			}
921 			if(c)
922 				*sp = c;
923 			top = 0;
924 			if(isref)
925 			{
926 #if SHOPT_FIXEDARRAY
927 				int n=0,dim;
928 #endif /* SHOPT_FIXEDARRAY */
929 #if NVCACHE
930 				nvcache.ok = 0;
931 #endif
932 				if(c=='.') /* don't optimize */
933 					shp->argaddr = 0;
934 				else if((flags&NV_NOREF) && (c!='[' && *cp!='.'))
935 				{
936 					if(c && !(flags&NV_NOADD))
937 						nv_unref(np);
938 					return(np);
939 				}
940 				while(nv_isref(np) && np->nvalue.cp)
941 				{
942 					root = nv_reftree(np);
943 					shp->last_root = root;
944 					shp->last_table = nv_reftable(np);
945 					sub = nv_refsub(np);
946 #if SHOPT_FIXEDARRAY
947 					n = nv_refindex(np);
948 					dim = nv_refdimen(np);
949 #endif /* SHOPT_FIXEDARRAY */
950 					np = nv_refnode(np);
951 #if SHOPT_FIXEDARRAY
952 					if(n)
953 					{
954 						ap = nv_arrayptr(np);
955 						ap->nelem = dim;
956 						nv_putsub(np,(char*)0,n);
957 					}
958 					else
959 #endif /* SHOPT_FIXEDARRAY */
960 					if(sub && c!='.')
961 						nv_putsub(np,sub,0L);
962 					flags |= NV_NOSCOPE;
963 					noscope = 1;
964 				}
965 				shp->first_root = root;
966 				if(nv_isref(np) && (c=='[' || c=='.' || !(flags&NV_ASSIGN)))
967 					errormsg(SH_DICT,ERROR_exit(1),e_noref,nv_name(np));
968 				if(sub && c==0)
969 				{
970 					if(flags&NV_ARRAY)
971 					{
972 						Namarr_t *ap = nv_arrayptr(np);
973 						nq = nv_opensub(np);
974 						if((flags&NV_ASSIGN) && (!nq || nv_isnull(nq)))
975 							ap->nelem++;
976 						if(!nq)
977 							goto addsub;
978 						else
979 							np = nq;
980 					}
981 					return(np);
982 				}
983 				if(np==nq)
984 					flags &= ~(noscope?0:NV_NOSCOPE);
985 #if SHOPT_FIXEDARRAY
986 				else if(c || n)
987 #else
988 				else if(c)
989 #endif /* SHOPT_FIXEDARRAY */
990 				{
991 #if SHOPT_FIXEDARRAY
992 					static char null[1] = "";
993 #endif /* SHOPT_FIXEDARRAY */
994 					c = (cp-sp);
995 					copy = strlen(cp=nv_name(np));
996 					dp->nofree |= 1;
997 #if SHOPT_FIXEDARRAY
998 					if(*sp==0)
999 						name = cp;
1000 					else
1001 #endif /* SHOPT_FIXEDARRAY */
1002 						name = copystack(cp,sp,sub);
1003 					sp = (char*)name + copy;
1004 					cp = sp+c;
1005 					c = *sp;
1006 					if(!noscope)
1007 						flags &= ~NV_NOSCOPE;
1008 #if SHOPT_FIXEDARRAY
1009 					if(c==0)
1010 						nv_endsubscript(np,null,NV_ADD);
1011 #endif /* SHOPT_FIXEDARRAY */
1012 				}
1013 				flags |= NV_NOREF;
1014 				if(*cp==0 &&  nv_isnull(np) && !nv_isarray(np))
1015 					nofree = NV_NOFREE;
1016 			}
1017 			shp->last_root = root;
1018 			if(*cp && cp[1]=='.')
1019 				cp++;
1020 			if(c=='.' && (cp[1]==0 ||  cp[1]=='=' || cp[1]=='+'))
1021 			{
1022 				nv_local = 1;
1023 				if(np)
1024 					nv_onattr(np,nofree);
1025 				return(np);
1026 			}
1027 			if(cp[-1]=='.')
1028 				cp--;
1029 			do
1030 			{
1031 #if SHOPT_FIXEDARRAY
1032 				int fixed;
1033 #endif /* SHOPT_FIXEDARRAY */
1034 				if(!np)
1035 				{
1036 					if(!nq && *sp=='[' && *cp==0 && cp[-1]==']')
1037 					{
1038 						/*
1039 						 * for backward compatibility
1040 						 * evaluate subscript for
1041 						 * possible side effects
1042 						 */
1043 						cp[-1] = 0;
1044 						sh_arith(shp,sp+1);
1045 						cp[-1] = ']';
1046 					}
1047 					return(np);
1048 				}
1049 #if SHOPT_FIXEDARRAY
1050 				fixed = 0;
1051 				if((ap=nv_arrayptr(np)) && ap->fixed)
1052 					fixed = 1;
1053 #endif /* SHOPT_FIXEDARRAY */
1054 				if(c=='[' || (c=='.' && nv_isarray(np)))
1055 				{
1056 					int n = 0;
1057 					sub = 0;
1058 					mode &= ~HASH_NOSCOPE;
1059 					if(c=='[')
1060 					{
1061 #if SHOPT_FIXEDARRAY
1062 						Namarr_t *ap = nv_arrayptr(np);
1063 #endif /* SHOPT_FIXEDARRAY */
1064 #if 0
1065 						int scan = ap?(ap->nelem&ARRAY_SCAN):0;
1066 #endif
1067 						n = mode|nv_isarray(np);
1068 						if(!mode && (flags&NV_ARRAY) && ((c=sp[1])=='*' || c=='@') && sp[2]==']')
1069 						{
1070 							/* not implemented yet */
1071 							dp->last = cp;
1072 							return(np);
1073 						}
1074 #if SHOPT_FIXEDARRAY
1075 						if(fixed)
1076 							flags |= NV_FARRAY;
1077 						else
1078 #endif /* SHOPT_FIXEDARRAY */
1079 						if((n&NV_ADD)&&(flags&NV_ARRAY))
1080 							n |= ARRAY_FILL;
1081 						if(flags&NV_ASSIGN)
1082 							n |= NV_ADD;
1083 						cp = nv_endsubscript(np,sp,n|(flags&(NV_ASSIGN|NV_FARRAY)));
1084 #if SHOPT_FIXEDARRAY
1085 						flags &= ~NV_FARRAY;
1086 						if(fixed)
1087 							flags &= ~NV_ARRAY;
1088 
1089 #endif /* SHOPT_FIXEDARRAY */
1090 #if 0
1091 						if(scan)
1092 							nv_putsub(np,NIL(char*),ARRAY_SCAN);
1093 #endif
1094 					}
1095 					else
1096 						cp = sp;
1097 					if((c = *cp)=='.' || (c=='[' && nv_isarray(np)) || (n&ARRAY_FILL) || ((ap || (flags&NV_ASSIGN)) &&  (flags&NV_ARRAY)))
1098 
1099 					{
1100 						int m = cp-sp;
1101 						sub = m?nv_getsub(np):0;
1102 						if(!sub)
1103 						{
1104 							if(m && !(n&NV_ADD))
1105 								return(0);
1106 							sub = "0";
1107 						}
1108 						n = strlen(sub)+2;
1109 						if(!copy)
1110 						{
1111 							copy = cp-name;
1112 							dp->nofree |= 1;
1113 							name = copystack((const char*)0, name,(const char*)0);
1114 							cp = (char*)name+copy;
1115 							sp = cp-m;
1116 						}
1117 						if(n <= m)
1118 						{
1119 							if(n)
1120 							{
1121 								memcpy(sp+1,sub,n-2);
1122 								sp[n-1] = ']';
1123 							}
1124 							if(n < m)
1125 							{
1126 								char *dp = sp+n;
1127 								while(*dp++=*cp++);
1128 								cp = sp+n;
1129 							}
1130 						}
1131 						else
1132 						{
1133 							int r = n-m;
1134 							m = sp-name;
1135 							name = stack_extend(name, cp-1, r);
1136 							sp = (char*)name + m;
1137 							*sp = '[';
1138 							memcpy(sp+1,sub,n-2);
1139 							sp[n-1] = ']';
1140 							cp = sp+n;
1141 
1142 						}
1143 					}
1144 					else if(c==0 && mode && (n=nv_aindex(np))>0)
1145 						nv_putsub(np,(char*)0,n);
1146 #if SHOPT_FIXEDARRAY
1147 					else if(n==0 && !fixed && (c==0 || (c=='[' && !nv_isarray(np))))
1148 #else
1149 					else if(n==0 && (c==0 || (c=='[' && !nv_isarray(np))))
1150 #endif /* SHOPT_FIXEDARRAY */
1151 					{
1152 						/* subscript must be 0*/
1153 						cp[-1] = 0;
1154 						n = sh_arith(shp,sp+1);
1155 						cp[-1] = ']';
1156 						if(n)
1157 							return(0);
1158 						if(c)
1159 							sp = cp;
1160 					}
1161 					dp->last = cp;
1162 					if(nv_isarray(np) && (c=='[' || c=='.' || (flags&NV_ARRAY)))
1163 					{
1164 					addsub:
1165 						sp = cp;
1166 						if(!(nq = nv_opensub(np)))
1167 						{
1168 							Namarr_t *ap = nv_arrayptr(np);
1169 							if(!sub && (flags&NV_NOADD))
1170 								return(0);
1171 							n = mode|((flags&NV_NOADD)?0:NV_ADD);
1172 							if(!ap && (n&NV_ADD))
1173 							{
1174 								nv_putsub(np,sub,ARRAY_FILL);
1175 								ap = nv_arrayptr(np);
1176 							}
1177 							if(n && ap && !ap->table)
1178 								ap->table = dtopen(&_Nvdisc,Dtoset);
1179 							if(ap && ap->table && (nq=nv_search(sub,ap->table,n)))
1180 								nq->nvenv = (char*)np;
1181 							if(nq && nv_isnull(nq))
1182 								nq = nv_arraychild(np,nq,c);
1183 						}
1184 						if(nq)
1185 						{
1186 							if(c=='.' && !nv_isvtree(nq))
1187 							{
1188 								if(flags&NV_NOADD)
1189 									return(0);
1190 								nv_setvtree(nq);
1191 							}
1192 							nv_onattr(np,nofree);
1193 							nofree = 0;
1194 							np = nq;
1195 						}
1196 						else if(memcmp(cp,"[0]",3))
1197 							return(nq);
1198 						else
1199 						{
1200 							/* ignore [0]  */
1201 							dp->last = cp += 3;
1202 							c = *cp;
1203 						}
1204 					}
1205 				}
1206 #if SHOPT_FIXEDARRAY
1207 				else if(nv_isarray(np) && (!fixed || cp[-1]!=']'))
1208 #else
1209 				else if(nv_isarray(np))
1210 #endif /* SHOPT_FIXEDARRAY */
1211 				{
1212 					if(c==0 && (flags&NV_MOVE))
1213 						return(np);
1214 					nv_putsub(np,NIL(char*),ARRAY_UNDEF);
1215 				}
1216 				nv_onattr(np,nofree);
1217 				nofree  = 0;
1218 				if(c=='.' && (fp=np->nvfun))
1219 				{
1220 					for(; fp; fp=fp->next)
1221 					{
1222 						if(fp->disc && fp->disc->createf)
1223 							break;
1224 					}
1225 					if(fp)
1226 					{
1227 						if((nq = (*fp->disc->createf)(np,cp+1,flags,fp)) == np)
1228 						{
1229 							add = NV_ADD;
1230 							shp->last_table = 0;
1231 							break;
1232 						}
1233 						else if(np=nq)
1234 						{
1235 							if((c = *(sp=cp=dp->last=fp->last))==0)
1236 							{
1237 								if(nv_isarray(np) && sp[-1]!=']')
1238 									nv_putsub(np,NIL(char*),ARRAY_UNDEF);
1239 								return(np);
1240 							}
1241 						}
1242 					}
1243 				}
1244 			}
1245 			while(c=='[');
1246 			if(c!='.' || cp[1]=='.')
1247 				return(np);
1248 			cp++;
1249 			break;
1250 		    default:
1251 			dp->last = cp;
1252 			if((c = mbchar(cp)) && !isaletter(c))
1253 				return(np);
1254 			while(xp=cp, c=mbchar(cp), isaname(c));
1255 			cp = xp;
1256 		}
1257 	}
1258 	return(np);
1259 }
1260 
1261 /*
1262  * delete the node <np> from the dictionary <root> and clear from the cache
1263  * if <root> is NULL, only the cache is cleared
1264  * if flags does not contain NV_NOFREE, the node is freed
1265  * if np==0  && !root && flags==0,  delete the Refdict dictionary
1266  */
1267 void nv_delete(Namval_t* np, Dt_t *root, int flags)
1268 {
1269 #if NVCACHE
1270 	register int		c;
1271 	struct Cache_entry	*xp;
1272 	for(c=0,xp=nvcache.entries ; c < NVCACHE; xp= &nvcache.entries[++c])
1273 	{
1274 		if(xp->np==np)
1275 			xp->root = 0;
1276 	}
1277 #endif
1278 	if(!np && !root && flags==0)
1279 	{
1280 		if(Refdict)
1281 			dtclose(Refdict);
1282 		Refdict = 0;
1283 		return;
1284 	}
1285 	if(root || !(flags&NV_NOFREE))
1286 	{
1287 		if(!(flags&NV_FUNCTION) && Refdict)
1288 		{
1289 			Namval_t **key = &np;
1290 			struct Namref *rp;
1291 			while(rp = (struct Namref*)dtmatch(Refdict,(void*)key))
1292 			{
1293 				if(rp->sub)
1294 					free(rp->sub);
1295 				rp->sub = 0;
1296 				rp = dtdelete(Refdict,(void*)rp);
1297 				rp->np = &NullNode;
1298 			}
1299 		}
1300 	}
1301 	if(root)
1302 	{
1303 		if(dtdelete(root,np))
1304 		{
1305 			if(!(flags&NV_NOFREE) && ((flags&NV_FUNCTION) || !nv_subsaved(np)))
1306 			{
1307 				Namarr_t    *ap;
1308 				if(nv_isarray(np) && np->nvfun &&
1309 					(ap=nv_arrayptr(np)) && array_assoc(ap)) {
1310 					while(nv_associative(np,0,NV_ANEXT))
1311 						nv_associative(np, 0, NV_ADELETE);
1312 					nv_associative(np, 0, NV_AFREE);
1313 					free((void*)np->nvfun);
1314 				}
1315 				free((void*)np);
1316 			}
1317 		}
1318 #if 0
1319 		else
1320 		{
1321 			sfprintf(sfstderr,"%s not deleted\n",nv_name(np));
1322 			sfsync(sfstderr);
1323 		}
1324 #endif
1325 	}
1326 }
1327 
1328 /*
1329  * Put <arg> into associative memory.
1330  * If <flags> & NV_ARRAY then follow array to next subscript
1331  * If <flags> & NV_NOARRAY then subscript is not allowed
1332  * If <flags> & NV_NOSCOPE then use the current scope only
1333  * If <flags> & NV_ASSIGN then assignment is allowed
1334  * If <flags> & NV_IDENT then name must be an identifier
1335  * If <flags> & NV_VARNAME then name must be a valid variable name
1336  * If <flags> & NV_NOADD then node will not be added if not found
1337  * If <flags> & NV_NOREF then don't follow reference
1338  * If <flags> & NV_NOFAIL then don't generate an error message on failure
1339  * If <flags> & NV_STATIC then unset before an assignment
1340  * If <flags> & NV_UNJUST then unset attributes before assignment
1341  * SH_INIT is only set while initializing the environment
1342  */
1343 Namval_t *nv_open(const char *name, Dt_t *root, int flags)
1344 {
1345 	Shell_t			*shp = sh_getinterp();
1346 	register char		*cp=(char*)name;
1347 	register int		c;
1348 	register Namval_t	*np=0;
1349 	Namfun_t		fun;
1350 	int			append=0;
1351 	const char		*msg = e_varname;
1352 	char			*fname = 0;
1353 	int			offset = staktell();
1354 	Dt_t			*funroot;
1355 #if NVCACHE
1356 	struct Cache_entry	*xp;
1357 #endif
1358 
1359 	sh_stats(STAT_NVOPEN);
1360 	memset(&fun,0,sizeof(fun));
1361 	shp->openmatch = 0;
1362 	shp->last_table = 0;
1363 	if(!root)
1364 		root = shp->var_tree;
1365 	shp->last_root = root;
1366 	if(root==shp->fun_tree)
1367 	{
1368 		flags |= NV_NOREF;
1369 		msg = e_badfun;
1370 		if(strchr(name,'.'))
1371 		{
1372 			name = cp = copystack(0,name,(const char*)0);
1373 			fname = strrchr(cp,'.');
1374 			*fname = 0;
1375 			fun.nofree |= 1;
1376 			flags &=  ~NV_IDENT;
1377 			funroot = root;
1378 			root = shp->var_tree;
1379 		}
1380 	}
1381 	else if(!(flags&(NV_IDENT|NV_VARNAME|NV_ASSIGN)))
1382 	{
1383 		long mode = ((flags&NV_NOADD)?0:NV_ADD);
1384 		if(flags&NV_NOSCOPE)
1385 			mode |= HASH_SCOPE|HASH_NOSCOPE;
1386 		np = nv_search(name,root,mode);
1387 		if(np && !(flags&NV_REF))
1388 		{
1389 			while(nv_isref(np))
1390 			{
1391 				shp->last_table = nv_reftable(np);
1392 				np = nv_refnode(np);
1393 			}
1394 		}
1395 		return(np);
1396 	}
1397 	else if(shp->prefix && (flags&NV_ASSIGN))
1398 	{
1399 		name = cp = copystack(shp->prefix,name,(const char*)0);
1400 		fun.nofree |= 1;
1401 	}
1402 	c = *(unsigned char*)cp;
1403 	if(root==shp->alias_tree)
1404 	{
1405 		msg = e_aliname;
1406 		while((c= *(unsigned char*)cp++) && (c!='=') && (c!='/') &&
1407 			(c>=0x200 || !(c=sh_lexstates[ST_NORM][c]) || c==S_EPAT || c==S_COLON));
1408 		if(shp->subshell && c=='=')
1409 			root = sh_subaliastree(1);
1410 		if(c= *--cp)
1411 			*cp = 0;
1412 		np = nv_search(name, root, (flags&NV_NOADD)?0:NV_ADD);
1413 		if(c)
1414 			*cp = c;
1415 		goto skip;
1416 	}
1417 	else if(flags&NV_IDENT)
1418 		msg = e_ident;
1419 	else if(c=='.')
1420 	{
1421 		c = *++cp;
1422 		flags |= NV_NOREF;
1423 		if(root==shp->var_tree)
1424 			root = shp->var_base;
1425 		shp->last_table = 0;
1426 	}
1427 	if(c= !isaletter(c))
1428 		goto skip;
1429 #if NVCACHE
1430 	for(c=0,xp=nvcache.entries ; c < NVCACHE; xp= &nvcache.entries[++c])
1431 	{
1432 		if(xp->root!=root)
1433 			continue;
1434 		if(*name==*xp->name && xp->namespace==shp->namespace && (flags&(NV_ARRAY|NV_NOSCOPE))==xp->flags && memcmp(xp->name,name,xp->len)==0 && (name[xp->len]==0 || name[xp->len]=='=' || name[xp->len]=='+'))
1435 		{
1436 			sh_stats(STAT_NVHITS);
1437 			np = xp->np;
1438 			cp = (char*)name+xp->len;
1439 			if(nv_isarray(np) && !(flags&NV_MOVE))
1440 				 nv_putsub(np,NIL(char*),ARRAY_UNDEF);
1441 			shp->last_table = xp->last_table;
1442 			shp->last_root = xp->last_root;
1443 			goto nocache;
1444 		}
1445 	}
1446 	nvcache.ok = 1;
1447 #endif
1448 	np = nv_create(name, root, flags, &fun);
1449 	cp = fun.last;
1450 #if NVCACHE
1451 	if(np && nvcache.ok && cp[-1]!=']')
1452 	{
1453 		xp = &nvcache.entries[nvcache.index];
1454 		if(*cp)
1455 		{
1456 			char *sp = strchr(name,*cp);
1457 			if(!sp)
1458 				goto nocache;
1459 			xp->len = sp-name;
1460 		}
1461 		else
1462 			xp->len = strlen(name);
1463 		c = roundof(xp->len+1,32);
1464 		if(c > xp->size)
1465 		{
1466 			if(xp->size==0)
1467 				xp->name = malloc(c);
1468 			else
1469 				xp->name = realloc(xp->name,c);
1470 			xp->size = c;
1471 		}
1472 		memcpy(xp->name,name,xp->len);
1473 		xp->name[xp->len] = 0;
1474 		xp->root = root;
1475 		xp->np = np;
1476 		xp->namespace = shp->namespace;
1477 		xp->last_table = shp->last_table;
1478 		xp->last_root = shp->last_root;
1479 		xp->flags = (flags&(NV_ARRAY|NV_NOSCOPE));
1480 		nvcache.index = (nvcache.index+1)&(NVCACHE-1);
1481 	}
1482 nocache:
1483 	nvcache.ok = 0;
1484 #endif
1485 	if(fname)
1486 	{
1487 		c = ((flags&NV_NOSCOPE)?HASH_NOSCOPE:0)|((flags&NV_NOADD)?0:NV_ADD);
1488 		*fname = '.';
1489 		np = nv_search(name, funroot, c);
1490 		*fname = 0;
1491 	}
1492 	else
1493 	{
1494 		if(*cp=='.' && cp[1]=='.')
1495 		{
1496 			append |= NV_NODISC;
1497 			cp+=2;
1498 		}
1499 		if(*cp=='+' && cp[1]=='=')
1500 		{
1501 			append |= NV_APPEND;
1502 			cp++;
1503 		}
1504 	}
1505 	c = *cp;
1506 skip:
1507 #if SHOPT_TYPEDEF
1508 	if(np && shp->mktype)
1509 		np = nv_addnode(np,0);
1510 #endif /* SHOPT_TYPEDEF */
1511 	if(c=='=' && np && (flags&NV_ASSIGN))
1512 	{
1513 		cp++;
1514 		if(sh_isstate(SH_INIT))
1515 		{
1516 			nv_putval(np, cp, NV_RDONLY);
1517 			if(np==PWDNOD)
1518 				nv_onattr(np,NV_TAGGED);
1519 		}
1520 		else
1521 		{
1522 			char *sub=0, *prefix= shp->prefix;
1523 			Namval_t *mp;
1524 			Namarr_t *ap;
1525 			int isref;
1526 			shp->prefix = 0;
1527 			if((flags&NV_STATIC) && !shp->mktype)
1528 			{
1529 				if(!nv_isnull(np))
1530 				{
1531 					shp->prefix = prefix;
1532 					return(np);
1533 				}
1534 			}
1535 			isref = nv_isref(np);
1536 #if SHOPT_FIXEDARRAY
1537 			if(sh_isoption(SH_XTRACE) && (ap=nv_arrayptr(np)) && !ap->fixed)
1538 #else
1539 			if(sh_isoption(SH_XTRACE) && nv_isarray(np))
1540 #endif /* SHOPT_FIXEDARRAY */
1541 				sub = nv_getsub(np);
1542 			c = msg==e_aliname? 0: (append | (flags&NV_EXPORT));
1543 			if(isref)
1544 				nv_offattr(np,NV_REF);
1545 			if(!append && (flags&NV_UNJUST))
1546 			{
1547 				if(!np->nvfun)
1548 					_nv_unset(np,NV_EXPORT);
1549 			}
1550 			if(flags&NV_MOVE)
1551 			{
1552 				if(ap=nv_arrayptr(np))
1553 				{
1554 					if(mp=nv_opensub(np))
1555 						np = mp;
1556 					else if(!array_assoc(ap) && (mp = nv_open(cp,shp->var_tree,NV_NOFAIL|NV_VARNAME|NV_NOARRAY|NV_NOASSIGN|NV_NOADD)) && nv_isvtree(np))
1557 					{
1558 						ap->nelem |= ARRAY_TREE;
1559 						nv_putsub(np,(char*)0,ARRAY_ADD|nv_aindex(np));
1560 						np = nv_opensub(np);
1561 						ap->nelem &= ~ARRAY_TREE;
1562 						ap->nelem -= 1;
1563 					}
1564 				}
1565 				_nv_unset(np,NV_EXPORT);
1566 			}
1567 			nv_putval(np, cp, c);
1568 			if(isref)
1569 			{
1570 				if(nv_search((char*)np,shp->var_base,HASH_BUCKET))
1571 					shp->last_root = shp->var_base;
1572 				nv_setref(np,(Dt_t*)0,NV_VARNAME);
1573 			}
1574 			savesub = sub;
1575 			shp->prefix = prefix;
1576 		}
1577 		nv_onattr(np, flags&NV_ATTRIBUTES);
1578 	}
1579 	else if(c)
1580 	{
1581 		if(flags&NV_NOFAIL)
1582 			return(0);
1583 		if(c=='.')
1584 			msg = e_noparent;
1585 		else if(c=='[')
1586 			msg = e_noarray;
1587 		errormsg(SH_DICT,ERROR_exit(1),msg,name);
1588 	}
1589 	if(fun.nofree&1)
1590 		stakseek(offset);
1591 	return(np);
1592 }
1593 
1594 #if SHOPT_MULTIBYTE
1595     static int ja_size(char*, int, int);
1596     static void ja_restore(void);
1597     static char *savep;
1598     static char savechars[8+1];
1599 #endif /* SHOPT_MULTIBYTE */
1600 
1601 /*
1602  * put value <string> into name-value node <np>.
1603  * If <np> is an array, then the element given by the
1604  *   current index is assigned to.
1605  * If <flags> contains NV_RDONLY, readonly attribute is ignored
1606  * If <flags> contains NV_INTEGER, string is a pointer to a number
1607  * If <flags> contains NV_NOFREE, previous value is freed, and <string>
1608  * becomes value of node and <flags> becomes attributes
1609  */
1610 void nv_putval(register Namval_t *np, const char *string, int flags)
1611 {
1612 	Shell_t	*shp = sh_getinterp();
1613 	register const char *sp=string;
1614 	register union Value *up;
1615 	register char *cp;
1616 	register int size = 0;
1617 	register int dot;
1618 	int	was_local = nv_local;
1619 	union Value u;
1620 #if SHOPT_FIXEDARRAY
1621 	Namarr_t	*ap;
1622 #endif /* SHOPT_FIXEDARRAY */
1623 	if(!(flags&NV_RDONLY) && nv_isattr (np, NV_RDONLY))
1624 		errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np));
1625 	/* The following could cause the shell to fork if assignment
1626 	 * would cause a side effect
1627 	 */
1628 	shp->argaddr = 0;
1629 	if(shp->subshell && !nv_local && !(flags&NV_RDONLY))
1630 		np = sh_assignok(np,1);
1631 	if(np->nvfun && np->nvfun->disc && !(flags&NV_NODISC) && !nv_isref(np))
1632 	{
1633 		/* This function contains disc */
1634 		if(!nv_local)
1635 		{
1636 			nv_local=1;
1637 			nv_putv(np,sp,flags,np->nvfun);
1638 			if(sp && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT)))
1639 				sh_envput(shp->env,np);
1640 			return;
1641 		}
1642 		/* called from disc, assign the actual value */
1643 	}
1644 	flags &= ~NV_NODISC;
1645 	nv_local=0;
1646 	if(flags&(NV_NOREF|NV_NOFREE))
1647 	{
1648 		if(np->nvalue.cp && np->nvalue.cp!=sp && !nv_isattr(np,NV_NOFREE))
1649 			free((void*)np->nvalue.cp);
1650 		np->nvalue.cp = (char*)sp;
1651 		nv_setattr(np,(flags&~NV_RDONLY)|NV_NOFREE);
1652 		return;
1653 	}
1654 	up= &np->nvalue;
1655 	if(nv_isattr(np,NV_INT16P) == NV_INT16)
1656 	{
1657 		if(!np->nvalue.up || !nv_isarray(np))
1658 		{
1659 			up = &u;
1660 			up->up = &np->nvalue;
1661 		}
1662 	}
1663 #if SHOPT_FIXEDARRAY
1664 	else if(np->nvalue.up && nv_isarray(np) && (ap=nv_arrayptr(np)) && !ap->fixed)
1665 #else
1666 	else if(np->nvalue.up && nv_isarray(np) && nv_arrayptr(np))
1667 #endif /* SHOPT_FIXEDARRAY */
1668 		up = np->nvalue.up;
1669 	if(up && up->cp==Empty)
1670 		up->cp = 0;
1671 	if(nv_isattr(np,NV_EXPORT))
1672 		nv_offattr(np,NV_IMPORT);
1673 	if(nv_isattr (np, NV_INTEGER))
1674 	{
1675 		if(nv_isattr(np, NV_DOUBLE) == NV_DOUBLE)
1676 		{
1677 			if(nv_isattr(np, NV_LONG) && sizeof(double)<sizeof(Sfdouble_t))
1678 			{
1679 				Sfdouble_t ld, old=0;
1680 				if(flags&NV_INTEGER)
1681 				{
1682 					if(flags&NV_LONG)
1683 						ld = *((Sfdouble_t*)sp);
1684 					else if(flags&NV_SHORT)
1685 						ld = *((float*)sp);
1686 					else
1687 						ld = *((double*)sp);
1688 				}
1689 				else
1690 					ld = sh_arith(shp,sp);
1691 				if(!up->ldp)
1692 					up->ldp = new_of(Sfdouble_t,0);
1693 				else if(flags&NV_APPEND)
1694 					old = *(up->ldp);
1695 				*(up->ldp) = old?ld+old:ld;
1696 			}
1697 			else
1698 			{
1699 				double d,od=0;
1700 				if(flags&NV_INTEGER)
1701 				{
1702 					if(flags&NV_LONG)
1703 						d = (double)(*(Sfdouble_t*)sp);
1704 					else if(flags&NV_SHORT)
1705 						d = (double)(*(float*)sp);
1706 					else
1707 						d = *(double*)sp;
1708 				}
1709 				else
1710 					d = sh_arith(shp,sp);
1711 				if(!up->dp)
1712 					up->dp = new_of(double,0);
1713 				else if(flags&NV_APPEND)
1714 					od = *(up->dp);
1715 				*(up->dp) = od?d+od:d;
1716 			}
1717 		}
1718 		else
1719 		{
1720 			if(nv_isattr(np, NV_LONG) && sizeof(int32_t)<sizeof(Sflong_t))
1721 			{
1722 				Sflong_t ll=0,oll=0;
1723 				if(flags&NV_INTEGER)
1724 				{
1725 					if((flags&NV_DOUBLE) == NV_DOUBLE)
1726 					{
1727 						if(flags&NV_LONG)
1728 							ll = *((Sfdouble_t*)sp);
1729 						else if(flags&NV_SHORT)
1730 							ll = *((float*)sp);
1731 						else
1732 							ll = *((double*)sp);
1733 					}
1734 					else if(nv_isattr(np,NV_UNSIGN))
1735 					{
1736 						if(flags&NV_LONG)
1737 							ll = *((Sfulong_t*)sp);
1738 						else if(flags&NV_SHORT)
1739 							ll = *((uint16_t*)sp);
1740 						else
1741 							ll = *((uint32_t*)sp);
1742 					}
1743 					else
1744 					{
1745 						if(flags&NV_LONG)
1746 							ll = *((Sflong_t*)sp);
1747 						else if(flags&NV_SHORT)
1748 							ll = *((uint16_t*)sp);
1749 						else
1750 							ll = *((uint32_t*)sp);
1751 					}
1752 				}
1753 				else if(sp)
1754 					ll = (Sflong_t)sh_arith(shp,sp);
1755 				if(!up->llp)
1756 					up->llp = new_of(Sflong_t,0);
1757 				else if(flags&NV_APPEND)
1758 					oll = *(up->llp);
1759 				*(up->llp) = ll+oll;
1760 			}
1761 			else
1762 			{
1763 				int32_t l=0,ol=0;
1764 				if(flags&NV_INTEGER)
1765 				{
1766 					if((flags&NV_DOUBLE) == NV_DOUBLE)
1767 					{
1768 						Sflong_t ll;
1769 						if(flags&NV_LONG)
1770 							ll = *((Sfdouble_t*)sp);
1771 						else if(flags&NV_SHORT)
1772 							ll = *((float*)sp);
1773 						else
1774 							ll = *((double*)sp);
1775 						l = (int32_t)ll;
1776 					}
1777 					else if(nv_isattr(np,NV_UNSIGN))
1778 					{
1779 						if(flags&NV_LONG)
1780 							l = *((Sfulong_t*)sp);
1781 						else if(flags&NV_SHORT)
1782 							l = *((uint16_t*)sp);
1783 						else
1784 							l = *(uint32_t*)sp;
1785 					}
1786 					else
1787 					{
1788 						if(flags&NV_LONG)
1789 							l = *((Sflong_t*)sp);
1790 						else if(flags&NV_SHORT)
1791 							l = *((int16_t*)sp);
1792 						else
1793 							l = *(int32_t*)sp;
1794 					}
1795 				}
1796 				else if(sp)
1797 				{
1798 					Sfdouble_t ld = sh_arith(shp,sp);
1799 					if(ld<0)
1800 						l = (int32_t)ld;
1801 					else
1802 						l = (uint32_t)ld;
1803 				}
1804 				if(nv_size(np) <= 1)
1805 					nv_setsize(np,10);
1806 				if(nv_isattr (np, NV_SHORT))
1807 				{
1808 					int16_t s=0;
1809 					if(flags&NV_APPEND)
1810 						s = *up->sp;
1811 					*(up->sp) = s+(int16_t)l;
1812 					nv_onattr(np,NV_NOFREE);
1813 				}
1814 				else
1815 				{
1816 					if(!up->lp)
1817 						up->lp = new_of(int32_t,0);
1818 					else if(flags&NV_APPEND)
1819 						ol =  *(up->lp);
1820 					*(up->lp) = l+ol;
1821 				}
1822 			}
1823 		}
1824 	}
1825 	else
1826 	{
1827 		const char *tofree=0;
1828 		int offset,append;
1829 #if _lib_pathnative
1830 		char buff[PATH_MAX];
1831 #endif /* _lib_pathnative */
1832 		if(flags&NV_INTEGER)
1833 		{
1834 			if((flags&NV_DOUBLE)==NV_DOUBLE)
1835 			{
1836 				if(flags&NV_LONG)
1837 					sfprintf(shp->strbuf,"%.*Lg",LDBL_DIG,*((Sfdouble_t*)sp));
1838 				else
1839 					sfprintf(shp->strbuf,"%.*g",DBL_DIG,*((double*)sp));
1840 			}
1841 			else if(flags&NV_UNSIGN)
1842 			{
1843 				if(flags&NV_LONG)
1844 					sfprintf(shp->strbuf,"%I*lu",sizeof(Sfulong_t),*((Sfulong_t*)sp));
1845 				else
1846 					sfprintf(shp->strbuf,"%lu",(unsigned long)((flags&NV_SHORT)?*((uint16_t*)sp):*((uint32_t*)sp)));
1847 			}
1848 			else
1849 			{
1850 				if(flags&NV_LONG)
1851 					sfprintf(shp->strbuf,"%I*ld",sizeof(Sflong_t),*((Sflong_t*)sp));
1852 				else
1853 					sfprintf(shp->strbuf,"%ld",(long)((flags&NV_SHORT)?*((int16_t*)sp):*((int32_t*)sp)));
1854 			}
1855 			sp = sfstruse(shp->strbuf);
1856 		}
1857 		if(nv_isattr(np, NV_HOST|NV_INTEGER)==NV_HOST && sp)
1858 		{
1859 #ifdef _lib_pathnative
1860 			/*
1861 			 * return the host file name given the UNIX name
1862 			 */
1863 			pathnative(sp,buff,sizeof(buff));
1864 			if(buff[1]==':' && buff[2]=='/')
1865 			{
1866 				buff[2] = '\\';
1867 				if(*buff>='A' &&  *buff<='Z')
1868 					*buff += 'a'-'A';
1869 			}
1870 			sp = buff;
1871 #else
1872 			;
1873 #endif /* _lib_pathnative */
1874 		}
1875 		else if((nv_isattr(np, NV_RJUST|NV_ZFILL|NV_LJUST)) && sp)
1876 		{
1877 			for(;*sp == ' '|| *sp=='\t';sp++);
1878 	        	if((nv_isattr(np,NV_ZFILL)) && (nv_isattr(np,NV_LJUST)))
1879 				for(;*sp=='0';sp++);
1880 			size = nv_size(np);
1881 #if SHOPT_MULTIBYTE
1882 			if(size)
1883 				size = ja_size((char*)sp,size,nv_isattr(np,NV_RJUST|NV_ZFILL));
1884 #endif /* SHOPT_MULTIBYTE */
1885 		}
1886 		if(!up->cp || *up->cp==0)
1887 			flags &= ~NV_APPEND;
1888 		if(!nv_isattr(np, NV_NOFREE))
1889 		{
1890 			/* delay free in case <sp> points into free region */
1891 			tofree = up->cp;
1892 		}
1893 		if(nv_isattr(np,NV_BINARY) && !(flags&NV_RAW))
1894 			tofree = 0;
1895 		if(nv_isattr(np,NV_LJUST|NV_RJUST) && nv_isattr(np,NV_LJUST|NV_RJUST)!=(NV_LJUST|NV_RJUST))
1896 			tofree = 0;
1897        	 	if (sp)
1898 		{
1899 			append=0;
1900 			if(sp==up->cp && !(flags&NV_APPEND))
1901 				return;
1902 			dot = strlen(sp);
1903 #if (_AST_VERSION>=20030127L)
1904 			if(nv_isattr(np,NV_BINARY))
1905 			{
1906 				int oldsize = (flags&NV_APPEND)?nv_size(np):0;
1907 				if(flags&NV_RAW)
1908 				{
1909 					if(tofree)
1910 					{
1911 						free((void*)tofree);
1912 						nv_offattr(np,NV_NOFREE);
1913 					}
1914 					up->cp = sp;
1915 					return;
1916 				}
1917 				size = 0;
1918 				if(nv_isattr(np,NV_ZFILL))
1919 					size = nv_size(np);
1920 				if(size==0)
1921 					size = oldsize + (3*dot/4);
1922 				*(cp = (char*)malloc(size+1)) = 0;
1923 				nv_offattr(np,NV_NOFREE);
1924 				if(oldsize)
1925 					memcpy((void*)cp,(void*)up->cp,oldsize);
1926 				up->cp = cp;
1927 				if(size <= oldsize)
1928 					return;
1929 				dot = base64decode(sp,dot, (void**)0, cp+oldsize, size-oldsize,(void**)0);
1930 				dot += oldsize;
1931 				if(!nv_isattr(np,NV_ZFILL) || nv_size(np)==0)
1932 					nv_setsize(np,dot);
1933 				else if(nv_isattr(np,NV_ZFILL) && (size>dot))
1934 					memset((void*)&cp[dot],0,size-dot);
1935 				return;
1936 			}
1937 			else
1938 #endif
1939 			{
1940 				if(size==0 && nv_isattr(np,NV_HOST)!=NV_HOST &&nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL))
1941 				{
1942 					nv_setsize(np,size=dot);
1943 					tofree = up->cp;
1944 				}
1945 				else if(size > dot)
1946 					dot = size;
1947 				else if(nv_isattr(np,NV_LJUST|NV_RJUST)==NV_LJUST && dot>size)
1948 					dot = size;
1949 				if(flags&NV_APPEND)
1950 				{
1951 					if(dot==0)
1952 						return;
1953 					append = strlen(up->cp);
1954 					if(!tofree || size)
1955 					{
1956 						offset = staktell();
1957 						stakputs(up->cp);
1958 						stakputs(sp);
1959 						stakputc(0);
1960 						sp = stakptr(offset);
1961 						dot += append;
1962 						append = 0;
1963 					}
1964 					else
1965 					{
1966 						flags &= ~NV_APPEND;
1967 					}
1968 				}
1969 			}
1970 			if(size==0 || tofree || dot || !(cp=(char*)up->cp))
1971 			{
1972 				if(dot==0 && !nv_isattr(np,NV_LJUST|NV_RJUST))
1973 				{
1974 					cp = Null;
1975 					nv_onattr(np,NV_NOFREE);
1976 				}
1977 				else
1978 				{
1979 					if(tofree && tofree!=Empty && tofree!=Null)
1980 					{
1981 						cp = (char*)realloc((void*)tofree,((unsigned)dot+append+8));
1982 						tofree = 0;
1983 					}
1984 					else
1985 						cp = (char*)malloc(((unsigned)dot+8));
1986 					cp[dot+append] = 0;
1987 					nv_offattr(np,NV_NOFREE);
1988 				}
1989 			}
1990 
1991 		}
1992 		else
1993 			cp = 0;
1994 		up->cp = cp;
1995 		if(sp)
1996 		{
1997 			int c = cp[dot+append];
1998 			memmove(cp+append,sp,dot);
1999 			cp[dot+append] = c;
2000 			if(nv_isattr(np, NV_RJUST) && nv_isattr(np, NV_ZFILL))
2001 				rightjust(cp,size,'0');
2002 			else if(nv_isattr(np, NV_LJUST|NV_RJUST)==NV_RJUST)
2003 				rightjust(cp,size,' ');
2004 			else if(nv_isattr(np, NV_LJUST|NV_RJUST)==NV_LJUST)
2005 			{
2006 				register char *dp;
2007 				dp = strlen (cp) + cp;
2008 				cp = cp+size;
2009 				for (; dp < cp; *dp++ = ' ');
2010 			 }
2011 #if SHOPT_MULTIBYTE
2012 			/* restore original string */
2013 			if(savep)
2014 				ja_restore();
2015 #endif /* SHOPT_MULTIBYTE */
2016 		}
2017 		if(flags&NV_APPEND)
2018 			stakseek(offset);
2019 		if(tofree && tofree!=Empty && tofree!=Null)
2020 			free((void*)tofree);
2021 	}
2022 	if(!was_local && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT)))
2023 		sh_envput(shp->env,np);
2024 	return;
2025 }
2026 
2027 /*
2028  *
2029  *   Right-justify <str> so that it contains no more than
2030  *   <size> characters.  If <str> contains fewer than <size>
2031  *   characters, left-pad with <fill>.  Trailing blanks
2032  *   in <str> will be ignored.
2033  *
2034  *   If the leftmost digit in <str> is not a digit, <fill>
2035  *   will default to a blank.
2036  */
2037 static void rightjust(char *str, int size, int fill)
2038 {
2039 	register int n;
2040 	register char *cp,*sp;
2041 	n = strlen(str);
2042 
2043 	/* ignore trailing blanks */
2044 	for(cp=str+n;n && *--cp == ' ';n--);
2045 	if (n == size)
2046 		return;
2047 	if(n > size)
2048         {
2049         	*(str+n) = 0;
2050         	for (sp = str, cp = str+n-size; sp <= str+size; *sp++ = *cp++);
2051         	return;
2052         }
2053 	else *(sp = str+size) = 0;
2054 	if (n == 0)
2055         {
2056         	while (sp > str)
2057                		*--sp = ' ';
2058         	return;
2059         }
2060 	while(n--)
2061 	{
2062 		sp--;
2063 		*sp = *cp--;
2064 	}
2065 	if(!isdigit(*str))
2066 		fill = ' ';
2067 	while(sp>str)
2068 		*--sp = fill;
2069 	return;
2070 }
2071 
2072 #if SHOPT_MULTIBYTE
2073     /*
2074      * handle left and right justified fields for multi-byte chars
2075      * given physical size, return a logical size which reflects the
2076      * screen width of multi-byte characters
2077      * Multi-width characters replaced by spaces if they cross the boundary
2078      * <type> is non-zero for right justified  fields
2079      */
2080 
2081     static int ja_size(char *str,int size,int type)
2082     {
2083 	register char *cp = str;
2084 	register int c, n=size;
2085 	register int outsize;
2086 	register char *oldcp=cp;
2087 	int oldn;
2088 	wchar_t w;
2089 	while(*cp)
2090 	{
2091 		oldn = n;
2092 		w = mbchar(cp);
2093 		if((outsize = mbwidth(w)) <0)
2094 			outsize = 0;
2095 		size -= outsize;
2096 		c = cp-oldcp;
2097 		n += (c-outsize);
2098 		oldcp = cp;
2099 		if(size<=0 && type==0)
2100 			break;
2101 	}
2102 	/* check for right justified fields that need truncating */
2103 	if(size <0)
2104 	{
2105 		if(type==0)
2106 		{
2107 			/* left justified and character crosses field boundary */
2108 			n = oldn;
2109 			/* save boundary char and replace with spaces */
2110 			size = c;
2111 			savechars[size] = 0;
2112 			while(size--)
2113 			{
2114 				savechars[size] = cp[size];
2115 				cp[size] = ' ';
2116 			}
2117 			savep = cp;
2118 		}
2119 		size = -size;
2120 		if(type)
2121 			n -= (ja_size(str,size,0)-size);
2122 	}
2123 	return(n);
2124     }
2125 
2126     static void ja_restore(void)
2127     {
2128 	register char *cp = savechars;
2129 	while(*cp)
2130 		*savep++ = *cp++;
2131 	savep = 0;
2132     }
2133 #endif /* SHOPT_MULTIBYTE */
2134 
2135 #ifndef _ENV_H
2136 static char *staknam(register Namval_t *np, char *value)
2137 {
2138 	register char *p,*q;
2139 	q = stakalloc(strlen(nv_name(np))+(value?strlen(value):0)+2);
2140 	p=strcopy(q,nv_name(np));
2141 	if(value)
2142 	{
2143 		*p++ = '=';
2144 		strcpy(p,value);
2145 	}
2146 	return(q);
2147 }
2148 #endif
2149 
2150 /*
2151  * put the name and attribute into value of attributes variable
2152  */
2153 #ifdef _ENV_H
2154 static void attstore(register Namval_t *np, void *data)
2155 {
2156 	register int flag, c = ' ';
2157 	NOT_USED(data);
2158 	if(!(nv_isattr(np,NV_EXPORT)))
2159 		return;
2160 	flag = nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER);
2161 	stakputc('=');
2162 	if((flag&NV_DOUBLE) == NV_DOUBLE)
2163 	{
2164 		/* export doubles as integers for ksh88 compatibility */
2165 		stakputc(c+NV_INTEGER|(flag&~(NV_DOUBLE|NV_EXPNOTE)));
2166 	}
2167 	else
2168 	{
2169 		stakputc(c+flag);
2170 		if(flag&NV_INTEGER)
2171 			c +=  nv_size(np);
2172 	}
2173 	stakputc(c);
2174 	stakputs(nv_name(np));
2175 }
2176 #else
2177 static void attstore(register Namval_t *np, void *data)
2178 {
2179 	register int flag = np->nvflag;
2180 	register struct adata *ap = (struct adata*)data;
2181 	ap->sh = sh_getinterp();
2182 	ap->tp = 0;
2183 	if(!(flag&NV_EXPORT) || (flag&NV_FUNCT))
2184 		return;
2185 	if((flag&(NV_UTOL|NV_LTOU|NV_INTEGER)) == (NV_UTOL|NV_LTOU))
2186 	{
2187 		data = (void*)nv_mapchar(np,0);
2188 		if(strcmp(data,e_tolower) && strcmp(data,e_toupper))
2189 			return;
2190 	}
2191 	flag &= (NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER);
2192 	*ap->attval++ = '=';
2193 	if((flag&NV_DOUBLE) == NV_DOUBLE)
2194 	{
2195 		/* export doubles as integers for ksh88 compatibility */
2196 		*ap->attval++ = ' '+ NV_INTEGER|(flag&~(NV_DOUBLE|NV_EXPNOTE));
2197 		*ap->attval = ' ';
2198 	}
2199 	else
2200 	{
2201 		*ap->attval++ = ' '+flag;
2202 		if(flag&NV_INTEGER)
2203 			*ap->attval = ' ' + nv_size(np);
2204 		else
2205 			*ap->attval = ' ';
2206 	}
2207 	ap->attval = strcopy(++ap->attval,nv_name(np));
2208 }
2209 #endif
2210 
2211 #ifndef _ENV_H
2212 static void pushnam(Namval_t *np, void *data)
2213 {
2214 	register char *value;
2215 	register struct adata *ap = (struct adata*)data;
2216 	ap->sh = sh_getinterp();
2217 	ap->tp = 0;
2218 	if(nv_isattr(np,NV_IMPORT) && np->nvenv)
2219 		*ap->argnam++ = np->nvenv;
2220 	else if(value=nv_getval(np))
2221 		*ap->argnam++ = staknam(np,value);
2222 	if(nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER))
2223 		ap->attsize += (strlen(nv_name(np))+4);
2224 }
2225 #endif
2226 
2227 /*
2228  * Generate the environment list for the child.
2229  */
2230 
2231 #ifdef _ENV_H
2232 char **sh_envgen(void)
2233 {
2234 	Shell_t *shp = sh_getinterp();
2235 	int offset,tell;
2236 	register char **er;
2237 	env_delete(shp->env,"_");
2238 	er = env_get(shp->env);
2239 	offset = staktell();
2240 	stakputs(e_envmarker);
2241 	tell = staktell();
2242 	nv_scan(shp->var_tree, attstore,(void*)0,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER));
2243 	if(tell ==staktell())
2244 		stakseek(offset);
2245 	else
2246 		*--er = stakfreeze(1)+offset;
2247 	return(er);
2248 }
2249 #else
2250 char **sh_envgen(void)
2251 {
2252 	register char **er;
2253 	register int namec;
2254 	register char *cp;
2255 	struct adata data;
2256 	Shell_t	*shp = sh_getinterp();
2257 	data.sh = shp;
2258 	data.tp = 0;
2259 	data.mapname = 0;
2260 	/* L_ARGNOD gets generated automatically as full path name of command */
2261 	nv_offattr(L_ARGNOD,NV_EXPORT);
2262 	data.attsize = 6;
2263 	namec = nv_scan(shp->var_tree,nullscan,(void*)0,NV_EXPORT,NV_EXPORT);
2264 	namec += shp->nenv;
2265 	er = (char**)stakalloc((namec+4)*sizeof(char*));
2266 	data.argnam = (er+=2) + shp->nenv;
2267 	if(shp->nenv)
2268 		memcpy((void*)er,environ,shp->nenv*sizeof(char*));
2269 	nv_scan(shp->var_tree, pushnam,&data,NV_EXPORT, NV_EXPORT);
2270 	*data.argnam = (char*)stakalloc(data.attsize);
2271 	cp = data.attval = strcopy(*data.argnam,e_envmarker);
2272 	nv_scan(shp->var_tree, attstore,&data,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER));
2273 	*data.attval = 0;
2274 	if(cp!=data.attval)
2275 		data.argnam++;
2276 	*data.argnam = 0;
2277 	return(er);
2278 }
2279 #endif
2280 
2281 struct scan
2282 {
2283 	void    (*scanfn)(Namval_t*, void*);
2284 	int     scanmask;
2285 	int     scanflags;
2286 	int     scancount;
2287 	void    *scandata;
2288 };
2289 
2290 static int scanfilter(Dt_t *dict, void *arg, void *data)
2291 {
2292 	register Namval_t *np = (Namval_t*)arg;
2293 	register int k=np->nvflag;
2294 	register struct scan *sp = (struct scan*)data;
2295 	register struct adata *tp = (struct adata*)sp->scandata;
2296 	char	*cp;
2297 	NOT_USED(dict);
2298 #if SHOPT_TYPEDEF
2299 	if(!is_abuiltin(np) && tp && tp->tp && nv_type(np)!=tp->tp)
2300 		return(0);
2301 #endif /*SHOPT_TYPEDEF */
2302 	if(sp->scanmask==NV_TABLE && nv_isvtree(np))
2303 		k = NV_TABLE;
2304 	if(sp->scanmask?(k&sp->scanmask)==sp->scanflags:(!sp->scanflags || (k&sp->scanflags)))
2305 	{
2306 		if(tp && tp->mapname)
2307 		{
2308 			if(sp->scanflags==NV_FUNCTION || sp->scanflags==(NV_NOFREE|NV_BINARY|NV_RAW))
2309 			{
2310 				int n = strlen(tp->mapname);
2311 				if(memcmp(np->nvname,tp->mapname,n) || np->nvname[n]!='.' || strchr(&np->nvname[n+1],'.'))
2312 					return(0);
2313 			}
2314 			else if((sp->scanflags==NV_UTOL||sp->scanflags==NV_LTOU) && (cp=(char*)nv_mapchar(np,0)) && strcmp(cp,tp->mapname))
2315 				return(0);
2316 		}
2317 		if(!np->nvalue.cp && !np->nvfun && !nv_isattr(np,~NV_DEFAULT))
2318 			return(0);
2319 		if(sp->scanfn)
2320 		{
2321 			if(nv_isarray(np))
2322 				nv_putsub(np,NIL(char*),0L);
2323 			(*sp->scanfn)(np,sp->scandata);
2324 		}
2325 		sp->scancount++;
2326 	}
2327 	return(0);
2328 }
2329 
2330 /*
2331  * Walk through the name-value pairs
2332  * if <mask> is non-zero, then only nodes with (nvflags&mask)==flags
2333  *	are visited
2334  * If <mask> is zero, and <flags> non-zero, then nodes with one or
2335  *	more of <flags> is visited
2336  * If <mask> and <flags> are zero, then all nodes are visted
2337  */
2338 int nv_scan(Dt_t *root, void (*fn)(Namval_t*,void*), void *data,int mask, int flags)
2339 {
2340 	Namval_t *np;
2341 	Dt_t *base=0;
2342 	struct scan sdata;
2343 	int (*hashfn)(Dt_t*, void*, void*);
2344 	sdata.scanmask = mask;
2345 	sdata.scanflags = flags&~NV_NOSCOPE;
2346 	sdata.scanfn = fn;
2347 	sdata.scancount = 0;
2348 	sdata.scandata = data;
2349 	hashfn = scanfilter;
2350 	if(flags&NV_NOSCOPE)
2351 		base = dtview((Dt_t*)root,0);
2352 	for(np=(Namval_t*)dtfirst(root);np; np=(Namval_t*)dtnext(root,np))
2353 		hashfn(root, np, &sdata);
2354 	if(base)
2355 		 dtview((Dt_t*)root,base);
2356 	return(sdata.scancount);
2357 }
2358 
2359 /*
2360  * create a new environment scope
2361  */
2362 void sh_scope(Shell_t *shp, struct argnod *envlist, int fun)
2363 {
2364 	register Dt_t		*newscope, *newroot=shp->var_base;
2365 	struct Ufunction	*rp;
2366 #if SHOPT_NAMESPACE
2367 	if(shp->namespace)
2368 		newroot = nv_dict(shp->namespace);
2369 #endif /* SHOPT_NAMESPACE */
2370 	newscope = dtopen(&_Nvdisc,Dtoset);
2371 	if(envlist)
2372 	{
2373 		dtview(newscope,(Dt_t*)shp->var_tree);
2374 		shp->var_tree = newscope;
2375 		nv_setlist(envlist,NV_EXPORT|NV_NOSCOPE|NV_IDENT|NV_ASSIGN,0);
2376 		if(!fun)
2377 			return;
2378 		shp->var_tree = dtview(newscope,0);
2379 	}
2380 	if((rp=shp->st.real_fun)  && rp->sdict)
2381 	{
2382 		dtview(rp->sdict,newroot);
2383 		newroot = rp->sdict;
2384 
2385 	}
2386 	dtview(newscope,(Dt_t*)newroot);
2387 	shp->var_tree = newscope;
2388 }
2389 
2390 /*
2391  * Remove freeable local space associated with the nvalue field
2392  * of nnod. This includes any strings representing the value(s) of the
2393  * node, as well as its dope vector, if it is an array.
2394  */
2395 
2396 void	sh_envnolocal (register Namval_t *np, void *data)
2397 {
2398 	struct adata *tp = (struct adata*)data;
2399 	char *cp=0;
2400 	if(np==VERSIONNOD && nv_isref(np))
2401 		return;
2402 	if(np==L_ARGNOD)
2403 		return;
2404 	if(np == tp->sh->namespace)
2405 		return;
2406 	if(nv_isref(np))
2407 		nv_unref(np);
2408 	if(nv_isattr(np,NV_EXPORT) && nv_isarray(np))
2409 	{
2410 		nv_putsub(np,NIL(char*),0);
2411 		if(cp = nv_getval(np))
2412 			cp = strdup(cp);
2413 	}
2414 	if(nv_isattr(np,NV_EXPORT|NV_NOFREE))
2415 	{
2416 		if(nv_isref(np) && np!=VERSIONNOD)
2417 		{
2418 			nv_offattr(np,NV_NOFREE|NV_REF);
2419 			free((void*)np->nvalue.nrp);
2420 			np->nvalue.cp = 0;
2421 		}
2422 		if(!cp)
2423 			return;
2424 	}
2425 	if(nv_isarray(np))
2426 		nv_putsub(np,NIL(char*),ARRAY_UNDEF);
2427 	_nv_unset(np,NV_RDONLY);
2428 	nv_setattr(np,0);
2429 	if(cp)
2430 	{
2431 		nv_putval(np,cp,0);
2432 		free((void*)cp);
2433 	}
2434 }
2435 
2436 /*
2437  * Currently this is a dummy, but someday will be needed
2438  * for reference counting
2439  */
2440 void	nv_close(Namval_t *np)
2441 {
2442 	NOT_USED(np);
2443 }
2444 
2445 static void table_unset(Shell_t *shp, register Dt_t *root, int flags, Dt_t *oroot)
2446 {
2447 	register Namval_t *np,*nq, *npnext;
2448 	for(np=(Namval_t*)dtfirst(root);np;np=npnext)
2449 	{
2450 		if(nq=dtsearch(oroot,np))
2451 		{
2452 			if(nv_cover(nq))
2453 			{
2454 				int subshell = shp->subshell;
2455 				shp->subshell = 0;
2456 				if(nv_isattr(nq, NV_INTEGER))
2457 				{
2458 					Sfdouble_t d = nv_getnum(nq);
2459 					nv_putval(nq,(char*)&d,NV_LDOUBLE);
2460 				}
2461 				else if(shp->test&4)
2462 					nv_putval(nq, strdup(nv_getval(nq)), NV_RDONLY);
2463 				else
2464 					nv_putval(nq, nv_getval(nq), NV_RDONLY);
2465 				shp->subshell = subshell;
2466 				np->nvfun = 0;
2467 			}
2468 			if(nv_isattr(nq,NV_EXPORT))
2469 				sh_envput(shp->env,nq);
2470 		}
2471 		shp->last_root = root;
2472 		shp->last_table = 0;
2473 		if(nv_isvtree(np))
2474 		{
2475 			int len = strlen(np->nvname);
2476 			npnext = (Namval_t*)dtnext(root,np);
2477 			while((nq=npnext) && memcmp(np->nvname,nq->nvname,len)==0 && nq->nvname[len]=='.')
2478 
2479 			{
2480 				_nv_unset(nq,flags);
2481 				npnext = (Namval_t*)dtnext(root,nq);
2482 				nv_delete(nq,root,0);
2483 			}
2484 		}
2485 		npnext = (Namval_t*)dtnext(root,np);
2486 		_nv_unset(np,flags);
2487 		nv_delete(np,root,0);
2488 	}
2489 }
2490 
2491 /*
2492  *
2493  *   Set the value of <np> to 0, and nullify any attributes
2494  *   that <np> may have had.  Free any freeable space occupied
2495  *   by the value of <np>.  If <np> denotes an array member, it
2496  *   will retain its attributes.
2497  *   <flags> can contain NV_RDONLY to override the readonly attribute
2498  *	being cleared.
2499  *   <flags> can contain NV_EXPORT to override preserve nvenv
2500  */
2501 void	_nv_unset(register Namval_t *np,int flags)
2502 {
2503 	Shell_t	*shp = sh_getinterp();
2504 	register union Value *up;
2505 #if SHOPT_FIXEDARRAY
2506 	Namarr_t	*ap;
2507 #endif /* SHOPT_FIXEDARRAY */
2508 	if(!(flags&NV_RDONLY) && nv_isattr (np,NV_RDONLY))
2509 		errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np));
2510 	if(is_afunction(np) && np->nvalue.ip)
2511 	{
2512 		register struct slnod *slp = (struct slnod*)(np->nvenv);
2513 		if(shp->st.real_fun == np->nvalue.rp)
2514 		{
2515 			np->nvalue.rp->running |= 1;
2516 			return;
2517 		}
2518 		if(slp && !nv_isattr(np,NV_NOFREE))
2519 		{
2520 			struct Ufunction *rq,*rp = np->nvalue.rp;
2521 			/* free function definition */
2522 			register char *name=nv_name(np),*cp= strrchr(name,'.');
2523 			if(cp)
2524 			{
2525 				Namval_t *npv;
2526 				*cp = 0;
2527 				 npv = nv_open(name,shp->var_tree,NV_NOARRAY|NV_VARNAME|NV_NOADD);
2528 				*cp++ = '.';
2529 				if(npv && npv!=shp->namespace)
2530 					nv_setdisc(npv,cp,NIL(Namval_t*),(Namfun_t*)npv);
2531 			}
2532 			if(rp->fname && shp->fpathdict && (rq = (struct Ufunction*)nv_search(rp->fname,shp->fpathdict,0)))
2533 			{
2534 				do
2535 				{
2536 					if(rq->np != np)
2537 						continue;
2538 					dtdelete(shp->fpathdict,rq);
2539 					break;
2540 				}
2541 				while(rq = (struct Ufunction*)dtnext(shp->fpathdict,rq));
2542 			}
2543 			if(rp->sdict)
2544 			{
2545 				Namval_t *mp, *nq;
2546 				for(mp=(Namval_t*)dtfirst(rp->sdict);mp;mp=nq)
2547 				{
2548 					nq = dtnext(rp->sdict,mp);
2549 					_nv_unset(mp,NV_RDONLY);
2550 					nv_delete(mp,rp->sdict,0);
2551 				}
2552 				dtclose(rp->sdict);
2553 			}
2554 			stakdelete(slp->slptr);
2555 			free((void*)np->nvalue.ip);
2556 			np->nvalue.ip = 0;
2557 		}
2558 		goto done;
2559 	}
2560 	if(shp->subshell)
2561 		np = sh_assignok(np,0);
2562 	nv_offattr(np,NV_NODISC);
2563 	if(np->nvfun && !nv_isref(np))
2564 	{
2565 		/* This function contains disc */
2566 		if(!nv_local)
2567 		{
2568 			nv_local=1;
2569 			nv_putv(np,NIL(char*),flags,np->nvfun);
2570 			nv_local=0;
2571 			return;
2572 		}
2573 		/* called from disc, assign the actual value */
2574 		nv_local=0;
2575 	}
2576 	if(nv_isattr(np,NV_INT16P) == NV_INT16)
2577 	{
2578 		np->nvalue.cp = nv_isarray(np)?Empty:0;
2579 		goto done;
2580 	}
2581 #if SHOPT_FIXEDARRAY
2582 	else if(np->nvalue.up && nv_isarray(np) && (ap=nv_arrayptr(np)) && !ap->fixed)
2583 #else
2584 	else if(np->nvalue.up && nv_isarray(np) && nv_arrayptr(np))
2585 #endif /* SHOPT_FIXEDARRAY */
2586 		up = np->nvalue.up;
2587 	else if(nv_isref(np) && !nv_isattr(np,NV_EXPORT|NV_MINIMAL) && np->nvalue.nrp)
2588 	{
2589 
2590 		if(np->nvalue.nrp->root)
2591 			dtdelete(Refdict,(void*)np->nvalue.nrp);
2592 		if(np->nvalue.nrp->sub)
2593 			free(np->nvalue.nrp->sub);
2594 		free((void*)np->nvalue.nrp);
2595 		np->nvalue.cp = 0;
2596 		up = 0;
2597 	}
2598 	else
2599 		up = &np->nvalue;
2600 	if(up && up->cp)
2601 	{
2602 		if(up->cp!=Empty && up->cp!=Null && !nv_isattr(np, NV_NOFREE))
2603 			free((void*)up->cp);
2604 		up->cp = 0;
2605 	}
2606 done:
2607 	if(!nv_isarray(np) || !nv_arrayptr(np))
2608 	{
2609 		nv_setsize(np,0);
2610 		if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT))
2611 		{
2612 			if(nv_isattr(np,NV_EXPORT) && !strchr(np->nvname,'['))
2613 				env_delete(shp->env,nv_name(np));
2614 			if(!(flags&NV_EXPORT) ||  nv_isattr(np,NV_EXPORT))
2615 				np->nvenv = 0;
2616 			nv_setattr(np,0);
2617 		}
2618 		else
2619 		{
2620 			nv_setattr(np,NV_MINIMAL);
2621 			nv_delete(np,(Dt_t*)0,0);
2622 		}
2623 	}
2624 }
2625 
2626 /*
2627  * return the node pointer in the highest level scope
2628  */
2629 Namval_t *sh_scoped(Shell_t *shp, register Namval_t *np)
2630 {
2631 	if(!dtvnext(shp->var_tree))
2632 		return(np);
2633 	return(dtsearch(shp->var_tree,np));
2634 }
2635 
2636 #if 1
2637 /*
2638  * return space separated list of names of variables in given tree
2639  */
2640 static char *tableval(Dt_t *root)
2641 {
2642 	static Sfio_t *out;
2643 	register Namval_t *np;
2644 	register int first=1;
2645 	register Dt_t *base = dtview(root,0);
2646         if(out)
2647                 sfseek(out,(Sfoff_t)0,SEEK_SET);
2648         else
2649                 out =  sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
2650 	for(np=(Namval_t*)dtfirst(root);np;np=(Namval_t*)dtnext(root,np))
2651 	{
2652                 if(!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE))
2653 		{
2654 			if(!first)
2655 				sfputc(out,' ');
2656 			else
2657 				first = 0;
2658 			sfputr(out,np->nvname,-1);
2659 		}
2660 	}
2661 	sfputc(out,0);
2662 	if(base)
2663 		dtview(root,base);
2664 	return((char*)out->_data);
2665 }
2666 #endif
2667 
2668 #if SHOPT_OPTIMIZE
2669 struct optimize
2670 {
2671 	Namfun_t	hdr;
2672 	Shell_t		*sh;
2673 	char		**ptr;
2674 	struct optimize	*next;
2675 	Namval_t	*np;
2676 };
2677 
2678 static struct optimize *opt_free;
2679 
2680 static void optimize_clear(Namval_t* np, Namfun_t *fp)
2681 {
2682 	struct optimize *op = (struct optimize*)fp;
2683 	nv_stack(np,fp);
2684 	nv_stack(np,(Namfun_t*)0);
2685 	for(;op && op->np==np; op=op->next)
2686 	{
2687 		if(op->ptr)
2688 		{
2689 			*op->ptr = 0;
2690 			op->ptr = 0;
2691 		}
2692 	}
2693 }
2694 
2695 static void put_optimize(Namval_t* np,const char *val,int flags,Namfun_t *fp)
2696 {
2697 	nv_putv(np,val,flags,fp);
2698 	optimize_clear(np,fp);
2699 }
2700 
2701 static Namfun_t *clone_optimize(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
2702 {
2703 	return((Namfun_t*)0);
2704 }
2705 
2706 static const Namdisc_t optimize_disc  = {sizeof(struct optimize),put_optimize,0,0,0,0,clone_optimize};
2707 
2708 void nv_optimize(Namval_t *np)
2709 {
2710 	Shell_t *shp = sh_getinterp();
2711 	register Namfun_t *fp;
2712 	register struct optimize *op, *xp;
2713 	if(shp->argaddr)
2714 	{
2715 		if(np==SH_LINENO)
2716 		{
2717 			shp->argaddr = 0;
2718 			return;
2719 		}
2720 		for(fp=np->nvfun; fp; fp = fp->next)
2721 		{
2722 			if(fp->disc && (fp->disc->getnum || fp->disc->getval))
2723 			{
2724 				shp->argaddr = 0;
2725 				return;
2726 			}
2727 			if(fp->disc== &optimize_disc)
2728 				break;
2729 		}
2730 		if((xp= (struct optimize*)fp) && xp->ptr==shp->argaddr)
2731 			return;
2732 		if(op = opt_free)
2733 			opt_free = op->next;
2734 		else
2735 			op=(struct optimize*)calloc(1,sizeof(struct optimize));
2736 		op->ptr = shp->argaddr;
2737 		op->np = np;
2738 		if(xp)
2739 		{
2740 			op->hdr.disc = 0;
2741 			op->next = xp->next;
2742 			xp->next = op;
2743 		}
2744 		else
2745 		{
2746 			op->hdr.disc = &optimize_disc;
2747 			op->next = (struct optimize*)shp->optlist;
2748 			shp->optlist = (void*)op;
2749 			nv_stack(np,&op->hdr);
2750 		}
2751 	}
2752 }
2753 
2754 void sh_optclear(Shell_t *shp, void *old)
2755 {
2756 	register struct optimize *op,*opnext;
2757 	for(op=(struct optimize*)shp->optlist; op; op = opnext)
2758 	{
2759 		opnext = op->next;
2760 		if(op->ptr && op->hdr.disc)
2761 		{
2762 			nv_stack(op->np,&op->hdr);
2763 			nv_stack(op->np,(Namfun_t*)0);
2764 		}
2765 		op->next = opt_free;
2766 		opt_free = op;
2767 	}
2768 	shp->optlist = old;
2769 }
2770 
2771 #else
2772 #   define	optimize_clear(np,fp)
2773 #endif /* SHOPT_OPTIMIZE */
2774 
2775 /*
2776  *   Return a pointer to a character string that denotes the value
2777  *   of <np>.  If <np> refers to an array,  return a pointer to
2778  *   the value associated with the current index.
2779  *
2780  *   If the value of <np> is an integer, the string returned will
2781  *   be overwritten by the next call to nv_getval.
2782  *
2783  *   If <np> has no value, 0 is returned.
2784  */
2785 
2786 char *nv_getval(register Namval_t *np)
2787 {
2788 	Shell_t *shp = sh_getinterp();
2789 	register union Value *up= &np->nvalue;
2790 	register int numeric;
2791 #if SHOPT_OPTIMIZE
2792 	if(!nv_local && shp->argaddr)
2793 		nv_optimize(np);
2794 #endif /* SHOPT_OPTIMIZE */
2795 	if((!np->nvfun || !np->nvfun->disc) && !nv_isattr(np,NV_ARRAY|NV_INTEGER|NV_FUNCT|NV_REF))
2796 		goto done;
2797 	if(nv_isref(np))
2798 	{
2799 		char *sub;
2800 		if(!np->nvalue.cp)
2801 			return(0);
2802 		shp->last_table = nv_reftable(np);
2803 		sub=nv_refsub(np);
2804 		np = nv_refnode(np);
2805 		if(sub)
2806 		{
2807 			sfprintf(shp->strbuf,"%s[%s]",nv_name(np),sub);
2808 			return(sfstruse(shp->strbuf));
2809 		}
2810 		return(nv_name(np));
2811 	}
2812 	if(np->nvfun && np->nvfun->disc)
2813 	{
2814 		if(!nv_local)
2815 		{
2816 			nv_local=1;
2817 			return(nv_getv(np, np->nvfun));
2818 		}
2819 		nv_local=0;
2820 	}
2821 	numeric = ((nv_isattr (np, NV_INTEGER)) != 0);
2822 	if(numeric)
2823 	{
2824 		Sflong_t  ll;
2825 		if(!up->cp)
2826 			return("0");
2827 		if(nv_isattr (np,NV_DOUBLE)==NV_DOUBLE)
2828 		{
2829 			Sfdouble_t ld;
2830 			double d;
2831 			char *format;
2832 			if(nv_isattr(np,NV_LONG))
2833 			{
2834 				ld = *up->ldp;
2835 				if(nv_isattr (np,NV_EXPNOTE))
2836 					format = "%.*Lg";
2837 				else if(nv_isattr (np,NV_HEXFLOAT))
2838 					format = "%.*La";
2839 				else
2840 					format = "%.*Lf";
2841 				sfprintf(shp->strbuf,format,nv_size(np),ld);
2842 			}
2843 			else
2844 			{
2845 				d = *up->dp;
2846 				if(nv_isattr (np,NV_EXPNOTE))
2847 					format = "%.*g";
2848 				else if(nv_isattr (np,NV_HEXFLOAT))
2849 					format = "%.*a";
2850 				else
2851 					format = "%.*f";
2852 				sfprintf(shp->strbuf,format,nv_size(np),d);
2853 			}
2854 			return(sfstruse(shp->strbuf));
2855 		}
2856 		else if(nv_isattr(np,NV_UNSIGN))
2857 		{
2858 	        	if(nv_isattr (np,NV_LONG))
2859 				ll = *(Sfulong_t*)up->llp;
2860 			else if(nv_isattr (np,NV_SHORT))
2861 			{
2862 				if(nv_isattr(np,NV_INT16P)==NV_INT16P)
2863 					ll = *(uint16_t*)(up->sp);
2864 				else
2865 					ll = (uint16_t)up->s;
2866 			}
2867 			else
2868 				ll = *(uint32_t*)(up->lp);
2869 		}
2870         	else if(nv_isattr (np,NV_LONG))
2871 			ll = *up->llp;
2872         	else if(nv_isattr (np,NV_SHORT))
2873 		{
2874 			if(nv_isattr(np,NV_INT16P)==NV_INT16P)
2875 				ll = *up->sp;
2876 			else
2877 				ll = up->s;
2878 		}
2879         	else
2880 			ll = *(up->lp);
2881 		if((numeric=nv_size(np))==10)
2882 		{
2883 			if(nv_isattr(np,NV_UNSIGN))
2884 			{
2885 				sfprintf(shp->strbuf,"%I*u",sizeof(ll),ll);
2886 				return(sfstruse(shp->strbuf));
2887 			}
2888 			numeric = 0;
2889 		}
2890 		return(fmtbase(ll,numeric, numeric&&numeric!=10));
2891 	}
2892 done:
2893 #if (_AST_VERSION>=20030127L)
2894 	/*
2895 	 * if NV_RAW flag is on, return pointer to binary data
2896 	 * otherwise, base64 encode the data and return this string
2897 	 */
2898 	if(up->cp && nv_isattr(np,NV_BINARY) && !nv_isattr(np,NV_RAW))
2899 	{
2900 		char *cp;
2901 		char *ep;
2902 		int size= nv_size(np), insize=(4*size)/3+size/45+8;
2903 		base64encode(up->cp, size, (void**)0, cp=getbuf(insize), insize, (void**)&ep);
2904 		*ep = 0;
2905 		return(cp);
2906 	}
2907 #endif
2908 	if(!nv_isattr(np,NV_LJUST|NV_RJUST) && (numeric=nv_size(np)) && up->cp && up->cp[numeric])
2909 	{
2910 		char *cp = getbuf(numeric+1);
2911 		memcpy(cp,up->cp,numeric);
2912 		cp[numeric]=0;
2913 		return(cp);
2914 	}
2915 	return ((char*)up->cp);
2916 }
2917 
2918 Sfdouble_t nv_getnum(register Namval_t *np)
2919 {
2920 	Shell_t	*shp = sh_getinterp();
2921 	register union Value *up;
2922 	register Sfdouble_t r=0;
2923 	register char *str;
2924 #if SHOPT_OPTIMIZE
2925 	if(!nv_local && shp->argaddr)
2926 		nv_optimize(np);
2927 #endif /* SHOPT_OPTIMIZE */
2928 	if(nv_istable(np))
2929 		errormsg(SH_DICT,ERROR_exit(1),e_number,nv_name(np));
2930      	if(np->nvfun && np->nvfun->disc)
2931 	{
2932 		if(!nv_local)
2933 		{
2934 			nv_local=1;
2935 			return(nv_getn(np, np->nvfun));
2936 		}
2937 		nv_local=0;
2938 	}
2939 	if(nv_isref(np))
2940 	{
2941 		str = nv_refsub(np);
2942 		np = nv_refnode(np);
2943 		if(str)
2944 			nv_putsub(np,str,0L);
2945 	}
2946      	if(nv_isattr (np, NV_INTEGER))
2947 	{
2948 		up= &np->nvalue;
2949 		if(!up->lp || up->cp==Empty)
2950 			r = 0;
2951 		else if(nv_isattr(np, NV_DOUBLE)==NV_DOUBLE)
2952 		{
2953 			if(nv_isattr(np, NV_LONG))
2954 	                       	r = *up->ldp;
2955 			else
2956        	                	r = *up->dp;
2957 		}
2958 		else if(nv_isattr(np, NV_UNSIGN))
2959 		{
2960 			if(nv_isattr(np, NV_LONG))
2961 				r = (Sflong_t)*((Sfulong_t*)up->llp);
2962 			else if(nv_isattr(np, NV_SHORT))
2963 			{
2964 				if(nv_isattr(np,NV_INT16P)==NV_INT16P)
2965 					r = (Sflong_t)(*(uint16_t*)up->sp);
2966 				else
2967 					r = (Sflong_t)((uint16_t)up->s);
2968 			}
2969 			else
2970 				r = *((uint32_t*)up->lp);
2971 		}
2972 		else
2973 		{
2974 			if(nv_isattr(np, NV_LONG))
2975 				r = *up->llp;
2976 			else if(nv_isattr(np, NV_SHORT))
2977 			{
2978 				if(nv_isattr(np,NV_INT16P)==NV_INT16P)
2979 					r = *up->sp;
2980 				else
2981 					r = up->s;
2982 			}
2983 			else
2984 				r = *up->lp;
2985 		}
2986 	}
2987 	else if((str=nv_getval(np)) && *str!=0)
2988 	{
2989 		if(nv_isattr(np,NV_LJUST|NV_RJUST) || (*str=='0' && !(str[1]=='x'||str[1]=='X')))
2990 		{
2991 			while(*str=='0')
2992 				str++;
2993 		}
2994 		r = sh_arith(shp,str);
2995 	}
2996 	return(r);
2997 }
2998 
2999 /*
3000  *   Give <np> the attributes <newatts,> and change its current
3001  *   value to conform to <newatts>.  The <size> of left and right
3002  *   justified fields may be given.
3003  */
3004 void nv_newattr (register Namval_t *np, unsigned newatts, int size)
3005 {
3006 	Shell_t *shp = sh_getinterp();
3007 	register char *sp;
3008 	register char *cp = 0;
3009 	register unsigned int n;
3010 	Namval_t *mp = 0;
3011 	Namarr_t *ap = 0;
3012 	int oldsize,oldatts,trans;
3013 	Namfun_t *fp= (newatts&NV_NODISC)?np->nvfun:0;
3014 	char *prefix = shp->prefix;
3015 	newatts &= ~NV_NODISC;
3016 
3017 	/* check for restrictions */
3018 	if(sh_isoption(SH_RESTRICTED) && ((sp=nv_name(np))==nv_name(PATHNOD) || sp==nv_name(SHELLNOD) || sp==nv_name(ENVNOD) || sp==nv_name(FPATHNOD)))
3019 		errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np));
3020 	/* handle attributes that do not change data separately */
3021 	n = np->nvflag;
3022 	trans = !(n&NV_INTEGER) && (n&(NV_LTOU|NV_UTOL));
3023 	if(newatts&NV_EXPORT)
3024 		nv_offattr(np,NV_IMPORT);
3025 	if(((n^newatts)&NV_EXPORT))
3026 	{
3027 		/* record changes to the environment */
3028 		if(n&NV_EXPORT)
3029 		{
3030 			nv_offattr(np,NV_EXPORT);
3031 			env_delete(shp->env,nv_name(np));
3032 		}
3033 		else
3034 		{
3035 			nv_onattr(np,NV_EXPORT);
3036 			sh_envput(shp->env,np);
3037 		}
3038 		if((n^newatts)==NV_EXPORT)
3039 			return;
3040 	}
3041 	oldsize = nv_size(np);
3042 	if((size==oldsize|| (n&NV_INTEGER)) && !trans && ((n^newatts)&~NV_NOCHANGE)==0)
3043 	{
3044 		if(size)
3045 			nv_setsize(np,size);
3046 		nv_offattr(np, ~NV_NOFREE);
3047 		nv_onattr(np, newatts);
3048 		return;
3049 	}
3050 	/* for an array, change all the elements */
3051 	if((ap=nv_arrayptr(np)) && ap->nelem>0)
3052 		nv_putsub(np,NIL(char*),ARRAY_SCAN);
3053 	oldsize = nv_size(np);
3054 	oldatts = np->nvflag;
3055 	if(fp)
3056 		np->nvfun = 0;
3057 	if(ap) /* add element to prevent array deletion */
3058 	{
3059 		ap->nelem++;
3060 #if SHOPT_FIXEDARRAY
3061 		if(ap->fixed)
3062 		{
3063 			nv_setsize(np,size);
3064 			np->nvflag &= NV_ARRAY;
3065 			np->nvflag |= newatts;
3066 			goto skip;
3067 		}
3068 #endif /* SHOPT_TYPEDEF */
3069 	}
3070 	do
3071 	{
3072 		nv_setsize(np,oldsize);
3073 		np->nvflag = oldatts;
3074 		if (sp = nv_getval(np))
3075  		{
3076 			if(nv_isattr(np,NV_ZFILL))
3077 				while(*sp=='0') sp++;
3078 			cp = (char*)malloc((n=strlen (sp)) + 8);
3079 			strcpy(cp, sp);
3080 			if(sp && (mp=nv_opensub(np)))
3081 			{
3082 				if(trans)
3083 				{
3084 					nv_disc(np, &ap->hdr,NV_POP);
3085 					nv_clone(np,mp,0);
3086 					nv_disc(np, &ap->hdr,NV_FIRST);
3087 					nv_offattr(mp,NV_ARRAY);
3088 				}
3089 				nv_newattr(mp,newatts&~NV_ARRAY,size);
3090 			}
3091 			if(!mp)
3092 			{
3093 				if(ap)
3094 					ap->nelem &= ~ARRAY_SCAN;
3095 				if(!trans)
3096 					_nv_unset(np,NV_RDONLY|NV_EXPORT);
3097 				if(ap)
3098 					ap->nelem |= ARRAY_SCAN;
3099 			}
3100 			if(size==0 && (newatts&NV_HOST)!=NV_HOST && (newatts&(NV_LJUST|NV_RJUST|NV_ZFILL)))
3101 				size = n;
3102 		}
3103 		else if(!trans)
3104 			_nv_unset(np,NV_EXPORT);
3105 		nv_setsize(np,size);
3106 		np->nvflag &= (NV_ARRAY|NV_NOFREE);
3107 		np->nvflag |= newatts;
3108 		if (cp)
3109 		{
3110 			if(!mp)
3111 				nv_putval (np, cp, NV_RDONLY);
3112 			free(cp);
3113 		}
3114 	}
3115 	while(ap && nv_nextsub(np));
3116 #if SHOPT_FIXEDARRAY
3117 skip:
3118 #endif /* SHOPT_TYPEDEF */
3119 	if(fp)
3120 		np->nvfun = fp;
3121 	if(ap)
3122 		ap->nelem--;
3123 	shp->prefix = prefix;
3124 	return;
3125 }
3126 
3127 static char *oldgetenv(const char *string)
3128 {
3129 	register char c0,c1;
3130 	register const char *cp, *sp;
3131 	register char **av = environ;
3132 	if(!string || (c0= *string)==0)
3133 		return(0);
3134 	if((c1=*++string)==0)
3135 		c1= '=';
3136 	while(cp = *av++)
3137 	{
3138 		if(cp[0]!=c0 || cp[1]!=c1)
3139 			continue;
3140 		sp = string;
3141 		while(*sp && *sp++ == *++cp);
3142 		if(*sp==0 && *++cp=='=')
3143 			return((char*)(cp+1));
3144 	}
3145 	return(0);
3146 }
3147 
3148 /*
3149  * This version of getenv uses the hash storage to access environment values
3150  */
3151 char *sh_getenv(const char *name)
3152 {
3153 	Shell_t *shp = sh_getinterp();
3154 	register Namval_t *np;
3155 	if(!shp->var_tree)
3156 	{
3157 #if 0
3158 		if(name[0] == 'P' && name[1] == 'A' && name[2] == 'T' && name[3] == 'H' && name[4] == 0 || name[0] == 'L' && ((name[1] == 'C' || name[1] == 'D') && name[2] == '_' || name[1] == 'A' && name[1] == 'N') || name[0] == 'V' && name[1] == 'P' && name[2] == 'A' && name[3] == 'T' && name[4] == 'H' && name[5] == 0 || name[0] == '_' && name[1] == 'R' && name[2] == 'L' && name[3] == 'D' || name[0] == '_' && name[1] == 'A' && name[2] == 'S' && name[3] == 'T' && name[4] == '_')
3159 #endif
3160 			return(oldgetenv(name));
3161 	}
3162 	else if((np = nv_search(name,shp->var_tree,0)) && nv_isattr(np,NV_EXPORT))
3163 		return(nv_getval(np));
3164 	return(0);
3165 }
3166 
3167 #ifndef _NEXT_SOURCE
3168 /*
3169  * Some dynamic linkers will make this file see the libc getenv(),
3170  * so sh_getenv() is used for the astintercept() callback.  Plain
3171  * getenv() is provided for static links.
3172  */
3173 char *getenv(const char *name)
3174 {
3175 	return sh_getenv(name);
3176 }
3177 #endif /* _NEXT_SOURCE */
3178 
3179 #undef putenv
3180 /*
3181  * This version of putenv uses the hash storage to assign environment values
3182  */
3183 int putenv(const char *name)
3184 {
3185 	Shell_t *shp = sh_getinterp();
3186 	register Namval_t *np;
3187 	if(name)
3188 	{
3189 		np = nv_open(name,shp->var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN);
3190 		if(!strchr(name,'='))
3191 			_nv_unset(np,0);
3192 	}
3193 	return(0);
3194 }
3195 
3196 /*
3197  * Override libast setenviron().
3198  */
3199 char* sh_setenviron(const char *name)
3200 {
3201 	Shell_t *shp = sh_getinterp();
3202 	register Namval_t *np;
3203 	if(name)
3204 	{
3205 		np = nv_open(name,shp->var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN);
3206 		if(strchr(name,'='))
3207 			return(nv_getval(np));
3208 		_nv_unset(np,0);
3209 	}
3210 	return("");
3211 }
3212 
3213 /*
3214  * Same linker dance as with getenv() above.
3215  */
3216 char* setenviron(const char *name)
3217 {
3218 	return sh_setenviron(name);
3219 }
3220 
3221 /*
3222  * normalize <cp> and return pointer to subscript if any
3223  * if <eq> is specified, return pointer to first = not in a subscript
3224  */
3225 static char *lastdot(char *cp, int eq)
3226 {
3227 	register char *ep=0;
3228 	register int c;
3229 	if(eq)
3230 		cp++;
3231 	while(c= mbchar(cp))
3232 	{
3233 		if(c=='[')
3234 		{
3235 			if(*cp==']')
3236 				cp++;
3237 			else
3238 				cp = nv_endsubscript((Namval_t*)0,ep=cp,0);
3239 		}
3240 		else if(c=='.')
3241 		{
3242 			if(*cp=='[')
3243 			{
3244 				cp = nv_endsubscript((Namval_t*)0,ep=cp,0);
3245 				if((ep=sh_checkid(ep+1,cp)) < cp)
3246 					cp=strcpy(ep,cp);
3247 			}
3248 			ep = 0;
3249 		}
3250 		else if(eq && c == '=')
3251 			return(cp-1);
3252 	}
3253 	return(eq?0:ep);
3254 }
3255 
3256 int nv_rename(register Namval_t *np, int flags)
3257 {
3258 	Shell_t			*shp = sh_getinterp();
3259 	register Namval_t	*mp=0,*nr=0;
3260 	register char		*cp;
3261 	int			arraynp=0,arraynr,index= -1;
3262 	Namval_t		*last_table = shp->last_table;
3263 	Dt_t			*last_root = shp->last_root;
3264 	Dt_t			*hp = 0;
3265 	char			*nvenv=0,*prefix=shp->prefix;
3266 	Namarr_t		*ap;
3267 	if(nv_isattr(np,NV_PARAM) && shp->st.prevst)
3268 	{
3269 		if(!(hp=(Dt_t*)shp->st.prevst->save_tree))
3270 			hp = dtvnext(shp->var_tree);
3271 	}
3272 	if(!nv_isattr(np,NV_MINIMAL))
3273 		nvenv = np->nvenv;
3274 	if(nvenv || (cp = nv_name(np)) && nv_isarray(np) && cp[strlen(cp)-1] == ']')
3275 		arraynp = 1;
3276 	if(!(cp=nv_getval(np)))
3277 	{
3278 		if(flags&NV_MOVE)
3279 			errormsg(SH_DICT,ERROR_exit(1),e_varname,"");
3280 		return(0);
3281 	}
3282 	if(lastdot(cp,0) && nv_isattr(np,NV_MINIMAL))
3283 		errormsg(SH_DICT,ERROR_exit(1),e_varname,nv_name(np));
3284 	arraynr = cp[strlen(cp)-1] == ']';
3285 	if(nv_isarray(np) && !(mp=nv_opensub(np)))
3286 		index=nv_aindex(np);
3287 	shp->prefix = 0;
3288 	if(!hp)
3289 		hp = shp->var_tree;
3290 	if(!(nr = nv_open(cp, hp, flags|NV_ARRAY|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
3291 	{
3292 #if SHOPT_NAMESPACE
3293 		if(shp->namespace)
3294 			hp = nv_dict(shp->namespace);
3295 		else
3296 #endif /* SHOPT_NAMESPACE */
3297 		hp = shp->var_base;
3298 	}
3299 	else if(shp->last_root)
3300 		hp = shp->last_root;
3301 	if(!nr)
3302 		nr= nv_open(cp, hp, flags|NV_NOREF|((flags&NV_MOVE)?0:NV_NOFAIL));
3303 	shp->prefix = prefix;
3304 	if(!nr)
3305 	{
3306 		if(!nv_isvtree(np))
3307 			_nv_unset(np,0);
3308 		return(0);
3309 	}
3310 	if(!mp && index>=0 && nv_isvtree(nr))
3311 	{
3312 		sfprintf(shp->strbuf,"%s[%d]%c",nv_name(np),index,0);
3313 		/* create a virtual node */
3314 		if(mp = nv_open(sfstruse(shp->strbuf),shp->var_tree,NV_VARNAME|NV_ADD|NV_ARRAY))
3315 		{
3316 			if(ap = nv_arrayptr(np))
3317 				ap->nelem++;
3318 			mp->nvenv = nvenv = (void*)np;
3319 		}
3320 	}
3321 	if(mp)
3322 	{
3323 		nvenv = (char*)np;
3324 		np = mp;
3325 	}
3326 	if(nr==np)
3327 	{
3328 		if(index<0)
3329 			return(0);
3330 		if(cp = nv_getval(np))
3331 			cp = strdup(cp);
3332 	}
3333 	_nv_unset(np,NV_EXPORT);
3334 	if(nr==np)
3335 	{
3336 		nv_putsub(np,(char*)0, index);
3337 		nv_putval(np,cp,0);
3338 		free((void*)cp);
3339 		return(1);
3340 	}
3341 	shp->prev_table = shp->last_table;
3342 	shp->prev_root = shp->last_root;
3343 	shp->last_table = last_table;
3344 	shp->last_root = last_root;
3345 	if(flags&NV_MOVE)
3346 	{
3347 		if(arraynp && !nv_isattr(np,NV_MINIMAL) && (mp=(Namval_t*)np->nvenv) && (ap=nv_arrayptr(mp)))
3348 			ap->nelem++;
3349 	}
3350 	if((nv_arrayptr(nr) && !arraynr) || nv_isvtree(nr))
3351 	{
3352 		if(ap=nv_arrayptr(np))
3353 		{
3354 			if(!ap->table)
3355 				ap->table = dtopen(&_Nvdisc,Dtoset);
3356 			if(ap->table)
3357 				mp = nv_search(nv_getsub(np),ap->table,NV_ADD);
3358 			nv_arraychild(np,mp,0);
3359 			nvenv = (void*)np;
3360 		}
3361 		else
3362 			mp = np;
3363 		nv_clone(nr,mp,(flags&NV_MOVE)|NV_COMVAR);
3364 		mp->nvenv = nvenv;
3365 		if(flags&NV_MOVE)
3366 		{
3367 			if(arraynr && !nv_isattr(nr,NV_MINIMAL) && (mp=(Namval_t*)nr->nvenv) && (ap=nv_arrayptr(mp)))
3368 			{
3369 				nv_putsub(mp,nr->nvname,0);
3370 				_nv_unset(mp,0);
3371 			}
3372 			nv_delete(nr,(Dt_t*)0,NV_NOFREE);
3373 		}
3374 	}
3375 	else
3376 	{
3377 		nv_putval(np,nv_getval(nr),0);
3378 		if(flags&NV_MOVE)
3379 		{
3380 			if(!nv_isattr(nr,NV_MINIMAL) && (mp=(Namval_t*)(nr->nvenv)) && (ap=nv_arrayptr(mp)))
3381 				ap->nelem--;
3382 			_nv_unset(nr,0);
3383 		}
3384 	}
3385 	return(1);
3386 }
3387 
3388 /*
3389  * Create a reference node from <np> to $np in dictionary <hp>
3390  */
3391 void nv_setref(register Namval_t *np, Dt_t *hp, int flags)
3392 {
3393 	Shell_t		*shp = sh_getinterp();
3394 	register Namval_t *nq=0, *nr=0;
3395 	register char	*ep,*cp;
3396 	Dt_t		*root = shp->last_root, *hpnext=0;
3397 	Namarr_t	*ap=0;
3398 	Dt_t		*openmatch;
3399 	if(nv_isref(np))
3400 		return;
3401 	if(nv_isarray(np))
3402 		errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np));
3403 	if(!(cp=nv_getval(np)))
3404 	{
3405 		_nv_unset(np,0);
3406 		nv_onattr(np,NV_REF);
3407 		return;
3408 	}
3409 	if((ep = lastdot(cp,0)) && nv_isattr(np,NV_MINIMAL))
3410 		errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np));
3411 	if(hp)
3412 		hpnext = dtvnext(hp);
3413 	if((nr=nv_open(cp, hp?hp:shp->var_tree, flags|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
3414 		nq = nr;
3415 	else if(hpnext && dtvnext(hpnext)==shp->var_base && (nr=nv_open(cp,hpnext,flags|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
3416 		nq = nr;
3417 	else if((openmatch=shp->openmatch) && hpnext==shp->var_base && (nr=nv_open(cp,hpnext,flags|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
3418 		nq = nr;
3419 	if(nq)
3420 		hp = shp->last_root;
3421 	else
3422 		hp = hp?(openmatch?openmatch:shp->var_base):shp->var_tree;
3423 	if(nr==np)
3424 	{
3425 		if(shp->namespace && nv_dict(shp->namespace)==hp)
3426 			errormsg(SH_DICT,ERROR_exit(1),e_selfref,nv_name(np));
3427 		/* bind to earlier scope, or add to global scope */
3428 		if(!(hp=dtvnext(hp)) || (nq=nv_search((char*)np,hp,NV_ADD|HASH_BUCKET))==np)
3429 			errormsg(SH_DICT,ERROR_exit(1),e_selfref,nv_name(np));
3430 		if(nv_isarray(nq))
3431 			nv_putsub(nq,(char*)0,ARRAY_UNDEF);
3432 	}
3433 #if SHOPT_FIXEDARRAY
3434 	if(nq && ep && nv_isarray(nq) && !((ap=nv_arrayptr(nq)) && ap->fixed) && !nv_getsub(nq))
3435 #else
3436 	if(nq && ep && nv_isarray(nq) && !nv_getsub(nq))
3437 #endif /* SHOPT_FIXEDARRAY */
3438 	{
3439 		if(!nv_arrayptr(nq))
3440 		{
3441 			nv_putsub(nq,"1",ARRAY_FILL);
3442 			_nv_unset(nq,NV_RDONLY);
3443 		}
3444 		nv_endsubscript(nq,ep-1,NV_ARRAY);
3445 	}
3446 	if(!nr)
3447 	{
3448 		shp->last_root = 0;
3449 		nr= nq = nv_open(cp, hp, flags);
3450 		if(shp->last_root)
3451 			hp = shp->last_root;
3452 	}
3453 	if(shp->last_root == shp->var_tree && root!=shp->var_tree)
3454 	{
3455 		_nv_unset(np,NV_RDONLY);
3456 		nv_onattr(np,NV_REF);
3457 		errormsg(SH_DICT,ERROR_exit(1),e_globalref,nv_name(np));
3458 	}
3459 	shp->instance = 1;
3460 	if(nq && !ep && (ap=nv_arrayptr(nq)) && !(ap->nelem&(ARRAY_UNDEF|ARRAY_SCAN)))
3461 		ep =  nv_getsub(nq);
3462 #if SHOPT_FIXEDARRAY
3463 	if(ep && !(ap && ap->fixed))
3464 #else
3465 	if(ep)
3466 #endif /* SHOPT_FIXEDARRAY */
3467 	{
3468 		/* cause subscript evaluation and return result */
3469 		if(nv_isarray(nq))
3470 			ep = nv_getsub(nq);
3471 		else
3472 		{
3473 			int n;
3474 			ep[n=strlen(ep)-1] = 0;
3475 			nv_putsub(nr, ep, ARRAY_FILL);
3476 			ep[n] = ']';
3477 			if(nq = nv_opensub(nr))
3478 				ep = 0;
3479 			else
3480 				ep = nv_getsub(nq=nr);
3481 		}
3482 	}
3483 	shp->instance = 0;
3484 	shp->last_root = root;
3485 	_nv_unset(np,0);
3486 	nv_delete(np,(Dt_t*)0,0);
3487 	np->nvalue.nrp = newof(0,struct Namref,1,sizeof(Dtlink_t));
3488 	np->nvalue.nrp->np = nq;
3489 	np->nvalue.nrp->root = hp;
3490 	if(ep)
3491 	{
3492 #if SHOPT_FIXEDARRAY
3493 		if(ap && ap->fixed)
3494 			np->nvalue.nrp->curi = ARRAY_FIXED|nv_arrfixed(nq,(Sfio_t*)0,1,&np->nvalue.nrp->dim);
3495 		else
3496 #endif /* SHOPT_FIXEDARRAY */
3497 			np->nvalue.nrp->sub = strdup(ep);
3498 	}
3499 	np->nvalue.nrp->table = shp->last_table;
3500 	nv_onattr(np,NV_REF|NV_NOFREE);
3501 	if(!Refdict)
3502 	{
3503 		NullNode.nvname = ".deleted";
3504 		NullNode.nvflag = NV_RDONLY;
3505 		Refdict = dtopen(&_Refdisc,Dtobag);
3506 	}
3507 	dtinsert(Refdict,np->nvalue.nrp);
3508 }
3509 
3510 /*
3511  * get the scope corresponding to <index>
3512  * whence uses the same values as lseeek()
3513  */
3514 Shscope_t *sh_getscope(int index, int whence)
3515 {
3516 	Shell_t *shp = sh_getinterp();
3517 	register struct sh_scoped *sp, *topmost;
3518 	if(whence==SEEK_CUR)
3519 		sp = &shp->st;
3520 	else
3521 	{
3522 		if ((struct sh_scoped*)shp->topscope != shp->st.self)
3523 			topmost = (struct sh_scoped*)shp->topscope;
3524 		else
3525 			topmost = &(shp->st);
3526 		sp = topmost;
3527 		if(whence==SEEK_SET)
3528 		{
3529 			int n =0;
3530 			while(sp = sp->prevst)
3531 				n++;
3532 			index = n - index;
3533 			sp = topmost;
3534 		}
3535 	}
3536 	if(index < 0)
3537 		return((Shscope_t*)0);
3538 	while(index-- && (sp = sp->prevst));
3539 	return((Shscope_t*)sp);
3540 }
3541 
3542 /*
3543  * make <scoped> the top scope and return previous scope
3544  */
3545 Shscope_t *sh_setscope(Shscope_t *scope)
3546 {
3547 	Shell_t *shp = sh_getinterp();
3548 	Shscope_t *old = (Shscope_t*)shp->st.self;
3549 	*shp->st.self = shp->st;
3550 	shp->st = *((struct sh_scoped*)scope);
3551 	shp->var_tree = scope->var_tree;
3552 	SH_PATHNAMENOD->nvalue.cp = shp->st.filename;
3553 	SH_FUNNAMENOD->nvalue.cp = shp->st.funname;
3554 	return(old);
3555 }
3556 
3557 void sh_unscope(Shell_t *shp)
3558 {
3559 	register Dt_t *root = shp->var_tree;
3560 	register Dt_t *dp = dtview(root,(Dt_t*)0);
3561 	if(dp)
3562 	{
3563 		table_unset(shp,root,NV_RDONLY|NV_NOSCOPE,dp);
3564 		if(shp->st.real_fun  && dp==shp->st.real_fun->sdict)
3565 		{
3566 			dp = dtview(dp,(Dt_t*)0);
3567 			shp->st.real_fun->sdict->view = dp;
3568 		}
3569 		shp->var_tree=dp;
3570 		dtclose(root);
3571 	}
3572 }
3573 
3574 /*
3575  * The inverse of creating a reference node
3576  */
3577 void nv_unref(register Namval_t *np)
3578 {
3579 	Namval_t *nq;
3580 	if(!nv_isref(np))
3581 		return;
3582 	nv_offattr(np,NV_NOFREE|NV_REF);
3583 	if(!np->nvalue.nrp)
3584 		return;
3585 	nq = nv_refnode(np);
3586 	if(Refdict)
3587 	{
3588 		if(np->nvalue.nrp->sub)
3589 			free(np->nvalue.nrp->sub);
3590 		dtdelete(Refdict,(void*)np->nvalue.nrp);
3591 	}
3592 	free((void*)np->nvalue.nrp);
3593 	np->nvalue.cp = strdup(nv_name(nq));
3594 #if SHOPT_OPTIMIZE
3595 	{
3596 		Namfun_t *fp;
3597 		for(fp=nq->nvfun; fp; fp = fp->next)
3598 		{
3599 			if(fp->disc== &optimize_disc)
3600 			{
3601 				optimize_clear(nq,fp);
3602 				return;
3603 			}
3604 		}
3605 	}
3606 #endif
3607 }
3608 
3609 /*
3610  * These following are for binary compatibility with the old hash library
3611  * They will be removed someday
3612  */
3613 
3614 #if defined(__IMPORT__) && defined(__EXPORT__)
3615 #   define extern __EXPORT__
3616 #endif
3617 
3618 #undef	hashscope
3619 
3620 extern Dt_t *hashscope(Dt_t *root)
3621 {
3622 	return(dtvnext(root));
3623 }
3624 
3625 #undef	hashfree
3626 
3627 extern Dt_t	*hashfree(Dt_t *root)
3628 {
3629 	Dt_t *dp = dtvnext(root);
3630 	dtclose(root);
3631 	return(dp);
3632 }
3633 
3634 #undef	hashname
3635 
3636 extern char	*hashname(void *obj)
3637 {
3638 	Namval_t *np = (Namval_t*)obj;
3639 	return(np->nvname);
3640 }
3641 
3642 #undef	hashlook
3643 
3644 extern void *hashlook(Dt_t *root, const char *name, int mode,int size)
3645 {
3646 	NOT_USED(size);
3647 	return((void*)nv_search(name,root,mode));
3648 }
3649 
3650 char *nv_name(register Namval_t *np)
3651 {
3652 	Shell_t	 *shp = sh_getinterp();
3653 	register Namval_t *table;
3654 	register Namfun_t *fp;
3655 #if SHOPT_FIXEDARRAY
3656 	Namarr_t	*ap;
3657 #endif /* SHOPT_FIXEDARRAY */
3658 	char *cp;
3659 	if(is_abuiltin(np) || is_afunction(np))
3660 	{
3661 #if SHOPT_NAMESPACE
3662 		if(shp->namespace && is_afunction(np))
3663 		{
3664 			char *name = nv_name(shp->namespace);
3665 			int n = strlen(name);
3666 			if(memcmp(np->nvname,name,n)==0 && np->nvname[n]=='.')
3667 				return(np->nvname+n+1);
3668 		}
3669 #endif /* SHOPT_NAMESPACE */
3670 		return(np->nvname);
3671 	}
3672 #if SHOPT_FIXEDARRAY
3673 	ap = nv_arrayptr(np);
3674 #endif /* SHOPT_FIXEDARRAY */
3675 	if(!nv_isattr(np,NV_MINIMAL|NV_EXPORT) && np->nvenv)
3676 	{
3677 		Namval_t *nq= shp->last_table, *mp= (Namval_t*)np->nvenv;
3678 		if(np==shp->last_table)
3679 			shp->last_table = 0;
3680 		if(nv_isarray(mp))
3681 			sfprintf(shp->strbuf,"%s[%s]",nv_name(mp),np->nvname);
3682 		else
3683 			sfprintf(shp->strbuf,"%s.%s",nv_name(mp),np->nvname);
3684 		shp->last_table = nq;
3685 		return(sfstruse(shp->strbuf));
3686 	}
3687 	if(nv_istable(np))
3688 #if 1
3689 		shp->last_table = nv_parent(np);
3690 #else
3691 		shp->last_table = nv_create(np,0, NV_LAST,(Namfun_t*)0);
3692 #endif
3693 	else if(!nv_isref(np))
3694 	{
3695 		for(fp= np->nvfun ; fp; fp=fp->next)
3696 		if(fp->disc && fp->disc->namef)
3697 		{
3698 			if(np==shp->last_table)
3699 				shp->last_table = 0;
3700 			return((*fp->disc->namef)(np,fp));
3701 		}
3702 	}
3703 	if(!(table=shp->last_table) || *np->nvname=='.' || table==shp->namespace || np==table)
3704 	{
3705 #if SHOPT_FIXEDARRAY
3706 		if(!ap || !ap->fixed || (ap->nelem&ARRAY_UNDEF))
3707 			return(np->nvname);
3708 		table = 0;
3709 #else
3710 		return(np->nvname);
3711 #endif /* SHOPT_FIXEDARRAY */
3712 	}
3713 	if(table)
3714 	{
3715 		cp = nv_name(table);
3716 		sfprintf(shp->strbuf,"%s.%s",cp,np->nvname);
3717 	}
3718 	else
3719 		sfprintf(shp->strbuf,"%s",np->nvname);
3720 #if SHOPT_FIXEDARRAY
3721 	if(ap && ap->fixed)
3722 		nv_arrfixed(np,shp->strbuf,1,(char*)0);
3723 #endif /* SHOPT_FIXEDARRAY */
3724 	return(sfstruse(shp->strbuf));
3725 }
3726 
3727 Namval_t *nv_lastdict(void)
3728 {
3729 	Shell_t *shp = sh_getinterp();
3730 	return(shp->last_table);
3731 }
3732 
3733 #undef nv_context
3734 /*
3735  * returns the data context for a builtin
3736  */
3737 void *nv_context(Namval_t *np)
3738 {
3739 	return((void*)np->nvfun);
3740 }
3741 
3742 #define DISABLE /* proto workaround */
3743 
3744 int nv_isnull DISABLE (register Namval_t *np)
3745 {
3746 	return(nv_isnull(np));
3747 }
3748 
3749 #undef nv_setsize
3750 int nv_setsize(register Namval_t *np, int size)
3751 {
3752 	int oldsize = nv_size(np);
3753 	if(size>=0)
3754 		np->nvsize = size;
3755 	return(oldsize);
3756 }
3757 
3758 Shell_t	*nv_shell(Namval_t *np)
3759 {
3760 	Namfun_t *fp;
3761 	for(fp=np->nvfun;fp;fp=fp->next)
3762 	{
3763 		if(!fp->disc)
3764 			return((Shell_t*)fp->last);
3765 	}
3766 	return(0);
3767 }
3768 
3769 #undef nv_unset
3770 
3771 void	nv_unset(register Namval_t *np)
3772 {
3773 	_nv_unset(np,0);
3774 	return;
3775 }
3776