diff options
Diffstat (limited to 'com32/elflink/ldlinux/readconfig.c')
-rw-r--r-- | com32/elflink/ldlinux/readconfig.c | 1572 |
1 files changed, 1572 insertions, 0 deletions
diff --git a/com32/elflink/ldlinux/readconfig.c b/com32/elflink/ldlinux/readconfig.c new file mode 100644 index 00000000..22efbe43 --- /dev/null +++ b/com32/elflink/ldlinux/readconfig.c @@ -0,0 +1,1572 @@ +/* ----------------------------------------------------------------------- * + * + * 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(char *); +extern void loadkeys(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); + +bail: + 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); + } +} |