%{ /* rclex.l -- lexer for Windows rc files parser */ /* Copyright 1997, 1998 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 "windres.h" #include "rcparse.h" #include #include /* Whether we are in rcdata mode, in which we returns the lengths of strings. */ static int rcdata_mode; /* List of allocated strings. */ struct alloc_string { struct alloc_string *next; char *s; }; static struct alloc_string *strings; /* Local functions. */ static void cpp_line PARAMS ((const char *)); static char *handle_quotes PARAMS ((const char *, unsigned long *)); static char *get_string PARAMS ((int)); %} %% "BEGIN" { return BEG; } "{" { return BEG; } "END" { return END; } "}" { return END; } "ACCELERATORS" { return ACCELERATORS; } "VIRTKEY" { return VIRTKEY; } "ASCII" { return ASCII; } "NOINVERT" { return NOINVERT; } "SHIFT" { return SHIFT; } "CONTROL" { return CONTROL; } "ALT" { return ALT; } "BITMAP" { return BITMAP; } "CURSOR" { return CURSOR; } "DIALOG" { return DIALOG; } "DIALOGEX" { return DIALOGEX; } "EXSTYLE" { return EXSTYLE; } "CAPTION" { return CAPTION; } "CLASS" { return CLASS; } "STYLE" { return STYLE; } "AUTO3STATE" { return AUTO3STATE; } "AUTOCHECKBOX" { return AUTOCHECKBOX; } "AUTORADIOBUTTON" { return AUTORADIOBUTTON; } "CHECKBOX" { return CHECKBOX; } "COMBOBOX" { return COMBOBOX; } "CTEXT" { return CTEXT; } "DEFPUSHBUTTON" { return DEFPUSHBUTTON; } "EDITTEXT" { return EDITTEXT; } "GROUPBOX" { return GROUPBOX; } "LISTBOX" { return LISTBOX; } "LTEXT" { return LTEXT; } "PUSHBOX" { return PUSHBOX; } "PUSHBUTTON" { return PUSHBUTTON; } "RADIOBUTTON" { return RADIOBUTTON; } "RTEXT" { return RTEXT; } "SCROLLBAR" { return SCROLLBAR; } "STATE3" { return STATE3; } "USERBUTTON" { return USERBUTTON; } "BEDIT" { return BEDIT; } "HEDIT" { return HEDIT; } "IEDIT" { return IEDIT; } "FONT" { return FONT; } "ICON" { return ICON; } "LANGUAGE" { return LANGUAGE; } "CHARACTERISTICS" { return CHARACTERISTICS; } "VERSION" { return VERSIONK; } "MENU" { return MENU; } "MENUEX" { return MENUEX; } "MENUITEM" { return MENUITEM; } "SEPARATOR" { return SEPARATOR; } "POPUP" { return POPUP; } "CHECKED" { return CHECKED; } "GRAYED" { return GRAYED; } "HELP" { return HELP; } "INACTIVE" { return INACTIVE; } "MENUBARBREAK" { return MENUBARBREAK; } "MENUBREAK" { return MENUBREAK; } "MESSAGETABLE" { return MESSAGETABLE; } "RCDATA" { return RCDATA; } "STRINGTABLE" { return STRINGTABLE; } "VERSIONINFO" { return VERSIONINFO; } "FILEVERSION" { return FILEVERSION; } "PRODUCTVERSION" { return PRODUCTVERSION; } "FILEFLAGSMASK" { return FILEFLAGSMASK; } "FILEFLAGS" { return FILEFLAGS; } "FILEOS" { return FILEOS; } "FILETYPE" { return FILETYPE; } "FILESUBTYPE" { return FILESUBTYPE; } "VALUE" { return VALUE; } "MOVEABLE" { return MOVEABLE; } "FIXED" { return FIXED; } "PURE" { return PURE; } "IMPURE" { return IMPURE; } "PRELOAD" { return PRELOAD; } "LOADONCALL" { return LOADONCALL; } "DISCARDABLE" { return DISCARDABLE; } "NOT" { 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) return BLOCKSTRINGFILEINFO; else if (strncmp (s, "VarFileInfo", sizeof "VarFileInfo" - 1) == 0 && s + sizeof "VarFileInfo" - 1 == send) return BLOCKVARFILEINFO; else { char *r; r = get_string (send - s + 1); strncpy (r, s, send - s); r[send - s] = '\0'; yylval.s = r; return BLOCK; } } "#"[^\n]* { cpp_line (yytext); } [0-9][x0-9A-Fa-f]*L { yylval.i.val = strtoul (yytext, 0, 0); yylval.i.dword = 1; return NUMBER; } [0-9][x0-9A-Fa-f]* { yylval.i.val = strtoul (yytext, 0, 0); yylval.i.dword = 0; return NUMBER; } ("\""[^\"\n]*"\""[ \t]*)+ { char *s; unsigned long length; s = handle_quotes (yytext, &length); if (! rcdata_mode) { yylval.s = s; return QUOTEDSTRING; } else { yylval.ss.length = length; yylval.ss.s = s; 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; return STRING; } [\n] { ++rc_lineno; } [ \t\r]+ { /* ignore whitespace */ } . { return *yytext; } %% #ifndef yywrap /* This is needed for some versions of lex. */ int yywrap () { return 1; } #endif /* Handle a C preprocessor line. */ static void cpp_line (s) const char *s; { int line; char *send, *fn; ++s; while (isspace ((unsigned char) *s)) ++s; line = strtol (s, &send, 0); if (*send != '\0' && ! isspace ((unsigned char) *send)) return; /* Subtract 1 because we are about to count the newline. */ rc_lineno = line - 1; s = send; while (isspace ((unsigned char) *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; } /* 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 (input, len) 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_A; ++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'); else if (*t >= 'A' && *t <= 'F') ch = (ch << 4) | (*t - 'A'); 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 ((unsigned char) *t)); while (isspace ((unsigned char) *t)) ++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 (len) int len; { struct alloc_string *as; as = (struct alloc_string *) xmalloc (sizeof *as); as->s = xmalloc (len); as->next = strings; strings = as->next; return as->s; } /* Discard all the strings we have allocated. The parser calls this when it no longer needs them. */ void rcparse_discard_strings () { 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 () { rcdata_mode = 1; } /* Go back to normal mode from rcdata mode. */ void rcparse_normal () { rcdata_mode = 0; }