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