1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-24 15:04:13 +00:00
cde/src/cmd/ksh93/sh/name.c
Martijn Dekker 222515bf08 Implement hash tables for virtual subshells (re: 102868f8, 9d428f8f)
The forking fix implemented in 102868f8 and 9d428f8f, which stops
the main shell's hash table from being cleared if PATH is changed
in a subshell, can cause a significant performance penalty for
certain scripts that do something like

    ( PATH=... command foo )

in a subshell, especially if done repeatedly. This is because the
hash table is cleared (and hence a subshell forks) even for
temporary PATH assignments preceding commands.

It also just plain doesn't work. For instance:

    $ hash -r; (ls) >/dev/null; hash
    ls=/bin/ls

Simply running an external command in a subshell caches the path in
the hash table that is shared with a main shell. To remedy this, we
would have to fork the subshell before forking any external
command. And that would be an unacceptable performance regression.

Virtual subshells do not need to fork when changing PATH if they
get their own hash tables. This commit adds these. The code for
alias subshell trees (which was removed in ec888867 because they
were broken and unneeded) provided the beginning of a template for
their implementation.

src/cmd/ksh93/sh/subshell.c:
- struct subshell: Add strack pointer to subshell hash table.
- Add sh_subtracktree(): return pointer to subshell hash table.
- sh_subfuntree(): Refactor a bit for legibility.
- sh_subshell(): Add code for cleaning up subshell hash table.

src/cmd/ksh93/sh/name.c:
- nv_putval(): Remove code to fork a subshell upon resetting PATH.
- nv_rehash(): When in a subshell, invalidate a hash table entry
  for a subshell by creating the subshell scope if needed, then
  giving that entry the NV_NOALIAS attribute to invalidate it.

src/cmd/ksh93/sh/path.c: path_search():
- To set a tracked alias/hash table entry, use sh_subtracktree()
  and pass the HASH_NOSCOPE flag to nv_search() so that any new
  entries are added to the current subshell table (if any) and do
  not influence any parent scopes.

src/cmd/ksh93/bltins/typeset.c: b_alias():
- b_alias(): For hash table entries, use sh_subtracktree() instead
  of forking a subshell. Keep forking for normal aliases.
- setall(): To set a tracked alias/hash table entry, pass the
  HASH_NOSCOPE flag to nv_search() so that any new entries are
  added to the current subshell table (if any) and do not influence
  any parent scopes.

src/cmd/ksh93/sh/init.c: put_restricted():
- Update code for clearing the hash table (when changing $PATH) to
  use sh_subtracktree().

src/cmd/ksh93/bltins/cd_pwd.c:
- When invalidating path name bindings to relative paths, use the
  subshell hash tree if applicable by calling sh_subtracktree().
- rehash(): Call nv_rehash() instead of _nv_unset()ting the hash
  table entry; this is needed to work correctly in subshells.

src/cmd/ksh93/tests/leaks.sh:
- Add leak tests for various PATH-related operations in the main
  shell and in a virtual subshell.
- Several pre-existing memory leaks are exposed by the new tests
  (I've confirmed these in 93u+). The tests are disabled and marked
  TODO for now, as these bugs have not yet been fixed.

src/cmd/ksh93/tests/subshell.sh:
- Update.

Resolves: https://github.com/ksh93/ksh/issues/66
2021-01-07 22:18:25 +00:00

3633 lines
83 KiB
C

/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1982-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* http://www.eclipse.org/org/documents/epl-v10.html *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* David Korn <dgk@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* AT&T Labs
*
*/
#define putenv ___putenv
#include "defs.h"
#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 */
static char *savesub = 0;
static char Null[1];
static Namval_t NullNode;
static Dt_t *Refdict;
static Dtdisc_t _Refdisc =
{
offsetof(struct Namref,np),sizeof(struct Namval_t*),sizeof(struct Namref)
};
#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*);
static void pushnam(Namval_t*,void*);
static char *staknam(Namval_t*, char*);
static void rightjust(char*, int, int);
static char *lastdot(char*, int);
struct adata
{
Shell_t *sh;
Namval_t *tp;
char *mapname;
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;
Dt_t *last_root;
char *name;
Namval_t *np;
Namval_t *last_table;
Namval_t *namespace;
int flags;
short size;
short len;
} entries[NVCACHE];
short index;
short ok;
};
static struct Namcache nvcache;
#endif
char nv_local = 0;
static void(*nullscan)(Namval_t*,void*);
#if ( SFIO_VERSION <= 20010201L )
# define _data data
#endif
/* ======== 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);
}
/*
* 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)
{
Shell_t *shp = sh_getinterp();
register struct sh_type *sp = (struct sh_type*)shp->mktype;
register int i;
register char *name=0;
if(sp->numnodes==0 && !nv_isnull(np) && shp->last_table)
{
/* could be an redefine */
Dt_t *root = nv_dict(shp->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(shp->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 <name> 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
* <flags> contains attributes for the parameters
*/
void nv_setlist(register struct argnod *arg,register int flags, Namval_t *typ)
{
Shell_t *shp = sh_getinterp();
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*/
#if SHOPT_NAMESPACE
if(shp->namespace && nv_dict(shp->namespace)==shp->var_tree)
flags |= NV_NOSCOPE;
#endif /* SHOPT_NAMESPACE */
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;
}
else
shp->prefix_root = shp->first_root = 0;
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|NV_FARRAY));
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(!array && tp->tre.tretyp!=TLST && tp->com.comset && !tp->com.comarg && tp->com.comset->argval[0]==0 && tp->com.comset->argval[1]=='[')
array |= (tp->com.comset->argflag&ARG_MESSAGE)?NV_IARRAY:NV_ARRAY;
if(prefix && tp->com.comset && *cp=='[')
{
shp->prefix = 0;
np = nv_open(prefix,shp->last_root,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((arg->argflag&ARG_APPEND) && (tp->tre.tretyp&COMMSK)==TCOM && tp->com.comset && !nv_isvtree(np) && (((ap=nv_arrayptr(np)) && !ap->fun && !nv_opensub(np)) || (!ap && nv_isarray(np) && tp->com.comarg && !((mp=nv_search(tp->com.comarg->argval,shp->fun_tree,0)) && nv_isattr(mp,BLT_DCL)))))
{
if(tp->com.comarg)
{
struct argnod *ap = tp->com.comset;
while(ap->argnxt.ap)
ap = ap->argnxt.ap;
ap->argnxt.ap = tp->com.comarg;
}
tp->com.comarg = tp->com.comset;
tp->com.comset = 0;
tp->com.comtyp = COMSCAN;
}
if(nv_isattr(np,NV_RDONLY) && np->nvfun && !(flags&NV_RDONLY))
errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np));
if(nv_isattr(np,NV_NOFREE) && nv_isnull(np))
nv_offattr(np,NV_NOFREE);
if(nv_istable(np))
_nv_unset(np,0);
if(typ && !array && (!shp->prefix || nv_isnull(np) || nv_isarray(np)))
{
if(!(nv_isnull(np)) && !nv_isarray(np))
_nv_unset(np,0);
nv_settype(np,typ,0);
}
if((flags&NV_STATIC) && !nv_isattr(np,NV_EXPORT) && !nv_isnull(np))
#if SHOPT_TYPEDEF
goto check_type;
#else
continue;
#endif /* SHOPT_TYPEDEF */
ap=nv_arrayptr(np);
#if SHOPT_FIXEDARRAY
if(ap && ap->fixed)
flags |= NV_FARRAY;
#endif /* SHOPT_FIXEDARRAY */
if(array && (!ap || !ap->hdr.type))
{
#if SHOPT_FIXEDARRAY
if(!(arg->argflag&ARG_APPEND) && (!ap || !ap->fixed))
#else
if(!(arg->argflag&ARG_APPEND))
#endif /* SHOPT_FIXEDARRAY */
_nv_unset(np,NV_EXPORT);
if(array&NV_ARRAY)
{
nv_setarray(np,nv_associative);
}
else
{
nv_onattr(np,NV_ARRAY);
}
}
if(array && 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 && ((array&NV_IARRAY) || !((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 SHOPT_FIXEDARRAY
if(!nv_isarray(np) || ((ap=nv_arrayptr(np)) && !ap->fixed && (ap->nelem&ARRAY_MASK)))
#else
if(!nv_isarray(np) || ((ap=nv_arrayptr(np)) && (ap->nelem&ARRAY_MASK)))
#endif /* SHOPT_FIXEDARRAY */
{
if(ap)
ap->nelem |= ARRAY_UNDEF;
_nv_unset(np,NV_EXPORT);
}
}
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(shp,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==0 && !tp->com.comset && !tp->com.comarg)
{
if(!(arg->argflag&ARG_APPEND) && nv_isattr(np,NV_BINARY|NV_NOFREE|NV_RAW)!=(NV_BINARY|NV_NOFREE|NV_RAW))
_nv_unset(np,NV_EXPORT);
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 || np)
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,0);
if(!(array&NV_IARRAY) && !(tp->com.comset->argflag&ARG_MESSAGE))
nv_setarray(np,nv_associative);
}
nv_setlist(tp->com.comset,flags&~NV_STATIC,0);
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,'['))
{
cp = stakcopy(nv_name(np));
nv_close(np);
if(!(arg->argflag&ARG_APPEND))
flag &= ~NV_ARRAY;
shp->prefix_root = shp->first_root;
np = nv_open(cp,shp->prefix_root?shp->prefix_root: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_isvtree(np)|| nv_arrayptr(np)) && !nv_type(np))
{
_nv_unset(np,NV_EXPORT);
if(ap && ap->fun)
nv_setarray(np,nv_associative);
}
}
else
{
if(!(arg->argflag&ARG_APPEND))
_nv_unset(np,NV_EXPORT);
if(!(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;
if(shp->prefix)
{
if(*shp->prefix=='_' && shp->prefix[1]=='.' && nv_isref(L_ARGNOD))
{
sfprintf(stkstd,"%s%s",nv_name(L_ARGNOD->nvalue.nrp->np),shp->prefix+1);
shp->prefix = stkfreeze(stkstd,1);
}
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(shp->prefix)
#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) && !typ && (tp->com.comarg || !tp->com.comset || tp->com.comset->argval[0]!='['))
{
nv_setvtree(np);
if(tp->com.comarg || tp->com.comset)
np->nvfun->dsize = 0;
}
#if SHOPT_TYPEDEF
goto check_type;
#else
continue;
#endif /* SHOPT_TYPEDEF */
}
cp = arg->argval;
mp = 0;
}
np = nv_open(cp,shp->prefix_root?shp->prefix_root: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(shp,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;
if(shp->namespace)
free(shp->prefix);
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 <name> by <n> bytes and move from cp-1 to end
* right by <n>. 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(offset + 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_getinterp();
char *sub=0, *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=0,isref,top=0,noscope=(flags&NV_NOSCOPE);
int nofree=0, level=0;
#if SHOPT_FIXEDARRAY
Namarr_t *ap;
#endif /* SHOPT_FIXEDARRAY */
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(level++ || ((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,dp);
dtview(shp->var_tree,rp->sdict);
}
if(np = nv_search(name,shp->var_tree,0))
{
#if SHOPT_NAMESPACE
if(shp->var_tree->walk==shp->var_base || (shp->var_tree->walk!=shp->var_tree && shp->namespace && nv_dict(shp->namespace)==shp->var_tree->walk))
#else
if(shp->var_tree->walk==shp->var_base)
#endif /* SHOPT_NAMESPACE */
{
#if SHOPT_NAMESPACE
if(!(nq = nv_search((char*)np,shp->var_base,HASH_BUCKET)))
#endif /* SHOPT_NAMESPACE */
nq = np;
shp->last_root = shp->var_tree->walk;
if((flags&NV_NOSCOPE) && *cp!='.')
{
if(mode==0)
root = shp->var_tree->walk;
else
{
nv_delete(np,(Dt_t*)0,NV_NOFREE);
np = 0;
}
}
}
else
{
if(shp->var_tree->walk)
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 SHOPT_NAMESPACE
if(!np && !noscope && *name!='.' && shp->namespace && root==shp->var_tree)
root = nv_dict(shp->namespace);
#endif /* SHOPT_NAMESPACE */
if(np || (np = nv_search(name,root,mode)))
{
isref = nv_isref(np);
shp->openmatch = root->walk?root->walk:root;
if(top)
{
if(nq==np)
{
flags &= ~NV_NOSCOPE;
root = shp->last_root;
}
else if(nq)
{
if(nv_isnull(np) && c!='.' && ((np->nvfun=nv_cover(nq)) || nq==OPTINDNOD))
{
np->nvname = nq->nvname;
#if SHOPT_NAMESPACE
if(shp->namespace && nv_dict(shp->namespace)==shp->var_tree && nv_isattr(nq,NV_EXPORT))
nv_onattr(np,NV_EXPORT);
#endif /* SHOPT_NAMESPACE */
if(nq==OPTINDNOD)
{
np->nvfun = nq->nvfun;
np->nvalue.lp = (&shp->st.optindex);
nv_onattr(np,NV_INTEGER|NV_NOFREE);
}
}
flags |= NV_NOSCOPE;
}
}
else if(add && nv_isnull(np) && c=='.' && cp[1]!='.')
nv_setvtree(np);
#if SHOPT_NAMESPACE
if(shp->namespace && root==nv_dict(shp->namespace))
{
flags |= NV_NOSCOPE;
shp->last_table = shp->namespace;
}
#endif /* SHOPT_NAMESPACE */
}
if(c)
*sp = c;
top = 0;
if(isref)
{
#if SHOPT_FIXEDARRAY
int n=0,dim;
#endif /* SHOPT_FIXEDARRAY */
#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);
#if SHOPT_FIXEDARRAY
n = nv_refindex(np);
dim = nv_refdimen(np);
#endif /* SHOPT_FIXEDARRAY */
np = nv_refnode(np);
#if SHOPT_FIXEDARRAY
if(n)
{
ap = nv_arrayptr(np);
ap->nelem = dim;
nv_putsub(np,(char*)0,n);
}
else
#endif /* SHOPT_FIXEDARRAY */
if(sub && c!='.')
nv_putsub(np,sub,0L);
flags |= NV_NOSCOPE;
noscope = 1;
}
shp->first_root = root;
if(nv_isref(np) && (c=='[' || c=='.' || !(flags&NV_ASSIGN)))
errormsg(SH_DICT,ERROR_exit(1),e_noref,nv_name(np));
if(sub && c==0)
{
if(flags&NV_ARRAY)
{
Namarr_t *ap = nv_arrayptr(np);
nq = nv_opensub(np);
if((flags&NV_ASSIGN) && (!nq || nv_isnull(nq)))
ap->nelem++;
if(!nq)
goto addsub;
else
np = nq;
}
return(np);
}
if(np==nq)
flags &= ~(noscope?0:NV_NOSCOPE);
#if SHOPT_FIXEDARRAY
else if(c || n)
#else
else if(c)
#endif /* SHOPT_FIXEDARRAY */
{
#if SHOPT_FIXEDARRAY
static char null[1] = "";
#endif /* SHOPT_FIXEDARRAY */
c = (cp-sp);
copy = strlen(cp=nv_name(np));
dp->nofree |= 1;
#if SHOPT_FIXEDARRAY
if(*sp==0)
name = cp;
else
#endif /* SHOPT_FIXEDARRAY */
name = copystack(cp,sp,sub);
sp = (char*)name + copy;
cp = sp+c;
c = *sp;
if(!noscope)
flags &= ~NV_NOSCOPE;
#if SHOPT_FIXEDARRAY
if(c==0)
nv_endsubscript(np,null,NV_ADD);
#endif /* SHOPT_FIXEDARRAY */
}
flags |= NV_NOREF;
if(*cp==0 && nv_isnull(np) && !nv_isarray(np))
nofree = NV_NOFREE;
}
shp->last_root = root;
if(*cp && cp[1]=='.')
cp++;
if(c=='.' && (cp[1]==0 || cp[1]=='=' || cp[1]=='+'))
{
nv_local = 1;
if(np)
nv_onattr(np,nofree);
return(np);
}
if(cp[-1]=='.')
cp--;
do
{
#if SHOPT_FIXEDARRAY
int fixed;
#endif /* SHOPT_FIXEDARRAY */
if(!np)
{
if(!nq && *sp=='[' && *cp==0 && cp[-1]==']')
{
/*
* for backward compatibility
* evaluate subscript for
* possible side effects
*/
cp[-1] = 0;
sh_arith(shp,sp+1);
cp[-1] = ']';
}
return(np);
}
#if SHOPT_FIXEDARRAY
fixed = 0;
if((ap=nv_arrayptr(np)) && ap->fixed)
fixed = 1;
#endif /* SHOPT_FIXEDARRAY */
if(c=='[' || (c=='.' && nv_isarray(np)))
{
int n = 0;
sub = 0;
mode &= ~HASH_NOSCOPE;
if(c=='[')
{
#if SHOPT_FIXEDARRAY
Namarr_t *ap = nv_arrayptr(np);
#endif /* SHOPT_FIXEDARRAY */
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 SHOPT_FIXEDARRAY
if(fixed)
flags |= NV_FARRAY;
else
#endif /* SHOPT_FIXEDARRAY */
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|NV_FARRAY)));
#if SHOPT_FIXEDARRAY
flags &= ~NV_FARRAY;
if(fixed)
flags &= ~NV_ARRAY;
#endif /* SHOPT_FIXEDARRAY */
}
else
cp = sp;
if((c = *cp)=='.' || (c=='[' && nv_isarray(np)) || (n&ARRAY_FILL) || ((ap || (flags&NV_ASSIGN)) && (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)
{
char *dp = sp+n;
while(*dp++=*cp++);
cp = sp+n;
}
}
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);
#if SHOPT_FIXEDARRAY
else if(n==0 && !fixed && (c==0 || (c=='[' && !nv_isarray(np))))
#else
else if(n==0 && (c==0 || (c=='[' && !nv_isarray(np))))
#endif /* SHOPT_FIXEDARRAY */
{
/* subscript must be 0*/
cp[-1] = 0;
n = sh_arith(shp,sp+1);
cp[-1] = ']';
if(n)
return(0);
if(c)
sp = cp;
}
dp->last = cp;
if(nv_isarray(np) && (c=='[' || c=='.' || (flags&NV_ARRAY)))
{
addsub:
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);
}
nv_onattr(np,nofree);
nofree = 0;
np = nq;
}
else if(memcmp(cp,"[0]",3))
return(nq);
else
{
/* ignore [0] */
dp->last = cp += 3;
c = *cp;
}
}
}
#if SHOPT_FIXEDARRAY
else if(nv_isarray(np) && (!fixed || cp[-1]!=']'))
#else
else if(nv_isarray(np))
#endif /* SHOPT_FIXEDARRAY */
{
if(c==0 && (flags&NV_MOVE))
return(np);
nv_putsub(np,NIL(char*),ARRAY_UNDEF);
}
nv_onattr(np,nofree);
nofree = 0;
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;
shp->last_table = 0;
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 <np> from the dictionary <root> and clear from the cache
* if <root> is NULL, only the cache is cleared
* if flags does not contain NV_NOFREE, the node is freed
* if np==0 && !root && flags==0, delete the Refdict dictionary
*/
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(!np && !root && flags==0)
{
if(Refdict)
dtclose(Refdict);
Refdict = 0;
return;
}
if(root || !(flags&NV_NOFREE))
{
if(!(flags&NV_FUNCTION) && Refdict)
{
Namval_t **key = &np;
struct Namref *rp;
while(rp = (struct Namref*)dtmatch(Refdict,(void*)key))
{
if(rp->sub)
free(rp->sub);
rp->sub = 0;
rp = dtdelete(Refdict,(void*)rp);
rp->np = &NullNode;
}
}
}
if(root)
{
if(dtdelete(root,np))
{
if(!(flags&NV_NOFREE) && ((flags&NV_FUNCTION) || !nv_subsaved(np,flags&NV_TABLE)))
{
Namarr_t *ap;
if(nv_isarray(np) && np->nvfun && (ap=nv_arrayptr(np)) && array_assoc(ap))
{
/* free associative array from memory */
while(nv_associative(np,0,NV_ANEXT))
nv_associative(np,0,NV_ADELETE);
nv_associative(np,0,NV_AFREE);
free((void*)np->nvfun);
}
free((void*)np);
}
}
}
}
/*
* Put <arg> into associative memory.
* If <flags> & NV_ARRAY then follow array to next subscript
* If <flags> & NV_NOARRAY then subscript is not allowed
* If <flags> & NV_NOSCOPE then use the current scope only
* If <flags> & NV_ASSIGN then assignment is allowed
* If <flags> & NV_IDENT then name must be an identifier
* If <flags> & NV_VARNAME then name must be a valid variable name
* If <flags> & NV_NOADD then node will not be added if not found
* If <flags> & NV_NOREF then don't follow reference
* If <flags> & NV_NOFAIL then don't generate an error message on failure
* If <flags> & NV_STATIC then unset before an assignment
* If <flags> & NV_UNJUST then unset attributes before 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_getinterp();
register char *cp=(char*)name;
register int c;
register Namval_t *np=0;
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->openmatch = 0;
shp->last_table = 0;
if(!root)
root = shp->var_tree;
shp->last_root = root;
if(root==shp->fun_tree)
{
flags |= NV_NOREF;
msg = e_badfun;
if(strchr(name,'.'))
{
name = cp = copystack(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(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 && xp->namespace==shp->namespace && (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) && !(flags&NV_MOVE))
nv_putsub(np,NIL(char*),ARRAY_UNDEF);
shp->last_table = xp->last_table;
shp->last_root = xp->last_root;
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->namespace = shp->namespace;
xp->last_table = shp->last_table;
xp->last_root = shp->last_root;
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;
Namval_t *mp;
Namarr_t *ap;
int isref;
shp->prefix = 0;
if((flags&NV_STATIC) && !shp->mktype)
{
if(!nv_isnull(np))
{
shp->prefix = prefix;
return(np);
}
}
isref = nv_isref(np);
#if SHOPT_FIXEDARRAY
if(sh_isoption(SH_XTRACE) && (ap=nv_arrayptr(np)) && !ap->fixed)
#else
if(sh_isoption(SH_XTRACE) && nv_isarray(np))
#endif /* SHOPT_FIXEDARRAY */
sub = nv_getsub(np);
c = msg==e_aliname? 0: (append | (flags&NV_EXPORT));
if(isref)
nv_offattr(np,NV_REF);
if(!append && (flags&NV_UNJUST))
{
if(!np->nvfun)
_nv_unset(np,NV_EXPORT);
}
if(flags&NV_MOVE)
{
if(ap=nv_arrayptr(np))
{
if(mp=nv_opensub(np))
np = mp;
else if(!array_assoc(ap) && (mp = nv_open(cp,shp->var_tree,NV_NOFAIL|NV_VARNAME|NV_NOARRAY|NV_NOASSIGN|NV_NOADD)) && nv_isvtree(np))
{
ap->nelem |= ARRAY_TREE;
nv_putsub(np,(char*)0,ARRAY_ADD|nv_aindex(np));
np = nv_opensub(np);
ap->nelem &= ~ARRAY_TREE;
ap->nelem -= 1;
}
}
_nv_unset(np,NV_EXPORT);
}
nv_putval(np, cp, c);
if(isref)
{
if(nv_search((char*)np,shp->var_base,HASH_BUCKET))
shp->last_root = shp->var_base;
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 <string> into name-value node <np>.
* If <np> is an array, then the element given by the
* current index is assigned to.
* If <flags> contains NV_RDONLY, readonly attribute is ignored
* If <flags> contains NV_INTEGER, string is a pointer to a number
* If <flags> contains NV_NOFREE, previous value is freed, and <string>
* becomes value of node and <flags> becomes attributes
*/
void nv_putval(register Namval_t *np, const char *string, int flags)
{
Shell_t *shp = sh_getinterp();
register const char *sp=string;
register union Value *up;
register char *cp;
register int size = 0;
register int dot;
int was_local = nv_local;
union Value u;
#if SHOPT_FIXEDARRAY
Namarr_t *ap;
#endif /* SHOPT_FIXEDARRAY */
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
*/
shp->argaddr = 0;
if(shp->subshell && !nv_local && !(flags&NV_RDONLY))
np = sh_assignok(np,1);
if(np->nvfun && np->nvfun->disc && !(flags&NV_NODISC) && !nv_isref(np))
{
/* 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(shp->env,np);
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_DOUBLE) == NV_INT16)
{
if(!np->nvalue.up || !nv_isarray(np))
{
up = &u;
up->up = &np->nvalue;
}
}
#if SHOPT_FIXEDARRAY
else if(np->nvalue.up && nv_isarray(np) && (ap=nv_arrayptr(np)) && !ap->fixed)
#else
else if(np->nvalue.up && nv_isarray(np) && nv_arrayptr(np))
#endif /* SHOPT_FIXEDARRAY */
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)<sizeof(Sfdouble_t))
{
Sfdouble_t ld, old=0;
if(flags&NV_INTEGER)
{
if(flags&NV_LONG)
ld = *((Sfdouble_t*)sp);
else if(flags&NV_SHORT)
ld = *((float*)sp);
else
ld = *((double*)sp);
}
else
ld = sh_arith(shp,sp);
if(!up->ldp)
up->ldp = new_of(Sfdouble_t,0);
else if(flags&NV_APPEND)
old = *(up->ldp);
*(up->ldp) = old?ld+old:ld;
}
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(shp,sp);
if(!up->dp)
up->dp = new_of(double,0);
else if(flags&NV_APPEND)
od = *(up->dp);
*(up->dp) = od?d+od:d;
}
}
else
{
if(nv_isattr(np, NV_LONG) && sizeof(int32_t)<sizeof(Sflong_t))
{
Sflong_t ll=0,oll=0;
if(flags&NV_INTEGER)
{
if((flags&NV_DOUBLE) == NV_DOUBLE)
{
if(flags&NV_LONG)
ll = *((Sfdouble_t*)sp);
else if(flags&NV_SHORT)
ll = *((float*)sp);
else
ll = *((double*)sp);
}
else if(nv_isattr(np,NV_UNSIGN))
{
if(flags&NV_LONG)
ll = *((Sfulong_t*)sp);
else if(flags&NV_SHORT)
ll = *((uint16_t*)sp);
else
ll = *((uint32_t*)sp);
}
else
{
if(flags&NV_LONG)
ll = *((Sflong_t*)sp);
else if(flags&NV_SHORT)
ll = *((uint16_t*)sp);
else
ll = *((uint32_t*)sp);
}
}
else if(sp)
ll = (Sflong_t)sh_arith(shp,sp);
if(!up->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(shp,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,append;
#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(shp->strbuf,"%.*Lg",LDBL_DIG,*((Sfdouble_t*)sp));
else
sfprintf(shp->strbuf,"%.*g",DBL_DIG,*((double*)sp));
}
else if(flags&NV_UNSIGN)
{
if(flags&NV_LONG)
sfprintf(shp->strbuf,"%I*lu",sizeof(Sfulong_t),*((Sfulong_t*)sp));
else
sfprintf(shp->strbuf,"%lu",(unsigned long)((flags&NV_SHORT)?*((uint16_t*)sp):*((uint32_t*)sp)));
}
else
{
if(flags&NV_LONG)
sfprintf(shp->strbuf,"%I*ld",sizeof(Sflong_t),*((Sflong_t*)sp));
else
sfprintf(shp->strbuf,"%ld",(long)((flags&NV_SHORT)?*((int16_t*)sp):*((int32_t*)sp)));
}
sp = sfstruse(shp->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 || *up->cp==0)
flags &= ~NV_APPEND;
if(!nv_isattr(np, NV_NOFREE))
{
/* delay free in case <sp> 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) && nv_isattr(np,NV_LJUST|NV_RJUST)!=(NV_LJUST|NV_RJUST))
tofree = 0;
if (sp)
{
append=0;
if(sp==up->cp && !(flags&NV_APPEND))
return;
dot = strlen(sp);
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)) = 0;
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
{
if(size==0 && nv_isattr(np,NV_HOST)!=NV_HOST &&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|NV_RJUST)==NV_LJUST && dot>size)
dot = size;
if(flags&NV_APPEND)
{
if(dot==0)
return;
append = strlen(up->cp);
if(!tofree || size)
{
offset = staktell();
stakputs(up->cp);
stakputs(sp);
stakputc(0);
sp = stakptr(offset);
dot += append;
append = 0;
}
else
{
flags &= ~NV_APPEND;
}
}
}
if(size==0 || tofree || dot || !(cp=(char*)up->cp))
{
if(dot==0 && !nv_isattr(np,NV_LJUST|NV_RJUST))
{
cp = Null;
nv_onattr(np,NV_NOFREE);
}
else
{
if(tofree && tofree!=Empty && tofree!=Null)
{
cp = (char*)realloc((void*)tofree,((unsigned)dot+append+8));
tofree = 0;
}
else
cp = (char*)malloc(((unsigned)dot+8));
cp[dot+append] = 0;
nv_offattr(np,NV_NOFREE);
}
}
}
else
cp = 0;
up->cp = cp;
if(sp)
{
int c = cp[dot+append];
memmove(cp+append,sp,dot);
cp[dot+append] = c;
if(nv_isattr(np, NV_RJUST) && nv_isattr(np, NV_ZFILL))
rightjust(cp,size,'0');
else if(nv_isattr(np, NV_LJUST|NV_RJUST)==NV_RJUST)
rightjust(cp,size,' ');
else if(nv_isattr(np, NV_LJUST|NV_RJUST)==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 && tofree!=Null)
free((void*)tofree);
}
if(!was_local && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT)))
sh_envput(shp->env,np);
return;
}
/*
*
* Right-justify <str> so that it contains no more than
* <size> characters. If <str> contains fewer than <size>
* characters, left-pad with <fill>. Trailing blanks
* in <str> will be ignored.
*
* If the leftmost digit in <str> is not a digit, <fill>
* 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
* <type> 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);
if((outsize = mbwidth(w)) <0)
outsize = 0;
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 */
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);
}
/*
* put the name and attribute into value of attributes variable
*/
static void attstore(register Namval_t *np, void *data)
{
register int flag = np->nvflag;
register struct adata *ap = (struct adata*)data;
ap->sh = sh_getinterp();
ap->tp = 0;
if(!(flag&NV_EXPORT) || (flag&NV_FUNCT))
return;
if((flag&(NV_UTOL|NV_LTOU|NV_INTEGER)) == (NV_UTOL|NV_LTOU))
{
data = (void*)nv_mapchar(np,0);
if(strcmp(data,e_tolower) && strcmp(data,e_toupper))
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));
}
static void pushnam(Namval_t *np, void *data)
{
register char *value;
register struct adata *ap = (struct adata*)data;
ap->sh = sh_getinterp();
ap->tp = 0;
if(nv_isattr(np,NV_IMPORT) && 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);
}
/*
* Generate the environment list for the child.
*/
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;
data.mapname = 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);
/* Export variable attributes into env var named by e_envmarker, unless POSIX mode is on */
cp = data.attval = strcopy(*data.argnam,e_envmarker);
if(!sh_isoption(SH_POSIX))
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);
}
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;
char *cp;
NOT_USED(dict);
#if SHOPT_TYPEDEF
if(!is_abuiltin(np) && tp && tp->tp && nv_type(np)!=tp->tp)
return(0);
#endif /*SHOPT_TYPEDEF */
if(sp->scanmask==NV_TABLE && nv_isvtree(np))
k = NV_TABLE;
if(sp->scanmask?(k&sp->scanmask)==sp->scanflags:(!sp->scanflags || (k&sp->scanflags)))
{
if(tp && tp->mapname)
{
if(sp->scanflags==NV_FUNCTION || sp->scanflags==(NV_NOFREE|NV_BINARY|NV_RAW))
{
int n = strlen(tp->mapname);
if(memcmp(np->nvname,tp->mapname,n) || np->nvname[n]!='.' || strchr(&np->nvname[n+1],'.'))
return(0);
}
else if((sp->scanflags==NV_UTOL||sp->scanflags==NV_LTOU) && (cp=(char*)nv_mapchar(np,0)) && strcmp(cp,tp->mapname))
return(0);
}
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);
}
/*
* Handler function to invalidate a path name binding in the hash table
*/
void nv_rehash(register Namval_t *np,void *data)
{
NOT_USED(data);
if(sh.subshell)
{
/* invalidate subshell node; if none exists, add a dummy */
np = nv_search(nv_name(np),sh.track_tree,NV_ADD|HASH_NOSCOPE);
}
nv_onattr(np,NV_NOALIAS);
}
/*
* Walk through the name-value pairs
* if <mask> is non-zero, then only nodes with (nvflags&mask)==flags
* are visited
* If <mask> is zero, and <flags> non-zero, then nodes with one or
* more of <flags> is visited
* If <mask> and <flags> are zero, then all nodes are visited
*/
int nv_scan(Dt_t *root, void (*fn)(Namval_t*,void*), void *data,int mask, int flags)
{
Namval_t *np;
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);
for(np=(Namval_t*)dtfirst(root);np; np=(Namval_t*)dtnext(root,np))
hashfn(root, np, &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;
#if SHOPT_NAMESPACE
if(shp->namespace)
newroot = nv_dict(shp->namespace);
#endif /* SHOPT_NAMESPACE */
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,0);
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)
{
struct adata *tp = (struct adata*)data;
char *cp=0;
if(np==VERSIONNOD && nv_isref(np))
return;
if(np==L_ARGNOD)
return;
if(np == tp->sh->namespace)
return;
if(nv_isref(np))
nv_unref(np);
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) && np!=VERSIONNOD)
{
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(nq=dtsearch(oroot,np))
{
if(nv_cover(nq))
{
unsigned 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 if(shp->test&4)
nv_putval(nq, strdup(nv_getval(nq)), NV_RDONLY);
else
nv_putval(nq, nv_getval(nq), NV_RDONLY);
shp->subshell = subshell;
np->nvfun = 0;
}
if(nv_isattr(nq,NV_EXPORT))
sh_envput(shp->env,nq);
}
shp->last_root = root;
shp->last_table = 0;
if(nv_isvtree(np))
{
int len = strlen(np->nvname);
npnext = (Namval_t*)dtnext(root,np);
while((nq=npnext) && memcmp(np->nvname,nq->nvname,len)==0 && nq->nvname[len]=='.')
{
_nv_unset(nq,flags);
npnext = (Namval_t*)dtnext(root,nq);
nv_delete(nq,root,NV_TABLE);
}
}
npnext = (Namval_t*)dtnext(root,np);
if(nv_arrayptr(np))
nv_putsub(np,NIL(char*),ARRAY_SCAN);
_nv_unset(np,flags);
nv_delete(np,root,NV_TABLE);
}
}
/*
*
* Set the value of <np> to 0, and nullify any attributes
* that <np> may have had. Free any freeable space occupied
* by the value of <np>. If <np> denotes an array member, it
* will retain its attributes.
* <flags> can contain NV_RDONLY to override the readonly attribute
* being cleared.
* <flags> can contain NV_EXPORT to override preserve nvenv
*/
void _nv_unset(register Namval_t *np,int flags)
{
Shell_t *shp = sh_getinterp();
register union Value *up;
#if SHOPT_FIXEDARRAY
Namarr_t *ap;
#endif /* SHOPT_FIXEDARRAY */
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(np->nvalue.rp->running)
{
np->nvalue.rp->running |= 1;
return;
}
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 && npv!=shp->namespace)
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)
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_isattr(np,NV_INT16P) == NV_INT16)
{
np->nvalue.cp = nv_isarray(np)?Empty:0;
goto done;
}
#if SHOPT_FIXEDARRAY
else if(np->nvalue.up && nv_isarray(np) && (ap=nv_arrayptr(np)) && !ap->fixed)
#else
else if(np->nvalue.up && nv_isarray(np) && nv_arrayptr(np))
#endif /* SHOPT_FIXEDARRAY */
up = np->nvalue.up;
else if(nv_isref(np) && !nv_isattr(np,NV_EXPORT|NV_MINIMAL) && np->nvalue.nrp)
{
if(np->nvalue.nrp->root)
dtdelete(Refdict,(void*)np->nvalue.nrp);
if(np->nvalue.nrp->sub)
free(np->nvalue.nrp->sub);
free((void*)np->nvalue.nrp);
np->nvalue.cp = 0;
up = 0;
}
else
up = &np->nvalue;
if(up && up->cp)
{
if(up->cp!=Empty && up->cp!=Null && !nv_isattr(np, NV_NOFREE))
free((void*)up->cp);
up->cp = 0;
}
done:
if(!nv_isarray(np) || !nv_arrayptr(np))
{
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_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));
}
/*
* 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);
}
#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)
{
Shell_t *shp = sh_getinterp();
register Namfun_t *fp;
register struct optimize *op, *xp;
if(shp->argaddr)
{
if(np==SH_LINENO)
{
shp->argaddr = 0;
return;
}
for(fp=np->nvfun; fp; fp = fp->next)
{
if(fp->disc && (fp->disc->getnum || fp->disc->getval))
{
shp->argaddr = 0;
return;
}
if(fp->disc== &optimize_disc)
break;
}
if((xp= (struct optimize*)fp) && xp->ptr==shp->argaddr)
return;
if(op = opt_free)
opt_free = op->next;
else
op=(struct optimize*)calloc(1,sizeof(struct optimize));
op->ptr = shp->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*)shp->optlist;
shp->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 <np>. If <np> refers to an array, return a pointer to
* the value associated with the current index.
*
* If the value of <np> is an integer, the string returned will
* be overwritten by the next call to nv_getval.
*
* If <np> has no value, 0 is returned.
*/
char *nv_getval(register Namval_t *np)
{
Shell_t *shp = sh_getinterp();
register union Value *up= &np->nvalue;
register int numeric;
#if SHOPT_OPTIMIZE
if(!nv_local && shp->argaddr)
nv_optimize(np);
#endif /* SHOPT_OPTIMIZE */
if((!np->nvfun || !np->nvfun->disc) && !nv_isattr(np,NV_ARRAY|NV_INTEGER|NV_FUNCT|NV_REF))
goto done;
if(nv_isref(np))
{
char *sub;
if(!np->nvalue.cp)
return(0);
shp->last_table = nv_reftable(np);
sub=nv_refsub(np);
np = nv_refnode(np);
if(sub)
{
sfprintf(shp->strbuf,"%s[%s]",nv_name(np),sub);
return(sfstruse(shp->strbuf));
}
return(nv_name(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(shp->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(shp->strbuf,format,nv_size(np),d);
}
return(sfstruse(shp->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(shp->strbuf,"%I*u",sizeof(ll),ll);
return(sfstruse(shp->strbuf));
}
numeric = 0;
}
return(fmtbase(ll,numeric, numeric&&numeric!=10));
}
done:
/*
* 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;
char *ep;
int size= nv_size(np), insize=(4*size)/3+size/45+8;
base64encode(up->cp, size, (void**)0, cp=getbuf(insize), insize, (void**)&ep);
*ep = 0;
return(cp);
}
if(!nv_isattr(np,NV_LJUST|NV_RJUST) && (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)
{
Shell_t *shp = sh_getinterp();
register union Value *up;
register Sfdouble_t r=0;
register char *str;
#if SHOPT_OPTIMIZE
if(!nv_local && shp->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(nv_isattr(np,NV_LJUST|NV_RJUST) || (*str=='0' && !(str[1]=='x'||str[1]=='X')))
{
while(*str=='0')
str++;
}
r = sh_arith(shp,str);
}
return(r);
}
/*
* Give <np> the attributes <newatts,> and change its current
* value to conform to <newatts>. The <size> of left and right
* justified fields may be given.
*/
void nv_newattr (register Namval_t *np, unsigned newatts, int size)
{
Shell_t *shp = sh_getinterp();
register char *sp;
register char *cp = 0;
register unsigned int n;
Namval_t *mp = 0;
Namarr_t *ap = 0;
int oldsize,oldatts,trans;
Namfun_t *fp= (newatts&NV_NODISC)?np->nvfun:0;
char *prefix = shp->prefix;
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;
trans = !(n&NV_INTEGER) && (n&(NV_LTOU|NV_UTOL)); /* transcode to lower or upper case */
if(newatts&NV_EXPORT)
nv_offattr(np,NV_IMPORT);
if(((n^newatts)&NV_EXPORT)) /* EXPORT attribute has been toggled */
{
/* record changes to the environment */
if(n&NV_EXPORT)
{
/* EXPORT exists on old attributes therefore not on new */
nv_offattr(np,NV_EXPORT);
env_delete(shp->env,nv_name(np));
}
else
{
/* EXPORT is now turned on for new attributes */
nv_onattr(np,NV_EXPORT);
sh_envput(shp->env,np);
}
if((n^newatts)==NV_EXPORT && !trans)
/* Only EXPORT attribute has changed and thus all work has been done. */
return;
}
oldsize = nv_size(np);
if((size==oldsize|| (n&NV_INTEGER)) && !trans && ((n^newatts)&~NV_NOCHANGE)==0)
{
if(size>0)
np->nvsize = size;
else if(size==NV_FLTSIZEZERO)
np->nvsize = 0;
nv_offattr(np, ~NV_NOFREE);
nv_onattr(np, newatts);
return;
}
if(size==NV_FLTSIZEZERO)
size = 0;
/* 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++;
#if SHOPT_FIXEDARRAY
if(ap->fixed)
{
nv_setsize(np,size);
np->nvflag &= NV_ARRAY;
np->nvflag |= newatts;
goto skip;
}
#endif /* SHOPT_TYPEDEF */
}
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)) + 8);
strcpy(cp, sp);
if(sp && (mp=nv_opensub(np)))
{
if(trans)
{
nv_disc(np, &ap->hdr,NV_POP);
nv_clone(np,mp,0);
nv_disc(np, &ap->hdr,NV_FIRST);
nv_offattr(mp,NV_ARRAY);
}
nv_newattr(mp,newatts&~NV_ARRAY,size);
}
if(!mp)
{
if(ap)
ap->nelem &= ~ARRAY_SCAN;
if(!trans)
_nv_unset(np,NV_RDONLY|NV_EXPORT);
if(ap)
ap->nelem |= ARRAY_SCAN;
}
if(size==0 && !(newatts&NV_INTEGER) && (newatts&NV_HOST)!=NV_HOST && (newatts&(NV_LJUST|NV_RJUST|NV_ZFILL)))
size = n;
}
else if(!trans)
_nv_unset(np,NV_EXPORT);
nv_setsize(np,size);
np->nvflag &= (NV_ARRAY|NV_NOFREE);
np->nvflag |= newatts;
if (cp)
{
if(!mp)
nv_putval (np, cp, NV_RDONLY);
free(cp);
}
}
while(ap && nv_nextsub(np));
#if SHOPT_FIXEDARRAY
skip:
#endif /* SHOPT_TYPEDEF */
if(fp)
np->nvfun = fp;
if(ap)
ap->nelem--;
shp->prefix = prefix;
return;
}
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 *sh_getenv(const char *name)
{
Shell_t *shp = sh_getinterp();
register Namval_t *np;
if(!shp->var_tree)
return(oldgetenv(name));
else if((np = nv_search(name,shp->var_tree,0)) && nv_isattr(np,NV_EXPORT))
return(nv_getval(np));
return(0);
}
#ifndef _NEXT_SOURCE
/*
* Some dynamic linkers will make this file see the libc getenv(),
* so sh_getenv() is used for the astintercept() callback. Plain
* getenv() is provided for static links.
*/
char *getenv(const char *name)
{
return sh_getenv(name);
}
#endif /* _NEXT_SOURCE */
#undef putenv
/*
* This version of putenv uses the hash storage to assign environment values
*/
int putenv(const char *name)
{
Shell_t *shp = sh_getinterp();
register Namval_t *np;
if(name)
{
np = nv_open(name,shp->var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN);
if(!strchr(name,'='))
_nv_unset(np,0);
}
return(0);
}
/*
* Override libast setenviron().
*/
char* sh_setenviron(const char *name)
{
Shell_t *shp = sh_getinterp();
register Namval_t *np;
if(name)
{
np = nv_open(name,shp->var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN);
if(strchr(name,'='))
return(nv_getval(np));
_nv_unset(np,0);
}
return("");
}
/*
* Same linker dance as with getenv() above.
*/
char* setenviron(const char *name)
{
return sh_setenviron(name);
}
/*
* normalize <cp> and return pointer to subscript if any
* if <eq> is specified, return pointer to first = not in a subscript
*/
static char *lastdot(char *cp, int eq)
{
register char *ep=0;
register int c;
if(eq)
cp++;
while(c= mbchar(cp))
{
if(c=='[')
{
if(*cp==']')
cp++;
else
cp = nv_endsubscript((Namval_t*)0,ep=cp,0);
}
else if(c=='.')
{
if(*cp=='[')
{
cp = nv_endsubscript((Namval_t*)0,ep=cp,0);
if((ep=sh_checkid(ep+1,cp)) < cp)
cp=strcpy(ep,cp);
}
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_getinterp();
register Namval_t *mp=0,*nr=0;
register char *cp;
int arraynp=0,arraynr,index= -1;
Namval_t *last_table = shp->last_table;
Dt_t *last_root = shp->last_root;
Dt_t *hp = 0;
char *nvenv=0,*prefix=shp->prefix;
Namarr_t *ap;
if(nv_isattr(np,NV_PARAM) && shp->st.prevst)
{
if(!(hp=(Dt_t*)shp->st.prevst->save_tree))
hp = dtvnext(shp->var_tree);
}
if(!nv_isattr(np,NV_MINIMAL))
nvenv = np->nvenv;
if(nvenv || (cp = nv_name(np)) && nv_isarray(np) && cp[strlen(cp)-1] == ']')
arraynp = 1;
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));
arraynr = cp[strlen(cp)-1] == ']';
if(nv_isarray(np) && !(mp=nv_opensub(np)))
index=nv_aindex(np);
shp->prefix = 0;
if(!hp)
hp = shp->var_tree;
if(!(nr = nv_open(cp, hp, flags|NV_ARRAY|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
{
#if SHOPT_NAMESPACE
if(shp->namespace)
hp = nv_dict(shp->namespace);
else
#endif /* SHOPT_NAMESPACE */
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));
shp->prefix = prefix;
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))
{
if(ap = nv_arrayptr(np))
ap->nelem++;
mp->nvenv = nvenv = (void*)np;
}
}
if(mp)
{
nvenv = (char*)np;
np = mp;
}
if(nr==np)
{
if(index<0)
return(0);
if(cp = nv_getval(np))
cp = strdup(cp);
}
_nv_unset(np,NV_EXPORT);
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;
if(flags&NV_MOVE)
{
if(arraynp && !nv_isattr(np,NV_MINIMAL) && (mp=(Namval_t*)np->nvenv) && (ap=nv_arrayptr(mp)) && !ap->fun)
ap->nelem++;
}
if((nv_arrayptr(nr) && !arraynr) || nv_isvtree(nr))
{
if(ap=nv_arrayptr(np))
{
if(!ap->table)
ap->table = dtopen(&_Nvdisc,Dtoset);
if(ap->table)
mp = nv_search(nv_getsub(np),ap->table,NV_ADD);
nv_arraychild(np,mp,0);
nvenv = (void*)np;
}
else
mp = np;
nv_clone(nr,mp,(flags&NV_MOVE)|NV_COMVAR);
mp->nvenv = nvenv;
if(flags&NV_MOVE)
{
if(arraynr && !nv_isattr(nr,NV_MINIMAL) && (mp=(Namval_t*)nr->nvenv) && (ap=nv_arrayptr(mp)))
{
nv_putsub(mp,nr->nvname,0);
_nv_unset(mp,0);
}
nv_delete(nr,(Dt_t*)0,NV_NOFREE);
}
}
else
{
nv_putval(np,nv_getval(nr),0);
if(flags&NV_MOVE)
{
if(!nv_isattr(nr,NV_MINIMAL) && (mp=(Namval_t*)(nr->nvenv)) && (ap=nv_arrayptr(mp)))
ap->nelem--;
_nv_unset(nr,0);
}
}
return(1);
}
/*
* Create a reference node from <np> to $np in dictionary <hp>
*/
void nv_setref(register Namval_t *np, Dt_t *hp, int flags)
{
Shell_t *shp = sh_getinterp();
register Namval_t *nq=0, *nr=0;
register char *ep,*cp;
Dt_t *root = shp->last_root, *hpnext=0;
Namarr_t *ap=0;
Dt_t *openmatch;
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,0);
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)
hpnext = dtvnext(hp);
if((nr=nv_open(cp, hp?hp:shp->var_tree, flags|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
nq = nr;
else if(hpnext && dtvnext(hpnext)==shp->var_base && (nr=nv_open(cp,hpnext,flags|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
nq = nr;
else if((openmatch=shp->openmatch) && hpnext==shp->var_base && (nr=nv_open(cp,hpnext,flags|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
nq = nr;
if(nq)
hp = shp->last_root;
else
hp = hp?(openmatch?openmatch:shp->var_base):shp->var_tree;
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(nv_isarray(nq))
nv_putsub(nq,(char*)0,ARRAY_UNDEF);
}
#if SHOPT_FIXEDARRAY
if(nq && ep && nv_isarray(nq) && !((ap=nv_arrayptr(nq)) && ap->fixed) && !nv_getsub(nq))
#else
if(nq && ep && nv_isarray(nq) && !nv_getsub(nq))
#endif /* SHOPT_FIXEDARRAY */
{
if(!nv_arrayptr(nq))
{
nv_putsub(nq,"1",ARRAY_FILL);
_nv_unset(nq,NV_RDONLY);
}
nv_endsubscript(nq,ep-1,NV_ARRAY);
}
if(!nr)
{
shp->last_root = 0;
nr= nq = nv_open(cp, hp, flags);
if(shp->last_root)
hp = shp->last_root;
}
if(shp->last_root == shp->var_tree && root!=shp->var_tree)
{
_nv_unset(np,NV_RDONLY);
nv_onattr(np,NV_REF);
errormsg(SH_DICT,ERROR_exit(1),e_globalref,nv_name(np));
}
shp->instance = 1;
if(nq && !ep && (ap=nv_arrayptr(nq)) && !(ap->nelem&(ARRAY_UNDEF|ARRAY_SCAN)))
ep = nv_getsub(nq);
#if SHOPT_FIXEDARRAY
if(ep && !(ap && ap->fixed))
#else
if(ep)
#endif /* SHOPT_FIXEDARRAY */
{
/* cause subscript evaluation and return result */
if(nv_isarray(nq))
ep = nv_getsub(nq);
else
{
int n;
ep[n=strlen(ep)-1] = 0;
nv_putsub(nr, ep, ARRAY_FILL);
ep[n] = ']';
if(nq = nv_opensub(nr))
ep = 0;
else
ep = nv_getsub(nq=nr);
}
}
shp->instance = 0;
shp->last_root = root;
_nv_unset(np,0);
nv_delete(np,(Dt_t*)0,0);
np->nvalue.nrp = newof(0,struct Namref,1,sizeof(Dtlink_t));
np->nvalue.nrp->np = nq;
np->nvalue.nrp->root = hp;
if(ep)
{
#if SHOPT_FIXEDARRAY
if(ap && ap->fixed)
np->nvalue.nrp->curi = ARRAY_FIXED|nv_arrfixed(nq,(Sfio_t*)0,1,&np->nvalue.nrp->dim);
else
#endif /* SHOPT_FIXEDARRAY */
np->nvalue.nrp->sub = strdup(ep);
}
np->nvalue.nrp->table = shp->last_table;
nv_onattr(np,NV_REF|NV_NOFREE);
if(!Refdict)
{
NullNode.nvname = ".deleted";
NullNode.nvflag = NV_RDONLY;
Refdict = dtopen(&_Refdisc,Dtobag);
}
dtinsert(Refdict,np->nvalue.nrp);
}
/*
* get the scope corresponding to <index>
* whence uses the same values as lseek()
*/
Shscope_t *sh_getscope(int index, int whence)
{
Shell_t *shp = sh_getinterp();
register struct sh_scoped *sp, *topmost;
if(whence==SEEK_CUR)
sp = &shp->st;
else
{
if ((struct sh_scoped*)shp->topscope != shp->st.self)
topmost = (struct sh_scoped*)shp->topscope;
else
topmost = &(shp->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 <scoped> the top scope and return previous scope
*/
Shscope_t *sh_setscope(Shscope_t *scope)
{
Shell_t *shp = sh_getinterp();
Shscope_t *old = (Shscope_t*)shp->st.self;
*shp->st.self = shp->st;
shp->st = *((struct sh_scoped*)scope);
shp->var_tree = scope->var_tree;
SH_PATHNAMENOD->nvalue.cp = shp->st.filename;
SH_FUNNAMENOD->nvalue.cp = shp->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);
if(dp)
{
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;
nv_offattr(np,NV_NOFREE|NV_REF);
if(!np->nvalue.nrp)
return;
nq = nv_refnode(np);
if(Refdict)
{
if(np->nvalue.nrp->sub)
free(np->nvalue.nrp->sub);
dtdelete(Refdict,(void*)np->nvalue.nrp);
}
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
}
char *nv_name(register Namval_t *np)
{
Shell_t *shp = sh_getinterp();
register Namval_t *table;
register Namfun_t *fp;
#if SHOPT_FIXEDARRAY
Namarr_t *ap;
#endif /* SHOPT_FIXEDARRAY */
char *cp;
if(is_abuiltin(np) || is_afunction(np))
{
#if SHOPT_NAMESPACE
if(shp->namespace && is_afunction(np))
{
char *name = nv_name(shp->namespace);
int n = strlen(name);
if(memcmp(np->nvname,name,n)==0 && np->nvname[n]=='.')
return(np->nvname+n+1);
}
#endif /* SHOPT_NAMESPACE */
return(np->nvname);
}
#if SHOPT_FIXEDARRAY
ap = nv_arrayptr(np);
#endif /* SHOPT_FIXEDARRAY */
if(!nv_isattr(np,NV_MINIMAL|NV_EXPORT) && np->nvenv)
{
Namval_t *nq= shp->last_table, *mp= (Namval_t*)np->nvenv;
if(np==shp->last_table)
shp->last_table = 0;
if(nv_isarray(mp))
sfprintf(shp->strbuf,"%s[%s]",nv_name(mp),np->nvname);
else
sfprintf(shp->strbuf,"%s.%s",nv_name(mp),np->nvname);
shp->last_table = nq;
return(sfstruse(shp->strbuf));
}
if(nv_istable(np))
shp->last_table = nv_parent(np);
else if(!nv_isref(np))
{
for(fp= np->nvfun ; fp; fp=fp->next)
if(fp->disc && fp->disc->namef)
{
if(np==shp->last_table)
shp->last_table = 0;
return((*fp->disc->namef)(np,fp));
}
}
if(!(table=shp->last_table) || *np->nvname=='.' || table==shp->namespace || np==table)
{
#if SHOPT_FIXEDARRAY
if(!ap || !ap->fixed || (ap->nelem&ARRAY_UNDEF))
return(np->nvname);
table = 0;
#else
return(np->nvname);
#endif /* SHOPT_FIXEDARRAY */
}
if(table)
{
cp = nv_name(table);
sfprintf(shp->strbuf,"%s.%s",cp,np->nvname);
}
else
sfprintf(shp->strbuf,"%s",np->nvname);
#if SHOPT_FIXEDARRAY
if(ap && ap->fixed)
nv_arrfixed(np,shp->strbuf,1,(char*)0);
#endif /* SHOPT_FIXEDARRAY */
return(sfstruse(shp->strbuf));
}
Namval_t *nv_lastdict(void)
{
Shell_t *shp = sh_getinterp();
return(shp->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);
}
#undef nv_unset
void nv_unset(register Namval_t *np)
{
_nv_unset(np,0);
return;
}