Merge branch '2988_ignore_unknown_esc_sequences'

* 2988_ignore_unknown_esc_sequences:
  When we see an unknown sequence, it is not enough to drop already received part - there can be more of it coming over e.g. a serial line.
  This change prevents misinterpreting an unknown ESC sequence's tail as a garbage input. To reproduce, run "sleep 3" and hold down Down_Arrow key until sleep runs. With debugging log enabled, the following can be seen:
  Ticket #2988: When an unknown key is pressed, it is interpreted as garbage.
This commit is contained in:
Slava Zanko 2013-05-23 11:42:56 +03:00
commit 1df77c409e
1 changed files with 83 additions and 70 deletions

View File

@ -2,7 +2,7 @@
Keyboard support routines.
Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
2005, 2006, 2007, 2009, 2010, 2011
2005, 2006, 2007, 2009, 2010, 2011, 2013
The Free Software Foundation, Inc.
Written by:
@ -10,6 +10,8 @@
Janne Kukonlehto, 1994, 1995
Jakub Jelinek, 1995
Norbert Warmuth, 1997
Denys Vlasenko <vda.linux@googlemail.com>, 2013
Slava Zanko <slavazanko@gmail.com>, 2013
This file is part of the Midnight Commander.
@ -1129,14 +1131,14 @@ correct_key_code (int code)
/* --------------------------------------------------------------------------------------------- */
static int
xgetch_second (void)
getch_with_timeout (unsigned int delay_us)
{
fd_set Read_FD_Set;
int c;
struct timeval time_out;
time_out.tv_sec = old_esc_mode_timeout / 1000000;
time_out.tv_usec = old_esc_mode_timeout % 1000000;
time_out.tv_sec = delay_us / 1000000u;
time_out.tv_usec = delay_us % 1000000u;
tty_nodelay (TRUE);
FD_ZERO (&Read_FD_Set);
FD_SET (input_fd, &Read_FD_Set);
@ -1745,14 +1747,31 @@ get_key_code (int no_delay)
if (pending_keys != NULL)
{
int d;
gboolean bad_seq;
d = *pending_keys++;
while (d == ESC_CHAR && *pending_keys != '\0')
while (d == ESC_CHAR)
d = ALT (*pending_keys++);
if (*pending_keys == '\0')
bad_seq = (*pending_keys != ESC_CHAR && *pending_keys != 0);
if (*pending_keys == '\0' || bad_seq)
pending_keys = seq_append = NULL;
if (bad_seq)
{
/* This is an unknown ESC sequence.
* To prevent interpreting its tail as a random garbage,
* eat and discard all buffered and quickly following chars.
* Small, but non-zero timeout is needed to reconnect
* escape sequence split up by e.g. a serial line.
*/
int paranoia = 20;
while (getch_with_timeout (old_esc_mode_timeout) >= 0 && --paranoia != 0)
;
goto nodelay_try_again;
}
if (d > 127 && d < 256 && use_8th_bit_as_meta)
d = ALT (d & 0x7f);
@ -1828,49 +1847,12 @@ get_key_code (int no_delay)
this = keys->child;
}
}
while (this != NULL)
{
if (c == this->ch)
{
if (this->child)
{
if (!push_char (c))
{
pending_keys = seq_buffer;
goto pend_send;
}
parent = this;
this = this->child;
if (parent->action == MCKEY_ESCAPE && old_esc_mode)
{
if (no_delay)
{
GET_TIME (esctime);
if (this == NULL)
{
/* Shouldn't happen */
fputs ("Internal error\n", stderr);
exit (EXIT_FAILURE);
}
goto nodelay_try_again;
}
esctime.tv_sec = -1;
c = xgetch_second ();
if (c == -1)
{
pending_keys = seq_append = NULL;
this = NULL;
return ESC_CHAR;
}
}
else
{
if (no_delay)
goto nodelay_try_again;
c = tty_lowlevel_getch ();
}
}
else
if (this->child == NULL)
{
/* We got a complete match, return and reset search */
int code;
@ -1880,36 +1862,67 @@ get_key_code (int no_delay)
this = NULL;
return correct_key_code (code);
}
}
else
{
if (this->next != NULL)
this = this->next;
else
/* No match yet, but it may be a prefix for a valid seq */
if (!push_char (c))
{
if ((parent != NULL) && (parent->action == MCKEY_ESCAPE))
{
/* Convert escape-digits to F-keys */
if (g_ascii_isdigit (c))
c = KEY_F (c - '0');
else if (c == ' ')
c = ESC_CHAR;
else
c = ALT (c);
pending_keys = seq_append = NULL;
this = NULL;
return correct_key_code (c);
}
/* Did not find a match or {c} was changed in the if above,
so we have to return everything we had skipped
*/
push_char (c);
pending_keys = seq_buffer;
goto pend_send;
}
parent = this;
this = this->child;
if (parent->action == MCKEY_ESCAPE && old_esc_mode)
{
if (no_delay)
{
GET_TIME (esctime);
goto nodelay_try_again;
}
esctime.tv_sec = -1;
c = getch_with_timeout (old_esc_mode_timeout);
if (c == -1)
{
pending_keys = seq_append = NULL;
this = NULL;
return ESC_CHAR;
}
continue;
}
if (no_delay)
goto nodelay_try_again;
c = tty_lowlevel_getch ();
continue;
}
}
/* c != this->ch. Try other keys with this prefix */
if (this->next != NULL)
{
this = this->next;
continue;
}
/* No match found. Is it one of our ESC <key> specials? */
if ((parent != NULL) && (parent->action == MCKEY_ESCAPE))
{
/* Convert escape-digits to F-keys */
if (g_ascii_isdigit (c))
c = KEY_F (c - '0');
else if (c == ' ')
c = ESC_CHAR;
else
c = ALT (c);
pending_keys = seq_append = NULL;
this = NULL;
return correct_key_code (c);
}
/* Unknown sequence. Maybe a prefix of a longer one. Save it. */
push_char (c);
pending_keys = seq_buffer;
goto pend_send;
} /* while (this != NULL) */
this = NULL;
return correct_key_code (c);
}