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