xref: /titanic_41/usr/src/lib/libshell/common/sh/bash.c (revision cde2885fdf538266ee2a3b08dee2d5075ce8fa2b)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2008 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 /*
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 
41 extern const char	bash_pre_rc[];
42 
43 static char *login_files[4];
44 
45 const 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.]";
50 const 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 
69 const char sh_optshopt[] =
70 "+[-1c?\n@(#)$Id: shopt (AT&T Research) 2003-02-13 $\n]"
71 "[-author?Karsten Fleischer <K.Fleischer@omnium.de>]"
72 USAGE_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 
153 static 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 
163 const Namdisc_t SH_GLOBIGNORE_disc  = { sizeof(Namfun_t), put_globignore };
164 
165 /* FUNCNAME discipline */
166 
167 struct	funcname
168 {
169 	Namfun_t	hdr;
170 };
171 
172 static 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 
182 const 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 
190 int     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(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 
311 void bash_init(int mode)
312 {
313 	Shell_t		*shp = &sh;
314 	Sfio_t		*iop;
315 	Namval_t	*np;
316 	int		n=0,xtrace,verbose;
317 	if(mode>0)
318 		goto reinit;
319 	if(mode < 0)
320 	{
321 		/* termination code */
322 		if(sh_isoption(SH_LOGIN_SHELL) && !sh_isoption(SH_POSIX))
323 			sh_source(shp, NiL, sh_mactry(shp,(char*)e_bash_logout));
324 		return;
325 	}
326 
327 	if(sh_isstate(SH_PREINIT))
328 	{	/* pre-init stage */
329 		if(sh_isoption(SH_RESTRICTED))
330 			sh_onoption(SH_RESTRICTED2);
331 		sh_onoption(SH_HISTORY2);
332 		sh_onoption(SH_INTERACTIVE_COMM);
333 		sh_onoption(SH_SOURCEPATH);
334 		sh_onoption(SH_HISTAPPEND);
335 		sh_onoption(SH_CMDHIST);
336 		sh_onoption(SH_LITHIST);
337 		sh_onoption(SH_NOEMPTYCMDCOMPL);
338 		if(shp->login_sh==2)
339 			sh_onoption(SH_LOGIN_SHELL);
340 		if(strcmp(astconf("CONFORMANCE",0,0),"standard")==0)
341 			sh_onoption(SH_POSIX);
342 		if(strcmp(astconf("UNIVERSE",0,0),"att")==0)
343 			sh_onoption(SH_XPG_ECHO);
344 		else
345 			sh_offoption(SH_XPG_ECHO);
346 		if(strcmp(astconf("PATH_RESOLVE",0,0),"physical")==0)
347 			sh_onoption(SH_PHYSICAL);
348 		else
349 			sh_offoption(SH_PHYSICAL);
350 
351 		/* add builtins */
352 		sh_addbuiltin("shopt", b_shopt, &sh);
353 
354 		/* set up some variables needed for --version
355 		 * needs to go here because --version option is parsed before the init script.
356 		 */
357 		if(np=nv_open("HOSTTYPE",shp->var_tree,0))
358 			nv_putval(np, BASH_HOSTTYPE, NV_NOFREE);
359 		if(np=nv_open("MACHTYPE",shp->var_tree,0))
360 			nv_putval(np, BASH_MACHTYPE, NV_NOFREE);
361 		if(np=nv_open("BASH_VERSION",shp->var_tree,0))
362 			nv_putval(np, BASH_VERSION, NV_NOFREE);
363 		if(np=nv_open("BASH_VERSINFO",shp->var_tree,0))
364 		{
365 			char *argv[7];
366 			argv[0] = BASH_MAJOR;
367 			argv[1] = BASH_MINOR;
368 			argv[2] = BASH_PATCH;
369 			argv[3] = BASH_BUILD;
370 			argv[4] = BASH_RELEASE;
371 			argv[5] = BASH_MACHTYPE;
372 			argv[6] = 0;
373 			nv_setvec(np, 0, 6, argv);
374 			nv_onattr(np,NV_RDONLY);
375 		}
376 		return;
377 	}
378 
379 	/* rest of init stage */
380 
381 	/* restrict BASH_ENV */
382 	if(np=nv_open("BASH_ENV",shp->var_tree,0))
383 	{
384 		const Namdisc_t *dp = nv_discfun(NV_DCRESTRICT);
385 		Namfun_t *fp = calloc(dp->dsize,1);
386 		fp->disc = dp;
387 		nv_disc(np, fp, 0);
388 	}
389 
390 	/* open GLOBIGNORE node */
391 	if(np=nv_open("GLOBIGNORE",shp->var_tree,0))
392 	{
393 		const Namdisc_t *dp = &SH_GLOBIGNORE_disc;
394 		Namfun_t *fp = calloc(dp->dsize,1);
395 		fp->disc = dp;
396 		nv_disc(np, fp, 0);
397 	}
398 
399 	/* set startup files */
400 	n=0;
401 	if(sh_isoption(SH_LOGIN_SHELL))
402 	{
403 		if(!sh_isoption(SH_POSIX))
404 		{
405 			login_files[n++] = (char*)e_bash_profile;
406 			login_files[n++] = (char*)e_bash_login;
407 		}
408 		login_files[n++] = (char*)e_profile;
409 	}
410 	shp->login_files = login_files;
411 reinit:
412 	xtrace = sh_isoption(SH_XTRACE);
413 	sh_offoption(SH_XTRACE);
414 	verbose = sh_isoption(SH_VERBOSE);
415 	sh_offoption(SH_VERBOSE);
416 	if(np = nv_open("SHELLOPTS", shp->var_tree, NV_NOADD))
417 		nv_offattr(np,NV_RDONLY);
418 	iop = sfopen(NULL, bash_pre_rc, "s");
419 	sh_eval(iop,0);
420 	if(xtrace)
421 		sh_offoption(SH_XTRACE);
422 	if(verbose)
423 		sh_offoption(SH_VERBOSE);
424 }
425