xref: /titanic_51/usr/src/lib/libshell/common/sh/nvdisc.c (revision 19449258028e6813f0b7a606b554b2fa37a390ec)
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 
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	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 
160 struct	vardisc
161 {
162 	Namfun_t	fun;
163 	Namval_t	*disc[5];
164 };
165 
166 struct blocked
167 {
168 	struct blocked	*next;
169 	Namval_t	*np;
170 	int		flags;
171 	void		*sub;
172 	int		isub;
173 };
174 
175 static 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  */
184 static 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 
208 static 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  */
218 static 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  */
238 static 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 	}
346 done:
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  */
355 static 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 
409 static char*	lookups(Namval_t *np, Namfun_t *handle)
410 {
411 	return(lookup(np,LOOKUPS,(Sfdouble_t*)0,handle));
412 }
413 
414 static 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  */
429 char *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  */
539 static 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 
581 static 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 
609 static const Namdisc_t Nv_bdisc	= {   0, putdisc, 0, 0, setdisc };
610 
611 Namfun_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 
627 int 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  */
660 Namfun_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  */
742 Namfun_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 
753 struct notify
754 {
755 	Namfun_t	hdr;
756 	char		**ptr;
757 };
758 
759 static 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 
770 static const Namdisc_t notify_disc  = {  0, put_notify };
771 
772 int 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 
789 int 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 
800 static 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  */
816 static 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 
851 void 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  */
883 int 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  */
963 static char* clone_getv(Namval_t *np, Namfun_t *handle)
964 {
965 	return(np->nvalue.np?nv_getval(np->nvalue.np):0);
966 }
967 
968 static Sfdouble_t clone_getn(Namval_t *np, Namfun_t *handle)
969 {
970 	return(np->nvalue.np?nv_getnum(np->nvalue.np):0);
971 }
972 
973 static 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 
985 static const Namdisc_t clone_disc =
986 {
987 	0,
988 	clone_putv,
989 	clone_getv,
990 	clone_getn
991 };
992 
993 Namval_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 
1011 Namval_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  */
1055 Namval_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));
1118 done:
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  */
1132 Namval_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
1201 extern Namfun_t *nv_stack(register Namval_t *np, register Namfun_t* fp)
1202 {
1203 	return(nv_disc(np,fp,0));
1204 }
1205 
1206 struct table
1207 {
1208 	Namfun_t	fun;
1209 	Namval_t	*parent;
1210 	Shell_t		*shp;
1211 	Dt_t		*dict;
1212 };
1213 
1214 static 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 
1223 static 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 
1230 static 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 
1248 static 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  */
1273 static 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 
1300 static 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 
1313 Namval_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 
1321 Dt_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  */
1343 Namval_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 
1374 const 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 
1386 int 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