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