|  | %{ /* rcparse.y -- parser for Windows rc files | 
|  | Copyright (C) 1997-2022 Free Software Foundation, Inc. | 
|  | Written by Ian Lance Taylor, Cygnus Support. | 
|  | Extended by Kai Tietz, Onevision. | 
|  |  | 
|  | This file is part of GNU Binutils. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program 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 General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program; if not, write to the Free Software | 
|  | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA | 
|  | 02110-1301, USA.  */ | 
|  |  | 
|  |  | 
|  | /* This is a parser for Windows rc files.  It is based on the parser | 
|  | by Gunther Ebert <gunther.ebert@ixos-leipzig.de>.  */ | 
|  |  | 
|  | #include "sysdep.h" | 
|  | #include "bfd.h" | 
|  | #include "bucomm.h" | 
|  | #include "libiberty.h" | 
|  | #include "windres.h" | 
|  | #include "safe-ctype.h" | 
|  |  | 
|  | /* The current language.  */ | 
|  |  | 
|  | static unsigned short language; | 
|  |  | 
|  | /* The resource information during a sub statement.  */ | 
|  |  | 
|  | static rc_res_res_info sub_res_info; | 
|  |  | 
|  | /* Dialog information.  This is built by the nonterminals styles and | 
|  | controls.  */ | 
|  |  | 
|  | static rc_dialog dialog; | 
|  |  | 
|  | /* This is used when building a style.  It is modified by the | 
|  | nonterminal styleexpr.  */ | 
|  |  | 
|  | static unsigned long style; | 
|  |  | 
|  | /* These are used when building a control.  They are set before using | 
|  | control_params.  */ | 
|  |  | 
|  | static rc_uint_type base_style; | 
|  | static rc_uint_type default_style; | 
|  | static rc_res_id class; | 
|  | static rc_res_id res_text_field; | 
|  | static unichar null_unichar; | 
|  |  | 
|  | /* This is used for COMBOBOX, LISTBOX and EDITTEXT which | 
|  | do not allow resource 'text' field in control definition. */ | 
|  | static const rc_res_id res_null_text = { 1, {{0, &null_unichar}}}; | 
|  |  | 
|  | %} | 
|  |  | 
|  | %union | 
|  | { | 
|  | rc_accelerator acc; | 
|  | rc_accelerator *pacc; | 
|  | rc_dialog_control *dialog_control; | 
|  | rc_menuitem *menuitem; | 
|  | struct | 
|  | { | 
|  | rc_rcdata_item *first; | 
|  | rc_rcdata_item *last; | 
|  | } rcdata; | 
|  | rc_rcdata_item *rcdata_item; | 
|  | rc_fixed_versioninfo *fixver; | 
|  | rc_ver_info *verinfo; | 
|  | rc_ver_stringtable *verstringtable; | 
|  | rc_ver_stringinfo *verstring; | 
|  | rc_ver_varinfo *vervar; | 
|  | rc_toolbar_item *toobar_item; | 
|  | rc_res_id id; | 
|  | rc_res_res_info res_info; | 
|  | struct | 
|  | { | 
|  | rc_uint_type on; | 
|  | rc_uint_type off; | 
|  | } memflags; | 
|  | struct | 
|  | { | 
|  | rc_uint_type val; | 
|  | /* Nonzero if this number was explicitly specified as long.  */ | 
|  | int dword; | 
|  | } i; | 
|  | rc_uint_type il; | 
|  | rc_uint_type is; | 
|  | const char *s; | 
|  | struct | 
|  | { | 
|  | rc_uint_type length; | 
|  | const char *s; | 
|  | } ss; | 
|  | unichar *uni; | 
|  | struct | 
|  | { | 
|  | rc_uint_type length; | 
|  | const unichar *s; | 
|  | } suni; | 
|  | }; | 
|  |  | 
|  | %token BEG END | 
|  | %token ACCELERATORS VIRTKEY ASCII NOINVERT SHIFT CONTROL ALT | 
|  | %token BITMAP | 
|  | %token CURSOR | 
|  | %token DIALOG DIALOGEX EXSTYLE CAPTION CLASS STYLE | 
|  | %token AUTO3STATE AUTOCHECKBOX AUTORADIOBUTTON CHECKBOX COMBOBOX CTEXT | 
|  | %token DEFPUSHBUTTON EDITTEXT GROUPBOX LISTBOX LTEXT PUSHBOX PUSHBUTTON | 
|  | %token RADIOBUTTON RTEXT SCROLLBAR STATE3 USERBUTTON | 
|  | %token BEDIT HEDIT IEDIT | 
|  | %token FONT | 
|  | %token ICON | 
|  | %token ANICURSOR ANIICON DLGINCLUDE DLGINIT FONTDIR HTML MANIFEST PLUGPLAY VXD TOOLBAR BUTTON | 
|  | %token LANGUAGE CHARACTERISTICS VERSIONK | 
|  | %token MENU MENUEX MENUITEM SEPARATOR POPUP CHECKED GRAYED HELP INACTIVE OWNERDRAW | 
|  | %token MENUBARBREAK MENUBREAK | 
|  | %token MESSAGETABLE | 
|  | %token RCDATA | 
|  | %token STRINGTABLE | 
|  | %token VERSIONINFO FILEVERSION PRODUCTVERSION FILEFLAGSMASK FILEFLAGS | 
|  | %token FILEOS FILETYPE FILESUBTYPE BLOCKSTRINGFILEINFO BLOCKVARFILEINFO | 
|  | %token VALUE | 
|  | %token <s> BLOCK | 
|  | %token MOVEABLE FIXED PURE IMPURE PRELOAD LOADONCALL DISCARDABLE | 
|  | %token NOT | 
|  | %token <uni> QUOTEDUNISTRING | 
|  | %token <s> QUOTEDSTRING STRING | 
|  | %token <i> NUMBER | 
|  | %token <suni> SIZEDUNISTRING | 
|  | %token <ss> SIZEDSTRING | 
|  | %token IGNORED_TOKEN | 
|  |  | 
|  | %type <pacc> acc_entries | 
|  | %type <acc> acc_entry acc_event | 
|  | %type <dialog_control> control control_params | 
|  | %type <menuitem> menuitems menuitem menuexitems menuexitem | 
|  | %type <rcdata> optrcdata_data optrcdata_data_int rcdata_data | 
|  | %type <rcdata_item> opt_control_data | 
|  | %type <fixver> fixedverinfo | 
|  | %type <verinfo> verblocks | 
|  | %type <verstringtable> verstringtables | 
|  | %type <verstring> vervals | 
|  | %type <vervar> vertrans | 
|  | %type <toobar_item> toolbar_data | 
|  | %type <res_info> suboptions memflags_move_discard memflags_move | 
|  | %type <memflags> memflag | 
|  | %type <id> id rcdata_id optresidc resref resid cresid | 
|  | %type <il> exstyle parennumber | 
|  | %type <il> numexpr posnumexpr cnumexpr optcnumexpr cposnumexpr | 
|  | %type <is> acc_options acc_option menuitem_flags menuitem_flag | 
|  | %type <s> file_name | 
|  | %type <uni> res_unicode_string resname res_unicode_string_concat | 
|  | %type <ss> sizedstring | 
|  | %type <suni> sizedunistring res_unicode_sizedstring res_unicode_sizedstring_concat | 
|  | %type <i> sizednumexpr sizedposnumexpr | 
|  |  | 
|  | %left '|' | 
|  | %left '^' | 
|  | %left '&' | 
|  | %left '+' '-' | 
|  | %left '*' '/' '%' | 
|  | %right '~' NEG | 
|  |  | 
|  | %% | 
|  |  | 
|  | input: | 
|  | /* empty */ | 
|  | | input accelerator | 
|  | | input bitmap | 
|  | | input cursor | 
|  | | input dialog | 
|  | | input font | 
|  | | input icon | 
|  | | input language | 
|  | | input menu | 
|  | | input menuex | 
|  | | input messagetable | 
|  | | input stringtable | 
|  | | input toolbar | 
|  | | input user | 
|  | | input versioninfo | 
|  | | input IGNORED_TOKEN | 
|  | ; | 
|  |  | 
|  | /* Accelerator resources.  */ | 
|  |  | 
|  | accelerator: | 
|  | id ACCELERATORS suboptions BEG acc_entries END | 
|  | { | 
|  | define_accelerator ($1, &$3, $5); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | acc_entries: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = NULL; | 
|  | } | 
|  | | acc_entries acc_entry | 
|  | { | 
|  | rc_accelerator *a; | 
|  |  | 
|  | a = (rc_accelerator *) res_alloc (sizeof *a); | 
|  | *a = $2; | 
|  | if ($1 == NULL) | 
|  | $$ = a; | 
|  | else | 
|  | { | 
|  | rc_accelerator **pp; | 
|  |  | 
|  | for (pp = &$1->next; *pp != NULL; pp = &(*pp)->next) | 
|  | ; | 
|  | *pp = a; | 
|  | $$ = $1; | 
|  | } | 
|  | } | 
|  | ; | 
|  |  | 
|  | acc_entry: | 
|  | acc_event cposnumexpr | 
|  | { | 
|  | $$ = $1; | 
|  | $$.id = $2; | 
|  | } | 
|  | | acc_event cposnumexpr ',' acc_options | 
|  | { | 
|  | $$ = $1; | 
|  | $$.id = $2; | 
|  | $$.flags |= $4; | 
|  | if (($$.flags & ACC_VIRTKEY) == 0 | 
|  | && ($$.flags & (ACC_SHIFT | ACC_CONTROL)) != 0) | 
|  | rcparse_warning (_("inappropriate modifiers for non-VIRTKEY")); | 
|  | } | 
|  | ; | 
|  |  | 
|  | acc_event: | 
|  | QUOTEDSTRING | 
|  | { | 
|  | const char *s = $1; | 
|  | char ch; | 
|  |  | 
|  | $$.next = NULL; | 
|  | $$.id = 0; | 
|  | ch = *s; | 
|  | if (ch != '^') | 
|  | $$.flags = 0; | 
|  | else | 
|  | { | 
|  | $$.flags = ACC_CONTROL | ACC_VIRTKEY; | 
|  | ++s; | 
|  | ch = TOUPPER (s[0]); | 
|  | } | 
|  | $$.key = ch; | 
|  | if (s[1] != '\0') | 
|  | rcparse_warning (_("accelerator should only be one character")); | 
|  | } | 
|  | | posnumexpr | 
|  | { | 
|  | $$.next = NULL; | 
|  | $$.flags = 0; | 
|  | $$.id = 0; | 
|  | $$.key = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | acc_options: | 
|  | acc_option | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | acc_options ',' acc_option | 
|  | { | 
|  | $$ = $1 | $3; | 
|  | } | 
|  | /* I've had one report that the comma is optional.  */ | 
|  | | acc_options acc_option | 
|  | { | 
|  | $$ = $1 | $2; | 
|  | } | 
|  | ; | 
|  |  | 
|  | acc_option: | 
|  | VIRTKEY | 
|  | { | 
|  | $$ = ACC_VIRTKEY; | 
|  | } | 
|  | | ASCII | 
|  | { | 
|  | /* This is just the absence of VIRTKEY.  */ | 
|  | $$ = 0; | 
|  | } | 
|  | | NOINVERT | 
|  | { | 
|  | $$ = ACC_NOINVERT; | 
|  | } | 
|  | | SHIFT | 
|  | { | 
|  | $$ = ACC_SHIFT; | 
|  | } | 
|  | | CONTROL | 
|  | { | 
|  | $$ = ACC_CONTROL; | 
|  | } | 
|  | | ALT | 
|  | { | 
|  | $$ = ACC_ALT; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Bitmap resources.  */ | 
|  |  | 
|  | bitmap: | 
|  | id BITMAP memflags_move file_name | 
|  | { | 
|  | define_bitmap ($1, &$3, $4); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Cursor resources.  */ | 
|  |  | 
|  | cursor: | 
|  | id CURSOR memflags_move_discard file_name | 
|  | { | 
|  | define_cursor ($1, &$3, $4); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Dialog resources.  */ | 
|  |  | 
|  | dialog: | 
|  | id DIALOG memflags_move exstyle posnumexpr cnumexpr cnumexpr | 
|  | cnumexpr | 
|  | { | 
|  | memset (&dialog, 0, sizeof dialog); | 
|  | dialog.x = $5; | 
|  | dialog.y = $6; | 
|  | dialog.width = $7; | 
|  | dialog.height = $8; | 
|  | dialog.style = WS_POPUP | WS_BORDER | WS_SYSMENU; | 
|  | dialog.exstyle = $4; | 
|  | dialog.menu.named = 1; | 
|  | dialog.class.named = 1; | 
|  | dialog.font = NULL; | 
|  | dialog.ex = NULL; | 
|  | dialog.controls = NULL; | 
|  | sub_res_info = $3; | 
|  | style = 0; | 
|  | } | 
|  | styles BEG controls END | 
|  | { | 
|  | define_dialog ($1, &sub_res_info, &dialog); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | | id DIALOGEX memflags_move exstyle posnumexpr cnumexpr cnumexpr | 
|  | cnumexpr | 
|  | { | 
|  | memset (&dialog, 0, sizeof dialog); | 
|  | dialog.x = $5; | 
|  | dialog.y = $6; | 
|  | dialog.width = $7; | 
|  | dialog.height = $8; | 
|  | dialog.style = WS_POPUP | WS_BORDER | WS_SYSMENU; | 
|  | dialog.exstyle = $4; | 
|  | dialog.menu.named = 1; | 
|  | dialog.class.named = 1; | 
|  | dialog.font = NULL; | 
|  | dialog.ex = ((rc_dialog_ex *) | 
|  | res_alloc (sizeof (rc_dialog_ex))); | 
|  | memset (dialog.ex, 0, sizeof (rc_dialog_ex)); | 
|  | dialog.controls = NULL; | 
|  | sub_res_info = $3; | 
|  | style = 0; | 
|  | } | 
|  | styles BEG controls END | 
|  | { | 
|  | define_dialog ($1, &sub_res_info, &dialog); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | | id DIALOGEX memflags_move exstyle posnumexpr cnumexpr cnumexpr | 
|  | cnumexpr cnumexpr | 
|  | { | 
|  | memset (&dialog, 0, sizeof dialog); | 
|  | dialog.x = $5; | 
|  | dialog.y = $6; | 
|  | dialog.width = $7; | 
|  | dialog.height = $8; | 
|  | dialog.style = WS_POPUP | WS_BORDER | WS_SYSMENU; | 
|  | dialog.exstyle = $4; | 
|  | dialog.menu.named = 1; | 
|  | dialog.class.named = 1; | 
|  | dialog.font = NULL; | 
|  | dialog.ex = ((rc_dialog_ex *) | 
|  | res_alloc (sizeof (rc_dialog_ex))); | 
|  | memset (dialog.ex, 0, sizeof (rc_dialog_ex)); | 
|  | dialog.ex->help = $9; | 
|  | dialog.controls = NULL; | 
|  | sub_res_info = $3; | 
|  | style = 0; | 
|  | } | 
|  | styles BEG controls END | 
|  | { | 
|  | define_dialog ($1, &sub_res_info, &dialog); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exstyle: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = 0; | 
|  | } | 
|  | | EXSTYLE '=' numexpr | 
|  | { | 
|  | $$ = $3; | 
|  | } | 
|  | ; | 
|  |  | 
|  | styles: | 
|  | /* empty */ | 
|  | | styles CAPTION res_unicode_string_concat | 
|  | { | 
|  | dialog.style |= WS_CAPTION; | 
|  | style |= WS_CAPTION; | 
|  | dialog.caption = $3; | 
|  | } | 
|  | | styles CLASS id | 
|  | { | 
|  | dialog.class = $3; | 
|  | } | 
|  | | styles STYLE | 
|  | styleexpr | 
|  | { | 
|  | dialog.style = style; | 
|  | } | 
|  | | styles EXSTYLE numexpr | 
|  | { | 
|  | dialog.exstyle = $3; | 
|  | } | 
|  | | styles CLASS res_unicode_string_concat | 
|  | { | 
|  | res_unistring_to_id (& dialog.class, $3); | 
|  | } | 
|  | | styles FONT numexpr ',' res_unicode_string_concat | 
|  | { | 
|  | dialog.style |= DS_SETFONT; | 
|  | style |= DS_SETFONT; | 
|  | dialog.pointsize = $3; | 
|  | dialog.font = $5; | 
|  | if (dialog.ex != NULL) | 
|  | { | 
|  | dialog.ex->weight = 0; | 
|  | dialog.ex->italic = 0; | 
|  | dialog.ex->charset = 1; | 
|  | } | 
|  | } | 
|  | | styles FONT numexpr ',' res_unicode_string_concat cnumexpr | 
|  | { | 
|  | dialog.style |= DS_SETFONT; | 
|  | style |= DS_SETFONT; | 
|  | dialog.pointsize = $3; | 
|  | dialog.font = $5; | 
|  | if (dialog.ex == NULL) | 
|  | rcparse_warning (_("extended FONT requires DIALOGEX")); | 
|  | else | 
|  | { | 
|  | dialog.ex->weight = $6; | 
|  | dialog.ex->italic = 0; | 
|  | dialog.ex->charset = 1; | 
|  | } | 
|  | } | 
|  | | styles FONT numexpr ',' res_unicode_string_concat cnumexpr cnumexpr | 
|  | { | 
|  | dialog.style |= DS_SETFONT; | 
|  | style |= DS_SETFONT; | 
|  | dialog.pointsize = $3; | 
|  | dialog.font = $5; | 
|  | if (dialog.ex == NULL) | 
|  | rcparse_warning (_("extended FONT requires DIALOGEX")); | 
|  | else | 
|  | { | 
|  | dialog.ex->weight = $6; | 
|  | dialog.ex->italic = $7; | 
|  | dialog.ex->charset = 1; | 
|  | } | 
|  | } | 
|  | | styles FONT numexpr ',' res_unicode_string_concat cnumexpr cnumexpr cnumexpr | 
|  | { | 
|  | dialog.style |= DS_SETFONT; | 
|  | style |= DS_SETFONT; | 
|  | dialog.pointsize = $3; | 
|  | dialog.font = $5; | 
|  | if (dialog.ex == NULL) | 
|  | rcparse_warning (_("extended FONT requires DIALOGEX")); | 
|  | else | 
|  | { | 
|  | dialog.ex->weight = $6; | 
|  | dialog.ex->italic = $7; | 
|  | dialog.ex->charset = $8; | 
|  | } | 
|  | } | 
|  | | styles MENU id | 
|  | { | 
|  | dialog.menu = $3; | 
|  | } | 
|  | | styles CHARACTERISTICS numexpr | 
|  | { | 
|  | sub_res_info.characteristics = $3; | 
|  | } | 
|  | | styles LANGUAGE numexpr cnumexpr | 
|  | { | 
|  | sub_res_info.language = $3 | ($4 << SUBLANG_SHIFT); | 
|  | } | 
|  | | styles VERSIONK numexpr | 
|  | { | 
|  | sub_res_info.version = $3; | 
|  | } | 
|  | ; | 
|  |  | 
|  | controls: | 
|  | /* empty */ | 
|  | | controls control | 
|  | { | 
|  | rc_dialog_control **pp; | 
|  |  | 
|  | for (pp = &dialog.controls; *pp != NULL; pp = &(*pp)->next) | 
|  | ; | 
|  | *pp = $2; | 
|  | } | 
|  | ; | 
|  |  | 
|  | control: | 
|  | AUTO3STATE optresidc | 
|  | { | 
|  | default_style = BS_AUTO3STATE | WS_TABSTOP; | 
|  | base_style = BS_AUTO3STATE; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_BUTTON; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | AUTOCHECKBOX optresidc | 
|  | { | 
|  | default_style = BS_AUTOCHECKBOX | WS_TABSTOP; | 
|  | base_style = BS_AUTOCHECKBOX | WS_TABSTOP; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_BUTTON; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | AUTORADIOBUTTON optresidc | 
|  | { | 
|  | default_style = BS_AUTORADIOBUTTON | WS_TABSTOP; | 
|  | base_style = BS_AUTORADIOBUTTON; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_BUTTON; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | BEDIT optresidc | 
|  | { | 
|  | default_style = ES_LEFT | WS_BORDER | WS_TABSTOP; | 
|  | base_style = ES_LEFT | WS_BORDER | WS_TABSTOP; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_EDIT; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | if (dialog.ex == NULL) | 
|  | rcparse_warning (_("BEDIT requires DIALOGEX")); | 
|  | res_string_to_id (&$$->class, "BEDIT"); | 
|  | } | 
|  | | CHECKBOX optresidc | 
|  | { | 
|  | default_style = BS_CHECKBOX | WS_TABSTOP; | 
|  | base_style = BS_CHECKBOX | WS_TABSTOP; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_BUTTON; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | COMBOBOX | 
|  | { | 
|  | /* This is as per MSDN documentation.  With some (???) | 
|  | versions of MS rc.exe their is no default style.  */ | 
|  | default_style = CBS_SIMPLE | WS_TABSTOP; | 
|  | base_style = 0; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_COMBOBOX; | 
|  | res_text_field = res_null_text; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $3; | 
|  | } | 
|  | | CONTROL optresidc numexpr cresid control_styleexpr cnumexpr | 
|  | cnumexpr cnumexpr cnumexpr optcnumexpr opt_control_data | 
|  | { | 
|  | $$ = define_control ($2, $3, $6, $7, $8, $9, $4, style, $10); | 
|  | if ($11 != NULL) | 
|  | { | 
|  | if (dialog.ex == NULL) | 
|  | rcparse_warning (_("control data requires DIALOGEX")); | 
|  | $$->data = $11; | 
|  | } | 
|  | } | 
|  | | CONTROL optresidc numexpr cresid control_styleexpr cnumexpr | 
|  | cnumexpr cnumexpr cnumexpr cnumexpr cnumexpr opt_control_data | 
|  | { | 
|  | $$ = define_control ($2, $3, $6, $7, $8, $9, $4, style, $10); | 
|  | if (dialog.ex == NULL) | 
|  | rcparse_warning (_("help ID requires DIALOGEX")); | 
|  | $$->help = $11; | 
|  | $$->data = $12; | 
|  | } | 
|  | | CTEXT optresidc | 
|  | { | 
|  | default_style = SS_CENTER | WS_GROUP; | 
|  | base_style = SS_CENTER; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_STATIC; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | DEFPUSHBUTTON optresidc | 
|  | { | 
|  | default_style = BS_DEFPUSHBUTTON | WS_TABSTOP; | 
|  | base_style = BS_DEFPUSHBUTTON | WS_TABSTOP; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_BUTTON; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | EDITTEXT | 
|  | { | 
|  | default_style = ES_LEFT | WS_BORDER | WS_TABSTOP; | 
|  | base_style = ES_LEFT | WS_BORDER | WS_TABSTOP; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_EDIT; | 
|  | res_text_field = res_null_text; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $3; | 
|  | } | 
|  | | GROUPBOX optresidc | 
|  | { | 
|  | default_style = BS_GROUPBOX; | 
|  | base_style = BS_GROUPBOX; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_BUTTON; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | HEDIT optresidc | 
|  | { | 
|  | default_style = ES_LEFT | WS_BORDER | WS_TABSTOP; | 
|  | base_style = ES_LEFT | WS_BORDER | WS_TABSTOP; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_EDIT; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | if (dialog.ex == NULL) | 
|  | rcparse_warning (_("IEDIT requires DIALOGEX")); | 
|  | res_string_to_id (&$$->class, "HEDIT"); | 
|  | } | 
|  | | ICON resref numexpr cnumexpr cnumexpr opt_control_data | 
|  | { | 
|  | $$ = define_icon_control ($2, $3, $4, $5, 0, 0, 0, $6, | 
|  | dialog.ex); | 
|  | } | 
|  | | ICON resref numexpr cnumexpr cnumexpr cnumexpr cnumexpr | 
|  | opt_control_data | 
|  | { | 
|  | $$ = define_icon_control ($2, $3, $4, $5, 0, 0, 0, $8, | 
|  | dialog.ex); | 
|  | } | 
|  | | ICON resref numexpr cnumexpr cnumexpr cnumexpr cnumexpr | 
|  | icon_styleexpr optcnumexpr opt_control_data | 
|  | { | 
|  | $$ = define_icon_control ($2, $3, $4, $5, style, $9, 0, $10, | 
|  | dialog.ex); | 
|  | } | 
|  | | ICON resref numexpr cnumexpr cnumexpr cnumexpr cnumexpr | 
|  | icon_styleexpr cnumexpr cnumexpr opt_control_data | 
|  | { | 
|  | $$ = define_icon_control ($2, $3, $4, $5, style, $9, $10, $11, | 
|  | dialog.ex); | 
|  | } | 
|  | | IEDIT optresidc | 
|  | { | 
|  | default_style = ES_LEFT | WS_BORDER | WS_TABSTOP; | 
|  | base_style = ES_LEFT | WS_BORDER | WS_TABSTOP; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_EDIT; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | if (dialog.ex == NULL) | 
|  | rcparse_warning (_("IEDIT requires DIALOGEX")); | 
|  | res_string_to_id (&$$->class, "IEDIT"); | 
|  | } | 
|  | | LISTBOX | 
|  | { | 
|  | default_style = LBS_NOTIFY | WS_BORDER; | 
|  | base_style = LBS_NOTIFY | WS_BORDER; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_LISTBOX; | 
|  | res_text_field = res_null_text; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $3; | 
|  | } | 
|  | | LTEXT optresidc | 
|  | { | 
|  | default_style = SS_LEFT | WS_GROUP; | 
|  | base_style = SS_LEFT; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_STATIC; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | PUSHBOX optresidc | 
|  | { | 
|  | default_style = BS_PUSHBOX | WS_TABSTOP; | 
|  | base_style = BS_PUSHBOX; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_BUTTON; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | PUSHBUTTON optresidc | 
|  | { | 
|  | default_style = BS_PUSHBUTTON | WS_TABSTOP; | 
|  | base_style = BS_PUSHBUTTON | WS_TABSTOP; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_BUTTON; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | RADIOBUTTON optresidc | 
|  | { | 
|  | default_style = BS_RADIOBUTTON | WS_TABSTOP; | 
|  | base_style = BS_RADIOBUTTON; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_BUTTON; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | RTEXT optresidc | 
|  | { | 
|  | default_style = SS_RIGHT | WS_GROUP; | 
|  | base_style = SS_RIGHT; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_STATIC; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | SCROLLBAR | 
|  | { | 
|  | default_style = SBS_HORZ; | 
|  | base_style = 0; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_SCROLLBAR; | 
|  | res_text_field = res_null_text; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $3; | 
|  | } | 
|  | | STATE3 optresidc | 
|  | { | 
|  | default_style = BS_3STATE | WS_TABSTOP; | 
|  | base_style = BS_3STATE; | 
|  | class.named = 0; | 
|  | class.u.id = CTL_BUTTON; | 
|  | res_text_field = $2; | 
|  | } | 
|  | control_params | 
|  | { | 
|  | $$ = $4; | 
|  | } | 
|  | | USERBUTTON resref numexpr ',' numexpr ',' numexpr ',' | 
|  | numexpr ',' numexpr ',' | 
|  | { style = WS_CHILD | WS_VISIBLE; } | 
|  | styleexpr optcnumexpr | 
|  | { | 
|  | rc_res_id cid; | 
|  | cid.named = 0; | 
|  | cid.u.id = CTL_BUTTON; | 
|  | $$ = define_control ($2, $3, $5, $7, $9, $11, cid, | 
|  | style, $15); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Parameters for a control.  The static variables DEFAULT_STYLE, | 
|  | BASE_STYLE, and CLASS must be initialized before this nonterminal | 
|  | is used.  DEFAULT_STYLE is the style to use if no style expression | 
|  | is specified.  BASE_STYLE is the base style to use if a style | 
|  | expression is specified; the style expression modifies the base | 
|  | style.  CLASS is the class of the control.  */ | 
|  |  | 
|  | control_params: | 
|  | numexpr cnumexpr cnumexpr cnumexpr cnumexpr opt_control_data | 
|  | { | 
|  | $$ = define_control (res_text_field, $1, $2, $3, $4, $5, class, | 
|  | default_style | WS_CHILD | WS_VISIBLE, 0); | 
|  | if ($6 != NULL) | 
|  | { | 
|  | if (dialog.ex == NULL) | 
|  | rcparse_warning (_("control data requires DIALOGEX")); | 
|  | $$->data = $6; | 
|  | } | 
|  | } | 
|  | | numexpr cnumexpr cnumexpr cnumexpr cnumexpr | 
|  | control_params_styleexpr optcnumexpr opt_control_data | 
|  | { | 
|  | $$ = define_control (res_text_field, $1, $2, $3, $4, $5, class, style, $7); | 
|  | if ($8 != NULL) | 
|  | { | 
|  | if (dialog.ex == NULL) | 
|  | rcparse_warning (_("control data requires DIALOGEX")); | 
|  | $$->data = $8; | 
|  | } | 
|  | } | 
|  | | numexpr cnumexpr cnumexpr cnumexpr cnumexpr | 
|  | control_params_styleexpr cnumexpr cnumexpr opt_control_data | 
|  | { | 
|  | $$ = define_control (res_text_field, $1, $2, $3, $4, $5, class, style, $7); | 
|  | if (dialog.ex == NULL) | 
|  | rcparse_warning (_("help ID requires DIALOGEX")); | 
|  | $$->help = $8; | 
|  | $$->data = $9; | 
|  | } | 
|  | ; | 
|  |  | 
|  | cresid: | 
|  | ',' resid | 
|  | { | 
|  | if ($2.named) | 
|  | res_unistring_to_id (&$$, $2.u.n.name); | 
|  | else | 
|  | $$=$2; | 
|  | } | 
|  | ; | 
|  |  | 
|  | optresidc: | 
|  | /* empty */ | 
|  | { | 
|  | res_string_to_id (&$$, ""); | 
|  | } | 
|  | | resid ',' { $$=$1; } | 
|  | ; | 
|  |  | 
|  | resid: | 
|  | posnumexpr | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = $1; | 
|  | } | 
|  | | res_unicode_string_concat | 
|  | { | 
|  | $$.named = 1; | 
|  | $$.u.n.name = $1; | 
|  | $$.u.n.length = unichar_len ($1); | 
|  | } | 
|  | ; | 
|  |  | 
|  | opt_control_data: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = NULL; | 
|  | } | 
|  | | BEG optrcdata_data END | 
|  | { | 
|  | $$ = $2.first; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* These only exist to parse a reduction out of a common case.  */ | 
|  |  | 
|  | control_styleexpr: | 
|  | ',' | 
|  | { style = WS_CHILD | WS_VISIBLE; } | 
|  | styleexpr | 
|  | ; | 
|  |  | 
|  | icon_styleexpr: | 
|  | ',' | 
|  | { style = SS_ICON | WS_CHILD | WS_VISIBLE; } | 
|  | styleexpr | 
|  | ; | 
|  |  | 
|  | control_params_styleexpr: | 
|  | ',' | 
|  | { style = base_style | WS_CHILD | WS_VISIBLE; } | 
|  | styleexpr | 
|  | ; | 
|  |  | 
|  | /* Font resources.  */ | 
|  |  | 
|  | font: | 
|  | id FONT memflags_move_discard file_name | 
|  | { | 
|  | define_font ($1, &$3, $4); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Icon resources.  */ | 
|  |  | 
|  | icon: | 
|  | id ICON memflags_move_discard file_name | 
|  | { | 
|  | define_icon ($1, &$3, $4); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Language command.  This changes the static variable language, which | 
|  | affects all subsequent resources.  */ | 
|  |  | 
|  | language: | 
|  | LANGUAGE numexpr cnumexpr | 
|  | { | 
|  | language = $2 | ($3 << SUBLANG_SHIFT); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Menu resources.  */ | 
|  |  | 
|  | menu: | 
|  | id MENU suboptions BEG menuitems END | 
|  | { | 
|  | define_menu ($1, &$3, $5); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | menuitems: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = NULL; | 
|  | } | 
|  | | menuitems menuitem | 
|  | { | 
|  | if ($1 == NULL) | 
|  | $$ = $2; | 
|  | else | 
|  | { | 
|  | rc_menuitem **pp; | 
|  |  | 
|  | for (pp = &$1->next; *pp != NULL; pp = &(*pp)->next) | 
|  | ; | 
|  | *pp = $2; | 
|  | $$ = $1; | 
|  | } | 
|  | } | 
|  | ; | 
|  |  | 
|  | menuitem: | 
|  | MENUITEM res_unicode_string_concat cnumexpr menuitem_flags | 
|  | { | 
|  | $$ = define_menuitem ($2, $3, $4, 0, 0, NULL); | 
|  | } | 
|  | | MENUITEM SEPARATOR | 
|  | { | 
|  | $$ = define_menuitem (NULL, 0, 0, 0, 0, NULL); | 
|  | } | 
|  | | POPUP res_unicode_string_concat menuitem_flags BEG menuitems END | 
|  | { | 
|  | $$ = define_menuitem ($2, 0, $3, 0, 0, $5); | 
|  | } | 
|  | ; | 
|  |  | 
|  | menuitem_flags: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = 0; | 
|  | } | 
|  | | menuitem_flags ',' menuitem_flag | 
|  | { | 
|  | $$ = $1 | $3; | 
|  | } | 
|  | | menuitem_flags menuitem_flag | 
|  | { | 
|  | $$ = $1 | $2; | 
|  | } | 
|  | ; | 
|  |  | 
|  | menuitem_flag: | 
|  | CHECKED | 
|  | { | 
|  | $$ = MENUITEM_CHECKED; | 
|  | } | 
|  | | GRAYED | 
|  | { | 
|  | $$ = MENUITEM_GRAYED; | 
|  | } | 
|  | | HELP | 
|  | { | 
|  | $$ = MENUITEM_HELP; | 
|  | } | 
|  | | INACTIVE | 
|  | { | 
|  | $$ = MENUITEM_INACTIVE; | 
|  | } | 
|  | | MENUBARBREAK | 
|  | { | 
|  | $$ = MENUITEM_MENUBARBREAK; | 
|  | } | 
|  | | MENUBREAK | 
|  | { | 
|  | $$ = MENUITEM_MENUBREAK; | 
|  | } | 
|  | | BITMAP | 
|  | { | 
|  | $$ = MENUITEM_BITMAP; | 
|  | } | 
|  | | OWNERDRAW | 
|  | { | 
|  | $$ = MENUITEM_OWNERDRAW; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Menuex resources.  */ | 
|  |  | 
|  | menuex: | 
|  | id MENUEX suboptions BEG menuexitems END | 
|  | { | 
|  | define_menu ($1, &$3, $5); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | menuexitems: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = NULL; | 
|  | } | 
|  | | menuexitems menuexitem | 
|  | { | 
|  | if ($1 == NULL) | 
|  | $$ = $2; | 
|  | else | 
|  | { | 
|  | rc_menuitem **pp; | 
|  |  | 
|  | for (pp = &$1->next; *pp != NULL; pp = &(*pp)->next) | 
|  | ; | 
|  | *pp = $2; | 
|  | $$ = $1; | 
|  | } | 
|  | } | 
|  | ; | 
|  |  | 
|  | menuexitem: | 
|  | MENUITEM res_unicode_string_concat | 
|  | { | 
|  | $$ = define_menuitem ($2, 0, 0, 0, 0, NULL); | 
|  | } | 
|  | | MENUITEM res_unicode_string_concat cnumexpr | 
|  | { | 
|  | $$ = define_menuitem ($2, $3, 0, 0, 0, NULL); | 
|  | } | 
|  | | MENUITEM res_unicode_string_concat cnumexpr cnumexpr optcnumexpr | 
|  | { | 
|  | $$ = define_menuitem ($2, $3, $4, $5, 0, NULL); | 
|  | } | 
|  | | MENUITEM SEPARATOR | 
|  | { | 
|  | $$ = define_menuitem (NULL, 0, 0, 0, 0, NULL); | 
|  | } | 
|  | | POPUP res_unicode_string_concat BEG menuexitems END | 
|  | { | 
|  | $$ = define_menuitem ($2, 0, 0, 0, 0, $4); | 
|  | } | 
|  | | POPUP res_unicode_string_concat cnumexpr BEG menuexitems END | 
|  | { | 
|  | $$ = define_menuitem ($2, $3, 0, 0, 0, $5); | 
|  | } | 
|  | | POPUP res_unicode_string_concat cnumexpr cnumexpr BEG menuexitems END | 
|  | { | 
|  | $$ = define_menuitem ($2, $3, $4, 0, 0, $6); | 
|  | } | 
|  | | POPUP res_unicode_string_concat cnumexpr cnumexpr cnumexpr optcnumexpr | 
|  | BEG menuexitems END | 
|  | { | 
|  | $$ = define_menuitem ($2, $3, $4, $5, $6, $8); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Messagetable resources.  */ | 
|  |  | 
|  | messagetable: | 
|  | id MESSAGETABLE memflags_move file_name | 
|  | { | 
|  | define_messagetable ($1, &$3, $4); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* We use a different lexing algorithm, because rcdata strings may | 
|  | contain embedded null bytes, and we need to know the length to use.  */ | 
|  |  | 
|  | optrcdata_data: | 
|  | { | 
|  | rcparse_rcdata (); | 
|  | } | 
|  | optrcdata_data_int | 
|  | { | 
|  | rcparse_normal (); | 
|  | $$ = $2; | 
|  | } | 
|  | ; | 
|  |  | 
|  | optrcdata_data_int: | 
|  | /* empty */ | 
|  | { | 
|  | $$.first = NULL; | 
|  | $$.last = NULL; | 
|  | } | 
|  | | rcdata_data | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | rcdata_data: | 
|  | sizedstring | 
|  | { | 
|  | rc_rcdata_item *ri; | 
|  |  | 
|  | ri = define_rcdata_string ($1.s, $1.length); | 
|  | $$.first = ri; | 
|  | $$.last = ri; | 
|  | } | 
|  | | sizedunistring | 
|  | { | 
|  | rc_rcdata_item *ri; | 
|  |  | 
|  | ri = define_rcdata_unistring ($1.s, $1.length); | 
|  | $$.first = ri; | 
|  | $$.last = ri; | 
|  | } | 
|  | | sizednumexpr | 
|  | { | 
|  | rc_rcdata_item *ri; | 
|  |  | 
|  | ri = define_rcdata_number ($1.val, $1.dword); | 
|  | $$.first = ri; | 
|  | $$.last = ri; | 
|  | } | 
|  | | rcdata_data ',' sizedstring | 
|  | { | 
|  | rc_rcdata_item *ri; | 
|  |  | 
|  | ri = define_rcdata_string ($3.s, $3.length); | 
|  | $$.first = $1.first; | 
|  | $1.last->next = ri; | 
|  | $$.last = ri; | 
|  | } | 
|  | | rcdata_data ',' sizedunistring | 
|  | { | 
|  | rc_rcdata_item *ri; | 
|  |  | 
|  | ri = define_rcdata_unistring ($3.s, $3.length); | 
|  | $$.first = $1.first; | 
|  | $1.last->next = ri; | 
|  | $$.last = ri; | 
|  | } | 
|  | | rcdata_data ',' sizednumexpr | 
|  | { | 
|  | rc_rcdata_item *ri; | 
|  |  | 
|  | ri = define_rcdata_number ($3.val, $3.dword); | 
|  | $$.first = $1.first; | 
|  | $1.last->next = ri; | 
|  | $$.last = ri; | 
|  | } | 
|  | | rcdata_data ',' | 
|  | { | 
|  | $$=$1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Stringtable resources.  */ | 
|  |  | 
|  | stringtable: | 
|  | STRINGTABLE suboptions BEG | 
|  | { sub_res_info = $2; rcparse_rcdata (); } | 
|  | string_data END { rcparse_normal (); } | 
|  | ; | 
|  |  | 
|  | string_data: | 
|  | /* empty */ | 
|  | | string_data numexpr res_unicode_sizedstring_concat | 
|  | { | 
|  | define_stringtable (&sub_res_info, $2, $3.s, $3.length); | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | | string_data numexpr ',' res_unicode_sizedstring_concat | 
|  | { | 
|  | define_stringtable (&sub_res_info, $2, $4.s, $4.length); | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | | string_data error | 
|  | { | 
|  | rcparse_warning (_("invalid stringtable resource.")); | 
|  | abort (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | rcdata_id: | 
|  | id | 
|  | { | 
|  | $$=$1; | 
|  | } | 
|  | | HTML | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = 23; | 
|  | } | 
|  | | RCDATA | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = RT_RCDATA; | 
|  | } | 
|  | | MANIFEST | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = RT_MANIFEST; | 
|  | } | 
|  | | PLUGPLAY | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = RT_PLUGPLAY; | 
|  | } | 
|  | | VXD | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = RT_VXD; | 
|  | } | 
|  | | DLGINCLUDE | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = RT_DLGINCLUDE; | 
|  | } | 
|  | | DLGINIT | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = RT_DLGINIT; | 
|  | } | 
|  | | ANICURSOR | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = RT_ANICURSOR; | 
|  | } | 
|  | | ANIICON | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = RT_ANIICON; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* User defined resources.  We accept general suboptions in the | 
|  | file_name case to keep the parser happy.  */ | 
|  |  | 
|  | user: | 
|  | id rcdata_id suboptions BEG optrcdata_data END | 
|  | { | 
|  | define_user_data ($1, $2, &$3, $5.first); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | | id rcdata_id suboptions file_name | 
|  | { | 
|  | define_user_file ($1, $2, &$3, $4); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | toolbar: | 
|  | id TOOLBAR suboptions numexpr cnumexpr BEG toolbar_data END | 
|  | { | 
|  | define_toolbar ($1, &$3, $4, $5, $7); | 
|  | } | 
|  | ; | 
|  |  | 
|  | toolbar_data: /* empty */ { $$= NULL; } | 
|  | | toolbar_data BUTTON id | 
|  | { | 
|  | rc_toolbar_item *c,*n; | 
|  | c = $1; | 
|  | n= (rc_toolbar_item *) | 
|  | res_alloc (sizeof (rc_toolbar_item)); | 
|  | if (c != NULL) | 
|  | while (c->next != NULL) | 
|  | c = c->next; | 
|  | n->prev = c; | 
|  | n->next = NULL; | 
|  | if (c != NULL) | 
|  | c->next = n; | 
|  | n->id = $3; | 
|  | if ($1 == NULL) | 
|  | $$ = n; | 
|  | else | 
|  | $$ = $1; | 
|  | } | 
|  | | toolbar_data SEPARATOR | 
|  | { | 
|  | rc_toolbar_item *c,*n; | 
|  | c = $1; | 
|  | n= (rc_toolbar_item *) | 
|  | res_alloc (sizeof (rc_toolbar_item)); | 
|  | if (c != NULL) | 
|  | while (c->next != NULL) | 
|  | c = c->next; | 
|  | n->prev = c; | 
|  | n->next = NULL; | 
|  | if (c != NULL) | 
|  | c->next = n; | 
|  | n->id.named = 0; | 
|  | n->id.u.id = 0; | 
|  | if ($1 == NULL) | 
|  | $$ = n; | 
|  | else | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Versioninfo resources.  */ | 
|  |  | 
|  | versioninfo: | 
|  | id VERSIONINFO fixedverinfo BEG verblocks END | 
|  | { | 
|  | define_versioninfo ($1, language, $3, $5); | 
|  | if (yychar != YYEMPTY) | 
|  | YYERROR; | 
|  | rcparse_discard_strings (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | fixedverinfo: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = ((rc_fixed_versioninfo *) | 
|  | res_alloc (sizeof (rc_fixed_versioninfo))); | 
|  | memset ($$, 0, sizeof (rc_fixed_versioninfo)); | 
|  | } | 
|  | | fixedverinfo FILEVERSION numexpr optcnumexpr optcnumexpr | 
|  | optcnumexpr | 
|  | { | 
|  | $1->file_version_ms = ($3 << 16) | ($4 & 0xffff); | 
|  | $1->file_version_ls = ($5 << 16) | ($6 & 0xffff); | 
|  | $$ = $1; | 
|  | } | 
|  | | fixedverinfo PRODUCTVERSION numexpr optcnumexpr optcnumexpr | 
|  | optcnumexpr | 
|  | { | 
|  | $1->product_version_ms = ($3 << 16) | ($4 & 0xffff); | 
|  | $1->product_version_ls = ($5 << 16) | ($6 & 0xffff); | 
|  | $$ = $1; | 
|  | } | 
|  | | fixedverinfo FILEFLAGSMASK numexpr | 
|  | { | 
|  | $1->file_flags_mask = $3; | 
|  | $$ = $1; | 
|  | } | 
|  | | fixedverinfo FILEFLAGS numexpr | 
|  | { | 
|  | $1->file_flags = $3; | 
|  | $$ = $1; | 
|  | } | 
|  | | fixedverinfo FILEOS numexpr | 
|  | { | 
|  | $1->file_os = $3; | 
|  | $$ = $1; | 
|  | } | 
|  | | fixedverinfo FILETYPE numexpr | 
|  | { | 
|  | $1->file_type = $3; | 
|  | $$ = $1; | 
|  | } | 
|  | | fixedverinfo FILESUBTYPE numexpr | 
|  | { | 
|  | $1->file_subtype = $3; | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* To handle verblocks successfully, the lexer handles BLOCK | 
|  | specially.  A BLOCK "StringFileInfo" is returned as | 
|  | BLOCKSTRINGFILEINFO.  A BLOCK "VarFileInfo" is returned as | 
|  | BLOCKVARFILEINFO.  A BLOCK with some other string returns BLOCK | 
|  | with the string as the value.  */ | 
|  |  | 
|  | verblocks: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = NULL; | 
|  | } | 
|  | | verblocks BLOCKSTRINGFILEINFO BEG verstringtables END | 
|  | { | 
|  | $$ = append_ver_stringfileinfo ($1, $4); | 
|  | } | 
|  | | verblocks BLOCKVARFILEINFO BEG VALUE res_unicode_string_concat vertrans END | 
|  | { | 
|  | $$ = append_ver_varfileinfo ($1, $5, $6); | 
|  | } | 
|  | ; | 
|  |  | 
|  | verstringtables: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = NULL; | 
|  | } | 
|  | | verstringtables BLOCK BEG vervals END | 
|  | { | 
|  | $$ = append_ver_stringtable ($1, $2, $4); | 
|  | } | 
|  | ; | 
|  |  | 
|  | vervals: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = NULL; | 
|  | } | 
|  | | vervals VALUE res_unicode_string_concat ',' res_unicode_string_concat | 
|  | { | 
|  | $$ = append_verval ($1, $3, $5); | 
|  | } | 
|  | ; | 
|  |  | 
|  | vertrans: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = NULL; | 
|  | } | 
|  | | vertrans cnumexpr cnumexpr | 
|  | { | 
|  | $$ = append_vertrans ($1, $2, $3); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* A resource ID.  */ | 
|  |  | 
|  | id: | 
|  | posnumexpr | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = $1; | 
|  | } | 
|  | | resname | 
|  | { | 
|  | res_unistring_to_id (&$$, $1); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* A resource reference.  */ | 
|  |  | 
|  | resname: | 
|  | res_unicode_string | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | STRING | 
|  | { | 
|  | unichar *h = NULL; | 
|  | unicode_from_ascii ((rc_uint_type *) NULL, &h, $1); | 
|  | $$ = h; | 
|  | } | 
|  | ; | 
|  |  | 
|  |  | 
|  | resref: | 
|  | posnumexpr ',' | 
|  | { | 
|  | $$.named = 0; | 
|  | $$.u.id = $1; | 
|  | } | 
|  | | resname | 
|  | { | 
|  | res_unistring_to_id (&$$, $1); | 
|  | } | 
|  | | resname ',' | 
|  | { | 
|  | res_unistring_to_id (&$$, $1); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Generic suboptions.  These may appear before the BEGIN in any | 
|  | multiline statement.  */ | 
|  |  | 
|  | suboptions: | 
|  | /* empty */ | 
|  | { | 
|  | memset (&$$, 0, sizeof (rc_res_res_info)); | 
|  | $$.language = language; | 
|  | /* FIXME: Is this the right default?  */ | 
|  | $$.memflags = MEMFLAG_MOVEABLE | MEMFLAG_PURE | MEMFLAG_DISCARDABLE; | 
|  | } | 
|  | | suboptions memflag | 
|  | { | 
|  | $$ = $1; | 
|  | $$.memflags |= $2.on; | 
|  | $$.memflags &=~ $2.off; | 
|  | } | 
|  | | suboptions CHARACTERISTICS numexpr | 
|  | { | 
|  | $$ = $1; | 
|  | $$.characteristics = $3; | 
|  | } | 
|  | | suboptions LANGUAGE numexpr cnumexpr | 
|  | { | 
|  | $$ = $1; | 
|  | $$.language = $3 | ($4 << SUBLANG_SHIFT); | 
|  | } | 
|  | | suboptions VERSIONK numexpr | 
|  | { | 
|  | $$ = $1; | 
|  | $$.version = $3; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Memory flags which default to MOVEABLE and DISCARDABLE.  */ | 
|  |  | 
|  | memflags_move_discard: | 
|  | /* empty */ | 
|  | { | 
|  | memset (&$$, 0, sizeof (rc_res_res_info)); | 
|  | $$.language = language; | 
|  | $$.memflags = MEMFLAG_MOVEABLE | MEMFLAG_DISCARDABLE; | 
|  | } | 
|  | | memflags_move_discard memflag | 
|  | { | 
|  | $$ = $1; | 
|  | $$.memflags |= $2.on; | 
|  | $$.memflags &=~ $2.off; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Memory flags which default to MOVEABLE.  */ | 
|  |  | 
|  | memflags_move: | 
|  | /* empty */ | 
|  | { | 
|  | memset (&$$, 0, sizeof (rc_res_res_info)); | 
|  | $$.language = language; | 
|  | $$.memflags = MEMFLAG_MOVEABLE | MEMFLAG_PURE | MEMFLAG_DISCARDABLE; | 
|  | } | 
|  | | memflags_move memflag | 
|  | { | 
|  | $$ = $1; | 
|  | $$.memflags |= $2.on; | 
|  | $$.memflags &=~ $2.off; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Memory flags.  This returns a struct with two integers, because we | 
|  | sometimes want to set bits and we sometimes want to clear them.  */ | 
|  |  | 
|  | memflag: | 
|  | MOVEABLE | 
|  | { | 
|  | $$.on = MEMFLAG_MOVEABLE; | 
|  | $$.off = 0; | 
|  | } | 
|  | | FIXED | 
|  | { | 
|  | $$.on = 0; | 
|  | $$.off = MEMFLAG_MOVEABLE; | 
|  | } | 
|  | | PURE | 
|  | { | 
|  | $$.on = MEMFLAG_PURE; | 
|  | $$.off = 0; | 
|  | } | 
|  | | IMPURE | 
|  | { | 
|  | $$.on = 0; | 
|  | $$.off = MEMFLAG_PURE; | 
|  | } | 
|  | | PRELOAD | 
|  | { | 
|  | $$.on = MEMFLAG_PRELOAD; | 
|  | $$.off = 0; | 
|  | } | 
|  | | LOADONCALL | 
|  | { | 
|  | $$.on = 0; | 
|  | $$.off = MEMFLAG_PRELOAD; | 
|  | } | 
|  | | DISCARDABLE | 
|  | { | 
|  | $$.on = MEMFLAG_DISCARDABLE; | 
|  | $$.off = 0; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* A file name.  */ | 
|  |  | 
|  | file_name: | 
|  | QUOTEDSTRING | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | STRING | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Concat string */ | 
|  | res_unicode_string_concat: | 
|  | res_unicode_string | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | | 
|  | res_unicode_string_concat res_unicode_string | 
|  | { | 
|  | rc_uint_type l1 = unichar_len ($1); | 
|  | rc_uint_type l2 = unichar_len ($2); | 
|  | unichar *h = (unichar *) res_alloc ((l1 + l2 + 1) * sizeof (unichar)); | 
|  | if (l1 != 0) | 
|  | memcpy (h, $1, l1 * sizeof (unichar)); | 
|  | if (l2 != 0) | 
|  | memcpy (h + l1, $2, l2  * sizeof (unichar)); | 
|  | h[l1 + l2] = 0; | 
|  | $$ = h; | 
|  | } | 
|  | ; | 
|  |  | 
|  | res_unicode_string: | 
|  | QUOTEDUNISTRING | 
|  | { | 
|  | $$ = unichar_dup ($1); | 
|  | } | 
|  | | QUOTEDSTRING | 
|  | { | 
|  | unichar *h = NULL; | 
|  | unicode_from_ascii ((rc_uint_type *) NULL, &h, $1); | 
|  | $$ = h; | 
|  | } | 
|  | ; | 
|  |  | 
|  | res_unicode_sizedstring: | 
|  | sizedunistring | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | sizedstring | 
|  | { | 
|  | unichar *h = NULL; | 
|  | rc_uint_type l = 0; | 
|  | unicode_from_ascii_len (&l, &h, $1.s, $1.length); | 
|  | $$.s = h; | 
|  | $$.length = l; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Concat string */ | 
|  | res_unicode_sizedstring_concat: | 
|  | res_unicode_sizedstring | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | | 
|  | res_unicode_sizedstring_concat res_unicode_sizedstring | 
|  | { | 
|  | rc_uint_type l1 = $1.length; | 
|  | rc_uint_type l2 = $2.length; | 
|  | unichar *h = (unichar *) res_alloc ((l1 + l2 + 1) * sizeof (unichar)); | 
|  | if (l1 != 0) | 
|  | memcpy (h, $1.s, l1 * sizeof (unichar)); | 
|  | if (l2 != 0) | 
|  | memcpy (h + l1, $2.s, l2  * sizeof (unichar)); | 
|  | h[l1 + l2] = 0; | 
|  | $$.length = l1 + l2; | 
|  | $$.s = h; | 
|  | } | 
|  | ; | 
|  |  | 
|  | sizedstring: | 
|  | SIZEDSTRING | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | sizedstring SIZEDSTRING | 
|  | { | 
|  | rc_uint_type l = $1.length + $2.length; | 
|  | char *h = (char *) res_alloc (l); | 
|  | memcpy (h, $1.s, $1.length); | 
|  | memcpy (h + $1.length, $2.s, $2.length); | 
|  | $$.s = h; | 
|  | $$.length = l; | 
|  | } | 
|  | ; | 
|  |  | 
|  | sizedunistring: | 
|  | SIZEDUNISTRING | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | sizedunistring SIZEDUNISTRING | 
|  | { | 
|  | rc_uint_type l = $1.length + $2.length; | 
|  | unichar *h = (unichar *) res_alloc (l * sizeof (unichar)); | 
|  | memcpy (h, $1.s, $1.length * sizeof (unichar)); | 
|  | memcpy (h + $1.length, $2.s, $2.length  * sizeof (unichar)); | 
|  | $$.s = h; | 
|  | $$.length = l; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* A style expression.  This changes the static variable STYLE.  We do | 
|  | it this way because rc appears to permit a style to be set to | 
|  | something like | 
|  | WS_GROUP | NOT WS_TABSTOP | 
|  | to mean that a default of WS_TABSTOP should be removed.  Anything | 
|  | which wants to accept a style must first set STYLE to the default | 
|  | value.  The styleexpr nonterminal will change STYLE as specified by | 
|  | the user.  Note that we do not accept arbitrary expressions here, | 
|  | just numbers separated by '|'.  */ | 
|  |  | 
|  | styleexpr: | 
|  | parennumber | 
|  | { | 
|  | style |= $1; | 
|  | } | 
|  | | NOT parennumber | 
|  | { | 
|  | style &=~ $2; | 
|  | } | 
|  | | styleexpr '|' parennumber | 
|  | { | 
|  | style |= $3; | 
|  | } | 
|  | | styleexpr '|' NOT parennumber | 
|  | { | 
|  | style &=~ $4; | 
|  | } | 
|  | ; | 
|  |  | 
|  | parennumber: | 
|  | NUMBER | 
|  | { | 
|  | $$ = $1.val; | 
|  | } | 
|  | | '(' numexpr ')' | 
|  | { | 
|  | $$ = $2; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* An optional expression with a leading comma.  */ | 
|  |  | 
|  | optcnumexpr: | 
|  | /* empty */ | 
|  | { | 
|  | $$ = 0; | 
|  | } | 
|  | | cnumexpr | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* An expression with a leading comma.  */ | 
|  |  | 
|  | cnumexpr: | 
|  | ',' numexpr | 
|  | { | 
|  | $$ = $2; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* A possibly negated numeric expression.  */ | 
|  |  | 
|  | numexpr: | 
|  | sizednumexpr | 
|  | { | 
|  | $$ = $1.val; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* A possibly negated expression with a size.  */ | 
|  |  | 
|  | sizednumexpr: | 
|  | NUMBER | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | '(' sizednumexpr ')' | 
|  | { | 
|  | $$ = $2; | 
|  | } | 
|  | | '~' sizednumexpr %prec '~' | 
|  | { | 
|  | $$.val = ~ $2.val; | 
|  | $$.dword = $2.dword; | 
|  | } | 
|  | | '-' sizednumexpr %prec NEG | 
|  | { | 
|  | $$.val = - $2.val; | 
|  | $$.dword = $2.dword; | 
|  | } | 
|  | | sizednumexpr '*' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val * $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizednumexpr '/' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val / ($3.val ? $3.val : 1); | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizednumexpr '%' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val % ($3.val ? $3.val : 1); | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizednumexpr '+' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val + $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizednumexpr '-' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val - $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizednumexpr '&' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val & $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizednumexpr '^' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val ^ $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizednumexpr '|' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val | $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* An expression with a leading comma which does not use unary | 
|  | negation.  */ | 
|  |  | 
|  | cposnumexpr: | 
|  | ',' posnumexpr | 
|  | { | 
|  | $$ = $2; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* An expression which does not use unary negation.  */ | 
|  |  | 
|  | posnumexpr: | 
|  | sizedposnumexpr | 
|  | { | 
|  | $$ = $1.val; | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* An expression which does not use unary negation.  We separate unary | 
|  | negation to avoid parsing conflicts when two numeric expressions | 
|  | appear consecutively.  */ | 
|  |  | 
|  | sizedposnumexpr: | 
|  | NUMBER | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | '(' sizednumexpr ')' | 
|  | { | 
|  | $$ = $2; | 
|  | } | 
|  | | '~' sizednumexpr %prec '~' | 
|  | { | 
|  | $$.val = ~ $2.val; | 
|  | $$.dword = $2.dword; | 
|  | } | 
|  | | sizedposnumexpr '*' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val * $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizedposnumexpr '/' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val / ($3.val ? $3.val : 1); | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizedposnumexpr '%' sizednumexpr | 
|  | { | 
|  | /* PR 17512: file: 89105a25.  */ | 
|  | $$.val = $1.val % ($3.val ? $3.val : 1); | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizedposnumexpr '+' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val + $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizedposnumexpr '-' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val - $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizedposnumexpr '&' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val & $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizedposnumexpr '^' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val ^ $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | | sizedposnumexpr '|' sizednumexpr | 
|  | { | 
|  | $$.val = $1.val | $3.val; | 
|  | $$.dword = $1.dword || $3.dword; | 
|  | } | 
|  | ; | 
|  |  | 
|  | %% | 
|  |  | 
|  | /* Set the language from the command line.  */ | 
|  |  | 
|  | void | 
|  | rcparse_set_language (int lang) | 
|  | { | 
|  | language = lang; | 
|  | } |