[terminfo] support the %'c' character constant - #1744
Conversation
The terminfo parser handled %{nn} integer constants but had no case for
%'c', so the operator fell through to the default branch and its bytes
were copied into the output as literal text.
xterm-256color's setaf uses %'\010' and %'\020' to test the colour index
against 8 and 16, so the conditional never evaluated and tiparm_s()
returned a malformed CSI sequence containing a literal BS:
setaf(112) -> \E[\010'3112m instead of \E[38;5;112m
0x08 and ' are not valid in a CSI parameter string, so terminals abort
the sequence and print the rest as text, which corrupts the display.
This affects every terminfo entry whose setaf/setab uses %'c', including
xterm-256color, xterm-16color, screen-256color, tmux-256color, alacritty,
xterm-kitty, xterm-ghostty and vte-256color.
Handle plain characters, octal escapes such as %'\010', and the common
named escapes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tiparm_s consulted `exec` only when producing output, never when
mutating the stack, so `%p`, `%{n}`, `%'c'` and the binary operators
ran in both arms of a `%?...%t...%e...%;`. Entries that place `%d`
after the `%;` then print whatever the untaken arm left behind:
setaf=\E[%?%p1%{8}%<%t%p1%{30}%+%e%p1%'R'%+%;%dm
For setaf(0) the live arm pushes 30, the dead arm pushes 0 and 'R'
and adds them to 82, and `%d` pops 82. Adding the `%'c'` push made
this reachable for the whole `*-16color` family: setaf(0..7) emitted
SGR 82-89, which terminals ignore, and setab(0..7) emitted SGR 92-99,
which are bright foreground colors, so a background request silently
recolored the foreground instead. Gating every stack operation on
`exec` brings xterm-16color and xterm-256color to zero mismatches
against ncurses tparm.
The backslash handling in `%'c'` is also removed. terminfo_load reads
compiled entries, and tic resolves escapes at compile time, so a source
`%'\010'` arrives as a raw 0x08 that the plain-character path already
handles. The only byte the escape branch could actually encounter is
a literal backslash from a source `%'\\'`, and it mis-parsed that one,
consuming the closing quote as the escaped character and pushing 0x27
instead of 0x5C.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Thanks for tracking this down — the diagnosis is right and the reproducer made it easy to verify. I've pushed a follow-up commit to your branch rather than round-tripping; details below so you can check my reasoning. Why the extra commit
The 16-color entries put For
The backslash branchI also dropped the escape handling. So the branch was reachable on exactly one input — a literal backslash from a source One note on the PR descriptionOn ncurses 6.x here, VerificationDifferential harness against ncurses |
|
That comment above is from Claude... Thanks for the PR. I wish terminfo wasn't an overcomplicated pile of crud, sigh. I'll get it merged in a little bit. |
|
The remaining CI failure looks pre-existing rather than related to this branch.
Other observations:
So I have not identified the actual cause — only that it does not appear to be introduced by this branch. |
Fixes #1743.
The terminfo parser added in 1e8221e handles
%{nn}integer constants but hasno case for the
%'c'character constant, so the operator falls through todefault: p++and its bytes are copied into the output as literal text.xterm-256color'ssetafuses%'\010'and%'\020'to test the colour indexagainst 8 and 16, so the conditional never evaluates:
0x08 and
'are not valid in a CSI parameter string, so terminals abort thesequence and print the remainder as text. That produces the
3112m-stylegarbage in the display and misaligns columns.
This adds
case '\'', handling plain characters, octal escapes such as%'\010', and the common named escapes.Affected terminals
Any entry whose
setaf/setabuses%'c'— xterm-256color, xterm-16color,screen-256color, tmux-256color, alacritty, xterm-kitty, xterm-ghostty,
vte-256color. Not affected: xterm, xterm-color, screen, tmux, linux.
Verification
setafcases)Rendered under 13 different
TERMvalues: entries using%'c'give 111 beforeand 0 after; entries without it give 0 in both cases — exact correlation, no
exceptions.
make checkgives 43 pass / 10 fail both with and without this patch. The 10failures are pre-existing on master and unrelated (mostly SQL function tests).
The issue includes a self-contained reproducer that exercises the parser
directly, with no terminal involved.
Disclosure
The diagnosis, reproducer and patch were produced with AI assistance
(Claude Code) and reviewed before submission. The reproducer is self-contained
so the claims can be verified independently.