main.c revision 1.1.1.15 1 1.1.1.8 christos /* $NetBSD: main.c,v 1.1.1.15 2026/01/18 16:39:06 christos Exp $ */
2 1.1.1.8 christos
3 1.1.1.15 christos /* Id: main.c,v 1.80 2024/12/31 20:50:11 tom Exp */
4 1.1 christos
5 1.1 christos #include <signal.h>
6 1.1.1.13 christos #if !defined(_WIN32) || defined(__MINGW32__)
7 1.1 christos #include <unistd.h> /* for _exit() */
8 1.1.1.6 christos #else
9 1.1.1.6 christos #include <stdlib.h> /* for _exit() */
10 1.1.1.6 christos #endif
11 1.1 christos
12 1.1 christos #include "defs.h"
13 1.1 christos
14 1.1.1.5 christos #ifdef HAVE_MKSTEMP
15 1.1.1.5 christos # define USE_MKSTEMP 1
16 1.1.1.5 christos #elif defined(HAVE_FCNTL_H)
17 1.1.1.5 christos # define USE_MKSTEMP 1
18 1.1.1.5 christos # include <fcntl.h> /* for open(), O_EXCL, etc. */
19 1.1.1.3 christos #else
20 1.1.1.3 christos # define USE_MKSTEMP 0
21 1.1.1.3 christos #endif
22 1.1.1.3 christos
23 1.1.1.15 christos #ifndef W_OK
24 1.1.1.15 christos #define W_OK 02
25 1.1.1.15 christos #endif
26 1.1.1.15 christos
27 1.1.1.3 christos #if USE_MKSTEMP
28 1.1.1.3 christos #include <sys/types.h>
29 1.1.1.3 christos #include <sys/stat.h>
30 1.1.1.3 christos
31 1.1.1.3 christos typedef struct _my_tmpfiles
32 1.1.1.3 christos {
33 1.1.1.3 christos struct _my_tmpfiles *next;
34 1.1.1.3 christos char *name;
35 1.1.1.3 christos }
36 1.1.1.3 christos MY_TMPFILES;
37 1.1.1.3 christos
38 1.1.1.3 christos static MY_TMPFILES *my_tmpfiles;
39 1.1.1.3 christos #endif /* USE_MKSTEMP */
40 1.1.1.3 christos
41 1.1 christos char dflag;
42 1.1.1.12 christos char dflag2;
43 1.1 christos char gflag;
44 1.1.1.3 christos char iflag;
45 1.1 christos char lflag;
46 1.1.1.2 christos static char oflag;
47 1.1 christos char rflag;
48 1.1.1.5 christos char sflag;
49 1.1 christos char tflag;
50 1.1 christos char vflag;
51 1.1 christos
52 1.1 christos const char *symbol_prefix;
53 1.1 christos const char *myname = "yacc";
54 1.1 christos
55 1.1 christos int lineno;
56 1.1 christos int outline;
57 1.1 christos
58 1.1 christos static char default_file_prefix[] = "y";
59 1.1.1.15 christos static int explicit_file_name;
60 1.1 christos
61 1.1 christos static char *file_prefix = default_file_prefix;
62 1.1 christos
63 1.1 christos char *code_file_name;
64 1.1.1.9 christos char *input_file_name;
65 1.1.1.9 christos size_t input_file_name_len = 0;
66 1.1.1.3 christos char *defines_file_name;
67 1.1.1.3 christos char *externs_file_name;
68 1.1.1.3 christos
69 1.1.1.2 christos static char *graph_file_name;
70 1.1.1.2 christos static char *output_file_name;
71 1.1.1.2 christos static char *verbose_file_name;
72 1.1 christos
73 1.1 christos FILE *action_file; /* a temp file, used to save actions associated */
74 1.1 christos /* with rules until the parser is written */
75 1.1 christos FILE *code_file; /* y.code.c (used when the -r option is specified) */
76 1.1 christos FILE *defines_file; /* y.tab.h */
77 1.1.1.3 christos FILE *externs_file; /* y.tab.i */
78 1.1 christos FILE *input_file; /* the input file */
79 1.1 christos FILE *output_file; /* y.tab.c */
80 1.1 christos FILE *text_file; /* a temp file, used to save text until all */
81 1.1 christos /* symbols have been defined */
82 1.1 christos FILE *union_file; /* a temp file, used to save the union */
83 1.1 christos /* definition until all symbol have been */
84 1.1 christos /* defined */
85 1.1 christos FILE *verbose_file; /* y.output */
86 1.1 christos FILE *graph_file; /* y.dot */
87 1.1 christos
88 1.1.1.6 christos Value_t nitems;
89 1.1.1.6 christos Value_t nrules;
90 1.1.1.6 christos Value_t nsyms;
91 1.1.1.6 christos Value_t ntokens;
92 1.1.1.6 christos Value_t nvars;
93 1.1 christos
94 1.1 christos Value_t start_symbol;
95 1.1 christos char **symbol_name;
96 1.1 christos char **symbol_pname;
97 1.1 christos Value_t *symbol_value;
98 1.1.1.6 christos Value_t *symbol_prec;
99 1.1 christos char *symbol_assoc;
100 1.1 christos
101 1.1.1.2 christos int pure_parser;
102 1.1.1.6 christos int token_table;
103 1.1.1.9 christos int error_verbose;
104 1.1.1.6 christos
105 1.1.1.6 christos #if defined(YYBTYACC)
106 1.1.1.6 christos Value_t *symbol_pval;
107 1.1.1.6 christos char **symbol_destructor;
108 1.1.1.6 christos char **symbol_type_tag;
109 1.1.1.6 christos int locations = 0; /* default to no position processing */
110 1.1.1.6 christos int backtrack = 0; /* default is no backtracking */
111 1.1.1.9 christos char *initial_action = NULL;
112 1.1.1.6 christos #endif
113 1.1.1.6 christos
114 1.1 christos int exit_code;
115 1.1 christos
116 1.1 christos Value_t *ritem;
117 1.1 christos Value_t *rlhs;
118 1.1 christos Value_t *rrhs;
119 1.1 christos Value_t *rprec;
120 1.1 christos Assoc_t *rassoc;
121 1.1 christos Value_t **derives;
122 1.1 christos char *nullable;
123 1.1 christos
124 1.1 christos /*
125 1.1 christos * Since fclose() is called via the signal handler, it might die. Don't loop
126 1.1 christos * if there is a problem closing a file.
127 1.1 christos */
128 1.1 christos #define DO_CLOSE(fp) \
129 1.1.1.15 christos if (fp != NULL) { \
130 1.1 christos FILE *use = fp; \
131 1.1.1.15 christos fp = NULL; \
132 1.1 christos fclose(use); \
133 1.1 christos }
134 1.1 christos
135 1.1 christos static int got_intr = 0;
136 1.1 christos
137 1.1 christos void
138 1.1 christos done(int k)
139 1.1 christos {
140 1.1 christos DO_CLOSE(input_file);
141 1.1 christos DO_CLOSE(output_file);
142 1.1.1.6 christos if (iflag)
143 1.1.1.6 christos DO_CLOSE(externs_file);
144 1.1.1.6 christos if (rflag)
145 1.1.1.6 christos DO_CLOSE(code_file);
146 1.1 christos
147 1.1 christos DO_CLOSE(action_file);
148 1.1 christos DO_CLOSE(defines_file);
149 1.1 christos DO_CLOSE(graph_file);
150 1.1 christos DO_CLOSE(text_file);
151 1.1 christos DO_CLOSE(union_file);
152 1.1 christos DO_CLOSE(verbose_file);
153 1.1 christos
154 1.1 christos if (got_intr)
155 1.1 christos _exit(EXIT_FAILURE);
156 1.1 christos
157 1.1 christos #ifdef NO_LEAKS
158 1.1.1.14 christos DO_FREE(input_file_name);
159 1.1.1.14 christos
160 1.1 christos if (rflag)
161 1.1 christos DO_FREE(code_file_name);
162 1.1 christos
163 1.1.1.12 christos if (dflag && !dflag2)
164 1.1 christos DO_FREE(defines_file_name);
165 1.1 christos
166 1.1.1.3 christos if (iflag)
167 1.1.1.3 christos DO_FREE(externs_file_name);
168 1.1.1.3 christos
169 1.1 christos if (oflag)
170 1.1 christos DO_FREE(output_file_name);
171 1.1 christos
172 1.1 christos if (vflag)
173 1.1 christos DO_FREE(verbose_file_name);
174 1.1 christos
175 1.1 christos if (gflag)
176 1.1 christos DO_FREE(graph_file_name);
177 1.1 christos
178 1.1 christos lr0_leaks();
179 1.1 christos lalr_leaks();
180 1.1 christos mkpar_leaks();
181 1.1.1.6 christos mstring_leaks();
182 1.1 christos output_leaks();
183 1.1 christos reader_leaks();
184 1.1 christos #endif
185 1.1 christos
186 1.1 christos exit(k);
187 1.1 christos }
188 1.1 christos
189 1.1 christos static void
190 1.1 christos onintr(int sig GCC_UNUSED)
191 1.1 christos {
192 1.1 christos got_intr = 1;
193 1.1 christos done(EXIT_FAILURE);
194 1.1 christos }
195 1.1 christos
196 1.1 christos static void
197 1.1 christos set_signals(void)
198 1.1 christos {
199 1.1 christos #ifdef SIGINT
200 1.1 christos if (signal(SIGINT, SIG_IGN) != SIG_IGN)
201 1.1 christos signal(SIGINT, onintr);
202 1.1 christos #endif
203 1.1 christos #ifdef SIGTERM
204 1.1 christos if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
205 1.1 christos signal(SIGTERM, onintr);
206 1.1 christos #endif
207 1.1 christos #ifdef SIGHUP
208 1.1 christos if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
209 1.1 christos signal(SIGHUP, onintr);
210 1.1 christos #endif
211 1.1 christos }
212 1.1 christos
213 1.1.1.14 christos #define SIZEOF(v) (sizeof(v) / sizeof((v)[0]))
214 1.1.1.14 christos
215 1.1.1.14 christos /*
216 1.1.1.14 christos * Long options are provided only as a compatibility aid for scripters.
217 1.1.1.14 christos */
218 1.1.1.14 christos /* *INDENT-OFF* */
219 1.1.1.14 christos static const struct {
220 1.1.1.14 christos const char long_opt[16];
221 1.1.1.14 christos const char yacc_arg;
222 1.1.1.14 christos const char yacc_opt;
223 1.1.1.14 christos } long_opts[] = {
224 1.1.1.14 christos { "defines", 1, 'H' },
225 1.1.1.14 christos { "file-prefix", 1, 'b' },
226 1.1.1.14 christos { "graph", 0, 'g' },
227 1.1.1.14 christos { "help", 0, 'h' },
228 1.1.1.14 christos { "name-prefix", 1, 'p' },
229 1.1.1.14 christos { "no-lines", 0, 'l' },
230 1.1.1.14 christos { "output", 1, 'o' },
231 1.1.1.14 christos { "version", 0, 'V' }
232 1.1.1.14 christos };
233 1.1.1.14 christos /* *INDENT-ON* */
234 1.1.1.14 christos
235 1.1.1.14 christos /*
236 1.1.1.14 christos * Usage-message is designed for 80 columns, with some unknowns. Account for
237 1.1.1.14 christos * those in the maximum width so that the usage message uses no relocatable
238 1.1.1.14 christos * pointers.
239 1.1.1.14 christos */
240 1.1.1.14 christos #define USAGE_COLS (80 + sizeof(DEFINES_SUFFIX) + sizeof(OUTPUT_SUFFIX))
241 1.1.1.14 christos
242 1.1 christos static void
243 1.1 christos usage(void)
244 1.1 christos {
245 1.1.1.14 christos /* *INDENT-OFF* */
246 1.1.1.14 christos static const char msg[][USAGE_COLS] =
247 1.1 christos {
248 1.1.1.14 christos { " -b file_prefix set filename prefix (default \"y.\")" },
249 1.1.1.14 christos { " -B create a backtracking parser" },
250 1.1.1.14 christos { " -d write definitions (" DEFINES_SUFFIX ")" },
251 1.1.1.14 christos { " -h print this help-message" },
252 1.1.1.14 christos { " -H defines_file write definitions to defines_file" },
253 1.1.1.14 christos { " -i write interface (y.tab.i)" },
254 1.1.1.14 christos { " -g write a graphical description" },
255 1.1.1.14 christos { " -l suppress #line directives" },
256 1.1.1.14 christos { " -L enable position processing, e.g., \"%locations\"" },
257 1.1.1.14 christos { " -o output_file (default \"" OUTPUT_SUFFIX "\")" },
258 1.1.1.14 christos { " -p symbol_prefix set symbol prefix (default \"yy\")" },
259 1.1.1.14 christos { " -P create a reentrant parser, e.g., \"%pure-parser\"" },
260 1.1.1.14 christos { " -r produce separate code and table files (y.code.c)" },
261 1.1.1.14 christos { " -s suppress #define's for quoted names in %token lines" },
262 1.1.1.14 christos { " -t add debugging support" },
263 1.1.1.14 christos { " -v write description (y.output)" },
264 1.1.1.14 christos { " -V show version information and exit" },
265 1.1 christos };
266 1.1.1.14 christos /* *INDENT-ON* */
267 1.1 christos unsigned n;
268 1.1 christos
269 1.1 christos fflush(stdout);
270 1.1 christos fprintf(stderr, "Usage: %s [options] filename\n", myname);
271 1.1.1.14 christos
272 1.1.1.14 christos fprintf(stderr, "\nOptions:\n");
273 1.1.1.14 christos for (n = 0; n < SIZEOF(msg); ++n)
274 1.1.1.14 christos {
275 1.1 christos fprintf(stderr, "%s\n", msg[n]);
276 1.1.1.14 christos }
277 1.1.1.14 christos
278 1.1.1.14 christos fprintf(stderr, "\nLong options:\n");
279 1.1.1.14 christos for (n = 0; n < SIZEOF(long_opts); ++n)
280 1.1.1.14 christos {
281 1.1.1.14 christos fprintf(stderr, " --%-20s-%c\n",
282 1.1.1.14 christos long_opts[n].long_opt,
283 1.1.1.14 christos long_opts[n].yacc_opt);
284 1.1.1.14 christos }
285 1.1 christos
286 1.1.1.12 christos exit(EXIT_FAILURE);
287 1.1 christos }
288 1.1 christos
289 1.1 christos static void
290 1.1.1.14 christos invalid_option(const char *option)
291 1.1.1.14 christos {
292 1.1.1.14 christos fprintf(stderr, "invalid option: %s\n", option);
293 1.1.1.14 christos usage();
294 1.1.1.14 christos }
295 1.1.1.14 christos
296 1.1.1.14 christos static void
297 1.1 christos setflag(int ch)
298 1.1 christos {
299 1.1 christos switch (ch)
300 1.1 christos {
301 1.1.1.6 christos case 'B':
302 1.1.1.6 christos #if defined(YYBTYACC)
303 1.1.1.6 christos backtrack = 1;
304 1.1.1.6 christos #else
305 1.1.1.6 christos unsupported_flag_warning("-B", "reconfigure with --enable-btyacc");
306 1.1.1.6 christos #endif
307 1.1.1.6 christos break;
308 1.1.1.6 christos
309 1.1 christos case 'd':
310 1.1 christos dflag = 1;
311 1.1.1.12 christos dflag2 = 0;
312 1.1 christos break;
313 1.1 christos
314 1.1 christos case 'g':
315 1.1 christos gflag = 1;
316 1.1 christos break;
317 1.1 christos
318 1.1.1.3 christos case 'i':
319 1.1.1.3 christos iflag = 1;
320 1.1.1.3 christos break;
321 1.1.1.3 christos
322 1.1 christos case 'l':
323 1.1 christos lflag = 1;
324 1.1 christos break;
325 1.1 christos
326 1.1.1.6 christos case 'L':
327 1.1.1.6 christos #if defined(YYBTYACC)
328 1.1.1.6 christos locations = 1;
329 1.1.1.6 christos #else
330 1.1.1.10 christos unsupported_flag_warning("-L", "reconfigure with --enable-btyacc");
331 1.1.1.6 christos #endif
332 1.1.1.6 christos break;
333 1.1.1.6 christos
334 1.1.1.2 christos case 'P':
335 1.1.1.2 christos pure_parser = 1;
336 1.1.1.2 christos break;
337 1.1.1.2 christos
338 1.1 christos case 'r':
339 1.1 christos rflag = 1;
340 1.1 christos break;
341 1.1 christos
342 1.1.1.5 christos case 's':
343 1.1.1.5 christos sflag = 1;
344 1.1.1.5 christos break;
345 1.1.1.5 christos
346 1.1 christos case 't':
347 1.1 christos tflag = 1;
348 1.1 christos break;
349 1.1 christos
350 1.1 christos case 'v':
351 1.1 christos vflag = 1;
352 1.1 christos break;
353 1.1 christos
354 1.1 christos case 'V':
355 1.1 christos printf("%s - %s\n", myname, VERSION);
356 1.1 christos exit(EXIT_SUCCESS);
357 1.1 christos
358 1.1.1.2 christos case 'y':
359 1.1.1.2 christos /* noop for bison compatibility. byacc is already designed to be posix
360 1.1.1.2 christos * yacc compatible. */
361 1.1.1.2 christos break;
362 1.1.1.2 christos
363 1.1 christos default:
364 1.1 christos usage();
365 1.1 christos }
366 1.1 christos }
367 1.1 christos
368 1.1 christos static void
369 1.1 christos getargs(int argc, char *argv[])
370 1.1 christos {
371 1.1 christos int i;
372 1.1.1.13 christos #ifdef HAVE_GETOPT
373 1.1.1.13 christos int ch;
374 1.1.1.14 christos #endif
375 1.1.1.13 christos
376 1.1.1.14 christos /*
377 1.1.1.14 christos * Map bison's long-options into yacc short options.
378 1.1.1.14 christos */
379 1.1.1.14 christos for (i = 1; i < argc; ++i)
380 1.1.1.14 christos {
381 1.1.1.14 christos char *a = argv[i];
382 1.1.1.14 christos
383 1.1.1.14 christos if (!strncmp(a, "--", 2))
384 1.1.1.14 christos {
385 1.1.1.14 christos char *eqls;
386 1.1.1.14 christos size_t lc;
387 1.1.1.14 christos size_t len;
388 1.1.1.14 christos
389 1.1.1.14 christos if ((len = strlen(a)) == 2)
390 1.1.1.14 christos break;
391 1.1.1.14 christos
392 1.1.1.14 christos if ((eqls = strchr(a, '=')) != NULL)
393 1.1.1.14 christos {
394 1.1.1.14 christos len = (size_t)(eqls - a);
395 1.1.1.14 christos if (len == 0 || eqls[1] == '\0')
396 1.1.1.14 christos invalid_option(a);
397 1.1.1.14 christos }
398 1.1.1.14 christos
399 1.1.1.14 christos for (lc = 0; lc < SIZEOF(long_opts); ++lc)
400 1.1.1.14 christos {
401 1.1.1.14 christos if (!strncmp(long_opts[lc].long_opt, a + 2, len - 2))
402 1.1.1.14 christos {
403 1.1.1.14 christos if (eqls != NULL && !long_opts[lc].yacc_arg)
404 1.1.1.14 christos invalid_option(a);
405 1.1.1.14 christos *a++ = '-';
406 1.1.1.14 christos *a++ = long_opts[lc].yacc_opt;
407 1.1.1.14 christos *a = '\0';
408 1.1.1.14 christos if (eqls)
409 1.1.1.14 christos {
410 1.1.1.14 christos while ((*a++ = *++eqls) != '\0') /* empty */ ;
411 1.1.1.14 christos }
412 1.1.1.14 christos break;
413 1.1.1.14 christos }
414 1.1.1.14 christos }
415 1.1.1.14 christos if (!strncmp(a, "--", 2))
416 1.1.1.14 christos invalid_option(a);
417 1.1.1.14 christos }
418 1.1.1.14 christos }
419 1.1.1.14 christos
420 1.1.1.14 christos #ifdef HAVE_GETOPT
421 1.1.1.13 christos if (argc > 0)
422 1.1.1.13 christos myname = argv[0];
423 1.1.1.13 christos
424 1.1.1.14 christos while ((ch = getopt(argc, argv, "Bb:dghH:ilLo:Pp:rstVvy")) != -1)
425 1.1.1.13 christos {
426 1.1.1.13 christos switch (ch)
427 1.1.1.13 christos {
428 1.1.1.13 christos case 'b':
429 1.1.1.13 christos file_prefix = optarg;
430 1.1.1.13 christos break;
431 1.1.1.14 christos case 'h':
432 1.1.1.14 christos usage();
433 1.1.1.14 christos break;
434 1.1.1.13 christos case 'H':
435 1.1.1.13 christos dflag = dflag2 = 1;
436 1.1.1.13 christos defines_file_name = optarg;
437 1.1.1.13 christos break;
438 1.1.1.13 christos case 'o':
439 1.1.1.13 christos output_file_name = optarg;
440 1.1.1.15 christos explicit_file_name = 1;
441 1.1.1.13 christos break;
442 1.1.1.13 christos case 'p':
443 1.1.1.13 christos symbol_prefix = optarg;
444 1.1.1.13 christos break;
445 1.1.1.13 christos default:
446 1.1.1.13 christos setflag(ch);
447 1.1.1.13 christos break;
448 1.1.1.13 christos }
449 1.1.1.13 christos }
450 1.1.1.13 christos if ((i = optind) < argc)
451 1.1.1.13 christos {
452 1.1.1.13 christos /* getopt handles "--" specially, while we handle "-" specially */
453 1.1.1.13 christos if (!strcmp(argv[i], "-"))
454 1.1.1.13 christos {
455 1.1.1.13 christos if ((i + 1) < argc)
456 1.1.1.13 christos usage();
457 1.1.1.13 christos input_file = stdin;
458 1.1.1.13 christos return;
459 1.1.1.13 christos }
460 1.1.1.13 christos }
461 1.1.1.13 christos #else
462 1.1 christos char *s;
463 1.1 christos int ch;
464 1.1 christos
465 1.1 christos if (argc > 0)
466 1.1 christos myname = argv[0];
467 1.1 christos
468 1.1 christos for (i = 1; i < argc; ++i)
469 1.1 christos {
470 1.1 christos s = argv[i];
471 1.1 christos if (*s != '-')
472 1.1 christos break;
473 1.1 christos switch (ch = *++s)
474 1.1 christos {
475 1.1 christos case '\0':
476 1.1 christos input_file = stdin;
477 1.1 christos if (i + 1 < argc)
478 1.1 christos usage();
479 1.1 christos return;
480 1.1 christos
481 1.1 christos case '-':
482 1.1 christos ++i;
483 1.1 christos goto no_more_options;
484 1.1 christos
485 1.1 christos case 'b':
486 1.1 christos if (*++s)
487 1.1 christos file_prefix = s;
488 1.1 christos else if (++i < argc)
489 1.1 christos file_prefix = argv[i];
490 1.1 christos else
491 1.1 christos usage();
492 1.1 christos continue;
493 1.1 christos
494 1.1.1.12 christos case 'H':
495 1.1.1.12 christos dflag = dflag2 = 1;
496 1.1.1.12 christos if (*++s)
497 1.1.1.12 christos defines_file_name = s;
498 1.1.1.12 christos else if (++i < argc)
499 1.1.1.12 christos defines_file_name = argv[i];
500 1.1.1.12 christos else
501 1.1.1.12 christos usage();
502 1.1.1.12 christos continue;
503 1.1.1.12 christos
504 1.1 christos case 'o':
505 1.1 christos if (*++s)
506 1.1 christos output_file_name = s;
507 1.1 christos else if (++i < argc)
508 1.1 christos output_file_name = argv[i];
509 1.1 christos else
510 1.1 christos usage();
511 1.1.1.15 christos explicit_file_name = 1;
512 1.1 christos continue;
513 1.1 christos
514 1.1 christos case 'p':
515 1.1 christos if (*++s)
516 1.1 christos symbol_prefix = s;
517 1.1 christos else if (++i < argc)
518 1.1 christos symbol_prefix = argv[i];
519 1.1 christos else
520 1.1 christos usage();
521 1.1 christos continue;
522 1.1 christos
523 1.1 christos default:
524 1.1 christos setflag(ch);
525 1.1 christos break;
526 1.1 christos }
527 1.1 christos
528 1.1 christos for (;;)
529 1.1 christos {
530 1.1 christos switch (ch = *++s)
531 1.1 christos {
532 1.1 christos case '\0':
533 1.1 christos goto end_of_option;
534 1.1 christos
535 1.1 christos default:
536 1.1 christos setflag(ch);
537 1.1 christos break;
538 1.1 christos }
539 1.1 christos }
540 1.1 christos end_of_option:;
541 1.1 christos }
542 1.1 christos
543 1.1.1.13 christos no_more_options:
544 1.1.1.13 christos
545 1.1.1.13 christos #endif /* HAVE_GETOPT */
546 1.1 christos if (i + 1 != argc)
547 1.1 christos usage();
548 1.1.1.9 christos input_file_name_len = strlen(argv[i]);
549 1.1.1.9 christos input_file_name = TMALLOC(char, input_file_name_len + 1);
550 1.1.1.9 christos NO_SPACE(input_file_name);
551 1.1.1.9 christos strcpy(input_file_name, argv[i]);
552 1.1 christos }
553 1.1 christos
554 1.1.1.3 christos void *
555 1.1.1.2 christos allocate(size_t n)
556 1.1 christos {
557 1.1.1.3 christos void *p;
558 1.1 christos
559 1.1 christos p = NULL;
560 1.1 christos if (n)
561 1.1 christos {
562 1.1 christos p = CALLOC(1, n);
563 1.1.1.2 christos NO_SPACE(p);
564 1.1 christos }
565 1.1 christos return (p);
566 1.1 christos }
567 1.1 christos
568 1.1 christos #define CREATE_FILE_NAME(dest, suffix) \
569 1.1.1.6 christos dest = alloc_file_name(len, suffix)
570 1.1.1.6 christos
571 1.1.1.6 christos static char *
572 1.1.1.6 christos alloc_file_name(size_t len, const char *suffix)
573 1.1.1.6 christos {
574 1.1.1.6 christos char *result = TMALLOC(char, len + strlen(suffix) + 1);
575 1.1.1.14 christos if (result == NULL)
576 1.1.1.14 christos on_error();
577 1.1.1.6 christos strcpy(result, file_prefix);
578 1.1.1.6 christos strcpy(result + len, suffix);
579 1.1.1.6 christos return result;
580 1.1.1.6 christos }
581 1.1 christos
582 1.1.1.9 christos static char *
583 1.1.1.9 christos find_suffix(char *name, const char *suffix)
584 1.1.1.9 christos {
585 1.1.1.9 christos size_t len = strlen(name);
586 1.1.1.9 christos size_t slen = strlen(suffix);
587 1.1.1.9 christos if (len >= slen)
588 1.1.1.9 christos {
589 1.1.1.9 christos name += len - slen;
590 1.1.1.9 christos if (strcmp(name, suffix) == 0)
591 1.1.1.9 christos return name;
592 1.1.1.9 christos }
593 1.1.1.9 christos return NULL;
594 1.1.1.9 christos }
595 1.1.1.9 christos
596 1.1 christos static void
597 1.1 christos create_file_names(void)
598 1.1 christos {
599 1.1 christos size_t len;
600 1.1 christos const char *defines_suffix;
601 1.1.1.3 christos const char *externs_suffix;
602 1.1.1.9 christos char *suffix;
603 1.1 christos
604 1.1.1.9 christos suffix = NULL;
605 1.1 christos defines_suffix = DEFINES_SUFFIX;
606 1.1.1.3 christos externs_suffix = EXTERNS_SUFFIX;
607 1.1 christos
608 1.1 christos /* compute the file_prefix from the user provided output_file_name */
609 1.1.1.15 christos if (output_file_name != NULL)
610 1.1 christos {
611 1.1.1.9 christos if (!(suffix = find_suffix(output_file_name, OUTPUT_SUFFIX))
612 1.1.1.9 christos && (suffix = find_suffix(output_file_name, ".c")))
613 1.1.1.3 christos {
614 1.1 christos defines_suffix = ".h";
615 1.1.1.3 christos externs_suffix = ".i";
616 1.1.1.3 christos }
617 1.1 christos }
618 1.1 christos
619 1.1.1.9 christos if (suffix != NULL)
620 1.1 christos {
621 1.1.1.14 christos len = (size_t)(suffix - output_file_name);
622 1.1.1.5 christos file_prefix = TMALLOC(char, len + 1);
623 1.1.1.2 christos NO_SPACE(file_prefix);
624 1.1 christos strncpy(file_prefix, output_file_name, len)[len] = 0;
625 1.1 christos }
626 1.1 christos else
627 1.1 christos len = strlen(file_prefix);
628 1.1 christos
629 1.1 christos /* if "-o filename" was not given */
630 1.1.1.15 christos if (output_file_name == NULL)
631 1.1 christos {
632 1.1 christos oflag = 1;
633 1.1 christos CREATE_FILE_NAME(output_file_name, OUTPUT_SUFFIX);
634 1.1 christos }
635 1.1 christos
636 1.1 christos if (rflag)
637 1.1 christos {
638 1.1 christos CREATE_FILE_NAME(code_file_name, CODE_SUFFIX);
639 1.1 christos }
640 1.1 christos else
641 1.1 christos code_file_name = output_file_name;
642 1.1 christos
643 1.1.1.12 christos if (dflag && !dflag2)
644 1.1 christos {
645 1.1.1.15 christos if (explicit_file_name)
646 1.1.1.15 christos {
647 1.1.1.15 christos char *xsuffix;
648 1.1.1.15 christos defines_file_name = strdup(output_file_name);
649 1.1.1.15 christos if (defines_file_name == NULL)
650 1.1.1.15 christos on_error();
651 1.1.1.15 christos /* does the output_file_name have a known suffix */
652 1.1.1.15 christos xsuffix = strrchr(output_file_name, '.');
653 1.1.1.15 christos if (xsuffix != NULL &&
654 1.1.1.15 christos (!strcmp(xsuffix, ".c") || /* good, old-fashioned C */
655 1.1.1.15 christos !strcmp(xsuffix, ".C") || /* C++, or C on Windows */
656 1.1.1.15 christos !strcmp(xsuffix, ".cc") || /* C++ */
657 1.1.1.15 christos !strcmp(xsuffix, ".cxx") || /* C++ */
658 1.1.1.15 christos !strcmp(xsuffix, ".cpp"))) /* C++ (Windows) */
659 1.1.1.15 christos {
660 1.1.1.15 christos strncpy(defines_file_name, output_file_name,
661 1.1.1.15 christos (size_t) (xsuffix - output_file_name + 1));
662 1.1.1.15 christos defines_file_name[xsuffix - output_file_name + 1] = 'h';
663 1.1.1.15 christos defines_file_name[xsuffix - output_file_name + 2] = 0;
664 1.1.1.15 christos }
665 1.1.1.15 christos else
666 1.1.1.15 christos {
667 1.1.1.15 christos fprintf(stderr, "%s: suffix of output file name %s"
668 1.1.1.15 christos " not recognized, no -d file generated.\n",
669 1.1.1.15 christos myname, output_file_name);
670 1.1.1.15 christos dflag = 0;
671 1.1.1.15 christos free(defines_file_name);
672 1.1.1.15 christos defines_file_name = NULL;
673 1.1.1.15 christos }
674 1.1.1.15 christos }
675 1.1.1.15 christos else
676 1.1.1.15 christos {
677 1.1.1.15 christos CREATE_FILE_NAME(defines_file_name, defines_suffix);
678 1.1.1.15 christos }
679 1.1 christos }
680 1.1 christos
681 1.1.1.3 christos if (iflag)
682 1.1.1.3 christos {
683 1.1.1.3 christos CREATE_FILE_NAME(externs_file_name, externs_suffix);
684 1.1.1.3 christos }
685 1.1.1.3 christos
686 1.1 christos if (vflag)
687 1.1 christos {
688 1.1 christos CREATE_FILE_NAME(verbose_file_name, VERBOSE_SUFFIX);
689 1.1 christos }
690 1.1 christos
691 1.1 christos if (gflag)
692 1.1 christos {
693 1.1 christos CREATE_FILE_NAME(graph_file_name, GRAPH_SUFFIX);
694 1.1 christos }
695 1.1 christos
696 1.1.1.9 christos if (suffix != NULL)
697 1.1 christos {
698 1.1 christos FREE(file_prefix);
699 1.1 christos }
700 1.1 christos }
701 1.1 christos
702 1.1.1.3 christos #if USE_MKSTEMP
703 1.1.1.3 christos static void
704 1.1.1.3 christos close_tmpfiles(void)
705 1.1.1.3 christos {
706 1.1.1.15 christos while (my_tmpfiles != NULL)
707 1.1.1.3 christos {
708 1.1.1.3 christos MY_TMPFILES *next = my_tmpfiles->next;
709 1.1.1.3 christos
710 1.1.1.6 christos (void)chmod(my_tmpfiles->name, 0644);
711 1.1.1.6 christos (void)unlink(my_tmpfiles->name);
712 1.1.1.3 christos
713 1.1.1.3 christos free(my_tmpfiles->name);
714 1.1.1.3 christos free(my_tmpfiles);
715 1.1.1.3 christos
716 1.1.1.3 christos my_tmpfiles = next;
717 1.1.1.3 christos }
718 1.1.1.3 christos }
719 1.1.1.3 christos
720 1.1.1.3 christos #ifndef HAVE_MKSTEMP
721 1.1.1.3 christos static int
722 1.1.1.3 christos my_mkstemp(char *temp)
723 1.1.1.3 christos {
724 1.1.1.3 christos int fd;
725 1.1.1.3 christos char *dname;
726 1.1.1.3 christos char *fname;
727 1.1.1.3 christos char *name;
728 1.1.1.3 christos
729 1.1.1.3 christos /*
730 1.1.1.3 christos * Split-up to use tempnam, rather than tmpnam; the latter (like
731 1.1.1.3 christos * mkstemp) is unusable on Windows.
732 1.1.1.3 christos */
733 1.1.1.3 christos if ((fname = strrchr(temp, '/')) != 0)
734 1.1.1.3 christos {
735 1.1.1.3 christos dname = strdup(temp);
736 1.1.1.3 christos dname[++fname - temp] = '\0';
737 1.1.1.3 christos }
738 1.1.1.3 christos else
739 1.1.1.3 christos {
740 1.1.1.3 christos dname = 0;
741 1.1.1.3 christos fname = temp;
742 1.1.1.3 christos }
743 1.1.1.3 christos if ((name = tempnam(dname, fname)) != 0)
744 1.1.1.3 christos {
745 1.1.1.3 christos fd = open(name, O_CREAT | O_EXCL | O_RDWR);
746 1.1.1.3 christos strcpy(temp, name);
747 1.1.1.3 christos }
748 1.1.1.3 christos else
749 1.1.1.3 christos {
750 1.1.1.3 christos fd = -1;
751 1.1.1.3 christos }
752 1.1.1.3 christos
753 1.1.1.3 christos if (dname != 0)
754 1.1.1.3 christos free(dname);
755 1.1.1.3 christos
756 1.1.1.3 christos return fd;
757 1.1.1.3 christos }
758 1.1.1.3 christos #define mkstemp(s) my_mkstemp(s)
759 1.1.1.3 christos #endif
760 1.1.1.3 christos
761 1.1.1.3 christos #endif
762 1.1.1.3 christos
763 1.1.1.3 christos /*
764 1.1.1.3 christos * tmpfile() should be adequate, except that it may require special privileges
765 1.1.1.3 christos * to use, e.g., MinGW and Windows 7 where it tries to use the root directory.
766 1.1.1.3 christos */
767 1.1.1.3 christos static FILE *
768 1.1.1.3 christos open_tmpfile(const char *label)
769 1.1.1.3 christos {
770 1.1.1.7 christos #define MY_FMT "%s/%.*sXXXXXX"
771 1.1.1.3 christos FILE *result;
772 1.1.1.3 christos #if USE_MKSTEMP
773 1.1.1.3 christos const char *tmpdir;
774 1.1.1.3 christos char *name;
775 1.1.1.3 christos
776 1.1.1.15 christos if (((tmpdir = getenv("TMPDIR")) == NULL || access(tmpdir, W_OK) != 0) &&
777 1.1.1.15 christos ((tmpdir = getenv("TEMP")) == NULL || access(tmpdir, W_OK) != 0))
778 1.1.1.3 christos {
779 1.1.1.3 christos #ifdef P_tmpdir
780 1.1.1.3 christos tmpdir = P_tmpdir;
781 1.1.1.3 christos #else
782 1.1.1.3 christos tmpdir = "/tmp";
783 1.1.1.3 christos #endif
784 1.1.1.3 christos if (access(tmpdir, W_OK) != 0)
785 1.1.1.3 christos tmpdir = ".";
786 1.1.1.3 christos }
787 1.1.1.3 christos
788 1.1.1.7 christos /* The size of the format is guaranteed to be longer than the result from
789 1.1.1.7 christos * printing empty strings with it; this calculation accounts for the
790 1.1.1.7 christos * string-lengths as well.
791 1.1.1.7 christos */
792 1.1.1.7 christos name = malloc(strlen(tmpdir) + sizeof(MY_FMT) + strlen(label));
793 1.1.1.3 christos
794 1.1.1.15 christos result = NULL;
795 1.1.1.15 christos if (name != NULL)
796 1.1.1.3 christos {
797 1.1.1.13 christos int fd;
798 1.1.1.13 christos const char *mark;
799 1.1.1.13 christos
800 1.1.1.6 christos mode_t save_umask = umask(0177);
801 1.1.1.6 christos
802 1.1.1.15 christos if ((mark = strrchr(label, '_')) == NULL)
803 1.1.1.3 christos mark = label + strlen(label);
804 1.1.1.3 christos
805 1.1.1.7 christos sprintf(name, MY_FMT, tmpdir, (int)(mark - label), label);
806 1.1.1.3 christos fd = mkstemp(name);
807 1.1.1.13 christos if (fd >= 0
808 1.1.1.15 christos && (result = fdopen(fd, "w+")) != NULL)
809 1.1.1.3 christos {
810 1.1.1.13 christos MY_TMPFILES *item;
811 1.1.1.3 christos
812 1.1.1.15 christos if (my_tmpfiles == NULL)
813 1.1.1.13 christos {
814 1.1.1.13 christos atexit(close_tmpfiles);
815 1.1.1.13 christos }
816 1.1.1.3 christos
817 1.1.1.13 christos item = NEW(MY_TMPFILES);
818 1.1.1.13 christos NO_SPACE(item);
819 1.1.1.3 christos
820 1.1.1.13 christos item->name = name;
821 1.1.1.13 christos NO_SPACE(item->name);
822 1.1.1.3 christos
823 1.1.1.13 christos item->next = my_tmpfiles;
824 1.1.1.13 christos my_tmpfiles = item;
825 1.1.1.13 christos }
826 1.1.1.13 christos else
827 1.1.1.13 christos {
828 1.1.1.13 christos FREE(name);
829 1.1.1.3 christos }
830 1.1.1.6 christos (void)umask(save_umask);
831 1.1.1.3 christos }
832 1.1.1.3 christos #else
833 1.1.1.3 christos result = tmpfile();
834 1.1.1.3 christos #endif
835 1.1.1.3 christos
836 1.1.1.15 christos if (result == NULL)
837 1.1.1.3 christos open_error(label);
838 1.1.1.3 christos return result;
839 1.1.1.7 christos #undef MY_FMT
840 1.1.1.3 christos }
841 1.1.1.3 christos
842 1.1 christos static void
843 1.1 christos open_files(void)
844 1.1 christos {
845 1.1 christos create_file_names();
846 1.1 christos
847 1.1.1.15 christos if (input_file == NULL)
848 1.1 christos {
849 1.1 christos input_file = fopen(input_file_name, "r");
850 1.1.1.15 christos if (input_file == NULL)
851 1.1 christos open_error(input_file_name);
852 1.1 christos }
853 1.1 christos
854 1.1.1.3 christos action_file = open_tmpfile("action_file");
855 1.1.1.3 christos text_file = open_tmpfile("text_file");
856 1.1 christos
857 1.1 christos if (vflag)
858 1.1 christos {
859 1.1 christos verbose_file = fopen(verbose_file_name, "w");
860 1.1.1.15 christos if (verbose_file == NULL)
861 1.1 christos open_error(verbose_file_name);
862 1.1 christos }
863 1.1 christos
864 1.1 christos if (gflag)
865 1.1 christos {
866 1.1 christos graph_file = fopen(graph_file_name, "w");
867 1.1.1.15 christos if (graph_file == NULL)
868 1.1 christos open_error(graph_file_name);
869 1.1 christos fprintf(graph_file, "digraph %s {\n", file_prefix);
870 1.1 christos fprintf(graph_file, "\tedge [fontsize=10];\n");
871 1.1 christos fprintf(graph_file, "\tnode [shape=box,fontsize=10];\n");
872 1.1 christos fprintf(graph_file, "\torientation=landscape;\n");
873 1.1 christos fprintf(graph_file, "\trankdir=LR;\n");
874 1.1 christos fprintf(graph_file, "\t/*\n");
875 1.1 christos fprintf(graph_file, "\tmargin=0.2;\n");
876 1.1 christos fprintf(graph_file, "\tpage=\"8.27,11.69\"; // for A4 printing\n");
877 1.1 christos fprintf(graph_file, "\tratio=auto;\n");
878 1.1 christos fprintf(graph_file, "\t*/\n");
879 1.1 christos }
880 1.1 christos
881 1.1.1.12 christos if (dflag || dflag2)
882 1.1 christos {
883 1.1 christos defines_file = fopen(defines_file_name, "w");
884 1.1.1.15 christos if (defines_file == NULL)
885 1.1 christos open_error(defines_file_name);
886 1.1.1.3 christos union_file = open_tmpfile("union_file");
887 1.1.1.3 christos }
888 1.1.1.3 christos
889 1.1.1.3 christos if (iflag)
890 1.1.1.3 christos {
891 1.1.1.3 christos externs_file = fopen(externs_file_name, "w");
892 1.1.1.15 christos if (externs_file == NULL)
893 1.1.1.3 christos open_error(externs_file_name);
894 1.1 christos }
895 1.1 christos
896 1.1 christos output_file = fopen(output_file_name, "w");
897 1.1.1.15 christos if (output_file == NULL)
898 1.1 christos open_error(output_file_name);
899 1.1 christos
900 1.1 christos if (rflag)
901 1.1 christos {
902 1.1 christos code_file = fopen(code_file_name, "w");
903 1.1.1.15 christos if (code_file == NULL)
904 1.1 christos open_error(code_file_name);
905 1.1 christos }
906 1.1 christos else
907 1.1 christos code_file = output_file;
908 1.1 christos }
909 1.1 christos
910 1.1 christos int
911 1.1 christos main(int argc, char *argv[])
912 1.1 christos {
913 1.1 christos SRexpect = -1;
914 1.1 christos RRexpect = -1;
915 1.1 christos exit_code = EXIT_SUCCESS;
916 1.1 christos
917 1.1 christos set_signals();
918 1.1 christos getargs(argc, argv);
919 1.1 christos open_files();
920 1.1 christos reader();
921 1.1 christos lr0();
922 1.1 christos lalr();
923 1.1 christos make_parser();
924 1.1 christos graph();
925 1.1 christos finalize_closure();
926 1.1 christos verbose();
927 1.1 christos output();
928 1.1 christos done(exit_code);
929 1.1 christos /*NOTREACHED */
930 1.1 christos }
931