1 /* Muscle table manager for Bison.
2
3 Copyright (C) 2001-2015, 2018-2021 Free Software Foundation, Inc.
4
5 This file is part of Bison, the GNU Compiler Compiler.
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <https://www.gnu.org/licenses/>. */
19
20 #include <config.h>
21 #include "system.h"
22
23 #include <hash.h>
24 #include <quote.h>
25
26 #include "complain.h"
27 #include "files.h"
28 #include "fixits.h"
29 #include "getargs.h"
30 #include "muscle-tab.h"
31
32 muscle_kind
33 muscle_kind_new (char const *k)
34 {
35 if (STREQ (k, "code"))
36 return muscle_code;
37 else if (STREQ (k, "keyword"))
38 return muscle_keyword;
39 else if (STREQ (k, "string"))
40 return muscle_string;
41 abort ();
42 }
43
44 char const *
45 muscle_kind_string (muscle_kind k)
46 {
47 switch (k)
48 {
49 case muscle_code: return "code";
50 case muscle_keyword: return "keyword";
51 case muscle_string: return "string";
52 }
53 abort ();
54 }
55
56
57 /* A key-value pair, along with storage that can be reclaimed when
58 this pair is no longer needed. */
59 typedef struct
60 {
61 char const *key;
62 char const *value;
63 char *storage;
64 muscle_kind kind;
65 } muscle_entry;
66
67
68 /* The name of muscle for the %define variable VAR (corresponding to
69 FIELD, if defined). */
70 static uniqstr
71 muscle_name (char const *var, char const *field)
72 {
73 if (field)
74 return UNIQSTR_CONCAT ("percent_define_", field, "(", var, ")");
75 else
76 return UNIQSTR_CONCAT ("percent_define(", var, ")");
77 }
78
79 /* An obstack used to create some entries. */
80 struct obstack muscle_obstack;
81
82 /* Initial capacity of muscles hash table. */
83 #define HT_INITIAL_CAPACITY 257
84
85 static struct hash_table *muscle_table = NULL;
86
87 static bool
88 hash_compare_muscles (void const *x, void const *y)
89 {
90 muscle_entry const *m1 = x;
91 muscle_entry const *m2 = y;
92 return STREQ (m1->key, m2->key);
93 }
94
95 static size_t
96 hash_muscle (const void *x, size_t tablesize)
97 {
98 muscle_entry const *m = x;
99 return hash_string (m->key, tablesize);
100 }
101
102 /* Create a fresh muscle name KEY, and insert in the hash table. */
103 static void *
104 muscle_entry_new (char const *key)
105 {
106 muscle_entry *res = xmalloc (sizeof *res);
107 res->key = key;
108 res->value = NULL;
109 res->storage = NULL;
110 hash_xinsert (muscle_table, res);
111 return res;
112 }
113
114 static void
115 muscle_entry_free (void *entry)
116 {
117 muscle_entry *mentry = entry;
118 free (mentry->storage);
119 free (mentry);
120 }
121
122 void
123 muscle_init (void)
124 {
125 /* Initialize the muscle obstack. */
126 obstack_init (&muscle_obstack);
127
128 muscle_table = hash_xinitialize (HT_INITIAL_CAPACITY, NULL, hash_muscle,
129 hash_compare_muscles, muscle_entry_free);
130 }
131
132
133 void
134 muscle_free (void)
135 {
136 hash_free (muscle_table);
137 obstack_free (&muscle_obstack, NULL);
138 }
139
140 /* Look for the muscle named KEY. Return NULL if does not exist. */
141 static muscle_entry *
142 muscle_lookup (char const *key)
143 {
144 muscle_entry probe;
145 probe.key = key;
146 return hash_lookup (muscle_table, &probe);
147 }
148
149
150 void
151 muscle_insert (char const *key, char const *value)
152 {
153 muscle_entry *entry = muscle_lookup (key);
154 if (entry)
155 free (entry->storage);
156 else
157 /* First insertion in the hash. */
158 entry = muscle_entry_new (key);
159 entry->value = value;
160 entry->storage = NULL;
161 }
162
163
164 /* Append VALUE to the current value of KEY. If KEY did not already
165 exist, create it. Use MUSCLE_OBSTACK. De-allocate the previously
166 associated value. Copy VALUE and SEPARATOR. If VALUE does not end
167 with TERMINATOR, append one. */
168
169 static void
170 muscle_grow (const char *key, const char *val,
171 const char *separator, const char *terminator)
172 {
173 muscle_entry *entry = muscle_lookup (key);
174 if (entry)
175 {
176 obstack_sgrow (&muscle_obstack, entry->value);
177 obstack_sgrow (&muscle_obstack, separator);
178 free (entry->storage);
179 }
180 else
181 entry = muscle_entry_new (key);
182
183 obstack_sgrow (&muscle_obstack, val);
184
185 size_t vals = strlen (val);
186 size_t terms = strlen (terminator);
187 if (terms <= vals
188 && STRNEQ (val + vals - terms, terminator))
189 obstack_sgrow (&muscle_obstack, terminator);
190
191 {
192 char const *new_val = obstack_finish0 (&muscle_obstack);
193 entry->value = entry->storage = xstrdup (new_val);
194 obstack_free (&muscle_obstack, new_val);
195 }
196 }
197
198 /*------------------------------------------------------------------.
199 | Using muscle_grow, append a synchronization line for the location |
200 | LOC to the current value of KEY. |
201 `------------------------------------------------------------------*/
202
203 static void
204 muscle_syncline_grow (char const *key, location loc)
205 {
206 obstack_printf (&muscle_obstack, "]b4_syncline(%d, ", loc.start.line);
207 obstack_quote (&muscle_obstack,
208 quotearg_style (c_quoting_style, map_file_name (loc.start.file)));
209 obstack_sgrow (&muscle_obstack, ")dnl\n[");
210 char const *extension = obstack_finish0 (&muscle_obstack);
211 muscle_grow (key, extension, "", "");
212 obstack_free (&muscle_obstack, extension);
213 }
214
215 /*------------------------------------------------------------------.
216 | Append VALUE to the current value of KEY, using muscle_grow. But |
217 | in addition, issue a synchronization line for the location LOC |
218 | using muscle_syncline_grow. |
219 `------------------------------------------------------------------*/
220
221 void
222 muscle_code_grow (const char *key, const char *val, location loc)
223 {
224 muscle_syncline_grow (key, loc);
225 muscle_grow (key, val, "", "\n");
226 }
227
228
229 void
230 muscle_pair_list_grow (const char *muscle,
231 const char *a1, const char *a2)
232 {
233 obstack_sgrow (&muscle_obstack, "[");
234 obstack_quote (&muscle_obstack, a1);
235 obstack_sgrow (&muscle_obstack, ", ");
236 obstack_quote (&muscle_obstack, a2);
237 obstack_sgrow (&muscle_obstack, "]");
238 char const *pair = obstack_finish0 (&muscle_obstack);
239 muscle_grow (muscle, pair, ",\n", "");
240 obstack_free (&muscle_obstack, pair);
241 }
242
243
244 char const *
245 muscle_find_const (char const *key)
246 {
247 muscle_entry *entry = muscle_lookup (key);
248 return entry ? entry->value : NULL;
249 }
250
251
252 char *
253 muscle_find (char const *key)
254 {
255 muscle_entry *entry = muscle_lookup (key);
256 if (entry)
257 {
258 aver (entry->value == entry->storage);
259 return entry->storage;
260 }
261 return NULL;
262 }
263
264
265 /* In the format 'file_name:line.column', append BOUND to MUSCLE. Use
266 digraphs for special characters in the file name. */
267
268 static void
269 muscle_boundary_grow (char const *key, boundary bound)
270 {
271 obstack_sgrow (&muscle_obstack, "[[");
272 obstack_escape (&muscle_obstack, bound.file);
273 obstack_printf (&muscle_obstack, ":%d.%d@@%d]]", bound.line, bound.column, bound.byte);
274 char const *extension = obstack_finish0 (&muscle_obstack);
275 muscle_grow (key, extension, "", "");
276 obstack_free (&muscle_obstack, extension);
277 }
278
279
280 void
281 muscle_location_grow (char const *key, location loc)
282 {
283 muscle_boundary_grow (key, loc.start);
284 muscle_grow (key, "", ", ", "");
285 muscle_boundary_grow (key, loc.end);
286 }
287
288 #define COMMON_DECODE(Value) \
289 case '$': \
290 ++(Value); aver (*(Value) == ']'); \
291 ++(Value); aver (*(Value) == '['); \
292 obstack_sgrow (&muscle_obstack, "$"); \
293 break; \
294 case '@': \
295 switch (*++(Value)) \
296 { \
297 case '@': obstack_sgrow (&muscle_obstack, "@" ); break; \
298 case '{': obstack_sgrow (&muscle_obstack, "[" ); break; \
299 case '}': obstack_sgrow (&muscle_obstack, "]" ); break; \
300 default: aver (false); break; \
301 } \
302 break; \
303 default: \
304 obstack_1grow (&muscle_obstack, *(Value)); \
305 break;
306
307 /* Reverse of obstack_escape. */
308 static char *
309 string_decode (char const *key)
310 {
311 char const *value = muscle_find_const (key);
312 if (!value)
313 return NULL;
314 do {
315 switch (*value)
316 {
317 COMMON_DECODE (value)
318 case '[':
319 case ']':
320 aver (false);
321 break;
322 }
323 } while (*value++);
324 char const *value_decoded = obstack_finish (&muscle_obstack);
325 char *res = xstrdup (value_decoded);
326 obstack_free (&muscle_obstack, value_decoded);
327 return res;
328 }
329
330 /* Reverse of muscle_location_grow. */
331 static location
332 location_decode (char const *value)
333 {
334 aver (value);
335 aver (*value == '[');
336 ++value; aver (*value == '[');
337 location loc;
338 while (*++value)
339 switch (*value)
340 {
341 COMMON_DECODE (value)
342 case '[':
343 aver (false);
344 break;
345 case ']':
346 ++value; aver (*value == ']');
347 char *boundary_str = obstack_finish0 (&muscle_obstack);
348 switch (*++value)
349 {
350 case ',':
351 boundary_set_from_string (&loc.start, boundary_str);
352 obstack_free (&muscle_obstack, boundary_str);
353 ++value; aver (*value == ' ');
354 ++value; aver (*value == '[');
355 ++value; aver (*value == '[');
356 break;
357 case '\0':
358 boundary_set_from_string (&loc.end, boundary_str);
359 obstack_free (&muscle_obstack, boundary_str);
360 return loc;
361 break;
362 default:
363 aver (false);
364 break;
365 }
366 break;
367 }
368 aver (false);
369 return loc;
370 }
371
372 void
373 muscle_user_name_list_grow (char const *key, char const *user_name,
374 location loc)
375 {
376 muscle_grow (key, "[[[[", ",", "");
377 muscle_grow (key, user_name, "", "");
378 muscle_grow (key, "]], ", "", "");
379 muscle_location_grow (key, loc);
380 muscle_grow (key, "]]", "", "");
381 }
382
383
384 /** Return an allocated string that represents the %define directive
385 that performs the assignment.
386
387 @param assignment "VAR", or "VAR=VAL".
388 @param value default value if VAL \a assignment has no '='.
389
390 For instance:
391 "foo", NULL => "%define foo"
392 "foo", "baz" => "%define foo baz"
393 "foo=bar", NULL => "%define foo bar"
394 "foo=bar", "baz" => "%define foo bar"
395 "foo=", NULL => "%define foo"
396 "foo=", "baz" => "%define foo"
397 */
398
399 static
400 char *
401 define_directive (char const *assignment,
402 muscle_kind kind,
403 char const *value)
404 {
405 char *eq = strchr (assignment, '=');
406 char const *fmt
407 = eq || !value || !*value ? "%%define %s"
408 : kind == muscle_code ? "%%define %s {%s}"
409 : kind == muscle_string ? "%%define %s \"%s\""
410 : "%%define %s %s";
411 char *res = xmalloc (strlen (fmt) + strlen (assignment)
412 + (value ? strlen (value) : 0));
413 sprintf (res, fmt, assignment, value);
414 eq = strchr (res, '=');
415 if (eq)
416 *eq = eq[1] ? ' ' : '\0';
417 return res;
418 }
419
420 /** If the \a variable name is obsolete, return the name to use,
421 * otherwise \a variable. If the \a value is obsolete, update it too.
422 *
423 * Allocates the returned value if needed, otherwise the returned
424 * value is exactly \a variable. */
425 static
426 char const *
427 muscle_percent_variable_update (char const *variable,
428 muscle_kind kind,
429 char const **value,
430 char **old, char **upd)
431 {
432 typedef struct
433 {
434 const char *obsolete;
435 const char *updated;
436 muscle_kind kind;
437 } conversion_type;
438 const conversion_type conversion[] =
439 {
440 { "%error-verbose", "parse.error=verbose", muscle_keyword },
441 { "%error_verbose", "parse.error=verbose", muscle_keyword },
442 { "abstract", "api.parser.abstract", muscle_keyword },
443 { "annotations", "api.parser.annotations", muscle_code },
444 { "api.push_pull", "api.push-pull", muscle_keyword },
445 { "api.tokens.prefix", "api.token.prefix", muscle_code },
446 { "extends", "api.parser.extends", muscle_keyword },
447 { "filename_type", "api.filename.type", muscle_code },
448 { "final", "api.parser.final", muscle_keyword },
449 { "implements", "api.parser.implements", muscle_keyword },
450 { "lex_symbol", "api.token.constructor", -1 },
451 { "location_type", "api.location.type", muscle_code },
452 { "lr.default-reductions", "lr.default-reduction", muscle_keyword },
453 { "lr.keep-unreachable-states", "lr.keep-unreachable-state", muscle_keyword },
454 { "lr.keep_unreachable_states", "lr.keep-unreachable-state", muscle_keyword },
455 { "namespace", "api.namespace", muscle_code },
456 { "package", "api.package", muscle_code },
457 { "parser_class_name", "api.parser.class", muscle_code },
458 { "public", "api.parser.public", muscle_keyword },
459 { "strictfp", "api.parser.strictfp", muscle_keyword },
460 { "stype", "api.value.type", -1 },
461 { "variant=", "api.value.type=variant", -1 },
462 { "variant=true", "api.value.type=variant", -1 },
463 { NULL, NULL, -1, }
464 };
465
466 for (conversion_type const *c = conversion; c->obsolete; ++c)
467 {
468 char const *eq = strchr (c->obsolete, '=');
469 if (eq
470 ? (!strncmp (c->obsolete, variable, eq - c->obsolete)
471 && STREQ (eq + 1, *value))
472 : STREQ (c->obsolete, variable))
473 {
474 /* Generate the deprecation warning. */
475 *old = c->obsolete[0] == '%'
476 ? xstrdup (c->obsolete)
477 : define_directive (c->obsolete, kind, *value);
478 *upd = define_directive (c->updated, c->kind, *value);
479 /* Update the variable and its value. */
480 {
481 char *res = xstrdup (c->updated);
482 char *eq2 = strchr (res, '=');
483 if (eq2)
484 {
485 *eq2 = '\0';
486 *value = eq2 + 1;
487 }
488 return res;
489 }
490 }
491 }
492 return variable;
493 }
494
495 void
496 muscle_percent_define_insert (char const *var, location variable_loc,
497 muscle_kind kind,
498 char const *value,
499 muscle_percent_define_how how)
500 {
501 /* Backward compatibility. */
502 char *old = NULL;
503 char *upd = NULL;
504 char const *variable
505 = muscle_percent_variable_update (var, kind,
506 &value, &old, &upd);
507 uniqstr name = muscle_name (variable, NULL);
508 uniqstr loc_name = muscle_name (variable, "loc");
509 uniqstr syncline_name = muscle_name (variable, "syncline");
510 uniqstr how_name = muscle_name (variable, "how");
511 uniqstr kind_name = muscle_name (variable, "kind");
512
513 /* Command-line options are processed before the grammar file. */
514 bool warned = false;
515 if (how == MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE)
516 {
517 char const *current_value = muscle_find_const (name);
518 if (current_value)
519 {
520 long l = strtol (muscle_find_const (how_name), NULL, 10);
521 muscle_percent_define_how how_old
522 = 0 <= l && l <= INT_MAX ? l : INT_MAX;
523 if (how_old == MUSCLE_PERCENT_DEFINE_F)
524 goto end;
525 /* If assigning the same value, make it a warning. */
526 warnings warn = STREQ (value, current_value) ? Wother : complaint;
527 complain (&variable_loc, warn,
528 _("%%define variable %s redefined"),
529 quote (variable));
530 location loc = muscle_percent_define_get_loc (variable);
531 subcomplain (&loc, warn, _("previous definition"));
532 fixits_register (&variable_loc, "");
533 warned = true;
534 }
535 }
536
537 if (!warned && old && upd)
538 deprecated_directive (&variable_loc, old, upd);
539
540 MUSCLE_INSERT_STRING (name, value);
541 muscle_insert (loc_name, "");
542 muscle_location_grow (loc_name, variable_loc);
543 muscle_insert (syncline_name, "");
544 muscle_syncline_grow (syncline_name, variable_loc);
545 muscle_user_name_list_grow ("percent_define_user_variables", variable,
546 variable_loc);
547 MUSCLE_INSERT_INT (how_name, how);
548 MUSCLE_INSERT_STRING (kind_name, muscle_kind_string (kind));
549 end:
550 free (old);
551 free (upd);
552 if (variable != var)
553 free ((char *) variable);
554 }
555
556 /* This is used for backward compatibility, e.g., "%define api.pure"
557 supersedes "%pure-parser". */
558 void
559 muscle_percent_define_ensure (char const *variable, location loc,
560 bool value)
561 {
562 uniqstr name = muscle_name (variable, NULL);
563 char const *val = value ? "" : "false";
564
565 /* Don't complain is VARIABLE is already defined, but be sure to set
566 its value to VAL. */
567 if (!muscle_find_const (name)
568 || muscle_percent_define_flag_if (variable) != value)
569 muscle_percent_define_insert (variable, loc, muscle_keyword, val,
570 MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
571 }
572
573 /* Mark %define VARIABLE as used. */
574 static void
575 muscle_percent_define_use (char const *variable)
576 {
577 muscle_insert (muscle_name (variable, "bison_variables"), "");
578 }
579
580 /* The value of %define variable VARIABLE (corresponding to FIELD, if
581 defined). Do not register as used, but diagnose unset variables. */
582
583 static
584 char const *
585 muscle_percent_define_get_raw (char const *variable, char const *field)
586 {
587 uniqstr name = muscle_name (variable, field);
588 char const *res = muscle_find_const (name);
589 if (!res)
590 complain (NULL, fatal, _("%s: undefined %%define variable %s"),
591 "muscle_percent_define_get_raw", quote (variable));
592 return res;
593 }
594
595 char *
596 muscle_percent_define_get (char const *variable)
597 {
598 uniqstr name = muscle_name (variable, NULL);
599 char *value = string_decode (name);
600 if (!value)
601 value = xstrdup ("");
602 muscle_percent_define_use (variable);
603 return value;
604 }
605
606 /* The kind of VARIABLE. An error if undefined. */
607 static muscle_kind
608 muscle_percent_define_get_kind (char const *variable)
609 {
610 return muscle_kind_new (muscle_percent_define_get_raw (variable, "kind"));
611 }
612
613 /* Check the kind of VARIABLE. An error if undefined. */
614 static void
615 muscle_percent_define_check_kind (char const *variable, muscle_kind kind)
616 {
617 if (muscle_percent_define_get_kind (variable) != kind)
618 {
619 location loc = muscle_percent_define_get_loc (variable);
620 switch (kind)
621 {
622 case muscle_code:
623 complain (&loc, Wdeprecated,
624 _("%%define variable '%s' requires '{...}' values"),
625 variable);
626 break;
627 case muscle_keyword:
628 complain (&loc, Wdeprecated,
629 _("%%define variable '%s' requires keyword values"),
630 variable);
631 break;
632 case muscle_string:
633 complain (&loc, Wdeprecated,
634 _("%%define variable '%s' requires '\"...\"' values"),
635 variable);
636 break;
637 }
638 }
639 }
640
641
642 location
643 muscle_percent_define_get_loc (char const *variable)
644 {
645 return location_decode (muscle_percent_define_get_raw (variable, "loc"));
646 }
647
648 char const *
649 muscle_percent_define_get_syncline (char const *variable)
650 {
651 return muscle_percent_define_get_raw (variable, "syncline");
652 }
653
654 bool
655 muscle_percent_define_ifdef (char const *variable)
656 {
657 if (muscle_find_const (muscle_name (variable, NULL)))
658 {
659 muscle_percent_define_use (variable);
660 return true;
661 }
662 else
663 return false;
664 }
665
666 bool
667 muscle_percent_define_flag_if (char const *variable)
668 {
669 uniqstr invalid_boolean_name = muscle_name (variable, "invalid_boolean");
670 bool res = false;
671
672 if (muscle_percent_define_ifdef (variable))
673 {
674 char *value = muscle_percent_define_get (variable);
675 muscle_percent_define_check_kind (variable, muscle_keyword);
676 if (value[0] == '\0' || STREQ (value, "true"))
677 res = true;
678 else if (STREQ (value, "false"))
679 res = false;
680 else if (!muscle_find_const (invalid_boolean_name))
681 {
682 muscle_insert (invalid_boolean_name, "");
683 location loc = muscle_percent_define_get_loc (variable);
684 complain (&loc, complaint,
685 _("invalid value for %%define Boolean variable %s"),
686 quote (variable));
687 }
688 free (value);
689 }
690 else
691 complain (NULL, fatal, _("%s: undefined %%define variable %s"),
692 "muscle_percent_define_flag", quote (variable));
693
694 return res;
695 }
696
697 void
698 muscle_percent_define_default (char const *variable, char const *value)
699 {
700 uniqstr name = muscle_name (variable, NULL);
701 if (!muscle_find_const (name))
702 {
703 MUSCLE_INSERT_STRING (name, value);
704 MUSCLE_INSERT_STRING (muscle_name (variable, "kind"), "keyword");
705 {
706 uniqstr loc_name = muscle_name (variable, "loc");
707 location loc;
708 loc.start.file = "<default value>";
709 loc.start.line = -1;
710 loc.start.column = -1;
711 loc.start.byte = -1;
712 loc.end = loc.start;
713 muscle_insert (loc_name, "");
714 muscle_location_grow (loc_name, loc);
715 }
716 muscle_insert (muscle_name (variable, "syncline"), "");
717 }
718 }
719
720 void
721 muscle_percent_define_check_values (char const * const *values)
722 {
723 for (; *values; ++values)
724 {
725 char const * const *variablep = values;
726 uniqstr name = muscle_name (*variablep, NULL);
727 char *value = string_decode (name);
728 muscle_percent_define_check_kind (*variablep, muscle_keyword);
729 if (value)
730 {
731 for (++values; *values; ++values)
732 if (STREQ (value, *values))
733 break;
734 if (!*values)
735 {
736 location loc = muscle_percent_define_get_loc (*variablep);
737 complain (&loc, complaint,
738 _("invalid value for %%define variable %s: %s"),
739 quote (*variablep), quote_n (1, value));
740 for (values = variablep + 1; *values; ++values)
741 subcomplain (&loc, complaint | no_caret | silent,
742 _("accepted value: %s"), quote (*values));
743 }
744 else
745 while (*values)
746 ++values;
747 free (value);
748 }
749 else
750 complain (NULL, fatal, _("%s: undefined %%define variable %s"),
751 "muscle_percent_define_check_values", quote (*variablep));
752 }
753 }
754
755 void
756 muscle_percent_code_grow (char const *qualifier, location qualifier_loc,
757 char const *code, location code_loc)
758 {
759 char const *name = UNIQSTR_CONCAT ("percent_code(", qualifier, ")");
760 muscle_code_grow (name, code, code_loc);
761 muscle_user_name_list_grow ("percent_code_user_qualifiers", qualifier,
762 qualifier_loc);
763 }
764
765
766 /*------------------------------------------------.
767 | Output the definition of ENTRY as a m4_define. |
768 `------------------------------------------------*/
769
770 static inline bool
771 muscle_m4_output (muscle_entry *entry, FILE *out)
772 {
773 fprintf (out,
774 "m4_define([b4_%s],\n"
775 "[[%s]])\n\n\n", entry->key, entry->value);
776 return true;
777 }
778
779 static bool
780 muscle_m4_output_processor (void *entry, void *out)
781 {
782 return muscle_m4_output (entry, out);
783 }
784
785
786 void
787 muscles_m4_output (FILE *out)
788 {
789 hash_do_for_each (muscle_table, muscle_m4_output_processor, out);
790 }