/* variables.c -- Functions for hacking shell variables. */
-/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Bash is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
+ the Free Software Foundation; either version 2, or (at your option)
any later version.
Bash is distributed in the hope that it will be useful, but WITHOUT
You should have received a copy of the GNU General Public License
along with Bash; see the file COPYING. If not, write to the Free
- Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
#include "config.h"
#include "bashtypes.h"
#include "posixstat.h"
+#include "posixtime.h"
#if defined (qnx)
# include <sys/vc.h>
#endif
#include <stdio.h>
-#include <ctype.h>
+#include "chartypes.h"
#include <pwd.h>
#include "bashansi.h"
+#include "bashintl.h"
#include "shell.h"
#include "flags.h"
#include "execute_cmd.h"
+#include "findcmd.h"
#include "mailcheck.h"
#include "input.h"
+#include "hashcmd.h"
+#include "pathexp.h"
#include "builtins/getopt.h"
#include "builtins/common.h"
-#include <tilde/tilde.h>
+
+#if defined (READLINE)
+# include "bashline.h"
+# include <readline/readline.h>
+#else
+# include <tilde/tilde.h>
+#endif
#if defined (HISTORY)
# include "bashhist.h"
+# include <readline/history.h>
#endif /* HISTORY */
+#if defined (PROGRAMMABLE_COMPLETION)
+# include "pcomplete.h"
+#endif
+
+#define TEMPENV_HASH_BUCKETS 4 /* must be power of two */
+
+#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
+
/* Variables used here and defined in other files. */
extern int posixly_correct;
-extern int variable_context, line_number;
-extern int interactive, interactive_shell, login_shell;
-extern int subshell_environment, indirection_level;
+extern int line_number;
+extern int subshell_environment, indirection_level, subshell_level;
extern int build_version, patch_level;
+extern int expanding_redir;
extern char *dist_version, *release_status;
extern char *shell_name;
extern char *primary_prompt, *secondary_prompt;
extern char *current_host_name;
-extern Function *this_shell_builtin;
+extern sh_builtin_func_t *this_shell_builtin;
+extern SHELL_VAR *this_shell_function;
+extern char *the_printed_command_except_trap;
extern char *this_command_name;
+extern char *command_execution_string;
extern time_t shell_start_time;
-/* The list of shell variables that the user has created, or that came from
- the environment. */
-HASH_TABLE *shell_variables = (HASH_TABLE *)NULL;
+#if defined (READLINE)
+extern int perform_hostname_completion;
+#endif
+
+/* The list of shell variables that the user has created at the global
+ scope, or that came from the environment. */
+VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL;
+
+/* The current list of shell variables, including function scopes */
+VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL;
/* The list of shell functions that the user has created, or that came from
the environment. */
HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
+#if defined (DEBUGGER)
+/* The table of shell function definitions that the user defined or that
+ came from the environment. */
+HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL;
+#endif
+
/* The current variable context. This is really a count of how deep into
executing functions we are. */
int variable_context = 0;
-/* The array of shell assignments which are made only in the environment
+/* The set of shell assignments which are made only in the environment
for a single command. */
-char **temporary_env = (char **)NULL;
-
-/* The array of shell assignments which are in the environment for the
- execution of a shell function. */
-char **function_env = (char **)NULL;
+HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;
-/* The array of shell assignments which are made only in the environment
- for the execution of a shell builtin command which may cause more than
- one command to be executed (e.g., "source"). */
-char **builtin_env = (char **)NULL;
+/* Set to non-zero if an assignment error occurs while putting variables
+ into the temporary environment. */
+int tempenv_assign_error;
/* Some funky variables which are known about specially. Here is where
"$*", "$1", and all the cruft is kept. */
WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
/* The value of $$. */
-int dollar_dollar_pid;
+pid_t dollar_dollar_pid;
/* An array which is passed to commands as their environment. It is
manufactured from the union of the initial environment and the
by initialize_variables (). */
int shell_level = 0;
-static char *have_local_variables;
-static int local_variable_stack_size;
-
/* Some forward declarations. */
-static void set_home_var ();
-static void set_shell_var ();
-static char *get_bash_name ();
-static void initialize_shell_level ();
-static void uidset ();
-static void initialize_dynamic_variables ();
-static void make_vers_array ();
-static void sbrand (); /* set bash random number generator. */
-static int qsort_var_comp ();
-
-/* 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)
-
-/* Initialize the shell variables from the current environment. */
+static void set_machine_vars __P((void));
+static void set_home_var __P((void));
+static void set_shell_var __P((void));
+static char *get_bash_name __P((void));
+static void initialize_shell_level __P((void));
+static void uidset __P((void));
+#if defined (ARRAY_VARS)
+static void make_vers_array __P((void));
+#endif
+
+static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t));
+#if defined (ARRAY_VARS)
+static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t));
+#endif
+static SHELL_VAR *get_self __P((SHELL_VAR *));
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
+#endif
+
+static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_seconds __P((SHELL_VAR *));
+static SHELL_VAR *init_seconds_var __P((void));
+
+static int brand __P((void));
+static void sbrand __P((unsigned long)); /* set bash random number generator. */
+static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_random __P((SHELL_VAR *));
+
+static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_lineno __P((SHELL_VAR *));
+
+static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_subshell __P((SHELL_VAR *));
+
+#if defined (HISTORY)
+static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
+#endif
+
+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
+static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t));
+static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
+#endif
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *get_groupset __P((SHELL_VAR *));
+#endif
+
+static SHELL_VAR *get_funcname __P((SHELL_VAR *));
+static SHELL_VAR *init_funcname_var __P((void));
+
+static void initialize_dynamic_variables __P((void));
+
+static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
+static SHELL_VAR *new_shell_variable __P((const char *));
+static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int));
+
+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
+
+static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
+static void push_temp_var __P((PTR_T));
+static void propagate_temp_var __P((PTR_T));
+static void dispose_temporary_env __P((sh_free_func_t *));
+
+static inline char *mk_env_string __P((const char *, const char *));
+static char **make_env_array_from_var_list __P((SHELL_VAR **));
+static char **make_var_export_array __P((VAR_CONTEXT *));
+static char **make_func_export_array __P((void));
+static void add_temp_array_to_env __P((char **, int, int));
+
+static int n_shell_variables __P((void));
+static int set_context __P((SHELL_VAR *));
+
+static void push_func_var __P((PTR_T));
+static void push_exported_var __P((PTR_T));
+
+static inline int find_special_var __P((const char *));
+
+/* Initialize the shell variables from the current environment.
+ If PRIVMODE is nonzero, don't import functions from ENV or
+ parse $SHELLOPTS. */
void
-initialize_shell_variables (env, no_functions)
+initialize_shell_variables (env, privmode)
char **env;
- int no_functions; /* If set, don't import functions from ENV. */
+ int privmode;
{
char *name, *string, *temp_string;
int c, char_index, string_index, string_length;
SHELL_VAR *temp_var;
- if (!shell_variables)
- shell_variables = make_hash_table (0);
+ if (shell_variables == 0)
+ {
+ shell_variables = global_variables = new_var_context ((char *)NULL, 0);
+ shell_variables->scope = 0;
+ shell_variables->table = hash_create (0);
+ }
- if (!shell_functions)
- shell_functions = make_hash_table (0);
+ if (shell_functions == 0)
+ shell_functions = hash_create (0);
+
+#if defined (DEBUGGER)
+ if (shell_function_defs == 0)
+ shell_function_defs = hash_create (0);
+#endif
for (string_index = 0; string = env[string_index++]; )
{
while ((c = *string++) && c != '=')
;
if (string[-1] == '=')
- char_index = string - name - 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;
+ continue;
/* ASSERT(name[char_index] == '=') */
name[char_index] = '\0';
/* Now, name = env variable name, string = env variable value, and
- char_index == strlen (name) */
+ char_index == strlen (name) */
- /* If exported function, define it now. */
- if (no_functions == 0 && STREQN ("() {", string, 4))
+ /* If exported function, define it now. Don't import functions from
+ the environment in privileged mode. */
+ if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))
{
string_length = strlen (string);
- temp_string = xmalloc (3 + string_length + char_index);
-#if 1
+ temp_string = (char *)xmalloc (3 + string_length + char_index);
+
strcpy (temp_string, name);
temp_string[char_index] = ' ';
strcpy (temp_string + char_index + 1, string);
-#else
- sprintf (temp_string, "%s %s", name, string);
-#endif
parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
if (temp_var = find_function (name))
{
- temp_var->attributes |= (att_exported | att_imported);
+ VSETATTR (temp_var, (att_exported|att_imported));
array_needs_making = 1;
}
else
- report_error ("error importing function definition for `%s'", name);
+ report_error (_("error importing function definition for `%s'"), name);
+ /* ( */
if (name[char_index - 1] == ')' && name[char_index - 2] == '\0')
- name[char_index - 2] = '(';
+ name[char_index - 2] = '('; /* ) */
}
#if defined (ARRAY_VARS)
# if 0
/* Array variables may not yet be exported. */
- else if (*string == '(' && string[1] == '[' && strchr (string, ')'))
+ else if (*string == '(' && string[1] == '[' && 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);
- temp_var->attributes |= (att_exported | att_imported);
+ VSETATTR (temp_var, (att_exported | att_imported));
array_needs_making = 1;
}
# endif
else
{
temp_var = bind_variable (name, string);
- temp_var->attributes |= (att_exported | att_imported);
+ VSETATTR (temp_var, (att_exported | att_imported));
array_needs_making = 1;
}
name[char_index] = '=';
- }
-
- /* If we got PWD from the environment, update our idea of the current
- working directory. In any case, make sure that PWD exists before
- checking it. It is possible for getcwd () to fail on shell startup,
- and in that case, PWD would be undefined. */
- temp_var = find_variable ("PWD");
- if (temp_var && imported_p (temp_var) &&
- (temp_string = value_cell (temp_var)) &&
- same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
- set_working_directory (temp_string);
- else
- {
- temp_string = get_working_directory ("shell-init");
- if (temp_string)
+ /* temp_var can be NULL if it was an exported function with a syntax
+ error (a different bug, but it still shouldn't dump core). */
+ if (temp_var && function_p (temp_var) == 0) /* XXX not yet */
{
- bind_variable ("PWD", temp_string);
- free (temp_string);
+ CACHE_IMPORTSTR (temp_var, name);
}
}
+ set_pwd ();
+
/* Set up initial value of $_ */
temp_var = bind_variable ("_", dollar_vars[0]);
/* 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 */
/* set up the prompts. */
if (interactive_shell)
{
+#if defined (PROMPT_STRING_DECODE)
set_if_not ("PS1", primary_prompt);
+#else
+ if (current_user.uid == -1)
+ get_current_user_info ();
+ set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt);
+#endif
set_if_not ("PS2", secondary_prompt);
}
set_if_not ("PS4", "+ ");
/* Don't allow IFS to be imported from the environment. */
temp_var = bind_variable ("IFS", " \t\n");
+ setifs (temp_var);
/* Magic machine types. Pretty convenient. */
- temp_var = bind_variable ("HOSTTYPE", HOSTTYPE);
- set_auto_export (temp_var);
- temp_var = bind_variable ("OSTYPE", OSTYPE);
- set_auto_export (temp_var);
- temp_var = bind_variable ("MACHTYPE", MACHTYPE);
- set_auto_export (temp_var);
- temp_var = bind_variable ("HOSTNAME", current_host_name);
- set_auto_export (temp_var);
+ set_machine_vars ();
/* Default MAILCHECK for interactive shells. Defer the creation of a
default MAILPATH until the startup files are read, because MAIL
- names a mail file if MAILCHECK is not set, and we should provide a
+ names a mail file if MAILPATH is not set, and we should provide a
default only if neither is set. */
if (interactive_shell)
- set_if_not ("MAILCHECK", "60");
+ set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
/* Do some things with shell level. */
initialize_shell_level ();
- /* Make a variable $PPID, which holds the pid of the shell's parent. */
- name = itos ((int) getppid ());
- temp_var = find_variable ("PPID");
- if (temp_var)
- temp_var->attributes &= ~(att_readonly | att_exported);
- temp_var = bind_variable ("PPID", name);
- temp_var->attributes |= (att_readonly | att_integer);
- free (name);
+ set_ppid ();
/* Initialize the `getopts' stuff. */
bind_variable ("OPTIND", "1");
make_vers_array ();
#endif
+ if (command_execution_string)
+ bind_variable ("BASH_EXECUTION_STRING", command_execution_string);
+
/* Find out if we're supposed to be in Posix.2 mode via an
environment variable. */
temp_var = find_variable ("POSIXLY_CORRECT");
that we are remembering commands on the history list. */
if (remember_on_history)
{
- name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history");
+ name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);
set_if_not ("HISTFILE", name);
free (name);
#endif /* HISTORY */
/* Seed the random number generator. */
- sbrand (dollar_dollar_pid + (long)shell_start_time);
+ sbrand (dollar_dollar_pid + shell_start_time);
/* Handle some "special" variables that we may have inherited from a
parent shell. */
}
#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_dynamic_variables ();
}
+/* **************************************************************** */
+/* */
+/* Setting values for special shell variables */
+/* */
+/* **************************************************************** */
+
+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 ()
{
temp_var = find_variable ("HOME");
if (temp_var == 0)
- {
- if (current_user.home_dir == 0)
- get_current_user_info ();
- temp_var = bind_variable ("HOME", current_user.home_dir);
- }
- temp_var->attributes |= att_exported;
+ 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 ();
temp_var = bind_variable ("SHELL", current_user.shell);
}
- temp_var->attributes |= att_exported;
+#if 0
+ VSETATTR (temp_var, att_exported);
+#endif
}
static char *
{
char *name;
- if ((login_shell == 1) && (*shell_name != '/'))
+ if ((login_shell == 1) && RELPATH(shell_name))
{
if (current_user.shell == 0)
- get_current_user_info ();
+ get_current_user_info ();
name = savestring (current_user.shell);
}
- else if (*shell_name == '/')
+ else if (ABSPATH(shell_name))
name = savestring (shell_name);
else if (shell_name[0] == '.' && shell_name[1] == '/')
{
int len;
cdir = get_string_value ("PWD");
- len = strlen (cdir);
- name = xmalloc (len + strlen (shell_name) + 1);
- strcpy (name, cdir);
- strcpy (name + len, shell_name + 1);
+ 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
{
tname = make_absolute (shell_name, get_string_value ("PWD"));
if (*shell_name == '.')
{
- name = canonicalize_pathname (tname);
+ name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
if (name == 0)
name = tname;
else
int change;
{
char new_level[5], *old_SHLVL;
- int old_level;
+ intmax_t old_level;
SHELL_VAR *temp_var;
old_SHLVL = get_string_value ("SHLVL");
- old_level = old_SHLVL ? atoi (old_SHLVL) : 0;
+ 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;
else if (shell_level > 1000)
{
- internal_error ("warning: shell level (%d) too high, resetting to 1", shell_level);
+ internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
shell_level = 1;
}
static void
initialize_shell_level ()
{
-#if 0
+ adjust_shell_level (1);
+}
+
+/* If we got PWD from the environment, update our idea of the current
+ working directory. In any case, make sure that PWD exists before
+ checking it. It is possible for getcwd () to fail on shell startup,
+ and in that case, PWD would be undefined. If this is an interactive
+ login shell, see if $HOME is the current working directory, and if
+ that's not the same string as $PWD, set PWD=$HOME. */
+
+void
+set_pwd ()
+{
+ SHELL_VAR *temp_var, *home_var;
+ char *temp_string, *home_string;
+
+ home_var = find_variable ("HOME");
+ home_string = home_var ? value_cell (home_var) : (char *)NULL;
+
+ temp_var = find_variable ("PWD");
+ if (temp_var && imported_p (temp_var) &&
+ (temp_string = value_cell (temp_var)) &&
+ same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
+ set_working_directory (temp_string);
+ else if (home_string && interactive_shell && login_shell &&
+ same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
+ {
+ set_working_directory (home_string);
+ temp_var = bind_variable ("PWD", home_string);
+ 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);
+ }
+ }
+
+ /* 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));
+}
+
+/* Make a variable $PPID, which holds the pid of the shell's parent. */
+void
+set_ppid ()
+{
+ char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
SHELL_VAR *temp_var;
- temp_var = set_if_not ("SHLVL", "0");
- set_auto_export (temp_var);
-#endif
- adjust_shell_level (1);
+ 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));
}
static void
uidset ()
{
- char *buff;
+ char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
register SHELL_VAR *v;
- buff = itos (current_user.uid);
+ b = inttostr (current_user.uid, buff, sizeof (buff));
v = find_variable ("UID");
- if (v)
- v->attributes &= ~att_readonly;
-
- v = bind_variable ("UID", buff);
- v->attributes |= (att_readonly | att_integer);
-
- if (current_user.euid != current_user.uid)
+ if (v == 0)
{
- free (buff);
- buff = itos (current_user.euid);
+ v = bind_variable ("UID", b);
+ VSETATTR (v, (att_readonly | att_integer));
}
- v = find_variable ("EUID");
- if (v)
- v->attributes &= ~att_readonly;
+ if (current_user.euid != current_user.uid)
+ b = inttostr (current_user.euid, buff, sizeof (buff));
- v = bind_variable ("EUID", buff);
- v->attributes |= (att_readonly | att_integer);
- free (buff);
+ v = find_variable ("EUID");
+ if (v == 0)
+ {
+ v = bind_variable ("EUID", b);
+ VSETATTR (v, (att_readonly | att_integer));
+ }
}
#if defined (ARRAY_VARS)
{
SHELL_VAR *vv;
ARRAY *av;
- char *s, d[16];
+ char *s, d[32], b[INT_STRLEN_BOUND(int) + 1];
- makunbound ("BASH_VERSINFO", shell_variables);
+ unbind_variable ("BASH_VERSINFO");
vv = make_new_array_variable ("BASH_VERSINFO");
av = array_cell (vv);
strcpy (d, dist_version);
- s = strchr (d, '.');
+ s = xstrchr (d, '.');
if (s)
*s++ = '\0';
- array_add_element (av, 0, d);
- array_add_element (av, 1, s);
- s = itos (patch_level);
- array_add_element (av, 2, s);
- free (s);
- s = itos (build_version);
- array_add_element (av, 3, s);
- free (s);
- array_add_element (av, 4, release_status);
- array_add_element (av, 5, MACHTYPE);
-
- vv->attributes |= att_readonly;
+ array_insert (av, 0, d);
+ array_insert (av, 1, s);
+ s = inttostr (patch_level, b, sizeof (b));
+ array_insert (av, 2, s);
+ s = inttostr (build_version, b, sizeof (b));
+ array_insert (av, 3, s);
+ array_insert (av, 4, release_status);
+ array_insert (av, 5, MACHTYPE);
+
+ VSETATTR (vv, att_readonly);
}
#endif /* ARRAY_VARS */
/* Set the environment variables $LINES and $COLUMNS in response to
a window size change. */
void
-set_lines_and_columns (lines, cols)
+sh_set_lines_and_columns (lines, cols)
int lines, cols;
{
- char *val;
-
- val = itos (lines);
- bind_variable ("LINES", val);
- free (val);
-
- val = itos (cols);
- bind_variable ("COLUMNS", val);
- free (val);
-}
-
-/* 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)
- v = bind_variable (name, value);
- return (v);
-}
-
-/* Map FUNCTION over the variables in VARIABLES. 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, var_hash_table)
- Function *function;
- HASH_TABLE* var_hash_table;
-{
- register int i;
- register BUCKET_CONTENTS *tlist;
- SHELL_VAR *var, **list;
- int list_index, list_size;
-
- list = (SHELL_VAR **)NULL;
- for (i = list_index = list_size = 0; i < var_hash_table->nbuckets; i++)
- {
- tlist = get_hash_bucket (i, var_hash_table);
-
- while (tlist)
- {
- var = (SHELL_VAR *)tlist->data;
-
- if (!function || (*function) (var))
- {
- if (list_index + 1 >= list_size)
- list = (SHELL_VAR **)
- xrealloc (list, (list_size += 20) * sizeof (SHELL_VAR *));
-
- list[list_index++] = var;
- list[list_index] = (SHELL_VAR *)NULL;
- }
- tlist = tlist->next;
- }
- }
- return (list);
-}
-
-void
-sort_variables (array)
- SHELL_VAR **array;
-{
- qsort (array, array_len ((char **)array), sizeof (SHELL_VAR *), 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);
-}
-
-/* Create a NULL terminated array of all the shell variables in TABLE. */
-static SHELL_VAR **
-all_vars (table)
- HASH_TABLE *table;
-{
- SHELL_VAR **list;
+ char val[INT_STRLEN_BOUND(int) + 1], *v;
- list = map_over ((Function *)NULL, table);
- if (list /* && posixly_correct */)
- sort_variables (list);
- return (list);
-}
+ v = inttostr (lines, val, sizeof (val));
+ bind_variable ("LINES", v);
-/* Create a NULL terminated array of all the shell variables. */
-SHELL_VAR **
-all_shell_variables ()
-{
- return (all_vars (shell_variables));
+ v = inttostr (cols, val, sizeof (val));
+ bind_variable ("COLUMNS", v);
}
-/* 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 LIST (a list of shell functions) to stdout in such a way that
+ they can be read back in. */
void
-print_vars_no_values (list)
+print_func_list (list)
register SHELL_VAR **list;
{
register int i;
register SHELL_VAR *var;
for (i = 0; list && (var = list[i]); i++)
- if (!invisible_p (var))
- printf ("%s\n", var->name);
+ {
+ printf ("%s ", var->name);
+ print_var_function (var);
+ printf ("\n");
+ }
}
-#endif
-
+
/* Print the value of a single SHELL_VAR. No newline is
output, but the variable is printed in such a way that
it can be read back in. */
print_assignment (var)
SHELL_VAR *var;
{
- if (function_p (var) && var->value)
- {
- printf ("%s=", var->name);
+ if (var_isset (var) == 0)
+ return;
+
+ if (function_p (var))
+ {
+ printf ("%s", var->name);
print_var_function (var);
printf ("\n");
}
#if defined (ARRAY_VARS)
- else if (array_p (var) && var->value)
+ else if (array_p (var))
print_array_assignment (var, 0);
#endif /* ARRAY_VARS */
- else if (var->value)
+ else
{
printf ("%s=", var->name);
print_var_value (var, 1);
{
char *t;
- if (var->value)
+ if (var_isset (var) == 0)
+ return;
+
+ if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var)))
{
- if (quote && contains_shell_metas (var->value))
- {
- t = single_quote (var->value);
- printf ("%s", t);
- free (t);
- }
- else
- printf ("%s", var->value);
+ t = ansic_quote (value_cell (var), 0, (int *)0);
+ printf ("%s", t);
+ free (t);
+ }
+ else if (quote && sh_contains_shell_metas (value_cell (var)))
+ {
+ t = sh_single_quote (value_cell (var));
+ printf ("%s", t);
+ free (t);
}
+ else
+ printf ("%s", value_cell (var));
}
/* Print the function cell of VAR, a shell variable. Do not
print_var_function (var)
SHELL_VAR *var;
{
- if (function_p (var) && var->value)
+ if (function_p (var) && var_isset (var))
printf ("%s", named_function_string ((char *)NULL, function_cell(var), 1));
}
-#if defined (ARRAY_VARS)
-void
-print_array_assignment (var, quoted)
- SHELL_VAR *var;
- int quoted;
-{
- char *vstr;
-
- if (quoted)
- vstr = quoted_array_assignment_string (array_cell (var));
- else
- vstr = array_to_assignment_string (array_cell (var));
-
- if (vstr == 0)
- printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
- else
- {
- printf ("%s=%s\n", var->name, vstr);
- free (vstr);
- }
-}
-#endif /* ARRAY_VARS */
-
/* **************************************************************** */
/* */
-/* Dynamic Variable Extension */
+/* Dynamic Variables */
/* */
/* **************************************************************** */
These are variables whose values are generated anew each time they are
referenced. These are implemented using a pair of function pointers
- in the struct variable: assign_func, which is called from bind_variable,
- and dynamic_value, which is called from find_variable.
-
- assign_func is called from bind_variable, if bind_variable discovers
- that the variable being assigned to has such a function. The function
- is called as
- SHELL_VAR *temp = (*(entry->assign_func)) (entry, value)
+ in the struct variable: assign_func, which is called from bind_variable
+ and, if arrays are compiled into the shell, some of the functions in
+ arrayfunc.c, and dynamic_value, which is called from find_variable.
+
+ assign_func is called from bind_variable_internal, if
+ bind_variable_internal discovers that the variable being assigned to
+ has such a function. The function is called as
+ SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind)
and the (SHELL_VAR *)temp is returned as the value of bind_variable. It
- is usually ENTRY (self).
+ is usually ENTRY (self). IND is an index for an array variable, and
+ unused otherwise.
- dynamic_value is called from find_variable to return a `new' value for
- the specified dynamic varible. If this function is NULL, the variable
- is treated as a `normal' shell variable. If it is not, however, then
- this function is called like this:
- tempvar = (*(var->dynamic_value)) (var);
+ dynamic_value is called from find_variable_internal to return a `new'
+ value for the specified dynamic varible. If this function is NULL,
+ the variable is treated as a `normal' shell variable. If it is not,
+ however, then this function is called like this:
+ tempvar = (*(var->dynamic_value)) (var);
Sometimes `tempvar' will replace the value of `var'. Other times, the
shell will simply use the string value. Pretty object-oriented, huh?
special meaning, even if you subsequently set it.
The special assignment code would probably have been better put in
- subst.c: do_assignment, in the same style as
+ subst.c: do_assignment_internal, in the same style as
stupidly_hack_special_variables, but I wanted the changes as
localized as possible. */
+#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
+ do \
+ { \
+ v = bind_variable (var, (val)); \
+ v->dynamic_value = gfunc; \
+ v->assign_func = afunc; \
+ } \
+ while (0)
+
+#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \
+ do \
+ { \
+ v = make_new_array_variable (var); \
+ v->dynamic_value = gfunc; \
+ v->assign_func = afunc; \
+ } \
+ while (0)
+
+static SHELL_VAR *
+null_assign (self, value, unused)
+ SHELL_VAR *self;
+ char *value;
+ arrayind_t unused;
+{
+ return (self);
+}
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *
+null_array_assign (self, value, ind)
+ SHELL_VAR *self;
+ char *value;
+ arrayind_t ind;
+{
+ return (self);
+}
+#endif
+
+/* Degenerate `dynamic_value' function; just returns what's passed without
+ manipulation. */
+static SHELL_VAR *
+get_self (self)
+ SHELL_VAR *self;
+{
+ return (self);
+}
+
+#if defined (ARRAY_VARS)
+/* A generic dynamic array variable initializer. Intialize array variable
+ NAME with dynamic value function GETFUNC and assignment function SETFUNC. */
+static SHELL_VAR *
+init_dynamic_array_var (name, getfunc, setfunc, attrs)
+ char *name;
+ sh_var_value_func_t *getfunc;
+ sh_var_assign_func_t *setfunc;
+ int attrs;
+{
+ SHELL_VAR *v;
+
+ v = find_variable (name);
+ if (v)
+ return (v);
+ INIT_DYNAMIC_ARRAY_VAR (name, getfunc, setfunc);
+ if (attrs)
+ VSETATTR (v, attrs);
+ return v;
+}
+#endif
+
+
/* The value of $SECONDS. This is the number of seconds since shell
invocation, or, the number of seconds since the last assignment + the
value of the last assignment. */
-static long seconds_value_assigned;
+static intmax_t seconds_value_assigned;
static SHELL_VAR *
-assign_seconds (self, value)
+assign_seconds (self, value, unused)
SHELL_VAR *self;
char *value;
+ arrayind_t unused;
{
- seconds_value_assigned = string_to_long (value);
+ if (legal_number (value, &seconds_value_assigned) == 0)
+ seconds_value_assigned = 0;
shell_start_time = NOW;
return (self);
}
char *p;
time_since_start = NOW - shell_start_time;
- p = itos((int) seconds_value_assigned + time_since_start);
+ p = itos(seconds_value_assigned + time_since_start);
- FREE (var->value);
+ FREE (value_cell (var));
- var->attributes |= att_integer;
- var->value = p;
+ VSETATTR (var, att_integer);
+ var_setvalue (var, p);
return (var);
}
+static SHELL_VAR *
+init_seconds_var ()
+{
+ SHELL_VAR *v;
+
+ v = find_variable ("SECONDS");
+ if (v)
+ {
+ if (legal_number (value_cell(v), &seconds_value_assigned) == 0)
+ seconds_value_assigned = 0;
+ }
+ INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
+ return v;
+}
+
/* The random number seed. You can change this by setting RANDOM. */
static unsigned long rseed = 1;
-static unsigned long last_random_value;
+static int last_random_value;
-/* 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;
-#if 0
- return ((unsigned int)(rseed / 65536) % 32768);
-#else
- return ((unsigned int)(rseed % 32768));
-#endif
+ return ((unsigned int)((rseed >> 16) & 32767)); /* was % 32768 */
}
/* Set the random number generator seed to SEED. */
static void
sbrand (seed)
- int seed;
+ unsigned long seed;
{
rseed = seed;
last_random_value = 0;
}
static SHELL_VAR *
-assign_random (self, value)
+assign_random (self, value, unused)
SHELL_VAR *self;
char *value;
+ arrayind_t unused;
{
- sbrand (atoi (value));
+ sbrand (strtoul (value, (char **)NULL, 10));
return (self);
}
-static SHELL_VAR *
-get_random (var)
- SHELL_VAR *var;
+int
+get_random_number ()
{
int rv;
- char *p;
/* Reset for command and process substitution. */
if (subshell_environment)
- sbrand (rseed + (int)(getpid() + NOW));
+ sbrand (rseed + getpid() + NOW);
do
rv = brand ();
- while (rv == (int)last_random_value);
+ while (rv == last_random_value);
+ return rv;
+}
+
+static SHELL_VAR *
+get_random (var)
+ SHELL_VAR *var;
+{
+ int rv;
+ char *p;
+ rv = get_random_number ();
last_random_value = rv;
- p = itos ((int)rv);
+ p = itos (rv);
- FREE (var->value);
+ FREE (value_cell (var));
- var->attributes |= att_integer;
- var->value = p;
+ VSETATTR (var, att_integer);
+ var_setvalue (var, p);
return (var);
}
+static SHELL_VAR *
+assign_lineno (var, value, unused)
+ SHELL_VAR *var;
+ char *value;
+ arrayind_t unused;
+{
+ intmax_t new_value;
+
+ if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
+ new_value = 0;
+ line_number = new_value;
+ return var;
+}
+
/* Function which returns the current line number. */
static SHELL_VAR *
get_lineno (var)
ln = executing_line_number ();
p = itos (ln);
- FREE (var->value);
- var->value = p;
+ FREE (value_cell (var));
+ var_setvalue (var, p);
return (var);
}
static SHELL_VAR *
-assign_lineno (var, value)
+assign_subshell (var, value, unused)
SHELL_VAR *var;
char *value;
+ arrayind_t unused;
{
- line_number = atoi (value);
+ intmax_t new_value;
+
+ if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
+ new_value = 0;
+ subshell_level = new_value;
return var;
}
+static SHELL_VAR *
+get_subshell (var)
+ SHELL_VAR *var;
+{
+ char *p;
+
+ p = itos (subshell_level);
+ FREE (value_cell (var));
+ var_setvalue (var, p);
+ return (var);
+}
+
+static SHELL_VAR *
+get_bash_command (var)
+ SHELL_VAR *var;
+{
+ char *p;
+
+ p = savestring (the_printed_command_except_trap);
+ FREE (value_cell (var));
+ var_setvalue (var, p);
+ return (var);
+}
+
#if defined (HISTORY)
static SHELL_VAR *
get_histcmd (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)
+#if defined (READLINE)
+/* When this function returns, VAR->value points to malloced memory. */
static SHELL_VAR *
-get_dirstack (self)
+get_comp_wordbreaks (var)
+ SHELL_VAR *var;
+{
+ char *p;
+
+ /* If we don't have anything yet, assign a default value. */
+ if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0)
+ enable_hostname_completion (perform_hostname_completion);
+
+#if 0
+ FREE (value_cell (var));
+ p = savestring (rl_completer_word_break_characters);
+
+ var_setvalue (var, p);
+#else
+ var_setvalue (var, rl_completer_word_break_characters);
+#endif
+
+ return (var);
+}
+
+/* When this function returns, rl_completer_word_break_characters points to
+ malloced memory. */
+static SHELL_VAR *
+assign_comp_wordbreaks (self, value, unused)
SHELL_VAR *self;
+ char *value;
+ arrayind_t unused;
{
- ARRAY *a;
- WORD_LIST *l;
+ if (rl_completer_word_break_characters &&
+ rl_completer_word_break_characters != rl_basic_word_break_characters)
+ free (rl_completer_word_break_characters);
- l = get_directory_stack ();
- a = word_list_to_array (l);
- dispose_array (array_cell (self));
- dispose_words (l);
- self->value = (char *)a;
+ rl_completer_word_break_characters = savestring (value);
return self;
}
+#endif /* READLINE */
-static SHELL_VAR *
-assign_dirstack (self, ind, value)
+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
+static SHELL_VAR *
+assign_dirstack (self, value, ind)
SHELL_VAR *self;
- int ind;
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;
+}
#endif /* PUSHD AND POPD && ARRAY_VARS */
#if defined (ARRAY_VARS)
group_set = get_group_list (&ng);
a = array_cell (self);
for (i = 0; i < ng; i++)
- array_add_element (a, i, group_set[i]);
+ array_insert (a, i, group_set[i]);
}
return (self);
}
#endif /* ARRAY_VARS */
-
+
+/* If ARRAY_VARS is not defined, this just returns the name of any
+ currently-executing function. If we have arrays, it's a call stack. */
+static SHELL_VAR *
+get_funcname (self)
+ SHELL_VAR *self;
+{
+#if ! defined (ARRAY_VARS)
+ char *t;
+ if (variable_context && this_shell_function)
+ {
+ FREE (value_cell (self));
+ t = savestring (this_shell_function->name);
+ var_setvalue (self, t);
+ }
+#endif
+ return (self);
+}
+
+void
+make_funcname_visible (on_or_off)
+ int on_or_off;
+{
+ SHELL_VAR *v;
+
+ v = find_variable ("FUNCNAME");
+ if (v == 0 || v->dynamic_value == 0)
+ return;
+
+ if (on_or_off)
+ VUNSETATTR (v, att_invisible);
+ else
+ VSETATTR (v, att_invisible);
+}
+
+static SHELL_VAR *
+init_funcname_var ()
+{
+ SHELL_VAR *v;
+
+ v = find_variable ("FUNCNAME");
+ if (v)
+ return v;
+#if defined (ARRAY_VARS)
+ INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign);
+#else
+ INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
+#endif
+ VSETATTR (v, att_invisible|att_noassign);
+ return v;
+}
+
static void
initialize_dynamic_variables ()
{
SHELL_VAR *v;
- v = bind_variable ("SECONDS", (char *)NULL);
- v->dynamic_value = get_seconds;
- v->assign_func = assign_seconds;
+ v = init_seconds_var ();
- v = bind_variable ("RANDOM", (char *)NULL);
- v->dynamic_value = get_random;
- v->assign_func = assign_random;
+ INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL);
+ INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell);
- v = bind_variable ("LINENO", (char *)NULL);
- v->dynamic_value = get_lineno;
- v->assign_func = assign_lineno;
+ INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
+ 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 (READLINE)
+ INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks);
#endif
#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
- v = make_new_array_variable ("DIRSTACK");
- v->dynamic_value = get_dirstack;
- v->assign_func = assign_dirstack;
+ v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0);
#endif /* PUSHD_AND_POPD && ARRAY_VARS */
#if defined (ARRAY_VARS)
- v = make_new_array_variable ("GROUPS");
- v->dynamic_value = get_groupset;
- v->assign_func = (DYNAMIC_FUNC *)NULL;
- v->attributes |= att_readonly;
+ v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign);
+
+# if defined (DEBUGGER)
+ v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign);
+ v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign);
+# endif /* DEBUGGER */
+ v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign);
+ v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign);
#endif
+
+ v = init_funcname_var ();
}
+/* **************************************************************** */
+/* */
+/* Retrieving variables and values */
+/* */
+/* **************************************************************** */
+
/* How to get a pointer to the shell variable or function named NAME.
HASHED_VARS is a pointer to the hash table containing the list
of interest (either variables or functions). */
-SHELL_VAR *
-var_lookup (name, hashed_vars)
- char *name;
+
+static SHELL_VAR *
+hash_lookup (name, hashed_vars)
+ const char *name;
HASH_TABLE *hashed_vars;
{
BUCKET_CONTENTS *bucket;
- bucket = find_hash_item (name, hashed_vars);
+ bucket = hash_search (name, hashed_vars, 0);
return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
}
+SHELL_VAR *
+var_lookup (name, vcontext)
+ const char *name;
+ VAR_CONTEXT *vcontext;
+{
+ VAR_CONTEXT *vc;
+ SHELL_VAR *v;
+
+ v = (SHELL_VAR *)NULL;
+ for (vc = vcontext; vc; vc = vc->down)
+ if (v = hash_lookup (name, vc->table))
+ break;
+
+ return v;
+}
+
/* Look up the variable entry named NAME. If SEARCH_TEMPENV is non-zero,
- then also search the temporarily built list of exported variables. */
+ then also search the temporarily built list of exported variables.
+ The lookup order is:
+ temporary_env
+ shell_variables list
+*/
+
SHELL_VAR *
-find_variable_internal (name, search_tempenv)
- char *name;
- int search_tempenv;
+find_variable_internal (name, force_tempenv)
+ const char *name;
+ int force_tempenv;
{
- SHELL_VAR *var = (SHELL_VAR *)NULL;
+ SHELL_VAR *var;
+ int search_tempenv;
+
+ var = (SHELL_VAR *)NULL;
/* If explicitly requested, first look in the temporary environment for
the variable. This allows constructs such as "foo=x eval 'echo $foo'"
to get the `exported' value of $foo. This happens if we are executing
a function or builtin, or if we are looking up a variable in a
"subshell environment". */
- if ((search_tempenv || subshell_environment) &&
- (temporary_env || builtin_env || function_env))
- var = find_tempenv_variable (name);
+ search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment);
+
+ if (search_tempenv && 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);
return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
/* Look up the variable entry named NAME. Returns the entry or NULL. */
SHELL_VAR *
find_variable (name)
- char *name;
+ const char *name;
{
- return (find_variable_internal
- (name, (variable_context || this_shell_builtin || builtin_env)));
+ return (find_variable_internal (name, (expanding_redir == 0 && this_shell_builtin != 0)));
}
/* Look up the function entry whose name matches STRING.
Returns the entry or NULL. */
SHELL_VAR *
find_function (name)
- char *name;
+ const char *name;
{
- return (var_lookup (name, shell_functions));
+ return (hash_lookup (name, shell_functions));
}
-/* Return the string value of a variable. Return NULL if the variable
- doesn't exist, or only has a function as a value. Don't cons a new
- string. */
-char *
-get_string_value (var_name)
- char *var_name;
+/* Find the function definition for the shell function named NAME. Returns
+ the entry or NULL. */
+FUNCTION_DEF *
+find_function_def (name)
+ const char *name;
{
- SHELL_VAR *var;
-
- var = find_variable (var_name);
+ return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
+}
- if (!var)
- return (char *)NULL;
+/* Return the value of VAR. VAR is assumed to have been the result of a
+ lookup without any subscript, if arrays are compiled into the shell. */
+char *
+get_variable_value (var)
+ SHELL_VAR *var;
+{
+ if (var == 0)
+ return ((char *)NULL);
#if defined (ARRAY_VARS)
else if (array_p (var))
return (array_reference (array_cell (var), 0));
#endif
else
- return (var->value);
+ return (value_cell (var));
}
-/* Create a local variable referenced by NAME. */
-SHELL_VAR *
-make_local_variable (name)
- char *name;
+/* Return the string value of a variable. Return NULL if the variable
+ doesn't exist. Don't cons a new string. This is a potential memory
+ leak if the variable is found in the temporary environment. Since
+ functions and variables have separate name spaces, returns NULL if
+ var_name is a shell function only. */
+char *
+get_string_value (var_name)
+ const char *var_name;
{
- SHELL_VAR *new_var, *old_var;
- BUCKET_CONTENTS *elt;
+ SHELL_VAR *var;
- /* local foo; local foo; is a no-op. */
- old_var = find_variable (name);
- if (old_var && old_var->context == variable_context)
- return (old_var);
+ var = find_variable (var_name);
+ return ((var) ? get_variable_value (var) : (char *)NULL);
+}
- elt = remove_hash_item (name, shell_variables);
- if (elt)
- {
- old_var = (SHELL_VAR *)elt->data;
- free (elt->key);
- free (elt);
- }
- else
- old_var = (SHELL_VAR *)NULL;
+/* 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);
+}
- /* If a variable does not already exist with this name, then
- just make a new one. */
- if (old_var == 0)
- new_var = bind_variable (name, "");
- else
- {
- new_var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+/* **************************************************************** */
+/* */
+/* Creating and setting variables */
+/* */
+/* **************************************************************** */
- new_var->name = savestring (name);
- new_var->value = xmalloc (1);
- new_var->value[0] = '\0';
+/* Set NAME to VALUE if NAME has no value. */
+SHELL_VAR *
+set_if_not (name, value)
+ char *name, *value;
+{
+ SHELL_VAR *v;
- new_var->dynamic_value = (DYNAMIC_FUNC *)NULL;
- new_var->assign_func = (DYNAMIC_FUNC *)NULL;
+ v = find_variable (name);
+ if (v == 0)
+ v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH);
+ return (v);
+}
- new_var->attributes = exported_p (old_var) ? att_exported : 0;
+/* Create a local variable referenced by NAME. */
+SHELL_VAR *
+make_local_variable (name)
+ const char *name;
+{
+ SHELL_VAR *new_var, *old_var;
+ VAR_CONTEXT *vc;
+ int was_tmpvar;
+ char *tmp_value;
- new_var->prev_context = old_var;
- elt = add_hash_item (savestring (name), shell_variables);
- elt->data = (char *)new_var;
+ /* local foo; local foo; is a no-op. */
+ old_var = find_variable (name);
+ if (old_var && local_p (old_var) && old_var->context == variable_context)
+ {
+ VUNSETATTR (old_var, att_invisible);
+ return (old_var);
}
- new_var->context = variable_context;
- new_var->attributes |= att_local;
+ 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)
+ {
+ internal_error (_("make_local_variable: no function context at current scope"));
+ return ((SHELL_VAR *)NULL);
+ }
+ 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)))
+ {
+ if (readonly_p (old_var))
+ sh_readonly (name);
+ return ((SHELL_VAR *)NULL);
+ }
- /* XXX */
- if (variable_context >= local_variable_stack_size)
+ if (old_var == 0)
+ new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH);
+ else
{
- int old_size = local_variable_stack_size;
- RESIZE_MALLOCED_BUFFER (have_local_variables, variable_context, 1,
- local_variable_stack_size, 8);
- bzero ((char *)have_local_variables + old_size,
- local_variable_stack_size - old_size);
+ new_var = make_new_variable (name, vc->table);
+
+ /* If we found this variable in one of the temporary environments,
+ inherit its value. Watch to see if this causes problems with
+ things like `x=4 local x'. */
+ if (was_tmpvar)
+ var_setvalue (new_var, savestring (tmp_value));
+
+ new_var->attributes = exported_p (old_var) ? att_exported : 0;
}
- have_local_variables[variable_context] = 1; /* XXX */
+
+ vc->flags |= VC_HASLOCAL;
+
+ new_var->context = variable_context;
+ VSETATTR (new_var, att_local);
+
+ if (ifsname (name))
+ setifs (new_var);
return (new_var);
}
ARRAY *array;
var = make_local_variable (name);
- array = new_array ();
+ if (var == 0 || array_p (var))
+ return var;
+
+ array = array_create ();
FREE (value_cell(var));
- var->value = (char *)array;
- var->attributes |= att_array;
+ var_setarray (var, array);
+ VSETATTR (var, att_array);
return var;
}
#endif /* ARRAY_VARS */
-/* Create a new shell variable with name NAME and add it to the hash table
- of shell variables. */
-static
-SHELL_VAR *
-make_new_variable (name)
- char *name;
+/* Create a new shell variable with name NAME. */
+static SHELL_VAR *
+new_shell_variable (name)
+ const char *name;
{
SHELL_VAR *entry;
- BUCKET_CONTENTS *elt;
entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
- entry->attributes = 0;
entry->name = savestring (name);
- entry->value = (char *)NULL;
+ var_setvalue (entry, (char *)NULL);
+ CLEAR_EXPORTSTR (entry);
- entry->dynamic_value = (DYNAMIC_FUNC *)NULL;
- entry->assign_func = (DYNAMIC_FUNC *)NULL;
+ entry->dynamic_value = (sh_var_value_func_t *)NULL;
+ entry->assign_func = (sh_var_assign_func_t *)NULL;
+
+ entry->attributes = 0;
/* Always assume variables are to be made at toplevel!
make_local_variable has the responsibilty of changing the
variable context. */
entry->context = 0;
- entry->prev_context = (SHELL_VAR *)NULL;
- elt = add_hash_item (savestring (name), shell_variables);
- elt->data = (char *)entry;
+ return (entry);
+}
+
+/* Create a new shell variable with name NAME and add it to the hash table
+ TABLE. */
+static SHELL_VAR *
+make_new_variable (name, table)
+ const char *name;
+ HASH_TABLE *table;
+{
+ SHELL_VAR *entry;
+ BUCKET_CONTENTS *elt;
+
+ entry = new_shell_variable (name);
+
+ /* Make sure we have a shell_variables hash table to add to. */
+ if (shell_variables == 0)
+ {
+ shell_variables = global_variables = new_var_context ((char *)NULL, 0);
+ shell_variables->scope = 0;
+ shell_variables->table = hash_create (0);
+ }
+
+ elt = hash_insert (savestring (name), table, HASH_NOSRCH);
+ elt->data = (PTR_T)entry;
return entry;
}
SHELL_VAR *entry;
ARRAY *array;
- entry = make_new_variable (name);
- array = new_array ();
- entry->value = (char *)array;
- entry->attributes |= att_array;
+ entry = make_new_variable (name, global_variables->table);
+ array = array_create ();
+ var_setarray (entry, array);
+ VSETATTR (entry, att_array);
return entry;
}
#endif
char *value;
{
char *retval;
- long lval;
+ 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
+ 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))
retval = savestring (value);
else
{
- retval = xmalloc (1);
+ retval = (char *)xmalloc (1);
retval[0] = '\0';
}
}
return retval;
}
-/* Bind a variable NAME to VALUE. This conses up the name
- and value strings. */
-SHELL_VAR *
-bind_variable (name, value)
- char *name, *value;
+/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
+ temporary environment (but usually is not). */
+static SHELL_VAR *
+bind_variable_internal (name, value, table, hflags)
+ const char *name;
+ char *value;
+ HASH_TABLE *table;
+ int hflags;
{
char *newval;
SHELL_VAR *entry;
- entry = var_lookup (name, shell_variables);
+ entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
if (entry == 0)
{
- entry = make_new_variable (name);
- entry->value = make_variable_value (entry, value);
+ entry = make_new_variable (name, table);
+ var_setvalue (entry, make_variable_value (entry, value));
+ }
+ else if (entry->assign_func) /* array vars have assign functions now */
+ {
+ INVALIDATE_EXPORTSTR (entry);
+ return ((*(entry->assign_func)) (entry, value, -1));
}
-#if defined (ARRAY_VARS)
- else if (entry->assign_func && array_p (entry) == 0)
-#else
- else if (entry->assign_func)
-#endif
- return ((*(entry->assign_func)) (entry, value));
else
{
- if (readonly_p (entry))
+ if (readonly_p (entry) || noassign_p (entry))
{
- report_error ("%s: readonly variable", name);
+ if (readonly_p (entry))
+ err_readonly (name);
return (entry);
}
/* Variables which are bound are visible. */
- entry->attributes &= ~att_invisible;
+ VUNSETATTR (entry, att_invisible);
newval = make_variable_value (entry, value);
+ /* Invalidate any cached export string */
+ INVALIDATE_EXPORTSTR (entry);
+
#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
x[0]=b or `read x[0]'. */
if (array_p (entry))
{
- array_add_element (array_cell (entry), 0, newval);
+ array_insert (array_cell (entry), 0, newval);
free (newval);
}
else
+#endif
{
- FREE (entry->value);
- entry->value = newval;
+ FREE (value_cell (entry));
+ var_setvalue (entry, newval);
}
-#else
- FREE (entry->value);
- entry->value = newval;
-#endif
}
if (mark_modified_vars)
- entry->attributes |= att_exported;
+ VSETATTR (entry, att_exported);
if (exported_p (entry))
array_needs_making = 1;
return (entry);
}
+
+/* Bind a variable NAME to VALUE. This conses up the name
+ and value strings. If we have a temporary environment, we bind there
+ first, then we bind into shell_variables. */
-#if defined (ARRAY_VARS)
-/* Convert a shell variable to an array variable. The original value is
- saved as array[0]. */
SHELL_VAR *
-convert_var_to_array (var)
- SHELL_VAR *var;
+bind_variable (name, value)
+ const char *name;
+ char *value;
{
- char *oldval;
- ARRAY *array;
+ SHELL_VAR *v;
+ VAR_CONTEXT *vc;
- oldval = value_cell (var);
- array = new_array ();
- array_add_element (array, 0, oldval);
- FREE (value_cell (var));
- var->value = (char *)array;
- var->attributes |= att_array;
- var->attributes &= ~att_invisible;
+ if (shell_variables == 0)
+ {
+ shell_variables = global_variables = new_var_context ((char *)NULL, 0);
+ shell_variables->scope = 0;
+ shell_variables->table = hash_create (0);
+ }
- return var;
-}
+ /* If we have a temporary environment, look there first for the variable,
+ and, if found, modify the value there before modifying it in the
+ shell_variables table. This allows sourced scripts to modify values
+ given to them in a temporary environment while modifying the variable
+ value that the caller sees. */
+ if (temporary_env)
+ bind_tempenv_variable (name, value);
-/* Perform an array assignment name[ind]=value. If NAME already exists and
- is not an array, and IND is 0, perform name=value instead. If NAME exists
- and is not an array, and IND is not 0, convert it into an array with the
- existing value as name[0].
+ /* XXX -- handle local variables here. */
+ for (vc = shell_variables; vc; vc = vc->down)
+ {
+ if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
+ {
+ v = hash_lookup (name, vc->table);
+ if (v)
+ return (bind_variable_internal (name, value, vc->table, 0));
+ }
+ }
+ return (bind_variable_internal (name, value, global_variables->table, 0));
+}
- If NAME does not exist, just create an array variable, no matter what
- IND's value may be. */
+/* Make VAR, a simple shell variable, have value VALUE. Once assigned a
+ value, variables are no longer invisible. This is a duplicate of part
+ of the internals of bind_variable. If the variable is exported, or
+ all modified variables should be exported, mark the variable for export
+ and note that the export environment needs to be recreated. */
SHELL_VAR *
-bind_array_variable (name, ind, value)
- char *name;
- int ind;
+bind_variable_value (var, value)
+ SHELL_VAR *var;
char *value;
{
- SHELL_VAR *entry;
- char *newval;
+ char *t;
- entry = var_lookup (name, shell_variables);
+ VUNSETATTR (var, att_invisible);
- if (entry == (SHELL_VAR *) 0)
- entry = make_new_array_variable (name);
- else if (readonly_p (entry))
- {
- report_error ("%s: readonly variable", name);
- return (entry);
- }
- else if (array_p (entry) == 0)
- entry = convert_var_to_array (entry);
+ t = make_variable_value (var, value);
+ FREE (value_cell (var));
+ var_setvalue (var, t);
- /* ENTRY is an array variable, and ARRAY points to the value. */
- newval = make_variable_value (entry, value);
- if (entry->assign_func)
- (*entry->assign_func) (entry, ind, newval);
- else
- array_add_element (array_cell (entry), ind, newval);
- FREE (newval);
+ INVALIDATE_EXPORTSTR (var);
- return (entry);
+ if (mark_modified_vars)
+ VSETATTR (var, att_exported);
+
+ if (exported_p (var))
+ array_needs_making = 1;
+
+ return (var);
}
+/* Bind/create a shell variable with the name LHS to the RHS.
+ This creates or modifies a variable such that it is an integer.
+
+ This used to be in expr.c, but it is here so that all of the
+ variable binding stuff is localized. Since we don't want any
+ recursive evaluation from bind_variable() (possible without this code,
+ since bind_variable() calls the evaluator for variables with the integer
+ attribute set), we temporarily turn off the integer attribute for each
+ variable we set here, then turn it back on after binding as necessary. */
+
SHELL_VAR *
-assign_array_from_string (name, value)
- char *name, *value;
+bind_int_variable (lhs, rhs)
+ char *lhs, *rhs;
{
- SHELL_VAR *var;
+ register SHELL_VAR *v;
+ char *t;
+ int isint, isarr;
- var = find_variable (name);
- if (var == 0)
- var = make_new_array_variable (name);
- else if (readonly_p (var))
+ isint = isarr = 0;
+#if defined (ARRAY_VARS)
+# if 0
+ if (t = xstrchr (lhs, '[')) /*]*/
+# else
+ if (valid_array_reference (lhs))
+# endif
{
- report_error ("%s: readonly variable", name);
- return ((SHELL_VAR *)NULL);
+ isarr = 1;
+ v = array_variable_part (lhs, (char **)0, (int *)0);
}
- else if (array_p (var) == 0)
- var = convert_var_to_array (var);
-#if 0
else
- empty_array (array_cell (var));
#endif
+ v = find_variable (lhs);
+
+ if (v)
+ {
+ isint = integer_p (v);
+ VUNSETATTR (v, att_integer);
+ }
+
+#if defined (ARRAY_VARS)
+ if (isarr)
+ v = assign_array_element (lhs, rhs);
+ else
+#endif
+ v = bind_variable (lhs, rhs);
+
+ if (isint)
+ VSETATTR (v, att_integer);
- return (assign_array_var_from_string (var, value));
+ return (v);
}
SHELL_VAR *
-assign_array_var_from_word_list (var, list)
- SHELL_VAR *var;
- WORD_LIST *list;
+bind_var_to_int (var, val)
+ char *var;
+ intmax_t val;
{
- register int i;
- register WORD_LIST *l;
- ARRAY *a;
+ char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
- for (a = array_cell (var), l = list, i = 0; l; l = l->next, i++)
- if (var->assign_func)
- (*var->assign_func) (var, i, l->word->word);
- else
- array_add_element (a, i, l->word->word);
- return var;
+ 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 *
-assign_array_var_from_string (var, value)
- SHELL_VAR *var;
- char *value;
+bind_function (name, value)
+ const char *name;
+ COMMAND *value;
{
- ARRAY *a;
- WORD_LIST *list, *nlist;
- char *w, *val, *nval;
- int ni, len, ind, last_ind;
-
- a = array_cell (var);
+ SHELL_VAR *entry;
- /* Expand the value string into a list of words, performing all the
- shell expansions including word splitting. */
- if (*value == '(')
+ entry = find_function (name);
+ if (entry == 0)
{
- ni = 1;
- val = extract_array_assignment_list (value, &ni);
- if (val == 0)
- return var;
- nlist = expand_string (val, 0);
- free (val);
+ BUCKET_CONTENTS *elt;
+
+ elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
+ entry = new_shell_variable (name);
+ elt->data = (PTR_T)entry;
}
else
- nlist = expand_string (value, 0);
+ INVALIDATE_EXPORTSTR (entry);
-#if 1
- /* Now that we are ready to assign values to the array, kill the existing
- value. */
- if (a)
- empty_array (a);
-#endif
-
- for (last_ind = 0, list = nlist; list; list = list->next)
- {
- w = list->word->word;
+ if (var_isset (entry))
+ dispose_command (function_cell (entry));
- /* We have a word of the form [ind]=value */
- if (w[0] == '[')
- {
- len = skipsubscript (w, 0);
+ if (value)
+ var_setfunc (entry, copy_command (value));
+ else
+ var_setfunc (entry, 0);
- if (w[len] != ']' || w[len+1] != '=')
- {
- nval = make_variable_value (var, w);
- if (var->assign_func)
- (*var->assign_func) (var, last_ind, nval);
- else
- array_add_element (a, last_ind, nval);
- FREE (nval);
- last_ind++;
- continue;
- }
+ VSETATTR (entry, att_function);
- if (len == 1)
- {
- report_error ("%s: bad array subscript", w);
- continue;
- }
+ if (mark_modified_vars)
+ VSETATTR (entry, att_exported);
- if (ALL_ELEMENT_SUB (w[1]) && len == 2)
- {
- report_error ("%s: cannot assign to non-numeric index", w);
- continue;
- }
+ VUNSETATTR (entry, att_invisible); /* Just to be sure */
- ind = array_expand_index (w + 1, len);
- if (ind < 0)
- {
- report_error ("%s: bad array subscript", w);
- continue;
- }
- last_ind = ind;
- val = w + len + 2;
- }
- else /* No [ind]=value, just a stray `=' */
- {
- ind = last_ind;
- val = w;
- }
+ if (exported_p (entry))
+ array_needs_making = 1;
- if (integer_p (var))
- this_command_name = (char *)NULL; /* no command name for errors */
- nval = make_variable_value (var, val);
- if (var->assign_func)
- (*var->assign_func) (var, ind, nval);
- else
- array_add_element (a, ind, nval);
- FREE (nval);
- last_ind++;
- }
+#if defined (PROGRAMMABLE_COMPLETION)
+ set_itemlist_dirty (&it_functions);
+#endif
- dispose_words (nlist);
- return (var);
+ return (entry);
}
-#endif /* ARRAY_VARS */
-/* Dispose of the information attached to VAR. */
+/* Bind a function definition, which includes source file and line number
+ information in addition to the command, into the FUNCTION_DEF hash table.*/
void
-dispose_variable (var)
- SHELL_VAR *var;
+bind_function_def (name, value)
+ const char *name;
+ FUNCTION_DEF *value;
{
- if (!var)
- return;
+ FUNCTION_DEF *entry;
+ BUCKET_CONTENTS *elt;
+ COMMAND *cmd;
- if (function_p (var))
- dispose_command (function_cell (var));
-#if defined (ARRAY_VARS)
- else if (array_p (var))
- dispose_array (array_cell (var));
-#endif
+ entry = find_function_def (name);
+ if (entry)
+ {
+ dispose_function_def_contents (entry);
+ entry = copy_function_def_contents (value, entry);
+ }
else
- FREE (value_cell (var));
-
- free (var->name);
-
- if (exported_p (var))
- array_needs_making = 1;
+ {
+ cmd = value->command;
+ value->command = 0;
+ entry = copy_function_def (value);
+ value->command = cmd;
- free (var);
+ elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH);
+ elt->data = (PTR_T *)entry;
+ }
}
-#if defined (ARRAY_VARS)
-/* This function is called with SUB pointing to just after the beginning
- `[' of an array subscript. */
+/* 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
-unbind_array_element (var, sub)
- SHELL_VAR *var;
- char *sub;
+assign_in_env (string)
+ const char *string;
{
- int len, ind;
- ARRAY_ELEMENT *ae;
+ int offset;
+ char *name, *temp, *value;
+ SHELL_VAR *var;
- len = skipsubscript (sub, 0);
- if (sub[len] != ']' || len == 0)
- {
- builtin_error ("%s[%s: bad array subscript", var->name, sub);
- return -1;
- }
- sub[len] = '\0';
+ offset = assignment (string, 0);
+ name = savestring (string);
+ value = (char *)NULL;
- if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
- {
- makunbound (var->name, shell_variables);
- return (0);
- }
- ind = array_expand_index (sub, len+1);
- if (ind < 0)
+ if (name[offset] == '=')
{
- builtin_error ("[%s]: bad array subscript", sub);
- return -1;
+ 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);
+ }
+
+ temp = name + offset + 1;
+ temp = (xstrchr (temp, '~') != 0) ? bash_tilde_expand (temp, 1) : savestring (temp);
+
+ value = expand_string_unsplit_to_string (temp, 0);
+ free (temp);
}
- ae = array_delete_element (array_cell (var), ind);
- if (ae)
- destroy_array_element (ae);
- return 0;
-}
-#endif
-/* Unset the variable referenced by NAME. */
-int
-unbind_variable (name)
- char *name;
-{
- SHELL_VAR *var = find_variable (name);
+ if (temporary_env == 0)
+ temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
- if (!var)
- return (-1);
+ var = hash_lookup (name, temporary_env);
+ if (var == 0)
+ var = make_new_variable (name, temporary_env);
+ else
+ FREE (value_cell (var));
- /* This function should never be called with an array variable name. */
-#if defined (ARRAY_VARS)
- if (array_p (var) == 0 && var->value)
-#else
- if (var->value)
-#endif
+ if (value == 0)
{
- free (var->value);
- var->value = (char *)NULL;
+ value = (char *)xmalloc (1); /* like do_assignment_internal */
+ value[0] = '\0';
}
- makunbound (name, shell_variables);
+ var_setvalue (var, value);
+ var->attributes |= (att_exported|att_tempvar);
+ var->context = variable_context; /* XXX */
- return (0);
-}
+ INVALIDATE_EXPORTSTR (var);
+ var->exportstr = mk_env_string (name, value);
-/* 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, hash_list)
- char *name;
- HASH_TABLE *hash_list;
-{
- BUCKET_CONTENTS *elt, *new_elt;
- SHELL_VAR *old_var, *new_var;
- char *t;
+ array_needs_making = 1;
- elt = remove_hash_item (name, hash_list);
+ if (ifsname (name))
+ setifs (var);
- if (elt == 0)
- return (-1);
+ if (echo_command_at_execute)
+ /* The Korn shell prints the `+ ' in front of assignment statements,
+ so we do too. */
+ xtrace_print_assignment (name, value, 0, 1);
- old_var = (SHELL_VAR *)elt->data;
- new_var = old_var->prev_context;
+ free (name);
+ return 1;
+}
- if (old_var && exported_p (old_var))
- array_needs_making++;
+/* **************************************************************** */
+/* */
+/* Copying variables */
+/* */
+/* **************************************************************** */
- /* If we're unsetting a local variable and we're still executing inside
- the function, just mark the variable as invisible.
- kill_all_local_variables will clean it up later. This must be done
- so that if the variable is subsequently assigned a new value inside
- the function, the `local' attribute is still present. We also need
- to add it back into the correct hash table. */
- if (old_var && local_p (old_var) && variable_context == old_var->context)
- {
- old_var->attributes |= att_invisible;
- new_elt = add_hash_item (savestring (old_var->name), hash_list);
- new_elt->data = (char *)old_var;
- stupidly_hack_special_variables (old_var->name);
- free (elt->key);
- free (elt);
- return (0);
- }
+#ifdef INCLUDE_UNUSED
+/* Copy VAR to a new data structure and return that structure. */
+SHELL_VAR *
+copy_variable (var)
+ SHELL_VAR *var;
+{
+ SHELL_VAR *copy = (SHELL_VAR *)NULL;
- if (new_var)
+ if (var)
{
- /* Has to be a variable, functions don't have previous contexts. */
- new_elt = add_hash_item (savestring (new_var->name), hash_list);
- new_elt->data = (char *)new_var;
-
- if (exported_p (new_var))
- set_auto_export (new_var);
- }
+ copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
- /* 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);
+ copy->attributes = var->attributes;
+ copy->name = savestring (var->name);
- free (elt->key);
- free (elt);
+ if (function_p (var))
+ var_setfunc (copy, copy_command (function_cell (var)));
+#if defined (ARRAY_VARS)
+ else if (array_p (var))
+ var_setarray (copy, dup_array (array_cell (var)));
+#endif
+ else if (value_cell (var))
+ var_setvalue (copy, savestring (value_cell (var)));
+ else
+ var_setvalue (copy, (char *)NULL);
- dispose_variable (old_var);
- stupidly_hack_special_variables (t);
- free (t);
- return (0);
-}
+ copy->dynamic_value = var->dynamic_value;
+ copy->assign_func = var->assign_func;
-#ifdef INCLUDE_UNUSED
-/* Remove the variable with NAME if it is a local variable in the
- current context. */
-int
-kill_local_variable (name)
- char *name;
-{
- SHELL_VAR *temp;
+ copy->exportstr = COPY_EXPORTSTR (var);
- temp = find_variable (name);
- if (temp && temp->context == variable_context)
- {
- makunbound (name, shell_variables);
- return (0);
+ copy->context = var->context;
}
- return (-1);
+ return (copy);
}
#endif
-/* 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);
-}
+/* **************************************************************** */
+/* */
+/* Deleting and unsetting variables */
+/* */
+/* **************************************************************** */
+/* Dispose of the information attached to VAR. */
void
-kill_all_local_variables ()
+dispose_variable (var)
+ SHELL_VAR *var;
{
- register int i, pass;
- register SHELL_VAR *var, **list;
- HASH_TABLE *varlist;
-
- /* If HAVE_LOCAL_VARIABLES == 0, it means that we don't have any local
- variables at all. If VARIABLE_CONTEXT >= LOCAL_VARIABLE_STACK_SIZE,
- it means that we have some local variables, but not in this variable
- context (level of function nesting). Also, if
- HAVE_LOCAL_VARIABLES[VARIABLE_CONTEXT] == 0, we have no local variables
- at this context. */
- if (have_local_variables == 0 ||
- variable_context >= local_variable_stack_size ||
- have_local_variables[variable_context] == 0)
+ if (var == 0)
return;
- for (pass = 0; pass < 2; pass++)
- {
- varlist = pass ? shell_functions : shell_variables;
+ 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));
- list = map_over (variable_in_context, varlist);
+ FREE_EXPORTSTR (var);
- if (list)
- {
- for (i = 0; var = list[i]; i++)
- {
- var->attributes &= ~att_local;
- makunbound (var->name, varlist);
- }
- free (list);
- }
- }
+ free (var->name);
- have_local_variables[variable_context] = 0; /* XXX */
+ if (exported_p (var))
+ array_needs_making = 1;
+
+ free (var);
}
-static void
-free_variable_hash_data (data)
- char *data;
+/* Unset the shell variable referenced by NAME. */
+int
+unbind_variable (name)
+ const char *name;
{
- SHELL_VAR *var, *prev;
+ return makunbound (name, shell_variables);
+}
- var = (SHELL_VAR *)data;
- while (var)
+/* Unset the shell function named NAME. */
+int
+unbind_func (name)
+ const char *name;
+{
+ BUCKET_CONTENTS *elt;
+ SHELL_VAR *func;
+
+ 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)
{
- prev = var->prev_context;
- dispose_variable (var);
- var = prev;
+ if (exported_p (func))
+ array_needs_making++;
+ dispose_variable (func);
}
-}
-/* Delete the entire contents of the hash table. */
-void
-delete_all_variables (hashed_vars)
- HASH_TABLE *hashed_vars;
-{
- flush_hash_table (hashed_vars, free_variable_hash_data);
+ free (elt->key);
+ free (elt);
+
+ return 0;
}
-static SHELL_VAR *
-new_shell_variable (name)
- char *name;
+int
+unbind_function_def (name)
+ const char *name;
{
- SHELL_VAR *var;
+ BUCKET_CONTENTS *elt;
+ FUNCTION_DEF *funcdef;
- var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+ elt = hash_remove (name, shell_function_defs, 0);
- bzero ((char *)var, sizeof (SHELL_VAR));
- var->name = savestring (name);
- return (var);
+ if (elt == 0)
+ return -1;
+
+ funcdef = (FUNCTION_DEF *)elt->data;
+ if (funcdef)
+ dispose_function_def (funcdef);
+
+ free (elt->key);
+ free (elt);
+
+ return 0;
}
-/* Do a function binding to a variable. You pass the name and
- the command to bind to. This conses the name and command. */
-SHELL_VAR *
-bind_function (name, value)
- char *name;
- COMMAND *value;
+/* 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;
+ BUCKET_CONTENTS *elt, *new_elt;
+ SHELL_VAR *old_var;
+ VAR_CONTEXT *v;
+ char *t;
- entry = find_function (name);
- if (!entry)
- {
- BUCKET_CONTENTS *elt;
+ for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
+ if (elt = hash_remove (name, v->table, 0))
+ break;
- elt = add_hash_item (savestring (name), shell_functions);
+ if (elt == 0)
+ return (-1);
- elt->data = (char *)new_shell_variable (name);
- entry = (SHELL_VAR *)elt->data;
- entry->dynamic_value = entry->assign_func = (DYNAMIC_FUNC *)NULL;
+ old_var = (SHELL_VAR *)elt->data;
- /* Functions are always made at the top level. This allows a
- function to define another function (like autoload). */
- entry->context = 0;
- }
+ if (old_var && exported_p (old_var))
+ array_needs_making++;
- if (entry->value)
- dispose_command ((COMMAND *)entry->value);
+ /* 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)
+ {
+ /* Reset the attributes. Preserve the export attribute if the variable
+ came from a temporary environment. Make sure it stays local, and
+ make it invisible. */
+ old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0;
+ VSETATTR (old_var, att_local);
+ VSETATTR (old_var, att_invisible);
+ 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);
- entry->value = value ? (char *)copy_command (value) : (char *)NULL;
- entry->attributes |= att_function;
+ free (elt->key);
+ free (elt);
+ return (0);
+ }
- if (mark_modified_vars)
- entry->attributes |= att_exported;
+ /* 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);
- entry->attributes &= ~att_invisible; /* Just to be sure */
+ 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 (entry);
+ return (0);
}
-#ifdef INCLUDE_UNUSED
-/* Copy VAR to a new data structure and return that structure. */
-SHELL_VAR *
-copy_variable (var)
- SHELL_VAR *var;
+/* Get rid of all of the variables in the current context. */
+void
+kill_all_local_variables ()
{
- SHELL_VAR *copy = (SHELL_VAR *)NULL;
-
- if (var)
- {
- copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+ VAR_CONTEXT *vc;
- copy->attributes = var->attributes;
- copy->name = savestring (var->name);
+ for (vc = shell_variables; vc; vc = vc->down)
+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
+ break;
+ if (vc == 0)
+ return; /* XXX */
- if (function_p (var))
- copy->value = (char *)copy_command (function_cell (var));
-#if defined (ARRAY_VARS)
- else if (array_p (var))
- copy->value = (char *)dup_array (array_cell (var));
-#endif
- else if (value_cell (var))
- copy->value = savestring (value_cell (var));
- else
- copy->value = (char *)NULL;
+ if (vc->table && vc_haslocals (vc))
+ {
+ delete_all_variables (vc->table);
+ hash_dispose (vc->table);
+ }
+ vc->table = (HASH_TABLE *)NULL;
+}
- copy->dynamic_value = var->dynamic_value;
- copy->assign_func = var->assign_func;
+static void
+free_variable_hash_data (data)
+ PTR_T data;
+{
+ SHELL_VAR *var;
- copy->context = var->context;
+ var = (SHELL_VAR *)data;
+ dispose_variable (var);
+}
- /* Don't bother copying previous contexts along with this variable. */
- copy->prev_context = (SHELL_VAR *)NULL;
- }
- return (copy);
+/* 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);
}
-#endif
+
+/* **************************************************************** */
+/* */
+/* Setting variable attributes */
+/* */
+/* **************************************************************** */
#define FIND_OR_MAKE_VARIABLE(name, entry) \
do \
SHELL_VAR *entry;
FIND_OR_MAKE_VARIABLE (name, entry);
- entry->attributes |= att_readonly;
+ VSETATTR (entry, att_readonly);
}
+#ifdef INCLUDE_UNUSED
/* Make the function associated with NAME be readonly.
If NAME does not exist, we just punt, like auto_export code below. */
void
set_func_read_only (name)
- char *name;
+ const char *name;
{
SHELL_VAR *entry;
entry = find_function (name);
if (entry)
- entry->attributes |= att_readonly;
+ VSETATTR (entry, att_readonly);
}
/* Make the variable associated with NAME be auto-exported.
/* Make the function associated with NAME be auto-exported. */
void
set_func_auto_export (name)
- char *name;
+ const char *name;
{
SHELL_VAR *entry;
if (entry)
set_auto_export (entry);
}
+#endif
-#if defined (ARRAY_VARS)
-/* This function assumes s[i] == '['; returns with s[ret] == ']' if
- an array subscript is correctly parsed. */
-int
-skipsubscript (s, i)
- char *s;
- int i;
+/* **************************************************************** */
+/* */
+/* Creating lists of variables */
+/* */
+/* **************************************************************** */
+
+static VARLIST *
+vlist_alloc (nentries)
+ int nentries;
{
- int count, c;
+ VARLIST *vlist;
+
+ vlist = (VARLIST *)xmalloc (sizeof (VARLIST));
+ vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *));
+ vlist->list_size = nentries;
+ vlist->list_len = 0;
+ vlist->list[0] = (SHELL_VAR *)NULL;
- for (count = 1; count && (c = s[++i]); )
+ 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)
{
- if (c == '[')
- count++;
- else if (c == ']')
- count--;
+ vlist->list_size = n;
+ vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
}
- return i;
+ return vlist;
}
-#endif /* ARRAY_VARS */
-/* Returns non-zero if STRING is an assignment statement. The returned value
- is the index of the `=' sign. */
-int
-assignment (string)
- char *string;
+static void
+vlist_add (vlist, var, flags)
+ VARLIST *vlist;
+ SHELL_VAR *var;
+ int flags;
{
- register int c, newi, indx;
+ register int i;
- c = string[indx = 0];
+ for (i = 0; i < vlist->list_len; i++)
+ if (STREQ (var->name, vlist->list[i]->name))
+ break;
+ if (i < vlist->list_len)
+ return;
- if (legal_variable_starter (c) == 0)
- return (0);
+ if (i >= vlist->list_size)
+ vlist = vlist_realloc (vlist, vlist->list_size + 16);
- while (c = string[indx])
- {
- /* The following is safe. Note that '=' at the start of a word
- is not an assignment statement. */
- if (c == '=')
- return (indx);
+ vlist->list[vlist->list_len++] = var;
+ vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
+}
-#if defined (ARRAY_VARS)
- if (c == '[')
- {
- newi = skipsubscript (string, indx);
- if (string[newi++] != ']')
- return (0);
- return ((string[newi] == '=') ? newi : 0);
- }
-#endif /* ARRAY_VARS */
+/* Map FUNCTION over the variables in VAR_HASH_TABLE. Return an array of the
+ variables for which FUNCTION returns a non-zero value. A NULL value
+ for FUNCTION means to use all variables. */
+SHELL_VAR **
+map_over (function, vc)
+ sh_var_map_func_t *function;
+ VAR_CONTEXT *vc;
+{
+ VAR_CONTEXT *v;
+ VARLIST *vlist;
+ SHELL_VAR **ret;
+ int nentries;
- /* Variable names in assignment statements may contain only letters,
- digits, and `_'. */
- if (legal_variable_char (c) == 0)
- return (0);
+ for (nentries = 0, v = vc; v; v = v->down)
+ nentries += HASH_ENTRIES (v->table);
- indx++;
- }
- return (0);
+ 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
-visible_var (var)
- SHELL_VAR *var;
+qsort_var_comp (var1, var2)
+ SHELL_VAR **var1, **var2;
{
- return (invisible_p (var) == 0);
+ int result;
+
+ if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
+ result = strcmp ((*var1)->name, (*var2)->name);
+
+ return (result);
}
+/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for
+ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */
static SHELL_VAR **
-_visible_names (table)
- HASH_TABLE *table;
+vapply (func)
+ sh_var_map_func_t *func;
{
SHELL_VAR **list;
- list = map_over (visible_var, table);
-
+ list = map_over (func, shell_variables);
if (list /* && posixly_correct */)
sort_variables (list);
+ return (list);
+}
+
+/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for
+ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */
+static SHELL_VAR **
+fapply (func)
+ sh_var_map_func_t *func;
+{
+ SHELL_VAR **list;
+ list = map_over_funcs (func);
+ if (list /* && posixly_correct */)
+ sort_variables (list);
return (list);
}
+/* Create a NULL terminated array of all the shell variables. */
SHELL_VAR **
-all_visible_variables ()
+all_shell_variables ()
+{
+ return (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 (_visible_names (shell_variables));
+ return (invisible_p (var) == 0);
}
SHELL_VAR **
all_visible_functions ()
{
- return (_visible_names (shell_functions));
+ return (fapply (visible_var));
+}
+
+SHELL_VAR **
+all_visible_variables ()
+{
+ return (vapply (visible_var));
}
/* Return non-zero if the variable VAR is visible and exported. Array
return (invisible_p (var) == 0 && exported_p (var));
}
-/* Make an array of assignment statements from the hash table
- HASHED_VARS which contains SHELL_VARs. Only visible, exported
- variables are eligible. */
+/* 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"));
+ 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 **
-make_var_array (hashed_vars)
- HASH_TABLE *hashed_vars;
+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;
- SHELL_VAR **vars;
-
- vars = map_over (visible_and_exported, hashed_vars);
- if (vars == 0)
- return (char **)NULL;
+ list = strvec_create ((1 + strvec_len ((char **)vars)));
- list = (char **)xmalloc ((1 + array_len ((char **)vars)) * sizeof (char *));
+#define USE_EXPORTSTR (value == var->exportstr)
for (i = 0, list_index = 0; var = vars[i]; i++)
{
- if (function_p (var))
+#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 (value)
{
- int name_len, value_len;
- char *p;
-
- name_len = strlen (var->name);
- value_len = strlen (value);
- p = list[list_index] = xmalloc (2 + name_len + value_len);
- strcpy (p, var->name);
- p[name_len] = '=';
- strcpy (p + name_len + 1, value);
+ /* 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
}
}
- free (vars);
list[list_index] = (char *)NULL;
return (list);
}
-/* Add STRING to the array of foo=bar strings that we already
- have to add to the environment. */
-int
-assign_in_env (string)
- char *string;
+/* 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, 0);
+ 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;
+
+ vcxt = shell_variables;
+ if (vc_isfuncenv (vcxt) == 0)
+ {
+ internal_error (_("pop_var_context: head of shell_variables not a function context"));
+ return;
+ }
+
+ if (ret = vcxt->down)
+ {
+ ret->up = (VAR_CONTEXT *)NULL;
+ shell_variables = ret;
+ if (vcxt->table)
+ hash_flush (vcxt->table, push_func_var);
+ dispose_var_context (vcxt);
+ }
+ else
+ internal_error (_("pop_var_context: no global_variables context"));
+}
+
+/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
+ all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
+void
+delete_all_contexts (vcxt)
+ VAR_CONTEXT *vcxt;
+{
+ VAR_CONTEXT *v, *t;
+
+ for (v = vcxt; v != global_variables; v = t)
+ {
+ t = v->down;
+ dispose_var_context (v);
+ }
+
+ delete_all_variables (global_variables->table);
+ shell_variables = global_variables;
+}
+
+/* **************************************************************** */
+/* */
+/* Pushing and Popping temporary variable scopes */
+/* */
+/* **************************************************************** */
+
+VAR_CONTEXT *
+push_scope (flags, tmpvars)
+ int flags;
+ HASH_TABLE *tmpvars;
+{
+ return (push_var_context ((char *)NULL, flags, tmpvars));
+}
+
+static void
+push_exported_var (data)
+ PTR_T data;
+{
+ SHELL_VAR *var, *v;
+
+ var = (SHELL_VAR *)data;
+
+ /* If a temp var had its export attribute set, or it's marked to be
+ propagated, bind it in the previous scope before disposing it. */
+ if (exported_p (var) || (var->attributes & att_propagate))
+ {
+ 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;
+ }
+
+ dispose_variable (var);
+}
+
+void
+pop_scope (is_special)
+ int is_special;
+{
+ 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;
+ }
+
+ ret = vcxt->down;
+ if (ret)
+ ret->up = (VAR_CONTEXT *)NULL;
+
+ shell_variables = ret;
+
+ /* Now we can take care of merging variables in VCXT into set of scopes
+ whose head is RET (shell_variables). */
+ FREE (vcxt->name);
+ if (vcxt->table)
+ {
+ if (is_special)
+ hash_flush (vcxt->table, push_func_var);
+ else
+ hash_flush (vcxt->table, push_exported_var);
+ hash_dispose (vcxt->table);
+ }
+ free (vcxt);
+
+ sv_ifs ("IFS"); /* XXX here for now */
+}
+
+/* **************************************************************** */
+/* */
+/* Pushing and Popping function contexts */
+/* */
+/* **************************************************************** */
+
+static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
+static int dollar_arg_stack_slots;
+static int dollar_arg_stack_index;
+
+/* XXX - we might want to consider pushing and popping the `getopts' state
+ when we modify the positional parameters. */
+void
+push_context (name, is_subshell, tempvars)
+ char *name; /* function name */
+ int is_subshell;
+ HASH_TABLE *tempvars;
+{
+ if (is_subshell == 0)
+ push_dollar_vars ();
+ variable_context++;
+ push_var_context (name, VC_FUNCENV, tempvars);
+}
+
+/* Only called when subshell == 0, so we don't need to check, and can
+ unconditionally pop the dollar vars off the stack. */
+void
+pop_context ()
+{
+ pop_dollar_vars ();
+ variable_context--;
+ pop_var_context ();
+
+ sv_ifs ("IFS"); /* XXX here for now */
+}
+
+/* Save the existing positional parameters on a stack. */
+void
+push_dollar_vars ()
+{
+ if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
+ {
+ dollar_arg_stack = (WORD_LIST **)
+ xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
+ * sizeof (WORD_LIST **));
+ }
+ dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+}
+
+/* Restore the positional parameters from our stack. */
+void
+pop_dollar_vars ()
+{
+ if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+ return;
+
+ remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
+ dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+ set_dollar_vars_unchanged ();
+}
+
+void
+dispose_saved_dollar_vars ()
+{
+ if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+ return;
+
+ dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+}
+
+/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */
+
+void
+push_args (list)
+ WORD_LIST *list;
+{
+#if defined (ARRAY_VARS) && defined (DEBUGGER)
+ SHELL_VAR *bash_argv_v, *bash_argc_v;
+ ARRAY *bash_argv_a, *bash_argc_a;
+ WORD_LIST *l;
+ arrayind_t i;
+ char *t;
+
+ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
+ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
+
+ for (l = list, i = 0; l; l = l->next, i++)
+ array_push (bash_argv_a, l->word->word);
+
+ t = itos (i);
+ array_push (bash_argc_a, t);
+ free (t);
+#endif /* ARRAY_VARS && DEBUGGER */
+}
+
+/* Remove arguments from BASH_ARGV array. Pop top element off BASH_ARGC
+ array and use that value as the count of elements to remove from
+ BASH_ARGV. */
+void
+pop_args ()
{
- int size, offset;
- char *name, *temp, *value;
- int nlen, vlen;
- WORD_LIST *list;
- SHELL_VAR *var;
+#if defined (ARRAY_VARS) && defined (DEBUGGER)
+ SHELL_VAR *bash_argv_v, *bash_argc_v;
+ ARRAY *bash_argv_a, *bash_argc_a;
+ ARRAY_ELEMENT *ce;
+ intmax_t i;
- offset = assignment (string);
- name = savestring (string);
- value = (char *)NULL;
+ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
+ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
-#define freetemp nlen
- if (name[offset] == '=')
- {
- name[offset] = 0;
+ ce = array_shift (bash_argc_a, 1, 0);
+ if (ce == 0 || legal_number (element_value (ce), &i) == 0)
+ i = 0;
- var = find_variable (name);
- if (var && readonly_p (var))
- {
- report_error ("%s: readonly variable", name);
- free (name);
- return (0);
- }
- temp = name + offset + 1;
- freetemp = strchr (temp, '~') != 0;
- if (freetemp)
- temp = bash_tilde_expand (temp);
+ for ( ; i > 0; i--)
+ array_pop (bash_argv_a);
+ array_dispose_element (ce);
+#endif /* ARRAY_VARS && DEBUGGER */
+}
- list = expand_string_unsplit (temp, 0);
- value = string_list (list);
+/*************************************************
+ * *
+ * Functions to manage special variables *
+ * *
+ *************************************************/
- if (list)
- dispose_words (list);
+/* Extern declarations for variables this code has to manage. */
+extern int eof_encountered, eof_encountered_limit, ignoreeof;
- if (freetemp)
- free (temp);
- }
-#undef freetemp
+#if defined (READLINE)
+extern int no_line_editing;
+extern int hostname_list_initialized;
+#endif
- nlen = strlen (name);
- vlen = value ? strlen (value) : 0;
- temp = xmalloc (2 + nlen + vlen);
- strcpy (temp, name);
- temp[nlen] = '=';
- temp[nlen + 1] = '\0';
- if (value)
- {
- if (*value)
- strcpy (temp + nlen + 1, value);
- free (value);
- }
- free (name);
+/* 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. */
- if (temporary_env == 0)
- {
- temporary_env = (char **)xmalloc (sizeof (char *));
- temporary_env [0] = (char *)NULL;
- }
+#define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0
- size = array_len (temporary_env);
- temporary_env = (char **)
- xrealloc (temporary_env, (size + 2) * (sizeof (char *)));
+/* This table will be sorted with qsort() the first time it's accessed. */
+struct name_and_function {
+ char *name;
+ sh_sv_func_t *function;
+};
- temporary_env[size] = temp;
- temporary_env[size + 1] = (char *)NULL;
- array_needs_making = 1;
+static struct name_and_function special_vars[] = {
+ { "GLOBIGNORE", sv_globignore },
- if (echo_command_at_execute)
- {
- /* The Korn shell prints the `+ ' in front of assignment statements,
- so we do too. */
- fprintf (stderr, "%s%s\n", indirection_level_string (), temp);
- fflush (stderr);
- }
+#if defined (HISTORY)
+ { "HISTCONTROL", sv_history_control },
+ { "HISTFILESIZE", sv_histsize },
+ { "HISTIGNORE", sv_histignore },
+ { "HISTSIZE", sv_histsize },
+ { "HISTTIMEFORMAT", sv_histtimefmt },
+#endif
- return 1;
-}
+#if defined (READLINE)
+ { "HOSTFILE", sv_hostfile },
+#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;
+ { "IFS", sv_ifs },
+ { "IGNOREEOF", sv_ignoreeof },
- if (array == 0)
- return ((SHELL_VAR *)NULL);
+ { "LANG", sv_locale },
+ { "LC_ALL", sv_locale },
+ { "LC_COLLATE", sv_locale },
+ { "LC_CTYPE", sv_locale },
+ { "LC_MESSAGES", sv_locale },
+ { "LC_NUMERIC", sv_locale },
- for (i = 0, l = strlen (name); array[i]; i++)
- {
- if (STREQN (array[i], name, l) && array[i][l] == '=')
- {
- SHELL_VAR *temp;
- char *w;
+ { "MAIL", sv_mail },
+ { "MAILCHECK", sv_mail },
+ { "MAILPATH", sv_mail },
- temp = new_shell_variable (name); /* XXX memory leak here */
- w = array[i] + l + 1;
+ { "OPTERR", sv_opterr },
+ { "OPTIND", sv_optind },
- temp->value = *w ? savestring (w) : (char *)NULL;
+ { "PATH", sv_path },
+ { "POSIXLY_CORRECT", sv_strict_posix },
- temp->attributes = att_exported;
- temp->context = 0;
- temp->prev_context = (SHELL_VAR *)NULL;
+#if defined (READLINE)
+ { "TERM", sv_terminal },
+ { "TERMCAP", sv_terminal },
+ { "TERMINFO", sv_terminal },
+#endif /* READLINE */
- temp->dynamic_value = temp->assign_func = (DYNAMIC_FUNC *)NULL;
+ { "TEXTDOMAIN", sv_locale },
+ { "TEXTDOMAINDIR", sv_locale },
- return (temp);
- }
- }
- return ((SHELL_VAR *)NULL);
-}
+#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+ { "TZ", sv_tz },
+#endif
-/* Find a variable in the temporary environment that is named NAME.
- The temporary environment can be either the environment provided
- to a simple command, or the environment provided to a shell function.
- We only search the function environment if we are currently executing
- a shell function body (variable_context > 0). Return a consed variable,
- or NULL if not found. */
-SHELL_VAR *
-find_tempenv_variable (name)
- char *name;
-{
- SHELL_VAR *var = (SHELL_VAR *)NULL;
+#if defined (HISTORY) && defined (BANG_HISTORY)
+ { "histchars", sv_histchars },
+#endif /* HISTORY && BANG_HISTORY */
- if (temporary_env)
- var = find_name_in_env_array (name, temporary_env);
+ { "ignoreeof", sv_ignoreeof },
- /* 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);
+ { (char *)0, (sh_sv_func_t *)0 }
+};
- if (!var && variable_context && function_env)
- var = find_name_in_env_array (name, function_env);
+#define N_SPECIAL_VARS (sizeof (special_vars) / sizeof (special_vars[0]) - 1)
- return (var);
+static int
+sv_compare (sv1, sv2)
+ struct name_and_function *sv1, *sv2;
+{
+ int r;
+
+ if ((r = sv1->name[0] - sv2->name[0]) == 0)
+ r = strcmp (sv1->name, sv2->name);
+ return r;
}
-/* 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;
+static inline int
+find_special_var (name)
+ const char *name;
{
- if (!*arrayp)
- return;
+ register int i, r;
- free_array (*arrayp);
- *arrayp = (char **)NULL;
- array_needs_making = 1;
+ for (i = 0; special_vars[i].name; i++)
+ {
+ r = special_vars[i].name[0] - name[0];
+ if (r == 0)
+ r = strcmp (special_vars[i].name, name);
+ if (r == 0)
+ return i;
+ else if (r > 0)
+ /* Can't match any of rest of elements in sorted list. Take this out
+ if it causes problems in certain environments. */
+ break;
+ }
+ return -1;
}
-/* Free the storage used in the variable array for temporary
- environment variables. */
+/* 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
-dispose_used_env_vars ()
+stupidly_hack_special_variables (name)
+ char *name;
{
- dispose_temporary_vars (&temporary_env);
+ static int sv_sorted = 0;
+ int i;
+
+ if (sv_sorted == 0) /* shouldn't need, but it's fairly cheap. */
+ {
+ qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]),
+ (QSFUNC *)sv_compare);
+ sv_sorted = 1;
+ }
+
+ i = find_special_var (name);
+ if (i != -1)
+ (*(special_vars[i].function)) (name);
}
-/* Free the storage used for temporary environment variables given to
- commands when executing inside of a function body. */
void
-dispose_function_env ()
+sv_ifs (name)
+ char *name;
{
- dispose_temporary_vars (&function_env);
+ SHELL_VAR *v;
+
+ v = find_variable ("IFS");
+ setifs (v);
}
-/* Free the storage used for temporary environment variables given to
- commands when executing a builtin command such as "source". */
+/* What to do just after the PATH variable has changed. */
void
-dispose_builtin_env ()
+sv_path (name)
+ char *name;
{
- dispose_temporary_vars (&builtin_env);
+ /* hash -r */
+ phash_flush ();
}
-/* Take all of the shell variables in ENV_ARRAY and make shell variables
- from them at the current variable context. */
-static void
-merge_env_array (env_array)
- char **env_array;
+/* What to do just after one of the MAILxxxx variables has changed. NAME
+ is the name of the variable. This is called with NAME set to one of
+ MAIL, MAILCHECK, or MAILPATH. */
+void
+sv_mail (name)
+ char *name;
{
- register int i, l;
- SHELL_VAR *temp;
- char *val, *name;
-
- if (env_array == 0)
- return;
-
- for (i = 0; env_array[i]; i++)
+ /* 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
{
- l = assignment (env_array[i]);
- name = env_array[i];
- val = env_array[i] + l + 1;
- name[l] = '\0';
- temp = bind_variable (name, val);
- name[l] = '=';
+ free_mail_files ();
+ remember_mail_dates ();
}
}
+/* What to do when GLOBIGNORE changes. */
void
-merge_temporary_env ()
+sv_globignore (name)
+ char *name;
{
- merge_env_array (temporary_env);
+ setup_glob_ignore (name);
}
+#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
-merge_builtin_env ()
+sv_terminal (name)
+ char *name;
{
- merge_env_array (builtin_env);
+ if (interactive_shell && no_line_editing == 0)
+ rl_reset_terminal (get_string_value ("TERM"));
}
-/* 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 = (char **)xrealloc (export_env, export_env_size * sizeof (char *)); \
- } \
- export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \
- export_env[export_env_index] = (char *)NULL; \
- } while (0)
+void
+sv_hostfile (name)
+ char *name;
+{
+ SHELL_VAR *v;
-#define ISFUNC(s, o) ((s[o + 1] == '(') && (s[o + 2] == ')'))
+ v = find_variable (name);
+ if (v == 0)
+ clear_hostname_list ();
+ else
+ hostname_list_initialized = 0;
+}
+#endif /* READLINE */
-/* 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;
+#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;
{
- register int i;
- int equal_offset;
-
- equal_offset = assignment (assign);
- if (equal_offset == 0)
- return (export_env);
+ 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; i < export_env_index; i++)
+ if (temp && *temp)
{
- if (STREQN (assign, export_env[i], equal_offset + 1))
+ if (legal_number (temp, &num))
{
- free (export_env[i]);
- export_env[i] = do_alloc ? savestring (assign) : assign;
- return (export_env);
+ 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;
+ }
}
}
- add_to_export_env (assign, do_alloc);
- return (export_env);
+ 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;
- int new_size;
+ setup_history_ignore (name);
+}
- if (array_needs_making)
+/* What to do after the HISTCONTROL variable changes. */
+void
+sv_history_control (name)
+ char *name;
+{
+ char *temp;
+ char *val;
+ int tptr;
+
+ history_control = 0;
+ temp = get_string_value (name);
+
+ if (temp == 0 || *temp == 0)
+ return;
+
+ tptr = 0;
+ while (val = extract_colon_unit (temp, &tptr))
{
- if (export_env)
- free_array_members (export_env);
+ if (STREQ (val, "ignorespace"))
+ history_control |= HC_IGNSPACE;
+ else if (STREQ (val, "ignoredups"))
+ history_control |= HC_IGNDUPS;
+ else if (STREQ (val, "ignoreboth"))
+ history_control |= HC_IGNBOTH;
+ else if (STREQ (val, "erasedups"))
+ history_control |= HC_ERASEDUPS;
- /* 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, without any temporary
- or function environments. */
- new_size = shell_variables->nentries + shell_functions->nentries + 1;
- if (new_size > export_env_size)
- {
- export_env_size = new_size;
- export_env = (char **)xrealloc (export_env, export_env_size * sizeof (char *));
- }
- export_env[export_env_index = 0] = (char *)NULL;
+ free (val);
+ }
+}
- temp_array = make_var_array (shell_variables);
- if (temp_array)
- {
- for (i = 0; temp_array[i]; i++)
- add_to_export_env (temp_array[i], 0);
- free (temp_array);
- }
+#if defined (BANG_HISTORY)
+/* Setting/unsetting of the history expansion character. */
+void
+sv_histchars (name)
+ char *name;
+{
+ char *temp;
- temp_array = make_var_array (shell_functions);
- if (temp_array)
+ temp = get_string_value (name);
+ if (temp)
+ {
+ history_expansion_char = *temp;
+ if (temp[0] && temp[1])
{
- for (i = 0; temp_array[i]; i++)
- add_to_export_env (temp_array[i], 0);
- free (temp_array);
+ 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 */
- if (function_env)
- for (i = 0; function_env[i]; i++)
- export_env = add_or_supercede_exported_var (function_env[i], 1);
-
- if (temporary_env)
- for (i = 0; temporary_env[i]; i++)
- export_env = add_or_supercede_exported_var (temporary_env[i], 1);
+void
+sv_histtimefmt (name)
+ char *name;
+{
+ SHELL_VAR *v;
-#if 0
- /* If we changed the array, then sort it alphabetically. */
- if (posixly_correct == 0 && (temporary_env || function_env))
- sort_char_array (export_env);
-#endif
+ v = find_variable (name);
+ history_write_timestamps = (v != 0);
+}
+#endif /* HISTORY */
- array_needs_making = 0;
- }
+#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+void
+sv_tz (name)
+ char *name;
+{
+ tzset ();
}
+#endif
-/* We always put _ in the environment as the name of this command. */
+/* 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
-put_command_name_into_env (command_name)
- char *command_name;
+sv_ignoreeof (name)
+ char *name;
{
- char *dummy;
+ SHELL_VAR *tmp_var;
+ char *temp;
- dummy = xmalloc (4 + strlen (command_name));
+ eof_encountered = 0;
- /* These three statements replace a call to sprintf */
- dummy[0] = '_';
- dummy[1] = '=';
- strcpy (dummy + 2, command_name);
- export_env = add_or_supercede_exported_var (dummy, 0);
+ tmp_var = find_variable (name);
+ ignoreeof = tmp_var != 0;
+ temp = tmp_var ? value_cell (tmp_var) : (char *)NULL;
+ if (temp)
+ eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10;
+ set_shellopts (); /* make sure `ignoreeof' is/is not in $SHELLOPTS */
}
void
-put_gnu_argv_flags_into_env (pid, flags_string)
- int pid;
- char *flags_string;
+sv_optind (name)
+ char *name;
{
- char *dummy, *pbuf;
- int l, fl;
-
- pbuf = itos (pid);
- l = strlen (pbuf);
+ char *tt;
+ int s;
- fl = strlen (flags_string);
+ tt = get_string_value ("OPTIND");
+ if (tt && *tt)
+ {
+ s = atoi (tt);
- dummy = 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);
+ /* 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);
+}
- free (pbuf);
+void
+sv_opterr (name)
+ char *name;
+{
+ char *tt;
- export_env = add_or_supercede_exported_var (dummy, 0);
+ tt = get_string_value ("OPTERR");
+ sh_opterr = (tt && *tt) ? atoi (tt) : 1;
}
-/* Return a string denoting what our indirection level is. */
-static char indirection_string[100];
+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 */
+}
-char *
-indirection_level_string ()
+void
+sv_locale (name)
+ char *name;
{
- register int i, j;
- char *ps4;
+ char *v;
+
+ v = get_string_value (name);
+ if (name[0] == 'L' && name[1] == 'A') /* LANG */
+ set_lang (name, v);
+ else
+ set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */
+}
- indirection_string[0] = '\0';
- ps4 = get_string_value ("PS4");
+#if defined (ARRAY_VARS)
+void
+set_pipestatus_array (ps, nproc)
+ int *ps;
+ int nproc;
+{
+ SHELL_VAR *v;
+ ARRAY *a;
+ ARRAY_ELEMENT *ae;
+ register int i;
+ char *t, tbuf[INT_STRLEN_BOUND(int) + 1];
- if (ps4 == 0 || *ps4 == '\0')
- return (indirection_string);
+ 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);
- ps4 = decode_prompt_string (ps4);
+ if (a == 0 || array_num_elements (a) == 0)
+ {
+ for (i = 0; i < nproc; i++) /* was ps[i] != -1, not i < nproc */
+ {
+ t = inttostr (ps[i], tbuf, sizeof (tbuf));
+ array_insert (a, i, t);
+ }
+ return;
+ }
- for (i = 0; *ps4 && i < indirection_level && i < 99; i++)
- indirection_string[i] = *ps4;
+ /* Fast case */
+ if (array_num_elements (a) == nproc && nproc == 1)
+ {
+ ae = element_forw (a->head);
+ free (element_value (ae));
+ ae->value = itos (ps[0]);
+ }
+ else if (array_num_elements (a) <= nproc)
+ {
+ /* modify in array_num_elements members in place, then add */
+ ae = a->head;
+ for (i = 0; i < array_num_elements (a); i++)
+ {
+ ae = element_forw (ae);
+ free (element_value (ae));
+ ae->value = itos (ps[i]);
+ }
+ /* add any more */
+ for ( ; i < nproc; i++)
+ {
+ t = inttostr (ps[i], tbuf, sizeof (tbuf));
+ array_insert (a, i, t);
+ }
+ }
+ else
+ {
+ /* deleting elements. it's faster to rebuild the array. */
+ array_flush (a);
+ for (i = 0; ps[i] != -1; i++)
+ {
+ t = inttostr (ps[i], tbuf, sizeof (tbuf));
+ array_insert (a, i, t);
+ }
+ }
+}
+#endif
- for (j = 1; *ps4 && ps4[j] && i < 99; i++, j++)
- indirection_string[i] = ps4[j];
+void
+set_pipestatus_from_exit (s)
+ int s;
+{
+#if defined (ARRAY_VARS)
+ static int v[2] = { 0, -1 };
- indirection_string[i] = '\0';
- free (ps4);
- return (indirection_string);
+ v[0] = s;
+ set_pipestatus_array (v, 1);
+#endif
}