%{ /* rclex.l -- lexer for Windows rc files parser */ /* Copyright 1997, 1998, 1999, 2001, 2002, 2003 Free Software Foundation, Inc. Written by Ian Lance Taylor, Cygnus Support. 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This is a lex input file which generates a lexer used by the Windows rc file parser. It basically just recognized a bunch of keywords. */ #include "bfd.h" #include "bucomm.h" #include "libiberty.h" #include "safe-ctype.h" #include "windres.h" #include "rcparse.h" #include <assert.h> /* Whether we are in rcdata mode, in which we returns the lengths of strings. */ static int rcdata_mode; /* Whether we are supressing lines from cpp (including windows.h or headers from your C sources may bring in externs and typedefs). When active, we return IGNORED_TOKEN, which lets us ignore these outside of resource constructs. Thus, it isn't required to protect all the non-preprocessor lines in your header files with #ifdef RC_INVOKED. It also means your RC file can't include other RC files if they're named "*.h". Sorry. Name them *.rch or whatever. */ static int suppress_cpp_data; #define MAYBE_RETURN(x) return suppress_cpp_data ? IGNORED_TOKEN : (x) /* The first filename we detect in the cpp output. We use this to tell included files from the original file. */ static char *initial_fn; /* List of allocated strings. */ struct alloc_string { struct alloc_string *next; char *s; }; static struct alloc_string *strings; /* Local functions. */ static void cpp_line (const char *); static char *handle_quotes (const char *, unsigned long *); static char *get_string (int); %} %% "BEGIN" { MAYBE_RETURN (BEG); } "{" { MAYBE_RETURN (BEG); } "END" { MAYBE_RETURN (END); } "}" { MAYBE_RETURN (END); } "ACCELERATORS" { MAYBE_RETURN (ACCELERATORS); } "VIRTKEY" { MAYBE_RETURN (VIRTKEY); } "ASCII" { MAYBE_RETURN (ASCII); } "NOINVERT" { MAYBE_RETURN (NOINVERT); } "SHIFT" { MAYBE_RETURN (SHIFT); } "CONTROL" { MAYBE_RETURN (CONTROL); } "ALT" { MAYBE_RETURN (ALT); } "BITMAP" { MAYBE_RETURN (BITMAP); } "CURSOR" { MAYBE_RETURN (CURSOR); } "DIALOG" { MAYBE_RETURN (DIALOG); } "DIALOGEX" { MAYBE_RETURN (DIALOGEX); } "EXSTYLE" { MAYBE_RETURN (EXSTYLE); } "CAPTION" { MAYBE_RETURN (CAPTION); } "CLASS" { MAYBE_RETURN (CLASS); } "STYLE" { MAYBE_RETURN (STYLE); } "AUTO3STATE" { MAYBE_RETURN (AUTO3STATE); } "AUTOCHECKBOX" { MAYBE_RETURN (AUTOCHECKBOX); } "AUTORADIOBUTTON" { MAYBE_RETURN (AUTORADIOBUTTON); } "CHECKBOX" { MAYBE_RETURN (CHECKBOX); } "COMBOBOX" { MAYBE_RETURN (COMBOBOX); } "CTEXT" { MAYBE_RETURN (CTEXT); } "DEFPUSHBUTTON" { MAYBE_RETURN (DEFPUSHBUTTON); } "EDITTEXT" { MAYBE_RETURN (EDITTEXT); } "GROUPBOX" { MAYBE_RETURN (GROUPBOX); } "LISTBOX" { MAYBE_RETURN (LISTBOX); } "LTEXT" { MAYBE_RETURN (LTEXT); } "PUSHBOX" { MAYBE_RETURN (PUSHBOX); } "PUSHBUTTON" { MAYBE_RETURN (PUSHBUTTON); } "RADIOBUTTON" { MAYBE_RETURN (RADIOBUTTON); } "RTEXT" { MAYBE_RETURN (RTEXT); } "SCROLLBAR" { MAYBE_RETURN (SCROLLBAR); } "STATE3" { MAYBE_RETURN (STATE3); } "USERBUTTON" { MAYBE_RETURN (USERBUTTON); } "BEDIT" { MAYBE_RETURN (BEDIT); } "HEDIT" { MAYBE_RETURN (HEDIT); } "IEDIT" { MAYBE_RETURN (IEDIT); } "FONT" { MAYBE_RETURN (FONT); } "ICON" { MAYBE_RETURN (ICON); } "LANGUAGE" { MAYBE_RETURN (LANGUAGE); } "CHARACTERISTICS" { MAYBE_RETURN (CHARACTERISTICS); } "VERSION" { MAYBE_RETURN (VERSIONK); } "MENU" { MAYBE_RETURN (MENU); } "MENUEX" { MAYBE_RETURN (MENUEX); } "MENUITEM" { MAYBE_RETURN (MENUITEM); } "SEPARATOR" { MAYBE_RETURN (SEPARATOR); } "POPUP" { MAYBE_RETURN (POPUP); } "CHECKED" { MAYBE_RETURN (CHECKED); } "GRAYED" { MAYBE_RETURN (GRAYED); } "HELP" { MAYBE_RETURN (HELP); } "INACTIVE" { MAYBE_RETURN (INACTIVE); } "MENUBARBREAK" { MAYBE_RETURN (MENUBARBREAK); } "MENUBREAK" { MAYBE_RETURN (MENUBREAK); } "MESSAGETABLE" { MAYBE_RETURN (MESSAGETABLE); } "RCDATA" { MAYBE_RETURN (RCDATA); } "STRINGTABLE" { MAYBE_RETURN (STRINGTABLE); } "VERSIONINFO" { MAYBE_RETURN (VERSIONINFO); } "FILEVERSION" { MAYBE_RETURN (FILEVERSION); } "PRODUCTVERSION" { MAYBE_RETURN (PRODUCTVERSION); } "FILEFLAGSMASK" { MAYBE_RETURN (FILEFLAGSMASK); } "FILEFLAGS" { MAYBE_RETURN (FILEFLAGS); } "FILEOS" { MAYBE_RETURN (FILEOS); } "FILETYPE" { MAYBE_RETURN (FILETYPE); } "FILESUBTYPE" { MAYBE_RETURN (FILESUBTYPE); } "VALUE" { MAYBE_RETURN (VALUE); } "MOVEABLE" { MAYBE_RETURN (MOVEABLE); } "FIXED" { MAYBE_RETURN (FIXED); } "PURE" { MAYBE_RETURN (PURE); } "IMPURE" { MAYBE_RETURN (IMPURE); } "PRELOAD" { MAYBE_RETURN (PRELOAD); } "LOADONCALL" { MAYBE_RETURN (LOADONCALL); } "DISCARDABLE" { MAYBE_RETURN (DISCARDABLE); } "NOT" { MAYBE_RETURN (NOT); } "BLOCK"[ \t\n]*"\""[^\#\n]*"\"" { char *s, *send; /* This is a hack to let us parse version information easily. */ s = strchr (yytext, '"'); ++s; send = strchr (s, '"'); if (strncmp (s, "StringFileInfo", sizeof "StringFileInfo" - 1) == 0 && s + sizeof "StringFileInfo" - 1 == send) MAYBE_RETURN (BLOCKSTRINGFILEINFO); else if (strncmp (s, "VarFileInfo", sizeof "VarFileInfo" - 1) == 0 && s + sizeof "VarFileInfo" - 1 == send) MAYBE_RETURN (BLOCKVARFILEINFO); else { char *r; r = get_string (send - s + 1); strncpy (r, s, send - s); r[send - s] = '\0'; yylval.s = r; MAYBE_RETURN (BLOCK); } } "#"[^\n]* { cpp_line (yytext); } [0-9][x0-9A-Fa-f]*L { yylval.i.val = strtoul (yytext, 0, 0); yylval.i.dword = 1; MAYBE_RETURN (NUMBER); } [0-9][x0-9A-Fa-f]* { yylval.i.val = strtoul (yytext, 0, 0); yylval.i.dword = 0; MAYBE_RETURN (NUMBER); } ("\""[^\"\n]*"\""[ \t\n]*)+ { char *s; unsigned long length; s = handle_quotes (yytext, &length); if (! rcdata_mode) { yylval.s = s; MAYBE_RETURN (QUOTEDSTRING); } else { yylval.ss.length = length; yylval.ss.s = s; MAYBE_RETURN (SIZEDSTRING); } } [A-Za-z][^ ,\t\r\n]* { char *s; /* I rejected comma in a string in order to handle VIRTKEY, CONTROL in an accelerator resource. This means that an unquoted file name can not contain a comma. I don't know what rc permits. */ s = get_string (strlen (yytext) + 1); strcpy (s, yytext); yylval.s = s; MAYBE_RETURN (STRING); } [\n] { ++rc_lineno; } [ \t\r]+ { /* ignore whitespace */ } . { MAYBE_RETURN (*yytext); } %% #ifndef yywrap /* This is needed for some versions of lex. */ int yywrap (void) { return 1; } #endif /* Handle a C preprocessor line. */ static void cpp_line (const char *s) { int line; char *send, *fn; ++s; while (ISSPACE (*s)) ++s; line = strtol (s, &send, 0); if (*send != '\0' && ! ISSPACE (*send)) return; /* Subtract 1 because we are about to count the newline. */ rc_lineno = line - 1; s = send; while (ISSPACE (*s)) ++s; if (*s != '"') return; ++s; send = strchr (s, '"'); if (send == NULL) return; fn = (char *) xmalloc (send - s + 1); strncpy (fn, s, send - s); fn[send - s] = '\0'; free (rc_filename); rc_filename = fn; if (!initial_fn) { initial_fn = xmalloc (strlen (fn) + 1); strcpy (initial_fn, fn); } /* Allow the initial file, regardless of name. Suppress all other files if they end in ".h" (this allows included "*.rc"). */ if (strcmp (initial_fn, fn) == 0 || strcmp (fn + strlen (fn) - 2, ".h") != 0) suppress_cpp_data = 0; else suppress_cpp_data = 1; } /* Handle a quoted string. The quotes are stripped. A pair of quotes in a string are turned into a single quote. Adjacent strings are merged separated by whitespace are merged, as in C. */ static char * handle_quotes (const char *input, unsigned long *len) { char *ret, *s; const char *t; int ch; ret = get_string (strlen (input) + 1); s = ret; t = input; if (*t == '"') ++t; while (*t != '\0') { if (*t == '\\') { ++t; switch (*t) { case '\0': rcparse_warning ("backslash at end of string"); break; case '\"': rcparse_warning ("use \"\" to put \" in a string"); break; case 'a': *s++ = ESCAPE_B; /* Strange, but true... */ ++t; break; case 'b': *s++ = ESCAPE_B; ++t; break; case 'f': *s++ = ESCAPE_F; ++t; break; case 'n': *s++ = ESCAPE_N; ++t; break; case 'r': *s++ = ESCAPE_R; ++t; break; case 't': *s++ = ESCAPE_T; ++t; break; case 'v': *s++ = ESCAPE_V; ++t; break; case '\\': *s++ = *t++; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': ch = *t - '0'; ++t; if (*t >= '0' && *t <= '7') { ch = (ch << 3) | (*t - '0'); ++t; if (*t >= '0' && *t <= '7') { ch = (ch << 3) | (*t - '0'); ++t; } } *s++ = ch; break; case 'x': ++t; ch = 0; while (1) { if (*t >= '0' && *t <= '9') ch = (ch << 4) | (*t - '0'); else if (*t >= 'a' && *t <= 'f') ch = (ch << 4) | (*t - 'a' + 10); else if (*t >= 'A' && *t <= 'F') ch = (ch << 4) | (*t - 'A' + 10); else break; ++t; } *s++ = ch; break; default: rcparse_warning ("unrecognized escape sequence"); *s++ = '\\'; *s++ = *t++; break; } } else if (*t != '"') *s++ = *t++; else if (t[1] == '\0') break; else if (t[1] == '"') { *s++ = '"'; t += 2; } else { ++t; assert (ISSPACE (*t)); while (ISSPACE (*t)) { if ((*t) == '\n') ++rc_lineno; ++t; } if (*t == '\0') break; assert (*t == '"'); ++t; } } *s = '\0'; *len = s - ret; return ret; } /* Allocate a string of a given length. */ static char * get_string (int len) { struct alloc_string *as; as = (struct alloc_string *) xmalloc (sizeof *as); as->s = xmalloc (len); as->next = strings; strings = as; return as->s; } /* Discard all the strings we have allocated. The parser calls this when it no longer needs them. */ void rcparse_discard_strings (void) { struct alloc_string *as; as = strings; while (as != NULL) { struct alloc_string *n; free (as->s); n = as->next; free (as); as = n; } strings = NULL; } /* Enter rcdata mode. */ void rcparse_rcdata (void) { rcdata_mode = 1; } /* Go back to normal mode from rcdata mode. */ void rcparse_normal (void) { rcdata_mode = 0; }