1218822Sdim/* rclex.c -- lexer for Windows rc files parser  */
238889Sjdp
3218822Sdim/* Copyright 1997, 1998, 1999, 2001, 2002, 2003, 2005, 2006, 2007
4218822Sdim   Free Software Foundation, Inc.
538889Sjdp
6218822Sdim   Written by Kai Tietz, Onevision.
738889Sjdp
838889Sjdp   This file is part of GNU Binutils.
938889Sjdp
1038889Sjdp   This program is free software; you can redistribute it and/or modify
1138889Sjdp   it under the terms of the GNU General Public License as published by
1238889Sjdp   the Free Software Foundation; either version 2 of the License, or
1338889Sjdp   (at your option) any later version.
1438889Sjdp
1538889Sjdp   This program is distributed in the hope that it will be useful,
1638889Sjdp   but WITHOUT ANY WARRANTY; without even the implied warranty of
1738889Sjdp   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1838889Sjdp   GNU General Public License for more details.
1938889Sjdp
2038889Sjdp   You should have received a copy of the GNU General Public License
2138889Sjdp   along with this program; if not, write to the Free Software
22218822Sdim   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
23218822Sdim   02110-1301, USA.  */
2438889Sjdp
25218822Sdim/* This is a lexer used by the Windows rc file parser.  It basically
26218822Sdim   just recognized a bunch of keywords.  */
2738889Sjdp
28218822Sdim#include "sysdep.h"
2938889Sjdp#include "bfd.h"
3038889Sjdp#include "bucomm.h"
3138889Sjdp#include "libiberty.h"
3292828Sobrien#include "safe-ctype.h"
3338889Sjdp#include "windres.h"
3438889Sjdp#include "rcparse.h"
3538889Sjdp
3638889Sjdp#include <assert.h>
3738889Sjdp
3838889Sjdp/* Whether we are in rcdata mode, in which we returns the lengths of
3938889Sjdp   strings.  */
4038889Sjdp
4138889Sjdpstatic int rcdata_mode;
4238889Sjdp
4361843Sobrien/* Whether we are supressing lines from cpp (including windows.h or
4461843Sobrien   headers from your C sources may bring in externs and typedefs).
4561843Sobrien   When active, we return IGNORED_TOKEN, which lets us ignore these
4661843Sobrien   outside of resource constructs.  Thus, it isn't required to protect
4761843Sobrien   all the non-preprocessor lines in your header files with #ifdef
4861843Sobrien   RC_INVOKED.  It also means your RC file can't include other RC
4961843Sobrien   files if they're named "*.h".  Sorry.  Name them *.rch or whatever.  */
5061843Sobrien
5161843Sobrienstatic int suppress_cpp_data;
5261843Sobrien
53218822Sdim#define IGNORE_CPP(x) (suppress_cpp_data ? IGNORED_TOKEN : (x))
5461843Sobrien
5561843Sobrien/* The first filename we detect in the cpp output.  We use this to
5661843Sobrien   tell included files from the original file.  */
5761843Sobrien
5861843Sobrienstatic char *initial_fn;
5961843Sobrien
6038889Sjdp/* List of allocated strings.  */
6138889Sjdp
6238889Sjdpstruct alloc_string
6338889Sjdp{
6438889Sjdp  struct alloc_string *next;
6538889Sjdp  char *s;
6638889Sjdp};
6738889Sjdp
6838889Sjdpstatic struct alloc_string *strings;
6938889Sjdp
70218822Sdimstruct rclex_keywords
71218822Sdim{
72218822Sdim  const char *name;
73218822Sdim  int tok;
74218822Sdim};
7538889Sjdp
76218822Sdim#define K(KEY)  { #KEY, KEY }
77218822Sdim#define KRT(KEY)  { #KEY, RT_##KEY }
7838889Sjdp
79218822Sdimstatic const struct rclex_keywords keywds[] =
8038889Sjdp{
81218822Sdim  K(ACCELERATORS), K(ALT), K(ANICURSOR), K(ANIICON), K(ASCII),
82218822Sdim  K(AUTO3STATE), K(AUTOCHECKBOX), K(AUTORADIOBUTTON),
83218822Sdim  K(BEDIT), { "BEGIN", BEG }, K(BITMAP), K(BLOCK), K(BUTTON),
84218822Sdim  K(CAPTION), K(CHARACTERISTICS), K(CHECKBOX), K(CHECKED),
85218822Sdim  K(CLASS), K(COMBOBOX), K(CONTROL), K(CTEXT), K(CURSOR),
86218822Sdim  K(DEFPUSHBUTTON), K(DIALOG), K(DIALOGEX), K(DISCARDABLE),
87218822Sdim  K(DLGINCLUDE), K(DLGINIT),
88218822Sdim  K(EDITTEXT), K(END), K(EXSTYLE),
89218822Sdim  K(FILEFLAGS), K(FILEFLAGSMASK), K(FILEOS), K(FILESUBTYPE),
90218822Sdim  K(FILETYPE), K(FILEVERSION), K(FIXED), K(FONT), K(FONTDIR),
91218822Sdim  K(GRAYED), KRT(GROUP_CURSOR), KRT(GROUP_ICON), K(GROUPBOX),
92218822Sdim  K(HEDIT), K(HELP), K(HTML),
93218822Sdim  K(ICON), K(IEDIT), K(IMPURE), K(INACTIVE),
94218822Sdim  K(LANGUAGE), K(LISTBOX), K(LOADONCALL), K(LTEXT),
95218822Sdim  K(MANIFEST), K(MENU), K(MENUBARBREAK), K(MENUBREAK),
96218822Sdim  K(MENUEX), K(MENUITEM), K(MESSAGETABLE), K(MOVEABLE),
97218822Sdim  K(NOINVERT), K(NOT),
98218822Sdim  K(PLUGPLAY), K(POPUP), K(PRELOAD), K(PRODUCTVERSION),
99218822Sdim  K(PURE), K(PUSHBOX), K(PUSHBUTTON),
100218822Sdim  K(RADIOBUTTON), K(RCDATA), K(RTEXT),
101218822Sdim  K(SCROLLBAR), K(SEPARATOR), K(SHIFT), K(STATE3),
102218822Sdim  K(STRINGTABLE), K(STYLE),
103218822Sdim  K(TOOLBAR),
104218822Sdim  K(USERBUTTON),
105218822Sdim  K(VALUE), { "VERSION", VERSIONK }, K(VERSIONINFO),
106218822Sdim  K(VIRTKEY), K(VXD),
107218822Sdim  { NULL, 0 },
108218822Sdim};
10938889Sjdp
110218822Sdim/* External input stream from resrc */
111218822Sdimextern FILE *cpp_pipe;
11238889Sjdp
113218822Sdim/* Lexical scanner helpers.  */
114218822Sdimstatic int rclex_lastch = -1;
115218822Sdimstatic size_t rclex_tok_max = 0;
116218822Sdimstatic size_t rclex_tok_pos = 0;
117218822Sdimstatic char *rclex_tok = NULL;
11838889Sjdp
119218822Sdimstatic int
120218822Sdimrclex_translatekeyword (const char *key)
12138889Sjdp{
122218822Sdim  if (key && ISUPPER (key[0]))
123218822Sdim    {
124218822Sdim      const struct rclex_keywords *kw = &keywds[0];
12538889Sjdp
126218822Sdim      do
127218822Sdim        {
128218822Sdim	  if (! strcmp (kw->name, key))
129218822Sdim	    return kw->tok;
130218822Sdim	  ++kw;
131218822Sdim        }
132218822Sdim      while (kw->name != NULL);
133218822Sdim    }
134218822Sdim  return STRING;
13538889Sjdp}
13638889Sjdp
13738889Sjdp/* Handle a C preprocessor line.  */
13838889Sjdp
13938889Sjdpstatic void
140218822Sdimcpp_line (void)
14138889Sjdp{
142218822Sdim  const char *s = rclex_tok;
14338889Sjdp  int line;
14438889Sjdp  char *send, *fn;
145218822Sdim  size_t len, mlen;
14638889Sjdp
14738889Sjdp  ++s;
14892828Sobrien  while (ISSPACE (*s))
14938889Sjdp    ++s;
15038889Sjdp
151218822Sdim  /* Check for #pragma code_page ( DEFAULT | <nr>).  */
152218822Sdim  len = strlen (s);
153218822Sdim  mlen = strlen ("pragma");
154218822Sdim  if (len > mlen && memcmp (s, "pragma", mlen) == 0 && ISSPACE (s[mlen]))
155218822Sdim    {
156218822Sdim      const char *end;
157218822Sdim
158218822Sdim      s += mlen + 1;
159218822Sdim      while (ISSPACE (*s))
160218822Sdim	++s;
161218822Sdim      len = strlen (s);
162218822Sdim      mlen = strlen ("code_page");
163218822Sdim      if (len <= mlen || memcmp (s, "code_page", mlen) != 0)
164218822Sdim	/* FIXME: We ought to issue a warning message about an unrecognised pragma.  */
165218822Sdim	return;
166218822Sdim      s += mlen;
167218822Sdim      while (ISSPACE (*s))
168218822Sdim	++s;
169218822Sdim      if (*s != '(')
170218822Sdim	/* FIXME: We ought to issue an error message about a malformed pragma.  */
171218822Sdim	return;
172218822Sdim      ++s;
173218822Sdim      while (ISSPACE (*s))
174218822Sdim	++s;
175218822Sdim      if (*s == 0 || (end = strchr (s, ')')) == NULL)
176218822Sdim	/* FIXME: We ought to issue an error message about a malformed pragma.  */
177218822Sdim	return;
178218822Sdim      len = (size_t) (end - s);
179218822Sdim      fn = xmalloc (len + 1);
180218822Sdim      if (len)
181218822Sdim      	memcpy (fn, s, len);
182218822Sdim      fn[len] = 0;
183218822Sdim      while (len > 0 && (fn[len - 1] > 0 && fn[len - 1] <= 0x20))
184218822Sdim	fn[--len] = 0;
185218822Sdim      if (! len || (len == strlen ("DEFAULT") && strcasecmp (fn, "DEFAULT") == 0))
186218822Sdim	wind_current_codepage = wind_default_codepage;
187218822Sdim      else if (len > 0)
188218822Sdim	{
189218822Sdim	  rc_uint_type ncp;
190218822Sdim
191218822Sdim	  if (fn[0] == '0' && (fn[1] == 'x' || fn[1] == 'X'))
192218822Sdim	      ncp = (rc_uint_type) strtol (fn + 2, NULL, 16);
193218822Sdim	  else
194218822Sdim	      ncp = (rc_uint_type) strtol (fn, NULL, 10);
195218822Sdim	  if (ncp == CP_UTF16 || ! unicode_is_valid_codepage (ncp))
196218822Sdim	    fatal (_("invalid value specified for pragma code_page.\n"));
197218822Sdim	  wind_current_codepage = ncp;
198218822Sdim	}
199218822Sdim      free (fn);
200218822Sdim      return;
201218822Sdim    }
202218822Sdim
20338889Sjdp  line = strtol (s, &send, 0);
20492828Sobrien  if (*send != '\0' && ! ISSPACE (*send))
20538889Sjdp    return;
20638889Sjdp
20738889Sjdp  /* Subtract 1 because we are about to count the newline.  */
20838889Sjdp  rc_lineno = line - 1;
20938889Sjdp
21038889Sjdp  s = send;
21192828Sobrien  while (ISSPACE (*s))
21238889Sjdp    ++s;
21338889Sjdp
21438889Sjdp  if (*s != '"')
21538889Sjdp    return;
21638889Sjdp
21738889Sjdp  ++s;
21838889Sjdp  send = strchr (s, '"');
21938889Sjdp  if (send == NULL)
22038889Sjdp    return;
22138889Sjdp
222218822Sdim  fn = xmalloc (send - s + 1);
22338889Sjdp  strncpy (fn, s, send - s);
22438889Sjdp  fn[send - s] = '\0';
22538889Sjdp
22638889Sjdp  free (rc_filename);
22738889Sjdp  rc_filename = fn;
22861843Sobrien
229218822Sdim  if (! initial_fn)
23061843Sobrien    {
23161843Sobrien      initial_fn = xmalloc (strlen (fn) + 1);
23299461Sobrien      strcpy (initial_fn, fn);
23361843Sobrien    }
23461843Sobrien
23561843Sobrien  /* Allow the initial file, regardless of name.  Suppress all other
23699461Sobrien     files if they end in ".h" (this allows included "*.rc").  */
23761843Sobrien  if (strcmp (initial_fn, fn) == 0
23861843Sobrien      || strcmp (fn + strlen (fn) - 2, ".h") != 0)
23961843Sobrien    suppress_cpp_data = 0;
24061843Sobrien  else
24161843Sobrien    suppress_cpp_data = 1;
24238889Sjdp}
24338889Sjdp
244218822Sdim/* Allocate a string of a given length.  */
245218822Sdim
246218822Sdimstatic char *
247218822Sdimget_string (int len)
248218822Sdim{
249218822Sdim  struct alloc_string *as;
250218822Sdim
251218822Sdim  as = xmalloc (sizeof *as);
252218822Sdim  as->s = xmalloc (len);
253218822Sdim
254218822Sdim  as->next = strings;
255218822Sdim  strings = as;
256218822Sdim
257218822Sdim  return as->s;
258218822Sdim}
259218822Sdim
26038889Sjdp/* Handle a quoted string.  The quotes are stripped.  A pair of quotes
26138889Sjdp   in a string are turned into a single quote.  Adjacent strings are
26238889Sjdp   merged separated by whitespace are merged, as in C.  */
26338889Sjdp
26438889Sjdpstatic char *
265218822Sdimhandle_quotes (rc_uint_type *len)
26638889Sjdp{
267218822Sdim  const char *input = rclex_tok;
26838889Sjdp  char *ret, *s;
26938889Sjdp  const char *t;
27038889Sjdp  int ch;
271218822Sdim  int num_xdigits;
27238889Sjdp
27338889Sjdp  ret = get_string (strlen (input) + 1);
27438889Sjdp
27538889Sjdp  s = ret;
27638889Sjdp  t = input;
27738889Sjdp  if (*t == '"')
27838889Sjdp    ++t;
27938889Sjdp  while (*t != '\0')
28038889Sjdp    {
28138889Sjdp      if (*t == '\\')
28238889Sjdp	{
28338889Sjdp	  ++t;
28438889Sjdp	  switch (*t)
28538889Sjdp	    {
28638889Sjdp	    case '\0':
28738889Sjdp	      rcparse_warning ("backslash at end of string");
28838889Sjdp	      break;
28938889Sjdp
29038889Sjdp	    case '\"':
29138889Sjdp	      rcparse_warning ("use \"\" to put \" in a string");
292218822Sdim	      *s++ = '"';
293218822Sdim	      ++t;
29438889Sjdp	      break;
29538889Sjdp
29638889Sjdp	    case 'a':
29799461Sobrien	      *s++ = ESCAPE_B; /* Strange, but true...  */
29838889Sjdp	      ++t;
29938889Sjdp	      break;
30038889Sjdp
30138889Sjdp	    case 'b':
30238889Sjdp	      *s++ = ESCAPE_B;
30338889Sjdp	      ++t;
30438889Sjdp	      break;
30538889Sjdp
30638889Sjdp	    case 'f':
30738889Sjdp	      *s++ = ESCAPE_F;
30838889Sjdp	      ++t;
30938889Sjdp	      break;
31038889Sjdp
31138889Sjdp	    case 'n':
31238889Sjdp	      *s++ = ESCAPE_N;
31338889Sjdp	      ++t;
31438889Sjdp	      break;
31538889Sjdp
31638889Sjdp	    case 'r':
31738889Sjdp	      *s++ = ESCAPE_R;
31838889Sjdp	      ++t;
31938889Sjdp	      break;
32038889Sjdp
32138889Sjdp	    case 't':
32238889Sjdp	      *s++ = ESCAPE_T;
32338889Sjdp	      ++t;
32438889Sjdp	      break;
32538889Sjdp
32638889Sjdp	    case 'v':
32738889Sjdp	      *s++ = ESCAPE_V;
32838889Sjdp	      ++t;
32938889Sjdp	      break;
33038889Sjdp
33138889Sjdp	    case '\\':
33238889Sjdp	      *s++ = *t++;
33338889Sjdp	      break;
33438889Sjdp
33538889Sjdp	    case '0': case '1': case '2': case '3':
33638889Sjdp	    case '4': case '5': case '6': case '7':
33738889Sjdp	      ch = *t - '0';
33838889Sjdp	      ++t;
33938889Sjdp	      if (*t >= '0' && *t <= '7')
34038889Sjdp		{
34138889Sjdp		  ch = (ch << 3) | (*t - '0');
34238889Sjdp		  ++t;
34338889Sjdp		  if (*t >= '0' && *t <= '7')
34438889Sjdp		    {
34538889Sjdp		      ch = (ch << 3) | (*t - '0');
34638889Sjdp		      ++t;
34738889Sjdp		    }
34838889Sjdp		}
34938889Sjdp	      *s++ = ch;
35038889Sjdp	      break;
35138889Sjdp
352218822Sdim	    case 'x': case 'X':
35338889Sjdp	      ++t;
35438889Sjdp	      ch = 0;
355218822Sdim	      /* We only handle single byte chars here.  Make sure
356218822Sdim		 we finish an escape sequence like "/xB0ABC" after
357218822Sdim		 the first two digits.  */
358218822Sdim              num_xdigits = 2;
359218822Sdim 	      while (num_xdigits--)
36038889Sjdp		{
36138889Sjdp		  if (*t >= '0' && *t <= '9')
36238889Sjdp		    ch = (ch << 4) | (*t - '0');
36338889Sjdp		  else if (*t >= 'a' && *t <= 'f')
36499461Sobrien		    ch = (ch << 4) | (*t - 'a' + 10);
36538889Sjdp		  else if (*t >= 'A' && *t <= 'F')
36699461Sobrien		    ch = (ch << 4) | (*t - 'A' + 10);
36738889Sjdp		  else
36838889Sjdp		    break;
36938889Sjdp		  ++t;
37038889Sjdp		}
37138889Sjdp	      *s++ = ch;
37238889Sjdp	      break;
37338889Sjdp
37438889Sjdp	    default:
37538889Sjdp	      rcparse_warning ("unrecognized escape sequence");
37638889Sjdp	      *s++ = '\\';
37738889Sjdp	      *s++ = *t++;
37838889Sjdp	      break;
37938889Sjdp	    }
38038889Sjdp	}
38138889Sjdp      else if (*t != '"')
38238889Sjdp	*s++ = *t++;
38338889Sjdp      else if (t[1] == '\0')
38438889Sjdp	break;
38538889Sjdp      else if (t[1] == '"')
38638889Sjdp	{
38738889Sjdp	  *s++ = '"';
38838889Sjdp	  t += 2;
38938889Sjdp	}
39038889Sjdp      else
39138889Sjdp	{
392218822Sdim	  rcparse_warning ("unexpected character after '\"'");
39338889Sjdp	  ++t;
39492828Sobrien	  assert (ISSPACE (*t));
39592828Sobrien	  while (ISSPACE (*t))
396130561Sobrien	    {
397130561Sobrien	      if ((*t) == '\n')
398130561Sobrien		++rc_lineno;
399130561Sobrien	      ++t;
400130561Sobrien	    }
40138889Sjdp	  if (*t == '\0')
40238889Sjdp	    break;
40338889Sjdp	  assert (*t == '"');
40438889Sjdp	  ++t;
40538889Sjdp	}
40638889Sjdp    }
40738889Sjdp
40838889Sjdp  *s = '\0';
40938889Sjdp
41038889Sjdp  *len = s - ret;
41138889Sjdp
41238889Sjdp  return ret;
41338889Sjdp}
41438889Sjdp
415218822Sdim/* Allocate a unicode string of a given length.  */
41638889Sjdp
417218822Sdimstatic unichar *
418218822Sdimget_unistring (int len)
41938889Sjdp{
420218822Sdim  return (unichar *) get_string (len * sizeof (unichar));
421218822Sdim}
42238889Sjdp
423218822Sdim/* Handle a quoted unicode string.  The quotes are stripped.  A pair of quotes
424218822Sdim   in a string are turned into a single quote.  Adjacent strings are
425218822Sdim   merged separated by whitespace are merged, as in C.  */
42638889Sjdp
427218822Sdimstatic unichar *
428218822Sdimhandle_uniquotes (rc_uint_type *len)
429218822Sdim{
430218822Sdim  const char *input = rclex_tok;
431218822Sdim  unichar *ret, *s;
432218822Sdim  const char *t;
433218822Sdim  int ch;
434218822Sdim  int num_xdigits;
43538889Sjdp
436218822Sdim  ret = get_unistring (strlen (input) + 1);
437218822Sdim
438218822Sdim  s = ret;
439218822Sdim  t = input;
440218822Sdim  if ((*t == 'L' || *t == 'l') && t[1] == '"')
441218822Sdim    t += 2;
442218822Sdim  else if (*t == '"')
443218822Sdim    ++t;
444218822Sdim  while (*t != '\0')
445218822Sdim    {
446218822Sdim      if (*t == '\\')
447218822Sdim	{
448218822Sdim	  ++t;
449218822Sdim	  switch (*t)
450218822Sdim	    {
451218822Sdim	    case '\0':
452218822Sdim	      rcparse_warning ("backslash at end of string");
453218822Sdim	      break;
454218822Sdim
455218822Sdim	    case '\"':
456218822Sdim	      rcparse_warning ("use \"\" to put \" in a string");
457218822Sdim	      break;
458218822Sdim
459218822Sdim	    case 'a':
460218822Sdim	      *s++ = ESCAPE_B; /* Strange, but true...  */
461218822Sdim	      ++t;
462218822Sdim	      break;
463218822Sdim
464218822Sdim	    case 'b':
465218822Sdim	      *s++ = ESCAPE_B;
466218822Sdim	      ++t;
467218822Sdim	      break;
468218822Sdim
469218822Sdim	    case 'f':
470218822Sdim	      *s++ = ESCAPE_F;
471218822Sdim	      ++t;
472218822Sdim	      break;
473218822Sdim
474218822Sdim	    case 'n':
475218822Sdim	      *s++ = ESCAPE_N;
476218822Sdim	      ++t;
477218822Sdim	      break;
478218822Sdim
479218822Sdim	    case 'r':
480218822Sdim	      *s++ = ESCAPE_R;
481218822Sdim	      ++t;
482218822Sdim	      break;
483218822Sdim
484218822Sdim	    case 't':
485218822Sdim	      *s++ = ESCAPE_T;
486218822Sdim	      ++t;
487218822Sdim	      break;
488218822Sdim
489218822Sdim	    case 'v':
490218822Sdim	      *s++ = ESCAPE_V;
491218822Sdim	      ++t;
492218822Sdim	      break;
493218822Sdim
494218822Sdim	    case '\\':
495218822Sdim	      *s++ = (unichar) *t++;
496218822Sdim	      break;
497218822Sdim
498218822Sdim	    case '0': case '1': case '2': case '3':
499218822Sdim	    case '4': case '5': case '6': case '7':
500218822Sdim	      ch = *t - '0';
501218822Sdim	      ++t;
502218822Sdim	      if (*t >= '0' && *t <= '7')
503218822Sdim		{
504218822Sdim		  ch = (ch << 3) | (*t - '0');
505218822Sdim		  ++t;
506218822Sdim		  if (*t >= '0' && *t <= '7')
507218822Sdim		    {
508218822Sdim		      ch = (ch << 3) | (*t - '0');
509218822Sdim		      ++t;
510218822Sdim		    }
511218822Sdim		}
512218822Sdim	      *s++ = (unichar) ch;
513218822Sdim	      break;
514218822Sdim
515218822Sdim	    case 'x': case 'X':
516218822Sdim	      ++t;
517218822Sdim	      ch = 0;
518218822Sdim	      /* We only handle two byte chars here.  Make sure
519218822Sdim		 we finish an escape sequence like "/xB0ABC" after
520218822Sdim		 the first two digits.  */
521218822Sdim              num_xdigits = 4;
522218822Sdim 	      while (num_xdigits--)
523218822Sdim		{
524218822Sdim		  if (*t >= '0' && *t <= '9')
525218822Sdim		    ch = (ch << 4) | (*t - '0');
526218822Sdim		  else if (*t >= 'a' && *t <= 'f')
527218822Sdim		    ch = (ch << 4) | (*t - 'a' + 10);
528218822Sdim		  else if (*t >= 'A' && *t <= 'F')
529218822Sdim		    ch = (ch << 4) | (*t - 'A' + 10);
530218822Sdim		  else
531218822Sdim		    break;
532218822Sdim		  ++t;
533218822Sdim		}
534218822Sdim	      *s++ = (unichar) ch;
535218822Sdim	      break;
536218822Sdim
537218822Sdim	    default:
538218822Sdim	      rcparse_warning ("unrecognized escape sequence");
539218822Sdim	      *s++ = '\\';
540218822Sdim	      *s++ = (unichar) *t++;
541218822Sdim	      break;
542218822Sdim	    }
543218822Sdim	}
544218822Sdim      else if (*t != '"')
545218822Sdim	*s++ = (unichar) *t++;
546218822Sdim      else if (t[1] == '\0')
547218822Sdim	break;
548218822Sdim      else if (t[1] == '"')
549218822Sdim	{
550218822Sdim	  *s++ = '"';
551218822Sdim	  t += 2;
552218822Sdim	}
553218822Sdim      else
554218822Sdim	{
555218822Sdim	  ++t;
556218822Sdim	  assert (ISSPACE (*t));
557218822Sdim	  while (ISSPACE (*t))
558218822Sdim	    {
559218822Sdim	      if ((*t) == '\n')
560218822Sdim		++rc_lineno;
561218822Sdim	      ++t;
562218822Sdim	    }
563218822Sdim	  if (*t == '\0')
564218822Sdim	    break;
565218822Sdim	  assert (*t == '"');
566218822Sdim	  ++t;
567218822Sdim	}
568218822Sdim    }
569218822Sdim
570218822Sdim  *s = '\0';
571218822Sdim
572218822Sdim  *len = s - ret;
573218822Sdim
574218822Sdim  return ret;
57538889Sjdp}
57638889Sjdp
57738889Sjdp/* Discard all the strings we have allocated.  The parser calls this
57838889Sjdp   when it no longer needs them.  */
57938889Sjdp
58038889Sjdpvoid
581130561Sobrienrcparse_discard_strings (void)
58238889Sjdp{
58338889Sjdp  struct alloc_string *as;
58438889Sjdp
58538889Sjdp  as = strings;
58638889Sjdp  while (as != NULL)
58738889Sjdp    {
58838889Sjdp      struct alloc_string *n;
58938889Sjdp
59038889Sjdp      free (as->s);
59138889Sjdp      n = as->next;
59238889Sjdp      free (as);
59338889Sjdp      as = n;
59438889Sjdp    }
59538889Sjdp
59638889Sjdp  strings = NULL;
59738889Sjdp}
59838889Sjdp
59938889Sjdp/* Enter rcdata mode.  */
60038889Sjdpvoid
601130561Sobrienrcparse_rcdata (void)
60238889Sjdp{
60338889Sjdp  rcdata_mode = 1;
60438889Sjdp}
60538889Sjdp
60638889Sjdp/* Go back to normal mode from rcdata mode.  */
60738889Sjdpvoid
608130561Sobrienrcparse_normal (void)
60938889Sjdp{
61038889Sjdp  rcdata_mode = 0;
61138889Sjdp}
612218822Sdim
613218822Sdimstatic void
614218822Sdimrclex_tok_add_char (int ch)
615218822Sdim{
616218822Sdim  if (! rclex_tok || rclex_tok_max <= rclex_tok_pos)
617218822Sdim    {
618218822Sdim      char *h = xmalloc (rclex_tok_max + 9);
619218822Sdim
620218822Sdim      if (! h)
621218822Sdim	abort ();
622218822Sdim      if (rclex_tok)
623218822Sdim	{
624218822Sdim	  memcpy (h, rclex_tok, rclex_tok_pos + 1);
625218822Sdim	  free (rclex_tok);
626218822Sdim	}
627218822Sdim      else
628218822Sdim	rclex_tok_pos = 0;
629218822Sdim      rclex_tok_max += 8;
630218822Sdim      rclex_tok = h;
631218822Sdim    }
632218822Sdim  if (ch != -1)
633218822Sdim    rclex_tok[rclex_tok_pos++] = (char) ch;
634218822Sdim  rclex_tok[rclex_tok_pos] = 0;
635218822Sdim}
636218822Sdim
637218822Sdimstatic int
638218822Sdimrclex_readch (void)
639218822Sdim{
640218822Sdim  int r = -1;
641218822Sdim
642218822Sdim  if ((r = rclex_lastch) != -1)
643218822Sdim    rclex_lastch = -1;
644218822Sdim  else
645218822Sdim    {
646218822Sdim      char ch;
647218822Sdim      do
648218822Sdim        {
649218822Sdim	  if (! cpp_pipe || feof (cpp_pipe)
650218822Sdim	      || fread (&ch, 1, 1,cpp_pipe) != 1)
651218822Sdim	    break;
652218822Sdim	  r = ((int) ch) & 0xff;
653218822Sdim        }
654218822Sdim      while (r == 0 || r == '\r');
655218822Sdim  }
656218822Sdim  rclex_tok_add_char (r);
657218822Sdim  return r;
658218822Sdim}
659218822Sdim
660218822Sdimstatic int
661218822Sdimrclex_peekch (void)
662218822Sdim{
663218822Sdim  int r;
664218822Sdim
665218822Sdim  if ((r = rclex_lastch) == -1)
666218822Sdim    {
667218822Sdim      if ((r = rclex_readch ()) != -1)
668218822Sdim	{
669218822Sdim	  rclex_lastch = r;
670218822Sdim	  if (rclex_tok_pos > 0)
671218822Sdim	    rclex_tok[--rclex_tok_pos] = 0;
672218822Sdim	}
673218822Sdim    }
674218822Sdim  return r;
675218822Sdim}
676218822Sdim
677218822Sdimstatic void
678218822Sdimrclex_string (void)
679218822Sdim{
680218822Sdim  int c;
681218822Sdim
682218822Sdim  while ((c = rclex_peekch ()) != -1)
683218822Sdim    {
684218822Sdim      if (c == '\n')
685218822Sdim	break;
686218822Sdim      if (c == '\\')
687218822Sdim        {
688218822Sdim	  rclex_readch ();
689218822Sdim	  if ((c = rclex_peekch ()) == -1 || c == '\n')
690218822Sdim	    break;
691218822Sdim	  rclex_readch ();
692218822Sdim        }
693218822Sdim      else if (rclex_readch () == '"')
694218822Sdim	{
695218822Sdim	  if (rclex_peekch () == '"')
696218822Sdim	    rclex_readch ();
697218822Sdim	  else
698218822Sdim	    break;
699218822Sdim	}
700218822Sdim    }
701218822Sdim}
702218822Sdim
703218822Sdimstatic rc_uint_type
704218822Sdimread_digit (int ch)
705218822Sdim{
706218822Sdim  rc_uint_type base = 10;
707218822Sdim  rc_uint_type ret, val;
708218822Sdim  int warned = 0;
709218822Sdim
710218822Sdim  ret = 0;
711218822Sdim  if (ch == '0')
712218822Sdim    {
713218822Sdim      base = 8;
714218822Sdim      switch (rclex_peekch ())
715218822Sdim	{
716218822Sdim	case 'o': case 'O':
717218822Sdim	  rclex_readch ();
718218822Sdim	  base = 8;
719218822Sdim	  break;
720218822Sdim
721218822Sdim	case 'x': case 'X':
722218822Sdim	  rclex_readch ();
723218822Sdim	  base = 16;
724218822Sdim	  break;
725218822Sdim	}
726218822Sdim    }
727218822Sdim  else
728218822Sdim    ret = (rc_uint_type) (ch - '0');
729218822Sdim  while ((ch = rclex_peekch ()) != -1)
730218822Sdim    {
731218822Sdim      if (ISDIGIT (ch))
732218822Sdim	val = (rc_uint_type) (ch - '0');
733218822Sdim      else if (ch >= 'a' && ch <= 'f')
734218822Sdim	val = (rc_uint_type) ((ch - 'a') + 10);
735218822Sdim      else if (ch >= 'A' && ch <= 'F')
736218822Sdim	val = (rc_uint_type) ((ch - 'A') + 10);
737218822Sdim      else
738218822Sdim	break;
739218822Sdim      rclex_readch ();
740218822Sdim      if (! warned && val >= base)
741218822Sdim	{
742218822Sdim	  warned = 1;
743218822Sdim	  rcparse_warning ("digit exceeds base");
744218822Sdim	}
745218822Sdim      ret *= base;
746218822Sdim      ret += val;
747218822Sdim    }
748218822Sdim  return ret;
749218822Sdim}
750218822Sdim
751218822Sdim/* yyparser entry method.  */
752218822Sdim
753218822Sdimint
754218822Sdimyylex (void)
755218822Sdim{
756218822Sdim  char *s;
757218822Sdim  unichar *us;
758218822Sdim  rc_uint_type length;
759218822Sdim  int ch;
760218822Sdim
761218822Sdim  /* Make sure that rclex_tok is initialized.  */
762218822Sdim  if (! rclex_tok)
763218822Sdim    rclex_tok_add_char (-1);
764218822Sdim
765218822Sdim  do
766218822Sdim    {
767218822Sdim      do
768218822Sdim	{
769218822Sdim	  /* Clear token.  */
770218822Sdim	  rclex_tok_pos = 0;
771218822Sdim	  rclex_tok[0] = 0;
772218822Sdim
773218822Sdim	  if ((ch = rclex_readch ()) == -1)
774218822Sdim	    return -1;
775218822Sdim	  if (ch == '\n')
776218822Sdim	    ++rc_lineno;
777218822Sdim	}
778218822Sdim      while (ch <= 0x20);
779218822Sdim
780218822Sdim      switch (ch)
781218822Sdim	{
782218822Sdim	case '#':
783218822Sdim	  while ((ch = rclex_peekch ()) != -1 && ch != '\n')
784218822Sdim	    rclex_readch ();
785218822Sdim	  cpp_line ();
786218822Sdim	  ch = IGNORED_TOKEN;
787218822Sdim	  break;
788218822Sdim
789218822Sdim	case '{':
790218822Sdim	  ch = IGNORE_CPP (BEG);
791218822Sdim	  break;
792218822Sdim
793218822Sdim	case '}':
794218822Sdim	  ch = IGNORE_CPP (END);
795218822Sdim	  break;
796218822Sdim
797218822Sdim	case '0': case '1': case '2': case '3': case '4':
798218822Sdim	case '5': case '6': case '7': case '8': case '9':
799218822Sdim	  yylval.i.val = read_digit (ch);
800218822Sdim	  yylval.i.dword = 0;
801218822Sdim	  switch (rclex_peekch ())
802218822Sdim	    {
803218822Sdim	    case 'l': case 'L':
804218822Sdim	      rclex_readch ();
805218822Sdim	      yylval.i.dword = 1;
806218822Sdim	      break;
807218822Sdim	    }
808218822Sdim	  ch = IGNORE_CPP (NUMBER);
809218822Sdim	  break;
810218822Sdim	case '"':
811218822Sdim	  rclex_string ();
812218822Sdim	  ch = IGNORE_CPP ((! rcdata_mode ? QUOTEDSTRING : SIZEDSTRING));
813218822Sdim	  if (ch == IGNORED_TOKEN)
814218822Sdim	    break;
815218822Sdim	  s = handle_quotes (&length);
816218822Sdim	  if (! rcdata_mode)
817218822Sdim	    yylval.s = s;
818218822Sdim	  else
819218822Sdim	    {
820218822Sdim	      yylval.ss.length = length;
821218822Sdim	      yylval.ss.s = s;
822218822Sdim	  }
823218822Sdim	  break;
824218822Sdim	case 'L': case 'l':
825218822Sdim	  if (rclex_peekch () == '"')
826218822Sdim	    {
827218822Sdim	      rclex_readch ();
828218822Sdim	      rclex_string ();
829218822Sdim	      ch = IGNORE_CPP ((! rcdata_mode ? QUOTEDUNISTRING : SIZEDUNISTRING));
830218822Sdim	      if (ch == IGNORED_TOKEN)
831218822Sdim		break;
832218822Sdim	      us = handle_uniquotes (&length);
833218822Sdim	      if (! rcdata_mode)
834218822Sdim		yylval.uni = us;
835218822Sdim	      else
836218822Sdim	        {
837218822Sdim		  yylval.suni.length = length;
838218822Sdim		  yylval.suni.s = us;
839218822Sdim	      }
840218822Sdim	      break;
841218822Sdim	    }
842218822Sdim	  /* Fall through.  */
843218822Sdim	default:
844218822Sdim	  if (ISIDST (ch) || ch=='$')
845218822Sdim	    {
846218822Sdim	      while ((ch = rclex_peekch ()) != -1 && (ISIDNUM (ch) || ch == '$' || ch == '.'))
847218822Sdim		rclex_readch ();
848218822Sdim	      ch = IGNORE_CPP (rclex_translatekeyword (rclex_tok));
849218822Sdim	      if (ch == STRING)
850218822Sdim		{
851218822Sdim		  s = get_string (strlen (rclex_tok) + 1);
852218822Sdim		  strcpy (s, rclex_tok);
853218822Sdim		  yylval.s = s;
854218822Sdim		}
855218822Sdim	      else if (ch == BLOCK)
856218822Sdim		{
857218822Sdim		  const char *hs = NULL;
858218822Sdim
859218822Sdim		  switch (yylex ())
860218822Sdim		  {
861218822Sdim		  case STRING:
862218822Sdim		  case QUOTEDSTRING:
863218822Sdim		    hs = yylval.s;
864218822Sdim		    break;
865218822Sdim		  case SIZEDSTRING:
866218822Sdim		    hs = yylval.s = yylval.ss.s;
867218822Sdim		    break;
868218822Sdim		  }
869218822Sdim		  if (! hs)
870218822Sdim		    {
871218822Sdim		      rcparse_warning ("BLOCK expects a string as argument.");
872218822Sdim		      ch = IGNORED_TOKEN;
873218822Sdim		    }
874218822Sdim		  else if (! strcmp (hs, "StringFileInfo"))
875218822Sdim		    ch = BLOCKSTRINGFILEINFO;
876218822Sdim		  else if (! strcmp (hs, "VarFileInfo"))
877218822Sdim		    ch = BLOCKVARFILEINFO;
878218822Sdim		}
879218822Sdim	      break;
880218822Sdim	    }
881218822Sdim	  ch = IGNORE_CPP (ch);
882218822Sdim	  break;
883218822Sdim	}
884218822Sdim    }
885218822Sdim  while (ch == IGNORED_TOKEN);
886218822Sdim
887218822Sdim  return ch;
888218822Sdim}
889