]> git.proxmox.com Git - grub2.git/blob - grub-core/script/lexer.c
Import grub2_2.02+dfsg1.orig.tar.xz
[grub2.git] / grub-core / script / lexer.c
1 /* lexer.c - The scripting lexer. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
20 #include <config.h>
21
22 #include <grub/parser.h>
23 #include <grub/misc.h>
24 #include <grub/mm.h>
25 #include <grub/script_sh.h>
26 #include <grub/i18n.h>
27
28 #define yytext_ptr char *
29 #include "grub_script.tab.h"
30 #include "grub_script.yy.h"
31
32 void
33 grub_script_lexer_ref (struct grub_lexer_param *state)
34 {
35 state->refs++;
36 }
37
38 void
39 grub_script_lexer_deref (struct grub_lexer_param *state)
40 {
41 state->refs--;
42 }
43
44 /* Start recording all characters passing through the lexer. */
45 unsigned
46 grub_script_lexer_record_start (struct grub_parser_param *parser)
47 {
48 struct grub_lexer_param *lexer = parser->lexerstate;
49
50 lexer->record++;
51 if (lexer->recording)
52 return lexer->recordpos;
53
54 lexer->recordpos = 0;
55 lexer->recordlen = GRUB_LEXER_INITIAL_RECORD_SIZE;
56 lexer->recording = grub_malloc (lexer->recordlen);
57 if (!lexer->recording)
58 {
59 grub_script_yyerror (parser, 0);
60 lexer->recordlen = 0;
61 }
62 return lexer->recordpos;
63 }
64
65 char *
66 grub_script_lexer_record_stop (struct grub_parser_param *parser, unsigned offset)
67 {
68 int count;
69 char *result;
70 struct grub_lexer_param *lexer = parser->lexerstate;
71
72 if (!lexer->record)
73 return 0;
74
75 lexer->record--;
76 if (!lexer->recording)
77 return 0;
78
79 count = lexer->recordpos - offset;
80 result = grub_script_malloc (parser, count + 1);
81 if (result) {
82 grub_strncpy (result, lexer->recording + offset, count);
83 result[count] = '\0';
84 }
85
86 if (lexer->record == 0)
87 {
88 grub_free (lexer->recording);
89 lexer->recording = 0;
90 lexer->recordlen = 0;
91 lexer->recordpos = 0;
92 }
93 return result;
94 }
95
96 /* Record STR if input recording is enabled. */
97 void
98 grub_script_lexer_record (struct grub_parser_param *parser, char *str)
99 {
100 int len;
101 char *old;
102 struct grub_lexer_param *lexer = parser->lexerstate;
103
104 if (!lexer->record || !lexer->recording)
105 return;
106
107 len = grub_strlen (str);
108 if (lexer->recordpos + len + 1 > lexer->recordlen)
109 {
110 old = lexer->recording;
111 if (lexer->recordlen < len)
112 lexer->recordlen = len;
113 lexer->recordlen *= 2;
114 lexer->recording = grub_realloc (lexer->recording, lexer->recordlen);
115 if (!lexer->recording)
116 {
117 grub_free (old);
118 lexer->recordpos = 0;
119 lexer->recordlen = 0;
120 grub_script_yyerror (parser, 0);
121 return;
122 }
123 }
124 grub_strcpy (lexer->recording + lexer->recordpos, str);
125 lexer->recordpos += len;
126 }
127
128 /* Read next line of input if necessary, and set yyscanner buffers. */
129 int
130 grub_script_lexer_yywrap (struct grub_parser_param *parserstate,
131 const char *input)
132 {
133 grub_size_t len = 0;
134 char *p = 0;
135 char *line = 0;
136 YY_BUFFER_STATE buffer;
137 struct grub_lexer_param *lexerstate = parserstate->lexerstate;
138
139 if (! lexerstate->refs && ! lexerstate->prefix && ! input)
140 return 1;
141
142 if (! lexerstate->getline && ! input)
143 {
144 grub_script_yyerror (parserstate, N_("unexpected end of file"));
145 return 1;
146 }
147
148 line = 0;
149 if (! input)
150 lexerstate->getline (&line, 1, lexerstate->getline_data);
151 else
152 line = grub_strdup (input);
153
154 if (! line)
155 {
156 grub_script_yyerror (parserstate, N_("out of memory"));
157 return 1;
158 }
159
160 len = grub_strlen (line);
161
162 /* Ensure '\n' at the end. */
163 if (line[0] == '\0')
164 {
165 grub_free (line);
166 line = grub_strdup ("\n");
167 len = 1;
168 }
169 else if (len && line[len - 1] != '\n')
170 {
171 p = grub_realloc (line, len + 2);
172 if (p)
173 {
174 p[len++] = '\n';
175 p[len] = '\0';
176 }
177 line = p;
178 }
179
180 if (! line)
181 {
182 grub_script_yyerror (parserstate, N_("out of memory"));
183 return 1;
184 }
185
186 /* Prepend any left over unput-text. */
187 if (lexerstate->prefix)
188 {
189 int plen = grub_strlen (lexerstate->prefix);
190
191 p = grub_malloc (len + plen + 1);
192 if (! p)
193 {
194 grub_free (line);
195 return 1;
196 }
197 grub_strcpy (p, lexerstate->prefix);
198 lexerstate->prefix = 0;
199
200 grub_strcpy (p + plen, line);
201 grub_free (line);
202
203 line = p;
204 len = len + plen;
205 }
206
207 buffer = yy_scan_string (line, lexerstate->yyscanner);
208 grub_free (line);
209
210 if (! buffer)
211 {
212 grub_script_yyerror (parserstate, 0);
213 return 1;
214 }
215 return 0;
216 }
217
218 struct grub_lexer_param *
219 grub_script_lexer_init (struct grub_parser_param *parser, char *script,
220 grub_reader_getline_t arg_getline, void *getline_data)
221 {
222 struct grub_lexer_param *lexerstate;
223
224 lexerstate = grub_zalloc (sizeof (*lexerstate));
225 if (!lexerstate)
226 return 0;
227
228 lexerstate->size = GRUB_LEXER_INITIAL_TEXT_SIZE;
229 lexerstate->text = grub_malloc (lexerstate->size);
230 if (!lexerstate->text)
231 {
232 grub_free (lexerstate);
233 return 0;
234 }
235
236 lexerstate->getline = arg_getline;
237 lexerstate->getline_data = getline_data;
238 /* The other elements of lexerstate are all zeros already. */
239
240 if (yylex_init (&lexerstate->yyscanner))
241 {
242 grub_free (lexerstate->text);
243 grub_free (lexerstate);
244 return 0;
245 }
246
247 yyset_extra (parser, lexerstate->yyscanner);
248 parser->lexerstate = lexerstate;
249
250 if (grub_script_lexer_yywrap (parser, script ?: "\n"))
251 {
252 parser->lexerstate = 0;
253 yylex_destroy (lexerstate->yyscanner);
254 grub_free (lexerstate->text);
255 grub_free (lexerstate);
256 return 0;
257 }
258
259 return lexerstate;
260 }
261
262 void
263 grub_script_lexer_fini (struct grub_lexer_param *lexerstate)
264 {
265 if (!lexerstate)
266 return;
267
268 yylex_destroy (lexerstate->yyscanner);
269
270 grub_free (lexerstate->recording);
271 grub_free (lexerstate->text);
272 grub_free (lexerstate);
273 }
274
275 int
276 grub_script_yylex (union YYSTYPE *value,
277 struct grub_parser_param *parserstate)
278 {
279 char *str;
280 int token;
281 grub_script_arg_type_t type;
282 struct grub_lexer_param *lexerstate = parserstate->lexerstate;
283
284 value->arg = 0;
285 if (parserstate->err)
286 return GRUB_PARSER_TOKEN_BAD;
287
288 if (lexerstate->eof)
289 return GRUB_PARSER_TOKEN_EOF;
290
291 /*
292 * Words with environment variables, like foo${bar}baz needs
293 * multiple tokens to be merged into a single grub_script_arg. We
294 * use two variables to achieve this: lexerstate->merge_start and
295 * lexerstate->merge_end
296 */
297
298 lexerstate->merge_start = 0;
299 lexerstate->merge_end = 0;
300 do
301 {
302 /* Empty lexerstate->text. */
303 lexerstate->used = 1;
304 lexerstate->text[0] = '\0';
305
306 token = yylex (value, lexerstate->yyscanner);
307 if (token == GRUB_PARSER_TOKEN_BAD)
308 break;
309
310 /* Merging feature uses lexerstate->text instead of yytext. */
311 if (lexerstate->merge_start)
312 {
313 str = lexerstate->text;
314 type = lexerstate->type;
315 }
316 else
317 {
318 str = yyget_text (lexerstate->yyscanner);
319 type = GRUB_SCRIPT_ARG_TYPE_TEXT;
320 }
321 grub_dprintf("lexer", "token %u text [%s]\n", token, str);
322
323 value->arg = grub_script_arg_add (parserstate, value->arg, type, str);
324 }
325 while (lexerstate->merge_start && !lexerstate->merge_end);
326
327 if (!value->arg || parserstate->err)
328 return GRUB_PARSER_TOKEN_BAD;
329
330 return token;
331 }
332
333 void
334 grub_script_yyerror (struct grub_parser_param *state, char const *err)
335 {
336 if (err)
337 grub_error (GRUB_ERR_INVALID_COMMAND, err);
338
339 grub_print_error ();
340 state->err++;
341 }