GRUB-1.98 changes
[grub2/jjazz.git] / script / lexer.c
blob5bcdf628b4c19d8c009c12ad51e030f1644ae9f4
1 /* lexer.c - The scripting lexer. */
2 /*
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>
22 #include <grub/mm.h>
23 #include <grub/script_sh.h>
25 #include "grub_script.tab.h"
27 static int
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);
38 static int
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));
53 if (! param)
54 return 0;
56 param->state = GRUB_PARSER_STATE_TEXT;
57 param->getline = getline;
58 param->script = script;
60 return param;
63 void
64 grub_script_lexer_ref (struct grub_lexer_param *state)
66 state->refs++;
69 void
70 grub_script_lexer_deref (struct grub_lexer_param *state)
72 state->refs--;
75 /* Start recording all characters passing through the lexer. */
76 void
77 grub_script_lexer_record_start (struct grub_lexer_param *state)
79 state->record = 1;
80 state->recordlen = 100;
81 state->recording = grub_malloc (state->recordlen);
82 state->recordpos = 0;
85 char *
86 grub_script_lexer_record_stop (struct grub_lexer_param *state)
88 state->record = 0;
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");
96 for (;;); /* XXX */
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. */
106 static void
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)
116 grub_free (old);
117 state->record = 0;
120 state->recording[state->recordpos++] = c;
123 /* Fetch the next character for the lexer. */
124 static void
125 nextchar (struct grub_lexer_param *state)
127 if (state->record)
128 recordchar (state, *state->script);
129 state->script++;
133 grub_script_yylex (union YYSTYPE *yylval, struct grub_parser_param *parsestate)
135 grub_parser_state_t newstate;
136 char use;
137 struct grub_lexer_param *state = parsestate->lexerstate;
138 int firstrun = 1;
140 yylval->arg = 0;
142 if (state->tokenonhold)
144 int token = state->tokenonhold;
145 state->tokenonhold = 0;
146 return token;
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)
158 && state->getline)
160 int doexit = 0;
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';
168 break;
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;
176 if (! state->script)
178 doexit = 1;
179 break;
182 if (doexit)
183 break;
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,
194 "\n");
196 else
198 grub_free (state->newscript);
199 state->newscript = 0;
200 state->done = 1;
201 grub_dprintf ("scripting", "token=`\\n'\n");
202 state->tokenonhold = '\n';
203 break;
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))
213 char *buffer = NULL;
214 int bufpos = 0;
215 /* Buffer is initially large enough to hold most commands
216 but extends automatically when needed. */
217 int bufsize = 128;
219 buffer = grub_malloc (bufsize);
221 /* In case the string is not quoted, this can be a one char
222 length symbol. */
223 if (newstate == GRUB_PARSER_STATE_TEXT)
225 int doexit = 0;
226 switch (*state->script)
228 case ' ':
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");
237 if (! firstrun)
238 doexit = 1;
239 break;
241 state->state = newstate;
242 nextchar (state);
244 grub_dprintf ("scripting", "token=` '\n");
245 if (! firstrun)
246 doexit = 1;
247 break;
248 case '{':
249 case '}':
250 case ';':
251 case '\n':
253 char c;
254 grub_dprintf ("scripting", "token=`%c'\n", *state->script);
255 c = *state->script;
256 nextchar (state);
257 state->tokenonhold = c;
258 doexit = 1;
259 break;
262 if (doexit)
264 grub_free (buffer);
265 break;
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))
277 break;
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)
287 int breakout = 0;
289 switch (use)
291 case ' ':
292 case '{':
293 case '}':
294 case ';':
295 case '\n':
296 breakout = 1;
298 if (breakout)
299 break;
302 if (use)
304 if (bufsize <= bufpos + 1)
306 bufsize <<= 1;
307 buffer = grub_realloc (buffer, bufsize);
309 buffer[bufpos++] = use;
312 state->state = newstate;
313 nextchar (state);
316 /* A string of text was read in. */
317 if (bufsize <= bufpos + 1)
319 bufsize <<= 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);
329 grub_free (buffer);
331 else if (newstate == GRUB_PARSER_STATE_VAR
332 || newstate == GRUB_PARSER_STATE_QVAR)
334 char *buffer = NULL;
335 int bufpos = 0;
336 /* Buffer is initially large enough to hold most commands
337 but extends automatically when needed. */
338 int bufsize = 128;
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
349 anymore. */
350 if (! (check_varstate (newstate)))
352 if (state->state == GRUB_PARSER_STATE_VARNAME2
353 || state->state == GRUB_PARSER_STATE_QVARNAME2)
354 nextchar (state);
355 state->state = newstate;
356 break;
359 if (use)
361 if (bufsize <= bufpos + 1)
363 bufsize <<= 1;
364 buffer = grub_realloc (buffer, bufsize);
366 buffer[bufpos++] = use;
369 nextchar (state);
370 state->state = newstate;
373 if (bufsize <= bufpos + 1)
375 bufsize <<= 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);
386 grub_free (buffer);
388 else
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");
393 return 0;
397 if (yylval->arg == 0)
399 int token = state->tokenonhold;
400 state->tokenonhold = 0;
401 return token;
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;
428 void
429 grub_script_yyerror (struct grub_parser_param *lex __attribute__ ((unused)),
430 char const *err)
432 grub_printf ("%s\n", err);