Home | History | Annotate | Download | only in binutils
      1 /* rclex.c -- lexer for Windows rc files parser  */
      2 
      3 /* Copyright (C) 1997-2014 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 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 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
    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
    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 *
    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 *
    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 *
    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 *
    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
    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
    601 rcparse_rcdata (void)
    602 {
    603   rcdata_mode = 1;
    604 }
    605 
    606 /* Go back to normal mode from rcdata mode.  */
    607 void
    608 rcparse_normal (void)
    609 {
    610   rcdata_mode = 0;
    611 }
    612 
    613 static void
    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
    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
    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
    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
    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
    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