summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-10-08 20:15:39 +0200
committerBram Moolenaar <Bram@vim.org>2019-10-08 20:15:39 +0200
commitc3e555b22f24f93aabd31943c35a9228abb6ecb6 (patch)
tree6de25dc7c62410ab48a795b7515186085d5df0fa
parentfbbd102be0f017b316f483893a95e4e78c286c9b (diff)
downloadvim-git-c3e555b22f24f93aabd31943c35a9228abb6ecb6.tar.gz
patch 8.1.2123: parsing CSI sequence is messyv8.1.2123
Problem: Parsing CSI sequence is messy. Solution: Generalize parsing a CSI sequence.
-rw-r--r--src/term.c303
-rw-r--r--src/version.c2
2 files changed, 160 insertions, 145 deletions
diff --git a/src/term.c b/src/term.c
index 2319453fb..f021a17ff 100644
--- a/src/term.c
+++ b/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;
-
- extra = 0;
- for (i = 2 + (tp[0] != CSI); i < len
- && !(tp[i] >= '{' && tp[i] <= '~')
- && !ASCII_ISALPHA(tp[i]); ++i)
- if (tp[i] == ';' && ++semicols == 1)
+ 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;
+
+ // 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);
-
- /* 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')
+ // 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;
+
+ // 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:
diff --git a/src/version.c b/src/version.c
index e207a3710..f07d0382c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2123,
+/**/
2122,
/**/
2121,