Import byacc-20121003.
[dragonfly.git] / contrib / byacc / main.c
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 }