386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Sat, 18 Jan 1992 19:46:31 +0000 (11:46 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Sat, 18 Jan 1992 19:46:31 +0000 (11:46 -0800)
Work on file usr/othersrc/public/bash-1.12/bash-1.12/variables.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/othersrc/public/bash-1.12/bash-1.12/variables.c [new file with mode: 0644]

diff --git a/usr/othersrc/public/bash-1.12/bash-1.12/variables.c b/usr/othersrc/public/bash-1.12/bash-1.12/variables.c
new file mode 100644 (file)
index 0000000..4f8dd38
--- /dev/null
@@ -0,0 +1,1608 @@
+/* variables.c -- Functions for hacking shell variables. */
+
+/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   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 1, or (at your option)
+   any later version.
+
+   Bash is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+#include "shell.h"
+#include "hash.h"
+#include "flags.h"
+
+#if defined (USG) && !defined (isc386) && !defined (sgi)
+struct passwd *getpwuid (), *getpwent ();
+#endif
+
+/* The list of shell variables that the user has created, or that came from
+   the environment. */
+HASH_TABLE *shell_variables = (HASH_TABLE *)NULL;
+
+/* The list of shell functions that the user has created, or that came from
+   the environment. */
+HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
+
+/* The current variable context.  This is really a count of how deep into
+   executing functions we are. */
+int variable_context = 0;
+
+/* The array of shell assignments which are made only in the environment
+   for a single command. */
+char **temporary_env = (char **)NULL;
+
+/* Some funky variables which are known about specially.  Here is where
+   "$*", "$1", and all the cruft is kept. */
+char *dollar_vars[10];
+WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
+
+/* The value of $$. */
+int dollar_dollar_pid;
+
+/* An array which is passed to commands as their environment.  It is
+   manufactured from the overlap of the initial environment and the
+   shell variables that are marked for export. */
+char **export_env = (char **)NULL;
+
+/* Non-zero means that we have to remake EXPORT_ENV. */
+int array_needs_making = 1;
+
+/* The list of variables that may not be unset in this shell. */
+char **non_unsettable_vars = (char **)NULL;
+
+#if defined (USG)
+#define DEFAULT_MAIL_PATH "/usr/mail/"
+#else
+#define DEFAULT_MAIL_PATH "/usr/spool/mail/"
+#endif
+
+/* Some forward declarations. */
+SHELL_VAR *set_if_not ();      /* returns new or existing entry */
+static void sbrand ();         /* set bash random number generator */
+
+/* Make VAR be auto-exported.  VAR is a pointer to a SHELL_VAR. */
+#define set_auto_export(var) \
+{ var->attributes |= att_exported; array_needs_making = 1; }
+
+/* Initialize the shell variables from the current environment. */
+initialize_shell_variables (env)
+     char *env[];
+{
+  extern char *primary_prompt, *secondary_prompt;
+  char *name, *string;
+  int c, char_index;
+  int string_index = 0;
+  SHELL_VAR *temp_var;
+
+  if (!shell_variables)
+    shell_variables = make_hash_table (0);
+
+  if (!shell_functions)
+    shell_functions = make_hash_table (0);
+
+  /* Set up $PWD. */
+  {
+    char *get_working_directory (), *cd;
+
+    cd = get_working_directory ("shell-init");
+    if (cd)
+      {
+       bind_variable ("PWD", cd);
+       free (cd);
+      }
+  }
+
+  while (string = env[string_index++])
+    {
+      char_index = 0;
+
+      name = (char *)alloca (1 + strlen (string));
+
+      while ((c = *string++) && c != '=')
+       name[char_index++] = c;
+
+      name[char_index] = '\0';
+
+      /* If exported function, define it now. */
+      /* Posix.2 style exported function: name()=value */
+      if (strncmp ("() {", string, 4) == 0 ||
+         ((name[char_index - 1] == ')' &&
+           name[char_index - 2] == '(' &&
+           string[0] == '{')))
+       {
+         char *eval_string;
+
+         eval_string = (char *)xmalloc (3 + strlen (string) + strlen (name));
+         sprintf (eval_string, "%s %s", name, string);
+         parse_and_execute (eval_string, name);
+
+         if (name[char_index - 1] == ')')
+           name[char_index - 2] = '\0';
+         set_func_auto_export (name);
+       }
+      else
+       {
+         SHELL_VAR *v;
+
+         v = bind_variable (name, string);
+         set_auto_export (v);
+       }
+    }
+
+  /* Remember this pid. */
+  dollar_dollar_pid = (int)getpid ();
+
+  /* Now make our own defaults in case the vars that we think are
+     important are missing. */
+  temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE);
+  set_auto_export (temp_var);
+
+  temp_var = set_if_not ("TERM", "dumb");
+  set_auto_export (temp_var);
+
+  set_if_not ("PS1", primary_prompt);
+  set_if_not ("PS2", secondary_prompt);
+  set_if_not ("IFS", " \t\n");
+
+  /* Magic machine types.  Pretty convenient. */
+  temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
+  set_auto_export (temp_var);
+
+  /* Default MAILPATH, and MAILCHECK. */
+  set_if_not ("MAILCHECK", "60");
+  if ((get_string_value ("MAIL") == (char *)NULL) &&
+      (get_string_value ("MAILPATH") == (char *)NULL))
+    {
+      extern char *current_user_name;
+      char *tem;
+
+      tem = (char *)xmalloc (1 + sizeof (DEFAULT_MAIL_PATH)
+                          + strlen (current_user_name));
+      strcpy (tem, DEFAULT_MAIL_PATH);
+      strcat (tem, current_user_name);
+
+      bind_variable ("MAILPATH", tem);
+      free (tem);
+    }
+
+  /* Do some things with shell level. */
+  temp_var = set_if_not ("SHLVL", "0");
+  set_auto_export (temp_var);
+  adjust_shell_level (1);
+
+  /* Make a variable $PPID, which holds the pid of the shell's parent.  */
+  {
+    char *ppid;
+    SHELL_VAR *v;
+    extern char *itos ();
+
+    ppid = itos ((int) getppid ());
+    v = find_variable ("PPID");
+
+    if (v)
+      v->attributes &= ~att_readonly;
+
+    v = bind_variable ("PPID", ppid);
+    v->attributes |= (att_readonly | att_integer);
+
+    non_unsettable ("PPID");
+    free (ppid);
+  }
+
+#if defined (GETOPTS_BUILTIN)
+  /* Initialize the `getopts' stuff. */
+  bind_variable ("OPTIND", "1");
+  bind_variable ("OPTERR", "1");
+#endif /* GETOPTS_BUILTIN */
+
+  /* Get the full pathname to THIS shell, and set the BASH variable
+     to it. */
+  {
+    extern char *shell_name, *find_user_command (), *full_pathname ();
+    extern int login_shell;
+    char *tname = find_user_command (shell_name);
+
+    if ((login_shell == 1) && (*shell_name != '/'))
+      {
+       struct passwd *entry = getpwuid (getuid ());
+
+       if (entry)
+         {
+           /* If HOME doesn't exist, set it. */
+           temp_var = (SHELL_VAR *)find_variable ("HOME");
+           if (!temp_var)
+             {
+               temp_var = bind_variable ("HOME", entry->pw_dir);
+               temp_var->attributes |= att_exported;
+             }
+           name = savestring (entry->pw_shell);
+         }
+       else
+         name = savestring ("a.out");
+       endpwent ();
+      }
+    else
+      {
+       if (!tname)
+         {
+           char *make_absolute ();
+           name = make_absolute (shell_name, get_string_value ("PWD"));
+         }
+       else
+         {
+           name = full_pathname (tname);
+           free (tname);
+         }
+      }
+
+    /* Make the exported environment variable SHELL be whatever the name of
+       this shell is.  Note that the `tset' command looks at this variable
+       to determine what style of commands to output; if it ends in "csh",
+       then C-shell commands are output, else Bourne shell commands. */
+    temp_var = set_if_not ("SHELL", name);
+    set_auto_export (temp_var);
+
+    /* Make a variable called BASH, which is the name of THIS shell. */
+    temp_var = bind_variable ("BASH", name);
+    temp_var->attributes |= att_exported;
+
+    free (name);
+  }
+
+  /* Make a variable called BASH_VERSION which contains the version info. */
+  {
+    char tt[12];
+    extern char *dist_version;
+    extern int build_version;
+
+    sprintf (tt, "%s.%d", dist_version, build_version);
+    bind_variable ("BASH_VERSION", tt);
+  }
+
+  /* Set history variables to defaults, and then do whatever we would
+     do if the variable had just been set. */
+  {
+    char *tilde_expand ();
+    char *tem = tilde_expand ("~/.bash_history");
+
+    set_if_not ("HISTFILE", tem);
+    free (tem);
+
+    set_if_not ("HISTSIZE", "500");
+    sv_histsize ("HISTSIZE");
+  }
+
+  /* seed the random number generator */
+
+  sbrand (dollar_dollar_pid);
+
+  /* If we have inherited `noclobber' from a previous shell, then set
+     noclobbering on. */
+  {
+    extern int noclobber;
+
+    noclobber = find_variable ("noclobber") != NULL;
+  }
+
+  /* Initialize the dynamic variables, and seed their values */
+  initialize_dynamic_variables ();
+
+  non_unsettable ("PATH");
+  non_unsettable ("PS1");
+  non_unsettable ("PS2");
+  non_unsettable ("IFS");
+
+  /* Get the users real user id, and save that in an readonly variable.
+     To make the variable *really* readonly, we have added it to a special
+     list of vars. */
+
+  sv_uids ();
+  set_var_read_only ("UID");
+  set_var_read_only ("EUID");
+
+  non_unsettable ("EUID");
+  non_unsettable ("UID");
+}
+
+adjust_shell_level (change)
+     int change;
+{
+  extern int shell_level;
+  extern char *itos ();
+  char *new_level;
+  int old_level;
+
+
+  old_level = atoi (get_string_value ("SHLVL"));
+  shell_level = old_level + change;
+  new_level = itos (shell_level);
+  bind_variable ("SHLVL", new_level);
+  free (new_level);
+}
+
+/* Add NAME to the list of variables that cannot be unset
+   if it isn't already there. */
+non_unsettable (name)
+     char *name;
+{
+  register int i;
+
+  if (!non_unsettable_vars)
+    {
+      non_unsettable_vars = (char **)xmalloc (1 * sizeof (char *));
+      non_unsettable_vars[0] = (char *)NULL;
+    }
+
+  for (i = 0; non_unsettable_vars[i]; i++)
+    if (strcmp (non_unsettable_vars[i], name) == 0)
+      return;
+
+  non_unsettable_vars = (char **)
+    xrealloc (non_unsettable_vars, (2 + i) * sizeof (char *));
+  non_unsettable_vars[i] = savestring (name);
+  non_unsettable_vars[i + 1] = (char *)NULL;
+}
+
+/* Set NAME to VALUE if NAME has no value. */
+SHELL_VAR *
+set_if_not (name, value)
+     char *name, *value;
+{
+  SHELL_VAR *v = find_variable (name);
+
+  if (!v)
+    v = bind_variable (name, value);
+  return (v);
+}
+
+/* Map FUNCTION over the variables in VARIABLES.  Return an array of the
+   variables that satisfy FUNCTION.  Satisfy means that FUNCTION returns
+   a non-zero value for.  A NULL value for FUNCTION means to use all
+   variables. */
+SHELL_VAR **
+map_over (function, var_hash_table)
+     Function *function;
+     HASH_TABLE* var_hash_table;
+{
+  register int i;
+  register BUCKET_CONTENTS *tlist;
+  SHELL_VAR *var, **list = (SHELL_VAR **)NULL;
+  int list_index = 0, list_size = 0;
+
+  for (i = 0; i < var_hash_table->nbuckets; i++)
+    {
+      tlist = get_hash_bucket (i, var_hash_table);
+
+      while (tlist)
+       {
+         var = (SHELL_VAR *)tlist->data;
+
+         if (!function || (*function) (var))
+           {
+             if (list_index + 1 >= list_size)
+               list = (SHELL_VAR **)
+                 xrealloc (list, (list_size += 20) * sizeof (SHELL_VAR *));
+
+             list[list_index++] = var;
+             list[list_index] = (SHELL_VAR *)NULL;
+           }
+         tlist = tlist->next;
+       }
+    }
+  return (list);
+}
+
+int
+qsort_var_comp (var1, var2)
+     SHELL_VAR **var1, **var2;
+{
+  return (strcmp ((*var1)->name, (*var2)->name));
+}
+
+sort_variables (array)
+     SHELL_VAR **array;
+{
+  qsort (array, array_len (array), sizeof (SHELL_VAR *), qsort_var_comp);
+}
+
+/* Create a NULL terminated array of all the shell variables in TABLE. */
+static SHELL_VAR **
+all_vars (table)
+     HASH_TABLE *table;
+{
+  SHELL_VAR **list;
+
+  list = map_over ((Function *)NULL, table);
+  if (list)
+    sort_variables (list);
+  return (list);
+}
+
+/* Create a NULL terminated array of all the shell variables. */
+SHELL_VAR **
+all_shell_variables ()
+{
+  return (all_vars (shell_variables));
+}
+
+/* Create a NULL terminated array of all the shell functions. */
+SHELL_VAR **
+all_shell_functions ()
+{
+  return (all_vars (shell_functions));
+}
+
+/* Print VARS to stdout in such a way that they can be read back in. */
+print_var_list (list)
+     register SHELL_VAR **list;
+{
+  register int i;
+  register SHELL_VAR *var;
+
+  for (i = 0; list && (var = list[i]); i++)
+    if (!invisible_p (var))
+      print_assignment (var);
+}
+
+#if defined (NOTDEF)
+/* Print LIST (a linked list of shell variables) to stdout
+   by printing the names, without the values.  Used to support the
+   `set +' command. */
+print_vars_no_values (list)
+     register SHELL_VAR **list;
+{
+  register int i;
+  register SHELL_VAR *var;
+
+  for (i = 0; list && (var = list[i]); i++)
+    if (!invisible_p (var))
+      printf ("%s\n", var->name);
+}
+#endif
+
+/* Print the value of a single SHELL_VAR.  No newline is
+   output, but the variable is printed in such a way that
+   it can be read back in. */
+print_assignment (var)
+     SHELL_VAR *var;
+{
+  if (function_p (var) && var->value)
+    {
+      printf ("%s=", var->name);
+      print_var_function (var);
+      printf ("\n");
+    }
+  else if (var->value)
+    {
+      printf ("%s=", var->name);
+      print_var_value (var);
+      printf ("\n");
+    }
+}
+
+/* Print the value cell of VAR, a shell variable.  Do not print
+   the name, nor leading/trailing newline. */
+print_var_value (var)
+     SHELL_VAR *var;
+{
+  if (var->value)
+    printf ("%s", var->value);
+}
+
+/* Print the function cell of VAR, a shell variable.  Do not
+   print the name, nor leading/trailing newline. */
+print_var_function (var)
+     SHELL_VAR *var;
+{
+  char *named_function_string ();
+
+  if (function_p (var) && var->value)
+    printf ("%s", named_function_string ((char *)NULL, var->value, 1));
+}
+
+
+/* **************************************************************** */
+/*                                                                  */
+/*                 Dynamic Variable Extension                       */
+/*                                                                  */
+/* **************************************************************** */
+
+/* DYNAMIC VARIABLES
+   
+   These are variables whose values are generated anew each time they are
+   referenced.  These are implemented using a pair of function pointers
+   in the struct variable: assign_func, which is called from bind_variable,
+   and dynamic_value, which is called from find_variable.
+   
+   assign_func is called from bind_variable, if bind_variable discovers
+   that the variable being assigned to has such a function.  The function
+   is called as
+       SHELL_VAR *temp = (*(entry->assign_func)) (entry, value)
+   and the (SHELL_VAR *)temp is returned as the value of bind_variable.  It
+   is usually ENTRY (self).
+   
+   dynamic_value is called from find_variable to return a `new' value for
+   the specified dynamic varible.  If this function is NULL, the variable
+   is treated as a `normal' shell variable.  If it is not, however, then
+   this function is called like this:
+       tempvar = (*(var->dynamic_value)) (var);
+   
+   Sometimes `tempvar' will replace the value of `var'.  Other times, the
+   shell will simply use the string value.  Pretty object-oriented, huh?
+   
+   Be warned, though: if you `unset' a special variable, it loses its
+   special meaning, even if you subsequently set it.
+   
+   The special assignment code would probably have been better put in
+   subst.c: do_assignment, in the same style as
+   stupidly_hack_special_variables, but I wanted the changes as
+   localized as possible.  */
+
+/* The value of $SECONDS.  This is the number of seconds since shell
+   invocation, or, the number of seconds since the last assignment + the
+   value of the last assignment. */
+static long seconds_value_assigned = (long)0;
+
+/* Originally defined in shell.c */
+extern time_t shell_start_time;
+
+SHELL_VAR *
+assign_seconds (self, value)
+     SHELL_VAR *self;
+     char *value;
+{
+  seconds_value_assigned = atol (value);
+  shell_start_time = NOW;
+  return (self);
+}
+
+SHELL_VAR *
+get_seconds (var)
+     SHELL_VAR *var;
+{
+  extern char *itos ();
+  time_t time_since_start;
+  char *p;
+
+  time_since_start = NOW - shell_start_time;
+  p = itos((int) seconds_value_assigned + time_since_start);
+
+  if (var->value)
+    free (var->value);
+
+  var->attributes |= att_integer;
+  var->value = p;
+  return (var);
+}
+
+/* The random number seed.  You can change this by setting RANDOM. */
+static unsigned long rseed = 1;
+
+/* A linear congruential random number generator based on the ANSI
+   C standard.  A more complicated one is overkill.  */
+
+/* Returns a pseudo-random number between 0 and 32767. */
+static int
+brand ()
+{
+  rseed = rseed * 1103515245 + 12345;
+  return ((unsigned int)(rseed / 65536) % 32768);
+}
+
+/* Set the random number generator seed to SEED. */
+static void
+sbrand (seed)
+     int seed;
+{
+  rseed = seed;
+}
+
+static SHELL_VAR *
+assign_random (self, value)
+     SHELL_VAR *self;
+     char *value;
+{
+  int s = atoi (value);
+
+  sbrand (s);
+  return (self);
+}
+
+static SHELL_VAR *
+get_random (var)
+     SHELL_VAR *var;
+{
+  int rv;
+  char *p;
+  extern char *itos ();
+
+  rv = brand ();
+  p = itos ((int)rv);
+  if (var->value)
+    free (var->value);
+
+  var->attributes |= att_integer;
+  var->value = p;
+  return (var);
+}
+
+/* Function which returns the current line number. */
+static SHELL_VAR *
+get_lineno (var)
+     SHELL_VAR *var;
+{
+  extern int line_number;
+  char *p;
+  extern char *itos ();
+
+  p = itos (line_number);
+  if (var->value)
+    free (var->value);
+  var->value = p;
+  return (var);
+}
+
+initialize_dynamic_variables ()
+{
+  SHELL_VAR *v;
+
+  v = bind_variable ("SECONDS", (char *)NULL);
+  v->dynamic_value = get_seconds;
+  v->assign_func = assign_seconds;
+
+  v = bind_variable ("RANDOM", (char *)NULL);
+  v->dynamic_value = get_random;
+  v->assign_func = assign_random;
+
+  v = bind_variable ("LINENO", (char *)NULL);
+  v->dynamic_value = get_lineno;
+  v->assign_func = (DYNAMIC_FUNC *)NULL;
+}
+
+/* How to get a pointer to the shell variable or function named NAME.
+   HASHED_VARS is a pointer to the hash table containing the list
+   of interest (either variables or functions). */
+SHELL_VAR *
+var_lookup (name, hashed_vars)
+     char *name;
+     HASH_TABLE *hashed_vars;
+{
+  BUCKET_CONTENTS *bucket;
+
+  bucket = find_hash_item (name, hashed_vars);
+
+  if (bucket)
+    return ((SHELL_VAR *)bucket->data);
+  else
+    return ((SHELL_VAR *)NULL);
+}
+
+/* Look up the variable entry whose name matches STRING.
+   Returns the entry or NULL. */
+SHELL_VAR *
+find_variable (name)
+     char *name;
+{
+  extern int variable_context;
+  extern Function *this_shell_builtin;
+  SHELL_VAR *find_tempenv_variable ();
+  SHELL_VAR *var = (SHELL_VAR *)NULL;
+
+  /* If we are executing a function or builtin, first look in the
+     temporary environment for the variable.  This allows constructs
+     such as "foo=x eval 'echo $foo'" to get the `exported' value
+     of $foo. */
+  if (variable_context || this_shell_builtin)
+    var = find_tempenv_variable (name);
+
+  if (!var)
+    var = var_lookup (name, shell_variables);
+
+  if (!var)
+    return ((SHELL_VAR *)NULL);
+
+  if (var->dynamic_value)
+    return ((*(var->dynamic_value)) (var));
+  else
+    return (var);
+}
+
+/* Look up the function entry whose name matches STRING.
+   Returns the entry or NULL. */
+SHELL_VAR *
+find_function (name)
+     char *name;
+{
+  return (var_lookup (name, shell_functions));
+}
+
+/* Return the string value of a variable.  Return NULL if the variable
+   doesn't exist, or only has a function as a value.  Don't cons a new
+   string. */
+char *
+get_string_value (var_name)
+     char *var_name;
+{
+  SHELL_VAR *var = find_variable (var_name);
+
+  if (!var)
+    return (char *)NULL;
+  else
+    return (var->value);
+}
+
+/* Create a local variable referenced by NAME. */
+SHELL_VAR *
+make_local_variable (name)
+     char *name;
+{
+  SHELL_VAR *new_var, *old_var, *bind_variable ();
+  BUCKET_CONTENTS *elt;
+
+  /* local foo; local foo;  is a no-op. */
+  old_var = find_variable (name);
+  if (old_var && old_var->context == variable_context)
+    return (old_var);
+
+  elt = remove_hash_item (name, shell_variables);
+  if (elt)
+    {
+      old_var = (SHELL_VAR *)elt->data;
+      free (elt->key);
+      free (elt);
+    }
+  else
+    old_var = (SHELL_VAR *)NULL;
+
+  /* If a variable does not already exist with this name, then
+     just make a new one. */
+  if (!old_var)
+    {
+      new_var = bind_variable (name, "");
+    }
+  else
+    {
+      new_var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+
+      new_var->name = savestring (name);
+      new_var->value = savestring ("");
+
+      new_var->dynamic_value = (DYNAMIC_FUNC *)NULL;
+      new_var->assign_func = (DYNAMIC_FUNC *)NULL;
+
+      new_var->attributes = 0;
+
+      if (exported_p (old_var))
+       new_var->attributes |= att_exported;
+
+      new_var->prev_context = old_var;
+      elt = add_hash_item (savestring (name), shell_variables);
+      elt->data = (char *)new_var;
+    }
+
+  new_var->context = variable_context;
+  return (new_var);
+}
+
+/* Bind a variable NAME to VALUE.  This conses up the name
+   and value strings. */
+SHELL_VAR *
+bind_variable (name, value)
+     char *name, *value;
+{
+  SHELL_VAR *entry = var_lookup (name, shell_variables);
+
+  if (!entry)
+    {
+      /* Make a new entry for this variable.  Then do the binding. */
+      entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+
+      entry->attributes = 0;
+      entry->name = savestring (name);
+
+      if (value)
+       entry->value = savestring (value);
+      else
+       entry->value = (char *)NULL;
+
+      entry->dynamic_value = (DYNAMIC_FUNC *)NULL;
+      entry->assign_func = (DYNAMIC_FUNC *)NULL;
+
+      /* Always assume variables are to be made at toplevel!
+        make_local_variable has the responsibilty of changing the
+        variable context. */
+      entry->context = 0;
+      entry->prev_context = (SHELL_VAR *)NULL;
+
+      {
+       BUCKET_CONTENTS *elt;
+
+       elt = add_hash_item (savestring (name), shell_variables);
+       elt->data = (char *)entry;
+      }
+    }
+  else if (entry->assign_func)
+    return ((*(entry->assign_func)) (entry, value));
+  else
+    {
+      if (readonly_p (entry))
+       {
+         report_error ("%s: read-only variable", name);
+         return (entry);
+       }
+
+      /* If this variable has had its type set to integer (via `declare -i'),
+        then do expression evaluation on it and store the result.  The
+        functions in expr.c (evalexp and bind_int_variable) are responsible
+        for turning off the integer flag if they don't want further
+        evaluation done. */
+      if (integer_p (entry))
+       {
+         long val, evalexp();
+         extern char *itos();
+
+         val = evalexp (value);
+         /* We cannot free () entry->value before this; what if the string
+            we are working is `even=even+2'?  We need the original value
+            around while we are doing the evaluation to handle any possible
+            recursion. */
+         if (entry->value)
+           free (entry->value);
+
+         entry->value = itos (val);
+       }
+      else
+       {
+         if (entry->value)
+           free (entry->value);
+
+         if (value)
+           entry->value = savestring (value);
+         else
+           entry->value = (char *)NULL;
+       }
+    }
+
+  if (mark_modified_vars)
+    entry->attributes |= att_exported;
+
+  if (exported_p (entry))
+    array_needs_making = 1;
+
+  return (entry);
+}
+
+/* Dispose of the information attached to VAR. */
+dispose_variable (var)
+     SHELL_VAR *var;
+{
+  if (!var)
+    return;
+
+  if (function_p (var))
+    dispose_command (var->value);
+  else if (var->value)
+    free (var->value);
+
+  free (var->name);
+
+  if (exported_p (var))
+    array_needs_making = 1;
+
+  free (var);
+}
+
+/* Unset the variable referenced by NAME. */
+unbind_variable (name)
+     char *name;
+{
+  SHELL_VAR *var = find_variable (name);
+
+  if (!var)
+    return (-1);
+
+  if (var->value)
+    {
+      free (var->value);
+      var->value = (char *)NULL;
+    }
+
+  makunbound (name, shell_variables);
+
+  return (0);
+}
+
+/* Make the variable associated with NAME go away.  HASH_LIST is the
+   hash table from which this variable should be deleted (either
+   shell_variables or shell_functions).
+   Returns non-zero if the variable couldn't be found. */
+makunbound (name, hash_list)
+     char *name;
+     HASH_TABLE *hash_list;
+{
+  BUCKET_CONTENTS *elt;
+  SHELL_VAR *old_var, *new_var;
+
+  elt = remove_hash_item (name, hash_list);
+
+  if (!elt)
+    return (-1);
+
+  old_var = (SHELL_VAR *)elt->data;
+  new_var = old_var->prev_context;
+
+  if (old_var && exported_p (old_var))
+    array_needs_making++;
+
+  if (new_var)
+    {
+      /* Has to be a variable, functions don't have previous contexts. */
+      BUCKET_CONTENTS *new_elt;
+
+      new_elt = add_hash_item (savestring (new_var->name), hash_list);
+      new_elt->data = (char *)new_var;
+
+      if (exported_p (new_var))
+       set_var_auto_export (new_var->name);
+    }
+
+  free (elt->key);
+  free (elt);
+
+  dispose_variable (old_var);
+  stupidly_hack_special_variables (name);
+  return (0);
+}
+
+/* Remove the variable with NAME if it is a local variable in the
+   current context. */
+kill_local_variable (name)
+     char *name;
+{
+  SHELL_VAR *temp = find_variable (name);
+
+  if (temp && (temp->context == variable_context))
+    {
+      makunbound (name, shell_variables);
+      return (0);
+    }
+  return (-1);
+}
+
+/* Get rid of all of the variables in the current context. */
+int
+variable_in_context (var)
+     SHELL_VAR *var;
+{
+  return (var && var->context == variable_context);
+}
+
+kill_all_local_variables ()
+{
+  register int i, pass;
+  register SHELL_VAR *var, **list;
+  HASH_TABLE *varlist;
+
+  for (pass = 0; pass < 2; pass++)
+    {
+      varlist = pass ? shell_functions : shell_variables;
+
+      list = map_over (variable_in_context, varlist);
+
+      if (list)
+       {
+         for (i = 0; var = list[i]; i++)
+           makunbound (var->name, varlist);
+
+         free (list);
+       }
+    }
+}
+
+/* Delete the entire contents of the hash table. */
+delete_all_variables (hashed_vars)
+     HASH_TABLE *hashed_vars;
+{
+  register int i;
+  register BUCKET_CONTENTS *bucket;
+
+  for (i = 0; i < hashed_vars->nbuckets; i++)
+    {
+      bucket = hashed_vars->bucket_array[i];
+
+      while (bucket)
+       {
+         BUCKET_CONTENTS *temp = bucket;
+         SHELL_VAR *var, *prev;
+
+         bucket = bucket->next;
+
+         var = (SHELL_VAR *)temp->data;
+
+         while (var)
+           {
+             prev = var->prev_context;
+             dispose_variable (var);
+
+             var = prev;
+           }
+
+         free (temp->key);
+         free (temp);
+       }
+      hashed_vars->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
+    }
+}
+
+SHELL_VAR *
+new_shell_variable (name)
+     char *name;
+{
+  SHELL_VAR *var;
+
+  var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+
+  bzero (var, sizeof (SHELL_VAR));
+  var->name = savestring (name);
+  return (var);
+}
+
+/* Do a function binding to a variable.  You pass the name and
+   the command to bind to.  This conses the name and command. */
+SHELL_VAR *
+bind_function (name, value)
+     char *name;
+     COMMAND *value;
+{
+  SHELL_VAR *entry = find_function (name);
+
+  if (!entry)
+    {
+      BUCKET_CONTENTS *elt;
+
+      elt = add_hash_item (savestring (name), shell_functions);
+
+      elt->data = (char *)new_shell_variable (name);
+      entry = (SHELL_VAR *)elt->data;
+      entry->dynamic_value = (DYNAMIC_FUNC *)NULL;
+      entry->assign_func = (DYNAMIC_FUNC *)NULL;
+
+      /* Functions are always made at the top level.  This allows a
+        function to define another function (like autoload). */
+      entry->context = 0;
+    }
+
+  if (entry->value)
+    dispose_command (entry->value);
+
+  if (value)   /* I don't think this can happen anymore */
+    entry->value = (char *)copy_command (value);
+  else
+    entry->value = (char *)NULL;
+
+  entry->attributes |= att_function;
+
+  if (mark_modified_vars)
+    entry->attributes |= att_exported;
+
+  entry->attributes &= ~att_invisible; /* Just to be sure */
+
+  array_needs_making = 1;
+
+  return (entry);
+}
+
+/* Copy VAR to a new data structure and return that structure. */
+SHELL_VAR *
+copy_variable (var)
+     SHELL_VAR *var;
+{
+  SHELL_VAR *copy = (SHELL_VAR *)NULL;
+
+  if (var)
+    {
+      copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+
+      copy->attributes = var->attributes;
+      copy->name = savestring (var->name);
+
+      if (function_p (var))
+       copy->value = (char *)copy_command (var->value);
+      else if (var->value)
+       copy->value = savestring (var->value);
+      else
+       copy->value = (char *)NULL;
+
+      copy->dynamic_value = var->dynamic_value;
+      copy->assign_func = var->assign_func;
+
+      copy->context = var->context;
+
+      /* Don't bother copying previous contexts along with this variable. */
+      copy->prev_context = (SHELL_VAR *)NULL;
+    }
+  return (copy);
+}
+
+/* Make the variable associated with NAME be read-only.
+   If NAME does not exist yet, create it. */
+set_var_read_only (name)
+     char *name;
+{
+  SHELL_VAR *entry = find_variable (name);
+
+  if (!entry)
+    {
+      entry = bind_variable (name, "");
+      if (!no_invisible_vars)
+       entry->attributes |= att_invisible;
+    }
+  entry->attributes |= att_readonly;
+}
+
+/* Make the function associated with NAME be read-only.
+   If NAME does not exist, we just punt, like auto_export code below. */
+set_func_read_only (name)
+     char *name;
+{
+  SHELL_VAR *entry = find_function (name);
+
+  if (entry)
+    entry->attributes |= att_readonly;
+}
+
+/* Make the variable associated with NAME be auto-exported.
+   If NAME does not exist yet, create it. */
+set_var_auto_export (name)
+     char *name;
+{
+  SHELL_VAR *entry = find_variable (name);
+
+  if (!entry)
+    {
+      entry = bind_variable (name, "");
+      if (!no_invisible_vars)
+       entry->attributes |= att_invisible;
+    }
+
+  set_auto_export (entry);
+}
+
+/* Make the function associated with NAME be auto-exported. */
+set_func_auto_export (name)
+     char *name;
+{
+  SHELL_VAR *entry = find_function (name);
+
+  if (entry)
+    {
+      entry->attributes |= att_exported;
+      array_needs_making = 1;
+    }
+}
+
+/* Returns non-zero if STRING is an assignment statement.  The returned value
+   is the index of the `=' sign. */
+assignment (string)
+     char *string;
+{
+  register int c, index = 0;
+
+  c = string[index];
+
+  if (!isletter (c) && c != '_')
+    return (0);
+
+  while (c = string[index])
+    {
+      /* The following is safe.  Note that '=' at the start of a word
+        is not an assignment statement. */
+      if (c == '=')
+       return (index);
+
+      if (!isletter (c) && !digit (c) && c != '_')
+       return (0);
+
+      index++;
+    }
+  return (0);
+}
+
+int
+visible_var (var)
+     SHELL_VAR *var;
+{
+  return (!invisible_p (var));
+}
+
+SHELL_VAR **
+all_visible_variables ()
+{
+  SHELL_VAR **list;
+
+  list = map_over (visible_var, shell_variables);
+
+  if (list)
+    sort_variables (list);
+
+  return (list);
+}
+
+SHELL_VAR **
+all_visible_functions ()
+{
+  SHELL_VAR **list;
+
+  list = map_over (visible_var, shell_functions);
+
+  if (list)
+    sort_variables (list);
+
+  return (list);
+}
+
+/* Return non-zero if the variable VAR is visible and exported. */
+int
+visible_and_exported (var)
+     SHELL_VAR *var;
+{
+  return (!invisible_p (var) && exported_p (var));
+}
+
+/* Make an array of assignment statements from the hash table
+   HASHED_VARS which contains SHELL_VARs.  Only visible, exported
+   variables are eligible. */
+char **
+make_var_array (hashed_vars)
+     HASH_TABLE *hashed_vars;
+{
+  register int i, list_index;
+  register SHELL_VAR *var;
+  char **list = (char **)NULL;
+  SHELL_VAR **vars;
+
+  vars = map_over (visible_and_exported, hashed_vars);
+
+  if (!vars)
+    return (char **)NULL;
+
+  list = (char **)xmalloc ((1 + array_len ((char **)vars)) * sizeof (char *));
+
+  for (i = 0, list_index = 0; var = vars[i]; i++)
+    {
+      char *value, *named_function_string ();
+
+      if (function_p (var))
+       {
+         value =
+           named_function_string ((char *)NULL,
+                                  (COMMAND *)function_cell (var), 0);
+       }
+      else
+       value = value_cell (var);
+
+      if (value)
+       {
+#if 0
+         list[list_index] =
+           (char *)xmalloc (2 + strlen (var->name) + strlen (value));
+
+         sprintf (list[list_index], "%s=%s", var->name, value);
+#else
+         /* Let's see if this makes any kind of performance difference. */
+         int name_len = strlen (var->name);
+         int value_len = strlen (value);
+         char  *p;
+
+         p = list[list_index] =  (char *)xmalloc (2 + name_len + value_len);
+         strcpy (p, var->name);
+         p[name_len] = '=';
+         strcpy (&p[name_len + 1], value);
+#endif
+         list_index++;
+       }
+    }
+
+  free (vars);
+  list[list_index] = (char *)NULL;
+  return (list);
+}
+
+/* Add STRING to the array of foo=bar strings that we already
+   have to add to the environment.  */
+assign_in_env (string)
+     char *string;
+{
+  int size;
+
+  int offset = assignment (string);
+  char *name = savestring (string);
+  char *temp, *value = (char *)NULL;
+
+  if (name[offset] == '=')
+    {
+      char *tilde_expand (), *string_list ();
+      WORD_LIST *list, *expand_string_unsplit ();
+
+      name[offset] = 0;
+      temp = name + offset + 1;
+      temp = tilde_expand (temp);
+
+      list = expand_string_unsplit (temp, 0);
+      value = string_list (list);
+
+      if (list)
+       dispose_words (list);
+
+      free (temp);
+    }
+
+  if (!value) value = savestring ("");
+
+  temp = (char *)xmalloc (2 + strlen (name) + strlen (value));
+  sprintf (temp, "%s=%s", name, value);
+  free (name);
+
+  if (!temporary_env)
+    {
+      temporary_env = (char **)xmalloc (sizeof (char *));
+      temporary_env [0] = (char *)NULL;
+    }
+
+  size = array_len (temporary_env);
+  temporary_env = (char **)
+    xrealloc (temporary_env, (size + 2) * (sizeof (char *)));
+
+  temporary_env[size] = (temp);
+  temporary_env[size + 1] = (char *)NULL;
+  array_needs_making = 1;
+
+  if (echo_command_at_execute)
+    {
+      /* The K*rn shell prints the `+ ' in front of assignment statements,
+        so we do too. */
+      extern char *indirection_level_string ();
+      fprintf (stderr, "%s%s\n", indirection_level_string (), temp);
+      fflush (stderr);
+    }
+}
+
+/* Find a variable in the temporary environment that is named NAME.
+   Return a consed variable, or NULL if not found. */
+SHELL_VAR *
+find_tempenv_variable (name)
+     char *name;
+{
+  register int i, l = strlen (name);
+
+  if (!temporary_env)
+    return ((SHELL_VAR *)NULL);
+
+  for (i = 0; temporary_env[i]; i++)
+    {
+      if (strncmp (temporary_env[i], name, l) == 0 &&
+         temporary_env[i][l] == '=')
+       {
+         SHELL_VAR *temp;
+
+         temp = new_shell_variable (name);
+
+         if (temporary_env[i][l + 1])
+           temp->value = savestring (&temporary_env[i][l + 1]);
+         else
+           temp->value = savestring ("");
+         temp->attributes = att_exported;
+         temp->context = 0;
+         temp->prev_context = (SHELL_VAR *)NULL;
+
+         temp->dynamic_value = (DYNAMIC_FUNC *)NULL;
+         temp->assign_func = (DYNAMIC_FUNC *)NULL;
+
+         return (temp);
+       }
+    }
+  return ((SHELL_VAR *)NULL);
+}
+
+/* Free the storage used in the variable array for temporary
+   environment variables. */
+dispose_used_env_vars ()
+{
+  if (!temporary_env)
+    return;
+
+  free_array (temporary_env);
+  temporary_env = (char **)NULL;
+  array_needs_making = 1;
+}
+
+/* Stupid comparison routine for qsort () ing strings. */
+int
+qsort_string_compare (s1, s2)
+     register char **s1, **s2;
+{
+  return (strcmp (*s1, *s2));
+}
+
+/* Sort ARRAY, a null terminated array of pointers to strings. */
+sort_char_array (array)
+     char **array;
+{
+  qsort (array, array_len (array), sizeof (char *), qsort_string_compare);
+}
+
+#define ISFUNC(s, o) ((s[o + 1] == '(')  && (s[o + 2] == ')'))
+
+/* Add ASSIGN to ARRAY, or supercede a previous assignment in the
+   array with the same left-hand side.  Return the new array. */
+char **
+add_or_supercede (assign, array)
+     char *assign;
+     register char **array;
+{
+  register int i;
+  int equal_offset = assignment (assign);
+
+  if (!equal_offset)
+    return (array);
+
+  /* If this is a function, then only supercede the function definition.
+     We do this by including the `=(' in the comparison.  */
+  if (assign[equal_offset + 1] == '(')
+    equal_offset++;
+
+  for (i = 0; array[i]; i++)
+    {
+      if (STREQN (assign, array[i], equal_offset + 1))
+       {
+         free (array[i]);
+         array[i] = savestring (assign);
+         return (array);
+       }
+    }
+  array = (char **)xrealloc (array, ((2 + i) * sizeof (char *)));
+  array[i++] = savestring (assign);
+  array[i] = (char *)NULL;
+  return (array);
+}
+
+/* Make the environment array for the command about to be executed.  If the
+   array needs making.  Otherwise, do nothing.  If a shell action could
+   change the array that commands receive for their environment, then the
+   code should `array_needs_making++'. */
+maybe_make_export_env ()
+{
+  register int i;
+  register char **temp_array;
+
+  if (array_needs_making)
+    {
+      if (export_env)
+       free_array (export_env);
+
+#ifdef SHADOWED_ENV
+      export_env =
+       (char **)xmalloc ((1 + array_len (shell_environment)) * sizeof (char *));
+
+      for (i = 0; shell_environment[i]; i++)
+       export_env[i] = savestring (shell_environment[i]);
+      export_env[i] = (char *)NULL;
+
+#else /* !SHADOWED_ENV */
+
+      export_env = (char **)xmalloc (sizeof (char *));
+      export_env[0] = (char *)NULL;
+
+#endif /* SHADOWED_ENV */
+
+      temp_array = make_var_array (shell_variables);
+      for (i = 0; temp_array && temp_array[i]; i++)
+       export_env = add_or_supercede (temp_array[i], export_env);
+      free_array (temp_array);
+
+      temp_array = make_var_array (shell_functions);
+      for (i = 0; temp_array && temp_array[i]; i++)
+       export_env = add_or_supercede (temp_array[i], export_env);
+      free_array (temp_array);
+
+      if (temporary_env)
+       {
+         for (i = 0; temporary_env[i]; i++)
+           export_env = add_or_supercede (temporary_env[i], export_env);
+
+         /* Sort the array alphabetically. */
+         sort_char_array (export_env);
+       }
+      array_needs_making = 0;
+    }
+}
+
+/* We always put _ in the environment as the name of this command. */
+put_command_name_into_env (command_name)
+     char *command_name;
+{
+  char *dummy;
+
+  dummy = (char *)xmalloc (4 + strlen (command_name));
+
+  /* These three statements replace a call to sprintf */
+  dummy[0] = '_';
+  dummy[1] = '=';
+  strcpy (&dummy[2], command_name);
+  export_env = add_or_supercede (dummy, export_env);
+  free (dummy);
+}
+
+/* We supply our own version of getenv () because we want library
+   routines to get the changed values of exported variables. */
+char *last_tempenv_value = (char *)NULL;
+
+/* The NeXT C library has getenv () defined and used in the same file.
+   This screws our scheme.  However, Bash will run on the NeXT using
+   the C library getenv (), since right now the only environment variable
+   that we care about is HOME, and that is already defined.  */
+#if defined (__STDC__)
+#  define _CONST_HACK const
+#else
+#  define _CONST_HACK
+#endif /* !__STDC__ */
+
+#if !defined (NeXT)
+char *
+getenv (name)
+     char _CONST_HACK *name;
+{
+  SHELL_VAR *var = find_tempenv_variable (name);
+
+  if (var)
+    {
+      if (last_tempenv_value)
+       free (last_tempenv_value);
+
+      last_tempenv_value = savestring (value_cell (var));
+      dispose_variable (var);
+      return (last_tempenv_value);
+    }
+  else if (shell_variables)
+    {
+      var = find_variable (name);
+      if (var && exported_p (var))
+       return (value_cell (var));
+    }
+  else
+    {
+      register int i, len = strlen (name);
+      extern char **environ;
+
+      /* In some cases, s5r3 invokes getenv() before main(); BSD systems
+         using gprof also exhibit this behavior.  This means that
+         shell_variables will be 0 when this is invoked.  We look up the
+        variable in the real environment in that case. */
+
+      for (i = 0; environ[i]; i++)
+       {
+         if ((strncmp (environ[i], name, len) == 0) &&
+             (environ[i][len] == '='))
+           return (environ[i] + len + 1);
+       }
+    }
+
+  return ((char *)NULL);
+}
+#endif /* NeXT */