xref: /freebsd/contrib/byacc/main.c (revision 09127a36371d5f5ac109ad1063e7fe53ee9bf356)
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 
21 typedef struct _my_tmpfiles
22 {
23     struct _my_tmpfiles *next;
24     char *name;
25 }
26 MY_TMPFILES;
27 
28 static MY_TMPFILES *my_tmpfiles;
29 #endif /* USE_MKSTEMP */
30 
31 char dflag;
32 char gflag;
33 char iflag;
34 char lflag;
35 static char oflag;
36 char rflag;
37 char sflag;
38 char tflag;
39 char vflag;
40 
41 const char *symbol_prefix;
42 const char *myname = "yacc";
43 
44 int lineno;
45 int outline;
46 
47 static char empty_string[] = "";
48 static char default_file_prefix[] = "y";
49 
50 static char *file_prefix = default_file_prefix;
51 
52 char *code_file_name;
53 char *input_file_name = empty_string;
54 char *defines_file_name;
55 char *externs_file_name;
56 
57 static char *graph_file_name;
58 static char *output_file_name;
59 static char *verbose_file_name;
60 
61 FILE *action_file;	/*  a temp file, used to save actions associated    */
62 			/*  with rules until the parser is written          */
63 FILE *code_file;	/*  y.code.c (used when the -r option is specified) */
64 FILE *defines_file;	/*  y.tab.h                                         */
65 FILE *externs_file;	/*  y.tab.i                                         */
66 FILE *input_file;	/*  the input file                                  */
67 FILE *output_file;	/*  y.tab.c                                         */
68 FILE *text_file;	/*  a temp file, used to save text until all        */
69 			/*  symbols have been defined                       */
70 FILE *union_file;	/*  a temp file, used to save the union             */
71 			/*  definition until all symbol have been           */
72 			/*  defined                                         */
73 FILE *verbose_file;	/*  y.output                                        */
74 FILE *graph_file;	/*  y.dot                                           */
75 
76 int nitems;
77 int nrules;
78 int nsyms;
79 int ntokens;
80 int nvars;
81 
82 Value_t start_symbol;
83 char **symbol_name;
84 char **symbol_pname;
85 Value_t *symbol_value;
86 short *symbol_prec;
87 char *symbol_assoc;
88 
89 int pure_parser;
90 int exit_code;
91 
92 Value_t *ritem;
93 Value_t *rlhs;
94 Value_t *rrhs;
95 Value_t *rprec;
96 Assoc_t *rassoc;
97 Value_t **derives;
98 char *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 
111 static int got_intr = 0;
112 
113 void
114 done(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 
161 static void
162 onintr(int sig GCC_UNUSED)
163 {
164     got_intr = 1;
165     done(EXIT_FAILURE);
166 }
167 
168 static void
169 set_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 
185 static void
186 usage(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 
216 static void
217 setflag(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 
271 static void
272 getargs(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 
351 void *
352 allocate(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 
371 static void
372 create_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
445 static void
446 close_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
463 static int
464 my_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  */
509 static FILE *
510 open_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 
572 static void
573 open_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 
640 int
641 main(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