1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1982-2012 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                 Eclipse Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*          http://www.eclipse.org/org/documents/epl-v10.html           *
11*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21/*
22 * AT&T Labs
23 *
24 */
25
26#include        "defs.h"
27#include        "variables.h"
28#include        "builtins.h"
29#include        "path.h"
30
31static void assign(Namval_t*,const char*,int,Namfun_t*);
32
33int nv_compare(Dt_t* dict, Void_t *sp, Void_t *dp, Dtdisc_t *disc)
34{
35	if(sp==dp)
36		return(0);
37	return(strcmp((char*)sp,(char*)dp));
38}
39
40/*
41 * call the next getval function in the chain
42 */
43char *nv_getv(Namval_t *np, register Namfun_t *nfp)
44{
45	register Namfun_t	*fp;
46	register char *cp;
47	if((fp = nfp) != NIL(Namfun_t*) && !nv_local)
48		fp = nfp = nfp->next;
49	nv_local=0;
50	for(; fp; fp=fp->next)
51	{
52		if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
53			continue;
54		if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
55			break;
56	}
57	if(fp && fp->disc->getval)
58		cp = (*fp->disc->getval)(np,fp);
59	else if(fp && fp->disc->getnum)
60	{
61		sfprintf(sh.strbuf,"%.*Lg",12,(*fp->disc->getnum)(np,fp));
62		cp = sfstruse(sh.strbuf);
63	}
64	else
65	{
66		nv_local=1;
67		cp = nv_getval(np);
68	}
69	return(cp);
70}
71
72/*
73 * call the next getnum function in the chain
74 */
75Sfdouble_t nv_getn(Namval_t *np, register Namfun_t *nfp)
76{
77	register Namfun_t	*fp;
78	register Sfdouble_t	d=0;
79	Shell_t			*shp = sh_getinterp();
80	char *str;
81	if((fp = nfp) != NIL(Namfun_t*) && !nv_local)
82		fp = nfp = nfp->next;
83	nv_local=0;
84	for(; fp; fp=fp->next)
85	{
86		if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
87			continue;
88		if(!fp->disc->getnum && nv_isattr(np,NV_INTEGER))
89			continue;
90		if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
91			break;
92	}
93	if(fp && fp->disc && fp->disc->getnum)
94		d = (*fp->disc->getnum)(np,fp);
95	else if(nv_isattr(np,NV_INTEGER))
96	{
97		nv_local = 1;
98		d =  nv_getnum(np);
99	}
100	else
101	{
102		if(fp && fp->disc && fp->disc->getval)
103			str = (*fp->disc->getval)(np,fp);
104		else
105			str = nv_getv(np,fp?fp:nfp);
106		if(str && *str)
107		{
108			if(nv_isattr(np,NV_LJUST|NV_RJUST) || (*str=='0' && !(str[1]=='x'||str[1]=='X')))
109			{
110				while(*str=='0')
111					str++;
112			}
113			d = sh_arith(shp,str);
114		}
115	}
116	return(d);
117}
118
119/*
120 * call the next assign function in the chain
121 */
122void nv_putv(Namval_t *np, const char *value, int flags, register Namfun_t *nfp)
123{
124	register Namfun_t	*fp, *fpnext;
125	Namarr_t		*ap;
126	if((fp=nfp) != NIL(Namfun_t*) && !nv_local)
127		fp = nfp = nfp->next;
128	nv_local=0;
129	if(flags&NV_NODISC)
130		fp = 0;
131	for(; fp; fp=fpnext)
132	{
133		fpnext = fp->next;
134		if(!fp->disc || !fp->disc->putval)
135		{
136			if(!value && (!(ap=nv_arrayptr(np)) || ap->nelem==0))
137			{
138				if(fp->disc || !(fp->nofree&1))
139					nv_disc(np,fp,NV_POP);
140				if(!(fp->nofree&1))
141					free((void*)fp);
142			}
143			continue;
144		}
145		if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
146			break;
147	}
148	if(!value && (flags&NV_TYPE) && fp && fp->disc->putval==assign)
149		fp = 0;
150	if(fp && fp->disc->putval)
151		(*fp->disc->putval)(np,value, flags, fp);
152	else
153	{
154		nv_local=1;
155		if(value)
156			nv_putval(np, value, flags);
157		else
158			_nv_unset(np, flags&(NV_RDONLY|NV_EXPORT));
159	}
160}
161
162#define	LOOKUPS		0
163#define	ASSIGN		1
164#define	APPEND		2
165#define	UNASSIGN	3
166#define	LOOKUPN		4
167#define BLOCKED		((Namval_t*)&nv_local)
168
169struct	vardisc
170{
171	Namfun_t	fun;
172	Namval_t	*disc[5];
173};
174
175struct blocked
176{
177	struct blocked	*next;
178	Namval_t	*np;
179	int		flags;
180	void		*sub;
181	int		isub;
182};
183
184static struct blocked	*blist;
185
186#define isblocked(bp,type)	((bp)->flags & (1<<(type)))
187#define block(bp,type)		((bp)->flags |= (1<<(type)))
188#define unblock(bp,type)	((bp)->flags &= ~(1<<(type)))
189
190/*
191 * returns pointer to blocking structure
192 */
193static struct blocked *block_info(Namval_t *np, struct blocked *pp)
194{
195	register struct blocked	*bp;
196	void			*sub=0;
197	int			isub=0;
198	if(nv_isarray(np) && (isub=nv_aindex(np)) < 0)
199		sub = nv_associative(np,(const char*)0,NV_ACURRENT);
200	for(bp=blist ; bp; bp=bp->next)
201	{
202		if(bp->np==np && bp->sub==sub && bp->isub==isub)
203			return(bp);
204	}
205	if(pp)
206	{
207		pp->np = np;
208		pp->flags = 0;
209		pp->isub = isub;
210		pp->sub = sub;
211		pp->next = blist;
212		blist = pp;
213	}
214	return(pp);
215}
216
217static void block_done(struct blocked *bp)
218{
219	blist = bp = bp->next;
220	if(bp && (bp->isub>=0 || bp->sub))
221		nv_putsub(bp->np, bp->sub,(bp->isub<0?0:bp->isub)|ARRAY_SETSUB);
222}
223
224/*
225 * free discipline if no more discipline functions
226 */
227static void chktfree(register Namval_t *np, register struct vardisc *vp)
228{
229	register int n;
230	for(n=0; n< sizeof(vp->disc)/sizeof(*vp->disc); n++)
231	{
232		if(vp->disc[n])
233			break;
234	}
235	if(n>=sizeof(vp->disc)/sizeof(*vp->disc))
236	{
237		/* no disc left so pop */
238		Namfun_t *fp;
239		if((fp=nv_stack(np, NIL(Namfun_t*))) && !(fp->nofree&1))
240			free((void*)fp);
241	}
242}
243
244/*
245 * This function performs an assignment disc on the given node <np>
246 */
247static void	assign(Namval_t *np,const char* val,int flags,Namfun_t *handle)
248{
249	int		type = (flags&NV_APPEND)?APPEND:ASSIGN;
250	register	struct vardisc *vp = (struct vardisc*)handle;
251	register	Namval_t *nq =  vp->disc[type];
252	struct blocked	block, *bp = block_info(np, &block);
253	Namval_t	node;
254	union Value	*up = np->nvalue.up;
255#if SHOPT_TYPEDEF
256	Namval_t	*tp, *nr;
257	if(val && (tp=nv_type(np)) && (nr=nv_open(val,sh.var_tree,NV_VARNAME|NV_ARRAY|NV_NOADD|NV_NOFAIL)) && tp==nv_type(nr))
258	{
259		char *sub = nv_getsub(np);
260		_nv_unset(np,0);
261		if(sub)
262		{
263			nv_putsub(np, sub, ARRAY_ADD);
264			nv_putval(np,nv_getval(nr), 0);
265		}
266		else
267			nv_clone(nr,np,0);
268		goto done;
269	}
270#endif /* SHOPT_TYPEDEF */
271	if(val || isblocked(bp,type))
272	{
273		if(!nq || isblocked(bp,type))
274		{
275			nv_putv(np,val,flags,handle);
276			goto done;
277		}
278		node = *SH_VALNOD;
279		if(!nv_isnull(SH_VALNOD))
280		{
281			nv_onattr(SH_VALNOD,NV_NOFREE);
282			_nv_unset(SH_VALNOD,0);
283		}
284		if(flags&NV_INTEGER)
285			nv_onattr(SH_VALNOD,(flags&(NV_LONG|NV_DOUBLE|NV_EXPNOTE|NV_HEXFLOAT|NV_SHORT)));
286		nv_putval(SH_VALNOD, val, (flags&NV_INTEGER)?flags:NV_NOFREE);
287	}
288	else
289		nq =  vp->disc[type=UNASSIGN];
290	if(nq && !isblocked(bp,type))
291	{
292		int bflag=0;
293		block(bp,type);
294		if (type==APPEND && (bflag= !isblocked(bp,LOOKUPS)))
295			block(bp,LOOKUPS);
296		sh_fun(nq,np,(char**)0);
297		unblock(bp,type);
298		if(bflag)
299			unblock(bp,LOOKUPS);
300		if(!vp->disc[type])
301			chktfree(np,vp);
302	}
303	if(nv_isarray(np))
304		np->nvalue.up = up;
305	if(val)
306	{
307		register char *cp;
308		Sfdouble_t d;
309		if(nv_isnull(SH_VALNOD))
310			cp=0;
311		else if(flags&NV_INTEGER)
312		{
313			d = nv_getnum(SH_VALNOD);
314			cp = (char*)(&d);
315			flags |= (NV_LONG|NV_DOUBLE);
316			flags &= ~NV_SHORT;
317		}
318		else
319			cp = nv_getval(SH_VALNOD);
320		if(cp)
321			nv_putv(np,cp,flags|NV_RDONLY,handle);
322		_nv_unset(SH_VALNOD,0);
323		/* restore everything but the nvlink field */
324		memcpy(&SH_VALNOD->nvname,  &node.nvname, sizeof(node)-sizeof(node.nvlink));
325	}
326	else if(sh_isstate(SH_INIT) || np==SH_FUNNAMENOD)
327	{
328		/* don't free functions during reinitialization */
329		nv_putv(np,val,flags,handle);
330	}
331	else if(!nq || !isblocked(bp,type))
332	{
333		Dt_t *root = sh_subfuntree(1);
334		int n;
335		Namarr_t *ap;
336		block(bp,type);
337		nv_disc(np,handle,NV_POP);
338		nv_putv(np, val, flags, handle);
339		if(sh.subshell)
340			goto done;
341		if(nv_isarray(np) && (ap=nv_arrayptr(np)) && ap->nelem>0)
342			goto done;
343		for(n=0; n < sizeof(vp->disc)/sizeof(*vp->disc); n++)
344		{
345			if((nq=vp->disc[n]) && !nv_isattr(nq,NV_NOFREE))
346			{
347				_nv_unset(nq,0);
348				dtdelete(root,nq);
349			}
350		}
351		unblock(bp,type);
352		if(!(handle->nofree&1))
353			free(handle);
354	}
355done:
356	if(bp== &block)
357		block_done(bp);
358	if(nq && nq->nvalue.rp->running==1)
359	{
360		nq->nvalue.rp->running=0;
361		_nv_unset(nq,0);
362	}
363}
364
365/*
366 * This function executes a lookup disc and then performs
367 * the lookup on the given node <np>
368 */
369static char*	lookup(Namval_t *np, int type, Sfdouble_t *dp,Namfun_t *handle)
370{
371	register struct vardisc	*vp = (struct vardisc*)handle;
372	struct blocked		block, *bp = block_info(np, &block);
373	register Namval_t	*nq = vp->disc[type];
374	register char		*cp=0;
375	Namval_t		node;
376	union Value		*up = np->nvalue.up;
377	if(nq && !isblocked(bp,type))
378	{
379		node = *SH_VALNOD;
380		if(!nv_isnull(SH_VALNOD))
381		{
382			nv_onattr(SH_VALNOD,NV_NOFREE);
383			_nv_unset(SH_VALNOD,0);
384		}
385		if(type==LOOKUPN)
386		{
387			nv_onattr(SH_VALNOD,NV_DOUBLE|NV_INTEGER);
388			nv_setsize(SH_VALNOD,10);
389		}
390		block(bp,type);
391		sh_fun(nq,np,(char**)0);
392		unblock(bp,type);
393		if(!vp->disc[type])
394			chktfree(np,vp);
395		if(type==LOOKUPN)
396		{
397			cp = (char*)(SH_VALNOD->nvalue.cp);
398			*dp = nv_getnum(SH_VALNOD);
399		}
400		else if(cp = nv_getval(SH_VALNOD))
401			cp = stkcopy(stkstd,cp);
402		_nv_unset(SH_VALNOD,NV_RDONLY);
403		if(!nv_isnull(&node))
404		{
405			/* restore everything but the nvlink field */
406			memcpy(&SH_VALNOD->nvname,  &node.nvname, sizeof(node)-sizeof(node.nvlink));
407		}
408	}
409	if(nv_isarray(np))
410		np->nvalue.up = up;
411	if(!cp)
412	{
413		if(type==LOOKUPS)
414			cp = nv_getv(np,handle);
415		else
416			*dp = nv_getn(np,handle);
417	}
418	if(bp== &block)
419		block_done(bp);
420	if(nq && nq->nvalue.rp->running==1)
421	{
422		nq->nvalue.rp->running=0;
423		_nv_unset(nq,0);
424	}
425	return(cp);
426}
427
428static char*	lookups(Namval_t *np, Namfun_t *handle)
429{
430	return(lookup(np,LOOKUPS,(Sfdouble_t*)0,handle));
431}
432
433static Sfdouble_t lookupn(Namval_t *np, Namfun_t *handle)
434{
435	Sfdouble_t	d;
436	lookup(np,LOOKUPN, &d ,handle);
437	return(d);
438}
439
440
441/*
442 * Set disc on given <event> to <action>
443 * If action==np, the current disc is returned
444 * A null return value indicates that no <event> is known for <np>
445 * If <event> is NULL, then return the event name after <action>
446 * If <event> is NULL, and <action> is NULL, return the first event
447 */
448char *nv_setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp)
449{
450	register struct vardisc *vp = (struct vardisc*)np->nvfun;
451	register int type;
452	char *empty = "";
453	while(vp)
454	{
455		if(vp->fun.disc && (vp->fun.disc->setdisc || vp->fun.disc->putval == assign))
456			break;
457		vp = (struct vardisc*)vp->fun.next;
458	}
459	if(vp && !vp->fun.disc)
460		vp = 0;
461	if(np == (Namval_t*)fp)
462	{
463		register const char *name;
464		register int getname=0;
465		/* top level call, check for get/set */
466		if(!event)
467		{
468			if(!action)
469				return((char*)nv_discnames[0]);
470			getname=1;
471			event = (char*)action;
472		}
473		for(type=0; name=nv_discnames[type]; type++)
474		{
475			if(strcmp(event,name)==0)
476				break;
477		}
478		if(getname)
479		{
480			event = 0;
481			if(name && !(name = nv_discnames[++type]))
482				action = 0;
483		}
484		if(!name)
485		{
486			for(fp=(Namfun_t*)vp; fp; fp=fp->next)
487			{
488				if(fp->disc && fp->disc->setdisc)
489					return((*fp->disc->setdisc)(np,event,action,fp));
490			}
491		}
492		else if(getname)
493			return((char*)name);
494	}
495	if(!fp)
496		return(NIL(char*));
497	if(np != (Namval_t*)fp)
498	{
499		/* not the top level */
500		while(fp = fp->next)
501		{
502			if(fp->disc && fp->disc->setdisc)
503				return((*fp->disc->setdisc)(np,event,action,fp));
504		}
505		return(NIL(char*));
506	}
507	/* Handle GET/SET/APPEND/UNSET disc */
508	if(vp && vp->fun.disc->putval!=assign)
509		vp = 0;
510	if(!vp)
511	{
512		Namdisc_t	*dp;
513		if(action==np)
514			return((char*)action);
515		if(!(vp = newof(NIL(struct vardisc*),struct vardisc,1,sizeof(Namdisc_t))))
516			return(0);
517		dp = (Namdisc_t*)(vp+1);
518		vp->fun.disc = dp;
519		memset(dp,0,sizeof(*dp));
520		dp->dsize = sizeof(struct vardisc);
521		dp->putval = assign;
522		if(nv_isarray(np) && !nv_arrayptr(np))
523			nv_putsub(np,(char*)0, 1);
524		nv_stack(np, (Namfun_t*)vp);
525	}
526	if(action==np)
527	{
528		action = vp->disc[type];
529		empty = 0;
530	}
531	else if(action)
532	{
533		Namdisc_t *dp = (Namdisc_t*)vp->fun.disc;
534		if(type==LOOKUPS)
535			dp->getval = lookups;
536		else if(type==LOOKUPN)
537			dp->getnum = lookupn;
538		vp->disc[type] = action;
539	}
540	else
541	{
542		struct blocked *bp;
543		action = vp->disc[type];
544		vp->disc[type] = 0;
545		if(!(bp=block_info(np,(struct blocked*)0)) || !isblocked(bp,UNASSIGN))
546			chktfree(np,vp);
547	}
548	return(action?(char*)action:empty);
549}
550
551/*
552 * Set disc on given <event> to <action>
553 * If action==np, the current disc is returned
554 * A null return value indicates that no <event> is known for <np>
555 * If <event> is NULL, then return the event name after <action>
556 * If <event> is NULL, and <action> is NULL, return the first event
557 */
558static char *setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp)
559{
560	register Nambfun_t *vp = (Nambfun_t*)fp;
561	register int type,getname=0;
562	register const char *name;
563	const char **discnames = vp->bnames;
564	/* top level call, check for discipline match */
565	if(!event)
566	{
567		if(!action)
568			return((char*)discnames[0]);
569		getname=1;
570		event = (char*)action;
571	}
572	for(type=0; name=discnames[type]; type++)
573	{
574		if(strcmp(event,name)==0)
575			break;
576	}
577	if(getname)
578	{
579		event = 0;
580		if(name && !(name = discnames[++type]))
581			action = 0;
582	}
583	if(!name)
584		return(nv_setdisc(np,event,action,fp));
585	else if(getname)
586		return((char*)name);
587	/* Handle the disciplines */
588	if(action==np)
589		action = vp->bltins[type];
590	else if(action)
591	{
592		Namval_t *tp = nv_type(np);
593		if(tp && (np = (Namval_t*)vp->bltins[type]) && nv_isattr(np,NV_STATICF))
594			errormsg(SH_DICT,ERROR_exit(1),e_staticfun,name,tp->nvname);
595		vp->bltins[type] = action;
596	}
597	else
598	{
599		action = vp->bltins[type];
600		vp->bltins[type] = 0;
601	}
602	return(action?(char*)action:"");
603}
604
605static void putdisc(Namval_t* np, const char* val, int flag, Namfun_t* fp)
606{
607	nv_putv(np,val,flag,fp);
608	if(!val && !(flag&NV_NOFREE))
609	{
610		register Nambfun_t *vp = (Nambfun_t*)fp;
611		register int i;
612		for(i=0; vp->bnames[i]; i++)
613		{
614			register Namval_t *mp;
615			if((mp=vp->bltins[i]) && !nv_isattr(mp,NV_NOFREE))
616			{
617				if(is_abuiltin(mp))
618				{
619					if(mp->nvfun && !nv_isattr(mp,NV_NOFREE))
620						free((void*)mp->nvfun);
621					dtdelete(sh.bltin_tree,mp);
622					free((void*)mp);
623				}
624			}
625		}
626		nv_disc(np,fp,NV_POP);
627		if(!(fp->nofree&1))
628			free((void*)fp);
629
630	}
631}
632
633static const Namdisc_t Nv_bdisc	= {   0, putdisc, 0, 0, setdisc };
634
635Namfun_t *nv_clone_disc(register Namfun_t *fp, int flags)
636{
637	register Namfun_t	*nfp;
638	register int		size;
639	if(!fp->disc && !fp->next && (fp->nofree&1))
640		return(fp);
641	if(!(size=fp->dsize) && (!fp->disc || !(size=fp->disc->dsize)))
642		size = sizeof(Namfun_t);
643	if(!(nfp=newof(NIL(Namfun_t*),Namfun_t,1,size-sizeof(Namfun_t))))
644		return(0);
645	memcpy(nfp,fp,size);
646	nfp->nofree &= ~1;
647	nfp->nofree |= (flags&NV_RDONLY)?1:0;
648	return(nfp);
649}
650
651int nv_adddisc(Namval_t *np, const char **names, Namval_t **funs)
652{
653	register Nambfun_t *vp;
654	register int n=0;
655	register const char **av=names;
656	if(av)
657	{
658		while(*av++)
659			n++;
660	}
661	if(!(vp = newof(NIL(Nambfun_t*),Nambfun_t,1,n*sizeof(Namval_t*))))
662		return(0);
663	vp->fun.dsize = sizeof(Nambfun_t)+n*sizeof(Namval_t*);
664	vp->fun.nofree |= 2;
665	vp->num = n;
666	if(funs)
667		memcpy((void*)vp->bltins, (void*)funs,n*sizeof(Namval_t*));
668	else while(n>=0)
669		vp->bltins[n--] = 0;
670	vp->fun.disc = &Nv_bdisc;
671	vp->bnames = names;
672	nv_stack(np,&vp->fun);
673	return(1);
674}
675
676/*
677 * push, pop, clne, or reorder disciplines onto node <np>
678 * mode can be one of
679 *    NV_FIRST:  Move or push <fp> to top of the stack or delete top
680 *    NV_LAST:	 Move or push <fp> to bottom of stack or delete last
681 *    NV_POP:	 Delete <fp> from top of the stack
682 *    NV_CLONE:  Replace fp with a copy created my malloc() and return it
683 */
684Namfun_t *nv_disc(register Namval_t *np, register Namfun_t* fp, int mode)
685{
686	Namfun_t *lp, **lpp;
687	if(nv_isref(np))
688		return(0);
689	if(mode==NV_CLONE && !fp)
690		return(0);
691	if(fp)
692	{
693		fp->subshell = sh.subshell;
694		if((lp=np->nvfun)==fp)
695		{
696			if(mode==NV_CLONE)
697			{
698				lp = nv_clone_disc(fp,0);
699				return(np->nvfun=lp);
700			}
701			if(mode==NV_FIRST || mode==0)
702				return(fp);
703			np->nvfun = lp->next;
704			if(mode==NV_POP)
705				return(fp);
706			if(mode==NV_LAST && (lp->next==0 || lp->next->disc==0))
707				return(fp);
708		}
709		/* see if <fp> is on the list already */
710		lpp = &np->nvfun;
711		if(lp)
712		{
713			while(lp->next && lp->next->disc)
714			{
715				if(lp->next==fp)
716				{
717					if(mode==NV_LAST && fp->next==0)
718						return(fp);
719					if(mode==NV_CLONE)
720					{
721						fp = nv_clone_disc(fp,0);
722						lp->next = fp;
723						return(fp);
724					}
725					lp->next = fp->next;
726					if(mode==NV_POP)
727						return(fp);
728					if(mode!=NV_LAST)
729						break;
730				}
731				lp = lp->next;
732			}
733			if(mode==NV_LAST && lp->disc)
734				lpp = &lp->next;
735		}
736		if(mode==NV_POP)
737			return(0);
738		/* push */
739		nv_offattr(np,NV_NODISC);
740		if(mode==NV_LAST)
741		{
742			if(lp && !lp->disc)
743				fp->next = lp;
744			else
745				fp->next = 0;
746		}
747		else
748		{
749			if((fp->nofree&1) && *lpp)
750				fp = nv_clone_disc(fp,0);
751			fp->next = *lpp;
752		}
753		*lpp = fp;
754	}
755	else
756	{
757		if(mode==NV_FIRST)
758			return(np->nvfun);
759		else if(mode==NV_LAST)
760			for(lp=np->nvfun; lp; fp=lp,lp=lp->next);
761		else if(fp = np->nvfun)
762			np->nvfun = fp->next;
763	}
764	return(fp);
765}
766
767/*
768 * returns discipline pointer if discipline with specified functions
769 * is on the discipline stack
770 */
771Namfun_t *nv_hasdisc(Namval_t *np, const Namdisc_t *dp)
772{
773	register Namfun_t *fp;
774	for(fp=np->nvfun; fp; fp = fp->next)
775	{
776		if(fp->disc== dp)
777			return(fp);
778	}
779	return(0);
780}
781
782struct notify
783{
784	Namfun_t	hdr;
785	char		**ptr;
786};
787
788static void put_notify(Namval_t* np,const char *val,int flags,Namfun_t *fp)
789{
790	struct notify *pp = (struct notify*)fp;
791	nv_putv(np,val,flags,fp);
792	nv_stack(np,fp);
793	nv_stack(np,(Namfun_t*)0);
794	*pp->ptr = 0;
795	if(!(fp->nofree&1))
796		free((void*)fp);
797}
798
799static const Namdisc_t notify_disc  = {  0, put_notify };
800
801int nv_unsetnotify(Namval_t *np, char **addr)
802{
803	register Namfun_t *fp;
804	for(fp=np->nvfun;fp;fp=fp->next)
805	{
806		if(fp->disc->putval==put_notify && ((struct notify*)fp)->ptr==addr)
807		{
808			nv_stack(np,fp);
809			nv_stack(np,(Namfun_t*)0);
810			if(!(fp->nofree&1))
811				free((void*)fp);
812			return(1);
813		}
814	}
815	return(0);
816}
817
818int nv_setnotify(Namval_t *np, char **addr)
819{
820	struct notify *pp = newof(0,struct notify, 1,0);
821	if(!pp)
822		return(0);
823	pp->ptr = addr;
824	pp->hdr.disc = &notify_disc;
825	nv_stack(np,&pp->hdr);
826	return(1);
827}
828
829static void *newnode(const char *name)
830{
831	register int s;
832	register Namval_t *np = newof(0,Namval_t,1,s=strlen(name)+1);
833	if(np)
834	{
835		np->nvname = (char*)np+sizeof(Namval_t);
836		memcpy(np->nvname,name,s);
837	}
838	return((void*)np);
839}
840
841/*
842 * clone a numeric value
843 */
844static void *num_clone(register Namval_t *np, void *val)
845{
846	register int size;
847	void *nval;
848	if(!val)
849		return(0);
850	if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
851	{
852		if(nv_isattr(np,NV_LONG))
853			size = sizeof(Sfdouble_t);
854		else if(nv_isattr(np,NV_SHORT))
855			size = sizeof(float);
856		else
857			size = sizeof(double);
858	}
859	else
860	{
861		if(nv_isattr(np,NV_LONG))
862			size = sizeof(Sflong_t);
863		else if(nv_isattr(np,NV_SHORT))
864		{
865			if(nv_isattr(np,NV_INT16P)==NV_INT16P)
866				size = sizeof(short);
867			else
868				return((void*)np->nvalue.ip);
869		}
870		else
871			size = sizeof(int32_t);
872	}
873	if(!(nval = malloc(size)))
874		return(0);
875	memcpy(nval,val,size);
876	return(nval);
877}
878
879void clone_all_disc( Namval_t *np, Namval_t *mp, int flags)
880{
881	register Namfun_t *fp, **mfp = &mp->nvfun, *nfp, *fpnext;
882	for(fp=np->nvfun; fp;fp=fpnext)
883	{
884		fpnext = fp->next;
885		if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef)
886			return;
887		if((fp->nofree&2) && (flags&NV_NODISC))
888			nfp = 0;
889		if(fp->disc && fp->disc->clonef)
890			nfp = (*fp->disc->clonef)(np,mp,flags,fp);
891		else	if(flags&NV_MOVE)
892			nfp = fp;
893		else
894			nfp = nv_clone_disc(fp,flags);
895		if(!nfp)
896			continue;
897		nfp->next = 0;
898		*mfp = nfp;
899		mfp = &nfp->next;
900	}
901}
902
903/*
904 * clone <mp> from <np> flags can be one of the following
905 * NV_APPEND - append <np> onto <mp>
906 * NV_MOVE - move <np> to <mp>
907 * NV_NOFREE - mark the new node as nofree
908 * NV_NODISC - discplines with funs non-zero will not be copied
909 * NV_COMVAR - cloning a compound variable
910 */
911int nv_clone(Namval_t *np, Namval_t *mp, int flags)
912{
913	Namfun_t	*fp, *fpnext;
914	const char	*val = mp->nvalue.cp;
915	unsigned short	flag = mp->nvflag;
916	unsigned short	size = mp->nvsize;
917	for(fp=mp->nvfun; fp; fp=fpnext)
918	{
919		fpnext = fp->next;
920		if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef)
921			break;
922		if(!(fp->nofree&1))
923			free((void*)fp);
924	}
925	mp->nvfun = fp;
926	if(fp=np->nvfun)
927	{
928		if(nv_isattr(mp,NV_EXPORT|NV_MINIMAL) == (NV_EXPORT|NV_MINIMAL))
929		{
930			mp->nvenv = 0;
931			nv_offattr(mp,NV_MINIMAL);
932		}
933		if(!(flags&NV_COMVAR) && !nv_isattr(np,NV_MINIMAL) && np->nvenv && !(nv_isattr(mp,NV_MINIMAL)))
934			mp->nvenv = np->nvenv;
935		mp->nvflag &= NV_MINIMAL;
936	        mp->nvflag |= np->nvflag&~(NV_ARRAY|NV_MINIMAL|NV_NOFREE);
937		flag = mp->nvflag;
938		clone_all_disc(np, mp, flags);
939	}
940	if(flags&NV_APPEND)
941		return(1);
942	if(mp->nvsize == size)
943	        nv_setsize(mp,nv_size(np));
944	if(mp->nvflag == flag)
945	        mp->nvflag = (np->nvflag&~(NV_MINIMAL))|(mp->nvflag&NV_MINIMAL);
946		if(nv_isattr(np,NV_EXPORT))
947			mp->nvflag |= (np->nvflag&NV_MINIMAL);
948	if(mp->nvalue.cp==val && !nv_isattr(np,NV_INTEGER))
949	{
950		if(np->nvalue.cp && np->nvalue.cp!=Empty && (flags&NV_COMVAR) && !(flags&NV_MOVE))
951		{
952			if(size)
953				mp->nvalue.cp = (char*)memdup(np->nvalue.cp,size);
954			else
955			        mp->nvalue.cp = strdup(np->nvalue.cp);
956			nv_offattr(mp,NV_NOFREE);
957		}
958		else if((np->nvfun || !nv_isattr(np,NV_ARRAY)) && !(mp->nvalue.cp = np->nvalue.cp))
959			nv_offattr(mp,NV_NOFREE);
960	}
961	if(flags&NV_MOVE)
962	{
963		if(nv_isattr(np,NV_INTEGER))
964			mp->nvalue.ip = np->nvalue.ip;
965		np->nvfun = 0;
966		np->nvalue.cp = 0;
967		if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(mp,NV_EXPORT))
968		{
969			mp->nvenv = np->nvenv;
970			if(nv_isattr(np,NV_MINIMAL))
971			{
972				np->nvenv = 0;
973				np->nvflag = NV_EXPORT;
974			}
975			else
976				np->nvflag = 0;
977		}
978		else
979			np->nvflag &= NV_MINIMAL;
980	        nv_setsize(np,0);
981		return(1);
982	}
983	else if((flags&NV_ARRAY) && !nv_isattr(np,NV_MINIMAL))
984		mp->nvenv = np->nvenv;
985	if(nv_isattr(np,NV_INTEGER) && mp->nvalue.ip!=np->nvalue.ip && np->nvalue.cp!=Empty)
986	{
987		mp->nvalue.ip = (int*)num_clone(np,(void*)np->nvalue.ip);
988		nv_offattr(mp,NV_NOFREE);
989	}
990	else if((flags&NV_NOFREE) && !nv_arrayptr(np))
991	        nv_onattr(np,NV_NOFREE);
992	return(1);
993}
994
995/*
996 *  The following discipline is for copy-on-write semantics
997 */
998static char* clone_getv(Namval_t *np, Namfun_t *handle)
999{
1000	return(np->nvalue.np?nv_getval(np->nvalue.np):0);
1001}
1002
1003static Sfdouble_t clone_getn(Namval_t *np, Namfun_t *handle)
1004{
1005	return(np->nvalue.np?nv_getnum(np->nvalue.np):0);
1006}
1007
1008static void clone_putv(Namval_t *np,const char* val,int flags,Namfun_t *handle)
1009{
1010	Namfun_t *dp = nv_stack(np,(Namfun_t*)0);
1011	Namval_t *mp = np->nvalue.np;
1012	if(!sh.subshell)
1013		free((void*)dp);
1014	if(val)
1015		nv_clone(mp,np,NV_NOFREE);
1016	np->nvalue.cp = 0;
1017	nv_putval(np,val,flags);
1018}
1019
1020static const Namdisc_t clone_disc =
1021{
1022	0,
1023	clone_putv,
1024	clone_getv,
1025	clone_getn
1026};
1027
1028Namval_t *nv_mkclone(Namval_t *mp)
1029{
1030	Namval_t *np;
1031	Namfun_t *dp;
1032	np = newof(0,Namval_t,1,0);
1033	np->nvflag = mp->nvflag;
1034	np->nvsize = mp->nvsize;
1035	np->nvname = mp->nvname;
1036	np->nvalue.np = mp;
1037	np->nvflag = mp->nvflag;
1038	dp = newof(0,Namfun_t,1,0);
1039	dp->disc = &clone_disc;
1040	nv_stack(np,dp);
1041	dtinsert(nv_dict(sh.namespace),np);
1042	return(np);
1043}
1044
1045Namval_t *nv_search(const char *name, Dt_t *root, int mode)
1046{
1047	register Namval_t *np;
1048	register Dt_t *dp = 0;
1049	if(mode&HASH_NOSCOPE)
1050		dp = dtview(root,0);
1051	if(mode&HASH_BUCKET)
1052	{
1053		Namval_t *mp = (void*)name;
1054		if(!(np = dtsearch(root,mp)) && (mode&NV_ADD))
1055			name = nv_name(mp);
1056	}
1057	else
1058	{
1059		if(*name=='.' && root==sh.var_tree && !dp)
1060			root = sh.var_base;
1061		np = dtmatch(root,(void*)name);
1062	}
1063#if SHOPT_COSHELL
1064	if(sh.inpool)
1065		mode |= HASH_NOSCOPE;
1066#endif /* SHOPT_COSHELL */
1067	if(!np && (mode&NV_ADD))
1068	{
1069		if(sh.namespace && !(mode&HASH_NOSCOPE) && root==sh.var_tree)
1070			root = nv_dict(sh.namespace);
1071		else if(!dp && !(mode&HASH_NOSCOPE))
1072		{
1073			register Dt_t *next;
1074			while(next=dtvnext(root))
1075				root = next;
1076		}
1077		np = (Namval_t*)dtinsert(root,newnode(name));
1078	}
1079	if(dp)
1080		dtview(root,dp);
1081	return(np);
1082}
1083
1084/*
1085 * finds function or builtin for given name and the discipline variable
1086 * if var!=0 the variable pointer is returned and the built-in name
1087 *    is put onto the stack at the current offset.
1088 * otherwise, a pointer to the builtin (variable or type) is returned
1089 * and var contains the poiner to the variable
1090 * if last==0 and first component of name is a reference, nv_bfsearch()
1091	will return 0.
1092 */
1093Namval_t *nv_bfsearch(const char *name, Dt_t *root, Namval_t **var, char **last)
1094{
1095	Shell_t		*shp = sh_getinterp();
1096	int		c,offset = staktell();
1097	register char	*sp, *cp=0;
1098	Namval_t	*np, *nq;
1099	char		*dname=0;
1100	if(var)
1101		*var = 0;
1102	/* check for . in the name before = */
1103	for(sp=(char*)name+1; *sp; sp++)
1104	{
1105		if(*sp=='=')
1106			return(0);
1107		if(*sp=='[')
1108		{
1109			while(*sp=='[')
1110			{
1111				sp = nv_endsubscript((Namval_t*)0,(char*)sp,0);
1112				if(sp[-1]!=']')
1113					return(0);
1114			}
1115			if(*sp==0)
1116				break;
1117			if(*sp!='.')
1118				return(0);
1119			cp = sp;
1120		}
1121		else if(*sp=='.')
1122			cp = sp;
1123	}
1124	if(!cp)
1125		return(var?nv_search(name,root,0):0);
1126	stakputs(name);
1127	stakputc(0);
1128	dname = cp+1;
1129	cp = stakptr(offset) + (cp-name);
1130	if(last)
1131		*last = cp;
1132	c = *cp;
1133	*cp = 0;
1134	nq=nv_open(stakptr(offset),0,NV_VARNAME|NV_NOASSIGN|NV_NOADD|NV_NOFAIL);
1135	*cp = c;
1136	if(!nq)
1137	{
1138		np = 0;
1139		goto done;
1140	}
1141	if(!var)
1142	{
1143		np = nq;
1144		goto done;
1145	}
1146	*var = nq;
1147	if(c=='[')
1148		nv_endsubscript(nq, cp,NV_NOADD);
1149	stakseek(offset);
1150#if SHOPT_NAMESPACE
1151	if(nv_istable(nq))
1152	{
1153		Namval_t *nsp = shp->namespace;
1154		if(last==0)
1155			return(nv_search(name,root,0));
1156		shp->namespace = 0;
1157		stakputs(nv_name(nq));
1158		shp->namespace = nsp;
1159		stakputs(dname-1);
1160		stakputc(0);
1161		np = nv_search(stakptr(offset),root,0);
1162		stakseek(offset);
1163		return(np);
1164	}
1165#endif /* SHOPT_NAMESPACE */
1166	while(nv_isarray(nq) && !nv_isattr(nq,NV_MINIMAL|NV_EXPORT) && nq->nvenv && nv_isarray((Namval_t*)nq->nvenv))
1167		nq = (Namval_t*)nq->nvenv;
1168	return((Namval_t*)nv_setdisc(nq,dname,nq,(Namfun_t*)nq));
1169done:
1170	stakseek(offset);
1171	return(np);
1172}
1173
1174/*
1175 * add or replace built-in version of command corresponding to <path>
1176 * The <bltin> argument is a pointer to the built-in
1177 * if <extra>==1, the built-in will be deleted
1178 * Special builtins cannot be added or deleted return failure
1179 * The return value for adding builtins is a pointer to the node or NULL on
1180 *   failure.  For delete NULL means success and the node that cannot be
1181 *   deleted is returned on failure.
1182 */
1183Namval_t *sh_addbuiltin(const char *path, Shbltin_f bltin, void *extra)
1184{
1185	register const char	*name;
1186	char			*cp;
1187	register Namval_t	*np, *nq=0;
1188	int			offset=staktell();
1189	if(extra==(void*)1)
1190		name = path;
1191	else if((name = path_basename(path))==path && bltin!=(Shbltin_f)SYSTYPESET->nvalue.bfp && (nq=nv_bfsearch(name,sh.bltin_tree,(Namval_t**)0,&cp)))
1192		path = name = stakptr(offset);
1193	else if(sh.bltin_dir && extra!=(void*)1)
1194	{
1195		stakputs(sh.bltin_dir);
1196		stakputc('/');
1197		stakputs(name);
1198		path = stakptr(offset);
1199	}
1200	if(np = nv_search(name,sh.bltin_tree,0))
1201	{
1202		/* exists without a path */
1203		stakseek(offset);
1204		if(extra == (void*)1)
1205		{
1206			if(np->nvfun && !nv_isattr(np,NV_NOFREE))
1207				free((void*)np->nvfun);
1208			dtdelete(sh.bltin_tree,np);
1209			return(0);
1210		}
1211		if(!bltin)
1212			return(np);
1213	}
1214	else for(np=(Namval_t*)dtfirst(sh.bltin_tree);np;np=(Namval_t*)dtnext(sh.bltin_tree,np))
1215	{
1216		if(strcmp(name,path_basename(nv_name(np))))
1217			continue;
1218		/* exists probably with different path so delete it */
1219		if(strcmp(path,nv_name(np)))
1220		{
1221			if(nv_isattr(np,BLT_SPC))
1222				return(np);
1223			if(!bltin)
1224				bltin = (Shbltin_f)np->nvalue.bfp;
1225			if(np->nvenv)
1226				dtdelete(sh.bltin_tree,np);
1227			if(extra == (void*)1)
1228				return(0);
1229			np = 0;
1230		}
1231		break;
1232	}
1233	if(!np && !(np = nv_search(path,sh.bltin_tree,bltin?NV_ADD:0)))
1234		return(0);
1235	stakseek(offset);
1236	if(nv_isattr(np,BLT_SPC))
1237	{
1238		if(extra)
1239			np->nvfun = (Namfun_t*)extra;
1240		return(np);
1241	}
1242	np->nvenv = 0;
1243	np->nvfun = 0;
1244	if(bltin)
1245	{
1246		np->nvalue.bfp = (Nambfp_f)bltin;
1247		nv_onattr(np,NV_BLTIN|NV_NOFREE);
1248		np->nvfun = (Namfun_t*)extra;
1249	}
1250	if(nq)
1251	{
1252		cp=nv_setdisc(nq,cp+1,np,(Namfun_t*)nq);
1253		nv_close(nq);
1254		if(!cp)
1255			errormsg(SH_DICT,ERROR_exit(1),e_baddisc,name);
1256	}
1257	if(extra == (void*)1)
1258		return(0);
1259	return(np);
1260}
1261
1262#undef nv_stack
1263extern Namfun_t *nv_stack(register Namval_t *np, register Namfun_t* fp)
1264{
1265	return(nv_disc(np,fp,0));
1266}
1267
1268struct table
1269{
1270	Namfun_t	fun;
1271	Namval_t	*parent;
1272	Shell_t		*shp;
1273	Dt_t		*dict;
1274};
1275
1276static Namval_t *next_table(register Namval_t* np, Dt_t *root,Namfun_t *fp)
1277{
1278	struct table *tp = (struct table *)fp;
1279	if(root)
1280		return((Namval_t*)dtnext(root,np));
1281	else
1282		return((Namval_t*)dtfirst(tp->dict));
1283}
1284
1285static Namval_t *create_table(Namval_t *np,const char *name,int flags,Namfun_t *fp)
1286{
1287	struct table *tp = (struct table *)fp;
1288	tp->shp->last_table = np;
1289	return(nv_create(name, tp->dict, flags, fp));
1290}
1291
1292static Namfun_t *clone_table(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
1293{
1294	struct table	*tp = (struct table*)fp;
1295	struct table	*ntp = (struct table*)nv_clone_disc(fp,0);
1296	Dt_t		*oroot=tp->dict,*nroot=dtopen(&_Nvdisc,Dtoset);
1297	if(!nroot)
1298		return(0);
1299	memcpy((void*)ntp,(void*)fp,sizeof(struct table));
1300	ntp->dict = nroot;
1301	ntp->parent = nv_lastdict();
1302	for(np=(Namval_t*)dtfirst(oroot);np;np=(Namval_t*)dtnext(oroot,np))
1303	{
1304		mp = (Namval_t*)dtinsert(nroot,newnode(np->nvname));
1305		nv_clone(np,mp,flags);
1306	}
1307	return(&ntp->fun);
1308}
1309
1310struct adata
1311{
1312	Shell_t		*sh;
1313	Namval_t	*tp;
1314	char		*mapname;
1315	char		**argnam;
1316	int		attsize;
1317	char		*attval;
1318};
1319
1320static void delete_fun(Namval_t *np, void *data)
1321{
1322	Shell_t *shp = ((struct adata*)data)->sh;
1323	nv_delete(np,shp->fun_tree,NV_NOFREE);
1324}
1325
1326static void put_table(register Namval_t* np, const char* val, int flags, Namfun_t* fp)
1327{
1328	register Dt_t		*root = ((struct table*)fp)->dict;
1329	register Namval_t	*nq, *mp;
1330	Namarr_t		*ap;
1331	struct adata		data;
1332	if(val)
1333	{
1334		nv_putv(np,val,flags,fp);
1335		return;
1336	}
1337	if(nv_isarray(np) && (ap=nv_arrayptr(np)) && array_elem(ap))
1338		return;
1339	memset(&data,0,sizeof(data));
1340	data.mapname = nv_name(np);
1341	data.sh = ((struct table*)fp)->shp;
1342	nv_scan(data.sh->fun_tree,delete_fun,(void*)&data,NV_FUNCTION,NV_FUNCTION|NV_NOSCOPE);
1343	dtview(root,0);
1344	for(mp=(Namval_t*)dtfirst(root);mp;mp=nq)
1345	{
1346		_nv_unset(mp,flags);
1347		nq = (Namval_t*)dtnext(root,mp);
1348		dtdelete(root,mp);
1349		free((void*)mp);
1350	}
1351	dtclose(root);
1352	if(!(fp->nofree&1))
1353		free((void*)fp);
1354	np->nvfun = 0;
1355}
1356
1357/*
1358 * return space separated list of names of variables in given tree
1359 */
1360static char *get_table(Namval_t *np, Namfun_t *fp)
1361{
1362	register Dt_t *root = ((struct table*)fp)->dict;
1363	static Sfio_t *out;
1364	register int first=1;
1365	register Dt_t *base = dtview(root,0);
1366        if(out)
1367                sfseek(out,(Sfoff_t)0,SEEK_SET);
1368        else
1369                out =  sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
1370	for(np=(Namval_t*)dtfirst(root);np;np=(Namval_t*)dtnext(root,np))
1371	{
1372                if(!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE))
1373		{
1374			if(!first)
1375				sfputc(out,' ');
1376			else
1377				first = 0;
1378			sfputr(out,np->nvname,-1);
1379		}
1380	}
1381	sfputc(out,0);
1382	if(base)
1383		dtview(root,base);
1384	return((char*)out->_data);
1385}
1386
1387static const Namdisc_t table_disc =
1388{
1389        sizeof(struct table),
1390        put_table,
1391        get_table,
1392        0,
1393        0,
1394        create_table,
1395        clone_table,
1396        0,
1397        next_table,
1398};
1399
1400Namval_t *nv_parent(Namval_t *np)
1401{
1402	struct table *tp = (struct table *)nv_hasdisc(np,&table_disc);
1403	if(tp)
1404		return(tp->parent);
1405	return(0);
1406}
1407
1408Dt_t *nv_dict(Namval_t* np)
1409{
1410	Shell_t 	*shp=sh_getinterp();
1411	struct table *tp = (struct table*)nv_hasdisc(np,&table_disc);
1412	if(tp)
1413		return(tp->dict);
1414	np = shp->last_table;
1415	while(np)
1416	{
1417		if(tp = (struct table*)nv_hasdisc(np,&table_disc))
1418			return(tp->dict);
1419#if 0
1420		np = nv_create(np,(const char*)0, NV_FIRST, (Namfun_t*)0);
1421#else
1422		break;
1423#endif
1424	}
1425	return(shp->var_tree);
1426}
1427
1428int nv_istable(Namval_t *np)
1429{
1430	return(nv_hasdisc(np,&table_disc)!=0);
1431}
1432
1433/*
1434 * create a mountable name-value pair tree
1435 */
1436Namval_t *nv_mount(Namval_t *np, const char *name, Dt_t *dict)
1437{
1438	Namval_t *mp, *pp;
1439	struct table *tp;
1440	if(nv_hasdisc(np,&table_disc))
1441		pp = np;
1442	else
1443		pp = nv_lastdict();
1444	if(!(tp = newof((struct table*)0, struct table,1,0)))
1445		return(0);
1446	if(name)
1447	{
1448		Namfun_t *fp = pp->nvfun;
1449		mp = (*fp->disc->createf)(pp,name,0,fp);
1450	}
1451	else
1452		mp = np;
1453	nv_offattr(mp,NV_TABLE);
1454	if(!nv_isnull(mp))
1455		_nv_unset(mp,NV_RDONLY);
1456	tp->shp = sh_getinterp();
1457	tp->dict = dict;
1458	tp->parent = pp;
1459	tp->fun.disc = &table_disc;
1460	nv_disc(mp, &tp->fun, NV_FIRST);
1461	return(mp);
1462}
1463
1464const Namdisc_t *nv_discfun(int which)
1465{
1466	switch(which)
1467	{
1468	    case NV_DCADD:
1469		return(&Nv_bdisc);
1470	    case NV_DCRESTRICT:
1471		return(&RESTRICTED_disc);
1472	}
1473	return(0);
1474}
1475
1476int nv_hasget(Namval_t *np)
1477{
1478	register Namfun_t	*fp;
1479	for(fp=np->nvfun; fp; fp=fp->next)
1480	{
1481		if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
1482			continue;
1483		return(1);
1484	}
1485	return(0);
1486}
1487
1488#if SHOPT_NAMESPACE
1489Namval_t *sh_fsearch(Shell_t *shp, const char *fname, int add)
1490{
1491	Stk_t	*stkp = shp->stk;
1492	int	offset = stktell(stkp);
1493	sfputr(stkp,nv_name(shp->namespace),'.');
1494	sfputr(stkp,fname,0);
1495	fname = stkptr(stkp,offset);
1496	return(nv_search(fname,sh_subfuntree(add&NV_ADD),add));
1497}
1498#endif /* SHOPT_NAMESPACE */
1499