Logo Search packages:      
Sourcecode: ksh version File versions  Download package

nvdisc.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*                  Copyright (c) 1982-2005 AT&T Corp.                  *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                            by AT&T Corp.                             *
*                                                                      *
*                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"

/*
 * call the next getval function in the chain
 */
char *nv_getv(Namval_t *np, register Namfun_t *nfp)
{
      register Namfun_t *fp;
      static char numbuf[20];
      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)
            sfsprintf(cp=numbuf,sizeof(numbuf),"%.*g\0",12,(*fp->disc->getnum)(np,fp));
      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)
            {
                  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];
};

/*
 * 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)
{
      register struct vardisc *vp = (struct vardisc*)handle;
      register Namval_t *nq, **disc;
      disc =  &(vp->disc[(flags&NV_APPEND)?APPEND:ASSIGN]);
      if(val || *disc==BLOCKED)
      {
            if(!(nq= *disc) || nq==BLOCKED)
            {
                  nv_putv(np,val,flags,handle);
                  return;
            }
            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
            disc =  &(vp->disc[UNASSIGN]);
      if((nq= *disc) && nq!=BLOCKED)
      {
            Namval_t *nq1, **disc1;
            *disc=BLOCKED;
            if ((flags&NV_APPEND) && (disc1= &(vp->disc[LOOKUP])) && (nq1= *disc1) && nq1!=BLOCKED)
                  *disc1=BLOCKED;
            sh_fun(nq,np,(char**)0);
            if((flags&NV_APPEND) && disc1 && nq1 && nq1!=BLOCKED)
                  *disc1=nq1;
            if(*disc==BLOCKED)
                  *disc=nq;
            else if(!*disc)
                  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);
      }
      else if(sh_isstate(SH_INIT))
      {
            /* don't free functions during reinitialization */
            nv_putv(np,val,flags,handle);
      }
      else if(!nq || nq!=BLOCKED)
      {
            Dt_t *root = sh_subfuntree(1);
            int n;
            for(n=0; n < sizeof(vp->disc)/sizeof(*vp->disc); n++)
            {
                  if(nq=vp->disc[n])
                  {
                        nv_unset(nq);
                        dtdelete(root,nq);
                  }
            }
            nv_unset(np);
      }
}

/*
 * 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;
      register Namval_t *nq;
      register char *cp=0;
      if((nq=vp->disc[LOOKUP]) &&  nq!=BLOCKED)
      {
            nv_unset(SH_VALNOD);
            vp->disc[LOOKUP]=BLOCKED;
            sh_fun(nq,np,(char**)0);
            if(vp->disc[LOOKUP]==BLOCKED)
                  vp->disc[LOOKUP]=nq;
            else if(!vp->disc[LOOKUP])
                  chktfree(np,vp);
            cp = nv_getval(SH_VALNOD);
      }
      if(!cp)
            cp = nv_getv(np,handle);
      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)
      {
            static const char *discnames[] = { "get", "set", "append", "unset", 0 };
            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
      {
            action = vp->disc[type];
            vp->disc[type] = 0;
            if(action!=BLOCKED)
                  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])
                  {
                        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)
{
      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*);
      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(short);
            else
                  size = sizeof(long);
      }
      if(!(nval = malloc(size)))
            return(0);
      memcpy(nval,val,size);
      return(nval);
}

int nv_clone(Namval_t *np, Namval_t *mp, int flags)
{
      Namfun_t *fp;
      if(fp=np->nvfun)
      {
              register Namfun_t **mfp = &mp->nvfun, *nfp;
            if(flags&NV_MOVE)
            {
                  mp->nvfun = fp;
                  goto skip;
            }
            while(fp)
            {
                  if(fp->disc && fp->disc->clonef)
                        nfp = (*fp->disc->clonef)(np,mp,flags,fp);
                  else if(!(nfp = nv_clone_disc(fp)))
                        continue;
                  nfp->next = 0;
                  *mfp = nfp;
                  mfp = &nfp->next;
                      fp = fp->next;
            }
      }
      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_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,1);
      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_NOARRAY|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,Dtset);
      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;;
      nv_putv(np,val,flags,fp);
      if(val)
            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);
}

Generated by  Doxygen 1.6.0   Back to index