1/* rclex.c -- lexer for Windows rc files parser  */
2
3/* Copyright 1997, 1998, 1999, 2001, 2002, 2003, 2005, 2006, 2007, 2008, 2009
4   Free Software Foundation, Inc.
5
6   Written by Kai Tietz, Onevision.
7
8   This file is part of GNU Binutils.
9
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 3 of the License, or
13   (at your option) any later version.
14
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
23   02110-1301, USA.  */
24
25
26/* This is a lexer used by the Windows rc file parser.  It basically
27   just recognized a bunch of keywords.  */
28
29#include "sysdep.h"
30#include "bfd.h"
31#include "bucomm.h"
32#include "libiberty.h"
33#include "safe-ctype.h"
34#include "windres.h"
35#include "rcparse.h"
36
37#include <assert.h>
38
39/* Whether we are in rcdata mode, in which we returns the lengths of
40   strings.  */
41
42static int rcdata_mode;
43
44/* Whether we are supressing lines from cpp (including windows.h or
45   headers from your C sources may bring in externs and typedefs).
46   When active, we return IGNORED_TOKEN, which lets us ignore these
47   outside of resource constructs.  Thus, it isn't required to protect
48   all the non-preprocessor lines in your header files with #ifdef
49   RC_INVOKED.  It also means your RC file can't include other RC
50   files if they're named "*.h".  Sorry.  Name them *.rch or whatever.  */
51
52static int suppress_cpp_data;
53
54#define IGNORE_CPP(x) (suppress_cpp_data ? IGNORED_TOKEN : (x))
55
56/* The first filename we detect in the cpp output.  We use this to
57   tell included files from the original file.  */
58
59static char *initial_fn;
60
61/* List of allocated strings.  */
62
63struct alloc_string
64{
65  struct alloc_string *next;
66  char *s;
67};
68
69static struct alloc_string *strings;
70
71struct rclex_keywords
72{
73  const char *name;
74  int tok;
75};
76
77#define K(KEY)  { #KEY, KEY }
78#define KRT(KEY)  { #KEY, RT_##KEY }
79
80static const struct rclex_keywords keywds[] =
81{
82  K(ACCELERATORS), K(ALT), K(ANICURSOR), K(ANIICON), K(ASCII),
83  K(AUTO3STATE), K(AUTOCHECKBOX), K(AUTORADIOBUTTON),
84  K(BEDIT), { "BEGIN", BEG }, K(BITMAP), K(BLOCK), K(BUTTON),
85  K(CAPTION), K(CHARACTERISTICS), K(CHECKBOX), K(CHECKED),
86  K(CLASS), K(COMBOBOX), K(CONTROL), K(CTEXT), K(CURSOR),
87  K(DEFPUSHBUTTON), K(DIALOG), K(DIALOGEX), K(DISCARDABLE),
88  K(DLGINCLUDE), K(DLGINIT),
89  K(EDITTEXT), K(END), K(EXSTYLE),
90  K(FILEFLAGS), K(FILEFLAGSMASK), K(FILEOS), K(FILESUBTYPE),
91  K(FILETYPE), K(FILEVERSION), K(FIXED), K(FONT), K(FONTDIR),
92  K(GRAYED), KRT(GROUP_CURSOR), KRT(GROUP_ICON), K(GROUPBOX),
93  K(HEDIT), K(HELP), K(HTML),
94  K(ICON), K(IEDIT), K(IMPURE), K(INACTIVE),
95  K(LANGUAGE), K(LISTBOX), K(LOADONCALL), K(LTEXT),
96  K(MANIFEST), K(MENU), K(MENUBARBREAK), K(MENUBREAK),
97  K(MENUEX), K(MENUITEM), K(MESSAGETABLE), K(MOVEABLE),
98  K(NOINVERT), K(NOT),
99  K(PLUGPLAY), K(POPUP), K(PRELOAD), K(PRODUCTVERSION),
100  K(PURE), K(PUSHBOX), K(PUSHBUTTON),
101  K(RADIOBUTTON), K(RCDATA), K(RTEXT),
102  K(SCROLLBAR), K(SEPARATOR), K(SHIFT), K(STATE3),
103  K(STRINGTABLE), K(STYLE),
104  K(TOOLBAR),
105  K(USERBUTTON),
106  K(VALUE), { "VERSION", VERSIONK }, K(VERSIONINFO),
107  K(VIRTKEY), K(VXD),
108  { NULL, 0 },
109};
110
111/* External input stream from resrc */
112extern FILE *cpp_pipe;
113
114/* Lexical scanner helpers.  */
115static int rclex_lastch = -1;
116static size_t rclex_tok_max = 0;
117static size_t rclex_tok_pos = 0;
118static char *rclex_tok = NULL;
119
120static int
121rclex_translatekeyword (const char *key)
122{
123  if (key && ISUPPER (key[0]))
124    {
125      const struct rclex_keywords *kw = &keywds[0];
126
127      do
128        {
129	  if (! strcmp (kw->name, key))
130	    return kw->tok;
131	  ++kw;
132        }
133      while (kw->name != NULL);
134    }
135  return STRING;
136}
137
138/* Handle a C preprocessor line.  */
139
140static void
141cpp_line (void)
142{
143  const char *s = rclex_tok;
144  int line;
145  char *send, *fn;
146  size_t len, mlen;
147
148  ++s;
149  while (ISSPACE (*s))
150    ++s;
151
152  /* Check for #pragma code_page ( DEFAULT | <nr>).  */
153  len = strlen (s);
154  mlen = strlen ("pragma");
155  if (len > mlen && memcmp (s, "pragma", mlen) == 0 && ISSPACE (s[mlen]))
156    {
157      const char *end;
158
159      s += mlen + 1;
160      while (ISSPACE (*s))
161	++s;
162      len = strlen (s);
163      mlen = strlen ("code_page");
164      if (len <= mlen || memcmp (s, "code_page", mlen) != 0)
165	/* FIXME: We ought to issue a warning message about an unrecognised pragma.  */
166	return;
167      s += mlen;
168      while (ISSPACE (*s))
169	++s;
170      if (*s != '(')
171	/* FIXME: We ought to issue an error message about a malformed pragma.  */
172	return;
173      ++s;
174      while (ISSPACE (*s))
175	++s;
176      if (*s == 0 || (end = strchr (s, ')')) == NULL)
177	/* FIXME: We ought to issue an error message about a malformed pragma.  */
178	return;
179      len = (size_t) (end - s);
180      fn = xmalloc (len + 1);
181      if (len)
182      	memcpy (fn, s, len);
183      fn[len] = 0;
184      while (len > 0 && (fn[len - 1] > 0 && fn[len - 1] <= 0x20))
185	fn[--len] = 0;
186      if (! len || (len == strlen ("DEFAULT") && strcasecmp (fn, "DEFAULT") == 0))
187	wind_current_codepage = wind_default_codepage;
188      else if (len > 0)
189	{
190	  rc_uint_type ncp;
191
192	  if (fn[0] == '0' && (fn[1] == 'x' || fn[1] == 'X'))
193	      ncp = (rc_uint_type) strtol (fn + 2, NULL, 16);
194	  else
195	      ncp = (rc_uint_type) strtol (fn, NULL, 10);
196	  if (ncp == CP_UTF16 || ! unicode_is_valid_codepage (ncp))
197	    fatal (_("invalid value specified for pragma code_page.\n"));
198	  wind_current_codepage = ncp;
199	}
200      free (fn);
201      return;
202    }
203
204  line = strtol (s, &send, 0);
205  if (*send != '\0' && ! ISSPACE (*send))
206    return;
207
208  /* Subtract 1 because we are about to count the newline.  */
209  rc_lineno = line - 1;
210
211  s = send;
212  while (ISSPACE (*s))
213    ++s;
214
215  if (*s != '"')
216    return;
217
218  ++s;
219  send = strchr (s, '"');
220  if (send == NULL)
221    return;
222
223  fn = xmalloc (send - s + 1);
224  strncpy (fn, s, send - s);
225  fn[send - s] = '\0';
226
227  free (rc_filename);
228  rc_filename = fn;
229
230  if (! initial_fn)
231    {
232      initial_fn = xmalloc (strlen (fn) + 1);
233      strcpy (initial_fn, fn);
234    }
235
236  /* Allow the initial file, regardless of name.  Suppress all other
237     files if they end in ".h" (this allows included "*.rc").  */
238  if (strcmp (initial_fn, fn) == 0
239      || strcmp (fn + strlen (fn) - 2, ".h") != 0)
240    suppress_cpp_data = 0;
241  else
242    suppress_cpp_data = 1;
243}
244
245/* Allocate a string of a given length.  */
246
247static char *
248get_string (int len)
249{
250  struct alloc_string *as;
251
252  as = xmalloc (sizeof *as);
253  as->s = xmalloc (len);
254
255  as->next = strings;
256  strings = as;
257
258  return as->s;
259}
260
261/* Handle a quoted string.  The quotes are stripped.  A pair of quotes
262   in a string are turned into a single quote.  Adjacent strings are
263   merged separated by whitespace are merged, as in C.  */
264
265static char *
266handle_quotes (rc_uint_type *len)
267{
268  const char *input = rclex_tok;
269  char *ret, *s;
270  const char *t;
271  int ch;
272  int num_xdigits;
273
274  ret = get_string (strlen (input) + 1);
275
276  s = ret;
277  t = input;
278  if (*t == '"')
279    ++t;
280  while (*t != '\0')
281    {
282      if (*t == '\\')
283	{
284	  ++t;
285	  switch (*t)
286	    {
287	    case '\0':
288	      rcparse_warning ("backslash at end of string");
289	      break;
290
291	    case '\"':
292	      rcparse_warning ("use \"\" to put \" in a string");
293	      *s++ = '"';
294	      ++t;
295	      break;
296
297	    case 'a':
298	      *s++ = ESCAPE_B; /* Strange, but true...  */
299	      ++t;
300	      break;
301
302	    case 'b':
303	      *s++ = ESCAPE_B;
304	      ++t;
305	      break;
306
307	    case 'f':
308	      *s++ = ESCAPE_F;
309	      ++t;
310	      break;
311
312	    case 'n':
313	      *s++ = ESCAPE_N;
314	      ++t;
315	      break;
316
317	    case 'r':
318	      *s++ = ESCAPE_R;
319	      ++t;
320	      break;
321
322	    case 't':
323	      *s++ = ESCAPE_T;
324	      ++t;
325	      break;
326
327	    case 'v':
328	      *s++ = ESCAPE_V;
329	      ++t;
330	      break;
331
332	    case '\\':
333	      *s++ = *t++;
334	      break;
335
336	    case '0': case '1': case '2': case '3':
337	    case '4': case '5': case '6': case '7':
338	      ch = *t - '0';
339	      ++t;
340	      if (*t >= '0' && *t <= '7')
341		{
342		  ch = (ch << 3) | (*t - '0');
343		  ++t;
344		  if (*t >= '0' && *t <= '7')
345		    {
346		      ch = (ch << 3) | (*t - '0');
347		      ++t;
348		    }
349		}
350	      *s++ = ch;
351	      break;
352
353	    case 'x': case 'X':
354	      ++t;
355	      ch = 0;
356	      /* We only handle single byte chars here.  Make sure
357		 we finish an escape sequence like "/xB0ABC" after
358		 the first two digits.  */
359              num_xdigits = 2;
360 	      while (num_xdigits--)
361		{
362		  if (*t >= '0' && *t <= '9')
363		    ch = (ch << 4) | (*t - '0');
364		  else if (*t >= 'a' && *t <= 'f')
365		    ch = (ch << 4) | (*t - 'a' + 10);
366		  else if (*t >= 'A' && *t <= 'F')
367		    ch = (ch << 4) | (*t - 'A' + 10);
368		  else
369		    break;
370		  ++t;
371		}
372	      *s++ = ch;
373	      break;
374
375	    default:
376	      rcparse_warning ("unrecognized escape sequence");
377	      *s++ = '\\';
378	      *s++ = *t++;
379	      break;
380	    }
381	}
382      else if (*t != '"')
383	*s++ = *t++;
384      else if (t[1] == '\0')
385	break;
386      else if (t[1] == '"')
387	{
388	  *s++ = '"';
389	  t += 2;
390	}
391      else
392	{
393	  ++t;
394	  if (! ISSPACE (*t))
395	    rcparse_warning ("unexpected character after '\"'");
396	  while (ISSPACE (*t))
397	    {
398	      if ((*t) == '\n')
399		++rc_lineno;
400	      ++t;
401	    }
402	  if (*t == '\0')
403	    break;
404	  assert (*t == '"');
405	  ++t;
406	}
407    }
408
409  *s = '\0';
410
411  *len = s - ret;
412
413  return ret;
414}
415
416/* Allocate a unicode string of a given length.  */
417
418static unichar *
419get_unistring (int len)
420{
421  return (unichar *) get_string (len * sizeof (unichar));
422}
423
424/* Handle a quoted unicode string.  The quotes are stripped.  A pair of quotes
425   in a string are turned into a single quote.  Adjacent strings are
426   merged separated by whitespace are merged, as in C.  */
427
428static unichar *
429handle_uniquotes (rc_uint_type *len)
430{
431  const char *input = rclex_tok;
432  unichar *ret, *s;
433  const char *t;
434  int ch;
435  int num_xdigits;
436
437  ret = get_unistring (strlen (input) + 1);
438
439  s = ret;
440  t = input;
441  if ((*t == 'L' || *t == 'l') && t[1] == '"')
442    t += 2;
443  else if (*t == '"')
444    ++t;
445  while (*t != '\0')
446    {
447      if (*t == '\\')
448	{
449	  ++t;
450	  switch (*t)
451	    {
452	    case '\0':
453	      rcparse_warning ("backslash at end of string");
454	      break;
455
456	    case '\"':
457	      rcparse_warning ("use \"\" to put \" in a string");
458	      break;
459
460	    case 'a':
461	      *s++ = ESCAPE_B; /* Strange, but true...  */
462	      ++t;
463	      break;
464
465	    case 'b':
466	      *s++ = ESCAPE_B;
467	      ++t;
468	      break;
469
470	    case 'f':
471	      *s++ = ESCAPE_F;
472	      ++t;
473	      break;
474
475	    case 'n':
476	      *s++ = ESCAPE_N;
477	      ++t;
478	      break;
479
480	    case 'r':
481	      *s++ = ESCAPE_R;
482	      ++t;
483	      break;
484
485	    case 't':
486	      *s++ = ESCAPE_T;
487	      ++t;
488	      break;
489
490	    case 'v':
491	      *s++ = ESCAPE_V;
492	      ++t;
493	      break;
494
495	    case '\\':
496	      *s++ = (unichar) *t++;
497	      break;
498
499	    case '0': case '1': case '2': case '3':
500	    case '4': case '5': case '6': case '7':
501	      ch = *t - '0';
502	      ++t;
503	      if (*t >= '0' && *t <= '7')
504		{
505		  ch = (ch << 3) | (*t - '0');
506		  ++t;
507		  if (*t >= '0' && *t <= '7')
508		    {
509		      ch = (ch << 3) | (*t - '0');
510		      ++t;
511		    }
512		}
513	      *s++ = (unichar) ch;
514	      break;
515
516	    case 'x': case 'X':
517	      ++t;
518	      ch = 0;
519	      /* We only handle two byte chars here.  Make sure
520		 we finish an escape sequence like "/xB0ABC" after
521		 the first two digits.  */
522              num_xdigits = 4;
523 	      while (num_xdigits--)
524		{
525		  if (*t >= '0' && *t <= '9')
526		    ch = (ch << 4) | (*t - '0');
527		  else if (*t >= 'a' && *t <= 'f')
528		    ch = (ch << 4) | (*t - 'a' + 10);
529		  else if (*t >= 'A' && *t <= 'F')
530		    ch = (ch << 4) | (*t - 'A' + 10);
531		  else
532		    break;
533		  ++t;
534		}
535	      *s++ = (unichar) ch;
536	      break;
537
538	    default:
539	      rcparse_warning ("unrecognized escape sequence");
540	      *s++ = '\\';
541	      *s++ = (unichar) *t++;
542	      break;
543	    }
544	}
545      else if (*t != '"')
546	*s++ = (unichar) *t++;
547      else if (t[1] == '\0')
548	break;
549      else if (t[1] == '"')
550	{
551	  *s++ = '"';
552	  t += 2;
553	}
554      else
555	{
556	  ++t;
557	  assert (ISSPACE (*t));
558	  while (ISSPACE (*t))
559	    {
560	      if ((*t) == '\n')
561		++rc_lineno;
562	      ++t;
563	    }
564	  if (*t == '\0')
565	    break;
566	  assert (*t == '"');
567	  ++t;
568	}
569    }
570
571  *s = '\0';
572
573  *len = s - ret;
574
575  return ret;
576}
577
578/* Discard all the strings we have allocated.  The parser calls this
579   when it no longer needs them.  */
580
581void
582rcparse_discard_strings (void)
583{
584  struct alloc_string *as;
585
586  as = strings;
587  while (as != NULL)
588    {
589      struct alloc_string *n;
590
591      free (as->s);
592      n = as->next;
593      free (as);
594      as = n;
595    }
596
597  strings = NULL;
598}
599
600/* Enter rcdata mode.  */
601void
602rcparse_rcdata (void)
603{
604  rcdata_mode = 1;
605}
606
607/* Go back to normal mode from rcdata mode.  */
608void
609rcparse_normal (void)
610{
611  rcdata_mode = 0;
612}
613
614static void
615rclex_tok_add_char (int ch)
616{
617  if (! rclex_tok || rclex_tok_max <= rclex_tok_pos)
618    {
619      char *h = xmalloc (rclex_tok_max + 9);
620
621      if (! h)
622	abort ();
623      if (rclex_tok)
624	{
625	  memcpy (h, rclex_tok, rclex_tok_pos + 1);
626	  free (rclex_tok);
627	}
628      else
629	rclex_tok_pos = 0;
630      rclex_tok_max += 8;
631      rclex_tok = h;
632    }
633  if (ch != -1)
634    rclex_tok[rclex_tok_pos++] = (char) ch;
635  rclex_tok[rclex_tok_pos] = 0;
636}
637
638static int
639rclex_readch (void)
640{
641  int r = -1;
642
643  if ((r = rclex_lastch) != -1)
644    rclex_lastch = -1;
645  else
646    {
647      char ch;
648      do
649        {
650	  if (! cpp_pipe || feof (cpp_pipe)
651	      || fread (&ch, 1, 1,cpp_pipe) != 1)
652	    break;
653	  r = ((int) ch) & 0xff;
654        }
655      while (r == 0 || r == '\r');
656  }
657  rclex_tok_add_char (r);
658  return r;
659}
660
661static int
662rclex_peekch (void)
663{
664  int r;
665
666  if ((r = rclex_lastch) == -1)
667    {
668      if ((r = rclex_readch ()) != -1)
669	{
670	  rclex_lastch = r;
671	  if (rclex_tok_pos > 0)
672	    rclex_tok[--rclex_tok_pos] = 0;
673	}
674    }
675  return r;
676}
677
678static void
679rclex_string (void)
680{
681  int c;
682
683  while ((c = rclex_peekch ()) != -1)
684    {
685      if (c == '\n')
686	break;
687      if (c == '\\')
688        {
689	  rclex_readch ();
690	  if ((c = rclex_peekch ()) == -1 || c == '\n')
691	    break;
692	  rclex_readch ();
693        }
694      else if (rclex_readch () == '"')
695	{
696	  /* PR 6714
697	     Skip any whitespace after the end of the double quotes.  */
698	  do
699	    {
700	      c = rclex_peekch ();
701	      if (ISSPACE (c))
702		rclex_readch ();
703	      else
704		c = -1;
705	    }
706	  while (c != -1);
707
708	  if (rclex_peekch () == '"')
709	    rclex_readch ();
710	  else
711	    break;
712	}
713    }
714}
715
716static rc_uint_type
717read_digit (int ch)
718{
719  rc_uint_type base = 10;
720  rc_uint_type ret, val;
721  int warned = 0;
722
723  ret = 0;
724  if (ch == '0')
725    {
726      base = 8;
727      switch (rclex_peekch ())
728	{
729	case 'o': case 'O':
730	  rclex_readch ();
731	  base = 8;
732	  break;
733
734	case 'x': case 'X':
735	  rclex_readch ();
736	  base = 16;
737	  break;
738	}
739    }
740  else
741    ret = (rc_uint_type) (ch - '0');
742  while ((ch = rclex_peekch ()) != -1)
743    {
744      if (ISDIGIT (ch))
745	val = (rc_uint_type) (ch - '0');
746      else if (ch >= 'a' && ch <= 'f')
747	val = (rc_uint_type) ((ch - 'a') + 10);
748      else if (ch >= 'A' && ch <= 'F')
749	val = (rc_uint_type) ((ch - 'A') + 10);
750      else
751	break;
752      rclex_readch ();
753      if (! warned && val >= base)
754	{
755	  warned = 1;
756	  rcparse_warning ("digit exceeds base");
757	}
758      ret *= base;
759      ret += val;
760    }
761  return ret;
762}
763
764/* yyparser entry method.  */
765
766int
767yylex (void)
768{
769  char *s;
770  unichar *us;
771  rc_uint_type length;
772  int ch;
773
774  /* Make sure that rclex_tok is initialized.  */
775  if (! rclex_tok)
776    rclex_tok_add_char (-1);
777
778  do
779    {
780      do
781	{
782	  /* Clear token.  */
783	  rclex_tok_pos = 0;
784	  rclex_tok[0] = 0;
785
786	  if ((ch = rclex_readch ()) == -1)
787	    return -1;
788	  if (ch == '\n')
789	    ++rc_lineno;
790	}
791      while (ch <= 0x20);
792
793      switch (ch)
794	{
795	case '#':
796	  while ((ch = rclex_peekch ()) != -1 && ch != '\n')
797	    rclex_readch ();
798	  cpp_line ();
799	  ch = IGNORED_TOKEN;
800	  break;
801
802	case '{':
803	  ch = IGNORE_CPP (BEG);
804	  break;
805
806	case '}':
807	  ch = IGNORE_CPP (END);
808	  break;
809
810	case '0': case '1': case '2': case '3': case '4':
811	case '5': case '6': case '7': case '8': case '9':
812	  yylval.i.val = read_digit (ch);
813	  yylval.i.dword = 0;
814	  switch (rclex_peekch ())
815	    {
816	    case 'l': case 'L':
817	      rclex_readch ();
818	      yylval.i.dword = 1;
819	      break;
820	    }
821	  ch = IGNORE_CPP (NUMBER);
822	  break;
823	case '"':
824	  rclex_string ();
825	  ch = IGNORE_CPP ((! rcdata_mode ? QUOTEDSTRING : SIZEDSTRING));
826	  if (ch == IGNORED_TOKEN)
827	    break;
828	  s = handle_quotes (&length);
829	  if (! rcdata_mode)
830	    yylval.s = s;
831	  else
832	    {
833	      yylval.ss.length = length;
834	      yylval.ss.s = s;
835	  }
836	  break;
837	case 'L': case 'l':
838	  if (rclex_peekch () == '"')
839	    {
840	      rclex_readch ();
841	      rclex_string ();
842	      ch = IGNORE_CPP ((! rcdata_mode ? QUOTEDUNISTRING : SIZEDUNISTRING));
843	      if (ch == IGNORED_TOKEN)
844		break;
845	      us = handle_uniquotes (&length);
846	      if (! rcdata_mode)
847		yylval.uni = us;
848	      else
849	        {
850		  yylval.suni.length = length;
851		  yylval.suni.s = us;
852	      }
853	      break;
854	    }
855	  /* Fall through.  */
856	default:
857	  if (ISIDST (ch) || ch=='$')
858	    {
859	      while ((ch = rclex_peekch ()) != -1
860		     && (ISIDNUM (ch) || ch == '$' || ch == '.'
861		         || ch == ':' || ch == '\\' || ch == '/'
862		         || ch == '_' || ch == '-')
863		    )
864		rclex_readch ();
865	      ch = IGNORE_CPP (rclex_translatekeyword (rclex_tok));
866	      if (ch == STRING)
867		{
868		  s = get_string (strlen (rclex_tok) + 1);
869		  strcpy (s, rclex_tok);
870		  yylval.s = s;
871		}
872	      else if (ch == BLOCK)
873		{
874		  const char *hs = NULL;
875
876		  switch (yylex ())
877		  {
878		  case STRING:
879		  case QUOTEDSTRING:
880		    hs = yylval.s;
881		    break;
882		  case SIZEDSTRING:
883		    hs = yylval.s = yylval.ss.s;
884		    break;
885		  }
886		  if (! hs)
887		    {
888		      rcparse_warning ("BLOCK expects a string as argument.");
889		      ch = IGNORED_TOKEN;
890		    }
891		  else if (! strcmp (hs, "StringFileInfo"))
892		    ch = BLOCKSTRINGFILEINFO;
893		  else if (! strcmp (hs, "VarFileInfo"))
894		    ch = BLOCKVARFILEINFO;
895		}
896	      break;
897	    }
898	  ch = IGNORE_CPP (ch);
899	  break;
900	}
901    }
902  while (ch == IGNORED_TOKEN);
903
904  return ch;
905}
906