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