You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1221 lines
30 KiB

/* ----------------------------------------------------------------------- *
*
* Copyright 2004-2009 H. Peter Anvin - All Rights Reserved
* Copyright 2009 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.
*
* ----------------------------------------------------------------------- */
/* ----------------------------------------------------------------------- *
*
* 02.06.2009 Calculate Pack http://www.calculate-linux.org
*
* В исходный код calcreadconfig.c внесены следующие изменения:
* - если параметр .conf или .cfg, то это конфигурационный файл меню
* в остальных случаях параметры аккумулируются в дополнения для передачи
* далее
* - если параметре, значения которого перечислены через запятую возможна
* замена одного из значений (например переходящий параметр test=1,2,3
* test=,,6 заменит в этом параметре последнее занчение => test=1,2,6
* - добавлены функции разбивки и объединения строки
*
* ----------------------------------------------------------------------- */
#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 <unistd.h>
#include "calcmenu.h"
/* 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;
/* 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;
/* Keep track of global default */
static int has_ui = 0; /* DEFAULT only counts if UI is found */
static const char *globaldefault = NULL;
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; })
/* Must match enum kernel_type */
const char * const kernel_types[] = {
"none",
"localboot",
"kernel",
"linux",
"boot",
"bss",
"pxe",
"fdimage",
"comboot",
"com32",
"config",
NULL
};
/*
* 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
static char *
skipspace(char *p)
{
while (*p && my_isspace(*p))
p++;
return p;
}
/* 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;
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;
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;
}
}
}
}
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;
if ( ld->label ) {
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] )
ipp += sprintf(ipp, " %s", 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);
}
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;
}
static 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 *sp = *p;
char *ep = sp;
while (*ep && !my_isspace(*ep))
ep++;
*p = ep;
return refstrndup(sp, ep-sp);
}
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;
static unsigned int ipappend = 0;
static struct labeldata ld;
static int parse_one_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;
}
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;
}
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));
} 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")) ) {
goto do_include;
} 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;
}
}
}
} else if ( looking_at(p, "text") ) {
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 = refstrdup(p);
}
} else if ( looking_at(p, "defaultparam") ) {
strncpy(default_tail_cmdline,skipspace(p+12),100);
} else if ( (ep = looking_at(p, "include")) ) {
do_include:
{
const char *file;
p = skipspace(ep);
file = refdup_word(&p);
p = skipspace(p);
if (*p) {
record(m, &ld, append);
m = current_menu = begin_submenu(p);
parse_one_config(file);
record(m, &ld, append);
m = current_menu = end_submenu();
} else {
parse_one_config(file);
}
refstr_put(file);
}
} else if ( looking_at(p, "menuparam") ) {
strncpy( defaultparam, skipspace(p+9), 50);
char *pp = strchr(defaultparam,' ');
/* Iportant only first work */
*pp = 0;
/* Other is number */
if(pp)
default_menu = atoi(pp+1);
else
default_menu = 0;
} 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;
}
} 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);
record(m, &ld, append);
ld.label = refstrdup(p);
ld.kernel = refstrdup(p);
ld.type = KT_KERNEL;
ld.passwd = NULL;
ld.append = NULL;
ld.initrd = NULL;
ld.menulabel = NULL;
ld.helptext = NULL;
ld.ipappend = ipappend;
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;
}
} else if ( looking_at(p, "timeout") ) {
m->timeout = (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") ) {
m->ontimeout = refstrdup(skipspace(p+9));
} else if ( looking_at(p, "allowoptions") ) {
m->allowedit = !!atoi(skipspace(p+12));
} else if ( looking_at(p, "ipappend") ) {
if (ld.label)
ld.ipappend = atoi(skipspace(p+8));
else
ipappend = atoi(skipspace(p+8));
} else if ( looking_at(p, "default") ) {
refstr_put(globaldefault);
globaldefault = refstrdup(skipspace(p+7));
} else if ( looking_at(p, "ui") ) {
has_ui = 1;
}
}
}
int splitstr(char *s,char ch, char **params,int maxparam) {
char **eparam = params + maxparam;
char **bparam = params;
while(params != eparam) {
char *es = strchr(s,ch);
(*params++) = s;
if(!es)
break;
*es = 0;
s = es + 1;
}
*params = 0;
return params - bparam;
}
void overlay_params(char **pars1,char **pars2) {
int force;
for( force=0;*pars2;pars1++,pars2++ ) {
if( !*pars1 )
force = 1;
if( force || strlen(*pars1) == 0 )
*pars1 = *pars2;
}
if( force )
*pars1 = 0;
}
char *join_params(char **params,char ch,char *s,int maxsize) {
*s = 0;
char splitch[2];
splitch[0]=ch;
splitch[1]=0;
for(;*params;params++) {
strncat(s,*params,maxsize-strlen(s));
strncat(s,splitch,maxsize-strlen(s));
}
*(s+strlen(s)-1)=0;
return s;
}
static int parse_one_config(const char *filename)
{
FILE *f;
if (!strcmp(filename, "~"))
filename = syslinux_config_file();
/* if param is config file (.cfg file) */
if(strlen(filename)>4 && !strcmp(filename+strlen(filename)-4,".cfg") ||
strlen(filename)>5 && !strcmp(filename+strlen(filename)-5,".conf") ||
strlen(filename)>12 && !strncmp(filename,"pxelinux.cfg",12) ) {
f = fopen(filename, "r");
if( !f )
if( startconfig[0] ) {
f = fopen(startconfig, "r");
if( !f )
return 1;
}
else
return 1;
else if( !startconfig[0] ) {
strncpy(startconfig,filename,255);
}
}
/* else it is additional param */
else {
char *newparam = filename;
char bufParam[512]= "\0";
char *i_eq = 0;
/* finding '=' in newparam */
if( (i_eq = strchr(newparam,'='))) {
/* copy from newparam part before '=' */
memcpy(bufParam, newparam, i_eq-newparam );
bufParam[i_eq-newparam] = '\0';
char *p;
/* find newparam in tail_cmdline
if param is found in tail_cmdline */
while((p = strstr(tail_cmdline,bufParam))) {
/* if it is param - not inner part str */
if( p == tail_cmdline || *(p-1) == ' ' ) {
/* p pointer to value of parameter */
p += strlen(bufParam)+1;
char *np = newparam + strlen(bufParam) + 1;
char *params1[256]; char str1[512];
char *params2[256]; char str2[512];
char out_str[256];
/* copy value of tail_cmdline in str1 */
strcpy(str1,p);
/* Truncates the end of the string containing the rest
of the parameters */
if(strchr(str1,' '))
*strchr(str1,' ')=0;
int lstr1 = strlen(str1);
/* spliting str1 to params */
splitstr(str1,',',params1,255);
/* copy value of newparam and split it */
strcpy(str2,np); splitstr(str2,',',params2,255);
/* overlaying params */
overlay_params(params1,params2);
/* join result of overlaying */
join_params(params1,',',out_str,255);
/* insert result in tail_cmdline */
memmove(p+strlen(out_str), p+lstr1, strlen(p)+1);
memmove(p,out_str,strlen(out_str));
return 0;
}
}
}
/* param is new - append it to tail_cmdline */
strcat(tail_cmdline," ");
strcat(tail_cmdline,filename);
return 0;
}
parse_config_file(f);
fclose(f);
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;
default_tail_cmdline[0] = 0;
tail_cmdline[0] = 0;
empty_string = refstrdup("");
/* 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 ) {
parse_one_config("~");
} else {
while ( (filename = *argv++) )
parse_one_config(filename);
}
if(strlen(default_tail_cmdline))
{
char *p = default_tail_cmdline;
while(strchr(p,' '))
{
*strchr(p,' ')= NULL;
if(strlen(p))
parse_one_config(p);
p += strlen(p)+1;
}
if(strlen(p))
{
parse_one_config(p);
}
}
/* On final EOF process the last label statement */
record(current_menu, &ld, append);
/* Common postprocessing */
resolve_gotos();
/* Handle global default */
if (has_ui && globaldefault) {
me = find_label(globaldefault);
if (me && me->menu != hide_menu) {
me->menu->defentry = me->entry;
start_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);
}
}