parser.l 19.4 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 36 37
 * 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
 *			  double quoted names to be scanned propperly.
 *
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 98
/* Some shortcut definitions */
ws	[ \f\t\r]
cident	[a-zA-Z_][0-9a-zA-Z_]*

%{

/*#define LEX_DEBUG*/

99 100
#include "config.h"

Alexandre Julliard's avatar
Alexandre Julliard committed
101 102 103
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
104
#include <ctype.h>
105
#include <assert.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
106

107 108 109 110
#ifndef HAVE_UNISTD_H
#define YY_NO_UNISTD_H
#endif

111
#include "wine/unicode.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
112 113 114 115 116
#include "wrc.h"
#include "utils.h"
#include "parser.h"
#include "newstruc.h"

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

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

122
static void addcchar(char c);
123
static void addwchar(WCHAR s);
124 125 126
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
127

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

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

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
/*
 * 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
156 157

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

static struct keyword keywords[] = {
166 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
	{ "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},
205
	{ "HTML",		tHTML,			0, 0, 0},
206 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
	{ "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
242 243 244
};

#define NKEYWORDS	(sizeof(keywords)/sizeof(keywords[0]))
245
#define KWP(p)		((const struct keyword *)(p))
246
static int kw_cmp_func(const void *s1, const void *s2)
Alexandre Julliard's avatar
Alexandre Julliard committed
247 248
{
	int ret;
249
	ret = strcasecmp(KWP(s1)->keyword, KWP(s2)->keyword);
Alexandre Julliard's avatar
Alexandre Julliard committed
250 251 252 253 254 255 256 257
	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
258
static struct keyword *iskeyword(char *kw)
Alexandre Julliard's avatar
Alexandre Julliard committed
259 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 299
{
	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;
}

%}

300 301 302 303 304
/*
 **************************************************************************
 * The flexer starts here
 **************************************************************************
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
305
%%
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
	/*
	 * 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.
	 */
324 325
<INITIAL,pp_cstrip>^{ws}*\#{ws}*pragma{ws}+	yy_push_state(pp_pragma);
<INITIAL,pp_cstrip>^{ws}*\#{ws}*	yy_push_state(pp_line);
326
<pp_line>[^\n]*	{
327
		int lineno, len;
328 329 330 331 332
		char *cptr;
		char *fname;
		yy_pop_state();
		lineno = (int)strtol(yytext, &cptr, 10);
		if(!lineno)
333
			parser_error("Malformed '#...' line-directive; invalid linenumber");
334 335
		fname = strchr(cptr, '"');
		if(!fname)
336
			parser_error("Malformed '#...' line-directive; missing filename");
337 338 339
		fname++;
		cptr = strchr(fname, '"');
		if(!cptr)
340
			parser_error("Malformed '#...' line-directive; missing terminating \"");
341 342 343
		*cptr = '\0';
		line_number = lineno - 1;	/* We didn't read the newline */
		input_name = xstrdup(fname);
344 345 346 347 348 349
                /* 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);
350 351
	}

352
<pp_pragma>code_page[^\n]*	yyless(9); yy_pop_state(); yy_push_state(pp_code_page);
353
<pp_pragma>[^\n]*		yy_pop_state(); if (pedantic) parser_warning("Unrecognized #pragma directive '%s'\n",yytext);
354 355

<pp_code_page>\({ws}*default{ws}*\)[^\n]*	current_codepage = -1; yy_pop_state();
356
<pp_code_page>\({ws}*utf8{ws}*\)[^\n]*		current_codepage = CP_UTF8; yy_pop_state();
357 358 359 360 361
<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 );
362
        if (current_codepage && current_codepage != CP_UTF8 && !wine_cp_get_table( current_codepage ))
363
        {
364
            parser_error("Codepage %d not supported", current_codepage);
365 366 367
            current_codepage = 0;
        }
    }
368
<pp_code_page>[^\n]*	yy_pop_state(); parser_error("Malformed #pragma code_page directive");
369

370 371 372 373 374
	/*
	 * Strip everything until a ';' taking
	 * into account braces {} for structures,
	 * classes and enums.
	 */
375 376
<pp_cstrip>\n			line_number++; char_number = 1;
<pp_cstrip>.			; /* ignore */
Alexandre Julliard's avatar
Alexandre Julliard committed
377 378 379 380

\{			return tBEGIN;
\}			return tEND;

381 382 383
[0-9]+[lL]?		{ parser_lval.num = strtoul(yytext,  0, 10); return toupper(yytext[yyleng-1]) == 'L' ? tLNUMBER : tNUMBER; }
0[xX][0-9A-Fa-f]+[lL]?	{ parser_lval.num = strtoul(yytext,  0, 16); return toupper(yytext[yyleng-1]) == 'L' ? tLNUMBER : tNUMBER; }
0[oO][0-7]+[lL]?	{ parser_lval.num = strtoul(yytext+2, 0, 8); return toupper(yytext[yyleng-1]) == 'L' ? tLNUMBER : tNUMBER; }
384 385

	/*
386
	 * The next two rules scan identifiers and filenames.
387 388 389 390 391
	 * This is achieved by using the priority ruling
	 * of the scanner where a '.' is valid in a filename
	 * and *only* in a filename. In this case, the second
	 * rule will be reduced because it is longer.
	 */
392
[A-Za-z_0-9.]+		{
393
				struct keyword *tok = iskeyword(yytext);
Alexandre Julliard's avatar
Alexandre Julliard committed
394

395
				if(tok)
Alexandre Julliard's avatar
Alexandre Julliard committed
396
				{
397
					if(wanted_id && !tok->alwayskw)
398
					{
399
						parser_lval.str = make_string(yytext);
400 401
						return tIDENT;
					}
402 403
					else
						return tok->token;
Alexandre Julliard's avatar
Alexandre Julliard committed
404 405 406
				}
				else
				{
407
					parser_lval.str = make_string(yytext);
408
					return tIDENT;
Alexandre Julliard's avatar
Alexandre Julliard committed
409 410
				}
			}
411
[A-Za-z_0-9./\\]+		parser_lval.str = make_string(yytext); return tFILENAME;
Alexandre Julliard's avatar
Alexandre Julliard committed
412

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

442
<tklstr>\\[0-9]+	parser_error("Bad escape sequence");
443 444 445 446 447 448 449 450 451 452 453 454 455 456
<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');
<tklstr>\\.		addwchar(yytext[1]);
<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
457 458 459 460
				char *yptr = yytext;
				while(*yptr)	/* FIXME: codepage translation */
					addwchar(*yptr++ & 0xff);
			}
461
<tklstr>\n		parser_error("Unterminated string");
Alexandre Julliard's avatar
Alexandre Julliard committed
462

463 464 465
	/*
	 * Normal string scanning
	 */
466 467 468
\"			yy_push_state(tkstr); cbufidx = 0;
<tkstr>\"{ws}+	|
<tkstr>\"		{
469
				yy_pop_state();
470
				parser_lval.str = get_buffered_cstring();
Alexandre Julliard's avatar
Alexandre Julliard committed
471 472
				return tSTRING;
			}
473
<tkstr>\\[0-7]{1,3}	{ /* octal escape sequence */
Alexandre Julliard's avatar
Alexandre Julliard committed
474 475 476
				int result;
				result = strtol(yytext+1, 0, 8);
				if ( result > 0xff )
477
					parser_error("Character constant out of range");
Alexandre Julliard's avatar
Alexandre Julliard committed
478 479
				addcchar((char)result);
			}
480
<tkstr>\\x[0-9a-fA-F]{2} {  /* hex escape sequence */
Alexandre Julliard's avatar
Alexandre Julliard committed
481 482 483 484
				int result;
				result = strtol(yytext+2, 0, 16);
				addcchar((char)result);
			}
485
<tkstr>\\x[0-9a-fA-F]	{  parser_error("Invalid hex escape sequence '%s'", yytext); }
486

487
<tkstr>\\[0-9]+		parser_error("Bad escape sequence");
488 489 490 491 492 493 494 495 496 497 498
<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
499 500 501 502
				char *yptr = yytext;
				while(*yptr)
					addcchar(*yptr++);
			}
503 504 505
<tkstr>\"\"		addcchar('\"');		/* "bla""bla"   -> "bla\"bla" */
<tkstr>\\\"\"		addcchar('\"');		/* "bla\""bla"  -> "bla\"bla" */
<tkstr>\"{ws}+\"	;			/* "bla" "bla"  -> "blabla" */
506
<tkstr>\n		parser_error("Unterminated string");
Alexandre Julliard's avatar
Alexandre Julliard committed
507

508 509 510
	/*
	 * Raw data scanning
	 */
511 512
\'			yy_push_state(tkrcd); cbufidx = 0;
<tkrcd>\'		{
513
				yy_pop_state();
514 515 516 517
				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);
518
				return tRAWDATA;
Alexandre Julliard's avatar
Alexandre Julliard committed
519
			}
520
<tkrcd>[0-9a-fA-F]{2}	{
Alexandre Julliard's avatar
Alexandre Julliard committed
521 522 523 524
				int result;
				result = strtol(yytext, 0, 16);
				addcchar((char)result);
			}
525 526
<tkrcd>{ws}+		;	/* Ignore space */
<tkrcd>\n		line_number++; char_number = 1;
527
<tkrcd>.		parser_error("Malformed data-line");
Alexandre Julliard's avatar
Alexandre Julliard committed
528

529 530 531 532
	/*
	 * Comment stripping
	 * Should never occur after preprocessing
	 */
533
<INITIAL,pp_cstrip>"/*"	{
534 535 536
				yy_push_state(comment);
				save_wanted_id = wanted_id;
				if(!no_preprocess)
537
					parser_warning("Found comments after preprocessing, please report\n");
538
			}
Alexandre Julliard's avatar
Alexandre Julliard committed
539 540
<comment>[^*\n]*	;
<comment>"*"+[^*/\n]*	;
Alexandre Julliard's avatar
Alexandre Julliard committed
541
<comment>\n		line_number++; char_number = 1;
542
<comment>"*"+"/"	yy_pop_state(); want_id = save_wanted_id;
Alexandre Julliard's avatar
Alexandre Julliard committed
543

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

547
\n			{
548
				want_id = wanted_id;
Alexandre Julliard's avatar
Alexandre Julliard committed
549
				line_number++;
Alexandre Julliard's avatar
Alexandre Julliard committed
550
				char_number = 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
551 552 553 554 555 556
				if(want_nl)
				{
					want_nl = 0;
					return tNL;
				}
			}
557
{ws}+			want_id = wanted_id;	/* Eat whitespace */
Alexandre Julliard's avatar
Alexandre Julliard committed
558 559

<INITIAL>.		return yytext[0];
560

561 562 563 564 565 566 567
<*>.|\n			{
				/* Catch all rule to find any unmatched text */
				if(*yytext == '\n')
				{
					line_number++;
					char_number = 1;
				}
568
				parser_warning("Unmatched text '%c' (0x%02x) YY_START=%d\n",
569
					isprint(*yytext & 0xff) ? *yytext : '.', *yytext, YY_START);
570 571
			}

Alexandre Julliard's avatar
Alexandre Julliard committed
572 573 574 575 576
%%

/* These dup functions copy the enclosed '\0' from
 * the resource string.
 */
577
static void addcchar(char c)
Alexandre Julliard's avatar
Alexandre Julliard committed
578
{
579 580 581 582 583
	if(cbufidx >= cbufalloc)
	{
		cbufalloc += 1024;
		cbuffer = xrealloc(cbuffer, cbufalloc * sizeof(cbuffer[0]));
		if(cbufalloc > 65536)
584
			parser_warning("Reallocating string buffer larger than 64kB\n");
585
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
586 587 588
	cbuffer[cbufidx++] = c;
}

589
static void addwchar(WCHAR s)
Alexandre Julliard's avatar
Alexandre Julliard committed
590
{
591 592 593 594 595
	if(wbufidx >= wbufalloc)
	{
		wbufalloc += 1024;
		wbuffer = xrealloc(wbuffer, wbufalloc * sizeof(wbuffer[0]));
		if(wbufalloc > 65536)
596
			parser_warning("Reallocating wide string buffer larger than 64kB\n");
597
	}
598
	wbuffer[wbufidx++] = s;
Alexandre Julliard's avatar
Alexandre Julliard committed
599 600
}

601
static string_t *get_buffered_cstring(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
602
{
603 604 605 606
    string_t *str = new_string();

    str->size = cbufidx;
    str->type = str_char;
607
    str->str.cstr = xmalloc(cbufidx+1);
608 609 610 611 612
    memcpy(str->str.cstr, cbuffer, cbufidx);
    str->str.cstr[cbufidx] = '\0';

    if (!current_codepage || current_codepage == -1 || !win32)  /* store as ANSI string */
    {
613
        if (!current_codepage) parser_error("Codepage set to Unicode only, cannot use ASCII string here");
614 615 616 617 618 619
        return str;
    }
    else  /* convert to Unicode before storing */
    {
        string_t *str_w = convert_string( str, str_unicode, current_codepage );
        if (!check_unicode_conversion( str, str_w, current_codepage ))
620
            parser_error("String %s does not convert identically to Unicode and back in codepage %d. "
621
                    "Try using a Unicode string instead", str->str.cstr, current_codepage );
622 623 624
        free_string( str );
        return str_w;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
625 626
}

627
static string_t *get_buffered_wstring(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
628 629 630 631
{
	string_t *str = new_string();
	str->size = wbufidx;
	str->type = str_unicode;
632 633
	str->str.wstr = xmalloc((wbufidx+1)*sizeof(WCHAR));
	memcpy(str->str.wstr, wbuffer, wbufidx*sizeof(WCHAR));
Alexandre Julliard's avatar
Alexandre Julliard committed
634 635 636 637
	str->str.wstr[wbufidx] = 0;
	return str;
}

638
static string_t *make_string(char *s)
Alexandre Julliard's avatar
Alexandre Julliard committed
639 640 641 642
{
	string_t *str = new_string();
	str->size = strlen(s);
	str->type = str_char;
643
	str->str.cstr = xmalloc(str->size+1);
Alexandre Julliard's avatar
Alexandre Julliard committed
644 645 646
	memcpy(str->str.cstr, s, str->size+1);
	return str;
}