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