1 /* lexer.c - The scripting lexer. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007,2008,2009 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/>.
20 #include <grub/parser.h>
21 #include <grub/misc.h>
23 #include <grub/script_sh.h>
25 #include "grub_script.tab.h"
28 check_varstate (grub_parser_state_t state
)
30 return (state
== GRUB_PARSER_STATE_VARNAME
31 || state
== GRUB_PARSER_STATE_VAR
32 || state
== GRUB_PARSER_STATE_QVAR
33 || state
== GRUB_PARSER_STATE_VARNAME2
34 || state
== GRUB_PARSER_STATE_QVARNAME
35 || state
== GRUB_PARSER_STATE_QVARNAME2
);
39 check_textstate (grub_parser_state_t state
)
41 return (state
== GRUB_PARSER_STATE_TEXT
42 || state
== GRUB_PARSER_STATE_ESC
43 || state
== GRUB_PARSER_STATE_QUOTE
44 || state
== GRUB_PARSER_STATE_DQUOTE
);
47 struct grub_lexer_param
*
48 grub_script_lexer_init (char *script
, grub_reader_getline_t getline
)
50 struct grub_lexer_param
*param
;
52 param
= grub_zalloc (sizeof (*param
));
56 param
->state
= GRUB_PARSER_STATE_TEXT
;
57 param
->getline
= getline
;
58 param
->script
= script
;
64 grub_script_lexer_ref (struct grub_lexer_param
*state
)
70 grub_script_lexer_deref (struct grub_lexer_param
*state
)
75 /* Start recording all characters passing through the lexer. */
77 grub_script_lexer_record_start (struct grub_lexer_param
*state
)
80 state
->recordlen
= 100;
81 state
->recording
= grub_malloc (state
->recordlen
);
86 grub_script_lexer_record_stop (struct grub_lexer_param
*state
)
90 /* Delete the last character, it is a `}'. */
91 if (state
->recordpos
> 0)
93 if (state
->recording
[--state
->recordpos
] != '}')
95 grub_printf ("Internal error while parsing menu entry");
98 state
->recording
[state
->recordpos
] = '\0';
101 return state
->recording
;
104 /* When recording is enabled, record the character C as the next item
105 in the character stream. */
107 recordchar (struct grub_lexer_param
*state
, char c
)
109 if (state
->recordpos
== state
->recordlen
)
111 char *old
= state
->recording
;
112 state
->recordlen
+= 100;
113 state
->recording
= grub_realloc (state
->recording
, state
->recordlen
);
114 if (! state
->recording
)
120 state
->recording
[state
->recordpos
++] = c
;
123 /* Fetch the next character for the lexer. */
125 nextchar (struct grub_lexer_param
*state
)
128 recordchar (state
, *state
->script
);
133 grub_script_yylex (union YYSTYPE
*yylval
, struct grub_parser_param
*parsestate
)
135 grub_parser_state_t newstate
;
137 struct grub_lexer_param
*state
= parsestate
->lexerstate
;
142 if (state
->tokenonhold
)
144 int token
= state
->tokenonhold
;
145 state
->tokenonhold
= 0;
149 for (;! state
->done
; firstrun
= 0)
151 if (! state
->script
|| ! *state
->script
)
153 /* Check if more tokens are requested by the parser. */
154 if (((state
->refs
&& ! parsestate
->err
)
155 || state
->state
== GRUB_PARSER_STATE_ESC
156 || state
->state
== GRUB_PARSER_STATE_QUOTE
157 || state
->state
== GRUB_PARSER_STATE_DQUOTE
)
161 if (state
->state
!= GRUB_PARSER_STATE_ESC
162 && state
->state
!= GRUB_PARSER_STATE_QUOTE
163 && state
->state
!= GRUB_PARSER_STATE_DQUOTE
164 && ! state
->was_newline
)
166 state
->was_newline
= 1;
167 state
->tokenonhold
= '\n';
170 while (! state
->script
|| ! *state
->script
)
172 grub_free (state
->newscript
);
173 state
->newscript
= 0;
174 state
->getline (&state
->newscript
, 1);
175 state
->script
= state
->newscript
;
184 grub_dprintf ("scripting", "token=`\\n'\n");
185 recordchar (state
, '\n');
186 if (state
->state
== GRUB_PARSER_STATE_VARNAME
)
187 state
->state
= GRUB_PARSER_STATE_TEXT
;
188 if (state
->state
== GRUB_PARSER_STATE_QVARNAME
)
189 state
->state
= GRUB_PARSER_STATE_DQUOTE
;
190 if (state
->state
== GRUB_PARSER_STATE_DQUOTE
191 || state
->state
== GRUB_PARSER_STATE_QUOTE
)
192 yylval
->arg
= grub_script_arg_add (parsestate
, yylval
->arg
,
193 GRUB_SCRIPT_ARG_TYPE_STR
,
198 grub_free (state
->newscript
);
199 state
->newscript
= 0;
201 grub_dprintf ("scripting", "token=`\\n'\n");
202 state
->tokenonhold
= '\n';
206 state
->was_newline
= 0;
208 newstate
= grub_parser_cmdline_state (state
->state
, *state
->script
, &use
);
210 /* Check if it is a text. */
211 if (check_textstate (newstate
))
215 /* Buffer is initially large enough to hold most commands
216 but extends automatically when needed. */
219 buffer
= grub_malloc (bufsize
);
221 /* In case the string is not quoted, this can be a one char
223 if (newstate
== GRUB_PARSER_STATE_TEXT
)
226 switch (*state
->script
)
229 while (*state
->script
)
231 newstate
= grub_parser_cmdline_state (state
->state
,
232 *state
->script
, &use
);
233 if (! (state
->state
== GRUB_PARSER_STATE_TEXT
234 && *state
->script
== ' '))
236 grub_dprintf ("scripting", "token=` '\n");
241 state
->state
= newstate
;
244 grub_dprintf ("scripting", "token=` '\n");
254 grub_dprintf ("scripting", "token=`%c'\n", *state
->script
);
257 state
->tokenonhold
= c
;
269 /* Read one token, possible quoted. */
270 while (*state
->script
)
272 newstate
= grub_parser_cmdline_state (state
->state
,
273 *state
->script
, &use
);
275 /* Check if a variable name starts. */
276 if (check_varstate (newstate
))
279 /* If the string is not quoted or escaped, stop processing
280 when a special token was found. It will be recognized
281 next time when this function is called. */
282 if (newstate
== GRUB_PARSER_STATE_TEXT
283 && state
->state
!= GRUB_PARSER_STATE_ESC
284 && state
->state
!= GRUB_PARSER_STATE_QUOTE
285 && state
->state
!= GRUB_PARSER_STATE_DQUOTE
)
304 if (bufsize
<= bufpos
+ 1)
307 buffer
= grub_realloc (buffer
, bufsize
);
309 buffer
[bufpos
++] = use
;
312 state
->state
= newstate
;
316 /* A string of text was read in. */
317 if (bufsize
<= bufpos
+ 1)
320 buffer
= grub_realloc (buffer
, bufsize
);
323 buffer
[bufpos
++] = 0;
325 grub_dprintf ("scripting", "token=`%s'\n", buffer
);
326 yylval
->arg
= grub_script_arg_add (parsestate
, yylval
->arg
,
327 GRUB_SCRIPT_ARG_TYPE_STR
, buffer
);
331 else if (newstate
== GRUB_PARSER_STATE_VAR
332 || newstate
== GRUB_PARSER_STATE_QVAR
)
336 /* Buffer is initially large enough to hold most commands
337 but extends automatically when needed. */
340 buffer
= grub_malloc (bufsize
);
342 /* This is a variable, read the variable name. */
343 while (*state
->script
)
345 newstate
= grub_parser_cmdline_state (state
->state
,
346 *state
->script
, &use
);
348 /* Check if this character is not part of the variable name
350 if (! (check_varstate (newstate
)))
352 if (state
->state
== GRUB_PARSER_STATE_VARNAME2
353 || state
->state
== GRUB_PARSER_STATE_QVARNAME2
)
355 state
->state
= newstate
;
361 if (bufsize
<= bufpos
+ 1)
364 buffer
= grub_realloc (buffer
, bufsize
);
366 buffer
[bufpos
++] = use
;
370 state
->state
= newstate
;
373 if (bufsize
<= bufpos
+ 1)
376 buffer
= grub_realloc (buffer
, bufsize
);
379 buffer
[bufpos
++] = 0;
381 state
->state
= newstate
;
382 yylval
->arg
= grub_script_arg_add (parsestate
, yylval
->arg
,
383 GRUB_SCRIPT_ARG_TYPE_VAR
, buffer
);
384 grub_dprintf ("scripting", "vartoken=`%s'\n", buffer
);
390 /* There is either text or a variable name. In the case you
391 arrive here there is a serious problem with the lexer. */
392 grub_error (GRUB_ERR_BAD_ARGUMENT
, "internal error");
397 if (yylval
->arg
== 0)
399 int token
= state
->tokenonhold
;
400 state
->tokenonhold
= 0;
404 if (yylval
->arg
->next
== 0 && yylval
->arg
->type
== GRUB_SCRIPT_ARG_TYPE_STR
)
406 /* Detect some special tokens. */
407 if (! grub_strcmp (yylval
->arg
->str
, "while"))
408 return GRUB_PARSER_TOKEN_WHILE
;
409 else if (! grub_strcmp (yylval
->arg
->str
, "if"))
410 return GRUB_PARSER_TOKEN_IF
;
411 else if (! grub_strcmp (yylval
->arg
->str
, "function"))
412 return GRUB_PARSER_TOKEN_FUNCTION
;
413 else if (! grub_strcmp (yylval
->arg
->str
, "menuentry"))
414 return GRUB_PARSER_TOKEN_MENUENTRY
;
415 else if (! grub_strcmp (yylval
->arg
->str
, "@"))
416 return GRUB_PARSER_TOKEN_MENUENTRY
;
417 else if (! grub_strcmp (yylval
->arg
->str
, "else"))
418 return GRUB_PARSER_TOKEN_ELSE
;
419 else if (! grub_strcmp (yylval
->arg
->str
, "then"))
420 return GRUB_PARSER_TOKEN_THEN
;
421 else if (! grub_strcmp (yylval
->arg
->str
, "fi"))
422 return GRUB_PARSER_TOKEN_FI
;
425 return GRUB_PARSER_TOKEN_ARG
;
429 grub_script_yyerror (struct grub_parser_param
*lex
__attribute__ ((unused
)),
432 grub_printf ("%s\n", err
);