nvdisc.c revision da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1982-2007 AT&T Knowledge Ventures *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Knowledge Ventures *
* *
* A copy of the License is available at *
* http://www.opensource.org/licenses/cpl1.0.txt *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* David Korn <dgk@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* AT&T Labs
*
*/
#include "defs.h"
#include "variables.h"
#include "builtins.h"
#include "path.h"
static const char *discnames[] = { "get", "set", "append", "unset", 0 };
int nv_compare(Dt_t* dict, Void_t *sp, Void_t *dp, Dtdisc_t *disc)
{
if(sp==dp)
return(0);
return(strcmp((char*)sp,(char*)dp));
}
/*
* call the next getval function in the chain
*/
char *nv_getv(Namval_t *np, register Namfun_t *nfp)
{
register Namfun_t *fp;
register char *cp;
if((fp = nfp) != NIL(Namfun_t*) && !nv_local)
fp = nfp = nfp->next;
nv_local=0;
for(; fp; fp=fp->next)
{
if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
continue;
if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
break;
}
if(fp && fp->disc->getval)
cp = (*fp->disc->getval)(np,fp);
else if(fp && fp->disc->getnum)
{
sfprintf(sh.strbuf,"%.*Lg",12,(*fp->disc->getnum)(np,fp));
cp = sfstruse(sh.strbuf);
}
else
{
nv_local=1;
cp = nv_getval(np);
}
return(cp);
}
/*
* call the next getnum function in the chain
*/
Sfdouble_t nv_getn(Namval_t *np, register Namfun_t *nfp)
{
register Namfun_t *fp;
register Sfdouble_t d=0;
char *str;
if((fp = nfp) != NIL(Namfun_t*) && !nv_local)
fp = nfp = nfp->next;
nv_local=0;
for(; fp; fp=fp->next)
{
if(!fp->disc->getnum && !fp->disc->getval)
continue;
if(!fp->disc->getnum && nv_isattr(np,NV_INTEGER))
continue;
if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
break;
}
if(fp && fp->disc->getnum)
d = (*fp->disc->getnum)(np,fp);
else if(nv_isattr(np,NV_INTEGER))
{
nv_local = 1;
d = nv_getnum(np);
}
else
{
if(fp && fp->disc->getval)
str = (*fp->disc->getval)(np,fp);
else
str = nv_getv(np,fp?fp:nfp);
if(str && *str)
{
while(*str=='0')
str++;
d = sh_arith(str);
}
}
return(d);
}
/*
* call the next assign function in the chain
*/
void nv_putv(Namval_t *np, const char *value, int flags, register Namfun_t *nfp)
{
register Namfun_t *fp, *fpnext;
if((fp=nfp) != NIL(Namfun_t*) && !nv_local)
fp = nfp = nfp->next;
nv_local=0;
for(; fp; fp=fpnext)
{
fpnext = fp->next;
if(!fp->disc->putval)
{
if(!value)
{
nv_disc(np,fp,NV_POP);
if(!fp->nofree)
free((void*)fp);
}
continue;
}
if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
break;
}
if(fp && fp->disc->putval)
(*fp->disc->putval)(np,value, flags, fp);
else
{
nv_local=1;
if(value)
nv_putval(np, value, flags);
else
_nv_unset(np, flags&NV_RDONLY);
}
}
#if 0
/*
* node creation discipline
*/
Namval_t *nv_create(register Namval_t* np,const char *name,int flag,register Namfun_t *fp)
{
fp = fp?fp->next:np->nvfun;
while(fp && fp->disc && !fp->disc->createf)
fp = fp->next;
if(fp && fp->disc->createf)
return((*fp->disc->createf)(np,name,flag,fp));
return(NIL(Namval_t*));
}
#endif
#define LOOKUP 0
#define ASSIGN 1
#define APPEND 2
#define UNASSIGN 3
#define BLOCKED ((Namval_t*)&nv_local)
struct vardisc
{
Namfun_t fun;
Namval_t *disc[4];
};
struct blocked
{
struct blocked *next;
Namval_t *np;
int flags;
void *sub;
int isub;
};
static struct blocked *blist;
#define isblocked(bp,type) ((bp)->flags & (1<<(type)))
#define block(bp,type) ((bp)->flags |= (1<<(type)))
#define unblock(bp,type) ((bp)->flags &= ~(1<<(type)))
/*
* returns pointer to blocking structure
*/
static struct blocked *block_info(Namval_t *np, struct blocked *pp)
{
register struct blocked *bp;
void *sub=0;
int isub=0;
if(nv_isarray(np) && (isub=nv_aindex(np)) < 0)
sub = nv_associative(np,(const char*)0,NV_ACURRENT);
for(bp=blist ; bp; bp=bp->next)
{
if(bp->np==np && bp->sub==sub && bp->isub==isub)
return(bp);
}
if(pp)
{
pp->np = np;
pp->flags = 0;
pp->isub = isub;
pp->sub = sub;
pp->next = blist;
blist = pp;
}
return(pp);
}
static void block_done(struct blocked *bp)
{
blist = bp = bp->next;
if(bp && (bp->isub>=0 || bp->sub))
nv_putsub(bp->np, bp->sub,(bp->isub<0?0:bp->isub)|ARRAY_SETSUB);
}
/*
* free discipline if no more discipline functions
*/
static void chktfree(register Namval_t *np, register struct vardisc *vp)
{
register int n;
for(n=0; n< sizeof(vp->disc)/sizeof(*vp->disc); n++)
{
if(vp->disc[n])
break;
}
if(n>=sizeof(vp->disc)/sizeof(*vp->disc))
{
/* no disc left so pop */
Namfun_t *fp;
if((fp=nv_stack(np, NIL(Namfun_t*))) && !fp->nofree)
free((void*)fp);
}
}
/*
* This function performs an assignment disc on the given node <np>
*/
static void assign(Namval_t *np,const char* val,int flags,Namfun_t *handle)
{
int type = (flags&NV_APPEND)?APPEND:ASSIGN;
register struct vardisc *vp = (struct vardisc*)handle;
register Namval_t *nq = vp->disc[type];
struct blocked block, *bp = block_info(np, &block);
Namval_t node;
if(val || isblocked(bp,type))
{
if(!nq || isblocked(bp,type))
{
nv_putv(np,val,flags,handle);
goto done;
}
node = *SH_VALNOD;
if(!nv_isnull(SH_VALNOD))
{
nv_onattr(SH_VALNOD,NV_NOFREE);
nv_unset(SH_VALNOD);
}
if(flags&NV_INTEGER)
nv_onattr(SH_VALNOD,(flags&(NV_INTEGER|NV_LONG|NV_DOUBLE|NV_EXPNOTE|NV_SHORT)));
nv_putval(SH_VALNOD, val, (flags&NV_INTEGER)?flags:NV_NOFREE);
}
else
nq = vp->disc[type=UNASSIGN];
if(nq && !isblocked(bp,type))
{
int bflag;
block(bp,type);
if (type==APPEND && (bflag= !isblocked(bp,LOOKUP)))
block(bp,LOOKUP);
sh_fun(nq,np,(char**)0);
unblock(bp,type);
if(bflag)
unblock(bp,LOOKUP);
if(!vp->disc[type])
chktfree(np,vp);
}
if(val)
{
register char *cp;
Sfdouble_t d;
if(nv_isnull(SH_VALNOD))
cp=0;
else if(flags&NV_INTEGER)
{
d = nv_getnum(SH_VALNOD);
cp = (char*)(&d);
flags |= (NV_LONG|NV_DOUBLE);
flags &= ~NV_SHORT;
}
else
cp = nv_getval(SH_VALNOD);
if(cp)
nv_putv(np,cp,flags|NV_RDONLY,handle);
nv_unset(SH_VALNOD);
/* restore everything but the nvlink field */
memcpy(&SH_VALNOD->nvname, &node.nvname, sizeof(node)-sizeof(node.nvlink));
}
else if(sh_isstate(SH_INIT))
{
/* don't free functions during reinitialization */
nv_putv(np,val,flags,handle);
}
else if(!nq || !isblocked(bp,type))
{
Dt_t *root = sh_subfuntree(1);
int n;
Namarr_t *ap;
block(bp,type);
nv_putv(np, val, flags, handle);
if(nv_isarray(np) && (ap=nv_arrayptr(np)) && ap->nelem>0)
goto done;
for(n=0; n < sizeof(vp->disc)/sizeof(*vp->disc); n++)
{
if((nq=vp->disc[n]) && !nv_isattr(nq,NV_NOFREE))
{
nv_unset(nq);
dtdelete(root,nq);
}
}
unblock(bp,type);
nv_disc(np,handle,NV_POP);
if(!handle->nofree)
free(handle);
}
done:
if(bp== &block)
block_done(bp);
}
/*
* This function executes a lookup disc and then performs
* the lookup on the given node <np>
*/
static char* lookup(Namval_t *np, Namfun_t *handle)
{
register struct vardisc *vp = (struct vardisc*)handle;
struct blocked block, *bp = block_info(np, &block);
register Namval_t *nq = vp->disc[LOOKUP];
register char *cp=0;
Namval_t node;
if(nq && !isblocked(bp,LOOKUP))
{
node = *SH_VALNOD;
if(!nv_isnull(SH_VALNOD))
{
nv_onattr(SH_VALNOD,NV_NOFREE);
nv_unset(SH_VALNOD);
}
block(bp,LOOKUP);
sh_fun(nq,np,(char**)0);
unblock(bp,LOOKUP);
if(!vp->disc[LOOKUP])
chktfree(np,vp);
cp = nv_getval(SH_VALNOD);
if(!nv_isnull(&node))
{
if(cp)
cp = strdup(cp);
/* restore everything but the nvlink field */
memcpy(&SH_VALNOD->nvname, &node.nvname, sizeof(node)-sizeof(node.nvlink));
}
}
if(!cp)
cp = nv_getv(np,handle);
if(bp== &block)
block_done(bp);
return(cp);
}
static const Namdisc_t shdisc =
{
sizeof(struct vardisc),
assign,
lookup
};
/*
* Set disc on given <event> to <action>
* If action==np, the current disc is returned
* A null return value indicates that no <event> is known for <np>
* If <event> is NULL, then return the event name after <action>
* If <event> is NULL, and <action> is NULL, return the first event
*/
char *nv_setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp)
{
register struct vardisc *vp = (struct vardisc*)np->nvfun;
register int type;
char *empty = "";
if(np == (Namval_t*)fp)
{
register const char *name;
register int getname=0;
/* top level call, check for get/set */
if(!event)
{
if(!action)
return((char*)discnames[0]);
getname=1;
event = (char*)action;
}
for(type=0; name=discnames[type]; type++)
{
if(strcmp(event,name)==0)
break;
}
if(getname)
{
event = 0;
if(name && !(name = discnames[++type]))
action = 0;
}
if(!name)
{
if((fp=(Namfun_t*)vp) && fp->disc->setdisc)
return((*fp->disc->setdisc)(np,event,action,fp));
}
else if(getname)
return((char*)name);
}
if(!fp)
return(NIL(char*));
if(np != (Namval_t*)fp)
{
/* not the top level */
while(fp = fp->next)
{
if(fp->disc->setdisc)
return((*fp->disc->setdisc)(np,event,action,fp));
}
return(NIL(char*));
}
/* Handle GET/SET/APPEND/UNSET disc */
if(vp && vp->fun.disc->putval!=assign)
vp = 0;
if(!vp)
{
if(action==np)
return((char*)action);
if(!(vp = newof(NIL(struct vardisc*),struct vardisc,1,0)))
return(0);
vp->fun.disc = &shdisc;
nv_stack(np, (Namfun_t*)vp);
}
if(action==np)
{
action = vp->disc[type];
empty = 0;
}
else if(action)
vp->disc[type] = action;
else
{
struct blocked *bp;
action = vp->disc[type];
vp->disc[type] = 0;
if(!(bp=block_info(np,(struct blocked*)0)) || !isblocked(bp,UNASSIGN))
chktfree(np,vp);
}
return(action?(char*)action:empty);
}
/*
* Set disc on given <event> to <action>
* If action==np, the current disc is returned
* A null return value indicates that no <event> is known for <np>
* If <event> is NULL, then return the event name after <action>
* If <event> is NULL, and <action> is NULL, return the first event
*/
static char *setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp)
{
register Nambfun_t *vp = (Nambfun_t*)fp;
register int type,getname=0;
register const char *name;
const char **discnames = vp->bnames;
/* top level call, check for discipline match */
if(!event)
{
if(!action)
return((char*)discnames[0]);
getname=1;
event = (char*)action;
}
for(type=0; name=discnames[type]; type++)
{
if(strcmp(event,name)==0)
break;
}
if(getname)
{
event = 0;
if(name && !(name = discnames[++type]))
action = 0;
}
if(!name)
return(nv_setdisc(np,event,action,fp));
else if(getname)
return((char*)name);
/* Handle the disciplines */
if(action==np)
action = vp->bltins[type];
else if(action)
vp->bltins[type] = action;
else
{
action = vp->bltins[type];
vp->bltins[type] = 0;
}
return(action?(char*)action:"");
}
static void putdisc(Namval_t* np, const char* val, int flag, Namfun_t* fp)
{
nv_putv(np,val,flag,fp);
if(!val)
{
register Nambfun_t *vp = (Nambfun_t*)fp;
register int i;
for(i=0; vp->bnames[i]; i++)
{
register Namval_t *mp;
if((mp=vp->bltins[i]) && !nv_isattr(mp,NV_NOFREE))
{
if(is_abuiltin(mp))
{
if(mp->nvfun && !nv_isattr(mp,NV_NOFREE))
free((void*)mp->nvfun);
dtdelete(sh.bltin_tree,mp);
free((void*)mp);
}
}
}
nv_disc(np,fp,NV_POP);
if(!fp->nofree)
free((void*)fp);
}
}
static const Namdisc_t Nv_bdisc = { 0, putdisc, 0, 0, setdisc };
static Namfun_t *nv_clone_disc(register Namfun_t *fp)
{
register Namfun_t *nfp;
register int size;
if(!(size=fp->dsize) && (!fp->disc || !(size=fp->disc->dsize)))
size = sizeof(Namfun_t);
if(!(nfp=newof(NIL(Namfun_t*),Namfun_t,1,size-sizeof(Namfun_t))))
return(0);
memcpy(nfp,fp,size);
nfp->nofree = 0;
return(nfp);
}
int nv_adddisc(Namval_t *np, const char **names, Namval_t **funs)
{
register Nambfun_t *vp;
register int n=0;
register const char **av=names;
if(av)
{
while(*av++)
n++;
}
if(!(vp = newof(NIL(Nambfun_t*),Nambfun_t,1,n*sizeof(Namval_t*))))
return(0);
vp->fun.dsize = sizeof(Nambfun_t)+n*sizeof(Namval_t*);
vp->fun.funs = 1;
if(funs)
memcpy((void*)vp->bltins, (void*)funs,n*sizeof(Namval_t*));
else while(n>=0)
vp->bltins[n--] = 0;
vp->fun.disc = &Nv_bdisc;
vp->bnames = names;
nv_stack(np,&vp->fun);
return(1);
}
/*
* push, pop, clone, or reorder disciplines onto node <np>
* mode can be one of
* NV_FIRST: Move or push <fp> to top of the stack or delete top
* NV_LAST: Move or push <fp> to bottom of stack or delete last
* NV_POP: Delete <fp> from top of the stack
* NV_CLONE: Replace fp with a copy created my malloc() and return it
*/
Namfun_t *nv_disc(register Namval_t *np, register Namfun_t* fp, int mode)
{
Namfun_t *lp, **lpp;
if(nv_isref(np))
return(0);
if(mode==NV_CLONE && !fp)
return(0);
if(fp)
{
if((lp=np->nvfun)==fp)
{
if(mode==NV_CLONE)
{
lp = nv_clone_disc(fp);
return(np->nvfun=lp);
}
if(mode==NV_FIRST || mode==0)
return(fp);
np->nvfun = lp->next;
if(mode==NV_POP)
return(fp);
}
/* see if <fp> is on the list already */
lpp = &np->nvfun;
if(lp)
{
while(lp->next)
{
if(lp->next==fp)
{
if(mode==NV_CLONE)
{
fp = nv_clone_disc(fp);
lp->next = fp;
return(fp);
}
lp->next = fp->next;
if(mode==NV_POP)
return(fp);
if(mode!=NV_LAST)
break;
}
lp = lp->next;
}
if(mode==NV_LAST)
lpp = &lp->next;
}
if(mode==NV_POP)
return(0);
/* push */
nv_offattr(np,NV_NODISC);
if(mode==NV_LAST)
fp->next = 0;
else
{
if(fp->nofree && *lpp)
fp = nv_clone_disc(fp);
fp->next = *lpp;
}
*lpp = fp;
}
else
{
if(mode==NV_FIRST)
return(np->nvfun);
else if(mode==NV_LAST)
for(lp=np->nvfun; lp; fp=lp,lp=lp->next);
else if(fp = np->nvfun)
np->nvfun = fp->next;
}
return(fp);
}
/*
* returns discipline pointer if discipline with specified functions
* is on the discipline stack
*/
Namfun_t *nv_hasdisc(Namval_t *np, const Namdisc_t *dp)
{
register Namfun_t *fp;
for(fp=np->nvfun; fp; fp = fp->next)
{
if(fp->disc== dp)
return(fp);
}
return(0);
}
struct notify
{
Namfun_t hdr;
char **ptr;
};
static void put_notify(Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
struct notify *pp = (struct notify*)fp;
nv_putv(np,val,flags,fp);
nv_stack(np,fp);
nv_stack(np,(Namfun_t*)0);
*pp->ptr = 0;
if(!fp->nofree)
free((void*)fp);
}
static const Namdisc_t notify_disc = { 0, put_notify };
int nv_unsetnotify(Namval_t *np, char **addr)
{
register Namfun_t *fp;
for(fp=np->nvfun;fp;fp=fp->next)
{
if(fp->disc->putval==put_notify && ((struct notify*)fp)->ptr==addr)
{
nv_stack(np,fp);
nv_stack(np,(Namfun_t*)0);
if(!fp->nofree)
free((void*)fp);
return(1);
}
}
return(0);
}
int nv_setnotify(Namval_t *np, char **addr)
{
struct notify *pp = newof(0,struct notify, 1,0);
if(!pp)
return(0);
pp->ptr = addr;
pp->hdr.disc = &notify_disc;
nv_stack(np,&pp->hdr);
return(1);
}
static void *newnode(const char *name)
{
register int s;
register Namval_t *np = newof(0,Namval_t,1,s=strlen(name)+1);
if(np)
{
np->nvname = (char*)np+sizeof(Namval_t);
memcpy(np->nvname,name,s);
}
return((void*)np);
}
#if SHOPT_NAMESPACE
/*
* clone a numeric value
*/
static void *num_clone(register Namval_t *np, void *val)
{
register int size;
void *nval;
if(!val)
return(0);
if(nv_isattr(np,NV_DOUBLE))
{
if(nv_isattr(np,NV_LONG))
size = sizeof(Sfdouble_t);
else if(nv_isattr(np,NV_SHORT))
size = sizeof(float);
else
size = sizeof(double);
}
else
{
if(nv_isattr(np,NV_LONG))
size = sizeof(Sflong_t);
else if(nv_isattr(np,NV_SHORT))
size = sizeof(int16_t);
else
size = sizeof(int32_t);
}
if(!(nval = malloc(size)))
return(0);
memcpy(nval,val,size);
return(nval);
}
static void clone_all_disc( Namval_t *np, Namval_t *mp, int flags)
{
register Namfun_t *fp, **mfp = &mp->nvfun, *nfp;
for(fp=np->nvfun; fp;fp=fp->next)
{
if(fp->funs && (flags&NV_NODISC))
nfp = 0;
if(fp->disc && fp->disc->clonef)
nfp = (*fp->disc->clonef)(np,mp,flags,fp);
else
nfp = nv_clone_disc(fp);
if(!nfp)
continue;
nfp->next = 0;
*mfp = nfp;
mfp = &nfp->next;
}
}
/*
* clone <mp> from <np> flags can be one of the following
* NV_APPEND - append <np> onto <mp>
* NV_MOVE - move <np> to <mp>
* NV_NOFREE - mark the new node as nofree
* NV_NODISC - discplines with funs non-zero will not be copied
*/
int nv_clone(Namval_t *np, Namval_t *mp, int flags)
{
Namfun_t *fp;
if(fp=np->nvfun)
{
if(flags&NV_MOVE)
{
mp->nvfun = fp;
goto skip;
}
clone_all_disc(np, mp, flags);
}
if(flags&NV_APPEND)
return(1);
skip:
nv_setsize(mp,nv_size(np));
if(!nv_isattr(mp,NV_MINIMAL) || nv_isattr(mp,NV_EXPORT))
mp->nvenv = (!nv_isattr(np,NV_MINIMAL)||nv_isattr(np,NV_EXPORT))?np->nvenv:0;
mp->nvalue.cp = np->nvalue.cp;
mp->nvflag = np->nvflag;
if(flags&NV_MOVE)
{
np->nvfun = 0;
np->nvalue.cp = 0;
if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(mp,NV_EXPORT))
np->nvenv = 0;
np->nvflag = 0;
nv_setsize(np,0);
return(1);
}
if(nv_isattr(np,NV_INTEGER))
mp->nvalue.ip = (int*)num_clone(np,(void*)np->nvalue.ip);
else if(flags&NV_NOFREE)
nv_onattr(np,NV_NOFREE);
return(1);
}
/*
* The following discipline is for copy-on-write semantics
*/
static char* clone_getv(Namval_t *np, Namfun_t *handle)
{
return(np->nvalue.np?nv_getval(np->nvalue.np):0);
}
static Sfdouble_t clone_getn(Namval_t *np, Namfun_t *handle)
{
return(np->nvalue.np?nv_getnum(np->nvalue.np):0);
}
static void clone_putv(Namval_t *np,const char* val,int flags,Namfun_t *handle)
{
Namfun_t *dp = nv_stack(np,(Namfun_t*)0);
Namval_t *mp = np->nvalue.np;
if(!sh.subshell)
free((void*)dp);
if(val)
nv_clone(mp,np,NV_NOFREE);
np->nvalue.cp = 0;
nv_putval(np,val,flags);
}
static const Namdisc_t clone_disc =
{
0,
clone_putv,
clone_getv,
clone_getn
};
Namval_t *nv_mkclone(Namval_t *mp)
{
Namval_t *np;
Namfun_t *dp;
np = newof(0,Namval_t,1,0);
np->nvflag = mp->nvflag;
np->nvsize = mp->nvsize;
np->nvname = mp->nvname;
np->nvalue.np = mp;
np->nvflag = mp->nvflag;
dp = newof(0,Namfun_t,1,0);
dp->disc = &clone_disc;
nv_stack(np,dp);
dtinsert(nv_dict(sh.namespace),np);
return(np);
}
#endif /* SHOPT_NAMESPACE */
Namval_t *nv_search(const char *name, Dt_t *root, int mode)
{
register Namval_t *np;
register Dt_t *dp = 0;
if(mode&HASH_NOSCOPE)
dp = dtview(root,0);
if(mode&HASH_BUCKET)
{
Namval_t *mp = (void*)name;
if(!(np = dtsearch(root,mp)) && (mode&NV_ADD))
name = nv_name(mp);
}
else
{
if(*name=='.' && root==sh.var_tree)
root = sh.var_base;
np = dtmatch(root,(void*)name);
}
if(!np && (mode&NV_ADD))
{
if(sh.namespace && !(mode&HASH_NOSCOPE) && root==sh.var_tree)
root = nv_dict(sh.namespace);
else if(!dp && !(mode&HASH_NOSCOPE))
{
register Dt_t *next;
while(next=dtvnext(root))
root = next;
}
np = (Namval_t*)dtinsert(root,newnode(name));
}
if(dp)
dtview(root,dp);
return(np);
}
/*
* finds function or builtin for given name and the discipline variable
* if var!=0 the variable pointer is returned and the built-in name
* is put onto the stack at the current offset.
* otherwise, a pointer to the builtin (variable or type) is returned
* and var contains the poiner to the variable
* if last==0 and first component of name is a reference, nv_bfsearch()
will return 0.
*/
Namval_t *nv_bfsearch(const char *name, Dt_t *root, Namval_t **var, char **last)
{
int offset = staktell();
register char *sp, *cp=0;
Namval_t *np, *nq;
if(var)
*var = 0;
/* check for . in the name before = */
for(sp=(char*)name+1; *sp; sp++)
{
if(*sp=='=')
return(0);
if(*sp=='.')
cp = sp;
}
if(!cp)
return(var?nv_search(name,root,0):0);
stakputs(name);
stakputc(0);
cp = stakptr(offset) + (cp-name);
if(last)
*last = cp;
*cp = 0;
nq=nv_open(stakptr(offset),0,NV_VARNAME|NV_NOASSIGN|NV_NOADD|NV_NOFAIL);
*cp = '.';
if(!nq)
{
np = 0;
goto done;
}
if(!var)
{
np = nq;
goto done;
}
*var = nq;
return((Namval_t*)nv_setdisc(nq,cp+1,nq,(Namfun_t*)nq));
done:
stakseek(offset);
return(np);
}
/*
* add or replace built-in version of command commresponding to <path>
* The <bltin> argument is a pointer to the built-in
* if <extra>==1, the built-in will be deleted
* Special builtins cannot be added or deleted return failure
* The return value for adding builtins is a pointer to the node or NULL on
* failure. For delete NULL means success and the node that cannot be
* deleted is returned on failure.
*/
Namval_t *sh_addbuiltin(const char *path, int (*bltin)(int, char*[],void*),void *extra)
{
register const char *name = path_basename(path);
char *cp;
register Namval_t *np, *nq=0;
int offset = staktell();
if(name==path && (nq=nv_bfsearch(name,sh.bltin_tree,(Namval_t**)0,&cp)))
path = name = stakptr(offset);
if(np = nv_search(path,sh.bltin_tree,0))
{
/* exists without a path */
if(extra == (void*)1)
{
if(np->nvfun && !nv_isattr(np,NV_NOFREE))
free((void*)np->nvfun);
dtdelete(sh.bltin_tree,np);
return(0);
}
if(!bltin)
return(np);
}
else for(np=(Namval_t*)dtfirst(sh.bltin_tree);np;np=(Namval_t*)dtnext(sh.bltin_tree,np))
{
if(strcmp(name,path_basename(nv_name(np))))
continue;
/* exists probably with different path so delete it */
if(strcmp(path,nv_name(np)))
{
if(nv_isattr(np,BLT_SPC))
return(np);
if(!bltin)
bltin = np->nvalue.bfp;
if(np->nvenv)
dtdelete(sh.bltin_tree,np);
if(extra == (void*)1)
return(0);
np = 0;
}
break;
}
if(!np && !(np = nv_search(path,sh.bltin_tree,bltin?NV_ADD:0)))
return(0);
if(nv_isattr(np,BLT_SPC))
return(np);
np->nvenv = 0;
np->nvfun = 0;
nv_setattr(np,0);
if(bltin)
{
np->nvalue.bfp = bltin;
nv_onattr(np,NV_BLTIN|NV_NOFREE);
np->nvfun = (Namfun_t*)extra;
}
if(nq)
{
cp=nv_setdisc(nq,cp+1,np,(Namfun_t*)nq);
nv_close(nq);
if(!cp)
errormsg(SH_DICT,ERROR_exit(1),e_baddisc,name);
}
if(extra == (void*)1)
return(0);
return(np);
}
#undef nv_stack
extern Namfun_t *nv_stack(register Namval_t *np, register Namfun_t* fp)
{
return(nv_disc(np,fp,0));
}
struct table
{
Namfun_t fun;
Namval_t *parent;
Shell_t *shp;
Dt_t *dict;
};
Namval_t *nv_parent(Namval_t *np)
{
if(!nv_istable(np))
return(0);
return(((struct table*)(np->nvfun))->parent);
}
static Namval_t *next_table(register Namval_t* np, Dt_t *root,Namfun_t *fp)
{
struct table *tp = (struct table *)fp;
if(root)
return((Namval_t*)dtnext(root,np));
else
return((Namval_t*)dtfirst(tp->dict));
}
static Namval_t *create_table(Namval_t *np,const char *name,int flags,Namfun_t *fp)
{
struct table *tp = (struct table *)fp;
tp->shp->last_table = np;
return(nv_create(name, tp->dict, flags, fp));
}
static Namfun_t *clone_table(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
{
struct table *tp = (struct table*)fp;
struct table *ntp = (struct table*)nv_clone_disc(fp);
Dt_t *oroot=tp->dict,*nroot=dtopen(&_Nvdisc,Dtoset);
if(!nroot)
return(0);
memcpy((void*)ntp,(void*)fp,sizeof(struct table));
ntp->dict = nroot;
ntp->parent = nv_lastdict();
for(np=(Namval_t*)dtfirst(oroot);np;np=(Namval_t*)dtnext(oroot,np))
{
mp = (Namval_t*)dtinsert(nroot,newnode(np->nvname));
nv_clone(np,mp,flags);
}
return(&ntp->fun);
}
static void put_table(register Namval_t* np, const char* val, int flags, Namfun_t* fp)
{
register Dt_t *root = ((struct table*)fp)->dict;
register Namval_t *nq, *mp;
Namarr_t *ap;
nv_putv(np,val,flags,fp);
if(val)
return;
if(nv_isarray(np) && (ap=nv_arrayptr(np)) && array_elem(ap))
return;
for(mp=(Namval_t*)dtfirst(root);mp;mp=nq)
{
_nv_unset(mp,flags);
nq = (Namval_t*)dtnext(root,mp);
dtdelete(root,mp);
free((void*)mp);
}
dtclose(root);
if(!fp->nofree)
free((void*)fp);
}
/*
* return space separated list of names of variables in given tree
*/
static char *get_table(Namval_t *np, Namfun_t *fp)
{
register Dt_t *root = ((struct table*)fp)->dict;
static Sfio_t *out;
register int first=1;
register Dt_t *base = dtview(root,0);
if(out)
sfseek(out,(Sfoff_t)0,SEEK_SET);
else
out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
for(np=(Namval_t*)dtfirst(root);np;np=(Namval_t*)dtnext(root,np))
{
if(!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE))
{
if(!first)
sfputc(out,' ');
else
first = 0;
sfputr(out,np->nvname,-1);
}
}
sfputc(out,0);
if(base)
dtview(root,base);
return((char*)out->_data);
}
static const Namdisc_t table_disc =
{
sizeof(struct table),
put_table,
get_table,
0,
0,
create_table,
clone_table,
0,
next_table,
};
Dt_t *nv_dict(Namval_t* np)
{
struct table *tp = (struct table*)nv_hasdisc(np,&table_disc);
if(tp)
return(tp->dict);
np = sh.last_table;
while(np)
{
if(tp = (struct table*)nv_hasdisc(np,&table_disc))
return(tp->dict);
#if 0
np = nv_create(np,(const char*)0, NV_FIRST, (Namfun_t*)0);
#endif
}
return(sh.var_tree);
}
/*
* create a mountable name-value pair tree
*/
Namval_t *nv_mount(Namval_t *np, const char *name, Dt_t *dict)
{
Namval_t *mp, *pp=0;
struct table *tp = newof((struct table*)0, struct table,1,0);
if(name)
{
if(nv_istable(np))
pp = np;
else
pp = nv_lastdict();
}
if(!(tp = newof((struct table*)0, struct table,1,0)))
return(0);
if(name)
{
Namfun_t *fp = pp->nvfun;
mp = (*fp->disc->createf)(pp,name,0,fp);
}
else
mp = np;
if(!nv_isnull(mp))
nv_unset(mp);
tp->shp = sh_getinterp();
tp->dict = dict;
tp->parent = pp;
tp->fun.disc = &table_disc;
nv_onattr(mp,NV_TABLE);
nv_disc(mp, &tp->fun, NV_LAST);
return(mp);
}
const Namdisc_t *nv_discfun(int which)
{
switch(which)
{
case NV_DCADD:
return(&Nv_bdisc);
case NV_DCRESTRICT:
return(&RESTRICTED_disc);
}
return(0);
}
/*
* This function turns variable <np> to the type <tp>
*/
int nv_settype(Namval_t* np, Namval_t *tp, int flags)
{
int isnull = nv_isnull(np);
char *val=0;
if(isnull)
flags &= ~NV_APPEND;
else
{
val = strdup(nv_getval(np));
if(!(flags&NV_APPEND))
_nv_unset(np, NV_RDONLY);
}
if(!nv_clone(tp,np,flags|NV_NOFREE))
return(0);
if(val)
{
nv_putval(np,val,NV_RDONLY);
free((void*)val);
}
return(0);
}