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., 59 Temple Place - Suite 330, Boston, MA 21 02111-1307, 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 312 ret = get_string (strlen (input) + 1); 313 314 s = ret; 315 t = input; 316 if (*t == '"') 317 ++t; 318 while (*t != '\0') 319 { 320 if (*t == '\\') 321 { 322 ++t; 323 switch (*t) 324 { 325 case '\0': 326 rcparse_warning ("backslash at end of string"); 327 break; 328 329 case '\"': 330 rcparse_warning ("use \"\" to put \" in a string"); 331 break; 332 333 case 'a': 334 *s++ = ESCAPE_B; /* Strange, but true... */ 335 ++t; 336 break; 337 338 case 'b': 339 *s++ = ESCAPE_B; 340 ++t; 341 break; 342 343 case 'f': 344 *s++ = ESCAPE_F; 345 ++t; 346 break; 347 348 case 'n': 349 *s++ = ESCAPE_N; 350 ++t; 351 break; 352 353 case 'r': 354 *s++ = ESCAPE_R; 355 ++t; 356 break; 357 358 case 't': 359 *s++ = ESCAPE_T; 360 ++t; 361 break; 362 363 case 'v': 364 *s++ = ESCAPE_V; 365 ++t; 366 break; 367 368 case '\\': 369 *s++ = *t++; 370 break; 371 372 case '0': case '1': case '2': case '3': 373 case '4': case '5': case '6': case '7': 374 ch = *t - '0'; 375 ++t; 376 if (*t >= '0' && *t <= '7') 377 { 378 ch = (ch << 3) | (*t - '0'); 379 ++t; 380 if (*t >= '0' && *t <= '7') 381 { 382 ch = (ch << 3) | (*t - '0'); 383 ++t; 384 } 385 } 386 *s++ = ch; 387 break; 388 389 case 'x': 390 ++t; 391 ch = 0; 392 while (1) 393 { 394 if (*t >= '0' && *t <= '9') 395 ch = (ch << 4) | (*t - '0'); 396 else if (*t >= 'a' && *t <= 'f') 397 ch = (ch << 4) | (*t - 'a' + 10); 398 else if (*t >= 'A' && *t <= 'F') 399 ch = (ch << 4) | (*t - 'A' + 10); 400 else 401 break; 402 ++t; 403 } 404 *s++ = ch; 405 break; 406 407 default: 408 rcparse_warning ("unrecognized escape sequence"); 409 *s++ = '\\'; 410 *s++ = *t++; 411 break; 412 } 413 } 414 else if (*t != '"') 415 *s++ = *t++; 416 else if (t[1] == '\0') 417 break; 418 else if (t[1] == '"') 419 { 420 *s++ = '"'; 421 t += 2; 422 } 423 else 424 { 425 ++t; 426 assert (ISSPACE (*t)); 427 while (ISSPACE (*t)) 428 { 429 if ((*t) == '\n') 430 ++rc_lineno; 431 ++t; 432 } 433 if (*t == '\0') 434 break; 435 assert (*t == '"'); 436 ++t; 437 } 438 } 439 440 *s = '\0'; 441 442 *len = s - ret; 443 444 return ret; 445 } 446 447 /* Allocate a string of a given length. */ 448 449 static char * 450 get_string (int len) 451 { 452 struct alloc_string *as; 453 454 as = (struct alloc_string *) xmalloc (sizeof *as); 455 as->s = xmalloc (len); 456 457 as->next = strings; 458 strings = as; 459 460 return as->s; 461 } 462 463 /* Discard all the strings we have allocated. The parser calls this 464 when it no longer needs them. */ 465 466 void 467 rcparse_discard_strings (void) 468 { 469 struct alloc_string *as; 470 471 as = strings; 472 while (as != NULL) 473 { 474 struct alloc_string *n; 475 476 free (as->s); 477 n = as->next; 478 free (as); 479 as = n; 480 } 481 482 strings = NULL; 483 } 484 485 /* Enter rcdata mode. */ 486 487 void 488 rcparse_rcdata (void) 489 { 490 rcdata_mode = 1; 491 } 492 493 /* Go back to normal mode from rcdata mode. */ 494 495 void 496 rcparse_normal (void) 497 { 498 rcdata_mode = 0; 499 } 500