14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*8462SApril.Chin@Sun.COM *          Copyright (c) 1982-2008 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
7*8462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                  David Korn <dgk@research.att.com>                   *
184887Schin *                                                                      *
194887Schin ***********************************************************************/
204887Schin /*
214887Schin  * bash specific extensions
224887Schin  * originally provided by Karsten Fleischer
234887Schin  */
244887Schin 
254887Schin #include "defs.h"
264887Schin #include "path.h"
274887Schin #include "io.h"
284887Schin #include "builtins.h"
294887Schin #include "name.h"
304887Schin 
314887Schin #ifndef BASH_MAJOR
324887Schin #   define BASH_MAJOR	"1"
334887Schin #   define BASH_MINOR	"0"
344887Schin #   define BASH_PATCH	"0"
354887Schin #   define BASH_BUILD	"0"
364887Schin #   define BASH_RELEASE	"experimental"
374887Schin #endif
384887Schin #define BASH_VERSION	BASH_MAJOR "." BASH_MINOR "." BASH_PATCH "(" BASH_BUILD ")-" BASH_RELEASE
394887Schin 
404887Schin 
414887Schin extern const char	bash_pre_rc[];
424887Schin 
434887Schin static char *login_files[4];
444887Schin 
454887Schin const char sh_bash1[] =
464887Schin 	"[B?Enable brace group expansion. This option is only availabe in bash "
474887Schin 	"compatibility mode. In ksh mode, brace group expansion is always on.]"
484887Schin 	"[P?Do not follow symbolic links, use physical directory structure "
494887Schin 	"instead. Only available in bash compatibility mode.]";
504887Schin const char sh_bash2[] =
514887Schin "[O]:?[shopt_option?\ashopt_option\a is one of the shell options accepted by "
524887Schin 	"the \bshopt\b builtin. If \ashopt_option\a is present, \b-O\b sets "
534887Schin 	"the value of that option; \b+O\b unsets it. If \ashopt_option\a is "
544887Schin 	"not supplied, the names and values of the shell options accepted by "
554887Schin 	"\bshopt\b are printed on the standard output. If the invocation "
564887Schin 	"option is \b+O\b, the output is displayed in a format that may be "
574887Schin 	"reused as input. Only available if invoked as \bbash\b.]"
584887Schin "[01:init-file|rcfile]:[file?Execute commands from \afile\a instead of the "
594887Schin 	"standard personal initialization file ~/.bashrc if the shell is "
604887Schin 	"interactive. Only available if invoked as \bbash\b.]"
614887Schin "[02:editing?For option compatibility with \bbash\b only. Ignored.]"
624887Schin "[03:profile?Read either the system-wide startup file or any of the "
634887Schin 	"personal initialization files. On by default for interactive "
644887Schin 	"shells. Only available if invoked as \bbash\b.]"
65*8462SApril.Chin@Sun.COM "[04:posix?If invoked as \bbash\b, turn on POSIX compatibility. \bBash\b in "
664887Schin 	"POSIX mode is not the same as \bksh\b.]"
67*8462SApril.Chin@Sun.COM "[05:version?Print version number and exit.]";
684887Schin 
694887Schin const char sh_optshopt[] =
704887Schin "+[-1c?\n@(#)$Id: shopt (AT&T Research) 2003-02-13 $\n]"
714887Schin "[-author?Karsten Fleischer <K.Fleischer@omnium.de>]"
724887Schin USAGE_LICENSE
734887Schin "[+NAME?shopt - set/unset variables controlling optional shell behavior]"
744887Schin "[+DESCRIPTION?\bshopt\b sets or unsets variables controlling optional shell "
754887Schin 	"behavior. With no options, or with the \b-p\b option, a list of all "
764887Schin 	"settable options is displayed, with an indication of whether or not "
774887Schin 	"each is set.]"
784887Schin "[p?Causes output to be displayed in a form that may be reused as input.]"
794887Schin "[s?Set each \aoptname\a.]"
804887Schin "[u?Unset each \aoptname\a.]"
814887Schin "[q?Suppress output (quiet mode). The return status indicates whether the "
824887Schin 	"\aoptname\a is set or unset. If multiple \aoptname\a arguments are "
834887Schin 	"given with \b-q\b, the return status is zero if all \aoptname\as are "
844887Schin 	"enabled; non-zero otherwise.]"
854887Schin "[o?Restricts the values of \aoptname\a to be those defined for the \b-o\b "
864887Schin 	"option to the set builtin.]"
874887Schin "[+?If either \b-s\b or \b-u\b is used with no \aoptname\a arguments, the "
884887Schin 	"display is limited to those options which are set or unset.]"
894887Schin "[+?\bshopt\b supports all bash options. Some settings do not have any effect "
904887Schin 	"or are are always on and cannot be changed.]"
914887Schin "[+?The value of \aoptname\a must be one of the following:]{"
924887Schin 		"[+cdable_vars?If set, arguments to the \bcd\b command are "
934887Schin 			"assumed to be names of variables whose values are to "
944887Schin 			"be used if the usual \bcd\b proceeding fails.]"
954887Schin 		"[+cdspell?Currently ignored.]"
964887Schin 		"[+checkhash?Always on.]"
974887Schin 		"[+checkwinsize?Currently ignored.]"
984887Schin 		"[+cmdhist?Always on.]"
994887Schin 		"[+dotglob?If set, include filenames beginning with a \b.\b "
1004887Schin 			"in the results of pathname expansion.]"
1014887Schin 		"[+execfail?Always on.]"
1024887Schin 		"[+expand_aliases?Always on.]"
1034887Schin 		"[+extglob?Enable extended pattern matching features.]"
1044887Schin 		"[+histappend?Always on.]"
1054887Schin 		"[+histreedit?If set and an edit mode is selected, the user "
1064887Schin 			"is given the opportunity to re-edit a failed history "
1074887Schin 			"substitution.]"
1084887Schin 		"[+histverify?If set and an edit mode is selected, the result "
1094887Schin 			"of a history substitution will not be executed "
1104887Schin 			"immediately but be placed in the edit buffer for "
1114887Schin 			"further modifications.]"
1124887Schin 		"[+hostcomplete?Currently ignored.]"
1134887Schin 		"[+huponexit?Currently ignored.]"
1144887Schin 		"[+interactive_comments?Always on.]"
1154887Schin 		"[+lithist?Always on.]"
1164887Schin 		"[+login_shell?This option is set if the shell is started as "
1174887Schin 			"a login shell. The value cannot be changed.]"
1184887Schin 		"[+mailwarn?Currently ignored.]"
1194887Schin 		"[+no_empty_cmd_completion?Always on.]"
1204887Schin 		"[+nocaseglob?Match filenames in a case-insensitive fashion "
1214887Schin 			"when performing filename expansion.]"
1224887Schin 		"[+nullglob?Allows filename patterns which match no files to "
1234887Schin 			"expand to a null string, rather than themselves.]"
1244887Schin 		"[+progcomp?Currently ignored.]"
1254887Schin 		"[+promptvars?Currently ignored.]"
1264887Schin 		"[+restricted_shell?This option is set if the shell is started "
1274887Schin 			"as a restricted shell. The value cannot be changed. "
1284887Schin 			"It is not reset during execution of startup files, "
1294887Schin 			"allowing the startup files to determine whether the "
1304887Schin 			"shell is restricted.]"
1314887Schin 		"[+shift_verbose?Currently ignored.]"
1324887Schin 		"[+sourcepath?If set, the \b.\b builtin uses the value of PATH "
1334887Schin 			"to find the directory containing the file supplied "
1344887Schin 			"as an argument.]"
1354887Schin 		"[+xpg_echo?If set, the \becho\b and \bprint\b builtins "
1364887Schin 			"expand backslash-escape sequences.]"
1374887Schin "}"
1384887Schin "\n"
1394887Schin "\n[optname ...]\n"
1404887Schin "\n"
1414887Schin "[+EXIT STATUS?]{"
1424887Schin 	"[+?The return status when listing options is zero if all \aoptnames\a "
1434887Schin 	"are enabled, non-zero otherwise. When setting or unsetting options, "
1444887Schin 	"the return status is zero unless an \aoptname\a is not a valid shell "
1454887Schin 	"option.]"
1464887Schin "}"
1474887Schin 
1484887Schin "[+SEE ALSO?\bset\b(1)]"
1494887Schin ;
1504887Schin 
1514887Schin /* GLOBIGNORE discipline. Turn on SH_DOTGLOB on set, turn off on unset. */
1524887Schin 
1534887Schin static void put_globignore(register Namval_t* np, const char *val, int flags, Namfun_t *fp)
1544887Schin {
1554887Schin 	if(val)
1564887Schin 		sh_onoption(SH_DOTGLOB);
1574887Schin 	else
1584887Schin 		sh_offoption(SH_DOTGLOB);
1594887Schin 
1604887Schin 	nv_putv(np,val,flags,fp);
1614887Schin }
1624887Schin 
1634887Schin const Namdisc_t SH_GLOBIGNORE_disc  = { sizeof(Namfun_t), put_globignore };
1644887Schin 
1654887Schin /* FUNCNAME discipline */
1664887Schin 
1674887Schin struct	funcname
1684887Schin {
1694887Schin 	Namfun_t	hdr;
1704887Schin };
1714887Schin 
1724887Schin static void put_funcname(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
1734887Schin {
1744887Schin 	/* bash silently returns with an error when FUNCNAME is set,
1754887Schin 	   unsetting FUNCNAME is allowed */
1764887Schin 	if(val && !(flags&NV_RDONLY))
1774887Schin 		error_info.exit(1);
1784887Schin 
1794887Schin 	nv_putv(np,val,flags,fp);
1804887Schin }
1814887Schin 
1824887Schin const Namdisc_t SH_FUNCNAME_disc  = { sizeof(struct funcname), put_funcname };
1834887Schin 
1844887Schin #define	SET_SET		1
1854887Schin #define	SET_UNSET	2
1864887Schin #define	SET_NOARGS	4
1874887Schin 
1884887Schin /* shopt builtin */
1894887Schin 
1904887Schin int     b_shopt(int argc,register char *argv[],void *extra)
1914887Schin {
1924887Schin         Shell_t *shp = (Shell_t*)extra;
1934887Schin 	int n, f, ret=0;
1944887Schin 	Shopt_t newflags=shp->options, opt;
1954887Schin 	int verbose=PRINT_SHOPT|PRINT_ALL|PRINT_NO_HEADER|PRINT_VERBOSE;
1964887Schin 	int setflag=0, quietflag=0, oflag=0;
1974887Schin 	memset(&opt,0,sizeof(opt));
1984887Schin #if SHOPT_RAWONLY
1994887Schin 	on_option(&newflags,SH_VIRAW);
2004887Schin #endif
2014887Schin 	while((n = optget(argv,sh_optshopt)))
2024887Schin 	{
2034887Schin 		switch(n)
2044887Schin 		{
2054887Schin 		case 'p':
2064887Schin 			verbose&=~PRINT_VERBOSE;
2074887Schin 			break;
2084887Schin 		case 's':
2094887Schin 		case 'u':
2104887Schin 			setflag|=n=='s'?SET_SET:SET_UNSET;
2114887Schin 			if(setflag==(SET_SET|SET_UNSET))
2124887Schin 			{
2134887Schin 				errormsg(SH_DICT,ERROR_ERROR,"cannot set and unset options simultaneously");
2144887Schin 				error_info.errors++;
2154887Schin 			}
2164887Schin 			break;
2174887Schin 		case 'q':
2184887Schin 			quietflag=1;
2194887Schin 			break;
2204887Schin 		case 'o':
2214887Schin 			oflag=1;
2224887Schin 			verbose&=~PRINT_SHOPT;
2234887Schin 			break;
2244887Schin 		case ':':
2254887Schin 			errormsg(SH_DICT,2, "%s", opt_info.arg);
2264887Schin 			continue;
2274887Schin 		case '?':
2284887Schin 			errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
2294887Schin 			return(-1);
2304887Schin 		}
2314887Schin 	}
2324887Schin 	if(error_info.errors)
2334887Schin 		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
2344887Schin 	argc -= opt_info.index;
2354887Schin 	if(argc==0)
2364887Schin 	{
2374887Schin 		/* no args, -s => mask=current options, -u mask=~(current options)
2384887Schin 		   else mask=all bits */
2394887Schin 		if(setflag&SET_SET)
2404887Schin 			opt=newflags;
2414887Schin 		else if(setflag&SET_UNSET)
2424887Schin 			for(n=0;n<4;n++)
2434887Schin 				opt.v[n]=~newflags.v[n];
2444887Schin 		else
2454887Schin 			memset(&opt,0xff,sizeof(opt));
2464887Schin 		setflag=SET_NOARGS;
2474887Schin 	}
2484887Schin 	while(argc>0)
2494887Schin 	{
2504887Schin 		f=1;
2514887Schin 		n=sh_lookopt(argv[opt_info.index],&f);
2524887Schin 		if(n<=0||(setflag
2534887Schin 			&& (is_option(&opt,SH_INTERACTIVE)
2544887Schin 			    || is_option(&opt,SH_RESTRICTED)
2554887Schin 			    || is_option(&opt,SH_RESTRICTED2)
2564887Schin 			    || is_option(&opt,SH_BASH)
2574887Schin 			    || is_option(&opt,SH_LOGIN_SHELL)))
2584887Schin 			||(oflag&&(n&SH_BASHOPT)))
2594887Schin 		{
2604887Schin 			errormsg(SH_DICT,ERROR_ERROR, e_option, argv[opt_info.index]);
2614887Schin 			error_info.errors++;
2624887Schin 			ret=1;
2634887Schin 		}
2644887Schin 		else if(f)
2654887Schin 			on_option(&opt,n&0xff);
2664887Schin 		else
2674887Schin 			off_option(&opt,n&0xff);
2684887Schin 		opt_info.index++;
2694887Schin 		argc--;
2704887Schin 	}
2714887Schin 	if(setflag&(SET_SET|SET_UNSET))
2724887Schin 	{
2734887Schin 		if(setflag&SET_SET)
2744887Schin 		{
2754887Schin 			if(sh_isoption(SH_INTERACTIVE))
2764887Schin 				off_option(&opt,SH_NOEXEC);
2774887Schin 			if(is_option(&opt,SH_VI)||is_option(&opt,SH_EMACS)||is_option(&opt,SH_GMACS))
2784887Schin 			{
2794887Schin 				off_option(&newflags,SH_VI);
2804887Schin 				off_option(&newflags,SH_EMACS);
2814887Schin 				off_option(&newflags,SH_GMACS);
2824887Schin 			}
2834887Schin 			for(n=0;n<4;n++)
2844887Schin 				newflags.v[n] |= opt.v[n];
2854887Schin 		}
2864887Schin 		else if(setflag&SET_UNSET)
2874887Schin 			for(n=0;n<4;n++)
2884887Schin 				newflags.v[n] &= ~opt.v[n];
289*8462SApril.Chin@Sun.COM 		sh_applyopts(shp,newflags);
2904887Schin 		shp->options = newflags;
2914887Schin 		if(is_option(&newflags,SH_XTRACE))
2924887Schin 			sh_trace(argv,1);
2934887Schin 	}
2944887Schin 	else if(!(setflag&SET_NOARGS)) /* no -s,-u but args, ret=0 if opt&mask==mask */
2954887Schin 	{
2964887Schin 		for(n=0;n<4;n++)
2974887Schin 			ret+=((newflags.v[n]&opt.v[n])!=opt.v[n]);
2984887Schin 	}
2994887Schin 	if(!quietflag&&!(setflag&(SET_SET|SET_UNSET)))
3004887Schin 		sh_printopts(newflags,verbose,&opt);
3014887Schin 	return(ret);
3024887Schin }
3034887Schin 
3044887Schin /* mode = 0: init, called two times
3054887Schin         before parsing shell args with SH_PREINIT state turned on
3064887Schin 	second time after sh_init() is through and with SH_PREINIT state turned off
3074887Schin    mode > 1: re-init
3084887Schin    mode < 0: shutdown
3094887Schin */
3104887Schin 
3114887Schin void bash_init(int mode)
3124887Schin {
313*8462SApril.Chin@Sun.COM 	Shell_t		*shp = &sh;
3144887Schin 	Sfio_t		*iop;
3154887Schin 	Namval_t	*np;
3164887Schin 	int		n=0,xtrace,verbose;
3174887Schin 	if(mode>0)
3184887Schin 		goto reinit;
3194887Schin 	if(mode < 0)
3204887Schin 	{
3214887Schin 		/* termination code */
3224887Schin 		if(sh_isoption(SH_LOGIN_SHELL) && !sh_isoption(SH_POSIX))
323*8462SApril.Chin@Sun.COM 			sh_source(shp, NiL, sh_mactry(shp,(char*)e_bash_logout));
3244887Schin 		return;
3254887Schin 	}
3264887Schin 
3274887Schin 	if(sh_isstate(SH_PREINIT))
3284887Schin 	{	/* pre-init stage */
3294887Schin 		if(sh_isoption(SH_RESTRICTED))
3304887Schin 			sh_onoption(SH_RESTRICTED2);
3314887Schin 		sh_onoption(SH_HISTORY2);
3324887Schin 		sh_onoption(SH_INTERACTIVE_COMM);
3334887Schin 		sh_onoption(SH_SOURCEPATH);
3344887Schin 		sh_onoption(SH_HISTAPPEND);
3354887Schin 		sh_onoption(SH_CMDHIST);
3364887Schin 		sh_onoption(SH_LITHIST);
3374887Schin 		sh_onoption(SH_NOEMPTYCMDCOMPL);
338*8462SApril.Chin@Sun.COM 		if(shp->login_sh==2)
3394887Schin 			sh_onoption(SH_LOGIN_SHELL);
3404887Schin 		if(strcmp(astconf("CONFORMANCE",0,0),"standard")==0)
3414887Schin 			sh_onoption(SH_POSIX);
3424887Schin 		if(strcmp(astconf("UNIVERSE",0,0),"att")==0)
3434887Schin 			sh_onoption(SH_XPG_ECHO);
3444887Schin 		else
3454887Schin 			sh_offoption(SH_XPG_ECHO);
3464887Schin 		if(strcmp(astconf("PATH_RESOLVE",0,0),"physical")==0)
3474887Schin 			sh_onoption(SH_PHYSICAL);
3484887Schin 		else
3494887Schin 			sh_offoption(SH_PHYSICAL);
3504887Schin 
3514887Schin 		/* add builtins */
3524887Schin 		sh_addbuiltin("shopt", b_shopt, &sh);
3534887Schin 
3544887Schin 		/* set up some variables needed for --version
3554887Schin 		 * needs to go here because --version option is parsed before the init script.
3564887Schin 		 */
357*8462SApril.Chin@Sun.COM 		if(np=nv_open("HOSTTYPE",shp->var_tree,0))
3584887Schin 			nv_putval(np, BASH_HOSTTYPE, NV_NOFREE);
359*8462SApril.Chin@Sun.COM 		if(np=nv_open("MACHTYPE",shp->var_tree,0))
3604887Schin 			nv_putval(np, BASH_MACHTYPE, NV_NOFREE);
361*8462SApril.Chin@Sun.COM 		if(np=nv_open("BASH_VERSION",shp->var_tree,0))
3624887Schin 			nv_putval(np, BASH_VERSION, NV_NOFREE);
363*8462SApril.Chin@Sun.COM 		if(np=nv_open("BASH_VERSINFO",shp->var_tree,0))
3644887Schin 		{
3654887Schin 			char *argv[7];
3664887Schin 			argv[0] = BASH_MAJOR;
3674887Schin 			argv[1] = BASH_MINOR;
3684887Schin 			argv[2] = BASH_PATCH;
3694887Schin 			argv[3] = BASH_BUILD;
3704887Schin 			argv[4] = BASH_RELEASE;
3714887Schin 			argv[5] = BASH_MACHTYPE;
3724887Schin 			argv[6] = 0;
3734887Schin 			nv_setvec(np, 0, 6, argv);
3744887Schin 			nv_onattr(np,NV_RDONLY);
3754887Schin 		}
3764887Schin 		return;
3774887Schin 	}
3784887Schin 
3794887Schin 	/* rest of init stage */
3804887Schin 
3814887Schin 	/* restrict BASH_ENV */
382*8462SApril.Chin@Sun.COM 	if(np=nv_open("BASH_ENV",shp->var_tree,0))
3834887Schin 	{
3844887Schin 		const Namdisc_t *dp = nv_discfun(NV_DCRESTRICT);
3854887Schin 		Namfun_t *fp = calloc(dp->dsize,1);
3864887Schin 		fp->disc = dp;
3874887Schin 		nv_disc(np, fp, 0);
3884887Schin 	}
3894887Schin 
3904887Schin 	/* open GLOBIGNORE node */
391*8462SApril.Chin@Sun.COM 	if(np=nv_open("GLOBIGNORE",shp->var_tree,0))
3924887Schin 	{
3934887Schin 		const Namdisc_t *dp = &SH_GLOBIGNORE_disc;
3944887Schin 		Namfun_t *fp = calloc(dp->dsize,1);
3954887Schin 		fp->disc = dp;
3964887Schin 		nv_disc(np, fp, 0);
3974887Schin 	}
3984887Schin 
3994887Schin 	/* set startup files */
4004887Schin 	n=0;
401*8462SApril.Chin@Sun.COM 	if(sh_isoption(SH_LOGIN_SHELL))
4024887Schin 	{
4034887Schin 		if(!sh_isoption(SH_POSIX))
4044887Schin 		{
4054887Schin 			login_files[n++] = (char*)e_bash_profile;
4064887Schin 			login_files[n++] = (char*)e_bash_login;
4074887Schin 		}
4084887Schin 		login_files[n++] = (char*)e_profile;
4094887Schin 	}
410*8462SApril.Chin@Sun.COM 	shp->login_files = login_files;
4114887Schin reinit:
4124887Schin 	xtrace = sh_isoption(SH_XTRACE);
4134887Schin 	sh_offoption(SH_XTRACE);
4144887Schin 	verbose = sh_isoption(SH_VERBOSE);
4154887Schin 	sh_offoption(SH_VERBOSE);
416*8462SApril.Chin@Sun.COM 	if(np = nv_open("SHELLOPTS", shp->var_tree, NV_NOADD))
4174887Schin 		nv_offattr(np,NV_RDONLY);
4184887Schin 	iop = sfopen(NULL, bash_pre_rc, "s");
4194887Schin 	sh_eval(iop,0);
4204887Schin 	if(xtrace)
4214887Schin 		sh_offoption(SH_XTRACE);
4224887Schin 	if(verbose)
4234887Schin 		sh_offoption(SH_VERBOSE);
4244887Schin }
425