parser.l 19.8 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>
Huw Davies's avatar
Huw Davies committed
106
#include <errno.h>
107
#include <limits.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
108

109 110 111 112
#ifndef HAVE_UNISTD_H
#define YY_NO_UNISTD_H
#endif

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

119
#include "parser.tab.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
120

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

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

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

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

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

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

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

#define NKEYWORDS	(sizeof(keywords)/sizeof(keywords[0]))
247
#define KWP(p)		((const struct keyword *)(p))
248
static int kw_cmp_func(const void *s1, const void *s2)
Alexandre Julliard's avatar
Alexandre Julliard committed
249 250
{
	int ret;
251
	ret = strcasecmp(KWP(s1)->keyword, KWP(s2)->keyword);
Alexandre Julliard's avatar
Alexandre Julliard committed
252 253 254 255 256 257 258 259
	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
260
static struct keyword *iskeyword(char *kw)
Alexandre Julliard's avatar
Alexandre Julliard committed
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 305 306 307 308
/* 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)
309
        parser_error("integer constant %s is too large", nptr);
310 311 312
    return l;
}

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

315 316 317 318 319
/*
 **************************************************************************
 * The flexer starts here
 **************************************************************************
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
320
%%
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
	/*
	 * 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.
	 */
339 340
<INITIAL,pp_cstrip>^{ws}*\#{ws}*pragma{ws}+	yy_push_state(pp_pragma);
<INITIAL,pp_cstrip>^{ws}*\#{ws}*	yy_push_state(pp_line);
341
<pp_line>[^\n]*	{
342
		int lineno, len;
343 344 345 346 347
		char *cptr;
		char *fname;
		yy_pop_state();
		lineno = (int)strtol(yytext, &cptr, 10);
		if(!lineno)
348
			parser_error("Malformed '#...' line-directive; invalid linenumber");
349 350
		fname = strchr(cptr, '"');
		if(!fname)
351
			parser_error("Malformed '#...' line-directive; missing filename");
352 353 354
		fname++;
		cptr = strchr(fname, '"');
		if(!cptr)
355
			parser_error("Malformed '#...' line-directive; missing terminating \"");
356 357 358
		*cptr = '\0';
		line_number = lineno - 1;	/* We didn't read the newline */
		input_name = xstrdup(fname);
359 360 361 362 363 364
                /* 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);
365 366
	}

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

<pp_code_page>\({ws}*default{ws}*\)[^\n]*	current_codepage = -1; yy_pop_state();
371
<pp_code_page>\({ws}*utf8{ws}*\)[^\n]*		current_codepage = CP_UTF8; yy_pop_state();
372 373 374 375 376
<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 );
377
        if (current_codepage != CP_UTF8 && !wine_cp_get_table( current_codepage ))
378
        {
379
            parser_error("Codepage %d not supported", current_codepage);
380 381 382
            current_codepage = 0;
        }
    }
383
<pp_code_page>[^\n]*	yy_pop_state(); parser_error("Malformed #pragma code_page directive");
384

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

\{			return tBEGIN;
\}			return tEND;

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

	/*
401
	 * The next two rules scan identifiers and filenames.
402 403 404 405 406
	 * 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.
	 */
407
[A-Za-z_0-9.]+		{
408
				struct keyword *tok = iskeyword(yytext);
Alexandre Julliard's avatar
Alexandre Julliard committed
409

410
				if(tok)
Alexandre Julliard's avatar
Alexandre Julliard committed
411
				{
412
					if(wanted_id && !tok->alwayskw)
413
					{
414
						parser_lval.str = make_string(yytext);
415 416
						return tIDENT;
					}
417 418
					else
						return tok->token;
Alexandre Julliard's avatar
Alexandre Julliard committed
419 420 421
				}
				else
				{
422
					parser_lval.str = make_string(yytext);
423
					return tIDENT;
Alexandre Julliard's avatar
Alexandre Julliard committed
424 425
				}
			}
426
[A-Za-z_0-9./\\]+		parser_lval.str = make_string(yytext); return tFILENAME;
Alexandre Julliard's avatar
Alexandre Julliard committed
427

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

457
<tklstr>\\[0-9]+	parser_error("Bad escape sequence");
458 459 460 461 462 463 464 465 466 467 468 469 470 471
<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
472 473 474 475
				char *yptr = yytext;
				while(*yptr)	/* FIXME: codepage translation */
					addwchar(*yptr++ & 0xff);
			}
476
<tklstr>\n		parser_error("Unterminated string");
Alexandre Julliard's avatar
Alexandre Julliard committed
477

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

502
<tkstr>\\[0-9]+		parser_error("Bad escape sequence");
503 504 505 506 507 508 509 510 511 512 513
<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
514 515 516 517
				char *yptr = yytext;
				while(*yptr)
					addcchar(*yptr++);
			}
518 519 520
<tkstr>\"\"		addcchar('\"');		/* "bla""bla"   -> "bla\"bla" */
<tkstr>\\\"\"		addcchar('\"');		/* "bla\""bla"  -> "bla\"bla" */
<tkstr>\"{ws}+\"	;			/* "bla" "bla"  -> "blabla" */
521
<tkstr>\n		parser_error("Unterminated string");
Alexandre Julliard's avatar
Alexandre Julliard committed
522

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

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

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

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

<INITIAL>.		return yytext[0];
575

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

Alexandre Julliard's avatar
Alexandre Julliard committed
587 588 589 590 591
%%

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

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

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

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

    if (!current_codepage || current_codepage == -1 || !win32)  /* store as ANSI string */
    {
628
        if (!current_codepage) parser_error("Codepage set to Unicode only, cannot use ASCII string here");
629 630 631 632 633 634
        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 ))
635
            parser_error("String %s does not convert identically to Unicode and back in codepage %d. "
636
                    "Try using a Unicode string instead", str->str.cstr, current_codepage );
637 638 639
        free_string( str );
        return str_w;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
640 641
}

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

653
static string_t *make_string(char *s)
Alexandre Julliard's avatar
Alexandre Julliard committed
654 655 656 657
{
	string_t *str = new_string();
	str->size = strlen(s);
	str->type = str_char;
658
	str->str.cstr = xmalloc(str->size+1);
Alexandre Julliard's avatar
Alexandre Julliard committed
659 660 661
	memcpy(str->str.cstr, s, str->size+1);
	return str;
}