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