Fixed: Fully consume unknown CSI sequences containing unsupported parameter and intermediate bytes

Standard ECMA-48: Control Functions for Coded Character Sets specifies the format of CSI commands.
- https://en.wikipedia.org/wiki/ANSI_escape_code#Control_Sequence_Introducer_commands
- https://invisible-island.net/xterm/ecma-48-parameter-format.html#section5.4

Previously unsupported bytes would be echoed to the terminal.

```shell
$ printf '\x1b[=u' # PF
u
$ printf '\x1b[=5u' # PPF
5u
$ printf '\x1b[=5!u' # PPIF
5!u
$ printf '\x1b[=5!%u' # PPIIF
5!0
$ printf '\x1b[=?5!%u' # PPPIIF
?5!0
```

This fixes a problem with fish shell 4.0.0 which uses that sequence.

Closes #4338

Co-authored-by: @krobelus <aclopte@gmail.com>
Co-authored-by: @agnostic-apollo  <agnosticapollo@gmail.com>
This commit is contained in:
Johannes Altmanninger
2025-03-01 15:59:13 +01:00
committed by agnostic-apollo
parent d2cd6ac2e5
commit a988383e01

View File

@@ -83,6 +83,10 @@ public final class TerminalEmulator {
private static final int ESC_APC = 20;
/** Escape processing: "ESC _" or Application Program Command (APC), followed by Escape. */
private static final int ESC_APC_ESCAPE = 21;
/** Escape processing: ESC [ <parameter bytes> */
private static final int ESC_CSI_UNSUPPORTED_PARAMETER_BYTE = 22;
/** Escape processing: ESC [ <parameter bytes> <intermediate bytes> */
private static final int ESC_CSI_UNSUPPORTED_INTERMEDIATE_BYTE = 23;
/** The number of parameter arguments including colon separated sub-parameters. */
private static final int MAX_ESCAPE_PARAMETERS = 32;
@@ -658,6 +662,10 @@ public final class TerminalEmulator {
case ESC_CSI:
doCsi(b);
break;
case ESC_CSI_UNSUPPORTED_PARAMETER_BYTE:
case ESC_CSI_UNSUPPORTED_INTERMEDIATE_BYTE:
doCsiUnsupportedParameterOrIntermediateByte(b);
break;
case ESC_CSI_EXCLAMATION:
if (b == 'p') { // Soft terminal reset (DECSTR, http://vt100.net/docs/vt510-rm/DECSTR).
reset();
@@ -1059,6 +1067,37 @@ public final class TerminalEmulator {
return mRightMargin - 1;
}
/**
* Process byte while in the {@link #ESC_CSI_UNSUPPORTED_PARAMETER_BYTE} or
* {@link #ESC_CSI_UNSUPPORTED_INTERMEDIATE_BYTE} escape state.
*
* Parse unsupported parameter, intermediate and final bytes but ignore them.
*
* > For Control Sequence Introducer, ... the ESC [ is followed by
* > - any number (including none) of "parameter bytes" in the range 0x300x3F (ASCII 09:;<=>?),
* > - then by any number of "intermediate bytes" in the range 0x200x2F (ASCII space and !"#$%&'()*+,-./),
* > - then finally by a single "final byte" in the range 0x400x7E (ASCII @AZ[\]^_`az{|}~).
*
* - https://en.wikipedia.org/wiki/ANSI_escape_code#Control_Sequence_Introducer_commands
* - https://invisible-island.net/xterm/ecma-48-parameter-format.html#section5.4
*/
private void doCsiUnsupportedParameterOrIntermediateByte(int b) {
if (mEscapeState == ESC_CSI_UNSUPPORTED_PARAMETER_BYTE && b >= 0x30 && b <= 0x3F) {
// Supported `09:;>?` or unsupported `<=` parameter byte after an
// initial unsupported parameter byte in `doCsi()`, or a sequential parameter byte.
continueSequence(ESC_CSI_UNSUPPORTED_PARAMETER_BYTE);
} else if (b >= 0x20 && b <= 0x2F) {
// Optional intermediate byte `!"#$%&'()*+,-./` after parameter or intermediate byte.
continueSequence(ESC_CSI_UNSUPPORTED_INTERMEDIATE_BYTE);
} else if (b >= 0x40 && b <= 0x7E) {
// Final byte `@AZ[\]^_`az{|}~` after parameter or intermediate byte.
// Calling `unknownSequence()` would log an error with only a final byte, so ignore it for now.
finishSequence();
} else {
unknownSequence(b);
}
}
/** Process byte while in the {@link #ESC_CSI_QUESTIONMARK} escape state. */
private void doCsiQuestionMark(int b) {
switch (b) {
@@ -1656,12 +1695,16 @@ public final class TerminalEmulator {
}
mCursorCol = newCol;
break;
case '?': // Esc [ ? -- start of a private mode set
case '?': // Esc [ ? -- start of a private parameter byte
continueSequence(ESC_CSI_QUESTIONMARK);
break;
case '>': // "Esc [ >" --
case '>': // "Esc [ >" -- start of a private parameter byte
continueSequence(ESC_CSI_BIGGERTHAN);
break;
case '<': // "Esc [ <" -- start of a private parameter byte
case '=': // "Esc [ =" -- start of a private parameter byte
continueSequence(ESC_CSI_UNSUPPORTED_PARAMETER_BYTE);
break;
case '`': // Horizontal position absolute (HPA - http://www.vt100.net/docs/vt510-rm/HPA).
setCursorColRespectingOriginMode(getArg0(1) - 1);
break;