1 /* $Id: main.c,v 1.69 2019/11/25 23:24:36 Tom.Shields 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 if (rflag) 152 DO_FREE(code_file_name); 153 154 if (dflag && !dflag2) 155 DO_FREE(defines_file_name); 156 157 if (iflag) 158 DO_FREE(externs_file_name); 159 160 if (oflag) 161 DO_FREE(output_file_name); 162 163 if (vflag) 164 DO_FREE(verbose_file_name); 165 166 if (gflag) 167 DO_FREE(graph_file_name); 168 169 lr0_leaks(); 170 lalr_leaks(); 171 mkpar_leaks(); 172 mstring_leaks(); 173 output_leaks(); 174 reader_leaks(); 175 #endif 176 177 exit(k); 178 } 179 180 static void 181 onintr(int sig GCC_UNUSED) 182 { 183 got_intr = 1; 184 done(EXIT_FAILURE); 185 } 186 187 static void 188 set_signals(void) 189 { 190 #ifdef SIGINT 191 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 192 signal(SIGINT, onintr); 193 #endif 194 #ifdef SIGTERM 195 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 196 signal(SIGTERM, onintr); 197 #endif 198 #ifdef SIGHUP 199 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 200 signal(SIGHUP, onintr); 201 #endif 202 } 203 204 static void 205 usage(void) 206 { 207 static const char *msg[] = 208 { 209 "" 210 ,"Options:" 211 ," -b file_prefix set filename prefix (default \"y.\")" 212 ," -B create a backtracking parser" 213 ," -d write definitions (" DEFINES_SUFFIX ")" 214 ," -H defines_file write definitions to defines_file" 215 ," -i write interface (y.tab.i)" 216 ," -g write a graphical description" 217 ," -l suppress #line directives" 218 ," -L enable position processing, e.g., \"%locations\"" 219 ," -o output_file (default \"" OUTPUT_SUFFIX "\")" 220 ," -p symbol_prefix set symbol prefix (default \"yy\")" 221 ," -P create a reentrant parser, e.g., \"%pure-parser\"" 222 ," -r produce separate code and table files (y.code.c)" 223 ," -s suppress #define's for quoted names in %token lines" 224 ," -t add debugging support" 225 ," -v write description (y.output)" 226 ," -V show version information and exit" 227 }; 228 unsigned n; 229 230 fflush(stdout); 231 fprintf(stderr, "Usage: %s [options] filename\n", myname); 232 for (n = 0; n < sizeof(msg) / sizeof(msg[0]); ++n) 233 fprintf(stderr, "%s\n", msg[n]); 234 235 exit(EXIT_FAILURE); 236 } 237 238 static void 239 setflag(int ch) 240 { 241 switch (ch) 242 { 243 case 'B': 244 #if defined(YYBTYACC) 245 backtrack = 1; 246 #else 247 unsupported_flag_warning("-B", "reconfigure with --enable-btyacc"); 248 #endif 249 break; 250 251 case 'd': 252 dflag = 1; 253 dflag2 = 0; 254 break; 255 256 case 'g': 257 gflag = 1; 258 break; 259 260 case 'i': 261 iflag = 1; 262 break; 263 264 case 'l': 265 lflag = 1; 266 break; 267 268 case 'L': 269 #if defined(YYBTYACC) 270 locations = 1; 271 #else 272 unsupported_flag_warning("-L", "reconfigure with --enable-btyacc"); 273 #endif 274 break; 275 276 case 'P': 277 pure_parser = 1; 278 break; 279 280 case 'r': 281 rflag = 1; 282 break; 283 284 case 's': 285 sflag = 1; 286 break; 287 288 case 't': 289 tflag = 1; 290 break; 291 292 case 'v': 293 vflag = 1; 294 break; 295 296 case 'V': 297 printf("%s - %s\n", myname, VERSION); 298 exit(EXIT_SUCCESS); 299 300 case 'y': 301 /* noop for bison compatibility. byacc is already designed to be posix 302 * yacc compatible. */ 303 break; 304 305 default: 306 usage(); 307 } 308 } 309 310 static void 311 getargs(int argc, char *argv[]) 312 { 313 int i; 314 #ifdef HAVE_GETOPT 315 int ch; 316 317 if (argc > 0) 318 myname = argv[0]; 319 320 while ((ch = getopt(argc, argv, "Bb:dgH:ilLo:Pp:rstVvy")) != -1) 321 { 322 switch (ch) 323 { 324 case 'b': 325 file_prefix = optarg; 326 break; 327 case 'H': 328 dflag = dflag2 = 1; 329 defines_file_name = optarg; 330 break; 331 case 'o': 332 output_file_name = optarg; 333 break; 334 case 'p': 335 symbol_prefix = optarg; 336 break; 337 default: 338 setflag(ch); 339 break; 340 } 341 } 342 if ((i = optind) < argc) 343 { 344 /* getopt handles "--" specially, while we handle "-" specially */ 345 if (!strcmp(argv[i], "-")) 346 { 347 if ((i + 1) < argc) 348 usage(); 349 input_file = stdin; 350 return; 351 } 352 } 353 #else 354 char *s; 355 int ch; 356 357 if (argc > 0) 358 myname = argv[0]; 359 360 for (i = 1; i < argc; ++i) 361 { 362 s = argv[i]; 363 if (*s != '-') 364 break; 365 switch (ch = *++s) 366 { 367 case '\0': 368 input_file = stdin; 369 if (i + 1 < argc) 370 usage(); 371 return; 372 373 case '-': 374 ++i; 375 goto no_more_options; 376 377 case 'b': 378 if (*++s) 379 file_prefix = s; 380 else if (++i < argc) 381 file_prefix = argv[i]; 382 else 383 usage(); 384 continue; 385 386 case 'H': 387 dflag = dflag2 = 1; 388 if (*++s) 389 defines_file_name = s; 390 else if (++i < argc) 391 defines_file_name = argv[i]; 392 else 393 usage(); 394 continue; 395 396 case 'o': 397 if (*++s) 398 output_file_name = s; 399 else if (++i < argc) 400 output_file_name = argv[i]; 401 else 402 usage(); 403 continue; 404 405 case 'p': 406 if (*++s) 407 symbol_prefix = s; 408 else if (++i < argc) 409 symbol_prefix = argv[i]; 410 else 411 usage(); 412 continue; 413 414 default: 415 setflag(ch); 416 break; 417 } 418 419 for (;;) 420 { 421 switch (ch = *++s) 422 { 423 case '\0': 424 goto end_of_option; 425 426 default: 427 setflag(ch); 428 break; 429 } 430 } 431 end_of_option:; 432 } 433 434 no_more_options: 435 436 #endif /* HAVE_GETOPT */ 437 if (i + 1 != argc) 438 usage(); 439 input_file_name_len = strlen(argv[i]); 440 input_file_name = TMALLOC(char, input_file_name_len + 1); 441 NO_SPACE(input_file_name); 442 strcpy(input_file_name, argv[i]); 443 } 444 445 void * 446 allocate(size_t n) 447 { 448 void *p; 449 450 p = NULL; 451 if (n) 452 { 453 p = CALLOC(1, n); 454 NO_SPACE(p); 455 } 456 return (p); 457 } 458 459 #define CREATE_FILE_NAME(dest, suffix) \ 460 dest = alloc_file_name(len, suffix) 461 462 static char * 463 alloc_file_name(size_t len, const char *suffix) 464 { 465 char *result = TMALLOC(char, len + strlen(suffix) + 1); 466 if (result == 0) 467 no_space(); 468 strcpy(result, file_prefix); 469 strcpy(result + len, suffix); 470 return result; 471 } 472 473 static char * 474 find_suffix(char *name, const char *suffix) 475 { 476 size_t len = strlen(name); 477 size_t slen = strlen(suffix); 478 if (len >= slen) 479 { 480 name += len - slen; 481 if (strcmp(name, suffix) == 0) 482 return name; 483 } 484 return NULL; 485 } 486 487 static void 488 create_file_names(void) 489 { 490 size_t len; 491 const char *defines_suffix; 492 const char *externs_suffix; 493 char *suffix; 494 495 suffix = NULL; 496 defines_suffix = DEFINES_SUFFIX; 497 externs_suffix = EXTERNS_SUFFIX; 498 499 /* compute the file_prefix from the user provided output_file_name */ 500 if (output_file_name != 0) 501 { 502 if (!(suffix = find_suffix(output_file_name, OUTPUT_SUFFIX)) 503 && (suffix = find_suffix(output_file_name, ".c"))) 504 { 505 defines_suffix = ".h"; 506 externs_suffix = ".i"; 507 } 508 } 509 510 if (suffix != NULL) 511 { 512 len = (size_t) (suffix - output_file_name); 513 file_prefix = TMALLOC(char, len + 1); 514 NO_SPACE(file_prefix); 515 strncpy(file_prefix, output_file_name, len)[len] = 0; 516 } 517 else 518 len = strlen(file_prefix); 519 520 /* if "-o filename" was not given */ 521 if (output_file_name == 0) 522 { 523 oflag = 1; 524 CREATE_FILE_NAME(output_file_name, OUTPUT_SUFFIX); 525 } 526 527 if (rflag) 528 { 529 CREATE_FILE_NAME(code_file_name, CODE_SUFFIX); 530 } 531 else 532 code_file_name = output_file_name; 533 534 if (dflag && !dflag2) 535 { 536 CREATE_FILE_NAME(defines_file_name, defines_suffix); 537 } 538 539 if (iflag) 540 { 541 CREATE_FILE_NAME(externs_file_name, externs_suffix); 542 } 543 544 if (vflag) 545 { 546 CREATE_FILE_NAME(verbose_file_name, VERBOSE_SUFFIX); 547 } 548 549 if (gflag) 550 { 551 CREATE_FILE_NAME(graph_file_name, GRAPH_SUFFIX); 552 } 553 554 if (suffix != NULL) 555 { 556 FREE(file_prefix); 557 } 558 } 559 560 #if USE_MKSTEMP 561 static void 562 close_tmpfiles(void) 563 { 564 while (my_tmpfiles != 0) 565 { 566 MY_TMPFILES *next = my_tmpfiles->next; 567 568 (void)chmod(my_tmpfiles->name, 0644); 569 (void)unlink(my_tmpfiles->name); 570 571 free(my_tmpfiles->name); 572 free(my_tmpfiles); 573 574 my_tmpfiles = next; 575 } 576 } 577 578 #ifndef HAVE_MKSTEMP 579 static int 580 my_mkstemp(char *temp) 581 { 582 int fd; 583 char *dname; 584 char *fname; 585 char *name; 586 587 /* 588 * Split-up to use tempnam, rather than tmpnam; the latter (like 589 * mkstemp) is unusable on Windows. 590 */ 591 if ((fname = strrchr(temp, '/')) != 0) 592 { 593 dname = strdup(temp); 594 dname[++fname - temp] = '\0'; 595 } 596 else 597 { 598 dname = 0; 599 fname = temp; 600 } 601 if ((name = tempnam(dname, fname)) != 0) 602 { 603 fd = open(name, O_CREAT | O_EXCL | O_RDWR); 604 strcpy(temp, name); 605 } 606 else 607 { 608 fd = -1; 609 } 610 611 if (dname != 0) 612 free(dname); 613 614 return fd; 615 } 616 #define mkstemp(s) my_mkstemp(s) 617 #endif 618 619 #endif 620 621 /* 622 * tmpfile() should be adequate, except that it may require special privileges 623 * to use, e.g., MinGW and Windows 7 where it tries to use the root directory. 624 */ 625 static FILE * 626 open_tmpfile(const char *label) 627 { 628 #define MY_FMT "%s/%.*sXXXXXX" 629 FILE *result; 630 #if USE_MKSTEMP 631 int fd; 632 const char *tmpdir; 633 char *name; 634 const char *mark; 635 636 if (((tmpdir = getenv("TMPDIR")) == 0 || access(tmpdir, W_OK) != 0) || 637 ((tmpdir = getenv("TEMP")) == 0 || access(tmpdir, W_OK) != 0)) 638 { 639 #ifdef P_tmpdir 640 tmpdir = P_tmpdir; 641 #else 642 tmpdir = "/tmp"; 643 #endif 644 if (access(tmpdir, W_OK) != 0) 645 tmpdir = "."; 646 } 647 648 /* The size of the format is guaranteed to be longer than the result from 649 * printing empty strings with it; this calculation accounts for the 650 * string-lengths as well. 651 */ 652 name = malloc(strlen(tmpdir) + sizeof(MY_FMT) + strlen(label)); 653 654 result = 0; 655 if (name != 0) 656 { 657 mode_t save_umask = umask(0177); 658 659 if ((mark = strrchr(label, '_')) == 0) 660 mark = label + strlen(label); 661 662 sprintf(name, MY_FMT, tmpdir, (int)(mark - label), label); 663 fd = mkstemp(name); 664 if (fd >= 0 665 && (result = fdopen(fd, "w+")) != 0) 666 { 667 MY_TMPFILES *item; 668 669 if (my_tmpfiles == 0) 670 { 671 atexit(close_tmpfiles); 672 } 673 674 item = NEW(MY_TMPFILES); 675 NO_SPACE(item); 676 677 item->name = name; 678 NO_SPACE(item->name); 679 680 item->next = my_tmpfiles; 681 my_tmpfiles = item; 682 } 683 else 684 { 685 FREE(name); 686 } 687 (void)umask(save_umask); 688 } 689 #else 690 result = tmpfile(); 691 #endif 692 693 if (result == 0) 694 open_error(label); 695 return result; 696 #undef MY_FMT 697 } 698 699 static void 700 open_files(void) 701 { 702 create_file_names(); 703 704 if (input_file == 0) 705 { 706 input_file = fopen(input_file_name, "r"); 707 if (input_file == 0) 708 open_error(input_file_name); 709 } 710 711 action_file = open_tmpfile("action_file"); 712 text_file = open_tmpfile("text_file"); 713 714 if (vflag) 715 { 716 verbose_file = fopen(verbose_file_name, "w"); 717 if (verbose_file == 0) 718 open_error(verbose_file_name); 719 } 720 721 if (gflag) 722 { 723 graph_file = fopen(graph_file_name, "w"); 724 if (graph_file == 0) 725 open_error(graph_file_name); 726 fprintf(graph_file, "digraph %s {\n", file_prefix); 727 fprintf(graph_file, "\tedge [fontsize=10];\n"); 728 fprintf(graph_file, "\tnode [shape=box,fontsize=10];\n"); 729 fprintf(graph_file, "\torientation=landscape;\n"); 730 fprintf(graph_file, "\trankdir=LR;\n"); 731 fprintf(graph_file, "\t/*\n"); 732 fprintf(graph_file, "\tmargin=0.2;\n"); 733 fprintf(graph_file, "\tpage=\"8.27,11.69\"; // for A4 printing\n"); 734 fprintf(graph_file, "\tratio=auto;\n"); 735 fprintf(graph_file, "\t*/\n"); 736 } 737 738 if (dflag || dflag2) 739 { 740 defines_file = fopen(defines_file_name, "w"); 741 if (defines_file == 0) 742 open_error(defines_file_name); 743 union_file = open_tmpfile("union_file"); 744 } 745 746 if (iflag) 747 { 748 externs_file = fopen(externs_file_name, "w"); 749 if (externs_file == 0) 750 open_error(externs_file_name); 751 } 752 753 output_file = fopen(output_file_name, "w"); 754 if (output_file == 0) 755 open_error(output_file_name); 756 757 if (rflag) 758 { 759 code_file = fopen(code_file_name, "w"); 760 if (code_file == 0) 761 open_error(code_file_name); 762 } 763 else 764 code_file = output_file; 765 } 766 767 int 768 main(int argc, char *argv[]) 769 { 770 SRexpect = -1; 771 RRexpect = -1; 772 exit_code = EXIT_SUCCESS; 773 774 set_signals(); 775 getargs(argc, argv); 776 open_files(); 777 reader(); 778 lr0(); 779 lalr(); 780 make_parser(); 781 graph(); 782 finalize_closure(); 783 verbose(); 784 output(); 785 done(exit_code); 786 /*NOTREACHED */ 787 } 788