maint: prefer STREQ/STRNEQ to strcmp contortions.
[m4/ericb.git] / src / freeze.c
blob9f36e8ccf1ccac0f88b1626fdf5c04f44d136967
1 /* GNU m4 -- A simple macro processor
2 Copyright (C) 1989-1994, 2004-2010, 2013 Free Software Foundation,
3 Inc.
5 This file is part of GNU M4.
7 GNU M4 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.
12 GNU M4 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.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 /* This module handles frozen files. */
23 #include <config.h>
25 #include "m4.h"
27 #include "binary-io.h"
28 #include "close-stream.h"
29 #include "quotearg.h"
30 #include "verify.h"
31 #include "xmemdup0.h"
33 static void produce_mem_dump (FILE *, const char *, size_t);
34 static void produce_resyntax_dump (m4 *, FILE *);
35 static void produce_syntax_dump (FILE *, m4_syntax_table *, char);
36 static void produce_module_dump (FILE *, m4_module *);
37 static void produce_symbol_dump (m4 *, FILE *, m4_symbol_table *);
38 static void *dump_symbol_CB (m4_symbol_table *, const char *,
39 size_t, m4_symbol *, void *);
40 static void issue_expect_message (m4 *, int);
41 static int decode_char (m4 *, FILE *, bool *);
44 /* Dump an ASCII-encoded representation of LEN bytes at MEM to FILE.
45 MEM may contain embedded NUL characters. */
46 static void
47 produce_mem_dump (FILE *file, const char *mem, size_t len)
49 char *quoted = quotearg_style_mem (escape_quoting_style, mem, len);
50 /* Any errors will be detected by ferror later. */
51 fwrite (quoted, strlen (quoted), 1, file);
55 /* Produce the 'R14\nPOSIX_EXTENDED\n' frozen file dump of the current
56 default regular expression syntax. Note that it would be a little
57 faster to use the encoded syntax in this format as used by re_compile(),
58 but the representation of RE_SYNTAX_POSIX_EXTENDED may change in
59 future (or alternative) implementations of re_compile, so we use an
60 unencoded representation here. */
62 static void
63 produce_resyntax_dump (m4 *context, FILE *file)
65 int code = m4_get_regexp_syntax_opt (context);
67 /* Don't dump default syntax code (`0' for GNU_EMACS). */
68 if (code)
70 const char *resyntax = m4_regexp_syntax_decode (code);
72 if (!resyntax)
73 m4_error (context, EXIT_FAILURE, 0, NULL,
74 _("invalid regexp syntax code `%d'"), code);
76 /* No need to use produce_mem_dump, since we know all resyntax
77 names are already ASCII-encoded. */
78 xfprintf (file, "R%zu\n%s\n", strlen (resyntax), resyntax);
82 static void
83 produce_syntax_dump (FILE *file, m4_syntax_table *syntax, char ch)
85 char buf[UCHAR_MAX + 1];
86 int code = m4_syntax_code (ch);
87 int count = 0;
88 int i;
90 for (i = 0; i < UCHAR_MAX + 1; ++i)
91 if (m4_has_syntax (syntax, i, code) && code != syntax->orig[i])
92 buf[count++] = i;
94 /* If code falls in M4_SYNTAX_MASKS, then we must treat it
95 specially, since it will not be found in syntax->orig. */
96 if (count == 1
97 && ((code == M4_SYNTAX_RQUOTE && *buf == *DEF_RQUOTE)
98 || (code == M4_SYNTAX_ECOMM && *buf == *DEF_ECOMM)))
99 return;
101 if (count || (code & M4_SYNTAX_MASKS))
103 xfprintf (file, "S%c%d\n", ch, count);
104 produce_mem_dump (file, buf, count);
105 fputc ('\n', file);
109 /* Store the debug mode in textual format. */
110 static void
111 produce_debugmode_state (FILE *file, int flags)
113 /* This code tracks the number of bits in M4_DEBUG_TRACE_VERBOSE. */
114 char str[15];
115 int offset = 0;
116 verify ((1 << (sizeof str - 1)) - 1 == M4_DEBUG_TRACE_VERBOSE);
117 if (flags & M4_DEBUG_TRACE_ARGS)
118 str[offset++] = 'a';
119 if (flags & M4_DEBUG_TRACE_EXPANSION)
120 str[offset++] = 'e';
121 if (flags & M4_DEBUG_TRACE_QUOTE)
122 str[offset++] = 'q';
123 if (flags & M4_DEBUG_TRACE_ALL)
124 str[offset++] = 't';
125 if (flags & M4_DEBUG_TRACE_LINE)
126 str[offset++] = 'l';
127 if (flags & M4_DEBUG_TRACE_FILE)
128 str[offset++] = 'f';
129 if (flags & M4_DEBUG_TRACE_PATH)
130 str[offset++] = 'p';
131 if (flags & M4_DEBUG_TRACE_CALL)
132 str[offset++] = 'c';
133 if (flags & M4_DEBUG_TRACE_INPUT)
134 str[offset++] = 'i';
135 if (flags & M4_DEBUG_TRACE_CALLID)
136 str[offset++] = 'x';
137 if (flags & M4_DEBUG_TRACE_MODULE)
138 str[offset++] = 'm';
139 if (flags & M4_DEBUG_TRACE_STACK)
140 str[offset++] = 's';
141 if (flags & M4_DEBUG_TRACE_DEREF)
142 str[offset++] = 'd';
143 if (flags & M4_DEBUG_TRACE_OUTPUT_DUMPDEF)
144 str[offset++] = 'o';
145 str[offset] = '\0';
146 if (offset)
147 xfprintf (file, "d%d\n%s\n", offset, str);
150 /* The modules must be dumped in the order in which they will be
151 reloaded from the frozen file. libltdl stores handles in a push
152 down stack, so we need to dump them in the reverse order to that. */
153 static void
154 produce_module_dump (FILE *file, m4_module *module)
156 const char *name = m4_get_module_name (module);
157 size_t len = strlen (name);
159 module = m4__module_next (module);
160 if (module)
161 produce_module_dump (file, module);
163 xfprintf (file, "M%zu\n", len);
164 produce_mem_dump (file, name, len);
165 fputc ('\n', file);
168 /* Process all entries in one bucket, from the last to the first.
169 This order ensures that, at reload time, pushdef's will be
170 executed with the oldest definitions first. */
171 static void
172 produce_symbol_dump (m4 *context, FILE *file, m4_symbol_table *symtab)
174 if (m4_symtab_apply (symtab, true, dump_symbol_CB, file))
175 assert (false);
178 /* Given a stack of symbol values starting with VALUE, destructively
179 reverse the stack and return the pointer to what was previously the
180 last value in the stack. VALUE may be NULL. The symbol table that
181 owns the value stack should not be modified or consulted until this
182 is called again to undo the effect. */
183 static m4_symbol_value *
184 reverse_symbol_value_stack (m4_symbol_value *value)
186 m4_symbol_value *result = NULL;
187 m4_symbol_value *next;
188 while (value)
190 next = VALUE_NEXT (value);
191 VALUE_NEXT (value) = result;
192 result = value;
193 value = next;
195 return result;
198 /* Dump the stack of values for SYMBOL, with name SYMBOL_NAME and
199 length LEN, located in SYMTAB. USERDATA is interpreted as the
200 FILE* to dump to. */
201 static void *
202 dump_symbol_CB (m4_symbol_table *symtab, const char *symbol_name, size_t len,
203 m4_symbol *symbol, void *userdata)
205 FILE *file = (FILE *) userdata;
206 m4_symbol_value *value;
207 m4_symbol_value *last;
209 last = value = reverse_symbol_value_stack (m4_get_symbol_value (symbol));
210 while (value)
212 m4_module *module = VALUE_MODULE (value);
213 const char *module_name = module ? m4_get_module_name (module) : NULL;
214 size_t module_len = module_name ? strlen (module_name) : 0;
216 if (m4_is_symbol_value_text (value))
218 const char *text = m4_get_symbol_value_text (value);
219 size_t text_len = m4_get_symbol_value_len (value);
220 xfprintf (file, "T%zu,%zu", len, text_len);
221 if (module)
222 xfprintf (file, ",%zu", module_len);
223 fputc ('\n', file);
225 produce_mem_dump (file, symbol_name, len);
226 fputc ('\n', file);
227 produce_mem_dump (file, text, text_len);
228 fputc ('\n', file);
229 if (module)
231 produce_mem_dump (file, module_name, module_len);
232 fputc ('\n', file);
235 else if (m4_is_symbol_value_func (value))
237 const m4_builtin *bp = m4_get_symbol_value_builtin (value);
238 size_t bp_len;
239 if (bp == NULL)
240 assert (!"INTERNAL ERROR: builtin not found in builtin table!");
241 bp_len = strlen (bp->name);
243 xfprintf (file, "F%zu,%zu", len, bp_len);
244 if (module)
245 xfprintf (file, ",%zu", module_len);
246 fputc ('\n', file);
248 produce_mem_dump (file, symbol_name, len);
249 fputc ('\n', file);
250 produce_mem_dump (file, bp->name, bp_len);
251 fputc ('\n', file);
252 if (module)
254 produce_mem_dump (file, module_name, module_len);
255 fputc ('\n', file);
258 else if (m4_is_symbol_value_placeholder (value))
259 ; /* Nothing to do for a builtin we couldn't reload earlier. */
260 else
261 assert (!"dump_symbol_CB");
262 value = VALUE_NEXT (value);
264 reverse_symbol_value_stack (last);
265 if (m4_get_symbol_traced (symbol))
266 xfprintf (file, "t%zu\n%s\n", len, symbol_name);
267 return NULL;
270 /* Produce a frozen state to the given file NAME. */
271 void
272 produce_frozen_state (m4 *context, const char *name)
274 FILE *file = fopen (name, O_BINARY ? "wb" : "w");
275 const char *str;
276 const m4_string_pair *pair;
278 if (!file)
280 m4_error (context, 0, errno, NULL, _("cannot open %s"),
281 quotearg_style (locale_quoting_style, name));
282 return;
285 /* Write a recognizable header. */
287 xfprintf (file, "# This is a frozen state file generated by GNU %s %s\n",
288 PACKAGE, VERSION);
289 fputs ("V2\n", file);
291 /* Dump quote delimiters. */
292 pair = m4_get_syntax_quotes (M4SYNTAX);
293 if (STRNEQ (pair->str1, DEF_LQUOTE) || STRNEQ (pair->str2, DEF_RQUOTE))
295 xfprintf (file, "Q%zu,%zu\n", pair->len1, pair->len2);
296 produce_mem_dump (file, pair->str1, pair->len1);
297 fputc ('\n', file);
298 produce_mem_dump (file, pair->str2, pair->len2);
299 fputc ('\n', file);
302 /* Dump comment delimiters. */
303 pair = m4_get_syntax_comments (M4SYNTAX);
304 if (STRNEQ (pair->str1, DEF_BCOMM) || STRNEQ (pair->str2, DEF_ECOMM))
306 xfprintf (file, "C%zu,%zu\n", pair->len1, pair->len2);
307 produce_mem_dump (file, pair->str1, pair->len1);
308 fputc ('\n', file);
309 produce_mem_dump (file, pair->str2, pair->len2);
310 fputc ('\n', file);
313 /* Dump regular expression syntax. */
314 produce_resyntax_dump (context, file);
316 /* Dump syntax table. */
317 str = "I@WLBOD${}SA(),RE";
318 while (*str)
319 produce_syntax_dump (file, M4SYNTAX, *str++);
321 /* Dump debugmode state. */
322 produce_debugmode_state (file, m4_get_debug_level_opt (context));
324 /* Dump all loaded modules. */
325 produce_module_dump (file, m4__module_next (NULL));
327 /* Dump all symbols. */
328 produce_symbol_dump (context, file, M4SYMTAB);
330 /* Let diversions be issued from output.c module, its cleaner to have this
331 piece of code there. */
332 m4_freeze_diversions (context, file);
334 /* All done. */
336 fputs ("# End of frozen state file\n", file);
337 if (close_stream (file) != 0)
338 m4_error (context, EXIT_FAILURE, errno, NULL,
339 _("unable to create frozen state"));
342 /* Issue a message saying that some character is an EXPECTED character. */
343 static void
344 issue_expect_message (m4 *context, int expected)
346 if (expected == '\n')
347 m4_error (context, EXIT_FAILURE, 0, NULL,
348 _("expecting line feed in frozen file"));
349 else
350 m4_error (context, EXIT_FAILURE, 0, NULL,
351 _("expecting character `%c' in frozen file"), expected);
355 /* Reload frozen state. */
357 /* Read the next character from the IN stream. Various escape
358 sequences are converted, and returned. EOF is returned if the end
359 of file is reached whilst reading the character, or on an
360 unrecognized escape sequence. */
362 static int
363 decode_char (m4 *context, FILE *in, bool *advance_line)
365 int ch = getc (in);
366 int next;
367 int value = 0;
369 if (*advance_line)
371 m4_set_current_line (context, m4_get_current_line (context) + 1);
372 *advance_line = false;
375 while (ch == '\\')
377 ch = getc (in);
378 switch (ch)
380 case 'a': return '\a';
381 case 'b': return '\b';
382 case 'f': return '\f';
383 case 'n': return '\n';
384 case 'r': return '\r';
385 case 't': return '\t';
386 case 'v': return '\v';
387 case '\\': return '\\';
389 case '\n':
390 ch = getc (in);
391 m4_set_current_line (context, m4_get_current_line (context) + 1);
392 continue;
394 case 'x': case 'X':
395 next = getc (in);
396 if (next >= '0' && next <= '9')
397 ch = (next - '0') * 16;
398 else if (next >= 'a' && next <= 'f')
399 ch = (next - 'a' + 10) * 16;
400 else if (next >= 'A' && next <= 'F')
401 ch = (next - 'A' + 10) * 16;
402 else
403 return EOF;
404 next = getc (in);
405 if (next >= '0' && next <= '9')
406 ch += next - '0';
407 else if (next >= 'a' && next <= 'f')
408 ch += next - 'a' + 10;
409 else if (next >= 'A' && next <= 'F')
410 ch += next - 'A' + 10;
411 else
412 return EOF;
413 return ch;
414 case '0': case '1': case '2': case '3':
415 value = ch - '0';
416 ch = getc (in);
417 /* fall through */
418 case '4': case '5': case '6': case '7':
419 if (ch >= '0' && ch <= '7')
421 value = value * 8 + ch - '0';
422 ch = getc (in);
424 else
426 ungetc (ch, in);
427 return value;
429 if (ch >= '0' && ch <= '7')
430 value = value * 8 + ch - '0';
431 else
432 ungetc (ch, in);
433 return value;
435 default:
436 return EOF;
440 if (ch == '\n')
441 *advance_line = true;
442 return ch;
446 /* Reload state from the given file NAME. We are seeking speed,
447 here. */
449 void
450 reload_frozen_state (m4 *context, const char *name)
452 FILE *file = NULL;
453 char *filepath;
454 int version;
455 int character;
456 int operation;
457 char syntax;
458 char *string[3];
459 size_t allocated[3];
460 int number[3] = {0};
461 bool advance_line = true;
463 #define GET_CHARACTER \
464 do \
466 if (advance_line) \
468 m4_set_current_line (context, \
469 m4_get_current_line (context) + 1); \
470 advance_line = false; \
472 character = getc (file); \
473 if (character == '\n') \
474 advance_line = true; \
476 while (0)
478 #define GET_NUMBER(Number, AllowNeg) \
479 do \
481 unsigned int n = 0; \
482 while (isdigit (character) && n <= INT_MAX / 10) \
484 n = 10 * n + character - '0'; \
485 GET_CHARACTER; \
487 if (((AllowNeg) ? INT_MIN: INT_MAX) < n \
488 || isdigit (character)) \
489 m4_error (context, EXIT_FAILURE, 0, NULL, \
490 _("integer overflow in frozen file")); \
491 (Number) = n; \
493 while (0)
495 #define GET_STRING(File, Buf, BufSize, StrLen, UseChar) \
496 do \
498 size_t len = (StrLen); \
499 char *p; \
500 int ch; \
501 if (UseChar) \
503 ungetc (character, File); \
504 if (advance_line) \
506 assert (character == '\n'); \
507 advance_line = false; \
510 CHECK_ALLOCATION ((Buf), (BufSize), len); \
511 p = (Buf); \
512 while (len-- > 0) \
514 ch = (version > 1 \
515 ? decode_char (context, File, &advance_line) \
516 : getc (File)); \
517 if (ch == EOF) \
518 m4_error (context, EXIT_FAILURE, 0, NULL, \
519 _("premature end of frozen file")); \
520 *p++ = ch; \
522 *p = '\0'; \
523 GET_CHARACTER; \
524 while (version > 1 && character == '\\') \
526 GET_CHARACTER; \
527 VALIDATE ('\n'); \
528 GET_CHARACTER; \
531 while (0)
533 #define VALIDATE(Expected) \
534 do \
536 if (character != (Expected)) \
537 issue_expect_message (context, (Expected)); \
539 while (0)
541 #define CHECK_ALLOCATION(Where, Allocated, Needed) \
542 do \
544 if ((Needed) + 1 > (Allocated)) \
546 free (Where); \
547 (Allocated) = (Needed) + 1; \
548 (Where) = xcharalloc (Allocated); \
551 while (0)
553 /* Skip comments (`#' at beginning of line) and blank lines, setting
554 character to the next directive or to EOF. */
556 #define GET_DIRECTIVE \
557 do \
559 GET_CHARACTER; \
560 if (character == '#') \
562 while (character != EOF && character != '\n') \
563 GET_CHARACTER; \
564 VALIDATE ('\n'); \
567 while (character == '\n')
569 filepath = m4_path_search (context, name, NULL);
570 file = m4_fopen (context, filepath, "r");
571 if (file == NULL)
572 m4_error (context, EXIT_FAILURE, errno, NULL, _("cannot open %s"),
573 quotearg_style (locale_quoting_style, name));
574 m4_set_current_file (context, name);
576 allocated[0] = 100;
577 string[0] = xcharalloc (allocated[0]);
578 allocated[1] = 100;
579 string[1] = xcharalloc (allocated[1]);
580 allocated[2] = 100;
581 string[2] = xcharalloc (allocated[2]);
583 /* Validate format version. Accept both `1' (m4 1.3 and 1.4.x) and
584 `2' (m4 2.0). */
585 GET_DIRECTIVE;
586 VALIDATE ('V');
587 GET_CHARACTER;
588 GET_NUMBER (version, false);
589 switch (version)
591 case 2:
592 break;
593 case 1:
594 m4__module_open (context, "m4", NULL);
595 if (m4_get_posixly_correct_opt (context))
596 m4__module_open (context, "traditional", NULL);
597 else
598 m4__module_open (context, "gnu", NULL);
599 /* Disable { and } categories, since ${11} was not supported in
600 1.4.x. */
601 m4_set_syntax (M4SYNTAX, 'O', '+', "{}", 2);
602 break;
603 default:
604 if (version > 2)
605 m4_error (context, EXIT_MISMATCH, 0, NULL,
606 _("frozen file version %d greater than max supported of 2"),
607 version);
608 else
609 m4_error (context, EXIT_FAILURE, 0, NULL,
610 _("ill-formed frozen file, version directive expected"));
612 VALIDATE ('\n');
614 GET_DIRECTIVE;
615 while (character != EOF)
617 switch (character)
619 default:
620 m4_error (context, EXIT_FAILURE, 0, NULL,
621 _("ill-formed frozen file, unknown directive %c"),
622 character);
624 case 'd':
625 /* Set debugmode flags. */
626 if (version < 2)
628 /* 'd' operator is not supported in format version 1. */
629 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
630 ill-formed frozen file, version 2 directive `%c' encountered"), 'd');
633 GET_CHARACTER;
634 GET_NUMBER (number[0], false);
635 VALIDATE ('\n');
636 GET_STRING (file, string[0], allocated[0], number[0], false);
637 VALIDATE ('\n');
639 if (m4_debug_decode (context, string[0], number[0]) < 0)
640 m4_error (context, EXIT_FAILURE, 0, NULL,
641 _("unknown debug mode %s"),
642 quotearg_style_mem (locale_quoting_style, string[0],
643 number[0]));
644 break;
646 case 'F':
647 GET_CHARACTER;
649 /* Get string lengths. */
651 GET_NUMBER (number[0], false);
652 VALIDATE (',');
653 GET_CHARACTER;
654 GET_NUMBER (number[1], false);
656 if (character == ',')
658 if (version > 1)
660 /* 'F' operator accepts an optional third argument for
661 format versions 2 or later. */
662 GET_CHARACTER;
663 GET_NUMBER (number[2], false);
665 else
666 /* 3 argument 'F' operations are invalid for format
667 version 1. */
668 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
669 ill-formed frozen file, version 2 directive `%c' encountered"), 'F');
671 else
673 number[2] = 0;
676 VALIDATE ('\n');
679 /* Get string contents. */
681 GET_STRING (file, string[0], allocated[0], number[0], false);
682 if (version > 1)
684 VALIDATE ('\n');
685 GET_CHARACTER;
687 GET_STRING (file, string[1], allocated[1], number[1], true);
688 if (version > 1 && number[2])
690 VALIDATE ('\n');
691 GET_CHARACTER;
693 GET_STRING (file, string[2], allocated[2], number[2], true);
694 VALIDATE ('\n');
696 /* Enter a macro having a builtin function as a definition. */
698 m4_module *module = NULL;
699 m4_symbol_value *token;
701 // Builtins cannot contain a NUL byte.
702 if (strlen (string[1]) < number[1])
703 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
704 ill-formed frozen file, invalid builtin %s encountered"),
705 quotearg_style_mem (locale_quoting_style, string[1],
706 number[1]));
707 if (number[2] > 0)
709 if (strlen (string[2]) < number[2])
710 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
711 ill-formed frozen file, invalid module %s encountered"),
712 quotearg_style_mem (locale_quoting_style,
713 string[2], number[2]));
714 module = m4__module_find (string[2]);
716 token = m4_builtin_find_by_name (module, string[1]);
718 if (token == NULL)
720 token = (m4_symbol_value *) xzalloc (sizeof *token);
721 m4_set_symbol_value_placeholder (token, xstrdup (string[1]));
722 VALUE_MODULE (token) = module;
723 VALUE_MIN_ARGS (token) = 0;
724 VALUE_MAX_ARGS (token) = -1;
726 m4_symbol_pushdef (M4SYMTAB, string[0], number[0], token);
728 break;
730 case 'M':
732 /* Load a module, but *without* perturbing the symbol table.
733 Note that any expansion from loading the module which would
734 have been seen when loading it originally is discarded
735 when loading it from a frozen file. */
737 if (version < 2)
739 /* 'M' operator is not supported in format version 1. */
740 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
741 ill-formed frozen file, version 2 directive `%c' encountered"), 'M');
744 GET_CHARACTER;
745 GET_NUMBER (number[0], false);
746 VALIDATE ('\n');
747 GET_STRING (file, string[0], allocated[0], number[0], false);
748 VALIDATE ('\n');
750 if (strlen (string[0]) < number[0])
751 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
752 ill-formed frozen file, invalid module %s encountered"),
753 quotearg_style_mem (locale_quoting_style,
754 string[0], number[0]));
755 m4__module_open (context, string[0], NULL);
757 break;
759 case 'R':
761 if (version < 2)
763 /* 'R' operator is not supported in format version 1. */
764 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
765 ill-formed frozen file, version 2 directive `%c' encountered"), 'R');
768 GET_CHARACTER;
769 GET_NUMBER (number[0], false);
770 VALIDATE ('\n');
771 GET_STRING (file, string[0], allocated[0], number[0], false);
772 VALIDATE ('\n');
774 m4_set_regexp_syntax_opt (context,
775 m4_regexp_syntax_encode (string[0]));
776 if (m4_get_regexp_syntax_opt (context) < 0
777 || strlen (string[0]) < number[0])
779 m4_error (context, EXIT_FAILURE, 0, NULL,
780 _("bad syntax-spec %s"),
781 quotearg_style_mem (locale_quoting_style, string[0],
782 number[0]));
785 break;
787 case 'S':
789 if (version < 2)
791 /* 'S' operator is not supported in format version 1. */
792 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
793 ill-formed frozen file, version 2 directive `%c' encountered"), 'S');
796 GET_CHARACTER;
797 syntax = character;
798 GET_CHARACTER;
799 GET_NUMBER (number[0], false);
800 VALIDATE ('\n');
801 GET_STRING (file, string[0], allocated[0], number[0], false);
803 /* Syntax under M4_SYNTAX_MASKS is handled specially; all
804 other characters are additive. */
805 if ((m4_set_syntax (M4SYNTAX, syntax,
806 (m4_syntax_code (syntax) & M4_SYNTAX_MASKS
807 ? '=' : '+'), string[0], number[0]) < 0)
808 && (syntax != '\0'))
810 m4_error (context, 0, 0, NULL,
811 _("undefined syntax code %c"), syntax);
813 break;
815 case 't':
816 /* Trace a macro name. */
817 if (version < 2)
819 /* 't' operator is not supported in format version 1. */
820 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
821 ill-formed frozen file, version 2 directive `%c' encountered"), 't');
824 GET_CHARACTER;
825 GET_NUMBER (number[0], false);
826 VALIDATE ('\n');
827 GET_STRING (file, string[0], allocated[0], number[0], false);
828 VALIDATE ('\n');
830 m4_set_symbol_name_traced (M4SYMTAB, string[0], number[0], true);
832 break;
834 case 'C':
835 case 'D':
836 case 'Q':
837 operation = character;
838 GET_CHARACTER;
840 /* Get string lengths. */
842 if (operation == 'D' && character == '-')
844 /* Accept a negative diversion number. */
845 GET_CHARACTER;
846 GET_NUMBER (number[0], true);
847 number[0] = -number[0];
849 else
850 GET_NUMBER (number[0], false);
851 VALIDATE (',');
852 GET_CHARACTER;
853 GET_NUMBER (number[1], false);
854 VALIDATE ('\n');
856 /* Get string contents. */
857 if (operation != 'D')
859 GET_STRING (file, string[0], allocated[0], number[0], false);
860 if (version > 1)
862 VALIDATE ('\n');
863 GET_CHARACTER;
866 else
867 GET_CHARACTER;
868 GET_STRING (file, string[1], allocated[1], number[1], true);
869 VALIDATE ('\n');
871 /* Act according to operation letter. */
873 switch (operation)
875 case 'C':
877 /* Change comment strings. */
879 m4_set_comment (M4SYNTAX, string[0], number[0], string[1],
880 number[1]);
881 break;
883 case 'D':
885 /* Select a diversion and add a string to it. */
887 m4_make_diversion (context, number[0]);
888 if (number[1] > 0)
889 m4_output_text (context, string[1], number[1]);
890 break;
892 case 'Q':
894 /* Change quote strings. */
896 m4_set_quotes (M4SYNTAX, string[0], number[0], string[1],
897 number[1]);
898 break;
900 default:
902 /* Cannot happen. */
904 break;
906 break;
908 case 'T':
909 GET_CHARACTER;
911 /* Get string lengths. */
913 GET_NUMBER (number[0], false);
914 VALIDATE (',');
915 GET_CHARACTER;
916 GET_NUMBER (number[1], false);
918 if (character == ',')
920 if (version > 1)
922 /* 'T' operator accepts an optional third argument for
923 format versions 2 or later. */
924 GET_CHARACTER;
925 GET_NUMBER (number[2], false);
927 else
929 /* 3 argument 'T' operations are invalid for format
930 version 1. */
931 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
932 ill-formed frozen file, version 2 directive `%c' encountered"), 'T');
935 else
936 number[2] = 0;
938 VALIDATE ('\n');
940 /* Get string contents. */
941 GET_STRING (file, string[0], allocated[0], number[0], false);
942 if (version > 1)
944 VALIDATE ('\n');
945 GET_CHARACTER;
947 GET_STRING (file, string[1], allocated[1], number[1], true);
948 if (version > 1 && number[2])
950 VALIDATE ('\n');
951 GET_CHARACTER;
953 GET_STRING (file, string[2], allocated[2], number[2], true);
954 VALIDATE ('\n');
956 /* Enter a macro having an expansion text as a definition. */
958 m4_symbol_value *token;
959 m4_module *module = NULL;
961 token = (m4_symbol_value *) xzalloc (sizeof *token);
962 if (number[2] > 0)
964 if (strlen (string[2]) < number[2])
965 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
966 ill-formed frozen file, invalid module %s encountered"),
967 quotearg_style_mem (locale_quoting_style,
968 string[2], number[2]));
969 module = m4__module_find (string[2]);
972 m4_set_symbol_value_text (token, xmemdup0 (string[1], number[1]),
973 number[1], 0);
974 VALUE_MODULE (token) = module;
975 VALUE_MAX_ARGS (token) = -1;
977 m4_symbol_pushdef (M4SYMTAB, string[0], number[0], token);
979 break;
982 GET_DIRECTIVE;
985 free (string[0]);
986 free (string[1]);
987 free (string[2]);
988 if (close_stream (file) != 0)
989 m4_error (context, EXIT_FAILURE, errno, NULL,
990 _("unable to read frozen state"));
991 m4_set_current_file (context, NULL);
992 m4_set_current_line (context, 0);
994 #undef GET_STRING
995 #undef GET_CHARACTER
996 #undef GET_NUMBER
997 #undef VALIDATE
998 #undef CHECK_ALLOCATION
999 #undef GET_DIRECTIVE