1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2008 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                  David Korn <dgk@research.att.com>                   *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * export [-p] [arg...]
23  * readonly [-p] [arg...]
24  * typeset [options]  [arg...]
25  * alias [-ptx] [arg...]
26  * unalias [arg...]
27  * builtin [-sd] [-f file] [name...]
28  * set [options] [name...]
29  * unset [-fnv] [name...]
30  *
31  *   David Korn
32  *   AT&T Labs
33  *
34  */
35 
36 #include	"defs.h"
37 #include	<error.h>
38 #include	"path.h"
39 #include	"name.h"
40 #include	"history.h"
41 #include	"builtins.h"
42 #include	"variables.h"
43 #include	"FEATURE/dynamic"
44 
45 struct tdata
46 {
47 	Shell_t 	*sh;
48 	Namval_t	*tp;
49 	Sfio_t  	*outfile;
50 	char    	*prefix;
51 	char    	*tname;
52 	char		*help;
53 	short     	aflag;
54 	short     	pflag;
55 	int     	argnum;
56 	int     	scanmask;
57 	Dt_t 		*scanroot;
58 	char    	**argnam;
59 };
60 
61 
62 static int	print_namval(Sfio_t*, Namval_t*, int, struct tdata*);
63 static void	print_attribute(Namval_t*,void*);
64 static void	print_all(Sfio_t*, Dt_t*, struct tdata*);
65 static void	print_scan(Sfio_t*, int, Dt_t*, int, struct tdata*);
66 static int	b_unall(int, char**, Dt_t*, Shell_t*);
67 static int	b_common(char**, int, Dt_t*, struct tdata*);
68 static void	pushname(Namval_t*,void*);
69 static void(*nullscan)(Namval_t*,void*);
70 
71 static Namval_t *load_class(const char *name)
72 {
73 	errormsg(SH_DICT,ERROR_exit(1),"%s: type not loadable",name);
74 	return(0);
75 }
76 
77 /*
78  * Note export and readonly are the same
79  */
80 #if 0
81     /* for the dictionary generator */
82     int    b_export(int argc,char *argv[],void *extra){}
83 #endif
84 int    b_readonly(int argc,char *argv[],void *extra)
85 {
86 	register int flag;
87 	char *command = argv[0];
88 	struct tdata tdata;
89 	NOT_USED(argc);
90 	memset((void*)&tdata,0,sizeof(tdata));
91 	tdata.sh = ((Shbltin_t*)extra)->shp;
92 	tdata.aflag = '-';
93 	while((flag = optget(argv,*command=='e'?sh_optexport:sh_optreadonly))) switch(flag)
94 	{
95 		case 'p':
96 			tdata.prefix = command;
97 			break;
98 		case ':':
99 			errormsg(SH_DICT,2, "%s", opt_info.arg);
100 			break;
101 		case '?':
102 			errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
103 			return(2);
104 	}
105 	if(error_info.errors)
106 		errormsg(SH_DICT,ERROR_usage(2),optusage(NIL(char*)));
107 	argv += (opt_info.index-1);
108 	if(*command=='r')
109 		flag = (NV_ASSIGN|NV_RDONLY|NV_VARNAME);
110 #ifdef _ENV_H
111 	else if(!argv[1])
112 	{
113 		char *cp,**env=env_get(tdata.sh->env);
114 		while(cp = *env++)
115 		{
116 			if(tdata.prefix)
117 				sfputr(sfstdout,tdata.prefix,' ');
118 			sfprintf(sfstdout,"%s\n",sh_fmtq(cp));
119 		}
120 		return(0);
121 	}
122 #endif
123 	else
124 	{
125 		flag = (NV_ASSIGN|NV_EXPORT|NV_IDENT);
126 		if(!tdata.sh->prefix)
127 			tdata.sh->prefix = "";
128 	}
129 	return(b_common(argv,flag,tdata.sh->var_tree, &tdata));
130 }
131 
132 
133 int    b_alias(int argc,register char *argv[],void *extra)
134 {
135 	register unsigned flag = NV_NOARRAY|NV_NOSCOPE|NV_ASSIGN;
136 	register Dt_t *troot;
137 	register int n;
138 	struct tdata tdata;
139 	NOT_USED(argc);
140 	memset((void*)&tdata,0,sizeof(tdata));
141 	tdata.sh = ((Shbltin_t*)extra)->shp;
142 	troot = tdata.sh->alias_tree;
143 	if(*argv[0]=='h')
144 		flag = NV_TAGGED;
145 	if(argv[1])
146 	{
147 		opt_info.offset = 0;
148 		opt_info.index = 1;
149 		*opt_info.option = 0;
150 		tdata.argnum = 0;
151 		tdata.aflag = *argv[1];
152 		while((n = optget(argv,sh_optalias))) switch(n)
153 		{
154 		    case 'p':
155 			tdata.prefix = argv[0];
156 			break;
157 		    case 't':
158 			flag |= NV_TAGGED;
159 			break;
160 		    case 'x':
161 			flag |= NV_EXPORT;
162 			break;
163 		    case ':':
164 			errormsg(SH_DICT,2, "%s", opt_info.arg);
165 			break;
166 		    case '?':
167 			errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
168 			return(2);
169 		}
170 		if(error_info.errors)
171 			errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
172 		argv += (opt_info.index-1);
173 		if(flag&NV_TAGGED)
174 		{
175 			/* hacks to handle hash -r | -- */
176 			if(argv[1] && argv[1][0]=='-')
177 			{
178 				if(argv[1][1]=='r' && argv[1][2]==0)
179 				{
180 					nv_putval(PATHNOD,nv_getval(PATHNOD),NV_RDONLY);
181 					argv++;
182 					if(!argv[1])
183 						return(0);
184 				}
185 				if(argv[1][0]=='-')
186 				{
187 					if(argv[1][1]=='-' && argv[1][2]==0)
188 						argv++;
189 					else
190 						errormsg(SH_DICT, ERROR_exit(1), e_option, argv[1]);
191 		}
192 			}
193 			troot = tdata.sh->track_tree;
194 		}
195 	}
196 	return(b_common(argv,flag,troot,&tdata));
197 }
198 
199 
200 #if 0
201     /* for the dictionary generator */
202     int    b_local(int argc,char *argv[],void *extra){}
203 #endif
204 int    b_typeset(int argc,register char *argv[],void *extra)
205 {
206 	register int	n, flag = NV_VARNAME|NV_ASSIGN;
207 	struct tdata	tdata;
208 	const char	*optstring = sh_opttypeset;
209 	Namdecl_t 	*ntp = (Namdecl_t*)((Shbltin_t*)extra)->ptr;
210 	Dt_t		*troot;
211 	int		isfloat=0, shortint=0, sflag=0;
212 	NOT_USED(argc);
213 	memset((void*)&tdata,0,sizeof(tdata));
214 	tdata.sh = ((Shbltin_t*)extra)->shp;
215 	if(ntp)
216 	{
217 		tdata.tp = ntp->tp;
218 		opt_info.disc = (Optdisc_t*)ntp->optinfof;
219 		optstring = ntp->optstring;
220 	}
221 	troot = tdata.sh->var_tree;
222 	while((n = optget(argv,optstring)))
223 	{
224 		switch(n)
225 		{
226 			case 'a':
227 				flag |= NV_IARRAY;
228 				if(opt_info.arg && *opt_info.arg!='[')
229 				{
230 					opt_info.index--;
231 					goto endargs;
232 				}
233 				tdata.tname = opt_info.arg;
234 				break;
235 			case 'A':
236 				flag |= NV_ARRAY;
237 				break;
238 			case 'C':
239 				flag |= NV_COMVAR;
240 				break;
241 			case 'E':
242 				/* The following is for ksh88 compatibility */
243 				if(opt_info.offset && !strchr(argv[opt_info.index],'E'))
244 				{
245 					tdata.argnum = (int)opt_info.num;
246 					break;
247 				}
248 			case 'F':
249 			case 'X':
250 				if(!opt_info.arg || (tdata.argnum = opt_info.num) <0)
251 					tdata.argnum = 10;
252 				isfloat = 1;
253 				if(n=='E')
254 				{
255 					flag &= ~NV_HEXFLOAT;
256 					flag |= NV_EXPNOTE;
257 				}
258 				else if(n=='X')
259 				{
260 					flag &= ~NV_EXPNOTE;
261 					flag |= NV_HEXFLOAT;
262 				}
263 				break;
264 			case 'b':
265 				flag |= NV_BINARY;
266 				break;
267 			case 'm':
268 				flag |= NV_MOVE;
269 				break;
270 			case 'n':
271 				flag &= ~NV_VARNAME;
272 				flag |= (NV_REF|NV_IDENT);
273 				break;
274 			case 'H':
275 				flag |= NV_HOST;
276 				break;
277 			case 'T':
278 				flag |= NV_TYPE;
279 				tdata.prefix = opt_info.arg;
280 				break;
281 			case 'L': case 'Z': case 'R':
282 				if(tdata.argnum==0)
283 					tdata.argnum = (int)opt_info.num;
284 				if(tdata.argnum < 0)
285 					errormsg(SH_DICT,ERROR_exit(1), e_badfield, tdata.argnum);
286 				if(n=='Z')
287 					flag |= NV_ZFILL;
288 				else
289 				{
290 					flag &= ~(NV_LJUST|NV_RJUST);
291 					flag |= (n=='L'?NV_LJUST:NV_RJUST);
292 				}
293 				break;
294 			case 'f':
295 				flag &= ~(NV_VARNAME|NV_ASSIGN);
296 				troot = tdata.sh->fun_tree;
297 				break;
298 			case 'i':
299 				if(!opt_info.arg || (tdata.argnum = opt_info.num) <0)
300 					tdata.argnum = 10;
301 				flag |= NV_INTEGER;
302 				break;
303 			case 'l':
304 				flag |= NV_UTOL;
305 				break;
306 			case 'p':
307 				tdata.prefix = argv[0];
308 				tdata.pflag = 1;
309 				break;
310 			case 'r':
311 				flag |= NV_RDONLY;
312 				break;
313 #ifdef SHOPT_TYPEDEF
314 			case 'S':
315 				sflag=1;
316 				break;
317 			case 'h':
318 				tdata.help = opt_info.arg;
319 				break;
320 #endif /*SHOPT_TYPEDEF*/
321 			case 's':
322 				shortint=1;
323 				break;
324 			case 't':
325 				flag |= NV_TAGGED;
326 				break;
327 			case 'u':
328 				flag |= NV_LTOU;
329 				break;
330 			case 'x':
331 				flag &= ~NV_VARNAME;
332 				flag |= (NV_EXPORT|NV_IDENT);
333 				break;
334 			case ':':
335 				errormsg(SH_DICT,2, "%s", opt_info.arg);
336 				break;
337 			case '?':
338 				errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
339 				opt_info.disc = 0;
340 				return(2);
341 		}
342 		if(tdata.aflag==0)
343 			tdata.aflag = *opt_info.option;
344 	}
345 endargs:
346 	argv += opt_info.index;
347 	opt_info.disc = 0;
348 	/* handle argument of + and - specially */
349 	if(*argv && argv[0][1]==0 && (*argv[0]=='+' || *argv[0]=='-'))
350 		tdata.aflag = *argv[0];
351 	else
352 		argv--;
353 	if((flag&NV_ZFILL) && !(flag&NV_LJUST))
354 		flag |= NV_RJUST;
355 	if((flag&NV_INTEGER) && (flag&(NV_LJUST|NV_RJUST|NV_ZFILL)))
356 		error_info.errors++;
357 	if((flag&NV_BINARY) && (flag&(NV_LJUST|NV_UTOL|NV_LTOU)))
358 		error_info.errors++;
359 	if((flag&NV_MOVE) && (flag&~(NV_MOVE|NV_VARNAME|NV_ASSIGN)))
360 		error_info.errors++;
361 	if((flag&NV_REF) && (flag&~(NV_REF|NV_IDENT|NV_ASSIGN)))
362 		error_info.errors++;
363 	if(troot==tdata.sh->fun_tree && ((isfloat || flag&~(NV_FUNCT|NV_TAGGED|NV_EXPORT|NV_LTOU))))
364 		error_info.errors++;
365 	if(error_info.errors)
366 		errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*)));
367 	if(isfloat)
368 		flag |= NV_DOUBLE;
369 	if(shortint)
370 		flag |= NV_SHORT|NV_INTEGER;
371 	if(sflag)
372 	{
373 		if(tdata.sh->mktype)
374 			flag |= NV_REF|NV_TAGGED;
375 		else if(!tdata.sh->typeinit)
376 			flag |= NV_STATIC|NV_IDENT;
377 	}
378 	if(tdata.sh->fn_depth && !tdata.pflag)
379 		flag |= NV_NOSCOPE;
380 	if(flag&NV_TYPE)
381 	{
382 		Stk_t *stkp = tdata.sh->stk;
383 		int offset = stktell(stkp);
384 		sfputr(stkp,NV_CLASS,-1);
385 		if(NV_CLASS[sizeof(NV_CLASS)-2]!='.')
386 			sfputc(stkp,'.');
387 		sfputr(stkp,tdata.prefix,0);
388 		tdata.tp = nv_open(stkptr(stkp,offset),tdata.sh->var_tree,NV_VARNAME|NV_NOARRAY|NV_NOASSIGN);
389 		stkseek(stkp,offset);
390 		if(!tdata.tp)
391 			errormsg(SH_DICT,ERROR_exit(1),"%s: unknown type",tdata.prefix);
392 		tdata.tp->nvenv = tdata.help;
393 		flag &= ~NV_TYPE;
394 	}
395 	else if(tdata.aflag==0 && ntp && ntp->tp)
396 		tdata.aflag = '-';
397 	if(!tdata.sh->mktype)
398 		tdata.help = 0;
399 	return(b_common(argv,flag,troot,&tdata));
400 }
401 
402 static void print_value(Sfio_t *iop, Namval_t *np, struct tdata *tp)
403 {
404 	char	 *name;
405 	if(nv_isnull(np))
406 		return;
407 	sfputr(iop,nv_name(np),tp->aflag=='+'?'\n':'=');
408 	if(tp->aflag=='+')
409 		return;
410 	if(nv_isarray(np) && nv_arrayptr(np))
411 	{
412 		nv_outnode(np,iop,-1,0);
413 		sfwrite(iop,")\n",2);
414 	}
415 	else
416 	{
417 		if(nv_isvtree(np))
418 			nv_onattr(np,NV_EXPORT);
419 		if(!(name = nv_getval(np)))
420 			name = Empty;
421 		if(!nv_isvtree(np))
422 			name = sh_fmtq(name);
423 		sfputr(iop,name,'\n');
424 	}
425 }
426 
427 static int     b_common(char **argv,register int flag,Dt_t *troot,struct tdata *tp)
428 {
429 	register char *name;
430 	char *last = 0;
431 	int nvflags=(flag&(NV_ARRAY|NV_NOARRAY|NV_VARNAME|NV_IDENT|NV_ASSIGN|NV_STATIC|NV_MOVE));
432 	int r=0, ref=0, comvar=(flag&NV_COMVAR),iarray=(flag&NV_IARRAY);
433 	Shell_t *shp =tp->sh;
434 	if(!shp->prefix)
435 	{
436 		if(!tp->pflag)
437 			nvflags |= NV_NOSCOPE;
438 	}
439 	else if(*shp->prefix==0)
440 		shp->prefix = 0;
441 	flag &= ~(NV_NOARRAY|NV_NOSCOPE|NV_VARNAME|NV_IDENT|NV_STATIC|NV_COMVAR|NV_IARRAY);
442 	if(argv[1])
443 	{
444 		if(flag&NV_REF)
445 		{
446 			flag &= ~NV_REF;
447 			ref=1;
448 			if(tp->aflag!='-')
449 				nvflags |= NV_NOREF;
450 		}
451 		if(tp->pflag)
452 			nvflags |= NV_NOREF;
453 		while(name = *++argv)
454 		{
455 			register unsigned newflag;
456 			register Namval_t *np;
457 			unsigned curflag;
458 			if(troot == shp->fun_tree)
459 			{
460 				/*
461 				 *functions can be exported or
462 				 * traced but not set
463 				 */
464 				flag &= ~NV_ASSIGN;
465 				if(flag&NV_LTOU)
466 				{
467 					/* Function names cannot be special builtin */
468 					if((np=nv_search(name,shp->bltin_tree,0)) && nv_isattr(np,BLT_SPC))
469 						errormsg(SH_DICT,ERROR_exit(1),e_badfun,name);
470 					np = nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE);
471 				}
472 				else  if((np=nv_search(name,troot,0)) && !is_afunction(np))
473 					np = 0;
474 				if(np && ((flag&NV_LTOU) || !nv_isnull(np) || nv_isattr(np,NV_LTOU)))
475 				{
476 					if(flag==0)
477 					{
478 						print_namval(sfstdout,np,tp->aflag=='+',tp);
479 						continue;
480 					}
481 					if(shp->subshell)
482 						sh_subfork();
483 					if(tp->aflag=='-')
484 						nv_onattr(np,flag|NV_FUNCTION);
485 					else if(tp->aflag=='+')
486 						nv_offattr(np,flag);
487 				}
488 				else
489 					r++;
490 				if(tp->help)
491 				{
492 					int offset = stktell(shp->stk);
493 					sfputr(shp->stk,shp->prefix,'.');
494 					sfputr(shp->stk,name,0);
495 					if((np=nv_search(stkptr(shp->stk,offset),troot,0)) && np->nvalue.cp)
496 						np->nvalue.rp->help = tp->help;
497 					stkseek(shp->stk,offset);
498 				}
499 				continue;
500 			}
501 			/* tracked alias */
502 			if(troot==shp->track_tree && tp->aflag=='-')
503 			{
504 				np = nv_search(name,troot,NV_ADD);
505 				path_alias(np,path_absolute(nv_name(np),NIL(Pathcomp_t*)));
506 				continue;
507 			}
508 			np = nv_open(name,troot,nvflags|NV_ARRAY);
509 			if(tp->pflag)
510 			{
511 				nv_attribute(np,sfstdout,tp->prefix,1);
512 				print_value(sfstdout,np,tp);
513 				continue;
514 			}
515 			if(flag==NV_ASSIGN && !ref && tp->aflag!='-' && !strchr(name,'='))
516 			{
517 				if(troot!=shp->var_tree && (nv_isnull(np) || !print_namval(sfstdout,np,0,tp)))
518 				{
519 					sfprintf(sfstderr,sh_translate(e_noalias),name);
520 					r++;
521 				}
522 				if(!comvar && !iarray)
523 					continue;
524 			}
525 			if(troot==shp->var_tree && ((tp->tp && !nv_isarray(np)) || !shp->st.real_fun && (nvflags&NV_STATIC)) && !strchr(name,'=') && !(shp->envlist  && nv_onlist(shp->envlist,name)))
526 				_nv_unset(np,0);
527 			if(troot==shp->var_tree)
528 			{
529 				if(iarray)
530 				{
531 					if(tp->tname)
532 						nv_atypeindex(np,tp->tname+1);
533 					else if(nv_isnull(np))
534 						nv_onattr(np,NV_ARRAY|(comvar?NV_NOFREE:0));
535 					else
536 						nv_putsub(np, (char*)0, 0);
537 				}
538 				else if(nvflags&NV_ARRAY)
539 				{
540 					if(comvar)
541 					{
542 						_nv_unset(np,NV_RDONLY);
543 						nv_onattr(np,NV_NOFREE);
544 					}
545 					nv_setarray(np,nv_associative);
546 				}
547 				else if(comvar && !nv_rename(np,flag|NV_COMVAR))
548 					nv_setvtree(np);
549 			}
550 			if(flag&NV_MOVE)
551 			{
552 				nv_rename(np, flag);
553 				nv_close(np);
554 				continue;
555 			}
556 			if(tp->tp)
557 			{
558 				nv_settype(np,tp->tp,tp->aflag=='-'?0:NV_APPEND);
559 				flag = (np->nvflag&NV_NOCHANGE);
560 			}
561 			curflag = np->nvflag;
562 			flag &= ~NV_ASSIGN;
563 			if(last=strchr(name,'='))
564 				*last = 0;
565 			if (tp->aflag == '-')
566 			{
567 				if((flag&NV_EXPORT) && strchr(name,'.'))
568 					errormsg(SH_DICT,ERROR_exit(1),e_badexport,name);
569 #if SHOPT_BSH
570 				if(flag&NV_EXPORT)
571 					nv_offattr(np,NV_IMPORT);
572 #endif /* SHOPT_BSH */
573 				newflag = curflag;
574 				if(flag&~NV_NOCHANGE)
575 					newflag &= NV_NOCHANGE;
576 				newflag |= flag;
577 				if (flag & (NV_LJUST|NV_RJUST))
578 				{
579 					if(!(flag&NV_RJUST))
580 						newflag &= ~NV_RJUST;
581 
582 					else if(!(flag&NV_LJUST))
583 						newflag &= ~NV_LJUST;
584 				}
585 				if(!(flag&NV_INTEGER))
586 				{
587 					if (flag & NV_UTOL)
588 						newflag &= ~NV_LTOU;
589 					else if (flag & NV_LTOU)
590 						newflag &= ~NV_UTOL;
591 				}
592 			}
593 			else
594 			{
595 				if((flag&NV_RDONLY) && (curflag&NV_RDONLY))
596 					errormsg(SH_DICT,ERROR_exit(1),e_readonly,nv_name(np));
597 				newflag = curflag & ~flag;
598 			}
599 			if (tp->aflag && (tp->argnum>0 || (curflag!=newflag)))
600 			{
601 				if(shp->subshell)
602 					sh_assignok(np,1);
603 				if(troot!=shp->var_tree)
604 					nv_setattr(np,newflag&~NV_ASSIGN);
605 				else
606 				{
607 					char *oldname=0;
608 					int len=strlen(name);
609 					if(tp->argnum==1 && newflag==NV_INTEGER && nv_isattr(np,NV_INTEGER))
610 						tp->argnum = 10;
611 					/* use reference name for export */
612 					if((newflag^curflag)&NV_EXPORT)
613 					{
614 						oldname = np->nvname;
615 						np->nvname = name;
616 					}
617 					if(np->nvfun && !nv_isarray(np) && name[len-1]=='.')
618 						newflag |= NV_NODISC;
619 					nv_newattr (np, newflag&~NV_ASSIGN,tp->argnum);
620 					if(oldname)
621 						np->nvname = oldname;
622 				}
623 			}
624 			if(tp->help && !nv_isattr(np,NV_MINIMAL|NV_EXPORT))
625 			{
626 				np->nvenv = tp->help;
627 				nv_onattr(np,NV_EXPORT);
628 			}
629 			if(last)
630 				*last = '=';
631 			/* set or unset references */
632 			if(ref)
633 			{
634 				if(tp->aflag=='-')
635 				{
636 					Dt_t *hp=0;
637 					if(nv_isattr(np,NV_PARAM) && shp->st.prevst)
638 					{
639 						if(!(hp=(Dt_t*)shp->st.prevst->save_tree))
640 							hp = dtvnext(shp->var_tree);
641 					}
642 					if(tp->sh->mktype)
643 						nv_onattr(np,NV_REF|NV_FUNCT);
644 					else
645 						nv_setref(np,hp,NV_VARNAME);
646 				}
647 				else
648 					nv_unref(np);
649 			}
650 			nv_close(np);
651 		}
652 	}
653 	else if(!tp->sh->envlist)
654 	{
655 		if(shp->prefix)
656 			errormsg(SH_DICT,2, "%s: compound assignment requires sub-variable name",shp->prefix);
657 		if(tp->aflag)
658 		{
659 			if(troot==shp->fun_tree)
660 			{
661 				flag |= NV_FUNCTION;
662 				tp->prefix = 0;
663 			}
664 			else if(troot==shp->var_tree)
665 			{
666 				flag |= (nvflags&NV_ARRAY);
667 				if(flag&NV_IARRAY)
668 					flag |= NV_ARRAY;
669 			}
670 			print_scan(sfstdout,flag,troot,tp->aflag=='+',tp);
671 		}
672 		else if(troot==shp->alias_tree)
673 			print_scan(sfstdout,0,troot,0,tp);
674 		else
675 			print_all(sfstdout,troot,tp);
676 		sfsync(sfstdout);
677 	}
678 	return(r);
679 }
680 
681 typedef void (*Iptr_t)(int,void*);
682 typedef int (*Fptr_t)(int, char*[], void*);
683 
684 #define GROWLIB	4
685 
686 static void		**liblist;
687 static unsigned short	*libattr;
688 static int		nlib;
689 static int		maxlib;
690 
691 /*
692  * This allows external routines to load from the same library */
693 void **sh_getliblist(void)
694 {
695 	return(liblist);
696 }
697 
698 /*
699  * add library to loaded list
700  * call (*lib_init)() on first load if defined
701  * always move to head of search list
702  * return: 0: already loaded 1: first load
703  */
704 #if SHOPT_DYNAMIC
705 int sh_addlib(void* library)
706 {
707 	register int	n;
708 	register int	r;
709 	Iptr_t		initfn;
710 	Shbltin_t	*sp = &sh.bltindata;
711 
712 	sp->nosfio = 0;
713 	for (n = r = 0; n < nlib; n++)
714 	{
715 		if (r)
716 		{
717 			liblist[n-1] = liblist[n];
718 			libattr[n-1] = libattr[n];
719 		}
720 		else if (liblist[n] == library)
721 			r++;
722 	}
723 	if (r)
724 		nlib--;
725 	else if ((initfn = (Iptr_t)dlllook(library, "lib_init")))
726 		(*initfn)(0,sp);
727 	if (nlib >= maxlib)
728 	{
729 		maxlib += GROWLIB;
730 		if (liblist)
731 		{
732 			liblist = (void**)realloc((void*)liblist, (maxlib+1)*sizeof(void**));
733 			libattr = (unsigned short*)realloc((void*)liblist, (maxlib+1)*sizeof(unsigned short*));
734 		}
735 		else
736 		{
737 			liblist = (void**)malloc((maxlib+1)*sizeof(void**));
738 			libattr = (unsigned short*)malloc((maxlib+1)*sizeof(unsigned short*));
739 		}
740 	}
741 	libattr[nlib] = (sp->nosfio?BLT_NOSFIO:0);
742 	liblist[nlib++] = library;
743 	liblist[nlib] = 0;
744 	return !r;
745 }
746 #else
747 int sh_addlib(void* library)
748 {
749 	return 0;
750 }
751 #endif /* SHOPT_DYNAMIC */
752 
753 /*
754  * add change or list built-ins
755  * adding builtins requires dlopen() interface
756  */
757 int	b_builtin(int argc,char *argv[],void *extra)
758 {
759 	register char *arg=0, *name;
760 	register int n, r=0, flag=0;
761 	register Namval_t *np;
762 	long dlete=0;
763 	struct tdata tdata;
764 	Fptr_t addr;
765 	Stk_t	*stkp;
766 	void *library=0;
767 	char *errmsg;
768 	NOT_USED(argc);
769 	memset(&tdata,0,sizeof(tdata));
770 	tdata.sh = ((Shbltin_t*)extra)->shp;
771 	stkp = tdata.sh->stk;
772 	while (n = optget(argv,sh_optbuiltin)) switch (n)
773 	{
774 	    case 's':
775 		flag = BLT_SPC;
776 		break;
777 	    case 'd':
778 		dlete=1;
779 		break;
780 	    case 'f':
781 #if SHOPT_DYNAMIC
782 		arg = opt_info.arg;
783 #else
784 		errormsg(SH_DICT,2, "adding built-ins not supported");
785 		error_info.errors++;
786 #endif /* SHOPT_DYNAMIC */
787 		break;
788 	    case ':':
789 		errormsg(SH_DICT,2, "%s", opt_info.arg);
790 		break;
791 	    case '?':
792 		errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
793 		break;
794 	}
795 	argv += opt_info.index;
796 	if(error_info.errors)
797 		errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*)));
798 	if(arg || *argv)
799 	{
800 		if(sh_isoption(SH_RESTRICTED))
801 			errormsg(SH_DICT,ERROR_exit(1),e_restricted,argv[-opt_info.index]);
802 		if(sh_isoption(SH_PFSH))
803 			errormsg(SH_DICT,ERROR_exit(1),e_pfsh,argv[-opt_info.index]);
804 		if(tdata.sh->subshell)
805 			sh_subfork();
806 	}
807 #if SHOPT_DYNAMIC
808 	if(arg)
809 	{
810 #if (_AST_VERSION>=20040404)
811 		if(!(library = dllplug(SH_ID,arg,NIL(char*),RTLD_LAZY,NIL(char*),0)))
812 #else
813 		if(!(library = dllfind(arg,NIL(char*),RTLD_LAZY,NIL(char*),0)))
814 #endif
815 		{
816 			errormsg(SH_DICT,ERROR_exit(0),"%s: %s",arg,dlerror());
817 			return(1);
818 		}
819 		sh_addlib(library);
820 	}
821 	else
822 #endif /* SHOPT_DYNAMIC */
823 	if(*argv==0 && !dlete)
824 	{
825 		print_scan(sfstdout, flag, tdata.sh->bltin_tree, 1, &tdata);
826 		return(0);
827 	}
828 	r = 0;
829 	flag = stktell(stkp);
830 	while(arg = *argv)
831 	{
832 		name = path_basename(arg);
833 		sfwrite(stkp,"b_",2);
834 		sfputr(stkp,name,0);
835 		errmsg = 0;
836 		addr = 0;
837 		for(n=(nlib?nlib:dlete); --n>=0;)
838 		{
839 			/* (char*) added for some sgi-mips compilers */
840 #if SHOPT_DYNAMIC
841 			if(dlete || (addr = (Fptr_t)dlllook(liblist[n],stkptr(stkp,flag))))
842 #else
843 			if(dlete)
844 #endif /* SHOPT_DYNAMIC */
845 			{
846 				if(np = sh_addbuiltin(arg, addr,pointerof(dlete)))
847 				{
848 					if(dlete || nv_isattr(np,BLT_SPC))
849 						errmsg = "restricted name";
850 					else
851 						nv_onattr(np,libattr[n]);
852 				}
853 				break;
854 			}
855 		}
856 		if(!dlete && !addr)
857 		{
858 			np = sh_addbuiltin(arg, 0 ,0);
859 			if(np && nv_isattr(np,BLT_SPC))
860 				errmsg = "restricted name";
861 			else if(!np)
862 				errmsg = "not found";
863 		}
864 		if(errmsg)
865 		{
866 			errormsg(SH_DICT,ERROR_exit(0),"%s: %s",*argv,errmsg);
867 			r = 1;
868 		}
869 		stkseek(stkp,flag);
870 		argv++;
871 	}
872 	return(r);
873 }
874 
875 int    b_set(int argc,register char *argv[],void *extra)
876 {
877 	struct tdata tdata;
878 	memset(&tdata,0,sizeof(tdata));
879 	tdata.sh = ((Shbltin_t*)extra)->shp;
880 	tdata.prefix=0;
881 	if(argv[1])
882 	{
883 		if(sh_argopts(argc,argv,tdata.sh) < 0)
884 			return(2);
885 		if(sh_isoption(SH_VERBOSE))
886 			sh_onstate(SH_VERBOSE);
887 		else
888 			sh_offstate(SH_VERBOSE);
889 		if(sh_isoption(SH_MONITOR))
890 			sh_onstate(SH_MONITOR);
891 		else
892 			sh_offstate(SH_MONITOR);
893 	}
894 	else
895 		/*scan name chain and print*/
896 		print_scan(sfstdout,0,tdata.sh->var_tree,0,&tdata);
897 	return(0);
898 }
899 
900 /*
901  * The removing of Shell variable names, aliases, and functions
902  * is performed here.
903  * Unset functions with unset -f
904  * Non-existent items being deleted give non-zero exit status
905  */
906 
907 int    b_unalias(int argc,register char *argv[],void *extra)
908 {
909 	Shell_t *shp = ((Shbltin_t*)extra)->shp;
910 	return(b_unall(argc,argv,shp->alias_tree,shp));
911 }
912 
913 int    b_unset(int argc,register char *argv[],void *extra)
914 {
915 	Shell_t *shp = ((Shbltin_t*)extra)->shp;
916 	return(b_unall(argc,argv,shp->var_tree,shp));
917 }
918 
919 static int b_unall(int argc, char **argv, register Dt_t *troot, Shell_t* shp)
920 {
921 	register Namval_t *np;
922 	register const char *name;
923 	register int r;
924 	Dt_t	*dp;
925 	int nflag=0,all=0,isfun;
926 	NOT_USED(argc);
927 	if(troot==shp->alias_tree)
928 	{
929 		name = sh_optunalias;
930 		if(shp->subshell)
931 			troot = sh_subaliastree(0);
932 	}
933 	else
934 		name = sh_optunset;
935 	while(r = optget(argv,name)) switch(r)
936 	{
937 		case 'f':
938 			troot = sh_subfuntree(1);
939 			break;
940 		case 'a':
941 			all=1;
942 			break;
943 		case 'n':
944 			nflag = NV_NOREF;
945 		case 'v':
946 			troot = shp->var_tree;
947 			break;
948 		case ':':
949 			errormsg(SH_DICT,2, "%s", opt_info.arg);
950 			break;
951 		case '?':
952 			errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
953 			return(2);
954 	}
955 	argv += opt_info.index;
956 	if(error_info.errors || (*argv==0 &&!all))
957 		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
958 	if(!troot)
959 		return(1);
960 	r = 0;
961 	if(troot==shp->var_tree)
962 		nflag |= NV_VARNAME;
963 	else
964 		nflag = NV_NOSCOPE;
965 	if(all)
966 		dtclear(troot);
967 	else while(name = *argv++)
968 	{
969 		if(np=nv_open(name,troot,NV_NOADD|NV_NOFAIL|nflag))
970 		{
971 			if(is_abuiltin(np))
972 			{
973 				r = 1;
974 				continue;
975 			}
976 			isfun = is_afunction(np);
977 			if(troot==shp->var_tree)
978 			{
979 				if(nv_isarray(np) && name[strlen(name)-1]==']' && !nv_getsub(np))
980 				{
981 					r=1;
982 					continue;
983 				}
984 
985 				if(shp->subshell)
986 					np=sh_assignok(np,0);
987 			}
988 			nv_unset(np);
989 			nv_close(np);
990 			if(troot==shp->var_tree && shp->st.real_fun && (dp=shp->var_tree->walk) && dp==shp->st.real_fun->sdict)
991 				nv_delete(np,dp,NV_NOFREE);
992 			else if(isfun)
993 				nv_delete(np,troot,NV_NOFREE);
994 		}
995 		else
996 			r = 1;
997 	}
998 	return(r);
999 }
1000 
1001 /*
1002  * print out the name and value of a name-value pair <np>
1003  */
1004 
1005 static int print_namval(Sfio_t *file,register Namval_t *np,register int flag, struct tdata *tp)
1006 {
1007 	register char *cp;
1008 	sh_sigcheck();
1009 	if(flag)
1010 		flag = '\n';
1011 	if(nv_isattr(np,NV_NOPRINT|NV_INTEGER)==NV_NOPRINT)
1012 	{
1013 		if(is_abuiltin(np))
1014 			sfputr(file,nv_name(np),'\n');
1015 		return(0);
1016 	}
1017 	if(tp->prefix)
1018 	{
1019 		if(*tp->prefix=='t')
1020 			nv_attribute(np,tp->outfile,tp->prefix,tp->aflag);
1021 		else
1022 			sfputr(file,tp->prefix,' ');
1023 	}
1024 	if(is_afunction(np))
1025 	{
1026 		Sfio_t *iop=0;
1027 		char *fname=0;
1028 		if(!flag && !np->nvalue.ip)
1029 			sfputr(file,"typeset -fu",' ');
1030 		else if(!flag && !nv_isattr(np,NV_FPOSIX))
1031 			sfputr(file,"function",' ');
1032 		sfputr(file,nv_name(np),-1);
1033 		if(nv_isattr(np,NV_FPOSIX))
1034 			sfwrite(file,"()",2);
1035 		if(np->nvalue.ip && np->nvalue.rp->hoffset>=0)
1036 			fname = np->nvalue.rp->fname;
1037 		else
1038 			flag = '\n';
1039 		if(flag)
1040 		{
1041 			if(tp->pflag && np->nvalue.ip && np->nvalue.rp->hoffset>=0)
1042 				sfprintf(file," #line %d %s\n",np->nvalue.rp->lineno,fname?sh_fmtq(fname):"");
1043 			else
1044 				sfputc(file, '\n');
1045 		}
1046 		else
1047 		{
1048 			if(nv_isattr(np,NV_FTMP))
1049 			{
1050 				fname = 0;
1051 				iop = tp->sh->heredocs;
1052 			}
1053 			else if(fname)
1054 				iop = sfopen(iop,fname,"r");
1055 			else if(tp->sh->hist_ptr)
1056 				iop = (tp->sh->hist_ptr)->histfp;
1057 			if(iop && sfseek(iop,(Sfoff_t)np->nvalue.rp->hoffset,SEEK_SET)>=0)
1058 				sfmove(iop,file, nv_size(np), -1);
1059 			else
1060 				flag = '\n';
1061 			if(fname)
1062 				sfclose(iop);
1063 		}
1064 		return(nv_size(np)+1);
1065 	}
1066 	if(nv_arrayptr(np))
1067 	{
1068 		print_value(file,np,tp);
1069 		return(0);
1070 	}
1071 	if(nv_isvtree(np))
1072 		nv_onattr(np,NV_EXPORT);
1073 	if(cp=nv_getval(np))
1074 	{
1075 		sfputr(file,nv_name(np),-1);
1076 		if(!flag)
1077 			flag = '=';
1078 		sfputc(file,flag);
1079 		if(flag != '\n')
1080 		{
1081 			if(nv_isref(np) && nv_refsub(np))
1082 			{
1083 				sfputr(file,sh_fmtq(cp),-1);
1084 				sfprintf(file,"[%s]\n", sh_fmtq(nv_refsub(np)));
1085 			}
1086 			else
1087 #if SHOPT_TYPEDEF
1088 				sfputr(file,nv_isvtree(np)?cp:sh_fmtq(cp),'\n');
1089 #else
1090 				sfputr(file,sh_fmtq(cp),'\n');
1091 #endif /* SHOPT_TYPEDEF */
1092 		}
1093 		return(1);
1094 	}
1095 	else if(tp->scanmask && tp->scanroot==tp->sh->var_tree)
1096 		sfputr(file,nv_name(np),'\n');
1097 	return(0);
1098 }
1099 
1100 /*
1101  * print attributes at all nodes
1102  */
1103 static void	print_all(Sfio_t *file,Dt_t *root, struct tdata *tp)
1104 {
1105 	tp->outfile = file;
1106 	nv_scan(root, print_attribute, (void*)tp, 0, 0);
1107 }
1108 
1109 /*
1110  * print the attributes of name value pair give by <np>
1111  */
1112 static void	print_attribute(register Namval_t *np,void *data)
1113 {
1114 	register struct tdata *dp = (struct tdata*)data;
1115 	nv_attribute(np,dp->outfile,dp->prefix,dp->aflag);
1116 }
1117 
1118 /*
1119  * print the nodes in tree <root> which have attributes <flag> set
1120  * of <option> is non-zero, no subscript or value is printed.
1121  */
1122 
1123 static void print_scan(Sfio_t *file, int flag, Dt_t *root, int option,struct tdata *tp)
1124 {
1125 	register char **argv;
1126 	register Namval_t *np;
1127 	register int namec;
1128 	Namval_t *onp = 0;
1129 	tp->sh->last_table=0;
1130 	flag &= ~NV_ASSIGN;
1131 	tp->scanmask = flag&~NV_NOSCOPE;
1132 	tp->scanroot = root;
1133 	tp->outfile = file;
1134 #if SHOPT_TYPEDEF
1135 	if(!tp->prefix && tp->tp)
1136 		tp->prefix = nv_name(tp->tp);
1137 #endif /* SHOPT_TYPEDEF */
1138 	if(flag&NV_INTEGER)
1139 		tp->scanmask |= (NV_DOUBLE|NV_EXPNOTE);
1140 	namec = nv_scan(root,nullscan,(void*)tp,tp->scanmask,flag);
1141 	argv = tp->argnam  = (char**)stkalloc(tp->sh->stk,(namec+1)*sizeof(char*));
1142 	namec = nv_scan(root, pushname, (void*)tp, tp->scanmask, flag&~NV_IARRAY);
1143 	if(mbcoll())
1144 		strsort(argv,namec,strcoll);
1145 	while(namec--)
1146 	{
1147 		if((np=nv_search(*argv++,root,0)) && np!=onp && (!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE)))
1148 		{
1149 			onp = np;
1150 			if(flag&NV_ARRAY)
1151 			{
1152 				if(nv_aindex(np)>=0)
1153 				{
1154 					if(!(flag&NV_IARRAY))
1155 						continue;
1156 				}
1157 				else if((flag&NV_IARRAY))
1158 					continue;
1159 
1160 			}
1161 			print_namval(file,np,option,tp);
1162 		}
1163 	}
1164 }
1165 
1166 /*
1167  * add the name of the node to the argument list argnam
1168  */
1169 
1170 static void pushname(Namval_t *np,void *data)
1171 {
1172 	struct tdata *tp = (struct tdata*)data;
1173 	*tp->argnam++ = nv_name(np);
1174 }
1175 
1176