1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1982-2010 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21/*
22 *
23 * Shell initialization
24 *
25 *   David Korn
26 *   AT&T Labs
27 *
28 */
29
30#include        "defs.h"
31#include        <stak.h>
32#include        <ccode.h>
33#include        <pwd.h>
34#include        <tmx.h>
35#include        "variables.h"
36#include        "path.h"
37#include        "fault.h"
38#include        "name.h"
39#include	"edit.h"
40#include	"jobs.h"
41#include	"io.h"
42#include	"shlex.h"
43#include	"builtins.h"
44#include	"FEATURE/time"
45#include	"FEATURE/dynamic"
46#include	"FEATURE/externs"
47#include	"lexstates.h"
48#include	"version.h"
49
50char e_version[]	= "\n@(#)$Id: Version "
51#if SHOPT_AUDIT
52#define ATTRS		1
53			"A"
54#endif
55#if SHOPT_BASH
56#define ATTRS		1
57			"B"
58#endif
59#if SHOPT_BGX
60#define ATTRS		1
61			"J"
62#endif
63#if SHOPT_ACCT
64#define ATTRS		1
65			"L"
66#endif
67#if SHOPT_MULTIBYTE
68#define ATTRS		1
69			"M"
70#endif
71#if SHOPT_PFSH && _hdr_exec_attr
72#define ATTRS		1
73			"P"
74#endif
75#if SHOPT_REGRESS
76#define ATTRS		1
77			"R"
78#endif
79#if ATTRS
80			" "
81#endif
82			SH_RELEASE " $\0\n";
83
84#if SHOPT_BASH
85    extern void bash_init(Shell_t*,int);
86#endif
87
88#define RANDMASK	0x7fff
89
90#ifndef ARG_MAX
91#   define ARG_MAX	(1*1024*1024)
92#endif
93#ifndef CHILD_MAX
94#   define CHILD_MAX	(1*1024)
95#endif
96#ifndef CLK_TCK
97#   define CLK_TCK	60
98#endif /* CLK_TCK */
99
100#ifndef environ
101    extern char	**environ;
102#endif
103
104#undef	getconf
105#define getconf(x)	strtol(astconf(x,NiL,NiL),NiL,0)
106
107struct seconds
108{
109	Namfun_t	hdr;
110	Shell_t		*sh;
111};
112
113struct rand
114{
115	Namfun_t	hdr;
116	int32_t		rand_last;
117};
118
119struct ifs
120{
121	Namfun_t	hdr;
122	Namval_t	*ifsnp;
123};
124
125struct match
126{
127	Namfun_t	hdr;
128	char		*val;
129	char		*rval;
130	int		vsize;
131	int		nmatch;
132	int		lastsub;
133	int		match[2*(MATCH_MAX+1)];
134};
135
136typedef struct _init_
137{
138	Shell_t		*sh;
139#if SHOPT_FS_3D
140	Namfun_t	VPATH_init;
141#endif /* SHOPT_FS_3D */
142	struct ifs	IFS_init;
143	Namfun_t	PATH_init;
144	Namfun_t	FPATH_init;
145	Namfun_t	CDPATH_init;
146	Namfun_t	SHELL_init;
147	Namfun_t	ENV_init;
148	Namfun_t	VISUAL_init;
149	Namfun_t	EDITOR_init;
150	Namfun_t	HISTFILE_init;
151	Namfun_t	HISTSIZE_init;
152	Namfun_t	OPTINDEX_init;
153	struct seconds	SECONDS_init;
154	struct rand	RAND_init;
155	Namfun_t	LINENO_init;
156	Namfun_t	L_ARG_init;
157	Namfun_t	SH_VERSION_init;
158	struct match	SH_MATCH_init;
159#ifdef _hdr_locale
160	Namfun_t	LC_TYPE_init;
161	Namfun_t	LC_NUM_init;
162	Namfun_t	LC_COLL_init;
163	Namfun_t	LC_MSG_init;
164	Namfun_t	LC_ALL_init;
165	Namfun_t	LANG_init;
166#endif /* _hdr_locale */
167} Init_t;
168
169static int		nbltins;
170static void		env_init(Shell_t*);
171static Init_t		*nv_init(Shell_t*);
172static Dt_t		*inittree(Shell_t*,const struct shtable2*);
173static int		shlvl;
174
175#ifdef _WINIX
176#   define EXE	"?(.exe)"
177#else
178#   define EXE
179#endif
180
181static int		rand_shift;
182
183
184/*
185 * Invalidate all path name bindings
186 */
187static void rehash(register Namval_t *np,void *data)
188{
189	NOT_USED(data);
190	nv_onattr(np,NV_NOALIAS);
191}
192
193/*
194 * out of memory routine for stak routines
195 */
196static char *nospace(int unused)
197{
198	NOT_USED(unused);
199	errormsg(SH_DICT,ERROR_exit(3),e_nospace);
200	return(NIL(char*));
201}
202
203/* Trap for VISUAL and EDITOR variables */
204static void put_ed(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
205{
206	register const char *cp, *name=nv_name(np);
207	register int	newopt=0;
208	Shell_t *shp = nv_shell(np);
209	if(*name=='E' && nv_getval(sh_scoped(shp,VISINOD)))
210		goto done;
211	if(!(cp=val) && (*name=='E' || !(cp=nv_getval(sh_scoped(shp,EDITNOD)))))
212		goto done;
213	/* turn on vi or emacs option if editor name is either*/
214	cp = path_basename(cp);
215	if(strmatch(cp,"*[Vv][Ii]*"))
216		newopt=SH_VI;
217	else if(strmatch(cp,"*gmacs*"))
218		newopt=SH_GMACS;
219	else if(strmatch(cp,"*macs*"))
220		newopt=SH_EMACS;
221	if(newopt)
222	{
223		sh_offoption(SH_VI);
224		sh_offoption(SH_EMACS);
225		sh_offoption(SH_GMACS);
226		sh_onoption(newopt);
227	}
228done:
229	nv_putv(np, val, flags, fp);
230}
231
232/* Trap for HISTFILE and HISTSIZE variables */
233static void put_history(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
234{
235	Shell_t *shp = nv_shell(np);
236	void 	*histopen = shp->hist_ptr;
237	char	*cp;
238	if(val && histopen)
239	{
240		if(np==HISTFILE && (cp=nv_getval(np)) && strcmp(val,cp)==0)
241			return;
242		if(np==HISTSIZE && sh_arith(val)==nv_getnum(HISTSIZE))
243			return;
244		hist_close(shp->hist_ptr);
245	}
246	nv_putv(np, val, flags, fp);
247	if(histopen)
248	{
249		if(val)
250			sh_histinit(shp);
251		else
252			hist_close(histopen);
253	}
254}
255
256/* Trap for OPTINDEX */
257static void put_optindex(Namval_t* np,const char *val,int flags,Namfun_t *fp)
258{
259	Shell_t *shp = nv_shell(np);
260	shp->st.opterror = shp->st.optchar = 0;
261	nv_putv(np, val, flags, fp);
262	if(!val)
263		nv_disc(np,fp,NV_POP);
264}
265
266static Sfdouble_t nget_optindex(register Namval_t* np, Namfun_t *fp)
267{
268	return((Sfdouble_t)*np->nvalue.lp);
269}
270
271static Namfun_t *clone_optindex(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
272{
273	Namfun_t *dp = (Namfun_t*)malloc(sizeof(Namfun_t));
274	memcpy((void*)dp,(void*)fp,sizeof(Namfun_t));
275	mp->nvalue.lp = np->nvalue.lp;
276	dp->nofree = 0;
277	return(dp);
278}
279
280
281/* Trap for restricted variables FPATH, PATH, SHELL, ENV */
282static void put_restricted(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
283{
284	Shell_t *shp = nv_shell(np);
285	int	path_scoped = 0;
286	Pathcomp_t *pp;
287	char *name = nv_name(np);
288	if(!(flags&NV_RDONLY) && sh_isoption(SH_RESTRICTED))
289		errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np));
290	if(np==PATHNOD	|| (path_scoped=(strcmp(name,PATHNOD->nvname)==0)))
291	{
292		nv_scan(shp->track_tree,rehash,(void*)0,NV_TAGGED,NV_TAGGED);
293		if(path_scoped && !val)
294			val = PATHNOD->nvalue.cp;
295	}
296	if(val && !(flags&NV_RDONLY) && np->nvalue.cp && strcmp(val,np->nvalue.cp)==0)
297		 return;
298	if(np==FPATHNOD)
299		shp->pathlist = (void*)path_unsetfpath((Pathcomp_t*)shp->pathlist);
300	nv_putv(np, val, flags, fp);
301	shp->universe = 0;
302	if(shp->pathlist)
303	{
304		val = np->nvalue.cp;
305		if(np==PATHNOD || path_scoped)
306			pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_PATH);
307		else if(val && np==FPATHNOD)
308			pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_FPATH);
309		else
310			return;
311		if(shp->pathlist = (void*)pp)
312			pp->shp = shp;
313		if(!val && (flags&NV_NOSCOPE))
314		{
315			Namval_t *mp = dtsearch(shp->var_tree,np);
316			if(mp && (val=nv_getval(mp)))
317				nv_putval(mp,val,NV_RDONLY);
318		}
319#if 0
320sfprintf(sfstderr,"%d: name=%s val=%s\n",getpid(),name,val);
321path_dump((Pathcomp_t*)shp->pathlist);
322#endif
323	}
324}
325
326static void put_cdpath(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
327{
328	Pathcomp_t *pp;
329	Shell_t *shp = nv_shell(np);
330	nv_putv(np, val, flags, fp);
331	if(!shp->cdpathlist)
332		return;
333	val = np->nvalue.cp;
334	pp = (void*)path_addpath((Pathcomp_t*)shp->cdpathlist,val,PATH_CDPATH);
335	if(shp->cdpathlist = (void*)pp)
336		pp->shp = shp;
337}
338
339#ifdef _hdr_locale
340    /*
341     * This function needs to be modified to handle international
342     * error message translations
343     */
344#if ERROR_VERSION >= 20000101L
345    static char* msg_translate(const char* catalog, const char* message)
346    {
347	NOT_USED(catalog);
348	return((char*)message);
349    }
350#else
351    static char* msg_translate(const char* message, int type)
352    {
353	NOT_USED(type);
354	return((char*)message);
355    }
356#endif
357
358    /* Trap for LC_ALL, LC_CTYPE, LC_MESSAGES, LC_COLLATE and LANG */
359    static void put_lang(Namval_t* np,const char *val,int flags,Namfun_t *fp)
360    {
361	Shell_t *shp = nv_shell(np);
362	int type;
363	char *cp;
364	char *name = nv_name(np);
365	if(name==(LCALLNOD)->nvname)
366		type = LC_ALL;
367	else if(name==(LCTYPENOD)->nvname)
368		type = LC_CTYPE;
369	else if(name==(LCMSGNOD)->nvname)
370		type = LC_MESSAGES;
371	else if(name==(LCCOLLNOD)->nvname)
372		type = LC_COLLATE;
373	else if(name==(LCNUMNOD)->nvname)
374		type = LC_NUMERIC;
375#ifdef LC_LANG
376	else if(name==(LANGNOD)->nvname)
377		type = LC_LANG;
378#else
379#define LC_LANG		LC_ALL
380	else if(name==(LANGNOD)->nvname && (!(cp=nv_getval(LCALLNOD)) || !*cp))
381		type = LC_LANG;
382#endif
383	else
384		type= -1;
385	if(!sh_isstate(SH_INIT) && (type>=0 || type==LC_ALL || type==LC_LANG))
386	{
387		struct lconv*	lc;
388		char*		r;
389#ifdef AST_LC_setenv
390		ast.locale.set |= AST_LC_setenv;
391#endif
392		r = setlocale(type,val?val:"");
393#ifdef AST_LC_setenv
394		ast.locale.set ^= AST_LC_setenv;
395#endif
396		if(!r && val)
397		{
398			if(!sh_isstate(SH_INIT) || shp->login_sh==0)
399				errormsg(SH_DICT,0,e_badlocale,val);
400			return;
401		}
402		shp->decomma = (lc=localeconv()) && lc->decimal_point && *lc->decimal_point==',';
403	}
404	nv_putv(np, val, flags, fp);
405	if(CC_NATIVE!=CC_ASCII && (type==LC_ALL || type==LC_LANG || type==LC_CTYPE))
406	{
407		if(sh_lexstates[ST_BEGIN]!=sh_lexrstates[ST_BEGIN])
408			free((void*)sh_lexstates[ST_BEGIN]);
409		if(ast.locale.set&(1<<AST_LC_CTYPE))
410		{
411			register int c;
412			char *state[4];
413			sh_lexstates[ST_BEGIN] = state[0] = (char*)malloc(4*(1<<CHAR_BIT));
414			memcpy(state[0],sh_lexrstates[ST_BEGIN],(1<<CHAR_BIT));
415			sh_lexstates[ST_NAME] = state[1] = state[0] + (1<<CHAR_BIT);
416			memcpy(state[1],sh_lexrstates[ST_NAME],(1<<CHAR_BIT));
417			sh_lexstates[ST_DOL] = state[2] = state[1] + (1<<CHAR_BIT);
418			memcpy(state[2],sh_lexrstates[ST_DOL],(1<<CHAR_BIT));
419			sh_lexstates[ST_BRACE] = state[3] = state[2] + (1<<CHAR_BIT);
420			memcpy(state[3],sh_lexrstates[ST_BRACE],(1<<CHAR_BIT));
421			for(c=0; c<(1<<CHAR_BIT); c++)
422			{
423				if(state[0][c]!=S_REG)
424					continue;
425				if(state[2][c]!=S_ERR)
426					continue;
427				if(isblank(c))
428				{
429					state[0][c]=0;
430					state[1][c]=S_BREAK;
431					state[2][c]=S_BREAK;
432					continue;
433				}
434				if(!isalpha(c))
435					continue;
436				state[0][c]=S_NAME;
437				if(state[1][c]==S_REG)
438					state[1][c]=0;
439				state[2][c]=S_ALP;
440				if(state[3][c]==S_ERR)
441					state[3][c]=0;
442			}
443		}
444		else
445		{
446			sh_lexstates[ST_BEGIN]=(char*)sh_lexrstates[ST_BEGIN];
447			sh_lexstates[ST_NAME]=(char*)sh_lexrstates[ST_NAME];
448			sh_lexstates[ST_DOL]=(char*)sh_lexrstates[ST_DOL];
449			sh_lexstates[ST_BRACE]=(char*)sh_lexrstates[ST_BRACE];
450		}
451	}
452#if ERROR_VERSION < 20000101L
453	if(type==LC_ALL || type==LC_MESSAGES)
454		error_info.translate = msg_translate;
455#endif
456    }
457#endif /* _hdr_locale */
458
459/* Trap for IFS assignment and invalidates state table */
460static void put_ifs(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
461{
462	register struct ifs *ip = (struct ifs*)fp;
463	Shell_t		*shp;
464	ip->ifsnp = 0;
465	if(!val)
466	{
467		fp = nv_stack(np, NIL(Namfun_t*));
468		if(fp && !fp->nofree)
469			free((void*)fp);
470	}
471	if(val != np->nvalue.cp)
472		nv_putv(np, val, flags, fp);
473	if(!val && !(flags&NV_CLONE) && (fp=np->nvfun) && !fp->disc && (shp=(Shell_t*)(fp->last)))
474		nv_stack(np,&((Init_t*)shp->init_context)->IFS_init.hdr);
475}
476
477/*
478 * This is the lookup function for IFS
479 * It keeps the sh.ifstable up to date
480 */
481static char* get_ifs(register Namval_t* np, Namfun_t *fp)
482{
483	register struct ifs *ip = (struct ifs*)fp;
484	register char *cp, *value;
485	register int c,n;
486	register Shell_t *shp = nv_shell(np);
487	value = nv_getv(np,fp);
488	if(np!=ip->ifsnp)
489	{
490		ip->ifsnp = np;
491		memset(shp->ifstable,0,(1<<CHAR_BIT));
492		if(cp=value)
493		{
494#if SHOPT_MULTIBYTE
495			while(n=mbsize(cp),c= *(unsigned char*)cp)
496#else
497			while(c= *(unsigned char*)cp++)
498#endif /* SHOPT_MULTIBYTE */
499			{
500#if SHOPT_MULTIBYTE
501				cp++;
502				if(n>1)
503				{
504					cp += (n-1);
505					shp->ifstable[c] = S_MBYTE;
506					continue;
507				}
508#endif /* SHOPT_MULTIBYTE */
509				n = S_DELIM;
510				if(c== *cp)
511					cp++;
512				else if(c=='\n')
513					n = S_NL;
514				else if(isspace(c))
515					n = S_SPACE;
516				shp->ifstable[c] = n;
517			}
518		}
519		else
520		{
521			shp->ifstable[' '] = shp->ifstable['\t'] = S_SPACE;
522			shp->ifstable['\n'] = S_NL;
523		}
524	}
525	return(value);
526}
527
528/*
529 * these functions are used to get and set the SECONDS variable
530 */
531#ifdef timeofday
532#   define dtime(tp) ((double)((tp)->tv_sec)+1e-6*((double)((tp)->tv_usec)))
533#   define tms	timeval
534#else
535#   define dtime(tp)	(((double)times(tp))/sh.lim.clk_tck)
536#   define timeofday(a)
537#endif
538
539static void put_seconds(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
540{
541	double d;
542	struct tms tp;
543	if(!val)
544	{
545		fp = nv_stack(np, NIL(Namfun_t*));
546		if(fp && !fp->nofree)
547			free((void*)fp);
548		nv_putv(np, val, flags, fp);
549		return;
550	}
551	if(!np->nvalue.dp)
552	{
553		nv_setsize(np,3);
554		nv_onattr(np,NV_DOUBLE);
555		np->nvalue.dp = new_of(double,0);
556	}
557	nv_putv(np, val, flags, fp);
558	d = *np->nvalue.dp;
559	timeofday(&tp);
560	*np->nvalue.dp = dtime(&tp)-d;
561}
562
563static char* get_seconds(register Namval_t* np, Namfun_t *fp)
564{
565	Shell_t *shp = nv_shell(np);
566	register int places = nv_size(np);
567	struct tms tp;
568	double d, offset = (np->nvalue.dp?*np->nvalue.dp:0);
569	NOT_USED(fp);
570	timeofday(&tp);
571	d = dtime(&tp)- offset;
572	sfprintf(shp->strbuf,"%.*f",places,d);
573	return(sfstruse(shp->strbuf));
574}
575
576static Sfdouble_t nget_seconds(register Namval_t* np, Namfun_t *fp)
577{
578	struct tms tp;
579	double offset = (np->nvalue.dp?*np->nvalue.dp:0);
580	NOT_USED(fp);
581	timeofday(&tp);
582	return(dtime(&tp)- offset);
583}
584
585/*
586 * These three functions are used to get and set the RANDOM variable
587 */
588static void put_rand(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
589{
590	struct rand *rp = (struct rand*)fp;
591	register long n;
592	if(!val)
593	{
594		fp = nv_stack(np, NIL(Namfun_t*));
595		if(fp && !fp->nofree)
596			free((void*)fp);
597		nv_unset(np);
598		return;
599	}
600	if(flags&NV_INTEGER)
601		n = *(double*)val;
602	else
603		n = sh_arith(val);
604	srand((int)(n&RANDMASK));
605	rp->rand_last = -1;
606	if(!np->nvalue.lp)
607		np->nvalue.lp = &rp->rand_last;
608}
609
610/*
611 * get random number in range of 0 - 2**15
612 * never pick same number twice in a row
613 */
614static Sfdouble_t nget_rand(register Namval_t* np, Namfun_t *fp)
615{
616	register long cur, last= *np->nvalue.lp;
617	NOT_USED(fp);
618	do
619		cur = (rand()>>rand_shift)&RANDMASK;
620	while(cur==last);
621	*np->nvalue.lp = cur;
622	return((Sfdouble_t)cur);
623}
624
625static char* get_rand(register Namval_t* np, Namfun_t *fp)
626{
627	register long n = nget_rand(np,fp);
628	return(fmtbase(n, 10, 0));
629}
630
631/*
632 * These three routines are for LINENO
633 */
634static Sfdouble_t nget_lineno(Namval_t* np, Namfun_t *fp)
635{
636	double d=1;
637	if(error_info.line >0)
638		d = error_info.line;
639	else if(error_info.context && error_info.context->line>0)
640		d = error_info.context->line;
641	NOT_USED(np);
642	NOT_USED(fp);
643	return(d);
644}
645
646static void put_lineno(Namval_t* np,const char *val,int flags,Namfun_t *fp)
647{
648	register long n;
649	Shell_t *shp = nv_shell(np);
650	if(!val)
651	{
652		fp = nv_stack(np, NIL(Namfun_t*));
653		if(fp && !fp->nofree)
654			free((void*)fp);
655		nv_unset(np);
656		return;
657	}
658	if(flags&NV_INTEGER)
659		n = *(double*)val;
660	else
661		n = sh_arith(val);
662	shp->st.firstline += nget_lineno(np,fp)+1-n;
663}
664
665static char* get_lineno(register Namval_t* np, Namfun_t *fp)
666{
667	register long n = nget_lineno(np,fp);
668	return(fmtbase(n, 10, 0));
669}
670
671static char* get_lastarg(Namval_t* np, Namfun_t *fp)
672{
673	Shell_t	*shp = nv_shell(np);
674	char	*cp;
675	int	pid;
676        if(sh_isstate(SH_INIT) && (cp=shp->lastarg) && *cp=='*' && (pid=strtol(cp+1,&cp,10)) && *cp=='*')
677		nv_putval(np,(pid==getppid()?cp+1:0),0);
678	return(shp->lastarg);
679}
680
681static void put_lastarg(Namval_t* np,const char *val,int flags,Namfun_t *fp)
682{
683	Shell_t *shp = nv_shell(np);
684	if(flags&NV_INTEGER)
685	{
686		sfprintf(shp->strbuf,"%.*g",12,*((double*)val));
687		val = sfstruse(shp->strbuf);
688	}
689	if(val)
690		val = strdup(val);
691	if(shp->lastarg && !nv_isattr(np,NV_NOFREE))
692		free((void*)shp->lastarg);
693	else
694		nv_offattr(np,NV_NOFREE);
695	shp->lastarg = (char*)val;
696	nv_offattr(np,NV_EXPORT);
697	np->nvenv = 0;
698}
699
700static int hasgetdisc(register Namfun_t *fp)
701{
702        while(fp && !fp->disc->getnum && !fp->disc->getval)
703                fp = fp->next;
704	return(fp!=0);
705}
706
707/*
708 * store the most recent value for use in .sh.match
709 */
710void sh_setmatch(const char *v, int vsize, int nmatch, int match[])
711{
712	struct match *mp = (struct match*)(SH_MATCHNOD->nvfun);
713	register int i,n;
714	if(mp->nmatch = nmatch)
715	{
716		memcpy(mp->match,match,nmatch*2*sizeof(match[0]));
717		for(n=match[0],i=1; i < 2*nmatch; i++)
718		{
719			if(mp->match[i] < n)
720				n = mp->match[i];
721		}
722		for(vsize=0,i=0; i < 2*nmatch; i++)
723		{
724			if((mp->match[i] -= n) > vsize)
725				vsize = mp->match[i];
726		}
727		v += n;
728		if(vsize >= mp->vsize)
729		{
730			if(mp->vsize)
731				mp->val = (char*)realloc(mp->val,vsize+1);
732			else
733				mp->val = (char*)malloc(vsize+1);
734			mp->vsize = vsize;
735		}
736		memcpy(mp->val,v,vsize);
737		mp->val[vsize] = 0;
738		nv_putsub(SH_MATCHNOD, NIL(char*), (nmatch-1)|ARRAY_FILL);
739		mp->lastsub = -1;
740	}
741}
742
743#define array_scan(np)	((nv_arrayptr(np)->nelem&ARRAY_SCAN))
744
745static char* get_match(register Namval_t* np, Namfun_t *fp)
746{
747	struct match *mp = (struct match*)fp;
748	int sub,n;
749	char *val;
750	sub = nv_aindex(np);
751	if(sub>=mp->nmatch)
752		return(0);
753	if(sub==mp->lastsub)
754		return(mp->rval);
755	if(mp->rval)
756	{
757		free((void*)mp->rval);
758		mp->rval = 0;
759	}
760	n = mp->match[2*sub+1]-mp->match[2*sub];
761	if(n<=0)
762		return("");
763	val = mp->val+mp->match[2*sub];
764	if(mp->val[mp->match[2*sub+1]]==0)
765		return(val);
766	mp->rval = (char*)malloc(n+1);
767	mp->lastsub = sub;
768	memcpy(mp->rval,val,n);
769	mp->rval[n] = 0;
770	return(mp->rval);
771}
772
773static const Namdisc_t SH_MATCH_disc  = { sizeof(struct match), 0, get_match };
774
775static char* get_version(register Namval_t* np, Namfun_t *fp)
776{
777	return(nv_getv(np,fp));
778}
779
780static Sfdouble_t nget_version(register Namval_t* np, Namfun_t *fp)
781{
782	register const char	*cp = e_version + strlen(e_version)-10;
783	register int		c;
784	Sflong_t		t = 0;
785	NOT_USED(fp);
786
787	while (c = *cp++)
788		if (c >= '0' && c <= '9')
789		{
790			t *= 10;
791			t += c - '0';
792		}
793	return((Sfdouble_t)t);
794}
795
796static const Namdisc_t SH_VERSION_disc	= {  0, 0, get_version, nget_version };
797
798#if SHOPT_FS_3D
799    /*
800     * set or unset the mappings given a colon separated list of directories
801     */
802    static void vpath_set(char *str, int mode)
803    {
804	register char *lastp, *oldp=str, *newp=strchr(oldp,':');
805	if(!sh.lim.fs3d)
806		return;
807	while(newp)
808	{
809		*newp++ = 0;
810		if(lastp=strchr(newp,':'))
811			*lastp = 0;
812		mount((mode?newp:""),oldp,FS3D_VIEW,0);
813		newp[-1] = ':';
814		oldp = newp;
815		newp=lastp;
816	}
817    }
818
819    /* catch vpath assignments */
820    static void put_vpath(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
821    {
822	register char *cp;
823	if(cp = nv_getval(np))
824		vpath_set(cp,0);
825	if(val)
826		vpath_set((char*)val,1);
827	nv_putv(np,val,flags,fp);
828    }
829    static const Namdisc_t VPATH_disc	= { 0, put_vpath  };
830    static Namfun_t VPATH_init	= { &VPATH_disc, 1  };
831#endif /* SHOPT_FS_3D */
832
833
834static const Namdisc_t IFS_disc		= {  sizeof(struct ifs), put_ifs, get_ifs };
835const Namdisc_t RESTRICTED_disc	= {  sizeof(Namfun_t), put_restricted };
836static const Namdisc_t CDPATH_disc	= {  sizeof(Namfun_t), put_cdpath };
837static const Namdisc_t EDITOR_disc	= {  sizeof(Namfun_t), put_ed };
838static const Namdisc_t HISTFILE_disc	= {  sizeof(Namfun_t), put_history };
839static const Namdisc_t OPTINDEX_disc	= {  sizeof(Namfun_t), put_optindex, 0, nget_optindex, 0, 0, clone_optindex };
840static const Namdisc_t SECONDS_disc	= {  sizeof(struct seconds), put_seconds, get_seconds, nget_seconds };
841static const Namdisc_t RAND_disc	= {  sizeof(struct rand), put_rand, get_rand, nget_rand };
842static const Namdisc_t LINENO_disc	= {  sizeof(Namfun_t), put_lineno, get_lineno, nget_lineno };
843static const Namdisc_t L_ARG_disc	= {  sizeof(Namfun_t), put_lastarg, get_lastarg };
844
845#if SHOPT_NAMESPACE
846    static char* get_nspace(Namval_t* np, Namfun_t *fp)
847    {
848	if(sh.namespace)
849		return(nv_name(sh.namespace));
850	return((char*)np->nvalue.cp);
851    }
852    static const Namdisc_t NSPACE_disc	= {  0, 0, get_nspace };
853    static Namfun_t NSPACE_init	= {  &NSPACE_disc, 1};
854#endif /* SHOPT_NAMESPACE */
855
856#ifdef _hdr_locale
857    static const Namdisc_t LC_disc	= {  sizeof(Namfun_t), put_lang };
858#endif /* _hdr_locale */
859
860/*
861 * This function will get called whenever a configuration parameter changes
862 */
863static int newconf(const char *name, const char *path, const char *value)
864{
865	register char *arg;
866	if(!name)
867		setenviron(value);
868	else if(strcmp(name,"UNIVERSE")==0 && strcmp(astconf(name,0,0),value))
869	{
870		sh.universe = 0;
871		/* set directory in new universe */
872		if(*(arg = path_pwd(0))=='/')
873			chdir(arg);
874		/* clear out old tracked alias */
875		stakseek(0);
876		stakputs(nv_getval(PATHNOD));
877		stakputc(0);
878		nv_putval(PATHNOD,stakseek(0),NV_RDONLY);
879	}
880	return(1);
881}
882
883#if	(CC_NATIVE != CC_ASCII)
884    static void a2e(char *d, const char *s)
885    {
886	register const unsigned char *t;
887	register int i;
888	t = CCMAP(CC_ASCII, CC_NATIVE);
889	for(i=0; i<(1<<CHAR_BIT); i++)
890		d[t[i]] = s[i];
891    }
892
893    static void init_ebcdic(void)
894    {
895	int i;
896	char *cp = (char*)malloc(ST_NONE*(1<<CHAR_BIT));
897	for(i=0; i < ST_NONE; i++)
898	{
899		a2e(cp,sh_lexrstates[i]);
900		sh_lexstates[i] = cp;
901		cp += (1<<CHAR_BIT);
902	}
903    }
904#endif
905
906/*
907 * return SH_TYPE_* bitmask for path
908 * 0 for "not a shell"
909 */
910int sh_type(register const char *path)
911{
912	register const char*	s;
913	register int		t = 0;
914
915	if (s = (const char*)strrchr(path, '/'))
916	{
917		if (*path == '-')
918			t |= SH_TYPE_LOGIN;
919		s++;
920	}
921	else
922		s = path;
923	if (*s == '-')
924	{
925		s++;
926		t |= SH_TYPE_LOGIN;
927	}
928	for (;;)
929	{
930		if (!(t & (SH_TYPE_KSH|SH_TYPE_BASH)))
931		{
932			if (*s == 'k')
933			{
934				s++;
935				t |= SH_TYPE_KSH;
936				continue;
937			}
938#if SHOPT_BASH
939			if (*s == 'b' && *(s+1) == 'a')
940			{
941				s += 2;
942				t |= SH_TYPE_BASH;
943				continue;
944			}
945#endif
946		}
947		if (!(t & (SH_TYPE_PROFILE|SH_TYPE_RESTRICTED)))
948		{
949#if SHOPT_PFSH
950			if (*s == 'p' && *(s+1) == 'f')
951			{
952				s += 2;
953				t |= SH_TYPE_PROFILE;
954				continue;
955			}
956#endif
957			if (*s == 'r')
958			{
959				s++;
960				t |= SH_TYPE_RESTRICTED;
961				continue;
962			}
963		}
964		break;
965	}
966	if (*s++ == 's' && (*s == 'h' || *s == 'u'))
967	{
968		s++;
969		t |= SH_TYPE_SH;
970		if ((t & SH_TYPE_KSH) && *s == '9' && *(s+1) == '3')
971			s += 2;
972#if _WINIX
973		if (*s == '.' && *(s+1) == 'e' && *(s+2) == 'x' && *(s+3) == 'e')
974			s += 4;
975#endif
976		if (!isalnum(*s))
977			return t;
978	}
979	return t & ~(SH_TYPE_BASH|SH_TYPE_KSH|SH_TYPE_PROFILE|SH_TYPE_RESTRICTED);
980}
981
982
983static char *get_mode(Namval_t* np, Namfun_t* nfp)
984{
985	mode_t mode = nv_getn(np,nfp);
986	return(fmtperm(mode));
987}
988
989static void put_mode(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
990{
991	if(val)
992	{
993		mode_t mode;
994		char *last;
995		if(flag&NV_INTEGER)
996		{
997			if(flag&NV_LONG)
998				mode = *(Sfdouble_t*)val;
999			else
1000				mode = *(double*)val;
1001		}
1002		else
1003			mode = strperm(val, &last,0);
1004		if(*last)
1005			errormsg(SH_DICT,ERROR_exit(1),"%s: invalid mode string",val);
1006		nv_putv(np,(char*)&mode,NV_INTEGER,nfp);
1007	}
1008	else
1009		nv_putv(np,val,flag,nfp);
1010}
1011
1012static const Namdisc_t modedisc =
1013{
1014	0,
1015        put_mode,
1016        get_mode,
1017};
1018
1019
1020/*
1021 * initialize the shell
1022 */
1023Shell_t *sh_init(register int argc,register char *argv[], Shinit_f userinit)
1024{
1025	Shell_t	*shp = &sh;
1026	register int n;
1027	int type;
1028	long v;
1029	static char *login_files[3];
1030	memfatal();
1031	n = strlen(e_version);
1032	if(e_version[n-1]=='$' && e_version[n-2]==' ')
1033		e_version[n-2]=0;
1034#if	(CC_NATIVE == CC_ASCII)
1035	memcpy(sh_lexstates,sh_lexrstates,ST_NONE*sizeof(char*));
1036#else
1037	init_ebcdic();
1038#endif
1039	umask(shp->mask=umask(0));
1040	shp->mac_context = sh_macopen(shp);
1041	shp->arg_context = sh_argopen(shp);
1042	shp->lex_context = (void*)sh_lexopen(0,shp,1);
1043	shp->ed_context = (void*)ed_open(shp);
1044	shp->strbuf = sfstropen();
1045	shp->stk = stkstd;
1046	sfsetbuf(shp->strbuf,(char*)0,64);
1047	sh_onstate(SH_INIT);
1048	error_info.exit = sh_exit;
1049	error_info.id = path_basename(argv[0]);
1050#if ERROR_VERSION >= 20000102L
1051	error_info.catalog = e_dict;
1052#endif
1053#if SHOPT_REGRESS
1054	{
1055		Opt_t*	nopt;
1056		Opt_t*	oopt;
1057		char*	a;
1058		char**	av = argv;
1059		char*	regress[3];
1060
1061		sh_regress_init(shp);
1062		regress[0] = "__regress__";
1063		regress[2] = 0;
1064		/* NOTE: only shp is used by __regress__ at this point */
1065		shp->bltindata.shp = shp;
1066		while ((a = *++av) && a[0] == '-' && (a[1] == 'I' || a[1] == '-' && a[2] == 'r'))
1067		{
1068			if (a[1] == 'I')
1069			{
1070				if (a[2])
1071					regress[1] = a + 2;
1072				else if (!(regress[1] = *++av))
1073					break;
1074			}
1075			else if (strncmp(a+2, "regress", 7))
1076				break;
1077			else if (a[9] == '=')
1078				regress[1] = a + 10;
1079			else if (!(regress[1] = *++av))
1080				break;
1081			nopt = optctx(0, 0);
1082			oopt = optctx(nopt, 0);
1083			b___regress__(2, regress, &shp->bltindata);
1084			optctx(oopt, nopt);
1085		}
1086	}
1087#endif
1088	shp->cpipe[0] = -1;
1089	shp->coutpipe = -1;
1090	shp->userid=getuid();
1091	shp->euserid=geteuid();
1092	shp->groupid=getgid();
1093	shp->egroupid=getegid();
1094	for(n=0;n < 10; n++)
1095	{
1096		/* don't use lower bits when rand() generates large numbers */
1097		if(rand() > RANDMASK)
1098		{
1099			rand_shift = 3;
1100			break;
1101		}
1102	}
1103	shp->lim.clk_tck = getconf("CLK_TCK");
1104	shp->lim.arg_max = getconf("ARG_MAX");
1105	shp->lim.open_max = getconf("OPEN_MAX");
1106	shp->lim.child_max = getconf("CHILD_MAX");
1107	shp->lim.ngroups_max = getconf("NGROUPS_MAX");
1108	shp->lim.posix_version = getconf("VERSION");
1109	shp->lim.posix_jobcontrol = getconf("JOB_CONTROL");
1110	if(shp->lim.arg_max <=0)
1111		shp->lim.arg_max = ARG_MAX;
1112	if(shp->lim.child_max <=0)
1113		shp->lim.child_max = CHILD_MAX;
1114	if((v = getconf("PID_MAX")) > 0 && shp->lim.child_max > v)
1115		shp->lim.child_max = v;
1116	if(shp->lim.open_max <0)
1117		shp->lim.open_max = OPEN_MAX;
1118	if(shp->lim.open_max > (SHRT_MAX-2))
1119		shp->lim.open_max = SHRT_MAX-2;
1120	if(shp->lim.clk_tck <=0)
1121		shp->lim.clk_tck = CLK_TCK;
1122#if SHOPT_FS_3D
1123	if(fs3d(FS3D_TEST))
1124		shp->lim.fs3d = 1;
1125#endif /* SHOPT_FS_3D */
1126	sh_ioinit(shp);
1127	/* initialize signal handling */
1128	sh_siginit(shp);
1129	stakinstall(NIL(Stak_t*),nospace);
1130	/* set up memory for name-value pairs */
1131	shp->init_context =  nv_init(shp);
1132	/* read the environment */
1133	if(argc>0)
1134	{
1135		type = sh_type(*argv);
1136		if(type&SH_TYPE_LOGIN)
1137			shp->login_sh = 2;
1138	}
1139	env_init(shp);
1140	if(!ENVNOD->nvalue.cp)
1141	{
1142		sfprintf(shp->strbuf,"%s/.kshrc",nv_getval(HOME));
1143		nv_putval(ENVNOD,sfstruse(shp->strbuf),NV_RDONLY);
1144	}
1145	*SHLVL->nvalue.ip +=1;
1146#if SHOPT_SPAWN
1147	{
1148		/*
1149		 * try to find the pathname for this interpreter
1150		 * try using environment variable _ or argv[0]
1151		 */
1152		char *cp=nv_getval(L_ARGNOD);
1153		char buff[PATH_MAX+1];
1154		shp->shpath = 0;
1155#if _AST_VERSION >= 20090202L
1156		if((n = pathprog(NiL, buff, sizeof(buff))) > 0 && n <= sizeof(buff))
1157			shp->shpath = strdup(buff);
1158#else
1159		sfprintf(shp->strbuf,"/proc/%d/exe",getpid());
1160		if((n=readlink(sfstruse(shp->strbuf),buff,sizeof(buff)-1))>0)
1161		{
1162			buff[n] = 0;
1163			shp->shpath = strdup(buff);
1164		}
1165#endif
1166		else if((cp && (sh_type(cp)&SH_TYPE_SH)) || (argc>0 && strchr(cp= *argv,'/')))
1167		{
1168			if(*cp=='/')
1169				shp->shpath = strdup(cp);
1170			else if(cp = nv_getval(PWDNOD))
1171			{
1172				int offset = staktell();
1173				stakputs(cp);
1174				stakputc('/');
1175				stakputs(argv[0]);
1176				pathcanon(stakptr(offset),PATH_DOTDOT);
1177				shp->shpath = strdup(stakptr(offset));
1178				stakseek(offset);
1179			}
1180		}
1181	}
1182#endif
1183	nv_putval(IFSNOD,(char*)e_sptbnl,NV_RDONLY);
1184#if SHOPT_FS_3D
1185	nv_stack(VPATHNOD, &VPATH_init);
1186#endif /* SHOPT_FS_3D */
1187	astconfdisc(newconf);
1188#if SHOPT_TIMEOUT
1189	shp->st.tmout = SHOPT_TIMEOUT;
1190#endif /* SHOPT_TIMEOUT */
1191	/* initialize jobs table */
1192	job_clear();
1193	if(argc>0)
1194	{
1195		/* check for restricted shell */
1196		if(type&SH_TYPE_RESTRICTED)
1197			sh_onoption(SH_RESTRICTED);
1198#if SHOPT_PFSH
1199		/* check for profile shell */
1200		else if(type&SH_TYPE_PROFILE)
1201			sh_onoption(SH_PFSH);
1202#endif
1203#if SHOPT_BASH
1204		/* check for invocation as bash */
1205		if(type&SH_TYPE_BASH)
1206		{
1207		        shp->userinit = userinit = bash_init;
1208			sh_onoption(SH_BASH);
1209			sh_onstate(SH_PREINIT);
1210			(*userinit)(shp, 0);
1211			sh_offstate(SH_PREINIT);
1212		}
1213#endif
1214		/* look for options */
1215		/* shp->st.dolc is $#	*/
1216		if((shp->st.dolc = sh_argopts(-argc,argv,shp)) < 0)
1217		{
1218			shp->exitval = 2;
1219			sh_done(shp,0);
1220		}
1221		opt_info.disc = 0;
1222		shp->st.dolv=argv+(argc-1)-shp->st.dolc;
1223		shp->st.dolv[0] = argv[0];
1224		if(shp->st.dolc < 1)
1225			sh_onoption(SH_SFLAG);
1226		if(!sh_isoption(SH_SFLAG))
1227		{
1228			shp->st.dolc--;
1229			shp->st.dolv++;
1230#if _WINIX
1231			{
1232				char*	name;
1233				name = shp->st.dolv[0];
1234				if(name[1]==':' && (name[2]=='/' || name[2]=='\\'))
1235				{
1236#if _lib_pathposix
1237					char*	p;
1238
1239					if((n = pathposix(name, NIL(char*), 0)) > 0 && (p = (char*)malloc(++n)))
1240					{
1241						pathposix(name, p, n);
1242						name = p;
1243					}
1244					else
1245#endif
1246					{
1247						name[1] = name[0];
1248						name[0] = name[2] = '/';
1249					}
1250				}
1251			}
1252#endif /* _WINIX */
1253		}
1254	}
1255#if SHOPT_PFSH
1256	if (sh_isoption(SH_PFSH))
1257	{
1258		struct passwd *pw = getpwuid(shp->userid);
1259		if(pw)
1260			shp->user = strdup(pw->pw_name);
1261
1262	}
1263#endif
1264	/* set[ug]id scripts require the -p flag */
1265	if(shp->userid!=shp->euserid || shp->groupid!=shp->egroupid)
1266	{
1267#ifdef SHOPT_P_SUID
1268		/* require sh -p to run setuid and/or setgid */
1269		if(!sh_isoption(SH_PRIVILEGED) && shp->userid >= SHOPT_P_SUID)
1270		{
1271			setuid(shp->euserid=shp->userid);
1272			setgid(shp->egroupid=shp->groupid);
1273		}
1274		else
1275#endif /* SHOPT_P_SUID */
1276			sh_onoption(SH_PRIVILEGED);
1277#ifdef SHELLMAGIC
1278		/* careful of #! setuid scripts with name beginning with - */
1279		if(shp->login_sh && argv[1] && strcmp(argv[0],argv[1])==0)
1280			errormsg(SH_DICT,ERROR_exit(1),e_prohibited);
1281#endif /*SHELLMAGIC*/
1282	}
1283	else
1284		sh_offoption(SH_PRIVILEGED);
1285	/* shname for $0 in profiles and . scripts */
1286	if(strmatch(argv[1],e_devfdNN))
1287		shp->shname = strdup(argv[0]);
1288	else
1289		shp->shname = strdup(shp->st.dolv[0]);
1290	/*
1291	 * return here for shell script execution
1292	 * but not for parenthesis subshells
1293	 */
1294	error_info.id = strdup(shp->st.dolv[0]); /* error_info.id is $0 */
1295	shp->jmpbuffer = (void*)&shp->checkbase;
1296	sh_pushcontext(&shp->checkbase,SH_JMPSCRIPT);
1297	shp->st.self = &shp->global;
1298        shp->topscope = (Shscope_t*)shp->st.self;
1299	sh_offstate(SH_INIT);
1300	login_files[0] = (char*)e_profile;
1301	login_files[1] = ".profile";
1302	shp->login_files = login_files;
1303	shp->bltindata.version = SH_VERSION;
1304	shp->bltindata.shp = shp;
1305	shp->bltindata.shrun = sh_run;
1306	shp->bltindata.shtrap = sh_trap;
1307	shp->bltindata.shexit = sh_exit;
1308	shp->bltindata.shbltin = sh_addbuiltin;
1309#if _AST_VERSION >= 20080617L
1310	shp->bltindata.shgetenv = sh_getenv;
1311	shp->bltindata.shsetenv = sh_setenviron;
1312	astintercept(&shp->bltindata,1);
1313#endif
1314#if 0
1315#define NV_MKINTTYPE(x,y,z)	nv_mkinttype(#x,sizeof(x),(x)-1<0,(y),(Namdisc_t*)z);
1316	NV_MKINTTYPE(pid_t,"process id",0);
1317	NV_MKINTTYPE(gid_t,"group id",0);
1318	NV_MKINTTYPE(uid_t,"user id",0);
1319	NV_MKINTTYPE(size_t,(const char*)0,0);
1320	NV_MKINTTYPE(ssize_t,(const char*)0,0);
1321	NV_MKINTTYPE(off_t,"offset in bytes",0);
1322	NV_MKINTTYPE(ino_t,"\ai-\anode number",0);
1323	NV_MKINTTYPE(mode_t,(const char*)0,&modedisc);
1324	NV_MKINTTYPE(dev_t,"device id",0);
1325	NV_MKINTTYPE(nlink_t,"hard link count",0);
1326	NV_MKINTTYPE(blkcnt_t,"block count",0);
1327	NV_MKINTTYPE(time_t,"seconds since the epoch",0);
1328	nv_mkstat();
1329#endif
1330	if(shp->userinit=userinit)
1331		(*userinit)(shp, 0);
1332	return(shp);
1333}
1334
1335Shell_t *sh_getinterp(void)
1336{
1337	return(&sh);
1338}
1339
1340/*
1341 * reinitialize before executing a script
1342 */
1343int sh_reinit(char *argv[])
1344{
1345	Shell_t	*shp = &sh;
1346	Shopt_t opt;
1347	Namval_t *np,*npnext;
1348	Dt_t	*dp;
1349	for(np=dtfirst(shp->fun_tree);np;np=npnext)
1350	{
1351		if((dp=shp->fun_tree)->walk)
1352			dp = dp->walk;
1353		npnext = (Namval_t*)dtnext(shp->fun_tree,np);
1354		if(np>= shp->bltin_cmds && np < &shp->bltin_cmds[nbltins])
1355			continue;
1356		if(is_abuiltin(np) && nv_isattr(np,NV_EXPORT))
1357			continue;
1358		if(*np->nvname=='/')
1359			continue;
1360		nv_delete(np,dp,NV_NOFREE);
1361	}
1362	dtclose(shp->alias_tree);
1363	shp->alias_tree = inittree(shp,shtab_aliases);
1364	shp->last_root = shp->var_tree;
1365	shp->namespace = 0;
1366	shp->inuse_bits = 0;
1367	if(shp->userinit)
1368		(*shp->userinit)(shp, 1);
1369	if(shp->heredocs)
1370	{
1371		sfclose(shp->heredocs);
1372		shp->heredocs = 0;
1373	}
1374	/* remove locals */
1375	sh_onstate(SH_INIT);
1376	nv_scan(shp->var_tree,sh_envnolocal,(void*)0,NV_EXPORT,0);
1377	nv_scan(shp->var_tree,sh_envnolocal,(void*)0,NV_ARRAY,NV_ARRAY);
1378	sh_offstate(SH_INIT);
1379	memset(shp->st.trapcom,0,(shp->st.trapmax+1)*sizeof(char*));
1380	memset((void*)&opt,0,sizeof(opt));
1381	if(sh_isoption(SH_TRACKALL))
1382		on_option(&opt,SH_TRACKALL);
1383	if(sh_isoption(SH_EMACS))
1384		on_option(&opt,SH_EMACS);
1385	if(sh_isoption(SH_GMACS))
1386		on_option(&opt,SH_GMACS);
1387	if(sh_isoption(SH_VI))
1388		on_option(&opt,SH_VI);
1389	if(sh_isoption(SH_VIRAW))
1390		on_option(&opt,SH_VIRAW);
1391	shp->options = opt;
1392	/* set up new args */
1393	if(argv)
1394		shp->arglist = sh_argcreate(argv);
1395	if(shp->arglist)
1396		sh_argreset(shp,shp->arglist,NIL(struct dolnod*));
1397	shp->envlist=0;
1398	shp->curenv = 0;
1399	shp->shname = error_info.id = strdup(shp->st.dolv[0]);
1400	sh_offstate(SH_FORKED);
1401	shp->fn_depth = shp->dot_depth = 0;
1402	sh_sigreset(0);
1403	if(!(SHLVL->nvalue.ip))
1404	{
1405		shlvl = 0;
1406		SHLVL->nvalue.ip = &shlvl;
1407		nv_onattr(SHLVL,NV_INTEGER|NV_EXPORT|NV_NOFREE);
1408	}
1409	*SHLVL->nvalue.ip +=1;
1410	shp->st.filename = strdup(shp->lastarg);
1411	return(1);
1412}
1413
1414/*
1415 * set when creating a local variable of this name
1416 */
1417Namfun_t *nv_cover(register Namval_t *np)
1418{
1419	if(np==IFSNOD || np==PATHNOD || np==SHELLNOD || np==FPATHNOD || np==CDPNOD || np==SECONDS || np==ENVNOD)
1420		return(np->nvfun);
1421#ifdef _hdr_locale
1422	if(np==LCALLNOD || np==LCTYPENOD || np==LCMSGNOD || np==LCCOLLNOD || np==LCNUMNOD || np==LANGNOD)
1423		return(np->nvfun);
1424#endif
1425	 return(0);
1426}
1427
1428static const char *shdiscnames[] = { "tilde", 0};
1429
1430#ifdef SHOPT_STATS
1431struct Stats
1432{
1433	Namfun_t	hdr;
1434	Shell_t		*sh;
1435	char		*nodes;
1436	int		numnodes;
1437	int		current;
1438};
1439
1440static Namval_t *next_stat(register Namval_t* np, Dt_t *root,Namfun_t *fp)
1441{
1442	struct Stats *sp = (struct Stats*)fp;
1443	if(!root)
1444		sp->current = 0;
1445	else if(++sp->current>=sp->numnodes)
1446		return(0);
1447	return(nv_namptr(sp->nodes,sp->current));
1448}
1449
1450static Namval_t *create_stat(Namval_t *np,const char *name,int flag,Namfun_t *fp)
1451{
1452	struct Stats		*sp = (struct Stats*)fp;
1453	register const char	*cp=name;
1454	register int		i=0,n;
1455	Namval_t		*nq=0;
1456	Shell_t			*shp = sp->sh;
1457	if(!name)
1458		return(SH_STATS);
1459	while((i=*cp++) && i != '=' && i != '+' && i!='[');
1460	n = (cp-1) -name;
1461	for(i=0; i < sp->numnodes; i++)
1462	{
1463		nq = nv_namptr(sp->nodes,i);
1464		if((n==0||memcmp(name,nq->nvname,n)==0) && nq->nvname[n]==0)
1465			goto found;
1466	}
1467	nq = 0;
1468found:
1469	if(nq)
1470	{
1471		fp->last = (char*)&name[n];
1472		shp->last_table = SH_STATS;
1473	}
1474	else
1475		errormsg(SH_DICT,ERROR_exit(1),e_notelem,n,name,nv_name(np));
1476	return(nq);
1477}
1478
1479static const Namdisc_t stat_disc =
1480{
1481	0, 0, 0, 0, 0,
1482	create_stat,
1483	0, 0,
1484	next_stat
1485};
1486
1487static char *name_stat(Namval_t *np, Namfun_t *fp)
1488{
1489	Shell_t	*shp = sh_getinterp();
1490	sfprintf(shp->strbuf,".sh.stats.%s",np->nvname);
1491	return(sfstruse(shp->strbuf));
1492}
1493
1494static const Namdisc_t	stat_child_disc =
1495{
1496	0,0,0,0,0,0,0,
1497	name_stat
1498};
1499
1500static Namfun_t	 stat_child_fun =
1501{
1502	&stat_child_disc, 1, 0, sizeof(Namfun_t)
1503};
1504
1505static void stat_init(Shell_t *shp)
1506{
1507	int		i,nstat = STAT_SUBSHELL+1;
1508	struct Stats	*sp = newof(0,struct Stats,1,nstat*NV_MINSZ);
1509	Namval_t	*np;
1510	sp->numnodes = nstat;
1511	sp->nodes = (char*)(sp+1);
1512	shp->stats = (int*)calloc(sizeof(int*),nstat);
1513	sp->sh = shp;
1514	for(i=0; i < nstat; i++)
1515	{
1516		np = nv_namptr(sp->nodes,i);
1517		np->nvfun = &stat_child_fun;
1518		np->nvname = (char*)shtab_stats[i].sh_name;
1519		nv_onattr(np,NV_RDONLY|NV_MINIMAL|NV_NOFREE|NV_INTEGER);
1520		nv_setsize(np,10);
1521		np->nvalue.ip = &shp->stats[i];
1522	}
1523	sp->hdr.dsize = sizeof(struct Stats) + nstat*(sizeof(int)+NV_MINSZ);
1524	sp->hdr.disc = &stat_disc;
1525	nv_stack(SH_STATS,&sp->hdr);
1526	sp->hdr.nofree = 1;
1527	nv_setvtree(SH_STATS);
1528}
1529#else
1530#   define stat_init(x)
1531#endif /* SHOPT_STATS */
1532
1533/*
1534 * Initialize the shell name and alias table
1535 */
1536static Init_t *nv_init(Shell_t *shp)
1537{
1538	Namval_t *np;
1539	register Init_t *ip;
1540	double d=0;
1541	ip = newof(0,Init_t,1,0);
1542	if(!ip)
1543		return(0);
1544	shp->nvfun.last = (char*)shp;
1545	shp->nvfun.nofree = 1;
1546	ip->sh = shp;
1547	shp->var_base = shp->var_tree = inittree(shp,shtab_variables);
1548	SHLVL->nvalue.ip = &shlvl;
1549	ip->IFS_init.hdr.disc = &IFS_disc;
1550	ip->IFS_init.hdr.nofree = 1;
1551	ip->PATH_init.disc = &RESTRICTED_disc;
1552	ip->PATH_init.nofree = 1;
1553	ip->FPATH_init.disc = &RESTRICTED_disc;
1554	ip->FPATH_init.nofree = 1;
1555	ip->CDPATH_init.disc = &CDPATH_disc;
1556	ip->CDPATH_init.nofree = 1;
1557	ip->SHELL_init.disc = &RESTRICTED_disc;
1558	ip->SHELL_init.nofree = 1;
1559	ip->ENV_init.disc = &RESTRICTED_disc;
1560	ip->ENV_init.nofree = 1;
1561	ip->VISUAL_init.disc = &EDITOR_disc;
1562	ip->VISUAL_init.nofree = 1;
1563	ip->EDITOR_init.disc = &EDITOR_disc;
1564	ip->EDITOR_init.nofree = 1;
1565	ip->HISTFILE_init.disc = &HISTFILE_disc;
1566	ip->HISTFILE_init.nofree = 1;
1567	ip->HISTSIZE_init.disc = &HISTFILE_disc;
1568	ip->HISTSIZE_init.nofree = 1;
1569	ip->OPTINDEX_init.disc = &OPTINDEX_disc;
1570	ip->OPTINDEX_init.nofree = 1;
1571	ip->SECONDS_init.hdr.disc = &SECONDS_disc;
1572	ip->SECONDS_init.hdr.nofree = 1;
1573	ip->RAND_init.hdr.disc = &RAND_disc;
1574	ip->RAND_init.hdr.nofree = 1;
1575	ip->SH_MATCH_init.hdr.disc = &SH_MATCH_disc;
1576	ip->SH_MATCH_init.hdr.nofree = 1;
1577	ip->SH_VERSION_init.disc = &SH_VERSION_disc;
1578	ip->SH_VERSION_init.nofree = 1;
1579	ip->LINENO_init.disc = &LINENO_disc;
1580	ip->LINENO_init.nofree = 1;
1581	ip->L_ARG_init.disc = &L_ARG_disc;
1582	ip->L_ARG_init.nofree = 1;
1583#ifdef _hdr_locale
1584	ip->LC_TYPE_init.disc = &LC_disc;
1585	ip->LC_TYPE_init.nofree = 1;
1586	ip->LC_NUM_init.disc = &LC_disc;
1587	ip->LC_NUM_init.nofree = 1;
1588	ip->LC_COLL_init.disc = &LC_disc;
1589	ip->LC_COLL_init.nofree = 1;
1590	ip->LC_MSG_init.disc = &LC_disc;
1591	ip->LC_MSG_init.nofree = 1;
1592	ip->LC_ALL_init.disc = &LC_disc;
1593	ip->LC_ALL_init.nofree = 1;
1594	ip->LANG_init.disc = &LC_disc;
1595	ip->LANG_init.nofree = 1;
1596#endif /* _hdr_locale */
1597	nv_stack(IFSNOD, &ip->IFS_init.hdr);
1598	nv_stack(PATHNOD, &ip->PATH_init);
1599	nv_stack(FPATHNOD, &ip->FPATH_init);
1600	nv_stack(CDPNOD, &ip->CDPATH_init);
1601	nv_stack(SHELLNOD, &ip->SHELL_init);
1602	nv_stack(ENVNOD, &ip->ENV_init);
1603	nv_stack(VISINOD, &ip->VISUAL_init);
1604	nv_stack(EDITNOD, &ip->EDITOR_init);
1605	nv_stack(HISTFILE, &ip->HISTFILE_init);
1606	nv_stack(HISTSIZE, &ip->HISTSIZE_init);
1607	nv_stack(OPTINDNOD, &ip->OPTINDEX_init);
1608	nv_stack(SECONDS, &ip->SECONDS_init.hdr);
1609	nv_stack(L_ARGNOD, &ip->L_ARG_init);
1610	nv_putval(SECONDS, (char*)&d, NV_DOUBLE);
1611	nv_stack(RANDNOD, &ip->RAND_init.hdr);
1612	d = (shp->pid&RANDMASK);
1613	nv_putval(RANDNOD, (char*)&d, NV_DOUBLE);
1614	nv_stack(LINENO, &ip->LINENO_init);
1615	nv_putsub(SH_MATCHNOD,(char*)0,10);
1616	nv_onattr(SH_MATCHNOD,NV_RDONLY);
1617	nv_stack(SH_MATCHNOD, &ip->SH_MATCH_init.hdr);
1618	nv_stack(SH_VERSIONNOD, &ip->SH_VERSION_init);
1619#ifdef _hdr_locale
1620	nv_stack(LCTYPENOD, &ip->LC_TYPE_init);
1621	nv_stack(LCALLNOD, &ip->LC_ALL_init);
1622	nv_stack(LCMSGNOD, &ip->LC_MSG_init);
1623	nv_stack(LCCOLLNOD, &ip->LC_COLL_init);
1624	nv_stack(LCNUMNOD, &ip->LC_NUM_init);
1625	nv_stack(LANGNOD, &ip->LANG_init);
1626#endif /* _hdr_locale */
1627	(PPIDNOD)->nvalue.lp = (&shp->ppid);
1628	(TMOUTNOD)->nvalue.lp = (&shp->st.tmout);
1629	(MCHKNOD)->nvalue.lp = (&sh_mailchk);
1630	(OPTINDNOD)->nvalue.lp = (&shp->st.optindex);
1631	/* set up the seconds clock */
1632	shp->alias_tree = inittree(shp,shtab_aliases);
1633	shp->track_tree = dtopen(&_Nvdisc,Dtset);
1634	shp->bltin_tree = inittree(shp,(const struct shtable2*)shtab_builtins);
1635	shp->fun_tree = dtopen(&_Nvdisc,Dtoset);
1636	dtview(shp->fun_tree,shp->bltin_tree);
1637#if SHOPT_NAMESPACE
1638	if(np = nv_mount(DOTSHNOD, "global", shp->var_tree))
1639		nv_onattr(np,NV_RDONLY);
1640	np = nv_search("namespace",nv_dict(DOTSHNOD),NV_ADD);
1641	nv_putval(np,".sh.global",NV_RDONLY|NV_NOFREE);
1642	nv_stack(np, &NSPACE_init);
1643#endif /* SHOPT_NAMESPACE */
1644	np = nv_mount(DOTSHNOD, "type", shp->typedict=dtopen(&_Nvdisc,Dtoset));
1645	nv_adddisc(DOTSHNOD, shdiscnames, (Namval_t**)0);
1646	SH_LINENO->nvalue.ip = &shp->st.lineno;
1647	VERSIONNOD->nvalue.nrp = newof(0,struct Namref,1,0);
1648        VERSIONNOD->nvalue.nrp->np = SH_VERSIONNOD;
1649        VERSIONNOD->nvalue.nrp->root = nv_dict(DOTSHNOD);
1650        VERSIONNOD->nvalue.nrp->table = DOTSHNOD;
1651	nv_onattr(VERSIONNOD,NV_RDONLY|NV_REF);
1652	stat_init(shp);
1653	return(ip);
1654}
1655
1656/*
1657 * initialize name-value pairs
1658 */
1659
1660static Dt_t *inittree(Shell_t *shp,const struct shtable2 *name_vals)
1661{
1662	register Namval_t *np;
1663	register const struct shtable2 *tp;
1664	register unsigned n = 0;
1665	register Dt_t *treep;
1666	Dt_t *base_treep, *dict;
1667	for(tp=name_vals;*tp->sh_name;tp++)
1668		n++;
1669	np = (Namval_t*)calloc(n,sizeof(Namval_t));
1670	if(!shp->bltin_nodes)
1671	{
1672		shp->bltin_nodes = np;
1673		shp->bltin_nnodes = n;
1674	}
1675	else if(name_vals==(const struct shtable2*)shtab_builtins)
1676	{
1677		shp->bltin_cmds = np;
1678		nbltins = n;
1679	}
1680	base_treep = treep = dtopen(&_Nvdisc,Dtoset);
1681	treep->user = (void*)shp;
1682	for(tp=name_vals;*tp->sh_name;tp++,np++)
1683	{
1684		if((np->nvname = strrchr(tp->sh_name,'.')) && np->nvname!=((char*)tp->sh_name))
1685			np->nvname++;
1686		else
1687		{
1688			np->nvname = (char*)tp->sh_name;
1689			treep = base_treep;
1690		}
1691		np->nvenv = 0;
1692		if(name_vals==(const struct shtable2*)shtab_builtins)
1693			np->nvalue.bfp = ((struct shtable3*)tp)->sh_value;
1694		else
1695		{
1696			if(name_vals == shtab_variables)
1697				np->nvfun = &sh.nvfun;
1698			np->nvalue.cp = (char*)tp->sh_value;
1699		}
1700		nv_setattr(np,tp->sh_number);
1701		if(nv_istable(np))
1702			nv_mount(np,(const char*)0,dict=dtopen(&_Nvdisc,Dtoset));
1703		if(nv_isattr(np,NV_INTEGER))
1704			nv_setsize(np,10);
1705		else
1706			nv_setsize(np,0);
1707		dtinsert(treep,np);
1708		if(nv_istable(np))
1709			treep = dict;
1710	}
1711	return(treep);
1712}
1713
1714/*
1715 * read in the process environment and set up name-value pairs
1716 * skip over items that are not name-value pairs
1717 */
1718
1719static void env_init(Shell_t *shp)
1720{
1721	register char *cp;
1722	register Namval_t	*np;
1723	register char **ep=environ;
1724	register char *next=0;
1725#ifdef _ENV_H
1726	shp->env = env_open(environ,3);
1727	env_delete(shp->env,"_");
1728#endif
1729	if(ep)
1730	{
1731		while(cp= *ep++)
1732		{
1733			if(*cp=='A' && cp[1]=='_' && cp[2]=='_' && cp[3]=='z' && cp[4]=='=')
1734				next = cp+4;
1735			else if(np=nv_open(cp,shp->var_tree,(NV_EXPORT|NV_IDENT|NV_ASSIGN|NV_NOFAIL)))
1736			{
1737				nv_onattr(np,NV_IMPORT);
1738				np->nvenv = cp;
1739				nv_close(np);
1740			}
1741			else  /* swap with front */
1742			{
1743				ep[-1] = environ[shp->nenv];
1744				environ[shp->nenv++] = cp;
1745			}
1746		}
1747		while(cp=next)
1748		{
1749			if(next = strchr(++cp,'='))
1750				*next = 0;
1751			np = nv_search(cp+2,shp->var_tree,NV_ADD);
1752			if(np!=SHLVL && nv_isattr(np,NV_IMPORT|NV_EXPORT))
1753			{
1754				int flag = *(unsigned char*)cp-' ';
1755				int size = *(unsigned char*)(cp+1)-' ';
1756				if((flag&NV_INTEGER) && size==0)
1757				{
1758					/* check for floating*/
1759					char *ep,*val = nv_getval(np);
1760					strtol(val,&ep,10);
1761					if(*ep=='.' || *ep=='e' || *ep=='E')
1762					{
1763						char *lp;
1764						flag |= NV_DOUBLE;
1765						if(*ep=='.')
1766						{
1767							strtol(ep+1,&lp,10);
1768							if(*lp)
1769								ep = lp;
1770						}
1771						if(*ep && *ep!='.')
1772						{
1773							flag |= NV_EXPNOTE;
1774							size = ep-val;
1775						}
1776						else
1777							size = strlen(ep);
1778						size--;
1779					}
1780				}
1781				nv_newattr(np,flag|NV_IMPORT|NV_EXPORT,size);
1782			}
1783			else
1784				cp += 2;
1785		}
1786	}
1787#ifdef _ENV_H
1788	env_delete(shp->env,e_envmarker);
1789#endif
1790	if(nv_isnull(PWDNOD) || nv_isattr(PWDNOD,NV_TAGGED))
1791	{
1792		nv_offattr(PWDNOD,NV_TAGGED);
1793		path_pwd(0);
1794	}
1795	if((cp = nv_getval(SHELLNOD)) && (sh_type(cp)&SH_TYPE_RESTRICTED))
1796		sh_onoption(SH_RESTRICTED); /* restricted shell */
1797	return;
1798}
1799
1800/*
1801 * terminate shell and free up the space
1802 */
1803int sh_term(void)
1804{
1805	sfdisc(sfstdin,SF_POPDISC);
1806	free((char*)sh.outbuff);
1807	stakset(NIL(char*),0);
1808	return(0);
1809}
1810
1811/* function versions of these */
1812
1813#define DISABLE	/* proto workaround */
1814
1815unsigned long sh_isoption DISABLE (int opt)
1816{
1817	return(sh_isoption(opt));
1818}
1819
1820unsigned long sh_onoption DISABLE (int opt)
1821{
1822	return(sh_onoption(opt));
1823}
1824
1825unsigned long sh_offoption DISABLE (int opt)
1826{
1827	return(sh_offoption(opt));
1828}
1829
1830void	sh_sigcheck DISABLE (void)
1831{
1832	sh_sigcheck();
1833}
1834
1835Dt_t*	sh_bltin_tree DISABLE (void)
1836{
1837	return(sh.bltin_tree);
1838}
1839