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

View File

@ -2,7 +2,7 @@
Keyboard support routines. Keyboard support routines.
Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 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. The Free Software Foundation, Inc.
Written by: Written by:
@ -10,6 +10,8 @@
Janne Kukonlehto, 1994, 1995 Janne Kukonlehto, 1994, 1995
Jakub Jelinek, 1995 Jakub Jelinek, 1995
Norbert Warmuth, 1997 Norbert Warmuth, 1997
Denys Vlasenko <vda.linux@googlemail.com>, 2013
Slava Zanko <slavazanko@gmail.com>, 2013
This file is part of the Midnight Commander. This file is part of the Midnight Commander.
@ -1129,14 +1131,14 @@ correct_key_code (int code)
/* --------------------------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------------------------- */
static int static int
xgetch_second (void) getch_with_timeout (unsigned int delay_us)
{ {
fd_set Read_FD_Set; fd_set Read_FD_Set;
int c; int c;
struct timeval time_out; struct timeval time_out;
time_out.tv_sec = old_esc_mode_timeout / 1000000; time_out.tv_sec = delay_us / 1000000u;
time_out.tv_usec = old_esc_mode_timeout % 1000000; time_out.tv_usec = delay_us % 1000000u;
tty_nodelay (TRUE); tty_nodelay (TRUE);
FD_ZERO (&Read_FD_Set); FD_ZERO (&Read_FD_Set);
FD_SET (input_fd, &Read_FD_Set); FD_SET (input_fd, &Read_FD_Set);
@ -1745,14 +1747,31 @@ get_key_code (int no_delay)
if (pending_keys != NULL) if (pending_keys != NULL)
{ {
int d; int d;
gboolean bad_seq;
d = *pending_keys++; d = *pending_keys++;
while (d == ESC_CHAR && *pending_keys != '\0') while (d == ESC_CHAR)
d = ALT (*pending_keys++); 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; 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) if (d > 127 && d < 256 && use_8th_bit_as_meta)
d = ALT (d & 0x7f); d = ALT (d & 0x7f);
@ -1828,12 +1847,22 @@ get_key_code (int no_delay)
this = keys->child; this = keys->child;
} }
} }
while (this != NULL) while (this != NULL)
{ {
if (c == this->ch) if (c == this->ch)
{ {
if (this->child) if (this->child == NULL)
{ {
/* We got a complete match, return and reset search */
int code;
pending_keys = seq_append = NULL;
code = this->code;
this = NULL;
return correct_key_code (code);
}
/* No match yet, but it may be a prefix for a valid seq */
if (!push_char (c)) if (!push_char (c))
{ {
pending_keys = seq_buffer; pending_keys = seq_buffer;
@ -1846,47 +1875,32 @@ get_key_code (int no_delay)
if (no_delay) if (no_delay)
{ {
GET_TIME (esctime); GET_TIME (esctime);
if (this == NULL)
{
/* Shouldn't happen */
fputs ("Internal error\n", stderr);
exit (EXIT_FAILURE);
}
goto nodelay_try_again; goto nodelay_try_again;
} }
esctime.tv_sec = -1; esctime.tv_sec = -1;
c = xgetch_second (); c = getch_with_timeout (old_esc_mode_timeout);
if (c == -1) if (c == -1)
{ {
pending_keys = seq_append = NULL; pending_keys = seq_append = NULL;
this = NULL; this = NULL;
return ESC_CHAR; return ESC_CHAR;
} }
continue;
} }
else
{
if (no_delay) if (no_delay)
goto nodelay_try_again; goto nodelay_try_again;
c = tty_lowlevel_getch (); c = tty_lowlevel_getch ();
continue;
} }
}
else
{
/* We got a complete match, return and reset search */
int code;
pending_keys = seq_append = NULL; /* c != this->ch. Try other keys with this prefix */
code = this->code;
this = NULL;
return correct_key_code (code);
}
}
else
{
if (this->next != NULL) if (this->next != NULL)
this = this->next;
else
{ {
this = this->next;
continue;
}
/* No match found. Is it one of our ESC <key> specials? */
if ((parent != NULL) && (parent->action == MCKEY_ESCAPE)) if ((parent != NULL) && (parent->action == MCKEY_ESCAPE))
{ {
/* Convert escape-digits to F-keys */ /* Convert escape-digits to F-keys */
@ -1901,15 +1915,14 @@ get_key_code (int no_delay)
this = NULL; this = NULL;
return correct_key_code (c); 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 /* Unknown sequence. Maybe a prefix of a longer one. Save it. */
*/
push_char (c); push_char (c);
pending_keys = seq_buffer; pending_keys = seq_buffer;
goto pend_send; goto pend_send;
}
} } /* while (this != NULL) */
}
this = NULL; this = NULL;
return correct_key_code (c); return correct_key_code (c);
} }