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