summaryrefslogtreecommitdiffstats
path: root/grub-core/script/execute.c
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/script/execute.c')
-rw-r--r--grub-core/script/execute.c1192
1 files changed, 1192 insertions, 0 deletions
diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c
new file mode 100644
index 0000000..2515840
--- /dev/null
+++ b/grub-core/script/execute.c
@@ -0,0 +1,1192 @@
+/* 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>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+#include <grub/verify.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;
+static unsigned long function_return;
+
+#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;
+
+/* Wildcard translator for GRUB script. */
+struct grub_script_wildcard_translator *grub_wildcard_translator;
+
+static char*
+wildcard_escape (const char *s)
+{
+ int i;
+ int len;
+ char ch;
+ char *p;
+
+ len = grub_strlen (s);
+ p = grub_malloc (len * 2 + 1);
+ if (! p)
+ return NULL;
+
+ i = 0;
+ while ((ch = *s++))
+ {
+ if (ch == '*' || ch == '\\' || ch == '?')
+ p[i++] = '\\';
+ p[i++] = ch;
+ }
+ p[i] = '\0';
+ return p;
+}
+
+static char*
+wildcard_unescape (const char *s)
+{
+ int i;
+ int len;
+ char ch;
+ char *p;
+
+ len = grub_strlen (s);
+ p = grub_malloc (len + 1);
+ if (! p)
+ return NULL;
+
+ i = 0;
+ while ((ch = *s++))
+ {
+ if (ch == '\\')
+ p[i++] = *s++;
+ else
+ p[i++] = ch;
+ }
+ p[i] = '\0';
+ return p;
+}
+
+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[])
+{
+ const char *p = NULL;
+ unsigned long count;
+
+ if (argc == 0)
+ count = 1;
+ else if (argc > 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
+ else
+ {
+ count = grub_strtoul (argv[0], &p, 10);
+ if (grub_errno)
+ return grub_errno;
+ if (*p != '\0')
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized number"));
+ if (count == 0)
+ /* TRANSLATORS: 0 is a quantifier. "break" (similar to bash)
+ can be used e.g. to break 3 loops at once.
+ But asking it to break 0 loops makes no sense. */
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't break 0 loops"));
+ }
+
+ is_continue = grub_strcmp (cmd->name, "break") ? 1 : 0;
+ active_breaks = count;
+ if (active_breaks > active_loops)
+ active_breaks = active_loops;
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_script_shift (grub_command_t cmd __attribute__((unused)),
+ int argc, char *argv[])
+{
+ const char *p = NULL;
+ 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, 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;
+}
+
+grub_err_t
+grub_script_return (grub_command_t cmd __attribute__((unused)),
+ int argc, char *argv[])
+{
+ const char *p = NULL;
+ unsigned long n;
+
+ if (! scope || argc > 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ /* TRANSLATORS: It's about not being
+ inside a function. "return" can be used only
+ in a function and this error occurs if it's used
+ anywhere else. */
+ N_("not in function body"));
+
+ if (argc == 0)
+ {
+ const char *t;
+ function_return = 1;
+ t = grub_env_get ("?");
+ if (!t)
+ return GRUB_ERR_NONE;
+ return grub_strtoul (t, NULL, 10);
+ }
+
+ n = grub_strtoul (argv[0], &p, 10);
+ if (grub_errno)
+ return grub_errno;
+ if (*p != '\0')
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("unrecognized number"));
+
+ function_return = 1;
+ return n ? grub_error (n, N_("false")) : 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, 0 };
+
+ if (grub_script_argv_next (&result))
+ goto fail;
+
+ if (! grub_env_special (name))
+ {
+ const 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, grub_strlen (v)))
+ goto fail;
+ }
+ }
+ else if (! scope)
+ {
+ if (grub_script_argv_append (&result, 0, 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, grub_strlen (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, " ", 1))
+ goto fail;
+
+ if (grub_script_argv_append (&result, scope->argv.args[i],
+ grub_strlen (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],
+ grub_strlen (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],
+ grub_strlen (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,
+ N_("invalid variable name `%s'"), name);
+
+ return grub_env_set (name, val);
+}
+
+struct gettext_context
+{
+ char **allowed_strings;
+ grub_size_t nallowed_strings;
+ grub_size_t additional_len;
+};
+
+static int
+parse_string (const char *str,
+ int (*hook) (const char *var, grub_size_t varlen,
+ char **ptr, struct gettext_context *ctx),
+ struct gettext_context *ctx,
+ char *put)
+{
+ const char *ptr;
+ int escaped = 0;
+ const char *optr;
+
+ for (ptr = str; ptr && *ptr; )
+ switch (*ptr)
+ {
+ case '\\':
+ escaped = !escaped;
+ if (!escaped && put)
+ *(put++) = '\\';
+ ptr++;
+ break;
+ case '$':
+ if (escaped)
+ {
+ escaped = 0;
+ if (put)
+ *(put++) = *ptr;
+ ptr++;
+ break;
+ }
+
+ ptr++;
+ switch (*ptr)
+ {
+ case '{':
+ {
+ optr = ptr + 1;
+ ptr = grub_strchr (optr, '}');
+ if (!ptr)
+ break;
+ if (hook (optr, ptr - optr, &put, ctx))
+ return 1;
+ ptr++;
+ break;
+ }
+ case '0' ... '9':
+ optr = ptr;
+ while (*ptr >= '0' && *ptr <= '9')
+ ptr++;
+ if (hook (optr, ptr - optr, &put, ctx))
+ return 1;
+ break;
+ case 'a' ... 'z':
+ case 'A' ... 'Z':
+ case '_':
+ optr = ptr;
+ while ((*ptr >= '0' && *ptr <= '9')
+ || (*ptr >= 'a' && *ptr <= 'z')
+ || (*ptr >= 'A' && *ptr <= 'Z')
+ || *ptr == '_')
+ ptr++;
+ if (hook (optr, ptr - optr, &put, ctx))
+ return 1;
+ break;
+ case '?':
+ case '#':
+ if (hook (ptr, 1, &put, ctx))
+ return 1;
+ ptr++;
+ break;
+ default:
+ if (put)
+ *(put++) = '$';
+ }
+ break;
+ default:
+ if (escaped && put)
+ *(put++) = '\\';
+ escaped = 0;
+ if (put)
+ *(put++) = *ptr;
+ ptr++;
+ break;
+ }
+ if (put)
+ *(put++) = 0;
+ return 0;
+}
+
+static int
+gettext_putvar (const char *str, grub_size_t len,
+ char **ptr, struct gettext_context *ctx)
+{
+ const char *var;
+ grub_size_t i;
+
+ for (i = 0; i < ctx->nallowed_strings; i++)
+ if (grub_strncmp (ctx->allowed_strings[i], str, len) == 0
+ && ctx->allowed_strings[i][len] == 0)
+ {
+ break;
+ }
+ if (i == ctx->nallowed_strings)
+ return 0;
+
+ /* Enough for any number. */
+ if (len == 1 && str[0] == '#' && scope != NULL)
+ {
+ grub_snprintf (*ptr, 30, "%u", scope->argv.argc);
+ *ptr += grub_strlen (*ptr);
+ return 0;
+ }
+ var = grub_env_get (ctx->allowed_strings[i]);
+ if (var)
+ *ptr = grub_stpcpy (*ptr, var);
+ return 0;
+}
+
+static int
+gettext_save_allow (const char *str, grub_size_t len,
+ char **ptr __attribute__ ((unused)),
+ struct gettext_context *ctx)
+{
+ ctx->allowed_strings[ctx->nallowed_strings++] = grub_strndup (str, len);
+ if (!ctx->allowed_strings[ctx->nallowed_strings - 1])
+ return 1;
+ return 0;
+}
+
+static int
+gettext_getlen (const char *str, grub_size_t len,
+ char **ptr __attribute__ ((unused)),
+ struct gettext_context *ctx)
+{
+ const char *var;
+ grub_size_t i;
+
+ for (i = 0; i < ctx->nallowed_strings; i++)
+ if (grub_strncmp (ctx->allowed_strings[i], str, len) == 0
+ && ctx->allowed_strings[i][len] == 0)
+ break;
+ if (i == ctx->nallowed_strings)
+ return 0;
+
+ /* Enough for any number. */
+ if (len == 1 && str[0] == '#')
+ {
+ ctx->additional_len += 30;
+ return 0;
+ }
+ var = grub_env_get (ctx->allowed_strings[i]);
+ if (var)
+ ctx->additional_len += grub_strlen (var);
+ return 0;
+}
+
+static int
+gettext_append (struct grub_script_argv *result, const char *orig_str)
+{
+ const char *template;
+ char *res = 0;
+ struct gettext_context ctx = {
+ .allowed_strings = 0,
+ .nallowed_strings = 0,
+ .additional_len = 1
+ };
+ int rval = 1;
+ const char *iptr;
+
+ grub_size_t dollar_cnt = 0;
+
+ for (iptr = orig_str; *iptr; iptr++)
+ if (*iptr == '$')
+ dollar_cnt++;
+ ctx.allowed_strings = grub_calloc (dollar_cnt, sizeof (ctx.allowed_strings[0]));
+
+ if (parse_string (orig_str, gettext_save_allow, &ctx, 0))
+ goto fail;
+
+ template = _(orig_str);
+
+ if (parse_string (template, gettext_getlen, &ctx, 0))
+ goto fail;
+
+ res = grub_malloc (grub_strlen (template) + ctx.additional_len);
+ if (!res)
+ goto fail;
+
+ if (parse_string (template, gettext_putvar, &ctx, res))
+ goto fail;
+
+ char *escaped = 0;
+ escaped = wildcard_escape (res);
+ if (grub_script_argv_append (result, escaped, grub_strlen (escaped)))
+ {
+ grub_free (escaped);
+ goto fail;
+ }
+ grub_free (escaped);
+
+ rval = 0;
+ fail:
+ grub_free (res);
+ {
+ grub_size_t i;
+ for (i = 0; i < ctx.nallowed_strings; i++)
+ grub_free (ctx.allowed_strings[i]);
+ }
+ grub_free (ctx.allowed_strings);
+ return rval;
+}
+
+static int
+append (struct grub_script_argv *result,
+ const char *s, int escape_type)
+{
+ int r;
+ char *p = 0;
+
+ if (escape_type == 0)
+ return grub_script_argv_append (result, s, grub_strlen (s));
+
+ if (escape_type > 0)
+ p = wildcard_escape (s);
+ else if (escape_type < 0)
+ p = wildcard_unescape (s);
+
+ if (! p)
+ return 1;
+
+ r = grub_script_argv_append (result, p, grub_strlen (p));
+ grub_free (p);
+ return r;
+}
+
+/* Convert arguments in ARGLIST into ARGV form. */
+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, 0 };
+
+ if (arglist == NULL)
+ return 1;
+
+ 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:
+ {
+ int need_cleanup = 0;
+
+ values = grub_script_env_get (arg->str, arg->type);
+ for (i = 0; values && values[i]; i++)
+ {
+ if (!need_cleanup)
+ {
+ if (i != 0 && grub_script_argv_next (&result))
+ {
+ need_cleanup = 1;
+ goto cleanup;
+ }
+
+ if (arg->type == GRUB_SCRIPT_ARG_TYPE_VAR)
+ {
+ int len;
+ char ch;
+ char *p;
+ char *op;
+ const char *s = values[i];
+
+ len = grub_strlen (values[i]);
+ /* \? -> \\\? */
+ /* \* -> \\\* */
+ /* \ -> \\ */
+ p = grub_malloc (len * 2 + 1);
+ if (! p)
+ {
+ need_cleanup = 1;
+ goto cleanup;
+ }
+
+ op = p;
+ while ((ch = *s++))
+ {
+ if (ch == '\\')
+ {
+ *op++ = '\\';
+ if (*s == '?' || *s == '*')
+ *op++ = '\\';
+ }
+ *op++ = ch;
+ }
+ *op = '\0';
+
+ need_cleanup = grub_script_argv_append (&result, p, op - p);
+ grub_free (p);
+ /* Fall through to cleanup */
+ }
+ else
+ {
+ need_cleanup = append (&result, values[i], 1);
+ /* Fall through to cleanup */
+ }
+ }
+
+cleanup:
+ grub_free (values[i]);
+ }
+ grub_free (values);
+
+ if (need_cleanup)
+ goto fail;
+
+ break;
+ }
+
+ case GRUB_SCRIPT_ARG_TYPE_BLOCK:
+ {
+ char *p;
+ if (grub_script_argv_append (&result, "{", 1))
+ goto fail;
+ p = wildcard_escape (arg->str);
+ if (!p)
+ goto fail;
+ if (grub_script_argv_append (&result, p,
+ grub_strlen (p)))
+ {
+ grub_free (p);
+ goto fail;
+ }
+ grub_free (p);
+ if (grub_script_argv_append (&result, "}", 1))
+ goto fail;
+ }
+ result.script = arg->script;
+ break;
+
+ case GRUB_SCRIPT_ARG_TYPE_TEXT:
+ if (arg->str[0] &&
+ grub_script_argv_append (&result, arg->str,
+ grub_strlen (arg->str)))
+ goto fail;
+ break;
+
+ case GRUB_SCRIPT_ARG_TYPE_GETTEXT:
+ {
+ if (gettext_append (&result, arg->str))
+ goto fail;
+ }
+ break;
+
+ case GRUB_SCRIPT_ARG_TYPE_DQSTR:
+ case GRUB_SCRIPT_ARG_TYPE_SQSTR:
+ if (append (&result, arg->str, 1))
+ goto fail;
+ break;
+ }
+ arg = arg->next;
+ }
+ }
+
+ if (! result.args[result.argc - 1])
+ result.argc--;
+
+ /* Perform wildcard expansion. */
+
+ int j;
+ int failed = 0;
+ struct grub_script_argv unexpanded = result;
+
+ result.argc = 0;
+ result.args = 0;
+ for (i = 0; unexpanded.args[i]; i++)
+ {
+ char **expansions = 0;
+ if (grub_wildcard_translator
+ && grub_wildcard_translator->expand (unexpanded.args[i],
+ &expansions))
+ {
+ grub_script_argv_free (&unexpanded);
+ goto fail;
+ }
+
+ if (! expansions)
+ {
+ grub_script_argv_next (&result);
+ append (&result, unexpanded.args[i], -1);
+ }
+ else
+ {
+ for (j = 0; expansions[j]; j++)
+ {
+ failed = (failed || grub_script_argv_next (&result) ||
+ append (&result, expansions[j], 0));
+ grub_free (expansions[j]);
+ }
+ grub_free (expansions);
+
+ if (failed)
+ {
+ grub_script_argv_free (&unexpanded);
+ goto fail;
+ }
+ }
+ }
+ grub_script_argv_free (&unexpanded);
+
+ *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;
+
+ func->executing++;
+ ret = grub_script_execute (func->func);
+ func->executing--;
+
+ function_return = 0;
+ active_loops = loops;
+ replace_scope (old_scope); /* free any scopes by setparams */
+ return ret;
+}
+
+/* Helper for grub_script_execute_sourcecode. */
+static grub_err_t
+grub_script_execute_sourcecode_getline (char **line,
+ int cont __attribute__ ((unused)),
+ void *data)
+{
+ const char **source = data;
+ const char *p;
+
+ if (! *source)
+ {
+ *line = 0;
+ return 0;
+ }
+
+ p = grub_strchr (*source, '\n');
+
+ if (p)
+ *line = grub_strndup (*source, p - *source);
+ else
+ *line = grub_strdup (*source);
+ *source = p ? p + 1 : 0;
+ return 0;
+}
+
+/* Execute a source script. */
+grub_err_t
+grub_script_execute_sourcecode (const char *source)
+{
+ grub_err_t ret = 0;
+ struct grub_script *parsed_script;
+
+ while (source)
+ {
+ char *line;
+
+ grub_script_execute_sourcecode_getline (&line, 0, &source);
+ parsed_script = grub_script_parse
+ (line, grub_script_execute_sourcecode_getline, &source);
+ if (! parsed_script)
+ {
+ ret = grub_errno;
+ grub_free (line);
+ break;
+ }
+
+ ret = grub_script_execute (parsed_script);
+ grub_script_free (parsed_script);
+ grub_free (line);
+ }
+
+ return ret;
+}
+
+/* Execute a source script in new scope. */
+grub_err_t
+grub_script_execute_new_scope (const char *source, int argc, char **args)
+{
+ grub_err_t ret = 0;
+ struct grub_script_scope new_scope;
+ struct grub_script_scope *old_scope;
+
+ new_scope.argv.argc = argc;
+ new_scope.argv.args = args;
+ new_scope.flags = 0;
+
+ old_scope = scope;
+ scope = &new_scope;
+
+ ret = grub_script_execute_sourcecode (source);
+
+ scope = old_scope;
+ 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, *cmdstring;
+ int argc, offset = 0, cmdlen = 0;
+ unsigned int i;
+ char **args;
+ int invert;
+ struct grub_script_argv argv = { 0, 0, 0 };
+
+ /* Lookup the command. */
+ if (grub_script_arglist_to_argv (cmdline->arglist, &argv) || ! argv.args || ! argv.args[0])
+ return grub_errno;
+
+ for (i = 0; i < argv.argc; i++)
+ {
+ cmdlen += grub_strlen (argv.args[i]) + 1;
+ }
+
+ cmdstring = grub_malloc (cmdlen);
+ if (!cmdstring)
+ {
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ N_("cannot allocate command buffer"));
+ }
+
+ for (i = 0; i < argv.argc; i++)
+ {
+ offset += grub_snprintf (cmdstring + offset, cmdlen - offset, "%s ",
+ argv.args[i]);
+ }
+ cmdstring[cmdlen - 1] = '\0';
+ grub_verify_string (cmdstring, GRUB_VERIFY_COMMAND);
+ grub_free (cmdstring);
+ invert = 0;
+ argc = argv.argc - 1;
+ args = argv.args + 1;
+ cmdname = argv.args[0];
+ if (grub_strcmp (cmdname, "!") == 0)
+ {
+ if (argv.argc < 2 || ! argv.args[1])
+ {
+ grub_script_argv_free (&argv);
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("no command is specified"));
+ }
+
+ invert = 1;
+ argc = argv.argc - 2;
+ args = argv.args + 2;
+ cmdname = argv.args[1];
+ }
+ 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)
+ {
+ if (grub_extractor_level && !(grubcmd->flags
+ & GRUB_COMMAND_FLAG_EXTRACTOR))
+ ret = grub_error (GRUB_ERR_EXTRACTOR,
+ "%s isn't allowed to execute in an extractor",
+ cmdname);
+ else if ((grubcmd->flags & GRUB_COMMAND_FLAG_BLOCKS) &&
+ (grubcmd->flags & GRUB_COMMAND_FLAG_EXTCMD))
+ ret = grub_extcmd_dispatcher (grubcmd, argc, args, argv.script);
+ else
+ ret = (grubcmd->func) (grubcmd, argc, args);
+ }
+ else
+ ret = grub_script_function_call (func, argc, args);
+
+ if (invert)
+ {
+ if (ret == GRUB_ERR_TEST_FAILURE)
+ grub_errno = ret = GRUB_ERR_NONE;
+ else if (ret == GRUB_ERR_NONE)
+ ret = grub_error (GRUB_ERR_TEST_FAILURE, N_("false"));
+ else
+ {
+ grub_print_error ();
+ ret = GRUB_ERR_NONE;
+ }
+ }
+
+ /* 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; cmd = cmd->next)
+ {
+ if (active_breaks)
+ break;
+
+ ret = grub_script_execute_cmd (cmd);
+
+ if (function_return)
+ break;
+ }
+
+ return ret;
+}
+
+/* Execute an if statement. */
+grub_err_t
+grub_script_execute_cmdif (struct grub_script_cmd *cmd)
+{
+ int ret;
+ const char *result;
+ struct grub_script_cmdif *cmdif = (struct grub_script_cmdif *) cmd;
+
+ /* Check if the commands results in a true or a false. The value is
+ read from the env variable `?'. */
+ ret = grub_script_execute_cmd (cmdif->exec_to_evaluate);
+ if (function_return)
+ return ret;
+
+ 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, 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 (function_return)
+ break;
+ }
+ }
+
+ 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 result;
+ struct grub_script_cmdwhile *cmdwhile = (struct grub_script_cmdwhile *) cmd;
+
+ active_loops++;
+ do {
+ result = grub_script_execute_cmd (cmdwhile->cond);
+ if (function_return)
+ break;
+
+ if (cmdwhile->until ? !result : result)
+ break;
+
+ result = grub_script_execute_cmd (cmdwhile->list);
+ if (function_return)
+ break;
+
+ 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 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);
+}