=== modified file 'ChangeLog.scripting' --- ChangeLog.scripting 2009-12-25 18:16:20 +0000 +++ ChangeLog.scripting 2009-12-25 12:13:34 +0000 @@ -1,5 +1,25 @@ 2009-12-25 BVK Chaitanya + * include/grub/script_sh.h (grub_script_arg_type_t): New types + added. + (grub_script_execute_arglist_to_arg): Function proptotype added. + (grub_script_execute_argument_to_string): Prototype is removed. + * script/execute.c (grub_script_execute_arglist_to_arg): New + function to convert arglist to argv. + (grub_script_execute_cmdfor): Updated to make use of arglist to + argv conversion function. + (grub_script_execute_cmdline): Likewise. + (grub_script_execute_menuentry): Likewise. + (grub_script_execute_argument_to_string): Removed. + * script/function.c (grub_script_function_create): Removed + unnecessary reference to removed function + grub_script_execute_argument_to_string. + * script/lexer.c (grub_script_yylex): Fixed grub_script_arg + argument construction. + * script/yylex.l: Likewise. + +2009-12-25 BVK Chaitanya + * conf/tests.rmk: Makerules for new unit tests. * include/grub/script_sh.h: Function prototypes for for statement functions. === modified file 'include/grub/script_sh.h' --- include/grub/script_sh.h 2009-12-25 18:16:20 +0000 +++ include/grub/script_sh.h 2009-12-25 12:13:34 +0000 @@ -45,8 +45,11 @@ typedef enum { - GRUB_SCRIPT_ARG_TYPE_STR, - GRUB_SCRIPT_ARG_TYPE_VAR + GRUB_SCRIPT_ARG_TYPE_VAR, + GRUB_SCRIPT_ARG_TYPE_TEXT, + GRUB_SCRIPT_ARG_TYPE_DQVAR, + GRUB_SCRIPT_ARG_TYPE_DQSTR, + GRUB_SCRIPT_ARG_TYPE_SQSTR } grub_script_arg_type_t; /* A part of an argument. */ @@ -172,6 +175,9 @@ /* Text of current token. */ char *text; + /* Type of text. */ + grub_script_arg_type_t type; + /* Flex scanner. */ void *yyscanner; @@ -316,7 +322,7 @@ int grub_script_function_call (grub_script_function_t func, int argc, char **args); -char * -grub_script_execute_argument_to_string (struct grub_script_arg *arg); +char ** +grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist); #endif /* ! GRUB_NORMAL_PARSER_HEADER */ === modified file 'script/execute.c' --- script/execute.c 2009-12-25 18:16:20 +0000 +++ script/execute.c 2009-12-25 12:13:34 +0000 @@ -35,49 +35,132 @@ return cmd->exec (cmd); } -/* Parse ARG and return the textual representation. Add strings are - concatenated and all values of the variables are filled in. */ -char * -grub_script_execute_argument_to_string (struct grub_script_arg *arg) +#define ROUND_UPTO(sz,up) (((sz) + (up) - 1) / (up) * (up)) + +/* Expand arguments in ARGLIST into multiple arguments. */ +char ** +grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist) { - int size = 0; - char *val; - char *chararg; - struct grub_script_arg *argi; - - /* First determine the size of the argument. */ - for (argi = arg; argi; argi = argi->next) - { - if (argi->type == 1) - { - val = grub_env_get (argi->str); - if (val) - size += grub_strlen (val); - } - else - size += grub_strlen (argi->str); - } - - /* Create the argument. */ - chararg = grub_malloc (size + 1); - if (! chararg) - return 0; - - *chararg = '\0'; - /* First determine the size of the argument. */ - for (argi = arg; argi; argi = argi->next) - { - if (argi->type == 1) - { - val = grub_env_get (argi->str); - if (val) - grub_strcat (chararg, val); - } - else - grub_strcat (chararg, argi->str); - } - - return chararg; + int i; + int oom; + int argc; + char *ptr; + char **argv; + char *value; + struct grub_script_arg *arg; + + auto void push (char *str); + void push (char *str) + { + char **ptr; + + if (oom) + return; + + ptr = grub_realloc (argv, ROUND_UPTO (sizeof(char*) * (argc + 1), 32)); + if (!ptr) + oom = 1; + else + { + ptr[argc++] = str; + argv = ptr; + } + } + + auto char* append (const char *str, grub_size_t nchar); + char* append (const char *str, grub_size_t nchar) + { + int len; + int old; + char *ptr; + + if (oom || !str) + return 0; + + len = nchar ?: grub_strlen (str); + old = argv[argc - 1] ? grub_strlen (argv[argc - 1]) : 0; + ptr = grub_realloc (argv[argc - 1], ROUND_UPTO(old + len + 1, 32)); + + if (ptr) + { + grub_strncpy (ptr + old, str, len); + ptr[old + len] = '\0'; + } + else + { + oom = 1; + grub_free (argv[argc - 1]); + } + argv[argc - 1] = ptr; + return argv[argc - 1]; + } + + /* Move *STR to the begining of next word, but return current word. */ + char* move_to_next (char **str) + { + char *end; + char *start; + + if (oom || !str || !*str) + return 0; + + start = *str; + while (*start && grub_isspace (*start)) start++; + if (*start == '\0') + return 0; + + end = start + 1; + while (*end && !grub_isspace (*end)) end++; + + *str = end; + return start; + } + + oom = 0; + argv = 0; + argc = 0; + for (; arglist; arglist = arglist->next) + { + push (0); + arg = arglist->arg; + + while (arg) + { + switch (arg->type) + { + case GRUB_SCRIPT_ARG_TYPE_VAR: + value = grub_env_get (arg->str); + while (*value && (ptr = move_to_next(&value))) + { + append (ptr, value - ptr); + if (*value) push(0); + } + break; + + case GRUB_SCRIPT_ARG_TYPE_TEXT: + case GRUB_SCRIPT_ARG_TYPE_DQSTR: + case GRUB_SCRIPT_ARG_TYPE_SQSTR: + append (arg->str, 0); + break; + + case GRUB_SCRIPT_ARG_TYPE_DQVAR: + append (grub_env_get (arg->str), 0); + break; + } + arg = arg->next; + } + } + push (0); /* Ensure argv[argc] == 0. */ + + if (oom) + { + for (i = 0; i < argc; i++) + grub_free (argv[i]); + grub_free (argv); + argv = 0; + } + + return argv; } /* Execute a single command line. */ @@ -85,7 +168,6 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) { struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd; - struct grub_script_arglist *arglist; char **args = 0; int i = 0; grub_command_t grubcmd; @@ -96,7 +178,11 @@ char *cmdname; /* Lookup the command. */ - cmdname = grub_script_execute_argument_to_string (cmdline->arglist->arg); + args = grub_script_execute_arglist_to_argv (cmdline->arglist); + if (!args) + return grub_errno; + + cmdname = args[0]; grubcmd = grub_command_find (cmdname); if (! grubcmd) { @@ -129,27 +215,15 @@ return 0; } } - grub_free (cmdname); - - if (cmdline->arglist->next) - { - argcount = cmdline->arglist->argcount - 1; - - /* Create argv from the arguments. */ - args = grub_malloc (sizeof (char *) * argcount); - for (arglist = cmdline->arglist->next; arglist; arglist = arglist->next) - { - char *str; - str = grub_script_execute_argument_to_string (arglist->arg); - args[i++] = str; - } - } + + /* Count argv size. */ + for (argcount = 0; args[argcount]; argcount++); /* Execute the GRUB command or function. */ if (grubcmd) - ret = (grubcmd->func) (grubcmd, argcount, args); + ret = (grubcmd->func) (grubcmd, argcount - 1, args + 1); else - ret = grub_script_function_call (func, argcount, args); + ret = grub_script_function_call (func, argcount - 1, args + 1); /* Free arguments. */ for (i = 0; i < argcount; i++) @@ -201,50 +275,24 @@ grub_err_t grub_script_execute_cmdfor (struct grub_script_cmd *cmd) { - char *str; - char *end; - char *start; - int last; + int i; int result; - struct grub_script_arglist *word; + char **args; struct grub_script_cmdfor *cmdfor = (struct grub_script_cmdfor *) cmd; + args = grub_script_execute_arglist_to_argv (cmdfor->words); + if (!args) + return grub_errno; + result = 0; - word = cmdfor->words; - while (word) + for (i = 0; args[i]; i++) { - str = grub_script_execute_argument_to_string (word->arg); - if (!str) - return 1; - - last = 0; - start = str; - while (!last && *start) - { - /* Skip whitespaces. */ - while (*start && grub_isspace (*start)) - start++; - - if (*start == '\0') - break; - - end = start + 1; - while (*end && !grub_isspace (*end)) - end++; - if (*end == '\0') - last = 1; - else - *end = '\0'; - - grub_env_set (cmdfor->name->str, start); - result = grub_script_execute_cmd (cmdfor->list); - start = end + 1; - } - - grub_free (str); - word = word->next; + grub_env_set (cmdfor->name->str, args[i]); + result = grub_script_execute_cmd (cmdfor->list); + grub_free (args[i]); } + grub_free (args); return result; } @@ -253,7 +301,6 @@ grub_script_execute_menuentry (struct grub_script_cmd *cmd) { struct grub_script_cmd_menuentry *cmd_menuentry; - struct grub_script_arglist *arglist; char **args = 0; int argcount = 0; int i = 0; @@ -262,22 +309,11 @@ if (cmd_menuentry->arglist) { - argcount = cmd_menuentry->arglist->argcount; - - /* Create argv from the arguments. */ - args = grub_malloc (sizeof (char *) * argcount); - - if (! args) - { - return grub_errno; - } - - for (arglist = cmd_menuentry->arglist; arglist; arglist = arglist->next) - { - char *str; - str = grub_script_execute_argument_to_string (arglist->arg); - args[i++] = str; - } + args = grub_script_execute_arglist_to_argv (cmd_menuentry->arglist); + if (!args) + return grub_errno; + + for (argcount = 0; args[argcount]; argcount++); } grub_normal_add_menu_entry (argcount, (const char **) args, === modified file 'script/function.c' --- script/function.c 2009-11-23 15:37:33 +0000 +++ script/function.c 2009-12-25 12:13:34 +0000 @@ -34,7 +34,7 @@ if (! func) return 0; - func->name = grub_script_execute_argument_to_string (functionname_arg); + func->name = grub_strdup (functionname_arg->str); if (! func->name) { grub_free (func); === modified file 'script/lexer.c' --- script/lexer.c 2009-12-20 07:30:02 +0000 +++ script/lexer.c 2009-12-25 12:13:34 +0000 @@ -302,13 +302,12 @@ { lexerstate->text[lexerstate->size] = '\0'; str = lexerstate->text; - type = (token == GRUB_PARSER_TOKEN_NAME ? - GRUB_SCRIPT_ARG_TYPE_VAR : GRUB_SCRIPT_ARG_TYPE_STR); + type = lexerstate->type; } else { str = yyget_text (lexerstate->yyscanner); - type = GRUB_SCRIPT_ARG_TYPE_STR; + type = GRUB_SCRIPT_ARG_TYPE_TEXT; } /* grub_printf ("tok %u, txt [%s] size %u\n", token, str, lexerstate->size); */ === modified file 'script/yylex.l' --- script/yylex.l 2009-12-20 07:00:31 +0000 +++ script/yylex.l 2009-12-25 12:13:34 +0000 @@ -11,8 +11,8 @@ #define yyrealloc grub_lexer_yyrealloc /* - * XXX As we don't have access to yyscanner, we cannot do much except - * to print the fatal error. + * As we don't have access to yyscanner, we cannot do much except to + * print the fatal error. */ #define YY_FATAL_ERROR(msg) \ do { \ @@ -42,6 +42,12 @@ grub_script_lexer_record (yyextra, yytext); \ } while (0) +#define ARG(t) \ + do { \ + yyextra->lexerstate->type = t; \ + return GRUB_PARSER_TOKEN_WORD; \ + } while (0) + /* We don't need YY_INPUT, as we rely on yy_scan_strings */ #define YY_INPUT(buf,res,max) do { res = 0; } while (0) @@ -197,15 +203,22 @@ { \\. { PUSH (yytext[1]); } - \" { yy_push_state (DQUOTE, yyscanner); } - \' { yy_push_state (SQUOTE, yyscanner); } + \" { + yy_push_state (DQUOTE, yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); + } + \' { + yy_push_state (SQUOTE, yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); + } \$ { yy_push_state (VAR, yyscanner); - return GRUB_PARSER_TOKEN_WORD; + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); } {CHAR} { PUSH (yytext[0]); } .|\n { - grub_script_yyerror (yyextra, "unrecognized token"); + /* This cannot happen. */ + grub_script_yyerror (yyextra, "internal error: unexpected characters in a word"); return GRUB_PARSER_TOKEN_BAD; } @@ -213,7 +226,7 @@ yy_pop_state (yyscanner); yypop_buffer_state (yyscanner); yyextra->lexerstate->merge_end = 1; - return GRUB_PARSER_TOKEN_WORD; + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); } } @@ -221,27 +234,39 @@ {NAME} { COPY (yytext); yy_pop_state (yyscanner); - return GRUB_PARSER_TOKEN_NAME; + if (YY_START == SPLIT) + ARG (GRUB_SCRIPT_ARG_TYPE_VAR); + else + ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR); } \{{NAME}\} { yytext[yyleng - 1] = '\0'; COPY (yytext + 1); yy_pop_state (yyscanner); - return GRUB_PARSER_TOKEN_NAME; + if (YY_START == SPLIT) + ARG (GRUB_SCRIPT_ARG_TYPE_VAR); + else + ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR); } .|\n { return GRUB_PARSER_TOKEN_BAD; } } { - \' { yy_pop_state (yyscanner); } + \' { + yy_pop_state (yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_SQSTR); + } (.|\n) { PUSH (yytext[0]); } } { - \" { yy_pop_state (yyscanner); } + \" { + yy_pop_state (yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR); + } \$ { yy_push_state (VAR, yyscanner); - return GRUB_PARSER_TOKEN_WORD; + ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR); } \\\\ { PUSH ('\\'); } \\\" { PUSH ('\"'); }