Subj : src/sbbs3/sexyz.c zmodem.c zmodem.h To : Git commit to main/sbbs/master From : Rob Swindell (on Debian Linux) Date : Mon Aug 24 2026 22:46:27 https://gitlab.synchro.net/main/sbbs/-/commit/d16135c1046c973abab5afad Modified Files: src/sbbs3/sexyz.c zmodem.c zmodem.h Log Message: zmodem: rebuild the plain-byte table when a receiver sets EscapeCtrlChars rx_plain_tab[] caches escape_ctrl_chars, and the commit that derived the receive fast path from it rebuilt the table in only two places: zmodem_init(), and zmodem_parse_zrinit(). A receiver never reaches the second one -- only a sender parses a received ZRINIT -- so on the receive side the table was whatever zmodem_init() built, with the flag still at its FALSE default. sexyz then assigns zm.escape_ctrl_chars directly from sexyz.ini's EscapeCtrlChars (sexyz.c), after zmodem_init() and with nothing to rebuild the table. With EscapeCtrlChars=true a receive therefore ran with a table that called control characters plain, and both new paths took them at face value: zmodem_rx() returned them verbatim and recv_span() copied them into the subpacket, where the old code dropped them as "unescaped ctrl char". That is the whole point of ESCCTL, and the config exists for exactly the links that need it. Rebuild in zmodem_send_zrinit(). A receive session always passes through it, the consumer's value is necessarily final there -- the ZRINIT built in that function is what announces ESCCTL to the peer -- and it costs a 256-iteration loop once per session. Reproduced and verified with a harness that replays sexyz's init order: before, an escape_ctrl_chars set after zmodem_init() left rx_plain_tab[0x01]=1; after, send_zrinit() brings it to 0. Since escape_ctrl_chars is a public struct field any consumer may assign, say so where it is declared and list the three rebuild points in the table function's comment. Also stop bypassing recv_byte()'s DebugRx trace. The span path skips recv_byte() entirely, so with DebugRx=true the per-byte RX log lost every byte a span carried -- around 97 % of the stream on random data, leaving a trace of nothing but escapes. Disable the span path when debug_rx is set, the way it already is for telnet mode, on the same grounds: both are cases where recv_byte() has to see every byte. No cost when the flag is off. No throughput change: 484.4 MB/s against 484.2 before, receiver CPU 0.55 s. Clean receives verify byte-identical 3 of 3 and the 3e-6 corruption gate passes 5 of 5, sender gate 3 of 3. Found by reviewing the series after the sexyz 3.5 release, at Rob Swindell's request. --- SBBSecho 3.37-Linux * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705) .