mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-28 16:09:51 +02:00
commit bash-20200427 snapshot
This commit is contained in:
@@ -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 <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
|
||||
|
||||
@@ -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
|
||||
|
||||
+124
-4
@@ -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
|
||||
|
||||
+28
-25
@@ -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
|
||||
|
||||
|
||||
+1
-1
@@ -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_ */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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}
|
||||
|
||||
+9
-2
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-3
@@ -12,18 +12,17 @@
|
||||
# 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()
|
||||
{
|
||||
|
||||
@@ -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 <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
|
||||
Reference in New Issue
Block a user