From 35bc7025c1db5078eb157eea7c0436754ac91aa3 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Fri, 7 May 2021 10:56:24 -0400 Subject: [PATCH] first set of changes to eliminate array subscript double expansion in math contexts --- CWRU/CWRU.chlog | 57 +++++++++++++++ MANIFEST | 8 +++ arrayfunc.c | 29 ++++---- doc/bash.1 | 10 +-- doc/bashref.texi | 11 ++- doc/version.texi | 6 +- execute_cmd.c | 71 ++++++++++++++----- lib/sh/zread.c | 2 + subst.c | 64 ++++++++++++----- subst.h | 1 + test.c | 24 +++++-- test.h | 3 +- tests/array.right | 17 ++--- tests/assoc.right | 9 +++ tests/assoc.tests | 3 + tests/assoc13.sub | 44 ++++++++++++ tests/quotearray.right | 85 ++++++++++++++++++++++ tests/quotearray.tests | 157 +++++++++++++++++++++++++++++++++++++++++ tests/quotearray1.sub | 105 +++++++++++++++++++++++++++ tests/quotearray2.sub | 92 ++++++++++++++++++++++++ tests/quotearray3.sub | 74 +++++++++++++++++++ tests/run-quotearray | 2 + 22 files changed, 797 insertions(+), 77 deletions(-) create mode 100644 tests/assoc13.sub create mode 100644 tests/quotearray.right create mode 100644 tests/quotearray.tests create mode 100644 tests/quotearray1.sub create mode 100644 tests/quotearray2.sub create mode 100644 tests/quotearray3.sub create mode 100644 tests/run-quotearray diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index ecc0159d..3f6c37e7 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -10151,3 +10151,60 @@ builtins/read.def builtins/mapfile.def - mapfile: call zsyncfd before calling the callback. Suggested by Koichi Murase ; we'll see how it goes + + 5/5 + --- +subst.h + - expand_subscript_string: extern declaration + +{arrayfunc,subst}.c + - expand_subscript_string: replace expand_assignment_string_to_string + with calls to this when expanding array subscripts + +subst.c + - cond_expand_word: call expand_word_internal with Q_ARITH if `special' + says we should quote for an arithmetic expression context + - expand_word_internal: call expand_array_subscript when we see `[' in + arithmetic or array subscript contexts, make conditional on the + compatibility level later + - expand_word_internal: make sure W_ARRAYREF makes it through this + function and into the returned word + + 5/6 + --- + +arrayfunc.c + - array_expand_index: call evalexp with a flag of 0 since we call + expand_arith_string with Q_ARITH and we want evalexp to remove + the quotes + +execute_cmd.c + - eval_arith_for_expr,execute_arith_command: now that Q_ARITH has an + effect on array subscripts (it quotes the special expansion + characters), call evalexp with 0 as the flags arg so quote removal + is performed on this quoted argument. Make this conditional on the + shell compatibility level later + - execute_cond_command: make sure to expand the argument to -v by + calling cond_expand_node with Q_ARITH, and correspondingly turn off + assoc_expand_once when calling unary_test with that argument, since + we want it to be expanded again to remove the quotes + - execute_cond_command: expand the arguments to the arithmetic operators + with Q_ARITH and pass TEST_ARITHEXP to binary_test to ensure that + it lets evalexp expand the arguments to remove the quoting + +test.c + - arithcomp: if TEST_ARITHEXP is in FLAGS, call evalexp with a flag + if 0 to force evalexp to expand the arguments to remove the quoting + +subst.c + - param_expand: since we call expand_arith_string with Q_ARITH, we need + to call evalexp with 0 instead of EXP_EXPANDED for $((...)) expansion + - expand_word_internal: if we recursively call expand_word_internal to + expand the contents of a double-quoted string, make sure we pass + through Q_ARITH if it appears in QUOTED + - verify_substring_values: call expand_arith_string with Q_ARITH and + correspondingly call evalexp with 0 instead of EXP_EXPANDED + +execute_cmd.c + - execute_cond_node: if -v is the operator, and the operand is a valid + array reference, pass TEST_ARRAYEXP flag to unary_test diff --git a/MANIFEST b/MANIFEST index 6fa77817..2f22d30b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -948,6 +948,7 @@ tests/assoc9.sub f tests/assoc10.sub f tests/assoc11.sub f tests/assoc12.sub f +tests/assoc13.sub f tests/attr.tests f tests/attr.right f tests/attr1.sub f @@ -1307,6 +1308,11 @@ tests/quote1.sub f tests/quote2.sub f tests/quote3.sub f tests/quote4.sub f +tests/quotearray.right f +tests/quotearray.tests f +tests/quotearray1.sub f +tests/quotearray2.sub f +tests/quotearray3.sub f tests/read.tests f tests/read.right f tests/read1.sub f @@ -1316,6 +1322,7 @@ tests/read4.sub f tests/read5.sub f tests/read6.sub f tests/read7.sub f +tests/read8.sub f tests/redir.tests f tests/redir.right f tests/redir1.sub f @@ -1408,6 +1415,7 @@ tests/run-precedence f tests/run-printf f tests/run-procsub f tests/run-quote f +tests/run-quotearray f tests/run-read f tests/run-redir f tests/run-rhs-exp f diff --git a/arrayfunc.c b/arrayfunc.c index 468935a6..873d7ca4 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -374,7 +374,7 @@ assign_array_element_internal (entry, name, vname, sub, sublen, value, flags) { sub[sublen-1] = '\0'; if ((flags & ASS_NOEXPAND) == 0) - akey = expand_assignment_string_to_string (sub, 0); /* [ */ + akey = expand_subscript_string (sub, 0); /* [ */ else akey = savestring (sub); sub[sublen-1] = ']'; @@ -579,7 +579,7 @@ assign_assoc_from_kvlist (var, nlist, h, flags) if (list->next) list = list->next; - akey = expand_assignment_string_to_string (k, 0); + akey = expand_subscript_string (k, 0); if (akey == 0 || *akey == 0) { err_badarraysub (k); @@ -587,7 +587,7 @@ assign_assoc_from_kvlist (var, nlist, h, flags) continue; } - aval = expand_assignment_string_to_string (v, 0); + aval = expand_subscript_string (v, 0); if (aval == 0) { aval = (char *)xmalloc (1); @@ -614,7 +614,7 @@ expand_and_quote_kvpair_word (w) { char *t, *r; - t = w ? expand_assignment_string_to_string (w, 0) : 0; + t = w ? expand_subscript_string (w, 0) : 0; r = sh_single_quote (t ? t : ""); free (t); return r; @@ -740,7 +740,7 @@ assign_compound_array_list (var, nlist, flags) { /* This is not performed above, see expand_compound_array_assignment */ w[len] = '\0'; /*[*/ - akey = expand_assignment_string_to_string (w+1, 0); + akey = expand_subscript_string (w+1, 0); w[len] = ']'; /* And we need to expand the value also, see below */ if (akey == 0 || *akey == 0) @@ -776,7 +776,7 @@ assign_compound_array_list (var, nlist, flags) /* See above; we need to expand the value here */ if (assoc_p (var)) { - val = expand_assignment_string_to_string (val, 0); + val = expand_subscript_string (val, 0); if (val == 0) { val = (char *)xmalloc (1); @@ -938,7 +938,7 @@ expand_and_quote_assoc_word (w, type) return (sh_single_quote (w)); w[ind] = '\0'; - t = expand_assignment_string_to_string (w+1, 0); + t = expand_subscript_string (w+1, 0); w[ind] = RBRACK; key = sh_single_quote (t ? t : ""); free (t); @@ -954,7 +954,7 @@ expand_and_quote_assoc_word (w, type) nword[i++] = w[ind++]; nword[i++] = w[ind++]; - t = expand_assignment_string_to_string (w+ind, 0); + t = expand_subscript_string (w+ind, 0); value = sh_single_quote (t ? t : ""); free (t); nword = xrealloc (nword, wlen + 5 + STRLEN (value)); @@ -1070,7 +1070,7 @@ unbind_array_element (var, sub, flags) if (assoc_p (var)) { - akey = (flags & VA_NOEXPAND) ? sub : expand_assignment_string_to_string (sub, 0); + akey = (flags & VA_NOEXPAND) ? sub : expand_subscript_string (sub, 0); if (akey == 0 || *akey == 0) { builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg)); @@ -1218,7 +1218,7 @@ array_expand_index (var, s, len, flags) int flags; { char *exp, *t, *savecmd; - int expok; + int expok, eflag; arrayind_t val; exp = (char *)xmalloc (len); @@ -1234,7 +1234,12 @@ array_expand_index (var, s, len, flags) #endif savecmd = this_command_name; this_command_name = (char *)NULL; - val = evalexp (t, EXP_EXPANDED, &expok); /* XXX - was 0 but we expanded exp already */ +#if 0 /* TAG:bash-5.2 */ + eflag = (shell_compatibility_level > 51) ? 0 : EXP_EXPANDED; +#else + eflag = 0; +#endif + val = evalexp (t, eflag, &expok); /* XXX - was 0 but we expanded exp already */ this_command_name = savecmd; if (t != exp) free (t); @@ -1435,7 +1440,7 @@ array_value_internal (s, quoted, flags, rtype, indp) { t[len - 1] = '\0'; if ((flags & AV_NOEXPAND) == 0) - akey = expand_assignment_string_to_string (t, 0); /* [ */ + akey = expand_subscript_string (t, 0); /* [ */ else akey = savestring (t); t[len - 1] = ']'; diff --git a/doc/bash.1 b/doc/bash.1 index c5ccacd6..41254999 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -5,12 +5,12 @@ .\" Case Western Reserve University .\" chet.ramey@case.edu .\" -.\" Last Change: Wed Apr 28 14:35:46 EDT 2021 +.\" Last Change: Wed May 5 16:28:46 EDT 2021 .\" .\" bash_builtins, strip all but Built-Ins section .if \n(zZ=1 .ig zZ .if \n(zY=1 .ig zY -.TH BASH 1 "2021 April 28" "GNU Bash 5.1" +.TH BASH 1 "2021 May 5" "GNU Bash 5.1" .\" .\" There's some problem with having a `@' .\" in a tagged paragraph with the BSD man macros. @@ -700,8 +700,10 @@ below under .SM .BR "ARITHMETIC EVALUATION" . If the value of the expression is non-zero, the return status is 0; -otherwise the return status is 1. This is exactly equivalent to -\fBlet "\fIexpression\fP"\fR. +otherwise the return status is 1. +The \fIexpression\fP is expanded as if it were within double quotes, +but double quote characters in \fIexpression\fP are not special and +are removed. .TP \fB[[\fP \fIexpression\fP \fB]]\fP Return a status of 0 or 1 depending on the evaluation of diff --git a/doc/bashref.texi b/doc/bashref.texi index 3e408c0a..77b9cb36 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -1126,13 +1126,12 @@ done The arithmetic @var{expression} is evaluated according to the rules described below (@pxref{Shell Arithmetic}). +The @var{expression} is expanded as if it were within double quotes, +but double quote characters in @var{expression} are not special and +are removed. If the value of the expression is non-zero, the return status is 0; -otherwise the return status is 1. This is exactly equivalent to -@example -let "@var{expression}" -@end example -@noindent -@xref{Bash Builtins}, for a full description of the @code{let} builtin. +otherwise the return status is 1. + @item [[@dots{}]] @rwindex [[ diff --git a/doc/version.texi b/doc/version.texi index 2d51ded3..f51259df 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -2,10 +2,10 @@ Copyright (C) 1988-2021 Free Software Foundation, Inc. @end ignore -@set LASTCHANGE Wed Mar 31 11:53:05 EDT 2021 +@set LASTCHANGE Wed May 5 16:29:06 EDT 2021 @set EDITION 5.1 @set VERSION 5.1 -@set UPDATED 31 March 2021 -@set UPDATED-MONTH March 2021 +@set UPDATED 5 May 2021 +@set UPDATED-MONTH May 2021 diff --git a/execute_cmd.c b/execute_cmd.c index 840ea550..0c629d5f 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -3010,7 +3010,7 @@ eval_arith_for_expr (l, okp) { WORD_LIST *new; intmax_t expresult; - int r; + int r, eflag; char *expr, *temp; expr = l->next ? string_list (l) : l->word->word; @@ -3037,9 +3037,15 @@ eval_arith_for_expr (l, okp) r = run_debug_trap (); /* In debugging mode, if the DEBUG trap returns a non-zero status, we skip the command. */ +#if 0 /* TAG:bash-5.2 */ + eflag = (shell_compatibility_level > 51) ? 0 : EXP_EXPANDED; +#else + eflag = 0; +#endif + #if defined (DEBUGGER) if (debugging_mode == 0 || r == EXECUTION_SUCCESS) - expresult = evalexp (new->word->word, EXP_EXPANDED, okp); + expresult = evalexp (new->word->word, eflag, okp); else { expresult = 0; @@ -3047,7 +3053,7 @@ eval_arith_for_expr (l, okp) *okp = 1; } #else - expresult = evalexp (new->word->word, EXP_EXPANDED, okp); + expresult = evalexp (new->word->word, eflag, okp); #endif dispose_words (new); } @@ -3744,7 +3750,7 @@ static int execute_arith_command (arith_command) ARITH_COM *arith_command; { - int expok, save_line_number, retval; + int expok, save_line_number, retval, eflag; intmax_t expresult; WORD_LIST *new; char *exp, *t; @@ -3805,7 +3811,12 @@ execute_arith_command (arith_command) if (exp) { - expresult = evalexp (exp, EXP_EXPANDED, &expok); +#if 0 /* TAG:bash-5.2 */ + eflag = (shell_compatibility_level > 51) ? 0 : EXP_EXPANDED; +#else + eflag = 0; +#endif + expresult = evalexp (exp, eflag, &expok); line_number = save_line_number; free (exp); } @@ -3831,8 +3842,8 @@ static int execute_cond_node (cond) COND_COM *cond; { - int result, invert, patmatch, rmatch, mflags, ignore; - char *arg1, *arg2; + int result, invert, patmatch, rmatch, arith, mode, mflags, ignore; + char *arg1, *arg2, *op; #if 0 char *t1, *t2; #endif @@ -3863,41 +3874,67 @@ execute_cond_node (cond) } else if (cond->type == COND_UNARY) { + int oa, varop, varflag; + if (ignore) comsub_ignore_return++; - arg1 = cond_expand_word (cond->left->op, 0); + varop = STREQ (cond->op->word, "-v"); +#if defined (ARRAY_VARS) + varflag = (varop && valid_array_reference (cond->left->op->word, VA_NOEXPAND)) ? TEST_ARRAYEXP : 0; +#else + varflag = 0; +#endif + arg1 = cond_expand_word (cond->left->op, varop ? 3 : 0); if (ignore) comsub_ignore_return--; if (arg1 == 0) arg1 = nullstr; if (echo_command_at_execute) xtrace_print_cond_term (cond->type, invert, cond->op, arg1, (char *)NULL); - result = unary_test (cond->op->word, arg1) ? EXECUTION_SUCCESS : EXECUTION_FAILURE; + /* TAG:bash-5.2 fix up later with set_expand_once() */ + oa = assoc_expand_once; +#if 0 + if (varop && shell_compatibility_level > 51) +#else + if (varop) +#endif + assoc_expand_once = 0; + result = unary_test (cond->op->word, arg1, varflag) ? EXECUTION_SUCCESS : EXECUTION_FAILURE; + assoc_expand_once = oa; if (arg1 != nullstr) free (arg1); } else if (cond->type == COND_BINARY) { rmatch = 0; - patmatch = (((cond->op->word[1] == '=') && (cond->op->word[2] == '\0') && - (cond->op->word[0] == '!' || cond->op->word[0] == '=')) || - (cond->op->word[0] == '=' && cond->op->word[1] == '\0')); + op = cond->op->word; + mode = 0; + patmatch = (((op[1] == '=') && (op[2] == '\0') && + (op[0] == '!' || op[0] == '=')) || + (op[0] == '=' && op[1] == '\0')); #if defined (COND_REGEXP) - rmatch = (cond->op->word[0] == '=' && cond->op->word[1] == '~' && - cond->op->word[2] == '\0'); + rmatch = (op[0] == '=' && op[1] == '~' && op[2] == '\0'); #endif + arith = STREQ (op, "-eq") || STREQ (op, "-ne") || STREQ (op, "-lt") || + STREQ (op, "-le") || STREQ (op, "-gt") || STREQ (op, "-ge"); + + if (arith) + mode = 3; + else if (rmatch && shell_compatibility_level > 31) + mode = 2; + else if (patmatch) + mode = 1; if (ignore) comsub_ignore_return++; - arg1 = cond_expand_word (cond->left->op, 0); + arg1 = cond_expand_word (cond->left->op, arith ? mode : 0); if (ignore) comsub_ignore_return--; if (arg1 == 0) arg1 = nullstr; if (ignore) comsub_ignore_return++; - arg2 = cond_expand_word (cond->right->op, - (rmatch && shell_compatibility_level > 31) ? 2 : (patmatch ? 1 : 0)); + arg2 = cond_expand_word (cond->right->op, mode); if (ignore) comsub_ignore_return--; if (arg2 == 0) diff --git a/lib/sh/zread.c b/lib/sh/zread.c index d8a74221..dafb7f60 100644 --- a/lib/sh/zread.c +++ b/lib/sh/zread.c @@ -59,6 +59,8 @@ zread (fd, buf, len) ssize_t r; check_signals (); /* check for signals before a blocking read */ + /* should generalize into a mechanism where different parts of the shell can + `register' timeouts and have them checked here. */ while (((r = read_builtin_timeout (fd)) < 0 || (r = read (fd, buf, len)) < 0) && errno == EINTR) { diff --git a/subst.c b/subst.c index 0d8c4879..385e4596 100644 --- a/subst.c +++ b/subst.c @@ -3594,7 +3594,7 @@ expand_arith_string (string, quoted) if (EXP_CHAR (string[i])) break; else if (string[i] == '\'' || string[i] == '\\' || string[i] == '"') - saw_quote = 1; + saw_quote = string[i]; ADVANCE_CHAR (string, slen, i); } @@ -3684,7 +3684,8 @@ cond_expand_word (w, special) expand_no_split_dollar_star = 1; w->flags |= W_NOSPLIT2; - l = call_expand_word_internal (w, 0, 0, (int *)0, (int *)0); + qflags = (special == 3) ? Q_ARITH : 0; + l = call_expand_word_internal (w, qflags, 0, (int *)0, (int *)0); expand_no_split_dollar_star = 0; if (l) { @@ -6714,7 +6715,7 @@ array_length_reference (s) if (assoc_p (var)) { t[len - 1] = '\0'; - akey = expand_assignment_string_to_string (t, 0); /* [ */ + akey = expand_subscript_string (t, 0); /* [ */ t[len - 1] = RBRACK; if (akey == 0 || *akey == 0) { @@ -7526,7 +7527,7 @@ verify_substring_values (v, value, substr, vtype, e1p, e2p) { char *t, *temp1, *temp2; arrayind_t len; - int expok; + int expok, eflag; #if defined (ARRAY_VARS) ARRAY *a; HASH_TABLE *h; @@ -7539,12 +7540,14 @@ verify_substring_values (v, value, substr, vtype, e1p, e2p) else t = (char *)0; - temp1 = expand_arith_string (substr, Q_DOUBLE_QUOTES); /* Q_ARITH? */ -#if 1 /* TAG: bash-5.2 */ - *e1p = evalexp (temp1, EXP_EXPANDED, &expok); + temp1 = expand_arith_string (substr, Q_DOUBLE_QUOTES|Q_ARITH); +#if 0 /* TAG: bash-5.2 */ + eflag = (shell_compatibility_level > 51) ? 0 : EXP_EXPANDED; #else - *e1p = evalexp (temp1, 0, &expok); /* XXX - EXP_EXPANDED? */ + eflag = 0; #endif + + *e1p = evalexp (temp1, eflag, &expok); free (temp1); if (expok == 0) return (0); @@ -7599,14 +7602,10 @@ verify_substring_values (v, value, substr, vtype, e1p, e2p) { t++; temp2 = savestring (t); - temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES); /* Q_ARITH? */ + temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES|Q_ARITH); free (temp2); t[-1] = ':'; -#if 1 /* TAG: bash-5.2 */ - *e2p = evalexp (temp1, EXP_EXPANDED, &expok); -#else - *e2p = evalexp (temp1, 0, &expok); /* XXX - EXP_EXPANDED? */ -#endif + *e2p = evalexp (temp1, eflag, &expok); free (temp1); if (expok == 0) return (0); @@ -9574,7 +9573,7 @@ param_expand (string, sindex, quoted, expanded_something, int *quoted_dollar_at_p, *had_quoted_null_p, pflags; { char *temp, *temp1, uerror[3], *savecmd; - int zindex, t_index, expok; + int zindex, t_index, expok, eflag; unsigned char c; intmax_t number; SHELL_VAR *var; @@ -9952,7 +9951,12 @@ arithsub: /* No error messages. */ savecmd = this_command_name; this_command_name = (char *)NULL; - number = evalexp (temp1, EXP_EXPANDED, &expok); +#if 0 /* TAG: bash-5.2 */ + eflag = (shell_compatibility_level > 51) ? 0 : EXP_EXPANDED; +#else + eflag = 0; +#endif + number = evalexp (temp1, eflag, &expok); this_command_name = savecmd; free (temp); free (temp1); @@ -10435,6 +10439,26 @@ add_string: } #endif /* PROCESS_SUBSTITUTION */ +#if defined (ARRAY_VARS) + case '[': /*]*/ +#if 0 /* TAG:bash-5.2 */ + if (((quoted & Q_ARITH) == 0 && (word->flags & W_ARRAYREF) == 0) || (shell_compatibility_level <= 51)) +#else + if (((quoted & Q_ARITH) == 0 && (word->flags & W_ARRAYREF) == 0)) +#endif + { + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0) + goto add_ifs_character; + else + goto add_character; + } + else + { + temp = expand_array_subscript (string, &sindex, quoted, word->flags); + goto add_string; + } +#endif + case '=': /* Posix.2 section 3.6.1 says that tildes following `=' in words which are not assignment statements are not expanded. If the @@ -10708,6 +10732,7 @@ add_twochars: break; case '"': + /* XXX - revisit this */ if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) && ((quoted & Q_ARITH) == 0)) goto add_character; @@ -10741,7 +10766,8 @@ add_twochars: temp_has_dollar_at = 0; /* does this quoted (sub)string include $@? */ /* Need to get W_HASQUOTEDNULL flag through this function. */ - list = expand_word_internal (tword, Q_DOUBLE_QUOTES, 0, &temp_has_dollar_at, (int *)NULL); + /* XXX - preserve Q_ARITH here? */ + list = expand_word_internal (tword, Q_DOUBLE_QUOTES|(quoted&Q_ARITH), 0, &temp_has_dollar_at, (int *)NULL); has_dollar_at += temp_has_dollar_at; if (list == &expand_word_error || list == &expand_word_fatal) @@ -11074,6 +11100,8 @@ finished_with_string: tword->flags |= W_NOGLOB; /* XXX */ if (word->flags & W_NOBRACE) tword->flags |= W_NOBRACE; /* XXX */ + if (word->flags & W_ARRAYREF) + tword->flags |= W_ARRAYREF; if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) tword->flags |= W_QUOTED; list = make_word_list (tword, (WORD_LIST *)NULL); @@ -11182,6 +11210,8 @@ set_word_flags: tword->flags |= W_NOGLOB; if (word->flags & W_NOBRACE) tword->flags |= W_NOBRACE; + if (word->flags & W_ARRAYREF) + tword->flags |= W_ARRAYREF; list = make_word_list (tword, (WORD_LIST *)NULL); } } diff --git a/subst.h b/subst.h index f8d715d4..bb461bc1 100644 --- a/subst.h +++ b/subst.h @@ -184,6 +184,7 @@ extern WORD_LIST *expand_string PARAMS((char *, int)); extern char *expand_string_to_string PARAMS((char *, int)); extern char *expand_string_unsplit_to_string PARAMS((char *, int)); extern char *expand_assignment_string_to_string PARAMS((char *, int)); +extern char *expand_subscript_string PARAMS((char *, int)); /* Expand an arithmetic expression string */ extern char *expand_arith_string PARAMS((char *, int)); diff --git a/test.c b/test.c index e2fd78b3..ac80e6b6 100644 --- a/test.c +++ b/test.c @@ -339,12 +339,19 @@ arithcomp (s, t, op, flags) intmax_t l, r; int expok; - if (flags & TEST_ARITHEXP) + if (flags & TEST_ARITHEXP) /* conditional command */ { - l = evalexp (s, EXP_EXPANDED, &expok); + int eflag; + +#if 0 /* TAG:bash-5.2 */ + eflag = (shell_compatibility_level > 51) ? 0 : EXP_EXPANDED; +#else + eflag = 0; +#endif + l = evalexp (s, eflag, &expok); if (expok == 0) return (FALSE); /* should probably longjmp here */ - r = evalexp (t, EXP_EXPANDED, &expok); + r = evalexp (t, eflag, &expok); if (expok == 0) return (FALSE); /* ditto */ } @@ -492,13 +499,13 @@ unary_operator () if (legal_number (argv[pos], &r)) { advance (0); - return (unary_test (op, argv[pos - 1])); + return (unary_test (op, argv[pos - 1], 0)); } else return (FALSE); } else - return (unary_test (op, "1")); + return (unary_test (op, "1", 0)); } /* All of the unary operators take an argument, so we first call @@ -506,12 +513,13 @@ unary_operator () argument, and then advances pos right past it. This means that pos - 1 is the location of the argument. */ unary_advance (); - return (unary_test (op, argv[pos - 1])); + return (unary_test (op, argv[pos - 1], 0)); } int -unary_test (op, arg) +unary_test (op, arg, flags) char *op, *arg; + int flags; { intmax_t r; struct stat stat_buf; @@ -630,6 +638,8 @@ unary_test (op, arg) int rtype, ret, flags; /* Let's assume that this has already been expanded once. */ + /* XXX - TAG:bash-5.2 fix with corresponding fix to execute_cmd.c: + execute_cond_node() that passes TEST_ARRAYEXP in FLAGS */ flags = assoc_expand_once ? AV_NOEXPAND : 0; t = array_value (arg, 0, flags, &rtype, (arrayind_t *)0); ret = t ? TRUE : FALSE; diff --git a/test.h b/test.h index ea3c33e9..f6625e66 100644 --- a/test.h +++ b/test.h @@ -27,11 +27,12 @@ #define TEST_PATMATCH 0x01 #define TEST_ARITHEXP 0x02 #define TEST_LOCALE 0x04 +#define TEST_ARRAYEXP 0x08 /* array subscript expansion */ extern int test_unop PARAMS((char *)); extern int test_binop PARAMS((char *)); -extern int unary_test PARAMS((char *, char *)); +extern int unary_test PARAMS((char *, char *, int)); extern int binary_test PARAMS((char *, char *, char *, int)); extern int test_command PARAMS((int, char **)); diff --git a/tests/array.right b/tests/array.right index 3a13f5ab..ace7b0b6 100644 --- a/tests/array.right +++ b/tests/array.right @@ -520,13 +520,11 @@ argv[1] = ./array23.sub: line 22: $( echo >&2 foo ) : syntax error: operand expected (error token is "$( echo >&2 foo ) ") ./array23.sub: line 23: $( echo >&2 foo ) : syntax error: operand expected (error token is "$( echo >&2 foo ) ") -foo -0 -foo -foo -foo -6 -./array23.sub: line 34: $( echo >&2 foo ): syntax error: operand expected (error token is "$( echo >&2 foo )") +./array23.sub: line 24: $( echo >&2 foo ) : syntax error: operand expected (error token is "$( echo >&2 foo ) ") +./array23.sub: line 26: $( echo >&2 foo ) : syntax error: operand expected (error token is "$( echo >&2 foo ) ") +./array23.sub: line 30: $( echo >&2 foo ): syntax error: operand expected (error token is "$( echo >&2 foo )") +./array23.sub: line 33: $( echo >&2 foo ): syntax error: operand expected (error token is "$( echo >&2 foo )") +./array23.sub: line 34: $index: syntax error: operand expected (error token is "$index") ./array23.sub: line 35: $( echo >&2 foo ): syntax error: operand expected (error token is "$( echo >&2 foo )") 0 0 @@ -743,10 +741,9 @@ argv[1] = argv[2] = argv[1] = 7 -./array27.sub: line 24: a[]]=7 : syntax error: invalid arithmetic operator (error token is "]=7 ") +7 declare -A A=([$'\t']="2" [" "]="2" ) -./array27.sub: line 36: ((: A[]]=2 : syntax error: invalid arithmetic operator (error token is "]=2 ") -declare -A A=([$'\t']="2" ["*"]="2" [" "]="2" ["@"]="2" ) +declare -A A=([$'\t']="2" ["*"]="2" [" "]="2" ["]"]="2" ["@"]="2" ) declare -A A=([$'\t']="2" ["*"]="2" [" "]="2" ["]"]="2" ["@"]="2" ) ./array27.sub: line 52: A[]]: bad array subscript declare -A A=([$'\t']="X" ["*"]="X" [" "]="X" ["@"]="X" ) diff --git a/tests/assoc.right b/tests/assoc.right index 2c3f0933..58d84550 100644 --- a/tests/assoc.right +++ b/tests/assoc.right @@ -283,3 +283,12 @@ declare -A v3=(["1 2"]="3 4 5" ["\$xtra"]="xtra" ) declare -A v1=(["20 40 80"]="new xtra" ["1 2"]="3 4 5" ) declare -A v2=(["20 40 80"]="new xtra" ["1 2"]="3 4 5" ) declare -A v3=(["1 2"]="3 4 5" ["\$xtra"]="new xtra" ) +declare -A assoc=(["*"]="star" ["!"]="bang" ["@"]="at" ) +at +star +declare -A a=(["@"]="at" ) +./assoc13.sub: line 22: ia[@]: bad array subscript +declare -a ia +declare -A a=(["@"]="at2" ) +declare -A a=(["@"]=" string" ) +declare -A a=(["*"]="star2" ["@"]="at" ) diff --git a/tests/assoc.tests b/tests/assoc.tests index be7d5f6d..a99f5209 100644 --- a/tests/assoc.tests +++ b/tests/assoc.tests @@ -247,3 +247,6 @@ ${THIS_SH} ./assoc11.sub # more kvpair associative array assignment tests ${THIS_SH} ./assoc12.sub + +# assignment to @ and * +${THIS_SH} ./assoc13.sub diff --git a/tests/assoc13.sub b/tests/assoc13.sub new file mode 100644 index 00000000..7e669727 --- /dev/null +++ b/tests/assoc13.sub @@ -0,0 +1,44 @@ +# assignment to @ and * + +declare -A assoc +key=@ +key2=* + +assoc[$key]=at +assoc[$key2]=star +assoc[!]=bang +declare -p assoc + +echo ${assoc[$key]} +echo ${assoc[$key2]} +unset assoc + +declare -A a + +a[@]=at +declare -p a + +declare -a ia +ia[@]=garbage + +declare -p ia + +declare a[@]=at2 +declare -p a + +unset a ia + +declare -A a +printf -v a[@] "%10s" string + +declare -p a +unset a + +declare -A a +declare a[$key2]=star +declare a[@]=at +declare a[*]=star2 + +declare -p a +unset a + diff --git a/tests/quotearray.right b/tests/quotearray.right new file mode 100644 index 00000000..5e127374 --- /dev/null +++ b/tests/quotearray.right @@ -0,0 +1,85 @@ +declare -A assoc=(["x],b[\$(echo uname >&2)"]="1" ) +declare -A assoc=(["\$key"]="1" ["x],b[\$(echo uname >&2)"]="1" ) +declare -A assoc=(["\$key"]="1" ["x],b[\$(echo uname >&2)"]="2" ) +./quotearray.tests: line 31: ((: 'assoc[x\],b\[\$(echo uname >&2)]++' : syntax error: operand expected (error token is "'assoc[x\],b\[\$(echo uname >&2)]++' ") +declare -A assoc=(["\$key"]="1" ["x],b[\$(echo uname >&2)"]="2" ) +./quotearray.tests: line 34: ((: 'assoc[x\],b\[\$(echo uname >&2)]'++ : syntax error: operand expected (error token is "'assoc[x\],b\[\$(echo uname >&2)]'++ ") +declare -A assoc=(["\$key"]="1" ["x],b[\$(echo uname >&2)"]="2" ) +declare -A assoc=(["\$key"]="1" ["x],b[\$(echo uname >&2)"]="3" ) +4 +klmnopqrst +klmnopqrst +klmno +klmnopqrst +declare -A A=(["\$(echo %)"]="5" [%]="10" ["]"]="10" ) +declare -A A=(["~"]="42" ) +42 +declare -A A=(["~"]="43" ) +42 +declare -A A=(["~"]="43" ["~0"]="43" ) +12 +declare -a a=([0]="12" [1]="42") +2 +2 +declare -Ai assoc=(["']"]="2" ["\$var"]="1" ) +105 +declare -A assoc=(["\` echo >&2 foo\`"]="42" ["\$( echo >&2 bar)"]="63" ) +./quotearray.tests: line 139: x],b[$(echo uname >&2): syntax error: invalid arithmetic operator (error token is "],b[$(echo uname >&2)") +./quotearray.tests: line 143: x],b[$(echo uname >&2): syntax error: invalid arithmetic operator (error token is "],b[$(echo uname >&2)") +1 +./quotearray.tests: line 146: x],b[$(echo uname >&2): syntax error: invalid arithmetic operator (error token is "],b[$(echo uname >&2)") +1 +./quotearray.tests: line 149: x],b[$(echo uname >&2): syntax error: invalid arithmetic operator (error token is "],b[$(echo uname >&2)") +1 +./quotearray.tests: line 152: x],b[$(echo uname >&2): syntax error: invalid arithmetic operator (error token is "],b[$(echo uname >&2)") +1 +declare -A assoc +0 +0 +1 +0 +0 +0 +declare -A assoc=(["\` echo >&2 foo\`"]="128" [0]="0" ["]"]="12" ["x],b[\$(echo uname >&2)"]="42" ["~"]="42" ["\$( echo 2>& date)"]="foo" ) +foo +0 +0 +./quotearray1.sub: line 67: 0\],b\[1: syntax error: invalid arithmetic operator (error token is "\],b\[1") +declare -a array +0 +0 +0 +0 +1 +1 +declare -A aa=(["\$( echo 2>& date)"]="foo" ) +foo +0 +1 +declare -A a=([1]="1" [0]="0" [" "]="11" ) +7 +7 +declare -A A=([$'\t']="2" [" "]="2" ) +declare -A A=([$'\t']="2" ["*"]="2" [" "]="2" ["]"]="2" ["@"]="2" ) +./quotearray2.sub: line 54: read: `A[]]': not a valid identifier +declare -A A=([$'\t']="X" ["*"]="X" [" "]="X" ["@"]="X" ) +./quotearray2.sub: line 62: printf: `A[]]': not a valid identifier +declare -A A=([$'\t']="X" ["*"]="X" [" "]="X" ["@"]="X" ) +./quotearray2.sub: line 70: declare: `A[]]=X': not a valid identifier +declare -A A=(["*"]="X" ["@"]="X" ) +./quotearray2.sub: line 78: declare: `A[]]=X': not a valid identifier +declare -A A=(["*"]="X" ["@"]="X" ) +./quotearray2.sub: line 89: let: assoc[x],b[$(echo: bad array subscript (error token is "b[$(echo") +declare -A assoc +declare -A assoc=(["\$var"]="value" ) +declare -A assoc=(["\$var"]="value" ) +declare -A assoc=(["\$var"]="value" ) +declare -A assoc=() +declare -A a=() +declare -A a=(["\$(echo foo)"]="1" ) +declare -A a=() +declare -A a=(["\$(echo foo)"]="1" ) +./quotearray3.sub: line 66: typeset: assoc: not found +1 +./quotearray3.sub: line 71: \@: syntax error: operand expected (error token is "\@") +1 diff --git a/tests/quotearray.tests b/tests/quotearray.tests new file mode 100644 index 00000000..9d49353b --- /dev/null +++ b/tests/quotearray.tests @@ -0,0 +1,157 @@ +# 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 . +# + +# a set of tests for associative arrays in arithmetic contexts + +declare -A assoc +key='x],b[$(echo uname >&2)' + +(( assoc[$key]++ )) +declare -p assoc + +(( assoc['$key']++ )) +declare -p assoc + +(( assoc["$key"]++ )) +declare -p assoc + +declare -A assoc + +(( 'assoc[$key]++' )) +declare -p assoc + +(( 'assoc[$key]'++ )) +declare -p assoc + +(( "assoc[$key]++" )) +declare -p assoc + +unset assoc + +typeset -A a +b="80's" + +((++a[$b])) + +((++a["$b"])) +[[ $((++a[$b])) ]] +[[ $((++a["$b"])) ]] + +echo ${a[$b]} +unset a + +declare -A A + +string=abcdefghijklmnopqrstuvwxyz + +echo ${string:10:10} +k1='%' +k2='$(echo %)' + +A[%]=10 +A[']']=10 +A[$k2]=5 + +k3=']' + +echo ${string:A[%]:A[$k1]} +echo ${string:A[%]:A[$k2]} +echo ${string:A[%]:A[$k3]} + +declare -p A + +unset A string key + +key='~' + +declare -A A +A[$key]=42 + +declare -p A + +echo $(( A[$key]++ )) +declare -p A + +key='~0' +A[$key]=42 +echo $(( A[$key]++ )) +declare -p A + +declare -a a +key='~-2' +a[0]=12 +a[$key]=42 +echo $(( a[7<(4+2)] )) + +declare -p a + +unset A a key + +declare -A A +declare -a a + +sString="devel packager's guide" +i=2 + +A["$sString"]=$i +a[$i]=$sString + +echo "${A[${a[i]}]}" +echo ${A["${a[i]}"]} + +unset A a + +#LANG=C +unset var assoc +var=\'\] +declare -Ai assoc +assoc[$var]=1 +assoc[$var]+=1 +((assoc['$var']++)) +typeset -p assoc + +unset assoc + +declare -A assoc +key1='` echo >&2 foo`' +key2='$( echo >&2 bar)' + +assoc[$key1]=42 +assoc[$key2]=63 + +echo $(( assoc[$key1] + assoc[$key2] )) +declare -p assoc +unset assoc + +declare -a a +key='x],b[$(echo uname >&2)' +a[$key]=42 + +expr='a[$key]' + +(( $expr )) +echo $? + +echo $(( $expr )) +echo $? + +echo $(( expr )) +echo $? + +(( expr )) +echo $? + +${THIS_SH} ./quotearray1.sub +${THIS_SH} ./quotearray2.sub +${THIS_SH} ./quotearray3.sub diff --git a/tests/quotearray1.sub b/tests/quotearray1.sub new file mode 100644 index 00000000..67607eb8 --- /dev/null +++ b/tests/quotearray1.sub @@ -0,0 +1,105 @@ +# 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 . +# + +# arithmetic operators for conditional commands and arithmetic commands + +declare -A assoc +declare -a index + +key='x],b[$(echo uname >&2)' +key1=']' +key2='` echo >&2 foo`' +key3='~' +key4='7<(4+2)' +key5='$( echo 2>& date)' + +[[ -n assoc[$key] ]] +declare -p assoc + +assoc[$key]=42 +assoc[$key1]=12 +assoc[$key2]=128 +assoc[$key3]=42 +assoc[0]=0 + +[[ assoc[$key] -eq assoc[$key] ]] +echo $? + +[[ assoc[$key] -gt assoc[$key1] ]] +echo $? + +[[ assoc[$key2] -lt assoc[$key] ]] +echo $? + +[[ assoc[$key] -eq assoc[$key3] ]] +echo $? + +[[ index[7<(4+2)] -le assoc[0] ]] +echo $? +[[ index[$key4] -le assoc[0] ]] +echo $? + +assoc[$key5]=foo +declare -p assoc + +echo "${assoc[$key5]}" + +[[ -v assoc[$key5] ]] +echo $? +[[ -v assoc[$key] ]] +echo $? + +unset assoc + +declare -a array +index='0],b[1'; +((array[$index]++)) + +declare -p array + +unset array + +declare -A assoc + +assoc[$key]=42 +assoc[$key4]=42 + +[[ -v assoc[$key] ]] +echo $? +[[ -v assoc["$key"] ]] +echo $? + +[[ -v assoc[$key4] ]] +echo $? +[[ -v assoc["$key4"] ]] +echo $? + +[[ -v assoc['$key'] ]] +echo $? +[[ -v assoc['$key4'] ]] +echo $? + +declare -A aa +aa[$key5]=foo + +declare -p aa +echo "${aa[$key5]}" + +[[ -v aa[$key5] ]] +echo $? + +[[ -v aa[$key] ]] +echo $? + +unset aa key diff --git a/tests/quotearray2.sub b/tests/quotearray2.sub new file mode 100644 index 00000000..b3dd5e13 --- /dev/null +++ b/tests/quotearray2.sub @@ -0,0 +1,92 @@ +# 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 . +# + +# assoc_expand_once for builtins + +typeset -A a +a[0]=0 a[1]=1 + +let "a[\" \"]=11" ; typeset -p a ; a[0]=0 + +unset a + +# tests for `problem' keys when using associative arrays and assoc_expand_once +# deal with problems for now; this is a placeholder for if and when I fix them + +typeset -A a + +k='[' +echo $(( a[$k]=7 )) + +k=']' +echo $(( a[$k]=7 )) + +unset a + +declare -A A + +for k in $'\t' ' '; do + (( A[$k]=2 )) +done +declare -p A + +for k in ']' '*' '@'; do + (( A[$k]=2 )) +done + +declare -p A + +unset A +declare -A A + +for k in $'\t' ' ' ']' '*' '@'; do + read "A[$k]" <<< X +done +declare -p A + +unset A +declare -A A + +for k in $'\t' ' ' ']' '*' '@'; do + printf -v "A[$k]" "%s" X +done +declare -p A + +unset A +declare -A A + +for k in ']' '*' '@'; do + declare A[$k]=X +done +declare -p A + +unset A +declare -A A + +for k in ']' '*' '@'; do + declare "A[$k]=X" +done +declare -p A + +unset A + +# this isn't right yet, but changes will show up here +shopt -s assoc_expand_once +declare -A assoc +key='x],b[$(echo uname >&2)' + +let assoc[$key]++ +declare -p assoc + +unset assoc diff --git a/tests/quotearray3.sub b/tests/quotearray3.sub new file mode 100644 index 00000000..4c76910d --- /dev/null +++ b/tests/quotearray3.sub @@ -0,0 +1,74 @@ +# 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 . +# + +# assoc_expand_once and unset builtin, which is treated specially + +declare -A assoc + +var=x123 +assoc['$var']=value +declare -p assoc + +shopt -u assoc_expand_once +unset "assoc[$var]" +declare -p assoc + +unset 'assoc[$var]' +declare -p assoc + +assoc['$var']=value +shopt -s assoc_expand_once +unset 'assoc[$var]' +declare -p assoc + +unset assoc + +declare -A a +a['$(echo foo)']=1 + +unset 'a[$(echo foo)]' +declare -p a + +key='$(echo foo)' +a[$key]=1 + +unset 'a[$key]' +declare -p a + +a[$key]=1 +unset "a[$key]" +declare -p a + +a[$key]=1 +unset a[$key] +declare -p a + +unset a + +typeset -A assoc +key=@ + +assoc[@]=at +assoc[!]=bang + +unset -v assoc[$key] +typeset -p assoc + +test -v assoc[$key] +echo $? + +[[ -v assoc[$key] ]] +echo $? + +unset assoc diff --git a/tests/run-quotearray b/tests/run-quotearray new file mode 100644 index 00000000..06e5e6eb --- /dev/null +++ b/tests/run-quotearray @@ -0,0 +1,2 @@ +${THIS_SH} ./quotearray.tests >${BASH_TSTOUT} 2>&1 +diff ${BASH_TSTOUT} quotearray.right && rm -f ${BASH_TSTOUT}