Blame arrayfunc.c

Packit b5e560
/* arrayfunc.c -- High-level array functions used by other parts of the shell. */
Packit b5e560
Packit b5e560
/* Copyright (C) 2001-2016 Free Software Foundation, Inc.
Packit b5e560
Packit b5e560
   This file is part of GNU Bash, the Bourne Again SHell.
Packit b5e560
Packit b5e560
   Bash is free software: you can redistribute it and/or modify
Packit b5e560
   it under the terms of the GNU General Public License as published by
Packit b5e560
   the Free Software Foundation, either version 3 of the License, or
Packit b5e560
   (at your option) any later version.
Packit b5e560
Packit b5e560
   Bash is distributed in the hope that it will be useful,
Packit b5e560
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b5e560
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit b5e560
   GNU General Public License for more details.
Packit b5e560
Packit b5e560
   You should have received a copy of the GNU General Public License
Packit b5e560
   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
Packit b5e560
*/
Packit b5e560
Packit b5e560
#include "config.h"
Packit b5e560
Packit b5e560
#if defined (ARRAY_VARS)
Packit b5e560
Packit b5e560
#if defined (HAVE_UNISTD_H)
Packit b5e560
#  include <unistd.h>
Packit b5e560
#endif
Packit b5e560
#include <stdio.h>
Packit b5e560
Packit b5e560
#include "bashintl.h"
Packit b5e560
Packit b5e560
#include "shell.h"
Packit b5e560
#include "pathexp.h"
Packit b5e560
Packit b5e560
#include "shmbutil.h"
Packit b5e560
#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
Packit b5e560
#  include <mbstr.h>		/* mbschr */
Packit b5e560
#endif
Packit b5e560
Packit b5e560
#include "builtins/common.h"
Packit b5e560
Packit b5e560
extern char *this_command_name;
Packit b5e560
extern int last_command_exit_value;
Packit b5e560
extern int array_needs_making;
Packit b5e560
Packit b5e560
static SHELL_VAR *bind_array_var_internal __P((SHELL_VAR *, arrayind_t, char *, char *, int));
Packit b5e560
static SHELL_VAR *assign_array_element_internal __P((SHELL_VAR *, char *, char *, char *, int, char *, int));
Packit b5e560
Packit b5e560
static char *quote_assign __P((const char *));
Packit b5e560
static void quote_array_assignment_chars __P((WORD_LIST *));
Packit b5e560
static char *array_value_internal __P((const char *, int, int, int *, arrayind_t *));
Packit b5e560
Packit b5e560
/* Standard error message to use when encountering an invalid array subscript */
Packit b5e560
const char * const bash_badsub_errmsg = N_("bad array subscript");
Packit b5e560
Packit b5e560
/* **************************************************************** */
Packit b5e560
/*								    */
Packit b5e560
/*  Functions to manipulate array variables and perform assignments */
Packit b5e560
/*								    */
Packit b5e560
/* **************************************************************** */
Packit b5e560
Packit b5e560
/* Convert a shell variable to an array variable.  The original value is
Packit b5e560
   saved as array[0]. */
Packit b5e560
SHELL_VAR *
Packit b5e560
convert_var_to_array (var)
Packit b5e560
     SHELL_VAR *var;
Packit b5e560
{
Packit b5e560
  char *oldval;
Packit b5e560
  ARRAY *array;
Packit b5e560
Packit b5e560
  oldval = value_cell (var);
Packit b5e560
  array = array_create ();
Packit b5e560
  if (oldval)
Packit b5e560
    array_insert (array, 0, oldval);
Packit b5e560
Packit b5e560
  FREE (value_cell (var));
Packit b5e560
  var_setarray (var, array);
Packit b5e560
Packit b5e560
  /* these aren't valid anymore */
Packit b5e560
  var->dynamic_value = (sh_var_value_func_t *)NULL;
Packit b5e560
  var->assign_func = (sh_var_assign_func_t *)NULL;
Packit b5e560
Packit b5e560
  INVALIDATE_EXPORTSTR (var);
Packit b5e560
  if (exported_p (var))
Packit b5e560
    array_needs_making++;
Packit b5e560
Packit b5e560
  VSETATTR (var, att_array);
Packit b5e560
  VUNSETATTR (var, att_invisible);
Packit b5e560
Packit b5e560
  /* Make sure it's not marked as an associative array any more */
Packit b5e560
  VUNSETATTR (var, att_assoc);
Packit b5e560
Packit b5e560
  /* Since namerefs can't be array variables, turn off nameref attribute */
Packit b5e560
  VUNSETATTR (var, att_nameref);
Packit b5e560
Packit b5e560
  return var;
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Convert a shell variable to an array variable.  The original value is
Packit b5e560
   saved as array[0]. */
Packit b5e560
SHELL_VAR *
Packit b5e560
convert_var_to_assoc (var)
Packit b5e560
     SHELL_VAR *var;
Packit b5e560
{
Packit b5e560
  char *oldval;
Packit b5e560
  HASH_TABLE *hash;
Packit b5e560
Packit b5e560
  oldval = value_cell (var);
Packit b5e560
  hash = assoc_create (0);
Packit b5e560
  if (oldval)
Packit b5e560
    assoc_insert (hash, savestring ("0"), oldval);
Packit b5e560
Packit b5e560
  FREE (value_cell (var));
Packit b5e560
  var_setassoc (var, hash);
Packit b5e560
Packit b5e560
  /* these aren't valid anymore */
Packit b5e560
  var->dynamic_value = (sh_var_value_func_t *)NULL;
Packit b5e560
  var->assign_func = (sh_var_assign_func_t *)NULL;
Packit b5e560
Packit b5e560
  INVALIDATE_EXPORTSTR (var);
Packit b5e560
  if (exported_p (var))
Packit b5e560
    array_needs_making++;
Packit b5e560
Packit b5e560
  VSETATTR (var, att_assoc);
Packit b5e560
  VUNSETATTR (var, att_invisible);
Packit b5e560
Packit b5e560
  /* Make sure it's not marked as an indexed array any more */
Packit b5e560
  VUNSETATTR (var, att_array);
Packit b5e560
Packit b5e560
  /* Since namerefs can't be array variables, turn off nameref attribute */
Packit b5e560
  VUNSETATTR (var, att_nameref);
Packit b5e560
Packit b5e560
  return var;
Packit b5e560
}
Packit b5e560
Packit b5e560
char *
Packit b5e560
make_array_variable_value (entry, ind, key, value, flags)
Packit b5e560
     SHELL_VAR *entry;
Packit b5e560
     arrayind_t ind;
Packit b5e560
     char *key;
Packit b5e560
     char *value;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  SHELL_VAR *dentry;
Packit b5e560
  char *newval;
Packit b5e560
Packit b5e560
  /* If we're appending, we need the old value of the array reference, so
Packit b5e560
     fake out make_variable_value with a dummy SHELL_VAR */
Packit b5e560
  if (flags & ASS_APPEND)
Packit b5e560
    {
Packit b5e560
      dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
Packit b5e560
      dentry->name = savestring (entry->name);
Packit b5e560
      if (assoc_p (entry))
Packit b5e560
	newval = assoc_reference (assoc_cell (entry), key);
Packit b5e560
      else
Packit b5e560
	newval = array_reference (array_cell (entry), ind);
Packit b5e560
      if (newval)
Packit b5e560
	dentry->value = savestring (newval);
Packit b5e560
      else
Packit b5e560
	{
Packit b5e560
	  dentry->value = (char *)xmalloc (1);
Packit b5e560
	  dentry->value[0] = '\0';
Packit b5e560
	}
Packit b5e560
      dentry->exportstr = 0;
Packit b5e560
      dentry->attributes = entry->attributes & ~(att_array|att_assoc|att_exported);
Packit b5e560
      /* Leave the rest of the members uninitialized; the code doesn't look
Packit b5e560
	 at them. */
Packit b5e560
      newval = make_variable_value (dentry, value, flags);	 
Packit b5e560
      dispose_variable (dentry);
Packit b5e560
    }
Packit b5e560
  else
Packit b5e560
    newval = make_variable_value (entry, value, flags);
Packit b5e560
Packit b5e560
  return newval;
Packit b5e560
}
Packit b5e560
  
Packit b5e560
static SHELL_VAR *
Packit b5e560
bind_array_var_internal (entry, ind, key, value, flags)
Packit b5e560
     SHELL_VAR *entry;
Packit b5e560
     arrayind_t ind;
Packit b5e560
     char *key;
Packit b5e560
     char *value;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  char *newval;
Packit b5e560
Packit b5e560
  newval = make_array_variable_value (entry, ind, key, value, flags);
Packit b5e560
Packit b5e560
  if (entry->assign_func)
Packit b5e560
    (*entry->assign_func) (entry, newval, ind, key);
Packit b5e560
  else if (assoc_p (entry))
Packit b5e560
    assoc_insert (assoc_cell (entry), key, newval);
Packit b5e560
  else
Packit b5e560
    array_insert (array_cell (entry), ind, newval);
Packit b5e560
  FREE (newval);
Packit b5e560
Packit b5e560
  VUNSETATTR (entry, att_invisible);	/* no longer invisible */
Packit b5e560
  return (entry);
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Perform an array assignment name[ind]=value.  If NAME already exists and
Packit b5e560
   is not an array, and IND is 0, perform name=value instead.  If NAME exists
Packit b5e560
   and is not an array, and IND is not 0, convert it into an array with the
Packit b5e560
   existing value as name[0].
Packit b5e560
Packit b5e560
   If NAME does not exist, just create an array variable, no matter what
Packit b5e560
   IND's value may be. */
Packit b5e560
SHELL_VAR *
Packit b5e560
bind_array_variable (name, ind, value, flags)
Packit b5e560
     char *name;
Packit b5e560
     arrayind_t ind;
Packit b5e560
     char *value;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  SHELL_VAR *entry;
Packit b5e560
Packit b5e560
  entry = find_shell_variable (name);
Packit b5e560
Packit b5e560
  if (entry == (SHELL_VAR *) 0)
Packit b5e560
    {
Packit b5e560
      /* Is NAME a nameref variable that points to an unset variable? */
Packit b5e560
      entry = find_variable_nameref_for_create (name, 0);
Packit b5e560
      if (entry == INVALID_NAMEREF_VALUE)
Packit b5e560
	return ((SHELL_VAR *)0);
Packit b5e560
      if (entry && nameref_p (entry))
Packit b5e560
	entry = make_new_array_variable (nameref_cell (entry));
Packit b5e560
    }
Packit b5e560
  if (entry == (SHELL_VAR *) 0)
Packit b5e560
    entry = make_new_array_variable (name);
Packit b5e560
  else if ((readonly_p (entry) && (flags&ASS_FORCE) == 0) || noassign_p (entry))
Packit b5e560
    {
Packit b5e560
      if (readonly_p (entry))
Packit b5e560
	err_readonly (name);
Packit b5e560
      return (entry);
Packit b5e560
    }
Packit b5e560
  else if (array_p (entry) == 0)
Packit b5e560
    entry = convert_var_to_array (entry);
Packit b5e560
Packit b5e560
  /* ENTRY is an array variable, and ARRAY points to the value. */
Packit b5e560
  return (bind_array_var_internal (entry, ind, 0, value, flags));
Packit b5e560
}
Packit b5e560
Packit b5e560
SHELL_VAR *
Packit b5e560
bind_array_element (entry, ind, value, flags)
Packit b5e560
     SHELL_VAR *entry;
Packit b5e560
     arrayind_t ind;
Packit b5e560
     char *value;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  return (bind_array_var_internal (entry, ind, 0, value, flags));
Packit b5e560
}
Packit b5e560
                    
Packit b5e560
SHELL_VAR *
Packit b5e560
bind_assoc_variable (entry, name, key, value, flags)
Packit b5e560
     SHELL_VAR *entry;
Packit b5e560
     char *name;
Packit b5e560
     char *key;
Packit b5e560
     char *value;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  SHELL_VAR *dentry;
Packit b5e560
  char *newval;
Packit b5e560
Packit b5e560
  if ((readonly_p (entry) && (flags&ASS_FORCE) == 0) || noassign_p (entry))
Packit b5e560
    {
Packit b5e560
      if (readonly_p (entry))
Packit b5e560
	err_readonly (name);
Packit b5e560
      return (entry);
Packit b5e560
    }
Packit b5e560
Packit b5e560
  return (bind_array_var_internal (entry, 0, key, value, flags));
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Parse NAME, a lhs of an assignment statement of the form v[s], and
Packit b5e560
   assign VALUE to that array element by calling bind_array_variable(). */
Packit b5e560
SHELL_VAR *
Packit b5e560
assign_array_element (name, value, flags)
Packit b5e560
     char *name, *value;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  char *sub, *vname;
Packit b5e560
  int sublen;
Packit b5e560
  SHELL_VAR *entry, *nv;
Packit b5e560
Packit b5e560
  vname = array_variable_name (name, &sub, &sublen);
Packit b5e560
Packit b5e560
  if (vname == 0)
Packit b5e560
    return ((SHELL_VAR *)NULL);
Packit b5e560
Packit b5e560
  if ((ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']') || (sublen <= 1))
Packit b5e560
    {
Packit b5e560
      free (vname);
Packit b5e560
      err_badarraysub (name);
Packit b5e560
      return ((SHELL_VAR *)NULL);
Packit b5e560
    }
Packit b5e560
Packit b5e560
  entry = find_variable (vname);
Packit b5e560
  entry = assign_array_element_internal (entry, name, vname, sub, sublen, value, flags);
Packit b5e560
Packit b5e560
  free (vname);
Packit b5e560
  return entry;
Packit b5e560
}
Packit b5e560
Packit b5e560
static SHELL_VAR *
Packit b5e560
assign_array_element_internal (entry, name, vname, sub, sublen, value, flags)
Packit b5e560
     SHELL_VAR *entry;
Packit b5e560
     char *name;		/* only used for error messages */
Packit b5e560
     char *vname;
Packit b5e560
     char *sub;
Packit b5e560
     int sublen;
Packit b5e560
     char *value;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  char *akey;
Packit b5e560
  arrayind_t ind;
Packit b5e560
Packit b5e560
  if (entry && assoc_p (entry))
Packit b5e560
    {
Packit b5e560
      sub[sublen-1] = '\0';
Packit b5e560
      akey = expand_assignment_string_to_string (sub, 0);	/* [ */
Packit b5e560
      sub[sublen-1] = ']';
Packit b5e560
      if (akey == 0 || *akey == 0)
Packit b5e560
	{
Packit b5e560
	  err_badarraysub (name);
Packit b5e560
	  FREE (akey);
Packit b5e560
	  return ((SHELL_VAR *)NULL);
Packit b5e560
	}
Packit b5e560
      entry = bind_assoc_variable (entry, vname, akey, value, flags);
Packit b5e560
    }
Packit b5e560
  else
Packit b5e560
    {
Packit b5e560
      ind = array_expand_index (entry, sub, sublen);
Packit b5e560
      /* negative subscripts to indexed arrays count back from end */
Packit b5e560
      if (entry && ind < 0)
Packit b5e560
	ind = (array_p (entry) ? array_max_index (array_cell (entry)) : 0) + 1 + ind;
Packit b5e560
      if (ind < 0)
Packit b5e560
	{
Packit b5e560
	  err_badarraysub (name);
Packit b5e560
	  return ((SHELL_VAR *)NULL);
Packit b5e560
	}
Packit b5e560
      entry = bind_array_variable (vname, ind, value, flags);
Packit b5e560
    }
Packit b5e560
Packit b5e560
  return (entry);
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Find the array variable corresponding to NAME.  If there is no variable,
Packit b5e560
   create a new array variable.  If the variable exists but is not an array,
Packit b5e560
   convert it to an indexed array.  If FLAGS&1 is non-zero, an existing
Packit b5e560
   variable is checked for the readonly or noassign attribute in preparation
Packit b5e560
   for assignment (e.g., by the `read' builtin).  If FLAGS&2 is non-zero, we
Packit b5e560
   create an associative array. */
Packit b5e560
SHELL_VAR *
Packit b5e560
find_or_make_array_variable (name, flags)
Packit b5e560
     char *name;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  SHELL_VAR *var;
Packit b5e560
Packit b5e560
  var = find_variable (name);
Packit b5e560
  if (var == 0)
Packit b5e560
    {
Packit b5e560
      /* See if we have a nameref pointing to a variable that hasn't been
Packit b5e560
	 created yet. */
Packit b5e560
      var = find_variable_last_nameref (name, 1);
Packit b5e560
      if (var && nameref_p (var) && invisible_p (var))
Packit b5e560
	{
Packit b5e560
	  internal_warning (_("%s: removing nameref attribute"), name);
Packit b5e560
	  VUNSETATTR (var, att_nameref);
Packit b5e560
	}
Packit b5e560
      if (var && nameref_p (var))
Packit b5e560
	{
Packit b5e560
	  if (valid_nameref_value (nameref_cell (var), 2) == 0)
Packit b5e560
	    {
Packit b5e560
	      sh_invalidid (nameref_cell (var));
Packit b5e560
	      return ((SHELL_VAR *)NULL);
Packit b5e560
	    }
Packit b5e560
	  var = (flags & 2) ? make_new_assoc_variable (nameref_cell (var)) : make_new_array_variable (nameref_cell (var));
Packit b5e560
	}
Packit b5e560
    }
Packit b5e560
Packit b5e560
  if (var == 0)
Packit b5e560
    var = (flags & 2) ? make_new_assoc_variable (name) : make_new_array_variable (name);
Packit b5e560
  else if ((flags & 1) && (readonly_p (var) || noassign_p (var)))
Packit b5e560
    {
Packit b5e560
      if (readonly_p (var))
Packit b5e560
	err_readonly (name);
Packit b5e560
      return ((SHELL_VAR *)NULL);
Packit b5e560
    }
Packit b5e560
  else if ((flags & 2) && array_p (var))
Packit b5e560
    {
Packit b5e560
      last_command_exit_value = 1;
Packit b5e560
      report_error (_("%s: cannot convert indexed to associative array"), name);
Packit b5e560
      return ((SHELL_VAR *)NULL);
Packit b5e560
    }
Packit b5e560
  else if (array_p (var) == 0 && assoc_p (var) == 0)
Packit b5e560
    var = convert_var_to_array (var);
Packit b5e560
Packit b5e560
  return (var);
Packit b5e560
}
Packit b5e560
  
Packit b5e560
/* Perform a compound assignment statement for array NAME, where VALUE is
Packit b5e560
   the text between the parens:  NAME=( VALUE ) */
Packit b5e560
SHELL_VAR *
Packit b5e560
assign_array_from_string (name, value, flags)
Packit b5e560
     char *name, *value;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  SHELL_VAR *var;
Packit b5e560
  int vflags;
Packit b5e560
Packit b5e560
  vflags = 1;
Packit b5e560
  if (flags & ASS_MKASSOC)
Packit b5e560
    vflags |= 2;
Packit b5e560
Packit b5e560
  var = find_or_make_array_variable (name, vflags);
Packit b5e560
  if (var == 0)
Packit b5e560
    return ((SHELL_VAR *)NULL);
Packit b5e560
Packit b5e560
  return (assign_array_var_from_string (var, value, flags));
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Sequentially assign the indices of indexed array variable VAR from the
Packit b5e560
   words in LIST. */
Packit b5e560
SHELL_VAR *
Packit b5e560
assign_array_var_from_word_list (var, list, flags)
Packit b5e560
     SHELL_VAR *var;
Packit b5e560
     WORD_LIST *list;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  register arrayind_t i;
Packit b5e560
  register WORD_LIST *l;
Packit b5e560
  ARRAY *a;
Packit b5e560
Packit b5e560
  a = array_cell (var);
Packit b5e560
  i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
Packit b5e560
Packit b5e560
  for (l = list; l; l = l->next, i++)
Packit b5e560
    bind_array_var_internal (var, i, 0, l->word->word, flags & ~ASS_APPEND);
Packit b5e560
Packit b5e560
  VUNSETATTR (var, att_invisible);	/* no longer invisible */
Packit b5e560
Packit b5e560
  return var;
Packit b5e560
}
Packit b5e560
Packit b5e560
WORD_LIST *
Packit b5e560
expand_compound_array_assignment (var, value, flags)
Packit b5e560
     SHELL_VAR *var;
Packit b5e560
     char *value;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  WORD_LIST *list, *nlist;
Packit b5e560
  WORD_LIST *hd, *tl, *t, *n;
Packit b5e560
  char *val;
Packit b5e560
  int ni;
Packit b5e560
Packit b5e560
  /* This condition is true when invoked from the declare builtin with a
Packit b5e560
     command like
Packit b5e560
	declare -a d='([1]="" [2]="bdef" [5]="hello world" "test")' */
Packit b5e560
  if (*value == '(')	/*)*/
Packit b5e560
    {
Packit b5e560
      ni = 1;
Packit b5e560
      val = extract_array_assignment_list (value, &ni);
Packit b5e560
      if (val == 0)
Packit b5e560
	return (WORD_LIST *)NULL;
Packit b5e560
    }
Packit b5e560
  else
Packit b5e560
    val = value;
Packit b5e560
Packit b5e560
  /* Expand the value string into a list of words, performing all the
Packit b5e560
     shell expansions including pathname generation and word splitting. */
Packit b5e560
  /* First we split the string on whitespace, using the shell parser
Packit b5e560
     (ksh93 seems to do this). */
Packit b5e560
  list = parse_string_to_word_list (val, 1, "array assign");
Packit b5e560
Packit b5e560
  if (var && assoc_p (var))
Packit b5e560
    {
Packit b5e560
      if (val != value)
Packit b5e560
	free (val);
Packit b5e560
      return list;
Packit b5e560
    }
Packit b5e560
Packit b5e560
  /* If we're using [subscript]=value, we need to quote each [ and ] to
Packit b5e560
     prevent unwanted filename expansion.  This doesn't need to be done
Packit b5e560
     for associative array expansion, since that uses a different expansion
Packit b5e560
     function (see assign_compound_array_list below). */
Packit b5e560
  if (list)
Packit b5e560
    quote_array_assignment_chars (list);
Packit b5e560
Packit b5e560
  /* Now that we've split it, perform the shell expansions on each
Packit b5e560
     word in the list. */
Packit b5e560
  nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;
Packit b5e560
Packit b5e560
  dispose_words (list);
Packit b5e560
Packit b5e560
  if (val != value)
Packit b5e560
    free (val);
Packit b5e560
Packit b5e560
  return nlist;
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Callers ensure that VAR is not NULL */
Packit b5e560
void
Packit b5e560
assign_compound_array_list (var, nlist, flags)
Packit b5e560
     SHELL_VAR *var;
Packit b5e560
     WORD_LIST *nlist;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  ARRAY *a;
Packit b5e560
  HASH_TABLE *h;
Packit b5e560
  WORD_LIST *list;
Packit b5e560
  char *w, *val, *nval, *savecmd;
Packit b5e560
  int len, iflags, free_val;
Packit b5e560
  arrayind_t ind, last_ind;
Packit b5e560
  char *akey;
Packit b5e560
Packit b5e560
  a = (var && array_p (var)) ? array_cell (var) : (ARRAY *)0;
Packit b5e560
  h = (var && assoc_p (var)) ? assoc_cell (var) : (HASH_TABLE *)0;
Packit b5e560
Packit b5e560
  akey = (char *)0;
Packit b5e560
  ind = 0;
Packit b5e560
Packit b5e560
  /* Now that we are ready to assign values to the array, kill the existing
Packit b5e560
     value. */
Packit b5e560
  if ((flags & ASS_APPEND) == 0)
Packit b5e560
    {
Packit b5e560
      if (a && array_p (var))
Packit b5e560
	array_flush (a);
Packit b5e560
      else if (h && assoc_p (var))
Packit b5e560
	assoc_flush (h);
Packit b5e560
    }
Packit b5e560
Packit b5e560
  last_ind = (a && (flags & ASS_APPEND)) ? array_max_index (a) + 1 : 0;
Packit b5e560
Packit b5e560
  for (list = nlist; list; list = list->next)
Packit b5e560
    {
Packit b5e560
      /* Don't allow var+=(values) to make assignments in VALUES append to
Packit b5e560
	 existing values by default. */
Packit b5e560
      iflags = flags & ~ASS_APPEND;
Packit b5e560
      w = list->word->word;
Packit b5e560
Packit b5e560
      /* We have a word of the form [ind]=value */
Packit b5e560
      if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[')
Packit b5e560
	{
Packit b5e560
	  /* Don't have to handle embedded quotes specially any more, since
Packit b5e560
	     associative array subscripts have not been expanded yet (see
Packit b5e560
	     above). */
Packit b5e560
	  len = skipsubscript (w, 0, 0);
Packit b5e560
Packit b5e560
	  /* XXX - changes for `+=' */
Packit b5e560
 	  if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
Packit b5e560
	    {
Packit b5e560
	      if (assoc_p (var))
Packit b5e560
		{
Packit b5e560
		  err_badarraysub (w);
Packit b5e560
		  continue;
Packit b5e560
		}
Packit b5e560
	      nval = make_variable_value (var, w, flags);
Packit b5e560
	      if (var->assign_func)
Packit b5e560
		(*var->assign_func) (var, nval, last_ind, 0);
Packit b5e560
	      else
Packit b5e560
		array_insert (a, last_ind, nval);
Packit b5e560
	      FREE (nval);
Packit b5e560
	      last_ind++;
Packit b5e560
	      continue;
Packit b5e560
	    }
Packit b5e560
Packit b5e560
	  if (len == 1)
Packit b5e560
	    {
Packit b5e560
	      err_badarraysub (w);
Packit b5e560
	      continue;
Packit b5e560
	    }
Packit b5e560
Packit b5e560
	  if (ALL_ELEMENT_SUB (w[1]) && len == 2)
Packit b5e560
	    {
Packit b5e560
	      last_command_exit_value = 1;
Packit b5e560
	      if (assoc_p (var))
Packit b5e560
		report_error (_("%s: invalid associative array key"), w);
Packit b5e560
	      else
Packit b5e560
		report_error (_("%s: cannot assign to non-numeric index"), w);
Packit b5e560
	      continue;
Packit b5e560
	    }
Packit b5e560
Packit b5e560
	  if (array_p (var))
Packit b5e560
	    {
Packit b5e560
	      ind = array_expand_index (var, w + 1, len);
Packit b5e560
	      /* negative subscripts to indexed arrays count back from end */
Packit b5e560
	      if (ind < 0)
Packit b5e560
		ind = array_max_index (array_cell (var)) + 1 + ind;
Packit b5e560
	      if (ind < 0)
Packit b5e560
		{
Packit b5e560
		  err_badarraysub (w);
Packit b5e560
		  continue;
Packit b5e560
		}
Packit b5e560
Packit b5e560
	      last_ind = ind;
Packit b5e560
	    }
Packit b5e560
	  else if (assoc_p (var))
Packit b5e560
	    {
Packit b5e560
	      /* This is not performed above, see expand_compound_array_assignment */
Packit b5e560
	      w[len] = '\0';	/*[*/
Packit b5e560
	      akey = expand_assignment_string_to_string (w+1, 0);
Packit b5e560
	      w[len] = ']';
Packit b5e560
	      /* And we need to expand the value also, see below */
Packit b5e560
	      if (akey == 0 || *akey == 0)
Packit b5e560
		{
Packit b5e560
		  err_badarraysub (w);
Packit b5e560
		  FREE (akey);
Packit b5e560
		  continue;
Packit b5e560
		}
Packit b5e560
	    }
Packit b5e560
Packit b5e560
	  /* XXX - changes for `+=' -- just accept the syntax.  ksh93 doesn't do this */
Packit b5e560
	  if (w[len + 1] == '+' && w[len + 2] == '=')
Packit b5e560
	    {
Packit b5e560
	      iflags |= ASS_APPEND;
Packit b5e560
	      val = w + len + 3;
Packit b5e560
	    }
Packit b5e560
	  else
Packit b5e560
	    val = w + len + 2;	    
Packit b5e560
	}
Packit b5e560
      else if (assoc_p (var))
Packit b5e560
	{
Packit b5e560
	  last_command_exit_value = 1;
Packit b5e560
	  report_error (_("%s: %s: must use subscript when assigning associative array"), var->name, w);
Packit b5e560
	  continue;
Packit b5e560
	}
Packit b5e560
      else		/* No [ind]=value, just a stray `=' */
Packit b5e560
	{
Packit b5e560
	  ind = last_ind;
Packit b5e560
	  val = w;
Packit b5e560
	}
Packit b5e560
Packit b5e560
      free_val = 0;
Packit b5e560
      /* See above; we need to expand the value here */
Packit b5e560
      if (assoc_p (var))
Packit b5e560
	{
Packit b5e560
	  val = expand_assignment_string_to_string (val, 0);
Packit b5e560
	  if (val == 0)
Packit b5e560
	    {
Packit b5e560
	      val = (char *)xmalloc (1);
Packit b5e560
	      val[0] = '\0';	/* like do_assignment_internal */
Packit b5e560
	    }
Packit b5e560
	  free_val = 1;
Packit b5e560
	}
Packit b5e560
Packit b5e560
      savecmd = this_command_name;
Packit b5e560
      if (integer_p (var))
Packit b5e560
	this_command_name = (char *)NULL;	/* no command name for errors */
Packit b5e560
      bind_array_var_internal (var, ind, akey, val, iflags);
Packit b5e560
      last_ind++;
Packit b5e560
      this_command_name = savecmd;
Packit b5e560
Packit b5e560
      if (free_val)
Packit b5e560
	free (val);
Packit b5e560
    }
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Perform a compound array assignment:  VAR->name=( VALUE ).  The
Packit b5e560
   VALUE has already had the parentheses stripped. */
Packit b5e560
SHELL_VAR *
Packit b5e560
assign_array_var_from_string (var, value, flags)
Packit b5e560
     SHELL_VAR *var;
Packit b5e560
     char *value;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  WORD_LIST *nlist;
Packit b5e560
Packit b5e560
  if (value == 0)
Packit b5e560
    return var;
Packit b5e560
Packit b5e560
  nlist = expand_compound_array_assignment (var, value, flags);
Packit b5e560
  assign_compound_array_list (var, nlist, flags);
Packit b5e560
Packit b5e560
  if (nlist)
Packit b5e560
    dispose_words (nlist);
Packit b5e560
Packit b5e560
  if (var)
Packit b5e560
    VUNSETATTR (var, att_invisible);	/* no longer invisible */
Packit b5e560
Packit b5e560
  return (var);
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Quote globbing chars and characters in $IFS before the `=' in an assignment
Packit b5e560
   statement (usually a compound array assignment) to protect them from
Packit b5e560
   unwanted filename expansion or word splitting. */
Packit b5e560
static char *
Packit b5e560
quote_assign (string)
Packit b5e560
     const char *string;
Packit b5e560
{
Packit b5e560
  size_t slen;
Packit b5e560
  int saw_eq;
Packit b5e560
  char *temp, *t, *subs;
Packit b5e560
  const char *s, *send;
Packit b5e560
  int ss, se;
Packit b5e560
  DECLARE_MBSTATE;
Packit b5e560
Packit b5e560
  slen = strlen (string);
Packit b5e560
  send = string + slen;
Packit b5e560
Packit b5e560
  t = temp = (char *)xmalloc (slen * 2 + 1);
Packit b5e560
  saw_eq = 0;
Packit b5e560
  for (s = string; *s; )
Packit b5e560
    {
Packit b5e560
      if (*s == '=')
Packit b5e560
	saw_eq = 1;
Packit b5e560
      if (saw_eq == 0 && *s == '[')		/* looks like a subscript */
Packit b5e560
	{
Packit b5e560
	  ss = s - string;
Packit b5e560
	  se = skipsubscript (string, ss, 0);
Packit b5e560
	  subs = substring (s, ss, se);
Packit b5e560
	  *t++ = '\\';
Packit b5e560
	  strcpy (t, subs);
Packit b5e560
	  t += se - ss;
Packit b5e560
	  *t++ = '\\';
Packit b5e560
	  *t++ = ']';
Packit b5e560
	  s += se + 1;
Packit b5e560
	  free (subs);
Packit b5e560
	  continue;
Packit b5e560
	}
Packit b5e560
      if (saw_eq == 0 && (glob_char_p (s) || isifs (*s)))
Packit b5e560
	*t++ = '\\';
Packit b5e560
Packit b5e560
      COPY_CHAR_P (t, s, send);
Packit b5e560
    }
Packit b5e560
  *t = '\0';
Packit b5e560
  return temp;
Packit b5e560
}
Packit b5e560
Packit b5e560
/* For each word in a compound array assignment, if the word looks like
Packit b5e560
   [ind]=value, quote globbing chars and characters in $IFS before the `='. */
Packit b5e560
static void
Packit b5e560
quote_array_assignment_chars (list)
Packit b5e560
     WORD_LIST *list;
Packit b5e560
{
Packit b5e560
  char *nword;
Packit b5e560
  WORD_LIST *l;
Packit b5e560
Packit b5e560
  for (l = list; l; l = l->next)
Packit b5e560
    {
Packit b5e560
      if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
Packit b5e560
	continue;	/* should not happen, but just in case... */
Packit b5e560
      /* Don't bother if it hasn't been recognized as an assignment or
Packit b5e560
	 doesn't look like [ind]=value */
Packit b5e560
      if ((l->word->flags & W_ASSIGNMENT) == 0)
Packit b5e560
	continue;
Packit b5e560
      if (l->word->word[0] != '[' || mbschr (l->word->word, '=') == 0) /* ] */
Packit b5e560
	continue;
Packit b5e560
Packit b5e560
      nword = quote_assign (l->word->word);
Packit b5e560
      free (l->word->word);
Packit b5e560
      l->word->word = nword;
Packit b5e560
      l->word->flags |= W_NOGLOB;	/* XXX - W_NOSPLIT also? */
Packit b5e560
    }
Packit b5e560
}
Packit b5e560
Packit b5e560
/* skipsubscript moved to subst.c to use private functions. 2009/02/24. */
Packit b5e560
Packit b5e560
/* This function is called with SUB pointing to just after the beginning
Packit b5e560
   `[' of an array subscript and removes the array element to which SUB
Packit b5e560
   expands from array VAR.  A subscript of `*' or `@' unsets the array. */
Packit b5e560
int
Packit b5e560
unbind_array_element (var, sub)
Packit b5e560
     SHELL_VAR *var;
Packit b5e560
     char *sub;
Packit b5e560
{
Packit b5e560
  int len;
Packit b5e560
  arrayind_t ind;
Packit b5e560
  char *akey;
Packit b5e560
  ARRAY_ELEMENT *ae;
Packit b5e560
Packit b5e560
  len = skipsubscript (sub, 0, (var && assoc_p(var)));
Packit b5e560
  if (sub[len] != ']' || len == 0)
Packit b5e560
    {
Packit b5e560
      builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg));
Packit b5e560
      return -1;
Packit b5e560
    }
Packit b5e560
  sub[len] = '\0';
Packit b5e560
Packit b5e560
  if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
Packit b5e560
    {
Packit b5e560
      if (array_p (var) || assoc_p (var))
Packit b5e560
	{
Packit b5e560
	  unbind_variable (var->name);	/* XXX -- {array,assoc}_flush ? */
Packit b5e560
	  return (0);
Packit b5e560
	}
Packit b5e560
      else
Packit b5e560
	return -2;	/* don't allow this to unset scalar variables */
Packit b5e560
    }
Packit b5e560
Packit b5e560
  if (assoc_p (var))
Packit b5e560
    {
Packit b5e560
      akey = expand_assignment_string_to_string (sub, 0);     /* [ */
Packit b5e560
      if (akey == 0 || *akey == 0)
Packit b5e560
	{
Packit b5e560
	  builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
Packit b5e560
	  FREE (akey);
Packit b5e560
	  return -1;
Packit b5e560
	}
Packit b5e560
      assoc_remove (assoc_cell (var), akey);
Packit b5e560
      free (akey);
Packit b5e560
    }
Packit b5e560
  else if (array_p (var))
Packit b5e560
    {
Packit b5e560
      ind = array_expand_index (var, sub, len+1);
Packit b5e560
      /* negative subscripts to indexed arrays count back from end */
Packit b5e560
      if (ind < 0)
Packit b5e560
	ind = array_max_index (array_cell (var)) + 1 + ind;
Packit b5e560
      if (ind < 0)
Packit b5e560
	{
Packit b5e560
	  builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
Packit b5e560
	  return -1;
Packit b5e560
	}
Packit b5e560
      ae = array_remove (array_cell (var), ind);
Packit b5e560
      if (ae)
Packit b5e560
	array_dispose_element (ae);
Packit b5e560
    }
Packit b5e560
  else	/* array_p (var) == 0 && assoc_p (var) == 0 */
Packit b5e560
    {
Packit b5e560
      akey = this_command_name;
Packit b5e560
      ind = array_expand_index (var, sub, len+1);
Packit b5e560
      this_command_name = akey;
Packit b5e560
      if (ind == 0)
Packit b5e560
	{
Packit b5e560
	  unbind_variable (var->name);
Packit b5e560
	  return (0);
Packit b5e560
	}
Packit b5e560
      else
Packit b5e560
	return -2;	/* any subscript other than 0 is invalid with scalar variables */
Packit b5e560
    }
Packit b5e560
Packit b5e560
  return 0;
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Format and output an array assignment in compound form VAR=(VALUES),
Packit b5e560
   suitable for re-use as input. */
Packit b5e560
void
Packit b5e560
print_array_assignment (var, quoted)
Packit b5e560
     SHELL_VAR *var;
Packit b5e560
     int quoted;
Packit b5e560
{
Packit b5e560
  char *vstr;
Packit b5e560
Packit b5e560
  vstr = array_to_assign (array_cell (var), quoted);
Packit b5e560
Packit b5e560
  if (vstr == 0)
Packit b5e560
    printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
Packit b5e560
  else
Packit b5e560
    {
Packit b5e560
      printf ("%s=%s\n", var->name, vstr);
Packit b5e560
      free (vstr);
Packit b5e560
    }
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Format and output an associative array assignment in compound form
Packit b5e560
   VAR=(VALUES), suitable for re-use as input. */
Packit b5e560
void
Packit b5e560
print_assoc_assignment (var, quoted)
Packit b5e560
     SHELL_VAR *var;
Packit b5e560
     int quoted;
Packit b5e560
{
Packit b5e560
  char *vstr;
Packit b5e560
Packit b5e560
  vstr = assoc_to_assign (assoc_cell (var), quoted);
Packit b5e560
Packit b5e560
  if (vstr == 0)
Packit b5e560
    printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
Packit b5e560
  else
Packit b5e560
    {
Packit b5e560
      printf ("%s=%s\n", var->name, vstr);
Packit b5e560
      free (vstr);
Packit b5e560
    }
Packit b5e560
}
Packit b5e560
Packit b5e560
/***********************************************************************/
Packit b5e560
/*								       */
Packit b5e560
/* Utility functions to manage arrays and their contents for expansion */
Packit b5e560
/*								       */
Packit b5e560
/***********************************************************************/
Packit b5e560
Packit b5e560
/* Return 1 if NAME is a properly-formed array reference v[sub]. */
Packit b5e560
int
Packit b5e560
valid_array_reference (name, flags)
Packit b5e560
     const char *name;
Packit b5e560
     int flags;
Packit b5e560
{
Packit b5e560
  char *t;
Packit b5e560
  int r, len;
Packit b5e560
Packit b5e560
  t = mbschr (name, '[');	/* ] */
Packit b5e560
  if (t)
Packit b5e560
    {
Packit b5e560
      *t = '\0';
Packit b5e560
      r = legal_identifier (name);
Packit b5e560
      *t = '[';
Packit b5e560
      if (r == 0)
Packit b5e560
	return 0;
Packit b5e560
      /* Check for a properly-terminated non-blank subscript. */
Packit b5e560
      len = skipsubscript (t, 0, 0);
Packit b5e560
      if (t[len] != ']' || len == 1)
Packit b5e560
	return 0;
Packit b5e560
      if (t[len+1] != '\0')
Packit b5e560
	return 0;
Packit b5e560
      for (r = 1; r < len; r++)
Packit b5e560
	if (whitespace (t[r]) == 0)
Packit b5e560
	  return 1;
Packit b5e560
      return 0;
Packit b5e560
    }
Packit b5e560
  return 0;
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Expand the array index beginning at S and extending LEN characters. */
Packit b5e560
arrayind_t
Packit b5e560
array_expand_index (var, s, len)
Packit b5e560
     SHELL_VAR *var;
Packit b5e560
     char *s;
Packit b5e560
     int len;
Packit b5e560
{
Packit b5e560
  char *exp, *t, *savecmd;
Packit b5e560
  int expok;
Packit b5e560
  arrayind_t val;
Packit b5e560
Packit b5e560
  exp = (char *)xmalloc (len);
Packit b5e560
  strncpy (exp, s, len - 1);
Packit b5e560
  exp[len - 1] = '\0';
Packit b5e560
  t = expand_arith_string (exp, Q_DOUBLE_QUOTES|Q_ARITH|Q_ARRAYSUB);	/* XXX - Q_ARRAYSUB for future use */
Packit b5e560
  savecmd = this_command_name;
Packit b5e560
  this_command_name = (char *)NULL;
Packit b5e560
  val = evalexp (t, &expok;;
Packit b5e560
  this_command_name = savecmd;
Packit b5e560
  free (t);
Packit b5e560
  free (exp);
Packit b5e560
  if (expok == 0)
Packit b5e560
    {
Packit b5e560
      last_command_exit_value = EXECUTION_FAILURE;
Packit b5e560
Packit b5e560
      if (no_longjmp_on_fatal_error)
Packit b5e560
	return 0;
Packit b5e560
      top_level_cleanup ();      
Packit b5e560
      jump_to_top_level (DISCARD);
Packit b5e560
    }
Packit b5e560
  return val;
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Return the name of the variable specified by S without any subscript.
Packit b5e560
   If SUBP is non-null, return a pointer to the start of the subscript
Packit b5e560
   in *SUBP. If LENP is non-null, the length of the subscript is returned
Packit b5e560
   in *LENP.  This returns newly-allocated memory. */
Packit b5e560
char *
Packit b5e560
array_variable_name (s, subp, lenp)
Packit b5e560
     const char *s;
Packit b5e560
     char **subp;
Packit b5e560
     int *lenp;
Packit b5e560
{
Packit b5e560
  char *t, *ret;
Packit b5e560
  int ind, ni;
Packit b5e560
Packit b5e560
  t = mbschr (s, '[');
Packit b5e560
  if (t == 0)
Packit b5e560
    {
Packit b5e560
      if (subp)
Packit b5e560
      	*subp = t;
Packit b5e560
      if (lenp)
Packit b5e560
	*lenp = 0;
Packit b5e560
      return ((char *)NULL);
Packit b5e560
    }
Packit b5e560
  ind = t - s;
Packit b5e560
  ni = skipsubscript (s, ind, 0);
Packit b5e560
  if (ni <= ind + 1 || s[ni] != ']')
Packit b5e560
    {
Packit b5e560
      err_badarraysub (s);
Packit b5e560
      if (subp)
Packit b5e560
      	*subp = t;
Packit b5e560
      if (lenp)
Packit b5e560
	*lenp = 0;
Packit b5e560
      return ((char *)NULL);
Packit b5e560
    }
Packit b5e560
Packit b5e560
  *t = '\0';
Packit b5e560
  ret = savestring (s);
Packit b5e560
  *t++ = '[';		/* ] */
Packit b5e560
Packit b5e560
  if (subp)
Packit b5e560
    *subp = t;
Packit b5e560
  if (lenp)
Packit b5e560
    *lenp = ni - ind;
Packit b5e560
Packit b5e560
  return ret;
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Return the variable specified by S without any subscript.  If SUBP is
Packit b5e560
   non-null, return a pointer to the start of the subscript in *SUBP.
Packit b5e560
   If LENP is non-null, the length of the subscript is returned in *LENP. */
Packit b5e560
SHELL_VAR *
Packit b5e560
array_variable_part (s, subp, lenp)
Packit b5e560
     const char *s;
Packit b5e560
     char **subp;
Packit b5e560
     int *lenp;
Packit b5e560
{
Packit b5e560
  char *t;
Packit b5e560
  SHELL_VAR *var;
Packit b5e560
Packit b5e560
  t = array_variable_name (s, subp, lenp);
Packit b5e560
  if (t == 0)
Packit b5e560
    return ((SHELL_VAR *)NULL);
Packit b5e560
  var = find_variable (t);		/* XXX - handle namerefs here? */
Packit b5e560
Packit b5e560
  free (t);
Packit b5e560
  return var;	/* now return invisible variables; caller must handle */
Packit b5e560
}
Packit b5e560
Packit b5e560
#define INDEX_ERROR() \
Packit b5e560
  do \
Packit b5e560
    { \
Packit b5e560
      if (var) \
Packit b5e560
	err_badarraysub (var->name); \
Packit b5e560
      else \
Packit b5e560
	{ \
Packit b5e560
	  t[-1] = '\0'; \
Packit b5e560
	  err_badarraysub (s); \
Packit b5e560
	  t[-1] = '[';	/* ] */\
Packit b5e560
	} \
Packit b5e560
      return ((char *)NULL); \
Packit b5e560
    } \
Packit b5e560
  while (0)
Packit b5e560
Packit b5e560
/* Return a string containing the elements in the array and subscript
Packit b5e560
   described by S.  If the subscript is * or @, obeys quoting rules akin
Packit b5e560
   to the expansion of $* and $@ including double quoting.  If RTYPE
Packit b5e560
   is non-null it gets 1 if the array reference is name[*], 2 if the
Packit b5e560
   reference is name[@], and 0 otherwise. */
Packit b5e560
static char *
Packit b5e560
array_value_internal (s, quoted, flags, rtype, indp)
Packit b5e560
     const char *s;
Packit b5e560
     int quoted, flags, *rtype;
Packit b5e560
     arrayind_t *indp;
Packit b5e560
{
Packit b5e560
  int len;
Packit b5e560
  arrayind_t ind;
Packit b5e560
  char *akey;
Packit b5e560
  char *retval, *t, *temp;
Packit b5e560
  WORD_LIST *l;
Packit b5e560
  SHELL_VAR *var;
Packit b5e560
Packit b5e560
  var = array_variable_part (s, &t, &len;;
Packit b5e560
Packit b5e560
  /* Expand the index, even if the variable doesn't exist, in case side
Packit b5e560
     effects are needed, like ${w[i++]} where w is unset. */
Packit b5e560
#if 0
Packit b5e560
  if (var == 0)
Packit b5e560
    return (char *)NULL;
Packit b5e560
#endif
Packit b5e560
Packit b5e560
  if (len == 0)
Packit b5e560
    return ((char *)NULL);	/* error message already printed */
Packit b5e560
Packit b5e560
  /* [ */
Packit b5e560
  akey = 0;
Packit b5e560
  if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
Packit b5e560
    {
Packit b5e560
      if (rtype)
Packit b5e560
	*rtype = (t[0] == '*') ? 1 : 2;
Packit b5e560
      if ((flags & AV_ALLOWALL) == 0)
Packit b5e560
	{
Packit b5e560
	  err_badarraysub (s);
Packit b5e560
	  return ((char *)NULL);
Packit b5e560
	}
Packit b5e560
      else if (var == 0 || value_cell (var) == 0)	/* XXX - check for invisible_p(var) ? */
Packit b5e560
	return ((char *)NULL);
Packit b5e560
      else if (array_p (var) == 0 && assoc_p (var) == 0)
Packit b5e560
	l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
Packit b5e560
      else if (assoc_p (var))
Packit b5e560
	{
Packit b5e560
	  l = assoc_to_word_list (assoc_cell (var));
Packit b5e560
	  if (l == (WORD_LIST *)NULL)
Packit b5e560
	    return ((char *)NULL);
Packit b5e560
	}
Packit b5e560
      else
Packit b5e560
	{
Packit b5e560
	  l = array_to_word_list (array_cell (var));
Packit b5e560
	  if (l == (WORD_LIST *)NULL)
Packit b5e560
	    return ((char *) NULL);
Packit b5e560
	}
Packit b5e560
Packit b5e560
      if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
Packit b5e560
	{
Packit b5e560
	  temp = string_list_dollar_star (l);
Packit b5e560
	  retval = quote_string (temp);		/* XXX - leak here */
Packit b5e560
	  free (temp);
Packit b5e560
	}
Packit b5e560
      else	/* ${name[@]} or unquoted ${name[*]} */
Packit b5e560
        /* XXX - bash-4.4/bash-5.0 test AV_ASSIGNRHS and pass PF_ASSIGNRHS */
Packit b5e560
	retval = string_list_dollar_at (l, quoted, (flags & AV_ASSIGNRHS) ? PF_ASSIGNRHS : 0);	/* XXX - leak here */
Packit b5e560
Packit b5e560
      dispose_words (l);
Packit b5e560
    }
Packit b5e560
  else
Packit b5e560
    {
Packit b5e560
      if (rtype)
Packit b5e560
	*rtype = 0;
Packit b5e560
      if (var == 0 || array_p (var) || assoc_p (var) == 0)
Packit b5e560
	{
Packit b5e560
	  if ((flags & AV_USEIND) == 0 || indp == 0)
Packit b5e560
	    {
Packit b5e560
	      ind = array_expand_index (var, t, len);
Packit b5e560
	      if (ind < 0)
Packit b5e560
		{
Packit b5e560
		  /* negative subscripts to indexed arrays count back from end */
Packit b5e560
		  if (var && array_p (var))
Packit b5e560
		    ind = array_max_index (array_cell (var)) + 1 + ind;
Packit b5e560
		  if (ind < 0)
Packit b5e560
		    INDEX_ERROR();
Packit b5e560
		}
Packit b5e560
	      if (indp)
Packit b5e560
		*indp = ind;
Packit b5e560
	    }
Packit b5e560
	  else if (indp)
Packit b5e560
	    ind = *indp;
Packit b5e560
	}
Packit b5e560
      else if (assoc_p (var))
Packit b5e560
	{
Packit b5e560
	  t[len - 1] = '\0';
Packit b5e560
	  akey = expand_assignment_string_to_string (t, 0);	/* [ */
Packit b5e560
	  t[len - 1] = ']';
Packit b5e560
	  if (akey == 0 || *akey == 0)
Packit b5e560
	    {
Packit b5e560
	      FREE (akey);
Packit b5e560
	      INDEX_ERROR();
Packit b5e560
	    }
Packit b5e560
	}
Packit b5e560
     
Packit b5e560
      if (var == 0 || value_cell (var) == 0)	/* XXX - check invisible_p(var) ? */
Packit b5e560
	{
Packit b5e560
          FREE (akey);
Packit b5e560
	  return ((char *)NULL);
Packit b5e560
	}
Packit b5e560
      if (array_p (var) == 0 && assoc_p (var) == 0)
Packit b5e560
	return (ind == 0 ? value_cell (var) : (char *)NULL);
Packit b5e560
      else if (assoc_p (var))
Packit b5e560
        {
Packit b5e560
	  retval = assoc_reference (assoc_cell (var), akey);
Packit b5e560
	  free (akey);
Packit b5e560
        }
Packit b5e560
      else
Packit b5e560
	retval = array_reference (array_cell (var), ind);
Packit b5e560
    }
Packit b5e560
Packit b5e560
  return retval;
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Return a string containing the elements described by the array and
Packit b5e560
   subscript contained in S, obeying quoting for subscripts * and @. */
Packit b5e560
char *
Packit b5e560
array_value (s, quoted, flags, rtype, indp)
Packit b5e560
     const char *s;
Packit b5e560
     int quoted, flags, *rtype;
Packit b5e560
     arrayind_t *indp;
Packit b5e560
{
Packit b5e560
  return (array_value_internal (s, quoted, flags|AV_ALLOWALL, rtype, indp));
Packit b5e560
}
Packit b5e560
Packit b5e560
/* Return the value of the array indexing expression S as a single string.
Packit b5e560
   If (FLAGS & AV_ALLOWALL) is 0, do not allow `@' and `*' subscripts.  This
Packit b5e560
   is used by other parts of the shell such as the arithmetic expression
Packit b5e560
   evaluator in expr.c. */
Packit b5e560
char *
Packit b5e560
get_array_value (s, flags, rtype, indp)
Packit b5e560
     const char *s;
Packit b5e560
     int flags, *rtype;
Packit b5e560
     arrayind_t *indp;
Packit b5e560
{
Packit b5e560
  return (array_value_internal (s, 0, flags, rtype, indp));
Packit b5e560
}
Packit b5e560
Packit b5e560
char *
Packit b5e560
array_keys (s, quoted)
Packit b5e560
     char *s;
Packit b5e560
     int quoted;
Packit b5e560
{
Packit b5e560
  int len;
Packit b5e560
  char *retval, *t, *temp;
Packit b5e560
  WORD_LIST *l;
Packit b5e560
  SHELL_VAR *var;
Packit b5e560
Packit b5e560
  var = array_variable_part (s, &t, &len;;
Packit b5e560
Packit b5e560
  /* [ */
Packit b5e560
  if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
Packit b5e560
    return (char *)NULL;
Packit b5e560
Packit b5e560
  if (var_isset (var) == 0 || invisible_p (var))
Packit b5e560
    return (char *)NULL;
Packit b5e560
Packit b5e560
  if (array_p (var) == 0 && assoc_p (var) == 0)
Packit b5e560
    l = add_string_to_list ("0", (WORD_LIST *)NULL);
Packit b5e560
  else if (assoc_p (var))
Packit b5e560
    l = assoc_keys_to_word_list (assoc_cell (var));
Packit b5e560
  else
Packit b5e560
    l = array_keys_to_word_list (array_cell (var));
Packit b5e560
  if (l == (WORD_LIST *)NULL)
Packit b5e560
    return ((char *) NULL);
Packit b5e560
Packit b5e560
  if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
Packit b5e560
    {
Packit b5e560
      temp = string_list_dollar_star (l);
Packit b5e560
      retval = quote_string (temp);
Packit b5e560
      free (temp);
Packit b5e560
    }
Packit b5e560
  else	/* ${!name[@]} or unquoted ${!name[*]} */
Packit b5e560
    retval = string_list_dollar_at (l, quoted, 0);
Packit b5e560
Packit b5e560
  dispose_words (l);
Packit b5e560
  return retval;
Packit b5e560
}
Packit b5e560
#endif /* ARRAY_VARS */