/* * (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 #include #include #include #include #include #include static LIST_HEAD(menus); struct menu* menu_get_menus(void) { if (list_empty(&menus)) return NULL; return list_entry(&menus, struct menu, list); } EXPORT_SYMBOL(menu_get_menus); void menu_free(struct menu *m) { struct menu_entry *me, *tmp; if (!m) return; free(m->name); free(m->display); free(m->auto_display); list_for_each_entry_safe(me, tmp, &m->entries, list) menu_entry_free(me); free(m); } EXPORT_SYMBOL(menu_free); int menu_add(struct menu *m) { if (!m || !m->name) return -EINVAL; if (menu_get_by_name(m->name)) return -EEXIST; list_add_tail(&m->list, &menus); return 0; } EXPORT_SYMBOL(menu_add); void menu_remove(struct menu *m) { if (!m) return; list_del(&m->list); } EXPORT_SYMBOL(menu_remove); int menu_add_entry(struct menu *m, struct menu_entry *me) { if (!m || !me || !me->display) return -EINVAL; m->nb_entries++; me->num = m->nb_entries; list_add_tail(&me->list, &m->entries); return 0; } EXPORT_SYMBOL(menu_add_entry); void menu_remove_entry(struct menu *m, struct menu_entry *me) { int i = 1; if (!m || !me) return; m->nb_entries--; list_del(&me->list); list_for_each_entry(me, &m->entries, list) me->num = i++; } EXPORT_SYMBOL(menu_remove_entry); struct menu* menu_get_by_name(char *name) { struct menu* m; if (!name) return NULL; list_for_each_entry(m, &menus, list) { if(strcmp(m->name, name) == 0) return m; } return NULL; } EXPORT_SYMBOL(menu_get_by_name); struct menu_entry* menu_entry_get_by_num(struct menu* m, int num) { struct menu_entry* me; if (!m || num < 1 || num > m->nb_entries) return NULL; list_for_each_entry(me, &m->entries, list) { if(me->num == num) return me; } return NULL; } EXPORT_SYMBOL(menu_entry_get_by_num); void menu_entry_free(struct menu_entry *me) { if (!me) return; me->free(me); } EXPORT_SYMBOL(menu_entry_free); static void __print_entry(const char *str) { static char outstr[256]; if (IS_ENABLED(CONFIG_SHELL_HUSH)) { process_escape_sequence(str, outstr, 256); puts(outstr); } else { puts(str); } } static void print_menu_entry(struct menu *m, struct menu_entry *me, int selected) { gotoXY(me->num + 1, 3); if (me->type == MENU_ENTRY_BOX) { if (me->box_state) puts("[*]"); else puts("[ ]"); } else { puts(" "); } printf(" %d: ", me->num); if (selected) puts("\e[7m"); __print_entry(me->display); if (selected) puts("\e[m"); } int menu_set_selected_entry(struct menu *m, struct menu_entry* me) { struct menu_entry* tmp; if (!m || !me) return -EINVAL; list_for_each_entry(tmp, &m->entries, list) { if(me == tmp) { m->selected = me; return 0; } } return -EINVAL; } EXPORT_SYMBOL(menu_set_selected_entry); int menu_set_selected(struct menu *m, int num) { struct menu_entry *me; me = menu_entry_get_by_num(m, num); if (!me) return -EINVAL; m->selected = me; return 0; } EXPORT_SYMBOL(menu_set_selected); int menu_set_auto_select(struct menu *m, int delay) { if (!m) return -EINVAL; m->auto_select = delay; return 0; } EXPORT_SYMBOL(menu_set_auto_select); static void print_menu(struct menu *m) { struct menu_entry *me; clear(); gotoXY(1, 2); if(m->display) { __print_entry(m->display); } else { puts("Menu : "); puts(m->name); } list_for_each_entry(me, &m->entries, list) { if(m->selected != me) print_menu_entry(m, me, 0); } if (!m->selected) { m->selected = list_first_entry(&m->entries, struct menu_entry, list); } print_menu_entry(m, m->selected, 1); } int menu_show(struct menu *m) { int ch, ch_previous = 0; int countdown; int auto_display_len = 16; uint64_t start, second; if(!m || list_empty(&m->entries)) return -EINVAL; print_menu(m); countdown = m->auto_select; if (m->auto_select >= 0) { gotoXY(m->nb_entries + 2, 3); if (!m->auto_display) { printf("Auto Select in"); } else { auto_display_len = strlen(m->auto_display); printf(m->auto_display); } printf(" %2d", countdown--); } start = get_time_ns(); second = start; while (m->auto_select > 0 && !is_timeout(start, m->auto_select * SECOND)) { if (tstc()) { m->auto_select = -1; break; } if (is_timeout(second, SECOND)) { printf("\b\b%2d", countdown--); second += SECOND; } } gotoXY(m->nb_entries + 2, 3); printf("%*c", auto_display_len + 4, ' '); gotoXY(m->selected->num + 1, 3); do { struct menu_entry *old_selected = m->selected; int repaint = 0; if (m->auto_select >= 0) ch = KEY_RETURN; else ch = read_key(); m->auto_select = -1; switch (ch) { case KEY_UP: m->selected = list_entry(m->selected->list.prev, struct menu_entry, list); if (&(m->selected->list) == &(m->entries)) { m->selected = list_entry(m->selected->list.prev, struct menu_entry, list); } repaint = 1; break; case KEY_DOWN: m->selected = list_entry(m->selected->list.next, struct menu_entry, list); if (&(m->selected->list) == &(m->entries)) { m->selected = list_entry(m->selected->list.next, struct menu_entry, list); } repaint = 1; break; case ' ': if (m->selected->type != MENU_ENTRY_BOX) break; m->selected->box_state = !m->selected->box_state; if (m->selected->action) m->selected->action(m, m->selected); repaint = 1; break; case KEY_ENTER: if (ch_previous == KEY_RETURN) break; case KEY_RETURN: if (ch_previous == KEY_ENTER) break; clear(); gotoXY(1,1); m->selected->action(m, m->selected); if (m->selected->non_re_ent) return m->selected->num; else print_menu(m); break; case KEY_HOME: m->selected = list_first_entry(&m->entries, struct menu_entry, list); repaint = 1; break; case KEY_END: m->selected = list_last_entry(&m->entries, struct menu_entry, list); repaint = 1; break; default: break; } if (repaint) { print_menu_entry(m, old_selected, 0); print_menu_entry(m, m->selected, 1); } ch_previous = ch; } while(1); return 0; } EXPORT_SYMBOL(menu_show); void menu_action_exit(struct menu *m, struct menu_entry *me) {} EXPORT_SYMBOL(menu_action_exit); struct submenu { char *submenu; struct menu_entry entry; }; static void menu_action_show(struct menu *m, struct menu_entry *me) { struct submenu *s = container_of(me, struct submenu, entry); struct menu *sm; if (me->type == MENU_ENTRY_BOX && !me->box_state) return; sm = menu_get_by_name(s->submenu); if (sm) menu_show(sm); else eprintf("no such menu: %s\n", s->submenu); } static void submenu_free(struct menu_entry *me) { struct submenu *s = container_of(me, struct submenu, entry); free(s->entry.display); free(s->submenu); free(s); } struct menu_entry *menu_add_submenu(struct menu *parent, char *submenu, char *display) { struct submenu *s = calloc(1, sizeof(*s)); int ret; if (!s) return ERR_PTR(-ENOMEM); s->submenu = strdup(submenu); s->entry.action = menu_action_show; s->entry.free = submenu_free; s->entry.display = strdup(display); if (!s->entry.display || !s->submenu) { ret = -ENOMEM; goto err_free; } ret = menu_add_entry(parent, &s->entry); if (ret) goto err_free; return &s->entry; err_free: submenu_free(&s->entry); return ERR_PTR(ret); } EXPORT_SYMBOL(menu_add_submenu); struct action_entry { char *command; struct menu_entry entry; }; static void menu_action_command(struct menu *m, struct menu_entry *me) { struct action_entry *e = container_of(me, struct action_entry, entry); int ret; const char *s = getenv(e->command); /* can be a command as boot */ if (!s) s = e->command; ret = run_command (s, 0); if (ret < 0) udelay(1000000); } static void menu_command_free(struct menu_entry *me) { struct action_entry *e = container_of(me, struct action_entry, entry); free(e->entry.display); free(e->command); free(e); } struct menu_entry *menu_add_command_entry(struct menu *m, char *display, char *command, menu_entry_type type) { struct action_entry *e = calloc(1, sizeof(*e)); int ret; if (!e) return ERR_PTR(-ENOMEM); e->command = strdup(command); e->entry.action = menu_action_command; e->entry.free = menu_command_free; e->entry.type = type; e->entry.display = strdup(display); if (!e->entry.display || !e->command) { ret = -ENOMEM; goto err_free; } ret = menu_add_entry(m, &e->entry); if (ret) goto err_free; return &e->entry; err_free: menu_command_free(&e->entry); return ERR_PTR(ret); } EXPORT_SYMBOL(menu_add_command_entry);