1 /* ----------------------------------------------------------------------- *
3 * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
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.
12 * ----------------------------------------------------------------------- */
24 #include <syslinux/adv.h>
25 #include <syslinux/config.h>
30 const char *empty_string;
32 /* Root menu, starting menu, hidden menu, and list of all menus */
33 struct menu *root_menu, *start_menu, *hide_menu, *menu_list;
35 /* These are global parameters regardless of which menu we're displaying */
36 int shiftkey = 0; /* Only display menu if shift key pressed */
39 long long totaltimeout = 0;
41 /* Keep track of global default */
42 static int has_ui = 0; /* DEFAULT only counts if UI is found */
43 static const char *globaldefault = NULL;
44 static bool menusave = false; /* True if there is any "menu save" */
46 /* Linked list of all entires, hidden or not; used by unlabel() */
47 static struct menu_entry *all_entries;
48 static struct menu_entry **all_entries_end = &all_entries;
50 static const struct messages messages[MSG_COUNT] = {
51 [MSG_AUTOBOOT] = {"autoboot", "Automatic boot in # second{,s}..."},
52 [MSG_TAB] = {"tabmsg", "Press [Tab] to edit options"},
53 [MSG_NOTAB] = {"notabmsg", ""},
54 [MSG_PASSPROMPT] = {"passprompt", "Password required"},
57 #define astrdup(x) ({ char *__x = (x); \
58 size_t __n = strlen(__x) + 1; \
59 char *__p = alloca(__n); \
60 if ( __p ) memcpy(__p, __x, __n); \
63 /* Must match enum kernel_type */
64 const char *const kernel_types[] = {
80 * Search the list of all menus for a specific label
82 static struct menu *find_menu(const char *label)
86 for (m = menu_list; m; m = m->next) {
87 if (!strcmp(label, m->label))
96 /* Strip ^ from a string, returning a new reference to the same refstring
98 static const char *strip_caret(const char *str)
114 return refstr_get(str);
116 r = q = refstr_alloc(strlen(str) - carets);
117 for (p = str; *p; p++)
121 *q = '\0'; /* refstr_alloc() already did this... */
126 /* Check to see if we are at a certain keyword (case insensitive) */
127 /* Returns a pointer to the first character past the keyword */
128 static char *looking_at(char *line, const char *kwd)
133 while (*p && *q && ((*p ^ *q) & ~0x20) == 0) {
139 return NULL; /* Didn't see the keyword */
141 return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */
144 static struct menu *new_menu(struct menu *parent,
145 struct menu_entry *parent_entry, const char *label)
147 struct menu *m = calloc(1, sizeof(struct menu));
151 m->title = refstr_get(empty_string);
156 m->parent_entry = parent_entry;
157 parent_entry->action = MA_SUBMENU;
158 parent_entry->submenu = m;
160 for (i = 0; i < MSG_COUNT; i++)
161 m->messages[i] = refstr_get(parent->messages[i]);
163 memcpy(m->mparm, parent->mparm, sizeof m->mparm);
165 m->allowedit = parent->allowedit;
166 m->timeout = parent->timeout;
167 m->save = parent->save;
168 m->immediate = parent->immediate;
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);
175 m->color_table = copy_color_table(parent->color_table);
177 for (i = 0; i < 12; i++) {
178 m->fkeyhelp[i].textname = refstr_get(parent->fkeyhelp[i].textname);
179 m->fkeyhelp[i].background =
180 refstr_get(parent->fkeyhelp[i].background);
184 for (i = 0; i < MSG_COUNT; i++)
185 m->messages[i] = refstrdup(messages[i].defmsg);
186 for (i = 0; i < NPARAMS; i++)
187 m->mparm[i] = mparm[i].value;
189 m->allowedit = true; /* Allow edits of the command line */
190 m->color_table = default_color_table();
202 enum kernel_type type;
205 const char *menulabel;
208 unsigned int ipappend;
209 unsigned int menuhide;
210 unsigned int menudefault;
211 unsigned int menuseparator;
212 unsigned int menudisabled;
213 unsigned int menuindent;
214 enum menu_action action;
217 struct menu *submenu;
220 /* Menu currently being parsed */
221 static struct menu *current_menu;
223 static void clear_label_data(struct labeldata *ld)
225 refstr_put(ld->label);
226 refstr_put(ld->kernel);
227 refstr_put(ld->append);
228 refstr_put(ld->initrd);
229 refstr_put(ld->menulabel);
230 refstr_put(ld->passwd);
232 memset(ld, 0, sizeof *ld);
235 static struct menu_entry *new_entry(struct menu *m)
237 struct menu_entry *me;
239 if (m->nentries >= m->nentries_space) {
240 if (!m->nentries_space)
241 m->nentries_space = 1;
243 m->nentries_space <<= 1;
245 m->menu_entries = realloc(m->menu_entries, m->nentries_space *
246 sizeof(struct menu_entry *));
249 me = calloc(1, sizeof(struct menu_entry));
251 me->entry = m->nentries;
252 m->menu_entries[m->nentries++] = me;
253 *all_entries_end = me;
254 all_entries_end = &me->next;
259 static void consider_for_hotkey(struct menu *m, struct menu_entry *me)
261 const char *p = strchr(me->displayname, '^');
263 if (me->action != MA_DISABLED) {
265 unsigned char hotkey = p[1] & ~0x20;
266 if (!m->menu_hotkeys[hotkey]) {
268 m->menu_hotkeys[hotkey] = me;
274 static void record(struct menu *m, struct labeldata *ld, const char *append)
277 struct menu_entry *me;
278 const struct syslinux_ipappend_strings *ipappend;
281 return; /* Nothing defined */
283 /* Hidden entries are recorded on a special "hidden menu" */
288 char ipoptions[4096], *ipp;
294 me->displayname = ld->menulabel
295 ? refstr_get(ld->menulabel) : refstr_get(ld->label);
296 me->label = refstr_get(ld->label);
297 me->passwd = refstr_get(ld->passwd);
298 me->helptext = ld->helptext;
300 me->action = ld->action ? ld->action : MA_CMD;
301 me->save = ld->save ? (ld->save > 0) : m->save;
302 me->immediate = ld->immediate ? (ld->immediate > 0) : m->immediate;
304 if (ld->menuindent) {
307 rsprintf(&dn, "%*s%s", ld->menuindent, "", me->displayname);
308 refstr_put(me->displayname);
309 me->displayname = dn;
312 if (ld->menuseparator) {
313 refstr_put(me->displayname);
314 me->displayname = refstr_get(empty_string);
317 if (ld->menuseparator || ld->menudisabled) {
318 me->action = MA_DISABLED;
319 refstr_put(me->label);
321 refstr_put(me->passwd);
326 consider_for_hotkey(m, me);
328 switch (me->action) {
334 ipp += sprintf(ipp, " initrd=%s", ld->initrd);
337 ipappend = syslinux_ipappend_strings();
338 for (i = 0; i < ipappend->count; i++) {
339 if ((ld->ipappend & (1U << i)) && ipappend->ptr[i] &&
341 ipp += sprintf(ipp, " %s", ipappend->ptr[i]);
348 if (!a || (a[0] == '-' && !a[1]))
351 if (ld->type == KT_KERNEL) {
352 rsprintf(&me->cmdline, "%s%s%s%s", ld->kernel, s, a, ipoptions);
354 rsprintf(&me->cmdline, ".%s %s%s%s%s",
355 kernel_types[ld->type], ld->kernel, s, a, ipoptions);
361 me->cmdline = refstr_get(ld->kernel);
366 me->submenu = ld->submenu;
370 me->cmdline = refstr_get(ld->kernel);
371 me->background = refstr_get(ld->append);
378 if (ld->menudefault && me->action == MA_CMD)
379 m->defentry = m->nentries - 1;
382 clear_label_data(ld);
385 static struct menu *begin_submenu(const char *tag)
387 struct menu_entry *me;
392 me = new_entry(current_menu);
393 me->displayname = refstrdup(tag);
394 return new_menu(current_menu, me, refstr_get(me->displayname));
397 static struct menu *end_submenu(void)
399 return current_menu->parent ? current_menu->parent : current_menu;
402 static struct menu_entry *find_label(const char *str)
405 struct menu_entry *me;
409 while (*p && !my_isspace(*p))
412 /* p now points to the first byte beyond the kernel name */
415 for (me = all_entries; me; me = me->next) {
416 if (!strncmp(str, me->label, pos) && !me->label[pos])
423 static const char *unlabel(const char *str)
425 /* Convert a CLI-style command line to an executable command line */
428 struct menu_entry *me;
432 while (*p && !my_isspace(*p))
435 /* p now points to the first byte beyond the kernel name */
438 for (me = all_entries; me; me = me->next) {
439 if (!strncmp(str, me->label, pos) && !me->label[pos]) {
440 /* Found matching label */
441 rsprintf(&q, "%s%s", me->cmdline, p);
450 static const char *refdup_word(char **p)
455 while (*ep && !my_isspace(*ep))
459 return refstrndup(sp, ep - sp);
462 int my_isxdigit(char c)
466 return (uc - '0') < 10 || ((uc | 0x20) - 'a') < 6;
469 unsigned int hexval(char c)
471 unsigned char uc = c | 0x20;
478 return uc - 'a' + 10;
481 unsigned int hexval2(const char *p)
483 return (hexval(p[0]) << 4) + hexval(p[1]);
486 uint32_t parse_argb(char **p)
498 while (my_isxdigit(*ep))
508 (hexval(sp[0]) * 0x11 << 16) +
509 (hexval(sp[1]) * 0x11 << 8) + (hexval(sp[2]) * 0x11);
513 (hexval(sp[0]) * 0x11 << 24) +
514 (hexval(sp[1]) * 0x11 << 16) +
515 (hexval(sp[2]) * 0x11 << 8) + (hexval(sp[3]) * 0x11);
517 case 6: /* #rrggbb */
518 case 9: /* #rrrgggbbb */
519 case 12: /* #rrrrggggbbbb */
523 (hexval2(sp + 0) << 16) +
524 (hexval2(sp + dl) << 8) + hexval2(sp + dl * 2);
526 case 8: /* #aarrggbb */
527 /* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb,
528 assume the latter is a more common format */
529 case 16: /* #aaaarrrrggggbbbb */
532 (hexval2(sp + 0) << 24) +
533 (hexval2(sp + dl) << 16) +
534 (hexval2(sp + dl * 2) << 8) + hexval2(sp + dl * 3);
537 argb = 0xffff0000; /* Bright red (error indication) */
545 * Parser state. This is global so that including multiple
546 * files work as expected, which is that everything works the
547 * same way as if the files had been concatenated together.
549 static const char *append = NULL;
550 static unsigned int ipappend = 0;
551 static struct labeldata ld;
553 static int parse_one_config(const char *filename);
555 static char *is_kernel_type(char *cmdstr, enum kernel_type *type)
557 const char *const *p;
559 enum kernel_type t = KT_NONE;
561 for (p = kernel_types; *p; p++, t++) {
562 if ((q = looking_at(cmdstr, *p))) {
571 static char *is_message_name(char *cmdstr, enum message_number *msgnr)
574 enum message_number i;
576 for (i = 0; i < MSG_COUNT; i++) {
577 if ((q = looking_at(cmdstr, messages[i].name))) {
586 static char *is_fkey(char *cmdstr, int *fkeyno)
591 if ((cmdstr[0] | 0x20) != 'f')
594 no = strtoul(cmdstr + 1, &q, 10);
598 if (no < 0 || no > 12)
601 *fkeyno = (no == 0) ? 10 : no - 1;
605 static void parse_config_file(FILE * f)
607 char line[MAX_LINE], *p, *ep, ch;
608 enum kernel_type type = -1;
609 enum message_number msgnr = -1;
611 struct menu *m = current_menu;
613 while (fgets(line, sizeof line, f)) {
614 p = strchr(line, '\r');
617 p = strchr(line, '\n');
623 if (looking_at(p, "menu")) {
624 p = skipspace(p + 4);
626 if (looking_at(p, "label")) {
628 refstr_put(ld.menulabel);
629 ld.menulabel = refstrdup(skipspace(p + 5));
630 } else if (m->parent_entry) {
631 refstr_put(m->parent_entry->displayname);
632 m->parent_entry->displayname = refstrdup(skipspace(p + 5));
633 consider_for_hotkey(m->parent, m->parent_entry);
635 /* MENU LABEL -> MENU TITLE on submenu */
636 refstr_put(m->title);
637 m->title = strip_caret(m->parent_entry->displayname);
640 } else if (looking_at(p, "title")) {
641 refstr_put(m->title);
642 m->title = refstrdup(skipspace(p + 5));
643 if (m->parent_entry) {
644 /* MENU TITLE -> MENU LABEL on submenu */
645 if (m->parent_entry->displayname == m->label) {
646 refstr_put(m->parent_entry->displayname);
647 m->parent_entry->displayname = refstr_get(m->title);
650 } else if (looking_at(p, "default")) {
653 } else if (m->parent_entry) {
654 m->parent->defentry = m->parent_entry->entry;
656 } else if (looking_at(p, "hide")) {
658 } else if (looking_at(p, "passwd")) {
660 refstr_put(ld.passwd);
661 ld.passwd = refstrdup(skipspace(p + 6));
662 } else if (m->parent_entry) {
663 refstr_put(m->parent_entry->passwd);
664 m->parent_entry->passwd = refstrdup(skipspace(p + 6));
666 } else if (looking_at(p, "shiftkey")) {
668 } else if (looking_at(p, "save")) {
674 } else if (looking_at(p, "nosave")) {
679 } else if (looking_at(p, "immediate")) {
684 } else if (looking_at(p, "noimmediate")) {
688 m->immediate = false;
689 } else if (looking_at(p, "onerror")) {
690 refstr_put(m->onerror);
691 m->onerror = refstrdup(skipspace(p + 7));
692 } else if (looking_at(p, "master")) {
693 p = skipspace(p + 6);
694 if (looking_at(p, "passwd")) {
695 refstr_put(m->menu_master_passwd);
696 m->menu_master_passwd = refstrdup(skipspace(p + 6));
698 } else if ((ep = looking_at(p, "include"))) {
700 } else if ((ep = looking_at(p, "background"))) {
702 refstr_put(m->menu_background);
703 m->menu_background = refdup_word(&p);
704 } else if ((ep = looking_at(p, "hidden"))) {
706 } else if ((ep = looking_at(p, "clear"))) {
708 } else if ((ep = is_message_name(p, &msgnr))) {
709 refstr_put(m->messages[msgnr]);
710 m->messages[msgnr] = refstrdup(skipspace(ep));
711 } else if ((ep = looking_at(p, "color")) ||
712 (ep = looking_at(p, "colour"))) {
714 struct color_table *cptr;
716 cptr = m->color_table;
717 for (i = 0; i < menu_color_table_size; i++) {
718 if ((ep = looking_at(p, cptr->name))) {
721 if (looking_at(p, "*")) {
724 refstr_put(cptr->ansi);
725 cptr->ansi = refdup_word(&p);
730 if (looking_at(p, "*"))
733 cptr->argb_fg = parse_argb(&p);
737 if (looking_at(p, "*"))
740 cptr->argb_bg = parse_argb(&p);
742 /* Parse a shadow mode */
745 if (ch == 'n') /* none */
746 cptr->shadow = SHADOW_NONE;
747 else if (ch == 's') /* std, standard */
748 cptr->shadow = SHADOW_NORMAL;
749 else if (ch == 'a') /* all */
750 cptr->shadow = SHADOW_ALL;
751 else if (ch == 'r') /* rev, reverse */
752 cptr->shadow = SHADOW_REVERSE;
760 } else if ((ep = looking_at(p, "msgcolor")) ||
761 (ep = looking_at(p, "msgcolour"))) {
762 unsigned int fg_mask = MSG_COLORS_DEF_FG;
763 unsigned int bg_mask = MSG_COLORS_DEF_BG;
764 enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW;
768 if (!looking_at(p, "*"))
769 fg_mask = parse_argb(&p);
773 if (!looking_at(p, "*"))
774 bg_mask = parse_argb(&p);
779 shadow = SHADOW_NONE;
782 shadow = SHADOW_NORMAL;
788 shadow = SHADOW_REVERSE;
791 /* go with default */
796 set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow);
797 } else if (looking_at(p, "separator")) {
798 record(m, &ld, append);
799 ld.label = refstr_get(empty_string);
800 ld.menuseparator = 1;
801 record(m, &ld, append);
802 } else if (looking_at(p, "disable") || looking_at(p, "disabled")) {
804 } else if (looking_at(p, "indent")) {
805 ld.menuindent = atoi(skipspace(p + 6));
806 } else if (looking_at(p, "begin")) {
807 record(m, &ld, append);
808 m = current_menu = begin_submenu(skipspace(p + 5));
809 } else if (looking_at(p, "end")) {
810 record(m, &ld, append);
811 m = current_menu = end_submenu();
812 } else if (looking_at(p, "quit")) {
815 } else if (looking_at(p, "goto")) {
817 ld.action = MA_GOTO_UNRES;
818 refstr_put(ld.kernel);
819 ld.kernel = refstrdup(skipspace(p + 4));
821 } else if (looking_at(p, "exit")) {
822 p = skipspace(p + 4);
823 if (ld.label && m->parent) {
825 /* This is really just a goto, except for the marker */
826 ld.action = MA_EXIT_UNRES;
827 refstr_put(ld.kernel);
828 ld.kernel = refstrdup(p);
831 ld.submenu = m->parent;
834 } else if (looking_at(p, "start")) {
836 } else if (looking_at(p, "help")) {
839 p = skipspace(p + 4);
841 refstr_put(ld.kernel);
842 ld.kernel = refdup_word(&p);
845 refstr_put(ld.append);
851 ld.append = refdup_word(&p); /* Background */
854 } else if ((ep = looking_at(p, "resolution"))) {
856 x = strtoul(ep, &ep, 0);
857 y = strtoul(skipspace(ep), NULL, 0);
858 set_resolution(x, y);
860 /* Unknown, check for layout parameters */
861 enum parameter_number mp;
862 for (mp = 0; mp < NPARAMS; mp++) {
863 if ((ep = looking_at(p, mparm[mp].name))) {
864 m->mparm[mp] = atoi(skipspace(ep));
869 } else if (looking_at(p, "text")) {
873 } cmd = TEXT_UNKNOWN;
874 int len = ld.helptext ? strlen(ld.helptext) : 0;
877 p = skipspace(p + 4);
879 if (looking_at(p, "help"))
882 while (fgets(line, sizeof line, f)) {
884 if (looking_at(p, "endtext"))
893 ld.helptext = realloc(ld.helptext, len + xlen + 1);
894 memcpy(ld.helptext + len, line, xlen + 1);
899 } else if ((ep = is_fkey(p, &fkeyno))) {
901 if (m->fkeyhelp[fkeyno].textname) {
902 refstr_put(m->fkeyhelp[fkeyno].textname);
903 m->fkeyhelp[fkeyno].textname = NULL;
905 if (m->fkeyhelp[fkeyno].background) {
906 refstr_put(m->fkeyhelp[fkeyno].background);
907 m->fkeyhelp[fkeyno].background = NULL;
910 refstr_put(m->fkeyhelp[fkeyno].textname);
911 m->fkeyhelp[fkeyno].textname = refdup_word(&p);
914 m->fkeyhelp[fkeyno].background = refdup_word(&p);
916 } else if ((ep = looking_at(p, "include"))) {
921 file = refdup_word(&p);
924 record(m, &ld, append);
925 m = current_menu = begin_submenu(p);
926 parse_one_config(file);
927 record(m, &ld, append);
928 m = current_menu = end_submenu();
930 parse_one_config(file);
934 } else if (looking_at(p, "append")) {
935 const char *a = refstrdup(skipspace(p + 6));
937 refstr_put(ld.append);
943 } else if (looking_at(p, "initrd")) {
944 const char *a = refstrdup(skipspace(p + 6));
946 refstr_put(ld.initrd);
951 } else if (looking_at(p, "label")) {
952 p = skipspace(p + 5);
953 record(m, &ld, append);
954 ld.label = refstrdup(p);
955 ld.kernel = refstrdup(p);
962 ld.ipappend = ipappend;
963 ld.menudefault = ld.menuhide = ld.menuseparator =
964 ld.menudisabled = ld.menuindent = 0;
965 } else if ((ep = is_kernel_type(p, &type))) {
967 refstr_put(ld.kernel);
968 ld.kernel = refstrdup(skipspace(ep));
971 } else if (looking_at(p, "timeout")) {
972 m->timeout = (atoi(skipspace(p + 7)) * CLK_TCK + 9) / 10;
973 } else if (looking_at(p, "totaltimeout")) {
974 totaltimeout = (atoll(skipspace(p + 13)) * CLK_TCK + 9) / 10;
975 } else if (looking_at(p, "ontimeout")) {
976 m->ontimeout = refstrdup(skipspace(p + 9));
977 } else if (looking_at(p, "allowoptions")) {
978 m->allowedit = !!atoi(skipspace(p + 12));
979 } else if (looking_at(p, "ipappend")) {
981 ld.ipappend = atoi(skipspace(p + 8));
983 ipappend = atoi(skipspace(p + 8));
984 } else if (looking_at(p, "default")) {
985 refstr_put(globaldefault);
986 globaldefault = refstrdup(skipspace(p + 7));
987 } else if (looking_at(p, "ui")) {
993 static int parse_one_config(const char *filename)
997 if (!strcmp(filename, "~"))
998 filename = syslinux_config_file();
1000 dprintf("Opening config file: %s ", filename);
1002 f = fopen(filename, "r");
1003 dprintf("%s\n", f ? "ok" : "failed");
1008 parse_config_file(f);
1014 static void resolve_gotos(void)
1016 struct menu_entry *me;
1019 for (me = all_entries; me; me = me->next) {
1020 if (me->action == MA_GOTO_UNRES || me->action == MA_EXIT_UNRES) {
1021 m = find_menu(me->cmdline);
1022 refstr_put(me->cmdline);
1026 me->action--; /* Drop the _UNRES */
1028 me->action = MA_DISABLED;
1034 void parse_configs(char **argv)
1036 const char *filename;
1038 struct menu_entry *me;
1040 empty_string = refstrdup("");
1042 /* Initialize defaults for the root and hidden menus */
1043 hide_menu = new_menu(NULL, NULL, refstrdup(".hidden"));
1044 root_menu = new_menu(NULL, NULL, refstrdup(".top"));
1045 start_menu = root_menu;
1047 /* Other initialization */
1048 memset(&ld, 0, sizeof(struct labeldata));
1050 /* Actually process the files */
1051 current_menu = root_menu;
1053 parse_one_config("~");
1055 while ((filename = *argv++))
1056 parse_one_config(filename);
1059 /* On final EOF process the last label statement */
1060 record(current_menu, &ld, append);
1062 /* Common postprocessing */
1065 /* Handle global default */
1066 if (has_ui && globaldefault) {
1067 me = find_label(globaldefault);
1068 if (me && me->menu != hide_menu) {
1069 me->menu->defentry = me->entry;
1070 start_menu = me->menu;
1074 /* If "menu save" is active, let the ADV override the global default */
1077 const char *lbl = syslinux_getadv(ADV_MENUSAVE, &len);
1080 lstr = refstr_alloc(len);
1081 memcpy(lstr, lbl, len); /* refstr_alloc() adds the final null */
1082 me = find_label(lstr);
1083 if (me && me->menu != hide_menu) {
1084 me->menu->defentry = me->entry;
1085 start_menu = me->menu;
1091 /* Final per-menu initialization, with all labels known */
1092 for (m = menu_list; m; m = m->next) {
1093 m->curentry = m->defentry; /* All menus start at their defaults */
1096 m->ontimeout = unlabel(m->ontimeout);
1098 m->onerror = unlabel(m->onerror);