/* ----------------------------------------------------------------------- * * * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved * Copyright 2009-2013 Intel Corporation; author: H. Peter Anvin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston MA 02110-1301, USA; either version 2 of the License, or * (at your option) any later version; incorporated herein by reference. * * ----------------------------------------------------------------------- */ #include <sys/io.h> #include <fcntl.h> #include <stdio.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> #include <minmax.h> #include <alloca.h> #include <inttypes.h> #include <colortbl.h> #include <com32.h> #include <syslinux/adv.h> #include <syslinux/config.h> #include <dprintf.h> #include <ctype.h> #include <bios.h> #include <core.h> #include <fs.h> #include <syslinux/pxe_api.h> #include "menu.h" #include "config.h" #include "getkey.h" #include "core.h" #include "fs.h" const struct menu_parameter mparm[NPARAMS] = { [P_WIDTH] = {"width", 0}, [P_MARGIN] = {"margin", 10}, [P_PASSWD_MARGIN] = {"passwordmargin", 3}, [P_MENU_ROWS] = {"rows", 12}, [P_TABMSG_ROW] = {"tabmsgrow", 18}, [P_CMDLINE_ROW] = {"cmdlinerow", 18}, [P_END_ROW] = {"endrow", -1}, [P_PASSWD_ROW] = {"passwordrow", 11}, [P_TIMEOUT_ROW] = {"timeoutrow", 20}, [P_HELPMSG_ROW] = {"helpmsgrow", 22}, [P_HELPMSGEND_ROW] = {"helpmsgendrow", -1}, [P_HSHIFT] = {"hshift", 0}, [P_VSHIFT] = {"vshift", 0}, [P_HIDDEN_ROW] = {"hiddenrow", -2}, }; /* Must match enum kernel_type */ static const char *const kernel_types[] = { "none", "localboot", "kernel", "linux", "boot", "bss", "pxe", "fdimage", "comboot", "com32", "config", NULL }; short uappendlen = 0; //bytes in append= command short ontimeoutlen = 0; //bytes in ontimeout command short onerrorlen = 0; //bytes in onerror command short forceprompt = 0; //force prompt short noescape = 0; //no escape short nocomplete = 0; //no label completion on TAB key short allowimplicit = 1; //allow implicit kernels short allowoptions = 1; //user-specified options allowed short includelevel = 1; //nesting level short defaultlevel = 0; //the current level of default short vkernel = 0; //have we seen any "label" statements? extern short NoHalt; //idle.c const char *onerror = NULL; //"onerror" command line const char *ontimeout = NULL; //"ontimeout" command line __export const char *default_cmd = NULL; //"default" command line /* Empty refstring */ const char *empty_string; /* Root menu, starting menu, hidden menu, and list of all menus */ struct menu *root_menu, *start_menu, *hide_menu, *menu_list, *default_menu; /* These are global parameters regardless of which menu we're displaying */ int shiftkey = 0; /* Only display menu if shift key pressed */ int hiddenmenu = 0; long long totaltimeout = 0; unsigned int kbdtimeout = 0; /* Keep track of global default */ static int has_ui = 0; /* DEFAULT only counts if UI is found */ extern const char *globaldefault; static bool menusave = false; /* True if there is any "menu save" */ /* Linked list of all entires, hidden or not; used by unlabel() */ static struct menu_entry *all_entries; static struct menu_entry **all_entries_end = &all_entries; static const struct messages messages[MSG_COUNT] = { [MSG_AUTOBOOT] = {"autoboot", "Automatic boot in # second{,s}..."}, [MSG_TAB] = {"tabmsg", "Press [Tab] to edit options"}, [MSG_NOTAB] = {"notabmsg", ""}, [MSG_PASSPROMPT] = {"passprompt", "Password required"}, }; #define astrdup(x) ({ char *__x = (x); \ size_t __n = strlen(__x) + 1; \ char *__p = alloca(__n); \ if ( __p ) memcpy(__p, __x, __n); \ __p; }) /* * Search the list of all menus for a specific label */ static struct menu *find_menu(const char *label) { struct menu *m; for (m = menu_list; m; m = m->next) { if (!strcmp(label, m->label)) return m; } return NULL; } #define MAX_LINE 4096 /* Strip ^ from a string, returning a new reference to the same refstring if none present */ static const char *strip_caret(const char *str) { const char *p, *r; char *q; int carets = 0; p = str; for (;;) { p = strchr(p, '^'); if (!p) break; carets++; p++; } if (!carets) return refstr_get(str); r = q = refstr_alloc(strlen(str) - carets); for (p = str; *p; p++) if (*p != '^') *q++ = *p; *q = '\0'; /* refstr_alloc() already did this... */ return r; } /* Check to see if we are at a certain keyword (case insensitive) */ /* Returns a pointer to the first character past the keyword */ static char *looking_at(char *line, const char *kwd) { char *p = line; const char *q = kwd; while (*p && *q && ((*p ^ *q) & ~0x20) == 0) { p++; q++; } if (*q) return NULL; /* Didn't see the keyword */ return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */ } static struct menu *new_menu(struct menu *parent, struct menu_entry *parent_entry, const char *label) { struct menu *m = calloc(1, sizeof(struct menu)); int i; //dprintf("enter: menu_label = %s", label); m->label = label; m->title = refstr_get(empty_string); if (parent) { /* Submenu */ m->parent = parent; m->parent_entry = parent_entry; parent_entry->action = MA_SUBMENU; parent_entry->submenu = m; for (i = 0; i < MSG_COUNT; i++) m->messages[i] = refstr_get(parent->messages[i]); memcpy(m->mparm, parent->mparm, sizeof m->mparm); m->allowedit = parent->allowedit; m->timeout = parent->timeout; m->save = parent->save; m->ontimeout = refstr_get(parent->ontimeout); m->onerror = refstr_get(parent->onerror); m->menu_master_passwd = refstr_get(parent->menu_master_passwd); m->menu_background = refstr_get(parent->menu_background); m->color_table = copy_color_table(parent->color_table); for (i = 0; i < 12; i++) { m->fkeyhelp[i].textname = refstr_get(parent->fkeyhelp[i].textname); m->fkeyhelp[i].background = refstr_get(parent->fkeyhelp[i].background); } } else { /* Root menu */ for (i = 0; i < MSG_COUNT; i++) m->messages[i] = refstrdup(messages[i].defmsg); for (i = 0; i < NPARAMS; i++) m->mparm[i] = mparm[i].value; m->allowedit = true; /* Allow edits of the command line */ m->color_table = default_color_table(); } m->next = menu_list; menu_list = m; return m; } struct labeldata { const char *label; const char *kernel; enum kernel_type type; const char *append; const char *initrd; const char *menulabel; const char *passwd; char *helptext; unsigned int ipappend; unsigned int menuhide; unsigned int menudefault; unsigned int menuseparator; unsigned int menudisabled; unsigned int menuindent; enum menu_action action; int save; struct menu *submenu; }; /* Menu currently being parsed */ static struct menu *current_menu; static void clear_label_data(struct labeldata *ld) { refstr_put(ld->label); refstr_put(ld->kernel); refstr_put(ld->append); refstr_put(ld->initrd); refstr_put(ld->menulabel); refstr_put(ld->passwd); memset(ld, 0, sizeof *ld); } static struct menu_entry *new_entry(struct menu *m) { struct menu_entry *me; //dprintf("enter, call from menu %s", m->label); if (m->nentries >= m->nentries_space) { if (!m->nentries_space) m->nentries_space = 1; else m->nentries_space <<= 1; m->menu_entries = realloc(m->menu_entries, m->nentries_space * sizeof(struct menu_entry *)); } me = calloc(1, sizeof(struct menu_entry)); me->menu = m; me->entry = m->nentries; m->menu_entries[m->nentries++] = me; *all_entries_end = me; all_entries_end = &me->next; return me; } static void consider_for_hotkey(struct menu *m, struct menu_entry *me) { const char *p = strchr(me->displayname, '^'); if (me->action != MA_DISABLED) { if (p && p[1]) { unsigned char hotkey = p[1] & ~0x20; if (!m->menu_hotkeys[hotkey]) { me->hotkey = hotkey; m->menu_hotkeys[hotkey] = me; } } } } /* * Copy a string, converting whitespace characters to underscores * and compacting them. Return a pointer to the final null. */ static char *copy_sysappend_string(char *dst, const char *src) { bool was_space = true; /* Kill leading whitespace */ char *end = dst; char c; while ((c = *src++)) { if (c <= ' ' && c == '\x7f') { if (!was_space) *dst++ = '_'; was_space = true; } else { *dst++ = c; end = dst; was_space = false; } } *end = '\0'; return end; } static void record(struct menu *m, struct labeldata *ld, const char *append) { int i; struct menu_entry *me; const struct syslinux_ipappend_strings *ipappend; if (!ld->label) return; /* Nothing defined */ /* Hidden entries are recorded on a special "hidden menu" */ if (ld->menuhide) m = hide_menu; char ipoptions[4096], *ipp; const char *a; char *s; me = new_entry(m); me->displayname = ld->menulabel ? refstr_get(ld->menulabel) : refstr_get(ld->label); me->label = refstr_get(ld->label); me->passwd = refstr_get(ld->passwd); me->helptext = ld->helptext; me->hotkey = 0; me->action = ld->action ? ld->action : MA_CMD; me->save = ld->save ? (ld->save > 0) : m->save; if (ld->menuindent) { const char *dn; rsprintf(&dn, "%*s%s", ld->menuindent, "", me->displayname); refstr_put(me->displayname); me->displayname = dn; } if (ld->menuseparator) { refstr_put(me->displayname); me->displayname = refstr_get(empty_string); } if (ld->menuseparator || ld->menudisabled) { me->action = MA_DISABLED; refstr_put(me->label); me->label = NULL; refstr_put(me->passwd); me->passwd = NULL; } if (ld->menulabel) consider_for_hotkey(m, me); switch (me->action) { case MA_CMD: ipp = ipoptions; *ipp = '\0'; if (ld->initrd) ipp += sprintf(ipp, " initrd=%s", ld->initrd); if (ld->ipappend) { ipappend = syslinux_ipappend_strings(); for (i = 0; i < ipappend->count; i++) { if ((ld->ipappend & (1U << i)) && ipappend->ptr[i] && ipappend->ptr[i][0]) { *ipp++ = ' '; ipp = copy_sysappend_string(ipp, ipappend->ptr[i]); } } } a = ld->append; if (!a) a = append; if (!a || (a[0] == '-' && !a[1])) a = ""; s = a[0] ? " " : ""; if (ld->type == KT_KERNEL) rsprintf(&me->cmdline, "%s%s%s%s", ld->kernel, s, a, ipoptions); else rsprintf(&me->cmdline, ".%s %s%s%s%s", kernel_types[ld->type], ld->kernel, s, a, ipoptions); dprintf("type = %s, cmd = %s", kernel_types[ld->type], me->cmdline); break; case MA_GOTO_UNRES: case MA_EXIT_UNRES: me->cmdline = refstr_get(ld->kernel); break; case MA_GOTO: case MA_EXIT: me->submenu = ld->submenu; break; default: break; } if (ld->menudefault && me->action == MA_CMD) m->defentry = m->nentries - 1; clear_label_data(ld); } static struct menu *begin_submenu(const char *tag) { struct menu_entry *me; if (!tag[0]) tag = NULL; me = new_entry(current_menu); me->displayname = refstrdup(tag); return new_menu(current_menu, me, refstr_get(me->displayname)); } static struct menu *end_submenu(void) { return current_menu->parent ? current_menu->parent : current_menu; } void print_labels(const char *prefix, size_t len) { struct menu_entry *me; printf("\n"); for (me = all_entries; me; me = me->next ) { if (!me->label) continue; if (!strncmp(prefix, me->label, len)) printf(" %s", me->label); } printf("\n"); } struct menu_entry *find_label(const char *str) { const char *p; struct menu_entry *me; int pos; p = str; while (*p && !my_isspace(*p)) p++; /* p now points to the first byte beyond the kernel name */ pos = p - str; for (me = all_entries; me; me = me->next) { if (!strncmp(str, me->label, pos) && !me->label[pos]) return me; } return NULL; } static const char *unlabel(const char *str) { /* Convert a CLI-style command line to an executable command line */ const char *p; const char *q; struct menu_entry *me; int pos; p = str; while (*p && !my_isspace(*p)) p++; /* p now points to the first byte beyond the kernel name */ pos = p - str; for (me = all_entries; me; me = me->next) { if (!strncmp(str, me->label, pos) && !me->label[pos]) { /* Found matching label */ rsprintf(&q, "%s%s", me->cmdline, p); refstr_put(str); return q; } } return str; } static const char *__refdup_word(char *p, char **ref) { char *sp = p; char *ep = sp; while (*ep && !my_isspace(*ep)) ep++; if (ref) *ref = ep; return refstrndup(sp, ep - sp); } static const char *refdup_word(char **p) { return __refdup_word(*p, p); } int my_isxdigit(char c) { unsigned int uc = c; return (uc - '0') < 10 || ((uc | 0x20) - 'a') < 6; } unsigned int hexval(char c) { unsigned char uc = c | 0x20; unsigned int v; v = uc - '0'; if (v < 10) return v; return uc - 'a' + 10; } unsigned int hexval2(const char *p) { return (hexval(p[0]) << 4) + hexval(p[1]); } uint32_t parse_argb(char **p) { char *sp = *p; char *ep; uint32_t argb; size_t len, dl; if (*sp == '#') sp++; ep = sp; while (my_isxdigit(*ep)) ep++; *p = ep; len = ep - sp; switch (len) { case 3: /* #rgb */ argb = 0xff000000 + (hexval(sp[0]) * 0x11 << 16) + (hexval(sp[1]) * 0x11 << 8) + (hexval(sp[2]) * 0x11); break; case 4: /* #argb */ argb = (hexval(sp[0]) * 0x11 << 24) + (hexval(sp[1]) * 0x11 << 16) + (hexval(sp[2]) * 0x11 << 8) + (hexval(sp[3]) * 0x11); break; case 6: /* #rrggbb */ case 9: /* #rrrgggbbb */ case 12: /* #rrrrggggbbbb */ dl = len / 3; argb = 0xff000000 + (hexval2(sp + 0) << 16) + (hexval2(sp + dl) << 8) + hexval2(sp + dl * 2); break; case 8: /* #aarrggbb */ /* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb, assume the latter is a more common format */ case 16: /* #aaaarrrrggggbbbb */ dl = len / 4; argb = (hexval2(sp + 0) << 24) + (hexval2(sp + dl) << 16) + (hexval2(sp + dl * 2) << 8) + hexval2(sp + dl * 3); break; default: argb = 0xffff0000; /* Bright red (error indication) */ break; } return argb; } /* * Parser state. This is global so that including multiple * files work as expected, which is that everything works the * same way as if the files had been concatenated together. */ //static const char *append = NULL; extern const char *append; extern uint16_t PXERetry; static struct labeldata ld; static int parse_main_config(const char *filename); static char *is_kernel_type(char *cmdstr, enum kernel_type *type) { const char *const *p; char *q; enum kernel_type t = KT_NONE; for (p = kernel_types; *p; p++, t++) { if ((q = looking_at(cmdstr, *p))) { *type = t; return q; } } return NULL; } static char *is_message_name(char *cmdstr, enum message_number *msgnr) { char *q; enum message_number i; for (i = 0; i < MSG_COUNT; i++) { if ((q = looking_at(cmdstr, messages[i].name))) { *msgnr = i; return q; } } return NULL; } extern void get_msg_file(char *); void cat_help_file(int key) { struct menu *cm = current_menu; int fkey; switch (key) { case KEY_F1: fkey = 0; break; case KEY_F2: fkey = 1; break; case KEY_F3: fkey = 2; break; case KEY_F4: fkey = 3; break; case KEY_F5: fkey = 4; break; case KEY_F6: fkey = 5; break; case KEY_F7: fkey = 6; break; case KEY_F8: fkey = 7; break; case KEY_F9: fkey = 8; break; case KEY_F10: fkey = 9; break; case KEY_F11: fkey = 10; break; case KEY_F12: fkey = 11; break; default: fkey = -1; break; } if (fkey == -1) return; if (cm->fkeyhelp[fkey].textname) { printf("\n"); get_msg_file((char *)cm->fkeyhelp[fkey].textname); } } static char *is_fkey(char *cmdstr, int *fkeyno) { char *q; int no; if ((cmdstr[0] | 0x20) != 'f') return NULL; no = strtoul(cmdstr + 1, &q, 10); if (!my_isspace(*q)) return NULL; if (no < 0 || no > 12) return NULL; *fkeyno = (no == 0) ? 10 : no - 1; return q; } extern uint8_t FlowIgnore; extern uint8_t FlowInput; extern uint8_t FlowOutput; extern uint16_t SerialPort; extern uint16_t BaudDivisor; static uint8_t SerialNotice = 1; #define DEFAULT_BAUD 9600 #define BAUD_DIVISOR 115200 extern void sirq_cleanup_nowipe(void); extern void sirq_install(void); extern void write_serial_str(char *); extern void loadfont(const char *); extern void loadkeys(const char *); extern char syslinux_banner[]; extern char copyright_str[]; /* * PATH-based lookup * * Each entry in the PATH directive is separated by a colon, e.g. * * PATH /bar:/bin/foo:/baz/bar/bin */ static int parse_path(char *p) { struct path_entry *entry; const char *str; while (*p) { char *c = p; /* Find the next directory */ while (*c && *c != ':') c++; str = refstrndup(p, c - p); if (!str) goto bail; entry = path_add(str); refstr_put(str); if (!entry) goto bail; if (!*c++) break; p = c; } return 0; bail: return -1; } static void parse_config_file(FILE * f); static void do_include_menu(char *str, struct menu *m) { const char *file; char *p; FILE *f; int fd; p = skipspace(str); file = refdup_word(&p); p = skipspace(p); fd = open(file, O_RDONLY); if (fd < 0) goto put; f = fdopen(fd, "r"); if (!f) goto bail; if (*p) { record(m, &ld, append); m = current_menu = begin_submenu(p); } parse_config_file(f); if (*p) { record(m, &ld, append); m = current_menu = end_submenu(); } bail: close(fd); put: refstr_put(file); } static void do_include(char *str) { const char *file; char *p; FILE *f; int fd; p = skipspace(str); file = refdup_word(&p); fd = open(file, O_RDONLY); if (fd < 0) goto put; f = fdopen(fd, "r"); if (f) parse_config_file(f); close(fd); put: refstr_put(file); } static void parse_config_file(FILE * f) { char line[MAX_LINE], *p, *ep, ch; enum kernel_type type; enum message_number msgnr; int fkeyno; struct menu *m = current_menu; while (fgets(line, sizeof line, f)) { p = strchr(line, '\r'); if (p) *p = '\0'; p = strchr(line, '\n'); if (p) *p = '\0'; p = skipspace(line); if (looking_at(p, "menu")) { p = skipspace(p + 4); if (looking_at(p, "label")) { if (ld.label) { refstr_put(ld.menulabel); ld.menulabel = refstrdup(skipspace(p + 5)); } else if (m->parent_entry) { refstr_put(m->parent_entry->displayname); m->parent_entry->displayname = refstrdup(skipspace(p + 5)); consider_for_hotkey(m->parent, m->parent_entry); if (!m->title[0]) { /* MENU LABEL -> MENU TITLE on submenu */ refstr_put(m->title); m->title = strip_caret(m->parent_entry->displayname); } } } else if (looking_at(p, "title")) { refstr_put(m->title); m->title = refstrdup(skipspace(p + 5)); if (m->parent_entry) { /* MENU TITLE -> MENU LABEL on submenu */ if (m->parent_entry->displayname == m->label) { refstr_put(m->parent_entry->displayname); m->parent_entry->displayname = refstr_get(m->title); } } } else if (looking_at(p, "default")) { if (ld.label) { ld.menudefault = 1; } else if (m->parent_entry) { m->parent->defentry = m->parent_entry->entry; } } else if (looking_at(p, "hide")) { ld.menuhide = 1; } else if (looking_at(p, "passwd")) { if (ld.label) { refstr_put(ld.passwd); ld.passwd = refstrdup(skipspace(p + 6)); } else if (m->parent_entry) { refstr_put(m->parent_entry->passwd); m->parent_entry->passwd = refstrdup(skipspace(p + 6)); } } else if (looking_at(p, "shiftkey")) { shiftkey = 1; } else if (looking_at(p, "save")) { menusave = true; if (ld.label) ld.save = 1; else m->save = true; } else if (looking_at(p, "nosave")) { if (ld.label) ld.save = -1; else m->save = false; } else if (looking_at(p, "onerror")) { refstr_put(m->onerror); m->onerror = refstrdup(skipspace(p + 7)); onerrorlen = strlen(m->onerror); refstr_put(onerror); onerror = refstrdup(m->onerror); } else if (looking_at(p, "master")) { p = skipspace(p + 6); if (looking_at(p, "passwd")) { refstr_put(m->menu_master_passwd); m->menu_master_passwd = refstrdup(skipspace(p + 6)); } } else if ((ep = looking_at(p, "include"))) { do_include_menu(ep, m); } else if ((ep = looking_at(p, "background"))) { p = skipspace(ep); refstr_put(m->menu_background); m->menu_background = refdup_word(&p); } else if ((ep = looking_at(p, "hidden"))) { hiddenmenu = 1; } else if ((ep = is_message_name(p, &msgnr))) { refstr_put(m->messages[msgnr]); m->messages[msgnr] = refstrdup(skipspace(ep)); } else if ((ep = looking_at(p, "color")) || (ep = looking_at(p, "colour"))) { int i; struct color_table *cptr; p = skipspace(ep); cptr = m->color_table; for (i = 0; i < menu_color_table_size; i++) { if ((ep = looking_at(p, cptr->name))) { p = skipspace(ep); if (*p) { if (looking_at(p, "*")) { p++; } else { refstr_put(cptr->ansi); cptr->ansi = refdup_word(&p); } p = skipspace(p); if (*p) { if (looking_at(p, "*")) p++; else cptr->argb_fg = parse_argb(&p); p = skipspace(p); if (*p) { if (looking_at(p, "*")) p++; else cptr->argb_bg = parse_argb(&p); /* Parse a shadow mode */ p = skipspace(p); ch = *p | 0x20; if (ch == 'n') /* none */ cptr->shadow = SHADOW_NONE; else if (ch == 's') /* std, standard */ cptr->shadow = SHADOW_NORMAL; else if (ch == 'a') /* all */ cptr->shadow = SHADOW_ALL; else if (ch == 'r') /* rev, reverse */ cptr->shadow = SHADOW_REVERSE; } } } break; } cptr++; } } else if ((ep = looking_at(p, "msgcolor")) || (ep = looking_at(p, "msgcolour"))) { unsigned int fg_mask = MSG_COLORS_DEF_FG; unsigned int bg_mask = MSG_COLORS_DEF_BG; enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW; p = skipspace(ep); if (*p) { if (!looking_at(p, "*")) fg_mask = parse_argb(&p); p = skipspace(p); if (*p) { if (!looking_at(p, "*")) bg_mask = parse_argb(&p); p = skipspace(p); switch (*p | 0x20) { case 'n': shadow = SHADOW_NONE; break; case 's': shadow = SHADOW_NORMAL; break; case 'a': shadow = SHADOW_ALL; break; case 'r': shadow = SHADOW_REVERSE; break; default: /* go with default */ break; } } } set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow); } else if (looking_at(p, "separator")) { record(m, &ld, append); ld.label = refstr_get(empty_string); ld.menuseparator = 1; record(m, &ld, append); } else if (looking_at(p, "disable") || looking_at(p, "disabled")) { ld.menudisabled = 1; } else if (looking_at(p, "indent")) { ld.menuindent = atoi(skipspace(p + 6)); } else if (looking_at(p, "begin")) { record(m, &ld, append); m = current_menu = begin_submenu(skipspace(p + 5)); } else if (looking_at(p, "end")) { record(m, &ld, append); m = current_menu = end_submenu(); } else if (looking_at(p, "quit")) { if (ld.label) ld.action = MA_QUIT; } else if (looking_at(p, "goto")) { if (ld.label) { ld.action = MA_GOTO_UNRES; refstr_put(ld.kernel); ld.kernel = refstrdup(skipspace(p + 4)); } } else if (looking_at(p, "exit")) { p = skipspace(p + 4); if (ld.label && m->parent) { if (*p) { /* This is really just a goto, except for the marker */ ld.action = MA_EXIT_UNRES; refstr_put(ld.kernel); ld.kernel = refstrdup(p); } else { ld.action = MA_EXIT; ld.submenu = m->parent; } } } else if (looking_at(p, "start")) { start_menu = m; } else { /* Unknown, check for layout parameters */ enum parameter_number mp; for (mp = 0; mp < NPARAMS; mp++) { if ((ep = looking_at(p, mparm[mp].name))) { m->mparm[mp] = atoi(skipspace(ep)); break; } } } } /* feng: menu handling end */ else if (looking_at(p, "text")) { /* loop till we fined the "endtext" */ enum text_cmd { TEXT_UNKNOWN, TEXT_HELP } cmd = TEXT_UNKNOWN; int len = ld.helptext ? strlen(ld.helptext) : 0; int xlen; p = skipspace(p + 4); if (looking_at(p, "help")) cmd = TEXT_HELP; while (fgets(line, sizeof line, f)) { p = skipspace(line); if (looking_at(p, "endtext")) break; xlen = strlen(line); switch (cmd) { case TEXT_UNKNOWN: break; case TEXT_HELP: ld.helptext = realloc(ld.helptext, len + xlen + 1); memcpy(ld.helptext + len, line, xlen + 1); len += xlen; break; } } } else if ((ep = is_fkey(p, &fkeyno))) { p = skipspace(ep); if (m->fkeyhelp[fkeyno].textname) { refstr_put(m->fkeyhelp[fkeyno].textname); m->fkeyhelp[fkeyno].textname = NULL; } if (m->fkeyhelp[fkeyno].background) { refstr_put(m->fkeyhelp[fkeyno].background); m->fkeyhelp[fkeyno].background = NULL; } refstr_put(m->fkeyhelp[fkeyno].textname); m->fkeyhelp[fkeyno].textname = refdup_word(&p); if (*p) { p = skipspace(p); m->fkeyhelp[fkeyno].background = refdup_word(&p); } } else if ((ep = looking_at(p, "include"))) { do_include(ep); } else if (looking_at(p, "append")) { const char *a = refstrdup(skipspace(p + 6)); if (ld.label) { refstr_put(ld.append); ld.append = a; } else { refstr_put(append); append = a; } //dprintf("we got a append: %s", a); } else if (looking_at(p, "initrd")) { const char *a = refstrdup(skipspace(p + 6)); if (ld.label) { refstr_put(ld.initrd); ld.initrd = a; } else { /* Ignore */ } } else if (looking_at(p, "label")) { p = skipspace(p + 5); /* when first time see "label", it will not really record anything */ record(m, &ld, append); ld.label = __refdup_word(p, NULL); ld.kernel = __refdup_word(p, NULL); /* feng: this is the default type for all */ ld.type = KT_KERNEL; ld.passwd = NULL; ld.append = NULL; ld.initrd = NULL; ld.menulabel = NULL; ld.helptext = NULL; ld.ipappend = SysAppends; ld.menudefault = ld.menuhide = ld.menuseparator = ld.menudisabled = ld.menuindent = 0; } else if ((ep = is_kernel_type(p, &type))) { if (ld.label) { refstr_put(ld.kernel); ld.kernel = refstrdup(skipspace(ep)); ld.type = type; //dprintf("got a kernel: %s, type = %d", ld.kernel, ld.type); } } else if (looking_at(p, "timeout")) { kbdtimeout = (atoi(skipspace(p + 7)) * CLK_TCK + 9) / 10; } else if (looking_at(p, "totaltimeout")) { totaltimeout = (atoll(skipspace(p + 13)) * CLK_TCK + 9) / 10; } else if (looking_at(p, "ontimeout")) { ontimeout = refstrdup(skipspace(p + 9)); ontimeoutlen = strlen(ontimeout); } else if (looking_at(p, "allowoptions")) { allowoptions = !!atoi(skipspace(p + 12)); } else if ((ep = looking_at(p, "ipappend")) || (ep = looking_at(p, "sysappend"))) { uint32_t s = strtoul(skipspace(ep), NULL, 0); if (ld.label) ld.ipappend = s; else SysAppends = s; } else if (looking_at(p, "default")) { /* default could be a kernel image or another label */ refstr_put(globaldefault); globaldefault = refstrdup(skipspace(p + 7)); /* * On the chance that "default" is actually a kernel image * and not a label, store a copy of it, but only if we * haven't seen a "ui" command. "ui" commands take * precendence over "default" commands. */ if (defaultlevel < LEVEL_UI) { defaultlevel = LEVEL_DEFAULT; refstr_put(default_cmd); default_cmd = refstrdup(globaldefault); } } else if (looking_at(p, "ui")) { has_ui = 1; defaultlevel = LEVEL_UI; refstr_put(default_cmd); default_cmd = refstrdup(skipspace(p + 2)); } /* * subset 1: pc_opencmd * display/font/kbdmap are rather similar, open a file then do sth */ else if (looking_at(p, "display")) { const char *filename; char *dst = KernelName; size_t len = FILENAME_MAX - 1; filename = refstrdup(skipspace(p + 7)); while (len-- && not_whitespace(*filename)) *dst++ = *filename++; *dst = '\0'; get_msg_file(KernelName); refstr_put(filename); } else if (looking_at(p, "font")) { const char *filename; char *dst = KernelName; size_t len = FILENAME_MAX - 1; filename = refstrdup(skipspace(p + 4)); while (len-- && not_whitespace(*filename)) *dst++ = *filename++; *dst = '\0'; loadfont(KernelName); refstr_put(filename); } else if (looking_at(p, "kbdmap")) { const char *filename; filename = refstrdup(skipspace(p + 6)); loadkeys(filename); refstr_put(filename); } /* * subset 2: pc_setint16 * set a global flag */ else if (looking_at(p, "implicit")) { allowimplicit = atoi(skipspace(p + 8)); } else if (looking_at(p, "prompt")) { forceprompt = atoi(skipspace(p + 6)); } else if (looking_at(p, "console")) { DisplayCon = atoi(skipspace(p + 7)); } else if (looking_at(p, "allowoptions")) { allowoptions = atoi(skipspace(p + 12)); } else if (looking_at(p, "noescape")) { noescape = atoi(skipspace(p + 8)); } else if (looking_at(p, "nocomplete")) { nocomplete = atoi(skipspace(p + 10)); } else if (looking_at(p, "nohalt")) { NoHalt = atoi(skipspace(p + 8)); } else if (looking_at(p, "onerror")) { refstr_put(m->onerror); m->onerror = refstrdup(skipspace(p + 7)); onerrorlen = strlen(m->onerror); refstr_put(onerror); onerror = refstrdup(m->onerror); } else if (looking_at(p, "pxeretry")) PXERetry = atoi(skipspace(p + 8)); /* serial setting, bps, flow control */ else if (looking_at(p, "serial")) { uint16_t port, flow; uint32_t baud; p = skipspace(p + 6); port = atoi(p); while (isalnum(*p)) p++; p = skipspace(p); /* Default to no flow control */ FlowOutput = 0; FlowInput = 0; baud = DEFAULT_BAUD; if (isalnum(*p)) { uint8_t ignore; /* setup baud */ baud = atoi(p); while (isalnum(*p)) p++; p = skipspace(p); ignore = 0; flow = 0; if (isalnum(*p)) { /* flow control */ flow = atoi(p); ignore = ((flow & 0x0F00) >> 4); } FlowIgnore = ignore; flow = ((flow & 0xff) << 8) | (flow & 0xff); flow &= 0xF00B; FlowOutput = (flow & 0xff); FlowInput = ((flow & 0xff00) >> 8); } /* * Parse baud */ if (baud < 75) { /* < 75 baud == bogus */ SerialPort = 0; continue; } baud = BAUD_DIVISOR / baud; baud &= 0xffff; BaudDivisor = baud; port = get_serial_port(port); SerialPort = port; /* * Begin code to actually set up the serial port */ sirq_cleanup_nowipe(); outb(0x83, port + 3); /* Enable DLAB */ io_delay(); outb((baud & 0xff), port); /* write divisor to LS */ io_delay(); outb(((baud & 0xff00) >> 8), port + 1); /* write to MS */ io_delay(); outb(0x03, port + 3); /* Disable DLAB */ io_delay(); /* * Read back LCR (detect missing hw). If nothing here * we'll read 00 or FF. */ if (inb(port + 3) != 0x03) { /* Assume serial port busted */ SerialPort = 0; continue; } outb(0x01, port + 2); /* Enable FIFOs if present */ io_delay(); /* Disable FIFO if unusable */ if (inb(port + 2) < 0x0C0) { outb(0, port + 2); io_delay(); } /* Assert bits in MCR */ outb(FlowOutput, port + 4); io_delay(); /* Enable interrupts if requested */ if (FlowOutput & 0x8) sirq_install(); /* Show some life */ if (SerialNotice != 0) { SerialNotice = 0; write_serial_str(syslinux_banner); write_serial_str(copyright_str); } } else if (looking_at(p, "say")) { printf("%s\n", p+4); } else if (looking_at(p, "path")) { if (parse_path(skipspace(p + 4))) printf("Failed to parse PATH\n"); } else if (looking_at(p, "sendcookies")) { const union syslinux_derivative_info *sdi; p += strlen("sendcookies"); sdi = syslinux_derivative_info(); if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) { SendCookies = strtoul(skipspace(p), NULL, 10); http_bake_cookies(); } } } } static int parse_main_config(const char *filename) { const char *mode = "r"; FILE *f; int fd; if (!filename) fd = open_config(); else fd = open(filename, O_RDONLY); if (fd < 0) return fd; if (config_cwd[0]) { if (chdir(config_cwd) < 0) printf("Failed to chdir to %s\n", config_cwd); config_cwd[0] = '\0'; } f = fdopen(fd, mode); parse_config_file(f); /* * Update ConfigName so that syslinux_config_file() returns * the filename we just opened. filesystem-specific * open_config() implementations are expected to update * ConfigName themselves. */ if (filename) strcpy(ConfigName, filename); return 0; } static void resolve_gotos(void) { struct menu_entry *me; struct menu *m; for (me = all_entries; me; me = me->next) { if (me->action == MA_GOTO_UNRES || me->action == MA_EXIT_UNRES) { m = find_menu(me->cmdline); refstr_put(me->cmdline); me->cmdline = NULL; if (m) { me->submenu = m; me->action--; /* Drop the _UNRES */ } else { me->action = MA_DISABLED; } } } } void parse_configs(char **argv) { const char *filename; struct menu *m; struct menu_entry *me; dprintf("enter"); empty_string = refstrdup(""); /* feng: reset current menu_list and entry list */ menu_list = NULL; all_entries = NULL; /* Initialize defaults for the root and hidden menus */ hide_menu = new_menu(NULL, NULL, refstrdup(".hidden")); root_menu = new_menu(NULL, NULL, refstrdup(".top")); start_menu = root_menu; /* Other initialization */ memset(&ld, 0, sizeof(struct labeldata)); /* Actually process the files */ current_menu = root_menu; if (!argv || !*argv) { if (parse_main_config(NULL) < 0) { printf("WARNING: No configuration file found\n"); return; } } else { while ((filename = *argv++)) { dprintf("Parsing config: %s", filename); parse_main_config(filename); } } /* On final EOF process the last label statement */ record(current_menu, &ld, append); /* Common postprocessing */ resolve_gotos(); /* Handle global default */ //if (has_ui && globaldefault) { if (globaldefault) { dprintf("gloabldefault = %s", globaldefault); me = find_label(globaldefault); if (me && me->menu != hide_menu) { me->menu->defentry = me->entry; start_menu = me->menu; default_menu = me->menu; } } /* If "menu save" is active, let the ADV override the global default */ if (menusave) { size_t len; const char *lbl = syslinux_getadv(ADV_MENUSAVE, &len); char *lstr; if (lbl && len) { lstr = refstr_alloc(len); memcpy(lstr, lbl, len); /* refstr_alloc() adds the final null */ me = find_label(lstr); if (me && me->menu != hide_menu) { me->menu->defentry = me->entry; start_menu = me->menu; } refstr_put(lstr); } } /* Final per-menu initialization, with all labels known */ for (m = menu_list; m; m = m->next) { m->curentry = m->defentry; /* All menus start at their defaults */ if (m->ontimeout) m->ontimeout = unlabel(m->ontimeout); if (m->onerror) m->onerror = unlabel(m->onerror); } }