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