/***********************************************************************
* *
* 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
/*
* export [-p] [arg...]
* readonly [-p] [arg...]
* typeset [options] [arg...]
* alias [-ptx] [arg...]
* unalias [arg...]
* builtin [-sd] [-f file] [name...]
* set [options] [name...]
* unset [-fnv] [name...]
*
* David Korn
* AT&T Labs
*
*/
#include "defs.h"
#include <error.h>
#include "path.h"
#include "name.h"
#include "history.h"
#include "builtins.h"
#include "variables.h"
#include "FEATURE/dynamic"
struct tdata
{
Shell_t *sh;
Namval_t *tp;
const char *wctname;
Sfio_t *outfile;
char *prefix;
char *tname;
char *help;
short aflag;
short pflag;
int argnum;
int scanmask;
Dt_t *scanroot;
char **argnam;
int indent;
int noref;
};
static int print_namval(Sfio_t*, Namval_t*, int, struct tdata*);
static void print_attribute(Namval_t*,void*);
static void print_all(Sfio_t*, Dt_t*, struct tdata*);
static void print_scan(Sfio_t*, int, Dt_t*, int, struct tdata*);
static int unall(int, char**, Dt_t*, Shell_t*);
static int setall(char**, int, Dt_t*, struct tdata*);
static void pushname(Namval_t*,void*);
static void(*nullscan)(Namval_t*,void*);
static Namval_t *load_class(const char *name)
{
errormsg(SH_DICT,ERROR_exit(1),"%s: type not loadable",name);
return(0);
}
/*
* Note export and readonly are the same
*/
#if 0
/* for the dictionary generator */
int b_export(int argc,char *argv[],Shbltin_t *context){}
#endif
int b_readonly(int argc,char *argv[],Shbltin_t *context)
{
register int flag;
char *command = argv[0];
struct tdata tdata;
NOT_USED(argc);
memset((void*)&tdata,0,sizeof(tdata));
tdata.sh = context->shp;
tdata.aflag = '-';
while((flag = optget(argv,*command=='e'?sh_optexport:sh_optreadonly))) switch(flag)
{
case 'p':
tdata.prefix = command;
break;
case ':':
errormsg(SH_DICT,2, "%s", opt_info.arg);
break;
case '?':
errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
return(2);
}
if(error_info.errors)
errormsg(SH_DICT,ERROR_usage(2),optusage(NIL(char*)));
argv += (opt_info.index-1);
if(*command=='r')
flag = (NV_ASSIGN|NV_RDONLY|NV_VARNAME);
#ifdef _ENV_H
else if(!argv[1])
{
char *cp,**env=env_get(tdata.sh->env);
while(cp = *env++)
{
if(tdata.prefix)
sfputr(sfstdout,tdata.prefix,' ');
sfprintf(sfstdout,"%s\n",sh_fmtq(cp));
}
return(0);
}
#endif
else
{
flag = (NV_ASSIGN|NV_EXPORT|NV_IDENT);
if(!tdata.sh->prefix)
tdata.sh->prefix = "";
}
return(setall(argv,flag,tdata.sh->var_tree, &tdata));
}
int b_alias(int argc,register char *argv[],Shbltin_t *context)
{
register unsigned flag = NV_NOARRAY|NV_NOSCOPE|NV_ASSIGN;
register Dt_t *troot;
register int n;
struct tdata tdata;
NOT_USED(argc);
memset((void*)&tdata,0,sizeof(tdata));
tdata.sh = context->shp;
troot = tdata.sh->alias_tree;
if(*argv[0]=='h')
flag = NV_TAGGED;
if(argv[1])
{
opt_info.offset = 0;
opt_info.index = 1;
*opt_info.option = 0;
tdata.argnum = 0;
tdata.aflag = *argv[1];
while((n = optget(argv,sh_optalias))) switch(n)
{
case 'p':
tdata.prefix = argv[0];
break;
case 't':
flag |= NV_TAGGED;
break;
case 'x':
flag |= NV_EXPORT;
break;
case ':':
errormsg(SH_DICT,2, "%s", opt_info.arg);
break;
case '?':
errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
return(2);
}
if(error_info.errors)
errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
argv += (opt_info.index-1);
if(flag&NV_TAGGED)
{
/* hacks to handle hash -r | -- */
if(argv[1] && argv[1][0]=='-')
{
if(argv[1][1]=='r' && argv[1][2]==0)
{
Namval_t *np = nv_search((char*)PATHNOD,tdata.sh->var_tree,HASH_BUCKET);
nv_putval(np,nv_getval(np),NV_RDONLY);
argv++;
if(!argv[1])
return(0);
}
if(argv[1][0]=='-')
{
if(argv[1][1]=='-' && argv[1][2]==0)
argv++;
else
errormsg(SH_DICT, ERROR_exit(1), e_option, argv[1]);
}
}
troot = tdata.sh->track_tree;
}
}
return(setall(argv,flag,troot,&tdata));
}
#if 0
/* for the dictionary generator */
int b_local(int argc,char *argv[],Shbltin_t *context){}
#endif
int b_typeset(int argc,register char *argv[],Shbltin_t *context)
{
register int n, flag = NV_VARNAME|NV_ASSIGN;
struct tdata tdata;
const char *optstring = sh_opttypeset;
Namdecl_t *ntp = (Namdecl_t*)context->ptr;
Dt_t *troot;
int isfloat=0, shortint=0, sflag=0;
NOT_USED(argc);
memset((void*)&tdata,0,sizeof(tdata));
tdata.sh = context->shp;
if(ntp)
{
tdata.tp = ntp->tp;
opt_info.disc = (Optdisc_t*)ntp->optinfof;
optstring = ntp->optstring;
}
troot = tdata.sh->var_tree;
while((n = optget(argv,optstring)))
{
if(tdata.aflag==0)
tdata.aflag = *opt_info.option;
switch(n)
{
case 'a':
flag |= NV_IARRAY;
if(opt_info.arg && *opt_info.arg!='[')
{
opt_info.index--;
goto endargs;
}
tdata.tname = opt_info.arg;
break;
case 'A':
flag |= NV_ARRAY;
break;
case 'C':
flag |= NV_COMVAR;
break;
case 'E':
/* The following is for ksh88 compatibility */
if(opt_info.offset && !strchr(argv[opt_info.index],'E'))
{
tdata.argnum = (int)opt_info.num;
break;
}
case 'F':
case 'X':
if(!opt_info.arg || (tdata.argnum = opt_info.num) <0)
tdata.argnum = (n=='X'?2*sizeof(Sfdouble_t):10);
isfloat = 1;
if(n=='E')
{
flag &= ~NV_HEXFLOAT;
flag |= NV_EXPNOTE;
}
else if(n=='X')
{
flag &= ~NV_EXPNOTE;
flag |= NV_HEXFLOAT;
}
break;
case 'b':
flag |= NV_BINARY;
break;
case 'm':
flag |= NV_MOVE;
break;
case 'n':
flag &= ~NV_VARNAME;
flag |= (NV_REF|NV_IDENT);
break;
case 'H':
flag |= NV_HOST;
break;
case 'T':
flag |= NV_TYPE;
tdata.prefix = opt_info.arg;
break;
case 'L': case 'Z': case 'R':
if(tdata.argnum==0)
tdata.argnum = (int)opt_info.num;
if(tdata.argnum < 0)
errormsg(SH_DICT,ERROR_exit(1), e_badfield, tdata.argnum);
if(n=='Z')
flag |= NV_ZFILL;
else
{
flag &= ~(NV_LJUST|NV_RJUST);
flag |= (n=='L'?NV_LJUST:NV_RJUST);
}
break;
case 'M':
if((tdata.wctname = opt_info.arg) && !nv_mapchar((Namval_t*)0,tdata.wctname))
errormsg(SH_DICT, ERROR_exit(1),e_unknownmap, tdata.wctname);
if(tdata.wctname && strcmp(tdata.wctname,e_tolower)==0)
flag |= NV_UTOL;
else
flag |= NV_LTOU;
if(!tdata.wctname)
flag |= NV_UTOL;
break;
case 'f':
flag &= ~(NV_VARNAME|NV_ASSIGN);
troot = tdata.sh->fun_tree;
break;
case 'i':
if(!opt_info.arg || (tdata.argnum = opt_info.num) <0)
tdata.argnum = 10;
flag |= NV_INTEGER;
break;
case 'l':
tdata.wctname = e_tolower;
flag |= NV_UTOL;
break;
case 'p':
tdata.prefix = argv[0];
tdata.pflag = 1;
flag &= ~NV_ASSIGN;
break;
case 'r':
flag |= NV_RDONLY;
break;
#ifdef SHOPT_TYPEDEF
case 'S':
sflag=1;
break;
case 'h':
tdata.help = opt_info.arg;
break;
#endif /*SHOPT_TYPEDEF*/
case 's':
shortint=1;
break;
case 't':
flag |= NV_TAGGED;
break;
case 'u':
tdata.wctname = e_toupper;
flag |= NV_LTOU;
break;
case 'x':
flag &= ~NV_VARNAME;
flag |= (NV_EXPORT|NV_IDENT);
break;
case ':':
errormsg(SH_DICT,2, "%s", opt_info.arg);
break;
case '?':
errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
opt_info.disc = 0;
return(2);
}
}
endargs:
argv += opt_info.index;
opt_info.disc = 0;
/* handle argument of + and - specially */
if(*argv && argv[0][1]==0 && (*argv[0]=='+' || *argv[0]=='-'))
tdata.aflag = *argv[0];
else
argv--;
if((flag&NV_ZFILL) && !(flag&NV_LJUST))
flag |= NV_RJUST;
if((flag&NV_INTEGER) && (flag&(NV_LJUST|NV_RJUST|NV_ZFILL)))
error_info.errors++;
if((flag&NV_BINARY) && (flag&(NV_LJUST|NV_UTOL|NV_LTOU)))
error_info.errors++;
if((flag&NV_MOVE) && (flag&~(NV_MOVE|NV_VARNAME|NV_ASSIGN)))
error_info.errors++;
if((flag&NV_REF) && (flag&~(NV_REF|NV_IDENT|NV_ASSIGN)))
error_info.errors++;
if((flag&NV_TYPE) && (flag&~(NV_TYPE|NV_VARNAME|NV_ASSIGN)))
error_info.errors++;
if(troot==tdata.sh->fun_tree && ((isfloat || flag&~(NV_FUNCT|NV_TAGGED|NV_EXPORT|NV_LTOU))))
error_info.errors++;
if(sflag && troot==tdata.sh->fun_tree)
{
/* static function */
sflag = 0;
flag |= NV_STATICF;
}
if(error_info.errors)
errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*)));
if(sizeof(char*)<8 && tdata.argnum > SHRT_MAX)
errormsg(SH_DICT,ERROR_exit(2),"option argument cannot be greater than %d",SHRT_MAX);
if(isfloat)
flag |= NV_DOUBLE;
if(shortint)
{
flag &= ~NV_LONG;
flag |= NV_SHORT|NV_INTEGER;
}
if(sflag)
{
if(tdata.sh->mktype)
flag |= NV_REF|NV_TAGGED;
else if(!tdata.sh->typeinit)
flag |= NV_STATIC|NV_IDENT;
}
if(tdata.sh->fn_depth && !tdata.pflag)
flag |= NV_NOSCOPE;
if(tdata.help)
tdata.help = strdup(tdata.help);
if(flag&NV_TYPE)
{
Stk_t *stkp = tdata.sh->stk;
int off=0,offset = stktell(stkp);
if(!tdata.prefix)
return(sh_outtype(tdata.sh,sfstdout));
sfputr(stkp,NV_CLASS,-1);
#if SHOPT_NAMESPACE
if(tdata.sh->namespace)
{
off = stktell(stkp)+1;
sfputr(stkp,nv_name(tdata.sh->namespace),'.');
}
else
#endif /* SHOPT_NAMESPACE */
if(NV_CLASS[sizeof(NV_CLASS)-2]!='.')
sfputc(stkp,'.');
sfputr(stkp,tdata.prefix,0);
tdata.tp = nv_open(stkptr(stkp,offset),tdata.sh->var_tree,NV_VARNAME|NV_NOARRAY|NV_NOASSIGN);
#if SHOPT_NAMESPACE
if(!tdata.tp && off)
{
*stkptr(stkp,off)=0;
tdata.tp = nv_open(stkptr(stkp,offset),tdata.sh->var_tree,NV_VARNAME|NV_NOARRAY|NV_NOASSIGN);
}
#endif /* SHOPT_NAMESPACE */
stkseek(stkp,offset);
if(!tdata.tp)
errormsg(SH_DICT,ERROR_exit(1),"%s: unknown type",tdata.prefix);
else if(nv_isnull(tdata.tp))
nv_newtype(tdata.tp);
tdata.tp->nvenv = tdata.help;
flag &= ~NV_TYPE;
if(nv_isattr(tdata.tp,NV_TAGGED))
{
nv_offattr(tdata.tp,NV_TAGGED);
return(0);
}
}
else if(tdata.aflag==0 && ntp && ntp->tp)
tdata.aflag = '-';
if(!tdata.sh->mktype)
tdata.help = 0;
if(tdata.aflag=='+' && (flag&(NV_ARRAY|NV_IARRAY|NV_COMVAR)) && argv[1])
errormsg(SH_DICT,ERROR_exit(1),e_nounattr);
return(setall(argv,flag,troot,&tdata));
}
static void print_value(Sfio_t *iop, Namval_t *np, struct tdata *tp)
{
char *name;
int aflag=tp->aflag;
if(nv_isnull(np))
{
if(!np->nvflag)
return;
aflag = '+';
}
else if(nv_istable(np))
{
Dt_t *root = tp->sh->last_root;
Namval_t *nsp = tp->sh->namespace;
char *cp;
if(!tp->pflag)
return;
cp = name = nv_name(np);
if(*name=='.')
name++;
if(tp->indent)
sfnputc(iop,'\t',tp->indent);
sfprintf(iop,"namespace %s\n", name);
if(tp->indent)
sfnputc(iop,'\t',tp->indent);
sfprintf(iop,"{\n", name);
tp->indent++;
/* output types from namespace */
tp->sh->namespace = 0;
tp->sh->prefix = nv_name(np)+1;
sh_outtype(tp->sh,iop);
tp->sh->prefix = 0;
tp->sh->namespace = np;
tp->sh->last_root = root;
/* output variables from namespace */
print_scan(iop,NV_NOSCOPE,nv_dict(np),aflag=='+',tp);
tp->wctname = cp;
tp->sh->namespace = 0;
/* output functions from namespace */
print_scan(iop,NV_FUNCTION|NV_NOSCOPE,tp->sh->fun_tree,aflag=='+',tp);
tp->wctname = 0;
tp->sh->namespace = nsp;
if(--tp->indent)
sfnputc(iop,'\t',tp->indent);
sfwrite(iop,"}\n",2);
return;
}
sfputr(iop,nv_name(np),aflag=='+'?'\n':'=');
if(aflag=='+')
return;
if(nv_isarray(np) && nv_arrayptr(np))
{
nv_outnode(np,iop,-1,0);
sfwrite(iop,")\n",2);
}
else
{
if(nv_isvtree(np))
nv_onattr(np,NV_EXPORT);
if(!(name = nv_getval(np)))
name = Empty;
if(!nv_isvtree(np))
name = sh_fmtq(name);
sfputr(iop,name,'\n');
}
}
static int setall(char **argv,register int flag,Dt_t *troot,struct tdata *tp)
{
register char *name;
char *last = 0;
int nvflags=(flag&(NV_ARRAY|NV_NOARRAY|NV_VARNAME|NV_IDENT|NV_ASSIGN|NV_STATIC|NV_MOVE));
int r=0, ref=0, comvar=(flag&NV_COMVAR),iarray=(flag&NV_IARRAY);
Shell_t *shp =tp->sh;
if(!shp->prefix)
{
if(!tp->pflag)
nvflags |= NV_NOSCOPE;
}
else if(*shp->prefix==0)
shp->prefix = 0;
if(*argv[0]=='+')
nvflags |= NV_NOADD;
flag &= ~(NV_NOARRAY|NV_NOSCOPE|NV_VARNAME|NV_IDENT|NV_STATIC|NV_COMVAR|NV_IARRAY);
if(argv[1])
{
if(flag&NV_REF)
{
flag &= ~NV_REF;
ref=1;
if(tp->aflag!='-')
nvflags |= NV_NOREF;
}
if(tp->pflag)
nvflags |= (NV_NOREF|NV_NOADD|NV_NOFAIL);
while(name = *++argv)
{
register unsigned newflag;
register Namval_t *np;
Namarr_t *ap;
Namval_t *mp;
unsigned curflag;
if(troot == shp->fun_tree)
{
/*
*functions can be exported or
* traced but not set
*/
flag &= ~NV_ASSIGN;
if(flag&NV_LTOU)
{
/* Function names cannot be special builtin */
if((np=nv_search(name,shp->bltin_tree,0)) && nv_isattr(np,BLT_SPC))
errormsg(SH_DICT,ERROR_exit(1),e_badfun,name);
#if SHOPT_NAMESPACE
if(shp->namespace)
np = sh_fsearch(shp,name,NV_ADD|HASH_NOSCOPE);
else
#endif /* SHOPT_NAMESPACE */
np = nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE);
}
else
{
if(shp->prefix)
{
sfprintf(shp->strbuf,"%s.%s%c",shp->prefix,name,0);
name = sfstruse(shp->strbuf);
}
#if SHOPT_NAMESPACE
np = 0;
if(shp->namespace)
np = sh_fsearch(shp,name,HASH_NOSCOPE);
if(!np)
#endif /* SHOPT_NAMESPACE */
if(np=nv_search(name,troot,0))
{
if(!is_afunction(np))
np = 0;
}
else if(memcmp(name,".sh.math.",9)==0 && sh_mathstd(name+9))
continue;
}
if(np && ((flag&NV_LTOU) || !nv_isnull(np) || nv_isattr(np,NV_LTOU)))
{
if(flag==0 && !tp->help)
{
print_namval(sfstdout,np,tp->aflag=='+',tp);
continue;
}
if(shp->subshell && !shp->subshare)
sh_subfork();
if(tp->aflag=='-')
nv_onattr(np,flag|NV_FUNCTION);
else if(tp->aflag=='+')
nv_offattr(np,flag);
}
else
r++;
if(tp->help)
{
int offset = stktell(shp->stk);
if(!np)
{
sfputr(shp->stk,shp->prefix,'.');
sfputr(shp->stk,name,0);
np = nv_search(stkptr(shp->stk,offset),troot,0);
stkseek(shp->stk,offset);
}
if(np && np->nvalue.cp)
np->nvalue.rp->help = tp->help;
}
continue;
}
/* tracked alias */
if(troot==shp->track_tree && tp->aflag=='-')
{
np = nv_search(name,troot,NV_ADD);
path_alias(np,path_absolute(shp,nv_name(np),NIL(Pathcomp_t*)));
continue;
}
np = nv_open(name,troot,nvflags|((nvflags&NV_ASSIGN)?0:NV_ARRAY)|((iarray|(nvflags&(NV_REF|NV_NOADD)==NV_REF))?NV_FARRAY:0));
if(!np)
continue;
if(nv_isnull(np) && !nv_isarray(np) && nv_isattr(np,NV_NOFREE))
nv_offattr(np,NV_NOFREE);
else if(tp->tp && !nv_isattr(np,NV_MINIMAL|NV_EXPORT) && (mp=(Namval_t*)np->nvenv) && (ap=nv_arrayptr(mp)) && (ap->nelem&ARRAY_TREE))
errormsg(SH_DICT,ERROR_exit(1),e_typecompat,nv_name(np));
else if((ap=nv_arrayptr(np)) && nv_aindex(np)>0 && ap->nelem==1 && nv_getval(np)==Empty)
{
ap->nelem++;
_nv_unset(np,0);
ap->nelem--;
}
else if(iarray && ap && ap->fun)
errormsg(SH_DICT,ERROR_exit(1),"cannot change associative array %s to index array",nv_name(np));
else if( (iarray||(flag&NV_ARRAY)) && nv_isvtree(np) && !nv_type(np))
_nv_unset(np,NV_EXPORT);
if(tp->pflag)
{
if(!nv_istable(np))
nv_attribute(np,sfstdout,tp->prefix,1);
print_value(sfstdout,np,tp);
continue;
}
if(flag==NV_ASSIGN && !ref && tp->aflag!='-' && !strchr(name,'='))
{
if(troot!=shp->var_tree && (nv_isnull(np) || !print_namval(sfstdout,np,0,tp)))
{
sfprintf(sfstderr,sh_translate(e_noalias),name);
r++;
}
if(!comvar && !iarray)
continue;
}
if(!nv_isarray(np) && !strchr(name,'=') && !(shp->envlist && nv_onlist(shp->envlist,name)))
{
if(comvar || (shp->last_root==shp->var_tree && (tp->tp || (!shp->st.real_fun && (nvflags&NV_STATIC)) || (!(flag&(NV_EXPORT|NV_RDONLY)) && nv_isattr(np,(NV_EXPORT|NV_IMPORT))==(NV_EXPORT|NV_IMPORT)))))
{
_nv_unset(np,0);
}
}
if(troot==shp->var_tree)
{
if(iarray)
{
if(tp->tname)
nv_atypeindex(np,tp->tname+1);
else if(nv_isnull(np))
nv_onattr(np,NV_ARRAY|(comvar?NV_NOFREE:0));
else
{
if(ap && comvar)
ap->nelem |= ARRAY_TREE;
nv_putsub(np, (char*)0, 0);
}
}
else if(nvflags&NV_ARRAY)
{
if(comvar)
{
Namarr_t *ap=nv_arrayptr(np);
if(ap)
ap->nelem |= ARRAY_TREE;
else
{
_nv_unset(np,NV_RDONLY);
nv_onattr(np,NV_NOFREE);
}
}
nv_setarray(np,nv_associative);
}
else if(comvar && !nv_isvtree(np) && !nv_rename(np,flag|NV_COMVAR))
nv_setvtree(np);
}
if(flag&NV_MOVE)
{
nv_rename(np, flag);
nv_close(np);
continue;
}
if(tp->tp && nv_type(np)!=tp->tp)
{
nv_settype(np,tp->tp,tp->aflag=='-'?0:NV_APPEND);
flag = (np->nvflag&NV_NOCHANGE);
}
flag &= ~NV_ASSIGN;
if(last=strchr(name,'='))
*last = 0;
if (shp->typeinit)
continue;
curflag = np->nvflag;
if(!(flag&NV_INTEGER) && (flag&(NV_LTOU|NV_UTOL)))
{
Namfun_t *fp;
char *cp;
if(!tp->wctname)
errormsg(SH_DICT,ERROR_exit(1),e_mapchararg,nv_name(np));
cp = (char*)nv_mapchar(np,0);
if(fp=nv_mapchar(np,tp->wctname))
{
if(tp->aflag=='+')
{
if(cp && strcmp(cp,tp->wctname)==0)
{
nv_disc(np,fp,NV_POP);
if(!(fp->nofree&1))
free((void*)fp);
nv_offattr(np,flag&(NV_LTOU|NV_UTOL));
}
}
else if(!cp || strcmp(cp,tp->wctname))
{
nv_disc(np,fp,NV_LAST);
nv_onattr(np,flag&(NV_LTOU|NV_UTOL));
}
}
}
if (tp->aflag == '-')
{
if((flag&NV_EXPORT) && (strchr(name,'.') || nv_isvtree(np)))
errormsg(SH_DICT,ERROR_exit(1),e_badexport,name);
#if SHOPT_BSH
if(flag&NV_EXPORT)
nv_offattr(np,NV_IMPORT);
#endif /* SHOPT_BSH */
newflag = curflag;
if(flag&~NV_NOCHANGE)
newflag &= NV_NOCHANGE;
newflag |= flag;
if (flag & (NV_LJUST|NV_RJUST))
{
if(!(flag&NV_RJUST))
newflag &= ~NV_RJUST;
else if(!(flag&NV_LJUST))
newflag &= ~NV_LJUST;
}
}
else
{
if((flag&NV_RDONLY) && (curflag&NV_RDONLY))
errormsg(SH_DICT,ERROR_exit(1),e_readonly,nv_name(np));
newflag = curflag & ~flag;
}
if (tp->aflag && (tp->argnum>0 || (curflag!=newflag)))
{
if(shp->subshell)
sh_assignok(np,1);
if(troot!=shp->var_tree)
nv_setattr(np,newflag&~NV_ASSIGN);
else
{
char *oldname=0;
int len=strlen(name);
if(tp->argnum==1 && newflag==NV_INTEGER && nv_isattr(np,NV_INTEGER))
tp->argnum = 10;
if(np->nvfun && !nv_isarray(np) && name[len-1]=='.')
newflag |= NV_NODISC;
nv_newattr (np, newflag&~NV_ASSIGN,tp->argnum);
if(oldname)
np->nvname = oldname;
}
}
if(tp->help && !nv_isattr(np,NV_MINIMAL|NV_EXPORT))
{
np->nvenv = tp->help;
nv_onattr(np,NV_EXPORT);
}
if(last)
*last = '=';
/* set or unset references */
if(ref)
{
if(tp->aflag=='-')
{
Dt_t *hp=0;
if(nv_isattr(np,NV_PARAM) && shp->st.prevst)
{
if(!(hp=(Dt_t*)shp->st.prevst->save_tree))
hp = dtvnext(shp->var_tree);
}
if(tp->sh->mktype)
nv_onattr(np,NV_REF|NV_FUNCT);
else
nv_setref(np,hp,NV_VARNAME);
}
else
nv_unref(np);
}
nv_close(np);
}
}
else
{
if(shp->prefix)
errormsg(SH_DICT,2, e_subcomvar,shp->prefix);
if(tp->aflag)
{
if(troot==shp->fun_tree)
{
flag |= NV_FUNCTION;
tp->prefix = 0;
}
else if(troot==shp->var_tree)
{
flag |= (nvflags&NV_ARRAY);
if(iarray)
flag |= NV_ARRAY|NV_IARRAY;
if(comvar)
flag |= NV_TABLE;
if(!(flag&~NV_ASSIGN))
tp->noref = 1;
}
if((flag&(NV_UTOL|NV_LTOU)) ==(NV_UTOL|NV_LTOU))
{
print_scan(sfstdout,flag&~NV_UTOL,troot,tp->aflag=='+',tp);
flag &= ~NV_LTOU;
}
print_scan(sfstdout,flag,troot,tp->aflag=='+',tp);
if(tp->noref)
{
tp->noref = 0;
print_scan(sfstdout,flag|NV_REF,troot,tp->aflag=='+',tp);
}
}
else if(troot==shp->alias_tree)
print_scan(sfstdout,0,troot,0,tp);
else
print_all(sfstdout,troot,tp);
sfsync(sfstdout);
}
return(r);
}
#if SHOPT_DYNAMIC
typedef void (*Libinit_f)(int,void*);
typedef struct Libcomp_s
{
void* dll;
char* lib;
dev_t dev;
ino_t ino;
unsigned int attr;
} Libcomp_t;
#define GROWLIB 4
static Libcomp_t *liblist;
static int nlib;
static int maxlib;
/*
* add library to loaded list
* call (*lib_init)() on first load if defined
* always move to head of search list
* return: 0: already loaded 1: first load
*/
int sh_addlib(Shell_t* shp, void* dll, char* name, Pathcomp_t* pp)
{
register int n;
register int r;
Libinit_f initfn;
Shbltin_t *sp = &shp->bltindata;
sp->nosfio = 0;
for (n = r = 0; n < nlib; n++)
{
if (r)
liblist[n-1] = liblist[n];
else if (liblist[n].dll == dll)
r++;
}
if (r)
nlib--;
else if ((initfn = (Libinit_f)dlllook(dll, "lib_init")))
(*initfn)(0,sp);
if (nlib >= maxlib)
{
maxlib += GROWLIB;
liblist = newof(liblist, Libcomp_t, maxlib+1, 0);
}
liblist[nlib].dll = dll;
liblist[nlib].attr = (sp->nosfio?BLT_NOSFIO:0);
if (name)
liblist[nlib].lib = strdup(name);
if (pp)
{
liblist[nlib].dev = pp->dev;
liblist[nlib].ino = pp->ino;
}
nlib++;
return !r;
}
Shbltin_f sh_getlib(Shell_t* shp, char* sym, Pathcomp_t* pp)
{
register int n;
for (n = 0; n < nlib; n++)
if (liblist[n].ino == pp->ino && liblist[n].dev == pp->dev)
return (Shbltin_f)dlllook(liblist[n].dll, sym);
return 0;
}
#else
int sh_addlib(Shell_t* shp, void* library, char* name, Pathcomp_t* pp)
{
return 0;
}
Shbltin_f sh_getlib(Shell_t* shp, char* name, Pathcomp_t* pp)
{
return 0;
}
#endif /* SHOPT_DYNAMIC */
/*
* add change or list built-ins
* adding builtins requires dlopen() interface
*/
int b_builtin(int argc,char *argv[],Shbltin_t *context)
{
register char *arg=0, *name;
register int n, r=0, flag=0;
register Namval_t *np;
long dlete=0;
struct tdata tdata;
Shbltin_f addr;
Stk_t *stkp;
void *library=0;
char *errmsg;
#ifdef SH_PLUGIN_VERSION
unsigned long ver;
int list = 0;
char path[1024];
#endif
NOT_USED(argc);
memset(&tdata,0,sizeof(tdata));
tdata.sh = context->shp;
stkp = tdata.sh->stk;
if(!tdata.sh->pathlist)
path_absolute(tdata.sh,argv[0],NIL(Pathcomp_t*));
while (n = optget(argv,sh_optbuiltin)) switch (n)
{
case 's':
flag = BLT_SPC;
break;
case 'd':
dlete=1;
break;
case 'f':
#if SHOPT_DYNAMIC
arg = opt_info.arg;
#else
errormsg(SH_DICT,2, "adding built-ins not supported");
error_info.errors++;
#endif /* SHOPT_DYNAMIC */
break;
case 'l':
#ifdef SH_PLUGIN_VERSION
list = 1;
#endif
break;
case ':':
errormsg(SH_DICT,2, "%s", opt_info.arg);
break;
case '?':
errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
break;
}
argv += opt_info.index;
if(error_info.errors)
errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*)));
if(arg || *argv)
{
if(sh_isoption(SH_RESTRICTED))
errormsg(SH_DICT,ERROR_exit(1),e_restricted,argv[-opt_info.index]);
if(sh_isoption(SH_PFSH))
errormsg(SH_DICT,ERROR_exit(1),e_pfsh,argv[-opt_info.index]);
if(tdata.sh->subshell && !tdata.sh->subshare)
sh_subfork();
}
#if SHOPT_DYNAMIC
if(arg)
{
#ifdef SH_PLUGIN_VERSION
if(!(library = dllplugin(SH_ID, arg, NiL, SH_PLUGIN_VERSION, &ver, RTLD_LAZY, path, sizeof(path))))
{
errormsg(SH_DICT,ERROR_exit(0),"%s: %s",arg,dllerror(0));
return(1);
}
if(list)
sfprintf(sfstdout, "%s %08lu %s\n", arg, ver, path);
#else
#if (_AST_VERSION>=20040404)
if(!(library = dllplug(SH_ID,arg,NIL(char*),RTLD_LAZY,NIL(char*),0)))
#else
if(!(library = dllfind(arg,NIL(char*),RTLD_LAZY,NIL(char*),0)))
#endif
{
errormsg(SH_DICT,ERROR_exit(0),"%s: %s",arg,dlerror());
return(1);
}
#endif
sh_addlib(tdata.sh,library,arg,NiL);
}
else
#endif /* SHOPT_DYNAMIC */
if(*argv==0 && !dlete)
{
print_scan(sfstdout, flag, tdata.sh->bltin_tree, 1, &tdata);
return(0);
}
r = 0;
flag = stktell(stkp);
while(arg = *argv)
{
name = path_basename(arg);
sfwrite(stkp,"b_",2);
sfputr(stkp,name,0);
errmsg = 0;
addr = 0;
if(dlete || liblist)
for(n=(nlib?nlib:dlete); --n>=0;)
{
#if SHOPT_DYNAMIC
if(!dlete && !liblist[n].dll)
continue;
if(dlete || (addr = (Shbltin_f)dlllook(liblist[n].dll,stkptr(stkp,flag))))
#else
if(dlete)
#endif /* SHOPT_DYNAMIC */
{
if(np = sh_addbuiltin(arg, addr,pointerof(dlete)))
{
if(dlete || nv_isattr(np,BLT_SPC))
errmsg = "restricted name";
#if SHOPT_DYNAMIC
else
nv_onattr(np,liblist[n].attr);
#endif /* SHOPT_DYNAMIC */
}
break;
}
}
if(!addr && (np = nv_search(arg,context->shp->bltin_tree,0)))
{
if(nv_isattr(np,BLT_SPC))
errmsg = "restricted name";
addr = (Shbltin_f)np->nvalue.bfp;
}
if(!dlete && !addr && !(np=sh_addbuiltin(arg,(Shbltin_f)0 ,0)))
errmsg = "not found";
if(errmsg)
{
errormsg(SH_DICT,ERROR_exit(0),"%s: %s",*argv,errmsg);
r = 1;
}
stkseek(stkp,flag);
argv++;
}
return(r);
}
int b_set(int argc,register char *argv[],Shbltin_t *context)
{
struct tdata tdata;
int was_monitor = sh_isoption(SH_MONITOR);
memset(&tdata,0,sizeof(tdata));
tdata.sh = context->shp;
tdata.prefix=0;
if(argv[1])
{
if(sh_argopts(argc,argv,tdata.sh) < 0)
return(2);
if(sh_isoption(SH_VERBOSE))
sh_onstate(SH_VERBOSE);
else
sh_offstate(SH_VERBOSE);
if(sh_isoption(SH_MONITOR) && !was_monitor)
sh_onstate(SH_MONITOR);
else if(!sh_isoption(SH_MONITOR) && was_monitor)
sh_offstate(SH_MONITOR);
}
else
/*scan name chain and print*/
print_scan(sfstdout,0,tdata.sh->var_tree,0,&tdata);
return(0);
}
/*
* The removing of Shell variable names, aliases, and functions
* is performed here.
* Unset functions with unset -f
* Non-existent items being deleted give non-zero exit status
*/
int b_unalias(int argc,register char *argv[],Shbltin_t *context)
{
Shell_t *shp = context->shp;
return(unall(argc,argv,shp->alias_tree,shp));
}
int b_unset(int argc,register char *argv[],Shbltin_t *context)
{
Shell_t *shp = context->shp;
return(unall(argc,argv,shp->var_tree,shp));
}
static int unall(int argc, char **argv, register Dt_t *troot, Shell_t* shp)
{
register Namval_t *np;
register const char *name;
volatile int r;
Dt_t *dp;
int nflag=0,all=0,isfun,jmpval;
struct checkpt buff;
NOT_USED(argc);
if(troot==shp->alias_tree)
{
name = sh_optunalias;
if(shp->subshell)
troot = sh_subaliastree(0);
}
else
name = sh_optunset;
while(r = optget(argv,name)) switch(r)
{
case 'f':
troot = sh_subfuntree(1);
break;
case 'a':
all=1;
break;
case 'n':
nflag = NV_NOREF;
case 'v':
troot = shp->var_tree;
break;
case ':':
errormsg(SH_DICT,2, "%s", opt_info.arg);
break;
case '?':
errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
return(2);
}
argv += opt_info.index;
if(error_info.errors || (*argv==0 &&!all))
errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
if(!troot)
return(1);
r = 0;
if(troot==shp->var_tree)
nflag |= NV_VARNAME;
else
nflag = NV_NOSCOPE;
if(all)
{
dtclear(troot);
return(r);
}
sh_pushcontext(shp,&buff,1);
while(name = *argv++)
{
jmpval = sigsetjmp(buff.buff,0);
np = 0;
if(jmpval==0)
{
#if SHOPT_NAMESPACE
if(shp->namespace && troot!=shp->var_tree)
np = sh_fsearch(shp,name,nflag?HASH_NOSCOPE:0);
if(!np)
#endif /* SHOPT_NAMESPACE */
np=nv_open(name,troot,NV_NOADD|nflag);
}
else
{
r = 1;
continue;
}
if(np)
{
if(is_abuiltin(np) || nv_isattr(np,NV_RDONLY))
{
if(nv_isattr(np,NV_RDONLY))
errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
r = 1;
continue;
}
isfun = is_afunction(np);
if(troot==shp->var_tree)
{
Namarr_t *ap;
#if SHOPT_FIXEDARRAY
if((ap=nv_arrayptr(np)) && !ap->fixed && name[strlen(name)-1]==']' && !nv_getsub(np))
#else
if(nv_isarray(np) && name[strlen(name)-1]==']' && !nv_getsub(np))
#endif /* SHOPT_FIXEDARRAY */
{
r=1;
continue;
}
if(shp->subshell)
np=sh_assignok(np,0);
}
if(!nv_isnull(np) || nv_size(np) || nv_isattr(np,~(NV_MINIMAL|NV_NOFREE)))
_nv_unset(np,0);
if(troot==shp->var_tree && shp->st.real_fun && (dp=shp->var_tree->walk) && dp==shp->st.real_fun->sdict)
nv_delete(np,dp,NV_NOFREE);
else if(isfun && !(np->nvalue.rp && np->nvalue.rp->running))
nv_delete(np,troot,0);
#if 0
/* causes unsetting local variable to expose global */
else if(shp->var_tree==troot && shp->var_tree!=shp->var_base && nv_search((char*)np,shp->var_tree,HASH_BUCKET|HASH_NOSCOPE))
nv_delete(np,shp->var_tree,0);
#endif
else
nv_close(np);
}
else if(troot==shp->alias_tree)
r = 1;
}
sh_popcontext(shp,&buff);
return(r);
}
/*
* print out the name and value of a name-value pair <np>
*/
static int print_namval(Sfio_t *file,register Namval_t *np,register int flag, struct tdata *tp)
{
register char *cp;
int indent=tp->indent, outname=0, isfun;
sh_sigcheck(tp->sh);
if(flag)
flag = '\n';
if(tp->noref && nv_isref(np))
return(0);
if(nv_isattr(np,NV_NOPRINT|NV_INTEGER)==NV_NOPRINT)
{
if(is_abuiltin(np) && strcmp(np->nvname,".sh.tilde"))
sfputr(file,nv_name(np),'\n');
return(0);
}
if(nv_istable(np))
{
print_value(file,np,tp);
return(0);
}
isfun = is_afunction(np);
if(tp->prefix)
{
outname = (*tp->prefix=='t' && (!nv_isnull(np) || nv_isattr(np,NV_FLOAT|NV_RDONLY|NV_BINARY|NV_RJUST|NV_NOPRINT)));
if(indent && (isfun || outname || *tp->prefix!='t'))
{
sfnputc(file,'\t',indent);
indent = 0;
}
if(!isfun)
{
if(*tp->prefix=='t')
nv_attribute(np,tp->outfile,tp->prefix,tp->aflag);
else
sfputr(file,tp->prefix,' ');
}
}
if(isfun)
{
Sfio_t *iop=0;
char *fname=0;
if(nv_isattr(np,NV_NOFREE))
return(0);
if(!flag && !np->nvalue.ip)
sfputr(file,"typeset -fu",' ');
else if(!flag && !nv_isattr(np,NV_FPOSIX))
sfputr(file,"function",' ');
cp = nv_name(np);
if(tp->wctname)
cp += strlen(tp->wctname)+1;
sfputr(file,cp,-1);
if(nv_isattr(np,NV_FPOSIX))
sfwrite(file,"()",2);
if(np->nvalue.ip && np->nvalue.rp->hoffset>=0)
fname = np->nvalue.rp->fname;
else
flag = '\n';
if(flag)
{
if(tp->pflag && np->nvalue.ip && np->nvalue.rp->hoffset>=0)
sfprintf(file," #line %d %s\n",np->nvalue.rp->lineno,fname?sh_fmtq(fname):"");
else
sfputc(file, '\n');
}
else
{
if(nv_isattr(np,NV_FTMP))
{
fname = 0;
iop = tp->sh->heredocs;
}
else if(fname)
iop = sfopen(iop,fname,"r");
else if(tp->sh->gd->hist_ptr)
iop = (tp->sh->gd->hist_ptr)->histfp;
if(iop && sfseek(iop,(Sfoff_t)np->nvalue.rp->hoffset,SEEK_SET)>=0)
sfmove(iop,file, nv_size(np), -1);
else
flag = '\n';
if(fname)
sfclose(iop);
}
return(nv_size(np)+1);
}
if(nv_arrayptr(np))
{
if(indent)
sfnputc(file,'\t',indent);
print_value(file,np,tp);
return(0);
}
if(nv_isvtree(np))
nv_onattr(np,NV_EXPORT);
if(cp=nv_getval(np))
{
if(indent)
sfnputc(file,'\t',indent);
sfputr(file,nv_name(np),-1);
if(!flag)
flag = '=';
sfputc(file,flag);
if(flag != '\n')
{
if(nv_isref(np) && nv_refsub(np))
{
sfputr(file,sh_fmtq(cp),-1);
sfprintf(file,"[%s]\n", sh_fmtq(nv_refsub(np)));
}
else
#if SHOPT_TYPEDEF
sfputr(file,nv_isvtree(np)?cp:sh_fmtq(cp),'\n');
#else
sfputr(file,sh_fmtq(cp),'\n');
#endif /* SHOPT_TYPEDEF */
}
return(1);
}
else if(outname || (tp->scanmask && tp->scanroot==tp->sh->var_tree))
sfputr(file,nv_name(np),'\n');
return(0);
}
/*
* print attributes at all nodes
*/
static void print_all(Sfio_t *file,Dt_t *root, struct tdata *tp)
{
tp->outfile = file;
nv_scan(root, print_attribute, (void*)tp, 0, 0);
}
/*
* print the attributes of name value pair give by <np>
*/
static void print_attribute(register Namval_t *np,void *data)
{
register struct tdata *dp = (struct tdata*)data;
nv_attribute(np,dp->outfile,dp->prefix,dp->aflag);
}
/*
* print the nodes in tree <root> which have attributes <flag> set
* of <option> is non-zero, no subscript or value is printed.
*/
static void print_scan(Sfio_t *file, int flag, Dt_t *root, int option,struct tdata *tp)
{
register char **argv;
register Namval_t *np;
register int namec;
Namval_t *onp = 0;
char *name=0;
int len;
tp->sh->last_table=0;
flag &= ~NV_ASSIGN;
tp->scanmask = flag&~NV_NOSCOPE;
tp->scanroot = root;
tp->outfile = file;
#if SHOPT_TYPEDEF
if(!tp->prefix && tp->tp)
tp->prefix = nv_name(tp->tp);
#endif /* SHOPT_TYPEDEF */
if(flag&NV_INTEGER)
tp->scanmask |= (NV_DOUBLE|NV_EXPNOTE);
if(flag==NV_LTOU || flag==NV_UTOL)
tp->scanmask |= NV_UTOL|NV_LTOU;
namec = nv_scan(root,nullscan,(void*)tp,tp->scanmask,flag);
argv = tp->argnam = (char**)stkalloc(tp->sh->stk,(namec+1)*sizeof(char*));
namec = nv_scan(root, pushname, (void*)tp, tp->scanmask, flag&~NV_IARRAY);
if(mbcoll())
strsort(argv,namec,strcoll);
if(namec==0 && tp->sh->namespace && nv_dict(tp->sh->namespace)==root)
{
sfnputc(file,'\t',tp->indent);
sfwrite(file,":\n",2);
}
else while(namec--)
{
if((np=nv_search(*argv++,root,0)) && np!=onp && (!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE)))
{
onp = np;
if(name)
{
char *newname = nv_name(np);
if(memcmp(name,newname,len)==0 && newname[len]== '.')
continue;
name = 0;
}
if(flag&NV_ARRAY)
{
if(nv_aindex(np)>=0)
{
if(!(flag&NV_IARRAY))
continue;
}
else if((flag&NV_IARRAY))
continue;
}
tp->scanmask = flag&~NV_NOSCOPE;
tp->scanroot = root;
print_namval(file,np,option,tp);
if(!is_abuiltin(np) && nv_isvtree(np))
{
name = nv_name(np);
len = strlen(name);
}
}
}
}
/*
* add the name of the node to the argument list argnam
*/
static void pushname(Namval_t *np,void *data)
{
struct tdata *tp = (struct tdata*)data;
*tp->argnam++ = nv_name(np);
}