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
41static 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
51static 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
58static char *initial_fn;
59
60/* List of allocated strings.  */
61
62struct alloc_string
63{
64  struct alloc_string *next;
65  char *s;
66};
67
68static struct alloc_string *strings;
69
70/* Local functions.  */
71
72static void cpp_line (const char *);
73static char *handle_quotes (const char *, unsigned long *);
74static 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.  */
242int yywrap (void)
243{
244  return 1;
245}
246#endif
247
248/* Handle a C preprocessor line.  */
249
250static void
251cpp_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
305static char *
306handle_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
454static char *
455get_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
471void
472rcparse_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
492void
493rcparse_rcdata (void)
494{
495  rcdata_mode = 1;
496}
497
498/* Go back to normal mode from rcdata mode.  */
499
500void
501rcparse_normal (void)
502{
503  rcdata_mode = 0;
504}
505