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