/*********************************************************************** * * * 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 * * * ***********************************************************************/ #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" 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(const char*,char*); static void rightjust(char*, int, int); struct adata { char **argnam; int attsize; char *attval; }; 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); for(; c= *sp; 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; } if(*cp) { if(len>0) sfwrite(out,cp,len); else sfputr(out,cp,-1); } stakseek(offset); } /* * Perform parameter assignment for a linked list of parameters * contains attributes for the parameters */ void nv_setlist(register struct argnod *arg,register int flags) { register char *cp; register Namval_t *np; char *trap=sh.st.trap[SH_DEBUGTRAP]; int traceon = (sh_isoption(SH_XTRACE)!=0); int array = (flags&(NV_ARRAY|NV_IARRAY)); flags &= ~(NV_TYPE|NV_ARRAY); if(sh_isoption(SH_ALLEXPORT)) flags |= NV_EXPORT; if(sh.prefix) { flags &= ~(NV_IDENT|NV_EXPORT); flags |= NV_VARNAME; } for(;arg; arg=arg->argnxt.ap) { sh.used_pos = 0; if(arg->argflag&ARG_MAC) cp = sh_mactrim(arg->argval,-1); else { Namval_t *mp; stakseek(0); if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED))) { int flag = (NV_VARNAME|NV_ARRAY|NV_ASSIGN); struct fornod *fp=(struct fornod*)arg->argchn.ap; register Shnode_t *tp=fp->fortre; char *prefix = sh.prefix; flag |= (flags&NV_NOSCOPE); if(arg->argflag&ARG_QUOTED) cp = sh_mactrim(fp->fornam,-1); else cp = fp->fornam; error_info.line = fp->fortyp-sh.st.firstline; if(sh.fn_depth && (Namval_t*)tp->com.comnamp==SYSTYPESET) flag |= NV_NOSCOPE; if(prefix && tp->com.comset && *cp=='[') { sh.prefix = 0; np = nv_open(prefix,sh.var_tree,flag); sh.prefix = prefix; if(np) { if(!nv_isarray(np)) { stakputc('.'); stakputs(cp); cp = stakfreeze(1); } nv_close(np); } } np = nv_open(cp,sh.var_tree,flag); if(array) { if(!(flags&NV_APPEND)) nv_unset(np); if(array&NV_ARRAY) { nv_setarray(np,nv_associative); } else { nv_onattr(np,NV_ARRAY); } } /* 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; char **argv = sh_argbuild(&argc,&tp->com,0); if(!(arg->argflag&ARG_APPEND)) { 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(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); } } continue; } if(tp->tre.tretyp==TLST || !tp->com.comset || tp->com.comset->argval[0]!='[') { if(*cp!='.' && *cp!='[' && strchr(cp,'[')) { nv_close(np); np = nv_open(cp,sh.var_tree,flag); } if((arg->argflag&ARG_APPEND) && !nv_isarray(np)) nv_unset(np); } else { if(sh_isoption(SH_BASH) || (array&NV_IARRAY)) { if(!(arg->argflag&ARG_APPEND)) nv_unset(np); } else if((arg->argflag&ARG_APPEND) && (!nv_isarray(np) || (nv_aindex(np)>=0))) { nv_unset(np); nv_setarray(np,nv_associative); } else nv_setarray(np,nv_associative); } if(prefix) cp = stakcopy(nv_name(np)); sh.prefix = cp; sh_exec(tp,sh_isstate(SH_ERREXIT)); sh.prefix = prefix; if(nv_isarray(np) && (mp=nv_opensub(np))) np = mp; nv_setvtree(np); continue; } cp = arg->argval; } if(sh.prefix && *cp=='.' && cp[1]=='=') cp++; np = nv_open(cp,sh.var_tree,flags); if(!np->nvfun) { if(sh.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=strchr(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(trap,name,sub,av,append); } } } } /* * 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(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) { 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==sh.var_tree) { if(dtvnext(root)) top = 1; else flags &= ~NV_NOSCOPE; } if(!dp->disc) copy = dp->nofree; 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==sh.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) mode |= HASH_NOSCOPE; if(top) nq = nv_search(name,sh.var_base,0); if(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=='.') nv_setvtree(np); } if(c) *sp = c; top = 0; if(isref) { char *sub=0; if(c=='.') /* don't optimize */ sh.argaddr = 0; else if(flags&NV_NOREF) { if(c) nv_unref(np); return(np); } while(nv_isref(np)) { root = nv_reftree(np); sh.last_table = nv_reftable(np); sub = nv_refsub(np); np = nv_refnode(np); if(sub && c!='.') nv_putsub(np,sub,0L); flags |= NV_NOSCOPE; } 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; } sh.last_root = root; do { if(!np) { if(*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))) { 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&&(flags&NV_ARRAY)) n |= ARRAY_FILL; cp = nv_endsubscript(np,sp,n); } else cp = sp; if((c = *cp)=='.' || c=='[' || (n&ARRAY_FILL)) { int m = cp-sp; char *sub = m?nv_getsub(np):0; if(!sub) 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|ARRAY_FILL); else if(n==0 && c==0) { /* subscript must be 0*/ cp[-1] = 0; c = sh_arith(sp+1); cp[-1] = ']'; if(c) return(0); } dp->last = cp; if(nv_isarray(np) && (c=='[' || c=='.' || (flags&NV_ARRAY))) { *(sp=cp) = 0; nq = nv_search(name,root,mode); *sp = c; if(nq && nv_isnull(nq)) nq = nv_arraychild(np,nq,c); if(!(np=nq)) return(np); } } else if(nv_isarray(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) && (c = *(cp=dp->last=fp->last))==0) return(np); } } } while(c=='['); if(c!='.') 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); } /* * 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 * SH_INIT is only set while initializing the environment */ Namval_t *nv_open(const char *name, Dt_t *root, int flags) { 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; memset(&fun,0,sizeof(fun)); sh.last_table = sh.namespace; if(!root) root = sh.var_tree; sh.last_root = root; if(root==sh_subfuntree(1)) { flags |= NV_NOREF; msg = e_badfun; if((np=sh.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 = sh.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)) { sh.last_table = nv_reftable(np); np = nv_refnode(np); } } return(np); } else if(sh.prefix && /**name!='.' &&*/ (flags&NV_ASSIGN)) { name = cp = copystack(sh.prefix,name,(const char*)0); fun.nofree = 1; } c = *(unsigned char*)cp; if(root==sh.alias_tree) { msg = e_aliname; while((c= *(unsigned char*)cp++) && (c!='=') && (c!='/') && (c>=0x200 || !(c=sh_lexstates[ST_NORM][c]) || c==S_EPAT)); if(sh.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==sh.var_tree) root = sh.var_base; sh.last_table = 0; } if(c= !isaletter(c)) goto skip; np = nv_create(name, root, flags, &fun); cp = fun.last; 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_APPEND; cp++; } c = *cp; skip: 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; if(sh_isoption(SH_XTRACE) && nv_isarray(np)) sub = nv_getsub(np); c = msg==e_aliname? 0: (append | (flags&NV_EXPORT)); nv_putval(np, cp, c); savesub = sub; } 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) 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; int was_local = nv_local; 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 && !nv_isattr(np,NV_REF)) { /* This function contains disc */ if(!nv_local) { nv_local=1; nv_putv(np,sp,flags,np->nvfun); if(sp && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT))) sh_envput(sh.env,np); return; } /* called from disc, assign the actual value */ } flags &= ~NV_NODISC; if(flags&(NV_NOREF|NV_NOFREE)) { if(!nv_isnull(np) && np->nvalue.cp!=sp) nv_unset(np); nv_local=0; np->nvalue.cp = (char*)sp; nv_setattr(np,(flags&~NV_RDONLY)|NV_NOFREE); return; } nv_local=0; up= &np->nvalue; #if !SHOPT_BSH if(nv_isattr(np,NV_EXPORT)) nv_offattr(np,NV_IMPORT); else if(!nv_isattr(np,NV_MINIMAL)) np->nvenv = 0; #endif /* SHOPT_BSH */ if(nv_isattr (np, NV_INTEGER)) { if(nv_isattr(np, 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) { 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->s; up->s = 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) { 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_LONG) sfprintf(sh.strbuf,"%lld\0",*((Sflong_t*)sp)); else sfprintf(sh.strbuf,"%ld\0",*((int32_t*)sp)); sp = sfstruse(sh.strbuf); } if(nv_isattr(np, NV_HOST)==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; } nv_offattr(np,NV_NOFREE); 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); 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); 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; cp = (char*)malloc(((unsigned)dot+1)); } else cp = 0; up->cp = cp; if(sp) { if(nv_isattr(np, NV_LTOU)) ltou(sp,cp); else if(nv_isattr (np, NV_UTOL)) sh_utol(sp,cp); else strcpy(cp, sp); 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)) = 0; 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) free((void*)tofree); } if(!was_local && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT))) sh_envput(sh.env,np); 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) && (flag&NV_INTEGER)) { /* export doubles as integers for ksh88 compatibility */ stakputc(c+(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; 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) && (flag&NV_INTEGER)) { /* export doubles as integers for ksh88 compatibility */ *ap->attval++ = ' '+(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; 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; /* L_ARGNOD gets generated automatically as full path name of command */ nv_offattr(L_ARGNOD,NV_EXPORT); data.attsize = 6; namec = nv_scan(sh.var_tree,nullscan,(void*)0,NV_EXPORT,NV_EXPORT); er = (char**)stakalloc((namec+4)*sizeof(char*)); data.argnam = (er+=2); nv_scan(sh.var_tree, pushnam,&data,NV_EXPORT, NV_EXPORT); *data.argnam = (char*)stakalloc(data.attsize); cp = data.attval = strcopy(*data.argnam,e_envmarker); nv_scan(sh.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; NOT_USED(dict); if(sp->scanmask?(k&sp->scanmask)==sp->scanflags:(!sp->scanflags || (k&sp->scanflags))) { if(!np->nvalue.cp && !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 nv_scope(struct argnod *envlist) { register Dt_t *newscope; newscope = dtopen(&_Nvdisc,Dtoset); dtview(newscope,(Dt_t*)sh.var_tree); sh.var_tree = (Dt_t*)newscope; if(envlist) nv_setlist(envlist,NV_EXPORT|NV_NOSCOPE|NV_IDENT|NV_ASSIGN); } /* * 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(register Dt_t *root, int flags, Dt_t *oroot) { register Namval_t *np,*nq; for(np=(Namval_t*)dtfirst(root);np;np=nq) { _nv_unset(np,flags); if(oroot && (nq=nv_search(nv_name(np),oroot,0)) && nv_isattr(nq,NV_EXPORT)) sh_envput(sh.env,nq); nq = (Namval_t*)dtnext(root,np); dtdelete(root,np); free((void*)np); } } /* * * 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. */ void _nv_unset(register Namval_t *np,int flags) { 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)) { /* free function definition */ register char *name=nv_name(np),*cp= strrchr(name,'.'); if(cp) { Namval_t *npv; *cp = 0; npv = nv_open(name,sh.var_tree,NV_NOARRAY|NV_VARNAME|NV_NOADD); *cp++ = '.'; if(npv) nv_setdisc(npv,cp,NIL(Namval_t*),(Namfun_t*)npv); } stakdelete(slp->slptr); free((void*)np->nvalue.ip); np->nvalue.ip = 0; } goto done; } if(sh.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); return; } /* called from disc, assign the actual value */ nv_local=0; } up = &np->nvalue; if(up->cp) { if(!nv_isattr (np, NV_NOFREE)) free((void*)up->cp); up->cp = 0; } done: if(!nv_isarray(np) || !nv_arrayptr(np)) { if(nv_isref(np)) 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(sh.env,nv_name(np)); np->nvenv = 0; nv_setattr(np,0); } else nv_setattr(np,NV_MINIMAL); } } void nv_unset(register Namval_t *np) { _nv_unset(np,0); } /* * return the node pointer in the highest level scope */ Namval_t *nv_scoped(register Namval_t *np) { if(!dtvnext(sh.var_tree)) return(np); return(dtsearch(sh.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; 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 const Namdisc_t optimize_disc = {sizeof(struct optimize),put_optimize}; void nv_optimize(Namval_t *np) { register Namfun_t *fp; register struct optimize *op, *xp; if(sh.argaddr) { for(fp=np->nvfun; fp; fp = fp->next) { if(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 && !nv_isattr(np,NV_ARRAY|NV_INTEGER|NV_FUNCT|NV_REF|NV_TABLE)) goto done; if(nv_isref(np)) { sh.last_table = nv_reftable(np); return(nv_name(nv_refnode(np))); } if(np->nvfun) { 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)) { 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 format = "%.*Lf"; sfprintf(sh.strbuf,format,nv_size(np),ld); } else { d = *up->dp; if(nv_isattr (np,NV_EXPNOTE)) format = "%.*g"; 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)) 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)) 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) { if(!nv_local) { nv_local=1; return(nv_getn(np, np->nvfun)); } nv_local=0; } if(nv_isattr (np, NV_INTEGER)) { up= &np->nvalue; if(!up->lp) r = 0; else if(nv_isattr(np, 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)) 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)) 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; /* 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 SHOPT_BSH if(newatts&NV_EXPORT) nv_offattr(np,NV_IMPORT); #endif /* SHOPT_BSH */ 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); } 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(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_onattr(mp,NV_NOFREE); } nv_unset(np); if(ap) ap->nelem |= ARRAY_SCAN; 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(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(""); } /* * copy to changing lower case to upper case * must be big enough to hold * and may point to the same place. */ static void ltou(register char const *str1,register char *str2) /*@ assume str1!=0 && str2!=0; return x satisfying strlen(in str1)==strlen(in str2); @*/ { register int c; for(; c= *((unsigned char*)str1); str1++,str2++) { if(islower(c)) *str2 = toupper(c); else *str2 = c; } *str2 = 0; } /* * normalize and return pointer to subscript if any */ static char *lastdot(register char *cp) { register char *dp=cp, *ep=0; register int c; while(c= *cp++) { *dp++ = c; if(c=='[') ep = cp; else if(c=='.') { if(*cp=='[') { ep = nv_endsubscript((Namval_t*)0,cp,0); c = ep-cp; memcpy(dp,cp,c); dp = sh_checkid(dp+1,dp+c); cp = ep; } ep = 0; } } *dp = 0; return(ep); } /* * Create a reference node from to $np in dictionary */ void nv_setref(register Namval_t *np, Dt_t *hp, int flags) { register Namval_t *nq, *nr; 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))) errormsg(SH_DICT,ERROR_exit(1),e_noref,nv_name(np)); if((ep = lastdot(cp)) && nv_isattr(np,NV_MINIMAL)) errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np)); if(!hp) hp = sh.var_tree; nr= nq = nv_open(cp, hp, flags|NV_NOREF); while(nv_isref(nr)) { sh.last_table = nv_reftable(nr); hp = nv_reftree(nr); nr = nv_refnode(nr); } if(nr==np) { if(sh.namespace && nv_dict(sh.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 0 nv_endsubscript(nq,ep,NV_ADD); #endif ep = nv_getsub(nq); } nv_unset(np); 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 = sh.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; return(old); } void nv_unscope(void) { register Dt_t *root = sh.var_tree; register Dt_t *dp = dtview(root,(Dt_t*)0); table_unset(root,NV_RDONLY|NV_NOSCOPE,dp); sh.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_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); }