Add a timeout for fuzzy matching, also some general tidying up.

This commit is contained in:
nicm
2026-10-16 12:28:43 +00:00
committed by tmux update bot
parent 0908d3dccb
commit cef0dd19a8

321
fuzzy.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: fuzzy.c,v 1.2 2026/09/28 16:55:08 nicm Exp $ */ /* $OpenBSD: fuzzy.c,v 1.3 2026/10/16 12:28:43 nicm Exp $ */
/* /*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -59,6 +59,23 @@
#define FUZZY_PENALTY_LEADING 1 #define FUZZY_PENALTY_LEADING 1
#define FUZZY_PENALTY_LEADING_MAX 10 #define FUZZY_PENALTY_LEADING_MAX 10
#define FUZZY_PENALTY_GAP 1 #define FUZZY_PENALTY_GAP 1
#define FUZZY_TIME_LIMIT 100
#define FUZZY_TIME_LOOP_CHECK 10000
/* State for matching. */
struct fuzzy_state {
uint64_t end_time;
u_int check;
int stopped;
struct fuzzy_char *cs;
u_int ncs;
u_int alloc;
u_int widths[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
struct utf8_data *tok;
u_int toklen;
int fold;
char *matched;
};
/* A single visible character of the text. */ /* A single visible character of the text. */
struct fuzzy_char { struct fuzzy_char {
@@ -78,9 +95,21 @@ struct fuzzy_term {
size_t len; size_t len;
}; };
/* Check whether a match has taken too long. */
static int
fuzzy_check_time(struct fuzzy_state *fs)
{
if (++fs->check % FUZZY_TIME_LOOP_CHECK != 0)
return (1);
if (get_timer() < fs->end_time)
return (1);
fs->stopped = 1;
return (0);
}
/* Is this character a word boundary, so a match after it scores higher? */ /* Is this character a word boundary, so a match after it scores higher? */
static int static int
fuzzy_is_boundary(const struct utf8_data *ud) fuzzy_is_boundary(struct utf8_data *ud)
{ {
static const char *boundary = " -_/.:"; static const char *boundary = " -_/.:";
@@ -94,7 +123,7 @@ fuzzy_is_boundary(const struct utf8_data *ud)
* directly without case folding. * directly without case folding.
*/ */
static int static int
fuzzy_char_equal(const struct utf8_data *a, const struct utf8_data *b, int fold) fuzzy_char_equal(struct utf8_data *a, struct utf8_data *b, int fold)
{ {
if (fold && if (fold &&
a->size == 1 && a->size == 1 &&
@@ -116,21 +145,20 @@ fuzzy_align(enum style_align align)
/* Add a visible character to the array, updating the alignment width. */ /* Add a visible character to the array, updating the alignment width. */
static void static void
fuzzy_add(struct fuzzy_char **cs, u_int *ncs, u_int *alloc, enum style_align a, fuzzy_add(struct fuzzy_state *fs, enum style_align a, struct utf8_data *ud)
const struct utf8_data *ud, u_int *widths)
{ {
struct fuzzy_char *fc; struct fuzzy_char *fc;
if (*ncs == *alloc) { if (fs->ncs == fs->alloc) {
*alloc = (*alloc == 0) ? 64 : *alloc * 2; fs->alloc = (fs->alloc == 0) ? 64 : fs->alloc * 2;
*cs = xreallocarray(*cs, *alloc, sizeof **cs); fs->cs = xreallocarray(fs->cs, fs->alloc, sizeof *fs->cs);
} }
fc = &(*cs)[(*ncs)++]; fc = &fs->cs[fs->ncs++];
fc->align = a; fc->align = a;
memcpy(&fc->ud, ud, sizeof fc->ud); memcpy(&fc->ud, ud, sizeof fc->ud);
fc->width = ud->width; fc->width = ud->width;
fc->offset = widths[a]; fc->offset = fs->widths[a];
widths[a] += ud->width; fs->widths[a] += ud->width;
} }
/* Decode a character as UTF-8. */ /* Decode a character as UTF-8. */
@@ -153,14 +181,13 @@ fuzzy_decode_one(const char *cp, const char *end, struct utf8_data *ud)
/* /*
* Scan the text into an array of visible characters, skipping styles and * Scan the text into an array of visible characters, skipping styles and
* recording the alignment and intra-alignment offset of each. Returns the * recording the alignment and intra-alignment offset of each. Fills in the
* array and its length and fills in the per-alignment widths. * array, its length and the per-alignment widths in the match state.
*/ */
static struct fuzzy_char * static void
fuzzy_scan(const char *text, u_int *ncs, u_int *widths) fuzzy_scan(struct fuzzy_state *fs, const char *text)
{ {
struct fuzzy_char *cs = NULL; u_int n, leading, i;
u_int alloc = 0, n, leading, i;
enum style_align current = STYLE_ALIGN_LEFT; enum style_align current = STYLE_ALIGN_LEFT;
struct style sy; struct style sy;
const char *cp = text, *textend = text + strlen(text); const char *cp = text, *textend = text + strlen(text);
@@ -168,35 +195,39 @@ fuzzy_scan(const char *text, u_int *ncs, u_int *widths)
struct utf8_data ud, hash, bracket; struct utf8_data ud, hash, bracket;
char *tmp; char *tmp;
*ncs = 0;
memset(widths, 0, sizeof *widths * (STYLE_ALIGN_ABSOLUTE_CENTRE + 1));
style_set(&sy, &grid_default_cell); style_set(&sy, &grid_default_cell);
utf8_set(&hash, '#'); utf8_set(&hash, '#');
utf8_set(&bracket, '['); utf8_set(&bracket, '[');
while (*cp != '\0') { while (*cp != '\0') {
if (!fuzzy_check_time(fs))
break;
/* Handle a run of #s, which may introduce a style. */ /* Handle a run of #s, which may introduce a style. */
if (*cp == '#') { if (*cp == '#') {
for (n = 0; cp[n] == '#'; n++) for (n = 0; cp[n] == '#'; n++) {
/* nothing */; if (!fuzzy_check_time(fs))
return;
}
if (cp[n] != '[') { if (cp[n] != '[') {
/* Escaped #s: ##->#, so half (rounded up). */ /* Escaped #s: ##->#, so half (rounded up). */
leading = (n % 2 == 0) ? n / 2 : n / 2 + 1; leading = (n % 2 == 0) ? n / 2 : n / 2 + 1;
for (i = 0; i < leading; i++) { for (i = 0; i < leading; i++) {
fuzzy_add(&cs, ncs, &alloc, current, if (!fuzzy_check_time(fs))
&hash, widths); return;
fuzzy_add(fs, current, &hash);
} }
cp += n; cp += n;
continue; continue;
} }
/* Even count: all #s escaped, the [ is literal. */ /* Even count: all #s escaped, the [ is literal. */
for (i = 0; i < n / 2; i++) for (i = 0; i < n / 2; i++) {
fuzzy_add(&cs, ncs, &alloc, current, &hash, if (!fuzzy_check_time(fs))
widths); return;
fuzzy_add(fs, current, &hash);
}
if (n % 2 == 0) { if (n % 2 == 0) {
fuzzy_add(&cs, ncs, &alloc, current, &bracket, fuzzy_add(fs, current, &bracket);
widths);
cp += n + 1; cp += n + 1;
continue; continue;
} }
@@ -223,9 +254,8 @@ fuzzy_scan(const char *text, u_int *ncs, u_int *widths)
*/ */
if (ud.size == 1 && (ud.data[0] <= 0x1f || ud.data[0] >= 0x7f)) if (ud.size == 1 && (ud.data[0] <= 0x1f || ud.data[0] >= 0x7f))
continue; continue;
fuzzy_add(&cs, ncs, &alloc, current, &ud, widths); fuzzy_add(fs, current, &ud);
} }
return (cs);
} }
/* /*
@@ -234,8 +264,8 @@ fuzzy_scan(const char *text, u_int *ncs, u_int *widths)
* character is visible, otherwise returns -1. * character is visible, otherwise returns -1.
*/ */
static int static int
fuzzy_column(const struct fuzzy_char *fc, const u_int *start, const u_int *src, fuzzy_column(struct fuzzy_char *fc, u_int *start, u_int *src, u_int *vis,
const u_int *vis, u_int *column) u_int *column)
{ {
enum style_align a = fc->align; enum style_align a = fc->align;
@@ -245,35 +275,34 @@ fuzzy_column(const struct fuzzy_char *fc, const u_int *start, const u_int *src,
return (0); return (0);
} }
/* Decode a UTF-8 term, stopping if it cannot fit in the given limit. */ /* Decode a UTF-8 term, stopping if it has more characters than the text. */
static int static int
fuzzy_decode(const char *tok, size_t len, struct utf8_data *out, u_int limit, fuzzy_decode(struct fuzzy_state *fs, const char *tok, size_t len)
u_int *n)
{ {
const char *cp = tok, *end = tok + len; const char *cp = tok, *end = tok + len;
*n = 0; fs->toklen = 0;
while (cp != end) { while (cp != end) {
if (*n == limit) if (fs->toklen == fs->ncs)
return (-1); return (-1);
cp = fuzzy_decode_one(cp, end, &out[(*n)++]); cp = fuzzy_decode_one(cp, end, &fs->tok[fs->toklen++]);
} }
return (0); return (0);
} }
/* Add the score for a fuzzy token matched at the given positions. */ /* Add the score for a fuzzy token matched at the given positions. */
static int static int
fuzzy_score_positions(const u_int *pos, u_int npos, const struct fuzzy_char *cs) fuzzy_score_positions(struct fuzzy_state *fs, u_int *pos)
{ {
u_int i, gap, span; u_int i, gap, span;
int score = 0; int score = 0;
if (npos == 0) if (fs->toklen == 0)
return (0); return (0);
if (pos[0] == 0) if (pos[0] == 0)
score += FUZZY_BONUS_START; score += FUZZY_BONUS_START;
else { else {
if (fuzzy_is_boundary(&cs[pos[0] - 1].ud)) if (fuzzy_is_boundary(&fs->cs[pos[0] - 1].ud))
score += FUZZY_BONUS_BOUNDARY; score += FUZZY_BONUS_BOUNDARY;
if (pos[0] < FUZZY_PENALTY_LEADING_MAX) if (pos[0] < FUZZY_PENALTY_LEADING_MAX)
score -= pos[0] * FUZZY_PENALTY_LEADING; score -= pos[0] * FUZZY_PENALTY_LEADING;
@@ -282,14 +311,14 @@ fuzzy_score_positions(const u_int *pos, u_int npos, const struct fuzzy_char *cs)
FUZZY_PENALTY_LEADING; FUZZY_PENALTY_LEADING;
} }
} }
for (i = 1; i < npos; i++) { for (i = 1; i < fs->toklen; i++) {
if (pos[i] == pos[i - 1] + 1) if (pos[i] == pos[i - 1] + 1)
score += FUZZY_BONUS_CONSECUTIVE; score += FUZZY_BONUS_CONSECUTIVE;
else if (fuzzy_is_boundary(&cs[pos[i] - 1].ud)) else if (fuzzy_is_boundary(&fs->cs[pos[i] - 1].ud))
score += FUZZY_BONUS_BOUNDARY; score += FUZZY_BONUS_BOUNDARY;
} }
span = pos[npos - 1] - pos[0] + 1; span = pos[fs->toklen - 1] - pos[0] + 1;
gap = span - npos; gap = span - fs->toklen;
score -= gap * FUZZY_PENALTY_GAP; score -= gap * FUZZY_PENALTY_GAP;
return (score); return (score);
} }
@@ -299,23 +328,27 @@ fuzzy_score_positions(const u_int *pos, u_int npos, const struct fuzzy_char *cs)
* token matches. * token matches.
*/ */
static int static int
fuzzy_match_fuzzy(const struct utf8_data *tok, u_int toklen, fuzzy_match_fuzzy(struct fuzzy_state *fs, int *score)
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
{ {
u_int pi, ci, *pos; u_int pi, ci, *pos;
int found, value; int found, value;
if (toklen == 0 || ncs == 0) if (fs->toklen == 0 || fs->ncs == 0)
return (0); return (0);
pos = xcalloc(toklen, sizeof *pos); pos = xcalloc(fs->toklen, sizeof *pos);
/* First find a subsequence from the start. */ /* First find a subsequence from the start. */
ci = 0; ci = 0;
for (pi = 0; pi < toklen; pi++) { for (pi = 0; pi < fs->toklen; pi++) {
while (ci != ncs && while (ci != fs->ncs &&
!fuzzy_char_equal(&tok[pi], &cs[ci].ud, fold)) !fuzzy_char_equal(&fs->tok[pi], &fs->cs[ci].ud, fs->fold)) {
if (!fuzzy_check_time(fs)) {
free(pos);
return (0);
}
ci++; ci++;
if (ci == ncs) { }
if (ci == fs->ncs) {
free(pos); free(pos);
return (0); return (0);
} }
@@ -323,11 +356,16 @@ fuzzy_match_fuzzy(const struct utf8_data *tok, u_int toklen,
} }
/* Then compact it backwards to prefer a shorter span. */ /* Then compact it backwards to prefer a shorter span. */
ci = pos[toklen - 1]; ci = pos[fs->toklen - 1];
for (pi = toklen; pi > 0; pi--) { for (pi = fs->toklen; pi > 0; pi--) {
found = 0; found = 0;
for (;;) { for (;;) {
if (fuzzy_char_equal(&tok[pi - 1], &cs[ci].ud, fold)) { if (!fuzzy_check_time(fs)) {
free(pos);
return (0);
}
if (fuzzy_char_equal(&fs->tok[pi - 1], &fs->cs[ci].ud,
fs->fold)) {
pos[pi - 1] = ci; pos[pi - 1] = ci;
found = 1; found = 1;
break; break;
@@ -344,78 +382,78 @@ fuzzy_match_fuzzy(const struct utf8_data *tok, u_int toklen,
ci--; ci--;
} }
value = fuzzy_score_positions(pos, toklen, cs); value = fuzzy_score_positions(fs, pos);
*score += value; *score += value;
for (pi = 0; pi < toklen; pi++) for (pi = 0; pi < fs->toklen; pi++)
matched[pos[pi]] = 1; fs->matched[pos[pi]] = 1;
free(pos); free(pos);
return (1); return (1);
} }
/* Score an exact, prefix or suffix match. */ /* Score an exact, prefix or suffix match. */
static int static int
fuzzy_score_exact(u_int start, u_int toklen, u_int ncs, fuzzy_score_exact(struct fuzzy_state *fs, struct fuzzy_term *term, u_int start)
const struct fuzzy_char *cs, int prefix, int suffix)
{ {
int score; int score;
score = FUZZY_BONUS_EXACT + toklen * FUZZY_BONUS_CONSECUTIVE; score = FUZZY_BONUS_EXACT + fs->toklen * FUZZY_BONUS_CONSECUTIVE;
if (prefix) if (term->prefix)
score += FUZZY_BONUS_PREFIX; score += FUZZY_BONUS_PREFIX;
if (suffix) if (term->suffix)
score += FUZZY_BONUS_SUFFIX; score += FUZZY_BONUS_SUFFIX;
if (start == 0) if (start == 0)
score += FUZZY_BONUS_START; score += FUZZY_BONUS_START;
else if (fuzzy_is_boundary(&cs[start - 1].ud)) else if (fuzzy_is_boundary(&fs->cs[start - 1].ud))
score += FUZZY_BONUS_BOUNDARY; score += FUZZY_BONUS_BOUNDARY;
if (start < FUZZY_PENALTY_LEADING_MAX) if (start < FUZZY_PENALTY_LEADING_MAX)
score -= start * FUZZY_PENALTY_LEADING; score -= start * FUZZY_PENALTY_LEADING;
else else
score -= FUZZY_PENALTY_LEADING_MAX * FUZZY_PENALTY_LEADING; score -= FUZZY_PENALTY_LEADING_MAX * FUZZY_PENALTY_LEADING;
if (!prefix && !suffix) if (!term->prefix && !term->suffix)
score -= ncs - (start + toklen); score -= fs->ncs - (start + fs->toklen);
return (score); return (score);
} }
/* Match an exact, prefix or suffix term against the visible characters. */ /* Match an exact, prefix or suffix term against the visible characters. */
static int static int
fuzzy_match_exact(const struct utf8_data *tok, u_int toklen, fuzzy_match_exact(struct fuzzy_state *fs, struct fuzzy_term *term, int *score)
struct fuzzy_char *cs, u_int ncs, int fold, int prefix, int suffix,
int *score, char *matched)
{ {
u_int start, end, i, j, best = 0; u_int start, end, i, j, best = 0;
int ok, found = 0, value, bestscore = 0; int ok, found = 0, value, bestscore = 0;
if (toklen == 0 || toklen > ncs) if (fs->toklen == 0 || fs->toklen > fs->ncs)
return (0); return (0);
if (prefix && suffix) { if (term->prefix && term->suffix) {
if (toklen != ncs) if (fs->toklen != fs->ncs)
return (0); return (0);
start = 0; start = 0;
end = 1; end = 1;
} else if (prefix) { } else if (term->prefix) {
start = 0; start = 0;
end = 1; end = 1;
} else if (suffix) { } else if (term->suffix) {
start = ncs - toklen; start = fs->ncs - fs->toklen;
end = start + 1; end = start + 1;
} else { } else {
start = 0; start = 0;
end = ncs - toklen + 1; end = fs->ncs - fs->toklen + 1;
} }
for (i = start; i < end; i++) { for (i = start; i < end; i++) {
ok = 1; ok = 1;
for (j = 0; j < toklen; j++) { for (j = 0; j < fs->toklen; j++) {
if (!fuzzy_char_equal(&tok[j], &cs[i + j].ud, fold)) { if (!fuzzy_check_time(fs))
return (0);
if (!fuzzy_char_equal(&fs->tok[j], &fs->cs[i + j].ud,
fs->fold)) {
ok = 0; ok = 0;
break; break;
} }
} }
if (!ok) if (!ok)
continue; continue;
value = fuzzy_score_exact(i, toklen, ncs, cs, prefix, suffix); value = fuzzy_score_exact(fs, term, i);
if (!found || value > bestscore) { if (!found || value > bestscore) {
found = 1; found = 1;
best = i; best = i;
@@ -425,9 +463,9 @@ fuzzy_match_exact(const struct utf8_data *tok, u_int toklen,
if (!found) if (!found)
return (0); return (0);
*score += bestscore; *score += bestscore;
if (matched != NULL) { if (!term->inverse) {
for (i = 0; i < toklen; i++) for (i = 0; i < fs->toklen; i++)
matched[best + i] = 1; fs->matched[best + i] = 1;
} }
return (1); return (1);
} }
@@ -472,22 +510,18 @@ fuzzy_parse_term(const char *start, const char *end, struct fuzzy_term *term)
/* Match one parsed term. */ /* Match one parsed term. */
static int static int
fuzzy_match_term(const struct fuzzy_term *term, struct utf8_data *tok, fuzzy_match_term(struct fuzzy_state *fs, struct fuzzy_term *term, int *score)
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
{ {
u_int toklen;
int value = 0, matched_term; int value = 0, matched_term;
if (fuzzy_decode(term->text, term->len, tok, ncs, &toklen) != 0) if (fuzzy_decode(fs, term->text, term->len) != 0)
return (term->inverse); return (term->inverse);
if (term->exact) { if (term->exact)
matched_term = fuzzy_match_exact(tok, toklen, cs, ncs, fold, matched_term = fuzzy_match_exact(fs, term, &value);
term->prefix, term->suffix, &value, else
term->inverse ? NULL : matched); matched_term = fuzzy_match_fuzzy(fs, &value);
} else { if (fs->stopped)
matched_term = fuzzy_match_fuzzy(tok, toklen, cs, ncs, fold, return (0);
&value, term->inverse ? NULL : matched);
}
if (term->inverse) if (term->inverse)
return (!matched_term); return (!matched_term);
@@ -499,8 +533,8 @@ fuzzy_match_term(const struct fuzzy_term *term, struct utf8_data *tok,
/* Match one AND group of terms. */ /* Match one AND group of terms. */
static int static int
fuzzy_match_group(const char *start, const char *end, struct utf8_data *tok, fuzzy_match_group(struct fuzzy_state *fs, const char *start, const char *end,
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched) int *score)
{ {
const char *cp = start, *sp; const char *cp = start, *sp;
struct fuzzy_term term; struct fuzzy_term term;
@@ -518,8 +552,7 @@ fuzzy_match_group(const char *start, const char *end, struct utf8_data *tok,
if (!fuzzy_parse_term(sp, cp, &term)) if (!fuzzy_parse_term(sp, cp, &term))
return (0); return (0);
any = 1; any = 1;
if (!fuzzy_match_term(&term, tok, cs, ncs, fold, score, if (!fuzzy_match_term(fs, &term, score))
matched))
return (0); return (0);
} }
return (any); return (any);
@@ -534,19 +567,20 @@ fuzzy_match_group(const char *start, const char *end, struct utf8_data *tok,
bitstr_t * bitstr_t *
fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score) fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
{ {
struct fuzzy_char *cs; struct fuzzy_state fs;
char *matched = NULL, *best = NULL, *groupmatched; char *best = NULL;
struct utf8_data *tok; bitstr_t *mask = NULL;
bitstr_t *mask; u_int i, j, column;
u_int ncs, i, j, column;
u_int widths[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
u_int start[STYLE_ALIGN_ABSOLUTE_CENTRE + 1]; u_int start[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
u_int src[STYLE_ALIGN_ABSOLUTE_CENTRE + 1]; u_int src[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
u_int vis[STYLE_ALIGN_ABSOLUTE_CENTRE + 1]; u_int vis[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
u_int wl, wc, wr, wa; u_int wl, wc, wr, wa;
size_t toksize; size_t toksize, size;
const char *cp, *sp; const char *cp, *sp;
int bestscore = 0, groupscore, found = 0, fold; int bestscore = 0, groupscore, found = 0;
memset(&fs, 0, sizeof fs);
fs.end_time = get_timer() + FUZZY_TIME_LIMIT;
if (width == 0) if (width == 0)
return (NULL); return (NULL);
@@ -561,24 +595,27 @@ fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
} }
/* Smart-case: fold unless the pattern has an uppercase character. */ /* Smart-case: fold unless the pattern has an uppercase character. */
fold = 1; fs.fold = 1;
for (cp = pattern; *cp != '\0'; cp++) { for (cp = pattern; *cp != '\0'; cp++) {
if (*cp >= 'A' && *cp <= 'Z') { if (*cp >= 'A' && *cp <= 'Z') {
fold = 0; fs.fold = 0;
break; break;
} }
} }
/* Scan the text into visible characters. */ /* Scan the text into visible characters. */
cs = fuzzy_scan(text, &ncs, widths); fuzzy_scan(&fs, text);
matched = xcalloc(ncs == 0 ? 1 : ncs, sizeof *matched); if (fs.stopped)
best = xcalloc(ncs == 0 ? 1 : ncs, sizeof *best); goto fail;
size = fs.ncs == 0 ? 1 : fs.ncs;
fs.matched = xcalloc(size, sizeof *fs.matched);
best = xcalloc(size, sizeof *best);
/* A term with more characters than the text cannot match. */ /* A term with more characters than the text cannot match. */
toksize = strlen(pattern); toksize = strlen(pattern);
if (toksize > ncs) if (toksize > fs.ncs)
toksize = ncs; toksize = fs.ncs;
tok = xreallocarray(NULL, toksize == 0 ? 1 : toksize, sizeof *tok); fs.tok = xreallocarray(NULL, toksize == 0 ? 1 : toksize, sizeof *fs.tok);
/* Match each |-separated group and keep the best-scoring one. */ /* Match each |-separated group and keep the best-scoring one. */
cp = pattern; cp = pattern;
@@ -590,34 +627,31 @@ fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
sp = cp; sp = cp;
while (*cp != '\0' && *cp != '|') while (*cp != '\0' && *cp != '|')
cp++; cp++;
memset(matched, 0, ncs == 0 ? 1 : ncs); memset(fs.matched, 0, size);
groupmatched = matched; if (fuzzy_match_group(&fs, sp, cp, &groupscore)) {
if (fuzzy_match_group(sp, cp, tok, cs, ncs, fold,
&groupscore, groupmatched)) {
if (!found || groupscore > bestscore) { if (!found || groupscore > bestscore) {
found = 1; found = 1;
bestscore = groupscore; bestscore = groupscore;
memcpy(best, matched, ncs == 0 ? 1 : ncs); memcpy(best, fs.matched, size);
} }
} }
if (fs.stopped)
break;
} }
free(tok); if (fs.stopped || !found)
if (!found) { goto fail;
free(best);
free(matched);
free(cs);
return (NULL);
}
/* /*
* Work out the trimmed widths and start columns of each alignment, * Work out the trimmed widths and start columns of each alignment,
* mirroring format_draw_none. * mirroring format_draw_none.
*/ */
wl = widths[STYLE_ALIGN_LEFT]; wl = fs.widths[STYLE_ALIGN_LEFT];
wc = widths[STYLE_ALIGN_CENTRE]; wc = fs.widths[STYLE_ALIGN_CENTRE];
wr = widths[STYLE_ALIGN_RIGHT]; wr = fs.widths[STYLE_ALIGN_RIGHT];
wa = widths[STYLE_ALIGN_ABSOLUTE_CENTRE]; wa = fs.widths[STYLE_ALIGN_ABSOLUTE_CENTRE];
while (wl + wc + wr > width) { while (wl + wc + wr > width) {
if (!fuzzy_check_time(&fs))
goto fail;
if (wc > 0) if (wc > 0)
wc--; wc--;
else if (wr > 0) else if (wr > 0)
@@ -633,12 +667,12 @@ fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
vis[STYLE_ALIGN_LEFT] = wl; vis[STYLE_ALIGN_LEFT] = wl;
start[STYLE_ALIGN_RIGHT] = width - wr; start[STYLE_ALIGN_RIGHT] = width - wr;
src[STYLE_ALIGN_RIGHT] = widths[STYLE_ALIGN_RIGHT] - wr; src[STYLE_ALIGN_RIGHT] = fs.widths[STYLE_ALIGN_RIGHT] - wr;
vis[STYLE_ALIGN_RIGHT] = wr; vis[STYLE_ALIGN_RIGHT] = wr;
start[STYLE_ALIGN_CENTRE] = start[STYLE_ALIGN_CENTRE] =
wl + ((width - wr) - wl) / 2 - wc / 2; wl + ((width - wr) - wl) / 2 - wc / 2;
src[STYLE_ALIGN_CENTRE] = widths[STYLE_ALIGN_CENTRE] / 2 - wc / 2; src[STYLE_ALIGN_CENTRE] = fs.widths[STYLE_ALIGN_CENTRE] / 2 - wc / 2;
vis[STYLE_ALIGN_CENTRE] = wc; vis[STYLE_ALIGN_CENTRE] = wc;
start[STYLE_ALIGN_ABSOLUTE_CENTRE] = (width - wa) / 2; start[STYLE_ALIGN_ABSOLUTE_CENTRE] = (width - wa) / 2;
@@ -647,20 +681,31 @@ fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
/* Set a bit for each column of each matched character. */ /* Set a bit for each column of each matched character. */
mask = bit_alloc(width); mask = bit_alloc(width);
for (i = 0; i < ncs; i++) { for (i = 0; i < fs.ncs; i++) {
if (!fuzzy_check_time(&fs))
goto fail;
if (!best[i]) if (!best[i])
continue; continue;
if (fuzzy_column(&cs[i], start, src, vis, &column) != 0) if (fuzzy_column(&fs.cs[i], start, src, vis, &column) != 0)
continue; continue;
for (j = 0; j < cs[i].width && column + j < width; j++) for (j = 0; j < fs.cs[i].width && column + j < width; j++)
bit_set(mask, column + j); bit_set(mask, column + j);
} }
free(best); free(best);
free(matched); free(fs.tok);
free(cs); free(fs.matched);
free(fs.cs);
if (score != NULL) if (score != NULL)
*score = (bestscore < 0) ? 0 : (u_int)bestscore; *score = (bestscore < 0) ? 0 : (u_int)bestscore;
return (mask); return (mask);
fail:
free(mask);
free(best);
free(fs.tok);
free(fs.matched);
free(fs.cs);
return (NULL);
} }