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