merge with mainline
This commit is contained in:
commit
a9b85993ee
554 changed files with 6765 additions and 5034 deletions
150
grub-core/script/argv.c
Normal file
150
grub-core/script/argv.c
Normal 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
578
grub-core/script/execute.c
Normal 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
105
grub-core/script/function.c
Normal 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
346
grub-core/script/lexer.c
Normal 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
81
grub-core/script/main.c
Normal 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
284
grub-core/script/parser.y
Normal 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
401
grub-core/script/script.c
Normal 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
328
grub-core/script/yylex.l
Normal 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue