123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- /* parser.y - The scripting parser. */
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2005,2006,2007,2008,2009,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 <http://www.gnu.org/licenses/>.
- */
- %{
- #include <grub/script_sh.h>
- #include <grub/mm.h>
- #include <grub/misc.h>
- #include <grub/i18n.h>
- #define YYFREE grub_free
- #define YYMALLOC grub_malloc
- #define YYLTYPE_IS_TRIVIAL 0
- #define YYENABLE_NLS 0
- #include "grub_script.tab.h"
- #pragma GCC diagnostic ignored "-Wmissing-declarations"
- %}
- %union {
- struct grub_script_cmd *cmd;
- struct grub_script_arglist *arglist;
- struct grub_script_arg *arg;
- char *string;
- struct {
- unsigned offset;
- struct grub_script_mem *memory;
- struct grub_script *scripts;
- };
- }
- %token GRUB_PARSER_TOKEN_BAD
- %token GRUB_PARSER_TOKEN_EOF 0 "end-of-input"
- %token GRUB_PARSER_TOKEN_NEWLINE "\n"
- %token GRUB_PARSER_TOKEN_AND "&&"
- %token GRUB_PARSER_TOKEN_OR "||"
- %token GRUB_PARSER_TOKEN_SEMI2 ";;"
- %token GRUB_PARSER_TOKEN_PIPE "|"
- %token GRUB_PARSER_TOKEN_AMP "&"
- %token GRUB_PARSER_TOKEN_SEMI ";"
- %token GRUB_PARSER_TOKEN_LBR "{"
- %token GRUB_PARSER_TOKEN_RBR "}"
- %token GRUB_PARSER_TOKEN_NOT "!"
- %token GRUB_PARSER_TOKEN_LSQBR2 "["
- %token GRUB_PARSER_TOKEN_RSQBR2 "]"
- %token GRUB_PARSER_TOKEN_LT "<"
- %token GRUB_PARSER_TOKEN_GT ">"
- %token <arg> GRUB_PARSER_TOKEN_CASE "case"
- %token <arg> GRUB_PARSER_TOKEN_DO "do"
- %token <arg> GRUB_PARSER_TOKEN_DONE "done"
- %token <arg> GRUB_PARSER_TOKEN_ELIF "elif"
- %token <arg> GRUB_PARSER_TOKEN_ELSE "else"
- %token <arg> GRUB_PARSER_TOKEN_ESAC "esac"
- %token <arg> GRUB_PARSER_TOKEN_FI "fi"
- %token <arg> GRUB_PARSER_TOKEN_FOR "for"
- %token <arg> GRUB_PARSER_TOKEN_IF "if"
- %token <arg> GRUB_PARSER_TOKEN_IN "in"
- %token <arg> GRUB_PARSER_TOKEN_SELECT "select"
- %token <arg> GRUB_PARSER_TOKEN_THEN "then"
- %token <arg> GRUB_PARSER_TOKEN_UNTIL "until"
- %token <arg> GRUB_PARSER_TOKEN_WHILE "while"
- %token <arg> GRUB_PARSER_TOKEN_FUNCTION "function"
- %token <arg> GRUB_PARSER_TOKEN_NAME "name"
- %token <arg> GRUB_PARSER_TOKEN_WORD "word"
- %type <arg> block block0
- %type <arglist> word argument arguments0 arguments1
- %type <cmd> script_init script
- %type <cmd> grubcmd ifclause ifcmd forcmd whilecmd untilcmd
- %type <cmd> command commands1 statement
- %pure-parser
- %lex-param { struct grub_parser_param *state };
- %parse-param { struct grub_parser_param *state };
- %start script_init
- %%
- /* It should be possible to do this in a clean way... */
- script_init: { state->err = 0; } script { state->parsed = $2; state->err = 0; }
- ;
- script: newlines0
- {
- $$ = 0;
- }
- | script statement delimiter newlines0
- {
- $$ = grub_script_append_cmd (state, $1, $2);
- }
- | error
- {
- $$ = 0;
- yyerror (state, N_("Incorrect command"));
- yyerrok;
- }
- ;
- newlines0: /* Empty */ | newlines1 ;
- newlines1: newlines0 "\n" ;
- delimiter: ";"
- | "\n"
- ;
- delimiters0: /* Empty */ | delimiters1 ;
- delimiters1: delimiter
- | delimiters1 "\n"
- ;
- word: GRUB_PARSER_TOKEN_NAME { $$ = grub_script_add_arglist (state, 0, $1); }
- | GRUB_PARSER_TOKEN_WORD { $$ = grub_script_add_arglist (state, 0, $1); }
- ;
- statement: command { $$ = $1; }
- | function { $$ = 0; }
- ;
- argument : "case" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "do" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "done" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "elif" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "else" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "esac" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "fi" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "for" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "if" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "in" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "select" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "then" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "until" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "while" { $$ = grub_script_add_arglist (state, 0, $1); }
- | "function" { $$ = grub_script_add_arglist (state, 0, $1); }
- | word { $$ = $1; }
- ;
- /*
- Block parameter is passed to commands in two forms: as unparsed
- string and as pre-parsed grub_script object. Passing as grub_script
- object makes memory management difficult, because:
- (1) Command may want to keep a reference to grub_script objects for
- later use, so script framework may not free the grub_script
- object after command completes.
- (2) Command may get called multiple times with same grub_script
- object under loops, so we should not let command implementation
- to free the grub_script object.
- To solve above problems, we rely on reference counting for
- grub_script objects. Commands that want to keep the grub_script
- object must take a reference to it.
- Other complexity comes with arbitrary nesting of grub_script
- objects: a grub_script object may have commands with several block
- parameters, and each block parameter may further contain multiple
- block parameters nested. We use temporary variable, state->scripts
- to collect nested child scripts (that are linked by siblings and
- children members), and will build grub_scripts tree from bottom.
- */
- block: "{"
- {
- grub_script_lexer_ref (state->lexerstate);
- $<offset>$ = grub_script_lexer_record_start (state);
- $<memory>$ = grub_script_mem_record (state);
- /* save currently known scripts. */
- $<scripts>$ = state->scripts;
- state->scripts = 0;
- }
- commands1 delimiters0 "}"
- {
- char *p;
- struct grub_script_mem *memory;
- struct grub_script *s = $<scripts>2;
- memory = grub_script_mem_record_stop (state, $<memory>2);
- if ((p = grub_script_lexer_record_stop (state, $<offset>2)))
- *grub_strrchr (p, '}') = '\0';
- $$ = grub_script_arg_add (state, 0, GRUB_SCRIPT_ARG_TYPE_BLOCK, p);
- if (! $$ || ! ($$->script = grub_script_create ($3, memory)))
- grub_script_mem_free (memory);
- else {
- /* attach nested scripts to $$->script as children */
- $$->script->children = state->scripts;
- /* restore old scripts; append $$->script to siblings. */
- state->scripts = $<scripts>2 ?: $$->script;
- if (s) {
- while (s->next_siblings)
- s = s->next_siblings;
- s->next_siblings = $$->script;
- }
- }
- grub_script_lexer_deref (state->lexerstate);
- }
- ;
- block0: /* Empty */ { $$ = 0; }
- | block { $$ = $1; }
- ;
- arguments0: /* Empty */ { $$ = 0; }
- | arguments1 { $$ = $1; }
- ;
- arguments1: argument arguments0
- {
- if ($1 && $2)
- {
- $1->next = $2;
- $1->argcount += $2->argcount;
- $2->argcount = 0;
- }
- $$ = $1;
- }
- ;
- grubcmd: word arguments0 block0
- {
- struct grub_script_arglist *x = $2;
- if ($3)
- x = grub_script_add_arglist (state, $2, $3);
- if ($1 && x) {
- $1->next = x;
- $1->argcount += x->argcount;
- x->argcount = 0;
- }
- $$ = grub_script_create_cmdline (state, $1);
- }
- ;
- /* A single command. */
- command: grubcmd { $$ = $1; }
- | ifcmd { $$ = $1; }
- | forcmd { $$ = $1; }
- | whilecmd { $$ = $1; }
- | untilcmd { $$ = $1; }
- ;
- /* A list of commands. */
- commands1: newlines0 command
- {
- $$ = grub_script_append_cmd (state, 0, $2);
- }
- | commands1 delimiters1 command
- {
- $$ = grub_script_append_cmd (state, $1, $3);
- }
- ;
- function: "function" "name"
- {
- grub_script_lexer_ref (state->lexerstate);
- state->func_mem = grub_script_mem_record (state);
- $<scripts>$ = state->scripts;
- state->scripts = 0;
- }
- newlines0 "{" commands1 delimiters1 "}"
- {
- struct grub_script *script;
- state->func_mem = grub_script_mem_record_stop (state,
- state->func_mem);
- script = grub_script_create ($6, state->func_mem);
- if (! script)
- grub_script_mem_free (state->func_mem);
- else {
- script->children = state->scripts;
- if (!grub_script_function_create ($2, script))
- grub_script_free (script);
- }
- state->scripts = $<scripts>3;
- grub_script_lexer_deref (state->lexerstate);
- }
- ;
- ifcmd: "if"
- {
- grub_script_lexer_ref (state->lexerstate);
- }
- ifclause "fi"
- {
- $$ = $3;
- grub_script_lexer_deref (state->lexerstate);
- }
- ;
- ifclause: commands1 delimiters1 "then" commands1 delimiters1
- {
- $$ = grub_script_create_cmdif (state, $1, $4, 0);
- }
- | commands1 delimiters1 "then" commands1 delimiters1 "else" commands1 delimiters1
- {
- $$ = grub_script_create_cmdif (state, $1, $4, $7);
- }
- | commands1 delimiters1 "then" commands1 delimiters1 "elif" ifclause
- {
- $$ = grub_script_create_cmdif (state, $1, $4, $7);
- }
- ;
- forcmd: "for" "name"
- {
- grub_script_lexer_ref (state->lexerstate);
- }
- "in" arguments0 delimiters1 "do" commands1 delimiters1 "done"
- {
- $$ = grub_script_create_cmdfor (state, $2, $5, $8);
- 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);
- }
- ;
|