1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
cde/src/cmd/ksh93/sh/init.c
Martijn Dekker c828ea8d0d Fix typeset -u/-l on NetBSD
On NetBSD, for some reason, the wctrans(3) and towctrans(3) C
library functions exist, but have no effect; the "toupper" and
"tolower" maps don't even translate case for ASCII, never mind wide
characters. This kills 'typeset -u' and 'typeset -l' on ksh, which
was the cause of most of the regression test failures on NetBSD.
Fallback versions for these functions are provided in init.c, but
were not being used on NetBSD because the feature test detected the
presence of these functions in the C library.

src/cmd/ksh93/features/locale:
- Replace the simple test for the presence of wctrans(3),
  towctrans(3), and the wctrans_t type by an actual feature test
  that checks that these functions not only compile, but are also
  capable of changing an ASCII 'q' to upper case and back again.

src/cmd/ksh93/sh/init.c: towctrans():
- Add wide character support to the fallback function, for whatever
  good that may do; on NetBSD, the wide-character towupper(3) and
  towlower(3) functions only change case for ASCII.
2021-05-18 18:26:33 +02:00

2145 lines
50 KiB
C

/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1982-2012 AT&T Intellectual Property *
* Copyright (c) 2020-2021 Contributors to ksh 93u+m *
* 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 <math.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 _lib_towupper && _lib_towlower
if(mbwide())
{
if(t==1 && iswupper((wint_t)c))
c = (int)towlower((wint_t)c);
else if(t==2 && iswlower((wint_t)c))
c = (int)towupper((wint_t)c);
}
else
#endif
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 !_std_malloc && !_AST_std_malloc
#define ATTRS 1
"v" /* uses vmalloc */
#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 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*)+sizeof(Dtlink_t)];
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;
#if SHOPT_VSH || SHOPT_ESH
Namfun_t VISUAL_init;
Namfun_t EDITOR_init;
#endif
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_TIME_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 char *env_init(Shell_t*);
static void env_import_attributes(Shell_t*,char*);
static Init_t *nv_init(Shell_t*);
static int shlvl;
static int rand_shift;
/*
* out of memory routine for stak routines
*/
static noreturn char *nomemory(int unused)
{
NOT_USED(unused);
errormsg(SH_DICT, ERROR_SYSTEM|ERROR_PANIC, "out of memory");
UNREACHABLE();
}
/*
* The following are wrapper functions for memory allocation.
* These functions will error out if the allocation fails.
*/
void *sh_malloc(size_t size)
{
void *cp = malloc(size);
if(!cp)
nomemory(0);
return(cp);
}
void *sh_realloc(void *ptr, size_t size)
{
void *cp = realloc(ptr, size);
if(!cp)
nomemory(0);
return(cp);
}
void *sh_calloc(size_t nmemb, size_t size)
{
void *cp = calloc(nmemb, size);
if(!cp)
nomemory(0);
return(cp);
}
char *sh_strdup(const char *s)
{
char *dup = strdup(s);
if(!dup)
nomemory(0);
return(dup);
}
void *sh_memdup(const void *s, size_t n)
{
void *dup = memdup(s, n);
if(!dup)
nomemory(0);
return(dup);
}
#if SHOPT_VSH || SHOPT_ESH
/* 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 = sh_getinterp();
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 SHOPT_VSH
if(strmatch(cp,"*[Vv][Ii]*"))
newopt=SH_VI;
#endif
#if SHOPT_VSH && SHOPT_ESH
else
#endif
#if SHOPT_ESH
if(strmatch(cp,"*gmacs*"))
newopt=SH_GMACS;
else if(strmatch(cp,"*macs*"))
newopt=SH_EMACS;
#endif
if(newopt)
{
#if SHOPT_VSH
sh_offoption(SH_VI);
#endif
#if SHOPT_ESH
sh_offoption(SH_EMACS);
sh_offoption(SH_GMACS);
#endif
sh_onoption(newopt);
}
done:
nv_putv(np, val, flags, fp);
}
#endif /* SHOPT_VSH || SHOPT_ESH */
/* 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 = sh_getinterp();
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 = sh_getinterp();
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*)sh_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 = sh_getinterp();
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));
UNREACHABLE();
}
if(np==PATHNOD || (path_scoped=(strcmp(name,PATHNOD->nvname)==0)))
{
/* Clear the hash table */
nv_scan(sh_subtracktree(1),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 = sh_getinterp();
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
/* Trap for the LC_* and LANG variables */
static void put_lang(Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
Shell_t *shp = sh_getinterp();
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;
else if(name==(LCTIMENOD)->nvname)
type = LC_TIME;
#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*)sh_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 = sh_getinterp();
value = nv_getv(np,fp);
if(np!=ip->ifsnp)
{
ip->ifsnp = np;
memset(shp->ifstable,0,(1<<CHAR_BIT));
if(cp=value)
{
while(n = mbsize(cp), c = *(unsigned char*)cp++)
{
if(n>1)
{
cp += (n-1);
shp->ifstable[c] = S_MBYTE;
continue;
}
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;
}
shp->ifstable[0] = S_EOF;
}
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 = sh_getinterp();
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(&sh,val);
srand(rp->rand_seed = (unsigned int)n);
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)
{
struct rand *rp = (struct rand*)fp;
register long cur, last= *np->nvalue.lp;
NOT_USED(fp);
do
cur = (rand_r(&rp->rand_seed)>>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));
}
void sh_reseed_rand(struct rand *rp)
{
struct tms tp;
unsigned int time;
static unsigned int seq;
timeofday(&tp);
time = (unsigned int)remainder(dtime(&tp) * 10000.0, (double)UINT_MAX);
srand(rp->rand_seed = shgd->current_pid ^ time ^ ++seq);
rp->rand_last = -1;
}
/*
* These three routines are for LINENO
*/
static Sfdouble_t nget_lineno(Namval_t* np, Namfun_t *fp)
{
int 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((Sfdouble_t)d);
}
static void put_lineno(Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
Sfdouble_t n;
Shell_t *shp = sh_getinterp();
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 = (Sfdouble_t)(*(double*)val);
else
n = sh_arith(shp,val);
shp->st.firstline += (int)(nget_lineno(np,fp) + 1 - n);
}
static char* get_lineno(register Namval_t* np, Namfun_t *fp)
{
long n = (long)nget_lineno(np,fp);
return(fmtbase(n, 10, 0));
}
static char* get_lastarg(Namval_t* np, Namfun_t *fp)
{
Shell_t *shp = sh_getinterp();
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 = sh_getinterp();
if(flags&NV_INTEGER)
{
sfprintf(shp->strbuf,"%.*g",12,*((double*)val));
val = sfstruse(shp->strbuf);
}
if(val)
val = sh_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;
}
/*
* 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 = (Namval_t*)(&(mp->node[0]));
register int i,n,x;
unsigned int savesub = shp->subshell;
Namarr_t *ap = nv_arrayptr(SH_MATCHNOD);
Namarr_t *ap_save = ap;
shp->subshell = 0;
#if !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_save->nelem = mp->nmatch = nmatch;
mp->v = v;
mp->first = match[0];
}
#if 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);
}
ap_save->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*)sh_realloc(mp->match,i+vsize+1);
else
mp->match = (int*)sh_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_save->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;
}
}
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*)sh_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 };
#if SHOPT_VSH || SHOPT_ESH
static const Namdisc_t EDITOR_disc = { sizeof(Namfun_t), put_ed };
#endif
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*)sh_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 = sh_getinterp();
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 = sh_getinterp();
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=sh_getinterp();
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 };
#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*)sh_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);
}
/*
* 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 = 0;
char *save_envmarker;
static char *login_files[2];
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 = sh_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.id = path_basename(argv[0]);
}
else
shp = sh_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);
b___regress__(2, regress, &shp->bltindata);
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*),nomemory);
/* 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);
sh_onoption(SH_LETOCTAL);
}
}
/* read the environment; don't import attributes yet, but save pointer to them */
save_envmarker = env_init(shp);
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 = sh_strdup(buff);
else if((cp && (sh_type(cp)&SH_TYPE_SH)) || (argc>0 && strchr(cp= *argv,'/')))
{
if(*cp=='/')
shp->gd->shpath = sh_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 = sh_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)
{
int dolv_index;
/* 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;
dolv_index = (argc - 1) - shp->st.dolc;
shp->st.dolv = argv + dolv_index;
shp->st.repl_index = dolv_index;
shp->st.repl_arg = argv[dolv_index];
shp->st.dolv[0] = argv[0];
if(shp->st.dolc < 1)
{
sh_onoption(SH_SFLAG);
off_option(&shp->offoptions,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*)sh_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_import_attributes(shp,save_envmarker);
#if SHOPT_PFSH
if (sh_isoption(SH_PFSH))
{
struct passwd *pw = getpwuid(shp->gd->userid);
if(pw)
shp->gd->user = sh_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);
UNREACHABLE();
}
#endif /*SHELLMAGIC*/
}
else
sh_offoption(SH_PRIVILEGED);
/* shname for $0 in profiles and . scripts */
if(sh_isdevfd(argv[1]))
shp->shname = sh_strdup(argv[0]);
else
shp->shname = sh_strdup(shp->st.dolv[0]);
/*
* return here for shell script execution
* but not for parenthesis subshells
*/
error_info.id = sh_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;
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(shp->userinit=userinit)
(*userinit)(shp, 0);
shp->exittrap = 0;
shp->errtrap = 0;
shp->end_fn = 0;
error_info.exit = sh_exit;
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 SHOPT_ESH
if(sh_isoption(SH_EMACS))
on_option(&opt,SH_EMACS);
if(sh_isoption(SH_GMACS))
on_option(&opt,SH_GMACS);
#endif
#if SHOPT_VSH
if(sh_isoption(SH_VI))
on_option(&opt,SH_VI);
if(sh_isoption(SH_VIRAW))
on_option(&opt,SH_VIRAW);
#endif
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 = sh_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 = sh_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;
shp->exittrap = 0;
shp->errtrap = 0;
shp->end_fn = 0;
/* update ${.sh.pid}, $$, $PPID */
shgd->current_pid = shgd->pid = getpid();
shgd->ppid = getppid();
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==LCTIMENOD || np==LANGNOD)
return(np->nvfun);
#endif
return(0);
}
static const char *shdiscnames[] = { "tilde", 0};
#if 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||strncmp(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));
UNREACHABLE();
}
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 = sh_newof(0,struct Stats,1,nstat*NV_MINSZ);
Namval_t *np;
sp->numnodes = nstat;
sp->nodes = (char*)(sp+1);
shgd->stats = (int*)sh_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 = sh_newof(0,Init_t,1,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;
#if SHOPT_VSH || SHOPT_ESH
ip->VISUAL_init.disc = &EDITOR_disc;
ip->VISUAL_init.nofree = 1;
ip->EDITOR_init.disc = &EDITOR_disc;
ip->EDITOR_init.nofree = 1;
#endif
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->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_TIME_init.disc = &LC_disc;
ip->LC_TIME_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);
#if SHOPT_VSH || SHOPT_ESH
nv_stack(VISINOD, &ip->VISUAL_init);
nv_stack(EDITNOD, &ip->EDITOR_init);
#endif
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);
nv_putval(RANDNOD, (char*)&d, NV_DOUBLE);
sh_reseed_rand((struct rand *)RANDNOD->nvfun);
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(LCTIMENOD, &ip->LC_TIME_init);
nv_stack(LANGNOD, &ip->LANG_init);
#endif /* _hdr_locale */
(PPIDNOD)->nvalue.pidp = (&shp->gd->ppid);
(SH_PIDNOD)->nvalue.pidp = (&shp->gd->current_pid);
(SH_SUBSHELLNOD)->nvalue.ip = (&shp->gd->realsubshell);
(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);
dtuserdata(shp->alias_tree,shp,1);
shp->track_tree = dtopen(&_Nvdisc,Dtset);
dtuserdata(shp->track_tree,shp,1);
shp->bltin_tree = sh_inittree(shp,(const struct shtable2*)shtab_builtins);
dtuserdata(shp->bltin_tree,shp,1);
shp->fun_base = shp->fun_tree = dtopen(&_Nvdisc,Dtoset);
dtuserdata(shp->fun_tree,shp,1);
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 = sh_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 = 0;
for(tp=name_vals;*tp->sh_name;tp++)
n++;
np = (Namval_t*)sh_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);
dtuserdata(treep,shp,1);
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
*
* Returns pointer to A__z env var from which to import attributes, or 0.
*/
static char *env_init(Shell_t *shp)
{
register char *cp;
register Namval_t *np;
register char **ep=environ;
char *next = 0; /* pointer to A__z env var */
if(ep)
{
while(cp = *ep++)
{
/* The magic A__z env var is an invention of ksh88. See e_envmarker[]. */
if(*cp=='A' && cp[1]=='_' && cp[2]=='_' && cp[3]=='z' && cp[4]=='=')
next = cp + 4;
else if(strncmp(cp,"KSH_VERSION=",12)==0)
continue;
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);
}
else /* swap with front */
{
ep[-1] = environ[shp->nenv];
environ[shp->nenv++] = cp;
}
}
}
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(next);
}
/*
* Import variable attributes from magic A__z env var pointed to by 'next'.
* If next == 0, this function does nothing.
*/
static void env_import_attributes(Shell_t *shp, char *next)
{
register char *cp;
register Namval_t *np;
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 *dp, *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--;
}
}
flag &= ~NV_RDONLY; /* refuse to import readonly attribute */
if(!flag)
continue;
nv_newattr(np,flag|NV_IMPORT|NV_EXPORT,size);
}
}
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 = sh_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);
}