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
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 dflag2;
37char gflag;
38char iflag;
39char lflag;
40static char oflag;
41char rflag;
42char sflag;
43char tflag;
44char vflag;
45
46const char *symbol_prefix;
47const char *myname = "yacc";
48
49int lineno;
50int outline;
51
52static char default_file_prefix[] = "y";
53
54static char *file_prefix = default_file_prefix;
55
56char *code_file_name;
57char *input_file_name;
58size_t input_file_name_len = 0;
59char *defines_file_name;
60char *externs_file_name;
61
62static char *graph_file_name;
63static char *output_file_name;
64static char *verbose_file_name;
65
66FILE *action_file;	/*  a temp file, used to save actions associated    */
67			/*  with rules until the parser is written          */
68FILE *code_file;	/*  y.code.c (used when the -r option is specified) */
69FILE *defines_file;	/*  y.tab.h                                         */
70FILE *externs_file;	/*  y.tab.i                                         */
71FILE *input_file;	/*  the input file                                  */
72FILE *output_file;	/*  y.tab.c                                         */
73FILE *text_file;	/*  a temp file, used to save text until all        */
74			/*  symbols have been defined                       */
75FILE *union_file;	/*  a temp file, used to save the union             */
76			/*  definition until all symbol have been           */
77			/*  defined                                         */
78FILE *verbose_file;	/*  y.output                                        */
79FILE *graph_file;	/*  y.dot                                           */
80
81Value_t nitems;
82Value_t nrules;
83Value_t nsyms;
84Value_t ntokens;
85Value_t nvars;
86
87Value_t start_symbol;
88char **symbol_name;
89char **symbol_pname;
90Value_t *symbol_value;
91Value_t *symbol_prec;
92char *symbol_assoc;
93
94int pure_parser;
95int token_table;
96int error_verbose;
97
98#if defined(YYBTYACC)
99Value_t *symbol_pval;
100char **symbol_destructor;
101char **symbol_type_tag;
102int locations = 0;	/* default to no position processing */
103int backtrack = 0;	/* default is no backtracking */
104char *initial_action = NULL;
105#endif
106
107int exit_code;
108
109Value_t *ritem;
110Value_t *rlhs;
111Value_t *rrhs;
112Value_t *rprec;
113Assoc_t *rassoc;
114Value_t **derives;
115char *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
128static int got_intr = 0;
129
130void
131done(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
180static void
181onintr(int sig GCC_UNUSED)
182{
183    got_intr = 1;
184    done(EXIT_FAILURE);
185}
186
187static void
188set_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
204static void
205usage(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
238static void
239setflag(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
310static void
311getargs(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
445void *
446allocate(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
462static char *
463alloc_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
473static char *
474find_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
487static void
488create_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
561static void
562close_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
579static int
580my_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 */
625static FILE *
626open_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
699static void
700open_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
767int
768main(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