| %{ | 
 | /* | 
 |  * Copyright 1994	Martin von Loewis | 
 |  * Copyright 1998-2000	Bertho A. Stultiens (BS) | 
 |  *           1999	Juergen Schmied (JS) | 
 |  * | 
 |  * 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 | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
 |  * | 
 |  * History: | 
 |  * 24-Jul-2000 BS	- Made a fix for broken Berkeley yacc on | 
 |  *			  non-terminals (see cjunk rule). | 
 |  * 21-May-2000 BS	- Partial implementation of font resources. | 
 |  *			- Corrected language propagation for binary | 
 |  *			  resources such as bitmaps, icons, cursors, | 
 |  *			  userres and rcdata. The language is now | 
 |  *			  correct in .res files. | 
 |  *			- Fixed reading the resource name as ident, | 
 |  *			  so that it may overlap keywords. | 
 |  * 20-May-2000 BS	- Implemented animated cursors and icons | 
 |  *			  resource types. | 
 |  * 30-Apr-2000 BS	- Reintegration into the wine-tree | 
 |  * 14-Jan-2000 BS	- Redid the usertype resources so that they | 
 |  *			  are compatible. | 
 |  * 02-Jan-2000 BS	- Removed the preprocessor from the grammar | 
 |  *			  except for the # command (line numbers). | 
 |  * | 
 |  * 06-Nov-1999 JS	- see CHANGES | 
 |  * | 
 |  * 29-Dec-1998 AdH	- Grammar and function extensions. | 
 |  *			     grammar: TOOLBAR resources, Named ICONs in | 
 |  *				DIALOGS | 
 |  *			     functions: semantic actions for the grammar | 
 |  *				changes, resource files can now be anywhere | 
 |  *				on the include path instead of just in the | 
 |  *				current directory | 
 |  * | 
 |  * 20-Jun-1998 BS	- Fixed a bug in load_file() where the name was not | 
 |  *			  printed out correctly. | 
 |  * | 
 |  * 17-Jun-1998 BS	- Fixed a bug in CLASS statement parsing which should | 
 |  *			  also accept a tSTRING as argument. | 
 |  * | 
 |  * 25-May-1998 BS	- Found out that I need to support language, version | 
 |  *			  and characteristics in inline resources (bitmap, | 
 |  *			  cursor, etc) but they can also be specified with | 
 |  *			  a filename. This renders my filename-scanning scheme | 
 |  *			  worthless. Need to build newline parsing to solve | 
 |  *			  this one. | 
 |  *			  It will come with version 1.1.0 (sigh). | 
 |  * | 
 |  * 19-May-1998 BS	- Started to build a builtin preprocessor | 
 |  * | 
 |  * 30-Apr-1998 BS	- Redid the stringtable parsing/handling. My previous | 
 |  *			  ideas had some serious flaws. | 
 |  * | 
 |  * 27-Apr-1998 BS	- Removed a lot of dead comments and put it in a doc | 
 |  *			  file. | 
 |  * | 
 |  * 21-Apr-1998 BS	- Added correct behavior for cursors and icons. | 
 |  *			- This file is growing too big. It is time to strip | 
 |  *			  things and put it in a support file. | 
 |  * | 
 |  * 19-Apr-1998 BS	- Tagged the stringtable resource so that only one | 
 |  *			  resource will be created. This because the table | 
 |  *			  has a different layout than other resources. The | 
 |  *			  table has to be sorted, and divided into smaller | 
 |  *			  resource entries (see comment in source). | 
 |  * | 
 |  * 17-Apr-1998 BS	- Almost all strings, including identifiers, are parsed | 
 |  *			  as string_t which include unicode strings upon | 
 |  *			  input. | 
 |  *			- Parser now emits a warning when compiling win32 | 
 |  *			  extensions in win16 mode. | 
 |  * | 
 |  * 16-Apr-1998 BS	- Raw data elements are now *optionally* separated | 
 |  *			  by commas. Read the comments in file sq2dq.l. | 
 |  *			- FIXME: there are instances in the source that rely | 
 |  *			  on the fact that int==32bit and pointers are int size. | 
 |  *			- Fixed the conflict in menuex by changing a rule | 
 |  *			  back into right recursion. See note in source. | 
 |  *			- UserType resources cannot have an expression as its | 
 |  *			  typeclass. See note in source. | 
 |  * | 
 |  * 15-Apr-1998 BS	- Changed all right recursion into left recursion to | 
 |  *			  get reduction of the parsestack. | 
 |  *			  This also helps communication between bison and flex. | 
 |  *			  Main advantage is that the Empty rule gets reduced | 
 |  *			  first, which is used to allocate/link things. | 
 |  *			  It also added a shift/reduce conflict in the menuex | 
 |  *			  handling, due to expression/option possibility, | 
 |  *			  although not serious. | 
 |  * | 
 |  * 14-Apr-1998 BS	- Redone almost the entire parser. We're not talking | 
 |  *			  about making it more efficient, but readable (for me) | 
 |  *			  and slightly easier to expand/change. | 
 |  *			  This is done primarily by using more reduce states | 
 |  *			  with many (intuitive) types for the various resource | 
 |  *			  statements. | 
 |  *			- Added expression handling for all resources where a | 
 |  *			  number is accepted (not only for win32). Also added | 
 |  *			  multiply and division (not MS compatible, but handy). | 
 |  *			  Unary minus introduced a shift/reduce conflict, but | 
 |  *			  it is not serious. | 
 |  * | 
 |  * 13-Apr-1998 BS	- Reordered a lot of things | 
 |  *			- Made the source more readable | 
 |  *			- Added Win32 resource definitions | 
 |  *			- Corrected syntax problems with an old yacc (;) | 
 |  *			- Added extra comment about grammar | 
 |  */ | 
 | #include "config.h" | 
 |  | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <stdarg.h> | 
 | #include <assert.h> | 
 | #include <ctype.h> | 
 | #include <string.h> | 
 | #ifdef HAVE_ALLOCA_H | 
 | #include <alloca.h> | 
 | #endif | 
 |  | 
 | #include "wrc.h" | 
 | #include "utils.h" | 
 | #include "newstruc.h" | 
 | #include "dumpres.h" | 
 | #include "wine/wpp.h" | 
 | #include "wine/unicode.h" | 
 | #include "parser.h" | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "wingdi.h" | 
 | #include "winuser.h" | 
 |  | 
 | #if defined(YYBYACC) | 
 | 	/* Berkeley yacc (byacc) doesn't seem to know about these */ | 
 | 	/* Some *BSD supplied versions do define these though */ | 
 | # ifndef YYEMPTY | 
 | #  define YYEMPTY	(-1)	/* Empty lookahead value of yychar */ | 
 | # endif | 
 | # ifndef YYLEX | 
 | #  define YYLEX		yylex() | 
 | # endif | 
 |  | 
 | #elif defined(YYBISON) | 
 | 	/* Bison was used for original development */ | 
 | 	/* #define YYEMPTY -2 */ | 
 | 	/* #define YYLEX   yylex() */ | 
 |  | 
 | #else | 
 | 	/* No yacc we know yet */ | 
 | # if !defined(YYEMPTY) || !defined(YYLEX) | 
 | #  error Yacc version/type unknown. This version needs to be verified for settings of YYEMPTY and YYLEX. | 
 | # elif defined(__GNUC__)	/* gcc defines the #warning directive */ | 
 | #  warning Yacc version/type unknown. It defines YYEMPTY and YYLEX, but is not tested | 
 |   /* #else we just take a chance that it works... */ | 
 | # endif | 
 | #endif | 
 |  | 
 | int want_nl = 0;	/* Signal flex that we need the next newline */ | 
 | int want_id = 0;	/* Signal flex that we need the next identifier */ | 
 | stringtable_t *tagstt;	/* Stringtable tag. | 
 | 			 * It is set while parsing a stringtable to one of | 
 | 			 * the stringtables in the sttres list or a new one | 
 | 			 * if the language was not parsed before. | 
 | 			 */ | 
 | stringtable_t *sttres;	/* Stringtable resources. This holds the list of | 
 | 			 * stringtables with different lanuages | 
 | 			 */ | 
 | static int dont_want_id = 0;	/* See language parsing for details */ | 
 |  | 
 | /* Set to the current options of the currently scanning stringtable */ | 
 | static int *tagstt_memopt; | 
 | static characts_t *tagstt_characts; | 
 | static version_t *tagstt_version; | 
 |  | 
 | static const char riff[4] = "RIFF";	/* RIFF file magic for animated cursor/icon */ | 
 |  | 
 | /* Prototypes of here defined functions */ | 
 | static event_t *get_event_head(event_t *p); | 
 | static control_t *get_control_head(control_t *p); | 
 | static ver_value_t *get_ver_value_head(ver_value_t *p); | 
 | static ver_block_t *get_ver_block_head(ver_block_t *p); | 
 | static resource_t *get_resource_head(resource_t *p); | 
 | static menuex_item_t *get_itemex_head(menuex_item_t *p); | 
 | static menu_item_t *get_item_head(menu_item_t *p); | 
 | static raw_data_t *merge_raw_data_str(raw_data_t *r1, string_t *str); | 
 | static raw_data_t *merge_raw_data_int(raw_data_t *r1, int i); | 
 | static raw_data_t *merge_raw_data_long(raw_data_t *r1, int i); | 
 | static raw_data_t *merge_raw_data(raw_data_t *r1, raw_data_t *r2); | 
 | static raw_data_t *str2raw_data(string_t *str); | 
 | static raw_data_t *int2raw_data(int i); | 
 | static raw_data_t *long2raw_data(int i); | 
 | static raw_data_t *load_file(string_t *name, language_t *lang); | 
 | static itemex_opt_t *new_itemex_opt(int id, int type, int state, int helpid); | 
 | static event_t *add_string_event(string_t *key, int id, int flags, event_t *prev); | 
 | static event_t *add_event(int key, int id, int flags, event_t *prev); | 
 | static dialogex_t *dialogex_version(version_t *v, dialogex_t *dlg); | 
 | static dialogex_t *dialogex_characteristics(characts_t *c, dialogex_t *dlg); | 
 | static dialogex_t *dialogex_language(language_t *l, dialogex_t *dlg); | 
 | static dialogex_t *dialogex_menu(name_id_t *m, dialogex_t *dlg); | 
 | static dialogex_t *dialogex_class(name_id_t *n, dialogex_t *dlg); | 
 | static dialogex_t *dialogex_font(font_id_t *f, dialogex_t *dlg); | 
 | static dialogex_t *dialogex_caption(string_t *s, dialogex_t *dlg); | 
 | static dialogex_t *dialogex_exstyle(style_t *st, dialogex_t *dlg); | 
 | static dialogex_t *dialogex_style(style_t *st, dialogex_t *dlg); | 
 | static name_id_t *convert_ctlclass(name_id_t *cls); | 
 | static control_t *ins_ctrl(int type, int special_style, control_t *ctrl, control_t *prev); | 
 | static dialog_t *dialog_version(version_t *v, dialog_t *dlg); | 
 | static dialog_t *dialog_characteristics(characts_t *c, dialog_t *dlg); | 
 | static dialog_t *dialog_language(language_t *l, dialog_t *dlg); | 
 | static dialog_t *dialog_menu(name_id_t *m, dialog_t *dlg); | 
 | static dialog_t *dialog_class(name_id_t *n, dialog_t *dlg); | 
 | static dialog_t *dialog_font(font_id_t *f, dialog_t *dlg); | 
 | static dialog_t *dialog_caption(string_t *s, dialog_t *dlg); | 
 | static dialog_t *dialog_exstyle(style_t * st, dialog_t *dlg); | 
 | static dialog_t *dialog_style(style_t * st, dialog_t *dlg); | 
 | static resource_t *build_stt_resources(stringtable_t *stthead); | 
 | static stringtable_t *find_stringtable(lvc_t *lvc); | 
 | static toolbar_item_t *ins_tlbr_button(toolbar_item_t *prev, toolbar_item_t *idrec); | 
 | static toolbar_item_t *get_tlbr_buttons_head(toolbar_item_t *p, int *nitems); | 
 | static string_t *make_filename(string_t *s); | 
 | static resource_t *build_fontdirs(resource_t *tail); | 
 | static resource_t *build_fontdir(resource_t **fnt, int nfnt); | 
 | static int rsrcid_to_token(int lookahead); | 
 |  | 
 | %} | 
 | %union{ | 
 | 	string_t	*str; | 
 | 	int		num; | 
 | 	int		*iptr; | 
 | 	char		*cptr; | 
 | 	resource_t	*res; | 
 | 	accelerator_t	*acc; | 
 | 	bitmap_t	*bmp; | 
 | 	dialog_t	*dlg; | 
 | 	dialogex_t	*dlgex; | 
 | 	font_t		*fnt; | 
 | 	fontdir_t	*fnd; | 
 | 	menu_t		*men; | 
 | 	menuex_t	*menex; | 
 | 	rcdata_t	*rdt; | 
 | 	stringtable_t	*stt; | 
 | 	stt_entry_t	*stte; | 
 | 	user_t		*usr; | 
 | 	messagetable_t	*msg; | 
 | 	versioninfo_t	*veri; | 
 | 	control_t	*ctl; | 
 | 	name_id_t	*nid; | 
 | 	font_id_t	*fntid; | 
 | 	language_t	*lan; | 
 | 	version_t	*ver; | 
 | 	characts_t	*chars; | 
 | 	event_t		*event; | 
 | 	menu_item_t	*menitm; | 
 | 	menuex_item_t	*menexitm; | 
 | 	itemex_opt_t	*exopt; | 
 | 	raw_data_t	*raw; | 
 | 	lvc_t		*lvc; | 
 | 	ver_value_t	*val; | 
 | 	ver_block_t	*blk; | 
 | 	ver_words_t	*verw; | 
 | 	toolbar_t	*tlbar; | 
 | 	toolbar_item_t	*tlbarItems; | 
 | 	dlginit_t       *dginit; | 
 | 	style_pair_t	*styles; | 
 | 	style_t		*style; | 
 | 	ani_any_t	*ani; | 
 | } | 
 |  | 
 | %token tNL | 
 | %token <num> tNUMBER tLNUMBER | 
 | %token <str> tSTRING tIDENT tFILENAME | 
 | %token <raw> tRAWDATA | 
 | %token tACCELERATORS tBITMAP tCURSOR tDIALOG tDIALOGEX tMENU tMENUEX tMESSAGETABLE | 
 | %token tRCDATA tVERSIONINFO tSTRINGTABLE tFONT tFONTDIR tICON | 
 | %token tAUTO3STATE tAUTOCHECKBOX tAUTORADIOBUTTON tCHECKBOX tDEFPUSHBUTTON | 
 | %token tPUSHBUTTON tRADIOBUTTON tSTATE3 /* PUSHBOX */ | 
 | %token tGROUPBOX tCOMBOBOX tLISTBOX tSCROLLBAR | 
 | %token tCONTROL tEDITTEXT | 
 | %token tRTEXT tCTEXT tLTEXT | 
 | %token tBLOCK tVALUE | 
 | %token tSHIFT tALT tASCII tVIRTKEY tGRAYED tCHECKED tINACTIVE tNOINVERT | 
 | %token tPURE tIMPURE tDISCARDABLE tLOADONCALL tPRELOAD tFIXED tMOVEABLE | 
 | %token tCLASS tCAPTION tCHARACTERISTICS tEXSTYLE tSTYLE tVERSION tLANGUAGE | 
 | %token tFILEVERSION tPRODUCTVERSION tFILEFLAGSMASK tFILEOS tFILETYPE tFILEFLAGS tFILESUBTYPE | 
 | %token tMENUBARBREAK tMENUBREAK tMENUITEM tPOPUP tSEPARATOR | 
 | %token tHELP | 
 | %token tSTRING tIDENT tRAWDATA | 
 | %token tTOOLBAR tBUTTON | 
 | %token tBEGIN tEND | 
 | %token tDLGINIT | 
 | %left '|' | 
 | %left '^' | 
 | %left '&' | 
 | %left '+' '-' | 
 | %left '*' '/' | 
 | %right '~' tNOT | 
 | %left pUPM | 
 |  | 
 | %type <res> 	resource_file resource resources resource_definition | 
 | %type <stt>	stringtable strings | 
 | %type <fnt>	font | 
 | %type <fnd>	fontdir | 
 | %type <acc> 	accelerators | 
 | %type <event> 	events | 
 | %type <bmp> 	bitmap | 
 | %type <ani> 	cursor icon | 
 | %type <dlg> 	dialog dlg_attributes | 
 | %type <ctl> 	ctrls gen_ctrl lab_ctrl ctrl_desc iconinfo | 
 | %type <iptr>	helpid | 
 | %type <dlgex> 	dialogex dlgex_attribs | 
 | %type <ctl>	exctrls gen_exctrl lab_exctrl exctrl_desc | 
 | %type <rdt> 	rcdata | 
 | %type <raw>	raw_data raw_elements opt_data file_raw | 
 | %type <veri> 	versioninfo fix_version | 
 | %type <verw>	ver_words | 
 | %type <blk>	ver_blocks ver_block | 
 | %type <val>	ver_values ver_value | 
 | %type <men> 	menu | 
 | %type <menitm>	item_definitions menu_body | 
 | %type <menex>	menuex | 
 | %type <menexitm> itemex_definitions menuex_body | 
 | %type <exopt>	itemex_p_options itemex_options | 
 | %type <msg> 	messagetable | 
 | %type <usr> 	userres | 
 | %type <num> 	item_options | 
 | %type <nid> 	nameid nameid_s ctlclass usertype | 
 | %type <num> 	acc_opt acc accs | 
 | %type <iptr>	loadmemopts lamo lama | 
 | %type <fntid>	opt_font opt_exfont opt_expr | 
 | %type <lvc>	opt_lvc | 
 | %type <lan>	opt_language | 
 | %type <chars>	opt_characts | 
 | %type <ver>	opt_version | 
 | %type <num>	expr xpr | 
 | %type <iptr>	e_expr | 
 | %type <tlbar>	toolbar | 
 | %type <tlbarItems>	toolbar_items | 
 | %type <dginit>  dlginit | 
 | %type <styles>  optional_style_pair | 
 | %type <num>	any_num | 
 | %type <style>   optional_style | 
 | %type <style>   style | 
 | %type <str>	filename | 
 |  | 
 | %% | 
 |  | 
 | resource_file | 
 | 	: resources { | 
 | 		resource_t *rsc; | 
 | 		/* First add stringtables to the resource-list */ | 
 | 		rsc = build_stt_resources(sttres); | 
 | 		/* 'build_stt_resources' returns a head and $1 is a tail */ | 
 | 		if($1) | 
 | 		{ | 
 | 			$1->next = rsc; | 
 | 			if(rsc) | 
 | 				rsc->prev = $1; | 
 | 		} | 
 | 		else | 
 | 			$1 = rsc; | 
 | 		/* Find the tail again */ | 
 | 		while($1 && $1->next) | 
 | 			$1 = $1->next; | 
 | 		/* Now add any fontdirecory */ | 
 | 		rsc = build_fontdirs($1); | 
 | 		/* 'build_fontdir' returns a head and $1 is a tail */ | 
 | 		if($1) | 
 | 		{ | 
 | 			$1->next = rsc; | 
 | 			if(rsc) | 
 | 				rsc->prev = $1; | 
 | 		} | 
 | 		else | 
 | 			$1 = rsc; | 
 | 		/* Final statement before were done */ | 
 | 		resource_top = get_resource_head($1); | 
 | 		} | 
 | 	; | 
 |  | 
 | /* Resources are put into a linked list */ | 
 | resources | 
 | 	: /* Empty */		{ $$ = NULL; want_id = 1; } | 
 | 	| resources resource	{ | 
 | 		if($2) | 
 | 		{ | 
 | 			resource_t *tail = $2; | 
 | 			resource_t *head = $2; | 
 | 			while(tail->next) | 
 | 				tail = tail->next; | 
 | 			while(head->prev) | 
 | 				head = head->prev; | 
 | 			head->prev = $1; | 
 | 			if($1) | 
 | 				$1->next = head; | 
 | 			$$ = tail; | 
 | 			/* Check for duplicate identifiers */ | 
 | 			while($1 && head) | 
 | 			{ | 
 | 				resource_t *rsc = $1; | 
 | 				while(rsc) | 
 | 				{ | 
 | 					if(rsc->type == head->type | 
 | 					&& rsc->lan->id == head->lan->id | 
 | 					&& rsc->lan->sub == head->lan->sub | 
 | 					&& !compare_name_id(rsc->name, head->name)) | 
 | 					{ | 
 | 						yyerror("Duplicate resource name '%s'", get_nameid_str(rsc->name)); | 
 | 					} | 
 | 					rsc = rsc->prev; | 
 | 				} | 
 | 				head = head->next; | 
 | 			} | 
 | 		} | 
 | 		else if($1) | 
 | 		{ | 
 | 			resource_t *tail = $1; | 
 | 			while(tail->next) | 
 | 				tail = tail->next; | 
 | 			$$ = tail; | 
 | 		} | 
 | 		else | 
 | 			$$ = NULL; | 
 |  | 
 | 		if(!dont_want_id)	/* See comments in language parsing below */ | 
 | 			want_id = 1; | 
 | 		dont_want_id = 0; | 
 | 		} | 
 | 	/* | 
 | 	 * The following newline rule will never get reduced because we never | 
 | 	 * get the tNL token, unless we explicitely set the 'want_nl' | 
 | 	 * flag, which we don't. | 
 | 	 * The *ONLY* reason for this to be here is because Berkeley | 
 | 	 * yacc (byacc), at least version 1.9, has a bug. | 
 | 	 * (identified in the generated parser on the second | 
 | 	 *  line with: | 
 | 	 *  static char yysccsid[] = "@(#)yaccpar   1.9 (Berkeley) 02/21/93"; | 
 | 	 * ) | 
 | 	 * This extra rule fixes it. | 
 | 	 * The problem is that the expression handling rule "expr: xpr" | 
 | 	 * is not reduced on non-terminal tokens, defined above in the | 
 | 	 * %token declarations. Token tNL is the only non-terminal that | 
 | 	 * can occur. The error becomes visible in the language parsing | 
 | 	 * rule below, which looks at the look-ahead token and tests it | 
 | 	 * for tNL. However, byacc already generates an error upon reading | 
 | 	 * the token instead of keeping it as a lookahead. The reason | 
 | 	 * lies in the lack of a $default transition in the "expr : xpr . " | 
 | 	 * state (currently state 25). It is probably ommitted because tNL | 
 | 	 * is a non-terminal and the state contains 2 s/r conflicts. The | 
 | 	 * state enumerates all possible transitions instead of using a | 
 | 	 * $default transition. | 
 | 	 * All in all, it is a bug in byacc. (period) | 
 | 	 */ | 
 | 	| resources tNL | 
 | 	; | 
 |  | 
 |  | 
 | /* Parse top level resource definitions etc. */ | 
 | resource | 
 | 	: expr usrcvt resource_definition { | 
 | 		$$ = $3; | 
 | 		if($$) | 
 | 		{ | 
 | 			if($1 > 65535 || $1 < -32768) | 
 | 				yyerror("Resource's ID out of range (%d)", $1); | 
 | 			$$->name = new_name_id(); | 
 | 			$$->name->type = name_ord; | 
 | 			$$->name->name.i_name = $1; | 
 | 			chat("Got %s (%d)", get_typename($3), $$->name->name.i_name); | 
 | 			} | 
 | 			} | 
 | 	| tIDENT usrcvt resource_definition { | 
 | 		$$ = $3; | 
 | 		if($$) | 
 | 		{ | 
 | 			$$->name = new_name_id(); | 
 | 			$$->name->type = name_str; | 
 | 			$$->name->name.s_name = $1; | 
 | 			chat("Got %s (%s)", get_typename($3), $$->name->name.s_name->str.cstr); | 
 | 		} | 
 | 		} | 
 | 	| stringtable { | 
 | 		/* Don't do anything, stringtables are converted to | 
 | 		 * resource_t structures when we are finished parsing and | 
 | 		 * the final rule of the parser is reduced (see above) | 
 | 		 */ | 
 | 		$$ = NULL; | 
 | 		chat("Got STRINGTABLE"); | 
 | 		} | 
 | 	| tLANGUAGE {want_nl = 1; } expr ',' expr { | 
 | 		/* We *NEED* the newline to delimit the expression. | 
 | 		 * Otherwise, we would not be able to set the next | 
 | 		 * want_id anymore because of the token-lookahead. | 
 | 		 * | 
 | 		 * However, we can test the lookahead-token for | 
 | 		 * being "non-expression" type, in which case we | 
 | 		 * continue. Fortunately, tNL is the only token that | 
 | 		 * will break expression parsing and is implicitely | 
 | 		 * void, so we just remove it. This scheme makes it | 
 | 		 * possible to do some (not all) fancy preprocessor | 
 | 		 * stuff. | 
 | 		 * BTW, we also need to make sure that the next | 
 | 		 * reduction of 'resources' above will *not* set | 
 | 		 * want_id because we already have a lookahead that | 
 | 		 * cannot be undone. | 
 | 		 */ | 
 | 		if(yychar != YYEMPTY && yychar != tNL) | 
 | 			dont_want_id = 1; | 
 |  | 
 | 		if(yychar == tNL) | 
 | 			yychar = YYEMPTY;	/* Could use 'yyclearin', but we already need the*/ | 
 | 						/* direct access to yychar in rule 'usrcvt' below. */ | 
 | 		else if(yychar == tIDENT) | 
 | 			yywarning("LANGUAGE statement not delimited with newline; next identifier might be wrong"); | 
 |  | 
 | 		want_nl = 0;	/* We don't want it anymore if we didn't get it */ | 
 |  | 
 | 		if(!win32) | 
 | 			yywarning("LANGUAGE not supported in 16-bit mode"); | 
 | 		if(currentlanguage) | 
 | 			free(currentlanguage); | 
 | 		if (get_language_codepage($3, $5) == -1) | 
 | 			yyerror( "Language %04x is not supported", ($5<<10) + $3); | 
 | 		currentlanguage = new_language($3, $5); | 
 | 		$$ = NULL; | 
 | 		chat("Got LANGUAGE %d,%d (0x%04x)", $3, $5, ($5<<10) + $3); | 
 | 		} | 
 | 	; | 
 |  | 
 | /* | 
 |  * Remapping of numerical resource types | 
 |  * (see also comment of called function below) | 
 |  */ | 
 | usrcvt	: /* Empty */	{ yychar = rsrcid_to_token(yychar); } | 
 | 	; | 
 |  | 
 | /* | 
 |  * Get a valid name/id | 
 |  */ | 
 | nameid	: expr	{ | 
 | 		if($1 > 65535 || $1 < -32768) | 
 | 			yyerror("Resource's ID out of range (%d)", $1); | 
 | 		$$ = new_name_id(); | 
 | 		$$->type = name_ord; | 
 | 		$$->name.i_name = $1; | 
 | 		} | 
 | 	| tIDENT { | 
 | 		$$ = new_name_id(); | 
 | 		$$->type = name_str; | 
 | 		$$->name.s_name = $1; | 
 | 		} | 
 | 	; | 
 |  | 
 | /* | 
 |  * Extra string recognition for CLASS statement in dialogs | 
 |  */ | 
 | nameid_s: nameid	{ $$ = $1; } | 
 | 	| tSTRING	{ | 
 | 		$$ = new_name_id(); | 
 | 		$$->type = name_str; | 
 | 		$$->name.s_name = $1; | 
 | 		} | 
 | 	; | 
 |  | 
 | /* get the value for a single resource*/ | 
 | resource_definition | 
 | 	: accelerators	{ $$ = new_resource(res_acc, $1, $1->memopt, $1->lvc.language); } | 
 | 	| bitmap	{ $$ = new_resource(res_bmp, $1, $1->memopt, $1->data->lvc.language); } | 
 | 	| cursor { | 
 | 		resource_t *rsc; | 
 | 		if($1->type == res_anicur) | 
 | 		{ | 
 | 			$$ = rsc = new_resource(res_anicur, $1->u.ani, $1->u.ani->memopt, $1->u.ani->data->lvc.language); | 
 | 		} | 
 | 		else if($1->type == res_curg) | 
 | 		{ | 
 | 			cursor_t *cur; | 
 | 			$$ = rsc = new_resource(res_curg, $1->u.curg, $1->u.curg->memopt, $1->u.curg->lvc.language); | 
 | 			for(cur = $1->u.curg->cursorlist; cur; cur = cur->next) | 
 | 			{ | 
 | 				rsc->prev = new_resource(res_cur, cur, $1->u.curg->memopt, $1->u.curg->lvc.language); | 
 | 				rsc->prev->next = rsc; | 
 | 				rsc = rsc->prev; | 
 | 				rsc->name = new_name_id(); | 
 | 				rsc->name->type = name_ord; | 
 | 				rsc->name->name.i_name = cur->id; | 
 | 			} | 
 | 		} | 
 | 		else | 
 | 			internal_error(__FILE__, __LINE__, "Invalid top-level type %d in cursor resource", $1->type); | 
 | 		free($1); | 
 | 		} | 
 | 	| dialog	{ $$ = new_resource(res_dlg, $1, $1->memopt, $1->lvc.language); } | 
 | 	| dialogex { | 
 | 		if(win32) | 
 | 			$$ = new_resource(res_dlgex, $1, $1->memopt, $1->lvc.language); | 
 | 		else | 
 | 			$$ = NULL; | 
 | 		} | 
 | 	| dlginit	{ $$ = new_resource(res_dlginit, $1, $1->memopt, $1->data->lvc.language); } | 
 | 	| font		{ $$ = new_resource(res_fnt, $1, $1->memopt, $1->data->lvc.language); } | 
 | 	| fontdir	{ $$ = new_resource(res_fntdir, $1, $1->memopt, $1->data->lvc.language); } | 
 | 	| icon { | 
 | 		resource_t *rsc; | 
 | 		if($1->type == res_aniico) | 
 | 		{ | 
 | 			$$ = rsc = new_resource(res_aniico, $1->u.ani, $1->u.ani->memopt, $1->u.ani->data->lvc.language); | 
 | 		} | 
 | 		else if($1->type == res_icog) | 
 | 		{ | 
 | 			icon_t *ico; | 
 | 			$$ = rsc = new_resource(res_icog, $1->u.icog, $1->u.icog->memopt, $1->u.icog->lvc.language); | 
 | 			for(ico = $1->u.icog->iconlist; ico; ico = ico->next) | 
 | 			{ | 
 | 				rsc->prev = new_resource(res_ico, ico, $1->u.icog->memopt, $1->u.icog->lvc.language); | 
 | 				rsc->prev->next = rsc; | 
 | 				rsc = rsc->prev; | 
 | 				rsc->name = new_name_id(); | 
 | 				rsc->name->type = name_ord; | 
 | 				rsc->name->name.i_name = ico->id; | 
 | 			} | 
 | 		} | 
 | 		else | 
 | 			internal_error(__FILE__, __LINE__, "Invalid top-level type %d in icon resource", $1->type); | 
 | 		free($1); | 
 | 		} | 
 | 	| menu		{ $$ = new_resource(res_men, $1, $1->memopt, $1->lvc.language); } | 
 | 	| menuex { | 
 | 		if(win32) | 
 | 			$$ = new_resource(res_menex, $1, $1->memopt, $1->lvc.language); | 
 | 		else | 
 | 			$$ = NULL; | 
 | 		} | 
 | 	| messagetable	{ $$ = new_resource(res_msg, $1, WRC_MO_MOVEABLE | WRC_MO_DISCARDABLE, $1->data->lvc.language); } | 
 | 	| rcdata	{ $$ = new_resource(res_rdt, $1, $1->memopt, $1->data->lvc.language); } | 
 | 	| toolbar	{ $$ = new_resource(res_toolbar, $1, $1->memopt, $1->lvc.language); } | 
 | 	| userres	{ $$ = new_resource(res_usr, $1, $1->memopt, $1->data->lvc.language); } | 
 | 	| versioninfo	{ $$ = new_resource(res_ver, $1, WRC_MO_MOVEABLE | WRC_MO_DISCARDABLE, $1->lvc.language); } | 
 | 	; | 
 |  | 
 |  | 
 | filename: tFILENAME	{ $$ = make_filename($1); } | 
 | 	| tIDENT	{ $$ = make_filename($1); } | 
 | 	| tSTRING	{ $$ = make_filename($1); } | 
 | 	; | 
 |  | 
 | /* ------------------------------ Bitmap ------------------------------ */ | 
 | bitmap	: tBITMAP loadmemopts file_raw	{ $$ = new_bitmap($3, $2); } | 
 | 	; | 
 |  | 
 | /* ------------------------------ Cursor ------------------------------ */ | 
 | cursor	: tCURSOR loadmemopts file_raw	{ | 
 | 		$$ = new_ani_any(); | 
 | 		if($3->size > 4 && !memcmp($3->data, riff, sizeof(riff))) | 
 | 		{ | 
 | 			$$->type = res_anicur; | 
 | 			$$->u.ani = new_ani_curico(res_anicur, $3, $2); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			$$->type = res_curg; | 
 | 			$$->u.curg = new_cursor_group($3, $2); | 
 | 		} | 
 | 	} | 
 | 	; | 
 |  | 
 | /* ------------------------------ Icon ------------------------------ */ | 
 | icon	: tICON loadmemopts file_raw	{ | 
 | 		$$ = new_ani_any(); | 
 | 		if($3->size > 4 && !memcmp($3->data, riff, sizeof(riff))) | 
 | 		{ | 
 | 			$$->type = res_aniico; | 
 | 			$$->u.ani = new_ani_curico(res_aniico, $3, $2); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			$$->type = res_icog; | 
 | 			$$->u.icog = new_icon_group($3, $2); | 
 | 		} | 
 | 	} | 
 | 	; | 
 |  | 
 | /* ------------------------------ Font ------------------------------ */ | 
 | 	/* | 
 | 	 * The reading of raw_data for fonts is a Borland BRC | 
 | 	 * extension. MS generates an error. However, it is | 
 | 	 * most logical to support this, considering how wine | 
 | 	 * enters things in CVS (ascii). | 
 | 	 */ | 
 | font	: tFONT loadmemopts file_raw	{ $$ = new_font($3, $2); } | 
 | 	; | 
 |  | 
 | 	/* | 
 | 	 * The fontdir is a Borland BRC extension which only | 
 | 	 * reads the data as 'raw_data' from the file. | 
 | 	 * I don't know whether it is interpreted. | 
 | 	 * The fontdir is generated if it was not present and | 
 | 	 * fonts are defined in the source. | 
 | 	 */ | 
 | fontdir	: tFONTDIR loadmemopts file_raw	{ $$ = new_fontdir($3, $2); } | 
 | 	; | 
 |  | 
 | /* ------------------------------ MessageTable ------------------------------ */ | 
 | /* It might be interesting to implement the MS Message compiler here as well | 
 |  * to get everything in one source. Might be a future project. | 
 |  */ | 
 | messagetable | 
 | 	: tMESSAGETABLE loadmemopts file_raw	{ | 
 | 		if(!win32) | 
 | 			yywarning("MESSAGETABLE not supported in 16-bit mode"); | 
 | 		$$ = new_messagetable($3, $2); | 
 | 		} | 
 | 	; | 
 |  | 
 | /* ------------------------------ RCData ------------------------------ */ | 
 | rcdata	: tRCDATA loadmemopts file_raw	{ $$ = new_rcdata($3, $2); } | 
 | 	; | 
 |  | 
 | /* ------------------------------ DLGINIT ------------------------------ */ | 
 | dlginit	: tDLGINIT loadmemopts file_raw	{ $$ = new_dlginit($3, $2); } | 
 | 	; | 
 |  | 
 | /* ------------------------------ UserType ------------------------------ */ | 
 | userres	: usertype loadmemopts file_raw		{ | 
 | 		#ifdef WORDS_BIGENDIAN | 
 | 			if(pedantic && byteorder != WRC_BO_LITTLE) | 
 | 		#else | 
 | 			if(pedantic && byteorder == WRC_BO_BIG) | 
 | 		#endif | 
 | 				yywarning("Byteordering is not little-endian and type cannot be interpreted"); | 
 | 			$$ = new_user($1, $3, $2); | 
 | 		} | 
 | 	; | 
 |  | 
 | usertype: tNUMBER { | 
 | 		$$ = new_name_id(); | 
 | 		$$->type = name_ord; | 
 | 		$$->name.i_name = $1; | 
 | 		} | 
 | 	| tIDENT { | 
 | 		$$ = new_name_id(); | 
 | 		$$->type = name_str; | 
 | 		$$->name.s_name = $1; | 
 | 		} | 
 | 	; | 
 |  | 
 | /* ------------------------------ Accelerator ------------------------------ */ | 
 | accelerators | 
 | 	: tACCELERATORS loadmemopts opt_lvc tBEGIN events tEND { | 
 | 		$$ = new_accelerator(); | 
 | 		if($2) | 
 | 		{ | 
 | 			$$->memopt = *($2); | 
 | 			free($2); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			$$->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE; | 
 | 		} | 
 | 		if(!$5) | 
 | 			yyerror("Accelerator table must have at least one entry"); | 
 | 		$$->events = get_event_head($5); | 
 | 		if($3) | 
 | 		{ | 
 | 			$$->lvc = *($3); | 
 | 			free($3); | 
 | 		} | 
 | 		if(!$$->lvc.language) | 
 | 			$$->lvc.language = dup_language(currentlanguage); | 
 | 		} | 
 | 	; | 
 |  | 
 | events	: /* Empty */ 				{ $$=NULL; } | 
 | 	| events tSTRING ',' expr acc_opt	{ $$=add_string_event($2, $4, $5, $1); } | 
 | 	| events expr ',' expr acc_opt		{ $$=add_event($2, $4, $5, $1); } | 
 | 	; | 
 |  | 
 | /* | 
 |  * The empty rule generates a s/r conflict because of {bi,u}nary expr | 
 |  * on - and +. It cannot be solved in any way because it is the same as | 
 |  * the if/then/else problem (LALR(1) problem). The conflict is moved | 
 |  * away by forcing it to be in the expression handling below. | 
 |  */ | 
 | acc_opt	: /* Empty */	{ $$ = 0; } | 
 | 	| ',' accs	{ $$ = $2; } | 
 | 	; | 
 |  | 
 | accs	: acc		{ $$ = $1; } | 
 | 	| accs ',' acc	{ $$ = $1 | $3; } | 
 | 	; | 
 |  | 
 | acc	: tNOINVERT 	{ $$ = WRC_AF_NOINVERT; } | 
 | 	| tSHIFT	{ $$ = WRC_AF_SHIFT; } | 
 | 	| tCONTROL	{ $$ = WRC_AF_CONTROL; } | 
 | 	| tALT		{ $$ = WRC_AF_ALT; } | 
 | 	| tASCII	{ $$ = WRC_AF_ASCII; } | 
 | 	| tVIRTKEY	{ $$ = WRC_AF_VIRTKEY; } | 
 | 	; | 
 |  | 
 | /* ------------------------------ Dialog ------------------------------ */ | 
 | /* FIXME: Support EXSTYLE in the dialog line itself */ | 
 | dialog	: tDIALOG loadmemopts expr ',' expr ',' expr ',' expr dlg_attributes | 
 | 	  tBEGIN  ctrls tEND { | 
 | 		if($2) | 
 | 		{ | 
 | 			$10->memopt = *($2); | 
 | 			free($2); | 
 | 		} | 
 | 		else | 
 | 			$10->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE | WRC_MO_DISCARDABLE; | 
 | 		$10->x = $3; | 
 | 		$10->y = $5; | 
 | 		$10->width = $7; | 
 | 		$10->height = $9; | 
 | 		$10->controls = get_control_head($12); | 
 | 		$$ = $10; | 
 | 		if(!$$->gotstyle) | 
 | 		{ | 
 | 			$$->style = new_style(0,0); | 
 | 			$$->style->or_mask = WS_POPUP; | 
 | 			$$->gotstyle = TRUE; | 
 | 		} | 
 | 		if($$->title) | 
 | 			$$->style->or_mask |= WS_CAPTION; | 
 | 		if($$->font) | 
 | 			$$->style->or_mask |= DS_SETFONT; | 
 |  | 
 | 		$$->style->or_mask &= ~($$->style->and_mask); | 
 | 		$$->style->and_mask = 0; | 
 |  | 
 | 		if(!$$->lvc.language) | 
 | 			$$->lvc.language = dup_language(currentlanguage); | 
 | 		} | 
 | 	; | 
 |  | 
 | dlg_attributes | 
 | 	: /* Empty */				{ $$=new_dialog(); } | 
 | 	| dlg_attributes tSTYLE style		{ $$=dialog_style($3,$1); } | 
 | 	| dlg_attributes tEXSTYLE style		{ $$=dialog_exstyle($3,$1); } | 
 | 	| dlg_attributes tCAPTION tSTRING	{ $$=dialog_caption($3,$1); } | 
 | 	| dlg_attributes opt_font		{ $$=dialog_font($2,$1); } | 
 | 	| dlg_attributes tCLASS nameid_s	{ $$=dialog_class($3,$1); } | 
 | 	| dlg_attributes tMENU nameid		{ $$=dialog_menu($3,$1); } | 
 | 	| dlg_attributes opt_language		{ $$=dialog_language($2,$1); } | 
 | 	| dlg_attributes opt_characts		{ $$=dialog_characteristics($2,$1); } | 
 | 	| dlg_attributes opt_version		{ $$=dialog_version($2,$1); } | 
 | 	; | 
 |  | 
 | ctrls	: /* Empty */				{ $$ = NULL; } | 
 | 	| ctrls tCONTROL	gen_ctrl	{ $$=ins_ctrl(-1, 0, $3, $1); } | 
 | 	| ctrls tEDITTEXT	ctrl_desc	{ $$=ins_ctrl(CT_EDIT, 0, $3, $1); } | 
 | 	| ctrls tLISTBOX	ctrl_desc	{ $$=ins_ctrl(CT_LISTBOX, 0, $3, $1); } | 
 | 	| ctrls tCOMBOBOX	ctrl_desc	{ $$=ins_ctrl(CT_COMBOBOX, 0, $3, $1); } | 
 | 	| ctrls tSCROLLBAR	ctrl_desc	{ $$=ins_ctrl(CT_SCROLLBAR, 0, $3, $1); } | 
 | 	| ctrls tCHECKBOX	lab_ctrl	{ $$=ins_ctrl(CT_BUTTON, BS_CHECKBOX, $3, $1); } | 
 | 	| ctrls tDEFPUSHBUTTON	lab_ctrl	{ $$=ins_ctrl(CT_BUTTON, BS_DEFPUSHBUTTON, $3, $1); } | 
 | 	| ctrls tGROUPBOX	lab_ctrl	{ $$=ins_ctrl(CT_BUTTON, BS_GROUPBOX, $3, $1);} | 
 | 	| ctrls tPUSHBUTTON	lab_ctrl	{ $$=ins_ctrl(CT_BUTTON, BS_PUSHBUTTON, $3, $1); } | 
 | /*	| ctrls tPUSHBOX	lab_ctrl	{ $$=ins_ctrl(CT_BUTTON, BS_PUSHBOX, $3, $1); } */ | 
 | 	| ctrls tRADIOBUTTON	lab_ctrl	{ $$=ins_ctrl(CT_BUTTON, BS_RADIOBUTTON, $3, $1); } | 
 | 	| ctrls tAUTO3STATE	lab_ctrl	{ $$=ins_ctrl(CT_BUTTON, BS_AUTO3STATE, $3, $1); } | 
 | 	| ctrls tSTATE3		lab_ctrl	{ $$=ins_ctrl(CT_BUTTON, BS_3STATE, $3, $1); } | 
 | 	| ctrls tAUTOCHECKBOX	lab_ctrl	{ $$=ins_ctrl(CT_BUTTON, BS_AUTOCHECKBOX, $3, $1); } | 
 | 	| ctrls tAUTORADIOBUTTON lab_ctrl	{ $$=ins_ctrl(CT_BUTTON, BS_AUTORADIOBUTTON, $3, $1); } | 
 | 	| ctrls tLTEXT		lab_ctrl	{ $$=ins_ctrl(CT_STATIC, SS_LEFT, $3, $1); } | 
 | 	| ctrls tCTEXT		lab_ctrl	{ $$=ins_ctrl(CT_STATIC, SS_CENTER, $3, $1); } | 
 | 	| ctrls tRTEXT		lab_ctrl	{ $$=ins_ctrl(CT_STATIC, SS_RIGHT, $3, $1); } | 
 | 	/* special treatment for icons, as the extent is optional */ | 
 | 	| ctrls tICON nameid_s opt_comma expr ',' expr ',' expr iconinfo { | 
 | 		$10->title = $3; | 
 | 		$10->id = $5; | 
 | 		$10->x = $7; | 
 | 		$10->y = $9; | 
 | 		$$ = ins_ctrl(CT_STATIC, SS_ICON, $10, $1); | 
 | 		} | 
 | 	; | 
 |  | 
 | lab_ctrl | 
 | 	: tSTRING opt_comma expr ',' expr ',' expr ',' expr ',' expr optional_style { | 
 | 		$$=new_control(); | 
 | 		$$->title = new_name_id(); | 
 | 		$$->title->type = name_str; | 
 | 		$$->title->name.s_name = $1; | 
 | 		$$->id = $3; | 
 | 		$$->x = $5; | 
 | 		$$->y = $7; | 
 | 		$$->width = $9; | 
 | 		$$->height = $11; | 
 | 		if($12) | 
 | 		{ | 
 | 			$$->style = $12; | 
 | 			$$->gotstyle = TRUE; | 
 | 		} | 
 | 		} | 
 | 	; | 
 |  | 
 | ctrl_desc | 
 | 	: expr ',' expr ',' expr ',' expr ',' expr optional_style { | 
 | 		$$ = new_control(); | 
 | 		$$->id = $1; | 
 | 		$$->x = $3; | 
 | 		$$->y = $5; | 
 | 		$$->width = $7; | 
 | 		$$->height = $9; | 
 | 		if($10) | 
 | 		{ | 
 | 			$$->style = $10; | 
 | 			$$->gotstyle = TRUE; | 
 | 		} | 
 | 		} | 
 | 	; | 
 |  | 
 | iconinfo: /* Empty */ | 
 | 		{ $$ = new_control(); } | 
 |  | 
 | 	| ',' expr ',' expr { | 
 | 		$$ = new_control(); | 
 | 		$$->width = $2; | 
 | 		$$->height = $4; | 
 | 		} | 
 | 	| ',' expr ',' expr ',' style { | 
 | 		$$ = new_control(); | 
 | 		$$->width = $2; | 
 | 		$$->height = $4; | 
 | 		$$->style = $6; | 
 | 		$$->gotstyle = TRUE; | 
 | 		} | 
 | 	| ',' expr ',' expr ',' style ',' style { | 
 | 		$$ = new_control(); | 
 | 		$$->width = $2; | 
 | 		$$->height = $4; | 
 | 		$$->style = $6; | 
 | 		$$->gotstyle = TRUE; | 
 | 		$$->exstyle = $8; | 
 | 		$$->gotexstyle = TRUE; | 
 | 		} | 
 | 	; | 
 |  | 
 | gen_ctrl: nameid_s opt_comma expr ',' ctlclass ',' style ',' expr ',' expr ',' expr ',' expr ',' style { | 
 | 		$$=new_control(); | 
 | 		$$->title = $1; | 
 | 		$$->id = $3; | 
 | 		$$->ctlclass = convert_ctlclass($5); | 
 | 		$$->style = $7; | 
 | 		$$->gotstyle = TRUE; | 
 | 		$$->x = $9; | 
 | 		$$->y = $11; | 
 | 		$$->width = $13; | 
 | 		$$->height = $15; | 
 | 		$$->exstyle = $17; | 
 | 		$$->gotexstyle = TRUE; | 
 | 		} | 
 | 	| nameid_s opt_comma expr ',' ctlclass ',' style ',' expr ',' expr ',' expr ',' expr { | 
 | 		$$=new_control(); | 
 | 		$$->title = $1; | 
 | 		$$->id = $3; | 
 | 		$$->ctlclass = convert_ctlclass($5); | 
 | 		$$->style = $7; | 
 | 		$$->gotstyle = TRUE; | 
 | 		$$->x = $9; | 
 | 		$$->y = $11; | 
 | 		$$->width = $13; | 
 | 		$$->height = $15; | 
 | 		} | 
 | 	; | 
 |  | 
 | opt_font | 
 | 	: tFONT expr ',' tSTRING	{ $$ = new_font_id($2, $4, 0, 0); } | 
 | 	; | 
 |  | 
 | /* ------------------------------ style flags ------------------------------ */ | 
 | optional_style		/* Abbused once to get optional ExStyle */ | 
 | 	: /* Empty */	{ $$ = NULL; } | 
 | 	| ',' style	{ $$ = $2; } | 
 | 	; | 
 |  | 
 | optional_style_pair | 
 | 	: /* Empty */		{ $$ = NULL; } | 
 | 	| ',' style		{ $$ = new_style_pair($2, 0); } | 
 | 	| ',' style ',' style 	{ $$ = new_style_pair($2, $4); } | 
 | 	; | 
 |  | 
 | style | 
 | 	: style '|' style	{ $$ = new_style($1->or_mask | $3->or_mask, $1->and_mask | $3->and_mask); free($1); free($3);} | 
 | 	| '(' style ')'		{ $$ = $2; } | 
 |         | any_num       	{ $$ = new_style($1, 0); } | 
 |         | tNOT any_num		{ $$ = new_style(0, $2); } | 
 |         ; | 
 |  | 
 | ctlclass | 
 | 	: expr	{ | 
 | 		$$ = new_name_id(); | 
 | 		$$->type = name_ord; | 
 | 		$$->name.i_name = $1; | 
 | 		} | 
 | 	| tSTRING { | 
 | 		$$ = new_name_id(); | 
 | 		$$->type = name_str; | 
 | 		$$->name.s_name = $1; | 
 | 		} | 
 | 	; | 
 |  | 
 | /* ------------------------------ DialogEx ------------------------------ */ | 
 | dialogex: tDIALOGEX loadmemopts expr ',' expr ',' expr ',' expr helpid dlgex_attribs | 
 | 	  tBEGIN  exctrls tEND { | 
 | 		if(!win32) | 
 | 			yywarning("DIALOGEX not supported in 16-bit mode"); | 
 | 		if($2) | 
 | 		{ | 
 | 			$11->memopt = *($2); | 
 | 			free($2); | 
 | 		} | 
 | 		else | 
 | 			$11->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE | WRC_MO_DISCARDABLE; | 
 | 		$11->x = $3; | 
 | 		$11->y = $5; | 
 | 		$11->width = $7; | 
 | 		$11->height = $9; | 
 | 		if($10) | 
 | 		{ | 
 | 			$11->helpid = *($10); | 
 | 			$11->gothelpid = TRUE; | 
 | 			free($10); | 
 | 		} | 
 | 		$11->controls = get_control_head($13); | 
 | 		$$ = $11; | 
 |  | 
 | 		assert($$->style != NULL); | 
 | 		if(!$$->gotstyle) | 
 | 		{ | 
 | 			$$->style->or_mask = WS_POPUP; | 
 | 			$$->gotstyle = TRUE; | 
 | 		} | 
 | 		if($$->title) | 
 | 			$$->style->or_mask |= WS_CAPTION; | 
 | 		if($$->font) | 
 | 			$$->style->or_mask |= DS_SETFONT; | 
 |  | 
 | 		$$->style->or_mask &= ~($$->style->and_mask); | 
 | 		$$->style->and_mask = 0; | 
 |  | 
 | 		if(!$$->lvc.language) | 
 | 			$$->lvc.language = dup_language(currentlanguage); | 
 | 		} | 
 | 	; | 
 |  | 
 | dlgex_attribs | 
 | 	: /* Empty */				{ $$=new_dialogex(); } | 
 | 	| dlgex_attribs tSTYLE style		{ $$=dialogex_style($3,$1); } | 
 | 	| dlgex_attribs tEXSTYLE style		{ $$=dialogex_exstyle($3,$1); } | 
 | 	| dlgex_attribs tCAPTION tSTRING	{ $$=dialogex_caption($3,$1); } | 
 | 	| dlgex_attribs opt_font		{ $$=dialogex_font($2,$1); } | 
 | 	| dlgex_attribs opt_exfont		{ $$=dialogex_font($2,$1); } | 
 | 	| dlgex_attribs tCLASS nameid_s		{ $$=dialogex_class($3,$1); } | 
 | 	| dlgex_attribs tMENU nameid		{ $$=dialogex_menu($3,$1); } | 
 | 	| dlgex_attribs opt_language		{ $$=dialogex_language($2,$1); } | 
 | 	| dlgex_attribs opt_characts		{ $$=dialogex_characteristics($2,$1); } | 
 | 	| dlgex_attribs opt_version		{ $$=dialogex_version($2,$1); } | 
 | 	; | 
 |  | 
 | exctrls	: /* Empty */				{ $$ = NULL; } | 
 | 	| exctrls tCONTROL	gen_exctrl	{ $$=ins_ctrl(-1, 0, $3, $1); } | 
 | 	| exctrls tEDITTEXT	exctrl_desc	{ $$=ins_ctrl(CT_EDIT, 0, $3, $1); } | 
 | 	| exctrls tLISTBOX	exctrl_desc	{ $$=ins_ctrl(CT_LISTBOX, 0, $3, $1); } | 
 | 	| exctrls tCOMBOBOX	exctrl_desc	{ $$=ins_ctrl(CT_COMBOBOX, 0, $3, $1); } | 
 | 	| exctrls tSCROLLBAR	exctrl_desc	{ $$=ins_ctrl(CT_SCROLLBAR, 0, $3, $1); } | 
 | 	| exctrls tCHECKBOX	lab_exctrl	{ $$=ins_ctrl(CT_BUTTON, BS_CHECKBOX, $3, $1); } | 
 | 	| exctrls tDEFPUSHBUTTON lab_exctrl	{ $$=ins_ctrl(CT_BUTTON, BS_DEFPUSHBUTTON, $3, $1); } | 
 | 	| exctrls tGROUPBOX	lab_exctrl	{ $$=ins_ctrl(CT_BUTTON, BS_GROUPBOX, $3, $1);} | 
 | 	| exctrls tPUSHBUTTON	lab_exctrl	{ $$=ins_ctrl(CT_BUTTON, BS_PUSHBUTTON, $3, $1); } | 
 | /*	| exctrls tPUSHBOX	lab_exctrl	{ $$=ins_ctrl(CT_BUTTON, BS_PUSHBOX, $3, $1); } */ | 
 | 	| exctrls tRADIOBUTTON	lab_exctrl	{ $$=ins_ctrl(CT_BUTTON, BS_RADIOBUTTON, $3, $1); } | 
 | 	| exctrls tAUTO3STATE	lab_exctrl	{ $$=ins_ctrl(CT_BUTTON, BS_AUTO3STATE, $3, $1); } | 
 | 	| exctrls tSTATE3	lab_exctrl	{ $$=ins_ctrl(CT_BUTTON, BS_3STATE, $3, $1); } | 
 | 	| exctrls tAUTOCHECKBOX	lab_exctrl	{ $$=ins_ctrl(CT_BUTTON, BS_AUTOCHECKBOX, $3, $1); } | 
 | 	| exctrls tAUTORADIOBUTTON lab_exctrl	{ $$=ins_ctrl(CT_BUTTON, BS_AUTORADIOBUTTON, $3, $1); } | 
 | 	| exctrls tLTEXT	lab_exctrl	{ $$=ins_ctrl(CT_STATIC, SS_LEFT, $3, $1); } | 
 | 	| exctrls tCTEXT	lab_exctrl	{ $$=ins_ctrl(CT_STATIC, SS_CENTER, $3, $1); } | 
 | 	| exctrls tRTEXT	lab_exctrl	{ $$=ins_ctrl(CT_STATIC, SS_RIGHT, $3, $1); } | 
 | 	/* special treatment for icons, as the extent is optional */ | 
 | 	| exctrls tICON nameid_s opt_comma expr ',' expr ',' expr iconinfo { | 
 | 		$10->title = $3; | 
 | 		$10->id = $5; | 
 | 		$10->x = $7; | 
 | 		$10->y = $9; | 
 | 		$$ = ins_ctrl(CT_STATIC, SS_ICON, $10, $1); | 
 | 		} | 
 | 	; | 
 |  | 
 | gen_exctrl | 
 | 	: nameid_s opt_comma expr ',' ctlclass ',' style ',' expr ',' expr ',' expr ',' | 
 | 	  expr ',' style helpid opt_data { | 
 | 		$$=new_control(); | 
 | 		$$->title = $1; | 
 | 		$$->id = $3; | 
 | 		$$->ctlclass = convert_ctlclass($5); | 
 | 		$$->style = $7; | 
 | 		$$->gotstyle = TRUE; | 
 | 		$$->x = $9; | 
 | 		$$->y = $11; | 
 | 		$$->width = $13; | 
 | 		$$->height = $15; | 
 | 		if($17) | 
 | 		{ | 
 | 			$$->exstyle = $17; | 
 | 			$$->gotexstyle = TRUE; | 
 | 		} | 
 | 		if($18) | 
 | 		{ | 
 | 			$$->helpid = *($18); | 
 | 			$$->gothelpid = TRUE; | 
 | 			free($18); | 
 | 		} | 
 | 		$$->extra = $19; | 
 | 		} | 
 | 	| nameid_s opt_comma expr ',' ctlclass ',' style ',' expr ',' expr ',' expr ',' expr opt_data { | 
 | 		$$=new_control(); | 
 | 		$$->title = $1; | 
 | 		$$->id = $3; | 
 | 		$$->style = $7; | 
 | 		$$->gotstyle = TRUE; | 
 | 		$$->ctlclass = convert_ctlclass($5); | 
 | 		$$->x = $9; | 
 | 		$$->y = $11; | 
 | 		$$->width = $13; | 
 | 		$$->height = $15; | 
 | 		$$->extra = $16; | 
 | 		} | 
 | 	; | 
 |  | 
 | lab_exctrl | 
 | 	: tSTRING opt_comma expr ',' expr ',' expr ',' expr ',' expr optional_style_pair helpid opt_data { | 
 | 		$$=new_control(); | 
 | 		$$->title = new_name_id(); | 
 | 		$$->title->type = name_str; | 
 | 		$$->title->name.s_name = $1; | 
 | 		$$->id = $3; | 
 | 		$$->x = $5; | 
 | 		$$->y = $7; | 
 | 		$$->width = $9; | 
 | 		$$->height = $11; | 
 | 		if($12) | 
 | 		{ | 
 | 			$$->style = $12->style; | 
 | 			$$->gotstyle = TRUE; | 
 |  | 
 | 			if ($12->exstyle) | 
 | 			{ | 
 | 			    $$->exstyle = $12->exstyle; | 
 | 			    $$->gotexstyle = TRUE; | 
 | 			} | 
 | 			free($12); | 
 | 		} | 
 |  | 
 | 		$$->extra = $14; | 
 | 		} | 
 | 	; | 
 |  | 
 | exctrl_desc | 
 | 	: expr ',' expr ',' expr ',' expr ',' expr optional_style_pair helpid opt_data { | 
 | 		$$ = new_control(); | 
 | 		$$->id = $1; | 
 | 		$$->x = $3; | 
 | 		$$->y = $5; | 
 | 		$$->width = $7; | 
 | 		$$->height = $9; | 
 | 		if($10) | 
 | 		{ | 
 | 			$$->style = $10->style; | 
 | 			$$->gotstyle = TRUE; | 
 |  | 
 | 			if ($10->exstyle) | 
 | 			{ | 
 | 			    $$->exstyle = $10->exstyle; | 
 | 			    $$->gotexstyle = TRUE; | 
 | 			} | 
 | 			free($10); | 
 | 		} | 
 | 		$$->extra = $12; | 
 | 		} | 
 | 	; | 
 |  | 
 | opt_data: /* Empty */	{ $$ = NULL; } | 
 | 	| raw_data	{ $$ = $1; } | 
 | 	; | 
 |  | 
 | helpid	: /* Empty */	{ $$ = NULL; } | 
 | 	| ',' expr	{ $$ = new_int($2); } | 
 | 	; | 
 |  | 
 | opt_exfont | 
 | 	: tFONT expr ',' tSTRING ',' expr ',' expr  opt_expr { $$ = new_font_id($2, $4, $6, $8); } | 
 | 	; | 
 |  | 
 | /* | 
 |  * FIXME: This odd expression is here to nullify an extra token found | 
 |  * in some appstudio produced resources which appear to do nothing. | 
 |  */ | 
 | opt_expr: /* Empty */	{ $$ = NULL; } | 
 | 	| ',' expr	{ $$ = NULL; } | 
 | 	; | 
 |  | 
 | /* ------------------------------ Menu ------------------------------ */ | 
 | menu	: tMENU loadmemopts opt_lvc menu_body { | 
 | 		if(!$4) | 
 | 			yyerror("Menu must contain items"); | 
 | 		$$ = new_menu(); | 
 | 		if($2) | 
 | 		{ | 
 | 			$$->memopt = *($2); | 
 | 			free($2); | 
 | 		} | 
 | 		else | 
 | 			$$->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE | WRC_MO_DISCARDABLE; | 
 | 		$$->items = get_item_head($4); | 
 | 		if($3) | 
 | 		{ | 
 | 			$$->lvc = *($3); | 
 | 			free($3); | 
 | 		} | 
 | 		if(!$$->lvc.language) | 
 | 			$$->lvc.language = dup_language(currentlanguage); | 
 | 		} | 
 | 	; | 
 |  | 
 | menu_body | 
 | 	: tBEGIN item_definitions tEND	{ $$ = $2; } | 
 | 	; | 
 |  | 
 | item_definitions | 
 | 	: /* Empty */	{$$ = NULL;} | 
 | 	| item_definitions tMENUITEM tSTRING opt_comma expr item_options { | 
 | 		$$=new_menu_item(); | 
 | 		$$->prev = $1; | 
 | 		if($1) | 
 | 			$1->next = $$; | 
 | 		$$->id =  $5; | 
 | 		$$->state = $6; | 
 | 		$$->name = $3; | 
 | 		} | 
 | 	| item_definitions tMENUITEM tSEPARATOR { | 
 | 		$$=new_menu_item(); | 
 | 		$$->prev = $1; | 
 | 		if($1) | 
 | 			$1->next = $$; | 
 | 		} | 
 | 	| item_definitions tPOPUP tSTRING item_options menu_body { | 
 | 		$$ = new_menu_item(); | 
 | 		$$->prev = $1; | 
 | 		if($1) | 
 | 			$1->next = $$; | 
 | 		$$->popup = get_item_head($5); | 
 | 		$$->name = $3; | 
 | 		} | 
 | 	; | 
 |  | 
 | /* NOTE: item_options is right recursive because it would introduce | 
 |  * a shift/reduce conflict on ',' in itemex_options due to the | 
 |  * empty rule here. The parser is now forced to look beyond the ',' | 
 |  * before reducing (force shift). | 
 |  * Right recursion here is not a problem because we cannot expect | 
 |  * more than 7 parserstack places to be occupied while parsing this | 
 |  * (who would want to specify a MF_x flag twice?). | 
 |  */ | 
 | item_options | 
 | 	: /* Empty */				{ $$ = 0; } | 
 | 	| ',' tCHECKED		item_options	{ $$ = $3 | MF_CHECKED; } | 
 | 	| ',' tGRAYED		item_options	{ $$ = $3 | MF_GRAYED; } | 
 | 	| ',' tHELP		item_options	{ $$ = $3 | MF_HELP; } | 
 | 	| ',' tINACTIVE		item_options	{ $$ = $3 | MF_DISABLED; } | 
 | 	| ',' tMENUBARBREAK	item_options	{ $$ = $3 | MF_MENUBARBREAK; } | 
 | 	| ',' tMENUBREAK	item_options	{ $$ = $3 | MF_MENUBREAK; } | 
 | 	; | 
 |  | 
 | /* ------------------------------ MenuEx ------------------------------ */ | 
 | menuex	: tMENUEX loadmemopts opt_lvc menuex_body	{ | 
 | 		if(!win32) | 
 | 			yywarning("MENUEX not supported in 16-bit mode"); | 
 | 		if(!$4) | 
 | 			yyerror("MenuEx must contain items"); | 
 | 		$$ = new_menuex(); | 
 | 		if($2) | 
 | 		{ | 
 | 			$$->memopt = *($2); | 
 | 			free($2); | 
 | 		} | 
 | 		else | 
 | 			$$->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE | WRC_MO_DISCARDABLE; | 
 | 		$$->items = get_itemex_head($4); | 
 | 		if($3) | 
 | 		{ | 
 | 			$$->lvc = *($3); | 
 | 			free($3); | 
 | 		} | 
 | 		if(!$$->lvc.language) | 
 | 			$$->lvc.language = dup_language(currentlanguage); | 
 | 		} | 
 | 	; | 
 |  | 
 | menuex_body | 
 | 	: tBEGIN itemex_definitions tEND { $$ = $2; } | 
 | 	; | 
 |  | 
 | itemex_definitions | 
 | 	: /* Empty */	{$$ = NULL; } | 
 | 	| itemex_definitions tMENUITEM tSTRING itemex_options { | 
 | 		$$ = new_menuex_item(); | 
 | 		$$->prev = $1; | 
 | 		if($1) | 
 | 			$1->next = $$; | 
 | 		$$->name = $3; | 
 | 		$$->id = $4->id; | 
 | 		$$->type = $4->type; | 
 | 		$$->state = $4->state; | 
 | 		$$->helpid = $4->helpid; | 
 | 		$$->gotid = $4->gotid; | 
 | 		$$->gottype = $4->gottype; | 
 | 		$$->gotstate = $4->gotstate; | 
 | 		$$->gothelpid = $4->gothelpid; | 
 | 		free($4); | 
 | 		} | 
 | 	| itemex_definitions tMENUITEM tSEPARATOR { | 
 | 		$$ = new_menuex_item(); | 
 | 		$$->prev = $1; | 
 | 		if($1) | 
 | 			$1->next = $$; | 
 | 		} | 
 | 	| itemex_definitions tPOPUP tSTRING itemex_p_options menuex_body { | 
 | 		$$ = new_menuex_item(); | 
 | 		$$->prev = $1; | 
 | 		if($1) | 
 | 			$1->next = $$; | 
 | 		$$->popup = get_itemex_head($5); | 
 | 		$$->name = $3; | 
 | 		$$->id = $4->id; | 
 | 		$$->type = $4->type; | 
 | 		$$->state = $4->state; | 
 | 		$$->helpid = $4->helpid; | 
 | 		$$->gotid = $4->gotid; | 
 | 		$$->gottype = $4->gottype; | 
 | 		$$->gotstate = $4->gotstate; | 
 | 		$$->gothelpid = $4->gothelpid; | 
 | 		free($4); | 
 | 		} | 
 | 	; | 
 |  | 
 | itemex_options | 
 | 	: /* Empty */			{ $$ = new_itemex_opt(0, 0, 0, 0); } | 
 | 	| ',' expr { | 
 | 		$$ = new_itemex_opt($2, 0, 0, 0); | 
 | 		$$->gotid = TRUE; | 
 | 		} | 
 | 	| ',' e_expr ',' e_expr item_options { | 
 | 		$$ = new_itemex_opt($2 ? *($2) : 0, $4 ? *($4) : 0, $5, 0); | 
 | 		$$->gotid = TRUE; | 
 | 		$$->gottype = TRUE; | 
 | 		$$->gotstate = TRUE; | 
 | 		if($2) free($2); | 
 | 		if($4) free($4); | 
 | 		} | 
 | 	| ',' e_expr ',' e_expr ',' expr { | 
 | 		$$ = new_itemex_opt($2 ? *($2) : 0, $4 ? *($4) : 0, $6, 0); | 
 | 		$$->gotid = TRUE; | 
 | 		$$->gottype = TRUE; | 
 | 		$$->gotstate = TRUE; | 
 | 		if($2) free($2); | 
 | 		if($4) free($4); | 
 | 		} | 
 | 	; | 
 |  | 
 | itemex_p_options | 
 | 	: /* Empty */			{ $$ = new_itemex_opt(0, 0, 0, 0); } | 
 | 	| ',' expr { | 
 | 		$$ = new_itemex_opt($2, 0, 0, 0); | 
 | 		$$->gotid = TRUE; | 
 | 		} | 
 | 	| ',' e_expr ',' expr { | 
 | 		$$ = new_itemex_opt($2 ? *($2) : 0, $4, 0, 0); | 
 | 		if($2) free($2); | 
 | 		$$->gotid = TRUE; | 
 | 		$$->gottype = TRUE; | 
 | 		} | 
 | 	| ',' e_expr ',' e_expr ',' expr { | 
 | 		$$ = new_itemex_opt($2 ? *($2) : 0, $4 ? *($4) : 0, $6, 0); | 
 | 		if($2) free($2); | 
 | 		if($4) free($4); | 
 | 		$$->gotid = TRUE; | 
 | 		$$->gottype = TRUE; | 
 | 		$$->gotstate = TRUE; | 
 | 		} | 
 | 	| ',' e_expr ',' e_expr ',' e_expr ',' expr { | 
 | 		$$ = new_itemex_opt($2 ? *($2) : 0, $4 ? *($4) : 0, $6 ? *($6) : 0, $8); | 
 | 		if($2) free($2); | 
 | 		if($4) free($4); | 
 | 		if($6) free($6); | 
 | 		$$->gotid = TRUE; | 
 | 		$$->gottype = TRUE; | 
 | 		$$->gotstate = TRUE; | 
 | 		$$->gothelpid = TRUE; | 
 | 		} | 
 | 	; | 
 |  | 
 | /* ------------------------------ StringTable ------------------------------ */ | 
 | /* Stringtables are parsed differently than other resources because their | 
 |  * layout is substantially different from other resources. | 
 |  * The table is parsed through a _global_ variable 'tagstt' which holds the | 
 |  * current stringtable descriptor (stringtable_t *) and 'sttres' that holds a | 
 |  * list of stringtables of different languages. | 
 |  */ | 
 | stringtable | 
 | 	: stt_head tBEGIN strings tEND { | 
 | 		if(!$3) | 
 | 		{ | 
 | 			yyerror("Stringtable must have at least one entry"); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			stringtable_t *stt; | 
 | 			/* Check if we added to a language table or created | 
 | 			 * a new one. | 
 | 			 */ | 
 | 			 for(stt = sttres; stt; stt = stt->next) | 
 | 			 { | 
 | 				if(stt == tagstt) | 
 | 					break; | 
 | 			 } | 
 | 			 if(!stt) | 
 | 			 { | 
 | 				/* It is a new one */ | 
 | 				if(sttres) | 
 | 				{ | 
 | 					sttres->prev = tagstt; | 
 | 					tagstt->next = sttres; | 
 | 					sttres = tagstt; | 
 | 				} | 
 | 				else | 
 | 					sttres = tagstt; | 
 | 			 } | 
 | 			 /* Else were done */ | 
 | 		} | 
 | 		if(tagstt_memopt) | 
 | 		{ | 
 | 			free(tagstt_memopt); | 
 | 			tagstt_memopt = NULL; | 
 | 		} | 
 |  | 
 | 		$$ = tagstt; | 
 | 		} | 
 | 	; | 
 |  | 
 | /* This is to get the language of the currently parsed stringtable */ | 
 | stt_head: tSTRINGTABLE loadmemopts opt_lvc { | 
 | 		if((tagstt = find_stringtable($3)) == NULL) | 
 | 			tagstt = new_stringtable($3); | 
 | 		tagstt_memopt = $2; | 
 | 		tagstt_version = $3->version; | 
 | 		tagstt_characts = $3->characts; | 
 | 		if($3) | 
 | 			free($3); | 
 | 		} | 
 | 	; | 
 |  | 
 | strings	: /* Empty */	{ $$ = NULL; } | 
 | 	| strings expr opt_comma tSTRING { | 
 | 		int i; | 
 | 		assert(tagstt != NULL); | 
 | 		if($2 > 65535 || $2 < -32768) | 
 | 			yyerror("Stringtable entry's ID out of range (%d)", $2); | 
 | 		/* Search for the ID */ | 
 | 		for(i = 0; i < tagstt->nentries; i++) | 
 | 		{ | 
 | 			if(tagstt->entries[i].id == $2) | 
 | 				yyerror("Stringtable ID %d already in use", $2); | 
 | 		} | 
 | 		/* If we get here, then we have a new unique entry */ | 
 | 		tagstt->nentries++; | 
 | 		tagstt->entries = xrealloc(tagstt->entries, sizeof(tagstt->entries[0]) * tagstt->nentries); | 
 | 		tagstt->entries[tagstt->nentries-1].id = $2; | 
 | 		tagstt->entries[tagstt->nentries-1].str = $4; | 
 | 		if(tagstt_memopt) | 
 | 			tagstt->entries[tagstt->nentries-1].memopt = *tagstt_memopt; | 
 | 		else | 
 | 			tagstt->entries[tagstt->nentries-1].memopt = WRC_MO_MOVEABLE | WRC_MO_DISCARDABLE | WRC_MO_PURE; | 
 | 		tagstt->entries[tagstt->nentries-1].version = tagstt_version; | 
 | 		tagstt->entries[tagstt->nentries-1].characts = tagstt_characts; | 
 |  | 
 | 		if(pedantic && !$4->size) | 
 | 			yywarning("Zero length strings make no sense"); | 
 | 		if(!win32 && $4->size > 254) | 
 | 			yyerror("Stringtable entry more than 254 characters"); | 
 | 		if(win32 && $4->size > 65534) /* Hmm..., does this happen? */ | 
 | 			yyerror("Stringtable entry more than 65534 characters (probably something else that went wrong)"); | 
 | 		$$ = tagstt; | 
 | 		} | 
 | 	; | 
 |  | 
 | opt_comma	/* There seem to be two ways to specify a stringtable... */ | 
 | 	: /* Empty */ | 
 | 	| ',' | 
 | 	; | 
 |  | 
 | /* ------------------------------ VersionInfo ------------------------------ */ | 
 | versioninfo | 
 | 	: tVERSIONINFO loadmemopts fix_version tBEGIN ver_blocks tEND { | 
 | 		$$ = $3; | 
 | 		if($2) | 
 | 		{ | 
 | 			$$->memopt = *($2); | 
 | 			free($2); | 
 | 		} | 
 | 		else | 
 | 			$$->memopt = WRC_MO_MOVEABLE | (win32 ? WRC_MO_PURE : 0); | 
 | 		$$->blocks = get_ver_block_head($5); | 
 | 		/* Set language; there is no version or characteristics */ | 
 | 		$$->lvc.language = dup_language(currentlanguage); | 
 | 		} | 
 | 	; | 
 |  | 
 | fix_version | 
 | 	: /* Empty */			{ $$ = new_versioninfo(); } | 
 | 	| fix_version tFILEVERSION expr ',' expr ',' expr ',' expr { | 
 | 		if($1->gotit.fv) | 
 | 			yyerror("FILEVERSION already defined"); | 
 | 		$$ = $1; | 
 | 		$$->filever_maj1 = $3; | 
 | 		$$->filever_maj2 = $5; | 
 | 		$$->filever_min1 = $7; | 
 | 		$$->filever_min2 = $9; | 
 | 		$$->gotit.fv = 1; | 
 | 		} | 
 | 	| fix_version tPRODUCTVERSION expr ',' expr ',' expr ',' expr { | 
 | 		if($1->gotit.pv) | 
 | 			yyerror("PRODUCTVERSION already defined"); | 
 | 		$$ = $1; | 
 | 		$$->prodver_maj1 = $3; | 
 | 		$$->prodver_maj2 = $5; | 
 | 		$$->prodver_min1 = $7; | 
 | 		$$->prodver_min2 = $9; | 
 | 		$$->gotit.pv = 1; | 
 | 		} | 
 | 	| fix_version tFILEFLAGS expr { | 
 | 		if($1->gotit.ff) | 
 | 			yyerror("FILEFLAGS already defined"); | 
 | 		$$ = $1; | 
 | 		$$->fileflags = $3; | 
 | 		$$->gotit.ff = 1; | 
 | 		} | 
 | 	| fix_version tFILEFLAGSMASK expr { | 
 | 		if($1->gotit.ffm) | 
 | 			yyerror("FILEFLAGSMASK already defined"); | 
 | 		$$ = $1; | 
 | 		$$->fileflagsmask = $3; | 
 | 		$$->gotit.ffm = 1; | 
 | 		} | 
 | 	| fix_version tFILEOS expr { | 
 | 		if($1->gotit.fo) | 
 | 			yyerror("FILEOS already defined"); | 
 | 		$$ = $1; | 
 | 		$$->fileos = $3; | 
 | 		$$->gotit.fo = 1; | 
 | 		} | 
 | 	| fix_version tFILETYPE expr { | 
 | 		if($1->gotit.ft) | 
 | 			yyerror("FILETYPE already defined"); | 
 | 		$$ = $1; | 
 | 		$$->filetype = $3; | 
 | 		$$->gotit.ft = 1; | 
 | 		} | 
 | 	| fix_version tFILESUBTYPE expr { | 
 | 		if($1->gotit.fst) | 
 | 			yyerror("FILESUBTYPE already defined"); | 
 | 		$$ = $1; | 
 | 		$$->filesubtype = $3; | 
 | 		$$->gotit.fst = 1; | 
 | 		} | 
 | 	; | 
 |  | 
 | ver_blocks | 
 | 	: /* Empty */			{ $$ = NULL; } | 
 | 	| ver_blocks ver_block { | 
 | 		$$ = $2; | 
 | 		$$->prev = $1; | 
 | 		if($1) | 
 | 			$1->next = $$; | 
 | 		} | 
 | 	; | 
 |  | 
 | ver_block | 
 | 	: tBLOCK tSTRING tBEGIN ver_values tEND { | 
 | 		$$ = new_ver_block(); | 
 | 		$$->name = $2; | 
 | 		$$->values = get_ver_value_head($4); | 
 | 		} | 
 | 	; | 
 |  | 
 | ver_values | 
 | 	: /* Empty */			{ $$ = NULL; } | 
 | 	| ver_values ver_value { | 
 | 		$$ = $2; | 
 | 		$$->prev = $1; | 
 | 		if($1) | 
 | 			$1->next = $$; | 
 | 		} | 
 | 	; | 
 |  | 
 | ver_value | 
 | 	: ver_block { | 
 | 		$$ = new_ver_value(); | 
 | 		$$->type = val_block; | 
 | 		$$->value.block = $1; | 
 | 		} | 
 | 	| tVALUE tSTRING ',' tSTRING { | 
 | 		$$ = new_ver_value(); | 
 | 		$$->type = val_str; | 
 | 		$$->key = $2; | 
 | 		$$->value.str = $4; | 
 | 		} | 
 | 	| tVALUE tSTRING ',' ver_words { | 
 | 		$$ = new_ver_value(); | 
 | 		$$->type = val_words; | 
 | 		$$->key = $2; | 
 | 		$$->value.words = $4; | 
 | 		} | 
 | 	; | 
 |  | 
 | ver_words | 
 | 	: expr			{ $$ = new_ver_words($1); } | 
 | 	| ver_words ',' expr	{ $$ = add_ver_words($1, $3); } | 
 | 	; | 
 |  | 
 | /* ------------------------------ Toolbar ------------------------------ */ | 
 | toolbar: tTOOLBAR loadmemopts expr ',' expr opt_lvc tBEGIN toolbar_items tEND { | 
 | 		int nitems; | 
 | 		toolbar_item_t *items = get_tlbr_buttons_head($8, &nitems); | 
 | 		$$ = new_toolbar($3, $5, items, nitems); | 
 | 		if($2) | 
 | 		{ | 
 | 			$$->memopt = *($2); | 
 | 			free($2); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			$$->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE; | 
 | 		} | 
 | 		if($6) | 
 | 		{ | 
 | 			$$->lvc = *($6); | 
 | 			free($6); | 
 | 		} | 
 | 		if(!$$->lvc.language) | 
 | 		{ | 
 | 			$$->lvc.language = dup_language(currentlanguage); | 
 | 		} | 
 | 		} | 
 | 	; | 
 |  | 
 | toolbar_items | 
 | 	:  /* Empty */			{ $$ = NULL; } | 
 | 	| toolbar_items tBUTTON expr	{ | 
 | 		toolbar_item_t *idrec = new_toolbar_item(); | 
 | 		idrec->id = $3; | 
 | 		$$ = ins_tlbr_button($1, idrec); | 
 | 		} | 
 | 	| toolbar_items tSEPARATOR	{ | 
 | 		toolbar_item_t *idrec = new_toolbar_item(); | 
 | 		idrec->id = 0; | 
 | 		$$ = ins_tlbr_button($1, idrec); | 
 | 	} | 
 | 	; | 
 |  | 
 | /* ------------------------------ Memory options ------------------------------ */ | 
 | loadmemopts | 
 | 	: /* Empty */		{ $$ = NULL; } | 
 | 	| loadmemopts lamo { | 
 | 		if($1) | 
 | 		{ | 
 | 			*($1) |= *($2); | 
 | 			$$ = $1; | 
 | 			free($2); | 
 | 		} | 
 | 		else | 
 | 			$$ = $2; | 
 | 		} | 
 | 	| loadmemopts lama { | 
 | 		if($1) | 
 | 		{ | 
 | 			*($1) &= *($2); | 
 | 			$$ = $1; | 
 | 			free($2); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			*$2 &= WRC_MO_MOVEABLE | WRC_MO_DISCARDABLE | WRC_MO_PURE; | 
 | 			$$ = $2; | 
 | 		} | 
 | 		} | 
 | 	; | 
 |  | 
 | lamo	: tPRELOAD	{ $$ = new_int(WRC_MO_PRELOAD); } | 
 | 	| tMOVEABLE	{ $$ = new_int(WRC_MO_MOVEABLE); } | 
 | 	| tDISCARDABLE	{ $$ = new_int(WRC_MO_DISCARDABLE); } | 
 | 	| tPURE		{ $$ = new_int(WRC_MO_PURE); } | 
 | 	; | 
 |  | 
 | lama	: tLOADONCALL	{ $$ = new_int(~WRC_MO_PRELOAD); } | 
 | 	| tFIXED	{ $$ = new_int(~WRC_MO_MOVEABLE); } | 
 | 	| tIMPURE	{ $$ = new_int(~WRC_MO_PURE); } | 
 | 	; | 
 |  | 
 | /* ------------------------------ Win32 options ------------------------------ */ | 
 | opt_lvc	: /* Empty */		{ $$ = new_lvc(); } | 
 | 	| opt_lvc opt_language { | 
 | 		if(!win32) | 
 | 			yywarning("LANGUAGE not supported in 16-bit mode"); | 
 | 		if($1->language) | 
 | 			yyerror("Language already defined"); | 
 | 		$$ = $1; | 
 | 		$1->language = $2; | 
 | 		} | 
 | 	| opt_lvc opt_characts { | 
 | 		if(!win32) | 
 | 			yywarning("CHARACTERISTICS not supported in 16-bit mode"); | 
 | 		if($1->characts) | 
 | 			yyerror("Characteristics already defined"); | 
 | 		$$ = $1; | 
 | 		$1->characts = $2; | 
 | 		} | 
 | 	| opt_lvc opt_version { | 
 | 		if(!win32) | 
 | 			yywarning("VERSION not supported in 16-bit mode"); | 
 | 		if($1->version) | 
 | 			yyerror("Version already defined"); | 
 | 		$$ = $1; | 
 | 		$1->version = $2; | 
 | 		} | 
 | 	; | 
 |  | 
 | 	/* | 
 | 	 * This here is another s/r conflict on {bi,u}nary + and -. | 
 | 	 * It is due to the look-ahead which must determine when the | 
 | 	 * rule opt_language ends. It could be solved with adding a | 
 | 	 * tNL at the end, but that seems unreasonable to do. | 
 | 	 * The conflict is now moved to the expression handling below. | 
 | 	 */ | 
 | opt_language | 
 | 	: tLANGUAGE expr ',' expr	{ $$ = new_language($2, $4); | 
 | 					  if (get_language_codepage($2, $4) == -1) | 
 | 						yyerror( "Language %04x is not supported", ($4<<10) + $2); | 
 | 					} | 
 | 	; | 
 |  | 
 | opt_characts | 
 | 	: tCHARACTERISTICS expr		{ $$ = new_characts($2); } | 
 | 	; | 
 |  | 
 | opt_version | 
 | 	: tVERSION expr			{ $$ = new_version($2); } | 
 | 	; | 
 |  | 
 | /* ------------------------------ Raw data handling ------------------------------ */ | 
 | raw_data: opt_lvc tBEGIN raw_elements tEND { | 
 | 		if($1) | 
 | 		{ | 
 | 			$3->lvc = *($1); | 
 | 			free($1); | 
 | 		} | 
 |  | 
 | 		if(!$3->lvc.language) | 
 | 			$3->lvc.language = dup_language(currentlanguage); | 
 |  | 
 | 		$$ = $3; | 
 | 		} | 
 | 	; | 
 |  | 
 | raw_elements | 
 | 	: tRAWDATA			{ $$ = $1; } | 
 | 	| tNUMBER			{ $$ = int2raw_data($1); } | 
 | 	| tLNUMBER			{ $$ = long2raw_data($1); } | 
 | 	| tSTRING			{ $$ = str2raw_data($1); } | 
 | 	| raw_elements opt_comma tRAWDATA { $$ = merge_raw_data($1, $3); free($3->data); free($3); } | 
 | 	| raw_elements opt_comma tNUMBER  { $$ = merge_raw_data_int($1, $3); } | 
 | 	| raw_elements opt_comma tLNUMBER { $$ = merge_raw_data_long($1, $3); } | 
 | 	| raw_elements opt_comma tSTRING  { $$ = merge_raw_data_str($1, $3); } | 
 | 	; | 
 |  | 
 | /* File data or raw data */ | 
 | file_raw: filename	{ $$ = load_file($1,dup_language(currentlanguage)); } | 
 | 	| raw_data	{ $$ = $1; } | 
 | 	; | 
 |  | 
 | /* ------------------------------ Win32 expressions ------------------------------ */ | 
 | /* All win16 numbers are also handled here. This is inconsistent with MS' | 
 |  * resource compiler, but what the heck, its just handy to have. | 
 |  */ | 
 | e_expr	: /* Empty */	{ $$ = 0; } | 
 | 	| expr		{ $$ = new_int($1); } | 
 | 	; | 
 |  | 
 | /* This rule moves ALL s/r conflicts on {bi,u}nary - and + to here */ | 
 | expr	: xpr	{ $$ = ($1); } | 
 | 	; | 
 |  | 
 | xpr	: xpr '+' xpr	{ $$ = ($1) + ($3); } | 
 | 	| xpr '-' xpr	{ $$ = ($1) - ($3); } | 
 | 	| xpr '|' xpr	{ $$ = ($1) | ($3); } | 
 | 	| xpr '&' xpr	{ $$ = ($1) & ($3); } | 
 | 	| xpr '*' xpr	{ $$ = ($1) * ($3); } | 
 | 	| xpr '/' xpr	{ $$ = ($1) / ($3); } | 
 | 	| xpr '^' xpr	{ $$ = ($1) ^ ($3); } | 
 | 	| '~' xpr	{ $$ = ~($2); } | 
 | 	| '-' xpr %prec pUPM	{ $$ = -($2); } | 
 | 	| '+' xpr %prec pUPM	{ $$ = $2; } | 
 | 	| '(' xpr ')'	{ $$ = $2; } | 
 | 	| any_num	{ $$ = $1; } | 
 | 	| tNOT any_num	{ $$ = ~($2); } | 
 | 	; | 
 |  | 
 | any_num	: tNUMBER	{ $$ = $1; } | 
 | 	| tLNUMBER	{ $$ = $1; } | 
 | 	; | 
 |  | 
 | %% | 
 | /* Dialog specific functions */ | 
 | static dialog_t *dialog_style(style_t * st, dialog_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->style == NULL) | 
 | 	{ | 
 | 		dlg->style = new_style(0,0); | 
 | 	} | 
 |  | 
 | 	if(dlg->gotstyle) | 
 | 	{ | 
 | 		yywarning("Style already defined, or-ing together"); | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		dlg->style->or_mask = 0; | 
 | 		dlg->style->and_mask = 0; | 
 | 	} | 
 | 	dlg->style->or_mask |= st->or_mask; | 
 | 	dlg->style->and_mask |= st->and_mask; | 
 | 	dlg->gotstyle = TRUE; | 
 | 	free(st); | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialog_t *dialog_exstyle(style_t *st, dialog_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->exstyle == NULL) | 
 | 	{ | 
 | 		dlg->exstyle = new_style(0,0); | 
 | 	} | 
 |  | 
 | 	if(dlg->gotexstyle) | 
 | 	{ | 
 | 		yywarning("ExStyle already defined, or-ing together"); | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		dlg->exstyle->or_mask = 0; | 
 | 		dlg->exstyle->and_mask = 0; | 
 | 	} | 
 | 	dlg->exstyle->or_mask |= st->or_mask; | 
 | 	dlg->exstyle->and_mask |= st->and_mask; | 
 | 	dlg->gotexstyle = TRUE; | 
 | 	free(st); | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialog_t *dialog_caption(string_t *s, dialog_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->title) | 
 | 		yyerror("Caption already defined"); | 
 | 	dlg->title = s; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialog_t *dialog_font(font_id_t *f, dialog_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->font) | 
 | 		yyerror("Font already defined"); | 
 | 	dlg->font = f; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialog_t *dialog_class(name_id_t *n, dialog_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->dlgclass) | 
 | 		yyerror("Class already defined"); | 
 | 	dlg->dlgclass = n; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialog_t *dialog_menu(name_id_t *m, dialog_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->menu) | 
 | 		yyerror("Menu already defined"); | 
 | 	dlg->menu = m; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialog_t *dialog_language(language_t *l, dialog_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->lvc.language) | 
 | 		yyerror("Language already defined"); | 
 | 	dlg->lvc.language = l; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialog_t *dialog_characteristics(characts_t *c, dialog_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->lvc.characts) | 
 | 		yyerror("Characteristics already defined"); | 
 | 	dlg->lvc.characts = c; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialog_t *dialog_version(version_t *v, dialog_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->lvc.version) | 
 | 		yyerror("Version already defined"); | 
 | 	dlg->lvc.version = v; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | /* Controls specific functions */ | 
 | static control_t *ins_ctrl(int type, int special_style, control_t *ctrl, control_t *prev) | 
 | { | 
 | 	/* Hm... this seems to be jammed in at all time... */ | 
 | 	int defaultstyle = WS_CHILD | WS_VISIBLE; | 
 |  | 
 | 	assert(ctrl != NULL); | 
 | 	ctrl->prev = prev; | 
 |  | 
 | 	if(prev) | 
 | 		prev->next = ctrl; | 
 |  | 
 | 	if(type != -1) | 
 | 	{ | 
 | 		ctrl->ctlclass = new_name_id(); | 
 | 		ctrl->ctlclass->type = name_ord; | 
 | 		ctrl->ctlclass->name.i_name = type; | 
 | 	} | 
 |  | 
 | 	switch(type) | 
 | 	{ | 
 | 	case CT_BUTTON: | 
 | 		if(special_style != BS_GROUPBOX && special_style != BS_RADIOBUTTON) | 
 | 			defaultstyle |= WS_TABSTOP; | 
 | 		break; | 
 | 	case CT_EDIT: | 
 | 		defaultstyle |= WS_TABSTOP | WS_BORDER; | 
 | 		break; | 
 | 	case CT_LISTBOX: | 
 | 		defaultstyle |= LBS_NOTIFY | WS_BORDER; | 
 | 		break; | 
 | 	case CT_COMBOBOX: | 
 |                 if (!(ctrl->style->or_mask & (CBS_SIMPLE | CBS_DROPDOWN | CBS_DROPDOWNLIST))) | 
 |                     defaultstyle |= CBS_SIMPLE; | 
 | 		break; | 
 | 	case CT_STATIC: | 
 | 		if(special_style == SS_CENTER || special_style == SS_LEFT || special_style == SS_RIGHT) | 
 | 			defaultstyle |= WS_GROUP; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	if(!ctrl->gotstyle)	/* Handle default style setting */ | 
 | 	{ | 
 | 		switch(type) | 
 | 		{ | 
 | 		case CT_EDIT: | 
 | 			defaultstyle |= ES_LEFT; | 
 | 			break; | 
 | 		case CT_LISTBOX: | 
 | 			defaultstyle |= LBS_NOTIFY; | 
 | 			break; | 
 | 		case CT_COMBOBOX: | 
 | 			defaultstyle |= CBS_SIMPLE | WS_TABSTOP; | 
 | 			break; | 
 | 		case CT_SCROLLBAR: | 
 | 			defaultstyle |= SBS_HORZ; | 
 | 			break; | 
 | 		case CT_BUTTON: | 
 | 			switch(special_style) | 
 | 			{ | 
 | 			case BS_CHECKBOX: | 
 | 			case BS_DEFPUSHBUTTON: | 
 | 			case BS_PUSHBUTTON: | 
 | /*			case BS_PUSHBOX:	*/ | 
 | 			case BS_AUTORADIOBUTTON: | 
 | 			case BS_AUTO3STATE: | 
 | 			case BS_3STATE: | 
 | 			case BS_AUTOCHECKBOX: | 
 | 				defaultstyle |= WS_TABSTOP; | 
 | 				break; | 
 | 			default: | 
 | 				yywarning("Unknown default button control-style 0x%08x", special_style); | 
 | 			case BS_GROUPBOX: | 
 | 			case BS_RADIOBUTTON: | 
 | 				break; | 
 | 			} | 
 | 			break; | 
 |  | 
 | 		case CT_STATIC: | 
 | 			switch(special_style) | 
 | 			{ | 
 | 			case SS_LEFT: | 
 | 			case SS_RIGHT: | 
 | 			case SS_CENTER: | 
 | 				defaultstyle |= WS_GROUP; | 
 | 				break; | 
 | 			case SS_ICON:	/* Special case */ | 
 | 				break; | 
 | 			default: | 
 | 				yywarning("Unknown default static control-style 0x%08x", special_style); | 
 | 				break; | 
 | 			} | 
 | 			break; | 
 | 		case -1:	/* Generic control */ | 
 | 			goto byebye; | 
 |  | 
 | 		default: | 
 | 			yyerror("Internal error (report this): Got weird control type 0x%08x", type); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* The SS_ICON flag is always forced in for icon controls */ | 
 | 	if(type == CT_STATIC && special_style == SS_ICON) | 
 | 		defaultstyle |= SS_ICON; | 
 |  | 
 | 	if (!ctrl->gotstyle) | 
 | 		ctrl->style = new_style(0,0); | 
 |  | 
 | 	/* combine all styles */ | 
 | 	ctrl->style->or_mask = ctrl->style->or_mask | defaultstyle | special_style; | 
 | 	ctrl->gotstyle = TRUE; | 
 | byebye: | 
 | 	/* combine with NOT mask */ | 
 | 	if (ctrl->gotstyle) | 
 | 	{ | 
 | 		ctrl->style->or_mask &= ~(ctrl->style->and_mask); | 
 | 		ctrl->style->and_mask = 0; | 
 | 	} | 
 | 	if (ctrl->gotexstyle) | 
 | 	{ | 
 | 		ctrl->exstyle->or_mask &= ~(ctrl->exstyle->and_mask); | 
 | 		ctrl->exstyle->and_mask = 0; | 
 | 	} | 
 | 	return ctrl; | 
 | } | 
 |  | 
 | static name_id_t *convert_ctlclass(name_id_t *cls) | 
 | { | 
 | 	char *cc = NULL; | 
 | 	int iclass; | 
 |  | 
 | 	if(cls->type == name_ord) | 
 | 		return cls; | 
 | 	assert(cls->type == name_str); | 
 | 	if(cls->type == str_unicode) | 
 | 	{ | 
 | 		yyerror("Don't yet support unicode class comparison"); | 
 | 	} | 
 | 	else | 
 | 		cc = cls->name.s_name->str.cstr; | 
 |  | 
 | 	if(!strcasecmp("BUTTON", cc)) | 
 | 		iclass = CT_BUTTON; | 
 | 	else if(!strcasecmp("COMBOBOX", cc)) | 
 | 		iclass = CT_COMBOBOX; | 
 | 	else if(!strcasecmp("LISTBOX", cc)) | 
 | 		iclass = CT_LISTBOX; | 
 | 	else if(!strcasecmp("EDIT", cc)) | 
 | 		iclass = CT_EDIT; | 
 | 	else if(!strcasecmp("STATIC", cc)) | 
 | 		iclass = CT_STATIC; | 
 | 	else if(!strcasecmp("SCROLLBAR", cc)) | 
 | 		iclass = CT_SCROLLBAR; | 
 | 	else | 
 | 		return cls;	/* No default, return user controlclass */ | 
 |  | 
 | 	free(cls->name.s_name->str.cstr); | 
 | 	free(cls->name.s_name); | 
 | 	cls->type = name_ord; | 
 | 	cls->name.i_name = iclass; | 
 | 	return cls; | 
 | } | 
 |  | 
 | /* DialogEx specific functions */ | 
 | static dialogex_t *dialogex_style(style_t * st, dialogex_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->style == NULL) | 
 | 	{ | 
 | 		dlg->style = new_style(0,0); | 
 | 	} | 
 |  | 
 | 	if(dlg->gotstyle) | 
 | 	{ | 
 | 		yywarning("Style already defined, or-ing together"); | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		dlg->style->or_mask = 0; | 
 | 		dlg->style->and_mask = 0; | 
 | 	} | 
 | 	dlg->style->or_mask |= st->or_mask; | 
 | 	dlg->style->and_mask |= st->and_mask; | 
 | 	dlg->gotstyle = TRUE; | 
 | 	free(st); | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialogex_t *dialogex_exstyle(style_t * st, dialogex_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->exstyle == NULL) | 
 | 	{ | 
 | 		dlg->exstyle = new_style(0,0); | 
 | 	} | 
 |  | 
 | 	if(dlg->gotexstyle) | 
 | 	{ | 
 | 		yywarning("ExStyle already defined, or-ing together"); | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		dlg->exstyle->or_mask = 0; | 
 | 		dlg->exstyle->and_mask = 0; | 
 | 	} | 
 | 	dlg->exstyle->or_mask |= st->or_mask; | 
 | 	dlg->exstyle->and_mask |= st->and_mask; | 
 | 	dlg->gotexstyle = TRUE; | 
 | 	free(st); | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialogex_t *dialogex_caption(string_t *s, dialogex_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->title) | 
 | 		yyerror("Caption already defined"); | 
 | 	dlg->title = s; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialogex_t *dialogex_font(font_id_t *f, dialogex_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->font) | 
 | 		yyerror("Font already defined"); | 
 | 	dlg->font = f; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialogex_t *dialogex_class(name_id_t *n, dialogex_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->dlgclass) | 
 | 		yyerror("Class already defined"); | 
 | 	dlg->dlgclass = n; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialogex_t *dialogex_menu(name_id_t *m, dialogex_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->menu) | 
 | 		yyerror("Menu already defined"); | 
 | 	dlg->menu = m; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialogex_t *dialogex_language(language_t *l, dialogex_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->lvc.language) | 
 | 		yyerror("Language already defined"); | 
 | 	dlg->lvc.language = l; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialogex_t *dialogex_characteristics(characts_t *c, dialogex_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->lvc.characts) | 
 | 		yyerror("Characteristics already defined"); | 
 | 	dlg->lvc.characts = c; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | static dialogex_t *dialogex_version(version_t *v, dialogex_t *dlg) | 
 | { | 
 | 	assert(dlg != NULL); | 
 | 	if(dlg->lvc.version) | 
 | 		yyerror("Version already defined"); | 
 | 	dlg->lvc.version = v; | 
 | 	return dlg; | 
 | } | 
 |  | 
 | /* Accelerator specific functions */ | 
 | static event_t *add_event(int key, int id, int flags, event_t *prev) | 
 | { | 
 | 	event_t *ev = new_event(); | 
 |  | 
 | 	if((flags & (WRC_AF_VIRTKEY | WRC_AF_ASCII)) == (WRC_AF_VIRTKEY | WRC_AF_ASCII)) | 
 | 		yyerror("Cannot use both ASCII and VIRTKEY"); | 
 |  | 
 | 	ev->key = key; | 
 | 	ev->id = id; | 
 | 	ev->flags = flags & ~WRC_AF_ASCII; | 
 | 	ev->prev = prev; | 
 | 	if(prev) | 
 | 		prev->next = ev; | 
 | 	return ev; | 
 | } | 
 |  | 
 | static event_t *add_string_event(string_t *key, int id, int flags, event_t *prev) | 
 | { | 
 | 	int keycode = 0; | 
 | 	event_t *ev = new_event(); | 
 |  | 
 | 	if(key->type != str_char) | 
 | 		yyerror("Key code must be an ascii string"); | 
 |  | 
 | 	if((flags & WRC_AF_VIRTKEY) && (!isupper(key->str.cstr[0] & 0xff) && !isdigit(key->str.cstr[0] & 0xff))) | 
 | 		yyerror("VIRTKEY code is not equal to ascii value"); | 
 |  | 
 | 	if(key->str.cstr[0] == '^' && (flags & WRC_AF_CONTROL) != 0) | 
 | 	{ | 
 | 		yyerror("Cannot use both '^' and CONTROL modifier"); | 
 | 	} | 
 | 	else if(key->str.cstr[0] == '^') | 
 | 	{ | 
 | 		keycode = toupper(key->str.cstr[1]) - '@'; | 
 | 		if(keycode >= ' ') | 
 | 			yyerror("Control-code out of range"); | 
 | 	} | 
 | 	else | 
 | 		keycode = key->str.cstr[0]; | 
 | 	ev->key = keycode; | 
 | 	ev->id = id; | 
 | 	ev->flags = flags & ~WRC_AF_ASCII; | 
 | 	ev->prev = prev; | 
 | 	if(prev) | 
 | 		prev->next = ev; | 
 | 	return ev; | 
 | } | 
 |  | 
 | /* MenuEx specific functions */ | 
 | static itemex_opt_t *new_itemex_opt(int id, int type, int state, int helpid) | 
 | { | 
 | 	itemex_opt_t *opt = (itemex_opt_t *)xmalloc(sizeof(itemex_opt_t)); | 
 | 	opt->id = id; | 
 | 	opt->type = type; | 
 | 	opt->state = state; | 
 | 	opt->helpid = helpid; | 
 | 	return opt; | 
 | } | 
 |  | 
 | /* Raw data functions */ | 
 | static raw_data_t *load_file(string_t *filename, language_t *lang) | 
 | { | 
 | 	FILE *fp = NULL; | 
 | 	char *path; | 
 | 	raw_data_t *rd; | 
 | 	string_t *name; | 
 | 	int codepage = get_language_codepage(lang->id, lang->sub); | 
 |  | 
 | 	/* FIXME: we may want to use utf-8 here */ | 
 | 	if (codepage <= 0 && filename->type != str_char) | 
 | 		yyerror("Cannot convert filename to ASCII string"); | 
 | 	name = convert_string( filename, str_char, codepage ); | 
 | 	if (!(path = wpp_find_include(name->str.cstr, 1))) | 
 | 		yyerror("Cannot open file %s", name->str.cstr); | 
 | 	if (!(fp = fopen( path, "rb" ))) | 
 | 		yyerror("Cannot open file %s", name->str.cstr); | 
 | 	free( path ); | 
 | 	rd = new_raw_data(); | 
 | 	fseek(fp, 0, SEEK_END); | 
 | 	rd->size = ftell(fp); | 
 | 	fseek(fp, 0, SEEK_SET); | 
 | 	rd->data = (char *)xmalloc(rd->size); | 
 | 	fread(rd->data, rd->size, 1, fp); | 
 | 	fclose(fp); | 
 | 	rd->lvc.language = lang; | 
 | 	free_string(name); | 
 | 	return rd; | 
 | } | 
 |  | 
 | static raw_data_t *int2raw_data(int i) | 
 | { | 
 | 	raw_data_t *rd; | 
 |  | 
 | 	if((int)((short)i) != i) | 
 | 		yywarning("Integer constant out of 16bit range (%d), truncated to %d\n", i, (short)i); | 
 |  | 
 | 	rd = new_raw_data(); | 
 | 	rd->size = sizeof(short); | 
 | 	rd->data = (char *)xmalloc(rd->size); | 
 | 	switch(byteorder) | 
 | 	{ | 
 | #ifdef WORDS_BIGENDIAN | 
 | 	default: | 
 | #endif | 
 | 	case WRC_BO_BIG: | 
 | 		rd->data[0] = HIBYTE(i); | 
 | 		rd->data[1] = LOBYTE(i); | 
 | 		break; | 
 |  | 
 | #ifndef WORDS_BIGENDIAN | 
 | 	default: | 
 | #endif | 
 | 	case WRC_BO_LITTLE: | 
 | 		rd->data[1] = HIBYTE(i); | 
 | 		rd->data[0] = LOBYTE(i); | 
 | 		break; | 
 | 	} | 
 | 	return rd; | 
 | } | 
 |  | 
 | static raw_data_t *long2raw_data(int i) | 
 | { | 
 | 	raw_data_t *rd; | 
 | 	rd = new_raw_data(); | 
 | 	rd->size = sizeof(int); | 
 | 	rd->data = (char *)xmalloc(rd->size); | 
 | 	switch(byteorder) | 
 | 	{ | 
 | #ifdef WORDS_BIGENDIAN | 
 | 	default: | 
 | #endif | 
 | 	case WRC_BO_BIG: | 
 | 		rd->data[0] = HIBYTE(HIWORD(i)); | 
 | 		rd->data[1] = LOBYTE(HIWORD(i)); | 
 | 		rd->data[2] = HIBYTE(LOWORD(i)); | 
 | 		rd->data[3] = LOBYTE(LOWORD(i)); | 
 | 		break; | 
 |  | 
 | #ifndef WORDS_BIGENDIAN | 
 | 	default: | 
 | #endif | 
 | 	case WRC_BO_LITTLE: | 
 | 		rd->data[3] = HIBYTE(HIWORD(i)); | 
 | 		rd->data[2] = LOBYTE(HIWORD(i)); | 
 | 		rd->data[1] = HIBYTE(LOWORD(i)); | 
 | 		rd->data[0] = LOBYTE(LOWORD(i)); | 
 | 		break; | 
 | 	} | 
 | 	return rd; | 
 | } | 
 |  | 
 | static raw_data_t *str2raw_data(string_t *str) | 
 | { | 
 | 	raw_data_t *rd; | 
 | 	rd = new_raw_data(); | 
 | 	rd->size = str->size * (str->type == str_char ? 1 : 2); | 
 | 	rd->data = (char *)xmalloc(rd->size); | 
 | 	if(str->type == str_char) | 
 | 		memcpy(rd->data, str->str.cstr, rd->size); | 
 | 	else if(str->type == str_unicode) | 
 | 	{ | 
 | 		int i; | 
 | 		switch(byteorder) | 
 | 		{ | 
 | #ifdef WORDS_BIGENDIAN | 
 | 		default: | 
 | #endif | 
 | 		case WRC_BO_BIG: | 
 | 			for(i = 0; i < str->size; i++) | 
 | 			{ | 
 | 				rd->data[2*i + 0] = HIBYTE((WORD)str->str.wstr[i]); | 
 | 				rd->data[2*i + 1] = LOBYTE((WORD)str->str.wstr[i]); | 
 | 			} | 
 | 			break; | 
 | #ifndef WORDS_BIGENDIAN | 
 | 		default: | 
 | #endif | 
 | 		case WRC_BO_LITTLE: | 
 | 			for(i = 0; i < str->size; i++) | 
 | 			{ | 
 | 				rd->data[2*i + 1] = HIBYTE((WORD)str->str.wstr[i]); | 
 | 				rd->data[2*i + 0] = LOBYTE((WORD)str->str.wstr[i]); | 
 | 			} | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	else | 
 | 		internal_error(__FILE__, __LINE__, "Invalid stringtype"); | 
 | 	return rd; | 
 | } | 
 |  | 
 | static raw_data_t *merge_raw_data(raw_data_t *r1, raw_data_t *r2) | 
 | { | 
 | 	r1->data = xrealloc(r1->data, r1->size + r2->size); | 
 | 	memcpy(r1->data + r1->size, r2->data, r2->size); | 
 | 	r1->size += r2->size; | 
 | 	return r1; | 
 | } | 
 |  | 
 | static raw_data_t *merge_raw_data_int(raw_data_t *r1, int i) | 
 | { | 
 | 	raw_data_t *t = int2raw_data(i); | 
 | 	merge_raw_data(r1, t); | 
 | 	free(t->data); | 
 | 	free(t); | 
 | 	return r1; | 
 | } | 
 |  | 
 | static raw_data_t *merge_raw_data_long(raw_data_t *r1, int i) | 
 | { | 
 | 	raw_data_t *t = long2raw_data(i); | 
 | 	merge_raw_data(r1, t); | 
 | 	free(t->data); | 
 | 	free(t); | 
 | 	return r1; | 
 | } | 
 |  | 
 | static raw_data_t *merge_raw_data_str(raw_data_t *r1, string_t *str) | 
 | { | 
 | 	raw_data_t *t = str2raw_data(str); | 
 | 	merge_raw_data(r1, t); | 
 | 	free(t->data); | 
 | 	free(t); | 
 | 	return r1; | 
 | } | 
 |  | 
 | /* Function the go back in a list to get the head */ | 
 | static menu_item_t *get_item_head(menu_item_t *p) | 
 | { | 
 | 	if(!p) | 
 | 		return NULL; | 
 | 	while(p->prev) | 
 | 		p = p->prev; | 
 | 	return p; | 
 | } | 
 |  | 
 | static menuex_item_t *get_itemex_head(menuex_item_t *p) | 
 | { | 
 | 	if(!p) | 
 | 		return NULL; | 
 | 	while(p->prev) | 
 | 		p = p->prev; | 
 | 	return p; | 
 | } | 
 |  | 
 | static resource_t *get_resource_head(resource_t *p) | 
 | { | 
 | 	if(!p) | 
 | 		return NULL; | 
 | 	while(p->prev) | 
 | 		p = p->prev; | 
 | 	return p; | 
 | } | 
 |  | 
 | static ver_block_t *get_ver_block_head(ver_block_t *p) | 
 | { | 
 | 	if(!p) | 
 | 		return NULL; | 
 | 	while(p->prev) | 
 | 		p = p->prev; | 
 | 	return p; | 
 | } | 
 |  | 
 | static ver_value_t *get_ver_value_head(ver_value_t *p) | 
 | { | 
 | 	if(!p) | 
 | 		return NULL; | 
 | 	while(p->prev) | 
 | 		p = p->prev; | 
 | 	return p; | 
 | } | 
 |  | 
 | static control_t *get_control_head(control_t *p) | 
 | { | 
 | 	if(!p) | 
 | 		return NULL; | 
 | 	while(p->prev) | 
 | 		p = p->prev; | 
 | 	return p; | 
 | } | 
 |  | 
 | static event_t *get_event_head(event_t *p) | 
 | { | 
 | 	if(!p) | 
 | 		return NULL; | 
 | 	while(p->prev) | 
 | 		p = p->prev; | 
 | 	return p; | 
 | } | 
 |  | 
 | /* Find a stringtable with given language */ | 
 | static stringtable_t *find_stringtable(lvc_t *lvc) | 
 | { | 
 | 	stringtable_t *stt; | 
 |  | 
 | 	assert(lvc != NULL); | 
 |  | 
 | 	if(!lvc->language) | 
 | 		lvc->language = dup_language(currentlanguage); | 
 |  | 
 | 	for(stt = sttres; stt; stt = stt->next) | 
 | 	{ | 
 | 		if(stt->lvc.language->id == lvc->language->id | 
 | 		&& stt->lvc.language->sub == lvc->language->sub) | 
 | 		{ | 
 | 			/* Found a table with the same language */ | 
 | 			/* The version and characteristics are now handled | 
 | 			 * in the generation of the individual stringtables. | 
 | 			 * This enables localized analysis. | 
 | 			if((stt->lvc.version && lvc->version && *(stt->lvc.version) != *(lvc->version)) | 
 | 			|| (!stt->lvc.version && lvc->version) | 
 | 			|| (stt->lvc.version && !lvc->version)) | 
 | 				yywarning("Stringtable's versions are not the same, using first definition"); | 
 |  | 
 | 			if((stt->lvc.characts && lvc->characts && *(stt->lvc.characts) != *(lvc->characts)) | 
 | 			|| (!stt->lvc.characts && lvc->characts) | 
 | 			|| (stt->lvc.characts && !lvc->characts)) | 
 | 				yywarning("Stringtable's characteristics are not the same, using first definition"); | 
 | 			*/ | 
 | 			return stt; | 
 | 		} | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* qsort sorting function for string table entries */ | 
 | #define STE(p)	((stt_entry_t *)(p)) | 
 | static int sort_stt_entry(const void *e1, const void *e2) | 
 | { | 
 | 	return STE(e1)->id - STE(e2)->id; | 
 | } | 
 | #undef STE | 
 |  | 
 | static resource_t *build_stt_resources(stringtable_t *stthead) | 
 | { | 
 | 	stringtable_t *stt; | 
 | 	stringtable_t *newstt; | 
 | 	resource_t *rsc; | 
 | 	resource_t *rsclist = NULL; | 
 | 	resource_t *rsctail = NULL; | 
 | 	int i; | 
 | 	int j; | 
 | 	DWORD andsum; | 
 | 	DWORD orsum; | 
 | 	characts_t *characts; | 
 | 	version_t *version; | 
 |  | 
 | 	if(!stthead) | 
 | 		return NULL; | 
 |  | 
 | 	/* For all languages defined */ | 
 | 	for(stt = stthead; stt; stt = stt->next) | 
 | 	{ | 
 | 		assert(stt->nentries > 0); | 
 |  | 
 | 		/* Sort the entries */ | 
 | 		if(stt->nentries > 1) | 
 | 			qsort(stt->entries, stt->nentries, sizeof(stt->entries[0]), sort_stt_entry); | 
 |  | 
 | 		for(i = 0; i < stt->nentries; ) | 
 | 		{ | 
 | 			newstt = new_stringtable(&stt->lvc); | 
 | 			newstt->entries = (stt_entry_t *)xmalloc(16 * sizeof(stt_entry_t)); | 
 | 			newstt->nentries = 16; | 
 | 			newstt->idbase = stt->entries[i].id & ~0xf; | 
 | 			for(j = 0; j < 16 && i < stt->nentries; j++) | 
 | 			{ | 
 | 				if(stt->entries[i].id - newstt->idbase == j) | 
 | 				{ | 
 | 					newstt->entries[j] = stt->entries[i]; | 
 | 					i++; | 
 | 				} | 
 | 			} | 
 | 			andsum = ~0; | 
 | 			orsum = 0; | 
 | 			characts = NULL; | 
 | 			version = NULL; | 
 | 			/* Check individual memory options and get | 
 | 			 * the first characteristics/version | 
 | 			 */ | 
 | 			for(j = 0; j < 16; j++) | 
 | 			{ | 
 | 				if(!newstt->entries[j].str) | 
 | 					continue; | 
 | 				andsum &= newstt->entries[j].memopt; | 
 | 				orsum |= newstt->entries[j].memopt; | 
 | 				if(!characts) | 
 | 					characts = newstt->entries[j].characts; | 
 | 				if(!version) | 
 | 					version = newstt->entries[j].version; | 
 | 			} | 
 | 			if(andsum != orsum) | 
 | 			{ | 
 | 				warning("Stringtable's memory options are not equal (idbase: %d)", newstt->idbase); | 
 | 			} | 
 | 			/* Check version and characteristics */ | 
 | 			for(j = 0; j < 16; j++) | 
 | 			{ | 
 | 				if(characts | 
 | 				&& newstt->entries[j].characts | 
 | 				&& *newstt->entries[j].characts != *characts) | 
 | 					warning("Stringtable's characteristics are not the same (idbase: %d)", newstt->idbase); | 
 | 				if(version | 
 | 				&& newstt->entries[j].version | 
 | 				&& *newstt->entries[j].version != *version) | 
 | 					warning("Stringtable's versions are not the same (idbase: %d)", newstt->idbase); | 
 | 			} | 
 | 			rsc = new_resource(res_stt, newstt, newstt->memopt, newstt->lvc.language); | 
 | 			rsc->name = new_name_id(); | 
 | 			rsc->name->type = name_ord; | 
 | 			rsc->name->name.i_name = (newstt->idbase >> 4) + 1; | 
 | 			rsc->memopt = andsum; /* Set to least common denominator */ | 
 | 			newstt->memopt = andsum; | 
 | 			newstt->lvc.characts = characts; | 
 | 			newstt->lvc.version = version; | 
 | 			if(!rsclist) | 
 | 			{ | 
 | 				rsclist = rsc; | 
 | 				rsctail = rsc; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				rsctail->next = rsc; | 
 | 				rsc->prev = rsctail; | 
 | 				rsctail = rsc; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return rsclist; | 
 | } | 
 |  | 
 |  | 
 | static toolbar_item_t *ins_tlbr_button(toolbar_item_t *prev, toolbar_item_t *idrec) | 
 | { | 
 | 	idrec->prev = prev; | 
 | 	if(prev) | 
 | 		prev->next = idrec; | 
 |  | 
 | 	return idrec; | 
 | } | 
 |  | 
 | static toolbar_item_t *get_tlbr_buttons_head(toolbar_item_t *p, int *nitems) | 
 | { | 
 | 	if(!p) | 
 | 	{ | 
 | 		*nitems = 0; | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	*nitems = 1; | 
 |  | 
 | 	while(p->prev) | 
 | 	{ | 
 | 		(*nitems)++; | 
 | 		p = p->prev; | 
 | 	} | 
 |  | 
 | 	return p; | 
 | } | 
 |  | 
 | static string_t *make_filename(string_t *str) | 
 | { | 
 |     if(str->type == str_char) | 
 |     { | 
 | 	char *cptr; | 
 |  | 
 | 	/* Remove escaped backslash and convert to forward */ | 
 | 	for(cptr = str->str.cstr; (cptr = strchr(cptr, '\\')) != NULL; cptr++) | 
 | 	{ | 
 | 		if(cptr[1] == '\\') | 
 | 		{ | 
 | 			memmove(cptr, cptr+1, strlen(cptr)); | 
 | 			str->size--; | 
 | 		} | 
 | 		*cptr = '/'; | 
 | 	} | 
 |     } | 
 |     else | 
 |     { | 
 | 	WCHAR *wptr; | 
 |  | 
 | 	/* Remove escaped backslash and convert to forward */ | 
 | 	for(wptr = str->str.wstr; (wptr = strchrW(wptr, '\\')) != NULL; wptr++) | 
 | 	{ | 
 | 		if(wptr[1] == '\\') | 
 | 		{ | 
 | 			memmove(wptr, wptr+1, strlenW(wptr)); | 
 | 			str->size--; | 
 | 		} | 
 | 		*wptr = '/'; | 
 | 	} | 
 |     } | 
 |     return str; | 
 | } | 
 |  | 
 | /* | 
 |  * Process all resources to extract fonts and build | 
 |  * a fontdir resource. | 
 |  * | 
 |  * Note: MS' resource compiler (build 1472) does not | 
 |  * handle font resources with different languages. | 
 |  * The fontdir is generated in the last active language | 
 |  * and font identifiers must be unique across the entire | 
 |  * source. | 
 |  * This is not logical considering the localization | 
 |  * constraints of all other resource types. MS has, | 
 |  * most probably, never testet localized fonts. However, | 
 |  * using fontresources is rare, so it might not occur | 
 |  * in normal applications. | 
 |  * Wine does require better localization because a lot | 
 |  * of languages are coded into the same executable. | 
 |  * Therefore, I will generate fontdirs for *each* | 
 |  * localized set of fonts. | 
 |  */ | 
 | static resource_t *build_fontdir(resource_t **fnt, int nfnt) | 
 | { | 
 | 	static int once = 0; | 
 | 	if(!once) | 
 | 	{ | 
 | 		warning("Need to parse fonts, not yet implemented (fnt: %p, nfnt: %d)", fnt, nfnt); | 
 | 		once++; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static resource_t *build_fontdirs(resource_t *tail) | 
 | { | 
 | 	resource_t *rsc; | 
 | 	resource_t *lst = NULL; | 
 | 	resource_t **fnt = NULL;	/* List of all fonts */ | 
 | 	int nfnt = 0; | 
 | 	resource_t **fnd = NULL;	/* List of all fontdirs */ | 
 | 	int nfnd = 0; | 
 | 	resource_t **lanfnt = NULL; | 
 | 	int nlanfnt = 0; | 
 | 	int i; | 
 | 	name_id_t nid; | 
 | 	string_t str; | 
 | 	int fntleft; | 
 |  | 
 | 	nid.type = name_str; | 
 | 	nid.name.s_name = &str; | 
 | 	str.type = str_char; | 
 | 	str.str.cstr = xstrdup("FONTDIR"); | 
 | 	str.size = 7; | 
 |  | 
 | 	/* Extract all fonts and fontdirs */ | 
 | 	for(rsc = tail; rsc; rsc = rsc->prev) | 
 | 	{ | 
 | 		if(rsc->type == res_fnt) | 
 | 		{ | 
 | 			nfnt++; | 
 | 			fnt = xrealloc(fnt, nfnt * sizeof(*fnt)); | 
 | 			fnt[nfnt-1] = rsc; | 
 | 		} | 
 | 		else if(rsc->type == res_fntdir) | 
 | 		{ | 
 | 			nfnd++; | 
 | 			fnd = xrealloc(fnd, nfnd * sizeof(*fnd)); | 
 | 			fnd[nfnd-1] = rsc; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Verify the name of the present fontdirs */ | 
 | 	for(i = 0; i < nfnd; i++) | 
 | 	{ | 
 | 		if(compare_name_id(&nid, fnd[i]->name)) | 
 | 		{ | 
 | 			warning("User supplied FONTDIR entry has an invalid name '%s', ignored", | 
 | 				get_nameid_str(fnd[i]->name)); | 
 | 			fnd[i] = NULL; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Sanity check */ | 
 | 	if(nfnt == 0) | 
 | 	{ | 
 | 		if(nfnd != 0) | 
 | 			warning("Found %d FONTDIR entries without any fonts present", nfnd); | 
 | 		goto clean; | 
 | 	} | 
 |  | 
 | 	/* Copy space */ | 
 | 	lanfnt = xmalloc(nfnt * sizeof(*lanfnt)); | 
 |  | 
 | 	/* Get all fonts covered by fontdirs */ | 
 | 	for(i = 0; i < nfnd; i++) | 
 | 	{ | 
 | 		int j; | 
 | 		WORD cnt; | 
 | 		int isswapped = 0; | 
 |  | 
 | 		if(!fnd[i]) | 
 | 			continue; | 
 | 		for(j = 0; j < nfnt; j++) | 
 | 		{ | 
 | 			if(!fnt[j]) | 
 | 				continue; | 
 | 			if(fnt[j]->lan->id == fnd[i]->lan->id && fnt[j]->lan->sub == fnd[i]->lan->sub) | 
 | 			{ | 
 | 				lanfnt[nlanfnt] = fnt[j]; | 
 | 				nlanfnt++; | 
 | 				fnt[j] = NULL; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		cnt = *(WORD *)fnd[i]->res.fnd->data->data; | 
 | 		if(nlanfnt == cnt) | 
 | 			isswapped = 0; | 
 | 		else if(nlanfnt == BYTESWAP_WORD(cnt)) | 
 | 			isswapped = 1; | 
 | 		else | 
 | 			error("FONTDIR for language %d,%d has wrong count (%d, expected %d)", | 
 | 				fnd[i]->lan->id, fnd[i]->lan->sub, cnt, nlanfnt); | 
 | #ifdef WORDS_BIGENDIAN | 
 | 		if((byteorder == WRC_BO_LITTLE && !isswapped) || (byteorder != WRC_BO_LITTLE && isswapped)) | 
 | #else | 
 | 		if((byteorder == WRC_BO_BIG && !isswapped) || (byteorder != WRC_BO_BIG && isswapped)) | 
 | #endif | 
 | 		{ | 
 | 			internal_error(__FILE__, __LINE__, "User supplied FONTDIR needs byteswapping"); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* We now have fonts left where we need to make a fontdir resource */ | 
 | 	for(i = fntleft = 0; i < nfnt; i++) | 
 | 	{ | 
 | 		if(fnt[i]) | 
 | 			fntleft++; | 
 | 	} | 
 | 	while(fntleft) | 
 | 	{ | 
 | 		/* Get fonts of same language in lanfnt[] */ | 
 | 		for(i = nlanfnt = 0; i < nfnt; i++) | 
 | 		{ | 
 | 			if(fnt[i]) | 
 | 			{ | 
 | 				if(!nlanfnt) | 
 | 				{ | 
 | 			addlanfnt: | 
 | 					lanfnt[nlanfnt] = fnt[i]; | 
 | 					nlanfnt++; | 
 | 					fnt[i] = NULL; | 
 | 					fntleft--; | 
 | 				} | 
 | 				else if(fnt[i]->lan->id == lanfnt[0]->lan->id && fnt[i]->lan->sub == lanfnt[0]->lan->sub) | 
 | 					goto addlanfnt; | 
 | 			} | 
 | 		} | 
 | 		/* and build a fontdir */ | 
 | 		rsc = build_fontdir(lanfnt, nlanfnt); | 
 | 		if(rsc) | 
 | 		{ | 
 | 			if(lst) | 
 | 			{ | 
 | 				lst->next = rsc; | 
 | 				rsc->prev = lst; | 
 | 			} | 
 | 			lst = rsc; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	free(lanfnt); | 
 | clean: | 
 | 	if(fnt) | 
 | 		free(fnt); | 
 | 	if(fnd) | 
 | 		free(fnd); | 
 | 	free(str.str.cstr); | 
 | 	return lst; | 
 | } | 
 |  | 
 | /* | 
 |  * This gets invoked to determine whether the next resource | 
 |  * is to be of a standard-type (e.g. bitmaps etc.), or should | 
 |  * be a user-type resource. This function is required because | 
 |  * there is the _possibility_ of a lookahead token in the | 
 |  * parser, which is generated from the "expr" state in the | 
 |  * "nameid" parsing. | 
 |  * | 
 |  * The general resource format is: | 
 |  * <identifier> <type> <flags> <resourcebody> | 
 |  * | 
 |  * The <identifier> can either be tIDENT or "expr". The latter | 
 |  * will always generate a lookahead, which is the <type> of the | 
 |  * resource to parse. Otherwise, we need to get a new token from | 
 |  * the scanner to determine the next step. | 
 |  * | 
 |  * The problem arrises when <type> is numerical. This case should | 
 |  * map onto default resource-types and be parsed as such instead | 
 |  * of being mapped onto user-type resources. | 
 |  * | 
 |  * The trick lies in the fact that yacc (bison) doesn't care about | 
 |  * intermediate changes of the lookahead while reducing a rule. We | 
 |  * simply replace the lookahead with a token that will result in | 
 |  * a shift to the appropriate rule for the specific resource-type. | 
 |  */ | 
 | static int rsrcid_to_token(int lookahead) | 
 | { | 
 | 	int token; | 
 | 	const char *type = "?"; | 
 |  | 
 | 	/* Get a token if we don't have one yet */ | 
 | 	if(lookahead == YYEMPTY) | 
 | 		lookahead = YYLEX; | 
 |  | 
 | 	/* Only numbers are possibly interesting */ | 
 | 	switch(lookahead) | 
 | 	{ | 
 | 	case tNUMBER: | 
 | 	case tLNUMBER: | 
 | 		break; | 
 | 	default: | 
 | 		return lookahead; | 
 | 	} | 
 |  | 
 | 	token = lookahead; | 
 |  | 
 | 	switch(yylval.num) | 
 | 	{ | 
 | 	case WRC_RT_CURSOR: | 
 | 		type = "CURSOR"; | 
 | 		token = tCURSOR; | 
 | 		break; | 
 | 	case WRC_RT_ICON: | 
 | 		type = "ICON"; | 
 | 		token = tICON; | 
 | 		break; | 
 | 	case WRC_RT_BITMAP: | 
 | 		type = "BITMAP"; | 
 | 		token = tBITMAP; | 
 | 		break; | 
 | 	case WRC_RT_FONT: | 
 | 		type = "FONT"; | 
 | 		token = tFONT; | 
 | 		break; | 
 | 	case WRC_RT_FONTDIR: | 
 | 		type = "FONTDIR"; | 
 | 		token = tFONTDIR; | 
 | 		break; | 
 | 	case WRC_RT_RCDATA: | 
 | 		type = "RCDATA"; | 
 | 		token = tRCDATA; | 
 | 		break; | 
 | 	case WRC_RT_MESSAGETABLE: | 
 | 		type = "MESSAGETABLE"; | 
 | 		token = tMESSAGETABLE; | 
 | 		break; | 
 | 	case WRC_RT_DLGINIT: | 
 | 		type = "DLGINIT"; | 
 | 		token = tDLGINIT; | 
 | 		break; | 
 | 	case WRC_RT_ACCELERATOR: | 
 | 		type = "ACCELERATOR"; | 
 | 		token = tACCELERATORS; | 
 | 		break; | 
 | 	case WRC_RT_MENU: | 
 | 		type = "MENU"; | 
 | 		token = tMENU; | 
 | 		break; | 
 | 	case WRC_RT_DIALOG: | 
 | 		type = "DIALOG"; | 
 | 		token = tDIALOG; | 
 | 		break; | 
 | 	case WRC_RT_VERSION: | 
 | 		type = "VERSION"; | 
 | 		token = tVERSIONINFO; | 
 | 		break; | 
 | 	case WRC_RT_TOOLBAR: | 
 | 		type = "TOOLBAR"; | 
 | 		token = tTOOLBAR; | 
 | 		break; | 
 |  | 
 | 	case WRC_RT_STRING: | 
 | 		type = "STRINGTABLE"; | 
 | 		break; | 
 |  | 
 | 	case WRC_RT_ANICURSOR: | 
 | 	case WRC_RT_ANIICON: | 
 | 	case WRC_RT_GROUP_CURSOR: | 
 | 	case WRC_RT_GROUP_ICON: | 
 | 		yywarning("Usertype uses reserved type ID %d, which is auto-generated", yylval.num); | 
 | 		return lookahead; | 
 |  | 
 | 	case WRC_RT_DLGINCLUDE: | 
 | 	case WRC_RT_PLUGPLAY: | 
 | 	case WRC_RT_VXD: | 
 | 	case WRC_RT_HTML: | 
 | 		yywarning("Usertype uses reserved type ID %d, which is not supported by wrc yet", yylval.num); | 
 | 	default: | 
 | 		return lookahead; | 
 | 	} | 
 |  | 
 | 	return token; | 
 | } |