* deflex.l (YY_NO_UNPUT): Define.
[binutils-gdb.git] / binutils / rclex.l
1 %{ /* rclex.l -- lexer for Windows rc files parser */
2 /* Copyright 1997, 1998, 1999, 2001, 2002, 2003 Free Software Foundation, Inc.
3 Written by Ian Lance Taylor, Cygnus Support.
4
5 This file is part of GNU Binutils.
6
7 This program 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 2 of the License, or
10 (at your option) any later version.
11
12 This program 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 this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA. */
21
22 /* This is a lex input file which generates a lexer used by the
23 Windows rc file parser. It basically just recognized a bunch of
24 keywords. */
25
26 #include "bfd.h"
27 #include "bucomm.h"
28 #include "libiberty.h"
29 #include "safe-ctype.h"
30 #include "windres.h"
31 #include "rcparse.h"
32
33 #include <assert.h>
34
35 #define YY_NO_UNPUT
36
37 /* Whether we are in rcdata mode, in which we returns the lengths of
38 strings. */
39
40 static int rcdata_mode;
41
42 /* Whether we are supressing lines from cpp (including windows.h or
43 headers from your C sources may bring in externs and typedefs).
44 When active, we return IGNORED_TOKEN, which lets us ignore these
45 outside of resource constructs. Thus, it isn't required to protect
46 all the non-preprocessor lines in your header files with #ifdef
47 RC_INVOKED. It also means your RC file can't include other RC
48 files if they're named "*.h". Sorry. Name them *.rch or whatever. */
49
50 static int suppress_cpp_data;
51
52 #define MAYBE_RETURN(x) return suppress_cpp_data ? IGNORED_TOKEN : (x)
53
54 /* The first filename we detect in the cpp output. We use this to
55 tell included files from the original file. */
56
57 static char *initial_fn;
58
59 /* List of allocated strings. */
60
61 struct alloc_string
62 {
63 struct alloc_string *next;
64 char *s;
65 };
66
67 static struct alloc_string *strings;
68
69 /* Local functions. */
70
71 static void cpp_line (const char *);
72 static char *handle_quotes (const char *, unsigned long *);
73 static char *get_string (int);
74
75 %}
76
77 %%
78
79 "BEGIN" { MAYBE_RETURN (BEG); }
80 "{" { MAYBE_RETURN (BEG); }
81 "END" { MAYBE_RETURN (END); }
82 "}" { MAYBE_RETURN (END); }
83 "ACCELERATORS" { MAYBE_RETURN (ACCELERATORS); }
84 "VIRTKEY" { MAYBE_RETURN (VIRTKEY); }
85 "ASCII" { MAYBE_RETURN (ASCII); }
86 "NOINVERT" { MAYBE_RETURN (NOINVERT); }
87 "SHIFT" { MAYBE_RETURN (SHIFT); }
88 "CONTROL" { MAYBE_RETURN (CONTROL); }
89 "ALT" { MAYBE_RETURN (ALT); }
90 "BITMAP" { MAYBE_RETURN (BITMAP); }
91 "CURSOR" { MAYBE_RETURN (CURSOR); }
92 "DIALOG" { MAYBE_RETURN (DIALOG); }
93 "DIALOGEX" { MAYBE_RETURN (DIALOGEX); }
94 "EXSTYLE" { MAYBE_RETURN (EXSTYLE); }
95 "CAPTION" { MAYBE_RETURN (CAPTION); }
96 "CLASS" { MAYBE_RETURN (CLASS); }
97 "STYLE" { MAYBE_RETURN (STYLE); }
98 "AUTO3STATE" { MAYBE_RETURN (AUTO3STATE); }
99 "AUTOCHECKBOX" { MAYBE_RETURN (AUTOCHECKBOX); }
100 "AUTORADIOBUTTON" { MAYBE_RETURN (AUTORADIOBUTTON); }
101 "CHECKBOX" { MAYBE_RETURN (CHECKBOX); }
102 "COMBOBOX" { MAYBE_RETURN (COMBOBOX); }
103 "CTEXT" { MAYBE_RETURN (CTEXT); }
104 "DEFPUSHBUTTON" { MAYBE_RETURN (DEFPUSHBUTTON); }
105 "EDITTEXT" { MAYBE_RETURN (EDITTEXT); }
106 "GROUPBOX" { MAYBE_RETURN (GROUPBOX); }
107 "LISTBOX" { MAYBE_RETURN (LISTBOX); }
108 "LTEXT" { MAYBE_RETURN (LTEXT); }
109 "PUSHBOX" { MAYBE_RETURN (PUSHBOX); }
110 "PUSHBUTTON" { MAYBE_RETURN (PUSHBUTTON); }
111 "RADIOBUTTON" { MAYBE_RETURN (RADIOBUTTON); }
112 "RTEXT" { MAYBE_RETURN (RTEXT); }
113 "SCROLLBAR" { MAYBE_RETURN (SCROLLBAR); }
114 "STATE3" { MAYBE_RETURN (STATE3); }
115 "USERBUTTON" { MAYBE_RETURN (USERBUTTON); }
116 "BEDIT" { MAYBE_RETURN (BEDIT); }
117 "HEDIT" { MAYBE_RETURN (HEDIT); }
118 "IEDIT" { MAYBE_RETURN (IEDIT); }
119 "FONT" { MAYBE_RETURN (FONT); }
120 "ICON" { MAYBE_RETURN (ICON); }
121 "LANGUAGE" { MAYBE_RETURN (LANGUAGE); }
122 "CHARACTERISTICS" { MAYBE_RETURN (CHARACTERISTICS); }
123 "VERSION" { MAYBE_RETURN (VERSIONK); }
124 "MENU" { MAYBE_RETURN (MENU); }
125 "MENUEX" { MAYBE_RETURN (MENUEX); }
126 "MENUITEM" { MAYBE_RETURN (MENUITEM); }
127 "SEPARATOR" { MAYBE_RETURN (SEPARATOR); }
128 "POPUP" { MAYBE_RETURN (POPUP); }
129 "CHECKED" { MAYBE_RETURN (CHECKED); }
130 "GRAYED" { MAYBE_RETURN (GRAYED); }
131 "HELP" { MAYBE_RETURN (HELP); }
132 "INACTIVE" { MAYBE_RETURN (INACTIVE); }
133 "MENUBARBREAK" { MAYBE_RETURN (MENUBARBREAK); }
134 "MENUBREAK" { MAYBE_RETURN (MENUBREAK); }
135 "MESSAGETABLE" { MAYBE_RETURN (MESSAGETABLE); }
136 "RCDATA" { MAYBE_RETURN (RCDATA); }
137 "STRINGTABLE" { MAYBE_RETURN (STRINGTABLE); }
138 "VERSIONINFO" { MAYBE_RETURN (VERSIONINFO); }
139 "FILEVERSION" { MAYBE_RETURN (FILEVERSION); }
140 "PRODUCTVERSION" { MAYBE_RETURN (PRODUCTVERSION); }
141 "FILEFLAGSMASK" { MAYBE_RETURN (FILEFLAGSMASK); }
142 "FILEFLAGS" { MAYBE_RETURN (FILEFLAGS); }
143 "FILEOS" { MAYBE_RETURN (FILEOS); }
144 "FILETYPE" { MAYBE_RETURN (FILETYPE); }
145 "FILESUBTYPE" { MAYBE_RETURN (FILESUBTYPE); }
146 "VALUE" { MAYBE_RETURN (VALUE); }
147 "MOVEABLE" { MAYBE_RETURN (MOVEABLE); }
148 "FIXED" { MAYBE_RETURN (FIXED); }
149 "PURE" { MAYBE_RETURN (PURE); }
150 "IMPURE" { MAYBE_RETURN (IMPURE); }
151 "PRELOAD" { MAYBE_RETURN (PRELOAD); }
152 "LOADONCALL" { MAYBE_RETURN (LOADONCALL); }
153 "DISCARDABLE" { MAYBE_RETURN (DISCARDABLE); }
154 "NOT" { MAYBE_RETURN (NOT); }
155
156 "BLOCK"[ \t\n]*"\""[^\#\n]*"\"" {
157 char *s, *send;
158
159 /* This is a hack to let us parse version
160 information easily. */
161
162 s = strchr (yytext, '"');
163 ++s;
164 send = strchr (s, '"');
165 if (strncmp (s, "StringFileInfo",
166 sizeof "StringFileInfo" - 1) == 0
167 && s + sizeof "StringFileInfo" - 1 == send)
168 MAYBE_RETURN (BLOCKSTRINGFILEINFO);
169 else if (strncmp (s, "VarFileInfo",
170 sizeof "VarFileInfo" - 1) == 0
171 && s + sizeof "VarFileInfo" - 1 == send)
172 MAYBE_RETURN (BLOCKVARFILEINFO);
173 else
174 {
175 char *r;
176
177 r = get_string (send - s + 1);
178 strncpy (r, s, send - s);
179 r[send - s] = '\0';
180 yylval.s = r;
181 MAYBE_RETURN (BLOCK);
182 }
183 }
184
185 "#"[^\n]* {
186 cpp_line (yytext);
187 }
188
189 [0-9][x0-9A-Fa-f]*L {
190 yylval.i.val = strtoul (yytext, 0, 0);
191 yylval.i.dword = 1;
192 MAYBE_RETURN (NUMBER);
193 }
194
195 [0-9][x0-9A-Fa-f]* {
196 yylval.i.val = strtoul (yytext, 0, 0);
197 yylval.i.dword = 0;
198 MAYBE_RETURN (NUMBER);
199 }
200
201 ("\""[^\"\n]*"\""[ \t\n]*)+ {
202 char *s;
203 unsigned long length;
204
205 s = handle_quotes (yytext, &length);
206 if (! rcdata_mode)
207 {
208 yylval.s = s;
209 MAYBE_RETURN (QUOTEDSTRING);
210 }
211 else
212 {
213 yylval.ss.length = length;
214 yylval.ss.s = s;
215 MAYBE_RETURN (SIZEDSTRING);
216 }
217 }
218
219 [A-Za-z][^ ,\t\r\n]* {
220 char *s;
221
222 /* I rejected comma in a string in order to
223 handle VIRTKEY, CONTROL in an accelerator
224 resource. This means that an unquoted
225 file name can not contain a comma. I
226 don't know what rc permits. */
227
228 s = get_string (strlen (yytext) + 1);
229 strcpy (s, yytext);
230 yylval.s = s;
231 MAYBE_RETURN (STRING);
232 }
233
234 [\n] { ++rc_lineno; }
235 [ \t\r]+ { /* ignore whitespace */ }
236 . { MAYBE_RETURN (*yytext); }
237
238 %%
239 #ifndef yywrap
240 /* This is needed for some versions of lex. */
241 int yywrap (void)
242 {
243 return 1;
244 }
245 #endif
246
247 /* Handle a C preprocessor line. */
248
249 static void
250 cpp_line (const char *s)
251 {
252 int line;
253 char *send, *fn;
254
255 ++s;
256 while (ISSPACE (*s))
257 ++s;
258
259 line = strtol (s, &send, 0);
260 if (*send != '\0' && ! ISSPACE (*send))
261 return;
262
263 /* Subtract 1 because we are about to count the newline. */
264 rc_lineno = line - 1;
265
266 s = send;
267 while (ISSPACE (*s))
268 ++s;
269
270 if (*s != '"')
271 return;
272
273 ++s;
274 send = strchr (s, '"');
275 if (send == NULL)
276 return;
277
278 fn = (char *) xmalloc (send - s + 1);
279 strncpy (fn, s, send - s);
280 fn[send - s] = '\0';
281
282 free (rc_filename);
283 rc_filename = fn;
284
285 if (!initial_fn)
286 {
287 initial_fn = xmalloc (strlen (fn) + 1);
288 strcpy (initial_fn, fn);
289 }
290
291 /* Allow the initial file, regardless of name. Suppress all other
292 files if they end in ".h" (this allows included "*.rc"). */
293 if (strcmp (initial_fn, fn) == 0
294 || strcmp (fn + strlen (fn) - 2, ".h") != 0)
295 suppress_cpp_data = 0;
296 else
297 suppress_cpp_data = 1;
298 }
299
300 /* Handle a quoted string. The quotes are stripped. A pair of quotes
301 in a string are turned into a single quote. Adjacent strings are
302 merged separated by whitespace are merged, as in C. */
303
304 static char *
305 handle_quotes (const char *input, unsigned long *len)
306 {
307 char *ret, *s;
308 const char *t;
309 int ch;
310
311 ret = get_string (strlen (input) + 1);
312
313 s = ret;
314 t = input;
315 if (*t == '"')
316 ++t;
317 while (*t != '\0')
318 {
319 if (*t == '\\')
320 {
321 ++t;
322 switch (*t)
323 {
324 case '\0':
325 rcparse_warning ("backslash at end of string");
326 break;
327
328 case '\"':
329 rcparse_warning ("use \"\" to put \" in a string");
330 break;
331
332 case 'a':
333 *s++ = ESCAPE_B; /* Strange, but true... */
334 ++t;
335 break;
336
337 case 'b':
338 *s++ = ESCAPE_B;
339 ++t;
340 break;
341
342 case 'f':
343 *s++ = ESCAPE_F;
344 ++t;
345 break;
346
347 case 'n':
348 *s++ = ESCAPE_N;
349 ++t;
350 break;
351
352 case 'r':
353 *s++ = ESCAPE_R;
354 ++t;
355 break;
356
357 case 't':
358 *s++ = ESCAPE_T;
359 ++t;
360 break;
361
362 case 'v':
363 *s++ = ESCAPE_V;
364 ++t;
365 break;
366
367 case '\\':
368 *s++ = *t++;
369 break;
370
371 case '0': case '1': case '2': case '3':
372 case '4': case '5': case '6': case '7':
373 ch = *t - '0';
374 ++t;
375 if (*t >= '0' && *t <= '7')
376 {
377 ch = (ch << 3) | (*t - '0');
378 ++t;
379 if (*t >= '0' && *t <= '7')
380 {
381 ch = (ch << 3) | (*t - '0');
382 ++t;
383 }
384 }
385 *s++ = ch;
386 break;
387
388 case 'x':
389 ++t;
390 ch = 0;
391 while (1)
392 {
393 if (*t >= '0' && *t <= '9')
394 ch = (ch << 4) | (*t - '0');
395 else if (*t >= 'a' && *t <= 'f')
396 ch = (ch << 4) | (*t - 'a' + 10);
397 else if (*t >= 'A' && *t <= 'F')
398 ch = (ch << 4) | (*t - 'A' + 10);
399 else
400 break;
401 ++t;
402 }
403 *s++ = ch;
404 break;
405
406 default:
407 rcparse_warning ("unrecognized escape sequence");
408 *s++ = '\\';
409 *s++ = *t++;
410 break;
411 }
412 }
413 else if (*t != '"')
414 *s++ = *t++;
415 else if (t[1] == '\0')
416 break;
417 else if (t[1] == '"')
418 {
419 *s++ = '"';
420 t += 2;
421 }
422 else
423 {
424 ++t;
425 assert (ISSPACE (*t));
426 while (ISSPACE (*t))
427 {
428 if ((*t) == '\n')
429 ++rc_lineno;
430 ++t;
431 }
432 if (*t == '\0')
433 break;
434 assert (*t == '"');
435 ++t;
436 }
437 }
438
439 *s = '\0';
440
441 *len = s - ret;
442
443 return ret;
444 }
445
446 /* Allocate a string of a given length. */
447
448 static char *
449 get_string (int len)
450 {
451 struct alloc_string *as;
452
453 as = (struct alloc_string *) xmalloc (sizeof *as);
454 as->s = xmalloc (len);
455
456 as->next = strings;
457 strings = as;
458
459 return as->s;
460 }
461
462 /* Discard all the strings we have allocated. The parser calls this
463 when it no longer needs them. */
464
465 void
466 rcparse_discard_strings (void)
467 {
468 struct alloc_string *as;
469
470 as = strings;
471 while (as != NULL)
472 {
473 struct alloc_string *n;
474
475 free (as->s);
476 n = as->next;
477 free (as);
478 as = n;
479 }
480
481 strings = NULL;
482 }
483
484 /* Enter rcdata mode. */
485
486 void
487 rcparse_rcdata (void)
488 {
489 rcdata_mode = 1;
490 }
491
492 /* Go back to normal mode from rcdata mode. */
493
494 void
495 rcparse_normal (void)
496 {
497 rcdata_mode = 0;
498 }