cmd: bootmenu: permit to select bootmenu entry with a shortcut

Permit to select a bootmenu entry with a key shortcut. This is
especially useful in production or testing scenario to automate flashing
procedure or testing procedure.

The boot entry are changed to append the shortcut key to it.

Example:
      1. Run default boot command.
      2. Boot system via TFTP.
      3. Boot production system from NAND.
      4. Boot recovery system from NAND.
      5. Load production system via TFTP then write to NAND.
      6. Load recovery system via TFTP then write to NAND.
      7. Load BL31+U-Boot FIP via TFTP then write to NAND.
      8. Load BL2 preloader via TFTP then write to NAND.
      9. Reboot.
      a. Reset all settings to factory defaults.
      0. Exit

0 is always reserved for Exit to console.
On pressing the keyboard key 2, the bootmenu entry 2 is selected and
executed.

Up to 34 key shortcut (0 excluded as reserved) are supported from 1-9
and a-z.
If a shortcut key not present in the bootmenu list is pressed, it is
simply ignored and eventually the autoboot is interrupted.

Capital A-Z are converted to lower a-z and the related option is
selected.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Tested-by: Petr Štetiar <ynezz@true.cz>
diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index a5c9790..d310877 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -114,6 +114,14 @@
 				++menu->active;
 			/* no menu key selected, regenerate menu */
 			return NULL;
+		case BKEY_SHORTCUT:
+			/* invalid shortcut, regenerate menu */
+			if (cch->shortcut_key >= menu->count - 1)
+				return NULL;
+			/* shortcut_key value for Exit is is -1 */
+			menu->active = cch->shortcut_key < 0 ? menu->count - 1 :
+							       cch->shortcut_key;
+			fallthrough;
 		case BKEY_SELECT:
 			iter = menu->first;
 			for (i = 0; i < menu->active; ++i)
@@ -161,6 +169,21 @@
 	free(menu);
 }
 
+static char bootmenu_entry_shortcut_key(int index)
+{
+	switch (index) {
+	/* 1-9 shortcut key (0 reserved) */
+	case 0 ... 8:
+		return '1' + index;
+	/* a-z shortcut key  */
+	case 9 ... 34:
+		return 'a' + index - 9;
+	/* We support shortcut for up to 34 options (0 reserved) */
+	default:
+		return -ENOENT;
+	}
+}
+
 /**
  * prepare_bootmenu_entry() - generate the bootmenu_xx entries
  *
@@ -184,6 +207,8 @@
 	struct bootmenu_entry *iter = *current;
 
 	while ((option = bootmenu_getoption(i))) {
+		char shortcut_key;
+		int len;
 
 		/* bootmenu_[num] format is "[title]=[commands]" */
 		sep = strchr(option, '=');
@@ -196,12 +221,22 @@
 		if (!entry)
 			return -ENOMEM;
 
-		entry->title = strndup(option, sep - option);
+		/* Add shotcut key option: %c. %s\0 */
+		len = sep - option + 4;
+
+		entry->title = malloc(len);
 		if (!entry->title) {
 			free(entry);
 			return -ENOMEM;
 		}
 
+		shortcut_key = bootmenu_entry_shortcut_key(i);
+		/* Use emtpy space if entry doesn't support shortcut key */
+		snprintf(entry->title, len, "%c%c %s",
+			 shortcut_key > 0 ? shortcut_key : ' ',
+			 shortcut_key > 0 ? '.' : ' ',
+			 option);
+
 		entry->command = strdup(sep + 1);
 		if (!entry->command) {
 			free(entry->title);
@@ -388,9 +423,9 @@
 
 		/* Add Quit entry if exiting bootmenu is disabled */
 		if (!IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE))
-			entry->title = strdup("Exit");
+			entry->title = strdup("0. Exit");
 		else
-			entry->title = strdup("Quit");
+			entry->title = strdup("0. Quit");
 
 		if (!entry->title) {
 			free(entry);