diff options
Diffstat (limited to 'com32/menu/readconfig.c')
-rw-r--r-- | com32/menu/readconfig.c | 162 |
1 files changed, 108 insertions, 54 deletions
diff --git a/com32/menu/readconfig.c b/com32/menu/readconfig.c index 376d8181..db80285d 100644 --- a/com32/menu/readconfig.c +++ b/com32/menu/readconfig.c @@ -24,6 +24,7 @@ #include <syslinux/config.h> #include "menu.h" +#include "hashtbl.h" /* Empty refstring */ const char *empty_string; @@ -74,20 +75,67 @@ const char * const kernel_types[] = { NULL }; +static struct hash_table label_hash; +static struct hash_table menu_hash; + /* - * Search the list of all menus for a specific label + * Set up or return a reference to a specific label. + * Consumes a reference count. */ -static struct menu * -find_menu(const char *label) +static struct menu_entry *label_ref(const char *label) +{ + struct hash_symbol *hs; + struct menu_entry *me; + struct hash_insert hi; + + hs = hash_find(&label_hash, label, &hi); + if (hs) { + refstr_put(label); + return container_of(hs, struct menu_entry, l); + } + + /* Unresolved reference; create a placeholder entry. */ + me = zalloc(sizeof *me); + me->l.key = label; /* Retains the reference */ + + hash_add(&hi, me); + return me; +} + +/* + * Set up or return a reference to a specific menu. + * Consumes a reference count. + */ +static struct menu *menu_ref(const char *label) { + struct hash_symbol *hs; struct menu *m; + struct hash_insert hi; - for (m = menu_list; m; m = m->next) { - if (!strcmp(label, m->label)) - return m; + hs = hash_find(&menu_hash, label, &hi); + if (hs) { + refstr_put(label); + return container_of(hs, struct menu, l); } - return NULL; + /* Unresolved reference; create a placeholder entry. */ + m = zalloc(sizeof *m); + m->l.key = label; /* Retains the reference */ + + hash_add(&hi, m); + return m; +} + +/* + * Search the list of all menus for a specific label + */ +static struct menu * +find_menu(const char *label) +{ + struct menu **mp; + + mp = (struct menu **)hash_find(&menu_hash, label, NULL); + return mp ? *mp : NULL; } #define MAX_LINE 4096 @@ -156,17 +204,23 @@ static struct menu * new_menu(struct menu *parent, { struct menu *m = calloc(1, sizeof(struct menu)); int i; + struct hash_insert hi; m->label = label; m->title = refstr_get(empty_string); + + if (!hash_find(&menu_hash, label, &hi)) + hash_add(&hi, label, m); + + /* We always have a parent entry for bookkeeping purposes */ + m->parent_entry = parent_entry; + parent_entry->action = MA_SUBMENU; + parent_entry->submenu = m; if (parent) { - /* Submenu */ + /* An actual submenu */ m->parent = parent; - m->parent_entry = parent_entry; - parent_entry->action = MA_SUBMENU; - parent_entry->submenu = m; - + for (i = 0; i < MSG_COUNT; i++) m->messages[i] = refstr_get(parent->messages[i]); @@ -221,7 +275,7 @@ struct labeldata { unsigned int menuindent; enum menu_action action; int save; - struct menu *submenu; + struct menu_entry *menuref; }; /* Menu currently being parsed */ @@ -240,9 +294,10 @@ clear_label_data(struct labeldata *ld) memset(ld, 0, sizeof *ld); } -static struct menu_entry *new_entry(struct menu *m) +static struct menu_entry *new_entry(struct menu *m, const char *label) { struct menu_entry *me; + const char *nl; if (m->nentries >= m->nentries_space) { if (!m->nentries_space) @@ -254,9 +309,17 @@ static struct menu_entry *new_entry(struct menu *m) sizeof(struct menu_entry *)); } - me = calloc(1, sizeof(struct menu_entry)); + refstr_get(label); + while (me = label_ref(label), me->action != MA_UNDEF) { + /* Duplicate label, generate synthetic name that can't collide */ + rsprintf(&nl, "%s\n%d", label, ++me->collision); + refstr_put(label); + label = nl; + } + me->menu = m; me->entry = m->nentries; + me->label = label; m->menu_entries[m->nentries++] = me; *all_entries_end = me; all_entries_end = &me->next; @@ -298,7 +361,7 @@ record(struct menu *m, struct labeldata *ld, const char *append) const char *a; char *s; - me = new_entry(m); + me = new_entry(m, ld->label); me->displayname = ld->menulabel ? refstr_get(ld->menulabel) : refstr_get(ld->label); @@ -364,14 +427,9 @@ record(struct menu *m, struct labeldata *ld, const char *append) } break; - case MA_GOTO_UNRES: - case MA_EXIT_UNRES: - me->cmdline = refstr_get(ld->kernel); - break; - case MA_GOTO: case MA_EXIT: - me->submenu = ld->submenu; + me->menuref = ld->menuref; break; default: @@ -404,8 +462,9 @@ static struct menu *end_submenu(void) static struct menu_entry *find_label(const char *str) { - const char *p; + const char *p, op; struct menu_entry *me; + struct menu_entry **lrp; int pos; p = str; @@ -413,14 +472,12 @@ static struct menu_entry *find_label(const char *str) p++; /* p now points to the first byte beyond the kernel name */ - pos = p-str; - - for (me = all_entries; me; me = me->next) { - if (!strncmp(str, me->label, pos) && !me->label[pos]) - return me; - } + op = *p; + *(char *)p = '\0'; /* Ugly, but works in our environment */ + lrp = hash_find(&label_hash, str, NULL); + *(char *)p = op; - return NULL; + return lrp ? *lrp : NULL; } static const char *unlabel(const char *str) @@ -429,25 +486,19 @@ static const char *unlabel(const char *str) const char *p; const char *q; struct menu_entry *me; - int pos; + + me = find_label(str); + if (!me) + return str; + /* Found matching label */ p = str; while ( *p && !my_isspace(*p) ) p++; - /* p now points to the first byte beyond the kernel name */ - pos = p-str; - - for (me = all_entries; me; me = me->next) { - if (!strncmp(str, me->label, pos) && !me->label[pos]) { - /* Found matching label */ - rsprintf(&q, "%s%s", me->cmdline, p); - refstr_put(str); - return q; - } - } - - return str; + rsprintf(&q, "%s%s", me->cmdline, p); + refstr_put(str); + return q; } static const char * @@ -812,21 +863,21 @@ static void parse_config_file(FILE *f) ld.action = MA_QUIT; } else if ( looking_at(p, "goto") ) { if (ld.label) { - ld.action = MA_GOTO_UNRES; - refstr_put(ld.kernel); - ld.kernel = refstrdup(skipspace(p+4)); + ld.action = MA_GOTO; + if (*p) { + ld.menuref = label_ref(skipspace(p+4)); + } } } else if ( looking_at(p, "exit") ) { p = skipspace(p+4); - if (ld.label && m->parent) { + if (ld.label) { + ld.action = MA_EXIT; if (*p) { /* This is really just a goto, except for the marker */ - ld.action = MA_EXIT_UNRES; - refstr_put(ld.kernel); - ld.kernel = refstrdup(p); - } else { - ld.action = MA_EXIT; - ld.submenu = m->parent; + ld.menuref = label_ref(p); + } else if (m->parent) { + /* True exit */ + ld.menuref = m->parent->parententry; } } } else if ( looking_at(p, "start") ) { @@ -1011,6 +1062,9 @@ void parse_configs(char **argv) empty_string = refstrdup(""); + hash_init(&label_hash, HASH_MEDIUM); + hash_init(&menu_hash, HASH_SMALL); + /* Initialize defaults for the root and hidden menus */ hide_menu = new_menu(NULL, NULL, refstrdup(".hidden")); root_menu = new_menu(NULL, NULL, refstrdup(".top")); |