From: Chet Ramey Date: Tue, 4 Jan 2022 16:05:30 +0000 (-0500) Subject: minor change to here-doc processing in compoind lists; change to completion to avoid... X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=be7fc0525eb4932c931a45026a20aa483d7bc671;p=thirdparty%2Fbash.git minor change to here-doc processing in compoind lists; change to completion to avoid quoting word expansion characters like `$' --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 81d96ae71..204e66712 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -2863,3 +2863,42 @@ variables.c - bind_int_variable: translate the assignment flags (ASS_xxx) to VA_xxx flags for valid_array_reference calls (ASS_ONEWORD); translate assignment flags to AV_xxx flags for array_variable_part + + 12/30 + ----- +subst.c + - parameter_brace_expand: when expanding an indirect variable, extend + the special case for array[@] and array[*] (set -u/no positional + parameters, obeying the baroque quoting rules) to the value of the + indirection. Report amd fix from konsolebox + + 12/31 + ----- +parse.y + - compound_list: when parsing a compound_list production, collect any + pending here-documents after reading a newline, not after reading + any command terminator. Fixes interactive-only prompting bug + reported back in 8/2021 by Hyunho Cho + + 1/1/2022 + -------- +bashline.c + - set_filename_quote_chars: break code that modifies + rl_filename_quote_characters based on whether DIRNAME needs to be + expanded from bash_directory_completion_hook into its own function + - bash_check_expchar: break code that checks whether DIRNAME will be + word expanded from bash_directory_completion_hook into its own + function + - bashline_reset,attempt_shell_completion: make sure complete_fullquote + is set to 1 (as it is by default) in case a completion modifies it + - bash_quote_filename: if we are completing (but not expanding -- + direxpand is unset) and backslash-quoting a filename with expansion + characters as determined by bash_check_expchar, make sure + filename_bstab is set not to include the expansion char (and any + following char and closer) and set complete_fullquote to 0 so + sh_backslash_quote uses filename_bstab. Fixes the longstanding issue + of quoting a `$', for instance, if the rest of the filename contains + any characters that need quoting in filenames. This assumes that the + filename is unquoted (*QCP == 0) so the word will be expanded and is + not part of the filename (if needed, we can use file_exists to check + whether the expansion characters are actually part of the filename) diff --git a/bashline.c b/bashline.c index 63891d6a6..1edff1e1f 100644 --- a/bashline.c +++ b/bashline.c @@ -1,6 +1,6 @@ /* bashline.c -- Bash's interface to the readline library. */ -/* Copyright (C) 1987-2021 Free Software Foundation, Inc. +/* Copyright (C) 1987-2022 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -192,6 +192,8 @@ static int return_zero PARAMS((const char *)); static char *bash_dequote_filename PARAMS((char *, int)); static char *quote_word_break_chars PARAMS((char *)); +static int bash_check_expchar PARAMS((char *, int, int *, int *)); +static void set_filename_quote_chars PARAMS((int, int, int)); static void set_filename_bstab PARAMS((const char *)); static char *bash_quote_filename PARAMS((char *, int, char *)); @@ -674,6 +676,8 @@ bashline_reset () rl_attempted_completion_function = attempt_shell_completion; rl_completion_entry_function = NULL; rl_ignore_some_completions_function = filename_completion_ignore; + + complete_fullquote = 1; rl_filename_quote_characters = default_filename_quote_characters; set_filename_bstab (rl_filename_quote_characters); @@ -1575,6 +1579,7 @@ attempt_shell_completion (text, start, end) matches = (char **)NULL; rl_ignore_some_completions_function = filename_completion_ignore; + complete_fullquote = 1; /* full filename quoting by default */ rl_filename_quote_characters = default_filename_quote_characters; set_filename_bstab (rl_filename_quote_characters); set_directory_hook (); @@ -3458,38 +3463,7 @@ bash_directory_completion_hook (dirname) return_value = should_expand_dirname = nextch = closer = 0; local_dirname = *dirname; - if (t = mbschr (local_dirname, '$')) - { - should_expand_dirname = '$'; - nextch = t[1]; - /* Deliberately does not handle the deprecated $[...] arithmetic - expansion syntax */ - if (nextch == '(') - closer = ')'; - else if (nextch == '{') - closer = '}'; - else - nextch = 0; - - if (closer) - { - int p; - char delims[2]; - - delims[0] = closer; delims[1] = 0; - p = skip_to_delim (t, 1, delims, SD_NOJMP|SD_COMPLETE); - if (t[p] != closer) - should_expand_dirname = 0; - } - } - else if (local_dirname[0] == '~') - should_expand_dirname = '~'; - else - { - t = mbschr (local_dirname, '`'); - if (t && unclosed_pair (local_dirname, strlen (local_dirname), "`") == 0) - should_expand_dirname = '`'; - } + should_expand_dirname = bash_check_expchar (local_dirname, 1, &nextch, &closer); if (should_expand_dirname && directory_exists (local_dirname, 1)) should_expand_dirname = 0; @@ -3508,24 +3482,8 @@ bash_directory_completion_hook (dirname) free (new_dirname); dispose_words (wl); local_dirname = *dirname; - /* XXX - change rl_filename_quote_characters here based on - should_expand_dirname/nextch/closer. This is the only place - custom_filename_quote_characters is modified. */ - if (rl_filename_quote_characters && *rl_filename_quote_characters) - { - int i, j, c; - i = strlen (default_filename_quote_characters); - custom_filename_quote_characters = xrealloc (custom_filename_quote_characters, i+1); - for (i = j = 0; c = default_filename_quote_characters[i]; i++) - { - if (c == should_expand_dirname || c == nextch || c == closer) - continue; - custom_filename_quote_characters[j++] = c; - } - custom_filename_quote_characters[j] = '\0'; - rl_filename_quote_characters = custom_filename_quote_characters; - set_filename_bstab (rl_filename_quote_characters); - } + + set_filename_quote_chars (should_expand_dirname, nextch, closer); } else { @@ -4194,6 +4152,95 @@ quote_word_break_chars (text) return ret; } +/* Return a character in DIRNAME that will cause shell expansion to be + performed. If NEXTP is non-null, *NEXTP gets the expansion character that + follows RET (e.g., '{' or `(' for `$'). If CLOSERP is non-null, *CLOSERP + gets the character that should close . If NEED_CLOSER is non- + zero, any expansion pair that isn't closed causes this function to + return 0, which indicates that we didn't find an expansion character. It's + used in case DIRNAME is going to be expanded. If DIRNAME is just going to + be quoted, NEED_CLOSER will be 0. */ +static int +bash_check_expchar (dirname, need_closer, nextp, closerp) + char *dirname; + int need_closer; + int *nextp, *closerp; +{ + char *t; + int ret, n, c; + + ret = n = c = 0; + if (t = mbschr (dirname, '$')) + { + ret = '$'; + n = t[1]; + /* Deliberately does not handle the deprecated $[...] arithmetic + expansion syntax */ + if (n == '(') + c = ')'; + else if (n == '{') + c = '}'; + else + n = 0; + + if (c && need_closer) /* XXX */ + { + int p; + char delims[2]; + + delims[0] = c; delims[1] = 0; + p = skip_to_delim (t, 1, delims, SD_NOJMP|SD_COMPLETE); + if (t[p] != c) + ret = 0; + } + } + else if (dirname[0] == '~') + ret = '~'; + else + { + t = mbschr (dirname, '`'); + if (t) + { + if (need_closer == 0) + ret = '`'; + else if (unclosed_pair (dirname, strlen (dirname), "`") == 0) + ret = '`'; + } + } + + if (nextp) + *nextp = n; + if (closerp) + *closerp = c; + + return ret; +} + +/* Make sure EXPCHAR and, if non-zero, NEXTCH and CLOSER are not in the set + of characters to be backslash-escaped. This is the only place + custom_filename_quote_characters is modified. */ +static void +set_filename_quote_chars (expchar, nextch, closer) + int expchar, nextch, closer; +{ + int i, j, c; + + if (rl_filename_quote_characters && *rl_filename_quote_characters) + { + i = strlen (default_filename_quote_characters); + custom_filename_quote_characters = xrealloc (custom_filename_quote_characters, i+1); + for (i = j = 0; c = default_filename_quote_characters[i]; i++) + { + if (c == expchar || c == nextch || c == closer) + continue; + custom_filename_quote_characters[j++] = c; + } + custom_filename_quote_characters[j] = '\0'; + rl_filename_quote_characters = custom_filename_quote_characters; + set_filename_bstab (rl_filename_quote_characters); + } +} + /* Use characters in STRING to populate the table of characters that should be backslash-quoted. The table will be used for sh_backslash_quote from this file. */ @@ -4222,6 +4269,7 @@ bash_quote_filename (s, rtype, qcp) { char *rtext, *mtext, *ret; int rlen, cs; + int expchar, nextch, closer; rtext = (char *)NULL; @@ -4239,7 +4287,17 @@ bash_quote_filename (s, rtype, qcp) the word being completed contains newlines, since those are not quoted correctly using backslashes (a backslash-newline pair is special to the shell parser). */ - if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && mbschr (s, '\n')) + expchar = nextch = closer = 0; + if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && dircomplete_expand == 0 && + (expchar = bash_check_expchar (s, 0, &nextch, &closer))) + { + /* Usually this will have been set by bash_directory_completion_hook, but + there are rare cases where it will not be. */ + if (rl_filename_quote_characters != custom_filename_quote_characters) + set_filename_quote_chars (expchar, nextch, closer); + complete_fullquote = 0; + } + else if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && mbschr (s, '\n')) cs = COMPLETE_SQUOTE; else if (*qcp == '"') cs = COMPLETE_DQUOTE; diff --git a/parse.y b/parse.y index e3b6e6d86..94195875f 100644 --- a/parse.y +++ b/parse.y @@ -1119,7 +1119,7 @@ pattern: WORD compound_list: newline_list list0 { $$ = $2; - if (need_here_doc) + if (need_here_doc && last_read_token == '\n') gather_here_documents (); } | newline_list list1 diff --git a/subst.c b/subst.c index 72c61dc2b..a9cb4e214 100644 --- a/subst.c +++ b/subst.c @@ -9352,6 +9352,11 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta to return the index we're supposed to be using. */ if (tdesc && tdesc->flags) tdesc->flags &= ~W_ARRAYIND; + + /* If the indir expansion contains $@/$*, extend the special treatment + of the case of no positional parameters and `set -u' to it. */ + if (contains_dollar_at && *contains_dollar_at) + all_element_arrayref = 1; } else { diff --git a/tests/assoc.right b/tests/assoc.right index 3516a6e55..9a1662c45 100644 --- a/tests/assoc.right +++ b/tests/assoc.right @@ -393,3 +393,8 @@ declare -A A=(["]"]="rbracket" ["["]="lbracket" ) declare -A A=() declare -A A=(["]"]="rbracket" ["["]="lbracket" ) declare -A A=() +declare -A A=(["]"]="rbracket" ["["]="lbracket" ) +declare -A A=() +declare -A A=(["]"]="rbracket" ["["]="lbracket" ) +declare -A A=() +5: ok 1 diff --git a/tests/assoc.tests b/tests/assoc.tests index 5fc2b2cff..c656536b5 100644 --- a/tests/assoc.tests +++ b/tests/assoc.tests @@ -262,3 +262,7 @@ ${THIS_SH} ./assoc16.sub # tests with `[' and `]' subscripts and `unset' ${THIS_SH} ./assoc17.sub + +# tests with `[' and `]' subscripts and printf/read/wait builtins +${THIS_SH} ./assoc18.sub + diff --git a/tests/assoc18.sub b/tests/assoc18.sub new file mode 100644 index 000000000..059760932 --- /dev/null +++ b/tests/assoc18.sub @@ -0,0 +1,59 @@ +# 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 . +# + +# test behavior of builtins that can take array subscript arguments, make +# sure they work in the presence of associative arrays and `problematic' keys +# if assoc_expand_once is set +# +# affected builtins: printf, read, wait + +declare -A A +rkey=']' +lkey='[' + +shopt -s assoc_expand_once + +printf -v A[$rkey] rbracket +printf -v A[$lkey] lbracket +declare -p A + +unset A[$rkey] +unset A[$lkey] +declare -p A + +unset A +declare -A A + +read A[$rkey] <<