/* variables.c -- Functions for hacking shell variables. */
-/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2002 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 <stdio.h>
#include "bashtypes.h"
#include "posixstat.h"
-#include <ctype.h>
-#include <pwd.h>
+#include "posixtime.h"
+
+#if defined (qnx)
+# include <sys/vc.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+#include <stdio.h>
+#include "chartypes.h"
+#include <pwd.h>
#include "bashansi.h"
+
#include "shell.h"
-#include "hash.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')
/* 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, shell_level;
-extern int subshell_environment;
-extern int build_version;
-extern char *dist_version;
+extern int line_number;
+extern int subshell_environment, indirection_level;
+extern int build_version, patch_level;
+extern char *dist_version, *release_status;
extern char *shell_name;
extern char *primary_prompt, *secondary_prompt;
-extern Function *this_shell_builtin;
+extern char *current_host_name;
+extern sh_builtin_func_t *this_shell_builtin;
+extern SHELL_VAR *this_shell_function;
+extern char *this_command_name;
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;
+/* 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. */
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;
-
-/* 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;
+HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;
/* 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;
/* Non-zero means that we have to remake EXPORT_ENV. */
int array_needs_making = 1;
-/* The list of variables that may not be unset in this shell. */
-char **non_unsettable_vars = (char **)NULL;
-
-static char *have_local_variables; /* XXX */
-static int local_variable_stack_size = 0; /* XXX */
+/* The number of times BASH has been executed. This is set
+ by initialize_variables (). */
+int shell_level = 0;
/* Some forward declarations. */
-static void initialize_dynamic_variables ();
-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 *assign_seconds __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_seconds __P((SHELL_VAR *));
+static SHELL_VAR *init_seconds_var __P((void));
-/* 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)
+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 *));
#if defined (HISTORY)
-# include "bashhist.h"
-#endif /* 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 *));
+static SHELL_VAR *init_dirstack_var __P((void));
+#endif
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *get_groupset __P((SHELL_VAR *));
+static SHELL_VAR *init_groups_var __P((void));
+#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));
+
+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 *));
+
+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
-/* Initialize the shell variables from the current environment. */
+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)
+initialize_shell_variables (env, privmode)
char **env;
+ int privmode;
{
- char *name, *string, *current_dir;
- int c, char_index;
- int string_index = 0;
+ 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)
- shell_functions = make_hash_table (0);
+ if (shell_functions == 0)
+ shell_functions = hash_create (0);
- while (string = env[string_index++])
+ for (string_index = 0; string = env[string_index++]; )
{
- int string_length;
-
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 (!privileged_mode && 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))
{
- SHELL_VAR *f;
- char *eval_string;
+ string_length = strlen (string);
+ temp_string = (char *)xmalloc (3 + string_length + char_index);
- eval_string = xmalloc (3 + string_length + strlen (name));
- sprintf (eval_string, "%s %s", name, string);
+ strcpy (temp_string, name);
+ temp_string[char_index] = ' ';
+ strcpy (temp_string + char_index + 1, string);
- parse_and_execute (eval_string, name, 0);
+ parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
- if (name[char_index - 1] == ')')
+ /* 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 (f = find_function (name))
+ if (temp_var = find_function (name))
{
- f->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);
+
+ /* ( */
+ 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] == '[' && xstrchr (string, ')'))
+ {
+ string_length = 1;
+ temp_string = extract_array_assignment_list (string, &string_length);
+ temp_var = assign_array_from_string (name, temp_string);
+ FREE (temp_string);
+ VSETATTR (temp_var, (att_exported | att_imported));
+ array_needs_making = 1;
}
+# endif
+#endif
else
{
- SHELL_VAR *v;
-
- v = bind_variable (name, string);
- v->attributes |= (att_exported | att_imported);
+ temp_var = bind_variable (name, string);
+ 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 getwd () 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) &&
- (current_dir = value_cell (temp_var)) &&
- same_file (current_dir, ".", (struct stat *)NULL, (struct stat *)NULL))
- set_working_directory (current_dir);
- else
- {
- current_dir = get_working_directory ("shell-init");
- if (current_dir)
- {
- bind_variable ("PWD", current_dir);
- free (current_dir);
- }
+ 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 */
+ {
+ CACHE_IMPORTSTR (temp_var, name);
+ }
}
+ set_pwd ();
+
+ /* Set up initial value of $_ */
+ temp_var = bind_variable ("_", dollar_vars[0]);
+
/* 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];
+ qnx_nidtostr (getnid (), node_name, sizeof (node_name));
+ temp_var = bind_variable ("NODE", node_name);
+ 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", "+ ");
-#if defined (INSECURITY)
- set_if_not ("IFS", " \t\n");
-#else
- bind_variable ("IFS", " \t\n");
-#endif /* INSECURITY */
+ /* Don't allow IFS to be imported from the environment. */
+ temp_var = bind_variable ("IFS", " \t\n");
+ setifs (temp_var);
/* Magic machine types. Pretty convenient. */
- temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
- set_auto_export (temp_var);
- temp_var = set_if_not ("OSTYPE", OSTYPE);
- set_auto_export (temp_var);
+ set_machine_vars ();
- /* Default MAILCHECK for interactive shells. */
+ /* 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 MAILPATH is not set, and we should provide a
+ default only if neither is set. */
if (interactive_shell)
- set_if_not ("MAILCHECK", "60");
+ set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
/* Do some things with shell level. */
- temp_var = set_if_not ("SHLVL", "0");
- set_auto_export (temp_var);
- adjust_shell_level (1);
-
- /* Make a variable $PPID, which holds the pid of the shell's parent. */
- {
- char *ppid;
- SHELL_VAR *v;
+ initialize_shell_level ();
- ppid = itos ((int) getppid ());
- v = find_variable ("PPID");
+ set_ppid ();
- if (v)
- v->attributes &= ~(att_readonly | att_exported);
-
- v = bind_variable ("PPID", ppid);
- v->attributes |= (att_readonly | att_integer);
-
- non_unsettable ("PPID");
- free (ppid);
- }
-
-#if defined (GETOPTS_BUILTIN)
/* Initialize the `getopts' stuff. */
bind_variable ("OPTIND", "1");
+ getopts_reset (0);
bind_variable ("OPTERR", "1");
-#endif /* GETOPTS_BUILTIN */
+ sh_opterr = 1;
+
+ if (login_shell == 1)
+ set_home_var ();
/* Get the full pathname to THIS shell, and set the BASH variable
to it. */
- {
- char *tname;
-
- 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
- {
- 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);
- free (tname);
- }
- else
- name = tname;
- }
- else
- name = savestring (current_user.shell);
- }
- else
- {
- name = full_pathname (tname);
- free (tname);
- }
- }
-
- /* 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);
-
- /* Make a variable called BASH, which is the name of THIS shell. */
- temp_var = bind_variable ("BASH", name);
+ name = get_bash_name ();
+ temp_var = bind_variable ("BASH", name);
+ free (name);
- 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. */
+ set_shell_var ();
/* Make a variable called BASH_VERSION which contains the version info. */
bind_variable ("BASH_VERSION", shell_version_string ());
+#if defined (ARRAY_VARS)
+ make_vers_array ();
+#endif
/* Find out if we're supposed to be in Posix.2 mode via an
environment variable. */
that we are remembering commands on the history list. */
if (remember_on_history)
{
- if (posixly_correct)
- name = tilde_expand ("~/.sh_history");
- else
- name = tilde_expand ("~/.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);
+ sbrand (dollar_dollar_pid + shell_start_time);
/* Handle some "special" variables that we may have inherited from a
parent shell. */
-
- noclobber = find_variable ("noclobber") != (SHELL_VAR *)NULL;
-
- 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)
{
- sv_command_oriented_history ("command_oriented_history");
- if (find_variable ("history_control"))
- sv_history_control ("history_control"); /* gone in next release */
- else
- sv_history_control ("HISTCONTROL");
+ sv_history_control ("HISTCONTROL");
+ sv_histignore ("HISTIGNORE");
}
#endif /* HISTORY */
+ /*
+ * 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 the dynamic variables, and seed their values. */
initialize_dynamic_variables ();
+}
- non_unsettable ("PATH");
- non_unsettable ("IFS");
+/* **************************************************************** */
+/* */
+/* Setting values for special shell variables */
+/* */
+/* **************************************************************** */
- if (interactive_shell)
+static void
+set_machine_vars ()
+{
+ SHELL_VAR *temp_var;
+
+ temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
+ temp_var = set_if_not ("OSTYPE", OSTYPE);
+ temp_var = set_if_not ("MACHTYPE", MACHTYPE);
+
+ temp_var = set_if_not ("HOSTNAME", current_host_name);
+}
+
+/* Set $HOME to the information in the password file if we didn't get
+ it from the environment. */
+
+/* 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;
+}
+
+static void
+set_home_var ()
+{
+ SHELL_VAR *temp_var;
+
+ temp_var = find_variable ("HOME");
+ if (temp_var == 0)
+ temp_var = bind_variable ("HOME", sh_get_home_dir ());
+#if 0
+ VSETATTR (temp_var, att_exported);
+#endif
+}
+
+/* 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 *temp_var;
+
+ 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);
+ }
+#if 0
+ VSETATTR (temp_var, att_exported);
+#endif
+}
+
+static char *
+get_bash_name ()
+{
+ char *name;
+
+ 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] == '/')
{
- non_unsettable ("PS1");
- non_unsettable ("PS2");
+ /* Fast path for common case. */
+ char *cdir;
+ int len;
+
+ cdir = get_string_value ("PWD");
+ if (cdir)
+ {
+ 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);
}
+ else
+ {
+ char *tname;
+ int s;
- /* Get the users real user id, and save that in a readonly variable.
- To make the variable *really* readonly, we have added it to a special
- list of vars. */
+ tname = find_user_command (shell_name);
- sv_uids ();
- set_var_read_only ("UID");
- set_var_read_only ("EUID");
+ 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);
+ }
+ }
- non_unsettable ("EUID");
- non_unsettable ("UID");
+ return (name);
}
void
adjust_shell_level (change)
int change;
{
- char *new_level, *old_SHLVL;
- int old_level;
+ char new_level[5], *old_SHLVL;
+ intmax_t old_level;
+ SHELL_VAR *temp_var;
old_SHLVL = get_string_value ("SHLVL");
- if (old_SHLVL)
- old_level = atoi (old_SHLVL);
- else
+ if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0)
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);
+ 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);
+ set_auto_export (temp_var);
+}
+
+static void
+initialize_shell_level ()
+{
+ adjust_shell_level (1);
}
-/* Add NAME to the list of variables that cannot be unset
- if it isn't already there. */
+/* 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
-non_unsettable (name)
- char *name;
+set_pwd ()
{
- register int i;
+ 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;
- if (!non_unsettable_vars)
+ 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))
{
- non_unsettable_vars = (char **)xmalloc (1 * sizeof (char *));
- non_unsettable_vars[0] = (char *)NULL;
+ set_working_directory (home_string);
+ temp_var = bind_variable ("PWD", home_string);
+ set_auto_export (temp_var);
+ }
+ else
+ {
+ temp_string = get_working_directory ("shell-init");
+ if (temp_string)
+ {
+ temp_var = bind_variable ("PWD", temp_string);
+ set_auto_export (temp_var);
+ free (temp_string);
+ }
}
- for (i = 0; non_unsettable_vars[i]; i++)
- if (STREQ (non_unsettable_vars[i], name))
- return;
-
- non_unsettable_vars = (char **)
- xrealloc (non_unsettable_vars, (2 + i) * sizeof (char *));
- non_unsettable_vars[i] = savestring (name);
- non_unsettable_vars[i + 1] = (char *)NULL;
+ /* 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);
+ VSETATTR (temp_var, (att_exported | att_invisible));
}
-/* Set NAME to VALUE if NAME has no value. */
-SHELL_VAR *
-set_if_not (name, value)
- char *name, *value;
+/* Make a variable $PPID, which holds the pid of the shell's parent. */
+void
+set_ppid ()
{
- SHELL_VAR *v = find_variable (name);
+ char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
+ SHELL_VAR *temp_var;
- if (!v)
- v = bind_variable (name, value);
- return (v);
+ 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);
+ VSETATTR (temp_var, (att_readonly | att_integer));
}
-/* 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 void
+uidset ()
{
- register int i;
- register BUCKET_CONTENTS *tlist;
- SHELL_VAR *var, **list = (SHELL_VAR **)NULL;
- int list_index = 0, list_size = 0;
+ char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
+ register SHELL_VAR *v;
- for (i = 0; i < var_hash_table->nbuckets; i++)
+ b = inttostr (current_user.uid, buff, sizeof (buff));
+ v = find_variable ("UID");
+ if (v == 0)
{
- tlist = get_hash_bucket (i, var_hash_table);
-
- while (tlist)
- {
- var = (SHELL_VAR *)tlist->data;
+ v = bind_variable ("UID", b);
+ VSETATTR (v, (att_readonly | att_integer));
+ }
- if (!function || (*function) (var))
- {
- if (list_index + 1 >= list_size)
- list = (SHELL_VAR **)
- xrealloc (list, (list_size += 20) * sizeof (SHELL_VAR *));
+ if (current_user.euid != current_user.uid)
+ b = inttostr (current_user.euid, buff, sizeof (buff));
- list[list_index++] = var;
- list[list_index] = (SHELL_VAR *)NULL;
- }
- tlist = tlist->next;
- }
+ v = find_variable ("EUID");
+ if (v == 0)
+ {
+ v = bind_variable ("EUID", b);
+ VSETATTR (v, (att_readonly | att_integer));
}
- return (list);
}
-void
-sort_variables (array)
- SHELL_VAR **array;
+#if defined (ARRAY_VARS)
+static void
+make_vers_array ()
{
- qsort (array, array_len ((char **)array), sizeof (SHELL_VAR *), qsort_var_comp);
+ 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 */
-static int
-qsort_var_comp (var1, var2)
- SHELL_VAR **var1, **var2;
+/* 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;
{
- int result;
+ char val[INT_STRLEN_BOUND(int) + 1], *v;
- if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
- result = strcmp ((*var1)->name, (*var2)->name);
+ v = inttostr (lines, val, sizeof (val));
+ bind_variable ("LINES", v);
- return (result);
+ v = inttostr (cols, val, sizeof (val));
+ bind_variable ("COLUMNS", v);
}
-/* Create a NULL terminated array of all the shell variables in TABLE. */
-static SHELL_VAR **
-all_vars (table)
- HASH_TABLE *table;
-{
- SHELL_VAR **list;
-
- list = map_over ((Function *)NULL, table);
- if (list)
- sort_variables (list);
- return (list);
-}
-
-/* Create a NULL terminated array of all the shell variables. */
-SHELL_VAR **
-all_shell_variables ()
-{
- return (all_vars (shell_variables));
-}
-
-/* Create a NULL terminated array of all the shell functions. */
-SHELL_VAR **
-all_shell_functions ()
-{
- return (all_vars (shell_functions));
-}
+/* **************************************************************** */
+/* */
+/* Printing variables and values */
+/* */
+/* **************************************************************** */
-/* Print VARS to stdout in such a way that they can be read back in. */
+/* 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_vars_no_values (list)
+/* Print LIST (a list of shell functions) to stdout in such a way that
+ they can be read back in. */
+void
+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");
}
- else if (var->value)
+#if defined (ARRAY_VARS)
+ else if (array_p (var))
+ print_array_assignment (var, 0);
+#endif /* ARRAY_VARS */
+ else
{
printf ("%s=", var->name);
- print_var_value (var);
+ print_var_value (var, 1);
printf ("\n");
}
}
/* Print the value cell of VAR, a shell variable. Do not print
- the name, nor leading/trailing newline. */
+ the name, nor leading/trailing newline. If QUOTE is non-zero,
+ and the value contains shell metacharacters, quote the value
+ in such a way that it can be read back in. */
void
-print_var_value (var)
+print_var_value (var, quote)
SHELL_VAR *var;
+ int quote;
{
- if (var->value)
- printf ("%s", var->value);
+ char *t;
+
+ if (var_isset (var) == 0)
+ return;
+
+ if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var)))
+ {
+ 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));
}
/* **************************************************************** */
-/* */
-/* Dynamic Variable Extension */
-/* */
+/* */
+/* Dynamic Variables */
+/* */
/* **************************************************************** */
/* 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).
-
- 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);
-
+ is usually ENTRY (self). IND is an index for an array variable, and
+ unused otherwise.
+
+ 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?
-
+
Be warned, though: if you `unset' a special variable, it loses its
special meaning, even if you subsequently set it.
-
+
The special assignment code would probably have been better put in
- subst.c: do_assignment, in the same style as
+ 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)); \
+ 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
+
/* The value of $SECONDS. This is the number of seconds since shell
invocation, or, the number of seconds since the last assignment + the
value of the last assignment. */
-static long seconds_value_assigned = (long)0;
+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 = atol (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 int last_random_value;
-/* 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;
{
- int s = atoi (value);
-
- sbrand (s);
+ sbrand (strtoul (value, (char **)NULL, 10));
return (self);
}
+int
+get_random_number ()
+{
+ int rv;
+
+ /* Reset for command and process substitution. */
+ if (subshell_environment)
+ sbrand (rseed + getpid() + NOW);
+
+ do
+ rv = brand ();
+ while (rv == last_random_value);
+ return rv;
+}
+
static SHELL_VAR *
get_random (var)
SHELL_VAR *var;
int rv;
char *p;
- rv = brand ();
- p = itos ((int)rv);
+ rv = get_random_number ();
+ last_random_value = 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)
SHELL_VAR *var;
{
char *p;
+ int ln;
- p = itos (line_number);
- FREE (var->value);
- var->value = p;
+ ln = executing_line_number ();
+ p = itos (ln);
+ FREE (value_cell (var));
+ var_setvalue (var, p);
return (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 (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;
+{
+ ARRAY *a;
+ WORD_LIST *l;
+
+ l = get_directory_stack ();
+ a = array_from_word_list (l);
+ array_dispose (array_cell (self));
+ dispose_words (l);
+ var_setarray (self, a);
+ return self;
+}
+
+static SHELL_VAR *
+init_dirstack_var ()
+{
+ SHELL_VAR *v;
+
+ v = find_variable ("DIRSTACK");
+ if (v)
+ return v;
+ INIT_DYNAMIC_ARRAY_VAR ("DIRSTACK", get_dirstack, assign_dirstack);
+ return v;
+}
+#endif /* PUSHD AND POPD && ARRAY_VARS */
+
+#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;
+{
+ 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);
+}
+
+static SHELL_VAR *
+init_groups_var ()
+{
+ SHELL_VAR *v;
+
+ v = find_variable ("GROUPS");
+ if (v)
+ return (v);
+ INIT_DYNAMIC_ARRAY_VAR ("GROUPS", get_groupset, null_array_assign);
+ VSETATTR (v, att_noassign);
+ return v;
+}
+#endif /* ARRAY_VARS */
+
+static SHELL_VAR *
+get_funcname (self)
+ SHELL_VAR *self;
+{
+ char *t;
+ if (variable_context && this_shell_function)
+ {
+ FREE (value_cell (self));
+ t = savestring (this_shell_function->name);
+ var_setvalue (self, t);
+ }
+ 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;
+ INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
+ VSETATTR (v, att_invisible|att_noassign);
+ return v;
+}
+
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;
-
- v = bind_variable ("LINENO", (char *)NULL);
- v->dynamic_value = get_lineno;
- v->assign_func = (DYNAMIC_FUNC *)NULL;
+ INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
+ INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
#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);
+#endif
+
+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
+ v = init_dirstack_var ();
+#endif /* PUSHD_AND_POPD && ARRAY_VARS */
+
+#if defined (ARRAY_VARS)
+ v = init_groups_var ();
#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;
- if (bucket)
- return ((SHELL_VAR *)bucket->data);
- else
- return ((SHELL_VAR *)NULL);
+ 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;
+ const char *name;
int search_tempenv;
{
- SHELL_VAR *var = (SHELL_VAR *)NULL;
+ SHELL_VAR *var;
+
+ 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);
+ if ((search_tempenv || subshell_environment) && temporary_env)
+ var = hash_lookup (name, temporary_env);
- if (!var)
+ if (var == 0)
var = var_lookup (name, shell_variables);
- if (!var)
+ if (var == 0)
return ((SHELL_VAR *)NULL);
- if (var->dynamic_value)
- return ((*(var->dynamic_value)) (var));
- else
- return (var);
+ 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, 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 (hash_lookup (name, shell_functions));
+}
+
+/* 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;
{
- return (var_lookup (name, shell_functions));
+ 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 (value_cell (var));
}
/* 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. */
+ 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)
- char *var_name;
+ const char *var_name;
{
- SHELL_VAR *var = find_variable (var_name);
+ SHELL_VAR *var;
- if (!var)
- return (char *)NULL;
- else
- return (var->value);
+ 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);
+ 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)
+ if (old_var && local_p (old_var) && old_var->context == variable_context)
return (old_var);
- elt = remove_hash_item (name, shell_variables);
- if (elt)
+ 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)
{
- old_var = (SHELL_VAR *)elt->data;
- free (elt->key);
- free (elt);
+ internal_error ("make_local_variable: no function context at current scope found");
+ return ((SHELL_VAR *)NULL);
}
- 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)
+ 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 = bind_variable (name, "");
+ if (readonly_p (old_var))
+ sh_readonly (name);
+ return ((SHELL_VAR *)NULL);
}
+
+ if (old_var == 0)
+ new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH);
else
{
- new_var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-
- new_var->name = savestring (name);
- new_var->value = savestring ("");
-
- new_var->dynamic_value = (DYNAMIC_FUNC *)NULL;
- new_var->assign_func = (DYNAMIC_FUNC *)NULL;
+ new_var = make_new_variable (name, vc->table);
- new_var->attributes = 0;
+ /* 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));
- if (exported_p (old_var))
- new_var->attributes |= att_exported;
-
- new_var->prev_context = old_var;
- elt = add_hash_item (savestring (name), shell_variables);
- elt->data = (char *)new_var;
+ new_var->attributes = exported_p (old_var) ? att_exported : 0;
}
+ vc->flags |= VC_HASLOCAL;
+
new_var->context = variable_context;
+ VSETATTR (new_var, att_local);
- /* XXX */
- if (local_variable_stack_size <= variable_context)
- {
- int old_size = local_variable_stack_size;
- while (local_variable_stack_size <= variable_context)
- local_variable_stack_size += 8;
- have_local_variables =
- xrealloc (have_local_variables, local_variable_stack_size);
- 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);
}
-/* Bind a variable NAME to VALUE. This conses up the name
- and value strings. */
+#if defined (ARRAY_VARS)
SHELL_VAR *
-bind_variable (name, value)
- char *name, *value;
+make_local_array_variable (name)
+ char *name;
{
- SHELL_VAR *entry = var_lookup (name, shell_variables);
- BUCKET_CONTENTS *elt;
-
- if (!entry)
- {
- /* Make a new entry for this variable. Then do the binding. */
- entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-
- entry->attributes = 0;
- entry->name = savestring (name);
+ SHELL_VAR *var;
+ ARRAY *array;
- if (value)
- {
- if (*value)
- entry->value = savestring (value);
- else
- {
- entry->value = xmalloc (1);
- entry->value[0] = '\0';
- }
- }
- else
- entry->value = (char *)NULL;
+ var = make_local_variable (name);
+ if (var == 0)
+ return var;
+ array = array_create ();
- entry->dynamic_value = (DYNAMIC_FUNC *)NULL;
- entry->assign_func = (DYNAMIC_FUNC *)NULL;
+ FREE (value_cell(var));
+ var_setarray (var, array);
+ VSETATTR (var, att_array);
+ return var;
+}
+#endif /* ARRAY_VARS */
- /* 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;
+/* Create a new shell variable with name NAME. */
+static SHELL_VAR *
+new_shell_variable (name)
+ const char *name;
+{
+ SHELL_VAR *entry;
- elt = add_hash_item (savestring (name), shell_variables);
- elt->data = (char *)entry;
- }
- else if (entry->assign_func)
- return ((*(entry->assign_func)) (entry, value));
- else
- {
- if (readonly_p (entry))
- {
- report_error ("%s: read-only variable", name);
- return (entry);
- }
+ entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
- /* Variables which are bound are visible. */
- entry->attributes &= ~att_invisible;
-
- /* If this variable has had its type set to integer (via `declare -i'),
- then do expression evaluation on it and store the result. The
- functions in expr.c (evalexp and bind_int_variable) are responsible
- for turning off the integer flag if they don't want further
- evaluation done. */
- if (integer_p (entry))
- {
- long val;
-
- val = evalexp (value);
- /* We cannot free () entry->value before this; what if the string
- we are working is `even=even+2'? We need the original value
- around while we are doing the evaluation to handle any possible
- recursion. */
- FREE (entry->value);
- entry->value = itos (val);
- }
- else
- {
- FREE (entry->value);
+ entry->name = savestring (name);
+ var_setvalue (entry, (char *)NULL);
+ CLEAR_EXPORTSTR (entry);
- if (value)
- {
- if (*value)
- entry->value = savestring (value);
- else
- {
- entry->value = xmalloc (1);
- entry->value[0] = '\0';
- }
- }
- else
- entry->value = (char *)NULL;
- }
- }
+ entry->dynamic_value = (sh_var_value_func_t *)NULL;
+ entry->assign_func = (sh_var_assign_func_t *)NULL;
- if (mark_modified_vars)
- entry->attributes |= att_exported;
+ entry->attributes = 0;
- if (exported_p (entry))
- array_needs_making = 1;
+ /* Always assume variables are to be made at toplevel!
+ make_local_variable has the responsibilty of changing the
+ variable context. */
+ entry->context = 0;
return (entry);
}
-/* Dispose of the information attached to VAR. */
-void
-dispose_variable (var)
- SHELL_VAR *var;
+/* 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;
{
- if (!var)
- return;
+ SHELL_VAR *entry;
+ BUCKET_CONTENTS *elt;
- if (function_p (var))
- dispose_command ((COMMAND *)var->value);
- else if (var->value)
- free (var->value);
+ entry = new_shell_variable (name);
- free (var->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);
+ }
- if (exported_p (var))
- array_needs_making = 1;
+ elt = hash_insert (savestring (name), table, HASH_NOSRCH);
+ elt->data = (PTR_T)entry;
- free (var);
+ return entry;
}
-/* Unset the variable referenced by NAME. */
-unbind_variable (name)
+#if defined (ARRAY_VARS)
+SHELL_VAR *
+make_new_array_variable (name)
char *name;
{
- SHELL_VAR *var = find_variable (name);
-
- if (!var)
- return (-1);
+ SHELL_VAR *entry;
+ ARRAY *array;
+
+ entry = make_new_variable (name, global_variables->table);
+ array = array_create ();
+ var_setarray (entry, array);
+ VSETATTR (entry, att_array);
+ return entry;
+}
+#endif
- if (var->value)
+char *
+make_variable_value (var, value)
+ SHELL_VAR *var;
+ char *value;
+{
+ char *retval;
+ intmax_t lval;
+ int expok;
+
+ /* If this variable has had its type set to integer (via `declare -i'),
+ then do expression evaluation on it and store the result. The
+ functions in expr.c (evalexp()) and bind_int_variable() are responsible
+ for turning off the integer flag if they don't want further
+ evaluation done. */
+ if (integer_p (var))
{
- free (var->value);
- var->value = (char *)NULL;
+ lval = evalexp (value, &expok);
+ if (expok == 0)
+ jump_to_top_level (DISCARD);
+ retval = itos (lval);
}
+ else if (value)
+ {
+ if (*value)
+ retval = savestring (value);
+ else
+ {
+ retval = (char *)xmalloc (1);
+ retval[0] = '\0';
+ }
+ }
+ else
+ retval = (char *)NULL;
- makunbound (name, shell_variables);
-
- return (0);
+ return retval;
}
-/* Make the variable associated with NAME go away. HASH_LIST is the
- hash table from which this variable should be deleted (either
- shell_variables or shell_functions).
- Returns non-zero if the variable couldn't be found. */
-makunbound (name, hash_list)
- char *name;
- HASH_TABLE *hash_list;
+/* 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)
+ const char *name;
+ char *value;
+ HASH_TABLE *table;
+ int hflags;
{
- BUCKET_CONTENTS *elt;
- SHELL_VAR *old_var, *new_var;
- char *t;
-
- elt = remove_hash_item (name, hash_list);
+ char *newval;
+ SHELL_VAR *entry;
- if (!elt)
- return (-1);
+ entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
- old_var = (SHELL_VAR *)elt->data;
- new_var = old_var->prev_context;
+ if (entry == 0)
+ {
+ entry = make_new_variable (name, table);
+ var_setvalue (entry, make_variable_value (entry, value));
+ }
+ else if (entry->assign_func) /* array vars have assign functions now */
+ {
+ INVALIDATE_EXPORTSTR (entry);
+ return ((*(entry->assign_func)) (entry, value, -1));
+ }
+ else
+ {
+ if (readonly_p (entry) || noassign_p (entry))
+ {
+ if (readonly_p (entry))
+ err_readonly (name);
+ return (entry);
+ }
- if (old_var && exported_p (old_var))
- array_needs_making++;
+ /* Variables which are bound are visible. */
+ VUNSETATTR (entry, att_invisible);
- if (new_var)
- {
- /* Has to be a variable, functions don't have previous contexts. */
- BUCKET_CONTENTS *new_elt;
+ newval = make_variable_value (entry, value);
- new_elt = add_hash_item (savestring (new_var->name), hash_list);
- new_elt->data = (char *)new_var;
+ /* Invalidate any cached export string */
+ INVALIDATE_EXPORTSTR (entry);
- if (exported_p (new_var))
- set_var_auto_export (new_var->name);
+#if defined (ARRAY_VARS)
+ /* XXX -- this bears looking at again -- XXX */
+ /* If an existing array variable x is being assigned to with x=b or
+ `read x' or something of that nature, silently convert it to
+ x[0]=b or `read x[0]'. */
+ if (array_p (entry))
+ {
+ array_insert (array_cell (entry), 0, newval);
+ free (newval);
+ }
+ else
+#endif
+ {
+ FREE (value_cell (entry));
+ var_setvalue (entry, newval);
+ }
}
- /* Have to save a copy of name here, because it might refer to
- old_var->name. If so, stupidly_hack_special_variables will
- reference freed memory. */
- t = savestring (name);
+ if (mark_modified_vars)
+ VSETATTR (entry, att_exported);
- free (elt->key);
- free (elt);
+ if (exported_p (entry))
+ array_needs_making = 1;
- dispose_variable (old_var);
- stupidly_hack_special_variables (t);
- free (t);
- return (0);
+ 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. */
-/* Remove the variable with NAME if it is a local variable in the
- current context. */
-kill_local_variable (name)
- char *name;
+SHELL_VAR *
+bind_variable (name, value)
+ const char *name;
+ char *value;
{
- SHELL_VAR *temp = find_variable (name);
+ SHELL_VAR *v;
+ VAR_CONTEXT *vc;
- if (temp && (temp->context == variable_context))
+ if (shell_variables == 0)
{
- makunbound (name, shell_variables);
- return (0);
+ shell_variables = global_variables = new_var_context ((char *)NULL, 0);
+ shell_variables->scope = 0;
+ shell_variables->table = hash_create (0);
}
- return (-1);
-}
-/* Get rid of all of the variables in the current context. */
-int
-variable_in_context (var)
- SHELL_VAR *var;
-{
- return (var && var->context == variable_context);
+ /* 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);
+
+ /* 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));
+ }
+ }
+ return (bind_variable_internal (name, value, global_variables->table, 0));
}
-void
-kill_all_local_variables ()
+/* 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_variable_value (var, value)
+ SHELL_VAR *var;
+ char *value;
{
- register int i, pass;
- register SHELL_VAR *var, **list;
- HASH_TABLE *varlist;
+ char *t;
- /* XXX */
- if (!have_local_variables || have_local_variables[variable_context] == 0)
- return;
+ VUNSETATTR (var, att_invisible);
- for (pass = 0; pass < 2; pass++)
- {
- varlist = pass ? shell_functions : shell_variables;
+ t = make_variable_value (var, value);
+ FREE (value_cell (var));
+ var_setvalue (var, t);
- list = map_over (variable_in_context, varlist);
+ INVALIDATE_EXPORTSTR (var);
- if (list)
- {
- for (i = 0; var = list[i]; i++)
- makunbound (var->name, varlist);
+ if (mark_modified_vars)
+ VSETATTR (var, att_exported);
- free (list);
- }
- }
+ if (exported_p (var))
+ array_needs_making = 1;
- have_local_variables[variable_context] = 0; /* XXX */
+ return (var);
}
-/* Delete the entire contents of the hash table. */
-void
-delete_all_variables (hashed_vars)
- HASH_TABLE *hashed_vars;
-{
- register int i;
- register BUCKET_CONTENTS *bucket;
+/* Bind/create a shell variable with the name LHS to the RHS.
+ This creates or modifies a variable such that it is an integer.
- for (i = 0; i < hashed_vars->nbuckets; i++)
- {
- bucket = hashed_vars->bucket_array[i];
+ 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. */
- while (bucket)
- {
- BUCKET_CONTENTS *temp = bucket;
- SHELL_VAR *var, *prev;
+SHELL_VAR *
+bind_int_variable (lhs, rhs)
+ char *lhs, *rhs;
+{
+ register SHELL_VAR *v;
+ char *t;
+ int isint, isarr;
- bucket = bucket->next;
+ isint = isarr = 0;
+#if defined (ARRAY_VARS)
+ if (t = xstrchr (lhs, '[')) /*]*/
+ {
+ isarr = 1;
+ v = array_variable_part (lhs, (char **)0, (int *)0);
+ }
+ else
+#endif
+ v = find_variable (lhs);
- var = (SHELL_VAR *)temp->data;
+ if (v)
+ {
+ isint = integer_p (v);
+ VUNSETATTR (v, att_integer);
+ }
- while (var)
- {
- prev = var->prev_context;
- dispose_variable (var);
+#if defined (ARRAY_VARS)
+ if (isarr)
+ v = assign_array_element (lhs, rhs);
+ else
+#endif
+ v = bind_variable (lhs, rhs);
- var = prev;
- }
+ if (isint)
+ VSETATTR (v, att_integer);
- free (temp->key);
- free (temp);
- }
- hashed_vars->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
- }
+ return (v);
}
-static SHELL_VAR *
-new_shell_variable (name)
- char *name;
+SHELL_VAR *
+bind_var_to_int (var, val)
+ char *var;
+ intmax_t val;
{
- SHELL_VAR *var;
-
- var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+ char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
- bzero ((char *)var, sizeof (SHELL_VAR));
- var->name = savestring (name);
- return (var);
+ p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
+ return (bind_int_variable (var, p));
}
/* 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;
+ const char *name;
COMMAND *value;
{
- SHELL_VAR *entry = find_function (name);
+ SHELL_VAR *entry;
- if (!entry)
+ entry = find_function (name);
+ if (entry == 0)
{
BUCKET_CONTENTS *elt;
- elt = add_hash_item (savestring (name), shell_functions);
+ elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
+ entry = new_shell_variable (name);
+ elt->data = (PTR_T)entry;
+ }
+ else
+ 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);
+}
+
+/* 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 (string)
+ const char *string;
+{
+ int offset;
+ char *name, *temp, *value;
+ SHELL_VAR *var;
+
+ offset = assignment (string);
+ name = savestring (string);
+ value = (char *)NULL;
+
+ if (name[offset] == '=')
+ {
+ name[offset] = 0;
+
+ var = find_variable (name);
+ if (var && (readonly_p (var) || noassign_p (var)))
+ {
+ if (readonly_p (var))
+ err_readonly (name);
+ free (name);
+ return (0);
+ }
- elt->data = (char *)new_shell_variable (name);
- entry = (SHELL_VAR *)elt->data;
- entry->dynamic_value = (DYNAMIC_FUNC *)NULL;
- entry->assign_func = (DYNAMIC_FUNC *)NULL;
+ temp = name + offset + 1;
+ temp = (xstrchr (temp, '~') != 0) ? bash_tilde_expand (temp, 1) : savestring (temp);
- /* Functions are always made at the top level. This allows a
- function to define another function (like autoload). */
- entry->context = 0;
+ value = expand_string_unsplit_to_string (temp, 0);
+ free (temp);
}
- if (entry->value)
- dispose_command ((COMMAND *)entry->value);
+ if (temporary_env == 0)
+ temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
- if (value) /* I don't think this can happen anymore */
- entry->value = (char *)copy_command (value);
+ var = hash_lookup (name, temporary_env);
+ if (var == 0)
+ var = make_new_variable (name, temporary_env);
else
- entry->value = (char *)NULL;
+ FREE (value_cell (var));
- entry->attributes |= att_function;
+ if (value == 0)
+ {
+ value = (char *)xmalloc (1); /* like do_assignment_internal */
+ value[0] = '\0';
+ }
- if (mark_modified_vars)
- entry->attributes |= att_exported;
+ var_setvalue (var, value);
+ var->attributes |= (att_exported|att_tempvar);
+ var->context = variable_context; /* XXX */
- entry->attributes &= ~att_invisible; /* Just to be sure */
+ INVALIDATE_EXPORTSTR (var);
+ var->exportstr = mk_env_string (name, value);
array_needs_making = 1;
- return (entry);
+ if (ifsname (name))
+ setifs (var);
+
+ if (echo_command_at_execute)
+ {
+ /* The Korn shell prints the `+ ' in front of assignment statements,
+ so we do too. */
+ fprintf (stderr, "%s%s=%s\n", indirection_level_string (), name, value);
+ fflush (stderr);
+ }
+
+ return 1;
}
+/* **************************************************************** */
+/* */
+/* Copying variables */
+/* */
+/* **************************************************************** */
+
+#ifdef INCLUDE_UNUSED
/* Copy VAR to a new data structure and return that structure. */
SHELL_VAR *
copy_variable (var)
copy->name = savestring (var->name);
if (function_p (var))
- copy->value = (char *)copy_command ((COMMAND *)var->value);
- else if (var->value)
- copy->value = savestring (var->value);
+ 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
- copy->value = (char *)NULL;
+ var_setvalue (copy, (char *)NULL);
copy->dynamic_value = var->dynamic_value;
copy->assign_func = var->assign_func;
- copy->context = var->context;
+ copy->exportstr = COPY_EXPORTSTR (var);
- /* Don't bother copying previous contexts along with this variable. */
- copy->prev_context = (SHELL_VAR *)NULL;
+ copy->context = var->context;
}
return (copy);
}
+#endif
-/* Make the variable associated with NAME be read-only.
- If NAME does not exist yet, create it. */
+/* **************************************************************** */
+/* */
+/* Deleting and unsetting variables */
+/* */
+/* **************************************************************** */
+
+/* Dispose of the information attached to VAR. */
void
-set_var_read_only (name)
- char *name;
+dispose_variable (var)
+ SHELL_VAR *var;
+{
+ if (var == 0)
+ return;
+
+ if (function_p (var))
+ dispose_command (function_cell (var));
+#if defined (ARRAY_VARS)
+ else if (array_p (var))
+ array_dispose (array_cell (var));
+#endif
+ else
+ FREE (value_cell (var));
+
+ FREE_EXPORTSTR (var);
+
+ free (var->name);
+
+ if (exported_p (var))
+ array_needs_making = 1;
+
+ free (var);
+}
+
+/* Unset the shell variable referenced by NAME. */
+int
+unbind_variable (name)
+ const char *name;
+{
+ return makunbound (name, shell_variables);
+}
+
+/* Unset the shell function named NAME. */
+int
+unbind_func (name)
+ const char *name;
{
- SHELL_VAR *entry = find_variable (name);
+ BUCKET_CONTENTS *elt;
+ SHELL_VAR *func;
- if (!entry)
+ 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)
{
- entry = bind_variable (name, "");
- if (!no_invisible_vars)
- entry->attributes |= att_invisible;
+ if (exported_p (func))
+ array_needs_making++;
+ dispose_variable (func);
}
- entry->attributes |= att_readonly;
+
+ free (elt->key);
+ free (elt);
+
+ return 0;
}
-/* Make the function associated with NAME be read-only.
- If NAME does not exist, we just punt, like auto_export code below. */
-void
-set_func_read_only (name)
- char *name;
+/* Make the variable associated with NAME go away. HASH_LIST is the
+ hash table from which this variable should be deleted (either
+ shell_variables or shell_functions).
+ Returns non-zero if the variable couldn't be found. */
+int
+makunbound (name, vc)
+ const char *name;
+ VAR_CONTEXT *vc;
{
- SHELL_VAR *entry = find_function (name);
+ BUCKET_CONTENTS *elt, *new_elt;
+ SHELL_VAR *old_var;
+ VAR_CONTEXT *v;
+ char *t;
- if (entry)
- entry->attributes |= att_readonly;
-}
+ for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
+ if (elt = hash_remove (name, v->table, 0))
+ break;
-/* Make the variable associated with NAME be auto-exported.
- If NAME does not exist yet, create it. */
-void
+ if (elt == 0)
+ return (-1);
+
+ old_var = (SHELL_VAR *)elt->data;
+
+ 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. 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)
+ {
+ VSETATTR (old_var, att_invisible);
+ FREE (value_cell (old_var));
+ 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);
+
+ free (elt->key);
+ free (elt);
+ return (0);
+ }
+
+ /* Have to save a copy of name here, because it might refer to
+ old_var->name. If so, stupidly_hack_special_variables will
+ reference freed memory. */
+ t = savestring (name);
+
+ free (elt->key);
+ free (elt);
+
+ dispose_variable (old_var);
+ stupidly_hack_special_variables (t);
+ free (t);
+
+ return (0);
+}
+
+/* Get rid of all of the variables in the current context. */
+void
+kill_all_local_variables ()
+{
+ VAR_CONTEXT *vc;
+
+ for (vc = shell_variables; vc; vc = vc->down)
+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
+ break;
+ if (vc == 0)
+ return; /* XXX */
+
+ if (vc->table && vc_haslocals (vc))
+ {
+ delete_all_variables (vc->table);
+ hash_dispose (vc->table);
+ }
+ vc->table = (HASH_TABLE *)NULL;
+}
+
+static void
+free_variable_hash_data (data)
+ PTR_T data;
+{
+ SHELL_VAR *var;
+
+ var = (SHELL_VAR *)data;
+ dispose_variable (var);
+}
+
+/* Delete the entire contents of the hash table. */
+void
+delete_all_variables (hashed_vars)
+ HASH_TABLE *hashed_vars;
+{
+ hash_flush (hashed_vars, free_variable_hash_data);
+}
+
+/* **************************************************************** */
+/* */
+/* Setting variable attributes */
+/* */
+/* **************************************************************** */
+
+#define FIND_OR_MAKE_VARIABLE(name, entry) \
+ do \
+ { \
+ entry = find_variable (name); \
+ if (!entry) \
+ { \
+ entry = bind_variable (name, ""); \
+ if (!no_invisible_vars) entry->attributes |= att_invisible; \
+ } \
+ } \
+ while (0)
+
+/* Make the variable associated with NAME be readonly.
+ If NAME does not exist yet, create it. */
+void
+set_var_read_only (name)
+ char *name;
+{
+ SHELL_VAR *entry;
+
+ FIND_OR_MAKE_VARIABLE (name, entry);
+ 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)
+ const char *name;
+{
+ SHELL_VAR *entry;
+
+ entry = find_function (name);
+ if (entry)
+ VSETATTR (entry, att_readonly);
+}
+
+/* Make the variable associated with NAME be auto-exported.
+ If NAME does not exist yet, create it. */
+void
set_var_auto_export (name)
char *name;
{
- SHELL_VAR *entry = find_variable (name);
+ SHELL_VAR *entry;
+
+ FIND_OR_MAKE_VARIABLE (name, entry);
+ set_auto_export (entry);
+}
+
+/* Make the function associated with NAME be auto-exported. */
+void
+set_func_auto_export (name)
+ const char *name;
+{
+ SHELL_VAR *entry;
+
+ entry = find_function (name);
+ if (entry)
+ set_auto_export (entry);
+}
+#endif
+
+/* **************************************************************** */
+/* */
+/* Creating lists of variables */
+/* */
+/* **************************************************************** */
+
+static VARLIST *
+vlist_alloc (nentries)
+ int nentries;
+{
+ 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;
+}
+
+static VARLIST *
+vlist_realloc (vlist, n)
+ VARLIST *vlist;
+ int n;
+{
+ if (vlist == 0)
+ return (vlist = vlist_alloc (n));
+ if (n > vlist->list_size)
+ {
+ vlist->list_size = n;
+ vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
+ }
+ return vlist;
+}
+
+static void
+vlist_add (vlist, var, flags)
+ VARLIST *vlist;
+ SHELL_VAR *var;
+ int flags;
+{
+ register int i;
+
+ for (i = 0; i < vlist->list_len; i++)
+ if (STREQ (var->name, vlist->list[i]->name))
+ break;
+ if (i < vlist->list_len)
+ return;
+
+ if (i >= vlist->list_size)
+ vlist = vlist_realloc (vlist, vlist->list_size + 16);
+
+ vlist->list[vlist->list_len++] = var;
+ vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
+}
+
+/* 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;
+
+ if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0)
+ return ((SHELL_VAR **)NULL);
+
+ 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);
+ }
+ }
+}
+
+void
+sort_variables (array)
+ SHELL_VAR **array;
+{
+ qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp);
+}
+
+static int
+qsort_var_comp (var1, var2)
+ SHELL_VAR **var1, **var2;
+{
+ 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 **
+vapply (func)
+ sh_var_map_func_t *func;
+{
+ SHELL_VAR **list;
+
+ 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_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 (fapply (visible_var));
+}
+
+SHELL_VAR **
+all_visible_variables ()
+{
+ return (vapply (visible_var));
+}
+
+/* Return non-zero if the variable VAR is visible and exported. Array
+ variables cannot be exported. */
+static int
+visible_and_exported (var)
+ SHELL_VAR *var;
+{
+ return (invisible_p (var) == 0 && exported_p (var));
+}
+
+/* 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;
+{
+ return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var));
+}
+
+SHELL_VAR **
+all_exported_variables ()
+{
+ return (vapply (visible_and_exported));
+}
+
+SHELL_VAR **
+local_exported_variables ()
+{
+ return (vapply (local_and_exported));
+}
+
+static int
+variable_in_context (var)
+ SHELL_VAR *var;
+{
+ return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context);
+}
+
+SHELL_VAR **
+all_local_variables ()
+{
+ VARLIST *vlist;
+ SHELL_VAR **ret;
+ VAR_CONTEXT *vc;
+
+ vc = shell_variables;
+ for (vc = shell_variables; vc; vc = vc->down)
+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
+ break;
+
+ if (vc == 0)
+ {
+ internal_error ("all_local_variables: no function context at current scope found");
+ 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;
+}
+
+#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;
+{
+ return (invisible_p (var) == 0 && array_p (var));
+}
+
+SHELL_VAR **
+all_array_variables ()
+{
+ return (vapply (visible_array_vars));
+}
+#endif /* ARRAY_VARS */
+
+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++)
+ {
+ if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen))
+ rlist[rind++] = savestring (varlist[vind]->name);
+ }
+ rlist[rind] = (char *)0;
+ free (varlist);
+
+ return rlist;
+}
+
+/* **************************************************************** */
+/* */
+/* Managing temporary variable scopes */
+/* */
+/* **************************************************************** */
+
+/* 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;
+
+ var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;
+
+ if (var)
+ {
+ FREE (value_cell (var));
+ var_setvalue (var, savestring (value));
+ INVALIDATE_EXPORTSTR (var);
+ }
+
+ return (var);
+}
+
+/* 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);
+}
+
+/* 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;
+{
+ SHELL_VAR *var, *v;
+ HASH_TABLE *binding_table;
+
+ var = (SHELL_VAR *)data;
+
+ binding_table = shell_variables->table;
+ if (binding_table == 0)
+ {
+ 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);
+ }
+
+ v = bind_variable_internal (var->name, value_cell (var), binding_table, 0);
+
+ /* 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;
+ }
+ v->attributes |= var->attributes;
+
+ dispose_variable (var);
+}
+
+static void
+propagate_temp_var (data)
+ PTR_T data;
+{
+ SHELL_VAR *var;
+
+ var = (SHELL_VAR *)data;
+ if (tempvar_p (var) && (var->attributes & att_propagate))
+ push_temp_var (data);
+ else
+ dispose_variable (var);
+}
+
+/* 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_env (pushf)
+ sh_free_func_t *pushf;
+{
+ hash_flush (temporary_env, pushf);
+ hash_dispose (temporary_env);
+ temporary_env = (HASH_TABLE *)NULL;
+
+ array_needs_making = 1;
+
+ sv_ifs ("IFS"); /* XXX here for now */
+}
+
+void
+dispose_used_env_vars ()
+{
+ if (temporary_env)
+ dispose_temporary_env (propagate_temp_var);
+}
+
+/* Take all of the shell variables in the temporary environment HASH_TABLE
+ and make shell variables from them at the current variable context. */
+void
+merge_temporary_env ()
+{
+ if (temporary_env)
+ dispose_temporary_env (push_temp_var);
+}
+
+/* **************************************************************** */
+/* */
+/* Creating and manipulating the environment */
+/* */
+/* **************************************************************** */
+
+static inline char *
+mk_env_string (name, value)
+ const char *name, *value;
+{
+ 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);
+}
+
+#ifdef DEBUG
+/* Debugging */
+static int
+valid_exportstr (v)
+ SHELL_VAR *v;
+{
+ char *s;
+
+ 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 != '=')
+ {
+ internal_error ("no `=' in exportstr for %s", v->name);
+ return (0);
+ }
+ return (1);
+}
+#endif
+
+static char **
+make_env_array_from_var_list (vars)
+ SHELL_VAR **vars;
+{
+ 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);
+}
+
+/* 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;
+{
+ 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);
+}
+
+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 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); \
+ } \
+ 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_exported_var (assign, do_alloc)
+ char *assign;
+ int do_alloc;
+{
+ register int i;
+ int equal_offset;
+
+ equal_offset = assignment (assign);
+ if (equal_offset == 0)
+ return (export_env);
+
+ /* 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; i < export_env_index; i++)
+ {
+ if (STREQN (assign, export_env[i], equal_offset + 1))
+ {
+ free (export_env[i]);
+ export_env[i] = do_alloc ? savestring (assign) : assign;
+ return (export_env);
+ }
+ }
+ 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
+ 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++'.
+
+ 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 char **temp_array;
+ int new_size;
+ VAR_CONTEXT *tcxt;
+
+ if (array_needs_making)
+ {
+ if (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);
+ }
+ 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)
+ {
+ 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;
+{
+ 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)
+ intmax_t pid;
+ char *flags_string;
+{
+ char *dummy, *pbuf;
+ int l, fl;
+
+ pbuf = itos (pid);
+ l = strlen (pbuf);
+
+ fl = strlen (flags_string);
+
+ dummy = (char *)xmalloc (l + fl + 30);
+ dummy[0] = '_';
+ strcpy (dummy + 1, pbuf);
+ strcpy (dummy + 1 + l, "_GNU_nonoption_argv_flags_");
+ dummy[l + 27] = '=';
+ strcpy (dummy + l + 28, flags_string);
+
+ free (pbuf);
+
+ export_env = add_or_supercede_exported_var (dummy, 0);
+}
+#endif
+
+/* **************************************************************** */
+/* */
+/* Managing variable contexts */
+/* */
+/* **************************************************************** */
+
+/* Allocate and return a new variable context with NAME and FLAGS.
+ NAME can be NULL. */
+
+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;
+{
+ 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);
+ 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;
- if (!entry)
+ vcxt = shell_variables;
+ if (vc_isfuncenv (vcxt) == 0)
{
- entry = bind_variable (name, "");
- if (!no_invisible_vars)
- entry->attributes |= att_invisible;
+ internal_error ("pop_var_context: head of shell_variables not a function context");
+ return;
}
- set_auto_export (entry);
+ 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");
}
-/* Make the function associated with NAME be auto-exported. */
+/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
+ all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
void
-set_func_auto_export (name)
- char *name;
+delete_all_contexts (vcxt)
+ VAR_CONTEXT *vcxt;
{
- SHELL_VAR *entry = find_function (name);
+ VAR_CONTEXT *v, *t;
- if (entry)
+ for (v = vcxt; v != global_variables; v = t)
{
- entry->attributes |= att_exported;
- array_needs_making = 1;
- }
+ t = v->down;
+ dispose_var_context (v);
+ }
+
+ delete_all_variables (global_variables->table);
+ shell_variables = global_variables;
}
-/* Returns non-zero if STRING is an assignment statement. The returned value
- is the index of the `=' sign. */
-assignment (string)
- char *string;
+/* **************************************************************** */
+/* */
+/* Pushing and Popping temporary variable scopes */
+/* */
+/* **************************************************************** */
+
+VAR_CONTEXT *
+push_scope (flags, tmpvars)
+ int flags;
+ HASH_TABLE *tmpvars;
{
- register int c, indx = 0;
+ return (push_var_context ((char *)NULL, flags, tmpvars));
+}
- c = string[indx];
+static void
+push_exported_var (data)
+ PTR_T data;
+{
+ SHELL_VAR *var, *v;
- if (!isletter (c) && c != '_')
- return (0);
+ var = (SHELL_VAR *)data;
- while (c = string[indx])
+ /* 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. */
+ if (exported_p (var) || (var->attributes & att_propagate))
{
- /* The following is safe. Note that '=' at the start of a word
- is not an assignment statement. */
- if (c == '=')
- return (indx);
-
- if (!isletter (c) && !digit (c) && c != '_')
- return (0);
-
- indx++;
+ var->attributes &= ~att_tempvar; /* XXX */
+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
+ if (shell_variables == global_variables)
+ var->attributes &= ~att_propagate;
+ v->attributes |= var->attributes;
}
- return (0);
-}
-static int
-visible_var (var)
- SHELL_VAR *var;
-{
- return (!invisible_p (var));
+ dispose_variable (var);
}
-SHELL_VAR **
-all_visible_variables ()
+void
+pop_scope (is_special)
+ int is_special;
{
- SHELL_VAR **list;
+ VAR_CONTEXT *vcxt, *ret;
+
+ vcxt = shell_variables;
+ if (vc_istempscope (vcxt) == 0)
+ {
+ internal_error ("pop_scope: head of shell_variables not a temporary environment scope");
+ return;
+ }
- list = map_over (visible_var, shell_variables);
+ ret = vcxt->down;
+ if (ret)
+ ret->up = (VAR_CONTEXT *)NULL;
- if (list)
- sort_variables (list);
+ shell_variables = ret;
- return (list);
-}
+ /* 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);
-SHELL_VAR **
-all_visible_functions ()
-{
- SHELL_VAR **list;
+ sv_ifs ("IFS"); /* XXX here for now */
+}
- list = map_over (visible_var, shell_functions);
+/* **************************************************************** */
+/* */
+/* Pushing and Popping function contexts */
+/* */
+/* **************************************************************** */
- if (list)
- sort_variables (list);
+static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
+static int dollar_arg_stack_slots;
+static int dollar_arg_stack_index;
- return (list);
+/* 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);
}
-/* Return non-zero if the variable VAR is visible and exported. */
-static int
-visible_and_exported (var)
- SHELL_VAR *var;
+/* 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 ()
{
- return (!invisible_p (var) && exported_p (var));
+ pop_dollar_vars ();
+ variable_context--;
+ pop_var_context ();
+
+ sv_ifs ("IFS"); /* XXX here for now */
}
-/* 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;
+/* Save the existing positional parameters on a stack. */
+void
+push_dollar_vars ()
{
- register int i, list_index;
- register SHELL_VAR *var;
- char **list = (char **)NULL;
- SHELL_VAR **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;
+}
- vars = map_over (visible_and_exported, hashed_vars);
+/* Restore the positional parameters from our stack. */
+void
+pop_dollar_vars ()
+{
+ if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+ return;
- if (!vars)
- return (char **)NULL;
+ 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 ();
+}
- list = (char **)xmalloc ((1 + array_len ((char **)vars)) * sizeof (char *));
+void
+dispose_saved_dollar_vars ()
+{
+ if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+ return;
- for (i = 0, list_index = 0; var = vars[i]; i++)
- {
- char *value;
+ dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+}
- if (function_p (var))
- value = named_function_string
- ((char *)NULL, (COMMAND *)function_cell (var), 0);
- else
- value = value_cell (var);
+/*************************************************
+ * *
+ * Functions to manage special variables *
+ * *
+ *************************************************/
- if (value)
- {
- int name_len = strlen (var->name);
- int value_len = strlen (value);
- char *p;
-
- 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++;
- }
- }
+/* Extern declarations for variables this code has to manage. */
+extern int eof_encountered, eof_encountered_limit, ignoreeof;
- free (vars);
- list[list_index] = (char *)NULL;
- return (list);
-}
+#if defined (READLINE)
+extern int no_line_editing;
+extern int hostname_list_initialized;
+#endif
-/* Add STRING to the array of foo=bar strings that we already
- have to add to the environment. */
-assign_in_env (string)
- char *string;
-{
- int size;
+/* 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. */
- int offset = assignment (string);
- char *name = savestring (string);
- char *temp, *value = (char *)NULL;
- int nlen, vlen;
+#define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0
- if (name[offset] == '=')
- {
- WORD_LIST *list;
+/* This table will be sorted with qsort() the first time it's accessed. */
+struct name_and_function {
+ char *name;
+ sh_sv_func_t *function;
+};
- name[offset] = 0;
- temp = name + offset + 1;
- temp = tilde_expand (temp);
+static struct name_and_function special_vars[] = {
+ { "GLOBIGNORE", sv_globignore },
- list = expand_string_unsplit (temp, 0);
- value = string_list (list);
+#if defined (HISTORY)
+ { "HISTCONTROL", sv_history_control },
+ { "HISTFILESIZE", sv_histsize },
+ { "HISTIGNORE", sv_histignore },
+ { "HISTSIZE", sv_histsize },
+#endif
- if (list)
- dispose_words (list);
+#if defined (READLINE)
+ { "HOSTFILE", sv_hostfile },
+#endif
- free (temp);
- }
+ { "IFS", sv_ifs },
+ { "IGNOREEOF", sv_ignoreeof },
- if (!value)
- value = savestring ("");
+ { "LANG", sv_locale },
+ { "LC_ALL", sv_locale },
+ { "LC_COLLATE", sv_locale },
+ { "LC_CTYPE", sv_locale },
+ { "LC_MESSAGES", sv_locale },
+ { "LC_NUMERIC", sv_locale },
- nlen = strlen (name);
- vlen = strlen (value);
- temp = xmalloc (2 + nlen + vlen);
- strcpy (temp, name);
- temp[nlen] = '=';
- strcpy (temp + nlen + 1, value);
- free (name);
- free (value);
+ { "MAIL", sv_mail },
+ { "MAILCHECK", sv_mail },
+ { "MAILPATH", sv_mail },
- if (!temporary_env)
- {
- temporary_env = (char **)xmalloc (sizeof (char *));
- temporary_env [0] = (char *)NULL;
- }
+ { "OPTERR", sv_opterr },
+ { "OPTIND", sv_optind },
- size = array_len (temporary_env);
- temporary_env = (char **)
- xrealloc (temporary_env, (size + 2) * (sizeof (char *)));
+ { "PATH", sv_path },
+ { "POSIXLY_CORRECT", sv_strict_posix },
- temporary_env[size] = (temp);
- temporary_env[size + 1] = (char *)NULL;
- array_needs_making = 1;
+#if defined (READLINE)
+ { "TERM", sv_terminal },
+ { "TERMCAP", sv_terminal },
+ { "TERMINFO", sv_terminal },
+#endif /* READLINE */
- if (echo_command_at_execute)
- {
- /* The K*rn shell prints the `+ ' in front of assignment statements,
- so we do too. */
- fprintf (stderr, "%s%s\n", indirection_level_string (), temp);
- fflush (stderr);
- }
+ { "TEXTDOMAIN", sv_locale },
+ { "TEXTDOMAINDIR", sv_locale },
- return 1;
-}
+#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+ { "TZ", sv_tz },
+#endif
-/* 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;
-{
- register int i, l = strlen (name);
+#if defined (HISTORY) && defined (BANG_HISTORY)
+ { "histchars", sv_histchars },
+#endif /* HISTORY && BANG_HISTORY */
- if (!array)
- return ((SHELL_VAR *)NULL);
+ { "ignoreeof", sv_ignoreeof },
- for (i = 0; array[i]; i++)
- {
- if (STREQN (array[i], name, l) && array[i][l] == '=')
- {
- SHELL_VAR *temp;
+ { (char *)0, (sh_sv_func_t *)0 }
+};
- temp = new_shell_variable (name);
+#define N_SPECIAL_VARS (sizeof (special_vars) / sizeof (special_vars[0]) - 1)
- if (array[i][l + 1])
- temp->value = savestring (&array[i][l + 1]);
- else
- temp->value = (char *) NULL;
+static int
+sv_compare (sv1, sv2)
+ struct name_and_function *sv1, *sv2;
+{
+ int r;
- temp->attributes = att_exported;
- temp->context = 0;
- temp->prev_context = (SHELL_VAR *)NULL;
+ if ((r = sv1->name[0] - sv2->name[0]) == 0)
+ r = strcmp (sv1->name, sv2->name);
+ return r;
+}
- temp->dynamic_value = (DYNAMIC_FUNC *)NULL;
- temp->assign_func = (DYNAMIC_FUNC *)NULL;
+static inline int
+find_special_var (name)
+ const char *name;
+{
+ register int i, r;
- return (temp);
- }
+ 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 ((SHELL_VAR *)NULL);
+ return -1;
}
-/* 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)
+/* 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;
{
- 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);
+ static int sv_sorted = 0;
+ int i;
- if (!var && variable_context && function_env)
- var = find_name_in_env_array (name, function_env);
+ 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;
+ }
- return (var);
+ i = find_special_var (name);
+ if (i != -1)
+ (*(special_vars[i].function)) (name);
}
-/* Free the storage allocated to the string array pointed to by ARRAYP, and
- make that variable have a null pointer as a value. */
-static void
-dispose_temporary_vars (arrayp)
- char ***arrayp;
+void
+sv_ifs (name)
+ char *name;
{
- if (!*arrayp)
- return;
+ SHELL_VAR *v;
- free_array (*arrayp);
- *arrayp = (char **)NULL;
- array_needs_making = 1;
+ v = find_variable ("IFS");
+ setifs (v);
}
-/* Free the storage used in the variable array for temporary
- environment variables. */
+/* What to do just after the PATH variable has changed. */
void
-dispose_used_env_vars ()
+sv_path (name)
+ char *name;
{
- dispose_temporary_vars (&temporary_env);
+ /* hash -r */
+ phash_flush ();
}
-/* Free the storage used for temporary environment variables given to
- commands when executing inside of a function body. */
+/* 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
-dispose_function_env ()
+sv_mail (name)
+ char *name;
{
- dispose_temporary_vars (&function_env);
+ /* 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 ();
+ }
}
-/* Free the storage used for temporary environment variables given to
- commands when executing a builtin command such as "source". */
+/* What to do when GLOBIGNORE changes. */
void
-dispose_builtin_env ()
+sv_globignore (name)
+ char *name;
{
- dispose_temporary_vars (&builtin_env);
+ setup_glob_ignore (name);
}
-/* Sort ARRAY, a null terminated array of pointers to strings. */
+#if defined (READLINE)
+/* 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
-sort_char_array (array)
- char **array;
+sv_terminal (name)
+ char *name;
{
- qsort (array, array_len (array), sizeof (char *),
- (Function *)qsort_string_compare);
+ if (interactive_shell && no_line_editing == 0)
+ rl_reset_terminal (get_string_value ("TERM"));
}
-#define ISFUNC(s, o) ((s[o + 1] == '(') && (s[o + 2] == ')'))
-
-/* Add ASSIGN to ARRAY, or supercede a previous assignment in the
- array with the same left-hand side. Return the new array. */
-char **
-add_or_supercede (assign, array)
- char *assign;
- register char **array;
+void
+sv_hostfile (name)
+ char *name;
{
- register int i;
- int equal_offset = assignment (assign);
+ SHELL_VAR *v;
- if (!equal_offset)
- return (array);
+ v = find_variable (name);
+ if (v == 0)
+ clear_hostname_list ();
+ else
+ hostname_list_initialized = 0;
+}
+#endif /* READLINE */
+
+#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;
- /* 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++;
+ temp = get_string_value (name);
- for (i = 0; array && array[i]; i++)
+ if (temp && *temp)
{
- if (STREQN (assign, array[i], equal_offset + 1))
+ if (legal_number (temp, &num))
{
- free (array[i]);
- array[i] = savestring (assign);
- return (array);
+ if (name[4] == 'S')
+ {
+ stifle_history (num);
+ 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;
+ }
}
}
- array = (char **)xrealloc (array, ((2 + i) * sizeof (char *)));
- array[i++] = savestring (assign);
- array[i] = (char *)NULL;
- return (array);
+ else if (name[4] == 'S')
+ unstifle_history ();
}
-/* 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++'. */
+/* What to do after the HISTIGNORE variable changes. */
void
-maybe_make_export_env ()
+sv_histignore (name)
+ char *name;
{
- register int i;
- register char **temp_array;
+ setup_history_ignore (name);
+}
- if (array_needs_making)
- {
- if (export_env)
- free_array (export_env);
+/* What to do after the HISTCONTROL variable changes. */
+void
+sv_history_control (name)
+ char *name;
+{
+ char *temp;
-#ifdef SHADOWED_ENV
- export_env =
- (char **)xmalloc ((1 + array_len (shell_environment)) * sizeof (char *));
+ history_control = 0;
+ temp = get_string_value (name);
- for (i = 0; shell_environment[i]; i++)
- export_env[i] = savestring (shell_environment[i]);
- export_env[i] = (char *)NULL;
+ if (temp && *temp && STREQN (temp, "ignore", 6))
+ {
+ if (temp[6] == 's') /* ignorespace */
+ history_control = 1;
+ else if (temp[6] == 'd') /* ignoredups */
+ history_control = 2;
+ else if (temp[6] == 'b') /* ignoreboth */
+ history_control = 3;
+ }
+}
-#else /* !SHADOWED_ENV */
+#if defined (BANG_HISTORY)
+/* Setting/unsetting of the history expansion character. */
+void
+sv_histchars (name)
+ char *name;
+{
+ char *temp;
- export_env = (char **)xmalloc (sizeof (char *));
- export_env[0] = (char *)NULL;
+ 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 */
+#endif /* HISTORY */
-#endif /* SHADOWED_ENV */
+#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+void
+sv_tz (name)
+ char *name;
+{
+ tzset ();
+}
+#endif
- 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);
+/* 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;
- 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);
+ eof_encountered = 0;
- if (function_env)
- for (i = 0; function_env[i]; i++)
- export_env = add_or_supercede (function_env[i], export_env);
+ 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 */
+}
- if (temporary_env)
- for (i = 0; temporary_env[i]; i++)
- export_env = add_or_supercede (temporary_env[i], export_env);
+void
+sv_optind (name)
+ char *name;
+{
+ char *tt;
+ int s;
- /* If we changed the array, then sort it alphabetically. */
- if (temporary_env || function_env)
- sort_char_array (export_env);
+ tt = get_string_value ("OPTIND");
+ if (tt && *tt)
+ {
+ s = atoi (tt);
- array_needs_making = 0;
+ /* 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);
}
-/* We always put _ in the environment as the name of this command. */
void
-put_command_name_into_env (command_name)
- char *command_name;
+sv_opterr (name)
+ char *name;
{
- char *dummy;
+ char *tt;
- dummy = xmalloc (4 + strlen (command_name));
+ tt = get_string_value ("OPTERR");
+ sh_opterr = (tt && *tt) ? atoi (tt) : 1;
+}
- /* 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);
+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 */
}
-/* We supply our own version of getenv () because we want library
- routines to get the changed values of exported variables. */
+void
+sv_locale (name)
+ char *name;
+{
+ char *v;
-/* The NeXT C library has getenv () defined and used in the same file.
- This screws our scheme. However, Bash will run on the NeXT using
- the C library getenv (), since right now the only environment variable
- that we care about is HOME, and that is already defined. */
-#if !defined (NeXT) && !defined (HPOSF1)
-static char *last_tempenv_value = (char *)NULL;
-extern char **environ;
+ 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* */
+}
-char *
-getenv (name)
-#if defined (Linux) || defined (__bsdi__) || defined (convex)
- const char *name;
-#else
- char const *name;
-#endif /* !Linux */
+#if defined (ARRAY_VARS)
+void
+set_pipestatus_array (ps, nproc)
+ int *ps;
+ int nproc;
{
- SHELL_VAR *var = find_tempenv_variable ((char *)name);
+ SHELL_VAR *v;
+ ARRAY *a;
+ ARRAY_ELEMENT *ae;
+ register int i;
+ char *t, tbuf[INT_STRLEN_BOUND(int) + 1];
- if (var)
+ 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)
{
- FREE (last_tempenv_value);
+ 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;
+ }
- last_tempenv_value = savestring (value_cell (var));
- dispose_variable (var);
- return (last_tempenv_value);
+ /* 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 (shell_variables)
+ else if (array_num_elements (a) <= nproc)
{
- var = find_variable ((char *)name);
- if (var && exported_p (var))
- return (value_cell (var));
+ /* 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
{
- register int i, len = strlen (name);
-
- /* In some cases, s5r3 invokes getenv() before main(); BSD systems
- using gprof also exhibit this behavior. This means that
- shell_variables will be 0 when this is invoked. We look up the
- variable in the real environment in that case. */
-
- for (i = 0; environ[i]; i++)
+ /* deleting elements. it's faster to rebuild the array. */
+ array_flush (a);
+ for (i = 0; ps[i] != -1; i++)
{
- if ((STREQN (environ[i], name, len)) && (environ[i][len] == '='))
- return (environ[i] + len + 1);
+ t = inttostr (ps[i], tbuf, sizeof (tbuf));
+ array_insert (a, i, t);
}
}
+}
+#endif
- return ((char *)NULL);
+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
}
-#endif /* !NeXT && !HPOSF1 */