frozen files format V2

Eric Blake <[email protected]>
Newsgroups gmane.comp.gnu.m4.patches
Message-ID <[email protected]>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I did some sweeping improvements on src/freeze.c.  It has bugged me that
we claimed that frozen files were editable, but that a user macro with
8-bit or control characters didn't escape those characters except for the
changesyntax S directive, causing problems in some editors.  So I fixed it
- - all strings that can come from arbitrary sources are now fully
escape-encoded in version 2 format files.  It also bugged me that we were
dumping 256 syntax S directives, even when using the default syntax; so
the dump now only produces output for non-default setup.  This fix is also
one step closer to allowing NUL to be handled transparently, since
changesyntax settings for NUL could now be represented (I have that as a
goal for 2.0, although it is a huge, multi-step task to reach it).  It is
also one step closer to adding new changesyntax categories of { and } (for
implementing ${11}).

The documentation still needs an overhaul (including merging the frozen
files section with the improvements on the branch), and I have some
testsuite improvements to add, but this patch was getting big enough that
I wanted to checkpoint things.

NOTE: This is an incompatible change for reloading any frozen files
created with m4 1.4q that used \ in macro names or values.  Fortunately,
1.4o only used version 1 frozen files and was the last widely released m4
version; to my knowledge, 1.4q was never released on any gnu.org sites.  I
don't think many users will be impacted, and the difference between 1.4q
and 1.9a (or 2.0) is reasonable, and early adopters of 1.4q should know
they were using a beta version subject to change.

2007-01-20  Eric Blake  <[email protected]>

	* src/freeze.c (produce_char_dump): Delete.  Replace with...
	(produce_mem_dump): ...this new function, to encode more strings.
	Also make the coding easier to read and slightly more compact.
	(produce_syntax_dump): Only dump non-default syntax codes.
	(produce_module_dump, dump_symbol_CB, produce_frozen_state):
	Encode non-ASCII characters in user-supplied strings.
	(GET_STRING): Accept escape sequences in version 2.
	* NEWS: Document this change.
	* TODO: Update.

- --
Don't work too hard, make some time for fun as well!

Eric Blake             [email protected]
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFFsudC84KuGfSFAYARAlmyAKCM3tEB7DPaBlw+3RF5bjT48IA4PQCeJ35f
mI63DIPk5fiGWRwDMzXsfoo=
=3ycf
-----END PGP SIGNATURE-----

_______________________________________________
M4-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/m4-patches
m4.patch228 (text/plain, 19.4 KB)
Index: NEWS
===================================================================
RCS file: /sources/m4/m4/NEWS,v
retrieving revision 1.36
diff -u -p -r1.36 NEWS
--- NEWS	15 Jan 2007 14:04:26 -0000	1.36
+++ NEWS	21 Jan 2007 03:51:24 -0000
@@ -192,10 +192,14 @@ promoted to 2.0.
 ** Other changes
 
 *** The syntax of frozen files format V2 has been improved to save
-    additional state.
-  - FIXME: format 2 still needs to be tweaked to allow complete ASCII
-  representation and to catch any more missing state; once 2.0 is released,
-  any further changes would introduce format 3.
+    additional state.  This includes the 'R' directive for default regular
+    expression syntax.  Also, a V2 file can now be represented completely
+    in ASCII, thanks to escape sequences.  Unfortunately, files frozen by
+    M4 1.4q that contain \ in macros cannot be read by 1.9b, but since 1.4q
+    was not widely distributed, this is not expected to be much of an
+    issue, and comes with the territory of using a beta release.
+  - FIXME: format 2 still needs to catch more missing state; once 2.0 is
+    released, any further changes would introduce format 3.
 
 FIXME: include the (long) list of changes in 1.4.x that were not already
   in earlier betas.
Index: TODO
===================================================================
RCS file: /sources/m4/m4/TODO,v
retrieving revision 1.20
diff -u -p -r1.20 TODO
--- TODO	5 Sep 2006 13:25:24 -0000	1.20
+++ TODO	21 Jan 2007 03:51:24 -0000
@@ -1,5 +1,5 @@
 GNU m4 TODO - Tasks that need implementing.		-*- outline -*-
-Copyright (C) 2000, 2001, 2006 Free Software Foundation, Inc.
+Copyright (C) 2000, 2001, 2006, 2007 Free Software Foundation, Inc.
 
 Tell the maintainers at <[email protected]> if you feel like volunteering
 for any of these ideas or if you have others to add.
@@ -66,10 +66,6 @@ for any of these ideas or if you have ot
   + Make m4 show include dependencies like gcc so Makefile targets are
     updated when their (included) input files are updated (Erick B).
 
-  + Sort out all the weird forms of interaction between changesyntax,
-    changecom and changequote.  What happens if you install a quote with
-    changequote and removes it with changesyntax and vice versa.
-
   + Add support for wide character sets.
 
 
Index: src/freeze.c
===================================================================
RCS file: /sources/m4/m4/src/freeze.c,v
retrieving revision 1.58
diff -u -p -r1.58 freeze.c
--- src/freeze.c	13 Jan 2007 13:55:37 -0000	1.58
+++ src/freeze.c	21 Jan 2007 03:51:24 -0000
@@ -23,57 +23,52 @@
 #include "m4/system.h"
 #include "m4.h"
 #include "m4private.h"
+#include "binary-io.h"
 
-static	int   decode_char	    (FILE *);
-static	void  issue_expect_message  (m4 *, int);
-static	int   produce_char_dump     (char *, int);
-static	void  produce_resyntax_dump (m4 *, FILE *);
-static	void  produce_syntax_dump   (FILE *, m4_syntax_table *, char);
-static	void  produce_module_dump   (FILE *, lt_dlhandle);
-static	void  produce_symbol_dump   (m4 *, FILE *, m4_symbol_table *);
-static	void *dump_symbol_CB	    (m4_symbol_table *, const char *,
-				     m4_symbol *, void *);
+static	void  produce_mem_dump		(FILE *, const char *, size_t);
+static	void  produce_resyntax_dump	(m4 *, FILE *);
+static	void  produce_syntax_dump	(FILE *, m4_syntax_table *, char);
+static	void  produce_module_dump	(FILE *, lt_dlhandle);
+static	void  produce_symbol_dump	(m4 *, FILE *, m4_symbol_table *);
+static	void *dump_symbol_CB		(m4_symbol_table *, const char *,
+					 m4_symbol *, void *);
+static	void  issue_expect_message	(m4 *, int);
+static	int   decode_char		(FILE *);
 
 
-/* Produce a frozen state to the given file NAME. */
-
-static int
-produce_char_dump (char *buf, int ch)
+/* Dump an ASCII-encoded representation of LEN bytes at MEM to FILE.
+   MEM may contain embedded NUL characters.  */
+static void
+produce_mem_dump (FILE *file, const char *mem, size_t len)
 {
-  char *p = buf;
-  int digit;
-
-  if (ch > 127 || ch < 32)
-    {
-      *p++  = '\\';
-
-      digit = ch / 64;
-      ch    = ch - (64 * digit);
-      *p++  = digit + '0';
-
-      digit = ch / 8;
-      ch    = ch - (8 * digit);
-      *p++  = digit + '0';
-
-      *p++  = ch + '0';
-    }
-  else
+  while (len--)
     {
+      int ch = to_uchar (*mem++);
       switch (ch)
 	{
-	case '\\':
-	  *p++ = '\\';
-	  *p++ = '\\';
-	  break;
-
+	case '\a': putc ('\\', file); putc ('a', file); break;
+	case '\b': putc ('\\', file); putc ('b', file); break;
+	case '\f': putc ('\\', file); putc ('f', file); break;
+	case '\n': putc ('\\', file); putc ('n', file); break;
+	case '\r': putc ('\\', file); putc ('r', file); break;
+	case '\t': putc ('\\', file); putc ('t', file); break;
+	case '\v': putc ('\\', file); putc ('v', file); break;
+	case '\\': putc ('\\', file); putc ('\\', file); break;
 	default:
-	  *p++ = ch;
+	  if (ch >= 0x7f || ch < 0x20)
+	    {
+	      int digit = ch / 16;
+	      ch %= 16;
+	      digit += digit > 9 ? 'a' - 10 : '0';
+	      ch += ch > 9 ? 'a' - 10 : '0';
+	      putc ('\\', file);
+	      putc ('x', file);
+	      putc (digit, file);
+	    }
+	  putc (ch, file);
 	  break;
 	}
     }
-  *p = '\0';
-
-  return strlen (buf);
 }
 
 
@@ -87,7 +82,7 @@ produce_char_dump (char *buf, int ch)
 static void
 produce_resyntax_dump (m4 *context, FILE *file)
 {
-  int code  = m4_get_regexp_syntax_opt (context);
+  int code = m4_get_regexp_syntax_opt (context);
 
   /* Don't dump default syntax code (`0' for GNU_EMACS).  */
   if (code)
@@ -98,59 +93,67 @@ produce_resyntax_dump (m4 *context, FILE
 	m4_error (context, EXIT_FAILURE, 0,
 		  _("invalid regexp syntax code `%d'"), code);
 
+      /* No need to use produce_mem_dump, since we know all resyntax
+	 names are already ASCII-encoded.  */
       fprintf (file, "R%d\n%s\n", strlen (resyntax), resyntax);
     }
 }
 
-#define MAX_CHAR_LENGTH 4	/* '\377' -> 4 characters */
-
 static void
 produce_syntax_dump (FILE *file, m4_syntax_table *syntax, char ch)
 {
-  char buf[1+ MAX_CHAR_LENGTH * sizeof (m4_syntax_table)];
+  char buf[256];
   int code = m4_syntax_code (ch);
   int count = 0;
-  int offset = 0;
   int i;
 
-  /* FIXME:  Can't set the syntax of '\000' since that character marks
-	     the end of a string, and when passed to `m4_set_syntax', tells
-	     it to set the syntax of every table entry. */
+  for (i = 0; i < 256; ++i)
+    if (m4_has_syntax (syntax, i, code) && code != syntax->orig[i])
+      buf[count++] = i;
+
+  /* If code falls in M4_SYNTAX_MASKS, then we must treat it
+     specially, since it will not be found in syntax->orig.  */
+  if (count == 1
+      && ((code == M4_SYNTAX_RQUOTE && *buf == *DEF_RQUOTE)
+	  || (code == M4_SYNTAX_ECOMM && *buf == *DEF_ECOMM)))
+    return;
 
-  for (i = 1; i < 256; ++i)
+  if (count || (code & M4_SYNTAX_MASKS))
     {
-      if (m4_has_syntax (syntax, i, code))
-	{
-	  offset += produce_char_dump (buf + offset, i);
-	  ++count;
-	}
+      fprintf (file, "S%c%d\n", ch, count);
+      produce_mem_dump (file, buf, count);
+      fputc ('\n', file);
     }
-
-  if (offset)
-      fprintf (file, "S%c%d\n%s\n", ch, count, buf);
 }
 
 /* The modules must be dumped in the order in which they will be
    reloaded from the frozen file.  libltdl stores handles in a push
    down stack, so we need to dump them in the reverse order to that.  */
-void
+static void
 produce_module_dump (FILE *file, lt_dlhandle handle)
 {
   const char *name = m4_get_module_name (handle);
+  size_t len = strlen (name);
 
   handle = m4__module_next (handle);
   if (handle)
     produce_module_dump (file, handle);
 
-  fprintf (file, "M%lu\n", (unsigned long int) strlen (name));
-  fputs (name, file);
+  /* GNU assumption: Although POSIX allows size_t to be just 16 bits,
+     independently of int, GNU assumes is at least as big as int.
+     Likewise, although POSIX allows size_t to be bigger than unsigned
+     long in some compilation environments, GNU assumes that size_t
+     always fits in unsigned long.  Unfortunately, GNU does not assume
+     that %zu is portable, yet.  */
+  fprintf (file, "M%lu\n", (unsigned long int) len);
+  produce_mem_dump (file, name, len);
   fputc ('\n', file);
 }
 
 /* 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.  */
-void
+static void
 produce_symbol_dump (m4 *context, FILE *file, m4_symbol_table *symtab)
 {
   if (m4_symtab_apply (symtab, dump_symbol_CB, file))
@@ -164,43 +167,43 @@ dump_symbol_CB (m4_symbol_table *symtab,
   lt_dlhandle   handle		= SYMBOL_HANDLE (symbol);
   const char   *module_name	= handle ? m4_get_module_name (handle) : NULL;
   FILE *	file		= (FILE *) userdata;
+  size_t	symbol_len	= strlen (symbol_name);
+  size_t	module_len	= module_name ? strlen (module_name) : 0;
 
   if (m4_is_symbol_text (symbol))
     {
-      fprintf (file, "T%lu,%lu",
-	       (unsigned long int) strlen (symbol_name),
-	       (unsigned long int) strlen (m4_get_symbol_text (symbol)));
+      const char *text = m4_get_symbol_text (symbol);
+      size_t text_len = strlen (text);
+      fprintf (file, "T%lu,%lu", (unsigned long int) symbol_len,
+	       (unsigned long int) text_len);
       if (handle)
-	fprintf (file, ",%lu", (unsigned long int) strlen (module_name));
+	fprintf (file, ",%lu", (unsigned long int) module_len);
       fputc ('\n', file);
 
-      fputs (symbol_name, file);
-      fputs (m4_get_symbol_text (symbol), file);
+      produce_mem_dump (file, symbol_name, symbol_len);
+      produce_mem_dump (file, text, text_len);
       if (handle)
-	fputs (module_name, file);
+	produce_mem_dump (file, module_name, module_len);
       fputc ('\n', file);
     }
   else if (m4_is_symbol_func (symbol))
     {
-      const m4_builtin *bp = m4_builtin_find_by_func (SYMBOL_HANDLE (symbol),
-						m4_get_symbol_func (symbol));
-
+      const m4_builtin *bp = m4_get_symbol_builtin (symbol);
+      size_t bp_len;
       if (bp == NULL)
 	assert (!"INTERNAL ERROR: builtin not found in builtin table!");
+      bp_len = strlen (bp->name);
 
-      fprintf (file, "F%lu,%lu",
-	       (unsigned long int) strlen (symbol_name),
-	       (unsigned long int) strlen (bp->name));
-
+      fprintf (file, "F%lu,%lu", (unsigned long int) symbol_len,
+	       (unsigned long int) bp_len);
       if (handle)
-	fprintf (file, ",%lu",
-		 (unsigned long int) strlen (module_name));
+	fprintf (file, ",%lu", (unsigned long int) module_len);
       fputc ('\n', file);
 
-      fputs (symbol_name, file);
-      fputs (bp->name, file);
+      produce_mem_dump (file, symbol_name, symbol_len);
+      produce_mem_dump (file, bp->name, bp_len);
       if (handle)
-	fputs (module_name, file);
+	produce_mem_dump (file, module_name, module_len);
       fputc ('\n', file);
     }
   else if (m4_is_symbol_placeholder (symbol))
@@ -211,18 +214,13 @@ dump_symbol_CB (m4_symbol_table *symtab,
   return NULL;
 }
 
+/* Produce a frozen state to the given file NAME. */
 void
 produce_frozen_state (m4 *context, const char *name)
 {
-  FILE *file;
-
-#ifdef WIN32
-# define FROZEN_WRITE "wb"
-#else
-# define FROZEN_WRITE "w"
-#endif
+  FILE *file = fopen (name, O_BINARY ? "wb" : "w");
+  const char *str;
 
-  file = fopen (name, FROZEN_WRITE);
   if (!file)
     {
       m4_error (context, 0, errno, _("cannot open `%s'"), name);
@@ -243,8 +241,10 @@ produce_frozen_state (m4 *context, const
       fprintf (file, "Q%lu,%lu\n",
 	       (unsigned long int) context->syntax->lquote.length,
 	       (unsigned long int) context->syntax->rquote.length);
-      fputs (context->syntax->lquote.string, file);
-      fputs (context->syntax->rquote.string, file);
+      produce_mem_dump (file, context->syntax->lquote.string,
+			context->syntax->lquote.length);
+      produce_mem_dump (file, context->syntax->rquote.string,
+			context->syntax->rquote.length);
       fputc ('\n', file);
     }
 
@@ -256,8 +256,10 @@ produce_frozen_state (m4 *context, const
       fprintf (file, "C%lu,%lu\n",
 	       (unsigned long int) context->syntax->bcomm.length,
 	       (unsigned long int) context->syntax->ecomm.length);
-      fputs (context->syntax->bcomm.string, file);
-      fputs (context->syntax->ecomm.string, file);
+      produce_mem_dump (file, context->syntax->bcomm.string,
+			context->syntax->bcomm.length);
+      produce_mem_dump (file, context->syntax->ecomm.string,
+			context->syntax->ecomm.length);
       fputc ('\n', file);
     }
 
@@ -267,23 +269,9 @@ produce_frozen_state (m4 *context, const
 
   /* Dump syntax table. */
 
-  produce_syntax_dump (file, M4SYNTAX, 'I');
-  produce_syntax_dump (file, M4SYNTAX, 'S');
-  produce_syntax_dump (file, M4SYNTAX, '(');
-  produce_syntax_dump (file, M4SYNTAX, ')');
-  produce_syntax_dump (file, M4SYNTAX, ',');
-  produce_syntax_dump (file, M4SYNTAX, '$');
-  produce_syntax_dump (file, M4SYNTAX, 'A');
-  produce_syntax_dump (file, M4SYNTAX, '@');
-  produce_syntax_dump (file, M4SYNTAX, 'O');
-
-  produce_syntax_dump (file, M4SYNTAX, 'W');
-  produce_syntax_dump (file, M4SYNTAX, 'D');
-
-  produce_syntax_dump (file, M4SYNTAX, 'L');
-  produce_syntax_dump (file, M4SYNTAX, 'R');
-  produce_syntax_dump (file, M4SYNTAX, 'B');
-  produce_syntax_dump (file, M4SYNTAX, 'E');
+  str = "IS(),$A@OWDLRBE";
+  while (*str)
+    produce_syntax_dump (file, M4SYNTAX, *str++);
 
   /* Dump all loaded modules.  */
   produce_module_dump (file, m4__module_next (0));
@@ -313,40 +301,78 @@ issue_expect_message (m4 *context, int e
 	      _("expecting character `%c' in frozen file"), expected);
 }
 
-/* Reload a frozen state from the given file NAME. */
+
+/* Reload frozen state.  */
 
-/* Read the next character from the IN stream.  Octal characters of the
-   form ``\nnn'' are converted, and returned.  EOF is returned if the end
-   of file is reached whilst reading the character.  */
+/* Read the next character from the IN stream.  Various escape
+   sequences are converted, and returned.  EOF is returned if the end
+   of file is reached whilst reading the character, or on an
+   unrecognized escape sequence.  */
 
 static int
 decode_char (FILE *in)
 {
-  int ch = fgetc (in);
+  int ch = getc (in);
+  int next;
+  int value = 0;
 
   if (ch == '\\')
     {
-      ch = fgetc (in);
-      if ((ch != EOF) && (ch != '\\'))
+      ch = getc (in);
+      switch (ch)
 	{
-	  int next;
-
-	  /* first octal digit */
-	  ch = (ch - '0') * 64;
-
-	  /* second octal digit */
-	  next = fgetc (in);
-	  if (next == EOF)
-	    ch = EOF;
+	case 'a': return '\a';
+	case 'b': return '\b';
+	case 'f': return '\f';
+	case 'n': return '\n';
+	case 'r': return '\r';
+	case 't': return '\t';
+	case 'v': return '\v';
+	case '\\': return '\\';
+
+	case 'x': case 'X':
+	  next = getc (in);
+	  if (next >= '0' && next <= '9')
+	    ch = (next - '0') * 16;
+	  else if (next >= 'a' && next <= 'f')
+	    ch = (next - 'a' + 10) * 16;
+	  else if (next >= 'A' && next <= 'F')
+	    ch = (next - 'A' + 10) * 16;
 	  else
-	    ch += 8 * (next - '0');
-
-	  /* third octal digit */
-	  next = fgetc (in);
-	  if (next == EOF)
-	    ch = EOF;
+	    return EOF;
+	  next = getc (in);
+	  if (next >= '0' && next <= '9')
+	    ch += next - '0';
+	  else if (next >= 'a' && next <= 'f')
+	    ch += next - 'a' + 10;
+	  else if (next >= 'A' && next <= 'F')
+	    ch += next - 'A' + 10;
+	  else
+	    return EOF;
+	  return ch;
+	case '0': case '1': case '2': case '3':
+	  value = ch - '0';
+	  ch = getc (in);
+	  /* fall through */
+	case '4': case '5': case '6': case '7':
+	  if (ch >= '0' && ch <= '7')
+	    {
+	      value = value * 8 + ch - '0';
+	      ch = getc (in);
+	    }
 	  else
-	    ch += (next - '0');
+	    {
+	      ungetc (ch, in);
+	      return value;
+	    }
+	  if (ch >= '0' && ch <= '7')
+	    value = value * 8 + ch - '0';
+	  else
+	    ungetc (ch, in);
+	  return value;
+
+	default:
+	  return EOF;
 	}
     }
 
@@ -354,7 +380,8 @@ decode_char (FILE *in)
 }
 
 
-/* We are seeking speed, here.  */
+/*  Reload state from the given file NAME.  We are seeking speed,
+    here.  */
 
 void
 reload_frozen_state (m4 *context, const char *name)
@@ -365,7 +392,7 @@ reload_frozen_state (m4 *context, const 
   int operation;
   char syntax;
   char *string[3];
-  int allocated[3];
+  size_t allocated[3];
   int number[3] = {0};
 
 #define GET_CHARACTER						\
@@ -386,12 +413,19 @@ reload_frozen_state (m4 *context, const 
 #define GET_STRING(File, Buf, BufSize, StrLen)			\
   do								\
     {								\
-      CHECK_ALLOCATION ((Buf), (BufSize), (StrLen));		\
-      if ((StrLen) > 0)						\
-	if (!fread ((Buf), (size_t) (StrLen), 1, (File)))	\
-	  m4_error (context, EXIT_FAILURE, 0,			\
-		    _("premature end of frozen file"));		\
-      (Buf)[(StrLen)] = '\0';					\
+      size_t len = (StrLen);					\
+      char *p = (Buf);						\
+      CHECK_ALLOCATION (p, (BufSize), len);			\
+      while (len-- > 0)						\
+	{							\
+	  int ch = (version > 1 ? decode_char (File)		\
+		    : getc (File));				\
+	  if (ch == EOF)					\
+	    m4_error (context, EXIT_FAILURE, 0,			\
+		      _("premature end of frozen file"));	\
+	  *p++ = ch;						\
+	}							\
+      *p = '\0';						\
     }								\
   while (0)
 
@@ -410,7 +444,7 @@ reload_frozen_state (m4 *context, const 
 	{							\
 	  free (Where);						\
 	  (Allocated) = (Needed) + 1;				\
-	  (Where) = xmalloc ((size_t) (Allocated));		\
+	  (Where) = xmalloc (Allocated);			\
 	}							\
     }								\
   while (0)
@@ -436,11 +470,11 @@ reload_frozen_state (m4 *context, const 
     m4_error (context, EXIT_FAILURE, errno, _("cannot open `%s'"), name);
 
   allocated[0] = 100;
-  string[0] = xmalloc ((size_t) allocated[0]);
+  string[0] = xmalloc (allocated[0]);
   allocated[1] = 100;
-  string[1] = xmalloc ((size_t) allocated[1]);
+  string[1] = xmalloc (allocated[1]);
   allocated[2] = 100;
-  string[2] = xmalloc ((size_t) allocated[2]);
+  string[2] = xmalloc (allocated[2]);
 
   /* Validate format version.  Accept both `1' (m4 1.3 and 1.4.x) and
      `2' (m4 2.0).  */
@@ -451,22 +485,13 @@ reload_frozen_state (m4 *context, const 
   switch (version)
     {
     case 2:
-      {
-	int ch;
-
-	/* Take care not to mix frozen state with startup state.  */
-	for (ch = 256; --ch > 0;)
-	  context->syntax->table[ch] = 0;
-      }
       break;
     case 1:
-      {
-	m4__module_open (context, "m4", NULL);
-	if (m4_get_posixly_correct_opt (context))
-	  m4__module_open (context, "traditional", NULL);
-	else
-	  m4__module_open (context, "gnu", NULL);
-      }
+      m4__module_open (context, "m4", NULL);
+      if (m4_get_posixly_correct_opt (context))
+	m4__module_open (context, "traditional", NULL);
+      else
+	m4__module_open (context, "gnu", NULL);
       break;
     default:
       if (version > 2)
@@ -613,26 +638,13 @@ ill-formed frozen file, version 2 direct
 	  GET_CHARACTER;
 	  GET_NUMBER (number[0]);
 	  VALIDATE ('\n');
+	  GET_STRING (file, string[0], allocated[0], number[0]);
 
-	  CHECK_ALLOCATION (string[0], allocated[0], number[0]);
-	  if (number[0] > 0)
-	    {
-	      int i;
-
-	      for (i = 0; i < number[0]; ++i)
-		{
-		  int ch = decode_char (file);
-
-		  if (ch < 0)
-		    m4_error (context, EXIT_FAILURE, 0,
-			      _("premature end of frozen file"));
-
-		  string[0][i] = ch;
-		}
-	    }
-	  string[0][number[0]] = '\0';
-
-	  if ((m4_set_syntax (context->syntax, syntax, '=', string[0]) < 0)
+	  /* Syntax under M4_SYNTAX_MASKS is handled specially; all
+	     other characters are additive.  */
+	  if ((m4_set_syntax (context->syntax, syntax,
+			      (m4_syntax_code (syntax) & M4_SYNTAX_MASKS
+			       ? '=' : '+'), string[0]) < 0)
 	      && (syntax != '\0'))
 	    {
 	      m4_error (context, 0, 0,
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.