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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/* ----------------------------------------------------------------------- *
*
* 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);
}
}