Commit graph

2 commits

Author SHA1 Message Date
Stephen Dennis
22eac628f2 fix: wild1 '?' after '*' captured one byte, splitting multibyte chars (#838)
The numextra path in wild1() (a '?' immediately after a '*') advanced the data
pointer by one byte per '?' and copied one byte per '?' in both capture-fill
sites, so on multibyte data it split a UTF-8 character across captures, e.g.
wild('*?x','éx') gave %0='\xC3' %1='\xA9' instead of %0='' %1='é'. (The
standalone '?' was already character-aware; only this post-'*' path was not.)

Make the three numextra sites character-oriented: the do-while '?'-skip advances
a whole character (wild_char_len); both capture-fill loops copy a whole character
(new wild_capture_char helper); the trailing '*' span is computed by walking back
numextra whole characters from the anchor (new wild_step_back helper) rather than
dstr - numextra bytes. datapos becomes const (read-only cursor).

wild_test.cpp extended with 8 '*?'-adjacency cases (ASCII + UTF-8); 28/28 pass,
ASCII captures unchanged. Smoke 1254/1254.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 14:19:16 -06:00
Stephen Dennis
bf8f72bc57 fix: wild() capturing path now folds non-ASCII case too (#837)
wild1() and wild()'s literal-prefix fast-match folded only ASCII in the data
side (byte-wise EQUAL/NOTEQUAL), so $-command / ^-listen wildcard captures did
not match non-ASCII letters case-insensitively (e.g. a $café * command would
not match CAFÉ ...), even though quick_wild/strmatch already did (#836).

Make the literal comparisons character-oriented via the same wild_lit_eq()
helper quick_wild_impl uses (fold each data char with mux_tolower, XOR-mask
aware, advancing the pattern by the folded length and the data by the original
length). Captures stay original-case: only the literal comparisons fold; the
'*'/'?' capture spans copy from the unmodified data. The scan-forward and retry
advances are now whole-character. EQUAL/NOTEQUAL are removed (no byte-wise
comparisons remain).

Verified with a new standalone unit harness, wild_test.cpp (links the real
wild.eo against libmux, supplies the mudstate/mudconf globals + pool_init, calls
wild()/quick_wild() directly and asserts %0..%9 captures) — muxscript's REPL
drives neither $-commands nor ^-listens and the smoke suite has no capture
coverage, so this is the only test of the capture path. 20 cases (ASCII + UTF-8,
captures + spans); first run against byte-wise wild1 to confirm it caught the
bug, then green after the fix. Smoke 1254/1254.

A pre-existing separate bug remains: wild1's post-'*' trailing-'?' (numextra)
handling is byte-wise and can split a multibyte character into a capture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 13:58:55 -06:00