1 /* parser.y - The scripting parser. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
21 #include <grub/script_sh.h>
23 #include <grub/misc.h>
24 #include <grub/i18n.h>
26 #define YYFREE grub_free
27 #define YYMALLOC grub_malloc
28 #define YYLTYPE_IS_TRIVIAL 0
29 #define YYENABLE_NLS 0
31 #include "grub_script.tab.h"
33 #pragma GCC diagnostic ignored "-Wunreachable-code"
34 #pragma GCC diagnostic ignored "-Wmissing-declarations"
39 struct grub_script_cmd
*cmd
;
40 struct grub_script_arglist
*arglist
;
41 struct grub_script_arg
*arg
;
45 struct grub_script_mem
*memory
;
46 struct grub_script
*scripts
;
50 %token GRUB_PARSER_TOKEN_BAD
51 %token GRUB_PARSER_TOKEN_EOF
0 "end-of-input"
53 %token GRUB_PARSER_TOKEN_NEWLINE
"\n"
54 %token GRUB_PARSER_TOKEN_AND
"&&"
55 %token GRUB_PARSER_TOKEN_OR
"||"
56 %token GRUB_PARSER_TOKEN_SEMI2
";;"
57 %token GRUB_PARSER_TOKEN_PIPE
"|"
58 %token GRUB_PARSER_TOKEN_AMP
"&"
59 %token GRUB_PARSER_TOKEN_SEMI
";"
60 %token GRUB_PARSER_TOKEN_LBR
"{"
61 %token GRUB_PARSER_TOKEN_RBR
"}"
62 %token GRUB_PARSER_TOKEN_NOT
"!"
63 %token GRUB_PARSER_TOKEN_LSQBR2
"["
64 %token GRUB_PARSER_TOKEN_RSQBR2
"]"
65 %token GRUB_PARSER_TOKEN_LT
"<"
66 %token GRUB_PARSER_TOKEN_GT
">"
68 %token
<arg
> GRUB_PARSER_TOKEN_CASE
"case"
69 %token
<arg
> GRUB_PARSER_TOKEN_DO
"do"
70 %token
<arg
> GRUB_PARSER_TOKEN_DONE
"done"
71 %token
<arg
> GRUB_PARSER_TOKEN_ELIF
"elif"
72 %token
<arg
> GRUB_PARSER_TOKEN_ELSE
"else"
73 %token
<arg
> GRUB_PARSER_TOKEN_ESAC
"esac"
74 %token
<arg
> GRUB_PARSER_TOKEN_FI
"fi"
75 %token
<arg
> GRUB_PARSER_TOKEN_FOR
"for"
76 %token
<arg
> GRUB_PARSER_TOKEN_IF
"if"
77 %token
<arg
> GRUB_PARSER_TOKEN_IN
"in"
78 %token
<arg
> GRUB_PARSER_TOKEN_SELECT
"select"
79 %token
<arg
> GRUB_PARSER_TOKEN_THEN
"then"
80 %token
<arg
> GRUB_PARSER_TOKEN_UNTIL
"until"
81 %token
<arg
> GRUB_PARSER_TOKEN_WHILE
"while"
82 %token
<arg
> GRUB_PARSER_TOKEN_FUNCTION
"function"
83 %token
<arg
> GRUB_PARSER_TOKEN_NAME
"name"
84 %token
<arg
> GRUB_PARSER_TOKEN_WORD
"word"
86 %type
<arg
> block block0
87 %type
<arglist
> word argument arguments0 arguments1
89 %type
<cmd
> script_init script
90 %type
<cmd
> grubcmd ifclause ifcmd forcmd whilecmd untilcmd
91 %type
<cmd
> command commands1 statement
94 %lex
-param
{ struct grub_parser_param
*state
};
95 %parse
-param
{ struct grub_parser_param
*state
};
100 /* It should be possible to do this in a clean way... */
101 script_init: { state
->err
= 0; } script
{ state
->parsed
= $2; state
->err
= 0; }
108 | script statement delimiter newlines0
110 $$
= grub_script_append_cmd
(state
, $1, $2);
115 yyerror (state
, N_
("Incorrect command"));
120 newlines0: /* Empty */ | newlines1
;
121 newlines1: newlines0
"\n" ;
126 delimiters0: /* Empty */ | delimiters1
;
127 delimiters1: delimiter
131 word: GRUB_PARSER_TOKEN_NAME
{ $$
= grub_script_add_arglist
(state
, 0, $1); }
132 | GRUB_PARSER_TOKEN_WORD
{ $$
= grub_script_add_arglist
(state
, 0, $1); }
135 statement: command
{ $$
= $1; }
136 | function
{ $$
= 0; }
139 argument
: "case" { $$
= grub_script_add_arglist
(state
, 0, $1); }
140 |
"do" { $$
= grub_script_add_arglist
(state
, 0, $1); }
141 |
"done" { $$
= grub_script_add_arglist
(state
, 0, $1); }
142 |
"elif" { $$
= grub_script_add_arglist
(state
, 0, $1); }
143 |
"else" { $$
= grub_script_add_arglist
(state
, 0, $1); }
144 |
"esac" { $$
= grub_script_add_arglist
(state
, 0, $1); }
145 |
"fi" { $$
= grub_script_add_arglist
(state
, 0, $1); }
146 |
"for" { $$
= grub_script_add_arglist
(state
, 0, $1); }
147 |
"if" { $$
= grub_script_add_arglist
(state
, 0, $1); }
148 |
"in" { $$
= grub_script_add_arglist
(state
, 0, $1); }
149 |
"select" { $$
= grub_script_add_arglist
(state
, 0, $1); }
150 |
"then" { $$
= grub_script_add_arglist
(state
, 0, $1); }
151 |
"until" { $$
= grub_script_add_arglist
(state
, 0, $1); }
152 |
"while" { $$
= grub_script_add_arglist
(state
, 0, $1); }
153 |
"function" { $$
= grub_script_add_arglist
(state
, 0, $1); }
158 Block parameter is passed to commands in two forms: as unparsed
159 string and as pre-parsed grub_script object. Passing as grub_script
160 object makes memory management difficult, because:
162 (1) Command may want to keep a reference to grub_script objects for
163 later use, so script framework may not free the grub_script
164 object after command completes.
166 (2) Command may get called multiple times with same grub_script
167 object under loops, so we should not let command implementation
168 to free the grub_script object.
170 To solve above problems, we rely on reference counting for
171 grub_script objects. Commands that want to keep the grub_script
172 object must take a reference to it.
174 Other complexity comes with arbitrary nesting of grub_script
175 objects: a grub_script object may have commands with several block
176 parameters, and each block parameter may further contain multiple
177 block parameters nested. We use temporary variable, state->scripts
178 to collect nested child scripts (that are linked by siblings and
179 children members), and will build grub_scripts tree from bottom.
183 grub_script_lexer_ref
(state
->lexerstate
);
184 $
<offset
>$
= grub_script_lexer_record_start
(state
);
185 $
<memory
>$
= grub_script_mem_record
(state
);
187 /* save currently known scripts. */
188 $
<scripts
>$
= state
->scripts
;
191 commands1 delimiters0
"}"
194 struct grub_script_mem
*memory
;
195 struct grub_script
*s
= $
<scripts
>2;
197 memory
= grub_script_mem_record_stop
(state
, $
<memory
>2);
198 if
((p
= grub_script_lexer_record_stop
(state
, $
<offset
>2)))
199 *grub_strrchr
(p
, '}') = '\0';
201 $$
= grub_script_arg_add
(state
, 0, GRUB_SCRIPT_ARG_TYPE_BLOCK
, p
);
202 if
(! $$ ||
! ($$
->script
= grub_script_create
($3, memory
)))
203 grub_script_mem_free
(memory
);
206 /* attach nested scripts to $$->script as children */
207 $$
->script
->children
= state
->scripts
;
209 /* restore old scripts; append $$->script to siblings. */
210 state
->scripts
= $
<scripts
>2 ?
: $$
->script
;
212 while
(s
->next_siblings
)
213 s
= s
->next_siblings
;
214 s
->next_siblings
= $$
->script
;
218 grub_script_lexer_deref
(state
->lexerstate
);
221 block0: /* Empty */ { $$
= 0; }
225 arguments0: /* Empty */ { $$
= 0; }
226 | arguments1
{ $$
= $1; }
228 arguments1: argument arguments0
233 $1->argcount
+= $2->argcount
;
240 grubcmd: word arguments0 block0
242 struct grub_script_arglist
*x
= $2;
245 x
= grub_script_add_arglist
(state
, $2, $3);
249 $1->argcount
+= x
->argcount
;
252 $$
= grub_script_create_cmdline
(state
, $1);
256 /* A single command. */
257 command: grubcmd
{ $$
= $1; }
259 | forcmd
{ $$
= $1; }
260 | whilecmd
{ $$
= $1; }
261 | untilcmd
{ $$
= $1; }
264 /* A list of commands. */
265 commands1: newlines0 command
267 $$
= grub_script_append_cmd
(state
, 0, $2);
269 | commands1 delimiters1 command
271 $$
= grub_script_append_cmd
(state
, $1, $3);
275 function: "function" "name"
277 grub_script_lexer_ref
(state
->lexerstate
);
278 state
->func_mem
= grub_script_mem_record
(state
);
280 $
<scripts
>$
= state
->scripts
;
283 delimiters0
"{" commands1 delimiters1
"}"
285 struct grub_script
*script
;
286 state
->func_mem
= grub_script_mem_record_stop
(state
,
288 script
= grub_script_create
($6, state
->func_mem
);
290 grub_script_mem_free
(state
->func_mem
);
292 script
->children
= state
->scripts
;
293 grub_script_function_create
($2, script
);
296 state
->scripts
= $
<scripts
>3;
297 grub_script_lexer_deref
(state
->lexerstate
);
303 grub_script_lexer_ref
(state
->lexerstate
);
308 grub_script_lexer_deref
(state
->lexerstate
);
311 ifclause: commands1 delimiters1
"then" commands1 delimiters1
313 $$
= grub_script_create_cmdif
(state
, $1, $4, 0);
315 | commands1 delimiters1
"then" commands1 delimiters1
"else" commands1 delimiters1
317 $$
= grub_script_create_cmdif
(state
, $1, $4, $7);
319 | commands1 delimiters1
"then" commands1 delimiters1
"elif" ifclause
321 $$
= grub_script_create_cmdif
(state
, $1, $4, $7);
327 grub_script_lexer_ref
(state
->lexerstate
);
329 "in" arguments0 delimiters1
"do" commands1 delimiters1
"done"
331 $$
= grub_script_create_cmdfor
(state
, $2, $5, $8);
332 grub_script_lexer_deref
(state
->lexerstate
);
338 grub_script_lexer_ref
(state
->lexerstate
);
340 commands1 delimiters1
"do" commands1 delimiters1
"done"
342 $$
= grub_script_create_cmdwhile
(state
, $3, $6, 0);
343 grub_script_lexer_deref
(state
->lexerstate
);
349 grub_script_lexer_ref
(state
->lexerstate
);
351 commands1 delimiters1
"do" commands1 delimiters1
"done"
353 $$
= grub_script_create_cmdwhile
(state
, $3, $6, 1);
354 grub_script_lexer_deref
(state
->lexerstate
);