]>
Commit | Line | Data |
---|---|---|
daac212a | 1 | /* lexer.c - The scripting lexer. */ |
2 | /* | |
3 | * GRUB -- GRand Unified Bootloader | |
547e494f | 4 | * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. |
daac212a | 5 | * |
5a79f472 | 6 | * GRUB is free software: you can redistribute it and/or modify |
daac212a | 7 | * it under the terms of the GNU General Public License as published by |
5a79f472 | 8 | * the Free Software Foundation, either version 3 of the License, or |
daac212a | 9 | * (at your option) any later version. |
10 | * | |
5a79f472 | 11 | * GRUB is distributed in the hope that it will be useful, |
daac212a | 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. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
5a79f472 | 17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
daac212a | 18 | */ |
19 | ||
1d12cf29 YB |
20 | #include <config.h> |
21 | ||
daac212a | 22 | #include <grub/parser.h> |
23 | #include <grub/misc.h> | |
24 | #include <grub/mm.h> | |
d558e6b5 | 25 | #include <grub/script_sh.h> |
daac212a | 26 | |
27 | #include "grub_script.tab.h" | |
547e494f | 28 | #include "grub_script.yy.h" |
daac212a | 29 | |
30 | void | |
bfa2bd9e | 31 | grub_script_lexer_ref (struct grub_lexer_param *state) |
daac212a | 32 | { |
bfa2bd9e | 33 | state->refs++; |
daac212a | 34 | } |
35 | ||
36 | void | |
bfa2bd9e | 37 | grub_script_lexer_deref (struct grub_lexer_param *state) |
daac212a | 38 | { |
bfa2bd9e | 39 | state->refs--; |
daac212a | 40 | } |
41 | ||
77c4a393 | 42 | /* Start recording all characters passing through the lexer. */ |
0993355a | 43 | unsigned |
547e494f | 44 | grub_script_lexer_record_start (struct grub_parser_param *parser) |
77c4a393 | 45 | { |
547e494f BC |
46 | struct grub_lexer_param *lexer = parser->lexerstate; |
47 | ||
0993355a BC |
48 | lexer->record++; |
49 | if (lexer->recording) | |
50 | return lexer->recordpos; | |
547e494f | 51 | |
0993355a | 52 | lexer->recordpos = 0; |
bae09d0d | 53 | lexer->recordlen = GRUB_LEXER_INITIAL_RECORD_SIZE; |
547e494f | 54 | lexer->recording = grub_malloc (lexer->recordlen); |
547e494f BC |
55 | if (!lexer->recording) |
56 | { | |
57 | grub_script_yyerror (parser, 0); | |
547e494f BC |
58 | lexer->recordlen = 0; |
59 | } | |
0993355a | 60 | return lexer->recordpos; |
77c4a393 | 61 | } |
62 | ||
63 | char * | |
0993355a | 64 | grub_script_lexer_record_stop (struct grub_parser_param *parser, unsigned offset) |
77c4a393 | 65 | { |
0993355a | 66 | int count; |
547e494f BC |
67 | char *result; |
68 | struct grub_lexer_param *lexer = parser->lexerstate; | |
69 | ||
0993355a | 70 | if (!lexer->record) |
547e494f | 71 | return 0; |
77c4a393 | 72 | |
0993355a BC |
73 | lexer->record--; |
74 | if (!lexer->recording) | |
75 | return 0; | |
547e494f | 76 | |
0993355a BC |
77 | count = lexer->recordpos - offset; |
78 | result = grub_script_malloc (parser, count + 1); | |
79 | if (result) { | |
80 | grub_strncpy (result, lexer->recording + offset, count); | |
81 | result[count] = '\0'; | |
82 | } | |
547e494f | 83 | |
0993355a BC |
84 | if (lexer->record == 0) |
85 | { | |
86 | grub_free (lexer->recording); | |
87 | lexer->recording = 0; | |
88 | lexer->recordlen = 0; | |
89 | lexer->recordpos = 0; | |
90 | } | |
547e494f | 91 | return result; |
77c4a393 | 92 | } |
93 | ||
547e494f BC |
94 | /* Record STR if input recording is enabled. */ |
95 | void | |
96 | grub_script_lexer_record (struct grub_parser_param *parser, char *str) | |
77c4a393 | 97 | { |
547e494f | 98 | int len; |
bae09d0d | 99 | char *old; |
547e494f BC |
100 | struct grub_lexer_param *lexer = parser->lexerstate; |
101 | ||
0993355a | 102 | if (!lexer->record || !lexer->recording) |
547e494f BC |
103 | return; |
104 | ||
105 | len = grub_strlen (str); | |
bae09d0d | 106 | if (lexer->recordpos + len + 1 > lexer->recordlen) |
77c4a393 | 107 | { |
bae09d0d | 108 | old = lexer->recording; |
0993355a | 109 | lexer->recordlen = grub_max (len, lexer->recordlen) * 2; |
547e494f BC |
110 | lexer->recording = grub_realloc (lexer->recording, lexer->recordlen); |
111 | if (!lexer->recording) | |
77c4a393 | 112 | { |
113 | grub_free (old); | |
bae09d0d | 114 | lexer->recordpos = 0; |
0993355a | 115 | lexer->recordlen = 0; |
547e494f BC |
116 | grub_script_yyerror (parser, 0); |
117 | return; | |
77c4a393 | 118 | } |
119 | } | |
547e494f BC |
120 | grub_strcpy (lexer->recording + lexer->recordpos, str); |
121 | lexer->recordpos += len; | |
77c4a393 | 122 | } |
123 | ||
547e494f | 124 | /* Read next line of input if necessary, and set yyscanner buffers. */ |
daac212a | 125 | int |
b06f83e3 BC |
126 | grub_script_lexer_yywrap (struct grub_parser_param *parserstate, |
127 | const char *input) | |
daac212a | 128 | { |
05d2ed32 | 129 | int len = 0; |
b06f83e3 BC |
130 | char *p = 0; |
131 | char *line = 0; | |
547e494f BC |
132 | YY_BUFFER_STATE buffer; |
133 | struct grub_lexer_param *lexerstate = parserstate->lexerstate; | |
daac212a | 134 | |
b06f83e3 BC |
135 | if (! lexerstate->refs && ! lexerstate->prefix && ! input) |
136 | return 1; | |
547e494f | 137 | |
b06f83e3 | 138 | if (! lexerstate->getline && ! input) |
547e494f BC |
139 | { |
140 | grub_script_yyerror (parserstate, "unexpected end of file"); | |
b06f83e3 | 141 | return 1; |
547e494f | 142 | } |
daac212a | 143 | |
547e494f | 144 | line = 0; |
b06f83e3 BC |
145 | if (! input) |
146 | lexerstate->getline (&line, 1); | |
147 | else | |
148 | line = grub_strdup (input); | |
0500dfd1 BC |
149 | |
150 | /* Ensure '\n' at the end. */ | |
151 | if (line && line[0] == '\0') | |
daac212a | 152 | { |
0500dfd1 BC |
153 | grub_free (line); |
154 | line = grub_strdup ("\n"); | |
daac212a | 155 | } |
156 | ||
0500dfd1 | 157 | if (line && (len = grub_strlen(line)) && line[len - 1] != '\n') |
547e494f | 158 | { |
0500dfd1 BC |
159 | p = grub_realloc (line, len + 2); |
160 | if (p) | |
161 | { | |
162 | p[len++] = '\n'; | |
163 | p[len] = '\0'; | |
164 | } | |
165 | line = p; | |
547e494f | 166 | } |
0500dfd1 | 167 | |
b06f83e3 | 168 | if (! line) |
daac212a | 169 | { |
0500dfd1 | 170 | grub_script_yyerror (parserstate, "out of memory"); |
b06f83e3 | 171 | return 1; |
daac212a | 172 | } |
173 | ||
0500dfd1 | 174 | /* Prepend any left over unput-text. */ |
b06f83e3 | 175 | if (lexerstate->prefix) |
daac212a | 176 | { |
b06f83e3 BC |
177 | int plen = grub_strlen (lexerstate->prefix); |
178 | ||
0500dfd1 | 179 | p = grub_malloc (len + plen + 1); |
b06f83e3 | 180 | if (! p) |
daac212a | 181 | { |
b06f83e3 BC |
182 | grub_free (line); |
183 | return 1; | |
daac212a | 184 | } |
b06f83e3 BC |
185 | grub_strcpy (p, lexerstate->prefix); |
186 | lexerstate->prefix = 0; | |
187 | ||
b06f83e3 | 188 | grub_strcpy (p + plen, line); |
0500dfd1 | 189 | grub_free (line); |
b06f83e3 BC |
190 | |
191 | line = p; | |
192 | len = len + plen; | |
547e494f BC |
193 | } |
194 | ||
b06f83e3 | 195 | buffer = yy_scan_string (line, lexerstate->yyscanner); |
547e494f | 196 | grub_free (line); |
b06f83e3 BC |
197 | |
198 | if (! buffer) | |
547e494f BC |
199 | { |
200 | grub_script_yyerror (parserstate, 0); | |
b06f83e3 | 201 | return 1; |
547e494f | 202 | } |
b06f83e3 | 203 | return 0; |
547e494f | 204 | } |
daac212a | 205 | |
547e494f BC |
206 | struct grub_lexer_param * |
207 | grub_script_lexer_init (struct grub_parser_param *parser, char *script, | |
208 | grub_reader_getline_t getline) | |
209 | { | |
547e494f BC |
210 | struct grub_lexer_param *lexerstate; |
211 | ||
212 | lexerstate = grub_zalloc (sizeof (*lexerstate)); | |
213 | if (!lexerstate) | |
214 | return 0; | |
215 | ||
df6dc211 BC |
216 | lexerstate->size = GRUB_LEXER_INITIAL_TEXT_SIZE; |
217 | lexerstate->text = grub_malloc (lexerstate->size); | |
547e494f BC |
218 | if (!lexerstate->text) |
219 | { | |
220 | grub_free (lexerstate); | |
221 | return 0; | |
222 | } | |
b39f9d20 | 223 | |
547e494f BC |
224 | lexerstate->getline = getline; /* rest are all zeros already */ |
225 | if (yylex_init (&lexerstate->yyscanner)) | |
226 | { | |
227 | grub_free (lexerstate->text); | |
228 | grub_free (lexerstate); | |
229 | return 0; | |
230 | } | |
231 | ||
b06f83e3 BC |
232 | yyset_extra (parser, lexerstate->yyscanner); |
233 | parser->lexerstate = lexerstate; | |
547e494f | 234 | |
b06f83e3 | 235 | if (grub_script_lexer_yywrap (parser, script ?: "\n")) |
547e494f | 236 | { |
b06f83e3 | 237 | parser->lexerstate = 0; |
547e494f BC |
238 | yylex_destroy (lexerstate->yyscanner); |
239 | grub_free (lexerstate->yyscanner); | |
547e494f BC |
240 | grub_free (lexerstate->text); |
241 | grub_free (lexerstate); | |
242 | return 0; | |
243 | } | |
547e494f BC |
244 | |
245 | return lexerstate; | |
246 | } | |
247 | ||
248 | void | |
249 | grub_script_lexer_fini (struct grub_lexer_param *lexerstate) | |
250 | { | |
251 | if (!lexerstate) | |
252 | return; | |
253 | ||
254 | yylex_destroy (lexerstate->yyscanner); | |
255 | ||
256 | grub_free (lexerstate->recording); | |
257 | grub_free (lexerstate->text); | |
258 | grub_free (lexerstate); | |
259 | } | |
260 | ||
261 | int | |
262 | grub_script_yylex (union YYSTYPE *value, | |
263 | struct grub_parser_param *parserstate) | |
264 | { | |
265 | char *str; | |
266 | int token; | |
267 | grub_script_arg_type_t type; | |
268 | struct grub_lexer_param *lexerstate = parserstate->lexerstate; | |
269 | ||
270 | value->arg = 0; | |
271 | if (parserstate->err) | |
272 | return GRUB_PARSER_TOKEN_BAD; | |
273 | ||
274 | if (lexerstate->eof) | |
275 | return GRUB_PARSER_TOKEN_EOF; | |
276 | ||
277 | /* | |
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 | |
282 | */ | |
283 | ||
284 | lexerstate->merge_start = 0; | |
285 | lexerstate->merge_end = 0; | |
286 | do | |
287 | { | |
288 | /* Empty lexerstate->text. */ | |
df6dc211 | 289 | lexerstate->used = 1; |
547e494f BC |
290 | lexerstate->text[0] = '\0'; |
291 | ||
292 | token = yylex (value, lexerstate->yyscanner); | |
293 | if (token == GRUB_PARSER_TOKEN_BAD) | |
294 | break; | |
295 | ||
296 | /* Merging feature uses lexerstate->text instead of yytext. */ | |
297 | if (lexerstate->merge_start) | |
fda6cb98 | 298 | { |
547e494f BC |
299 | str = lexerstate->text; |
300 | type = lexerstate->type; | |
daac212a | 301 | } |
fda6cb98 | 302 | else |
303 | { | |
547e494f BC |
304 | str = yyget_text (lexerstate->yyscanner); |
305 | type = GRUB_SCRIPT_ARG_TYPE_TEXT; | |
fda6cb98 | 306 | } |
83672756 | 307 | grub_dprintf("lexer", "token %u text [%s]\n", token, str); |
b39f9d20 | 308 | |
547e494f | 309 | value->arg = grub_script_arg_add (parserstate, value->arg, type, str); |
fda6cb98 | 310 | } |
547e494f | 311 | while (lexerstate->merge_start && !lexerstate->merge_end); |
daac212a | 312 | |
547e494f BC |
313 | if (!value->arg || parserstate->err) |
314 | return GRUB_PARSER_TOKEN_BAD; | |
daac212a | 315 | |
547e494f | 316 | return token; |
daac212a | 317 | } |
318 | ||
319 | void | |
547e494f | 320 | grub_script_yyerror (struct grub_parser_param *state, char const *err) |
daac212a | 321 | { |
547e494f BC |
322 | if (err) |
323 | grub_error (GRUB_ERR_INVALID_COMMAND, err); | |
324 | ||
325 | grub_print_error (); | |
326 | state->err++; | |
daac212a | 327 | } |