parser.l 20.1 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2
/* -*-C-*-
 *
3
 * Copyright 1998-2000	Bertho A. Stultiens (BS)
Alexandre Julliard's avatar
Alexandre Julliard committed
4
 *
5 6 7 8 9 10 11 12 13 14 15 16
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 19
 *
 * History:
20 21
 * 21-May-2000 BS	- Fixed the ident requirement of resource names
 *			  which can be keywords.
22 23 24 25 26
 * 30-Apr-2000 BS	- Reintegration into the wine-tree
 * 11-Jan-2000 BS	- Very drastic cleanup because we don't have a
 *			  preprocessor in here anymore.
 * 02-Jan-2000 BS	- Removed the preprocessor code
 * 23-Dec-1999 BS	- Removed the copyright for Martin von Loewis.
27
 *			  There is really nothing left of his code in
28
 *			  this parser.
Alexandre Julliard's avatar
Alexandre Julliard committed
29 30 31 32 33 34 35
 * 20-Jun-1998 BS	- Changed the filename conversion. Filenames are
 *			  case-sensitive inder *nix, but not under dos.
 *			  default behaviour is to convert to lower case.
 *			- All backslashes are converted to forward and
 *			  both single and double slash is recognized as
 *			  MS/Borland does.
 *			- Fixed a bug in 'yywf' case that prevented
36
 *			  double quoted names to be scanned properly.
Alexandre Julliard's avatar
Alexandre Julliard committed
37
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
 * 19-May-1998 BS	- Started to build a preprocessor.
 *			- Changed keyword processing completely to
 *			  table-lookups.
 *
 * 20-Apr-1998 BS	- Added ';' comment stripping
 *
 * 17-Apr-1998 BS	- Made the win32 keywords optional when compiling in
 *			  16bit mode
 *
 * 15-Apr-1998 BS	- Changed string handling to include escapes
 *			- Added unicode string handling (no codepage
 *			  translation though).
 *			- 'Borrowed' the main idea of string scanning from
 *			  the flex manual pages.
 *			- Added conditional handling of scanning depending
 *			  on the state of the parser. This was mainly required
 *			  to distinguish a file to load or raw data that
 *			  follows. MS's definition of filenames is rather
 *			  complex... It can be unquoted or double quoted. If
 *			  double quoted, then the '\\' char is not automatically
 *			  escaped according to Borland's rc compiler, but it
 *			  accepts both "\\path\\file.rc" and "\path\file.rc".
 *			  This makes life very hard! I go for the escaped
 *			  version, as this seems to be the documented way...
 *			- Single quoted strings are now parsed and converted
 *			  here.
 *			- Added comment stripping. The implementation is
 *			  'borrowed' from the flex manpages.
 *			- Rebuild string processing so that it may contain
 *			  escaped '\0'.
 */

/* Exclusive string handling */
71
%x tkstr
Alexandre Julliard's avatar
Alexandre Julliard committed
72
/* Exclusive unicode string handling */
73
%x tklstr
Alexandre Julliard's avatar
Alexandre Julliard committed
74
/* Exclusive rcdata single quoted data handling */
75
%x tkrcd
Alexandre Julliard's avatar
Alexandre Julliard committed
76 77 78
/* Exclusive comment eating... */
%x comment
/* Set when stripping c-junk */
79
%x pp_cstrip
80 81
/* Set when scanning #line style directives */
%x pp_line
82 83 84
/* Set when scanning #pragma */
%x pp_pragma
%x pp_code_page
Alexandre Julliard's avatar
Alexandre Julliard committed
85

86
%option stack
87
%option noinput nounput noyy_top_state noyywrap
88
%option 8bit never-interactive
89
%option prefix="parser_"
90

Alexandre Julliard's avatar
Alexandre Julliard committed
91 92 93 94 95 96 97
/* Some shortcut definitions */
ws	[ \f\t\r]

%{

/*#define LEX_DEBUG*/

98 99
#include "config.h"

Alexandre Julliard's avatar
Alexandre Julliard committed
100 101 102
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
103
#include <ctype.h>
104
#include <assert.h>
Huw Davies's avatar
Huw Davies committed
105
#include <errno.h>
106
#include <limits.h>
107 108
#define YY_NO_UNISTD_H

109
#include "../tools.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
110 111 112 113
#include "wrc.h"
#include "utils.h"
#include "parser.h"
#include "newstruc.h"
114
#include "wpp_private.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
115

116
#include "parser.tab.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
117

Alexandre Julliard's avatar
Alexandre Julliard committed
118
/* Always update the current character position within a line */
119
#define YY_USER_ACTION	char_number+=yyleng; wanted_id = want_id; want_id = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
120

121
#define YY_USER_INIT current_codepage = utf8_input ? CP_UTF8 : -1;
122

123
static void addcchar(char c);
124
static void addwchar(WCHAR s);
125 126 127
static string_t *get_buffered_cstring(void);
static string_t *get_buffered_wstring(void);
static string_t *make_string(char *s);
Alexandre Julliard's avatar
Alexandre Julliard committed
128

129
static char *cbuffer;		/* Buffers for string collection */
Alexandre Julliard's avatar
Alexandre Julliard committed
130
static int cbufidx;
131
static int cbufalloc = 0;
132
static WCHAR *wbuffer;
Alexandre Julliard's avatar
Alexandre Julliard committed
133
static int wbufidx;
134
static int wbufalloc = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
135

136 137
static int current_codepage = -1;  /* use language default */

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/*
 * This one is a bit tricky.
 * We set 'want_id' in the parser to get the first
 * identifier we get across in the scanner, but we
 * also want it to be reset at nearly any token we
 * see. Exceptions are:
 * - newlines
 * - comments
 * - whitespace
 *
 * The scanner will automatically reset 'want_id'
 * after *each* scanner reduction and puts is value
 * into the var below. In this way we can see the
 * state after the YY_RULE_SETUP (i.e. the user action;
 * see above) and don't have to worry too much when
 * it needs to be reset.
 */
static int wanted_id = 0;
static int save_wanted_id;	/* To save across comment reductions */
Alexandre Julliard's avatar
Alexandre Julliard committed
157 158

struct keyword {
159 160 161 162 163
	const char	*keyword;
	int		token;
	int		isextension;
	int		needcase;
	int		alwayskw;
Alexandre Julliard's avatar
Alexandre Julliard committed
164 165 166
};

static struct keyword keywords[] = {
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
	{ "ACCELERATORS",	tACCELERATORS,		0, 0, 0},
	{ "ALT",		tALT,			0, 0, 0},
	{ "ASCII",		tASCII,			0, 0, 0},
	{ "AUTO3STATE",		tAUTO3STATE,		1, 0, 0},
	{ "AUTOCHECKBOX",	tAUTOCHECKBOX,		1, 0, 0},
	{ "AUTORADIOBUTTON",	tAUTORADIOBUTTON,	1, 0, 0},
	{ "BEGIN",		tBEGIN,			0, 0, 0},
	{ "BITMAP",		tBITMAP,		0, 0, 0},
	{ "BLOCK",		tBLOCK,			0, 0, 0},
	{ "BUTTON",		tBUTTON,		1, 0, 0},
	{ "CAPTION",		tCAPTION,		0, 0, 0},
	{ "CHARACTERISTICS",	tCHARACTERISTICS,	1, 0, 0},
	{ "CHECKBOX",		tCHECKBOX,		0, 0, 0},
	{ "CHECKED",		tCHECKED,		0, 0, 0},
	{ "CLASS",		tCLASS,			0, 0, 0},
	{ "COMBOBOX",		tCOMBOBOX,		0, 0, 0},
	{ "CONTROL",		tCONTROL,		0, 0, 0},
	{ "CTEXT",		tCTEXT,			0, 0, 0},
	{ "CURSOR",		tCURSOR,		0, 0, 0},
	{ "DEFPUSHBUTTON",	tDEFPUSHBUTTON,		0, 0, 0},
	{ "DIALOG",		tDIALOG,		0, 0, 0},
	{ "DIALOGEX",		tDIALOGEX,		1, 0, 0},
	{ "DISCARDABLE",	tDISCARDABLE,		0, 0, 0},
	{ "DLGINIT",		tDLGINIT,		0, 0, 0},
	{ "EDITTEXT",		tEDITTEXT,		0, 0, 0},
	{ "END",		tEND,			0, 0, 0},
	{ "EXSTYLE",		tEXSTYLE,		0, 0, 0},
	{ "FILEFLAGS",		tFILEFLAGS,		0, 0, 0},
	{ "FILEFLAGSMASK",	tFILEFLAGSMASK,		0, 0, 0},
	{ "FILEOS",		tFILEOS,		0, 0, 0},
	{ "FILESUBTYPE",	tFILESUBTYPE,		0, 0, 0},
	{ "FILETYPE",		tFILETYPE,		0, 0, 0},
	{ "FILEVERSION",	tFILEVERSION,		0, 0, 0},
	{ "FIXED",		tFIXED,			0, 0, 0},
	{ "FONT",		tFONT,			0, 0, 0},
	{ "FONTDIR",		tFONTDIR,		0, 0, 0},	/* This is a Borland BRC extension */
	{ "GRAYED",		tGRAYED,		0, 0, 0},
	{ "GROUPBOX",		tGROUPBOX,		0, 0, 0},
	{ "HELP",		tHELP,			0, 0, 0},
206
	{ "HTML",		tHTML,			0, 0, 0},
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
	{ "ICON",		tICON,			0, 0, 0},
	{ "IMPURE",		tIMPURE,		0, 0, 0},
	{ "INACTIVE",		tINACTIVE,		0, 0, 0},
	{ "LANGUAGE",		tLANGUAGE,		1, 0, 1},
	{ "LISTBOX",		tLISTBOX,		0, 0, 0},
	{ "LOADONCALL",		tLOADONCALL,		0, 0, 0},
	{ "LTEXT",		tLTEXT,			0, 0, 0},
	{ "MENU",		tMENU,			0, 0, 0},
	{ "MENUBARBREAK",	tMENUBARBREAK,		0, 0, 0},
	{ "MENUBREAK",		tMENUBREAK,		0, 0, 0},
	{ "MENUEX",		tMENUEX,		1, 0, 0},
	{ "MENUITEM",		tMENUITEM,		0, 0, 0},
	{ "MESSAGETABLE",	tMESSAGETABLE,		1, 0, 0},
	{ "MOVEABLE",		tMOVEABLE,		0, 0, 0},
	{ "NOINVERT",		tNOINVERT,		0, 0, 0},
	{ "NOT",		tNOT,			0, 0, 0},
	{ "POPUP",		tPOPUP,			0, 0, 0},
	{ "PRELOAD",		tPRELOAD,		0, 0, 0},
	{ "PRODUCTVERSION",	tPRODUCTVERSION,	0, 0, 0},
	{ "PURE",		tPURE,			0, 0, 0},
	{ "PUSHBUTTON",		tPUSHBUTTON,		0, 0, 0},
	{ "RADIOBUTTON",	tRADIOBUTTON,		0, 0, 0},
	{ "RCDATA",		tRCDATA,		0, 0, 0},
	{ "RTEXT",		tRTEXT,			0, 0, 0},
	{ "SCROLLBAR",		tSCROLLBAR,		0, 0, 0},
	{ "SEPARATOR",		tSEPARATOR,		0, 0, 0},
	{ "SHIFT",		tSHIFT,			0, 0, 0},
	{ "STATE3",		tSTATE3,		1, 0, 0},
	{ "STRING",		tSTRING,		0, 0, 0},
	{ "STRINGTABLE",	tSTRINGTABLE,		0, 0, 1},
	{ "STYLE",		tSTYLE,			0, 0, 0},
	{ "TOOLBAR",		tTOOLBAR,		1, 0, 0},
	{ "VALUE",		tVALUE,			0, 0, 0},
	{ "VERSION",		tVERSION,		1, 0, 0},
	{ "VERSIONINFO",	tVERSIONINFO,		0, 0, 0},
	{ "VIRTKEY",		tVIRTKEY,		0, 0, 0}
Alexandre Julliard's avatar
Alexandre Julliard committed
243 244 245
};

#define NKEYWORDS	(sizeof(keywords)/sizeof(keywords[0]))
246
#define KWP(p)		((const struct keyword *)(p))
247
static int kw_cmp_func(const void *s1, const void *s2)
Alexandre Julliard's avatar
Alexandre Julliard committed
248 249
{
	int ret;
250
	ret = compare_striA(KWP(s1)->keyword, KWP(s2)->keyword);
Alexandre Julliard's avatar
Alexandre Julliard committed
251 252 253 254 255 256 257 258
	if(!ret && (KWP(s1)->needcase || KWP(s2)->needcase))
		return strcmp(KWP(s1)->keyword, KWP(s2)->keyword);
	else
		return ret;
}

#define KW_BSEARCH
#define DO_SORT
259
static struct keyword *iskeyword(char *kw)
Alexandre Julliard's avatar
Alexandre Julliard committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
{
	struct keyword *kwp;
	struct keyword key;
	key.keyword = kw;
	key.needcase = 0;
#ifdef DO_SORT
	{
		/* Make sure that it is sorted for bsearsh */
		static int sorted = 0;
		if(!sorted)
		{
			qsort(keywords, NKEYWORDS, sizeof(keywords[0]), kw_cmp_func);
			sorted = 1;
		}
	}
#endif
#ifdef KW_BSEARCH
	kwp = bsearch(&key, keywords, NKEYWORDS, sizeof(keywords[0]), kw_cmp_func);
#else
	{
		int i;
		for(i = 0; i < NKEYWORDS; i++)
		{
			if(!kw_cmp_func(&key, &keywords[i]))
				break;
		}
		if(i < NKEYWORDS)
			kwp = &keywords[i];
		else
			kwp = NULL;
	}
#endif

	if(kwp == NULL || (kwp->isextension && !extensions))
		return NULL;
	else
		return kwp;
}

299 300 301 302 303 304 305 306 307
/* converts an integer in string form to an unsigned long and prints an error
 * on overflow */
static unsigned long xstrtoul(const char *nptr, char **endptr, int base)
{
    unsigned long l;

    errno = 0;
    l = strtoul(nptr, endptr, base);
    if (l == ULONG_MAX && errno == ERANGE)
308
        parser_error("integer constant %s is too large", nptr);
309 310 311
    return l;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
312 313
%}

314 315 316 317 318
/*
 **************************************************************************
 * The flexer starts here
 **************************************************************************
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
319
%%
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
	/*
	 * Catch the GCC-style line statements here and parse them.
	 * This has the advantage that you can #include at any
	 * stage in the resource file.
	 * The preprocessor generates line directives in the format:
	 * # <linenum> "filename" <codes>
	 *
	 * Codes can be a sequence of:
	 * - 1 start of new file
	 * - 2 returning to previous
	 * - 3 system header
	 * - 4 interpret as C-code
	 *
	 * 4 is not used and 1 mutually excludes 2
	 * Anyhow, we are not really interested in these at all
	 * because we only want to know the linenumber and
	 * filename.
	 */
338 339
<INITIAL,pp_cstrip>^{ws}*\#{ws}*pragma{ws}+	yy_push_state(pp_pragma);
<INITIAL,pp_cstrip>^{ws}*\#{ws}*	yy_push_state(pp_line);
340
<pp_line>[^\n]*	{
341
		int lineno, len;
342 343 344 345 346
		char *cptr;
		char *fname;
		yy_pop_state();
		lineno = (int)strtol(yytext, &cptr, 10);
		if(!lineno)
347
			parser_error("Malformed '#...' line-directive; invalid linenumber");
348 349
		fname = strchr(cptr, '"');
		if(!fname)
350
			parser_error("Malformed '#...' line-directive; missing filename");
351 352 353
		fname++;
		cptr = strchr(fname, '"');
		if(!cptr)
354
			parser_error("Malformed '#...' line-directive; missing terminating \"");
355 356 357
		*cptr = '\0';
		line_number = lineno - 1;	/* We didn't read the newline */
		input_name = xstrdup(fname);
358 359 360 361 362 363
                /* ignore contents of C include files */
                len = strlen(input_name);
                if (len > 1 && !strcasecmp( input_name + len - 2, ".h" ))
                    BEGIN(pp_cstrip);
                else
                    BEGIN(INITIAL);
364 365
	}

366
<pp_pragma>code_page[^\n]*	yyless(9); yy_pop_state(); yy_push_state(pp_code_page);
367
<pp_pragma>[^\n]*		yy_pop_state(); if (pedantic) parser_warning("Unrecognized #pragma directive '%s'\n",yytext);
368 369

<pp_code_page>\({ws}*default{ws}*\)[^\n]*	current_codepage = -1; yy_pop_state();
370
<pp_code_page>\({ws}*utf8{ws}*\)[^\n]*		current_codepage = CP_UTF8; yy_pop_state();
371 372 373 374 375
<pp_code_page>\({ws}*[0-9]+{ws}*\)[^\n]* {
        char *p = yytext;
        yy_pop_state();
        while (*p < '0' || *p > '9') p++;
        current_codepage = strtol( p, NULL, 10 );
376
        if (!is_valid_codepage( current_codepage ))
377
        {
378
            parser_error("Codepage %d not supported", current_codepage);
379 380 381
            current_codepage = 0;
        }
    }
382
<pp_code_page>[^\n]*	yy_pop_state(); parser_error("Malformed #pragma code_page directive");
383

384 385 386 387 388
	/*
	 * Strip everything until a ';' taking
	 * into account braces {} for structures,
	 * classes and enums.
	 */
389 390
<pp_cstrip>\n			line_number++; char_number = 1;
<pp_cstrip>.			; /* ignore */
Alexandre Julliard's avatar
Alexandre Julliard committed
391 392 393 394

\{			return tBEGIN;
\}			return tEND;

395 396 397 398 399 400
[0-9]+[lL]?		{ parser_lval.num = xstrtoul(yytext,  0, 10);
                          return (yytext[yyleng-1] == 'L' || yytext[yyleng-1] == 'l') ? tLNUMBER : tNUMBER; }
0[xX][0-9A-Fa-f]+[lL]?	{ parser_lval.num = xstrtoul(yytext,  0, 16);
                          return (yytext[yyleng-1] == 'L' || yytext[yyleng-1] == 'l') ? tLNUMBER : tNUMBER; }
0[oO][0-7]+[lL]?	{ parser_lval.num = xstrtoul(yytext+2, 0, 8);
                          return (yytext[yyleng-1] == 'L' || yytext[yyleng-1] == 'l') ? tLNUMBER : tNUMBER; }
401

402
[A-Za-z_0-9./\\][A-Za-z_0-9./\\\-]*	{
403
				struct keyword *tok = iskeyword(yytext);
Alexandre Julliard's avatar
Alexandre Julliard committed
404

405
				if(tok)
Alexandre Julliard's avatar
Alexandre Julliard committed
406
				{
407
					if(wanted_id && !tok->alwayskw)
408
					{
409
						parser_lval.str = make_string(yytext);
410 411
						return tIDENT;
					}
412 413
					else
						return tok->token;
Alexandre Julliard's avatar
Alexandre Julliard committed
414 415 416
				}
				else
				{
417
					parser_lval.str = make_string(yytext);
418
					return tIDENT;
Alexandre Julliard's avatar
Alexandre Julliard committed
419 420 421
				}
			}

422 423 424
	/*
	 * Wide string scanning
	 */
Alexandre Julliard's avatar
Alexandre Julliard committed
425
L\"			{
426
				yy_push_state(tklstr);
Alexandre Julliard's avatar
Alexandre Julliard committed
427 428
				wbufidx = 0;
				if(!win32)
429
					parser_error("16bit resource contains unicode strings");
Alexandre Julliard's avatar
Alexandre Julliard committed
430
			}
431 432
<tklstr>\"{ws}+	|
<tklstr>\"		{
433
				yy_pop_state();
434
				parser_lval.str = get_buffered_wstring();
Alexandre Julliard's avatar
Alexandre Julliard committed
435 436
				return tSTRING;
			}
437
<tklstr>\\[0-7]{1,6}	{ /* octal escape sequence */
438 439
				unsigned int result;
				result = strtoul(yytext+1, 0, 8);
Alexandre Julliard's avatar
Alexandre Julliard committed
440
				if ( result > 0xffff )
441
					parser_error("Character constant out of range");
442
				addwchar((WCHAR)result);
Alexandre Julliard's avatar
Alexandre Julliard committed
443
			}
444
<tklstr>\\x[0-9a-fA-F]{4} {  /* hex escape sequence */
445 446 447
				unsigned int result;
				result = strtoul(yytext+2, 0, 16);
				addwchar((WCHAR)result);
Alexandre Julliard's avatar
Alexandre Julliard committed
448
			}
449
<tklstr>\\x[0-9a-fA-F]{1,3} {  parser_error("Invalid hex escape sequence '%s'", yytext); }
450

451
<tklstr>\\[0-9]+	parser_error("Bad escape sequence");
452 453 454 455 456 457 458 459
<tklstr>\\\n{ws}*	line_number++; char_number = 1; /* backslash at EOL continues string after leading whitespace on next line */
<tklstr>\\a		addwchar('\a');
<tklstr>\\b		addwchar('\b');
<tklstr>\\f		addwchar('\f');
<tklstr>\\n		addwchar('\n');
<tklstr>\\r		addwchar('\r');
<tklstr>\\t		addwchar('\t');
<tklstr>\\v		addwchar('\v');
460 461 462 463 464
<tklstr>\\.		{
			    if (yytext[1] & 0x80)
                                parser_error("Invalid char %u in wide string", (unsigned char)yytext[1]);
			    addwchar(yytext[1]);
			}
465 466 467 468 469
<tklstr>\\\r\n		addwchar(yytext[2]); line_number++; char_number = 1;
<tklstr>\"\"		addwchar('\"');		/* "bla""bla"  -> "bla\"bla" */
<tklstr>\\\"\"		addwchar('\"');		/* "bla\""bla" -> "bla\"bla" */
<tklstr>\"{ws}+\"	;			/* "bla" "bla" -> "blabla" */
<tklstr>[^\\\n\"]+	{
Alexandre Julliard's avatar
Alexandre Julliard committed
470 471
				char *yptr = yytext;
				while(*yptr)	/* FIXME: codepage translation */
472 473 474 475 476
                                {
                                    if (*yptr & 0x80)
                                        parser_error("Invalid char %u in wide string", (unsigned char)*yptr);
                                    addwchar(*yptr++ & 0xff);
                                }
Alexandre Julliard's avatar
Alexandre Julliard committed
477
			}
478
<tklstr>\n		parser_error("Unterminated string");
Alexandre Julliard's avatar
Alexandre Julliard committed
479

480 481 482
	/*
	 * Normal string scanning
	 */
483 484 485
\"			yy_push_state(tkstr); cbufidx = 0;
<tkstr>\"{ws}+	|
<tkstr>\"		{
486
				yy_pop_state();
487
				parser_lval.str = get_buffered_cstring();
Alexandre Julliard's avatar
Alexandre Julliard committed
488 489
				return tSTRING;
			}
490
<tkstr>\\[0-7]{1,3}	{ /* octal escape sequence */
Alexandre Julliard's avatar
Alexandre Julliard committed
491 492 493
				int result;
				result = strtol(yytext+1, 0, 8);
				if ( result > 0xff )
494
					parser_error("Character constant out of range");
Alexandre Julliard's avatar
Alexandre Julliard committed
495 496
				addcchar((char)result);
			}
497
<tkstr>\\x[0-9a-fA-F]{2} {  /* hex escape sequence */
Alexandre Julliard's avatar
Alexandre Julliard committed
498 499 500 501
				int result;
				result = strtol(yytext+2, 0, 16);
				addcchar((char)result);
			}
502
<tkstr>\\x[0-9a-fA-F]	{  parser_error("Invalid hex escape sequence '%s'", yytext); }
503

504
<tkstr>\\[0-9]+		parser_error("Bad escape sequence");
505 506 507 508 509 510 511 512 513 514 515
<tkstr>\\\n{ws}*	line_number++; char_number = 1; /* backslash at EOL continues string after leading whitespace on next line */
<tkstr>\\a		addcchar('\a');
<tkstr>\\b		addcchar('\b');
<tkstr>\\f		addcchar('\f');
<tkstr>\\n		addcchar('\n');
<tkstr>\\r		addcchar('\r');
<tkstr>\\t		addcchar('\t');
<tkstr>\\v		addcchar('\v');
<tkstr>\\.		addcchar(yytext[1]);
<tkstr>\\\r\n		addcchar(yytext[2]); line_number++; char_number = 1;
<tkstr>[^\\\n\"]+	{
Alexandre Julliard's avatar
Alexandre Julliard committed
516 517 518 519
				char *yptr = yytext;
				while(*yptr)
					addcchar(*yptr++);
			}
520 521 522
<tkstr>\"\"		addcchar('\"');		/* "bla""bla"   -> "bla\"bla" */
<tkstr>\\\"\"		addcchar('\"');		/* "bla\""bla"  -> "bla\"bla" */
<tkstr>\"{ws}+\"	;			/* "bla" "bla"  -> "blabla" */
523
<tkstr>\n		parser_error("Unterminated string");
Alexandre Julliard's avatar
Alexandre Julliard committed
524

525 526 527
	/*
	 * Raw data scanning
	 */
528 529
\'			yy_push_state(tkrcd); cbufidx = 0;
<tkrcd>\'		{
530
				yy_pop_state();
531 532 533 534
				parser_lval.raw = new_raw_data();
				parser_lval.raw->size = cbufidx;
				parser_lval.raw->data = xmalloc(parser_lval.raw->size);
				memcpy(parser_lval.raw->data, cbuffer, parser_lval.raw->size);
535
				return tRAWDATA;
Alexandre Julliard's avatar
Alexandre Julliard committed
536
			}
537
<tkrcd>[0-9a-fA-F]{2}	{
Alexandre Julliard's avatar
Alexandre Julliard committed
538 539 540 541
				int result;
				result = strtol(yytext, 0, 16);
				addcchar((char)result);
			}
542 543
<tkrcd>{ws}+		;	/* Ignore space */
<tkrcd>\n		line_number++; char_number = 1;
544
<tkrcd>.		parser_error("Malformed data-line");
Alexandre Julliard's avatar
Alexandre Julliard committed
545

546 547 548 549
	/*
	 * Comment stripping
	 * Should never occur after preprocessing
	 */
550
<INITIAL,pp_cstrip>"/*"	{
551 552 553
				yy_push_state(comment);
				save_wanted_id = wanted_id;
				if(!no_preprocess)
554
					parser_warning("Found comments after preprocessing, please report\n");
555
			}
Alexandre Julliard's avatar
Alexandre Julliard committed
556 557
<comment>[^*\n]*	;
<comment>"*"+[^*/\n]*	;
Alexandre Julliard's avatar
Alexandre Julliard committed
558
<comment>\n		line_number++; char_number = 1;
559
<comment>"*"+"/"	yy_pop_state(); want_id = save_wanted_id;
Alexandre Julliard's avatar
Alexandre Julliard committed
560

561
;[^\n]*			want_id = wanted_id; /* not really comment, but left-over c-junk */
562
"//"[^\n]*		want_id = wanted_id; if(!no_preprocess) parser_warning("Found comments after preprocessing, please report\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
563

564
\n			{
565
				want_id = wanted_id;
Alexandre Julliard's avatar
Alexandre Julliard committed
566
				line_number++;
Alexandre Julliard's avatar
Alexandre Julliard committed
567
				char_number = 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
568 569 570 571 572 573
				if(want_nl)
				{
					want_nl = 0;
					return tNL;
				}
			}
574
{ws}+			want_id = wanted_id;	/* Eat whitespace */
Alexandre Julliard's avatar
Alexandre Julliard committed
575

576
<INITIAL>[ -~]		return yytext[0];
577

578 579 580 581 582 583 584
<*>.|\n			{
				/* Catch all rule to find any unmatched text */
				if(*yytext == '\n')
				{
					line_number++;
					char_number = 1;
				}
585 586
				parser_error("Unmatched text '%c' (0x%02x) YY_START=%d",
                                             isprint((unsigned char)*yytext) ? *yytext : '.', *yytext, YY_START);
587 588
			}

Alexandre Julliard's avatar
Alexandre Julliard committed
589 590 591 592 593
%%

/* These dup functions copy the enclosed '\0' from
 * the resource string.
 */
594
static void addcchar(char c)
Alexandre Julliard's avatar
Alexandre Julliard committed
595
{
596 597 598 599 600
	if(cbufidx >= cbufalloc)
	{
		cbufalloc += 1024;
		cbuffer = xrealloc(cbuffer, cbufalloc * sizeof(cbuffer[0]));
		if(cbufalloc > 65536)
601
			parser_warning("Reallocating string buffer larger than 64kB\n");
602
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
603 604 605
	cbuffer[cbufidx++] = c;
}

606
static void addwchar(WCHAR s)
Alexandre Julliard's avatar
Alexandre Julliard committed
607
{
608 609 610 611 612
	if(wbufidx >= wbufalloc)
	{
		wbufalloc += 1024;
		wbuffer = xrealloc(wbuffer, wbufalloc * sizeof(wbuffer[0]));
		if(wbufalloc > 65536)
613
			parser_warning("Reallocating wide string buffer larger than 64kB\n");
614
	}
615
	wbuffer[wbufidx++] = s;
Alexandre Julliard's avatar
Alexandre Julliard committed
616 617
}

618
static string_t *get_buffered_cstring(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
619
{
620 621 622 623
    string_t *str = new_string();

    str->size = cbufidx;
    str->type = str_char;
624
    str->str.cstr = xmalloc(cbufidx+1);
625 626 627 628 629
    memcpy(str->str.cstr, cbuffer, cbufidx);
    str->str.cstr[cbufidx] = '\0';

    if (!current_codepage || current_codepage == -1 || !win32)  /* store as ANSI string */
    {
630
        if (!current_codepage) parser_error("Codepage set to Unicode only, cannot use ASCII string here");
631 632 633 634
        return str;
    }
    else  /* convert to Unicode before storing */
    {
635
        string_t *str_w = convert_string_unicode( str, current_codepage );
636
        if (check_valid_utf8( str, current_codepage ))
637
            parser_warning( "string \"%s\" seems to be UTF-8 but codepage %u is in use, maybe use --utf8?\n",
638
                            str->str.cstr, current_codepage );
639 640 641
        free_string( str );
        return str_w;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
642 643
}

644
static string_t *get_buffered_wstring(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
645 646 647 648
{
	string_t *str = new_string();
	str->size = wbufidx;
	str->type = str_unicode;
649 650
	str->str.wstr = xmalloc((wbufidx+1)*sizeof(WCHAR));
	memcpy(str->str.wstr, wbuffer, wbufidx*sizeof(WCHAR));
Alexandre Julliard's avatar
Alexandre Julliard committed
651 652 653 654
	str->str.wstr[wbufidx] = 0;
	return str;
}

655
static string_t *make_string(char *s)
Alexandre Julliard's avatar
Alexandre Julliard committed
656
{
657
	string_t *ret, *str = new_string();
Alexandre Julliard's avatar
Alexandre Julliard committed
658 659
	str->size = strlen(s);
	str->type = str_char;
660
	str->str.cstr = xmalloc(str->size+1);
Alexandre Julliard's avatar
Alexandre Julliard committed
661
	memcpy(str->str.cstr, s, str->size+1);
662 663 664 665
        if (current_codepage <= 0 || !win32) return str;
	ret = convert_string_unicode( str, current_codepage );
	free_string( str );
	return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
666
}