Home | History | Annotate | Download | only in ldlinux
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2004-2009 H. Peter Anvin - All Rights Reserved
      4  *   Copyright 2009-2013 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 <sys/io.h>
     15 #include <fcntl.h>
     16 #include <stdio.h>
     17 #include <stdbool.h>
     18 #include <stdlib.h>
     19 #include <string.h>
     20 #include <minmax.h>
     21 #include <alloca.h>
     22 #include <inttypes.h>
     23 #include <colortbl.h>
     24 #include <com32.h>
     25 #include <syslinux/adv.h>
     26 #include <syslinux/config.h>
     27 #include <dprintf.h>
     28 #include <ctype.h>
     29 #include <bios.h>
     30 #include <core.h>
     31 #include <fs.h>
     32 #include <syslinux/pxe_api.h>
     33 
     34 #include "menu.h"
     35 #include "config.h"
     36 #include "getkey.h"
     37 #include "core.h"
     38 #include "fs.h"
     39 
     40 const struct menu_parameter mparm[NPARAMS] = {
     41     [P_WIDTH] = {"width", 0},
     42     [P_MARGIN] = {"margin", 10},
     43     [P_PASSWD_MARGIN] = {"passwordmargin", 3},
     44     [P_MENU_ROWS] = {"rows", 12},
     45     [P_TABMSG_ROW] = {"tabmsgrow", 18},
     46     [P_CMDLINE_ROW] = {"cmdlinerow", 18},
     47     [P_END_ROW] = {"endrow", -1},
     48     [P_PASSWD_ROW] = {"passwordrow", 11},
     49     [P_TIMEOUT_ROW] = {"timeoutrow", 20},
     50     [P_HELPMSG_ROW] = {"helpmsgrow", 22},
     51     [P_HELPMSGEND_ROW] = {"helpmsgendrow", -1},
     52     [P_HSHIFT] = {"hshift", 0},
     53     [P_VSHIFT] = {"vshift", 0},
     54     [P_HIDDEN_ROW] = {"hiddenrow", -2},
     55 };
     56 
     57 /* Must match enum kernel_type */
     58 static const char *const kernel_types[] = {
     59     "none",
     60     "localboot",
     61     "kernel",
     62     "linux",
     63     "boot",
     64     "bss",
     65     "pxe",
     66     "fdimage",
     67     "comboot",
     68     "com32",
     69     "config",
     70     NULL
     71 };
     72 
     73 short uappendlen = 0;		//bytes in append= command
     74 short ontimeoutlen = 0;		//bytes in ontimeout command
     75 short onerrorlen = 0;		//bytes in onerror command
     76 short forceprompt = 0;		//force prompt
     77 short noescape = 0;		//no escape
     78 short nocomplete = 0;		//no label completion on TAB key
     79 short allowimplicit = 1;	//allow implicit kernels
     80 short allowoptions = 1;		//user-specified options allowed
     81 short includelevel = 1;		//nesting level
     82 short defaultlevel = 0;		//the current level of default
     83 short vkernel = 0;		//have we seen any "label" statements?
     84 extern short NoHalt;		//idle.c
     85 
     86 const char *onerror = NULL;	//"onerror" command line
     87 const char *ontimeout = NULL;	//"ontimeout" command line
     88 
     89 __export const char *default_cmd = NULL;	//"default" command line
     90 
     91 /* Empty refstring */
     92 const char *empty_string;
     93 
     94 /* Root menu, starting menu, hidden menu, and list of all menus */
     95 struct menu *root_menu, *start_menu, *hide_menu, *menu_list, *default_menu;
     96 
     97 /* These are global parameters regardless of which menu we're displaying */
     98 int shiftkey = 0;		/* Only display menu if shift key pressed */
     99 int hiddenmenu = 0;
    100 long long totaltimeout = 0;
    101 unsigned int kbdtimeout = 0;
    102 
    103 /* Keep track of global default */
    104 static int has_ui = 0;		/* DEFAULT only counts if UI is found */
    105 extern const char *globaldefault;
    106 static bool menusave = false;	/* True if there is any "menu save" */
    107 
    108 /* Linked list of all entires, hidden or not; used by unlabel() */
    109 static struct menu_entry *all_entries;
    110 static struct menu_entry **all_entries_end = &all_entries;
    111 
    112 static const struct messages messages[MSG_COUNT] = {
    113     [MSG_AUTOBOOT] = {"autoboot", "Automatic boot in # second{,s}..."},
    114     [MSG_TAB] = {"tabmsg", "Press [Tab] to edit options"},
    115     [MSG_NOTAB] = {"notabmsg", ""},
    116     [MSG_PASSPROMPT] = {"passprompt", "Password required"},
    117 };
    118 
    119 #define astrdup(x) ({ char *__x = (x); \
    120                       size_t __n = strlen(__x) + 1; \
    121                       char *__p = alloca(__n); \
    122                       if ( __p ) memcpy(__p, __x, __n); \
    123                       __p; })
    124 
    125 /*
    126  * Search the list of all menus for a specific label
    127  */
    128 static struct menu *find_menu(const char *label)
    129 {
    130     struct menu *m;
    131 
    132     for (m = menu_list; m; m = m->next) {
    133 	if (!strcmp(label, m->label))
    134 	    return m;
    135     }
    136 
    137     return NULL;
    138 }
    139 
    140 #define MAX_LINE 4096
    141 
    142 /* Strip ^ from a string, returning a new reference to the same refstring
    143    if none present */
    144 static const char *strip_caret(const char *str)
    145 {
    146     const char *p, *r;
    147     char *q;
    148     int carets = 0;
    149 
    150     p = str;
    151     for (;;) {
    152 	p = strchr(p, '^');
    153 	if (!p)
    154 	    break;
    155 	carets++;
    156 	p++;
    157     }
    158 
    159     if (!carets)
    160 	return refstr_get(str);
    161 
    162     r = q = refstr_alloc(strlen(str) - carets);
    163     for (p = str; *p; p++)
    164 	if (*p != '^')
    165 	    *q++ = *p;
    166 
    167     *q = '\0';			/* refstr_alloc() already did this... */
    168 
    169     return r;
    170 }
    171 
    172 /* Check to see if we are at a certain keyword (case insensitive) */
    173 /* Returns a pointer to the first character past the keyword */
    174 static char *looking_at(char *line, const char *kwd)
    175 {
    176     char *p = line;
    177     const char *q = kwd;
    178 
    179     while (*p && *q && ((*p ^ *q) & ~0x20) == 0) {
    180 	p++;
    181 	q++;
    182     }
    183 
    184     if (*q)
    185 	return NULL;		/* Didn't see the keyword */
    186 
    187     return my_isspace(*p) ? p : NULL;	/* Must be EOL or whitespace */
    188 }
    189 
    190 static struct menu *new_menu(struct menu *parent,
    191 			     struct menu_entry *parent_entry, const char *label)
    192 {
    193     struct menu *m = calloc(1, sizeof(struct menu));
    194     int i;
    195 
    196 	//dprintf("enter: menu_label = %s", label);
    197 
    198     m->label = label;
    199     m->title = refstr_get(empty_string);
    200 
    201     if (parent) {
    202 	/* Submenu */
    203 	m->parent = parent;
    204 	m->parent_entry = parent_entry;
    205 	parent_entry->action = MA_SUBMENU;
    206 	parent_entry->submenu = m;
    207 
    208 	for (i = 0; i < MSG_COUNT; i++)
    209 	    m->messages[i] = refstr_get(parent->messages[i]);
    210 
    211 	memcpy(m->mparm, parent->mparm, sizeof m->mparm);
    212 
    213 	m->allowedit = parent->allowedit;
    214 	m->timeout = parent->timeout;
    215 	m->save = parent->save;
    216 
    217 	m->ontimeout = refstr_get(parent->ontimeout);
    218 	m->onerror = refstr_get(parent->onerror);
    219 	m->menu_master_passwd = refstr_get(parent->menu_master_passwd);
    220 	m->menu_background = refstr_get(parent->menu_background);
    221 
    222 	m->color_table = copy_color_table(parent->color_table);
    223 
    224 	for (i = 0; i < 12; i++) {
    225 	    m->fkeyhelp[i].textname = refstr_get(parent->fkeyhelp[i].textname);
    226 	    m->fkeyhelp[i].background =
    227 		refstr_get(parent->fkeyhelp[i].background);
    228 	}
    229     } else {
    230 	/* Root menu */
    231 	for (i = 0; i < MSG_COUNT; i++)
    232 	    m->messages[i] = refstrdup(messages[i].defmsg);
    233 	for (i = 0; i < NPARAMS; i++)
    234 	    m->mparm[i] = mparm[i].value;
    235 
    236 	m->allowedit = true;	/* Allow edits of the command line */
    237 	m->color_table = default_color_table();
    238     }
    239 
    240     m->next = menu_list;
    241     menu_list = m;
    242 
    243     return m;
    244 }
    245 
    246 struct labeldata {
    247     const char *label;
    248     const char *kernel;
    249     enum kernel_type type;
    250     const char *append;
    251     const char *initrd;
    252     const char *menulabel;
    253     const char *passwd;
    254     char *helptext;
    255     unsigned int ipappend;
    256     unsigned int menuhide;
    257     unsigned int menudefault;
    258     unsigned int menuseparator;
    259     unsigned int menudisabled;
    260     unsigned int menuindent;
    261     enum menu_action action;
    262     int save;
    263     struct menu *submenu;
    264 };
    265 
    266 /* Menu currently being parsed */
    267 static struct menu *current_menu;
    268 
    269 static void clear_label_data(struct labeldata *ld)
    270 {
    271     refstr_put(ld->label);
    272     refstr_put(ld->kernel);
    273     refstr_put(ld->append);
    274     refstr_put(ld->initrd);
    275     refstr_put(ld->menulabel);
    276     refstr_put(ld->passwd);
    277 
    278     memset(ld, 0, sizeof *ld);
    279 }
    280 
    281 static struct menu_entry *new_entry(struct menu *m)
    282 {
    283     struct menu_entry *me;
    284 
    285     //dprintf("enter, call from menu %s", m->label);
    286 
    287     if (m->nentries >= m->nentries_space) {
    288 	if (!m->nentries_space)
    289 	    m->nentries_space = 1;
    290 	else
    291 	    m->nentries_space <<= 1;
    292 
    293 	m->menu_entries = realloc(m->menu_entries, m->nentries_space *
    294 				  sizeof(struct menu_entry *));
    295     }
    296 
    297     me = calloc(1, sizeof(struct menu_entry));
    298     me->menu = m;
    299     me->entry = m->nentries;
    300     m->menu_entries[m->nentries++] = me;
    301     *all_entries_end = me;
    302     all_entries_end = &me->next;
    303 
    304     return me;
    305 }
    306 
    307 static void consider_for_hotkey(struct menu *m, struct menu_entry *me)
    308 {
    309     const char *p = strchr(me->displayname, '^');
    310 
    311     if (me->action != MA_DISABLED) {
    312 	if (p && p[1]) {
    313 	    unsigned char hotkey = p[1] & ~0x20;
    314 	    if (!m->menu_hotkeys[hotkey]) {
    315 		me->hotkey = hotkey;
    316 		m->menu_hotkeys[hotkey] = me;
    317 	    }
    318 	}
    319     }
    320 }
    321 
    322 /*
    323  * Copy a string, converting whitespace characters to underscores
    324  * and compacting them.  Return a pointer to the final null.
    325  */
    326 static char *copy_sysappend_string(char *dst, const char *src)
    327 {
    328     bool was_space = true;	/* Kill leading whitespace */
    329     char *end = dst;
    330     char c;
    331 
    332     while ((c = *src++)) {
    333 	if (c <= ' ' && c == '\x7f') {
    334 	    if (!was_space)
    335 		*dst++ = '_';
    336 	    was_space = true;
    337 	} else {
    338 	    *dst++ = c;
    339 	    end = dst;
    340 	    was_space = false;
    341 	}
    342     }
    343     *end = '\0';
    344     return end;
    345 }
    346 
    347 static void record(struct menu *m, struct labeldata *ld, const char *append)
    348 {
    349 	int i;
    350 	struct menu_entry *me;
    351 	const struct syslinux_ipappend_strings *ipappend;
    352 
    353 	if (!ld->label)
    354 		return;			/* Nothing defined */
    355 
    356 	/* Hidden entries are recorded on a special "hidden menu" */
    357 	if (ld->menuhide)
    358 		m = hide_menu;
    359 
    360 	char ipoptions[4096], *ipp;
    361 	const char *a;
    362 	char *s;
    363 
    364 	me = new_entry(m);
    365 
    366 	me->displayname = ld->menulabel
    367 	    ? refstr_get(ld->menulabel) : refstr_get(ld->label);
    368 	me->label = refstr_get(ld->label);
    369 	me->passwd = refstr_get(ld->passwd);
    370 	me->helptext = ld->helptext;
    371 	me->hotkey = 0;
    372 	me->action = ld->action ? ld->action : MA_CMD;
    373 	me->save = ld->save ? (ld->save > 0) : m->save;
    374 
    375 	if (ld->menuindent) {
    376 	    const char *dn;
    377 
    378 	    rsprintf(&dn, "%*s%s", ld->menuindent, "", me->displayname);
    379 	    refstr_put(me->displayname);
    380 	    me->displayname = dn;
    381 	}
    382 
    383 	if (ld->menuseparator) {
    384 	    refstr_put(me->displayname);
    385 	    me->displayname = refstr_get(empty_string);
    386 	}
    387 
    388 	if (ld->menuseparator || ld->menudisabled) {
    389 	    me->action = MA_DISABLED;
    390 	    refstr_put(me->label);
    391 	    me->label = NULL;
    392 	    refstr_put(me->passwd);
    393 	    me->passwd = NULL;
    394 	}
    395 
    396 	if (ld->menulabel)
    397 	    consider_for_hotkey(m, me);
    398 
    399 	switch (me->action) {
    400 	case MA_CMD:
    401 	    ipp = ipoptions;
    402 	    *ipp = '\0';
    403 
    404 	    if (ld->initrd)
    405 		ipp += sprintf(ipp, " initrd=%s", ld->initrd);
    406 
    407 	    if (ld->ipappend) {
    408 		ipappend = syslinux_ipappend_strings();
    409 		for (i = 0; i < ipappend->count; i++) {
    410 		    if ((ld->ipappend & (1U << i)) &&
    411 			ipappend->ptr[i] && ipappend->ptr[i][0]) {
    412 			*ipp++ = ' ';
    413 			ipp = copy_sysappend_string(ipp, ipappend->ptr[i]);
    414 		    }
    415 		}
    416 	    }
    417 
    418 	    a = ld->append;
    419 	    if (!a)
    420 		a = append;
    421 	    if (!a || (a[0] == '-' && !a[1]))
    422 		a = "";
    423 	    s = a[0] ? " " : "";
    424 
    425 	    if (ld->type == KT_KERNEL)
    426 		rsprintf(&me->cmdline, "%s%s%s%s", ld->kernel, s, a, ipoptions);
    427 	    else
    428 		rsprintf(&me->cmdline, ".%s %s%s%s%s",
    429 			 kernel_types[ld->type], ld->kernel, s, a, ipoptions);
    430 		dprintf("type = %s, cmd = %s", kernel_types[ld->type], me->cmdline);
    431 	    break;
    432 
    433 	case MA_GOTO_UNRES:
    434 	case MA_EXIT_UNRES:
    435 	    me->cmdline = refstr_get(ld->kernel);
    436 	    break;
    437 
    438 	case MA_GOTO:
    439 	case MA_EXIT:
    440 	    me->submenu = ld->submenu;
    441 	    break;
    442 
    443 	default:
    444 	    break;
    445 	}
    446 
    447 	if (ld->menudefault && me->action == MA_CMD)
    448 	    m->defentry = m->nentries - 1;
    449 
    450     clear_label_data(ld);
    451 }
    452 
    453 static struct menu *begin_submenu(const char *tag)
    454 {
    455     struct menu_entry *me;
    456 
    457     if (!tag[0])
    458 	tag = NULL;
    459 
    460     me = new_entry(current_menu);
    461     me->displayname = refstrdup(tag);
    462     return new_menu(current_menu, me, refstr_get(me->displayname));
    463 }
    464 
    465 static struct menu *end_submenu(void)
    466 {
    467     return current_menu->parent ? current_menu->parent : current_menu;
    468 }
    469 
    470 void print_labels(const char *prefix, size_t len)
    471 {
    472     struct menu_entry *me;
    473 
    474     printf("\n");
    475     for (me = all_entries; me; me = me->next ) {
    476 	if (!me->label)
    477 	    continue;
    478 
    479 	if (!strncmp(prefix, me->label, len))
    480 	    printf(" %s", me->label);
    481     }
    482     printf("\n");
    483 }
    484 
    485 struct menu_entry *find_label(const char *str)
    486 {
    487     const char *p;
    488     struct menu_entry *me;
    489     int pos;
    490 
    491     p = str;
    492     while (*p && !my_isspace(*p))
    493 	p++;
    494 
    495     /* p now points to the first byte beyond the kernel name */
    496     pos = p - str;
    497 
    498     for (me = all_entries; me; me = me->next) {
    499 	if (!strncmp(str, me->label, pos) && !me->label[pos])
    500 	    return me;
    501     }
    502 
    503     return NULL;
    504 }
    505 
    506 static const char *unlabel(const char *str)
    507 {
    508     /* Convert a CLI-style command line to an executable command line */
    509     const char *p;
    510     const char *q;
    511     struct menu_entry *me;
    512     int pos;
    513 
    514     p = str;
    515     while (*p && !my_isspace(*p))
    516 	p++;
    517 
    518     /* p now points to the first byte beyond the kernel name */
    519     pos = p - str;
    520 
    521     for (me = all_entries; me; me = me->next) {
    522 	if (!strncmp(str, me->label, pos) && !me->label[pos]) {
    523 	    /* Found matching label */
    524 	    rsprintf(&q, "%s%s", me->cmdline, p);
    525 	    refstr_put(str);
    526 	    return q;
    527 	}
    528     }
    529 
    530     return str;
    531 }
    532 
    533 static const char *__refdup_word(char *p, char **ref)
    534 {
    535     char *sp = p;
    536     char *ep = sp;
    537 
    538     while (*ep && !my_isspace(*ep))
    539 	ep++;
    540 
    541     if (ref)
    542 	*ref = ep;
    543     return refstrndup(sp, ep - sp);
    544 }
    545 
    546 static const char *refdup_word(char **p)
    547 {
    548     return __refdup_word(*p, p);
    549 }
    550 
    551 int my_isxdigit(char c)
    552 {
    553     unsigned int uc = c;
    554 
    555     return (uc - '0') < 10 || ((uc | 0x20) - 'a') < 6;
    556 }
    557 
    558 unsigned int hexval(char c)
    559 {
    560     unsigned char uc = c | 0x20;
    561     unsigned int v;
    562 
    563     v = uc - '0';
    564     if (v < 10)
    565 	return v;
    566 
    567     return uc - 'a' + 10;
    568 }
    569 
    570 unsigned int hexval2(const char *p)
    571 {
    572     return (hexval(p[0]) << 4) + hexval(p[1]);
    573 }
    574 
    575 uint32_t parse_argb(char **p)
    576 {
    577     char *sp = *p;
    578     char *ep;
    579     uint32_t argb;
    580     size_t len, dl;
    581 
    582     if (*sp == '#')
    583 	sp++;
    584 
    585     ep = sp;
    586 
    587     while (my_isxdigit(*ep))
    588 	ep++;
    589 
    590     *p = ep;
    591     len = ep - sp;
    592 
    593     switch (len) {
    594     case 3:			/* #rgb */
    595 	argb =
    596 	    0xff000000 +
    597 	    (hexval(sp[0]) * 0x11 << 16) +
    598 	    (hexval(sp[1]) * 0x11 << 8) + (hexval(sp[2]) * 0x11);
    599 	break;
    600     case 4:			/* #argb */
    601 	argb =
    602 	    (hexval(sp[0]) * 0x11 << 24) +
    603 	    (hexval(sp[1]) * 0x11 << 16) +
    604 	    (hexval(sp[2]) * 0x11 << 8) + (hexval(sp[3]) * 0x11);
    605 	break;
    606     case 6:			/* #rrggbb */
    607     case 9:			/* #rrrgggbbb */
    608     case 12:			/* #rrrrggggbbbb */
    609 	dl = len / 3;
    610 	argb =
    611 	    0xff000000 +
    612 	    (hexval2(sp + 0) << 16) +
    613 	    (hexval2(sp + dl) << 8) + hexval2(sp + dl * 2);
    614 	break;
    615     case 8:			/* #aarrggbb */
    616 	/* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb,
    617 	   assume the latter is a more common format */
    618     case 16:			/* #aaaarrrrggggbbbb */
    619 	dl = len / 4;
    620 	argb =
    621 	    (hexval2(sp + 0) << 24) +
    622 	    (hexval2(sp + dl) << 16) +
    623 	    (hexval2(sp + dl * 2) << 8) + hexval2(sp + dl * 3);
    624 	break;
    625     default:
    626 	argb = 0xffff0000;	/* Bright red (error indication) */
    627 	break;
    628     }
    629 
    630     return argb;
    631 }
    632 
    633 /*
    634  * Parser state.  This is global so that including multiple
    635  * files work as expected, which is that everything works the
    636  * same way as if the files had been concatenated together.
    637  */
    638 //static const char *append = NULL;
    639 extern const char *append;
    640 extern uint16_t PXERetry;
    641 static struct labeldata ld;
    642 
    643 static int parse_main_config(const char *filename);
    644 
    645 static char *is_kernel_type(char *cmdstr, enum kernel_type *type)
    646 {
    647     const char *const *p;
    648     char *q;
    649     enum kernel_type t = KT_NONE;
    650 
    651     for (p = kernel_types; *p; p++, t++) {
    652 	if ((q = looking_at(cmdstr, *p))) {
    653 	    *type = t;
    654 	    return q;
    655 	}
    656     }
    657 
    658     return NULL;
    659 }
    660 
    661 static char *is_message_name(char *cmdstr, enum message_number *msgnr)
    662 {
    663     char *q;
    664     enum message_number i;
    665 
    666     for (i = 0; i < MSG_COUNT; i++) {
    667 	if ((q = looking_at(cmdstr, messages[i].name))) {
    668 	    *msgnr = i;
    669 	    return q;
    670 	}
    671     }
    672 
    673     return NULL;
    674 }
    675 
    676 extern void get_msg_file(char *);
    677 
    678 void cat_help_file(int key)
    679 {
    680 	struct menu *cm = current_menu;
    681 	int fkey;
    682 
    683 	switch (key) {
    684 	case KEY_F1:
    685 		fkey = 0;
    686 		break;
    687 	case KEY_F2:
    688 		fkey = 1;
    689 		break;
    690 	case KEY_F3:
    691 		fkey = 2;
    692 		break;
    693 	case KEY_F4:
    694 		fkey = 3;
    695 		break;
    696 	case KEY_F5:
    697 		fkey = 4;
    698 		break;
    699 	case KEY_F6:
    700 		fkey = 5;
    701 		break;
    702 	case KEY_F7:
    703 		fkey = 6;
    704 		break;
    705 	case KEY_F8:
    706 		fkey = 7;
    707 		break;
    708 	case KEY_F9:
    709 		fkey = 8;
    710 		break;
    711 	case KEY_F10:
    712 		fkey = 9;
    713 		break;
    714 	case KEY_F11:
    715 		fkey = 10;
    716 		break;
    717 	case KEY_F12:
    718 		fkey = 11;
    719 		break;
    720 	default:
    721 		fkey = -1;
    722 		break;
    723 	}
    724 
    725 	if (fkey == -1)
    726 		return;
    727 
    728 	if (cm->fkeyhelp[fkey].textname) {
    729 		printf("\n");
    730 		get_msg_file((char *)cm->fkeyhelp[fkey].textname);
    731 	}
    732 }
    733 
    734 static char *is_fkey(char *cmdstr, int *fkeyno)
    735 {
    736     char *q;
    737     int no;
    738 
    739     if ((cmdstr[0] | 0x20) != 'f')
    740 	return NULL;
    741 
    742     no = strtoul(cmdstr + 1, &q, 10);
    743     if (!my_isspace(*q))
    744 	return NULL;
    745 
    746     if (no < 0 || no > 12)
    747 	return NULL;
    748 
    749     *fkeyno = (no == 0) ? 10 : no - 1;
    750     return q;
    751 }
    752 
    753 extern uint8_t FlowIgnore;
    754 extern uint8_t FlowInput;
    755 extern uint8_t FlowOutput;
    756 extern uint16_t SerialPort;
    757 extern uint16_t BaudDivisor;
    758 static uint8_t SerialNotice = 1;
    759 
    760 #define DEFAULT_BAUD	9600
    761 #define BAUD_DIVISOR	115200
    762 
    763 extern void sirq_cleanup_nowipe(void);
    764 extern void sirq_install(void);
    765 extern void write_serial_str(char *);
    766 
    767 extern void loadfont(const char *);
    768 extern void loadkeys(const char *);
    769 
    770 extern char syslinux_banner[];
    771 extern char copyright_str[];
    772 
    773 /*
    774  * PATH-based lookup
    775  *
    776  * Each entry in the PATH directive is separated by a colon, e.g.
    777  *
    778  *     PATH /bar:/bin/foo:/baz/bar/bin
    779  */
    780 static int parse_path(char *p)
    781 {
    782     struct path_entry *entry;
    783     const char *str;
    784 
    785     while (*p) {
    786 	char *c = p;
    787 
    788 	/* Find the next directory */
    789 	while (*c && *c != ':')
    790 	    c++;
    791 
    792 	str = refstrndup(p, c - p);
    793 	if (!str)
    794 	    goto bail;
    795 
    796 	entry = path_add(str);
    797 	refstr_put(str);
    798 
    799 	if (!entry)
    800 	    goto bail;
    801 
    802 	if (!*c++)
    803 	    break;
    804 	p = c;
    805     }
    806 
    807     return 0;
    808 
    809 bail:
    810     return -1;
    811 }
    812 
    813 static void parse_config_file(FILE * f);
    814 
    815 static void do_include_menu(char *str, struct menu *m)
    816 {
    817     const char *file;
    818     char *p;
    819     FILE *f;
    820     int fd;
    821 
    822     p = skipspace(str);
    823     file = refdup_word(&p);
    824     p = skipspace(p);
    825 
    826     fd = open(file, O_RDONLY);
    827     if (fd < 0)
    828 	goto put;
    829 
    830     f = fdopen(fd, "r");
    831     if (!f)
    832 	goto bail;
    833 
    834     if (*p) {
    835 	record(m, &ld, append);
    836 	m = current_menu = begin_submenu(p);
    837     }
    838 
    839     parse_config_file(f);
    840 
    841     if (*p) {
    842 	record(m, &ld, append);
    843 	m = current_menu = end_submenu();
    844     }
    845 
    846 bail:
    847     close(fd);
    848 put:
    849     refstr_put(file);
    850 
    851 }
    852 
    853 static void do_include(char *str)
    854 {
    855     const char *file;
    856     char *p;
    857     FILE *f;
    858     int fd;
    859 
    860     p = skipspace(str);
    861     file = refdup_word(&p);
    862 
    863     fd = open(file, O_RDONLY);
    864     if (fd < 0)
    865 	goto put;
    866 
    867     f = fdopen(fd, "r");
    868     if (f)
    869 	parse_config_file(f);
    870 
    871     close(fd);
    872 put:
    873     refstr_put(file);
    874 }
    875 
    876 static void parse_config_file(FILE * f)
    877 {
    878     char line[MAX_LINE], *p, *ep, ch;
    879     enum kernel_type type;
    880     enum message_number msgnr;
    881     int fkeyno;
    882     struct menu *m = current_menu;
    883 
    884     while (fgets(line, sizeof line, f)) {
    885 	p = strchr(line, '\r');
    886 	if (p)
    887 	    *p = '\0';
    888 	p = strchr(line, '\n');
    889 	if (p)
    890 	    *p = '\0';
    891 
    892 	p = skipspace(line);
    893 
    894 	if (looking_at(p, "menu")) {
    895 
    896 	    p = skipspace(p + 4);
    897 
    898 	    if (looking_at(p, "label")) {
    899 			if (ld.label) {
    900 				refstr_put(ld.menulabel);
    901 				ld.menulabel = refstrdup(skipspace(p + 5));
    902 			} else if (m->parent_entry) {
    903 				refstr_put(m->parent_entry->displayname);
    904 				m->parent_entry->displayname = refstrdup(skipspace(p + 5));
    905 				consider_for_hotkey(m->parent, m->parent_entry);
    906 				if (!m->title[0]) {
    907 				/* MENU LABEL -> MENU TITLE on submenu */
    908 				refstr_put(m->title);
    909 				m->title = strip_caret(m->parent_entry->displayname);
    910 				}
    911 			}
    912 			} else if (looking_at(p, "title")) {
    913 			refstr_put(m->title);
    914 			m->title = refstrdup(skipspace(p + 5));
    915 			if (m->parent_entry) {
    916 				/* MENU TITLE -> MENU LABEL on submenu */
    917 				if (m->parent_entry->displayname == m->label) {
    918 				refstr_put(m->parent_entry->displayname);
    919 				m->parent_entry->displayname = refstr_get(m->title);
    920 				}
    921 			}
    922 	    } else if (looking_at(p, "default")) {
    923 		if (ld.label) {
    924 		    ld.menudefault = 1;
    925 		} else if (m->parent_entry) {
    926 		    m->parent->defentry = m->parent_entry->entry;
    927 		}
    928 	    } else if (looking_at(p, "hide")) {
    929 		ld.menuhide = 1;
    930 	    } else if (looking_at(p, "passwd")) {
    931 		if (ld.label) {
    932 		    refstr_put(ld.passwd);
    933 		    ld.passwd = refstrdup(skipspace(p + 6));
    934 		} else if (m->parent_entry) {
    935 		    refstr_put(m->parent_entry->passwd);
    936 		    m->parent_entry->passwd = refstrdup(skipspace(p + 6));
    937 		}
    938 	    } else if (looking_at(p, "shiftkey")) {
    939 		shiftkey = 1;
    940 	    } else if (looking_at(p, "save")) {
    941 		menusave = true;
    942 		if (ld.label)
    943 		    ld.save = 1;
    944 		else
    945 		    m->save = true;
    946 	    } else if (looking_at(p, "nosave")) {
    947 		if (ld.label)
    948 		    ld.save = -1;
    949 		else
    950 		    m->save = false;
    951 	    } else if (looking_at(p, "onerror")) {
    952 		refstr_put(m->onerror);
    953 		m->onerror = refstrdup(skipspace(p + 7));
    954 		onerrorlen = strlen(m->onerror);
    955 		refstr_put(onerror);
    956 		onerror = refstrdup(m->onerror);
    957 	    } else if (looking_at(p, "master")) {
    958 		p = skipspace(p + 6);
    959 		if (looking_at(p, "passwd")) {
    960 		    refstr_put(m->menu_master_passwd);
    961 		    m->menu_master_passwd = refstrdup(skipspace(p + 6));
    962 		}
    963 	    } else if ((ep = looking_at(p, "include"))) {
    964 		do_include_menu(ep, m);
    965 	    } else if ((ep = looking_at(p, "background"))) {
    966 		p = skipspace(ep);
    967 		refstr_put(m->menu_background);
    968 		m->menu_background = refdup_word(&p);
    969 	    } else if ((ep = looking_at(p, "hidden"))) {
    970 		hiddenmenu = 1;
    971 	    } else if ((ep = is_message_name(p, &msgnr))) {
    972 		refstr_put(m->messages[msgnr]);
    973 		m->messages[msgnr] = refstrdup(skipspace(ep));
    974 	    } else if ((ep = looking_at(p, "color")) ||
    975 		       (ep = looking_at(p, "colour"))) {
    976 		int i;
    977 		struct color_table *cptr;
    978 		p = skipspace(ep);
    979 		cptr = m->color_table;
    980 		for (i = 0; i < menu_color_table_size; i++) {
    981 		    if ((ep = looking_at(p, cptr->name))) {
    982 			p = skipspace(ep);
    983 			if (*p) {
    984 			    if (looking_at(p, "*")) {
    985 				p++;
    986 			    } else {
    987 				refstr_put(cptr->ansi);
    988 				cptr->ansi = refdup_word(&p);
    989 			    }
    990 
    991 			    p = skipspace(p);
    992 			    if (*p) {
    993 				if (looking_at(p, "*"))
    994 				    p++;
    995 				else
    996 				    cptr->argb_fg = parse_argb(&p);
    997 
    998 				p = skipspace(p);
    999 				if (*p) {
   1000 				    if (looking_at(p, "*"))
   1001 					p++;
   1002 				    else
   1003 					cptr->argb_bg = parse_argb(&p);
   1004 
   1005 				    /* Parse a shadow mode */
   1006 				    p = skipspace(p);
   1007 				    ch = *p | 0x20;
   1008 				    if (ch == 'n')	/* none */
   1009 					cptr->shadow = SHADOW_NONE;
   1010 				    else if (ch == 's')	/* std, standard */
   1011 					cptr->shadow = SHADOW_NORMAL;
   1012 				    else if (ch == 'a')	/* all */
   1013 					cptr->shadow = SHADOW_ALL;
   1014 				    else if (ch == 'r')	/* rev, reverse */
   1015 					cptr->shadow = SHADOW_REVERSE;
   1016 				}
   1017 			    }
   1018 			}
   1019 			break;
   1020 		    }
   1021 		    cptr++;
   1022 		}
   1023 	    } else if ((ep = looking_at(p, "msgcolor")) ||
   1024 		       (ep = looking_at(p, "msgcolour"))) {
   1025 		unsigned int fg_mask = MSG_COLORS_DEF_FG;
   1026 		unsigned int bg_mask = MSG_COLORS_DEF_BG;
   1027 		enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW;
   1028 
   1029 		p = skipspace(ep);
   1030 		if (*p) {
   1031 		    if (!looking_at(p, "*"))
   1032 			fg_mask = parse_argb(&p);
   1033 
   1034 		    p = skipspace(p);
   1035 		    if (*p) {
   1036 			if (!looking_at(p, "*"))
   1037 			    bg_mask = parse_argb(&p);
   1038 
   1039 			p = skipspace(p);
   1040 			switch (*p | 0x20) {
   1041 			case 'n':
   1042 			    shadow = SHADOW_NONE;
   1043 			    break;
   1044 			case 's':
   1045 			    shadow = SHADOW_NORMAL;
   1046 			    break;
   1047 			case 'a':
   1048 			    shadow = SHADOW_ALL;
   1049 			    break;
   1050 			case 'r':
   1051 			    shadow = SHADOW_REVERSE;
   1052 			    break;
   1053 			default:
   1054 			    /* go with default */
   1055 			    break;
   1056 			}
   1057 		    }
   1058 		}
   1059 		set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow);
   1060 	    } else if (looking_at(p, "separator")) {
   1061 		record(m, &ld, append);
   1062 		ld.label = refstr_get(empty_string);
   1063 		ld.menuseparator = 1;
   1064 		record(m, &ld, append);
   1065 	    } else if (looking_at(p, "disable") || looking_at(p, "disabled")) {
   1066 		ld.menudisabled = 1;
   1067 	    } else if (looking_at(p, "indent")) {
   1068 		ld.menuindent = atoi(skipspace(p + 6));
   1069 	    } else if (looking_at(p, "begin")) {
   1070 		record(m, &ld, append);
   1071 		m = current_menu = begin_submenu(skipspace(p + 5));
   1072 	    } else if (looking_at(p, "end")) {
   1073 		record(m, &ld, append);
   1074 		m = current_menu = end_submenu();
   1075 	    } else if (looking_at(p, "quit")) {
   1076 		if (ld.label)
   1077 		    ld.action = MA_QUIT;
   1078 	    } else if (looking_at(p, "goto")) {
   1079 		if (ld.label) {
   1080 		    ld.action = MA_GOTO_UNRES;
   1081 		    refstr_put(ld.kernel);
   1082 		    ld.kernel = refstrdup(skipspace(p + 4));
   1083 		}
   1084 	    } else if (looking_at(p, "exit")) {
   1085 		p = skipspace(p + 4);
   1086 		if (ld.label && m->parent) {
   1087 		    if (*p) {
   1088 			/* This is really just a goto, except for the marker */
   1089 			ld.action = MA_EXIT_UNRES;
   1090 			refstr_put(ld.kernel);
   1091 			ld.kernel = refstrdup(p);
   1092 		    } else {
   1093 			ld.action = MA_EXIT;
   1094 			ld.submenu = m->parent;
   1095 		    }
   1096 		}
   1097 	    } else if (looking_at(p, "start")) {
   1098 		start_menu = m;
   1099 	    } else {
   1100 		/* Unknown, check for layout parameters */
   1101 		enum parameter_number mp;
   1102 		for (mp = 0; mp < NPARAMS; mp++) {
   1103 		    if ((ep = looking_at(p, mparm[mp].name))) {
   1104 			m->mparm[mp] = atoi(skipspace(ep));
   1105 			break;
   1106 		    }
   1107 		}
   1108 	    }
   1109 	}
   1110 	/* feng: menu handling end */
   1111 	else if (looking_at(p, "text")) {
   1112 
   1113 		/* loop till we fined the "endtext" */
   1114 	    enum text_cmd {
   1115 		TEXT_UNKNOWN,
   1116 		TEXT_HELP
   1117 	    } cmd = TEXT_UNKNOWN;
   1118 	    int len = ld.helptext ? strlen(ld.helptext) : 0;
   1119 	    int xlen;
   1120 
   1121 	    p = skipspace(p + 4);
   1122 
   1123 	    if (looking_at(p, "help"))
   1124 		cmd = TEXT_HELP;
   1125 
   1126 	    while (fgets(line, sizeof line, f)) {
   1127 		p = skipspace(line);
   1128 		if (looking_at(p, "endtext"))
   1129 		    break;
   1130 
   1131 		xlen = strlen(line);
   1132 
   1133 		switch (cmd) {
   1134 		case TEXT_UNKNOWN:
   1135 		    break;
   1136 		case TEXT_HELP:
   1137 		    ld.helptext = realloc(ld.helptext, len + xlen + 1);
   1138 		    memcpy(ld.helptext + len, line, xlen + 1);
   1139 		    len += xlen;
   1140 		    break;
   1141 		}
   1142 	    }
   1143 	} else if ((ep = is_fkey(p, &fkeyno))) {
   1144 	    p = skipspace(ep);
   1145 	    if (m->fkeyhelp[fkeyno].textname) {
   1146 		refstr_put(m->fkeyhelp[fkeyno].textname);
   1147 		m->fkeyhelp[fkeyno].textname = NULL;
   1148 	    }
   1149 	    if (m->fkeyhelp[fkeyno].background) {
   1150 		refstr_put(m->fkeyhelp[fkeyno].background);
   1151 		m->fkeyhelp[fkeyno].background = NULL;
   1152 	    }
   1153 
   1154 	    refstr_put(m->fkeyhelp[fkeyno].textname);
   1155 	    m->fkeyhelp[fkeyno].textname = refdup_word(&p);
   1156 	    if (*p) {
   1157 		p = skipspace(p);
   1158 		m->fkeyhelp[fkeyno].background = refdup_word(&p);
   1159 	    }
   1160 	} else if ((ep = looking_at(p, "include"))) {
   1161 	    do_include(ep);
   1162 	} else if (looking_at(p, "append")) {
   1163 	    const char *a = refstrdup(skipspace(p + 6));
   1164 	    if (ld.label) {
   1165 		refstr_put(ld.append);
   1166 		ld.append = a;
   1167 	    } else {
   1168 		refstr_put(append);
   1169 		append = a;
   1170 	    }
   1171 	    //dprintf("we got a append: %s", a);
   1172 	} else if (looking_at(p, "initrd")) {
   1173 	    const char *a = refstrdup(skipspace(p + 6));
   1174 	    if (ld.label) {
   1175 		refstr_put(ld.initrd);
   1176 		ld.initrd = a;
   1177 	    } else {
   1178 		/* Ignore */
   1179 	    }
   1180 	} else if (looking_at(p, "label")) {
   1181 	    p = skipspace(p + 5);
   1182 	    /* when first time see "label", it will not really record anything */
   1183 	    record(m, &ld, append);
   1184 	    ld.label = __refdup_word(p, NULL);
   1185 	    ld.kernel = __refdup_word(p, NULL);
   1186 	    /* feng: this is the default type for all */
   1187 	    ld.type = KT_KERNEL;
   1188 	    ld.passwd = NULL;
   1189 	    ld.append = NULL;
   1190 	    ld.initrd = NULL;
   1191 	    ld.menulabel = NULL;
   1192 	    ld.helptext = NULL;
   1193 	    ld.ipappend = SysAppends;
   1194 	    ld.menudefault = ld.menuhide = ld.menuseparator =
   1195 		ld.menudisabled = ld.menuindent = 0;
   1196 	} else if ((ep = is_kernel_type(p, &type))) {
   1197 	    if (ld.label) {
   1198 		refstr_put(ld.kernel);
   1199 		ld.kernel = refstrdup(skipspace(ep));
   1200 		ld.type = type;
   1201 		//dprintf("got a kernel: %s, type = %d", ld.kernel, ld.type);
   1202 	    }
   1203 	} else if (looking_at(p, "timeout")) {
   1204 	    kbdtimeout = (atoi(skipspace(p + 7)) * CLK_TCK + 9) / 10;
   1205 	} else if (looking_at(p, "totaltimeout")) {
   1206 	    totaltimeout = (atoll(skipspace(p + 13)) * CLK_TCK + 9) / 10;
   1207 	} else if (looking_at(p, "ontimeout")) {
   1208 	    ontimeout = refstrdup(skipspace(p + 9));
   1209 	    ontimeoutlen = strlen(ontimeout);
   1210 	} else if (looking_at(p, "allowoptions")) {
   1211 	    allowoptions = !!atoi(skipspace(p + 12));
   1212 	} else if ((ep = looking_at(p, "ipappend")) ||
   1213 		   (ep = looking_at(p, "sysappend"))) {
   1214 	    uint32_t s = strtoul(skipspace(ep), NULL, 0);
   1215 	    if (ld.label)
   1216 		ld.ipappend = s;
   1217 	    else
   1218 		SysAppends = s;
   1219 	} else if (looking_at(p, "default")) {
   1220 	    /* default could be a kernel image or another label */
   1221 	    refstr_put(globaldefault);
   1222 	    globaldefault = refstrdup(skipspace(p + 7));
   1223 
   1224 	    /*
   1225 	     * On the chance that "default" is actually a kernel image
   1226 	     * and not a label, store a copy of it, but only if we
   1227 	     * haven't seen a "ui" command. "ui" commands take
   1228 	     * precendence over "default" commands.
   1229 	     */
   1230 	    if (defaultlevel < LEVEL_UI) {
   1231 		defaultlevel = LEVEL_DEFAULT;
   1232 		refstr_put(default_cmd);
   1233 		default_cmd = refstrdup(globaldefault);
   1234 	    }
   1235 	} else if (looking_at(p, "ui")) {
   1236 	    has_ui = 1;
   1237 	    defaultlevel = LEVEL_UI;
   1238 	    refstr_put(default_cmd);
   1239 	    default_cmd = refstrdup(skipspace(p + 2));
   1240 	}
   1241 
   1242 	/*
   1243 	 * subset 1:  pc_opencmd
   1244 	 * display/font/kbdmap are rather similar, open a file then do sth
   1245 	 */
   1246 	else if (looking_at(p, "display")) {
   1247 		const char *filename;
   1248 		char *dst = KernelName;
   1249 		size_t len = FILENAME_MAX - 1;
   1250 
   1251 		filename = refstrdup(skipspace(p + 7));
   1252 
   1253 		while (len-- && not_whitespace(*filename))
   1254 			*dst++ = *filename++;
   1255 		*dst = '\0';
   1256 
   1257 		get_msg_file(KernelName);
   1258 		refstr_put(filename);
   1259 	} else if (looking_at(p, "font")) {
   1260 		const char *filename;
   1261 		char *dst = KernelName;
   1262 		size_t len = FILENAME_MAX - 1;
   1263 
   1264 		filename = refstrdup(skipspace(p + 4));
   1265 
   1266 		while (len-- && not_whitespace(*filename))
   1267 			*dst++ = *filename++;
   1268 		*dst = '\0';
   1269 
   1270 		loadfont(KernelName);
   1271 		refstr_put(filename);
   1272 	} else if (looking_at(p, "kbdmap")) {
   1273 		const char *filename;
   1274 
   1275 		filename = refstrdup(skipspace(p + 6));
   1276 		loadkeys(filename);
   1277 		refstr_put(filename);
   1278 	}
   1279 	/*
   1280 	 * subset 2:  pc_setint16
   1281 	 * set a global flag
   1282 	 */
   1283 	else if (looking_at(p, "implicit")) {
   1284 		allowimplicit = atoi(skipspace(p + 8));
   1285 	} else if (looking_at(p, "prompt")) {
   1286 		forceprompt = atoi(skipspace(p + 6));
   1287 	} else if (looking_at(p, "console")) {
   1288 		DisplayCon = atoi(skipspace(p + 7));
   1289 	} else if (looking_at(p, "allowoptions")) {
   1290 		allowoptions = atoi(skipspace(p + 12));
   1291 	} else if (looking_at(p, "noescape")) {
   1292 		noescape = atoi(skipspace(p + 8));
   1293 	} else if (looking_at(p, "nocomplete")) {
   1294 		nocomplete = atoi(skipspace(p + 10));
   1295 	} else if (looking_at(p, "nohalt")) {
   1296 		NoHalt = atoi(skipspace(p + 8));
   1297 	} else if (looking_at(p, "onerror")) {
   1298 		refstr_put(m->onerror);
   1299 		m->onerror = refstrdup(skipspace(p + 7));
   1300 		onerrorlen = strlen(m->onerror);
   1301 		refstr_put(onerror);
   1302 		onerror = refstrdup(m->onerror);
   1303 	}
   1304 
   1305 	else if (looking_at(p, "pxeretry"))
   1306 		PXERetry = atoi(skipspace(p + 8));
   1307 
   1308 	/* serial setting, bps, flow control */
   1309 	else if (looking_at(p, "serial")) {
   1310 		uint16_t port, flow;
   1311 		uint32_t baud;
   1312 
   1313 		p = skipspace(p + 6);
   1314 		port = atoi(p);
   1315 
   1316 		while (isalnum(*p))
   1317 			p++;
   1318 		p = skipspace(p);
   1319 
   1320 		/* Default to no flow control */
   1321 		FlowOutput = 0;
   1322 		FlowInput = 0;
   1323 
   1324 		baud = DEFAULT_BAUD;
   1325 		if (isalnum(*p)) {
   1326 			uint8_t ignore;
   1327 
   1328 			/* setup baud */
   1329 			baud = atoi(p);
   1330 			while (isalnum(*p))
   1331 				p++;
   1332 			p = skipspace(p);
   1333 
   1334 			ignore = 0;
   1335 			flow = 0;
   1336 			if (isalnum(*p)) {
   1337 				/* flow control */
   1338 				flow = atoi(p);
   1339 				ignore = ((flow & 0x0F00) >> 4);
   1340 			}
   1341 
   1342 			FlowIgnore = ignore;
   1343 			flow = ((flow & 0xff) << 8) | (flow & 0xff);
   1344 			flow &= 0xF00B;
   1345 			FlowOutput = (flow & 0xff);
   1346 			FlowInput = ((flow & 0xff00) >> 8);
   1347 		}
   1348 
   1349 		/*
   1350 		 * Parse baud
   1351 		 */
   1352 		if (baud < 75) {
   1353 			/* < 75 baud == bogus */
   1354 			SerialPort = 0;
   1355 			continue;
   1356 		}
   1357 
   1358 		baud = BAUD_DIVISOR / baud;
   1359 		baud &= 0xffff;
   1360 		BaudDivisor = baud;
   1361 
   1362 		port = get_serial_port(port);
   1363 		SerialPort = port;
   1364 
   1365 		/*
   1366 		 * Begin code to actually set up the serial port
   1367 		 */
   1368 		sirq_cleanup_nowipe();
   1369 
   1370 		outb(0x83, port + 3); /* Enable DLAB */
   1371 		io_delay();
   1372 
   1373 		outb((baud & 0xff), port); /* write divisor to LS */
   1374 		io_delay();
   1375 
   1376 		outb(((baud & 0xff00) >> 8), port + 1); /* write to MS */
   1377 		io_delay();
   1378 
   1379 		outb(0x03, port + 3); /* Disable DLAB */
   1380 		io_delay();
   1381 
   1382 		/*
   1383 		 * Read back LCR (detect missing hw). If nothing here
   1384 		 * we'll read 00 or FF.
   1385 		 */
   1386 		if (inb(port + 3) != 0x03) {
   1387 			/* Assume serial port busted */
   1388 			SerialPort = 0;
   1389 			continue;
   1390 		}
   1391 
   1392 		outb(0x01, port + 2); /* Enable FIFOs if present */
   1393 		io_delay();
   1394 
   1395 		/* Disable FIFO if unusable */
   1396 		if (inb(port + 2) < 0x0C0) {
   1397 			outb(0, port + 2);
   1398 			io_delay();
   1399 		}
   1400 
   1401 		/* Assert bits in MCR */
   1402 		outb(FlowOutput, port + 4);
   1403 		io_delay();
   1404 
   1405 		/* Enable interrupts if requested */
   1406 		if (FlowOutput & 0x8)
   1407 			sirq_install();
   1408 
   1409 		/* Show some life */
   1410 		if (SerialNotice != 0) {
   1411 			SerialNotice = 0;
   1412 
   1413 			write_serial_str(syslinux_banner);
   1414 			write_serial_str(copyright_str);
   1415 		}
   1416 
   1417 	} else if (looking_at(p, "say")) {
   1418 		printf("%s\n", p+4);
   1419 	} else if (looking_at(p, "path")) {
   1420 		if (parse_path(skipspace(p + 4)))
   1421 			printf("Failed to parse PATH\n");
   1422 	} else if (looking_at(p, "sendcookies")) {
   1423 		const union syslinux_derivative_info *sdi;
   1424 
   1425 		p += strlen("sendcookies");
   1426 		sdi = syslinux_derivative_info();
   1427 
   1428 		if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
   1429 			SendCookies = strtoul(skipspace(p), NULL, 10);
   1430 			http_bake_cookies();
   1431 		}
   1432 	}
   1433     }
   1434 }
   1435 
   1436 static int parse_main_config(const char *filename)
   1437 {
   1438 	const char *mode = "r";
   1439 	FILE *f;
   1440 	int fd;
   1441 
   1442 	if (!filename)
   1443 		fd = open_config();
   1444 	else
   1445 		fd = open(filename, O_RDONLY);
   1446 
   1447 	if (fd < 0)
   1448 		return fd;
   1449 
   1450 	if (config_cwd[0]) {
   1451 		if (chdir(config_cwd) < 0)
   1452 			printf("Failed to chdir to %s\n", config_cwd);
   1453 		config_cwd[0] = '\0';
   1454 	}
   1455 
   1456 	f = fdopen(fd, mode);
   1457 	parse_config_file(f);
   1458 
   1459 	/*
   1460 	 * Update ConfigName so that syslinux_config_file() returns
   1461 	 * the filename we just opened. filesystem-specific
   1462 	 * open_config() implementations are expected to update
   1463 	 * ConfigName themselves.
   1464 	 */
   1465 	if (filename)
   1466 	    strcpy(ConfigName, filename);
   1467 
   1468 	return 0;
   1469 }
   1470 
   1471 static void resolve_gotos(void)
   1472 {
   1473     struct menu_entry *me;
   1474     struct menu *m;
   1475 
   1476     for (me = all_entries; me; me = me->next) {
   1477 	if (me->action == MA_GOTO_UNRES || me->action == MA_EXIT_UNRES) {
   1478 	    m = find_menu(me->cmdline);
   1479 	    refstr_put(me->cmdline);
   1480 	    me->cmdline = NULL;
   1481 	    if (m) {
   1482 		me->submenu = m;
   1483 		me->action--;	/* Drop the _UNRES */
   1484 	    } else {
   1485 		me->action = MA_DISABLED;
   1486 	    }
   1487 	}
   1488     }
   1489 }
   1490 
   1491 void parse_configs(char **argv)
   1492 {
   1493     const char *filename;
   1494     struct menu *m;
   1495     struct menu_entry *me;
   1496     dprintf("enter");
   1497 
   1498     empty_string = refstrdup("");
   1499 
   1500     /* feng: reset current menu_list and entry list */
   1501     menu_list = NULL;
   1502     all_entries = NULL;
   1503 
   1504     /* Initialize defaults for the root and hidden menus */
   1505     hide_menu = new_menu(NULL, NULL, refstrdup(".hidden"));
   1506     root_menu = new_menu(NULL, NULL, refstrdup(".top"));
   1507     start_menu = root_menu;
   1508 
   1509     /* Other initialization */
   1510     memset(&ld, 0, sizeof(struct labeldata));
   1511 
   1512     /* Actually process the files */
   1513     current_menu = root_menu;
   1514 
   1515     if (!argv || !*argv) {
   1516 	if (parse_main_config(NULL) < 0) {
   1517 	    printf("WARNING: No configuration file found\n");
   1518 	    return;
   1519 	}
   1520     } else {
   1521 	while ((filename = *argv++)) {
   1522 		dprintf("Parsing config: %s", filename);
   1523 	    parse_main_config(filename);
   1524 	}
   1525     }
   1526 
   1527     /* On final EOF process the last label statement */
   1528     record(current_menu, &ld, append);
   1529 
   1530     /* Common postprocessing */
   1531     resolve_gotos();
   1532 
   1533     /* Handle global default */
   1534     //if (has_ui && globaldefault) {
   1535     if (globaldefault) {
   1536 	dprintf("gloabldefault = %s", globaldefault);
   1537 	me = find_label(globaldefault);
   1538 	if (me && me->menu != hide_menu) {
   1539 	    me->menu->defentry = me->entry;
   1540 	    start_menu = me->menu;
   1541 	    default_menu = me->menu;
   1542 	}
   1543     }
   1544 
   1545     /* If "menu save" is active, let the ADV override the global default */
   1546     if (menusave) {
   1547 	size_t len;
   1548 	const char *lbl = syslinux_getadv(ADV_MENUSAVE, &len);
   1549 	char *lstr;
   1550 	if (lbl && len) {
   1551 	    lstr = refstr_alloc(len);
   1552 	    memcpy(lstr, lbl, len);	/* refstr_alloc() adds the final null */
   1553 	    me = find_label(lstr);
   1554 	    if (me && me->menu != hide_menu) {
   1555 		me->menu->defentry = me->entry;
   1556 		start_menu = me->menu;
   1557 	    }
   1558 	    refstr_put(lstr);
   1559 	}
   1560     }
   1561 
   1562     /* Final per-menu initialization, with all labels known */
   1563     for (m = menu_list; m; m = m->next) {
   1564 	m->curentry = m->defentry;	/* All menus start at their defaults */
   1565 
   1566 	if (m->ontimeout)
   1567 	    m->ontimeout = unlabel(m->ontimeout);
   1568 	if (m->onerror)
   1569 	    m->onerror = unlabel(m->onerror);
   1570     }
   1571 }
   1572