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