https://gitlab.synchro.net/main/sbbs/-/commit/6cce9286f030bf47bd57e6d5
Modified Files:
src/syncterm/ripper.c
Log Message:
Fix three |# (RIP_NO_MORE) parsing bugs in parse_rip()
Bug reported by NightFox, who should really open bug reports on
SourceForge instead of making people chase things down secondhand.
1. Stale '!' rendered when |# follows non-RIP text
When non-RIP bytes precede '!' in the same buffer (e.g. the common
case of \n\r or \r\n line endings before a RIP line), rip_start is
non-zero. The |# handler called handle_rip_line() which took its
first branch: deferring the RIP data to pending, truncating blen to
rip_start, and returning false. But the handler unconditionally ran
rip_start = pos + 1, which now exceeded blen. At the end of
parse_rip(), rip_start <= maxlen was true but rip_start > blen, so
buffer_rip was skipped but rip_start was returned as the output
length — reading the stale '!' byte from the physically-uncleared
buffer position. Commit 27e6a20f added the rip_start <= blen guard
to prevent a crash but did not fix the data leak.
Fix: check handle_rip_line()'s return value. When it returns false
(deferred), don't modify rip_start. When it returns true (processed
immediately), do the post-processing as before. Also pass
RIP_STATE_BANG instead of RIP_STATE_CMD since both paths want BANG
state after |#.
2. |# must flush immediately, even when deferred
The entire purpose of the |# special handling (added in 93e05beb)
is to flush RIP commands without waiting for a line terminator.
When handle_rip_line() defers (returns false), the RIP data sits
in pending unprocessed until the next parse_rip() call — defeating
the flush semantics. This matters for interactive commands like
the NY2008 popup (|1000) that block waiting for user input: the
popup wouldn't appear until after the next data arrived.
Fix: when handle_rip_line() returns false, process pending
immediately via do_rip_string() and reset newstate to FLUSHING
so the next flush call knows there's nothing left to process.
3. |# bytes leak as visible text from moredata
When handle_rip_line()'s first branch defers RIP data, remaining
bytes after |# are saved to moredata. On the next parse_rip()
call, the flush path processes pending and switches to moredata.
But handle_rip_line() unconditionally resets rip_start to the
sentinel (maxlen+1), even though the restored state (BANG) means
the moredata buffer is entirely RIP data. With rip_start as
sentinel, the |# handler's handle_rip_line() call took the second
path with remove=0, failing to remove the |# bytes from the
buffer. They were then returned to the caller as visible text.
Fix: after the flush path switches to moredata, re-check the
restored state. If it's not BOL/MOL (i.e. still inside a RIP
sequence), reset rip_start to 0.
4. Allow '!' to start new RIP sequence after |#
Per the spec, |# means "end of RIPscrip scene." Testing against
RIPterm (reference implementation) confirms that '!' after |# on
the same line starts a new RIP sequence. SyncTERM's BANG state
only accepted '|', sending '!' to unrip_line -> MOL where it was
rejected as a RIP start character.
Fix: accept '!', '\x01', and '\x02' in RIP_STATE_BANG, updating
rip_start and staying in BANG. This matches RIPterm's behavior
where both |#|#|# (repeated NO_MORE) and |#!|... (new sequence)
work on the same line.
Co-Authored-By: Claude Opus 4.6 (1M context) <
noreply@anthropic.com>
---
■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net