Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: invalid completion with a custom matcher



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;
                    }



Messages sorted by: Reverse Date, Date, Thread, Author