/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1982-2007 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * David Korn * * * ***********************************************************************/ #pragma prototyped /* * * Shell initialization * * David Korn * AT&T Labs * */ #include "defs.h" #include #include #include #include #include "variables.h" #include "path.h" #include "fault.h" #include "name.h" #include "edit.h" #include "jobs.h" #include "io.h" #include "shlex.h" #include "builtins.h" #include "FEATURE/time" #include "FEATURE/dynamic" #include "lexstates.h" #include "version.h" #if SHOPT_MULTIBYTE char e_version[] = "\n@(#)$Id: Version M "SH_RELEASE" $\0\n"; #else char e_version[] = "\n@(#)$Id: Version "SH_RELEASE" $\0\n"; #endif /* SHOPT_MULTIBYTE */ #if SHOPT_BASH extern void bash_init(int); #endif #define RANDMASK 0x7fff #ifndef CLK_TCK # define CLK_TCK 60 #endif /* CLK_TCK */ #ifndef environ extern char **environ; #endif #undef getconf #define getconf(x) strtol(astconf(x,NiL,NiL),NiL,0) struct seconds { Namfun_t hdr; Shell_t *sh; }; struct rand { Namfun_t hdr; Shell_t *sh; int32_t rand_last; }; struct ifs { Namfun_t hdr; Shell_t *sh; Namval_t *ifsnp; }; struct shell { Namfun_t hdr; Shell_t *sh; }; struct match { Namfun_t hdr; char *val; char *rval; int vsize; int nmatch; int lastsub; int match[2*(MATCH_MAX+1)]; }; typedef struct _init_ { Shell_t *sh; #if SHOPT_FS_3D Namfun_t VPATH_init; #endif /* SHOPT_FS_3D */ struct ifs IFS_init; struct shell PATH_init; #ifdef PATH_BFPATH struct shell FPATH_init; struct shell CDPATH_init; #endif struct shell SHELL_init; struct shell ENV_init; struct shell VISUAL_init; struct shell EDITOR_init; struct shell OPTINDEX_init; struct seconds SECONDS_init; struct rand RAND_init; struct shell LINENO_init; struct shell L_ARG_init; struct match SH_MATCH_init; #ifdef _hdr_locale struct shell LC_TYPE_init; struct shell LC_NUM_init; struct shell LC_COLL_init; struct shell LC_MSG_init; struct shell LC_ALL_init; struct shell LANG_init; #endif /* _hdr_locale */ } Init_t; static void env_init(Shell_t*); static Init_t *nv_init(Shell_t*); static Dt_t *inittree(Shell_t*,const struct shtable2*); #ifdef _WINIX # define EXE "?(.exe)" #else # define EXE #endif static int rand_shift; /* * Invalidate all path name bindings */ static void rehash(register Namval_t *np,void *data) { NOT_USED(data); nv_onattr(np,NV_NOALIAS); } /* * out of memory routine for stak routines */ static char *nospace(int unused) { NOT_USED(unused); errormsg(SH_DICT,ERROR_exit(3),e_nospace); return(NIL(char*)); } /* Trap for VISUAL and EDITOR variables */ static void put_ed(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { register const char *cp, *name=nv_name(np); if(*name=='E' && nv_getval(nv_scoped(VISINOD))) goto done; sh_offoption(SH_VI); sh_offoption(SH_EMACS); sh_offoption(SH_GMACS); if(!(cp=val) && (*name=='E' || !(cp=nv_getval(nv_scoped(EDITNOD))))) goto done; /* turn on vi or emacs option if editor name is either*/ cp = path_basename(cp); if(strmatch(cp,"*[Vv][Ii]*")) sh_onoption(SH_VI); else if(strmatch(cp,"*gmacs*")) sh_onoption(SH_GMACS); else if(strmatch(cp,"*macs*")) sh_onoption(SH_EMACS); done: nv_putv(np, val, flags, fp); } /* Trap for OPTINDEX */ static void put_optindex(Namval_t* np,const char *val,int flags,Namfun_t *fp) { Shell_t *shp = ((struct shell*)fp)->sh; shp->st.opterror = shp->st.optchar = 0; nv_putv(np, val, flags, fp); } static Sfdouble_t nget_optindex(register Namval_t* np, Namfun_t *fp) { return((Sfdouble_t)*np->nvalue.lp); } /* Trap for restricted variables FPATH, PATH, SHELL, ENV */ static void put_restricted(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { Shell_t *shp = ((struct shell*)fp)->sh; int path_scoped = 0; #ifdef PATH_BFPATH Pathcomp_t *pp; char *name = nv_name(np); #endif if(!(flags&NV_RDONLY) && sh_isoption(SH_RESTRICTED)) errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np)); if(np==PATHNOD || (path_scoped=(strcmp(name,PATHNOD->nvname)==0))) { #ifndef PATH_BFPATH shp->lastpath = 0; #endif nv_scan(shp->track_tree,rehash,(void*)0,NV_TAGGED,NV_TAGGED); if(path_scoped && !val) val = PATHNOD->nvalue.cp; } if(val && !(flags&NV_RDONLY) && np->nvalue.cp && strcmp(val,np->nvalue.cp)==0) return; #ifdef PATH_BFPATH if(shp->pathlist && np==FPATHNOD) shp->pathlist = (void*)path_unsetfpath((Pathcomp_t*)shp->pathlist); #endif nv_putv(np, val, flags, fp); #ifdef PATH_BFPATH if(shp->pathlist) { val = np->nvalue.cp; if(np==PATHNOD || path_scoped) pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_PATH); else if(val && np==FPATHNOD) pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_FPATH); else return; if(shp->pathlist = (void*)pp) pp->shp = shp; if(!val && (flags&NV_NOSCOPE)) { Namval_t *mp = dtsearch(shp->var_tree,np); if(mp && (val=nv_getval(mp))) nv_putval(mp,val,NV_RDONLY); } #if 0 sfprintf(sfstderr,"%d: name=%s val=%s\n",getpid(),name,val); path_dump((Pathcomp_t*)shp->pathlist); #endif } #endif } #ifdef PATH_BFPATH static void put_cdpath(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { Pathcomp_t *pp; Shell_t *shp = ((struct shell*)fp)->sh; nv_putv(np, val, flags, fp); if(!shp->cdpathlist) return; val = np->nvalue.cp; pp = (void*)path_addpath((Pathcomp_t*)shp->cdpathlist,val,PATH_CDPATH); if(shp->cdpathlist = (void*)pp) pp->shp = shp; } #endif #ifdef _hdr_locale /* * This function needs to be modified to handle international * error message translations */ #if ERROR_VERSION >= 20000101L static char* msg_translate(const char* catalog, const char* message) { NOT_USED(catalog); return((char*)message); } #else static char* msg_translate(const char* message, int type) { NOT_USED(type); return((char*)message); } #endif /* Trap for LC_ALL, LC_TYPE, LC_MESSAGES, LC_COLLATE and LANG */ static void put_lang(Namval_t* np,const char *val,int flags,Namfun_t *fp) { int type; char *lc_all = nv_getval(LCALLNOD); char *name = nv_name(np); if(name==(LCALLNOD)->nvname) type = LC_ALL; else if(name==(LCTYPENOD)->nvname) type = LC_CTYPE; else if(name==(LCMSGNOD)->nvname) type = LC_MESSAGES; else if(name==(LCCOLLNOD)->nvname) type = LC_COLLATE; else if(name==(LCNUMNOD)->nvname) type = LC_NUMERIC; else if(name==(LANGNOD)->nvname && (!lc_all || *lc_all==0)) type = LC_ALL; else type= -1; if(sh_isstate(SH_INIT) && type>=0 && type!=LC_ALL && lc_all && *lc_all) type= -1; if(type>=0) { if(!setlocale(type,val?val:"")) { if(!sh_isstate(SH_INIT) || sh.login_sh==0) errormsg(SH_DICT,0,e_badlocale,val); return; } } if(CC_NATIVE==CC_ASCII && (type==LC_ALL || type==LC_CTYPE)) { if(sh_lexstates[ST_BEGIN]!=sh_lexrstates[ST_BEGIN]) free((void*)sh_lexstates[ST_BEGIN]); if(ast.locale.set&(1<ifsnp = 0; if(val != np->nvalue.cp) nv_putv(np, val, flags, fp); } /* * This is the lookup function for IFS * It keeps the sh.ifstable up to date */ static char* get_ifs(register Namval_t* np, Namfun_t *fp) { register struct ifs *ip = (struct ifs*)fp; register char *cp, *value; register int c,n; register Shell_t *shp = ip->sh; value = nv_getv(np,fp); if(np!=ip->ifsnp) { ip->ifsnp = np; memset(shp->ifstable,0,(1<1) { cp += (n-1); shp->ifstable[c] = S_MBYTE; continue; } #endif /* SHOPT_MULTIBYTE */ n = S_DELIM; if(c== *cp) cp++; else if(c=='\n') n = S_NL; else if(isspace(c)) n = S_SPACE; shp->ifstable[c] = n; } } else { shp->ifstable[' '] = shp->ifstable['\t'] = S_SPACE; shp->ifstable['\n'] = S_NL; } } return(value); } /* * these functions are used to get and set the SECONDS variable */ #ifdef timeofday # define dtime(tp) ((double)((tp)->tv_sec)+1e-6*((double)((tp)->tv_usec))) # define tms timeval #else # define dtime(tp) (((double)times(tp))/sh.lim.clk_tck) # define timeofday(a) #endif static void put_seconds(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { double d; struct tms tp; if(!val) { nv_stack(np, NIL(Namfun_t*)); nv_unset(np); return; } if(!np->nvalue.dp) { nv_setsize(np,3); np->nvalue.dp = new_of(double,0); } nv_putv(np, val, flags, fp); d = *np->nvalue.dp; timeofday(&tp); *np->nvalue.dp = dtime(&tp)-d; } static char* get_seconds(register Namval_t* np, Namfun_t *fp) { register int places = nv_size(np); struct tms tp; double d, offset = (np->nvalue.dp?*np->nvalue.dp:0); NOT_USED(fp); timeofday(&tp); d = dtime(&tp)- offset; sfprintf(sh.strbuf,"%.*f",places,d); return(sfstruse(sh.strbuf)); } static Sfdouble_t nget_seconds(register Namval_t* np, Namfun_t *fp) { struct tms tp; double offset = (np->nvalue.dp?*np->nvalue.dp:0); NOT_USED(fp); timeofday(&tp); return(dtime(&tp)- offset); } /* * These three functions are used to get and set the RANDOM variable */ static void put_rand(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { struct rand *rp = (struct rand*)fp; register long n; if(!val) { nv_stack(np, NIL(Namfun_t*)); nv_unset(np); return; } if(flags&NV_INTEGER) n = *(double*)val; else n = sh_arith(val); srand((int)(n&RANDMASK)); rp->rand_last = -1; if(!np->nvalue.lp) np->nvalue.lp = &rp->rand_last; } /* * get random number in range of 0 - 2**15 * never pick same number twice in a row */ static Sfdouble_t nget_rand(register Namval_t* np, Namfun_t *fp) { register long cur, last= *np->nvalue.lp; NOT_USED(fp); do cur = (rand()>>rand_shift)&RANDMASK; while(cur==last); *np->nvalue.lp = cur; return((Sfdouble_t)cur); } static char* get_rand(register Namval_t* np, Namfun_t *fp) { register long n = nget_rand(np,fp); return(fmtbase(n, 10, 0)); } /* * These three routines are for LINENO */ static Sfdouble_t nget_lineno(Namval_t* np, Namfun_t *fp) { double d=1; if(error_info.line >0) d = error_info.line; else if(error_info.context && error_info.context->line>0) d = error_info.context->line; NOT_USED(np); NOT_USED(fp); return(d); } static void put_lineno(Namval_t* np,const char *val,int flags,Namfun_t *fp) { register long n; Shell_t *shp = ((struct shell*)fp)->sh; if(!val) { nv_stack(np, NIL(Namfun_t*)); nv_unset(np); return; } if(flags&NV_INTEGER) n = *(double*)val; else n = sh_arith(val); shp->st.firstline += nget_lineno(np,fp)+1-n; } static char* get_lineno(register Namval_t* np, Namfun_t *fp) { register long n = nget_lineno(np,fp); return(fmtbase(n, 10, 0)); } static char* get_lastarg(Namval_t* np, Namfun_t *fp) { NOT_USED(np); return(sh.lastarg); } static void put_lastarg(Namval_t* np,const char *val,int flags,Namfun_t *fp) { if(flags&NV_INTEGER) { sfprintf(sh.strbuf,"%.*g",12,*((double*)val)); val = sfstruse(sh.strbuf); } if(sh.lastarg && !nv_isattr(np,NV_NOFREE)) free((void*)sh.lastarg); else nv_offattr(np,NV_NOFREE); if(val) sh.lastarg = strdup(val); else sh.lastarg = 0; } static int hasgetdisc(register Namfun_t *fp) { while(fp && !fp->disc->getnum && !fp->disc->getval) fp = fp->next; return(fp!=0); } /* * store the most recent value for use in .sh.match */ void sh_setmatch(const char *v, int vsize, int nmatch, int match[]) { struct match *mp = (struct match*)(SH_MATCHNOD->nvfun); register int i,n; if(mp->nmatch = nmatch) { memcpy(mp->match,match,nmatch*2*sizeof(match[0])); for(n=match[0],i=1; i < 2*nmatch; i++) { if(mp->match[i] < n) n = mp->match[i]; } for(vsize=0,i=0; i < 2*nmatch; i++) { if((mp->match[i] -= n) > vsize) vsize = mp->match[i]; } v += n; if(vsize >= mp->vsize) { if(mp->vsize) mp->val = (char*)realloc(mp->val,vsize+1); else mp->val = (char*)malloc(vsize+1); mp->vsize = vsize; } memcpy(mp->val,v,vsize); mp->val[vsize] = 0; nv_putsub(SH_MATCHNOD, NIL(char*), nmatch|ARRAY_FILL); mp->lastsub = -1; } } #define array_scan(np) ((nv_arrayptr(np)->nelem&ARRAY_SCAN)) static char* get_match(register Namval_t* np, Namfun_t *fp) { struct match *mp = (struct match*)fp; int sub,n; char *val; sub = nv_aindex(np); if(sub>=mp->nmatch) return(0); if(sub==mp->lastsub) return(mp->rval); if(mp->rval) { free((void*)mp->rval); mp->rval = 0; } n = mp->match[2*sub+1]-mp->match[2*sub]; if(n<=0) return(""); val = mp->val+mp->match[2*sub]; if(mp->val[mp->match[2*sub+1]]==0) return(val); mp->rval = (char*)malloc(n+1); mp->lastsub = sub; memcpy(mp->rval,val,n); mp->rval[n] = 0; return(mp->rval); } static const Namdisc_t SH_MATCH_disc = { sizeof(struct match), 0, get_match }; #if SHOPT_FS_3D /* * set or unset the mappings given a colon separated list of directories */ static void vpath_set(char *str, int mode) { register char *lastp, *oldp=str, *newp=strchr(oldp,':'); if(!sh.lim.fs3d) return; while(newp) { *newp++ = 0; if(lastp=strchr(newp,':')) *lastp = 0; mount((mode?newp:""),oldp,FS3D_VIEW,0); newp[-1] = ':'; oldp = newp; newp=lastp; } } /* catch vpath assignments */ static void put_vpath(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { register char *cp; if(cp = nv_getval(np)) vpath_set(cp,0); if(val) vpath_set((char*)val,1); nv_putv(np,val,flags,fp); } static const Namdisc_t VPATH_disc = { 0, put_vpath }; static Namfun_t VPATH_init = { &VPATH_disc, 1 }; #endif /* SHOPT_FS_3D */ static const Namdisc_t IFS_disc = { sizeof(struct ifs), put_ifs, get_ifs }; const Namdisc_t RESTRICTED_disc = { sizeof(struct shell), put_restricted }; #ifdef PATH_BFPATH static const Namdisc_t CDPATH_disc = { sizeof(struct shell), put_cdpath }; #endif static const Namdisc_t EDITOR_disc = { sizeof(struct shell), put_ed }; static const Namdisc_t OPTINDEX_disc = { sizeof(struct shell), put_optindex, 0, nget_optindex }; static const Namdisc_t SECONDS_disc = { sizeof(struct seconds), put_seconds, get_seconds, nget_seconds }; static const Namdisc_t RAND_disc = { sizeof(struct rand), put_rand, get_rand, nget_rand }; static const Namdisc_t LINENO_disc = { sizeof(struct shell), put_lineno, get_lineno, nget_lineno }; static const Namdisc_t L_ARG_disc = { sizeof(struct shell), put_lastarg, get_lastarg }; #if SHOPT_NAMESPACE static char* get_nspace(Namval_t* np, Namfun_t *fp) { if(sh.namespace) return(nv_name(sh.namespace)); return((char*)np->nvalue.cp); } static const Namdisc_t NSPACE_disc = { 0, 0, get_nspace }; static Namfun_t NSPACE_init = { &NSPACE_disc, 1}; #endif /* SHOPT_NAMESPACE */ #ifdef _hdr_locale static const Namdisc_t LC_disc = { sizeof(struct shell), put_lang }; #endif /* _hdr_locale */ /* * This function will get called whenever a configuration parameter changes */ static int newconf(const char *name, const char *path, const char *value) { register char *arg; if(!name) setenviron(value); else if(strcmp(name,"UNIVERSE")==0 && strcmp(astconf(name,0,0),value)) { sh.universe = 0; /* set directory in new universe */ if(*(arg = path_pwd(0))=='/') chdir(arg); /* clear out old tracked alias */ stakseek(0); stakputs(nv_getval(PATHNOD)); stakputc(0); nv_putval(PATHNOD,stakseek(0),NV_RDONLY); } return(1); } #if (CC_NATIVE != CC_ASCII) static void a2e(char *d, const char *s) { register const unsigned char *t; register int i; t = CCMAP(CC_ASCII, CC_NATIVE); for(i=0; i<(1<= 20000102L error_info.catalog = e_dict; #endif sh.cpipe[0] = -1; sh.coutpipe = -1; sh.userid=getuid(); sh.euserid=geteuid(); sh.groupid=getgid(); sh.egroupid=getegid(); for(n=0;n < 10; n++) { /* don't use lower bits when rand() generates large numbers */ if(rand() > RANDMASK) { rand_shift = 3; break; } } sh.lim.clk_tck = getconf("CLK_TCK"); sh.lim.arg_max = getconf("ARG_MAX"); sh.lim.open_max = getconf("OPEN_MAX"); sh.lim.child_max = getconf("CHILD_MAX"); sh.lim.ngroups_max = getconf("NGROUPS_MAX"); sh.lim.posix_version = getconf("VERSION"); sh.lim.posix_jobcontrol = getconf("JOB_CONTROL"); if(sh.lim.arg_max <=0) sh.lim.arg_max = ARG_MAX; if(sh.lim.child_max <=0) sh.lim.child_max = CHILD_MAX; if(sh.lim.open_max <0) sh.lim.open_max = OPEN_MAX; if(sh.lim.open_max > (SHRT_MAX-2)) sh.lim.open_max = SHRT_MAX-2; if(sh.lim.clk_tck <=0) sh.lim.clk_tck = CLK_TCK; #if SHOPT_FS_3D if(fs3d(FS3D_TEST)) sh.lim.fs3d = 1; #endif /* SHOPT_FS_3D */ sh_ioinit(); /* initialize signal handling */ sh_siginit(); stakinstall(NIL(Stak_t*),nospace); /* set up memory for name-value pairs */ sh.init_context = nv_init(&sh); /* read the environment */ if(argc>0) { type = sh_type(*argv); if(type&SH_TYPE_LOGIN) sh.login_sh = 2; } env_init(&sh); #if SHOPT_SPAWN { /* * try to find the pathname for this interpreter * try using environment variable _ or argv[0] */ char *last, *cp=nv_getval(L_ARGNOD); char buff[PATH_MAX+1]; sh.shpath = 0; sfprintf(sh.strbuf,"/proc/%d/exe",getpid()); if((n=readlink(sfstruse(sh.strbuf),buff,sizeof(buff)-1))>0) { buff[n] = 0; sh.shpath = strdup(buff); } else if((cp && (sh_type(cp)&SH_TYPE_SH)) || (argc>0 && strchr(cp= *argv,'/'))) { if(*cp=='/') sh.shpath = strdup(cp); else if(cp = nv_getval(PWDNOD)) { int offset = staktell(); stakputs(cp); stakputc('/'); stakputs(argv[0]); pathcanon(stakptr(offset),PATH_DOTDOT); sh.shpath = strdup(stakptr(offset)); stakseek(offset); } } } #endif nv_putval(IFSNOD,(char*)e_sptbnl,NV_RDONLY); #if SHOPT_FS_3D nv_stack(VPATHNOD, &VPATH_init); #endif /* SHOPT_FS_3D */ astconfdisc(newconf); #if SHOPT_TIMEOUT sh.st.tmout = SHOPT_TIMEOUT; #endif /* SHOPT_TIMEOUT */ /* initialize jobs table */ job_clear(); if(argc>0) { /* check for restricted shell */ if(type&SH_TYPE_RESTRICTED) sh_onoption(SH_RESTRICTED); #if SHOPT_PFSH /* check for profile shell */ else if(type&SH_TYPE_PROFILE) sh_onoption(SH_PFSH); #endif #if SHOPT_BASH /* check for invocation as bash */ if(type&SH_TYPE_BASH) { sh.userinit = userinit = bash_init; sh_onoption(SH_BASH); sh_onstate(SH_PREINIT); (*userinit)(0); sh_offstate(SH_PREINIT); } #endif /* look for options */ /* sh.st.dolc is $# */ if((sh.st.dolc = sh_argopts(-argc,argv)) < 0) { sh.exitval = 2; sh_done(0); } opt_info.disc = 0; sh.st.dolv=argv+(argc-1)-sh.st.dolc; sh.st.dolv[0] = argv[0]; if(sh.st.dolc < 1) sh_onoption(SH_SFLAG); if(!sh_isoption(SH_SFLAG)) { sh.st.dolc--; sh.st.dolv++; #if _WINIX { char* name; name = sh.st.dolv[0]; if(name[1]==':' && (name[2]=='/' || name[2]=='\\')) { #if _lib_pathposix char* p; if((n = pathposix(name, NIL(char*), 0)) > 0 && (p = (char*)malloc(++n))) { pathposix(name, p, n); name = p; } else #endif { name[1] = name[0]; name[0] = name[2] = '/'; } } } #endif /* _WINIX */ } } #if SHOPT_PFSH if (sh_isoption(SH_PFSH)) { struct passwd *pw = getpwuid(sh.userid); if(pw) sh.user = strdup(pw->pw_name); } #endif /* set[ug]id scripts require the -p flag */ if(sh.userid!=sh.euserid || sh.groupid!=sh.egroupid) { #if SHOPT_P_SUID /* require sh -p to run setuid and/or setgid */ if(!sh_isoption(SH_PRIVILEGED) && sh.euserid < SHOPT_P_SUID) { setuid(sh.euserid=sh.userid); setgid(sh.egroupid=sh.groupid); } else #else sh_onoption(SH_PRIVILEGED); #endif /* SHOPT_P_SUID */ #ifdef SHELLMAGIC /* careful of #! setuid scripts with name beginning with - */ if(sh.login_sh && argv[1] && strcmp(argv[0],argv[1])==0) errormsg(SH_DICT,ERROR_exit(1),e_prohibited); #endif /*SHELLMAGIC*/ } else sh_offoption(SH_PRIVILEGED); /* shname for $0 in profiles and . scripts */ if(strmatch(argv[1],e_devfdNN)) sh.shname = strdup(argv[0]); else sh.shname = strdup(sh.st.dolv[0]); /* * return here for shell script execution * but not for parenthesis subshells */ error_info.id = strdup(sh.st.dolv[0]); /* error_info.id is $0 */ sh.jmpbuffer = (void*)&sh.checkbase; sh_pushcontext(&sh.checkbase,SH_JMPSCRIPT); sh.st.self = &sh.global; sh.topscope = (Shscope_t*)sh.st.self; sh_offstate(SH_INIT); login_files[0] = (char*)e_profile; login_files[1] = ".profile"; sh.login_files = login_files; if(sh.userinit=userinit) (*userinit)(0); sh.bltindata.version = SH_VERSION; sh.bltindata.shp = &sh; sh.bltindata.shrun = sh_run; sh.bltindata.shtrap = sh_trap; sh.bltindata.shexit = sh_exit; sh.bltindata.shbltin = sh_addbuiltin; return(&sh); } Shell_t *sh_getinterp(void) { return(&sh); } /* * reinitialize before executing a script */ int sh_reinit(char *argv[]) { Shopt_t opt; dtclear(sh.fun_tree); dtclose(sh.alias_tree); sh.alias_tree = inittree(&sh,shtab_aliases); sh.namespace = 0; sh.inuse_bits = 0; if(sh.userinit) (*sh.userinit)(1); if(sh.heredocs) { sfclose(sh.heredocs); sh.heredocs = 0; } /* remove locals */ sh_onstate(SH_INIT); nv_scan(sh.var_tree,sh_envnolocal,(void*)0,NV_EXPORT,0); nv_scan(sh.var_tree,sh_envnolocal,(void*)0,NV_ARRAY,NV_ARRAY); sh_offstate(SH_INIT); memset(sh.st.trapcom,0,(sh.st.trapmax+1)*sizeof(char*)); memset((void*)&opt,0,sizeof(opt)); if(sh_isoption(SH_TRACKALL)) on_option(&opt,SH_TRACKALL); if(sh_isoption(SH_EMACS)) on_option(&opt,SH_EMACS); if(sh_isoption(SH_GMACS)) on_option(&opt,SH_GMACS); if(sh_isoption(SH_VI)) on_option(&opt,SH_VI); if(sh_isoption(SH_VIRAW)) on_option(&opt,SH_VIRAW); sh.options = opt; /* set up new args */ if(argv) sh.arglist = sh_argcreate(argv); if(sh.arglist) sh_argreset(sh.arglist,NIL(struct dolnod*)); sh.envlist=0; sh.curenv = 0; sh.shname = error_info.id = strdup(sh.st.dolv[0]); sh_offstate(SH_FORKED); sh.fn_depth = sh.dot_depth = 0; sh_sigreset(0); return(1); } /* * set when creating a local variable of this name */ Namfun_t *nv_cover(register Namval_t *np) { #ifdef PATH_BFPATH if(np==IFSNOD || np==PATHNOD || np==SHELLNOD || np==FPATHNOD || np==CDPNOD || np==SECONDS) #else if(np==IFSNOD || np==PATHNOD || np==SHELLNOD || np==SECONDS) #endif return(np->nvfun); #ifdef _hdr_locale if(np==LCALLNOD || np==LCTYPENOD || np==LCMSGNOD || np==LCCOLLNOD || np==LCNUMNOD || np==LANGNOD) return(np->nvfun); #endif return(0); } static Namtype_t typeset; static const char *shdiscnames[] = { "tilde", 0}; /* * Initialize the shell name and alias table */ static Init_t *nv_init(Shell_t *shp) { Namval_t *np; register Init_t *ip; double d=0; ip = newof(0,Init_t,1,0); if(!ip) return(0); ip->sh = shp; shp->var_base = shp->var_tree = inittree(shp,shtab_variables); ip->IFS_init.hdr.disc = &IFS_disc; ip->IFS_init.hdr.nofree = 1; ip->IFS_init.sh = shp; ip->PATH_init.hdr.disc = &RESTRICTED_disc; ip->PATH_init.hdr.nofree = 1; ip->PATH_init.sh = shp; #ifdef PATH_BFPATH ip->FPATH_init.hdr.disc = &RESTRICTED_disc; ip->FPATH_init.hdr.nofree = 1; ip->FPATH_init.sh = shp; ip->CDPATH_init.hdr.disc = &CDPATH_disc; ip->CDPATH_init.hdr.nofree = 1; ip->CDPATH_init.sh = shp; #endif ip->SHELL_init.hdr.disc = &RESTRICTED_disc; ip->SHELL_init.sh = shp; ip->SHELL_init.hdr.nofree = 1; ip->ENV_init.hdr.disc = &RESTRICTED_disc; ip->ENV_init.hdr.nofree = 1; ip->ENV_init.sh = shp; ip->VISUAL_init.hdr.disc = &EDITOR_disc; ip->VISUAL_init.hdr.nofree = 1; ip->VISUAL_init.sh = shp; ip->EDITOR_init.hdr.disc = &EDITOR_disc; ip->EDITOR_init.hdr.nofree = 1; ip->EDITOR_init.sh = shp; ip->OPTINDEX_init.hdr.disc = &OPTINDEX_disc; ip->OPTINDEX_init.hdr.nofree = 1; ip->OPTINDEX_init.sh = shp; ip->SECONDS_init.hdr.disc = &SECONDS_disc; ip->SECONDS_init.hdr.nofree = 1; ip->SECONDS_init.sh = shp; ip->RAND_init.hdr.disc = &RAND_disc; ip->RAND_init.hdr.nofree = 1; ip->SH_MATCH_init.hdr.disc = &SH_MATCH_disc; ip->SH_MATCH_init.hdr.nofree = 1; ip->LINENO_init.hdr.disc = &LINENO_disc; ip->LINENO_init.hdr.nofree = 1; ip->LINENO_init.sh = shp; ip->L_ARG_init.hdr.disc = &L_ARG_disc; ip->L_ARG_init.hdr.nofree = 1; #ifdef _hdr_locale ip->LC_TYPE_init.hdr.disc = &LC_disc; ip->LC_TYPE_init.hdr.nofree = 1; ip->LC_NUM_init.hdr.disc = &LC_disc; ip->LC_NUM_init.hdr.nofree = 1; ip->LC_COLL_init.hdr.disc = &LC_disc; ip->LC_COLL_init.hdr.nofree = 1; ip->LC_MSG_init.hdr.disc = &LC_disc; ip->LC_MSG_init.hdr.nofree = 1; ip->LC_ALL_init.hdr.disc = &LC_disc; ip->LC_ALL_init.hdr.nofree = 1; ip->LANG_init.hdr.disc = &LC_disc; ip->LANG_init.hdr.nofree = 1; ip->LC_TYPE_init.sh = shp; ip->LC_NUM_init.sh = shp; ip->LC_COLL_init.sh = shp; ip->LC_MSG_init.sh = shp; ip->LANG_init.sh = shp; #endif /* _hdr_locale */ nv_stack(IFSNOD, &ip->IFS_init.hdr); nv_stack(PATHNOD, &ip->PATH_init.hdr); #ifdef PATH_BFPATH nv_stack(FPATHNOD, &ip->FPATH_init.hdr); nv_stack(CDPNOD, &ip->CDPATH_init.hdr); #endif nv_stack(SHELLNOD, &ip->SHELL_init.hdr); nv_stack(ENVNOD, &ip->ENV_init.hdr); nv_stack(VISINOD, &ip->VISUAL_init.hdr); nv_stack(EDITNOD, &ip->EDITOR_init.hdr); nv_stack(OPTINDNOD, &ip->OPTINDEX_init.hdr); nv_stack(SECONDS, &ip->SECONDS_init.hdr); nv_stack(L_ARGNOD, &ip->L_ARG_init.hdr); nv_putval(SECONDS, (char*)&d, NV_INTEGER|NV_DOUBLE); nv_stack(RANDNOD, &ip->RAND_init.hdr); d = (shp->pid&RANDMASK); nv_putval(RANDNOD, (char*)&d, NV_INTEGER|NV_DOUBLE); nv_stack(LINENO, &ip->LINENO_init.hdr); nv_putsub(SH_MATCHNOD,(char*)0,10); nv_onattr(SH_MATCHNOD,NV_RDONLY); nv_stack(SH_MATCHNOD, &ip->SH_MATCH_init.hdr); #ifdef _hdr_locale nv_stack(LCTYPENOD, &ip->LC_TYPE_init.hdr); nv_stack(LCALLNOD, &ip->LC_ALL_init.hdr); nv_stack(LCMSGNOD, &ip->LC_MSG_init.hdr); nv_stack(LCCOLLNOD, &ip->LC_COLL_init.hdr); nv_stack(LCNUMNOD, &ip->LC_NUM_init.hdr); nv_stack(LANGNOD, &ip->LANG_init.hdr); #endif /* _hdr_locale */ (PPIDNOD)->nvalue.lp = (&shp->ppid); (TMOUTNOD)->nvalue.lp = (&shp->st.tmout); (MCHKNOD)->nvalue.lp = (&sh_mailchk); (OPTINDNOD)->nvalue.lp = (&shp->st.optindex); /* set up the seconds clock */ shp->alias_tree = inittree(shp,shtab_aliases); shp->track_tree = dtopen(&_Nvdisc,Dtset); shp->bltin_tree = inittree(shp,(const struct shtable2*)shtab_builtins); typeset.shp = shp; typeset.optstring = sh_opttypeset; nv_search("typeset",shp->bltin_tree,0)->nvfun = (void*)&typeset; #if SHOPT_BASH nv_search("local",shp->bltin_tree,0)->nvfun = (void*)&typeset; #endif shp->fun_tree = dtopen(&_Nvdisc,Dtoset); dtview(shp->fun_tree,shp->bltin_tree); #if SHOPT_NAMESPACE if(np = nv_mount(DOTSHNOD, "global", shp->var_tree)) nv_onattr(np,NV_RDONLY); np = nv_search("namespace",nv_dict(DOTSHNOD),NV_ADD); nv_putval(np,".sh.global",NV_RDONLY|NV_NOFREE); nv_stack(np, &NSPACE_init); #endif /* SHOPT_NAMESPACE */ np = nv_mount(DOTSHNOD, "type", dtopen(&_Nvdisc,Dtoset)); nv_adddisc(DOTSHNOD, shdiscnames, (Namval_t**)0); return(ip); } /* * initialize name-value pairs */ static Dt_t *inittree(Shell_t *shp,const struct shtable2 *name_vals) { register Namval_t *np; register const struct shtable2 *tp; register unsigned n = 0; register Dt_t *treep; Dt_t *base_treep, *dict; for(tp=name_vals;*tp->sh_name;tp++) n++; np = (Namval_t*)calloc(n,sizeof(Namval_t)); if(!shp->bltin_nodes) shp->bltin_nodes = np; else if(name_vals==(const struct shtable2*)shtab_builtins) shp->bltin_cmds = np; base_treep = treep = dtopen(&_Nvdisc,Dtoset); for(tp=name_vals;*tp->sh_name;tp++,np++) { if((np->nvname = strrchr(tp->sh_name,'.')) && np->nvname!=((char*)tp->sh_name)) np->nvname++; else { np->nvname = (char*)tp->sh_name; treep = base_treep; } np->nvenv = 0; if(name_vals==(const struct shtable2*)shtab_builtins) np->nvalue.bfp = ((struct shtable3*)tp)->sh_value; else np->nvalue.cp = (char*)tp->sh_value; nv_setattr(np,tp->sh_number); if(nv_istable(np)) nv_mount(np,(const char*)0,dict=dtopen(&_Nvdisc,Dtoset)); if(nv_isattr(np,NV_INTEGER)) nv_setsize(np,10); else nv_setsize(np,0); dtinsert(treep,np); if(nv_istable(np)) treep = dict; } return(treep); } /* * read in the process environment and set up name-value pairs * skip over items that are not name-value pairs */ static void env_init(Shell_t *shp) { register char *cp; register Namval_t *np; register char **ep=environ; register char *next=0; #ifdef _ENV_H shp->env = env_open(environ,3); env_delete(shp->env,"_"); #endif if(ep) { while(cp= *ep++) { if(*cp=='A' && cp[1]=='_' && cp[2]=='_' && cp[3]=='z' && cp[4]=='=') next = cp+4; else if(np=nv_open(cp,shp->var_tree,(NV_EXPORT|NV_IDENT|NV_ASSIGN|NV_NOFAIL))) { nv_onattr(np,NV_IMPORT); np->nvenv = cp; nv_close(np); } } while(cp=next) { if(next = strchr(++cp,'=')) *next = 0; np = nv_search(cp+2,shp->var_tree,NV_ADD); if(nv_isattr(np,NV_IMPORT|NV_EXPORT)) { int flag = *(unsigned char*)cp-' '; int size = *(unsigned char*)(cp+1)-' '; if((flag&NV_INTEGER) && size==0) { /* check for floating*/ char *ep,*val = nv_getval(np); strtol(val,&ep,10); if(*ep=='.' || *ep=='e' || *ep=='E') { char *lp; flag |= NV_DOUBLE; if(*ep=='.') { strtol(ep+1,&lp,10); if(*lp) ep = lp; } if(*ep && *ep!='.') { flag |= NV_EXPNOTE; size = ep-val; } else size = strlen(ep); size--; } } nv_newattr(np,flag|NV_IMPORT|NV_EXPORT,size); } } } #ifdef _ENV_H env_delete(sh.env,e_envmarker); #endif if(nv_isnull(PWDNOD) || nv_isattr(PWDNOD,NV_TAGGED)) { nv_offattr(PWDNOD,NV_TAGGED); path_pwd(0); } if((cp = nv_getval(SHELLNOD)) && (sh_type(cp)&SH_TYPE_RESTRICTED)) sh_onoption(SH_RESTRICTED); /* restricted shell */ return; } /* * terminate shell and free up the space */ int sh_term(void) { sfdisc(sfstdin,SF_POPDISC); free((char*)sh.outbuff); stakset(NIL(char*),0); return(0); } /* function versions of these */ #define DISABLE /* proto workaround */ unsigned long sh_isoption DISABLE (int opt) { return(sh_isoption(opt)); } unsigned long sh_onoption DISABLE (int opt) { return(sh_onoption(opt)); } unsigned long sh_offoption DISABLE (int opt) { return(sh_offoption(opt)); } void sh_sigcheck DISABLE (void) { sh_sigcheck(); } Dt_t* sh_bltin_tree DISABLE (void) { return(sh.bltin_tree); }