From 2d2b0929809de5278c1ecd66d138231b1084c1ad Mon Sep 17 00:00:00 2001
From: Benno Schulenberg <bensberg@telfort.nl>
Date: Sat, 20 Jan 2018 18:13:29 +0100
Subject: [PATCH] input: consume only the actual length of an escape sequence

Instead of assuming that the entire contents of the keybuffer are an
escape sequence, even if this is far longer than any escape sequence
can be.

This fixes https://savannah.gnu.org/bugs/?48659.
---
 src/winio.c | 42 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 34 insertions(+), 8 deletions(-)

diff --git a/src/winio.c b/src/winio.c
index 7ab42e7c..908a94d8 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -761,15 +761,17 @@ int parse_kbinput(WINDOW *win)
  * keypad values, into their corresponding key values.  These sequences
  * are generated when the keypad doesn't support the needed keys.
  * Assume that Escape has already been read in. */
-int convert_sequence(const int *seq, size_t length)
+int convert_sequence(const int *seq, size_t length, int *consumed)
 {
 	if (length > 1) {
+		*consumed = 2;
 		switch (seq[0]) {
 			case 'O':
 				switch (seq[1]) {
 					case '1':
 						if (length > 4  && seq[2] == ';') {
 
+		*consumed = 5;
 		switch (seq[3]) {
 			case '2':
 				switch (seq[4]) {
@@ -806,6 +808,7 @@ int convert_sequence(const int *seq, size_t length)
 						}
 						break;
 					case '2':
+						*consumed = 4;
 						if (length >= 3) {
 							switch (seq[2]) {
 								case 'P': /* Esc O 2 P == F13 on xterm. */
@@ -820,6 +823,7 @@ int convert_sequence(const int *seq, size_t length)
 						}
 						break;
 					case '5':
+						*consumed = 4;
 						if (length >= 3) {
 							switch (seq[2]) {
 								case 'A': /* Esc O 5 A == Ctrl-Up on Haiku. */
@@ -832,6 +836,7 @@ int convert_sequence(const int *seq, size_t length)
 									return CONTROL_LEFT;
 							}
 						}
+						break;
 					case 'A': /* Esc O A == Up on VT100/VT320/xterm. */
 					case 'B': /* Esc O B == Down on VT100/VT320/xterm. */
 					case 'C': /* Esc O C == Right on VT100/VT320/xterm. */
@@ -934,6 +939,7 @@ int convert_sequence(const int *seq, size_t length)
 				switch (seq[1]) {
 					case '1':
 						if (length > 3 && seq[3] == '~') {
+							*consumed = 4;
 							switch (seq[2]) {
 								case '1': /* Esc [ 1 1 ~ == F1 on rxvt/Eterm. */
 									return KEY_F(1);
@@ -955,6 +961,7 @@ int convert_sequence(const int *seq, size_t length)
 							}
 						} else if (length > 4 && seq[2] == ';') {
 
+		*consumed = 5;
 		switch (seq[3]) {
 			case '2':
 				switch (seq[4]) {
@@ -1031,12 +1038,15 @@ int convert_sequence(const int *seq, size_t length)
 #endif
 		}
 
-						} else if (length > 2 && seq[2] == '~')
+						} else if (length > 2 && seq[2] == '~') {
+							*consumed = 3;
 							/* Esc [ 1 ~ == Home on VT320/Linux console. */
 							return KEY_HOME;
+						}
 						break;
 					case '2':
 						if (length > 3 && seq[3] == '~') {
+							*consumed = 4;
 							switch (seq[2]) {
 								case '0': /* Esc [ 2 0 ~ == F9 on VT220/VT320/
 										   * Linux console/xterm/rxvt/Eterm. */
@@ -1064,23 +1074,27 @@ int convert_sequence(const int *seq, size_t length)
 						break;
 					case '3': /* Esc [ 3 ~ == Delete on VT220/VT320/
 							   * Linux console/xterm/Terminal. */
+						*consumed = 3;
 						if (length > 2 && seq[2] == '~')
 							return KEY_DC;
 						break;
 					case '4': /* Esc [ 4 ~ == End on VT220/VT320/
 							   * Linux console/xterm. */
+						*consumed = 3;
 						if (length > 2 && seq[2] == '~')
 							return KEY_END;
 						break;
 					case '5': /* Esc [ 5 ~ == PageUp on VT220/VT320/
 							   * Linux console/xterm/Terminal;
 							   * Esc [ 5 ^ == PageUp on Eterm. */
+						*consumed = 3;
 						if (length > 2 && (seq[2] == '~' || seq[2] == '^'))
 							return KEY_PPAGE;
 						break;
 					case '6': /* Esc [ 6 ~ == PageDown on VT220/VT320/
 							   * Linux console/xterm/Terminal;
 							   * Esc [ 6 ^ == PageDown on Eterm. */
+						*consumed = 3;
 						if (length > 2 && (seq[2] == '~' || seq[2] == '^'))
 							return KEY_NPAGE;
 						break;
@@ -1088,6 +1102,7 @@ int convert_sequence(const int *seq, size_t length)
 							   * Esc [ 7 $ == Shift-Home on Eterm/rxvt;
 							   * Esc [ 7 ^ == Control-Home on Eterm/rxvt;
 							   * Esc [ 7 @ == Shift-Control-Home on same. */
+						*consumed = 3;
 						if (length > 2 && seq[2] == '~')
 							return KEY_HOME;
 						else if (length > 2 && seq[2] == '$')
@@ -1103,6 +1118,7 @@ int convert_sequence(const int *seq, size_t length)
 							   * Esc [ 8 $ == Shift-End on Eterm/rxvt;
 							   * Esc [ 8 ^ == Control-End on Eterm/rxvt;
 							   * Esc [ 8 @ == Shift-Control-End on same. */
+						*consumed = 3;
 						if (length > 2 && seq[2] == '~')
 							return KEY_END;
 						else if (length > 2 && seq[2] == '$')
@@ -1144,6 +1160,7 @@ int convert_sequence(const int *seq, size_t length)
 						return KEY_F(2);
 					case 'O':
 						if (length > 2) {
+							*consumed = 3;
 							switch (seq[2]) {
 								case 'P': /* Esc [ O P == F1 on xterm. */
 									return KEY_F(1);
@@ -1189,6 +1206,7 @@ int convert_sequence(const int *seq, size_t length)
 						return arrow_from_abcd(seq[1]);
 					case '[':
 						if (length > 2) {
+							*consumed = 3;
 							switch (seq[2]) {
 								case 'A': /* Esc [ [ A == F1 on Linux console. */
 									return KEY_F(1);
@@ -1234,15 +1252,23 @@ int arrow_from_abcd(int kbinput)
  * isn't empty, and that the initial escape has already been read in. */
 int parse_escape_sequence(WINDOW *win, int kbinput)
 {
-	int retval, *sequence, length;
+	int retval, *sequence, length, consumed;
 
-	/* Put back the non-escape character, get the complete escape
-	 * sequence, translate the sequence into its corresponding key
-	 * value, and save that as the result. */
+	/* Put back the non-escape code, then grab at most five integers
+	 * (the longest possible escape sequence) from the keybuffer and
+	 * translate the sequence into its corresponding keycode. */
 	unget_input(kbinput);
-	length = key_buffer_len;
+	length = (key_buffer_len < 5 ? key_buffer_len : 5);
 	sequence = get_input(NULL, length);
-	retval = convert_sequence(sequence, length);
+	retval = convert_sequence(sequence, length, &consumed);
+
+	if (length < consumed)
+		fprintf(stderr, "Overconsumed keycodes -- please report a bug.\n");
+
+	/* If not all grabbed integers were consumed, put the leftovers back. */
+	for (int i = length - consumed; i > 0; i--)
+		unget_input(sequence[consumed - 1 + i]);
+
 	free(sequence);
 
 	/* If we got an unrecognized escape sequence, notify the user. */
-- 
2.14.3

