Files
2026-09-04 15:04:37 -04:00

3.4 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What this is

A tool that runs an interactive shell behind a simulated serial (baud-rate) throttle, so that terminal output animates one character at a time instead of appearing all at once (e.g. to replay old VT100/ANSI animations that a fast terminal would render instantly).

  • baudshell — the program. Pure Python 3, self-contained: it launches bash --norc in a PTY and throttles both directions of I/O in-process (no helper binary, no cc build step).
  • baud.c — an optional standalone C pipe filter with the same throttling idea (reads stdin, re-emits it one char per 10/baud seconds to stdout). It is not used by baudshell; it exists for the "throttle a piped animation file" use case, e.g. cat anim.ans | baud 9600. It compiles independently (cc baud.c -o baud); the baud binary it produces is gitignored.

There is no build system, test suite, linter, or CI. Everything besides the two source files (the baud binary, .DS_Store) is in .gitignore.

Run

No build step is needed for the shell:

./baudshell            # or: python3 baudshell
./baudshell 2400       # simulate 2400 baud instead of the default 9600

argv[1], if given, is the baud rate (a positive integer; default 9600, lower is slower). A non-integer or non-positive rate prints an error and exits 2.

The program puts the terminal into raw mode and restores it on exit, so keystrokes reach the shell char-by-char. To leave, type exit in the shell (or close the terminal); the program detects the shell's exit and cleans up.

Architecture

baudshell multiplexes two file descriptors with select(): the terminal (sys.stdin) and the PTY master. The single subprocess is bash (in its own session via preexec_fn=os.setsid, on the PTY slave); the throttle itself runs in-process.

keyboard ─► fd_term ─► throttled_copy ─► master ─► shell (bash)
                                       │
terminal ◄─ fd_term ◄─ throttled_copy ◄─ master ◄┘
  • Throttling is throttled_copy(src, dst, baud): read a chunk from src, then write it to dst one byte at a time, sleeping 10/baud seconds between bytes. This runs for both directions — keystrokes are throttled into the shell, and the shell's output is throttled out to the terminal, mirroring a real serial link.
  • delay_per_char(baud) returns 10/baud seconds. A serial byte is 10 bits (1 start + 8 data + 1 stop), so a line at baud bits/s carries baud/10 bytes/s; this is the same formula the original baud.c used.

End-of-stream / clean shutdown (the reason the old version hung)

The loop breaks as soon as either copy returns True (stream finished), which is how the two termination cases are handled:

  • Shell exits (master reaches EOF) → stop, then reap the shell and propagate its exit status. This is the fix for the original bug: the previous version ignored the copy result and spun forever after the shell exited.
  • stdin reaches EOF (terminal closed / piped input drained) → stop, and shell.terminate() kills the still-running shell.

A finally block always restores the terminal's termios settings. Throttle math: rate must be > 0; unlike the C original, Python's time.sleep has no upper bound, so very low rates are accepted rather than rejected.