I'll try to answer my own question. Here's what I'm planning to do:
- Printables (ASCII 0x20 to 0x7E) are echoed.
- CR is echoed as CR LF (because the Enter key on terminals normally
sends CR, and an ANSI terminal requires CR to move the cursor to the
left, and LF to move it to the next line).
- BS (backspace, 0x08) and DEL (0x7F) are treated identically and are
echoed as "\b \b" (in C syntax) - that is, backspace, space,
backspace, to erase the last character on the terminal.
All other control characters are not echoed. (Not to say they're not processed, but they're not automatically echoed. What they do is outside the scope of what I'm asking about.)
My reasoning is that the remaining control characters are usually meant to do something, and that something is meant to happen at the host, not at the terminal.
For example DC1/DC3 (^Q/^S) are often used as flow control ("XON/XOFF") - it doesn't make sense to echo the ^S (XOFF) back to the terminal, since the purpose is to flow control the host. Echoing XOFF back to the terminal would flow control the terminal, clearly not what was intended. So echoing this makes no sense.
Similarly, ANSI escape sequences sent by the terminal (cursor up/down/left/right, etc.) should not be echoed.
Bottom line - echo printables only. Control characters as a rule should not be echoed, except on a case-by-case basis depending on their function (carriage return, backspace, etc.).
I'd like comments on whether or not this is the right thing to do (and why).