1 /* rclex.c -- lexer for Windows rc files parser */
3 /* Copyright 1997, 1998, 1999, 2001, 2002, 2003, 2005, 2006, 2007
4 Free Software Foundation, Inc.
6 Written by Kai Tietz, Onevision.
8 This file is part of GNU Binutils.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
25 /* This is a lexer used by the Windows rc file parser. It basically
26 just recognized a bunch of keywords. */
31 #include "libiberty.h"
32 #include "safe-ctype.h"
38 /* Whether we are in rcdata mode, in which we returns the lengths of
41 static int rcdata_mode
;
43 /* Whether we are supressing lines from cpp (including windows.h or
44 headers from your C sources may bring in externs and typedefs).
45 When active, we return IGNORED_TOKEN, which lets us ignore these
46 outside of resource constructs. Thus, it isn't required to protect
47 all the non-preprocessor lines in your header files with #ifdef
48 RC_INVOKED. It also means your RC file can't include other RC
49 files if they're named "*.h". Sorry. Name them *.rch or whatever. */
51 static int suppress_cpp_data
;
53 #define IGNORE_CPP(x) (suppress_cpp_data ? IGNORED_TOKEN : (x))
55 /* The first filename we detect in the cpp output. We use this to
56 tell included files from the original file. */
58 static char *initial_fn
;
60 /* List of allocated strings. */
64 struct alloc_string
*next
;
68 static struct alloc_string
*strings
;
76 #define K(KEY) { #KEY, KEY }
77 #define KRT(KEY) { #KEY, RT_##KEY }
79 static const struct rclex_keywords keywds
[] =
81 K(ACCELERATORS
), K(ALT
), K(ANICURSOR
), K(ANIICON
), K(ASCII
),
82 K(AUTO3STATE
), K(AUTOCHECKBOX
), K(AUTORADIOBUTTON
),
83 K(BEDIT
), { "BEGIN", BEG
}, K(BITMAP
), K(BLOCK
), K(BUTTON
),
84 K(CAPTION
), K(CHARACTERISTICS
), K(CHECKBOX
), K(CHECKED
),
85 K(CLASS
), K(COMBOBOX
), K(CONTROL
), K(CTEXT
), K(CURSOR
),
86 K(DEFPUSHBUTTON
), K(DIALOG
), K(DIALOGEX
), K(DISCARDABLE
),
87 K(DLGINCLUDE
), K(DLGINIT
),
88 K(EDITTEXT
), K(END
), K(EXSTYLE
),
89 K(FILEFLAGS
), K(FILEFLAGSMASK
), K(FILEOS
), K(FILESUBTYPE
),
90 K(FILETYPE
), K(FILEVERSION
), K(FIXED
), K(FONT
), K(FONTDIR
),
91 K(GRAYED
), KRT(GROUP_CURSOR
), KRT(GROUP_ICON
), K(GROUPBOX
),
92 K(HEDIT
), K(HELP
), K(HTML
),
93 K(ICON
), K(IEDIT
), K(IMPURE
), K(INACTIVE
),
94 K(LANGUAGE
), K(LISTBOX
), K(LOADONCALL
), K(LTEXT
),
95 K(MANIFEST
), K(MENU
), K(MENUBARBREAK
), K(MENUBREAK
),
96 K(MENUEX
), K(MENUITEM
), K(MESSAGETABLE
), K(MOVEABLE
),
98 K(PLUGPLAY
), K(POPUP
), K(PRELOAD
), K(PRODUCTVERSION
),
99 K(PURE
), K(PUSHBOX
), K(PUSHBUTTON
),
100 K(RADIOBUTTON
), K(RCDATA
), K(RTEXT
),
101 K(SCROLLBAR
), K(SEPARATOR
), K(SHIFT
), K(STATE3
),
102 K(STRINGTABLE
), K(STYLE
),
105 K(VALUE
), { "VERSION", VERSIONK
}, K(VERSIONINFO
),
110 /* External input stream from resrc */
111 extern FILE *cpp_pipe
;
113 /* Lexical scanner helpers. */
114 static int rclex_lastch
= -1;
115 static size_t rclex_tok_max
= 0;
116 static size_t rclex_tok_pos
= 0;
117 static char *rclex_tok
= NULL
;
120 rclex_translatekeyword (const char *key
)
122 if (key
&& ISUPPER (key
[0]))
124 const struct rclex_keywords
*kw
= &keywds
[0];
128 if (! strcmp (kw
->name
, key
))
132 while (kw
->name
!= NULL
);
137 /* Handle a C preprocessor line. */
142 const char *s
= rclex_tok
;
150 line
= strtol (s
, &send
, 0);
151 if (*send
!= '\0' && ! ISSPACE (*send
))
154 /* Subtract 1 because we are about to count the newline. */
155 rc_lineno
= line
- 1;
165 send
= strchr (s
, '"');
169 fn
= xmalloc (send
- s
+ 1);
170 strncpy (fn
, s
, send
- s
);
178 initial_fn
= xmalloc (strlen (fn
) + 1);
179 strcpy (initial_fn
, fn
);
182 /* Allow the initial file, regardless of name. Suppress all other
183 files if they end in ".h" (this allows included "*.rc"). */
184 if (strcmp (initial_fn
, fn
) == 0
185 || strcmp (fn
+ strlen (fn
) - 2, ".h") != 0)
186 suppress_cpp_data
= 0;
188 suppress_cpp_data
= 1;
191 /* Allocate a string of a given length. */
196 struct alloc_string
*as
;
198 as
= xmalloc (sizeof *as
);
199 as
->s
= xmalloc (len
);
207 /* Handle a quoted string. The quotes are stripped. A pair of quotes
208 in a string are turned into a single quote. Adjacent strings are
209 merged separated by whitespace are merged, as in C. */
212 handle_quotes (rc_uint_type
*len
)
214 const char *input
= rclex_tok
;
220 ret
= get_string (strlen (input
) + 1);
234 rcparse_warning ("backslash at end of string");
238 rcparse_warning ("use \"\" to put \" in a string");
244 *s
++ = ESCAPE_B
; /* Strange, but true... */
282 case '0': case '1': case '2': case '3':
283 case '4': case '5': case '6': case '7':
286 if (*t
>= '0' && *t
<= '7')
288 ch
= (ch
<< 3) | (*t
- '0');
290 if (*t
>= '0' && *t
<= '7')
292 ch
= (ch
<< 3) | (*t
- '0');
302 /* We only handle single byte chars here. Make sure
303 we finish an escape sequence like "/xB0ABC" after
304 the first two digits. */
306 while (num_xdigits
--)
308 if (*t
>= '0' && *t
<= '9')
309 ch
= (ch
<< 4) | (*t
- '0');
310 else if (*t
>= 'a' && *t
<= 'f')
311 ch
= (ch
<< 4) | (*t
- 'a' + 10);
312 else if (*t
>= 'A' && *t
<= 'F')
313 ch
= (ch
<< 4) | (*t
- 'A' + 10);
322 rcparse_warning ("unrecognized escape sequence");
330 else if (t
[1] == '\0')
332 else if (t
[1] == '"')
339 rcparse_warning ("unexpected character after '\"'");
341 assert (ISSPACE (*t
));
362 /* Allocate a unicode string of a given length. */
365 get_unistring (int len
)
367 return (unichar
*) get_string (len
* sizeof (unichar
));
370 /* Handle a quoted unicode string. The quotes are stripped. A pair of quotes
371 in a string are turned into a single quote. Adjacent strings are
372 merged separated by whitespace are merged, as in C. */
375 handle_uniquotes (rc_uint_type
*len
)
377 const char *input
= rclex_tok
;
383 ret
= get_unistring (strlen (input
) + 1);
387 if ((*t
== 'L' || *t
== 'l') && t
[1] == '"')
399 rcparse_warning ("backslash at end of string");
403 rcparse_warning ("use \"\" to put \" in a string");
407 *s
++ = ESCAPE_B
; /* Strange, but true... */
442 *s
++ = (unichar
) *t
++;
445 case '0': case '1': case '2': case '3':
446 case '4': case '5': case '6': case '7':
449 if (*t
>= '0' && *t
<= '7')
451 ch
= (ch
<< 3) | (*t
- '0');
453 if (*t
>= '0' && *t
<= '7')
455 ch
= (ch
<< 3) | (*t
- '0');
465 /* We only handle two byte chars here. Make sure
466 we finish an escape sequence like "/xB0ABC" after
467 the first two digits. */
469 while (num_xdigits
--)
471 if (*t
>= '0' && *t
<= '9')
472 ch
= (ch
<< 4) | (*t
- '0');
473 else if (*t
>= 'a' && *t
<= 'f')
474 ch
= (ch
<< 4) | (*t
- 'a' + 10);
475 else if (*t
>= 'A' && *t
<= 'F')
476 ch
= (ch
<< 4) | (*t
- 'A' + 10);
485 rcparse_warning ("unrecognized escape sequence");
487 *s
++ = (unichar
) *t
++;
492 *s
++ = (unichar
) *t
++;
493 else if (t
[1] == '\0')
495 else if (t
[1] == '"')
503 assert (ISSPACE (*t
));
524 /* Discard all the strings we have allocated. The parser calls this
525 when it no longer needs them. */
528 rcparse_discard_strings (void)
530 struct alloc_string
*as
;
535 struct alloc_string
*n
;
546 /* Enter rcdata mode. */
548 rcparse_rcdata (void)
553 /* Go back to normal mode from rcdata mode. */
555 rcparse_normal (void)
561 rclex_tok_add_char (int ch
)
563 if (! rclex_tok
|| rclex_tok_max
<= rclex_tok_pos
)
565 char *h
= xmalloc (rclex_tok_max
+ 9);
571 memcpy (h
, rclex_tok
, rclex_tok_pos
+ 1);
580 rclex_tok
[rclex_tok_pos
++] = (char) ch
;
581 rclex_tok
[rclex_tok_pos
] = 0;
589 if ((r
= rclex_lastch
) != -1)
596 if (! cpp_pipe
|| feof (cpp_pipe
)
597 || fread (&ch
, 1, 1,cpp_pipe
) != 1)
599 r
= ((int) ch
) & 0xff;
601 while (r
== 0 || r
== '\r');
603 rclex_tok_add_char (r
);
612 if ((r
= rclex_lastch
) == -1)
614 if ((r
= rclex_readch ()) != -1)
617 if (rclex_tok_pos
> 0)
618 rclex_tok
[--rclex_tok_pos
] = 0;
629 while ((c
= rclex_peekch ()) != -1)
636 if ((c
= rclex_peekch ()) == -1 || c
== '\n')
640 else if (rclex_readch () == '"')
642 if (rclex_peekch () == '"')
653 rc_uint_type base
= 10;
654 rc_uint_type ret
, val
;
661 switch (rclex_peekch ())
675 ret
= (rc_uint_type
) (ch
- '0');
676 while ((ch
= rclex_peekch ()) != -1)
679 val
= (rc_uint_type
) (ch
- '0');
680 else if (ch
>= 'a' && ch
<= 'f')
681 val
= (rc_uint_type
) ((ch
- 'a') + 10);
682 else if (ch
>= 'A' && ch
<= 'F')
683 val
= (rc_uint_type
) ((ch
- 'A') + 10);
687 if (! warned
&& val
>= base
)
690 rcparse_warning ("digit exceeds base");
698 /* yyparser entry method. */
708 /* Make sure that rclex_tok is initialized. */
710 rclex_tok_add_char (-1);
720 if ((ch
= rclex_readch ()) == -1)
730 while ((ch
= rclex_peekch ()) != -1 && ch
!= '\n')
737 ch
= IGNORE_CPP (BEG
);
741 ch
= IGNORE_CPP (END
);
744 case '0': case '1': case '2': case '3': case '4':
745 case '5': case '6': case '7': case '8': case '9':
746 yylval
.i
.val
= read_digit (ch
);
748 switch (rclex_peekch ())
755 ch
= IGNORE_CPP (NUMBER
);
759 ch
= IGNORE_CPP ((! rcdata_mode
? QUOTEDSTRING
: SIZEDSTRING
));
760 if (ch
== IGNORED_TOKEN
)
762 s
= handle_quotes (&length
);
767 yylval
.ss
.length
= length
;
772 if (rclex_peekch () == '"')
776 ch
= IGNORE_CPP ((! rcdata_mode
? QUOTEDUNISTRING
: SIZEDUNISTRING
));
777 if (ch
== IGNORED_TOKEN
)
779 us
= handle_uniquotes (&length
);
784 yylval
.suni
.length
= length
;
791 if (ISIDST (ch
) || ch
=='$')
793 while ((ch
= rclex_peekch ()) != -1 && (ISIDNUM (ch
) || ch
== '$' || ch
== '.'))
795 ch
= IGNORE_CPP (rclex_translatekeyword (rclex_tok
));
798 s
= get_string (strlen (rclex_tok
) + 1);
799 strcpy (s
, rclex_tok
);
802 else if (ch
== BLOCK
)
804 const char *hs
= NULL
;
813 hs
= yylval
.s
= yylval
.ss
.s
;
818 rcparse_warning ("BLOCK expects a string as argument.");
821 else if (! strcmp (hs
, "StringFileInfo"))
822 ch
= BLOCKSTRINGFILEINFO
;
823 else if (! strcmp (hs
, "VarFileInfo"))
824 ch
= BLOCKVARFILEINFO
;
828 ch
= IGNORE_CPP (ch
);
832 while (ch
== IGNORED_TOKEN
);