Backspace, whatever the terminal calls it

CosmOS's line editor looks for 0x08, which is what Voyager's keyboard
sends. A POSIX terminal sends its own erase character instead, and on
most of them that is 0x7F.

It stayed hidden while the terminal was doing the editing: canonical
mode consumes the erase character itself and hands over a finished
line. Key mode turns ICANON off, which is the point of it, so from the
day the shell started editing its own line - 7360374 - Backspace worked
in Voyager and did nothing at all in the console-only emulator. The
Programming Manual already claimed the console normalised Backspace on a
terminal the way it normalises the arrow keys; the code did not.

The erase character is read from the terminal's own VERASE rather than
assumed to be 0x7F, because some terminals are set to 0x08 and a person
who has moved their erase key has said where it is. Forward Delete is
untouched and stays 0x86: two keys, two values.

Only when standard input really is a terminal. A file or a pipe holding
0x7F holds a byte somebody wrote, not a key somebody pressed.

Three checks in terminal.sh under a pseudo-terminal whose VERASE is set
on the slave side: erase at 0x7F arrives as Backspace, erase at 0x08
still does, and a 0x7F read from a file is left alone. The last one is
what fails on the obvious wrong fix of translating 0x7F unconditionally,
which was verified with break.sh along with the fix's removal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-09-05 11:56:17 -04:00
co-authored by Claude Opus 5
parent 019c93a587
commit 8631a78229
3 changed files with 87 additions and 3 deletions
+35 -1
View File
@@ -610,14 +610,48 @@ static int consoleKeyFromEscape(void) {
// One key from standard input: a byte as it arrived, or one of the console's own key values
// where a terminal sent a sequence meaning one.
// ---- The terminal's erase key is not the one this machine knows ----
//
// SplitBit's Backspace is 0x08. That is what Voyager's keyboard sends and what CosmOS's line
// editor looks for. A POSIX terminal sends whatever ITS erase character is, and on most of
// them that is 0x7F.
//
// It went unnoticed for as long as the terminal was doing the editing: in canonical mode the
// tty eats its own erase character and hands over a finished line. Key mode turns ICANON off,
// which is the point of it, and from then on the byte arrives raw and means nothing to the
// editor. So Backspace worked in Voyager and stopped working in the console-only emulator on
// the day the shell started editing the line itself.
//
// VERASE IS ASKED RATHER THAN 0x7F ASSUMED, because the terminal is what knows: it was saved
// on the way into raw mode, some terminals really are set to 0x08, and a person who has moved
// their erase key somewhere else has said where it is.
//
// ONLY WHEN STANDARD INPUT IS A TERMINAL. A file or a pipe holding 0x7F holds a byte that
// somebody wrote, not a key somebody pressed, and rewriting it would corrupt input that has
// nothing to do with terminals. consoleTerminalSaved is exactly that question: it is only
// ever set after an isatty succeeded.
static int consoleEraseNormalized(int got) {
if (!consoleTerminalSaved || got < 0) {
return got;
}
const cc_t erase = consoleSavedTerminal.c_cc[VERASE];
// A terminal with no erase key at all, which says so this way, has nothing to translate.
if (erase == _POSIX_VDISABLE) {
return got;
}
return got == (int)erase ? 0x08 : got;
}
static int consoleKeyFromInput(int mayWait) {
for (;;) {
int got;
if (consoleHeldByte >= 0) {
// Already normalized on the way in, and doing it twice would be wrong the day
// 0x08 is somebody's erase character: it is its own answer.
got = consoleHeldByte;
consoleHeldByte = -1;
} else {
got = consoleFromInput(mayWait);
got = consoleEraseNormalized(consoleFromInput(mayWait));
}
if (got < 0) {
return got;