Home | History | Annotate | Download | only in menu
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2004-2009 H. Peter Anvin - All Rights Reserved
      4  *   Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
      5  *
      6  *   This program is free software; you can redistribute it and/or modify
      7  *   it under the terms of the GNU General Public License as published by
      8  *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
      9  *   Boston MA 02110-1301, USA; either version 2 of the License, or
     10  *   (at your option) any later version; incorporated herein by reference.
     11  *
     12  * ----------------------------------------------------------------------- */
     13 
     14 #include <stdio.h>
     15 #include <stdbool.h>
     16 #include <stdlib.h>
     17 #include <string.h>
     18 #include <ctype.h>
     19 #include <minmax.h>
     20 #include <alloca.h>
     21 #include <inttypes.h>
     22 #include <colortbl.h>
     23 #include <com32.h>
     24 #include <syslinux/adv.h>
     25 #include <syslinux/config.h>
     26 
     27 #include "menu.h"
     28 
     29 /* Empty refstring */
     30 const char *empty_string;
     31 
     32 /* Root menu, starting menu, hidden menu, and list of all menus */
     33 struct menu *root_menu, *start_menu, *hide_menu, *menu_list;
     34 
     35 /* These are global parameters regardless of which menu we're displaying */
     36 int shiftkey = 0;		/* Only display menu if shift key pressed */
     37 int hiddenmenu = 0;
     38 int clearmenu = 0;
     39 long long totaltimeout = 0;
     40 const char *hide_key[KEY_MAX];
     41 
     42 /* Keep track of global default */
     43 static int has_ui = 0;		/* DEFAULT only counts if UI is found */
     44 static const char *globaldefault = NULL;
     45 static bool menusave = false;	/* True if there is any "menu save" */
     46 
     47 /* Linked list of all entires, hidden or not; used by unlabel() */
     48 static struct menu_entry *all_entries;
     49 static struct menu_entry **all_entries_end = &all_entries;
     50 
     51 static const struct messages messages[MSG_COUNT] = {
     52     [MSG_AUTOBOOT] = {"autoboot", "Automatic boot in # second{,s}..."},
     53     [MSG_TAB] = {"tabmsg", "Press [Tab] to edit options"},
     54     [MSG_NOTAB] = {"notabmsg", ""},
     55     [MSG_PASSPROMPT] = {"passprompt", "Password required"},
     56 };
     57 
     58 #define astrdup(x) ({ char *__x = (x); \
     59                       size_t __n = strlen(__x) + 1; \
     60                       char *__p = alloca(__n); \
     61                       if ( __p ) memcpy(__p, __x, __n); \
     62                       __p; })
     63 
     64 /* Must match enum kernel_type */
     65 static const char *const kernel_types[] = {
     66     "none",
     67     "localboot",
     68     "kernel",
     69     "linux",
     70     "boot",
     71     "bss",
     72     "pxe",
     73     "fdimage",
     74     "comboot",
     75     "com32",
     76     "config",
     77     NULL
     78 };
     79 
     80 /*
     81  * Search the list of all menus for a specific label
     82  */
     83 static struct menu *find_menu(const char *label)
     84 {
     85     struct menu *m;
     86 
     87     for (m = menu_list; m; m = m->next) {
     88 	if (!strcmp(label, m->label))
     89 	    return m;
     90     }
     91 
     92     return NULL;
     93 }
     94 
     95 #define MAX_LINE 4096
     96 
     97 /* Strip ^ from a string, returning a new reference to the same refstring
     98    if none present */
     99 static const char *strip_caret(const char *str)
    100 {
    101     const char *p, *r;
    102     char *q;
    103     int carets = 0;
    104 
    105     p = str;
    106     for (;;) {
    107 	p = strchr(p, '^');
    108 	if (!p)
    109 	    break;
    110 	carets++;
    111 	p++;
    112     }
    113 
    114     if (!carets)
    115 	return refstr_get(str);
    116 
    117     r = q = refstr_alloc(strlen(str) - carets);
    118     for (p = str; *p; p++)
    119 	if (*p != '^')
    120 	    *q++ = *p;
    121 
    122     *q = '\0';			/* refstr_alloc() already did this... */
    123 
    124     return r;
    125 }
    126 
    127 /* Check to see if we are at a certain keyword (case insensitive) */
    128 /* Returns a pointer to the first character past the keyword */
    129 static char *looking_at(char *line, const char *kwd)
    130 {
    131     char *p = line;
    132     const char *q = kwd;
    133 
    134     while (*p && *q && ((*p ^ *q) & ~0x20) == 0) {
    135 	p++;
    136 	q++;
    137     }
    138 
    139     if (*q)
    140 	return NULL;		/* Didn't see the keyword */
    141 
    142     return my_isspace(*p) ? p : NULL;	/* Must be EOL or whitespace */
    143 }
    144 
    145 /* Get a single word into a new refstr; advances the input pointer */
    146 static char *get_word(char *str, char **word)
    147 {
    148     char *p = str;
    149     char *q;
    150 
    151     while (*p && !my_isspace(*p))
    152 	p++;
    153 
    154     *word = q = refstr_alloc(p - str);
    155     memcpy(q, str, p - str);
    156     /* refstr_alloc() already inserted a terminating NUL */
    157 
    158     return p;
    159 }
    160 
    161 static struct menu *new_menu(struct menu *parent,
    162 			     struct menu_entry *parent_entry, const char *label)
    163 {
    164     struct menu *m = calloc(1, sizeof(struct menu));
    165     int i;
    166 
    167     m->label = label;
    168     m->title = refstr_get(empty_string);
    169 
    170     if (parent) {
    171 	/* Submenu */
    172 	m->parent = parent;
    173 	m->parent_entry = parent_entry;
    174 	parent_entry->action = MA_SUBMENU;
    175 	parent_entry->submenu = m;
    176 
    177 	for (i = 0; i < MSG_COUNT; i++)
    178 	    m->messages[i] = refstr_get(parent->messages[i]);
    179 
    180 	memcpy(m->mparm, parent->mparm, sizeof m->mparm);
    181 
    182 	m->allowedit = parent->allowedit;
    183 	m->timeout = parent->timeout;
    184 	m->save = parent->save;
    185 	m->immediate = parent->immediate;
    186 
    187 	m->ontimeout = refstr_get(parent->ontimeout);
    188 	m->onerror = refstr_get(parent->onerror);
    189 	m->menu_master_passwd = refstr_get(parent->menu_master_passwd);
    190 	m->menu_background = refstr_get(parent->menu_background);
    191 
    192 	m->color_table = copy_color_table(parent->color_table);
    193 
    194 	for (i = 0; i < 12; i++) {
    195 	    m->fkeyhelp[i].textname = refstr_get(parent->fkeyhelp[i].textname);
    196 	    m->fkeyhelp[i].background =
    197 		refstr_get(parent->fkeyhelp[i].background);
    198 	}
    199     } else {
    200 	/* Root menu */
    201 	for (i = 0; i < MSG_COUNT; i++)
    202 	    m->messages[i] = refstrdup(messages[i].defmsg);
    203 	for (i = 0; i < NPARAMS; i++)
    204 	    m->mparm[i] = mparm[i].value;
    205 
    206 	m->allowedit = true;	/* Allow edits of the command line */
    207 	m->color_table = default_color_table();
    208     }
    209 
    210     m->next = menu_list;
    211     menu_list = m;
    212 
    213     return m;
    214 }
    215 
    216 struct labeldata {
    217     const char *label;
    218     const char *kernel;
    219     enum kernel_type type;
    220     const char *append;
    221     const char *initrd;
    222     const char *menulabel;
    223     const char *passwd;
    224     char *helptext;
    225     unsigned int ipappend;
    226     unsigned int menuhide;
    227     unsigned int menudefault;
    228     unsigned int menuseparator;
    229     unsigned int menudisabled;
    230     unsigned int menuindent;
    231     enum menu_action action;
    232     int save;
    233     int immediate;
    234     struct menu *submenu;
    235 };
    236 
    237 /* Menu currently being parsed */
    238 static struct menu *current_menu;
    239 
    240 static void clear_label_data(struct labeldata *ld)
    241 {
    242     refstr_put(ld->label);
    243     refstr_put(ld->kernel);
    244     refstr_put(ld->append);
    245     refstr_put(ld->initrd);
    246     refstr_put(ld->menulabel);
    247     refstr_put(ld->passwd);
    248 
    249     memset(ld, 0, sizeof *ld);
    250 }
    251 
    252 static struct menu_entry *new_entry(struct menu *m)
    253 {
    254     struct menu_entry *me;
    255 
    256     if (m->nentries >= m->nentries_space) {
    257 	if (!m->nentries_space)
    258 	    m->nentries_space = 1;
    259 	else
    260 	    m->nentries_space <<= 1;
    261 
    262 	m->menu_entries = realloc(m->menu_entries, m->nentries_space *
    263 				  sizeof(struct menu_entry *));
    264     }
    265 
    266     me = calloc(1, sizeof(struct menu_entry));
    267     me->menu = m;
    268     me->entry = m->nentries;
    269     m->menu_entries[m->nentries++] = me;
    270     *all_entries_end = me;
    271     all_entries_end = &me->next;
    272 
    273     return me;
    274 }
    275 
    276 static void consider_for_hotkey(struct menu *m, struct menu_entry *me)
    277 {
    278     const char *p = strchr(me->displayname, '^');
    279 
    280     if (me->action != MA_DISABLED) {
    281 	if (p && p[1]) {
    282 	    unsigned char hotkey = p[1] & ~0x20;
    283 	    if (!m->menu_hotkeys[hotkey]) {
    284 		me->hotkey = hotkey;
    285 		m->menu_hotkeys[hotkey] = me;
    286 	    }
    287 	}
    288     }
    289 }
    290 
    291 /*
    292  * Copy a string, converting whitespace characters to underscores
    293  * and compacting them.  Return a pointer to the final null.
    294  */
    295 static char *copy_sysappend_string(char *dst, const char *src)
    296 {
    297     bool was_space = true;	/* Kill leading whitespace */
    298     char *end = dst;
    299     char c;
    300 
    301     while ((c = *src++)) {
    302 	if (c <= ' ' && c == '\x7f') {
    303 	    if (!was_space)
    304 		*dst++ = '_';
    305 	    was_space = true;
    306 	} else {
    307 	    *dst++ = c;
    308 	    end = dst;
    309 	    was_space = false;
    310 	}
    311     }
    312     *end = '\0';
    313     return end;
    314 }
    315 
    316 static void record(struct menu *m, struct labeldata *ld, const char *append)
    317 {
    318     int i;
    319     struct menu_entry *me;
    320     const struct syslinux_ipappend_strings *ipappend;
    321 
    322     if (!ld->label)
    323 	return;			/* Nothing defined */
    324 
    325     /* Hidden entries are recorded on a special "hidden menu" */
    326     if (ld->menuhide)
    327 	m = hide_menu;
    328 
    329     if (ld->label) {
    330 	char ipoptions[4096], *ipp;
    331 	const char *a;
    332 	char *s;
    333 
    334 	me = new_entry(m);
    335 
    336 	me->displayname = ld->menulabel
    337 	    ? refstr_get(ld->menulabel) : refstr_get(ld->label);
    338 	me->label = refstr_get(ld->label);
    339 	me->passwd = refstr_get(ld->passwd);
    340 	me->helptext = ld->helptext;
    341 	me->hotkey = 0;
    342 	me->action = ld->action ? ld->action : MA_CMD;
    343 	me->save = ld->save ? (ld->save > 0) : m->save;
    344 	me->immediate = ld->immediate ? (ld->immediate > 0) : m->immediate;
    345 
    346 	if (ld->menuindent) {
    347 	    const char *dn;
    348 
    349 	    rsprintf(&dn, "%*s%s", ld->menuindent, "", me->displayname);
    350 	    refstr_put(me->displayname);
    351 	    me->displayname = dn;
    352 	}
    353 
    354 	if (ld->menuseparator) {
    355 	    refstr_put(me->displayname);
    356 	    me->displayname = refstr_get(empty_string);
    357 	}
    358 
    359 	if (ld->menuseparator || ld->menudisabled) {
    360 	    me->action = MA_DISABLED;
    361 	    refstr_put(me->label);
    362 	    me->label = NULL;
    363 	    refstr_put(me->passwd);
    364 	    me->passwd = NULL;
    365 	}
    366 
    367 	if (ld->menulabel)
    368 	    consider_for_hotkey(m, me);
    369 
    370 	switch (me->action) {
    371 	case MA_CMD:
    372 	    ipp = ipoptions;
    373 	    *ipp = '\0';
    374 
    375 	    if (ld->initrd)
    376 		ipp += sprintf(ipp, " initrd=%s", ld->initrd);
    377 
    378 	    if (ld->ipappend) {
    379 		ipappend = syslinux_ipappend_strings();
    380 		for (i = 0; i < ipappend->count; i++) {
    381 		    if ((ld->ipappend & (1U << i)) &&
    382 			ipappend->ptr[i] && ipappend->ptr[i][0]) {
    383 			*ipp++ = ' ';
    384 			ipp = copy_sysappend_string(ipp, ipappend->ptr[i]);
    385 		    }
    386 		}
    387 	    }
    388 
    389 	    a = ld->append;
    390 	    if (!a)
    391 		a = append;
    392 	    if (!a || (a[0] == '-' && !a[1]))
    393 		a = "";
    394 	    s = a[0] ? " " : "";
    395 	    if (ld->type == KT_KERNEL) {
    396 		rsprintf(&me->cmdline, "%s%s%s%s", ld->kernel, s, a, ipoptions);
    397 	    } else {
    398 		rsprintf(&me->cmdline, ".%s %s%s%s%s",
    399 			 kernel_types[ld->type], ld->kernel, s, a, ipoptions);
    400 	    }
    401 	    break;
    402 
    403 	case MA_GOTO_UNRES:
    404 	case MA_EXIT_UNRES:
    405 	    me->cmdline = refstr_get(ld->kernel);
    406 	    break;
    407 
    408 	case MA_GOTO:
    409 	case MA_EXIT:
    410 	    me->submenu = ld->submenu;
    411 	    break;
    412 
    413 	case MA_HELP:
    414 	    me->cmdline = refstr_get(ld->kernel);
    415 	    me->background = refstr_get(ld->append);
    416 	    break;
    417 
    418 	default:
    419 	    break;
    420 	}
    421 
    422 	if (ld->menudefault && (me->action == MA_CMD ||
    423 				me->action == MA_GOTO ||
    424 				me->action == MA_GOTO_UNRES))
    425 	    m->defentry = m->nentries - 1;
    426     }
    427 
    428     clear_label_data(ld);
    429 }
    430 
    431 static struct menu *begin_submenu(const char *tag)
    432 {
    433     struct menu_entry *me;
    434 
    435     if (!tag[0])
    436 	tag = NULL;
    437 
    438     me = new_entry(current_menu);
    439     me->displayname = refstrdup(tag);
    440     return new_menu(current_menu, me, refstr_get(me->displayname));
    441 }
    442 
    443 static struct menu *end_submenu(void)
    444 {
    445     return current_menu->parent ? current_menu->parent : current_menu;
    446 }
    447 
    448 static struct menu_entry *find_label(const char *str)
    449 {
    450     const char *p;
    451     struct menu_entry *me;
    452     int pos;
    453 
    454     p = str;
    455     while (*p && !my_isspace(*p))
    456 	p++;
    457 
    458     /* p now points to the first byte beyond the kernel name */
    459     pos = p - str;
    460 
    461     for (me = all_entries; me; me = me->next) {
    462 	if (!strncmp(str, me->label, pos) && !me->label[pos])
    463 	    return me;
    464     }
    465 
    466     return NULL;
    467 }
    468 
    469 static const char *unlabel(const char *str)
    470 {
    471     /* Convert a CLI-style command line to an executable command line */
    472     const char *p;
    473     const char *q;
    474     struct menu_entry *me;
    475     int pos;
    476 
    477     p = str;
    478     while (*p && !my_isspace(*p))
    479 	p++;
    480 
    481     /* p now points to the first byte beyond the kernel name */
    482     pos = p - str;
    483 
    484     for (me = all_entries; me; me = me->next) {
    485 	if (!strncmp(str, me->label, pos) && !me->label[pos]) {
    486 	    /* Found matching label */
    487 	    rsprintf(&q, "%s%s", me->cmdline, p);
    488 	    refstr_put(str);
    489 	    return q;
    490 	}
    491     }
    492 
    493     return str;
    494 }
    495 
    496 static const char *refdup_word(char **p)
    497 {
    498     char *sp = *p;
    499     char *ep = sp;
    500 
    501     while (*ep && !my_isspace(*ep))
    502 	ep++;
    503 
    504     *p = ep;
    505     return refstrndup(sp, ep - sp);
    506 }
    507 
    508 int my_isxdigit(char c)
    509 {
    510     unsigned int uc = c;
    511 
    512     return (uc - '0') < 10 || ((uc | 0x20) - 'a') < 6;
    513 }
    514 
    515 unsigned int hexval(char c)
    516 {
    517     unsigned char uc = c | 0x20;
    518     unsigned int v;
    519 
    520     v = uc - '0';
    521     if (v < 10)
    522 	return v;
    523 
    524     return uc - 'a' + 10;
    525 }
    526 
    527 unsigned int hexval2(const char *p)
    528 {
    529     return (hexval(p[0]) << 4) + hexval(p[1]);
    530 }
    531 
    532 uint32_t parse_argb(char **p)
    533 {
    534     char *sp = *p;
    535     char *ep;
    536     uint32_t argb;
    537     size_t len, dl;
    538 
    539     if (*sp == '#')
    540 	sp++;
    541 
    542     ep = sp;
    543 
    544     while (my_isxdigit(*ep))
    545 	ep++;
    546 
    547     *p = ep;
    548     len = ep - sp;
    549 
    550     switch (len) {
    551     case 3:			/* #rgb */
    552 	argb =
    553 	    0xff000000 +
    554 	    (hexval(sp[0]) * 0x11 << 16) +
    555 	    (hexval(sp[1]) * 0x11 << 8) + (hexval(sp[2]) * 0x11);
    556 	break;
    557     case 4:			/* #argb */
    558 	argb =
    559 	    (hexval(sp[0]) * 0x11 << 24) +
    560 	    (hexval(sp[1]) * 0x11 << 16) +
    561 	    (hexval(sp[2]) * 0x11 << 8) + (hexval(sp[3]) * 0x11);
    562 	break;
    563     case 6:			/* #rrggbb */
    564     case 9:			/* #rrrgggbbb */
    565     case 12:			/* #rrrrggggbbbb */
    566 	dl = len / 3;
    567 	argb =
    568 	    0xff000000 +
    569 	    (hexval2(sp + 0) << 16) +
    570 	    (hexval2(sp + dl) << 8) + hexval2(sp + dl * 2);
    571 	break;
    572     case 8:			/* #aarrggbb */
    573 	/* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb,
    574 	   assume the latter is a more common format */
    575     case 16:			/* #aaaarrrrggggbbbb */
    576 	dl = len / 4;
    577 	argb =
    578 	    (hexval2(sp + 0) << 24) +
    579 	    (hexval2(sp + dl) << 16) +
    580 	    (hexval2(sp + dl * 2) << 8) + hexval2(sp + dl * 3);
    581 	break;
    582     default:
    583 	argb = 0xffff0000;	/* Bright red (error indication) */
    584 	break;
    585     }
    586 
    587     return argb;
    588 }
    589 
    590 /*
    591  * Parser state.  This is global so that including multiple
    592  * files work as expected, which is that everything works the
    593  * same way as if the files had been concatenated together.
    594  */
    595 static const char *append = NULL;
    596 static unsigned int ipappend = 0;
    597 static struct labeldata ld;
    598 
    599 static int parse_one_config(const char *filename);
    600 
    601 static char *is_kernel_type(char *cmdstr, enum kernel_type *type)
    602 {
    603     const char *const *p;
    604     char *q;
    605     enum kernel_type t = KT_NONE;
    606 
    607     for (p = kernel_types; *p; p++, t++) {
    608 	if ((q = looking_at(cmdstr, *p))) {
    609 	    *type = t;
    610 	    return q;
    611 	}
    612     }
    613 
    614     return NULL;
    615 }
    616 
    617 static char *is_message_name(char *cmdstr, enum message_number *msgnr)
    618 {
    619     char *q;
    620     enum message_number i;
    621 
    622     for (i = 0; i < MSG_COUNT; i++) {
    623 	if ((q = looking_at(cmdstr, messages[i].name))) {
    624 	    *msgnr = i;
    625 	    return q;
    626 	}
    627     }
    628 
    629     return NULL;
    630 }
    631 
    632 static char *is_fkey(char *cmdstr, int *fkeyno)
    633 {
    634     char *q;
    635     int no;
    636 
    637     if ((cmdstr[0] | 0x20) != 'f')
    638 	return NULL;
    639 
    640     no = strtoul(cmdstr + 1, &q, 10);
    641     if (!my_isspace(*q))
    642 	return NULL;
    643 
    644     if (no < 0 || no > 12)
    645 	return NULL;
    646 
    647     *fkeyno = (no == 0) ? 10 : no - 1;
    648     return q;
    649 }
    650 
    651 static void parse_config_file(FILE * f)
    652 {
    653     char line[MAX_LINE], *p, *ep, ch;
    654     enum kernel_type type = -1;
    655     enum message_number msgnr = -1;
    656     int fkeyno = 0;
    657     struct menu *m = current_menu;
    658 
    659     while (fgets(line, sizeof line, f)) {
    660 	p = strchr(line, '\r');
    661 	if (p)
    662 	    *p = '\0';
    663 	p = strchr(line, '\n');
    664 	if (p)
    665 	    *p = '\0';
    666 
    667 	p = skipspace(line);
    668 
    669 	if (looking_at(p, "menu")) {
    670 	    p = skipspace(p + 4);
    671 
    672 	    if (looking_at(p, "label")) {
    673 		if (ld.label) {
    674 		    refstr_put(ld.menulabel);
    675 		    ld.menulabel = refstrdup(skipspace(p + 5));
    676 		} else if (m->parent_entry) {
    677 		    refstr_put(m->parent_entry->displayname);
    678 		    m->parent_entry->displayname = refstrdup(skipspace(p + 5));
    679 		    consider_for_hotkey(m->parent, m->parent_entry);
    680 		    if (!m->title[0]) {
    681 			/* MENU LABEL -> MENU TITLE on submenu */
    682 			refstr_put(m->title);
    683 			m->title = strip_caret(m->parent_entry->displayname);
    684 		    }
    685 		}
    686 	    } else if (looking_at(p, "title")) {
    687 		refstr_put(m->title);
    688 		m->title = refstrdup(skipspace(p + 5));
    689 		if (m->parent_entry) {
    690 		    /* MENU TITLE -> MENU LABEL on submenu */
    691 		    if (m->parent_entry->displayname == m->label) {
    692 			refstr_put(m->parent_entry->displayname);
    693 			m->parent_entry->displayname = refstr_get(m->title);
    694 		    }
    695 		}
    696 	    } else if (looking_at(p, "default")) {
    697 		if (ld.label) {
    698 		    ld.menudefault = 1;
    699 		} else if (m->parent_entry) {
    700 		    m->parent->defentry = m->parent_entry->entry;
    701 		}
    702 	    } else if (looking_at(p, "hide")) {
    703 		ld.menuhide = 1;
    704 	    } else if (looking_at(p, "passwd")) {
    705 		if (ld.label) {
    706 		    refstr_put(ld.passwd);
    707 		    ld.passwd = refstrdup(skipspace(p + 6));
    708 		} else if (m->parent_entry) {
    709 		    refstr_put(m->parent_entry->passwd);
    710 		    m->parent_entry->passwd = refstrdup(skipspace(p + 6));
    711 		}
    712 	    } else if (looking_at(p, "shiftkey")) {
    713 		shiftkey = 1;
    714 	    } else if (looking_at(p, "save")) {
    715 		menusave = true;
    716 		if (ld.label)
    717 		    ld.save = 1;
    718 		else
    719 		    m->save = true;
    720 	    } else if (looking_at(p, "nosave")) {
    721 		if (ld.label)
    722 		    ld.save = -1;
    723 		else
    724 		    m->save = false;
    725 	    } else if (looking_at(p, "immediate")) {
    726 		if (ld.label)
    727 		    ld.immediate = 1;
    728 		else
    729 		    m->immediate = true;
    730 	    } else if (looking_at(p, "noimmediate")) {
    731 		if (ld.label)
    732 		    ld.immediate = -1;
    733 		else
    734 		    m->immediate = false;
    735 	    } else if (looking_at(p, "onerror")) {
    736 		refstr_put(m->onerror);
    737 		m->onerror = refstrdup(skipspace(p + 7));
    738 	    } else if (looking_at(p, "master")) {
    739 		p = skipspace(p + 6);
    740 		if (looking_at(p, "passwd")) {
    741 		    refstr_put(m->menu_master_passwd);
    742 		    m->menu_master_passwd = refstrdup(skipspace(p + 6));
    743 		}
    744 	    } else if ((ep = looking_at(p, "include"))) {
    745 		goto do_include;
    746 	    } else if ((ep = looking_at(p, "background"))) {
    747 		p = skipspace(ep);
    748 		refstr_put(m->menu_background);
    749 		m->menu_background = refdup_word(&p);
    750 	    } else if ((ep = looking_at(p, "hidden"))) {
    751 		hiddenmenu = 1;
    752 	    } else if (looking_at(p, "hiddenkey")) {
    753 		char *key_name, *k, *ek;
    754 		const char *command;
    755 		int key;
    756 		p = get_word(skipspace(p + 9), &key_name);
    757 		command = refstrdup(skipspace(p));
    758 		k = key_name;
    759 		for (;;) {
    760 		    ek = strchr(k+1, ',');
    761 		    if (ek)
    762 			*ek = '\0';
    763 		    key = key_name_to_code(k);
    764 		    if (key >= 0) {
    765 			refstr_put(hide_key[key]);
    766 			hide_key[key] = refstr_get(command);
    767 		    }
    768 		    if (!ek)
    769 			break;
    770 		    k = ek+1;
    771 		}
    772 		refstr_put(key_name);
    773 		refstr_put(command);
    774 	    } else if ((ep = looking_at(p, "clear"))) {
    775 		clearmenu = 1;
    776 	    } else if ((ep = is_message_name(p, &msgnr))) {
    777 		refstr_put(m->messages[msgnr]);
    778 		m->messages[msgnr] = refstrdup(skipspace(ep));
    779 	    } else if ((ep = looking_at(p, "color")) ||
    780 		       (ep = looking_at(p, "colour"))) {
    781 		int i;
    782 		struct color_table *cptr;
    783 		p = skipspace(ep);
    784 		cptr = m->color_table;
    785 		for (i = 0; i < menu_color_table_size; i++) {
    786 		    if ((ep = looking_at(p, cptr->name))) {
    787 			p = skipspace(ep);
    788 			if (*p) {
    789 			    if (looking_at(p, "*")) {
    790 				p++;
    791 			    } else {
    792 				refstr_put(cptr->ansi);
    793 				cptr->ansi = refdup_word(&p);
    794 			    }
    795 
    796 			    p = skipspace(p);
    797 			    if (*p) {
    798 				if (looking_at(p, "*"))
    799 				    p++;
    800 				else
    801 				    cptr->argb_fg = parse_argb(&p);
    802 
    803 				p = skipspace(p);
    804 				if (*p) {
    805 				    if (looking_at(p, "*"))
    806 					p++;
    807 				    else
    808 					cptr->argb_bg = parse_argb(&p);
    809 
    810 				    /* Parse a shadow mode */
    811 				    p = skipspace(p);
    812 				    ch = *p | 0x20;
    813 				    if (ch == 'n')	/* none */
    814 					cptr->shadow = SHADOW_NONE;
    815 				    else if (ch == 's')	/* std, standard */
    816 					cptr->shadow = SHADOW_NORMAL;
    817 				    else if (ch == 'a')	/* all */
    818 					cptr->shadow = SHADOW_ALL;
    819 				    else if (ch == 'r')	/* rev, reverse */
    820 					cptr->shadow = SHADOW_REVERSE;
    821 				}
    822 			    }
    823 			}
    824 			break;
    825 		    }
    826 		    cptr++;
    827 		}
    828 	    } else if ((ep = looking_at(p, "msgcolor")) ||
    829 		       (ep = looking_at(p, "msgcolour"))) {
    830 		unsigned int fg_mask = MSG_COLORS_DEF_FG;
    831 		unsigned int bg_mask = MSG_COLORS_DEF_BG;
    832 		enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW;
    833 
    834 		p = skipspace(ep);
    835 		if (*p) {
    836 		    if (!looking_at(p, "*"))
    837 			fg_mask = parse_argb(&p);
    838 
    839 		    p = skipspace(p);
    840 		    if (*p) {
    841 			if (!looking_at(p, "*"))
    842 			    bg_mask = parse_argb(&p);
    843 
    844 			p = skipspace(p);
    845 			switch (*p | 0x20) {
    846 			case 'n':
    847 			    shadow = SHADOW_NONE;
    848 			    break;
    849 			case 's':
    850 			    shadow = SHADOW_NORMAL;
    851 			    break;
    852 			case 'a':
    853 			    shadow = SHADOW_ALL;
    854 			    break;
    855 			case 'r':
    856 			    shadow = SHADOW_REVERSE;
    857 			    break;
    858 			default:
    859 			    /* go with default */
    860 			    break;
    861 			}
    862 		    }
    863 		}
    864 		set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow);
    865 	    } else if (looking_at(p, "separator")) {
    866 		record(m, &ld, append);
    867 		ld.label = refstr_get(empty_string);
    868 		ld.menuseparator = 1;
    869 		record(m, &ld, append);
    870 	    } else if (looking_at(p, "disable") || looking_at(p, "disabled")) {
    871 		ld.menudisabled = 1;
    872 	    } else if (looking_at(p, "indent")) {
    873 		ld.menuindent = atoi(skipspace(p + 6));
    874 	    } else if (looking_at(p, "begin")) {
    875 		record(m, &ld, append);
    876 		m = current_menu = begin_submenu(skipspace(p + 5));
    877 	    } else if (looking_at(p, "end")) {
    878 		record(m, &ld, append);
    879 		m = current_menu = end_submenu();
    880 	    } else if (looking_at(p, "quit")) {
    881 		if (ld.label)
    882 		    ld.action = MA_QUIT;
    883 	    } else if (looking_at(p, "goto")) {
    884 		if (ld.label) {
    885 		    ld.action = MA_GOTO_UNRES;
    886 		    refstr_put(ld.kernel);
    887 		    ld.kernel = refstrdup(skipspace(p + 4));
    888 		}
    889 	    } else if (looking_at(p, "exit")) {
    890 		p = skipspace(p + 4);
    891 		if (ld.label && m->parent) {
    892 		    if (*p) {
    893 			/* This is really just a goto, except for the marker */
    894 			ld.action = MA_EXIT_UNRES;
    895 			refstr_put(ld.kernel);
    896 			ld.kernel = refstrdup(p);
    897 		    } else {
    898 			ld.action = MA_EXIT;
    899 			ld.submenu = m->parent;
    900 		    }
    901 		}
    902 	    } else if (looking_at(p, "start")) {
    903 		start_menu = m;
    904 	    } else if (looking_at(p, "help")) {
    905 		if (ld.label) {
    906 		    ld.action = MA_HELP;
    907 		    p = skipspace(p + 4);
    908 
    909 		    refstr_put(ld.kernel);
    910 		    ld.kernel = refdup_word(&p);
    911 
    912 		    if (ld.append) {
    913 			refstr_put(ld.append);
    914 			ld.append = NULL;
    915 		    }
    916 
    917 		    if (*p) {
    918 			p = skipspace(p);
    919 			ld.append = refdup_word(&p); /* Background */
    920 		    }
    921 		}
    922 	    } else if ((ep = looking_at(p, "resolution"))) {
    923 		int x, y;
    924 		x = strtoul(ep, &ep, 0);
    925 		y = strtoul(skipspace(ep), NULL, 0);
    926 		set_resolution(x, y);
    927 	    } else {
    928 		/* Unknown, check for layout parameters */
    929 		enum parameter_number mp;
    930 		for (mp = 0; mp < NPARAMS; mp++) {
    931 		    if ((ep = looking_at(p, mparm[mp].name))) {
    932 			m->mparm[mp] = atoi(skipspace(ep));
    933 			break;
    934 		    }
    935 		}
    936 	    }
    937 	} else if (looking_at(p, "text")) {
    938 	    enum text_cmd {
    939 		TEXT_UNKNOWN,
    940 		TEXT_HELP
    941 	    } cmd = TEXT_UNKNOWN;
    942 	    int len = ld.helptext ? strlen(ld.helptext) : 0;
    943 	    int xlen;
    944 
    945 	    p = skipspace(p + 4);
    946 
    947 	    if (looking_at(p, "help"))
    948 		cmd = TEXT_HELP;
    949 
    950 	    while (fgets(line, sizeof line, f)) {
    951 		p = skipspace(line);
    952 		if (looking_at(p, "endtext"))
    953 		    break;
    954 
    955 		xlen = strlen(line);
    956 
    957 		switch (cmd) {
    958 		case TEXT_UNKNOWN:
    959 		    break;
    960 		case TEXT_HELP:
    961 		    ld.helptext = realloc(ld.helptext, len + xlen + 1);
    962 		    memcpy(ld.helptext + len, line, xlen + 1);
    963 		    len += xlen;
    964 		    break;
    965 		}
    966 	    }
    967 	} else if ((ep = is_fkey(p, &fkeyno))) {
    968 	    p = skipspace(ep);
    969 	    if (m->fkeyhelp[fkeyno].textname) {
    970 		refstr_put(m->fkeyhelp[fkeyno].textname);
    971 		m->fkeyhelp[fkeyno].textname = NULL;
    972 	    }
    973 	    if (m->fkeyhelp[fkeyno].background) {
    974 		refstr_put(m->fkeyhelp[fkeyno].background);
    975 		m->fkeyhelp[fkeyno].background = NULL;
    976 	    }
    977 
    978 	    refstr_put(m->fkeyhelp[fkeyno].textname);
    979 	    m->fkeyhelp[fkeyno].textname = refdup_word(&p);
    980 	    if (*p) {
    981 		p = skipspace(p);
    982 		m->fkeyhelp[fkeyno].background = refdup_word(&p);
    983 	    }
    984 	} else if ((ep = looking_at(p, "include"))) {
    985 do_include:
    986 	    {
    987 		const char *file;
    988 		p = skipspace(ep);
    989 		file = refdup_word(&p);
    990 		p = skipspace(p);
    991 		if (*p) {
    992 		    record(m, &ld, append);
    993 		    m = current_menu = begin_submenu(p);
    994 		    parse_one_config(file);
    995 		    record(m, &ld, append);
    996 		    m = current_menu = end_submenu();
    997 		} else {
    998 		    parse_one_config(file);
    999 		}
   1000 		refstr_put(file);
   1001 	    }
   1002 	} else if (looking_at(p, "append")) {
   1003 	    const char *a = refstrdup(skipspace(p + 6));
   1004 	    if (ld.label) {
   1005 		refstr_put(ld.append);
   1006 		ld.append = a;
   1007 	    } else {
   1008 		refstr_put(append);
   1009 		append = a;
   1010 	    }
   1011 	} else if (looking_at(p, "initrd")) {
   1012 	    const char *a = refstrdup(skipspace(p + 6));
   1013 	    if (ld.label) {
   1014 		refstr_put(ld.initrd);
   1015 		ld.initrd = a;
   1016 	    } else {
   1017 		/* Ignore */
   1018 	    }
   1019 	} else if (looking_at(p, "label")) {
   1020 	    p = skipspace(p + 5);
   1021 	    record(m, &ld, append);
   1022 	    ld.label = refstrdup(p);
   1023 	    ld.kernel = refstrdup(p);
   1024 	    ld.type = KT_KERNEL;
   1025 	    ld.passwd = NULL;
   1026 	    ld.append = NULL;
   1027 	    ld.initrd = NULL;
   1028 	    ld.menulabel = NULL;
   1029 	    ld.helptext = NULL;
   1030 	    ld.ipappend = ipappend;
   1031 	    ld.menudefault = ld.menuhide = ld.menuseparator =
   1032 		ld.menudisabled = ld.menuindent = 0;
   1033 	} else if ((ep = is_kernel_type(p, &type))) {
   1034 	    if (ld.label) {
   1035 		refstr_put(ld.kernel);
   1036 		ld.kernel = refstrdup(skipspace(ep));
   1037 		ld.type = type;
   1038 	    }
   1039 	} else if (looking_at(p, "timeout")) {
   1040 	    m->timeout = (atoi(skipspace(p + 7)) * CLK_TCK + 9) / 10;
   1041 	} else if (looking_at(p, "totaltimeout")) {
   1042 	    totaltimeout = (atoll(skipspace(p + 13)) * CLK_TCK + 9) / 10;
   1043 	} else if (looking_at(p, "ontimeout")) {
   1044 	    m->ontimeout = refstrdup(skipspace(p + 9));
   1045 	} else if (looking_at(p, "allowoptions")) {
   1046 	    m->allowedit = !!atoi(skipspace(p + 12));
   1047 	} else if ((ep = looking_at(p, "ipappend")) ||
   1048 		   (ep = looking_at(p, "sysappend"))) {
   1049 	    uint32_t s = strtoul(skipspace(ep), NULL, 0);
   1050 	    if (ld.label)
   1051 		ld.ipappend = s;
   1052 	    else
   1053 		ipappend = s;
   1054 	} else if (looking_at(p, "default")) {
   1055 	    refstr_put(globaldefault);
   1056 	    globaldefault = refstrdup(skipspace(p + 7));
   1057 	} else if (looking_at(p, "ui")) {
   1058 	    has_ui = 1;
   1059 	}
   1060     }
   1061 }
   1062 
   1063 static int parse_one_config(const char *filename)
   1064 {
   1065     FILE *f;
   1066 
   1067     if (!strcmp(filename, "~"))
   1068 	filename = syslinux_config_file();
   1069 
   1070     dprintf("Opening config file: %s ", filename);
   1071 
   1072     f = fopen(filename, "r");
   1073     dprintf("%s\n", f ? "ok" : "failed");
   1074 
   1075     if (!f)
   1076 	return -1;
   1077 
   1078     parse_config_file(f);
   1079     fclose(f);
   1080 
   1081     return 0;
   1082 }
   1083 
   1084 static void resolve_gotos(void)
   1085 {
   1086     struct menu_entry *me;
   1087     struct menu *m;
   1088 
   1089     for (me = all_entries; me; me = me->next) {
   1090 	if (me->action == MA_GOTO_UNRES || me->action == MA_EXIT_UNRES) {
   1091 	    m = find_menu(me->cmdline);
   1092 	    refstr_put(me->cmdline);
   1093 	    me->cmdline = NULL;
   1094 	    if (m) {
   1095 		me->submenu = m;
   1096 		me->action--;	/* Drop the _UNRES */
   1097 	    } else {
   1098 		me->action = MA_DISABLED;
   1099 	    }
   1100 	}
   1101     }
   1102 }
   1103 
   1104 void parse_configs(char **argv)
   1105 {
   1106     const char *filename;
   1107     struct menu *m;
   1108     struct menu_entry *me;
   1109     int k;
   1110 
   1111     empty_string = refstrdup("");
   1112 
   1113     /* Initialize defaults for the root and hidden menus */
   1114     hide_menu = new_menu(NULL, NULL, refstrdup(".hidden"));
   1115     root_menu = new_menu(NULL, NULL, refstrdup(".top"));
   1116     start_menu = root_menu;
   1117 
   1118     /* Other initialization */
   1119     memset(&ld, 0, sizeof(struct labeldata));
   1120 
   1121     /* Actually process the files */
   1122     current_menu = root_menu;
   1123     if (!*argv) {
   1124 	parse_one_config("~");
   1125     } else {
   1126 	while ((filename = *argv++))
   1127 	    parse_one_config(filename);
   1128     }
   1129 
   1130     /* On final EOF process the last label statement */
   1131     record(current_menu, &ld, append);
   1132 
   1133     /* Common postprocessing */
   1134     resolve_gotos();
   1135 
   1136     /* Handle global default */
   1137     if (has_ui && globaldefault) {
   1138 	me = find_label(globaldefault);
   1139 	if (me && me->menu != hide_menu) {
   1140 	    me->menu->defentry = me->entry;
   1141 	    start_menu = me->menu;
   1142 	}
   1143     }
   1144 
   1145     /* If "menu save" is active, let the ADV override the global default */
   1146     if (menusave) {
   1147 	size_t len;
   1148 	const char *lbl = syslinux_getadv(ADV_MENUSAVE, &len);
   1149 	char *lstr;
   1150 	if (lbl && len) {
   1151 	    lstr = refstr_alloc(len);
   1152 	    memcpy(lstr, lbl, len);	/* refstr_alloc() adds the final null */
   1153 	    me = find_label(lstr);
   1154 	    if (me && me->menu != hide_menu) {
   1155 		me->menu->defentry = me->entry;
   1156 		start_menu = me->menu;
   1157 	    }
   1158 	    refstr_put(lstr);
   1159 	}
   1160     }
   1161 
   1162     /* Final per-menu initialization, with all labels known */
   1163     for (m = menu_list; m; m = m->next) {
   1164 	m->curentry = m->defentry;	/* All menus start at their defaults */
   1165 
   1166 	if (m->ontimeout)
   1167 	    m->ontimeout = unlabel(m->ontimeout);
   1168 	if (m->onerror)
   1169 	    m->onerror = unlabel(m->onerror);
   1170     }
   1171 
   1172     /* Final global initialization, with all labels known */
   1173     for (k = 0; k < KEY_MAX; k++) {
   1174 	if (hide_key[k])
   1175 	    hide_key[k] = unlabel(hide_key[k]);
   1176     }
   1177 }
   1178