1 /* ----------------------------------------------------------------------- *
3 * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
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.
11 * ----------------------------------------------------------------------- */
21 #include <syslinux/config.h>
26 const char *empty_string
;
28 /* Root menu, starting menu, hidden menu, and list of all menus */
29 struct menu
*root_menu
, *start_menu
, *hide_menu
, *menu_list
;
31 /* These are global parameters regardless of which menu we're displaying */
32 int shiftkey
= 0; /* Only display menu if shift key pressed */
34 long long totaltimeout
= 0;
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
;
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" },
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); \
53 /* Must match enum kernel_type */
54 const char * const kernel_types
[] = {
70 * Search the list of all menus for a specific label
73 find_menu(const char *label
)
77 for (m
= menu_list
; m
; m
= m
->next
) {
78 if (!strcmp(label
, m
->label
))
90 while (*p
&& my_isspace(*p
))
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 */
129 looking_at(char *line
, const char *kwd
)
134 while ( *p
&& *q
&& ((*p
^*q
) & ~0x20) == 0 ) {
140 return NULL
; /* Didn't see the keyword */
142 return my_isspace(*p
) ? p
: NULL
; /* Must be EOL or whitespace */
145 static struct menu
* new_menu(struct menu
*parent
,
146 struct menu_entry
*parent_entry
,
149 struct menu
*m
= calloc(1, sizeof(struct menu
));
153 m
->title
= refstr_get(empty_string
);
158 m
->parent_entry
= parent_entry
;
159 parent_entry
->action
= MA_SUBMENU
;
160 parent_entry
->submenu
= m
;
162 for (i
= 0; i
< MSG_COUNT
; i
++)
163 m
->messages
[i
] = refstr_get(parent
->messages
[i
]);
165 memcpy(m
->mparm
, parent
->mparm
, sizeof m
->mparm
);
167 m
->allowedit
= parent
->allowedit
;
168 m
->timeout
= parent
->timeout
;
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
= refstr_get(parent
->fkeyhelp
[i
].background
);
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
;
188 m
->allowedit
= 1; /* Allow edits of the command line */
189 m
->color_table
= default_color_table();
201 enum kernel_type type
;
204 const char *menulabel
;
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
;
217 /* Menu currently being parsed */
218 static struct menu
*current_menu
;
221 clear_label_data(struct labeldata
*ld
)
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
);
230 memset(ld
, 0, sizeof *ld
);
233 static struct menu_entry
*new_entry(struct menu
*m
)
235 struct menu_entry
*me
;
237 if (m
->nentries
>= m
->nentries_space
) {
238 if (!m
->nentries_space
)
239 m
->nentries_space
= 1;
241 m
->nentries_space
<<= 1;
243 m
->menu_entries
= realloc(m
->menu_entries
, m
->nentries_space
*
244 sizeof(struct menu_entry
*));
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
;
256 static void consider_for_hotkey(struct menu
*m
, struct menu_entry
*me
)
258 const char *p
= strchr(me
->displayname
, '^');
260 if (me
->action
!= MA_DISABLED
) {
262 unsigned char hotkey
= p
[1] & ~0x20;
263 if ( !m
->menu_hotkeys
[hotkey
] ) {
265 m
->menu_hotkeys
[hotkey
] = me
;
272 record(struct menu
*m
, struct labeldata
*ld
, const char *append
)
275 struct menu_entry
*me
;
276 const struct syslinux_ipappend_strings
*ipappend
;
279 return; /* Nothing defined */
281 /* Hidden entries are recorded on a special "hidden menu" */
286 char ipoptions
[4096], *ipp
;
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
;
298 me
->action
= ld
->action
? ld
->action
: MA_CMD
;
300 if ( ld
->menuindent
) {
303 rsprintf(&dn
, "%*s%s", ld
->menuindent
, "", me
->displayname
);
304 refstr_put(me
->displayname
);
305 me
->displayname
= dn
;
308 if ( ld
->menuseparator
) {
309 refstr_put(me
->displayname
);
310 me
->displayname
= refstr_get(empty_string
);
313 if ( ld
->menuseparator
|| ld
->menudisabled
) {
314 me
->action
= MA_DISABLED
;
315 refstr_put(me
->label
);
317 refstr_put(me
->passwd
);
322 consider_for_hotkey(m
, me
);
324 switch (me
->action
) {
330 ipp
+= sprintf(ipp
, " initrd=%s", ld
->initrd
);
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
]);
343 if ( !a
|| (a
[0] == '-' && !a
[1]) )
346 if (ld
->type
== KT_KERNEL
) {
347 rsprintf(&me
->cmdline
, "%s%s%s%s",
348 ld
->kernel
, s
, a
, ipoptions
);
350 rsprintf(&me
->cmdline
, ".%s %s%s%s%s",
351 kernel_types
[ld
->type
], ld
->kernel
, s
, a
, ipoptions
);
357 me
->cmdline
= refstr_get(ld
->kernel
);
362 me
->submenu
= ld
->submenu
;
369 if ( ld
->menudefault
&& me
->action
== MA_CMD
)
370 m
->defentry
= m
->nentries
-1;
373 clear_label_data(ld
);
376 static struct menu
*begin_submenu(const char *tag
)
378 struct menu_entry
*me
;
383 me
= new_entry(current_menu
);
384 me
->displayname
= refstrdup(tag
);
385 return new_menu(current_menu
, me
, refstr_get(me
->displayname
));
388 static struct menu
*end_submenu(void)
390 return current_menu
->parent
? current_menu
->parent
: current_menu
;
393 static const char *unlabel(const char *str
)
395 /* Convert a CLI-style command line to an executable command line */
398 struct menu_entry
*me
;
402 while ( *p
&& !my_isspace(*p
) )
405 /* p now points to the first byte beyond the kernel name */
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
);
421 refdup_word(char **p
)
426 while (*ep
&& !my_isspace(*ep
))
430 return refstrndup(sp
, ep
-sp
);
433 int my_isxdigit(char c
)
437 return (uc
-'0') < 10 ||
441 unsigned int hexval(char c
)
443 unsigned char uc
= c
| 0x20;
453 unsigned int hexval2(const char *p
)
455 return (hexval(p
[0]) << 4)+hexval(p
[1]);
458 uint32_t parse_argb(char **p
)
470 while (my_isxdigit(*ep
))
480 (hexval(sp
[0])*0x11 << 16) +
481 (hexval(sp
[1])*0x11 << 8) +
482 (hexval(sp
[2])*0x11);
486 (hexval(sp
[0])*0x11 << 24) +
487 (hexval(sp
[1])*0x11 << 16) +
488 (hexval(sp
[2])*0x11 << 8) +
489 (hexval(sp
[3])*0x11);
491 case 6: /* #rrggbb */
492 case 9: /* #rrrgggbbb */
493 case 12: /* #rrrrggggbbbb */
497 (hexval2(sp
+0) << 16) +
498 (hexval2(sp
+dl
) << 8) +
501 case 8: /* #aarrggbb */
502 /* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb,
503 assume the latter is a more common format */
504 case 16: /* #aaaarrrrggggbbbb */
507 (hexval2(sp
+0) << 24) +
508 (hexval2(sp
+dl
) << 16) +
509 (hexval2(sp
+dl
*2) << 8) +
513 argb
= 0xffff0000; /* Bright red (error indication) */
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.
525 static const char *append
= NULL
;
526 static unsigned int ipappend
= 0;
527 static struct labeldata ld
;
529 static int parse_one_config(const char *filename
);
531 static char *is_kernel_type(char *cmdstr
, enum kernel_type
*type
)
533 const char * const *p
;
535 enum kernel_type t
= KT_NONE
;
537 for (p
= kernel_types
; *p
; p
++, t
++) {
538 if ((q
= looking_at(cmdstr
, *p
))) {
547 static char *is_message_name(char *cmdstr
, enum message_number
*msgnr
)
550 enum message_number i
;
552 for (i
= 0; i
< MSG_COUNT
; i
++) {
553 if ((q
= looking_at(cmdstr
, messages
[i
].name
))) {
562 static char *is_fkey(char *cmdstr
, int *fkeyno
)
567 if ((cmdstr
[0]|0x20) != 'f')
570 no
= strtoul(cmdstr
+1, &q
, 10);
574 if (no
< 0 || no
> 12)
577 *fkeyno
= (no
== 0) ? 10 : no
-1;
581 static void parse_config_file(FILE *f
)
583 char line
[MAX_LINE
], *p
, *ep
, ch
;
584 enum kernel_type type
;
585 enum message_number msgnr
;
587 struct menu
*m
= current_menu
;
589 while ( fgets(line
, sizeof line
, f
) ) {
590 p
= strchr(line
, '\r');
593 p
= strchr(line
, '\n');
599 if ( looking_at(p
, "menu") ) {
602 if ( looking_at(p
, "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
);
611 /* MENU LABEL -> MENU TITLE on submenu */
612 refstr_put(m
->title
);
613 m
->title
= strip_caret(m
->parent_entry
->displayname
);
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
);
626 } else if ( looking_at(p
, "default") ) {
629 } else if (m
->parent_entry
) {
630 m
->parent
->defentry
= m
->parent_entry
->entry
;
632 } else if ( looking_at(p
, "hide") ) {
634 } else if ( looking_at(p
, "passwd") ) {
636 refstr_put(ld
.passwd
);
637 ld
.passwd
= refstrdup(skipspace(p
+6));
638 } else if ( m
->parent_entry
) {
639 refstr_put(m
->parent_entry
->passwd
);
640 m
->parent_entry
->passwd
= refstrdup(skipspace(p
+6));
642 } else if ( looking_at(p
, "shiftkey") ) {
644 } else if ( looking_at(p
, "onerror") ) {
645 refstr_put(m
->onerror
);
646 m
->onerror
= refstrdup(skipspace(p
+7));
647 } else if ( looking_at(p
, "master") ) {
649 if ( looking_at(p
, "passwd") ) {
650 refstr_put(m
->menu_master_passwd
);
651 m
->menu_master_passwd
= refstrdup(skipspace(p
+6));
653 } else if ( (ep
= looking_at(p
, "include")) ) {
655 } else if ( (ep
= looking_at(p
, "background")) ) {
657 refstr_put(m
->menu_background
);
658 m
->menu_background
= refdup_word(&p
);
659 } else if ( (ep
= looking_at(p
, "hidden")) ) {
661 } else if ( (ep
= is_message_name(p
, &msgnr
)) ) {
662 refstr_put(m
->messages
[msgnr
]);
663 m
->messages
[msgnr
] = refstrdup(skipspace(ep
));
664 } else if ((ep
= looking_at(p
, "color")) ||
665 (ep
= looking_at(p
, "colour"))) {
667 struct color_table
*cptr
;
669 cptr
= m
->color_table
;
670 for ( i
= 0; i
< menu_color_table_size
; i
++ ) {
671 if ( (ep
= looking_at(p
, cptr
->name
)) ) {
674 if (looking_at(p
, "*")) {
677 refstr_put(cptr
->ansi
);
678 cptr
->ansi
= refdup_word(&p
);
683 if (looking_at(p
, "*"))
686 cptr
->argb_fg
= parse_argb(&p
);
690 if (looking_at(p
, "*"))
693 cptr
->argb_bg
= parse_argb(&p
);
695 /* Parse a shadow mode */
698 if (ch
== 'n') /* none */
699 cptr
->shadow
= SHADOW_NONE
;
700 else if (ch
== 's') /* std, standard */
701 cptr
->shadow
= SHADOW_NORMAL
;
702 else if (ch
== 'a') /* all */
703 cptr
->shadow
= SHADOW_ALL
;
704 else if (ch
== 'r') /* rev, reverse */
705 cptr
->shadow
= SHADOW_REVERSE
;
713 } else if ((ep
= looking_at(p
, "msgcolor")) ||
714 (ep
= looking_at(p
, "msgcolour"))) {
715 unsigned int fg_mask
= MSG_COLORS_DEF_FG
;
716 unsigned int bg_mask
= MSG_COLORS_DEF_BG
;
717 enum color_table_shadow shadow
= MSG_COLORS_DEF_SHADOW
;
721 if (!looking_at(p
, "*"))
722 fg_mask
= parse_argb(&p
);
726 if (!looking_at(p
, "*"))
727 bg_mask
= parse_argb(&p
);
732 shadow
= SHADOW_NONE
;
735 shadow
= SHADOW_NORMAL
;
741 shadow
= SHADOW_REVERSE
;
744 /* go with default */
749 set_msg_colors_global(m
->color_table
, fg_mask
, bg_mask
, shadow
);
750 } else if ( looking_at(p
, "separator") ) {
751 record(m
, &ld
, append
);
752 ld
.label
= refstr_get(empty_string
);
753 ld
.menuseparator
= 1;
754 record(m
, &ld
, append
);
755 } else if ( looking_at(p
, "disable") ||
756 looking_at(p
, "disabled")) {
758 } else if ( looking_at(p
, "indent") ) {
759 ld
.menuindent
= atoi(skipspace(p
+6));
760 } else if ( looking_at(p
, "begin") ) {
761 record(m
, &ld
, append
);
762 m
= current_menu
= begin_submenu(skipspace(p
+5));
763 } else if ( looking_at(p
, "end") ) {
764 record(m
, &ld
, append
);
765 m
= current_menu
= end_submenu();
766 } else if ( looking_at(p
, "quit") ) {
769 } else if ( looking_at(p
, "goto") ) {
771 ld
.action
= MA_GOTO_UNRES
;
772 refstr_put(ld
.kernel
);
773 ld
.kernel
= refstrdup(skipspace(p
+4));
775 } else if ( looking_at(p
, "exit") ) {
777 if (ld
.label
&& m
->parent
) {
779 /* This is really just a goto, except for the marker */
780 ld
.action
= MA_EXIT_UNRES
;
781 refstr_put(ld
.kernel
);
782 ld
.kernel
= refstrdup(p
);
785 ld
.submenu
= m
->parent
;
788 } else if ( looking_at(p
, "start") ) {
791 /* Unknown, check for layout parameters */
792 enum parameter_number mp
;
793 for (mp
= 0; mp
< NPARAMS
; mp
++) {
794 if ( (ep
= looking_at(p
, mparm
[mp
].name
)) ) {
795 m
->mparm
[mp
] = atoi(skipspace(ep
));
800 } else if ( looking_at(p
, "text") ) {
804 } cmd
= TEXT_UNKNOWN
;
805 int len
= ld
.helptext
? strlen(ld
.helptext
) : 0;
810 if (looking_at(p
, "help"))
813 while ( fgets(line
, sizeof line
, f
) ) {
815 if (looking_at(p
, "endtext"))
824 ld
.helptext
= realloc(ld
.helptext
, len
+xlen
+1);
825 memcpy(ld
.helptext
+len
, line
, xlen
+1);
830 } else if ( (ep
= is_fkey(p
, &fkeyno
)) ) {
832 if (m
->fkeyhelp
[fkeyno
].textname
) {
833 refstr_put(m
->fkeyhelp
[fkeyno
].textname
);
834 m
->fkeyhelp
[fkeyno
].textname
= NULL
;
836 if (m
->fkeyhelp
[fkeyno
].background
) {
837 refstr_put(m
->fkeyhelp
[fkeyno
].background
);
838 m
->fkeyhelp
[fkeyno
].background
= NULL
;
841 refstr_put(m
->fkeyhelp
[fkeyno
].textname
);
842 m
->fkeyhelp
[fkeyno
].textname
= refdup_word(&p
);
845 m
->fkeyhelp
[fkeyno
].background
= refdup_word(&p
);
847 } else if ( (ep
= looking_at(p
, "include")) ) {
852 file
= refdup_word(&p
);
855 record(m
, &ld
, append
);
856 m
= current_menu
= begin_submenu(p
);
857 parse_one_config(file
);
858 record(m
, &ld
, append
);
859 m
= current_menu
= end_submenu();
861 parse_one_config(file
);
865 } else if ( looking_at(p
, "append") ) {
866 const char *a
= refstrdup(skipspace(p
+6));
868 refstr_put(ld
.append
);
874 } else if ( looking_at(p
, "initrd") ) {
875 const char *a
= refstrdup(skipspace(p
+6));
877 refstr_put(ld
.initrd
);
882 } else if ( looking_at(p
, "label") ) {
884 record(m
, &ld
, append
);
885 ld
.label
= refstrdup(p
);
886 ld
.kernel
= refstrdup(p
);
893 ld
.ipappend
= ipappend
;
894 ld
.menudefault
= ld
.menuhide
= ld
.menuseparator
=
895 ld
.menudisabled
= ld
.menuindent
= 0;
896 } else if ( (ep
= is_kernel_type(p
, &type
)) ) {
898 refstr_put(ld
.kernel
);
899 ld
.kernel
= refstrdup(skipspace(ep
));
902 } else if ( looking_at(p
, "timeout") ) {
903 m
->timeout
= (atoi(skipspace(p
+7))*CLK_TCK
+9)/10;
904 } else if ( looking_at(p
, "totaltimeout") ) {
905 totaltimeout
= (atoll(skipspace(p
+13))*CLK_TCK
+9)/10;
906 } else if ( looking_at(p
, "ontimeout") ) {
907 m
->ontimeout
= refstrdup(skipspace(p
+9));
908 } else if ( looking_at(p
, "allowoptions") ) {
909 m
->allowedit
= atoi(skipspace(p
+12));
910 } else if ( looking_at(p
, "ipappend") ) {
912 ld
.ipappend
= atoi(skipspace(p
+8));
914 ipappend
= atoi(skipspace(p
+8));
919 static int parse_one_config(const char *filename
)
923 if (!strcmp(filename
, "~"))
924 filename
= syslinux_config_file();
926 f
= fopen(filename
, "r");
930 parse_config_file(f
);
936 static void resolve_gotos(void)
938 struct menu_entry
*me
;
941 for (me
= all_entries
; me
; me
= me
->next
) {
942 if (me
->action
== MA_GOTO_UNRES
||
943 me
->action
== MA_EXIT_UNRES
) {
944 m
= find_menu(me
->cmdline
);
945 refstr_put(me
->cmdline
);
949 me
->action
--; /* Drop the _UNRES */
951 me
->action
= MA_DISABLED
;
957 void parse_configs(char **argv
)
959 const char *filename
;
962 empty_string
= refstrdup("");
964 /* Initialize defaults for the root and hidden menus */
965 hide_menu
= new_menu(NULL
, NULL
, refstrdup(".hidden"));
966 root_menu
= new_menu(NULL
, NULL
, refstrdup(".top"));
967 start_menu
= root_menu
;
969 /* Other initialization */
970 memset(&ld
, 0, sizeof(struct labeldata
));
972 /* Actually process the files */
973 current_menu
= root_menu
;
975 parse_one_config("~");
977 while ( (filename
= *argv
++) )
978 parse_one_config(filename
);
981 /* On final EOF process the last label statement */
982 record(current_menu
, &ld
, append
);
984 /* Common postprocessing */
987 for (m
= menu_list
; m
; m
= m
->next
) {
988 m
->curentry
= m
->defentry
; /* All menus start at their defaults */
991 m
->ontimeout
= unlabel(m
->ontimeout
);
993 m
->onerror
= unlabel(m
->onerror
);