1 /* lexer.c - The scripting lexer. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007 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.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_QUOTE
43 || state
== GRUB_PARSER_STATE_DQUOTE
);
46 struct grub_lexer_param
*
47 grub_script_lexer_init (char *script
, grub_err_t (*getline
) (char **))
49 struct grub_lexer_param
*param
;
51 param
= grub_malloc (sizeof (*param
));
55 param
->state
= GRUB_PARSER_STATE_TEXT
;
56 param
->getline
= getline
;
60 param
->script
= script
;
70 grub_script_lexer_ref (struct grub_lexer_param
*state
)
76 grub_script_lexer_deref (struct grub_lexer_param
*state
)
81 /* Start recording all characters passing through the lexer. */
83 grub_script_lexer_record_start (struct grub_lexer_param
*state
)
86 state
->recordlen
= 100;
87 state
->recording
= grub_malloc (state
->recordlen
);
92 grub_script_lexer_record_stop (struct grub_lexer_param
*state
)
96 /* Delete the last character, it is a `}'. */
97 if (state
->recordpos
> 0)
99 if (state
->recording
[--state
->recordpos
] != '}')
101 grub_printf ("Internal error while parsing menu entry");
104 state
->recording
[state
->recordpos
] = '\0';
107 return state
->recording
;
110 /* When recording is enabled, record the character C as the next item
111 in the character stream. */
113 recordchar (struct grub_lexer_param
*state
, char c
)
115 if (state
->recordpos
== state
->recordlen
)
117 char *old
= state
->recording
;
118 state
->recordlen
+= 100;
119 state
->recording
= grub_realloc (state
->recording
, state
->recordlen
);
120 if (! state
->recording
)
126 state
->recording
[state
->recordpos
++] = c
;
129 /* Fetch the next character for the lexer. */
131 nextchar (struct grub_lexer_param
*state
)
134 recordchar (state
, *state
->script
);
139 grub_script_yylex2 (union YYSTYPE
*yylval
,
140 struct grub_parser_param
*parsestate
);
143 grub_script_yylex (union YYSTYPE
*yylval
, struct grub_parser_param
*parsestate
)
149 r
= grub_script_yylex2 (yylval
, parsestate
);
157 grub_script_yylex2 (union YYSTYPE
*yylval
, struct grub_parser_param
*parsestate
)
159 grub_parser_state_t newstate
;
163 struct grub_lexer_param
*state
= parsestate
->lexerstate
;
168 if (! *state
->script
)
170 /* Check if more tokens are requested by the parser. */
172 || state
->state
== GRUB_PARSER_STATE_ESC
)
175 while (!state
->script
|| ! grub_strlen (state
->script
))
177 grub_free (state
->newscript
);
178 state
->newscript
= 0;
179 state
->getline (&state
->newscript
);
180 state
->script
= state
->newscript
;
184 grub_dprintf ("scripting", "token=`\\n'\n");
185 recordchar (state
, '\n');
186 if (state
->state
!= GRUB_PARSER_STATE_ESC
)
191 grub_free (state
->newscript
);
192 state
->newscript
= 0;
194 grub_dprintf ("scripting", "token=`\\n'\n");
199 newstate
= grub_parser_cmdline_state (state
->state
, *state
->script
, &use
);
201 /* Check if it is a text. */
202 if (check_textstate (newstate
))
204 /* In case the string is not quoted, this can be a one char
206 if (newstate
== GRUB_PARSER_STATE_TEXT
)
208 switch (*state
->script
)
211 while (*state
->script
)
213 newstate
= grub_parser_cmdline_state (state
->state
,
214 *state
->script
, &use
);
215 if (! (state
->state
== GRUB_PARSER_STATE_TEXT
216 && *state
->script
== ' '))
218 grub_dprintf ("scripting", "token=` '\n");
221 state
->state
= newstate
;
224 grub_dprintf ("scripting", "token=` '\n");
232 grub_dprintf ("scripting", "token=`%c'\n", *state
->script
);
240 /* XXX: Use a better size. */
241 buffer
= grub_script_malloc (parsestate
, 2048);
247 /* Read one token, possible quoted. */
248 while (*state
->script
)
250 newstate
= grub_parser_cmdline_state (state
->state
,
251 *state
->script
, &use
);
253 /* Check if a variable name starts. */
254 if (check_varstate (newstate
))
257 /* If the string is not quoted or escaped, stop processing
258 when a special token was found. It will be recognized
259 next time when this function is called. */
260 if (newstate
== GRUB_PARSER_STATE_TEXT
261 && state
->state
!= GRUB_PARSER_STATE_ESC
)
281 state
->state
= newstate
;
285 /* A string of text was read in. */
287 grub_dprintf ("scripting", "token=`%s'\n", buffer
);
288 yylval
->string
= buffer
;
290 /* Detect some special tokens. */
291 if (! grub_strcmp (buffer
, "while"))
292 return GRUB_PARSER_TOKEN_WHILE
;
293 else if (! grub_strcmp (buffer
, "if"))
294 return GRUB_PARSER_TOKEN_IF
;
295 else if (! grub_strcmp (buffer
, "function"))
296 return GRUB_PARSER_TOKEN_FUNCTION
;
297 else if (! grub_strcmp (buffer
, "menuentry"))
298 return GRUB_PARSER_TOKEN_MENUENTRY
;
299 else if (! grub_strcmp (buffer
, "@"))
300 return GRUB_PARSER_TOKEN_MENUENTRY
;
301 else if (! grub_strcmp (buffer
, "else"))
302 return GRUB_PARSER_TOKEN_ELSE
;
303 else if (! grub_strcmp (buffer
, "then"))
304 return GRUB_PARSER_TOKEN_THEN
;
305 else if (! grub_strcmp (buffer
, "fi"))
306 return GRUB_PARSER_TOKEN_FI
;
308 return GRUB_PARSER_TOKEN_NAME
;
310 else if (newstate
== GRUB_PARSER_STATE_VAR
311 || newstate
== GRUB_PARSER_STATE_QVAR
)
313 /* XXX: Use a better size. */
314 buffer
= grub_script_malloc (parsestate
, 2096);
320 /* This is a variable, read the variable name. */
321 while (*state
->script
)
323 newstate
= grub_parser_cmdline_state (state
->state
,
324 *state
->script
, &use
);
326 /* Check if this character is not part of the variable name
328 if (! (check_varstate (newstate
)))
330 if (state
->state
== GRUB_PARSER_STATE_VARNAME2
331 || state
->state
== GRUB_PARSER_STATE_QVARNAME2
)
333 state
->state
= newstate
;
340 state
->state
= newstate
;
344 state
->state
= newstate
;
345 yylval
->string
= buffer
;
346 grub_dprintf ("scripting", "vartoken=`%s'\n", buffer
);
348 return GRUB_PARSER_TOKEN_VAR
;
352 /* There is either text or a variable name. In the case you
353 arrive here there is a serious problem with the lexer. */
354 grub_error (GRUB_ERR_BAD_ARGUMENT
, "Internal error\n");
360 grub_script_yyerror (struct grub_parser_param
*lex
__attribute__ ((unused
)),
363 grub_printf ("%s\n", err
);