%{ /* rcparse.y -- parser for Windows rc files
- Copyright 1997 Free Software Foundation, Inc.
+ Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support.
This file is part of GNU Binutils.
#include "bucomm.h"
#include "libiberty.h"
#include "windres.h"
+#include "safe-ctype.h"
-/* The current language. The default is U.S. English. */
+/* The current language. */
-static unsigned short language = 0x409;
+static unsigned short language;
/* The resource information during a sub statement. */
struct accelerator *pacc;
struct dialog_control *dialog_control;
struct menuitem *menuitem;
- struct rcdata_data *rcdata;
+ struct
+ {
+ struct rcdata_item *first;
+ struct rcdata_item *last;
+ } rcdata;
+ struct rcdata_item *rcdata_item;
struct stringtable_data *stringtable;
struct fixed_versioninfo *fixver;
struct ver_info *verinfo;
} i;
unsigned long il;
unsigned short is;
- char *s;
+ const char *s;
+ struct
+ {
+ unsigned long length;
+ const char *s;
+ } ss;
};
%token BEG END
%token BEDIT HEDIT IEDIT
%token FONT
%token ICON
-%token LANGUAGE CHARACTERISTICS VERSION
+%token LANGUAGE CHARACTERISTICS VERSIONK
%token MENU MENUEX MENUITEM SEPARATOR POPUP CHECKED GRAYED HELP INACTIVE
%token MENUBARBREAK MENUBREAK
%token MESSAGETABLE
%token NOT
%token <s> QUOTEDSTRING STRING
%token <i> NUMBER
+%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 rcdata_data opt_control_data
+%type <rcdata> optrcdata_data optrcdata_data_int rcdata_data
+%type <rcdata_item> opt_control_data
%type <fixver> fixedverinfo
%type <verinfo> verblocks
%type <verstring> vervals
%type <vervar> vertrans
%type <res_info> suboptions memflags_move_discard memflags_move
%type <memflags> memflag
-%type <id> id
+%type <id> id resref
%type <il> exstyle parennumber
%type <il> numexpr posnumexpr cnumexpr optcnumexpr cposnumexpr
%type <is> acc_options acc_option menuitem_flags menuitem_flag
-%type <s> optstringc file_name
+%type <s> optstringc file_name resname
%type <i> sizednumexpr sizedposnumexpr
%left '|'
| input stringtable
| input user
| input versioninfo
+ | input IGNORED_TOKEN
;
/* Accelerator resources. */
id ACCELERATORS suboptions BEG acc_entries END
{
define_accelerator ($1, &$3, $5);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
;
{
struct accelerator *a;
- a = (struct accelerator *) xmalloc (sizeof *a);
+ a = (struct accelerator *) res_alloc (sizeof *a);
*a = $2;
if ($1 == NULL)
$$ = a;
$$ = $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
{
- char *s = $1;
+ const char *s = $1;
+ char ch;
+ $$.next = NULL;
$$.id = 0;
- if (*s != '^')
+ ch = *s;
+ if (ch != '^')
$$.flags = 0;
else
{
- $$.flags = ACC_CONTROL;
+ $$.flags = ACC_CONTROL | ACC_VIRTKEY;
++s;
+ ch = *s;
+ ch = TOUPPER (ch);
}
- $$.key = *s;
+ $$.key = ch;
if (s[1] != '\0')
- rcparse_warning ("accelerator should only be one character");
- free (s);
+ rcparse_warning (_("accelerator should only be one character"));
}
| posnumexpr
{
+ $$.next = NULL;
$$.flags = 0;
$$.id = 0;
$$.key = $1;
{
$$ = $1 | $3;
}
+ /* I've had one report that the comma is optional. */
+ | acc_options acc_option
+ {
+ $$ = $1 | $2;
+ }
;
acc_option:
id BITMAP memflags_move file_name
{
define_bitmap ($1, &$3, $4);
- free ($4);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
;
id CURSOR memflags_move_discard file_name
{
define_cursor ($1, &$3, $4);
- free ($4);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
;
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
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 = ((struct dialog_ex *)
- xmalloc (sizeof (struct dialog_ex)));
+ res_alloc (sizeof (struct dialog_ex)));
memset (dialog.ex, 0, sizeof (struct 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
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 = ((struct dialog_ex *)
- xmalloc (sizeof (struct dialog_ex)));
+ res_alloc (sizeof (struct dialog_ex)));
memset (dialog.ex, 0, sizeof (struct 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 ();
}
;
/* empty */
| styles CAPTION QUOTEDSTRING
{
- dialog.caption = $3;
+ dialog.style |= WS_CAPTION;
+ style |= WS_CAPTION;
+ unicode_from_ascii ((int *) NULL, &dialog.caption, $3);
}
| styles CLASS id
{
dialog.class = $3;
}
| styles STYLE
- { style = dialog.style }
styleexpr
{
dialog.style = style;
{
dialog.exstyle = $3;
}
+ | styles CLASS QUOTEDSTRING
+ {
+ res_string_to_id (& dialog.class, $3);
+ }
| styles FONT numexpr ',' QUOTEDSTRING
{
+ dialog.style |= DS_SETFONT;
+ style |= DS_SETFONT;
+ dialog.pointsize = $3;
+ unicode_from_ascii ((int *) NULL, &dialog.font, $5);
+ if (dialog.ex != NULL)
+ {
+ dialog.ex->weight = 0;
+ dialog.ex->italic = 0;
+ dialog.ex->charset = 1;
+ }
+ }
+ | styles FONT numexpr ',' QUOTEDSTRING cnumexpr
+ {
+ dialog.style |= DS_SETFONT;
+ style |= DS_SETFONT;
dialog.pointsize = $3;
- dialog.font = $5;
+ unicode_from_ascii ((int *) NULL, &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 ',' QUOTEDSTRING cnumexpr cnumexpr
{
+ dialog.style |= DS_SETFONT;
+ style |= DS_SETFONT;
+ dialog.pointsize = $3;
+ unicode_from_ascii ((int *) NULL, &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 ',' QUOTEDSTRING cnumexpr cnumexpr cnumexpr
+ {
+ dialog.style |= DS_SETFONT;
+ style |= DS_SETFONT;
dialog.pointsize = $3;
- dialog.font = $5;
+ unicode_from_ascii ((int *) NULL, &dialog.font, $5);
if (dialog.ex == NULL)
- rcparse_warning ("extended FONT requires DIALOGEX");
+ rcparse_warning (_("extended FONT requires DIALOGEX"));
else
{
dialog.ex->weight = $6;
dialog.ex->italic = $7;
+ dialog.ex->charset = $8;
}
}
| styles MENU id
}
| styles LANGUAGE numexpr cnumexpr
{
- sub_res_info.language = $3 | ($4 << 8);
+ sub_res_info.language = $3 | ($4 << SUBLANG_SHIFT);
}
- | styles VERSION numexpr
+ | styles VERSIONK numexpr
{
sub_res_info.version = $3;
}
{
$$ = $3;
if (dialog.ex == NULL)
- rcparse_warning ("IEDIT requires DIALOGEX");
+ rcparse_warning (_("BEDIT requires DIALOGEX"));
res_string_to_id (&$$->class, "BEDIT");
}
| CHECKBOX
if ($11 != NULL)
{
if (dialog.ex == NULL)
- rcparse_warning ("control data requires DIALOGEX");
+ rcparse_warning (_("control data requires DIALOGEX"));
$$->data = $11;
}
}
{
$$ = define_control ($2, $3, $6, $7, $8, $9, $4, style, $10);
if (dialog.ex == NULL)
- rcparse_warning ("help ID requires DIALOGEX");
+ rcparse_warning (_("help ID requires DIALOGEX"));
$$->help = $11;
$$->data = $12;
}
+ | CONTROL optstringc numexpr ',' QUOTEDSTRING control_styleexpr
+ cnumexpr cnumexpr cnumexpr cnumexpr optcnumexpr opt_control_data
+ {
+ $$ = define_control ($2, $3, $7, $8, $9, $10, 0, style, $11);
+ if ($12 != NULL)
+ {
+ if (dialog.ex == NULL)
+ rcparse_warning ("control data requires DIALOGEX");
+ $$->data = $12;
+ }
+ $$->class.named = 1;
+ unicode_from_ascii (&$$->class.u.n.length, &$$->class.u.n.name, $5);
+ }
+ | CONTROL optstringc numexpr ',' QUOTEDSTRING control_styleexpr
+ cnumexpr cnumexpr cnumexpr cnumexpr cnumexpr cnumexpr opt_control_data
+ {
+ $$ = define_control ($2, $3, $7, $8, $9, $10, 0, style, $11);
+ if (dialog.ex == NULL)
+ rcparse_warning ("help ID requires DIALOGEX");
+ $$->help = $12;
+ $$->data = $13;
+ $$->class.named = 1;
+ unicode_from_ascii (&$$->class.u.n.length, &$$->class.u.n.name, $5);
+ }
| CTEXT
{
default_style = SS_CENTER | WS_GROUP;
{
$$ = $3;
if (dialog.ex == NULL)
- rcparse_warning ("IEDIT requires DIALOGEX");
+ rcparse_warning (_("IEDIT requires DIALOGEX"));
res_string_to_id (&$$->class, "HEDIT");
}
- | ICON optstringc numexpr cnumexpr cnumexpr opt_control_data
- {
- $$ = define_control ($2, $3, $4, $5, 0, 0, CTL_STATIC,
- SS_ICON | WS_CHILD | WS_VISIBLE, 0);
- if ($6 != NULL)
- {
- if (dialog.ex == NULL)
- rcparse_warning ("control data requires DIALOGEX");
- $$->data = $6;
- }
- }
- | ICON optstringc numexpr cnumexpr cnumexpr cnumexpr cnumexpr
+ | 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_control ($2, $3, $4, $5, $6, $7, CTL_STATIC,
- style, $9);
- if ($10 != NULL)
- {
- if (dialog.ex == NULL)
- rcparse_warning ("control data requires DIALOGEX");
- $$->data = $10;
- }
- }
- | ICON optstringc numexpr cnumexpr cnumexpr cnumexpr cnumexpr
+ {
+ $$ = 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_control ($2, $3, $4, $5, $6, $7, CTL_STATIC,
- style, $9);
- if (dialog.ex == NULL)
- rcparse_warning ("help ID requires DIALOGEX");
- $$->help = $10;
- $$->data = $11;
- }
+ {
+ $$ = define_icon_control ($2, $3, $4, $5, style, $9, $10, $11,
+ dialog.ex);
+ }
| IEDIT
{
default_style = ES_LEFT | WS_BORDER | WS_TABSTOP;
{
$$ = $3;
if (dialog.ex == NULL)
- rcparse_warning ("IEDIT requires DIALOGEX");
+ rcparse_warning (_("IEDIT requires DIALOGEX"));
res_string_to_id (&$$->class, "IEDIT");
}
| LISTBOX
}
| USERBUTTON QUOTEDSTRING ',' numexpr ',' numexpr ',' numexpr ','
numexpr ',' numexpr ','
- { style = WS_CHILD | WS_VISIBLE }
+ { style = WS_CHILD | WS_VISIBLE; }
styleexpr optcnumexpr
{
$$ = define_control ($2, $4, $6, $8, $10, $12, CTL_BUTTON,
if ($7 != NULL)
{
if (dialog.ex == NULL)
- rcparse_warning ("control data requires DIALOGEX");
+ rcparse_warning (_("control data requires DIALOGEX"));
$$->data = $7;
}
}
if ($9 != NULL)
{
if (dialog.ex == NULL)
- rcparse_warning ("control data requires DIALOGEX");
+ rcparse_warning (_("control data requires DIALOGEX"));
$$->data = $9;
}
}
{
$$ = define_control ($1, $2, $3, $4, $5, $6, class, style, $8);
if (dialog.ex == NULL)
- rcparse_warning ("help ID requires DIALOGEX");
+ rcparse_warning (_("help ID requires DIALOGEX"));
$$->help = $9;
$$->data = $10;
}
{
$$ = NULL;
}
+ | QUOTEDSTRING
+ {
+ $$ = $1;
+ }
| QUOTEDSTRING ','
{
$$ = $1;
}
| BEG optrcdata_data END
{
- $$ = $2;
+ $$ = $2.first;
}
;
id FONT memflags_move_discard file_name
{
define_font ($1, &$3, $4);
- free ($4);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
;
id ICON memflags_move_discard file_name
{
define_icon ($1, &$3, $4);
- free ($4);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
;
language:
LANGUAGE numexpr cnumexpr
{
- language = $2 | ($3 << 8);
+ language = $2 | ($3 << SUBLANG_SHIFT);
}
;
id MENU suboptions BEG menuitems END
{
define_menu ($1, &$3, $5);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
;
id MENUEX suboptions BEG menuexitems END
{
define_menu ($1, &$3, $5);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
;
{
$$ = define_menuitem ($2, $3, $4, $5, 0, NULL);
}
+ | MENUITEM SEPARATOR
+ {
+ $$ = define_menuitem (NULL, 0, 0, 0, 0, NULL);
+ }
| POPUP QUOTEDSTRING BEG menuexitems END
{
$$ = define_menuitem ($2, 0, 0, 0, 0, $4);
id MESSAGETABLE memflags_move file_name
{
define_messagetable ($1, &$3, $4);
- free ($4);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
;
rcdata:
id RCDATA suboptions BEG optrcdata_data END
{
- define_rcdata ($1, &$3, $5);
+ define_rcdata ($1, &$3, $5.first);
+ 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 */
{
- $$ = NULL;
+ $$.first = NULL;
+ $$.last = NULL;
}
| rcdata_data
{
;
rcdata_data:
- QUOTEDSTRING
+ SIZEDSTRING
{
- $$ = append_rcdata_string (NULL, $1);
+ struct rcdata_item *ri;
+
+ ri = define_rcdata_string ($1.s, $1.length);
+ $$.first = ri;
+ $$.last = ri;
}
| sizednumexpr
{
- $$ = append_rcdata_number (NULL, $1.val, $1.dword);
+ struct rcdata_item *ri;
+
+ ri = define_rcdata_number ($1.val, $1.dword);
+ $$.first = ri;
+ $$.last = ri;
}
- | rcdata_data ',' QUOTEDSTRING
+ | rcdata_data ',' SIZEDSTRING
{
- $$ = append_rcdata_string ($1, $3);
+ struct rcdata_item *ri;
+
+ ri = define_rcdata_string ($3.s, $3.length);
+ $$.first = $1.first;
+ $1.last->next = ri;
+ $$.last = ri;
}
| rcdata_data ',' sizednumexpr
{
- $$ = append_rcdata_number ($1, $3.val, $3.dword);
+ struct rcdata_item *ri;
+
+ ri = define_rcdata_number ($3.val, $3.dword);
+ $$.first = $1.first;
+ $1.last->next = ri;
+ $$.last = ri;
}
;
| string_data numexpr QUOTEDSTRING
{
define_stringtable (&sub_res_info, $2, $3);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
| string_data numexpr ',' QUOTEDSTRING
{
define_stringtable (&sub_res_info, $2, $4);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
;
file_name case to keep the parser happy. */
user:
- id id suboptions BEG rcdata_data END
+ id id suboptions BEG optrcdata_data END
{
- define_user_data ($1, $2, &$3, $5);
+ define_user_data ($1, $2, &$3, $5.first);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
| id id suboptions file_name
{
define_user_file ($1, $2, &$3, $4);
- free ($4);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
;
id VERSIONINFO fixedverinfo BEG verblocks END
{
define_versioninfo ($1, language, $3, $5);
+ if (yychar != YYEMPTY)
+ YYERROR;
+ rcparse_discard_strings ();
}
;
/* empty */
{
$$ = ((struct fixed_versioninfo *)
- xmalloc (sizeof (struct fixed_versioninfo)));
+ res_alloc (sizeof (struct fixed_versioninfo)));
memset ($$, 0, sizeof (struct fixed_versioninfo));
}
| fixedverinfo FILEVERSION numexpr cnumexpr cnumexpr cnumexpr
}
| STRING
{
- res_string_to_id (&$$, $1);
- free ($1);
+ char *copy, *s;
+
+ /* It seems that resource ID's are forced to upper case. */
+ copy = xstrdup ($1);
+ for (s = copy; *s != '\0'; s++)
+ *s = TOUPPER (*s);
+ res_string_to_id (&$$, copy);
+ free (copy);
+ }
+ ;
+
+/* A resource reference. */
+
+resname:
+ QUOTEDSTRING
+ {
+ $$ = $1;
+ }
+ | QUOTEDSTRING ','
+ {
+ $$ = $1;
+ }
+ | STRING ','
+ {
+ $$ = $1;
+ }
+ ;
+
+
+resref:
+ posnumexpr ','
+ {
+ $$.named = 0;
+ $$.u.id = $1;
+ }
+ | resname
+ {
+ char *copy, *s;
+
+ /* It seems that resource ID's are forced to upper case. */
+ copy = xstrdup ($1);
+ for (s = copy; *s != '\0'; s++)
+ *s = TOUPPER (*s);
+ res_string_to_id (&$$, copy);
+ free (copy);
}
;
memset (&$$, 0, sizeof (struct res_res_info));
$$.language = language;
/* FIXME: Is this the right default? */
- $$.memflags = MEMFLAG_MOVEABLE;
+ $$.memflags = MEMFLAG_MOVEABLE | MEMFLAG_PURE | MEMFLAG_DISCARDABLE;
}
| suboptions memflag
{
| suboptions LANGUAGE numexpr cnumexpr
{
$$ = $1;
- $$.language = $3 | ($4 << 8);
+ $$.language = $3 | ($4 << SUBLANG_SHIFT);
}
- | suboptions VERSION numexpr
+ | suboptions VERSIONK numexpr
{
$$ = $1;
$$.version = $3;
{
memset (&$$, 0, sizeof (struct res_res_info));
$$.language = language;
- $$.memflags = MEMFLAG_MOVEABLE;
+ $$.memflags = MEMFLAG_MOVEABLE | MEMFLAG_PURE | MEMFLAG_DISCARDABLE;
}
- | memflags_move_discard memflag
+ | memflags_move memflag
{
$$ = $1;
$$.memflags |= $2.on;