95b2191705ac826833e0951fa323242a9f8859d1
[grub.git] / grub-core / script / yylex.l
1 %{
2 /* yylex.l  The scripting lexer.  */
3 /*
4  *  GRUB  --  GRand Unified Bootloader
5  *  Copyright (C) 2009,2010  Free Software Foundation, Inc.
6  *
7  *  GRUB is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  GRUB is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <grub/parser.h>
22 #include <grub/misc.h>
23 #include <grub/mm.h>
24 #include <grub/script_sh.h>
25 #include <grub/i18n.h>
26 #include "grub_script.tab.h"
27
28 #pragma GCC diagnostic ignored "-Wunused-parameter"
29 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
30 #pragma GCC diagnostic ignored "-Wmissing-declarations"
31 #pragma GCC diagnostic ignored "-Wunused-function"
32 #pragma GCC diagnostic ignored "-Wsign-compare"
33
34 #define yyalloc(size, scanner)   (grub_malloc((size)))
35 #define yyfree(ptr, scanner)   (grub_free((ptr)))
36 #define yyrealloc(ptr, size, scanner) (grub_realloc((ptr), (size)))
37
38 /* 
39  * As we don't have access to yyscanner, we cannot do much except to
40  * print the fatal error.
41  */
42 #define YY_FATAL_ERROR(msg)                     \
43   do {                                          \
44     grub_printf (_("fatal error: %s\n"), _(msg));     \
45   } while (0)
46
47 #define COPY(str, hint)                         \
48   do {                                          \
49     copy_string (yyextra, str, hint);           \
50   } while (0)
51
52
53 #define RECORD                                  \
54   do {                                          \
55     grub_script_lexer_record (yyextra, yytext); \
56   } while (0)
57
58 #define ARG(t)                        \
59   do {                                \
60     yyextra->lexerstate->type = t;    \
61     return GRUB_PARSER_TOKEN_WORD;    \
62   } while (0)
63
64 /* We don't need YY_INPUT, as we rely on yy_scan_strings */
65 #define YY_INPUT(buf,res,max) do { res = 0; } while (0)
66
67 /* forward declarations */
68 static int grub_lexer_unput (const char *input, yyscan_t yyscanner);
69 static int grub_lexer_resplit (const char *input, yyscan_t yyscanner);
70
71 static void  copy_string (struct grub_parser_param *, const char *,
72                           unsigned hint);
73
74 %}
75
76 %top{
77
78 #include <config.h>
79
80 #include <sys/types.h>
81
82 typedef size_t yy_size_t;
83 #define YY_TYPEDEF_YY_SIZE_T 1
84
85 /* 
86  * Some flex hacks for -nostdinc; XXX We need to fix these when libc
87  * support becomes availble in GRUB.
88  */
89
90 #ifndef GRUB_UTIL
91 #define stdin  0
92 #define stdout 0
93
94 #define fprintf(...) 0
95 #define exit(...) grub_fatal("fatal error in lexer")
96 #endif
97
98 }
99
100 %option ecs
101 %option meta-ecs
102
103 %option warn
104 %option array
105 %option stack
106 %option reentrant
107 %option bison-bridge
108 %option never-interactive
109
110 %option noyyfree noyyalloc noyyrealloc
111 %option nounistd nostdinit nodefault noyylineno
112
113 /* Reduce lexer size, by not defining these.  */
114 %option noyy_top_state
115 %option noinput nounput
116 %option noyyget_in noyyset_in
117 %option noyyget_out noyyset_out
118 %option noyyget_debug noyyset_debug
119 %option noyyget_lineno noyyset_lineno
120
121 %option extra-type="struct grub_parser_param*"
122
123 BLANK           [ \t]
124 COMMENT         #.*$
125
126 CHAR            [^{}|&$;<> \t\n\'\"\\]
127 DIGITS          [[:digit:]]+
128 NAME            [[:alpha:]_][[:alnum:]_]*
129
130 ESC             \\(.|\n)
131 SQCHR           [^\']
132 DQCHR           {ESC}|[^\\\"]
133 DQSTR           \"{DQCHR}*\"
134 I18NSTR         \$\"{DQCHR}*\"
135 SQSTR           \'{SQCHR}*\'
136 SPECIAL         \?|\#|\*|\@
137 VARIABLE        ${NAME}|$\{{NAME}\}|${DIGITS}|$\{{DIGITS}\}|${SPECIAL}|$\{{SPECIAL}\}
138 WORD            ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE}|{I18NSTR})+
139
140 MULTILINE       {WORD}?((\"{DQCHR}*)|(\$\"{DQCHR}*)|(\'{SQCHR}*))
141 POS_MULTILINE   {WORD}?\\\n
142
143 %x              SPLIT
144 %x              DQUOTE
145 %x              I18NQUOTE
146 %x              SQUOTE
147 %x              VAR
148
149 %%
150
151  /* White spaces */
152 {BLANK}+        { RECORD; }
153 {COMMENT}       { RECORD; }
154
155  /* Special symbols */
156 "\n"            { RECORD; return GRUB_PARSER_TOKEN_NEWLINE; }
157 "||"            { RECORD; return GRUB_PARSER_TOKEN_OR;      }
158 "&&"            { RECORD; return GRUB_PARSER_TOKEN_AND;     }
159 ";;"            { RECORD; return GRUB_PARSER_TOKEN_SEMI2;   }
160 "|"             { RECORD; return GRUB_PARSER_TOKEN_PIPE;    }
161 "&"             { RECORD; return GRUB_PARSER_TOKEN_AMP;     }
162 ";"             { RECORD; return GRUB_PARSER_TOKEN_SEMI;    }
163 "<"             { RECORD; return GRUB_PARSER_TOKEN_LT;      }
164 ">"             { RECORD; return GRUB_PARSER_TOKEN_GT;      }
165
166  /* Reserved words */
167 "{"             { RECORD; return GRUB_PARSER_TOKEN_LBR;       }
168 "}"             { RECORD; return GRUB_PARSER_TOKEN_RBR;       }
169 "[["            { RECORD; return GRUB_PARSER_TOKEN_LSQBR2;    }
170 "]]"            { RECORD; return GRUB_PARSER_TOKEN_RSQBR2;    }
171 "case"          { RECORD; return GRUB_PARSER_TOKEN_CASE;      }
172 "do"            { RECORD; return GRUB_PARSER_TOKEN_DO;        }
173 "done"          { RECORD; return GRUB_PARSER_TOKEN_DONE;      }
174 "elif"          { RECORD; return GRUB_PARSER_TOKEN_ELIF;      }
175 "else"          { RECORD; return GRUB_PARSER_TOKEN_ELSE;      }
176 "esac"          { RECORD; return GRUB_PARSER_TOKEN_ESAC;      }
177 "fi"            { RECORD; return GRUB_PARSER_TOKEN_FI;        }
178 "for"           { RECORD; return GRUB_PARSER_TOKEN_FOR;       }
179 "if"            { RECORD; return GRUB_PARSER_TOKEN_IF;        }
180 "in"            { RECORD; return GRUB_PARSER_TOKEN_IN;        }
181 "select"        { RECORD; return GRUB_PARSER_TOKEN_SELECT;    }
182 "then"          { RECORD; return GRUB_PARSER_TOKEN_THEN;      }
183 "until"         { RECORD; return GRUB_PARSER_TOKEN_UNTIL;     }
184 "while"         { RECORD; return GRUB_PARSER_TOKEN_WHILE;     }
185 "function"      { RECORD; return GRUB_PARSER_TOKEN_FUNCTION;  }
186
187 {MULTILINE}     {
188                   if (grub_lexer_unput (yytext, yyscanner))
189                     return GRUB_PARSER_TOKEN_BAD;
190                 }
191
192 {POS_MULTILINE} {
193                   if (yyg->yy_c_buf_p + 1 == &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
194                     {
195                       if (grub_lexer_unput (yytext, yyscanner))
196                         return GRUB_PARSER_TOKEN_BAD;
197                     }
198                   else
199                     {
200                       RECORD;
201                       yypush_buffer_state (YY_CURRENT_BUFFER, yyscanner);
202                       if (grub_lexer_resplit (yytext, yyscanner))
203                         {
204                           yypop_buffer_state (yyscanner);
205                           return GRUB_PARSER_TOKEN_WORD;
206                         }
207                       yyextra->lexerstate->resplit = 1;
208                     }
209                 }
210
211
212 {NAME}          { RECORD; return GRUB_PARSER_TOKEN_NAME; }
213 {WORD}          {
214                   RECORD;
215                   yypush_buffer_state (YY_CURRENT_BUFFER, yyscanner);
216                   if (grub_lexer_resplit (yytext, yyscanner))
217                     {
218                       yypop_buffer_state (yyscanner);
219                       return GRUB_PARSER_TOKEN_WORD;
220                     }
221                   yyextra->lexerstate->resplit = 1;
222                 }
223 .               {
224                   grub_script_yyerror (yyextra, yytext);
225                   return GRUB_PARSER_TOKEN_BAD;
226                 }
227
228  /* Split word into multiple args */
229
230 <SPLIT>{
231   \\.           { COPY (yytext, yyleng); }
232   \\\n          { /* ignore */ }
233   \"            {
234                   yy_push_state (DQUOTE, yyscanner);
235                   ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
236                 }
237   \'            {
238                   yy_push_state (SQUOTE, yyscanner);
239                   ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
240                 }
241   "\$\""        {
242                   yy_push_state (I18NQUOTE, yyscanner);
243                   ARG (GRUB_SCRIPT_ARG_TYPE_GETTEXT);
244                 }
245   \$            {
246                   yy_push_state (VAR, yyscanner);
247                   ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
248                 }
249   \\            |
250   [^\"\'\$\\]+  { COPY (yytext, yyleng); }
251   <<EOF>>       {
252                   yy_pop_state (yyscanner);
253                   yypop_buffer_state (yyscanner);
254                   yyextra->lexerstate->resplit = 0;
255                   yyextra->lexerstate->merge_end = 1;
256                   ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
257                 }
258 }
259
260 <VAR>{
261   {SPECIAL}     |
262   {DIGITS}      |
263   {NAME}        {
264                   COPY (yytext, yyleng);
265                   yy_pop_state (yyscanner);
266                   if (YY_START == SPLIT)
267                     ARG (GRUB_SCRIPT_ARG_TYPE_VAR);
268                   else
269                     ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR);
270                 }
271   \{{SPECIAL}\} |
272   \{{DIGITS}\}  |
273   \{{NAME}\}    {
274                   yytext[yyleng - 1] = '\0';
275                   COPY (yytext + 1, yyleng - 2);
276                   yy_pop_state (yyscanner);
277                   if (YY_START == SPLIT)
278                     ARG (GRUB_SCRIPT_ARG_TYPE_VAR);
279                   else
280                     ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR);
281                 }
282   .|\n          { return GRUB_PARSER_TOKEN_BAD; }
283 }
284
285 <SQUOTE>{
286   \'            {
287                   yy_pop_state (yyscanner);
288                   ARG (GRUB_SCRIPT_ARG_TYPE_SQSTR);
289                 }
290   [^\']+        { COPY (yytext, yyleng); }
291 }
292
293 <DQUOTE>{
294   \\\$          { COPY ("$", 1); }
295   \\\\          { COPY ("\\", 1); }
296   \\\"          { COPY ("\"", 1); }
297   \\\n          { /* ignore */ }
298   [^\"\$\\\n]+  { COPY (yytext, yyleng); }
299   \"            {
300                   yy_pop_state (yyscanner);
301                   ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR);
302                 }
303   \$            {
304                   yy_push_state (VAR, yyscanner);
305                   ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR);
306                 }
307   (.|\n)        { COPY (yytext, yyleng); }
308 }
309
310 <I18NQUOTE>{
311   \\\\          { COPY ("\\\\", 2); }
312   \\\"          { COPY ("\"", 1); }
313   \\\n          { /* ignore */ }
314   [^\"\\\n]+    { COPY (yytext, yyleng); }
315   \"            {
316                   yy_pop_state (yyscanner);
317                   ARG (GRUB_SCRIPT_ARG_TYPE_GETTEXT);
318                 }
319   \\            { COPY ("\\", 1); }
320   (.|\n)        { COPY (yytext, yyleng); }
321 }
322
323 <<EOF>>         {
324                   yypop_buffer_state (yyscanner);
325                   yyextra->lexerstate->eof = 1;
326                   return GRUB_PARSER_TOKEN_EOF;
327                 }
328 %%
329
330 int
331 yywrap (yyscan_t yyscanner)
332 {
333   if (yyget_extra (yyscanner)->lexerstate->resplit)
334     return 1;
335
336   return grub_script_lexer_yywrap (yyget_extra (yyscanner), 0);
337 }
338
339 static void copy_string (struct grub_parser_param *parser, const char *str, unsigned hint)
340 {
341   grub_size_t size;
342   char *ptr;
343   unsigned len;
344
345   len = hint ? hint : grub_strlen (str);
346   if (parser->lexerstate->used + len >= parser->lexerstate->size)
347     {
348       size = len * 2;
349       if (size < parser->lexerstate->size * 2)
350         size = parser->lexerstate->size * 2;
351       ptr = grub_realloc (parser->lexerstate->text, size);
352       if (!ptr)
353         {
354           grub_script_yyerror (parser, 0);
355           return;
356         }
357
358       parser->lexerstate->text = ptr;
359       parser->lexerstate->size = size;
360     }
361   grub_strcpy (parser->lexerstate->text + parser->lexerstate->used - 1, str);
362   parser->lexerstate->used += len;
363 }
364
365 static int
366 grub_lexer_resplit (const char *text, yyscan_t yyscanner)
367 {
368   /* resplit text */
369   if (yy_scan_string (text, yyscanner))
370     {
371       yyget_extra (yyscanner)->lexerstate->merge_start = 1;
372       yy_push_state (SPLIT, yyscanner);
373       return 0;
374     }
375   grub_script_yyerror (yyget_extra (yyscanner), 0);
376   return 1;
377 }
378
379 static int
380 grub_lexer_unput (const char *text, yyscan_t yyscanner)
381 {
382   struct grub_lexer_param *lexerstate = yyget_extra (yyscanner)->lexerstate;
383
384   grub_free (lexerstate->prefix);
385
386   lexerstate->prefix = grub_strdup (text);
387   if (! lexerstate->prefix)
388     {
389       grub_script_yyerror (yyget_extra (yyscanner), N_("out of memory"));
390       return 1;
391     }
392   return 0;
393 }