diff --git a/conf/tests.rmk b/conf/tests.rmk index 9af2f8f86..9144e5528 100644 --- a/conf/tests.rmk +++ b/conf/tests.rmk @@ -71,6 +71,9 @@ grub_script_dollar_SOURCES = tests/grub_script_dollar.in check_SCRIPTS += grub_script_comments grub_script_comments_SOURCES = tests/grub_script_comments.in +check_SCRIPTS += grub_script_functions +grub_script_functions_SOURCES = tests/grub_script_functions.in + # List of tests to execute on "make check" # SCRIPTED_TESTS = example_scripted_test # SCRIPTED_TESTS += example_grub_script_test @@ -87,6 +90,7 @@ SCRIPTED_TESTS += grub_script_blanklines SCRIPTED_TESTS += grub_script_final_semicolon SCRIPTED_TESTS += grub_script_dollar SCRIPTED_TESTS += grub_script_comments +SCRIPTED_TESTS += grub_script_functions # dependencies between tests and testing-tools $(SCRIPTED_TESTS): grub-shell grub-shell-tester diff --git a/include/grub/script_sh.h b/include/grub/script_sh.h index 53f4f13bb..730aa3005 100644 --- a/include/grub/script_sh.h +++ b/include/grub/script_sh.h @@ -73,6 +73,15 @@ struct grub_script_arglist int argcount; }; +/* Scope for grub script constructs. */ +struct grub_script_scope +{ + struct grub_script_scope *next; + + char **args; + unsigned int argc; +}; + /* A single command line. */ struct grub_script_cmdline { @@ -329,8 +338,8 @@ grub_script_function_t grub_script_function_create (struct grub_script_arg *func void grub_script_function_remove (const char *name); grub_script_function_t grub_script_function_find (char *functionname); int grub_script_function_iterate (int (*iterate) (grub_script_function_t)); -int grub_script_function_call (grub_script_function_t func, - int argc, char **args); +grub_err_t grub_script_function_call (grub_script_function_t func, + int argc, char **args); char ** grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *count); diff --git a/script/execute.c b/script/execute.c index e10558b4d..571b6785b 100644 --- a/script/execute.c +++ b/script/execute.c @@ -30,6 +30,52 @@ is sizeof (int) * 3, and one extra for a possible -ve sign. */ #define ERRNO_DIGITS_MAX (sizeof (int) * 3 + 1) +static struct grub_script_scope *scope = 0; + +static char * +grub_script_env_get (const char *name) +{ + char *p = 0; + unsigned long num = 0; + + if (! scope) + return grub_env_get (name); + + if (grub_isdigit (name[0])) + { + num = grub_strtoul (name, &p, 10); + if (p && *p == '\0') + { + if (num == 0) + return 0; /* XXX no file name, for now. */ + + return (num > scope->argc ? 0 : scope->args[num - 1]); + } + else + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "bad variabe name substitution"); + return 0; + } + } + else if (grub_strcmp (name, "#") == 0) + { + static char buf[32]; /* Rewritten everytime. */ + grub_snprintf (buf, sizeof (buf), "%u", scope->argc); + return buf; + } + else + return grub_env_get (name); +} + +static grub_err_t +grub_script_env_set (const char *name, const char *val) +{ + if (grub_isdigit (name[0]) || grub_strcmp (name, "#") == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad variable name"); + + return grub_env_set (name, val); +} + static grub_err_t grub_script_execute_cmd (struct grub_script_cmd *cmd) { @@ -143,7 +189,7 @@ grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *c switch (arg->type) { case GRUB_SCRIPT_ARG_TYPE_VAR: - value = grub_env_get (arg->str); + value = grub_script_env_get (arg->str); while (value && *value && (ptr = move_to_next(&value))) { empty = 0; @@ -168,7 +214,7 @@ grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *c case GRUB_SCRIPT_ARG_TYPE_DQVAR: empty = 0; - append (grub_env_get (arg->str), 0); + append (grub_script_env_get (arg->str), 0); break; } arg = arg->next; @@ -191,6 +237,23 @@ grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *c return argv; } +/* 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; + struct grub_script_scope new_scope; + + new_scope.argc = argc; + new_scope.args = args; + grub_list_push (GRUB_AS_LIST_P (&scope), GRUB_AS_LIST (&new_scope)); + + ret = grub_script_execute (func->func); + + grub_list_pop (GRUB_AS_LIST_P (&scope)); + return ret; +} + /* Execute a single command line. */ grub_err_t grub_script_execute_cmdline (struct grub_script_cmd *cmd) @@ -232,12 +295,12 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) /* Create two strings and set the variable. */ *eq = '\0'; eq++; - grub_env_set (assign, eq); + grub_script_env_set (assign, eq); } grub_free (assign); grub_snprintf (errnobuf, sizeof (errnobuf), "%d", grub_errno); - grub_env_set ("?", errnobuf); + grub_script_env_set ("?", errnobuf); grub_print_error (); @@ -291,7 +354,7 @@ grub_script_execute_cmdif (struct grub_script_cmd *cmd) /* 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 ("?"); + result = grub_script_env_get ("?"); grub_errno = GRUB_ERR_NONE; @@ -320,7 +383,7 @@ grub_script_execute_cmdfor (struct grub_script_cmd *cmd) result = 0; for (i = 0; i < argcount; i++) { - grub_env_set (cmdfor->name->str, args[i]); + grub_script_env_set (cmdfor->name->str, args[i]); result = grub_script_execute_cmd (cmdfor->list); grub_free (args[i]); } diff --git a/script/function.c b/script/function.c index ded470c4e..82c753bcd 100644 --- a/script/function.c +++ b/script/function.c @@ -115,12 +115,3 @@ grub_script_function_iterate (int (*iterate) (grub_script_function_t)) return 0; } - -int -grub_script_function_call (grub_script_function_t func, - int argc __attribute__((unused)), - char **args __attribute__((unused))) -{ - /* XXX: Arguments are not supported yet. */ - return grub_script_execute (func->func); -} diff --git a/script/yylex.l b/script/yylex.l index 7d4ea9e4e..f563ac30d 100644 --- a/script/yylex.l +++ b/script/yylex.l @@ -119,7 +119,8 @@ DIGITS [[:digit:]]+ NAME [[:alpha:]_][[:alnum:][:digit:]_]* ESC \\. -VARIABLE ${NAME}|$\{{NAME}\}|${DIGITS}|$\{{DIGITS}\}|$\?|$\{\?\} +SPECIAL \?|\# +VARIABLE ${NAME}|$\{{NAME}\}|${DIGITS}|$\{{DIGITS}\}|${SPECIAL}|$\{{SPECIAL}\} DQSTR \"([^\\\"]|{ESC})*\" SQSTR \'[^\']*\' WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+ @@ -221,7 +222,7 @@ WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+ } { - \? | + {SPECIAL} | {DIGITS} | {NAME} { COPY (yytext, yyleng); @@ -231,7 +232,7 @@ WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+ else ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR); } - \{\?\} | + \{{SPECIAL}\} | \{{DIGITS}\} | \{{NAME}\} { yytext[yyleng - 1] = '\0'; diff --git a/tests/grub_script_functions.in b/tests/grub_script_functions.in new file mode 100644 index 000000000..41af87474 --- /dev/null +++ b/tests/grub_script_functions.in @@ -0,0 +1,63 @@ +#! @builddir@/grub-shell-tester + +# Run GRUB script in a Qemu instance +# 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 . + +echo parameter count +function fcount { + echo "$#" +} + +fcount +fcount a +fcount a b + +echo parameter count, with nesting +function ffcount { + echo "$#" + fcount + fcount a + fcount a b +} + +ffcount +ffcount 1 +ffcount 1 2 + +echo parameters +function fparam { + echo 1 $1 + echo 2 $2 + echo 3 $3 +} + +fparam +fparam a +fparam a b + +echo parameters, with nesting +function ffparam { + echo 1 $1 + echo 2 $2 + echo 3 $3 + fparam + fparam a + fparam a b +} + +ffparam +ffparam 1 +ffparam 1 2