/* mp4h -- A macro processor for HTML documents Copyright 2000-2002, Denis Barbier All rights reserved. This program 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 2, or (at your option) any later version. This program is a work based on GNU m4 version 1.4n. Below is the original copyright. */ /* GNU m4 -- A simple macro processor Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc. This program 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 2, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This module handles frozen files. */ #include "mp4h.h" static int lineno; /*-------------------------------------------------------------------. | 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; int h; symbol *sym; const builtin *bp; int number[2]; if (file = fopen (name, "w"), !file) { MP4HERROR ((warning_status, errno, name)); return; } /* Write a recognizable header. */ fprintf (file, "# This is a frozen state file generated by %s %s\n", PACKAGE, VERSION); fprintf (file, "V1\n"); /* Dump comment delimiters. */ if (strcmp (eolcomm.string, DEF_EOLCOMM)) { fprintf (file, "C%d\n", (int) eolcomm.length); fputs (eolcomm.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: if (*SYMBOL_NAME (sym) == '<') { fprintf (file, "A%d,%d\n", (int) strlen (SYMBOL_NAME (sym)) - 1, (int) strlen (SYMBOL_TEXT (sym))); fputs (SYMBOL_NAME (sym) + 1, file); } else { fprintf (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); if (*SYMBOL_NAME (sym) != '<') { fputc (SYMBOL_CONTAINER (sym) ? '1' : '0', file); fputc (SYMBOL_EXPAND_ARGS (sym) ? '1' : '0', file); fputc ('\n', file); number[0] = (SYMBOL_HOOK_BEGIN (sym) ? strlen (SYMBOL_HOOK_BEGIN (sym)) : 0); number[1] = (SYMBOL_HOOK_END (sym) ? strlen (SYMBOL_HOOK_END (sym)) : 0); fprintf (file, "%d,%d\n", number[0], number[1]); if (SYMBOL_HOOK_BEGIN (sym)) fputs (SYMBOL_HOOK_BEGIN (sym), file); if (SYMBOL_HOOK_END (sym)) fputs (SYMBOL_HOOK_END (sym), file); fputc ('\n', file); } break; case TOKEN_FUNC: bp = find_builtin_by_addr (SYMBOL_FUNC (sym)); if (bp == NULL) { MP4HERROR ((warning_status, 0, "\ INTERNAL ERROR: Built-in not found in builtin table!")); exit (1); } fprintf (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; default: MP4HERROR ((warning_status, 0, "\ INTERNAL ERROR: Bad token data type in freeze_one_symbol ()")); exit (1); break; } } /* Reverse the bucket once more, putting it back as it was. */ symtab[h] = reverse_symbol_list (symtab[h]); } /* All done. */ fputs ("# End of frozen state file\n", file); fclose (file); } /*----------------------------------------------------------------------. | Issue a message saying that some character is an EXPECTED character. | `----------------------------------------------------------------------*/ static void issue_expect_message (int expected) { if (expected == '\n') MP4HERROR ((EXIT_FAILURE, 0, _("%d: Expecting line feed in frozen file"), lineno)); else MP4HERROR ((EXIT_FAILURE, 0, _("%d: Expecting character `%c' in frozen file"), lineno, expected)); } /*-------------------------------------------------. | Reload a frozen state from the given file NAME. | `-------------------------------------------------*/ /* We are seeking speed, here. */ #define GET_CHARACTER \ (character = getc (file)) #define GET_NUMBER(Number) \ do \ { \ (Number) = 0; \ while (isdigit (character)) \ { \ (Number) = 10 * (Number) + character - '0'; \ GET_CHARACTER; \ } \ } \ while (0) #define VALIDATE(Expected) \ do \ { \ if (character != (Expected)) \ issue_expect_message ((Expected)); \ } \ while (0) 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; symbol *sym, *var; boolean container, expand_args; file = path_search (name, (char **)NULL); if (file == NULL) MP4HERROR ((EXIT_FAILURE, errno, _("Cannot open %s"), name)); lineno = 1; allocated[0] = 100; string[0] = xmalloc ((size_t) allocated[0]); allocated[1] = 100; string[1] = xmalloc ((size_t) allocated[1]); while (GET_CHARACTER, character != EOF) { switch (character) { default: MP4HERROR ((EXIT_FAILURE, 0, _("Ill-formated frozen file"))); case '\n': /* Skip empty lines. */ lineno++; break; case '#': /* Comments are introduced by `#' at beginning of line, and are ignored. */ while (character != EOF && character != '\n') GET_CHARACTER; VALIDATE ('\n'); lineno++; break; case 'C': /* Change comment strings. */ GET_CHARACTER; GET_NUMBER (number[0]); GET_CHARACTER; VALIDATE ('\n'); lineno++; if (number[0] + 1 > allocated[0]) { free (string[0]); allocated[0] = number[0] + 1; string[0] = xmalloc ((size_t) allocated[0]); } if (number[0] > 0) if (!fread (string[0], (size_t) number[0], 1, file)) MP4HERROR ((EXIT_FAILURE, 0, _("Premature end of frozen file"))); string[0][number[0]] = '\0'; GET_CHARACTER; VALIDATE ('\n'); lineno++; eolcomm.string = string[0]; eolcomm.length = strlen (eolcomm.string); break; case 'A': case 'F': case 'T': operation = character; GET_CHARACTER; /* Get string lengths. Accept a negative diversion number. */ number[1] = 0; GET_NUMBER (number[0]); VALIDATE (','); GET_CHARACTER; GET_NUMBER (number[1]); VALIDATE ('\n'); lineno++; /* Get first string contents. */ if (number[0] + 1 > allocated[0]) { free (string[0]); allocated[0] = number[0] + 1; string[0] = xmalloc ((size_t) allocated[0]); } if (number[0] > 0) if (!fread (string[0], (size_t) number[0], 1, file)) MP4HERROR ((EXIT_FAILURE, 0, _("Premature end of frozen file"))); string[0][number[0]] = '\0'; /* Get second string contents. */ if (number[1] + 1 > allocated[1]) { free (string[1]); allocated[1] = number[1] + 1; string[1] = xmalloc ((size_t) allocated[1]); } if (number[1] > 0) if (!fread (string[1], (size_t) number[1], 1, file)) MP4HERROR ((EXIT_FAILURE, 0, _("Premature end of frozen file"))); string[1][number[1]] = '\0'; GET_CHARACTER; VALIDATE ('\n'); lineno++; /* Act according to operation letter. */ switch (operation) { case 'A': /* Define a variable. */ var = lookup_variable (string[0], SYMBOL_INSERT); SYMBOL_TYPE (var) = TOKEN_TEXT; SYMBOL_TEXT (var) = xstrdup (string[0]); break; case 'F': /* Enter a macro having a builtin function as a definition. */ bp = find_builtin_by_name (string[1]); if (bp) define_builtin (string[0], bp, 0); else MP4HERROR ((warning_status, 0, _("\ `%s' from frozen file not found in builtin table!"), string[0])); break; case 'T': GET_CHARACTER; container = (character == '1'); GET_CHARACTER; expand_args = (character == '1'); GET_CHARACTER; VALIDATE ('\n'); lineno++; /* Enter a macro having an expansion text as a definition. */ define_user_macro (string[0], string[1], SYMBOL_INSERT, container, expand_args, FALSE); sym = lookup_symbol (string[0], SYMBOL_LOOKUP); /* Add hooks. */ GET_CHARACTER; GET_NUMBER (number[0]); VALIDATE (','); GET_CHARACTER; GET_NUMBER (number[1]); VALIDATE ('\n'); lineno++; if (number[0] > 0) { if (number[0] + 1 > allocated[0]) { free (string[0]); allocated[0] = number[0] + 1; string[0] = xmalloc ((size_t) allocated[0]); } if (!fread (string[0], (size_t) number[0], 1, file)) MP4HERROR ((EXIT_FAILURE, 0, _("Premature end of frozen file"))); string[0][number[0]] = '\0'; SYMBOL_HOOK_BEGIN (sym) = xstrdup (string[0]); } if (number[1] > 0) { if (number[1] + 1 > allocated[1]) { free (string[1]); allocated[1] = number[1] + 1; string[1] = xmalloc ((size_t) allocated[1]); } if (!fread (string[1], (size_t) number[1], 1, file)) MP4HERROR ((EXIT_FAILURE, 0, _("Premature end of frozen file"))); string[1][number[1]] = '\0'; SYMBOL_HOOK_END (sym) = xstrdup (string[1]); } GET_CHARACTER; VALIDATE ('\n'); lineno++; break; default: /* Cannot happen. */ break; } break; case 'V': /* Validate format version. Only `1' is acceptable for now. */ GET_CHARACTER; VALIDATE ('1'); GET_CHARACTER; VALIDATE ('\n'); lineno++; break; } } free (string[0]); free (string[1]); fclose (file); #undef GET_CHARACTER #undef GET_NUMBER #undef VALIDATE }