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