1
/* lexer.c - The scripting lexer. */
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/>.
22
#include <grub/parser.h>
23
#include <grub/misc.h>
25
#include <grub/script_sh.h>
27
#include "grub_script.tab.h"
28
#include "grub_script.yy.h"
31
grub_script_lexer_ref (struct grub_lexer_param *state)
37
grub_script_lexer_deref (struct grub_lexer_param *state)
42
/* Start recording all characters passing through the lexer. */
44
grub_script_lexer_record_start (struct grub_parser_param *parser)
46
struct grub_lexer_param *lexer = parser->lexerstate;
50
return lexer->recordpos;
53
lexer->recordlen = GRUB_LEXER_INITIAL_RECORD_SIZE;
54
lexer->recording = grub_malloc (lexer->recordlen);
55
if (!lexer->recording)
57
grub_script_yyerror (parser, 0);
60
return lexer->recordpos;
64
grub_script_lexer_record_stop (struct grub_parser_param *parser, unsigned offset)
68
struct grub_lexer_param *lexer = parser->lexerstate;
74
if (!lexer->recording)
77
count = lexer->recordpos - offset;
78
result = grub_script_malloc (parser, count + 1);
80
grub_strncpy (result, lexer->recording + offset, count);
84
if (lexer->record == 0)
86
grub_free (lexer->recording);
94
/* Record STR if input recording is enabled. */
96
grub_script_lexer_record (struct grub_parser_param *parser, char *str)
100
struct grub_lexer_param *lexer = parser->lexerstate;
102
if (!lexer->record || !lexer->recording)
105
len = grub_strlen (str);
106
if (lexer->recordpos + len + 1 > lexer->recordlen)
108
old = lexer->recording;
109
lexer->recordlen = grub_max (len, lexer->recordlen) * 2;
110
lexer->recording = grub_realloc (lexer->recording, lexer->recordlen);
111
if (!lexer->recording)
114
lexer->recordpos = 0;
115
lexer->recordlen = 0;
116
grub_script_yyerror (parser, 0);
120
grub_strcpy (lexer->recording + lexer->recordpos, str);
121
lexer->recordpos += len;
124
/* Read next line of input if necessary, and set yyscanner buffers. */
126
grub_script_lexer_yywrap (struct grub_parser_param *parserstate,
132
YY_BUFFER_STATE buffer;
133
struct grub_lexer_param *lexerstate = parserstate->lexerstate;
135
if (! lexerstate->refs && ! lexerstate->prefix && ! input)
138
if (! lexerstate->getline && ! input)
140
grub_script_yyerror (parserstate, "unexpected end of file");
146
lexerstate->getline (&line, 1);
148
line = grub_strdup (input);
150
/* Ensure '\n' at the end. */
151
if (line && line[0] == '\0')
154
line = grub_strdup ("\n");
157
if (line && (len = grub_strlen(line)) && line[len - 1] != '\n')
159
p = grub_realloc (line, len + 2);
170
grub_script_yyerror (parserstate, "out of memory");
174
/* Prepend any left over unput-text. */
175
if (lexerstate->prefix)
177
int plen = grub_strlen (lexerstate->prefix);
179
p = grub_malloc (len + plen + 1);
185
grub_strcpy (p, lexerstate->prefix);
186
lexerstate->prefix = 0;
188
grub_strcpy (p + plen, line);
195
buffer = yy_scan_string (line, lexerstate->yyscanner);
200
grub_script_yyerror (parserstate, 0);
206
struct grub_lexer_param *
207
grub_script_lexer_init (struct grub_parser_param *parser, char *script,
208
grub_reader_getline_t getline)
210
struct grub_lexer_param *lexerstate;
212
lexerstate = grub_zalloc (sizeof (*lexerstate));
216
lexerstate->size = GRUB_LEXER_INITIAL_TEXT_SIZE;
217
lexerstate->text = grub_malloc (lexerstate->size);
218
if (!lexerstate->text)
220
grub_free (lexerstate);
224
lexerstate->getline = getline; /* rest are all zeros already */
225
if (yylex_init (&lexerstate->yyscanner))
227
grub_free (lexerstate->text);
228
grub_free (lexerstate);
232
yyset_extra (parser, lexerstate->yyscanner);
233
parser->lexerstate = lexerstate;
235
if (grub_script_lexer_yywrap (parser, script ?: "\n"))
237
parser->lexerstate = 0;
238
yylex_destroy (lexerstate->yyscanner);
239
grub_free (lexerstate->yyscanner);
240
grub_free (lexerstate->text);
241
grub_free (lexerstate);
249
grub_script_lexer_fini (struct grub_lexer_param *lexerstate)
254
yylex_destroy (lexerstate->yyscanner);
256
grub_free (lexerstate->recording);
257
grub_free (lexerstate->text);
258
grub_free (lexerstate);
262
grub_script_yylex (union YYSTYPE *value,
263
struct grub_parser_param *parserstate)
267
grub_script_arg_type_t type;
268
struct grub_lexer_param *lexerstate = parserstate->lexerstate;
271
if (parserstate->err)
272
return GRUB_PARSER_TOKEN_BAD;
275
return GRUB_PARSER_TOKEN_EOF;
278
* Words with environment variables, like foo${bar}baz needs
279
* multiple tokens to be merged into a single grub_script_arg. We
280
* use two variables to achieve this: lexerstate->merge_start and
281
* lexerstate->merge_end
284
lexerstate->merge_start = 0;
285
lexerstate->merge_end = 0;
288
/* Empty lexerstate->text. */
289
lexerstate->used = 1;
290
lexerstate->text[0] = '\0';
292
token = yylex (value, lexerstate->yyscanner);
293
if (token == GRUB_PARSER_TOKEN_BAD)
296
/* Merging feature uses lexerstate->text instead of yytext. */
297
if (lexerstate->merge_start)
299
str = lexerstate->text;
300
type = lexerstate->type;
304
str = yyget_text (lexerstate->yyscanner);
305
type = GRUB_SCRIPT_ARG_TYPE_TEXT;
307
grub_dprintf("lexer", "token %u text [%s]\n", token, str);
309
value->arg = grub_script_arg_add (parserstate, value->arg, type, str);
311
while (lexerstate->merge_start && !lexerstate->merge_end);
313
if (!value->arg || parserstate->err)
314
return GRUB_PARSER_TOKEN_BAD;
320
grub_script_yyerror (struct grub_parser_param *state, char const *err)
323
grub_error (GRUB_ERR_INVALID_COMMAND, err);