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
done(int k)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
onintr(int sig GCC_UNUSED)181 onintr(int sig GCC_UNUSED)
182 {
183 got_intr = 1;
184 done(EXIT_FAILURE);
185 }
186
187 static void
set_signals(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
usage(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
setflag(int ch)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
getargs(int argc,char * argv[])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 *
allocate(size_t n)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 *
alloc_file_name(size_t len,const char * suffix)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 *
find_suffix(char * name,const char * suffix)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
create_file_names(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
close_tmpfiles(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
my_mkstemp(char * temp)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 *
open_tmpfile(const char * label)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
open_files(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
main(int argc,char * argv[])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