/* -*-C-*-
 *
 * Copyright 1994 Martin von Loewis
 * Copyright 1998 Bertho A. Stultiens (BS)
 *
 * 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.
 *
 * 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 rules when looking for a filename */
%x yywf
%x yywf_s
/* Exclusive string handling */
%x yystr
/* Exclusive unicode string handling */
%x yylstr
/* Exclusive rcdata single quoted data handling */
%x yyrcd
/* Exclusive comment eating... */
%x comment
/* Preprocessor exclusives */
%x pp_incl
%x pp_def
%x pp_undef
%x pp_if
%x pp_ifdef
%x pp_ifndef
%x pp_elif
%x pp_else
%x pp_endif
%x pp_error
/* Set when accumulating #define's expansion text */
%x pp_def_s
/* Set when processing function type defines */
%x pp_ignore
/* Set when need to strip to eol */
%x pp_ignore_eol
/* Set when handling a false #if case */
%x pp_false
/* Set when stripping c-junk */
%x pp_strips
%x pp_stripp
%x pp_stripp_final

/*%option stack*/
%option never-interactive
/*%option noyywrap */
/* Some shortcut definitions */
ws	[ \f\t\r]
cident	[a-zA-Z_][0-9a-zA-Z_]*

%{

#if !defined(YY_FLEX_MAJOR_VERSION) || (1000 * YY_FLEX_MAJOR_VERSION + YY_FLEX_MINOR_VERSION < 2005)
#error Must use flex version 2.5.1 or higher (yy_scan_* routines are required).
#endif

/*#define LEX_DEBUG*/

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "wrc.h"
#include "utils.h"
#include "preproc.h"
#include "parser.h"
#include "newstruc.h"

#include "y.tab.h"

#define YY_USE_PROTOS
#define YY_NO_UNPUT

/* Always update the current character position within a line */
#define YY_USER_ACTION	char_number+=yyleng;

raw_data_t *new_raw_data(void);

void addcchar(char c);
void addwchar(short s);
string_t *get_buffered_cstring(void);
string_t *get_buffered_wstring(void);
string_t *make_string(char *s);
string_t *make_filename(char *s, int len);

int line_number = 1;		/* The current line */
int char_number = 1;		/* The current char pos within the line */
static char *cbuffer;		/* Buffers for string collection */
static int cbufidx;
static int cbufalloc = 0;
static short *wbuffer;
static int wbufidx;
static int wbufalloc = 0;
static int want_nl = 0;		/* Set when newline needs to go to parser */
static int want_ident = 0;	/* Set is #ifdef, #ifndef or defined is seen */
static int stripslevel = 0;	/* Count {} during pp_strips mode */
static int stripplevel = 0;	/* Count () during pp_strips mode */
static char *substtext = NULL;	/* Holds the substition text while getting a define */
static int cjunk_tagline;	/* Where did we start stripping (helps error tracking) */

#ifdef YY_USE_STACK
void push_to(int start)	{ yy_push_state(start); }
void pop_start(void)	{ yy_pop_state(start); }
#else
#define MAXSTARTSTACK	32
static int startstack[MAXSTARTSTACK];
static int startstackidx = 0;

void push_to(int start)
{
	if(yydebug)
		printf("push_to(%d): %d -> %d\n", line_number, YY_START, start);
	if(startstackidx >= MAXSTARTSTACK-1)
		internal_error(__FILE__, __LINE__, "Start condition stack overflow");
	startstack[startstackidx++] = YY_START;
	BEGIN(start);
}

void pop_start(void)
{
	if(yydebug)
		printf("pop_start(%d): %d <- %d\n", line_number, startstack[startstackidx-1], YY_START);
	if(startstackidx <= 0)
		internal_error(__FILE__, __LINE__, "Start condition stack underflow");
	--startstackidx;
	BEGIN(startstack[startstackidx]);
}
#endif


struct bufferstackentry {
	YY_BUFFER_STATE	bufferstate;	/* Buffer to switch back to */
	struct pp_entry	*define;	/* Points to expanding define
					   or NULL if handling includes
					 */
	int line_number;		/* Line that we were handling */
	int char_number;		/* The current position */
	char *filename;			/* Filename that we were handling */
};

#define MAXBUFFERSTACK 128
static struct bufferstackentry bufferstack[MAXBUFFERSTACK];
static int bufferstackidx = 0;

void push_buffer(YY_BUFFER_STATE buf, struct pp_entry *ppp, char *filename)
{
	if(yydebug)
		printf("push_buffer: %p %p %p\n", buf, ppp, filename);
	if(bufferstackidx >= MAXBUFFERSTACK-1)
		internal_error(__FILE__, __LINE__, "Buffer stack overflow");
	memset(&bufferstack[bufferstackidx], 0, sizeof(bufferstack[0]));
	bufferstack[bufferstackidx].bufferstate = buf;
	bufferstack[bufferstackidx].define = ppp;
	if(ppp)
		ppp->expanding = 1;
	else if(filename)
	{
		/* These will track the yyerror to the correct file and line */
		bufferstack[bufferstackidx].line_number = line_number;
		bufferstack[bufferstackidx].char_number = char_number;
		line_number = 1;
		char_number = 1;
		bufferstack[bufferstackidx].filename = input_name;
		input_name = filename;
	}
	else
		internal_error(__FILE__, __LINE__, "Pushing buffer without knowing where to go to");
	bufferstackidx++;
}

YY_BUFFER_STATE pop_buffer(void)
{
	if(bufferstackidx <= 0)
		return (YY_BUFFER_STATE)0;
	bufferstackidx--;
	if(bufferstack[bufferstackidx].define)
		bufferstack[bufferstackidx].define->expanding = 0;
	else
	{
		line_number = bufferstack[bufferstackidx].line_number;
		char_number = bufferstack[bufferstackidx].char_number;
		input_name = bufferstack[bufferstackidx].filename;
		fclose(yyin);
	}
	if(yydebug)
		printf("pop_buffer: %p %p (%d, %d) %p\n",
			bufferstack[bufferstackidx].bufferstate,
			bufferstack[bufferstackidx].define,
			bufferstack[bufferstackidx].line_number,
			bufferstack[bufferstackidx].char_number,
			bufferstack[bufferstackidx].filename);
	yy_switch_to_buffer(bufferstack[bufferstackidx].bufferstate);
	return bufferstack[bufferstackidx].bufferstate;
}

void do_include(char *name, int namelen)
{
	char *cpy = (char *)xmalloc(namelen);
	strcpy(cpy, name+1);	/* strip leading " or < */
	cpy[namelen-2] = '\0';	/* strip trailing " or > */
	if((yyin = open_include(cpy, name[0] == '"')) == NULL)
		yyerror("Unable to open include file %s", cpy);
	push_buffer(YY_CURRENT_BUFFER, NULL, cpy);
	yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
}


struct keyword {
	char	*keyword;
	int	token;
	int	isextension;
	int	needcase;
	int	alwayskeyword;
};

static struct keyword keywords[] = {
	{ "ACCELERATORS", ACCELERATORS, 0, 0, 0},
	{ "ALT", ALT, 0, 0, 0},
	{ "ASCII", ASCII, 0, 0, 0},
	{ "AUTO3STATE", AUTO3STATE, 1, 0, 0},
	{ "AUTOCHECKBOX", AUTOCHECKBOX, 1, 0, 0},
	{ "AUTORADIOBUTTON", AUTORADIOBUTTON, 1, 0, 0},
	{ "BEGIN", tBEGIN, 0, 0, 1},
	{ "BITMAP", tBITMAP, 0, 0, 0},
	{ "BLOCK", BLOCK, 0, 0, 1},
	{ "BUTTON", BUTTON, 1, 0, 0},
	{ "CAPTION", CAPTION, 0, 0, 0},
	{ "CHARACTERISTICS", CHARACTERISTICS, 1, 0, 0},
	{ "CHECKBOX", CHECKBOX, 0, 0, 0},
	{ "CHECKED", CHECKED, 0, 0, 0},
	{ "CLASS", CLASS, 0, 0, 0},
	{ "COMBOBOX", COMBOBOX, 0, 0, 0},
	{ "CONTROL", CONTROL, 0, 0, 0},
	{ "CTEXT", CTEXT, 0, 0, 0},
	{ "CURSOR", CURSOR, 0, 0, 0},
	{ "defined", tDEFINED, 0, 1, 1},
	{ "DEFPUSHBUTTON", DEFPUSHBUTTON, 0, 0, 1},
	{ "DIALOG", DIALOG, 0, 0, 0},
	{ "DIALOGEX", DIALOGEX, 1, 0, 0},
	{ "DISCARDABLE", DISCARDABLE, 0, 0, 0},
	{ "DLGINIT", DLGINIT, 0, 0, 0},
	{ "EDITTEXT", EDITTEXT, 0, 0, 0},
	{ "END", tEND, 0, 0, 1},
	{ "EXSTYLE", EXSTYLE, 0, 0, 0},
	{ "extern", tEXTERN, 0, 1, 1},
	{ "FILEFLAGS", FILEFLAGS, 0, 0, 0},
	{ "FILEFLAGSMASK", FILEFLAGSMASK, 0, 0, 0},
	{ "FILEOS", FILEOS, 0, 0, 0},
	{ "FILESUBTYPE", FILESUBTYPE, 0, 0, 0},
	{ "FILETYPE", FILETYPE, 0, 0, 0},
	{ "FILEVERSION", FILEVERSION, 0, 0, 0},
	{ "FIXED", tFIXED, 0, 0, 0},
	{ "FONT", FONT, 0, 0, 0},
	{ "GRAYED", GRAYED, 0, 0, 0},
	{ "GROUPBOX", GROUPBOX, 0, 0, 0},
	{ "HELP", HELP, 0, 0, 0},
	{ "ICON", ICON, 0, 0, 0},
	{ "IMPURE", IMPURE, 0, 0, 0},
	{ "INACTIVE", INACTIVE, 0, 0, 0},
	{ "LANGUAGE", LANGUAGE, 1, 0, 1},
	{ "LISTBOX", LISTBOX, 0, 0, 0},
	{ "LOADONCALL", LOADONCALL, 0, 0, 0},
	{ "LTEXT", LTEXT, 0, 0, 0},
	{ "MENU", MENU, 0, 0, 0},
	{ "MENUBARBREAK", MENUBARBREAK, 0, 0, 0},
	{ "MENUBREAK", MENUBREAK, 0, 0, 0},
	{ "MENUEX", MENUEX, 1, 0, 0},
	{ "MENUITEM", MENUITEM, 0, 0, 0},
	{ "MESSAGETABLE", MESSAGETABLE, 1, 0, 0},
	{ "MOVEABLE", MOVEABLE, 0, 0, 0},
	{ "NOINVERT", NOINVERT, 0, 0, 0},
	{ "NOT", NOT, 0, 0, 0},
	{ "POPUP", POPUP, 0, 0, 0},
	{ "PRELOAD", PRELOAD, 0, 0, 0},
	{ "PRODUCTVERSION", PRODUCTVERSION, 0, 0, 0},
	{ "PURE", tPURE, 0, 0, 0},
	{ "PUSHBUTTON", PUSHBUTTON, 0, 0, 0},
	{ "RADIOBUTTON", RADIOBUTTON, 0, 0, 0},
	{ "RCDATA", RCDATA, 0, 0, 0},
	{ "RTEXT", RTEXT, 0, 0, 0},
	{ "SCROLLBAR", SCROLLBAR, 0, 0, 0},
	{ "SEPARATOR", SEPARATOR, 0, 0, 0},
	{ "SHIFT", SHIFT, 0, 0, 0},
	{ "STATE3", STATE3, 1, 0, 0},
	{ "STRING", tSTRING, 0, 0, 0},
	{ "STRINGTABLE", STRINGTABLE, 0, 0, 1},
	{ "STYLE", STYLE, 0, 0, 0},
	{ "TOOLBAR", TOOLBAR, 1, 0, 0},
	{ "typedef", tTYPEDEF, 0, 1, 1},
	{ "VALUE", VALUE, 0, 0, 0},
	{ "VERSION", VERSION, 1, 0, 0},
	{ "VERSIONINFO", VERSIONINFO, 0, 0, 0},
	{ "VIRTKEY", VIRTKEY, 0, 0, 0}
};

#define NKEYWORDS	(sizeof(keywords)/sizeof(keywords[0]))
#define KWP(p)		((struct keyword *)(p))
int kw_cmp_func(const void *s1, const void *s2)
{
	int ret;
	ret = strcasecmp(KWP(s1)->keyword, KWP(s2)->keyword);
	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
struct keyword *iskeyword(char *kw)
{
	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

#ifdef LEX_DEBUG
	if(kwp && !strcmp(kwp->keyword, "LANGUAGE"))
		printf("Got Language\n");
#endif
	if(kwp == NULL || (kwp->isextension && !extensions))
		return NULL;
	else
		return kwp;
}

void add_to_substtext(char *text, int len)
{
	if(!substtext)
	{
		substtext = xstrdup(text);
	}
	else
	{
		substtext = (char *)xrealloc(substtext, strlen(substtext)+len+1);
		strcat(substtext, text);
	}
}

%}

%%
	/* #include handling */
^{ws}*#{ws}*include{ws}*	push_to(pp_incl);
<pp_incl>\<[^\n\>]+\>		do_include(yytext, yyleng); pop_start();
<pp_incl>\"[^\n\>]+\"		do_include(yytext, yyleng); pop_start();
<pp_incl>.		yyerror("Malformed #include");

	/* #define handling */
^{ws}*#{ws}*define{ws}*	push_to(pp_def);
<pp_def>{cident}	{
				set_define(yytext);
				push_to(pp_def_s);
			}
<pp_def>{cident}\(	push_to(pp_ignore); /* Ignore function-like defines for now*/
<pp_def>.		yyerror("Malformed #define");

<pp_ignore,pp_def_s>[^\/\\\n]*	{
			if(YY_START == pp_def_s)
				add_to_substtext(yytext, yyleng);
		}
<pp_ignore,pp_def_s>\/[^\/\*][^\/\\\n]*	{ /* Comment is handled in normal handling */
			if(YY_START == pp_def_s)
				add_to_substtext(yytext, yyleng);
		}
<pp_ignore,pp_def_s>\\{ws}*\n	line_number++;	char_number = 1; /* Line continuation */
<pp_ignore,pp_def_s>\n		{
			if(YY_START == pp_def_s)
			{
				add_define(substtext ? substtext : "");
				free(substtext);
				substtext = NULL;
			}
			line_number++;
			char_number = 1;
			pop_start();
			pop_start();
		}

	/* #undef handling */
^{ws}*#{ws}*undef{ws}*	push_to(pp_undef);
<pp_undef>{cident}	{
				del_define(yytext);
				pop_start();
				/*push_to(pp_ignore);*/
			}

	/* Conditional handling */
<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*if{ws}*	{
			if(YY_START == pp_false)
			{
				if(yydebug)
					printf("(%d)#if ignored\n", line_number);
				push_if(0, 0, 1);
				push_to(pp_ignore_eol);
			}
			else
			{
				push_to(INITIAL);
				want_nl = 1;
				return tIF;
			}
		}
<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*ifdef{ws}*	{
			if(YY_START == pp_false)
			{
				if(yydebug)
					printf("(%d)#ifdef ignored\n", line_number);
				push_if(0, 0, 1);
				push_to(pp_ignore_eol);
			}
			else
			{
				push_to(INITIAL);
				want_nl = 1;
				want_ident = 1;
				return tIFDEF;
			}
		}
<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*ifndef{ws}*	{
			if(YY_START == pp_false)
			{
				if(yydebug)
					printf("(%d)#ifndef ignored\n", line_number);
				push_if(0, 0, 1);
				push_to(pp_ignore_eol);
			}
			else
			{
				push_to(INITIAL);
				want_nl = 1;
				want_ident = 1;
				return tIFNDEF;
			}
		}
<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*elif{ws}*	{
			if(!isnevertrue_if())
			{
				push_to(INITIAL);
				want_nl = 1;
				return tELIF;
			}
			else if(YY_START == pp_false)
				push_to(pp_ignore_eol);
			if(yydebug)
				printf("(%d)#elif ignored\n", line_number);
		}
<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*else{ws}*	{
			if(!isnevertrue_if())
			{
				push_to(INITIAL);
				want_nl = 1;
				return tELSE;
			}
			if(yydebug)
				printf("(%d)#else ignored\n", line_number);
		}
<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*endif{ws}*	{
			if(!isnevertrue_if())
			{
				want_nl = 1;
				return tENDIF;
			}
			else
			{
				if(yydebug)
					printf("(%d)#endif ignored\n", line_number);
				pop_if();
			}
		}

	/* The error directive */
^{ws}*#{ws}*error{ws}*	push_to(pp_error);
<pp_error>[^\n]*	yyerror("Error directive: %s", yytext);

	/* preprocessor junk */
^{ws}*#{ws}*pragma[^\n]*	;	/* Ignore #pragma */
^{ws}*#{ws}*line[^\n]*		;	/* Ignore #line */
 /* We'll get an error on malformed #xxx statements
  * by not recognising '#' at all. This helps tracking
  * preprocessor errors.
  */
 /*^{ws}*#{ws}*			;	 Ignore # */

<pp_strips>\{		stripslevel++;
<pp_strips>\}		stripslevel--;
<pp_strips>;		if(!stripslevel) pop_start();
<pp_strips>\/[^*\n]		; /* To catch comments */
<pp_strips>[^\{\};\n#/]*	; /* Ignore rest */

<pp_stripp>\(		stripplevel++;
<pp_stripp>\)		{
				stripplevel--;
				if(!stripplevel)
				{
					pop_start();
					push_to(pp_stripp_final);
				}
			}
<pp_stripp>\/[^*\n]		; /* To catch comments */
<pp_stripp>[^\(\);\n#/]*	; /* Ignore rest */

<pp_stripp_final>{ws}*	; /* Ignore */
<pp_stripp_final>;	pop_start(); /* Kill the semicolon */
<pp_stripp_final>\n	line_number++; char_number = 1; pop_start();
<pp_stripp_final>.	yyless(0); pop_start();

<pp_false>.		;	/* Ignore everything except #xxx during false #if state */

<pp_ignore_eol>[^\n]*	pop_start();

 /* These are special cases due to filename scanning */
<yywf>[Dd][Ii][Ss][Cc][Aa][Rr][Dd][Aa][Bb][Ll][Ee]	return DISCARDABLE;
<yywf>[Ff][Ii][Xx][Ee][Dd]				return tFIXED;
<yywf>[Ii][Mm][Pp][Uu][Rr][Ee]				return IMPURE;
<yywf>[Mm][Oo][Vv][Ee][Aa][Bb][Ll][Ee]			return MOVEABLE;
<yywf>[Ll][Oo][Aa][Dd][Oo][Nn][Cc][Aa][Ll][Ll]		return LOADONCALL;
<yywf>[Pp][Rr][Ee][Ll][Oo][Aa][Dd]			return PRELOAD;
<yywf>[Pp][Uu][Rr][Ee]					return tPURE;

\{			return tBEGIN;
\}			return tEND;

[0-9]+[lL]?		{ yylval.num = strtoul(yytext,  0, 10); return toupper(yytext[yyleng-1]) == 'L' ? LNUMBER : NUMBER; }
0[xX][0-9A-Fa-f]+[lL]?	{ yylval.num = strtoul(yytext,  0, 16); return toupper(yytext[yyleng-1]) == 'L' ? LNUMBER : NUMBER; }
0[oO][0-7]+[lL]?	{ yylval.num = strtoul(yytext+2, 0, 8); return toupper(yytext[yyleng-1]) == 'L' ? LNUMBER : NUMBER; }
[A-Za-z_0-9]+		{
				struct keyword *token;
				struct pp_entry *ppp;

				want_rscname = 0;
				
				if(want_ident)
				{
					/* Prevent preprocessor subst */
					want_ident = 0;
					yylval.str = make_string(yytext);
				#ifdef LEX_DEBUG
					printf("want IDENT (%s, %d, %d): <%s>\n", input_name, line_number, char_number, yytext);
				#endif
					return IDENT;
				}
				else if((ppp = pp_lookup(yytext)) != NULL)
				{
					/* Do preprocessor substitution,
					 * but expand only if macro is not
					 * already expanding.
					 */
					if(!ppp->expanding)
					{
				#ifdef LEX_DEBUG
						printf("expand IDENT (%s, %d, %d): <%s>\n", input_name, line_number, char_number, yytext);
				#endif
						push_buffer(YY_CURRENT_BUFFER, ppp, NULL);
						yy_scan_string(ppp->subst);
					}
				}
				else if((token = iskeyword(yytext)) != NULL
				  && !(!token->alwayskeyword && want_rscname))
				{
					switch(token->token)
					{
					case tDEFINED:
						want_ident = 1;
						break;
					/*case RCDATA:*/
					case CURSOR:
					case tBITMAP:
					case MESSAGETABLE:
					case DLGINIT:
						push_to(yywf);
						break;
					case FONT:
					case ICON:
						if(!indialog)
							push_to(yywf);
						break;
					case DIALOG:
					case DIALOGEX:
						indialog = 1;
						break;
					}
					return token->token;
				}
				else
				{
					yylval.str = make_string(yytext);
				#ifdef LEX_DEBUG
					printf("%s IDENT (%s, %d, %d): <%s>\n",
						want_rscname ? "rscname" : "just",
						input_name,
						line_number,
						char_number,
						yytext);
				#endif
					return IDENT;
				}
			}
\|\|			return LOGOR;
\&\&			return LOGAND;
\=\=			return EQ;
\!\=			return NE;
\<\=			return LTE;
\>\=			return GTE;

<yywf>[^ \f\t\r\n\"]*	{ pop_start(); yylval.str = make_filename(yytext, yyleng); return FILENAME; }
<yywf>\"		push_to(yywf_s);
<yywf_s>[^\"\n]*\"	{ pop_start(); pop_start(); yylval.str = make_filename(yytext, yyleng-1); return FILENAME; }
<yywf_s>\n		yyerror("Newline in filename");

L\"			{
				push_to(yylstr);
				wbufidx = 0;
				if(!win32)
					yywarning("16bit resource contains unicode strings\n");
			}
<yylstr>\"		{
				pop_start();
				yylval.str = get_buffered_wstring();
				return tSTRING;
			}
<yylstr>\n		yyerror("Unterminated string");
<yylstr>\\[0-7]{1,6}	{ /* octal escape sequence */
				int result;
				result = strtol(yytext+1, 0, 8);
				if ( result > 0xffff )
					yyerror("Character constant out of range");
				addwchar((short)result);
			}
<yylstr>\\x[0-9a-fA-F]{4} {  /* hex escape sequence */
				int result;
				result = strtol(yytext+2, 0, 16);
				addwchar((short)result);
			}
<yylstr>\\[0-9]+	yyerror("Bad escape secuence");
<yylstr>\\a		addwchar('\a');
<yylstr>\\b		addwchar('\b');
<yylstr>\\f		addwchar('\f');
<yylstr>\\n		addwchar('\n');
<yylstr>\\r		addwchar('\r');
<yylstr>\\t		addwchar('\t');
<yylstr>\\v		addwchar('\v');
<yylstr>\\(.|\n)	addwchar(yytext[1]);
<yylstr>\"\"		addcchar('\"');		/* "bla""bla"  -> "bla\"bla" */
<yylstr>\\\"\"		addcchar('\"');		/* "bla\""bla" -> "bla\"bla" */
<yylstr>\"{ws}+\"	;			/* "bla" "bla" -> "blabla" */
<yylstr>[^\\\n\"]+	{
				char *yptr = yytext;
				while(*yptr)	/* FIXME: codepage translation */
					addwchar(*yptr++ & 0xff);
			}

\"			{
				push_to(yystr);
				cbufidx = 0;
			}
<yystr>\"		{
				pop_start();
				yylval.str = get_buffered_cstring();
				return tSTRING;
			}
<yystr>\n		yyerror("Unterminated string");
<yystr>\\[0-7]{1,3}	{ /* octal escape sequence */
				int result;
				result = strtol(yytext+1, 0, 8);
				if ( result > 0xff )
					yyerror("Character constant out of range");
				addcchar((char)result);
			}
<yystr>\\x[0-9a-fA-F]{2} {  /* hex escape sequence */
				int result;
				result = strtol(yytext+2, 0, 16);
				addcchar((char)result);
			}
<yystr>\\[0-9]+		yyerror("Bad escape secuence");
<yystr>\\a		addcchar('\a');
<yystr>\\b		addcchar('\b');
<yystr>\\f		addcchar('\f');
<yystr>\\n		addcchar('\n');
<yystr>\\r		addcchar('\r');
<yystr>\\t		addcchar('\t');
<yystr>\\v		addcchar('\v');
<yystr>\\(.|\n)		addcchar(yytext[1]);
<yystr>[^\\\n\"]+	{
				char *yptr = yytext;
				while(*yptr)
					addcchar(*yptr++);
			}
<yystr>\"\"		addcchar('\"');		/* "bla""bla"   -> "bla\"bla" */
<yystr>\\\"\"		addcchar('\"');		/* "bla\""bla"  -> "bla\"bla" */
<yystr>\"{ws}+\"	;			/* "bla" "bla"  -> "blabla" */
<yystr>.		yywarning("Matched %c");


\'			{
				push_to(yyrcd);
				cbufidx = 0;
			}
<yyrcd>\'		{
				pop_start();
				yylval.raw = new_raw_data();
				yylval.raw->size = cbufidx;
				yylval.raw->data = xmalloc(yylval.raw->size);
				memcpy(yylval.raw->data, cbuffer, yylval.raw->size);
				return RAWDATA;
			}
<yyrcd>[0-9a-fA-F]{2}	{
				int result;
				result = strtol(yytext, 0, 16);
				addcchar((char)result);
			}
<yyrcd>{ws}+		;	/* Ignore space */
<yyrcd>\n		line_number++; char_number = 1;
<yyrcd>.		yyerror("Malformed data-line");

<INITIAL,pp_ignore,pp_def_s,pp_strips,pp_stripp>"/*"	push_to(comment);	/* Eat comment */
<comment>[^*\n]*	;
<comment>"*"+[^*/\n]*	;
<comment>\n		line_number++; char_number = 1;
<comment>"*"+"/"	pop_start();

;[^\n]*			; /* Eat comment */
<INITIAL,pp_ignore,pp_def_s>"//"[^\n]*		; /* Eat comment */

<INITIAL,yywf,pp_false,pp_strips,pp_stripp>\n	{
				if(YY_START == yywf)
					pop_start();
				line_number++;
				char_number = 1;
				if(want_nl)
				{
					want_nl = 0;
					return tNL;
				}
			}
<INITIAL,yywf>{ws}+	;	/* Eat whitespace */

<INITIAL>.		return yytext[0];
<<EOF>>			{
				YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
				if(!pop_buffer())
                                {
					if(YY_START == pp_strips || YY_START == pp_stripp || YY_START == pp_stripp_final)
						yyerror("Unexpected end of file during c-junk scanning (started at %d)", cjunk_tagline);
					else
						yyterminate();
                                }
				yy_delete_buffer(b);
			}

<*>.|\n			{
				/* Catch all rule to find any unmatched text */
				if(*yytext == '\n')
				{
					line_number++;
					char_number = 1;
				}
				yywarning("Unmatched text '%c' (0x%02x) YY_START=%d",
					isprint(*yytext) ? *yytext : '.', *yytext, YY_START);
			}

%%

#ifndef yywrap
int yywrap(void)
{
#if 0
	if(bufferstackidx > 0)
	{
		return 0;
 	}
#endif
	return 1;
}
#endif

/* These dup functions copy the enclosed '\0' from
 * the resource string.
 */
void addcchar(char c)
{
	if(cbufidx >= cbufalloc)
	{
		cbufalloc += 1024;
		cbuffer = xrealloc(cbuffer, cbufalloc * sizeof(cbuffer[0]));
		if(cbufalloc > 65536)
			yywarning("Reallocating string buffer larger than 64kB");
	}
	cbuffer[cbufidx++] = c;
}

void addwchar(short s)
{
	if(wbufidx >= wbufalloc)
	{
		wbufalloc += 1024;
		wbuffer = xrealloc(wbuffer, wbufalloc * sizeof(wbuffer[0]));
		if(wbufalloc > 65536)
			yywarning("Reallocating wide string buffer larger than 64kB");
	}

	/*
	 * BS 08-Aug-1999 FIXME: The '& 0xff' is probably a bug, but I have
	 * not experienced it yet and I seem to remember that this was for
	 * a reason. But, as so many things you tend to forget why.
	 * I guess that there were problems due to the sign extension of
	 * shorts WRT chars (e.g. 0x80 becomes 0xff80 instead of 0x0080).
	 * This should then be fixed in the lexer calling the function.
	 */
	wbuffer[wbufidx++] = (short)(s & 0xff);
}

string_t *get_buffered_cstring(void)
{
	string_t *str = new_string();
	str->size = cbufidx;
	str->type = str_char;
	str->str.cstr = (char *)xmalloc(cbufidx+1);
	memcpy(str->str.cstr, cbuffer, cbufidx);
	str->str.cstr[cbufidx] = '\0';
/*	printf("got cstring \"%s\"\n", str->str.cstr); */
	return str;
}

string_t *get_buffered_wstring(void)
{
	string_t *str = new_string();
	str->size = wbufidx;
	str->type = str_unicode;
	str->str.wstr = (short *)xmalloc(2*(wbufidx+1));
	memcpy(str->str.wstr, wbuffer, wbufidx);
	str->str.wstr[wbufidx] = 0;
	return str;
}

string_t *make_string(char *s)
{
	string_t *str = new_string();
	str->size = strlen(s);
	str->type = str_char;
	str->str.cstr = (char *)xmalloc(str->size+1);
	memcpy(str->str.cstr, s, str->size+1);
	return str;
}

string_t *make_filename(char *s, int len)
{
	char *cptr;
	string_t *str = new_string();

	str->size = len;
	str->type = str_char;
	str->str.cstr = (char *)xmalloc(str->size+1);
	memcpy(str->str.cstr, s, str->size);
	str->str.cstr[str->size] = '\0';

	/* Remove escaped backslash and convert to forward */
	cptr = str->str.cstr;
	for(cptr = str->str.cstr; (cptr = strchr(cptr, '\\')) != NULL; cptr++)
	{
		if(cptr[1] == '\\')
		{
			memmove(cptr, cptr+1, strlen(cptr));
			str->size--;
		}
		*cptr = '/';
	}

	/* Convert to lower case. Seems to be reasonable to do */
	for(cptr = str->str.cstr; !leave_case && *cptr; cptr++)
	{
		*cptr = tolower(*cptr);
	}
	return str;
}

/* Called from the parser to signal filename request */
void set_yywf(void)
{
	push_to(yywf);
}

/* Called from the parser to signal preprocessor if case */
void set_pp_ignore(int state)
{
	if(state)
		push_to(pp_false);
	else
		pop_start();
}

/* Called from the parser to kill c-junk */
void strip_til_semicolon(void)
{
	cjunk_tagline = line_number;
	push_to(pp_strips);
}

void strip_til_parenthesis(void)
{
	cjunk_tagline = line_number;
	stripplevel = 1;	/* One scanned already */
	push_to(pp_stripp);
}


