Home | History | Annotate | Download | only in bltins
      1 /***********************************************************************
      2 *                                                                      *
      3 *               This software is part of the ast package               *
      4 *          Copyright (c) 1982-2009 AT&T Intellectual Property          *
      5 *                      and is licensed under the                       *
      6 *                  Common Public License, Version 1.0                  *
      7 *                    by AT&T Intellectual Property                     *
      8 *                                                                      *
      9 *                A copy of the License is available at                 *
     10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
     11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
     12 *                                                                      *
     13 *              Information and Software Systems Research               *
     14 *                            AT&T Research                             *
     15 *                           Florham Park NJ                            *
     16 *                                                                      *
     17 *                  David Korn <dgk (at) 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 = (n=='X'?2*sizeof(Sfdouble_t):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 		else if(nv_isnull(tdata.tp))
    393 			nv_newtype(tdata.tp);
    394 		tdata.tp->nvenv = tdata.help;
    395 		flag &= ~NV_TYPE;
    396 	}
    397 	else if(tdata.aflag==0 && ntp && ntp->tp)
    398 		tdata.aflag = '-';
    399 	if(!tdata.sh->mktype)
    400 		tdata.help = 0;
    401 	return(b_common(argv,flag,troot,&tdata));
    402 }
    403 
    404 static void print_value(Sfio_t *iop, Namval_t *np, struct tdata *tp)
    405 {
    406 	char	 *name;
    407 	int	aflag=tp->aflag;
    408 	if(nv_isnull(np))
    409 	{
    410 		if(!np->nvflag)
    411 			return;
    412 		aflag = '+';
    413 	}
    414 	sfputr(iop,nv_name(np),aflag=='+'?'\n':'=');
    415 	if(aflag=='+')
    416 		return;
    417 	if(nv_isarray(np) && nv_arrayptr(np))
    418 	{
    419 		nv_outnode(np,iop,-1,0);
    420 		sfwrite(iop,")\n",2);
    421 	}
    422 	else
    423 	{
    424 		if(nv_isvtree(np))
    425 			nv_onattr(np,NV_EXPORT);
    426 		if(!(name = nv_getval(np)))
    427 			name = Empty;
    428 		if(!nv_isvtree(np))
    429 			name = sh_fmtq(name);
    430 		sfputr(iop,name,'\n');
    431 	}
    432 }
    433 
    434 static int     b_common(char **argv,register int flag,Dt_t *troot,struct tdata *tp)
    435 {
    436 	register char *name;
    437 	char *last = 0;
    438 	int nvflags=(flag&(NV_ARRAY|NV_NOARRAY|NV_VARNAME|NV_IDENT|NV_ASSIGN|NV_STATIC|NV_MOVE));
    439 	int r=0, ref=0, comvar=(flag&NV_COMVAR),iarray=(flag&NV_IARRAY);
    440 	Shell_t *shp =tp->sh;
    441 	if(!shp->prefix)
    442 	{
    443 		if(!tp->pflag)
    444 			nvflags |= NV_NOSCOPE;
    445 	}
    446 	else if(*shp->prefix==0)
    447 		shp->prefix = 0;
    448 	flag &= ~(NV_NOARRAY|NV_NOSCOPE|NV_VARNAME|NV_IDENT|NV_STATIC|NV_COMVAR|NV_IARRAY);
    449 	if(argv[1])
    450 	{
    451 		if(flag&NV_REF)
    452 		{
    453 			flag &= ~NV_REF;
    454 			ref=1;
    455 			if(tp->aflag!='-')
    456 				nvflags |= NV_NOREF;
    457 		}
    458 		if(tp->pflag)
    459 			nvflags |= NV_NOREF;
    460 		while(name = *++argv)
    461 		{
    462 			register unsigned newflag;
    463 			register Namval_t *np;
    464 			unsigned curflag;
    465 			if(troot == shp->fun_tree)
    466 			{
    467 				/*
    468 				 *functions can be exported or
    469 				 * traced but not set
    470 				 */
    471 				flag &= ~NV_ASSIGN;
    472 				if(flag&NV_LTOU)
    473 				{
    474 					/* Function names cannot be special builtin */
    475 					if((np=nv_search(name,shp->bltin_tree,0)) && nv_isattr(np,BLT_SPC))
    476 						errormsg(SH_DICT,ERROR_exit(1),e_badfun,name);
    477 					np = nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE);
    478 				}
    479 				else  if((np=nv_search(name,troot,0)) && !is_afunction(np))
    480 					np = 0;
    481 				if(np && ((flag&NV_LTOU) || !nv_isnull(np) || nv_isattr(np,NV_LTOU)))
    482 				{
    483 					if(flag==0)
    484 					{
    485 						print_namval(sfstdout,np,tp->aflag=='+',tp);
    486 						continue;
    487 					}
    488 					if(shp->subshell && !shp->subshare)
    489 						sh_subfork();
    490 					if(tp->aflag=='-')
    491 						nv_onattr(np,flag|NV_FUNCTION);
    492 					else if(tp->aflag=='+')
    493 						nv_offattr(np,flag);
    494 				}
    495 				else
    496 					r++;
    497 				if(tp->help)
    498 				{
    499 					int offset = stktell(shp->stk);
    500 					sfputr(shp->stk,shp->prefix,'.');
    501 					sfputr(shp->stk,name,0);
    502 					if((np=nv_search(stkptr(shp->stk,offset),troot,0)) && np->nvalue.cp)
    503 						np->nvalue.rp->help = tp->help;
    504 					stkseek(shp->stk,offset);
    505 				}
    506 				continue;
    507 			}
    508 			/* tracked alias */
    509 			if(troot==shp->track_tree && tp->aflag=='-')
    510 			{
    511 				np = nv_search(name,troot,NV_ADD);
    512 				path_alias(np,path_absolute(nv_name(np),NIL(Pathcomp_t*)));
    513 				continue;
    514 			}
    515 			np = nv_open(name,troot,nvflags|NV_ARRAY);
    516 			if(tp->pflag)
    517 			{
    518 				nv_attribute(np,sfstdout,tp->prefix,1);
    519 				print_value(sfstdout,np,tp);
    520 				continue;
    521 			}
    522 			if(flag==NV_ASSIGN && !ref && tp->aflag!='-' && !strchr(name,'='))
    523 			{
    524 				if(troot!=shp->var_tree && (nv_isnull(np) || !print_namval(sfstdout,np,0,tp)))
    525 				{
    526 					sfprintf(sfstderr,sh_translate(e_noalias),name);
    527 					r++;
    528 				}
    529 				if(!comvar && !iarray)
    530 					continue;
    531 			}
    532 			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)))
    533 				_nv_unset(np,0);
    534 			if(troot==shp->var_tree)
    535 			{
    536 				if(iarray)
    537 				{
    538 					if(tp->tname)
    539 						nv_atypeindex(np,tp->tname+1);
    540 					else if(nv_isnull(np))
    541 						nv_onattr(np,NV_ARRAY|(comvar?NV_NOFREE:0));
    542 					else
    543 						nv_putsub(np, (char*)0, 0);
    544 				}
    545 				else if(nvflags&NV_ARRAY)
    546 				{
    547 					if(comvar)
    548 					{
    549 						Namarr_t *ap=nv_arrayptr(np);
    550 						if(ap)
    551 							ap->nelem |= ARRAY_TREE;
    552 						else
    553 						{
    554 							_nv_unset(np,NV_RDONLY);
    555 							nv_onattr(np,NV_NOFREE);
    556 						}
    557 					}
    558 					nv_setarray(np,nv_associative);
    559 				}
    560 				else if(comvar && !nv_isvtree(np) && !nv_rename(np,flag|NV_COMVAR))
    561 					nv_setvtree(np);
    562 			}
    563 			if(flag&NV_MOVE)
    564 			{
    565 				nv_rename(np, flag);
    566 				nv_close(np);
    567 				continue;
    568 			}
    569 			if(tp->tp && nv_type(np)!=tp->tp)
    570 			{
    571 				nv_settype(np,tp->tp,tp->aflag=='-'?0:NV_APPEND);
    572 				flag = (np->nvflag&NV_NOCHANGE);
    573 			}
    574 			curflag = np->nvflag;
    575 			flag &= ~NV_ASSIGN;
    576 			if(last=strchr(name,'='))
    577 				*last = 0;
    578 			if (shp->typeinit)
    579 				continue;
    580 			if (tp->aflag == '-')
    581 			{
    582 				if((flag&NV_EXPORT) && (strchr(name,'.') || nv_isvtree(np)))
    583 					errormsg(SH_DICT,ERROR_exit(1),e_badexport,name);
    584 #if SHOPT_BSH
    585 				if(flag&NV_EXPORT)
    586 					nv_offattr(np,NV_IMPORT);
    587 #endif /* SHOPT_BSH */
    588 				newflag = curflag;
    589 				if(flag&~NV_NOCHANGE)
    590 					newflag &= NV_NOCHANGE;
    591 				newflag |= flag;
    592 				if (flag & (NV_LJUST|NV_RJUST))
    593 				{
    594 					if(!(flag&NV_RJUST))
    595 						newflag &= ~NV_RJUST;
    596 
    597 					else if(!(flag&NV_LJUST))
    598 						newflag &= ~NV_LJUST;
    599 				}
    600 				if(!(flag&NV_INTEGER))
    601 				{
    602 					if (flag & NV_UTOL)
    603 						newflag &= ~NV_LTOU;
    604 					else if (flag & NV_LTOU)
    605 						newflag &= ~NV_UTOL;
    606 				}
    607 			}
    608 			else
    609 			{
    610 				if((flag&NV_RDONLY) && (curflag&NV_RDONLY))
    611 					errormsg(SH_DICT,ERROR_exit(1),e_readonly,nv_name(np));
    612 				newflag = curflag & ~flag;
    613 			}
    614 			if (tp->aflag && (tp->argnum>0 || (curflag!=newflag)))
    615 			{
    616 				if(shp->subshell)
    617 					sh_assignok(np,1);
    618 				if(troot!=shp->var_tree)
    619 					nv_setattr(np,newflag&~NV_ASSIGN);
    620 				else
    621 				{
    622 					char *oldname=0;
    623 					int len=strlen(name);
    624 					if(tp->argnum==1 && newflag==NV_INTEGER && nv_isattr(np,NV_INTEGER))
    625 						tp->argnum = 10;
    626 					/* use reference name for export */
    627 					if((newflag^curflag)&NV_EXPORT)
    628 					{
    629 						oldname = np->nvname;
    630 						np->nvname = name;
    631 					}
    632 					if(np->nvfun && !nv_isarray(np) && name[len-1]=='.')
    633 						newflag |= NV_NODISC;
    634 					nv_newattr (np, newflag&~NV_ASSIGN,tp->argnum);
    635 					if(oldname)
    636 						np->nvname = oldname;
    637 				}
    638 			}
    639 			if(tp->help && !nv_isattr(np,NV_MINIMAL|NV_EXPORT))
    640 			{
    641 				np->nvenv = tp->help;
    642 				nv_onattr(np,NV_EXPORT);
    643 			}
    644 			if(last)
    645 				*last = '=';
    646 			/* set or unset references */
    647 			if(ref)
    648 			{
    649 				if(tp->aflag=='-')
    650 				{
    651 					Dt_t *hp=0;
    652 					if(nv_isattr(np,NV_PARAM) && shp->st.prevst)
    653 					{
    654 						if(!(hp=(Dt_t*)shp->st.prevst->save_tree))
    655 							hp = dtvnext(shp->var_tree);
    656 					}
    657 					if(tp->sh->mktype)
    658 						nv_onattr(np,NV_REF|NV_FUNCT);
    659 					else
    660 						nv_setref(np,hp,NV_VARNAME);
    661 				}
    662 				else
    663 					nv_unref(np);
    664 			}
    665 			nv_close(np);
    666 		}
    667 	}
    668 	else if(!tp->sh->envlist)
    669 	{
    670 		if(shp->prefix)
    671 			errormsg(SH_DICT,2, "%s: compound assignment requires sub-variable name",shp->prefix);
    672 		if(tp->aflag)
    673 		{
    674 			if(troot==shp->fun_tree)
    675 			{
    676 				flag |= NV_FUNCTION;
    677 				tp->prefix = 0;
    678 			}
    679 			else if(troot==shp->var_tree)
    680 			{
    681 				flag |= (nvflags&NV_ARRAY);
    682 				if(flag&NV_IARRAY)
    683 					flag |= NV_ARRAY;
    684 			}
    685 			print_scan(sfstdout,flag,troot,tp->aflag=='+',tp);
    686 		}
    687 		else if(troot==shp->alias_tree)
    688 			print_scan(sfstdout,0,troot,0,tp);
    689 		else
    690 			print_all(sfstdout,troot,tp);
    691 		sfsync(sfstdout);
    692 	}
    693 	return(r);
    694 }
    695 
    696 typedef void (*Iptr_t)(int,void*);
    697 typedef int (*Fptr_t)(int, char*[], void*);
    698 
    699 #define GROWLIB	4
    700 
    701 static void		**liblist;
    702 static unsigned short	*libattr;
    703 static int		nlib;
    704 static int		maxlib;
    705 
    706 /*
    707  * This allows external routines to load from the same library */
    708 void **sh_getliblist(void)
    709 {
    710 	return(liblist);
    711 }
    712 
    713 /*
    714  * add library to loaded list
    715  * call (*lib_init)() on first load if defined
    716  * always move to head of search list
    717  * return: 0: already loaded 1: first load
    718  */
    719 #if SHOPT_DYNAMIC
    720 int sh_addlib(void* library)
    721 {
    722 	register int	n;
    723 	register int	r;
    724 	Iptr_t		initfn;
    725 	Shbltin_t	*sp = &sh.bltindata;
    726 
    727 	sp->nosfio = 0;
    728 	for (n = r = 0; n < nlib; n++)
    729 	{
    730 		if (r)
    731 		{
    732 			liblist[n-1] = liblist[n];
    733 			libattr[n-1] = libattr[n];
    734 		}
    735 		else if (liblist[n] == library)
    736 			r++;
    737 	}
    738 	if (r)
    739 		nlib--;
    740 	else if ((initfn = (Iptr_t)dlllook(library, "lib_init")))
    741 		(*initfn)(0,sp);
    742 	if (nlib >= maxlib)
    743 	{
    744 		maxlib += GROWLIB;
    745 		if (liblist)
    746 		{
    747 			liblist = (void**)realloc((void*)liblist, (maxlib+1)*sizeof(void**));
    748 			libattr = (unsigned short*)realloc((void*)liblist, (maxlib+1)*sizeof(unsigned short*));
    749 		}
    750 		else
    751 		{
    752 			liblist = (void**)malloc((maxlib+1)*sizeof(void**));
    753 			libattr = (unsigned short*)malloc((maxlib+1)*sizeof(unsigned short*));
    754 		}
    755 	}
    756 	libattr[nlib] = (sp->nosfio?BLT_NOSFIO:0);
    757 	liblist[nlib++] = library;
    758 	liblist[nlib] = 0;
    759 	return !r;
    760 }
    761 #else
    762 int sh_addlib(void* library)
    763 {
    764 	return 0;
    765 }
    766 #endif /* SHOPT_DYNAMIC */
    767 
    768 /*
    769  * add change or list built-ins
    770  * adding builtins requires dlopen() interface
    771  */
    772 int	b_builtin(int argc,char *argv[],void *extra)
    773 {
    774 	register char *arg=0, *name;
    775 	register int n, r=0, flag=0;
    776 	register Namval_t *np;
    777 	long dlete=0;
    778 	struct tdata tdata;
    779 	Fptr_t addr;
    780 	Stk_t	*stkp;
    781 	void *library=0;
    782 	char *errmsg;
    783 	NOT_USED(argc);
    784 	memset(&tdata,0,sizeof(tdata));
    785 	tdata.sh = ((Shbltin_t*)extra)->shp;
    786 	stkp = tdata.sh->stk;
    787 	if(!tdata.sh->pathlist)
    788 		path_absolute(argv[0],NIL(Pathcomp_t*));
    789 	while (n = optget(argv,sh_optbuiltin)) switch (n)
    790 	{
    791 	    case 's':
    792 		flag = BLT_SPC;
    793 		break;
    794 	    case 'd':
    795 		dlete=1;
    796 		break;
    797 	    case 'f':
    798 #if SHOPT_DYNAMIC
    799 		arg = opt_info.arg;
    800 #else
    801 		errormsg(SH_DICT,2, "adding built-ins not supported");
    802 		error_info.errors++;
    803 #endif /* SHOPT_DYNAMIC */
    804 		break;
    805 	    case ':':
    806 		errormsg(SH_DICT,2, "%s", opt_info.arg);
    807 		break;
    808 	    case '?':
    809 		errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
    810 		break;
    811 	}
    812 	argv += opt_info.index;
    813 	if(error_info.errors)
    814 		errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*)));
    815 	if(arg || *argv)
    816 	{
    817 		if(sh_isoption(SH_RESTRICTED))
    818 			errormsg(SH_DICT,ERROR_exit(1),e_restricted,argv[-opt_info.index]);
    819 		if(sh_isoption(SH_PFSH))
    820 			errormsg(SH_DICT,ERROR_exit(1),e_pfsh,argv[-opt_info.index]);
    821 		if(tdata.sh->subshell && !tdata.sh->subshare)
    822 			sh_subfork();
    823 	}
    824 #if SHOPT_DYNAMIC
    825 	if(arg)
    826 	{
    827 #if (_AST_VERSION>=20040404)
    828 		if(!(library = dllplug(SH_ID,arg,NIL(char*),RTLD_LAZY,NIL(char*),0)))
    829 #else
    830 		if(!(library = dllfind(arg,NIL(char*),RTLD_LAZY,NIL(char*),0)))
    831 #endif
    832 		{
    833 			errormsg(SH_DICT,ERROR_exit(0),"%s: %s",arg,dlerror());
    834 			return(1);
    835 		}
    836 		sh_addlib(library);
    837 	}
    838 	else
    839 #endif /* SHOPT_DYNAMIC */
    840 	if(*argv==0 && !dlete)
    841 	{
    842 		print_scan(sfstdout, flag, tdata.sh->bltin_tree, 1, &tdata);
    843 		return(0);
    844 	}
    845 	r = 0;
    846 	flag = stktell(stkp);
    847 	while(arg = *argv)
    848 	{
    849 		name = path_basename(arg);
    850 		sfwrite(stkp,"b_",2);
    851 		sfputr(stkp,name,0);
    852 		errmsg = 0;
    853 		addr = 0;
    854 		for(n=(nlib?nlib:dlete); --n>=0;)
    855 		{
    856 			/* (char*) added for some sgi-mips compilers */
    857 #if SHOPT_DYNAMIC
    858 			if(dlete || (addr = (Fptr_t)dlllook(liblist[n],stkptr(stkp,flag))))
    859 #else
    860 			if(dlete)
    861 #endif /* SHOPT_DYNAMIC */
    862 			{
    863 				if(np = sh_addbuiltin(arg, addr,pointerof(dlete)))
    864 				{
    865 					if(dlete || nv_isattr(np,BLT_SPC))
    866 						errmsg = "restricted name";
    867 					else
    868 						nv_onattr(np,libattr[n]);
    869 				}
    870 				break;
    871 			}
    872 		}
    873 		if(!dlete && !addr)
    874 		{
    875 			np = sh_addbuiltin(arg, 0 ,0);
    876 			if(np && nv_isattr(np,BLT_SPC))
    877 				errmsg = "restricted name";
    878 			else if(!np)
    879 				errmsg = "not found";
    880 		}
    881 		if(errmsg)
    882 		{
    883 			errormsg(SH_DICT,ERROR_exit(0),"%s: %s",*argv,errmsg);
    884 			r = 1;
    885 		}
    886 		stkseek(stkp,flag);
    887 		argv++;
    888 	}
    889 	return(r);
    890 }
    891 
    892 int    b_set(int argc,register char *argv[],void *extra)
    893 {
    894 	struct tdata tdata;
    895 	memset(&tdata,0,sizeof(tdata));
    896 	tdata.sh = ((Shbltin_t*)extra)->shp;
    897 	tdata.prefix=0;
    898 	if(argv[1])
    899 	{
    900 		if(sh_argopts(argc,argv,tdata.sh) < 0)
    901 			return(2);
    902 		if(sh_isoption(SH_VERBOSE))
    903 			sh_onstate(SH_VERBOSE);
    904 		else
    905 			sh_offstate(SH_VERBOSE);
    906 		if(sh_isoption(SH_MONITOR))
    907 			sh_onstate(SH_MONITOR);
    908 		else
    909 			sh_offstate(SH_MONITOR);
    910 	}
    911 	else
    912 		/*scan name chain and print*/
    913 		print_scan(sfstdout,0,tdata.sh->var_tree,0,&tdata);
    914 	return(0);
    915 }
    916 
    917 /*
    918  * The removing of Shell variable names, aliases, and functions
    919  * is performed here.
    920  * Unset functions with unset -f
    921  * Non-existent items being deleted give non-zero exit status
    922  */
    923 
    924 int    b_unalias(int argc,register char *argv[],void *extra)
    925 {
    926 	Shell_t *shp = ((Shbltin_t*)extra)->shp;
    927 	return(b_unall(argc,argv,shp->alias_tree,shp));
    928 }
    929 
    930 int    b_unset(int argc,register char *argv[],void *extra)
    931 {
    932 	Shell_t *shp = ((Shbltin_t*)extra)->shp;
    933 	return(b_unall(argc,argv,shp->var_tree,shp));
    934 }
    935 
    936 static int b_unall(int argc, char **argv, register Dt_t *troot, Shell_t* shp)
    937 {
    938 	register Namval_t *np;
    939 	register const char *name;
    940 	register int r;
    941 	Dt_t	*dp;
    942 	int nflag=0,all=0,isfun,jmpval;
    943 	struct checkpt buff;
    944 	NOT_USED(argc);
    945 	if(troot==shp->alias_tree)
    946 	{
    947 		name = sh_optunalias;
    948 		if(shp->subshell)
    949 			troot = sh_subaliastree(0);
    950 	}
    951 	else
    952 		name = sh_optunset;
    953 	while(r = optget(argv,name)) switch(r)
    954 	{
    955 		case 'f':
    956 			troot = sh_subfuntree(1);
    957 			break;
    958 		case 'a':
    959 			all=1;
    960 			break;
    961 		case 'n':
    962 			nflag = NV_NOREF;
    963 		case 'v':
    964 			troot = shp->var_tree;
    965 			break;
    966 		case ':':
    967 			errormsg(SH_DICT,2, "%s", opt_info.arg);
    968 			break;
    969 		case '?':
    970 			errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
    971 			return(2);
    972 	}
    973 	argv += opt_info.index;
    974 	if(error_info.errors || (*argv==0 &&!all))
    975 		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
    976 	if(!troot)
    977 		return(1);
    978 	r = 0;
    979 	if(troot==shp->var_tree)
    980 		nflag |= NV_VARNAME;
    981 	else
    982 		nflag = NV_NOSCOPE;
    983 	if(all)
    984 	{
    985 		dtclear(troot);
    986 		return(r);
    987 	}
    988 	sh_pushcontext(&buff,1);
    989 	while(name = *argv++)
    990 	{
    991 		jmpval = sigsetjmp(buff.buff,0);
    992 		np = 0;
    993 		if(jmpval==0)
    994 			np=nv_open(name,troot,NV_NOADD|nflag);
    995 		else
    996 		{
    997 			r = 1;
    998 			continue;
    999 		}
   1000 		if(np)
   1001 		{
   1002 			if(is_abuiltin(np) || nv_isattr(np,NV_RDONLY))
   1003 			{
   1004 				if(nv_isattr(np,NV_RDONLY))
   1005 					errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
   1006 				r = 1;
   1007 				continue;
   1008 			}
   1009 			isfun = is_afunction(np);
   1010 			if(troot==shp->var_tree)
   1011 			{
   1012 				if(nv_isarray(np) && name[strlen(name)-1]==']' && !nv_getsub(np))
   1013 				{
   1014 					r=1;
   1015 					continue;
   1016 				}
   1017 
   1018 				if(shp->subshell)
   1019 					np=sh_assignok(np,0);
   1020 			}
   1021 			if(!nv_isnull(np))
   1022 				nv_unset(np);
   1023 			nv_close(np);
   1024 			if(troot==shp->var_tree && shp->st.real_fun && (dp=shp->var_tree->walk) && dp==shp->st.real_fun->sdict)
   1025 				nv_delete(np,dp,NV_NOFREE);
   1026 			else if(isfun)
   1027 				nv_delete(np,troot,NV_NOFREE);
   1028 		}
   1029 	}
   1030 	sh_popcontext(&buff);
   1031 	return(r);
   1032 }
   1033 
   1034 /*
   1035  * print out the name and value of a name-value pair <np>
   1036  */
   1037 
   1038 static int print_namval(Sfio_t *file,register Namval_t *np,register int flag, struct tdata *tp)
   1039 {
   1040 	register char *cp;
   1041 	sh_sigcheck();
   1042 	if(flag)
   1043 		flag = '\n';
   1044 	if(nv_isattr(np,NV_NOPRINT|NV_INTEGER)==NV_NOPRINT)
   1045 	{
   1046 		if(is_abuiltin(np))
   1047 			sfputr(file,nv_name(np),'\n');
   1048 		return(0);
   1049 	}
   1050 	if(tp->prefix)
   1051 	{
   1052 		if(*tp->prefix=='t')
   1053 			nv_attribute(np,tp->outfile,tp->prefix,tp->aflag);
   1054 		else
   1055 			sfputr(file,tp->prefix,' ');
   1056 	}
   1057 	if(is_afunction(np))
   1058 	{
   1059 		Sfio_t *iop=0;
   1060 		char *fname=0;
   1061 		if(!flag && !np->nvalue.ip)
   1062 			sfputr(file,"typeset -fu",' ');
   1063 		else if(!flag && !nv_isattr(np,NV_FPOSIX))
   1064 			sfputr(file,"function",' ');
   1065 		sfputr(file,nv_name(np),-1);
   1066 		if(nv_isattr(np,NV_FPOSIX))
   1067 			sfwrite(file,"()",2);
   1068 		if(np->nvalue.ip && np->nvalue.rp->hoffset>=0)
   1069 			fname = np->nvalue.rp->fname;
   1070 		else
   1071 			flag = '\n';
   1072 		if(flag)
   1073 		{
   1074 			if(tp->pflag && np->nvalue.ip && np->nvalue.rp->hoffset>=0)
   1075 				sfprintf(file," #line %d %s\n",np->nvalue.rp->lineno,fname?sh_fmtq(fname):"");
   1076 			else
   1077 				sfputc(file, '\n');
   1078 		}
   1079 		else
   1080 		{
   1081 			if(nv_isattr(np,NV_FTMP))
   1082 			{
   1083 				fname = 0;
   1084 				iop = tp->sh->heredocs;
   1085 			}
   1086 			else if(fname)
   1087 				iop = sfopen(iop,fname,"r");
   1088 			else if(tp->sh->hist_ptr)
   1089 				iop = (tp->sh->hist_ptr)->histfp;
   1090 			if(iop && sfseek(iop,(Sfoff_t)np->nvalue.rp->hoffset,SEEK_SET)>=0)
   1091 				sfmove(iop,file, nv_size(np), -1);
   1092 			else
   1093 				flag = '\n';
   1094 			if(fname)
   1095 				sfclose(iop);
   1096 		}
   1097 		return(nv_size(np)+1);
   1098 	}
   1099 	if(nv_arrayptr(np))
   1100 	{
   1101 		print_value(file,np,tp);
   1102 		return(0);
   1103 	}
   1104 	if(nv_isvtree(np))
   1105 		nv_onattr(np,NV_EXPORT);
   1106 	if(cp=nv_getval(np))
   1107 	{
   1108 		sfputr(file,nv_name(np),-1);
   1109 		if(!flag)
   1110 			flag = '=';
   1111 		sfputc(file,flag);
   1112 		if(flag != '\n')
   1113 		{
   1114 			if(nv_isref(np) && nv_refsub(np))
   1115 			{
   1116 				sfputr(file,sh_fmtq(cp),-1);
   1117 				sfprintf(file,"[%s]\n", sh_fmtq(nv_refsub(np)));
   1118 			}
   1119 			else
   1120 #if SHOPT_TYPEDEF
   1121 				sfputr(file,nv_isvtree(np)?cp:sh_fmtq(cp),'\n');
   1122 #else
   1123 				sfputr(file,sh_fmtq(cp),'\n');
   1124 #endif /* SHOPT_TYPEDEF */
   1125 		}
   1126 		return(1);
   1127 	}
   1128 	else if(tp->scanmask && tp->scanroot==tp->sh->var_tree)
   1129 		sfputr(file,nv_name(np),'\n');
   1130 	return(0);
   1131 }
   1132 
   1133 /*
   1134  * print attributes at all nodes
   1135  */
   1136 static void	print_all(Sfio_t *file,Dt_t *root, struct tdata *tp)
   1137 {
   1138 	tp->outfile = file;
   1139 	nv_scan(root, print_attribute, (void*)tp, 0, 0);
   1140 }
   1141 
   1142 /*
   1143  * print the attributes of name value pair give by <np>
   1144  */
   1145 static void	print_attribute(register Namval_t *np,void *data)
   1146 {
   1147 	register struct tdata *dp = (struct tdata*)data;
   1148 	nv_attribute(np,dp->outfile,dp->prefix,dp->aflag);
   1149 }
   1150 
   1151 /*
   1152  * print the nodes in tree <root> which have attributes <flag> set
   1153  * of <option> is non-zero, no subscript or value is printed.
   1154  */
   1155 
   1156 static void print_scan(Sfio_t *file, int flag, Dt_t *root, int option,struct tdata *tp)
   1157 {
   1158 	register char **argv;
   1159 	register Namval_t *np;
   1160 	register int namec;
   1161 	Namval_t *onp = 0;
   1162 	tp->sh->last_table=0;
   1163 	flag &= ~NV_ASSIGN;
   1164 	tp->scanmask = flag&~NV_NOSCOPE;
   1165 	tp->scanroot = root;
   1166 	tp->outfile = file;
   1167 #if SHOPT_TYPEDEF
   1168 	if(!tp->prefix && tp->tp)
   1169 		tp->prefix = nv_name(tp->tp);
   1170 #endif /* SHOPT_TYPEDEF */
   1171 	if(flag&NV_INTEGER)
   1172 		tp->scanmask |= (NV_DOUBLE|NV_EXPNOTE);
   1173 	namec = nv_scan(root,nullscan,(void*)tp,tp->scanmask,flag);
   1174 	argv = tp->argnam  = (char**)stkalloc(tp->sh->stk,(namec+1)*sizeof(char*));
   1175 	namec = nv_scan(root, pushname, (void*)tp, tp->scanmask, flag&~NV_IARRAY);
   1176 	if(mbcoll())
   1177 		strsort(argv,namec,strcoll);
   1178 	while(namec--)
   1179 	{
   1180 		if((np=nv_search(*argv++,root,0)) && np!=onp && (!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE)))
   1181 		{
   1182 			onp = np;
   1183 			if(flag&NV_ARRAY)
   1184 			{
   1185 				if(nv_aindex(np)>=0)
   1186 				{
   1187 					if(!(flag&NV_IARRAY))
   1188 						continue;
   1189 				}
   1190 				else if((flag&NV_IARRAY))
   1191 					continue;
   1192 
   1193 			}
   1194 			print_namval(file,np,option,tp);
   1195 		}
   1196 	}
   1197 }
   1198 
   1199 /*
   1200  * add the name of the node to the argument list argnam
   1201  */
   1202 
   1203 static void pushname(Namval_t *np,void *data)
   1204 {
   1205 	struct tdata *tp = (struct tdata*)data;
   1206 	*tp->argnam++ = nv_name(np);
   1207 }
   1208 
   1209