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