1 %{ /* rclex.l -- lexer for Windows rc files parser */ 2 /* Copyright 1997, 1998, 1999, 2001, 2002, 2003, 2005 3 Free Software Foundation, Inc. 4 Written by Ian Lance Taylor, Cygnus Support. 5 6 This file is part of GNU Binutils. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 21 02110-1301, USA. */ 22 23 /* This is a lex input file which generates a lexer used by the 24 Windows rc file parser. It basically just recognized a bunch of 25 keywords. */ 26 27 #include "bfd.h" 28 #include "bucomm.h" 29 #include "libiberty.h" 30 #include "safe-ctype.h" 31 #include "windres.h" 32 #include "rcparse.h" 33 34 #include <assert.h> 35 36 #define YY_NO_UNPUT 37 38 /* Whether we are in rcdata mode, in which we returns the lengths of 39 strings. */ 40 41 static int rcdata_mode; 42 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. */ 50 51 static int suppress_cpp_data; 52 53 #define MAYBE_RETURN(x) return suppress_cpp_data ? IGNORED_TOKEN : (x) 54 55 /* The first filename we detect in the cpp output. We use this to 56 tell included files from the original file. */ 57 58 static char *initial_fn; 59 60 /* List of allocated strings. */ 61 62 struct alloc_string 63 { 64 struct alloc_string *next; 65 char *s; 66 }; 67 68 static struct alloc_string *strings; 69 70 /* Local functions. */ 71 72 static void cpp_line (const char *); 73 static char *handle_quotes (const char *, unsigned long *); 74 static char *get_string (int); 75 76 %} 77 78 %% 79 80 "BEGIN" { MAYBE_RETURN (BEG); } 81 "{" { MAYBE_RETURN (BEG); } 82 "END" { MAYBE_RETURN (END); } 83 "}" { MAYBE_RETURN (END); } 84 "ACCELERATORS" { MAYBE_RETURN (ACCELERATORS); } 85 "VIRTKEY" { MAYBE_RETURN (VIRTKEY); } 86 "ASCII" { MAYBE_RETURN (ASCII); } 87 "NOINVERT" { MAYBE_RETURN (NOINVERT); } 88 "SHIFT" { MAYBE_RETURN (SHIFT); } 89 "CONTROL" { MAYBE_RETURN (CONTROL); } 90 "ALT" { MAYBE_RETURN (ALT); } 91 "BITMAP" { MAYBE_RETURN (BITMAP); } 92 "CURSOR" { MAYBE_RETURN (CURSOR); } 93 "DIALOG" { MAYBE_RETURN (DIALOG); } 94 "DIALOGEX" { MAYBE_RETURN (DIALOGEX); } 95 "EXSTYLE" { MAYBE_RETURN (EXSTYLE); } 96 "CAPTION" { MAYBE_RETURN (CAPTION); } 97 "CLASS" { MAYBE_RETURN (CLASS); } 98 "STYLE" { MAYBE_RETURN (STYLE); } 99 "AUTO3STATE" { MAYBE_RETURN (AUTO3STATE); } 100 "AUTOCHECKBOX" { MAYBE_RETURN (AUTOCHECKBOX); } 101 "AUTORADIOBUTTON" { MAYBE_RETURN (AUTORADIOBUTTON); } 102 "CHECKBOX" { MAYBE_RETURN (CHECKBOX); } 103 "COMBOBOX" { MAYBE_RETURN (COMBOBOX); } 104 "CTEXT" { MAYBE_RETURN (CTEXT); } 105 "DEFPUSHBUTTON" { MAYBE_RETURN (DEFPUSHBUTTON); } 106 "EDITTEXT" { MAYBE_RETURN (EDITTEXT); } 107 "GROUPBOX" { MAYBE_RETURN (GROUPBOX); } 108 "LISTBOX" { MAYBE_RETURN (LISTBOX); } 109 "LTEXT" { MAYBE_RETURN (LTEXT); } 110 "PUSHBOX" { MAYBE_RETURN (PUSHBOX); } 111 "PUSHBUTTON" { MAYBE_RETURN (PUSHBUTTON); } 112 "RADIOBUTTON" { MAYBE_RETURN (RADIOBUTTON); } 113 "RTEXT" { MAYBE_RETURN (RTEXT); } 114 "SCROLLBAR" { MAYBE_RETURN (SCROLLBAR); } 115 "STATE3" { MAYBE_RETURN (STATE3); } 116 "USERBUTTON" { MAYBE_RETURN (USERBUTTON); } 117 "BEDIT" { MAYBE_RETURN (BEDIT); } 118 "HEDIT" { MAYBE_RETURN (HEDIT); } 119 "IEDIT" { MAYBE_RETURN (IEDIT); } 120 "FONT" { MAYBE_RETURN (FONT); } 121 "ICON" { MAYBE_RETURN (ICON); } 122 "LANGUAGE" { MAYBE_RETURN (LANGUAGE); } 123 "CHARACTERISTICS" { MAYBE_RETURN (CHARACTERISTICS); } 124 "VERSION" { MAYBE_RETURN (VERSIONK); } 125 "MENU" { MAYBE_RETURN (MENU); } 126 "MENUEX" { MAYBE_RETURN (MENUEX); } 127 "MENUITEM" { MAYBE_RETURN (MENUITEM); } 128 "SEPARATOR" { MAYBE_RETURN (SEPARATOR); } 129 "POPUP" { MAYBE_RETURN (POPUP); } 130 "CHECKED" { MAYBE_RETURN (CHECKED); } 131 "GRAYED" { MAYBE_RETURN (GRAYED); } 132 "HELP" { MAYBE_RETURN (HELP); } 133 "INACTIVE" { MAYBE_RETURN (INACTIVE); } 134 "MENUBARBREAK" { MAYBE_RETURN (MENUBARBREAK); } 135 "MENUBREAK" { MAYBE_RETURN (MENUBREAK); } 136 "MESSAGETABLE" { MAYBE_RETURN (MESSAGETABLE); } 137 "RCDATA" { MAYBE_RETURN (RCDATA); } 138 "STRINGTABLE" { MAYBE_RETURN (STRINGTABLE); } 139 "VERSIONINFO" { MAYBE_RETURN (VERSIONINFO); } 140 "FILEVERSION" { MAYBE_RETURN (FILEVERSION); } 141 "PRODUCTVERSION" { MAYBE_RETURN (PRODUCTVERSION); } 142 "FILEFLAGSMASK" { MAYBE_RETURN (FILEFLAGSMASK); } 143 "FILEFLAGS" { MAYBE_RETURN (FILEFLAGS); } 144 "FILEOS" { MAYBE_RETURN (FILEOS); } 145 "FILETYPE" { MAYBE_RETURN (FILETYPE); } 146 "FILESUBTYPE" { MAYBE_RETURN (FILESUBTYPE); } 147 "VALUE" { MAYBE_RETURN (VALUE); } 148 "MOVEABLE" { MAYBE_RETURN (MOVEABLE); } 149 "FIXED" { MAYBE_RETURN (FIXED); } 150 "PURE" { MAYBE_RETURN (PURE); } 151 "IMPURE" { MAYBE_RETURN (IMPURE); } 152 "PRELOAD" { MAYBE_RETURN (PRELOAD); } 153 "LOADONCALL" { MAYBE_RETURN (LOADONCALL); } 154 "DISCARDABLE" { MAYBE_RETURN (DISCARDABLE); } 155 "NOT" { MAYBE_RETURN (NOT); } 156 157 "BLOCK"[ \t\n]*"\""[^\#\n]*"\"" { 158 char *s, *send; 159 160 /* This is a hack to let us parse version 161 information easily. */ 162 163 s = strchr (yytext, '"'); 164 ++s; 165 send = strchr (s, '"'); 166 if (strncmp (s, "StringFileInfo", 167 sizeof "StringFileInfo" - 1) == 0 168 && s + sizeof "StringFileInfo" - 1 == send) 169 MAYBE_RETURN (BLOCKSTRINGFILEINFO); 170 else if (strncmp (s, "VarFileInfo", 171 sizeof "VarFileInfo" - 1) == 0 172 && s + sizeof "VarFileInfo" - 1 == send) 173 MAYBE_RETURN (BLOCKVARFILEINFO); 174 else 175 { 176 char *r; 177 178 r = get_string (send - s + 1); 179 strncpy (r, s, send - s); 180 r[send - s] = '\0'; 181 yylval.s = r; 182 MAYBE_RETURN (BLOCK); 183 } 184 } 185 186 "#"[^\n]* { 187 cpp_line (yytext); 188 } 189 190 [0-9][x0-9A-Fa-f]*L { 191 yylval.i.val = strtoul (yytext, 0, 0); 192 yylval.i.dword = 1; 193 MAYBE_RETURN (NUMBER); 194 } 195 196 [0-9][x0-9A-Fa-f]* { 197 yylval.i.val = strtoul (yytext, 0, 0); 198 yylval.i.dword = 0; 199 MAYBE_RETURN (NUMBER); 200 } 201 202 ("\""[^\"\n]*"\""[ \t\n]*)+ { 203 char *s; 204 unsigned long length; 205 206 s = handle_quotes (yytext, &length); 207 if (! rcdata_mode) 208 { 209 yylval.s = s; 210 MAYBE_RETURN (QUOTEDSTRING); 211 } 212 else 213 { 214 yylval.ss.length = length; 215 yylval.ss.s = s; 216 MAYBE_RETURN (SIZEDSTRING); 217 } 218 } 219 220 [A-Za-z][^ ,\t\r\n]* { 221 char *s; 222 223 /* I rejected comma in a string in order to 224 handle VIRTKEY, CONTROL in an accelerator 225 resource. This means that an unquoted 226 file name can not contain a comma. I 227 don't know what rc permits. */ 228 229 s = get_string (strlen (yytext) + 1); 230 strcpy (s, yytext); 231 yylval.s = s; 232 MAYBE_RETURN (STRING); 233 } 234 235 [\n] { ++rc_lineno; } 236 [ \t\r]+ { /* ignore whitespace */ } 237 . { MAYBE_RETURN (*yytext); } 238 239 %% 240 #ifndef yywrap 241 /* This is needed for some versions of lex. */ 242 int yywrap (void) 243 { 244 return 1; 245 } 246 #endif 247 248 /* Handle a C preprocessor line. */ 249 250 static void 251 cpp_line (const char *s) 252 { 253 int line; 254 char *send, *fn; 255 256 ++s; 257 while (ISSPACE (*s)) 258 ++s; 259 260 line = strtol (s, &send, 0); 261 if (*send != '\0' && ! ISSPACE (*send)) 262 return; 263 264 /* Subtract 1 because we are about to count the newline. */ 265 rc_lineno = line - 1; 266 267 s = send; 268 while (ISSPACE (*s)) 269 ++s; 270 271 if (*s != '"') 272 return; 273 274 ++s; 275 send = strchr (s, '"'); 276 if (send == NULL) 277 return; 278 279 fn = (char *) xmalloc (send - s + 1); 280 strncpy (fn, s, send - s); 281 fn[send - s] = '\0'; 282 283 free (rc_filename); 284 rc_filename = fn; 285 286 if (!initial_fn) 287 { 288 initial_fn = xmalloc (strlen (fn) + 1); 289 strcpy (initial_fn, fn); 290 } 291 292 /* Allow the initial file, regardless of name. Suppress all other 293 files if they end in ".h" (this allows included "*.rc"). */ 294 if (strcmp (initial_fn, fn) == 0 295 || strcmp (fn + strlen (fn) - 2, ".h") != 0) 296 suppress_cpp_data = 0; 297 else 298 suppress_cpp_data = 1; 299 } 300 301 /* Handle a quoted string. The quotes are stripped. A pair of quotes 302 in a string are turned into a single quote. Adjacent strings are 303 merged separated by whitespace are merged, as in C. */ 304 305 static char * 306 handle_quotes (const char *input, unsigned long *len) 307 { 308 char *ret, *s; 309 const char *t; 310 int ch; 311 int num_xdigits; 312 313 ret = get_string (strlen (input) + 1); 314 315 s = ret; 316 t = input; 317 if (*t == '"') 318 ++t; 319 while (*t != '\0') 320 { 321 if (*t == '\\') 322 { 323 ++t; 324 switch (*t) 325 { 326 case '\0': 327 rcparse_warning ("backslash at end of string"); 328 break; 329 330 case '\"': 331 rcparse_warning ("use \"\" to put \" in a string"); 332 break; 333 334 case 'a': 335 *s++ = ESCAPE_B; /* Strange, but true... */ 336 ++t; 337 break; 338 339 case 'b': 340 *s++ = ESCAPE_B; 341 ++t; 342 break; 343 344 case 'f': 345 *s++ = ESCAPE_F; 346 ++t; 347 break; 348 349 case 'n': 350 *s++ = ESCAPE_N; 351 ++t; 352 break; 353 354 case 'r': 355 *s++ = ESCAPE_R; 356 ++t; 357 break; 358 359 case 't': 360 *s++ = ESCAPE_T; 361 ++t; 362 break; 363 364 case 'v': 365 *s++ = ESCAPE_V; 366 ++t; 367 break; 368 369 case '\\': 370 *s++ = *t++; 371 break; 372 373 case '0': case '1': case '2': case '3': 374 case '4': case '5': case '6': case '7': 375 ch = *t - '0'; 376 ++t; 377 if (*t >= '0' && *t <= '7') 378 { 379 ch = (ch << 3) | (*t - '0'); 380 ++t; 381 if (*t >= '0' && *t <= '7') 382 { 383 ch = (ch << 3) | (*t - '0'); 384 ++t; 385 } 386 } 387 *s++ = ch; 388 break; 389 390 case 'x': 391 ++t; 392 ch = 0; 393 /* We only handle single byte chars here. Make sure 394 we finish an escape sequence like "/xB0ABC" after 395 the first two digits. */ 396 num_xdigits = 2; 397 while (num_xdigits--) 398 { 399 if (*t >= '0' && *t <= '9') 400 ch = (ch << 4) | (*t - '0'); 401 else if (*t >= 'a' && *t <= 'f') 402 ch = (ch << 4) | (*t - 'a' + 10); 403 else if (*t >= 'A' && *t <= 'F') 404 ch = (ch << 4) | (*t - 'A' + 10); 405 else 406 break; 407 ++t; 408 } 409 *s++ = ch; 410 break; 411 412 default: 413 rcparse_warning ("unrecognized escape sequence"); 414 *s++ = '\\'; 415 *s++ = *t++; 416 break; 417 } 418 } 419 else if (*t != '"') 420 *s++ = *t++; 421 else if (t[1] == '\0') 422 break; 423 else if (t[1] == '"') 424 { 425 *s++ = '"'; 426 t += 2; 427 } 428 else 429 { 430 ++t; 431 assert (ISSPACE (*t)); 432 while (ISSPACE (*t)) 433 { 434 if ((*t) == '\n') 435 ++rc_lineno; 436 ++t; 437 } 438 if (*t == '\0') 439 break; 440 assert (*t == '"'); 441 ++t; 442 } 443 } 444 445 *s = '\0'; 446 447 *len = s - ret; 448 449 return ret; 450 } 451 452 /* Allocate a string of a given length. */ 453 454 static char * 455 get_string (int len) 456 { 457 struct alloc_string *as; 458 459 as = (struct alloc_string *) xmalloc (sizeof *as); 460 as->s = xmalloc (len); 461 462 as->next = strings; 463 strings = as; 464 465 return as->s; 466 } 467 468 /* Discard all the strings we have allocated. The parser calls this 469 when it no longer needs them. */ 470 471 void 472 rcparse_discard_strings (void) 473 { 474 struct alloc_string *as; 475 476 as = strings; 477 while (as != NULL) 478 { 479 struct alloc_string *n; 480 481 free (as->s); 482 n = as->next; 483 free (as); 484 as = n; 485 } 486 487 strings = NULL; 488 } 489 490 /* Enter rcdata mode. */ 491 492 void 493 rcparse_rcdata (void) 494 { 495 rcdata_mode = 1; 496 } 497 498 /* Go back to normal mode from rcdata mode. */ 499 500 void 501 rcparse_normal (void) 502 { 503 rcdata_mode = 0; 504 } 505