* deflex.l (YY_NO_UNPUT): Define.
[external/binutils.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 }