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