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