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