xref: /illumos-gate/usr/src/contrib/ast/src/cmd/ksh93/sh/name.c (revision 5ae8bd53)
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))
38 static char	*savesub = 0;
39 
40 #if !_lib_pathnative && _lib_uwin_path
41 
42 #define _lib_pathnative		1
43 
44 extern int	uwin_path(const char*, char*, int);
45 
46 size_t
47 pathnative(const char* path, char* buf, size_t siz)
48 {
49 	return uwin_path(path, buf, siz);
50 }
51 
52 #endif /* _lib_pathnative */
53 
54 static 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
59 static void	ltou(char*);
60 static void	utol(char*);
61 static void	rightjust(char*, int, int);
62 static char	*lastdot(char*, int);
63 
64 struct 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 
104 char		nv_local = 0;
105 #ifndef _ENV_H
106 static void(*nullscan)(Namval_t*,void*);
107 #endif
108 
109 #if ( SFIO_VERSION  <= 20010201L )
110 #   define _data        data
111 #endif
112 
113 #if !SHOPT_MULTIBYTE
114 #   define mbchar(p)       (*(unsigned char*)p++)
115 #endif /* SHOPT_MULTIBYTE */
116 
117 /* ========	name value pair routines	======== */
118 
119 #include	"shnodes.h"
120 #include	"builtins.h"
121 
122 static char *getbuf(size_t len)
123 {
124 	static char *buf;
125 	static size_t buflen;
126 	if(buflen < len)
127 	{
128 		if(buflen==0)
129 			buf = (char*)malloc(len);
130 		else
131 			buf = (char*)realloc(buf,len);
132 		buflen = len;
133 	}
134 	return(buf);
135 }
136 
137 #ifdef _ENV_H
138 void sh_envput(Env_t* ep,Namval_t *np)
139 {
140 	int offset = staktell();
141 	Namarr_t *ap = nv_arrayptr(np);
142 	char *val;
143 	if(ap)
144 	{
145 		if(ap->nelem&ARRAY_UNDEF)
146 			nv_putsub(np,"0",0L);
147 		else if(!(val=nv_getsub(np)) || strcmp(val,"0"))
148 			return;
149 	}
150 	if(!(val = nv_getval(np)))
151 		return;
152 	stakputs(nv_name(np));
153 	stakputc('=');
154 	stakputs(val);
155 	stakseek(offset);
156 	env_add(ep,stakptr(offset),ENV_STRDUP);
157 }
158 #endif
159 
160 /*
161  * output variable name in format for re-input
162  */
163 void nv_outname(Sfio_t *out, char *name, int len)
164 {
165 	const char *cp=name, *sp;
166 	int c, offset = staktell();
167 	while(sp= strchr(cp,'['))
168 	{
169 		if(len>0 && cp+len <= sp)
170 			break;
171 		sfwrite(out,cp,++sp-cp);
172 		stakseek(offset);
173 		while(c= *sp++)
174 		{
175 			if(c==']')
176 				break;
177 			else if(c=='\\')
178 			{
179 				if(*sp=='[' || *sp==']' || *sp=='\\')
180 					c = *sp++;
181 			}
182 			stakputc(c);
183 		}
184 		stakputc(0);
185 		sfputr(out,sh_fmtq(stakptr(offset)),-1);
186 		if(len>0)
187 		{
188 			sfputc(out,']');
189 			return;
190 		}
191 		cp = sp-1;
192 	}
193 	if(*cp)
194 	{
195 		if(len>0)
196 			sfwrite(out,cp,len);
197 		else
198 			sfputr(out,cp,-1);
199 	}
200 	stakseek(offset);
201 }
202 
203 #if SHOPT_TYPEDEF
204 Namval_t *nv_addnode(Namval_t* np, int remove)
205 {
206 	register struct sh_type	*sp = (struct sh_type*)sh.mktype;
207 	register int		i;
208 	register char		*name=0;
209 	if(sp->numnodes==0 && !nv_isnull(np) && sh.last_table)
210 	{
211 		/* could be an redefine */
212 		Dt_t *root = nv_dict(sh.last_table);
213 		sp->rp = np;
214 		nv_delete(np,root,NV_NOFREE);
215 		np = nv_search(sp->rp->nvname,root,NV_ADD);
216 	}
217 	if(sp->numnodes && memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1))
218 	{
219 		name = (sp->nodes[0])->nvname;
220 		i = strlen(name);
221 		if(memcmp(np->nvname,name,i))
222 			return(np);
223 	}
224 	if(sp->rp && sp->numnodes)
225 	{
226 		/* check for a redefine */
227 		if(name && np->nvname[i]=='.' && np->nvname[i+1]=='_' && np->nvname[i+2]==0)
228 			sp->rp = 0;
229 		else
230 		{
231 			Dt_t *root = nv_dict(sh.last_table);
232 			nv_delete(sp->nodes[0],root,NV_NOFREE);
233 			dtinsert(root,sp->rp);
234 			errormsg(SH_DICT,ERROR_exit(1),e_redef,sp->nodes[0]->nvname);
235 		}
236 	}
237 	for(i=0; i < sp->numnodes; i++)
238 	{
239 		if(np == sp->nodes[i])
240 		{
241 			if(remove)
242 			{
243 				while(++i < sp->numnodes)
244 					sp->nodes[i-1] = sp->nodes[i];
245 				sp->numnodes--;
246 			}
247 			return(np);
248 		}
249 	}
250 	if(remove)
251 		return(np);
252 	if(sp->numnodes==sp->maxnodes)
253 	{
254 		sp->maxnodes += 20;
255 		sp->nodes = (Namval_t**)realloc(sp->nodes,sizeof(Namval_t*)*sp->maxnodes);
256 	}
257 	sp->nodes[sp->numnodes++] = np;
258 	return(np);
259 }
260 #endif /* SHOPT_TYPEDEF */
261 
262 /*
263  * given a list of assignments, determine <name> is on the list
264    returns a pointer to the argnod on the list or NULL
265  */
266 struct argnod *nv_onlist(struct argnod *arg, const char *name)
267 {
268 	char *cp;
269 	int len = strlen(name);
270 	for(;arg; arg=arg->argnxt.ap)
271 	{
272 		if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED|ARG_MESSAGE)))
273 			cp = ((struct fornod*)arg->argchn.ap)->fornam;
274 		else
275 			cp = arg->argval;
276 		if(memcmp(cp,name,len)==0 && (cp[len]==0 || cp[len]=='='))
277 			return(arg);
278 	}
279 	return(0);
280 }
281 
282 /*
283  * Perform parameter assignment for a linked list of parameters
284  * <flags> contains attributes for the parameters
285  */
286 void nv_setlist(register struct argnod *arg,register int flags, 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  */
622 static 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  */
638 static 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  */
671 static 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 
685 Namval_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  */
1074 void 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  */
1117 Namval_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 	}
1254 nocache:
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;
1278 skip:
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  */
1359 void 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  */
1749 static 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
1847 static 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
1865 static 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
1888 static 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
1917 static 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
1937 char **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
1954 char **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 
1984 struct scan
1985 {
1986 	void    (*scanfn)(Namval_t*, void*);
1987 	int     scanmask;
1988 	int     scanflags;
1989 	int     scancount;
1990 	void    *scandata;
1991 };
1992 
1993 static 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  */
2027 int 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  */
2049 void 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 
2079 void	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  */
2119 void	nv_close(Namval_t *np)
2120 {
2121 	NOT_USED(np);
2122 }
2123 
2124 static 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  */
2185 void	_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 	}
2267 done:
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  */
2292 Namval_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  */
2303 static 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
2332 struct optimize
2333 {
2334 	Namfun_t	hdr;
2335 	Shell_t		*sh;
2336 	char		**ptr;
2337 	struct optimize	*next;
2338 	Namval_t	*np;
2339 };
2340 
2341 static struct optimize *opt_free;
2342 
2343 static 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 
2358 static 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 
2364 static Namfun_t *clone_optimize(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
2365 {
2366 	return((Namfun_t*)0);
2367 }
2368 
2369 static const Namdisc_t optimize_disc  = {sizeof(struct optimize),put_optimize,0,0,0,0,clone_optimize};
2370 
2371 void 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 
2416 void 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 
2448 char *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 	}
2545 done:
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 
2571 Sfdouble_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  */
2655 void 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 
2747 static 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  */
2771 char *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  */
2795 char *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  */
2805 int 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  */
2820 char* 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  */
2836 char* setenviron(const char *name)
2837 {
2838 	return sh_setenviron(name);
2839 }
2840 
2841 /*
2842  * convert <str> to upper case
2843  */
2844 static 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  */
2857 static 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  */
2871 static 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 
2902 int 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  */
2985 void 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  */
3064 Shscope_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  */
3094 Shscope_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 
3105 void 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  */
3122 void 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 
3159 extern Dt_t *hashscope(Dt_t *root)
3160 {
3161 	return(dtvnext(root));
3162 }
3163 
3164 #undef	hashfree
3165 
3166 extern 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 
3175 extern char	*hashname(void *obj)
3176 {
3177 	Namval_t *np = (Namval_t*)obj;
3178 	return(np->nvname);
3179 }
3180 
3181 #undef	hashlook
3182 
3183 extern 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 
3189 char *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 
3231 Namval_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  */
3240 void *nv_context(Namval_t *np)
3241 {
3242 	return((void*)np->nvfun);
3243 }
3244 
3245 #define DISABLE /* proto workaround */
3246 
3247 int nv_isnull DISABLE (register Namval_t *np)
3248 {
3249 	return(nv_isnull(np));
3250 }
3251 
3252 #undef nv_setsize
3253 int 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 
3261 Shell_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 
3274 void	nv_unset(register Namval_t *np)
3275 {
3276 	_nv_unset(np,0);
3277 	return;
3278 }
3279