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