From b712b26632d4af1e1c9454a3b330ed7b401e165b Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 20 Aug 2010 10:22:47 +0200 Subject: Add Menu Framework Introduce a menu framework that allow us to create list menu to simplify barebox and make it more user-frendly This kind of menu is very usefull when you do not have a keyboard or a serial console attached to your board to allow you to interract with barebox For the develloper part, The framework introduce two API 1) C that allow you to create menu, submenu, entry and complex menu action 2) Command that allow you as the C API to create menu, submenu, entry and complex menu action but this time the actions will be store in a function and then be evaluated and excecuted at runtime. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- commands/menu.c | 481 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 481 insertions(+) create mode 100644 commands/menu.c (limited to 'commands/menu.c') diff --git a/commands/menu.c b/commands/menu.c new file mode 100644 index 0000000000..c1bd0e2a44 --- /dev/null +++ b/commands/menu.c @@ -0,0 +1,481 @@ +/* + * (C) Copyright 2009-2010 Jean-Christophe PLAGNIOL-VILLARD + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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; version 2 of + * the License. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +typedef enum { +#if defined(CONFIG_CMD_MENU_MANAGEMENT) + action_add, + action_remove, + action_select, +#endif + action_list, + action_show, +} menu_action; + +struct cmd_menu { + char *menu; + menu_action action; +#if defined(CONFIG_CMD_MENU_MANAGEMENT) + int entry; + int re_entrant; + char *description; + char *command; + int num; +#endif +}; + +#if defined(CONFIG_CMD_MENU_MANAGEMENT) +#define OPTS "m:earlc:d:RsSn:" +#define is_entry(x) ((x)->entry) +#else +#define OPTS "m:ls" +#define is_entry(x) (0) +#endif + +#if defined(CONFIG_CMD_MENU_MANAGEMENT) +/* + * menu -e -a -m -c [-R] -d + */ +static int do_menu_entry_add(struct cmd_menu *cm) +{ + struct menu_entry *me; + struct menu *m; + int len; + int ret = -ENOMEM; + + if (!cm->menu || !cm->command || !cm->description) + return -EINVAL; + + m = menu_get_by_name(cm->menu); + + if (!m) { + eprintf("Menu '%s' not found\n", cm->menu); + return -EINVAL; + } + + me = menu_entry_alloc(); + + if (!me) + goto free; + + me->action = menu_action_run; + + len = strlen(cm->command) + 1; + + me->priv = calloc(len, sizeof(char)); + + if (!me->priv) + goto free; + + strncpy(me->priv, cm->command, len); + + len = strlen(cm->description) + 1; + + me->display = calloc(len, sizeof(char));; + + if (!m->display) + goto free; + + strncpy(me->display, cm->description, len); + + ret = menu_add_entry(m, me); + + if (ret) + goto free; + + me->non_re_ent = !cm->re_entrant; + + return 0; + +free: + eputs("Entry add fail\n"); + + free(me->priv); + + menu_entry_free(me); + + return ret; +} + +/* + * menu -e -r -m -n + */ +static int do_menu_entry_remove(struct cmd_menu *cm) +{ + struct menu *m; + struct menu_entry *me; + + if (!cm->menu || cm->num < 0) + return -EINVAL; + + m = menu_get_by_name(cm->menu); + + if (!m) { + eprintf("Menu '%s' not found\n", cm->menu); + return -EINVAL; + } + + me = menu_entry_get_by_num(m, cm->num); + + if (!me) { + eprintf("Entry '%s' not found\n", cm->num); + return -EINVAL; + } + + menu_remove_entry(m, me); + + menu_entry_free(me); + + return 0; +} + +/* + * menu -a -m -d + */ +static int do_menu_add(struct cmd_menu *cm) +{ + struct menu *m; + int len = 0; + int ret = -ENOMEM; + + if (!cm->menu || !cm->description) + return -EINVAL; + + m = menu_alloc(); + + if (!m) + goto free; + + len = strlen(cm->menu) + 1; + + m->name = calloc(len, sizeof(char));; + if (!m->name) + goto free; + + strncpy(m->name, cm->menu, len); + + len = strlen(cm->description) + 1; + + m->display = calloc(len, sizeof(char));; + + if (!m->display) + goto free; + + strncpy(m->display, cm->description, len); + + ret = menu_add(m); + + if (ret) + goto free; + + return 0; + +free: + eprintf("Menu '%s' add fail", cm->menu); + if (ret == -EEXIST) + eputs(" already exist"); + eputs("\n"); + + menu_free(m); + + return ret; +} +/* + * menu -r -m + */ +static int do_menu_remove(struct cmd_menu *cm) +{ + struct menu *m; + + m = menu_get_by_name(cm->menu); + + if (!m) { + eprintf("Menu '%s' not found\n", cm->menu); + return -EINVAL; + } + + menu_remove(m); + + menu_free(m); + + return 0; +} + +/* + * menu -m -S -n + */ +static int do_menu_select(struct cmd_menu *cm) +{ + struct menu *m; + + if (cm->num < 0) + return -EINVAL; + + m = menu_get_by_name(cm->menu); + + if (!m) { + eprintf("Menu '%s' not found\n", cm->menu); + return -EINVAL; + } + + if (!menu_set_selected(m, cm->num)) { + eprintf("Entry '%d' not found\n", cm->num); + return -EINVAL; + } + + return 0; +} +#endif + +/* + * menu -s -m + */ +static int do_menu_show(struct cmd_menu *cm) +{ + struct menu *m; + + if (cm->menu) + m = menu_get_by_name(cm->menu); + else + m = menu_get_by_name("boot"); + + return menu_show(m); +} + +static void print_entries(struct menu *m) +{ + struct list_head *pos; + struct menu_entry *me; + + list_for_each(pos, &(m->entries.list)) { + me = list_entry(pos, struct menu_entry, list); + printf("%d: %s\n", me->num, me->display); + } +} + +/* + * menu -l + * menu -e -l [menu] + */ +static int do_menu_list(struct cmd_menu *cm) +{ + struct list_head *pos; + struct menu* m = NULL; + struct menu* menus = menu_get_menus(); + + if (is_entry(cm)) { + if (cm->menu) + m = menu_get_by_name(cm->menu); + + if (m) { + print_entries(m); + return 0; + } + } + + list_for_each(pos, &menus->list) { + m = list_entry(pos, struct menu, list); + printf("%s: %s\n", m->name, m->display? m->display : m->name); + if (is_entry(cm)) + print_entries(m); + } + + return 0; +} + +#if defined(CONFIG_CMD_MENU_MANAGEMENT) +static int do_menu_entry(struct cmd_menu *cm) +{ + switch(cm->action) { + case action_list: + return do_menu_list(cm); + case action_remove: + return do_menu_entry_remove(cm); + case action_add: + return do_menu_entry_add(cm); + case action_select: + case action_show: + break; + } + return -EINVAL; +} +#else +static int do_menu_entry(struct cmd_menu *cm) +{ + return -EINVAL; +} +#endif + +static int do_menu(struct command *cmdtp, int argc, char *argv[]) +{ + struct cmd_menu cm; + int opt; + int ret = -EINVAL; + + if (!argc) + return COMMAND_ERROR_USAGE; + + memset(&cm, 0, sizeof(struct cmd_menu)); +#if defined(CONFIG_CMD_MENU_MANAGEMENT) + cm.num = -EINVAL; +#endif + + cm.action = action_show; + + while((opt = getopt(argc, argv, OPTS)) > 0) { + switch(opt) { + case 'm': + cm.menu = optarg; + break; + case 'l': + cm.action = action_list; + break; + case 's': + cm.action = action_show; + break; +#if defined(CONFIG_CMD_MENU_MANAGEMENT) + case 'e': + cm.entry = 1; + break; + case 'a': + cm.action = action_add; + break; + case 'r': + cm.action = action_remove; + break; + case 'c': + cm.command = optarg; + break; + case 'd': + cm.description = optarg; + break; + case 'R': + cm.re_entrant = 1; + break; + case 'S': + cm.action = action_select; + break; + case 'n': + cm.num = simple_strtoul(optarg, NULL, 10); + break; +#endif + default: + return 1; + } + } + + if (is_entry(&cm)) { + ret = do_menu_entry(&cm); + goto end; + } + + switch(cm.action) { + case action_list: + ret = do_menu_list(&cm); + break; +#if defined(CONFIG_CMD_MENU_MANAGEMENT) + case action_remove: + ret = do_menu_remove(&cm); + break; + case action_add: + ret = do_menu_add(&cm); + break; + case action_select: + ret = do_menu_select(&cm); + break; +#endif + case action_show: + ret = do_menu_show(&cm); + break; + } + +end: + if (ret) + return 0; + + return 1; +} + +static const __maybe_unused char cmd_menu_help[] = +"Usage: menu [OPTION]... \n" +"Manage Menu\n" +" -m menu\n" +" -l list\n" +" -s show\n" +#if defined(CONFIG_CMD_MENU_MANAGEMENT) +"Advanced\n" +" -e menu entry\n" +" -a add\n" +" -r remove\n" +" -S select\n" +#endif +"\n" +"How to\n" +"\n" +"Show menu\n" +" menu -s -m \n" +"\n" +"List menu\n" +" menu -l\n" +"\n" +#if defined(CONFIG_CMD_MENU_MANAGEMENT) +"Add a menu\n" +" menu -a -m -d \n" +"\n" +"Remove a menu\n" +" menu -r -m \n" +"\n" +"Add an entry\n" +" (-R for do no exit the menu after executing the command)\n" +" menu -e -a -m -c [-R] -d \n" +"\n" +"Remove an entry\n" +" menu -e -r -m -n \n" +"\n" +"Select an entry\n" +" menu -m -S -n \n" +"\n" +"List menu\n" +" menu -e -l [menu]\n" +"\n" +"Menu example\n" +"menu -a -m boot -d \"Boot Menu\"\n" +"menu -e -a -m boot -c boot -d \"Boot\"\n" +"menu -e -a -m boot -c reset -d \"Reset\"\n" +"menu -s -m boot\n" +#else +"Menu example\n" +"menu -s -m boot\n" +#endif +; + +BAREBOX_CMD_START(menu) + .cmd = do_menu, + .usage = "Menu Management", + BAREBOX_CMD_HELP(cmd_menu_help) +BAREBOX_CMD_END -- cgit v1.2.3