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
40const 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 */
58static 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
73short uappendlen = 0;		//bytes in append= command
74short ontimeoutlen = 0;		//bytes in ontimeout command
75short onerrorlen = 0;		//bytes in onerror command
76short forceprompt = 0;		//force prompt
77short noescape = 0;		//no escape
78short nocomplete = 0;		//no label completion on TAB key
79short allowimplicit = 1;	//allow implicit kernels
80short allowoptions = 1;		//user-specified options allowed
81short includelevel = 1;		//nesting level
82short defaultlevel = 0;		//the current level of default
83short vkernel = 0;		//have we seen any "label" statements?
84extern short NoHalt;		//idle.c
85
86const char *onerror = NULL;	//"onerror" command line
87const char *ontimeout = NULL;	//"ontimeout" command line
88
89__export const char *default_cmd = NULL;	//"default" command line
90
91/* Empty refstring */
92const char *empty_string;
93
94/* Root menu, starting menu, hidden menu, and list of all menus */
95struct menu *root_menu, *start_menu, *hide_menu, *menu_list, *default_menu;
96
97/* These are global parameters regardless of which menu we're displaying */
98int shiftkey = 0;		/* Only display menu if shift key pressed */
99int hiddenmenu = 0;
100long long totaltimeout = 0;
101unsigned int kbdtimeout = 0;
102
103/* Keep track of global default */
104static int has_ui = 0;		/* DEFAULT only counts if UI is found */
105extern const char *globaldefault;
106static bool menusave = false;	/* True if there is any "menu save" */
107
108/* Linked list of all entires, hidden or not; used by unlabel() */
109static struct menu_entry *all_entries;
110static struct menu_entry **all_entries_end = &all_entries;
111
112static 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 */
128static 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 */
144static 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 */
174static 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
190static 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
246struct 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 */
267static struct menu *current_menu;
268
269static 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
281static 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
307static 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 */
326static 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
347static 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
453static 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
465static struct menu *end_submenu(void)
466{
467    return current_menu->parent ? current_menu->parent : current_menu;
468}
469
470void 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
485struct 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
506static 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
533static 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
546static const char *refdup_word(char **p)
547{
548    return __refdup_word(*p, p);
549}
550
551int my_isxdigit(char c)
552{
553    unsigned int uc = c;
554
555    return (uc - '0') < 10 || ((uc | 0x20) - 'a') < 6;
556}
557
558unsigned 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
570unsigned int hexval2(const char *p)
571{
572    return (hexval(p[0]) << 4) + hexval(p[1]);
573}
574
575uint32_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;
639extern const char *append;
640extern uint16_t PXERetry;
641static struct labeldata ld;
642
643static int parse_main_config(const char *filename);
644
645static 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
661static 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
676extern void get_msg_file(char *);
677
678void 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
734static 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
753extern uint8_t FlowIgnore;
754extern uint8_t FlowInput;
755extern uint8_t FlowOutput;
756extern uint16_t SerialPort;
757extern uint16_t BaudDivisor;
758static uint8_t SerialNotice = 1;
759
760#define DEFAULT_BAUD	9600
761#define BAUD_DIVISOR	115200
762
763extern void sirq_cleanup_nowipe(void);
764extern void sirq_install(void);
765extern void write_serial_str(char *);
766
767extern void loadfont(const char *);
768extern void loadkeys(const char *);
769
770extern char syslinux_banner[];
771extern 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 */
780static 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
809bail:
810    return -1;
811}
812
813static void parse_config_file(FILE * f);
814
815static 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
846bail:
847    close(fd);
848put:
849    refstr_put(file);
850
851}
852
853static 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);
872put:
873    refstr_put(file);
874}
875
876static 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
1436static 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
1471static 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
1491void 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