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