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