diff --git a/ChangeLog b/ChangeLog index 371e910c5..a155853d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,59 @@ +2010-09-04 BVK Chaitanya + + Support for options to appear multiple times on cmdline. + + * include/grub/lib/arg.h (grub_arg_list_alloc): New prototype. + * grub-core/commands/extcmd.c: Support for repeatable option. + * grub-core/lib/arg.c (grub_arg_list_alloc): New function for + repeatable option support. + + Refactor menuentry into a regular command. + + * grub-core/commands/menuentry.c: New file, menuentry command + implementation. + * grub-core/Makefile.core.def: Rule update for normal.mod. + * grub-core/normal/main.c: Moved menuentry creation to + grub-core/commands/menuentry.c. + * grub-core/normal/menu.c (grub_menu_execute_entry): Removed. + (grub_menu_execute_entry_real): Removed. + * grub-core/script/execute.c (grub_script_execute_sourcecode): New + function. + (grub_script_execute_menuentry): Removed. + * grub-core/script/parser.y (menuentry): Removed. + * grub-core/script/script.c (grub_script_create_cmdmenu): Removed. + * grub-core/script/yylex.l (menuentry): Removed. + * include/grub/menu.h (grub_menu_init): New prototype. + (grub_menu_fini): New prototype. + * include/grub/normal.h (grub_normal_add_menu_entry): Removed. + * include/grub/script_sh.h (grub_script_cmd_menuentry): Removed. + (grub_script_execute_sourcecode): New prototype. + +2010-09-04 BVK Chaitanya + + "return" command for GRUB script functions. + + * tests/grub_script_return.in: New test. + * Makefile.util.def: Rules for new test. + + * grub-core/script/execute.c (grub_script_return): New function. + * grub-core/script/main.c: Register/unregister return commaond. + * include/grub/script_sh.h (grub_script_return): New prototype. + +2010-09-04 BVK Chaitanya + + "setparams" command to update positional parameters. + + * tests/grub_script_setparams.in: New test. + * Makefile.util.def: Rules for new test. + + * grub-core/script/argv.c (grub_script_argv_make): New function. + * grub-core/script/execute.c (replace_scope): New function. + (grub_script_setparams): New function. + * grub-core/script/lexer.c: Remove unused variables. + * grub-core/script/main.c: Register/unregister setparams command. + * include/grub/script_sh.h (grub_script_argv_make): New prototype. + (grub_script_setparams): New prototype. + 2010-09-04 BVK Chaitanya * grub-core/normal/completion.c (grub_normal_do_completion): Fix diff --git a/Makefile.util.def b/Makefile.util.def index b811b4101..a3d5381d4 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -513,6 +513,12 @@ script = { common = tests/grub_script_setparams.in; }; +script = { + testcase; + name = grub_script_return; + common = tests/grub_script_return.in; +}; + script = { testcase; name = grub_cmd_regexp; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 0c5d3cf30..df67ae948 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1164,6 +1164,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; @@ -1385,8 +1387,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 be4fa3347..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} }; @@ -53,7 +55,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 +74,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; @@ -107,8 +109,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) @@ -171,16 +173,79 @@ 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; - 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; @@ -189,25 +254,32 @@ 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; } 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/grub-core/script/execute.c b/grub-core/script/execute.c index 3962b6ba5..f35e9ce76 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -34,6 +34,7 @@ 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 @@ -143,6 +144,30 @@ grub_script_setparams (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_error (GRUB_ERR_BAD_ARGUMENT, "not in function scope"); + + 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_error (GRUB_ERR_BAD_ARGUMENT, "bad argument"); + + function_return = 1; + return n ? grub_error (GRUB_ERR_TEST_FAILURE, "false") : GRUB_ERR_NONE; +} + static int grub_env_special (const char *name) { @@ -370,6 +395,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; replace_scope (old_scope); /* free any scopes by setparams */ return ret; @@ -518,8 +544,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; } @@ -528,14 +562,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 @@ -569,6 +606,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; } } @@ -584,18 +623,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/lexer.c b/grub-core/script/lexer.c index 3ab40049f..8d1623fb8 100644 --- a/grub-core/script/lexer.c +++ b/grub-core/script/lexer.c @@ -205,8 +205,6 @@ struct grub_lexer_param * grub_script_lexer_init (struct grub_parser_param *parser, char *script, grub_reader_getline_t getline) { - int len; - YY_BUFFER_STATE buffer; struct grub_lexer_param *lexerstate; lexerstate = grub_zalloc (sizeof (*lexerstate)); diff --git a/grub-core/script/main.c b/grub-core/script/main.c index 4984848f0..482bb3fef 100644 --- a/grub-core/script/main.c +++ b/grub-core/script/main.c @@ -45,6 +45,7 @@ static grub_command_t cmd_break; static grub_command_t cmd_continue; static grub_command_t cmd_shift; static grub_command_t cmd_setparams; +static grub_command_t cmd_return; void grub_script_init (void) @@ -58,6 +59,8 @@ grub_script_init (void) cmd_setparams = grub_register_command ("setparams", grub_script_setparams, N_("[VALUE]..."), N_("Set positional parameters.")); + cmd_return = grub_register_command ("return", grub_script_return, + N_("[n]"), N_("Return from a function.")); } void @@ -78,4 +81,8 @@ grub_script_fini (void) if (cmd_setparams) grub_unregister_command (cmd_setparams); cmd_setparams = 0; + + if (cmd_return) + grub_unregister_command (cmd_return); + cmd_return = 0; } 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 */ diff --git a/include/grub/script_sh.h b/include/grub/script_sh.h index f46dff51d..b3c8960f0 100644 --- a/include/grub/script_sh.h +++ b/include/grub/script_sh.h @@ -325,11 +325,8 @@ grub_err_t grub_script_shift (grub_command_t cmd, int argc, char *argv[]); /* SETPARAMS command for GRUB script functions. */ grub_err_t grub_script_setparams (grub_command_t cmd, int argc, char *argv[]); -/* Break command for loops. */ -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. */ 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