1 /* $Id: main.c,v 1.74 2023/05/11 07:51:36 tom Exp $ */ 2 3 #include <signal.h> 4 #if !defined(_WIN32) || defined(__MINGW32__) 5 #include <unistd.h> /* for _exit() */ 6 #else 7 #include <stdlib.h> /* for _exit() */ 8 #endif 9 10 #include "defs.h" 11 12 #ifdef HAVE_MKSTEMP 13 # define USE_MKSTEMP 1 14 #elif defined(HAVE_FCNTL_H) 15 # define USE_MKSTEMP 1 16 # include <fcntl.h> /* for open(), O_EXCL, etc. */ 17 #else 18 # define USE_MKSTEMP 0 19 #endif 20 21 #if USE_MKSTEMP 22 #include <sys/types.h> 23 #include <sys/stat.h> 24 25 typedef struct _my_tmpfiles 26 { 27 struct _my_tmpfiles *next; 28 char *name; 29 } 30 MY_TMPFILES; 31 32 static MY_TMPFILES *my_tmpfiles; 33 #endif /* USE_MKSTEMP */ 34 35 char dflag; 36 char dflag2; 37 char gflag; 38 char iflag; 39 char lflag; 40 static char oflag; 41 char rflag; 42 char sflag; 43 char tflag; 44 char vflag; 45 46 const char *symbol_prefix; 47 const char *myname = "yacc"; 48 49 int lineno; 50 int outline; 51 52 static char default_file_prefix[] = "y"; 53 54 static char *file_prefix = default_file_prefix; 55 56 char *code_file_name; 57 char *input_file_name; 58 size_t input_file_name_len = 0; 59 char *defines_file_name; 60 char *externs_file_name; 61 62 static char *graph_file_name; 63 static char *output_file_name; 64 static char *verbose_file_name; 65 66 FILE *action_file; /* a temp file, used to save actions associated */ 67 /* with rules until the parser is written */ 68 FILE *code_file; /* y.code.c (used when the -r option is specified) */ 69 FILE *defines_file; /* y.tab.h */ 70 FILE *externs_file; /* y.tab.i */ 71 FILE *input_file; /* the input file */ 72 FILE *output_file; /* y.tab.c */ 73 FILE *text_file; /* a temp file, used to save text until all */ 74 /* symbols have been defined */ 75 FILE *union_file; /* a temp file, used to save the union */ 76 /* definition until all symbol have been */ 77 /* defined */ 78 FILE *verbose_file; /* y.output */ 79 FILE *graph_file; /* y.dot */ 80 81 Value_t nitems; 82 Value_t nrules; 83 Value_t nsyms; 84 Value_t ntokens; 85 Value_t nvars; 86 87 Value_t start_symbol; 88 char **symbol_name; 89 char **symbol_pname; 90 Value_t *symbol_value; 91 Value_t *symbol_prec; 92 char *symbol_assoc; 93 94 int pure_parser; 95 int token_table; 96 int error_verbose; 97 98 #if defined(YYBTYACC) 99 Value_t *symbol_pval; 100 char **symbol_destructor; 101 char **symbol_type_tag; 102 int locations = 0; /* default to no position processing */ 103 int backtrack = 0; /* default is no backtracking */ 104 char *initial_action = NULL; 105 #endif 106 107 int exit_code; 108 109 Value_t *ritem; 110 Value_t *rlhs; 111 Value_t *rrhs; 112 Value_t *rprec; 113 Assoc_t *rassoc; 114 Value_t **derives; 115 char *nullable; 116 117 /* 118 * Since fclose() is called via the signal handler, it might die. Don't loop 119 * if there is a problem closing a file. 120 */ 121 #define DO_CLOSE(fp) \ 122 if (fp != 0) { \ 123 FILE *use = fp; \ 124 fp = 0; \ 125 fclose(use); \ 126 } 127 128 static int got_intr = 0; 129 130 void 131 done(int k) 132 { 133 DO_CLOSE(input_file); 134 DO_CLOSE(output_file); 135 if (iflag) 136 DO_CLOSE(externs_file); 137 if (rflag) 138 DO_CLOSE(code_file); 139 140 DO_CLOSE(action_file); 141 DO_CLOSE(defines_file); 142 DO_CLOSE(graph_file); 143 DO_CLOSE(text_file); 144 DO_CLOSE(union_file); 145 DO_CLOSE(verbose_file); 146 147 if (got_intr) 148 _exit(EXIT_FAILURE); 149 150 #ifdef NO_LEAKS 151 DO_FREE(input_file_name); 152 153 if (rflag) 154 DO_FREE(code_file_name); 155 156 if (dflag && !dflag2) 157 DO_FREE(defines_file_name); 158 159 if (iflag) 160 DO_FREE(externs_file_name); 161 162 if (oflag) 163 DO_FREE(output_file_name); 164 165 if (vflag) 166 DO_FREE(verbose_file_name); 167 168 if (gflag) 169 DO_FREE(graph_file_name); 170 171 lr0_leaks(); 172 lalr_leaks(); 173 mkpar_leaks(); 174 mstring_leaks(); 175 output_leaks(); 176 reader_leaks(); 177 #endif 178 179 exit(k); 180 } 181 182 static void 183 onintr(int sig GCC_UNUSED) 184 { 185 got_intr = 1; 186 done(EXIT_FAILURE); 187 } 188 189 static void 190 set_signals(void) 191 { 192 #ifdef SIGINT 193 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 194 signal(SIGINT, onintr); 195 #endif 196 #ifdef SIGTERM 197 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 198 signal(SIGTERM, onintr); 199 #endif 200 #ifdef SIGHUP 201 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 202 signal(SIGHUP, onintr); 203 #endif 204 } 205 206 #define SIZEOF(v) (sizeof(v) / sizeof((v)[0])) 207 208 /* 209 * Long options are provided only as a compatibility aid for scripters. 210 */ 211 /* *INDENT-OFF* */ 212 static const struct { 213 const char long_opt[16]; 214 const char yacc_arg; 215 const char yacc_opt; 216 } long_opts[] = { 217 { "defines", 1, 'H' }, 218 { "file-prefix", 1, 'b' }, 219 { "graph", 0, 'g' }, 220 { "help", 0, 'h' }, 221 { "name-prefix", 1, 'p' }, 222 { "no-lines", 0, 'l' }, 223 { "output", 1, 'o' }, 224 { "version", 0, 'V' } 225 }; 226 /* *INDENT-ON* */ 227 228 /* 229 * Usage-message is designed for 80 columns, with some unknowns. Account for 230 * those in the maximum width so that the usage message uses no relocatable 231 * pointers. 232 */ 233 #define USAGE_COLS (80 + sizeof(DEFINES_SUFFIX) + sizeof(OUTPUT_SUFFIX)) 234 235 static void 236 usage(void) 237 { 238 /* *INDENT-OFF* */ 239 static const char msg[][USAGE_COLS] = 240 { 241 { " -b file_prefix set filename prefix (default \"y.\")" }, 242 { " -B create a backtracking parser" }, 243 { " -d write definitions (" DEFINES_SUFFIX ")" }, 244 { " -h print this help-message" }, 245 { " -H defines_file write definitions to defines_file" }, 246 { " -i write interface (y.tab.i)" }, 247 { " -g write a graphical description" }, 248 { " -l suppress #line directives" }, 249 { " -L enable position processing, e.g., \"%locations\"" }, 250 { " -o output_file (default \"" OUTPUT_SUFFIX "\")" }, 251 { " -p symbol_prefix set symbol prefix (default \"yy\")" }, 252 { " -P create a reentrant parser, e.g., \"%pure-parser\"" }, 253 { " -r produce separate code and table files (y.code.c)" }, 254 { " -s suppress #define's for quoted names in %token lines" }, 255 { " -t add debugging support" }, 256 { " -v write description (y.output)" }, 257 { " -V show version information and exit" }, 258 }; 259 /* *INDENT-ON* */ 260 unsigned n; 261 262 fflush(stdout); 263 fprintf(stderr, "Usage: %s [options] filename\n", myname); 264 265 fprintf(stderr, "\nOptions:\n"); 266 for (n = 0; n < SIZEOF(msg); ++n) 267 { 268 fprintf(stderr, "%s\n", msg[n]); 269 } 270 271 fprintf(stderr, "\nLong options:\n"); 272 for (n = 0; n < SIZEOF(long_opts); ++n) 273 { 274 fprintf(stderr, " --%-20s-%c\n", 275 long_opts[n].long_opt, 276 long_opts[n].yacc_opt); 277 } 278 279 exit(EXIT_FAILURE); 280 } 281 282 static void 283 invalid_option(const char *option) 284 { 285 fprintf(stderr, "invalid option: %s\n", option); 286 usage(); 287 } 288 289 static void 290 setflag(int ch) 291 { 292 switch (ch) 293 { 294 case 'B': 295 #if defined(YYBTYACC) 296 backtrack = 1; 297 #else 298 unsupported_flag_warning("-B", "reconfigure with --enable-btyacc"); 299 #endif 300 break; 301 302 case 'd': 303 dflag = 1; 304 dflag2 = 0; 305 break; 306 307 case 'g': 308 gflag = 1; 309 break; 310 311 case 'i': 312 iflag = 1; 313 break; 314 315 case 'l': 316 lflag = 1; 317 break; 318 319 case 'L': 320 #if defined(YYBTYACC) 321 locations = 1; 322 #else 323 unsupported_flag_warning("-L", "reconfigure with --enable-btyacc"); 324 #endif 325 break; 326 327 case 'P': 328 pure_parser = 1; 329 break; 330 331 case 'r': 332 rflag = 1; 333 break; 334 335 case 's': 336 sflag = 1; 337 break; 338 339 case 't': 340 tflag = 1; 341 break; 342 343 case 'v': 344 vflag = 1; 345 break; 346 347 case 'V': 348 printf("%s - %s\n", myname, VERSION); 349 exit(EXIT_SUCCESS); 350 351 case 'y': 352 /* noop for bison compatibility. byacc is already designed to be posix 353 * yacc compatible. */ 354 break; 355 356 default: 357 usage(); 358 } 359 } 360 361 static void 362 getargs(int argc, char *argv[]) 363 { 364 int i; 365 #ifdef HAVE_GETOPT 366 int ch; 367 #endif 368 369 /* 370 * Map bison's long-options into yacc short options. 371 */ 372 for (i = 1; i < argc; ++i) 373 { 374 char *a = argv[i]; 375 376 if (!strncmp(a, "--", 2)) 377 { 378 char *eqls; 379 size_t lc; 380 size_t len; 381 382 if ((len = strlen(a)) == 2) 383 break; 384 385 if ((eqls = strchr(a, '=')) != NULL) 386 { 387 len = (size_t)(eqls - a); 388 if (len == 0 || eqls[1] == '\0') 389 invalid_option(a); 390 } 391 392 for (lc = 0; lc < SIZEOF(long_opts); ++lc) 393 { 394 if (!strncmp(long_opts[lc].long_opt, a + 2, len - 2)) 395 { 396 if (eqls != NULL && !long_opts[lc].yacc_arg) 397 invalid_option(a); 398 *a++ = '-'; 399 *a++ = long_opts[lc].yacc_opt; 400 *a = '\0'; 401 if (eqls) 402 { 403 while ((*a++ = *++eqls) != '\0') /* empty */ ; 404 } 405 break; 406 } 407 } 408 if (!strncmp(a, "--", 2)) 409 invalid_option(a); 410 } 411 } 412 413 #ifdef HAVE_GETOPT 414 if (argc > 0) 415 myname = argv[0]; 416 417 while ((ch = getopt(argc, argv, "Bb:dghH:ilLo:Pp:rstVvy")) != -1) 418 { 419 switch (ch) 420 { 421 case 'b': 422 file_prefix = optarg; 423 break; 424 case 'h': 425 usage(); 426 break; 427 case 'H': 428 dflag = dflag2 = 1; 429 defines_file_name = optarg; 430 break; 431 case 'o': 432 output_file_name = optarg; 433 break; 434 case 'p': 435 symbol_prefix = optarg; 436 break; 437 default: 438 setflag(ch); 439 break; 440 } 441 } 442 if ((i = optind) < argc) 443 { 444 /* getopt handles "--" specially, while we handle "-" specially */ 445 if (!strcmp(argv[i], "-")) 446 { 447 if ((i + 1) < argc) 448 usage(); 449 input_file = stdin; 450 return; 451 } 452 } 453 #else 454 char *s; 455 int ch; 456 457 if (argc > 0) 458 myname = argv[0]; 459 460 for (i = 1; i < argc; ++i) 461 { 462 s = argv[i]; 463 if (*s != '-') 464 break; 465 switch (ch = *++s) 466 { 467 case '\0': 468 input_file = stdin; 469 if (i + 1 < argc) 470 usage(); 471 return; 472 473 case '-': 474 ++i; 475 goto no_more_options; 476 477 case 'b': 478 if (*++s) 479 file_prefix = s; 480 else if (++i < argc) 481 file_prefix = argv[i]; 482 else 483 usage(); 484 continue; 485 486 case 'H': 487 dflag = dflag2 = 1; 488 if (*++s) 489 defines_file_name = s; 490 else if (++i < argc) 491 defines_file_name = argv[i]; 492 else 493 usage(); 494 continue; 495 496 case 'o': 497 if (*++s) 498 output_file_name = s; 499 else if (++i < argc) 500 output_file_name = argv[i]; 501 else 502 usage(); 503 continue; 504 505 case 'p': 506 if (*++s) 507 symbol_prefix = s; 508 else if (++i < argc) 509 symbol_prefix = argv[i]; 510 else 511 usage(); 512 continue; 513 514 default: 515 setflag(ch); 516 break; 517 } 518 519 for (;;) 520 { 521 switch (ch = *++s) 522 { 523 case '\0': 524 goto end_of_option; 525 526 default: 527 setflag(ch); 528 break; 529 } 530 } 531 end_of_option:; 532 } 533 534 no_more_options: 535 536 #endif /* HAVE_GETOPT */ 537 if (i + 1 != argc) 538 usage(); 539 input_file_name_len = strlen(argv[i]); 540 input_file_name = TMALLOC(char, input_file_name_len + 1); 541 NO_SPACE(input_file_name); 542 strcpy(input_file_name, argv[i]); 543 } 544 545 void * 546 allocate(size_t n) 547 { 548 void *p; 549 550 p = NULL; 551 if (n) 552 { 553 p = CALLOC(1, n); 554 NO_SPACE(p); 555 } 556 return (p); 557 } 558 559 #define CREATE_FILE_NAME(dest, suffix) \ 560 dest = alloc_file_name(len, suffix) 561 562 static char * 563 alloc_file_name(size_t len, const char *suffix) 564 { 565 char *result = TMALLOC(char, len + strlen(suffix) + 1); 566 if (result == NULL) 567 on_error(); 568 strcpy(result, file_prefix); 569 strcpy(result + len, suffix); 570 return result; 571 } 572 573 static char * 574 find_suffix(char *name, const char *suffix) 575 { 576 size_t len = strlen(name); 577 size_t slen = strlen(suffix); 578 if (len >= slen) 579 { 580 name += len - slen; 581 if (strcmp(name, suffix) == 0) 582 return name; 583 } 584 return NULL; 585 } 586 587 static void 588 create_file_names(void) 589 { 590 size_t len; 591 const char *defines_suffix; 592 const char *externs_suffix; 593 char *suffix; 594 595 suffix = NULL; 596 defines_suffix = DEFINES_SUFFIX; 597 externs_suffix = EXTERNS_SUFFIX; 598 599 /* compute the file_prefix from the user provided output_file_name */ 600 if (output_file_name != 0) 601 { 602 if (!(suffix = find_suffix(output_file_name, OUTPUT_SUFFIX)) 603 && (suffix = find_suffix(output_file_name, ".c"))) 604 { 605 defines_suffix = ".h"; 606 externs_suffix = ".i"; 607 } 608 } 609 610 if (suffix != NULL) 611 { 612 len = (size_t)(suffix - output_file_name); 613 file_prefix = TMALLOC(char, len + 1); 614 NO_SPACE(file_prefix); 615 strncpy(file_prefix, output_file_name, len)[len] = 0; 616 } 617 else 618 len = strlen(file_prefix); 619 620 /* if "-o filename" was not given */ 621 if (output_file_name == 0) 622 { 623 oflag = 1; 624 CREATE_FILE_NAME(output_file_name, OUTPUT_SUFFIX); 625 } 626 627 if (rflag) 628 { 629 CREATE_FILE_NAME(code_file_name, CODE_SUFFIX); 630 } 631 else 632 code_file_name = output_file_name; 633 634 if (dflag && !dflag2) 635 { 636 CREATE_FILE_NAME(defines_file_name, defines_suffix); 637 } 638 639 if (iflag) 640 { 641 CREATE_FILE_NAME(externs_file_name, externs_suffix); 642 } 643 644 if (vflag) 645 { 646 CREATE_FILE_NAME(verbose_file_name, VERBOSE_SUFFIX); 647 } 648 649 if (gflag) 650 { 651 CREATE_FILE_NAME(graph_file_name, GRAPH_SUFFIX); 652 } 653 654 if (suffix != NULL) 655 { 656 FREE(file_prefix); 657 } 658 } 659 660 #if USE_MKSTEMP 661 static void 662 close_tmpfiles(void) 663 { 664 while (my_tmpfiles != 0) 665 { 666 MY_TMPFILES *next = my_tmpfiles->next; 667 668 (void)chmod(my_tmpfiles->name, 0644); 669 (void)unlink(my_tmpfiles->name); 670 671 free(my_tmpfiles->name); 672 free(my_tmpfiles); 673 674 my_tmpfiles = next; 675 } 676 } 677 678 #ifndef HAVE_MKSTEMP 679 static int 680 my_mkstemp(char *temp) 681 { 682 int fd; 683 char *dname; 684 char *fname; 685 char *name; 686 687 /* 688 * Split-up to use tempnam, rather than tmpnam; the latter (like 689 * mkstemp) is unusable on Windows. 690 */ 691 if ((fname = strrchr(temp, '/')) != 0) 692 { 693 dname = strdup(temp); 694 dname[++fname - temp] = '\0'; 695 } 696 else 697 { 698 dname = 0; 699 fname = temp; 700 } 701 if ((name = tempnam(dname, fname)) != 0) 702 { 703 fd = open(name, O_CREAT | O_EXCL | O_RDWR); 704 strcpy(temp, name); 705 } 706 else 707 { 708 fd = -1; 709 } 710 711 if (dname != 0) 712 free(dname); 713 714 return fd; 715 } 716 #define mkstemp(s) my_mkstemp(s) 717 #endif 718 719 #endif 720 721 /* 722 * tmpfile() should be adequate, except that it may require special privileges 723 * to use, e.g., MinGW and Windows 7 where it tries to use the root directory. 724 */ 725 static FILE * 726 open_tmpfile(const char *label) 727 { 728 #define MY_FMT "%s/%.*sXXXXXX" 729 FILE *result; 730 #if USE_MKSTEMP 731 const char *tmpdir; 732 char *name; 733 734 if (((tmpdir = getenv("TMPDIR")) == 0 || access(tmpdir, W_OK) != 0) || 735 ((tmpdir = getenv("TEMP")) == 0 || access(tmpdir, W_OK) != 0)) 736 { 737 #ifdef P_tmpdir 738 tmpdir = P_tmpdir; 739 #else 740 tmpdir = "/tmp"; 741 #endif 742 if (access(tmpdir, W_OK) != 0) 743 tmpdir = "."; 744 } 745 746 /* The size of the format is guaranteed to be longer than the result from 747 * printing empty strings with it; this calculation accounts for the 748 * string-lengths as well. 749 */ 750 name = malloc(strlen(tmpdir) + sizeof(MY_FMT) + strlen(label)); 751 752 result = 0; 753 if (name != 0) 754 { 755 int fd; 756 const char *mark; 757 758 mode_t save_umask = umask(0177); 759 760 if ((mark = strrchr(label, '_')) == 0) 761 mark = label + strlen(label); 762 763 sprintf(name, MY_FMT, tmpdir, (int)(mark - label), label); 764 fd = mkstemp(name); 765 if (fd >= 0 766 && (result = fdopen(fd, "w+")) != 0) 767 { 768 MY_TMPFILES *item; 769 770 if (my_tmpfiles == 0) 771 { 772 atexit(close_tmpfiles); 773 } 774 775 item = NEW(MY_TMPFILES); 776 NO_SPACE(item); 777 778 item->name = name; 779 NO_SPACE(item->name); 780 781 item->next = my_tmpfiles; 782 my_tmpfiles = item; 783 } 784 else 785 { 786 FREE(name); 787 } 788 (void)umask(save_umask); 789 } 790 #else 791 result = tmpfile(); 792 #endif 793 794 if (result == 0) 795 open_error(label); 796 return result; 797 #undef MY_FMT 798 } 799 800 static void 801 open_files(void) 802 { 803 create_file_names(); 804 805 if (input_file == 0) 806 { 807 input_file = fopen(input_file_name, "r"); 808 if (input_file == 0) 809 open_error(input_file_name); 810 } 811 812 action_file = open_tmpfile("action_file"); 813 text_file = open_tmpfile("text_file"); 814 815 if (vflag) 816 { 817 verbose_file = fopen(verbose_file_name, "w"); 818 if (verbose_file == 0) 819 open_error(verbose_file_name); 820 } 821 822 if (gflag) 823 { 824 graph_file = fopen(graph_file_name, "w"); 825 if (graph_file == 0) 826 open_error(graph_file_name); 827 fprintf(graph_file, "digraph %s {\n", file_prefix); 828 fprintf(graph_file, "\tedge [fontsize=10];\n"); 829 fprintf(graph_file, "\tnode [shape=box,fontsize=10];\n"); 830 fprintf(graph_file, "\torientation=landscape;\n"); 831 fprintf(graph_file, "\trankdir=LR;\n"); 832 fprintf(graph_file, "\t/*\n"); 833 fprintf(graph_file, "\tmargin=0.2;\n"); 834 fprintf(graph_file, "\tpage=\"8.27,11.69\"; // for A4 printing\n"); 835 fprintf(graph_file, "\tratio=auto;\n"); 836 fprintf(graph_file, "\t*/\n"); 837 } 838 839 if (dflag || dflag2) 840 { 841 defines_file = fopen(defines_file_name, "w"); 842 if (defines_file == 0) 843 open_error(defines_file_name); 844 union_file = open_tmpfile("union_file"); 845 } 846 847 if (iflag) 848 { 849 externs_file = fopen(externs_file_name, "w"); 850 if (externs_file == 0) 851 open_error(externs_file_name); 852 } 853 854 output_file = fopen(output_file_name, "w"); 855 if (output_file == 0) 856 open_error(output_file_name); 857 858 if (rflag) 859 { 860 code_file = fopen(code_file_name, "w"); 861 if (code_file == 0) 862 open_error(code_file_name); 863 } 864 else 865 code_file = output_file; 866 } 867 868 int 869 main(int argc, char *argv[]) 870 { 871 SRexpect = -1; 872 RRexpect = -1; 873 exit_code = EXIT_SUCCESS; 874 875 set_signals(); 876 getargs(argc, argv); 877 open_files(); 878 reader(); 879 lr0(); 880 lalr(); 881 make_parser(); 882 graph(); 883 finalize_closure(); 884 verbose(); 885 output(); 886 done(exit_code); 887 /*NOTREACHED */ 888 } 889