parser.l 20.6 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 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
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#else
112 113 114
#define YY_NO_UNISTD_H
#endif

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

121
#include "parser.tab.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
122

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

126 127
#define YY_USER_INIT current_codepage = -1;

128
static void addcchar(char c);
129
static void addwchar(WCHAR s);
130 131 132
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
133

134
static char *cbuffer;		/* Buffers for string collection */
Alexandre Julliard's avatar
Alexandre Julliard committed
135
static int cbufidx;
136
static int cbufalloc = 0;
137
static WCHAR *wbuffer;
Alexandre Julliard's avatar
Alexandre Julliard committed
138
static int wbufidx;
139
static int wbufalloc = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
140

141 142
static int current_codepage = -1;  /* use language default */

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
/*
 * 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
162 163

struct keyword {
164 165 166 167 168
	const char	*keyword;
	int		token;
	int		isextension;
	int		needcase;
	int		alwayskw;
Alexandre Julliard's avatar
Alexandre Julliard committed
169 170 171
};

static struct keyword keywords[] = {
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 207 208 209 210
	{ "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},
211
	{ "HTML",		tHTML,			0, 0, 0},
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 244 245 246 247
	{ "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
248 249 250
};

#define NKEYWORDS	(sizeof(keywords)/sizeof(keywords[0]))
251
#define KWP(p)		((const struct keyword *)(p))
252
static int kw_cmp_func(const void *s1, const void *s2)
Alexandre Julliard's avatar
Alexandre Julliard committed
253 254
{
	int ret;
255
	ret = strcasecmp(KWP(s1)->keyword, KWP(s2)->keyword);
Alexandre Julliard's avatar
Alexandre Julliard committed
256 257 258 259 260 261 262 263
	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
264
static struct keyword *iskeyword(char *kw)
Alexandre Julliard's avatar
Alexandre Julliard committed
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 300 301 302 303
{
	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;
}

304 305 306 307 308 309 310 311 312
/* 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)
313
        parser_error("integer constant %s is too large", nptr);
314 315 316
    return l;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
317 318
%}

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

371
<pp_pragma>code_page[^\n]*	yyless(9); yy_pop_state(); yy_push_state(pp_code_page);
372
<pp_pragma>[^\n]*		yy_pop_state(); if (pedantic) parser_warning("Unrecognized #pragma directive '%s'\n",yytext);
373 374

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

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

\{			return tBEGIN;
\}			return tEND;

400 401 402 403 404 405
[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; }
406 407

	/*
408
	 * The next two rules scan identifiers and filenames.
409 410 411 412 413
	 * 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.
	 */
414
[A-Za-z_0-9.]+		{
415
				struct keyword *tok = iskeyword(yytext);
Alexandre Julliard's avatar
Alexandre Julliard committed
416

417
				if(tok)
Alexandre Julliard's avatar
Alexandre Julliard committed
418
				{
419
					if(wanted_id && !tok->alwayskw)
420
					{
421
						parser_lval.str = make_string(yytext);
422 423
						return tIDENT;
					}
424 425
					else
						return tok->token;
Alexandre Julliard's avatar
Alexandre Julliard committed
426 427 428
				}
				else
				{
429
					parser_lval.str = make_string(yytext);
430
					return tIDENT;
Alexandre Julliard's avatar
Alexandre Julliard committed
431 432
				}
			}
433
[A-Za-z_0-9./\\]+		parser_lval.str = make_string(yytext); return tFILENAME;
Alexandre Julliard's avatar
Alexandre Julliard committed
434

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

464
<tklstr>\\[0-9]+	parser_error("Bad escape sequence");
465 466 467 468 469 470 471 472
<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');
473 474 475 476 477
<tklstr>\\.		{
			    if (yytext[1] & 0x80)
                                parser_error("Invalid char %u in wide string", (unsigned char)yytext[1]);
			    addwchar(yytext[1]);
			}
478 479 480 481 482
<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
483 484
				char *yptr = yytext;
				while(*yptr)	/* FIXME: codepage translation */
485 486 487 488 489
                                {
                                    if (*yptr & 0x80)
                                        parser_error("Invalid char %u in wide string", (unsigned char)*yptr);
                                    addwchar(*yptr++ & 0xff);
                                }
Alexandre Julliard's avatar
Alexandre Julliard committed
490
			}
491
<tklstr>\n		parser_error("Unterminated string");
Alexandre Julliard's avatar
Alexandre Julliard committed
492

493 494 495
	/*
	 * Normal string scanning
	 */
496 497 498
\"			yy_push_state(tkstr); cbufidx = 0;
<tkstr>\"{ws}+	|
<tkstr>\"		{
499
				yy_pop_state();
500
				parser_lval.str = get_buffered_cstring();
Alexandre Julliard's avatar
Alexandre Julliard committed
501 502
				return tSTRING;
			}
503
<tkstr>\\[0-7]{1,3}	{ /* octal escape sequence */
Alexandre Julliard's avatar
Alexandre Julliard committed
504 505 506
				int result;
				result = strtol(yytext+1, 0, 8);
				if ( result > 0xff )
507
					parser_error("Character constant out of range");
Alexandre Julliard's avatar
Alexandre Julliard committed
508 509
				addcchar((char)result);
			}
510
<tkstr>\\x[0-9a-fA-F]{2} {  /* hex escape sequence */
Alexandre Julliard's avatar
Alexandre Julliard committed
511 512 513 514
				int result;
				result = strtol(yytext+2, 0, 16);
				addcchar((char)result);
			}
515
<tkstr>\\x[0-9a-fA-F]	{  parser_error("Invalid hex escape sequence '%s'", yytext); }
516

517
<tkstr>\\[0-9]+		parser_error("Bad escape sequence");
518 519 520 521 522 523 524 525 526 527 528
<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
529 530 531 532
				char *yptr = yytext;
				while(*yptr)
					addcchar(*yptr++);
			}
533 534 535
<tkstr>\"\"		addcchar('\"');		/* "bla""bla"   -> "bla\"bla" */
<tkstr>\\\"\"		addcchar('\"');		/* "bla\""bla"  -> "bla\"bla" */
<tkstr>\"{ws}+\"	;			/* "bla" "bla"  -> "blabla" */
536
<tkstr>\n		parser_error("Unterminated string");
Alexandre Julliard's avatar
Alexandre Julliard committed
537

538 539 540
	/*
	 * Raw data scanning
	 */
541 542
\'			yy_push_state(tkrcd); cbufidx = 0;
<tkrcd>\'		{
543
				yy_pop_state();
544 545 546 547
				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);
548
				return tRAWDATA;
Alexandre Julliard's avatar
Alexandre Julliard committed
549
			}
550
<tkrcd>[0-9a-fA-F]{2}	{
Alexandre Julliard's avatar
Alexandre Julliard committed
551 552 553 554
				int result;
				result = strtol(yytext, 0, 16);
				addcchar((char)result);
			}
555 556
<tkrcd>{ws}+		;	/* Ignore space */
<tkrcd>\n		line_number++; char_number = 1;
557
<tkrcd>.		parser_error("Malformed data-line");
Alexandre Julliard's avatar
Alexandre Julliard committed
558

559 560 561 562
	/*
	 * Comment stripping
	 * Should never occur after preprocessing
	 */
563
<INITIAL,pp_cstrip>"/*"	{
564 565 566
				yy_push_state(comment);
				save_wanted_id = wanted_id;
				if(!no_preprocess)
567
					parser_warning("Found comments after preprocessing, please report\n");
568
			}
Alexandre Julliard's avatar
Alexandre Julliard committed
569 570
<comment>[^*\n]*	;
<comment>"*"+[^*/\n]*	;
Alexandre Julliard's avatar
Alexandre Julliard committed
571
<comment>\n		line_number++; char_number = 1;
572
<comment>"*"+"/"	yy_pop_state(); want_id = save_wanted_id;
Alexandre Julliard's avatar
Alexandre Julliard committed
573

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

577
\n			{
578
				want_id = wanted_id;
Alexandre Julliard's avatar
Alexandre Julliard committed
579
				line_number++;
Alexandre Julliard's avatar
Alexandre Julliard committed
580
				char_number = 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
581 582 583 584 585 586
				if(want_nl)
				{
					want_nl = 0;
					return tNL;
				}
			}
587
{ws}+			want_id = wanted_id;	/* Eat whitespace */
Alexandre Julliard's avatar
Alexandre Julliard committed
588

589
<INITIAL>[ -~]		return yytext[0];
590

591 592 593 594 595 596 597
<*>.|\n			{
				/* Catch all rule to find any unmatched text */
				if(*yytext == '\n')
				{
					line_number++;
					char_number = 1;
				}
598 599
				parser_error("Unmatched text '%c' (0x%02x) YY_START=%d",
                                             isprint((unsigned char)*yytext) ? *yytext : '.', *yytext, YY_START);
600 601
			}

Alexandre Julliard's avatar
Alexandre Julliard committed
602 603 604 605 606
%%

/* These dup functions copy the enclosed '\0' from
 * the resource string.
 */
607
static void addcchar(char c)
Alexandre Julliard's avatar
Alexandre Julliard committed
608
{
609 610 611 612 613
	if(cbufidx >= cbufalloc)
	{
		cbufalloc += 1024;
		cbuffer = xrealloc(cbuffer, cbufalloc * sizeof(cbuffer[0]));
		if(cbufalloc > 65536)
614
			parser_warning("Reallocating string buffer larger than 64kB\n");
615
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
616 617 618
	cbuffer[cbufidx++] = c;
}

619
static void addwchar(WCHAR s)
Alexandre Julliard's avatar
Alexandre Julliard committed
620
{
621 622 623 624 625
	if(wbufidx >= wbufalloc)
	{
		wbufalloc += 1024;
		wbuffer = xrealloc(wbuffer, wbufalloc * sizeof(wbuffer[0]));
		if(wbufalloc > 65536)
626
			parser_warning("Reallocating wide string buffer larger than 64kB\n");
627
	}
628
	wbuffer[wbufidx++] = s;
Alexandre Julliard's avatar
Alexandre Julliard committed
629 630
}

631
static string_t *get_buffered_cstring(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
632
{
633 634 635 636
    string_t *str = new_string();

    str->size = cbufidx;
    str->type = str_char;
637
    str->str.cstr = xmalloc(cbufidx+1);
638 639 640 641 642
    memcpy(str->str.cstr, cbuffer, cbufidx);
    str->str.cstr[cbufidx] = '\0';

    if (!current_codepage || current_codepage == -1 || !win32)  /* store as ANSI string */
    {
643
        if (!current_codepage) parser_error("Codepage set to Unicode only, cannot use ASCII string here");
644 645 646 647 648 649
        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 ))
650
            parser_error("String %s does not convert identically to Unicode and back in codepage %d. "
651
                    "Try using a Unicode string instead", str->str.cstr, current_codepage );
652
        if (check_valid_utf8( str, current_codepage ))
653
            parser_warning( "string \"%s\" seems to be UTF-8 but codepage %u is in use.\n",
654
                            str->str.cstr, current_codepage );
655 656 657
        free_string( str );
        return str_w;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
658 659
}

660
static string_t *get_buffered_wstring(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
661 662 663 664
{
	string_t *str = new_string();
	str->size = wbufidx;
	str->type = str_unicode;
665 666
	str->str.wstr = xmalloc((wbufidx+1)*sizeof(WCHAR));
	memcpy(str->str.wstr, wbuffer, wbufidx*sizeof(WCHAR));
Alexandre Julliard's avatar
Alexandre Julliard committed
667 668 669 670
	str->str.wstr[wbufidx] = 0;
	return str;
}

671
static string_t *make_string(char *s)
Alexandre Julliard's avatar
Alexandre Julliard committed
672 673 674 675
{
	string_t *str = new_string();
	str->size = strlen(s);
	str->type = str_char;
676
	str->str.cstr = xmalloc(str->size+1);
Alexandre Julliard's avatar
Alexandre Julliard committed
677 678 679
	memcpy(str->str.cstr, s, str->size+1);
	return str;
}