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