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