aboutsummaryrefslogtreecommitdiffstats
path: root/shell.c
diff options
context:
space:
mode:
authorJari Aalto <jari.aalto@cante.net>1996-08-26 18:22:31 +0000
committerJari Aalto <jari.aalto@cante.net>2009-09-12 16:46:49 +0000
commit726f63884db0132f01745f1fb4465e6621088ccf (patch)
tree6c2f7765a890a97e0e513cb539df43283a8f7c4d /shell.c
downloadandroid_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.tar.gz
android_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.tar.bz2
android_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.zip
Imported from ../bash-1.14.7.tar.gz.
Diffstat (limited to 'shell.c')
-rw-r--r--shell.c1792
1 files changed, 1792 insertions, 0 deletions
diff --git a/shell.c b/shell.c
new file mode 100644
index 0000000..26b20c0
--- /dev/null
+++ b/shell.c
@@ -0,0 +1,1792 @@
+/* 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) 1988, 1991 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+
+ 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.
+
+ 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.
+
+ Birthdate:
+ Sunday, January 10th, 1988.
+ Initial author: Brian Fox
+*/
+#define INSTALL_DEBUG_MODE
+
+#include "bashtypes.h"
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/file.h>
+#include "filecntl.h"
+#include <pwd.h>
+#include "posixstat.h"
+#include "bashansi.h"
+
+#if defined (HAVE_VARARGS_H)
+#include <varargs.h>
+#endif
+
+#include "shell.h"
+#include "flags.h"
+
+#if defined (JOB_CONTROL)
+#include "jobs.h"
+#endif /* JOB_CONTROL */
+
+#include "input.h"
+#include "execute_cmd.h"
+
+#if defined (HISTORY)
+# include "bashhist.h"
+# include <readline/history.h>
+#endif
+
+#include <tilde/tilde.h>
+
+#if defined (USG) && !defined (HAVE_GETPW_DECLS)
+extern struct passwd *getpwuid ();
+#endif /* USG && !HAVE_GETPW_DECLS */
+
+extern int yydebug;
+#if !defined (errno)
+extern int errno;
+#endif
+
+extern char *dist_version;
+extern int patch_level, build_version;
+extern int subshell_environment; /* Found in execute_cmd.c. */
+extern int last_command_exit_value;
+extern int return_catch_flag;
+extern jmp_buf return_catch;
+extern int need_here_doc, current_command_line_count, line_number;
+extern char *ps1_prompt, **prompt_string_pointer;
+extern int loop_level, continuing, breaking;
+extern int parse_and_execute_level;
+extern char *this_command_name;
+
+/* Non-zero means that this shell has already been run; i.e. you should
+ call shell_reinitialize () if you need to start afresh. */
+static int shell_initialized = 0;
+static int sourced_env = 0;
+
+/* The current maintainer of the shell. You change this in the
+ Makefile. */
+#if !defined (MAINTAINER)
+#define MAINTAINER "bash-maintainers@prep.ai.mit.edu"
+#endif
+
+char *the_current_maintainer = MAINTAINER;
+
+char *primary_prompt = PPROMPT;
+char *secondary_prompt = SPROMPT;
+
+COMMAND *global_command = (COMMAND *)NULL;
+
+/* Non-zero after SIGINT. */
+int interrupt_state = 0;
+
+/* Information about the current user. */
+struct user_info current_user =
+{
+ -1, -1, -1, -1, (char *)NULL, (char *)NULL, (char *)NULL
+};
+
+/* The current host's name. */
+char *current_host_name = (char *)NULL;
+
+/* Non-zero means that this shell is a login shell.
+ Specifically:
+ 0 = not login shell.
+ 1 = login shell from getty (or equivalent fake out)
+ -1 = login shell from "-login" flag.
+ -2 = both from getty, and from flag.
+ */
+int login_shell = 0;
+
+/* Non-zero means that at this moment, the shell is interactive. In
+ general, this means that the shell is at this moment reading input
+ from the keyboard. */
+int interactive = 0;
+
+/* Non-zero means that the shell was started as an interactive shell. */
+int interactive_shell = 0;
+
+/* Tells what state the shell was in when it started:
+ 0 = non-interactive shell script
+ 1 = interactive
+ 2 = -c command
+ This is a superset of the information provided by interactive_shell.
+*/
+int startup_state = 0;
+
+/* Special debugging helper. */
+int debugging_login_shell = 0;
+
+/* The environment that the shell passes to other commands. */
+char **shell_environment;
+
+/* Non-zero when we are executing a top-level command. */
+int executing = 0;
+
+/* The number of commands executed so far. */
+int current_command_number = 1;
+
+/* The environment at the top-level REP loop. We use this in the case of
+ error return. */
+jmp_buf top_level, catch;
+
+#if defined (JOB_CONTROL) || defined (_POSIX_VERSION)
+/* The signal masks that this shell runs with. */
+sigset_t top_level_mask;
+#endif /* JOB_CONTROL */
+
+/* Non-zero is the recursion depth for commands. */
+int indirection_level = 0;
+
+/* The number of times BASH has been executed. This is set
+ by initialize_variables () in variables.c. */
+int shell_level = 0;
+
+/* The name of this shell, as taken from argv[0]. */
+char *shell_name = (char *)NULL;
+
+/* time in seconds when the shell was started */
+time_t shell_start_time;
+
+/* The name of the .(shell)rc file. */
+static char *bashrc_file = "~/.bashrc";
+
+/* Non-zero means to act more like the Bourne shell on startup. */
+static int act_like_sh = 0;
+
+/* Values for the long-winded argument names. */
+static int debugging = 0; /* Do debugging things. */
+static int no_rc = 0; /* Don't execute ~/.bashrc */
+static int no_profile = 0; /* Don't execute .profile */
+static int do_version = 0; /* Display interesting version info. */
+static int quiet = 0; /* Be quiet when starting up. */
+static int make_login_shell = 0; /* Make this shell be a `-bash' shell. */
+
+int no_line_editing = 0; /* Don't do fancy line editing. */
+int no_brace_expansion = 0; /* Non-zero means no foo{a,b} -> fooa foob. */
+
+int posixly_correct = 0; /* Non-zero means posix.2 superset. */
+
+/* Some long-winded argument names. These are obviously new. */
+#define Int 1
+#define Charp 2
+struct {
+ char *name;
+ int type;
+ int *int_value;
+ char **char_value;
+} long_args[] = {
+ { "debug", Int, &debugging, (char **)0x0 },
+ { "norc", Int, &no_rc, (char **)0x0 },
+ { "noprofile", Int, &no_profile, (char **)0x0 },
+ { "rcfile", Charp, (int *)0x0, &bashrc_file },
+ { "version", Int, &do_version, (char **)0x0 },
+ { "quiet", Int, &quiet, (char **)0x0 },
+ { "login", Int, &make_login_shell, (char **)0x0 },
+ { "nolineediting", Int, &no_line_editing, (char **)0x0 },
+ { "nobraceexpansion", Int, &no_brace_expansion, (char **)0x0 },
+ { "posix", Int, &posixly_correct, (char **)0x0 },
+ { (char *)0x0, Int, (int *)0x0, (char **)0x0 }
+};
+
+/* These are extern so execute_simple_command can set them, and then
+ longjmp back to main to execute a shell script, instead of calling
+ main () again and resulting in indefinite, possibly fatal, stack
+ growth. */
+jmp_buf subshell_top_level;
+int subshell_argc;
+char **subshell_argv;
+char **subshell_envp;
+
+#if defined (BUFFERED_INPUT)
+/* The file descriptor from which the shell is reading input. */
+int default_buffered_input = -1;
+#endif
+
+static int want_pending_command;
+static char *local_pending_command;
+
+static int isnetconn ();
+static void run_startup_files ();
+
+static void shell_initialize ();
+static void shell_reinitialize ();
+static void initialize_signals ();
+static void initialize_terminating_signals ();
+
+main (argc, argv, env)
+ int argc;
+ char **argv, **env;
+{
+ register int i;
+ int arg_index, locally_skip_execution;
+ int top_level_arg_index, read_from_stdin;
+ FILE *default_input;
+
+ /* There is a bug in the NeXT 2.1 rlogind that causes opens
+ of /dev/tty to fail. */
+#if defined (RLOGIN_PGRP_BUG)
+ {
+ int tty_fd;
+
+ tty_fd = open ("/dev/tty", O_RDWR);
+
+ if (tty_fd < 0)
+ {
+ char *tty;
+ tty = (char *)ttyname (fileno (stdin));
+ tty_fd = open (tty, O_RDWR);
+ }
+ close (tty_fd);
+ }
+#endif /* RLOGIN_PGRP_BUG */
+
+ /* Wait forever if we are debugging a login shell. */
+ while (debugging_login_shell);
+
+ current_user.uid = getuid ();
+ current_user.gid = getgid ();
+ current_user.euid = geteuid ();
+ current_user.egid = getegid ();
+
+ /* See whether or not we are running setuid or setgid. */
+ privileged_mode = (current_user.uid != current_user.euid) ||
+ (current_user.gid != current_user.egid);
+
+ posixly_correct = (getenv ("POSIXLY_CORRECT") != (char *)NULL) ||
+ (getenv ("POSIX_PEDANTIC") != (char *)NULL);
+
+#if defined (USE_GNU_MALLOC_LIBRARY)
+ mcheck (programming_error, (void (*) ())0);
+#endif /* USE_GNU_MALLOC_LIBRARY */
+
+ if (setjmp (subshell_top_level))
+ {
+ argc = subshell_argc;
+ argv = subshell_argv;
+ env = subshell_envp;
+ sourced_env = 0;
+ }
+
+ /* Initialize local variables for all `invocations' of main (). */
+ arg_index = 1;
+ local_pending_command = (char *)NULL;
+ want_pending_command = 0;
+ locally_skip_execution = 0;
+ read_from_stdin = 0;
+ default_input = stdin;
+#if defined (BUFFERED_INPUT)
+ default_buffered_input = -1;
+#endif
+
+ /* Fix for the `infinite process creation' bug when running shell scripts
+ from startup files on System V. */
+ login_shell = make_login_shell = 0;
+
+ /* If this shell has already been run, then reinitialize it to a
+ vanilla state. */
+ if (shell_initialized || shell_name)
+ {
+ /* Make sure that we do not infinitely recurse as a login shell. */
+ if (*shell_name == '-')
+ shell_name++;
+
+ shell_reinitialize ();
+ if (setjmp (top_level))
+ exit (2);
+ }
+
+ /* Here's a hack. If the name of this shell is "sh", then don't do
+ any startup files; just try to be more like /bin/sh. */
+ /* XXX - next version - make this be the same as -posix. */
+ shell_name = base_pathname (argv[0]);
+ if (*shell_name == '-')
+ shell_name++;
+ if (shell_name[0] == 's' && shell_name[1] == 'h' && !shell_name[2])
+ act_like_sh++;
+
+ yydebug = 0;
+
+ shell_environment = env;
+ shell_name = argv[0];
+ dollar_vars[0] = savestring (shell_name);
+
+ if (*shell_name == '-')
+ {
+ shell_name++;
+ login_shell++;
+ }
+
+#if defined (JOB_CONTROL)
+ if (act_like_sh)
+ job_control = 0; /* XXX - not posix */
+#endif /* JOB_CONTROL */
+
+ shell_start_time = NOW; /* NOW now defined in general.h */
+
+ /* A program may start an interactive shell with
+ "execl ("/bin/bash", "-", NULL)".
+ If so, default the name of this shell to our name. */
+ if (!shell_name || !*shell_name || (shell_name[0] == '-' && !shell_name[1]))
+ shell_name = "bash";
+
+ /* Parse argument flags from the input line. */
+
+ /* Find full word arguments first. */
+ while ((arg_index != argc) && *(argv[arg_index]) == '-')
+ {
+ for (i = 0; long_args[i].name; i++)
+ {
+ if (STREQ (&(argv[arg_index][1]), long_args[i].name))
+ {
+ if (long_args[i].type == Int)
+ *long_args[i].int_value = 1;
+ else
+ {
+ if (!argv[++arg_index])
+ {
+ report_error ("option `%s' expected an argument",
+ long_args[i].name);
+ exit (1);
+ }
+ else
+ *long_args[i].char_value = argv[arg_index];
+ }
+ goto handle_next_arg;
+ }
+ }
+ break; /* No such argument. Maybe flag arg. */
+ handle_next_arg:
+ arg_index++;
+ }
+
+ /* If we're in a strict Posix.2 mode, turn on interactive comments. */
+ if (posixly_correct)
+ interactive_comments = 1;
+
+ /* If user supplied the "-login" flag, then set and invert LOGIN_SHELL. */
+ if (make_login_shell)
+ {
+ login_shell++;
+ login_shell = -login_shell;
+ }
+
+ /* All done with full word options; do standard shell option parsing.*/
+ this_command_name = shell_name; /* for error reporting */
+ while (arg_index != argc && argv[arg_index] &&
+ (*argv[arg_index] == '-' || *argv[arg_index] == '+'))
+ {
+ /* There are flag arguments, so parse them. */
+ int arg_character, on_or_off, next_arg;
+ char *o_option, *arg_string;
+
+ i = 1;
+ next_arg = arg_index + 1;
+ arg_string = argv[arg_index];
+ on_or_off = arg_string[0];
+
+ /* A single `-' signals the end of options. From the 4.3 BSD sh.
+ An option `--' means the same thing; this is the standard
+ getopt(3) meaning. */
+ if (arg_string[0] == '-' &&
+ (arg_string[1] == '\0' ||
+ (arg_string[1] == '-' && arg_string[2] == '\0')))
+ {
+ arg_index++;
+ break;
+ }
+
+ while (arg_character = arg_string[i++])
+ {
+ switch (arg_character)
+ {
+ case 'c':
+ want_pending_command = 1;
+ break;
+
+ case 's':
+ read_from_stdin = 1;
+ break;
+
+ case 'o':
+ o_option = argv[next_arg];
+ if (!o_option)
+ {
+ list_minus_o_opts ();
+ break;
+ }
+ if (set_minus_o_option (on_or_off, o_option) != EXECUTION_SUCCESS)
+ exit (1);
+ next_arg++;
+ break;
+
+ default:
+ if (change_flag (arg_character, on_or_off) == FLAG_ERROR)
+ {
+ report_error ("%c%c: bad option", on_or_off, arg_character);
+ exit (1);
+ }
+
+ }
+ }
+ /* Can't do just a simple increment anymore -- what about
+ "bash -abouo emacs ignoreeof -hP"? */
+ arg_index = next_arg;
+ }
+
+ /* Need to get the argument to a -c option processed in the
+ above loop. The next arg is a command to execute, and the
+ following args are $0...$n respectively. */
+ if (want_pending_command)
+ {
+ local_pending_command = argv[arg_index];
+ if (!local_pending_command)
+ {
+ report_error ("`-c' requires an argument");
+ exit (1);
+ }
+ arg_index++;
+ }
+ this_command_name = (char *)NULL;
+
+ /* First, let the outside world know about our interactive status.
+ A shell is interactive if the `-i' flag was given, or if all of
+ the following conditions are met:
+ no -c command
+ no arguments remaining or the -s flag given
+ standard input is a terminal
+ standard output is a terminal
+ Refer to Posix.2, the description of the `sh' utility. */
+
+ if (forced_interactive || /* -i flag */
+ (!local_pending_command && /* No -c command and ... */
+ ((arg_index == argc) || /* no remaining args or... */
+ read_from_stdin) && /* -s flag with args, and */
+ isatty (fileno (stdin)) && /* Input is a terminal and */
+ isatty (fileno (stdout)))) /* output is a terminal. */
+ {
+ interactive_shell = startup_state = interactive = 1;
+ }
+ else
+ {
+#if defined (HISTORY)
+# if defined (BANG_HISTORY)
+ history_expansion = 0;
+# endif
+ remember_on_history = 0;
+#endif /* HISTORY */
+ interactive_shell = startup_state = interactive = 0;
+ no_line_editing = 1;
+#if defined (JOB_CONTROL)
+ job_control = 0;
+#endif /* JOB_CONTROL */
+ }
+
+#define CLOSE_FDS_AT_LOGIN
+#if defined (CLOSE_FDS_AT_LOGIN)
+ /*
+ * Some systems have the bad habit of starting login shells with lots of open
+ * file descriptors. For instance, most systems that have picked up the
+ * pre-4.0 Sun YP code leave a file descriptor open each time you call one
+ * of the getpw* functions, and it's set to be open across execs. That
+ * means one for login, one for xterm, one for shelltool, etc.
+ */
+ if (login_shell && interactive_shell)
+ {
+ for (i = 3; i < 20; i++)
+ close (i);
+ }
+#endif /* CLOSE_FDS_AT_LOGIN */
+
+ /* From here on in, the shell must be a normal functioning shell.
+ Variables from the environment are expected to be set, etc. */
+ shell_initialize ();
+
+ if (interactive_shell)
+ {
+ char *term = getenv ("TERM");
+ no_line_editing |= term && (STREQ (term, "emacs"));
+ }
+
+ top_level_arg_index = arg_index;
+
+ if (!quiet && do_version)
+ show_shell_version ();
+
+ /* Give this shell a place to longjmp to before executing the
+ startup files. This allows users to press C-c to abort the
+ lengthy startup. */
+ {
+ int code;
+
+ code = setjmp (top_level);
+
+ if (code)
+ {
+ if (code == EXITPROG)
+ goto exit_shell;
+ else
+ locally_skip_execution++;
+ }
+ }
+
+ arg_index = top_level_arg_index;
+
+ /* Execute the start-up scripts. */
+
+ if (!interactive_shell)
+ {
+ makunbound ("PS1", shell_variables);
+ makunbound ("PS2", shell_variables);
+ interactive = 0;
+ }
+ else
+ {
+ change_flag ('i', FLAG_ON);
+ interactive = 1;
+ }
+
+ if (!locally_skip_execution)
+ run_startup_files ();
+
+#if defined (RESTRICTED_SHELL)
+ /* I turn on the restrictions afterwards because it is explictly
+ stated in the POSIX spec that PATH cannot be set in a restricted
+ shell, except in .profile. */
+ maybe_make_restricted (shell_name);
+#endif /* RESTRICTED_SHELL */
+
+ if (local_pending_command)
+ {
+ /* Bind remaining args to $0 ... $n */
+ WORD_LIST *args = (WORD_LIST *)NULL;
+ while (arg_index != argc)
+ args = make_word_list (make_word (argv[arg_index++]), args);
+ if (args)
+ {
+ args = REVERSE_LIST (args, WORD_LIST *);
+ /* Posix.2 4.56.3 says that the first argument after
+ sh -c command becomes $0, and the rest of the arguments
+ are bound to $1 ... $N. */
+ shell_name = savestring (args->word->word); /* XXX */
+ dollar_vars[0] = savestring (args->word->word);
+ remember_args (args->next, 1);
+ dispose_words (args);
+ }
+
+ startup_state = 2;
+#if defined (ONESHOT)
+ run_one_command (local_pending_command);
+ goto exit_shell;
+#else /* ONESHOT */
+ with_input_from_string (local_pending_command, "-c");
+ goto read_and_execute;
+#endif /* !ONESHOT */
+ }
+
+ /* Do the things that should be done only for interactive shells. */
+ if (interactive_shell)
+ {
+ /* Set up for checking for presence of mail. */
+ remember_mail_dates ();
+ reset_mail_timer ();
+
+#if defined (HISTORY)
+ /* Initialize the interactive history stuff. */
+ if (!shell_initialized)
+ load_history ();
+#endif /* HISTORY */
+
+ /* Initialize terminal state for interactive shells after the
+ .bash_profile and .bashrc are interpreted. */
+ get_tty_state ();
+ }
+
+ /* Get possible input filename. */
+ if ((arg_index != argc) && !read_from_stdin)
+ {
+ int fd;
+ char *filename;
+
+ free (dollar_vars[0]);
+ dollar_vars[0] = savestring (argv[arg_index]);
+ filename = savestring (argv[arg_index]);
+
+ fd = open (filename, O_RDONLY);
+ if ((fd < 0) && (errno == ENOENT) && (absolute_program (filename) == 0))
+ {
+ char *path_filename;
+ /* If it's not in the current directory, try looking through PATH
+ for it. */
+ path_filename = find_path_file (argv[arg_index]);
+ if (path_filename)
+ {
+ free (filename);
+ filename = path_filename;
+ fd = open (filename, O_RDONLY);
+ }
+ }
+
+ arg_index++;
+ if (fd < 0)
+ {
+ file_error (filename);
+ exit (1);
+ }
+
+ /* Only do this with file descriptors we can seek on. */
+ if (lseek (fd, 0L, 1) != -1)
+ {
+ unsigned char sample[80];
+ int sample_len;
+
+ /* Check to see if the `file' in `bash file' is a binary file
+ according to the same tests done by execute_simple_command (),
+ and report an error and exit if it is. */
+ sample_len = read (fd, sample, sizeof (sample));
+ if (sample_len > 0 && (check_binary_file (sample, sample_len)))
+ {
+ report_error ("%s: cannot execute binary file", filename);
+ exit (EX_BINARY_FILE);
+ }
+ /* Now rewind the file back to the beginning. */
+ lseek (fd, 0L, 0);
+ }
+
+#if defined (BUFFERED_INPUT)
+ default_buffered_input = fd;
+ if (default_buffered_input == -1)
+ {
+ file_error (filename);
+ exit (127);
+ }
+ 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. */
+ {
+ int script_fd, nfds;
+
+ nfds = getdtablesize ();
+ if (nfds <= 0)
+ nfds = 20;
+ if (nfds > 256)
+ nfds = 256;
+ script_fd = dup2 (fd, nfds - 1);
+ if (script_fd)
+ {
+ close (fd);
+ fd = script_fd;
+ }
+ }
+
+ default_input = fdopen (fd, "r");
+
+ if (!default_input)
+ {
+ file_error (filename);
+ exit (127);
+ }
+
+ SET_CLOSE_ON_EXEC (fd);
+ if (fileno (default_input) != fd)
+ SET_CLOSE_ON_EXEC (fileno (default_input));
+
+#endif /* !BUFFERED_INPUT */
+
+ if (!interactive_shell || (!isatty (fd)))
+ {
+#if defined (HISTORY)
+# if defined (BANG_HISTORY)
+ history_expansion = 0;
+# endif
+ remember_on_history = 0;
+#endif /* HISTORY */
+ interactive = interactive_shell = 0;
+ no_line_editing = 1;
+#if defined (JOB_CONTROL)
+ set_job_control (0);
+#endif /* JOB_CONTROL */
+ }
+ else
+ {
+ /* I don't believe that this code is ever executed, even in
+ the presence of /dev/fd. */
+ dup2 (fd, 0);
+ close (fd);
+ fclose (default_input);
+ }
+ }
+ else if (!interactive)
+ /* In this mode, bash is reading a script from stdin, which is a
+ pipe or redirected file. */
+#if defined (BUFFERED_INPUT)
+ default_buffered_input = fileno (stdin); /* == 0 */
+#else
+ setbuf (default_input, (char *)NULL);
+#endif /* !BUFFERED_INPUT */
+
+ /* Bind remaining args to $1 ... $n */
+ {
+ WORD_LIST *args = (WORD_LIST *)NULL;
+ while (arg_index != argc)
+ args = make_word_list (make_word (argv[arg_index++]), args);
+ args = REVERSE_LIST (args, WORD_LIST *);
+ remember_args (args, 1);
+ dispose_words (args);
+ }
+
+#if defined (BUFFERED_INPUT)
+ if (!interactive)
+ unset_nodelay_mode (default_buffered_input);
+ else
+ unset_nodelay_mode (fileno (stdin));
+#else
+ unset_nodelay_mode (fileno (stdin));
+#endif /* !BUFFERED_INPUT */
+
+ /* with_input_from_stdin really means `with_input_from_readline' */
+ if (interactive && !no_line_editing)
+ with_input_from_stdin ();
+ else
+#if defined (BUFFERED_INPUT)
+ {
+ if (!interactive)
+ with_input_from_buffered_stream (default_buffered_input, dollar_vars[0]);
+ else
+ with_input_from_stream (default_input, dollar_vars[0]);
+ }
+#else /* !BUFFERED_INPUT */
+ with_input_from_stream (default_input, dollar_vars[0]);
+#endif /* !BUFFERED_INPUT */
+
+#if !defined (ONESHOT)
+ read_and_execute:
+#endif /* !ONESHOT */
+
+ shell_initialized = 1;
+
+ /* Read commands until exit condition. */
+ reader_loop ();
+
+ exit_shell:
+ /* Do trap[0] if defined. */
+ if (signal_is_trapped (0))
+ last_command_exit_value = run_exit_trap ();
+
+#if defined (PROCESS_SUBSTITUTION)
+ unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+#if defined (HISTORY)
+ if (interactive_shell)
+ maybe_save_shell_history ();
+#endif /* HISTORY */
+
+#if defined (JOB_CONTROL)
+ /* If this shell is interactive, terminate all stopped jobs and
+ restore the original terminal process group. */
+ end_job_control ();
+#endif /* JOB_CONTROL */
+
+ /* Always return the exit status of the last command to our parent. */
+ exit (last_command_exit_value);
+}
+
+#if !defined (SYS_PROFILE)
+# define SYS_PROFILE "/etc/profile"
+#endif /* !SYS_PROFILE */
+
+/* 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
+ for both interactive and non-interactive shells (section 4.56.5.3) */
+static void
+run_startup_files ()
+{
+ if (!posixly_correct)
+ {
+ if (login_shell)
+ {
+ /* We don't execute .bashrc for login shells. */
+ no_rc++;
+ if (no_profile == 0)
+ maybe_execute_file (SYS_PROFILE, 1);
+ }
+
+ if (login_shell && !no_profile)
+ {
+ if (act_like_sh)
+ maybe_execute_file ("~/.profile", 1);
+ else
+ {
+ if (maybe_execute_file ("~/.bash_profile", 1) == 0)
+ if (maybe_execute_file ("~/.bash_login", 1) == 0)
+ maybe_execute_file ("~/.profile", 1);
+ }
+ }
+
+ /* Execute ~/.bashrc for most shells. Never execute it if
+ ACT_LIKE_SH is set, or if NO_RC is set.
+
+ If the executable file "/usr/gnu/src/bash/foo" contains:
+
+ #!/usr/gnu/bin/bash
+ echo hello
+
+ 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
+ */
+ if (!act_like_sh && !no_rc &&
+ (interactive_shell || (isnetconn (fileno (stdin)) &&
+ local_pending_command)))
+ maybe_execute_file (bashrc_file, 1);
+ }
+
+ /* Try a TMB suggestion. If running a script, then execute the
+ file mentioned in the ENV variable. */
+ if (!privileged_mode && sourced_env++ == 0 && act_like_sh == 0 &&
+ (posixly_correct || !interactive_shell))
+ {
+ char *env_file = (char *)NULL;
+
+ if (!posixly_correct)
+ env_file = getenv ("BASH_ENV");
+ if (!env_file)
+ env_file = getenv ("ENV");
+
+ if (env_file && *env_file)
+ {
+ WORD_LIST *list;
+ char *expanded_file_name;
+
+ list = expand_string_unsplit (env_file, 1);
+ if (list)
+ {
+ expanded_file_name = string_list (list);
+ dispose_words (list);
+
+ if (expanded_file_name && *expanded_file_name)
+ maybe_execute_file (expanded_file_name, 1);
+
+ if (expanded_file_name)
+ free (expanded_file_name);
+ }
+ }
+ }
+}
+
+#if defined (RESTRICTED_SHELL)
+/* Perhaps make this shell a `restricted' one, based on NAME.
+ If the basename of NAME is "rbash", then this shell is restricted.
+ In a restricted shell, PATH and SHELL are read-only and non-unsettable.
+ Do this also if `restricted' is already set to 1; maybe the shell was
+ started with -r. */
+maybe_make_restricted (name)
+ char *name;
+{
+ char *temp;
+
+ temp = base_pathname (shell_name);
+ if (restricted || (STREQ (temp, "rbash")))
+ {
+ set_var_read_only ("PATH");
+ non_unsettable ("PATH");
+ set_var_read_only ("SHELL");
+ non_unsettable ("SHELL");
+ restricted++;
+ }
+}
+#endif /* RESTRICTED_SHELL */
+
+/* Try to execute the contents of FNAME. If FNAME doesn't exist,
+ that is not an error, but other kinds of errors are. A non-zero
+ FORCE_NONINTERACTIVE means to set the value of `interactive' to
+ 0 so things like job control are disabled; the value is unchanged
+ otherwise. Returns -1 in the case of an error, 0 in the case that
+ the file was not found, and 1 if the file was found and executed. */
+maybe_execute_file (fname, force_noninteractive)
+ char *fname;
+ int force_noninteractive;
+{
+ jmp_buf old_return_catch;
+ int return_val, fd, tresult, old_interactive;
+ char *filename, *string;
+ struct stat file_info;
+
+ filename = tilde_expand (fname);
+ fd = open (filename, O_RDONLY);
+
+ if (fd < 0)
+ {
+file_error_and_exit:
+ if (errno != ENOENT)
+ file_error (filename);
+ free (filename);
+ return ((errno == ENOENT) ? 0 : -1);
+ }
+
+ if (fstat (fd, &file_info) == -1)
+ goto file_error_and_exit;
+
+ if (S_ISDIR (file_info.st_mode))
+ {
+ internal_error ("%s: cannot execute directories", filename);
+ free (filename);
+ return -1;
+ }
+
+ string = (char *)xmalloc (1 + (int)file_info.st_size);
+ tresult = read (fd, string, file_info.st_size);
+
+ {
+ int tt = errno;
+ close (fd);
+ errno = tt;
+ }
+
+ if (tresult != file_info.st_size)
+ {
+ free (string);
+ goto file_error_and_exit;
+ }
+ string[file_info.st_size] = '\0';
+
+ return_catch_flag++;
+ xbcopy ((char *)return_catch, (char *)old_return_catch, sizeof (jmp_buf));
+
+ if (force_noninteractive)
+ {
+ old_interactive = interactive;
+ interactive = 0;
+ }
+
+ return_val = setjmp (return_catch);
+
+ /* If `return' was seen outside of a function, but in the script, then
+ force parse_and_execute () to clean up. */
+ if (return_val)
+ parse_and_execute_cleanup ();
+ else
+ tresult = parse_and_execute (string, filename, -1);
+
+ if (force_noninteractive)
+ interactive = old_interactive;
+
+ return_catch_flag--;
+ xbcopy ((char *)old_return_catch, (char *)return_catch, sizeof (jmp_buf));
+
+ free (filename);
+
+ return (1);
+}
+
+#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. */
+run_one_command (command)
+ char *command;
+{
+ int code;
+
+ code = setjmp (top_level);
+
+ if (code != NOT_JUMPED)
+ {
+#if defined (PROCESS_SUBSTITUTION)
+ unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+ 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:
+ programming_error ("Bad jump %d", code);
+ }
+ }
+ return (parse_and_execute (savestring (command), "-c", -1));
+}
+#endif /* ONESHOT */
+
+reader_loop ()
+{
+ int our_indirection_level;
+ COMMAND *current_command = (COMMAND *)NULL;
+
+ our_indirection_level = ++indirection_level;
+
+ while (!EOF_Reached)
+ {
+ int code;
+
+ code = setjmp (top_level);
+
+#if defined (PROCESS_SUBSTITUTION)
+ unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+ if (interactive_shell && signal_is_ignored (SIGINT) == 0)
+ set_signal_handler (SIGINT, sigint_sighandler);
+
+ if (code != NOT_JUMPED)
+ {
+ indirection_level = our_indirection_level;
+
+ switch (code)
+ {
+ /* Some kind of throw to top_level has occured. */
+ case FORCE_EOF:
+ case EXITPROG:
+ current_command = (COMMAND *)NULL;
+ EOF_Reached = EOF;
+ goto exec_done;
+
+ case DISCARD:
+ /* Obstack free command elements, etc. */
+ if (current_command)
+ {
+ dispose_command (current_command);
+ current_command = (COMMAND *)NULL;
+ }
+ last_command_exit_value = 1;
+ break;
+
+ default:
+ programming_error ("Bad jump %d", code);
+ }
+ }
+
+ executing = 0;
+ dispose_used_env_vars ();
+
+#if (defined (Ultrix) && defined (mips)) || !defined (HAVE_ALLOCA)
+ /* Attempt to reclaim memory allocated with alloca (). */
+ (void) alloca (0);
+#endif
+
+ if (read_command () == 0)
+ {
+ if (global_command)
+ {
+ current_command = global_command;
+
+ current_command_number++;
+
+ /* POSIX spec: "-n: The shell reads commands but does
+ not execute them; this can be used to check for shell
+ script syntax errors. The shell ignores the -n option
+ for interactive shells. " */
+ if (interactive_shell || !read_but_dont_execute)
+ {
+ executing = 1;
+ execute_command (current_command);
+ }
+
+ exec_done:
+ if (current_command)
+ {
+ dispose_command (current_command);
+ current_command = (COMMAND *)NULL;
+ }
+ QUIT;
+ }
+ }
+ else
+ {
+ /* Parse error, maybe discard rest of stream if not interactive. */
+ if (!interactive)
+ EOF_Reached = EOF;
+ }
+ if (just_one_command)
+ EOF_Reached = EOF;
+ }
+ indirection_level--;
+}
+
+/* Return a string denoting what our indirection level is. */
+static char indirection_string[100];
+
+char *
+indirection_level_string ()
+{
+ register int i, j;
+ char *ps4;
+
+ indirection_string[0] = '\0';
+ ps4 = get_string_value ("PS4");
+
+ if (ps4 == 0 || *ps4 == '\0')
+ return (indirection_string);
+
+ ps4 = decode_prompt_string (ps4);
+
+ for (i = 0; *ps4 && i < indirection_level && i < 99; i++)
+ indirection_string[i] = *ps4;
+
+ for (j = 1; *ps4 && ps4[j] && i < 99; i++, j++)
+ indirection_string[i] = ps4[j];
+
+ indirection_string[i] = '\0';
+ free (ps4);
+ return (indirection_string);
+}
+
+static sighandler
+alrm_catcher(i)
+ int i;
+{
+ printf ("%ctimed out waiting for input: auto-logout\n", '\07');
+ longjmp (top_level, EXITPROG);
+#if !defined (VOID_SIGHANDLER)
+ return (0);
+#endif /* !VOID_SIGHANDLER */
+}
+
+parse_command ()
+{
+ int r;
+
+ need_here_doc = 0;
+ run_pending_traps ();
+
+ /* Allow the execution of a random command just before the printing
+ of each primary prompt. If the shell variable PROMPT_COMMAND
+ is set then the value of it is the command to execute. */
+ if (interactive && bash_input.type != st_string)
+ {
+ char *command_to_execute;
+
+ command_to_execute = get_string_value ("PROMPT_COMMAND");
+ if (command_to_execute)
+ execute_prompt_command (command_to_execute);
+ }
+
+ current_command_line_count = 0;
+ r = yyparse ();
+
+ if (need_here_doc)
+ gather_here_documents ();
+
+ return (r);
+}
+
+read_command ()
+{
+ SHELL_VAR *tmout_var = (SHELL_VAR *)NULL;
+ int tmout_len = 0, result;
+ SigHandler *old_alrm = (SigHandler *)NULL;
+
+ prompt_string_pointer = &ps1_prompt;
+ global_command = (COMMAND *)NULL;
+
+ /* Only do timeouts if interactive. */
+ if (interactive)
+ {
+ tmout_var = find_variable ("TMOUT");
+
+ if (tmout_var && tmout_var->value)
+ {
+ tmout_len = atoi (tmout_var->value);
+ if (tmout_len > 0)
+ {
+ old_alrm = set_signal_handler (SIGALRM, alrm_catcher);
+ alarm (tmout_len);
+ }
+ }
+ }
+
+ QUIT;
+
+ current_command_line_count = 0;
+ result = parse_command ();
+
+ if (interactive && tmout_var && (tmout_len > 0))
+ {
+ alarm(0);
+ set_signal_handler (SIGALRM, old_alrm);
+ }
+ return (result);
+}
+
+/* Cause STREAM to buffer lines as opposed to characters or blocks. */
+static void
+line_buffer_stream (stream)
+ FILE *stream;
+{
+ /* If your machine doesn't have either of setlinebuf or setvbuf,
+ you can just comment out the buffering commands, and the shell
+ will still work. It will take more cycles, though. */
+#if defined (HAVE_SETLINEBUF)
+ setlinebuf (stream);
+#else
+# if defined (_IOLBF)
+# if defined (REVERSED_SETVBUF_ARGS)
+ setvbuf (stream, _IOLBF, (char *)NULL, BUFSIZ);
+# else /* !REVERSED_SETVBUF_ARGS */
+ setvbuf (stream, (char *)NULL, _IOLBF, BUFSIZ);
+# endif /* !REVERSED_SETVBUF_ARGS */
+# endif /* _IOLBF */
+#endif /* !HAVE_SETLINEBUF */
+}
+
+/* Do whatever is necessary to initialize the shell.
+ Put new initializations in here. */
+static void
+shell_initialize ()
+{
+ /* Line buffer output for stderr and stdout. */
+ line_buffer_stream (stderr);
+ line_buffer_stream (stdout);
+
+ /* Sort the array of shell builtins so that the binary search in
+ find_shell_builtin () works correctly. */
+ initialize_shell_builtins ();
+
+ /* Initialize the trap signal handlers before installing our own
+ signal handlers. traps.c:restore_original_signals () is responsible
+ for restoring the original default signal handlers. That function
+ is called when we make a new child. */
+ initialize_traps ();
+ initialize_signals ();
+
+ /* Initialize current_user.name and current_host_name. */
+ {
+ struct passwd *entry = getpwuid (current_user.uid);
+ char hostname[256];
+
+ if (gethostname (hostname, 255) < 0)
+ current_host_name = "??host??";
+ else
+ current_host_name = savestring (hostname);
+
+ if (entry)
+ {
+ current_user.user_name = savestring (entry->pw_name);
+ if (entry->pw_shell && entry->pw_shell[0])
+ current_user.shell = savestring (entry->pw_shell);
+ else
+ current_user.shell = savestring ("/bin/sh");
+ current_user.home_dir = savestring (entry->pw_dir);
+ }
+ else
+ {
+ current_user.user_name = savestring ("I have no name!");
+ current_user.shell = savestring ("/bin/sh");
+ current_user.home_dir = savestring ("/");
+ }
+
+ endpwent ();
+ }
+
+ /* Initialize our interface to the tilde expander. */
+ tilde_initialize ();
+
+ /* Initialize internal and environment variables. */
+ initialize_shell_variables (shell_environment);
+
+ /* Initialize filename hash tables. */
+ initialize_filename_hashing ();
+
+ /* Initialize the data structures for storing and running jobs. */
+ initialize_jobs ();
+
+ /* Initialize input streams to null. */
+ initialize_bash_input ();
+}
+
+/* Function called by main () when it appears that the shell has already
+ had some initialization performed. This is supposed to reset the world
+ back to a pristine state, as if we had been exec'ed. */
+static void
+shell_reinitialize ()
+{
+ /* The default shell prompts. */
+ primary_prompt = PPROMPT;
+ secondary_prompt = SPROMPT;
+
+ /* Things that get 1. */
+ current_command_number = 1;
+
+ /* We have decided that the ~/.bashrc file should not be executed
+ for the invocation of each shell script. If the variable $ENV
+ (or $BASH_ENV) is set, its value is used as the name of a file
+ to source. */
+ no_rc = no_profile = 1;
+
+ /* Things that get 0. */
+ login_shell = make_login_shell = interactive = executing = 0;
+ debugging = do_version = line_number = last_command_exit_value = 0;
+ forced_interactive = interactive_shell = subshell_environment = 0;
+
+#if defined (HISTORY)
+# if defined (BANG_HISTORY)
+ history_expansion = 0;
+# endif
+ remember_on_history = 0;
+#endif /* HISTORY */
+
+#if defined (RESTRICTED_SHELL)
+ restricted = 0;
+#endif /* RESTRICTED_SHELL */
+
+ /* Ensure that the default startup file is used. (Except that we don't
+ execute this file for reinitialized shells). */
+ bashrc_file = "~/.bashrc";
+
+ /* Delete all variables and functions. They will be reinitialized when
+ the environment is parsed. */
+
+ delete_all_variables (shell_variables);
+ delete_all_variables (shell_functions);
+
+ /* Pretend the PATH variable has changed. */
+ sv_path ("PATH");
+}
+
+static void
+initialize_signals ()
+{
+ initialize_terminating_signals ();
+ initialize_job_signals ();
+#if defined (INITIALIZE_SIGLIST)
+ initialize_siglist ();
+#endif
+}
+
+void
+reinitialize_signals ()
+{
+ initialize_terminating_signals ();
+ initialize_job_signals ();
+}
+
+/* A structure describing a signal that terminates the shell if not
+ caught. The orig_handler member is present so children can reset
+ these signals back to their original handlers. */
+struct termsig {
+ int signum;
+ SigHandler *orig_handler;
+};
+
+#define NULL_HANDLER (SigHandler *)SIG_DFL
+
+/* The list of signals that would terminate the shell if not caught.
+ We catch them, but just so that we can write the history file,
+ and so forth. */
+static struct termsig terminating_signals[] = {
+#ifdef SIGHUP
+ SIGHUP, NULL_HANDLER,
+#endif
+
+#ifdef SIGINT
+ SIGINT, NULL_HANDLER,
+#endif
+
+#ifdef SIGILL
+ SIGILL, NULL_HANDLER,
+#endif
+
+#ifdef SIGTRAP
+ SIGTRAP, NULL_HANDLER,
+#endif
+
+#ifdef SIGIOT
+ SIGIOT, NULL_HANDLER,
+#endif
+
+#ifdef SIGDANGER
+ SIGDANGER, NULL_HANDLER,
+#endif
+
+#ifdef SIGEMT
+ SIGEMT, NULL_HANDLER,
+#endif
+
+#ifdef SIGFPE
+ SIGFPE, NULL_HANDLER,
+#endif
+
+#ifdef SIGBUS
+ SIGBUS, NULL_HANDLER,
+#endif
+
+#ifdef SIGSEGV
+ SIGSEGV, NULL_HANDLER,
+#endif
+
+#ifdef SIGSYS
+ SIGSYS, NULL_HANDLER,
+#endif
+
+#ifdef SIGPIPE
+ SIGPIPE, NULL_HANDLER,
+#endif
+
+#ifdef SIGALRM
+ SIGALRM, NULL_HANDLER,
+#endif
+
+#ifdef SIGTERM
+ SIGTERM, NULL_HANDLER,
+#endif
+
+#ifdef SIGXCPU
+ SIGXCPU, NULL_HANDLER,
+#endif
+
+#ifdef SIGXFSZ
+ SIGXFSZ, NULL_HANDLER,
+#endif
+
+#ifdef SIGVTALRM
+ SIGVTALRM, NULL_HANDLER,
+#endif
+
+#ifdef SIGPROF
+ SIGPROF, NULL_HANDLER,
+#endif
+
+#ifdef SIGLOST
+ SIGLOST, NULL_HANDLER,
+#endif
+
+#ifdef SIGUSR1
+ SIGUSR1, NULL_HANDLER,
+#endif
+
+#ifdef SIGUSR2
+ SIGUSR2, NULL_HANDLER,
+#endif
+};
+
+#define TERMSIGS_LENGTH (sizeof (terminating_signals) / sizeof (struct termsig))
+
+#define XSIG(x) (terminating_signals[x].signum)
+#define XHANDLER(x) (terminating_signals[x].orig_handler)
+
+/* This function belongs here? */
+sighandler
+termination_unwind_protect (sig)
+ int sig;
+{
+ if (sig == SIGINT && signal_is_trapped (SIGINT))
+ run_interrupt_trap ();
+
+#if defined (HISTORY)
+ if (interactive_shell)
+ maybe_save_shell_history ();
+#endif /* HISTORY */
+
+#if defined (JOB_CONTROL)
+ if (interactive && sig == SIGHUP)
+ hangup_all_jobs ();
+ end_job_control ();
+#endif /* JOB_CONTROL */
+
+#if defined (PROCESS_SUBSTITUTION)
+ unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+ run_exit_trap ();
+ set_signal_handler (sig, SIG_DFL);
+ kill (getpid (), sig);
+
+#if !defined (VOID_SIGHANDLER)
+ return (0);
+#endif /* VOID_SIGHANDLER */
+}
+
+/* Initialize signals that will terminate the shell to do some
+ unwind protection. */
+static void
+initialize_terminating_signals ()
+{
+ register int i;
+
+ /* The following code is to avoid an expensive call to
+ set_signal_handler () for each terminating_signals. Fortunately,
+ this is possible in Posix. Unfortunately, we have to call signal ()
+ on non-Posix systems for each signal in terminating_signals. */
+#if defined (_POSIX_VERSION)
+ struct sigaction act, oact;
+
+ act.sa_handler = termination_unwind_protect;
+ act.sa_flags = 0;
+ sigemptyset (&act.sa_mask);
+ sigemptyset (&oact.sa_mask);
+ for (i = 0; i < TERMSIGS_LENGTH; i++)
+ sigaddset (&act.sa_mask, XSIG (i));
+ for (i = 0; i < TERMSIGS_LENGTH; i++)
+ {
+ sigaction (XSIG (i), &act, &oact);
+ terminating_signals[i].orig_handler = oact.sa_handler;
+ /* Don't do anything with signals that are ignored at shell entry
+ if the shell is not interactive. */
+ if (!interactive_shell && oact.sa_handler == SIG_IGN)
+ {
+ sigaction (XSIG (i), &oact, &act);
+ set_signal_ignored (XSIG (i));
+ }
+ }
+
+#else /* !_POSIX_VERSION */
+
+ for (i = 0; i < TERMSIGS_LENGTH; i++)
+ {
+ terminating_signals[i].orig_handler =
+ set_signal_handler (XSIG (i), termination_unwind_protect);
+ /* Don't do anything with signals that are ignored at shell entry
+ if the shell is not interactive. */
+ if (!interactive_shell && terminating_signals[i].orig_handler == SIG_IGN)
+ {
+ set_signal_handler (XSIG (i), SIG_IGN);
+ set_signal_ignored (XSIG (i));
+ }
+ }
+
+#endif /* !_POSIX_VERSION */
+
+#if defined (JOB_CONTROL) || defined (_POSIX_VERSION)
+ /* All shells use the signal mask they inherit, and pass it along
+ to child processes. Children will never block SIGCHLD, though. */
+ sigemptyset (&top_level_mask);
+ sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &top_level_mask);
+ sigdelset (&top_level_mask, SIGCHLD);
+#endif /* JOB_CONTROL || _POSIX_VERSION */
+
+ /* And, some signals that are specifically ignored by the shell. */
+ set_signal_handler (SIGQUIT, SIG_IGN);
+
+ if (interactive)
+ {
+ set_signal_handler (SIGINT, sigint_sighandler);
+ set_signal_handler (SIGTERM, SIG_IGN);
+ }
+}
+
+void
+reset_terminating_signals ()
+{
+ register int i;
+
+#if defined (_POSIX_VERSION)
+ struct sigaction act;
+
+ act.sa_flags = 0;
+ sigemptyset (&act.sa_mask);
+ for (i = 0; i < TERMSIGS_LENGTH; i++)
+ {
+ /* Skip a signal if it's trapped or handled specially, because the
+ trap code will restore the correct value. */
+ if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
+ continue;
+
+ act.sa_handler = XHANDLER (i);
+ sigaction (XSIG (i), &act, (struct sigaction *) NULL);
+ }
+#else
+ for (i = 0; i < TERMSIGS_LENGTH; i++)
+ {
+ if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
+ continue;
+
+ set_signal_handler (XSIG (i), XHANDLER (i));
+ }
+#endif
+}
+#undef XSIG
+#undef XHANDLER
+
+/* What to do when we've been interrupted, and it is safe to handle it. */
+void
+throw_to_top_level ()
+{
+ int print_newline = 0;
+
+ if (interrupt_state)
+ {
+ print_newline = 1;
+ interrupt_state--;
+ }
+
+ if (interrupt_state)
+ return;
+
+ last_command_exit_value |= 128;
+
+ /* Run any traps set on SIGINT. */
+ run_interrupt_trap ();
+
+ /* Cleanup string parser environment. */
+ while (parse_and_execute_level)
+ parse_and_execute_cleanup ();
+
+#if defined (JOB_CONTROL)
+ give_terminal_to (shell_pgrp);
+#endif /* JOB_CONTROL */
+
+#if defined (JOB_CONTROL) || defined (_POSIX_VERSION)
+ sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
+#endif
+
+ reset_parser ();
+
+#if defined (READLINE)
+ if (interactive)
+ bashline_reinitialize ();
+#endif /* READLINE */
+
+#if defined (PROCESS_SUBSTITUTION)
+ unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+ run_unwind_protects ();
+ loop_level = continuing = breaking = 0;
+ return_catch_flag = 0;
+
+ if (interactive && print_newline)
+ {
+ fflush (stdout);
+ fprintf (stderr, "\n");
+ fflush (stderr);
+ }
+
+ /* An interrupted `wait' command in a script does not exit the script. */
+ if (interactive || (interactive_shell && !shell_initialized) ||
+ (print_newline && signal_is_trapped (SIGINT)))
+ longjmp (top_level, DISCARD);
+ else
+ longjmp (top_level, EXITPROG);
+}
+
+/* When non-zero, we throw_to_top_level (). */
+int interrupt_immediately = 0;
+
+/* What we really do when SIGINT occurs. */
+sighandler
+sigint_sighandler (sig)
+ int sig;
+{
+#if defined (USG) && !defined (_POSIX_VERSION)
+ set_signal_handler (sig, sigint_sighandler);
+#endif
+
+ /* interrupt_state needs to be set for the stack of interrupts to work
+ right. Should it be set unconditionally? */
+ if (!interrupt_state)
+ interrupt_state++;
+
+ if (interrupt_immediately)
+ {
+ interrupt_immediately = 0;
+ throw_to_top_level ();
+ }
+#if !defined (VOID_SIGHANDLER)
+ return (0);
+#endif /* VOID_SIGHANDLER */
+}
+
+/* Give version information about this shell. */
+char *
+shell_version_string ()
+{
+ static char tt[16] = { '\0' };
+
+ if (!tt[0])
+ sprintf (tt, "%s.%d(%d)", dist_version, patch_level, build_version);
+ return tt;
+}
+
+void
+show_shell_version ()
+{
+ printf ("GNU %s, version %s\n", base_pathname (shell_name),
+ shell_version_string ());
+}
+
+#if !defined (USG) && defined (ENOTSOCK)
+# if !defined (HAVE_SOCKETS)
+# define HAVE_SOCKETS
+# endif
+#endif
+
+#if defined (HAVE_SOCKETS)
+#include <sys/socket.h>
+#endif
+
+/* Is FD a socket or network connection? */
+static int
+isnetconn (fd)
+ int fd;
+{
+#if defined (USGr4) || defined (USGr4_2)
+ /* Sockets on SVR4 and SVR4.2 are character special (streams) devices. */
+ struct stat sb;
+
+ if (fstat (fd, &sb) < 0)
+ return (0);
+ return (S_ISCHR (sb.st_mode));
+#else /* !USGr4 && !USGr4_2 */
+# if defined (HAVE_SOCKETS)
+ int rv, l;
+ struct sockaddr sa;
+
+ l = sizeof(sa);
+ rv = getpeername(0, &sa, &l);
+ return ((rv < 0 && errno == ENOTSOCK) ? 0 : 1);
+# else /* !HAVE_SOCKETS */
+# if defined (S_ISSOCK)
+ struct stat sb;
+
+ if (fstat (fd, &sb) < 0)
+ return (0);
+ return (S_ISSOCK (sb.st_mode));
+# else /* !S_ISSOCK */
+ return (0);
+# endif /* !S_ISSOCK */
+# endif /* !HAVE_SOCKETS */
+#endif /* !USGr4 && !USGr4_2 */
+}