/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * 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 * * * ***********************************************************************/ #pragma prototyped /* * AT&T Labs * */ #define putenv ___putenv #include "defs.h" #include #include "variables.h" #include "path.h" #include "lexstates.h" #include "timeout.h" #include "FEATURE/externs" #include "streval.h" #define NVCACHE 8 /* must be a power of 2 */ #define Empty ((char*)(e_sptbnl+3)) static char *savesub = 0; #if !_lib_pathnative && _lib_uwin_path #define _lib_pathnative 1 extern int uwin_path(const char*, char*, int); size_t pathnative(const char* path, char* buf, size_t siz) { return uwin_path(path, buf, siz); } #endif /* _lib_pathnative */ static void attstore(Namval_t*,void*); #ifndef _ENV_H static void pushnam(Namval_t*,void*); static char *staknam(Namval_t*, char*); #endif static void ltou(char*); static void utol(char*); static void rightjust(char*, int, int); static char *lastdot(char*, int); struct adata { Shell_t *sh; Namval_t *tp; char **argnam; int attsize; char *attval; }; #if SHOPT_TYPEDEF struct sh_type { void *previous; Namval_t **nodes; Namval_t *rp; short numnodes; short maxnodes; }; #endif /*SHOPT_TYPEDEF */ #if NVCACHE struct Namcache { struct Cache_entry { Dt_t *root; char *name; Namval_t *np; Namval_t *last_table; int flags; short size; short len; } entries[NVCACHE]; short index; short ok; }; static struct Namcache nvcache; #endif char nv_local = 0; #ifndef _ENV_H static void(*nullscan)(Namval_t*,void*); #endif #if ( SFIO_VERSION <= 20010201L ) # define _data data #endif #if !SHOPT_MULTIBYTE # define mbchar(p) (*(unsigned char*)p++) #endif /* SHOPT_MULTIBYTE */ /* ======== name value pair routines ======== */ #include "shnodes.h" #include "builtins.h" static char *getbuf(size_t len) { static char *buf; static size_t buflen; if(buflen < len) { if(buflen==0) buf = (char*)malloc(len); else buf = (char*)realloc(buf,len); buflen = len; } return(buf); } #ifdef _ENV_H void sh_envput(Env_t* ep,Namval_t *np) { int offset = staktell(); Namarr_t *ap = nv_arrayptr(np); char *val; if(ap) { if(ap->nelem&ARRAY_UNDEF) nv_putsub(np,"0",0L); else if(!(val=nv_getsub(np)) || strcmp(val,"0")) return; } if(!(val = nv_getval(np))) return; stakputs(nv_name(np)); stakputc('='); stakputs(val); stakseek(offset); env_add(ep,stakptr(offset),ENV_STRDUP); } #endif /* * output variable name in format for re-input */ void nv_outname(Sfio_t *out, char *name, int len) { const char *cp=name, *sp; int c, offset = staktell(); while(sp= strchr(cp,'[')) { if(len>0 && cp+len <= sp) break; sfwrite(out,cp,++sp-cp); stakseek(offset); while(c= *sp++) { if(c==']') break; else if(c=='\\') { if(*sp=='[' || *sp==']' || *sp=='\\') c = *sp++; } stakputc(c); } stakputc(0); sfputr(out,sh_fmtq(stakptr(offset)),-1); if(len>0) { sfputc(out,']'); return; } cp = sp-1; } if(*cp) { if(len>0) sfwrite(out,cp,len); else sfputr(out,cp,-1); } stakseek(offset); } #if SHOPT_TYPEDEF Namval_t *nv_addnode(Namval_t* np, int remove) { register struct sh_type *sp = (struct sh_type*)sh.mktype; register int i; register char *name=0; if(sp->numnodes==0 && !nv_isnull(np) && sh.last_table) { /* could be an redefine */ Dt_t *root = nv_dict(sh.last_table); sp->rp = np; nv_delete(np,root,NV_NOFREE); np = nv_search(sp->rp->nvname,root,NV_ADD); } if(sp->numnodes && memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1)) { name = (sp->nodes[0])->nvname; i = strlen(name); if(memcmp(np->nvname,name,i)) return(np); } if(sp->rp && sp->numnodes) { /* check for a redefine */ if(name && np->nvname[i]=='.' && np->nvname[i+1]=='_' && np->nvname[i+2]==0) sp->rp = 0; else { Dt_t *root = nv_dict(sh.last_table); nv_delete(sp->nodes[0],root,NV_NOFREE); dtinsert(root,sp->rp); errormsg(SH_DICT,ERROR_exit(1),e_redef,sp->nodes[0]->nvname); } } for(i=0; i < sp->numnodes; i++) { if(np == sp->nodes[i]) { if(remove) { while(++i < sp->numnodes) sp->nodes[i-1] = sp->nodes[i]; sp->numnodes--; } return(np); } } if(remove) return(np); if(sp->numnodes==sp->maxnodes) { sp->maxnodes += 20; sp->nodes = (Namval_t**)realloc(sp->nodes,sizeof(Namval_t*)*sp->maxnodes); } sp->nodes[sp->numnodes++] = np; return(np); } #endif /* SHOPT_TYPEDEF */ /* * given a list of assignments, determine is on the list returns a pointer to the argnod on the list or NULL */ struct argnod *nv_onlist(struct argnod *arg, const char *name) { char *cp; int len = strlen(name); for(;arg; arg=arg->argnxt.ap) { if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED|ARG_MESSAGE))) cp = ((struct fornod*)arg->argchn.ap)->fornam; else cp = arg->argval; if(memcmp(cp,name,len)==0 && (cp[len]==0 || cp[len]=='=')) return(arg); } return(0); } /* * Perform parameter assignment for a linked list of parameters * contains attributes for the parameters */ void nv_setlist(register struct argnod *arg,register int flags) { Shell_t *shp = &sh; register char *cp; register Namval_t *np, *mp; char *trap=shp->st.trap[SH_DEBUGTRAP]; char *prefix = shp->prefix; int traceon = (sh_isoption(SH_XTRACE)!=0); int array = (flags&(NV_ARRAY|NV_IARRAY)); Namarr_t *ap; Namval_t node; struct Namref nr; #if SHOPT_TYPEDEF int maketype = flags&NV_TYPE; struct sh_type shtp; if(maketype) { shtp.previous = shp->mktype; shp->mktype=(void*)&shtp; shtp.numnodes=0; shtp.maxnodes = 20; shtp.rp = 0; shtp.nodes =(Namval_t**)malloc(shtp.maxnodes*sizeof(Namval_t*)); } #endif /* SHOPT_TYPEDEF*/ flags &= ~(NV_TYPE|NV_ARRAY|NV_IARRAY); if(sh_isoption(SH_ALLEXPORT)) flags |= NV_EXPORT; if(shp->prefix) { flags &= ~(NV_IDENT|NV_EXPORT); flags |= NV_VARNAME; } for(;arg; arg=arg->argnxt.ap) { shp->used_pos = 0; if(arg->argflag&ARG_MAC) { shp->prefix = 0; cp = sh_mactrim(shp,arg->argval,(flags&NV_NOREF)?-3:-1); shp->prefix = prefix; } else { stakseek(0); if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED|ARG_MESSAGE))) { int flag = (NV_VARNAME|NV_ARRAY|NV_ASSIGN); int sub=0; struct fornod *fp=(struct fornod*)arg->argchn.ap; register Shnode_t *tp=fp->fortre; flag |= (flags&(NV_NOSCOPE|NV_STATIC)); if(arg->argflag&ARG_QUOTED) cp = sh_mactrim(shp,fp->fornam,-1); else cp = fp->fornam; error_info.line = fp->fortyp-shp->st.firstline; if(shp->fn_depth && (Namval_t*)tp->com.comnamp==SYSTYPESET) flag |= NV_NOSCOPE; if(prefix && tp->com.comset && *cp=='[') { shp->prefix = 0; np = nv_open(prefix,shp->var_tree,flag); shp->prefix = prefix; if(np) { if(nv_isvtree(np) && !nv_isarray(np)) { stakputc('.'); stakputs(cp); cp = stakfreeze(1); } nv_close(np); } } np = nv_open(cp,shp->var_tree,flag|NV_ASSIGN); if((flags&NV_STATIC) && !nv_isnull(np)) #if SHOPT_TYPEDEF goto check_type; #else continue; #endif /* SHOPT_TYPEDEF */ if(array) { if(!(arg->argflag&ARG_APPEND)) nv_unset(np); if(array&NV_ARRAY) { nv_setarray(np,nv_associative); } else { nv_onattr(np,NV_ARRAY); } if(tp->tre.tretyp!=TLST && !tp->com.comset && !tp->com.comarg) #if SHOPT_TYPEDEF goto check_type; #else continue; #endif /* SHOPT_TYPEDEF */ } /* check for array assignment */ if(tp->tre.tretyp!=TLST && tp->com.comarg && !tp->com.comset && !((mp=tp->com.comnamp) && nv_isattr(mp,BLT_DCL))) { int argc; Dt_t *last_root = shp->last_root; char **argv = sh_argbuild(shp,&argc,&tp->com,0); shp->last_root = last_root; #if SHOPT_TYPEDEF if(shp->mktype && shp->dot_depth==0 && np==((struct sh_type*)shp->mktype)->nodes[0]) { shp->mktype = 0; errormsg(SH_DICT,ERROR_exit(1),"%s: not a known type name",argv[0]); } #endif /* SHOPT_TYPEDEF */ if(!(arg->argflag&ARG_APPEND)) { if(!nv_isarray(np) || ((ap=nv_arrayptr(np)) && (ap->nelem&ARRAY_MASK))) nv_unset(np); } nv_setvec(np,(arg->argflag&ARG_APPEND),argc,argv); if(traceon || trap) { int n = -1; char *name = nv_name(np); if(arg->argflag&ARG_APPEND) n = '+'; if(trap) sh_debug(shp,trap,name,(char*)0,argv,(arg->argflag&ARG_APPEND)|ARG_ASSIGN); if(traceon) { sh_trace(NIL(char**),0); sfputr(sfstderr,name,n); sfwrite(sfstderr,"=( ",3); while(cp= *argv++) sfputr(sfstderr,sh_fmtq(cp),' '); sfwrite(sfstderr,")\n",2); } } #if SHOPT_TYPEDEF goto check_type; #else continue; #endif /* SHOPT_TYPEDEF */ } if((tp->tre.tretyp&COMMSK)==TFUN) goto skip; if(tp->tre.tretyp==TLST || !tp->com.comset || tp->com.comset->argval[0]!='[') { if(tp->tre.tretyp!=TLST && !tp->com.comnamp && tp->com.comset && tp->com.comset->argval[0]==0 && tp->com.comset->argchn.ap) { if(prefix) cp = stakcopy(nv_name(np)); shp->prefix = cp; if(tp->com.comset->argval[1]=='[') { if((arg->argflag&ARG_APPEND) && (!nv_isarray(np) || (nv_aindex(np)>=0))) nv_unset(np); if(!(array&NV_IARRAY) && !(tp->com.comset->argflag&ARG_MESSAGE)) nv_setarray(np,nv_associative); } nv_setlist(tp->com.comset,flags); shp->prefix = prefix; if(tp->com.comset->argval[1]!='[') nv_setvtree(np); nv_close(np); #if SHOPT_TYPEDEF goto check_type; #else continue; #endif /* SHOPT_TYPEDEF */ } if(*cp!='.' && *cp!='[' && strchr(cp,'[')) { nv_close(np); np = nv_open(cp,shp->var_tree,flag); } if(arg->argflag&ARG_APPEND) { if(nv_isarray(np)) { if((sub=nv_aimax(np)) < 0 && nv_arrayptr(np)) errormsg(SH_DICT,ERROR_exit(1),e_badappend,nv_name(np)); if(sub>=0) sub++; } if(!nv_isnull(np) && np->nvalue.cp!=Empty && !nv_isvtree(np)) sub=1; } else if(np->nvalue.cp && np->nvalue.cp!=Empty && !nv_type(np)) { _nv_unset(np,NV_EXPORT); } } else { if(!(arg->argflag&ARG_APPEND)) _nv_unset(np,NV_EXPORT); if(!sh_isoption(SH_BASH) && !(array&NV_IARRAY) && !nv_isarray(np)) nv_setarray(np,nv_associative); } skip: if(sub>0) { sfprintf(stkstd,"%s[%d]",prefix?nv_name(np):cp,sub); shp->prefix = stakfreeze(1); nv_putsub(np,(char*)0,ARRAY_ADD|ARRAY_FILL|sub); } else if(prefix) shp->prefix = stakcopy(nv_name(np)); else shp->prefix = cp; shp->last_table = 0; memset(&nr,0,sizeof(nr)); memcpy(&node,L_ARGNOD,sizeof(node)); L_ARGNOD->nvalue.nrp = &nr; nr.np = np; nr.root = shp->last_root; nr.table = shp->last_table; L_ARGNOD->nvflag = NV_REF|NV_NOFREE; L_ARGNOD->nvfun = 0; sh_exec(tp,sh_isstate(SH_ERREXIT)); #if SHOPT_TYPEDEF if(!maketype) #endif { L_ARGNOD->nvalue.nrp = node.nvalue.nrp; L_ARGNOD->nvflag = node.nvflag; L_ARGNOD->nvfun = node.nvfun; } shp->prefix = prefix; if(nv_isarray(np) && (mp=nv_opensub(np))) np = mp; while(tp->tre.tretyp==TLST) { if(!tp->lst.lstlef || !tp->lst.lstlef->tre.tretyp==TCOM || tp->lst.lstlef->com.comarg || tp->lst.lstlef->com.comset && tp->lst.lstlef->com.comset->argval[0]!='[') break; tp = tp->lst.lstrit; } if(!nv_isarray(np) && (tp->com.comarg || !tp->com.comset || tp->com.comset->argval[0]!='[')) nv_setvtree(np); #if SHOPT_TYPEDEF goto check_type; #else continue; #endif /* SHOPT_TYPEDEF */ } cp = arg->argval; mp = 0; } np = nv_open(cp,shp->var_tree,flags); if(!np->nvfun && (flags&NV_NOREF)) { if(shp->used_pos) nv_onattr(np,NV_PARAM); else nv_offattr(np,NV_PARAM); } if(traceon || trap) { register char *sp=cp; char *name=nv_name(np); char *sub=0; int append = 0; if(nv_isarray(np)) sub = savesub; if(cp=lastdot(sp,'=')) { if(cp[-1]=='+') append = ARG_APPEND; cp++; } if(traceon) { sh_trace(NIL(char**),0); nv_outname(sfstderr,name,-1); if(sub) sfprintf(sfstderr,"[%s]",sh_fmtq(sub)); if(cp) { if(append) sfputc(sfstderr,'+'); sfprintf(sfstderr,"=%s\n",sh_fmtq(cp)); } } if(trap) { char *av[2]; av[0] = cp; av[1] = 0; sh_debug(shp,trap,name,sub,av,append); } } #if SHOPT_TYPEDEF check_type: if(maketype) { nv_open(shtp.nodes[0]->nvname,shp->var_tree,NV_ASSIGN|NV_VARNAME|NV_NOADD|NV_NOFAIL); np = nv_mktype(shtp.nodes,shtp.numnodes); free((void*)shtp.nodes); shp->mktype = shtp.previous; maketype = 0; shp->prefix = 0; if(nr.np == np) { L_ARGNOD->nvalue.nrp = node.nvalue.nrp; L_ARGNOD->nvflag = node.nvflag; L_ARGNOD->nvfun = node.nvfun; } } #endif /* SHOPT_TYPEDEF */ } } /* * copy the subscript onto the stack */ static void stak_subscript(const char *sub, int last) { register int c; stakputc('['); while(c= *sub++) { if(c=='[' || c==']' || c=='\\') stakputc('\\'); stakputc(c); } stakputc(last); } /* * construct a new name from a prefix and base name on the stack */ static char *copystack(const char *prefix, register const char *name, const char *sub) { register int last=0,offset = staktell(); if(prefix) { stakputs(prefix); if(*stakptr(staktell()-1)=='.') stakseek(staktell()-1); if(*name=='.' && name[1]=='[') last = staktell()+2; if(*name!='[' && *name!='.' && *name!='=' && *name!='+') stakputc('.'); if(*name=='.' && (name[1]=='=' || name[1]==0)) stakputc('.'); } if(last) { stakputs(name); if(sh_checkid(stakptr(last),(char*)0)) stakseek(staktell()-2); } if(sub) stak_subscript(sub,']'); if(!last) stakputs(name); stakputc(0); return(stakptr(offset)); } /* * grow this stack string by bytes and move from cp-1 to end * right by . Returns beginning of string on the stack */ static char *stack_extend(const char *cname, char *cp, int n) { register char *name = (char*)cname; int offset = name - stakptr(0); int m = cp-name; stakseek(strlen(name)+n+1); name = stakptr(offset); cp = name + m; m = strlen(cp)+1; while(m-->0) cp[n+m]=cp[m]; return((char*)name); } Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) { Shell_t *shp = &sh; char *cp=(char*)name, *sp, *xp; register int c; register Namval_t *np=0, *nq=0; Namfun_t *fp=0; long mode, add=0; int copy=1,isref,top=0,noscope=(flags&NV_NOSCOPE); if(root==shp->var_tree) { if(dtvnext(root)) top = 1; else flags &= ~NV_NOSCOPE; } if(!dp->disc) copy = dp->nofree&1; if(*cp=='.') cp++; while(1) { switch(c = *(unsigned char*)(sp = cp)) { case '[': if(flags&NV_NOARRAY) { dp->last = cp; return(np); } cp = nv_endsubscript((Namval_t*)0,sp,0); if(sp==name || sp[-1]=='.') c = *(sp = cp); goto skip; case '.': if(flags&NV_IDENT) return(0); if(root==shp->var_tree) flags &= ~NV_EXPORT; if(!copy && !(flags&NV_NOREF)) { c = sp-name; copy = cp-name; dp->nofree |= 1; name = copystack((const char*)0, name,(const char*)0); cp = (char*)name+copy; sp = (char*)name+c; c = '.'; } skip: case '+': case '=': *sp = 0; case 0: isref = 0; dp->last = cp; mode = (c=='.' || (flags&NV_NOADD))?add:NV_ADD; if((flags&NV_NOSCOPE) && c!='.') mode |= HASH_NOSCOPE; np=0; if(top) { struct Ufunction *rp; if((rp=shp->st.real_fun) && !rp->sdict && (flags&NV_STATIC)) { Dt_t *dp = dtview(shp->var_tree,(Dt_t*)0); rp->sdict = dtopen(&_Nvdisc,Dtoset); dtview(rp->sdict,shp->var_base); dtview(shp->var_tree,rp->sdict); } if(np = nv_search(name,shp->var_tree,0)) { if(shp->var_tree->walk == shp->var_base) { nq = np; if(flags&NV_NOSCOPE) { if(mode==0) root = shp->var_base; else { nv_delete(np,(Dt_t*)0,0); np = 0; } } } else { root = shp->var_tree->walk; flags |= NV_NOSCOPE; noscope = 1; } } if(rp && rp->sdict && (flags&NV_STATIC)) { root = rp->sdict; if(np && shp->var_tree->walk==shp->var_tree) { _nv_unset(np,0); nv_delete(np,shp->var_tree,0); np = 0; } if(!np || shp->var_tree->walk!=root) np = nv_search(name,root,HASH_NOSCOPE|NV_ADD); } } if(np || (np = nv_search(name,root,mode))) { isref = nv_isref(np); if(top) { if(nq==np) flags &= ~NV_NOSCOPE; else if(nq) { if(nv_isnull(np) && c!='.' && (np->nvfun=nv_cover(nq))) np->nvname = nq->nvname; flags |= NV_NOSCOPE; } } else if(add && nv_isnull(np) && c=='.' && cp[1]!='.') nv_setvtree(np); } if(c) *sp = c; top = 0; if(isref) { char *sub=0; #if NVCACHE nvcache.ok = 0; #endif if(c=='.') /* don't optimize */ shp->argaddr = 0; else if((flags&NV_NOREF) && (c!='[' || *cp!='.')) { if(c && !(flags&NV_NOADD)) nv_unref(np); return(np); } while(nv_isref(np) && np->nvalue.cp) { root = nv_reftree(np); shp->last_root = root; shp->last_table = nv_reftable(np); sub = nv_refsub(np); np = nv_refnode(np); if(sub && c!='.') nv_putsub(np,sub,0L); flags |= NV_NOSCOPE; noscope = 1; } if(nv_isref(np) && (c=='[' || c=='.' || !(flags&NV_ASSIGN))) errormsg(SH_DICT,ERROR_exit(1),e_noref,nv_name(np)); if(sub && c==0) return(np); if(np==nq) flags &= ~(noscope?0:NV_NOSCOPE); else if(c) { c = (cp-sp); copy = strlen(cp=nv_name(np)); dp->nofree |= 1; name = copystack(cp,sp,sub); sp = (char*)name + copy; cp = sp+c; c = *sp; if(!noscope) flags &= ~NV_NOSCOPE; } flags |= NV_NOREF; } shp->last_root = root; if(cp[1]=='.') cp++; if(c=='.' && (cp[1]==0 || cp[1]=='=' || cp[1]=='+')) { nv_local = 1; return(np); } if(cp[-1]=='.') cp--; do { if(!np) { if(!nq && *sp=='[' && *cp==0 && cp[-1]==']') { /* * for backward compatibility * evaluate subscript for * possible side effects */ cp[-1] = 0; sh_arith(sp+1); cp[-1] = ']'; } return(np); } if(c=='[' || (c=='.' && nv_isarray(np))) { char *sub=0; int n = 0; if(c=='[') { n = mode|nv_isarray(np); if(!mode && (flags&NV_ARRAY) && ((c=sp[1])=='*' || c=='@') && sp[2]==']') { /* not implemented yet */ dp->last = cp; return(np); } if((n&NV_ADD)&&(flags&NV_ARRAY)) n |= ARRAY_FILL; if(flags&NV_ASSIGN) n |= NV_ADD; cp = nv_endsubscript(np,sp,n|(flags&NV_ASSIGN)); } else cp = sp; if((c = *cp)=='.' || (c=='[' && nv_isarray(np)) || (n&ARRAY_FILL) || (flags&NV_ARRAY)) { int m = cp-sp; sub = m?nv_getsub(np):0; if(!sub) { if(m && !(n&NV_ADD)) return(0); sub = "0"; } n = strlen(sub)+2; if(!copy) { copy = cp-name; dp->nofree |= 1; name = copystack((const char*)0, name,(const char*)0); cp = (char*)name+copy; sp = cp-m; } if(n <= m) { if(n) { memcpy(sp+1,sub,n-2); sp[n-1] = ']'; } if(n < m) cp=strcpy(sp+n,cp); } else { int r = n-m; m = sp-name; name = stack_extend(name, cp-1, r); sp = (char*)name + m; *sp = '['; memcpy(sp+1,sub,n-2); sp[n-1] = ']'; cp = sp+n; } } else if(c==0 && mode && (n=nv_aindex(np))>0) nv_putsub(np,(char*)0,n); else if(n==0 && (c==0 || (c=='[' && !nv_isarray(np)))) { /* subscript must be 0*/ cp[-1] = 0; n = sh_arith(sp+1); cp[-1] = ']'; if(n) return(0); if(c) sp = cp; } dp->last = cp; if(nv_isarray(np) && (c=='[' || c=='.' || (flags&NV_ARRAY))) { sp = cp; if(!(nq = nv_opensub(np))) { Namarr_t *ap = nv_arrayptr(np); if(!sub && (flags&NV_NOADD)) return(0); n = mode|((flags&NV_NOADD)?0:NV_ADD); if(!ap && (n&NV_ADD)) { nv_putsub(np,sub,ARRAY_FILL); ap = nv_arrayptr(np); } if(n && ap && !ap->table) ap->table = dtopen(&_Nvdisc,Dtoset); if(ap && ap->table && (nq=nv_search(sub,ap->table,n))) nq->nvenv = (char*)np; if(nq && nv_isnull(nq)) nq = nv_arraychild(np,nq,c); } if(nq) { if(c=='.' && !nv_isvtree(nq)) { if(flags&NV_NOADD) return(0); nv_setvtree(nq); } np = nq; } else if(memcmp(cp,"[0]",3)) return(nq); else { /* ignore [0] */ dp->last = cp += 3; c = *cp; } } } else if(nv_isarray(np)) { if(c==0 && (flags&NV_MOVE)) return(np); nv_putsub(np,NIL(char*),ARRAY_UNDEF); } if(c=='.' && (fp=np->nvfun)) { for(; fp; fp=fp->next) { if(fp->disc && fp->disc->createf) break; } if(fp) { if((nq = (*fp->disc->createf)(np,cp+1,flags,fp)) == np) { add = NV_ADD; break; } else if(np=nq) { if((c = *(sp=cp=dp->last=fp->last))==0) { if(nv_isarray(np) && sp[-1]!=']') nv_putsub(np,NIL(char*),ARRAY_UNDEF); return(np); } } } } } while(c=='['); if(c!='.' || cp[1]=='.') return(np); cp++; break; default: dp->last = cp; if((c = mbchar(cp)) && !isaletter(c)) return(np); while(xp=cp, c=mbchar(cp), isaname(c)); cp = xp; } } return(np); } /* * delete the node from the dictionary and clear from the cache * if is NULL, only the cache is cleared * if flags does not contain NV_NOFREE, the node is freed */ void nv_delete(Namval_t* np, Dt_t *root, int flags) { #if NVCACHE register int c; struct Cache_entry *xp; for(c=0,xp=nvcache.entries ; c < NVCACHE; xp= &nvcache.entries[++c]) { if(xp->np==np) xp->root = 0; } #endif if(root) { if(dtdelete(root,np)) { if(!(flags&NV_NOFREE) && ((flags&NV_FUNCTION) || !nv_subsaved(np))) free((void*)np); } #if 0 else { sfprintf(sfstderr,"%s not deleted\n",nv_name(np)); sfsync(sfstderr); } #endif } } /* * Put into associative memory. * If & NV_ARRAY then follow array to next subscript * If & NV_NOARRAY then subscript is not allowed * If & NV_NOSCOPE then use the current scope only * If & NV_ASSIGN then assignment is allowed * If & NV_IDENT then name must be an identifier * If & NV_VARNAME then name must be a valid variable name * If & NV_NOADD then node will not be added if not found * If & NV_NOREF then don't follow reference * If & NV_NOFAIL then don't generate an error message on failure * If & NV_STATIC then unset before an assignment * SH_INIT is only set while initializing the environment */ Namval_t *nv_open(const char *name, Dt_t *root, int flags) { Shell_t *shp = &sh; register char *cp=(char*)name; register int c; register Namval_t *np; Namfun_t fun; int append=0; const char *msg = e_varname; char *fname = 0; int offset = staktell(); Dt_t *funroot; #if NVCACHE struct Cache_entry *xp; #endif sh_stats(STAT_NVOPEN); memset(&fun,0,sizeof(fun)); shp->last_table = shp->namespace; if(!root) root = shp->var_tree; shp->last_root = root; if(root==shp->fun_tree) { flags |= NV_NOREF; msg = e_badfun; if((np=shp->namespace) || strchr(name,'.')) { name = cp = copystack(np?nv_name(np):0,name,(const char*)0); fname = strrchr(cp,'.'); *fname = 0; fun.nofree |= 1; flags &= ~NV_IDENT; funroot = root; root = shp->var_tree; } } else if(!(flags&(NV_IDENT|NV_VARNAME|NV_ASSIGN))) { long mode = ((flags&NV_NOADD)?0:NV_ADD); if(flags&NV_NOSCOPE) mode |= HASH_SCOPE|HASH_NOSCOPE; np = nv_search(name,root,mode); if(np && !(flags&NV_REF)) { while(nv_isref(np)) { shp->last_table = nv_reftable(np); np = nv_refnode(np); } } return(np); } else if(shp->prefix && (flags&NV_ASSIGN)) { name = cp = copystack(shp->prefix,name,(const char*)0); fun.nofree |= 1; } c = *(unsigned char*)cp; if(root==shp->alias_tree) { msg = e_aliname; while((c= *(unsigned char*)cp++) && (c!='=') && (c!='/') && (c>=0x200 || !(c=sh_lexstates[ST_NORM][c]) || c==S_EPAT || c==S_COLON)); if(shp->subshell && c=='=') root = sh_subaliastree(1); if(c= *--cp) *cp = 0; np = nv_search(name, root, (flags&NV_NOADD)?0:NV_ADD); if(c) *cp = c; goto skip; } else if(flags&NV_IDENT) msg = e_ident; else if(c=='.') { c = *++cp; flags |= NV_NOREF; if(root==shp->var_tree) root = shp->var_base; shp->last_table = 0; } if(c= !isaletter(c)) goto skip; #if NVCACHE for(c=0,xp=nvcache.entries ; c < NVCACHE; xp= &nvcache.entries[++c]) { if(xp->root!=root) continue; if(*name==*xp->name && (flags&(NV_ARRAY|NV_NOSCOPE))==xp->flags && memcmp(xp->name,name,xp->len)==0 && (name[xp->len]==0 || name[xp->len]=='=' || name[xp->len]=='+')) { sh_stats(STAT_NVHITS); np = xp->np; cp = (char*)name+xp->len; if(nv_isarray(np)) nv_putsub(np,NIL(char*),ARRAY_UNDEF); shp->last_table = xp->last_table; goto nocache; } } nvcache.ok = 1; #endif np = nv_create(name, root, flags, &fun); cp = fun.last; #if NVCACHE if(np && nvcache.ok && cp[-1]!=']') { xp = &nvcache.entries[nvcache.index]; if(*cp) { char *sp = strchr(name,*cp); if(!sp) goto nocache; xp->len = sp-name; } else xp->len = strlen(name); c = roundof(xp->len+1,32); if(c > xp->size) { if(xp->size==0) xp->name = malloc(c); else xp->name = realloc(xp->name,c); xp->size = c; } memcpy(xp->name,name,xp->len); xp->name[xp->len] = 0; xp->root = root; xp->np = np; xp->last_table = shp->last_table; xp->flags = (flags&(NV_ARRAY|NV_NOSCOPE)); nvcache.index = (nvcache.index+1)&(NVCACHE-1); } nocache: nvcache.ok = 0; #endif if(fname) { c = ((flags&NV_NOSCOPE)?HASH_NOSCOPE:0)|((flags&NV_NOADD)?0:NV_ADD); *fname = '.'; np = nv_search(name, funroot, c); *fname = 0; } else { if(*cp=='.' && cp[1]=='.') { append |= NV_NODISC; cp+=2; } if(*cp=='+' && cp[1]=='=') { append |= NV_APPEND; cp++; } } c = *cp; skip: #if SHOPT_TYPEDEF if(np && shp->mktype) np = nv_addnode(np,0); #endif /* SHOPT_TYPEDEF */ if(c=='=' && np && (flags&NV_ASSIGN)) { cp++; if(sh_isstate(SH_INIT)) { nv_putval(np, cp, NV_RDONLY); if(np==PWDNOD) nv_onattr(np,NV_TAGGED); } else { char *sub=0, *prefix= shp->prefix; int isref; shp->prefix = 0; if((flags&NV_STATIC) && !shp->mktype) { if(!nv_isnull(np)) return(np); } isref = nv_isref(np); if(sh_isoption(SH_XTRACE) && nv_isarray(np)) sub = nv_getsub(np); c = msg==e_aliname? 0: (append | (flags&NV_EXPORT)); if(isref) nv_offattr(np,NV_REF); nv_putval(np, cp, c); if(isref) nv_setref(np,(Dt_t*)0,NV_VARNAME); savesub = sub; shp->prefix = prefix; } nv_onattr(np, flags&NV_ATTRIBUTES); } else if(c) { if(flags&NV_NOFAIL) return(0); if(c=='.') msg = e_noparent; else if(c=='[') msg = e_noarray; errormsg(SH_DICT,ERROR_exit(1),msg,name); } if(fun.nofree&1) stakseek(offset); return(np); } #if SHOPT_MULTIBYTE static int ja_size(char*, int, int); static void ja_restore(void); static char *savep; static char savechars[8+1]; #endif /* SHOPT_MULTIBYTE */ /* * put value into name-value node . * If is an array, then the element given by the * current index is assigned to. * If contains NV_RDONLY, readonly attribute is ignored * If contains NV_INTEGER, string is a pointer to a number * If contains NV_NOFREE, previous value is freed, and * becomes value of node and becomes attributes */ void nv_putval(register Namval_t *np, const char *string, int flags) { register const char *sp=string; register union Value *up; register char *cp; register int size = 0; register int dot; #ifdef _ENV_H int was_local = nv_local; #endif union Value u; if(!(flags&NV_RDONLY) && nv_isattr (np, NV_RDONLY)) errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np)); /* The following could cause the shell to fork if assignment * would cause a side effect */ sh.argaddr = 0; if(sh.subshell && !nv_local) np = sh_assignok(np,1); if(np->nvfun && np->nvfun->disc && !(flags&NV_NODISC) && !nv_isattr(np,NV_REF)) { /* This function contains disc */ if(!nv_local) { nv_local=1; nv_putv(np,sp,flags,np->nvfun); #ifdef _ENV_H if(sp && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT))) sh_envput(sh.env,np); #endif return; } /* called from disc, assign the actual value */ } flags &= ~NV_NODISC; nv_local=0; if(flags&(NV_NOREF|NV_NOFREE)) { if(np->nvalue.cp && np->nvalue.cp!=sp && !nv_isattr(np,NV_NOFREE)) free((void*)np->nvalue.cp); np->nvalue.cp = (char*)sp; nv_setattr(np,(flags&~NV_RDONLY)|NV_NOFREE); return; } up= &np->nvalue; if(nv_isattr(np,NV_INT16P) == NV_INT16) { if(!np->nvalue.up || !nv_isarray(np)) { up = &u; up->up = &np->nvalue; } } else if(np->nvalue.up && nv_isarray(np) && nv_arrayptr(np)) up = np->nvalue.up; if(up && up->cp==Empty) up->cp = 0; if(nv_isattr(np,NV_EXPORT)) nv_offattr(np,NV_IMPORT); if(nv_isattr (np, NV_INTEGER)) { if(nv_isattr(np, NV_DOUBLE) == NV_DOUBLE) { if(nv_isattr(np, NV_LONG) && sizeof(double)ldp) up->ldp = new_of(Sfdouble_t,0); else if(flags&NV_APPEND) old = *(up->ldp); *(up->ldp) = ld+old; } else { double d,od=0; if(flags&NV_INTEGER) { if(flags&NV_LONG) d = (double)(*(Sfdouble_t*)sp); else if(flags&NV_SHORT) d = (double)(*(float*)sp); else d = *(double*)sp; } else d = sh_arith(sp); if(!up->dp) up->dp = new_of(double,0); else if(flags&NV_APPEND) od = *(up->dp); *(up->dp) = d+od; } } else { if(nv_isattr(np, NV_LONG) && sizeof(int32_t)llp) up->llp = new_of(Sflong_t,0); else if(flags&NV_APPEND) oll = *(up->llp); *(up->llp) = ll+oll; } else { int32_t l=0,ol=0; if(flags&NV_INTEGER) { if((flags&NV_DOUBLE) == NV_DOUBLE) { Sflong_t ll; if(flags&NV_LONG) ll = *((Sfdouble_t*)sp); else if(flags&NV_SHORT) ll = *((float*)sp); else ll = *((double*)sp); l = (int32_t)ll; } else if(nv_isattr(np,NV_UNSIGN)) { if(flags&NV_LONG) l = *((Sfulong_t*)sp); else if(flags&NV_SHORT) l = *((uint16_t*)sp); else l = *(uint32_t*)sp; } else { if(flags&NV_LONG) l = *((Sflong_t*)sp); else if(flags&NV_SHORT) l = *((int16_t*)sp); else l = *(int32_t*)sp; } } else if(sp) { Sfdouble_t ld = sh_arith(sp); if(ld<0) l = (int32_t)ld; else l = (uint32_t)ld; } if(nv_size(np) <= 1) nv_setsize(np,10); if(nv_isattr (np, NV_SHORT)) { int16_t s=0; if(flags&NV_APPEND) s = *up->sp; *(up->sp) = s+(int16_t)l; nv_onattr(np,NV_NOFREE); } else { if(!up->lp) up->lp = new_of(int32_t,0); else if(flags&NV_APPEND) ol = *(up->lp); *(up->lp) = l+ol; } } } } else { const char *tofree=0; int offset; #if _lib_pathnative char buff[PATH_MAX]; #endif /* _lib_pathnative */ if(flags&NV_INTEGER) { if((flags&NV_DOUBLE)==NV_DOUBLE) { if(flags&NV_LONG) sfprintf(sh.strbuf,"%.*Lg",LDBL_DIG,*((Sfdouble_t*)sp)); else sfprintf(sh.strbuf,"%.*g",DBL_DIG,*((double*)sp)); } else if(flags&NV_UNSIGN) { if(flags&NV_LONG) sfprintf(sh.strbuf,"%I*lu",sizeof(Sfulong_t),*((Sfulong_t*)sp)); else sfprintf(sh.strbuf,"%lu",(unsigned long)((flags&NV_SHORT)?*((uint16_t*)sp):*((uint32_t*)sp))); } else { if(flags&NV_LONG) sfprintf(sh.strbuf,"%I*ld",sizeof(Sflong_t),*((Sflong_t*)sp)); else sfprintf(sh.strbuf,"%ld",(long)((flags&NV_SHORT)?*((int16_t*)sp):*((int32_t*)sp))); } sp = sfstruse(sh.strbuf); } if(nv_isattr(np, NV_HOST|NV_INTEGER)==NV_HOST && sp) { #ifdef _lib_pathnative /* * return the host file name given the UNIX name */ pathnative(sp,buff,sizeof(buff)); if(buff[1]==':' && buff[2]=='/') { buff[2] = '\\'; if(*buff>='A' && *buff<='Z') *buff += 'a'-'A'; } sp = buff; #else ; #endif /* _lib_pathnative */ } else if((nv_isattr(np, NV_RJUST|NV_ZFILL|NV_LJUST)) && sp) { for(;*sp == ' '|| *sp=='\t';sp++); if((nv_isattr(np,NV_ZFILL)) && (nv_isattr(np,NV_LJUST))) for(;*sp=='0';sp++); size = nv_size(np); #if SHOPT_MULTIBYTE if(size) size = ja_size((char*)sp,size,nv_isattr(np,NV_RJUST|NV_ZFILL)); #endif /* SHOPT_MULTIBYTE */ } if(!up->cp) flags &= ~NV_APPEND; if((flags&NV_APPEND) && !nv_isattr(np,NV_BINARY)) { offset = staktell(); stakputs(up->cp); stakputs(sp); stakputc(0); sp = stakptr(offset); } if(!nv_isattr(np, NV_NOFREE)) { /* delay free in case points into free region */ tofree = up->cp; } if(nv_isattr(np,NV_BINARY) && !(flags&NV_RAW)) tofree = 0; if(nv_isattr(np,NV_LJUST|NV_RJUST)) tofree = 0; if (sp) { dot = strlen(sp); #if (_AST_VERSION>=20030127L) if(nv_isattr(np,NV_BINARY)) { int oldsize = (flags&NV_APPEND)?nv_size(np):0; if(flags&NV_RAW) { if(tofree) { free((void*)tofree); nv_offattr(np,NV_NOFREE); } up->cp = sp; return; } size = 0; if(nv_isattr(np,NV_ZFILL)) size = nv_size(np); if(size==0) size = oldsize + (3*dot/4); cp = (char*)malloc(size+1); nv_offattr(np,NV_NOFREE); if(oldsize) memcpy((void*)cp,(void*)up->cp,oldsize); up->cp = cp; if(size <= oldsize) return; dot = base64decode(sp,dot, (void**)0, cp+oldsize, size-oldsize,(void**)0); dot += oldsize; if(!nv_isattr(np,NV_ZFILL) || nv_size(np)==0) nv_setsize(np,dot); else if(nv_isattr(np,NV_ZFILL) && (size>dot)) memset((void*)&cp[dot],0,size-dot); return; } else #endif if(size==0 && nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL)) nv_setsize(np,size=dot); else if(size > dot) dot = size; else if(nv_isattr(np,NV_LJUST) && dot>size) dot = size; if(size==0 || tofree || !(cp=(char*)up->cp)) { cp = (char*)malloc(((unsigned)dot+1)); cp[dot] = 0; nv_offattr(np,NV_NOFREE); } } else cp = 0; up->cp = cp; if(sp) { int c = cp[dot]; memcpy(cp,sp,dot); cp[dot]=0; if(nv_isattr(np, NV_LTOU)) ltou(cp); else if(nv_isattr (np, NV_UTOL)) utol(cp); cp[dot] = c; if(nv_isattr(np, NV_RJUST) && nv_isattr(np, NV_ZFILL)) rightjust(cp,size,'0'); else if(nv_isattr(np, NV_RJUST)) rightjust(cp,size,' '); else if(nv_isattr(np, NV_LJUST)) { register char *dp; dp = strlen (cp) + cp; cp = cp+size; for (; dp < cp; *dp++ = ' '); } #if SHOPT_MULTIBYTE /* restore original string */ if(savep) ja_restore(); #endif /* SHOPT_MULTIBYTE */ } if(flags&NV_APPEND) stakseek(offset); if(tofree && tofree!=Empty) free((void*)tofree); } #ifdef _ENV_H if(!was_local && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT))) sh_envput(sh.env,np); #endif return; } /* * * Right-justify so that it contains no more than * characters. If contains fewer than * characters, left-pad with . Trailing blanks * in will be ignored. * * If the leftmost digit in is not a digit, * will default to a blank. */ static void rightjust(char *str, int size, int fill) { register int n; register char *cp,*sp; n = strlen(str); /* ignore trailing blanks */ for(cp=str+n;n && *--cp == ' ';n--); if (n == size) return; if(n > size) { *(str+n) = 0; for (sp = str, cp = str+n-size; sp <= str+size; *sp++ = *cp++); return; } else *(sp = str+size) = 0; if (n == 0) { while (sp > str) *--sp = ' '; return; } while(n--) { sp--; *sp = *cp--; } if(!isdigit(*str)) fill = ' '; while(sp>str) *--sp = fill; return; } #if SHOPT_MULTIBYTE /* * handle left and right justified fields for multi-byte chars * given physical size, return a logical size which reflects the * screen width of multi-byte characters * Multi-width characters replaced by spaces if they cross the boundary * is non-zero for right justified fields */ static int ja_size(char *str,int size,int type) { register char *cp = str; register int c, n=size; register int outsize; register char *oldcp=cp; int oldn; wchar_t w; while(*cp) { oldn = n; w = mbchar(cp); outsize = mbwidth(w); size -= outsize; c = cp-oldcp; n += (c-outsize); oldcp = cp; if(size<=0 && type==0) break; } /* check for right justified fields that need truncating */ if(size <0) { if(type==0) { /* left justified and character crosses field boundary */ n = oldn; /* save boundary char and replace with spaces */ size = c; savechars[size] = 0; while(size--) { savechars[size] = cp[size]; cp[size] = ' '; } savep = cp; } size = -size; if(type) n -= (ja_size(str,size,0)-size); } return(n); } static void ja_restore(void) { register char *cp = savechars; while(*cp) *savep++ = *cp++; savep = 0; } #endif /* SHOPT_MULTIBYTE */ #ifndef _ENV_H static char *staknam(register Namval_t *np, char *value) { register char *p,*q; q = stakalloc(strlen(nv_name(np))+(value?strlen(value):0)+2); p=strcopy(q,nv_name(np)); if(value) { *p++ = '='; strcpy(p,value); } return(q); } #endif /* * put the name and attribute into value of attributes variable */ #ifdef _ENV_H static void attstore(register Namval_t *np, void *data) { register int flag, c = ' '; NOT_USED(data); if(!(nv_isattr(np,NV_EXPORT))) return; flag = nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER); stakputc('='); if((flag&NV_DOUBLE) == NV_DOUBLE) { /* export doubles as integers for ksh88 compatibility */ stakputc(c+NV_INTEGER|(flag&~(NV_DOUBLE|NV_EXPNOTE))); } else { stakputc(c+flag); if(flag&NV_INTEGER) c += nv_size(np); } stakputc(c); stakputs(nv_name(np)); } #else static void attstore(register Namval_t *np, void *data) { register int flag = np->nvflag; register struct adata *ap = (struct adata*)data; ap->sh = &sh; ap->tp = 0; if(!(flag&NV_EXPORT) || (flag&NV_FUNCT)) return; flag &= (NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER); *ap->attval++ = '='; if((flag&NV_DOUBLE) == NV_DOUBLE) { /* export doubles as integers for ksh88 compatibility */ *ap->attval++ = ' '+ NV_INTEGER|(flag&~(NV_DOUBLE|NV_EXPNOTE)); *ap->attval = ' '; } else { *ap->attval++ = ' '+flag; if(flag&NV_INTEGER) *ap->attval = ' ' + nv_size(np); else *ap->attval = ' '; } ap->attval = strcopy(++ap->attval,nv_name(np)); } #endif #ifndef _ENV_H static void pushnam(Namval_t *np, void *data) { register char *value; register struct adata *ap = (struct adata*)data; ap->sh = &sh; ap->tp = 0; if(nv_isattr(np,NV_IMPORT)) { if(np->nvenv) *ap->argnam++ = np->nvenv; } else if(value=nv_getval(np)) *ap->argnam++ = staknam(np,value); if(nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER)) ap->attsize += (strlen(nv_name(np))+4); } #endif /* * Generate the environment list for the child. */ #ifdef _ENV_H char **sh_envgen(void) { int offset,tell; register char **er; env_delete(sh.env,"_"); er = env_get(sh.env); offset = staktell(); stakputs(e_envmarker); tell = staktell(); nv_scan(sh.var_tree, attstore,(void*)0,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER)); if(tell ==staktell()) stakseek(offset); else *--er = stakfreeze(1)+offset; return(er); } #else char **sh_envgen(void) { register char **er; register int namec; register char *cp; struct adata data; Shell_t *shp = sh_getinterp(); data.sh = shp; data.tp = 0; /* L_ARGNOD gets generated automatically as full path name of command */ nv_offattr(L_ARGNOD,NV_EXPORT); data.attsize = 6; namec = nv_scan(shp->var_tree,nullscan,(void*)0,NV_EXPORT,NV_EXPORT); namec += shp->nenv; er = (char**)stakalloc((namec+4)*sizeof(char*)); data.argnam = (er+=2) + shp->nenv; if(shp->nenv) memcpy((void*)er,environ,shp->nenv*sizeof(char*)); nv_scan(shp->var_tree, pushnam,&data,NV_EXPORT, NV_EXPORT); *data.argnam = (char*)stakalloc(data.attsize); cp = data.attval = strcopy(*data.argnam,e_envmarker); nv_scan(shp->var_tree, attstore,&data,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER)); *data.attval = 0; if(cp!=data.attval) data.argnam++; *data.argnam = 0; return(er); } #endif struct scan { void (*scanfn)(Namval_t*, void*); int scanmask; int scanflags; int scancount; void *scandata; }; static int scanfilter(Dt_t *dict, void *arg, void *data) { register Namval_t *np = (Namval_t*)arg; register int k=np->nvflag; register struct scan *sp = (struct scan*)data; register struct adata *tp = (struct adata*)sp->scandata; NOT_USED(dict); #if SHOPT_TYPEDEF if(tp && tp->tp && nv_type(np)!=tp->tp) return(0); #endif /*SHOPT_TYPEDEF */ if(sp->scanmask?(k&sp->scanmask)==sp->scanflags:(!sp->scanflags || (k&sp->scanflags))) { if(!np->nvalue.cp && !np->nvfun && !nv_isattr(np,~NV_DEFAULT)) return(0); if(sp->scanfn) { if(nv_isarray(np)) nv_putsub(np,NIL(char*),0L); (*sp->scanfn)(np,sp->scandata); } sp->scancount++; } return(0); } /* * Walk through the name-value pairs * if is non-zero, then only nodes with (nvflags&mask)==flags * are visited * If is zero, and non-zero, then nodes with one or * more of is visited * If and are zero, then all nodes are visted */ int nv_scan(Dt_t *root, void (*fn)(Namval_t*,void*), void *data,int mask, int flags) { Dt_t *base=0; struct scan sdata; int (*hashfn)(Dt_t*, void*, void*); sdata.scanmask = mask; sdata.scanflags = flags&~NV_NOSCOPE; sdata.scanfn = fn; sdata.scancount = 0; sdata.scandata = data; hashfn = scanfilter; if(flags&NV_NOSCOPE) base = dtview((Dt_t*)root,0); dtwalk(root, hashfn,&sdata); if(base) dtview((Dt_t*)root,base); return(sdata.scancount); } /* * create a new environment scope */ void sh_scope(Shell_t *shp, struct argnod *envlist, int fun) { register Dt_t *newscope, *newroot=shp->var_base; struct Ufunction *rp; newscope = dtopen(&_Nvdisc,Dtoset); if(envlist) { dtview(newscope,(Dt_t*)shp->var_tree); shp->var_tree = newscope; nv_setlist(envlist,NV_EXPORT|NV_NOSCOPE|NV_IDENT|NV_ASSIGN); if(!fun) return; shp->var_tree = dtview(newscope,0); } if((rp=shp->st.real_fun) && rp->sdict) { dtview(rp->sdict,newroot); newroot = rp->sdict; } dtview(newscope,(Dt_t*)newroot); shp->var_tree = newscope; } /* * Remove freeable local space associated with the nvalue field * of nnod. This includes any strings representing the value(s) of the * node, as well as its dope vector, if it is an array. */ void sh_envnolocal (register Namval_t *np, void *data) { char *cp=0; NOT_USED(data); if(nv_isattr(np,NV_EXPORT) && nv_isarray(np)) { nv_putsub(np,NIL(char*),0); if(cp = nv_getval(np)) cp = strdup(cp); } if(nv_isattr(np,NV_EXPORT|NV_NOFREE)) { if(nv_isref(np)) { nv_offattr(np,NV_NOFREE|NV_REF); free((void*)np->nvalue.nrp); np->nvalue.cp = 0; } if(!cp) return; } if(nv_isarray(np)) nv_putsub(np,NIL(char*),ARRAY_UNDEF); _nv_unset(np,NV_RDONLY); nv_setattr(np,0); if(cp) { nv_putval(np,cp,0); free((void*)cp); } } /* * Currently this is a dummy, but someday will be needed * for reference counting */ void nv_close(Namval_t *np) { NOT_USED(np); } static void table_unset(Shell_t *shp, register Dt_t *root, int flags, Dt_t *oroot) { register Namval_t *np,*nq, *npnext; for(np=(Namval_t*)dtfirst(root);np;np=npnext) { if(nv_isref(np)) nv_unref(np); if(nq=dtsearch(oroot,np)) { if(nv_cover(nq)) { int subshell = shp->subshell; shp->subshell = 0; if(nv_isattr(nq, NV_INTEGER)) { Sfdouble_t d = nv_getnum(nq); nv_putval(nq,(char*)&d,NV_LDOUBLE); } else nv_putval(nq, nv_getval(nq), NV_RDONLY); shp->subshell = subshell; np->nvfun = 0; } #ifdef _ENV_H if(nv_isattr(nq,NV_EXPORT)) sh_envput(shp->env,nq); #endif } npnext = (Namval_t*)dtnext(root,np); shp->last_root = root; shp->last_table = 0; if(nv_isvtree(np)) { int len = strlen(np->nvname); while((nq=npnext) && memcmp(np->nvname,nq->nvname,len)==0 && nq->nvname[len]=='.') { npnext = (Namval_t*)dtnext(root,nq); _nv_unset(nq,flags); nv_delete(nq,root,0); } } _nv_unset(np,flags); nv_delete(np,root,0); } } /* * * Set the value of to 0, and nullify any attributes * that may have had. Free any freeable space occupied * by the value of . If denotes an array member, it * will retain its attributes. * can contain NV_RDONLY to override the readonly attribute * being cleared. * can contain NV_EXPORT to override preserve nvenv */ void _nv_unset(register Namval_t *np,int flags) { Shell_t *shp = &sh; register union Value *up; if(!(flags&NV_RDONLY) && nv_isattr (np,NV_RDONLY)) errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np)); if(is_afunction(np) && np->nvalue.ip) { register struct slnod *slp = (struct slnod*)(np->nvenv); if(slp && !nv_isattr(np,NV_NOFREE)) { struct Ufunction *rq,*rp = np->nvalue.rp; /* free function definition */ register char *name=nv_name(np),*cp= strrchr(name,'.'); if(cp) { Namval_t *npv; *cp = 0; npv = nv_open(name,shp->var_tree,NV_NOARRAY|NV_VARNAME|NV_NOADD); *cp++ = '.'; if(npv) nv_setdisc(npv,cp,NIL(Namval_t*),(Namfun_t*)npv); } if(rp->fname && shp->fpathdict && (rq = (struct Ufunction*)nv_search(rp->fname,shp->fpathdict,0))) { do { if(rq->np != np) continue; dtdelete(shp->fpathdict,rq); break; } while(rq = (struct Ufunction*)dtnext(shp->fpathdict,rq)); } if(rp->sdict) { Namval_t *mp, *nq; for(mp=(Namval_t*)dtfirst(rp->sdict);mp;mp=nq) { nq = dtnext(rp->sdict,mp); _nv_unset(mp,NV_RDONLY); nv_delete(mp,rp->sdict,0); } dtclose(rp->sdict); } stakdelete(slp->slptr); free((void*)np->nvalue.ip); np->nvalue.ip = 0; } goto done; } if(shp->subshell && !nv_isnull(np)) np = sh_assignok(np,0); nv_offattr(np,NV_NODISC); if(np->nvfun && !nv_isref(np)) { /* This function contains disc */ if(!nv_local) { nv_local=1; nv_putv(np,NIL(char*),flags,np->nvfun); nv_local=0; return; } /* called from disc, assign the actual value */ nv_local=0; } if(nv_isarray(np) && np->nvalue.cp!=Empty && np->nvfun) up = np->nvalue.up; else up = &np->nvalue; if(up && up->cp) { if(up->cp!=Empty && !nv_isattr(np, NV_NOFREE)) free((void*)up->cp); up->cp = 0; } done: if(!nv_isarray(np) || !nv_arrayptr(np)) { if(nv_isref(np) && !nv_isattr(np,NV_EXPORT)) free((void*)np->nvalue.nrp); nv_setsize(np,0); if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT)) { if(nv_isattr(np,NV_EXPORT) && !strchr(np->nvname,'[')) env_delete(shp->env,nv_name(np)); if(!(flags&NV_EXPORT) || nv_isattr(np,NV_IMPORT|NV_EXPORT)==(NV_IMPORT|NV_EXPORT)) np->nvenv = 0; nv_setattr(np,0); } else { nv_setattr(np,NV_MINIMAL); nv_delete(np,(Dt_t*)0,0); } } } /* * return the node pointer in the highest level scope */ Namval_t *sh_scoped(Shell_t *shp, register Namval_t *np) { if(!dtvnext(shp->var_tree)) return(np); return(dtsearch(shp->var_tree,np)); } #if 1 /* * return space separated list of names of variables in given tree */ static char *tableval(Dt_t *root) { static Sfio_t *out; register Namval_t *np; 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); } #endif #if SHOPT_OPTIMIZE struct optimize { Namfun_t hdr; Shell_t *sh; char **ptr; struct optimize *next; Namval_t *np; }; static struct optimize *opt_free; static void optimize_clear(Namval_t* np, Namfun_t *fp) { struct optimize *op = (struct optimize*)fp; nv_stack(np,fp); nv_stack(np,(Namfun_t*)0); for(;op && op->np==np; op=op->next) { if(op->ptr) { *op->ptr = 0; op->ptr = 0; } } } static void put_optimize(Namval_t* np,const char *val,int flags,Namfun_t *fp) { nv_putv(np,val,flags,fp); optimize_clear(np,fp); } static Namfun_t *clone_optimize(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp) { return((Namfun_t*)0); } static const Namdisc_t optimize_disc = {sizeof(struct optimize),put_optimize,0,0,0,0,clone_optimize}; void nv_optimize(Namval_t *np) { register Namfun_t *fp; register struct optimize *op, *xp; if(sh.argaddr) { if(np==SH_LINENO) { sh.argaddr = 0; return; } for(fp=np->nvfun; fp; fp = fp->next) { if(fp->disc && (fp->disc->getnum || fp->disc->getval)) { sh.argaddr = 0; return; } if(fp->disc== &optimize_disc) break; } if((xp= (struct optimize*)fp) && xp->ptr==sh.argaddr) return; if(op = opt_free) opt_free = op->next; else op=(struct optimize*)calloc(1,sizeof(struct optimize)); op->ptr = sh.argaddr; op->np = np; if(xp) { op->hdr.disc = 0; op->next = xp->next; xp->next = op; } else { op->hdr.disc = &optimize_disc; op->next = (struct optimize*)sh.optlist; sh.optlist = (void*)op; nv_stack(np,&op->hdr); } } } void sh_optclear(Shell_t *shp, void *old) { register struct optimize *op,*opnext; for(op=(struct optimize*)shp->optlist; op; op = opnext) { opnext = op->next; if(op->ptr && op->hdr.disc) { nv_stack(op->np,&op->hdr); nv_stack(op->np,(Namfun_t*)0); } op->next = opt_free; opt_free = op; } shp->optlist = old; } #else # define optimize_clear(np,fp) #endif /* SHOPT_OPTIMIZE */ /* * Return a pointer to a character string that denotes the value * of . If refers to an array, return a pointer to * the value associated with the current index. * * If the value of is an integer, the string returned will * be overwritten by the next call to nv_getval. * * If has no value, 0 is returned. */ char *nv_getval(register Namval_t *np) { register union Value *up= &np->nvalue; register int numeric; #if SHOPT_OPTIMIZE if(!nv_local && sh.argaddr) nv_optimize(np); #endif /* SHOPT_OPTIMIZE */ if((!np->nvfun || !np->nvfun->disc) && !nv_isattr(np,NV_ARRAY|NV_INTEGER|NV_FUNCT|NV_REF|NV_TABLE)) goto done; if(nv_isref(np)) { if(!np->nvalue.cp) return(0); sh.last_table = nv_reftable(np); return(nv_name(nv_refnode(np))); } if(np->nvfun && np->nvfun->disc) { if(!nv_local) { nv_local=1; return(nv_getv(np, np->nvfun)); } nv_local=0; } numeric = ((nv_isattr (np, NV_INTEGER)) != 0); if(numeric) { Sflong_t ll; if(!up->cp) return("0"); if(nv_isattr (np,NV_DOUBLE)==NV_DOUBLE) { Sfdouble_t ld; double d; char *format; if(nv_isattr(np,NV_LONG)) { ld = *up->ldp; if(nv_isattr (np,NV_EXPNOTE)) format = "%.*Lg"; else if(nv_isattr (np,NV_HEXFLOAT)) format = "%.*La"; else format = "%.*Lf"; sfprintf(sh.strbuf,format,nv_size(np),ld); } else { d = *up->dp; if(nv_isattr (np,NV_EXPNOTE)) format = "%.*g"; else if(nv_isattr (np,NV_HEXFLOAT)) format = "%.*a"; else format = "%.*f"; sfprintf(sh.strbuf,format,nv_size(np),d); } return(sfstruse(sh.strbuf)); } else if(nv_isattr(np,NV_UNSIGN)) { if(nv_isattr (np,NV_LONG)) ll = *(Sfulong_t*)up->llp; else if(nv_isattr (np,NV_SHORT)) { if(nv_isattr(np,NV_INT16P)==NV_INT16P) ll = *(uint16_t*)(up->sp); else ll = (uint16_t)up->s; } else ll = *(uint32_t*)(up->lp); } else if(nv_isattr (np,NV_LONG)) ll = *up->llp; else if(nv_isattr (np,NV_SHORT)) { if(nv_isattr(np,NV_INT16P)==NV_INT16P) ll = *up->sp; else ll = up->s; } else ll = *(up->lp); if((numeric=nv_size(np))==10) { if(nv_isattr(np,NV_UNSIGN)) { sfprintf(sh.strbuf,"%I*u",sizeof(ll),ll); return(sfstruse(sh.strbuf)); } numeric = 0; } return(fmtbasell(ll,numeric, numeric&&numeric!=10)); } done: #if (_AST_VERSION>=20030127L) /* * if NV_RAW flag is on, return pointer to binary data * otherwise, base64 encode the data and return this string */ if(up->cp && nv_isattr(np,NV_BINARY) && !nv_isattr(np,NV_RAW)) { char *cp; int size= nv_size(np), insize=(4*size)/3+size/45+8; base64encode(up->cp, size, (void**)0, cp=getbuf(insize), insize, (void**)0); return(cp); } #endif if((numeric=nv_size(np)) && up->cp && up->cp[numeric]) { char *cp = getbuf(numeric+1); memcpy(cp,up->cp,numeric); cp[numeric]=0; return(cp); } return ((char*)up->cp); } Sfdouble_t nv_getnum(register Namval_t *np) { register union Value *up; register Sfdouble_t r=0; register char *str; #if SHOPT_OPTIMIZE if(!nv_local && sh.argaddr) nv_optimize(np); #endif /* SHOPT_OPTIMIZE */ if(nv_istable(np)) errormsg(SH_DICT,ERROR_exit(1),e_number,nv_name(np)); if(np->nvfun && np->nvfun->disc) { if(!nv_local) { nv_local=1; return(nv_getn(np, np->nvfun)); } nv_local=0; } if(nv_isref(np)) { str = nv_refsub(np); np = nv_refnode(np); if(str) nv_putsub(np,str,0L); } if(nv_isattr (np, NV_INTEGER)) { up= &np->nvalue; if(!up->lp || up->cp==Empty) r = 0; else if(nv_isattr(np, NV_DOUBLE)==NV_DOUBLE) { if(nv_isattr(np, NV_LONG)) r = *up->ldp; else r = *up->dp; } else if(nv_isattr(np, NV_UNSIGN)) { if(nv_isattr(np, NV_LONG)) r = (Sflong_t)*((Sfulong_t*)up->llp); else if(nv_isattr(np, NV_SHORT)) { if(nv_isattr(np,NV_INT16P)==NV_INT16P) r = (Sflong_t)(*(uint16_t*)up->sp); else r = (Sflong_t)((uint16_t)up->s); } else r = *((uint32_t*)up->lp); } else { if(nv_isattr(np, NV_LONG)) r = *up->llp; else if(nv_isattr(np, NV_SHORT)) { if(nv_isattr(np,NV_INT16P)==NV_INT16P) r = *up->sp; else r = up->s; } else r = *up->lp; } } else if((str=nv_getval(np)) && *str!=0) { if(np->nvfun || nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL)) { while(*str=='0') str++; } r = sh_arith(str); } return(r); } /* * Give the attributes and change its current * value to conform to . The of left and right * justified fields may be given. */ void nv_newattr (register Namval_t *np, unsigned newatts, int size) { register char *sp; register char *cp = 0; register unsigned int n; Namarr_t *ap = 0; int oldsize,oldatts; Namfun_t *fp= (newatts&NV_NODISC)?np->nvfun:0; newatts &= ~NV_NODISC; /* check for restrictions */ if(sh_isoption(SH_RESTRICTED) && ((sp=nv_name(np))==nv_name(PATHNOD) || sp==nv_name(SHELLNOD) || sp==nv_name(ENVNOD) || sp==nv_name(FPATHNOD))) errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np)); /* handle attributes that do not change data separately */ n = np->nvflag; if(newatts&NV_EXPORT) nv_offattr(np,NV_IMPORT); #ifdef _ENV_H if(((n^newatts)&NV_EXPORT)) { /* record changes to the environment */ if(n&NV_EXPORT) env_delete(sh.env,nv_name(np)); else sh_envput(sh.env,np); } #endif if((size==0||(n&NV_INTEGER)) && ((n^newatts)&~NV_NOCHANGE)==0) { if(size) nv_setsize(np,size); nv_offattr(np, ~NV_NOFREE); nv_onattr(np, newatts); return; } /* for an array, change all the elements */ if((ap=nv_arrayptr(np)) && ap->nelem>0) nv_putsub(np,NIL(char*),ARRAY_SCAN); oldsize = nv_size(np); oldatts = np->nvflag; if(fp) np->nvfun = 0; if(ap) /* add element to prevent array deletion */ ap->nelem++; do { nv_setsize(np,oldsize); np->nvflag = oldatts; if (sp = nv_getval(np)) { if(nv_isattr(np,NV_ZFILL)) while(*sp=='0') sp++; cp = (char*)malloc((n=strlen (sp)) + 1); strcpy(cp, sp); if(ap) { Namval_t *mp; ap->nelem &= ~ARRAY_SCAN; if(mp=nv_opensub(np)) { nv_unset(mp); mp->nvalue.cp = Empty; } else nv_unset(np); ap->nelem |= ARRAY_SCAN; } else nv_unset(np); if(size==0 && (newatts&(NV_LJUST|NV_RJUST|NV_ZFILL))) size = n; } else nv_unset(np); nv_setsize(np,size); np->nvflag &= NV_ARRAY; np->nvflag |= newatts; if (cp) { nv_putval (np, cp, NV_RDONLY); free(cp); } } while(ap && nv_nextsub(np)); if(fp) np->nvfun = fp; if(ap) ap->nelem--; return; } #ifndef _NEXT_SOURCE static char *oldgetenv(const char *string) { register char c0,c1; register const char *cp, *sp; register char **av = environ; if(!string || (c0= *string)==0) return(0); if((c1=*++string)==0) c1= '='; while(cp = *av++) { if(cp[0]!=c0 || cp[1]!=c1) continue; sp = string; while(*sp && *sp++ == *++cp); if(*sp==0 && *++cp=='=') return((char*)(cp+1)); } return(0); } /* * This version of getenv uses the hash storage to access environment values */ char *getenv(const char *name) /*@ assume name!=0; @*/ { register Namval_t *np; if(!sh.var_tree) return(oldgetenv(name)); if((np = nv_search(name,sh.var_tree,0)) && nv_isattr(np,NV_EXPORT)) return(nv_getval(np)); if(name[0] == 'P' && name[1] == 'A' && name[2] == 'T' && name[3] == 'H' && name[4] == 0) return(oldgetenv(name)); return(0); } #endif /* _NEXT_SOURCE */ #undef putenv /* * This version of putenv uses the hash storage to assign environment values */ int putenv(const char *name) { register Namval_t *np; if(name) { np = nv_open(name,sh.var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN); if(!strchr(name,'=')) nv_unset(np); } return(0); } /* * Override libast setenv() */ char* setenviron(const char *name) { register Namval_t *np; if(name) { np = nv_open(name,sh.var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN); if(strchr(name,'=')) return(nv_getval(np)); nv_unset(np); } return(""); } /* * convert to upper case */ static void ltou(register char *str) { register int c; for(; c= *((unsigned char*)str); str++) { if(islower(c)) *str = toupper(c); } } /* * convert to lower case */ static void utol(register char *str) { register int c; for(; c= *((unsigned char*)str); str++) { if(isupper(c)) *str = tolower(c); } } /* * normalize and return pointer to subscript if any * if is specified, return pointer to first = not in a subscript */ static char *lastdot(register char *cp, int eq) { register char *ep=0; register int c; if(eq) cp++; while(c= *cp++) { if(c=='[') cp = nv_endsubscript((Namval_t*)0,ep=cp,0); else if(c=='.') { if(*cp=='[') cp = nv_endsubscript((Namval_t*)0,cp,0); ep = 0; } else if(eq && c == '=') return(cp-1); } return(eq?0:ep); } int nv_rename(register Namval_t *np, int flags) { Shell_t *shp = &sh; register Namval_t *mp=0,*nr=0; register char *cp; int index= -1; Namval_t *last_table = shp->last_table; Dt_t *last_root = shp->last_root; Dt_t *hp = 0; if(nv_isattr(np,NV_PARAM) && shp->st.prevst) { if(!(hp=(Dt_t*)shp->st.prevst->save_tree)) hp = dtvnext(shp->var_tree); } if(!(cp=nv_getval(np))) { if(flags&NV_MOVE) errormsg(SH_DICT,ERROR_exit(1),e_varname,""); return(0); } if(lastdot(cp,0) && nv_isattr(np,NV_MINIMAL)) errormsg(SH_DICT,ERROR_exit(1),e_varname,nv_name(np)); if(nv_isarray(np) && !(mp=nv_opensub(np))) index=nv_aindex(np); if(!hp) hp = shp->var_tree; if(!(nr = nv_open(cp, hp, flags|NV_ARRAY|NV_NOREF|NV_NOSCOPE|NV_NOADD|NV_NOFAIL))) hp = shp->var_base; else if(shp->last_root) hp = shp->last_root; if(!nr) nr= nv_open(cp, hp, flags|NV_NOREF|((flags&NV_MOVE)?0:NV_NOFAIL)); if(!nr) { if(!nv_isvtree(np)) _nv_unset(np,0); return(0); } if(!mp && index>=0 && nv_isvtree(nr)) { sfprintf(shp->strbuf,"%s[%d]%c",nv_name(np),index,0); /* create a virtual node */ if(mp = nv_open(sfstruse(shp->strbuf),shp->var_tree,NV_VARNAME|NV_ADD|NV_ARRAY)) mp->nvenv = (void*)np; } if(mp) np = mp; if(nr==np) { if(index<0) return(0); if(cp = nv_getval(np)) cp = strdup(cp); } _nv_unset(np,0); if(nr==np) { nv_putsub(np,(char*)0, index); nv_putval(np,cp,0); free((void*)cp); return(1); } shp->prev_table = shp->last_table; shp->prev_root = shp->last_root; shp->last_table = last_table; shp->last_root = last_root; nv_clone(nr,np,(flags&NV_MOVE)|NV_COMVAR); if(flags&NV_MOVE) nv_delete(nr,(Dt_t*)0,NV_NOFREE); return(1); } /* * Create a reference node from to $np in dictionary */ void nv_setref(register Namval_t *np, Dt_t *hp, int flags) { Shell_t *shp = &sh; register Namval_t *nq, *nr=0; register char *ep,*cp; if(nv_isref(np)) return; if(nv_isarray(np)) errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np)); if(!(cp=nv_getval(np))) { nv_unset(np); nv_onattr(np,NV_REF); return; } if((ep = lastdot(cp,0)) && nv_isattr(np,NV_MINIMAL)) errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np)); if(!hp) hp = shp->var_tree; if(!(nr = nq = nv_open(cp, hp, flags|NV_NOSCOPE|NV_NOADD|NV_NOFAIL))) hp = shp->var_base; else if(shp->last_root) hp = shp->last_root; if(nq && ep && nv_isarray(nq) && !nv_getsub(nq)) nv_endsubscript(nq,ep-1,NV_ADD); if(!nr) nr= nq = nv_open(cp, hp, flags); if(nr==np) { if(shp->namespace && nv_dict(shp->namespace)==hp) errormsg(SH_DICT,ERROR_exit(1),e_selfref,nv_name(np)); /* bind to earlier scope, or add to global scope */ if(!(hp=dtvnext(hp)) || (nq=nv_search((char*)np,hp,NV_ADD|HASH_BUCKET))==np) errormsg(SH_DICT,ERROR_exit(1),e_selfref,nv_name(np)); } if(ep) { /* cause subscript evaluation and return result */ if(nv_isarray(nq)) ep = nv_getsub(nq); else { ep[strlen(ep)-1] = 0; nv_putsub(nr, ep, 0); ep[strlen(ep)-1] = ']'; if(nq = nv_opensub(nr)) ep = 0; else nq = nr; } } nv_unset(np); nv_delete(np,(Dt_t*)0,0); np->nvalue.nrp = newof(0,struct Namref,1,0); np->nvalue.nrp->np = nq; np->nvalue.nrp->root = hp; if(ep) np->nvalue.nrp->sub = strdup(ep); np->nvalue.nrp->table = shp->last_table; nv_onattr(np,NV_REF|NV_NOFREE); } /* * get the scope corresponding to * whence uses the same values as lseeek() */ Shscope_t *sh_getscope(int index, int whence) { register struct sh_scoped *sp, *topmost; if(whence==SEEK_CUR) sp = &sh.st; else { if ((struct sh_scoped*)sh.topscope != sh.st.self) topmost = (struct sh_scoped*)sh.topscope; else topmost = &(sh.st); sp = topmost; if(whence==SEEK_SET) { int n =0; while(sp = sp->prevst) n++; index = n - index; sp = topmost; } } if(index < 0) return((Shscope_t*)0); while(index-- && (sp = sp->prevst)); return((Shscope_t*)sp); } /* * make the top scope and return previous scope */ Shscope_t *sh_setscope(Shscope_t *scope) { Shscope_t *old = (Shscope_t*)sh.st.self; *sh.st.self = sh.st; sh.st = *((struct sh_scoped*)scope); sh.var_tree = scope->var_tree; SH_PATHNAMENOD->nvalue.cp = sh.st.filename; SH_FUNNAMENOD->nvalue.cp = sh.st.funname; return(old); } void sh_unscope(Shell_t *shp) { register Dt_t *root = shp->var_tree; register Dt_t *dp = dtview(root,(Dt_t*)0); table_unset(shp,root,NV_RDONLY|NV_NOSCOPE,dp); if(shp->st.real_fun && dp==shp->st.real_fun->sdict) { dp = dtview(dp,(Dt_t*)0); shp->st.real_fun->sdict->view = dp; } shp->var_tree=dp; dtclose(root); } /* * The inverse of creating a reference node */ void nv_unref(register Namval_t *np) { Namval_t *nq; if(!nv_isref(np)) return; nq = nv_refnode(np); nv_offattr(np,NV_NOFREE|NV_REF); free((void*)np->nvalue.nrp); np->nvalue.cp = strdup(nv_name(nq)); #if SHOPT_OPTIMIZE { Namfun_t *fp; for(fp=nq->nvfun; fp; fp = fp->next) { if(fp->disc== &optimize_disc) { optimize_clear(nq,fp); return; } } } #endif } /* * These following are for binary compatibility with the old hash library * They will be removed someday */ #if defined(__IMPORT__) && defined(__EXPORT__) # define extern __EXPORT__ #endif #undef hashscope extern Dt_t *hashscope(Dt_t *root) { return(dtvnext(root)); } #undef hashfree extern Dt_t *hashfree(Dt_t *root) { Dt_t *dp = dtvnext(root); dtclose(root); return(dp); } #undef hashname extern char *hashname(void *obj) { Namval_t *np = (Namval_t*)obj; return(np->nvname); } #undef hashlook extern void *hashlook(Dt_t *root, const char *name, int mode,int size) { NOT_USED(size); return((void*)nv_search(name,root,mode)); } char *nv_name(register Namval_t *np) { register Namval_t *table; register Namfun_t *fp; char *cp; if(is_abuiltin(np) || is_afunction(np)) return(np->nvname); if(!nv_isattr(np,NV_MINIMAL|NV_EXPORT) && np->nvenv) { Namval_t *nq= sh.last_table, *mp= (Namval_t*)np->nvenv; if(np==sh.last_table) sh.last_table = 0; if(nv_isarray(mp)) sfprintf(sh.strbuf,"%s[%s]",nv_name(mp),np->nvname); else sfprintf(sh.strbuf,"%s.%s",nv_name(mp),np->nvname); sh.last_table = nq; return(sfstruse(sh.strbuf)); } if(nv_istable(np)) #if 1 sh.last_table = nv_parent(np); #else sh.last_table = nv_create(np,0, NV_LAST,(Namfun_t*)0); #endif else if(!nv_isref(np)) { for(fp= np->nvfun ; fp; fp=fp->next) if(fp->disc && fp->disc->namef) { if(np==sh.last_table) sh.last_table = 0; return((*fp->disc->namef)(np,fp)); } } if(!(table=sh.last_table) || *np->nvname=='.' || table==sh.namespace || np==table) return(np->nvname); cp = nv_name(table); sfprintf(sh.strbuf,"%s.%s",cp,np->nvname); return(sfstruse(sh.strbuf)); } Namval_t *nv_lastdict(void) { return(sh.last_table); } #undef nv_context /* * returns the data context for a builtin */ void *nv_context(Namval_t *np) { return((void*)np->nvfun); } #define DISABLE /* proto workaround */ int nv_isnull DISABLE (register Namval_t *np) { return(nv_isnull(np)); } #undef nv_setsize int nv_setsize(register Namval_t *np, int size) { int oldsize = nv_size(np); if(size>=0) np->nvsize = size; return(oldsize); } Shell_t *nv_shell(Namval_t *np) { Namfun_t *fp; for(fp=np->nvfun;fp;fp=fp->next) { if(!fp->disc) return((Shell_t*)fp->last); } return(0); } #undef nv_unset void nv_unset(register Namval_t *np) { _nv_unset(np,0); return; }