1 /* GNU m4 -- A simple macro processor
3 Copyright (C) 1989-1994, 2006-2011 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. */
25 /*-------------------------------------------------------------------.
26 | Destructively reverse a symbol list and return the reversed list. |
27 `-------------------------------------------------------------------*/
30 reverse_symbol_list (symbol *sym)
38 next = SYMBOL_NEXT (sym);
39 SYMBOL_NEXT (sym) = result;
46 /*------------------------------------------------.
47 | Produce a frozen state to the given file NAME. |
48 `------------------------------------------------*/
51 produce_frozen_state (const char *name)
58 file = fopen (name, O_BINARY ? "wb" : "w");
61 M4ERROR ((EXIT_FAILURE, errno, "cannot open `%s'", name));
65 /* Write a recognizable header. */
67 xfprintf (file, "# This is a frozen state file generated by %s\n",
69 xfprintf (file, "V1\n");
71 /* Dump quote delimiters. */
73 if (strcmp (lquote.string, DEF_LQUOTE) || strcmp (rquote.string, DEF_RQUOTE))
75 xfprintf (file, "Q%d,%d\n", (int) lquote.length, (int) rquote.length);
76 fputs (lquote.string, file);
77 fputs (rquote.string, file);
81 /* Dump comment delimiters. */
83 if (strcmp (bcomm.string, DEF_BCOMM) || strcmp (ecomm.string, DEF_ECOMM))
85 xfprintf (file, "C%d,%d\n", (int) bcomm.length, (int) ecomm.length);
86 fputs (bcomm.string, file);
87 fputs (ecomm.string, file);
91 /* Dump all symbols. */
93 for (h = 0; h < hash_table_size; h++)
96 /* Process all entries in one bucket, from the last to the first.
97 This order ensures that, at reload time, pushdef's will be
98 executed with the oldest definitions first. */
100 symtab[h] = reverse_symbol_list (symtab[h]);
101 for (sym = symtab[h]; sym; sym = SYMBOL_NEXT (sym))
103 switch (SYMBOL_TYPE (sym))
106 xfprintf (file, "T%d,%d\n",
107 (int) strlen (SYMBOL_NAME (sym)),
108 (int) strlen (SYMBOL_TEXT (sym)));
109 fputs (SYMBOL_NAME (sym), file);
110 fputs (SYMBOL_TEXT (sym), file);
115 bp = find_builtin_by_addr (SYMBOL_FUNC (sym));
118 M4ERROR ((warning_status, 0, "\
119 INTERNAL ERROR: builtin not found in builtin table!"));
122 xfprintf (file, "F%d,%d\n",
123 (int) strlen (SYMBOL_NAME (sym)),
124 (int) strlen (bp->name));
125 fputs (SYMBOL_NAME (sym), file);
126 fputs (bp->name, file);
131 /* Ignore placeholder tokens that exist due to traceon. */
135 M4ERROR ((warning_status, 0, "\
136 INTERNAL ERROR: bad token data type in freeze_one_symbol ()"));
142 /* Reverse the bucket once more, putting it back as it was. */
144 symtab[h] = reverse_symbol_list (symtab[h]);
147 /* Let diversions be issued from output.c module, its cleaner to have this
148 piece of code there. */
150 freeze_diversions (file);
154 fputs ("# End of frozen state file\n", file);
155 if (close_stream (file) != 0)
156 M4ERROR ((EXIT_FAILURE, errno, "unable to create frozen state"));
159 /*----------------------------------------------------------------------.
160 | Issue a message saying that some character is an EXPECTED character. |
161 `----------------------------------------------------------------------*/
164 issue_expect_message (int expected)
166 if (expected == '\n')
167 M4ERROR ((EXIT_FAILURE, 0, "expecting line feed in frozen file"));
169 M4ERROR ((EXIT_FAILURE, 0, "expecting character `%c' in frozen file",
173 /*-------------------------------------------------.
174 | Reload a frozen state from the given file NAME. |
175 `-------------------------------------------------*/
177 /* We are seeking speed, here. */
180 reload_frozen_state (const char *name)
189 bool advance_line = true;
191 #define GET_CHARACTER \
197 advance_line = false; \
199 (character = getc (file)); \
200 if (character == '\n') \
201 advance_line = true; \
205 #define GET_NUMBER(Number, AllowNeg) \
208 unsigned int n = 0; \
209 while (isdigit (character) && n <= INT_MAX / 10U) \
211 n = 10 * n + character - '0'; \
214 if (((AllowNeg) ? INT_MIN : INT_MAX) + 0U < n \
215 || isdigit (character)) \
216 m4_error (EXIT_FAILURE, 0, \
217 _("integer overflow in frozen file")); \
222 #define VALIDATE(Expected) \
225 if (character != (Expected)) \
226 issue_expect_message (Expected); \
230 /* Skip comments (`#' at beginning of line) and blank lines, setting
231 character to the next directive or to EOF. */
233 #define GET_DIRECTIVE \
237 if (character == '#') \
239 while (character != EOF && character != '\n') \
244 while (character == '\n')
246 #define GET_STRING(i) \
251 if (number[(i)] + 1 > allocated[(i)]) \
253 free (string[(i)]); \
254 allocated[(i)] = number[(i)] + 1; \
255 string[(i)] = xcharalloc ((size_t) allocated[(i)]); \
257 if (number[(i)] > 0 \
258 && !fread (string[(i)], (size_t) number[(i)], 1, file)) \
259 m4_error (EXIT_FAILURE, 0, \
260 _("premature end of frozen file")); \
261 string[(i)][number[(i)]] = '\0'; \
263 while ((tmp = memchr(p, '\n', number[(i)] - (p - string[(i)])))) \
266 p = (char *) tmp + 1; \
271 file = m4_path_search (name, NULL);
273 M4ERROR ((EXIT_FAILURE, errno, "cannot open %s", name));
277 string[0] = xcharalloc ((size_t) allocated[0]);
279 string[1] = xcharalloc ((size_t) allocated[1]);
281 /* Validate format version. Only `1' is acceptable for now. */
285 GET_NUMBER (number[0], false);
287 M4ERROR ((EXIT_MISMATCH, 0,
288 "frozen file version %d greater than max supported of 1",
290 else if (number[0] < 1)
291 M4ERROR ((EXIT_FAILURE, 0,
292 "ill-formed frozen file, version directive expected"));
296 while (character != EOF)
301 M4ERROR ((EXIT_FAILURE, 0, "ill-formed frozen file"));
308 operation = character;
311 /* Get string lengths. Accept a negative diversion number. */
313 if (operation == 'D' && character == '-')
316 GET_NUMBER (number[0], true);
317 number[0] = -number[0];
320 GET_NUMBER (number[0], false);
323 GET_NUMBER (number[1], false);
326 if (operation != 'D')
332 /* Act according to operation letter. */
338 /* Change comment strings. */
340 set_comment (string[0], string[1]);
345 /* Select a diversion and add a string to it. */
347 make_diversion (number[0]);
349 output_text (string[1], number[1]);
354 /* Enter a macro having a builtin function as a definition. */
356 bp = find_builtin_by_name (string[1]);
357 define_builtin (string[0], bp, SYMBOL_PUSHDEF);
362 /* Enter a macro having an expansion text as a definition. */
364 define_user_macro (string[0], string[1], SYMBOL_PUSHDEF);
369 /* Change quote strings. */
371 set_quotes (string[0], string[1]);
388 if (close_stream (file) != 0)
389 m4_error (EXIT_FAILURE, errno, _("unable to read frozen state"));