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