summaryrefslogtreecommitdiff
path: root/usr/src/lib/libshell/common/sh/nvtree.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libshell/common/sh/nvtree.c')
-rw-r--r--usr/src/lib/libshell/common/sh/nvtree.c679
1 files changed, 679 insertions, 0 deletions
diff --git a/usr/src/lib/libshell/common/sh/nvtree.c b/usr/src/lib/libshell/common/sh/nvtree.c
new file mode 100644
index 0000000000..82657e3a0c
--- /dev/null
+++ b/usr/src/lib/libshell/common/sh/nvtree.c
@@ -0,0 +1,679 @@
+/***********************************************************************
+* *
+* 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
+
+/*
+ * code for tree nodes and name walking
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include "name.h"
+#include "argnod.h"
+
+struct nvdir
+{
+ Dt_t *root;
+ Namval_t *hp;
+ Namval_t *table;
+ Namval_t *(*nextnode)(Namval_t*,Dt_t*,Namfun_t*);
+ Namfun_t *fun;
+ struct nvdir *prev;
+ int len;
+ int offset;
+ char data[1];
+};
+
+char *nv_getvtree(Namval_t*, Namfun_t *);
+static void put_tree(Namval_t*, const char*, int,Namfun_t*);
+
+static Namval_t *create_tree(Namval_t *np,const char *name,int flag,Namfun_t *dp)
+{
+ register Namfun_t *fp=dp;
+ while(fp=fp->next)
+ {
+ if(fp->disc && fp->disc->createf)
+ {
+ if(np=(*fp->disc->createf)(np,name,flag,fp))
+ dp->last = fp->last;
+ return(np);
+ }
+ }
+ return((flag&NV_NOADD)?0:np);
+}
+
+static const Namdisc_t treedisc =
+{
+ 0,
+ put_tree,
+ nv_getvtree,
+ 0,
+ 0,
+ create_tree
+};
+
+static char *nextdot(const char *str)
+{
+ register char *cp;
+ if(*str=='.')
+ str++;
+ if(*str =='[')
+ {
+ cp = nv_endsubscript((Namval_t*)0,(char*)str,0);
+ return(*cp=='.'?cp:0);
+ }
+ else
+ return(strchr(str,'.'));
+}
+
+static Namfun_t *nextdisc(Namval_t *np)
+{
+ register Namfun_t *fp;
+ if(nv_isref(np))
+ return(0);
+ for(fp=np->nvfun;fp;fp=fp->next)
+ {
+ if(fp && fp->disc && fp->disc->nextf)
+ return(fp);
+ }
+ return(0);
+}
+
+void *nv_diropen(const char *name)
+{
+ char *next,*last;
+ int c,len=strlen(name);
+ struct nvdir *save, *dp = new_of(struct nvdir,len);
+ Namval_t *np, fake;
+ Namfun_t *nfp;
+ if(!dp)
+ return(0);
+ memset((void*)dp, 0, sizeof(*dp));
+ last=dp->data;
+ if(name[len-1]=='*' || name[len-1]=='@')
+ len -= 1;
+ name = memcpy(last,name,len);
+ last[len] = 0;
+ dp->len = len;
+ dp->root = sh.var_tree;
+ dp->table = sh.last_table;
+ if(*name)
+ {
+ fake.nvname = (char*)name;
+ dp->hp = (Namval_t*)dtprev(dp->root,&fake);
+ dp->hp = (Namval_t*)dtnext(dp->root,dp->hp);
+ }
+ else
+ dp->hp = (Namval_t*)dtfirst(dp->root);
+ while(next= nextdot(last))
+ {
+ c = *next;
+ *next = 0;
+ np = nv_search(last,dp->root,0);
+ *next = c;
+ if(np && ((nfp=nextdisc(np)) || nv_istable(np)))
+ {
+ if(!(save = new_of(struct nvdir,0)))
+ return(0);
+ *save = *dp;
+ dp->prev = save;
+ if(nv_istable(np))
+ dp->root = nv_dict(np);
+ else
+ dp->root = (Dt_t*)dp;
+ dp->offset = last-(char*)name;
+ if(dp->offset<len)
+ dp->len = len-dp->offset;
+ else
+ dp->len = 0;
+ if(nfp)
+ {
+ dp->nextnode = nfp->disc->nextf;
+ dp->table = np;
+ dp->fun = nfp;
+ dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp);
+ }
+ else
+ dp->nextnode = 0;
+ }
+ else
+ break;
+ last = next+1;
+ }
+ return((void*)dp);
+}
+
+
+static Namval_t *nextnode(struct nvdir *dp)
+{
+ if(dp->nextnode)
+ return((*dp->nextnode)(dp->hp,dp->root,dp->fun));
+ if(dp->len && memcmp(dp->data, dp->hp->nvname, dp->len))
+ return(0);
+ return((Namval_t*)dtnext(dp->root,dp->hp));
+}
+
+char *nv_dirnext(void *dir)
+{
+ register struct nvdir *save, *dp = (struct nvdir*)dir;
+ register Namval_t *np, *last_table;
+ register char *cp;
+ Namfun_t *nfp;
+ while(1)
+ {
+ while(np=dp->hp)
+ {
+ dp->hp = nextnode(dp);
+ if(nv_isnull(np))
+ continue;
+ last_table = sh.last_table;
+ sh.last_table = dp->table;
+ cp = nv_name(np);
+ sh.last_table = last_table;
+ if(!dp->len || memcmp(cp+dp->offset,dp->data,dp->len)==0)
+ {
+ if((nfp=nextdisc(np)) || nv_istable(np))
+ {
+ Dt_t *root;
+ if(nv_istable(np))
+ root = nv_dict(np);
+ else
+ root = (Dt_t*)dp;
+ /* check for recursive walk */
+ for(save=dp; save; save=save->prev)
+ {
+ if(save->root==root)
+ break;
+ }
+ if(save)
+ continue;
+ if(!(save = new_of(struct nvdir,0)))
+ return(0);
+ *save = *dp;
+ dp->prev = save;
+ dp->root = root;
+ dp->len = 0;
+ if(nfp && np->nvfun)
+ {
+ dp->nextnode = nfp->disc->nextf;
+ dp->table = np;
+ dp->fun = nfp;
+ dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp);
+ }
+ else
+ dp->nextnode = 0;
+ }
+ return(cp);
+ }
+ }
+ if(!(save=dp->prev))
+ break;
+#if 0
+ sh.last_table = dp->table;
+#endif
+ *dp = *save;
+ free((void*)save);
+ }
+ return(0);
+}
+
+void nv_dirclose(void *dir)
+{
+ struct nvdir *dp = (struct nvdir*)dir;
+ if(dp->prev)
+ nv_dirclose((void*)dp->prev);
+ free(dir);
+}
+
+static void outtype(Namval_t *np, Namfun_t *fp, Sfio_t* out, const char *prefix)
+{
+ char *type=0;
+ Namval_t *tp = fp->type;
+ if(!tp && fp->disc && fp->disc->typef)
+ tp = (*fp->disc->typef)(np,fp);
+ for(fp=fp->next;fp;fp=fp->next)
+ {
+ if(fp->type || (fp->disc && fp->disc->typef &&(*fp->disc->typef)(np,fp)))
+ {
+ outtype(np,fp,out,prefix);
+ break;
+ }
+ }
+ if(prefix && *prefix=='t')
+ type = "-T";
+ else if(!prefix)
+ type = "type";
+ if(type)
+ sfprintf(out,"%s %s ",type,tp->nvname);
+}
+
+/*
+ * print the attributes of name value pair give by <np>
+ */
+void nv_attribute(register Namval_t *np,Sfio_t *out,char *prefix,int noname)
+{
+ register const Shtable_t *tp;
+ register char *cp;
+ register unsigned val;
+ register unsigned mask;
+ register unsigned attr;
+ Namfun_t *fp=0;
+ for(fp=np->nvfun;fp;fp=fp->next)
+ {
+ if(fp->type || (fp->disc && fp->disc->typef &&(*fp->disc->typef)(np,fp)))
+ break;
+ }
+#if 0
+ if(!fp && !nv_isattr(np,~NV_ARRAY))
+ {
+ if(!nv_isattr(np,NV_ARRAY) || nv_aindex(np)>=0)
+ return;
+ }
+#else
+ if(!fp && !nv_isattr(np,~NV_MINIMAL))
+ return;
+#endif
+
+ if ((attr=nv_isattr(np,~NV_NOFREE)) || fp)
+ {
+ if((attr&NV_NOPRINT)==NV_NOPRINT)
+ attr &= ~NV_NOPRINT;
+ if(!attr && !fp)
+ return;
+ if(prefix)
+ sfputr(out,prefix,' ');
+ for(tp = shtab_attributes; *tp->sh_name;tp++)
+ {
+ val = tp->sh_number;
+ mask = val;
+ if(fp && (val&NV_INTEGER))
+ break;
+ /*
+ * the following test is needed to prevent variables
+ * with E attribute from being given the F
+ * attribute as well
+ */
+ if(val==(NV_INTEGER|NV_DOUBLE) && (attr&NV_EXPNOTE))
+ continue;
+ if(val&NV_INTEGER)
+ mask |= NV_DOUBLE;
+ else if(val&NV_HOST)
+ mask = NV_HOST;
+ if((attr&mask)==val)
+ {
+ if(val==NV_ARRAY)
+ {
+ Namarr_t *ap = nv_arrayptr(np);
+ if(array_assoc(ap))
+ {
+ if(tp->sh_name[1]!='A')
+ continue;
+ }
+ else if(tp->sh_name[1]=='A')
+ continue;
+#if 0
+ cp = "associative";
+ else
+ cp = "indexed";
+ if(!prefix)
+ sfputr(out,cp,' ');
+ else if(*cp=='i')
+ tp++;
+#endif
+ }
+ if(prefix)
+ {
+ if(*tp->sh_name=='-')
+ sfprintf(out,"%.2s ",tp->sh_name);
+ }
+ else
+ sfputr(out,tp->sh_name+2,' ');
+ if ((val&(NV_LJUST|NV_RJUST|NV_ZFILL)) && !(val&NV_INTEGER) && val!=NV_HOST)
+ sfprintf(out,"%d ",nv_size(np));
+ }
+ if(val==NV_INTEGER && nv_isattr(np,NV_INTEGER))
+ {
+ if(nv_size(np) != 10)
+ {
+ if(nv_isattr(np, NV_DOUBLE))
+ cp = "precision";
+ else
+ cp = "base";
+ if(!prefix)
+ sfputr(out,cp,' ');
+ sfprintf(out,"%d ",nv_size(np));
+ }
+ break;
+ }
+ }
+ if(fp)
+ outtype(np,fp,out,prefix);
+ if(noname)
+ return;
+ sfputr(out,nv_name(np),'\n');
+ }
+}
+
+struct Walk
+{
+ Sfio_t *out;
+ Dt_t *root;
+ int noscope;
+ int indent;
+};
+
+static void outval(char *name, const char *vname, struct Walk *wp)
+{
+ register Namval_t *np, *nq;
+ register Namfun_t *fp;
+ int isarray=0, associative=0, special=0;
+ if(!(np=nv_open(vname,wp->root,NV_ARRAY|NV_VARNAME|NV_NOADD|NV_NOASSIGN|wp->noscope)))
+ return;
+ if(nv_isarray(np) && *name=='.')
+ special = 1;
+ if(!special && (fp=nv_hasdisc(np,&treedisc)))
+ {
+ if(!wp->out)
+ {
+ fp = nv_stack(np,fp);
+ if(fp = nv_stack(np,NIL(Namfun_t*)))
+ free((void*)fp);
+ np->nvfun = 0;
+ }
+ return;
+ }
+ if(nv_isnull(np))
+ return;
+ if(special || nv_isarray(np))
+ {
+ isarray=1;
+ associative= nv_aindex(np)<0;
+ if(array_elem(nv_arrayptr(np))==0)
+ isarray=2;
+ else
+ nq = nv_putsub(np,NIL(char*),ARRAY_SCAN|(wp->out?ARRAY_NOCHILD:0));
+ }
+ if(!wp->out)
+ {
+ _nv_unset(np,NV_RDONLY);
+ nv_close(np);
+ return;
+ }
+ if(isarray==1 && !nq)
+ return;
+ if(special)
+ {
+ associative = 1;
+ sfnputc(wp->out,'\t',wp->indent);
+ }
+ else
+ {
+ sfnputc(wp->out,'\t',wp->indent);
+ nv_attribute(np,wp->out,"typeset",'=');
+ nv_outname(wp->out,name,-1);
+ sfputc(wp->out,(isarray==2?'\n':'='));
+ if(isarray)
+ {
+ if(isarray==2)
+ return;
+ sfwrite(wp->out,"(\n",2);
+ sfnputc(wp->out,'\t',++wp->indent);
+ }
+ }
+ while(1)
+ {
+ char *fmtq,*ep;
+ if(isarray && associative)
+ {
+ if(!(fmtq = nv_getsub(np)))
+ break;
+ sfprintf(wp->out,"[%s]",sh_fmtq(fmtq));
+ sfputc(wp->out,'=');
+ }
+ if(!(fmtq = sh_fmtq(nv_getval(np))))
+ fmtq = "";
+ else if(!associative && (ep=strchr(fmtq,'=')))
+ {
+ char *qp = strchr(fmtq,'\'');
+ if(!qp || qp>ep)
+ {
+ sfwrite(wp->out,fmtq,ep-fmtq);
+ sfputc(wp->out,'\\');
+ fmtq = ep;
+ }
+ }
+ if(*name=='[' && !isarray)
+ sfprintf(wp->out,"(%s)\n",fmtq);
+ else
+ sfputr(wp->out,fmtq,'\n');
+ if(!nv_nextsub(np))
+ break;
+ sfnputc(wp->out,'\t',wp->indent);
+ }
+ if(isarray && !special)
+ {
+ sfnputc(wp->out,'\t',--wp->indent);
+ sfwrite(wp->out,")\n",2);
+ }
+}
+
+/*
+ * format initialization list given a list of assignments <argp>
+ */
+static char **genvalue(char **argv, const char *prefix, int n, struct Walk *wp)
+{
+ register char *cp,*nextcp,*arg;
+ register int m,r;
+ register Sfio_t *outfile = wp->out;
+ if(n==0)
+ m = strlen(prefix);
+ else if(cp=nextdot(prefix))
+ m = cp-prefix;
+ else
+ m = strlen(prefix)-1;
+ m++;
+ if(outfile)
+ {
+ sfwrite(outfile,"(\n",2);
+ wp->indent++;
+ }
+ for(; arg= *argv; argv++)
+ {
+ cp = arg + n;
+ if(n==0 && cp[m-1]!='.')
+ continue;
+ if(n && cp[m-1]==0)
+ break;
+ if(n==0 || strncmp(arg,prefix-n,m+n)==0)
+ {
+ cp +=m;
+ r = 0;
+ if(*cp=='.')
+ cp++,r++;
+ if(nextcp=nextdot(cp))
+ {
+ if(outfile)
+ {
+ sfnputc(outfile,'\t',wp->indent);
+ nv_outname(outfile,cp,nextcp-cp);
+ sfputc(outfile,'=');
+ }
+ argv = genvalue(argv,cp,n+m+r,wp);
+ if(outfile)
+ sfputc(outfile,'\n');
+ if(*argv)
+ continue;
+ break;
+ }
+ else if(outfile && argv[1] && memcmp(arg,argv[1],r=strlen(arg))==0 && argv[1][r]=='[')
+ {
+ Namval_t *np = nv_open(arg,wp->root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|wp->noscope);
+ if(!np)
+ continue;
+ sfnputc(outfile,'\t',wp->indent);
+ nv_attribute(np,outfile,"typeset",1);
+ nv_close(np);
+ sfputr(outfile,arg+m+(n?n+1:0),'=');
+ argv = genvalue(++argv,cp,cp-arg ,wp);
+ sfputc(outfile,'\n');
+ }
+ else if(outfile && *cp=='[')
+ {
+ sfnputc(outfile,'\t',wp->indent);
+ sfputr(outfile,cp,'=');
+ argv = genvalue(++argv,cp,cp-arg ,wp);
+ sfputc(outfile,'\n');
+ }
+ else
+ outval(cp,arg,wp);
+ }
+ else
+ break;
+ }
+ if(outfile)
+ {
+ int c = prefix[m-1];
+ cp = (char*)prefix;
+ if(c=='.')
+ cp[m-1] = 0;
+ outval(".",prefix-n,wp);
+ if(c=='.')
+ cp[m-1] = c;
+ sfnputc(outfile,'\t',wp->indent-1);
+ sfputc(outfile,')');
+ }
+ return(--argv);
+}
+
+/*
+ * walk the virtual tree and print or delete name-value pairs
+ */
+static char *walk_tree(register Namval_t *np, int dlete)
+{
+ static Sfio_t *out;
+ struct Walk walk;
+ Sfio_t *outfile;
+ int savtop = staktell();
+ char *savptr = stakfreeze(0);
+ register struct argnod *ap=0;
+ struct argnod *arglist=0;
+ char *name,*cp, **argv;
+ char *subscript=0;
+ void *dir;
+ int n=0, noscope=(dlete&NV_NOSCOPE);
+ stakputs(nv_name(np));
+ if(subscript = nv_getsub(np))
+ {
+ stakputc('[');
+ stakputs(subscript);
+ stakputc(']');
+ stakputc('.');
+ }
+ name = stakfreeze(1);
+ dir = nv_diropen(name);
+ if(subscript)
+ name[strlen(name)-1] = 0;
+ while(cp = nv_dirnext(dir))
+ {
+ stakseek(ARGVAL);
+ stakputs(cp);
+ ap = (struct argnod*)stakfreeze(1);
+ ap->argflag = ARG_RAW;
+ ap->argchn.ap = arglist;
+ n++;
+ arglist = ap;
+ }
+ argv = (char**)stakalloc((n+1)*sizeof(char*));
+ argv += n;
+ *argv = 0;
+ for(; ap; ap=ap->argchn.ap)
+ *--argv = ap->argval;
+ nv_dirclose(dir);
+ if(dlete&1)
+ outfile = 0;
+ else if(!(outfile=out))
+ outfile = out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
+ else
+ sfseek(outfile,0L,SEEK_SET);
+ walk.out = outfile;
+ walk.root = sh.last_root;
+ walk.indent = 0;
+ walk.noscope = noscope;
+ genvalue(argv,name,0,&walk);
+ stakset(savptr,savtop);
+ if(!outfile)
+ return((char*)0);
+ sfputc(out,0);
+ return((char*)out->_data);
+}
+
+/*
+ * get discipline for compound initializations
+ */
+char *nv_getvtree(register Namval_t *np, Namfun_t *fp)
+{
+ NOT_USED(fp);
+ if(nv_isattr(np,NV_BINARY) && nv_isattr(np,NV_RAW))
+ return(nv_getv(np,fp));
+ if(nv_isattr(np,NV_ARRAY) && nv_arraychild(np,(Namval_t*)0,0)==np)
+ return(nv_getv(np,fp));
+ return(walk_tree(np,0));
+}
+
+/*
+ * put discipline for compound initializations
+ */
+static void put_tree(register Namval_t *np, const char *val, int flags,Namfun_t *fp)
+{
+ struct Namarray *ap;
+ int nleft = 0;
+ if(!nv_isattr(np,NV_INTEGER))
+ walk_tree(np,(flags&NV_NOSCOPE)|1);
+ nv_putv(np, val, flags,fp);
+ if(nv_isattr(np,NV_INTEGER))
+ return;
+ if(ap= nv_arrayptr(np))
+ nleft = array_elem(ap);
+ if(nleft==0)
+ {
+ fp = nv_stack(np,fp);
+ if(fp = nv_stack(np,NIL(Namfun_t*)))
+ {
+ free((void*)fp);
+ }
+ }
+}
+
+/*
+ * Insert discipline to cause $x to print current tree
+ */
+void nv_setvtree(register Namval_t *np)
+{
+ register Namfun_t *nfp;
+ if(nv_hasdisc(np, &treedisc))
+ return;
+ nfp = newof(NIL(void*),Namfun_t,1,0);
+ nfp->disc = &treedisc;
+ nv_stack(np, nfp);
+}
+