home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / shells / bashsrc.zoo / shell.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-06-05  |  25.4 KB  |  1,033 lines

  1. /* shell.c -- GNU's idea of the POSIX shell specification.
  2.    Hopefully, this shell will contain significant enhancements.
  3.  
  4.    This file is part of Bash, the Bourne Again SHell.
  5.    Bash is free software; no one can prevent you from reading the source
  6.    code, or giving it to someone else.
  7.    This file is copyrighted under the GNU General Public License, which
  8.    can be found in the file called COPYING.
  9.  
  10.    Copyright (C) 1989 Free Software Foundation, Inc.
  11.  
  12.    This file is part of GNU Bash.
  13.  
  14.    Bash is distributed in the hope that it will be useful, but
  15.    WITHOUT ANY WARRANTY.  No author or distributor accepts
  16.    responsibility to anyone for the consequences of using it or for
  17.    whether it serves any particular purpose or works at all, unless he
  18.    says so in writing.  Refer to the GNU Emacs General Public License
  19.    for full details.
  20.  
  21.    Everyone is granted permission to copy, modify and redistribute
  22.    Bash, but only under the conditions described in the GNU General Public
  23.    License.   A copy of this license is supposed to have been given to you
  24.    along with GNU Emacs so you can know your rights and responsibilities.
  25.    It should be in a file named COPYING.
  26.  
  27.    Among other things, the copyright notice and this notice must be
  28.    preserved on all copies.
  29.  
  30.   Modification history:
  31.  
  32.   Sunday, January 10th, 1988.
  33.   Initial author: Brian Fox
  34.  
  35. */
  36.  
  37. #include <stdio.h>
  38. #include <signal.h>
  39. #include <errno.h>
  40. #include <sys/types.h>
  41. #ifndef SONY
  42. #include <fcntl.h>
  43. #endif
  44. #include <sys/file.h>
  45. #include <sys/stat.h>
  46. #include <pwd.h>
  47.  
  48. #if defined (HAVE_VPRINTF)
  49. #include <varargs.h>
  50. #endif
  51.  
  52. #include "shell.h"
  53. #include "flags.h"
  54.  
  55. #ifdef SYSV
  56. struct passwd *getpwuid();
  57. #endif
  58.  
  59. extern char *dist_version;
  60. extern int build_version;
  61.  
  62. extern int yydebug;
  63.  
  64. /* Non-zero means that this shell has already been run; i.e. you should
  65.    call shell_reinitialize () if you need to start afresh. */
  66. int shell_initialized = 0;
  67.  
  68. /* The current maintainer of the shell.  You change this in the
  69.    Makefile. */
  70. #ifndef MAINTAINER
  71. #define MAINTAINER "deliberately-anonymous"
  72. #endif
  73. char *the_current_maintainer = MAINTAINER;
  74.  
  75. #ifndef PPROMPT
  76. #define PPROMPT "bash\\$ "
  77. #endif
  78. char *primary_prompt = PPROMPT;
  79.  
  80. #ifndef SPROMPT
  81. #define SPROMPT "bash> "
  82. #endif
  83. char *secondary_prompt = SPROMPT;
  84.  
  85. COMMAND *global_command = (COMMAND *)NULL;
  86.  
  87. /* Non-zero after SIGINT. */
  88. int interrupt_state = 0;
  89.  
  90. /* The current user's name. */
  91. char *current_user_name = (char *)NULL;
  92.  
  93. /* The current host's name. */
  94. char *current_host_name = (char *)NULL;
  95.  
  96. /* Non-zero means that this shell is a login shell.
  97.    Specifically:
  98.    0 = not login shell.
  99.    1 = login shell from getty (or equivalent fake out)
  100.   -1 = login shell from "-login" flag.
  101.   -2 = both from getty, and from flag.
  102.  */
  103. int login_shell = 0;
  104.  
  105. /* Non-zero means this shell is running interactively. */
  106. int interactive = 0;
  107.  
  108. /* Non-zero means to remember lines typed to the shell on the history
  109.    list.  This is different than the user-controlled behaviour; this
  110.    becomes zero when we read lines from a file, for example. */
  111. int remember_on_history = 1;
  112.  
  113. /* Non-zero means this shell is restricted. */
  114. int restricted = 0;
  115.  
  116. /* Special debugging helper. */
  117. int debugging_login_shell = 0;
  118.  
  119. /* The environment that the shell passes to other commands. */
  120. char **shell_environment;
  121.  
  122. /* Non-zero when we are executing a top-level command. */
  123. int executing = 0;
  124.  
  125. /* The number of commands executed so far. */
  126. int current_command_number = 1;
  127.  
  128. /* The environment at the top-level REP loop.  We use this in the case of
  129.    error return. */
  130. jmp_buf top_level, catch;
  131.  
  132. /* Non-zero is the recursion depth for commands. */
  133. int indirection_level = 0;
  134.  
  135. /* The number of times BASH has been executed.  This is set
  136.    by initialize_variables () in variables.c. */
  137. int shell_level = 0;
  138.  
  139. /* The name of this shell, as taken from argv[0]. */
  140. char *shell_name;
  141.  
  142. /* The name of the .(shell)rc file. */
  143. char *bashrc_file = "~/.bashrc";
  144.  
  145. /* Non-zero means to act more like the Bourne shell on startup. */
  146. int act_like_sh = 0;
  147.  
  148. /* Values for the long-winded argument names. */
  149. int debugging = 0;        /* Do debugging things. */
  150. int no_rc = 0;            /* Don't execute ~/.bashrc */
  151. int no_profile = 0;        /* Don't execute .profile */
  152. int do_version = 0;        /* Display interesting version info. */
  153. int quiet = 0;            /* Be quiet when starting up. */
  154. int make_login_shell = 0;    /* Make this shell be a `-bash' shell. */
  155. int no_line_editing = 0;    /* Don't do fancy line editing. */
  156. int no_brace_expansion = 0;    /* Non-zero means no foo{a,b} -> fooa fooa. */
  157.  
  158. /* Some long-winded argument names.  These are obviously new. */
  159. #define Int 1
  160. #define Charp 2
  161. struct {
  162.   char *name;
  163.   int *value;
  164.   int type;
  165. } long_args[] = {
  166.   { "debug", &debugging, Int },
  167.   { "norc", &no_rc, Int },
  168.   { "noprofile", &no_profile, Int },
  169.   { "rcfile", (int *)&bashrc_file, Charp},
  170.   { "version", &do_version, Int},
  171.   { "quiet", &quiet, Int},
  172.   { "login", &make_login_shell, Int},
  173.   { "nolineediting", &no_line_editing, Int},
  174.   { "nobraceexpansion", &no_brace_expansion, Int},
  175.   { (char *)NULL, (int *)0x0, 0 }
  176. };
  177.  
  178. main (argc, argv, env)
  179.      int argc;
  180.      char **argv, **env;
  181. {
  182.   int i, arg_index = 1;
  183.   extern int yydebug;
  184.   FILE *default_input = stdin;
  185.   char *local_pending_command = (char *)NULL;
  186.   extern int last_command_exit_value;
  187.   int locally_skip_execution = 0, top_level_arg_index;
  188.   extern char *base_pathname ();
  189. #ifdef JOB_CONTROL
  190.   extern int job_control;
  191. #endif
  192.  
  193.   /* Wait forever if we are debugging a login shell. */
  194.   while (debugging_login_shell);
  195.       
  196.   /* If this shell has already been run, then reinitialize it to a
  197.      vanilla state. */
  198.   if (shell_initialized)
  199.     {
  200.       shell_reinitialize ();
  201.       if (setjmp (top_level))
  202.     exit (2);
  203.     }
  204.  
  205.   /* Here's a hack.  If the name of this shell is "sh", then don't do
  206.      any startup files; just try to be more like /bin/sh. */
  207.   {
  208.     char *tshell_name = base_pathname (argv[0]);
  209.  
  210.     if (*tshell_name == '-')
  211.       tshell_name++;
  212.  
  213.     if (strcmp (tshell_name, "sh") == 0)
  214.       act_like_sh++;
  215.   }
  216.  
  217.   yydebug = 0;
  218.  
  219.   shell_environment = env;
  220.   shell_name = argv[0];
  221.   if (*shell_name == '-')
  222.     {
  223.       shell_name++;
  224.       login_shell++;
  225.     }
  226.  
  227. #ifdef JOB_CONTROL
  228.   if (act_like_sh)
  229.     job_control = 0;
  230. #endif
  231.  
  232.   dollar_vars[0] = savestring (argv[0]);
  233.  
  234.   /* Parse argument flags from the input line. */
  235.  
  236.   /* Find full word arguments first. */
  237.   while ((arg_index != argc) && *(argv[arg_index]) == '-')
  238.     {
  239.       for (i = 0; long_args[i].name; i++)
  240.     {
  241.       if (strcmp (&(argv[arg_index][1]), long_args[i].name) == 0)
  242.         {
  243.           if (long_args[i].type == Int)
  244.         *(long_args[i].value) = 1;
  245.           else
  246.         {
  247.           if (!argv[++arg_index])
  248.             {
  249.               report_error ("%s: Flag `%s' expected an argument",
  250.                     shell_name, long_args[i].name);
  251.               exit (1);
  252.             }
  253.           else
  254.             *long_args[i].value = (int)argv[arg_index];
  255.         }
  256.           goto next_arg;
  257.         }
  258.     }
  259.       break;            /* No such argument.  Maybe flag arg. */
  260.     next_arg:
  261.       arg_index++;
  262.     }
  263.  
  264.   /* If user supplied the "-login" flag, then set and invert LOGIN_SHELL. */
  265.   if (make_login_shell)
  266.     login_shell = -++login_shell;
  267.  
  268.   /* All done with full word args; do standard shell arg parsing.*/
  269.   while (arg_index != argc && argv[arg_index] &&
  270.      (*(argv[arg_index]) == '-' || (*argv[arg_index] == '+')))
  271.     {
  272.       /* There are flag arguments, so parse them. */
  273.       int arg_character;
  274.       int on_or_off = (*argv[arg_index]);
  275.       int  i = 1;
  276.  
  277.       while (arg_character = (argv[arg_index])[i++])
  278.     {
  279.       switch (arg_character)
  280.         {
  281.         case 'c':
  282.           /* The next arg is a command to execute, and the following args
  283.          are $1 .. $n respectively. */
  284.           local_pending_command = argv[++arg_index];
  285.           if (!local_pending_command)
  286.         {
  287.           report_error ("`%cc' requires an argument", on_or_off);
  288.           exit (1);
  289.         }
  290.  
  291.           arg_index++;
  292.           goto after_flags;
  293.           break;
  294.  
  295.         default:
  296.           if (change_flag_char (arg_character, on_or_off) == FLAG_ERROR)
  297.         {
  298.           report_error ("%c%c: bad option", on_or_off, arg_character);
  299.           exit (1);
  300.         }
  301.  
  302.         }
  303.     }
  304.       arg_index++;
  305.     }
  306.  
  307.  after_flags:
  308.  
  309.   /* First, let the outside world know about our interactive status. */
  310.   if (forced_interactive ||
  311.       (!local_pending_command &&
  312.        arg_index == argc &&
  313.        isatty (fileno (stdin)) &&
  314.        isatty (fileno (stdout))))
  315.     interactive = 1;
  316.   else
  317.     {
  318.       interactive = 0;
  319. #ifdef JOB_CONTROL
  320.       job_control = 0;
  321. #endif
  322.     }
  323.  
  324.   /* From here on in, the shell must be a normal functioning shell.
  325.      Variables from the environment are expected to be set, etc. */
  326.   shell_initialize ();
  327.  
  328.   if (interactive)
  329.     {
  330.       char *emacs = (char *)getenv ("EMACS");
  331.       if (emacs && (strcmp (emacs, "t") == 0))
  332.     no_line_editing = 1;
  333.     }
  334.  
  335.   top_level_arg_index = arg_index;
  336.  
  337.   if (!quiet && do_version)
  338.     show_shell_version ();
  339.  
  340.   /* Give this shell a place to longjmp to before executing the
  341.      startup files.  This allows users to press C-c to abort the
  342.      lengthy startup. */
  343.   if (setjmp (top_level))
  344.     {
  345.       if (!interactive)
  346.     exit (2);
  347.       else
  348.     locally_skip_execution++;
  349.     }
  350.  
  351.   arg_index = top_level_arg_index;
  352.  
  353.   /* Execute the start-up scripts. */
  354.  
  355.   if (!interactive)
  356.     {
  357.       makunbound ("PS1");
  358.       makunbound ("PS2");
  359.     }
  360.  
  361.   if (!locally_skip_execution)
  362.     {
  363.       if (login_shell)
  364.     maybe_execute_file ("/etc/profile");
  365.  
  366.       if (login_shell && !no_profile)
  367.     {
  368.       /* If we are doing the .bash_profile, then don't do the .bashrc. */
  369.       no_rc++;
  370.  
  371.       if (act_like_sh)
  372.         maybe_execute_file ("~/.profile");
  373.       else
  374.         {
  375.           if (maybe_execute_file ("~/.bash_profile") == 0)
  376.         if (maybe_execute_file ("~/.bash_login") == 0)
  377.           maybe_execute_file ("~/.profile");
  378.         }
  379.  
  380.       /* I turn on the restrictions afterwards because it is explictly
  381.          stated in the POSIX spec that PATH cannot be set in a restricted
  382.          shell, except in .profile. */
  383.       if (*++(argv[0]) == 'r')
  384.         {
  385.           set_var_read_only ("PATH");
  386.           restricted++;
  387.         }
  388.     }
  389.  
  390.       /* Execute ~/.bashrc for all shells except direct script shells,
  391.      and shells that are doing -c "command". */
  392.  
  393.       if (arg_index == argc && !no_rc && !act_like_sh &&
  394.       (!local_pending_command || shell_level < 2))
  395.     maybe_execute_file (bashrc_file);
  396.  
  397.       /* Try a TMB suggestion.  If running a script, then execute the
  398.      file mentioned in the ENV variable. */
  399.       if (!interactive)
  400.     {
  401.       char *env_file = (char *)getenv ("ENV");
  402.       if (env_file && *env_file)
  403.         maybe_execute_file (env_file);
  404.     }
  405.  
  406.       if (local_pending_command)
  407.     {
  408.       with_input_from_string (local_pending_command, "-c");
  409.       goto read_and_execute;
  410.     }
  411.     }
  412.   /* Do the things that should be done only for interactive shells. */
  413.   if (interactive)
  414.     {
  415.       /* Set up for checking for presence of mail. */
  416. #ifdef SYSV
  417.       /* Under SYSV, we can only tell if you have mail if the
  418.      modification date has changed.  So remember the current
  419.      modification dates. */
  420.       remember_mail_dates ();
  421. #else
  422.       /* Under 4.x, you have mail if there is something in your inbox.
  423.      I set the remembered mail dates to 1900.  */
  424.       reset_mail_files ();
  425. #endif /* SYSV */
  426.  
  427.       /* If this was a login shell, then assume that /bin/login has already
  428.      taken care of informing the user that they have new mail.  Otherwise,
  429.      we want to check right away. */
  430.       if (login_shell == 1)
  431.     {
  432. #ifndef SYSV
  433.       remember_mail_dates ();
  434. #endif  /* SYSV */
  435.     }
  436.  
  437.       reset_mail_timer ();
  438.  
  439.       /* Initialize the interactive history stuff. */
  440.  
  441.       if (!shell_initialized)
  442.     {
  443.       char *hf = get_string_value ("HISTFILE");
  444.     if (hf)
  445.       read_history (hf);
  446.     }
  447.     }
  448.  
  449.   /* Get possible input filename. */
  450.  get_input_filename:
  451.   if (arg_index != argc)
  452.     {
  453.       int fd;
  454.  
  455.       free (dollar_vars[0]);
  456.       dollar_vars[0] = savestring (argv[arg_index]);
  457.  
  458.       fd = open (argv[arg_index++], O_RDONLY);
  459.       if (fd < 0)
  460.     {
  461.       file_error (dollar_vars[0]);
  462.       exit (1);
  463.     }
  464.       else
  465.     {
  466.       default_input = fdopen (fd, "r");
  467.       if (!default_input)
  468.         {
  469.           file_error (dollar_vars[0]);
  470.           exit (1);
  471.         }
  472.     }
  473.  
  474.       if (!interactive || (!isatty (fd)))
  475.     {
  476.       extern int history_expansion;
  477.       history_expansion = interactive = 0;
  478. #ifdef JOB_CONTROL
  479.       set_job_control (0);
  480. #endif
  481.     }
  482.       else
  483.     {
  484.       dup2 (fd, 0);
  485.       close (fd);
  486.       fclose (default_input);
  487.     }
  488.     }
  489.  
  490.   /* Bind remaining args to $1 ... $n */
  491.   {
  492.     WORD_LIST *args = (WORD_LIST *)NULL;
  493.     while (arg_index != argc)
  494.       args = make_word_list (make_word (argv[arg_index++]), args);
  495.     args = (WORD_LIST *)reverse_list (args);
  496.     remember_args (args, 1);
  497.     dispose_words (args);
  498.   }
  499.  
  500.   if (interactive && !no_line_editing)
  501.     with_input_from_stdin ();
  502.   else
  503.     with_input_from_stream (default_input, dollar_vars[0]);
  504.  
  505.  read_and_execute:
  506.  
  507.   shell_initialized = 1;
  508.  
  509.   /* Read commands until exit condition. */
  510.   reader_loop ();
  511.  
  512.   /* Do trap[0] if defined. */
  513.   run_exit_trap ();
  514.   
  515.   /* Save the history of executed commands. */
  516.   if (interactive)
  517.     {
  518.       char *hf = get_string_value ("HISTFILE");
  519.       if (hf)
  520.     write_history (hf);
  521.     }
  522.  
  523.   /* Always return the exit status of the last command to our parent. */
  524.   exit (last_command_exit_value);
  525. }
  526.  
  527. /* Try to execute the contents of FNAME.  If FNAME doesn't exist,
  528.    that is not an error, but other kinds of errors are.  Returns
  529.    -1 in the case of an error, 0 in the case that the file was not
  530.    found, and 1 if the file was found and executed. */
  531. maybe_execute_file (fname)
  532.      char *fname;
  533. {
  534.   extern int errno;
  535.   char *tilde_expand ();
  536.   char *filename = tilde_expand (fname);
  537.  
  538.   struct stat file_info;
  539.  
  540.   if (stat (filename, &file_info) == -1)
  541.     {
  542.       if (errno != ENOENT)
  543.     {
  544.     file_error_and_exit:
  545.       file_error (filename);
  546.       free (filename);
  547.       return (-1);
  548.     }
  549.       free (filename);
  550.       return (0);
  551.     }
  552.   else
  553.     {
  554.       int tt, tresult;
  555.       char *string = (char *)xmalloc (1 + file_info.st_size);
  556.       int fd = open (filename, O_RDONLY);
  557.  
  558.       if (fd < 0)
  559.     {
  560.     hack_file_error:
  561.       free (string);
  562.       goto file_error_and_exit;
  563.     }
  564.  
  565.       tresult = read (fd, string, file_info.st_size);
  566.       tt = errno;
  567.       close (fd);
  568.       errno = tt;
  569.       if (tresult != file_info.st_size)
  570.     goto hack_file_error;
  571.       string[file_info.st_size] = '\0';
  572.  
  573.       parse_and_execute (string, filename);
  574.       free (filename);
  575.  
  576.       return (1);
  577.     }
  578. }
  579.  
  580. reader_loop ()
  581. {
  582.   extern int indirection_level;
  583.   int our_indirection_level;
  584.   COMMAND *current_command = (COMMAND *)NULL;
  585.  
  586.   our_indirection_level = ++indirection_level;
  587.  
  588.   while (!EOF_Reached)
  589.     {
  590.       sighandler sigint_sighandler ();
  591.       int code = setjmp (top_level);
  592.  
  593.       signal (SIGINT, sigint_sighandler);
  594.  
  595.       if (code != NOT_JUMPED)
  596.     {
  597.       indirection_level = our_indirection_level;
  598.  
  599.       switch (code)
  600.         {
  601.           /* Some kind of throw to top_level has occured. */
  602.         case FORCE_EOF:
  603.         case EXITPROG:
  604.           current_command = (COMMAND *)NULL;
  605.           EOF_Reached = EOF;
  606.           goto exec_done;
  607.  
  608.         case DISCARD:
  609.           /* Obstack free command elements, etc. */
  610.           break;
  611.  
  612.         default:
  613.           programming_error ("Bad jump %d", code);
  614.         }
  615.     }
  616.  
  617.       executing = 0;
  618.       dispose_used_env_vars ();
  619.  
  620.       if (read_command () == 0)
  621.     {
  622.       if (global_command) {
  623.         current_command = global_command;
  624.  
  625.         current_command_number++;
  626.  
  627.         /* POSIX spec: "-n  The shell reads commands but does
  628.            not execute them; this can be used to check for shell
  629.            script syntax errors.  The shell ignores the -n option
  630.            for interactive shells. " */
  631.  
  632.         if (interactive || !read_but_dont_execute)
  633.           {
  634.         executing = 1;
  635.         execute_command (current_command);
  636.           }
  637.  
  638.       exec_done:
  639.         if (current_command)
  640.           dispose_command (current_command);
  641.         QUIT;
  642.       }
  643.     }
  644.       else
  645.     {
  646.       /* Parse error, maybe discard rest of stream if not interactive. */
  647.       if (!interactive)
  648.         EOF_Reached = EOF;
  649.     }
  650.       if (just_one_command)
  651.     EOF_Reached = EOF;
  652.     }
  653.   indirection_level--;
  654. }
  655.  
  656. /* Return a string denoting what our indirection level is. */
  657. static char indirection_string[100];
  658.  
  659. char *
  660. indirection_level_string ()
  661. {
  662.   register int i;
  663.   char *get_string_value (), *ps4 = get_string_value ("PS4");
  664.  
  665.   if (!ps4)
  666.     ps4 = "+";
  667.  
  668.   for (i = 0; i < 100 && i < indirection_level; i++)
  669.     indirection_string[i] = *ps4;
  670.  
  671.   indirection_string[i] = '\0';
  672.   return (indirection_string);
  673. }
  674.  
  675. read_command ()
  676. {
  677.   extern char *ps1_prompt, **prompt_string_pointer;
  678.  
  679.   prompt_string_pointer = &ps1_prompt;
  680.   global_command = (COMMAND *)NULL;
  681.   return (yyparse ());
  682. }
  683.  
  684. /* Do whatever is necessary to initialize the shell.
  685.    Put new initializations in here. */
  686. shell_initialize ()
  687. {
  688.   /* Line buffer output for stderr. 
  689.      If your machine doesn't have either of setlinebuf or setvbuf,
  690.      you can just comment out the buffering commands, and the shell
  691.      will still work.  It will take more cycles, though. */
  692. #if defined (HAVE_SETLINEBUF)
  693.   setlinebuf (stderr);
  694.   setlinebuf (stdout);
  695. #else
  696. # if defined (_IOLBF)
  697.   setvbuf (stderr, (char *)NULL, _IOLBF, BUFSIZ);
  698.   setvbuf (stdout, (char *)NULL, _IOLBF, BUFSIZ);
  699. # endif
  700. #endif /* HAVE_SETLINEBUF */
  701.  
  702.   /* The initialization of the basic underlying signal handlers must
  703.      happen before we initialize_traps ().  */
  704.   initialize_signals ();
  705.   initialize_traps ();
  706.  
  707.   /* Initialize current_user_name and current_host_name. */
  708.   {
  709.     struct passwd *entry = getpwuid (getuid ());
  710.     char hostname[256];
  711.  
  712.     if (gethostname (hostname, 255) < 0)
  713.       current_host_name = "??host??";
  714.     else
  715.       current_host_name = savestring (hostname);
  716.  
  717.     if (entry)
  718.       current_user_name = savestring (entry->pw_name);
  719.     else
  720.       current_user_name = savestring ("I have no name!");
  721.   }
  722.  
  723.   initialize_shell_variables (shell_environment);
  724.   initialize_filename_hashing ();
  725.   initialize_jobs ();
  726. }
  727.  
  728. /* Function called by main () when it appears that the shell has already
  729.    had some initialization preformed.  This is supposed to reset the world
  730.    back to a pristine state, as if we had been exec'ed. */
  731. shell_reinitialize ()
  732. {
  733.   extern int line_number, last_command_exit_value;
  734.  
  735.   /* The default shell prompts. */
  736.   primary_prompt = PPROMPT;
  737.   secondary_prompt = SPROMPT;
  738.  
  739.   /* Things that get 1. */
  740.   remember_on_history = current_command_number = 1;
  741.  
  742.   /* We have decided that the ~/.bashrc file should not be executed
  743.      for the invocation of each shell script.  Perhaps some other file
  744.      should.  */
  745.   act_like_sh = 1;
  746.  
  747.   /* Things that get 0. */
  748.   login_shell = make_login_shell = interactive = restricted = executing = 0;
  749.   debugging = no_rc = no_profile = do_version = line_number = 0;
  750.   last_command_exit_value = 0;
  751.  
  752.   /* Ensure that the default startup file is used. */
  753.   bashrc_file = "~/.bashrc";
  754.  
  755.   /* Delete all shell variables, except for functions. */
  756.   {
  757.     register SHELL_VAR *list = variable_list;
  758.     register SHELL_VAR *new_list = (SHELL_VAR *)NULL;
  759.     register SHELL_VAR *temp;
  760.  
  761.     variable_list = (SHELL_VAR *)NULL;
  762.  
  763.     while (list)
  764.       {
  765.     if (function_p (list))
  766.       {
  767.         temp = list->next;
  768.         list->next = new_list;
  769.         new_list = list;
  770.         list = temp;
  771.       }
  772.     else
  773.       {
  774.         temp = list;
  775.         list = list->next;
  776.         dispose_variable (temp);
  777.       }
  778.       }
  779.     variable_list = new_list;
  780.   }
  781.  
  782.   /* Pretend the PATH variable has changed. */
  783.   sv_path ("PATH");
  784. }
  785.  
  786. initialize_signals ()
  787. {
  788.   initialize_terminating_signals ();
  789.   initialize_job_signals ();
  790. #ifdef INITIALIZE_SIGLIST
  791.   initialize_siglist ();
  792. #endif
  793. }
  794.  
  795. /* The list of signals that would terminate the shell if not caught.
  796.    We catch them, but just so that we can write the history file,
  797.    and so forth. */
  798. int terminating_signals[] = {
  799.   SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGIOT,
  800. #ifdef SIGDANGER
  801.   SIGDANGER,
  802. #endif
  803. #ifdef SIGEMT
  804.   SIGEMT,
  805. #endif
  806.   SIGFPE, SIGKILL, SIGBUS, SIGSEGV, SIGSYS, SIGPIPE, SIGALRM, SIGTERM,
  807. #ifdef SIGXCPU
  808.   SIGXCPU,
  809. #endif
  810. #ifdef SIGXFSZ
  811.   SIGXFSZ,
  812. #endif
  813. #ifdef SIGVTALRM
  814.   SIGVTALRM,
  815. #endif
  816. #ifdef SIGPROF
  817.   SIGPROF,
  818. #endif
  819. #ifdef SIGLOST
  820.   SIGLOST,
  821. #endif
  822. #ifdef SIGUSR1
  823.   SIGUSR1, SIGUSR2
  824. #endif
  825.     };
  826.  
  827. #define TERMSIGS_LENGTH (sizeof (terminating_signals) / sizeof (int))
  828.  
  829. /* This function belongs here? */
  830. sighandler
  831. termination_unwind_protect (sig)
  832.      int sig;
  833. {
  834.   save_history ();
  835.   signal (sig, SIG_DFL);
  836.   kill (getpid (), sig);
  837. }
  838.  
  839. /* Initialize signals that will terminate the shell to do some
  840.    unwind protection. */
  841. initialize_terminating_signals ()
  842. {
  843.   register int i;
  844.  
  845.   for (i = 0; i < TERMSIGS_LENGTH; i++)
  846.     signal (terminating_signals[i], termination_unwind_protect);
  847.  
  848.   /* And, some signals that are specifically ignored by the shell. */
  849.   signal (SIGQUIT, SIG_IGN);
  850.  
  851.   if (login_shell)
  852.     signal (SIGTERM, SIG_IGN);
  853. }
  854.  
  855. /* What to do when we've been interrupted, and it is safe to handle it. */
  856. sighandler
  857. throw_to_top_level ()
  858. {
  859.   extern int last_command_exit_value, loop_level, continuing, breaking;
  860.   extern int return_catch_flag;
  861.  
  862.   if (interrupt_state)
  863.     interrupt_state --;
  864.  
  865.   if (interrupt_state)
  866.     return;
  867.  
  868. #ifdef JOB_CONTROL
  869.   {
  870.     extern int shell_pgrp;
  871.     give_terminal_to (shell_pgrp);
  872.   }
  873. #endif  /* JOB_CONTROL */
  874.  
  875.   run_unwind_protects ();
  876.   loop_level = continuing = breaking = 0;
  877.   return_catch_flag = 0;
  878.  
  879.   reset_parser ();
  880.   if (interactive)
  881.     {
  882.       fflush (stdout);
  883.       fprintf (stderr, "\n");
  884.     }
  885.  
  886.   last_command_exit_value |= 128;
  887.  
  888.   if (interactive)
  889.     longjmp (top_level, DISCARD);
  890.   else
  891.     longjmp (top_level, EXITPROG);
  892. }
  893.  
  894. /* When non-zero, we throw_to_top_level (). */
  895. int interrupt_immediately = 0;
  896.  
  897. /* What we really do when SIGINT occurs. */
  898. sighandler
  899. sigint_sighandler ()
  900. {
  901.   if (interrupt_immediately)
  902.     {
  903.       interrupt_immediately = 0;
  904.       throw_to_top_level ();
  905.     }
  906.   if (!interrupt_state)
  907.     interrupt_state++;
  908. }
  909.     
  910. /* Write the existing history out to the history file. */
  911. save_history ()
  912. {
  913.   void using_history ();
  914.  
  915.   using_history ();
  916.   write_history (get_string_value ("HISTFILE"));
  917. }
  918.  
  919. /* Make a bug report, even to the extent of mailing it.  Hope that it
  920.    gets where it is supposed to go.  If not, maybe the user will send
  921.    it back to me. */
  922. #include <readline/history.h>
  923. /* Number of commands to place in the bug report. */
  924. #define LAST_INTERESTING_HISTORY_COUNT 6
  925.  
  926. #if defined (HAVE_VPRINTF)
  927. make_bug_report (va_alist)
  928.      va_dcl
  929. #else
  930. make_bug_report (reason, arg1, arg2)
  931.      char *reason;
  932. #endif /* HAVE_VPRINTF */
  933. {
  934.   extern char *current_host_name, *current_user_name, *the_current_maintainer;
  935.   extern int interactive, login_shell;
  936.   register int len = where_history ();
  937.   register int i = len - LAST_INTERESTING_HISTORY_COUNT;
  938.   FILE *stream, *popen ();
  939.   HIST_ENTRY **list = history_list ();
  940.  
  941. #if defined (HAVE_VPRINTF)
  942.   char *reason;
  943.   va_list args;
  944. #endif /* HAVE_VPRINTF */
  945.  
  946.   stream = popen ("/bin/rmail bash-maintainers@ai.mit.edu", "w");
  947.  
  948.   save_history ();
  949.   if (i < 0) i = 0;
  950.  
  951.   if (stream)
  952.     {
  953.       fprintf (stream, "To: bash-maintainers@ai.mit.edu\n");
  954.       fprintf (stream, "Subject: Bash-%s.%d bug-report: ",
  955.            dist_version, build_version);
  956.  
  957. #if defined (HAVE_VPRINTF)
  958.       va_start (args);
  959.       reason = va_arg (args, char *);
  960.       vfprintf (stream, reason, args);
  961.       va_end (args);
  962. #else
  963.       fprintf (stream, reason, arg1, arg2);
  964. #endif /* HAVE_VPRINTF */
  965.  
  966.       fprintf (stream, "\n");
  967.  
  968.       /* Write the history into the mail file.  Maybe we can recreate
  969.      the bug? */
  970.       fprintf (stream,
  971. "This is a Bash bug report.  Bash maintainers should be getting this report.\n\
  972. If this mail has bounced, for right now please send it to:\n\
  973. \n\
  974.     %s\n\
  975. \n\
  976. since he is the current maintainer of this version of the shell.\n\
  977. \n\
  978. This is %s (invoked as `%s'), version %s.%d, on host %s, used by %s.\n\
  979. This shell is %sinteractive, and it is %sa login shell.\n\
  980. \n\
  981. The host is a %s running %s.\n\
  982. \n\
  983. The current environment is:\n",
  984.            the_current_maintainer,
  985.            get_string_value ("BASH"), full_pathname (dollar_vars[0]),
  986.            dist_version, build_version, current_host_name,
  987.            current_user_name, interactive ? "" : "not ",
  988.            login_shell ? "" : "not ", SYSTEM_NAME, OS_NAME);
  989.  
  990.       {
  991.     SHELL_VAR *v = variable_list;
  992.  
  993.     while (v)
  994.       {
  995.         if (!invisible_p (v))
  996.           {
  997.         if (function_p (v))
  998.           {
  999.             fprintf (stream, "function %s () {\n", v->name);
  1000.             fprintf (stream, "%s\n}",
  1001.                  make_command_string (function_cell (v)));
  1002.           }
  1003.         else
  1004.           fprintf (stream, "%s=%s", v->name, value_cell (v));
  1005.  
  1006.         fprintf (stream, "\n");
  1007.           }
  1008.         v = v->next;
  1009.       }
  1010.       }
  1011.  
  1012.       fprintf (stream, "\nAnd here are the last %d commands.\n\n",
  1013.            LAST_INTERESTING_HISTORY_COUNT);
  1014.  
  1015.       for (; i < len; i++)
  1016.     fprintf (stream, "%s\n", list[i]->line);
  1017.  
  1018.       pclose (stream);
  1019.     } else {
  1020.       fprintf (stderr, "Can't mail bug report!\n");
  1021.     }
  1022. }
  1023.  
  1024. /* Give version information about this shell. */
  1025. show_shell_version ()
  1026. {
  1027.   extern char *shell_name;
  1028.   extern int version;
  1029.  
  1030.   printf ("GNU %s, version %s.%d\n", base_pathname (shell_name),
  1031.       dist_version, build_version);
  1032. }
  1033.