bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] fix `shopt -u force_fignore' affecting unrelated parts


From: Koichi Murase
Subject: [PATCH] fix `shopt -u force_fignore' affecting unrelated parts
Date: Sun, 7 Jul 2024 19:55:41 +0900

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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]