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