diff --git a/include/grub/script_sh.h b/include/grub/script_sh.h index 159da62..ab5bf43 100644 --- a/include/grub/script_sh.h +++ b/include/grub/script_sh.h @@ -77,11 +77,6 @@ struct grub_script_cmdline /* The arguments for this command. */ struct grub_script_arglist *arglist; - - /* The command name of this command. XXX: Perhaps an argument - should be used for this so we can use variables as command - name. */ - char *cmdname; }; /* A block of commands, this can be used to group commands. */ @@ -161,6 +156,9 @@ struct grub_lexer_param /* Size of RECORDING. */ int recordlen; + + /* The token that is already parsed but not yet returned. */ + int tokenonhold; }; /* State of the parser as passes to the parser. */ @@ -191,7 +189,6 @@ grub_script_add_arglist (struct grub_parser_param *state, struct grub_script_arg *arg); struct grub_script_cmd * grub_script_create_cmdline (struct grub_parser_param *state, - char *cmdname, struct grub_script_arglist *arglist); struct grub_script_cmd * grub_script_create_cmdblock (struct grub_parser_param *state); @@ -276,7 +273,7 @@ struct grub_script_function }; typedef struct grub_script_function *grub_script_function_t; -grub_script_function_t grub_script_function_create (char *functionname, +grub_script_function_t grub_script_function_create (struct grub_script_arg *functionname, struct grub_script *cmd); void grub_script_function_remove (const char *name); grub_script_function_t grub_script_function_find (char *functionname); @@ -284,4 +281,7 @@ 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); +char * +grub_script_execute_argument_to_string (struct grub_script_arg *arg); + #endif /* ! GRUB_NORMAL_PARSER_HEADER */ diff --git a/kern/parser.c b/kern/parser.c index 685ab22..ee6b169 100644 --- a/kern/parser.c +++ b/kern/parser.c @@ -47,8 +47,8 @@ static struct grub_parser_state_transition state_transitions[] = { GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME2, '{', 0}, { GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME, 0, 1}, - { GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_DQUOTE, ' ', 1}, { GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_TEXT, '\"', 0}, + { GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_DQUOTE, ' ', 1}, { GRUB_PARSER_STATE_QVARNAME2, GRUB_PARSER_STATE_DQUOTE, '}', 0}, { 0, 0, 0, 0} @@ -60,9 +60,7 @@ grub_parser_state_t grub_parser_cmdline_state (grub_parser_state_t state, char c, char *result) { struct grub_parser_state_transition *transition; - struct grub_parser_state_transition *next_match = 0; struct grub_parser_state_transition default_transition; - int found = 0; default_transition.to_state = state; default_transition.keep_value = 1; @@ -70,26 +68,24 @@ grub_parser_cmdline_state (grub_parser_state_t state, char c, char *result) /* Look for a good translation. */ for (transition = state_transitions; transition->from_state; transition++) { + if (transition->from_state != state) + continue; /* An exact match was found, use it. */ - if (transition->from_state == state && transition->input == c) - { - found = 1; - break; - } + if (transition->input == c) + break; + + if (transition->input == ' ' && ! grub_isalpha (c) + && ! grub_isdigit (c) && c != '_') + break; /* A less perfect match was found, use this one if no exact match can be found. */ - if (transition->from_state == state && transition->input == 0) - next_match = transition; + if (transition->input == 0) + break; } - if (! found) - { - if (next_match) - transition = next_match; - else - transition = &default_transition; - } + if (! transition->from_state) + transition = &default_transition; if (transition->keep_value) *result = c; diff --git a/script/sh/execute.c b/script/sh/execute.c index 0989f79..e0b7b2e 100644 --- a/script/sh/execute.c +++ b/script/sh/execute.c @@ -37,7 +37,7 @@ grub_script_execute_cmd (struct grub_script_cmd *cmd) /* Parse ARG and return the textual representation. Add strings are concatenated and all values of the variables are filled in. */ -static char * +char * grub_script_execute_argument_to_string (struct grub_script_arg *arg) { int size = 0; @@ -93,20 +93,22 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) int argcount = 0; grub_script_function_t func = 0; char errnobuf[6]; + char *cmdname; /* Lookup the command. */ - grubcmd = grub_command_find (cmdline->cmdname); + cmdname = grub_script_execute_argument_to_string (cmdline->arglist->arg); + grubcmd = grub_command_find (cmdname); if (! grubcmd) { /* Ignore errors. */ grub_errno = GRUB_ERR_NONE; /* It's not a GRUB command, try all functions. */ - func = grub_script_function_find (cmdline->cmdname); + func = grub_script_function_find (cmdname); if (! func) { /* As a last resort, try if it is an assignment. */ - char *assign = grub_strdup (cmdline->cmdname); + char *assign = grub_strdup (cmdname); char *eq = grub_strchr (assign, '='); if (eq) @@ -123,14 +125,15 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) return 0; } } + grub_free (cmdname); - if (cmdline->arglist) + if (cmdline->arglist->next) { - argcount = cmdline->arglist->argcount; + argcount = cmdline->arglist->argcount - 1; /* Create argv from the arguments. */ args = grub_malloc (sizeof (char *) * argcount); - for (arglist = cmdline->arglist; arglist; arglist = arglist->next) + for (arglist = cmdline->arglist->next; arglist; arglist = arglist->next) { char *str; str = grub_script_execute_argument_to_string (arglist->arg); diff --git a/script/sh/function.c b/script/sh/function.c index db6b903..dc411cc 100644 --- a/script/sh/function.c +++ b/script/sh/function.c @@ -24,7 +24,8 @@ static grub_script_function_t grub_script_function_list; grub_script_function_t -grub_script_function_create (char *functionname, struct grub_script *cmd) +grub_script_function_create (struct grub_script_arg *functionname_arg, + struct grub_script *cmd) { grub_script_function_t func; grub_script_function_t *p; @@ -33,7 +34,7 @@ grub_script_function_create (char *functionname, struct grub_script *cmd) if (! func) return 0; - func->name = grub_strdup (functionname); + func->name = grub_script_execute_argument_to_string (functionname_arg); if (! func->name) { grub_free (func); @@ -46,14 +47,14 @@ grub_script_function_create (char *functionname, struct grub_script *cmd) p = &grub_script_function_list; while (*p) { - if (grub_strcmp ((*p)->name, functionname) >= 0) + if (grub_strcmp ((*p)->name, func->name) >= 0) break; p = &((*p)->next); } /* If the function already exists, overwrite the old function. */ - if (*p && grub_strcmp ((*p)->name, functionname) == 0) + if (*p && grub_strcmp ((*p)->name, func->name) == 0) { grub_script_function_t q; diff --git a/script/sh/lexer.c b/script/sh/lexer.c index 8bae487..1dba553 100644 --- a/script/sh/lexer.c +++ b/script/sh/lexer.c @@ -62,6 +62,7 @@ grub_script_lexer_init (char *script, grub_reader_getline_t getline) param->recording = 0; param->recordpos = 0; param->recordlen = 0; + param->tokenonhold = 0; return param; } @@ -136,224 +137,247 @@ nextchar (struct grub_lexer_param *state) } int -grub_script_yylex2 (union YYSTYPE *yylval, - struct grub_parser_param *parsestate); - -int grub_script_yylex (union YYSTYPE *yylval, struct grub_parser_param *parsestate) { - int r = -1; - - while (r == -1) - { - r = grub_script_yylex2 (yylval, parsestate); - if (r == ' ') - r = -1; - } - return r; -} - -int -grub_script_yylex2 (union YYSTYPE *yylval, struct grub_parser_param *parsestate) -{ grub_parser_state_t newstate; char use; char *buffer; char *bp; struct grub_lexer_param *state = parsestate->lexerstate; + int firstrun = 1; - if (state->done) - return 0; + yylval->arg = 0; - if (! *state->script) + if (state->tokenonhold) { - /* Check if more tokens are requested by the parser. */ - if ((state->refs - || state->state == GRUB_PARSER_STATE_ESC) - && state->getline) - { - while (!state->script || ! grub_strlen (state->script)) - { - grub_free (state->newscript); - state->newscript = 0; - state->getline (&state->newscript, 1); - state->script = state->newscript; - if (! state->script) - return 0; - } - grub_dprintf ("scripting", "token=`\\n'\n"); - recordchar (state, '\n'); - if (state->state != GRUB_PARSER_STATE_ESC) - return '\n'; - } - else - { - grub_free (state->newscript); - state->newscript = 0; - state->done = 1; - grub_dprintf ("scripting", "token=`\\n'\n"); - return '\n'; - } + int token = state->tokenonhold; + state->tokenonhold = 0; + return token; } - newstate = grub_parser_cmdline_state (state->state, *state->script, &use); - - /* Check if it is a text. */ - if (check_textstate (newstate)) + for (;! state->done && (*state->script || firstrun); firstrun = 0) { - /* In case the string is not quoted, this can be a one char - length symbol. */ - if (newstate == GRUB_PARSER_STATE_TEXT) + + if (! *state->script) { - switch (*state->script) + /* Check if more tokens are requested by the parser. */ + if (((state->refs && ! parsestate->err) + || state->state == GRUB_PARSER_STATE_ESC) + && state->getline) { - case ' ': - while (*state->script) + int doexit = 0; + while (!state->script || ! grub_strlen (state->script)) { - newstate = grub_parser_cmdline_state (state->state, - *state->script, &use); - if (! (state->state == GRUB_PARSER_STATE_TEXT - && *state->script == ' ')) + grub_free (state->newscript); + state->newscript = 0; + state->getline (&state->newscript, 1); + state->script = state->newscript; + if (! state->script) { - grub_dprintf ("scripting", "token=` '\n"); - return ' '; + doexit = 1; + break; } - state->state = newstate; - nextchar (state); } - grub_dprintf ("scripting", "token=` '\n"); - return ' '; - case '{': - case '}': - case ';': - case '\n': - { - char c; - grub_dprintf ("scripting", "token=`%c'\n", *state->script); - c = *state->script;; - nextchar (state); - return c; - } + if (doexit) + break; + grub_dprintf ("scripting", "token=`\\n'\n"); + recordchar (state, '\n'); + if (state->state != GRUB_PARSER_STATE_ESC) + { + state->tokenonhold = '\n'; + break; + } + } + else + { + grub_free (state->newscript); + state->newscript = 0; + state->done = 1; + grub_dprintf ("scripting", "token=`\\n'\n"); + state->tokenonhold = '\n'; + break; } } - /* XXX: Use a better size. */ - buffer = grub_script_malloc (parsestate, 2048); - if (! buffer) - return 0; - - bp = buffer; - - /* Read one token, possible quoted. */ - while (*state->script) + newstate = grub_parser_cmdline_state (state->state, *state->script, &use); + + /* Check if it is a text. */ + if (check_textstate (newstate)) { - newstate = grub_parser_cmdline_state (state->state, - *state->script, &use); - - /* Check if a variable name starts. */ - if (check_varstate (newstate)) - break; - - /* If the string is not quoted or escaped, stop processing - when a special token was found. It will be recognized - next time when this function is called. */ - if (newstate == GRUB_PARSER_STATE_TEXT - && state->state != GRUB_PARSER_STATE_ESC) + /* In case the string is not quoted, this can be a one char + length symbol. */ + if (newstate == GRUB_PARSER_STATE_TEXT) { - int breakout = 0; - - switch (use) + int doexit = 0; + switch (*state->script) { case ' ': + while (*state->script) + { + newstate = grub_parser_cmdline_state (state->state, + *state->script, &use); + if (! (state->state == GRUB_PARSER_STATE_TEXT + && *state->script == ' ')) + { + grub_dprintf ("scripting", "token=` '\n"); + if (! firstrun) + doexit = 1; + break; + } + state->state = newstate; + nextchar (state); + } + grub_dprintf ("scripting", "token=` '\n"); + if (! firstrun) + doexit = 1; + break; case '{': case '}': case ';': case '\n': - breakout = 1; + { + char c; + grub_dprintf ("scripting", "token=`%c'\n", *state->script); + c = *state->script;; + nextchar (state); + state->tokenonhold = c; + doexit = 1; + break; + } } - if (breakout) + if (doexit) break; - *(bp++) = use; } - else if (use) - *(bp++) = use; + /* XXX: Use a better size. */ + buffer = grub_script_malloc (parsestate, 2048); + if (! buffer) + return 0; + + bp = buffer; + + /* Read one token, possible quoted. */ + while (*state->script) + { + newstate = grub_parser_cmdline_state (state->state, + *state->script, &use); + + /* Check if a variable name starts. */ + if (check_varstate (newstate)) + break; + + /* If the string is not quoted or escaped, stop processing + when a special token was found. It will be recognized + next time when this function is called. */ + if (newstate == GRUB_PARSER_STATE_TEXT + && state->state != GRUB_PARSER_STATE_ESC) + { + int breakout = 0; + + switch (use) + { + case ' ': + case '{': + case '}': + case ';': + case '\n': + breakout = 1; + } + if (breakout) + break; + *(bp++) = use; + } + else if (use) + *(bp++) = use; + + state->state = newstate; + nextchar (state); + } + + /* A string of text was read in. */ + *bp = '\0'; + grub_dprintf ("scripting", "token=`%s'\n", buffer); + yylval->arg = grub_script_arg_add (parsestate, yylval->arg, + GRUB_SCRIPT_ARG_TYPE_STR, buffer); + + } + else if (newstate == GRUB_PARSER_STATE_VAR + || newstate == GRUB_PARSER_STATE_QVAR) + { + /* XXX: Use a better size. */ + buffer = grub_script_malloc (parsestate, 2096); + if (! buffer) + return 0; + + bp = buffer; + + /* This is a variable, read the variable name. */ + while (*state->script) + { + newstate = grub_parser_cmdline_state (state->state, + *state->script, &use); + + /* Check if this character is not part of the variable name + anymore. */ + if (! (check_varstate (newstate))) + { + if (state->state == GRUB_PARSER_STATE_VARNAME2 + || state->state == GRUB_PARSER_STATE_QVARNAME2) + nextchar (state); + state->state = newstate; + break; + } + + if (use) + *(bp++) = use; + nextchar (state); + state->state = newstate; + } + + *bp = '\0'; state->state = newstate; - nextchar (state); + yylval->arg = grub_script_arg_add (parsestate, yylval->arg, + GRUB_SCRIPT_ARG_TYPE_VAR, buffer); + grub_dprintf ("scripting", "vartoken=`%s'\n", buffer); } + else + { + /* There is either text or a variable name. In the case you + arrive here there is a serious problem with the lexer. */ + grub_error (GRUB_ERR_BAD_ARGUMENT, "Internal error\n"); + return 0; + } + } + + if (yylval->arg == 0) + { + int token = state->tokenonhold; + state->tokenonhold = 0; + return token; + } - /* A string of text was read in. */ - *bp = '\0'; - grub_dprintf ("scripting", "token=`%s'\n", buffer); - yylval->string = buffer; - + if (yylval->arg->next == 0 && yylval->arg->type == GRUB_SCRIPT_ARG_TYPE_STR) + { /* Detect some special tokens. */ - if (! grub_strcmp (buffer, "while")) + if (! grub_strcmp (yylval->arg->str, "while")) return GRUB_PARSER_TOKEN_WHILE; - else if (! grub_strcmp (buffer, "if")) + else if (! grub_strcmp (yylval->arg->str, "if")) return GRUB_PARSER_TOKEN_IF; - else if (! grub_strcmp (buffer, "function")) + else if (! grub_strcmp (yylval->arg->str, "function")) return GRUB_PARSER_TOKEN_FUNCTION; - else if (! grub_strcmp (buffer, "menuentry")) + else if (! grub_strcmp (yylval->arg->str, "menuentry")) return GRUB_PARSER_TOKEN_MENUENTRY; - else if (! grub_strcmp (buffer, "@")) + else if (! grub_strcmp (yylval->arg->str, "@")) return GRUB_PARSER_TOKEN_MENUENTRY; - else if (! grub_strcmp (buffer, "else")) + else if (! grub_strcmp (yylval->arg->str, "else")) return GRUB_PARSER_TOKEN_ELSE; - else if (! grub_strcmp (buffer, "then")) + else if (! grub_strcmp (yylval->arg->str, "then")) return GRUB_PARSER_TOKEN_THEN; - else if (! grub_strcmp (buffer, "fi")) + else if (! grub_strcmp (yylval->arg->str, "fi")) return GRUB_PARSER_TOKEN_FI; - else - return GRUB_PARSER_TOKEN_NAME; } - else if (newstate == GRUB_PARSER_STATE_VAR - || newstate == GRUB_PARSER_STATE_QVAR) - { - /* XXX: Use a better size. */ - buffer = grub_script_malloc (parsestate, 2096); - if (! buffer) - return 0; - - bp = buffer; - - /* This is a variable, read the variable name. */ - while (*state->script) - { - newstate = grub_parser_cmdline_state (state->state, - *state->script, &use); - - /* Check if this character is not part of the variable name - anymore. */ - if (! (check_varstate (newstate))) - { - if (state->state == GRUB_PARSER_STATE_VARNAME2 - || state->state == GRUB_PARSER_STATE_QVARNAME2) - nextchar (state); - state->state = newstate; - break; - } - - if (use) - *(bp++) = use; - nextchar (state); - state->state = newstate; - } - *bp = '\0'; - state->state = newstate; - yylval->string = buffer; - grub_dprintf ("scripting", "vartoken=`%s'\n", buffer); - - return GRUB_PARSER_TOKEN_VAR; - } - else - { - /* There is either text or a variable name. In the case you - arrive here there is a serious problem with the lexer. */ - grub_error (GRUB_ERR_BAD_ARGUMENT, "Internal error\n"); - return 0; - } + return GRUB_PARSER_TOKEN_ARG; } void diff --git a/script/sh/parser.y b/script/sh/parser.y index 366f6b0..094a885 100644 --- a/script/sh/parser.y +++ b/script/sh/parser.y @@ -42,13 +42,10 @@ %token GRUB_PARSER_TOKEN_ELSE "else" %token GRUB_PARSER_TOKEN_THEN "then" %token GRUB_PARSER_TOKEN_FI "fi" -%token GRUB_PARSER_TOKEN_NAME -%token GRUB_PARSER_TOKEN_VAR +%token GRUB_PARSER_TOKEN_ARG %type script_init script grubcmd command commands commandblock menuentry if %type arguments; -%type argument; -%type "if" "while" "function" "else" "then" "fi" -%type text GRUB_PARSER_TOKEN_NAME GRUB_PARSER_TOKEN_VAR +%type GRUB_PARSER_TOKEN_ARG; %pure-parser %lex-param { struct grub_parser_param *state }; @@ -62,9 +59,18 @@ script_init: { state->err = 0; } script } ; -script: commands { $$ = $1; } +script: { $$ = 0; } + | '\n' { $$ = 0; } + | commands { $$ = $1; } | function '\n' { $$ = 0; } | menuentry '\n' { $$ = $1; } + | error + { + $$ = 0; + yyerror (state, "Incorrect command"); + state->err = 1; + yyerrok; + } ; delimiter: '\n' @@ -76,61 +82,21 @@ newlines: /* Empty */ | newlines '\n' ; -/* Some tokens are both used as token or as plain text. XXX: Add all - tokens without causing conflicts. */ -text: GRUB_PARSER_TOKEN_NAME - { - $$ = $1; - } - | "if" - { - $$ = $1; - } - | "while" - { - $$ = $1; - } -; -/* An argument can consist of some static text mixed with variables, - for example: `foo${bar}baz'. */ -argument: GRUB_PARSER_TOKEN_VAR - { - $$ = grub_script_arg_add (state, 0, GRUB_SCRIPT_ARG_TYPE_VAR, $1); - } - | text - { - $$ = grub_script_arg_add (state, 0, GRUB_SCRIPT_ARG_TYPE_STR, $1); - } -/* XXX: Currently disabled to simplify the parser. This should be - parsed by yet another parser for readability. */ -/* | argument GRUB_PARSER_TOKEN_VAR */ -/* { */ -/* $$ = grub_script_arg_add ($1, GRUB_SCRIPT_ARG_TYPE_VAR, $2); */ -/* } */ -/* | argument text */ -/* { */ -/* $$ = grub_script_arg_add ($1, GRUB_SCRIPT_ARG_TYPE_STR, $2); */ -/* } */ -; -arguments: argument +arguments: GRUB_PARSER_TOKEN_ARG { $$ = grub_script_add_arglist (state, 0, $1); } - | arguments argument + | arguments GRUB_PARSER_TOKEN_ARG { $$ = grub_script_add_arglist (state, $1, $2); } ; -grubcmd: GRUB_PARSER_TOKEN_NAME arguments +grubcmd: arguments { - $$ = grub_script_create_cmdline (state, $1, $2); - } - | GRUB_PARSER_TOKEN_NAME - { - $$ = grub_script_create_cmdline (state, $1, 0); + $$ = grub_script_create_cmdline (state, $1); } ; @@ -138,13 +104,6 @@ grubcmd: GRUB_PARSER_TOKEN_NAME arguments command: grubcmd delimiter { $$ = $1; } | if delimiter { $$ = $1; } | commandblock delimiter { $$ = $1; } - | error delimiter - { - $$ = 0; - yyerror (state, "Incorrect command"); - state->err = 1; - yyerrok; - } ; /* A block of commands. */ @@ -166,7 +125,7 @@ commands: command executed on the right moment. So the `commands' rule should be recognized after executing the `grub_script_mem_record; and before `grub_script_mem_record_stop'. */ -function: "function" GRUB_PARSER_TOKEN_NAME +function: "function" GRUB_PARSER_TOKEN_ARG { grub_script_lexer_ref (state->lexerstate); } newlines '{' @@ -204,10 +163,10 @@ commandblock: '{' ; /* A menu entry. Carefully save the memory that is allocated. */ -menuentry: "menuentry" arguments +menuentry: "menuentry" { grub_script_lexer_ref (state->lexerstate); - } newlines '{' + } arguments newlines '{' { grub_script_lexer_record_start (state->lexerstate); } newlines commands '}' @@ -215,7 +174,7 @@ menuentry: "menuentry" arguments char *menu_entry; menu_entry = grub_script_lexer_record_stop (state->lexerstate); grub_script_lexer_deref (state->lexerstate); - $$ = grub_script_create_cmdmenu (state, $2, menu_entry, 0); + $$ = grub_script_create_cmdmenu (state, $3, menu_entry, 0); } ; diff --git a/script/sh/script.c b/script/sh/script.c index 3e80b83..89fa947 100644 --- a/script/sh/script.c +++ b/script/sh/script.c @@ -161,7 +161,7 @@ grub_script_add_arglist (struct grub_parser_param *state, holds all arguments for this command. */ struct grub_script_cmd * grub_script_create_cmdline (struct grub_parser_param *state, - char *cmdname, struct grub_script_arglist *arglist) + struct grub_script_arglist *arglist) { struct grub_script_cmdline *cmd; @@ -171,7 +171,6 @@ grub_script_create_cmdline (struct grub_parser_param *state, cmd->cmd.exec = grub_script_execute_cmdline; cmd->cmd.next = 0; cmd->arglist = arglist; - cmd->cmdname = cmdname; return (struct grub_script_cmd *) cmd; }