type DecodedAtom = { value: string; next: number; }; const simpleEscapes: Readonly> = { a: "\x07", b: "\b", e: "\x1b", f: "\f", n: "\n", r: "\r", t: "\t", v: "\v", "\\": "\\", '"': '"', "'": "'", }; function controlCharacter(value: string): string { if (value === "?") return "\x7f"; return String.fromCharCode(value.toUpperCase().charCodeAt(0) & 0x1f); } function readAtom(source: string, index: number): DecodedAtom { const character = source[index]; if (character === undefined) { throw new RangeError("Inputrc key sequence ends unexpectedly"); } if (character !== "\\") { return { value: character, next: index + 1 }; } const escaped = source[index + 1]; if (escaped === undefined) { throw new RangeError("Inputrc key sequence ends after an escape"); } if (escaped === "C") { if (source[index + 2] !== "-") throw new RangeError("Control escape must use \\C-"); const control = readAtom(source, index + 3); if (control.value.length !== 1) throw new RangeError("Control escape must resolve to one character"); return { value: controlCharacter(control.value), next: control.next }; } if (escaped === "M") { if (source[index + 2] !== "-") throw new RangeError("Meta escape must use \\M-"); const meta = readAtom(source, index + 3); return { value: `\x1b${meta.value}`, next: meta.next }; } if (escaped === "x") { const digits = source.slice(index + 2).match(/^[0-9a-fA-F]{1,2}/)?.[0]; if (digits === undefined) throw new RangeError("Hex escape requires at least one digit"); return { value: String.fromCharCode(Number.parseInt(digits, 16)), next: index + 2 + digits.length, }; } if (/[0-7]/.test(escaped)) { const digits = source.slice(index + 1).match(/^[0-7]{1,3}/)?.[0]; if (digits === undefined) throw new RangeError("Octal escape requires at least one digit"); return { value: String.fromCharCode(Number.parseInt(digits, 8)), next: index + 1 + digits.length, }; } const simple = simpleEscapes[escaped]; if (simple === undefined) throw new RangeError(`Unsupported inputrc escape: \\${escaped}`); return { value: simple, next: index + 2 }; } /** Decode a canonical inputrc key-sequence string to terminal input bytes. */ export function decodeInputrcKeySequence(source: string): string { let decoded = ""; let index = 0; while (index < source.length) { const atom = readAtom(source, index); decoded += atom.value; index = atom.next; } return decoded; }