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