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