1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
cde/cde/programs/dtksh/init.c
2020-09-20 11:29:00 -06:00

2711 lines
69 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"
#ifdef BUILD_DTKSH
#include <Dt/DtNlUtils.h>
#include <Dt/EnvControlP.h>
#include <stdio.h>
#include <nl_types.h>
#include <X11/X.h>
#include <X11/Intrinsic.h>
#include <X11/IntrinsicP.h>
#include <X11/CoreP.h>
#include <X11/StringDefs.h>
#include <Xm/XmStrDefs.h>
#include <setjmp.h>
#include <string.h>
#include <ctype.h>
#include <Xm/Xm.h>
#include <Xm/Protocols.h>
#include "hash.h"
#include "stdio.h"
#define NO_AST
#include "dtksh.h"
#undef NO_AST
#include "xmksh.h"
#include "dtkcmds.h"
#include "xmcvt.h"
#include "widget.h"
#include "extra.h"
#include "xmwidgets.h"
#include "msgs.h"
#include <locale.h>
#endif
#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 Dt_t *inittree(Shell_t*,const struct shtable2*);
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
#ifdef BUILD_DTKSH
/*
* This version of putenv uses the hash storage to assign environment values
*
* The original ksh93 code had this routine called "putenv". This hid
* the libc version of putenv, which caused problems for dtksh on systems
* with shared libraries, as it caused the existence of two separate and
* non-overlapping environments. To the best of my knowledge there are
* no calls to this routine. - harry phinney 8/15/1994.
*/
int ksh_putenv(const char *name)
{
Namval_t *np;
if(name)
{
np = nv_open(name,sh.var_tree,NV_EXPORT|NV_IDENT|NV_ARRAY|NV_ASSIGN);
if(!strchr(name,'='))
nv_unset(np);
}
return(0);
}
void
SyncEnv(
char *name)
{
char *value, *buf;
value = getenv(name);
if(value != (char *)NULL)
{
buf = malloc(strlen(name) + strlen(value) + 2);
strcpy(buf, name);
strcat(buf, "=");
strcat(buf, value);
ksh_putenv(buf);
free(buf); /* I hope it's legal to free this! */
}
}
/*
* The following struct is based on an alphanumerically sorted list of
* all ksh-generated messages which might be displayed, and
* must be looked up in a message catalog. The point of this is to
* translate from the AT&T/UI style of string-based message indexing
* to the X/OPEN style of message number lookup. The following struct
* allows us to do a binary search on the strings to find the associated
* set number and message ID. While we could have used the string index
* as the message number, that would cause headaches if/when we
* need to add more messages, as all following messages would have to
* be renumbered.
*/
typedef struct {
char *string;
int setNum;
int msgNum;
} MsgStr, *MsgPtr;
static MsgStr allmsgs[] = {
{" Done", 25, 1},
{" Running", 25, 3},
{" [-n] [arg...]", 25, 4},
{" [arg...]", 25, 5},
{" [dir] [list]", 25, 6},
{" [job...]", 25, 7},
{" [n]", 25, 8},
{" [name [pathname] ]", 25, 9},
{" [name]", 25, 10},
{" [top] [base]", 25, 11},
{" expr...", 25, 12},
{" format [arg...]", 25, 13},
{" is a function", 25, 14},
{" is a keyword", 25, 15},
{" is a shell builtin", 25, 16},
{" is an exported function", 25, 17},
{" is an undefined function", 25, 18},
{" name [arg...]", 25, 19},
{":a:[name] optstring name [args...]", 25, 20},
{" seconds", 25, 21},
{"${HOME:-.}/.profile", 25, 23},
{"%c: invalid character in expression - %s", 25, 24},
{"%c: unknown format specifier", 25, 25},
{"%d-%d: invalid range", 25, 26},
{"%d: invalid binary script version", 25, 27},
{"%s is an alias for ", 25, 28},
{"%s is an exported alias for ", 25, 29},
{"%s missing", 25, 30},
{"%s unknown base", 25, 31},
{"%s: ':' expected for '?' operator", 25, 32},
{"%s: Ambiguous", 25, 33},
{"%s: Arguments must be %job or process ids", 25, 34},
{"%s: alias not found\n", 25, 35},
{"%s: arithmetic syntax error", 25, 36},
{"%s: assignment requires lvalue", 25, 37},
{"%s: bad file unit number", 25, 38},
{"%s: bad format", 25, 39},
{"%s: bad number", 25, 40},
{"%s: bad option(s)", 25, 41},
{"%s: bad substitution", 25, 42},
{"%s: bad trap", 25, 43},
{"%s: cannot create", 25, 44},
{"%s: cannot execute", 25, 45},
{"%s: cannot open", 25, 46},
{"%s: divide by zero", 25, 47},
{"%s: domain exception", 25, 48},
{"%s: fails %s", 25, 49},
{"%s: file already exists", 25, 50},
{"%s: illegal function name", 25, 51},
{"%s: invalid alias name", 25, 52},
{"%s: invalid discipline function", 25, 53},
{"%s: invalid export name", 25, 54},
{"%s: invalid function name", 25, 55},
{"%s: invalid name", 25, 56},
{"%s: invalid regular expression", 25, 57},
{"%s: invalid self reference", 25, 58},
{"%s: invalid use of :", 25, 59},
{"%s: invalid variable name", 25, 60},
{"%s: is not an identifier", 25, 61},
{"%s: is read only", 25, 62},
{"%s: label not implemented", 25, 63},
{"%s: limit exceeded", 25, 64},
{"%s: more tokens expected", 25, 65},
{"%s: no parent", 25, 66},
{"%s: no reference name", 25, 67},
{"%s: not found", 25, 68},
{"%s: not implemented", 25, 69},
{"%s: operands have incompatible types", 25, 70},
{"%s: overflow exception", 25, 71},
{"%s: parameter not set", 25, 72},
{"%s: parameter null or not set", 25, 73},
{"%s: recursion too deep", 25, 74},
{"%s: reference variable cannot be an array", 25, 75},
{"%s: requires pathname argument", 25, 76},
{"%s: restricted", 25, 77},
{"%s: singularity exception", 25, 78},
{"%s: subscript out of range", 25, 79},
{"%s: unbalanced parenthesis", 25, 80},
{"%s: unknown function", 25, 81},
{"%s: unknown locale", 25, 82},
{"%s: unknown operator", 25, 83},
{"%s: unknown signal name", 25, 84},
{"%s: would cause loop", 25, 85},
{"(coredump)", 25, 86},
{"-c requires argument", 25, 87},
{"-e - requires single argument", 25, 88},
{"/vpix", 25, 89},
{"<command unknown>", 25, 90},
{"AC:E#?F#?H:[name]L#?R#?Z#?fi#?[base]lnprtux [name=[value]...]", 25, 91},
{"AE#?F#?HL#?R#?Z#?fi#?[base]lnprtux [name=[value]...]", 25, 92},
{"Abort", 25, 93},
{"Ad:[delim]prst#[timeout]u#[filenum] [name...]", 25, 94},
{"Alarm call", 25, 95},
{"Bad root node specification", 25, 96},
{"Bad system call", 25, 97},
{"Broken Pipe", 25, 98},
{"Bus error", 25, 99},
{"Cannot start job control", 25, 100},
{"Current option settings", 25, 101},
{"DIL signal", 25, 102},
{"Death of Child", 25, 103},
{"DircabefhkmnpstuvxCR:[file]o:?[option] [arg...]", 25, 104},
{"DircabefhkmnpstuvxCo:?[option] [arg...]", 25, 105},
{"EMT trap", 25, 106},
{"Exceeded CPU time limit", 25, 107},
{"Exceeded file size limit", 25, 108},
{"Floating exception", 25, 109},
{"HSacdfmnstv [limit]", 25, 110},
{"Hangup", 25, 111},
{"IO signal", 25, 112},
{"Illegal instruction", 25, 113},
{"Interrupt", 25, 114},
{"Killed", 25, 115},
{"LP [dir] [change]", 25, 116},
{"Memory fault", 25, 117},
{"Migrate process", 25, 118},
{"No job control", 25, 119},
{"Phone interrupt", 25, 120},
{"Polling alarm", 25, 121},
{"Power fail", 25, 122},
{"Profiling time alarm", 25, 123},
{"Quit", 25, 124},
{"Resources lost", 25, 125},
{"Reverting to old tty driver...", 25, 126},
{"S [mask]", 25, 127},
{"Security label changed", 25, 129},
{"Socket interrupt", 25, 130},
{"Sound completed", 25, 131},
{"Stopped (signal)", 25, 132},
{"Stopped (tty input)", 25, 133},
{"Stopped process continued", 25, 134},
{"Stopped", 25, 135},
{"Stopped(tty output)", 25, 136},
{"Switching to new tty driver...", 25, 137},
{"System crash soon", 25, 138},
{"Terminated", 25, 139},
{"Trace/BPT trap", 25, 140},
{"Unrecognized version", 25, 141},
{"Use 'exit' to terminate this shell", 25, 142},
{"User signal 1", 25, 143},
{"User signal 2", 25, 144},
{"Version not defined", 25, 145},
{"Virtual time alarm", 25, 146},
{"Window size change", 25, 147},
{"You have running jobs", 25, 148},
{"You have stopped jobs", 25, 149},
{"[_[:alpha:]]*([_[:alnum:]])", 25, 150},
{"\n@(#)Version 12/28/93\0\n", 25, 151},
{"\n@(#)Version M-12/28/93\0\n", 25, 152},
{"\nreal", 25, 153},
{"\r\n\007shell will timeout in 60 seconds due to inactivity", 25, 154},
{"a name...", 25, 155},
{"a:c [command [args...] ]", 25, 156},
{"afpv name...", 25, 157},
{"alarm %s %.3f\n", 25, 158},
{"alarm -r %s +%.3g\n", 25, 159},
{"argument expected", 25, 160},
{"bad directory", 25, 161},
{"bad file unit number", 25, 162},
{"bad substitution", 25, 163},
{"cannot access parent directories", 25, 164},
{"cannot create pipe", 25, 165},
{"cannot create tempory file", 25, 166},
{"cannot fork", 25, 167},
{"cannot get %s", 25, 168},
{"cannot set %s", 25, 169},
{"cannot set alarm", 25, 170},
{"condition(s) required", 25, 171},
{"dsf:[library] [name...]", 25, 172},
{"e:[editor]lnrsN# [first] [last]", 25, 173},
{"end of file", 25, 174},
{"f:[format]enprsu:[filenum] [arg...]", 25, 175},
{"fnv name...", 25, 176},
{"hist -e \"${VISUAL:-${EDITOR:-vi}}\" ", 25, 177},
{"history file cannot open", 25, 178},
{"incorrect syntax", 25, 179},
{"invalid argument of type %c", 25, 180},
{"is a shell builtin version of", 25, 181},
{"is a tracked alias for", 25, 182},
{"kill", 25, 183},
{"line %d: $ not preceded by \\", 25, 184},
{"line %d: %c within ${} should be quoted", 25, 185},
{"line %d: %s unknown label", 25, 186},
{"line %d: %s within [[...]] obsolete, use ((...))", 25, 187},
{"line %d: '=' obsolete, use '=='", 25, 188},
{"line %d: -a obsolete, use -e", 25, 189},
{"line %d: \\ in front of %c reserved for future use", 25, 190},
{"line %d: `...` obsolete, use $(...)", 25, 191},
{"line %d: escape %c to avoid ambiguities", 25, 192},
{"line %d: label %s ignored", 25, 193},
{"line %d: quote %c to avoid ambiguities", 25, 194},
{"line %d: set %s obsolete", 25, 195},
{"line %d: spaces required for nested subshell", 25, 196},
{"line %d: use braces to avoid ambiguities with $id[...]", 25, 197},
{"line %d: use space or tab to separate operators %c and %c", 25, 198},
{"ln#[signum]s:[signame] sig...", 25, 199},
{"login setuid/setgid shells prohibited", 25, 200},
{"mapping", 25, 201},
{"newline", 25, 202},
{"nlp [job...]", 25, 203},
{"no history file", 25, 204},
{"no query process", 25, 205},
{"no such job", 25, 206},
{"no such process", 25, 207},
{"not supported", 25, 208},
{"off", 25, 209},
{"on", 25, 210},
{"open file limit exceeded", 25, 211},
{"out of memory", 25, 212},
{"p [action condition...]", 25, 213},
{"p [name[=value]...]", 25, 214},
{"parameter not set", 25, 215},
{"permission denied", 25, 216},
{"process already exists", 25, 217},
{"ptx [name=[value]...]", 25, 218},
{"pvV name [arg]...", 25, 219},
{"r [varname seconds]", 25, 220},
{"syntax error at line %d: `%s' %s", 25, 221},
{"syntax error at line %d: duplicate label %s", 25, 222},
{"syntax error: `%s' %s", 25, 223},
{"sys", 25, 224},
{"timed out waiting for input", 25, 225},
{"unexpected", 25, 226},
{"universe not accessible", 25, 227},
{"unlimited", 25, 228},
{"unmatched", 25, 229},
{"user", 25, 230},
{"versions", 25, 231},
{"write to %d failed", 25, 232},
{"you have mail in $_", 25, 233},
{"zero byte", 25, 234},
};
#define _CLIENT_CAT_NAME "dtksh"
/*
* Without this proto, standard C says that _DtGetMessage() returns
* an int, even though it really returns a pointer. The compiler is
* then free to use the high 32-bits of the return for
* something else (like scratch), and that can garble the pointer.
*/
char *_DtGetMessage(char *filename, int set, int n, char *s);
#define GETMESSAGE(set, number, string)\
(_DtGetMessage(_CLIENT_CAT_NAME, set, number, string))
static int localeChanged = 1;
void LocaleChanged(Namval_t *np, const char *val, int flags, Namfun_t *fp)
{
localeChanged = 1;
nv_putv(np, val, flags, fp);
setlocale(LC_ALL, "");
}
static Namdisc_t localeDisc = { 0, LocaleChanged, NULL, NULL, NULL, NULL, NULL, NULL };
static Namfun_t localeFun = {NULL, NULL };
/****************************************************************************
*
* The following two functions are ugly, but necessary. Ksh reserves file
* descriptors 0 - 9 for use by shell scripts, and has intimate knowledge
* of how and when they were opened. Unfortunately, certain dtksh functions
* (XtInitialize, catopen, ttdt_open, _DtActionInvoke, others) open file
* descriptors which are not known to ksh. We can't let these file
* descriptors fall in the 0 - 9 range, because we can't afford to have
* the shell script overriding our file descriptors. Therefore, any of
* our commands which open files must first lock our file descriptors 0 - 9,
* thus forcing the command to get a file descriptor out of the shell's
* range. After the command has opened its file descriptor, it then needs
* to unlock file descriptors 0 - 9, so that the shell script will have
* access to them again.
*
**************************************************************************/
/*
* Return a list of the file descriptors we had to open, to lock out file
* descriptors 0 - 9; this list should be freed (and the file descriptors
* closed) by calling UnlockkshFileDescriptors().
*/
int *LockKshFileDescriptors(void)
{
int * fdList;
int i;
int fd, newfd;
fdList = (int *)malloc(sizeof(int) * 10);
for (i = 0; i < 10; i++)
fdList[i] = -1;
if ((fd = open("/dev/null", O_RDONLY)) >= 0)
{
if (fd < 10)
{
fdList[0] = fd;
for (i = 1; i < 10; i++)
{
if ((newfd = dup(fd)) < 10)
fdList[i] = newfd;
else
{
close(newfd);
break;
}
}
}
else
close(fd);
}
return(fdList);
}
void UnlockKshFileDescriptors(int *fdList)
{
int i;
for (i = 0; i < 10; i++)
{
if (fdList[i] != (-1))
close(fdList[i]);
}
free((char *)fdList);
}
/*
* This function overrides the traditional libDtSvc version of this function.
* Since ksh dynamically changes to match the user's locale, we potentially
* need to change our message catalog in the middle of running.
*/
char *savedNlsPath;
char *_DtGetMessage(char *filename, int set, int n, char *s)
{
nl_catd catopen();
char *catgets();
int catclose();
static nl_catd nlmsg_fd = (nl_catd)-1;
static char *lang = NULL;
char *msg;
char * newLang;
int * lockedFds;
int swappedNlsPath = 0;
char *pEqual;
static char pathBuf[1024] = {(char)'N', (char)'L', (char)'S', (char)'P',
(char)'A', (char)'T', (char)'H', (char)'='};
if ( localeChanged )
{
char *oldPath;
localeChanged = 0;
newLang = (char *) getenv ("LANG");
if (lang)
free(lang);
if (newLang)
lang = strdup(newLang);
else
lang = NULL;
if (nlmsg_fd != (nl_catd)-1)
catclose(nlmsg_fd);
if(strcmp((oldPath = getenv("NLSPATH")), savedNlsPath) != 0)
{
swappedNlsPath = 1;
pEqual = strchr(pathBuf, '=');
strcpy(pEqual + 1, savedNlsPath);
/*
* Only call putenv if pathBuf isn't already holding NLSPATH
* in the environment.
*/
if(oldPath != (pEqual + 1))
putenv(pathBuf);
}
lockedFds = LockKshFileDescriptors();
nlmsg_fd = catopen(filename, 0);
UnlockKshFileDescriptors(lockedFds);
if(swappedNlsPath != 0)
{
swappedNlsPath = 0;
pEqual = strchr(pathBuf, '=');
strcpy(pEqual + 1, oldPath);
if(oldPath != (pEqual + 1))
putenv(pathBuf);
}
}
msg=catgets(nlmsg_fd,set,n,s);
return (msg);
}
/*
* This function needs to be modified to handle international
* error message translations
*/
static char *msg_translate(const char *message,int type)
{
int startIndex = 0, endIndex = sizeof(allmsgs)/sizeof(MsgStr) - 1,
midIndex, res, weFoundIt = 0;
if(type != 1) /* if it's not a shell message, don't translate */
return((char*)message);
while(startIndex != endIndex)
{
midIndex = (startIndex + endIndex) / 2;
if(midIndex == startIndex)
{
if((res = strcmp(allmsgs[startIndex].string, message)) == 0)
{
weFoundIt = 1;
midIndex = startIndex;
break;
}
else if(res < 0)
{
if((res = strcmp(allmsgs[endIndex].string, message)) == 0)
{
weFoundIt = 1;
midIndex = endIndex;
break;
}
}
/* we didn't find a match in the table */
weFoundIt = 0;
break;
}
else
{
if((res = strcmp(allmsgs[midIndex].string, message)) == 0)
{
weFoundIt = 1;
break; /* we found it */
}
else if(res < 0)
startIndex = midIndex;
else
endIndex = midIndex;
}
}
if(weFoundIt)
return GETMESSAGE(allmsgs[midIndex].setNum, allmsgs[midIndex].msgNum, (char *)message);
return((char*)message);
}
#else
static char* msg_translate(const char* catalog, const char* message)
{
NOT_USED(catalog);
return((char*)message);
}
#endif
/* 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];
}
}
#ifdef BUILD_DTKSH
if(type==LC_ALL || type==LC_MESSAGES)
error_info.translate = msg_translate;
#endif
}
#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);
#ifdef BUILD_DTKSH
int * lockedFds;
lockedFds = LockKshFileDescriptors();
(void) XtSetLanguageProc((XtAppContext)NULL, (XtLanguageProc)NULL,
(XtPointer)NULL);
setlocale(LC_ALL, "");
DtNlInitialize();
_DtEnvControl(DT_ENV_SET);
localeFun.disc = &localeDisc;
nv_stack(LANGNOD, &localeFun);
UnlockKshFileDescriptors(lockedFds);
/*
* Save the current setting of NLSPATH. The user/script may want to
* set its own NLSPATH to access its message catalog, so we need to
* remember where to find our own catalog(s). This saved path is used
* in ksh93/src/cmd/ksh93/sh/init.c: _DtGetMessage(). We don't mess
* with the user/script's setting of LANG as we want to track changes
* in LANG.
*/
savedNlsPath = strdup(getenv("NLSPATH"));
/*
* Sync the libc environment (set up by DtEnvControl) with our internal
* hash table environment.
*/
SyncEnv("NLSPATH");
SyncEnv("LANG");
#endif
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 = 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 = 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
*/
static Dt_t *inittree(Shell_t *shp,const struct shtable2 *name_vals)
{
register Namval_t *np;
register const struct shtable2 *tp;
register unsigned n = 0;
register Dt_t *treep;
Dt_t *base_treep, *dict;
for(tp=name_vals;*tp->sh_name;tp++)
n++;
np = (Namval_t*)calloc(n,sizeof(Namval_t));
if(!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);
}