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