| %{ /* 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 <ctype.h> |
| #include <assert.h> |
| |
| /* 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; |
| } |