diff --git a/ChangeLog b/ChangeLog index 1db00bc4d..070461b5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2010-04-09 BVK Chaitanya <bvk.groups@gmail.com> + + While and until loops support to GRUB script. + + * include/grub/script_sh.h (grub_script_cmdwhile): New struct. + (grub_script_create_cmdwhile): New function prototype. + (grub_script_execute_cmdwhile): New function prototype. + * script/execute.c (grub_script_execute_cmdwhile): New function. + * script/parser.y (command): New commands. + (whilecmd): New grammar rule. + (untilcmd): New grammar rule. + * script/script.c (grub_script_create_cmdwhile): New function. + * util/grub-script-check.c (grub_script_execute_cmdwhile): New + function. + + * tests/grub_script_while1.in: New testcase. + * conf/tests.rmk: Rule for new testcase. + 2010-04-09 Vladimir Serbinenko <phcoder@gmail.com> * util/grub.d/00_header.in: Add few missing quotes. Recognise *.jpeg diff --git a/conf/tests.rmk b/conf/tests.rmk index ca1f0cd71..5f78ea940 100644 --- a/conf/tests.rmk +++ b/conf/tests.rmk @@ -53,6 +53,9 @@ grub_script_vars1_SOURCES = tests/grub_script_vars1.in check_SCRIPTS += grub_script_for1 grub_script_for1_SOURCES = tests/grub_script_for1.in +check_SCRIPTS += grub_script_while1 +grub_script_while1_SOURCES = tests/grub_script_while1.in + check_SCRIPTS += grub_script_blanklines grub_script_blanklines_SOURCES = tests/grub_script_blanklines.in @@ -69,6 +72,7 @@ SCRIPTED_TESTS = grub_script_echo1 SCRIPTED_TESTS += grub_script_echo_keywords SCRIPTED_TESTS += grub_script_vars1 SCRIPTED_TESTS += grub_script_for1 +SCRIPTED_TESTS += grub_script_while1 SCRIPTED_TESTS += grub_script_blanklines SCRIPTED_TESTS += grub_script_final_semicolon diff --git a/include/grub/script_sh.h b/include/grub/script_sh.h index 33c6d2b85..b55b6a806 100644 --- a/include/grub/script_sh.h +++ b/include/grub/script_sh.h @@ -121,6 +121,21 @@ struct grub_script_cmdfor struct grub_script_cmd *list; }; +/* A while/until command. */ +struct grub_script_cmdwhile +{ + struct grub_script_cmd cmd; + + /* The command list used as condition. */ + struct grub_script_cmd *cond; + + /* The command list executed in each loop. */ + struct grub_script_cmd *list; + + /* The flag to indicate this as "until" loop. */ + int until; +}; + /* A menu entry generate statement. */ struct grub_script_cmd_menuentry { @@ -234,6 +249,12 @@ grub_script_create_cmdfor (struct grub_parser_param *state, struct grub_script_arglist *words, struct grub_script_cmd *list); +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_cmd * grub_script_create_cmdmenu (struct grub_parser_param *state, struct grub_script_arglist *arglist, @@ -283,6 +304,7 @@ grub_err_t grub_script_execute_cmdline (struct grub_script_cmd *cmd); grub_err_t grub_script_execute_cmdblock (struct grub_script_cmd *cmd); grub_err_t grub_script_execute_cmdif (struct grub_script_cmd *cmd); grub_err_t grub_script_execute_cmdfor (struct grub_script_cmd *cmd); +grub_err_t grub_script_execute_cmdwhile (struct grub_script_cmd *cmd); grub_err_t grub_script_execute_menuentry (struct grub_script_cmd *cmd); /* Execute any GRUB pre-parsed command or script. */ diff --git a/script/execute.c b/script/execute.c index 6cd15e87d..40f161267 100644 --- a/script/execute.c +++ b/script/execute.c @@ -26,13 +26,24 @@ #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 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; - return cmd->exec (cmd); + ret = cmd->exec (cmd); + + grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret); + grub_env_set ("?", errnobuf); + return ret; } #define ARG_ALLOCATION_UNIT (32 * sizeof (char)) @@ -260,13 +271,14 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) grub_err_t grub_script_execute_cmdblock (struct grub_script_cmd *cmd) { + int ret = 0; struct grub_script_cmdblock *cmdblock = (struct grub_script_cmdblock *) cmd; /* Loop over every command and execute it. */ for (cmd = cmdblock->cmdlist; cmd; cmd = cmd->next) - grub_script_execute_cmd (cmd); + ret = grub_script_execute_cmd (cmd); - return 0; + return ret; } /* Execute an if statement. */ @@ -317,6 +329,26 @@ grub_script_execute_cmdfor (struct grub_script_cmd *cmd) 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; + + result = 0; + do { + cond = grub_script_execute_cmd (cmdwhile->cond); + if (cmdwhile->until ? !cond : cond) + break; + + result = grub_script_execute_cmd (cmdwhile->list); + } while (1); /* XXX Put a check for ^C here */ + + return result; +} + /* Execute the menu entry generate statement. */ grub_err_t grub_script_execute_menuentry (struct grub_script_cmd *cmd) diff --git a/script/parser.y b/script/parser.y index fdd3f7589..a30dd78b3 100644 --- a/script/parser.y +++ b/script/parser.y @@ -74,8 +74,9 @@ %token <arg> GRUB_PARSER_TOKEN_WORD "word" %type <arglist> word argument arguments0 arguments1 -%type <cmd> script_init script grubcmd ifcmd forcmd command -%type <cmd> commands1 menuentry statement +%type <cmd> script_init script +%type <cmd> grubcmd ifcmd forcmd whilecmd untilcmd +%type <cmd> command commands1 menuentry statement %pure-parser %lex-param { struct grub_parser_param *state }; @@ -171,9 +172,11 @@ grubcmd: word arguments0 ; /* A single command. */ -command: grubcmd { $$ = $1; } - | ifcmd { $$ = $1; } - | forcmd { $$ = $1; } +command: grubcmd { $$ = $1; } + | ifcmd { $$ = $1; } + | forcmd { $$ = $1; } + | whilecmd { $$ = $1; } + | untilcmd { $$ = $1; } ; /* A list of commands. */ @@ -248,3 +251,25 @@ forcmd: "for" "name" 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); + } +; diff --git a/script/script.c b/script/script.c index 9142a8245..4c87d9491 100644 --- a/script/script.c +++ b/script/script.c @@ -245,6 +245,28 @@ grub_script_create_cmdfor (struct grub_parser_param *state, 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. diff --git a/tests/grub_script_while1.in b/tests/grub_script_while1.in new file mode 100644 index 000000000..554247f76 --- /dev/null +++ b/tests/grub_script_while1.in @@ -0,0 +1,32 @@ +#! @builddir@/grub-shell-tester + +echo one +foo="" +while test "$foo" != "1111"; do foo="${foo}1"; echo "$foo"; done + +echo two +foo="" +while test "$foo" != "aaaa" +do + foo="${foo}a" + echo $foo +done + +foo="" +until test "$foo" = "1111"; do foo="${foo}1"; echo $foo; done +foo="" +until test "$foo" = "aaaa" +do + foo="${foo}a" + echo $foo +done + +# check "$?" in condition gets its value from while body commands +foo="" +false +while test "$?" != "0" +do + echo $foo + foo="${foo}1" + test "$foo" = "111111" +done diff --git a/util/grub-script-check.c b/util/grub-script-check.c index 7dc400dde..af39dcdc0 100644 --- a/util/grub-script-check.c +++ b/util/grub-script-check.c @@ -87,6 +87,12 @@ grub_script_execute_cmdfor (struct grub_script_cmd *cmd __attribute__ ((unused)) return 0; } +grub_err_t +grub_script_execute_cmdwhile (struct grub_script_cmd *cmd __attribute__ ((unused))) +{ + return 0; +} + grub_err_t grub_script_execute_menuentry (struct grub_script_cmd *cmd __attribute__ ((unused))) {