xref: /titanic_52/usr/src/contrib/ast/src/cmd/ksh93/bltins/typeset.c (revision 906afcb89d0412cc073b95c2d701a804a8cdb62c)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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 	const char	*wctname;
50 	Sfio_t  	*outfile;
51 	char    	*prefix;
52 	char    	*tname;
53 	char		*help;
54 	short     	aflag;
55 	short     	pflag;
56 	int     	argnum;
57 	int     	scanmask;
58 	Dt_t 		*scanroot;
59 	char    	**argnam;
60 	int		indent;
61 	int		noref;
62 };
63 
64 
65 static int	print_namval(Sfio_t*, Namval_t*, int, struct tdata*);
66 static void	print_attribute(Namval_t*,void*);
67 static void	print_all(Sfio_t*, Dt_t*, struct tdata*);
68 static void	print_scan(Sfio_t*, int, Dt_t*, int, struct tdata*);
69 static int	unall(int, char**, Dt_t*, Shell_t*);
70 static int	setall(char**, int, Dt_t*, struct tdata*);
71 static void	pushname(Namval_t*,void*);
72 static void(*nullscan)(Namval_t*,void*);
73 
74 static Namval_t *load_class(const char *name)
75 {
76 	errormsg(SH_DICT,ERROR_exit(1),"%s: type not loadable",name);
77 	return(0);
78 }
79 
80 /*
81  * Note export and readonly are the same
82  */
83 #if 0
84     /* for the dictionary generator */
85     int    b_export(int argc,char *argv[],Shbltin_t *context){}
86 #endif
87 int    b_readonly(int argc,char *argv[],Shbltin_t *context)
88 {
89 	register int flag;
90 	char *command = argv[0];
91 	struct tdata tdata;
92 	NOT_USED(argc);
93 	memset((void*)&tdata,0,sizeof(tdata));
94 	tdata.sh = context->shp;
95 	tdata.aflag = '-';
96 	while((flag = optget(argv,*command=='e'?sh_optexport:sh_optreadonly))) switch(flag)
97 	{
98 		case 'p':
99 			tdata.prefix = command;
100 			break;
101 		case ':':
102 			errormsg(SH_DICT,2, "%s", opt_info.arg);
103 			break;
104 		case '?':
105 			errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
106 			return(2);
107 	}
108 	if(error_info.errors)
109 		errormsg(SH_DICT,ERROR_usage(2),optusage(NIL(char*)));
110 	argv += (opt_info.index-1);
111 	if(*command=='r')
112 		flag = (NV_ASSIGN|NV_RDONLY|NV_VARNAME);
113 #ifdef _ENV_H
114 	else if(!argv[1])
115 	{
116 		char *cp,**env=env_get(tdata.sh->env);
117 		while(cp = *env++)
118 		{
119 			if(tdata.prefix)
120 				sfputr(sfstdout,tdata.prefix,' ');
121 			sfprintf(sfstdout,"%s\n",sh_fmtq(cp));
122 		}
123 		return(0);
124 	}
125 #endif
126 	else
127 	{
128 		flag = (NV_ASSIGN|NV_EXPORT|NV_IDENT);
129 		if(!tdata.sh->prefix)
130 			tdata.sh->prefix = "";
131 	}
132 	return(setall(argv,flag,tdata.sh->var_tree, &tdata));
133 }
134 
135 
136 int    b_alias(int argc,register char *argv[],Shbltin_t *context)
137 {
138 	register unsigned flag = NV_NOARRAY|NV_NOSCOPE|NV_ASSIGN;
139 	register Dt_t *troot;
140 	register int n;
141 	struct tdata tdata;
142 	NOT_USED(argc);
143 	memset((void*)&tdata,0,sizeof(tdata));
144 	tdata.sh = context->shp;
145 	troot = tdata.sh->alias_tree;
146 	if(*argv[0]=='h')
147 		flag = NV_TAGGED;
148 	if(argv[1])
149 	{
150 		opt_info.offset = 0;
151 		opt_info.index = 1;
152 		*opt_info.option = 0;
153 		tdata.argnum = 0;
154 		tdata.aflag = *argv[1];
155 		while((n = optget(argv,sh_optalias))) switch(n)
156 		{
157 		    case 'p':
158 			tdata.prefix = argv[0];
159 			break;
160 		    case 't':
161 			flag |= NV_TAGGED;
162 			break;
163 		    case 'x':
164 			flag |= NV_EXPORT;
165 			break;
166 		    case ':':
167 			errormsg(SH_DICT,2, "%s", opt_info.arg);
168 			break;
169 		    case '?':
170 			errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
171 			return(2);
172 		}
173 		if(error_info.errors)
174 			errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
175 		argv += (opt_info.index-1);
176 		if(flag&NV_TAGGED)
177 		{
178 			/* hacks to handle hash -r | -- */
179 			if(argv[1] && argv[1][0]=='-')
180 			{
181 				if(argv[1][1]=='r' && argv[1][2]==0)
182 				{
183 					Namval_t *np = nv_search((char*)PATHNOD,tdata.sh->var_tree,HASH_BUCKET);
184 					nv_putval(np,nv_getval(np),NV_RDONLY);
185 					argv++;
186 					if(!argv[1])
187 						return(0);
188 				}
189 				if(argv[1][0]=='-')
190 				{
191 					if(argv[1][1]=='-' && argv[1][2]==0)
192 						argv++;
193 					else
194 						errormsg(SH_DICT, ERROR_exit(1), e_option, argv[1]);
195 		}
196 			}
197 			troot = tdata.sh->track_tree;
198 		}
199 	}
200 	return(setall(argv,flag,troot,&tdata));
201 }
202 
203 
204 #if 0
205     /* for the dictionary generator */
206     int    b_local(int argc,char *argv[],Shbltin_t *context){}
207 #endif
208 int    b_typeset(int argc,register char *argv[],Shbltin_t *context)
209 {
210 	register int	n, flag = NV_VARNAME|NV_ASSIGN;
211 	struct tdata	tdata;
212 	const char	*optstring = sh_opttypeset;
213 	Namdecl_t 	*ntp = (Namdecl_t*)context->ptr;
214 	Dt_t		*troot;
215 	int		isfloat=0, shortint=0, sflag=0;
216 	NOT_USED(argc);
217 	memset((void*)&tdata,0,sizeof(tdata));
218 	tdata.sh = context->shp;
219 	if(ntp)
220 	{
221 		tdata.tp = ntp->tp;
222 		opt_info.disc = (Optdisc_t*)ntp->optinfof;
223 		optstring = ntp->optstring;
224 	}
225 	troot = tdata.sh->var_tree;
226 	while((n = optget(argv,optstring)))
227 	{
228 		if(tdata.aflag==0)
229 			tdata.aflag = *opt_info.option;
230 		switch(n)
231 		{
232 			case 'a':
233 				flag |= NV_IARRAY;
234 				if(opt_info.arg && *opt_info.arg!='[')
235 				{
236 					opt_info.index--;
237 					goto endargs;
238 				}
239 				tdata.tname = opt_info.arg;
240 				break;
241 			case 'A':
242 				flag |= NV_ARRAY;
243 				break;
244 			case 'C':
245 				flag |= NV_COMVAR;
246 				break;
247 			case 'E':
248 				/* The following is for ksh88 compatibility */
249 				if(opt_info.offset && !strchr(argv[opt_info.index],'E'))
250 				{
251 					tdata.argnum = (int)opt_info.num;
252 					break;
253 				}
254 				/* FALLTHROUGH */
255 			case 'F':
256 			case 'X':
257 				if(!opt_info.arg || (tdata.argnum = opt_info.num) <0)
258 					tdata.argnum = (n=='X'?2*sizeof(Sfdouble_t):10);
259 				isfloat = 1;
260 				if(n=='E')
261 				{
262 					flag &= ~NV_HEXFLOAT;
263 					flag |= NV_EXPNOTE;
264 				}
265 				else if(n=='X')
266 				{
267 					flag &= ~NV_EXPNOTE;
268 					flag |= NV_HEXFLOAT;
269 				}
270 				break;
271 			case 'b':
272 				flag |= NV_BINARY;
273 				break;
274 			case 'm':
275 				flag |= NV_MOVE;
276 				break;
277 			case 'n':
278 				flag &= ~NV_VARNAME;
279 				flag |= (NV_REF|NV_IDENT);
280 				break;
281 			case 'H':
282 				flag |= NV_HOST;
283 				break;
284 			case 'T':
285 				flag |= NV_TYPE;
286 				tdata.prefix = opt_info.arg;
287 				break;
288 			case 'L': case 'Z': case 'R':
289 				if(tdata.argnum==0)
290 					tdata.argnum = (int)opt_info.num;
291 				if(tdata.argnum < 0)
292 					errormsg(SH_DICT,ERROR_exit(1), e_badfield, tdata.argnum);
293 				if(n=='Z')
294 					flag |= NV_ZFILL;
295 				else
296 				{
297 					flag &= ~(NV_LJUST|NV_RJUST);
298 					flag |= (n=='L'?NV_LJUST:NV_RJUST);
299 				}
300 				break;
301 			case 'M':
302 				if((tdata.wctname = opt_info.arg) && !nv_mapchar((Namval_t*)0,tdata.wctname))
303 					errormsg(SH_DICT, ERROR_exit(1),e_unknownmap, tdata.wctname);
304 				if(tdata.wctname && strcmp(tdata.wctname,e_tolower)==0)
305 					flag |= NV_UTOL;
306 				else
307 					flag |= NV_LTOU;
308 				if(!tdata.wctname)
309 					flag |= NV_UTOL;
310 				break;
311 			case 'f':
312 				flag &= ~(NV_VARNAME|NV_ASSIGN);
313 				troot = tdata.sh->fun_tree;
314 				break;
315 			case 'i':
316 				if(!opt_info.arg || (tdata.argnum = opt_info.num) <0)
317 					tdata.argnum = 10;
318 				flag |= NV_INTEGER;
319 				break;
320 			case 'l':
321 				tdata.wctname = e_tolower;
322 				flag |= NV_UTOL;
323 				break;
324 			case 'p':
325 				tdata.prefix = argv[0];
326 				tdata.pflag = 1;
327 				flag &= ~NV_ASSIGN;
328 				break;
329 			case 'r':
330 				flag |= NV_RDONLY;
331 				break;
332 #ifdef SHOPT_TYPEDEF
333 			case 'S':
334 				sflag=1;
335 				break;
336 			case 'h':
337 				tdata.help = opt_info.arg;
338 				break;
339 #endif /*SHOPT_TYPEDEF*/
340 			case 's':
341 				shortint=1;
342 				break;
343 			case 't':
344 				flag |= NV_TAGGED;
345 				break;
346 			case 'u':
347 				tdata.wctname = e_toupper;
348 				flag |= NV_LTOU;
349 				break;
350 			case 'x':
351 				flag &= ~NV_VARNAME;
352 				flag |= (NV_EXPORT|NV_IDENT);
353 				break;
354 			case ':':
355 				errormsg(SH_DICT,2, "%s", opt_info.arg);
356 				break;
357 			case '?':
358 				errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
359 				opt_info.disc = 0;
360 				return(2);
361 		}
362 	}
363 endargs:
364 	argv += opt_info.index;
365 	opt_info.disc = 0;
366 	/* handle argument of + and - specially */
367 	if(*argv && argv[0][1]==0 && (*argv[0]=='+' || *argv[0]=='-'))
368 		tdata.aflag = *argv[0];
369 	else
370 		argv--;
371 	if((flag&NV_ZFILL) && !(flag&NV_LJUST))
372 		flag |= NV_RJUST;
373 	if((flag&NV_INTEGER) && (flag&(NV_LJUST|NV_RJUST|NV_ZFILL)))
374 		error_info.errors++;
375 	if((flag&NV_BINARY) && (flag&(NV_LJUST|NV_UTOL|NV_LTOU)))
376 		error_info.errors++;
377 	if((flag&NV_MOVE) && (flag&~(NV_MOVE|NV_VARNAME|NV_ASSIGN)))
378 		error_info.errors++;
379 	if((flag&NV_REF) && (flag&~(NV_REF|NV_IDENT|NV_ASSIGN)))
380 		error_info.errors++;
381 	if((flag&NV_TYPE) && (flag&~(NV_TYPE|NV_VARNAME|NV_ASSIGN)))
382 		error_info.errors++;
383 	if(troot==tdata.sh->fun_tree && ((isfloat || flag&~(NV_FUNCT|NV_TAGGED|NV_EXPORT|NV_LTOU))))
384 		error_info.errors++;
385 	if(sflag && troot==tdata.sh->fun_tree)
386 	{
387 		/* static function */
388 		sflag = 0;
389 		flag |= NV_STATICF;
390 	}
391 	if(error_info.errors)
392 		errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*)));
393 	if(sizeof(char*)<8 && tdata.argnum > SHRT_MAX)
394 		errormsg(SH_DICT,ERROR_exit(2),"option argument cannot be greater than %d",SHRT_MAX);
395 	if(isfloat)
396 		flag |= NV_DOUBLE;
397 	if(shortint)
398 	{
399 		flag &= ~NV_LONG;
400 		flag |= NV_SHORT|NV_INTEGER;
401 	}
402 	if(sflag)
403 	{
404 		if(tdata.sh->mktype)
405 			flag |= NV_REF|NV_TAGGED;
406 		else if(!tdata.sh->typeinit)
407 			flag |= NV_STATIC|NV_IDENT;
408 	}
409 	if(tdata.sh->fn_depth && !tdata.pflag)
410 		flag |= NV_NOSCOPE;
411 	if(tdata.help)
412 		tdata.help = strdup(tdata.help);
413 	if(flag&NV_TYPE)
414 	{
415 		Stk_t *stkp = tdata.sh->stk;
416 		int off=0,offset = stktell(stkp);
417 		if(!tdata.prefix)
418 			return(sh_outtype(tdata.sh,sfstdout));
419 		sfputr(stkp,NV_CLASS,-1);
420 #if SHOPT_NAMESPACE
421 		if(tdata.sh->namespace)
422 		{
423 			off = stktell(stkp)+1;
424 			sfputr(stkp,nv_name(tdata.sh->namespace),'.');
425 		}
426 		else
427 #endif /* SHOPT_NAMESPACE */
428 		if(NV_CLASS[sizeof(NV_CLASS)-2]!='.')
429 			sfputc(stkp,'.');
430 		sfputr(stkp,tdata.prefix,0);
431 		tdata.tp = nv_open(stkptr(stkp,offset),tdata.sh->var_tree,NV_VARNAME|NV_NOARRAY|NV_NOASSIGN);
432 #if SHOPT_NAMESPACE
433 		if(!tdata.tp && off)
434 		{
435 			*stkptr(stkp,off)=0;
436 			tdata.tp = nv_open(stkptr(stkp,offset),tdata.sh->var_tree,NV_VARNAME|NV_NOARRAY|NV_NOASSIGN);
437 		}
438 #endif /* SHOPT_NAMESPACE */
439 		stkseek(stkp,offset);
440 		if(!tdata.tp)
441 			errormsg(SH_DICT,ERROR_exit(1),"%s: unknown type",tdata.prefix);
442 		else if(nv_isnull(tdata.tp))
443 			nv_newtype(tdata.tp);
444 		tdata.tp->nvenv = tdata.help;
445 		flag &= ~NV_TYPE;
446 		if(nv_isattr(tdata.tp,NV_TAGGED))
447 		{
448 			nv_offattr(tdata.tp,NV_TAGGED);
449 			return(0);
450 		}
451 	}
452 	else if(tdata.aflag==0 && ntp && ntp->tp)
453 		tdata.aflag = '-';
454 	if(!tdata.sh->mktype)
455 		tdata.help = 0;
456 	if(tdata.aflag=='+' && (flag&(NV_ARRAY|NV_IARRAY|NV_COMVAR)) && argv[1])
457 		errormsg(SH_DICT,ERROR_exit(1),e_nounattr);
458 	return(setall(argv,flag,troot,&tdata));
459 }
460 
461 static void print_value(Sfio_t *iop, Namval_t *np, struct tdata *tp)
462 {
463 	char	 *name;
464 	int	aflag=tp->aflag;
465 	if(nv_isnull(np))
466 	{
467 		if(!np->nvflag)
468 			return;
469 		aflag = '+';
470 	}
471 	else if(nv_istable(np))
472 	{
473 		Dt_t	*root = tp->sh->last_root;
474 		Namval_t *nsp = tp->sh->namespace;
475 		char *cp;
476 		if(!tp->pflag)
477 			return;
478 		cp = name = nv_name(np);
479 		if(*name=='.')
480 			name++;
481 		if(tp->indent)
482 			sfnputc(iop,'\t',tp->indent);
483 		sfprintf(iop,"namespace %s\n", name);
484 		if(tp->indent)
485 			sfnputc(iop,'\t',tp->indent);
486 		sfprintf(iop,"{\n", name);
487 		tp->indent++;
488 		/* output types from namespace */
489 		tp->sh->namespace = 0;
490 		tp->sh->prefix = nv_name(np)+1;
491 		sh_outtype(tp->sh,iop);
492 		tp->sh->prefix = 0;
493 		tp->sh->namespace = np;
494 		tp->sh->last_root = root;
495 		/* output variables from namespace */
496 		print_scan(iop,NV_NOSCOPE,nv_dict(np),aflag=='+',tp);
497 		tp->wctname = cp;
498 		tp->sh->namespace = 0;
499 		/* output functions from namespace */
500 		print_scan(iop,NV_FUNCTION|NV_NOSCOPE,tp->sh->fun_tree,aflag=='+',tp);
501 		tp->wctname = 0;
502 		tp->sh->namespace = nsp;
503 		if(--tp->indent)
504 			sfnputc(iop,'\t',tp->indent);
505 		sfwrite(iop,"}\n",2);
506 		return;
507 	}
508 	sfputr(iop,nv_name(np),aflag=='+'?'\n':'=');
509 	if(aflag=='+')
510 		return;
511 	if(nv_isarray(np) && nv_arrayptr(np))
512 	{
513 		nv_outnode(np,iop,-1,0);
514 		sfwrite(iop,")\n",2);
515 	}
516 	else
517 	{
518 		if(nv_isvtree(np))
519 			nv_onattr(np,NV_EXPORT);
520 		if(!(name = nv_getval(np)))
521 			name = Empty;
522 		if(!nv_isvtree(np))
523 			name = sh_fmtq(name);
524 		sfputr(iop,name,'\n');
525 	}
526 }
527 
528 static int     setall(char **argv,register int flag,Dt_t *troot,struct tdata *tp)
529 {
530 	register char *name;
531 	char *last = 0;
532 	int nvflags=(flag&(NV_ARRAY|NV_NOARRAY|NV_VARNAME|NV_IDENT|NV_ASSIGN|NV_STATIC|NV_MOVE));
533 	int r=0, ref=0, comvar=(flag&NV_COMVAR),iarray=(flag&NV_IARRAY);
534 	Shell_t *shp =tp->sh;
535 	if(!shp->prefix)
536 	{
537 		if(!tp->pflag)
538 			nvflags |= NV_NOSCOPE;
539 	}
540 	else if(*shp->prefix==0)
541 		shp->prefix = 0;
542 	if(*argv[0]=='+')
543 		nvflags |= NV_NOADD;
544 	flag &= ~(NV_NOARRAY|NV_NOSCOPE|NV_VARNAME|NV_IDENT|NV_STATIC|NV_COMVAR|NV_IARRAY);
545 	if(argv[1])
546 	{
547 		if(flag&NV_REF)
548 		{
549 			flag &= ~NV_REF;
550 			ref=1;
551 			if(tp->aflag!='-')
552 				nvflags |= NV_NOREF;
553 		}
554 		if(tp->pflag)
555 			nvflags |= (NV_NOREF|NV_NOADD|NV_NOFAIL);
556 		while(name = *++argv)
557 		{
558 			register unsigned newflag;
559 			register Namval_t *np;
560 			Namarr_t	*ap;
561 			Namval_t	*mp;
562 			unsigned curflag;
563 			if(troot == shp->fun_tree)
564 			{
565 				/*
566 				 *functions can be exported or
567 				 * traced but not set
568 				 */
569 				flag &= ~NV_ASSIGN;
570 				if(flag&NV_LTOU)
571 				{
572 					/* Function names cannot be special builtin */
573 					if((np=nv_search(name,shp->bltin_tree,0)) && nv_isattr(np,BLT_SPC))
574 						errormsg(SH_DICT,ERROR_exit(1),e_badfun,name);
575 #if SHOPT_NAMESPACE
576 					if(shp->namespace)
577 						np = sh_fsearch(shp,name,NV_ADD|HASH_NOSCOPE);
578 					else
579 #endif /* SHOPT_NAMESPACE */
580 					np = nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE);
581 				}
582 				else
583 				{
584 					if(shp->prefix)
585 					{
586 						sfprintf(shp->strbuf,"%s.%s%c",shp->prefix,name,0);
587 						name = sfstruse(shp->strbuf);
588 					}
589 #if SHOPT_NAMESPACE
590 					np = 0;
591 					if(shp->namespace)
592 						np = sh_fsearch(shp,name,HASH_NOSCOPE);
593 					if(!np)
594 #endif /* SHOPT_NAMESPACE */
595 					if(np=nv_search(name,troot,0))
596 					{
597 						if(!is_afunction(np))
598 							np = 0;
599 					}
600 					else if(memcmp(name,".sh.math.",9)==0 && sh_mathstd(name+9))
601 						continue;
602 				}
603 				if(np && ((flag&NV_LTOU) || !nv_isnull(np) || nv_isattr(np,NV_LTOU)))
604 				{
605 					if(flag==0 && !tp->help)
606 					{
607 						print_namval(sfstdout,np,tp->aflag=='+',tp);
608 						continue;
609 					}
610 					if(shp->subshell && !shp->subshare)
611 						sh_subfork();
612 					if(tp->aflag=='-')
613 						nv_onattr(np,flag|NV_FUNCTION);
614 					else if(tp->aflag=='+')
615 						nv_offattr(np,flag);
616 				}
617 				else
618 					r++;
619 				if(tp->help)
620 				{
621 					int offset = stktell(shp->stk);
622 					if(!np)
623 					{
624 						sfputr(shp->stk,shp->prefix,'.');
625 						sfputr(shp->stk,name,0);
626 						np = nv_search(stkptr(shp->stk,offset),troot,0);
627 						stkseek(shp->stk,offset);
628 					}
629 					if(np && np->nvalue.cp)
630 						np->nvalue.rp->help = tp->help;
631 				}
632 				continue;
633 			}
634 			/* tracked alias */
635 			if(troot==shp->track_tree && tp->aflag=='-')
636 			{
637 				np = nv_search(name,troot,NV_ADD);
638 				path_alias(np,path_absolute(shp,nv_name(np),NIL(Pathcomp_t*)));
639 				continue;
640 			}
641 			np = nv_open(name,troot,nvflags|((nvflags&NV_ASSIGN)?0:NV_ARRAY)|((iarray|(nvflags&(NV_REF|NV_NOADD)==NV_REF))?NV_FARRAY:0));
642 			if(!np)
643 				continue;
644 			if(nv_isnull(np) && !nv_isarray(np) && nv_isattr(np,NV_NOFREE))
645 				nv_offattr(np,NV_NOFREE);
646 			else if(tp->tp && !nv_isattr(np,NV_MINIMAL|NV_EXPORT) && (mp=(Namval_t*)np->nvenv) && (ap=nv_arrayptr(mp)) && (ap->nelem&ARRAY_TREE))
647 				errormsg(SH_DICT,ERROR_exit(1),e_typecompat,nv_name(np));
648 			else if((ap=nv_arrayptr(np)) && nv_aindex(np)>0 && ap->nelem==1 && nv_getval(np)==Empty)
649 			{
650 				ap->nelem++;
651 				_nv_unset(np,0);
652 				ap->nelem--;
653 			}
654 			else if(iarray && ap && ap->fun)
655 				errormsg(SH_DICT,ERROR_exit(1),"cannot change associative array %s to index array",nv_name(np));
656 			else if( (iarray||(flag&NV_ARRAY)) && nv_isvtree(np) && !nv_type(np))
657 				_nv_unset(np,NV_EXPORT);
658 			if(tp->pflag)
659 			{
660 				if(!nv_istable(np))
661 					nv_attribute(np,sfstdout,tp->prefix,1);
662 				print_value(sfstdout,np,tp);
663 				continue;
664 			}
665 			if(flag==NV_ASSIGN && !ref && tp->aflag!='-' && !strchr(name,'='))
666 			{
667 				if(troot!=shp->var_tree && (nv_isnull(np) || !print_namval(sfstdout,np,0,tp)))
668 				{
669 					sfprintf(sfstderr,sh_translate(e_noalias),name);
670 					r++;
671 				}
672 				if(!comvar && !iarray)
673 					continue;
674 			}
675 			if(!nv_isarray(np) && !strchr(name,'=') && !(shp->envlist  && nv_onlist(shp->envlist,name)))
676 			{
677 				if(comvar || (shp->last_root==shp->var_tree && (tp->tp || (!shp->st.real_fun && (nvflags&NV_STATIC)) || (!(flag&(NV_EXPORT|NV_RDONLY)) && nv_isattr(np,(NV_EXPORT|NV_IMPORT))==(NV_EXPORT|NV_IMPORT)))))
678 {
679 					_nv_unset(np,0);
680 }
681 			}
682 			if(troot==shp->var_tree)
683 			{
684 				if(iarray)
685 				{
686 					if(tp->tname)
687 						nv_atypeindex(np,tp->tname+1);
688 					else if(nv_isnull(np))
689 						nv_onattr(np,NV_ARRAY|(comvar?NV_NOFREE:0));
690 					else
691 					{
692 						if(ap && comvar)
693 							ap->nelem |= ARRAY_TREE;
694 						nv_putsub(np, (char*)0, 0);
695 					}
696 				}
697 				else if(nvflags&NV_ARRAY)
698 				{
699 					if(comvar)
700 					{
701 						Namarr_t *ap=nv_arrayptr(np);
702 						if(ap)
703 							ap->nelem |= ARRAY_TREE;
704 						else
705 						{
706 							_nv_unset(np,NV_RDONLY);
707 							nv_onattr(np,NV_NOFREE);
708 						}
709 					}
710 					nv_setarray(np,nv_associative);
711 				}
712 				else if(comvar && !nv_isvtree(np) && !nv_rename(np,flag|NV_COMVAR))
713 					nv_setvtree(np);
714 			}
715 			if(flag&NV_MOVE)
716 			{
717 				nv_rename(np, flag);
718 				nv_close(np);
719 				continue;
720 			}
721 			if(tp->tp && nv_type(np)!=tp->tp)
722 			{
723 				nv_settype(np,tp->tp,tp->aflag=='-'?0:NV_APPEND);
724 				flag = (np->nvflag&NV_NOCHANGE);
725 			}
726 			flag &= ~NV_ASSIGN;
727 			if(last=strchr(name,'='))
728 				*last = 0;
729 			if (shp->typeinit)
730 				continue;
731 			curflag = np->nvflag;
732 			if(!(flag&NV_INTEGER) && (flag&(NV_LTOU|NV_UTOL)))
733 			{
734 				Namfun_t *fp;
735 				char  *cp;
736 				if(!tp->wctname)
737 					errormsg(SH_DICT,ERROR_exit(1),e_mapchararg,nv_name(np));
738 				cp = (char*)nv_mapchar(np,0);
739 				if(fp=nv_mapchar(np,tp->wctname))
740 				{
741 					if(tp->aflag=='+')
742 					{
743 						if(cp && strcmp(cp,tp->wctname)==0)
744 						{
745 							nv_disc(np,fp,NV_POP);
746 							if(!(fp->nofree&1))
747 								free((void*)fp);
748 							nv_offattr(np,flag&(NV_LTOU|NV_UTOL));
749 						}
750 					}
751 					else if(!cp || strcmp(cp,tp->wctname))
752 					{
753 						nv_disc(np,fp,NV_LAST);
754 						nv_onattr(np,flag&(NV_LTOU|NV_UTOL));
755 					}
756 				}
757 			}
758 			if (tp->aflag == '-')
759 			{
760 				if((flag&NV_EXPORT) && (strchr(name,'.') || nv_isvtree(np)))
761 					errormsg(SH_DICT,ERROR_exit(1),e_badexport,name);
762 #if SHOPT_BSH
763 				if(flag&NV_EXPORT)
764 					nv_offattr(np,NV_IMPORT);
765 #endif /* SHOPT_BSH */
766 				newflag = curflag;
767 				if(flag&~NV_NOCHANGE)
768 					newflag &= NV_NOCHANGE;
769 				newflag |= flag;
770 				if (flag & (NV_LJUST|NV_RJUST))
771 				{
772 					if(!(flag&NV_RJUST))
773 						newflag &= ~NV_RJUST;
774 
775 					else if(!(flag&NV_LJUST))
776 						newflag &= ~NV_LJUST;
777 				}
778 			}
779 			else
780 			{
781 				if((flag&NV_RDONLY) && (curflag&NV_RDONLY))
782 					errormsg(SH_DICT,ERROR_exit(1),e_readonly,nv_name(np));
783 				newflag = curflag & ~flag;
784 			}
785 			if (tp->aflag && (tp->argnum>0 || (curflag!=newflag)))
786 			{
787 				if(shp->subshell)
788 					sh_assignok(np,1);
789 				if(troot!=shp->var_tree)
790 					nv_setattr(np,newflag&~NV_ASSIGN);
791 				else
792 				{
793 					char *oldname=0;
794 					int len=strlen(name);
795 					if(tp->argnum==1 && newflag==NV_INTEGER && nv_isattr(np,NV_INTEGER))
796 						tp->argnum = 10;
797 					if(np->nvfun && !nv_isarray(np) && name[len-1]=='.')
798 						newflag |= NV_NODISC;
799 					nv_newattr (np, newflag&~NV_ASSIGN,tp->argnum);
800 					if(oldname)
801 						np->nvname = oldname;
802 				}
803 			}
804 			if(tp->help && !nv_isattr(np,NV_MINIMAL|NV_EXPORT))
805 			{
806 				np->nvenv = tp->help;
807 				nv_onattr(np,NV_EXPORT);
808 			}
809 			if(last)
810 				*last = '=';
811 			/* set or unset references */
812 			if(ref)
813 			{
814 				if(tp->aflag=='-')
815 				{
816 					Dt_t *hp=0;
817 					if(nv_isattr(np,NV_PARAM) && shp->st.prevst)
818 					{
819 						if(!(hp=(Dt_t*)shp->st.prevst->save_tree))
820 							hp = dtvnext(shp->var_tree);
821 					}
822 					if(tp->sh->mktype)
823 						nv_onattr(np,NV_REF|NV_FUNCT);
824 					else
825 						nv_setref(np,hp,NV_VARNAME);
826 				}
827 				else
828 					nv_unref(np);
829 			}
830 			nv_close(np);
831 		}
832 	}
833 	else
834 	{
835 		if(shp->prefix)
836 			errormsg(SH_DICT,2, e_subcomvar,shp->prefix);
837 		if(tp->aflag)
838 		{
839 			if(troot==shp->fun_tree)
840 			{
841 				flag |= NV_FUNCTION;
842 				tp->prefix = 0;
843 			}
844 			else if(troot==shp->var_tree)
845 			{
846 				flag |= (nvflags&NV_ARRAY);
847 				if(iarray)
848 					flag |= NV_ARRAY|NV_IARRAY;
849 				if(comvar)
850 					flag |= NV_TABLE;
851 				if(!(flag&~NV_ASSIGN))
852 					tp->noref = 1;
853 			}
854 			if((flag&(NV_UTOL|NV_LTOU)) ==(NV_UTOL|NV_LTOU))
855 			{
856 				print_scan(sfstdout,flag&~NV_UTOL,troot,tp->aflag=='+',tp);
857 				flag &= ~NV_LTOU;
858 			}
859 			print_scan(sfstdout,flag,troot,tp->aflag=='+',tp);
860 			if(tp->noref)
861 			{
862 				tp->noref = 0;
863 				print_scan(sfstdout,flag|NV_REF,troot,tp->aflag=='+',tp);
864 			}
865 		}
866 		else if(troot==shp->alias_tree)
867 			print_scan(sfstdout,0,troot,0,tp);
868 		else
869 			print_all(sfstdout,troot,tp);
870 		sfsync(sfstdout);
871 	}
872 	return(r);
873 }
874 
875 #if SHOPT_DYNAMIC
876 
877 typedef void (*Libinit_f)(int,void*);
878 
879 typedef struct Libcomp_s
880 {
881 	void*		dll;
882 	char*		lib;
883 	dev_t		dev;
884 	ino_t		ino;
885 	unsigned int	attr;
886 } Libcomp_t;
887 
888 #define GROWLIB	4
889 
890 static Libcomp_t	*liblist;
891 static int		nlib;
892 static int		maxlib;
893 
894 /*
895  * add library to loaded list
896  * call (*lib_init)() on first load if defined
897  * always move to head of search list
898  * return: 0: already loaded 1: first load
899  */
900 
901 int sh_addlib(Shell_t* shp, void* dll, char* name, Pathcomp_t* pp)
902 {
903 	register int	n;
904 	register int	r;
905 	Libinit_f	initfn;
906 	Shbltin_t	*sp = &shp->bltindata;
907 
908 	sp->nosfio = 0;
909 	for (n = r = 0; n < nlib; n++)
910 	{
911 		if (r)
912 			liblist[n-1] = liblist[n];
913 		else if (liblist[n].dll == dll)
914 			r++;
915 	}
916 	if (r)
917 		nlib--;
918 	else if ((initfn = (Libinit_f)dlllook(dll, "lib_init")))
919 		(*initfn)(0,sp);
920 	if (nlib >= maxlib)
921 	{
922 		maxlib += GROWLIB;
923 		liblist = newof(liblist, Libcomp_t, maxlib+1, 0);
924 	}
925 	liblist[nlib].dll = dll;
926 	liblist[nlib].attr = (sp->nosfio?BLT_NOSFIO:0);
927 	if (name)
928 		liblist[nlib].lib = strdup(name);
929 	if (pp)
930 	{
931 		liblist[nlib].dev = pp->dev;
932 		liblist[nlib].ino = pp->ino;
933 	}
934 	nlib++;
935 	return !r;
936 }
937 
938 Shbltin_f sh_getlib(Shell_t* shp, char* sym, Pathcomp_t* pp)
939 {
940 	register int	n;
941 
942 	for (n = 0; n < nlib; n++)
943 		if (liblist[n].ino == pp->ino && liblist[n].dev == pp->dev)
944 			return (Shbltin_f)dlllook(liblist[n].dll, sym);
945 	return 0;
946 }
947 
948 #else
949 
950 int sh_addlib(Shell_t* shp, void* library, char* name, Pathcomp_t* pp)
951 {
952 	return 0;
953 }
954 
955 Shbltin_f sh_getlib(Shell_t* shp, char* name, Pathcomp_t* pp)
956 {
957 	return 0;
958 }
959 
960 #endif /* SHOPT_DYNAMIC */
961 
962 /*
963  * add change or list built-ins
964  * adding builtins requires dlopen() interface
965  */
966 int	b_builtin(int argc,char *argv[],Shbltin_t *context)
967 {
968 	register char *arg=0, *name;
969 	register int n, r=0, flag=0;
970 	register Namval_t *np;
971 	long dlete=0;
972 	struct tdata tdata;
973 	Shbltin_f addr;
974 	Stk_t	*stkp;
975 	void *library=0;
976 	char *errmsg;
977 #ifdef SH_PLUGIN_VERSION
978 	unsigned long ver;
979 	int list = 0;
980 	char path[1024];
981 #endif
982 	NOT_USED(argc);
983 	memset(&tdata,0,sizeof(tdata));
984 	tdata.sh = context->shp;
985 	stkp = tdata.sh->stk;
986 	if(!tdata.sh->pathlist)
987 		path_absolute(tdata.sh,argv[0],NIL(Pathcomp_t*));
988 	while (n = optget(argv,sh_optbuiltin)) switch (n)
989 	{
990 	    case 's':
991 		flag = BLT_SPC;
992 		break;
993 	    case 'd':
994 		dlete=1;
995 		break;
996 	    case 'f':
997 #if SHOPT_DYNAMIC
998 		arg = opt_info.arg;
999 #else
1000 		errormsg(SH_DICT,2, "adding built-ins not supported");
1001 		error_info.errors++;
1002 #endif /* SHOPT_DYNAMIC */
1003 		break;
1004 	    case 'l':
1005 #ifdef SH_PLUGIN_VERSION
1006 		list = 1;
1007 #endif
1008 	        break;
1009 	    case ':':
1010 		errormsg(SH_DICT,2, "%s", opt_info.arg);
1011 		break;
1012 	    case '?':
1013 		errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
1014 		break;
1015 	}
1016 	argv += opt_info.index;
1017 	if(error_info.errors)
1018 		errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*)));
1019 	if(arg || *argv)
1020 	{
1021 		if(sh_isoption(SH_RESTRICTED))
1022 			errormsg(SH_DICT,ERROR_exit(1),e_restricted,argv[-opt_info.index]);
1023 		if(sh_isoption(SH_PFSH))
1024 			errormsg(SH_DICT,ERROR_exit(1),e_pfsh,argv[-opt_info.index]);
1025 		if(tdata.sh->subshell && !tdata.sh->subshare)
1026 			sh_subfork();
1027 	}
1028 #if SHOPT_DYNAMIC
1029 	if(arg)
1030 	{
1031 #ifdef SH_PLUGIN_VERSION
1032 		if(!(library = dllplugin(SH_ID, arg, NiL, SH_PLUGIN_VERSION, &ver, RTLD_LAZY, path, sizeof(path))))
1033 		{
1034 			errormsg(SH_DICT,ERROR_exit(0),"%s: %s",arg,dllerror(0));
1035 			return(1);
1036 		}
1037 		if(list)
1038 			sfprintf(sfstdout, "%s %08lu %s\n", arg, ver, path);
1039 #else
1040 #if (_AST_VERSION>=20040404)
1041 		if(!(library = dllplug(SH_ID,arg,NIL(char*),RTLD_LAZY,NIL(char*),0)))
1042 #else
1043 		if(!(library = dllfind(arg,NIL(char*),RTLD_LAZY,NIL(char*),0)))
1044 #endif
1045 		{
1046 			errormsg(SH_DICT,ERROR_exit(0),"%s: %s",arg,dlerror());
1047 			return(1);
1048 		}
1049 #endif
1050 		sh_addlib(tdata.sh,library,arg,NiL);
1051 	}
1052 	else
1053 #endif /* SHOPT_DYNAMIC */
1054 	if(*argv==0 && !dlete)
1055 	{
1056 		print_scan(sfstdout, flag, tdata.sh->bltin_tree, 1, &tdata);
1057 		return(0);
1058 	}
1059 	r = 0;
1060 	flag = stktell(stkp);
1061 	while(arg = *argv)
1062 	{
1063 		name = path_basename(arg);
1064 		sfwrite(stkp,"b_",2);
1065 		sfputr(stkp,name,0);
1066 		errmsg = 0;
1067 		addr = 0;
1068 		if(dlete || liblist)
1069 			for(n=(nlib?nlib:dlete); --n>=0;)
1070 			{
1071 #if SHOPT_DYNAMIC
1072 				if(!dlete && !liblist[n].dll)
1073 					continue;
1074 				if(dlete || (addr = (Shbltin_f)dlllook(liblist[n].dll,stkptr(stkp,flag))))
1075 #else
1076 				if(dlete)
1077 #endif /* SHOPT_DYNAMIC */
1078 				{
1079 					if(np = sh_addbuiltin(arg, addr,pointerof(dlete)))
1080 					{
1081 						if(dlete || nv_isattr(np,BLT_SPC))
1082 							errmsg = "restricted name";
1083 #if SHOPT_DYNAMIC
1084 						else
1085 							nv_onattr(np,liblist[n].attr);
1086 #endif /* SHOPT_DYNAMIC */
1087 					}
1088 					break;
1089 				}
1090 			}
1091 		if(!addr && (np = nv_search(arg,context->shp->bltin_tree,0)))
1092 		{
1093 			if(nv_isattr(np,BLT_SPC))
1094 				errmsg = "restricted name";
1095 			addr = (Shbltin_f)np->nvalue.bfp;
1096 		}
1097 		if(!dlete && !addr && !(np=sh_addbuiltin(arg,(Shbltin_f)0 ,0)))
1098 			errmsg = "not found";
1099 		if(errmsg)
1100 		{
1101 			errormsg(SH_DICT,ERROR_exit(0),"%s: %s",*argv,errmsg);
1102 			r = 1;
1103 		}
1104 		stkseek(stkp,flag);
1105 		argv++;
1106 	}
1107 	return(r);
1108 }
1109 
1110 int    b_set(int argc,register char *argv[],Shbltin_t *context)
1111 {
1112 	struct tdata tdata;
1113 	int was_monitor = sh_isoption(SH_MONITOR);
1114 	memset(&tdata,0,sizeof(tdata));
1115 	tdata.sh = context->shp;
1116 	tdata.prefix=0;
1117 	if(argv[1])
1118 	{
1119 		if(sh_argopts(argc,argv,tdata.sh) < 0)
1120 			return(2);
1121 		if(sh_isoption(SH_VERBOSE))
1122 			sh_onstate(SH_VERBOSE);
1123 		else
1124 			sh_offstate(SH_VERBOSE);
1125 		if(sh_isoption(SH_MONITOR) && !was_monitor)
1126 			sh_onstate(SH_MONITOR);
1127 		else if(!sh_isoption(SH_MONITOR)  && was_monitor)
1128 			sh_offstate(SH_MONITOR);
1129 	}
1130 	else
1131 		/*scan name chain and print*/
1132 		print_scan(sfstdout,0,tdata.sh->var_tree,0,&tdata);
1133 	return(0);
1134 }
1135 
1136 /*
1137  * The removing of Shell variable names, aliases, and functions
1138  * is performed here.
1139  * Unset functions with unset -f
1140  * Non-existent items being deleted give non-zero exit status
1141  */
1142 
1143 int    b_unalias(int argc,register char *argv[],Shbltin_t *context)
1144 {
1145 	Shell_t *shp = context->shp;
1146 	return(unall(argc,argv,shp->alias_tree,shp));
1147 }
1148 
1149 int    b_unset(int argc,register char *argv[],Shbltin_t *context)
1150 {
1151 	Shell_t *shp = context->shp;
1152 	return(unall(argc,argv,shp->var_tree,shp));
1153 }
1154 
1155 static int unall(int argc, char **argv, register Dt_t *troot, Shell_t* shp)
1156 {
1157 	register Namval_t *np;
1158 	register const char *name;
1159 	volatile int r;
1160 	Dt_t	*dp;
1161 	int nflag=0,all=0,isfun,jmpval;
1162 	struct checkpt buff;
1163 	NOT_USED(argc);
1164 	if(troot==shp->alias_tree)
1165 	{
1166 		name = sh_optunalias;
1167 		if(shp->subshell)
1168 			troot = sh_subaliastree(0);
1169 	}
1170 	else
1171 		name = sh_optunset;
1172 	while(r = optget(argv,name)) switch(r)
1173 	{
1174 		case 'f':
1175 			troot = sh_subfuntree(1);
1176 			break;
1177 		case 'a':
1178 			all=1;
1179 			break;
1180 		case 'n':
1181 			nflag = NV_NOREF;
1182 			/* FALLTHROUGH */
1183 		case 'v':
1184 			troot = shp->var_tree;
1185 			break;
1186 		case ':':
1187 			errormsg(SH_DICT,2, "%s", opt_info.arg);
1188 			break;
1189 		case '?':
1190 			errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
1191 			return(2);
1192 	}
1193 	argv += opt_info.index;
1194 	if(error_info.errors || (*argv==0 &&!all))
1195 		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
1196 	if(!troot)
1197 		return(1);
1198 	r = 0;
1199 	if(troot==shp->var_tree)
1200 		nflag |= NV_VARNAME;
1201 	else
1202 		nflag = NV_NOSCOPE;
1203 	if(all)
1204 	{
1205 		dtclear(troot);
1206 		return(r);
1207 	}
1208 	sh_pushcontext(shp,&buff,1);
1209 	while(name = *argv++)
1210 	{
1211 		jmpval = sigsetjmp(buff.buff,0);
1212 		np = 0;
1213 		if(jmpval==0)
1214 		{
1215 #if SHOPT_NAMESPACE
1216 			if(shp->namespace && troot!=shp->var_tree)
1217 				np = sh_fsearch(shp,name,nflag?HASH_NOSCOPE:0);
1218 			if(!np)
1219 #endif /* SHOPT_NAMESPACE */
1220 			np=nv_open(name,troot,NV_NOADD|nflag);
1221 		}
1222 		else
1223 		{
1224 			r = 1;
1225 			continue;
1226 		}
1227 		if(np)
1228 		{
1229 			if(is_abuiltin(np) || nv_isattr(np,NV_RDONLY))
1230 			{
1231 				if(nv_isattr(np,NV_RDONLY))
1232 					errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
1233 				r = 1;
1234 				continue;
1235 			}
1236 			isfun = is_afunction(np);
1237 			if(troot==shp->var_tree)
1238 			{
1239 				Namarr_t *ap;
1240 #if SHOPT_FIXEDARRAY
1241 				if((ap=nv_arrayptr(np)) && !ap->fixed  && name[strlen(name)-1]==']' && !nv_getsub(np))
1242 #else
1243 				if(nv_isarray(np) && name[strlen(name)-1]==']' && !nv_getsub(np))
1244 #endif /* SHOPT_FIXEDARRAY */
1245 				{
1246 					r=1;
1247 					continue;
1248 				}
1249 
1250 				if(shp->subshell)
1251 					np=sh_assignok(np,0);
1252 			}
1253 			if(!nv_isnull(np) || nv_size(np) || nv_isattr(np,~(NV_MINIMAL|NV_NOFREE)))
1254 				_nv_unset(np,0);
1255 			if(troot==shp->var_tree && shp->st.real_fun && (dp=shp->var_tree->walk) && dp==shp->st.real_fun->sdict)
1256 				nv_delete(np,dp,NV_NOFREE);
1257 			else if(isfun && !(np->nvalue.rp && np->nvalue.rp->running))
1258 				nv_delete(np,troot,0);
1259 #if 0
1260 			/* causes unsetting local variable to expose global */
1261 			else if(shp->var_tree==troot && shp->var_tree!=shp->var_base && nv_search((char*)np,shp->var_tree,HASH_BUCKET|HASH_NOSCOPE))
1262 				nv_delete(np,shp->var_tree,0);
1263 #endif
1264 			else
1265 				nv_close(np);
1266 
1267 		}
1268 		else if(troot==shp->alias_tree)
1269 			r = 1;
1270 	}
1271 	sh_popcontext(shp,&buff);
1272 	return(r);
1273 }
1274 
1275 /*
1276  * print out the name and value of a name-value pair <np>
1277  */
1278 
1279 static int print_namval(Sfio_t *file,register Namval_t *np,register int flag, struct tdata *tp)
1280 {
1281 	register char *cp;
1282 	int	indent=tp->indent, outname=0, isfun;
1283 	sh_sigcheck(tp->sh);
1284 	if(flag)
1285 		flag = '\n';
1286 	if(tp->noref && nv_isref(np))
1287 		return(0);
1288 	if(nv_isattr(np,NV_NOPRINT|NV_INTEGER)==NV_NOPRINT)
1289 	{
1290 		if(is_abuiltin(np) && strcmp(np->nvname,".sh.tilde"))
1291 			sfputr(file,nv_name(np),'\n');
1292 		return(0);
1293 	}
1294 	if(nv_istable(np))
1295 	{
1296 		print_value(file,np,tp);
1297 		return(0);
1298 	}
1299 	isfun = is_afunction(np);
1300 	if(tp->prefix)
1301 	{
1302 		outname = (*tp->prefix=='t' &&  (!nv_isnull(np) || nv_isattr(np,NV_FLOAT|NV_RDONLY|NV_BINARY|NV_RJUST|NV_NOPRINT)));
1303 		if(indent && (isfun || outname || *tp->prefix!='t'))
1304 		{
1305 			sfnputc(file,'\t',indent);
1306 			indent = 0;
1307 		}
1308 		if(!isfun)
1309 		{
1310 			if(*tp->prefix=='t')
1311 			nv_attribute(np,tp->outfile,tp->prefix,tp->aflag);
1312 			else
1313 				sfputr(file,tp->prefix,' ');
1314 		}
1315 	}
1316 	if(isfun)
1317 	{
1318 		Sfio_t *iop=0;
1319 		char *fname=0;
1320 		if(nv_isattr(np,NV_NOFREE))
1321 			return(0);
1322 		if(!flag && !np->nvalue.ip)
1323 			sfputr(file,"typeset -fu",' ');
1324 		else if(!flag && !nv_isattr(np,NV_FPOSIX))
1325 			sfputr(file,"function",' ');
1326 		cp = nv_name(np);
1327 		if(tp->wctname)
1328 			cp += strlen(tp->wctname)+1;
1329 		sfputr(file,cp,-1);
1330 		if(nv_isattr(np,NV_FPOSIX))
1331 			sfwrite(file,"()",2);
1332 		if(np->nvalue.ip && np->nvalue.rp->hoffset>=0)
1333 			fname = np->nvalue.rp->fname;
1334 		else
1335 			flag = '\n';
1336 		if(flag)
1337 		{
1338 			if(tp->pflag && np->nvalue.ip && np->nvalue.rp->hoffset>=0)
1339 				sfprintf(file," #line %d %s\n",np->nvalue.rp->lineno,fname?sh_fmtq(fname):"");
1340 			else
1341 				sfputc(file, '\n');
1342 		}
1343 		else
1344 		{
1345 			if(nv_isattr(np,NV_FTMP))
1346 			{
1347 				fname = 0;
1348 				iop = tp->sh->heredocs;
1349 			}
1350 			else if(fname)
1351 				iop = sfopen(iop,fname,"r");
1352 			else if(tp->sh->gd->hist_ptr)
1353 				iop = (tp->sh->gd->hist_ptr)->histfp;
1354 			if(iop && sfseek(iop,(Sfoff_t)np->nvalue.rp->hoffset,SEEK_SET)>=0)
1355 				sfmove(iop,file, nv_size(np), -1);
1356 			else
1357 				flag = '\n';
1358 			if(fname)
1359 				sfclose(iop);
1360 		}
1361 		return(nv_size(np)+1);
1362 	}
1363 	if(nv_arrayptr(np))
1364 	{
1365 		if(indent)
1366 			sfnputc(file,'\t',indent);
1367 		print_value(file,np,tp);
1368 		return(0);
1369 	}
1370 	if(nv_isvtree(np))
1371 		nv_onattr(np,NV_EXPORT);
1372 	if(cp=nv_getval(np))
1373 	{
1374 		if(indent)
1375 			sfnputc(file,'\t',indent);
1376 		sfputr(file,nv_name(np),-1);
1377 		if(!flag)
1378 			flag = '=';
1379 		sfputc(file,flag);
1380 		if(flag != '\n')
1381 		{
1382 			if(nv_isref(np) && nv_refsub(np))
1383 			{
1384 				sfputr(file,sh_fmtq(cp),-1);
1385 				sfprintf(file,"[%s]\n", sh_fmtq(nv_refsub(np)));
1386 			}
1387 			else
1388 #if SHOPT_TYPEDEF
1389 				sfputr(file,nv_isvtree(np)?cp:sh_fmtq(cp),'\n');
1390 #else
1391 				sfputr(file,sh_fmtq(cp),'\n');
1392 #endif /* SHOPT_TYPEDEF */
1393 		}
1394 		return(1);
1395 	}
1396 	else if(outname || (tp->scanmask && tp->scanroot==tp->sh->var_tree))
1397 		sfputr(file,nv_name(np),'\n');
1398 	return(0);
1399 }
1400 
1401 /*
1402  * print attributes at all nodes
1403  */
1404 static void	print_all(Sfio_t *file,Dt_t *root, struct tdata *tp)
1405 {
1406 	tp->outfile = file;
1407 	nv_scan(root, print_attribute, (void*)tp, 0, 0);
1408 }
1409 
1410 /*
1411  * print the attributes of name value pair give by <np>
1412  */
1413 static void	print_attribute(register Namval_t *np,void *data)
1414 {
1415 	register struct tdata *dp = (struct tdata*)data;
1416 	nv_attribute(np,dp->outfile,dp->prefix,dp->aflag);
1417 }
1418 
1419 /*
1420  * print the nodes in tree <root> which have attributes <flag> set
1421  * of <option> is non-zero, no subscript or value is printed.
1422  */
1423 
1424 static void print_scan(Sfio_t *file, int flag, Dt_t *root, int option,struct tdata *tp)
1425 {
1426 	register char **argv;
1427 	register Namval_t *np;
1428 	register int namec;
1429 	Namval_t *onp = 0;
1430 	char	*name=0;
1431 	int	len;
1432 	tp->sh->last_table=0;
1433 	flag &= ~NV_ASSIGN;
1434 	tp->scanmask = flag&~NV_NOSCOPE;
1435 	tp->scanroot = root;
1436 	tp->outfile = file;
1437 #if SHOPT_TYPEDEF
1438 	if(!tp->prefix && tp->tp)
1439 		tp->prefix = nv_name(tp->tp);
1440 #endif /* SHOPT_TYPEDEF */
1441 	if(flag&NV_INTEGER)
1442 		tp->scanmask |= (NV_DOUBLE|NV_EXPNOTE);
1443 	if(flag==NV_LTOU || flag==NV_UTOL)
1444 		tp->scanmask |= NV_UTOL|NV_LTOU;
1445 	namec = nv_scan(root,nullscan,(void*)tp,tp->scanmask,flag);
1446 	argv = tp->argnam  = (char**)stkalloc(tp->sh->stk,(namec+1)*sizeof(char*));
1447 	namec = nv_scan(root, pushname, (void*)tp, tp->scanmask, flag&~NV_IARRAY);
1448 	if(mbcoll())
1449 		strsort(argv,namec,strcoll);
1450 	if(namec==0 && tp->sh->namespace && nv_dict(tp->sh->namespace)==root)
1451 	{
1452 		sfnputc(file,'\t',tp->indent);
1453 		sfwrite(file,":\n",2);
1454 	}
1455 	else while(namec--)
1456 	{
1457 		if((np=nv_search(*argv++,root,0)) && np!=onp && (!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE)))
1458 		{
1459 			onp = np;
1460 			if(name)
1461 			{
1462 				char *newname = nv_name(np);
1463 				if(memcmp(name,newname,len)==0 && newname[len]== '.')
1464 					continue;
1465 				name = 0;
1466 			}
1467 			if(flag&NV_ARRAY)
1468 			{
1469 				if(nv_aindex(np)>=0)
1470 				{
1471 					if(!(flag&NV_IARRAY))
1472 						continue;
1473 				}
1474 				else if((flag&NV_IARRAY))
1475 					continue;
1476 
1477 			}
1478 			tp->scanmask = flag&~NV_NOSCOPE;
1479 			tp->scanroot = root;
1480 			print_namval(file,np,option,tp);
1481 			if(!is_abuiltin(np) && nv_isvtree(np))
1482 			{
1483 				name = nv_name(np);
1484 				len = strlen(name);
1485 			}
1486 		}
1487 	}
1488 }
1489 
1490 /*
1491  * add the name of the node to the argument list argnam
1492  */
1493 
1494 static void pushname(Namval_t *np,void *data)
1495 {
1496 	struct tdata *tp = (struct tdata*)data;
1497 	*tp->argnam++ = nv_name(np);
1498 }
1499 
1500