bf4737612e3aa68c6e985f7f1eaed7fc22986af4
[platform/upstream/m4.git] / src / freeze.c
1 /* GNU m4 -- A simple macro processor
2
3    Copyright (C) 1989-1994, 2006-2011 Free Software Foundation, Inc.
4
5    This file is part of GNU M4.
6
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.
11
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.
16
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/>.
19 */
20
21 /* This module handles frozen files.  */
22
23 #include "m4.h"
24
25 /*-------------------------------------------------------------------.
26 | Destructively reverse a symbol list and return the reversed list.  |
27 `-------------------------------------------------------------------*/
28
29 static symbol *
30 reverse_symbol_list (symbol *sym)
31 {
32   symbol *result;
33   symbol *next;
34
35   result = NULL;
36   while (sym)
37     {
38       next = SYMBOL_NEXT (sym);
39       SYMBOL_NEXT (sym) = result;
40       result = sym;
41       sym = next;
42     }
43   return result;
44 }
45
46 /*------------------------------------------------.
47 | Produce a frozen state to the given file NAME.  |
48 `------------------------------------------------*/
49
50 void
51 produce_frozen_state (const char *name)
52 {
53   FILE *file;
54   size_t h;
55   symbol *sym;
56   const builtin *bp;
57
58   file = fopen (name, O_BINARY ? "wb" : "w");
59   if (!file)
60     {
61       M4ERROR ((EXIT_FAILURE, errno, "cannot open `%s'", name));
62       return;
63     }
64
65   /* Write a recognizable header.  */
66
67   xfprintf (file, "# This is a frozen state file generated by %s\n",
68            PACKAGE_STRING);
69   xfprintf (file, "V1\n");
70
71   /* Dump quote delimiters.  */
72
73   if (strcmp (lquote.string, DEF_LQUOTE) || strcmp (rquote.string, DEF_RQUOTE))
74     {
75       xfprintf (file, "Q%d,%d\n", (int) lquote.length, (int) rquote.length);
76       fputs (lquote.string, file);
77       fputs (rquote.string, file);
78       fputc ('\n', file);
79     }
80
81   /* Dump comment delimiters.  */
82
83   if (strcmp (bcomm.string, DEF_BCOMM) || strcmp (ecomm.string, DEF_ECOMM))
84     {
85       xfprintf (file, "C%d,%d\n", (int) bcomm.length, (int) ecomm.length);
86       fputs (bcomm.string, file);
87       fputs (ecomm.string, file);
88       fputc ('\n', file);
89     }
90
91   /* Dump all symbols.  */
92
93   for (h = 0; h < hash_table_size; h++)
94     {
95
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.  */
99
100       symtab[h] = reverse_symbol_list (symtab[h]);
101       for (sym = symtab[h]; sym; sym = SYMBOL_NEXT (sym))
102         {
103           switch (SYMBOL_TYPE (sym))
104             {
105             case TOKEN_TEXT:
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);
111               fputc ('\n', file);
112               break;
113
114             case TOKEN_FUNC:
115               bp = find_builtin_by_addr (SYMBOL_FUNC (sym));
116               if (bp == NULL)
117                 {
118                   M4ERROR ((warning_status, 0, "\
119 INTERNAL ERROR: builtin not found in builtin table!"));
120                   abort ();
121                 }
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);
127               fputc ('\n', file);
128               break;
129
130             case TOKEN_VOID:
131               /* Ignore placeholder tokens that exist due to traceon.  */
132               break;
133
134             default:
135               M4ERROR ((warning_status, 0, "\
136 INTERNAL ERROR: bad token data type in freeze_one_symbol ()"));
137               abort ();
138               break;
139             }
140         }
141
142       /* Reverse the bucket once more, putting it back as it was.  */
143
144       symtab[h] = reverse_symbol_list (symtab[h]);
145     }
146
147   /* Let diversions be issued from output.c module, its cleaner to have this
148      piece of code there.  */
149
150   freeze_diversions (file);
151
152   /* All done.  */
153
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"));
157 }
158
159 /*----------------------------------------------------------------------.
160 | Issue a message saying that some character is an EXPECTED character.  |
161 `----------------------------------------------------------------------*/
162
163 static void
164 issue_expect_message (int expected)
165 {
166   if (expected == '\n')
167     M4ERROR ((EXIT_FAILURE, 0, "expecting line feed in frozen file"));
168   else
169     M4ERROR ((EXIT_FAILURE, 0, "expecting character `%c' in frozen file",
170               expected));
171 }
172
173 /*-------------------------------------------------.
174 | Reload a frozen state from the given file NAME.  |
175 `-------------------------------------------------*/
176
177 /* We are seeking speed, here.  */
178
179 void
180 reload_frozen_state (const char *name)
181 {
182   FILE *file;
183   int character;
184   int operation;
185   char *string[2];
186   int allocated[2];
187   int number[2];
188   const builtin *bp;
189   bool advance_line = true;
190
191 #define GET_CHARACTER                                           \
192   do                                                            \
193     {                                                           \
194       if (advance_line)                                         \
195         {                                                       \
196           current_line++;                                       \
197           advance_line = false;                                 \
198         }                                                       \
199       (character = getc (file));                                \
200       if (character == '\n')                                    \
201         advance_line = true;                                    \
202     }                                                           \
203   while (0)
204
205 #define GET_NUMBER(Number, AllowNeg)                            \
206   do                                                            \
207     {                                                           \
208       unsigned int n = 0;                                       \
209       while (isdigit (character) && n <= INT_MAX / 10U)         \
210         {                                                       \
211           n = 10 * n + character - '0';                         \
212           GET_CHARACTER;                                        \
213         }                                                       \
214       if (((AllowNeg) ? INT_MIN : INT_MAX) + 0U < n             \
215           || isdigit (character))                               \
216         m4_error (EXIT_FAILURE, 0,                              \
217                   _("integer overflow in frozen file"));        \
218       (Number) = n;                                             \
219     }                                                           \
220   while (0)
221
222 #define VALIDATE(Expected)                                      \
223   do                                                            \
224     {                                                           \
225       if (character != (Expected))                              \
226         issue_expect_message (Expected);                        \
227     }                                                           \
228   while (0)
229
230   /* Skip comments (`#' at beginning of line) and blank lines, setting
231      character to the next directive or to EOF.  */
232
233 #define GET_DIRECTIVE                                           \
234   do                                                            \
235     {                                                           \
236       GET_CHARACTER;                                            \
237       if (character == '#')                                     \
238         {                                                       \
239           while (character != EOF && character != '\n')         \
240             GET_CHARACTER;                                      \
241           VALIDATE ('\n');                                      \
242         }                                                       \
243     }                                                           \
244   while (character == '\n')
245
246 #define GET_STRING(i)                                                   \
247   do                                                                    \
248     {                                                                   \
249       void *tmp;                                                        \
250       char *p;                                                          \
251       if (number[(i)] + 1 > allocated[(i)])                             \
252         {                                                               \
253           free (string[(i)]);                                           \
254           allocated[(i)] = number[(i)] + 1;                             \
255           string[(i)] = xcharalloc ((size_t) allocated[(i)]);           \
256         }                                                               \
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';                                  \
262       p = string[(i)];                                                  \
263       while ((tmp = memchr(p, '\n', number[(i)] - (p - string[(i)]))))  \
264         {                                                               \
265           current_line++;                                               \
266           p = (char *) tmp + 1;                                         \
267         }                                                               \
268     }                                                                   \
269   while (0)
270
271   file = m4_path_search (name, NULL);
272   if (file == NULL)
273     M4ERROR ((EXIT_FAILURE, errno, "cannot open %s", name));
274   current_file = name;
275
276   allocated[0] = 100;
277   string[0] = xcharalloc ((size_t) allocated[0]);
278   allocated[1] = 100;
279   string[1] = xcharalloc ((size_t) allocated[1]);
280
281   /* Validate format version.  Only `1' is acceptable for now.  */
282   GET_DIRECTIVE;
283   VALIDATE ('V');
284   GET_CHARACTER;
285   GET_NUMBER (number[0], false);
286   if (number[0] > 1)
287     M4ERROR ((EXIT_MISMATCH, 0,
288               "frozen file version %d greater than max supported of 1",
289               number[0]));
290   else if (number[0] < 1)
291     M4ERROR ((EXIT_FAILURE, 0,
292               "ill-formed frozen file, version directive expected"));
293   VALIDATE ('\n');
294
295   GET_DIRECTIVE;
296   while (character != EOF)
297     {
298       switch (character)
299         {
300         default:
301           M4ERROR ((EXIT_FAILURE, 0, "ill-formed frozen file"));
302
303         case 'C':
304         case 'D':
305         case 'F':
306         case 'T':
307         case 'Q':
308           operation = character;
309           GET_CHARACTER;
310
311           /* Get string lengths.  Accept a negative diversion number.  */
312
313           if (operation == 'D' && character == '-')
314             {
315               GET_CHARACTER;
316               GET_NUMBER (number[0], true);
317               number[0] = -number[0];
318             }
319           else
320             GET_NUMBER (number[0], false);
321           VALIDATE (',');
322           GET_CHARACTER;
323           GET_NUMBER (number[1], false);
324           VALIDATE ('\n');
325
326           if (operation != 'D')
327             GET_STRING (0);
328           GET_STRING (1);
329           GET_CHARACTER;
330           VALIDATE ('\n');
331
332           /* Act according to operation letter.  */
333
334           switch (operation)
335             {
336             case 'C':
337
338               /* Change comment strings.  */
339
340               set_comment (string[0], string[1]);
341               break;
342
343             case 'D':
344
345               /* Select a diversion and add a string to it.  */
346
347               make_diversion (number[0]);
348               if (number[1] > 0)
349                 output_text (string[1], number[1]);
350               break;
351
352             case 'F':
353
354               /* Enter a macro having a builtin function as a definition.  */
355
356               bp = find_builtin_by_name (string[1]);
357               define_builtin (string[0], bp, SYMBOL_PUSHDEF);
358               break;
359
360             case 'T':
361
362               /* Enter a macro having an expansion text as a definition.  */
363
364               define_user_macro (string[0], string[1], SYMBOL_PUSHDEF);
365               break;
366
367             case 'Q':
368
369               /* Change quote strings.  */
370
371               set_quotes (string[0], string[1]);
372               break;
373
374             default:
375
376               /* Cannot happen.  */
377
378               break;
379             }
380           break;
381
382         }
383       GET_DIRECTIVE;
384     }
385
386   free (string[0]);
387   free (string[1]);
388   if (close_stream (file) != 0)
389     m4_error (EXIT_FAILURE, errno, _("unable to read frozen state"));
390   current_file = NULL;
391   current_line = 0;
392
393 #undef GET_CHARACTER
394 #undef GET_DIRECTIVE
395 #undef GET_NUMBER
396 #undef VALIDATE
397 #undef GET_STRING
398 }