diff options
Diffstat (limited to 'src/freeze.c')
-rw-r--r-- | src/freeze.c | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/src/freeze.c b/src/freeze.c new file mode 100644 index 0000000..bf47376 --- /dev/null +++ b/src/freeze.c @@ -0,0 +1,398 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989-1994, 2006-2011 Free Software Foundation, Inc. + + This file is part of GNU M4. + + GNU M4 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + GNU M4 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* This module handles frozen files. */ + +#include "m4.h" + +/*-------------------------------------------------------------------. +| Destructively reverse a symbol list and return the reversed list. | +`-------------------------------------------------------------------*/ + +static symbol * +reverse_symbol_list (symbol *sym) +{ + symbol *result; + symbol *next; + + result = NULL; + while (sym) + { + next = SYMBOL_NEXT (sym); + SYMBOL_NEXT (sym) = result; + result = sym; + sym = next; + } + return result; +} + +/*------------------------------------------------. +| Produce a frozen state to the given file NAME. | +`------------------------------------------------*/ + +void +produce_frozen_state (const char *name) +{ + FILE *file; + size_t h; + symbol *sym; + const builtin *bp; + + file = fopen (name, O_BINARY ? "wb" : "w"); + if (!file) + { + M4ERROR ((EXIT_FAILURE, errno, "cannot open `%s'", name)); + return; + } + + /* Write a recognizable header. */ + + xfprintf (file, "# This is a frozen state file generated by %s\n", + PACKAGE_STRING); + xfprintf (file, "V1\n"); + + /* Dump quote delimiters. */ + + if (strcmp (lquote.string, DEF_LQUOTE) || strcmp (rquote.string, DEF_RQUOTE)) + { + xfprintf (file, "Q%d,%d\n", (int) lquote.length, (int) rquote.length); + fputs (lquote.string, file); + fputs (rquote.string, file); + fputc ('\n', file); + } + + /* Dump comment delimiters. */ + + if (strcmp (bcomm.string, DEF_BCOMM) || strcmp (ecomm.string, DEF_ECOMM)) + { + xfprintf (file, "C%d,%d\n", (int) bcomm.length, (int) ecomm.length); + fputs (bcomm.string, file); + fputs (ecomm.string, file); + fputc ('\n', file); + } + + /* Dump all symbols. */ + + for (h = 0; h < hash_table_size; h++) + { + + /* Process all entries in one bucket, from the last to the first. + This order ensures that, at reload time, pushdef's will be + executed with the oldest definitions first. */ + + symtab[h] = reverse_symbol_list (symtab[h]); + for (sym = symtab[h]; sym; sym = SYMBOL_NEXT (sym)) + { + switch (SYMBOL_TYPE (sym)) + { + case TOKEN_TEXT: + xfprintf (file, "T%d,%d\n", + (int) strlen (SYMBOL_NAME (sym)), + (int) strlen (SYMBOL_TEXT (sym))); + fputs (SYMBOL_NAME (sym), file); + fputs (SYMBOL_TEXT (sym), file); + fputc ('\n', file); + break; + + case TOKEN_FUNC: + bp = find_builtin_by_addr (SYMBOL_FUNC (sym)); + if (bp == NULL) + { + M4ERROR ((warning_status, 0, "\ +INTERNAL ERROR: builtin not found in builtin table!")); + abort (); + } + xfprintf (file, "F%d,%d\n", + (int) strlen (SYMBOL_NAME (sym)), + (int) strlen (bp->name)); + fputs (SYMBOL_NAME (sym), file); + fputs (bp->name, file); + fputc ('\n', file); + break; + + case TOKEN_VOID: + /* Ignore placeholder tokens that exist due to traceon. */ + break; + + default: + M4ERROR ((warning_status, 0, "\ +INTERNAL ERROR: bad token data type in freeze_one_symbol ()")); + abort (); + break; + } + } + + /* Reverse the bucket once more, putting it back as it was. */ + + symtab[h] = reverse_symbol_list (symtab[h]); + } + + /* Let diversions be issued from output.c module, its cleaner to have this + piece of code there. */ + + freeze_diversions (file); + + /* All done. */ + + fputs ("# End of frozen state file\n", file); + if (close_stream (file) != 0) + M4ERROR ((EXIT_FAILURE, errno, "unable to create frozen state")); +} + +/*----------------------------------------------------------------------. +| Issue a message saying that some character is an EXPECTED character. | +`----------------------------------------------------------------------*/ + +static void +issue_expect_message (int expected) +{ + if (expected == '\n') + M4ERROR ((EXIT_FAILURE, 0, "expecting line feed in frozen file")); + else + M4ERROR ((EXIT_FAILURE, 0, "expecting character `%c' in frozen file", + expected)); +} + +/*-------------------------------------------------. +| Reload a frozen state from the given file NAME. | +`-------------------------------------------------*/ + +/* We are seeking speed, here. */ + +void +reload_frozen_state (const char *name) +{ + FILE *file; + int character; + int operation; + char *string[2]; + int allocated[2]; + int number[2]; + const builtin *bp; + bool advance_line = true; + +#define GET_CHARACTER \ + do \ + { \ + if (advance_line) \ + { \ + current_line++; \ + advance_line = false; \ + } \ + (character = getc (file)); \ + if (character == '\n') \ + advance_line = true; \ + } \ + while (0) + +#define GET_NUMBER(Number, AllowNeg) \ + do \ + { \ + unsigned int n = 0; \ + while (isdigit (character) && n <= INT_MAX / 10U) \ + { \ + n = 10 * n + character - '0'; \ + GET_CHARACTER; \ + } \ + if (((AllowNeg) ? INT_MIN : INT_MAX) + 0U < n \ + || isdigit (character)) \ + m4_error (EXIT_FAILURE, 0, \ + _("integer overflow in frozen file")); \ + (Number) = n; \ + } \ + while (0) + +#define VALIDATE(Expected) \ + do \ + { \ + if (character != (Expected)) \ + issue_expect_message (Expected); \ + } \ + while (0) + + /* Skip comments (`#' at beginning of line) and blank lines, setting + character to the next directive or to EOF. */ + +#define GET_DIRECTIVE \ + do \ + { \ + GET_CHARACTER; \ + if (character == '#') \ + { \ + while (character != EOF && character != '\n') \ + GET_CHARACTER; \ + VALIDATE ('\n'); \ + } \ + } \ + while (character == '\n') + +#define GET_STRING(i) \ + do \ + { \ + void *tmp; \ + char *p; \ + if (number[(i)] + 1 > allocated[(i)]) \ + { \ + free (string[(i)]); \ + allocated[(i)] = number[(i)] + 1; \ + string[(i)] = xcharalloc ((size_t) allocated[(i)]); \ + } \ + if (number[(i)] > 0 \ + && !fread (string[(i)], (size_t) number[(i)], 1, file)) \ + m4_error (EXIT_FAILURE, 0, \ + _("premature end of frozen file")); \ + string[(i)][number[(i)]] = '\0'; \ + p = string[(i)]; \ + while ((tmp = memchr(p, '\n', number[(i)] - (p - string[(i)])))) \ + { \ + current_line++; \ + p = (char *) tmp + 1; \ + } \ + } \ + while (0) + + file = m4_path_search (name, NULL); + if (file == NULL) + M4ERROR ((EXIT_FAILURE, errno, "cannot open %s", name)); + current_file = name; + + allocated[0] = 100; + string[0] = xcharalloc ((size_t) allocated[0]); + allocated[1] = 100; + string[1] = xcharalloc ((size_t) allocated[1]); + + /* Validate format version. Only `1' is acceptable for now. */ + GET_DIRECTIVE; + VALIDATE ('V'); + GET_CHARACTER; + GET_NUMBER (number[0], false); + if (number[0] > 1) + M4ERROR ((EXIT_MISMATCH, 0, + "frozen file version %d greater than max supported of 1", + number[0])); + else if (number[0] < 1) + M4ERROR ((EXIT_FAILURE, 0, + "ill-formed frozen file, version directive expected")); + VALIDATE ('\n'); + + GET_DIRECTIVE; + while (character != EOF) + { + switch (character) + { + default: + M4ERROR ((EXIT_FAILURE, 0, "ill-formed frozen file")); + + case 'C': + case 'D': + case 'F': + case 'T': + case 'Q': + operation = character; + GET_CHARACTER; + + /* Get string lengths. Accept a negative diversion number. */ + + if (operation == 'D' && character == '-') + { + GET_CHARACTER; + GET_NUMBER (number[0], true); + number[0] = -number[0]; + } + else + GET_NUMBER (number[0], false); + VALIDATE (','); + GET_CHARACTER; + GET_NUMBER (number[1], false); + VALIDATE ('\n'); + + if (operation != 'D') + GET_STRING (0); + GET_STRING (1); + GET_CHARACTER; + VALIDATE ('\n'); + + /* Act according to operation letter. */ + + switch (operation) + { + case 'C': + + /* Change comment strings. */ + + set_comment (string[0], string[1]); + break; + + case 'D': + + /* Select a diversion and add a string to it. */ + + make_diversion (number[0]); + if (number[1] > 0) + output_text (string[1], number[1]); + break; + + case 'F': + + /* Enter a macro having a builtin function as a definition. */ + + bp = find_builtin_by_name (string[1]); + define_builtin (string[0], bp, SYMBOL_PUSHDEF); + break; + + case 'T': + + /* Enter a macro having an expansion text as a definition. */ + + define_user_macro (string[0], string[1], SYMBOL_PUSHDEF); + break; + + case 'Q': + + /* Change quote strings. */ + + set_quotes (string[0], string[1]); + break; + + default: + + /* Cannot happen. */ + + break; + } + break; + + } + GET_DIRECTIVE; + } + + free (string[0]); + free (string[1]); + if (close_stream (file) != 0) + m4_error (EXIT_FAILURE, errno, _("unable to read frozen state")); + current_file = NULL; + current_line = 0; + +#undef GET_CHARACTER +#undef GET_DIRECTIVE +#undef GET_NUMBER +#undef VALIDATE +#undef GET_STRING +} |