From: Chet Ramey Date: Thu, 30 Apr 2020 15:59:39 +0000 (-0400) Subject: commit bash-20200427 snapshot X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c6c7ae81bb0db03acb1085ac4692934856bf7302;p=thirdparty%2Fbash.git commit bash-20200427 snapshot --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index da7f24c60..051945bc0 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -8211,3 +8211,46 @@ subst.c 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 + + 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 + , + 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 diff --git a/MANIFEST b/MANIFEST index b31e8595d..1beac61f1 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1447,6 +1447,7 @@ tests/varenv15.in f 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 diff --git a/arrayfunc.c b/arrayfunc.c index 6d0aa6633..5b0dc2f1e 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -40,6 +40,11 @@ #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; @@ -54,6 +59,7 @@ static void assign_assoc_from_kvlist PARAMS((SHELL_VAR *, WORD_LIST *, HASH_TABL 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 */ @@ -594,9 +600,11 @@ assign_assoc_from_kvlist (var, nlist, h, flags) #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 @@ -632,7 +640,7 @@ assign_compound_array_list (var, nlist, flags) 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); @@ -851,6 +859,118 @@ quote_assign (string) 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 diff --git a/arrayfunc.h b/arrayfunc.h index cad13ab42..c2bbb98a6 100644 --- a/arrayfunc.h +++ b/arrayfunc.h @@ -1,6 +1,6 @@ /* 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. @@ -44,41 +44,44 @@ extern int array_expand_once; #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 diff --git a/patchlevel.h b/patchlevel.h index 9074f4ddf..98e714da8 100644 --- a/patchlevel.h +++ b/patchlevel.h @@ -25,6 +25,6 @@ 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_ */ diff --git a/subst.c b/subst.c index e5e05df22..05f46c510 100644 --- a/subst.c +++ b/subst.c @@ -354,6 +354,8 @@ static WORD_LIST *brace_expand_word_list PARAMS((WORD_LIST *, int)); #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)); @@ -11504,7 +11506,260 @@ make_internal_declare (word, option, cmd) 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) @@ -11515,13 +11770,13 @@ 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) @@ -11532,137 +11787,7 @@ shell_expand_word_list (tlist, eflags) 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; diff --git a/tests/assoc.right b/tests/assoc.right index e1e45fcbc..d176bcd47 100644 --- a/tests/assoc.right +++ b/tests/assoc.right @@ -249,3 +249,11 @@ declare -A dict=(["?"]="quest" ["*"]="star" ["'"]="squote" ["\$"]="dol" ["\""]=" 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 diff --git a/tests/assoc11.sub b/tests/assoc11.sub index 73200f35b..13111a52b 100644 --- a/tests/assoc11.sub +++ b/tests/assoc11.sub @@ -68,3 +68,23 @@ loaddict 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 ) diff --git a/tests/run-varenv b/tests/run-varenv index f147d70bf..be394cc74 100644 --- a/tests/run-varenv +++ b/tests/run-varenv @@ -1,2 +1,4 @@ +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} diff --git a/tests/varenv.right b/tests/varenv.right index 34a130f8f..81cdd09f6 100644 --- a/tests/varenv.right +++ b/tests/varenv.right @@ -246,9 +246,16 @@ declare -- var 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 diff --git a/tests/varenv.tests b/tests/varenv.tests index 495fe7eda..16371a851 100644 --- a/tests/varenv.tests +++ b/tests/varenv.tests @@ -256,6 +256,7 @@ ${THIS_SH} ./varenv15.sub ${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 diff --git a/tests/varenv18.sub b/tests/varenv18.sub index e587a7c75..4ad7f1a46 100644 --- a/tests/varenv18.sub +++ b/tests/varenv18.sub @@ -12,18 +12,17 @@ # along with this program. If not, see . # -# 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() { diff --git a/tests/varenv19.sub b/tests/varenv19.sub new file mode 100644 index 000000000..753f508cd --- /dev/null +++ b/tests/varenv19.sub @@ -0,0 +1,51 @@ +# 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 . +# + +# 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