or at least -- (if we are inheriting), because we don't want the
declare to get skipped before we perform the word assignment. Fixes
bug reported by Ross Goldberg <ross.goldberg@gmail.com>
+
+ 4/28
+ ----
+subst.c
+ - expand_declaration_argument: new function, broke code that handles
+ compound assignments that are arguments to declaration commands out
+ of shell_expand_word_list into this function. No functional change
+ yet
+
+ 4/29
+ ----
+subst.c
+ - expand_compound_assignment_word: helper function for
+ expand_declaration_argument: takes NAME[+]=( VALUE ), converts VALUE
+ to a list of words, then single-quotes each word and reconstructs
+ the original word. This assumes the result will go to
+ do_word_assignment, which will remove the single quotes
+ - expand_oneword: helper function for expand_compound_assignment_word,
+ takes VALUE and performs the splitting into words, and then the
+ expansion and single-quoting of each individual word. Indexed and
+ associative arrays take different code paths, because they undergo
+ different expansions and associative arrays need special handling to
+ avoid having to scan for the end of the subscript multiple times
+ - expand_declaration_argument: call expand_compound_assignment_word to
+ get the expansion-before-calling-builtins word expansion sequence
+ correct. Better fix for for bug report from Kevin Locke
+ <kevin@kevinlocke.name>,
+ https://savannah.gnu.org/support/index.php?109669
+
+arrayfunc.c
+ - quote_array_compound_word: take [IND]=VALUE and convert it to
+ ['IND']='VALUE'. Called by quote_compound_array_list for each word
+ in the list
+ - expand_and_quote_assoc_qword: take [KEY]=VALUE and convert it to
+ ['expanded-key']='expanded-value' (or VALUE to 'expanded-value').
+ Called by subst.c:expand_oneword() for each word in the list
+ - quote_compound_array_list: take a list of words and convert each
+ [IND]=VALUE to ['IND']='VALUE' (or just 'VALUE' if there is no
+ [IND]=). Used for indexed arrays
+
+arrayfunc.h
+ - expand_and_quote_assoc_word,quote_compound_array_list: new extern
+ declarations
tests/varenv16.sub f
tests/varenv17.sub f
tests/varenv18.sub f
+tests/varenv19.sub f
tests/version f
tests/version.mini f
tests/vredir.tests f
#include "builtins/common.h"
+#ifndef LBRACK
+# define LBRACK '['
+# define RBRACK ']'
+#endif
+
/* This variable means to not expand associative array subscripts more than
once, when performing variable expansion. */
int assoc_expand_once = 0;
static char *quote_assign PARAMS((const char *));
static void quote_array_assignment_chars PARAMS((WORD_LIST *));
+static char *quote_compound_array_word PARAMS((char *, int));
static char *array_value_internal PARAMS((const char *, int, int, int *, arrayind_t *));
/* Standard error message to use when encountering an invalid array subscript */
#endif
/* Callers ensure that VAR is not NULL. Associative array assignments have not
- been expanded when this is called, so we don't have to scan through the
- expanded subscript to find the ending bracket; indexed array assignments
- have been expanded.
+ been expanded when this is called, or have been expanded once and single-
+ quoted, so we don't have to scan through an unquoted expanded subscript to
+ find the ending bracket; indexed array assignments have been expanded and
+ possibly single-quoted to prevent further expansion.
+
If this is an associative array, we perform the assignments into NHASH and
set NHASH to be the value of VAR after processing the assignments in NLIST */
void
last_ind = (a && (flags & ASS_APPEND)) ? array_max_index (a) + 1 : 0;
#if ASSOC_KVPAIR_ASSIGNMENT
- if (assoc_p (var) && nlist && (nlist->word->flags & W_ASSIGNMENT) == 0 && nlist->word->word[0] != '[') /*]*/
+ if (assoc_p (var) && nlist && (nlist->word->flags & W_ASSIGNMENT) == 0 && nlist->word->word[0] != '[') /*]*/
{
iflags = flags & ~ASS_APPEND;
assign_assoc_from_kvlist (var, nlist, nhash, iflags);
return temp;
}
+/* Take a word W of the form [IND]=VALUE and transform it to ['IND]='VALUE'
+ to prevent further expansion. This is called for compound assignments to
+ indexed arrays. W has already undergone word expansions. If W has no [IND]=,
+ just single-quote and return it. */
+static char *
+quote_compound_array_word (w, type)
+ char *w;
+ int type;
+{
+ char *nword, *sub, *value, *t;
+ int ind, wlen, i;
+
+ if (w[0] != LBRACK)
+ return (sh_single_quote (w));
+ ind = skipsubscript (w, 0, 0);
+ if (w[ind] != RBRACK)
+ return (sh_single_quote (w));
+
+ wlen = strlen (w);
+ w[ind] = '\0';
+ sub = sh_single_quote (w+1);
+ w[ind] = RBRACK;
+
+ nword = xmalloc (wlen * 4 + 5); /* wlen*4 is max single quoted length */
+ nword[0] = LBRACK;
+ i = STRLEN (sub);
+ memcpy (nword+1, sub, i);
+ i++; /* accommodate the opening LBRACK */
+ nword[i++] = w[ind++]; /* RBRACK */
+ if (w[ind] == '+')
+ nword[i++] = w[ind++];
+ nword[i++] = w[ind++];
+ value = sh_single_quote (w + ind);
+ strcpy (nword + i, value);
+
+ return nword;
+}
+
+/* Expand the key and value in W, which is of the form [KEY]=VALUE, and
+ reconstruct W with the expanded and single-quoted version:
+ ['expanded-key']='expanded-value'. If there is no [KEY]=, single-quote the
+ word and return it. Very similar to previous function, but does not assume
+ W has already been expanded, and expands the KEY and VALUE separately.
+ Used for compound assignments to associative arrays that are arguments to
+ declaration builtins (declare -A a=( list )). */
+char *
+expand_and_quote_assoc_word (w, type)
+ char *w;
+ int type;
+{
+ char *nword, *key, *value, *t;
+ int ind, wlen, i;
+
+ if (w[0] != LBRACK)
+ return (sh_single_quote (w));
+ ind = skipsubscript (w, 0, 0);
+ if (w[ind] != RBRACK)
+ return (sh_single_quote (w));
+
+ w[ind] = '\0';
+ t = expand_assignment_string_to_string (w+1, 0);
+ w[ind] = RBRACK;
+ key = sh_single_quote (t ? t : "");
+ free (t);
+
+ wlen = STRLEN (key);
+ nword = xmalloc (wlen + 5);
+ nword[0] = LBRACK;
+ memcpy (nword+1, key, wlen);
+ i = wlen + 1; /* accommodate the opening LBRACK */
+
+ nword[i++] = w[ind++]; /* RBRACK */
+ if (w[ind] == '+')
+ nword[i++] = w[ind++];
+ nword[i++] = w[ind++];
+
+ t = expand_assignment_string_to_string (w+ind, 0);
+ value = sh_single_quote (t ? t : "");
+ free (t);
+ nword = xrealloc (nword, wlen + 5 + STRLEN (value));
+ strcpy (nword + i, value);
+
+ free (key);
+ free (value);
+
+ return nword;
+}
+
+/* For each word in a compound array assignment, if the word looks like
+ [ind]=value, single-quote ind and value, but leave the brackets and
+ the = sign (and any `+') alone. This is used for indexed arrays. */
+void
+quote_compound_array_list (list, type)
+ WORD_LIST *list;
+ int type;
+{
+ char *t;
+ 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... */
+ if ((l->word->flags & W_ASSIGNMENT) == 0)
+ t = sh_single_quote (l->word->word);
+ else
+ t = quote_compound_array_word (l->word->word, type);
+ free (l->word->word);
+ l->word->word = t;
+ }
+}
+
/* 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
/* arrayfunc.h -- declarations for miscellaneous array functions in arrayfunc.c */
-/* Copyright (C) 2001-2010 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2020 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#define VA_NOEXPAND 0x001
#define VA_ONEWORD 0x002
-extern SHELL_VAR *convert_var_to_array __P((SHELL_VAR *));
-extern SHELL_VAR *convert_var_to_assoc __P((SHELL_VAR *));
+extern SHELL_VAR *convert_var_to_array PARAMS((SHELL_VAR *));
+extern SHELL_VAR *convert_var_to_assoc PARAMS((SHELL_VAR *));
-extern char *make_array_variable_value __P((SHELL_VAR *, arrayind_t, char *, char *, int));
+extern char *make_array_variable_value PARAMS((SHELL_VAR *, arrayind_t, char *, char *, int));
-extern SHELL_VAR *bind_array_variable __P((char *, arrayind_t, char *, int));
-extern SHELL_VAR *bind_array_element __P((SHELL_VAR *, arrayind_t, char *, int));
-extern SHELL_VAR *assign_array_element __P((char *, char *, int));
+extern SHELL_VAR *bind_array_variable PARAMS((char *, arrayind_t, char *, int));
+extern SHELL_VAR *bind_array_element PARAMS((SHELL_VAR *, arrayind_t, char *, int));
+extern SHELL_VAR *assign_array_element PARAMS((char *, char *, int));
-extern SHELL_VAR *bind_assoc_variable __P((SHELL_VAR *, char *, char *, char *, int));
+extern SHELL_VAR *bind_assoc_variable PARAMS((SHELL_VAR *, char *, char *, char *, int));
-extern SHELL_VAR *find_or_make_array_variable __P((char *, int));
+extern SHELL_VAR *find_or_make_array_variable PARAMS((char *, int));
-extern SHELL_VAR *assign_array_from_string __P((char *, char *, int));
-extern SHELL_VAR *assign_array_var_from_word_list __P((SHELL_VAR *, WORD_LIST *, int));
+extern SHELL_VAR *assign_array_from_string PARAMS((char *, char *, int));
+extern SHELL_VAR *assign_array_var_from_word_list PARAMS((SHELL_VAR *, WORD_LIST *, int));
-extern WORD_LIST *expand_compound_array_assignment __P((SHELL_VAR *, char *, int));
-extern void assign_compound_array_list __P((SHELL_VAR *, WORD_LIST *, int));
-extern SHELL_VAR *assign_array_var_from_string __P((SHELL_VAR *, char *, int));
+extern WORD_LIST *expand_compound_array_assignment PARAMS((SHELL_VAR *, char *, int));
+extern void assign_compound_array_list PARAMS((SHELL_VAR *, WORD_LIST *, int));
+extern SHELL_VAR *assign_array_var_from_string PARAMS((SHELL_VAR *, char *, int));
-extern int unbind_array_element __P((SHELL_VAR *, char *, int));
-extern int skipsubscript __P((const char *, int, int));
+extern char *expand_and_quote_assoc_word PARAMS((char *, int));
+extern void quote_compound_array_list PARAMS((WORD_LIST *, int));
-extern void print_array_assignment __P((SHELL_VAR *, int));
-extern void print_assoc_assignment __P((SHELL_VAR *, int));
+extern int unbind_array_element PARAMS((SHELL_VAR *, char *, int));
+extern int skipsubscript PARAMS((const char *, int, int));
-extern arrayind_t array_expand_index __P((SHELL_VAR *, char *, int, int));
-extern int valid_array_reference __P((const char *, int));
-extern char *array_value __P((const char *, int, int, int *, arrayind_t *));
-extern char *get_array_value __P((const char *, int, int *, arrayind_t *));
+extern void print_array_assignment PARAMS((SHELL_VAR *, int));
+extern void print_assoc_assignment PARAMS((SHELL_VAR *, int));
-extern char *array_keys __P((char *, int, int));
+extern arrayind_t array_expand_index PARAMS((SHELL_VAR *, char *, int, int));
+extern int valid_array_reference PARAMS((const char *, int));
+extern char *array_value PARAMS((const char *, int, int, int *, arrayind_t *));
+extern char *get_array_value PARAMS((const char *, int, int *, arrayind_t *));
-extern char *array_variable_name __P((const char *, int, char **, int *));
-extern SHELL_VAR *array_variable_part __P((const char *, int, char **, int *));
+extern char *array_keys PARAMS((char *, int, int));
+
+extern char *array_variable_name PARAMS((const char *, int, char **, int *));
+extern SHELL_VAR *array_variable_part PARAMS((const char *, int, char **, int *));
#else
regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh
looks for to find the patch level (for the sccs version string). */
-#define PATCHLEVEL 16
+#define PATCHLEVEL 17
#endif /* _PATCHLEVEL_H_ */
#endif
#if defined (ARRAY_VARS)
static int make_internal_declare PARAMS((char *, char *, char *));
+static void expand_compound_assignment_word PARAMS((WORD_LIST *, int));
+static WORD_LIST *expand_declaration_argument PARAMS((WORD_LIST *, WORD_LIST *));
#endif
static WORD_LIST *shell_expand_word_list PARAMS((WORD_LIST *, int));
static WORD_LIST *expand_word_list_internal PARAMS((WORD_LIST *, int));
dispose_words (wl);
return r;
}
-#endif
+
+/* Expand VALUE in NAME[+]=( VALUE ) to a list of words. FLAGS is 1 if NAME
+ is an associative array.
+
+ If we are processing an indexed array, expand_compound_array_assignment
+ will expand all the individual words and quote_compound_array_list will
+ single-quote them. If we are processing an associative array, we use
+ parse_string_to_word_list to split VALUE into a list of words instead of
+ faking up a shell variable and calling expand_compound_array_assignment.
+ expand_and_quote_assoc_word expands and single-quotes each word in VALUE
+ together so we don't have problems finding the end of the subscript when
+ quoting it.
+
+ Words in VALUE can be individual words, which are expanded and single-quoted,
+ or words of the form [IND]=VALUE, which end up as explained below, as
+ ['expanded-ind']='expanded-value'. */
+
+static WORD_LIST *
+expand_oneword (value, flags)
+ char *value;
+ int flags;
+{
+ WORD_LIST *l, *nl;
+ char *t;
+
+ if (flags == 0)
+ {
+ /* Indexed array */
+ l = expand_compound_array_assignment ((SHELL_VAR *)NULL, value, flags);
+ /* Now we quote the results of the expansion above to prevent double
+ expansion. */
+ quote_compound_array_list (l, flags);
+ return l;
+ }
+ else
+ {
+ /* Associative array */
+ l = parse_string_to_word_list (value, 1, "array assign");
+ /* For associative arrays, with their arbitrary subscripts, we have to
+ expand and quote in one step so we don't have to search for the
+ closing right bracket more than once. */
+ for (nl = l; nl; nl = nl->next)
+ {
+ if ((nl->word->flags & W_ASSIGNMENT) == 0)
+ t = sh_single_quote (nl->word->word ? nl->word->word : "");
+ else
+ t = expand_and_quote_assoc_word (nl->word->word, flags);
+ free (nl->word->word);
+ nl->word->word = t;
+ }
+ return l;
+ }
+}
+
+/* Expand a single compound assignment argument to a declaration builtin.
+ This word takes the form NAME[+]=( VALUE ). The NAME[+]= is passed through
+ unchanged. The VALUE is expanded and each word in the result is single-
+ quoted. Words of the form [key]=value end up as
+ ['expanded-key']='expanded-value'. Associative arrays have special
+ handling, see expand_oneword() above. The return value is
+ NAME[+]=( expanded-and-quoted-VALUE ). */
+static void
+expand_compound_assignment_word (tlist, flags)
+ WORD_LIST *tlist;
+ int flags;
+{
+ WORD_LIST *l;
+ int wlen, oind, t;
+ char *value, *temp;
+
+/*itrace("expand_compound_assignment_word: original word = -%s-", tlist->word->word);*/
+ wlen = strlen (tlist->word->word);
+ t = assignment (tlist->word->word, 0);
+
+ /* value doesn't have the open and close parens */
+ oind = 1;
+ value = extract_array_assignment_list (tlist->word->word + t + 1, &oind);
+ /* This performs one round of expansion on the index/key and value and
+ single-quotes each word in the result. */
+ l = expand_oneword (value, flags);
+ free (value);
+
+ value = string_list (l);
+ wlen = STRLEN (value);
+
+ /* Now, let's rebuild the string */
+ temp = xmalloc (t + 3 + wlen + 1); /* name[+]=(value) */
+ memcpy (temp, tlist->word->word, ++t);
+ temp[t++] = '(';
+ if (value)
+ memcpy (temp + t, value, wlen);
+ t += wlen;
+ temp[t++] = ')';
+ temp[t] = '\0';
+/*itrace("expand_compound_assignment_word: reconstructed word = -%s-", temp);*/
+
+ free (tlist->word->word);
+ tlist->word->word = temp;
+
+ free (value);
+}
+
+/* Expand and process an argument to a declaration command. We have already
+ set flags in TLIST->word->flags depending on the declaration command
+ (declare, local, etc.) and the options supplied to it (-a, -A, etc.).
+ TLIST->word->word is of the form NAME[+]=( VALUE ).
+
+ This does several things, all using pieces of other functions to get the
+ evaluation sequence right. It's called for compound array assignments with
+ the W_ASSIGNMENT flag set (basically, valid identifier names on the lhs).
+ It parses out which flags need to be set for declare to create the variable
+ correctly, then calls declare internally (make_internal_declare) to make
+ sure the variable exists with the correct attributes. Before the variable
+ is created, it calls expand_compound_assignment_word to expand VALUE to a
+ list of words, appropriately quoted for further evaluation. This preserves
+ the semantics of word-expansion-before-calling-builtins. Finally, it calls
+ do_word_assignment to perform the expansion and assignment with the same
+ expansion semantics as a standalone assignment statement (no word splitting,
+ etc.) even though the word is single-quoted so all that needs to happen is
+ quote removal. */
+static WORD_LIST *
+expand_declaration_argument (tlist, wcmd)
+ WORD_LIST *tlist, *wcmd;
+{
+ char opts[16], omap[128];
+ int t, opti, oind, skip, inheriting;
+ WORD_LIST *l;
+
+ inheriting = localvar_inherit;
+ opti = 0;
+ if (tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL|W_CHKLOCAL|W_ASSIGNARRAY))
+ opts[opti++] = '-';
+
+ if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL)) == (W_ASSIGNASSOC|W_ASSNGLOBAL))
+ {
+ opts[opti++] = 'g';
+ opts[opti++] = 'A';
+ }
+ else if (tlist->word->flags & W_ASSIGNASSOC)
+ {
+ opts[opti++] = 'A';
+ }
+ else if ((tlist->word->flags & (W_ASSIGNARRAY|W_ASSNGLOBAL)) == (W_ASSIGNARRAY|W_ASSNGLOBAL))
+ {
+ opts[opti++] = 'g';
+ opts[opti++] = 'a';
+ }
+ else if (tlist->word->flags & W_ASSIGNARRAY)
+ {
+ opts[opti++] = 'a';
+ }
+ else if (tlist->word->flags & W_ASSNGLOBAL)
+ opts[opti++] = 'g';
+
+ if (tlist->word->flags & W_CHKLOCAL)
+ opts[opti++] = 'G';
+
+ /* If we have special handling note the integer attribute and others
+ that transform the value upon assignment. What we do is take all
+ of the option arguments and scan through them looking for options
+ that cause such transformations, and add them to the `opts' array. */
+
+ memset (omap, '\0', sizeof (omap));
+ for (l = wcmd->next; l != tlist; l = l->next)
+ {
+ if (l->word->word[0] != '-')
+ break; /* non-option argument */
+ if (l->word->word[0] == '-' && l->word->word[1] == '-' && l->word->word[2] == 0)
+ break; /* -- signals end of options */
+ for (oind = 1; l->word->word[oind]; oind++)
+ switch (l->word->word[oind])
+ {
+ case 'I':
+ inheriting = 1;
+ case 'i':
+ case 'l':
+ case 'u':
+ case 'c':
+ omap[l->word->word[oind]] = 1;
+ if (opti == 0)
+ opts[opti++] = '-';
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (oind = 0; oind < sizeof (omap); oind++)
+ if (omap[oind])
+ opts[opti++] = oind;
+
+ /* If there are no -a/-A options, but we have a compound assignment,
+ we have a choice: we can set opts[0]='-', opts[1]='a', since the
+ default is to create an indexed array, and call
+ make_internal_declare with that, or we can just skip the -a and let
+ declare_builtin deal with it. Once we're here, we're better set
+ up for the latter, since we don't want to deal with looking up
+ any existing variable here -- better to let declare_builtin do it.
+ We need the variable created, though, especially if it's local, so
+ we get the scoping right before we call do_word_assignment.
+ To ensure that make_local_declare gets called, we add `--' if there
+ aren't any options. */
+ if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSIGNARRAY)) == 0)
+ {
+ if (opti == 0)
+ {
+ opts[opti++] = '-';
+ opts[opti++] = '-';
+ }
+ }
+ opts[opti] = '\0';
+
+ /* This isn't perfect, but it's a start. Improvements later. We expand
+ tlist->word->word and single-quote the results to avoid multiple
+ expansions by, say, do_assignment_internal(). We have to weigh the
+ cost of reconstructing the compound assignment string with its single
+ quoting and letting the declare builtin handle it. The single quotes
+ will prevent any unwanted additional expansion or word splitting. */
+ expand_compound_assignment_word (tlist, (tlist->word->flags & W_ASSIGNASSOC) ? 1 : 0);
+
+ skip = 0;
+ if (opti > 0)
+ {
+ t = make_internal_declare (tlist->word->word, opts, wcmd ? wcmd->word->word : (char *)0);
+ if (t != EXECUTION_SUCCESS)
+ {
+ last_command_exit_value = t;
+ if (tlist->word->flags & W_FORCELOCAL) /* non-fatal error */
+ skip = 1;
+ else
+ exp_jump_to_top_level (DISCARD);
+ }
+ }
+
+ if (skip == 0)
+ {
+ t = do_word_assignment (tlist->word, 0);
+ if (t == 0)
+ {
+ last_command_exit_value = EXECUTION_FAILURE;
+ exp_jump_to_top_level (DISCARD);
+ }
+ }
+
+ /* Now transform the word as ksh93 appears to do and go on */
+ t = assignment (tlist->word->word, 0);
+ tlist->word->word[t] = '\0';
+ if (tlist->word->word[t - 1] == '+')
+ tlist->word->word[t - 1] = '\0'; /* cut off append op */
+ tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC|W_ASSIGNARRAY);
+
+ return (tlist);
+}
+#endif /* ARRAY_VARS */
static WORD_LIST *
shell_expand_word_list (tlist, eflags)
int expanded_something, has_dollar_at;
/* We do tilde expansion all the time. This is what 1003.2 says. */
- new_list = (WORD_LIST *)NULL;
- for (wcmd = tlist; wcmd; wcmd = wcmd->next)
- if (wcmd->word->flags & W_ASSNBLTIN)
- break;
+ wcmd = new_list = (WORD_LIST *)NULL;
for (orig_list = tlist; tlist; tlist = next)
{
+ if (wcmd == 0 && (tlist->word->flags & W_ASSNBLTIN))
+ wcmd = tlist;
+
next = tlist->next;
#if defined (ARRAY_VARS)
because `declare' does some evaluation of compound assignments on
its own. */
if ((tlist->word->flags & (W_COMPASSIGN|W_ASSIGNARG)) == (W_COMPASSIGN|W_ASSIGNARG))
- {
- int t;
- char opts[16];
- int opti, skip, inheriting, array;
-
- inheriting = localvar_inherit;
- opti = 0;
- if (tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL|W_CHKLOCAL|W_ASSIGNARRAY))
- opts[opti++] = '-';
-
- if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL)) == (W_ASSIGNASSOC|W_ASSNGLOBAL))
- {
- opts[opti++] = 'g';
- opts[opti++] = 'A';
- }
- else if (tlist->word->flags & W_ASSIGNASSOC)
- {
- opts[opti++] = 'A';
- /* This doesn't work right if a variable with the same name but
- a different type exists at a previous scope; it generates
- errors that a user would find confusing. */
-/* opts[opti++] = 'I'; */
- }
- else if ((tlist->word->flags & (W_ASSIGNARRAY|W_ASSNGLOBAL)) == (W_ASSIGNARRAY|W_ASSNGLOBAL))
- {
- opts[opti++] = 'g';
- opts[opti++] = 'a';
- }
- else if (tlist->word->flags & W_ASSIGNARRAY)
- {
- opts[opti++] = 'a';
-/* opts[opti++] = 'I'; */
- }
- else if (tlist->word->flags & W_ASSNGLOBAL)
- opts[opti++] = 'g';
-
- if (tlist->word->flags & W_CHKLOCAL)
- opts[opti++] = 'G';
-
- /* If we have special handling note the integer attribute and others
- that transform the value upon assignment. What we do is take all
- of the option arguments and scan through them looking for options
- that cause such transformations, and add them to the `opts' array. */
-/* if (opti > 0) */
- {
- char omap[128];
- int oind;
- WORD_LIST *l;
-
- memset (omap, '\0', sizeof (omap));
- for (l = orig_list->next; l != tlist; l = l->next)
- {
- if (l->word->word[0] != '-')
- break; /* non-option argument */
- if (l->word->word[0] == '-' && l->word->word[1] == '-' && l->word->word[2] == 0)
- break; /* -- signals end of options */
- for (oind = 1; l->word->word[oind]; oind++)
- switch (l->word->word[oind])
- {
- case 'I':
- inheriting = 1;
- case 'i':
- case 'l':
- case 'u':
- case 'c':
- omap[l->word->word[oind]] = 1;
- if (opti == 0)
- opts[opti++] = '-';
- break;
- default:
- break;
- }
- }
-
- for (oind = 0; oind < sizeof (omap); oind++)
- if (omap[oind])
- opts[opti++] = oind;
- }
-
- /* If there are no -a/-A options, but we have a compound assignment,
- we have a choice: we can set opts[0]='-', opt[1]='a', since the
- default is to create an indexed array, and call
- make_internal_declare, or we can just skip it and let
- declare_builtin deal with it. Once we're here, we're better set
- up for the former. We don't do this if we're inheriting local
- variables' attributes and values here, since that makes `-a' no
- longer the default; we pass `--' instead if we don't have any
- options at all, and just leave off the -a if we have some. */
- if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSIGNARRAY)) == 0)
- {
- if (opti == 0)
- {
- opts[opti++] = '-';
- opts[opti++] = inheriting ? '-' : 'a';
- }
- else if (inheriting == 0)
- opts[opti++] = 'a';
- }
-
- opts[opti] = '\0';
- skip = 0;
- if (opti > 0)
- {
- t = make_internal_declare (tlist->word->word, opts, wcmd ? wcmd->word->word : (char *)0);
- if (t != EXECUTION_SUCCESS)
- {
- last_command_exit_value = t;
- if (tlist->word->flags & W_FORCELOCAL) /* non-fatal error */
- skip = 1;
- else
- exp_jump_to_top_level (DISCARD);
- }
- }
-
- if (skip == 0)
- {
- t = do_word_assignment (tlist->word, 0);
- if (t == 0)
- {
- last_command_exit_value = EXECUTION_FAILURE;
- exp_jump_to_top_level (DISCARD);
- }
- }
-
- /* Now transform the word as ksh93 appears to do and go on */
- t = assignment (tlist->word->word, 0);
- tlist->word->word[t] = '\0';
- if (tlist->word->word[t - 1] == '+')
- tlist->word->word[t - 1] = '\0'; /* cut off append op */
- tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC|W_ASSIGNARRAY);
- }
+ expand_declaration_argument (tlist, wcmd);
#endif
expanded_something = 0;
dict=( "?" "quest" "*" "star" "'" "squote" "\$" "dol" "\"" "dquote" "\\" "bslash" "@" "at" "}" "rbrace" "{" "lbrace" "\`" "bquote" )
declare -A foo=([two]="" [one]="1" )
foo=( two "" one "1" )
+rparen dquote rbrace bs
+declare -A a=([")"]="rparen" ["\""]="dquote" ["]"]="rbrace" ["\\"]="bs" )
+")" "rparen" "\"" "dquote" "]" "rbrace" "\\" "bs"
+declare -A a=([")"]="rparen" ["\""]="dquote" ["]"]="rbrace" ["\\"]="bs" )
+declare -A a=([")"]="rparen" ["\""]="dquote" ["]"]="rbrace" ["\\"]="bs" )
+declare -A a=([")"]="rparen" ["\""]="dquote" ["]"]="rbrace" ["\\"]="bs" )
+declare -Arx foo=([two]="2" [three]="3" [one]="1" )
+./assoc11.sub: line 90: foo: readonly variable
foo=(one 1 two)
declare -p foo
echo foo=\( ${foo[@]@K} \)
+
+typeset -A a=( [\\]=bs [\"]=dquote [\)]=rparen [\]]=rbrace )
+echo ${a[@]}
+declare -p a
+
+echo ${a[@]@K}
+echo ${a[@]@A}
+
+eval "${a[@]@A}"
+declare -p a
+
+eval "a=( ${a[@]@K} )"
+declare -p a
+
+unset a foo
+readonly -A foo=( one 1 two 2 three 3 )
+
+export foo
+declare -p foo
+declare foo+=( seven 7 eight 8 )
+echo "warning: some of these tests will fail if arrays have not" >&2
+echo "warning: been compiled into the shell" >&2
${THIS_SH} ./varenv.tests 2>&1 | grep -v '^expect' > ${BASH_TSTOUT}
diff ${BASH_TSTOUT} varenv.right && rm -f ${BASH_TSTOUT}
declare -- var="local"
declare -- var="f1"
declare -- var="local"
-declare -a arr=()
+declare -a arr=([0]="zero" [1]="one" [2]="two" [3]="three" [4]="four" [5]="five")
+declare -a arr=([0]="zero" [1]="one" [2]="two")
declare -a arr=([0]="three" [1]="four" [2]="five")
-./varenv18.sub: line 39: !name: unbound variable
+declare -a arr=([0]="zero" [1]="one" [2]="two")
+ddd 0
+aaa 1 2 3
+bbb 4 5 6
+ccc 7 8 9
+declare -a x=([0]="one" [1]="two" [2]="three")
+./varenv19.sub: line 51: declare: x: not found
a=z
a=b
a=z
${THIS_SH} ./varenv16.sub
${THIS_SH} ./varenv17.sub
${THIS_SH} ./varenv18.sub
+${THIS_SH} ./varenv19.sub
# make sure variable scoping is done right
tt() { typeset a=b;echo a=$a; };a=z;echo a=$a;tt;echo a=$a
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-# THIS DOESN'T WORK RIGHT YET
-
arr=(zero one two)
four=four
f()
{
local -a arr=( "${arr[@]}" )
+ arr+=(three four five)
declare -p arr
}
-
f
+declare -p arr
f1()
{
--- /dev/null
+# This program 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.
+#
+# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# variable attribute inheritance problems without specifying -a or -A
+
+function aaa() {
+ local x='1 2 3'
+ echo "aaa ${x}"
+}
+
+function bbb {
+ local x
+ x=(4 5 6)
+ echo "bbb ${x[*]}"
+}
+
+ccc()
+{
+ local x=(7 8 9)
+ echo "ccc ${x[*]}"
+}
+
+function ddd
+{
+ local -r x='0'
+ echo "ddd ${x}"
+ aaa
+ bbb
+ ccc
+}
+
+ddd
+
+f()
+{
+ local x=(one two three)
+ declare -p x
+}
+f
+declare -p x