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
muscle_kind_new(char const * k)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 *
muscle_kind_string(muscle_kind k)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
muscle_name(char const * var,char const * field)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
hash_compare_muscles(void const * x,void const * y)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
hash_muscle(const void * x,size_t tablesize)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 *
muscle_entry_new(char const * key)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
muscle_entry_free(void * entry)115 muscle_entry_free (void *entry)
116 {
117 muscle_entry *mentry = entry;
118 free (mentry->storage);
119 free (mentry);
120 }
121
122 void
muscle_init(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
muscle_free(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 *
muscle_lookup(char const * key)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
muscle_insert(char const * key,char const * value)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
muscle_grow(const char * key,const char * val,const char * separator,const char * terminator)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
muscle_syncline_grow(char const * key,location loc)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, 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
muscle_code_grow(const char * key,const char * val,location loc)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
muscle_pair_list_grow(const char * muscle,const char * a1,const char * a2)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 *
muscle_find_const(char const * key)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 *
muscle_find(char const * key)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
muscle_boundary_grow(char const * key,boundary bound)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
muscle_location_grow(char const * key,location loc)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 *
string_decode(char const * key)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
location_decode(char const * value)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
muscle_user_name_list_grow(char const * key,char const * user_name,location loc)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 *
define_directive(char const * assignment,muscle_kind kind,char const * value)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 *
muscle_percent_variable_update(char const * variable,muscle_kind kind,char const ** value,char ** old,char ** upd)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
muscle_percent_define_insert(char const * var,location variable_loc,muscle_kind kind,char const * value,muscle_percent_define_how how)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 muscle_percent_define_how how_old
521 = atoi (muscle_find_const (how_name));
522 if (how_old == MUSCLE_PERCENT_DEFINE_F)
523 goto end;
524 /* If assigning the same value, make it a warning. */
525 warnings warn = STREQ (value, current_value) ? Wother : complaint;
526 complain (&variable_loc, warn,
527 _("%%define variable %s redefined"),
528 quote (variable));
529 location loc = muscle_percent_define_get_loc (variable);
530 subcomplain (&loc, warn, _("previous definition"));
531 fixits_register (&variable_loc, "");
532 warned = true;
533 }
534 }
535
536 if (!warned && old && upd)
537 deprecated_directive (&variable_loc, old, upd);
538
539 MUSCLE_INSERT_STRING (name, value);
540 muscle_insert (loc_name, "");
541 muscle_location_grow (loc_name, variable_loc);
542 muscle_insert (syncline_name, "");
543 muscle_syncline_grow (syncline_name, variable_loc);
544 muscle_user_name_list_grow ("percent_define_user_variables", variable,
545 variable_loc);
546 MUSCLE_INSERT_INT (how_name, how);
547 MUSCLE_INSERT_STRING (kind_name, muscle_kind_string (kind));
548 end:
549 free (old);
550 free (upd);
551 if (variable != var)
552 free ((char *) variable);
553 }
554
555 /* This is used for backward compatibility, e.g., "%define api.pure"
556 supersedes "%pure-parser". */
557 void
muscle_percent_define_ensure(char const * variable,location loc,bool value)558 muscle_percent_define_ensure (char const *variable, location loc,
559 bool value)
560 {
561 uniqstr name = muscle_name (variable, NULL);
562 char const *val = value ? "" : "false";
563
564 /* Don't complain is VARIABLE is already defined, but be sure to set
565 its value to VAL. */
566 if (!muscle_find_const (name)
567 || muscle_percent_define_flag_if (variable) != value)
568 muscle_percent_define_insert (variable, loc, muscle_keyword, val,
569 MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
570 }
571
572 /* Mark %define VARIABLE as used. */
573 static void
muscle_percent_define_use(char const * variable)574 muscle_percent_define_use (char const *variable)
575 {
576 muscle_insert (muscle_name (variable, "bison_variables"), "");
577 }
578
579 /* The value of %define variable VARIABLE (corresponding to FIELD, if
580 defined). Do not register as used, but diagnose unset variables. */
581
582 static
583 char const *
muscle_percent_define_get_raw(char const * variable,char const * field)584 muscle_percent_define_get_raw (char const *variable, char const *field)
585 {
586 uniqstr name = muscle_name (variable, field);
587 char const *res = muscle_find_const (name);
588 if (!res)
589 complain (NULL, fatal, _("%s: undefined %%define variable %s"),
590 "muscle_percent_define_get_raw", quote (variable));
591 return res;
592 }
593
594 char *
muscle_percent_define_get(char const * variable)595 muscle_percent_define_get (char const *variable)
596 {
597 uniqstr name = muscle_name (variable, NULL);
598 char *value = string_decode (name);
599 if (!value)
600 value = xstrdup ("");
601 muscle_percent_define_use (variable);
602 return value;
603 }
604
605 /* The kind of VARIABLE. An error if undefined. */
606 static muscle_kind
muscle_percent_define_get_kind(char const * variable)607 muscle_percent_define_get_kind (char const *variable)
608 {
609 return muscle_kind_new (muscle_percent_define_get_raw (variable, "kind"));
610 }
611
612 /* Check the kind of VARIABLE. An error if undefined. */
613 static void
muscle_percent_define_check_kind(char const * variable,muscle_kind kind)614 muscle_percent_define_check_kind (char const *variable, muscle_kind kind)
615 {
616 if (muscle_percent_define_get_kind (variable) != kind)
617 {
618 location loc = muscle_percent_define_get_loc (variable);
619 switch (kind)
620 {
621 case muscle_code:
622 complain (&loc, Wdeprecated,
623 _("%%define variable '%s' requires '{...}' values"),
624 variable);
625 break;
626 case muscle_keyword:
627 complain (&loc, Wdeprecated,
628 _("%%define variable '%s' requires keyword values"),
629 variable);
630 break;
631 case muscle_string:
632 complain (&loc, Wdeprecated,
633 _("%%define variable '%s' requires '\"...\"' values"),
634 variable);
635 break;
636 }
637 }
638 }
639
640
641 location
muscle_percent_define_get_loc(char const * variable)642 muscle_percent_define_get_loc (char const *variable)
643 {
644 return location_decode (muscle_percent_define_get_raw (variable, "loc"));
645 }
646
647 char const *
muscle_percent_define_get_syncline(char const * variable)648 muscle_percent_define_get_syncline (char const *variable)
649 {
650 return muscle_percent_define_get_raw (variable, "syncline");
651 }
652
653 bool
muscle_percent_define_ifdef(char const * variable)654 muscle_percent_define_ifdef (char const *variable)
655 {
656 if (muscle_find_const (muscle_name (variable, NULL)))
657 {
658 muscle_percent_define_use (variable);
659 return true;
660 }
661 else
662 return false;
663 }
664
665 bool
muscle_percent_define_flag_if(char const * variable)666 muscle_percent_define_flag_if (char const *variable)
667 {
668 uniqstr invalid_boolean_name = muscle_name (variable, "invalid_boolean");
669 bool res = false;
670
671 if (muscle_percent_define_ifdef (variable))
672 {
673 char *value = muscle_percent_define_get (variable);
674 muscle_percent_define_check_kind (variable, muscle_keyword);
675 if (value[0] == '\0' || STREQ (value, "true"))
676 res = true;
677 else if (STREQ (value, "false"))
678 res = false;
679 else if (!muscle_find_const (invalid_boolean_name))
680 {
681 muscle_insert (invalid_boolean_name, "");
682 location loc = muscle_percent_define_get_loc (variable);
683 complain (&loc, complaint,
684 _("invalid value for %%define Boolean variable %s"),
685 quote (variable));
686 }
687 free (value);
688 }
689 else
690 complain (NULL, fatal, _("%s: undefined %%define variable %s"),
691 "muscle_percent_define_flag", quote (variable));
692
693 return res;
694 }
695
696 void
muscle_percent_define_default(char const * variable,char const * value)697 muscle_percent_define_default (char const *variable, char const *value)
698 {
699 uniqstr name = muscle_name (variable, NULL);
700 if (!muscle_find_const (name))
701 {
702 MUSCLE_INSERT_STRING (name, value);
703 MUSCLE_INSERT_STRING (muscle_name (variable, "kind"), "keyword");
704 {
705 uniqstr loc_name = muscle_name (variable, "loc");
706 location loc;
707 loc.start.file = "<default value>";
708 loc.start.line = -1;
709 loc.start.column = -1;
710 loc.start.byte = -1;
711 loc.end = loc.start;
712 muscle_insert (loc_name, "");
713 muscle_location_grow (loc_name, loc);
714 }
715 muscle_insert (muscle_name (variable, "syncline"), "");
716 }
717 }
718
719 void
muscle_percent_define_check_values(char const * const * values)720 muscle_percent_define_check_values (char const * const *values)
721 {
722 for (; *values; ++values)
723 {
724 char const * const *variablep = values;
725 uniqstr name = muscle_name (*variablep, NULL);
726 char *value = string_decode (name);
727 muscle_percent_define_check_kind (*variablep, muscle_keyword);
728 if (value)
729 {
730 for (++values; *values; ++values)
731 if (STREQ (value, *values))
732 break;
733 if (!*values)
734 {
735 location loc = muscle_percent_define_get_loc (*variablep);
736 complain (&loc, complaint,
737 _("invalid value for %%define variable %s: %s"),
738 quote (*variablep), quote_n (1, value));
739 for (values = variablep + 1; *values; ++values)
740 subcomplain (&loc, complaint | no_caret | silent,
741 _("accepted value: %s"), quote (*values));
742 }
743 else
744 while (*values)
745 ++values;
746 free (value);
747 }
748 else
749 complain (NULL, fatal, _("%s: undefined %%define variable %s"),
750 "muscle_percent_define_check_values", quote (*variablep));
751 }
752 }
753
754 void
muscle_percent_code_grow(char const * qualifier,location qualifier_loc,char const * code,location code_loc)755 muscle_percent_code_grow (char const *qualifier, location qualifier_loc,
756 char const *code, location code_loc)
757 {
758 char const *name = UNIQSTR_CONCAT ("percent_code(", qualifier, ")");
759 muscle_code_grow (name, code, code_loc);
760 muscle_user_name_list_grow ("percent_code_user_qualifiers", qualifier,
761 qualifier_loc);
762 }
763
764
765 /*------------------------------------------------.
766 | Output the definition of ENTRY as a m4_define. |
767 `------------------------------------------------*/
768
769 static inline bool
muscle_m4_output(muscle_entry * entry,FILE * out)770 muscle_m4_output (muscle_entry *entry, FILE *out)
771 {
772 fprintf (out,
773 "m4_define([b4_%s],\n"
774 "[[%s]])\n\n\n", entry->key, entry->value);
775 return true;
776 }
777
778 static bool
muscle_m4_output_processor(void * entry,void * out)779 muscle_m4_output_processor (void *entry, void *out)
780 {
781 return muscle_m4_output (entry, out);
782 }
783
784
785 void
muscles_m4_output(FILE * out)786 muscles_m4_output (FILE *out)
787 {
788 hash_do_for_each (muscle_table, muscle_m4_output_processor, out);
789 }
790