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