magic replaces Tcl's stdin channel with its own (TerminalInputProc) during
startup (_magic_startup). The replacement builds a brand-new Tcl channel over
the same fd; the new channel starts with an empty buffer. If anything had
already read() stdin bytes out of the kernel into the *old* channel's buffer
before the swap -- e.g. a script/commands piped in fast enough that Tcl buffered
them during startup -- those bytes were silently dropped, and every command
after that point vanished. This was timing-dependent, so it surfaced only
intermittently ("piped input discarded sometimes").
Fix: before creating the replacement channel, drain any bytes still buffered in
the old channel (Tcl_InputBuffered / Tcl_ReadChars) and hand them to
TerminalInputProc via TxBuffer, which it already serves ahead of any fresh
read(). The swap is now lossless by construction rather than by luck. On the
normal path Tcl_InputBuffered() is 0, so the new branch is inert.
How to reproduce / observe the original loss:
Temporarily force the old channel to buffer input just before the swap, right
after `fsOrig = Tcl_GetChannelInstanceData(oldchannel);` in _magic_startup():
{ Tcl_Obj *l = Tcl_NewObj(); Tcl_IncrRefCount(l);
Tcl_GetsObj(oldchannel, l); /* consume 1 line, buffering the rest */
fprintf(stderr, "buffered=%d\n", (int)Tcl_InputBuffered(oldchannel));
Tcl_DecrRefCount(l); }
then pipe a burst of commands (more than one line):
printf 'puts a\nputs b\n...\nquit -noprompt\n' | ./run_magicnull.sh
Comment the drain out and every command after the first disappears; restore it
and they all survive. Measured with 218 bytes buffered: 0/20 commands survive
without the drain, 19/20 with it (the one line the probe consumed aside).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>