• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
find_menu(const char * label)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 */
strip_caret(const char * str)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 */
looking_at(char * line,const char * kwd)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 */
get_word(char * str,char ** word)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 
new_menu(struct menu * parent,struct menu_entry * parent_entry,const char * label)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 
clear_label_data(struct labeldata * ld)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 
new_entry(struct menu * m)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 
consider_for_hotkey(struct menu * m,struct menu_entry * me)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  */
copy_sysappend_string(char * dst,const char * src)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 
record(struct menu * m,struct labeldata * ld,const char * append)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 
begin_submenu(const char * tag)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 
end_submenu(void)443 static struct menu *end_submenu(void)
444 {
445     return current_menu->parent ? current_menu->parent : current_menu;
446 }
447 
find_label(const char * str)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 
unlabel(const char * str)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 
refdup_word(char ** p)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 
my_isxdigit(char c)508 int my_isxdigit(char c)
509 {
510     unsigned int uc = c;
511 
512     return (uc - '0') < 10 || ((uc | 0x20) - 'a') < 6;
513 }
514 
hexval(char c)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 
hexval2(const char * p)527 unsigned int hexval2(const char *p)
528 {
529     return (hexval(p[0]) << 4) + hexval(p[1]);
530 }
531 
parse_argb(char ** p)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 
is_kernel_type(char * cmdstr,enum kernel_type * type)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 
is_message_name(char * cmdstr,enum message_number * msgnr)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 
is_fkey(char * cmdstr,int * fkeyno)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 
parse_config_file(FILE * f)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 
parse_one_config(const char * filename)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 
resolve_gotos(void)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 
parse_configs(char ** argv)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