1 /* ----------------------------------------------------------------------- *
2 *
3 * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2013 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 <sys/io.h>
15 #include <fcntl.h>
16 #include <stdio.h>
17 #include <stdbool.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <minmax.h>
21 #include <alloca.h>
22 #include <inttypes.h>
23 #include <colortbl.h>
24 #include <com32.h>
25 #include <syslinux/adv.h>
26 #include <syslinux/config.h>
27 #include <dprintf.h>
28 #include <ctype.h>
29 #include <bios.h>
30 #include <core.h>
31 #include <fs.h>
32 #include <syslinux/pxe_api.h>
33
34 #include "menu.h"
35 #include "config.h"
36 #include "getkey.h"
37 #include "core.h"
38 #include "fs.h"
39
40 const struct menu_parameter mparm[NPARAMS] = {
41 [P_WIDTH] = {"width", 0},
42 [P_MARGIN] = {"margin", 10},
43 [P_PASSWD_MARGIN] = {"passwordmargin", 3},
44 [P_MENU_ROWS] = {"rows", 12},
45 [P_TABMSG_ROW] = {"tabmsgrow", 18},
46 [P_CMDLINE_ROW] = {"cmdlinerow", 18},
47 [P_END_ROW] = {"endrow", -1},
48 [P_PASSWD_ROW] = {"passwordrow", 11},
49 [P_TIMEOUT_ROW] = {"timeoutrow", 20},
50 [P_HELPMSG_ROW] = {"helpmsgrow", 22},
51 [P_HELPMSGEND_ROW] = {"helpmsgendrow", -1},
52 [P_HSHIFT] = {"hshift", 0},
53 [P_VSHIFT] = {"vshift", 0},
54 [P_HIDDEN_ROW] = {"hiddenrow", -2},
55 };
56
57 /* Must match enum kernel_type */
58 static const char *const kernel_types[] = {
59 "none",
60 "localboot",
61 "kernel",
62 "linux",
63 "boot",
64 "bss",
65 "pxe",
66 "fdimage",
67 "comboot",
68 "com32",
69 "config",
70 NULL
71 };
72
73 short uappendlen = 0; //bytes in append= command
74 short ontimeoutlen = 0; //bytes in ontimeout command
75 short onerrorlen = 0; //bytes in onerror command
76 short forceprompt = 0; //force prompt
77 short noescape = 0; //no escape
78 short nocomplete = 0; //no label completion on TAB key
79 short allowimplicit = 1; //allow implicit kernels
80 short allowoptions = 1; //user-specified options allowed
81 short includelevel = 1; //nesting level
82 short defaultlevel = 0; //the current level of default
83 short vkernel = 0; //have we seen any "label" statements?
84 extern short NoHalt; //idle.c
85
86 const char *onerror = NULL; //"onerror" command line
87 const char *ontimeout = NULL; //"ontimeout" command line
88
89 __export const char *default_cmd = NULL; //"default" command line
90
91 /* Empty refstring */
92 const char *empty_string;
93
94 /* Root menu, starting menu, hidden menu, and list of all menus */
95 struct menu *root_menu, *start_menu, *hide_menu, *menu_list, *default_menu;
96
97 /* These are global parameters regardless of which menu we're displaying */
98 int shiftkey = 0; /* Only display menu if shift key pressed */
99 int hiddenmenu = 0;
100 long long totaltimeout = 0;
101 unsigned int kbdtimeout = 0;
102
103 /* Keep track of global default */
104 static int has_ui = 0; /* DEFAULT only counts if UI is found */
105 extern const char *globaldefault;
106 static bool menusave = false; /* True if there is any "menu save" */
107
108 /* Linked list of all entires, hidden or not; used by unlabel() */
109 static struct menu_entry *all_entries;
110 static struct menu_entry **all_entries_end = &all_entries;
111
112 static const struct messages messages[MSG_COUNT] = {
113 [MSG_AUTOBOOT] = {"autoboot", "Automatic boot in # second{,s}..."},
114 [MSG_TAB] = {"tabmsg", "Press [Tab] to edit options"},
115 [MSG_NOTAB] = {"notabmsg", ""},
116 [MSG_PASSPROMPT] = {"passprompt", "Password required"},
117 };
118
119 #define astrdup(x) ({ char *__x = (x); \
120 size_t __n = strlen(__x) + 1; \
121 char *__p = alloca(__n); \
122 if ( __p ) memcpy(__p, __x, __n); \
123 __p; })
124
125 /*
126 * Search the list of all menus for a specific label
127 */
find_menu(const char * label)128 static struct menu *find_menu(const char *label)
129 {
130 struct menu *m;
131
132 for (m = menu_list; m; m = m->next) {
133 if (!strcmp(label, m->label))
134 return m;
135 }
136
137 return NULL;
138 }
139
140 #define MAX_LINE 4096
141
142 /* Strip ^ from a string, returning a new reference to the same refstring
143 if none present */
strip_caret(const char * str)144 static const char *strip_caret(const char *str)
145 {
146 const char *p, *r;
147 char *q;
148 int carets = 0;
149
150 p = str;
151 for (;;) {
152 p = strchr(p, '^');
153 if (!p)
154 break;
155 carets++;
156 p++;
157 }
158
159 if (!carets)
160 return refstr_get(str);
161
162 r = q = refstr_alloc(strlen(str) - carets);
163 for (p = str; *p; p++)
164 if (*p != '^')
165 *q++ = *p;
166
167 *q = '\0'; /* refstr_alloc() already did this... */
168
169 return r;
170 }
171
172 /* Check to see if we are at a certain keyword (case insensitive) */
173 /* Returns a pointer to the first character past the keyword */
looking_at(char * line,const char * kwd)174 static char *looking_at(char *line, const char *kwd)
175 {
176 char *p = line;
177 const char *q = kwd;
178
179 while (*p && *q && ((*p ^ *q) & ~0x20) == 0) {
180 p++;
181 q++;
182 }
183
184 if (*q)
185 return NULL; /* Didn't see the keyword */
186
187 return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */
188 }
189
new_menu(struct menu * parent,struct menu_entry * parent_entry,const char * label)190 static struct menu *new_menu(struct menu *parent,
191 struct menu_entry *parent_entry, const char *label)
192 {
193 struct menu *m = calloc(1, sizeof(struct menu));
194 int i;
195
196 //dprintf("enter: menu_label = %s", label);
197
198 m->label = label;
199 m->title = refstr_get(empty_string);
200
201 if (parent) {
202 /* Submenu */
203 m->parent = parent;
204 m->parent_entry = parent_entry;
205 parent_entry->action = MA_SUBMENU;
206 parent_entry->submenu = m;
207
208 for (i = 0; i < MSG_COUNT; i++)
209 m->messages[i] = refstr_get(parent->messages[i]);
210
211 memcpy(m->mparm, parent->mparm, sizeof m->mparm);
212
213 m->allowedit = parent->allowedit;
214 m->timeout = parent->timeout;
215 m->save = parent->save;
216
217 m->ontimeout = refstr_get(parent->ontimeout);
218 m->onerror = refstr_get(parent->onerror);
219 m->menu_master_passwd = refstr_get(parent->menu_master_passwd);
220 m->menu_background = refstr_get(parent->menu_background);
221
222 m->color_table = copy_color_table(parent->color_table);
223
224 for (i = 0; i < 12; i++) {
225 m->fkeyhelp[i].textname = refstr_get(parent->fkeyhelp[i].textname);
226 m->fkeyhelp[i].background =
227 refstr_get(parent->fkeyhelp[i].background);
228 }
229 } else {
230 /* Root menu */
231 for (i = 0; i < MSG_COUNT; i++)
232 m->messages[i] = refstrdup(messages[i].defmsg);
233 for (i = 0; i < NPARAMS; i++)
234 m->mparm[i] = mparm[i].value;
235
236 m->allowedit = true; /* Allow edits of the command line */
237 m->color_table = default_color_table();
238 }
239
240 m->next = menu_list;
241 menu_list = m;
242
243 return m;
244 }
245
246 struct labeldata {
247 const char *label;
248 const char *kernel;
249 enum kernel_type type;
250 const char *append;
251 const char *initrd;
252 const char *menulabel;
253 const char *passwd;
254 char *helptext;
255 unsigned int ipappend;
256 unsigned int menuhide;
257 unsigned int menudefault;
258 unsigned int menuseparator;
259 unsigned int menudisabled;
260 unsigned int menuindent;
261 enum menu_action action;
262 int save;
263 struct menu *submenu;
264 };
265
266 /* Menu currently being parsed */
267 static struct menu *current_menu;
268
clear_label_data(struct labeldata * ld)269 static void clear_label_data(struct labeldata *ld)
270 {
271 refstr_put(ld->label);
272 refstr_put(ld->kernel);
273 refstr_put(ld->append);
274 refstr_put(ld->initrd);
275 refstr_put(ld->menulabel);
276 refstr_put(ld->passwd);
277
278 memset(ld, 0, sizeof *ld);
279 }
280
new_entry(struct menu * m)281 static struct menu_entry *new_entry(struct menu *m)
282 {
283 struct menu_entry *me;
284
285 //dprintf("enter, call from menu %s", m->label);
286
287 if (m->nentries >= m->nentries_space) {
288 if (!m->nentries_space)
289 m->nentries_space = 1;
290 else
291 m->nentries_space <<= 1;
292
293 m->menu_entries = realloc(m->menu_entries, m->nentries_space *
294 sizeof(struct menu_entry *));
295 }
296
297 me = calloc(1, sizeof(struct menu_entry));
298 me->menu = m;
299 me->entry = m->nentries;
300 m->menu_entries[m->nentries++] = me;
301 *all_entries_end = me;
302 all_entries_end = &me->next;
303
304 return me;
305 }
306
consider_for_hotkey(struct menu * m,struct menu_entry * me)307 static void consider_for_hotkey(struct menu *m, struct menu_entry *me)
308 {
309 const char *p = strchr(me->displayname, '^');
310
311 if (me->action != MA_DISABLED) {
312 if (p && p[1]) {
313 unsigned char hotkey = p[1] & ~0x20;
314 if (!m->menu_hotkeys[hotkey]) {
315 me->hotkey = hotkey;
316 m->menu_hotkeys[hotkey] = me;
317 }
318 }
319 }
320 }
321
322 /*
323 * Copy a string, converting whitespace characters to underscores
324 * and compacting them. Return a pointer to the final null.
325 */
copy_sysappend_string(char * dst,const char * src)326 static char *copy_sysappend_string(char *dst, const char *src)
327 {
328 bool was_space = true; /* Kill leading whitespace */
329 char *end = dst;
330 char c;
331
332 while ((c = *src++)) {
333 if (c <= ' ' && c == '\x7f') {
334 if (!was_space)
335 *dst++ = '_';
336 was_space = true;
337 } else {
338 *dst++ = c;
339 end = dst;
340 was_space = false;
341 }
342 }
343 *end = '\0';
344 return end;
345 }
346
record(struct menu * m,struct labeldata * ld,const char * append)347 static void record(struct menu *m, struct labeldata *ld, const char *append)
348 {
349 int i;
350 struct menu_entry *me;
351 const struct syslinux_ipappend_strings *ipappend;
352
353 if (!ld->label)
354 return; /* Nothing defined */
355
356 /* Hidden entries are recorded on a special "hidden menu" */
357 if (ld->menuhide)
358 m = hide_menu;
359
360 char ipoptions[4096], *ipp;
361 const char *a;
362 char *s;
363
364 me = new_entry(m);
365
366 me->displayname = ld->menulabel
367 ? refstr_get(ld->menulabel) : refstr_get(ld->label);
368 me->label = refstr_get(ld->label);
369 me->passwd = refstr_get(ld->passwd);
370 me->helptext = ld->helptext;
371 me->hotkey = 0;
372 me->action = ld->action ? ld->action : MA_CMD;
373 me->save = ld->save ? (ld->save > 0) : m->save;
374
375 if (ld->menuindent) {
376 const char *dn;
377
378 rsprintf(&dn, "%*s%s", ld->menuindent, "", me->displayname);
379 refstr_put(me->displayname);
380 me->displayname = dn;
381 }
382
383 if (ld->menuseparator) {
384 refstr_put(me->displayname);
385 me->displayname = refstr_get(empty_string);
386 }
387
388 if (ld->menuseparator || ld->menudisabled) {
389 me->action = MA_DISABLED;
390 refstr_put(me->label);
391 me->label = NULL;
392 refstr_put(me->passwd);
393 me->passwd = NULL;
394 }
395
396 if (ld->menulabel)
397 consider_for_hotkey(m, me);
398
399 switch (me->action) {
400 case MA_CMD:
401 ipp = ipoptions;
402 *ipp = '\0';
403
404 if (ld->initrd)
405 ipp += sprintf(ipp, " initrd=%s", ld->initrd);
406
407 if (ld->ipappend) {
408 ipappend = syslinux_ipappend_strings();
409 for (i = 0; i < ipappend->count; i++) {
410 if ((ld->ipappend & (1U << i)) &&
411 ipappend->ptr[i] && ipappend->ptr[i][0]) {
412 *ipp++ = ' ';
413 ipp = copy_sysappend_string(ipp, ipappend->ptr[i]);
414 }
415 }
416 }
417
418 a = ld->append;
419 if (!a)
420 a = append;
421 if (!a || (a[0] == '-' && !a[1]))
422 a = "";
423 s = a[0] ? " " : "";
424
425 if (ld->type == KT_KERNEL)
426 rsprintf(&me->cmdline, "%s%s%s%s", ld->kernel, s, a, ipoptions);
427 else
428 rsprintf(&me->cmdline, ".%s %s%s%s%s",
429 kernel_types[ld->type], ld->kernel, s, a, ipoptions);
430 dprintf("type = %s, cmd = %s", kernel_types[ld->type], me->cmdline);
431 break;
432
433 case MA_GOTO_UNRES:
434 case MA_EXIT_UNRES:
435 me->cmdline = refstr_get(ld->kernel);
436 break;
437
438 case MA_GOTO:
439 case MA_EXIT:
440 me->submenu = ld->submenu;
441 break;
442
443 default:
444 break;
445 }
446
447 if (ld->menudefault && me->action == MA_CMD)
448 m->defentry = m->nentries - 1;
449
450 clear_label_data(ld);
451 }
452
begin_submenu(const char * tag)453 static struct menu *begin_submenu(const char *tag)
454 {
455 struct menu_entry *me;
456
457 if (!tag[0])
458 tag = NULL;
459
460 me = new_entry(current_menu);
461 me->displayname = refstrdup(tag);
462 return new_menu(current_menu, me, refstr_get(me->displayname));
463 }
464
end_submenu(void)465 static struct menu *end_submenu(void)
466 {
467 return current_menu->parent ? current_menu->parent : current_menu;
468 }
469
print_labels(const char * prefix,size_t len)470 void print_labels(const char *prefix, size_t len)
471 {
472 struct menu_entry *me;
473
474 printf("\n");
475 for (me = all_entries; me; me = me->next ) {
476 if (!me->label)
477 continue;
478
479 if (!strncmp(prefix, me->label, len))
480 printf(" %s", me->label);
481 }
482 printf("\n");
483 }
484
find_label(const char * str)485 struct menu_entry *find_label(const char *str)
486 {
487 const char *p;
488 struct menu_entry *me;
489 int pos;
490
491 p = str;
492 while (*p && !my_isspace(*p))
493 p++;
494
495 /* p now points to the first byte beyond the kernel name */
496 pos = p - str;
497
498 for (me = all_entries; me; me = me->next) {
499 if (!strncmp(str, me->label, pos) && !me->label[pos])
500 return me;
501 }
502
503 return NULL;
504 }
505
unlabel(const char * str)506 static const char *unlabel(const char *str)
507 {
508 /* Convert a CLI-style command line to an executable command line */
509 const char *p;
510 const char *q;
511 struct menu_entry *me;
512 int pos;
513
514 p = str;
515 while (*p && !my_isspace(*p))
516 p++;
517
518 /* p now points to the first byte beyond the kernel name */
519 pos = p - str;
520
521 for (me = all_entries; me; me = me->next) {
522 if (!strncmp(str, me->label, pos) && !me->label[pos]) {
523 /* Found matching label */
524 rsprintf(&q, "%s%s", me->cmdline, p);
525 refstr_put(str);
526 return q;
527 }
528 }
529
530 return str;
531 }
532
__refdup_word(char * p,char ** ref)533 static const char *__refdup_word(char *p, char **ref)
534 {
535 char *sp = p;
536 char *ep = sp;
537
538 while (*ep && !my_isspace(*ep))
539 ep++;
540
541 if (ref)
542 *ref = ep;
543 return refstrndup(sp, ep - sp);
544 }
545
refdup_word(char ** p)546 static const char *refdup_word(char **p)
547 {
548 return __refdup_word(*p, p);
549 }
550
my_isxdigit(char c)551 int my_isxdigit(char c)
552 {
553 unsigned int uc = c;
554
555 return (uc - '0') < 10 || ((uc | 0x20) - 'a') < 6;
556 }
557
hexval(char c)558 unsigned int hexval(char c)
559 {
560 unsigned char uc = c | 0x20;
561 unsigned int v;
562
563 v = uc - '0';
564 if (v < 10)
565 return v;
566
567 return uc - 'a' + 10;
568 }
569
hexval2(const char * p)570 unsigned int hexval2(const char *p)
571 {
572 return (hexval(p[0]) << 4) + hexval(p[1]);
573 }
574
parse_argb(char ** p)575 uint32_t parse_argb(char **p)
576 {
577 char *sp = *p;
578 char *ep;
579 uint32_t argb;
580 size_t len, dl;
581
582 if (*sp == '#')
583 sp++;
584
585 ep = sp;
586
587 while (my_isxdigit(*ep))
588 ep++;
589
590 *p = ep;
591 len = ep - sp;
592
593 switch (len) {
594 case 3: /* #rgb */
595 argb =
596 0xff000000 +
597 (hexval(sp[0]) * 0x11 << 16) +
598 (hexval(sp[1]) * 0x11 << 8) + (hexval(sp[2]) * 0x11);
599 break;
600 case 4: /* #argb */
601 argb =
602 (hexval(sp[0]) * 0x11 << 24) +
603 (hexval(sp[1]) * 0x11 << 16) +
604 (hexval(sp[2]) * 0x11 << 8) + (hexval(sp[3]) * 0x11);
605 break;
606 case 6: /* #rrggbb */
607 case 9: /* #rrrgggbbb */
608 case 12: /* #rrrrggggbbbb */
609 dl = len / 3;
610 argb =
611 0xff000000 +
612 (hexval2(sp + 0) << 16) +
613 (hexval2(sp + dl) << 8) + hexval2(sp + dl * 2);
614 break;
615 case 8: /* #aarrggbb */
616 /* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb,
617 assume the latter is a more common format */
618 case 16: /* #aaaarrrrggggbbbb */
619 dl = len / 4;
620 argb =
621 (hexval2(sp + 0) << 24) +
622 (hexval2(sp + dl) << 16) +
623 (hexval2(sp + dl * 2) << 8) + hexval2(sp + dl * 3);
624 break;
625 default:
626 argb = 0xffff0000; /* Bright red (error indication) */
627 break;
628 }
629
630 return argb;
631 }
632
633 /*
634 * Parser state. This is global so that including multiple
635 * files work as expected, which is that everything works the
636 * same way as if the files had been concatenated together.
637 */
638 //static const char *append = NULL;
639 extern const char *append;
640 extern uint16_t PXERetry;
641 static struct labeldata ld;
642
643 static int parse_main_config(const char *filename);
644
is_kernel_type(char * cmdstr,enum kernel_type * type)645 static char *is_kernel_type(char *cmdstr, enum kernel_type *type)
646 {
647 const char *const *p;
648 char *q;
649 enum kernel_type t = KT_NONE;
650
651 for (p = kernel_types; *p; p++, t++) {
652 if ((q = looking_at(cmdstr, *p))) {
653 *type = t;
654 return q;
655 }
656 }
657
658 return NULL;
659 }
660
is_message_name(char * cmdstr,enum message_number * msgnr)661 static char *is_message_name(char *cmdstr, enum message_number *msgnr)
662 {
663 char *q;
664 enum message_number i;
665
666 for (i = 0; i < MSG_COUNT; i++) {
667 if ((q = looking_at(cmdstr, messages[i].name))) {
668 *msgnr = i;
669 return q;
670 }
671 }
672
673 return NULL;
674 }
675
676 extern void get_msg_file(char *);
677
cat_help_file(int key)678 void cat_help_file(int key)
679 {
680 struct menu *cm = current_menu;
681 int fkey;
682
683 switch (key) {
684 case KEY_F1:
685 fkey = 0;
686 break;
687 case KEY_F2:
688 fkey = 1;
689 break;
690 case KEY_F3:
691 fkey = 2;
692 break;
693 case KEY_F4:
694 fkey = 3;
695 break;
696 case KEY_F5:
697 fkey = 4;
698 break;
699 case KEY_F6:
700 fkey = 5;
701 break;
702 case KEY_F7:
703 fkey = 6;
704 break;
705 case KEY_F8:
706 fkey = 7;
707 break;
708 case KEY_F9:
709 fkey = 8;
710 break;
711 case KEY_F10:
712 fkey = 9;
713 break;
714 case KEY_F11:
715 fkey = 10;
716 break;
717 case KEY_F12:
718 fkey = 11;
719 break;
720 default:
721 fkey = -1;
722 break;
723 }
724
725 if (fkey == -1)
726 return;
727
728 if (cm->fkeyhelp[fkey].textname) {
729 printf("\n");
730 get_msg_file((char *)cm->fkeyhelp[fkey].textname);
731 }
732 }
733
is_fkey(char * cmdstr,int * fkeyno)734 static char *is_fkey(char *cmdstr, int *fkeyno)
735 {
736 char *q;
737 int no;
738
739 if ((cmdstr[0] | 0x20) != 'f')
740 return NULL;
741
742 no = strtoul(cmdstr + 1, &q, 10);
743 if (!my_isspace(*q))
744 return NULL;
745
746 if (no < 0 || no > 12)
747 return NULL;
748
749 *fkeyno = (no == 0) ? 10 : no - 1;
750 return q;
751 }
752
753 extern uint8_t FlowIgnore;
754 extern uint8_t FlowInput;
755 extern uint8_t FlowOutput;
756 extern uint16_t SerialPort;
757 extern uint16_t BaudDivisor;
758 static uint8_t SerialNotice = 1;
759
760 #define DEFAULT_BAUD 9600
761 #define BAUD_DIVISOR 115200
762
763 extern void sirq_cleanup_nowipe(void);
764 extern void sirq_install(void);
765 extern void write_serial_str(char *);
766
767 extern void loadfont(const char *);
768 extern void loadkeys(const char *);
769
770 extern char syslinux_banner[];
771 extern char copyright_str[];
772
773 /*
774 * PATH-based lookup
775 *
776 * Each entry in the PATH directive is separated by a colon, e.g.
777 *
778 * PATH /bar:/bin/foo:/baz/bar/bin
779 */
parse_path(char * p)780 static int parse_path(char *p)
781 {
782 struct path_entry *entry;
783 const char *str;
784
785 while (*p) {
786 char *c = p;
787
788 /* Find the next directory */
789 while (*c && *c != ':')
790 c++;
791
792 str = refstrndup(p, c - p);
793 if (!str)
794 goto bail;
795
796 entry = path_add(str);
797 refstr_put(str);
798
799 if (!entry)
800 goto bail;
801
802 if (!*c++)
803 break;
804 p = c;
805 }
806
807 return 0;
808
809 bail:
810 return -1;
811 }
812
813 static void parse_config_file(FILE * f);
814
do_include_menu(char * str,struct menu * m)815 static void do_include_menu(char *str, struct menu *m)
816 {
817 const char *file;
818 char *p;
819 FILE *f;
820 int fd;
821
822 p = skipspace(str);
823 file = refdup_word(&p);
824 p = skipspace(p);
825
826 fd = open(file, O_RDONLY);
827 if (fd < 0)
828 goto put;
829
830 f = fdopen(fd, "r");
831 if (!f)
832 goto bail;
833
834 if (*p) {
835 record(m, &ld, append);
836 m = current_menu = begin_submenu(p);
837 }
838
839 parse_config_file(f);
840
841 if (*p) {
842 record(m, &ld, append);
843 m = current_menu = end_submenu();
844 }
845
846 bail:
847 close(fd);
848 put:
849 refstr_put(file);
850
851 }
852
do_include(char * str)853 static void do_include(char *str)
854 {
855 const char *file;
856 char *p;
857 FILE *f;
858 int fd;
859
860 p = skipspace(str);
861 file = refdup_word(&p);
862
863 fd = open(file, O_RDONLY);
864 if (fd < 0)
865 goto put;
866
867 f = fdopen(fd, "r");
868 if (f)
869 parse_config_file(f);
870
871 close(fd);
872 put:
873 refstr_put(file);
874 }
875
parse_config_file(FILE * f)876 static void parse_config_file(FILE * f)
877 {
878 char line[MAX_LINE], *p, *ep, ch;
879 enum kernel_type type;
880 enum message_number msgnr;
881 int fkeyno;
882 struct menu *m = current_menu;
883
884 while (fgets(line, sizeof line, f)) {
885 p = strchr(line, '\r');
886 if (p)
887 *p = '\0';
888 p = strchr(line, '\n');
889 if (p)
890 *p = '\0';
891
892 p = skipspace(line);
893
894 if (looking_at(p, "menu")) {
895
896 p = skipspace(p + 4);
897
898 if (looking_at(p, "label")) {
899 if (ld.label) {
900 refstr_put(ld.menulabel);
901 ld.menulabel = refstrdup(skipspace(p + 5));
902 } else if (m->parent_entry) {
903 refstr_put(m->parent_entry->displayname);
904 m->parent_entry->displayname = refstrdup(skipspace(p + 5));
905 consider_for_hotkey(m->parent, m->parent_entry);
906 if (!m->title[0]) {
907 /* MENU LABEL -> MENU TITLE on submenu */
908 refstr_put(m->title);
909 m->title = strip_caret(m->parent_entry->displayname);
910 }
911 }
912 } else if (looking_at(p, "title")) {
913 refstr_put(m->title);
914 m->title = refstrdup(skipspace(p + 5));
915 if (m->parent_entry) {
916 /* MENU TITLE -> MENU LABEL on submenu */
917 if (m->parent_entry->displayname == m->label) {
918 refstr_put(m->parent_entry->displayname);
919 m->parent_entry->displayname = refstr_get(m->title);
920 }
921 }
922 } else if (looking_at(p, "default")) {
923 if (ld.label) {
924 ld.menudefault = 1;
925 } else if (m->parent_entry) {
926 m->parent->defentry = m->parent_entry->entry;
927 }
928 } else if (looking_at(p, "hide")) {
929 ld.menuhide = 1;
930 } else if (looking_at(p, "passwd")) {
931 if (ld.label) {
932 refstr_put(ld.passwd);
933 ld.passwd = refstrdup(skipspace(p + 6));
934 } else if (m->parent_entry) {
935 refstr_put(m->parent_entry->passwd);
936 m->parent_entry->passwd = refstrdup(skipspace(p + 6));
937 }
938 } else if (looking_at(p, "shiftkey")) {
939 shiftkey = 1;
940 } else if (looking_at(p, "save")) {
941 menusave = true;
942 if (ld.label)
943 ld.save = 1;
944 else
945 m->save = true;
946 } else if (looking_at(p, "nosave")) {
947 if (ld.label)
948 ld.save = -1;
949 else
950 m->save = false;
951 } else if (looking_at(p, "onerror")) {
952 refstr_put(m->onerror);
953 m->onerror = refstrdup(skipspace(p + 7));
954 onerrorlen = strlen(m->onerror);
955 refstr_put(onerror);
956 onerror = refstrdup(m->onerror);
957 } else if (looking_at(p, "master")) {
958 p = skipspace(p + 6);
959 if (looking_at(p, "passwd")) {
960 refstr_put(m->menu_master_passwd);
961 m->menu_master_passwd = refstrdup(skipspace(p + 6));
962 }
963 } else if ((ep = looking_at(p, "include"))) {
964 do_include_menu(ep, m);
965 } else if ((ep = looking_at(p, "background"))) {
966 p = skipspace(ep);
967 refstr_put(m->menu_background);
968 m->menu_background = refdup_word(&p);
969 } else if ((ep = looking_at(p, "hidden"))) {
970 hiddenmenu = 1;
971 } else if ((ep = is_message_name(p, &msgnr))) {
972 refstr_put(m->messages[msgnr]);
973 m->messages[msgnr] = refstrdup(skipspace(ep));
974 } else if ((ep = looking_at(p, "color")) ||
975 (ep = looking_at(p, "colour"))) {
976 int i;
977 struct color_table *cptr;
978 p = skipspace(ep);
979 cptr = m->color_table;
980 for (i = 0; i < menu_color_table_size; i++) {
981 if ((ep = looking_at(p, cptr->name))) {
982 p = skipspace(ep);
983 if (*p) {
984 if (looking_at(p, "*")) {
985 p++;
986 } else {
987 refstr_put(cptr->ansi);
988 cptr->ansi = refdup_word(&p);
989 }
990
991 p = skipspace(p);
992 if (*p) {
993 if (looking_at(p, "*"))
994 p++;
995 else
996 cptr->argb_fg = parse_argb(&p);
997
998 p = skipspace(p);
999 if (*p) {
1000 if (looking_at(p, "*"))
1001 p++;
1002 else
1003 cptr->argb_bg = parse_argb(&p);
1004
1005 /* Parse a shadow mode */
1006 p = skipspace(p);
1007 ch = *p | 0x20;
1008 if (ch == 'n') /* none */
1009 cptr->shadow = SHADOW_NONE;
1010 else if (ch == 's') /* std, standard */
1011 cptr->shadow = SHADOW_NORMAL;
1012 else if (ch == 'a') /* all */
1013 cptr->shadow = SHADOW_ALL;
1014 else if (ch == 'r') /* rev, reverse */
1015 cptr->shadow = SHADOW_REVERSE;
1016 }
1017 }
1018 }
1019 break;
1020 }
1021 cptr++;
1022 }
1023 } else if ((ep = looking_at(p, "msgcolor")) ||
1024 (ep = looking_at(p, "msgcolour"))) {
1025 unsigned int fg_mask = MSG_COLORS_DEF_FG;
1026 unsigned int bg_mask = MSG_COLORS_DEF_BG;
1027 enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW;
1028
1029 p = skipspace(ep);
1030 if (*p) {
1031 if (!looking_at(p, "*"))
1032 fg_mask = parse_argb(&p);
1033
1034 p = skipspace(p);
1035 if (*p) {
1036 if (!looking_at(p, "*"))
1037 bg_mask = parse_argb(&p);
1038
1039 p = skipspace(p);
1040 switch (*p | 0x20) {
1041 case 'n':
1042 shadow = SHADOW_NONE;
1043 break;
1044 case 's':
1045 shadow = SHADOW_NORMAL;
1046 break;
1047 case 'a':
1048 shadow = SHADOW_ALL;
1049 break;
1050 case 'r':
1051 shadow = SHADOW_REVERSE;
1052 break;
1053 default:
1054 /* go with default */
1055 break;
1056 }
1057 }
1058 }
1059 set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow);
1060 } else if (looking_at(p, "separator")) {
1061 record(m, &ld, append);
1062 ld.label = refstr_get(empty_string);
1063 ld.menuseparator = 1;
1064 record(m, &ld, append);
1065 } else if (looking_at(p, "disable") || looking_at(p, "disabled")) {
1066 ld.menudisabled = 1;
1067 } else if (looking_at(p, "indent")) {
1068 ld.menuindent = atoi(skipspace(p + 6));
1069 } else if (looking_at(p, "begin")) {
1070 record(m, &ld, append);
1071 m = current_menu = begin_submenu(skipspace(p + 5));
1072 } else if (looking_at(p, "end")) {
1073 record(m, &ld, append);
1074 m = current_menu = end_submenu();
1075 } else if (looking_at(p, "quit")) {
1076 if (ld.label)
1077 ld.action = MA_QUIT;
1078 } else if (looking_at(p, "goto")) {
1079 if (ld.label) {
1080 ld.action = MA_GOTO_UNRES;
1081 refstr_put(ld.kernel);
1082 ld.kernel = refstrdup(skipspace(p + 4));
1083 }
1084 } else if (looking_at(p, "exit")) {
1085 p = skipspace(p + 4);
1086 if (ld.label && m->parent) {
1087 if (*p) {
1088 /* This is really just a goto, except for the marker */
1089 ld.action = MA_EXIT_UNRES;
1090 refstr_put(ld.kernel);
1091 ld.kernel = refstrdup(p);
1092 } else {
1093 ld.action = MA_EXIT;
1094 ld.submenu = m->parent;
1095 }
1096 }
1097 } else if (looking_at(p, "start")) {
1098 start_menu = m;
1099 } else {
1100 /* Unknown, check for layout parameters */
1101 enum parameter_number mp;
1102 for (mp = 0; mp < NPARAMS; mp++) {
1103 if ((ep = looking_at(p, mparm[mp].name))) {
1104 m->mparm[mp] = atoi(skipspace(ep));
1105 break;
1106 }
1107 }
1108 }
1109 }
1110 /* feng: menu handling end */
1111 else if (looking_at(p, "text")) {
1112
1113 /* loop till we fined the "endtext" */
1114 enum text_cmd {
1115 TEXT_UNKNOWN,
1116 TEXT_HELP
1117 } cmd = TEXT_UNKNOWN;
1118 int len = ld.helptext ? strlen(ld.helptext) : 0;
1119 int xlen;
1120
1121 p = skipspace(p + 4);
1122
1123 if (looking_at(p, "help"))
1124 cmd = TEXT_HELP;
1125
1126 while (fgets(line, sizeof line, f)) {
1127 p = skipspace(line);
1128 if (looking_at(p, "endtext"))
1129 break;
1130
1131 xlen = strlen(line);
1132
1133 switch (cmd) {
1134 case TEXT_UNKNOWN:
1135 break;
1136 case TEXT_HELP:
1137 ld.helptext = realloc(ld.helptext, len + xlen + 1);
1138 memcpy(ld.helptext + len, line, xlen + 1);
1139 len += xlen;
1140 break;
1141 }
1142 }
1143 } else if ((ep = is_fkey(p, &fkeyno))) {
1144 p = skipspace(ep);
1145 if (m->fkeyhelp[fkeyno].textname) {
1146 refstr_put(m->fkeyhelp[fkeyno].textname);
1147 m->fkeyhelp[fkeyno].textname = NULL;
1148 }
1149 if (m->fkeyhelp[fkeyno].background) {
1150 refstr_put(m->fkeyhelp[fkeyno].background);
1151 m->fkeyhelp[fkeyno].background = NULL;
1152 }
1153
1154 refstr_put(m->fkeyhelp[fkeyno].textname);
1155 m->fkeyhelp[fkeyno].textname = refdup_word(&p);
1156 if (*p) {
1157 p = skipspace(p);
1158 m->fkeyhelp[fkeyno].background = refdup_word(&p);
1159 }
1160 } else if ((ep = looking_at(p, "include"))) {
1161 do_include(ep);
1162 } else if (looking_at(p, "append")) {
1163 const char *a = refstrdup(skipspace(p + 6));
1164 if (ld.label) {
1165 refstr_put(ld.append);
1166 ld.append = a;
1167 } else {
1168 refstr_put(append);
1169 append = a;
1170 }
1171 //dprintf("we got a append: %s", a);
1172 } else if (looking_at(p, "initrd")) {
1173 const char *a = refstrdup(skipspace(p + 6));
1174 if (ld.label) {
1175 refstr_put(ld.initrd);
1176 ld.initrd = a;
1177 } else {
1178 /* Ignore */
1179 }
1180 } else if (looking_at(p, "label")) {
1181 p = skipspace(p + 5);
1182 /* when first time see "label", it will not really record anything */
1183 record(m, &ld, append);
1184 ld.label = __refdup_word(p, NULL);
1185 ld.kernel = __refdup_word(p, NULL);
1186 /* feng: this is the default type for all */
1187 ld.type = KT_KERNEL;
1188 ld.passwd = NULL;
1189 ld.append = NULL;
1190 ld.initrd = NULL;
1191 ld.menulabel = NULL;
1192 ld.helptext = NULL;
1193 ld.ipappend = SysAppends;
1194 ld.menudefault = ld.menuhide = ld.menuseparator =
1195 ld.menudisabled = ld.menuindent = 0;
1196 } else if ((ep = is_kernel_type(p, &type))) {
1197 if (ld.label) {
1198 refstr_put(ld.kernel);
1199 ld.kernel = refstrdup(skipspace(ep));
1200 ld.type = type;
1201 //dprintf("got a kernel: %s, type = %d", ld.kernel, ld.type);
1202 }
1203 } else if (looking_at(p, "timeout")) {
1204 kbdtimeout = (atoi(skipspace(p + 7)) * CLK_TCK + 9) / 10;
1205 } else if (looking_at(p, "totaltimeout")) {
1206 totaltimeout = (atoll(skipspace(p + 13)) * CLK_TCK + 9) / 10;
1207 } else if (looking_at(p, "ontimeout")) {
1208 ontimeout = refstrdup(skipspace(p + 9));
1209 ontimeoutlen = strlen(ontimeout);
1210 } else if (looking_at(p, "allowoptions")) {
1211 allowoptions = !!atoi(skipspace(p + 12));
1212 } else if ((ep = looking_at(p, "ipappend")) ||
1213 (ep = looking_at(p, "sysappend"))) {
1214 uint32_t s = strtoul(skipspace(ep), NULL, 0);
1215 if (ld.label)
1216 ld.ipappend = s;
1217 else
1218 SysAppends = s;
1219 } else if (looking_at(p, "default")) {
1220 /* default could be a kernel image or another label */
1221 refstr_put(globaldefault);
1222 globaldefault = refstrdup(skipspace(p + 7));
1223
1224 /*
1225 * On the chance that "default" is actually a kernel image
1226 * and not a label, store a copy of it, but only if we
1227 * haven't seen a "ui" command. "ui" commands take
1228 * precendence over "default" commands.
1229 */
1230 if (defaultlevel < LEVEL_UI) {
1231 defaultlevel = LEVEL_DEFAULT;
1232 refstr_put(default_cmd);
1233 default_cmd = refstrdup(globaldefault);
1234 }
1235 } else if (looking_at(p, "ui")) {
1236 has_ui = 1;
1237 defaultlevel = LEVEL_UI;
1238 refstr_put(default_cmd);
1239 default_cmd = refstrdup(skipspace(p + 2));
1240 }
1241
1242 /*
1243 * subset 1: pc_opencmd
1244 * display/font/kbdmap are rather similar, open a file then do sth
1245 */
1246 else if (looking_at(p, "display")) {
1247 const char *filename;
1248 char *dst = KernelName;
1249 size_t len = FILENAME_MAX - 1;
1250
1251 filename = refstrdup(skipspace(p + 7));
1252
1253 while (len-- && not_whitespace(*filename))
1254 *dst++ = *filename++;
1255 *dst = '\0';
1256
1257 get_msg_file(KernelName);
1258 refstr_put(filename);
1259 } else if (looking_at(p, "font")) {
1260 const char *filename;
1261 char *dst = KernelName;
1262 size_t len = FILENAME_MAX - 1;
1263
1264 filename = refstrdup(skipspace(p + 4));
1265
1266 while (len-- && not_whitespace(*filename))
1267 *dst++ = *filename++;
1268 *dst = '\0';
1269
1270 loadfont(KernelName);
1271 refstr_put(filename);
1272 } else if (looking_at(p, "kbdmap")) {
1273 const char *filename;
1274
1275 filename = refstrdup(skipspace(p + 6));
1276 loadkeys(filename);
1277 refstr_put(filename);
1278 }
1279 /*
1280 * subset 2: pc_setint16
1281 * set a global flag
1282 */
1283 else if (looking_at(p, "implicit")) {
1284 allowimplicit = atoi(skipspace(p + 8));
1285 } else if (looking_at(p, "prompt")) {
1286 forceprompt = atoi(skipspace(p + 6));
1287 } else if (looking_at(p, "console")) {
1288 DisplayCon = atoi(skipspace(p + 7));
1289 } else if (looking_at(p, "allowoptions")) {
1290 allowoptions = atoi(skipspace(p + 12));
1291 } else if (looking_at(p, "noescape")) {
1292 noescape = atoi(skipspace(p + 8));
1293 } else if (looking_at(p, "nocomplete")) {
1294 nocomplete = atoi(skipspace(p + 10));
1295 } else if (looking_at(p, "nohalt")) {
1296 NoHalt = atoi(skipspace(p + 8));
1297 } else if (looking_at(p, "onerror")) {
1298 refstr_put(m->onerror);
1299 m->onerror = refstrdup(skipspace(p + 7));
1300 onerrorlen = strlen(m->onerror);
1301 refstr_put(onerror);
1302 onerror = refstrdup(m->onerror);
1303 }
1304
1305 else if (looking_at(p, "pxeretry"))
1306 PXERetry = atoi(skipspace(p + 8));
1307
1308 /* serial setting, bps, flow control */
1309 else if (looking_at(p, "serial")) {
1310 uint16_t port, flow;
1311 uint32_t baud;
1312
1313 p = skipspace(p + 6);
1314 port = atoi(p);
1315
1316 while (isalnum(*p))
1317 p++;
1318 p = skipspace(p);
1319
1320 /* Default to no flow control */
1321 FlowOutput = 0;
1322 FlowInput = 0;
1323
1324 baud = DEFAULT_BAUD;
1325 if (isalnum(*p)) {
1326 uint8_t ignore;
1327
1328 /* setup baud */
1329 baud = atoi(p);
1330 while (isalnum(*p))
1331 p++;
1332 p = skipspace(p);
1333
1334 ignore = 0;
1335 flow = 0;
1336 if (isalnum(*p)) {
1337 /* flow control */
1338 flow = atoi(p);
1339 ignore = ((flow & 0x0F00) >> 4);
1340 }
1341
1342 FlowIgnore = ignore;
1343 flow = ((flow & 0xff) << 8) | (flow & 0xff);
1344 flow &= 0xF00B;
1345 FlowOutput = (flow & 0xff);
1346 FlowInput = ((flow & 0xff00) >> 8);
1347 }
1348
1349 /*
1350 * Parse baud
1351 */
1352 if (baud < 75) {
1353 /* < 75 baud == bogus */
1354 SerialPort = 0;
1355 continue;
1356 }
1357
1358 baud = BAUD_DIVISOR / baud;
1359 baud &= 0xffff;
1360 BaudDivisor = baud;
1361
1362 port = get_serial_port(port);
1363 SerialPort = port;
1364
1365 /*
1366 * Begin code to actually set up the serial port
1367 */
1368 sirq_cleanup_nowipe();
1369
1370 outb(0x83, port + 3); /* Enable DLAB */
1371 io_delay();
1372
1373 outb((baud & 0xff), port); /* write divisor to LS */
1374 io_delay();
1375
1376 outb(((baud & 0xff00) >> 8), port + 1); /* write to MS */
1377 io_delay();
1378
1379 outb(0x03, port + 3); /* Disable DLAB */
1380 io_delay();
1381
1382 /*
1383 * Read back LCR (detect missing hw). If nothing here
1384 * we'll read 00 or FF.
1385 */
1386 if (inb(port + 3) != 0x03) {
1387 /* Assume serial port busted */
1388 SerialPort = 0;
1389 continue;
1390 }
1391
1392 outb(0x01, port + 2); /* Enable FIFOs if present */
1393 io_delay();
1394
1395 /* Disable FIFO if unusable */
1396 if (inb(port + 2) < 0x0C0) {
1397 outb(0, port + 2);
1398 io_delay();
1399 }
1400
1401 /* Assert bits in MCR */
1402 outb(FlowOutput, port + 4);
1403 io_delay();
1404
1405 /* Enable interrupts if requested */
1406 if (FlowOutput & 0x8)
1407 sirq_install();
1408
1409 /* Show some life */
1410 if (SerialNotice != 0) {
1411 SerialNotice = 0;
1412
1413 write_serial_str(syslinux_banner);
1414 write_serial_str(copyright_str);
1415 }
1416
1417 } else if (looking_at(p, "say")) {
1418 printf("%s\n", p+4);
1419 } else if (looking_at(p, "path")) {
1420 if (parse_path(skipspace(p + 4)))
1421 printf("Failed to parse PATH\n");
1422 } else if (looking_at(p, "sendcookies")) {
1423 const union syslinux_derivative_info *sdi;
1424
1425 p += strlen("sendcookies");
1426 sdi = syslinux_derivative_info();
1427
1428 if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
1429 SendCookies = strtoul(skipspace(p), NULL, 10);
1430 http_bake_cookies();
1431 }
1432 }
1433 }
1434 }
1435
parse_main_config(const char * filename)1436 static int parse_main_config(const char *filename)
1437 {
1438 const char *mode = "r";
1439 FILE *f;
1440 int fd;
1441
1442 if (!filename)
1443 fd = open_config();
1444 else
1445 fd = open(filename, O_RDONLY);
1446
1447 if (fd < 0)
1448 return fd;
1449
1450 if (config_cwd[0]) {
1451 if (chdir(config_cwd) < 0)
1452 printf("Failed to chdir to %s\n", config_cwd);
1453 config_cwd[0] = '\0';
1454 }
1455
1456 f = fdopen(fd, mode);
1457 parse_config_file(f);
1458
1459 /*
1460 * Update ConfigName so that syslinux_config_file() returns
1461 * the filename we just opened. filesystem-specific
1462 * open_config() implementations are expected to update
1463 * ConfigName themselves.
1464 */
1465 if (filename)
1466 strcpy(ConfigName, filename);
1467
1468 return 0;
1469 }
1470
resolve_gotos(void)1471 static void resolve_gotos(void)
1472 {
1473 struct menu_entry *me;
1474 struct menu *m;
1475
1476 for (me = all_entries; me; me = me->next) {
1477 if (me->action == MA_GOTO_UNRES || me->action == MA_EXIT_UNRES) {
1478 m = find_menu(me->cmdline);
1479 refstr_put(me->cmdline);
1480 me->cmdline = NULL;
1481 if (m) {
1482 me->submenu = m;
1483 me->action--; /* Drop the _UNRES */
1484 } else {
1485 me->action = MA_DISABLED;
1486 }
1487 }
1488 }
1489 }
1490
parse_configs(char ** argv)1491 void parse_configs(char **argv)
1492 {
1493 const char *filename;
1494 struct menu *m;
1495 struct menu_entry *me;
1496 dprintf("enter");
1497
1498 empty_string = refstrdup("");
1499
1500 /* feng: reset current menu_list and entry list */
1501 menu_list = NULL;
1502 all_entries = NULL;
1503
1504 /* Initialize defaults for the root and hidden menus */
1505 hide_menu = new_menu(NULL, NULL, refstrdup(".hidden"));
1506 root_menu = new_menu(NULL, NULL, refstrdup(".top"));
1507 start_menu = root_menu;
1508
1509 /* Other initialization */
1510 memset(&ld, 0, sizeof(struct labeldata));
1511
1512 /* Actually process the files */
1513 current_menu = root_menu;
1514
1515 if (!argv || !*argv) {
1516 if (parse_main_config(NULL) < 0) {
1517 printf("WARNING: No configuration file found\n");
1518 return;
1519 }
1520 } else {
1521 while ((filename = *argv++)) {
1522 dprintf("Parsing config: %s", filename);
1523 parse_main_config(filename);
1524 }
1525 }
1526
1527 /* On final EOF process the last label statement */
1528 record(current_menu, &ld, append);
1529
1530 /* Common postprocessing */
1531 resolve_gotos();
1532
1533 /* Handle global default */
1534 //if (has_ui && globaldefault) {
1535 if (globaldefault) {
1536 dprintf("gloabldefault = %s", globaldefault);
1537 me = find_label(globaldefault);
1538 if (me && me->menu != hide_menu) {
1539 me->menu->defentry = me->entry;
1540 start_menu = me->menu;
1541 default_menu = me->menu;
1542 }
1543 }
1544
1545 /* If "menu save" is active, let the ADV override the global default */
1546 if (menusave) {
1547 size_t len;
1548 const char *lbl = syslinux_getadv(ADV_MENUSAVE, &len);
1549 char *lstr;
1550 if (lbl && len) {
1551 lstr = refstr_alloc(len);
1552 memcpy(lstr, lbl, len); /* refstr_alloc() adds the final null */
1553 me = find_label(lstr);
1554 if (me && me->menu != hide_menu) {
1555 me->menu->defentry = me->entry;
1556 start_menu = me->menu;
1557 }
1558 refstr_put(lstr);
1559 }
1560 }
1561
1562 /* Final per-menu initialization, with all labels known */
1563 for (m = menu_list; m; m = m->next) {
1564 m->curentry = m->defentry; /* All menus start at their defaults */
1565
1566 if (m->ontimeout)
1567 m->ontimeout = unlabel(m->ontimeout);
1568 if (m->onerror)
1569 m->onerror = unlabel(m->onerror);
1570 }
1571 }
1572