merge with mainline

This commit is contained in:
BVK Chaitanya 2010-08-25 14:45:56 +05:30
commit a9b85993ee
554 changed files with 6765 additions and 5034 deletions

150
grub-core/script/argv.c Normal file
View file

@ -0,0 +1,150 @@
/* argv.c - methods for constructing argument vector */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2010 Free Software Foundation, Inc.
*
* GRUB 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, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/mm.h>
#include <grub/script_sh.h>
/* Return nearest power of two that is >= v. */
static unsigned
round_up_exp (unsigned v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
if (sizeof (v) > 4)
v |= v >> 32;
v++;
v += (v == 0);
return v;
}
void
grub_script_argv_free (struct grub_script_argv *argv)
{
unsigned i;
if (argv->args)
{
for (i = 0; i < argv->argc; i++)
grub_free (argv->args[i]);
grub_free (argv->args);
}
argv->argc = 0;
argv->args = 0;
}
/* Make argv from argc, args pair. */
int
grub_script_argv_make (struct grub_script_argv *argv, int argc, char **args)
{
int i;
struct grub_script_argv r = { 0, 0};
for (i = 0; i < argc; i++)
if (grub_script_argv_next (&r) || grub_script_argv_append (&r, args[i]))
{
grub_script_argv_free (&r);
return 1;
}
*argv = r;
return 0;
}
/* Prepare for next argc. */
int
grub_script_argv_next (struct grub_script_argv *argv)
{
char **p = argv->args;
if (argv->args && argv->args[argv->argc - 1] == 0)
return 0;
p = grub_realloc (p, round_up_exp ((argv->argc + 2) * sizeof (char *)));
if (! p)
return 1;
argv->argc++;
argv->args = p;
if (argv->argc == 1)
argv->args[0] = 0;
argv->args[argv->argc] = 0;
return 0;
}
/* Append `s' to the last argument. */
int
grub_script_argv_append (struct grub_script_argv *argv, const char *s)
{
int a, b;
char *p = argv->args[argv->argc - 1];
if (! s)
return 0;
a = p ? grub_strlen (p) : 0;
b = grub_strlen (s);
p = grub_realloc (p, round_up_exp ((a + b + 1) * sizeof (char)));
if (! p)
return 1;
grub_strcpy (p + a, s);
argv->args[argv->argc - 1] = p;
return 0;
}
/* Split `s' and append words as multiple arguments. */
int
grub_script_argv_split_append (struct grub_script_argv *argv, char *s)
{
char ch;
char *p;
int errors = 0;
if (! s)
return 0;
while (! errors && *s)
{
p = s;
while (*s && ! grub_isspace (*s))
s++;
ch = *s;
*s = '\0';
errors += grub_script_argv_append (argv, p);
*s = ch;
while (*s && grub_isspace (*s))
s++;
if (*s)
errors += grub_script_argv_next (argv);
}
return errors;
}

578
grub-core/script/execute.c Normal file
View file

@ -0,0 +1,578 @@
/* execute.c -- Execute a GRUB script. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB 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, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/env.h>
#include <grub/script_sh.h>
#include <grub/command.h>
#include <grub/menu.h>
#include <grub/lib/arg.h>
#include <grub/normal.h>
/* Max digits for a char is 3 (0xFF is 255), similarly for an int it
is sizeof (int) * 3, and one extra for a possible -ve sign. */
#define ERRNO_DIGITS_MAX (sizeof (int) * 3 + 1)
static unsigned long is_continue;
static unsigned long active_loops;
static unsigned long active_breaks;
#define GRUB_SCRIPT_SCOPE_MALLOCED 1
#define GRUB_SCRIPT_SCOPE_ARGS_MALLOCED 2
/* Scope for grub script functions. */
struct grub_script_scope
{
unsigned flags;
unsigned shifts;
struct grub_script_argv argv;
};
static struct grub_script_scope *scope = 0;
static void
replace_scope (struct grub_script_scope *new_scope)
{
if (scope)
{
scope->argv.argc += scope->shifts;
scope->argv.args -= scope->shifts;
if (scope->flags & GRUB_SCRIPT_SCOPE_ARGS_MALLOCED)
grub_script_argv_free (&scope->argv);
if (scope->flags & GRUB_SCRIPT_SCOPE_MALLOCED)
grub_free (scope);
}
scope = new_scope;
}
grub_err_t
grub_script_break (grub_command_t cmd, int argc, char *argv[])
{
char *p = 0;
unsigned long count;
if (argc == 0)
count = 1;
else if ((argc > 1) || (count = grub_strtoul (argv[0], &p, 10)) == 0 ||
(*p != '\0'))
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad break");
is_continue = grub_strcmp (cmd->name, "break") ? 1 : 0;
active_breaks = grub_min (active_loops, count);
return GRUB_ERR_NONE;
}
grub_err_t
grub_script_shift (grub_command_t cmd __attribute__((unused)),
int argc, char *argv[])
{
char *p = 0;
unsigned long n = 0;
if (! scope)
return GRUB_ERR_NONE;
if (argc == 0)
n = 1;
else if (argc > 1)
return GRUB_ERR_BAD_ARGUMENT;
else
{
n = grub_strtoul (argv[0], &p, 10);
if (*p != '\0')
return GRUB_ERR_BAD_ARGUMENT;
}
if (n > scope->argv.argc)
return GRUB_ERR_BAD_ARGUMENT;
scope->shifts += n;
scope->argv.argc -= n;
scope->argv.args += n;
return GRUB_ERR_NONE;
}
grub_err_t
grub_script_setparams (grub_command_t cmd __attribute__((unused)),
int argc, char **args)
{
struct grub_script_scope *new_scope;
struct grub_script_argv argv = { 0, 0 };
if (! scope)
return GRUB_ERR_INVALID_COMMAND;
new_scope = grub_malloc (sizeof (*new_scope));
if (! new_scope)
return grub_errno;
if (grub_script_argv_make (&argv, argc, args))
{
grub_free (new_scope);
return grub_errno;
}
new_scope->shifts = 0;
new_scope->argv = argv;
new_scope->flags = GRUB_SCRIPT_SCOPE_MALLOCED |
GRUB_SCRIPT_SCOPE_ARGS_MALLOCED;
replace_scope (new_scope);
return GRUB_ERR_NONE;
}
static int
grub_env_special (const char *name)
{
if (grub_isdigit (name[0]) ||
grub_strcmp (name, "#") == 0 ||
grub_strcmp (name, "*") == 0 ||
grub_strcmp (name, "@") == 0)
return 1;
return 0;
}
static char **
grub_script_env_get (const char *name, grub_script_arg_type_t type)
{
unsigned i;
struct grub_script_argv result = { 0, 0 };
if (grub_script_argv_next (&result))
goto fail;
if (! grub_env_special (name))
{
char *v = grub_env_get (name);
if (v && v[0])
{
if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
{
if (grub_script_argv_split_append (&result, v))
goto fail;
}
else
if (grub_script_argv_append (&result, v))
goto fail;
}
}
else if (! scope)
{
if (grub_script_argv_append (&result, 0))
goto fail;
}
else if (grub_strcmp (name, "#") == 0)
{
char buffer[ERRNO_DIGITS_MAX + 1];
grub_snprintf (buffer, sizeof (buffer), "%u", scope->argv.argc);
if (grub_script_argv_append (&result, buffer))
goto fail;
}
else if (grub_strcmp (name, "*") == 0)
{
for (i = 0; i < scope->argv.argc; i++)
if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
{
if (i != 0 && grub_script_argv_next (&result))
goto fail;
if (grub_script_argv_split_append (&result, scope->argv.args[i]))
goto fail;
}
else
{
if (i != 0 && grub_script_argv_append (&result, " "))
goto fail;
if (grub_script_argv_append (&result, scope->argv.args[i]))
goto fail;
}
}
else if (grub_strcmp (name, "@") == 0)
{
for (i = 0; i < scope->argv.argc; i++)
{
if (i != 0 && grub_script_argv_next (&result))
goto fail;
if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
{
if (grub_script_argv_split_append (&result, scope->argv.args[i]))
goto fail;
}
else
if (grub_script_argv_append (&result, scope->argv.args[i]))
goto fail;
}
}
else
{
unsigned long num = grub_strtoul (name, 0, 10);
if (num == 0)
; /* XXX no file name, for now. */
else if (num <= scope->argv.argc)
{
if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
{
if (grub_script_argv_split_append (&result,
scope->argv.args[num - 1]))
goto fail;
}
else
if (grub_script_argv_append (&result, scope->argv.args[num - 1]))
goto fail;
}
}
return result.args;
fail:
grub_script_argv_free (&result);
return 0;
}
static grub_err_t
grub_script_env_set (const char *name, const char *val)
{
if (grub_env_special (name))
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad variable name");
return grub_env_set (name, val);
}
/* Expand arguments in ARGLIST into multiple arguments. */
static int
grub_script_arglist_to_argv (struct grub_script_arglist *arglist,
struct grub_script_argv *argv)
{
int i;
char **values = 0;
struct grub_script_arg *arg = 0;
struct grub_script_argv result = { 0, 0 };
for (; arglist && arglist->arg; arglist = arglist->next)
{
if (grub_script_argv_next (&result))
goto fail;
arg = arglist->arg;
while (arg)
{
switch (arg->type)
{
case GRUB_SCRIPT_ARG_TYPE_VAR:
case GRUB_SCRIPT_ARG_TYPE_DQVAR:
values = grub_script_env_get (arg->str, arg->type);
for (i = 0; values && values[i]; i++)
{
if (i != 0 && grub_script_argv_next (&result))
goto fail;
if (grub_script_argv_append (&result, values[i]))
goto fail;
}
grub_free (values);
break;
case GRUB_SCRIPT_ARG_TYPE_TEXT:
if (grub_strlen (arg->str) &&
grub_script_argv_append (&result, arg->str))
goto fail;
break;
case GRUB_SCRIPT_ARG_TYPE_DQSTR:
case GRUB_SCRIPT_ARG_TYPE_SQSTR:
if (grub_script_argv_append (&result, arg->str))
goto fail;
break;
}
arg = arg->next;
}
}
if (! result.args[result.argc - 1])
result.argc--;
*argv = result;
return 0;
fail:
grub_script_argv_free (&result);
return 1;
}
static grub_err_t
grub_script_execute_cmd (struct grub_script_cmd *cmd)
{
int ret;
char errnobuf[ERRNO_DIGITS_MAX + 1];
if (cmd == 0)
return 0;
ret = cmd->exec (cmd);
grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret);
grub_env_set ("?", errnobuf);
return ret;
}
/* Execute a function call. */
grub_err_t
grub_script_function_call (grub_script_function_t func, int argc, char **args)
{
grub_err_t ret = 0;
unsigned long loops = active_loops;
struct grub_script_scope *old_scope;
struct grub_script_scope new_scope;
active_loops = 0;
new_scope.flags = 0;
new_scope.shifts = 0;
new_scope.argv.argc = argc;
new_scope.argv.args = args;
old_scope = scope;
scope = &new_scope;
ret = grub_script_execute (func->func);
active_loops = loops;
replace_scope (old_scope); /* free any scopes by setparams */
return ret;
}
/* Execute a single command line. */
grub_err_t
grub_script_execute_cmdline (struct grub_script_cmd *cmd)
{
struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd;
grub_command_t grubcmd;
grub_err_t ret = 0;
grub_script_function_t func = 0;
char errnobuf[18];
char *cmdname;
struct grub_script_argv argv = { 0, 0 };
/* Lookup the command. */
if (grub_script_arglist_to_argv (cmdline->arglist, &argv) || ! argv.args[0])
return grub_errno;
cmdname = argv.args[0];
grubcmd = grub_command_find (cmdname);
if (! grubcmd)
{
grub_errno = GRUB_ERR_NONE;
/* It's not a GRUB command, try all functions. */
func = grub_script_function_find (cmdname);
if (! func)
{
/* As a last resort, try if it is an assignment. */
char *assign = grub_strdup (cmdname);
char *eq = grub_strchr (assign, '=');
if (eq)
{
/* This was set because the command was not found. */
grub_errno = GRUB_ERR_NONE;
/* Create two strings and set the variable. */
*eq = '\0';
eq++;
grub_script_env_set (assign, eq);
}
grub_free (assign);
grub_snprintf (errnobuf, sizeof (errnobuf), "%d", grub_errno);
grub_script_env_set ("?", errnobuf);
grub_script_argv_free (&argv);
grub_print_error ();
return 0;
}
}
/* Execute the GRUB command or function. */
if (grubcmd)
ret = (grubcmd->func) (grubcmd, argv.argc - 1, argv.args + 1);
else
ret = grub_script_function_call (func, argv.argc - 1, argv.args + 1);
/* Free arguments. */
grub_script_argv_free (&argv);
if (grub_errno == GRUB_ERR_TEST_FAILURE)
grub_errno = GRUB_ERR_NONE;
grub_print_error ();
grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret);
grub_env_set ("?", errnobuf);
return ret;
}
/* Execute a block of one or more commands. */
grub_err_t
grub_script_execute_cmdlist (struct grub_script_cmd *list)
{
int ret = 0;
struct grub_script_cmd *cmd;
/* Loop over every command and execute it. */
for (cmd = list->next; cmd && ! active_breaks; cmd = cmd->next)
ret = grub_script_execute_cmd (cmd);
return ret;
}
/* Execute an if statement. */
grub_err_t
grub_script_execute_cmdif (struct grub_script_cmd *cmd)
{
struct grub_script_cmdif *cmdif = (struct grub_script_cmdif *) cmd;
char *result;
/* Check if the commands results in a true or a false. The value is
read from the env variable `?'. */
grub_script_execute_cmd (cmdif->exec_to_evaluate);
result = grub_env_get ("?");
grub_errno = GRUB_ERR_NONE;
/* Execute the `if' or the `else' part depending on the value of
`?'. */
if (result && ! grub_strcmp (result, "0"))
return grub_script_execute_cmd (cmdif->exec_on_true);
else
return grub_script_execute_cmd (cmdif->exec_on_false);
}
/* Execute a for statement. */
grub_err_t
grub_script_execute_cmdfor (struct grub_script_cmd *cmd)
{
unsigned i;
grub_err_t result;
struct grub_script_argv argv = { 0, 0 };
struct grub_script_cmdfor *cmdfor = (struct grub_script_cmdfor *) cmd;
if (grub_script_arglist_to_argv (cmdfor->words, &argv))
return grub_errno;
active_loops++;
result = 0;
for (i = 0; i < argv.argc; i++)
{
if (is_continue && active_breaks == 1)
active_breaks = 0;
if (! active_breaks)
{
grub_script_env_set (cmdfor->name->str, argv.args[i]);
result = grub_script_execute_cmd (cmdfor->list);
}
}
if (active_breaks)
active_breaks--;
active_loops--;
grub_script_argv_free (&argv);
return result;
}
/* Execute a "while" or "until" command. */
grub_err_t
grub_script_execute_cmdwhile (struct grub_script_cmd *cmd)
{
int cond;
int result;
struct grub_script_cmdwhile *cmdwhile = (struct grub_script_cmdwhile *) cmd;
active_loops++;
result = 0;
do {
cond = grub_script_execute_cmd (cmdwhile->cond);
if (cmdwhile->until ? !cond : cond)
break;
result = grub_script_execute_cmd (cmdwhile->list);
if (active_breaks == 1 && is_continue)
active_breaks = 0;
if (active_breaks)
break;
} while (1); /* XXX Put a check for ^C here */
if (active_breaks)
active_breaks--;
active_loops--;
return result;
}
/* Execute the menu entry generate statement. */
grub_err_t
grub_script_execute_menuentry (struct grub_script_cmd *cmd)
{
struct grub_script_cmd_menuentry *cmd_menuentry;
struct grub_script_argv argv = { 0, 0 };
cmd_menuentry = (struct grub_script_cmd_menuentry *) cmd;
if (cmd_menuentry->arglist)
{
if (grub_script_arglist_to_argv (cmd_menuentry->arglist, &argv))
return grub_errno;
}
grub_normal_add_menu_entry (argv.argc, (const char **) argv.args,
cmd_menuentry->sourcecode);
grub_script_argv_free (&argv);
return grub_errno;
}
/* Execute any GRUB pre-parsed command or script. */
grub_err_t
grub_script_execute (struct grub_script *script)
{
if (script == 0)
return 0;
return grub_script_execute_cmd (script->cmd);
}

105
grub-core/script/function.c Normal file
View file

@ -0,0 +1,105 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2007,2009,2010 Free Software Foundation, Inc.
*
* GRUB 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, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/misc.h>
#include <grub/script_sh.h>
#include <grub/parser.h>
#include <grub/mm.h>
grub_script_function_t grub_script_function_list;
grub_script_function_t
grub_script_function_create (struct grub_script_arg *functionname_arg,
struct grub_script *cmd)
{
grub_script_function_t func;
grub_script_function_t *p;
func = (grub_script_function_t) grub_malloc (sizeof (*func));
if (! func)
return 0;
func->name = grub_strdup (functionname_arg->str);
if (! func->name)
{
grub_free (func);
return 0;
}
func->func = cmd;
/* Keep the list sorted for simplicity. */
p = &grub_script_function_list;
while (*p)
{
if (grub_strcmp ((*p)->name, func->name) >= 0)
break;
p = &((*p)->next);
}
/* If the function already exists, overwrite the old function. */
if (*p && grub_strcmp ((*p)->name, func->name) == 0)
{
grub_script_function_t q;
q = *p;
grub_script_free (q->func);
q->func = cmd;
grub_free (func);
func = q;
}
else
{
func->next = *p;
*p = func;
}
return func;
}
void
grub_script_function_remove (const char *name)
{
grub_script_function_t *p, q;
for (p = &grub_script_function_list, q = *p; q; p = &(q->next), q = q->next)
if (grub_strcmp (name, q->name) == 0)
{
*p = q->next;
grub_free (q->name);
grub_script_free (q->func);
grub_free (q);
break;
}
}
grub_script_function_t
grub_script_function_find (char *functionname)
{
grub_script_function_t func;
for (func = grub_script_function_list; func; func = func->next)
if (grub_strcmp (functionname, func->name) == 0)
break;
if (! func)
grub_error (GRUB_ERR_UNKNOWN_COMMAND, "unknown command `%.20s'", functionname);
return func;
}

346
grub-core/script/lexer.c Normal file
View file

@ -0,0 +1,346 @@
/* lexer.c - The scripting lexer. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB 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, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/parser.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/script_sh.h>
#include "grub_script.tab.h"
#include "grub_script.yy.h"
void
grub_script_lexer_ref (struct grub_lexer_param *state)
{
state->refs++;
}
void
grub_script_lexer_deref (struct grub_lexer_param *state)
{
state->refs--;
}
/* Start recording all characters passing through the lexer. */
void
grub_script_lexer_record_start (struct grub_parser_param *parser)
{
struct grub_lexer_param *lexer = parser->lexerstate;
lexer->record = 1;
lexer->recordpos = 0;
if (lexer->recording) /* reuse last record */
return;
lexer->recordlen = GRUB_LEXER_INITIAL_RECORD_SIZE;
lexer->recording = grub_malloc (lexer->recordlen);
if (!lexer->recording)
{
grub_script_yyerror (parser, 0);
lexer->record = 0;
lexer->recordlen = 0;
}
}
char *
grub_script_lexer_record_stop (struct grub_parser_param *parser)
{
char *ptr;
char *result;
struct grub_lexer_param *lexer = parser->lexerstate;
auto char *compact (char *start, char *end);
char *compact (char *start, char *end)
{
/* Delete '{' and '}' characters and whitespaces. */
while (*start && grub_isspace (*start)) start++;
if (*start == '{') start++;
while (*start && grub_isspace (*start)) start++;
while (*end && grub_isspace (*end)) end--;
if (*end == '}') end--;
while (*end && grub_isspace (*end)) end--;
end[1] = '\0';
return start;
}
if (!lexer->record || !lexer->recording)
return 0;
/* XXX This is not necessary in BASH. */
ptr = compact (lexer->recording, lexer->recording + lexer->recordpos - 1);
lexer->record = 0;
lexer->recordpos = 0;
/* This memory would be freed by, grub_script_free. */
result = grub_script_malloc (parser, grub_strlen (ptr) + 1);
if (result)
grub_strcpy (result, ptr);
return result;
}
#define MAX(a,b) ((a) < (b) ? (b) : (a))
/* Record STR if input recording is enabled. */
void
grub_script_lexer_record (struct grub_parser_param *parser, char *str)
{
int len;
char *old;
struct grub_lexer_param *lexer = parser->lexerstate;
if (!lexer->record)
return;
len = grub_strlen (str);
if (lexer->recordpos + len + 1 > lexer->recordlen)
{
old = lexer->recording;
lexer->recordlen = MAX (len, lexer->recordlen) * 2;
lexer->recording = grub_realloc (lexer->recording, lexer->recordlen);
if (!lexer->recording)
{
grub_free (old);
lexer->record = 0;
lexer->recordpos = 0;
lexer->recordlen /= 2;
grub_script_yyerror (parser, 0);
return;
}
}
grub_strcpy (lexer->recording + lexer->recordpos, str);
lexer->recordpos += len;
}
/* Append '\n' to SRC, before '\0' */
static char *
append_newline (const char *src)
{
char *line;
grub_size_t len;
len = grub_strlen (src);
line = grub_malloc (len + 2);
if (!line)
return 0;
grub_strcpy (line, src);
line[len] = '\n';
line[len + 1] = '\0';
return line;
}
/* Read next line of input if necessary, and set yyscanner buffers. */
int
grub_script_lexer_yywrap (struct grub_parser_param *parserstate)
{
int len;
char *line;
char *line2;
YY_BUFFER_STATE buffer;
struct grub_lexer_param *lexerstate = parserstate->lexerstate;
if (!lexerstate->refs)
return 0;
if (!lexerstate->getline)
{
grub_script_yyerror (parserstate, "unexpected end of file");
return 0;
}
line = 0;
buffer = 0;
lexerstate->getline (&line, 1);
if (!line)
{
grub_script_yyerror (parserstate, 0); /* XXX this could be for ^C case? */
return 0;
}
len = grub_strlen (line);
if (line[len - 1] == '\n')
{
buffer = yy_scan_string (line, lexerstate->yyscanner);
}
else
{
line2 = append_newline (line);
if (line2)
{
buffer = yy_scan_string (line2, lexerstate->yyscanner);
grub_free (line2);
}
}
grub_free (line);
if (!buffer)
{
grub_script_yyerror (parserstate, 0);
return 0;
}
return 1;
}
struct grub_lexer_param *
grub_script_lexer_init (struct grub_parser_param *parser, char *script,
grub_reader_getline_t getline)
{
int len;
char *script2;
YY_BUFFER_STATE buffer;
struct grub_lexer_param *lexerstate;
lexerstate = grub_zalloc (sizeof (*lexerstate));
if (!lexerstate)
return 0;
lexerstate->size = GRUB_LEXER_INITIAL_TEXT_SIZE;
lexerstate->text = grub_malloc (lexerstate->size);
if (!lexerstate->text)
{
grub_free (lexerstate);
return 0;
}
lexerstate->getline = getline; /* rest are all zeros already */
if (yylex_init (&lexerstate->yyscanner))
{
grub_free (lexerstate->text);
grub_free (lexerstate);
return 0;
}
buffer = 0;
script = script ? : "\n";
len = grub_strlen (script);
if (script[len - 1] == '\n')
{
buffer = yy_scan_string (script, lexerstate->yyscanner);
}
else
{
script2 = append_newline (script);
if (script2)
{
buffer = yy_scan_string (script2, lexerstate->yyscanner);
grub_free (script2);
}
}
if (!buffer)
{
yylex_destroy (lexerstate->yyscanner);
grub_free (lexerstate->yyscanner);
grub_free (lexerstate->text);
grub_free (lexerstate);
return 0;
}
yyset_extra (parser, lexerstate->yyscanner);
return lexerstate;
}
void
grub_script_lexer_fini (struct grub_lexer_param *lexerstate)
{
if (!lexerstate)
return;
yylex_destroy (lexerstate->yyscanner);
grub_free (lexerstate->recording);
grub_free (lexerstate->text);
grub_free (lexerstate);
}
int
grub_script_yylex (union YYSTYPE *value,
struct grub_parser_param *parserstate)
{
char *str;
int token;
grub_script_arg_type_t type;
struct grub_lexer_param *lexerstate = parserstate->lexerstate;
value->arg = 0;
if (parserstate->err)
return GRUB_PARSER_TOKEN_BAD;
if (lexerstate->eof)
return GRUB_PARSER_TOKEN_EOF;
/*
* Words with environment variables, like foo${bar}baz needs
* multiple tokens to be merged into a single grub_script_arg. We
* use two variables to achieve this: lexerstate->merge_start and
* lexerstate->merge_end
*/
lexerstate->merge_start = 0;
lexerstate->merge_end = 0;
do
{
/* Empty lexerstate->text. */
lexerstate->used = 1;
lexerstate->text[0] = '\0';
token = yylex (value, lexerstate->yyscanner);
if (token == GRUB_PARSER_TOKEN_BAD)
break;
/* Merging feature uses lexerstate->text instead of yytext. */
if (lexerstate->merge_start)
{
str = lexerstate->text;
type = lexerstate->type;
}
else
{
str = yyget_text (lexerstate->yyscanner);
type = GRUB_SCRIPT_ARG_TYPE_TEXT;
}
grub_dprintf("lexer", "token %u text [%s]\n", token, str);
value->arg = grub_script_arg_add (parserstate, value->arg, type, str);
}
while (lexerstate->merge_start && !lexerstate->merge_end);
if (!value->arg || parserstate->err)
return GRUB_PARSER_TOKEN_BAD;
return token;
}
void
grub_script_yyerror (struct grub_parser_param *state, char const *err)
{
if (err)
grub_error (GRUB_ERR_INVALID_COMMAND, err);
grub_print_error ();
state->err++;
}

81
grub-core/script/main.c Normal file
View file

@ -0,0 +1,81 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* GRUB 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, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/dl.h>
#include <grub/i18n.h>
#include <grub/parser.h>
#include <grub/script_sh.h>
grub_err_t
grub_normal_parse_line (char *line, grub_reader_getline_t getline)
{
struct grub_script *parsed_script;
/* Parse the script. */
parsed_script = grub_script_parse (line, getline);
if (parsed_script)
{
/* Execute the command(s). */
grub_script_execute (parsed_script);
/* The parsed script was executed, throw it away. */
grub_script_free (parsed_script);
}
return grub_errno;
}
static grub_command_t cmd_break;
static grub_command_t cmd_continue;
static grub_command_t cmd_shift;
static grub_command_t cmd_setparams;
void
grub_script_init (void)
{
cmd_break = grub_register_command ("break", grub_script_break,
N_("[n]"), N_("Exit from loops"));
cmd_continue = grub_register_command ("continue", grub_script_break,
N_("[n]"), N_("Continue loops"));
cmd_shift = grub_register_command ("shift", grub_script_shift,
N_("[n]"), N_("Shift positional parameters."));
cmd_setparams = grub_register_command ("setparams", grub_script_setparams,
N_("[VALUE]..."),
N_("Set positional parameters."));
}
void
grub_script_fini (void)
{
if (cmd_break)
grub_unregister_command (cmd_break);
cmd_break = 0;
if (cmd_continue)
grub_unregister_command (cmd_continue);
cmd_continue = 0;
if (cmd_shift)
grub_unregister_command (cmd_shift);
cmd_shift = 0;
if (cmd_setparams)
grub_unregister_command (cmd_setparams);
cmd_setparams = 0;
}

284
grub-core/script/parser.y Normal file
View file

@ -0,0 +1,284 @@
/* parser.y - The scripting parser. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB 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, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
%{
#include <grub/script_sh.h>
#include <grub/mm.h>
#define YYFREE grub_free
#define YYMALLOC grub_malloc
#define YYLTYPE_IS_TRIVIAL 0
#define YYENABLE_NLS 0
#include "grub_script.tab.h"
%}
%union {
struct grub_script_cmd *cmd;
struct grub_script_arglist *arglist;
struct grub_script_arg *arg;
char *string;
}
%token GRUB_PARSER_TOKEN_BAD
%token GRUB_PARSER_TOKEN_EOF 0 "end-of-input"
%token GRUB_PARSER_TOKEN_NEWLINE "\n"
%token GRUB_PARSER_TOKEN_AND "&&"
%token GRUB_PARSER_TOKEN_OR "||"
%token GRUB_PARSER_TOKEN_SEMI2 ";;"
%token GRUB_PARSER_TOKEN_PIPE "|"
%token GRUB_PARSER_TOKEN_AMP "&"
%token GRUB_PARSER_TOKEN_SEMI ";"
%token GRUB_PARSER_TOKEN_LBR "{"
%token GRUB_PARSER_TOKEN_RBR "}"
%token GRUB_PARSER_TOKEN_NOT "!"
%token GRUB_PARSER_TOKEN_LSQBR2 "["
%token GRUB_PARSER_TOKEN_RSQBR2 "]"
%token GRUB_PARSER_TOKEN_LT "<"
%token GRUB_PARSER_TOKEN_GT ">"
%token <arg> GRUB_PARSER_TOKEN_CASE "case"
%token <arg> GRUB_PARSER_TOKEN_DO "do"
%token <arg> GRUB_PARSER_TOKEN_DONE "done"
%token <arg> GRUB_PARSER_TOKEN_ELIF "elif"
%token <arg> GRUB_PARSER_TOKEN_ELSE "else"
%token <arg> GRUB_PARSER_TOKEN_ESAC "esac"
%token <arg> GRUB_PARSER_TOKEN_FI "fi"
%token <arg> GRUB_PARSER_TOKEN_FOR "for"
%token <arg> GRUB_PARSER_TOKEN_IF "if"
%token <arg> GRUB_PARSER_TOKEN_IN "in"
%token <arg> GRUB_PARSER_TOKEN_SELECT "select"
%token <arg> GRUB_PARSER_TOKEN_THEN "then"
%token <arg> GRUB_PARSER_TOKEN_UNTIL "until"
%token <arg> GRUB_PARSER_TOKEN_WHILE "while"
%token <arg> GRUB_PARSER_TOKEN_TIME "time"
%token <arg> GRUB_PARSER_TOKEN_FUNCTION "function"
%token <arg> GRUB_PARSER_TOKEN_MENUENTRY "menuentry"
%token <arg> GRUB_PARSER_TOKEN_NAME "name"
%token <arg> GRUB_PARSER_TOKEN_WORD "word"
%type <arglist> word argument arguments0 arguments1
%type <cmd> script_init script
%type <cmd> grubcmd ifclause ifcmd forcmd whilecmd untilcmd
%type <cmd> command commands1 menuentry statement
%pure-parser
%lex-param { struct grub_parser_param *state };
%parse-param { struct grub_parser_param *state };
%start script_init
%%
/* It should be possible to do this in a clean way... */
script_init: { state->err = 0; } script { state->parsed = $2; state->err = 0; }
;
script: newlines0
{
$$ = 0;
}
| script statement delimiter newlines0
{
$$ = grub_script_append_cmd (state, $1, $2);
}
| error
{
$$ = 0;
yyerror (state, "Incorrect command");
yyerrok;
}
;
newlines0: /* Empty */ | newlines1 ;
newlines1: newlines0 "\n" ;
delimiter: ";"
| "\n"
;
delimiters0: /* Empty */ | delimiters1 ;
delimiters1: delimiter
| delimiters1 "\n"
;
word: GRUB_PARSER_TOKEN_NAME { $$ = grub_script_add_arglist (state, 0, $1); }
| GRUB_PARSER_TOKEN_WORD { $$ = grub_script_add_arglist (state, 0, $1); }
;
statement: command { $$ = $1; }
| function { $$ = 0; }
| menuentry { $$ = $1; }
;
argument : "case" { $$ = grub_script_add_arglist (state, 0, $1); }
| "do" { $$ = grub_script_add_arglist (state, 0, $1); }
| "done" { $$ = grub_script_add_arglist (state, 0, $1); }
| "elif" { $$ = grub_script_add_arglist (state, 0, $1); }
| "else" { $$ = grub_script_add_arglist (state, 0, $1); }
| "esac" { $$ = grub_script_add_arglist (state, 0, $1); }
| "fi" { $$ = grub_script_add_arglist (state, 0, $1); }
| "for" { $$ = grub_script_add_arglist (state, 0, $1); }
| "if" { $$ = grub_script_add_arglist (state, 0, $1); }
| "in" { $$ = grub_script_add_arglist (state, 0, $1); }
| "select" { $$ = grub_script_add_arglist (state, 0, $1); }
| "then" { $$ = grub_script_add_arglist (state, 0, $1); }
| "until" { $$ = grub_script_add_arglist (state, 0, $1); }
| "while" { $$ = grub_script_add_arglist (state, 0, $1); }
| "function" { $$ = grub_script_add_arglist (state, 0, $1); }
| "menuentry" { $$ = grub_script_add_arglist (state, 0, $1); }
| word { $$ = $1; }
;
arguments0: /* Empty */ { $$ = 0; }
| arguments1 { $$ = $1; }
;
arguments1: argument arguments0
{
if ($1 && $2)
{
$1->next = $2;
$1->argcount += $2->argcount;
$2->argcount = 0;
}
$$ = $1;
}
;
grubcmd: word arguments0
{
if ($1 && $2) {
$1->next = $2;
$1->argcount += $2->argcount;
$2->argcount = 0;
}
$$ = grub_script_create_cmdline (state, $1);
}
;
/* A single command. */
command: grubcmd { $$ = $1; }
| ifcmd { $$ = $1; }
| forcmd { $$ = $1; }
| whilecmd { $$ = $1; }
| untilcmd { $$ = $1; }
;
/* A list of commands. */
commands1: newlines0 command
{
$$ = grub_script_append_cmd (state, 0, $2);
}
| commands1 delimiters1 command
{
$$ = grub_script_append_cmd (state, $1, $3);
}
;
function: "function" "name"
{
grub_script_lexer_ref (state->lexerstate);
state->func_mem = grub_script_mem_record (state);
}
delimiters0 "{" commands1 delimiters1 "}"
{
struct grub_script *script;
state->func_mem = grub_script_mem_record_stop (state,
state->func_mem);
script = grub_script_create ($6, state->func_mem);
if (script)
grub_script_function_create ($2, script);
grub_script_lexer_deref (state->lexerstate);
}
;
menuentry: "menuentry"
{
grub_script_lexer_ref (state->lexerstate);
}
arguments1
{
grub_script_lexer_record_start (state);
}
delimiters0 "{" commands1 delimiters1 "}"
{
char *menu_entry;
menu_entry = grub_script_lexer_record_stop (state);
grub_script_lexer_deref (state->lexerstate);
$$ = grub_script_create_cmdmenu (state, $3, menu_entry, 0);
}
;
ifcmd: "if"
{
grub_script_lexer_ref (state->lexerstate);
}
ifclause "fi"
{
$$ = $3;
grub_script_lexer_deref (state->lexerstate);
}
;
ifclause: commands1 delimiters1 "then" commands1 delimiters1
{
$$ = grub_script_create_cmdif (state, $1, $4, 0);
}
| commands1 delimiters1 "then" commands1 delimiters1 "else" commands1 delimiters1
{
$$ = grub_script_create_cmdif (state, $1, $4, $7);
}
| commands1 delimiters1 "then" commands1 delimiters1 "elif" ifclause
{
$$ = grub_script_create_cmdif (state, $1, $4, $7);
}
;
forcmd: "for" "name"
{
grub_script_lexer_ref (state->lexerstate);
}
"in" arguments0 delimiters1 "do" commands1 delimiters1 "done"
{
$$ = grub_script_create_cmdfor (state, $2, $5, $8);
grub_script_lexer_deref (state->lexerstate);
}
;
whilecmd: "while"
{
grub_script_lexer_ref (state->lexerstate);
}
commands1 delimiters1 "do" commands1 delimiters1 "done"
{
$$ = grub_script_create_cmdwhile (state, $3, $6, 0);
grub_script_lexer_deref (state->lexerstate);
}
;
untilcmd: "until"
{
grub_script_lexer_ref (state->lexerstate);
}
commands1 delimiters1 "do" commands1 delimiters1 "done"
{
$$ = grub_script_create_cmdwhile (state, $3, $6, 1);
grub_script_lexer_deref (state->lexerstate);
}
;

401
grub-core/script/script.c Normal file
View file

@ -0,0 +1,401 @@
/* script.c -- Functions to create an in memory description of the script. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2009,2010 Free Software Foundation, Inc.
*
* GRUB 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, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/misc.h>
#include <grub/script_sh.h>
#include <grub/parser.h>
#include <grub/mm.h>
/* It is not possible to deallocate the memory when a syntax error was
found. Because of that it is required to keep track of all memory
allocations. The memory is freed in case of an error, or assigned
to the parsed script when parsing was successful.
In case of the normal malloc, some additional bytes are allocated
for this datastructure. All reserved memory is stored in a linked
list so it can be easily freed. The original memory can be found
from &mem. */
struct grub_script_mem
{
struct grub_script_mem *next;
char mem;
};
/* Return malloc'ed memory and keep track of the allocation. */
void *
grub_script_malloc (struct grub_parser_param *state, grub_size_t size)
{
struct grub_script_mem *mem;
mem = (struct grub_script_mem *) grub_malloc (size + sizeof (*mem)
- sizeof (char));
if (!mem)
return 0;
grub_dprintf ("scripting", "malloc %p\n", mem);
mem->next = state->memused;
state->memused = mem;
return (void *) &mem->mem;
}
/* Free all memory described by MEM. */
static void
grub_script_mem_free (struct grub_script_mem *mem)
{
struct grub_script_mem *memfree;
while (mem)
{
memfree = mem->next;
grub_dprintf ("scripting", "free %p\n", mem);
grub_free (mem);
mem = memfree;
}
}
/* Start recording memory usage. Returns the memory that should be
restored when calling stop. */
struct grub_script_mem *
grub_script_mem_record (struct grub_parser_param *state)
{
struct grub_script_mem *mem = state->memused;
state->memused = 0;
return mem;
}
/* Stop recording memory usage. Restore previous recordings using
RESTORE. Return the recorded memory. */
struct grub_script_mem *
grub_script_mem_record_stop (struct grub_parser_param *state,
struct grub_script_mem *restore)
{
struct grub_script_mem *mem = state->memused;
state->memused = restore;
return mem;
}
/* Free the memory reserved for CMD and all of it's children. */
void
grub_script_free (struct grub_script *script)
{
if (!script)
return;
grub_script_mem_free (script->mem);
grub_free (script);
}
/* Extend the argument arg with a variable or string of text. If ARG
is zero a new list is created. */
struct grub_script_arg *
grub_script_arg_add (struct grub_parser_param *state,
struct grub_script_arg *arg, grub_script_arg_type_t type,
char *str)
{
struct grub_script_arg *argpart;
struct grub_script_arg *ll;
int len;
argpart =
(struct grub_script_arg *) grub_script_malloc (state, sizeof (*arg));
if (!argpart)
return arg;
argpart->type = type;
len = grub_strlen (str) + 1;
argpart->str = grub_script_malloc (state, len);
if (!argpart->str)
return arg; /* argpart is freed later, during grub_script_free. */
grub_memcpy (argpart->str, str, len);
argpart->next = 0;
if (!arg)
return argpart;
for (ll = arg; ll->next; ll = ll->next);
ll->next = argpart;
return arg;
}
/* Add the argument ARG to the end of the argument list LIST. If LIST
is zero, a new list will be created. */
struct grub_script_arglist *
grub_script_add_arglist (struct grub_parser_param *state,
struct grub_script_arglist *list,
struct grub_script_arg *arg)
{
struct grub_script_arglist *link;
struct grub_script_arglist *ll;
grub_dprintf ("scripting", "arglist\n");
link =
(struct grub_script_arglist *) grub_script_malloc (state, sizeof (*link));
if (!link)
return list;
link->next = 0;
link->arg = arg;
link->argcount = 0;
if (!list)
{
link->argcount++;
return link;
}
list->argcount++;
/* Look up the last link in the chain. */
for (ll = list; ll->next; ll = ll->next);
ll->next = link;
return list;
}
/* Create a command that describes a single command line. CMDLINE
contains the name of the command that should be executed. ARGLIST
holds all arguments for this command. */
struct grub_script_cmd *
grub_script_create_cmdline (struct grub_parser_param *state,
struct grub_script_arglist *arglist)
{
struct grub_script_cmdline *cmd;
grub_dprintf ("scripting", "cmdline\n");
cmd = grub_script_malloc (state, sizeof (*cmd));
if (!cmd)
return 0;
cmd->cmd.exec = grub_script_execute_cmdline;
cmd->cmd.next = 0;
cmd->arglist = arglist;
return (struct grub_script_cmd *) cmd;
}
/* Create a command that functions as an if statement. If BOOL is
evaluated to true (the value is returned in envvar '?'), the
interpreter will run the command TRUE, otherwise the interpreter
runs the command FALSE. */
struct grub_script_cmd *
grub_script_create_cmdif (struct grub_parser_param *state,
struct grub_script_cmd *exec_to_evaluate,
struct grub_script_cmd *exec_on_true,
struct grub_script_cmd *exec_on_false)
{
struct grub_script_cmdif *cmd;
grub_dprintf ("scripting", "cmdif\n");
cmd = grub_script_malloc (state, sizeof (*cmd));
if (!cmd)
return 0;
cmd->cmd.exec = grub_script_execute_cmdif;
cmd->cmd.next = 0;
cmd->exec_to_evaluate = exec_to_evaluate;
cmd->exec_on_true = exec_on_true;
cmd->exec_on_false = exec_on_false;
return (struct grub_script_cmd *) cmd;
}
/* Create a command that functions as a for statement. */
struct grub_script_cmd *
grub_script_create_cmdfor (struct grub_parser_param *state,
struct grub_script_arg *name,
struct grub_script_arglist *words,
struct grub_script_cmd *list)
{
struct grub_script_cmdfor *cmd;
grub_dprintf ("scripting", "cmdfor\n");
cmd = grub_script_malloc (state, sizeof (*cmd));
if (! cmd)
return 0;
cmd->cmd.exec = grub_script_execute_cmdfor;
cmd->cmd.next = 0;
cmd->name = name;
cmd->words = words;
cmd->list = list;
return (struct grub_script_cmd *) cmd;
}
/* Create a "while" or "until" command. */
struct grub_script_cmd *
grub_script_create_cmdwhile (struct grub_parser_param *state,
struct grub_script_cmd *cond,
struct grub_script_cmd *list,
int is_an_until_loop)
{
struct grub_script_cmdwhile *cmd;
cmd = grub_script_malloc (state, sizeof (*cmd));
if (! cmd)
return 0;
cmd->cmd.exec = grub_script_execute_cmdwhile;
cmd->cmd.next = 0;
cmd->cond = cond;
cmd->list = list;
cmd->until = is_an_until_loop;
return (struct grub_script_cmd *) cmd;
}
/* Create a command that adds a menu entry to the menu. Title is an
argument that is parsed to generate a string that can be used as
the title. The sourcecode for this entry is passed in SOURCECODE.
The options for this entry are passed in OPTIONS. */
struct grub_script_cmd *
grub_script_create_cmdmenu (struct grub_parser_param *state,
struct grub_script_arglist *arglist,
char *sourcecode, int options)
{
struct grub_script_cmd_menuentry *cmd;
cmd = grub_script_malloc (state, sizeof (*cmd));
if (!cmd)
return 0;
cmd->cmd.exec = grub_script_execute_menuentry;
cmd->cmd.next = 0;
cmd->sourcecode = sourcecode;
cmd->arglist = arglist;
cmd->options = options;
return (struct grub_script_cmd *) cmd;
}
/* Create a chain of commands. LAST contains the command that should
be added at the end of LIST's list. If LIST is zero, a new list
will be created. */
struct grub_script_cmd *
grub_script_append_cmd (struct grub_parser_param *state,
struct grub_script_cmd *list,
struct grub_script_cmd *last)
{
struct grub_script_cmd *ptr;
grub_dprintf ("scripting", "append command\n");
if (! last)
return list;
if (! list)
{
list = grub_script_malloc (state, sizeof (*list));
if (! list)
return 0;
list->exec = grub_script_execute_cmdlist;
list->next = last;
}
else
{
ptr = list;
while (ptr->next)
ptr = ptr->next;
ptr->next = last;
}
return list;
}
struct grub_script *
grub_script_create (struct grub_script_cmd *cmd, struct grub_script_mem *mem)
{
struct grub_script *parsed;
parsed = grub_malloc (sizeof (*parsed));
if (!parsed)
{
grub_script_mem_free (mem);
grub_free (cmd);
return 0;
}
parsed->mem = mem;
parsed->cmd = cmd;
return parsed;
}
/* Parse the script passed in SCRIPT and return the parsed
datastructure that is ready to be interpreted. */
struct grub_script *
grub_script_parse (char *script, grub_reader_getline_t getline)
{
struct grub_script *parsed;
struct grub_script_mem *membackup;
struct grub_lexer_param *lexstate;
struct grub_parser_param *parsestate;
parsed = grub_malloc (sizeof (*parsed));
if (!parsed)
return 0;
parsestate = grub_zalloc (sizeof (*parsestate));
if (!parsestate)
return 0;
/* Initialize the lexer. */
lexstate = grub_script_lexer_init (parsestate, script, getline);
if (!lexstate)
{
grub_free (parsed);
grub_free (parsestate);
return 0;
}
parsestate->lexerstate = lexstate;
membackup = grub_script_mem_record (parsestate);
/* Parse the script. */
if (grub_script_yyparse (parsestate) || parsestate->err)
{
struct grub_script_mem *memfree;
memfree = grub_script_mem_record_stop (parsestate, membackup);
grub_script_mem_free (memfree);
grub_script_lexer_fini (lexstate);
grub_free (parsestate);
return 0;
}
parsed->mem = grub_script_mem_record_stop (parsestate, membackup);
parsed->cmd = parsestate->parsed;
grub_script_lexer_fini (lexstate);
grub_free (parsestate);
return parsed;
}

328
grub-core/script/yylex.l Normal file
View file

@ -0,0 +1,328 @@
%{
/* yylex.l The scripting lexer. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009,2010 Free Software Foundation, Inc.
*
* GRUB 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, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/parser.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/script_sh.h>
#include "grub_script.tab.h"
#define yyfree grub_lexer_yyfree
#define yyalloc grub_lexer_yyalloc
#define yyrealloc grub_lexer_yyrealloc
/*
* As we don't have access to yyscanner, we cannot do much except to
* print the fatal error.
*/
#define YY_FATAL_ERROR(msg) \
do { \
grub_printf ("fatal error: %s\n", msg); \
} while (0)
#define COPY(str, hint) \
do { \
copy_string (yyextra, str, hint); \
} while (0)
#define RECORD \
do { \
grub_script_lexer_record (yyextra, yytext); \
} while (0)
#define ARG(t) \
do { \
yyextra->lexerstate->type = t; \
return GRUB_PARSER_TOKEN_WORD; \
} while (0)
/* We don't need YY_INPUT, as we rely on yy_scan_strings */
#define YY_INPUT(buf,res,max) do { res = 0; } while (0)
/* forward declarations */
static void grub_lexer_yyfree (void *, yyscan_t yyscanner);
static void* grub_lexer_yyalloc (yy_size_t, yyscan_t yyscanner);
static void* grub_lexer_yyrealloc (void*, yy_size_t, yyscan_t yyscanner);
static void copy_string (struct grub_parser_param *, const char *,
unsigned hint);
%}
%top{
#include <sys/types.h>
typedef size_t yy_size_t;
#define YY_TYPEDEF_YY_SIZE_T 1
/*
* Some flex hacks for -nostdinc; XXX We need to fix these when libc
* support becomes availble in GRUB.
*/
#ifndef GRUB_UTIL
#define stdin 0
#define stdout 0
#define fprintf(...) 0
#define exit(...)
#endif
}
%option ecs
%option meta-ecs
%option warn
%option array
%option stack
%option reentrant
%option bison-bridge
%option never-interactive
%option noyyfree noyyalloc noyyrealloc
%option nounistd nostdinit nodefault noyylineno noyywrap
/* Reduce lexer size, by not defining these. */
%option noyy_top_state
%option noinput nounput
%option noyyget_in noyyset_in
%option noyyget_out noyyset_out
%option noyyget_debug noyyset_debug
%option noyyget_lineno noyyset_lineno
%option extra-type="struct grub_parser_param*"
BLANK [ \t]
COMMENT #.*$
CHAR [^{}|&$;<> \t\n\'\"\\]
DIGITS [[:digit:]]+
NAME [[:alpha:]_][[:alnum:]_]*
ESC \\.
SPECIAL \?|\#|\*|\@
VARIABLE ${NAME}|$\{{NAME}\}|${DIGITS}|$\{{DIGITS}\}|${SPECIAL}|$\{{SPECIAL}\}
DQSTR \"([^\\\"]|{ESC})*\"
SQSTR \'[^\']*\'
WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+
%x SPLIT
%x DQUOTE
%x SQUOTE
%x VAR
%%
/* White spaces */
{BLANK}+ { RECORD; }
{COMMENT} { RECORD; }
/* Special symbols */
"\n" { RECORD; return GRUB_PARSER_TOKEN_NEWLINE; }
"||" { RECORD; return GRUB_PARSER_TOKEN_OR; }
"&&" { RECORD; return GRUB_PARSER_TOKEN_AND; }
";;" { RECORD; return GRUB_PARSER_TOKEN_SEMI2; }
"|" { RECORD; return GRUB_PARSER_TOKEN_PIPE; }
"&" { RECORD; return GRUB_PARSER_TOKEN_AMP; }
";" { RECORD; return GRUB_PARSER_TOKEN_SEMI; }
"<" { RECORD; return GRUB_PARSER_TOKEN_LT; }
">" { RECORD; return GRUB_PARSER_TOKEN_GT; }
/* Reserved words */
"!" { RECORD; return GRUB_PARSER_TOKEN_NOT; }
"{" { RECORD; return GRUB_PARSER_TOKEN_LBR; }
"}" { RECORD; return GRUB_PARSER_TOKEN_RBR; }
"[[" { RECORD; return GRUB_PARSER_TOKEN_RSQBR2; }
"]]" { RECORD; return GRUB_PARSER_TOKEN_LSQBR2; }
"time" { RECORD; return GRUB_PARSER_TOKEN_TIME; }
"case" { RECORD; return GRUB_PARSER_TOKEN_CASE; }
"do" { RECORD; return GRUB_PARSER_TOKEN_DO; }
"done" { RECORD; return GRUB_PARSER_TOKEN_DONE; }
"elif" { RECORD; return GRUB_PARSER_TOKEN_ELIF; }
"else" { RECORD; return GRUB_PARSER_TOKEN_ELSE; }
"esac" { RECORD; return GRUB_PARSER_TOKEN_ESAC; }
"fi" { RECORD; return GRUB_PARSER_TOKEN_FI; }
"for" { RECORD; return GRUB_PARSER_TOKEN_FOR; }
"if" { RECORD; return GRUB_PARSER_TOKEN_IF; }
"in" { RECORD; return GRUB_PARSER_TOKEN_IN; }
"select" { RECORD; return GRUB_PARSER_TOKEN_SELECT; }
"then" { RECORD; return GRUB_PARSER_TOKEN_THEN; }
"until" { RECORD; return GRUB_PARSER_TOKEN_UNTIL; }
"while" { RECORD; return GRUB_PARSER_TOKEN_WHILE; }
"function" { RECORD; return GRUB_PARSER_TOKEN_FUNCTION; }
"menuentry" { RECORD; return GRUB_PARSER_TOKEN_MENUENTRY; }
{NAME} { RECORD; return GRUB_PARSER_TOKEN_NAME; }
{WORD} {
RECORD;
/* resplit yytext */
grub_dprintf ("lexer", "word: [%s]\n", yytext);
yypush_buffer_state (YY_CURRENT_BUFFER, yyscanner);
if (yy_scan_string (yytext, yyscanner))
{
yyextra->lexerstate->merge_start = 1;
yy_push_state (SPLIT, yyscanner);
}
else
{
grub_script_yyerror (yyextra, 0);
yypop_buffer_state (yyscanner);
return GRUB_PARSER_TOKEN_WORD;
}
}
.|\n {
grub_script_yyerror (yyextra, "unrecognized token");
return GRUB_PARSER_TOKEN_BAD;
}
/* Split word into multiple args */
<SPLIT>{
\\. { COPY (yytext + 1, yyleng - 1); }
\" {
yy_push_state (DQUOTE, yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
}
\' {
yy_push_state (SQUOTE, yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
}
\$ {
yy_push_state (VAR, yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
}
\\ |
[^\"\'\$\\]+ { COPY (yytext, yyleng); }
<<EOF>> {
yy_pop_state (yyscanner);
yypop_buffer_state (yyscanner);
yyextra->lexerstate->merge_end = 1;
ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
}
}
<VAR>{
{SPECIAL} |
{DIGITS} |
{NAME} {
COPY (yytext, yyleng);
yy_pop_state (yyscanner);
if (YY_START == SPLIT)
ARG (GRUB_SCRIPT_ARG_TYPE_VAR);
else
ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR);
}
\{{SPECIAL}\} |
\{{DIGITS}\} |
\{{NAME}\} {
yytext[yyleng - 1] = '\0';
COPY (yytext + 1, yyleng - 2);
yy_pop_state (yyscanner);
if (YY_START == SPLIT)
ARG (GRUB_SCRIPT_ARG_TYPE_VAR);
else
ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR);
}
.|\n { return GRUB_PARSER_TOKEN_BAD; }
}
<SQUOTE>{
\' {
yy_pop_state (yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_SQSTR);
}
[^\']+ { COPY (yytext, yyleng); }
}
<DQUOTE>{
\\\$ { COPY ("$", 1); }
\\\\ { COPY ("\\", 1); }
\\\" { COPY ("\"", 1); }
\\\n { /* ignore */ }
[^\"\$\\\n]+ { COPY (yytext, yyleng); }
\" {
yy_pop_state (yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR);
}
\$ {
yy_push_state (VAR, yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR);
}
(.|\n) { COPY (yytext, yyleng); }
}
<<EOF>> {
yypop_buffer_state (yyscanner);
if (! grub_script_lexer_yywrap (yyextra))
{
yyextra->lexerstate->eof = 1;
return GRUB_PARSER_TOKEN_EOF;
}
}
%%
static void
grub_lexer_yyfree (void *ptr, yyscan_t yyscanner __attribute__ ((unused)))
{
grub_free(ptr);
}
static void*
grub_lexer_yyalloc (yy_size_t size, yyscan_t yyscanner __attribute__ ((unused)))
{
return grub_malloc (size);
}
static void*
grub_lexer_yyrealloc (void *ptr, yy_size_t size,
yyscan_t yyscanner __attribute__ ((unused)))
{
return grub_realloc (ptr, size);
}
#define MAX(a,b) ((a) < (b) ? (b) : (a))
static void copy_string (struct grub_parser_param *parser, const char *str, unsigned hint)
{
int size;
char *ptr;
unsigned len;
len = hint ? hint : grub_strlen (str);
if (parser->lexerstate->used + len >= parser->lexerstate->size)
{
size = MAX (len, parser->lexerstate->size) * 2;
ptr = grub_realloc (parser->lexerstate->text, size);
if (!ptr)
{
grub_script_yyerror (parser, 0);
return;
}
parser->lexerstate->text = ptr;
parser->lexerstate->size = size;
}
grub_strcpy (parser->lexerstate->text + parser->lexerstate->used - 1, str);
parser->lexerstate->used += len;
}