
1: %{/* nlmheader.y - parse NLM header specification keywords. 2: Copyright 1993, 1994, 1995, 1997, 1998, 2001, 2002, 2003, 2007 3: Free Software Foundation, Inc. 4: 5: This file is part of GNU Binutils. 6: 7: This program is free software; you can redistribute it and/or modify 8: it under the terms of the GNU General Public License as published by 9: the Free Software Foundation; either version 3 of the License, or 10: (at your option) any later version. 11: 12: This program is distributed in the hope that it will be useful, 13: but WITHOUT ANY WARRANTY; without even the implied warranty of 14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15: GNU General Public License for more details. 16: 17: You should have received a copy of the GNU General Public License 18: along with this program; if not, write to the Free Software 19: Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 20: MA 02110-1301, USA. */ 21: 22: /* Written by Ian Lance Taylor <ian@cygnus.com>. 23: 24: This bison file parses the commands recognized by the NetWare NLM 25: linker, except for lists of object files. It stores the 26: information in global variables. 27: 28: This implementation is based on the description in the NetWare Tool 29: Maker Specification manual, edition 1.0. */ 30: 31: #include "sysdep.h" 32: #include "safe-ctype.h" 33: #include "bfd.h" 34: #include "nlm/common.h" 35: #include "nlm/internal.h" 36: #include "bucomm.h" 37: #include "nlmconv.h" 38: 39: /* Information is stored in the structures pointed to by these 40: variables. */ 41: 42: Nlm_Internal_Fixed_Header *fixed_hdr; 43: Nlm_Internal_Variable_Header *var_hdr; 44: Nlm_Internal_Version_Header *version_hdr; 45: Nlm_Internal_Copyright_Header *copyright_hdr; 46: Nlm_Internal_Extended_Header *extended_hdr; 47: 48: /* Procedure named by CHECK. */ 49: char *check_procedure; 50: /* File named by CUSTOM. */ 51: char *custom_file; 52: /* Whether to generate debugging information (DEBUG). */ 53: bfd_boolean debug_info; 54: /* Procedure named by EXIT. */ 55: char *exit_procedure; 56: /* Exported symbols (EXPORT). */ 57: struct string_list *export_symbols; 58: /* List of files from INPUT. */ 59: struct string_list *input_files; 60: /* Map file name (MAP, FULLMAP). */ 61: char *map_file; 62: /* Whether a full map has been requested (FULLMAP). */ 63: bfd_boolean full_map; 64: /* File named by HELP. */ 65: char *help_file; 66: /* Imported symbols (IMPORT). */ 67: struct string_list *import_symbols; 68: /* File named by MESSAGES. */ 69: char *message_file; 70: /* Autoload module list (MODULE). */ 71: struct string_list *modules; 72: /* File named by OUTPUT. */ 73: char *output_file; 74: /* File named by SHARELIB. */ 75: char *sharelib_file; 76: /* Start procedure name (START). */ 77: char *start_procedure; 78: /* VERBOSE. */ 79: bfd_boolean verbose; 80: /* RPC description file (XDCDATA). */ 81: char *rpc_file; 82: 83: /* The number of serious errors that have occurred. */ 84: int parse_errors; 85: 86: /* The current symbol prefix when reading a list of import or export 87: symbols. */ 88: static char *symbol_prefix; 89: 90: /* Parser error message handler. */ 91: #define yyerror(msg) nlmheader_error (msg); 92: 93: /* Local functions. */ 94: static int yylex (void); 95: static void nlmlex_file_push (const char *); 96: static bfd_boolean nlmlex_file_open (const char *); 97: static int nlmlex_buf_init (void); 98: static char nlmlex_buf_add (int); 99: static long nlmlex_get_number (const char *); 100: static void nlmheader_identify (void); 101: static void nlmheader_warn (const char *, int); 102: static void nlmheader_error (const char *); 103: static struct string_list * string_list_cons (char *, struct string_list *); 104: static struct string_list * string_list_append (struct string_list *, 105: struct string_list *); 106: static struct string_list * string_list_append1 (struct string_list *, 107: char *); 108: static char *xstrdup (const char *); 109: 110: %} 111: 112: %union 113: { 114: char *string; 115: struct string_list *list; 116: }; 117: 118: /* The reserved words. */ 119: 120: %token CHECK CODESTART COPYRIGHT CUSTOM DATE DEBUG DESCRIPTION EXIT 121: %token EXPORT FLAG_ON FLAG_OFF FULLMAP HELP IMPORT INPUT MAP MESSAGES 122: %token MODULE MULTIPLE OS_DOMAIN OUTPUT PSEUDOPREEMPTION REENTRANT 123: %token SCREENNAME SHARELIB STACK START SYNCHRONIZE 124: %token THREADNAME TYPE VERBOSE VERSIONK XDCDATA 125: 126: /* Arguments. */ 127: 128: %token <string> STRING 129: %token <string> QUOTED_STRING 130: 131: /* Typed non-terminals. */ 132: %type <list> symbol_list_opt symbol_list string_list 133: %type <string> symbol 134: 135: %% 136: 137: /* Keywords must start in the leftmost column of the file. Arguments 138: may appear anywhere else. The lexer uses this to determine what 139: token to return, so we don't have to worry about it here. */ 140: 141: /* The entire file is just a list of commands. */ 142: 143: file: 144: commands 145: ; 146: 147: /* A possibly empty list of commands. */ 148: 149: commands: 150: /* May be empty. */ 151: | command commands 152: ; 153: 154: /* A single command. There is where most of the work takes place. */ 155: 156: command: 157: CHECK STRING 158: { 159: check_procedure = $2; 160: } 161: | CODESTART STRING 162: { 163: nlmheader_warn (_("CODESTART is not implemented; sorry"), -1); 164: free ($2); 165: } 166: | COPYRIGHT QUOTED_STRING 167: { 168: int len; 169: 170: strncpy (copyright_hdr->stamp, "CoPyRiGhT=", 10); 171: len = strlen ($2); 172: if (len >= NLM_MAX_COPYRIGHT_MESSAGE_LENGTH) 173: { 174: nlmheader_warn (_("copyright string is too long"), 175: NLM_MAX_COPYRIGHT_MESSAGE_LENGTH - 1); 176: len = NLM_MAX_COPYRIGHT_MESSAGE_LENGTH - 1; 177: } 178: copyright_hdr->copyrightMessageLength = len; 179: strncpy (copyright_hdr->copyrightMessage, $2, len); 180: copyright_hdr->copyrightMessage[len] = '\0'; 181: free ($2); 182: } 183: | CUSTOM STRING 184: { 185: custom_file = $2; 186: } 187: | DATE STRING STRING STRING 188: { 189: /* We don't set the version stamp here, because we use the 190: version stamp to detect whether the required VERSION 191: keyword was given. */ 192: version_hdr->month = nlmlex_get_number ($2); 193: version_hdr->day = nlmlex_get_number ($3); 194: version_hdr->year = nlmlex_get_number ($4); 195: free ($2); 196: free ($3); 197: free ($4); 198: if (version_hdr->month < 1 || version_hdr->month > 12) 199: nlmheader_warn (_("illegal month"), -1); 200: if (version_hdr->day < 1 || version_hdr->day > 31) 201: nlmheader_warn (_("illegal day"), -1); 202: if (version_hdr->year < 1900 || version_hdr->year > 3000) 203: nlmheader_warn (_("illegal year"), -1); 204: } 205: | DEBUG 206: { 207: debug_info = TRUE; 208: } 209: | DESCRIPTION QUOTED_STRING 210: { 211: int len; 212: 213: len = strlen ($2); 214: if (len > NLM_MAX_DESCRIPTION_LENGTH) 215: { 216: nlmheader_warn (_("description string is too long"), 217: NLM_MAX_DESCRIPTION_LENGTH); 218: len = NLM_MAX_DESCRIPTION_LENGTH; 219: } 220: var_hdr->descriptionLength = len; 221: strncpy (var_hdr->descriptionText, $2, len); 222: var_hdr->descriptionText[len] = '\0'; 223: free ($2); 224: } 225: | EXIT STRING 226: { 227: exit_procedure = $2; 228: } 229: | EXPORT 230: { 231: symbol_prefix = NULL; 232: } 233: symbol_list_opt 234: { 235: export_symbols = string_list_append (export_symbols, $3); 236: } 237: | FLAG_ON STRING 238: { 239: fixed_hdr->flags |= nlmlex_get_number ($2); 240: free ($2); 241: } 242: | FLAG_OFF STRING 243: { 244: fixed_hdr->flags &=~ nlmlex_get_number ($2); 245: free ($2); 246: } 247: | FULLMAP 248: { 249: map_file = ""; 250: full_map = TRUE; 251: } 252: | FULLMAP STRING 253: { 254: map_file = $2; 255: full_map = TRUE; 256: } 257: | HELP STRING 258: { 259: help_file = $2; 260: } 261: | IMPORT 262: { 263: symbol_prefix = NULL; 264: } 265: symbol_list_opt 266: { 267: import_symbols = string_list_append (import_symbols, $3); 268: } 269: | INPUT string_list 270: { 271: input_files = string_list_append (input_files, $2); 272: } 273: | MAP 274: { 275: map_file = ""; 276: } 277: | MAP STRING 278: { 279: map_file = $2; 280: } 281: | MESSAGES STRING 282: { 283: message_file = $2; 284: } 285: | MODULE string_list 286: { 287: modules = string_list_append (modules, $2); 288: } 289: | MULTIPLE 290: { 291: fixed_hdr->flags |= 0x2; 292: } 293: | OS_DOMAIN 294: { 295: fixed_hdr->flags |= 0x10; 296: } 297: | OUTPUT STRING 298: { 299: if (output_file == NULL) 300: output_file = $2; 301: else 302: nlmheader_warn (_("ignoring duplicate OUTPUT statement"), -1); 303: } 304: | PSEUDOPREEMPTION 305: { 306: fixed_hdr->flags |= 0x8; 307: } 308: | REENTRANT 309: { 310: fixed_hdr->flags |= 0x1; 311: } 312: | SCREENNAME QUOTED_STRING 313: { 314: int len; 315: 316: len = strlen ($2); 317: if (len >= NLM_MAX_SCREEN_NAME_LENGTH) 318: { 319: nlmheader_warn (_("screen name is too long"), 320: NLM_MAX_SCREEN_NAME_LENGTH); 321: len = NLM_MAX_SCREEN_NAME_LENGTH; 322: } 323: var_hdr->screenNameLength = len; 324: strncpy (var_hdr->screenName, $2, len); 325: var_hdr->screenName[NLM_MAX_SCREEN_NAME_LENGTH] = '\0'; 326: free ($2); 327: } 328: | SHARELIB STRING 329: { 330: sharelib_file = $2; 331: } 332: | STACK STRING 333: { 334: var_hdr->stackSize = nlmlex_get_number ($2); 335: free ($2); 336: } 337: | START STRING 338: { 339: start_procedure = $2; 340: } 341: | SYNCHRONIZE 342: { 343: fixed_hdr->flags |= 0x4; 344: } 345: | THREADNAME QUOTED_STRING 346: { 347: int len; 348: 349: len = strlen ($2); 350: if (len >= NLM_MAX_THREAD_NAME_LENGTH) 351: { 352: nlmheader_warn (_("thread name is too long"), 353: NLM_MAX_THREAD_NAME_LENGTH); 354: len = NLM_MAX_THREAD_NAME_LENGTH; 355: } 356: var_hdr->threadNameLength = len; 357: strncpy (var_hdr->threadName, $2, len); 358: var_hdr->threadName[len] = '\0'; 359: free ($2); 360: } 361: | TYPE STRING 362: { 363: fixed_hdr->moduleType = nlmlex_get_number ($2); 364: free ($2); 365: } 366: | VERBOSE 367: { 368: verbose = TRUE; 369: } 370: | VERSIONK STRING STRING STRING 371: { 372: long val; 373: 374: strncpy (version_hdr->stamp, "VeRsIoN#", 8); 375: version_hdr->majorVersion = nlmlex_get_number ($2); 376: val = nlmlex_get_number ($3); 377: if (val < 0 || val > 99) 378: nlmheader_warn (_("illegal minor version number (must be between 0 and 99)"), 379: -1); 380: else 381: version_hdr->minorVersion = val; 382: val = nlmlex_get_number ($4); 383: if (val < 0) 384: nlmheader_warn (_("illegal revision number (must be between 0 and 26)"), 385: -1); 386: else if (val > 26) 387: version_hdr->revision = 0; 388: else 389: version_hdr->revision = val; 390: free ($2); 391: free ($3); 392: free ($4); 393: } 394: | VERSIONK STRING STRING 395: { 396: long val; 397: 398: strncpy (version_hdr->stamp, "VeRsIoN#", 8); 399: version_hdr->majorVersion = nlmlex_get_number ($2); 400: val = nlmlex_get_number ($3); 401: if (val < 0 || val > 99) 402: nlmheader_warn (_("illegal minor version number (must be between 0 and 99)"), 403: -1); 404: else 405: version_hdr->minorVersion = val; 406: version_hdr->revision = 0; 407: free ($2); 408: free ($3); 409: } 410: | XDCDATA STRING 411: { 412: rpc_file = $2; 413: } 414: ; 415: 416: /* A possibly empty list of symbols. */ 417: 418: symbol_list_opt: 419: /* Empty. */ 420: { 421: $$ = NULL; 422: } 423: | symbol_list 424: { 425: $$ = $1; 426: } 427: ; 428: 429: /* A list of symbols in an import or export list. Prefixes may appear 430: in parentheses. We need to use left recursion here to avoid 431: building up a large import list on the parser stack. */ 432: 433: symbol_list: 434: symbol 435: { 436: $$ = string_list_cons ($1, NULL); 437: } 438: | symbol_prefix 439: { 440: $$ = NULL; 441: } 442: | symbol_list symbol 443: { 444: $$ = string_list_append1 ($1, $2); 445: } 446: | symbol_list symbol_prefix 447: { 448: $$ = $1; 449: } 450: ; 451: 452: /* A prefix for subsequent symbols. */ 453: 454: symbol_prefix: 455: '(' STRING ')' 456: { 457: if (symbol_prefix != NULL) 458: free (symbol_prefix); 459: symbol_prefix = $2; 460: } 461: ; 462: 463: /* A single symbol. */ 464: 465: symbol: 466: STRING 467: { 468: if (symbol_prefix == NULL) 469: $$ = $1; 470: else 471: { 472: $$ = xmalloc (strlen (symbol_prefix) + strlen ($1) + 2); 473: sprintf ($$, "%s@%s", symbol_prefix, $1); 474: free ($1); 475: } 476: } 477: ; 478: 479: /* A list of strings. */ 480: 481: string_list: 482: /* May be empty. */ 483: { 484: $$ = NULL; 485: } 486: | STRING string_list 487: { 488: $$ = string_list_cons ($1, $2); 489: } 490: ; 491: 492: %% 493: 494: /* If strerror is just a macro, we want to use the one from libiberty 495: since it will handle undefined values. */ 496: #undef strerror 497: extern char *strerror PARAMS ((int)); 498: 499: /* The lexer is simple, too simple for flex. Keywords are only 500: recognized at the start of lines. Everything else must be an 501: argument. A comma is treated as whitespace. */ 502: 503: /* The states the lexer can be in. */ 504: 505: enum lex_state 506: { 507: /* At the beginning of a line. */ 508: BEGINNING_OF_LINE, 509: /* In the middle of a line. */ 510: IN_LINE 511: }; 512: 513: /* We need to keep a stack of files to handle file inclusion. */ 514: 515: struct input 516: { 517: /* The file to read from. */ 518: FILE *file; 519: /* The name of the file. */ 520: char *name; 521: /* The current line number. */ 522: int lineno; 523: /* The current state. */ 524: enum lex_state state; 525: /* The next file on the stack. */ 526: struct input *next; 527: }; 528: 529: /* The current input file. */ 530: 531: static struct input current; 532: 533: /* The character which introduces comments. */ 534: #define COMMENT_CHAR '#' 535: ^L 536: /* Start the lexer going on the main input file. */ 537: 538: bfd_boolean 539: nlmlex_file (const char *name) 540: { 541: current.next = NULL; 542: return nlmlex_file_open (name); 543: } 544: 545: /* Start the lexer going on a subsidiary input file. */ 546: 547: static void 548: nlmlex_file_push (const char *name) 549: { 550: struct input *push; 551: 552: push = (struct input *) xmalloc (sizeof (struct input)); 553: *push = current; 554: if (nlmlex_file_open (name)) 555: current.next = push; 556: else 557: { 558: current = *push; 559: free (push); 560: } 561: } 562: 563: /* Start lexing from a file. */ 564: 565: static bfd_boolean 566: nlmlex_file_open (const char *name) 567: { 568: current.file = fopen (name, "r"); 569: if (current.file == NULL) 570: { 571: fprintf (stderr, "%s:%s: %s\n", program_name, name, strerror (errno)); 572: ++parse_errors; 573: return FALSE; 574: } 575: current.name = xstrdup (name); 576: current.lineno = 1; 577: current.state = BEGINNING_OF_LINE; 578: return TRUE; 579: } 580: ^L 581: /* Table used to turn keywords into tokens. */ 582: 583: struct keyword_tokens_struct 584: { 585: const char *keyword; 586: int token; 587: }; 588: 589: static struct keyword_tokens_struct keyword_tokens[] = 590: { 591: { "CHECK", CHECK }, 592: { "CODESTART", CODESTART }, 593: { "COPYRIGHT", COPYRIGHT }, 594: { "CUSTOM", CUSTOM }, 595: { "DATE", DATE }, 596: { "DEBUG", DEBUG }, 597: { "DESCRIPTION", DESCRIPTION }, 598: { "EXIT", EXIT }, 599: { "EXPORT", EXPORT }, 600: { "FLAG_ON", FLAG_ON }, 601: { "FLAG_OFF", FLAG_OFF }, 602: { "FULLMAP", FULLMAP }, 603: { "HELP", HELP }, 604: { "IMPORT", IMPORT }, 605: { "INPUT", INPUT }, 606: { "MAP", MAP }, 607: { "MESSAGES", MESSAGES }, 608: { "MODULE", MODULE }, 609: { "MULTIPLE", MULTIPLE }, 610: { "OS_DOMAIN", OS_DOMAIN }, 611: { "OUTPUT", OUTPUT }, 612: { "PSEUDOPREEMPTION", PSEUDOPREEMPTION }, 613: { "REENTRANT", REENTRANT }, 614: { "SCREENNAME", SCREENNAME }, 615: { "SHARELIB", SHARELIB }, 616: { "STACK", STACK }, 617: { "STACKSIZE", STACK }, 618: { "START", START }, 619: { "SYNCHRONIZE", SYNCHRONIZE }, 620: { "THREADNAME", THREADNAME }, 621: { "TYPE", TYPE }, 622: { "VERBOSE", VERBOSE }, 623: { "VERSION", VERSIONK }, 624: { "XDCDATA", XDCDATA } 625: }; 626: 627: #define KEYWORD_COUNT (sizeof (keyword_tokens) / sizeof (keyword_tokens[0])) 628: ^L 629: /* The lexer accumulates strings in these variables. */ 630: static char *lex_buf; 631: static int lex_size; 632: static int lex_pos; 633: 634: /* Start accumulating strings into the buffer. */ 635: #define BUF_INIT() \ 636: ((void) (lex_buf != NULL ? lex_pos = 0 : nlmlex_buf_init ())) 637: 638: static int 639: nlmlex_buf_init (void) 640: { 641: lex_size = 10; 642: lex_buf = xmalloc (lex_size + 1); 643: lex_pos = 0; 644: return 0; 645: } 646: 647: /* Finish a string in the buffer. */ 648: #define BUF_FINISH() ((void) (lex_buf[lex_pos] = '\0')) 649: 650: /* Accumulate a character into the buffer. */ 651: #define BUF_ADD(c) \ 652: ((void) (lex_pos < lex_size \ 653: ? lex_buf[lex_pos++] = (c) \ 654: : nlmlex_buf_add (c))) 655: 656: static char 657: nlmlex_buf_add (int c) 658: { 659: if (lex_pos >= lex_size) 660: { 661: lex_size *= 2; 662: lex_buf = xrealloc (lex_buf, lex_size + 1); 663: } 664: 665: return lex_buf[lex_pos++] = c; 666: } 667: ^L 668: /* The lexer proper. This is called by the bison generated parsing 669: code. */ 670: 671: static int 672: yylex (void) 673: { 674: int c; 675: 676: tail_recurse: 677: 678: c = getc (current.file); 679: 680: /* Commas are treated as whitespace characters. */ 681: while (ISSPACE (c) || c == ',') 682: { 683: current.state = IN_LINE; 684: if (c == '\n') 685: { 686: ++current.lineno; 687: current.state = BEGINNING_OF_LINE; 688: } 689: c = getc (current.file); 690: } 691: 692: /* At the end of the file we either pop to the previous file or 693: finish up. */ 694: if (c == EOF) 695: { 696: fclose (current.file); 697: free (current.name); 698: if (current.next == NULL) 699: return 0; 700: else 701: { 702: struct input *next; 703: 704: next = current.next; 705: current = *next; 706: free (next); 707: goto tail_recurse; 708: } 709: } 710: 711: /* A comment character always means to drop everything until the 712: next newline. */ 713: if (c == COMMENT_CHAR) 714: { 715: do 716: { 717: c = getc (current.file); 718: } 719: while (c != '\n'); 720: ++current.lineno; 721: current.state = BEGINNING_OF_LINE; 722: goto tail_recurse; 723: } 724: 725: /* An '@' introduces an include file. */ 726: if (c == '@') 727: { 728: do 729: { 730: c = getc (current.file); 731: if (c == '\n') 732: ++current.lineno; 733: } 734: while (ISSPACE (c)); 735: BUF_INIT (); 736: while (! ISSPACE (c) && c != EOF) 737: { 738: BUF_ADD (c); 739: c = getc (current.file); 740: } 741: BUF_FINISH (); 742: 743: ungetc (c, current.file); 744: 745: nlmlex_file_push (lex_buf); 746: goto tail_recurse; 747: } 748: 749: /* A non-space character at the start of a line must be the start of 750: a keyword. */ 751: if (current.state == BEGINNING_OF_LINE) 752: { 753: BUF_INIT (); 754: while (ISALNUM (c) || c == '_') 755: { 756: BUF_ADD (TOUPPER (c)); 757: c = getc (current.file); 758: } 759: BUF_FINISH (); 760: 761: if (c != EOF && ! ISSPACE (c) && c != ',') 762: { 763: nlmheader_identify (); 764: fprintf (stderr, _("%s:%d: illegal character in keyword: %c\n"), 765: current.name, current.lineno, c); 766: } 767: else 768: { 769: unsigned int i; 770: 771: for (i = 0; i < KEYWORD_COUNT; i++) 772: { 773: if (lex_buf[0] == keyword_tokens[i].keyword[0] 774: && strcmp (lex_buf, keyword_tokens[i].keyword) == 0) 775: { 776: /* Pushing back the final whitespace avoids worrying 777: about \n here. */ 778: ungetc (c, current.file); 779: current.state = IN_LINE; 780: return keyword_tokens[i].token; 781: } 782: } 783: 784: nlmheader_identify ();