1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-24 06:54:13 +00:00
cde/src/cmd/ksh93/sh/init.c
Martijn Dekker ddaa145b3d Reinstate 'r' and 'history' as preset aliases for interactive ksh
Following a community discussion, it became clear that 'r' is
particularly problematic as a regular builtin, as the name can and
does conflict with at least one legit external command by that
name. There was a consensus against removing it altogether and
letting users set the alias in their login scripts. However,
aliases are easier to bypass, remove or rename than builtins are.
My compromise is to reinstate 'r' as a preset alias on interactive
shells only, along with 'history', as was done in 17f81ebe before
they were converted to builtins in 03224ae3. So this reintroduces
the notion of predefined aliases to ksh 93u+m, but only for
interactive shells that are not initialised in POSIX mode.

src/cmd/ksh93/Makefile,
src/cmd/ksh93/Mamfile,
src/cmd/ksh93/include/shtable.h,
src/cmd/ksh93/data/aliases.c:
- Restore aliases.c containing shtab_aliases[], a table specifying
  the preset aliases.

src/cmd/ksh93/include/shtable.h,
src/cmd/ksh93/sh/init.c:
- Rename inittree() to sh_inittree() and make it extern, because we
  need to use it in main.c (sh_main()).

src/cmd/ksh93/sh/main.c: sh_main():
- Init preset aliases from shtab_aliases[] only if the shell is
  interactive and not in POSIX mode.

src/cmd/ksh93/bltins/typeset.c,
src/cmd/ksh93/tests/alias.sh:
- unall(): When unsetting an alias, pass on the NV_NOFREE attribute
  to nv_delete() to avoid an erroneous attempt to free a preset
  alias from read-only memory. See: 5d50f825

src/cmd/ksh93/data/builtins.c:
- Remove "history" and "r" entries from shtab_builtins[].
- Revert changes to inline fc/hist docs in sh_opthist[].

src/cmd/ksh93/bltins/hist.c: b_hist():
- Remove handling for 'history' and 'r' as builtins.

src/cmd/ksh93/sh.1:
- Update accordingly.

Resolves: https://github.com/ksh93/ksh/issues/125
2020-09-11 21:35:45 +02:00

2138 lines
49 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
/*
*
* Shell initialization
*
* David Korn
* AT&T Labs
*
*/
#include "defs.h"
#include <stak.h>
#include <ccode.h>
#include <pwd.h>
#include <tmx.h>
#include <regex.h>
#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 "FEATURE/externs"
#include "lexstates.h"
#include "version.h"
#if _hdr_wctype
#include <ast_wchar.h>
#include <wctype.h>
#endif
#if !_typ_wctrans_t
#undef wctrans_t
#define wctrans_t sh_wctrans_t
typedef long wctrans_t;
#endif
#if !_lib_wctrans
#undef wctrans
#define wctrans sh_wctrans
static wctrans_t wctrans(const char *name)
{
if(strcmp(name,e_tolower)==0)
return(1);
else if(strcmp(name,e_toupper)==0)
return(2);
return(0);
}
#endif
#if !_lib_towctrans
#undef towctrans
#define towctrans sh_towctrans
static int towctrans(int c, wctrans_t t)
{
if(t==1 && isupper(c))
c = tolower(c);
else if(t==2 && islower(c))
c = toupper(c);
return(c);
}
#endif
char e_version[] = "\n@(#)$Id: Version "
#if SHOPT_AUDIT
#define ATTRS 1
"A"
#endif
#if SHOPT_BGX
#define ATTRS 1
"J"
#endif
#if SHOPT_ACCT
#define ATTRS 1
"L"
#endif
#if SHOPT_MULTIBYTE
#define ATTRS 1
"M"
#endif
#if SHOPT_PFSH && _hdr_exec_attr
#define ATTRS 1
"P"
#endif
#if SHOPT_REGRESS
#define ATTRS 1
"R"
#endif
#if ATTRS
" "
#endif
SH_RELEASE " $\0\n";
#define RANDMASK 0x7fff
#ifndef ARG_MAX
# define ARG_MAX (1*1024*1024)
#endif
#ifndef CHILD_MAX
# define CHILD_MAX (1*1024)
#endif
#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;
Namval_t *ifsnp;
};
struct match
{
Namfun_t hdr;
const char *v;
char *val;
char *rval[2];
regoff_t *match;
char node[NV_MINSZ+sizeof(char*)];
regoff_t first;
int vsize;
int nmatch;
int index;
int lastsub[2];
};
typedef struct _init_
{
Shell_t *sh;
struct ifs IFS_init;
Namfun_t PATH_init;
Namfun_t FPATH_init;
Namfun_t CDPATH_init;
Namfun_t SHELL_init;
Namfun_t ENV_init;
Namfun_t VISUAL_init;
Namfun_t EDITOR_init;
Namfun_t HISTFILE_init;
Namfun_t HISTSIZE_init;
Namfun_t OPTINDEX_init;
struct seconds SECONDS_init;
struct rand RAND_init;
Namfun_t LINENO_init;
Namfun_t L_ARG_init;
Namfun_t SH_VERSION_init;
struct match SH_MATCH_init;
Namfun_t SH_MATH_init;
#ifdef _hdr_locale
Namfun_t LC_TYPE_init;
Namfun_t LC_NUM_init;
Namfun_t LC_COLL_init;
Namfun_t LC_MSG_init;
Namfun_t LC_ALL_init;
Namfun_t LANG_init;
#endif /* _hdr_locale */
} Init_t;
static Init_t *ip;
static int lctype;
static int nbltins;
static void env_init(Shell_t*,int);
static Init_t *nv_init(Shell_t*);
static int shlvl;
#ifdef _WINIX
# define EXE "?(.exe)"
#else
# define EXE
#endif
static int rand_shift;
/*
* 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);
register int newopt=0;
Shell_t *shp = nv_shell(np);
if(*name=='E' && nv_getval(sh_scoped(shp,VISINOD)))
goto done;
if(!(cp=val) && (*name=='E' || !(cp=nv_getval(sh_scoped(shp,EDITNOD)))))
goto done;
/* turn on vi or emacs option if editor name is either*/
cp = path_basename(cp);
if(strmatch(cp,"*[Vv][Ii]*"))
newopt=SH_VI;
else if(strmatch(cp,"*gmacs*"))
newopt=SH_GMACS;
else if(strmatch(cp,"*macs*"))
newopt=SH_EMACS;
if(newopt)
{
sh_offoption(SH_VI);
sh_offoption(SH_EMACS);
sh_offoption(SH_GMACS);
sh_onoption(newopt);
}
done:
nv_putv(np, val, flags, fp);
}
/* Trap for HISTFILE and HISTSIZE variables */
static void put_history(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
Shell_t *shp = nv_shell(np);
void *histopen = shp->gd->hist_ptr;
char *cp;
if(val && histopen)
{
if(np==HISTFILE && (cp=nv_getval(np)) && strcmp(val,cp)==0)
return;
if(np==HISTSIZE && sh_arith(shp,val)==nv_getnum(HISTSIZE))
return;
hist_close(shp->gd->hist_ptr);
}
nv_putv(np, val, flags, fp);
if(histopen)
{
if(val)
sh_histinit(shp);
else
hist_close(histopen);
}
}
/* Trap for OPTINDEX */
static void put_optindex(Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
Shell_t *shp = nv_shell(np);
shp->st.opterror = shp->st.optchar = 0;
nv_putv(np, val, flags, fp);
if(!val)
nv_disc(np,fp,NV_POP);
}
static Sfdouble_t nget_optindex(register Namval_t* np, Namfun_t *fp)
{
return((Sfdouble_t)*np->nvalue.lp);
}
static Namfun_t *clone_optindex(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
{
Namfun_t *dp = (Namfun_t*)malloc(sizeof(Namfun_t));
memcpy((void*)dp,(void*)fp,sizeof(Namfun_t));
mp->nvalue.lp = np->nvalue.lp;
dp->nofree = 0;
return(dp);
}
/* 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 = nv_shell(np);
int path_scoped = 0, fpath_scoped=0;
Pathcomp_t *pp;
char *name = nv_name(np);
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)))
{
/* Clear the hash table */
nv_scan(shp->track_tree,nv_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;
if(np==FPATHNOD || (fpath_scoped=(strcmp(name,FPATHNOD->nvname)==0)))
shp->pathlist = (void*)path_unsetfpath(shp);
nv_putv(np, val, flags, fp);
shp->universe = 0;
if(shp->pathlist)
{
val = np->nvalue.cp;
if(np==PATHNOD || path_scoped)
pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_PATH);
else if(val && (np==FPATHNOD || fpath_scoped))
pp = (void*)path_addpath(shp,(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);
}
}
}
static void put_cdpath(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
Pathcomp_t *pp;
Shell_t *shp = nv_shell(np);
nv_putv(np, val, flags, fp);
if(!shp->cdpathlist)
return;
val = np->nvalue.cp;
pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->cdpathlist,val,PATH_CDPATH);
if(shp->cdpathlist = (void*)pp)
pp->shp = shp;
}
#ifdef _hdr_locale
/*
* This function needs to be modified to handle international
* error message translations
*/
static char* msg_translate(const char* catalog, const char* message)
{
NOT_USED(catalog);
return((char*)message);
}
/* Trap for LC_ALL, LC_CTYPE, LC_MESSAGES, LC_COLLATE and LANG */
static void put_lang(Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
Shell_t *shp = nv_shell(np);
int type;
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;
#ifdef LC_LANG
else if(name==(LANGNOD)->nvname)
type = LC_LANG;
#else
#define LC_LANG LC_ALL
else if(name==(LANGNOD)->nvname && (!(name=nv_getval(LCALLNOD)) || !*name))
type = LC_LANG;
#endif
else
type= -1;
if(!sh_isstate(SH_INIT) && (type>=0 || type==LC_ALL || type==LC_LANG))
{
char* r;
#ifdef AST_LC_setenv
ast.locale.set |= AST_LC_setenv;
#endif
r = setlocale(type,val?val:"");
#ifdef AST_LC_setenv
ast.locale.set ^= AST_LC_setenv;
#endif
if(!r && val)
{
if(!sh_isstate(SH_INIT) || shp->login_sh==0)
errormsg(SH_DICT,0,e_badlocale,val);
return;
}
}
nv_putv(np, val, flags, fp);
if(CC_NATIVE!=CC_ASCII && (type==LC_ALL || type==LC_LANG || type==LC_CTYPE))
{
if(sh_lexstates[ST_BEGIN]!=sh_lexrstates[ST_BEGIN])
free((void*)sh_lexstates[ST_BEGIN]);
lctype++;
if(ast.locale.set&(1<<AST_LC_CTYPE))
{
register int c;
char *state[4];
sh_lexstates[ST_BEGIN] = state[0] = (char*)malloc(4*(1<<CHAR_BIT));
memcpy(state[0],sh_lexrstates[ST_BEGIN],(1<<CHAR_BIT));
sh_lexstates[ST_NAME] = state[1] = state[0] + (1<<CHAR_BIT);
memcpy(state[1],sh_lexrstates[ST_NAME],(1<<CHAR_BIT));
sh_lexstates[ST_DOL] = state[2] = state[1] + (1<<CHAR_BIT);
memcpy(state[2],sh_lexrstates[ST_DOL],(1<<CHAR_BIT));
sh_lexstates[ST_BRACE] = state[3] = state[2] + (1<<CHAR_BIT);
memcpy(state[3],sh_lexrstates[ST_BRACE],(1<<CHAR_BIT));
for(c=0; c<(1<<CHAR_BIT); c++)
{
if(state[0][c]!=S_REG)
continue;
if(state[2][c]!=S_ERR)
continue;
if(isblank(c))
{
state[0][c]=0;
state[1][c]=S_BREAK;
state[2][c]=S_BREAK;
continue;
}
if(!isalpha(c))
continue;
state[0][c]=S_NAME;
if(state[1][c]==S_REG)
state[1][c]=0;
state[2][c]=S_ALP;
if(state[3][c]==S_ERR)
state[3][c]=0;
}
}
else
{
sh_lexstates[ST_BEGIN]=(char*)sh_lexrstates[ST_BEGIN];
sh_lexstates[ST_NAME]=(char*)sh_lexrstates[ST_NAME];
sh_lexstates[ST_DOL]=(char*)sh_lexrstates[ST_DOL];
sh_lexstates[ST_BRACE]=(char*)sh_lexrstates[ST_BRACE];
}
}
}
#endif /* _hdr_locale */
/* Trap for IFS assignment and invalidates state table */
static void put_ifs(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
register struct ifs *ip = (struct ifs*)fp;
ip->ifsnp = 0;
if(!val)
{
fp = nv_stack(np, NIL(Namfun_t*));
if(fp && !fp->nofree)
{
free((void*)fp);
fp = 0;
}
}
if(val != np->nvalue.cp)
nv_putv(np, val, flags, fp);
if(!val)
{
if(fp)
fp->next = np->nvfun;
np->nvfun = 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 = nv_shell(np);
value = nv_getv(np,fp);
if(np!=ip->ifsnp)
{
ip->ifsnp = np;
memset(shp->ifstable,0,(1<<CHAR_BIT));
if(cp=value)
{
#if SHOPT_MULTIBYTE
while(n=mbsize(cp),c= *(unsigned char*)cp)
#else
while(c= *(unsigned char*)cp++)
#endif /* SHOPT_MULTIBYTE */
{
#if SHOPT_MULTIBYTE
cp++;
if(n>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))/shgd->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_putv(np, val, flags, fp);
fp = nv_stack(np, NIL(Namfun_t*));
if(fp && !fp->nofree)
free((void*)fp);
return;
}
if(!np->nvalue.dp)
{
nv_setsize(np,3);
nv_onattr(np,NV_DOUBLE);
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)
{
Shell_t *shp = nv_shell(np);
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(shp->strbuf,"%.*f",places,d);
return(sfstruse(shp->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)
{
fp = nv_stack(np, NIL(Namfun_t*));
if(fp && !fp->nofree)
free((void*)fp);
_nv_unset(np,NV_RDONLY);
return;
}
if(flags&NV_INTEGER)
n = *(double*)val;
else
n = sh_arith(rp->sh,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 = nv_shell(np);
if(!val)
{
fp = nv_stack(np, NIL(Namfun_t*));
if(fp && !fp->nofree)
free((void*)fp);
_nv_unset(np,NV_RDONLY);
return;
}
if(flags&NV_INTEGER)
n = *(double*)val;
else
n = sh_arith(shp,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)
{
Shell_t *shp = nv_shell(np);
char *cp;
int pid;
if(sh_isstate(SH_INIT) && (cp=shp->lastarg) && *cp=='*' && (pid=strtol(cp+1,&cp,10)) && *cp=='*')
nv_putval(np,cp+1,0);
return(shp->lastarg);
}
static void put_lastarg(Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
Shell_t *shp = nv_shell(np);
if(flags&NV_INTEGER)
{
sfprintf(shp->strbuf,"%.*g",12,*((double*)val));
val = sfstruse(shp->strbuf);
}
if(val)
val = strdup(val);
if(shp->lastarg && !nv_isattr(np,NV_NOFREE))
free((void*)shp->lastarg);
else
nv_offattr(np,NV_NOFREE);
shp->lastarg = (char*)val;
nv_offattr(np,NV_EXPORT);
np->nvenv = 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
* treat .sh.match as a two dimensional array
*/
void sh_setmatch(Shell_t *shp,const char *v, int vsize, int nmatch, regoff_t match[],int index)
{
struct match *mp = &ip->SH_MATCH_init;
Namval_t *np = nv_namptr(mp->node,0);
register int i,n,x;
unsigned int savesub = shp->subshell;
Namarr_t *ap = nv_arrayptr(SH_MATCHNOD);
shp->subshell = 0;
#ifndef SHOPT_2DMATCH
index = 0;
#else
if(index==0)
#endif /* SHOPT_2DMATCH */
{
if(ap->hdr.next != &mp->hdr)
{
free((void*)ap);
ap = nv_arrayptr(np);
SH_MATCHNOD->nvfun = &ap->hdr;
}
if(ap)
{
ap->nelem &= ~ARRAY_SCAN;
i = array_elem(ap);
ap->nelem++;
while(--i>= 0)
{
nv_putsub(SH_MATCHNOD, (char*)0,i);
_nv_unset(SH_MATCHNOD,NV_RDONLY);
}
ap->nelem--;
}
if(!nv_hasdisc(SH_MATCHNOD,mp->hdr.disc))
nv_disc(SH_MATCHNOD,&mp->hdr,NV_LAST);
if(nmatch)
nv_putsub(SH_MATCHNOD, NIL(char*), (nmatch-1)|ARRAY_FILL|ARRAY_SETSUB);
ap = nv_arrayptr(SH_MATCHNOD);
ap->nelem = mp->nmatch = nmatch;
mp->v = v;
mp->first = match[0];
}
#ifdef SHOPT_2DMATCH
else
{
if(index==1)
{
np->nvalue.cp = Empty;
np->nvfun = SH_MATCHNOD->nvfun;
nv_onattr(np,NV_NOFREE|NV_ARRAY);
SH_MATCHNOD->nvfun = 0;
for(i=0; i < mp->nmatch; i++)
{
nv_putsub(SH_MATCHNOD, (char*)0, i);
nv_arraychild(SH_MATCHNOD, np,0);
}
if(ap = nv_arrayptr(SH_MATCHNOD))
ap->nelem = mp->nmatch;
}
ap = nv_arrayptr(np);
nv_putsub(np, NIL(char*), index|ARRAY_FILL|ARRAY_SETSUB);
}
#endif /* SHOPT_2DMATCH */
shp->subshell = savesub;
index *= 2*mp->nmatch;
if(mp->nmatch)
{
for(n=mp->first+(mp->v-v),vsize=0,i=0; i < 2*nmatch; i++)
{
if(match[i]>=0 && (match[i] - n) > vsize)
vsize = match[i] -n;
}
i = (index+2*mp->nmatch)*sizeof(match[0]);
if((i+vsize) >= mp->vsize)
{
if(mp->vsize)
mp->match = (int*)realloc(mp->match,i+vsize+1);
else
mp->match = (int*)malloc(i+vsize+1);
mp->vsize = i+vsize+1;
}
mp->val = ((char*)mp->match)+i;
memcpy(mp->match+index,match,nmatch*2*sizeof(match[0]));
for(x=0,i=0; i < 2*nmatch; i++)
{
if(match[i]>=0)
mp->match[index+i] -= n;
else
x=1;
}
ap->nelem -= x;
while(i < 2*mp->nmatch)
mp->match[index+i++] = -1;
memcpy(mp->val,v+n,vsize);
mp->val[vsize] = 0;
mp->lastsub[0] = mp->lastsub[1] = -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,sub2=0,n,i =!mp->index;
char *val;
sub = nv_aindex(SH_MATCHNOD);
if(np!=SH_MATCHNOD)
sub2 = nv_aindex(np);
if(sub>=mp->nmatch)
return(0);
if(sub2>0)
sub += sub2*mp->nmatch;
if(sub==mp->lastsub[!i])
return(mp->rval[!i]);
else if(sub==mp->lastsub[i])
return(mp->rval[i]);
n = mp->match[2*sub+1]-mp->match[2*sub];
if(n<=0)
return(mp->match[2*sub]<0?Empty:"");
val = mp->val+mp->match[2*sub];
if(mp->val[mp->match[2*sub+1]]==0)
return(val);
mp->index = i;
if(mp->rval[i])
{
free((void*)mp->rval[i]);
mp->rval[i] = 0;
}
mp->rval[i] = (char*)malloc(n+1);
mp->lastsub[i] = sub;
memcpy(mp->rval[i],val,n);
mp->rval[i][n] = 0;
return(mp->rval[i]);
}
static const Namdisc_t SH_MATCH_disc = { sizeof(struct match), 0, get_match };
static char* get_version(register Namval_t* np, Namfun_t *fp)
{
return(nv_getv(np,fp));
}
static Sfdouble_t nget_version(register Namval_t* np, Namfun_t *fp)
{
register const char *cp = e_version + strlen(e_version)-10;
register int c;
Sflong_t t = 0;
NOT_USED(fp);
while (c = *cp++)
if (c >= '0' && c <= '9')
{
t *= 10;
t += c - '0';
}
return((Sfdouble_t)t);
}
static const Namdisc_t SH_VERSION_disc = { 0, 0, get_version, nget_version };
static const Namdisc_t IFS_disc = { sizeof(struct ifs), put_ifs, get_ifs };
const Namdisc_t RESTRICTED_disc = { sizeof(Namfun_t), put_restricted };
static const Namdisc_t CDPATH_disc = { sizeof(Namfun_t), put_cdpath };
static const Namdisc_t EDITOR_disc = { sizeof(Namfun_t), put_ed };
static const Namdisc_t HISTFILE_disc = { sizeof(Namfun_t), put_history };
static const Namdisc_t OPTINDEX_disc = { sizeof(Namfun_t), put_optindex, 0, nget_optindex, 0, 0, clone_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(Namfun_t), put_lineno, get_lineno, nget_lineno };
static const Namdisc_t L_ARG_disc = { sizeof(Namfun_t), put_lastarg, get_lastarg };
#define MAX_MATH_ARGS 3
static char *name_math(Namval_t *np, Namfun_t *fp)
{
Shell_t *shp = sh_getinterp();
sfprintf(shp->strbuf,".sh.math.%s",np->nvname);
return(sfstruse(shp->strbuf));
}
static const Namdisc_t math_child_disc =
{
0,0,0,0,0,0,0,
name_math
};
static Namfun_t math_child_fun =
{
&math_child_disc, 1, 0, sizeof(Namfun_t)
};
static void math_init(Shell_t *shp)
{
Namval_t *np;
char *name;
int i;
shp->mathnodes = (char*)calloc(1,MAX_MATH_ARGS*(NV_MINSZ+5));
name = shp->mathnodes+MAX_MATH_ARGS*NV_MINSZ;
for(i=0; i < MAX_MATH_ARGS; i++)
{
np = nv_namptr(shp->mathnodes,i);
np->nvfun = &math_child_fun;
memcpy(name,"arg",3);
name[3] = '1'+i;
np->nvname = name;
name+=5;
nv_onattr(np,NV_MINIMAL|NV_NOFREE|NV_LDOUBLE|NV_RDONLY);
}
}
static Namval_t *create_math(Namval_t *np,const char *name,int flag,Namfun_t *fp)
{
Shell_t *shp = nv_shell(np);
if(!name)
return(SH_MATHNOD);
if(name[0]!='a' || name[1]!='r' || name[2]!='g' || name[4] || !isdigit(name[3]) || (name[3]=='0' || (name[3]-'0')>MAX_MATH_ARGS))
return(0);
fp->last = (char*)&name[4];
return(nv_namptr(shp->mathnodes,name[3]-'1'));
}
static char* get_math(register Namval_t* np, Namfun_t *fp)
{
Shell_t *shp = nv_shell(np);
Namval_t *mp,fake;
char *val;
int first=0;
fake.nvname = ".sh.math.";
mp = (Namval_t*)dtprev(shp->fun_tree,&fake);
while(mp=(Namval_t*)dtnext(shp->fun_tree,mp))
{
if(memcmp(mp->nvname,".sh.math.",9))
break;
if(first++)
sfputc(shp->strbuf,' ');
sfputr(shp->strbuf,mp->nvname+9,-1);
}
val = sfstruse(shp->strbuf);
return(val);
}
static char *setdisc_any(Namval_t *np, const char *event, Namval_t *action, Namfun_t *fp)
{
Shell_t *shp=nv_shell(np);
Namval_t *mp,fake;
char *name;
int getname=0, off=staktell();
fake.nvname = nv_name(np);
if(!event)
{
if(!action)
{
mp = (Namval_t*)dtprev(shp->fun_tree,&fake);
return((char*)dtnext(shp->fun_tree,mp));
}
getname = 1;
}
stakputs(fake.nvname);
stakputc('.');
stakputs(event);
stakputc(0);
name = stakptr(off);
mp = nv_search(name, shp->fun_tree, action?NV_ADD:0);
stakseek(off);
if(getname)
return(mp?(char*)dtnext(shp->fun_tree,mp):0);
if(action==np)
action = mp;
return(action?(char*)action:"");
}
static const Namdisc_t SH_MATH_disc = { 0, 0, get_math, 0, setdisc_any, create_math, };
#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(Namfun_t), 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)
{
Shell_t *shp = sh_getinterp();
register char *arg;
if(!name)
setenviron(value);
else if(strcmp(name,"UNIVERSE")==0 && strcmp(astconf(name,0,0),value))
{
shp->universe = 0;
/* set directory in new universe */
if(*(arg = path_pwd(shp,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<<CHAR_BIT); i++)
d[t[i]] = s[i];
}
static void init_ebcdic(void)
{
int i;
char *cp = (char*)malloc(ST_NONE*(1<<CHAR_BIT));
for(i=0; i < ST_NONE; i++)
{
a2e(cp,sh_lexrstates[i]);
sh_lexstates[i] = cp;
cp += (1<<CHAR_BIT);
}
}
#endif
/*
* return SH_TYPE_* bitmask for path
* 0 for "not a shell"
*/
int sh_type(register const char *path)
{
register const char* s;
register int t = 0;
if (s = (const char*)strrchr(path, '/'))
{
if (*path == '-')
t |= SH_TYPE_LOGIN;
s++;
}
else
s = path;
if (*s == '-')
{
s++;
t |= SH_TYPE_LOGIN;
}
for (;;)
{
if (!(t & SH_TYPE_KSH))
{
if (*s == 'k')
{
s++;
t |= SH_TYPE_KSH;
continue;
}
}
if (!(t & (SH_TYPE_PROFILE|SH_TYPE_RESTRICTED)))
{
#if SHOPT_PFSH
if (*s == 'p' && *(s+1) == 'f')
{
s += 2;
t |= SH_TYPE_PROFILE;
continue;
}
#endif
if (*s == 'r')
{
s++;
t |= SH_TYPE_RESTRICTED;
continue;
}
}
break;
}
if (*s++ == 's' && (*s == 'h' || *s == 'u'))
{
s++;
t |= SH_TYPE_SH;
#if _WINIX
if (!(t & SH_TYPE_KSH) && (!*s || *s == '.'))
#else
if (!(t & SH_TYPE_KSH) && !*s)
#endif
t |= SH_TYPE_POSIX;
if ((t & SH_TYPE_KSH) && *s == '9' && *(s+1) == '3')
s += 2;
#if _WINIX
if (*s == '.' && *(s+1) == 'e' && *(s+2) == 'x' && *(s+3) == 'e')
s += 4;
#endif
if (!isalnum(*s))
return t;
}
return t & ~(SH_TYPE_KSH|SH_TYPE_PROFILE|SH_TYPE_RESTRICTED);
}
static char *get_mode(Namval_t* np, Namfun_t* nfp)
{
mode_t mode = nv_getn(np,nfp);
return(fmtperm(mode));
}
static void put_mode(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
{
if(val)
{
mode_t mode;
char *last=0;
if(flag&NV_INTEGER)
{
if(flag&NV_LONG)
mode = *(Sfdouble_t*)val;
else
mode = *(double*)val;
}
else
mode = strperm(val, &last,0);
if(*last)
errormsg(SH_DICT,ERROR_exit(1),"%s: invalid mode string",val);
nv_putv(np,(char*)&mode,NV_INTEGER,nfp);
}
else
nv_putv(np,val,flag,nfp);
}
static const Namdisc_t modedisc =
{
0,
put_mode,
get_mode,
};
/*
* initialize the shell
*/
Shell_t *sh_init(register int argc,register char *argv[], Shinit_f userinit)
{
static int beenhere;
Shell_t *shp;
register int n;
int type;
static char *login_files[3];
memfatal();
n = strlen(e_version);
if(e_version[n-1]=='$' && e_version[n-2]==' ')
e_version[n-2]=0;
#if (CC_NATIVE == CC_ASCII)
memcpy(sh_lexstates,sh_lexrstates,ST_NONE*sizeof(char*));
#else
init_ebcdic();
#endif
if(!beenhere)
{
beenhere = 1;
shp = &sh;
#if SHOPT_REGRESS
sh_regress_init(shp);
#endif
shgd = newof(0,struct shared,1,0);
shgd->current_pid = shgd->pid = getpid();
shgd->ppid = getppid();
shgd->userid=getuid();
shgd->euserid=geteuid();
shgd->groupid=getgid();
shgd->egroupid=getegid();
shgd->lim.clk_tck = getconf("CLK_TCK");
shgd->lim.arg_max = getconf("ARG_MAX");
shgd->lim.child_max = getconf("CHILD_MAX");
shgd->lim.ngroups_max = getconf("NGROUPS_MAX");
shgd->lim.posix_version = getconf("VERSION");
shgd->lim.posix_jobcontrol = getconf("JOB_CONTROL");
if(shgd->lim.arg_max <=0)
shgd->lim.arg_max = ARG_MAX;
if(shgd->lim.child_max <=0)
shgd->lim.child_max = CHILD_MAX;
if(shgd->lim.clk_tck <=0)
shgd->lim.clk_tck = CLK_TCK;
shgd->ed_context = (void*)ed_open(shp);
error_info.exit = sh_exit;
error_info.id = path_basename(argv[0]);
}
else
shp = newof(0,Shell_t,1,0);
umask(shp->mask=umask(0));
shp->gd = shgd;
shp->mac_context = sh_macopen(shp);
shp->arg_context = sh_argopen(shp);
shp->lex_context = (void*)sh_lexopen(0,shp,1);
shp->strbuf = sfstropen();
shp->stk = stkstd;
sfsetbuf(shp->strbuf,(char*)0,64);
sh_onstate(SH_INIT);
error_info.catalog = e_dict;
#if SHOPT_REGRESS
{
Opt_t* nopt;
Opt_t* oopt;
char* a;
char** av = argv;
char* regress[3];
regress[0] = "__regress__";
regress[2] = 0;
/* NOTE: only shp is used by __regress__ at this point */
shp->bltindata.shp = shp;
while ((a = *++av) && a[0] == '-' && (a[1] == 'I' || a[1] == '-' && a[2] == 'r'))
{
if (a[1] == 'I')
{
if (a[2])
regress[1] = a + 2;
else if (!(regress[1] = *++av))
break;
}
else if (strncmp(a+2, "regress", 7))
break;
else if (a[9] == '=')
regress[1] = a + 10;
else if (!(regress[1] = *++av))
break;
nopt = optctx(0, 0);
oopt = optctx(nopt, 0);
error_info.exit = exit; /* avoid crash on b___regress__ error as shell is not fully initialized */
b___regress__(2, regress, &shp->bltindata);
error_info.exit = sh_exit;
optctx(oopt, nopt);
}
}
#endif
shp->cpipe[0] = -1;
shp->coutpipe = -1;
for(n=0;n < 10; n++)
{
/* don't use lower bits when rand() generates large numbers */
if(rand() > RANDMASK)
{
rand_shift = 3;
break;
}
}
sh_ioinit(shp);
/* initialize signal handling */
sh_siginit(shp);
stakinstall(NIL(Stak_t*),nospace);
/* set up memory for name-value pairs */
shp->init_context = nv_init(shp);
/* initialize shell type */
if(argc>0)
{
type = sh_type(*argv);
if(type&SH_TYPE_LOGIN)
shp->login_sh = 2;
if(type&SH_TYPE_POSIX)
sh_onoption(SH_POSIX);
}
/* read the environment; don't import attributes yet */
env_init(shp,0);
if(!ENVNOD->nvalue.cp)
{
sfprintf(shp->strbuf,"%s/.kshrc",nv_getval(HOME));
nv_putval(ENVNOD,sfstruse(shp->strbuf),NV_RDONLY);
}
*SHLVL->nvalue.ip +=1;
nv_offattr(SHLVL,NV_IMPORT);
#if SHOPT_SPAWN
{
/*
* try to find the pathname for this interpreter
* try using environment variable _ or argv[0]
*/
char *cp=nv_getval(L_ARGNOD);
char buff[PATH_MAX+1];
shp->gd->shpath = 0;
if((n = pathprog(NiL, buff, sizeof(buff))) > 0 && n <= sizeof(buff))
shp->gd->shpath = strdup(buff);
else if((cp && (sh_type(cp)&SH_TYPE_SH)) || (argc>0 && strchr(cp= *argv,'/')))
{
if(*cp=='/')
shp->gd->shpath = strdup(cp);
else if(cp = nv_getval(PWDNOD))
{
int offset = staktell();
stakputs(cp);
stakputc('/');
stakputs(argv[0]);
pathcanon(stakptr(offset),PATH_DOTDOT);
shp->gd->shpath = strdup(stakptr(offset));
stakseek(offset);
}
}
}
#endif
nv_putval(IFSNOD,(char*)e_sptbnl,NV_RDONLY);
astconfdisc(newconf);
#if SHOPT_TIMEOUT
shp->st.tmout = SHOPT_TIMEOUT;
#endif /* SHOPT_TIMEOUT */
/* initialize jobs table */
job_clear();
sh_onoption(SH_MULTILINE);
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
/* look for options */
/* shp->st.dolc is $# */
if((shp->st.dolc = sh_argopts(-argc,argv,shp)) < 0)
{
shp->exitval = 2;
sh_done(shp,0);
}
opt_info.disc = 0;
shp->st.dolv=argv+(argc-1)-shp->st.dolc;
shp->st.dolv[0] = argv[0];
if(shp->st.dolc < 1)
sh_onoption(SH_SFLAG);
if(!sh_isoption(SH_SFLAG))
{
shp->st.dolc--;
shp->st.dolv++;
#if _WINIX
{
char* name;
name = shp->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(beenhere==1)
{
struct lconv* lc;
shp->decomma = (lc=localeconv()) && lc->decimal_point && *lc->decimal_point==',';
beenhere = 2;
}
}
/* import variable attributes from environment */
if(!sh_isoption(SH_POSIX))
env_init(shp,1);
#if SHOPT_PFSH
if (sh_isoption(SH_PFSH))
{
struct passwd *pw = getpwuid(shp->gd->userid);
if(pw)
shp->gd->user = strdup(pw->pw_name);
}
#endif
/* set[ug]id scripts require the -p flag */
if(shp->gd->userid!=shp->gd->euserid || shp->gd->groupid!=shp->gd->egroupid)
{
#ifdef SHOPT_P_SUID
/* require sh -p to run setuid and/or setgid */
if(!sh_isoption(SH_PRIVILEGED) && shp->gd->userid >= SHOPT_P_SUID)
{
setuid(shp->gd->euserid=shp->gd->userid);
setgid(shp->gd->egroupid=shp->gd->groupid);
}
else
#endif /* SHOPT_P_SUID */
sh_onoption(SH_PRIVILEGED);
#ifdef SHELLMAGIC
/* careful of #! setuid scripts with name beginning with - */
if(shp->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(sh_isdevfd(argv[1]))
shp->shname = strdup(argv[0]);
else
shp->shname = strdup(shp->st.dolv[0]);
/*
* return here for shell script execution
* but not for parenthesis subshells
*/
error_info.id = strdup(shp->st.dolv[0]); /* error_info.id is $0 */
shp->jmpbuffer = (void*)&shp->checkbase;
sh_pushcontext(shp,&shp->checkbase,SH_JMPSCRIPT);
shp->st.self = &shp->global;
shp->topscope = (Shscope_t*)shp->st.self;
sh_offstate(SH_INIT);
login_files[0] = (char*)e_profile;
login_files[1] = ".profile";
shp->gd->login_files = login_files;
shp->bltindata.version = SH_VERSION;
shp->bltindata.shp = shp;
shp->bltindata.shrun = sh_run;
shp->bltindata.shtrap = sh_trap;
shp->bltindata.shexit = sh_exit;
shp->bltindata.shbltin = sh_addbuiltin;
shp->bltindata.shgetenv = sh_getenv;
shp->bltindata.shsetenv = sh_setenviron;
astintercept(&shp->bltindata,1);
#if 0
#define NV_MKINTTYPE(x,y,z) nv_mkinttype(#x,sizeof(x),(x)-1<0,(y),(Namdisc_t*)z);
NV_MKINTTYPE(pid_t,"process id",0);
NV_MKINTTYPE(gid_t,"group id",0);
NV_MKINTTYPE(uid_t,"user id",0);
NV_MKINTTYPE(size_t,(const char*)0,0);
NV_MKINTTYPE(ssize_t,(const char*)0,0);
NV_MKINTTYPE(off_t,"offset in bytes",0);
NV_MKINTTYPE(ino_t,"\ai-\anode number",0);
NV_MKINTTYPE(mode_t,(const char*)0,&modedisc);
NV_MKINTTYPE(dev_t,"device id",0);
NV_MKINTTYPE(nlink_t,"hard link count",0);
NV_MKINTTYPE(blkcnt_t,"block count",0);
NV_MKINTTYPE(time_t,"seconds since the epoch",0);
nv_mkstat();
#endif
if(shp->userinit=userinit)
(*userinit)(shp, 0);
return(shp);
}
Shell_t *sh_getinterp(void)
{
return(&sh);
}
/*
* reinitialize before executing a script
*/
int sh_reinit(char *argv[])
{
Shell_t *shp = sh_getinterp();
Shopt_t opt;
Namval_t *np,*npnext;
Dt_t *dp;
struct adata
{
Shell_t *sh;
void *extra[2];
} data;
for(np=dtfirst(shp->fun_tree);np;np=npnext)
{
if((dp=shp->fun_tree)->walk)
dp = dp->walk;
npnext = (Namval_t*)dtnext(shp->fun_tree,np);
if(np>= shgd->bltin_cmds && np < &shgd->bltin_cmds[nbltins])
continue;
if(is_abuiltin(np) && nv_isattr(np,NV_EXPORT))
continue;
if(*np->nvname=='/')
continue;
nv_delete(np,dp,NV_NOFREE);
}
dtclose(shp->alias_tree);
shp->alias_tree = dtopen(&_Nvdisc,Dtoset);
shp->last_root = shp->var_tree;
shp->inuse_bits = 0;
if(shp->userinit)
(*shp->userinit)(shp, 1);
if(shp->heredocs)
{
sfclose(shp->heredocs);
shp->heredocs = 0;
}
/* remove locals */
sh_onstate(SH_INIT);
memset(&data,0,sizeof(data));
data.sh = shp;
nv_scan(shp->var_tree,sh_envnolocal,(void*)&data,NV_EXPORT,0);
nv_scan(shp->var_tree,sh_envnolocal,(void*)&data,NV_ARRAY,NV_ARRAY);
sh_offstate(SH_INIT);
memset(shp->st.trapcom,0,(shp->st.trapmax+1)*sizeof(char*));
memset((void*)&opt,0,sizeof(opt));
#if SHOPT_NAMESPACE
if(shp->namespace)
{
dp=nv_dict(shp->namespace);
if(dp==shp->var_tree)
shp->var_tree = dtview(dp,0);
_nv_unset(shp->namespace,NV_RDONLY);
shp->namespace = 0;
}
#endif /* SHOPT_NAMESPACE */
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);
shp->options = opt;
/* set up new args */
if(argv)
shp->arglist = sh_argcreate(argv);
if(shp->arglist)
sh_argreset(shp,shp->arglist,NIL(struct dolnod*));
shp->envlist=0;
shp->curenv = 0;
shp->shname = error_info.id = strdup(shp->st.dolv[0]);
sh_offstate(SH_FORKED);
shp->fn_depth = shp->dot_depth = 0;
sh_sigreset(0);
if(!(SHLVL->nvalue.ip))
{
shlvl = 0;
SHLVL->nvalue.ip = &shlvl;
nv_onattr(SHLVL,NV_INTEGER|NV_EXPORT|NV_NOFREE);
}
*SHLVL->nvalue.ip +=1;
nv_offattr(SHLVL,NV_IMPORT);
shp->st.filename = strdup(shp->lastarg);
nv_delete((Namval_t*)0, (Dt_t*)0, 0);
job.exitval = 0;
shp->inpipe = shp->outpipe = 0;
job_clear();
job.in_critical = 0;
return(1);
}
/*
* set when creating a local variable of this name
*/
Namfun_t *nv_cover(register Namval_t *np)
{
if(np==IFSNOD || np==PATHNOD || np==SHELLNOD || np==FPATHNOD || np==CDPNOD || np==SECONDS || np==ENVNOD || np==LINENO)
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 const char *shdiscnames[] = { "tilde", 0};
#ifdef SHOPT_STATS
struct Stats
{
Namfun_t hdr;
Shell_t *sh;
char *nodes;
int numnodes;
int current;
};
static Namval_t *next_stat(register Namval_t* np, Dt_t *root,Namfun_t *fp)
{
struct Stats *sp = (struct Stats*)fp;
if(!root)
sp->current = 0;
else if(++sp->current>=sp->numnodes)
return(0);
return(nv_namptr(sp->nodes,sp->current));
}
static Namval_t *create_stat(Namval_t *np,const char *name,int flag,Namfun_t *fp)
{
struct Stats *sp = (struct Stats*)fp;
register const char *cp=name;
register int i=0,n;
Namval_t *nq=0;
Shell_t *shp = sp->sh;
if(!name)
return(SH_STATS);
while((i=*cp++) && i != '=' && i != '+' && i!='[');
n = (cp-1) -name;
for(i=0; i < sp->numnodes; i++)
{
nq = nv_namptr(sp->nodes,i);
if((n==0||memcmp(name,nq->nvname,n)==0) && nq->nvname[n]==0)
goto found;
}
nq = 0;
found:
if(nq)
{
fp->last = (char*)&name[n];
shp->last_table = SH_STATS;
}
else
errormsg(SH_DICT,ERROR_exit(1),e_notelem,n,name,nv_name(np));
return(nq);
}
static const Namdisc_t stat_disc =
{
0, 0, 0, 0, 0,
create_stat,
0, 0,
next_stat
};
static char *name_stat(Namval_t *np, Namfun_t *fp)
{
Shell_t *shp = sh_getinterp();
sfprintf(shp->strbuf,".sh.stats.%s",np->nvname);
return(sfstruse(shp->strbuf));
}
static const Namdisc_t stat_child_disc =
{
0,0,0,0,0,0,0,
name_stat
};
static Namfun_t stat_child_fun =
{
&stat_child_disc, 1, 0, sizeof(Namfun_t)
};
static void stat_init(Shell_t *shp)
{
int i,nstat = STAT_SUBSHELL+1;
struct Stats *sp = newof(0,struct Stats,1,nstat*NV_MINSZ);
Namval_t *np;
sp->numnodes = nstat;
sp->nodes = (char*)(sp+1);
shgd->stats = (int*)calloc(sizeof(int),nstat);
sp->sh = shp;
for(i=0; i < nstat; i++)
{
np = nv_namptr(sp->nodes,i);
np->nvfun = &stat_child_fun;
np->nvname = (char*)shtab_stats[i].sh_name;
nv_onattr(np,NV_RDONLY|NV_MINIMAL|NV_NOFREE|NV_INTEGER);
nv_setsize(np,10);
np->nvalue.ip = &shgd->stats[i];
}
sp->hdr.dsize = sizeof(struct Stats) + nstat*(sizeof(int)+NV_MINSZ);
sp->hdr.disc = &stat_disc;
nv_stack(SH_STATS,&sp->hdr);
sp->hdr.nofree = 1;
nv_setvtree(SH_STATS);
}
#else
# define stat_init(x)
#endif /* SHOPT_STATS */
/*
* Initialize the shell name and alias table
*/
static Init_t *nv_init(Shell_t *shp)
{
double d=0;
ip = newof(0,Init_t,1,0);
if(!ip)
return(0);
shp->nvfun.last = (char*)shp;
shp->nvfun.nofree = 1;
ip->sh = shp;
shp->var_base = shp->var_tree = sh_inittree(shp,shtab_variables);
SHLVL->nvalue.ip = &shlvl;
ip->IFS_init.hdr.disc = &IFS_disc;
ip->PATH_init.disc = &RESTRICTED_disc;
ip->PATH_init.nofree = 1;
ip->FPATH_init.disc = &RESTRICTED_disc;
ip->FPATH_init.nofree = 1;
ip->CDPATH_init.disc = &CDPATH_disc;
ip->CDPATH_init.nofree = 1;
ip->SHELL_init.disc = &RESTRICTED_disc;
ip->SHELL_init.nofree = 1;
ip->ENV_init.disc = &RESTRICTED_disc;
ip->ENV_init.nofree = 1;
ip->VISUAL_init.disc = &EDITOR_disc;
ip->VISUAL_init.nofree = 1;
ip->EDITOR_init.disc = &EDITOR_disc;
ip->EDITOR_init.nofree = 1;
ip->HISTFILE_init.disc = &HISTFILE_disc;
ip->HISTFILE_init.nofree = 1;
ip->HISTSIZE_init.disc = &HISTFILE_disc;
ip->HISTSIZE_init.nofree = 1;
ip->OPTINDEX_init.disc = &OPTINDEX_disc;
ip->OPTINDEX_init.nofree = 1;
ip->SECONDS_init.hdr.disc = &SECONDS_disc;
ip->SECONDS_init.hdr.nofree = 1;
ip->RAND_init.hdr.disc = &RAND_disc;
ip->RAND_init.hdr.nofree = 1;
ip->RAND_init.sh = shp;
ip->SH_MATCH_init.hdr.disc = &SH_MATCH_disc;
ip->SH_MATCH_init.hdr.nofree = 1;
ip->SH_MATH_init.disc = &SH_MATH_disc;
ip->SH_MATH_init.nofree = 1;
ip->SH_VERSION_init.disc = &SH_VERSION_disc;
ip->SH_VERSION_init.nofree = 1;
ip->LINENO_init.disc = &LINENO_disc;
ip->LINENO_init.nofree = 1;
ip->L_ARG_init.disc = &L_ARG_disc;
ip->L_ARG_init.nofree = 1;
#ifdef _hdr_locale
ip->LC_TYPE_init.disc = &LC_disc;
ip->LC_TYPE_init.nofree = 1;
ip->LC_NUM_init.disc = &LC_disc;
ip->LC_NUM_init.nofree = 1;
ip->LC_COLL_init.disc = &LC_disc;
ip->LC_COLL_init.nofree = 1;
ip->LC_MSG_init.disc = &LC_disc;
ip->LC_MSG_init.nofree = 1;
ip->LC_ALL_init.disc = &LC_disc;
ip->LC_ALL_init.nofree = 1;
ip->LANG_init.disc = &LC_disc;
ip->LANG_init.nofree = 1;
#endif /* _hdr_locale */
nv_stack(IFSNOD, &ip->IFS_init.hdr);
ip->IFS_init.hdr.nofree = 1;
nv_stack(PATHNOD, &ip->PATH_init);
nv_stack(FPATHNOD, &ip->FPATH_init);
nv_stack(CDPNOD, &ip->CDPATH_init);
nv_stack(SHELLNOD, &ip->SHELL_init);
nv_stack(ENVNOD, &ip->ENV_init);
nv_stack(VISINOD, &ip->VISUAL_init);
nv_stack(EDITNOD, &ip->EDITOR_init);
nv_stack(HISTFILE, &ip->HISTFILE_init);
nv_stack(HISTSIZE, &ip->HISTSIZE_init);
nv_stack(OPTINDNOD, &ip->OPTINDEX_init);
nv_stack(SECONDS, &ip->SECONDS_init.hdr);
nv_stack(L_ARGNOD, &ip->L_ARG_init);
nv_putval(SECONDS, (char*)&d, NV_DOUBLE);
nv_stack(RANDNOD, &ip->RAND_init.hdr);
d = (shp->gd->pid&RANDMASK);
nv_putval(RANDNOD, (char*)&d, NV_DOUBLE);
nv_stack(LINENO, &ip->LINENO_init);
SH_MATCHNOD->nvfun = &ip->SH_MATCH_init.hdr;
nv_putsub(SH_MATCHNOD,(char*)0,10);
nv_stack(SH_MATHNOD, &ip->SH_MATH_init);
nv_stack(SH_VERSIONNOD, &ip->SH_VERSION_init);
#ifdef _hdr_locale
nv_stack(LCTYPENOD, &ip->LC_TYPE_init);
nv_stack(LCALLNOD, &ip->LC_ALL_init);
nv_stack(LCMSGNOD, &ip->LC_MSG_init);
nv_stack(LCCOLLNOD, &ip->LC_COLL_init);
nv_stack(LCNUMNOD, &ip->LC_NUM_init);
nv_stack(LANGNOD, &ip->LANG_init);
#endif /* _hdr_locale */
(PPIDNOD)->nvalue.lp = (&shp->gd->ppid);
(SH_PIDNOD)->nvalue.lp = (&shp->gd->current_pid);
(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 = dtopen(&_Nvdisc,Dtoset);
shp->track_tree = dtopen(&_Nvdisc,Dtset);
shp->bltin_tree = sh_inittree(shp,(const struct shtable2*)shtab_builtins);
shp->fun_tree = dtopen(&_Nvdisc,Dtoset);
dtview(shp->fun_tree,shp->bltin_tree);
nv_mount(DOTSHNOD, "type", shp->typedict=dtopen(&_Nvdisc,Dtoset));
nv_adddisc(DOTSHNOD, shdiscnames, (Namval_t**)0);
DOTSHNOD->nvalue.cp = Empty;
nv_onattr(DOTSHNOD,NV_RDONLY);
SH_LINENO->nvalue.ip = &shp->st.lineno;
VERSIONNOD->nvalue.nrp = newof(0,struct Namref,1,0);
VERSIONNOD->nvalue.nrp->np = SH_VERSIONNOD;
VERSIONNOD->nvalue.nrp->root = nv_dict(DOTSHNOD);
VERSIONNOD->nvalue.nrp->table = DOTSHNOD;
nv_onattr(VERSIONNOD,NV_REF);
math_init(shp);
if(!shgd->stats)
stat_init(shp);
return(ip);
}
/*
* initialize name-value pairs
*/
Dt_t *sh_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(!shgd->bltin_nodes)
{
shgd->bltin_nodes = np;
shgd->bltin_nnodes = n;
}
else if(name_vals==(const struct shtable2*)shtab_builtins)
{
shgd->bltin_cmds = np;
nbltins = n;
}
base_treep = treep = dtopen(&_Nvdisc,Dtoset);
treep->user = (void*)shp;
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 = (Nambfp_f)((struct shtable3*)tp)->sh_value;
else
{
if(name_vals == shtab_variables)
np->nvfun = &shp->nvfun;
np->nvalue.cp = (char*)tp->sh_value;
}
nv_setattr(np,tp->sh_number);
if(nv_isattr(np,NV_TABLE))
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
*
* Must be called with import_attributes == 0 first, then again with
* import_attributes == 1 if variable attributes are to be imported
* from the environment.
*/
static void env_init(Shell_t *shp, int import_attributes)
{
register char *cp;
register Namval_t *np,*mp;
register char **ep=environ;
char *dp;
int nenv=0,k=0,size=0;
Namval_t *np0;
static char *next=0; /* next variable whose attributes to import */
if(import_attributes)
goto import_attributes;
if(!ep)
goto skip;
while(*ep++)
nenv++;
np = newof(0,Namval_t,nenv,0);
for(np0=np,ep=environ;cp= *ep; ep++)
{
dp = strchr(cp,'=');
if(!dp)
continue;
*dp++ = 0;
if(mp = dtmatch(shp->var_base,cp))
{
mp->nvenv = (char*)cp;
dp[-1] = '=';
}
else if(strcmp(cp,e_envmarker)==0)
{
dp[-1] = '=';
next = cp + strlen(e_envmarker);
continue;
}
else
{
k++;
mp = np++;
mp->nvname = cp;
size += strlen(cp);
}
nv_onattr(mp,NV_IMPORT);
if(mp->nvfun || nv_isattr(mp,NV_INTEGER))
nv_putval(mp,dp,0);
else
{
mp->nvalue.cp = dp;
nv_onattr(mp,NV_NOFREE);
}
nv_onattr(mp,NV_EXPORT|NV_IMPORT);
}
np = (Namval_t*)realloc((void*)np0,k*sizeof(Namval_t));
dp = (char*)malloc(size+k);
while(k-->0)
{
size = strlen(np->nvname);
memcpy(dp,np->nvname,size+1);
np->nvname[size] = '=';
np->nvenv = np->nvname;
np->nvname = dp;
dp += size+1;
dtinsert(shp->var_base,np++);
}
skip:
if(nv_isnull(PWDNOD) || nv_isattr(PWDNOD,NV_TAGGED))
{
nv_offattr(PWDNOD,NV_TAGGED);
path_pwd(shp,0);
}
if((cp = nv_getval(SHELLNOD)) && (sh_type(cp)&SH_TYPE_RESTRICTED))
sh_onoption(SH_RESTRICTED); /* restricted shell */
return;
/* Import variable attributes from environment (from variable named by e_envmarker) */
import_attributes:
while(cp=next)
{
if(next = strchr(++cp,'='))
*next = 0;
np = nv_search(cp+2,shp->var_tree,NV_ADD);
if(np!=SHLVL && 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 *val = nv_getval(np);
strtol(val,&dp,10);
if(*dp=='.' || *dp=='e' || *dp=='E')
{
char *lp;
flag |= NV_DOUBLE;
if(*dp=='.')
{
strtol(dp+1,&lp,10);
if(*lp)
dp = lp;
}
if(*dp && *dp!='.')
{
flag |= NV_EXPNOTE;
size = dp-val;
}
else
size = strlen(dp);
size--;
}
}
nv_newattr(np,flag|NV_IMPORT|NV_EXPORT,size);
if((flag&(NV_INTEGER|NV_UTOL|NV_LTOU))==(NV_UTOL|NV_LTOU))
nv_mapchar(np,(flag&NV_UTOL)?e_tolower:e_toupper);
}
else
cp += 2;
}
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 (Shell_t *shp)
{
if(!shp)
shp = sh_getinterp();
sh_sigcheck(shp);
}
Dt_t* sh_bltin_tree DISABLE (void)
{
return(sh.bltin_tree);
}
/*
* This code is for character mapped variables with wctrans()
*/
struct Mapchar
{
Namfun_t hdr;
const char *name;
wctrans_t trans;
int lctype;
};
static void put_trans(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
struct Mapchar *mp = (struct Mapchar*)fp;
int c,offset = staktell(),off=offset;
if(val)
{
if(mp->lctype!=lctype)
{
mp->lctype = lctype;
mp->trans = wctrans(mp->name);
}
if(!mp->trans || (flags&NV_INTEGER))
goto skip;
while(c = mbchar(val))
{
c = towctrans(c,mp->trans);
stakseek(off+c);
stakseek(off);
c = mbconv(stakptr(off),c);
off += c;
stakseek(off);
}
stakputc(0);
val = stakptr(offset);
}
else
{
nv_putv(np,val,flags,fp);
nv_disc(np,fp,NV_POP);
if(!(fp->nofree&1))
free((void*)fp);
stakseek(offset);
return;
}
skip:
nv_putv(np,val,flags,fp);
stakseek(offset);
}
static const Namdisc_t TRANS_disc = { sizeof(struct Mapchar), put_trans };
Namfun_t *nv_mapchar(Namval_t *np,const char *name)
{
wctrans_t trans = name?wctrans(name):0;
struct Mapchar *mp=0;
int n=0,low;
if(np)
mp = (struct Mapchar*)nv_hasdisc(np,&TRANS_disc);
if(!name)
return(mp?(Namfun_t*)mp->name:0);
if(!trans)
return(0);
if(!np)
return(((Namfun_t*)0)+1);
if((low=strcmp(name,e_tolower)) && strcmp(name,e_toupper))
n += strlen(name)+1;
if(mp)
{
if(strcmp(name,mp->name)==0)
return(&mp->hdr);
nv_disc(np,&mp->hdr,NV_POP);
if(!(mp->hdr.nofree&1))
free((void*)mp);
}
mp = newof(0,struct Mapchar,1,n);
mp->trans = trans;
mp->lctype = lctype;
if(low==0)
mp->name = e_tolower;
else if(n==0)
mp->name = e_toupper;
else
{
mp->name = (char*)(mp+1);
strcpy((char*)mp->name,name);
}
mp->hdr.disc = &TRANS_disc;
return(&mp->hdr);
}