]> git.saurik.com Git - bison.git/blame_incremental - src/output.c
gnulib, autoconf: update.
[bison.git] / src / output.c
... / ...
CommitLineData
1/* Output the generated parsing program for Bison.
2
3 Copyright (C) 1984, 1986, 1989, 1992, 2000-2011 Free Software
4 Foundation, Inc.
5
6 This file is part of Bison, the GNU Compiler Compiler.
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20
21#include <config.h>
22#include "system.h"
23
24#include <configmake.h>
25#include <error.h>
26#include <get-errno.h>
27#include <quotearg.h>
28#include <spawn-pipe.h>
29#include <timevar.h>
30#include <wait-process.h>
31
32#include "complain.h"
33#include "files.h"
34#include "getargs.h"
35#include "gram.h"
36#include "muscle-tab.h"
37#include "output.h"
38#include "reader.h"
39#include "scan-code.h" /* max_left_semantic_context */
40#include "scan-skel.h"
41#include "symtab.h"
42#include "tables.h"
43
44# define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
45
46static struct obstack format_obstack;
47
48
49/*-------------------------------------------------------------------.
50| Create a function NAME which associates to the muscle NAME the |
51| result of formatting the FIRST and then TABLE_DATA[BEGIN..END[ (of |
52| TYPE), and to the muscle NAME_max, the max value of the |
53| TABLE_DATA. |
54`-------------------------------------------------------------------*/
55
56
57#define GENERATE_MUSCLE_INSERT_TABLE(Name, Type) \
58 \
59static void \
60Name (char const *name, \
61 Type *table_data, \
62 Type first, \
63 int begin, \
64 int end) \
65{ \
66 Type min = first; \
67 Type max = first; \
68 long int lmin; \
69 long int lmax; \
70 int i; \
71 int j = 1; \
72 \
73 obstack_fgrow1 (&format_obstack, "%6d", first); \
74 for (i = begin; i < end; ++i) \
75 { \
76 obstack_1grow (&format_obstack, ','); \
77 if (j >= 10) \
78 { \
79 obstack_sgrow (&format_obstack, "\n "); \
80 j = 1; \
81 } \
82 else \
83 ++j; \
84 obstack_fgrow1 (&format_obstack, "%6d", table_data[i]); \
85 if (table_data[i] < min) \
86 min = table_data[i]; \
87 if (max < table_data[i]) \
88 max = table_data[i]; \
89 } \
90 obstack_1grow (&format_obstack, 0); \
91 muscle_insert (name, obstack_finish (&format_obstack)); \
92 \
93 lmin = min; \
94 lmax = max; \
95 /* Build `NAME_min' and `NAME_max' in the obstack. */ \
96 obstack_fgrow1 (&format_obstack, "%s_min", name); \
97 obstack_1grow (&format_obstack, 0); \
98 MUSCLE_INSERT_LONG_INT (obstack_finish (&format_obstack), lmin); \
99 obstack_fgrow1 (&format_obstack, "%s_max", name); \
100 obstack_1grow (&format_obstack, 0); \
101 MUSCLE_INSERT_LONG_INT (obstack_finish (&format_obstack), lmax); \
102}
103
104GENERATE_MUSCLE_INSERT_TABLE(muscle_insert_unsigned_int_table, unsigned int)
105GENERATE_MUSCLE_INSERT_TABLE(muscle_insert_int_table, int)
106GENERATE_MUSCLE_INSERT_TABLE(muscle_insert_base_table, base_number)
107GENERATE_MUSCLE_INSERT_TABLE(muscle_insert_rule_number_table, rule_number)
108GENERATE_MUSCLE_INSERT_TABLE(muscle_insert_symbol_number_table, symbol_number)
109GENERATE_MUSCLE_INSERT_TABLE(muscle_insert_item_number_table, item_number)
110GENERATE_MUSCLE_INSERT_TABLE(muscle_insert_state_number_table, state_number)
111
112
113/*--------------------------------------------------------------------.
114| Print to OUT a representation of STRING escaped both for C and M4. |
115`--------------------------------------------------------------------*/
116
117static void
118escaped_output (FILE *out, char const *string)
119{
120 char const *p;
121 fprintf (out, "[[");
122
123 for (p = quotearg_style (c_quoting_style, string); *p; p++)
124 switch (*p)
125 {
126 case '$': fputs ("$][", out); break;
127 case '@': fputs ("@@", out); break;
128 case '[': fputs ("@{", out); break;
129 case ']': fputs ("@}", out); break;
130 default: fputc (*p, out); break;
131 }
132
133 fprintf (out, "]]");
134}
135
136
137/*------------------------------------------------------------------.
138| Prepare the muscles related to the symbols: translate, tname, and |
139| toknum. |
140`------------------------------------------------------------------*/
141
142static void
143prepare_symbols (void)
144{
145 MUSCLE_INSERT_BOOL ("token_table", token_table_flag);
146 MUSCLE_INSERT_INT ("tokens_number", ntokens);
147 MUSCLE_INSERT_INT ("nterms_number", nvars);
148 MUSCLE_INSERT_INT ("undef_token_number", undeftoken->number);
149 MUSCLE_INSERT_INT ("user_token_number_max", max_user_token_number);
150
151 muscle_insert_symbol_number_table ("translate",
152 token_translations,
153 token_translations[0],
154 1, max_user_token_number + 1);
155
156 /* tname -- token names. */
157 {
158 int i;
159 /* We assume that the table will be output starting at column 2. */
160 int j = 2;
161 struct quoting_options *qo = clone_quoting_options (0);
162 set_quoting_style (qo, c_quoting_style);
163 set_quoting_flags (qo, QA_SPLIT_TRIGRAPHS);
164 for (i = 0; i < nsyms; i++)
165 {
166 char *cp = quotearg_alloc (symbols[i]->tag, -1, qo);
167 /* Width of the next token, including the two quotes, the
168 comma and the space. */
169 int width = strlen (cp) + 2;
170
171 if (j + width > 75)
172 {
173 obstack_sgrow (&format_obstack, "\n ");
174 j = 1;
175 }
176
177 if (i)
178 obstack_1grow (&format_obstack, ' ');
179 MUSCLE_OBSTACK_SGROW (&format_obstack, cp);
180 free (cp);
181 obstack_1grow (&format_obstack, ',');
182 j += width;
183 }
184 free (qo);
185 obstack_sgrow (&format_obstack, " ]b4_null[");
186
187 /* Finish table and store. */
188 obstack_1grow (&format_obstack, 0);
189 muscle_insert ("tname", obstack_finish (&format_obstack));
190 }
191
192 /* Output YYTOKNUM. */
193 {
194 int i;
195 int *values = xnmalloc (ntokens, sizeof *values);
196 for (i = 0; i < ntokens; ++i)
197 values[i] = symbols[i]->user_token_number;
198 muscle_insert_int_table ("toknum", values,
199 values[0], 1, ntokens);
200 free (values);
201 }
202}
203
204
205/*-------------------------------------------------------------.
206| Prepare the muscles related to the rules: rhs, prhs, r1, r2, |
207| rline, dprec, merger. |
208`-------------------------------------------------------------*/
209
210static void
211prepare_rules (void)
212{
213 rule_number r;
214 unsigned int i = 0;
215 item_number *rhs = xnmalloc (nritems, sizeof *rhs);
216 unsigned int *prhs = xnmalloc (nrules, sizeof *prhs);
217 unsigned int *rline = xnmalloc (nrules, sizeof *rline);
218 symbol_number *r1 = xnmalloc (nrules, sizeof *r1);
219 unsigned int *r2 = xnmalloc (nrules, sizeof *r2);
220 int *dprec = xnmalloc (nrules, sizeof *dprec);
221 int *merger = xnmalloc (nrules, sizeof *merger);
222
223 for (r = 0; r < nrules; ++r)
224 {
225 item_number *rhsp = NULL;
226 /* Index of rule R in RHS. */
227 prhs[r] = i;
228 /* RHS of the rule R. */
229 for (rhsp = rules[r].rhs; *rhsp >= 0; ++rhsp)
230 rhs[i++] = *rhsp;
231 /* LHS of the rule R. */
232 r1[r] = rules[r].lhs->number;
233 /* Length of rule R's RHS. */
234 r2[r] = i - prhs[r];
235 /* Separator in RHS. */
236 rhs[i++] = -1;
237 /* Line where rule was defined. */
238 rline[r] = rules[r].location.start.line;
239 /* Dynamic precedence (GLR). */
240 dprec[r] = rules[r].dprec;
241 /* Merger-function index (GLR). */
242 merger[r] = rules[r].merger;
243 }
244 aver (i == nritems);
245
246 muscle_insert_item_number_table ("rhs", rhs, ritem[0], 1, nritems);
247 muscle_insert_unsigned_int_table ("prhs", prhs, 0, 0, nrules);
248 muscle_insert_unsigned_int_table ("rline", rline, 0, 0, nrules);
249 muscle_insert_symbol_number_table ("r1", r1, 0, 0, nrules);
250 muscle_insert_unsigned_int_table ("r2", r2, 0, 0, nrules);
251 muscle_insert_int_table ("dprec", dprec, 0, 0, nrules);
252 muscle_insert_int_table ("merger", merger, 0, 0, nrules);
253
254 MUSCLE_INSERT_INT ("rules_number", nrules);
255 MUSCLE_INSERT_INT ("max_left_semantic_context", max_left_semantic_context);
256
257 free (rhs);
258 free (prhs);
259 free (rline);
260 free (r1);
261 free (r2);
262 free (dprec);
263 free (merger);
264}
265
266/*--------------------------------------------.
267| Prepare the muscles related to the states. |
268`--------------------------------------------*/
269
270static void
271prepare_states (void)
272{
273 state_number i;
274 symbol_number *values = xnmalloc (nstates, sizeof *values);
275 for (i = 0; i < nstates; ++i)
276 values[i] = states[i]->accessing_symbol;
277 muscle_insert_symbol_number_table ("stos", values,
278 0, 1, nstates);
279 free (values);
280
281 MUSCLE_INSERT_INT ("last", high);
282 MUSCLE_INSERT_INT ("final_state_number", final_state->number);
283 MUSCLE_INSERT_INT ("states_number", nstates);
284}
285
286
287
288/*---------------------------------.
289| Output the user actions to OUT. |
290`---------------------------------*/
291
292static void
293user_actions_output (FILE *out)
294{
295 rule_number r;
296
297 fputs ("m4_define([b4_actions], \n[", out);
298 for (r = 0; r < nrules; ++r)
299 if (rules[r].action)
300 {
301 fprintf (out, "b4_case(%d, [b4_syncline(%d, ", r + 1,
302 rules[r].action_location.start.line);
303 escaped_output (out, rules[r].action_location.start.file);
304 fprintf (out, ")\n[ %s]])\n\n", rules[r].action);
305 }
306 fputs ("])\n\n", out);
307}
308
309/*--------------------------------------.
310| Output the merge functions to OUT. |
311`--------------------------------------*/
312
313static void
314merger_output (FILE *out)
315{
316 int n;
317 merger_list* p;
318
319 fputs ("m4_define([b4_mergers], \n[[", out);
320 for (n = 1, p = merge_functions; p != NULL; n += 1, p = p->next)
321 {
322 if (p->type[0] == '\0')
323 fprintf (out, " case %d: *yy0 = %s (*yy0, *yy1); break;\n",
324 n, p->name);
325 else
326 fprintf (out, " case %d: yy0->%s = %s (*yy0, *yy1); break;\n",
327 n, p->type, p->name);
328 }
329 fputs ("]])\n\n", out);
330}
331
332/*--------------------------------------.
333| Output the tokens definition to OUT. |
334`--------------------------------------*/
335
336static void
337token_definitions_output (FILE *out)
338{
339 int i;
340 char const *sep = "";
341
342 fputs ("m4_define([b4_tokens], \n[", out);
343 for (i = 0; i < ntokens; ++i)
344 {
345 symbol *sym = symbols[i];
346 int number = sym->user_token_number;
347
348 /* At this stage, if there are literal string aliases, they are
349 part of SYMBOLS, so we should not find their aliased symbols
350 here. */
351 aver (number != USER_NUMBER_HAS_STRING_ALIAS);
352
353 /* Skip error token. */
354 if (sym == errtoken)
355 continue;
356
357 /* If this string has an alias, then it is necessarily the alias
358 which is to be output. */
359 if (sym->alias)
360 sym = sym->alias;
361
362 /* Don't output literal chars or strings (when defined only as a
363 string). Note that must be done after the alias resolution:
364 think about `%token 'f' "f"'. */
365 if (sym->tag[0] == '\'' || sym->tag[0] == '\"')
366 continue;
367
368 /* Don't #define nonliteral tokens whose names contain periods,
369 dashes or '$' (as does the default value of the EOF token). */
370 if (strchr (sym->tag, '.')
371 || strchr (sym->tag, '-')
372 || strchr (sym->tag, '$'))
373 continue;
374
375 fprintf (out, "%s[[[%s]], %d]",
376 sep, sym->tag, number);
377 sep = ",\n";
378 }
379 fputs ("])\n\n", out);
380}
381
382
383/*---------------------------------------------------.
384| Output the symbol destructors or printers to OUT. |
385`---------------------------------------------------*/
386
387static void
388symbol_code_props_output (FILE *out, char const *what,
389 code_props const *(*get)(symbol const *))
390{
391 int i;
392 char const *sep = "";
393
394 fputs ("m4_define([b4_symbol_", out);
395 fputs (what, out);
396 fputs ("], \n[", out);
397 for (i = 0; i < nsyms; ++i)
398 {
399 symbol *sym = symbols[i];
400 char const *code = (*get) (sym)->code;
401 if (code)
402 {
403 location loc = (*get) (sym)->location;
404 /* Filename, lineno,
405 Symbol-name, Symbol-number,
406 code, optional typename. */
407 fprintf (out, "%s[", sep);
408 sep = ",\n";
409 escaped_output (out, loc.start.file);
410 fprintf (out, ", %d, ", loc.start.line);
411 escaped_output (out, sym->tag);
412 fprintf (out, ", %d, [[%s]]", sym->number, code);
413 if (sym->type_name)
414 fprintf (out, ", [[%s]]", sym->type_name);
415 fputc (']', out);
416 }
417 }
418 fputs ("])\n\n", out);
419}
420
421
422static void
423prepare_actions (void)
424{
425 /* Figure out the actions for the specified state, indexed by
426 lookahead token type. */
427
428 muscle_insert_rule_number_table ("defact", yydefact,
429 yydefact[0], 1, nstates);
430
431 /* Figure out what to do after reducing with each rule, depending on
432 the saved state from before the beginning of parsing the data
433 that matched this rule. */
434 muscle_insert_state_number_table ("defgoto", yydefgoto,
435 yydefgoto[0], 1, nsyms - ntokens);
436
437
438 /* Output PACT. */
439 muscle_insert_base_table ("pact", base,
440 base[0], 1, nstates);
441 MUSCLE_INSERT_INT ("pact_ninf", base_ninf);
442
443 /* Output PGOTO. */
444 muscle_insert_base_table ("pgoto", base,
445 base[nstates], nstates + 1, nvectors);
446
447 muscle_insert_base_table ("table", table,
448 table[0], 1, high + 1);
449 MUSCLE_INSERT_INT ("table_ninf", table_ninf);
450
451 muscle_insert_base_table ("check", check,
452 check[0], 1, high + 1);
453
454 /* GLR parsing slightly modifies YYTABLE and YYCHECK (and thus
455 YYPACT) so that in states with unresolved conflicts, the default
456 reduction is not used in the conflicted entries, so that there is
457 a place to put a conflict pointer.
458
459 This means that YYCONFLP and YYCONFL are nonsense for a non-GLR
460 parser, so we could avoid accidents by not writing them out in
461 that case. Nevertheless, it seems even better to be able to use
462 the GLR skeletons even without the non-deterministic tables. */
463 muscle_insert_unsigned_int_table ("conflict_list_heads", conflict_table,
464 conflict_table[0], 1, high + 1);
465 muscle_insert_unsigned_int_table ("conflicting_rules", conflict_list,
466 0, 1, conflict_list_cnt);
467}
468
469/*--------------------------------------------.
470| Output the definitions of all the muscles. |
471`--------------------------------------------*/
472
473static void
474muscles_output (FILE *out)
475{
476 fputs ("m4_init()\n", out);
477
478 user_actions_output (out);
479 merger_output (out);
480 token_definitions_output (out);
481 symbol_code_props_output (out, "destructors", &symbol_destructor_get);
482 symbol_code_props_output (out, "printers", &symbol_printer_get);
483
484 muscles_m4_output (out);
485}
486\f
487/*---------------------------.
488| Call the skeleton parser. |
489`---------------------------*/
490
491static void
492output_skeleton (void)
493{
494 FILE *in;
495 int filter_fd[2];
496 char const *argv[10];
497 pid_t pid;
498
499 /* Compute the names of the package data dir and skeleton files. */
500 char const m4sugar[] = "m4sugar/m4sugar.m4";
501 char const m4bison[] = "bison.m4";
502 char *full_m4sugar;
503 char *full_m4bison;
504 char *full_skeleton;
505 char const *p;
506 char const *m4 = (p = getenv ("M4")) ? p : M4;
507 char const *pkgdatadir = compute_pkgdatadir ();
508 size_t skeleton_size = strlen (skeleton) + 1;
509 size_t pkgdatadirlen = strlen (pkgdatadir);
510 while (pkgdatadirlen && pkgdatadir[pkgdatadirlen - 1] == '/')
511 pkgdatadirlen--;
512 full_skeleton = xmalloc (pkgdatadirlen + 1
513 + (skeleton_size < sizeof m4sugar
514 ? sizeof m4sugar : skeleton_size));
515 strncpy (full_skeleton, pkgdatadir, pkgdatadirlen);
516 full_skeleton[pkgdatadirlen] = '/';
517 strcpy (full_skeleton + pkgdatadirlen + 1, m4sugar);
518 full_m4sugar = xstrdup (full_skeleton);
519 strcpy (full_skeleton + pkgdatadirlen + 1, m4bison);
520 full_m4bison = xstrdup (full_skeleton);
521 if (strchr (skeleton, '/'))
522 strcpy (full_skeleton, skeleton);
523 else
524 strcpy (full_skeleton + pkgdatadirlen + 1, skeleton);
525
526 /* Test whether m4sugar.m4 is readable, to check for proper
527 installation. A faulty installation can cause deadlock, so a
528 cheap sanity check is worthwhile. */
529 xfclose (xfopen (full_m4sugar, "r"));
530
531 /* Create an m4 subprocess connected to us via two pipes. */
532
533 if (trace_flag & trace_tools)
534 fprintf (stderr, "running: %s %s - %s %s\n",
535 m4, full_m4sugar, full_m4bison, full_skeleton);
536
537 /* Some future version of GNU M4 (most likely 1.6) may treat the -dV in a
538 position-dependent manner. Keep it as the first argument so that all
539 files are traced.
540
541 See the thread starting at
542 <http://lists.gnu.org/archive/html/bug-bison/2008-07/msg00000.html>
543 for details. */
544 {
545 int i = 0;
546 argv[i++] = m4;
547
548 /* When POSIXLY_CORRECT is set, GNU M4 1.6 and later disable GNU
549 extensions, which Bison's skeletons depend on. With older M4,
550 it has no effect. M4 1.4.12 added a -g/--gnu command-line
551 option to make it explicit that a program wants GNU M4
552 extensions even when POSIXLY_CORRECT is set.
553
554 See the thread starting at
555 <http://lists.gnu.org/archive/html/bug-bison/2008-07/msg00000.html>
556 for details. */
557 if (*M4_GNU_OPTION)
558 argv[i++] = M4_GNU_OPTION;
559
560 argv[i++] = "-I";
561 argv[i++] = pkgdatadir;
562 if (trace_flag & trace_m4)
563 argv[i++] = "-dV";
564 argv[i++] = full_m4sugar;
565 argv[i++] = "-";
566 argv[i++] = full_m4bison;
567 argv[i++] = full_skeleton;
568 argv[i++] = NULL;
569 aver (i <= ARRAY_CARDINALITY (argv));
570 }
571
572 /* The ugly cast is because gnulib gets the const-ness wrong. */
573 pid = create_pipe_bidi ("m4", m4, (char **)(void*)argv, false, true,
574 true, filter_fd);
575 free (full_m4sugar);
576 free (full_m4bison);
577 free (full_skeleton);
578
579 if (trace_flag & trace_muscles)
580 muscles_output (stderr);
581 {
582 FILE *out = fdopen (filter_fd[1], "w");
583 if (! out)
584 error (EXIT_FAILURE, get_errno (),
585 "fdopen");
586 muscles_output (out);
587 xfclose (out);
588 }
589
590 /* Read and process m4's output. */
591 timevar_push (TV_M4);
592 in = fdopen (filter_fd[0], "r");
593 if (! in)
594 error (EXIT_FAILURE, get_errno (),
595 "fdopen");
596 scan_skel (in);
597 /* scan_skel should have read all of M4's output. Otherwise, when we
598 close the pipe, we risk letting M4 report a broken-pipe to the
599 Bison user. */
600 aver (feof (in));
601 xfclose (in);
602 wait_subprocess (pid, "m4", false, false, true, true, NULL);
603 timevar_pop (TV_M4);
604}
605
606static void
607prepare (void)
608{
609 /* BISON_USE_PUSH_FOR_PULL is for the test suite and should not be documented
610 for the user. */
611 char const *use_push_for_pull_env = getenv ("BISON_USE_PUSH_FOR_PULL");
612 bool use_push_for_pull_flag = false;
613 if (use_push_for_pull_env != NULL
614 && use_push_for_pull_env[0] != '\0'
615 && 0 != strcmp (use_push_for_pull_env, "0"))
616 use_push_for_pull_flag = true;
617
618 /* Flags. */
619 MUSCLE_INSERT_BOOL ("debug_flag", debug_flag);
620 MUSCLE_INSERT_BOOL ("defines_flag", defines_flag);
621 MUSCLE_INSERT_BOOL ("error_verbose_flag", error_verbose);
622 MUSCLE_INSERT_BOOL ("glr_flag", glr_parser);
623 MUSCLE_INSERT_BOOL ("locations_flag", locations_flag);
624 MUSCLE_INSERT_BOOL ("nondeterministic_flag", nondeterministic_parser);
625 MUSCLE_INSERT_BOOL ("synclines_flag", !no_lines_flag);
626 MUSCLE_INSERT_BOOL ("tag_seen_flag", tag_seen);
627 MUSCLE_INSERT_BOOL ("use_push_for_pull_flag", use_push_for_pull_flag);
628 MUSCLE_INSERT_BOOL ("yacc_flag", yacc_flag);
629
630 /* File names. */
631 if (spec_name_prefix)
632 MUSCLE_INSERT_STRING ("prefix", spec_name_prefix);
633
634 MUSCLE_INSERT_STRING ("file_name_all_but_ext", all_but_ext);
635
636#define DEFINE(Name) MUSCLE_INSERT_STRING (#Name, Name ? Name : "")
637 DEFINE (dir_prefix);
638 DEFINE (parser_file_name);
639 DEFINE (spec_defines_file);
640 DEFINE (spec_file_prefix);
641 DEFINE (spec_graph_file);
642 DEFINE (spec_name_prefix);
643 DEFINE (spec_outfile);
644 DEFINE (spec_verbose_file);
645#undef DEFINE
646
647 /* Find the right skeleton file, and add muscles about the skeletons. */
648 if (skeleton)
649 MUSCLE_INSERT_C_STRING ("skeleton", skeleton);
650 else
651 skeleton = language->skeleton;
652
653 /* About the skeletons. */
654 {
655 /* b4_pkgdatadir is used inside m4_include in the skeletons, so digraphs
656 would never be expanded. Hopefully no one has M4-special characters in
657 his Bison installation path. */
658 MUSCLE_INSERT_STRING_RAW ("pkgdatadir", compute_pkgdatadir ());
659 }
660}
661
662
663/*----------------------------------------------------------.
664| Output the parsing tables and the parser code to ftable. |
665`----------------------------------------------------------*/
666
667void
668output (void)
669{
670 obstack_init (&format_obstack);
671
672 prepare_symbols ();
673 prepare_rules ();
674 prepare_states ();
675 prepare_actions ();
676
677 prepare ();
678
679 /* Process the selected skeleton file. */
680 output_skeleton ();
681
682 obstack_free (&format_obstack, NULL);
683}
684
685char const *
686compute_pkgdatadir (void)
687{
688 char const *pkgdatadir = getenv ("BISON_PKGDATADIR");
689 return pkgdatadir ? pkgdatadir : PKGDATADIR;
690}