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