/* variables.c -- Functions for hacking shell variables. */
-/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005 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)
+ the Free Software Foundation; either version 2, or (at your option)
any later version.
Bash is distributed in the hope that it will be useful, but WITHOUT
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. */
+ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
#include "config.h"
#include "bashtypes.h"
#include "posixstat.h"
+#include "posixtime.h"
+
+#if defined (qnx)
+# if defined (qnx6)
+# include <sy/netmgr.h>
+# else
+# include <sys/vc.h>
+# endif /* !qnx6 */
+#endif /* qnx */
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <stdio.h>
-#include <ctype.h>
+#include "chartypes.h"
#include <pwd.h>
#include "bashansi.h"
+#include "bashintl.h"
#include "shell.h"
#include "flags.h"
#include "execute_cmd.h"
+#include "findcmd.h"
#include "mailcheck.h"
#include "input.h"
+#include "hashcmd.h"
+#include "pathexp.h"
+#include "builtins/getopt.h"
#include "builtins/common.h"
-#include <tilde/tilde.h>
+
+#if defined (READLINE)
+# include "bashline.h"
+# include <readline/readline.h>
+#else
+# include <tilde/tilde.h>
+#endif
#if defined (HISTORY)
# include "bashhist.h"
+# include <readline/history.h>
#endif /* HISTORY */
+#if defined (PROGRAMMABLE_COMPLETION)
+# include "pcomplete.h"
+#endif
+
+#define TEMPENV_HASH_BUCKETS 4 /* must be power of two */
+
+#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
+
+extern char **environ;
+
/* Variables used here and defined in other files. */
extern int posixly_correct;
-extern int variable_context, line_number;
-extern int interactive, interactive_shell, login_shell;
-extern int subshell_environment, indirection_level;
+extern int line_number;
+extern int subshell_environment, indirection_level, subshell_level;
extern int build_version, patch_level;
+extern int expanding_redir;
extern char *dist_version, *release_status;
extern char *shell_name;
extern char *primary_prompt, *secondary_prompt;
extern char *current_host_name;
-extern Function *this_shell_builtin;
+extern sh_builtin_func_t *this_shell_builtin;
+extern SHELL_VAR *this_shell_function;
+extern char *the_printed_command_except_trap;
extern char *this_command_name;
+extern char *command_execution_string;
extern time_t shell_start_time;
-/* The list of shell variables that the user has created, or that came from
- the environment. */
-HASH_TABLE *shell_variables = (HASH_TABLE *)NULL;
+#if defined (READLINE)
+extern int no_line_editing;
+extern int perform_hostname_completion;
+#endif
+
+/* The list of shell variables that the user has created at the global
+ scope, or that came from the environment. */
+VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL;
+
+/* The current list of shell variables, including function scopes */
+VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL;
/* The list of shell functions that the user has created, or that came from
the environment. */
HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
+#if defined (DEBUGGER)
+/* The table of shell function definitions that the user defined or that
+ came from the environment. */
+HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL;
+#endif
+
/* 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
+/* The set of shell assignments which are made only in the environment
for a single command. */
-char **temporary_env = (char **)NULL;
+HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;
-/* The array of shell assignments which are in the environment for the
- execution of a shell function. */
-char **function_env = (char **)NULL;
-
-/* The array of shell assignments which are made only in the environment
- for the execution of a shell builtin command which may cause more than
- one command to be executed (e.g., "source"). */
-char **builtin_env = (char **)NULL;
+/* Set to non-zero if an assignment error occurs while putting variables
+ into the temporary environment. */
+int tempenv_assign_error;
/* Some funky variables which are known about specially. Here is where
"$*", "$1", and all the cruft is kept. */
WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
/* The value of $$. */
-int dollar_dollar_pid;
+pid_t 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
+ manufactured from the union of the initial environment and the
shell variables that are marked for export. */
char **export_env = (char **)NULL;
+static int export_env_index;
+static int export_env_size;
+
+#if defined (READLINE)
+static int winsize_assignment; /* currently assigning to LINES or COLUMNS */
+static int winsize_assigned; /* assigned to LINES or COLUMNS */
+#endif
/* Non-zero means that we have to remake EXPORT_ENV. */
int array_needs_making = 1;
by initialize_variables (). */
int shell_level = 0;
-static char *have_local_variables;
-static int local_variable_stack_size;
-
/* Some forward declarations. */
-static void uidset ();
-static void initialize_dynamic_variables ();
-static void make_vers_array ();
-static void sbrand (); /* set bash random number generator. */
-static int qsort_var_comp ();
+static void set_machine_vars __P((void));
+static void set_home_var __P((void));
+static void set_shell_var __P((void));
+static char *get_bash_name __P((void));
+static void initialize_shell_level __P((void));
+static void uidset __P((void));
+#if defined (ARRAY_VARS)
+static void make_vers_array __P((void));
+#endif
+
+static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t));
+#if defined (ARRAY_VARS)
+static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t));
+#endif
+static SHELL_VAR *get_self __P((SHELL_VAR *));
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
+#endif
+
+static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_seconds __P((SHELL_VAR *));
+static SHELL_VAR *init_seconds_var __P((void));
+
+static int brand __P((void));
+static void sbrand __P((unsigned long)); /* set bash random number generator. */
+static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_random __P((SHELL_VAR *));
+
+static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_lineno __P((SHELL_VAR *));
+
+static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_subshell __P((SHELL_VAR *));
-/* Make VAR be auto-exported. VAR is a pointer to a SHELL_VAR. */
-#define set_auto_export(var) \
- do { var->attributes |= att_exported; array_needs_making = 1; } while (0)
+#if defined (HISTORY)
+static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
+#endif
+
+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
+static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
+#endif
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *get_groupset __P((SHELL_VAR *));
+#endif
+
+static SHELL_VAR *get_funcname __P((SHELL_VAR *));
+static SHELL_VAR *init_funcname_var __P((void));
+
+static void initialize_dynamic_variables __P((void));
+
+static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
+static SHELL_VAR *new_shell_variable __P((const char *));
+static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
+
+static void free_variable_hash_data __P((PTR_T));
+
+static VARLIST *vlist_alloc __P((int));
+static VARLIST *vlist_realloc __P((VARLIST *, int));
+static void vlist_add __P((VARLIST *, SHELL_VAR *, int));
+
+static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
+
+static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **));
+
+static SHELL_VAR **vapply __P((sh_var_map_func_t *));
+static SHELL_VAR **fapply __P((sh_var_map_func_t *));
-/* Initialize the shell variables from the current environment. */
+static int visible_var __P((SHELL_VAR *));
+static int visible_and_exported __P((SHELL_VAR *));
+static int local_and_exported __P((SHELL_VAR *));
+static int variable_in_context __P((SHELL_VAR *));
+#if defined (ARRAY_VARS)
+static int visible_array_vars __P((SHELL_VAR *));
+#endif
+
+static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
+static void push_temp_var __P((PTR_T));
+static void propagate_temp_var __P((PTR_T));
+static void dispose_temporary_env __P((sh_free_func_t *));
+
+static inline char *mk_env_string __P((const char *, const char *));
+static char **make_env_array_from_var_list __P((SHELL_VAR **));
+static char **make_var_export_array __P((VAR_CONTEXT *));
+static char **make_func_export_array __P((void));
+static void add_temp_array_to_env __P((char **, int, int));
+
+static int n_shell_variables __P((void));
+static int set_context __P((SHELL_VAR *));
+
+static void push_func_var __P((PTR_T));
+static void push_exported_var __P((PTR_T));
+
+static inline int find_special_var __P((const char *));
+
+/* Initialize the shell variables from the current environment.
+ If PRIVMODE is nonzero, don't import functions from ENV or
+ parse $SHELLOPTS. */
void
-initialize_shell_variables (env, no_functions)
+initialize_shell_variables (env, privmode)
char **env;
- int no_functions; /* If set, don't import functions from ENV. */
+ int privmode;
{
char *name, *string, *temp_string;
int c, char_index, string_index, string_length;
SHELL_VAR *temp_var;
- if (!shell_variables)
- shell_variables = make_hash_table (0);
+ if (shell_variables == 0)
+ {
+ shell_variables = global_variables = new_var_context ((char *)NULL, 0);
+ shell_variables->scope = 0;
+ shell_variables->table = hash_create (0);
+ }
+
+ if (shell_functions == 0)
+ shell_functions = hash_create (0);
- if (!shell_functions)
- shell_functions = make_hash_table (0);
+#if defined (DEBUGGER)
+ if (shell_function_defs == 0)
+ shell_function_defs = hash_create (0);
+#endif
for (string_index = 0; string = env[string_index++]; )
{
char_index = 0;
-
- string_length = strlen (string);
- name = xmalloc (1 + string_length);
-
+ name = string;
while ((c = *string++) && c != '=')
- name[char_index++] = c;
+ ;
+ if (string[-1] == '=')
+ char_index = string - name - 1;
+
+ /* If there are weird things in the environment, like `=xxx' or a
+ string without an `=', just skip them. */
+ if (char_index == 0)
+ continue;
+ /* ASSERT(name[char_index] == '=') */
name[char_index] = '\0';
+ /* Now, name = env variable name, string = env variable value, and
+ char_index == strlen (name) */
- /* If exported function, define it now. */
- if (no_functions == 0 && STREQN ("() {", string, 4))
+ /* If exported function, define it now. Don't import functions from
+ the environment in privileged mode. */
+ if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))
{
- temp_string = xmalloc (3 + string_length + strlen (name));
- sprintf (temp_string, "%s %s", name, string);
+ string_length = strlen (string);
+ temp_string = (char *)xmalloc (3 + string_length + char_index);
- parse_and_execute (temp_string, name, 0);
+ strcpy (temp_string, name);
+ temp_string[char_index] = ' ';
+ strcpy (temp_string + char_index + 1, string);
- if (name[char_index - 1] == ')')
+ parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
+
+ /* Ancient backwards compatibility. Old versions of bash exported
+ functions like name()=() {...} */
+ if (name[char_index - 1] == ')' && name[char_index - 2] == '(')
name[char_index - 2] = '\0';
if (temp_var = find_function (name))
{
- temp_var->attributes |= (att_exported | att_imported);
+ VSETATTR (temp_var, (att_exported|att_imported));
array_needs_making = 1;
}
else
- report_error ("error importing function definition for `%s'", name);
+ report_error (_("error importing function definition for `%s'"), name);
+
+ /* ( */
+ if (name[char_index - 1] == ')' && name[char_index - 2] == '\0')
+ name[char_index - 2] = '('; /* ) */
}
#if defined (ARRAY_VARS)
# if 0
/* Array variables may not yet be exported. */
- else if (*string == '(' && string[1] == '[' && strchr (string, ')'))
+ else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
{
string_length = 1;
temp_string = extract_array_assignment_list (string, &string_length);
temp_var = assign_array_from_string (name, temp_string);
FREE (temp_string);
- temp_var->attributes |= (att_exported | att_imported);
+ VSETATTR (temp_var, (att_exported | att_imported));
array_needs_making = 1;
}
# endif
#endif
else
{
- temp_var = bind_variable (name, string);
- temp_var->attributes |= (att_exported | att_imported);
+ temp_var = bind_variable (name, string, 0);
+ VSETATTR (temp_var, (att_exported | att_imported));
array_needs_making = 1;
}
- free (name);
- }
- /* If we got PWD from the environment, update our idea of the current
- working directory. In any case, make sure that PWD exists before
- checking it. It is possible for getcwd () to fail on shell startup,
- and in that case, PWD would be undefined. */
- temp_var = find_variable ("PWD");
- if (temp_var && imported_p (temp_var) &&
- (temp_string = value_cell (temp_var)) &&
- same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
- set_working_directory (temp_string);
- else
- {
- temp_string = get_working_directory ("shell-init");
- if (temp_string)
+ name[char_index] = '=';
+ /* temp_var can be NULL if it was an exported function with a syntax
+ error (a different bug, but it still shouldn't dump core). */
+ if (temp_var && function_p (temp_var) == 0) /* XXX not yet */
{
- bind_variable ("PWD", temp_string);
- free (temp_string);
+ CACHE_IMPORTSTR (temp_var, name);
}
}
+ set_pwd ();
+
/* Set up initial value of $_ */
- temp_var = bind_variable ("_", dollar_vars[0]);
+#if 0
+ temp_var = bind_variable ("_", dollar_vars[0], 0);
+#else
+ temp_var = set_if_not ("_", dollar_vars[0]);
+#endif
/* Remember this pid. */
- dollar_dollar_pid = (int)getpid ();
+ dollar_dollar_pid = 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);
+#if 0
+ set_auto_export (temp_var); /* XXX */
+#endif
temp_var = set_if_not ("TERM", "dumb");
- set_auto_export (temp_var);
+#if 0
+ set_auto_export (temp_var); /* XXX */
+#endif
+
+#if defined (qnx)
+ /* set node id -- don't import it from the environment */
+ {
+ char node_name[22];
+# if defined (qnx6)
+ netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name));
+# else
+ qnx_nidtostr (getnid (), node_name, sizeof (node_name));
+# endif
+ temp_var = bind_variable ("NODE", node_name, 0);
+ set_auto_export (temp_var);
+ }
+#endif
/* set up the prompts. */
if (interactive_shell)
{
+#if defined (PROMPT_STRING_DECODE)
set_if_not ("PS1", primary_prompt);
+#else
+ if (current_user.uid == -1)
+ get_current_user_info ();
+ set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt);
+#endif
set_if_not ("PS2", secondary_prompt);
}
set_if_not ("PS4", "+ ");
/* Don't allow IFS to be imported from the environment. */
- temp_var = bind_variable ("IFS", " \t\n");
+ temp_var = bind_variable ("IFS", " \t\n", 0);
+ setifs (temp_var);
/* Magic machine types. Pretty convenient. */
- temp_var = bind_variable ("HOSTTYPE", HOSTTYPE);
- set_auto_export (temp_var);
- temp_var = bind_variable ("OSTYPE", OSTYPE);
- set_auto_export (temp_var);
- temp_var = bind_variable ("MACHTYPE", MACHTYPE);
- set_auto_export (temp_var);
- temp_var = bind_variable ("HOSTNAME", current_host_name);
- set_auto_export (temp_var);
+ set_machine_vars ();
/* Default MAILCHECK for interactive shells. Defer the creation of a
default MAILPATH until the startup files are read, because MAIL
- names a mail file if MAILCHECK is not set, and we should provide a
+ names a mail file if MAILPATH is not set, and we should provide a
default only if neither is set. */
if (interactive_shell)
- set_if_not ("MAILCHECK", "60");
+ {
+ temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
+ VSETATTR (temp_var, att_integer);
+ }
/* Do some things with shell level. */
- temp_var = set_if_not ("SHLVL", "0");
- set_auto_export (temp_var);
- adjust_shell_level (1);
+ initialize_shell_level ();
- /* Make a variable $PPID, which holds the pid of the shell's parent. */
- name = itos ((int) getppid ());
- temp_var = find_variable ("PPID");
- if (temp_var)
- temp_var->attributes &= ~(att_readonly | att_exported);
- temp_var = bind_variable ("PPID", name);
- temp_var->attributes |= (att_readonly | att_integer);
- free (name);
+ set_ppid ();
/* Initialize the `getopts' stuff. */
- bind_variable ("OPTIND", "1");
- sv_optind ("OPTIND");
- bind_variable ("OPTERR", "1");
- sv_opterr ("OPTERR");
+ temp_var = bind_variable ("OPTIND", "1", 0);
+ VSETATTR (temp_var, att_integer);
+ getopts_reset (0);
+ bind_variable ("OPTERR", "1", 0);
+ sh_opterr = 1;
+
+ if (login_shell == 1)
+ set_home_var ();
/* Get the full pathname to THIS shell, and set the BASH variable
to it. */
- if ((login_shell == 1) && (*shell_name != '/'))
- {
- /* If HOME doesn't exist, set it. */
- temp_var = set_if_not ("HOME", current_user.home_dir);
- temp_var->attributes |= att_exported;
-
- name = savestring (current_user.shell);
- }
- else if (*shell_name == '/')
- name = savestring (shell_name);
- else
- {
- char *tname;
- int s;
-
- tname = find_user_command (shell_name);
-
- if (tname == 0)
- {
- /* Try the current directory. If there is not an executable
- there, just punt and use the login shell. */
- s = file_status (shell_name);
- if (s & FS_EXECABLE)
- {
- tname = make_absolute (shell_name, get_string_value ("PWD"));
- if (*shell_name == '.')
- {
- name = canonicalize_pathname (tname);
- if (name == 0)
- name = tname;
- else
- free (tname);
- }
- else
- name = tname;
- }
- else
- name = savestring (current_user.shell);
- }
- else
- {
- name = full_pathname (tname);
- free (tname);
- }
- }
- temp_var = bind_variable ("BASH", name);
+ name = get_bash_name ();
+ temp_var = bind_variable ("BASH", name, 0);
free (name);
/* Make the exported environment variable SHELL be the user's login
shell. 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", current_user.shell);
- set_auto_export (temp_var);
+ set_shell_var ();
/* Make a variable called BASH_VERSION which contains the version info. */
- bind_variable ("BASH_VERSION", shell_version_string ());
+ bind_variable ("BASH_VERSION", shell_version_string (), 0);
#if defined (ARRAY_VARS)
make_vers_array ();
#endif
+ if (command_execution_string)
+ bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
+
/* Find out if we're supposed to be in Posix.2 mode via an
environment variable. */
temp_var = find_variable ("POSIXLY_CORRECT");
that we are remembering commands on the history list. */
if (remember_on_history)
{
- name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history");
+ name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);
set_if_not ("HISTFILE", name);
free (name);
#endif /* HISTORY */
/* Seed the random number generator. */
- sbrand (dollar_dollar_pid + (long)shell_start_time);
+ sbrand (dollar_dollar_pid + shell_start_time);
/* Handle some "special" variables that we may have inherited from a
parent shell. */
-
- temp_var = find_variable ("IGNOREEOF");
- if (!temp_var)
- temp_var = find_variable ("ignoreeof");
- if (temp_var && imported_p (temp_var))
- sv_ignoreeof (temp_var->name);
+ if (interactive_shell)
+ {
+ temp_var = find_variable ("IGNOREEOF");
+ if (!temp_var)
+ temp_var = find_variable ("ignoreeof");
+ if (temp_var && imported_p (temp_var))
+ sv_ignoreeof (temp_var->name);
+ }
#if defined (HISTORY)
if (interactive_shell && remember_on_history)
}
#endif /* HISTORY */
+#if defined (READLINE) && defined (STRICT_POSIX)
+ /* POSIXLY_CORRECT will only be 1 here if the shell was compiled
+ -DSTRICT_POSIX */
+ if (interactive_shell && posixly_correct && no_line_editing == 0)
+ rl_prefer_env_winsize = 1;
+#endif /* READLINE && STRICT_POSIX */
+
+ /*
+ * 24 October 2001
+ *
+ * I'm tired of the arguing and bug reports. Bash now leaves SSH_CLIENT
+ * and SSH2_CLIENT alone. I'm going to rely on the shell_level check in
+ * isnetconn() to avoid running the startup files more often than wanted.
+ * That will, of course, only work if the user's login shell is bash, so
+ * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
+ * in config-top.h.
+ */
+#if 0
+ temp_var = find_variable ("SSH_CLIENT");
+ if (temp_var && imported_p (temp_var))
+ {
+ VUNSETATTR (temp_var, att_exported);
+ array_needs_making = 1;
+ }
+ temp_var = find_variable ("SSH2_CLIENT");
+ if (temp_var && imported_p (temp_var))
+ {
+ VUNSETATTR (temp_var, att_exported);
+ array_needs_making = 1;
+ }
+#endif
+
/* Get the user's real and effective user ids. */
uidset ();
initialize_dynamic_variables ();
}
-void
-adjust_shell_level (change)
- int change;
-{
- char *new_level, *old_SHLVL;
- int old_level;
-
- old_SHLVL = get_string_value ("SHLVL");
- if (old_SHLVL)
- old_level = atoi (old_SHLVL);
- else
- old_level = 0;
-
- shell_level = old_level + change;
- if (shell_level < 0)
- shell_level = 0;
- new_level = itos (shell_level);
- bind_variable ("SHLVL", new_level);
- free (new_level);
-}
+/* **************************************************************** */
+/* */
+/* Setting values for special shell variables */
+/* */
+/* **************************************************************** */
static void
-uidset ()
+set_machine_vars ()
{
- char *buff;
- register SHELL_VAR *v;
-
- buff = itos (current_user.uid);
- v = find_variable ("UID");
- if (v)
- v->attributes &= ~att_readonly;
-
- v = bind_variable ("UID", buff);
- v->attributes |= (att_readonly | att_integer);
-
- if (current_user.euid != current_user.uid)
- {
- free (buff);
- buff = itos (current_user.euid);
- }
+ SHELL_VAR *temp_var;
- v = find_variable ("EUID");
- if (v)
- v->attributes &= ~att_readonly;
+ temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
+ temp_var = set_if_not ("OSTYPE", OSTYPE);
+ temp_var = set_if_not ("MACHTYPE", MACHTYPE);
- v = bind_variable ("EUID", buff);
- v->attributes |= (att_readonly | att_integer);
- free (buff);
+ temp_var = set_if_not ("HOSTNAME", current_host_name);
}
-#if defined (ARRAY_VARS)
-static void
-make_vers_array ()
-{
- SHELL_VAR *vv;
- ARRAY *av;
- char *s, d[16];
-
- makunbound ("BASH_VERSINFO", shell_variables);
+/* Set $HOME to the information in the password file if we didn't get
+ it from the environment. */
- vv = make_new_array_variable ("BASH_VERSINFO");
- av = array_cell (vv);
- strcpy (d, dist_version);
- s = strchr (d, '.');
- if (s)
- *s++ = '\0';
- array_add_element (av, 0, d);
- array_add_element (av, 1, s);
- s = itos (patch_level);
- array_add_element (av, 2, s);
- free (s);
- s = itos (build_version);
- array_add_element (av, 3, s);
- free (s);
- array_add_element (av, 4, release_status);
- array_add_element (av, 5, MACHTYPE);
-
- vv->attributes |= att_readonly;
+/* This function is not static so the tilde and readline libraries can
+ use it. */
+char *
+sh_get_home_dir ()
+{
+ if (current_user.home_dir == 0)
+ get_current_user_info ();
+ return current_user.home_dir;
}
-#endif /* ARRAY_VARS */
-/* Set the environment variables $LINES and $COLUMNS in response to
- a window size change. */
-void
-set_lines_and_columns (lines, cols)
- int lines, cols;
+static void
+set_home_var ()
{
- char *val;
-
- val = itos (lines);
- bind_variable ("LINES", val);
- free (val);
+ SHELL_VAR *temp_var;
- val = itos (cols);
- bind_variable ("COLUMNS", val);
- free (val);
+ temp_var = find_variable ("HOME");
+ if (temp_var == 0)
+ temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
+#if 0
+ VSETATTR (temp_var, att_exported);
+#endif
}
-/* Set NAME to VALUE if NAME has no value. */
-SHELL_VAR *
-set_if_not (name, value)
- char *name, *value;
+/* Set $SHELL to the user's login shell if it is not already set. Call
+ get_current_user_info if we haven't already fetched the shell. */
+static void
+set_shell_var ()
{
- SHELL_VAR *v;
+ SHELL_VAR *temp_var;
- v = find_variable (name);
- if (!v)
- v = bind_variable (name, value);
- return (v);
+ temp_var = find_variable ("SHELL");
+ if (temp_var == 0)
+ {
+ if (current_user.shell == 0)
+ get_current_user_info ();
+ temp_var = bind_variable ("SHELL", current_user.shell, 0);
+ }
+#if 0
+ VSETATTR (temp_var, att_exported);
+#endif
}
-/* 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;
+static char *
+get_bash_name ()
{
- register int i;
- register BUCKET_CONTENTS *tlist;
- SHELL_VAR *var, **list = (SHELL_VAR **)NULL;
- int list_index = 0, list_size = 0;
+ char *name;
- for (i = 0; i < var_hash_table->nbuckets; i++)
+ if ((login_shell == 1) && RELPATH(shell_name))
+ {
+ if (current_user.shell == 0)
+ get_current_user_info ();
+ name = savestring (current_user.shell);
+ }
+ else if (ABSPATH(shell_name))
+ name = savestring (shell_name);
+ else if (shell_name[0] == '.' && shell_name[1] == '/')
{
- tlist = get_hash_bucket (i, var_hash_table);
+ /* Fast path for common case. */
+ char *cdir;
+ int len;
- while (tlist)
+ cdir = get_string_value ("PWD");
+ if (cdir)
{
- 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;
+ len = strlen (cdir);
+ name = (char *)xmalloc (len + strlen (shell_name) + 1);
+ strcpy (name, cdir);
+ strcpy (name + len, shell_name + 1);
}
+ else
+ name = savestring (shell_name);
}
- return (list);
-}
+ else
+ {
+ char *tname;
+ int s;
-void
-sort_variables (array)
- SHELL_VAR **array;
-{
- qsort (array, array_len ((char **)array), sizeof (SHELL_VAR *), qsort_var_comp);
-}
+ tname = find_user_command (shell_name);
-static int
-qsort_var_comp (var1, var2)
- SHELL_VAR **var1, **var2;
+ if (tname == 0)
+ {
+ /* Try the current directory. If there is not an executable
+ there, just punt and use the login shell. */
+ s = file_status (shell_name);
+ if (s & FS_EXECABLE)
+ {
+ tname = make_absolute (shell_name, get_string_value ("PWD"));
+ if (*shell_name == '.')
+ {
+ name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
+ if (name == 0)
+ name = tname;
+ else
+ free (tname);
+ }
+ else
+ name = tname;
+ }
+ else
+ {
+ if (current_user.shell == 0)
+ get_current_user_info ();
+ name = savestring (current_user.shell);
+ }
+ }
+ else
+ {
+ name = full_pathname (tname);
+ free (tname);
+ }
+ }
+
+ return (name);
+}
+
+void
+adjust_shell_level (change)
+ int change;
{
- int result;
+ char new_level[5], *old_SHLVL;
+ intmax_t old_level;
+ SHELL_VAR *temp_var;
- if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
- result = strcmp ((*var1)->name, (*var2)->name);
+ old_SHLVL = get_string_value ("SHLVL");
+ if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0)
+ old_level = 0;
- return (result);
+ shell_level = old_level + change;
+ if (shell_level < 0)
+ shell_level = 0;
+ else if (shell_level > 1000)
+ {
+ internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
+ shell_level = 1;
+ }
+
+ /* We don't need the full generality of itos here. */
+ if (shell_level < 10)
+ {
+ new_level[0] = shell_level + '0';
+ new_level[1] = '\0';
+ }
+ else if (shell_level < 100)
+ {
+ new_level[0] = (shell_level / 10) + '0';
+ new_level[1] = (shell_level % 10) + '0';
+ new_level[2] = '\0';
+ }
+ else if (shell_level < 1000)
+ {
+ new_level[0] = (shell_level / 100) + '0';
+ old_level = shell_level % 100;
+ new_level[1] = (old_level / 10) + '0';
+ new_level[2] = (old_level % 10) + '0';
+ new_level[3] = '\0';
+ }
+
+ temp_var = bind_variable ("SHLVL", new_level, 0);
+ set_auto_export (temp_var);
}
-/* Create a NULL terminated array of all the shell variables in TABLE. */
-static SHELL_VAR **
-all_vars (table)
- HASH_TABLE *table;
+static void
+initialize_shell_level ()
{
- SHELL_VAR **list;
+ adjust_shell_level (1);
+}
- list = map_over ((Function *)NULL, table);
- if (list && posixly_correct)
- sort_variables (list);
- return (list);
+/* If we got PWD from the environment, update our idea of the current
+ working directory. In any case, make sure that PWD exists before
+ checking it. It is possible for getcwd () to fail on shell startup,
+ and in that case, PWD would be undefined. If this is an interactive
+ login shell, see if $HOME is the current working directory, and if
+ that's not the same string as $PWD, set PWD=$HOME. */
+
+void
+set_pwd ()
+{
+ SHELL_VAR *temp_var, *home_var;
+ char *temp_string, *home_string;
+
+ home_var = find_variable ("HOME");
+ home_string = home_var ? value_cell (home_var) : (char *)NULL;
+
+ temp_var = find_variable ("PWD");
+ if (temp_var && imported_p (temp_var) &&
+ (temp_string = value_cell (temp_var)) &&
+ same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
+ set_working_directory (temp_string);
+ else if (home_string && interactive_shell && login_shell &&
+ same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
+ {
+ set_working_directory (home_string);
+ temp_var = bind_variable ("PWD", home_string, 0);
+ set_auto_export (temp_var);
+ }
+ else
+ {
+ temp_string = get_working_directory ("shell-init");
+ if (temp_string)
+ {
+ temp_var = bind_variable ("PWD", temp_string, 0);
+ set_auto_export (temp_var);
+ free (temp_string);
+ }
+ }
+
+ /* According to the Single Unix Specification, v2, $OLDPWD is an
+ `environment variable' and therefore should be auto-exported.
+ Make a dummy invisible variable for OLDPWD, and mark it as exported. */
+ temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
+ VSETATTR (temp_var, (att_exported | att_invisible));
}
-/* Create a NULL terminated array of all the shell variables. */
-SHELL_VAR **
-all_shell_variables ()
+/* Make a variable $PPID, which holds the pid of the shell's parent. */
+void
+set_ppid ()
{
- return (all_vars (shell_variables));
+ char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
+ SHELL_VAR *temp_var;
+
+ name = inttostr (getppid (), namebuf, sizeof(namebuf));
+ temp_var = find_variable ("PPID");
+ if (temp_var)
+ VUNSETATTR (temp_var, (att_readonly | att_exported));
+ temp_var = bind_variable ("PPID", name, 0);
+ VSETATTR (temp_var, (att_readonly | att_integer));
}
-/* Create a NULL terminated array of all the shell functions. */
-SHELL_VAR **
-all_shell_functions ()
+static void
+uidset ()
+{
+ char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
+ register SHELL_VAR *v;
+
+ b = inttostr (current_user.uid, buff, sizeof (buff));
+ v = find_variable ("UID");
+ if (v == 0)
+ {
+ v = bind_variable ("UID", b, 0);
+ VSETATTR (v, (att_readonly | att_integer));
+ }
+
+ if (current_user.euid != current_user.uid)
+ b = inttostr (current_user.euid, buff, sizeof (buff));
+
+ v = find_variable ("EUID");
+ if (v == 0)
+ {
+ v = bind_variable ("EUID", b, 0);
+ VSETATTR (v, (att_readonly | att_integer));
+ }
+}
+
+#if defined (ARRAY_VARS)
+static void
+make_vers_array ()
+{
+ SHELL_VAR *vv;
+ ARRAY *av;
+ char *s, d[32], b[INT_STRLEN_BOUND(int) + 1];
+
+ unbind_variable ("BASH_VERSINFO");
+
+ vv = make_new_array_variable ("BASH_VERSINFO");
+ av = array_cell (vv);
+ strcpy (d, dist_version);
+ s = xstrchr (d, '.');
+ if (s)
+ *s++ = '\0';
+ array_insert (av, 0, d);
+ array_insert (av, 1, s);
+ s = inttostr (patch_level, b, sizeof (b));
+ array_insert (av, 2, s);
+ s = inttostr (build_version, b, sizeof (b));
+ array_insert (av, 3, s);
+ array_insert (av, 4, release_status);
+ array_insert (av, 5, MACHTYPE);
+
+ VSETATTR (vv, att_readonly);
+}
+#endif /* ARRAY_VARS */
+
+/* Set the environment variables $LINES and $COLUMNS in response to
+ a window size change. */
+void
+sh_set_lines_and_columns (lines, cols)
+ int lines, cols;
{
- return (all_vars (shell_functions));
+ char val[INT_STRLEN_BOUND(int) + 1], *v;
+
+ /* If we are currently assigning to LINES or COLUMNS, don't do anything. */
+ if (winsize_assignment)
+ return;
+
+ v = inttostr (lines, val, sizeof (val));
+ bind_variable ("LINES", v, 0);
+
+ v = inttostr (cols, val, sizeof (val));
+ bind_variable ("COLUMNS", v, 0);
}
-/* Print VARS to stdout in such a way that they can be read back in. */
+/* **************************************************************** */
+/* */
+/* Printing variables and values */
+/* */
+/* **************************************************************** */
+
+/* Print LIST (a list of shell variables) to stdout in such a way that
+ they can be read back in. */
void
print_var_list (list)
register SHELL_VAR **list;
register SHELL_VAR *var;
for (i = 0; list && (var = list[i]); i++)
- if (!invisible_p (var))
+ if (invisible_p (var) == 0)
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 LIST (a list of shell functions) to stdout in such a way that
+ they can be read back in. */
void
-print_vars_no_values (list)
+print_func_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))
- printf ("%s\n", var->name);
+ {
+ printf ("%s ", var->name);
+ print_var_function (var);
+ printf ("\n");
+ }
}
-#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)
+ if (var_isset (var) == 0)
+ return;
+
+ if (function_p (var))
{
- printf ("%s=", var->name);
+ printf ("%s", var->name);
print_var_function (var);
printf ("\n");
}
#if defined (ARRAY_VARS)
- else if (array_p (var) && var->value)
+ else if (array_p (var))
print_array_assignment (var, 0);
#endif /* ARRAY_VARS */
- else if (var->value)
+ else
{
printf ("%s=", var->name);
print_var_value (var, 1);
{
char *t;
- if (var->value)
+ if (var_isset (var) == 0)
+ return;
+
+ if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var)))
{
- if (quote && contains_shell_metas (var->value))
- {
- t = single_quote (var->value);
- printf ("%s", t);
- free (t);
- }
- else
- printf ("%s", var->value);
+ t = ansic_quote (value_cell (var), 0, (int *)0);
+ printf ("%s", t);
+ free (t);
+ }
+ else if (quote && sh_contains_shell_metas (value_cell (var)))
+ {
+ t = sh_single_quote (value_cell (var));
+ printf ("%s", t);
+ free (t);
}
+ else
+ printf ("%s", value_cell (var));
}
/* Print the function cell of VAR, a shell variable. Do not
print_var_function (var)
SHELL_VAR *var;
{
- if (function_p (var) && var->value)
+ if (function_p (var) && var_isset (var))
printf ("%s", named_function_string ((char *)NULL, function_cell(var), 1));
}
-#if defined (ARRAY_VARS)
-void
-print_array_assignment (var, quoted)
- SHELL_VAR *var;
- int quoted;
-{
- char *vstr;
-
- if (quoted)
- vstr = quoted_array_assignment_string (array_cell (var));
- else
- vstr = array_to_assignment_string (array_cell (var));
-
- if (vstr == 0)
- printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
- else
- {
- printf ("%s=%s\n", var->name, vstr);
- free (vstr);
- }
-}
-#endif /* ARRAY_VARS */
-
/* **************************************************************** */
/* */
-/* 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)
+ in the struct variable: assign_func, which is called from bind_variable
+ and, if arrays are compiled into the shell, some of the functions in
+ arrayfunc.c, and dynamic_value, which is called from find_variable.
+
+ assign_func is called from bind_variable_internal, if
+ bind_variable_internal discovers that the variable being assigned to
+ has such a function. The function is called as
+ SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind)
and the (SHELL_VAR *)temp is returned as the value of bind_variable. It
- is usually ENTRY (self).
+ is usually ENTRY (self). IND is an index for an array variable, and
+ unused otherwise.
- 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);
+ dynamic_value is called from find_variable_internal 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?
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
+ subst.c: do_assignment_internal, in the same style as
stupidly_hack_special_variables, but I wanted the changes as
localized as possible. */
+#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
+ do \
+ { \
+ v = bind_variable (var, (val), 0); \
+ v->dynamic_value = gfunc; \
+ v->assign_func = afunc; \
+ } \
+ while (0)
+
+#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \
+ do \
+ { \
+ v = make_new_array_variable (var); \
+ v->dynamic_value = gfunc; \
+ v->assign_func = afunc; \
+ } \
+ while (0)
+
+static SHELL_VAR *
+null_assign (self, value, unused)
+ SHELL_VAR *self;
+ char *value;
+ arrayind_t unused;
+{
+ return (self);
+}
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *
+null_array_assign (self, value, ind)
+ SHELL_VAR *self;
+ char *value;
+ arrayind_t ind;
+{
+ return (self);
+}
+#endif
+
+/* Degenerate `dynamic_value' function; just returns what's passed without
+ manipulation. */
+static SHELL_VAR *
+get_self (self)
+ SHELL_VAR *self;
+{
+ return (self);
+}
+
+#if defined (ARRAY_VARS)
+/* A generic dynamic array variable initializer. Intialize array variable
+ NAME with dynamic value function GETFUNC and assignment function SETFUNC. */
+static SHELL_VAR *
+init_dynamic_array_var (name, getfunc, setfunc, attrs)
+ char *name;
+ sh_var_value_func_t *getfunc;
+ sh_var_assign_func_t *setfunc;
+ int attrs;
+{
+ SHELL_VAR *v;
+
+ v = find_variable (name);
+ if (v)
+ return (v);
+ INIT_DYNAMIC_ARRAY_VAR (name, getfunc, setfunc);
+ if (attrs)
+ VSETATTR (v, attrs);
+ return v;
+}
+#endif
+
+
/* 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;
+static intmax_t seconds_value_assigned;
static SHELL_VAR *
-assign_seconds (self, value)
+assign_seconds (self, value, unused)
SHELL_VAR *self;
char *value;
+ arrayind_t unused;
{
- seconds_value_assigned = string_to_long (value);
+ if (legal_number (value, &seconds_value_assigned) == 0)
+ seconds_value_assigned = 0;
shell_start_time = NOW;
return (self);
}
char *p;
time_since_start = NOW - shell_start_time;
- p = itos((int) seconds_value_assigned + time_since_start);
+ p = itos(seconds_value_assigned + time_since_start);
- FREE (var->value);
+ FREE (value_cell (var));
- var->attributes |= att_integer;
- var->value = p;
+ VSETATTR (var, att_integer);
+ var_setvalue (var, p);
return (var);
}
+static SHELL_VAR *
+init_seconds_var ()
+{
+ SHELL_VAR *v;
+
+ v = find_variable ("SECONDS");
+ if (v)
+ {
+ if (legal_number (value_cell(v), &seconds_value_assigned) == 0)
+ seconds_value_assigned = 0;
+ }
+ INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
+ return v;
+}
+
/* The random number seed. You can change this by setting RANDOM. */
static unsigned long rseed = 1;
-static unsigned long last_random_value;
+static int last_random_value;
+static int seeded_subshell = 0;
-/* A linear congruential random number generator based on the ANSI
- C standard. A more complicated one is overkill. */
+/* A linear congruential random number generator based on the example
+ one in the ANSI C standard. This one isn't very good, but 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);
+ return ((unsigned int)((rseed >> 16) & 32767)); /* was % 32768 */
}
/* Set the random number generator seed to SEED. */
static void
sbrand (seed)
- int seed;
+ unsigned long seed;
{
rseed = seed;
+ last_random_value = 0;
}
static SHELL_VAR *
-assign_random (self, value)
+assign_random (self, value, unused)
SHELL_VAR *self;
char *value;
+ arrayind_t unused;
{
- sbrand (atoi (value));
+ sbrand (strtoul (value, (char **)NULL, 10));
+ if (subshell_environment)
+ seeded_subshell = 1;
return (self);
}
-static SHELL_VAR *
-get_random (var)
- SHELL_VAR *var;
+int
+get_random_number ()
{
int rv;
- char *p;
/* Reset for command and process substitution. */
- if (subshell_environment)
- sbrand ((int)(getpid() + NOW));
+ if (subshell_environment && seeded_subshell == 0)
+ {
+ sbrand (rseed + getpid() + NOW);
+ seeded_subshell = 1;
+ }
do
rv = brand ();
- while (rv == (int)last_random_value);
+ while (rv == last_random_value);
+ return rv;
+}
+
+static SHELL_VAR *
+get_random (var)
+ SHELL_VAR *var;
+{
+ int rv;
+ char *p;
+ rv = get_random_number ();
last_random_value = rv;
- p = itos ((int)rv);
+ p = itos (rv);
- FREE (var->value);
+ FREE (value_cell (var));
- var->attributes |= att_integer;
- var->value = p;
+ VSETATTR (var, att_integer);
+ var_setvalue (var, p);
return (var);
}
+static SHELL_VAR *
+assign_lineno (var, value, unused)
+ SHELL_VAR *var;
+ char *value;
+ arrayind_t unused;
+{
+ intmax_t new_value;
+
+ if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
+ new_value = 0;
+ line_number = new_value;
+ return var;
+}
+
/* Function which returns the current line number. */
static SHELL_VAR *
get_lineno (var)
ln = executing_line_number ();
p = itos (ln);
- FREE (var->value);
- var->value = p;
+ FREE (value_cell (var));
+ var_setvalue (var, p);
return (var);
}
static SHELL_VAR *
-assign_lineno (var, value)
+assign_subshell (var, value, unused)
SHELL_VAR *var;
char *value;
+ arrayind_t unused;
{
- line_number = atoi (value);
+ intmax_t new_value;
+
+ if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
+ new_value = 0;
+ subshell_level = new_value;
return var;
}
-#if defined (HISTORY)
+static SHELL_VAR *
+get_subshell (var)
+ SHELL_VAR *var;
+{
+ char *p;
+
+ p = itos (subshell_level);
+ FREE (value_cell (var));
+ var_setvalue (var, p);
+ return (var);
+}
+
+static SHELL_VAR *
+get_bash_command (var)
+ SHELL_VAR *var;
+{
+ char *p;
+
+
+ if (the_printed_command_except_trap)
+ p = savestring (the_printed_command_except_trap);
+ else
+ {
+ p = (char *)xmalloc (1);
+ p[0] = '\0';
+ }
+ FREE (value_cell (var));
+ var_setvalue (var, p);
+ return (var);
+}
+
+#if defined (HISTORY)
static SHELL_VAR *
get_histcmd (var)
SHELL_VAR *var;
char *p;
p = itos (history_number ());
- FREE (var->value);
- var->value = p;
+ FREE (value_cell (var));
+ var_setvalue (var, p);
return (var);
}
#endif
+#if defined (READLINE)
+/* When this function returns, VAR->value points to malloced memory. */
+static SHELL_VAR *
+get_comp_wordbreaks (var)
+ SHELL_VAR *var;
+{
+ char *p;
+
+ /* If we don't have anything yet, assign a default value. */
+ if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0)
+ enable_hostname_completion (perform_hostname_completion);
+
+#if 0
+ FREE (value_cell (var));
+ p = savestring (rl_completer_word_break_characters);
+
+ var_setvalue (var, p);
+#else
+ var_setvalue (var, rl_completer_word_break_characters);
+#endif
+
+ return (var);
+}
+
+/* When this function returns, rl_completer_word_break_characters points to
+ malloced memory. */
+static SHELL_VAR *
+assign_comp_wordbreaks (self, value, unused)
+ SHELL_VAR *self;
+ char *value;
+ arrayind_t unused;
+{
+ if (rl_completer_word_break_characters &&
+ rl_completer_word_break_characters != rl_basic_word_break_characters)
+ free (rl_completer_word_break_characters);
+
+ rl_completer_word_break_characters = savestring (value);
+ return self;
+}
+#endif /* READLINE */
+
#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
static SHELL_VAR *
+assign_dirstack (self, value, ind)
+ SHELL_VAR *self;
+ char *value;
+ arrayind_t ind;
+{
+ set_dirstack_element (ind, 1, value);
+ return self;
+}
+
+static SHELL_VAR *
get_dirstack (self)
SHELL_VAR *self;
{
WORD_LIST *l;
l = get_directory_stack ();
- a = word_list_to_array (l);
- dispose_array (array_cell (self));
- self->value = (char *)a;
+ a = array_from_word_list (l);
+ array_dispose (array_cell (self));
+ dispose_words (l);
+ var_setarray (self, a);
return self;
}
+#endif /* PUSHD AND POPD && ARRAY_VARS */
-static SHELL_VAR *
-assign_dirstack (self, ind, value)
+#if defined (ARRAY_VARS)
+/* We don't want to initialize the group set with a call to getgroups()
+ unless we're asked to, but we only want to do it once. */
+static SHELL_VAR *
+get_groupset (self)
SHELL_VAR *self;
- int ind;
- char *value;
{
- set_dirstack_element (ind, 1, value);
- return self;
+ register int i;
+ int ng;
+ ARRAY *a;
+ static char **group_set = (char **)NULL;
+
+ if (group_set == 0)
+ {
+ group_set = get_group_list (&ng);
+ a = array_cell (self);
+ for (i = 0; i < ng; i++)
+ array_insert (a, i, group_set[i]);
+ }
+ return (self);
+}
+#endif /* ARRAY_VARS */
+
+/* If ARRAY_VARS is not defined, this just returns the name of any
+ currently-executing function. If we have arrays, it's a call stack. */
+static SHELL_VAR *
+get_funcname (self)
+ SHELL_VAR *self;
+{
+#if ! defined (ARRAY_VARS)
+ char *t;
+ if (variable_context && this_shell_function)
+ {
+ FREE (value_cell (self));
+ t = savestring (this_shell_function->name);
+ var_setvalue (self, t);
+ }
+#endif
+ return (self);
+}
+
+void
+make_funcname_visible (on_or_off)
+ int on_or_off;
+{
+ SHELL_VAR *v;
+
+ v = find_variable ("FUNCNAME");
+ if (v == 0 || v->dynamic_value == 0)
+ return;
+
+ if (on_or_off)
+ VUNSETATTR (v, att_invisible);
+ else
+ VSETATTR (v, att_invisible);
+}
+
+static SHELL_VAR *
+init_funcname_var ()
+{
+ SHELL_VAR *v;
+
+ v = find_variable ("FUNCNAME");
+ if (v)
+ return v;
+#if defined (ARRAY_VARS)
+ INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign);
+#else
+ INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
+#endif
+ VSETATTR (v, att_invisible|att_noassign);
+ return v;
}
-#endif /* PUSHD AND POPD && ARRAY_VARS */
static void
initialize_dynamic_variables ()
{
SHELL_VAR *v;
- v = bind_variable ("SECONDS", (char *)NULL);
- v->dynamic_value = get_seconds;
- v->assign_func = assign_seconds;
+ v = init_seconds_var ();
- v = bind_variable ("RANDOM", (char *)NULL);
- v->dynamic_value = get_random;
- v->assign_func = assign_random;
+ INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL);
+ INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell);
- v = bind_variable ("LINENO", (char *)NULL);
- v->dynamic_value = get_lineno;
- v->assign_func = assign_lineno;
+ INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
+ VSETATTR (v, att_integer);
+ INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
+ VSETATTR (v, att_integer);
#if defined (HISTORY)
- v = bind_variable ("HISTCMD", (char *)NULL);
- v->dynamic_value = get_histcmd;
- v->assign_func = (DYNAMIC_FUNC *)NULL;
+ INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
+ VSETATTR (v, att_integer);
+#endif
+
+#if defined (READLINE)
+ INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks);
#endif
#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
- v = make_new_array_variable ("DIRSTACK");
- v->dynamic_value = get_dirstack;
- v->assign_func = assign_dirstack;
+ v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0);
#endif /* PUSHD_AND_POPD && ARRAY_VARS */
+
+#if defined (ARRAY_VARS)
+ v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign);
+
+# if defined (DEBUGGER)
+ v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset);
+ v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset);
+# endif /* DEBUGGER */
+ v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset);
+ v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset);
+#endif
+
+ v = init_funcname_var ();
}
+/* **************************************************************** */
+/* */
+/* Retrieving variables and values */
+/* */
+/* **************************************************************** */
+
/* 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;
+
+static SHELL_VAR *
+hash_lookup (name, hashed_vars)
+ const char *name;
HASH_TABLE *hashed_vars;
{
BUCKET_CONTENTS *bucket;
- bucket = find_hash_item (name, hashed_vars);
+ bucket = hash_search (name, hashed_vars, 0);
return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
}
+SHELL_VAR *
+var_lookup (name, vcontext)
+ const char *name;
+ VAR_CONTEXT *vcontext;
+{
+ VAR_CONTEXT *vc;
+ SHELL_VAR *v;
+
+ v = (SHELL_VAR *)NULL;
+ for (vc = vcontext; vc; vc = vc->down)
+ if (v = hash_lookup (name, vc->table))
+ break;
+
+ return v;
+}
+
/* Look up the variable entry named NAME. If SEARCH_TEMPENV is non-zero,
- then also search the temporarily built list of exported variables. */
+ then also search the temporarily built list of exported variables.
+ The lookup order is:
+ temporary_env
+ shell_variables list
+*/
+
SHELL_VAR *
-find_variable_internal (name, search_tempenv)
- char *name;
- int search_tempenv;
+find_variable_internal (name, force_tempenv)
+ const char *name;
+ int force_tempenv;
{
- SHELL_VAR *var = (SHELL_VAR *)NULL;
+ SHELL_VAR *var;
+ int search_tempenv;
+
+ var = (SHELL_VAR *)NULL;
/* If explicitly requested, 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. This happens if we are executing
a function or builtin, or if we are looking up a variable in a
"subshell environment". */
- if ((search_tempenv || subshell_environment) &&
- (temporary_env || builtin_env || function_env))
- var = find_tempenv_variable (name);
+ search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment);
- if (!var)
+ if (search_tempenv && temporary_env)
+ var = hash_lookup (name, temporary_env);
+
+ if (var == 0)
var = var_lookup (name, shell_variables);
- if (!var)
+ if (var == 0)
return ((SHELL_VAR *)NULL);
return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
/* Look up the variable entry named NAME. Returns the entry or NULL. */
SHELL_VAR *
find_variable (name)
- char *name;
+ const char *name;
{
- return (find_variable_internal
- (name, (variable_context || this_shell_builtin || builtin_env)));
+ return (find_variable_internal (name, (expanding_redir == 0 && this_shell_builtin != 0)));
}
/* Look up the function entry whose name matches STRING.
Returns the entry or NULL. */
SHELL_VAR *
find_function (name)
- char *name;
+ const char *name;
{
- return (var_lookup (name, shell_functions));
+ return (hash_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;
+/* Find the function definition for the shell function named NAME. Returns
+ the entry or NULL. */
+FUNCTION_DEF *
+find_function_def (name)
+ const char *name;
{
- SHELL_VAR *var = find_variable (var_name);
+ return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
+}
- if (!var)
- return (char *)NULL;
+/* Return the value of VAR. VAR is assumed to have been the result of a
+ lookup without any subscript, if arrays are compiled into the shell. */
+char *
+get_variable_value (var)
+ SHELL_VAR *var;
+{
+ if (var == 0)
+ return ((char *)NULL);
#if defined (ARRAY_VARS)
else if (array_p (var))
return (array_reference (array_cell (var), 0));
#endif
else
- return (var->value);
+ return (value_cell (var));
+}
+
+/* Return the string value of a variable. Return NULL if the variable
+ doesn't exist. Don't cons a new string. This is a potential memory
+ leak if the variable is found in the temporary environment. Since
+ functions and variables have separate name spaces, returns NULL if
+ var_name is a shell function only. */
+char *
+get_string_value (var_name)
+ const char *var_name;
+{
+ SHELL_VAR *var;
+
+ var = find_variable (var_name);
+ return ((var) ? get_variable_value (var) : (char *)NULL);
+}
+
+/* This is present for use by the tilde and readline libraries. */
+char *
+sh_get_env_value (v)
+ const char *v;
+{
+ return get_string_value (v);
+}
+
+/* **************************************************************** */
+/* */
+/* Creating and setting variables */
+/* */
+/* **************************************************************** */
+
+/* Set NAME to VALUE if NAME has no value. */
+SHELL_VAR *
+set_if_not (name, value)
+ char *name, *value;
+{
+ SHELL_VAR *v;
+
+ v = find_variable (name);
+ if (v == 0)
+ v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
+ return (v);
}
/* Create a local variable referenced by NAME. */
SHELL_VAR *
make_local_variable (name)
- char *name;
+ const char *name;
{
SHELL_VAR *new_var, *old_var;
- BUCKET_CONTENTS *elt;
+ VAR_CONTEXT *vc;
+ int was_tmpvar;
+ char *tmp_value;
/* 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)
+ if (old_var && local_p (old_var) && old_var->context == variable_context)
{
- old_var = (SHELL_VAR *)elt->data;
- free (elt->key);
- free (elt);
+ VUNSETATTR (old_var, att_invisible);
+ return (old_var);
}
- 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)
+ was_tmpvar = old_var && tempvar_p (old_var);
+ if (was_tmpvar)
+ tmp_value = value_cell (old_var);
+
+ for (vc = shell_variables; vc; vc = vc->down)
+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
+ break;
+
+ if (vc == 0)
{
- new_var = bind_variable (name, "");
+ internal_error (_("make_local_variable: no function context at current scope"));
+ return ((SHELL_VAR *)NULL);
}
- else
+ else if (vc->table == 0)
+ vc->table = hash_create (TEMPENV_HASH_BUCKETS);
+
+ /* Since this is called only from the local/declare/typeset code, we can
+ call builtin_error here without worry (of course, it will also work
+ for anything that sets this_command_name). Variables with the `noassign'
+ attribute may not be made local. The test against old_var's context
+ level is to disallow local copies of readonly global variables (since I
+ believe that this could be a security hole). Readonly copies of calling
+ function local variables are OK. */
+ if (old_var && (noassign_p (old_var) ||
+ (readonly_p (old_var) && old_var->context == 0)))
{
- new_var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+ if (readonly_p (old_var))
+ sh_readonly (name);
+ return ((SHELL_VAR *)NULL);
+ }
- new_var->name = savestring (name);
- new_var->value = xmalloc (1);
- new_var->value[0] = '\0';
+ if (old_var == 0)
+ new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH, 0);
+ else
+ {
+ new_var = make_new_variable (name, vc->table);
- new_var->dynamic_value = (DYNAMIC_FUNC *)NULL;
- new_var->assign_func = (DYNAMIC_FUNC *)NULL;
+ /* If we found this variable in one of the temporary environments,
+ inherit its value. Watch to see if this causes problems with
+ things like `x=4 local x'. */
+ if (was_tmpvar)
+ var_setvalue (new_var, savestring (tmp_value));
new_var->attributes = exported_p (old_var) ? att_exported : 0;
-
- new_var->prev_context = old_var;
- elt = add_hash_item (savestring (name), shell_variables);
- elt->data = (char *)new_var;
}
+ vc->flags |= VC_HASLOCAL;
+
new_var->context = variable_context;
- new_var->attributes |= att_local;
+ VSETATTR (new_var, att_local);
- /* XXX */
- if (variable_context >= local_variable_stack_size)
- {
- int old_size = local_variable_stack_size;
- RESIZE_MALLOCED_BUFFER (have_local_variables, variable_context, 1,
- local_variable_stack_size, 8);
- bzero ((char *)have_local_variables + old_size,
- local_variable_stack_size - old_size);
- }
- have_local_variables[variable_context] = 1; /* XXX */
+ if (ifsname (name))
+ setifs (new_var);
return (new_var);
}
ARRAY *array;
var = make_local_variable (name);
- array = new_array ();
+ if (var == 0 || array_p (var))
+ return var;
+
+ array = array_create ();
FREE (value_cell(var));
- var->value = (char *)array;
- var->attributes |= att_array;
+ var_setarray (var, array);
+ VSETATTR (var, att_array);
return var;
}
#endif /* ARRAY_VARS */
-/* Create a new shell variable with name NAME and add it to the hash table
- of shell variables. */
-static
-SHELL_VAR *
-make_new_variable (name)
- char *name;
+/* Create a new shell variable with name NAME. */
+static SHELL_VAR *
+new_shell_variable (name)
+ const char *name;
{
SHELL_VAR *entry;
- BUCKET_CONTENTS *elt;
entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
- entry->attributes = 0;
entry->name = savestring (name);
- entry->value = (char *)NULL;
+ var_setvalue (entry, (char *)NULL);
+ CLEAR_EXPORTSTR (entry);
+
+ entry->dynamic_value = (sh_var_value_func_t *)NULL;
+ entry->assign_func = (sh_var_assign_func_t *)NULL;
- entry->dynamic_value = (DYNAMIC_FUNC *)NULL;
- entry->assign_func = (DYNAMIC_FUNC *)NULL;
+ entry->attributes = 0;
/* 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;
- elt = add_hash_item (savestring (name), shell_variables);
- elt->data = (char *)entry;
+ return (entry);
+}
+
+/* Create a new shell variable with name NAME and add it to the hash table
+ TABLE. */
+static SHELL_VAR *
+make_new_variable (name, table)
+ const char *name;
+ HASH_TABLE *table;
+{
+ SHELL_VAR *entry;
+ BUCKET_CONTENTS *elt;
+
+ entry = new_shell_variable (name);
+
+ /* Make sure we have a shell_variables hash table to add to. */
+ if (shell_variables == 0)
+ {
+ shell_variables = global_variables = new_var_context ((char *)NULL, 0);
+ shell_variables->scope = 0;
+ shell_variables->table = hash_create (0);
+ }
+
+ elt = hash_insert (savestring (name), table, HASH_NOSRCH);
+ elt->data = (PTR_T)entry;
return entry;
}
SHELL_VAR *entry;
ARRAY *array;
- entry = make_new_variable (name);
- array = new_array ();
- entry->value = (char *)array;
- entry->attributes |= att_array;
+ entry = make_new_variable (name, global_variables->table);
+ array = array_create ();
+ var_setarray (entry, array);
+ VSETATTR (entry, att_array);
return entry;
}
#endif
char *
-make_variable_value (var, value)
+make_variable_value (var, value, flags)
SHELL_VAR *var;
char *value;
+ int flags;
{
- char *retval;
- long lval;
+ char *retval, *oval;
+ intmax_t lval, rval;
+ int expok, olen;
/* 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
+ 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 (var))
{
- lval = evalexp (value);
- retval = itos (lval);
+ if (flags & ASS_APPEND)
+ {
+ oval = value_cell (var);
+ lval = evalexp (oval, &expok); /* ksh93 seems to do this */
+ if (expok == 0)
+ jump_to_top_level (DISCARD);
+ }
+ rval = evalexp (value, &expok);
+ if (expok == 0)
+ jump_to_top_level (DISCARD);
+ if (flags & ASS_APPEND)
+ rval += lval;
+ retval = itos (rval);
}
else if (value)
{
- if (*value)
+ if (flags & ASS_APPEND)
+ {
+ oval = get_variable_value (var);
+ if (oval == 0) /* paranoia */
+ oval = "";
+ olen = STRLEN (oval);
+ retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
+ strcpy (retval, oval);
+ if (value)
+ strcpy (retval+olen, value);
+ }
+ else if (*value)
retval = savestring (value);
else
{
- retval = xmalloc (1);
+ retval = (char *)xmalloc (1);
retval[0] = '\0';
}
}
return retval;
}
-/* Bind a variable NAME to VALUE. This conses up the name
- and value strings. */
-SHELL_VAR *
-bind_variable (name, value)
- char *name, *value;
+/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
+ temporary environment (but usually is not). */
+static SHELL_VAR *
+bind_variable_internal (name, value, table, hflags, aflags)
+ const char *name;
+ char *value;
+ HASH_TABLE *table;
+ int hflags, aflags;
{
char *newval;
SHELL_VAR *entry;
- entry = var_lookup (name, shell_variables);
+ entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
if (entry == 0)
{
- entry = make_new_variable (name);
- entry->value = make_variable_value (entry, value);
+ entry = make_new_variable (name, table);
+ var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
+ }
+ else if (entry->assign_func) /* array vars have assign functions now */
+ {
+ INVALIDATE_EXPORTSTR (entry);
+ newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value;
+ entry = (*(entry->assign_func)) (entry, newval, -1);
+ if (newval != value)
+ free (newval);
+ return (entry);
}
-#if defined (ARRAY_VARS)
- else if (entry->assign_func && array_p (entry) == 0)
-#else
- else if (entry->assign_func)
-#endif
- return ((*(entry->assign_func)) (entry, value));
else
{
- if (readonly_p (entry))
+ if (readonly_p (entry) || noassign_p (entry))
{
- report_error ("%s: readonly variable", name);
+ if (readonly_p (entry))
+ err_readonly (name);
return (entry);
}
/* Variables which are bound are visible. */
- entry->attributes &= ~att_invisible;
+ VUNSETATTR (entry, att_invisible);
+
+ newval = make_variable_value (entry, value, aflags); /* XXX */
- newval = make_variable_value (entry, value);
+ /* Invalidate any cached export string */
+ INVALIDATE_EXPORTSTR (entry);
#if defined (ARRAY_VARS)
/* XXX -- this bears looking at again -- XXX */
`read x' or something of that nature, silently convert it to
x[0]=b or `read x[0]'. */
if (array_p (entry))
- array_add_element (array_cell (entry), 0, newval);
- else
{
- FREE (entry->value);
- entry->value = newval;
+ array_insert (array_cell (entry), 0, newval);
+ free (newval);
}
-#else
- FREE (entry->value);
- entry->value = newval;
+ else
#endif
+ {
+ FREE (value_cell (entry));
+ var_setvalue (entry, newval);
+ }
}
if (mark_modified_vars)
- entry->attributes |= att_exported;
+ VSETATTR (entry, att_exported);
if (exported_p (entry))
array_needs_making = 1;
return (entry);
}
+
+/* Bind a variable NAME to VALUE. This conses up the name
+ and value strings. If we have a temporary environment, we bind there
+ first, then we bind into shell_variables. */
-#if defined (ARRAY_VARS)
-/* Convert a shell variable to an array variable. The original value is
- saved as array[0]. */
SHELL_VAR *
-convert_var_to_array (var)
- SHELL_VAR *var;
+bind_variable (name, value, flags)
+ const char *name;
+ char *value;
+ int flags;
{
- char *oldval;
- ARRAY *array;
+ SHELL_VAR *v;
+ VAR_CONTEXT *vc;
- oldval = value_cell (var);
- array = new_array ();
- array_add_element (array, 0, oldval);
- FREE (value_cell (var));
- var->value = (char *)array;
- var->attributes |= att_array;
- var->attributes &= ~att_invisible;
+ if (shell_variables == 0)
+ {
+ shell_variables = global_variables = new_var_context ((char *)NULL, 0);
+ shell_variables->scope = 0;
+ shell_variables->table = hash_create (0);
+ }
- return var;
-}
+ /* If we have a temporary environment, look there first for the variable,
+ and, if found, modify the value there before modifying it in the
+ shell_variables table. This allows sourced scripts to modify values
+ given to them in a temporary environment while modifying the variable
+ value that the caller sees. */
+ if (temporary_env)
+ bind_tempenv_variable (name, value);
-/* Perform an array assignment name[ind]=value. If NAME already exists and
- is not an array, and IND is 0, perform name=value instead. If NAME exists
- and is not an array, and IND is not 0, convert it into an array with the
- existing value as name[0].
+ /* XXX -- handle local variables here. */
+ for (vc = shell_variables; vc; vc = vc->down)
+ {
+ if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
+ {
+ v = hash_lookup (name, vc->table);
+ if (v)
+ return (bind_variable_internal (name, value, vc->table, 0, flags));
+ }
+ }
+ return (bind_variable_internal (name, value, global_variables->table, 0, flags));
+}
- If NAME does not exist, just create an array variable, no matter what
- IND's value may be. */
+/* Make VAR, a simple shell variable, have value VALUE. Once assigned a
+ value, variables are no longer invisible. This is a duplicate of part
+ of the internals of bind_variable. If the variable is exported, or
+ all modified variables should be exported, mark the variable for export
+ and note that the export environment needs to be recreated. */
SHELL_VAR *
-bind_array_variable (name, ind, value)
- char *name;
- int ind;
+bind_variable_value (var, value, aflags)
+ SHELL_VAR *var;
char *value;
+ int aflags;
{
- SHELL_VAR *entry;
- char *newval;
+ char *t;
- entry = var_lookup (name, shell_variables);
+ VUNSETATTR (var, att_invisible);
- if (entry == (SHELL_VAR *) 0)
- entry = make_new_array_variable (name);
- else if (readonly_p (entry))
+ if (var->assign_func)
{
- report_error ("%s: readonly variable", name);
- return (entry);
+ /* If we're appending, we need the old value, so use
+ make_variable_value */
+ t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value;
+ (*(var->assign_func)) (var, t, -1);
+ if (t != value && t)
+ free (t);
}
- else if (array_p (entry) == 0)
- entry = convert_var_to_array (entry);
-
- /* ENTRY is an array variable, and ARRAY points to the value. */
- newval = make_variable_value (entry, value);
- if (entry->assign_func)
- (*entry->assign_func) (entry, ind, newval);
else
- array_add_element (array_cell (entry), ind, newval);
- FREE (newval);
+ {
+ t = make_variable_value (var, value, aflags);
+ FREE (value_cell (var));
+ var_setvalue (var, t);
+ }
- return (entry);
-}
+ INVALIDATE_EXPORTSTR (var);
-SHELL_VAR *
-assign_array_from_string (name, value)
- char *name, *value;
-{
- SHELL_VAR *var;
+ if (mark_modified_vars)
+ VSETATTR (var, att_exported);
- var = find_variable (name);
- if (var == 0)
- var = make_new_array_variable (name);
- else if (array_p (var) == 0)
- var = convert_var_to_array (var);
+ if (exported_p (var))
+ array_needs_making = 1;
- return (assign_array_var_from_string (var, value));
+ return (var);
}
+/* Bind/create a shell variable with the name LHS to the RHS.
+ This creates or modifies a variable such that it is an integer.
+
+ This used to be in expr.c, but it is here so that all of the
+ variable binding stuff is localized. Since we don't want any
+ recursive evaluation from bind_variable() (possible without this code,
+ since bind_variable() calls the evaluator for variables with the integer
+ attribute set), we temporarily turn off the integer attribute for each
+ variable we set here, then turn it back on after binding as necessary. */
+
SHELL_VAR *
-assign_array_var_from_word_list (var, list)
- SHELL_VAR *var;
- WORD_LIST *list;
+bind_int_variable (lhs, rhs)
+ char *lhs, *rhs;
{
- register int i;
- register WORD_LIST *l;
- ARRAY *a;
+ register SHELL_VAR *v;
+ char *t;
+ int isint, isarr;
- for (a = array_cell (var), l = list, i = 0; l; l = l->next, i++)
- if (var->assign_func)
- (*var->assign_func) (var, i, l->word->word);
- else
- array_add_element (a, i, l->word->word);
- return var;
+ isint = isarr = 0;
+#if defined (ARRAY_VARS)
+# if 0
+ if (t = xstrchr (lhs, '[')) /*]*/
+# else
+ if (valid_array_reference (lhs))
+# endif
+ {
+ isarr = 1;
+ v = array_variable_part (lhs, (char **)0, (int *)0);
+ }
+ else
+#endif
+ v = find_variable (lhs);
+
+ if (v)
+ {
+ isint = integer_p (v);
+ VUNSETATTR (v, att_integer);
+ }
+
+#if defined (ARRAY_VARS)
+ if (isarr)
+ v = assign_array_element (lhs, rhs, 0);
+ else
+#endif
+ v = bind_variable (lhs, rhs, 0);
+
+ if (isint)
+ VSETATTR (v, att_integer);
+
+ return (v);
}
SHELL_VAR *
-assign_array_var_from_string (var, value)
- SHELL_VAR *var;
- char *value;
+bind_var_to_int (var, val)
+ char *var;
+ intmax_t val;
{
- ARRAY *a;
- WORD_LIST *list, *nlist;
- char *w, *val, *nval;
- int ni, len, ind, last_ind;
+ char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
+
+ p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
+ return (bind_int_variable (var, p));
+}
- a = array_cell (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)
+ const char *name;
+ COMMAND *value;
+{
+ SHELL_VAR *entry;
- /* Expand the value string into a list of words, performing all the
- shell expansions including word splitting. */
- if (*value == '(')
+ entry = find_function (name);
+ if (entry == 0)
{
- ni = 1;
- val = extract_array_assignment_list (value, &ni);
- if (val == 0)
- return var;
- nlist = expand_string (val, 0);
- free (val);
+ BUCKET_CONTENTS *elt;
+
+ elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
+ entry = new_shell_variable (name);
+ elt->data = (PTR_T)entry;
}
else
- nlist = expand_string (value, 0);
+ INVALIDATE_EXPORTSTR (entry);
+
+ if (var_isset (entry))
+ dispose_command (function_cell (entry));
+
+ if (value)
+ var_setfunc (entry, copy_command (value));
+ else
+ var_setfunc (entry, 0);
+
+ VSETATTR (entry, att_function);
+
+ if (mark_modified_vars)
+ VSETATTR (entry, att_exported);
+
+ VUNSETATTR (entry, att_invisible); /* Just to be sure */
+
+ if (exported_p (entry))
+ array_needs_making = 1;
+
+#if defined (PROGRAMMABLE_COMPLETION)
+ set_itemlist_dirty (&it_functions);
+#endif
+
+ return (entry);
+}
+
+/* Bind a function definition, which includes source file and line number
+ information in addition to the command, into the FUNCTION_DEF hash table.*/
+void
+bind_function_def (name, value)
+ const char *name;
+ FUNCTION_DEF *value;
+{
+ FUNCTION_DEF *entry;
+ BUCKET_CONTENTS *elt;
+ COMMAND *cmd;
- for (last_ind = 0, list = nlist; list; list = list->next)
+ entry = find_function_def (name);
+ if (entry)
+ {
+ dispose_function_def_contents (entry);
+ entry = copy_function_def_contents (value, entry);
+ }
+ else
{
- w = list->word->word;
+ cmd = value->command;
+ value->command = 0;
+ entry = copy_function_def (value);
+ value->command = cmd;
- /* We have a word of the form [ind]=value */
- if (w[0] == '[')
- {
- len = skipsubscript (w, 0);
+ elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH);
+ elt->data = (PTR_T *)entry;
+ }
+}
- if (w[len] != ']' || w[len+1] != '=')
- {
- nval = make_variable_value (var, w);
- if (var->assign_func)
- (*var->assign_func) (var, last_ind, nval);
- else
- array_add_element (a, last_ind, nval);
- FREE (nval);
- last_ind++;
- continue;
- }
+/* Add STRING, which is of the form foo=bar, to the temporary environment
+ HASH_TABLE (temporary_env). The functions in execute_cmd.c are
+ responsible for moving the main temporary env to one of the other
+ temporary environments. The expansion code in subst.c calls this. */
+int
+assign_in_env (word)
+ WORD_DESC *word;
+{
+ int offset;
+ char *name, *temp, *value;
+ SHELL_VAR *var;
+ const char *string;
- if (len == 1)
- {
- report_error ("%s: bad array subscript", w);
- continue;
- }
+ string = word->word;
- if (ALL_ELEMENT_SUB (w[1]) && len == 2)
- {
- report_error ("%s: cannot assign to non-numeric index", w);
- continue;
- }
+ offset = assignment (string, 0);
+ name = savestring (string);
+ value = (char *)NULL;
- ind = array_expand_index (w + 1, len);
- if (ind < 0)
- {
- report_error ("%s: bad array subscript", w);
- continue;
- }
- last_ind = ind;
- val = w + len + 2;
- }
- else /* No [ind]=value, just a stray `=' */
+ if (name[offset] == '=')
+ {
+ name[offset] = 0;
+
+ /* ignore the `+' when assigning temporary environment */
+ if (name[offset - 1] == '+')
+ name[offset - 1] = '\0';
+
+ var = find_variable (name);
+ if (var && (readonly_p (var) || noassign_p (var)))
{
- ind = last_ind;
- val = w;
+ if (readonly_p (var))
+ err_readonly (name);
+ free (name);
+ return (0);
}
- if (integer_p (var))
- this_command_name = (char *)NULL; /* no command name for errors */
- nval = make_variable_value (var, val);
- if (var->assign_func)
- (*var->assign_func) (var, ind, nval);
- else
- array_add_element (a, ind, nval);
- FREE (nval);
- last_ind++;
+ temp = name + offset + 1;
+#if 0
+ temp = (xstrchr (temp, '~') != 0) ? bash_tilde_expand (temp, 1) : savestring (temp);
+ value = expand_string_unsplit_to_string (temp, 0);
+ free (temp);
+#else
+ value = expand_assignment_string_to_string (temp, 0);
+#endif
}
- dispose_words (nlist);
- return (var);
+ if (temporary_env == 0)
+ temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
+
+ var = hash_lookup (name, temporary_env);
+ if (var == 0)
+ var = make_new_variable (name, temporary_env);
+ else
+ FREE (value_cell (var));
+
+ if (value == 0)
+ {
+ value = (char *)xmalloc (1); /* like do_assignment_internal */
+ value[0] = '\0';
+ }
+
+ var_setvalue (var, value);
+ var->attributes |= (att_exported|att_tempvar);
+ var->context = variable_context; /* XXX */
+
+ INVALIDATE_EXPORTSTR (var);
+ var->exportstr = mk_env_string (name, value);
+
+ array_needs_making = 1;
+
+ if (ifsname (name))
+ setifs (var);
+
+ if (echo_command_at_execute)
+ /* The Korn shell prints the `+ ' in front of assignment statements,
+ so we do too. */
+ xtrace_print_assignment (name, value, 0, 1);
+
+ free (name);
+ return 1;
}
-#endif /* ARRAY_VARS */
+
+/* **************************************************************** */
+/* */
+/* Copying variables */
+/* */
+/* **************************************************************** */
+
+#ifdef INCLUDE_UNUSED
+/* 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))
+ var_setfunc (copy, copy_command (function_cell (var)));
+#if defined (ARRAY_VARS)
+ else if (array_p (var))
+ var_setarray (copy, dup_array (array_cell (var)));
+#endif
+ else if (value_cell (var))
+ var_setvalue (copy, savestring (value_cell (var)));
+ else
+ var_setvalue (copy, (char *)NULL);
+
+ copy->dynamic_value = var->dynamic_value;
+ copy->assign_func = var->assign_func;
+
+ copy->exportstr = COPY_EXPORTSTR (var);
+
+ copy->context = var->context;
+ }
+ return (copy);
+}
+#endif
+
+/* **************************************************************** */
+/* */
+/* Deleting and unsetting variables */
+/* */
+/* **************************************************************** */
/* Dispose of the information attached to VAR. */
void
dispose_variable (var)
SHELL_VAR *var;
{
- if (!var)
+ if (var == 0)
return;
if (function_p (var))
dispose_command (function_cell (var));
#if defined (ARRAY_VARS)
else if (array_p (var))
- dispose_array (array_cell (var));
+ array_dispose (array_cell (var));
#endif
else
FREE (value_cell (var));
+ FREE_EXPORTSTR (var);
+
free (var->name);
if (exported_p (var))
free (var);
}
-#if defined (ARRAY_VARS)
-/* This function is called with SUB pointing to just after the beginning
- `[' of an array subscript. */
+/* Unset the shell variable referenced by NAME. */
int
-unbind_array_element (var, sub)
- SHELL_VAR *var;
- char *sub;
+unbind_variable (name)
+ const char *name;
{
- int len, ind;
- ARRAY_ELEMENT *ae;
+ return makunbound (name, shell_variables);
+}
- len = skipsubscript (sub, 0);
- if (sub[len] != ']' || len == 0)
- {
- builtin_error ("%s[%s: bad array subscript", var->name, sub);
- return -1;
- }
- sub[len] = '\0';
+/* Unset the shell function named NAME. */
+int
+unbind_func (name)
+ const char *name;
+{
+ BUCKET_CONTENTS *elt;
+ SHELL_VAR *func;
- if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
- {
- makunbound (var->name, shell_variables);
- return (0);
- }
- ind = array_expand_index (sub, len+1);
- if (ind < 0)
+ elt = hash_remove (name, shell_functions, 0);
+
+ if (elt == 0)
+ return -1;
+
+#if defined (PROGRAMMABLE_COMPLETION)
+ set_itemlist_dirty (&it_functions);
+#endif
+
+ func = (SHELL_VAR *)elt->data;
+ if (func)
{
- builtin_error ("[%s: bad array subscript", sub);
- return -1;
+ if (exported_p (func))
+ array_needs_making++;
+ dispose_variable (func);
}
- ae = array_delete_element (array_cell (var), ind);
- if (ae)
- destroy_array_element (ae);
- return 0;
+
+ free (elt->key);
+ free (elt);
+
+ return 0;
}
-#endif
-/* Unset the variable referenced by NAME. */
int
-unbind_variable (name)
- char *name;
+unbind_function_def (name)
+ const char *name;
{
- SHELL_VAR *var = find_variable (name);
+ BUCKET_CONTENTS *elt;
+ FUNCTION_DEF *funcdef;
- if (!var)
- return (-1);
+ elt = hash_remove (name, shell_function_defs, 0);
- /* This function should never be called with an array variable name. */
-#if defined (ARRAY_VARS)
- if (array_p (var) == 0 && var->value)
-#else
- if (var->value)
-#endif
- {
- free (var->value);
- var->value = (char *)NULL;
- }
+ if (elt == 0)
+ return -1;
- makunbound (name, shell_variables);
+ funcdef = (FUNCTION_DEF *)elt->data;
+ if (funcdef)
+ dispose_function_def (funcdef);
- return (0);
+ free (elt->key);
+ free (elt);
+
+ return 0;
}
/* Make the variable associated with NAME go away. HASH_LIST is the
shell_variables or shell_functions).
Returns non-zero if the variable couldn't be found. */
int
-makunbound (name, hash_list)
- char *name;
- HASH_TABLE *hash_list;
+makunbound (name, vc)
+ const char *name;
+ VAR_CONTEXT *vc;
{
- BUCKET_CONTENTS *elt;
- SHELL_VAR *old_var, *new_var;
+ BUCKET_CONTENTS *elt, *new_elt;
+ SHELL_VAR *old_var;
+ VAR_CONTEXT *v;
char *t;
- elt = remove_hash_item (name, hash_list);
+ for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
+ if (elt = hash_remove (name, v->table, 0))
+ break;
- if (!elt)
+ if (elt == 0)
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 we're unsetting a local variable and we're still executing inside
- the function, just mark the variable as invisible.
- kill_all_local_variables will clean it up later. This must be done
- so that if the variable is subsequently assigned a new value inside
- the function, the `local' attribute is still present. We also need
- to add it back into the correct hash table. */
+ the function, just mark the variable as invisible. The function
+ eventually called by pop_var_context() will clean it up later. This
+ must be done so that if the variable is subsequently assigned a new
+ value inside the function, the `local' attribute is still present.
+ We also need to add it back into the correct hash table. */
if (old_var && local_p (old_var) && variable_context == old_var->context)
{
- old_var->attributes |= att_invisible;
- elt = add_hash_item (savestring (old_var->name), hash_list);
- elt->data = (char *)old_var;
+#if defined (ARRAY_VARS)
+ if (array_p (old_var))
+ array_dispose (array_cell (old_var));
+ else
+#endif
+ FREE (value_cell (old_var));
+ /* Reset the attributes. Preserve the export attribute if the variable
+ came from a temporary environment. Make sure it stays local, and
+ make it invisible. */
+ old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0;
+ VSETATTR (old_var, att_local);
+ VSETATTR (old_var, att_invisible);
+ var_setvalue (old_var, (char *)NULL);
+ INVALIDATE_EXPORTSTR (old_var);
+
+ new_elt = hash_insert (savestring (old_var->name), v->table, 0);
+ new_elt->data = (PTR_T)old_var;
stupidly_hack_special_variables (old_var->name);
- return (0);
- }
- 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_auto_export (new_var);
+ free (elt->key);
+ free (elt);
+ return (0);
}
/* Have to save a copy of name here, because it might refer to
dispose_variable (old_var);
stupidly_hack_special_variables (t);
free (t);
- return (0);
-}
-/* Remove the variable with NAME if it is a local variable in the
- current context. */
-int
-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);
+ return (0);
}
/* 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);
-}
-
void
kill_all_local_variables ()
{
- register int i, pass;
- register SHELL_VAR *var, **list;
- HASH_TABLE *varlist;
-
- /* If HAVE_LOCAL_VARIABLES == 0, it means that we don't have any local
- variables at all. If VARIABLE_CONTEXT >= LOCAL_VARIABLE_STACK_SIZE,
- it means that we have some local variables, but not in this variable
- context (level of function nesting). Also, if
- HAVE_LOCAL_VARIABLES[VARIABLE_CONTEXT] == 0, we have no local variables
- at this context. */
- if (have_local_variables == 0 ||
- variable_context >= local_variable_stack_size ||
- have_local_variables[variable_context] == 0)
- return;
-
- for (pass = 0; pass < 2; pass++)
- {
- varlist = pass ? shell_functions : shell_variables;
+ VAR_CONTEXT *vc;
- list = map_over (variable_in_context, varlist);
+ for (vc = shell_variables; vc; vc = vc->down)
+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
+ break;
+ if (vc == 0)
+ return; /* XXX */
- if (list)
- {
- for (i = 0; var = list[i]; i++)
- {
- var->attributes &= ~att_local;
- makunbound (var->name, varlist);
- }
- free (list);
- }
+ if (vc->table && vc_haslocals (vc))
+ {
+ delete_all_variables (vc->table);
+ hash_dispose (vc->table);
}
-
- have_local_variables[variable_context] = 0; /* XXX */
+ vc->table = (HASH_TABLE *)NULL;
}
static void
free_variable_hash_data (data)
- char *data;
+ PTR_T data;
{
- SHELL_VAR *var, *prev;
+ SHELL_VAR *var;
var = (SHELL_VAR *)data;
- while (var)
- {
- prev = var->prev_context;
- dispose_variable (var);
- var = prev;
- }
+ dispose_variable (var);
}
/* Delete the entire contents of the hash table. */
delete_all_variables (hashed_vars)
HASH_TABLE *hashed_vars;
{
- flush_hash_table (hashed_vars, free_variable_hash_data);
+ hash_flush (hashed_vars, free_variable_hash_data);
}
-static SHELL_VAR *
-new_shell_variable (name)
- char *name;
-{
- SHELL_VAR *var;
-
- var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-
- bzero ((char *)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;
-
- 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 = 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 ((COMMAND *)entry->value);
-
- entry->value = value ? (char *)copy_command (value) : (char *)NULL;
- entry->attributes |= att_function;
-
- if (mark_modified_vars)
- entry->attributes |= att_exported;
-
- entry->attributes &= ~att_invisible; /* Just to be sure */
-
- if (exported_p (entry))
- 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 (function_cell (var));
-#if defined (ARRAY_VARS)
- else if (array_p (var))
- copy->value = (char *)dup_array (array_cell (var));
-#endif
- else if (value_cell (var))
- copy->value = savestring (value_cell (var));
- 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);
-}
+/* **************************************************************** */
+/* */
+/* Setting variable attributes */
+/* */
+/* **************************************************************** */
#define FIND_OR_MAKE_VARIABLE(name, entry) \
do \
entry = find_variable (name); \
if (!entry) \
{ \
- entry = bind_variable (name, ""); \
+ entry = bind_variable (name, "", 0); \
if (!no_invisible_vars) entry->attributes |= att_invisible; \
} \
} \
SHELL_VAR *entry;
FIND_OR_MAKE_VARIABLE (name, entry);
- entry->attributes |= att_readonly;
+ VSETATTR (entry, att_readonly);
}
+#ifdef INCLUDE_UNUSED
/* Make the function associated with NAME be readonly.
If NAME does not exist, we just punt, like auto_export code below. */
void
set_func_read_only (name)
- char *name;
+ const char *name;
{
- SHELL_VAR *entry = find_function (name);
+ SHELL_VAR *entry;
+ entry = find_function (name);
if (entry)
- entry->attributes |= att_readonly;
+ VSETATTR (entry, att_readonly);
}
/* Make the variable associated with NAME be auto-exported.
/* Make the function associated with NAME be auto-exported. */
void
set_func_auto_export (name)
- char *name;
+ const char *name;
{
SHELL_VAR *entry;
if (entry)
set_auto_export (entry);
}
+#endif
-#if defined (ARRAY_VARS)
-/* This function assumes s[i] == '['; returns with s[ret] == ']' if
- an array subscript is correctly parsed. */
-int
-skipsubscript (s, i)
- char *s;
- int i;
+/* **************************************************************** */
+/* */
+/* Creating lists of variables */
+/* */
+/* **************************************************************** */
+
+static VARLIST *
+vlist_alloc (nentries)
+ int nentries;
{
- int count, c;
+ VARLIST *vlist;
+
+ vlist = (VARLIST *)xmalloc (sizeof (VARLIST));
+ vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *));
+ vlist->list_size = nentries;
+ vlist->list_len = 0;
+ vlist->list[0] = (SHELL_VAR *)NULL;
+
+ return vlist;
+}
- for (count = 1; count && (c = s[++i]); )
+static VARLIST *
+vlist_realloc (vlist, n)
+ VARLIST *vlist;
+ int n;
+{
+ if (vlist == 0)
+ return (vlist = vlist_alloc (n));
+ if (n > vlist->list_size)
{
- if (c == '[')
- count++;
- else if (c == ']')
- count--;
+ vlist->list_size = n;
+ vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
}
- return i;
+ return vlist;
}
-#endif /* ARRAY_VARS */
-/* Returns non-zero if STRING is an assignment statement. The returned value
- is the index of the `=' sign. */
-int
-assignment (string)
- char *string;
+static void
+vlist_add (vlist, var, flags)
+ VARLIST *vlist;
+ SHELL_VAR *var;
+ int flags;
{
- register int c, newi, indx;
+ register int i;
- c = string[indx = 0];
+ for (i = 0; i < vlist->list_len; i++)
+ if (STREQ (var->name, vlist->list[i]->name))
+ break;
+ if (i < vlist->list_len)
+ return;
- if (legal_variable_starter (c) == 0)
- return (0);
+ if (i >= vlist->list_size)
+ vlist = vlist_realloc (vlist, vlist->list_size + 16);
- while (c = string[indx])
- {
- /* The following is safe. Note that '=' at the start of a word
- is not an assignment statement. */
- if (c == '=')
- return (indx);
+ vlist->list[vlist->list_len++] = var;
+ vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
+}
-#if defined (ARRAY_VARS)
- if (c == '[')
- {
- newi = skipsubscript (string, indx);
- if (string[newi++] != ']')
- return (0);
- return ((string[newi] == '=') ? newi : 0);
- }
-#endif /* ARRAY_VARS */
+/* Map FUNCTION over the variables in VAR_HASH_TABLE. Return an array of the
+ variables for which FUNCTION returns a non-zero value. A NULL value
+ for FUNCTION means to use all variables. */
+SHELL_VAR **
+map_over (function, vc)
+ sh_var_map_func_t *function;
+ VAR_CONTEXT *vc;
+{
+ VAR_CONTEXT *v;
+ VARLIST *vlist;
+ SHELL_VAR **ret;
+ int nentries;
+
+ for (nentries = 0, v = vc; v; v = v->down)
+ nentries += HASH_ENTRIES (v->table);
+
+ if (nentries == 0)
+ return (SHELL_VAR **)NULL;
+
+ vlist = vlist_alloc (nentries);
+
+ for (v = vc; v; v = v->down)
+ flatten (v->table, function, vlist, 0);
+
+ ret = vlist->list;
+ free (vlist);
+ return ret;
+}
+
+SHELL_VAR **
+map_over_funcs (function)
+ sh_var_map_func_t *function;
+{
+ VARLIST *vlist;
+ SHELL_VAR **ret;
- /* Variable names in assignment statements may contain only letters,
- digits, and `_'. */
- if (legal_variable_char (c) == 0)
- return (0);
+ if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0)
+ return ((SHELL_VAR **)NULL);
- indx++;
+ vlist = vlist_alloc (HASH_ENTRIES (shell_functions));
+
+ flatten (shell_functions, function, vlist, 0);
+
+ ret = vlist->list;
+ free (vlist);
+ return ret;
+}
+
+/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those
+ elements for which FUNC succeeds to VLIST->list. FLAGS is reserved
+ for future use. Only unique names are added to VLIST. If FUNC is
+ NULL, each variable in VAR_HASH_TABLE is added to VLIST. If VLIST is
+ NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE. If VLIST
+ and FUNC are both NULL, nothing happens. */
+static void
+flatten (var_hash_table, func, vlist, flags)
+ HASH_TABLE *var_hash_table;
+ sh_var_map_func_t *func;
+ VARLIST *vlist;
+ int flags;
+{
+ register int i;
+ register BUCKET_CONTENTS *tlist;
+ int r;
+ SHELL_VAR *var;
+
+ if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0))
+ return;
+
+ for (i = 0; i < var_hash_table->nbuckets; i++)
+ {
+ for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next)
+ {
+ var = (SHELL_VAR *)tlist->data;
+
+ r = func ? (*func) (var) : 1;
+ if (r && vlist)
+ vlist_add (vlist, var, flags);
+ }
}
- return (0);
+}
+
+void
+sort_variables (array)
+ SHELL_VAR **array;
+{
+ qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp);
}
static int
-visible_var (var)
- SHELL_VAR *var;
+qsort_var_comp (var1, var2)
+ SHELL_VAR **var1, **var2;
{
- return (invisible_p (var) == 0);
+ int result;
+
+ if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
+ result = strcmp ((*var1)->name, (*var2)->name);
+
+ return (result);
}
+/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for
+ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */
static SHELL_VAR **
-_visible_names (table)
- HASH_TABLE *table;
+vapply (func)
+ sh_var_map_func_t *func;
{
SHELL_VAR **list;
- list = map_over (visible_var, table);
-
- if (list && posixly_correct)
+ list = map_over (func, shell_variables);
+ if (list /* && posixly_correct */)
sort_variables (list);
+ return (list);
+}
+/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for
+ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */
+static SHELL_VAR **
+fapply (func)
+ sh_var_map_func_t *func;
+{
+ SHELL_VAR **list;
+
+ list = map_over_funcs (func);
+ if (list /* && posixly_correct */)
+ sort_variables (list);
return (list);
}
+/* Create a NULL terminated array of all the shell variables. */
SHELL_VAR **
-all_visible_variables ()
+all_shell_variables ()
{
- return (_visible_names (shell_variables));
+ return (vapply ((sh_var_map_func_t *)NULL));
+}
+
+/* Create a NULL terminated array of all the shell functions. */
+SHELL_VAR **
+all_shell_functions ()
+{
+ return (fapply ((sh_var_map_func_t *)NULL));
+}
+
+static int
+visible_var (var)
+ SHELL_VAR *var;
+{
+ return (invisible_p (var) == 0);
}
SHELL_VAR **
all_visible_functions ()
{
- return (_visible_names (shell_functions));
+ return (fapply (visible_var));
+}
+
+SHELL_VAR **
+all_visible_variables ()
+{
+ return (vapply (visible_var));
}
/* Return non-zero if the variable VAR is visible and exported. Array
return (invisible_p (var) == 0 && 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;
+/* Return non-zero if VAR is a local variable in the current context and
+ is exported. */
+static int
+local_and_exported (var)
+ SHELL_VAR *var;
{
- register int i, list_index;
- register SHELL_VAR *var;
- char **list, *value;
- SHELL_VAR **vars;
+ return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var));
+}
- list = (char **)NULL;
- vars = map_over (visible_and_exported, hashed_vars);
+SHELL_VAR **
+all_exported_variables ()
+{
+ return (vapply (visible_and_exported));
+}
- if (!vars)
- return (char **)NULL;
+SHELL_VAR **
+local_exported_variables ()
+{
+ return (vapply (local_and_exported));
+}
- list = (char **)xmalloc ((1 + array_len ((char **)vars)) * sizeof (char *));
+static int
+variable_in_context (var)
+ SHELL_VAR *var;
+{
+ return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context);
+}
- for (i = 0, list_index = 0; var = vars[i]; i++)
- {
- if (function_p (var))
- value = named_function_string ((char *)NULL, function_cell (var), 0);
-#if defined (ARRAY_VARS)
- else if (array_p (var))
-# if 0
- value = array_to_assignment_string (array_cell (var));
-# else
- continue; /* XXX array vars cannot yet be exported */
-# endif
-#endif
- else
- value = value_cell (var);
+SHELL_VAR **
+all_local_variables ()
+{
+ VARLIST *vlist;
+ SHELL_VAR **ret;
+ VAR_CONTEXT *vc;
- if (value)
- {
- int name_len, value_len;
- char *p;
-
- name_len = strlen (var->name);
- value_len = strlen (value);
- p = list[list_index] = xmalloc (2 + name_len + value_len);
- strcpy (p, var->name);
- p[name_len] = '=';
- strcpy (p + name_len + 1, value);
- list_index++;
-#if defined (ARRAY_VARS)
- if (array_p (var))
- free (value);
-#endif
- }
- }
+ vc = shell_variables;
+ for (vc = shell_variables; vc; vc = vc->down)
+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
+ break;
- free (vars);
- list[list_index] = (char *)NULL;
- return (list);
+ if (vc == 0)
+ {
+ internal_error (_("all_local_variables: no function context at current scope"));
+ return (SHELL_VAR **)NULL;
+ }
+ if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0)
+ return (SHELL_VAR **)NULL;
+
+ vlist = vlist_alloc (HASH_ENTRIES (vc->table));
+
+ flatten (vc->table, variable_in_context, vlist, 0);
+
+ ret = vlist->list;
+ free (vlist);
+ if (ret)
+ sort_variables (ret);
+ return ret;
}
-/* Add STRING to the array of foo=bar strings that we already
- have to add to the environment. */
-int
-assign_in_env (string)
- char *string;
+#if defined (ARRAY_VARS)
+/* Return non-zero if the variable VAR is visible and an array. */
+static int
+visible_array_vars (var)
+ SHELL_VAR *var;
{
- int size, offset;
- char *name, *temp, *value;
- int nlen, vlen;
- WORD_LIST *list;
- SHELL_VAR *var;
+ return (invisible_p (var) == 0 && array_p (var));
+}
- offset = assignment (string);
- name = savestring (string);
- value = (char *)NULL;
+SHELL_VAR **
+all_array_variables ()
+{
+ return (vapply (visible_array_vars));
+}
+#endif /* ARRAY_VARS */
-#define freetemp nlen
- if (name[offset] == '=')
+char **
+all_variables_matching_prefix (prefix)
+ const char *prefix;
+{
+ SHELL_VAR **varlist;
+ char **rlist;
+ int vind, rind, plen;
+
+ plen = STRLEN (prefix);
+ varlist = all_visible_variables ();
+ for (vind = 0; varlist && varlist[vind]; vind++)
+ ;
+ if (varlist == 0 || vind == 0)
+ return ((char **)NULL);
+ rlist = strvec_create (vind + 1);
+ for (vind = rind = 0; varlist[vind]; vind++)
{
- name[offset] = 0;
-
- var = find_variable (name);
- if (var && readonly_p (var))
- {
- report_error ("%s: readonly variable", name);
- return (0);
- }
- temp = name + offset + 1;
- freetemp = strchr (temp, '~') != 0;
- if (freetemp)
- temp = bash_tilde_expand (temp);
+ if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen))
+ rlist[rind++] = savestring (varlist[vind]->name);
+ }
+ rlist[rind] = (char *)0;
+ free (varlist);
- list = expand_string_unsplit (temp, 0);
- value = string_list (list);
+ return rlist;
+}
- if (list)
- dispose_words (list);
+/* **************************************************************** */
+/* */
+/* Managing temporary variable scopes */
+/* */
+/* **************************************************************** */
- if (freetemp)
- free (temp);
- }
-#undef freetemp
+/* Make variable NAME have VALUE in the temporary environment. */
+static SHELL_VAR *
+bind_tempenv_variable (name, value)
+ const char *name;
+ char *value;
+{
+ SHELL_VAR *var;
- nlen = strlen (name);
- vlen = value ? strlen (value) : 0;
- temp = xmalloc (2 + nlen + vlen);
- strcpy (temp, name);
- temp[nlen] = '=';
- temp[nlen + 1] = '\0';
- if (value)
- {
- if (*value)
- strcpy (temp + nlen + 1, value);
- free (value);
- }
- free (name);
+ var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;
- if (temporary_env == 0)
+ if (var)
{
- temporary_env = (char **)xmalloc (sizeof (char *));
- temporary_env [0] = (char *)NULL;
+ FREE (value_cell (var));
+ var_setvalue (var, savestring (value));
+ INVALIDATE_EXPORTSTR (var);
}
- 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 Korn shell prints the `+ ' in front of assignment statements,
- so we do too. */
- fprintf (stderr, "%s%s\n", indirection_level_string (), temp);
- fflush (stderr);
- }
+ return (var);
+}
- return 1;
+/* Find a variable in the temporary environment that is named NAME.
+ Return the SHELL_VAR *, or NULL if not found. */
+SHELL_VAR *
+find_tempenv_variable (name)
+ const char *name;
+{
+ return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL);
}
-/* Search for NAME in ARRAY, an array of strings in the same format as the
- environment array (i.e, name=value). If NAME is present, make a new
- variable and return it. Otherwise, return NULL. */
-static SHELL_VAR *
-find_name_in_env_array (name, array)
- char *name;
- char **array;
+/* Push the variable described by (SHELL_VAR *)DATA down to the next
+ variable context from the temporary environment. */
+static void
+push_temp_var (data)
+ PTR_T data;
{
- register int i, l;
+ SHELL_VAR *var, *v;
+ HASH_TABLE *binding_table;
- if (array == 0)
- return ((SHELL_VAR *)NULL);
+ var = (SHELL_VAR *)data;
- for (i = 0, l = strlen (name); array[i]; i++)
+ binding_table = shell_variables->table;
+ if (binding_table == 0)
{
- if (STREQN (array[i], name, l) && array[i][l] == '=')
- {
- SHELL_VAR *temp;
- char *w;
-
- temp = new_shell_variable (name);
- w = array[i] + l + 1;
-
- temp->value = *w ? savestring (w) : (char *)NULL;
-
- temp->attributes = att_exported;
- temp->context = 0;
- temp->prev_context = (SHELL_VAR *)NULL;
+ if (shell_variables == global_variables)
+ /* shouldn't happen */
+ binding_table = shell_variables->table = global_variables->table = hash_create (0);
+ else
+ binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
+ }
- temp->dynamic_value = temp->assign_func = (DYNAMIC_FUNC *)NULL;
+ v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
- return (temp);
- }
+ /* XXX - should we set the context here? It shouldn't matter because of how
+ assign_in_env works, but might want to check. */
+ if (binding_table == global_variables->table) /* XXX */
+ var->attributes &= ~(att_tempvar|att_propagate);
+ else
+ {
+ var->attributes |= att_propagate;
+ if (binding_table == shell_variables->table)
+ shell_variables->flags |= VC_HASTMPVAR;
}
- return ((SHELL_VAR *)NULL);
+ v->attributes |= var->attributes;
+
+ dispose_variable (var);
}
-/* Find a variable in the temporary environment that is named NAME.
- The temporary environment can be either the environment provided
- to a simple command, or the environment provided to a shell function.
- We only search the function environment if we are currently executing
- a shell function body (variable_context > 0). Return a consed variable,
- or NULL if not found. */
-SHELL_VAR *
-find_tempenv_variable (name)
- char *name;
+static void
+propagate_temp_var (data)
+ PTR_T data;
{
- SHELL_VAR *var = (SHELL_VAR *)NULL;
-
- if (temporary_env)
- var = find_name_in_env_array (name, temporary_env);
-
- /* We don't check this_shell_builtin because the command that needs the
- value from builtin_env may be a disk command run inside a script run
- with `.' and a temporary env. */
- if (!var && builtin_env)
- var = find_name_in_env_array (name, builtin_env);
-
- if (!var && variable_context && function_env)
- var = find_name_in_env_array (name, function_env);
+ SHELL_VAR *var;
- return (var);
+ var = (SHELL_VAR *)data;
+ if (tempvar_p (var) && (var->attributes & att_propagate))
+ push_temp_var (data);
+ else
+ dispose_variable (var);
}
-/* Free the storage allocated to the string array pointed to by ARRAYP, and
- make that variable have a null pointer as a value. */
+/* Free the storage used in the hash table for temporary
+ environment variables. PUSHF is a function to be called
+ to free each hash table entry. It takes care of pushing variables
+ to previous scopes if appropriate. */
static void
-dispose_temporary_vars (arrayp)
- char ***arrayp;
+dispose_temporary_env (pushf)
+ sh_free_func_t *pushf;
{
- if (!*arrayp)
- return;
+ hash_flush (temporary_env, pushf);
+ hash_dispose (temporary_env);
+ temporary_env = (HASH_TABLE *)NULL;
- free_array (*arrayp);
- *arrayp = (char **)NULL;
array_needs_making = 1;
+
+ sv_ifs ("IFS"); /* XXX here for now */
}
-/* Free the storage used in the variable array for temporary
- environment variables. */
void
dispose_used_env_vars ()
{
- dispose_temporary_vars (&temporary_env);
+ if (temporary_env)
+ dispose_temporary_env (propagate_temp_var);
}
-/* Free the storage used for temporary environment variables given to
- commands when executing inside of a function body. */
+/* Take all of the shell variables in the temporary environment HASH_TABLE
+ and make shell variables from them at the current variable context. */
void
-dispose_function_env ()
+merge_temporary_env ()
{
- dispose_temporary_vars (&function_env);
+ if (temporary_env)
+ dispose_temporary_env (push_temp_var);
}
-/* Free the storage used for temporary environment variables given to
- commands when executing a builtin command such as "source". */
-void
-dispose_builtin_env ()
+/* **************************************************************** */
+/* */
+/* Creating and manipulating the environment */
+/* */
+/* **************************************************************** */
+
+static inline char *
+mk_env_string (name, value)
+ const char *name, *value;
{
- dispose_temporary_vars (&builtin_env);
+ int name_len, value_len;
+ char *p;
+
+ name_len = strlen (name);
+ value_len = STRLEN (value);
+ p = (char *)xmalloc (2 + name_len + value_len);
+ strcpy (p, name);
+ p[name_len] = '=';
+ if (value && *value)
+ strcpy (p + name_len + 1, value);
+ else
+ p[name_len + 1] = '\0';
+ return (p);
}
-/* Take all of the shell variables in ENV_ARRAY and make shell variables
- from them at the current variable context. */
-static void
-merge_env_array (env_array)
- char **env_array;
+#ifdef DEBUG
+/* Debugging */
+static int
+valid_exportstr (v)
+ SHELL_VAR *v;
{
- register int i, l;
- SHELL_VAR *temp;
- char *w, *name;
-
- if (env_array == 0)
- return;
+ char *s;
- for (i = 0; env_array[i]; i++)
+ s = v->exportstr;
+ if (legal_variable_starter ((unsigned char)*s) == 0)
+ {
+ internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
+ return (0);
+ }
+ for (s = v->exportstr + 1; s && *s; s++)
+ {
+ if (*s == '=')
+ break;
+ if (legal_variable_char ((unsigned char)*s) == 0)
+ {
+ internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
+ return (0);
+ }
+ }
+ if (*s != '=')
{
- l = assignment (env_array[i]);
- name = env_array[i];
- w = env_array[i] + l + 1;
- name[l] = '\0';
- temp = bind_variable (name, w);
- name[l] = '=';
+ internal_error (_("no `=' in exportstr for %s"), v->name);
+ return (0);
}
+ return (1);
}
+#endif
-void
-merge_temporary_env ()
+static char **
+make_env_array_from_var_list (vars)
+ SHELL_VAR **vars;
{
- merge_env_array (temporary_env);
+ register int i, list_index;
+ register SHELL_VAR *var;
+ char **list, *value;
+
+ list = strvec_create ((1 + strvec_len ((char **)vars)));
+
+#define USE_EXPORTSTR (value == var->exportstr)
+
+ for (i = 0, list_index = 0; var = vars[i]; i++)
+ {
+#if defined (__CYGWIN__)
+ /* We don't use the exportstr stuff on Cygwin at all. */
+ INVALIDATE_EXPORTSTR (var);
+#endif
+ if (var->exportstr)
+ value = var->exportstr;
+ else if (function_p (var))
+ value = named_function_string ((char *)NULL, function_cell (var), 0);
+#if defined (ARRAY_VARS)
+ else if (array_p (var))
+# if 0
+ value = array_to_assignment_string (array_cell (var));
+# else
+ continue; /* XXX array vars cannot yet be exported */
+# endif
+#endif
+ else
+ value = value_cell (var);
+
+ if (value)
+ {
+ /* Gee, I'd like to get away with not using savestring() if we're
+ using the cached exportstr... */
+ list[list_index] = USE_EXPORTSTR ? savestring (value)
+ : mk_env_string (var->name, value);
+
+ if (USE_EXPORTSTR == 0)
+ SAVE_EXPORTSTR (var, list[list_index]);
+
+ list_index++;
+#undef USE_EXPORTSTR
+
+#if 0 /* not yet */
+#if defined (ARRAY_VARS)
+ if (array_p (var))
+ free (value);
+#endif
+#endif
+ }
+ }
+
+ list[list_index] = (char *)NULL;
+ return (list);
}
-void
-merge_builtin_env ()
+/* Make an array of assignment statements from the hash table
+ HASHED_VARS which contains SHELL_VARs. Only visible, exported
+ variables are eligible. */
+static char **
+make_var_export_array (vcxt)
+ VAR_CONTEXT *vcxt;
{
- merge_env_array (builtin_env);
+ char **list;
+ SHELL_VAR **vars;
+
+ vars = map_over (visible_and_exported, vcxt);
+
+ if (vars == 0)
+ return (char **)NULL;
+
+ list = make_env_array_from_var_list (vars);
+
+ free (vars);
+ return (list);
}
-#define ISFUNC(s, o) ((s[o + 1] == '(') && (s[o + 2] == ')'))
+static char **
+make_func_export_array ()
+{
+ char **list;
+ SHELL_VAR **vars;
+
+ vars = map_over_funcs (visible_and_exported);
+ if (vars == 0)
+ return (char **)NULL;
+
+ list = make_env_array_from_var_list (vars);
+
+ free (vars);
+ return (list);
+}
-/* Add ASSIGN to ARRAY, or supercede a previous assignment in the
- array with the same left-hand side. Return the new array. */
+/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */
+#define add_to_export_env(envstr,do_alloc) \
+do \
+ { \
+ if (export_env_index >= (export_env_size - 1)) \
+ { \
+ export_env_size += 16; \
+ export_env = strvec_resize (export_env, export_env_size); \
+ environ = export_env; \
+ } \
+ export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \
+ export_env[export_env_index] = (char *)NULL; \
+ } while (0)
+
+/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the
+ array with the same left-hand side. Return the new EXPORT_ENV. */
char **
-add_or_supercede (assign, array)
+add_or_supercede_exported_var (assign, do_alloc)
char *assign;
- register char **array;
+ int do_alloc;
{
register int i;
- int equal_offset = assignment (assign);
+ int equal_offset;
- if (!equal_offset)
- return (array);
+ equal_offset = assignment (assign, 0);
+ if (equal_offset == 0)
+ return (export_env);
- /* 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++;
+ /* If this is a function, then only supersede the function definition.
+ We do this by including the `=() {' in the comparison, like
+ initialize_shell_variables does. */
+ if (assign[equal_offset + 1] == '(' &&
+ strncmp (assign + equal_offset + 2, ") {", 3) == 0) /* } */
+ equal_offset += 4;
- for (i = 0; array && array[i]; i++)
+ for (i = 0; i < export_env_index; i++)
{
- if (STREQN (assign, array[i], equal_offset + 1))
+ if (STREQN (assign, export_env[i], equal_offset + 1))
{
- free (array[i]);
- array[i] = savestring (assign);
- return (array);
+ free (export_env[i]);
+ export_env[i] = do_alloc ? savestring (assign) : assign;
+ return (export_env);
}
}
- array = (char **)xrealloc (array, ((2 + i) * sizeof (char *)));
- array[i++] = savestring (assign);
- array[i] = (char *)NULL;
- return (array);
+ add_to_export_env (assign, do_alloc);
+ return (export_env);
+}
+
+static void
+add_temp_array_to_env (temp_array, do_alloc, do_supercede)
+ char **temp_array;
+ int do_alloc, do_supercede;
+{
+ register int i;
+
+ if (temp_array == 0)
+ return;
+
+ for (i = 0; temp_array[i]; i++)
+ {
+ if (do_supercede)
+ export_env = add_or_supercede_exported_var (temp_array[i], do_alloc);
+ else
+ add_to_export_env (temp_array[i], do_alloc);
+ }
+
+ free (temp_array);
}
-/* Make the environment array for the command about to be executed. If the
+/* 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++'. */
+ code should `array_needs_making++'.
+
+ The order to add to the array is:
+ temporary_env
+ list of var contexts whose head is shell_variables
+ shell_functions
+
+ This is the shell variable lookup order. We add only new variable
+ names at each step, which allows local variables and variables in
+ the temporary environments to shadow variables in the global (or
+ any previous) scope.
+*/
+
+static int
+n_shell_variables ()
+{
+ VAR_CONTEXT *vc;
+ int n;
+
+ for (n = 0, vc = shell_variables; vc; vc = vc->down)
+ n += HASH_ENTRIES (vc->table);
+ return n;
+}
+
void
maybe_make_export_env ()
{
- register int i;
register char **temp_array;
+ int new_size;
+ VAR_CONTEXT *tcxt;
if (array_needs_making)
{
if (export_env)
- free_array (export_env);
-
- export_env = (char **)xmalloc (sizeof (char *));
- export_env[0] = (char *)NULL;
-
- 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 (function_env)
- for (i = 0; function_env[i]; i++)
- export_env = add_or_supercede (function_env[i], export_env);
+ strvec_flush (export_env);
+
+ /* Make a guess based on how many shell variables and functions we
+ have. Since there will always be array variables, and array
+ variables are not (yet) exported, this will always be big enough
+ for the exported variables and functions. */
+ new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 +
+ HASH_ENTRIES (temporary_env);
+ if (new_size > export_env_size)
+ {
+ export_env_size = new_size;
+ export_env = strvec_resize (export_env, export_env_size);
+ environ = export_env;
+ }
+ export_env[export_env_index = 0] = (char *)NULL;
+ /* Make a dummy variable context from the temporary_env, stick it on
+ the front of shell_variables, call make_var_export_array on the
+ whole thing to flatten it, and convert the list of SHELL_VAR *s
+ to the form needed by the environment. */
if (temporary_env)
- for (i = 0; temporary_env[i]; i++)
- export_env = add_or_supercede (temporary_env[i], export_env);
-
-#if 0
- /* If we changed the array, then sort it alphabetically. */
- if (posixly_correct == 0 && (temporary_env || function_env))
- sort_char_array (export_env);
+ {
+ tcxt = new_var_context ((char *)NULL, 0);
+ tcxt->table = temporary_env;
+ tcxt->down = shell_variables;
+ }
+ else
+ tcxt = shell_variables;
+
+ temp_array = make_var_export_array (tcxt);
+ if (temp_array)
+ add_temp_array_to_env (temp_array, 0, 0);
+
+ if (tcxt != shell_variables)
+ free (tcxt);
+
+#if defined (RESTRICTED_SHELL)
+ /* Restricted shells may not export shell functions. */
+ temp_array = restricted ? (char **)0 : make_func_export_array ();
+#else
+ temp_array = make_func_export_array ();
#endif
+ if (temp_array)
+ add_temp_array_to_env (temp_array, 0, 0);
array_needs_making = 0;
}
}
+/* This is an efficiency hack. PWD and OLDPWD are auto-exported, so
+ we will need to remake the exported environment every time we
+ change directories. `_' is always put into the environment for
+ every external command, so without special treatment it will always
+ cause the environment to be remade.
+
+ If there is no other reason to make the exported environment, we can
+ just update the variables in place and mark the exported environment
+ as no longer needing a remake. */
+void
+update_export_env_inplace (env_prefix, preflen, value)
+ char *env_prefix;
+ int preflen;
+ char *value;
+{
+ char *evar;
+
+ evar = (char *)xmalloc (STRLEN (value) + preflen + 1);
+ strcpy (evar, env_prefix);
+ if (value)
+ strcpy (evar + preflen, value);
+ export_env = add_or_supercede_exported_var (evar, 0);
+}
+
/* We always put _ in the environment as the name of this command. */
void
put_command_name_into_env (command_name)
char *command_name;
{
- char *dummy;
-
- dummy = 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);
+ update_export_env_inplace ("_=", 2, command_name);
}
+#if 0 /* UNUSED -- it caused too many problems */
void
put_gnu_argv_flags_into_env (pid, flags_string)
- int pid;
+ intmax_t pid;
char *flags_string;
{
char *dummy, *pbuf;
fl = strlen (flags_string);
- dummy = xmalloc (l + fl + 30);
+ dummy = (char *)xmalloc (l + fl + 30);
dummy[0] = '_';
strcpy (dummy + 1, pbuf);
strcpy (dummy + 1 + l, "_GNU_nonoption_argv_flags_");
free (pbuf);
- export_env = add_or_supercede (dummy, export_env);
- free (dummy);
+ export_env = add_or_supercede_exported_var (dummy, 0);
}
+#endif
+
+/* **************************************************************** */
+/* */
+/* Managing variable contexts */
+/* */
+/* **************************************************************** */
-/* Return a string denoting what our indirection level is. */
-static char indirection_string[100];
+/* Allocate and return a new variable context with NAME and FLAGS.
+ NAME can be NULL. */
-char *
-indirection_level_string ()
+VAR_CONTEXT *
+new_var_context (name, flags)
+ char *name;
+ int flags;
+{
+ VAR_CONTEXT *vc;
+
+ vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT));
+ vc->name = name ? savestring (name) : (char *)NULL;
+ vc->scope = variable_context;
+ vc->flags = flags;
+
+ vc->up = vc->down = (VAR_CONTEXT *)NULL;
+ vc->table = (HASH_TABLE *)NULL;
+
+ return vc;
+}
+
+/* Free a variable context and its data, including the hash table. Dispose
+ all of the variables. */
+void
+dispose_var_context (vc)
+ VAR_CONTEXT *vc;
+{
+ FREE (vc->name);
+
+ if (vc->table)
+ {
+ delete_all_variables (vc->table);
+ hash_dispose (vc->table);
+ }
+
+ free (vc);
+}
+
+/* Set VAR's scope level to the current variable context. */
+static int
+set_context (var)
+ SHELL_VAR *var;
{
- register int i, j;
- char *ps4;
+ return (var->context = variable_context);
+}
+
+/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of
+ temporary variables, and push it onto shell_variables. This is
+ for shell functions. */
+VAR_CONTEXT *
+push_var_context (name, flags, tempvars)
+ char *name;
+ int flags;
+ HASH_TABLE *tempvars;
+{
+ VAR_CONTEXT *vc;
+
+ vc = new_var_context (name, flags);
+ vc->table = tempvars;
+ if (tempvars)
+ {
+ /* Have to do this because the temp environment was created before
+ variable_context was incremented. */
+ flatten (tempvars, set_context, (VARLIST *)NULL, 0);
+ vc->flags |= VC_HASTMPVAR;
+ }
+ vc->down = shell_variables;
+ shell_variables->up = vc;
+
+ return (shell_variables = vc);
+}
+
+static void
+push_func_var (data)
+ PTR_T data;
+{
+ SHELL_VAR *var, *v;
+
+ var = (SHELL_VAR *)data;
+
+ if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
+ {
+ /* XXX - should we set v->context here? */
+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
+ if (shell_variables == global_variables)
+ var->attributes &= ~(att_tempvar|att_propagate);
+ else
+ shell_variables->flags |= VC_HASTMPVAR;
+ v->attributes |= var->attributes;
+ }
+
+ dispose_variable (var);
+}
+
+/* Pop the top context off of VCXT and dispose of it, returning the rest of
+ the stack. */
+void
+pop_var_context ()
+{
+ VAR_CONTEXT *ret, *vcxt;
+
+ vcxt = shell_variables;
+ if (vc_isfuncenv (vcxt) == 0)
+ {
+ internal_error (_("pop_var_context: head of shell_variables not a function context"));
+ return;
+ }
+
+ if (ret = vcxt->down)
+ {
+ ret->up = (VAR_CONTEXT *)NULL;
+ shell_variables = ret;
+ if (vcxt->table)
+ hash_flush (vcxt->table, push_func_var);
+ dispose_var_context (vcxt);
+ }
+ else
+ internal_error (_("pop_var_context: no global_variables context"));
+}
+
+/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
+ all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
+void
+delete_all_contexts (vcxt)
+ VAR_CONTEXT *vcxt;
+{
+ VAR_CONTEXT *v, *t;
+
+ for (v = vcxt; v != global_variables; v = t)
+ {
+ t = v->down;
+ dispose_var_context (v);
+ }
+
+ delete_all_variables (global_variables->table);
+ shell_variables = global_variables;
+}
+
+/* **************************************************************** */
+/* */
+/* Pushing and Popping temporary variable scopes */
+/* */
+/* **************************************************************** */
+
+VAR_CONTEXT *
+push_scope (flags, tmpvars)
+ int flags;
+ HASH_TABLE *tmpvars;
+{
+ return (push_var_context ((char *)NULL, flags, tmpvars));
+}
+
+static void
+push_exported_var (data)
+ PTR_T data;
+{
+ SHELL_VAR *var, *v;
+
+ var = (SHELL_VAR *)data;
+
+ /* If a temp var had its export attribute set, or it's marked to be
+ propagated, bind it in the previous scope before disposing it. */
+ /* XXX - This isn't exactly right, because all tempenv variables have the
+ export attribute set. */
+#if 0
+ if (exported_p (var) || (var->attributes & att_propagate))
+#else
+ if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
+#endif
+ {
+ var->attributes &= ~att_tempvar; /* XXX */
+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
+ if (shell_variables == global_variables)
+ var->attributes &= ~att_propagate;
+ v->attributes |= var->attributes;
+ }
- indirection_string[0] = '\0';
- ps4 = get_string_value ("PS4");
+ dispose_variable (var);
+}
- if (ps4 == 0 || *ps4 == '\0')
- return (indirection_string);
+void
+pop_scope (is_special)
+ int is_special;
+{
+ VAR_CONTEXT *vcxt, *ret;
- ps4 = decode_prompt_string (ps4);
+ vcxt = shell_variables;
+ if (vc_istempscope (vcxt) == 0)
+ {
+ internal_error (_("pop_scope: head of shell_variables not a temporary environment scope"));
+ return;
+ }
- for (i = 0; *ps4 && i < indirection_level && i < 99; i++)
- indirection_string[i] = *ps4;
+ ret = vcxt->down;
+ if (ret)
+ ret->up = (VAR_CONTEXT *)NULL;
- for (j = 1; *ps4 && ps4[j] && i < 99; i++, j++)
- indirection_string[i] = ps4[j];
+ shell_variables = ret;
- indirection_string[i] = '\0';
- free (ps4);
- return (indirection_string);
+ /* Now we can take care of merging variables in VCXT into set of scopes
+ whose head is RET (shell_variables). */
+ FREE (vcxt->name);
+ if (vcxt->table)
+ {
+ if (is_special)
+ hash_flush (vcxt->table, push_func_var);
+ else
+ hash_flush (vcxt->table, push_exported_var);
+ hash_dispose (vcxt->table);
+ }
+ free (vcxt);
+
+ sv_ifs ("IFS"); /* XXX here for now */
+}
+
+/* **************************************************************** */
+/* */
+/* Pushing and Popping function contexts */
+/* */
+/* **************************************************************** */
+
+static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
+static int dollar_arg_stack_slots;
+static int dollar_arg_stack_index;
+
+/* XXX - we might want to consider pushing and popping the `getopts' state
+ when we modify the positional parameters. */
+void
+push_context (name, is_subshell, tempvars)
+ char *name; /* function name */
+ int is_subshell;
+ HASH_TABLE *tempvars;
+{
+ if (is_subshell == 0)
+ push_dollar_vars ();
+ variable_context++;
+ push_var_context (name, VC_FUNCENV, tempvars);
+}
+
+/* Only called when subshell == 0, so we don't need to check, and can
+ unconditionally pop the dollar vars off the stack. */
+void
+pop_context ()
+{
+ pop_dollar_vars ();
+ variable_context--;
+ pop_var_context ();
+
+ sv_ifs ("IFS"); /* XXX here for now */
+}
+
+/* Save the existing positional parameters on a stack. */
+void
+push_dollar_vars ()
+{
+ if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
+ {
+ dollar_arg_stack = (WORD_LIST **)
+ xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
+ * sizeof (WORD_LIST **));
+ }
+ dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+}
+
+/* Restore the positional parameters from our stack. */
+void
+pop_dollar_vars ()
+{
+ if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+ return;
+
+ remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
+ dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+ set_dollar_vars_unchanged ();
+}
+
+void
+dispose_saved_dollar_vars ()
+{
+ if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+ return;
+
+ dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+}
+
+/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */
+
+void
+push_args (list)
+ WORD_LIST *list;
+{
+#if defined (ARRAY_VARS) && defined (DEBUGGER)
+ SHELL_VAR *bash_argv_v, *bash_argc_v;
+ ARRAY *bash_argv_a, *bash_argc_a;
+ WORD_LIST *l;
+ arrayind_t i;
+ char *t;
+
+ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
+ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
+
+ for (l = list, i = 0; l; l = l->next, i++)
+ array_push (bash_argv_a, l->word->word);
+
+ t = itos (i);
+ array_push (bash_argc_a, t);
+ free (t);
+#endif /* ARRAY_VARS && DEBUGGER */
+}
+
+/* Remove arguments from BASH_ARGV array. Pop top element off BASH_ARGC
+ array and use that value as the count of elements to remove from
+ BASH_ARGV. */
+void
+pop_args ()
+{
+#if defined (ARRAY_VARS) && defined (DEBUGGER)
+ SHELL_VAR *bash_argv_v, *bash_argc_v;
+ ARRAY *bash_argv_a, *bash_argc_a;
+ ARRAY_ELEMENT *ce;
+ intmax_t i;
+
+ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
+ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
+
+ ce = array_shift (bash_argc_a, 1, 0);
+ if (ce == 0 || legal_number (element_value (ce), &i) == 0)
+ i = 0;
+
+ for ( ; i > 0; i--)
+ array_pop (bash_argv_a);
+ array_dispose_element (ce);
+#endif /* ARRAY_VARS && DEBUGGER */
+}
+
+/*************************************************
+ * *
+ * Functions to manage special variables *
+ * *
+ *************************************************/
+
+/* Extern declarations for variables this code has to manage. */
+extern int eof_encountered, eof_encountered_limit, ignoreeof;
+
+#if defined (READLINE)
+extern int hostname_list_initialized;
+#endif
+
+/* An alist of name.function for each special variable. Most of the
+ functions don't do much, and in fact, this would be faster with a
+ switch statement, but by the end of this file, I am sick of switch
+ statements. */
+
+#define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0
+
+/* This table will be sorted with qsort() the first time it's accessed. */
+struct name_and_function {
+ char *name;
+ sh_sv_func_t *function;
+};
+
+static struct name_and_function special_vars[] = {
+#if defined (READLINE)
+# if defined (STRICT_POSIX)
+ { "COLUMNS", sv_winsize },
+# endif
+ { "COMP_WORDBREAKS", sv_comp_wordbreaks },
+#endif
+
+ { "GLOBIGNORE", sv_globignore },
+
+#if defined (HISTORY)
+ { "HISTCONTROL", sv_history_control },
+ { "HISTFILESIZE", sv_histsize },
+ { "HISTIGNORE", sv_histignore },
+ { "HISTSIZE", sv_histsize },
+ { "HISTTIMEFORMAT", sv_histtimefmt },
+#endif
+
+#if defined (__CYGWIN__)
+ { "HOME", sv_home },
+#endif
+
+#if defined (READLINE)
+ { "HOSTFILE", sv_hostfile },
+#endif
+
+ { "IFS", sv_ifs },
+ { "IGNOREEOF", sv_ignoreeof },
+
+ { "LANG", sv_locale },
+ { "LC_ALL", sv_locale },
+ { "LC_COLLATE", sv_locale },
+ { "LC_CTYPE", sv_locale },
+ { "LC_MESSAGES", sv_locale },
+ { "LC_NUMERIC", sv_locale },
+ { "LC_TIME", sv_locale },
+
+#if defined (READLINE) && defined (STRICT_POSIX)
+ { "LINES", sv_winsize },
+#endif
+
+ { "MAIL", sv_mail },
+ { "MAILCHECK", sv_mail },
+ { "MAILPATH", sv_mail },
+
+ { "OPTERR", sv_opterr },
+ { "OPTIND", sv_optind },
+
+ { "PATH", sv_path },
+ { "POSIXLY_CORRECT", sv_strict_posix },
+
+#if defined (READLINE)
+ { "TERM", sv_terminal },
+ { "TERMCAP", sv_terminal },
+ { "TERMINFO", sv_terminal },
+#endif /* READLINE */
+
+ { "TEXTDOMAIN", sv_locale },
+ { "TEXTDOMAINDIR", sv_locale },
+
+#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+ { "TZ", sv_tz },
+#endif
+
+#if defined (HISTORY) && defined (BANG_HISTORY)
+ { "histchars", sv_histchars },
+#endif /* HISTORY && BANG_HISTORY */
+
+ { "ignoreeof", sv_ignoreeof },
+
+ { (char *)0, (sh_sv_func_t *)0 }
+};
+
+#define N_SPECIAL_VARS (sizeof (special_vars) / sizeof (special_vars[0]) - 1)
+
+static int
+sv_compare (sv1, sv2)
+ struct name_and_function *sv1, *sv2;
+{
+ int r;
+
+ if ((r = sv1->name[0] - sv2->name[0]) == 0)
+ r = strcmp (sv1->name, sv2->name);
+ return r;
+}
+
+static inline int
+find_special_var (name)
+ const char *name;
+{
+ register int i, r;
+
+ for (i = 0; special_vars[i].name; i++)
+ {
+ r = special_vars[i].name[0] - name[0];
+ if (r == 0)
+ r = strcmp (special_vars[i].name, name);
+ if (r == 0)
+ return i;
+ else if (r > 0)
+ /* Can't match any of rest of elements in sorted list. Take this out
+ if it causes problems in certain environments. */
+ break;
+ }
+ return -1;
+}
+
+/* The variable in NAME has just had its state changed. Check to see if it
+ is one of the special ones where something special happens. */
+void
+stupidly_hack_special_variables (name)
+ char *name;
+{
+ static int sv_sorted = 0;
+ int i;
+
+ if (sv_sorted == 0) /* shouldn't need, but it's fairly cheap. */
+ {
+ qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]),
+ (QSFUNC *)sv_compare);
+ sv_sorted = 1;
+ }
+
+ i = find_special_var (name);
+ if (i != -1)
+ (*(special_vars[i].function)) (name);
+}
+
+void
+sv_ifs (name)
+ char *name;
+{
+ SHELL_VAR *v;
+
+ v = find_variable ("IFS");
+ setifs (v);
+}
+
+/* What to do just after the PATH variable has changed. */
+void
+sv_path (name)
+ char *name;
+{
+ /* hash -r */
+ phash_flush ();
+}
+
+/* What to do just after one of the MAILxxxx variables has changed. NAME
+ is the name of the variable. This is called with NAME set to one of
+ MAIL, MAILCHECK, or MAILPATH. */
+void
+sv_mail (name)
+ char *name;
+{
+ /* If the time interval for checking the files has changed, then
+ reset the mail timer. Otherwise, one of the pathname vars
+ to the users mailbox has changed, so rebuild the array of
+ filenames. */
+ if (name[4] == 'C') /* if (strcmp (name, "MAILCHECK") == 0) */
+ reset_mail_timer ();
+ else
+ {
+ free_mail_files ();
+ remember_mail_dates ();
+ }
+}
+
+/* What to do when GLOBIGNORE changes. */
+void
+sv_globignore (name)
+ char *name;
+{
+ setup_glob_ignore (name);
+}
+
+#if defined (READLINE)
+void
+sv_comp_wordbreaks (name)
+ char *name;
+{
+ SHELL_VAR *sv;
+
+ sv = find_variable (name);
+ if (sv == 0)
+ rl_completer_word_break_characters = (char *)NULL;
+}
+
+/* What to do just after one of the TERMxxx variables has changed.
+ If we are an interactive shell, then try to reset the terminal
+ information in readline. */
+void
+sv_terminal (name)
+ char *name;
+{
+ if (interactive_shell && no_line_editing == 0)
+ rl_reset_terminal (get_string_value ("TERM"));
+}
+
+void
+sv_hostfile (name)
+ char *name;
+{
+ SHELL_VAR *v;
+
+ v = find_variable (name);
+ if (v == 0)
+ clear_hostname_list ();
+ else
+ hostname_list_initialized = 0;
+}
+
+#if defined (STRICT_POSIX)
+/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values
+ found in the initial environment) to override the terminal size reported by
+ the kernel. */
+void
+sv_winsize (name)
+ char *name;
+{
+ SHELL_VAR *v;
+ intmax_t xd;
+ int d;
+
+ if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing)
+ return;
+
+ v = find_variable (name);
+ if (v == 0 || var_isnull (v))
+ rl_reset_screen_size ();
+ else
+ {
+ if (legal_number (value_cell (v), &xd) == 0)
+ return;
+ winsize_assignment = winsize_assigned = 1;
+ d = xd; /* truncate */
+ if (name[0] == 'L') /* LINES */
+ rl_set_screen_size (d, -1);
+ else /* COLUMNS */
+ rl_set_screen_size (-1, d);
+ winsize_assignment = 0;
+ }
+}
+#endif /* STRICT_POSIX */
+#endif /* READLINE */
+
+/* Update the value of HOME in the export environment so tilde expansion will
+ work on cygwin. */
+#if defined (__CYGWIN__)
+sv_home (name)
+ char *name;
+{
+ array_needs_making = 1;
+ maybe_make_export_env ();
+}
+#endif
+
+#if defined (HISTORY)
+/* What to do after the HISTSIZE or HISTFILESIZE variables change.
+ If there is a value for this HISTSIZE (and it is numeric), then stifle
+ the history. Otherwise, if there is NO value for this variable,
+ unstifle the history. If name is HISTFILESIZE, and its value is
+ numeric, truncate the history file to hold no more than that many
+ lines. */
+void
+sv_histsize (name)
+ char *name;
+{
+ char *temp;
+ intmax_t num;
+ int hmax;
+
+ temp = get_string_value (name);
+
+ if (temp && *temp)
+ {
+ if (legal_number (temp, &num))
+ {
+ if (name[4] == 'S')
+ {
+ hmax = num;
+ stifle_history (hmax);
+ num = where_history ();
+ if (history_lines_this_session > num)
+ history_lines_this_session = num;
+ }
+ else
+ {
+ history_truncate_file (get_string_value ("HISTFILE"), (int)num);
+ if (num <= history_lines_in_file)
+ history_lines_in_file = num;
+ }
+ }
+ }
+ else if (name[4] == 'S')
+ unstifle_history ();
+}
+
+/* What to do after the HISTIGNORE variable changes. */
+void
+sv_histignore (name)
+ char *name;
+{
+ setup_history_ignore (name);
+}
+
+/* What to do after the HISTCONTROL variable changes. */
+void
+sv_history_control (name)
+ char *name;
+{
+ char *temp;
+ char *val;
+ int tptr;
+
+ history_control = 0;
+ temp = get_string_value (name);
+
+ if (temp == 0 || *temp == 0)
+ return;
+
+ tptr = 0;
+ while (val = extract_colon_unit (temp, &tptr))
+ {
+ if (STREQ (val, "ignorespace"))
+ history_control |= HC_IGNSPACE;
+ else if (STREQ (val, "ignoredups"))
+ history_control |= HC_IGNDUPS;
+ else if (STREQ (val, "ignoreboth"))
+ history_control |= HC_IGNBOTH;
+ else if (STREQ (val, "erasedups"))
+ history_control |= HC_ERASEDUPS;
+
+ free (val);
+ }
+}
+
+#if defined (BANG_HISTORY)
+/* Setting/unsetting of the history expansion character. */
+void
+sv_histchars (name)
+ char *name;
+{
+ char *temp;
+
+ temp = get_string_value (name);
+ if (temp)
+ {
+ history_expansion_char = *temp;
+ if (temp[0] && temp[1])
+ {
+ history_subst_char = temp[1];
+ if (temp[2])
+ history_comment_char = temp[2];
+ }
+ }
+ else
+ {
+ history_expansion_char = '!';
+ history_subst_char = '^';
+ history_comment_char = '#';
+ }
+}
+#endif /* BANG_HISTORY */
+
+void
+sv_histtimefmt (name)
+ char *name;
+{
+ SHELL_VAR *v;
+
+ v = find_variable (name);
+ history_write_timestamps = (v != 0);
+}
+#endif /* HISTORY */
+
+#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+void
+sv_tz (name)
+ char *name;
+{
+ tzset ();
+}
+#endif
+
+/* If the variable exists, then the value of it can be the number
+ of times we actually ignore the EOF. The default is small,
+ (smaller than csh, anyway). */
+void
+sv_ignoreeof (name)
+ char *name;
+{
+ SHELL_VAR *tmp_var;
+ char *temp;
+
+ eof_encountered = 0;
+
+ tmp_var = find_variable (name);
+ ignoreeof = tmp_var != 0;
+ temp = tmp_var ? value_cell (tmp_var) : (char *)NULL;
+ if (temp)
+ eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10;
+ set_shellopts (); /* make sure `ignoreeof' is/is not in $SHELLOPTS */
+}
+
+void
+sv_optind (name)
+ char *name;
+{
+ char *tt;
+ int s;
+
+ tt = get_string_value ("OPTIND");
+ if (tt && *tt)
+ {
+ s = atoi (tt);
+
+ /* According to POSIX, setting OPTIND=1 resets the internal state
+ of getopt (). */
+ if (s < 0 || s == 1)
+ s = 0;
+ }
+ else
+ s = 0;
+ getopts_reset (s);
+}
+
+void
+sv_opterr (name)
+ char *name;
+{
+ char *tt;
+
+ tt = get_string_value ("OPTERR");
+ sh_opterr = (tt && *tt) ? atoi (tt) : 1;
+}
+
+void
+sv_strict_posix (name)
+ char *name;
+{
+ SET_INT_VAR (name, posixly_correct);
+ posix_initialize (posixly_correct);
+#if defined (READLINE)
+ if (interactive_shell)
+ posix_readline_initialize (posixly_correct);
+#endif /* READLINE */
+ set_shellopts (); /* make sure `posix' is/is not in $SHELLOPTS */
+}
+
+void
+sv_locale (name)
+ char *name;
+{
+ char *v;
+
+ v = get_string_value (name);
+ if (name[0] == 'L' && name[1] == 'A') /* LANG */
+ set_lang (name, v);
+ else
+ set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */
+}
+
+#if defined (ARRAY_VARS)
+void
+set_pipestatus_array (ps, nproc)
+ int *ps;
+ int nproc;
+{
+ SHELL_VAR *v;
+ ARRAY *a;
+ ARRAY_ELEMENT *ae;
+ register int i;
+ char *t, tbuf[INT_STRLEN_BOUND(int) + 1];
+
+ v = find_variable ("PIPESTATUS");
+ if (v == 0)
+ v = make_new_array_variable ("PIPESTATUS");
+ if (array_p (v) == 0)
+ return; /* Do nothing if not an array variable. */
+ a = array_cell (v);
+
+ if (a == 0 || array_num_elements (a) == 0)
+ {
+ for (i = 0; i < nproc; i++) /* was ps[i] != -1, not i < nproc */
+ {
+ t = inttostr (ps[i], tbuf, sizeof (tbuf));
+ array_insert (a, i, t);
+ }
+ return;
+ }
+
+ /* Fast case */
+ if (array_num_elements (a) == nproc && nproc == 1)
+ {
+ ae = element_forw (a->head);
+ free (element_value (ae));
+ ae->value = itos (ps[0]);
+ }
+ else if (array_num_elements (a) <= nproc)
+ {
+ /* modify in array_num_elements members in place, then add */
+ ae = a->head;
+ for (i = 0; i < array_num_elements (a); i++)
+ {
+ ae = element_forw (ae);
+ free (element_value (ae));
+ ae->value = itos (ps[i]);
+ }
+ /* add any more */
+ for ( ; i < nproc; i++)
+ {
+ t = inttostr (ps[i], tbuf, sizeof (tbuf));
+ array_insert (a, i, t);
+ }
+ }
+ else
+ {
+ /* deleting elements. it's faster to rebuild the array. */
+ array_flush (a);
+ for (i = 0; ps[i] != -1; i++)
+ {
+ t = inttostr (ps[i], tbuf, sizeof (tbuf));
+ array_insert (a, i, t);
+ }
+ }
+}
+#endif
+
+void
+set_pipestatus_from_exit (s)
+ int s;
+{
+#if defined (ARRAY_VARS)
+ static int v[2] = { 0, -1 };
+
+ v[0] = s;
+ set_pipestatus_array (v, 1);
+#endif
}