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