From aa5cd41af5b603b06f0082be797e668be3f7e256 Mon Sep 17 00:00:00 2001 From: BVK Chaitanya Date: Wed, 25 Aug 2010 19:35:52 +0530 Subject: [PATCH 1/6] return command for functions --- ChangeLog | 11 +++ Makefile.util.def | 6 ++ grub-core/script/execute.c | 60 +++++++++++++--- grub-core/script/main.c | 7 ++ include/grub/script_sh.h | 3 + tests/grub_script_return.in | 134 ++++++++++++++++++++++++++++++++++++ 6 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 tests/grub_script_return.in diff --git a/ChangeLog b/ChangeLog index 2db022484..0ce38240f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2010-08-25 BVK Chaitanya + + "return" command for GRUB script functions. + + * grub-core/script/main.c: Register/unregister return command. + * grub-core/script/execute.c (grub_script_return): New function. + * include/grub/script_sh.h (grub_script_return): New prototype. + + * tests/grub_script_return.in: New test for return command. + * Makefile.util.def: Rules for grub_script_return test. + 2010-08-23 BVK Chaitanya New Automake based build system for GRUB. diff --git a/Makefile.util.def b/Makefile.util.def index fd3428e76..4998bd254 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -496,6 +496,12 @@ script = { common = tests/grub_script_shift.in; }; +script = { + testcase; + name = grub_script_return; + common = tests/grub_script_return.in; +}; + program = { testcase; name = example_unit_test; diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index 26a46b12b..e78a41bb5 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -33,6 +33,7 @@ static unsigned long is_continue; static unsigned long active_loops; static unsigned long active_breaks; +static unsigned long function_return; /* Scope for grub script functions. */ struct grub_script_scope @@ -90,6 +91,30 @@ grub_script_shift (grub_command_t cmd __attribute__((unused)), return GRUB_ERR_NONE; } +grub_err_t +grub_script_return (grub_command_t cmd __attribute__((unused)), + int argc, char *argv[]) +{ + char *p; + unsigned long n; + + if (! scope || argc > 1) + return GRUB_ERR_BAD_ARGUMENT; + + if (argc == 0) + { + function_return = 1; + return grub_strtoul (grub_env_get ("?"), NULL, 10); + } + + n = grub_strtoul (argv[0], &p, 10); + if (*p != '\0') + return GRUB_ERR_BAD_ARGUMENT; + + function_return = 1; + return n; +} + static int grub_env_special (const char *name) { @@ -310,6 +335,7 @@ grub_script_function_call (grub_script_function_t func, int argc, char **args) ret = grub_script_execute (func->func); + function_return = 0; active_loops = loops; scope = old_scope; return ret; @@ -395,8 +421,16 @@ grub_script_execute_cmdlist (struct grub_script_cmd *list) 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); + for (cmd = list->next; cmd; cmd = cmd->next) + { + if (active_breaks) + break; + + ret = grub_script_execute_cmd (cmd); + + if (function_return) + break; + } return ret; } @@ -405,14 +439,17 @@ grub_script_execute_cmdlist (struct grub_script_cmd *list) grub_err_t grub_script_execute_cmdif (struct grub_script_cmd *cmd) { - struct grub_script_cmdif *cmdif = (struct grub_script_cmdif *) cmd; + int ret; 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 `?'. */ - grub_script_execute_cmd (cmdif->exec_to_evaluate); - result = grub_env_get ("?"); + 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 @@ -447,6 +484,8 @@ grub_script_execute_cmdfor (struct grub_script_cmd *cmd) { grub_script_env_set (cmdfor->name->str, argv.args[i]); result = grub_script_execute_cmd (cmdfor->list); + if (function_return) + break; } } @@ -462,18 +501,21 @@ grub_script_execute_cmdfor (struct grub_script_cmd *cmd) 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) + 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; diff --git a/grub-core/script/main.c b/grub-core/script/main.c index ff714d060..7caeb2661 100644 --- a/grub-core/script/main.c +++ b/grub-core/script/main.c @@ -44,6 +44,7 @@ grub_normal_parse_line (char *line, grub_reader_getline_t getline) static grub_command_t cmd_break; static grub_command_t cmd_continue; static grub_command_t cmd_shift; +static grub_command_t cmd_return; void grub_script_init (void) @@ -54,6 +55,8 @@ grub_script_init (void) N_("[n]"), N_("Continue loops")); cmd_shift = grub_register_command ("shift", grub_script_shift, N_("[n]"), N_("Shift positional parameters.")); + cmd_return = grub_register_command ("return", grub_script_return, + N_("[n]"), N_("Return from a function.")); } void @@ -70,4 +73,8 @@ grub_script_fini (void) if (cmd_shift) grub_unregister_command (cmd_shift); cmd_shift = 0; + + if (cmd_return) + grub_unregister_command (cmd_return); + cmd_return = 0; } diff --git a/include/grub/script_sh.h b/include/grub/script_sh.h index 77e807360..844ef7930 100644 --- a/include/grub/script_sh.h +++ b/include/grub/script_sh.h @@ -321,6 +321,9 @@ grub_err_t grub_script_break (grub_command_t cmd, int argc, char *argv[]); /* SHIFT command for GRUB script. */ grub_err_t grub_script_shift (grub_command_t cmd, int argc, char *argv[]); +/* RETURN command for functions. */ +grub_err_t grub_script_return (grub_command_t cmd, int argc, char *argv[]); + /* This variable points to the parsed command. This is used to communicate with the bison code. */ extern struct grub_script_cmd *grub_script_parsed; diff --git a/tests/grub_script_return.in b/tests/grub_script_return.in new file mode 100644 index 000000000..712d1dfcf --- /dev/null +++ b/tests/grub_script_return.in @@ -0,0 +1,134 @@ +#! @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 . + +function f1 { + return + echo one +} +f1 + +function f2 { + true + return + echo one +} +if f2; then echo true; else echo false; fi + +function f3 { + false + return + echo one +} +if f3; then echo true; else echo false; fi + +function f4 { + true + return 1; + echo one +} +if f4; then echo true; else echo false; fi + +function f5 { + false + return 0; + echo one +} +if f5; then echo true; else echo false; fi + +function f6 { + echo one + if true; then + echo two + return 0 + else + echo three + return 1 + fi + echo four +} +if f6; then echo true; else echo false; fi + +function f7 { + if return 1; then + echo one + else + echo no + fi +} +if f7; then echo true; else echo false; fi + +function f8 { + echo one + for v in 1 2 3 4 5; do + echo $v + if test $v = 3; then return 1; fi + done + echo two +} +if f8; then echo true; else echo false; fi + +function f9 { + x=1 + echo one + until test x = 11111111; do + echo $x + x="1$x" + if test $x = 1111; then return 0; fi + done + echo two +} +if f9; then echo true; else echo false; fi + +function f10 { + echo one + while return 0; do + echo two + done + echo three +} +if f10; then echo true; else echo false; fi + +function f11 { + f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + f9 + f10 +} +if f11; then echo true; else echo false; fi + +function f12 { + echo one + f11 + return 1 + echo two +} +if f12; then echo true; else echo false; fi + +function f13 { + echo one + f12 + echo two + return 0 +} +if f13; then echo true; else echo false; fi From 78780e7005c687d35d9a4a0ba09059520a1ecff6 Mon Sep 17 00:00:00 2001 From: BVK Chaitanya Date: Fri, 3 Sep 2010 20:53:38 +0530 Subject: [PATCH 2/6] fix hotkey handling --- grub-core/commands/menuentry.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c index be4fa3347..b6662bb96 100644 --- a/grub-core/commands/menuentry.c +++ b/grub-core/commands/menuentry.c @@ -107,8 +107,8 @@ append_menu_entry (int argc, const char **args, char **classes, menu_hotkey = hotkey_aliases[i].key; break; } - if (i > ARRAY_SIZE (hotkey_aliases)) - goto fail; + if (i == ARRAY_SIZE (hotkey_aliases)) + menu_hotkey = hotkey[0]; } if (! argc) From 9284756e17c87a76c2ec22b1c59f5e5eab2f3a1b Mon Sep 17 00:00:00 2001 From: BVK Chaitanya Date: Fri, 3 Sep 2010 21:08:12 +0530 Subject: [PATCH 3/6] merge menuentry.mod into normal.mod --- grub-core/Makefile.core.def | 7 ++----- grub-core/commands/menuentry.c | 6 ++++-- grub-core/normal/main.c | 2 ++ include/grub/menu.h | 3 +++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index bba3503a6..be005378b 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1173,6 +1173,8 @@ module = { common = script/lexer.c; common = script/argv.c; + common = commands/menuentry.c; + common = unidata.c; common_nodist = grub_script.tab.c; common_nodist = grub_script.yy.c; @@ -1408,8 +1410,3 @@ module = { name = test_blockarg; common = tests/test_blockarg.c; }; - -module = { - name = menuentry; - common = commands/menuentry.c; -}; \ No newline at end of file diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c index b6662bb96..5e20d4285 100644 --- a/grub-core/commands/menuentry.c +++ b/grub-core/commands/menuentry.c @@ -200,14 +200,16 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) static grub_extcmd_t cmd; -GRUB_MOD_INIT(menuentry) +void +grub_menu_init (void) { cmd = grub_register_extcmd ("menuentry", grub_cmd_menuentry, GRUB_COMMAND_FLAG_BOTH | GRUB_COMMAND_FLAG_BLOCKS, N_("BLOCK"), N_("Define a menuentry."), options); } -GRUB_MOD_FINI(menuentry) +void +grub_menu_fini (void) { grub_unregister_extcmd (cmd); } diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 64d438a91..6a008f577 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -476,6 +476,7 @@ GRUB_MOD_INIT(normal) { grub_context_init (); grub_script_init (); + grub_menu_init (); grub_xputs_saved = grub_xputs; grub_xputs = grub_xputs_normal; @@ -515,6 +516,7 @@ GRUB_MOD_FINI(normal) { grub_context_fini (); grub_script_fini (); + grub_menu_fini (); grub_xputs = grub_xputs_saved; diff --git a/include/grub/menu.h b/include/grub/menu.h index 9dc257ab7..608253863 100644 --- a/include/grub/menu.h +++ b/include/grub/menu.h @@ -100,4 +100,7 @@ void grub_menu_execute_with_fallback (grub_menu_t menu, void grub_menu_entry_run (grub_menu_entry_t entry); int grub_menu_get_default_entry_index (grub_menu_t menu); +void grub_menu_init (void); +void grub_menu_fini (void); + #endif /* GRUB_MENU_HEADER */ From e89f9ec539fdae1e5425d207a60a6ce1e9384106 Mon Sep 17 00:00:00 2001 From: BVK Chaitanya Date: Fri, 3 Sep 2010 22:28:16 +0530 Subject: [PATCH 4/6] add setparams prefix --- grub-core/commands/menuentry.c | 63 ++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c index 5e20d4285..6fdb172b6 100644 --- a/grub-core/commands/menuentry.c +++ b/grub-core/commands/menuentry.c @@ -53,7 +53,7 @@ static struct static grub_err_t append_menu_entry (int argc, const char **args, char **classes, const char *users, const char *hotkey, - const char *sourcecode) + const char *prefix, const char *sourcecode) { unsigned i; int menu_hotkey = 0; @@ -72,7 +72,7 @@ append_menu_entry (int argc, const char **args, char **classes, last = &menu->entry_list; - menu_sourcecode = grub_strdup (sourcecode); + menu_sourcecode = grub_xasprintf ("%s%s", prefix ?: "", sourcecode); if (! menu_sourcecode) return grub_errno; @@ -171,11 +171,63 @@ append_menu_entry (int argc, const char **args, char **classes, return grub_errno; } +static char * +setparams_prefix (int argc, char **args) +{ + int i; + int j; + char *p; + char *result; + grub_size_t len = 10; + static const char *escape_characters = "\"\\"; + + auto char *strescpy (char *, const char *, const char *); + char * strescpy (char *d, const char *s, const char *escapes) + { + while (*s) + { + if (grub_strchr (escapes, *s)) + *d++ = '\\'; + *d++ = *s++; + } + *d = '\0'; + return d; + } + + /* Count resulting string length */ + for (i = 0; i < argc; i++) + { + len += 3; /* 3 = 1 space + 2 quotes */ + p = args[i]; + while (*p) + len += grub_strchr (escape_characters, *p++) ? 2 : 1; + } + + result = grub_malloc (len + 2); + if (! result) + return 0; + + grub_strcpy (result, "setparams"); + i = 9; + + for (j = 0; j < argc; j++) + { + result[i++] = ' '; + result[i++] = '"'; + i = strescpy (result + i, args[j], escape_characters) - result; + result[i++] = '"'; + } + result[i++] = '\n'; + result[i] = '\0'; + return result; +} + static grub_err_t grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) { char ch; char *src; + char *prefix; unsigned len; grub_err_t r; @@ -189,12 +241,17 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) ch = src[len - 1]; src[len - 1] = '\0'; + prefix = setparams_prefix (argc - 1, args); + if (! prefix) + return grub_errno; + r = append_menu_entry (argc - 1, (const char **) args, ctxt->state[0].args, ctxt->state[1].arg, - ctxt->state[2].arg, src + 1); + ctxt->state[2].arg, prefix, src + 1); src[len - 1] = ch; args[argc - 1] = src; + grub_free (prefix); return r; } From 80f5b97cdce1cf615da9ad4fccfdbd9af6c3d47a Mon Sep 17 00:00:00 2001 From: BVK Chaitanya Date: Sat, 4 Sep 2010 08:14:50 +0530 Subject: [PATCH 5/6] --source option for menuentry --- grub-core/commands/menuentry.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c index 6fdb172b6..fc1ae71c7 100644 --- a/grub-core/commands/menuentry.c +++ b/grub-core/commands/menuentry.c @@ -33,6 +33,8 @@ static const struct grub_arg_option options[] = N_("Users allowed to boot this entry."), "USERNAME", ARG_TYPE_STRING}, {"hotkey", 3, 0, N_("Keyboard key for this entry."), "KEY", ARG_TYPE_STRING}, + {"source", 4, 0, + N_("Menu entry definition as a string."), "STRING", ARG_TYPE_STRING}, {0, 0, 0, 0, 0, 0} }; @@ -231,8 +233,19 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) unsigned len; grub_err_t r; - if (! argc || ! ctxt->script) - return GRUB_ERR_BAD_ARGUMENT; + if (! argc) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing arguments"); + + if (ctxt->state[3].set && ctxt->script) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "multiple menuentry definitions"); + + if (! ctxt->state[3].set && ! ctxt->script) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menuentry definition"); + + if (! ctxt->script) + return append_menu_entry (argc, (const char **) args, + ctxt->state[0].args, ctxt->state[1].arg, + ctxt->state[2].arg, 0, ctxt->state[3].arg); src = args[argc - 1]; args[argc - 1] = NULL; From 49649ac85d1c384597a3834948249fa9aac7ddd5 Mon Sep 17 00:00:00 2001 From: BVK Chaitanya Date: Sat, 4 Sep 2010 14:32:59 +0530 Subject: [PATCH 6/6] review comments --- grub-core/script/execute.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index e78a41bb5..2c3772ed8 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -99,7 +99,7 @@ grub_script_return (grub_command_t cmd __attribute__((unused)), unsigned long n; if (! scope || argc > 1) - return GRUB_ERR_BAD_ARGUMENT; + return grub_error (GRUB_ERR_BAD_ARGUMENT, "not in function scope"); if (argc == 0) { @@ -109,10 +109,10 @@ grub_script_return (grub_command_t cmd __attribute__((unused)), n = grub_strtoul (argv[0], &p, 10); if (*p != '\0') - return GRUB_ERR_BAD_ARGUMENT; + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad argument"); function_return = 1; - return n; + return n ? grub_error (GRUB_ERR_TEST_FAILURE, "false") : GRUB_ERR_NONE; } static int