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