Avoid bugs on platforms that mishandle trailing /.
[m4.git] / src / freeze.c
blob44400f3f1b27d57c2ee48d9e15fadf1d46dc8298
1 /* GNU m4 -- A simple macro processor
2 Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006,
3 2007, 2008 Free Software Foundation, 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[14];
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 str[offset] = '\0';
144 if (offset)
145 xfprintf (file, "d%d\n%s\n", offset, str);
148 /* The modules must be dumped in the order in which they will be
149 reloaded from the frozen file. libltdl stores handles in a push
150 down stack, so we need to dump them in the reverse order to that. */
151 static void
152 produce_module_dump (FILE *file, m4_module *module)
154 const char *name = m4_get_module_name (module);
155 size_t len = strlen (name);
157 module = m4__module_next (module);
158 if (module)
159 produce_module_dump (file, module);
161 xfprintf (file, "M%zu\n", len);
162 produce_mem_dump (file, name, len);
163 fputc ('\n', file);
166 /* Process all entries in one bucket, from the last to the first.
167 This order ensures that, at reload time, pushdef's will be
168 executed with the oldest definitions first. */
169 static void
170 produce_symbol_dump (m4 *context, FILE *file, m4_symbol_table *symtab)
172 if (m4_symtab_apply (symtab, true, dump_symbol_CB, file))
173 assert (false);
176 /* Given a stack of symbol values starting with VALUE, destructively
177 reverse the stack and return the pointer to what was previously the
178 last value in the stack. VALUE may be NULL. The symbol table that
179 owns the value stack should not be modified or consulted until this
180 is called again to undo the effect. */
181 static m4_symbol_value *
182 reverse_symbol_value_stack (m4_symbol_value *value)
184 m4_symbol_value *result = NULL;
185 m4_symbol_value *next;
186 while (value)
188 next = VALUE_NEXT (value);
189 VALUE_NEXT (value) = result;
190 result = value;
191 value = next;
193 return result;
196 /* Dump the stack of values for SYMBOL, with name SYMBOL_NAME and
197 length LEN, located in SYMTAB. USERDATA is interpreted as the
198 FILE* to dump to. */
199 static void *
200 dump_symbol_CB (m4_symbol_table *symtab, const char *symbol_name, size_t len,
201 m4_symbol *symbol, void *userdata)
203 FILE *file = (FILE *) userdata;
204 m4_symbol_value *value;
205 m4_symbol_value *last;
207 last = value = reverse_symbol_value_stack (m4_get_symbol_value (symbol));
208 while (value)
210 m4_module *module = VALUE_MODULE (value);
211 const char *module_name = module ? m4_get_module_name (module) : NULL;
212 size_t module_len = module_name ? strlen (module_name) : 0;
214 if (m4_is_symbol_value_text (value))
216 const char *text = m4_get_symbol_value_text (value);
217 size_t text_len = m4_get_symbol_value_len (value);
218 xfprintf (file, "T%zu,%zu", len, text_len);
219 if (module)
220 xfprintf (file, ",%zu", module_len);
221 fputc ('\n', file);
223 produce_mem_dump (file, symbol_name, len);
224 fputc ('\n', file);
225 produce_mem_dump (file, text, text_len);
226 fputc ('\n', file);
227 if (module)
229 produce_mem_dump (file, module_name, module_len);
230 fputc ('\n', file);
233 else if (m4_is_symbol_value_func (value))
235 const m4_builtin *bp = m4_get_symbol_value_builtin (value);
236 size_t bp_len;
237 if (bp == NULL)
238 assert (!"INTERNAL ERROR: builtin not found in builtin table!");
239 bp_len = strlen (bp->name);
241 xfprintf (file, "F%zu,%zu", len, bp_len);
242 if (module)
243 xfprintf (file, ",%zu", module_len);
244 fputc ('\n', file);
246 produce_mem_dump (file, symbol_name, len);
247 fputc ('\n', file);
248 produce_mem_dump (file, bp->name, bp_len);
249 fputc ('\n', file);
250 if (module)
252 produce_mem_dump (file, module_name, module_len);
253 fputc ('\n', file);
256 else if (m4_is_symbol_value_placeholder (value))
257 ; /* Nothing to do for a builtin we couldn't reload earlier. */
258 else
259 assert (!"dump_symbol_CB");
260 value = VALUE_NEXT (value);
262 reverse_symbol_value_stack (last);
263 if (m4_get_symbol_traced (symbol))
264 xfprintf (file, "t%zu\n%s\n", len, symbol_name);
265 return NULL;
268 /* Produce a frozen state to the given file NAME. */
269 void
270 produce_frozen_state (m4 *context, const char *name)
272 FILE *file = fopen (name, O_BINARY ? "wb" : "w");
273 const char *str;
274 const m4_string_pair *pair;
276 if (!file)
278 m4_error (context, 0, errno, NULL, _("cannot open `%s'"), name);
279 return;
282 /* Write a recognizable header. */
284 xfprintf (file, "# This is a frozen state file generated by GNU %s %s\n",
285 PACKAGE, VERSION);
286 fputs ("V2\n", file);
288 /* Dump quote delimiters. */
289 pair = m4_get_syntax_quotes (M4SYNTAX);
290 if (strcmp (pair->str1, DEF_LQUOTE) || strcmp (pair->str2, DEF_RQUOTE))
292 xfprintf (file, "Q%zu,%zu\n", pair->len1, pair->len2);
293 produce_mem_dump (file, pair->str1, pair->len1);
294 fputc ('\n', file);
295 produce_mem_dump (file, pair->str2, pair->len2);
296 fputc ('\n', file);
299 /* Dump comment delimiters. */
300 pair = m4_get_syntax_comments (M4SYNTAX);
301 if (strcmp (pair->str1, DEF_BCOMM) || strcmp (pair->str2, DEF_ECOMM))
303 xfprintf (file, "C%zu,%zu\n", pair->len1, pair->len2);
304 produce_mem_dump (file, pair->str1, pair->len1);
305 fputc ('\n', file);
306 produce_mem_dump (file, pair->str2, pair->len2);
307 fputc ('\n', file);
310 /* Dump regular expression syntax. */
311 produce_resyntax_dump (context, file);
313 /* Dump syntax table. */
314 str = "I@WLBOD${}SA(),RE";
315 while (*str)
316 produce_syntax_dump (file, M4SYNTAX, *str++);
318 /* Dump debugmode state. */
319 produce_debugmode_state (file, m4_get_debug_level_opt (context));
321 /* Dump all loaded modules. */
322 produce_module_dump (file, m4__module_next (NULL));
324 /* Dump all symbols. */
325 produce_symbol_dump (context, file, M4SYMTAB);
327 /* Let diversions be issued from output.c module, its cleaner to have this
328 piece of code there. */
329 m4_freeze_diversions (context, file);
331 /* All done. */
333 fputs ("# End of frozen state file\n", file);
334 if (close_stream (file) != 0)
335 m4_error (context, EXIT_FAILURE, errno, NULL,
336 _("unable to create frozen state"));
339 /* Issue a message saying that some character is an EXPECTED character. */
340 static void
341 issue_expect_message (m4 *context, int expected)
343 if (expected == '\n')
344 m4_error (context, EXIT_FAILURE, 0, NULL,
345 _("expecting line feed in frozen file"));
346 else
347 m4_error (context, EXIT_FAILURE, 0, NULL,
348 _("expecting character `%c' in frozen file"), expected);
352 /* Reload frozen state. */
354 /* Read the next character from the IN stream. Various escape
355 sequences are converted, and returned. EOF is returned if the end
356 of file is reached whilst reading the character, or on an
357 unrecognized escape sequence. */
359 static int
360 decode_char (m4 *context, FILE *in, bool *advance_line)
362 int ch = getc (in);
363 int next;
364 int value = 0;
366 if (*advance_line)
368 m4_set_current_line (context, m4_get_current_line (context) + 1);
369 *advance_line = false;
372 while (ch == '\\')
374 ch = getc (in);
375 switch (ch)
377 case 'a': return '\a';
378 case 'b': return '\b';
379 case 'f': return '\f';
380 case 'n': return '\n';
381 case 'r': return '\r';
382 case 't': return '\t';
383 case 'v': return '\v';
384 case '\\': return '\\';
386 case '\n':
387 ch = getc (in);
388 m4_set_current_line (context, m4_get_current_line (context) + 1);
389 continue;
391 case 'x': case 'X':
392 next = getc (in);
393 if (next >= '0' && next <= '9')
394 ch = (next - '0') * 16;
395 else if (next >= 'a' && next <= 'f')
396 ch = (next - 'a' + 10) * 16;
397 else if (next >= 'A' && next <= 'F')
398 ch = (next - 'A' + 10) * 16;
399 else
400 return EOF;
401 next = getc (in);
402 if (next >= '0' && next <= '9')
403 ch += next - '0';
404 else if (next >= 'a' && next <= 'f')
405 ch += next - 'a' + 10;
406 else if (next >= 'A' && next <= 'F')
407 ch += next - 'A' + 10;
408 else
409 return EOF;
410 return ch;
411 case '0': case '1': case '2': case '3':
412 value = ch - '0';
413 ch = getc (in);
414 /* fall through */
415 case '4': case '5': case '6': case '7':
416 if (ch >= '0' && ch <= '7')
418 value = value * 8 + ch - '0';
419 ch = getc (in);
421 else
423 ungetc (ch, in);
424 return value;
426 if (ch >= '0' && ch <= '7')
427 value = value * 8 + ch - '0';
428 else
429 ungetc (ch, in);
430 return value;
432 default:
433 return EOF;
437 if (ch == '\n')
438 *advance_line = true;
439 return ch;
443 /* Reload state from the given file NAME. We are seeking speed,
444 here. */
446 void
447 reload_frozen_state (m4 *context, const char *name)
449 FILE *file;
450 int version;
451 int character;
452 int operation;
453 char syntax;
454 char *string[3];
455 size_t allocated[3];
456 int number[3] = {0};
457 bool advance_line = true;
459 #define GET_CHARACTER \
460 do \
462 if (advance_line) \
464 m4_set_current_line (context, \
465 m4_get_current_line (context) + 1); \
466 advance_line = false; \
468 character = getc (file); \
469 if (character == '\n') \
470 advance_line = true; \
472 while (0)
474 #define GET_NUMBER(Number, AllowNeg) \
475 do \
477 unsigned int n = 0; \
478 while (isdigit (character) && n <= INT_MAX / 10) \
480 n = 10 * n + character - '0'; \
481 GET_CHARACTER; \
483 if (((AllowNeg) ? INT_MIN: INT_MAX) < n \
484 || isdigit (character)) \
485 m4_error (context, EXIT_FAILURE, 0, NULL, \
486 _("integer overflow in frozen file")); \
487 (Number) = n; \
489 while (0)
491 #define GET_STRING(File, Buf, BufSize, StrLen, UseChar) \
492 do \
494 size_t len = (StrLen); \
495 char *p; \
496 int ch; \
497 if (UseChar) \
499 ungetc (character, File); \
500 if (advance_line) \
502 assert (character == '\n'); \
503 advance_line = false; \
506 CHECK_ALLOCATION ((Buf), (BufSize), len); \
507 p = (Buf); \
508 while (len-- > 0) \
510 ch = (version > 1 \
511 ? decode_char (context, File, &advance_line) \
512 : getc (File)); \
513 if (ch == EOF) \
514 m4_error (context, EXIT_FAILURE, 0, NULL, \
515 _("premature end of frozen file")); \
516 *p++ = ch; \
518 *p = '\0'; \
519 GET_CHARACTER; \
520 while (version > 1 && character == '\\') \
522 GET_CHARACTER; \
523 VALIDATE ('\n'); \
524 GET_CHARACTER; \
527 while (0)
529 #define VALIDATE(Expected) \
530 do \
532 if (character != (Expected)) \
533 issue_expect_message (context, (Expected)); \
535 while (0)
537 #define CHECK_ALLOCATION(Where, Allocated, Needed) \
538 do \
540 if ((Needed) + 1 > (Allocated)) \
542 free (Where); \
543 (Allocated) = (Needed) + 1; \
544 (Where) = xcharalloc (Allocated); \
547 while (0)
549 /* Skip comments (`#' at beginning of line) and blank lines, setting
550 character to the next directive or to EOF. */
552 #define GET_DIRECTIVE \
553 do \
555 GET_CHARACTER; \
556 if (character == '#') \
558 while (character != EOF && character != '\n') \
559 GET_CHARACTER; \
560 VALIDATE ('\n'); \
563 while (character == '\n')
565 file = m4_path_search (context, name, (char **)NULL);
566 if (file == NULL)
567 m4_error (context, EXIT_FAILURE, errno, NULL, _("cannot open `%s'"), name);
568 m4_set_current_file (context, name);
570 allocated[0] = 100;
571 string[0] = xcharalloc (allocated[0]);
572 allocated[1] = 100;
573 string[1] = xcharalloc (allocated[1]);
574 allocated[2] = 100;
575 string[2] = xcharalloc (allocated[2]);
577 /* Validate format version. Accept both `1' (m4 1.3 and 1.4.x) and
578 `2' (m4 2.0). */
579 GET_DIRECTIVE;
580 VALIDATE ('V');
581 GET_CHARACTER;
582 GET_NUMBER (version, false);
583 switch (version)
585 case 2:
586 break;
587 case 1:
588 m4__module_open (context, "m4", NULL);
589 if (m4_get_posixly_correct_opt (context))
590 m4__module_open (context, "traditional", NULL);
591 else
592 m4__module_open (context, "gnu", NULL);
593 /* Disable { and } categories, since ${11} was not supported in
594 1.4.x. */
595 m4_set_syntax (M4SYNTAX, 'O', '+', "{}", 2);
596 break;
597 default:
598 if (version > 2)
599 m4_error (context, EXIT_MISMATCH, 0, NULL,
600 _("frozen file version %d greater than max supported of 2"),
601 version);
602 else
603 m4_error (context, EXIT_FAILURE, 0, NULL,
604 _("ill-formed frozen file, version directive expected"));
606 VALIDATE ('\n');
608 GET_DIRECTIVE;
609 while (character != EOF)
611 switch (character)
613 default:
614 m4_error (context, EXIT_FAILURE, 0, NULL,
615 _("ill-formed frozen file, unknown directive %c"),
616 character);
618 case 'd':
619 /* Set debugmode flags. */
620 if (version < 2)
622 /* 'd' operator is not supported in format version 1. */
623 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
624 ill-formed frozen file, version 2 directive `%c' encountered"), 'd');
627 GET_CHARACTER;
628 GET_NUMBER (number[0], false);
629 VALIDATE ('\n');
630 GET_STRING (file, string[0], allocated[0], number[0], false);
631 VALIDATE ('\n');
633 if (m4_debug_decode (context, string[0]) < 0)
634 m4_error (context, EXIT_FAILURE, 0, NULL,
635 _("unknown debug mode `%s'"),
636 quotearg_style_mem (locale_quoting_style, string[0],
637 number[0]));
638 break;
640 case 'F':
641 GET_CHARACTER;
643 /* Get string lengths. */
645 GET_NUMBER (number[0], false);
646 VALIDATE (',');
647 GET_CHARACTER;
648 GET_NUMBER (number[1], false);
650 if (character == ',')
652 if (version > 1)
654 /* 'F' operator accepts an optional third argument for
655 format versions 2 or later. */
656 GET_CHARACTER;
657 GET_NUMBER (number[2], false);
659 else
660 /* 3 argument 'F' operations are invalid for format
661 version 1. */
662 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
663 ill-formed frozen file, version 2 directive `%c' encountered"), 'F');
665 else
667 number[2] = 0;
670 VALIDATE ('\n');
673 /* Get string contents. */
675 GET_STRING (file, string[0], allocated[0], number[0], false);
676 if (version > 1)
678 VALIDATE ('\n');
679 GET_CHARACTER;
681 GET_STRING (file, string[1], allocated[1], number[1], true);
682 if (version > 1 && number[2])
684 VALIDATE ('\n');
685 GET_CHARACTER;
687 GET_STRING (file, string[2], allocated[2], number[2], true);
688 VALIDATE ('\n');
690 /* Enter a macro having a builtin function as a definition. */
692 m4_module *module = NULL;
693 m4_symbol_value *token;
695 if (number[2] > 0)
696 module = m4__module_find (string[2]);
697 token = m4_builtin_find_by_name (module, string[1]);
699 if (token == NULL)
701 token = (m4_symbol_value *) xzalloc (sizeof *token);
702 m4_set_symbol_value_placeholder (token, xstrdup (string[1]));
703 VALUE_MODULE (token) = module;
704 VALUE_MIN_ARGS (token) = 0;
705 VALUE_MAX_ARGS (token) = -1;
707 m4_symbol_pushdef (M4SYMTAB, string[0], number[0], token);
709 break;
711 case 'M':
713 /* Load a module, but *without* perturbing the symbol table.
714 Note that any expansion from loading the module which would
715 have been seen when loading it originally is discarded
716 when loading it from a frozen file. */
718 if (version < 2)
720 /* 'M' operator is not supported in format version 1. */
721 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
722 ill-formed frozen file, version 2 directive `%c' encountered"), 'M');
725 GET_CHARACTER;
726 GET_NUMBER (number[0], false);
727 VALIDATE ('\n');
728 GET_STRING (file, string[0], allocated[0], number[0], false);
729 VALIDATE ('\n');
731 m4__module_open (context, string[0], NULL);
733 break;
735 case 'R':
737 if (version < 2)
739 /* 'R' 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"), 'R');
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 m4_set_regexp_syntax_opt (context,
751 m4_regexp_syntax_encode (string[0]));
752 if (m4_get_regexp_syntax_opt (context) < 0)
754 m4_error (context, EXIT_FAILURE, 0, NULL,
755 _("unknown regexp syntax code `%s'"),
756 quotearg_style_mem (locale_quoting_style, string[0],
757 number[0]));
760 break;
762 case 'S':
764 if (version < 2)
766 /* 'S' operator is not supported in format version 1. */
767 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
768 ill-formed frozen file, version 2 directive `%c' encountered"), 'S');
771 GET_CHARACTER;
772 syntax = character;
773 GET_CHARACTER;
774 GET_NUMBER (number[0], false);
775 VALIDATE ('\n');
776 GET_STRING (file, string[0], allocated[0], number[0], false);
778 /* Syntax under M4_SYNTAX_MASKS is handled specially; all
779 other characters are additive. */
780 if ((m4_set_syntax (M4SYNTAX, syntax,
781 (m4_syntax_code (syntax) & M4_SYNTAX_MASKS
782 ? '=' : '+'), string[0], number[0]) < 0)
783 && (syntax != '\0'))
785 m4_error (context, 0, 0, NULL,
786 _("undefined syntax code %c"), syntax);
788 break;
790 case 't':
791 /* Trace a macro name. */
792 if (version < 2)
794 /* 't' operator is not supported in format version 1. */
795 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
796 ill-formed frozen file, version 2 directive `%c' encountered"), 't');
799 GET_CHARACTER;
800 GET_NUMBER (number[0], false);
801 VALIDATE ('\n');
802 GET_STRING (file, string[0], allocated[0], number[0], false);
803 VALIDATE ('\n');
805 m4_set_symbol_name_traced (M4SYMTAB, string[0], number[0], true);
807 break;
809 case 'C':
810 case 'D':
811 case 'Q':
812 operation = character;
813 GET_CHARACTER;
815 /* Get string lengths. */
817 if (operation == 'D' && character == '-')
819 /* Accept a negative diversion number. */
820 GET_CHARACTER;
821 GET_NUMBER (number[0], true);
822 number[0] = -number[0];
824 else
825 GET_NUMBER (number[0], false);
826 VALIDATE (',');
827 GET_CHARACTER;
828 GET_NUMBER (number[1], false);
829 VALIDATE ('\n');
831 /* Get string contents. */
832 if (operation != 'D')
834 GET_STRING (file, string[0], allocated[0], number[0], false);
835 if (version > 1)
837 VALIDATE ('\n');
838 GET_CHARACTER;
841 else
842 GET_CHARACTER;
843 GET_STRING (file, string[1], allocated[1], number[1], true);
844 VALIDATE ('\n');
846 /* Act according to operation letter. */
848 switch (operation)
850 case 'C':
852 /* Change comment strings. */
854 m4_set_comment (M4SYNTAX, string[0], number[0], string[1],
855 number[1]);
856 break;
858 case 'D':
860 /* Select a diversion and add a string to it. */
862 m4_make_diversion (context, number[0]);
863 if (number[1] > 0)
864 m4_output_text (context, string[1], number[1]);
865 break;
867 case 'Q':
869 /* Change quote strings. */
871 m4_set_quotes (M4SYNTAX, string[0], number[0], string[1],
872 number[1]);
873 break;
875 default:
877 /* Cannot happen. */
879 break;
881 break;
883 case 'T':
884 GET_CHARACTER;
886 /* Get string lengths. */
888 GET_NUMBER (number[0], false);
889 VALIDATE (',');
890 GET_CHARACTER;
891 GET_NUMBER (number[1], false);
893 if (character == ',')
895 if (version > 1)
897 /* 'T' operator accepts an optional third argument for
898 format versions 2 or later. */
899 GET_CHARACTER;
900 GET_NUMBER (number[2], false);
902 else
904 /* 3 argument 'T' operations are invalid for format
905 version 1. */
906 m4_error (context, EXIT_FAILURE, 0, NULL, _("\
907 ill-formed frozen file, version 2 directive `%c' encountered"), 'T');
910 else
911 number[2] = 0;
913 VALIDATE ('\n');
915 /* Get string contents. */
916 GET_STRING (file, string[0], allocated[0], number[0], false);
917 if (version > 1)
919 VALIDATE ('\n');
920 GET_CHARACTER;
922 GET_STRING (file, string[1], allocated[1], number[1], true);
923 if (version > 1 && number[2])
925 VALIDATE ('\n');
926 GET_CHARACTER;
928 GET_STRING (file, string[2], allocated[2], number[2], true);
929 VALIDATE ('\n');
931 /* Enter a macro having an expansion text as a definition. */
933 m4_symbol_value *token;
934 m4_module *module = NULL;
936 token = (m4_symbol_value *) xzalloc (sizeof *token);
937 if (number[2] > 0)
938 module = m4__module_find (string[2]);
940 m4_set_symbol_value_text (token, xmemdup0 (string[1], number[1]),
941 number[1], 0);
942 VALUE_MODULE (token) = module;
943 VALUE_MAX_ARGS (token) = -1;
945 m4_symbol_pushdef (M4SYMTAB, string[0], number[0], token);
947 break;
950 GET_DIRECTIVE;
953 free (string[0]);
954 free (string[1]);
955 free (string[2]);
956 if (close_stream (file) != 0)
957 m4_error (context, EXIT_FAILURE, errno, NULL,
958 _("unable to read frozen state"));
959 m4_set_current_file (context, NULL);
960 m4_set_current_line (context, 0);
962 #undef GET_STRING
963 #undef GET_CHARACTER
964 #undef GET_NUMBER
965 #undef VALIDATE
966 #undef CHECK_ALLOCATION
967 #undef GET_DIRECTIVE