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