From 5719cde838b6e86a02831373dae81642653b872f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 20 Jun 2024 14:24:53 -0700 Subject: [dynui] Generalise the concept of a menu to a dynamic user interface We currently have an abstract model of a dynamic menu as a list of items, each of which has a name, a description, and assorted metadata such as a shortcut key. The "menu" and "item" commands construct representations in this abstract model, and the "choose" command then presents the items as a single-choice menu, with the selected item's name used as the output value. This same abstraction may be used to model a dynamic form as a list of editable items, each of which has a corresponding setting name, an optional description label, and assorted metadata such as a shortcut key. By defining a "form" command as an alias for the "menu" command, we could construct and present forms using commands such as: #!ipxe form Login to ${url} item username Username or email address item --secret password Password present or #!ipxe form Configure IPv4 networking for ${netX/ifname} item netX/ip IPv4 address item netX/netmask Subnet mask item netX/gateway Gateway address item netX/dns DNS server address present Reusing the same abstract model for both menus and forms allows us to minimise the increase in code size, since the implementation of the "form" and "item" commands is essentially zero-cost. Rename everything within the abstract data model from "menu" to "dynamic user interface" to reflect this generalisation. Signed-off-by: Michael Brown --- src/config/config.c | 2 +- src/core/dynui.c | 182 ++++++++++++++++++++++++++ src/core/menu.c | 182 -------------------------- src/core/parseopt.c | 18 +-- src/hci/commands/dynui_cmd.c | 295 +++++++++++++++++++++++++++++++++++++++++++ src/hci/commands/menu_cmd.c | 294 ------------------------------------------ src/hci/tui/menu_ui.c | 43 ++++--- src/include/ipxe/dynui.h | 50 ++++++++ src/include/ipxe/errfile.h | 2 +- src/include/ipxe/menu.h | 49 ------- src/include/ipxe/parseopt.h | 4 +- 11 files changed, 562 insertions(+), 559 deletions(-) create mode 100644 src/core/dynui.c delete mode 100644 src/core/menu.c create mode 100644 src/hci/commands/dynui_cmd.c delete mode 100644 src/hci/commands/menu_cmd.c create mode 100644 src/include/ipxe/dynui.h delete mode 100644 src/include/ipxe/menu.h diff --git a/src/config/config.c b/src/config/config.c index 209336c..6667123 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -225,7 +225,7 @@ REQUIRE_OBJECT ( dhcp_cmd ); REQUIRE_OBJECT ( sanboot_cmd ); #endif #ifdef MENU_CMD -REQUIRE_OBJECT ( menu_cmd ); +REQUIRE_OBJECT ( dynui_cmd ); #endif #ifdef LOGIN_CMD REQUIRE_OBJECT ( login_cmd ); diff --git a/src/core/dynui.c b/src/core/dynui.c new file mode 100644 index 0000000..6e053b9 --- /dev/null +++ b/src/core/dynui.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Dynamic user interfaces + * + */ + +#include +#include +#include +#include +#include + +/** List of all dynamic user interfaces */ +static LIST_HEAD ( dynamic_uis ); + +/** + * Create dynamic user interface + * + * @v name User interface name, or NULL + * @v title User interface title, or NULL + * @ret dynui Dynamic user interface, or NULL on failure + */ +struct dynamic_ui * create_dynui ( const char *name, const char *title ) { + struct dynamic_ui *dynui; + size_t name_len; + size_t title_len; + size_t len; + char *name_copy; + char *title_copy; + + /* Destroy any existing user interface of this name */ + dynui = find_dynui ( name ); + if ( dynui ) + destroy_dynui ( dynui ); + + /* Use empty title if none given */ + if ( ! title ) + title = ""; + + /* Allocate user interface */ + name_len = ( name ? ( strlen ( name ) + 1 /* NUL */ ) : 0 ); + title_len = ( strlen ( title ) + 1 /* NUL */ ); + len = ( sizeof ( *dynui ) + name_len + title_len ); + dynui = zalloc ( len ); + if ( ! dynui ) + return NULL; + name_copy = ( ( void * ) ( dynui + 1 ) ); + title_copy = ( name_copy + name_len ); + + /* Initialise user interface */ + if ( name ) { + strcpy ( name_copy, name ); + dynui->name = name_copy; + } + strcpy ( title_copy, title ); + dynui->title = title_copy; + INIT_LIST_HEAD ( &dynui->items ); + + /* Add to list of user interfaces */ + list_add_tail ( &dynui->list, &dynamic_uis ); + + DBGC ( dynui, "DYNUI %s created with title \"%s\"\n", + dynui->name, dynui->title ); + + return dynui; +} + +/** + * Add dynamic user interface item + * + * @v dynui Dynamic user interface + * @v name Name, or NULL + * @v text Text, or NULL + * @v shortcut Shortcut key + * @v is_default Item is the default item + * @ret item User interface item, or NULL on failure + */ +struct dynamic_item * add_dynui_item ( struct dynamic_ui *dynui, + const char *name, const char *text, + int shortcut, int is_default ) { + struct dynamic_item *item; + size_t name_len; + size_t text_len; + size_t len; + char *name_copy; + char *text_copy; + + /* Use empty text if none given */ + if ( ! text ) + text = ""; + + /* Allocate item */ + name_len = ( name ? ( strlen ( name ) + 1 /* NUL */ ) : 0 ); + text_len = ( strlen ( text ) + 1 /* NUL */ ); + len = ( sizeof ( *item ) + name_len + text_len ); + item = zalloc ( len ); + if ( ! item ) + return NULL; + name_copy = ( ( void * ) ( item + 1 ) ); + text_copy = ( name_copy + name_len ); + + /* Initialise item */ + if ( name ) { + strcpy ( name_copy, name ); + item->name = name_copy; + } + strcpy ( text_copy, text ); + item->text = text_copy; + item->shortcut = shortcut; + item->is_default = is_default; + + /* Add to list of items */ + list_add_tail ( &item->list, &dynui->items ); + + return item; +} + +/** + * Destroy dynamic user interface + * + * @v dynui Dynamic user interface + */ +void destroy_dynui ( struct dynamic_ui *dynui ) { + struct dynamic_item *item; + struct dynamic_item *tmp; + + /* Remove from list of user interfaces */ + list_del ( &dynui->list ); + + /* Free items */ + list_for_each_entry_safe ( item, tmp, &dynui->items, list ) { + list_del ( &item->list ); + free ( item ); + } + + /* Free user interface */ + free ( dynui ); +} + +/** + * Find dynamic user interface + * + * @v name User interface name, or NULL + * @ret dynui Dynamic user interface, or NULL if not found + */ +struct dynamic_ui * find_dynui ( const char *name ) { + struct dynamic_ui *dynui; + + list_for_each_entry ( dynui, &dynamic_uis, list ) { + if ( ( dynui->name == name ) || + ( strcmp ( dynui->name, name ) == 0 ) ) { + return dynui; + } + } + + return NULL; +} diff --git a/src/core/menu.c b/src/core/menu.c deleted file mode 100644 index b9d0d5b..0000000 --- a/src/core/menu.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown . - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** @file - * - * Menu selection - * - */ - -#include -#include -#include -#include -#include - -/** List of all menus */ -static LIST_HEAD ( menus ); - -/** - * Create menu - * - * @v name Menu name, or NULL - * @v title Menu title, or NULL - * @ret menu Menu, or NULL on failure - */ -struct menu * create_menu ( const char *name, const char *title ) { - size_t name_len; - size_t title_len; - size_t len; - struct menu *menu; - char *name_copy; - char *title_copy; - - /* Destroy any existing menu of this name */ - menu = find_menu ( name ); - if ( menu ) - destroy_menu ( menu ); - - /* Use empty title if none given */ - if ( ! title ) - title = ""; - - /* Allocate menu */ - name_len = ( name ? ( strlen ( name ) + 1 /* NUL */ ) : 0 ); - title_len = ( strlen ( title ) + 1 /* NUL */ ); - len = ( sizeof ( *menu ) + name_len + title_len ); - menu = zalloc ( len ); - if ( ! menu ) - return NULL; - name_copy = ( ( void * ) ( menu + 1 ) ); - title_copy = ( name_copy + name_len ); - - /* Initialise menu */ - if ( name ) { - strcpy ( name_copy, name ); - menu->name = name_copy; - } - strcpy ( title_copy, title ); - menu->title = title_copy; - INIT_LIST_HEAD ( &menu->items ); - - /* Add to list of menus */ - list_add_tail ( &menu->list, &menus ); - - DBGC ( menu, "MENU %s created with title \"%s\"\n", - menu->name, menu->title ); - - return menu; -} - -/** - * Add menu item - * - * @v menu Menu - * @v name Name, or NULL - * @v text Text, or NULL - * @v shortcut Shortcut key - * @v is_default Item is the default item - * @ret item Menu item, or NULL on failure - */ -struct menu_item * add_menu_item ( struct menu *menu, const char *name, - const char *text, int shortcut, - int is_default ) { - size_t name_len; - size_t text_len; - size_t len; - struct menu_item *item; - char *name_copy; - char *text_copy; - - /* Use empty text if none given */ - if ( ! text ) - text = ""; - - /* Allocate item */ - name_len = ( name ? ( strlen ( name ) + 1 /* NUL */ ) : 0 ); - text_len = ( strlen ( text ) + 1 /* NUL */ ); - len = ( sizeof ( *item ) + name_len + text_len ); - item = zalloc ( len ); - if ( ! item ) - return NULL; - name_copy = ( ( void * ) ( item + 1 ) ); - text_copy = ( name_copy + name_len ); - - /* Initialise item */ - if ( name ) { - strcpy ( name_copy, name ); - item->name = name_copy; - } - strcpy ( text_copy, text ); - item->text = text_copy; - item->shortcut = shortcut; - item->is_default = is_default; - - /* Add to list of items */ - list_add_tail ( &item->list, &menu->items ); - - return item; -} - -/** - * Destroy menu - * - * @v menu Menu - */ -void destroy_menu ( struct menu *menu ) { - struct menu_item *item; - struct menu_item *tmp; - - /* Remove from list of menus */ - list_del ( &menu->list ); - - /* Free items */ - list_for_each_entry_safe ( item, tmp, &menu->items, list ) { - list_del ( &item->list ); - free ( item ); - } - - /* Free menu */ - free ( menu ); -} - -/** - * Find menu - * - * @v name Menu name, or NULL - * @ret menu Menu, or NULL if not found - */ -struct menu * find_menu ( const char *name ) { - struct menu *menu; - - list_for_each_entry ( menu, &menus, list ) { - if ( ( menu->name == name ) || - ( strcmp ( menu->name, name ) == 0 ) ) { - return menu; - } - } - - return NULL; -} diff --git a/src/core/parseopt.c b/src/core/parseopt.c index 8410e6e..b657c3f 100644 --- a/src/core/parseopt.c +++ b/src/core/parseopt.c @@ -33,7 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include +#include #include #include #include @@ -194,21 +194,21 @@ int parse_netdev_configurator ( char *text, } /** - * Parse menu name + * Parse dynamic user interface name * * @v text Text - * @ret menu Menu + * @ret dynui Dynamic user interface * @ret rc Return status code */ -int parse_menu ( char *text, struct menu **menu ) { +int parse_dynui ( char *text, struct dynamic_ui **dynui ) { - /* Find menu */ - *menu = find_menu ( text ); - if ( ! *menu ) { + /* Find user interface */ + *dynui = find_dynui ( text ); + if ( ! *dynui ) { if ( text ) { - printf ( "\"%s\": no such menu\n", text ); + printf ( "\"%s\": no such user interface\n", text ); } else { - printf ( "No default menu\n" ); + printf ( "No default user interface\n" ); } return -ENOENT; } diff --git a/src/hci/commands/dynui_cmd.c b/src/hci/commands/dynui_cmd.c new file mode 100644 index 0000000..dbaa669 --- /dev/null +++ b/src/hci/commands/dynui_cmd.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Dynamic user interface commands + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_MISC, "Menu", DHCP_EB_FEATURE_MENU, 1 ); + +/** "dynui" options */ +struct dynui_options { + /** Name */ + char *name; + /** Delete */ + int delete; +}; + +/** "dynui" option list */ +static struct option_descriptor dynui_opts[] = { + OPTION_DESC ( "name", 'n', required_argument, + struct dynui_options, name, parse_string ), + OPTION_DESC ( "delete", 'd', no_argument, + struct dynui_options, delete, parse_flag ), +}; + +/** "dynui" command descriptor */ +static struct command_descriptor dynui_cmd = + COMMAND_DESC ( struct dynui_options, dynui_opts, 0, MAX_ARGUMENTS, + "[]" ); + +/** + * The "dynui" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int dynui_exec ( int argc, char **argv ) { + struct dynui_options opts; + struct dynamic_ui *dynui; + char *title; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &dynui_cmd, &opts ) ) != 0 ) + goto err_parse_options; + + /* Parse title */ + title = concat_args ( &argv[optind] ); + if ( ! title ) { + rc = -ENOMEM; + goto err_parse_title; + } + + /* Create dynamic user interface */ + dynui = create_dynui ( opts.name, title ); + if ( ! dynui ) { + rc = -ENOMEM; + goto err_create_dynui; + } + + /* Destroy dynamic user interface, if applicable */ + if ( opts.delete ) + destroy_dynui ( dynui ); + + /* Success */ + rc = 0; + + err_create_dynui: + free ( title ); + err_parse_title: + err_parse_options: + return rc; +} + +/** "item" options */ +struct item_options { + /** Dynamic user interface name */ + char *dynui; + /** Shortcut key */ + unsigned int key; + /** Use as default */ + int is_default; + /** Use as a separator */ + int is_gap; +}; + +/** "item" option list */ +static struct option_descriptor item_opts[] = { + OPTION_DESC ( "menu", 'm', required_argument, + struct item_options, dynui, parse_string ), + OPTION_DESC ( "key", 'k', required_argument, + struct item_options, key, parse_key ), + OPTION_DESC ( "default", 'd', no_argument, + struct item_options, is_default, parse_flag ), + OPTION_DESC ( "gap", 'g', no_argument, + struct item_options, is_gap, parse_flag ), +}; + +/** "item" command descriptor */ +static struct command_descriptor item_cmd = + COMMAND_DESC ( struct item_options, item_opts, 0, MAX_ARGUMENTS, + "[<name> [<text>]]" ); + +/** + * The "item" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int item_exec ( int argc, char **argv ) { + struct item_options opts; + struct dynamic_ui *dynui; + struct dynamic_item *item; + char *name = NULL; + char *text = NULL; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &item_cmd, &opts ) ) != 0 ) + goto err_parse_options; + + /* Parse name, if present */ + if ( ! opts.is_gap ) + name = argv[optind++]; /* May be NULL */ + + /* Parse text, if present */ + if ( optind < argc ) { + text = concat_args ( &argv[optind] ); + if ( ! text ) { + rc = -ENOMEM; + goto err_parse_text; + } + } + + /* Identify dynamic user interface */ + if ( ( rc = parse_dynui ( opts.dynui, &dynui ) ) != 0 ) + goto err_parse_dynui; + + /* Add dynamic user interface item */ + item = add_dynui_item ( dynui, name, ( text ? text : "" ), + opts.key, opts.is_default ); + if ( ! item ) { + rc = -ENOMEM; + goto err_add_dynui_item; + } + + /* Success */ + rc = 0; + + err_add_dynui_item: + err_parse_dynui: + free ( text ); + err_parse_text: + err_parse_options: + return rc; +} + +/** "choose" options */ +struct choose_options { + /** Dynamic user interface name */ + char *dynui; + /** Timeout */ + unsigned long timeout; + /** Default selection */ + char *select; + /** Keep dynamic user interface */ + int keep; +}; + +/** "choose" option list */ +static struct option_descriptor choose_opts[] = { + OPTION_DESC ( "menu", 'm', required_argument, + struct choose_options, dynui, parse_string ), + OPTION_DESC ( "default", 'd', required_argument, + struct choose_options, select, parse_string ), + OPTION_DESC ( "timeout", 't', required_argument, + struct choose_options, timeout, parse_timeout ), + OPTION_DESC ( "keep", 'k', no_argument, + struct choose_options, keep, parse_flag ), +}; + +/** "choose" command descriptor */ +static struct command_descriptor choose_cmd = + COMMAND_DESC ( struct choose_options, choose_opts, 1, 1, "<setting>" ); + +/** + * The "choose" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int choose_exec ( int argc, char **argv ) { + struct choose_options opts; + struct named_setting setting; + struct dynamic_ui *dynui; + struct dynamic_item *item; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &choose_cmd, &opts ) ) != 0 ) + goto err_parse_options; + + /* Parse setting name */ + if ( ( rc = parse_autovivified_setting ( argv[optind], + &setting ) ) != 0 ) + goto err_parse_setting; + + /* Identify dynamic user interface */ + if ( ( rc = parse_dynui ( opts.dynui, &dynui ) ) != 0 ) + goto err_parse_dynui; + + /* Show as menu */ + if ( ( rc = show_menu ( dynui, opts.timeout, opts.select, + &item ) ) != 0 ) + goto err_show_menu; + + /* Apply default type if necessary */ + if ( ! setting.setting.type ) + setting.setting.type = &setting_type_string; + + /* Store setting */ + if ( ( rc = storef_setting ( setting.settings, &setting.setting, + item->name ) ) != 0 ) { + printf ( "Could not store \"%s\": %s\n", + setting.setting.name, strerror ( rc ) ); + goto err_store; + } + + /* Success */ + rc = 0; + + err_store: + err_show_menu: + /* Destroy dynamic user interface, if applicable */ + if ( ! opts.keep ) + destroy_dynui ( dynui ); + err_parse_dynui: + err_parse_setting: + err_parse_options: + return rc; +} + +/** Dynamic user interface commands */ +struct command dynui_commands[] __command = { + { + .name = "menu", + .exec = dynui_exec, + }, + { + .name = "item", + .exec = item_exec, + }, + { + .name = "choose", + .exec = choose_exec, + }, +}; diff --git a/src/hci/commands/menu_cmd.c b/src/hci/commands/menu_cmd.c deleted file mode 100644 index 8b6d2a9..0000000 --- a/src/hci/commands/menu_cmd.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** @file - * - * Menu commands - * - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <getopt.h> -#include <ipxe/menu.h> -#include <ipxe/command.h> -#include <ipxe/parseopt.h> -#include <ipxe/settings.h> -#include <ipxe/features.h> - -FEATURE ( FEATURE_MISC, "Menu", DHCP_EB_FEATURE_MENU, 1 ); - -/** "menu" options */ -struct menu_options { - /** Name */ - char *name; - /** Delete */ - int delete; -}; - -/** "menu" option list */ -static struct option_descriptor menu_opts[] = { - OPTION_DESC ( "name", 'n', required_argument, - struct menu_options, name, parse_string ), - OPTION_DESC ( "delete", 'd', no_argument, - struct menu_options, delete, parse_flag ), -}; - -/** "menu" command descriptor */ -static struct command_descriptor menu_cmd = - COMMAND_DESC ( struct menu_options, menu_opts, 0, MAX_ARGUMENTS, - "[<title>]" ); - -/** - * The "menu" command - * - * @v argc Argument count - * @v argv Argument list - * @ret rc Return status code - */ -static int menu_exec ( int argc, char **argv ) { - struct menu_options opts; - struct menu *menu; - char *title; - int rc; - - /* Parse options */ - if ( ( rc = parse_options ( argc, argv, &menu_cmd, &opts ) ) != 0 ) - goto err_parse_options; - - /* Parse title */ - title = concat_args ( &argv[optind] ); - if ( ! title ) { - rc = -ENOMEM; - goto err_parse_title; - } - - /* Create menu */ - menu = create_menu ( opts.name, title ); - if ( ! menu ) { - rc = -ENOMEM; - goto err_create_menu; - } - - /* Destroy menu, if applicable */ - if ( opts.delete ) - destroy_menu ( menu ); - - /* Success */ - rc = 0; - - err_create_menu: - free ( title ); - err_parse_title: - err_parse_options: - return rc; -} - -/** "item" options */ -struct item_options { - /** Menu name */ - char *menu; - /** Shortcut key */ - unsigned int key; - /** Use as default */ - int is_default; - /** Use as a separator */ - int is_gap; -}; - -/** "item" option list */ -static struct option_descriptor item_opts[] = { - OPTION_DESC ( "menu", 'm', required_argument, - struct item_options, menu, parse_string ), - OPTION_DESC ( "key", 'k', required_argument, - struct item_options, key, parse_key ), - OPTION_DESC ( "default", 'd', no_argument, - struct item_options, is_default, parse_flag ), - OPTION_DESC ( "gap", 'g', no_argument, - struct item_options, is_gap, parse_flag ), -}; - -/** "item" command descriptor */ -static struct command_descriptor item_cmd = - COMMAND_DESC ( struct item_options, item_opts, 0, MAX_ARGUMENTS, - "[<name> [<text>]]" ); - -/** - * The "item" command - * - * @v argc Argument count - * @v argv Argument list - * @ret rc Return status code - */ -static int item_exec ( int argc, char **argv ) { - struct item_options opts; - struct menu *menu; - struct menu_item *item; - char *name = NULL; - char *text = NULL; - int rc; - - /* Parse options */ - if ( ( rc = parse_options ( argc, argv, &item_cmd, &opts ) ) != 0 ) - goto err_parse_options; - - /* Parse name, if present */ - if ( ! opts.is_gap ) - name = argv[optind++]; /* May be NULL */ - - /* Parse text, if present */ - if ( optind < argc ) { - text = concat_args ( &argv[optind] ); - if ( ! text ) { - rc = -ENOMEM; - goto err_parse_text; - } - } - - /* Identify menu */ - if ( ( rc = parse_menu ( opts.menu, &menu ) ) != 0 ) - goto err_parse_menu; - - /* Add menu item */ - item = add_menu_item ( menu, name, ( text ? text : "" ), - opts.key, opts.is_default ); - if ( ! item ) { - rc = -ENOMEM; - goto err_add_menu_item; - } - - /* Success */ - rc = 0; - - err_add_menu_item: - err_parse_menu: - free ( text ); - err_parse_text: - err_parse_options: - return rc; -} - -/** "choose" options */ -struct choose_options { - /** Menu name */ - char *menu; - /** Timeout */ - unsigned long timeout; - /** Default selection */ - char *select; - /** Keep menu */ - int keep; -}; - -/** "choose" option list */ -static struct option_descriptor choose_opts[] = { - OPTION_DESC ( "menu", 'm', required_argument, - struct choose_options, menu, parse_string ), - OPTION_DESC ( "default", 'd', required_argument, - struct choose_options, select, parse_string ), - OPTION_DESC ( "timeout", 't', required_argument, - struct choose_options, timeout, parse_timeout ), - OPTION_DESC ( "keep", 'k', no_argument, - struct choose_options, keep, parse_flag ), -}; - -/** "choose" command descriptor */ -static struct command_descriptor choose_cmd = - COMMAND_DESC ( struct choose_options, choose_opts, 1, 1, "<setting>" ); - -/** - * The "choose" command - * - * @v argc Argument count - * @v argv Argument list - * @ret rc Return status code - */ -static int choose_exec ( int argc, char **argv ) { - struct choose_options opts; - struct named_setting setting; - struct menu *menu; - struct menu_item *item; - int rc; - - /* Parse options */ - if ( ( rc = parse_options ( argc, argv, &choose_cmd, &opts ) ) != 0 ) - goto err_parse_options; - - /* Parse setting name */ - if ( ( rc = parse_autovivified_setting ( argv[optind], - &setting ) ) != 0 ) - goto err_parse_setting; - - /* Identify menu */ - if ( ( rc = parse_menu ( opts.menu, &menu ) ) != 0 ) - goto err_parse_menu; - - /* Show menu */ - if ( ( rc = show_menu ( menu, opts.timeout, opts.select, &item ) ) != 0) - goto err_show_menu; - - /* Apply default type if necessary */ - if ( ! setting.setting.type ) - setting.setting.type = &setting_type_string; - - /* Store setting */ - if ( ( rc = storef_setting ( setting.settings, &setting.setting, - item->name ) ) != 0 ) { - printf ( "Could not store \"%s\": %s\n", - setting.setting.name, strerror ( rc ) ); - goto err_store; - } - - /* Success */ - rc = 0; - - err_store: - err_show_menu: - /* Destroy menu, if applicable */ - if ( ! opts.keep ) - destroy_menu ( menu ); - err_parse_menu: - err_parse_setting: - err_parse_options: - return rc; -} - -/** Menu commands */ -struct command menu_commands[] __command = { - { - .name = "menu", - .exec = menu_exec, - }, - { - .name = "item", - .exec = item_exec, - }, - { - .name = "choose", - .exec = choose_exec, - }, -}; diff --git a/src/hci/tui/menu_ui.c b/src/hci/tui/menu_ui.c index 067e2d8..ab4e602 100644 --- a/src/hci/tui/menu_ui.c +++ b/src/hci/tui/menu_ui.c @@ -37,7 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/console.h> #include <ipxe/ansicol.h> #include <ipxe/jumpscroll.h> -#include <ipxe/menu.h> +#include <ipxe/dynui.h> /* Screen layout */ #define TITLE_ROW 1U @@ -49,8 +49,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A menu user interface */ struct menu_ui { - /** Menu */ - struct menu *menu; + /** Dynamic user interface */ + struct dynamic_ui *dynui; /** Jump scroller */ struct jump_scroller scroll; /** Timeout (0=indefinite) */ @@ -60,14 +60,15 @@ struct menu_ui { /** * Return a numbered menu item * - * @v menu Menu + * @v dynui Dynamic user interface * @v index Index * @ret item Menu item, or NULL */ -static struct menu_item * menu_item ( struct menu *menu, unsigned int index ) { - struct menu_item *item; +static struct dynamic_item * menu_item ( struct dynamic_ui *dynui, + unsigned int index ) { + struct dynamic_item *item; - list_for_each_entry ( item, &menu->items, list ) { + list_for_each_entry ( item, &dynui->items, list ) { if ( index-- == 0 ) return item; } @@ -82,7 +83,7 @@ static struct menu_item * menu_item ( struct menu *menu, unsigned int index ) { * @v index Index */ static void draw_menu_item ( struct menu_ui *ui, unsigned int index ) { - struct menu_item *item; + struct dynamic_item *item; unsigned int row_offset; char buf[ MENU_COLS + 1 /* NUL */ ]; char timeout_buf[6]; /* "(xxx)" + NUL */ @@ -95,7 +96,7 @@ static void draw_menu_item ( struct menu_ui *ui, unsigned int index ) { move ( ( MENU_ROW + row_offset ), MENU_COL ); /* Get menu item */ - item = menu_item ( ui->menu, index ); + item = menu_item ( ui->dynui, index ); if ( item ) { /* Draw separators in a different colour */ @@ -171,8 +172,8 @@ static void draw_menu_items ( struct menu_ui *ui ) { * @ret selected Selected item * @ret rc Return status code */ -static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { - struct menu_item *item; +static int menu_loop ( struct menu_ui *ui, struct dynamic_item **selected ) { + struct dynamic_item *item; unsigned long timeout; unsigned int previous; unsigned int move; @@ -217,7 +218,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { break; default: i = 0; - list_for_each_entry ( item, &ui->menu->items, + list_for_each_entry ( item, &ui->dynui->items, list ) { if ( ! ( item->shortcut && ( item->shortcut == key ) ) ) { @@ -238,7 +239,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { /* Move selection, if applicable */ while ( move ) { move = jump_scroll_move ( &ui->scroll, move ); - item = menu_item ( ui->menu, ui->scroll.current ); + item = menu_item ( ui->dynui, ui->scroll.current ); if ( item->name ) break; } @@ -252,7 +253,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { } /* Record selection */ - item = menu_item ( ui->menu, ui->scroll.current ); + item = menu_item ( ui->dynui, ui->scroll.current ); assert ( item != NULL ); assert ( item->name != NULL ); *selected = item; @@ -265,14 +266,14 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { /** * Show menu * - * @v menu Menu + * @v dynui Dynamic user interface * @v timeout Timeout period, in ticks (0=indefinite) * @ret selected Selected item * @ret rc Return status code */ -int show_menu ( struct menu *menu, unsigned long timeout, - const char *select, struct menu_item **selected ) { - struct menu_item *item; +int show_menu ( struct dynamic_ui *dynui, unsigned long timeout, + const char *select, struct dynamic_item **selected ) { + struct dynamic_item *item; struct menu_ui ui; char buf[ MENU_COLS + 1 /* NUL */ ]; int named_count = 0; @@ -280,10 +281,10 @@ int show_menu ( struct menu *menu, unsigned long timeout, /* Initialise UI */ memset ( &ui, 0, sizeof ( ui ) ); - ui.menu = menu; + ui.dynui = dynui; ui.scroll.rows = MENU_ROWS; ui.timeout = timeout; - list_for_each_entry ( item, &menu->items, list ) { + list_for_each_entry ( item, &dynui->items, list ) { if ( item->name ) { if ( ! named_count ) ui.scroll.current = ui.scroll.count; @@ -315,7 +316,7 @@ int show_menu ( struct menu *menu, unsigned long timeout, /* Draw initial content */ attron ( A_BOLD ); - snprintf ( buf, sizeof ( buf ), "%s", ui.menu->title ); + snprintf ( buf, sizeof ( buf ), "%s", ui.dynui->title ); mvprintw ( TITLE_ROW, ( ( COLS - strlen ( buf ) ) / 2 ), "%s", buf ); attroff ( A_BOLD ); jump_scroll ( &ui.scroll ); diff --git a/src/include/ipxe/dynui.h b/src/include/ipxe/dynui.h new file mode 100644 index 0000000..25124a3 --- /dev/null +++ b/src/include/ipxe/dynui.h @@ -0,0 +1,50 @@ +#ifndef _IPXE_DYNUI_H +#define _IPXE_DYNUI_H + +/** @file + * + * Dynamic user interfaces + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/list.h> + +/** A dynamic user interface */ +struct dynamic_ui { + /** List of dynamic user interfaces */ + struct list_head list; + /** Name */ + const char *name; + /** Title */ + const char *title; + /** Dynamic user interface items */ + struct list_head items; +}; + +/** A dynamic user interface item */ +struct dynamic_item { + /** List of dynamic user interface items */ + struct list_head list; + /** Name */ + const char *name; + /** Text */ + const char *text; + /** Shortcut key */ + int shortcut; + /** Is default item */ + int is_default; +}; + +extern struct dynamic_ui * create_dynui ( const char *name, const char *title ); +extern struct dynamic_item * add_dynui_item ( struct dynamic_ui *dynui, + const char *name, + const char *text, int shortcut, + int is_default ); +extern void destroy_dynui ( struct dynamic_ui *dynui ); +extern struct dynamic_ui * find_dynui ( const char *name ); +extern int show_menu ( struct dynamic_ui *dynui, unsigned long timeout, + const char *select, struct dynamic_item **selected ); + +#endif /* _IPXE_DYNUI_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index a9ce656..d75661b 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -364,7 +364,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_cms ( ERRFILE_OTHER | 0x002a0000 ) #define ERRFILE_imgtrust ( ERRFILE_OTHER | 0x002b0000 ) #define ERRFILE_menu_ui ( ERRFILE_OTHER | 0x002c0000 ) -#define ERRFILE_menu_cmd ( ERRFILE_OTHER | 0x002d0000 ) +#define ERRFILE_dynui_cmd ( ERRFILE_OTHER | 0x002d0000 ) #define ERRFILE_validator ( ERRFILE_OTHER | 0x002e0000 ) #define ERRFILE_ocsp ( ERRFILE_OTHER | 0x002f0000 ) #define ERRFILE_nslookup ( ERRFILE_OTHER | 0x00300000 ) diff --git a/src/include/ipxe/menu.h b/src/include/ipxe/menu.h deleted file mode 100644 index d11a925..0000000 --- a/src/include/ipxe/menu.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef _IPXE_MENU_H -#define _IPXE_MENU_H - -/** @file - * - * Menu selection - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <ipxe/list.h> - -/** A menu */ -struct menu { - /** List of menus */ - struct list_head list; - /** Name */ - const char *name; - /** Title */ - const char *title; - /** Menu items */ - struct list_head items; -}; - -/** A menu item */ -struct menu_item { - /** List of menu items */ - struct list_head list; - /** Name */ - const char *name; - /** Text */ - const char *text; - /** Shortcut key */ - int shortcut; - /** Is default item */ - int is_default; -}; - -extern struct menu * create_menu ( const char *name, const char *title ); -extern struct menu_item * add_menu_item ( struct menu *menu, const char *name, - const char *text, int shortcut, - int is_default ); -extern void destroy_menu ( struct menu *menu ); -extern struct menu * find_menu ( const char *name ); -extern int show_menu ( struct menu *menu, unsigned long timeout, - const char *select, struct menu_item **selected ); - -#endif /* _IPXE_MENU_H */ diff --git a/src/include/ipxe/parseopt.h b/src/include/ipxe/parseopt.h index 61010f2..5c449cd 100644 --- a/src/include/ipxe/parseopt.h +++ b/src/include/ipxe/parseopt.h @@ -16,7 +16,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct net_device; struct net_device_configurator; -struct menu; +struct dynamic_ui; struct parameters; /** A command-line option descriptor */ @@ -142,7 +142,7 @@ extern int parse_netdev ( char *text, struct net_device **netdev ); extern int parse_netdev_configurator ( char *text, struct net_device_configurator **configurator ); -extern int parse_menu ( char *text, struct menu **menu ); +extern int parse_dynui ( char *text, struct dynamic_ui **dynui ); extern int parse_flag ( char *text __unused, int *flag ); extern int parse_key ( char *text, unsigned int *key ); extern int parse_settings ( char *text, struct settings **settings ); -- cgit v1.1