mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.1.2123: parsing CSI sequence is messy
Problem: Parsing CSI sequence is messy. Solution: Generalize parsing a CSI sequence.
This commit is contained in:
parent
fbbd102be0
commit
c3e555b22f
299
src/term.c
299
src/term.c
@ -4435,72 +4435,105 @@ check_termcode(
|
||||
# endif
|
||||
)
|
||||
{
|
||||
/* Check for some responses from the terminal starting with
|
||||
* "<Esc>[" or CSI:
|
||||
char_u *argp = tp[0] == ESC ? tp + 2 : tp + 1;
|
||||
|
||||
/*
|
||||
* Check for responses from the terminal starting with {lead}:
|
||||
* "<Esc>[" or CSI followed by [0-9>?]
|
||||
*
|
||||
* - Xterm version string: <Esc>[>{x};{vers};{y}c
|
||||
* Libvterm returns {x} == 0, {vers} == 100, {y} == 0.
|
||||
* - Xterm version string: {lead}>{x};{vers};{y}c
|
||||
* Also eat other possible responses to t_RV, rxvt returns
|
||||
* "<Esc>[?1;2c". Also accept CSI instead of <Esc>[.
|
||||
* mrxvt has been reported to have "+" in the version. Assume
|
||||
* the escape sequence ends with a letter or one of "{|}~".
|
||||
* "{lead}?1;2c".
|
||||
*
|
||||
* - Cursor position report: <Esc>[{row};{col}R
|
||||
* - Cursor position report: {lead}{row};{col}R
|
||||
* The final byte must be 'R'. It is used for checking the
|
||||
* ambiguous-width character state.
|
||||
*
|
||||
* - window position reply: <Esc>[3;{x};{y}t
|
||||
* - window position reply: {lead}3;{x};{y}t
|
||||
*
|
||||
* - key with modifiers when modifyOtherKeys is enabled:
|
||||
* {lead}27;{modifier};{key}~
|
||||
* {lead}{key};{modifier}u
|
||||
*/
|
||||
char_u *argp = tp[0] == ESC ? tp + 2 : tp + 1;
|
||||
|
||||
if ((*T_CRV != NUL || *T_U7 != NUL || did_request_winpos)
|
||||
&& ((tp[0] == ESC && len >= 3 && tp[1] == '[')
|
||||
if (((tp[0] == ESC && len >= 3 && tp[1] == '[')
|
||||
|| (tp[0] == CSI && len >= 2))
|
||||
&& (VIM_ISDIGIT(*argp) || *argp == '>' || *argp == '?'))
|
||||
&& (VIM_ISDIGIT(*argp) || *argp == '>' || *argp == '?'))
|
||||
{
|
||||
int col = 0;
|
||||
int semicols = 0;
|
||||
int row_char = NUL;
|
||||
int first = -1; // optional char right after {lead}
|
||||
int trail; // char that ends CSI sequence
|
||||
int arg[3] = {-1, -1, -1}; // argument numbers
|
||||
int argc; // number of arguments
|
||||
char_u *ap = argp;
|
||||
int csi_len;
|
||||
|
||||
extra = 0;
|
||||
for (i = 2 + (tp[0] != CSI); i < len
|
||||
&& !(tp[i] >= '{' && tp[i] <= '~')
|
||||
&& !ASCII_ISALPHA(tp[i]); ++i)
|
||||
if (tp[i] == ';' && ++semicols == 1)
|
||||
// Check for non-digit after CSI.
|
||||
if (!VIM_ISDIGIT(*ap))
|
||||
first = *ap++;
|
||||
|
||||
// Find up to three argument numbers.
|
||||
for (argc = 0; argc < 3; )
|
||||
{
|
||||
if (ap >= tp + len)
|
||||
{
|
||||
extra = i + 1;
|
||||
row_char = tp[i - 1];
|
||||
not_enough:
|
||||
LOG_TR(("Not enough characters for CSI sequence"));
|
||||
return -1;
|
||||
}
|
||||
if (i == len)
|
||||
{
|
||||
LOG_TR(("Not enough characters for CRV"));
|
||||
return -1;
|
||||
if (*ap == ';')
|
||||
arg[argc++] = -1; // omitted number
|
||||
else if (VIM_ISDIGIT(*ap))
|
||||
{
|
||||
arg[argc] = 0;
|
||||
for (;;)
|
||||
{
|
||||
if (ap >= tp + len)
|
||||
goto not_enough;
|
||||
if (!VIM_ISDIGIT(*ap))
|
||||
break;
|
||||
arg[argc] = arg[argc] * 10 + (*ap - '0');
|
||||
++ap;
|
||||
}
|
||||
++argc;
|
||||
}
|
||||
if (*ap == ';')
|
||||
++ap;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (extra > 0)
|
||||
col = atoi((char *)tp + extra);
|
||||
// mrxvt has been reported to have "+" in the version. Assume
|
||||
// the escape sequence ends with a letter or one of "{|}~".
|
||||
while (ap < tp + len
|
||||
&& !(*ap >= '{' && *ap <= '~')
|
||||
&& !ASCII_ISALPHA(*ap))
|
||||
++ap;
|
||||
if (ap >= tp + len)
|
||||
goto not_enough;
|
||||
trail = *ap;
|
||||
csi_len = (int)(ap - tp) + 1;
|
||||
|
||||
/* Eat it when it has 2 arguments and ends in 'R'. Also when
|
||||
* u7_status is not "sent", it may be from a previous Vim that
|
||||
* just exited. But not for <S-F3>, it sends something
|
||||
* similar, check for row and column to make sense. */
|
||||
if (semicols == 1 && tp[i] == 'R')
|
||||
// Cursor position report: Eat it when there are 2 arguments
|
||||
// and it ends in 'R'. Also when u7_status is not "sent", it
|
||||
// may be from a previous Vim that just exited. But not for
|
||||
// <S-F3>, it sends something similar, check for row and column
|
||||
// to make sense.
|
||||
if (first == -1 && argc == 2 && trail == 'R')
|
||||
{
|
||||
if (row_char == '2' && col >= 2)
|
||||
if (arg[0] == 2 && arg[1] >= 2)
|
||||
{
|
||||
char *aw = NULL;
|
||||
|
||||
LOG_TR(("Received U7 status: %s", tp));
|
||||
u7_status.tr_progress = STATUS_GOT;
|
||||
did_cursorhold = TRUE;
|
||||
if (col == 2)
|
||||
if (arg[1] == 2)
|
||||
aw = "single";
|
||||
else if (col == 3)
|
||||
else if (arg[1] == 3)
|
||||
aw = "double";
|
||||
if (aw != NULL && STRCMP(aw, p_ambw) != 0)
|
||||
{
|
||||
/* Setting the option causes a screen redraw. Do
|
||||
* that right away if possible, keeping any
|
||||
* messages. */
|
||||
// Setting the option causes a screen redraw. Do
|
||||
// that right away if possible, keeping any
|
||||
// messages.
|
||||
set_option_value((char_u *)"ambw", 0L,
|
||||
(char_u *)aw, 0);
|
||||
# ifdef DEBUG_TERMRESPONSE
|
||||
@ -4516,34 +4549,35 @@ check_termcode(
|
||||
}
|
||||
key_name[0] = (int)KS_EXTRA;
|
||||
key_name[1] = (int)KE_IGNORE;
|
||||
slen = i + 1;
|
||||
slen = csi_len;
|
||||
# ifdef FEAT_EVAL
|
||||
set_vim_var_string(VV_TERMU7RESP, tp, slen);
|
||||
# endif
|
||||
}
|
||||
/* eat it when at least one digit and ending in 'c' */
|
||||
else if (*T_CRV != NUL && i > 2 + (tp[0] != CSI)
|
||||
&& tp[i] == 'c')
|
||||
|
||||
// Version string: Eat it when there is at least one digit and
|
||||
// it ends in 'c'
|
||||
else if (*T_CRV != NUL && ap > argp + 1 && trail == 'c')
|
||||
{
|
||||
int version = col;
|
||||
int version = arg[1];
|
||||
|
||||
LOG_TR(("Received CRV response: %s", tp));
|
||||
crv_status.tr_progress = STATUS_GOT;
|
||||
did_cursorhold = TRUE;
|
||||
|
||||
/* If this code starts with CSI, you can bet that the
|
||||
* terminal uses 8-bit codes. */
|
||||
// If this code starts with CSI, you can bet that the
|
||||
// terminal uses 8-bit codes.
|
||||
if (tp[0] == CSI)
|
||||
switch_to_8bit();
|
||||
|
||||
/* rxvt sends its version number: "20703" is 2.7.3.
|
||||
* Screen sends 40500.
|
||||
* Ignore it for when the user has set 'term' to xterm,
|
||||
* even though it's an rxvt. */
|
||||
// rxvt sends its version number: "20703" is 2.7.3.
|
||||
// Screen sends 40500.
|
||||
// Ignore it for when the user has set 'term' to xterm,
|
||||
// even though it's an rxvt.
|
||||
if (version > 20000)
|
||||
version = 0;
|
||||
|
||||
if (tp[1 + (tp[0] != CSI)] == '>' && semicols == 2)
|
||||
if (first == '>' && argc == 3)
|
||||
{
|
||||
int need_flush = FALSE;
|
||||
int is_iterm2 = FALSE;
|
||||
@ -4551,10 +4585,10 @@ check_termcode(
|
||||
|
||||
// mintty 2.9.5 sends 77;20905;0c.
|
||||
// (77 is ASCII 'M' for mintty.)
|
||||
if (STRNCMP(tp + extra - 3, "77;", 3) == 0)
|
||||
if (arg[0] == 77)
|
||||
is_mintty = TRUE;
|
||||
|
||||
/* if xterm version >= 141 try to get termcap codes */
|
||||
// if xterm version >= 141 try to get termcap codes
|
||||
if (version >= 141)
|
||||
{
|
||||
LOG_TR(("Enable checking for XT codes"));
|
||||
@ -4563,13 +4597,12 @@ check_termcode(
|
||||
req_codes_from_term();
|
||||
}
|
||||
|
||||
/* libvterm sends 0;100;0 */
|
||||
if (version == 100
|
||||
&& STRNCMP(tp + extra - 2, "0;100;0c", 8) == 0)
|
||||
// libvterm sends 0;100;0
|
||||
if (version == 100 && arg[0] == 0 && arg[2] == 0)
|
||||
{
|
||||
/* If run from Vim $COLORS is set to the number of
|
||||
* colors the terminal supports. Otherwise assume
|
||||
* 256, libvterm supports even more. */
|
||||
// If run from Vim $COLORS is set to the number of
|
||||
// colors the terminal supports. Otherwise assume
|
||||
// 256, libvterm supports even more.
|
||||
if (mch_getenv((char_u *)"COLORS") == NULL)
|
||||
may_adjust_color_count(256);
|
||||
/* Libvterm can handle SGR mouse reporting. */
|
||||
@ -4581,56 +4614,54 @@ check_termcode(
|
||||
if (version == 95)
|
||||
{
|
||||
// Mac Terminal.app sends 1;95;0
|
||||
if (STRNCMP(tp + extra - 2, "1;95;0c", 7) == 0)
|
||||
if (arg[0] == 1 && arg[2] == 0)
|
||||
{
|
||||
is_not_xterm = TRUE;
|
||||
is_mac_terminal = TRUE;
|
||||
}
|
||||
// iTerm2 sends 0;95;0
|
||||
if (STRNCMP(tp + extra - 2, "0;95;0c", 7) == 0)
|
||||
else if (arg[0] == 0 && arg[2] == 0)
|
||||
is_iterm2 = TRUE;
|
||||
// old iTerm2 sends 0;95;
|
||||
else if (STRNCMP(tp + extra - 2, "0;95;c", 6) == 0)
|
||||
else if (arg[0] == 0 && arg[2] == -1)
|
||||
is_not_xterm = TRUE;
|
||||
}
|
||||
|
||||
/* Only set 'ttymouse' automatically if it was not set
|
||||
* by the user already. */
|
||||
// Only set 'ttymouse' automatically if it was not set
|
||||
// by the user already.
|
||||
if (!option_was_set((char_u *)"ttym"))
|
||||
{
|
||||
/* Xterm version 277 supports SGR. Also support
|
||||
* Terminal.app, iTerm2 and mintty. */
|
||||
// Xterm version 277 supports SGR. Also support
|
||||
// Terminal.app, iTerm2 and mintty.
|
||||
if (version >= 277 || is_iterm2 || is_mac_terminal
|
||||
|| is_mintty)
|
||||
set_option_value((char_u *)"ttym", 0L,
|
||||
(char_u *)"sgr", 0);
|
||||
/* if xterm version >= 95 use mouse dragging */
|
||||
// if xterm version >= 95 use mouse dragging
|
||||
else if (version >= 95)
|
||||
set_option_value((char_u *)"ttym", 0L,
|
||||
(char_u *)"xterm2", 0);
|
||||
}
|
||||
|
||||
/* Detect terminals that set $TERM to something like
|
||||
* "xterm-256colors" but are not fully xterm
|
||||
* compatible. */
|
||||
// Detect terminals that set $TERM to something like
|
||||
// "xterm-256colors" but are not fully xterm
|
||||
// compatible.
|
||||
|
||||
/* Gnome terminal sends 1;3801;0, 1;4402;0 or 1;2501;0.
|
||||
* xfce4-terminal sends 1;2802;0.
|
||||
* screen sends 83;40500;0
|
||||
* Assuming any version number over 2500 is not an
|
||||
* xterm (without the limit for rxvt and screen). */
|
||||
if (col >= 2500)
|
||||
// Gnome terminal sends 1;3801;0, 1;4402;0 or 1;2501;0.
|
||||
// xfce4-terminal sends 1;2802;0.
|
||||
// screen sends 83;40500;0
|
||||
// Assuming any version number over 2500 is not an
|
||||
// xterm (without the limit for rxvt and screen).
|
||||
if (arg[1] >= 2500)
|
||||
is_not_xterm = TRUE;
|
||||
|
||||
/* PuTTY sends 0;136;0
|
||||
* vandyke SecureCRT sends 1;136;0 */
|
||||
if (version == 136
|
||||
&& STRNCMP(tp + extra - 1, ";136;0c", 7) == 0)
|
||||
// PuTTY sends 0;136;0
|
||||
// vandyke SecureCRT sends 1;136;0
|
||||
else if (version == 136 && arg[2] == 0)
|
||||
is_not_xterm = TRUE;
|
||||
|
||||
/* Konsole sends 0;115;0 */
|
||||
if (version == 115
|
||||
&& STRNCMP(tp + extra - 2, "0;115;0c", 8) == 0)
|
||||
// Konsole sends 0;115;0
|
||||
else if (version == 115 && arg[0] == 0 && arg[2] == 0)
|
||||
is_not_xterm = TRUE;
|
||||
|
||||
// Xterm first responded to this request at patch level
|
||||
@ -4638,11 +4669,11 @@ check_termcode(
|
||||
if (version < 95)
|
||||
is_not_xterm = TRUE;
|
||||
|
||||
/* Only request the cursor style if t_SH and t_RS are
|
||||
* set. Only supported properly by xterm since version
|
||||
* 279 (otherwise it returns 0x18).
|
||||
* Not for Terminal.app, it can't handle t_RS, it
|
||||
* echoes the characters to the screen. */
|
||||
// Only request the cursor style if t_SH and t_RS are
|
||||
// set. Only supported properly by xterm since version
|
||||
// 279 (otherwise it returns 0x18).
|
||||
// Not for Terminal.app, it can't handle t_RS, it
|
||||
// echoes the characters to the screen.
|
||||
if (rcs_status.tr_progress == STATUS_GET
|
||||
&& version >= 279
|
||||
&& !is_not_xterm
|
||||
@ -4655,9 +4686,9 @@ check_termcode(
|
||||
need_flush = TRUE;
|
||||
}
|
||||
|
||||
/* Only request the cursor blink mode if t_RC set. Not
|
||||
* for Gnome terminal, it can't handle t_RC, it
|
||||
* echoes the characters to the screen. */
|
||||
// Only request the cursor blink mode if t_RC set. Not
|
||||
// for Gnome terminal, it can't handle t_RC, it
|
||||
// echoes the characters to the screen.
|
||||
if (rbm_status.tr_progress == STATUS_GET
|
||||
&& !is_not_xterm
|
||||
&& *T_CRC != NUL)
|
||||
@ -4671,7 +4702,7 @@ check_termcode(
|
||||
if (need_flush)
|
||||
out_flush();
|
||||
}
|
||||
slen = i + 1;
|
||||
slen = csi_len;
|
||||
# ifdef FEAT_EVAL
|
||||
set_vim_var_string(VV_TERMRESPONSE, tp, slen);
|
||||
# endif
|
||||
@ -4681,69 +4712,51 @@ check_termcode(
|
||||
key_name[1] = (int)KE_IGNORE;
|
||||
}
|
||||
|
||||
/* Check blinking cursor from xterm:
|
||||
* {lead}?12;1$y set
|
||||
* {lead}?12;2$y not set
|
||||
*
|
||||
* {lead} can be <Esc>[ or CSI
|
||||
*/
|
||||
// Check blinking cursor from xterm:
|
||||
// {lead}?12;1$y set
|
||||
// {lead}?12;2$y not set
|
||||
//
|
||||
// {lead} can be <Esc>[ or CSI
|
||||
else if (rbm_status.tr_progress == STATUS_SENT
|
||||
&& tp[(j = 1 + (tp[0] == ESC))] == '?'
|
||||
&& i == j + 6
|
||||
&& tp[j + 1] == '1'
|
||||
&& tp[j + 2] == '2'
|
||||
&& tp[j + 3] == ';'
|
||||
&& tp[i - 1] == '$'
|
||||
&& tp[i] == 'y')
|
||||
&& first == '?'
|
||||
&& ap == argp + 6
|
||||
&& arg[0] == 12
|
||||
&& ap[-1] == '$'
|
||||
&& trail == 'y')
|
||||
{
|
||||
initial_cursor_blink = (tp[j + 4] == '1');
|
||||
initial_cursor_blink = (arg[1] == '1');
|
||||
rbm_status.tr_progress = STATUS_GOT;
|
||||
LOG_TR(("Received cursor blinking mode response: %s", tp));
|
||||
key_name[0] = (int)KS_EXTRA;
|
||||
key_name[1] = (int)KE_IGNORE;
|
||||
slen = i + 1;
|
||||
slen = csi_len;
|
||||
# ifdef FEAT_EVAL
|
||||
set_vim_var_string(VV_TERMBLINKRESP, tp, slen);
|
||||
# endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for a window position response from the terminal:
|
||||
* {lead}3;{x};{y}t
|
||||
*/
|
||||
else if (did_request_winpos
|
||||
&& ((len >= 4 && tp[0] == ESC && tp[1] == '[')
|
||||
|| (len >= 3 && tp[0] == CSI))
|
||||
&& tp[(j = 1 + (tp[0] == ESC))] == '3'
|
||||
&& tp[j + 1] == ';')
|
||||
// Check for a window position response from the terminal:
|
||||
// {lead}3;{x};{y}t
|
||||
else if (did_request_winpos && argc == 3 && arg[0] == 3
|
||||
&& trail == 't')
|
||||
{
|
||||
j += 2;
|
||||
for (i = j; i < len && vim_isdigit(tp[i]); ++i)
|
||||
;
|
||||
if (i < len && tp[i] == ';')
|
||||
{
|
||||
winpos_x = atoi((char *)tp + j);
|
||||
j = i + 1;
|
||||
for (i = j; i < len && vim_isdigit(tp[i]); ++i)
|
||||
;
|
||||
if (i < len && tp[i] == 't')
|
||||
{
|
||||
winpos_y = atoi((char *)tp + j);
|
||||
/* got finished code: consume it */
|
||||
key_name[0] = (int)KS_EXTRA;
|
||||
key_name[1] = (int)KE_IGNORE;
|
||||
slen = i + 1;
|
||||
winpos_x = arg[1];
|
||||
winpos_y = arg[2];
|
||||
// got finished code: consume it
|
||||
key_name[0] = (int)KS_EXTRA;
|
||||
key_name[1] = (int)KE_IGNORE;
|
||||
slen = csi_len;
|
||||
|
||||
if (--did_request_winpos <= 0)
|
||||
winpos_status.tr_progress = STATUS_GOT;
|
||||
}
|
||||
}
|
||||
if (i == len)
|
||||
{
|
||||
LOG_TR(("not enough characters for winpos"));
|
||||
return -1;
|
||||
}
|
||||
if (--did_request_winpos <= 0)
|
||||
winpos_status.tr_progress = STATUS_GOT;
|
||||
}
|
||||
|
||||
// TODO: key with modifier:
|
||||
// {lead}27;{modifier};{key}~
|
||||
// {lead}{key};{modifier}u
|
||||
|
||||
// else: Unknown CSI sequence. We could drop it, but then the
|
||||
// user can't create a map for it.
|
||||
}
|
||||
|
||||
/* Check for fore/background color response from the terminal:
|
||||
|
@ -753,6 +753,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
2123,
|
||||
/**/
|
||||
2122,
|
||||
/**/
|
||||
|
Loading…
x
Reference in New Issue
Block a user