efi_loader: correct input of special keys

Don't set unicode_char if scan_code is set.
Add support for page up, page down, and insert.
Correct input of function keys.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
index 3cb580e..d0e8617 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -361,6 +361,48 @@
 	return EFI_EXIT(EFI_UNSUPPORTED);
 }
 
+/*
+ * Analyze modifiers (shift, alt, ctrl) for function keys.
+ * This gets called when we have already parsed CSI.
+ *
+ * @modifiers:  bitmask (shift, alt, ctrl)
+ * @return:	the unmodified code
+ */
+static char skip_modifiers(int *modifiers)
+{
+	char c, mod = 0, ret = 0;
+
+	c = getc();
+
+	if (c != ';') {
+		ret = c;
+		if (c == '~')
+			goto out;
+		c = getc();
+	}
+	for (;;) {
+		switch (c) {
+		case '0'...'9':
+			mod *= 10;
+			mod += c - '0';
+		/* fall through */
+		case ';':
+			c = getc();
+			break;
+		default:
+			goto out;
+		}
+	}
+out:
+	if (mod)
+		--mod;
+	if (modifiers)
+		*modifiers = mod;
+	if (!ret)
+		ret = c;
+	return ret;
+}
+
 static efi_status_t EFIAPI efi_cin_read_key_stroke(
 			struct efi_simple_input_interface *this,
 			struct efi_input_key *key)
@@ -383,14 +425,21 @@
 
 	ch = getc();
 	if (ch == cESC) {
-		/* Escape Sequence */
+		/*
+		 * Xterm Control Sequences
+		 * https://www.xfree86.org/4.8.0/ctlseqs.html
+		 */
 		ch = getc();
 		switch (ch) {
 		case cESC: /* ESC */
 			pressed_key.scan_code = 23;
 			break;
 		case 'O': /* F1 - F4 */
-			pressed_key.scan_code = getc() - 'P' + 11;
+			ch = getc();
+			/* skip modifiers */
+			if (ch <= '9')
+				ch = getc();
+			pressed_key.scan_code = ch - 'P' + 11;
 			break;
 		case 'a'...'z':
 			ch = ch - 'a';
@@ -407,17 +456,51 @@
 			case 'H': /* Home */
 				pressed_key.scan_code = 5;
 				break;
-			case '1': /* F5 - F8 */
-				pressed_key.scan_code = getc() - '0' + 11;
-				getc();
+			case '1':
+				ch = skip_modifiers(NULL);
+				switch (ch) {
+				case '1'...'5': /* F1 - F5 */
+					pressed_key.scan_code = ch - '1' + 11;
+					break;
+				case '7'...'9': /* F6 - F8 */
+					pressed_key.scan_code = ch - '7' + 16;
+					break;
+				case 'A'...'D': /* up, down right, left */
+					pressed_key.scan_code = ch - 'A' + 1;
+					break;
+				case 'F':
+					pressed_key.scan_code = 6; /* End */
+					break;
+				case 'H':
+					pressed_key.scan_code = 5; /* Home */
+					break;
+				}
 				break;
-			case '2': /* F9 - F12 */
-				pressed_key.scan_code = getc() - '0' + 19;
-				getc();
+			case '2':
+				ch = skip_modifiers(NULL);
+				switch (ch) {
+				case '0'...'1': /* F9 - F10 */
+					pressed_key.scan_code = ch - '0' + 19;
+					break;
+				case '3'...'4': /* F11 - F12 */
+					pressed_key.scan_code = ch - '3' + 21;
+					break;
+				case '~': /* INS */
+					pressed_key.scan_code = 7;
+					break;
+				}
 				break;
 			case '3': /* DEL */
 				pressed_key.scan_code = 8;
-				getc();
+				skip_modifiers(NULL);
+				break;
+			case '5': /* PG UP */
+				pressed_key.scan_code = 9;
+				skip_modifiers(NULL);
+				break;
+			case '6': /* PG DOWN */
+				pressed_key.scan_code = 10;
+				skip_modifiers(NULL);
 				break;
 			}
 			break;
@@ -426,7 +509,8 @@
 		/* Backspace */
 		ch = 0x08;
 	}
-	pressed_key.unicode_char = ch;
+	if (!pressed_key.scan_code)
+		pressed_key.unicode_char = ch;
 	*key = pressed_key;
 
 	return EFI_EXIT(EFI_SUCCESS);