1/* $Id: main.c,v 1.73 2021/08/08 20:39:34 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 25typedef struct _my_tmpfiles 26{ 27 struct _my_tmpfiles *next; 28 char *name; 29} 30MY_TMPFILES; 31 32static MY_TMPFILES *my_tmpfiles; 33#endif /* USE_MKSTEMP */ 34 35char dflag; 36char dflag2; 37char gflag; 38char iflag; 39char lflag; 40static char oflag; 41char rflag; 42char sflag; 43char tflag; 44char vflag; 45 46const char *symbol_prefix; 47const char *myname = "yacc"; 48 49int lineno; 50int outline; 51 52static char default_file_prefix[] = "y"; 53 54static char *file_prefix = default_file_prefix; 55 56char *code_file_name; 57char *input_file_name; 58size_t input_file_name_len = 0; 59char *defines_file_name; 60char *externs_file_name; 61 62static char *graph_file_name; 63static char *output_file_name; 64static char *verbose_file_name; 65 66FILE *action_file; /* a temp file, used to save actions associated */ 67 /* with rules until the parser is written */ 68FILE *code_file; /* y.code.c (used when the -r option is specified) */ 69FILE *defines_file; /* y.tab.h */ 70FILE *externs_file; /* y.tab.i */ 71FILE *input_file; /* the input file */ 72FILE *output_file; /* y.tab.c */ 73FILE *text_file; /* a temp file, used to save text until all */ 74 /* symbols have been defined */ 75FILE *union_file; /* a temp file, used to save the union */ 76 /* definition until all symbol have been */ 77 /* defined */ 78FILE *verbose_file; /* y.output */ 79FILE *graph_file; /* y.dot */ 80 81Value_t nitems; 82Value_t nrules; 83Value_t nsyms; 84Value_t ntokens; 85Value_t nvars; 86 87Value_t start_symbol; 88char **symbol_name; 89char **symbol_pname; 90Value_t *symbol_value; 91Value_t *symbol_prec; 92char *symbol_assoc; 93 94int pure_parser; 95int token_table; 96int error_verbose; 97 98#if defined(YYBTYACC) 99Value_t *symbol_pval; 100char **symbol_destructor; 101char **symbol_type_tag; 102int locations = 0; /* default to no position processing */ 103int backtrack = 0; /* default is no backtracking */ 104char *initial_action = NULL; 105#endif 106 107int exit_code; 108 109Value_t *ritem; 110Value_t *rlhs; 111Value_t *rrhs; 112Value_t *rprec; 113Assoc_t *rassoc; 114Value_t **derives; 115char *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 128static int got_intr = 0; 129 130void 131done(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 182static void 183onintr(int sig GCC_UNUSED) 184{ 185 got_intr = 1; 186 done(EXIT_FAILURE); 187} 188 189static void 190set_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* */ 212static 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 235static void 236usage(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 282static void 283invalid_option(const char *option) 284{ 285 fprintf(stderr, "invalid option: %s\n", option); 286 usage(); 287} 288 289static void 290setflag(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 361static void 362getargs(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 545void * 546allocate(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 562static char * 563alloc_file_name(size_t len, const char *suffix) 564{ 565 char *result = TMALLOC(char, len + strlen(suffix) + 1); 566 if (result == 0) 567 no_space(); 568 strcpy(result, file_prefix); 569 strcpy(result + len, suffix); 570 return result; 571} 572 573static char * 574find_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 587static void 588create_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 661static void 662close_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 679static int 680my_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 */ 725static FILE * 726open_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 800static void 801open_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 868int 869main(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