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