%{ /* * Copyright Martin von Loewis, 1994 * Copyright 1998 Bertho A. Stultiens (BS) * * 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* seperated * 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 #include #include #include #include #include #ifdef HAVE_ALLOCA_H #include #endif #include "wrc.h" #include "utils.h" #include "newstruc.h" #include "dumpres.h" #include "preproc.h" #include "parser.h" #include "winuser.h" #ifdef __BORLANDC__ #pragma warn -sig #endif DWORD andmask; /* Used to parse 'NOT NUMBER' expressions */ int indialog = 0; /* Signal flex that we're parsing a dialog */ int want_rscname = 0; /* Set when a resource's name is required */ 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 */ /* Set to the current options of the currently scanning stringtable */ static int *tagstt_memopt; static characts_t *tagstt_characts; static version_t *tagstt_version; /* Prototypes of here defined functions */ void split_cursors(raw_data_t *rd, cursor_group_t *curg, int *ncur); void split_icons(raw_data_t *rd, icon_group_t *icog, int *nico); int alloc_cursor_id(language_t *); int alloc_icon_id(language_t *); void ins_stt_entry(stt_entry_t *ste); int check_stt_entry(stringtable_t *tabs, stt_entry_t *ste); event_t *get_event_head(event_t *p); control_t *get_control_head(control_t *p); ver_value_t *get_ver_value_head(ver_value_t *p); ver_block_t *get_ver_block_head(ver_block_t *p); resource_t *get_resource_head(resource_t *p); menuex_item_t *get_itemex_head(menuex_item_t *p); menu_item_t *get_item_head(menu_item_t *p); raw_data_t *merge_raw_data_str(raw_data_t *r1, string_t *str); raw_data_t *merge_raw_data_int(raw_data_t *r1, int i); raw_data_t *merge_raw_data(raw_data_t *r1, raw_data_t *r2); raw_data_t *str2raw_data(string_t *str); raw_data_t *int2raw_data(int i); raw_data_t *load_file(string_t *name); itemex_opt_t *new_itemex_opt(int id, int type, int state, int helpid); event_t *add_string_event(string_t *key, int id, int flags, event_t *prev); event_t *add_event(int key, int id, int flags, event_t *prev); dialogex_t *dialogex_version(version_t *v, dialogex_t *dlg); dialogex_t *dialogex_characteristics(characts_t *c, dialogex_t *dlg); dialogex_t *dialogex_language(language_t *l, dialogex_t *dlg); dialogex_t *dialogex_menu(name_id_t *m, dialogex_t *dlg); dialogex_t *dialogex_class(name_id_t *n, dialogex_t *dlg); dialogex_t *dialogex_font(font_id_t *f, dialogex_t *dlg); dialogex_t *dialogex_caption(string_t *s, dialogex_t *dlg); dialogex_t *dialogex_exstyle(int st, dialogex_t *dlg); dialogex_t *dialogex_style(int st, dialogex_t *dlg); name_id_t *convert_ctlclass(name_id_t *cls); control_t *ins_ctrl(int type, int style, control_t *ctrl, control_t *prev); dialog_t *dialog_version(version_t *v, dialog_t *dlg); dialog_t *dialog_characteristics(characts_t *c, dialog_t *dlg); dialog_t *dialog_language(language_t *l, dialog_t *dlg); dialog_t *dialog_menu(name_id_t *m, dialog_t *dlg); dialog_t *dialog_class(name_id_t *n, dialog_t *dlg); dialog_t *dialog_font(font_id_t *f, dialog_t *dlg); dialog_t *dialog_caption(string_t *s, dialog_t *dlg); dialog_t *dialog_exstyle(int st, dialog_t *dlg); dialog_t *dialog_style(int st, dialog_t *dlg); resource_t *build_stt_resources(stringtable_t *stthead); stringtable_t *find_stringtable(lvc_t *lvc); toolbar_item_t *ins_tlbr_button(toolbar_item_t *prev, toolbar_item_t *idrec); toolbar_item_t *get_tlbr_buttons_head(toolbar_item_t *p, int *nitems); %} %union{ string_t *str; int num; int *iptr; resource_t *res; accelerator_t *acc; bitmap_t *bmp; cursor_t *cur; cursor_group_t *curg; dialog_t *dlg; dialogex_t *dlgex; font_t *fnt; icon_t *ico; icon_group_t *icog; 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; } %token tIF tIFDEF tIFNDEF tELSE tELIF tENDIF tDEFINED tNL %token tTYPEDEF tEXTERN %token NUMBER %token tSTRING IDENT FILENAME %token RAWDATA %token ACCELERATORS tBITMAP CURSOR DIALOG DIALOGEX MENU MENUEX MESSAGETABLE %token RCDATA VERSIONINFO STRINGTABLE FONT ICON %token AUTO3STATE AUTOCHECKBOX AUTORADIOBUTTON CHECKBOX DEFPUSHBUTTON %token PUSHBUTTON RADIOBUTTON STATE3 /* PUSHBOX */ %token GROUPBOX COMBOBOX LISTBOX SCROLLBAR %token CONTROL EDITTEXT %token RTEXT CTEXT LTEXT %token BLOCK VALUE %token SHIFT ALT ASCII VIRTKEY GRAYED CHECKED INACTIVE NOINVERT %token tPURE IMPURE DISCARDABLE LOADONCALL PRELOAD tFIXED MOVEABLE %token CLASS CAPTION CHARACTERISTICS EXSTYLE STYLE VERSION LANGUAGE %token FILEVERSION PRODUCTVERSION FILEFLAGSMASK FILEOS FILETYPE FILEFLAGS FILESUBTYPE %token MENUBARBREAK MENUBREAK MENUITEM POPUP SEPARATOR %token HELP %token tSTRING IDENT RAWDATA %token TOOLBAR BUTTON %token tBEGIN tEND %left LOGOR %left LOGAND %left '|' %left '^' %left '&' %left EQ NE %left '<' LTE '>' GTE %left '+' '-' %left '*' '/' %right '~' '!' NOT %type resource_file resource resources resource_definition %type stringtable strings %type font %type icon %type accelerators %type events %type bitmap %type cursor %type dialog dlg_attributes %type ctrls gen_ctrl lab_ctrl ctrl_desc iconinfo %type optional_style helpid %type dialogex dlgex_attribs %type exctrls gen_exctrl lab_exctrl exctrl_desc %type rcdata %type raw_data raw_elements opt_data %type versioninfo fix_version %type ver_words %type ver_blocks ver_block %type ver_values ver_value %type menu %type item_definitions menu_body %type menuex %type itemex_definitions menuex_body %type itemex_p_options itemex_options %type messagetable %type userres %type item_options %type nameid nameid_s ctlclass usertype %type acc_opt %type loadmemopts lamo lama %type opt_font opt_exfont %type opt_lvc %type opt_language %type opt_characts %type opt_version %type expr xpr dummy %type e_expr %type pp_expr pp_constant %type toolbar %type toolbar_items %% 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; /* Final statement before were done */ resource_top = get_resource_head($1); } ; /* Resources are put into a linked list */ resources : /* Empty */ { $$ = NULL; want_rscname = 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; } else if($1) { resource_t *tail = $1; while(tail->next) tail = tail->next; $$ = tail; } else $$ = NULL; want_rscname = 1; } | resources preprocessor { $$ = $1; want_rscname = 1; } | resources cjunk { $$ = $1; want_rscname = 1; } ; /* The buildin preprocessor */ preprocessor : tIF pp_expr tNL { pop_start(); push_if($2 ? *($2) : 0, 0, 0); if($2) free($2);} | tIFDEF IDENT tNL { pop_start(); push_if(pp_lookup($2->str.cstr) != NULL, 0, 0); } | tIFNDEF IDENT tNL { pop_start(); push_if(pp_lookup($2->str.cstr) == NULL, 0, 0); } | tELIF pp_expr tNL { pop_start(); push_if($2 ? *($2) : 0, pop_if(), 0); if($2) free($2); } | tELSE tNL { pop_start(); push_if(1, pop_if(), 0); } | tENDIF tNL { pop_if(); } ; pp_expr : pp_constant { $$ = $1; } | pp_expr LOGOR pp_expr { $$ = new_int($1 && $3 ? (*$1 || *$3) : 0); if($1) free($1); if($3) free($3); } | pp_expr LOGAND pp_expr { $$ = new_int($1 && $3 ? (*$1 && *$3) : 0); if($1) free($1); if($3) free($3); } | pp_expr '+' pp_expr { $$ = new_int($1 && $3 ? (*$1 + *$3) : 0); if($1) free($1); if($3) free($3); } | pp_expr '-' pp_expr { $$ = new_int($1 && $3 ? (*$1 - *$3) : 0); if($1) free($1); if($3) free($3); } | pp_expr '^' pp_expr { $$ = new_int($1 && $3 ? (*$1 ^ *$3) : 0); if($1) free($1); if($3) free($3); } | pp_expr EQ pp_expr { $$ = new_int($1 && $3 ? (*$1 == *$3) : 0); if($1) free($1); if($3) free($3); } | pp_expr NE pp_expr { $$ = new_int($1 && $3 ? (*$1 != *$3) : 0); if($1) free($1); if($3) free($3); } | pp_expr '<' pp_expr { $$ = new_int($1 && $3 ? (*$1 < *$3) : 0); if($1) free($1); if($3) free($3); } | pp_expr '>' pp_expr { $$ = new_int($1 && $3 ? (*$1 > *$3) : 0); if($1) free($1); if($3) free($3); } | pp_expr LTE pp_expr { $$ = new_int($1 && $3 ? (*$1 <= *$3) : 0); if($1) free($1); if($3) free($3); } | pp_expr GTE pp_expr { $$ = new_int($1 && $3 ? (*$1 >= *$3) : 0); if($1) free($1); if($3) free($3); } | '~' pp_expr { $$ = $2; if($2) *$2 = ~(*$2); } | '+' pp_expr { $$ = $2; } | '-' pp_expr { $$ = $2; if($2) *$2 = -(*$2); } | '!' pp_expr { $$ = $2; if($2) *$2 = !(*$2); } | '(' pp_expr ')' { $$ = $2; } ; pp_constant : NUMBER { $$ = new_int($1); } | IDENT { $$ = NULL; } | tDEFINED IDENT { $$ = new_int(pp_lookup($2->str.cstr) != NULL); } | tDEFINED '(' IDENT ')' { $$ = new_int(pp_lookup($3->str.cstr) != NULL); } ; /* C ignore stuff */ cjunk : tTYPEDEF { strip_til_semicolon(); } | tEXTERN { strip_til_semicolon(); } | IDENT IDENT { strip_til_semicolon(); } | IDENT '(' { strip_til_parenthesis(); } | IDENT '*' { strip_til_semicolon(); } ; /* Parse top level resource definitions etc. */ resource : nameid resource_definition { $$ = $2; if($$) { $$->name = $1; if($1->type == name_ord) { chat("Got %s (%d)",get_typename($2),$1->name.i_name); } else if($1->type == name_str) { chat("Got %s (%s)",get_typename($2),$1->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"); } | opt_language { if(!win32) yywarning("LANGUAGE not supported in 16-bit mode"); if(currentlanguage) free(currentlanguage); currentlanguage = $1; $$ = NULL; } ; /* * Get a valid name/id */ nameid : expr { $$ = new_name_id(); $$->type = name_ord; $$->name.i_name = $1; want_rscname = 0; } | IDENT { $$ = new_name_id(); $$->type = name_str; $$->name.s_name = $1; want_rscname = 0; } ; /* * Extra string recognition for CLASS statement in dialogs */ nameid_s: nameid { $$ = $1; } | tSTRING { $$ = new_name_id(); $$->type = name_str; $$->name.s_name = $1; want_rscname = 0; } ; /* 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, dup_language(currentlanguage)); } | cursor { resource_t *rsc; cursor_t *cur; $$ = rsc = new_resource(res_curg, $1, $1->memopt, dup_language(currentlanguage)); for(cur = $1->cursorlist; cur; cur = cur->next) { rsc->prev = new_resource(res_cur, cur, $1->memopt, dup_language(currentlanguage)); rsc->prev->next = rsc; rsc = rsc->prev; rsc->name = new_name_id(); rsc->name->type = name_ord; rsc->name->name.i_name = cur->id; } } | 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; } | font { $$=new_resource(res_fnt, $1, $1->memopt, dup_language(currentlanguage)); } | icon { resource_t *rsc; icon_t *ico; $$ = rsc = new_resource(res_icog, $1, $1->memopt, dup_language(currentlanguage)); for(ico = $1->iconlist; ico; ico = ico->next) { rsc->prev = new_resource(res_ico, ico, $1->memopt, dup_language(currentlanguage)); rsc->prev->next = rsc; rsc = rsc->prev; rsc->name = new_name_id(); rsc->name->type = name_ord; rsc->name->name.i_name = ico->id; } } | 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, dup_language(currentlanguage)); } | rcdata { $$ = new_resource(res_rdt, $1, $1->memopt, $1->lvc.language); } | toolbar { $$ = new_resource(res_toolbar, $1, $1->memopt, $1->lvc.language); } | userres { $$ = new_resource(res_usr, $1, $1->memopt, dup_language(currentlanguage)); } | versioninfo { $$ = new_resource(res_ver, $1, WRC_MO_MOVEABLE | WRC_MO_DISCARDABLE, dup_language(currentlanguage)); } ; /* ------------------------------ Bitmap ------------------------------ */ bitmap : tBITMAP loadmemopts FILENAME { $$ = new_bitmap(load_file($3), $2); } | tBITMAP loadmemopts raw_data { $$ = new_bitmap($3, $2); } ; /* ------------------------------ Cursor ------------------------------ */ cursor : CURSOR loadmemopts FILENAME { $$ = new_cursor_group(load_file($3), $2); } | CURSOR loadmemopts raw_data { $$ = new_cursor_group($3, $2); } ; /* ------------------------------ Font ------------------------------ */ /* FIXME: Should we allow raw_data here? */ font : FONT loadmemopts FILENAME { $$ = new_font(load_file($3), $2); } ; /* ------------------------------ Icon ------------------------------ */ icon : ICON loadmemopts FILENAME { $$ = new_icon_group(load_file($3), $2); } | ICON loadmemopts raw_data { $$ = new_icon_group($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 : MESSAGETABLE FILENAME { if(!win32) yywarning("MESSAGETABLE not supported in 16-bit mode"); $$ = new_messagetable(load_file($2)); } ; /* ------------------------------ RCData ------------------------------ */ rcdata : RCDATA loadmemopts opt_lvc raw_data { $$ = new_rcdata($4, $2); if($3) { $$->lvc = *($3); free($3); } if(!$$->lvc.language) $$->lvc.language = dup_language(currentlanguage); } ; /* ------------------------------ UserType ------------------------------ */ userres : usertype loadmemopts FILENAME { $$ = new_user($1, load_file($3), $2); } | usertype loadmemopts raw_data { $$ = new_user($1, $3, $2); } ; /* NOTE: This here is an exception where I do not allow an expression. * Reason for this is that it is not possible to set the 'yywf' condition * for flex if loadmemopts is empty. Reading an expression requires a * lookahead to determine its end. In this case here, that would mean that * the filename has been read as IDENT or tSTRING, which is incorrect. * Note also that IDENT cannot be used as a file-name because it is lacking * the '.'. */ /* I also allow string identifiers as classtypes. Not MS implemented, but * seems to be reasonable to implement. */ /* Allowing anything else than NUMBER makes it very hard to get rid of * prototypes. So, I remove IDENT to be able to get prototypes out of the * world. */ usertype: NUMBER { $$ = new_name_id(); $$->type = name_ord; $$->name.i_name = $1; set_yywf(); } /* | IDENT { $$ = new_name_id(); $$->type = name_str; $$->name.s_name = $1; set_yywf(); } */ | tSTRING { $$ = new_name_id(); $$->type = name_str; $$->name.s_name = $1; set_yywf(); } ; /* ------------------------------ Accelerator ------------------------------ */ accelerators : ACCELERATORS 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); } ; acc_opt : /* Empty */ { $$=0; } | acc_opt ',' NOINVERT { $$=$1 | WRC_AF_NOINVERT; } | acc_opt ',' SHIFT { $$=$1 | WRC_AF_SHIFT; } | acc_opt ',' CONTROL { $$=$1 | WRC_AF_CONTROL; } | acc_opt ',' ALT { $$=$1 | WRC_AF_ALT; } | acc_opt ',' ASCII { $$=$1 | WRC_AF_ASCII; } | acc_opt ',' VIRTKEY { $$=$1 | WRC_AF_VIRTKEY; } ; /* ------------------------------ Dialog ------------------------------ */ /* FIXME: Support EXSTYLE in the dialog line itself */ dialog : DIALOG 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 = WS_POPUP; $$->gotstyle = TRUE; } if($$->title) $$->style |= WS_CAPTION; if($$->font) $$->style |= DS_SETFONT; indialog = FALSE; if(!$$->lvc.language) $$->lvc.language = dup_language(currentlanguage); } ; dlg_attributes : /* Empty */ { $$=new_dialog(); } | dlg_attributes STYLE expr { $$=dialog_style($3,$1); } | dlg_attributes EXSTYLE expr { $$=dialog_exstyle($3,$1); } | dlg_attributes CAPTION tSTRING { $$=dialog_caption($3,$1); } | dlg_attributes opt_font { $$=dialog_font($2,$1); } | dlg_attributes CLASS nameid_s { $$=dialog_class($3,$1); } | dlg_attributes MENU 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 CONTROL gen_ctrl { $$=ins_ctrl(-1, 0, $3, $1); } | ctrls EDITTEXT ctrl_desc { $$=ins_ctrl(CT_EDIT, 0, $3, $1); } | ctrls LISTBOX ctrl_desc { $$=ins_ctrl(CT_LISTBOX, 0, $3, $1); } | ctrls COMBOBOX ctrl_desc { $$=ins_ctrl(CT_COMBOBOX, 0, $3, $1); } | ctrls SCROLLBAR ctrl_desc { $$=ins_ctrl(CT_SCROLLBAR, 0, $3, $1); } | ctrls CHECKBOX lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_CHECKBOX, $3, $1); } | ctrls DEFPUSHBUTTON lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_DEFPUSHBUTTON, $3, $1); } | ctrls GROUPBOX lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_GROUPBOX, $3, $1);} | ctrls PUSHBUTTON lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_PUSHBUTTON, $3, $1); } /* | ctrls PUSHBOX lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_PUSHBOX, $3, $1); } */ | ctrls RADIOBUTTON lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_RADIOBUTTON, $3, $1); } | ctrls AUTO3STATE lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTO3STATE, $3, $1); } | ctrls STATE3 lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_3STATE, $3, $1); } | ctrls AUTOCHECKBOX lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTOCHECKBOX, $3, $1); } | ctrls AUTORADIOBUTTON lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTORADIOBUTTON, $3, $1); } | ctrls LTEXT lab_ctrl { $$=ins_ctrl(CT_STATIC, SS_LEFT, $3, $1); } | ctrls CTEXT lab_ctrl { $$=ins_ctrl(CT_STATIC, SS_CENTER, $3, $1); } | ctrls RTEXT lab_ctrl { $$=ins_ctrl(CT_STATIC, SS_RIGHT, $3, $1); } /* special treatment for icons, as the extent is optional */ | ctrls ICON nameid_s ',' expr ',' expr ',' expr iconinfo { if($3->type == name_str) { $10->title = $3->name.s_name; } else { $10->title = NULL; } $10->id = $5; $10->x = $7; $10->y = $9; $$ = ins_ctrl(CT_STATIC, SS_ICON, $10, $1); } ; lab_ctrl : tSTRING ',' expr ',' expr ',' expr ',' expr ',' expr optional_style { $$=new_control(); $$->title = $1; $$->id = $3; $$->x = $5; $$->y = $7; $$->width = $9; $$->height = $11; if($12) { $$->style = *($12); $$->gotstyle = TRUE; free($12); } } ; 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; free($10); } } ; iconinfo: /* Empty */ { $$ = new_control(); } | ',' expr ',' expr { $$ = new_control(); $$->width = $2; $$->height = $4; } | ',' expr ',' expr ',' expr { $$ = new_control(); $$->width = $2; $$->height = $4; $$->style = $6; $$->gotstyle = TRUE; } | ',' expr ',' expr ',' expr ',' expr { $$ = new_control(); $$->width = $2; $$->height = $4; $$->style = $6; $$->gotstyle = TRUE; $$->exstyle = $8; $$->gotexstyle = TRUE; } ; gen_ctrl: tSTRING ',' expr ',' ctlclass ',' expr ',' expr ',' 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; $$->exstyle = $17; $$->gotexstyle = TRUE; } | tSTRING ',' expr ',' ctlclass ',' expr ',' 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 : FONT expr ',' tSTRING { $$ = new_font_id($2, $4, 0, 0); } ; optional_style /* Abbused once to get optional ExStyle */ : /* Empty */ { $$ = NULL; } | ',' expr { $$ = new_int($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: DIALOGEX 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; if(!$$->gotstyle) { $$->style = WS_POPUP; $$->gotstyle = TRUE; } if($$->title) $$->style |= WS_CAPTION; if($$->font) $$->style |= DS_SETFONT; indialog = FALSE; if(!$$->lvc.language) $$->lvc.language = dup_language(currentlanguage); } ; dlgex_attribs : /* Empty */ { $$=new_dialogex(); } | dlgex_attribs STYLE expr { $$=dialogex_style($3,$1); } | dlgex_attribs EXSTYLE expr { $$=dialogex_exstyle($3,$1); } | dlgex_attribs CAPTION tSTRING { $$=dialogex_caption($3,$1); } | dlgex_attribs opt_exfont { $$=dialogex_font($2,$1); } | dlgex_attribs CLASS nameid_s { $$=dialogex_class($3,$1); } | dlgex_attribs MENU 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 CONTROL gen_exctrl { $$=ins_ctrl(-1, 0, $3, $1); } | exctrls EDITTEXT exctrl_desc { $$=ins_ctrl(CT_EDIT, 0, $3, $1); } | exctrls LISTBOX exctrl_desc { $$=ins_ctrl(CT_LISTBOX, 0, $3, $1); } | exctrls COMBOBOX exctrl_desc { $$=ins_ctrl(CT_COMBOBOX, 0, $3, $1); } | exctrls SCROLLBAR exctrl_desc { $$=ins_ctrl(CT_SCROLLBAR, 0, $3, $1); } | exctrls CHECKBOX lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_CHECKBOX, $3, $1); } | exctrls DEFPUSHBUTTON lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_DEFPUSHBUTTON, $3, $1); } | exctrls GROUPBOX lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_GROUPBOX, $3, $1);} | exctrls PUSHBUTTON lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_PUSHBUTTON, $3, $1); } /* | exctrls PUSHBOX lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_PUSHBOX, $3, $1); } */ | exctrls RADIOBUTTON lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_RADIOBUTTON, $3, $1); } | exctrls AUTO3STATE lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTO3STATE, $3, $1); } | exctrls STATE3 lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_3STATE, $3, $1); } | exctrls AUTOCHECKBOX lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTOCHECKBOX, $3, $1); } | exctrls AUTORADIOBUTTON lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTORADIOBUTTON, $3, $1); } | exctrls LTEXT lab_exctrl { $$=ins_ctrl(CT_STATIC, SS_LEFT, $3, $1); } | exctrls CTEXT lab_exctrl { $$=ins_ctrl(CT_STATIC, SS_CENTER, $3, $1); } | exctrls RTEXT lab_exctrl { $$=ins_ctrl(CT_STATIC, SS_RIGHT, $3, $1); } /* special treatment for icons, as the extent is optional */ | exctrls ICON tSTRING ',' 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 : tSTRING ',' expr ',' ctlclass ',' expr ',' expr ',' expr ',' expr ',' expr ',' e_expr 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; free($17); } if($18) { $$->helpid = *($18); $$->gothelpid = TRUE; free($18); } $$->extra = $19; } | tSTRING ',' expr ',' ctlclass ',' expr ',' 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 ',' expr ',' expr ',' expr ',' expr ',' expr optional_style opt_data { $$=new_control(); $$->title = $1; $$->id = $3; $$->x = $5; $$->y = $7; $$->width = $9; $$->height = $11; if($12) { $$->style = *($12); $$->gotstyle = TRUE; free($12); } $$->extra = $13; } ; exctrl_desc : expr ',' expr ',' expr ',' expr ',' expr optional_style opt_data { $$ = new_control(); $$->id = $1; $$->x = $3; $$->y = $5; $$->width = $7; $$->height = $9; if($10) { $$->style = *($10); $$->gotstyle = TRUE; free($10); } $$->extra = $11; } ; opt_data: /* Empty */ { $$ = NULL; } | raw_data { $$ = $1; } ; helpid : /* Empty */ { $$ = NULL; } | ',' expr { $$ = new_int($2); } ; opt_exfont : FONT expr ',' tSTRING ',' expr ',' expr { $$ = new_font_id($2, $4, $6, $8); } ; /* ------------------------------ Menu ------------------------------ */ menu : MENU 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 MENUITEM tSTRING ',' expr item_options { $$=new_menu_item(); $$->prev = $1; if($1) $1->next = $$; $$->id = $5; $$->state = $6; $$->name = $3; } | item_definitions MENUITEM SEPARATOR { $$=new_menu_item(); $$->prev = $1; if($1) $1->next = $$; } | item_definitions POPUP 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; } | ',' CHECKED item_options { $$ = $3 | MF_CHECKED; } | ',' GRAYED item_options { $$ = $3 | MF_GRAYED; } | ',' HELP item_options { $$ = $3 | MF_HELP; } | ',' INACTIVE item_options { $$ = $3 | MF_DISABLED; } | ',' MENUBARBREAK item_options { $$ = $3 | MF_MENUBARBREAK; } | ',' MENUBREAK item_options { $$ = $3 | MF_MENUBREAK; } ; /* ------------------------------ MenuEx ------------------------------ */ menuex : MENUEX 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 MENUITEM 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 MENUITEM SEPARATOR { $$ = new_menuex_item(); $$->prev = $1; if($1) $1->next = $$; } | itemex_definitions POPUP 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: STRINGTABLE 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); /* 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(!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 : VERSIONINFO fix_version tBEGIN ver_blocks tEND { $$ = $2; $2->blocks = get_ver_block_head($4); } ; fix_version : /* Empty */ { $$ = new_versioninfo(); } | fix_version FILEVERSION 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 PRODUCTVERSION 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 FILEFLAGS expr { if($1->gotit.ff) yyerror("FILEFLAGS already defined"); $$ = $1; $$->fileflags = $3; $$->gotit.ff = 1; } | fix_version FILEFLAGSMASK expr { if($1->gotit.ffm) yyerror("FILEFLAGSMASK already defined"); $$ = $1; $$->fileflagsmask = $3; $$->gotit.ffm = 1; } | fix_version FILEOS expr { if($1->gotit.fo) yyerror("FILEOS already defined"); $$ = $1; $$->fileos = $3; $$->gotit.fo = 1; } | fix_version FILETYPE expr { if($1->gotit.ft) yyerror("FILETYPE already defined"); $$ = $1; $$->filetype = $3; $$->gotit.ft = 1; } | fix_version FILESUBTYPE 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 : BLOCK 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; } | VALUE tSTRING ',' tSTRING { $$ = new_ver_value(); $$->type = val_str; $$->key = $2; $$->value.str = $4; } | VALUE 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: TOOLBAR 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 BUTTON expr { toolbar_item_t *idrec = new_toolbar_item(); idrec->id = $3; $$ = ins_tlbr_button($1, idrec); } | toolbar_items SEPARATOR { 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 : PRELOAD { $$ = new_int(WRC_MO_PRELOAD); } | MOVEABLE { $$ = new_int(WRC_MO_MOVEABLE); } | DISCARDABLE { $$ = new_int(WRC_MO_DISCARDABLE); } | tPURE { $$ = new_int(WRC_MO_PURE); } ; lama : LOADONCALL { $$ = new_int(~WRC_MO_PRELOAD); } | tFIXED { $$ = new_int(~WRC_MO_MOVEABLE); } | IMPURE { $$ = 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; } ; opt_language : LANGUAGE expr ',' expr { $$ = new_language($2, $4); } ; opt_characts : CHARACTERISTICS expr { $$ = new_characts($2); } ; opt_version : VERSION expr { $$ = new_version($2); } ; /* ------------------------------ Raw data handking ------------------------------ */ raw_data: tBEGIN raw_elements tEND { $$ = $2; } ; raw_elements : RAWDATA { $$ = $1; } | NUMBER { $$ = int2raw_data($1); } | tSTRING { $$ = str2raw_data($1); } | raw_elements opt_comma RAWDATA { $$ = merge_raw_data($1, $3); free($3->data); free($3); } | raw_elements opt_comma NUMBER { $$ = merge_raw_data_int($1, $3); } | raw_elements opt_comma tSTRING { $$ = merge_raw_data_str($1, $3); } ; /* ------------------------------ 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); } ; expr : dummy xpr { $$ = ($2) & andmask; } ; dummy : /* Empty */ { $$ = 0; andmask = -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 { $$ = ~($2); } | '-' xpr { $$ = -($2); } /* FIXME: shift/reduce conflict */ /* | '+' xpr { $$ = $2; } */ | '(' xpr ')' { $$ = $2; } | NUMBER { $$ = $1; want_rscname = 0; } | NOT NUMBER { $$ = 0; andmask &= ~($2); } ; %% /* Dialog specific functions */ dialog_t *dialog_style(int st, dialog_t *dlg) { DWORD s = 0; assert(dlg != NULL); if(dlg->gotstyle) { yywarning("Style already defined, or-ing together"); s = dlg->style; } dlg->style = st | s; dlg->gotstyle = TRUE; return dlg; } dialog_t *dialog_exstyle(int st, dialog_t *dlg) { DWORD s = 0; assert(dlg != NULL); if(dlg->gotexstyle) { yywarning("ExStyle already defined, or-ing together"); s = dlg->style; } dlg->exstyle = st | s; dlg->gotexstyle = TRUE; return dlg; } 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; } 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; } 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; } 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; } 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; } 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; } 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 */ control_t *ins_ctrl(int type, int style, control_t *ctrl, control_t *prev) { 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; } /* Hm... this seems to be jammed in at all time... */ ctrl->style |= WS_CHILD | WS_VISIBLE; switch(type) { case CT_BUTTON: ctrl->style |= style; if(style != BS_GROUPBOX && style != BS_RADIOBUTTON) ctrl->style |= WS_TABSTOP; break; case CT_EDIT: ctrl->style |= WS_TABSTOP | WS_BORDER; break; case CT_LISTBOX: ctrl->style |= LBS_NOTIFY | WS_BORDER; break; case CT_COMBOBOX: ctrl->style |= CBS_SIMPLE; break; case CT_STATIC: ctrl->style |= style; if(style == SS_CENTER || style == SS_LEFT || style == SS_RIGHT) ctrl->style |= WS_GROUP; break; } if(!ctrl->gotstyle) /* Handle default style setting */ { switch(type) { case CT_EDIT: ctrl->style |= ES_LEFT; break; case CT_LISTBOX: ctrl->style |= LBS_NOTIFY; break; case CT_COMBOBOX: ctrl->style |= CBS_SIMPLE | WS_TABSTOP; break; case CT_SCROLLBAR: ctrl->style |= SBS_HORZ; break; case CT_BUTTON: switch(style) { case BS_CHECKBOX: case BS_DEFPUSHBUTTON: case BS_PUSHBUTTON: case BS_GROUPBOX: /* case BS_PUSHBOX: */ case BS_AUTORADIOBUTTON: case BS_AUTO3STATE: case BS_3STATE: case BS_AUTOCHECKBOX: ctrl->style |= WS_TABSTOP; break; default: yywarning("Unknown default button control-style 0x%08x", style); case BS_RADIOBUTTON: break; } break; case CT_STATIC: switch(style) { case SS_LEFT: case SS_RIGHT: case SS_CENTER: ctrl->style |= WS_GROUP; break; case SS_ICON: /* Special case */ break; default: yywarning("Unknown default static control-style 0x%08x", 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 && style == SS_ICON) ctrl->style |= SS_ICON; ctrl->gotstyle = TRUE; byebye: return ctrl; } name_id_t *convert_ctlclass(name_id_t *cls) { char *cc; 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 */ dialogex_t *dialogex_style(int st, dialogex_t *dlg) { DWORD s = 0; assert(dlg != NULL); if(dlg->gotstyle) { yywarning("Style already defined, or-ing together"); s = dlg->style; } dlg->style = st | s; dlg->gotstyle = TRUE; return dlg; } dialogex_t *dialogex_exstyle(int st, dialogex_t *dlg) { DWORD s = 0; assert(dlg != NULL); if(dlg->gotexstyle) { yywarning("ExStyle already defined, or-ing together"); s = dlg->exstyle; } dlg->exstyle = st | s; dlg->gotexstyle = TRUE; return dlg; } 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; } 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; } 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; } 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; } 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; } 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; } 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 */ 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; } 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]) && !isdigit(key->str.cstr[0]))) 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 */ 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 */ raw_data_t *load_file(string_t *name) { FILE *fp; raw_data_t *rd; if(name->type != str_char) yyerror("Filename must be ASCII string"); fp = open_include(name->str.cstr, 1); if(!fp) yyerror("Cannot open file %s", name->str.cstr); 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); HEAPCHECK(); return rd; } raw_data_t *int2raw_data(int i) { raw_data_t *rd; rd = new_raw_data(); rd->size = sizeof(short); rd->data = (char *)xmalloc(rd->size); *(short *)(rd->data) = (short)i; return rd; } 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); memcpy(rd->data, str->str.cstr, rd->size); return rd; } 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; } 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; } 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 */ menu_item_t *get_item_head(menu_item_t *p) { if(!p) return NULL; while(p->prev) p = p->prev; return p; } menuex_item_t *get_itemex_head(menuex_item_t *p) { if(!p) return NULL; while(p->prev) p = p->prev; return p; } resource_t *get_resource_head(resource_t *p) { if(!p) return NULL; while(p->prev) p = p->prev; return p; } ver_block_t *get_ver_block_head(ver_block_t *p) { if(!p) return NULL; while(p->prev) p = p->prev; return p; } ver_value_t *get_ver_value_head(ver_value_t *p) { if(!p) return NULL; while(p->prev) p = p->prev; return p; } control_t *get_control_head(control_t *p) { if(!p) return NULL; while(p->prev) p = p->prev; return p; } 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 */ 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->id == lvc->language->id) { /* 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)) int sort_stt_entry(const void *e1, const void *e2) { return STE(e1)->id - STE(e2)->id; } #undef STE 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; } /* Cursor and icon splitter functions */ typedef struct { language_t lan; int id; } id_alloc_t; static int get_new_id(id_alloc_t **list, int *n, language_t *lan) { int i; assert(lan != NULL); assert(list != NULL); assert(n != NULL); if(!*list) { *list = (id_alloc_t *)xmalloc(sizeof(id_alloc_t)); *n = 1; (*list)[0].lan = *lan; (*list)[0].id = 1; return 1; } for(i = 0; i < *n; i++) { if((*list)[i].lan.id == lan->id && (*list)[i].lan.sub == lan->sub) return ++((*list)[i].id); } *list = (id_alloc_t *)xrealloc(*list, sizeof(id_alloc_t) * (*n+1)); (*list)[*n].lan = *lan; (*list)[*n].id = 1; *n += 1; return 1; } int alloc_icon_id(language_t *lan) { static id_alloc_t *idlist = NULL; static int nid = 0; return get_new_id(&idlist, &nid, lan); } int alloc_cursor_id(language_t *lan) { static id_alloc_t *idlist = NULL; static int nid = 0; return get_new_id(&idlist, &nid, lan); } #define BPTR(base) ((char *)(rd->data + (base))) #define WPTR(base) ((WORD *)(rd->data + (base))) #define DPTR(base) ((DWORD *)(rd->data + (base))) void split_icons(raw_data_t *rd, icon_group_t *icog, int *nico) { int cnt; int i; icon_dir_entry_t *ide; icon_t *ico; icon_t *list = NULL; /* FIXME: Distinguish between normal and animated icons (RIFF format) */ if(WPTR(0)[1] != 1) yyerror("Icon resource data has invalid type id %d", WPTR(0)[1]); cnt = WPTR(0)[2]; ide = (icon_dir_entry_t *)&(WPTR(0)[3]); for(i = 0; i < cnt; i++) { ico = new_icon(); ico->id = alloc_icon_id(icog->lvc.language); ico->lvc.language = dup_language(icog->lvc.language); if(ide[i].offset > rd->size || ide[i].offset + ide[i].ressize > rd->size) yyerror("Icon resource data corrupt"); ico->width = ide[i].width; ico->height = ide[i].height; ico->nclr = ide[i].nclr; ico->planes = ide[i].planes; ico->bits = ide[i].bits; if(!ico->planes) { /* Argh! They did not fill out the resdir structure */ ico->planes = ((BITMAPINFOHEADER *)BPTR(ide[i].offset))->biPlanes; } if(!ico->bits) { /* Argh! They did not fill out the resdir structure */ ico->bits = ((BITMAPINFOHEADER *)BPTR(ide[i].offset))->biBitCount; } ico->data = new_raw_data(); copy_raw_data(ico->data, rd, ide[i].offset, ide[i].ressize); if(!list) { list = ico; } else { ico->next = list; list->prev = ico; list = ico; } } icog->iconlist = list; *nico = cnt; } void split_cursors(raw_data_t *rd, cursor_group_t *curg, int *ncur) { int cnt; int i; cursor_dir_entry_t *cde; cursor_t *cur; cursor_t *list = NULL; /* FIXME: Distinguish between normal and animated cursors (RIFF format)*/ if(WPTR(0)[1] != 2) yyerror("Cursor resource data has invalid type id %d", WPTR(0)[1]); cnt = WPTR(0)[2]; cde = (cursor_dir_entry_t *)&(WPTR(0)[3]); for(i = 0; i < cnt; i++) { cur = new_cursor(); cur->id = alloc_cursor_id(curg->lvc.language); cur->lvc.language = dup_language(curg->lvc.language); if(cde[i].offset > rd->size || cde[i].offset + cde[i].ressize > rd->size) yyerror("Cursor resource data corrupt"); cur->width = cde[i].width; cur->height = cde[i].height; cur->nclr = cde[i].nclr; /* The next two are to support color cursors */ cur->planes = ((BITMAPINFOHEADER *)BPTR(cde[i].offset))->biPlanes; cur->bits = ((BITMAPINFOHEADER *)BPTR(cde[i].offset))->biBitCount; if(!win32 && (cur->planes != 1 || cur->bits != 1)) yywarning("Win16 cursor contains colors"); cur->xhot = cde[i].xhot; cur->yhot = cde[i].yhot; cur->data = new_raw_data(); copy_raw_data(cur->data, rd, cde[i].offset, cde[i].ressize); if(!list) { list = cur; } else { cur->next = list; list->prev = cur; list = cur; } } curg->cursorlist = list; *ncur = cnt; } #undef BPTR #undef WPTR #undef DPTR toolbar_item_t *ins_tlbr_button(toolbar_item_t *prev, toolbar_item_t *idrec) { idrec->prev = prev; if(prev) prev->next = idrec; return idrec; } 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; }