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