1 /* GNU m4 -- A simple macro processor
3 Copyright (C) 1989-1994, 2006-2014, 2016-2017, 2020-2021 Free Software
6 This file is part of GNU M4.
8 GNU M4 is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 GNU M4 is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <https://www.gnu.org/licenses/>.
22 /* This module handles frozen files. */
26 /*-------------------------------------------------------------------.
27 | Destructively reverse a symbol list and return the reversed list. |
28 `-------------------------------------------------------------------*/
31 reverse_symbol_list (symbol *sym)
39 next = SYMBOL_STACK (sym);
40 SYMBOL_STACK (sym) = result;
48 freeze_symbol (symbol *sym, void *arg)
54 /* Process all entries in one stack, from the last to the first.
55 This order ensures that, at reload time, pushdef's will be
56 executed with the oldest definitions first. */
58 s = reverse_symbol_list (s);
59 for (sym = s; sym; sym = SYMBOL_STACK (sym))
61 switch (SYMBOL_TYPE (sym))
64 xfprintf (file, "T%d,%d\n",
65 (int) strlen (SYMBOL_NAME (sym)),
66 (int) strlen (SYMBOL_TEXT (sym)));
67 fputs (SYMBOL_NAME (sym), file);
68 fputs (SYMBOL_TEXT (sym), file);
73 bp = find_builtin_by_addr (SYMBOL_FUNC (sym));
76 M4ERROR ((warning_status, 0, "\
77 INTERNAL ERROR: builtin not found in builtin table!"));
80 xfprintf (file, "F%d,%d\n",
81 (int) strlen (SYMBOL_NAME (sym)),
82 (int) strlen (bp->name));
83 fputs (SYMBOL_NAME (sym), file);
84 fputs (bp->name, file);
89 /* Ignore placeholder tokens that exist due to traceon. */
93 M4ERROR ((warning_status, 0, "\
94 INTERNAL ERROR: bad token data type in freeze_symbol ()"));
100 /* Reverse the stack once more, putting it back as it was. */
101 reverse_symbol_list (s);
104 /*------------------------------------------------.
105 | Produce a frozen state to the given file NAME. |
106 `------------------------------------------------*/
109 produce_frozen_state (const char *name)
113 file = fopen (name, O_BINARY ? "wbe" : "we");
115 m4_failure (errno, _("cannot open `%s'"), name);
117 /* Write a recognizable header. */
119 xfprintf (file, "# This is a frozen state file generated by %s\n",
121 xfprintf (file, "V1\n");
123 /* Dump quote delimiters. */
125 if (strcmp (lquote.string, DEF_LQUOTE) || strcmp (rquote.string, DEF_RQUOTE))
127 xfprintf (file, "Q%d,%d\n", (int) lquote.length, (int) rquote.length);
128 fputs (lquote.string, file);
129 fputs (rquote.string, file);
133 /* Dump comment delimiters. */
135 if (strcmp (bcomm.string, DEF_BCOMM) || strcmp (ecomm.string, DEF_ECOMM))
137 xfprintf (file, "C%d,%d\n", (int) bcomm.length, (int) ecomm.length);
138 fputs (bcomm.string, file);
139 fputs (ecomm.string, file);
143 /* Dump all symbols. */
145 hack_all_symbols (freeze_symbol, file);
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 m4_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 m4_failure (0, _("expecting line feed in frozen file"));
169 m4_failure (0, _("expecting character `%c' in frozen file"), expected);
172 /*-------------------------------------------------.
173 | Reload a frozen state from the given file NAME. |
174 `-------------------------------------------------*/
176 /* We are seeking speed, here. */
179 reload_frozen_state (const char *name)
188 bool advance_line = true;
190 #define GET_CHARACTER \
196 advance_line = false; \
198 (character = getc (file)); \
199 if (character == '\n') \
200 advance_line = true; \
204 #define GET_NUMBER(Number, AllowNeg) \
207 unsigned int n = 0; \
208 while (c_isdigit (character) && n <= INT_MAX / 10U) \
210 n = 10 * n + character - '0'; \
213 if (((AllowNeg) ? INT_MIN : INT_MAX) + 0U < n \
214 || c_isdigit (character)) \
215 m4_failure (0, _("integer overflow in frozen file")); \
220 #define VALIDATE(Expected) \
223 if (character != (Expected)) \
224 issue_expect_message (Expected); \
228 /* Skip comments (`#' at beginning of line) and blank lines, setting
229 character to the next directive or to EOF. */
231 #define GET_DIRECTIVE \
235 if (character == '#') \
237 while (character != EOF && character != '\n') \
242 while (character == '\n')
244 #define GET_STRING(i) \
249 if (number[(i)] + 1 > allocated[(i)]) \
251 free (string[(i)]); \
252 allocated[(i)] = number[(i)] + 1; \
253 string[(i)] = xcharalloc ((size_t) allocated[(i)]); \
255 if (number[(i)] > 0 \
256 && !fread (string[(i)], (size_t) number[(i)], 1, file)) \
257 m4_failure (0, _("premature end of frozen file")); \
258 string[(i)][number[(i)]] = '\0'; \
260 while ((tmp = memchr(p, '\n', number[(i)] - (p - string[(i)])))) \
263 p = (char *) tmp + 1; \
268 file = m4_path_search (name, NULL);
270 m4_failure (errno, _("cannot open %s"), name);
274 string[0] = xcharalloc ((size_t) allocated[0]);
276 string[1] = xcharalloc ((size_t) allocated[1]);
278 /* Validate format version. Only `1' is acceptable for now. */
282 GET_NUMBER (number[0], false);
284 M4ERROR ((EXIT_MISMATCH, 0,
285 _("frozen file version %d greater than max supported of 1"),
287 else if (number[0] < 1)
288 m4_failure (0, _("ill-formed frozen file, version directive expected"));
292 while (character != EOF)
297 m4_failure (0, _("ill-formed frozen file"));
304 operation = character;
307 /* Get string lengths. Accept a negative diversion number. */
309 if (operation == 'D' && character == '-')
312 GET_NUMBER (number[0], true);
313 number[0] = -number[0];
316 GET_NUMBER (number[0], false);
319 GET_NUMBER (number[1], false);
322 if (operation != 'D')
328 /* Act according to operation letter. */
334 /* Change comment strings. */
336 set_comment (string[0], string[1]);
341 /* Select a diversion and add a string to it. */
343 make_diversion (number[0]);
345 output_text (string[1], number[1]);
350 /* Enter a macro having a builtin function as a definition. */
352 bp = find_builtin_by_name (string[1]);
353 define_builtin (string[0], bp, SYMBOL_PUSHDEF);
358 /* Enter a macro having an expansion text as a definition. */
360 define_user_macro (string[0], string[1], SYMBOL_PUSHDEF);
365 /* Change quote strings. */
367 set_quotes (string[0], string[1]);
384 if (close_stream (file) != 0)
385 m4_failure (errno, _("unable to read frozen state"));