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