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