Configuration Information [Automatically generated, do not change]: Machine: x86_64 OS: linux-gnu Compiler: gcc Compilation CFLAGS: -march=native -O3 uname output: Linux chatoyancy 6.5.12-100.fc37.x86_64 #1 SMP PREEMPT_DYNAMIC Mon Nov 20 22:28:44 UTC 2023 x86_64 GNU/Linux Machine Type: x86_64-pc-linux-gnu
Bash Version: 5.3 Patch Level: 0 Release Status: alpha Description: The filtering by `shopt -u force_fignore' is also applied to the suppression of completons unrelated to FIGNORE, which results in strange behaviors in command- and directory-name completions with `shopt -u force_fignore'. This is because `_ignore_completion_names ()' (bashline.c) keeps the original completion list with `shopt -u force_fignore' regardless of whether the current filtering is that by FIGNORE. The `force_fignore == 0' should take effect only for the filtering by FIGNORE. Bash 3.0..devel are affected. I haven't confirmed it with the actual binary, but I think Bash 2.02..2.05b are also affected when the compiler option `-DNO_FORCE_FIGNORE' is specified. The problem was identified after the report originally submitted by Maƫlan <https://github.com/Maelan> to bash-completion: https://github.com/scop/bash-completion/issues/1229 . Repeat-By (case 1 - bash_progcomp_ignore_filenames): $ bash --norc $ rm -rf tmpdir && mkdir tmpdir && cd tmpdir $ touch hello.txt $ shopt -s force_fignore $ compgen -d <-- Nothing is generated as expected $ shopt -u force_fignore $ compgen -d hello.txt <-- This is unexpected. In this case, `_ignore_completion_names ()' is called by `bash_progcomp_ignore_filenames ()' (bashline.c) to remove all non-directory filenames. Repeat-By (case 2 - bash_ignore_filenames): $ bash --norc $ rm -rf tmpdir && mkdir tmpdir && cd tmpdir $ touch nonexistent-command-name.txt $ shopt -s force_fignore $ nonexistent-command-name[TAB] <-- Nothing happens as expected $ shopt -u force_fignore $ nonexistent-command-name[TAB] <-- This completes the line as follows: $ nonexistent-command-name.txt <-- This is unexpecdted. In this case, `_ignore_completion_names ()' is called by `bash_ignore_filenames ()' (bashline.c) to suppress non-directory filenames in the current directory for the command-name completions. Repeat-By (case 3 - bash_ignore_everything): $ bash --norc $ rm -rf tmpdir && mkdir tmpdir && cd tmpdir $ touch hello.txt $ shopt -s no_empty_cmd_completion $ shopt -s force_fignore $ [TAB] <-- Nothing happens as expected $ shopt -u force_fignore $ [TAB] <-- This completes the line as follows: $ hello.txt <-- This is unexpected. In this case, `_ignore_completion_names ()' is called by `bash_ignore_everything ()' (bashline.c) to implement `shopt -s no_empty_command_completion'. Fix: In this patch, `_ignore_completion_names ()' is changed to restore the original completion list with `shopt -u force_fignore' only when the completion list becomes empty by the FIGNORE filtering. This patch was created and tested with the devel branch. The function `_ignore_completion_names ()' (bashline.c) is called by the four functions in bashline.c: * filename_completion_ignore () * bash_progcomp_ignore_filenames () * bash_ignore_filenames () * bash_ignore_everything () The first one is the function that performs the filtering by FIGNORE, and its behavior should be kept. The second to fourth functions are the ones related to case 1..3 in Repeat-By section of this report, respectively, so I think their behavior should not be affecetd by `shopt -s force_fignore'. The function `_ignore_completion_names ()' can detect whether the current filtering is that by FIGNORE by checking its second argument, `name_func'. Change history: The current strange behaviors seem to be present from Bash 3.0 where `force_fignore' is first introduced, and are still present in the devel branch. The filtering by force_fignore was introduced by commit d3a24ed2 (2011-11-29, "Initial devel branch import from bash-3.0-alpha"). This commit includes everything from the 3.0-alpha release, so a finer change history is unavailable. Before that version, `force_fignore' was implemented as a C-macro `#define NO_FORCE_FIGNORE', which was undefined (and hence effectively the same as `shopt -s force_fignore') by default. The C-macro was introduce in 2.02 (commit cce855bc, 1998-04-18, "Imported from ../bash-2.02.tar.gz."). Before that, the behavior was always like `shopt -s force_fignore'. --- bashline.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bashline.c b/bashline.c index 9cdd9bc4..174f5311 100644 --- a/bashline.c +++ b/bashline.c @@ -3072,6 +3072,8 @@ _ignore_completion_names (char **names, sh_ignore_func_t *name_func) size_t idx, nidx; char **oldnames; int oidx; + int allow_empty = 1; + if (name_func == name_is_acceptable) allow_empty = force_fignore; /* If there is only one completion, see if it is acceptable. If it is not, free it up. In any case, short-circuit and return. This is a @@ -3079,7 +3081,7 @@ _ignore_completion_names (char **names, sh_ignore_func_t *name_func) if there is only one completion; it is the completion itself. */ if (names[1] == (char *)0) { - if (force_fignore) + if (allow_empty) if ((*name_func) (names[0]) == 0) { free (names[0]); @@ -3095,7 +3097,7 @@ _ignore_completion_names (char **names, sh_ignore_func_t *name_func) ; newnames = strvec_create (nidx + 1); - if (force_fignore == 0) + if (allow_empty == 0) { oldnames = strvec_create (nidx - 1); oidx = 0; @@ -3106,7 +3108,7 @@ _ignore_completion_names (char **names, sh_ignore_func_t *name_func) { if ((*name_func) (names[idx])) newnames[nidx++] = names[idx]; - else if (force_fignore == 0) + else if (allow_empty == 0) oldnames[oidx++] = names[idx]; else free (names[idx]); @@ -3117,7 +3119,7 @@ _ignore_completion_names (char **names, sh_ignore_func_t *name_func) /* If none are acceptable then let the completer handle it. */ if (nidx == 1) { - if (force_fignore) + if (allow_empty) { free (names[0]); names[0] = (char *)NULL; @@ -3129,7 +3131,7 @@ _ignore_completion_names (char **names, sh_ignore_func_t *name_func) return; } - if (force_fignore == 0) + if (allow_empty == 0) { while (oidx) free (oldnames[--oidx]); -- 2.45.0