1 %{ /* rclex.l -- lexer for Windows rc files parser  */
2 /* Copyright 1997, 1998, 1999, 2001, 2002, 2003, 2005
3    Free Software Foundation, Inc.
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
20    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
21    02110-1301, USA.  */
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"
30 #include "safe-ctype.h"
31 #include "windres.h"
32 #include "rcparse.h"
33 
34 #include <assert.h>
35 
36 #define YY_NO_UNPUT
37 
38 /* Whether we are in rcdata mode, in which we returns the lengths of
39    strings.  */
40 
41 static int rcdata_mode;
42 
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 
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 
72 static void cpp_line (const char *);
73 static char *handle_quotes (const char *, unsigned long *);
74 static char *get_string (int);
75 
76 %}
77 
78 %%
79 
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); }
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)
169 			    MAYBE_RETURN (BLOCKSTRINGFILEINFO);
170 			  else if (strncmp (s, "VarFileInfo",
171 					    sizeof "VarFileInfo" - 1) == 0
172 				   && s + sizeof "VarFileInfo" - 1 == send)
173 			    MAYBE_RETURN (BLOCKVARFILEINFO);
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;
182 			      MAYBE_RETURN (BLOCK);
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;
193 			  MAYBE_RETURN (NUMBER);
194 			}
195 
196 [0-9][x0-9A-Fa-f]*	{
197 			  yylval.i.val = strtoul (yytext, 0, 0);
198 			  yylval.i.dword = 0;
199 			  MAYBE_RETURN (NUMBER);
200 			}
201 
202 ("\""[^\"\n]*"\""[ \t\n]*)+ {
203 			  char *s;
204 			  unsigned long length;
205 
206 			  s = handle_quotes (yytext, &length);
207 			  if (! rcdata_mode)
208 			    {
209 			      yylval.s = s;
210 			      MAYBE_RETURN (QUOTEDSTRING);
211 			    }
212 			  else
213 			    {
214 			      yylval.ss.length = length;
215 			      yylval.ss.s = s;
216 			      MAYBE_RETURN (SIZEDSTRING);
217 			    }
218 			}
219 
220 [A-Za-z][^ ,\t\r\n]*	{
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;
232 			  MAYBE_RETURN (STRING);
233 			}
234 
235 [\n]			{ ++rc_lineno; }
236 [ \t\r]+		{ /* ignore whitespace */ }
237 .			{ MAYBE_RETURN (*yytext); }
238 
239 %%
240 #ifndef yywrap
241 /* This is needed for some versions of lex.  */
242 int yywrap (void)
243 {
244   return 1;
245 }
246 #endif
247 
248 /* Handle a C preprocessor line.  */
249 
250 static void
251 cpp_line (const char *s)
252 {
253   int line;
254   char *send, *fn;
255 
256   ++s;
257   while (ISSPACE (*s))
258     ++s;
259 
260   line = strtol (s, &send, 0);
261   if (*send != '\0' && ! ISSPACE (*send))
262     return;
263 
264   /* Subtract 1 because we are about to count the newline.  */
265   rc_lineno = line - 1;
266 
267   s = send;
268   while (ISSPACE (*s))
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;
285 
286   if (!initial_fn)
287     {
288       initial_fn = xmalloc (strlen (fn) + 1);
289       strcpy (initial_fn, fn);
290     }
291 
292   /* Allow the initial file, regardless of name.  Suppress all other
293      files if they end in ".h" (this allows included "*.rc").  */
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;
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 *
306 handle_quotes (const char *input, unsigned long *len)
307 {
308   char *ret, *s;
309   const char *t;
310   int ch;
311   int num_xdigits;
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':
335 	      *s++ = ESCAPE_B; /* Strange, but true...  */
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;
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--)
398 		{
399 		  if (*t >= '0' && *t <= '9')
400 		    ch = (ch << 4) | (*t - '0');
401 		  else if (*t >= 'a' && *t <= 'f')
402 		    ch = (ch << 4) | (*t - 'a' + 10);
403 		  else if (*t >= 'A' && *t <= 'F')
404 		    ch = (ch << 4) | (*t - 'A' + 10);
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;
431 	  assert (ISSPACE (*t));
432 	  while (ISSPACE (*t))
433 	    {
434 	      if ((*t) == '\n')
435 		++rc_lineno;
436 	      ++t;
437 	    }
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 *
455 get_string (int len)
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;
463   strings = as;
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
472 rcparse_discard_strings (void)
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
493 rcparse_rcdata (void)
494 {
495   rcdata_mode = 1;
496 }
497 
498 /* Go back to normal mode from rcdata mode.  */
499 
500 void
501 rcparse_normal (void)
502 {
503   rcdata_mode = 0;
504 }
505