/* arrayfunc.c -- High-level array functions used by other parts of the shell. */
-/* Copyright (C) 2001-2003 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2011 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
- Bash is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2, or (at your option) any later
- version.
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- Bash is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with Bash; see the file COPYING. If not, write to the Free Software
- Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
#include "config.h"
#include "bashintl.h"
#include "shell.h"
+#include "pathexp.h"
#include "shmbutil.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 *));
+static char *array_value_internal __P((char *, int, int, int *, arrayind_t *));
/* Standard error message to use when encountering an invalid array subscript */
-char *bash_badsub_errmsg = N_("bad array subscript");
+const char * const bash_badsub_errmsg = N_("bad array subscript");
/* **************************************************************** */
/* */
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;
+}
+
+char *
+make_array_variable_value (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);
+
+ return newval;
+}
+
+static SHELL_VAR *
+bind_array_var_internal (entry, ind, key, value, flags)
+ SHELL_VAR *entry;
+ arrayind_t ind;
+ char *key;
+ char *value;
+ int flags;
+{
+ char *newval;
+
+ newval = make_array_variable_value (entry, ind, key, 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);
+
+ VUNSETATTR (entry, att_invisible); /* no longer invisible */
+ 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
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)
+bind_array_variable (name, ind, value, flags)
char *name;
arrayind_t ind;
char *value;
+ int flags;
{
SHELL_VAR *entry;
- char *newval;
- entry = var_lookup (name, shell_variables);
+ entry = find_shell_variable (name);
if (entry == (SHELL_VAR *) 0)
entry = make_new_array_variable (name);
entry = convert_var_to_array (entry);
/* ENTRY is an array variable, and ARRAY points to the value. */
- newval = make_variable_value (entry, value);
- if (entry->assign_func)
- (*entry->assign_func) (entry, newval, ind);
- else
- array_insert (array_cell (entry), ind, newval);
- FREE (newval);
+ return (bind_array_var_internal (entry, ind, 0, value, flags));
+}
- return (entry);
+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)
+assign_array_element (name, value, flags)
char *name, *value;
+ int flags;
{
char *sub, *vname;
- arrayind_t ind;
int sublen;
SHELL_VAR *entry;
return ((SHELL_VAR *)NULL);
}
- ind = array_expand_index (sub, sublen);
- if (ind < 0)
+ 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))
{
- free (vname);
- err_badarraysub (name);
- return ((SHELL_VAR *)NULL);
+ sub[sublen-1] = '\0';
+ akey = expand_assignment_string_to_string (sub, 0); /* [ */
+ sub[sublen-1] = ']';
+ if (akey == 0 || *akey == 0)
+ {
+ err_badarraysub (name);
+ FREE (akey);
+ return ((SHELL_VAR *)NULL);
+ }
+ entry = bind_assoc_variable (entry, vname, akey, value, flags);
+ }
+ else
+ {
+ ind = array_expand_index (entry, sub, sublen);
+ /* negative subscripts to indexed arrays count back from end */
+ if (entry && ind < 0)
+ ind = (array_p (entry) ? array_max_index (array_cell (entry)) : 0) + 1 + ind;
+ if (ind < 0)
+ {
+ err_badarraysub (name);
+ return ((SHELL_VAR *)NULL);
+ }
+ entry = bind_array_variable (vname, ind, value, flags);
}
- entry = bind_array_variable (vname, ind, value);
-
- free (vname);
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 CHECK_FLAGS is non-zero, an existing
+ 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). */
+ 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, check_flags)
+find_or_make_array_variable (name, flags)
char *name;
- int check_flags;
+ int flags;
{
SHELL_VAR *var;
var = find_variable (name);
+ if (var == 0)
+ {
+ /* See if we have a nameref pointing to a variable that hasn't been
+ created yet. */
+ var = find_variable_last_nameref (name);
+ if (var && nameref_p (var))
+ var = (flags & 2) ? make_new_assoc_variable (nameref_cell (var)) : make_new_array_variable (nameref_cell (var));
+ }
if (var == 0)
- var = make_new_array_variable (name);
- else if (check_flags && (readonly_p (var) || noassign_p (var)))
+ 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 (array_p (var) == 0)
+ else if ((flags & 2) && array_p (var))
+ {
+ last_command_exit_value = 1;
+ 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)
+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, 1);
+ var = find_or_make_array_variable (name, vflags);
if (var == 0)
return ((SHELL_VAR *)NULL);
- return (assign_array_var_from_string (var, value));
+ 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)
+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;
- for (a = array_cell (var), l = list, i = 0; l; l = l->next, i++)
+ 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);
+ (*var->assign_func) (var, l->word->word, i, 0);
else
array_insert (a, i, l->word->word);
return var;
}
-/* 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)
+WORD_LIST *
+expand_compound_array_assignment (var, value, flags)
SHELL_VAR *var;
char *value;
+ int flags;
{
- ARRAY *a;
WORD_LIST *list, *nlist;
- char *w, *val, *nval;
- int ni, len;
- arrayind_t ind, last_ind;
+ WORD_LIST *hd, *tl, *t, *n;
+ char *val;
+ int ni;
- if (value == 0)
- return var;
-
- /* If this is called from declare_builtin, value[0] == '(' and
- xstrchr(value, ')') != 0. In this case, we need to extract
- the value from between the parens before going on. */
+ /* 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 var;
+ return (WORD_LIST *)NULL;
}
else
val = value;
(ksh93 seems to do this). */
list = parse_string_to_word_list (val, 1, "array assign");
+ if (var && assoc_p (var))
+ {
+ if (val != value)
+ free (val);
+ return list;
+ }
+
/* If we're using [subscript]=value, we need to quote each [ and ] to
- prevent unwanted filename expansion. */
+ 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)
quote_array_assignment_chars (list);
if (val != value)
free (val);
- a = array_cell (var);
+ 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 (a)
- array_flush (a);
+ 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 (last_ind = 0, list = nlist; list; list = list->next)
+ 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] == '[')
{
- len = skipsubscript (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);
- if (w[len] != ']' || w[len+1] != '=')
+ /* XXX - changes for `+=' */
+ if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
{
- nval = make_variable_value (var, w);
+ 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);
+ (*var->assign_func) (var, nval, last_ind, 0);
else
array_insert (a, last_ind, nval);
FREE (nval);
if (ALL_ELEMENT_SUB (w[1]) && len == 2)
{
- report_error (_("%s: cannot assign to non-numeric index"), w);
+ last_command_exit_value = 1;
+ if (assoc_p (var))
+ report_error (_("%s: invalid associative array key"), w);
+ else
+ report_error (_("%s: cannot assign to non-numeric index"), w);
continue;
}
- ind = array_expand_index (w + 1, len);
- if (ind < 0)
+ if (array_p (var))
{
- err_badarraysub (w);
- continue;
+ ind = array_expand_index (var, w + 1, len);
+ /* negative subscripts to indexed arrays count back from end */
+ if (ind < 0)
+ ind = array_max_index (array_cell (var)) + 1 + ind;
+ 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);
+ FREE (akey);
+ 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;
}
- last_ind = ind;
- val = w + len + 2;
+ else
+ val = w + len + 2;
+ }
+ else if (assoc_p (var))
+ {
+ last_command_exit_value = 1;
+ report_error (_("%s: %s: must use subscript when assigning associative array"), var->name, w);
+ continue;
}
else /* No [ind]=value, just a stray `=' */
{
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);
+ if (val == 0)
+ {
+ val = (char *)xmalloc (1);
+ val[0] = '\0'; /* like do_assignment_internal */
+ }
+ free_val = 1;
+ }
+
if (integer_p (var))
this_command_name = (char *)NULL; /* no command name for errors */
- nval = make_variable_value (var, val);
- if (var->assign_func)
- (*var->assign_func) (var, nval, ind);
- else
- array_insert (a, ind, nval);
- FREE (nval);
+ 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);
- dispose_words (nlist);
+ 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 the `[' and `]' before the `=' to protect them from
- unwanted filename expansion. */
+ [ind]=value, quote globbing chars and characters in $IFS before the `='. */
static void
quote_array_assignment_chars (list)
WORD_LIST *list;
{
- char *s, *t, *nword;
- int saw_eq;
+ 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] != '[' || xstrchr (l->word->word, '=') == 0) /* ] */
+ /* Don't bother if it hasn't been recognized as an assignment or
+ doesn't look like [ind]=value */
+ if ((l->word->flags & W_ASSIGNMENT) == 0)
continue;
- s = nword = (char *)xmalloc (strlen (l->word->word) * 2 + 1);
- saw_eq = 0;
- for (t = l->word->word; *t; )
- {
- if (*t == '=')
- saw_eq = 1;
- if (saw_eq == 0 && (*t == '[' || *t == ']'))
- *s++ = '\\';
- *s++ = *t++;
- }
- *s = '\0';
+ 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;
+ l->word->flags |= W_NOGLOB; /* XXX - W_NOSPLIT also? */
}
}
-/* This function assumes s[i] == '['; returns with s[ret] == ']' if
- an array subscript is correctly parsed. */
-int
-skipsubscript (s, i)
- const char *s;
- int i;
-{
- int count, c;
-#if defined (HANDLE_MULTIBYTE)
- mbstate_t state, state_bak;
- size_t slength, mblength;
- size_t mb_cur_max;
-#endif
-
-#if defined (HANDLE_MULTIBYTE)
- memset (&state, '\0', sizeof (mbstate_t));
- slength = strlen (s + i);
- mb_cur_max = MB_CUR_MAX;
-#endif
-
- count = 1;
- while (count)
- {
- /* Advance one (possibly multibyte) character in S starting at I. */
-#if defined (HANDLE_MULTIBYTE)
- if (mb_cur_max > 1)
- {
- state_bak = state;
- mblength = mbrlen (s + i, slength, &state);
-
- if (MB_INVALIDCH (mblength))
- {
- state = state_bak;
- i++;
- slength--;
- }
- else if (MB_NULLWCH (mblength))
- return i;
- else
- {
- i += mblength;
- slength -= mblength;
- }
- }
- else
-#endif
- ++i;
-
- c = s[i];
-
- if (c == 0)
- break;
- else if (c == '[')
- count++;
- else if (c == ']')
- count--;
- }
-
- return i;
-}
+/* 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
{
int len;
arrayind_t ind;
+ char *akey;
ARRAY_ELEMENT *ae;
- len = skipsubscript (sub, 0);
+ len = skipsubscript (sub, 0, 0);
if (sub[len] != ']' || len == 0)
{
builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg));
unbind_variable (var->name);
return (0);
}
- ind = array_expand_index (sub, len+1);
- if (ind < 0)
+
+ if (assoc_p (var))
{
- builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
- return -1;
+ akey = expand_assignment_string_to_string (sub, 0); /* [ */
+ if (akey == 0 || *akey == 0)
+ {
+ builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
+ FREE (akey);
+ return -1;
+ }
+ assoc_remove (assoc_cell (var), akey);
+ free (akey);
+ }
+ else
+ {
+ ind = array_expand_index (var, sub, len+1);
+ /* negative subscripts to indexed arrays count back from end */
+ if (ind < 0)
+ ind = array_max_index (array_cell (var)) + 1 + ind;
+ 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);
}
- ae = array_remove (array_cell (var), ind);
- if (ae)
- array_dispose_element (ae);
+
return 0;
}
}
}
+/* 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 */
char *t;
int r, len;
- t = xstrchr (name, '['); /* ] */
+ t = mbschr (name, '['); /* ] */
if (t)
{
*t = '\0';
if (r == 0)
return 0;
/* Check for a properly-terminated non-blank subscript. */
- len = skipsubscript (t, 0);
+ len = skipsubscript (t, 0, 0);
if (t[len] != ']' || len == 1)
return 0;
for (r = 1; r < len; r++)
/* Expand the array index beginning at S and extending LEN characters. */
arrayind_t
-array_expand_index (s, len)
+array_expand_index (var, s, len)
+ SHELL_VAR *var;
char *s;
int len;
{
exp = (char *)xmalloc (len);
strncpy (exp, s, len - 1);
exp[len - 1] = '\0';
- t = expand_string_to_string (exp, 0);
+ t = expand_arith_string (exp, 0);
this_command_name = (char *)NULL;
val = evalexp (t, &expok);
free (t);
if (expok == 0)
{
last_command_exit_value = EXECUTION_FAILURE;
+
+ top_level_cleanup ();
jump_to_top_level (DISCARD);
}
return val;
char *t, *ret;
int ind, ni;
- t = xstrchr (s, '[');
+ t = mbschr (s, '[');
if (t == 0)
{
if (subp)
return ((char *)NULL);
}
ind = t - s;
- ni = skipsubscript (s, ind);
+ ni = skipsubscript (s, ind, 0);
if (ni <= ind + 1 || s[ni] != ']')
{
err_badarraysub (s);
var = find_variable (t);
free (t);
+#if 0
return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var;
+#else
+ return var; /* now return invisible variables; caller must handle */
+#endif
}
+#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[@] or name[*]
- and 0 otherwise. */
+ 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, allow_all, rtype)
+array_value_internal (s, quoted, flags, rtype, indp)
char *s;
- int quoted, allow_all, *rtype;
+ int quoted, flags, *rtype;
+ arrayind_t *indp;
{
int len;
arrayind_t ind;
+ char *akey;
char *retval, *t, *temp;
WORD_LIST *l;
SHELL_VAR *var;
return ((char *)NULL); /* error message already printed */
/* [ */
+ akey = 0;
if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
{
if (rtype)
- *rtype = 1;
- if (allow_all == 0)
+ *rtype = (t[0] == '*') ? 1 : 2;
+ if ((flags & AV_ALLOWALL) == 0)
{
err_badarraysub (s);
return ((char *)NULL);
}
- else if (var == 0)
+ else if (var == 0 || value_cell (var) == 0) /* XXX - check for invisible_p(var) ? */
return ((char *)NULL);
- else if (array_p (var) == 0)
+ 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 (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
{
temp = string_list_dollar_star (l);
- retval = quote_string (temp);
+ retval = quote_string (temp); /* XXX - leak here */
free (temp);
}
else /* ${name[@]} or unquoted ${name[*]} */
- retval = string_list_dollar_at (l, quoted);
+ retval = string_list_dollar_at (l, quoted); /* XXX - leak here */
dispose_words (l);
}
{
if (rtype)
*rtype = 0;
- ind = array_expand_index (t, len);
- if (ind < 0)
+ if (var == 0 || array_p (var) || assoc_p (var) == 0)
{
- if (var)
- err_badarraysub (var->name);
- else
+ 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)
{
- t[-1] = '\0';
- err_badarraysub (s);
- t[-1] = '['; /* ] */
+ FREE (akey);
+ INDEX_ERROR();
}
+ }
+
+ if (var == 0 || value_cell (var) == 0) /* XXX - check invisible_p(var) ? */
+ {
+ FREE (akey);
return ((char *)NULL);
}
- if (var == 0)
- return ((char *)NULL);
- if (array_p (var) == 0)
+ if (array_p (var) == 0 && assoc_p (var) == 0)
return (ind == 0 ? value_cell (var) : (char *)NULL);
- retval = array_reference (array_cell (var), ind);
+ 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, rtype)
+array_value (s, quoted, flags, rtype, indp)
char *s;
- int quoted, *rtype;
+ int quoted, flags, *rtype;
+ arrayind_t *indp;
{
- return (array_value_internal (s, quoted, 1, rtype));
+ 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 ALLOW_ALL 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. */
+ 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, allow_all, rtype)
+get_array_value (s, flags, rtype, indp)
char *s;
- int allow_all, *rtype;
+ int flags, *rtype;
+ arrayind_t *indp;
{
- return (array_value_internal (s, 0, allow_all, rtype));
+ return (array_value_internal (s, 0, flags, rtype, indp));
}
char *
if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
return (char *)NULL;
- if (array_p (var) == 0)
+ 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);
- }
+ 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)))
{