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