/* variables.c -- Functions for hacking shell variables. */
-/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2010 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 2, or (at your option)
- any later version.
+ 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 3 of the License, or
+ (at your option) any later version.
- Bash is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
- License for more details.
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with Bash; see the file COPYING. If not, write to the Free
- Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
#include "config.h"
#include "bashtypes.h"
#include "posixstat.h"
+#include "posixtime.h"
-#if defined (qnx)
-# include <sys/vc.h>
-#endif
+#if defined (__QNX__)
+# if defined (__QNXNTO__)
+# include <sys/netmgr.h>
+# else
+# include <sys/vc.h>
+# endif /* !__QNXNTO__ */
+#endif /* __QNX__ */
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <stdio.h>
-#include <ctype.h>
-#include <pwd.h>
+#include "chartypes.h"
+#if defined (HAVE_PWD_H)
+# include <pwd.h>
+#endif
#include "bashansi.h"
+#include "bashintl.h"
+
+#define NEED_XTRACE_SET_DECL
#include "shell.h"
#include "flags.h"
#include "findcmd.h"
#include "mailcheck.h"
#include "input.h"
+#include "hashcmd.h"
+#include "pathexp.h"
+#include "alias.h"
#include "builtins/getopt.h"
#include "builtins/common.h"
# include "pcomplete.h"
#endif
+#define TEMPENV_HASH_BUCKETS 4 /* must be power of two */
+
+#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
+
+extern char **environ;
+
/* Variables used here and defined in other files. */
extern int posixly_correct;
-extern int variable_context, line_number;
-extern int interactive, interactive_shell, login_shell;
-extern int subshell_environment, indirection_level;
+extern int line_number, line_number_base;
+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;
+extern int assigning_in_environment;
+extern int executing_builtin;
+extern int funcnest_max;
-/* The list of shell variables that the user has created, or that came from
- the environment. */
-HASH_TABLE *shell_variables = (HASH_TABLE *)NULL;
+#if defined (READLINE)
+extern int no_line_editing;
+extern int perform_hostname_completion;
+#endif
+
+/* The list of shell variables that the user has created at the global
+ scope, or that came from the environment. */
+VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL;
+
+/* The current list of shell variables, including function scopes */
+VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL;
/* The list of shell functions that the user has created, or that came from
the environment. */
HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
+#if defined (DEBUGGER)
+/* The table of shell function definitions that the user defined or that
+ came from the environment. */
+HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL;
+#endif
+
/* The current variable context. This is really a count of how deep into
executing functions we are. */
int variable_context = 0;
-/* The array of shell assignments which are made only in the environment
+/* The set of shell assignments which are made only in the environment
for a single command. */
-char **temporary_env = (char **)NULL;
-
-/* 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;
+
+/* Non-zero means that we have to remake EXPORT_ENV. */
+int array_needs_making = 1;
+
+/* The number of times BASH has been executed. This is set
+ by initialize_variables (). */
+int shell_level = 0;
/* An array which is passed to commands as their environment. It is
manufactured from the union of the initial environment and the
static int export_env_index;
static int export_env_size;
-/* Non-zero means that we have to remake EXPORT_ENV. */
-int array_needs_making = 1;
+#if defined (READLINE)
+static int winsize_assignment; /* currently assigning to LINES or COLUMNS */
+#endif
-/* The number of times BASH has been executed. This is set
- by initialize_variables (). */
-int shell_level = 0;
+/* Some forward declarations. */
+static void create_variable_tables __P((void));
+
+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 char *have_local_variables;
-static int local_variable_stack_size;
+static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
+#if defined (ARRAY_VARS)
+static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
+#endif
+static SHELL_VAR *get_self __P((SHELL_VAR *));
-/* 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)
+#if defined (ARRAY_VARS)
+static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
+static SHELL_VAR *init_dynamic_assoc_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, char *));
+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 void seedrand __P((void)); /* seed generator randomly */
+static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_random __P((SHELL_VAR *));
+
+static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_lineno __P((SHELL_VAR *));
+
+static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_subshell __P((SHELL_VAR *));
+
+static SHELL_VAR *get_bashpid __P((SHELL_VAR *));
+
+#if defined (HISTORY)
+static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
+#endif
+
+#if defined (READLINE)
+static SHELL_VAR *get_comp_wordbreaks __P((SHELL_VAR *));
+static SHELL_VAR *assign_comp_wordbreaks __P((SHELL_VAR *, char *, arrayind_t, char *));
+#endif
+
+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
+static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
+#endif
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *get_groupset __P((SHELL_VAR *));
+
+static SHELL_VAR *build_hashcmd __P((SHELL_VAR *));
+static SHELL_VAR *get_hashcmd __P((SHELL_VAR *));
+static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *, char *, arrayind_t, char *));
+# if defined (ALIAS)
+static SHELL_VAR *build_aliasvar __P((SHELL_VAR *));
+static SHELL_VAR *get_aliasvar __P((SHELL_VAR *));
+static SHELL_VAR *assign_aliasvar __P((SHELL_VAR *, char *, arrayind_t, char *));
+# endif
+#endif
+
+static SHELL_VAR *get_funcname __P((SHELL_VAR *));
+static SHELL_VAR *init_funcname_var __P((void));
+
+static void initialize_dynamic_variables __P((void));
+
+static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
+static SHELL_VAR *new_shell_variable __P((const char *));
+static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
+
+static void dispose_variable_value __P((SHELL_VAR *));
+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 export_environment_candidate __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 *));
+
+static void
+create_variable_tables ()
+{
+ if (shell_variables == 0)
+ {
+ shell_variables = global_variables = new_var_context ((char *)NULL, 0);
+ shell_variables->scope = 0;
+ shell_variables->table = hash_create (0);
+ }
+
+ if (shell_functions == 0)
+ shell_functions = hash_create (0);
+
+#if defined (DEBUGGER)
+ if (shell_function_defs == 0)
+ shell_function_defs = hash_create (0);
+#endif
+}
/* Initialize the shell variables from the current environment.
If PRIVMODE is nonzero, don't import functions from ENV or
int c, char_index, string_index, string_length;
SHELL_VAR *temp_var;
- if (shell_variables == 0)
- shell_variables = make_hash_table (0);
-
- if (shell_functions == 0)
- shell_functions = make_hash_table (0);
+ create_variable_tables ();
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) */
+
+ temp_var = (SHELL_VAR *)NULL;
- /* If exported function, define it now. */
+ /* 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);
+ temp_string = (char *)xmalloc (3 + string_length + char_index);
strcpy (temp_string, name);
temp_string[char_index] = ' ';
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')
#if defined (ARRAY_VARS)
# if 0
/* Array variables may not yet be exported. */
- else if (*string == '(' && string[1] == '[' && strchr (string, ')'))
+ else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
{
string_length = 1;
temp_string = extract_array_assignment_list (string, &string_length);
}
# endif
#endif
+#if 0
+ else if (legal_identifier (name))
+#else
else
+#endif
{
- temp_var = bind_variable (name, string);
- VSETATTR (temp_var, (att_exported | att_imported));
- array_needs_making = 1;
+ temp_var = bind_variable (name, string, 0);
+ if (temp_var)
+ {
+ if (legal_identifier (name))
+ VSETATTR (temp_var, (att_exported | att_imported));
+ else
+ VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
+ array_needs_making = 1;
+ }
}
name[char_index] = '=';
/* temp_var can be NULL if it was an exported function with a syntax
- error (a different bug, but it still shouldn't dump core). */
+ error (a different bug, but it still shouldn't dump core). */
if (temp_var && function_p (temp_var) == 0) /* XXX not yet */
{
CACHE_IMPORTSTR (temp_var, name);
}
}
- /* 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 = 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));
+ set_pwd ();
/* Set up initial value of $_ */
- temp_var = bind_variable ("_", dollar_vars[0]);
+ temp_var = set_if_not ("_", 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)
+#if defined (__QNX__)
/* set node id -- don't import it from the environment */
{
char node_name[22];
+# if defined (__QNXNTO__)
+ netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name));
+# else
qnx_nidtostr (getnid (), node_name, sizeof (node_name));
- temp_var = bind_variable ("NODE", node_name);
+# endif
+ temp_var = bind_variable ("NODE", node_name, 0);
set_auto_export (temp_var);
}
#endif
set_if_not ("PS4", "+ ");
/* Don't allow IFS to be imported from the environment. */
- temp_var = bind_variable ("IFS", " \t\n");
+ temp_var = bind_variable ("IFS", " \t\n", 0);
+ setifs (temp_var);
/* Magic machine types. Pretty convenient. */
- temp_var = bind_variable ("HOSTTYPE", HOSTTYPE);
- set_auto_export (temp_var);
- temp_var = bind_variable ("OSTYPE", OSTYPE);
- set_auto_export (temp_var);
- temp_var = bind_variable ("MACHTYPE", MACHTYPE);
- set_auto_export (temp_var);
- temp_var = bind_variable ("HOSTNAME", current_host_name);
- set_auto_export (temp_var);
+ set_machine_vars ();
/* Default MAILCHECK for interactive shells. Defer the creation of a
default MAILPATH until the startup files are read, because MAIL
- names a mail file if MAILCHECK is not set, and we should provide a
+ names a mail file if MAILPATH is not set, and we should provide a
default only if neither is set. */
if (interactive_shell)
- set_if_not ("MAILCHECK", "60");
+ {
+ temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
+ VSETATTR (temp_var, att_integer);
+ }
/* Do some things with shell level. */
initialize_shell_level ();
set_ppid ();
/* Initialize the `getopts' stuff. */
- bind_variable ("OPTIND", "1");
+ temp_var = bind_variable ("OPTIND", "1", 0);
+ VSETATTR (temp_var, att_integer);
getopts_reset (0);
- bind_variable ("OPTERR", "1");
+ bind_variable ("OPTERR", "1", 0);
sh_opterr = 1;
- if (login_shell == 1)
+ if (login_shell == 1 && posixly_correct == 0)
set_home_var ();
/* Get the full pathname to THIS shell, and set the BASH variable
to it. */
name = get_bash_name ();
- temp_var = bind_variable ("BASH", name);
+ temp_var = bind_variable ("BASH", name, 0);
free (name);
/* Make the exported environment variable SHELL be the user's login
set_shell_var ();
/* Make a variable called BASH_VERSION which contains the version info. */
- bind_variable ("BASH_VERSION", shell_version_string ());
+ bind_variable ("BASH_VERSION", shell_version_string (), 0);
#if defined (ARRAY_VARS)
make_vers_array ();
#endif
+ if (command_execution_string)
+ bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
+
/* Find out if we're supposed to be in Posix.2 mode via an
environment variable. */
temp_var = find_variable ("POSIXLY_CORRECT");
that we are remembering commands on the history list. */
if (remember_on_history)
{
- name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history");
+ name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);
set_if_not ("HISTFILE", name);
free (name);
+#if 0
set_if_not ("HISTSIZE", "500");
sv_histsize ("HISTSIZE");
+#endif
}
#endif /* HISTORY */
/* Seed the random number generator. */
- sbrand (dollar_dollar_pid + (long)shell_start_time);
+ seedrand ();
/* Handle some "special" variables that we may have inherited from a
parent shell. */
{
sv_history_control ("HISTCONTROL");
sv_histignore ("HISTIGNORE");
+ sv_histtimefmt ("HISTTIMEFORMAT");
}
#endif /* HISTORY */
+#if defined (READLINE) && defined (STRICT_POSIX)
+ /* POSIXLY_CORRECT will only be 1 here if the shell was compiled
+ -DSTRICT_POSIX */
+ if (interactive_shell && posixly_correct && no_line_editing == 0)
+ rl_prefer_env_winsize = 1;
+#endif /* READLINE && STRICT_POSIX */
+
+ /*
+ * 24 October 2001
+ *
+ * I'm tired of the arguing and bug reports. Bash now leaves SSH_CLIENT
+ * and SSH2_CLIENT alone. I'm going to rely on the shell_level check in
+ * isnetconn() to avoid running the startup files more often than wanted.
+ * That will, of course, only work if the user's login shell is bash, so
+ * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
+ * in config-top.h.
+ */
+#if 0
temp_var = find_variable ("SSH_CLIENT");
if (temp_var && imported_p (temp_var))
{
VUNSETATTR (temp_var, att_exported);
array_needs_making = 1;
}
+ temp_var = find_variable ("SSH2_CLIENT");
+ if (temp_var && imported_p (temp_var))
+ {
+ VUNSETATTR (temp_var, att_exported);
+ array_needs_making = 1;
+ }
+#endif
/* Get the user's real and effective user ids. */
uidset ();
+ temp_var = find_variable ("BASH_XTRACEFD");
+ if (temp_var && imported_p (temp_var))
+ sv_xtracefd (temp_var->name);
+
/* Initialize the dynamic variables, and seed their values. */
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 *
-get_home_dir ()
+sh_get_home_dir ()
{
if (current_user.home_dir == 0)
get_current_user_info ();
temp_var = find_variable ("HOME");
if (temp_var == 0)
- temp_var = bind_variable ("HOME", get_home_dir ());
+ temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
+#if 0
VSETATTR (temp_var, att_exported);
+#endif
}
/* Set $SHELL to the user's login shell if it is not already set. Call
{
if (current_user.shell == 0)
get_current_user_info ();
- temp_var = bind_variable ("SHELL", current_user.shell);
+ temp_var = bind_variable ("SHELL", current_user.shell, 0);
}
+#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_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;
}
new_level[3] = '\0';
}
- temp_var = bind_variable ("SHLVL", new_level);
+ temp_var = bind_variable ("SHLVL", new_level, 0);
set_auto_export (temp_var);
}
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, 0);
+ set_auto_export (temp_var);
+ }
+ else
+ {
+ temp_string = get_working_directory ("shell-init");
+ if (temp_string)
+ {
+ temp_var = bind_variable ("PWD", temp_string, 0);
+ set_auto_export (temp_var);
+ free (temp_string);
+ }
+ }
+
+ /* According to the Single Unix Specification, v2, $OLDPWD is an
+ `environment variable' and therefore should be auto-exported.
+ Make a dummy invisible variable for OLDPWD, and mark it as exported. */
+ temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
+ VSETATTR (temp_var, (att_exported | att_invisible));
+}
+
/* Make a variable $PPID, which holds the pid of the shell's parent. */
void
set_ppid ()
{
- char namebuf[32], *name;
+ char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
SHELL_VAR *temp_var;
- name = inttostr ((int) getppid (), namebuf, sizeof(namebuf));
+ 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);
+ temp_var = bind_variable ("PPID", name, 0);
VSETATTR (temp_var, (att_readonly | att_integer));
}
static void
uidset ()
{
- char buff[32], *b;
+ char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
register SHELL_VAR *v;
b = inttostr (current_user.uid, buff, sizeof (buff));
v = find_variable ("UID");
- if (v)
- VUNSETATTR (v, att_readonly);
-
- v = bind_variable ("UID", b);
- VSETATTR (v, (att_readonly | att_integer));
+ if (v == 0)
+ {
+ v = bind_variable ("UID", b, 0);
+ VSETATTR (v, (att_readonly | att_integer));
+ }
if (current_user.euid != current_user.uid)
b = inttostr (current_user.euid, buff, sizeof (buff));
v = find_variable ("EUID");
- if (v)
- VUNSETATTR (v, att_readonly);
-
- v = bind_variable ("EUID", b);
- VSETATTR (v, (att_readonly | att_integer));
+ if (v == 0)
+ {
+ v = bind_variable ("EUID", b, 0);
+ VSETATTR (v, (att_readonly | att_integer));
+ }
}
#if defined (ARRAY_VARS)
{
SHELL_VAR *vv;
ARRAY *av;
- char *s, d[32];
+ 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);
s = strchr (d, '.');
if (s)
*s++ = '\0';
- array_add_element (av, 0, d);
- array_add_element (av, 1, s);
- s = inttostr (patch_level, d, sizeof (d));
- array_add_element (av, 2, s);
- s = inttostr (build_version, d, sizeof (d));
- array_add_element (av, 3, s);
- array_add_element (av, 4, release_status);
- array_add_element (av, 5, MACHTYPE);
+ 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);
}
/* 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[32], *v;
+ char val[INT_STRLEN_BOUND(int) + 1], *v;
+
+#if defined (READLINE)
+ /* If we are currently assigning to LINES or COLUMNS, don't do anything. */
+ if (winsize_assignment)
+ return;
+#endif
v = inttostr (lines, val, sizeof (val));
- bind_variable ("LINES", v);
+ bind_variable ("LINES", v, 0);
v = inttostr (cols, val, sizeof (val));
- bind_variable ("COLUMNS", v);
+ bind_variable ("COLUMNS", v, 0);
}
-/* Set NAME to VALUE if NAME has no value. */
-SHELL_VAR *
-set_if_not (name, value)
- char *name, *value;
+/* **************************************************************** */
+/* */
+/* Printing variables and values */
+/* */
+/* **************************************************************** */
+
+/* Print LIST (a list of shell variables) to stdout in such a way that
+ they can be read back in. */
+void
+print_var_list (list)
+ register SHELL_VAR **list;
{
- SHELL_VAR *v;
+ register int i;
+ register SHELL_VAR *var;
- v = find_variable (name);
- if (v == 0)
- v = bind_variable (name, value);
- return (v);
+ for (i = 0; list && (var = list[i]); i++)
+ if (invisible_p (var) == 0)
+ print_assignment (var);
}
-/* 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;
+/* Print LIST (a list of shell functions) to stdout in such a way that
+ they can be read back in. */
+void
+print_func_list (list)
+ register SHELL_VAR **list;
{
register int i;
- register BUCKET_CONTENTS *tlist;
- SHELL_VAR *var, **list;
- int list_index, list_size;
+ register SHELL_VAR *var;
- list = (SHELL_VAR **)NULL;
- for (i = list_index = list_size = 0; i < var_hash_table->nbuckets; i++)
+ for (i = 0; list && (var = list[i]); 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;
- }
+ printf ("%s ", var->name);
+ print_var_function (var);
+ printf ("\n");
}
- return (list);
}
-
+
+/* 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. */
void
-sort_variables (array)
- SHELL_VAR **array;
+print_assignment (var)
+ SHELL_VAR *var;
{
- qsort (array, array_len ((char **)array), sizeof (SHELL_VAR *), qsort_var_comp);
-}
+ if (var_isset (var) == 0)
+ return;
-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;
-
- list = map_over ((Function *)NULL, table);
- if (list /* && posixly_correct */)
- sort_variables (list);
- return (list);
-}
-
-/* Create a NULL terminated array of all the shell variables. */
-SHELL_VAR **
-all_shell_variables ()
-{
- return (all_vars (shell_variables));
-}
-
-/* Create a NULL terminated array of all the shell functions. */
-SHELL_VAR **
-all_shell_functions ()
-{
- return (all_vars (shell_functions));
-}
-
-/* Print VARS to stdout in such a way that they can be read back in. */
-void
-print_var_list (list)
- register SHELL_VAR **list;
-{
- register int i;
- register SHELL_VAR *var;
-
- for (i = 0; list && (var = list[i]); i++)
- if (!invisible_p (var))
- print_assignment (var);
-}
-
-#if defined (NOTDEF)
-/* Print LIST (a linked list of shell variables) to stdout
- by printing the names, without the values. Used to support the
- `set +' command. */
-void
-print_vars_no_values (list)
- register SHELL_VAR **list;
-{
- register int i;
- register SHELL_VAR *var;
-
- for (i = 0; list && (var = list[i]); i++)
- if (!invisible_p (var))
- printf ("%s\n", var->name);
-}
-#endif
-
-/* Print the value of a single SHELL_VAR. No newline is
- output, but the variable is printed in such a way that
- it can be read back in. */
-void
-print_assignment (var)
- SHELL_VAR *var;
-{
- if (function_p (var) && var->value)
+ if (function_p (var))
{
- printf ("%s=", var->name);
+ printf ("%s", var->name);
print_var_function (var);
printf ("\n");
}
#if defined (ARRAY_VARS)
- else if (array_p (var) && var->value)
+ else if (array_p (var))
print_array_assignment (var, 0);
+ else if (assoc_p (var))
+ print_assoc_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)
- 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));
+ char *x;
- if (vstr == 0)
- printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
- else
+ if (function_p (var) && var_isset (var))
{
- printf ("%s=%s\n", var->name, vstr);
- free (vstr);
+ x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL);
+ printf ("%s", x);
}
}
-#endif /* ARRAY_VARS */
/* **************************************************************** */
/* */
-/* Dynamic Variable Extension */
+/* Dynamic Variables */
/* */
/* **************************************************************** */
These are variables whose values are generated anew each time they are
referenced. These are implemented using a pair of function pointers
- in the struct variable: assign_func, which is called from bind_variable,
- and dynamic_value, which is called from find_variable.
-
- assign_func is called from bind_variable, if bind_variable discovers
- that the variable being assigned to has such a function. The function
- is called as
- SHELL_VAR *temp = (*(entry->assign_func)) (entry, value)
+ in the struct variable: assign_func, which is called from bind_variable
+ and, if arrays are compiled into the shell, some of the functions in
+ arrayfunc.c, and dynamic_value, which is called from find_variable.
+
+ assign_func is called from bind_variable_internal, if
+ bind_variable_internal discovers that the variable being assigned to
+ has such a function. The function is called as
+ SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind)
and the (SHELL_VAR *)temp is returned as the value of bind_variable. It
- is usually ENTRY (self).
+ is usually ENTRY (self). IND is an index for an array variable, and
+ unused otherwise.
- dynamic_value is called from find_variable to return a `new' value for
- the specified dynamic varible. If this function is NULL, the variable
- is treated as a `normal' shell variable. If it is not, however, then
- this function is called like this:
- tempvar = (*(var->dynamic_value)) (var);
+ dynamic_value is called from find_variable_internal to return a `new'
+ value for the specified dynamic varible. If this function is NULL,
+ the variable is treated as a `normal' shell variable. If it is not,
+ however, then this function is called like this:
+ tempvar = (*(var->dynamic_value)) (var);
Sometimes `tempvar' will replace the value of `var'. Other times, the
shell will simply use the string value. Pretty object-oriented, huh?
special meaning, even if you subsequently set it.
The special assignment code would probably have been better put in
- subst.c: do_assignment, in the same style as
+ subst.c: do_assignment_internal, in the same style as
stupidly_hack_special_variables, but I wanted the changes as
localized as possible. */
+#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
+ do \
+ { \
+ v = bind_variable (var, (val), 0); \
+ v->dynamic_value = gfunc; \
+ v->assign_func = afunc; \
+ } \
+ while (0)
+
+#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \
+ do \
+ { \
+ v = make_new_array_variable (var); \
+ v->dynamic_value = gfunc; \
+ v->assign_func = afunc; \
+ } \
+ while (0)
+
+#define INIT_DYNAMIC_ASSOC_VAR(var, gfunc, afunc) \
+ do \
+ { \
+ v = make_new_assoc_variable (var); \
+ v->dynamic_value = gfunc; \
+ v->assign_func = afunc; \
+ } \
+ while (0)
+
static SHELL_VAR *
-null_assign (self, value)
+null_assign (self, value, unused, key)
SHELL_VAR *self;
char *value;
+ arrayind_t unused;
+ char *key;
{
return (self);
}
#if defined (ARRAY_VARS)
static SHELL_VAR *
-null_array_assign (self, ind, value)
+null_array_assign (self, value, ind, key)
SHELL_VAR *self;
- int ind;
char *value;
+ arrayind_t ind;
+ char *key;
+{
+ 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;
+}
+
+static SHELL_VAR *
+init_dynamic_assoc_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_ASSOC_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, key)
SHELL_VAR *self;
char *value;
+ arrayind_t unused;
+ char *key;
{
- seconds_value_assigned = strtol (value, (char **)NULL, 10);
+ 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));
VSETATTR (var, att_integer);
- var->value = p;
+ var_setvalue (var, p);
return (var);
}
+static SHELL_VAR *
+init_seconds_var ()
+{
+ SHELL_VAR *v;
+
+ v = find_variable ("SECONDS");
+ if (v)
+ {
+ if (legal_number (value_cell(v), &seconds_value_assigned) == 0)
+ seconds_value_assigned = 0;
+ }
+ INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
+ return v;
+}
+
/* The random number seed. You can change this by setting RANDOM. */
static unsigned long rseed = 1;
-static unsigned long last_random_value;
+static int last_random_value;
+static int seeded_subshell = 0;
-/* A linear congruential random number generator based on the ANSI
- C standard. This one isn't very good (the values are alternately
- odd and even, for example), but 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;
+ /* From "Random number generators: good ones are hard to find",
+ Park and Miller, Communications of the ACM, vol. 31, no. 10,
+ October 1988, p. 1195. filtered through FreeBSD */
+ long h, l;
+
+ /* Can't seed with 0. */
+ if (rseed == 0)
+ rseed = 123459876;
+ h = rseed / 127773;
+ l = rseed % 127773;
+ rseed = 16807 * l - 2836 * h;
+#if 0
+ if (rseed < 0)
+ rseed += 0x7fffffff;
+#endif
return ((unsigned int)(rseed & 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 void
+seedrand ()
+{
+ struct timeval tv;
+
+ gettimeofday (&tv, NULL);
+ sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
+}
+
static SHELL_VAR *
-assign_random (self, value)
+assign_random (self, value, unused, key)
SHELL_VAR *self;
char *value;
+ arrayind_t unused;
+ char *key;
{
- sbrand (atoi (value));
+ sbrand (strtoul (value, (char **)NULL, 10));
+ if (subshell_environment)
+ seeded_subshell = getpid ();
return (self);
}
-static SHELL_VAR *
-get_random (var)
- SHELL_VAR *var;
+int
+get_random_number ()
{
- int rv;
- char *p;
+ int rv, pid;
/* Reset for command and process substitution. */
- if (subshell_environment)
- sbrand (rseed + (int)(getpid() + NOW));
+ pid = getpid ();
+ if (subshell_environment && seeded_subshell != pid)
+ {
+ seedrand ();
+ seeded_subshell = pid;
+ }
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));
VSETATTR (var, att_integer);
- var->value = p;
+ var_setvalue (var, p);
return (var);
}
+static SHELL_VAR *
+assign_lineno (var, value, unused, key)
+ SHELL_VAR *var;
+ char *value;
+ arrayind_t unused;
+ char *key;
+{
+ intmax_t new_value;
+
+ if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
+ new_value = 0;
+ line_number = line_number_base = 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, key)
SHELL_VAR *var;
char *value;
+ arrayind_t unused;
+ char *key;
{
- 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_bashpid (var)
+ SHELL_VAR *var;
+{
+ int pid;
+ char *p;
+
+ pid = getpid ();
+ p = itos (pid);
+
+ FREE (value_cell (var));
+ VSETATTR (var, att_integer|att_readonly);
+ var_setvalue (var, p);
+ return (var);
+}
+
+static SHELL_VAR *
+get_bash_command (var)
+ SHELL_VAR *var;
+{
+ char *p;
+
+ if (the_printed_command_except_trap)
+ p = savestring (the_printed_command_except_trap);
+ else
+ {
+ p = (char *)xmalloc (1);
+ p[0] = '\0';
+ }
+ FREE (value_cell (var));
+ var_setvalue (var, p);
+ return (var);
+}
+
#if defined (HISTORY)
static SHELL_VAR *
get_histcmd (var)
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;
+{
+ /* 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);
+
+ FREE (value_cell (var));
+ var_setvalue (var, savestring (rl_completer_word_break_characters));
+
+ return (var);
+}
+
+/* When this function returns, rl_completer_word_break_characters points to
+ malloced memory. */
+static SHELL_VAR *
+assign_comp_wordbreaks (self, value, unused, key)
SHELL_VAR *self;
+ char *value;
+ arrayind_t unused;
+ char *key;
{
- 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, key)
SHELL_VAR *self;
- int ind;
char *value;
+ arrayind_t ind;
+ char *key;
{
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 (0);
+ 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 */
static SHELL_VAR *
-get_funcname (self)
+build_hashcmd (self)
SHELL_VAR *self;
{
- if (variable_context && this_shell_function)
+ HASH_TABLE *h;
+ int i;
+ char *k, *v;
+ BUCKET_CONTENTS *item;
+
+ h = assoc_cell (self);
+ if (h)
+ assoc_dispose (h);
+
+ if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0)
{
- FREE (self->value);
- self->value = savestring (this_shell_function->name);
+ var_setvalue (self, (char *)NULL);
+ return self;
}
- return (self);
-}
-void
-make_funcname_visible (on_or_off)
- int on_or_off;
-{
- SHELL_VAR *v;
+ h = assoc_create (hashed_filenames->nbuckets);
+ for (i = 0; i < hashed_filenames->nbuckets; i++)
+ {
+ for (item = hash_items (i, hashed_filenames); item; item = item->next)
+ {
+ k = savestring (item->key);
+ v = pathdata(item)->path;
+ assoc_insert (h, k, v);
+ }
+ }
+
+ var_setvalue (self, (char *)h);
+ return self;
+}
+
+static SHELL_VAR *
+get_hashcmd (self)
+ SHELL_VAR *self;
+{
+ build_hashcmd (self);
+ return (self);
+}
+
+static SHELL_VAR *
+assign_hashcmd (self, value, ind, key)
+ SHELL_VAR *self;
+ char *value;
+ arrayind_t ind;
+ char *key;
+{
+ phash_insert (key, value, 0, 0);
+ return (build_hashcmd (self));
+}
+
+#if defined (ALIAS)
+static SHELL_VAR *
+build_aliasvar (self)
+ SHELL_VAR *self;
+{
+ HASH_TABLE *h;
+ int i;
+ char *k, *v;
+ BUCKET_CONTENTS *item;
+
+ h = assoc_cell (self);
+ if (h)
+ assoc_dispose (h);
+
+ if (aliases == 0 || HASH_ENTRIES (aliases) == 0)
+ {
+ var_setvalue (self, (char *)NULL);
+ return self;
+ }
+
+ h = assoc_create (aliases->nbuckets);
+ for (i = 0; i < aliases->nbuckets; i++)
+ {
+ for (item = hash_items (i, aliases); item; item = item->next)
+ {
+ k = savestring (item->key);
+ v = ((alias_t *)(item->data))->value;
+ assoc_insert (h, k, v);
+ }
+ }
+
+ var_setvalue (self, (char *)h);
+ return self;
+}
+
+static SHELL_VAR *
+get_aliasvar (self)
+ SHELL_VAR *self;
+{
+ build_aliasvar (self);
+ return (self);
+}
+
+static SHELL_VAR *
+assign_aliasvar (self, value, ind, key)
+ SHELL_VAR *self;
+ char *value;
+ arrayind_t ind;
+ char *key;
+{
+ add_alias (key, value);
+ return (build_aliasvar (self));
+}
+#endif /* ALIAS */
+
+#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)
VSETATTR (v, att_invisible);
}
-#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
- do \
- { \
- v = bind_variable (var, val); \
- v->dynamic_value = gfunc; \
- v->assign_func = afunc; \
- } while (0)
+static SHELL_VAR *
+init_funcname_var ()
+{
+ SHELL_VAR *v;
-#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)
+ 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;
- INIT_DYNAMIC_VAR ("SECONDS", (char *)NULL, get_seconds, assign_seconds);
+ v = init_seconds_var ();
+
+ 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);
+
INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
+ VSETATTR (v, att_integer);
INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
+ VSETATTR (v, att_integer);
+
+ INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
+ VSETATTR (v, att_integer|att_readonly);
#if defined (HISTORY)
- INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (DYNAMIC_FUNC *)NULL);
+ INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
+ VSETATTR (v, att_integer);
+#endif
+
+#if defined (READLINE)
+ INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks);
#endif
#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
- INIT_DYNAMIC_ARRAY_VAR ("DIRSTACK", get_dirstack, assign_dirstack);
+ v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0);
#endif /* PUSHD_AND_POPD && ARRAY_VARS */
#if defined (ARRAY_VARS)
- INIT_DYNAMIC_ARRAY_VAR ("GROUPS", get_groupset, null_array_assign);
+ v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign);
+
+# if defined (DEBUGGER)
+ v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset);
+ v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset);
+# endif /* DEBUGGER */
+ v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset);
+ v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset);
+
+ v = init_dynamic_assoc_var ("BASH_CMDS", get_hashcmd, assign_hashcmd, att_nofree);
+# if defined (ALIAS)
+ v = init_dynamic_assoc_var ("BASH_ALIASES", get_aliasvar, assign_aliasvar, att_nofree);
+# endif
#endif
- INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
- VSETATTR (v, att_invisible);
+ v = init_funcname_var ();
}
+/* **************************************************************** */
+/* */
+/* Retrieving variables and values */
+/* */
+/* **************************************************************** */
+
/* How to get a pointer to the shell variable or function named NAME.
HASHED_VARS is a pointer to the hash table containing the list
of interest (either variables or functions). */
-SHELL_VAR *
-var_lookup (name, hashed_vars)
- char *name;
+
+static SHELL_VAR *
+hash_lookup (name, hashed_vars)
+ const char *name;
HASH_TABLE *hashed_vars;
{
BUCKET_CONTENTS *bucket;
- bucket = find_hash_item (name, hashed_vars);
+ bucket = hash_search (name, hashed_vars, 0);
return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
}
+SHELL_VAR *
+var_lookup (name, vcontext)
+ const char *name;
+ VAR_CONTEXT *vcontext;
+{
+ VAR_CONTEXT *vc;
+ SHELL_VAR *v;
+
+ v = (SHELL_VAR *)NULL;
+ for (vc = vcontext; vc; vc = vc->down)
+ if (v = hash_lookup (name, vc->table))
+ break;
+
+ return v;
+}
+
/* Look up the variable entry named NAME. If SEARCH_TEMPENV is non-zero,
- then also search the temporarily built list of exported variables. */
+ then also search the temporarily built list of exported variables.
+ The lookup order is:
+ temporary_env
+ shell_variables list
+*/
+
SHELL_VAR *
-find_variable_internal (name, search_tempenv)
- char *name;
- int search_tempenv;
+find_variable_internal (name, force_tempenv)
+ const char *name;
+ int force_tempenv;
{
- SHELL_VAR *var = (SHELL_VAR *)NULL;
+ SHELL_VAR *var;
+ int search_tempenv;
+
+ var = (SHELL_VAR *)NULL;
/* If explicitly requested, first look in the temporary environment for
the variable. This allows constructs such as "foo=x eval 'echo $foo'"
to get the `exported' value of $foo. This happens if we are executing
a function or builtin, or if we are looking up a variable in a
"subshell environment". */
- if ((search_tempenv || subshell_environment) &&
- (temporary_env || builtin_env || function_env))
- var = find_tempenv_variable (name);
+ search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment);
- if (!var)
+ if (search_tempenv && temporary_env)
+ var = hash_lookup (name, temporary_env);
+
+ if (var == 0)
var = var_lookup (name, shell_variables);
- if (!var)
+ if (var == 0)
+ return ((SHELL_VAR *)NULL);
+
+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
+}
+
+SHELL_VAR *
+find_global_variable (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ var = var_lookup (name, global_variables);
+
+ 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 && (assigning_in_environment || executing_builtin))));
}
/* 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. This is a potential memory leak if the variable is found
- in the temporary environment. */
-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);
+#if defined (DEBUGGER)
+ return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
+#else
+ return ((FUNCTION_DEF *)0);
+#endif
+}
- 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));
+ else if (assoc_p (var))
+ return (assoc_reference (assoc_cell (var), "0"));
#endif
else
- return (var->value);
+ return (value_cell (var));
+}
+
+/* Return the string value of a variable. Return NULL if the variable
+ doesn't exist. Don't cons a new string. This is a potential memory
+ leak if the variable is found in the temporary environment. Since
+ functions and variables have separate name spaces, returns NULL if
+ var_name is a shell function only. */
+char *
+get_string_value (var_name)
+ const char *var_name;
+{
+ SHELL_VAR *var;
+
+ var = find_variable (var_name);
+ return ((var) ? get_variable_value (var) : (char *)NULL);
}
/* This is present for use by the tilde and readline libraries. */
char *
-get_env_value (v)
- char *v;
+sh_get_env_value (v)
+ const char *v;
{
return get_string_value (v);
}
+/* **************************************************************** */
+/* */
+/* Creating and setting variables */
+/* */
+/* **************************************************************** */
+
+/* Set NAME to VALUE if NAME has no value. */
+SHELL_VAR *
+set_if_not (name, value)
+ char *name, *value;
+{
+ SHELL_VAR *v;
+
+ if (shell_variables == 0)
+ create_variable_tables ();
+
+ v = find_variable (name);
+ if (v == 0)
+ v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
+ return (v);
+}
+
/* Create a local variable referenced by NAME. */
SHELL_VAR *
make_local_variable (name)
- char *name;
+ const char *name;
{
SHELL_VAR *new_var, *old_var;
- BUCKET_CONTENTS *elt;
+ VAR_CONTEXT *vc;
+ int was_tmpvar;
+ char *tmp_value;
/* local foo; local foo; is a no-op. */
old_var = find_variable (name);
- if (old_var && old_var->context == variable_context)
- return (old_var);
+ if (old_var && local_p (old_var) && old_var->context == variable_context)
+ {
+ VUNSETATTR (old_var, att_invisible);
+ return (old_var);
+ }
- /* 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). */
- if (old_var && readonly_p (old_var))
+ was_tmpvar = old_var && tempvar_p (old_var);
+ if (was_tmpvar)
+ tmp_value = value_cell (old_var);
+
+ for (vc = shell_variables; vc; vc = vc->down)
+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
+ break;
+
+ if (vc == 0)
{
- builtin_error ("%s: readonly variable");
+ 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);
- elt = remove_hash_item (name, shell_variables);
- if (elt)
+ /* 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)))
{
- old_var = (SHELL_VAR *)elt->data;
- free (elt->key);
- free (elt);
+ if (readonly_p (old_var))
+ sh_readonly (name);
+ return ((SHELL_VAR *)NULL);
}
- else
- old_var = (SHELL_VAR *)NULL;
- /* If a variable does not already exist with this name, then
- just make a new one. */
if (old_var == 0)
- new_var = bind_variable (name, "");
+ new_var = make_new_variable (name, vc->table);
else
{
- new_var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-
- new_var->name = savestring (name);
- new_var->value = xmalloc (1);
- new_var->value[0] = '\0';
+ new_var = make_new_variable (name, vc->table);
- CLEAR_EXPORTSTR (new_var);
-
- new_var->dynamic_value = (DYNAMIC_FUNC *)NULL;
- new_var->assign_func = (DYNAMIC_FUNC *)NULL;
+ /* If we found this variable in one of the temporary environments,
+ inherit its value. Watch to see if this causes problems with
+ things like `x=4 local x'. */
+ if (was_tmpvar)
+ var_setvalue (new_var, savestring (tmp_value));
new_var->attributes = exported_p (old_var) ? att_exported : 0;
-
- new_var->prev_context = old_var;
- elt = add_hash_item (savestring (name), shell_variables);
- elt->data = (char *)new_var;
}
+ vc->flags |= VC_HASLOCAL;
+
new_var->context = variable_context;
VSETATTR (new_var, att_local);
- /* XXX */
- if (variable_context >= local_variable_stack_size)
- {
- int old_size = local_variable_stack_size;
- RESIZE_MALLOCED_BUFFER (have_local_variables, variable_context, 1,
- local_variable_stack_size, 8);
- bzero ((char *)have_local_variables + old_size,
- local_variable_stack_size - old_size);
- }
- have_local_variables[variable_context] = 1; /* XXX */
+ if (ifsname (name))
+ setifs (new_var);
return (new_var);
}
-#if defined (ARRAY_VARS)
-SHELL_VAR *
-make_local_array_variable (name)
- char *name;
-{
- SHELL_VAR *var;
- ARRAY *array;
-
- var = make_local_variable (name);
- if (var == 0)
- return var;
- array = new_array ();
-
- FREE (value_cell(var));
- var->value = (char *)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)
+ create_variable_tables ();
+
+ 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 = make_new_variable (name, global_variables->table);
+ array = array_create ();
+
+ var_setarray (entry, array);
VSETATTR (entry, att_array);
return entry;
}
-#endif
-char *
-make_variable_value (var, value)
- SHELL_VAR *var;
- char *value;
+SHELL_VAR *
+make_local_array_variable (name)
+ char *name;
{
- char *retval;
- long lval;
- int expok;
+ SHELL_VAR *var;
+ ARRAY *array;
- /* If this variable has had its type set to integer (via `declare -i'),
- then do expression evaluation on it and store the result. The
- functions in expr.c (evalexp and bind_int_variable) are responsible
- for turning off the integer flag if they don't want further
- evaluation done. */
- if (integer_p (var))
- {
- lval = evalexp (value, &expok);
- if (expok == 0)
- jump_to_top_level (DISCARD);
- retval = itos (lval);
- }
- else if (value)
- {
- if (*value)
- retval = savestring (value);
- else
- {
- retval = xmalloc (1);
- retval[0] = '\0';
- }
- }
+ var = make_local_variable (name);
+ if (var == 0 || array_p (var))
+ return var;
+
+ array = array_create ();
+
+ dispose_variable_value (var);
+ var_setarray (var, array);
+ VSETATTR (var, att_array);
+ return var;
+}
+
+SHELL_VAR *
+make_new_assoc_variable (name)
+ char *name;
+{
+ SHELL_VAR *entry;
+ HASH_TABLE *hash;
+
+ entry = make_new_variable (name, global_variables->table);
+ hash = assoc_create (0);
+
+ var_setassoc (entry, hash);
+ VSETATTR (entry, att_assoc);
+ return entry;
+}
+
+SHELL_VAR *
+make_local_assoc_variable (name)
+ char *name;
+{
+ SHELL_VAR *var;
+ HASH_TABLE *hash;
+
+ var = make_local_variable (name);
+ if (var == 0 || assoc_p (var))
+ return var;
+
+ dispose_variable_value (var);
+ hash = assoc_create (0);
+
+ var_setassoc (var, hash);
+ VSETATTR (var, att_assoc);
+ return var;
+}
+#endif
+
+char *
+make_variable_value (var, value, flags)
+ SHELL_VAR *var;
+ char *value;
+ int flags;
+{
+ char *retval, *oval;
+ intmax_t lval, rval;
+ int expok, olen, op;
+
+ /* If this variable has had its type set to integer (via `declare -i'),
+ then do expression evaluation on it and store the result. The
+ functions in expr.c (evalexp()) and bind_int_variable() are responsible
+ for turning off the integer flag if they don't want further
+ evaluation done. */
+ if (integer_p (var))
+ {
+ if (flags & ASS_APPEND)
+ {
+ oval = value_cell (var);
+ lval = evalexp (oval, &expok); /* ksh93 seems to do this */
+ if (expok == 0)
+ {
+ top_level_cleanup ();
+ jump_to_top_level (DISCARD);
+ }
+ }
+ rval = evalexp (value, &expok);
+ if (expok == 0)
+ {
+ top_level_cleanup ();
+ jump_to_top_level (DISCARD);
+ }
+ if (flags & ASS_APPEND)
+ rval += lval;
+ retval = itos (rval);
+ }
+#if defined (CASEMOD_ATTRS)
+ else if (capcase_p (var) || uppercase_p (var) || lowercase_p (var))
+ {
+ if (flags & ASS_APPEND)
+ {
+ oval = get_variable_value (var);
+ if (oval == 0) /* paranoia */
+ oval = "";
+ olen = STRLEN (oval);
+ retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
+ strcpy (retval, oval);
+ if (value)
+ strcpy (retval+olen, value);
+ }
+ else if (*value)
+ retval = savestring (value);
+ else
+ {
+ retval = (char *)xmalloc (1);
+ retval[0] = '\0';
+ }
+ op = capcase_p (var) ? CASE_CAPITALIZE
+ : (uppercase_p (var) ? CASE_UPPER : CASE_LOWER);
+ oval = sh_modcase (retval, (char *)0, op);
+ free (retval);
+ retval = oval;
+ }
+#endif /* CASEMOD_ATTRS */
+ else if (value)
+ {
+ if (flags & ASS_APPEND)
+ {
+ oval = get_variable_value (var);
+ if (oval == 0) /* paranoia */
+ oval = "";
+ olen = STRLEN (oval);
+ retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
+ strcpy (retval, oval);
+ if (value)
+ strcpy (retval+olen, value);
+ }
+ else if (*value)
+ retval = savestring (value);
+ else
+ {
+ retval = (char *)xmalloc (1);
+ retval[0] = '\0';
+ }
+ }
else
retval = (char *)NULL;
return retval;
}
-/* Bind a variable NAME to VALUE. This conses up the name
- and value strings. */
-SHELL_VAR *
-bind_variable (name, value)
- char *name, *value;
+/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
+ temporary environment (but usually is not). */
+static SHELL_VAR *
+bind_variable_internal (name, value, table, hflags, aflags)
+ const char *name;
+ char *value;
+ HASH_TABLE *table;
+ int hflags, aflags;
{
char *newval;
SHELL_VAR *entry;
- entry = var_lookup (name, shell_variables);
+ entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
if (entry == 0)
{
- entry = make_new_variable (name);
- entry->value = make_variable_value (entry, value);
+ entry = make_new_variable (name, table);
+ var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
}
-#if defined (ARRAY_VARS)
- else if (entry->assign_func && array_p (entry) == 0)
-#else
- else if (entry->assign_func)
-#endif
+ else if (entry->assign_func) /* array vars have assign functions now */
{
INVALIDATE_EXPORTSTR (entry);
- return ((*(entry->assign_func)) (entry, value));
+ newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value;
+ if (assoc_p (entry))
+ entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0"));
+ else if (array_p (entry))
+ entry = (*(entry->assign_func)) (entry, newval, 0, 0);
+ else
+ entry = (*(entry->assign_func)) (entry, newval, -1, 0);
+ if (newval != value)
+ free (newval);
+ return (entry);
}
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. */
VUNSETATTR (entry, att_invisible);
- newval = make_variable_value (entry, value);
+ newval = make_variable_value (entry, value, aflags); /* XXX */
/* Invalidate any cached export string */
INVALIDATE_EXPORTSTR (entry);
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
+ else if (assoc_p (entry))
{
- FREE (entry->value);
- entry->value = newval;
+ assoc_insert (assoc_cell (entry), savestring ("0"), newval);
+ free (newval);
}
-#else
- FREE (entry->value);
- entry->value = newval;
+ else
#endif
+ {
+ FREE (value_cell (entry));
+ var_setvalue (entry, newval);
+ }
}
if (mark_modified_vars)
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. */
+
+SHELL_VAR *
+bind_variable (name, value, flags)
+ const char *name;
+ char *value;
+ int flags;
+{
+ SHELL_VAR *v;
+ VAR_CONTEXT *vc;
+
+ if (shell_variables == 0)
+ create_variable_tables ();
+
+ /* If we have a temporary environment, look there first for the variable,
+ and, if found, modify the value there before modifying it in the
+ shell_variables table. This allows sourced scripts to modify values
+ given to them in a temporary environment while modifying the variable
+ value that the caller sees. */
+ if (temporary_env)
+ bind_tempenv_variable (name, value);
+
+ /* XXX -- handle local variables here. */
+ for (vc = shell_variables; vc; vc = vc->down)
+ {
+ if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
+ {
+ v = hash_lookup (name, vc->table);
+ if (v)
+ return (bind_variable_internal (name, value, vc->table, 0, flags));
+ }
+ }
+ return (bind_variable_internal (name, value, global_variables->table, 0, flags));
+}
/* Make VAR, a simple shell variable, have value VALUE. Once assigned a
value, variables are no longer invisible. This is a duplicate of part
all modified variables should be exported, mark the variable for export
and note that the export environment needs to be recreated. */
SHELL_VAR *
-bind_variable_value (var, value)
+bind_variable_value (var, value, aflags)
SHELL_VAR *var;
char *value;
+ int aflags;
{
char *t;
VUNSETATTR (var, att_invisible);
- t = make_variable_value (var, value);
- FREE (var->value);
- var->value = t;
+ if (var->assign_func)
+ {
+ /* If we're appending, we need the old value, so use
+ make_variable_value */
+ t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value;
+ (*(var->assign_func)) (var, t, -1, 0);
+ if (t != value && t)
+ free (t);
+ }
+ else
+ {
+ t = make_variable_value (var, value, aflags);
+ FREE (value_cell (var));
+ var_setvalue (var, t);
+ }
INVALIDATE_EXPORTSTR (var);
char *lhs, *rhs;
{
register SHELL_VAR *v;
- int isint;
+ int isint, isarr;
+
+ isint = isarr = 0;
+#if defined (ARRAY_VARS)
+ if (valid_array_reference (lhs))
+ {
+ isarr = 1;
+ v = array_variable_part (lhs, (char **)0, (int *)0);
+ }
+ else
+#endif
+ v = find_variable (lhs);
- isint = 0;
- v = find_variable (lhs);
if (v)
{
isint = integer_p (v);
VUNSETATTR (v, att_integer);
}
- v = bind_variable (lhs, rhs);
- if (isint)
+#if defined (ARRAY_VARS)
+ if (isarr)
+ v = assign_array_element (lhs, rhs, 0);
+ else
+#endif
+ v = bind_variable (lhs, rhs, 0);
+
+ if (v && isint)
VSETATTR (v, att_integer);
return (v);
}
-#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_var_to_int (var, val)
+ char *var;
+ intmax_t val;
{
- char *oldval;
- ARRAY *array;
-
- oldval = value_cell (var);
- array = new_array ();
- array_add_element (array, 0, oldval);
+ char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
- FREE (value_cell (var));
- var->value = (char *)array;
-
- INVALIDATE_EXPORTSTR (var);
-
- VSETATTR (var, att_array);
- VUNSETATTR (var, att_invisible);
-
- return var;
+ p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
+ return (bind_int_variable (var, p));
}
-/* 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].
-
- If NAME does not exist, just create an array variable, no matter what
- IND's value may be. */
+/* 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_array_variable (name, ind, value)
- char *name;
- int ind;
- char *value;
+bind_function (name, value)
+ const char *name;
+ COMMAND *value;
{
SHELL_VAR *entry;
- char *newval;
- entry = var_lookup (name, shell_variables);
-
- if (entry == (SHELL_VAR *) 0)
- entry = make_new_array_variable (name);
- else if (readonly_p (entry))
+ entry = find_function (name);
+ if (entry == 0)
{
- report_error ("%s: readonly variable", name);
- return (entry);
+ BUCKET_CONTENTS *elt;
+
+ elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
+ entry = new_shell_variable (name);
+ elt->data = (PTR_T)entry;
}
- else if (array_p (entry) == 0)
- entry = convert_var_to_array (entry);
+ else
+ INVALIDATE_EXPORTSTR (entry);
- /* ENTRY is an array variable, and ARRAY points to the value. */
- newval = make_variable_value (entry, value);
- if (entry->assign_func)
- (*entry->assign_func) (entry, ind, newval);
+ if (var_isset (entry))
+ dispose_command (function_cell (entry));
+
+ if (value)
+ var_setfunc (entry, copy_command (value));
else
- array_add_element (array_cell (entry), ind, newval);
- FREE (newval);
+ var_setfunc (entry, 0);
+
+ VSETATTR (entry, att_function);
+
+ if (mark_modified_vars)
+ VSETATTR (entry, att_exported);
+
+ VUNSETATTR (entry, att_invisible); /* Just to be sure */
+
+ if (exported_p (entry))
+ array_needs_making = 1;
+
+#if defined (PROGRAMMABLE_COMPLETION)
+ set_itemlist_dirty (&it_functions);
+#endif
return (entry);
}
-/* Perform a compound assignment statement for array NAME, where VALUE is
- the text between the parens: NAME=( VALUE ) */
-SHELL_VAR *
-assign_array_from_string (name, value)
- char *name, *value;
+#if defined (DEBUGGER)
+/* Bind a function definition, which includes source file and line number
+ information in addition to the command, into the FUNCTION_DEF hash table.*/
+void
+bind_function_def (name, value)
+ const char *name;
+ FUNCTION_DEF *value;
{
- SHELL_VAR *var;
+ FUNCTION_DEF *entry;
+ BUCKET_CONTENTS *elt;
+ COMMAND *cmd;
- var = find_variable (name);
- if (var == 0)
- var = make_new_array_variable (name);
- else if (readonly_p (var))
+ entry = find_function_def (name);
+ if (entry)
{
- report_error ("%s: readonly variable", name);
- return ((SHELL_VAR *)NULL);
+ dispose_function_def_contents (entry);
+ entry = copy_function_def_contents (value, entry);
}
- else if (array_p (var) == 0)
- var = convert_var_to_array (var);
+ else
+ {
+ cmd = value->command;
+ value->command = 0;
+ entry = copy_function_def (value);
+ value->command = cmd;
- return (assign_array_var_from_string (var, value));
+ elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH);
+ elt->data = (PTR_T *)entry;
+ }
}
+#endif /* DEBUGGER */
-SHELL_VAR *
-assign_array_var_from_word_list (var, list)
- SHELL_VAR *var;
- WORD_LIST *list;
+/* Add STRING, which is of the form foo=bar, to the temporary environment
+ HASH_TABLE (temporary_env). The functions in execute_cmd.c are
+ responsible for moving the main temporary env to one of the other
+ temporary environments. The expansion code in subst.c calls this. */
+int
+assign_in_env (word, flags)
+ WORD_DESC *word;
+ int flags;
{
- register int i;
- register WORD_LIST *l;
- ARRAY *a;
+ int offset;
+ char *name, *temp, *value;
+ SHELL_VAR *var;
+ const char *string;
- 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;
-}
+ string = word->word;
-/* For each word in a compound array assignment, if the word looks like
- [ind]=value, quote the `[' and `]' before the `=' to protect them from
- unwanted filename expansion. */
-static void
-quote_array_assignment_chars (list)
- WORD_LIST *list;
-{
- char *s, *t, *nword;
- int saw_eq;
- WORD_LIST *l;
+ offset = assignment (string, 0);
+ name = savestring (string);
+ value = (char *)NULL;
- for (l = list; l; l = l->next)
+ if (name[offset] == '=')
{
- if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
- continue; /* should not happen, but just in case... */
- /* Don't bother if it doesn't look like [ind]=value */
- if (l->word->word[0] != '[' || strchr (l->word->word, '=') == 0) /* ] */
- continue;
- s = nword = xmalloc (strlen (l->word->word) * 2 + 1);
- saw_eq = 0;
- for (t = l->word->word; *t; )
+ name[offset] = 0;
+
+ /* ignore the `+' when assigning temporary environment */
+ if (name[offset - 1] == '+')
+ name[offset - 1] = '\0';
+
+ var = find_variable (name);
+ if (var && (readonly_p (var) || noassign_p (var)))
{
- if (*t == '=')
- saw_eq = 1;
- if (saw_eq == 0 && (*t == '[' || *t == ']'))
- *s++ = '\\';
- *s++ = *t++;
+ if (readonly_p (var))
+ err_readonly (name);
+ free (name);
+ return (0);
}
- *s = '\0';
- free (l->word->word);
- l->word->word = nword;
+
+ temp = name + offset + 1;
+ value = expand_assignment_string_to_string (temp, 0);
}
-}
-/* Perform a compound array assignment: VAR->name=( VALUE ). The
- VALUE has already had the parentheses stripped. */
-SHELL_VAR *
-assign_array_var_from_string (var, value)
- SHELL_VAR *var;
- char *value;
-{
- ARRAY *a;
- WORD_LIST *list, *nlist;
- char *w, *val, *nval;
- int ni, len, ind, last_ind;
+ if (temporary_env == 0)
+ temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
- if (value == 0)
- return var;
+ var = hash_lookup (name, temporary_env);
+ if (var == 0)
+ var = make_new_variable (name, temporary_env);
+ else
+ FREE (value_cell (var));
- /* If this is called from declare_builtin, value[0] == '(' and
- strchr(value, ')') != 0. In this case, we need to extract
- the value from between the parens before going on. */
- if (*value == '(') /*)*/
+ if (value == 0)
{
- ni = 1;
- val = extract_array_assignment_list (value, &ni);
- if (val == 0)
- return var;
+ value = (char *)xmalloc (1); /* like do_assignment_internal */
+ value[0] = '\0';
}
- else
- val = value;
- /* Expand the value string into a list of words, performing all the
- shell expansions including pathname generation and word splitting. */
- /* First we split the string on whitespace, using the shell parser
- (ksh93 seems to do this). */
- list = parse_string_to_word_list (val, "array assign");
+ var_setvalue (var, value);
+ var->attributes |= (att_exported|att_tempvar);
+ var->context = variable_context; /* XXX */
+
+ INVALIDATE_EXPORTSTR (var);
+ var->exportstr = mk_env_string (name, value);
- /* If we're using [subscript]=value, we need to quote each [ and ] to
- prevent unwanted filename expansion. */
- if (list)
- quote_array_assignment_chars (list);
+ array_needs_making = 1;
- /* Now that we've split it, perform the shell expansions on each
- word in the list. */
- nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;
+#if 0
+ if (ifsname (name))
+ setifs (var);
+else
+#endif
+ if (flags)
+ stupidly_hack_special_variables (name);
- dispose_words (list);
+ 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);
- if (val != value)
- free (val);
+ free (name);
+ return 1;
+}
- a = array_cell (var);
+/* **************************************************************** */
+/* */
+/* Copying variables */
+/* */
+/* **************************************************************** */
- /* Now that we are ready to assign values to the array, kill the existing
- value. */
- if (a)
- empty_array (a);
+#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;
- for (last_ind = 0, list = nlist; list; list = list->next)
+ if (var)
{
- w = list->word->word;
+ copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
- /* We have a word of the form [ind]=value */
- if (w[0] == '[')
- {
- len = skipsubscript (w, 0);
+ copy->attributes = var->attributes;
+ copy->name = savestring (var->name);
- 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;
- }
+ if (function_p (var))
+ var_setfunc (copy, copy_command (function_cell (var)));
+#if defined (ARRAY_VARS)
+ else if (array_p (var))
+ var_setarray (copy, array_copy (array_cell (var)));
+ else if (assoc_p (var))
+ var_setassoc (copy, assoc_copy (assoc_cell (var)));
+#endif
+ else if (value_cell (var))
+ var_setvalue (copy, savestring (value_cell (var)));
+ else
+ var_setvalue (copy, (char *)NULL);
- if (len == 1)
- {
- report_error ("%s: bad array subscript", w);
- continue;
- }
+ copy->dynamic_value = var->dynamic_value;
+ copy->assign_func = var->assign_func;
- if (ALL_ELEMENT_SUB (w[1]) && len == 2)
- {
- report_error ("%s: cannot assign to non-numeric index", w);
- continue;
- }
+ copy->exportstr = COPY_EXPORTSTR (var);
- 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 (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++;
+ copy->context = var->context;
}
-
- dispose_words (nlist);
- return (var);
+ return (copy);
}
-#endif /* ARRAY_VARS */
+#endif
+
+/* **************************************************************** */
+/* */
+/* Deleting and unsetting variables */
+/* */
+/* **************************************************************** */
/* Dispose of the information attached to VAR. */
-void
-dispose_variable (var)
+static void
+dispose_variable_value (var)
SHELL_VAR *var;
{
- if (!var)
- return;
-
if (function_p (var))
dispose_command (function_cell (var));
#if defined (ARRAY_VARS)
else if (array_p (var))
- dispose_array (array_cell (var));
+ array_dispose (array_cell (var));
+ else if (assoc_p (var))
+ assoc_dispose (assoc_cell (var));
#endif
else
FREE (value_cell (var));
+}
+
+void
+dispose_variable (var)
+ SHELL_VAR *var;
+{
+ if (var == 0)
+ return;
+
+ if (nofree_p (var) == 0)
+ dispose_variable_value (var);
FREE_EXPORTSTR (var);
free (var);
}
-#if defined (ARRAY_VARS)
-/* This function is called with SUB pointing to just after the beginning
- `[' of an array subscript. */
+/* Unset the shell variable referenced by NAME. */
int
-unbind_array_element (var, sub)
- SHELL_VAR *var;
- char *sub;
+unbind_variable (name)
+ const char *name;
{
- int len, ind;
- ARRAY_ELEMENT *ae;
+ return makunbound (name, shell_variables);
+}
- len = skipsubscript (sub, 0);
- if (sub[len] != ']' || len == 0)
- {
- builtin_error ("%s[%s: bad array subscript", var->name, sub);
- return -1;
- }
- sub[len] = '\0';
+/* Unset the shell function named NAME. */
+int
+unbind_func (name)
+ const char *name;
+{
+ BUCKET_CONTENTS *elt;
+ SHELL_VAR *func;
- if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
- {
- makunbound (var->name, shell_variables);
- return (0);
- }
- ind = array_expand_index (sub, len+1);
- if (ind < 0)
+ elt = hash_remove (name, shell_functions, 0);
+
+ if (elt == 0)
+ return -1;
+
+#if defined (PROGRAMMABLE_COMPLETION)
+ set_itemlist_dirty (&it_functions);
+#endif
+
+ func = (SHELL_VAR *)elt->data;
+ if (func)
{
- builtin_error ("[%s]: bad array subscript", sub);
- return -1;
+ if (exported_p (func))
+ array_needs_making++;
+ dispose_variable (func);
}
- ae = array_delete_element (array_cell (var), ind);
- if (ae)
- destroy_array_element (ae);
- return 0;
+
+ free (elt->key);
+ free (elt);
+
+ return 0;
}
-#endif
-/* Unset the variable referenced by NAME. */
+#if defined (DEBUGGER)
int
-unbind_variable (name)
- char *name;
+unbind_function_def (name)
+ const char *name;
{
- SHELL_VAR *var;
+ BUCKET_CONTENTS *elt;
+ FUNCTION_DEF *funcdef;
- var = find_variable (name);
- if (!var)
- return (-1);
+ elt = hash_remove (name, shell_function_defs, 0);
- /* This function should never be called with an array variable name. */
-#if defined (ARRAY_VARS)
- if (array_p (var) == 0 && var->value)
-#else
- if (var->value)
-#endif
- {
- free (var->value);
- var->value = (char *)NULL;
- }
+ if (elt == 0)
+ return -1;
- makunbound (name, shell_variables);
+ funcdef = (FUNCTION_DEF *)elt->data;
+ if (funcdef)
+ dispose_function_def (funcdef);
- return (0);
+ free (elt->key);
+ free (elt);
+
+ return 0;
}
+#endif /* DEBUGGER */
/* 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;
+makunbound (name, vc)
+ const char *name;
+ VAR_CONTEXT *vc;
{
BUCKET_CONTENTS *elt, *new_elt;
- SHELL_VAR *old_var, *new_var;
+ SHELL_VAR *old_var;
+ VAR_CONTEXT *v;
char *t;
- elt = remove_hash_item (name, hash_list);
+ for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
+ if (elt = hash_remove (name, v->table, 0))
+ break;
if (elt == 0)
return (-1);
old_var = (SHELL_VAR *)elt->data;
- new_var = old_var->prev_context;
if (old_var && exported_p (old_var))
array_needs_making++;
-#if defined (PROGRAMMABLE_COMPLETION)
- if (hash_list == shell_functions)
- set_itemlist_dirty (&it_functions);
-#endif
-
/* If we're unsetting a local variable and we're still executing inside
- the function, just mark the variable as invisible.
- kill_all_local_variables will clean it up later. This must be done
- so that if the variable is subsequently assigned a new value inside
- the function, the `local' attribute is still present. We also need
- to add it back into the correct hash table. */
+ the function, just mark the variable as invisible. The function
+ eventually called by pop_var_context() will clean it up later. This
+ must be done so that if the variable is subsequently assigned a new
+ value inside the function, the `local' attribute is still present.
+ We also need to add it back into the correct hash table. */
if (old_var && local_p (old_var) && variable_context == old_var->context)
{
+ if (nofree_p (old_var))
+ var_setvalue (old_var, (char *)NULL);
+#if defined (ARRAY_VARS)
+ else if (array_p (old_var))
+ array_dispose (array_cell (old_var));
+ else if (assoc_p (old_var))
+ assoc_dispose (assoc_cell (old_var));
+#endif
+ else
+ FREE (value_cell (old_var));
+ /* Reset the attributes. Preserve the export attribute if the variable
+ came from a temporary environment. Make sure it stays local, and
+ make it invisible. */
+ old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0;
+ VSETATTR (old_var, att_local);
VSETATTR (old_var, att_invisible);
+ var_setvalue (old_var, (char *)NULL);
INVALIDATE_EXPORTSTR (old_var);
- new_elt = add_hash_item (savestring (old_var->name), hash_list);
- new_elt->data = (char *)old_var;
+
+ new_elt = hash_insert (savestring (old_var->name), v->table, 0);
+ new_elt->data = (PTR_T)old_var;
stupidly_hack_special_variables (old_var->name);
+
free (elt->key);
free (elt);
return (0);
}
- if (new_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);
- }
-
/* 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. */
dispose_variable (old_var);
stupidly_hack_special_variables (t);
free (t);
- return (0);
-}
-
-#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;
- temp = find_variable (name);
- if (temp && temp->context == variable_context)
- {
- makunbound (name, shell_variables);
- return (0);
- }
- return (-1);
+ return (0);
}
-#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);
-}
-
void
kill_all_local_variables ()
{
- register int i, pass;
- register SHELL_VAR *var, **list;
- HASH_TABLE *varlist;
-
- /* If HAVE_LOCAL_VARIABLES == 0, it means that we don't have any local
- variables at all. If VARIABLE_CONTEXT >= LOCAL_VARIABLE_STACK_SIZE,
- it means that we have some local variables, but not in this variable
- context (level of function nesting). Also, if
- HAVE_LOCAL_VARIABLES[VARIABLE_CONTEXT] == 0, we have no local variables
- at this context. */
- if (have_local_variables == 0 ||
- variable_context >= local_variable_stack_size ||
- have_local_variables[variable_context] == 0)
- return;
-
- for (pass = 0; pass < 2; pass++)
- {
- varlist = pass ? shell_functions : shell_variables;
+ VAR_CONTEXT *vc;
- list = map_over (variable_in_context, varlist);
+ for (vc = shell_variables; vc; vc = vc->down)
+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
+ break;
+ if (vc == 0)
+ return; /* XXX */
- if (list)
- {
- for (i = 0; var = list[i]; i++)
- {
- VUNSETATTR (var, att_local);
- makunbound (var->name, varlist);
- }
- free (list);
- }
+ if (vc->table && vc_haslocals (vc))
+ {
+ delete_all_variables (vc->table);
+ hash_dispose (vc->table);
}
-
- have_local_variables[variable_context] = 0; /* XXX */
+ vc->table = (HASH_TABLE *)NULL;
}
static void
free_variable_hash_data (data)
- char *data;
+ PTR_T data;
{
- SHELL_VAR *var, *prev;
+ SHELL_VAR *var;
var = (SHELL_VAR *)data;
- while (var)
- {
- prev = var->prev_context;
- dispose_variable (var);
- var = prev;
- }
+ dispose_variable (var);
}
/* Delete the entire contents of the hash table. */
delete_all_variables (hashed_vars)
HASH_TABLE *hashed_vars;
{
- flush_hash_table (hashed_vars, free_variable_hash_data);
-}
-
-static SHELL_VAR *
-new_shell_variable (name)
- char *name;
-{
- SHELL_VAR *var;
-
- var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-
- bzero ((char *)var, sizeof (SHELL_VAR));
- var->name = savestring (name);
- return (var);
-}
-
-/* Do a function binding to a variable. You pass the name and
- the command to bind to. This conses the name and command. */
-SHELL_VAR *
-bind_function (name, value)
- char *name;
- COMMAND *value;
-{
- SHELL_VAR *entry;
-
- entry = find_function (name);
- if (!entry)
- {
- BUCKET_CONTENTS *elt;
-
- elt = add_hash_item (savestring (name), shell_functions);
-
- entry = new_shell_variable (name);
- entry->dynamic_value = entry->assign_func = (DYNAMIC_FUNC *)NULL;
- CLEAR_EXPORTSTR (entry);
-
- /* Functions are always made at the top level. This allows a
- function to define another function (like autoload). */
- entry->context = 0;
-
- elt->data = (char *)entry;
- }
-
- INVALIDATE_EXPORTSTR (entry);
-
- if (entry->value)
- dispose_command ((COMMAND *)entry->value);
-
- entry->value = value ? (char *)copy_command (value) : (char *)NULL;
- VSETATTR (entry, att_function);
-
- if (mark_modified_vars)
- VSETATTR (entry, att_exported);
-
- VUNSETATTR (entry, att_invisible); /* Just to be sure */
-
- if (exported_p (entry))
- array_needs_making = 1;
-
-#if defined (PROGRAMMABLE_COMPLETION)
- set_itemlist_dirty (&it_functions);
-#endif
-
- return (entry);
+ hash_flush (hashed_vars, free_variable_hash_data);
}
-#ifdef INCLUDE_UNUSED
-/* Copy VAR to a new data structure and return that structure. */
-SHELL_VAR *
-copy_variable (var)
- SHELL_VAR *var;
-{
- SHELL_VAR *copy = (SHELL_VAR *)NULL;
-
- if (var)
- {
- copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-
- copy->attributes = var->attributes;
- copy->name = savestring (var->name);
-
- if (function_p (var))
- copy->value = (char *)copy_command (function_cell (var));
-#if defined (ARRAY_VARS)
- else if (array_p (var))
- copy->value = (char *)dup_array (array_cell (var));
-#endif
- else if (value_cell (var))
- copy->value = savestring (value_cell (var));
- else
- copy->value = (char *)NULL;
-
- copy->dynamic_value = var->dynamic_value;
- copy->assign_func = var->assign_func;
-
- copy->exportstr = COPY_EXPORTSTR (var);
-
- copy->context = var->context;
-
- /* Don't bother copying previous contexts along with this variable. */
- copy->prev_context = (SHELL_VAR *)NULL;
- }
- return (copy);
-}
-#endif
+/* **************************************************************** */
+/* */
+/* Setting variable attributes */
+/* */
+/* **************************************************************** */
#define FIND_OR_MAKE_VARIABLE(name, entry) \
do \
entry = find_variable (name); \
if (!entry) \
{ \
- entry = bind_variable (name, ""); \
- if (!no_invisible_vars) entry->attributes |= att_invisible; \
+ entry = bind_variable (name, "", 0); \
+ if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \
} \
} \
while (0)
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;
/* Make the function associated with NAME be auto-exported. */
void
set_func_auto_export (name)
- char *name;
+ const char *name;
{
SHELL_VAR *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;
-{
- int count, c;
-
- for (count = 1; count && (c = s[++i]); )
- {
- if (c == '[')
- count++;
- else if (c == ']')
- count--;
- }
- return i;
-}
-#endif /* ARRAY_VARS */
+/* **************************************************************** */
+/* */
+/* Creating lists of variables */
+/* */
+/* **************************************************************** */
-/* Returns non-zero if STRING is an assignment statement. The returned value
- is the index of the `=' sign. */
-int
-assignment (string)
- char *string;
+static VARLIST *
+vlist_alloc (nentries)
+ int nentries;
{
- register int c, newi, indx;
+ VARLIST *vlist;
- c = string[indx = 0];
+ 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;
- if (legal_variable_starter (c) == 0)
- return (0);
+ return vlist;
+}
- while (c = string[indx])
+static VARLIST *
+vlist_realloc (vlist, n)
+ VARLIST *vlist;
+ int n;
+{
+ if (vlist == 0)
+ return (vlist = vlist_alloc (n));
+ if (n > vlist->list_size)
{
- /* The following is safe. Note that '=' at the start of a word
- is not an assignment statement. */
- if (c == '=')
- return (indx);
-
-#if defined (ARRAY_VARS)
- if (c == '[')
- {
- newi = skipsubscript (string, indx);
- if (string[newi++] != ']')
- return (0);
- return ((string[newi] == '=') ? newi : 0);
- }
-#endif /* ARRAY_VARS */
-
- /* Variable names in assignment statements may contain only letters,
- digits, and `_'. */
- if (legal_variable_char (c) == 0)
- return (0);
-
- indx++;
+ vlist->list_size = n;
+ vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
}
- return (0);
+ return vlist;
}
-static int
-visible_var (var)
+static void
+vlist_add (vlist, var, flags)
+ VARLIST *vlist;
SHELL_VAR *var;
+ int flags;
{
- return (invisible_p (var) == 0);
+ register int i;
+
+ for (i = 0; i < vlist->list_len; i++)
+ if (STREQ (var->name, vlist->list[i]->name))
+ break;
+ if (i < vlist->list_len)
+ return;
+
+ if (i >= vlist->list_size)
+ vlist = vlist_realloc (vlist, vlist->list_size + 16);
+
+ vlist->list[vlist->list_len++] = var;
+ vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
}
-static SHELL_VAR **
-_visible_names (table)
- HASH_TABLE *table;
+/* 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;
{
- SHELL_VAR **list;
+ VAR_CONTEXT *v;
+ VARLIST *vlist;
+ SHELL_VAR **ret;
+ int nentries;
- list = map_over (visible_var, table);
+ for (nentries = 0, v = vc; v; v = v->down)
+ nentries += HASH_ENTRIES (v->table);
- if (list /* && posixly_correct */)
- sort_variables (list);
+ if (nentries == 0)
+ return (SHELL_VAR **)NULL;
- return (list);
+ 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 **
-all_visible_functions ()
+map_over_funcs (function)
+ sh_var_map_func_t *function;
{
- return (_visible_names (shell_functions));
+ 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;
}
-SHELL_VAR **
-all_visible_variables ()
+/* 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;
{
- return (_visible_names (shell_variables));
-}
+ register int i;
+ register BUCKET_CONTENTS *tlist;
+ int r;
+ SHELL_VAR *var;
-/* Return non-zero if the variable VAR is visible and exported. Array
+ if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0))
+ return;
+
+ for (i = 0; i < var_hash_table->nbuckets; i++)
+ {
+ for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next)
+ {
+ var = (SHELL_VAR *)tlist->data;
+
+ r = func ? (*func) (var) : 1;
+ if (r && vlist)
+ vlist_add (vlist, var, flags);
+ }
+ }
+}
+
+void
+sort_variables (array)
+ SHELL_VAR **array;
+{
+ qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp);
+}
+
+static int
+qsort_var_comp (var1, var2)
+ SHELL_VAR **var1, **var2;
+{
+ int result;
+
+ if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
+ result = strcmp ((*var1)->name, (*var2)->name);
+
+ return (result);
+}
+
+/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for
+ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */
+static SHELL_VAR **
+vapply (func)
+ sh_var_map_func_t *func;
+{
+ SHELL_VAR **list;
+
+ list = map_over (func, shell_variables);
+ if (list /* && posixly_correct */)
+ sort_variables (list);
+ return (list);
+}
+
+/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for
+ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */
+static SHELL_VAR **
+fapply (func)
+ sh_var_map_func_t *func;
+{
+ SHELL_VAR **list;
+
+ list = map_over_funcs (func);
+ if (list /* && posixly_correct */)
+ sort_variables (list);
+ return (list);
+}
+
+/* Create a NULL terminated array of all the shell variables. */
+SHELL_VAR **
+all_shell_variables ()
+{
+ return (vapply ((sh_var_map_func_t *)NULL));
+}
+
+/* Create a NULL terminated array of all the shell functions. */
+SHELL_VAR **
+all_shell_functions ()
+{
+ return (fapply ((sh_var_map_func_t *)NULL));
+}
+
+static int
+visible_var (var)
+ SHELL_VAR *var;
+{
+ return (invisible_p (var) == 0);
+}
+
+SHELL_VAR **
+all_visible_functions ()
+{
+ return (fapply (visible_var));
+}
+
+SHELL_VAR **
+all_visible_variables ()
+{
+ return (vapply (visible_var));
+}
+
+/* Return non-zero if the variable VAR is visible and exported. Array
variables cannot be exported. */
static int
visible_and_exported (var)
return (invisible_p (var) == 0 && exported_p (var));
}
+/* Candidate variables for the export environment are either valid variables
+ with the export attribute or invalid variables inherited from the initial
+ environment and simply passed through. */
+static int
+export_environment_candidate (var)
+ SHELL_VAR *var;
+{
+ return (exported_p (var) && (invisible_p (var) == 0 || imported_p (var)));
+}
+
+/* Return non-zero if VAR is a local variable in the current context and
+ is exported. */
+static int
+local_and_exported (var)
+ SHELL_VAR *var;
+{
+ return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var));
+}
+
SHELL_VAR **
all_exported_variables ()
{
- SHELL_VAR **list;
+ return (vapply (visible_and_exported));
+}
- list = map_over (visible_and_exported, shell_variables);
- if (list)
- sort_variables (list);
- return (list);
+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)
SHELL_VAR **
all_array_variables ()
{
- SHELL_VAR **list;
-
- list = map_over (visible_array_vars, shell_variables);
- if (list)
- sort_variables (list);
- return (list);
+ return (vapply (visible_array_vars));
}
#endif /* ARRAY_VARS */
char **
all_variables_matching_prefix (prefix)
- char *prefix;
+ const char *prefix;
{
SHELL_VAR **varlist;
char **rlist;
;
if (varlist == 0 || vind == 0)
return ((char **)NULL);
- rlist = alloc_array (vind + 1);
+ 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++] = 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);
+}
+
+char **tempvar_list;
+int tvlist_ind;
+
+/* 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, 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;
+
+ if (find_special_var (var->name) >= 0)
+ tempvar_list[tvlist_ind++] = savestring (var->name);
+
+ 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
+ {
+ if (find_special_var (var->name) >= 0)
+ tempvar_list[tvlist_ind++] = savestring (var->name);
+ 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. PUSHF stores names of variables
+ that require special handling (e.g., IFS) on tempvar_list, so this
+ function can call stupidly_hack_special_variables on all the
+ variables in the list when the temporary hash table is destroyed. */
+static void
+dispose_temporary_env (pushf)
+ sh_free_func_t *pushf;
+{
+ int i;
+
+ tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1);
+ tempvar_list[tvlist_ind = 0] = 0;
+
+ hash_flush (temporary_env, pushf);
+ hash_dispose (temporary_env);
+ temporary_env = (HASH_TABLE *)NULL;
+
+ tempvar_list[tvlist_ind] = 0;
+
+ array_needs_making = 1;
+
+#if 0
+ sv_ifs ("IFS"); /* XXX here for now -- check setifs in assign_in_env */
+#endif
+ for (i = 0; i < tvlist_ind; i++)
+ stupidly_hack_special_variables (tempvar_list[i]);
+
+ strvec_dispose (tempvar_list);
+ tempvar_list = 0;
+ tvlist_ind = 0;
+}
+
+void
+dispose_used_env_vars ()
+{
+ if (temporary_env)
+ {
+ dispose_temporary_env (propagate_temp_var);
+ maybe_make_export_env ();
+ }
+}
+
+/* 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)
- char *name, *value;
+ const char *name, *value;
{
int name_len, value_len;
char *p;
name_len = strlen (name);
value_len = STRLEN (value);
- p = xmalloc (2 + name_len + value_len);
+ p = (char *)xmalloc (2 + name_len + value_len);
strcpy (p, name);
p[name_len] = '=';
if (value && *value)
return (p);
}
+#ifdef DEBUG
/* Debugging */
static int
valid_exportstr (v)
char *s;
s = v->exportstr;
- if (legal_variable_starter (*s) == 0)
+ if (s == 0)
{
- internal_error ("invalid character %d in exportstr for %s", *s, v->name);
+ internal_error (_("%s has null exportstr"), v->name);
+ return (0);
+ }
+ 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 (*s) == 0)
+ break;
+ if (legal_variable_char ((unsigned char)*s) == 0)
{
- internal_error ("invalid character %d in exportstr for %s", *s, v->name);
+ 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);
+ internal_error (_("no `=' in exportstr for %s"), v->name);
return (0);
}
return (1);
}
+#endif
-/* Make an array of assignment statements from the hash table
- HASHED_VARS which contains SHELL_VARs. Only visible, exported
- variables are eligible. */
-char **
-make_var_array (hashed_vars)
- HASH_TABLE *hashed_vars;
+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 = alloc_array ((1 + array_len ((char **)vars)));
+ list = strvec_create ((1 + strvec_len ((char **)vars)));
#define USE_EXPORTSTR (value == var->exportstr)
for (i = 0, list_index = 0; var = vars[i]; i++)
{
- if (var->exportstr)
- {
-#if defined(__CYGWIN__) || defined (__CYGWIN32__)
- INVALIDATE_EXPORTSTR (var);
- value = value_cell (var);
-#else
- /* XXX -- this test can go away in the next release, to be replaced
- by a simple `value = var->exportstr;', when the exportstr code
- is better-tested. Until then, don't do it for cygwin at all,
- since that system has some weird environment variables. */
- if (valid_exportstr (var))
- value = var->exportstr;
- else
- {
- INVALIDATE_EXPORTSTR (var);
- value = value_cell (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)
value = array_to_assignment_string (array_cell (var));
# else
continue; /* XXX array vars cannot yet be exported */
+# endif
+ else if (assoc_p (var))
+# if 0
+ value = assoc_to_assignment_string (assoc_cell (var));
+# else
+ continue; /* XXX associative array vars cannot yet be exported */
# endif
#endif
else
list[list_index] = USE_EXPORTSTR ? savestring (value)
: mk_env_string (var->name, value);
- if (USE_EXPORTSTR == 0 && function_p (var))
- {
- SAVE_EXPORTSTR (var, list[list_index]);
- }
+ 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))
+ if (array_p (var) || assoc_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;
{
- int size, offset;
- char *name, *temp, *value;
- int nlen, vlen;
- WORD_LIST *list;
- SHELL_VAR *var;
+ char **list;
+ SHELL_VAR **vars;
- offset = assignment (string);
- name = savestring (string);
- value = (char *)NULL;
+#if 0
+ vars = map_over (visible_and_exported, vcxt);
+#else
+ vars = map_over (export_environment_candidate, vcxt);
+#endif
- if (name[offset] == '=')
- {
- name[offset] = 0;
+ if (vars == 0)
+ return (char **)NULL;
- var = find_variable (name);
- if (var && readonly_p (var))
- {
- report_error ("%s: readonly variable", name);
- free (name);
- return (0);
- }
- temp = name + offset + 1;
- temp = (strchr (temp, '~') != 0) ? bash_tilde_expand (temp) : savestring (temp);
+ list = make_env_array_from_var_list (vars);
- list = expand_string_unsplit (temp, 0);
- value = string_list (list);
+ free (vars);
+ return (list);
+}
- if (list)
- dispose_words (list);
+static char **
+make_func_export_array ()
+{
+ char **list;
+ SHELL_VAR **vars;
- free (temp);
- }
+ vars = map_over_funcs (visible_and_exported);
+ if (vars == 0)
+ return (char **)NULL;
- temp = mk_env_string (name, value);
- FREE (value);
- free (name);
+ list = make_env_array_from_var_list (vars);
- if (temporary_env == 0)
- {
- temporary_env = (char **)xmalloc (sizeof (char *));
- temporary_env [0] = (char *)NULL;
- }
+ free (vars);
+ return (list);
+}
- size = array_len (temporary_env);
- temporary_env = (char **)
- xrealloc (temporary_env, (size + 2) * (sizeof (char *)));
+/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */
+#define add_to_export_env(envstr,do_alloc) \
+do \
+ { \
+ if (export_env_index >= (export_env_size - 1)) \
+ { \
+ export_env_size += 16; \
+ export_env = strvec_resize (export_env, export_env_size); \
+ environ = export_env; \
+ } \
+ export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \
+ export_env[export_env_index] = (char *)NULL; \
+ } while (0)
- temporary_env[size] = temp;
- temporary_env[size + 1] = (char *)NULL;
- array_needs_making = 1;
+/* 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;
- if (echo_command_at_execute)
+ 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++)
{
- /* 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 (STREQN (assign, export_env[i], equal_offset + 1))
+ {
+ free (export_env[i]);
+ export_env[i] = do_alloc ? savestring (assign) : assign;
+ return (export_env);
+ }
}
-
- return 1;
+ add_to_export_env (assign, do_alloc);
+ return (export_env);
}
-/* 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;
+static void
+add_temp_array_to_env (temp_array, do_alloc, do_supercede)
+ char **temp_array;
+ int do_alloc, do_supercede;
{
- register int i, l;
+ register int i;
- if (array == 0)
- return ((SHELL_VAR *)NULL);
+ if (temp_array == 0)
+ return;
- for (i = 0, l = strlen (name); array[i]; i++)
+ for (i = 0; temp_array[i]; i++)
{
- if (STREQN (array[i], name, l) && array[i][l] == '=')
- {
- SHELL_VAR *temp;
- char *w;
+ 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);
+ }
- /* This is a potential memory leak. The code should really save
- the created variables in some auxiliary data structure, which
- can be disposed of at the appropriate time. */
- temp = new_shell_variable (name);
- w = array[i] + l + 1;
+ free (temp_array);
+}
- temp->value = *w ? savestring (w) : (char *)NULL;
+/* 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++'.
- temp->attributes = att_exported|att_tempvar;
- temp->context = 0;
- temp->prev_context = (SHELL_VAR *)NULL;
+ The order to add to the array is:
+ temporary_env
+ list of var contexts whose head is shell_variables
+ shell_functions
- temp->dynamic_value = temp->assign_func = (DYNAMIC_FUNC *)NULL;
- CLEAR_EXPORTSTR (temp);
+ 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.
+*/
- return (temp);
- }
- }
- return ((SHELL_VAR *)NULL);
+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;
}
-/* 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)
+int
+chkexport (name)
char *name;
{
- SHELL_VAR *var;
+ SHELL_VAR *v;
- var = (SHELL_VAR *)NULL;
+ v = find_variable (name);
+ if (v && exported_p (v))
+ {
+ array_needs_making = 1;
+ maybe_make_export_env ();
+ return 1;
+ }
+ return 0;
+}
- if (temporary_env)
- var = find_name_in_env_array (name, temporary_env);
+void
+maybe_make_export_env ()
+{
+ register char **temp_array;
+ int new_size;
+ VAR_CONTEXT *tcxt;
- /* We don't check this_shell_builtin because the command that needs the
- value from builtin_env may be a disk command run inside a script run
- with `.' and a temporary env. */
- if (!var && builtin_env)
- var = find_name_in_env_array (name, builtin_env);
+ if (array_needs_making)
+ {
+ if (export_env)
+ strvec_flush (export_env);
- if (!var && variable_context && function_env)
- var = find_name_in_env_array (name, function_env);
+ /* Make a guess based on how many shell variables and functions we
+ have. Since there will always be array variables, and array
+ variables are not (yet) exported, this will always be big enough
+ for the exported variables and functions. */
+ new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 +
+ HASH_ENTRIES (temporary_env);
+ if (new_size > export_env_size)
+ {
+ export_env_size = new_size;
+ export_env = strvec_resize (export_env, export_env_size);
+ environ = export_env;
+ }
+ export_env[export_env_index = 0] = (char *)NULL;
- return (var);
-}
+ /* 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);
-/* 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;
-{
- if (!*arrayp)
- return;
+ if (tcxt != shell_variables)
+ free (tcxt);
- free_array (*arrayp);
- *arrayp = (char **)NULL;
- array_needs_making = 1;
+#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;
+ }
}
-/* Free the storage used in the variable array for temporary
- environment variables. */
+/* 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
-dispose_used_env_vars ()
+update_export_env_inplace (env_prefix, preflen, value)
+ char *env_prefix;
+ int preflen;
+ char *value;
{
- dispose_temporary_vars (&temporary_env);
+ 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);
}
-/* Free the storage used for temporary environment variables given to
- commands when executing inside of a function body. */
+/* We always put _ in the environment as the name of this command. */
void
-dispose_function_env ()
+put_command_name_into_env (command_name)
+ char *command_name;
{
- dispose_temporary_vars (&function_env);
+ update_export_env_inplace ("_=", 2, command_name);
}
-/* Free the storage used for temporary environment variables given to
- commands when executing a builtin command such as "source". */
+#if 0 /* UNUSED -- it caused too many problems */
void
-dispose_builtin_env ()
+put_gnu_argv_flags_into_env (pid, flags_string)
+ intmax_t pid;
+ char *flags_string;
{
- dispose_temporary_vars (&builtin_env);
+ 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
-/* 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;
+/* **************************************************************** */
+/* */
+/* 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;
{
- register int i, l;
- SHELL_VAR *temp;
- char *val, *name;
+ VAR_CONTEXT *vc;
- if (env_array == 0)
- return;
+ vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT));
+ vc->name = name ? savestring (name) : (char *)NULL;
+ vc->scope = variable_context;
+ vc->flags = flags;
- for (i = 0; env_array[i]; i++)
- {
- 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] = '=';
- }
+ 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
-merge_temporary_env ()
+dispose_var_context (vc)
+ VAR_CONTEXT *vc;
{
- merge_env_array (temporary_env);
+ FREE (vc->name);
+
+ if (vc->table)
+ {
+ delete_all_variables (vc->table);
+ hash_dispose (vc->table);
+ }
+
+ free (vc);
}
-void
-merge_builtin_env ()
+/* Set VAR's scope level to the current variable context. */
+static int
+set_context (var)
+ SHELL_VAR *var;
{
- merge_env_array (builtin_env);
+ return (var->context = variable_context);
}
-int
-any_temporary_variables ()
+/* 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;
{
- return (temporary_env || function_env);
-}
+ VAR_CONTEXT *vc;
-/* 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)
+ 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;
-#define ISFUNCTION(s, o) ((s[o + 1] == '(') && (s[o + 2] == ')'))
+ return (shell_variables = vc);
+}
-/* 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;
+static void
+push_func_var (data)
+ PTR_T data;
{
- register int i;
- int equal_offset;
+ SHELL_VAR *var, *v;
- equal_offset = assignment (assign);
- if (equal_offset == 0)
- return (export_env);
+ var = (SHELL_VAR *)data;
- /* If this is a function, then only supercede the function definition.
- We do this by including the `=(' in the comparison. */
- if (assign[equal_offset + 1] == '(')
- equal_offset++;
+ if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
+ {
+ /* Make sure we have a hash table to store the variable in while it is
+ being propagated down to the global variables table. Create one if
+ we have to */
+ if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0)
+ shell_variables->table = hash_create (0);
+ /* XXX - should we set v->context here? */
+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
+ if (shell_variables == global_variables)
+ var->attributes &= ~(att_tempvar|att_propagate);
+ else
+ shell_variables->flags |= VC_HASTMPVAR;
+ v->attributes |= var->attributes;
+ }
+ else
+ stupidly_hack_special_variables (var->name); /* XXX */
- for (i = 0; i < export_env_index; i++)
+ 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)
{
- if (STREQN (assign, export_env[i], equal_offset + 1))
- {
- free (export_env[i]);
- export_env[i] = do_alloc ? savestring (assign) : assign;
- return (export_env);
- }
+ internal_error (_("pop_var_context: head of shell_variables not a function context"));
+ return;
}
- add_to_export_env (assign, do_alloc);
- return (export_env);
+
+ if (ret = vcxt->down)
+ {
+ ret->up = (VAR_CONTEXT *)NULL;
+ shell_variables = ret;
+ if (vcxt->table)
+ hash_flush (vcxt->table, push_func_var);
+ dispose_var_context (vcxt);
+ }
+ else
+ internal_error (_("pop_var_context: no global_variables context"));
}
-/* Make the 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++'. */
+/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
+ all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
void
-maybe_make_export_env ()
+delete_all_contexts (vcxt)
+ VAR_CONTEXT *vcxt;
{
- register int i;
- register char **temp_array;
- int new_size;
+ VAR_CONTEXT *v, *t;
- if (array_needs_making)
+ for (v = vcxt; v != global_variables; v = t)
{
- if (export_env)
- free_array_members (export_env);
+ t = v->down;
+ dispose_var_context (v);
+ }
- /* 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 = HASH_ENTRIES (shell_variables) + HASH_ENTRIES (shell_functions) + 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;
+ delete_all_variables (global_variables->table);
+ shell_variables = global_variables;
+}
- 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);
- }
+/* **************************************************************** */
+/* */
+/* Pushing and Popping temporary variable scopes */
+/* */
+/* **************************************************************** */
- temp_array = make_var_array (shell_functions);
- if (temp_array)
- {
- for (i = 0; temp_array[i]; i++)
- add_to_export_env (temp_array[i], 0);
- free (temp_array);
- }
+VAR_CONTEXT *
+push_scope (flags, tmpvars)
+ int flags;
+ HASH_TABLE *tmpvars;
+{
+ return (push_var_context ((char *)NULL, flags, tmpvars));
+}
- if (function_env)
- for (i = 0; function_env[i]; i++)
- export_env = add_or_supercede_exported_var (function_env[i], 1);
+static void
+push_exported_var (data)
+ PTR_T data;
+{
+ SHELL_VAR *var, *v;
- if (temporary_env)
- for (i = 0; temporary_env[i]; i++)
- export_env = add_or_supercede_exported_var (temporary_env[i], 1);
+ var = (SHELL_VAR *)data;
+ /* If a temp var had its export attribute set, or it's marked to be
+ propagated, bind it in the previous scope before disposing it. */
+ /* XXX - This isn't exactly right, because all tempenv variables have the
+ export attribute set. */
#if 0
- /* If we changed the array, then sort it alphabetically. */
- if (posixly_correct == 0 && (temporary_env || function_env))
- sort_char_array (export_env);
+ if (exported_p (var) || (var->attributes & att_propagate))
+#else
+ if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
#endif
+ {
+ var->attributes &= ~att_tempvar; /* XXX */
+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
+ if (shell_variables == global_variables)
+ var->attributes &= ~att_propagate;
+ v->attributes |= var->attributes;
+ }
+ else
+ stupidly_hack_special_variables (var->name); /* XXX */
- array_needs_making = 0;
+ 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 */
}
-/* 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.
+/* **************************************************************** */
+/* */
+/* 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;
- 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. */
+/* XXX - we might want to consider pushing and popping the `getopts' state
+ when we modify the positional parameters. */
void
-update_export_env_inplace (env_prefix, preflen, value)
- char *env_prefix;
- int preflen;
- char *value;
+push_context (name, is_subshell, tempvars)
+ char *name; /* function name */
+ int is_subshell;
+ HASH_TABLE *tempvars;
{
- char *evar;
-
- evar = xmalloc (STRLEN (value) + preflen + 1);
- strcpy (evar, env_prefix);
- if (value)
- strcpy (evar + preflen, value);
- export_env = add_or_supercede_exported_var (evar, 0);
+ if (is_subshell == 0)
+ push_dollar_vars ();
+ variable_context++;
+ push_var_context (name, VC_FUNCENV, tempvars);
}
-/* We always put _ in the environment as the name of this command. */
+/* Only called when subshell == 0, so we don't need to check, and can
+ unconditionally pop the dollar vars off the stack. */
void
-put_command_name_into_env (command_name)
- char *command_name;
+pop_context ()
{
- update_export_env_inplace ("_=", 2, command_name);
+ pop_dollar_vars ();
+ variable_context--;
+ pop_var_context ();
+
+ sv_ifs ("IFS"); /* XXX here for now */
}
-#if 0 /* UNUSED -- it caused too many problems */
+/* Save the existing positional parameters on a stack. */
void
-put_gnu_argv_flags_into_env (pid, flags_string)
- int pid;
- char *flags_string;
+push_dollar_vars ()
{
- char *dummy, *pbuf;
- int l, fl;
-
- pbuf = itos (pid);
- l = strlen (pbuf);
+ 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;
+}
- fl = strlen (flags_string);
+/* Restore the positional parameters from our stack. */
+void
+pop_dollar_vars ()
+{
+ if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+ return;
- 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);
+ 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 ();
+}
- free (pbuf);
+void
+dispose_saved_dollar_vars ()
+{
+ if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+ return;
- export_env = add_or_supercede_exported_var (dummy, 0);
+ dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
}
-#endif
-/* Return a string denoting what our indirection level is. */
-static char indirection_string[100];
+/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */
-char *
-indirection_level_string ()
+void
+push_args (list)
+ WORD_LIST *list;
{
- register int i, j;
- char *ps4;
-
- indirection_string[0] = '\0';
- ps4 = get_string_value ("PS4");
-
- if (ps4 == 0 || *ps4 == '\0')
- return (indirection_string);
+#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;
- ps4 = decode_prompt_string (ps4);
+ 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 (i = 0; *ps4 && i < indirection_level && i < 99; i++)
- indirection_string[i] = *ps4;
+ for (l = list, i = 0; l; l = l->next, i++)
+ array_push (bash_argv_a, l->word->word);
- for (j = 1; *ps4 && ps4[j] && i < 99; i++, j++)
- indirection_string[i] = ps4[j];
+ t = itos (i);
+ array_push (bash_argc_a, t);
+ free (t);
+#endif /* ARRAY_VARS && DEBUGGER */
+}
- indirection_string[i] = '\0';
- free (ps4);
- return (indirection_string);
+/* Remove arguments from BASH_ARGV array. Pop top element off BASH_ARGC
+ array and use that value as the count of elements to remove from
+ BASH_ARGV. */
+void
+pop_args ()
+{
+#if defined (ARRAY_VARS) && defined (DEBUGGER)
+ SHELL_VAR *bash_argv_v, *bash_argc_v;
+ ARRAY *bash_argv_a, *bash_argc_a;
+ ARRAY_ELEMENT *ce;
+ intmax_t i;
+
+ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
+ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
+
+ ce = array_shift (bash_argc_a, 1, 0);
+ if (ce == 0 || legal_number (element_value (ce), &i) == 0)
+ i = 0;
+
+ for ( ; i > 0; i--)
+ array_pop (bash_argv_a);
+ array_dispose_element (ce);
+#endif /* ARRAY_VARS && DEBUGGER */
}
/*************************************************
extern int eof_encountered, eof_encountered_limit, ignoreeof;
#if defined (READLINE)
-extern int no_line_editing;
extern int hostname_list_initialized;
#endif
#define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0
+/* This table will be sorted with qsort() the first time it's accessed. */
struct name_and_function {
char *name;
- VFunction *function;
-} special_vars[] = {
- { "PATH", sv_path },
- { "MAIL", sv_mail },
- { "MAILPATH", sv_mail },
- { "MAILCHECK", sv_mail },
+ sh_sv_func_t *function;
+};
- { "POSIXLY_CORRECT", sv_strict_posix },
- { "GLOBIGNORE", sv_globignore },
+static struct name_and_function special_vars[] = {
+ { "BASH_XTRACEFD", sv_xtracefd },
- /* Variables which only do something special when READLINE is defined. */
#if defined (READLINE)
- { "TERM", sv_terminal },
- { "TERMCAP", sv_terminal },
- { "TERMINFO", sv_terminal },
- { "HOSTFILE", sv_hostfile },
-#endif /* READLINE */
+# if defined (STRICT_POSIX)
+ { "COLUMNS", sv_winsize },
+# endif
+ { "COMP_WORDBREAKS", sv_comp_wordbreaks },
+#endif
+
+ { "FUNCNEST", sv_funcnest },
+
+ { "GLOBIGNORE", sv_globignore },
- /* Variables which only do something special when HISTORY is defined. */
#if defined (HISTORY)
+ { "HISTCONTROL", sv_history_control },
+ { "HISTFILESIZE", sv_histsize },
{ "HISTIGNORE", sv_histignore },
{ "HISTSIZE", sv_histsize },
- { "HISTFILESIZE", sv_histsize },
- { "HISTCONTROL", sv_history_control },
-# if defined (BANG_HISTORY)
- { "histchars", sv_histchars },
-# endif /* BANG_HISTORY */
-#endif /* HISTORY */
+ { "HISTTIMEFORMAT", sv_histtimefmt },
+#endif
- { "IGNOREEOF", sv_ignoreeof },
- { "ignoreeof", sv_ignoreeof },
+#if defined (__CYGWIN__)
+ { "HOME", sv_home },
+#endif
- { "OPTIND", sv_optind },
- { "OPTERR", sv_opterr },
+#if defined (READLINE)
+ { "HOSTFILE", sv_hostfile },
+#endif
- { "TEXTDOMAIN", sv_locale },
- { "TEXTDOMAINDIR", sv_locale },
+ { "IFS", sv_ifs },
+ { "IGNOREEOF", sv_ignoreeof },
+
+ { "LANG", sv_locale },
{ "LC_ALL", sv_locale },
{ "LC_COLLATE", sv_locale },
{ "LC_CTYPE", sv_locale },
{ "LC_MESSAGES", sv_locale },
{ "LC_NUMERIC", sv_locale },
- { "LANG", sv_locale },
+ { "LC_TIME", sv_locale },
+
+#if defined (READLINE) && defined (STRICT_POSIX)
+ { "LINES", sv_winsize },
+#endif
+
+ { "MAIL", sv_mail },
+ { "MAILCHECK", sv_mail },
+ { "MAILPATH", sv_mail },
+
+ { "OPTERR", sv_opterr },
+ { "OPTIND", sv_optind },
+
+ { "PATH", sv_path },
+ { "POSIXLY_CORRECT", sv_strict_posix },
-#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+#if defined (READLINE)
+ { "TERM", sv_terminal },
+ { "TERMCAP", sv_terminal },
+ { "TERMINFO", sv_terminal },
+#endif /* READLINE */
+
+ { "TEXTDOMAIN", sv_locale },
+ { "TEXTDOMAINDIR", sv_locale },
+
+#if defined (HAVE_TZSET)
{ "TZ", sv_tz },
#endif
- { (char *)0, (VFunction *)0 }
+#if defined (HISTORY) && defined (BANG_HISTORY)
+ { "histchars", sv_histchars },
+#endif /* HISTORY && BANG_HISTORY */
+
+ { "ignoreeof", sv_ignoreeof },
+
+ { (char *)0, (sh_sv_func_t *)0 }
};
+#define N_SPECIAL_VARS (sizeof (special_vars) / sizeof (special_vars[0]) - 1)
+
+static int
+sv_compare (sv1, sv2)
+ struct name_and_function *sv1, *sv2;
+{
+ int r;
+
+ if ((r = sv1->name[0] - sv2->name[0]) == 0)
+ r = strcmp (sv1->name, sv2->name);
+ return r;
+}
+
+static inline int
+find_special_var (name)
+ const char *name;
+{
+ register int i, r;
+
+ for (i = 0; special_vars[i].name; i++)
+ {
+ r = special_vars[i].name[0] - name[0];
+ if (r == 0)
+ r = strcmp (special_vars[i].name, name);
+ if (r == 0)
+ return i;
+ else if (r > 0)
+ /* Can't match any of rest of elements in sorted list. Take this out
+ if it causes problems in certain environments. */
+ break;
+ }
+ return -1;
+}
+
/* The variable in NAME has just had its state changed. Check to see if it
is one of the special ones where something special happens. */
void
stupidly_hack_special_variables (name)
char *name;
{
+ static int sv_sorted = 0;
int i;
- for (i = 0; special_vars[i].name; i++)
+ if (sv_sorted == 0) /* shouldn't need, but it's fairly cheap. */
{
- if (STREQ (special_vars[i].name, name))
- {
- (*(special_vars[i].function)) (name);
- return;
- }
+ 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);
+}
+
+/* Special variables that need hooks to be run when they are unset as part
+ of shell reinitialization should have their sv_ functions run here. */
+void
+reinit_special_variables ()
+{
+#if defined (READLINE)
+ sv_comp_wordbreaks ("COMP_WORDBREAKS");
+#endif
+ sv_globignore ("GLOBIGNORE");
+ sv_opterr ("OPTERR");
+}
+
+void
+sv_ifs (name)
+ char *name;
+{
+ SHELL_VAR *v;
+
+ v = find_variable ("IFS");
+ setifs (v);
}
/* What to do just after the PATH variable has changed. */
char *name;
{
/* hash -r */
- flush_hashed_filenames ();
+ phash_flush ();
}
/* What to do just after one of the MAILxxxx variables has changed. NAME
}
}
+void
+sv_funcnest (name)
+ char *name;
+{
+ SHELL_VAR *v;
+ intmax_t num;
+
+ v = find_variable (name);
+ if (v == 0)
+ funcnest_max = 0;
+ else if (legal_number (value_cell (v), &num) == 0)
+ funcnest_max = 0;
+ else
+ funcnest_max = num;
+}
+
/* What to do when GLOBIGNORE changes. */
void
sv_globignore (name)
char *name;
{
- setup_glob_ignore (name);
+ if (privileged_mode == 0)
+ setup_glob_ignore (name);
}
#if defined (READLINE)
+void
+sv_comp_wordbreaks (name)
+ char *name;
+{
+ SHELL_VAR *sv;
+
+ sv = find_variable (name);
+ if (sv == 0)
+ reset_completer_word_break_chars ();
+}
+
/* 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. */
else
hostname_list_initialized = 0;
}
+
+#if defined (STRICT_POSIX)
+/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values
+ found in the initial environment) to override the terminal size reported by
+ the kernel. */
+void
+sv_winsize (name)
+ char *name;
+{
+ SHELL_VAR *v;
+ intmax_t xd;
+ int d;
+
+ if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing)
+ return;
+
+ v = find_variable (name);
+ if (v == 0 || var_isnull (v))
+ rl_reset_screen_size ();
+ else
+ {
+ if (legal_number (value_cell (v), &xd) == 0)
+ return;
+ winsize_assignment = 1;
+ d = xd; /* truncate */
+ if (name[0] == 'L') /* LINES */
+ rl_set_screen_size (d, -1);
+ else /* COLUMNS */
+ rl_set_screen_size (-1, d);
+ winsize_assignment = 0;
+ }
+}
+#endif /* STRICT_POSIX */
#endif /* READLINE */
+/* Update the value of HOME in the export environment so tilde expansion will
+ work on cygwin. */
+#if defined (__CYGWIN__)
+sv_home (name)
+ char *name;
+{
+ array_needs_making = 1;
+ maybe_make_export_env ();
+}
+#endif
+
#if defined (HISTORY)
/* What to do after the HISTSIZE or HISTFILESIZE variables change.
If there is a value for this HISTSIZE (and it is numeric), then stifle
char *name;
{
char *temp;
- long num;
+ intmax_t num;
+ int hmax;
temp = get_string_value (name);
if (temp && *temp)
{
if (legal_number (temp, &num))
- {
+ {
+ hmax = num;
if (name[4] == 'S')
{
- stifle_history (num);
- num = where_history ();
- if (history_lines_this_session > num)
- history_lines_this_session = num;
+ stifle_history (hmax);
+ hmax = where_history ();
+ if (history_lines_this_session > hmax)
+ history_lines_this_session = hmax;
}
else
{
- history_truncate_file (get_string_value ("HISTFILE"), (int)num);
- if (num <= history_lines_in_file)
- history_lines_in_file = num;
+ history_truncate_file (get_string_value ("HISTFILE"), hmax);
+ if (hmax <= history_lines_in_file)
+ history_lines_in_file = hmax;
}
}
}
char *name;
{
char *temp;
+ char *val;
+ int tptr;
history_control = 0;
temp = get_string_value (name);
- if (temp && *temp && STREQN (temp, "ignore", 6))
+ if (temp == 0 || *temp == 0)
+ return;
+
+ tptr = 0;
+ while (val = extract_colon_unit (temp, &tptr))
{
- if (temp[6] == 's') /* ignorespace */
- history_control = 1;
- else if (temp[6] == 'd') /* ignoredups */
- history_control = 2;
- else if (temp[6] == 'b') /* ignoreboth */
- history_control = 3;
+ if (STREQ (val, "ignorespace"))
+ history_control |= HC_IGNSPACE;
+ else if (STREQ (val, "ignoredups"))
+ history_control |= HC_IGNDUPS;
+ else if (STREQ (val, "ignoreboth"))
+ history_control |= HC_IGNBOTH;
+ else if (STREQ (val, "erasedups"))
+ history_control |= HC_ERASEDUPS;
+
+ free (val);
}
}
}
}
#endif /* BANG_HISTORY */
+
+void
+sv_histtimefmt (name)
+ char *name;
+{
+ SHELL_VAR *v;
+
+ v = find_variable (name);
+ history_write_timestamps = (v != 0);
+}
#endif /* HISTORY */
-#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+#if defined (HAVE_TZSET)
void
sv_tz (name)
char *name;
{
- tzset ();
+ if (chkexport (name))
+ tzset ();
}
#endif
#if defined (ARRAY_VARS)
void
-set_pipestatus_array (ps)
+set_pipestatus_array (ps, nproc)
int *ps;
+ int nproc;
{
SHELL_VAR *v;
ARRAY *a;
+ ARRAY_ELEMENT *ae;
register int i;
- char *t, tbuf[16];
+ char *t, tbuf[INT_STRLEN_BOUND(int) + 1];
v = find_variable ("PIPESTATUS");
if (v == 0)
if (array_p (v) == 0)
return; /* Do nothing if not an array variable. */
a = array_cell (v);
- if (a)
- empty_array (a);
- for (i = 0; ps[i] != -1; i++)
+
+ if (a == 0 || array_num_elements (a) == 0)
+ {
+ for (i = 0; i < nproc; i++) /* was ps[i] != -1, not i < nproc */
+ {
+ t = inttostr (ps[i], tbuf, sizeof (tbuf));
+ array_insert (a, i, t);
+ }
+ return;
+ }
+
+ /* Fast case */
+ if (array_num_elements (a) == nproc && nproc == 1)
{
- t = inttostr (ps[i], tbuf, sizeof (tbuf));
- array_add_element (a, i, t);
+ 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);
+ }
+ }
+}
+
+ARRAY *
+save_pipestatus_array ()
+{
+ SHELL_VAR *v;
+ ARRAY *a, *a2;
+
+ v = find_variable ("PIPESTATUS");
+ if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
+ return ((ARRAY *)NULL);
+
+ a = array_cell (v);
+ a2 = array_copy (array_cell (v));
+
+ return a2;
+}
+
+void
+restore_pipestatus_array (a)
+ ARRAY *a;
+{
+ SHELL_VAR *v;
+ ARRAY *a2;
+
+ v = find_variable ("PIPESTATUS");
+ /* XXX - should we still assign even if existing value is NULL? */
+ if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
+ return;
+
+ a2 = array_cell (v);
+ var_setarray (v, a);
+
+ array_dispose (a2);
}
#endif
static int v[2] = { 0, -1 };
v[0] = s;
- set_pipestatus_array (v);
+ set_pipestatus_array (v, 1);
#endif
}
+
+void
+sv_xtracefd (name)
+ char *name;
+{
+ SHELL_VAR *v;
+ char *t, *e;
+ int fd;
+ FILE *fp;
+
+ v = find_variable (name);
+ if (v == 0)
+ {
+ xtrace_reset ();
+ return;
+ }
+
+ t = value_cell (v);
+ if (t == 0 || *t == 0)
+ xtrace_reset ();
+ else
+ {
+ fd = (int)strtol (t, &e, 10);
+ if (e != t && *e == '\0' && sh_validfd (fd))
+ {
+ fp = fdopen (fd, "w");
+ if (fp == 0)
+ internal_error (_("%s: %s: cannot open as FILE"), name, value_cell (v));
+ else
+ xtrace_set (fd, fp);
+ }
+ else
+ internal_error (_("%s: %s: invalid value for trace file descriptor"), name, value_cell (v));
+ }
+}