>From 5a69dd4e2e80fa8b9f2ed85be2fd7b647f8daec7 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Tue, 6 Oct 2026 13:36:13 -0400 Subject: [PATCH 6/6] fnmatch: do collation comparisons for equivalence classes and ranges the primary-level-only comparisons needed here are implemented by treating wcscoll as a black box, assuming only that non-character codepoints U+10FFFE and U+10FFFF have distinct implicit primary weights. only single-character collating elements are supported. in the absence of working space to build an NFA representation of the pattern, multi-character collating elements seem to require a backtracking (exponential time) approach to matching, which is not viable. --- src/regex/fnmatch.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/regex/fnmatch.c b/src/regex/fnmatch.c index f93369e6..6758bd6e 100644 --- a/src/regex/fnmatch.c +++ b/src/regex/fnmatch.c @@ -107,10 +107,27 @@ static int casefold(int k) return c == k ? towlower(k) : c; } +static int cmp(int a, int b, int collation_order) +{ + if (!collation_order) + return a-b; + /* Append non-characters with a known implicit primary weight order + * in order to detect differences that are only post-primary-level. */ + wchar_t ac[3] = { a, 0x10fffe }; + wchar_t bc[3] = { b, 0x10ffff }; + int d1 = wcscoll(ac, bc); + ac[1]++; + bc[1]--; + int d2 = wcscoll(ac, bc); + if (d1 != d2) return 0; + return d1; +} + static int match_bracket(const char *p, int k, int kfold, int esc_ch) { wchar_t wc; int inv = 0; + int is_cs = 0, is_eq = 0; p++; if (*p=='^' || *p=='!') { inv = 1; @@ -126,7 +143,10 @@ static int match_bracket(const char *p, int k, int kfold, int esc_ch) wc = p[-1]; for (; *p != ']'; p++) { wchar_t wc0 = wc; + int is_cs_0 = is_cs; int is_range = 0; + is_cs = 0; + is_eq = 0; if (p[0]=='-' && p[1]!=']') { is_range = 1; p++; @@ -151,6 +171,8 @@ static int match_bracket(const char *p, int k, int kfold, int esc_ch) if (*p0 == esc_ch) p0++; int l = mbtowc(&wc, p0, 4); if (l != p-1-p0) continue; + is_cs = 1; + if (z=='=') is_eq = 1; } else { if (*p == esc_ch) p++; if (*p < 128U) { @@ -162,10 +184,14 @@ static int match_bracket(const char *p, int k, int kfold, int esc_ch) } } if (is_range) { - if ((k>=wc0 && k<=wc) || (kfold>=wc0 && kfold<=wc)) + if (cmp(k, wc0, is_cs_0)>=0 && cmp(k, wc, is_cs)<=0) + return !inv; + if (kfold != k && cmp(kfold, wc0, is_cs_0)>=0 && cmp(kfold, wc, is_cs)<=0) return !inv; } else if (p[1]!='-' || p[2]==']') { - if (wc==k || wc==kfold) + if (!cmp(k, wc, is_eq)) + return !inv; + if (kfold != k && !cmp(kfold, wc, is_eq)) return !inv; } } -- 2.21.0