diff --git a/ptxdist/configs/ecu01/ptxconfig b/ptxdist/configs/ecu01/ptxconfig index a725eb0..0fd28b4 100644 --- a/ptxdist/configs/ecu01/ptxconfig +++ b/ptxdist/configs/ecu01/ptxconfig @@ -261,7 +261,7 @@ PTXCONF_ROOTFS_HOSTS=y PTXCONF_ROOTFS_ISSUE=y # PTXCONF_ROOTFS_MODPROBE_CONF is not set PTXCONF_ROOTFS_NSSWITCH_CONF=y -PTXCONF_ROOTFS_PROFILE=y +# PTXCONF_ROOTFS_PROFILE is not set PTXCONF_ROOTFS_PROTOCOLS=y PTXCONF_ROOTFS_RESOLV_FILE=y # PTXCONF_ROOTFS_RESOLV_LINK is not set @@ -360,21 +360,35 @@ PTXCONF_BASH_ARITHMETIC_FOR=y PTXCONF_BASH_ARRAY=y PTXCONF_BASH_HISTORY=y PTXCONF_BASH_BRACE=y +PTXCONF_BASH_CASEMODATTR=y +PTXCONF_BASH_CASEMODEXP=y +PTXCONF_BASH_CMDTIMING=y PTXCONF_BASH_CONDITIONAL=y +PTXCONF_BASH_CONDITIONAL_REGEX=y +PTXCONF_BASH_COPROCESSES=y +# PTXCONF_BASH_DEBUGGER is not set +# PTXCONF_BASH_DIREXPDEFLT is not set PTXCONF_BASH_DIRSTACK=y +PTXCONF_BASH_DISABLED_BUILDINS=y +PTXCONF_BASH_DPARAN_ARITH=y PTXCONF_BASH_EXTPATTERN=y +PTXCONF_BASH_EXTPATTERN_DEFLT=y +# PTXCONF_BASH_GLOB_ASCIIRANGE_DEFLT is not set PTXCONF_BASH_HELP=y PTXCONF_BASH_CMDHISTORY=y PTXCONF_BASH_JOBS=y +PTXCONF_BASH_MULTIBYTE=y PTXCONF_BASH_PROCSUBST=y -PTXCONF_BASH_COMPLETION=y +# PTXCONF_BASH_BASHCOMPLETION is not set PTXCONF_BASH_ESC=y PTXCONF_BASH_EDIT=y # PTXCONF_BASH_RESTRICTED is not set # PTXCONF_BASH_SELECT is not set +# PTXCONF_BASH_SINGLE_HELPLINE is not set # PTXCONF_BASH_GPROF is not set PTXCONF_BASH_STATIC=y # PTXCONF_BASH_CURSES is not set +# PTXCONF_BASH_SH is not set # PTXCONF_BC is not set PTXCONF_BUSYBOX=y PTXCONF_BUSYBOX_TELNETD_STARTSCRIPT=y @@ -2028,7 +2042,8 @@ PTXCONF_NCURSES_TERMCAP=y # PTXCONF_SLANG is not set # PTXCONF_SPARSEHASH is not set # PTXCONF_SQLITE is not set -# PTXCONF_TERMCAP is not set +PTXCONF_TERMCAP=y +PTXCONF_TERMCAP_TERMCAP=y # PTXCONF_XERCES is not set PTXCONF_ZLIB=y # PTXCONF_ZLIB_STATIC is not set diff --git a/ptxdist/local_src/ecu01-basicsys/etc/bash.bashrc b/ptxdist/local_src/ecu01-basicsys/etc/bash.bashrc new file mode 100644 index 0000000..1aa9e30 --- /dev/null +++ b/ptxdist/local_src/ecu01-basicsys/etc/bash.bashrc @@ -0,0 +1,41 @@ +# +# /etc/bash.bashrc - config file for *interactive* bash shells +# +# (this is sourced after profile scripts (/etc/profile and +# ~/.profile, but before config script ~/.bashrc)) +# +# Note that /etc/profile and ~/.profile are ONLY sourced for +# *LOGIN* shells, while /etc/bash.bashrc and ~/.bashrc are sourced +# for all interactive bash shells +# + +printf $"# Running /etc/bash.bashrc ...\n" + +# /etc/profile.environment - config for sub-shells +#PS1="\\u@\\h:\\w " +PS1='\u \t : \w # ' +PS2=" >" +PS4="+ " + +alias vim='vi' +alias l='ls -l' +alias ll='ls -al' +alias ..='cd ..' +alias ...='cd ../..' +alias md='mkdir' +alias rd='rmdir' + +# Quantron: persistent history file for bash shells +# +# (note that HIST* variables, including HISTFILE, are settings +# for interactive shells and therefore do not belong into +# /etc/profile or ~/.profile. +# It should also NOT be exportet to the environment since HISTFILE +# affects ksh/ksh93 and other POSIX(-like) shells, which use +# different history file formats) +# +# (this means ptxdist usage of HISTFILE in ptx-supplied +# /etc/profile is plain wrong) +HISTFILE='/var/dyn/bash_history' + +# EOF. diff --git a/ptxdist/local_src/ecu01-basicsys/etc/profile b/ptxdist/local_src/ecu01-basicsys/etc/profile new file mode 100644 index 0000000..02fa716 --- /dev/null +++ b/ptxdist/local_src/ecu01-basicsys/etc/profile @@ -0,0 +1,36 @@ +# +# /etc/profile - config for profile shells +# + +printf "## Running /etc/profile ...\n" + +# do not set interactive shell properties like HISTFILE, PS1, PS2, +# ..., or aliases here. +# Such stuff belongs into the startup files for *interactive* shell +# sessions/etc/bash.bashrc or /etc/ksh.kshrc + +# This fixes the backspace when telnetting in. +if [ "$TERM" != "linux" ]; then + stty erase ^H +fi + + +# Exec local profile +if [ -e "/etc/profile.local" ]; then + printf "running /etc/profile.local\n" + . /etc/profile.local +fi + +# bug: workaround for issue with bash4 where an interactive login +# shell does not source /etc/bash.bashrc & ~/.bashrc +# Ordering is wrong with this workaround, because /etc/profile and +# ~/.profile should be sourced before /etc/bash.bashrc and +# ~/.bashrc instead of somewhere in the middle as in this ugly +# workaround +if [[ "$-" == *i* ]] && [[ "$0" == *bash* ]] ; then + [[ -r /etc/bash.bashrc ]] && source /etc/bash.bashrc + [[ -r $HOME/.bashrc ]] && source $HOME/.bashrc +fi + + +# EOF. diff --git a/ptxdist/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch b/ptxdist/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch new file mode 100644 index 0000000..d9a187d --- /dev/null +++ b/ptxdist/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch @@ -0,0 +1,5467 @@ +From: Chet Ramey +Date: Thu, 15 Jan 2015 10:20:04 -0500 +Subject: [PATCH] Bash-4.3 patch 31 + +--- + patchlevel.h | 2 +- + subst.h | 1 + + variables.c | 32 +- + variables.c.orig | 5365 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 5397 insertions(+), 3 deletions(-) + create mode 100644 variables.c.orig + +diff --git a/patchlevel.h b/patchlevel.h +index e5dde5245275..0ad46aafbdd9 100644 +--- a/patchlevel.h ++++ b/patchlevel.h +@@ -25,6 +25,6 @@ + regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh + looks for to find the patch level (for the sccs version string). */ + +-#define PATCHLEVEL 30 ++#define PATCHLEVEL 31 + + #endif /* _PATCHLEVEL_H_ */ +diff --git a/subst.h b/subst.h +index cedaf8b6b444..1c300ab96b04 100644 +--- a/subst.h ++++ b/subst.h +@@ -47,6 +47,7 @@ + #define ASS_MKASSOC 0x0004 + #define ASS_MKGLOBAL 0x0008 /* force global assignment */ + #define ASS_NAMEREF 0x0010 /* assigning to nameref variable */ ++#define ASS_FROMREF 0x0020 /* assigning from value of nameref variable */ + + /* Flags for the string extraction functions. */ + #define SX_NOALLOC 0x0001 /* just skip; don't return substring */ +diff --git a/variables.c b/variables.c +index 7c82710e0f0b..81b7877e32e8 100644 +--- a/variables.c ++++ b/variables.c +@@ -2516,10 +2516,27 @@ bind_variable_internal (name, value, table, hflags, aflags) + HASH_TABLE *table; + int hflags, aflags; + { +- char *newval; ++ char *newname, *newval; + SHELL_VAR *entry; ++#if defined (ARRAY_VARS) ++ arrayind_t ind; ++ char *subp; ++ int sublen; ++#endif + ++ newname = 0; ++#if defined (ARRAY_VARS) ++ if ((aflags & ASS_FROMREF) && (hflags & HASH_NOSRCH) == 0 && valid_array_reference (name)) ++ { ++ newname = array_variable_name (name, &subp, &sublen); ++ if (newname == 0) ++ return (SHELL_VAR *)NULL; /* XXX */ ++ entry = hash_lookup (newname, table); ++ } ++ else ++#endif + entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table); ++ + /* Follow the nameref chain here if this is the global variables table */ + if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table) + { +@@ -2550,6 +2567,16 @@ bind_variable_internal (name, value, table, hflags, aflags) + var_setvalue (entry, make_variable_value (entry, value, 0)); + } + } ++#if defined (ARRAY_VARS) ++ else if (entry == 0 && newname) ++ { ++ entry = make_new_array_variable (newname); /* indexed array by default */ ++ if (entry == 0) ++ return entry; ++ ind = array_expand_index (name, subp, sublen); ++ bind_array_element (entry, ind, value, aflags); ++ } ++#endif + else if (entry == 0) + { + entry = make_new_variable (name, table); +@@ -2670,7 +2697,8 @@ bind_variable (name, value, flags) + normal. */ + if (nameref_cell (nv) == 0) + return (bind_variable_internal (nv->name, value, nvc->table, 0, flags)); +- return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags)); ++ /* XXX - bug here with ref=array[index] */ ++ return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags|ASS_FROMREF)); + } + else + v = nv; +diff --git a/variables.c.orig b/variables.c.orig +new file mode 100644 +index 000000000000..7c82710e0f0b +--- /dev/null ++++ b/variables.c.orig +@@ -0,0 +1,5365 @@ ++/* variables.c -- Functions for hacking shell variables. */ ++ ++/* Copyright (C) 1987-2013 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 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. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bash. If not, see . ++*/ ++ ++#include "config.h" ++ ++#include "bashtypes.h" ++#include "posixstat.h" ++#include "posixtime.h" ++ ++#if defined (__QNX__) ++# if defined (__QNXNTO__) ++# include ++# else ++# include ++# endif /* !__QNXNTO__ */ ++#endif /* __QNX__ */ ++ ++#if defined (HAVE_UNISTD_H) ++# include ++#endif ++ ++#include ++#include "chartypes.h" ++#if defined (HAVE_PWD_H) ++# include ++#endif ++#include "bashansi.h" ++#include "bashintl.h" ++ ++#define NEED_XTRACE_SET_DECL ++ ++#include "shell.h" ++#include "flags.h" ++#include "execute_cmd.h" ++#include "findcmd.h" ++#include "mailcheck.h" ++#include "input.h" ++#include "hashcmd.h" ++#include "pathexp.h" ++#include "alias.h" ++#include "jobs.h" ++ ++#include "version.h" ++ ++#include "builtins/getopt.h" ++#include "builtins/common.h" ++#include "builtins/builtext.h" ++ ++#if defined (READLINE) ++# include "bashline.h" ++# include ++#else ++# include ++#endif ++ ++#if defined (HISTORY) ++# include "bashhist.h" ++# include ++#endif /* HISTORY */ ++ ++#if defined (PROGRAMMABLE_COMPLETION) ++# include "pcomplete.h" ++#endif ++ ++#define TEMPENV_HASH_BUCKETS 4 /* must be power of two */ ++ ++#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0') ++ ++#define BASHFUNC_PREFIX "BASH_FUNC_" ++#define BASHFUNC_PREFLEN 10 /* == strlen(BASHFUNC_PREFIX */ ++#define BASHFUNC_SUFFIX "%%" ++#define BASHFUNC_SUFFLEN 2 /* == strlen(BASHFUNC_SUFFIX) */ ++ ++extern char **environ; ++ ++/* Variables used here and defined in other files. */ ++extern int posixly_correct; ++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 int last_command_exit_value; ++extern char *dist_version, *release_status; ++extern char *shell_name; ++extern char *primary_prompt, *secondary_prompt; ++extern char *current_host_name; ++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; ++ ++#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 set of shell assignments which are made only in the environment ++ for a single command. */ ++HASH_TABLE *temporary_env = (HASH_TABLE *)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. */ ++char *dollar_vars[10]; ++WORD_LIST *rest_of_args = (WORD_LIST *)NULL; ++ ++/* The value of $$. */ ++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 ++ shell variables that are marked for export. */ ++char **export_env = (char **)NULL; ++static int export_env_index; ++static int export_env_size; ++ ++#if defined (READLINE) ++static int winsize_assignment; /* currently assigning to LINES or COLUMNS */ ++#endif ++ ++static HASH_TABLE *last_table_searched; /* hash_lookup sets this */ ++ ++/* 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 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 *)); ++ ++#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 *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *)); ++static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **)); ++static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **)); ++ ++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 *, int)); ++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 ++ parse $SHELLOPTS. */ ++void ++initialize_shell_variables (env, privmode) ++ char **env; ++ int privmode; ++{ ++ char *name, *string, *temp_string; ++ int c, char_index, string_index, string_length, ro; ++ SHELL_VAR *temp_var; ++ ++ create_variable_tables (); ++ ++ for (string_index = 0; string = env[string_index++]; ) ++ { ++ char_index = 0; ++ name = string; ++ while ((c = *string++) && c != '=') ++ ; ++ if (string[-1] == '=') ++ char_index = string - name - 1; ++ ++ /* If there are weird things in the environment, like `=xxx' or a ++ string without an `=', just skip them. */ ++ if (char_index == 0) ++ continue; ++ ++ /* ASSERT(name[char_index] == '=') */ ++ name[char_index] = '\0'; ++ /* Now, name = env variable name, string = env variable value, and ++ char_index == strlen (name) */ ++ ++ temp_var = (SHELL_VAR *)NULL; ++ ++ /* 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 (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) && ++ STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) && ++ STREQN ("() {", string, 4)) ++ { ++ size_t namelen; ++ char *tname; /* desired imported function name */ ++ ++ namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN; ++ ++ tname = name + BASHFUNC_PREFLEN; /* start of func name */ ++ tname[namelen] = '\0'; /* now tname == func name */ ++ ++ string_length = strlen (string); ++ temp_string = (char *)xmalloc (namelen + string_length + 2); ++ ++ memcpy (temp_string, tname, namelen); ++ temp_string[namelen] = ' '; ++ memcpy (temp_string + namelen + 1, string, string_length + 1); ++ ++ /* Don't import function names that are invalid identifiers from the ++ environment, though we still allow them to be defined as shell ++ variables. */ ++ if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname))) ++ parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD); ++ ++ if (temp_var = find_function (tname)) ++ { ++ VSETATTR (temp_var, (att_exported|att_imported)); ++ array_needs_making = 1; ++ } ++ else ++ { ++ if (temp_var = bind_variable (name, string, 0)) ++ { ++ VSETATTR (temp_var, (att_exported | att_imported | att_invisible)); ++ array_needs_making = 1; ++ } ++ last_command_exit_value = 1; ++ report_error (_("error importing function definition for `%s'"), tname); ++ } ++ ++ /* Restore original suffix */ ++ tname[namelen] = BASHFUNC_SUFFIX[0]; ++ } ++#if defined (ARRAY_VARS) ++# if ARRAY_EXPORT ++ /* Array variables may not yet be exported. */ ++ else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')') ++ { ++ string_length = 1; ++ temp_string = extract_array_assignment_list (string, &string_length); ++ temp_var = assign_array_from_string (name, temp_string); ++ FREE (temp_string); ++ VSETATTR (temp_var, (att_exported | att_imported)); ++ array_needs_making = 1; ++ } ++# endif /* ARRAY_EXPORT */ ++#endif ++#if 0 ++ else if (legal_identifier (name)) ++#else ++ else ++#endif ++ { ++ ro = 0; ++ if (posixly_correct && STREQ (name, "SHELLOPTS")) ++ { ++ temp_var = find_variable ("SHELLOPTS"); ++ ro = temp_var && readonly_p (temp_var); ++ if (temp_var) ++ VUNSETATTR (temp_var, att_readonly); ++ } ++ 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)); ++ if (ro) ++ VSETATTR (temp_var, att_readonly); ++ 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). */ ++ if (temp_var && function_p (temp_var) == 0) /* XXX not yet */ ++ { ++ CACHE_IMPORTSTR (temp_var, name); ++ } ++ } ++ ++ set_pwd (); ++ ++ /* Set up initial value of $_ */ ++ temp_var = set_if_not ("_", dollar_vars[0]); ++ ++ /* Remember this pid. */ ++ 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); ++#if 0 ++ set_auto_export (temp_var); /* XXX */ ++#endif ++ ++ temp_var = set_if_not ("TERM", "dumb"); ++#if 0 ++ set_auto_export (temp_var); /* XXX */ ++#endif ++ ++#if defined (__QNX__) ++ /* set node id -- don't import it from the environment */ ++ { ++ char node_name[22]; ++# if defined (__QNXNTO__) ++ netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name)); ++# else ++ qnx_nidtostr (getnid (), node_name, sizeof (node_name)); ++# endif ++ temp_var = bind_variable ("NODE", node_name, 0); ++ set_auto_export (temp_var); ++ } ++#endif ++ ++ /* set up the prompts. */ ++ if (interactive_shell) ++ { ++#if defined (PROMPT_STRING_DECODE) ++ set_if_not ("PS1", primary_prompt); ++#else ++ if (current_user.uid == -1) ++ get_current_user_info (); ++ set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt); ++#endif ++ set_if_not ("PS2", secondary_prompt); ++ } ++ set_if_not ("PS4", "+ "); ++ ++ /* Don't allow IFS to be imported from the environment. */ ++ temp_var = bind_variable ("IFS", " \t\n", 0); ++ setifs (temp_var); ++ ++ /* Magic machine types. Pretty convenient. */ ++ 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 MAILPATH is not set, and we should provide a ++ default only if neither is set. */ ++ if (interactive_shell) ++ { ++ 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. */ ++ temp_var = bind_variable ("OPTIND", "1", 0); ++ VSETATTR (temp_var, att_integer); ++ getopts_reset (0); ++ bind_variable ("OPTERR", "1", 0); ++ sh_opterr = 1; ++ ++ if (login_shell == 1 && 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, 0); ++ free (name); ++ ++ /* Make the exported environment variable SHELL be the user's login ++ shell. Note that the `tset' command looks at this variable ++ to determine what style of commands to output; if it ends in "csh", ++ then C-shell commands are output, else Bourne shell commands. */ ++ set_shell_var (); ++ ++ /* Make a variable called BASH_VERSION which contains the version info. */ ++ 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"); ++ if (!temp_var) ++ temp_var = find_variable ("POSIX_PEDANTIC"); ++ if (temp_var && imported_p (temp_var)) ++ sv_strict_posix (temp_var->name); ++ ++#if defined (HISTORY) ++ /* Set history variables to defaults, and then do whatever we would ++ do if the variable had just been set. Do this only in the case ++ that we are remembering commands on the history list. */ ++ if (remember_on_history) ++ { ++ name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0); ++ ++ set_if_not ("HISTFILE", name); ++ free (name); ++ } ++#endif /* HISTORY */ ++ ++ /* Seed the random number generator. */ ++ seedrand (); ++ ++ /* Handle some "special" variables that we may have inherited from a ++ parent shell. */ ++ if (interactive_shell) ++ { ++ temp_var = find_variable ("IGNOREEOF"); ++ if (!temp_var) ++ temp_var = find_variable ("ignoreeof"); ++ if (temp_var && imported_p (temp_var)) ++ sv_ignoreeof (temp_var->name); ++ } ++ ++#if defined (HISTORY) ++ if (interactive_shell && remember_on_history) ++ { ++ sv_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 * ++sh_get_home_dir () ++{ ++ if (current_user.home_dir == 0) ++ get_current_user_info (); ++ return current_user.home_dir; ++} ++ ++static void ++set_home_var () ++{ ++ SHELL_VAR *temp_var; ++ ++ temp_var = find_variable ("HOME"); ++ if (temp_var == 0) ++ temp_var = bind_variable ("HOME", sh_get_home_dir (), 0); ++#if 0 ++ VSETATTR (temp_var, att_exported); ++#endif ++} ++ ++/* Set $SHELL to the user's login shell if it is not already set. Call ++ get_current_user_info if we haven't already fetched the shell. */ ++static void ++set_shell_var () ++{ ++ SHELL_VAR *temp_var; ++ ++ temp_var = find_variable ("SHELL"); ++ if (temp_var == 0) ++ { ++ if (current_user.shell == 0) ++ get_current_user_info (); ++ temp_var = bind_variable ("SHELL", current_user.shell, 0); ++ } ++#if 0 ++ VSETATTR (temp_var, att_exported); ++#endif ++} ++ ++static char * ++get_bash_name () ++{ ++ char *name; ++ ++ if ((login_shell == 1) && RELPATH(shell_name)) ++ { ++ if (current_user.shell == 0) ++ get_current_user_info (); ++ name = savestring (current_user.shell); ++ } ++ else if (ABSPATH(shell_name)) ++ name = savestring (shell_name); ++ else if (shell_name[0] == '.' && shell_name[1] == '/') ++ { ++ /* Fast path for common case. */ ++ char *cdir; ++ int len; ++ ++ cdir = get_string_value ("PWD"); ++ if (cdir) ++ { ++ len = strlen (cdir); ++ name = (char *)xmalloc (len + strlen (shell_name) + 1); ++ strcpy (name, cdir); ++ strcpy (name + len, shell_name + 1); ++ } ++ else ++ name = savestring (shell_name); ++ } ++ else ++ { ++ char *tname; ++ int s; ++ ++ tname = find_user_command (shell_name); ++ ++ if (tname == 0) ++ { ++ /* Try the current directory. If there is not an executable ++ there, just punt and use the login shell. */ ++ s = file_status (shell_name); ++ if (s & FS_EXECABLE) ++ { ++ tname = make_absolute (shell_name, get_string_value ("PWD")); ++ if (*shell_name == '.') ++ { ++ name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS); ++ if (name == 0) ++ name = tname; ++ else ++ free (tname); ++ } ++ else ++ name = tname; ++ } ++ else ++ { ++ if (current_user.shell == 0) ++ get_current_user_info (); ++ name = savestring (current_user.shell); ++ } ++ } ++ else ++ { ++ name = full_pathname (tname); ++ free (tname); ++ } ++ } ++ ++ return (name); ++} ++ ++void ++adjust_shell_level (change) ++ int change; ++{ ++ char new_level[5], *old_SHLVL; ++ intmax_t old_level; ++ SHELL_VAR *temp_var; ++ ++ old_SHLVL = get_string_value ("SHLVL"); ++ 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); ++ shell_level = 1; ++ } ++ ++ /* We don't need the full generality of itos here. */ ++ if (shell_level < 10) ++ { ++ new_level[0] = shell_level + '0'; ++ new_level[1] = '\0'; ++ } ++ else if (shell_level < 100) ++ { ++ new_level[0] = (shell_level / 10) + '0'; ++ new_level[1] = (shell_level % 10) + '0'; ++ new_level[2] = '\0'; ++ } ++ else if (shell_level < 1000) ++ { ++ new_level[0] = (shell_level / 100) + '0'; ++ old_level = shell_level % 100; ++ new_level[1] = (old_level / 10) + '0'; ++ new_level[2] = (old_level % 10) + '0'; ++ new_level[3] = '\0'; ++ } ++ ++ temp_var = bind_variable ("SHLVL", new_level, 0); ++ set_auto_export (temp_var); ++} ++ ++static void ++initialize_shell_level () ++{ ++ 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[INT_STRLEN_BOUND(pid_t) + 1], *name; ++ SHELL_VAR *temp_var; ++ ++ name = inttostr (getppid (), namebuf, sizeof(namebuf)); ++ temp_var = find_variable ("PPID"); ++ if (temp_var) ++ VUNSETATTR (temp_var, (att_readonly | att_exported)); ++ temp_var = bind_variable ("PPID", name, 0); ++ VSETATTR (temp_var, (att_readonly | att_integer)); ++} ++ ++static void ++uidset () ++{ ++ char buff[INT_STRLEN_BOUND(uid_t) + 1], *b; ++ register SHELL_VAR *v; ++ ++ b = inttostr (current_user.uid, buff, sizeof (buff)); ++ v = find_variable ("UID"); ++ if (v == 0) ++ { ++ v = bind_variable ("UID", b, 0); ++ VSETATTR (v, (att_readonly | att_integer)); ++ } ++ ++ if (current_user.euid != current_user.uid) ++ b = inttostr (current_user.euid, buff, sizeof (buff)); ++ ++ v = find_variable ("EUID"); ++ if (v == 0) ++ { ++ v = bind_variable ("EUID", b, 0); ++ VSETATTR (v, (att_readonly | att_integer)); ++ } ++} ++ ++#if defined (ARRAY_VARS) ++static void ++make_vers_array () ++{ ++ SHELL_VAR *vv; ++ ARRAY *av; ++ char *s, d[32], b[INT_STRLEN_BOUND(int) + 1]; ++ ++ unbind_variable ("BASH_VERSINFO"); ++ ++ vv = make_new_array_variable ("BASH_VERSINFO"); ++ av = array_cell (vv); ++ strcpy (d, dist_version); ++ s = strchr (d, '.'); ++ if (s) ++ *s++ = '\0'; ++ array_insert (av, 0, d); ++ array_insert (av, 1, s); ++ s = inttostr (patch_level, b, sizeof (b)); ++ array_insert (av, 2, s); ++ s = inttostr (build_version, b, sizeof (b)); ++ array_insert (av, 3, s); ++ array_insert (av, 4, release_status); ++ array_insert (av, 5, MACHTYPE); ++ ++ VSETATTR (vv, att_readonly); ++} ++#endif /* ARRAY_VARS */ ++ ++/* Set the environment variables $LINES and $COLUMNS in response to ++ a window size change. */ ++void ++sh_set_lines_and_columns (lines, cols) ++ int lines, cols; ++{ ++ 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, 0); ++ ++ v = inttostr (cols, val, sizeof (val)); ++ bind_variable ("COLUMNS", v, 0); ++} ++ ++/* **************************************************************** */ ++/* */ ++/* Printing variables and values */ ++/* */ ++/* **************************************************************** */ ++ ++/* Print LIST (a list of shell variables) to stdout in such a way that ++ they can be read back in. */ ++void ++print_var_list (list) ++ register SHELL_VAR **list; ++{ ++ register int i; ++ register SHELL_VAR *var; ++ ++ for (i = 0; list && (var = list[i]); i++) ++ if (invisible_p (var) == 0) ++ print_assignment (var); ++} ++ ++/* Print LIST (a list of shell functions) to stdout in such a way that ++ they can be read back in. */ ++void ++print_func_list (list) ++ register SHELL_VAR **list; ++{ ++ register int i; ++ register SHELL_VAR *var; ++ ++ for (i = 0; list && (var = list[i]); i++) ++ { ++ printf ("%s ", var->name); ++ print_var_function (var); ++ printf ("\n"); ++ } ++} ++ ++/* 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 (var_isset (var) == 0) ++ return; ++ ++ if (function_p (var)) ++ { ++ printf ("%s", var->name); ++ print_var_function (var); ++ printf ("\n"); ++ } ++#if defined (ARRAY_VARS) ++ else if (array_p (var)) ++ print_array_assignment (var, 0); ++ else if (assoc_p (var)) ++ print_assoc_assignment (var, 0); ++#endif /* ARRAY_VARS */ ++ else ++ { ++ printf ("%s=", var->name); ++ print_var_value (var, 1); ++ printf ("\n"); ++ } ++} ++ ++/* Print the value cell of VAR, a shell variable. Do not print ++ the name, nor leading/trailing newline. If QUOTE is non-zero, ++ and the value contains shell metacharacters, quote the value ++ in such a way that it can be read back in. */ ++void ++print_var_value (var, quote) ++ SHELL_VAR *var; ++ int quote; ++{ ++ char *t; ++ ++ if (var_isset (var) == 0) ++ return; ++ ++ if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var))) ++ { ++ t = ansic_quote (value_cell (var), 0, (int *)0); ++ printf ("%s", t); ++ free (t); ++ } ++ else if (quote && sh_contains_shell_metas (value_cell (var))) ++ { ++ t = sh_single_quote (value_cell (var)); ++ printf ("%s", t); ++ free (t); ++ } ++ else ++ printf ("%s", value_cell (var)); ++} ++ ++/* Print the function cell of VAR, a shell variable. Do not ++ print the name, nor leading/trailing newline. */ ++void ++print_var_function (var) ++ SHELL_VAR *var; ++{ ++ char *x; ++ ++ if (function_p (var) && var_isset (var)) ++ { ++ x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL); ++ printf ("%s", x); ++ } ++} ++ ++/* **************************************************************** */ ++/* */ ++/* Dynamic Variables */ ++/* */ ++/* **************************************************************** */ ++ ++/* DYNAMIC VARIABLES ++ ++ These are variables whose values are generated anew each time they are ++ referenced. These are implemented using a pair of function pointers ++ in the struct variable: assign_func, which is called from bind_variable ++ and, 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). IND is an index for an array variable, and ++ unused otherwise. ++ ++ dynamic_value is called from find_variable_internal to return a `new' ++ value for the specified dynamic varible. If this function is NULL, ++ the variable is treated as a `normal' shell variable. If it is not, ++ however, then this function is called like this: ++ tempvar = (*(var->dynamic_value)) (var); ++ ++ Sometimes `tempvar' will replace the value of `var'. Other times, the ++ shell will simply use the string value. Pretty object-oriented, huh? ++ ++ Be warned, though: if you `unset' a special variable, it loses its ++ special meaning, even if you subsequently set it. ++ ++ The special assignment code would probably have been better put in ++ subst.c: do_assignment_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, 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, value, ind, key) ++ SHELL_VAR *self; ++ 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. Initialize 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 intmax_t seconds_value_assigned; ++ ++static SHELL_VAR * ++assign_seconds (self, value, unused, key) ++ SHELL_VAR *self; ++ char *value; ++ arrayind_t unused; ++ char *key; ++{ ++ if (legal_number (value, &seconds_value_assigned) == 0) ++ seconds_value_assigned = 0; ++ shell_start_time = NOW; ++ return (self); ++} ++ ++static SHELL_VAR * ++get_seconds (var) ++ SHELL_VAR *var; ++{ ++ time_t time_since_start; ++ char *p; ++ ++ time_since_start = NOW - shell_start_time; ++ p = itos(seconds_value_assigned + time_since_start); ++ ++ FREE (value_cell (var)); ++ ++ VSETATTR (var, att_integer); ++ var_setvalue (var, p); ++ return (var); ++} ++ ++static SHELL_VAR * ++init_seconds_var () ++{ ++ SHELL_VAR *v; ++ ++ v = find_variable ("SECONDS"); ++ if (v) ++ { ++ if (legal_number (value_cell(v), &seconds_value_assigned) == 0) ++ seconds_value_assigned = 0; ++ } ++ INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds); ++ return v; ++} ++ ++/* The random number seed. You can change this by setting RANDOM. */ ++static unsigned long rseed = 1; ++static int last_random_value; ++static int seeded_subshell = 0; ++ ++/* 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 () ++{ ++ /* 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) ++ 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, unused, key) ++ SHELL_VAR *self; ++ char *value; ++ arrayind_t unused; ++ char *key; ++{ ++ sbrand (strtoul (value, (char **)NULL, 10)); ++ if (subshell_environment) ++ seeded_subshell = getpid (); ++ return (self); ++} ++ ++int ++get_random_number () ++{ ++ int rv, pid; ++ ++ /* Reset for command and process substitution. */ ++ pid = getpid (); ++ if (subshell_environment && seeded_subshell != pid) ++ { ++ seedrand (); ++ seeded_subshell = pid; ++ } ++ ++ do ++ rv = brand (); ++ 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 (rv); ++ ++ FREE (value_cell (var)); ++ ++ VSETATTR (var, att_integer); ++ 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) ++ SHELL_VAR *var; ++{ ++ char *p; ++ int ln; ++ ++ ln = executing_line_number (); ++ p = itos (ln); ++ FREE (value_cell (var)); ++ var_setvalue (var, p); ++ return (var); ++} ++ ++static SHELL_VAR * ++assign_subshell (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; ++ 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) ++ SHELL_VAR *var; ++{ ++ char *p; ++ ++ p = itos (history_number ()); ++ FREE (value_cell (var)); ++ var_setvalue (var, p); ++ return (var); ++} ++#endif ++ ++#if defined (READLINE) ++/* When this function returns, VAR->value points to malloced memory. */ ++static SHELL_VAR * ++get_comp_wordbreaks (var) ++ SHELL_VAR *var; ++{ ++ /* 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; ++{ ++ if (rl_completer_word_break_characters && ++ rl_completer_word_break_characters != rl_basic_word_break_characters) ++ free (rl_completer_word_break_characters); ++ ++ rl_completer_word_break_characters = savestring (value); ++ return self; ++} ++#endif /* READLINE */ ++ ++#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) ++static SHELL_VAR * ++assign_dirstack (self, value, ind, key) ++ SHELL_VAR *self; ++ 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) ++/* We don't want to initialize the group set with a call to getgroups() ++ unless we're asked to, but we only want to do it once. */ ++static SHELL_VAR * ++get_groupset (self) ++ SHELL_VAR *self; ++{ ++ register int i; ++ int ng; ++ ARRAY *a; ++ static char **group_set = (char **)NULL; ++ ++ if (group_set == 0) ++ { ++ group_set = get_group_list (&ng); ++ a = array_cell (self); ++ for (i = 0; i < ng; i++) ++ array_insert (a, i, group_set[i]); ++ } ++ return (self); ++} ++ ++static SHELL_VAR * ++build_hashcmd (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 (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0) ++ { ++ var_setvalue (self, (char *)NULL); ++ return self; ++ } ++ ++ 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) ++ return; ++ ++ if (on_or_off) ++ VUNSETATTR (v, att_invisible); ++ else ++ VSETATTR (v, att_invisible); ++} ++ ++static SHELL_VAR * ++init_funcname_var () ++{ ++ SHELL_VAR *v; ++ ++ v = find_variable ("FUNCNAME"); ++ if (v) ++ return v; ++#if defined (ARRAY_VARS) ++ INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign); ++#else ++ INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign); ++#endif ++ VSETATTR (v, att_invisible|att_noassign); ++ return v; ++} ++ ++static void ++initialize_dynamic_variables () ++{ ++ SHELL_VAR *v; ++ ++ v = 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, (sh_var_assign_func_t *)NULL); ++ VSETATTR (v, att_integer); ++#endif ++ ++#if defined (READLINE) ++ INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks); ++#endif ++ ++#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) ++ v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0); ++#endif /* PUSHD_AND_POPD && ARRAY_VARS */ ++ ++#if defined (ARRAY_VARS) ++ v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign); ++ ++# if defined (DEBUGGER) ++ v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset); ++ v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset); ++# endif /* DEBUGGER */ ++ v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset); ++ v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset); ++ ++ 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 ++ ++ 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). */ ++ ++static SHELL_VAR * ++hash_lookup (name, hashed_vars) ++ const char *name; ++ HASH_TABLE *hashed_vars; ++{ ++ BUCKET_CONTENTS *bucket; ++ ++ bucket = hash_search (name, hashed_vars, 0); ++ /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that ++ table. */ ++ if (bucket) ++ last_table_searched = hashed_vars; ++ 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. ++ The lookup order is: ++ temporary_env ++ shell_variables list ++*/ ++ ++SHELL_VAR * ++find_variable_internal (name, force_tempenv) ++ const char *name; ++ int force_tempenv; ++{ ++ SHELL_VAR *var; ++ int search_tempenv; ++ VAR_CONTEXT *vc; ++ ++ 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". */ ++ search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment); ++ ++ if (search_tempenv && temporary_env) ++ var = hash_lookup (name, temporary_env); ++ ++ vc = shell_variables; ++#if 0 ++if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */ ++ expanding_redir && ++ (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin)) ++ { ++ itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV"); ++ while (vc && (vc->flags & VC_BLTNENV)) ++ vc = vc->down; ++ if (vc == 0) ++ vc = shell_variables; ++ } ++#endif ++ ++ if (var == 0) ++ var = var_lookup (name, vc); ++ ++ if (var == 0) ++ return ((SHELL_VAR *)NULL); ++ ++ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); ++} ++ ++/* Look up and resolve the chain of nameref variables starting at V all the ++ way to NULL or non-nameref. */ ++SHELL_VAR * ++find_variable_nameref (v) ++ SHELL_VAR *v; ++{ ++ int level; ++ char *newname; ++ SHELL_VAR *orig, *oldv; ++ ++ level = 0; ++ orig = v; ++ while (v && nameref_p (v)) ++ { ++ level++; ++ if (level > NAMEREF_MAX) ++ return ((SHELL_VAR *)0); /* error message here? */ ++ newname = nameref_cell (v); ++ if (newname == 0 || *newname == '\0') ++ return ((SHELL_VAR *)0); ++ oldv = v; ++ v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); ++ if (v == orig || v == oldv) ++ { ++ internal_warning (_("%s: circular name reference"), orig->name); ++ return ((SHELL_VAR *)0); ++ } ++ } ++ return v; ++} ++ ++/* Resolve the chain of nameref variables for NAME. XXX - could change later */ ++SHELL_VAR * ++find_variable_last_nameref (name) ++ const char *name; ++{ ++ SHELL_VAR *v, *nv; ++ char *newname; ++ int level; ++ ++ nv = v = find_variable_noref (name); ++ level = 0; ++ while (v && nameref_p (v)) ++ { ++ level++; ++ if (level > NAMEREF_MAX) ++ return ((SHELL_VAR *)0); /* error message here? */ ++ newname = nameref_cell (v); ++ if (newname == 0 || *newname == '\0') ++ return ((SHELL_VAR *)0); ++ nv = v; ++ v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); ++ } ++ return nv; ++} ++ ++/* Resolve the chain of nameref variables for NAME. XXX - could change later */ ++SHELL_VAR * ++find_global_variable_last_nameref (name) ++ const char *name; ++{ ++ SHELL_VAR *v, *nv; ++ char *newname; ++ int level; ++ ++ nv = v = find_global_variable_noref (name); ++ level = 0; ++ while (v && nameref_p (v)) ++ { ++ level++; ++ if (level > NAMEREF_MAX) ++ return ((SHELL_VAR *)0); /* error message here? */ ++ newname = nameref_cell (v); ++ if (newname == 0 || *newname == '\0') ++ return ((SHELL_VAR *)0); ++ nv = v; ++ v = find_global_variable_noref (newname); ++ } ++ return nv; ++} ++ ++static SHELL_VAR * ++find_nameref_at_context (v, vc) ++ SHELL_VAR *v; ++ VAR_CONTEXT *vc; ++{ ++ SHELL_VAR *nv, *nv2; ++ VAR_CONTEXT *nvc; ++ char *newname; ++ int level; ++ ++ nv = v; ++ level = 1; ++ while (nv && nameref_p (nv)) ++ { ++ level++; ++ if (level > NAMEREF_MAX) ++ return ((SHELL_VAR *)NULL); ++ newname = nameref_cell (nv); ++ if (newname == 0 || *newname == '\0') ++ return ((SHELL_VAR *)NULL); ++ nv2 = hash_lookup (newname, vc->table); ++ if (nv2 == 0) ++ break; ++ nv = nv2; ++ } ++ return nv; ++} ++ ++/* Do nameref resolution from the VC, which is the local context for some ++ function or builtin, `up' the chain to the global variables context. If ++ NVCP is not NULL, return the variable context where we finally ended the ++ nameref resolution (so the bind_variable_internal can use the correct ++ variable context and hash table). */ ++static SHELL_VAR * ++find_variable_nameref_context (v, vc, nvcp) ++ SHELL_VAR *v; ++ VAR_CONTEXT *vc; ++ VAR_CONTEXT **nvcp; ++{ ++ SHELL_VAR *nv, *nv2; ++ VAR_CONTEXT *nvc; ++ ++ /* Look starting at the current context all the way `up' */ ++ for (nv = v, nvc = vc; nvc; nvc = nvc->down) ++ { ++ nv2 = find_nameref_at_context (nv, nvc); ++ if (nv2 == 0) ++ continue; ++ nv = nv2; ++ if (*nvcp) ++ *nvcp = nvc; ++ if (nameref_p (nv) == 0) ++ break; ++ } ++ return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv); ++} ++ ++/* Do nameref resolution from the VC, which is the local context for some ++ function or builtin, `up' the chain to the global variables context. If ++ NVCP is not NULL, return the variable context where we finally ended the ++ nameref resolution (so the bind_variable_internal can use the correct ++ variable context and hash table). */ ++static SHELL_VAR * ++find_variable_last_nameref_context (v, vc, nvcp) ++ SHELL_VAR *v; ++ VAR_CONTEXT *vc; ++ VAR_CONTEXT **nvcp; ++{ ++ SHELL_VAR *nv, *nv2; ++ VAR_CONTEXT *nvc; ++ ++ /* Look starting at the current context all the way `up' */ ++ for (nv = v, nvc = vc; nvc; nvc = nvc->down) ++ { ++ nv2 = find_nameref_at_context (nv, nvc); ++ if (nv2 == 0) ++ continue; ++ nv = nv2; ++ if (*nvcp) ++ *nvcp = nvc; ++ } ++ return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL); ++} ++ ++/* Find a variable, forcing a search of the temporary environment first */ ++SHELL_VAR * ++find_variable_tempenv (name) ++ const char *name; ++{ ++ SHELL_VAR *var; ++ ++ var = find_variable_internal (name, 1); ++ if (var && nameref_p (var)) ++ var = find_variable_nameref (var); ++ return (var); ++} ++ ++/* Find a variable, not forcing a search of the temporary environment first */ ++SHELL_VAR * ++find_variable_notempenv (name) ++ const char *name; ++{ ++ SHELL_VAR *var; ++ ++ var = find_variable_internal (name, 0); ++ if (var && nameref_p (var)) ++ var = find_variable_nameref (var); ++ return (var); ++} ++ ++SHELL_VAR * ++find_global_variable (name) ++ const char *name; ++{ ++ SHELL_VAR *var; ++ ++ var = var_lookup (name, global_variables); ++ if (var && nameref_p (var)) ++ var = find_variable_nameref (var); ++ ++ if (var == 0) ++ return ((SHELL_VAR *)NULL); ++ ++ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); ++} ++ ++SHELL_VAR * ++find_global_variable_noref (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); ++} ++ ++SHELL_VAR * ++find_shell_variable (name) ++ const char *name; ++{ ++ SHELL_VAR *var; ++ ++ var = var_lookup (name, shell_variables); ++ if (var && nameref_p (var)) ++ var = find_variable_nameref (var); ++ ++ if (var == 0) ++ return ((SHELL_VAR *)NULL); ++ ++ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); ++} ++ ++/* Look up the variable entry named NAME. Returns the entry or NULL. */ ++SHELL_VAR * ++find_variable (name) ++ const char *name; ++{ ++ SHELL_VAR *v; ++ ++ last_table_searched = 0; ++ v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); ++ if (v && nameref_p (v)) ++ v = find_variable_nameref (v); ++ return v; ++} ++ ++SHELL_VAR * ++find_variable_noref (name) ++ const char *name; ++{ ++ SHELL_VAR *v; ++ ++ v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); ++ return v; ++} ++ ++/* Look up the function entry whose name matches STRING. ++ Returns the entry or NULL. */ ++SHELL_VAR * ++find_function (name) ++ const char *name; ++{ ++ return (hash_lookup (name, shell_functions)); ++} ++ ++/* Find the function definition for the shell function named NAME. Returns ++ the entry or NULL. */ ++FUNCTION_DEF * ++find_function_def (name) ++ const char *name; ++{ ++#if defined (DEBUGGER) ++ return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs)); ++#else ++ return ((FUNCTION_DEF *)0); ++#endif ++} ++ ++/* 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 (value_cell (var)); ++} ++ ++/* Return the string value of a variable. Return NULL if the variable ++ doesn't exist. Don't cons a new string. This is a potential memory ++ leak if the variable is found in the temporary environment. Since ++ functions and variables have separate name spaces, returns NULL if ++ var_name is a shell function only. */ ++char * ++get_string_value (var_name) ++ const char *var_name; ++{ ++ SHELL_VAR *var; ++ ++ var = find_variable (var_name); ++ return ((var) ? get_variable_value (var) : (char *)NULL); ++} ++ ++/* This is present for use by the tilde and readline libraries. */ ++char * ++sh_get_env_value (v) ++ const char *v; ++{ ++ return get_string_value (v); ++} ++ ++/* **************************************************************** */ ++/* */ ++/* Creating and setting variables */ ++/* */ ++/* **************************************************************** */ ++ ++/* Set NAME to VALUE if NAME has no value. */ ++SHELL_VAR * ++set_if_not (name, value) ++ char *name, *value; ++{ ++ SHELL_VAR *v; ++ ++ 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) ++ const char *name; ++{ ++ SHELL_VAR *new_var, *old_var; ++ 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 && local_p (old_var) && old_var->context == variable_context) ++ return (old_var); ++ ++ was_tmpvar = old_var && tempvar_p (old_var); ++ /* If we're making a local variable in a shell function, the temporary env ++ has already been merged into the function's variable context stack. We ++ can assume that a temporary var in the same context appears in the same ++ VAR_CONTEXT and can safely be returned without creating a new variable ++ (which results in duplicate names in the same VAR_CONTEXT->table */ ++ /* We can't just test tmpvar_p because variables in the temporary env given ++ to a shell function appear in the function's local variable VAR_CONTEXT ++ but retain their tempvar attribute. We want temporary variables that are ++ found in temporary_env, hence the test for last_table_searched, which is ++ set in hash_lookup and only (so far) checked here. */ ++ if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env) ++ { ++ VUNSETATTR (old_var, att_invisible); ++ return (old_var); ++ } ++ if (was_tmpvar) ++ tmp_value = value_cell (old_var); ++ ++ for (vc = shell_variables; vc; vc = vc->down) ++ if (vc_isfuncenv (vc) && vc->scope == variable_context) ++ break; ++ ++ if (vc == 0) ++ { ++ internal_error (_("make_local_variable: no function context at current scope")); ++ return ((SHELL_VAR *)NULL); ++ } ++ else if (vc->table == 0) ++ vc->table = hash_create (TEMPENV_HASH_BUCKETS); ++ ++ /* Since this is called only from the local/declare/typeset code, we can ++ call builtin_error here without worry (of course, it will also work ++ for anything that sets this_command_name). Variables with the `noassign' ++ attribute may not be made local. The test against old_var's context ++ level is to disallow local copies of readonly global variables (since I ++ believe that this could be a security hole). Readonly copies of calling ++ function local variables are OK. */ ++ if (old_var && (noassign_p (old_var) || ++ (readonly_p (old_var) && old_var->context == 0))) ++ { ++ if (readonly_p (old_var)) ++ sh_readonly (name); ++ else if (noassign_p (old_var)) ++ builtin_error (_("%s: variable may not be assigned value"), name); ++#if 0 ++ /* Let noassign variables through with a warning */ ++ if (readonly_p (old_var)) ++#endif ++ return ((SHELL_VAR *)NULL); ++ } ++ ++ if (old_var == 0) ++ new_var = make_new_variable (name, vc->table); ++ else ++ { ++ new_var = make_new_variable (name, vc->table); ++ ++ /* If we found this variable in one of the temporary environments, ++ inherit its value. Watch to see if this causes problems with ++ things like `x=4 local x'. XXX - see above for temporary env ++ variables with the same context level as variable_context */ ++ /* XXX - we should only do this if the variable is not an array. */ ++ if (was_tmpvar) ++ var_setvalue (new_var, savestring (tmp_value)); ++ ++ new_var->attributes = exported_p (old_var) ? att_exported : 0; ++ } ++ ++ vc->flags |= VC_HASLOCAL; ++ ++ new_var->context = variable_context; ++ VSETATTR (new_var, att_local); ++ ++ if (ifsname (name)) ++ setifs (new_var); ++ ++ if (was_tmpvar == 0) ++ VSETATTR (new_var, att_invisible); /* XXX */ ++ return (new_var); ++} ++ ++/* Create a new shell variable with name NAME. */ ++static SHELL_VAR * ++new_shell_variable (name) ++ const char *name; ++{ ++ SHELL_VAR *entry; ++ ++ entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); ++ ++ entry->name = savestring (name); ++ var_setvalue (entry, (char *)NULL); ++ CLEAR_EXPORTSTR (entry); ++ ++ entry->dynamic_value = (sh_var_value_func_t *)NULL; ++ entry->assign_func = (sh_var_assign_func_t *)NULL; ++ ++ entry->attributes = 0; ++ ++ /* Always assume variables are to be made at toplevel! ++ make_local_variable has the responsibility of changing the ++ variable context. */ ++ entry->context = 0; ++ ++ 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; ++} ++ ++#if defined (ARRAY_VARS) ++SHELL_VAR * ++make_new_array_variable (name) ++ char *name; ++{ ++ SHELL_VAR *entry; ++ ARRAY *array; ++ ++ entry = make_new_variable (name, global_variables->table); ++ array = array_create (); ++ ++ var_setarray (entry, array); ++ VSETATTR (entry, att_array); ++ return entry; ++} ++ ++SHELL_VAR * ++make_local_array_variable (name, assoc_ok) ++ char *name; ++ int assoc_ok; ++{ ++ SHELL_VAR *var; ++ ARRAY *array; ++ ++ var = make_local_variable (name); ++ if (var == 0 || array_p (var) || (assoc_ok && assoc_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); ++ } ++ /* This can be fooled if the variable's value changes while evaluating ++ `rval'. We can change it if we move the evaluation of lval to here. */ ++ 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 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 = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table); ++ /* Follow the nameref chain here if this is the global variables table */ ++ if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table) ++ { ++ entry = find_global_variable (entry->name); ++ /* Let's see if we have a nameref referencing a variable that hasn't yet ++ been created. */ ++ if (entry == 0) ++ entry = find_variable_last_nameref (name); /* XXX */ ++ if (entry == 0) /* just in case */ ++ return (entry); ++ } ++ ++ /* The first clause handles `declare -n ref; ref=x;' */ ++ if (entry && invisible_p (entry) && nameref_p (entry)) ++ goto assign_value; ++ else if (entry && nameref_p (entry)) ++ { ++ newval = nameref_cell (entry); ++#if defined (ARRAY_VARS) ++ /* declare -n foo=x[2] */ ++ if (valid_array_reference (newval)) ++ /* XXX - should it be aflags? */ ++ entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags); ++ else ++#endif ++ { ++ entry = make_new_variable (newval, table); ++ var_setvalue (entry, make_variable_value (entry, value, 0)); ++ } ++ } ++ else if (entry == 0) ++ { ++ entry = make_new_variable (name, table); ++ var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */ ++ } ++ else if (entry->assign_func) /* array vars have assign functions now */ ++ { ++ INVALIDATE_EXPORTSTR (entry); ++ newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value; ++ 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 ++ { ++assign_value: ++ if (readonly_p (entry) || noassign_p (entry)) ++ { ++ if (readonly_p (entry)) ++ err_readonly (name); ++ return (entry); ++ } ++ ++ /* Variables which are bound are visible. */ ++ VUNSETATTR (entry, att_invisible); ++ ++#if defined (ARRAY_VARS) ++ if (assoc_p (entry) || array_p (entry)) ++ newval = make_array_variable_value (entry, 0, "0", value, aflags); ++ else ++#endif ++ ++ newval = make_variable_value (entry, value, aflags); /* XXX */ ++ ++ /* Invalidate any cached export string */ ++ INVALIDATE_EXPORTSTR (entry); ++ ++#if defined (ARRAY_VARS) ++ /* XXX -- this bears looking at again -- XXX */ ++ /* If an existing array variable x is being assigned to with x=b or ++ `read x' or something of that nature, silently convert it to ++ x[0]=b or `read x[0]'. */ ++ if (assoc_p (entry)) ++ { ++ assoc_insert (assoc_cell (entry), savestring ("0"), newval); ++ free (newval); ++ } ++ else if (array_p (entry)) ++ { ++ array_insert (array_cell (entry), 0, newval); ++ free (newval); ++ } ++ else ++#endif ++ { ++ FREE (value_cell (entry)); ++ var_setvalue (entry, newval); ++ } ++ } ++ ++ if (mark_modified_vars) ++ VSETATTR (entry, att_exported); ++ ++ if (exported_p (entry)) ++ array_needs_making = 1; ++ ++ return (entry); ++} ++ ++/* Bind a variable NAME to VALUE. This conses up the name ++ and value strings. If we have a temporary environment, we bind there ++ first, then we bind into shell_variables. */ ++ ++SHELL_VAR * ++bind_variable (name, value, flags) ++ const char *name; ++ char *value; ++ int flags; ++{ ++ SHELL_VAR *v, *nv; ++ VAR_CONTEXT *vc, *nvc; ++ int level; ++ ++ 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); ++ nvc = vc; ++ if (v && nameref_p (v)) ++ { ++ nv = find_variable_nameref_context (v, vc, &nvc); ++ if (nv == 0) ++ { ++ nv = find_variable_last_nameref_context (v, vc, &nvc); ++ if (nv && nameref_p (nv)) ++ { ++ /* If this nameref variable doesn't have a value yet, ++ set the value. Otherwise, assign using the value as ++ normal. */ ++ if (nameref_cell (nv) == 0) ++ return (bind_variable_internal (nv->name, value, nvc->table, 0, flags)); ++ return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags)); ++ } ++ else ++ v = nv; ++ } ++ else ++ v = nv; ++ } ++ if (v) ++ return (bind_variable_internal (v->name, value, nvc->table, 0, flags)); ++ } ++ } ++ /* bind_variable_internal will handle nameref resolution in this case */ ++ return (bind_variable_internal (name, value, global_variables->table, 0, flags)); ++} ++ ++SHELL_VAR * ++bind_global_variable (name, value, flags) ++ const char *name; ++ char *value; ++ int flags; ++{ ++ SHELL_VAR *v, *nv; ++ VAR_CONTEXT *vc, *nvc; ++ int level; ++ ++ if (shell_variables == 0) ++ create_variable_tables (); ++ ++ /* bind_variable_internal will handle nameref resolution in this case */ ++ 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 ++ of the internals of bind_variable. If the variable is exported, or ++ all modified variables should be exported, mark the variable for export ++ and note that the export environment needs to be recreated. */ ++SHELL_VAR * ++bind_variable_value (var, value, aflags) ++ SHELL_VAR *var; ++ char *value; ++ int aflags; ++{ ++ char *t; ++ int invis; ++ ++ invis = invisible_p (var); ++ VUNSETATTR (var, att_invisible); ++ ++ 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); ++#if defined (ARRAY_VARS) ++ if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0))) ++#else ++ if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0)) ++#endif ++ { ++ free (t); ++ if (invis) ++ VSETATTR (var, att_invisible); /* XXX */ ++ return ((SHELL_VAR *)NULL); ++ } ++ FREE (value_cell (var)); ++ var_setvalue (var, t); ++ } ++ ++ INVALIDATE_EXPORTSTR (var); ++ ++ if (mark_modified_vars) ++ VSETATTR (var, att_exported); ++ ++ if (exported_p (var)) ++ array_needs_making = 1; ++ ++ return (var); ++} ++ ++/* Bind/create a shell variable with the name LHS to the RHS. ++ This creates or modifies a variable such that it is an integer. ++ ++ This used to be in expr.c, but it is here so that all of the ++ variable binding stuff is localized. Since we don't want any ++ recursive evaluation from bind_variable() (possible without this code, ++ since bind_variable() calls the evaluator for variables with the integer ++ attribute set), we temporarily turn off the integer attribute for each ++ variable we set here, then turn it back on after binding as necessary. */ ++ ++SHELL_VAR * ++bind_int_variable (lhs, rhs) ++ char *lhs, *rhs; ++{ ++ register SHELL_VAR *v; ++ int isint, isarr, implicitarray; ++ ++ isint = isarr = implicitarray = 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); ++ ++ if (v) ++ { ++ isint = integer_p (v); ++ VUNSETATTR (v, att_integer); ++#if defined (ARRAY_VARS) ++ if (array_p (v) && isarr == 0) ++ implicitarray = 1; ++#endif ++ } ++ ++#if defined (ARRAY_VARS) ++ if (isarr) ++ v = assign_array_element (lhs, rhs, 0); ++ else if (implicitarray) ++ v = bind_array_variable (lhs, 0, rhs, 0); ++ else ++#endif ++ v = bind_variable (lhs, rhs, 0); ++ ++ if (v && isint) ++ VSETATTR (v, att_integer); ++ ++ VUNSETATTR (v, att_invisible); ++ ++ return (v); ++} ++ ++SHELL_VAR * ++bind_var_to_int (var, val) ++ char *var; ++ intmax_t val; ++{ ++ char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p; ++ ++ p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0); ++ return (bind_int_variable (var, p)); ++} ++ ++/* Do a function binding to a variable. You pass the name and ++ the command to bind to. This conses the name and command. */ ++SHELL_VAR * ++bind_function (name, value) ++ const char *name; ++ COMMAND *value; ++{ ++ SHELL_VAR *entry; ++ ++ entry = find_function (name); ++ if (entry == 0) ++ { ++ BUCKET_CONTENTS *elt; ++ ++ elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH); ++ entry = new_shell_variable (name); ++ elt->data = (PTR_T)entry; ++ } ++ else ++ INVALIDATE_EXPORTSTR (entry); ++ ++ if (var_isset (entry)) ++ dispose_command (function_cell (entry)); ++ ++ if (value) ++ var_setfunc (entry, copy_command (value)); ++ else ++ var_setfunc (entry, 0); ++ ++ VSETATTR (entry, att_function); ++ ++ if (mark_modified_vars) ++ VSETATTR (entry, att_exported); ++ ++ VUNSETATTR (entry, att_invisible); /* Just to be sure */ ++ ++ if (exported_p (entry)) ++ array_needs_making = 1; ++ ++#if defined (PROGRAMMABLE_COMPLETION) ++ set_itemlist_dirty (&it_functions); ++#endif ++ ++ return (entry); ++} ++ ++#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; ++{ ++ FUNCTION_DEF *entry; ++ BUCKET_CONTENTS *elt; ++ COMMAND *cmd; ++ ++ entry = find_function_def (name); ++ if (entry) ++ { ++ dispose_function_def_contents (entry); ++ entry = copy_function_def_contents (value, entry); ++ } ++ else ++ { ++ cmd = value->command; ++ value->command = 0; ++ entry = copy_function_def (value); ++ value->command = cmd; ++ ++ elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH); ++ elt->data = (PTR_T *)entry; ++ } ++} ++#endif /* DEBUGGER */ ++ ++/* 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; ++{ ++ int offset, aflags; ++ char *name, *temp, *value; ++ SHELL_VAR *var; ++ const char *string; ++ ++ string = word->word; ++ ++ aflags = 0; ++ offset = assignment (string, 0); ++ name = savestring (string); ++ value = (char *)NULL; ++ ++ if (name[offset] == '=') ++ { ++ name[offset] = 0; ++ ++ /* don't ignore the `+' when assigning temporary environment */ ++ if (name[offset - 1] == '+') ++ { ++ name[offset - 1] = '\0'; ++ aflags |= ASS_APPEND; ++ } ++ ++ var = find_variable (name); ++ if (var && (readonly_p (var) || noassign_p (var))) ++ { ++ if (readonly_p (var)) ++ err_readonly (name); ++ free (name); ++ return (0); ++ } ++ ++ temp = name + offset + 1; ++ value = expand_assignment_string_to_string (temp, 0); ++ ++ if (var && (aflags & ASS_APPEND)) ++ { ++ temp = make_variable_value (var, value, aflags); ++ FREE (value); ++ value = temp; ++ } ++ } ++ ++ if (temporary_env == 0) ++ temporary_env = hash_create (TEMPENV_HASH_BUCKETS); ++ ++ var = hash_lookup (name, temporary_env); ++ if (var == 0) ++ var = make_new_variable (name, temporary_env); ++ else ++ FREE (value_cell (var)); ++ ++ if (value == 0) ++ { ++ value = (char *)xmalloc (1); /* like do_assignment_internal */ ++ value[0] = '\0'; ++ } ++ ++ var_setvalue (var, value); ++ var->attributes |= (att_exported|att_tempvar); ++ var->context = variable_context; /* XXX */ ++ ++ INVALIDATE_EXPORTSTR (var); ++ var->exportstr = mk_env_string (name, value, 0); ++ ++ array_needs_making = 1; ++ ++ if (flags) ++ stupidly_hack_special_variables (name); ++ ++ if (echo_command_at_execute) ++ /* The Korn shell prints the `+ ' in front of assignment statements, ++ so we do too. */ ++ xtrace_print_assignment (name, value, 0, 1); ++ ++ free (name); ++ return 1; ++} ++ ++/* **************************************************************** */ ++/* */ ++/* Copying variables */ ++/* */ ++/* **************************************************************** */ ++ ++#ifdef INCLUDE_UNUSED ++/* Copy VAR to a new data structure and return that structure. */ ++SHELL_VAR * ++copy_variable (var) ++ SHELL_VAR *var; ++{ ++ SHELL_VAR *copy = (SHELL_VAR *)NULL; ++ ++ if (var) ++ { ++ copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); ++ ++ copy->attributes = var->attributes; ++ copy->name = savestring (var->name); ++ ++ if (function_p (var)) ++ var_setfunc (copy, copy_command (function_cell (var))); ++#if defined (ARRAY_VARS) ++ else if (array_p (var)) ++ var_setarray (copy, array_copy (array_cell (var))); ++ else if (assoc_p (var)) ++ var_setassoc (copy, assoc_copy (assoc_cell (var))); ++#endif ++ else if (nameref_cell (var)) /* XXX - nameref */ ++ var_setref (copy, savestring (nameref_cell (var))); ++ else if (value_cell (var)) /* XXX - nameref */ ++ var_setvalue (copy, savestring (value_cell (var))); ++ else ++ var_setvalue (copy, (char *)NULL); ++ ++ copy->dynamic_value = var->dynamic_value; ++ copy->assign_func = var->assign_func; ++ ++ copy->exportstr = COPY_EXPORTSTR (var); ++ ++ copy->context = var->context; ++ } ++ return (copy); ++} ++#endif ++ ++/* **************************************************************** */ ++/* */ ++/* Deleting and unsetting variables */ ++/* */ ++/* **************************************************************** */ ++ ++/* Dispose of the information attached to VAR. */ ++static void ++dispose_variable_value (var) ++ SHELL_VAR *var; ++{ ++ if (function_p (var)) ++ dispose_command (function_cell (var)); ++#if defined (ARRAY_VARS) ++ else if (array_p (var)) ++ array_dispose (array_cell (var)); ++ else if (assoc_p (var)) ++ assoc_dispose (assoc_cell (var)); ++#endif ++ else if (nameref_p (var)) ++ FREE (nameref_cell (var)); ++ 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->name); ++ ++ if (exported_p (var)) ++ array_needs_making = 1; ++ ++ free (var); ++} ++ ++/* Unset the shell variable referenced by NAME. Unsetting a nameref variable ++ unsets the variable it resolves to but leaves the nameref alone. */ ++int ++unbind_variable (name) ++ const char *name; ++{ ++ SHELL_VAR *v, *nv; ++ int r; ++ ++ v = var_lookup (name, shell_variables); ++ nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL; ++ ++ r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables); ++ return r; ++} ++ ++/* Unbind NAME, where NAME is assumed to be a nameref variable */ ++int ++unbind_nameref (name) ++ const char *name; ++{ ++ SHELL_VAR *v; ++ ++ v = var_lookup (name, shell_variables); ++ if (v && nameref_p (v)) ++ return makunbound (name, shell_variables); ++ return 0; ++} ++ ++/* Unset the shell function named NAME. */ ++int ++unbind_func (name) ++ const char *name; ++{ ++ BUCKET_CONTENTS *elt; ++ SHELL_VAR *func; ++ ++ elt = hash_remove (name, shell_functions, 0); ++ ++ if (elt == 0) ++ return -1; ++ ++#if defined (PROGRAMMABLE_COMPLETION) ++ set_itemlist_dirty (&it_functions); ++#endif ++ ++ func = (SHELL_VAR *)elt->data; ++ if (func) ++ { ++ if (exported_p (func)) ++ array_needs_making++; ++ dispose_variable (func); ++ } ++ ++ free (elt->key); ++ free (elt); ++ ++ return 0; ++} ++ ++#if defined (DEBUGGER) ++int ++unbind_function_def (name) ++ const char *name; ++{ ++ BUCKET_CONTENTS *elt; ++ FUNCTION_DEF *funcdef; ++ ++ elt = hash_remove (name, shell_function_defs, 0); ++ ++ if (elt == 0) ++ return -1; ++ ++ funcdef = (FUNCTION_DEF *)elt->data; ++ if (funcdef) ++ dispose_function_def (funcdef); ++ ++ free (elt->key); ++ free (elt); ++ ++ return 0; ++} ++#endif /* DEBUGGER */ ++ ++int ++delete_var (name, vc) ++ const char *name; ++ VAR_CONTEXT *vc; ++{ ++ BUCKET_CONTENTS *elt; ++ SHELL_VAR *old_var; ++ VAR_CONTEXT *v; ++ ++ 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; ++ free (elt->key); ++ free (elt); ++ ++ dispose_variable (old_var); ++ return (0); ++} ++ ++/* Make the variable associated with NAME go away. HASH_LIST is the ++ hash table from which this variable should be deleted (either ++ shell_variables or shell_functions). ++ Returns non-zero if the variable couldn't be found. */ ++int ++makunbound (name, vc) ++ const char *name; ++ VAR_CONTEXT *vc; ++{ ++ BUCKET_CONTENTS *elt, *new_elt; ++ SHELL_VAR *old_var; ++ VAR_CONTEXT *v; ++ char *t; ++ ++ 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; ++ ++ if (old_var && exported_p (old_var)) ++ array_needs_making++; ++ ++ /* If we're unsetting a local variable and we're still executing inside ++ the function, just mark the variable as invisible. The function ++ eventually called by pop_var_context() will clean it up later. This ++ must be done so that if the variable is subsequently assigned a new ++ value inside the function, the `local' attribute is still present. ++ We also need to add it back into the correct hash table. */ ++ if (old_var && local_p (old_var) && variable_context == old_var->context) ++ { ++ 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 if (nameref_p (old_var)) ++ FREE (nameref_cell (old_var)); ++ 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 = hash_insert (savestring (old_var->name), v->table, 0); ++ new_elt->data = (PTR_T)old_var; ++ stupidly_hack_special_variables (old_var->name); ++ ++ free (elt->key); ++ free (elt); ++ return (0); ++ } ++ ++ /* Have to save a copy of name here, because it might refer to ++ old_var->name. If so, stupidly_hack_special_variables will ++ reference freed memory. */ ++ t = savestring (name); ++ ++ free (elt->key); ++ free (elt); ++ ++ dispose_variable (old_var); ++ stupidly_hack_special_variables (t); ++ free (t); ++ ++ return (0); ++} ++ ++/* Get rid of all of the variables in the current context. */ ++void ++kill_all_local_variables () ++{ ++ VAR_CONTEXT *vc; ++ ++ for (vc = shell_variables; vc; vc = vc->down) ++ if (vc_isfuncenv (vc) && vc->scope == variable_context) ++ break; ++ if (vc == 0) ++ return; /* XXX */ ++ ++ if (vc->table && vc_haslocals (vc)) ++ { ++ delete_all_variables (vc->table); ++ hash_dispose (vc->table); ++ } ++ vc->table = (HASH_TABLE *)NULL; ++} ++ ++static void ++free_variable_hash_data (data) ++ PTR_T data; ++{ ++ SHELL_VAR *var; ++ ++ var = (SHELL_VAR *)data; ++ dispose_variable (var); ++} ++ ++/* Delete the entire contents of the hash table. */ ++void ++delete_all_variables (hashed_vars) ++ HASH_TABLE *hashed_vars; ++{ ++ hash_flush (hashed_vars, free_variable_hash_data); ++} ++ ++/* **************************************************************** */ ++/* */ ++/* Setting variable attributes */ ++/* */ ++/* **************************************************************** */ ++ ++#define FIND_OR_MAKE_VARIABLE(name, entry) \ ++ do \ ++ { \ ++ entry = find_variable (name); \ ++ if (!entry) \ ++ { \ ++ entry = bind_variable (name, "", 0); \ ++ if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \ ++ } \ ++ } \ ++ while (0) ++ ++/* Make the variable associated with NAME be readonly. ++ If NAME does not exist yet, create it. */ ++void ++set_var_read_only (name) ++ char *name; ++{ ++ SHELL_VAR *entry; ++ ++ FIND_OR_MAKE_VARIABLE (name, entry); ++ VSETATTR (entry, att_readonly); ++} ++ ++#ifdef INCLUDE_UNUSED ++/* Make the function associated with NAME be readonly. ++ If NAME does not exist, we just punt, like auto_export code below. */ ++void ++set_func_read_only (name) ++ const char *name; ++{ ++ SHELL_VAR *entry; ++ ++ entry = find_function (name); ++ if (entry) ++ VSETATTR (entry, att_readonly); ++} ++ ++/* Make the variable associated with NAME be auto-exported. ++ If NAME does not exist yet, create it. */ ++void ++set_var_auto_export (name) ++ char *name; ++{ ++ SHELL_VAR *entry; ++ ++ FIND_OR_MAKE_VARIABLE (name, entry); ++ set_auto_export (entry); ++} ++ ++/* Make the function associated with NAME be auto-exported. */ ++void ++set_func_auto_export (name) ++ const char *name; ++{ ++ SHELL_VAR *entry; ++ ++ entry = find_function (name); ++ if (entry) ++ set_auto_export (entry); ++} ++#endif ++ ++/* **************************************************************** */ ++/* */ ++/* Creating lists of variables */ ++/* */ ++/* **************************************************************** */ ++ ++static VARLIST * ++vlist_alloc (nentries) ++ int nentries; ++{ ++ VARLIST *vlist; ++ ++ vlist = (VARLIST *)xmalloc (sizeof (VARLIST)); ++ vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *)); ++ vlist->list_size = nentries; ++ vlist->list_len = 0; ++ vlist->list[0] = (SHELL_VAR *)NULL; ++ ++ return vlist; ++} ++ ++static VARLIST * ++vlist_realloc (vlist, n) ++ VARLIST *vlist; ++ int n; ++{ ++ if (vlist == 0) ++ return (vlist = vlist_alloc (n)); ++ if (n > vlist->list_size) ++ { ++ vlist->list_size = n; ++ vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *)); ++ } ++ return vlist; ++} ++ ++static void ++vlist_add (vlist, var, flags) ++ VARLIST *vlist; ++ SHELL_VAR *var; ++ int flags; ++{ ++ register int i; ++ ++ for (i = 0; i < vlist->list_len; i++) ++ if (STREQ (var->name, vlist->list[i]->name)) ++ break; ++ if (i < vlist->list_len) ++ return; ++ ++ if (i >= vlist->list_size) ++ vlist = vlist_realloc (vlist, vlist->list_size + 16); ++ ++ vlist->list[vlist->list_len++] = var; ++ vlist->list[vlist->list_len] = (SHELL_VAR *)NULL; ++} ++ ++/* Map FUNCTION over the variables in VAR_HASH_TABLE. Return an array of the ++ variables for which FUNCTION returns a non-zero value. A NULL value ++ for FUNCTION means to use all variables. */ ++SHELL_VAR ** ++map_over (function, vc) ++ sh_var_map_func_t *function; ++ VAR_CONTEXT *vc; ++{ ++ VAR_CONTEXT *v; ++ VARLIST *vlist; ++ SHELL_VAR **ret; ++ int nentries; ++ ++ for (nentries = 0, v = vc; v; v = v->down) ++ nentries += HASH_ENTRIES (v->table); ++ ++ if (nentries == 0) ++ return (SHELL_VAR **)NULL; ++ ++ vlist = vlist_alloc (nentries); ++ ++ for (v = vc; v; v = v->down) ++ flatten (v->table, function, vlist, 0); ++ ++ ret = vlist->list; ++ free (vlist); ++ return ret; ++} ++ ++SHELL_VAR ** ++map_over_funcs (function) ++ sh_var_map_func_t *function; ++{ ++ VARLIST *vlist; ++ SHELL_VAR **ret; ++ ++ if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0) ++ return ((SHELL_VAR **)NULL); ++ ++ vlist = vlist_alloc (HASH_ENTRIES (shell_functions)); ++ ++ flatten (shell_functions, function, vlist, 0); ++ ++ ret = vlist->list; ++ free (vlist); ++ return ret; ++} ++ ++/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those ++ elements for which FUNC succeeds to VLIST->list. FLAGS is reserved ++ for future use. Only unique names are added to VLIST. If FUNC is ++ NULL, each variable in VAR_HASH_TABLE is added to VLIST. If VLIST is ++ NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE. If VLIST ++ and FUNC are both NULL, nothing happens. */ ++static void ++flatten (var_hash_table, func, vlist, flags) ++ HASH_TABLE *var_hash_table; ++ sh_var_map_func_t *func; ++ VARLIST *vlist; ++ int flags; ++{ ++ register int i; ++ register BUCKET_CONTENTS *tlist; ++ int r; ++ SHELL_VAR *var; ++ ++ if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0)) ++ return; ++ ++ for (i = 0; i < var_hash_table->nbuckets; i++) ++ { ++ for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next) ++ { ++ var = (SHELL_VAR *)tlist->data; ++ ++ r = func ? (*func) (var) : 1; ++ if (r && vlist) ++ vlist_add (vlist, var, flags); ++ } ++ } ++} ++ ++void ++sort_variables (array) ++ SHELL_VAR **array; ++{ ++ qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp); ++} ++ ++static int ++qsort_var_comp (var1, var2) ++ SHELL_VAR **var1, **var2; ++{ ++ int result; ++ ++ if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0) ++ result = strcmp ((*var1)->name, (*var2)->name); ++ ++ return (result); ++} ++ ++/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for ++ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */ ++static SHELL_VAR ** ++vapply (func) ++ sh_var_map_func_t *func; ++{ ++ SHELL_VAR **list; ++ ++ list = map_over (func, shell_variables); ++ if (list /* && posixly_correct */) ++ sort_variables (list); ++ return (list); ++} ++ ++/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for ++ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */ ++static SHELL_VAR ** ++fapply (func) ++ sh_var_map_func_t *func; ++{ ++ SHELL_VAR **list; ++ ++ list = map_over_funcs (func); ++ if (list /* && posixly_correct */) ++ sort_variables (list); ++ return (list); ++} ++ ++/* Create a NULL terminated array of all the shell variables. */ ++SHELL_VAR ** ++all_shell_variables () ++{ ++ return (vapply ((sh_var_map_func_t *)NULL)); ++} ++ ++/* Create a NULL terminated array of all the shell functions. */ ++SHELL_VAR ** ++all_shell_functions () ++{ ++ return (fapply ((sh_var_map_func_t *)NULL)); ++} ++ ++static int ++visible_var (var) ++ SHELL_VAR *var; ++{ ++ return (invisible_p (var) == 0); ++} ++ ++SHELL_VAR ** ++all_visible_functions () ++{ ++ return (fapply (visible_var)); ++} ++ ++SHELL_VAR ** ++all_visible_variables () ++{ ++ return (vapply (visible_var)); ++} ++ ++/* Return non-zero if the variable VAR is visible and exported. Array ++ variables cannot be exported. */ ++static int ++visible_and_exported (var) ++ SHELL_VAR *var; ++{ ++ return (invisible_p (var) == 0 && exported_p (var)); ++} ++ ++/* 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 () ++{ ++ return (vapply (visible_and_exported)); ++} ++ ++SHELL_VAR ** ++local_exported_variables () ++{ ++ return (vapply (local_and_exported)); ++} ++ ++static int ++variable_in_context (var) ++ SHELL_VAR *var; ++{ ++ return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context); ++} ++ ++SHELL_VAR ** ++all_local_variables () ++{ ++ VARLIST *vlist; ++ SHELL_VAR **ret; ++ VAR_CONTEXT *vc; ++ ++ vc = shell_variables; ++ for (vc = shell_variables; vc; vc = vc->down) ++ if (vc_isfuncenv (vc) && vc->scope == variable_context) ++ break; ++ ++ if (vc == 0) ++ { ++ internal_error (_("all_local_variables: no function context at current scope")); ++ return (SHELL_VAR **)NULL; ++ } ++ if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0) ++ return (SHELL_VAR **)NULL; ++ ++ vlist = vlist_alloc (HASH_ENTRIES (vc->table)); ++ ++ flatten (vc->table, variable_in_context, vlist, 0); ++ ++ ret = vlist->list; ++ free (vlist); ++ if (ret) ++ sort_variables (ret); ++ return ret; ++} ++ ++#if defined (ARRAY_VARS) ++/* Return non-zero if the variable VAR is visible and an array. */ ++static int ++visible_array_vars (var) ++ SHELL_VAR *var; ++{ ++ return (invisible_p (var) == 0 && array_p (var)); ++} ++ ++SHELL_VAR ** ++all_array_variables () ++{ ++ return (vapply (visible_array_vars)); ++} ++#endif /* ARRAY_VARS */ ++ ++char ** ++all_variables_matching_prefix (prefix) ++ const char *prefix; ++{ ++ SHELL_VAR **varlist; ++ char **rlist; ++ int vind, rind, plen; ++ ++ plen = STRLEN (prefix); ++ varlist = all_visible_variables (); ++ for (vind = 0; varlist && varlist[vind]; vind++) ++ ; ++ if (varlist == 0 || vind == 0) ++ return ((char **)NULL); ++ rlist = strvec_create (vind + 1); ++ for (vind = rind = 0; varlist[vind]; vind++) ++ { ++ if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen)) ++ rlist[rind++] = savestring (varlist[vind]->name); ++ } ++ rlist[rind] = (char *)0; ++ free (varlist); ++ ++ return rlist; ++} ++ ++/* **************************************************************** */ ++/* */ ++/* Managing temporary variable scopes */ ++/* */ ++/* **************************************************************** */ ++ ++/* Make variable NAME have VALUE in the temporary environment. */ ++static SHELL_VAR * ++bind_tempenv_variable (name, value) ++ const char *name; ++ char *value; ++{ ++ SHELL_VAR *var; ++ ++ var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL; ++ ++ if (var) ++ { ++ FREE (value_cell (var)); ++ var_setvalue (var, savestring (value)); ++ INVALIDATE_EXPORTSTR (var); ++ } ++ ++ return (var); ++} ++ ++/* Find a variable in the temporary environment that is named NAME. ++ Return the SHELL_VAR *, or NULL if not found. */ ++SHELL_VAR * ++find_tempenv_variable (name) ++ const char *name; ++{ ++ return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL); ++} ++ ++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, isfunc) ++ const char *name, *value; ++ int isfunc; ++{ ++ size_t name_len, value_len; ++ char *p, *q; ++ ++ name_len = strlen (name); ++ value_len = STRLEN (value); ++ ++ /* If we are exporting a shell function, construct the encoded function ++ name. */ ++ if (isfunc && value) ++ { ++ p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2); ++ q = p; ++ memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN); ++ q += BASHFUNC_PREFLEN; ++ memcpy (q, name, name_len); ++ q += name_len; ++ memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN); ++ q += BASHFUNC_SUFFLEN; ++ } ++ else ++ { ++ p = (char *)xmalloc (2 + name_len + value_len); ++ memcpy (p, name, name_len); ++ q = p + name_len; ++ } ++ ++ q[0] = '='; ++ if (value && *value) ++ memcpy (q + 1, value, value_len + 1); ++ else ++ q[1] = '\0'; ++ ++ return (p); ++} ++ ++#ifdef DEBUG ++/* Debugging */ ++static int ++valid_exportstr (v) ++ SHELL_VAR *v; ++{ ++ char *s; ++ ++ s = v->exportstr; ++ if (s == 0) ++ { ++ 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 ((unsigned char)*s) == 0) ++ { ++ internal_error (_("invalid character %d in exportstr for %s"), *s, v->name); ++ return (0); ++ } ++ } ++ if (*s != '=') ++ { ++ internal_error (_("no `=' in exportstr for %s"), v->name); ++ return (0); ++ } ++ return (1); ++} ++#endif ++ ++static char ** ++make_env_array_from_var_list (vars) ++ SHELL_VAR **vars; ++{ ++ register int i, list_index; ++ register SHELL_VAR *var; ++ char **list, *value; ++ ++ list = strvec_create ((1 + strvec_len ((char **)vars))); ++ ++#define USE_EXPORTSTR (value == var->exportstr) ++ ++ for (i = 0, list_index = 0; var = vars[i]; i++) ++ { ++#if defined (__CYGWIN__) ++ /* We don't use the exportstr stuff on Cygwin at all. */ ++ INVALIDATE_EXPORTSTR (var); ++#endif ++ if (var->exportstr) ++ value = var->exportstr; ++ else if (function_p (var)) ++ value = named_function_string ((char *)NULL, function_cell (var), 0); ++#if defined (ARRAY_VARS) ++ else if (array_p (var)) ++# if ARRAY_EXPORT ++ value = array_to_assignment_string (array_cell (var)); ++# else ++ continue; /* XXX array vars cannot yet be exported */ ++# endif /* ARRAY_EXPORT */ ++ 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 ++ value = value_cell (var); ++ ++ if (value) ++ { ++ /* Gee, I'd like to get away with not using savestring() if we're ++ using the cached exportstr... */ ++ list[list_index] = USE_EXPORTSTR ? savestring (value) ++ : mk_env_string (var->name, value, function_p (var)); ++ ++ 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) || assoc_p (var)) ++ free (value); ++#endif ++#endif ++ } ++ } ++ ++ list[list_index] = (char *)NULL; ++ return (list); ++} ++ ++/* Make an array of assignment statements from the hash table ++ HASHED_VARS which contains SHELL_VARs. Only visible, exported ++ variables are eligible. */ ++static char ** ++make_var_export_array (vcxt) ++ VAR_CONTEXT *vcxt; ++{ ++ char **list; ++ SHELL_VAR **vars; ++ ++#if 0 ++ vars = map_over (visible_and_exported, vcxt); ++#else ++ vars = map_over (export_environment_candidate, vcxt); ++#endif ++ ++ if (vars == 0) ++ return (char **)NULL; ++ ++ list = make_env_array_from_var_list (vars); ++ ++ free (vars); ++ return (list); ++} ++ ++static char ** ++make_func_export_array () ++{ ++ char **list; ++ SHELL_VAR **vars; ++ ++ vars = map_over_funcs (visible_and_exported); ++ if (vars == 0) ++ return (char **)NULL; ++ ++ list = make_env_array_from_var_list (vars); ++ ++ free (vars); ++ return (list); ++} ++ ++/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */ ++#define add_to_export_env(envstr,do_alloc) \ ++do \ ++ { \ ++ if (export_env_index >= (export_env_size - 1)) \ ++ { \ ++ export_env_size += 16; \ ++ export_env = strvec_resize (export_env, export_env_size); \ ++ environ = export_env; \ ++ } \ ++ export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \ ++ export_env[export_env_index] = (char *)NULL; \ ++ } while (0) ++ ++/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the ++ array with the same left-hand side. Return the new EXPORT_ENV. */ ++char ** ++add_or_supercede_exported_var (assign, do_alloc) ++ char *assign; ++ int do_alloc; ++{ ++ register int i; ++ int equal_offset; ++ ++ equal_offset = assignment (assign, 0); ++ if (equal_offset == 0) ++ return (export_env); ++ ++ /* If this is a function, then only supersede the function definition. ++ We do this by including the `=() {' in the comparison, like ++ initialize_shell_variables does. */ ++ if (assign[equal_offset + 1] == '(' && ++ strncmp (assign + equal_offset + 2, ") {", 3) == 0) /* } */ ++ equal_offset += 4; ++ ++ for (i = 0; i < export_env_index; i++) ++ { ++ if (STREQN (assign, export_env[i], equal_offset + 1)) ++ { ++ free (export_env[i]); ++ export_env[i] = do_alloc ? savestring (assign) : assign; ++ return (export_env); ++ } ++ } ++ add_to_export_env (assign, do_alloc); ++ return (export_env); ++} ++ ++static void ++add_temp_array_to_env (temp_array, do_alloc, do_supercede) ++ char **temp_array; ++ int do_alloc, do_supercede; ++{ ++ register int i; ++ ++ if (temp_array == 0) ++ return; ++ ++ for (i = 0; temp_array[i]; i++) ++ { ++ if (do_supercede) ++ export_env = add_or_supercede_exported_var (temp_array[i], do_alloc); ++ else ++ add_to_export_env (temp_array[i], do_alloc); ++ } ++ ++ free (temp_array); ++} ++ ++/* Make the environment array for the command about to be executed, if the ++ array needs making. Otherwise, do nothing. If a shell action could ++ change the array that commands receive for their environment, then the ++ code should `array_needs_making++'. ++ ++ The order to add to the array is: ++ temporary_env ++ list of var contexts whose head is shell_variables ++ shell_functions ++ ++ This is the shell variable lookup order. We add only new variable ++ names at each step, which allows local variables and variables in ++ the temporary environments to shadow variables in the global (or ++ any previous) scope. ++*/ ++ ++static int ++n_shell_variables () ++{ ++ VAR_CONTEXT *vc; ++ int n; ++ ++ for (n = 0, vc = shell_variables; vc; vc = vc->down) ++ n += HASH_ENTRIES (vc->table); ++ return n; ++} ++ ++int ++chkexport (name) ++ char *name; ++{ ++ SHELL_VAR *v; ++ ++ v = find_variable (name); ++ if (v && exported_p (v)) ++ { ++ array_needs_making = 1; ++ maybe_make_export_env (); ++ return 1; ++ } ++ return 0; ++} ++ ++void ++maybe_make_export_env () ++{ ++ register char **temp_array; ++ int new_size; ++ VAR_CONTEXT *tcxt; ++ ++ if (array_needs_making) ++ { ++ if (export_env) ++ strvec_flush (export_env); ++ ++ /* Make a guess based on how many shell variables and functions we ++ have. Since there will always be array variables, and array ++ variables are not (yet) exported, this will always be big enough ++ for the exported variables and functions. */ ++ new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 + ++ HASH_ENTRIES (temporary_env); ++ if (new_size > export_env_size) ++ { ++ export_env_size = new_size; ++ export_env = strvec_resize (export_env, export_env_size); ++ environ = export_env; ++ } ++ export_env[export_env_index = 0] = (char *)NULL; ++ ++ /* Make a dummy variable context from the temporary_env, stick it on ++ the front of shell_variables, call make_var_export_array on the ++ whole thing to flatten it, and convert the list of SHELL_VAR *s ++ to the form needed by the environment. */ ++ if (temporary_env) ++ { ++ tcxt = new_var_context ((char *)NULL, 0); ++ tcxt->table = temporary_env; ++ tcxt->down = shell_variables; ++ } ++ else ++ tcxt = shell_variables; ++ ++ temp_array = make_var_export_array (tcxt); ++ if (temp_array) ++ add_temp_array_to_env (temp_array, 0, 0); ++ ++ if (tcxt != shell_variables) ++ free (tcxt); ++ ++#if defined (RESTRICTED_SHELL) ++ /* Restricted shells may not export shell functions. */ ++ temp_array = restricted ? (char **)0 : make_func_export_array (); ++#else ++ temp_array = make_func_export_array (); ++#endif ++ if (temp_array) ++ add_temp_array_to_env (temp_array, 0, 0); ++ ++ array_needs_making = 0; ++ } ++} ++ ++/* This is an efficiency hack. PWD and OLDPWD are auto-exported, so ++ we will need to remake the exported environment every time we ++ change directories. `_' is always put into the environment for ++ every external command, so without special treatment it will always ++ cause the environment to be remade. ++ ++ If there is no other reason to make the exported environment, we can ++ just update the variables in place and mark the exported environment ++ as no longer needing a remake. */ ++void ++update_export_env_inplace (env_prefix, preflen, value) ++ char *env_prefix; ++ int preflen; ++ char *value; ++{ ++ char *evar; ++ ++ evar = (char *)xmalloc (STRLEN (value) + preflen + 1); ++ strcpy (evar, env_prefix); ++ if (value) ++ strcpy (evar + preflen, value); ++ export_env = add_or_supercede_exported_var (evar, 0); ++} ++ ++/* We always put _ in the environment as the name of this command. */ ++void ++put_command_name_into_env (command_name) ++ char *command_name; ++{ ++ update_export_env_inplace ("_=", 2, command_name); ++} ++ ++/* **************************************************************** */ ++/* */ ++/* Managing variable contexts */ ++/* */ ++/* **************************************************************** */ ++ ++/* Allocate and return a new variable context with NAME and FLAGS. ++ NAME can be NULL. */ ++ ++VAR_CONTEXT * ++new_var_context (name, flags) ++ char *name; ++ int flags; ++{ ++ VAR_CONTEXT *vc; ++ ++ vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT)); ++ vc->name = name ? savestring (name) : (char *)NULL; ++ vc->scope = variable_context; ++ vc->flags = flags; ++ ++ vc->up = vc->down = (VAR_CONTEXT *)NULL; ++ vc->table = (HASH_TABLE *)NULL; ++ ++ return vc; ++} ++ ++/* Free a variable context and its data, including the hash table. Dispose ++ all of the variables. */ ++void ++dispose_var_context (vc) ++ VAR_CONTEXT *vc; ++{ ++ FREE (vc->name); ++ ++ if (vc->table) ++ { ++ delete_all_variables (vc->table); ++ hash_dispose (vc->table); ++ } ++ ++ free (vc); ++} ++ ++/* Set VAR's scope level to the current variable context. */ ++static int ++set_context (var) ++ SHELL_VAR *var; ++{ ++ return (var->context = variable_context); ++} ++ ++/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of ++ temporary variables, and push it onto shell_variables. This is ++ for shell functions. */ ++VAR_CONTEXT * ++push_var_context (name, flags, tempvars) ++ char *name; ++ int flags; ++ HASH_TABLE *tempvars; ++{ ++ VAR_CONTEXT *vc; ++ ++ vc = new_var_context (name, flags); ++ vc->table = tempvars; ++ if (tempvars) ++ { ++ /* Have to do this because the temp environment was created before ++ variable_context was incremented. */ ++ flatten (tempvars, set_context, (VARLIST *)NULL, 0); ++ vc->flags |= VC_HASTMPVAR; ++ } ++ vc->down = shell_variables; ++ shell_variables->up = vc; ++ ++ return (shell_variables = vc); ++} ++ ++static void ++push_func_var (data) ++ PTR_T data; ++{ ++ SHELL_VAR *var, *v; ++ ++ var = (SHELL_VAR *)data; ++ ++ if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate))) ++ { ++ /* 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 */ ++ ++ dispose_variable (var); ++} ++ ++/* Pop the top context off of VCXT and dispose of it, returning the rest of ++ the stack. */ ++void ++pop_var_context () ++{ ++ VAR_CONTEXT *ret, *vcxt; ++ ++ vcxt = shell_variables; ++ if (vc_isfuncenv (vcxt) == 0) ++ { ++ internal_error (_("pop_var_context: head of shell_variables not a function context")); ++ return; ++ } ++ ++ if (ret = vcxt->down) ++ { ++ ret->up = (VAR_CONTEXT *)NULL; ++ shell_variables = ret; ++ if (vcxt->table) ++ hash_flush (vcxt->table, push_func_var); ++ dispose_var_context (vcxt); ++ } ++ else ++ internal_error (_("pop_var_context: no global_variables context")); ++} ++ ++/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and ++ all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */ ++void ++delete_all_contexts (vcxt) ++ VAR_CONTEXT *vcxt; ++{ ++ VAR_CONTEXT *v, *t; ++ ++ for (v = vcxt; v != global_variables; v = t) ++ { ++ t = v->down; ++ dispose_var_context (v); ++ } ++ ++ delete_all_variables (global_variables->table); ++ shell_variables = global_variables; ++} ++ ++/* **************************************************************** */ ++/* */ ++/* Pushing and Popping temporary variable scopes */ ++/* */ ++/* **************************************************************** */ ++ ++VAR_CONTEXT * ++push_scope (flags, tmpvars) ++ int flags; ++ HASH_TABLE *tmpvars; ++{ ++ return (push_var_context ((char *)NULL, flags, tmpvars)); ++} ++ ++static void ++push_exported_var (data) ++ PTR_T data; ++{ ++ SHELL_VAR *var, *v; ++ ++ var = (SHELL_VAR *)data; ++ ++ /* If a temp var had its export attribute set, or it's marked to be ++ propagated, bind it in the previous scope before disposing it. */ ++ /* XXX - This isn't exactly right, because all tempenv variables have the ++ export attribute set. */ ++#if 0 ++ if (exported_p (var) || (var->attributes & att_propagate)) ++#else ++ if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate)) ++#endif ++ { ++ var->attributes &= ~att_tempvar; /* XXX */ ++ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0); ++ if (shell_variables == global_variables) ++ var->attributes &= ~att_propagate; ++ v->attributes |= var->attributes; ++ } ++ else ++ stupidly_hack_special_variables (var->name); /* XXX */ ++ ++ dispose_variable (var); ++} ++ ++void ++pop_scope (is_special) ++ int is_special; ++{ ++ VAR_CONTEXT *vcxt, *ret; ++ ++ vcxt = shell_variables; ++ if (vc_istempscope (vcxt) == 0) ++ { ++ internal_error (_("pop_scope: head of shell_variables not a temporary environment scope")); ++ return; ++ } ++ ++ ret = vcxt->down; ++ if (ret) ++ ret->up = (VAR_CONTEXT *)NULL; ++ ++ shell_variables = ret; ++ ++ /* Now we can take care of merging variables in VCXT into set of scopes ++ whose head is RET (shell_variables). */ ++ FREE (vcxt->name); ++ if (vcxt->table) ++ { ++ if (is_special) ++ hash_flush (vcxt->table, push_func_var); ++ else ++ hash_flush (vcxt->table, push_exported_var); ++ hash_dispose (vcxt->table); ++ } ++ free (vcxt); ++ ++ sv_ifs ("IFS"); /* XXX here for now */ ++} ++ ++/* **************************************************************** */ ++/* */ ++/* Pushing and Popping function contexts */ ++/* */ ++/* **************************************************************** */ ++ ++static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL; ++static int dollar_arg_stack_slots; ++static int dollar_arg_stack_index; ++ ++/* XXX - we might want to consider pushing and popping the `getopts' state ++ when we modify the positional parameters. */ ++void ++push_context (name, is_subshell, tempvars) ++ char *name; /* function name */ ++ int is_subshell; ++ HASH_TABLE *tempvars; ++{ ++ if (is_subshell == 0) ++ push_dollar_vars (); ++ variable_context++; ++ push_var_context (name, VC_FUNCENV, tempvars); ++} ++ ++/* Only called when subshell == 0, so we don't need to check, and can ++ unconditionally pop the dollar vars off the stack. */ ++void ++pop_context () ++{ ++ pop_dollar_vars (); ++ variable_context--; ++ pop_var_context (); ++ ++ sv_ifs ("IFS"); /* XXX here for now */ ++} ++ ++/* Save the existing positional parameters on a stack. */ ++void ++push_dollar_vars () ++{ ++ if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots) ++ { ++ dollar_arg_stack = (WORD_LIST **) ++ xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10) ++ * sizeof (WORD_LIST *)); ++ } ++ dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args (); ++ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; ++} ++ ++/* Restore the positional parameters from our stack. */ ++void ++pop_dollar_vars () ++{ ++ if (!dollar_arg_stack || dollar_arg_stack_index == 0) ++ return; ++ ++ remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1); ++ dispose_words (dollar_arg_stack[dollar_arg_stack_index]); ++ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; ++ set_dollar_vars_unchanged (); ++} ++ ++void ++dispose_saved_dollar_vars () ++{ ++ if (!dollar_arg_stack || dollar_arg_stack_index == 0) ++ return; ++ ++ dispose_words (dollar_arg_stack[dollar_arg_stack_index]); ++ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; ++} ++ ++/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */ ++ ++void ++push_args (list) ++ WORD_LIST *list; ++{ ++#if defined (ARRAY_VARS) && defined (DEBUGGER) ++ SHELL_VAR *bash_argv_v, *bash_argc_v; ++ ARRAY *bash_argv_a, *bash_argc_a; ++ WORD_LIST *l; ++ arrayind_t i; ++ char *t; ++ ++ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a); ++ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a); ++ ++ for (l = list, i = 0; l; l = l->next, i++) ++ array_push (bash_argv_a, l->word->word); ++ ++ t = itos (i); ++ array_push (bash_argc_a, t); ++ free (t); ++#endif /* ARRAY_VARS && DEBUGGER */ ++} ++ ++/* Remove arguments from BASH_ARGV array. Pop top element off BASH_ARGC ++ array and use that value as the count of elements to remove from ++ BASH_ARGV. */ ++void ++pop_args () ++{ ++#if defined (ARRAY_VARS) && defined (DEBUGGER) ++ SHELL_VAR *bash_argv_v, *bash_argc_v; ++ ARRAY *bash_argv_a, *bash_argc_a; ++ ARRAY_ELEMENT *ce; ++ intmax_t i; ++ ++ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a); ++ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a); ++ ++ ce = array_shift (bash_argc_a, 1, 0); ++ if (ce == 0 || legal_number (element_value (ce), &i) == 0) ++ i = 0; ++ ++ for ( ; i > 0; i--) ++ array_pop (bash_argv_a); ++ array_dispose_element (ce); ++#endif /* ARRAY_VARS && DEBUGGER */ ++} ++ ++/************************************************* ++ * * ++ * Functions to manage special variables * ++ * * ++ *************************************************/ ++ ++/* Extern declarations for variables this code has to manage. */ ++extern int eof_encountered, eof_encountered_limit, ignoreeof; ++ ++#if defined (READLINE) ++extern int hostname_list_initialized; ++#endif ++ ++/* An alist of name.function for each special variable. Most of the ++ functions don't do much, and in fact, this would be faster with a ++ switch statement, but by the end of this file, I am sick of switch ++ statements. */ ++ ++#define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0 ++ ++/* This table will be sorted with qsort() the first time it's accessed. */ ++struct name_and_function { ++ char *name; ++ sh_sv_func_t *function; ++}; ++ ++static struct name_and_function special_vars[] = { ++ { "BASH_COMPAT", sv_shcompat }, ++ { "BASH_XTRACEFD", sv_xtracefd }, ++ ++#if defined (JOB_CONTROL) ++ { "CHILD_MAX", sv_childmax }, ++#endif ++ ++#if defined (READLINE) ++# if defined (STRICT_POSIX) ++ { "COLUMNS", sv_winsize }, ++# endif ++ { "COMP_WORDBREAKS", sv_comp_wordbreaks }, ++#endif ++ ++ { "FUNCNEST", sv_funcnest }, ++ ++ { "GLOBIGNORE", sv_globignore }, ++ ++#if defined (HISTORY) ++ { "HISTCONTROL", sv_history_control }, ++ { "HISTFILESIZE", sv_histsize }, ++ { "HISTIGNORE", sv_histignore }, ++ { "HISTSIZE", sv_histsize }, ++ { "HISTTIMEFORMAT", sv_histtimefmt }, ++#endif ++ ++#if defined (__CYGWIN__) ++ { "HOME", sv_home }, ++#endif ++ ++#if defined (READLINE) ++ { "HOSTFILE", sv_hostfile }, ++#endif ++ ++ { "IFS", sv_ifs }, ++ { "IGNOREEOF", sv_ignoreeof }, ++ ++ { "LANG", sv_locale }, ++ { "LC_ALL", sv_locale }, ++ { "LC_COLLATE", sv_locale }, ++ { "LC_CTYPE", sv_locale }, ++ { "LC_MESSAGES", sv_locale }, ++ { "LC_NUMERIC", sv_locale }, ++ { "LC_TIME", sv_locale }, ++ ++#if defined (READLINE) && defined (STRICT_POSIX) ++ { "LINES", sv_winsize }, ++#endif ++ ++ { "MAIL", sv_mail }, ++ { "MAILCHECK", sv_mail }, ++ { "MAILPATH", sv_mail }, ++ ++ { "OPTERR", sv_opterr }, ++ { "OPTIND", sv_optind }, ++ ++ { "PATH", sv_path }, ++ { "POSIXLY_CORRECT", sv_strict_posix }, ++ ++#if defined (READLINE) ++ { "TERM", sv_terminal }, ++ { "TERMCAP", sv_terminal }, ++ { "TERMINFO", sv_terminal }, ++#endif /* READLINE */ ++ ++ { "TEXTDOMAIN", sv_locale }, ++ { "TEXTDOMAINDIR", sv_locale }, ++ ++#if defined (HAVE_TZSET) ++ { "TZ", sv_tz }, ++#endif ++ ++#if defined (HISTORY) && defined (BANG_HISTORY) ++ { "histchars", sv_histchars }, ++#endif /* HISTORY && BANG_HISTORY */ ++ ++ { "ignoreeof", sv_ignoreeof }, ++ ++ { (char *)0, (sh_sv_func_t *)0 } ++}; ++ ++#define N_SPECIAL_VARS (sizeof (special_vars) / sizeof (special_vars[0]) - 1) ++ ++static int ++sv_compare (sv1, sv2) ++ struct name_and_function *sv1, *sv2; ++{ ++ int r; ++ ++ if ((r = sv1->name[0] - sv2->name[0]) == 0) ++ r = strcmp (sv1->name, sv2->name); ++ return r; ++} ++ ++static inline int ++find_special_var (name) ++ const char *name; ++{ ++ register int i, r; ++ ++ for (i = 0; special_vars[i].name; i++) ++ { ++ r = special_vars[i].name[0] - name[0]; ++ if (r == 0) ++ r = strcmp (special_vars[i].name, name); ++ if (r == 0) ++ return i; ++ else if (r > 0) ++ /* Can't match any of rest of elements in sorted list. Take this out ++ if it causes problems in certain environments. */ ++ break; ++ } ++ return -1; ++} ++ ++/* The variable in NAME has just had its state changed. Check to see if it ++ is one of the special ones where something special happens. */ ++void ++stupidly_hack_special_variables (name) ++ char *name; ++{ ++ static int sv_sorted = 0; ++ int i; ++ ++ if (sv_sorted == 0) /* shouldn't need, but it's fairly cheap. */ ++ { ++ qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]), ++ (QSFUNC *)sv_compare); ++ sv_sorted = 1; ++ } ++ ++ i = find_special_var (name); ++ if (i != -1) ++ (*(special_vars[i].function)) (name); ++} ++ ++/* 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. */ ++void ++sv_path (name) ++ char *name; ++{ ++ /* hash -r */ ++ phash_flush (); ++} ++ ++/* What to do just after one of the MAILxxxx variables has changed. NAME ++ is the name of the variable. This is called with NAME set to one of ++ MAIL, MAILCHECK, or MAILPATH. */ ++void ++sv_mail (name) ++ char *name; ++{ ++ /* If the time interval for checking the files has changed, then ++ reset the mail timer. Otherwise, one of the pathname vars ++ to the users mailbox has changed, so rebuild the array of ++ filenames. */ ++ if (name[4] == 'C') /* if (strcmp (name, "MAILCHECK") == 0) */ ++ reset_mail_timer (); ++ else ++ { ++ free_mail_files (); ++ remember_mail_dates (); ++ } ++} ++ ++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; ++{ ++ 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. */ ++void ++sv_terminal (name) ++ char *name; ++{ ++ if (interactive_shell && no_line_editing == 0) ++ rl_reset_terminal (get_string_value ("TERM")); ++} ++ ++void ++sv_hostfile (name) ++ char *name; ++{ ++ SHELL_VAR *v; ++ ++ v = find_variable (name); ++ if (v == 0) ++ clear_hostname_list (); ++ else ++ hostname_list_initialized = 0; ++} ++ ++#if defined (STRICT_POSIX) ++/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values ++ found in the initial environment) to override the terminal size reported by ++ the kernel. */ ++void ++sv_winsize (name) ++ char *name; ++{ ++ SHELL_VAR *v; ++ intmax_t xd; ++ int d; ++ ++ if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing) ++ return; ++ ++ v = find_variable (name); ++ if (v == 0 || var_isnull (v)) ++ rl_reset_screen_size (); ++ else ++ { ++ if (legal_number (value_cell (v), &xd) == 0) ++ return; ++ winsize_assignment = 1; ++ d = xd; /* truncate */ ++ if (name[0] == 'L') /* LINES */ ++ rl_set_screen_size (d, -1); ++ else /* COLUMNS */ ++ rl_set_screen_size (-1, d); ++ winsize_assignment = 0; ++ } ++} ++#endif /* STRICT_POSIX */ ++#endif /* READLINE */ ++ ++/* Update the value of HOME in the export environment so tilde expansion will ++ work on cygwin. */ ++#if defined (__CYGWIN__) ++sv_home (name) ++ char *name; ++{ ++ array_needs_making = 1; ++ maybe_make_export_env (); ++} ++#endif ++ ++#if defined (HISTORY) ++/* What to do after the HISTSIZE or HISTFILESIZE variables change. ++ If there is a value for this HISTSIZE (and it is numeric), then stifle ++ the history. Otherwise, if there is NO value for this variable, ++ unstifle the history. If name is HISTFILESIZE, and its value is ++ numeric, truncate the history file to hold no more than that many ++ lines. */ ++void ++sv_histsize (name) ++ char *name; ++{ ++ char *temp; ++ intmax_t num; ++ int hmax; ++ ++ temp = get_string_value (name); ++ ++ if (temp && *temp) ++ { ++ if (legal_number (temp, &num)) ++ { ++ hmax = num; ++ if (hmax < 0 && name[4] == 'S') ++ unstifle_history (); /* unstifle history if HISTSIZE < 0 */ ++ else if (name[4] == 'S') ++ { ++ stifle_history (hmax); ++ hmax = where_history (); ++ if (history_lines_this_session > hmax) ++ history_lines_this_session = hmax; ++ } ++ else if (hmax >= 0) /* truncate HISTFILE if HISTFILESIZE >= 0 */ ++ { ++ history_truncate_file (get_string_value ("HISTFILE"), hmax); ++ if (hmax <= history_lines_in_file) ++ history_lines_in_file = hmax; ++ } ++ } ++ } ++ else if (name[4] == 'S') ++ unstifle_history (); ++} ++ ++/* What to do after the HISTIGNORE variable changes. */ ++void ++sv_histignore (name) ++ char *name; ++{ ++ setup_history_ignore (name); ++} ++ ++/* What to do after the HISTCONTROL variable changes. */ ++void ++sv_history_control (name) ++ char *name; ++{ ++ char *temp; ++ char *val; ++ int tptr; ++ ++ history_control = 0; ++ temp = get_string_value (name); ++ ++ if (temp == 0 || *temp == 0) ++ return; ++ ++ tptr = 0; ++ while (val = extract_colon_unit (temp, &tptr)) ++ { ++ if (STREQ (val, "ignorespace")) ++ history_control |= HC_IGNSPACE; ++ else if (STREQ (val, "ignoredups")) ++ history_control |= HC_IGNDUPS; ++ else if (STREQ (val, "ignoreboth")) ++ history_control |= HC_IGNBOTH; ++ else if (STREQ (val, "erasedups")) ++ history_control |= HC_ERASEDUPS; ++ ++ free (val); ++ } ++} ++ ++#if defined (BANG_HISTORY) ++/* Setting/unsetting of the history expansion character. */ ++void ++sv_histchars (name) ++ char *name; ++{ ++ char *temp; ++ ++ temp = get_string_value (name); ++ if (temp) ++ { ++ history_expansion_char = *temp; ++ if (temp[0] && temp[1]) ++ { ++ history_subst_char = temp[1]; ++ if (temp[2]) ++ history_comment_char = temp[2]; ++ } ++ } ++ else ++ { ++ history_expansion_char = '!'; ++ history_subst_char = '^'; ++ history_comment_char = '#'; ++ } ++} ++#endif /* BANG_HISTORY */ ++ ++void ++sv_histtimefmt (name) ++ char *name; ++{ ++ SHELL_VAR *v; ++ ++ if (v = find_variable (name)) ++ { ++ if (history_comment_char == 0) ++ history_comment_char = '#'; ++ } ++ history_write_timestamps = (v != 0); ++} ++#endif /* HISTORY */ ++ ++#if defined (HAVE_TZSET) ++void ++sv_tz (name) ++ char *name; ++{ ++ if (chkexport (name)) ++ tzset (); ++} ++#endif ++ ++/* If the variable exists, then the value of it can be the number ++ of times we actually ignore the EOF. The default is small, ++ (smaller than csh, anyway). */ ++void ++sv_ignoreeof (name) ++ char *name; ++{ ++ SHELL_VAR *tmp_var; ++ char *temp; ++ ++ eof_encountered = 0; ++ ++ tmp_var = find_variable (name); ++ ignoreeof = tmp_var != 0; ++ temp = tmp_var ? value_cell (tmp_var) : (char *)NULL; ++ if (temp) ++ eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10; ++ set_shellopts (); /* make sure `ignoreeof' is/is not in $SHELLOPTS */ ++} ++ ++void ++sv_optind (name) ++ char *name; ++{ ++ char *tt; ++ int s; ++ ++ tt = get_string_value ("OPTIND"); ++ if (tt && *tt) ++ { ++ s = atoi (tt); ++ ++ /* According to POSIX, setting OPTIND=1 resets the internal state ++ of getopt (). */ ++ if (s < 0 || s == 1) ++ s = 0; ++ } ++ else ++ s = 0; ++ getopts_reset (s); ++} ++ ++void ++sv_opterr (name) ++ char *name; ++{ ++ char *tt; ++ ++ tt = get_string_value ("OPTERR"); ++ sh_opterr = (tt && *tt) ? atoi (tt) : 1; ++} ++ ++void ++sv_strict_posix (name) ++ char *name; ++{ ++ SET_INT_VAR (name, posixly_correct); ++ posix_initialize (posixly_correct); ++#if defined (READLINE) ++ if (interactive_shell) ++ posix_readline_initialize (posixly_correct); ++#endif /* READLINE */ ++ set_shellopts (); /* make sure `posix' is/is not in $SHELLOPTS */ ++} ++ ++void ++sv_locale (name) ++ char *name; ++{ ++ char *v; ++ int r; ++ ++ v = get_string_value (name); ++ if (name[0] == 'L' && name[1] == 'A') /* LANG */ ++ r = set_lang (name, v); ++ else ++ r = set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */ ++ ++#if 1 ++ if (r == 0 && posixly_correct) ++ last_command_exit_value = 1; ++#endif ++} ++ ++#if defined (ARRAY_VARS) ++void ++set_pipestatus_array (ps, nproc) ++ int *ps; ++ int nproc; ++{ ++ SHELL_VAR *v; ++ ARRAY *a; ++ ARRAY_ELEMENT *ae; ++ register int i; ++ char *t, tbuf[INT_STRLEN_BOUND(int) + 1]; ++ ++ v = find_variable ("PIPESTATUS"); ++ if (v == 0) ++ v = make_new_array_variable ("PIPESTATUS"); ++ if (array_p (v) == 0) ++ return; /* Do nothing if not an array variable. */ ++ a = array_cell (v); ++ ++ if (a == 0 || array_num_elements (a) == 0) ++ { ++ for (i = 0; i < nproc; i++) /* was ps[i] != -1, not i < nproc */ ++ { ++ t = inttostr (ps[i], tbuf, sizeof (tbuf)); ++ array_insert (a, i, t); ++ } ++ return; ++ } ++ ++ /* Fast case */ ++ if (array_num_elements (a) == nproc && nproc == 1) ++ { ++ ae = element_forw (a->head); ++ free (element_value (ae)); ++ ae->value = itos (ps[0]); ++ } ++ else if (array_num_elements (a) <= nproc) ++ { ++ /* modify in array_num_elements members in place, then add */ ++ ae = a->head; ++ for (i = 0; i < array_num_elements (a); i++) ++ { ++ ae = element_forw (ae); ++ free (element_value (ae)); ++ ae->value = itos (ps[i]); ++ } ++ /* add any more */ ++ for ( ; i < nproc; i++) ++ { ++ t = inttostr (ps[i], tbuf, sizeof (tbuf)); ++ array_insert (a, i, t); ++ } ++ } ++ else ++ { ++ /* deleting elements. it's faster to rebuild the array. */ ++ array_flush (a); ++ for (i = 0; ps[i] != -1; i++) ++ { ++ t = inttostr (ps[i], tbuf, sizeof (tbuf)); ++ array_insert (a, i, t); ++ } ++ } ++} ++ ++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 ++ ++void ++set_pipestatus_from_exit (s) ++ int s; ++{ ++#if defined (ARRAY_VARS) ++ static int v[2] = { 0, -1 }; ++ ++ v[0] = s; ++ set_pipestatus_array (v, 1); ++#endif ++} ++ ++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)); ++ } ++} ++ ++#define MIN_COMPAT_LEVEL 31 ++ ++void ++sv_shcompat (name) ++ char *name; ++{ ++ SHELL_VAR *v; ++ char *val; ++ int tens, ones, compatval; ++ ++ v = find_variable (name); ++ if (v == 0) ++ { ++ shell_compatibility_level = DEFAULT_COMPAT_LEVEL; ++ set_compatibility_opts (); ++ return; ++ } ++ val = value_cell (v); ++ if (val == 0 || *val == '\0') ++ { ++ shell_compatibility_level = DEFAULT_COMPAT_LEVEL; ++ set_compatibility_opts (); ++ return; ++ } ++ /* Handle decimal-like compatibility version specifications: 4.2 */ ++ if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0) ++ { ++ tens = val[0] - '0'; ++ ones = val[2] - '0'; ++ compatval = tens*10 + ones; ++ } ++ /* Handle integer-like compatibility version specifications: 42 */ ++ else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0) ++ { ++ tens = val[0] - '0'; ++ ones = val[1] - '0'; ++ compatval = tens*10 + ones; ++ } ++ else ++ { ++compat_error: ++ internal_error (_("%s: %s: compatibility value out of range"), name, val); ++ shell_compatibility_level = DEFAULT_COMPAT_LEVEL; ++ set_compatibility_opts (); ++ return; ++ } ++ ++ if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL) ++ goto compat_error; ++ ++ shell_compatibility_level = compatval; ++ set_compatibility_opts (); ++} ++ ++#if defined (JOB_CONTROL) ++void ++sv_childmax (name) ++ char *name; ++{ ++ char *tt; ++ int s; ++ ++ tt = get_string_value (name); ++ s = (tt && *tt) ? atoi (tt) : 0; ++ set_maxchild (s); ++} ++#endif diff --git a/ptxdist/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch b/ptxdist/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch new file mode 100644 index 0000000..801b4a6 --- /dev/null +++ b/ptxdist/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch @@ -0,0 +1,5409 @@ +From: Chet Ramey +Date: Thu, 15 Jan 2015 10:20:45 -0500 +Subject: [PATCH] Bash-4.3 patch 32 + +--- + jobs.c | 4 +- + patchlevel.h | 2 +- + variables.c.orig | 5365 ------------------------------------------------------ + 3 files changed, 4 insertions(+), 5367 deletions(-) + delete mode 100644 variables.c.orig + +diff --git a/jobs.c b/jobs.c +index f38b0c3f4446..b6e59eba0de8 100644 +--- a/jobs.c ++++ b/jobs.c +@@ -3339,7 +3339,9 @@ itrace("waitchld: waitpid returns %d block = %d", pid, block); + if (posixly_correct && this_shell_builtin && this_shell_builtin == wait_builtin) + { + interrupt_immediately = 0; +- trap_handler (SIGCHLD); /* set pending_traps[SIGCHLD] */ ++ /* This was trap_handler (SIGCHLD) but that can lose traps if ++ children_exited > 1 */ ++ queue_sigchld_trap (children_exited); + wait_signal_received = SIGCHLD; + /* If we're in a signal handler, let CHECK_WAIT_INTR pick it up; + run_pending_traps will call run_sigchld_trap later */ +diff --git a/patchlevel.h b/patchlevel.h +index 0ad46aafbdd9..b8bf38704ed2 100644 +--- a/patchlevel.h ++++ b/patchlevel.h +@@ -25,6 +25,6 @@ + regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh + looks for to find the patch level (for the sccs version string). */ + +-#define PATCHLEVEL 31 ++#define PATCHLEVEL 32 + + #endif /* _PATCHLEVEL_H_ */ +diff --git a/variables.c.orig b/variables.c.orig +deleted file mode 100644 +index 7c82710e0f0b..000000000000 +--- a/variables.c.orig ++++ /dev/null +@@ -1,5365 +0,0 @@ +-/* variables.c -- Functions for hacking shell variables. */ +- +-/* Copyright (C) 1987-2013 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 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. +- +- You should have received a copy of the GNU General Public License +- along with Bash. If not, see . +-*/ +- +-#include "config.h" +- +-#include "bashtypes.h" +-#include "posixstat.h" +-#include "posixtime.h" +- +-#if defined (__QNX__) +-# if defined (__QNXNTO__) +-# include +-# else +-# include +-# endif /* !__QNXNTO__ */ +-#endif /* __QNX__ */ +- +-#if defined (HAVE_UNISTD_H) +-# include +-#endif +- +-#include +-#include "chartypes.h" +-#if defined (HAVE_PWD_H) +-# include +-#endif +-#include "bashansi.h" +-#include "bashintl.h" +- +-#define NEED_XTRACE_SET_DECL +- +-#include "shell.h" +-#include "flags.h" +-#include "execute_cmd.h" +-#include "findcmd.h" +-#include "mailcheck.h" +-#include "input.h" +-#include "hashcmd.h" +-#include "pathexp.h" +-#include "alias.h" +-#include "jobs.h" +- +-#include "version.h" +- +-#include "builtins/getopt.h" +-#include "builtins/common.h" +-#include "builtins/builtext.h" +- +-#if defined (READLINE) +-# include "bashline.h" +-# include +-#else +-# include +-#endif +- +-#if defined (HISTORY) +-# include "bashhist.h" +-# include +-#endif /* HISTORY */ +- +-#if defined (PROGRAMMABLE_COMPLETION) +-# include "pcomplete.h" +-#endif +- +-#define TEMPENV_HASH_BUCKETS 4 /* must be power of two */ +- +-#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0') +- +-#define BASHFUNC_PREFIX "BASH_FUNC_" +-#define BASHFUNC_PREFLEN 10 /* == strlen(BASHFUNC_PREFIX */ +-#define BASHFUNC_SUFFIX "%%" +-#define BASHFUNC_SUFFLEN 2 /* == strlen(BASHFUNC_SUFFIX) */ +- +-extern char **environ; +- +-/* Variables used here and defined in other files. */ +-extern int posixly_correct; +-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 int last_command_exit_value; +-extern char *dist_version, *release_status; +-extern char *shell_name; +-extern char *primary_prompt, *secondary_prompt; +-extern char *current_host_name; +-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; +- +-#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 set of shell assignments which are made only in the environment +- for a single command. */ +-HASH_TABLE *temporary_env = (HASH_TABLE *)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. */ +-char *dollar_vars[10]; +-WORD_LIST *rest_of_args = (WORD_LIST *)NULL; +- +-/* The value of $$. */ +-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 +- shell variables that are marked for export. */ +-char **export_env = (char **)NULL; +-static int export_env_index; +-static int export_env_size; +- +-#if defined (READLINE) +-static int winsize_assignment; /* currently assigning to LINES or COLUMNS */ +-#endif +- +-static HASH_TABLE *last_table_searched; /* hash_lookup sets this */ +- +-/* 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 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 *)); +- +-#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 *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *)); +-static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **)); +-static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **)); +- +-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 *, int)); +-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 +- parse $SHELLOPTS. */ +-void +-initialize_shell_variables (env, privmode) +- char **env; +- int privmode; +-{ +- char *name, *string, *temp_string; +- int c, char_index, string_index, string_length, ro; +- SHELL_VAR *temp_var; +- +- create_variable_tables (); +- +- for (string_index = 0; string = env[string_index++]; ) +- { +- char_index = 0; +- name = string; +- while ((c = *string++) && c != '=') +- ; +- if (string[-1] == '=') +- char_index = string - name - 1; +- +- /* If there are weird things in the environment, like `=xxx' or a +- string without an `=', just skip them. */ +- if (char_index == 0) +- continue; +- +- /* ASSERT(name[char_index] == '=') */ +- name[char_index] = '\0'; +- /* Now, name = env variable name, string = env variable value, and +- char_index == strlen (name) */ +- +- temp_var = (SHELL_VAR *)NULL; +- +- /* 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 (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) && +- STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) && +- STREQN ("() {", string, 4)) +- { +- size_t namelen; +- char *tname; /* desired imported function name */ +- +- namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN; +- +- tname = name + BASHFUNC_PREFLEN; /* start of func name */ +- tname[namelen] = '\0'; /* now tname == func name */ +- +- string_length = strlen (string); +- temp_string = (char *)xmalloc (namelen + string_length + 2); +- +- memcpy (temp_string, tname, namelen); +- temp_string[namelen] = ' '; +- memcpy (temp_string + namelen + 1, string, string_length + 1); +- +- /* Don't import function names that are invalid identifiers from the +- environment, though we still allow them to be defined as shell +- variables. */ +- if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname))) +- parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD); +- +- if (temp_var = find_function (tname)) +- { +- VSETATTR (temp_var, (att_exported|att_imported)); +- array_needs_making = 1; +- } +- else +- { +- if (temp_var = bind_variable (name, string, 0)) +- { +- VSETATTR (temp_var, (att_exported | att_imported | att_invisible)); +- array_needs_making = 1; +- } +- last_command_exit_value = 1; +- report_error (_("error importing function definition for `%s'"), tname); +- } +- +- /* Restore original suffix */ +- tname[namelen] = BASHFUNC_SUFFIX[0]; +- } +-#if defined (ARRAY_VARS) +-# if ARRAY_EXPORT +- /* Array variables may not yet be exported. */ +- else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')') +- { +- string_length = 1; +- temp_string = extract_array_assignment_list (string, &string_length); +- temp_var = assign_array_from_string (name, temp_string); +- FREE (temp_string); +- VSETATTR (temp_var, (att_exported | att_imported)); +- array_needs_making = 1; +- } +-# endif /* ARRAY_EXPORT */ +-#endif +-#if 0 +- else if (legal_identifier (name)) +-#else +- else +-#endif +- { +- ro = 0; +- if (posixly_correct && STREQ (name, "SHELLOPTS")) +- { +- temp_var = find_variable ("SHELLOPTS"); +- ro = temp_var && readonly_p (temp_var); +- if (temp_var) +- VUNSETATTR (temp_var, att_readonly); +- } +- 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)); +- if (ro) +- VSETATTR (temp_var, att_readonly); +- 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). */ +- if (temp_var && function_p (temp_var) == 0) /* XXX not yet */ +- { +- CACHE_IMPORTSTR (temp_var, name); +- } +- } +- +- set_pwd (); +- +- /* Set up initial value of $_ */ +- temp_var = set_if_not ("_", dollar_vars[0]); +- +- /* Remember this pid. */ +- 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); +-#if 0 +- set_auto_export (temp_var); /* XXX */ +-#endif +- +- temp_var = set_if_not ("TERM", "dumb"); +-#if 0 +- set_auto_export (temp_var); /* XXX */ +-#endif +- +-#if defined (__QNX__) +- /* set node id -- don't import it from the environment */ +- { +- char node_name[22]; +-# if defined (__QNXNTO__) +- netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name)); +-# else +- qnx_nidtostr (getnid (), node_name, sizeof (node_name)); +-# endif +- temp_var = bind_variable ("NODE", node_name, 0); +- set_auto_export (temp_var); +- } +-#endif +- +- /* set up the prompts. */ +- if (interactive_shell) +- { +-#if defined (PROMPT_STRING_DECODE) +- set_if_not ("PS1", primary_prompt); +-#else +- if (current_user.uid == -1) +- get_current_user_info (); +- set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt); +-#endif +- set_if_not ("PS2", secondary_prompt); +- } +- set_if_not ("PS4", "+ "); +- +- /* Don't allow IFS to be imported from the environment. */ +- temp_var = bind_variable ("IFS", " \t\n", 0); +- setifs (temp_var); +- +- /* Magic machine types. Pretty convenient. */ +- 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 MAILPATH is not set, and we should provide a +- default only if neither is set. */ +- if (interactive_shell) +- { +- 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. */ +- temp_var = bind_variable ("OPTIND", "1", 0); +- VSETATTR (temp_var, att_integer); +- getopts_reset (0); +- bind_variable ("OPTERR", "1", 0); +- sh_opterr = 1; +- +- if (login_shell == 1 && 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, 0); +- free (name); +- +- /* Make the exported environment variable SHELL be the user's login +- shell. Note that the `tset' command looks at this variable +- to determine what style of commands to output; if it ends in "csh", +- then C-shell commands are output, else Bourne shell commands. */ +- set_shell_var (); +- +- /* Make a variable called BASH_VERSION which contains the version info. */ +- 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"); +- if (!temp_var) +- temp_var = find_variable ("POSIX_PEDANTIC"); +- if (temp_var && imported_p (temp_var)) +- sv_strict_posix (temp_var->name); +- +-#if defined (HISTORY) +- /* Set history variables to defaults, and then do whatever we would +- do if the variable had just been set. Do this only in the case +- that we are remembering commands on the history list. */ +- if (remember_on_history) +- { +- name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0); +- +- set_if_not ("HISTFILE", name); +- free (name); +- } +-#endif /* HISTORY */ +- +- /* Seed the random number generator. */ +- seedrand (); +- +- /* Handle some "special" variables that we may have inherited from a +- parent shell. */ +- if (interactive_shell) +- { +- temp_var = find_variable ("IGNOREEOF"); +- if (!temp_var) +- temp_var = find_variable ("ignoreeof"); +- if (temp_var && imported_p (temp_var)) +- sv_ignoreeof (temp_var->name); +- } +- +-#if defined (HISTORY) +- if (interactive_shell && remember_on_history) +- { +- sv_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 * +-sh_get_home_dir () +-{ +- if (current_user.home_dir == 0) +- get_current_user_info (); +- return current_user.home_dir; +-} +- +-static void +-set_home_var () +-{ +- SHELL_VAR *temp_var; +- +- temp_var = find_variable ("HOME"); +- if (temp_var == 0) +- temp_var = bind_variable ("HOME", sh_get_home_dir (), 0); +-#if 0 +- VSETATTR (temp_var, att_exported); +-#endif +-} +- +-/* Set $SHELL to the user's login shell if it is not already set. Call +- get_current_user_info if we haven't already fetched the shell. */ +-static void +-set_shell_var () +-{ +- SHELL_VAR *temp_var; +- +- temp_var = find_variable ("SHELL"); +- if (temp_var == 0) +- { +- if (current_user.shell == 0) +- get_current_user_info (); +- temp_var = bind_variable ("SHELL", current_user.shell, 0); +- } +-#if 0 +- VSETATTR (temp_var, att_exported); +-#endif +-} +- +-static char * +-get_bash_name () +-{ +- char *name; +- +- if ((login_shell == 1) && RELPATH(shell_name)) +- { +- if (current_user.shell == 0) +- get_current_user_info (); +- name = savestring (current_user.shell); +- } +- else if (ABSPATH(shell_name)) +- name = savestring (shell_name); +- else if (shell_name[0] == '.' && shell_name[1] == '/') +- { +- /* Fast path for common case. */ +- char *cdir; +- int len; +- +- cdir = get_string_value ("PWD"); +- if (cdir) +- { +- len = strlen (cdir); +- name = (char *)xmalloc (len + strlen (shell_name) + 1); +- strcpy (name, cdir); +- strcpy (name + len, shell_name + 1); +- } +- else +- name = savestring (shell_name); +- } +- else +- { +- char *tname; +- int s; +- +- tname = find_user_command (shell_name); +- +- if (tname == 0) +- { +- /* Try the current directory. If there is not an executable +- there, just punt and use the login shell. */ +- s = file_status (shell_name); +- if (s & FS_EXECABLE) +- { +- tname = make_absolute (shell_name, get_string_value ("PWD")); +- if (*shell_name == '.') +- { +- name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS); +- if (name == 0) +- name = tname; +- else +- free (tname); +- } +- else +- name = tname; +- } +- else +- { +- if (current_user.shell == 0) +- get_current_user_info (); +- name = savestring (current_user.shell); +- } +- } +- else +- { +- name = full_pathname (tname); +- free (tname); +- } +- } +- +- return (name); +-} +- +-void +-adjust_shell_level (change) +- int change; +-{ +- char new_level[5], *old_SHLVL; +- intmax_t old_level; +- SHELL_VAR *temp_var; +- +- old_SHLVL = get_string_value ("SHLVL"); +- 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); +- shell_level = 1; +- } +- +- /* We don't need the full generality of itos here. */ +- if (shell_level < 10) +- { +- new_level[0] = shell_level + '0'; +- new_level[1] = '\0'; +- } +- else if (shell_level < 100) +- { +- new_level[0] = (shell_level / 10) + '0'; +- new_level[1] = (shell_level % 10) + '0'; +- new_level[2] = '\0'; +- } +- else if (shell_level < 1000) +- { +- new_level[0] = (shell_level / 100) + '0'; +- old_level = shell_level % 100; +- new_level[1] = (old_level / 10) + '0'; +- new_level[2] = (old_level % 10) + '0'; +- new_level[3] = '\0'; +- } +- +- temp_var = bind_variable ("SHLVL", new_level, 0); +- set_auto_export (temp_var); +-} +- +-static void +-initialize_shell_level () +-{ +- 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[INT_STRLEN_BOUND(pid_t) + 1], *name; +- SHELL_VAR *temp_var; +- +- name = inttostr (getppid (), namebuf, sizeof(namebuf)); +- temp_var = find_variable ("PPID"); +- if (temp_var) +- VUNSETATTR (temp_var, (att_readonly | att_exported)); +- temp_var = bind_variable ("PPID", name, 0); +- VSETATTR (temp_var, (att_readonly | att_integer)); +-} +- +-static void +-uidset () +-{ +- char buff[INT_STRLEN_BOUND(uid_t) + 1], *b; +- register SHELL_VAR *v; +- +- b = inttostr (current_user.uid, buff, sizeof (buff)); +- v = find_variable ("UID"); +- if (v == 0) +- { +- v = bind_variable ("UID", b, 0); +- VSETATTR (v, (att_readonly | att_integer)); +- } +- +- if (current_user.euid != current_user.uid) +- b = inttostr (current_user.euid, buff, sizeof (buff)); +- +- v = find_variable ("EUID"); +- if (v == 0) +- { +- v = bind_variable ("EUID", b, 0); +- VSETATTR (v, (att_readonly | att_integer)); +- } +-} +- +-#if defined (ARRAY_VARS) +-static void +-make_vers_array () +-{ +- SHELL_VAR *vv; +- ARRAY *av; +- char *s, d[32], b[INT_STRLEN_BOUND(int) + 1]; +- +- unbind_variable ("BASH_VERSINFO"); +- +- vv = make_new_array_variable ("BASH_VERSINFO"); +- av = array_cell (vv); +- strcpy (d, dist_version); +- s = strchr (d, '.'); +- if (s) +- *s++ = '\0'; +- array_insert (av, 0, d); +- array_insert (av, 1, s); +- s = inttostr (patch_level, b, sizeof (b)); +- array_insert (av, 2, s); +- s = inttostr (build_version, b, sizeof (b)); +- array_insert (av, 3, s); +- array_insert (av, 4, release_status); +- array_insert (av, 5, MACHTYPE); +- +- VSETATTR (vv, att_readonly); +-} +-#endif /* ARRAY_VARS */ +- +-/* Set the environment variables $LINES and $COLUMNS in response to +- a window size change. */ +-void +-sh_set_lines_and_columns (lines, cols) +- int lines, cols; +-{ +- 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, 0); +- +- v = inttostr (cols, val, sizeof (val)); +- bind_variable ("COLUMNS", v, 0); +-} +- +-/* **************************************************************** */ +-/* */ +-/* Printing variables and values */ +-/* */ +-/* **************************************************************** */ +- +-/* Print LIST (a list of shell variables) to stdout in such a way that +- they can be read back in. */ +-void +-print_var_list (list) +- register SHELL_VAR **list; +-{ +- register int i; +- register SHELL_VAR *var; +- +- for (i = 0; list && (var = list[i]); i++) +- if (invisible_p (var) == 0) +- print_assignment (var); +-} +- +-/* Print LIST (a list of shell functions) to stdout in such a way that +- they can be read back in. */ +-void +-print_func_list (list) +- register SHELL_VAR **list; +-{ +- register int i; +- register SHELL_VAR *var; +- +- for (i = 0; list && (var = list[i]); i++) +- { +- printf ("%s ", var->name); +- print_var_function (var); +- printf ("\n"); +- } +-} +- +-/* 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 (var_isset (var) == 0) +- return; +- +- if (function_p (var)) +- { +- printf ("%s", var->name); +- print_var_function (var); +- printf ("\n"); +- } +-#if defined (ARRAY_VARS) +- else if (array_p (var)) +- print_array_assignment (var, 0); +- else if (assoc_p (var)) +- print_assoc_assignment (var, 0); +-#endif /* ARRAY_VARS */ +- else +- { +- printf ("%s=", var->name); +- print_var_value (var, 1); +- printf ("\n"); +- } +-} +- +-/* Print the value cell of VAR, a shell variable. Do not print +- the name, nor leading/trailing newline. If QUOTE is non-zero, +- and the value contains shell metacharacters, quote the value +- in such a way that it can be read back in. */ +-void +-print_var_value (var, quote) +- SHELL_VAR *var; +- int quote; +-{ +- char *t; +- +- if (var_isset (var) == 0) +- return; +- +- if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var))) +- { +- t = ansic_quote (value_cell (var), 0, (int *)0); +- printf ("%s", t); +- free (t); +- } +- else if (quote && sh_contains_shell_metas (value_cell (var))) +- { +- t = sh_single_quote (value_cell (var)); +- printf ("%s", t); +- free (t); +- } +- else +- printf ("%s", value_cell (var)); +-} +- +-/* Print the function cell of VAR, a shell variable. Do not +- print the name, nor leading/trailing newline. */ +-void +-print_var_function (var) +- SHELL_VAR *var; +-{ +- char *x; +- +- if (function_p (var) && var_isset (var)) +- { +- x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL); +- printf ("%s", x); +- } +-} +- +-/* **************************************************************** */ +-/* */ +-/* Dynamic Variables */ +-/* */ +-/* **************************************************************** */ +- +-/* DYNAMIC VARIABLES +- +- These are variables whose values are generated anew each time they are +- referenced. These are implemented using a pair of function pointers +- in the struct variable: assign_func, which is called from bind_variable +- and, 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). IND is an index for an array variable, and +- unused otherwise. +- +- dynamic_value is called from find_variable_internal to return a `new' +- value for the specified dynamic varible. If this function is NULL, +- the variable is treated as a `normal' shell variable. If it is not, +- however, then this function is called like this: +- tempvar = (*(var->dynamic_value)) (var); +- +- Sometimes `tempvar' will replace the value of `var'. Other times, the +- shell will simply use the string value. Pretty object-oriented, huh? +- +- Be warned, though: if you `unset' a special variable, it loses its +- special meaning, even if you subsequently set it. +- +- The special assignment code would probably have been better put in +- subst.c: do_assignment_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, 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, value, ind, key) +- SHELL_VAR *self; +- 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. Initialize 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 intmax_t seconds_value_assigned; +- +-static SHELL_VAR * +-assign_seconds (self, value, unused, key) +- SHELL_VAR *self; +- char *value; +- arrayind_t unused; +- char *key; +-{ +- if (legal_number (value, &seconds_value_assigned) == 0) +- seconds_value_assigned = 0; +- shell_start_time = NOW; +- return (self); +-} +- +-static SHELL_VAR * +-get_seconds (var) +- SHELL_VAR *var; +-{ +- time_t time_since_start; +- char *p; +- +- time_since_start = NOW - shell_start_time; +- p = itos(seconds_value_assigned + time_since_start); +- +- FREE (value_cell (var)); +- +- VSETATTR (var, att_integer); +- var_setvalue (var, p); +- return (var); +-} +- +-static SHELL_VAR * +-init_seconds_var () +-{ +- SHELL_VAR *v; +- +- v = find_variable ("SECONDS"); +- if (v) +- { +- if (legal_number (value_cell(v), &seconds_value_assigned) == 0) +- seconds_value_assigned = 0; +- } +- INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds); +- return v; +-} +- +-/* The random number seed. You can change this by setting RANDOM. */ +-static unsigned long rseed = 1; +-static int last_random_value; +-static int seeded_subshell = 0; +- +-/* 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 () +-{ +- /* 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) +- 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, unused, key) +- SHELL_VAR *self; +- char *value; +- arrayind_t unused; +- char *key; +-{ +- sbrand (strtoul (value, (char **)NULL, 10)); +- if (subshell_environment) +- seeded_subshell = getpid (); +- return (self); +-} +- +-int +-get_random_number () +-{ +- int rv, pid; +- +- /* Reset for command and process substitution. */ +- pid = getpid (); +- if (subshell_environment && seeded_subshell != pid) +- { +- seedrand (); +- seeded_subshell = pid; +- } +- +- do +- rv = brand (); +- 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 (rv); +- +- FREE (value_cell (var)); +- +- VSETATTR (var, att_integer); +- 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) +- SHELL_VAR *var; +-{ +- char *p; +- int ln; +- +- ln = executing_line_number (); +- p = itos (ln); +- FREE (value_cell (var)); +- var_setvalue (var, p); +- return (var); +-} +- +-static SHELL_VAR * +-assign_subshell (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; +- 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) +- SHELL_VAR *var; +-{ +- char *p; +- +- p = itos (history_number ()); +- FREE (value_cell (var)); +- var_setvalue (var, p); +- return (var); +-} +-#endif +- +-#if defined (READLINE) +-/* When this function returns, VAR->value points to malloced memory. */ +-static SHELL_VAR * +-get_comp_wordbreaks (var) +- SHELL_VAR *var; +-{ +- /* 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; +-{ +- if (rl_completer_word_break_characters && +- rl_completer_word_break_characters != rl_basic_word_break_characters) +- free (rl_completer_word_break_characters); +- +- rl_completer_word_break_characters = savestring (value); +- return self; +-} +-#endif /* READLINE */ +- +-#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) +-static SHELL_VAR * +-assign_dirstack (self, value, ind, key) +- SHELL_VAR *self; +- 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) +-/* We don't want to initialize the group set with a call to getgroups() +- unless we're asked to, but we only want to do it once. */ +-static SHELL_VAR * +-get_groupset (self) +- SHELL_VAR *self; +-{ +- register int i; +- int ng; +- ARRAY *a; +- static char **group_set = (char **)NULL; +- +- if (group_set == 0) +- { +- group_set = get_group_list (&ng); +- a = array_cell (self); +- for (i = 0; i < ng; i++) +- array_insert (a, i, group_set[i]); +- } +- return (self); +-} +- +-static SHELL_VAR * +-build_hashcmd (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 (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0) +- { +- var_setvalue (self, (char *)NULL); +- return self; +- } +- +- 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) +- return; +- +- if (on_or_off) +- VUNSETATTR (v, att_invisible); +- else +- VSETATTR (v, att_invisible); +-} +- +-static SHELL_VAR * +-init_funcname_var () +-{ +- SHELL_VAR *v; +- +- v = find_variable ("FUNCNAME"); +- if (v) +- return v; +-#if defined (ARRAY_VARS) +- INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign); +-#else +- INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign); +-#endif +- VSETATTR (v, att_invisible|att_noassign); +- return v; +-} +- +-static void +-initialize_dynamic_variables () +-{ +- SHELL_VAR *v; +- +- v = 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, (sh_var_assign_func_t *)NULL); +- VSETATTR (v, att_integer); +-#endif +- +-#if defined (READLINE) +- INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks); +-#endif +- +-#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) +- v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0); +-#endif /* PUSHD_AND_POPD && ARRAY_VARS */ +- +-#if defined (ARRAY_VARS) +- v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign); +- +-# if defined (DEBUGGER) +- v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset); +- v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset); +-# endif /* DEBUGGER */ +- v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset); +- v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset); +- +- 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 +- +- 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). */ +- +-static SHELL_VAR * +-hash_lookup (name, hashed_vars) +- const char *name; +- HASH_TABLE *hashed_vars; +-{ +- BUCKET_CONTENTS *bucket; +- +- bucket = hash_search (name, hashed_vars, 0); +- /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that +- table. */ +- if (bucket) +- last_table_searched = hashed_vars; +- 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. +- The lookup order is: +- temporary_env +- shell_variables list +-*/ +- +-SHELL_VAR * +-find_variable_internal (name, force_tempenv) +- const char *name; +- int force_tempenv; +-{ +- SHELL_VAR *var; +- int search_tempenv; +- VAR_CONTEXT *vc; +- +- 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". */ +- search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment); +- +- if (search_tempenv && temporary_env) +- var = hash_lookup (name, temporary_env); +- +- vc = shell_variables; +-#if 0 +-if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */ +- expanding_redir && +- (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin)) +- { +- itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV"); +- while (vc && (vc->flags & VC_BLTNENV)) +- vc = vc->down; +- if (vc == 0) +- vc = shell_variables; +- } +-#endif +- +- if (var == 0) +- var = var_lookup (name, vc); +- +- if (var == 0) +- return ((SHELL_VAR *)NULL); +- +- return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); +-} +- +-/* Look up and resolve the chain of nameref variables starting at V all the +- way to NULL or non-nameref. */ +-SHELL_VAR * +-find_variable_nameref (v) +- SHELL_VAR *v; +-{ +- int level; +- char *newname; +- SHELL_VAR *orig, *oldv; +- +- level = 0; +- orig = v; +- while (v && nameref_p (v)) +- { +- level++; +- if (level > NAMEREF_MAX) +- return ((SHELL_VAR *)0); /* error message here? */ +- newname = nameref_cell (v); +- if (newname == 0 || *newname == '\0') +- return ((SHELL_VAR *)0); +- oldv = v; +- v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); +- if (v == orig || v == oldv) +- { +- internal_warning (_("%s: circular name reference"), orig->name); +- return ((SHELL_VAR *)0); +- } +- } +- return v; +-} +- +-/* Resolve the chain of nameref variables for NAME. XXX - could change later */ +-SHELL_VAR * +-find_variable_last_nameref (name) +- const char *name; +-{ +- SHELL_VAR *v, *nv; +- char *newname; +- int level; +- +- nv = v = find_variable_noref (name); +- level = 0; +- while (v && nameref_p (v)) +- { +- level++; +- if (level > NAMEREF_MAX) +- return ((SHELL_VAR *)0); /* error message here? */ +- newname = nameref_cell (v); +- if (newname == 0 || *newname == '\0') +- return ((SHELL_VAR *)0); +- nv = v; +- v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); +- } +- return nv; +-} +- +-/* Resolve the chain of nameref variables for NAME. XXX - could change later */ +-SHELL_VAR * +-find_global_variable_last_nameref (name) +- const char *name; +-{ +- SHELL_VAR *v, *nv; +- char *newname; +- int level; +- +- nv = v = find_global_variable_noref (name); +- level = 0; +- while (v && nameref_p (v)) +- { +- level++; +- if (level > NAMEREF_MAX) +- return ((SHELL_VAR *)0); /* error message here? */ +- newname = nameref_cell (v); +- if (newname == 0 || *newname == '\0') +- return ((SHELL_VAR *)0); +- nv = v; +- v = find_global_variable_noref (newname); +- } +- return nv; +-} +- +-static SHELL_VAR * +-find_nameref_at_context (v, vc) +- SHELL_VAR *v; +- VAR_CONTEXT *vc; +-{ +- SHELL_VAR *nv, *nv2; +- VAR_CONTEXT *nvc; +- char *newname; +- int level; +- +- nv = v; +- level = 1; +- while (nv && nameref_p (nv)) +- { +- level++; +- if (level > NAMEREF_MAX) +- return ((SHELL_VAR *)NULL); +- newname = nameref_cell (nv); +- if (newname == 0 || *newname == '\0') +- return ((SHELL_VAR *)NULL); +- nv2 = hash_lookup (newname, vc->table); +- if (nv2 == 0) +- break; +- nv = nv2; +- } +- return nv; +-} +- +-/* Do nameref resolution from the VC, which is the local context for some +- function or builtin, `up' the chain to the global variables context. If +- NVCP is not NULL, return the variable context where we finally ended the +- nameref resolution (so the bind_variable_internal can use the correct +- variable context and hash table). */ +-static SHELL_VAR * +-find_variable_nameref_context (v, vc, nvcp) +- SHELL_VAR *v; +- VAR_CONTEXT *vc; +- VAR_CONTEXT **nvcp; +-{ +- SHELL_VAR *nv, *nv2; +- VAR_CONTEXT *nvc; +- +- /* Look starting at the current context all the way `up' */ +- for (nv = v, nvc = vc; nvc; nvc = nvc->down) +- { +- nv2 = find_nameref_at_context (nv, nvc); +- if (nv2 == 0) +- continue; +- nv = nv2; +- if (*nvcp) +- *nvcp = nvc; +- if (nameref_p (nv) == 0) +- break; +- } +- return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv); +-} +- +-/* Do nameref resolution from the VC, which is the local context for some +- function or builtin, `up' the chain to the global variables context. If +- NVCP is not NULL, return the variable context where we finally ended the +- nameref resolution (so the bind_variable_internal can use the correct +- variable context and hash table). */ +-static SHELL_VAR * +-find_variable_last_nameref_context (v, vc, nvcp) +- SHELL_VAR *v; +- VAR_CONTEXT *vc; +- VAR_CONTEXT **nvcp; +-{ +- SHELL_VAR *nv, *nv2; +- VAR_CONTEXT *nvc; +- +- /* Look starting at the current context all the way `up' */ +- for (nv = v, nvc = vc; nvc; nvc = nvc->down) +- { +- nv2 = find_nameref_at_context (nv, nvc); +- if (nv2 == 0) +- continue; +- nv = nv2; +- if (*nvcp) +- *nvcp = nvc; +- } +- return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL); +-} +- +-/* Find a variable, forcing a search of the temporary environment first */ +-SHELL_VAR * +-find_variable_tempenv (name) +- const char *name; +-{ +- SHELL_VAR *var; +- +- var = find_variable_internal (name, 1); +- if (var && nameref_p (var)) +- var = find_variable_nameref (var); +- return (var); +-} +- +-/* Find a variable, not forcing a search of the temporary environment first */ +-SHELL_VAR * +-find_variable_notempenv (name) +- const char *name; +-{ +- SHELL_VAR *var; +- +- var = find_variable_internal (name, 0); +- if (var && nameref_p (var)) +- var = find_variable_nameref (var); +- return (var); +-} +- +-SHELL_VAR * +-find_global_variable (name) +- const char *name; +-{ +- SHELL_VAR *var; +- +- var = var_lookup (name, global_variables); +- if (var && nameref_p (var)) +- var = find_variable_nameref (var); +- +- if (var == 0) +- return ((SHELL_VAR *)NULL); +- +- return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); +-} +- +-SHELL_VAR * +-find_global_variable_noref (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); +-} +- +-SHELL_VAR * +-find_shell_variable (name) +- const char *name; +-{ +- SHELL_VAR *var; +- +- var = var_lookup (name, shell_variables); +- if (var && nameref_p (var)) +- var = find_variable_nameref (var); +- +- if (var == 0) +- return ((SHELL_VAR *)NULL); +- +- return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); +-} +- +-/* Look up the variable entry named NAME. Returns the entry or NULL. */ +-SHELL_VAR * +-find_variable (name) +- const char *name; +-{ +- SHELL_VAR *v; +- +- last_table_searched = 0; +- v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); +- if (v && nameref_p (v)) +- v = find_variable_nameref (v); +- return v; +-} +- +-SHELL_VAR * +-find_variable_noref (name) +- const char *name; +-{ +- SHELL_VAR *v; +- +- v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); +- return v; +-} +- +-/* Look up the function entry whose name matches STRING. +- Returns the entry or NULL. */ +-SHELL_VAR * +-find_function (name) +- const char *name; +-{ +- return (hash_lookup (name, shell_functions)); +-} +- +-/* Find the function definition for the shell function named NAME. Returns +- the entry or NULL. */ +-FUNCTION_DEF * +-find_function_def (name) +- const char *name; +-{ +-#if defined (DEBUGGER) +- return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs)); +-#else +- return ((FUNCTION_DEF *)0); +-#endif +-} +- +-/* 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 (value_cell (var)); +-} +- +-/* Return the string value of a variable. Return NULL if the variable +- doesn't exist. Don't cons a new string. This is a potential memory +- leak if the variable is found in the temporary environment. Since +- functions and variables have separate name spaces, returns NULL if +- var_name is a shell function only. */ +-char * +-get_string_value (var_name) +- const char *var_name; +-{ +- SHELL_VAR *var; +- +- var = find_variable (var_name); +- return ((var) ? get_variable_value (var) : (char *)NULL); +-} +- +-/* This is present for use by the tilde and readline libraries. */ +-char * +-sh_get_env_value (v) +- const char *v; +-{ +- return get_string_value (v); +-} +- +-/* **************************************************************** */ +-/* */ +-/* Creating and setting variables */ +-/* */ +-/* **************************************************************** */ +- +-/* Set NAME to VALUE if NAME has no value. */ +-SHELL_VAR * +-set_if_not (name, value) +- char *name, *value; +-{ +- SHELL_VAR *v; +- +- 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) +- const char *name; +-{ +- SHELL_VAR *new_var, *old_var; +- 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 && local_p (old_var) && old_var->context == variable_context) +- return (old_var); +- +- was_tmpvar = old_var && tempvar_p (old_var); +- /* If we're making a local variable in a shell function, the temporary env +- has already been merged into the function's variable context stack. We +- can assume that a temporary var in the same context appears in the same +- VAR_CONTEXT and can safely be returned without creating a new variable +- (which results in duplicate names in the same VAR_CONTEXT->table */ +- /* We can't just test tmpvar_p because variables in the temporary env given +- to a shell function appear in the function's local variable VAR_CONTEXT +- but retain their tempvar attribute. We want temporary variables that are +- found in temporary_env, hence the test for last_table_searched, which is +- set in hash_lookup and only (so far) checked here. */ +- if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env) +- { +- VUNSETATTR (old_var, att_invisible); +- return (old_var); +- } +- if (was_tmpvar) +- tmp_value = value_cell (old_var); +- +- for (vc = shell_variables; vc; vc = vc->down) +- if (vc_isfuncenv (vc) && vc->scope == variable_context) +- break; +- +- if (vc == 0) +- { +- internal_error (_("make_local_variable: no function context at current scope")); +- return ((SHELL_VAR *)NULL); +- } +- else if (vc->table == 0) +- vc->table = hash_create (TEMPENV_HASH_BUCKETS); +- +- /* Since this is called only from the local/declare/typeset code, we can +- call builtin_error here without worry (of course, it will also work +- for anything that sets this_command_name). Variables with the `noassign' +- attribute may not be made local. The test against old_var's context +- level is to disallow local copies of readonly global variables (since I +- believe that this could be a security hole). Readonly copies of calling +- function local variables are OK. */ +- if (old_var && (noassign_p (old_var) || +- (readonly_p (old_var) && old_var->context == 0))) +- { +- if (readonly_p (old_var)) +- sh_readonly (name); +- else if (noassign_p (old_var)) +- builtin_error (_("%s: variable may not be assigned value"), name); +-#if 0 +- /* Let noassign variables through with a warning */ +- if (readonly_p (old_var)) +-#endif +- return ((SHELL_VAR *)NULL); +- } +- +- if (old_var == 0) +- new_var = make_new_variable (name, vc->table); +- else +- { +- new_var = make_new_variable (name, vc->table); +- +- /* If we found this variable in one of the temporary environments, +- inherit its value. Watch to see if this causes problems with +- things like `x=4 local x'. XXX - see above for temporary env +- variables with the same context level as variable_context */ +- /* XXX - we should only do this if the variable is not an array. */ +- if (was_tmpvar) +- var_setvalue (new_var, savestring (tmp_value)); +- +- new_var->attributes = exported_p (old_var) ? att_exported : 0; +- } +- +- vc->flags |= VC_HASLOCAL; +- +- new_var->context = variable_context; +- VSETATTR (new_var, att_local); +- +- if (ifsname (name)) +- setifs (new_var); +- +- if (was_tmpvar == 0) +- VSETATTR (new_var, att_invisible); /* XXX */ +- return (new_var); +-} +- +-/* Create a new shell variable with name NAME. */ +-static SHELL_VAR * +-new_shell_variable (name) +- const char *name; +-{ +- SHELL_VAR *entry; +- +- entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); +- +- entry->name = savestring (name); +- var_setvalue (entry, (char *)NULL); +- CLEAR_EXPORTSTR (entry); +- +- entry->dynamic_value = (sh_var_value_func_t *)NULL; +- entry->assign_func = (sh_var_assign_func_t *)NULL; +- +- entry->attributes = 0; +- +- /* Always assume variables are to be made at toplevel! +- make_local_variable has the responsibility of changing the +- variable context. */ +- entry->context = 0; +- +- 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; +-} +- +-#if defined (ARRAY_VARS) +-SHELL_VAR * +-make_new_array_variable (name) +- char *name; +-{ +- SHELL_VAR *entry; +- ARRAY *array; +- +- entry = make_new_variable (name, global_variables->table); +- array = array_create (); +- +- var_setarray (entry, array); +- VSETATTR (entry, att_array); +- return entry; +-} +- +-SHELL_VAR * +-make_local_array_variable (name, assoc_ok) +- char *name; +- int assoc_ok; +-{ +- SHELL_VAR *var; +- ARRAY *array; +- +- var = make_local_variable (name); +- if (var == 0 || array_p (var) || (assoc_ok && assoc_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); +- } +- /* This can be fooled if the variable's value changes while evaluating +- `rval'. We can change it if we move the evaluation of lval to here. */ +- 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 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 = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table); +- /* Follow the nameref chain here if this is the global variables table */ +- if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table) +- { +- entry = find_global_variable (entry->name); +- /* Let's see if we have a nameref referencing a variable that hasn't yet +- been created. */ +- if (entry == 0) +- entry = find_variable_last_nameref (name); /* XXX */ +- if (entry == 0) /* just in case */ +- return (entry); +- } +- +- /* The first clause handles `declare -n ref; ref=x;' */ +- if (entry && invisible_p (entry) && nameref_p (entry)) +- goto assign_value; +- else if (entry && nameref_p (entry)) +- { +- newval = nameref_cell (entry); +-#if defined (ARRAY_VARS) +- /* declare -n foo=x[2] */ +- if (valid_array_reference (newval)) +- /* XXX - should it be aflags? */ +- entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags); +- else +-#endif +- { +- entry = make_new_variable (newval, table); +- var_setvalue (entry, make_variable_value (entry, value, 0)); +- } +- } +- else if (entry == 0) +- { +- entry = make_new_variable (name, table); +- var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */ +- } +- else if (entry->assign_func) /* array vars have assign functions now */ +- { +- INVALIDATE_EXPORTSTR (entry); +- newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value; +- 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 +- { +-assign_value: +- if (readonly_p (entry) || noassign_p (entry)) +- { +- if (readonly_p (entry)) +- err_readonly (name); +- return (entry); +- } +- +- /* Variables which are bound are visible. */ +- VUNSETATTR (entry, att_invisible); +- +-#if defined (ARRAY_VARS) +- if (assoc_p (entry) || array_p (entry)) +- newval = make_array_variable_value (entry, 0, "0", value, aflags); +- else +-#endif +- +- newval = make_variable_value (entry, value, aflags); /* XXX */ +- +- /* Invalidate any cached export string */ +- INVALIDATE_EXPORTSTR (entry); +- +-#if defined (ARRAY_VARS) +- /* XXX -- this bears looking at again -- XXX */ +- /* If an existing array variable x is being assigned to with x=b or +- `read x' or something of that nature, silently convert it to +- x[0]=b or `read x[0]'. */ +- if (assoc_p (entry)) +- { +- assoc_insert (assoc_cell (entry), savestring ("0"), newval); +- free (newval); +- } +- else if (array_p (entry)) +- { +- array_insert (array_cell (entry), 0, newval); +- free (newval); +- } +- else +-#endif +- { +- FREE (value_cell (entry)); +- var_setvalue (entry, newval); +- } +- } +- +- if (mark_modified_vars) +- VSETATTR (entry, att_exported); +- +- if (exported_p (entry)) +- array_needs_making = 1; +- +- return (entry); +-} +- +-/* Bind a variable NAME to VALUE. This conses up the name +- and value strings. If we have a temporary environment, we bind there +- first, then we bind into shell_variables. */ +- +-SHELL_VAR * +-bind_variable (name, value, flags) +- const char *name; +- char *value; +- int flags; +-{ +- SHELL_VAR *v, *nv; +- VAR_CONTEXT *vc, *nvc; +- int level; +- +- 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); +- nvc = vc; +- if (v && nameref_p (v)) +- { +- nv = find_variable_nameref_context (v, vc, &nvc); +- if (nv == 0) +- { +- nv = find_variable_last_nameref_context (v, vc, &nvc); +- if (nv && nameref_p (nv)) +- { +- /* If this nameref variable doesn't have a value yet, +- set the value. Otherwise, assign using the value as +- normal. */ +- if (nameref_cell (nv) == 0) +- return (bind_variable_internal (nv->name, value, nvc->table, 0, flags)); +- return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags)); +- } +- else +- v = nv; +- } +- else +- v = nv; +- } +- if (v) +- return (bind_variable_internal (v->name, value, nvc->table, 0, flags)); +- } +- } +- /* bind_variable_internal will handle nameref resolution in this case */ +- return (bind_variable_internal (name, value, global_variables->table, 0, flags)); +-} +- +-SHELL_VAR * +-bind_global_variable (name, value, flags) +- const char *name; +- char *value; +- int flags; +-{ +- SHELL_VAR *v, *nv; +- VAR_CONTEXT *vc, *nvc; +- int level; +- +- if (shell_variables == 0) +- create_variable_tables (); +- +- /* bind_variable_internal will handle nameref resolution in this case */ +- 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 +- of the internals of bind_variable. If the variable is exported, or +- all modified variables should be exported, mark the variable for export +- and note that the export environment needs to be recreated. */ +-SHELL_VAR * +-bind_variable_value (var, value, aflags) +- SHELL_VAR *var; +- char *value; +- int aflags; +-{ +- char *t; +- int invis; +- +- invis = invisible_p (var); +- VUNSETATTR (var, att_invisible); +- +- 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); +-#if defined (ARRAY_VARS) +- if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0))) +-#else +- if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0)) +-#endif +- { +- free (t); +- if (invis) +- VSETATTR (var, att_invisible); /* XXX */ +- return ((SHELL_VAR *)NULL); +- } +- FREE (value_cell (var)); +- var_setvalue (var, t); +- } +- +- INVALIDATE_EXPORTSTR (var); +- +- if (mark_modified_vars) +- VSETATTR (var, att_exported); +- +- if (exported_p (var)) +- array_needs_making = 1; +- +- return (var); +-} +- +-/* Bind/create a shell variable with the name LHS to the RHS. +- This creates or modifies a variable such that it is an integer. +- +- This used to be in expr.c, but it is here so that all of the +- variable binding stuff is localized. Since we don't want any +- recursive evaluation from bind_variable() (possible without this code, +- since bind_variable() calls the evaluator for variables with the integer +- attribute set), we temporarily turn off the integer attribute for each +- variable we set here, then turn it back on after binding as necessary. */ +- +-SHELL_VAR * +-bind_int_variable (lhs, rhs) +- char *lhs, *rhs; +-{ +- register SHELL_VAR *v; +- int isint, isarr, implicitarray; +- +- isint = isarr = implicitarray = 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); +- +- if (v) +- { +- isint = integer_p (v); +- VUNSETATTR (v, att_integer); +-#if defined (ARRAY_VARS) +- if (array_p (v) && isarr == 0) +- implicitarray = 1; +-#endif +- } +- +-#if defined (ARRAY_VARS) +- if (isarr) +- v = assign_array_element (lhs, rhs, 0); +- else if (implicitarray) +- v = bind_array_variable (lhs, 0, rhs, 0); +- else +-#endif +- v = bind_variable (lhs, rhs, 0); +- +- if (v && isint) +- VSETATTR (v, att_integer); +- +- VUNSETATTR (v, att_invisible); +- +- return (v); +-} +- +-SHELL_VAR * +-bind_var_to_int (var, val) +- char *var; +- intmax_t val; +-{ +- char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p; +- +- p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0); +- return (bind_int_variable (var, p)); +-} +- +-/* Do a function binding to a variable. You pass the name and +- the command to bind to. This conses the name and command. */ +-SHELL_VAR * +-bind_function (name, value) +- const char *name; +- COMMAND *value; +-{ +- SHELL_VAR *entry; +- +- entry = find_function (name); +- if (entry == 0) +- { +- BUCKET_CONTENTS *elt; +- +- elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH); +- entry = new_shell_variable (name); +- elt->data = (PTR_T)entry; +- } +- else +- INVALIDATE_EXPORTSTR (entry); +- +- if (var_isset (entry)) +- dispose_command (function_cell (entry)); +- +- if (value) +- var_setfunc (entry, copy_command (value)); +- else +- var_setfunc (entry, 0); +- +- VSETATTR (entry, att_function); +- +- if (mark_modified_vars) +- VSETATTR (entry, att_exported); +- +- VUNSETATTR (entry, att_invisible); /* Just to be sure */ +- +- if (exported_p (entry)) +- array_needs_making = 1; +- +-#if defined (PROGRAMMABLE_COMPLETION) +- set_itemlist_dirty (&it_functions); +-#endif +- +- return (entry); +-} +- +-#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; +-{ +- FUNCTION_DEF *entry; +- BUCKET_CONTENTS *elt; +- COMMAND *cmd; +- +- entry = find_function_def (name); +- if (entry) +- { +- dispose_function_def_contents (entry); +- entry = copy_function_def_contents (value, entry); +- } +- else +- { +- cmd = value->command; +- value->command = 0; +- entry = copy_function_def (value); +- value->command = cmd; +- +- elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH); +- elt->data = (PTR_T *)entry; +- } +-} +-#endif /* DEBUGGER */ +- +-/* 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; +-{ +- int offset, aflags; +- char *name, *temp, *value; +- SHELL_VAR *var; +- const char *string; +- +- string = word->word; +- +- aflags = 0; +- offset = assignment (string, 0); +- name = savestring (string); +- value = (char *)NULL; +- +- if (name[offset] == '=') +- { +- name[offset] = 0; +- +- /* don't ignore the `+' when assigning temporary environment */ +- if (name[offset - 1] == '+') +- { +- name[offset - 1] = '\0'; +- aflags |= ASS_APPEND; +- } +- +- var = find_variable (name); +- if (var && (readonly_p (var) || noassign_p (var))) +- { +- if (readonly_p (var)) +- err_readonly (name); +- free (name); +- return (0); +- } +- +- temp = name + offset + 1; +- value = expand_assignment_string_to_string (temp, 0); +- +- if (var && (aflags & ASS_APPEND)) +- { +- temp = make_variable_value (var, value, aflags); +- FREE (value); +- value = temp; +- } +- } +- +- if (temporary_env == 0) +- temporary_env = hash_create (TEMPENV_HASH_BUCKETS); +- +- var = hash_lookup (name, temporary_env); +- if (var == 0) +- var = make_new_variable (name, temporary_env); +- else +- FREE (value_cell (var)); +- +- if (value == 0) +- { +- value = (char *)xmalloc (1); /* like do_assignment_internal */ +- value[0] = '\0'; +- } +- +- var_setvalue (var, value); +- var->attributes |= (att_exported|att_tempvar); +- var->context = variable_context; /* XXX */ +- +- INVALIDATE_EXPORTSTR (var); +- var->exportstr = mk_env_string (name, value, 0); +- +- array_needs_making = 1; +- +- if (flags) +- stupidly_hack_special_variables (name); +- +- if (echo_command_at_execute) +- /* The Korn shell prints the `+ ' in front of assignment statements, +- so we do too. */ +- xtrace_print_assignment (name, value, 0, 1); +- +- free (name); +- return 1; +-} +- +-/* **************************************************************** */ +-/* */ +-/* Copying variables */ +-/* */ +-/* **************************************************************** */ +- +-#ifdef INCLUDE_UNUSED +-/* Copy VAR to a new data structure and return that structure. */ +-SHELL_VAR * +-copy_variable (var) +- SHELL_VAR *var; +-{ +- SHELL_VAR *copy = (SHELL_VAR *)NULL; +- +- if (var) +- { +- copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); +- +- copy->attributes = var->attributes; +- copy->name = savestring (var->name); +- +- if (function_p (var)) +- var_setfunc (copy, copy_command (function_cell (var))); +-#if defined (ARRAY_VARS) +- else if (array_p (var)) +- var_setarray (copy, array_copy (array_cell (var))); +- else if (assoc_p (var)) +- var_setassoc (copy, assoc_copy (assoc_cell (var))); +-#endif +- else if (nameref_cell (var)) /* XXX - nameref */ +- var_setref (copy, savestring (nameref_cell (var))); +- else if (value_cell (var)) /* XXX - nameref */ +- var_setvalue (copy, savestring (value_cell (var))); +- else +- var_setvalue (copy, (char *)NULL); +- +- copy->dynamic_value = var->dynamic_value; +- copy->assign_func = var->assign_func; +- +- copy->exportstr = COPY_EXPORTSTR (var); +- +- copy->context = var->context; +- } +- return (copy); +-} +-#endif +- +-/* **************************************************************** */ +-/* */ +-/* Deleting and unsetting variables */ +-/* */ +-/* **************************************************************** */ +- +-/* Dispose of the information attached to VAR. */ +-static void +-dispose_variable_value (var) +- SHELL_VAR *var; +-{ +- if (function_p (var)) +- dispose_command (function_cell (var)); +-#if defined (ARRAY_VARS) +- else if (array_p (var)) +- array_dispose (array_cell (var)); +- else if (assoc_p (var)) +- assoc_dispose (assoc_cell (var)); +-#endif +- else if (nameref_p (var)) +- FREE (nameref_cell (var)); +- 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->name); +- +- if (exported_p (var)) +- array_needs_making = 1; +- +- free (var); +-} +- +-/* Unset the shell variable referenced by NAME. Unsetting a nameref variable +- unsets the variable it resolves to but leaves the nameref alone. */ +-int +-unbind_variable (name) +- const char *name; +-{ +- SHELL_VAR *v, *nv; +- int r; +- +- v = var_lookup (name, shell_variables); +- nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL; +- +- r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables); +- return r; +-} +- +-/* Unbind NAME, where NAME is assumed to be a nameref variable */ +-int +-unbind_nameref (name) +- const char *name; +-{ +- SHELL_VAR *v; +- +- v = var_lookup (name, shell_variables); +- if (v && nameref_p (v)) +- return makunbound (name, shell_variables); +- return 0; +-} +- +-/* Unset the shell function named NAME. */ +-int +-unbind_func (name) +- const char *name; +-{ +- BUCKET_CONTENTS *elt; +- SHELL_VAR *func; +- +- elt = hash_remove (name, shell_functions, 0); +- +- if (elt == 0) +- return -1; +- +-#if defined (PROGRAMMABLE_COMPLETION) +- set_itemlist_dirty (&it_functions); +-#endif +- +- func = (SHELL_VAR *)elt->data; +- if (func) +- { +- if (exported_p (func)) +- array_needs_making++; +- dispose_variable (func); +- } +- +- free (elt->key); +- free (elt); +- +- return 0; +-} +- +-#if defined (DEBUGGER) +-int +-unbind_function_def (name) +- const char *name; +-{ +- BUCKET_CONTENTS *elt; +- FUNCTION_DEF *funcdef; +- +- elt = hash_remove (name, shell_function_defs, 0); +- +- if (elt == 0) +- return -1; +- +- funcdef = (FUNCTION_DEF *)elt->data; +- if (funcdef) +- dispose_function_def (funcdef); +- +- free (elt->key); +- free (elt); +- +- return 0; +-} +-#endif /* DEBUGGER */ +- +-int +-delete_var (name, vc) +- const char *name; +- VAR_CONTEXT *vc; +-{ +- BUCKET_CONTENTS *elt; +- SHELL_VAR *old_var; +- VAR_CONTEXT *v; +- +- 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; +- free (elt->key); +- free (elt); +- +- dispose_variable (old_var); +- return (0); +-} +- +-/* Make the variable associated with NAME go away. HASH_LIST is the +- hash table from which this variable should be deleted (either +- shell_variables or shell_functions). +- Returns non-zero if the variable couldn't be found. */ +-int +-makunbound (name, vc) +- const char *name; +- VAR_CONTEXT *vc; +-{ +- BUCKET_CONTENTS *elt, *new_elt; +- SHELL_VAR *old_var; +- VAR_CONTEXT *v; +- char *t; +- +- 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; +- +- if (old_var && exported_p (old_var)) +- array_needs_making++; +- +- /* If we're unsetting a local variable and we're still executing inside +- the function, just mark the variable as invisible. The function +- eventually called by pop_var_context() will clean it up later. This +- must be done so that if the variable is subsequently assigned a new +- value inside the function, the `local' attribute is still present. +- We also need to add it back into the correct hash table. */ +- if (old_var && local_p (old_var) && variable_context == old_var->context) +- { +- 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 if (nameref_p (old_var)) +- FREE (nameref_cell (old_var)); +- 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 = hash_insert (savestring (old_var->name), v->table, 0); +- new_elt->data = (PTR_T)old_var; +- stupidly_hack_special_variables (old_var->name); +- +- free (elt->key); +- free (elt); +- return (0); +- } +- +- /* Have to save a copy of name here, because it might refer to +- old_var->name. If so, stupidly_hack_special_variables will +- reference freed memory. */ +- t = savestring (name); +- +- free (elt->key); +- free (elt); +- +- dispose_variable (old_var); +- stupidly_hack_special_variables (t); +- free (t); +- +- return (0); +-} +- +-/* Get rid of all of the variables in the current context. */ +-void +-kill_all_local_variables () +-{ +- VAR_CONTEXT *vc; +- +- for (vc = shell_variables; vc; vc = vc->down) +- if (vc_isfuncenv (vc) && vc->scope == variable_context) +- break; +- if (vc == 0) +- return; /* XXX */ +- +- if (vc->table && vc_haslocals (vc)) +- { +- delete_all_variables (vc->table); +- hash_dispose (vc->table); +- } +- vc->table = (HASH_TABLE *)NULL; +-} +- +-static void +-free_variable_hash_data (data) +- PTR_T data; +-{ +- SHELL_VAR *var; +- +- var = (SHELL_VAR *)data; +- dispose_variable (var); +-} +- +-/* Delete the entire contents of the hash table. */ +-void +-delete_all_variables (hashed_vars) +- HASH_TABLE *hashed_vars; +-{ +- hash_flush (hashed_vars, free_variable_hash_data); +-} +- +-/* **************************************************************** */ +-/* */ +-/* Setting variable attributes */ +-/* */ +-/* **************************************************************** */ +- +-#define FIND_OR_MAKE_VARIABLE(name, entry) \ +- do \ +- { \ +- entry = find_variable (name); \ +- if (!entry) \ +- { \ +- entry = bind_variable (name, "", 0); \ +- if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \ +- } \ +- } \ +- while (0) +- +-/* Make the variable associated with NAME be readonly. +- If NAME does not exist yet, create it. */ +-void +-set_var_read_only (name) +- char *name; +-{ +- SHELL_VAR *entry; +- +- FIND_OR_MAKE_VARIABLE (name, entry); +- VSETATTR (entry, att_readonly); +-} +- +-#ifdef INCLUDE_UNUSED +-/* Make the function associated with NAME be readonly. +- If NAME does not exist, we just punt, like auto_export code below. */ +-void +-set_func_read_only (name) +- const char *name; +-{ +- SHELL_VAR *entry; +- +- entry = find_function (name); +- if (entry) +- VSETATTR (entry, att_readonly); +-} +- +-/* Make the variable associated with NAME be auto-exported. +- If NAME does not exist yet, create it. */ +-void +-set_var_auto_export (name) +- char *name; +-{ +- SHELL_VAR *entry; +- +- FIND_OR_MAKE_VARIABLE (name, entry); +- set_auto_export (entry); +-} +- +-/* Make the function associated with NAME be auto-exported. */ +-void +-set_func_auto_export (name) +- const char *name; +-{ +- SHELL_VAR *entry; +- +- entry = find_function (name); +- if (entry) +- set_auto_export (entry); +-} +-#endif +- +-/* **************************************************************** */ +-/* */ +-/* Creating lists of variables */ +-/* */ +-/* **************************************************************** */ +- +-static VARLIST * +-vlist_alloc (nentries) +- int nentries; +-{ +- VARLIST *vlist; +- +- vlist = (VARLIST *)xmalloc (sizeof (VARLIST)); +- vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *)); +- vlist->list_size = nentries; +- vlist->list_len = 0; +- vlist->list[0] = (SHELL_VAR *)NULL; +- +- return vlist; +-} +- +-static VARLIST * +-vlist_realloc (vlist, n) +- VARLIST *vlist; +- int n; +-{ +- if (vlist == 0) +- return (vlist = vlist_alloc (n)); +- if (n > vlist->list_size) +- { +- vlist->list_size = n; +- vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *)); +- } +- return vlist; +-} +- +-static void +-vlist_add (vlist, var, flags) +- VARLIST *vlist; +- SHELL_VAR *var; +- int flags; +-{ +- register int i; +- +- for (i = 0; i < vlist->list_len; i++) +- if (STREQ (var->name, vlist->list[i]->name)) +- break; +- if (i < vlist->list_len) +- return; +- +- if (i >= vlist->list_size) +- vlist = vlist_realloc (vlist, vlist->list_size + 16); +- +- vlist->list[vlist->list_len++] = var; +- vlist->list[vlist->list_len] = (SHELL_VAR *)NULL; +-} +- +-/* Map FUNCTION over the variables in VAR_HASH_TABLE. Return an array of the +- variables for which FUNCTION returns a non-zero value. A NULL value +- for FUNCTION means to use all variables. */ +-SHELL_VAR ** +-map_over (function, vc) +- sh_var_map_func_t *function; +- VAR_CONTEXT *vc; +-{ +- VAR_CONTEXT *v; +- VARLIST *vlist; +- SHELL_VAR **ret; +- int nentries; +- +- for (nentries = 0, v = vc; v; v = v->down) +- nentries += HASH_ENTRIES (v->table); +- +- if (nentries == 0) +- return (SHELL_VAR **)NULL; +- +- vlist = vlist_alloc (nentries); +- +- for (v = vc; v; v = v->down) +- flatten (v->table, function, vlist, 0); +- +- ret = vlist->list; +- free (vlist); +- return ret; +-} +- +-SHELL_VAR ** +-map_over_funcs (function) +- sh_var_map_func_t *function; +-{ +- VARLIST *vlist; +- SHELL_VAR **ret; +- +- if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0) +- return ((SHELL_VAR **)NULL); +- +- vlist = vlist_alloc (HASH_ENTRIES (shell_functions)); +- +- flatten (shell_functions, function, vlist, 0); +- +- ret = vlist->list; +- free (vlist); +- return ret; +-} +- +-/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those +- elements for which FUNC succeeds to VLIST->list. FLAGS is reserved +- for future use. Only unique names are added to VLIST. If FUNC is +- NULL, each variable in VAR_HASH_TABLE is added to VLIST. If VLIST is +- NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE. If VLIST +- and FUNC are both NULL, nothing happens. */ +-static void +-flatten (var_hash_table, func, vlist, flags) +- HASH_TABLE *var_hash_table; +- sh_var_map_func_t *func; +- VARLIST *vlist; +- int flags; +-{ +- register int i; +- register BUCKET_CONTENTS *tlist; +- int r; +- SHELL_VAR *var; +- +- if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0)) +- return; +- +- for (i = 0; i < var_hash_table->nbuckets; i++) +- { +- for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next) +- { +- var = (SHELL_VAR *)tlist->data; +- +- r = func ? (*func) (var) : 1; +- if (r && vlist) +- vlist_add (vlist, var, flags); +- } +- } +-} +- +-void +-sort_variables (array) +- SHELL_VAR **array; +-{ +- qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp); +-} +- +-static int +-qsort_var_comp (var1, var2) +- SHELL_VAR **var1, **var2; +-{ +- int result; +- +- if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0) +- result = strcmp ((*var1)->name, (*var2)->name); +- +- return (result); +-} +- +-/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for +- which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */ +-static SHELL_VAR ** +-vapply (func) +- sh_var_map_func_t *func; +-{ +- SHELL_VAR **list; +- +- list = map_over (func, shell_variables); +- if (list /* && posixly_correct */) +- sort_variables (list); +- return (list); +-} +- +-/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for +- which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */ +-static SHELL_VAR ** +-fapply (func) +- sh_var_map_func_t *func; +-{ +- SHELL_VAR **list; +- +- list = map_over_funcs (func); +- if (list /* && posixly_correct */) +- sort_variables (list); +- return (list); +-} +- +-/* Create a NULL terminated array of all the shell variables. */ +-SHELL_VAR ** +-all_shell_variables () +-{ +- return (vapply ((sh_var_map_func_t *)NULL)); +-} +- +-/* Create a NULL terminated array of all the shell functions. */ +-SHELL_VAR ** +-all_shell_functions () +-{ +- return (fapply ((sh_var_map_func_t *)NULL)); +-} +- +-static int +-visible_var (var) +- SHELL_VAR *var; +-{ +- return (invisible_p (var) == 0); +-} +- +-SHELL_VAR ** +-all_visible_functions () +-{ +- return (fapply (visible_var)); +-} +- +-SHELL_VAR ** +-all_visible_variables () +-{ +- return (vapply (visible_var)); +-} +- +-/* Return non-zero if the variable VAR is visible and exported. Array +- variables cannot be exported. */ +-static int +-visible_and_exported (var) +- SHELL_VAR *var; +-{ +- return (invisible_p (var) == 0 && exported_p (var)); +-} +- +-/* 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 () +-{ +- return (vapply (visible_and_exported)); +-} +- +-SHELL_VAR ** +-local_exported_variables () +-{ +- return (vapply (local_and_exported)); +-} +- +-static int +-variable_in_context (var) +- SHELL_VAR *var; +-{ +- return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context); +-} +- +-SHELL_VAR ** +-all_local_variables () +-{ +- VARLIST *vlist; +- SHELL_VAR **ret; +- VAR_CONTEXT *vc; +- +- vc = shell_variables; +- for (vc = shell_variables; vc; vc = vc->down) +- if (vc_isfuncenv (vc) && vc->scope == variable_context) +- break; +- +- if (vc == 0) +- { +- internal_error (_("all_local_variables: no function context at current scope")); +- return (SHELL_VAR **)NULL; +- } +- if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0) +- return (SHELL_VAR **)NULL; +- +- vlist = vlist_alloc (HASH_ENTRIES (vc->table)); +- +- flatten (vc->table, variable_in_context, vlist, 0); +- +- ret = vlist->list; +- free (vlist); +- if (ret) +- sort_variables (ret); +- return ret; +-} +- +-#if defined (ARRAY_VARS) +-/* Return non-zero if the variable VAR is visible and an array. */ +-static int +-visible_array_vars (var) +- SHELL_VAR *var; +-{ +- return (invisible_p (var) == 0 && array_p (var)); +-} +- +-SHELL_VAR ** +-all_array_variables () +-{ +- return (vapply (visible_array_vars)); +-} +-#endif /* ARRAY_VARS */ +- +-char ** +-all_variables_matching_prefix (prefix) +- const char *prefix; +-{ +- SHELL_VAR **varlist; +- char **rlist; +- int vind, rind, plen; +- +- plen = STRLEN (prefix); +- varlist = all_visible_variables (); +- for (vind = 0; varlist && varlist[vind]; vind++) +- ; +- if (varlist == 0 || vind == 0) +- return ((char **)NULL); +- rlist = strvec_create (vind + 1); +- for (vind = rind = 0; varlist[vind]; vind++) +- { +- if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen)) +- rlist[rind++] = savestring (varlist[vind]->name); +- } +- rlist[rind] = (char *)0; +- free (varlist); +- +- return rlist; +-} +- +-/* **************************************************************** */ +-/* */ +-/* Managing temporary variable scopes */ +-/* */ +-/* **************************************************************** */ +- +-/* Make variable NAME have VALUE in the temporary environment. */ +-static SHELL_VAR * +-bind_tempenv_variable (name, value) +- const char *name; +- char *value; +-{ +- SHELL_VAR *var; +- +- var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL; +- +- if (var) +- { +- FREE (value_cell (var)); +- var_setvalue (var, savestring (value)); +- INVALIDATE_EXPORTSTR (var); +- } +- +- return (var); +-} +- +-/* Find a variable in the temporary environment that is named NAME. +- Return the SHELL_VAR *, or NULL if not found. */ +-SHELL_VAR * +-find_tempenv_variable (name) +- const char *name; +-{ +- return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL); +-} +- +-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, isfunc) +- const char *name, *value; +- int isfunc; +-{ +- size_t name_len, value_len; +- char *p, *q; +- +- name_len = strlen (name); +- value_len = STRLEN (value); +- +- /* If we are exporting a shell function, construct the encoded function +- name. */ +- if (isfunc && value) +- { +- p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2); +- q = p; +- memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN); +- q += BASHFUNC_PREFLEN; +- memcpy (q, name, name_len); +- q += name_len; +- memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN); +- q += BASHFUNC_SUFFLEN; +- } +- else +- { +- p = (char *)xmalloc (2 + name_len + value_len); +- memcpy (p, name, name_len); +- q = p + name_len; +- } +- +- q[0] = '='; +- if (value && *value) +- memcpy (q + 1, value, value_len + 1); +- else +- q[1] = '\0'; +- +- return (p); +-} +- +-#ifdef DEBUG +-/* Debugging */ +-static int +-valid_exportstr (v) +- SHELL_VAR *v; +-{ +- char *s; +- +- s = v->exportstr; +- if (s == 0) +- { +- 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 ((unsigned char)*s) == 0) +- { +- internal_error (_("invalid character %d in exportstr for %s"), *s, v->name); +- return (0); +- } +- } +- if (*s != '=') +- { +- internal_error (_("no `=' in exportstr for %s"), v->name); +- return (0); +- } +- return (1); +-} +-#endif +- +-static char ** +-make_env_array_from_var_list (vars) +- SHELL_VAR **vars; +-{ +- register int i, list_index; +- register SHELL_VAR *var; +- char **list, *value; +- +- list = strvec_create ((1 + strvec_len ((char **)vars))); +- +-#define USE_EXPORTSTR (value == var->exportstr) +- +- for (i = 0, list_index = 0; var = vars[i]; i++) +- { +-#if defined (__CYGWIN__) +- /* We don't use the exportstr stuff on Cygwin at all. */ +- INVALIDATE_EXPORTSTR (var); +-#endif +- if (var->exportstr) +- value = var->exportstr; +- else if (function_p (var)) +- value = named_function_string ((char *)NULL, function_cell (var), 0); +-#if defined (ARRAY_VARS) +- else if (array_p (var)) +-# if ARRAY_EXPORT +- value = array_to_assignment_string (array_cell (var)); +-# else +- continue; /* XXX array vars cannot yet be exported */ +-# endif /* ARRAY_EXPORT */ +- 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 +- value = value_cell (var); +- +- if (value) +- { +- /* Gee, I'd like to get away with not using savestring() if we're +- using the cached exportstr... */ +- list[list_index] = USE_EXPORTSTR ? savestring (value) +- : mk_env_string (var->name, value, function_p (var)); +- +- 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) || assoc_p (var)) +- free (value); +-#endif +-#endif +- } +- } +- +- list[list_index] = (char *)NULL; +- return (list); +-} +- +-/* Make an array of assignment statements from the hash table +- HASHED_VARS which contains SHELL_VARs. Only visible, exported +- variables are eligible. */ +-static char ** +-make_var_export_array (vcxt) +- VAR_CONTEXT *vcxt; +-{ +- char **list; +- SHELL_VAR **vars; +- +-#if 0 +- vars = map_over (visible_and_exported, vcxt); +-#else +- vars = map_over (export_environment_candidate, vcxt); +-#endif +- +- if (vars == 0) +- return (char **)NULL; +- +- list = make_env_array_from_var_list (vars); +- +- free (vars); +- return (list); +-} +- +-static char ** +-make_func_export_array () +-{ +- char **list; +- SHELL_VAR **vars; +- +- vars = map_over_funcs (visible_and_exported); +- if (vars == 0) +- return (char **)NULL; +- +- list = make_env_array_from_var_list (vars); +- +- free (vars); +- return (list); +-} +- +-/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */ +-#define add_to_export_env(envstr,do_alloc) \ +-do \ +- { \ +- if (export_env_index >= (export_env_size - 1)) \ +- { \ +- export_env_size += 16; \ +- export_env = strvec_resize (export_env, export_env_size); \ +- environ = export_env; \ +- } \ +- export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \ +- export_env[export_env_index] = (char *)NULL; \ +- } while (0) +- +-/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the +- array with the same left-hand side. Return the new EXPORT_ENV. */ +-char ** +-add_or_supercede_exported_var (assign, do_alloc) +- char *assign; +- int do_alloc; +-{ +- register int i; +- int equal_offset; +- +- equal_offset = assignment (assign, 0); +- if (equal_offset == 0) +- return (export_env); +- +- /* If this is a function, then only supersede the function definition. +- We do this by including the `=() {' in the comparison, like +- initialize_shell_variables does. */ +- if (assign[equal_offset + 1] == '(' && +- strncmp (assign + equal_offset + 2, ") {", 3) == 0) /* } */ +- equal_offset += 4; +- +- for (i = 0; i < export_env_index; i++) +- { +- if (STREQN (assign, export_env[i], equal_offset + 1)) +- { +- free (export_env[i]); +- export_env[i] = do_alloc ? savestring (assign) : assign; +- return (export_env); +- } +- } +- add_to_export_env (assign, do_alloc); +- return (export_env); +-} +- +-static void +-add_temp_array_to_env (temp_array, do_alloc, do_supercede) +- char **temp_array; +- int do_alloc, do_supercede; +-{ +- register int i; +- +- if (temp_array == 0) +- return; +- +- for (i = 0; temp_array[i]; i++) +- { +- if (do_supercede) +- export_env = add_or_supercede_exported_var (temp_array[i], do_alloc); +- else +- add_to_export_env (temp_array[i], do_alloc); +- } +- +- free (temp_array); +-} +- +-/* Make the environment array for the command about to be executed, if the +- array needs making. Otherwise, do nothing. If a shell action could +- change the array that commands receive for their environment, then the +- code should `array_needs_making++'. +- +- The order to add to the array is: +- temporary_env +- list of var contexts whose head is shell_variables +- shell_functions +- +- This is the shell variable lookup order. We add only new variable +- names at each step, which allows local variables and variables in +- the temporary environments to shadow variables in the global (or +- any previous) scope. +-*/ +- +-static int +-n_shell_variables () +-{ +- VAR_CONTEXT *vc; +- int n; +- +- for (n = 0, vc = shell_variables; vc; vc = vc->down) +- n += HASH_ENTRIES (vc->table); +- return n; +-} +- +-int +-chkexport (name) +- char *name; +-{ +- SHELL_VAR *v; +- +- v = find_variable (name); +- if (v && exported_p (v)) +- { +- array_needs_making = 1; +- maybe_make_export_env (); +- return 1; +- } +- return 0; +-} +- +-void +-maybe_make_export_env () +-{ +- register char **temp_array; +- int new_size; +- VAR_CONTEXT *tcxt; +- +- if (array_needs_making) +- { +- if (export_env) +- strvec_flush (export_env); +- +- /* Make a guess based on how many shell variables and functions we +- have. Since there will always be array variables, and array +- variables are not (yet) exported, this will always be big enough +- for the exported variables and functions. */ +- new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 + +- HASH_ENTRIES (temporary_env); +- if (new_size > export_env_size) +- { +- export_env_size = new_size; +- export_env = strvec_resize (export_env, export_env_size); +- environ = export_env; +- } +- export_env[export_env_index = 0] = (char *)NULL; +- +- /* Make a dummy variable context from the temporary_env, stick it on +- the front of shell_variables, call make_var_export_array on the +- whole thing to flatten it, and convert the list of SHELL_VAR *s +- to the form needed by the environment. */ +- if (temporary_env) +- { +- tcxt = new_var_context ((char *)NULL, 0); +- tcxt->table = temporary_env; +- tcxt->down = shell_variables; +- } +- else +- tcxt = shell_variables; +- +- temp_array = make_var_export_array (tcxt); +- if (temp_array) +- add_temp_array_to_env (temp_array, 0, 0); +- +- if (tcxt != shell_variables) +- free (tcxt); +- +-#if defined (RESTRICTED_SHELL) +- /* Restricted shells may not export shell functions. */ +- temp_array = restricted ? (char **)0 : make_func_export_array (); +-#else +- temp_array = make_func_export_array (); +-#endif +- if (temp_array) +- add_temp_array_to_env (temp_array, 0, 0); +- +- array_needs_making = 0; +- } +-} +- +-/* This is an efficiency hack. PWD and OLDPWD are auto-exported, so +- we will need to remake the exported environment every time we +- change directories. `_' is always put into the environment for +- every external command, so without special treatment it will always +- cause the environment to be remade. +- +- If there is no other reason to make the exported environment, we can +- just update the variables in place and mark the exported environment +- as no longer needing a remake. */ +-void +-update_export_env_inplace (env_prefix, preflen, value) +- char *env_prefix; +- int preflen; +- char *value; +-{ +- char *evar; +- +- evar = (char *)xmalloc (STRLEN (value) + preflen + 1); +- strcpy (evar, env_prefix); +- if (value) +- strcpy (evar + preflen, value); +- export_env = add_or_supercede_exported_var (evar, 0); +-} +- +-/* We always put _ in the environment as the name of this command. */ +-void +-put_command_name_into_env (command_name) +- char *command_name; +-{ +- update_export_env_inplace ("_=", 2, command_name); +-} +- +-/* **************************************************************** */ +-/* */ +-/* Managing variable contexts */ +-/* */ +-/* **************************************************************** */ +- +-/* Allocate and return a new variable context with NAME and FLAGS. +- NAME can be NULL. */ +- +-VAR_CONTEXT * +-new_var_context (name, flags) +- char *name; +- int flags; +-{ +- VAR_CONTEXT *vc; +- +- vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT)); +- vc->name = name ? savestring (name) : (char *)NULL; +- vc->scope = variable_context; +- vc->flags = flags; +- +- vc->up = vc->down = (VAR_CONTEXT *)NULL; +- vc->table = (HASH_TABLE *)NULL; +- +- return vc; +-} +- +-/* Free a variable context and its data, including the hash table. Dispose +- all of the variables. */ +-void +-dispose_var_context (vc) +- VAR_CONTEXT *vc; +-{ +- FREE (vc->name); +- +- if (vc->table) +- { +- delete_all_variables (vc->table); +- hash_dispose (vc->table); +- } +- +- free (vc); +-} +- +-/* Set VAR's scope level to the current variable context. */ +-static int +-set_context (var) +- SHELL_VAR *var; +-{ +- return (var->context = variable_context); +-} +- +-/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of +- temporary variables, and push it onto shell_variables. This is +- for shell functions. */ +-VAR_CONTEXT * +-push_var_context (name, flags, tempvars) +- char *name; +- int flags; +- HASH_TABLE *tempvars; +-{ +- VAR_CONTEXT *vc; +- +- vc = new_var_context (name, flags); +- vc->table = tempvars; +- if (tempvars) +- { +- /* Have to do this because the temp environment was created before +- variable_context was incremented. */ +- flatten (tempvars, set_context, (VARLIST *)NULL, 0); +- vc->flags |= VC_HASTMPVAR; +- } +- vc->down = shell_variables; +- shell_variables->up = vc; +- +- return (shell_variables = vc); +-} +- +-static void +-push_func_var (data) +- PTR_T data; +-{ +- SHELL_VAR *var, *v; +- +- var = (SHELL_VAR *)data; +- +- if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate))) +- { +- /* 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 */ +- +- dispose_variable (var); +-} +- +-/* Pop the top context off of VCXT and dispose of it, returning the rest of +- the stack. */ +-void +-pop_var_context () +-{ +- VAR_CONTEXT *ret, *vcxt; +- +- vcxt = shell_variables; +- if (vc_isfuncenv (vcxt) == 0) +- { +- internal_error (_("pop_var_context: head of shell_variables not a function context")); +- return; +- } +- +- if (ret = vcxt->down) +- { +- ret->up = (VAR_CONTEXT *)NULL; +- shell_variables = ret; +- if (vcxt->table) +- hash_flush (vcxt->table, push_func_var); +- dispose_var_context (vcxt); +- } +- else +- internal_error (_("pop_var_context: no global_variables context")); +-} +- +-/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and +- all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */ +-void +-delete_all_contexts (vcxt) +- VAR_CONTEXT *vcxt; +-{ +- VAR_CONTEXT *v, *t; +- +- for (v = vcxt; v != global_variables; v = t) +- { +- t = v->down; +- dispose_var_context (v); +- } +- +- delete_all_variables (global_variables->table); +- shell_variables = global_variables; +-} +- +-/* **************************************************************** */ +-/* */ +-/* Pushing and Popping temporary variable scopes */ +-/* */ +-/* **************************************************************** */ +- +-VAR_CONTEXT * +-push_scope (flags, tmpvars) +- int flags; +- HASH_TABLE *tmpvars; +-{ +- return (push_var_context ((char *)NULL, flags, tmpvars)); +-} +- +-static void +-push_exported_var (data) +- PTR_T data; +-{ +- SHELL_VAR *var, *v; +- +- var = (SHELL_VAR *)data; +- +- /* If a temp var had its export attribute set, or it's marked to be +- propagated, bind it in the previous scope before disposing it. */ +- /* XXX - This isn't exactly right, because all tempenv variables have the +- export attribute set. */ +-#if 0 +- if (exported_p (var) || (var->attributes & att_propagate)) +-#else +- if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate)) +-#endif +- { +- var->attributes &= ~att_tempvar; /* XXX */ +- v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0); +- if (shell_variables == global_variables) +- var->attributes &= ~att_propagate; +- v->attributes |= var->attributes; +- } +- else +- stupidly_hack_special_variables (var->name); /* XXX */ +- +- dispose_variable (var); +-} +- +-void +-pop_scope (is_special) +- int is_special; +-{ +- VAR_CONTEXT *vcxt, *ret; +- +- vcxt = shell_variables; +- if (vc_istempscope (vcxt) == 0) +- { +- internal_error (_("pop_scope: head of shell_variables not a temporary environment scope")); +- return; +- } +- +- ret = vcxt->down; +- if (ret) +- ret->up = (VAR_CONTEXT *)NULL; +- +- shell_variables = ret; +- +- /* Now we can take care of merging variables in VCXT into set of scopes +- whose head is RET (shell_variables). */ +- FREE (vcxt->name); +- if (vcxt->table) +- { +- if (is_special) +- hash_flush (vcxt->table, push_func_var); +- else +- hash_flush (vcxt->table, push_exported_var); +- hash_dispose (vcxt->table); +- } +- free (vcxt); +- +- sv_ifs ("IFS"); /* XXX here for now */ +-} +- +-/* **************************************************************** */ +-/* */ +-/* Pushing and Popping function contexts */ +-/* */ +-/* **************************************************************** */ +- +-static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL; +-static int dollar_arg_stack_slots; +-static int dollar_arg_stack_index; +- +-/* XXX - we might want to consider pushing and popping the `getopts' state +- when we modify the positional parameters. */ +-void +-push_context (name, is_subshell, tempvars) +- char *name; /* function name */ +- int is_subshell; +- HASH_TABLE *tempvars; +-{ +- if (is_subshell == 0) +- push_dollar_vars (); +- variable_context++; +- push_var_context (name, VC_FUNCENV, tempvars); +-} +- +-/* Only called when subshell == 0, so we don't need to check, and can +- unconditionally pop the dollar vars off the stack. */ +-void +-pop_context () +-{ +- pop_dollar_vars (); +- variable_context--; +- pop_var_context (); +- +- sv_ifs ("IFS"); /* XXX here for now */ +-} +- +-/* Save the existing positional parameters on a stack. */ +-void +-push_dollar_vars () +-{ +- if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots) +- { +- dollar_arg_stack = (WORD_LIST **) +- xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10) +- * sizeof (WORD_LIST *)); +- } +- dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args (); +- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; +-} +- +-/* Restore the positional parameters from our stack. */ +-void +-pop_dollar_vars () +-{ +- if (!dollar_arg_stack || dollar_arg_stack_index == 0) +- return; +- +- remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1); +- dispose_words (dollar_arg_stack[dollar_arg_stack_index]); +- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; +- set_dollar_vars_unchanged (); +-} +- +-void +-dispose_saved_dollar_vars () +-{ +- if (!dollar_arg_stack || dollar_arg_stack_index == 0) +- return; +- +- dispose_words (dollar_arg_stack[dollar_arg_stack_index]); +- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; +-} +- +-/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */ +- +-void +-push_args (list) +- WORD_LIST *list; +-{ +-#if defined (ARRAY_VARS) && defined (DEBUGGER) +- SHELL_VAR *bash_argv_v, *bash_argc_v; +- ARRAY *bash_argv_a, *bash_argc_a; +- WORD_LIST *l; +- arrayind_t i; +- char *t; +- +- GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a); +- GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a); +- +- for (l = list, i = 0; l; l = l->next, i++) +- array_push (bash_argv_a, l->word->word); +- +- t = itos (i); +- array_push (bash_argc_a, t); +- free (t); +-#endif /* ARRAY_VARS && DEBUGGER */ +-} +- +-/* Remove arguments from BASH_ARGV array. Pop top element off BASH_ARGC +- array and use that value as the count of elements to remove from +- BASH_ARGV. */ +-void +-pop_args () +-{ +-#if defined (ARRAY_VARS) && defined (DEBUGGER) +- SHELL_VAR *bash_argv_v, *bash_argc_v; +- ARRAY *bash_argv_a, *bash_argc_a; +- ARRAY_ELEMENT *ce; +- intmax_t i; +- +- GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a); +- GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a); +- +- ce = array_shift (bash_argc_a, 1, 0); +- if (ce == 0 || legal_number (element_value (ce), &i) == 0) +- i = 0; +- +- for ( ; i > 0; i--) +- array_pop (bash_argv_a); +- array_dispose_element (ce); +-#endif /* ARRAY_VARS && DEBUGGER */ +-} +- +-/************************************************* +- * * +- * Functions to manage special variables * +- * * +- *************************************************/ +- +-/* Extern declarations for variables this code has to manage. */ +-extern int eof_encountered, eof_encountered_limit, ignoreeof; +- +-#if defined (READLINE) +-extern int hostname_list_initialized; +-#endif +- +-/* An alist of name.function for each special variable. Most of the +- functions don't do much, and in fact, this would be faster with a +- switch statement, but by the end of this file, I am sick of switch +- statements. */ +- +-#define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0 +- +-/* This table will be sorted with qsort() the first time it's accessed. */ +-struct name_and_function { +- char *name; +- sh_sv_func_t *function; +-}; +- +-static struct name_and_function special_vars[] = { +- { "BASH_COMPAT", sv_shcompat }, +- { "BASH_XTRACEFD", sv_xtracefd }, +- +-#if defined (JOB_CONTROL) +- { "CHILD_MAX", sv_childmax }, +-#endif +- +-#if defined (READLINE) +-# if defined (STRICT_POSIX) +- { "COLUMNS", sv_winsize }, +-# endif +- { "COMP_WORDBREAKS", sv_comp_wordbreaks }, +-#endif +- +- { "FUNCNEST", sv_funcnest }, +- +- { "GLOBIGNORE", sv_globignore }, +- +-#if defined (HISTORY) +- { "HISTCONTROL", sv_history_control }, +- { "HISTFILESIZE", sv_histsize }, +- { "HISTIGNORE", sv_histignore }, +- { "HISTSIZE", sv_histsize }, +- { "HISTTIMEFORMAT", sv_histtimefmt }, +-#endif +- +-#if defined (__CYGWIN__) +- { "HOME", sv_home }, +-#endif +- +-#if defined (READLINE) +- { "HOSTFILE", sv_hostfile }, +-#endif +- +- { "IFS", sv_ifs }, +- { "IGNOREEOF", sv_ignoreeof }, +- +- { "LANG", sv_locale }, +- { "LC_ALL", sv_locale }, +- { "LC_COLLATE", sv_locale }, +- { "LC_CTYPE", sv_locale }, +- { "LC_MESSAGES", sv_locale }, +- { "LC_NUMERIC", sv_locale }, +- { "LC_TIME", sv_locale }, +- +-#if defined (READLINE) && defined (STRICT_POSIX) +- { "LINES", sv_winsize }, +-#endif +- +- { "MAIL", sv_mail }, +- { "MAILCHECK", sv_mail }, +- { "MAILPATH", sv_mail }, +- +- { "OPTERR", sv_opterr }, +- { "OPTIND", sv_optind }, +- +- { "PATH", sv_path }, +- { "POSIXLY_CORRECT", sv_strict_posix }, +- +-#if defined (READLINE) +- { "TERM", sv_terminal }, +- { "TERMCAP", sv_terminal }, +- { "TERMINFO", sv_terminal }, +-#endif /* READLINE */ +- +- { "TEXTDOMAIN", sv_locale }, +- { "TEXTDOMAINDIR", sv_locale }, +- +-#if defined (HAVE_TZSET) +- { "TZ", sv_tz }, +-#endif +- +-#if defined (HISTORY) && defined (BANG_HISTORY) +- { "histchars", sv_histchars }, +-#endif /* HISTORY && BANG_HISTORY */ +- +- { "ignoreeof", sv_ignoreeof }, +- +- { (char *)0, (sh_sv_func_t *)0 } +-}; +- +-#define N_SPECIAL_VARS (sizeof (special_vars) / sizeof (special_vars[0]) - 1) +- +-static int +-sv_compare (sv1, sv2) +- struct name_and_function *sv1, *sv2; +-{ +- int r; +- +- if ((r = sv1->name[0] - sv2->name[0]) == 0) +- r = strcmp (sv1->name, sv2->name); +- return r; +-} +- +-static inline int +-find_special_var (name) +- const char *name; +-{ +- register int i, r; +- +- for (i = 0; special_vars[i].name; i++) +- { +- r = special_vars[i].name[0] - name[0]; +- if (r == 0) +- r = strcmp (special_vars[i].name, name); +- if (r == 0) +- return i; +- else if (r > 0) +- /* Can't match any of rest of elements in sorted list. Take this out +- if it causes problems in certain environments. */ +- break; +- } +- return -1; +-} +- +-/* The variable in NAME has just had its state changed. Check to see if it +- is one of the special ones where something special happens. */ +-void +-stupidly_hack_special_variables (name) +- char *name; +-{ +- static int sv_sorted = 0; +- int i; +- +- if (sv_sorted == 0) /* shouldn't need, but it's fairly cheap. */ +- { +- qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]), +- (QSFUNC *)sv_compare); +- sv_sorted = 1; +- } +- +- i = find_special_var (name); +- if (i != -1) +- (*(special_vars[i].function)) (name); +-} +- +-/* 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. */ +-void +-sv_path (name) +- char *name; +-{ +- /* hash -r */ +- phash_flush (); +-} +- +-/* What to do just after one of the MAILxxxx variables has changed. NAME +- is the name of the variable. This is called with NAME set to one of +- MAIL, MAILCHECK, or MAILPATH. */ +-void +-sv_mail (name) +- char *name; +-{ +- /* If the time interval for checking the files has changed, then +- reset the mail timer. Otherwise, one of the pathname vars +- to the users mailbox has changed, so rebuild the array of +- filenames. */ +- if (name[4] == 'C') /* if (strcmp (name, "MAILCHECK") == 0) */ +- reset_mail_timer (); +- else +- { +- free_mail_files (); +- remember_mail_dates (); +- } +-} +- +-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; +-{ +- 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. */ +-void +-sv_terminal (name) +- char *name; +-{ +- if (interactive_shell && no_line_editing == 0) +- rl_reset_terminal (get_string_value ("TERM")); +-} +- +-void +-sv_hostfile (name) +- char *name; +-{ +- SHELL_VAR *v; +- +- v = find_variable (name); +- if (v == 0) +- clear_hostname_list (); +- else +- hostname_list_initialized = 0; +-} +- +-#if defined (STRICT_POSIX) +-/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values +- found in the initial environment) to override the terminal size reported by +- the kernel. */ +-void +-sv_winsize (name) +- char *name; +-{ +- SHELL_VAR *v; +- intmax_t xd; +- int d; +- +- if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing) +- return; +- +- v = find_variable (name); +- if (v == 0 || var_isnull (v)) +- rl_reset_screen_size (); +- else +- { +- if (legal_number (value_cell (v), &xd) == 0) +- return; +- winsize_assignment = 1; +- d = xd; /* truncate */ +- if (name[0] == 'L') /* LINES */ +- rl_set_screen_size (d, -1); +- else /* COLUMNS */ +- rl_set_screen_size (-1, d); +- winsize_assignment = 0; +- } +-} +-#endif /* STRICT_POSIX */ +-#endif /* READLINE */ +- +-/* Update the value of HOME in the export environment so tilde expansion will +- work on cygwin. */ +-#if defined (__CYGWIN__) +-sv_home (name) +- char *name; +-{ +- array_needs_making = 1; +- maybe_make_export_env (); +-} +-#endif +- +-#if defined (HISTORY) +-/* What to do after the HISTSIZE or HISTFILESIZE variables change. +- If there is a value for this HISTSIZE (and it is numeric), then stifle +- the history. Otherwise, if there is NO value for this variable, +- unstifle the history. If name is HISTFILESIZE, and its value is +- numeric, truncate the history file to hold no more than that many +- lines. */ +-void +-sv_histsize (name) +- char *name; +-{ +- char *temp; +- intmax_t num; +- int hmax; +- +- temp = get_string_value (name); +- +- if (temp && *temp) +- { +- if (legal_number (temp, &num)) +- { +- hmax = num; +- if (hmax < 0 && name[4] == 'S') +- unstifle_history (); /* unstifle history if HISTSIZE < 0 */ +- else if (name[4] == 'S') +- { +- stifle_history (hmax); +- hmax = where_history (); +- if (history_lines_this_session > hmax) +- history_lines_this_session = hmax; +- } +- else if (hmax >= 0) /* truncate HISTFILE if HISTFILESIZE >= 0 */ +- { +- history_truncate_file (get_string_value ("HISTFILE"), hmax); +- if (hmax <= history_lines_in_file) +- history_lines_in_file = hmax; +- } +- } +- } +- else if (name[4] == 'S') +- unstifle_history (); +-} +- +-/* What to do after the HISTIGNORE variable changes. */ +-void +-sv_histignore (name) +- char *name; +-{ +- setup_history_ignore (name); +-} +- +-/* What to do after the HISTCONTROL variable changes. */ +-void +-sv_history_control (name) +- char *name; +-{ +- char *temp; +- char *val; +- int tptr; +- +- history_control = 0; +- temp = get_string_value (name); +- +- if (temp == 0 || *temp == 0) +- return; +- +- tptr = 0; +- while (val = extract_colon_unit (temp, &tptr)) +- { +- if (STREQ (val, "ignorespace")) +- history_control |= HC_IGNSPACE; +- else if (STREQ (val, "ignoredups")) +- history_control |= HC_IGNDUPS; +- else if (STREQ (val, "ignoreboth")) +- history_control |= HC_IGNBOTH; +- else if (STREQ (val, "erasedups")) +- history_control |= HC_ERASEDUPS; +- +- free (val); +- } +-} +- +-#if defined (BANG_HISTORY) +-/* Setting/unsetting of the history expansion character. */ +-void +-sv_histchars (name) +- char *name; +-{ +- char *temp; +- +- temp = get_string_value (name); +- if (temp) +- { +- history_expansion_char = *temp; +- if (temp[0] && temp[1]) +- { +- history_subst_char = temp[1]; +- if (temp[2]) +- history_comment_char = temp[2]; +- } +- } +- else +- { +- history_expansion_char = '!'; +- history_subst_char = '^'; +- history_comment_char = '#'; +- } +-} +-#endif /* BANG_HISTORY */ +- +-void +-sv_histtimefmt (name) +- char *name; +-{ +- SHELL_VAR *v; +- +- if (v = find_variable (name)) +- { +- if (history_comment_char == 0) +- history_comment_char = '#'; +- } +- history_write_timestamps = (v != 0); +-} +-#endif /* HISTORY */ +- +-#if defined (HAVE_TZSET) +-void +-sv_tz (name) +- char *name; +-{ +- if (chkexport (name)) +- tzset (); +-} +-#endif +- +-/* If the variable exists, then the value of it can be the number +- of times we actually ignore the EOF. The default is small, +- (smaller than csh, anyway). */ +-void +-sv_ignoreeof (name) +- char *name; +-{ +- SHELL_VAR *tmp_var; +- char *temp; +- +- eof_encountered = 0; +- +- tmp_var = find_variable (name); +- ignoreeof = tmp_var != 0; +- temp = tmp_var ? value_cell (tmp_var) : (char *)NULL; +- if (temp) +- eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10; +- set_shellopts (); /* make sure `ignoreeof' is/is not in $SHELLOPTS */ +-} +- +-void +-sv_optind (name) +- char *name; +-{ +- char *tt; +- int s; +- +- tt = get_string_value ("OPTIND"); +- if (tt && *tt) +- { +- s = atoi (tt); +- +- /* According to POSIX, setting OPTIND=1 resets the internal state +- of getopt (). */ +- if (s < 0 || s == 1) +- s = 0; +- } +- else +- s = 0; +- getopts_reset (s); +-} +- +-void +-sv_opterr (name) +- char *name; +-{ +- char *tt; +- +- tt = get_string_value ("OPTERR"); +- sh_opterr = (tt && *tt) ? atoi (tt) : 1; +-} +- +-void +-sv_strict_posix (name) +- char *name; +-{ +- SET_INT_VAR (name, posixly_correct); +- posix_initialize (posixly_correct); +-#if defined (READLINE) +- if (interactive_shell) +- posix_readline_initialize (posixly_correct); +-#endif /* READLINE */ +- set_shellopts (); /* make sure `posix' is/is not in $SHELLOPTS */ +-} +- +-void +-sv_locale (name) +- char *name; +-{ +- char *v; +- int r; +- +- v = get_string_value (name); +- if (name[0] == 'L' && name[1] == 'A') /* LANG */ +- r = set_lang (name, v); +- else +- r = set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */ +- +-#if 1 +- if (r == 0 && posixly_correct) +- last_command_exit_value = 1; +-#endif +-} +- +-#if defined (ARRAY_VARS) +-void +-set_pipestatus_array (ps, nproc) +- int *ps; +- int nproc; +-{ +- SHELL_VAR *v; +- ARRAY *a; +- ARRAY_ELEMENT *ae; +- register int i; +- char *t, tbuf[INT_STRLEN_BOUND(int) + 1]; +- +- v = find_variable ("PIPESTATUS"); +- if (v == 0) +- v = make_new_array_variable ("PIPESTATUS"); +- if (array_p (v) == 0) +- return; /* Do nothing if not an array variable. */ +- a = array_cell (v); +- +- if (a == 0 || array_num_elements (a) == 0) +- { +- for (i = 0; i < nproc; i++) /* was ps[i] != -1, not i < nproc */ +- { +- t = inttostr (ps[i], tbuf, sizeof (tbuf)); +- array_insert (a, i, t); +- } +- return; +- } +- +- /* Fast case */ +- if (array_num_elements (a) == nproc && nproc == 1) +- { +- ae = element_forw (a->head); +- free (element_value (ae)); +- ae->value = itos (ps[0]); +- } +- else if (array_num_elements (a) <= nproc) +- { +- /* modify in array_num_elements members in place, then add */ +- ae = a->head; +- for (i = 0; i < array_num_elements (a); i++) +- { +- ae = element_forw (ae); +- free (element_value (ae)); +- ae->value = itos (ps[i]); +- } +- /* add any more */ +- for ( ; i < nproc; i++) +- { +- t = inttostr (ps[i], tbuf, sizeof (tbuf)); +- array_insert (a, i, t); +- } +- } +- else +- { +- /* deleting elements. it's faster to rebuild the array. */ +- array_flush (a); +- for (i = 0; ps[i] != -1; i++) +- { +- t = inttostr (ps[i], tbuf, sizeof (tbuf)); +- array_insert (a, i, t); +- } +- } +-} +- +-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 +- +-void +-set_pipestatus_from_exit (s) +- int s; +-{ +-#if defined (ARRAY_VARS) +- static int v[2] = { 0, -1 }; +- +- v[0] = s; +- set_pipestatus_array (v, 1); +-#endif +-} +- +-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)); +- } +-} +- +-#define MIN_COMPAT_LEVEL 31 +- +-void +-sv_shcompat (name) +- char *name; +-{ +- SHELL_VAR *v; +- char *val; +- int tens, ones, compatval; +- +- v = find_variable (name); +- if (v == 0) +- { +- shell_compatibility_level = DEFAULT_COMPAT_LEVEL; +- set_compatibility_opts (); +- return; +- } +- val = value_cell (v); +- if (val == 0 || *val == '\0') +- { +- shell_compatibility_level = DEFAULT_COMPAT_LEVEL; +- set_compatibility_opts (); +- return; +- } +- /* Handle decimal-like compatibility version specifications: 4.2 */ +- if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0) +- { +- tens = val[0] - '0'; +- ones = val[2] - '0'; +- compatval = tens*10 + ones; +- } +- /* Handle integer-like compatibility version specifications: 42 */ +- else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0) +- { +- tens = val[0] - '0'; +- ones = val[1] - '0'; +- compatval = tens*10 + ones; +- } +- else +- { +-compat_error: +- internal_error (_("%s: %s: compatibility value out of range"), name, val); +- shell_compatibility_level = DEFAULT_COMPAT_LEVEL; +- set_compatibility_opts (); +- return; +- } +- +- if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL) +- goto compat_error; +- +- shell_compatibility_level = compatval; +- set_compatibility_opts (); +-} +- +-#if defined (JOB_CONTROL) +-void +-sv_childmax (name) +- char *name; +-{ +- char *tt; +- int s; +- +- tt = get_string_value (name); +- s = (tt && *tt) ? atoi (tt) : 0; +- set_maxchild (s); +-} +-#endif diff --git a/ptxdist/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch b/ptxdist/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch new file mode 100644 index 0000000..cda1797 --- /dev/null +++ b/ptxdist/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch @@ -0,0 +1,204 @@ +From: Chet Ramey +Date: Thu, 15 Jan 2015 10:21:08 -0500 +Subject: [PATCH] Bash-4.3 patch 33 + +--- + bashline.c | 6 ++++-- + builtins/common.h | 4 ++++ + builtins/read.def | 31 ++++++++++++++++++++++++++++--- + patchlevel.h | 2 +- + shell.c | 9 +++++++++ + sig.c | 6 ++++-- + 6 files changed, 50 insertions(+), 8 deletions(-) + +diff --git a/bashline.c b/bashline.c +index 77ca033f2cc8..c87415171a4a 100644 +--- a/bashline.c ++++ b/bashline.c +@@ -202,6 +202,7 @@ extern int current_command_line_count, saved_command_line_count; + extern int last_command_exit_value; + extern int array_needs_making; + extern int posixly_correct, no_symbolic_links; ++extern int sigalrm_seen; + extern char *current_prompt_string, *ps1_prompt; + extern STRING_INT_ALIST word_token_alist[]; + extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin; +@@ -4208,8 +4209,9 @@ bash_event_hook () + { + /* If we're going to longjmp to top_level, make sure we clean up readline. + check_signals will call QUIT, which will eventually longjmp to top_level, +- calling run_interrupt_trap along the way. */ +- if (interrupt_state) ++ calling run_interrupt_trap along the way. The check for sigalrm_seen is ++ to clean up the read builtin's state. */ ++ if (terminating_signal || interrupt_state || sigalrm_seen) + rl_cleanup_after_signal (); + bashline_reset_event_hook (); + check_signals_and_traps (); /* XXX */ +diff --git a/builtins/common.h b/builtins/common.h +index cae16b10fb65..a1298cb9c84a 100644 +--- a/builtins/common.h ++++ b/builtins/common.h +@@ -122,6 +122,10 @@ extern void bash_logout __P((void)); + /* Functions from getopts.def */ + extern void getopts_reset __P((int)); + ++/* Functions from read.def */ ++extern void read_tty_cleanup __P((void)); ++extern int read_tty_modified __P((void)); ++ + /* Functions from set.def */ + extern int minus_o_option_value __P((char *)); + extern void list_minus_o_opts __P((int, int)); +diff --git a/builtins/read.def b/builtins/read.def +index 43971544d081..56c23010bbe8 100644 +--- a/builtins/read.def ++++ b/builtins/read.def +@@ -140,10 +140,12 @@ static void reset_alarm __P((void)); + procenv_t alrmbuf; + int sigalrm_seen; + +-static int reading; ++static int reading, tty_modified; + static SigHandler *old_alrm; + static unsigned char delim; + ++static struct ttsave termsave; ++ + /* In all cases, SIGALRM just sets a flag that we check periodically. This + avoids problems with the semi-tricky stuff we do with the xfree of + input_string at the top of the unwind-protect list (see below). */ +@@ -188,7 +190,6 @@ read_builtin (list) + struct stat tsb; + SHELL_VAR *var; + TTYSTRUCT ttattrs, ttset; +- struct ttsave termsave; + #if defined (ARRAY_VARS) + WORD_LIST *alist; + #endif +@@ -221,7 +222,7 @@ read_builtin (list) + USE_VAR(ps2); + USE_VAR(lastsig); + +- sigalrm_seen = reading = 0; ++ sigalrm_seen = reading = tty_modified = 0; + + i = 0; /* Index into the string that we are reading. */ + raw = edit = 0; /* Not reading raw input by default. */ +@@ -438,6 +439,8 @@ read_builtin (list) + retval = 128+SIGALRM; + goto assign_vars; + } ++ if (interactive_shell == 0) ++ initialize_terminating_signals (); + old_alrm = set_signal_handler (SIGALRM, sigalrm); + add_unwind_protect (reset_alarm, (char *)NULL); + #if defined (READLINE) +@@ -482,7 +485,10 @@ read_builtin (list) + i = silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset); + if (i < 0) + sh_ttyerror (1); ++ tty_modified = 1; + add_unwind_protect ((Function *)ttyrestore, (char *)&termsave); ++ if (interactive_shell == 0) ++ initialize_terminating_signals (); + } + } + else if (silent) /* turn off echo but leave term in canonical mode */ +@@ -497,7 +503,10 @@ read_builtin (list) + if (i < 0) + sh_ttyerror (1); + ++ tty_modified = 1; + add_unwind_protect ((Function *)ttyrestore, (char *)&termsave); ++ if (interactive_shell == 0) ++ initialize_terminating_signals (); + } + + /* This *must* be the top unwind-protect on the stack, so the manipulation +@@ -588,6 +597,8 @@ read_builtin (list) + } + else + lastsig = 0; ++ if (terminating_signal && tty_modified) ++ ttyrestore (&termsave); /* fix terminal before exiting */ + CHECK_TERMSIG; + eof = 1; + break; +@@ -978,6 +989,20 @@ ttyrestore (ttp) + struct ttsave *ttp; + { + ttsetattr (ttp->fd, ttp->attrs); ++ tty_modified = 0; ++} ++ ++void ++read_tty_cleanup () ++{ ++ if (tty_modified) ++ ttyrestore (&termsave); ++} ++ ++int ++read_tty_modified () ++{ ++ return (tty_modified); + } + + #if defined (READLINE) +diff --git a/patchlevel.h b/patchlevel.h +index b8bf38704ed2..cefe6bdd3a13 100644 +--- a/patchlevel.h ++++ b/patchlevel.h +@@ -25,6 +25,6 @@ + regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh + looks for to find the patch level (for the sccs version string). */ + +-#define PATCHLEVEL 32 ++#define PATCHLEVEL 33 + + #endif /* _PATCHLEVEL_H_ */ +diff --git a/shell.c b/shell.c +index bbc8a66cc2eb..2fd8179ba10d 100644 +--- a/shell.c ++++ b/shell.c +@@ -73,6 +73,7 @@ + #endif + + #if defined (READLINE) ++# include + # include "bashline.h" + #endif + +@@ -909,6 +910,14 @@ exit_shell (s) + fflush (stdout); /* XXX */ + fflush (stderr); + ++ /* Clean up the terminal if we are in a state where it's been modified. */ ++#if defined (READLINE) ++ if (RL_ISSTATE (RL_STATE_TERMPREPPED) && rl_deprep_term_function) ++ (*rl_deprep_term_function) (); ++#endif ++ if (read_tty_modified ()) ++ read_tty_cleanup (); ++ + /* Do trap[0] if defined. Allow it to override the exit status + passed to us. */ + if (signal_is_trapped (0)) +diff --git a/sig.c b/sig.c +index 3b62ea5d7c5d..8bc45c17f478 100644 +--- a/sig.c ++++ b/sig.c +@@ -532,8 +532,10 @@ termsig_sighandler (sig) + #if defined (READLINE) + /* Set the event hook so readline will call it after the signal handlers + finish executing, so if this interrupted character input we can get +- quick response. */ +- if (interactive_shell && interactive && no_line_editing == 0) ++ quick response. If readline is active or has modified the terminal we ++ need to set this no matter what the signal is, though the check for ++ RL_STATE_TERMPREPPED is possibly redundant. */ ++ if (RL_ISSTATE (RL_STATE_SIGHANDLER) || RL_ISSTATE (RL_STATE_TERMPREPPED)) + bashline_set_event_hook (); + #endif + diff --git a/ptxdist/patches/bash-4.3.30/0101-etc_bashrc_bashlogout.patch b/ptxdist/patches/bash-4.3.30/0101-etc_bashrc_bashlogout.patch new file mode 100644 index 0000000..5154683 --- /dev/null +++ b/ptxdist/patches/bash-4.3.30/0101-etc_bashrc_bashlogout.patch @@ -0,0 +1,22 @@ +diff --git a/config-top.h b/config-top.h +index 7a90b16..52d582a 100644 +--- a/config-top.h ++++ b/config-top.h +@@ -73,14 +73,14 @@ + #define KSH_COMPATIBLE_SELECT + + /* System-wide .bashrc file for interactive shells. */ +-/* #define SYS_BASHRC "/etc/bash.bashrc" */ ++#define SYS_BASHRC "/etc/bash.bashrc" + + /* System-wide .bash_logout for login shells. */ +-/* #define SYS_BASH_LOGOUT "/etc/bash.bash_logout" */ ++#define SYS_BASH_LOGOUT "/etc/bash.bash_logout" + + /* Define this to make non-interactive shells begun with argv[0][0] == '-' + run the startup files when not in posix mode. */ +-/* #define NON_INTERACTIVE_LOGIN_SHELLS */ ++#define NON_INTERACTIVE_LOGIN_SHELLS 1 + + /* Define this if you want bash to try to check whether it's being run by + sshd and source the .bashrc if so (like the rshd behavior). */ diff --git a/ptxdist/patches/bash-4.3.30/series b/ptxdist/patches/bash-4.3.30/series new file mode 100644 index 0000000..ed42ce1 --- /dev/null +++ b/ptxdist/patches/bash-4.3.30/series @@ -0,0 +1,4 @@ +0001-Bash-4.3-patch-31.patch +0002-Bash-4.3-patch-32.patch +0003-Bash-4.3-patch-33.patch +0101-etc_bashrc_bashlogout.patch diff --git a/ptxdist/projectroot/etc/passwd b/ptxdist/projectroot/etc/passwd index 5df5a35..1d394b8 100644 --- a/ptxdist/projectroot/etc/passwd +++ b/ptxdist/projectroot/etc/passwd @@ -1,4 +1,4 @@ -root:x:0:0:root:/home:/bin/sh +root:x:0:0:root:/home:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh ftp:x:11:101:ftp user:/home:/bin/false www:x:12:102:www user:/home:/bin/false diff --git a/ptxdist/projectroot/etc/profile.environment b/ptxdist/projectroot/etc/profile.environment index e4b85cc..cb47464 100644 --- a/ptxdist/projectroot/etc/profile.environment +++ b/ptxdist/projectroot/etc/profile.environment @@ -1,13 +1,11 @@ -# /etc/profile.environment - config for sub-shells -#PS1="\\u@\\h:\\w " -PS1='\u \t : \w # ' -PS2=" >" -PS4="+ " +# +# /etc/profile.environment +# + +# do not set interactive shell properties like HISTFILE, PS1, PS2, +# ..., or aliases here. +# Such stuff belongs into the startup files for *interactive* shell +# sessions/etc/bash.bashrc or /etc/ksh.kshrc + +# EOF. -alias vim='vi' -alias l='ls -l' -alias ll='ls -al' -alias ..='cd ..' -alias ...='cd ../..' -alias md='mkdir' -alias rd='rmdir' diff --git a/ptxdist/rules/bash.in b/ptxdist/rules/bash.in new file mode 100644 index 0000000..183c751 --- /dev/null +++ b/ptxdist/rules/bash.in @@ -0,0 +1,269 @@ +## SECTION=shell_and_console +menuconfig BASH + tristate "bash " + select LIBC_DL + select GCCLIBS_GCC_S + select NCURSES if BASH_CURSES + select TERMCAP if !BASH_CURSES + help + The GNU Bourne Again SHell + Bash is an sh-compatible command language interpreter that executes + commands read from the standard input or from a file. Bash also + incorporates useful features from the Korn and C shells (ksh and csh). + + Bash is ultimately intended to be a conformant implementation of the + IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2). + + Included in the bash package is the Programmable Completion Code, + by Ian Macdonald. + + http://www.gnu.org/software/bash/ + +if BASH + +config BASH_SHLIKE + bool + prompt "Enable minimal sh like configuration" + help + This produces a shell with minimal features, close to the + historical Bourne shell. + +comment "" + depends on INVISIBLE + +if !BASH_SHLIKE + +config BASH_ALIASES + bool + prompt "Enable aliases" + default y + help + Aliases allow a string to be substituted for a word + when it is used as the first word of a simple command. + The shell maintains a list of aliases that may be + set and unset with the alias and unalias builtin commands + +config BASH_ARITHMETIC_FOR + bool + prompt "Enable arithmetic for command" + help + Include support for the alternate form of the for command that + behaves like the C language for statement. + This does not work properly in bash-2.05b! + +config BASH_ARRAY + bool + prompt "Include shell array variables" + default y + +config BASH_HISTORY + bool + prompt "Turn on csh-style history substitution" + default y + help + The bash shell supports a history expansion feature + that is similar to the history expansion in csh. + +config BASH_BRACE + bool + prompt "Include brace expansion" + default y + help + Brace expansion is a mechanism by which arbitrary + strings may be generated. This mechanism is similar + to pathname expansion, but the filenames generated + need not exist. + +config BASH_CASEMODATTR + bool + prompt "include case-modifying variable attributes" + +config BASH_CASEMODEXP + bool + prompt "include case-modifying word expansions" + +config BASH_CMDTIMING + bool + prompt "enable the time reserved word and command timing" + +config BASH_CONDITIONAL + bool + default y + prompt "Enable the conditional command" + +config BASH_CONDITIONAL_REGEX + bool + prompt "enable extended regular expression matching in conditional commands" + depends on BASH_CONDITIONAL + +config BASH_COPROCESSES + bool + default y + prompt "enable coprocess support and the coproc reserved word" + +config BASH_DEBUGGER + bool + prompt "enable support for bash debugger" + +config BASH_DIREXPDEFLT + bool + prompt "enable the direxpand shell option by default" + +config BASH_DIRSTACK + bool + prompt "Enable builtins pushd/popd/dirs" + default y + help + Without options, "dirs" displays the list of + currently remembered directories. The default + display is on a single line with directory + names separated by spaces. Directories are + added to the list with the pushd command; + the popd command removes entries from the list. + +config BASH_DISABLED_BUILDINS + bool + prompt "Allow disabled builtins to still be invoked" + +config BASH_DPARAN_ARITH + bool + prompt "include ((...)) command" + default y + +config BASH_EXTPATTERN + bool + default y + # fails to build without this in 4.3.30 + prompt "Include ksh-style extended pattern matching" if BROKEN + +config BASH_EXTPATTERN_DEFLT + bool + prompt "force extended pattern matching to be enabled by default" + +config BASH_GLOB_ASCIIRANGE_DEFLT + bool + prompt "bracket range pattern matching uses C locale" + help + force bracket range expressions in pattern matching to use the C + locale by default + +config BASH_HELP + bool + prompt "Include the help builtin" + help + This builtin supports minimal help features inside bash + +config BASH_CMDHISTORY + bool + prompt "Turn on command history" + default y + help + This enables command history features. The shell + buffers commands in a ringbuffer, which can be listed, + searched and accessed by various commands and magic + shell expansion features. + +config BASH_JOBS + bool + prompt "Enable job control features" + default y + help + This enables support for background jobs in bash. + You can list the actual managed jobs by the "jobs" command. + +config BASH_MULTIBYTE + bool + prompt "Enable multibyte characters" + default y + help + Enable multibyte characters if OS supports them. + +config BASH_PROCSUBST + bool + prompt "Enable process substitution" + default y + help + This enables process substitution if the operating system + provides the necessary support. + Process substitution is supported on systems that support + named pipes (FIFOs) or the '/dev/fd' method of naming open + files. It takes the form of <(list) or >(list). + +config BASH_BASHCOMPLETION + bool + prompt "Enable programmable completion" + help + Enable the programmable completion facilities. + When word completion is attempted for an argument to a + command for which a completion specification (a compspec) has + been defined using the complete builtin, the programmable + completion facilities are invoked. + If Readline is not enabled, this option has no effect. + +config BASH_ESC + bool + prompt "Turn on escape character decoding in prompts" + default y + help + Turn on the interpretation of a number of backslash-escaped + characters in the $PS1, $PS2, $PS3, and $PS4 prompt strings. + +config BASH_EDIT + bool +# prompt "Turn on command line editing" + default y + help + Include support for command-line editing and history with + the Bash version of the Readline library. + +config BASH_RESTRICTED + bool + prompt "Enable a restricted shell" + help + Include support for a restricted shell. If this is enabled, + Bash enters a restricted mode, when called as rbash or invoked + with the `--restricted' or `-r' option. + A restricted shell is used to set up an environment + more controlled than the standard shell. + +config BASH_SELECT + bool + prompt "Include select command" + help + Include the select builtin, which allows the generation of + simple menus. + +config BASH_SEP_HELPFILES + bool + depends on BROKEN + prompt "use external files for help builtin documentation" + +config BASH_SINGLE_HELPLINE + bool + prompt "store help documentation as a single string to ease translation" + +endif + +config BASH_GPROF + bool + prompt "Allow profiling with gprof" + help + This builds a Bash binary that produces profiling information + to be processed by gprof each time it is executed. + +config BASH_STATIC + bool + prompt "Link bash statically" + help + This causes Bash to be linked statically, if gcc is being used. + This could be used to build a version to use as root's shell. + +config BASH_CURSES + bool + prompt "Use libcurses instead of libtermcap" + +config BASH_SH + bool + default BUSYBOX = n || BUSYBOX_SH_IS_NONE + +endif diff --git a/ptxdist/rules/bash.make b/ptxdist/rules/bash.make new file mode 100644 index 0000000..6a1e5d7 --- /dev/null +++ b/ptxdist/rules/bash.make @@ -0,0 +1,111 @@ +# -*-makefile-*- +# +# Copyright (C) 2003-2009 by Pengutronix e.K., Hildesheim, Germany +# For further information about the PTXdist project and license conditions +# see the README file. +# + +# +# We provide this package +# +PACKAGES-$(PTXCONF_BASH) += bash + +# +# Paths and names +# +BASH_VERSION := 4.3.30 +BASH_MD5 := a27b3ee9be83bd3ba448c0ff52b28447 +BASH := bash-$(BASH_VERSION) +BASH_SUFFIX := tar.gz +BASH_URL := $(call ptx/mirror, GNU, bash/$(BASH).$(BASH_SUFFIX)) +BASH_SOURCE := $(SRCDIR)/$(BASH).$(BASH_SUFFIX) +BASH_DIR := $(BUILDDIR)/$(BASH) +BASH_MAKE_PAR := NO +BASH_LICENSE := GPL-3.0-or-later +BASH_LICENSE_FILES := \ + file://COPYING;md5=d32239bcb673463ab874e80d47fae504 \ + file://general.c;startline=1;endline=19;md5=c018785d21f8c206ca7a13fa7d027568 + +# ---------------------------------------------------------------------------- +# Prepare +# ---------------------------------------------------------------------------- + +BASH_PATH := PATH=$(CROSS_PATH) +BASH_ENV := \ + $(CROSS_ENV) \ + bash_cv_job_control_missing=$(call ptx/ifdef, PTXCONF_BASH_JOBS, present, missing) \ + bash_cv_termcap_lib=$(call ptx/ifdef, PTXCONF_BASH_CURSES, libncurses, libtermcap) + +BASH_AUTOCONF := \ + $(CROSS_AUTOCONF_USR) \ + $(GLOBAL_LARGE_FILE_OPTION) \ + --without-bash-malloc \ + --$(call ptx/endis, PTXCONF_BASH_SHLIKE)-minimal-config \ + --$(call ptx/endis, PTXCONF_BASH_ALIASES)-alias \ + --$(call ptx/endis, PTXCONF_BASH_ARITHMETIC_FOR)-arith-for-command \ + --$(call ptx/endis, PTXCONF_BASH_ARRAY)-array-variables \ + --$(call ptx/endis, PTXCONF_BASH_HISTORY)-bang-history \ + --$(call ptx/endis, PTXCONF_BASH_BRACE)-brace-expansion \ + --$(call ptx/endis, PTXCONF_BASH_CASEMODATTR)-casemod-attributes \ + --$(call ptx/endis, PTXCONF_BASH_CASEMODEXP)-casemod-expansions \ + --$(call ptx/endis, PTXCONF_BASH_CMDTIMING)-command-timing \ + --$(call ptx/endis, PTXCONF_BASH_CONDITIONAL)-cond-command \ + --$(call ptx/endis, PTXCONF_BASH_CONDITIONAL_REGEX)-cond-regexp \ + --$(call ptx/endis, PTXCONF_BASH_COPROCESSES)-coprocesses \ + --$(call ptx/endis, PTXCONF_BASH_DEBUGGER)-debugger \ + --$(call ptx/endis, PTXCONF_BASH_DIREXPDEFLT)-direxpand-default \ + --$(call ptx/endis, PTXCONF_BASH_DIRSTACK)-directory-stack \ + --$(call ptx/endis, PTXCONF_BASH_DISABLED_BUILDINS)-disabled-builtins \ + --$(call ptx/endis, PTXCONF_BASH_DPARAN_ARITH)-dparen-arithmetic \ + --$(call ptx/endis, PTXCONF_BASH_EXTPATTERN)-extended-glob \ + --$(call ptx/endis, PTXCONF_BASH_EXTPATTERN_DEFLT)-extended-glob-default \ + --$(call ptx/endis, PTXCONF_BASH_GLOB_ASCIIRANGE_DEFLT)-glob-asciiranges-default \ + --$(call ptx/endis, PTXCONF_BASH_HELP)-help-builtin \ + --$(call ptx/endis, PTXCONF_BASH_CMDHISTORY)-history \ + --$(call ptx/endis, PTXCONF_BASH_JOBS)-job-control \ + --$(call ptx/endis, PTXCONF_BASH_MULTIBYTE)-multibyte \ + --$(call ptx/endis, PTXCONF_BASH_PROCSUBST)-process-substitution \ + --$(call ptx/endis, PTXCONF_BASH_BASHCOMPLETION)-progcomp \ + --$(call ptx/endis, PTXCONF_BASH_ESC)-prompt-string-decoding \ + --$(call ptx/endis, PTXCONF_BASH_EDIT)-readline \ + --$(call ptx/endis, PTXCONF_BASH_RESTRICTED)-restricted \ + --$(call ptx/endis, PTXCONF_BASH_SELECT)-select \ + --$(call ptx/endis, PTXCONF_BASH_SEP_HELPFILES)-separate-helpfiles \ + --$(call ptx/endis, PTXCONF_BASH_SINGLE_HELPLINE)-single-help-strings \ + --$(call ptx/endis, PTXCONF_BASH_GPROF)-profiling \ + --$(call ptx/endis, PTXCONF_BASH_STATIC)-static-link \ + --$(call ptx/wwo, PTXCONF_BASH_CURSES)-curses \ + --enable-xpg-echo-default + +# ---------------------------------------------------------------------------- +# Target-Install +# ---------------------------------------------------------------------------- + +$(STATEDIR)/bash.targetinstall: + @$(call targetinfo) + + @$(call install_init, bash) + @$(call install_fixup, bash,PRIORITY,optional) + @$(call install_fixup, bash,SECTION,base) + @$(call install_fixup, bash,AUTHOR,"Robert Schwebel ") + @$(call install_fixup, bash,DESCRIPTION,missing) + +# this is reverse from UNIX tradition: /usr should depend on rootfs +# not rootfs depend on /usr + @$(call install_copy, bash, 0, 0, 0755, -, /usr/bin/bash) + @$(call install_link, bash, bash, /usr/bin/sh) + @$(call install_link, bash, ../usr/bin/bash, /bin/sh) + @$(call install_link, bash, ../usr/bin/bash, /bin/bash) + +# this would be the correct solution, but for some cursed issue +# this does not work (yet) +# @$(call install_copy, bash, 0, 0, 0755, -, /bin/bash) +# @$(call install_link, bash, ./bash, /bin/sh) +# @$(call install_link, bash, ../../bin/bash, /usr/bin/sh) +# @$(call install_link, bash, ../../bin/bash, /usr/bin/bash) + + @$(call install_finish, bash) + + @$(call touch) + +# vim: syntax=make diff --git a/ptxdist/rules/ecu01-basicsys.make b/ptxdist/rules/ecu01-basicsys.make index 7e948b3..944432b 100644 --- a/ptxdist/rules/ecu01-basicsys.make +++ b/ptxdist/rules/ecu01-basicsys.make @@ -210,9 +210,10 @@ $(STATEDIR)/ecu01-basicsys.targetinstall: #fonts @$(call install_tree, ecu01-basicsys, 0, 0, $(ECU01_BASICSYS_DIR)/usr/lib/fonts, /usr/lib/fonts) - #local environment - @$(call install_copy, ecu01-basicsys, 0, 0, 0755, $(ECU01_BASICSYS_DIR)/etc/profile.local, \ - /etc/profile.local) + #environment + @$(call install_copy, ecu01-basicsys, 0, 0, 0644, $(ECU01_BASICSYS_DIR)/etc/bash.bashrc, /etc/bash.bashrc) + @$(call install_copy, ecu01-basicsys, 0, 0, 0644, $(ECU01_BASICSYS_DIR)/etc/profile, /etc/profile) + @$(call install_copy, ecu01-basicsys, 0, 0, 0644, $(ECU01_BASICSYS_DIR)/etc/profile.local, /etc/profile.local) # SSH Key, Passphrase quantron-a @$(call install_copy, ecu01-basicsys, 0, 0, 0600, $(ECU01_BASICSYS_DIR)/etc/ssh/ecu_unix_rsa, /etc/ssh/ecu_unix_rsa)