/* arrayfunc.c -- High-level array functions used by other parts of the shell. */
-/* Copyright (C) 2001 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2006 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#endif
#include <stdio.h>
+#include "bashintl.h"
+
#include "shell.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 *, int));
static void quote_array_assignment_chars __P((WORD_LIST *));
-static char *array_value_internal __P((char *, int, int));
+static char *array_value_internal __P((char *, int, int, int *));
+
+/* Standard error message to use when encountering an invalid array subscript */
+char *bash_badsub_errmsg = N_("bad array subscript");
/* **************************************************************** */
/* */
ARRAY *array;
oldval = value_cell (var);
- array = new_array ();
- array_add_element (array, 0, oldval);
+ array = array_create ();
+ if (oldval)
+ array_insert (array, 0, oldval);
FREE (value_cell (var));
- var->value = (char *)array;
+ 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;
}
+static SHELL_VAR *
+bind_array_var_internal (entry, ind, value, flags)
+ SHELL_VAR *entry;
+ arrayind_t ind;
+ 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);
+ 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_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);
+ 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
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);
else if (readonly_p (entry) || noassign_p (entry))
{
if (readonly_p (entry))
- report_error ("%s: readonly variable", name);
+ 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. */
- newval = make_variable_value (entry, value);
- if (entry->assign_func)
- (*entry->assign_func) (entry, ind, newval);
- else
- array_add_element (array_cell (entry), ind, newval);
- FREE (newval);
-
- return (entry);
+ return (bind_array_var_internal (entry, ind, 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;
if ((ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']') || (sublen <= 1))
{
free (vname);
- report_error ("%s: bad array subscript", name);
+ err_badarraysub (name);
return ((SHELL_VAR *)NULL);
}
if (ind < 0)
{
free (vname);
- report_error ("%s: bad array subscript", name);
+ err_badarraysub (name);
return ((SHELL_VAR *)NULL);
}
- entry = bind_array_variable (vname, ind, value);
+ entry = bind_array_variable (vname, ind, value, flags);
free (vname);
return (entry);
else if (check_flags && (readonly_p (var) || noassign_p (var)))
{
if (readonly_p (var))
- report_error ("%s: readonly variable", name);
+ err_readonly (name);
return ((SHELL_VAR *)NULL);
}
else if (array_p (var) == 0)
/* 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;
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, i, l->word->word);
+ (*var->assign_func) (var, l->word->word, i);
else
- array_add_element (a, i, l->word->word);
+ 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)
- SHELL_VAR *var;
+WORD_LIST *
+expand_compound_array_assignment (value, flags)
char *value;
+ int flags;
{
- ARRAY *a;
WORD_LIST *list, *nlist;
- char *w, *val, *nval;
- int ni, len;
- arrayind_t ind, last_ind;
+ char *val;
+ int ni;
- if (value == 0)
- return var;
-
- /* If this is called from declare_builtin, value[0] == '(' and
- strchr(value, ')') != 0. In this case, we need to extract
- the value from between the parens before going on. */
+ /* I don't believe this condition is ever true any more. */
if (*value == '(') /*)*/
{
ni = 1;
val = extract_array_assignment_list (value, &ni);
if (val == 0)
- return var;
+ return (WORD_LIST *)NULL;
}
else
val = value;
shell expansions including pathname generation and word splitting. */
/* First we split the string on whitespace, using the shell parser
(ksh93 seems to do this). */
- list = parse_string_to_word_list (val, "array assign");
+ 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. */
if (val != value)
free (val);
+ return nlist;
+}
+
+void
+assign_compound_array_list (var, nlist, flags)
+ SHELL_VAR *var;
+ WORD_LIST *nlist;
+ int flags;
+{
+ ARRAY *a;
+ WORD_LIST *list;
+ char *w, *val, *nval;
+ int len, iflags;
+ arrayind_t ind, last_ind;
+
a = array_cell (var);
/* Now that we are ready to assign values to the array, kill the existing
value. */
- if (a)
- empty_array (a);
+ if (a && (flags & ASS_APPEND) == 0)
+ array_flush (a);
+ last_ind = (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 (w[0] == '[')
+ if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[')
{
len = skipsubscript (w, 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);
+ nval = make_variable_value (var, w, flags);
if (var->assign_func)
- (*var->assign_func) (var, last_ind, nval);
+ (*var->assign_func) (var, nval, last_ind);
else
- array_add_element (a, last_ind, nval);
+ array_insert (a, last_ind, nval);
FREE (nval);
last_ind++;
continue;
if (len == 1)
{
- report_error ("%s: bad array subscript", w);
+ err_badarraysub (w);
continue;
}
if (ALL_ELEMENT_SUB (w[1]) && len == 2)
{
- report_error ("%s: cannot assign to non-numeric index", w);
+ report_error (_("%s: cannot assign to non-numeric index"), w);
continue;
}
ind = array_expand_index (w + 1, len);
if (ind < 0)
{
- report_error ("%s: bad array subscript", w);
+ err_badarraysub (w);
continue;
}
last_ind = ind;
- val = w + len + 2;
+ /* 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 /* No [ind]=value, just a stray `=' */
{
if (integer_p (var))
this_command_name = (char *)NULL; /* no command name for errors */
- nval = make_variable_value (var, val);
- if (var->assign_func)
- (*var->assign_func) (var, ind, nval);
- else
- array_add_element (a, ind, nval);
- FREE (nval);
+ bind_array_var_internal (var, ind, val, iflags);
last_ind++;
}
+}
+
+/* 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 (value, flags);
+ assign_compound_array_list (var, nlist, flags);
- dispose_words (nlist);
+ if (nlist)
+ dispose_words (nlist);
return (var);
}
if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
continue; /* should not happen, but just in case... */
/* Don't bother if it doesn't look like [ind]=value */
- if (l->word->word[0] != '[' || strchr (l->word->word, '=') == 0) /* ] */
+ if (l->word->word[0] != '[' || xstrchr (l->word->word, '=') == 0) /* ] */
continue;
s = nword = (char *)xmalloc (strlen (l->word->word) * 2 + 1);
saw_eq = 0;
int i;
{
int count, c;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t state, state_bak;
+ size_t slength, mblength;
+#endif
- for (count = 1; count && (c = s[++i]); )
+#if defined (HANDLE_MULTIBYTE)
+ memset (&state, '\0', sizeof (mbstate_t));
+ slength = strlen (s + i);
+#endif
+
+ count = 1;
+ while (count)
{
- if (c == '[')
+ /* 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;
}
len = skipsubscript (sub, 0);
if (sub[len] != ']' || len == 0)
{
- builtin_error ("%s[%s: bad array subscript", var->name, sub);
+ 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)
{
- makunbound (var->name, shell_variables);
+ unbind_variable (var->name);
return (0);
}
ind = array_expand_index (sub, len+1);
if (ind < 0)
{
- builtin_error ("[%s]: bad array subscript", sub);
+ builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
return -1;
}
- ae = array_delete_element (array_cell (var), ind);
+ ae = array_remove (array_cell (var), ind);
if (ae)
- destroy_array_element (ae);
+ array_dispose_element (ae);
return 0;
}
{
char *vstr;
- if (quoted)
- vstr = quoted_array_assignment_string (array_cell (var));
- else
- vstr = array_to_assignment_string (array_cell (var));
+ vstr = array_to_assign (array_cell (var), quoted);
if (vstr == 0)
printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
char *t;
int r, len;
- t = strchr (name, '['); /* ] */
+ t = xstrchr (name, '['); /* ] */
if (t)
{
*t = '\0';
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 = strchr (s, '[');
+ t = xstrchr (s, '[');
if (t == 0)
- return ((char *)NULL);
+ {
+ if (subp)
+ *subp = t;
+ if (lenp)
+ *lenp = 0;
+ return ((char *)NULL);
+ }
ind = t - s;
ni = skipsubscript (s, ind);
if (ni <= ind + 1 || s[ni] != ']')
{
- report_error ("%s: bad array subscript", s);
+ err_badarraysub (s);
+ if (subp)
+ *subp = t;
+ if (lenp)
+ *lenp = 0;
return ((char *)NULL);
}
var = find_variable (t);
free (t);
- return var;
+ return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var;
}
/* 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. */
+ 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. */
static char *
-array_value_internal (s, quoted, allow_all)
+array_value_internal (s, quoted, allow_all, rtype)
char *s;
- int quoted, allow_all;
+ int quoted, allow_all, *rtype;
{
int len;
arrayind_t ind;
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 (allow_all == 0)
{
- report_error ("%s: bad array subscript", s);
+ err_badarraysub (s);
return ((char *)NULL);
}
+ else if (var == 0 || value_cell (var) == 0)
+ return ((char *)NULL);
else if (array_p (var) == 0)
- {
- l = (WORD_LIST *)NULL;
- l = add_string_to_list (value_cell (var), l);
- }
+ l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
else
{
l = array_to_word_list (array_cell (var));
}
else
{
+ if (rtype)
+ *rtype = 0;
ind = array_expand_index (t, len);
if (ind < 0)
{
- report_error ("%s: bad array subscript", var->name);
+ if (var)
+ err_badarraysub (var->name);
+ else
+ {
+ t[-1] = '\0';
+ err_badarraysub (s);
+ t[-1] = '['; /* ] */
+ }
return ((char *)NULL);
}
+ if (var == 0)
+ return ((char *)NULL);
if (array_p (var) == 0)
- return (ind == 0 ? savestring (value_cell (var)) : (char *)NULL);
+ return (ind == 0 ? value_cell (var) : (char *)NULL);
retval = array_reference (array_cell (var), ind);
- if (retval)
- retval = quote_escapes (retval);
}
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)
+array_value (s, quoted, rtype)
char *s;
- int quoted;
+ int quoted, *rtype;
{
- return (array_value_internal (s, quoted, 1));
+ return (array_value_internal (s, quoted, 1, rtype));
}
/* Return the value of the array indexing expression S as a single string.
by other parts of the shell such as the arithmetic expression evaluator
in expr.c. */
char *
-get_array_value (s, allow_all)
+get_array_value (s, allow_all, rtype)
char *s;
- int allow_all;
+ int allow_all, *rtype;
{
- return (array_value_internal (s, 0, allow_all));
+ return (array_value_internal (s, 0, allow_all, rtype));
}
+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 (array_p (var) == 0)
+ l = add_string_to_list ("0", (WORD_LIST *)NULL);
+ 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 */