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