Home | History | Annotate | Download | only in sh
      1 /***********************************************************************
      2 *                                                                      *
      3 *               This software is part of the ast package               *
      4 *          Copyright (c) 1982-2009 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 (at) 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 	if(vp && !vp->fun.disc)
    435 		vp = 0;
    436 	if(np == (Namval_t*)fp)
    437 	{
    438 		register const char *name;
    439 		register int getname=0;
    440 		/* top level call, check for get/set */
    441 		if(!event)
    442 		{
    443 			if(!action)
    444 				return((char*)nv_discnames[0]);
    445 			getname=1;
    446 			event = (char*)action;
    447 		}
    448 		for(type=0; name=nv_discnames[type]; type++)
    449 		{
    450 			if(strcmp(event,name)==0)
    451 				break;
    452 		}
    453 		if(getname)
    454 		{
    455 			event = 0;
    456 			if(name && !(name = nv_discnames[++type]))
    457 				action = 0;
    458 		}
    459 		if(!name)
    460 		{
    461 			for(fp=(Namfun_t*)vp; fp; fp=fp->next)
    462 			{
    463 				if(fp->disc && fp->disc->setdisc)
    464 					return((*fp->disc->setdisc)(np,event,action,fp));
    465 			}
    466 		}
    467 		else if(getname)
    468 			return((char*)name);
    469 	}
    470 	if(!fp)
    471 		return(NIL(char*));
    472 	if(np != (Namval_t*)fp)
    473 	{
    474 		/* not the top level */
    475 		while(fp = fp->next)
    476 		{
    477 			if(fp->disc && fp->disc->setdisc)
    478 				return((*fp->disc->setdisc)(np,event,action,fp));
    479 		}
    480 		return(NIL(char*));
    481 	}
    482 	/* Handle GET/SET/APPEND/UNSET disc */
    483 	if(vp && vp->fun.disc->putval!=assign)
    484 		vp = 0;
    485 	if(!vp)
    486 	{
    487 		Namdisc_t	*dp;
    488 		if(action==np)
    489 			return((char*)action);
    490 		if(!(vp = newof(NIL(struct vardisc*),struct vardisc,1,sizeof(Namdisc_t))))
    491 			return(0);
    492 		dp = (Namdisc_t*)(vp+1);
    493 		vp->fun.disc = dp;
    494 		memset(dp,0,sizeof(*dp));
    495 		dp->dsize = sizeof(struct vardisc);
    496 		dp->putval = assign;
    497 		nv_stack(np, (Namfun_t*)vp);
    498 	}
    499 	if(action==np)
    500 	{
    501 		action = vp->disc[type];
    502 		empty = 0;
    503 	}
    504 	else if(action)
    505 	{
    506 		Namdisc_t *dp = (Namdisc_t*)vp->fun.disc;
    507 		if(type==LOOKUPS)
    508 			dp->getval = lookups;
    509 		else if(type==LOOKUPN)
    510 			dp->getnum = lookupn;
    511 		vp->disc[type] = action;
    512 	}
    513 	else
    514 	{
    515 		struct blocked *bp;
    516 		action = vp->disc[type];
    517 		vp->disc[type] = 0;
    518 		if(!(bp=block_info(np,(struct blocked*)0)) || !isblocked(bp,UNASSIGN))
    519 			chktfree(np,vp);
    520 	}
    521 	return(action?(char*)action:empty);
    522 }
    523 
    524 /*
    525  * Set disc on given <event> to <action>
    526  * If action==np, the current disc is returned
    527  * A null return value indicates that no <event> is known for <np>
    528  * If <event> is NULL, then return the event name after <action>
    529  * If <event> is NULL, and <action> is NULL, return the first event
    530  */
    531 static char *setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp)
    532 {
    533 	register Nambfun_t *vp = (Nambfun_t*)fp;
    534 	register int type,getname=0;
    535 	register const char *name;
    536 	const char **discnames = vp->bnames;
    537 	/* top level call, check for discipline match */
    538 	if(!event)
    539 	{
    540 		if(!action)
    541 			return((char*)discnames[0]);
    542 		getname=1;
    543 		event = (char*)action;
    544 	}
    545 	for(type=0; name=discnames[type]; type++)
    546 	{
    547 		if(strcmp(event,name)==0)
    548 			break;
    549 	}
    550 	if(getname)
    551 	{
    552 		event = 0;
    553 		if(name && !(name = discnames[++type]))
    554 			action = 0;
    555 	}
    556 	if(!name)
    557 		return(nv_setdisc(np,event,action,fp));
    558 	else if(getname)
    559 		return((char*)name);
    560 	/* Handle the disciplines */
    561 	if(action==np)
    562 		action = vp->bltins[type];
    563 	else if(action)
    564 		vp->bltins[type] = action;
    565 	else
    566 	{
    567 		action = vp->bltins[type];
    568 		vp->bltins[type] = 0;
    569 	}
    570 	return(action?(char*)action:"");
    571 }
    572 
    573 static void putdisc(Namval_t* np, const char* val, int flag, Namfun_t* fp)
    574 {
    575 	nv_putv(np,val,flag,fp);
    576 	if(!val && !(flag&NV_NOFREE))
    577 	{
    578 		register Nambfun_t *vp = (Nambfun_t*)fp;
    579 		register int i;
    580 		for(i=0; vp->bnames[i]; i++)
    581 		{
    582 			register Namval_t *mp;
    583 			if((mp=vp->bltins[i]) && !nv_isattr(mp,NV_NOFREE))
    584 			{
    585 				if(is_abuiltin(mp))
    586 				{
    587 					if(mp->nvfun && !nv_isattr(mp,NV_NOFREE))
    588 						free((void*)mp->nvfun);
    589 					dtdelete(sh.bltin_tree,mp);
    590 					free((void*)mp);
    591 				}
    592 			}
    593 		}
    594 		nv_disc(np,fp,NV_POP);
    595 		if(!(fp->nofree&1))
    596 			free((void*)fp);
    597 
    598 	}
    599 }
    600 
    601 static const Namdisc_t Nv_bdisc	= {   0, putdisc, 0, 0, setdisc };
    602 
    603 Namfun_t *nv_clone_disc(register Namfun_t *fp, int flags)
    604 {
    605 	register Namfun_t	*nfp;
    606 	register int		size;
    607 	if(!fp->disc && !fp->next && (fp->nofree&1))
    608 		return(fp);
    609 	if(!(size=fp->dsize) && (!fp->disc || !(size=fp->disc->dsize)))
    610 		size = sizeof(Namfun_t);
    611 	if(!(nfp=newof(NIL(Namfun_t*),Namfun_t,1,size-sizeof(Namfun_t))))
    612 		return(0);
    613 	memcpy(nfp,fp,size);
    614 	nfp->nofree &= ~1;
    615 	nfp->nofree |= (flags&NV_RDONLY)?1:0;
    616 	return(nfp);
    617 }
    618 
    619 int nv_adddisc(Namval_t *np, const char **names, Namval_t **funs)
    620 {
    621 	register Nambfun_t *vp;
    622 	register int n=0;
    623 	register const char **av=names;
    624 	if(av)
    625 	{
    626 		while(*av++)
    627 			n++;
    628 	}
    629 	if(!(vp = newof(NIL(Nambfun_t*),Nambfun_t,1,n*sizeof(Namval_t*))))
    630 		return(0);
    631 	vp->fun.dsize = sizeof(Nambfun_t)+n*sizeof(Namval_t*);
    632 	vp->fun.nofree |= 2;
    633 	vp->num = n;
    634 	if(funs)
    635 		memcpy((void*)vp->bltins, (void*)funs,n*sizeof(Namval_t*));
    636 	else while(n>=0)
    637 		vp->bltins[n--] = 0;
    638 	vp->fun.disc = &Nv_bdisc;
    639 	vp->bnames = names;
    640 	nv_stack(np,&vp->fun);
    641 	return(1);
    642 }
    643 
    644 /*
    645  * push, pop, clne, or reorder disciplines onto node <np>
    646  * mode can be one of
    647  *    NV_FIRST:  Move or push <fp> to top of the stack or delete top
    648  *    NV_LAST:	 Move or push <fp> to bottom of stack or delete last
    649  *    NV_POP:	 Delete <fp> from top of the stack
    650  *    NV_CLONE:  Replace fp with a copy created my malloc() and return it
    651  */
    652 Namfun_t *nv_disc(register Namval_t *np, register Namfun_t* fp, int mode)
    653 {
    654 	Namfun_t *lp, **lpp;
    655 	if(nv_isref(np))
    656 		return(0);
    657 	if(mode==NV_CLONE && !fp)
    658 		return(0);
    659 	if(fp)
    660 	{
    661 		fp->subshell = sh.subshell;
    662 		if((lp=np->nvfun)==fp)
    663 		{
    664 			if(mode==NV_CLONE)
    665 			{
    666 				lp = nv_clone_disc(fp,0);
    667 				return(np->nvfun=lp);
    668 			}
    669 			if(mode==NV_FIRST || mode==0)
    670 				return(fp);
    671 			np->nvfun = lp->next;
    672 			if(mode==NV_POP)
    673 				return(fp);
    674 			if(mode==NV_LAST && (lp->next==0 || lp->next->disc==0))
    675 				return(fp);
    676 		}
    677 		/* see if <fp> is on the list already */
    678 		lpp = &np->nvfun;
    679 		if(lp)
    680 		{
    681 			while(lp->next && lp->next->disc)
    682 			{
    683 				if(lp->next==fp)
    684 				{
    685 					if(mode==NV_LAST && fp->next==0)
    686 						return(fp);
    687 					if(mode==NV_CLONE)
    688 					{
    689 						fp = nv_clone_disc(fp,0);
    690 						lp->next = fp;
    691 						return(fp);
    692 					}
    693 					lp->next = fp->next;
    694 					if(mode==NV_POP)
    695 						return(fp);
    696 					if(mode!=NV_LAST)
    697 						break;
    698 				}
    699 				lp = lp->next;
    700 			}
    701 			if(mode==NV_LAST)
    702 				lpp = &lp->next;
    703 		}
    704 		if(mode==NV_POP)
    705 			return(0);
    706 		/* push */
    707 		nv_offattr(np,NV_NODISC);
    708 		if(mode==NV_LAST)
    709 			fp->next = 0;
    710 		else
    711 		{
    712 			if((fp->nofree&1) && *lpp)
    713 				fp = nv_clone_disc(fp,0);
    714 			fp->next = *lpp;
    715 		}
    716 		*lpp = fp;
    717 	}
    718 	else
    719 	{
    720 		if(mode==NV_FIRST)
    721 			return(np->nvfun);
    722 		else if(mode==NV_LAST)
    723 			for(lp=np->nvfun; lp; fp=lp,lp=lp->next);
    724 		else if(fp = np->nvfun)
    725 			np->nvfun = fp->next;
    726 	}
    727 	return(fp);
    728 }
    729 
    730 /*
    731  * returns discipline pointer if discipline with specified functions
    732  * is on the discipline stack
    733  */
    734 Namfun_t *nv_hasdisc(Namval_t *np, const Namdisc_t *dp)
    735 {
    736 	register Namfun_t *fp;
    737 	for(fp=np->nvfun; fp; fp = fp->next)
    738 	{
    739 		if(fp->disc== dp)
    740 			return(fp);
    741 	}
    742 	return(0);
    743 }
    744 
    745 struct notify
    746 {
    747 	Namfun_t	hdr;
    748 	char		**ptr;
    749 };
    750 
    751 static void put_notify(Namval_t* np,const char *val,int flags,Namfun_t *fp)
    752 {
    753 	struct notify *pp = (struct notify*)fp;
    754 	nv_putv(np,val,flags,fp);
    755 	nv_stack(np,fp);
    756 	nv_stack(np,(Namfun_t*)0);
    757 	*pp->ptr = 0;
    758 	if(!(fp->nofree&1))
    759 		free((void*)fp);
    760 }
    761 
    762 static const Namdisc_t notify_disc  = {  0, put_notify };
    763 
    764 int nv_unsetnotify(Namval_t *np, char **addr)
    765 {
    766 	register Namfun_t *fp;
    767 	for(fp=np->nvfun;fp;fp=fp->next)
    768 	{
    769 		if(fp->disc->putval==put_notify && ((struct notify*)fp)->ptr==addr)
    770 		{
    771 			nv_stack(np,fp);
    772 			nv_stack(np,(Namfun_t*)0);
    773 			if(!(fp->nofree&1))
    774 				free((void*)fp);
    775 			return(1);
    776 		}
    777 	}
    778 	return(0);
    779 }
    780 
    781 int nv_setnotify(Namval_t *np, char **addr)
    782 {
    783 	struct notify *pp = newof(0,struct notify, 1,0);
    784 	if(!pp)
    785 		return(0);
    786 	pp->ptr = addr;
    787 	pp->hdr.disc = &notify_disc;
    788 	nv_stack(np,&pp->hdr);
    789 	return(1);
    790 }
    791 
    792 static void *newnode(const char *name)
    793 {
    794 	register int s;
    795 	register Namval_t *np = newof(0,Namval_t,1,s=strlen(name)+1);
    796 	if(np)
    797 	{
    798 		np->nvname = (char*)np+sizeof(Namval_t);
    799 		memcpy(np->nvname,name,s);
    800 	}
    801 	return((void*)np);
    802 }
    803 
    804 #if SHOPT_NAMESPACE
    805 /*
    806  * clone a numeric value
    807  */
    808 static void *num_clone(register Namval_t *np, void *val)
    809 {
    810 	register int size;
    811 	void *nval;
    812 	if(!val)
    813 		return(0);
    814 	if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
    815 	{
    816 		if(nv_isattr(np,NV_LONG))
    817 			size = sizeof(Sfdouble_t);
    818 		else if(nv_isattr(np,NV_SHORT))
    819 			size = sizeof(float);
    820 		else
    821 			size = sizeof(double);
    822 	}
    823 	else
    824 	{
    825 		if(nv_isattr(np,NV_LONG))
    826 			size = sizeof(Sflong_t);
    827 		else if(nv_isattr(np,NV_SHORT))
    828 		{
    829 			if(nv_isattr(np,NV_INT16P)==NV_INT16P)
    830 				size = sizeof(short);
    831 			else
    832 				return((void*)np->nvalue.ip);
    833 		}
    834 		else
    835 			size = sizeof(int32_t);
    836 	}
    837 	if(!(nval = malloc(size)))
    838 		return(0);
    839 	memcpy(nval,val,size);
    840 	return(nval);
    841 }
    842 
    843 void clone_all_disc( Namval_t *np, Namval_t *mp, int flags)
    844 {
    845 	register Namfun_t *fp, **mfp = &mp->nvfun, *nfp, *fpnext;
    846 	for(fp=np->nvfun; fp;fp=fpnext)
    847 	{
    848 		fpnext = fp->next;
    849 		if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef)
    850 			return;
    851 		if((fp->nofree&2) && (flags&NV_NODISC))
    852 			nfp = 0;
    853 		if(fp->disc && fp->disc->clonef)
    854 			nfp = (*fp->disc->clonef)(np,mp,flags,fp);
    855 		else	if(flags&NV_MOVE)
    856 			nfp = fp;
    857 		else
    858 			nfp = nv_clone_disc(fp,flags);
    859 		if(!nfp)
    860 			continue;
    861 		nfp->next = 0;
    862 		*mfp = nfp;
    863 		mfp = &nfp->next;
    864 	}
    865 }
    866 
    867 /*
    868  * clone <mp> from <np> flags can be one of the following
    869  * NV_APPEND - append <np> onto <mp>
    870  * NV_MOVE - move <np> to <mp>
    871  * NV_NOFREE - mark the new node as nofree
    872  * NV_NODISC - discplines with funs non-zero will not be copied
    873  * NV_COMVAR - cloning a compound variable
    874  */
    875 int nv_clone(Namval_t *np, Namval_t *mp, int flags)
    876 {
    877 	Namfun_t	*fp, *fpnext;
    878 	const char	*val = mp->nvalue.cp;
    879 	unsigned short	flag = mp->nvflag;
    880 	unsigned short	size = mp->nvsize;
    881 	for(fp=mp->nvfun; fp; fp=fpnext)
    882 	{
    883 		fpnext = fp->next;
    884 		if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef)
    885 			break;
    886 		if(!(fp->nofree&1))
    887 			free((void*)fp);
    888 	}
    889 	mp->nvfun = fp;
    890 	if(fp=np->nvfun)
    891 	{
    892 		if(nv_isattr(mp,NV_EXPORT|NV_MINIMAL) == (NV_EXPORT|NV_MINIMAL))
    893 		{
    894 			mp->nvenv = 0;
    895 			nv_offattr(mp,NV_MINIMAL);
    896 		}
    897 		if(!(flags&NV_COMVAR) && !nv_isattr(np,NV_MINIMAL) && np->nvenv && !(nv_isattr(mp,NV_MINIMAL)))
    898 			mp->nvenv = np->nvenv;
    899 		mp->nvflag &= NV_MINIMAL;
    900 	        mp->nvflag |= np->nvflag&~(NV_ARRAY|NV_MINIMAL|NV_NOFREE);
    901 		flag = mp->nvflag;
    902 		clone_all_disc(np, mp, flags);
    903 	}
    904 	if(flags&NV_APPEND)
    905 		return(1);
    906 	if(mp->nvsize == size)
    907 	        nv_setsize(mp,nv_size(np));
    908 	if(mp->nvflag == flag)
    909 	        mp->nvflag = (np->nvflag&~(NV_MINIMAL))|(mp->nvflag&NV_MINIMAL);
    910 	if(mp->nvalue.cp==val && !nv_isattr(np,NV_INTEGER))
    911 	{
    912 		if(np->nvalue.cp && np->nvalue.cp!=Empty && (flags&NV_COMVAR) && !(flags&NV_MOVE))
    913 		{
    914 			if(size)
    915 				mp->nvalue.cp = (char*)memdup(np->nvalue.cp,size);
    916 			else
    917 			        mp->nvalue.cp = strdup(np->nvalue.cp);
    918 			nv_offattr(mp,NV_NOFREE);
    919 		}
    920 		else if(!(mp->nvalue.cp = np->nvalue.cp))
    921 			nv_offattr(mp,NV_NOFREE);
    922 	}
    923 	if(flags&NV_MOVE)
    924 	{
    925 		if(nv_isattr(np,NV_INTEGER))
    926 			mp->nvalue.ip = np->nvalue.ip;
    927 		np->nvfun = 0;
    928 		np->nvalue.cp = 0;
    929 		if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(mp,NV_EXPORT))
    930 		        np->nvenv = 0;
    931 		np->nvflag &= NV_MINIMAL;
    932 	        nv_setsize(np,0);
    933 		return(1);
    934 	}
    935 	if(nv_isattr(np,NV_INTEGER) && mp->nvalue.ip!=np->nvalue.ip)
    936 	{
    937 		mp->nvalue.ip = (int*)num_clone(np,(void*)np->nvalue.ip);
    938 		nv_offattr(mp,NV_NOFREE);
    939 	}
    940 	else if(flags&NV_NOFREE)
    941 	        nv_onattr(np,NV_NOFREE);
    942 	return(1);
    943 }
    944 
    945 /*
    946  *  The following discipline is for copy-on-write semantics
    947  */
    948 static char* clone_getv(Namval_t *np, Namfun_t *handle)
    949 {
    950 	return(np->nvalue.np?nv_getval(np->nvalue.np):0);
    951 }
    952 
    953 static Sfdouble_t clone_getn(Namval_t *np, Namfun_t *handle)
    954 {
    955 	return(np->nvalue.np?nv_getnum(np->nvalue.np):0);
    956 }
    957 
    958 static void clone_putv(Namval_t *np,const char* val,int flags,Namfun_t *handle)
    959 {
    960 	Namfun_t *dp = nv_stack(np,(Namfun_t*)0);
    961 	Namval_t *mp = np->nvalue.np;
    962 	if(!sh.subshell)
    963 		free((void*)dp);
    964 	if(val)
    965 		nv_clone(mp,np,NV_NOFREE);
    966 	np->nvalue.cp = 0;
    967 	nv_putval(np,val,flags);
    968 }
    969 
    970 static const Namdisc_t clone_disc =
    971 {
    972 	0,
    973 	clone_putv,
    974 	clone_getv,
    975 	clone_getn
    976 };
    977 
    978 Namval_t *nv_mkclone(Namval_t *mp)
    979 {
    980 	Namval_t *np;
    981 	Namfun_t *dp;
    982 	np = newof(0,Namval_t,1,0);
    983 	np->nvflag = mp->nvflag;
    984 	np->nvsize = mp->nvsize;
    985 	np->nvname = mp->nvname;
    986 	np->nvalue.np = mp;
    987 	np->nvflag = mp->nvflag;
    988 	dp = newof(0,Namfun_t,1,0);
    989 	dp->disc = &clone_disc;
    990 	nv_stack(np,dp);
    991 	dtinsert(nv_dict(sh.namespace),np);
    992 	return(np);
    993 }
    994 #endif /* SHOPT_NAMESPACE */
    995 
    996 Namval_t *nv_search(const char *name, Dt_t *root, int mode)
    997 {
    998 	register Namval_t *np;
    999 	register Dt_t *dp = 0;
   1000 	if(mode&HASH_NOSCOPE)
   1001 		dp = dtview(root,0);
   1002 	if(mode&HASH_BUCKET)
   1003 	{
   1004 		Namval_t *mp = (void*)name;
   1005 		if(!(np = dtsearch(root,mp)) && (mode&NV_ADD))
   1006 			name = nv_name(mp);
   1007 	}
   1008 	else
   1009 	{
   1010 		if(*name=='.' && root==sh.var_tree && !dp)
   1011 			root = sh.var_base;
   1012 		np = dtmatch(root,(void*)name);
   1013 	}
   1014 	if(!np && (mode&NV_ADD))
   1015 	{
   1016 		if(sh.namespace && !(mode&HASH_NOSCOPE) && root==sh.var_tree)
   1017 			root = nv_dict(sh.namespace);
   1018 		else if(!dp && !(mode&HASH_NOSCOPE))
   1019 		{
   1020 			register Dt_t *next;
   1021 			while(next=dtvnext(root))
   1022 				root = next;
   1023 		}
   1024 		np = (Namval_t*)dtinsert(root,newnode(name));
   1025 	}
   1026 	if(dp)
   1027 		dtview(root,dp);
   1028 	return(np);
   1029 }
   1030 
   1031 /*
   1032  * finds function or builtin for given name and the discipline variable
   1033  * if var!=0 the variable pointer is returned and the built-in name
   1034  *    is put onto the stack at the current offset.
   1035  * otherwise, a pointer to the builtin (variable or type) is returned
   1036  * and var contains the poiner to the variable
   1037  * if last==0 and first component of name is a reference, nv_bfsearch()
   1038 	will return 0.
   1039  */
   1040 Namval_t *nv_bfsearch(const char *name, Dt_t *root, Namval_t **var, char **last)
   1041 {
   1042 	int		c,offset = staktell();
   1043 	register char	*sp, *cp=0;
   1044 	Namval_t	*np, *nq;
   1045 	char		*dname=0;
   1046 	if(var)
   1047 		*var = 0;
   1048 	/* check for . in the name before = */
   1049 	for(sp=(char*)name+1; *sp; sp++)
   1050 	{
   1051 		if(*sp=='=')
   1052 			return(0);
   1053 		if(*sp=='[')
   1054 		{
   1055 			if(sp[-1]!='.')
   1056 				dname = sp;
   1057 			while(*sp=='[')
   1058 			{
   1059 				sp = nv_endsubscript((Namval_t*)0,(char*)sp,0);
   1060 				if(sp[-1]!=']')
   1061 					return(0);
   1062 			}
   1063 			if(*sp==0)
   1064 				break;
   1065 			if(*sp!='.')
   1066 				return(0);
   1067 			if(dname)
   1068 			{
   1069 				cp = dname;
   1070 				dname = sp+1;
   1071 			}
   1072 		}
   1073 		else if(*sp=='.')
   1074 			cp = sp;
   1075 	}
   1076 	if(!cp)
   1077 		return(var?nv_search(name,root,0):0);
   1078 	stakputs(name);
   1079 	stakputc(0);
   1080 	if(!dname)
   1081 		dname = cp+1;
   1082 	cp = stakptr(offset) + (cp-name);
   1083 	if(last)
   1084 		*last = cp;
   1085 	c = *cp;
   1086 	*cp = 0;
   1087 	nq=nv_open(stakptr(offset),0,NV_VARNAME|NV_ARRAY|NV_NOASSIGN|NV_NOADD|NV_NOFAIL);
   1088 	*cp = c;
   1089 	if(!nq)
   1090 	{
   1091 		np = 0;
   1092 		goto done;
   1093 	}
   1094 	if(!var)
   1095 	{
   1096 		np = nq;
   1097 		goto done;
   1098 	}
   1099 	*var = nq;
   1100 	if(c=='[')
   1101 		nv_endsubscript(nq, cp,NV_NOADD);
   1102 	return((Namval_t*)nv_setdisc(nq,dname,nq,(Namfun_t*)nq));
   1103 done:
   1104 	stakseek(offset);
   1105 	return(np);
   1106 }
   1107 
   1108 /*
   1109  * add or replace built-in version of command corresponding to <path>
   1110  * The <bltin> argument is a pointer to the built-in
   1111  * if <extra>==1, the built-in will be deleted
   1112  * Special builtins cannot be added or deleted return failure
   1113  * The return value for adding builtins is a pointer to the node or NULL on
   1114  *   failure.  For delete NULL means success and the node that cannot be
   1115  *   deleted is returned on failure.
   1116  */
   1117 Namval_t *sh_addbuiltin(const char *path, int (*bltin)(int, char*[],void*),void *extra)
   1118 {
   1119 	register const char	*name = path_basename(path);
   1120 	char			*cp;
   1121 	register Namval_t	*np, *nq=0;
   1122 	int			offset=staktell();
   1123 	if(name==path && (nq=nv_bfsearch(name,sh.bltin_tree,(Namval_t**)0,&cp)))
   1124 		path = name = stakptr(offset);
   1125 	if(np = nv_search(path,sh.bltin_tree,0))
   1126 	{
   1127 		/* exists without a path */
   1128 		if(extra == (void*)1)
   1129 		{
   1130 			if(np->nvfun && !nv_isattr(np,NV_NOFREE))
   1131 				free((void*)np->nvfun);
   1132 			dtdelete(sh.bltin_tree,np);
   1133 			return(0);
   1134 		}
   1135 		if(!bltin)
   1136 			return(np);
   1137 	}
   1138 	else for(np=(Namval_t*)dtfirst(sh.bltin_tree);np;np=(Namval_t*)dtnext(sh.bltin_tree,np))
   1139 	{
   1140 		if(strcmp(name,path_basename(nv_name(np))))
   1141 			continue;
   1142 		/* exists probably with different path so delete it */
   1143 		if(strcmp(path,nv_name(np)))
   1144 		{
   1145 			if(nv_isattr(np,BLT_SPC))
   1146 				return(np);
   1147 			if(!bltin)
   1148 				bltin = np->nvalue.bfp;
   1149 			if(np->nvenv)
   1150 				dtdelete(sh.bltin_tree,np);
   1151 			if(extra == (void*)1)
   1152 				return(0);
   1153 			np = 0;
   1154 		}
   1155 		break;
   1156 	}
   1157 	if(!np && !(np = nv_search(path,sh.bltin_tree,bltin?NV_ADD:0)))
   1158 		return(0);
   1159 	if(nv_isattr(np,BLT_SPC))
   1160 	{
   1161 		if(extra)
   1162 			np->nvfun = (Namfun_t*)extra;
   1163 		return(np);
   1164 	}
   1165 	np->nvenv = 0;
   1166 	np->nvfun = 0;
   1167 	if(bltin)
   1168 	{
   1169 		np->nvalue.bfp = bltin;
   1170 		nv_onattr(np,NV_BLTIN|NV_NOFREE);
   1171 		np->nvfun = (Namfun_t*)extra;
   1172 	}
   1173 	if(nq)
   1174 	{
   1175 		cp=nv_setdisc(nq,cp+1,np,(Namfun_t*)nq);
   1176 		nv_close(nq);
   1177 		if(!cp)
   1178 			errormsg(SH_DICT,ERROR_exit(1),e_baddisc,name);
   1179 	}
   1180 	if(extra == (void*)1)
   1181 		return(0);
   1182 	return(np);
   1183 }
   1184 
   1185 #undef nv_stack
   1186 extern Namfun_t *nv_stack(register Namval_t *np, register Namfun_t* fp)
   1187 {
   1188 	return(nv_disc(np,fp,0));
   1189 }
   1190 
   1191 struct table
   1192 {
   1193 	Namfun_t	fun;
   1194 	Namval_t	*parent;
   1195 	Shell_t		*shp;
   1196 	Dt_t		*dict;
   1197 };
   1198 
   1199 static Namval_t *next_table(register Namval_t* np, Dt_t *root,Namfun_t *fp)
   1200 {
   1201 	struct table *tp = (struct table *)fp;
   1202 	if(root)
   1203 		return((Namval_t*)dtnext(root,np));
   1204 	else
   1205 		return((Namval_t*)dtfirst(tp->dict));
   1206 }
   1207 
   1208 static Namval_t *create_table(Namval_t *np,const char *name,int flags,Namfun_t *fp)
   1209 {
   1210 	struct table *tp = (struct table *)fp;
   1211 	tp->shp->last_table = np;
   1212 	return(nv_create(name, tp->dict, flags, fp));
   1213 }
   1214 
   1215 static Namfun_t *clone_table(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
   1216 {
   1217 	struct table	*tp = (struct table*)fp;
   1218 	struct table	*ntp = (struct table*)nv_clone_disc(fp,0);
   1219 	Dt_t		*oroot=tp->dict,*nroot=dtopen(&_Nvdisc,Dtoset);
   1220 	if(!nroot)
   1221 		return(0);
   1222 	memcpy((void*)ntp,(void*)fp,sizeof(struct table));
   1223 	ntp->dict = nroot;
   1224 	ntp->parent = nv_lastdict();
   1225 	for(np=(Namval_t*)dtfirst(oroot);np;np=(Namval_t*)dtnext(oroot,np))
   1226 	{
   1227 		mp = (Namval_t*)dtinsert(nroot,newnode(np->nvname));
   1228 		nv_clone(np,mp,flags);
   1229 	}
   1230 	return(&ntp->fun);
   1231 }
   1232 
   1233 static void put_table(register Namval_t* np, const char* val, int flags, Namfun_t* fp)
   1234 {
   1235 	register Dt_t		*root = ((struct table*)fp)->dict;
   1236 	register Namval_t	*nq, *mp;
   1237 	Namarr_t		*ap;
   1238 	nv_putv(np,val,flags,fp);
   1239 	if(val)
   1240 		return;
   1241 	if(nv_isarray(np) && (ap=nv_arrayptr(np)) && array_elem(ap))
   1242 		return;
   1243 	for(mp=(Namval_t*)dtfirst(root);mp;mp=nq)
   1244 	{
   1245 		_nv_unset(mp,flags);
   1246 		nq = (Namval_t*)dtnext(root,mp);
   1247 		dtdelete(root,mp);
   1248 		free((void*)mp);
   1249 	}
   1250 	dtclose(root);
   1251 	if(!(fp->nofree&1))
   1252 		free((void*)fp);
   1253 }
   1254 
   1255 /*
   1256  * return space separated list of names of variables in given tree
   1257  */
   1258 static char *get_table(Namval_t *np, Namfun_t *fp)
   1259 {
   1260 	register Dt_t *root = ((struct table*)fp)->dict;
   1261 	static Sfio_t *out;
   1262 	register int first=1;
   1263 	register Dt_t *base = dtview(root,0);
   1264         if(out)
   1265                 sfseek(out,(Sfoff_t)0,SEEK_SET);
   1266         else
   1267                 out =  sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
   1268 	for(np=(Namval_t*)dtfirst(root);np;np=(Namval_t*)dtnext(root,np))
   1269 	{
   1270                 if(!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE))
   1271 		{
   1272 			if(!first)
   1273 				sfputc(out,' ');
   1274 			else
   1275 				first = 0;
   1276 			sfputr(out,np->nvname,-1);
   1277 		}
   1278 	}
   1279 	sfputc(out,0);
   1280 	if(base)
   1281 		dtview(root,base);
   1282 	return((char*)out->_data);
   1283 }
   1284 
   1285 static const Namdisc_t table_disc =
   1286 {
   1287         sizeof(struct table),
   1288         put_table,
   1289         get_table,
   1290         0,
   1291         0,
   1292         create_table,
   1293         clone_table,
   1294         0,
   1295         next_table,
   1296 };
   1297 
   1298 Namval_t *nv_parent(Namval_t *np)
   1299 {
   1300 	struct table *tp = (struct table *)nv_hasdisc(np,&table_disc);
   1301 	if(tp)
   1302 		return(tp->parent);
   1303 	return(0);
   1304 }
   1305 
   1306 Dt_t *nv_dict(Namval_t* np)
   1307 {
   1308 	struct table *tp = (struct table*)nv_hasdisc(np,&table_disc);
   1309 	if(tp)
   1310 		return(tp->dict);
   1311 	np = sh.last_table;
   1312 	while(np)
   1313 	{
   1314 		if(tp = (struct table*)nv_hasdisc(np,&table_disc))
   1315 			return(tp->dict);
   1316 #if 0
   1317 		np = nv_create(np,(const char*)0, NV_FIRST, (Namfun_t*)0);
   1318 #else
   1319 		break;
   1320 #endif
   1321 	}
   1322 	return(sh.var_tree);
   1323 }
   1324 
   1325 /*
   1326  * create a mountable name-value pair tree
   1327  */
   1328 Namval_t *nv_mount(Namval_t *np, const char *name, Dt_t *dict)
   1329 {
   1330 	Namval_t *mp, *pp=0;
   1331 	struct table *tp = newof((struct table*)0, struct table,1,0);
   1332 	if(name)
   1333 	{
   1334 		if(nv_istable(np))
   1335 			pp = np;
   1336 		else
   1337 			pp = nv_lastdict();
   1338 	}
   1339 	if(!(tp = newof((struct table*)0, struct table,1,0)))
   1340 		return(0);
   1341 	if(name)
   1342 	{
   1343 		Namfun_t *fp = pp->nvfun;
   1344 		mp = (*fp->disc->createf)(pp,name,0,fp);
   1345 	}
   1346 	else
   1347 		mp = np;
   1348 	if(!nv_isnull(mp))
   1349 		nv_unset(mp);
   1350 	tp->shp = sh_getinterp();
   1351 	tp->dict = dict;
   1352 	tp->parent = pp;
   1353 	tp->fun.disc = &table_disc;
   1354 	nv_onattr(mp,NV_TABLE);
   1355 	nv_disc(mp, &tp->fun, NV_FIRST);
   1356 	return(mp);
   1357 }
   1358 
   1359 const Namdisc_t *nv_discfun(int which)
   1360 {
   1361 	switch(which)
   1362 	{
   1363 	    case NV_DCADD:
   1364 		return(&Nv_bdisc);
   1365 	    case NV_DCRESTRICT:
   1366 		return(&RESTRICTED_disc);
   1367 	}
   1368 	return(0);
   1369 }
   1370 
   1371