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*                 Eclipse Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*          http://www.eclipse.org/org/documents/epl-v10.html           *
11*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20/*
21 * bash specific extensions
22 * originally provided by Karsten Fleischer
23 */
24
25#include "defs.h"
26#include "path.h"
27#include "io.h"
28#include "builtins.h"
29#include "name.h"
30
31#ifndef BASH_MAJOR
32#   define BASH_MAJOR	"1"
33#   define BASH_MINOR	"0"
34#   define BASH_PATCH	"0"
35#   define BASH_BUILD	"0"
36#   define BASH_RELEASE	"experimental"
37#endif
38#define BASH_VERSION	BASH_MAJOR "." BASH_MINOR "." BASH_PATCH "(" BASH_BUILD ")-" BASH_RELEASE
39
40
41extern const char	bash_pre_rc[];
42
43static char *login_files[4];
44
45const char sh_bash1[] =
46	"[B?Enable brace group expansion. This option is only availabe in bash "
47	"compatibility mode. In ksh mode, brace group expansion is always on.]"
48	"[P?Do not follow symbolic links, use physical directory structure "
49	"instead. Only available in bash compatibility mode.]";
50const char sh_bash2[] =
51"[O]:?[shopt_option?\ashopt_option\a is one of the shell options accepted by "
52	"the \bshopt\b builtin. If \ashopt_option\a is present, \b-O\b sets "
53	"the value of that option; \b+O\b unsets it. If \ashopt_option\a is "
54	"not supplied, the names and values of the shell options accepted by "
55	"\bshopt\b are printed on the standard output. If the invocation "
56	"option is \b+O\b, the output is displayed in a format that may be "
57	"reused as input. Only available if invoked as \bbash\b.]"
58"[01:init-file|rcfile]:[file?Execute commands from \afile\a instead of the "
59	"standard personal initialization file ~/.bashrc if the shell is "
60	"interactive. Only available if invoked as \bbash\b.]"
61"[02:editing?For option compatibility with \bbash\b only. Ignored.]"
62"[03:profile?Read either the system-wide startup file or any of the "
63	"personal initialization files. On by default for interactive "
64	"shells. Only available if invoked as \bbash\b.]"
65"[04:posix?If invoked as \bbash\b, turn on POSIX compatibility. \bBash\b in "
66	"POSIX mode is not the same as \bksh\b.]"
67"[05:version?Print version number and exit.]";
68
69const char sh_optshopt[] =
70"+[-1c?\n@(#)$Id: shopt (AT&T Research) 2003-02-13 $\n]"
71"[-author?Karsten Fleischer <K.Fleischer@omnium.de>]"
72USAGE_LICENSE
73"[+NAME?shopt - set/unset variables controlling optional shell behavior]"
74"[+DESCRIPTION?\bshopt\b sets or unsets variables controlling optional shell "
75	"behavior. With no options, or with the \b-p\b option, a list of all "
76	"settable options is displayed, with an indication of whether or not "
77	"each is set.]"
78"[p?Causes output to be displayed in a form that may be reused as input.]"
79"[s?Set each \aoptname\a.]"
80"[u?Unset each \aoptname\a.]"
81"[q?Suppress output (quiet mode). The return status indicates whether the "
82	"\aoptname\a is set or unset. If multiple \aoptname\a arguments are "
83	"given with \b-q\b, the return status is zero if all \aoptname\as are "
84	"enabled; non-zero otherwise.]"
85"[o?Restricts the values of \aoptname\a to be those defined for the \b-o\b "
86	"option to the set builtin.]"
87"[+?If either \b-s\b or \b-u\b is used with no \aoptname\a arguments, the "
88	"display is limited to those options which are set or unset.]"
89"[+?\bshopt\b supports all bash options. Some settings do not have any effect "
90	"or are are always on and cannot be changed.]"
91"[+?The value of \aoptname\a must be one of the following:]{"
92		"[+cdable_vars?If set, arguments to the \bcd\b command are "
93			"assumed to be names of variables whose values are to "
94			"be used if the usual \bcd\b proceeding fails.]"
95		"[+cdspell?Currently ignored.]"
96		"[+checkhash?Always on.]"
97		"[+checkwinsize?Currently ignored.]"
98		"[+cmdhist?Always on.]"
99		"[+dotglob?If set, include filenames beginning with a \b.\b "
100			"in the results of pathname expansion.]"
101		"[+execfail?Always on.]"
102		"[+expand_aliases?Always on.]"
103		"[+extglob?Enable extended pattern matching features.]"
104		"[+histappend?Always on.]"
105		"[+histreedit?If set and an edit mode is selected, the user "
106			"is given the opportunity to re-edit a failed history "
107			"substitution.]"
108		"[+histverify?If set and an edit mode is selected, the result "
109			"of a history substitution will not be executed "
110			"immediately but be placed in the edit buffer for "
111			"further modifications.]"
112		"[+hostcomplete?Currently ignored.]"
113		"[+huponexit?Currently ignored.]"
114		"[+interactive_comments?Always on.]"
115		"[+lithist?Always on.]"
116		"[+login_shell?This option is set if the shell is started as "
117			"a login shell. The value cannot be changed.]"
118		"[+mailwarn?Currently ignored.]"
119		"[+no_empty_cmd_completion?Always on.]"
120		"[+nocaseglob?Match filenames in a case-insensitive fashion "
121			"when performing filename expansion.]"
122		"[+nullglob?Allows filename patterns which match no files to "
123			"expand to a null string, rather than themselves.]"
124		"[+progcomp?Currently ignored.]"
125		"[+promptvars?Currently ignored.]"
126		"[+restricted_shell?This option is set if the shell is started "
127			"as a restricted shell. The value cannot be changed. "
128			"It is not reset during execution of startup files, "
129			"allowing the startup files to determine whether the "
130			"shell is restricted.]"
131		"[+shift_verbose?Currently ignored.]"
132		"[+sourcepath?If set, the \b.\b builtin uses the value of PATH "
133			"to find the directory containing the file supplied "
134			"as an argument.]"
135		"[+xpg_echo?If set, the \becho\b and \bprint\b builtins "
136			"expand backslash-escape sequences.]"
137"}"
138"\n"
139"\n[optname ...]\n"
140"\n"
141"[+EXIT STATUS?]{"
142	"[+?The return status when listing options is zero if all \aoptnames\a "
143	"are enabled, non-zero otherwise. When setting or unsetting options, "
144	"the return status is zero unless an \aoptname\a is not a valid shell "
145	"option.]"
146"}"
147
148"[+SEE ALSO?\bset\b(1)]"
149;
150
151/* GLOBIGNORE discipline. Turn on SH_DOTGLOB on set, turn off on unset. */
152
153static void put_globignore(register Namval_t* np, const char *val, int flags, Namfun_t *fp)
154{
155	if(val)
156		sh_onoption(SH_DOTGLOB);
157	else
158		sh_offoption(SH_DOTGLOB);
159
160	nv_putv(np,val,flags,fp);
161}
162
163const Namdisc_t SH_GLOBIGNORE_disc  = { sizeof(Namfun_t), put_globignore };
164
165/* FUNCNAME discipline */
166
167struct	funcname
168{
169	Namfun_t	hdr;
170};
171
172static void put_funcname(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
173{
174	/* bash silently returns with an error when FUNCNAME is set,
175	   unsetting FUNCNAME is allowed */
176	if(val && !(flags&NV_RDONLY))
177		error_info.exit(1);
178
179	nv_putv(np,val,flags,fp);
180}
181
182const Namdisc_t SH_FUNCNAME_disc  = { sizeof(struct funcname), put_funcname };
183
184#define	SET_SET		1
185#define	SET_UNSET	2
186#define	SET_NOARGS	4
187
188/* shopt builtin */
189
190int     b_shopt(int argc,register char *argv[],void *extra)
191{
192        Shell_t *shp = (Shell_t*)extra;
193	int n, f, ret=0;
194	Shopt_t newflags=shp->options, opt;
195	int verbose=PRINT_SHOPT|PRINT_ALL|PRINT_NO_HEADER|PRINT_VERBOSE;
196	int setflag=0, quietflag=0, oflag=0;
197	memset(&opt,0,sizeof(opt));
198#if SHOPT_RAWONLY
199	on_option(&newflags,SH_VIRAW);
200#endif
201	while((n = optget(argv,sh_optshopt)))
202	{
203		switch(n)
204		{
205		case 'p':
206			verbose&=~PRINT_VERBOSE;
207			break;
208		case 's':
209		case 'u':
210			setflag|=n=='s'?SET_SET:SET_UNSET;
211			if(setflag==(SET_SET|SET_UNSET))
212			{
213				errormsg(SH_DICT,ERROR_ERROR,"cannot set and unset options simultaneously");
214				error_info.errors++;
215			}
216			break;
217		case 'q':
218			quietflag=1;
219			break;
220		case 'o':
221			oflag=1;
222			verbose&=~PRINT_SHOPT;
223			break;
224		case ':':
225			errormsg(SH_DICT,2, "%s", opt_info.arg);
226			continue;
227		case '?':
228			errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
229			return(-1);
230		}
231	}
232	if(error_info.errors)
233		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
234	argc -= opt_info.index;
235	if(argc==0)
236	{
237		/* no args, -s => mask=current options, -u mask=~(current options)
238		   else mask=all bits */
239		if(setflag&SET_SET)
240			opt=newflags;
241		else if(setflag&SET_UNSET)
242			for(n=0;n<4;n++)
243				opt.v[n]=~newflags.v[n];
244		else
245			memset(&opt,0xff,sizeof(opt));
246		setflag=SET_NOARGS;
247	}
248	while(argc>0)
249	{
250		f=1;
251		n=sh_lookopt(argv[opt_info.index],&f);
252		if(n<=0||(setflag
253			&& (is_option(&opt,SH_INTERACTIVE)
254			    || is_option(&opt,SH_RESTRICTED)
255			    || is_option(&opt,SH_RESTRICTED2)
256			    || is_option(&opt,SH_BASH)
257			    || is_option(&opt,SH_LOGIN_SHELL)))
258			||(oflag&&(n&SH_BASHOPT)))
259		{
260			errormsg(SH_DICT,ERROR_ERROR, e_option, argv[opt_info.index]);
261			error_info.errors++;
262			ret=1;
263		}
264		else if(f)
265			on_option(&opt,n&0xff);
266		else
267			off_option(&opt,n&0xff);
268		opt_info.index++;
269		argc--;
270	}
271	if(setflag&(SET_SET|SET_UNSET))
272	{
273		if(setflag&SET_SET)
274		{
275			if(sh_isoption(SH_INTERACTIVE))
276				off_option(&opt,SH_NOEXEC);
277			if(is_option(&opt,SH_VI)||is_option(&opt,SH_EMACS)||is_option(&opt,SH_GMACS))
278			{
279				off_option(&newflags,SH_VI);
280				off_option(&newflags,SH_EMACS);
281				off_option(&newflags,SH_GMACS);
282			}
283			for(n=0;n<4;n++)
284				newflags.v[n] |= opt.v[n];
285		}
286		else if(setflag&SET_UNSET)
287			for(n=0;n<4;n++)
288				newflags.v[n] &= ~opt.v[n];
289		sh_applyopts(shp,newflags);
290		shp->options = newflags;
291		if(is_option(&newflags,SH_XTRACE))
292			sh_trace(shp,argv,1);
293	}
294	else if(!(setflag&SET_NOARGS)) /* no -s,-u but args, ret=0 if opt&mask==mask */
295	{
296		for(n=0;n<4;n++)
297			ret+=((newflags.v[n]&opt.v[n])!=opt.v[n]);
298	}
299	if(!quietflag&&!(setflag&(SET_SET|SET_UNSET)))
300		sh_printopts(newflags,verbose,&opt);
301	return(ret);
302}
303
304/* mode = 0: init, called two times
305        before parsing shell args with SH_PREINIT state turned on
306	second time after sh_init() is through and with SH_PREINIT state turned off
307   mode > 1: re-init
308   mode < 0: shutdown
309*/
310
311void bash_init(Shell_t *shp,int mode)
312{
313	Sfio_t		*iop;
314	Namval_t	*np;
315	int		n=0,xtrace,verbose;
316	if(mode>0)
317		goto reinit;
318	if(mode < 0)
319	{
320		/* termination code */
321		if(sh_isoption(SH_LOGIN_SHELL) && !sh_isoption(SH_POSIX))
322			sh_source(shp, NiL, sh_mactry(shp,(char*)e_bash_logout));
323		return;
324	}
325
326	if(sh_isstate(SH_PREINIT))
327	{	/* pre-init stage */
328		if(sh_isoption(SH_RESTRICTED))
329			sh_onoption(SH_RESTRICTED2);
330		sh_onoption(SH_HISTORY2);
331		sh_onoption(SH_INTERACTIVE_COMM);
332		sh_onoption(SH_SOURCEPATH);
333		sh_onoption(SH_HISTAPPEND);
334		sh_onoption(SH_CMDHIST);
335		sh_onoption(SH_LITHIST);
336		sh_onoption(SH_NOEMPTYCMDCOMPL);
337		if(shp->login_sh==2)
338			sh_onoption(SH_LOGIN_SHELL);
339		if(strcmp(astconf("CONFORMANCE",0,0),"standard")==0)
340			sh_onoption(SH_POSIX);
341		if(strcmp(astconf("UNIVERSE",0,0),"att")==0)
342			sh_onoption(SH_XPG_ECHO);
343		else
344			sh_offoption(SH_XPG_ECHO);
345		if(strcmp(astconf("PATH_RESOLVE",0,0),"physical")==0)
346			sh_onoption(SH_PHYSICAL);
347		else
348			sh_offoption(SH_PHYSICAL);
349
350		/* add builtins */
351		sh_addbuiltin("shopt", b_shopt, &sh);
352
353		/* set up some variables needed for --version
354		 * needs to go here because --version option is parsed before the init script.
355		 */
356		if(np=nv_open("HOSTTYPE",shp->var_tree,0))
357			nv_putval(np, BASH_HOSTTYPE, NV_NOFREE);
358		if(np=nv_open("MACHTYPE",shp->var_tree,0))
359			nv_putval(np, BASH_MACHTYPE, NV_NOFREE);
360		if(np=nv_open("BASH_VERSION",shp->var_tree,0))
361			nv_putval(np, BASH_VERSION, NV_NOFREE);
362		if(np=nv_open("BASH_VERSINFO",shp->var_tree,0))
363		{
364			char *argv[7];
365			argv[0] = BASH_MAJOR;
366			argv[1] = BASH_MINOR;
367			argv[2] = BASH_PATCH;
368			argv[3] = BASH_BUILD;
369			argv[4] = BASH_RELEASE;
370			argv[5] = BASH_MACHTYPE;
371			argv[6] = 0;
372			nv_setvec(np, 0, 6, argv);
373			nv_onattr(np,NV_RDONLY);
374		}
375		return;
376	}
377
378	/* rest of init stage */
379
380	/* restrict BASH_ENV */
381	if(np=nv_open("BASH_ENV",shp->var_tree,0))
382	{
383		const Namdisc_t *dp = nv_discfun(NV_DCRESTRICT);
384		Namfun_t *fp = calloc(dp->dsize,1);
385		fp->disc = dp;
386		nv_disc(np, fp, 0);
387	}
388
389	/* open GLOBIGNORE node */
390	if(np=nv_open("GLOBIGNORE",shp->var_tree,0))
391	{
392		const Namdisc_t *dp = &SH_GLOBIGNORE_disc;
393		Namfun_t *fp = calloc(dp->dsize,1);
394		fp->disc = dp;
395		nv_disc(np, fp, 0);
396	}
397
398	/* set startup files */
399	n=0;
400	if(sh_isoption(SH_LOGIN_SHELL))
401	{
402		if(!sh_isoption(SH_POSIX))
403		{
404			login_files[n++] = (char*)e_bash_profile;
405			login_files[n++] = (char*)e_bash_login;
406		}
407		login_files[n++] = (char*)e_profile;
408	}
409	shp->login_files = login_files;
410reinit:
411	xtrace = sh_isoption(SH_XTRACE);
412	sh_offoption(SH_XTRACE);
413	verbose = sh_isoption(SH_VERBOSE);
414	sh_offoption(SH_VERBOSE);
415	if(np = nv_open("SHELLOPTS", shp->var_tree, NV_NOADD))
416		nv_offattr(np,NV_RDONLY);
417	iop = sfopen(NULL, bash_pre_rc, "s");
418	sh_eval(iop,0);
419	if(xtrace)
420		sh_offoption(SH_XTRACE);
421	if(verbose)
422		sh_offoption(SH_VERBOSE);
423}
424