X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=shell.c;h=98d8303c0685c24e6c3387121432cf9ab61d564c;hb=f73dda092b33638d2d5e9c35375f687a607b5403;hp=4870f80d0f1c0586941790d159a06174b102b1af;hpb=e8ce775db824de329b81293b4e5d8fbd65624528;p=platform%2Fupstream%2Fbash.git diff --git a/shell.c b/shell.c index 4870f80..98d8303 100644 --- a/shell.c +++ b/shell.c @@ -1,30 +1,22 @@ -/* shell.c -- GNU's idea of the POSIX shell specification. +/* shell.c -- GNU's idea of the POSIX shell specification. */ - This file is part of Bash, the Bourne Again SHell. Bash is free - software; no one can prevent you from reading the source code, or - giving it to someone else. This file is copyrighted under the GNU - General Public License, which can be found in the file called - COPYING. +/* Copyright (C) 1987,1991 Free Software Foundation, Inc. - Copyright (C) 1988, 1991 Free Software Foundation, Inc. + This file is part of GNU Bash, the Bourne Again SHell. - This file is part of GNU Bash. + Bash is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. Bash is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY. No author or distributor accepts responsibility to - anyone for the consequences of using it or for whether it serves - any particular purpose or works at all, unless he says so in - writing. Refer to the GNU Emacs General Public License for full - details. + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. - Everyone is granted permission to copy, modify and redistribute - Bash, but only under the conditions described in the GNU General - Public License. A copy of this license is supposed to have been - given to you along with GNU Emacs so you can know your rights and - responsibilities. It should be in a file named COPYING. - - Among other things, the copyright notice and this notice must be - preserved on all copies. + You should have received a copy of the GNU General Public License + along with Bash; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. Birthdate: Sunday, January 10th, 1988. @@ -35,8 +27,11 @@ #include "config.h" #include "bashtypes.h" -#include +#ifndef _MINIX +# include +#endif #include "posixstat.h" +#include "posixtime.h" #include "bashansi.h" #include #include @@ -48,6 +43,8 @@ # include #endif +#define NEED_SH_SETLINEBUF_DECL /* used in externs.h */ + #include "shell.h" #include "flags.h" #include "trap.h" @@ -61,6 +58,7 @@ #include "input.h" #include "execute_cmd.h" +#include "findcmd.h" #if defined (HISTORY) # include "bashhist.h" @@ -68,7 +66,11 @@ #endif #include -#include +#include + +#if defined (__OPENNT) +# include +#endif #if !defined (HAVE_GETPW_DECLS) extern struct passwd *getpwuid (); @@ -78,14 +80,20 @@ extern struct passwd *getpwuid (); extern int errno; #endif +#if defined (NO_MAIN_ENV_ARG) +extern char **environ; /* used if no third argument to main() */ +#endif + extern char *dist_version, *release_status; extern int patch_level, build_version; +extern int shell_level; extern int subshell_environment; extern int last_command_exit_value; extern int line_number; extern char *primary_prompt, *secondary_prompt; extern int expand_aliases; extern char *this_command_name; +extern int array_needs_making; /* Non-zero means that this shell has already been run; i.e. you should call shell_reinitialize () if you need to start afresh. */ @@ -96,7 +104,8 @@ COMMAND *global_command = (COMMAND *)NULL; /* Information about the current user. */ struct user_info current_user = { - -1, -1, -1, -1, (char *)NULL, (char *)NULL, (char *)NULL + (uid_t)-1, (uid_t)-1, (gid_t)-1, (gid_t)-1, + (char *)NULL, (char *)NULL, (char *)NULL }; /* The current host's name. */ @@ -106,7 +115,7 @@ char *current_host_name = (char *)NULL; Specifically: 0 = not login shell. 1 = login shell from getty (or equivalent fake out) - -1 = login shell from "-login" flag. + -1 = login shell from "--login" flag. -2 = both from getty, and from flag. */ int login_shell = 0; @@ -119,6 +128,10 @@ int interactive = 0; /* Non-zero means that the shell was started as an interactive shell. */ int interactive_shell = 0; +/* Non-zero means to send a SIGHUP to all jobs when an interactive login + shell exits. */ +int hup_on_exit = 0; + /* Tells what state the shell was in when it started: 0 = non-interactive shell script 1 = interactive @@ -177,6 +190,8 @@ static int want_initial_help; /* --help option */ int no_line_editing = 0; /* Don't do fancy line editing. */ int posixly_correct = 0; /* Non-zero means posix.2 superset. */ int dump_translatable_strings; /* Dump strings in $"...", don't execute. */ +int dump_po_strings; /* Dump strings in $"..." in po format */ +int wordexp_only = 0; /* Do word expansion only */ /* Some long-winded argument names. These are obviously new. */ #define Int 1 @@ -188,8 +203,10 @@ struct { char **char_value; } long_args[] = { { "debug", Int, &debugging, (char **)0x0 }, + { "dump-po-strings", Int, &dump_po_strings, (char **)0x0 }, { "dump-strings", Int, &dump_translatable_strings, (char **)0x0 }, { "help", Int, &want_initial_help, (char **)0x0 }, + { "init-file", Charp, (int *)0x0, &bashrc_file }, { "login", Int, &make_login_shell, (char **)0x0 }, { "noediting", Int, &no_line_editing, (char **)0x0 }, { "noprofile", Int, &no_profile, (char **)0x0 }, @@ -201,6 +218,7 @@ struct { #endif { "verbose", Int, &echo_input_at_read, (char **)0x0 }, { "version", Int, &do_version, (char **)0x0 }, + { "wordexp", Int, &wordexp_only, (char **)0x0 }, { (char *)0x0, Int, (int *)0x0, (char **)0x0 } }; @@ -218,47 +236,116 @@ char **subshell_envp; int default_buffered_input = -1; #endif -static int read_from_stdin; /* -s flag supplied */ -static int want_pending_command; /* -c flag supplied */ +/* The following two variables are not static so they can show up in $-. */ +int read_from_stdin; /* -s flag supplied */ +int want_pending_command; /* -c flag supplied */ + +static int shell_reinitialized = 0; static char *local_pending_command; static FILE *default_input; -static int parse_long_options (); -static int parse_shell_options (); -static void run_startup_files (); -static int bind_args (); -static int open_shell_script (); -static void set_bash_input (); -static int run_one_command (); +static STRING_INT_ALIST *shopt_alist; +static int shopt_ind = 0, shopt_len = 0; + +static int parse_long_options __P((char **, int, int)); +static int parse_shell_options __P((char **, int, int)); +static int bind_args __P((char **, int, int, int)); + +static void add_shopt_to_alist __P((char *, int)); +static void run_shopt_alist __P((void)); + +static void execute_env_file __P((char *)); +static void run_startup_files __P((void)); +static int open_shell_script __P((char *)); +static void set_bash_input __P((void)); +static int run_one_command __P((char *)); +static int run_wordexp __P((char *)); + +static int uidget __P((void)); +static int isnetconn __P((int)); + +static void init_interactive __P((void)); +static void init_noninteractive __P((void)); -static int uidget (); -static int isnetconn (); +static void set_shell_name __P((char *)); +static void shell_initialize __P((void)); +static void shell_reinitialize __P((void)); -static void init_interactive (), init_noninteractive (); -static void set_shell_name (); -static void shell_initialize (); -static void shell_reinitialize (); +static void show_shell_usage __P((FILE *, int)); -static void show_shell_usage (); +#ifdef __CYGWIN__ +static void +_cygwin32_check_tmp () +{ + struct stat sb; + + if (stat ("/tmp", &sb) < 0) + internal_warning ("could not find /tmp, please create!"); + else + { + if (S_ISDIR (sb.st_mode) == 0) + internal_warning ("/tmp must be a valid directory name"); + } +} +#endif /* __CYGWIN__ */ +#if defined (NO_MAIN_ENV_ARG) +/* systems without third argument to main() */ +int +main (argc, argv) + int argc; + char **argv; +#else /* !NO_MAIN_ENV_ARG */ int main (argc, argv, env) int argc; char **argv, **env; +#endif /* !NO_MAIN_ENV_ARG */ { register int i; - int code, saverst; + int code, old_errexit_flag; +#if defined (RESTRICTED_SHELL) + int saverst; +#endif volatile int locally_skip_execution; volatile int arg_index, top_level_arg_index; +#ifdef __OPENNT + char **env; + + env = environ; +#endif /* __OPENNT */ + + USE_VAR(argc); + USE_VAR(argv); + USE_VAR(env); + USE_VAR(code); + USE_VAR(old_errexit_flag); +#if defined (RESTRICTED_SHELL) + USE_VAR(saverst); +#endif /* Catch early SIGINTs. */ code = setjmp (top_level); if (code) exit (2); +#if defined (USING_BASH_MALLOC) && defined (DEBUG) +# if 0 /* memory tracing */ + malloc_set_trace(1); +# endif + +# if 0 + malloc_set_register (1); +# endif +#endif + check_dev_tty (); +#ifdef __CYGWIN__ + _cygwin32_check_tmp (); +#endif /* __CYGWIN__ */ + /* Wait forever if we are debugging a login shell. */ while (debugging_login_shell); @@ -266,8 +353,8 @@ main (argc, argv, env) running_setuid = uidget (); - posixly_correct = (getenv ("POSIXLY_CORRECT") != (char *)NULL) || - (getenv ("POSIX_PEDANTIC") != (char *)NULL); + if (getenv ("POSIXLY_CORRECT") || getenv ("POSIX_PEDANTIC")) + posixly_correct = 1; #if defined (USE_GNU_MALLOC_LIBRARY) mcheck (programming_error, (void (*) ())0); @@ -281,6 +368,8 @@ main (argc, argv, env) sourced_env = 0; } + shell_reinitialized = 0; + /* Initialize `local' variables for all `invocations' of main (). */ arg_index = 1; local_pending_command = (char *)NULL; @@ -335,10 +424,15 @@ main (argc, argv, env) login_shell = -login_shell; } + set_login_shell (login_shell != 0); + /* All done with full word options; do standard shell option parsing.*/ this_command_name = shell_name; /* for error reporting */ arg_index = parse_shell_options (argv, arg_index, argc); + if (dump_po_strings) + dump_translatable_strings = 1; + if (dump_translatable_strings) read_but_dont_execute = 1; @@ -371,6 +465,7 @@ main (argc, argv, env) if (forced_interactive || /* -i flag */ (!local_pending_command && /* No -c command and ... */ + wordexp_only == 0 && /* No --wordexp and ... */ ((arg_index == argc) || /* no remaining args or... */ read_from_stdin) && /* -s flag with args, and */ isatty (fileno (stdin)) && /* Input is a terminal and */ @@ -399,13 +494,14 @@ main (argc, argv, env) other Posix.2 things. */ if (posixly_correct) { - posix_initialize (posixly_correct); -#if defined (READLINE) - if (interactive_shell) - posix_readline_initialize (posixly_correct); -#endif + bind_variable ("POSIXLY_CORRECT", "y"); + sv_strict_posix ("POSIXLY_CORRECT"); } + /* Now we run the shopt_alist and process the options. */ + if (shopt_alist) + run_shopt_alist (); + /* From here on in, the shell must be a normal functioning shell. Variables from the environment are expected to be set, etc. */ shell_initialize (); @@ -419,11 +515,12 @@ main (argc, argv, env) term = getenv ("TERM"); no_line_editing |= term && (STREQ (term, "emacs")); term = getenv ("EMACS"); - running_under_emacs = term ? ((fnmatch ("*term*", term, 0) == 0) ? 2 : 1) + running_under_emacs = term ? ((strmatch ("*term*", term, 0) == 0) ? 2 : 1) : 0; } top_level_arg_index = arg_index; + old_errexit_flag = exit_immediately_on_error; /* Give this shell a place to longjmp to before executing the startup files. This allows users to press C-c to abort the @@ -439,6 +536,9 @@ main (argc, argv, env) /* Reset job control, since run_startup_files turned it off. */ set_job_control (interactive_shell); #endif + /* Reset value of `set -e', since it's turned off before running + the startup files. */ + exit_immediately_on_error += old_errexit_flag; locally_skip_execution++; } } @@ -451,7 +551,8 @@ main (argc, argv, env) { makunbound ("PS1", shell_variables); makunbound ("PS2", shell_variables); - interactive = expand_aliases = 0; + interactive = 0; + expand_aliases = posixly_correct; } else { @@ -460,39 +561,56 @@ main (argc, argv, env) } #if defined (RESTRICTED_SHELL) + /* Set restricted_shell based on whether the basename of $0 indicates that + the shell should be restricted or if the `-r' option was supplied at + startup. */ + restricted_shell = shell_is_restricted (shell_name); + /* If the `-r' option is supplied at invocation, make sure that the shell is not in restricted mode when running the startup files. */ - saverst = restricted; - restricted = 0; + saverst = restricted; + restricted = 0; #endif + /* The startup files are run with `set -e' temporarily disabled. */ if (locally_skip_execution == 0 && running_setuid == 0) - run_startup_files (); + { + old_errexit_flag = exit_immediately_on_error; + exit_immediately_on_error = 0; + + run_startup_files (); + exit_immediately_on_error += old_errexit_flag; + } /* If we are invoked as `sh', turn on Posix mode. */ if (act_like_sh) { - posix_initialize (posixly_correct = 1); -#if defined (READLINE) - if (interactive_shell) - posix_readline_initialize (posixly_correct); -#endif + bind_variable ("POSIXLY_CORRECT", "y"); + sv_strict_posix ("POSIXLY_CORRECT"); } #if defined (RESTRICTED_SHELL) - /* Turn on the restrictions after parsing the startup files. This + /* Turn on the restrictions after executing the startup files. This means that `bash -r' or `set -r' invoked from a startup file will turn on the restrictions after the startup files are executed. */ restricted = saverst || restricted; - maybe_make_restricted (shell_name); + if (shell_reinitialized == 0) + maybe_make_restricted (shell_name); #endif /* RESTRICTED_SHELL */ + if (wordexp_only) + { + startup_state = 3; + last_command_exit_value = run_wordexp (argv[arg_index]); + exit_shell (last_command_exit_value); + } + if (local_pending_command) { arg_index = bind_args (argv, arg_index, argc, 0); - startup_state = 2; #if defined (ONESHOT) + executing = 1; run_one_command (local_pending_command); exit_shell (last_command_exit_value); #else /* ONESHOT */ @@ -521,7 +639,6 @@ main (argc, argv, env) /* Bind remaining args to $1 ... $n */ arg_index = bind_args (argv, arg_index, argc, 1); - /* Do the things that should be done only for interactive shells. */ if (interactive_shell) { @@ -650,7 +767,7 @@ parse_shell_options (argv, arg_start, arg_end) o_option = argv[next_arg]; if (o_option == 0) { - list_minus_o_opts (-1); + list_minus_o_opts (-1, (on_or_off == '-') ? 0 : 1); break; } if (set_minus_o_option (on_or_off, o_option) != EXECUTION_SUCCESS) @@ -658,6 +775,21 @@ parse_shell_options (argv, arg_start, arg_end) next_arg++; break; + case 'O': + /* Since some of these can be overridden by the normal + interactive/non-interactive shell initialization or + initializing posix mode, we save the options and process + them after initialization. */ + o_option = argv[next_arg]; + if (o_option == 0) + { + shopt_listopt (o_option, (on_or_off == '-') ? 0 : 1); + break; + } + add_shopt_to_alist (o_option, on_or_off); + next_arg++; + break; + case 'D': dump_translatable_strings = 1; break; @@ -680,7 +812,7 @@ parse_shell_options (argv, arg_start, arg_end) } /* Exit the shell with status S. */ -int +void exit_shell (s) int s; { @@ -699,15 +831,34 @@ exit_shell (s) #endif /* HISTORY */ #if defined (JOB_CONTROL) + /* If the user has run `shopt -s huponexit', hangup all jobs when we exit + an interactive login shell. ksh does this unconditionally. */ + if (interactive_shell && login_shell && hup_on_exit) + hangup_all_jobs (); + /* If this shell is interactive, terminate all stopped jobs and - restore the original terminal process group. */ - end_job_control (); + restore the original terminal process group. Don't do this if we're + in a subshell and calling exit_shell after, for example, a failed + word expansion. */ + if (subshell_environment == 0) + end_job_control (); #endif /* JOB_CONTROL */ /* Always return the exit status of the last command to our parent. */ exit (s); } +#ifdef INCLUDE_UNUSED +/* A wrapper for exit that (optionally) can do other things, like malloc + statistics tracing. */ +void +sh_exit (s) + int s; +{ + exit (s); +} +#endif + /* Source the bash startup files. If POSIXLY_CORRECT is non-zero, we obey the Posix.2 startup file rules: $ENV is expanded, and if the file it names exists, that file is sourced. The Posix.2 rules are in effect @@ -723,16 +874,16 @@ exit_shell (s) then: - COMMAND EXECUTE BASHRC - -------------------------------- - bash -c foo NO - bash foo NO - foo NO - rsh machine ls YES (for rsh, which calls `bash -c') - rsh machine foo YES (for shell started by rsh) NO (for foo!) - echo ls | bash NO - login NO - bash YES + COMMAND EXECUTE BASHRC + -------------------------------- + bash -c foo NO + bash foo NO + foo NO + rsh machine ls YES (for rsh, which calls `bash -c') + rsh machine foo YES (for shell started by rsh) NO (for foo!) + echo ls | bash NO + login NO + bash YES */ static void @@ -740,20 +891,13 @@ execute_env_file (env_file) char *env_file; { char *fn; - WORD_LIST *list; if (env_file && *env_file) { - list = expand_string_unsplit (env_file, Q_DOUBLE_QUOTES); - if (list) - { - fn = string_list (list); - dispose_words (list); - - if (fn && *fn) - maybe_execute_file (fn, 1); - FREE (fn); - } + fn = expand_string_unsplit_to_string (env_file, Q_DOUBLE_QUOTES); + if (fn && *fn) + maybe_execute_file (fn, 1); + FREE (fn); } } @@ -763,16 +907,69 @@ run_startup_files () #if defined (JOB_CONTROL) int old_job_control; #endif + int sourced_login, run_by_ssh; - /* get the rshd case out of the way first. */ + /* get the rshd/sshd case out of the way first. */ if (interactive_shell == 0 && no_rc == 0 && login_shell == 0 && - act_like_sh == 0 && local_pending_command && isnetconn (fileno (stdin))) + act_like_sh == 0 && local_pending_command) { +#ifdef SSH_SOURCE_BASHRC + run_by_ssh = (find_variable ("SSH_CLIENT") != (SHELL_VAR *)0) || + (find_variable ("SSH2_CLIENT") != (SHELL_VAR *)0); +#else + run_by_ssh = 0; +#endif + + /* If we were run by sshd or we think we were run by rshd, execute + ~/.bashrc if we are a top-level shell. */ + if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2) + { #ifdef SYS_BASHRC - maybe_execute_file (SYS_BASHRC, 1); +# if defined (__OPENNT) + maybe_execute_file (_prefixInstallPath(SYS_BASHRC, NULL, 0), 1); +# else + maybe_execute_file (SYS_BASHRC, 1); +# endif #endif - maybe_execute_file (bashrc_file, 1); - return; + maybe_execute_file (bashrc_file, 1); + return; + } + } + +#if defined (JOB_CONTROL) + /* Startup files should be run without job control enabled. */ + old_job_control = interactive_shell ? set_job_control (0) : 0; +#endif + + sourced_login = 0; + + /* A shell begun with the --login flag that is not in posix mode runs + the login shell startup files, no matter whether or not it is + interactive. If NON_INTERACTIVE_LOGIN_SHELLS is defined, run the + startup files if argv[0][0] == '-' as well. */ +#if defined (NON_INTERACTIVE_LOGIN_SHELLS) + if (login_shell && posixly_correct == 0) +#else + if (login_shell < 0 && posixly_correct == 0) +#endif + { + /* We don't execute .bashrc for login shells. */ + no_rc++; + + /* Execute /etc/profile and one of the personal login shell + initialization files. */ + if (no_profile == 0) + { + maybe_execute_file (SYS_PROFILE, 1); + + if (act_like_sh) /* sh */ + maybe_execute_file ("~/.profile", 1); + else if ((maybe_execute_file ("~/.bash_profile", 1) == 0) && + (maybe_execute_file ("~/.bash_login", 1) == 0)) /* bash */ + maybe_execute_file ("~/.profile", 1); + } + + sourced_login = 1; } /* A non-interactive shell not named `sh' and not in posix mode reads and @@ -787,48 +984,49 @@ run_startup_files () return; } -#if defined (JOB_CONTROL) - /* Startup files should be run without job control enabled. */ - old_job_control = set_job_control (0); -#endif - /* Interactive shell or `-su' shell. */ if (posixly_correct == 0) /* bash, sh */ { - /* We don't execute .bashrc for login shells. */ - if (login_shell) - no_rc++; + if (login_shell && sourced_login++ == 0) + { + /* We don't execute .bashrc for login shells. */ + no_rc++; - /* Execute /etc/profile and one of the personal login shell - initialization files. */ - if (login_shell && no_profile == 0) - { - maybe_execute_file (SYS_PROFILE, 1); + /* Execute /etc/profile and one of the personal login shell + initialization files. */ + if (no_profile == 0) + { + maybe_execute_file (SYS_PROFILE, 1); - if (act_like_sh) /* sh */ - maybe_execute_file ("~/.profile", 1); - else if ((maybe_execute_file ("~/.bash_profile", 1) == 0) && - (maybe_execute_file ("~/.bash_login", 1) == 0)) /* bash */ - maybe_execute_file ("~/.profile", 1); - } + if (act_like_sh) /* sh */ + maybe_execute_file ("~/.profile", 1); + else if ((maybe_execute_file ("~/.bash_profile", 1) == 0) && + (maybe_execute_file ("~/.bash_login", 1) == 0)) /* bash */ + maybe_execute_file ("~/.profile", 1); + } + } /* bash */ if (act_like_sh == 0 && no_rc == 0) { #ifdef SYS_BASHRC +# if defined (__OPENNT) + maybe_execute_file (_prefixInstallPath(SYS_BASHRC, NULL, 0), 1); +# else maybe_execute_file (SYS_BASHRC, 1); +# endif #endif - maybe_execute_file (bashrc_file, 1); + maybe_execute_file (bashrc_file, 1); } /* sh */ else if (act_like_sh && privileged_mode == 0 && sourced_env++ == 0) - execute_env_file (get_string_value ("ENV")); + execute_env_file (get_string_value ("ENV")); } else /* bash --posix, sh --posix */ { /* bash and sh */ if (interactive_shell && privileged_mode == 0 && sourced_env++ == 0) - execute_env_file (get_string_value ("ENV")); + execute_env_file (get_string_value ("ENV")); } #if defined (JOB_CONTROL) @@ -837,10 +1035,26 @@ run_startup_files () } #if defined (RESTRICTED_SHELL) +/* Return 1 if the shell should be a restricted one based on NAME or the + value of `restricted'. Don't actually do anything, just return a + boolean value. */ +int +shell_is_restricted (name) + char *name; +{ + char *temp; + + if (restricted) + return 1; + temp = base_pathname (name); + return (STREQ (temp, RESTRICTED_SHELL_NAME)); +} + /* Perhaps make this shell a `restricted' one, based on NAME. If the basename of NAME is "rbash", then this shell is restricted. The name of the restricted shell is a configurable option, see config.h. - In a restricted shell, PATH and SHELL are read-only and non-unsettable. + In a restricted shell, PATH, SHELL, ENV, and BASH_ENV are read-only + and non-unsettable. Do this also if `restricted' is already set to 1; maybe the shell was started with -r. */ int @@ -854,7 +1068,9 @@ maybe_make_restricted (name) { set_var_read_only ("PATH"); set_var_read_only ("SHELL"); - restricted++; + set_var_read_only ("ENV"); + set_var_read_only ("BASH_ENV"); + restricted = 1; } return (restricted); } @@ -894,6 +1110,74 @@ disable_priv_mode () current_user.egid = current_user.gid; } +static int +run_wordexp (words) + char *words; +{ + int code, nw, nb; + WORD_LIST *wl, *result; + + code = setjmp (top_level); + + if (code != NOT_JUMPED) + { + switch (code) + { + /* Some kind of throw to top_level has occured. */ + case FORCE_EOF: + return last_command_exit_value = 127; + case EXITPROG: + return last_command_exit_value; + case DISCARD: + return last_command_exit_value = 1; + default: + command_error ("run_wordexp", CMDERR_BADJUMP, code, 0); + } + } + + /* Run it through the parser to get a list of words and expand them */ + if (words && *words) + { + with_input_from_string (words, "--wordexp"); + if (parse_command () != 0) + return (126); + if (global_command == 0) + { + printf ("0\n0\n"); + return (0); + } + if (global_command->type != cm_simple) + return (126); + wl = global_command->value.Simple->words; + result = wl ? expand_words_no_vars (wl) : (WORD_LIST *)0; + } + else + result = (WORD_LIST *)0; + + last_command_exit_value = 0; + + if (result == 0) + { + printf ("0\n0\n"); + return (0); + } + + /* Count up the number of words and bytes, and print them. Don't count + the trailing NUL byte. */ + for (nw = nb = 0, wl = result; wl; wl = wl->next) + { + nw++; + nb += strlen (wl->word->word); + } + printf ("%u\n%u\n", nw, nb); + /* Print each word on a separate line. This will have to be changed when + the interface to glibc is completed. */ + for (wl = result; wl; wl = wl->next) + printf ("%s\n", wl->word->word); + + return (0); +} + #if defined (ONESHOT) /* Run one command, given as the argument to the -c option. Tell parse_and_execute not to fork for a simple command. */ @@ -920,7 +1204,7 @@ run_one_command (command) case DISCARD: return last_command_exit_value = 1; default: - programming_error ("run_one_command: bad jump: code %d", code); + command_error ("run_one_command", CMDERR_BADJUMP, code, 0); } } return (parse_and_execute (savestring (command), "-c", SEVAL_NOHIST)); @@ -950,7 +1234,7 @@ bind_args (argv, arg_start, arg_end, start_index) remember_args (args->next, 1); } else /* bind to $1...$n for shell script */ - remember_args (args, 1); + remember_args (args, 1); dispose_words (args); } @@ -968,9 +1252,9 @@ static int open_shell_script (script_name) char *script_name; { - int fd, e; + int fd, e, fd_is_tty; char *filename, *path_filename; - unsigned char sample[80]; + char sample[80]; int sample_len; struct stat sb; @@ -1002,8 +1286,14 @@ open_shell_script (script_name) exit ((e == ENOENT) ? EX_NOTFOUND : EX_NOINPUT); } - /* Only do this with file descriptors we can seek on. */ - if (lseek (fd, 0L, 1) != -1) +#ifdef HAVE_DEV_FD + fd_is_tty = isatty (fd); +#else + fd_is_tty = 0; +#endif + + /* Only do this with non-tty file descriptors we can seek on. */ + if (fd_is_tty == 0 && (lseek (fd, 0L, 1) != -1)) { /* Check to see if the `file' in `bash file' is a binary file according to the same tests done by execute_simple_command (), @@ -1014,7 +1304,7 @@ open_shell_script (script_name) e = errno; if ((fstat (fd, &sb) == 0) && S_ISDIR (sb.st_mode)) internal_error ("%s: is a directory", filename); - else + else { errno = e; file_error (filename); @@ -1030,23 +1320,19 @@ open_shell_script (script_name) lseek (fd, 0L, 0); } -#if defined (BUFFERED_INPUT) - default_buffered_input = fd; -# if 0 - /* This is never executed. */ - if (default_buffered_input == -1) - { - file_error (filename); - exit (EX_NOTFOUND); - } -# endif - SET_CLOSE_ON_EXEC (default_buffered_input); -#else /* !BUFFERED_INPUT */ /* Open the script. But try to move the file descriptor to a randomly large one, in the hopes that any descriptors used by the script will - not match with ours. */ + not match with ours. */ fd = move_to_high_fd (fd, 0, -1); +#if defined (__CYGWIN__) && defined (O_TEXT) + setmode (fd, O_TEXT); +#endif + +#if defined (BUFFERED_INPUT) + default_buffered_input = fd; + SET_CLOSE_ON_EXEC (default_buffered_input); +#else /* !BUFFERED_INPUT */ default_input = fdopen (fd, "r"); if (default_input == 0) @@ -1060,13 +1346,10 @@ open_shell_script (script_name) SET_CLOSE_ON_EXEC (fileno (default_input)); #endif /* !BUFFERED_INPUT */ - if (interactive_shell == 0 || isatty (fd) == 0) - /* XXX - does this really need to be called again here? */ - init_noninteractive (); - else + /* Just about the only way for this code to be executed is if something + like `bash -i /dev/stdin' is executed. */ + if (interactive_shell && fd_is_tty) { - /* I don't believe that this code is ever executed, even in - the presence of /dev/fd. */ dup2 (fd, 0); close (fd); fd = 0; @@ -1077,6 +1360,12 @@ open_shell_script (script_name) default_input = stdin; #endif } + else if (forced_interactive && fd_is_tty == 0) + /* But if a script is called with something like `bash -i scriptname', + we need to do a non-interactive setup here, since we didn't do it + before. */ + init_noninteractive (); + free (filename); return (fd); } @@ -1089,10 +1378,10 @@ set_bash_input () no-delay mode. */ #if defined (BUFFERED_INPUT) if (interactive == 0) - unset_nodelay_mode (default_buffered_input); + sh_unset_nodelay_mode (default_buffered_input); else #endif /* !BUFFERED_INPUT */ - unset_nodelay_mode (fileno (stdin)); + sh_unset_nodelay_mode (fileno (stdin)); /* with_input_from_stdin really means `with_input_from_readline' */ if (interactive && no_line_editing == 0) @@ -1110,6 +1399,31 @@ set_bash_input () #endif /* !BUFFERED_INPUT */ } +/* Close the current shell script input source and forget about it. This is + extern so execute_cmd.c:initialize_subshell() can call it. If CHECK_ZERO + is non-zero, we close default_buffered_input even if it's the standard + input (fd 0). */ +void +unset_bash_input (check_zero) + int check_zero; +{ +#if defined (BUFFERED_INPUT) + if ((check_zero && default_buffered_input >= 0) || + (check_zero == 0 && default_buffered_input > 0)) + { + close_buffered_fd (default_buffered_input); + default_buffered_input = bash_input.location.buffered_fd = -1; + } +#else /* !BUFFERED_INPUT */ + if (default_input) + { + fclose (default_input); + default_input = (FILE *)NULL; + } +#endif /* !BUFFERED_INPUT */ +} + + #if !defined (PROGRAM) # define PROGRAM "bash" #endif @@ -1201,8 +1515,11 @@ shell_initialize () char hostname[256]; /* Line buffer output for stderr and stdout. */ - setlinebuf (stderr); - setlinebuf (stdout); + if (shell_initialized == 0) + { + sh_setlinebuf (stderr); + sh_setlinebuf (stdout); + } /* Sort the array of shell builtins so that the binary search in find_shell_builtin () works correctly. */ @@ -1254,8 +1571,14 @@ shell_initialize () /* Initialize input streams to null. */ initialize_bash_input (); - /* Initialize the shell options. */ - initialize_shell_options (); + /* Initialize the shell options. Don't import the shell options + from the environment variable $SHELLOPTS if we are running in + privileged or restricted mode or if the shell is running setuid. */ +#if defined (RESTRICTED_SHELL) + initialize_shell_options (privileged_mode||restricted||running_setuid); +#else + initialize_shell_options (privileged_mode||running_setuid); +#endif } /* Function called by main () when it appears that the shell has already @@ -1300,10 +1623,7 @@ shell_reinitialize () delete_all_variables (shell_variables); delete_all_variables (shell_functions); -#if 0 - /* Pretend the PATH variable has changed. */ - flush_hashed_filenames (); -#endif + shell_reinitialized = 1; } static void @@ -1323,7 +1643,7 @@ show_shell_usage (fp, extra) fprintf (fp, "\t--%s\n", long_args[i].name); fputs ("Shell options:\n", fp); - fputs ("\t-irsD or -c command\t\t(invocation only)\n", fp); + fputs ("\t-irsD or -c command or -O shopt_option\t\t(invocation only)\n", fp); for (i = 0, set_opts = 0; shell_builtins[i].name; i++) if (STREQ (shell_builtins[i].name, "set")) @@ -1350,6 +1670,34 @@ show_shell_usage (fp, extra) } } +static void +add_shopt_to_alist (opt, on_or_off) + char *opt; + int on_or_off; +{ + if (shopt_ind >= shopt_len) + { + shopt_len += 8; + shopt_alist = (STRING_INT_ALIST *)xrealloc (shopt_alist, shopt_len * sizeof (shopt_alist[0])); + } + shopt_alist[shopt_ind].word = opt; + shopt_alist[shopt_ind].token = on_or_off; + shopt_ind++; +} + +static void +run_shopt_alist () +{ + register int i; + + for (i = 0; i < shopt_ind; i++) + if (shopt_setopt (shopt_alist[i].word, (shopt_alist[i].token == '-')) != EXECUTION_SUCCESS) + exit (EX_USAGE); + free (shopt_alist); + shopt_alist = 0; + shopt_ind = shopt_len = 0; +} + /* The second and subsequent conditions must match those used to decide whether or not to call getpeername() in isnetconn(). */ #if defined (HAVE_SYS_SOCKET_H) && defined (HAVE_GETPEERNAME) && !defined (SVR4_2) @@ -1361,15 +1709,16 @@ static int isnetconn (fd) int fd; { -#if defined (HAVE_GETPEERNAME) && !defined (SVR4_2) - int rv, l; +#if defined (HAVE_GETPEERNAME) && !defined (SVR4_2) && !defined (__BEOS__) + int rv; + socklen_t l; struct sockaddr sa; l = sizeof(sa); - rv = getpeername(0, &sa, &l); + rv = getpeername(fd, &sa, &l); /* Solaris 2.5 getpeername() returns EINVAL if the fd is not a socket. */ return ((rv < 0 && (errno == ENOTSOCK || errno == EINVAL)) ? 0 : 1); -#else /* !HAVE_GETPEERNAME || SVR4_2 */ +#else /* !HAVE_GETPEERNAME || SVR4_2 || __BEOS__ */ # if defined (SVR4) || defined (SVR4_2) /* Sockets on SVR4 and SVR4.2 are character special (streams) devices. */ struct stat sb; @@ -1384,15 +1733,15 @@ isnetconn (fd) # endif /* S_ISFIFO */ return (S_ISCHR (sb.st_mode)); # else /* !SVR4 && !SVR4_2 */ -# if defined (S_ISSOCK) +# if defined (S_ISSOCK) && !defined (__BEOS__) struct stat sb; if (fstat (fd, &sb) < 0) return (0); return (S_ISSOCK (sb.st_mode)); -# else /* !S_ISSOCK */ +# else /* !S_ISSOCK || __BEOS__ */ return (0); -# endif /* !S_ISSOCK */ +# endif /* !S_ISSOCK || __BEOS__ */ # endif /* !SVR4 && !SVR4_2 */ -#endif /* !HAVE_GETPEERNAME || SVR4_2 */ +#endif /* !HAVE_GETPEERNAME || SVR4_2 || __BEOS__ */ }