From df14511c45b0c0ceda09b6adcbf94a03b2ed59a5 Mon Sep 17 00:00:00 2001 From: "William F. Jolitz" Date: Sat, 18 Jan 1992 11:46:31 -0800 Subject: [PATCH] 386BSD 0.1 development Work on file usr/othersrc/public/bash-1.12/bash-1.12/variables.c Co-Authored-By: Lynne Greer Jolitz Synthesized-from: 386BSD-0.1 --- .../public/bash-1.12/bash-1.12/variables.c | 1608 +++++++++++++++++ 1 file changed, 1608 insertions(+) create mode 100644 usr/othersrc/public/bash-1.12/bash-1.12/variables.c 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 index 0000000000..4f8dd3864f --- /dev/null +++ b/usr/othersrc/public/bash-1.12/bash-1.12/variables.c @@ -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 +#include +#include +#include + +#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 */ -- 2.20.1