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