[PATCH v4 3/7] eficonfig: add direct menu entry access mode

Masahisa Kojima masahisa.kojima at linaro.org
Mon Oct 24 06:48:00 CEST 2022


This commit adds the direct menu entry access mode.
User can select the menu entry by '&' key followed by
the menu title name.

User input is read in UTF-16, then UTF-16 string is converted
into UTF-8 internally because string comparison relies on strncasecmp().
There is no equivalent string comparison function for UTF-16.

Signed-off-by: Masahisa Kojima <masahisa.kojima at linaro.org>
---
Newly added in v4

 cmd/eficonfig.c      | 120 ++++++++++++++++++++++++++++++++++++++++++-
 common/menu.c        |   3 ++
 include/efi_config.h |   3 ++
 include/menu.h       |   1 +
 4 files changed, 126 insertions(+), 1 deletion(-)

diff --git a/cmd/eficonfig.c b/cmd/eficonfig.c
index 0cb0770ac3..56d9268f9f 100644
--- a/cmd/eficonfig.c
+++ b/cmd/eficonfig.c
@@ -22,6 +22,7 @@
 
 static struct efi_simple_text_input_protocol *cin;
 
+#define EFICONFIG_ACCESSOR_STR_MAX 16
 #define EFICONFIG_DESCRIPTION_MAX 32
 #define EFICONFIG_OPTIONAL_DATA_MAX 64
 
@@ -155,7 +156,28 @@ static void eficonfig_print_entry(void *data)
 	if (reverse)
 		puts(ANSI_COLOR_REVERSE);
 
-	printf("%s", entry->title);
+	if (reverse && entry->efi_menu->direct_access_mode) {
+		size_t len = u16_strlen(entry->efi_menu->accessor_str);
+		char *accessor_str, *p;
+
+		accessor_str = calloc(1, utf16_utf8_strlen(entry->efi_menu->accessor_str) + 1);
+		if (!accessor_str) {
+			printf("%s", entry->title);
+			return;
+		}
+		p = accessor_str;
+		utf16_utf8_strncpy(&p, entry->efi_menu->accessor_str, EFICONFIG_ACCESSOR_STR_MAX);
+		len = strlen(accessor_str);
+		if (!strncasecmp(accessor_str, entry->title, len)) {
+			printf("%.*s" ANSI_COLOR_RESET "%s", (int)len, entry->title,
+			       &entry->title[len]);
+		} else {
+			printf("%s", entry->title);
+		}
+		free(accessor_str);
+	} else {
+		printf("%s", entry->title);
+	}
 
 	if (reverse)
 		puts(ANSI_COLOR_RESET);
@@ -182,6 +204,83 @@ static void eficonfig_display_statusline(struct menu *m)
 	       entry->efi_menu->count + 6, 1, entry->efi_menu->count + 7, 1);
 }
 
+/**
+ * eficonfig_handle_direct_accessor() - handle direct access user input
+ *
+ * @efi_menu:	pointer to the efimenu structure
+ * Return:	key string to identify the selected entry
+ */
+static char *eficonfig_handle_direct_accessor(struct efimenu *efi_menu)
+{
+	efi_status_t ret;
+	char *accessor_str, *p;
+	struct efi_input_key key;
+	struct list_head *pos, *n;
+	struct eficonfig_entry *entry;
+	static int len;
+
+	/* Read user input */
+	do {
+		ret = EFI_CALL(cin->read_key_stroke(cin, &key));
+		mdelay(10);
+	} while (ret == EFI_NOT_READY);
+
+	/* If user presses Ctrl+C or ESC, exit direct access mode */
+	if (key.unicode_char == 0x3 || key.scan_code == 23)
+		goto out;
+
+	/* If user presses ENTER, exit direct access mode and return the active entry */
+	if (key.unicode_char == u'\r') {
+		list_for_each_safe(pos, n, &efi_menu->list) {
+			entry = list_entry(pos, struct eficonfig_entry, list);
+			if (entry->num == efi_menu->active) {
+				efi_menu->direct_access_mode = false;
+				memset(efi_menu->accessor_str, 0,
+				       EFICONFIG_ACCESSOR_STR_MAX * sizeof(u16));
+				return entry->key;
+			}
+		}
+
+		/* no matching entry */
+		goto out;
+	}
+
+	/* Ignore other control code and efi scan code */
+	if (key.unicode_char < 0x20 || key.scan_code != 0)
+		return NULL;
+
+	len = u16_strlen(efi_menu->accessor_str);
+	if (len < EFICONFIG_ACCESSOR_STR_MAX - 1)
+		efi_menu->accessor_str[len] = key.unicode_char;
+
+	accessor_str = calloc(1, utf16_utf8_strlen(efi_menu->accessor_str) + 1);
+	if (!accessor_str)
+		return NULL;
+
+	p = accessor_str;
+	utf16_utf8_strncpy(&p, efi_menu->accessor_str, EFICONFIG_ACCESSOR_STR_MAX);
+
+	list_for_each_safe(pos, n, &efi_menu->list) {
+		entry = list_entry(pos, struct eficonfig_entry, list);
+		if (!strncasecmp(accessor_str, entry->title, strlen(accessor_str))) {
+			efi_menu->active = entry->num;
+			free(accessor_str);
+			return NULL;
+		}
+	}
+
+	/* does not match any entries */
+	free(accessor_str);
+	efi_menu->active = 0;
+	return NULL;
+
+out:
+	efi_menu->direct_access_mode = false;
+	memset(efi_menu->accessor_str, 0, EFICONFIG_ACCESSOR_STR_MAX * sizeof(u16));
+	efi_menu->active = 0;
+	return NULL;
+}
+
 /**
  * eficonfig_choice_entry() - user key input handler
  *
@@ -196,6 +295,9 @@ static char *eficonfig_choice_entry(void *data)
 	enum bootmenu_key key = KEY_NONE;
 	struct efimenu *efi_menu = data;
 
+	if (efi_menu->direct_access_mode)
+		return eficonfig_handle_direct_accessor(efi_menu);
+
 	while (1) {
 		bootmenu_loop((struct bootmenu_data *)efi_menu, &key, &esc);
 
@@ -221,6 +323,10 @@ static char *eficonfig_choice_entry(void *data)
 			/* Quit by choosing the last entry */
 			entry = list_last_entry(&efi_menu->list, struct eficonfig_entry, list);
 			return entry->key;
+		case KEY_AMPERSAND:
+			memset(efi_menu->accessor_str, 0, EFICONFIG_ACCESSOR_STR_MAX * sizeof(u16));
+			efi_menu->direct_access_mode = true;
+			return NULL;
 		default:
 			/* Pressed key is not valid, no need to regenerate the menu */
 			break;
@@ -248,6 +354,7 @@ void eficonfig_destroy(struct efimenu *efi_menu)
 		free(entry);
 	}
 	free(efi_menu->menu_header);
+	free(efi_menu->accessor_str);
 	free(efi_menu);
 }
 
@@ -385,6 +492,9 @@ efi_status_t eficonfig_process_common(struct efimenu *efi_menu, char *menu_heade
 		if (!efi_menu->menu_header)
 			return EFI_OUT_OF_RESOURCES;
 	}
+	efi_menu->accessor_str = calloc(1, EFICONFIG_ACCESSOR_STR_MAX * sizeof(u16));
+	if (!efi_menu->accessor_str)
+		return EFI_OUT_OF_RESOURCES;
 
 	menu = menu_create(NULL, 0, 1, eficonfig_display_statusline,
 			   eficonfig_print_entry, eficonfig_choice_entry,
@@ -1866,6 +1976,11 @@ static efi_status_t eficonfig_choice_change_boot_order(struct efimenu *efi_menu)
 	enum bootmenu_key key = KEY_NONE;
 	struct eficonfig_boot_order *entry;
 
+	if (efi_menu->direct_access_mode) {
+		eficonfig_handle_direct_accessor(efi_menu);
+		return EFI_NOT_READY;
+	}
+
 	while (1) {
 		bootmenu_loop(NULL, &key, &esc);
 
@@ -1931,6 +2046,9 @@ static efi_status_t eficonfig_choice_change_boot_order(struct efimenu *efi_menu)
 			break;
 		case KEY_QUIT:
 			return EFI_ABORTED;
+		case KEY_AMPERSAND:
+			efi_menu->direct_access_mode = true;
+			return EFI_NOT_READY;
 		default:
 			/* Pressed key is not valid, no need to regenerate the menu */
 			break;
diff --git a/common/menu.c b/common/menu.c
index 8fe00965c0..6ea9f5c9b8 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -557,4 +557,7 @@ void bootmenu_loop(struct bootmenu_data *menu,
 
 	if (c == ' ')
 		*key = KEY_SPACE;
+
+	if (c == '&')
+		*key = KEY_AMPERSAND;
 }
diff --git a/include/efi_config.h b/include/efi_config.h
index 86bc801211..1b84e2d579 100644
--- a/include/efi_config.h
+++ b/include/efi_config.h
@@ -45,6 +45,7 @@ struct eficonfig_entry {
  * @active:		active menu entry index
  * @count:		total count of menu entry
  * @menu_header:	menu header string
+ * @accessor_str:	pointer to the accessor string for entry shortcut
  * @list:		menu entry list structure
  */
 struct efimenu {
@@ -52,6 +53,8 @@ struct efimenu {
 	int active;
 	int count;
 	char *menu_header;
+	bool direct_access_mode;
+	u16 *accessor_str;
 	struct list_head list;
 };
 
diff --git a/include/menu.h b/include/menu.h
index 702aacb170..03bf8dc4f5 100644
--- a/include/menu.h
+++ b/include/menu.h
@@ -51,6 +51,7 @@ enum bootmenu_key {
 	KEY_PLUS,
 	KEY_MINUS,
 	KEY_SPACE,
+	KEY_AMPERSAND,
 };
 
 void bootmenu_autoboot_loop(struct bootmenu_data *menu,
-- 
2.17.1



More information about the U-Boot mailing list