tweaks to ANSI sequence (OSC) handling (#34964)

Fixes: #34604

Prompted by that I realized we do not correctly recognize both "ST"
sequences we want to recognize, fix that.
This commit is contained in:
Luca Boccassi
2024-11-01 11:18:57 +00:00
committed by GitHub
6 changed files with 65 additions and 20 deletions
+27 -8
View File
@@ -698,7 +698,8 @@ char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
STATE_OTHER,
STATE_ESCAPE,
STATE_CSI,
STATE_CSO,
STATE_OSC,
STATE_OSC_CLOSING,
} state = STATE_OTHER;
_cleanup_(memstream_done) MemStream m = {};
size_t isz, shift[2] = {}, n_carriage_returns = 0;
@@ -711,7 +712,7 @@ char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
*
* 1. Replaces TABs by 8 spaces
* 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
* 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences
* 3. Strips ANSI operating system sequences (OSC), i.e. ESC ']' … ST sequences
* 4. Strip trailing \r characters (since they would "move the cursor", but have no
* other effect).
*
@@ -719,7 +720,7 @@ char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
* are any other special characters. Truncated ANSI sequences are left-as is too. This call is
* supposed to suppress the most basic formatting noise, but nothing else.
*
* Why care for CSO sequences? Well, to undo what terminal_urlify() and friends generate. */
* Why care for OSC sequences? Well, to undo what terminal_urlify() and friends generate. */
isz = _isz ? *_isz : strlen(*ibuf);
@@ -766,8 +767,8 @@ char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
} else if (*i == '[') { /* ANSI CSI */
state = STATE_CSI;
begin = i + 1;
} else if (*i == ']') { /* ANSI CSO */
state = STATE_CSO;
} else if (*i == ']') { /* ANSI OSC */
state = STATE_OSC;
begin = i + 1;
} else {
fputc('\x1B', f);
@@ -793,17 +794,35 @@ char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
break;
case STATE_CSO:
case STATE_OSC:
assert(n_carriage_returns == 0);
/* There are three kinds of OSC terminators: \x07, \x1b\x5c or \x9c. We only support
* the first two, because the last one is a valid UTF-8 codepoint and hence creates
* an ambiguity (many Terminal emulators refuse to support it as well). */
if (i >= *ibuf + isz || /* EOT … */
(*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */
(!IN_SET(*i, '\x07', '\x1b') && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */
fputc('\x1B', f);
fputc(']', f);
advance_offsets(i - *ibuf, highlight, shift, 2);
state = STATE_OTHER;
i = begin-1;
} else if (*i == '\a')
} else if (*i == '\x07') /* Single character ST */
state = STATE_OTHER;
else if (*i == '\x1B')
state = STATE_OSC_CLOSING;
break;
case STATE_OSC_CLOSING:
if (i >= *ibuf + isz || /* EOT … */
*i != '\x5c') { /* … or incomplete two-byte ST in sequence */
fputc('\x1B', f);
fputc(']', f);
advance_offsets(i - *ibuf, highlight, shift, 2);
state = STATE_OTHER;
i = begin-1;
} else if (*i == '\x5c')
state = STATE_OTHER;
break;
+2 -2
View File
@@ -1923,12 +1923,12 @@ int get_default_background_color(double *ret_red, double *ret_green, double *ret
if (tcsetattr(STDIN_FILENO, TCSADRAIN, &new_termios) < 0)
return -errno;
r = loop_write(STDOUT_FILENO, "\x1B]11;?\x07", SIZE_MAX);
r = loop_write(STDOUT_FILENO, ANSI_OSC "11;?" ANSI_ST, SIZE_MAX);
if (r < 0)
goto finish;
usec_t end = usec_add(now(CLOCK_MONOTONIC), 333 * USEC_PER_MSEC);
char buf[STRLEN("\x1B]11;rgb:0/0/0\x07")]; /* shortest possible reply */
char buf[STRLEN(ANSI_OSC "11;rgb:0/0/0" ANSI_ST)]; /* shortest possible reply */
size_t buf_full = 0;
BackgroundColorContext context = {};
+10
View File
@@ -27,6 +27,16 @@
#define ANSI_WINDOW_TITLE_PUSH "\x1b[22;2t"
#define ANSI_WINDOW_TITLE_POP "\x1b[23;2t"
/* ANSI "string terminator" character ("ST"). Terminal emulators typically allow three different ones: 0x07,
* 0x9c, and 0x1B 0x5C. We'll avoid 0x07 (BEL, aka ^G) since it might trigger unexpected TTY signal
* handling. And we'll avoid 0x9c since that's also valid regular codepoint in UTF-8 and elsewhere, and
* creates ambiguities. Because of that some terminal emulators explicitly choose not to support it. Hence we
* use 0x1B 0x5c */
#define ANSI_ST "\e\\"
/* The "operating system command" ("OSC") start sequence */
#define ANSI_OSC "\e]"
bool isatty_safe(int fd);
int terminal_reset_defensive(int fd, bool switch_to_text);