PATCH: invalid completion with a custom matcher

Michael <[email protected]>
Newsgroups gmane.comp.shells.zsh.devel
Message-ID <CA+DUctJ3bUD+hgtcWkWOy8TzHt=Zgvu8W9B4OefcJn4AwRYtxw@mail.gmail.com>
Problem depends on having a case insensitive matcher enabled for
replicating with zsh -f, as far as I understand it is commonly used. At
least I had on it for a long time.

The diff looks big, but it's mostly adding one extra check and nesting
everything under it.

    Have this in `.zshrc`.
    `zstyle ':completion:*' matcher-list ':{a-zA-Z}={A-Za-z}'`

    Consider this scenario:

    ```
    % touch "Strategy TB" ; touch "Strategy Scenario"
    ```

    Then user types `"St<TAB>"`. In the ideal world this produces common
    prefix: `"Strategy "` then subsequent `<TAB>`s switch between HBT and
    Scenario. But instead it completes `"Strategy t"`, note lower case
`'t'`.
    Subsequent press produces `"Strategy TB"` leaving `"Strategy Scenaio"`
    inaccessible.

    In `compmatch.c:join_strs()`, when two strings differ at a character
    position, zsh tries matchers to find a common representation. The
    bld_line() function builds a string that matches one of the inputs, but
    the result is added to the output without verifying it also matches the
    other input.

    With the matcher `m:{a-zA-Z}={A-Za-z}`, the pattern `{a-zA-Z}`
    matches any letter. So both `S` and `T` individually match it.
    `bld_line()` succeeds for one string (e.g. lowercases `T` -> `t`), and
that
    character is unconditionally appended to the completion, producing
    `"Sample t"` instead of stopping at the correct LCP `"Sample "`.

    The fix adds a verification: after `bld_line()` succeeds, call it again
    with reversed arguments to confirm the built line matches both strings
    before adding it to the output.

diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
index bc82ff4d0..9b39dc243 100644
--- a/Src/Zle/compmatch.c
+++ b/Src/Zle/compmatch.c
@@ -2037,32 +2037,45 @@ join_strs(int la, char *sa, int lb, char *sb)
                            bp = &sa;
                            blp = &la;
                        }
-                       /* Now try to build a string that matches the other
-                        * string. */
+                        /* Now try to build a string that matches the
other string. */
                        if ((bl = bld_line(mp, line, *ap, *bp, *blp, 0))) {
-                           /* Found one, put it into the return string. */
-                           char *convstr =
-                               zlelineasstring(line, mp->llen, 0, &convlen,
-                                               NULL, 0);
-                           if (rr <= convlen) {
-                               ptrdiff_t diff = rp - rs;
-                               int alloclen = (convlen > 20) ? convlen :
20;
-
-                               rs = realloc(rs, (rl += alloclen));
-                               rr += alloclen;
-                               rp = rs + diff;
-                           }
-                           memcpy(rp, convstr, convlen);
-                           rp += convlen;
-                           rr -= convlen;
-                           /* HERE: multibyte chars */
-                           *ap += mp->wlen;
-                           *alp -= mp->wlen;
-
-                           *bp += bl;
-                           *blp -= bl;
-                           t = 1;
-                           free(convstr);
+                           /*
+                            * Verify the built line also matches *ap (the
+                            * matched string), not just *bp.  This prevents
+                            * adding characters that only match one string
+                            * when the matcher pattern is too generic
+                            * (e.g. {a-zA-Z} matching any letter).
+                            */
+                           VARARR(ZLE_CHAR_T, line2, mp->llen);
+                           int bl2;
+
+                           if ((bl2 = bld_line(mp, line2, *bp, *ap, *alp,
0))
+                               && bl2 == bl) {
+                               /* Found one, put it into the return
string. */
+                               char *convstr =
+                                   zlelineasstring(line, mp->llen, 0,
&convlen,
+                                                   NULL, 0);
+                               if (rr <= convlen) {
+                                   ptrdiff_t diff = rp - rs;
+                                   int alloclen = (convlen > 20) ? convlen
: 20;
+
+                                   rs = realloc(rs, (rl += alloclen));
+                                   rr += alloclen;
+                                   rp = rs + diff;
+                               }
+                               memcpy(rp, convstr, convlen);
+                               rp += convlen;
+                               rr -= convlen;
+                               /* HERE: multibyte chars */
+                               *ap += mp->wlen;
+                               *alp -= mp->wlen;
+
+                               *bp += bl;
+                               *blp -= bl;
+                               t = 1;
+                               free(convstr);
+                           } else
+                               t = 0;
                        } else
                            t = 0;
                    }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.