m4-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: branch-1_4 allocation overflow


From: Eric Blake
Subject: Re: branch-1_4 allocation overflow
Date: Mon, 13 Nov 2006 19:08:16 +0000 (UTC)
User-agent: Loom/3.14 (http://gmane.org/)

Gary V. Vaughan <gary <at> gnu.org> writes:

> >
> > Here's the two-part patch I am using for head.  First I optimized  
> > the data
> > representation, and got rid of a number of potential size_t-to-int  
> > bugs on 64-
> > bit platforms.  The second part pulls in a gnulib list structure,  
> > to convert
> > the sparse array into an efficient AVL tree.
> 
> Nice! I remember thinking about doing something similar in the pre- 
> gnulib
> days, but was exhausted after writing the hash ADT and forgot about it.

I liked this so much (in the interest of removing arbitrary limits) that I 
ported it to the branch.  In the process, I discovered that gl_oset is lighter-
weight than gl_list, fixed a bug in gnulib's oset, and found that I still had a 
bug in head when hitting the EMFILE limit on my platform (calling fclose on a 
bogus char* is never a good idea :).  Tripped on cygwin, which apparently 
cannot handle more than 1024 fd's (even though <limits.h> claims otherwise), by 
doing:

$ build/src/m4
include(examples/forloop.m4)dnl
forloop(i,1,2200,`divert(i)divnum
')
build/src/m4:stdin:2: cannot create temporary file for diversion: Too many open 
files
Aborted (core dumped)

So I'll have to re-port this to head.  But I'm not quite sure how to add it to 
the head testsuite, since not all platforms have a puny OPEN_MAX limit.  And on 
head, maybe it would be worth the extra complexity of closing and reopening 
temp files, to get around even that arbitrary limit.

2006-11-13  Eric Blake  <address@hidden>

        Backport sparse diversion handling from head.
        * m4/gnulib-cache.m4: Augment with 'gnulib-tool --import
        avltree-oset'.
        * src/output.c (struct m4_diversion): Rename from struct
        diversion, and update members.  All users changed.
        (diversion_table): Change to an ordered set, instead of an array.
        (div0): New storage for diversion 0.
        (diversions): No longer needed.
        (free_list): New list to allow recycling diversion storage.
        (diversion_storage): New storage to reduce malloc overhead.
        (cmp_diversion_CB, threshold_diversion_CB): New callbacks.
        (output_init, output_exit, cleanup_tmpfile, make_room_for)
        (make_diversion): Handle new diversion storage scheme.
        (insert_diversion_helper): New function.
        (insert_diversion, undivert_all, freeze_diversions): Use it.
        * doc/m4.texinfo (Divert, Diversions): Move hidden test of memory
        exhaustion to visible test of large diversion numbers.
        * NEWS: Document this fix.

Index: NEWS
===================================================================
RCS file: /sources/m4/m4/NEWS,v
retrieving revision 1.1.1.1.2.80
diff -u -r1.1.1.1.2.80 NEWS
--- NEWS        11 Nov 2006 12:54:46 -0000      1.1.1.1.2.80
+++ NEWS        13 Nov 2006 18:48:48 -0000
@@ -5,7 +5,10 @@
 Version 1.4.8 - ?? ??? 2006, by ??  (CVS version 1.4.7a)
 
 * The `divert' macro and `-H'/`--hashsize' command line option no longer
-  cause a core dump when handed extra large values.
+  cause a core dump when handed extra large values.  Also, `divert' now
+  uses memory proportional to the number of diversions in use, rather than
+  to the maximum diversion number encountered, so that large diversion
+  numbers are less likely to exhaust system memory.
 * The `--help' and `--version' command line options now consistently
   override all earlier options.  For example, `m4 --debugfile=trace
   --help' now no longer accidentally creates an empty file `trace'.
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.1.1.1.2.100
diff -u -r1.1.1.1.2.100 m4.texinfo
--- doc/m4.texinfo      11 Nov 2006 12:54:46 -0000      1.1.1.1.2.100
+++ doc/m4.texinfo      13 Nov 2006 18:48:48 -0000
@@ -3652,20 +3652,6 @@
 f
 m4exit
 @end example
-
address@hidden We also need to test allocation overflow.  On 32-bit
address@hidden platforms, this should fail outright.  But on 64-bit platforms
address@hidden with enough memory, the allocation might succeed (hopefully
address@hidden testers don't mind the memory thrashing), so fake the same
address@hidden output for test success.
-
address@hidden
-divert(eval(`1<<28'))
-divert(`2')
-errprint(__program__`: memory exhausted
-')m4exit(`1')
address@hidden: memory exhausted
address@hidden example
 @end ignore
 
 @c FIXME: need some explanation here why this is a useful feature, not
@@ -3725,10 +3711,10 @@
 @result{}Wrapped TEXT preceeds diverted text.
 @end example
 
-If output is diverted to a non-existent diversion, it is simply
-discarded.  This can be used to suppress unwanted output.  A common
-example of unwanted output is the trailing newlines after macro
-definitions.  Here is how to avoid them.
+If output is diverted to a negative diversion, it is simply discarded.
+This can be used to suppress unwanted output.  A common example of
+unwanted output is the trailing newlines after macro definitions.  Here
+is a common programming idiom in @code{m4} for avoiding them.
 
 @example
 divert(`-1')
@@ -3738,7 +3724,19 @@
 @result{}
 @end example
 
-This is a common programming idiom in @code{m4}.
address@hidden @acronym{GNU} extensions
+Traditional implementations only supported ten diversions.  But as a
address@hidden extension, diversion numbers can be as large as positive
+integers will allow, rather than treating a multi-digit diversion number
+as a request to discard text.
+
address@hidden
+divert(eval(`1<<28'))world
+divert(`2')hello
+^D
address@hidden
address@hidden
address@hidden example
 
 Note that @code{divert} is an English word, but also an active macro
 without arguments.  When processing plain text, the word might appear in
Index: m4/gnulib-cache.m4
===================================================================
RCS file: /sources/m4/m4/m4/Attic/gnulib-cache.m4,v
retrieving revision 1.1.2.19
diff -u -r1.1.2.19 gnulib-cache.m4
--- m4/gnulib-cache.m4  1 Nov 2006 22:29:08 -0000       1.1.2.19
+++ m4/gnulib-cache.m4  13 Nov 2006 18:48:48 -0000
@@ -15,11 +15,11 @@
 
 
 # Specification in the form of a command-line invocation:
-#   gnulib-tool --import --dir=. --lib=libm4 --source-base=lib --m4-base=m4 --
doc-base=doc --aux-dir=. --no-libtool --macro-prefix=M4 binary-io clean-temp 
cloexec close-stream closeout config-h error fdl fopen-safer free gendocs 
getopt gnupload mkstemp obstack regex stdbool stdlib-safer strstr strtol 
unlocked-io verror xalloc xvasprintf
+#   gnulib-tool --import --dir=. --lib=libm4 --source-base=lib --m4-base=m4 --
doc-base=doc --aux-dir=. --no-libtool --macro-prefix=M4 avltree-oset binary-io 
clean-temp cloexec close-stream closeout config-h error fdl fopen-safer free 
gendocs getopt gnupload mkstemp obstack regex stdbool stdlib-safer strstr 
strtol unlocked-io verror xalloc xvasprintf
 
 # Specification in the form of a few gnulib-tool.m4 macro invocations:
 gl_LOCAL_DIR([])
-gl_MODULES([binary-io clean-temp cloexec close-stream closeout config-h error 
fdl fopen-safer free gendocs getopt gnupload mkstemp obstack regex stdbool 
stdlib-safer strstr strtol unlocked-io verror xalloc xvasprintf])
+gl_MODULES([avltree-oset binary-io clean-temp cloexec close-stream closeout 
config-h error fdl fopen-safer free gendocs getopt gnupload mkstemp obstack 
regex stdbool stdlib-safer strstr strtol unlocked-io verror xalloc xvasprintf])
 gl_AVOID([])
 gl_SOURCE_BASE([lib])
 gl_M4_BASE([m4])
Index: src/output.c
===================================================================
RCS file: /sources/m4/m4/src/Attic/output.c,v
retrieving revision 1.1.1.1.2.15
diff -u -r1.1.1.1.2.15 output.c
--- src/output.c        8 Nov 2006 05:08:26 -0000       1.1.1.1.2.15
+++ src/output.c        13 Nov 2006 18:48:48 -0000
@@ -23,6 +23,8 @@
 
 #include <sys/stat.h>
 
+#include "gl_avltree_oset.h"
+
 /* Size of initial in-memory buffer size for diversions.  Small diversions
    would usually fit in.  */
 #define INITIAL_BUFFER_SIZE 512
@@ -40,24 +42,40 @@
    This code is fairly entangled with the code in input.c, and maybe it
    belongs there?  */
 
-/* In a struct diversion, only one of file or buffer be may non-NULL,
-   depending on the fact output is diverted to a file or in memory
-   buffer.  Further, if buffer is NULL, then pointer is NULL, size and
-   unused are zero.  */
+typedef struct temp_dir m4_temp_dir;
+
+/* When part of diversion_table, each struct m4_diversion either
+   represents an open file (zero size, non-NULL u.file), an in-memory
+   buffer (non-zero size, non-NULL u.buffer), or an unused placeholder
+   diversion (zero size, u is NULL).  When not part of
+   diversion_table, u.next is a pointer to the free_list chain.  */
 
-struct diversion
+typedef struct m4_diversion m4_diversion;
+
+struct m4_diversion
   {
-    FILE *file;                        /* diversion file on disk */
-    char *buffer;              /* in-memory diversion buffer */
+    union
+      {
+       FILE *file;             /* diversion file on disk */
+       char *buffer;           /* in-memory diversion buffer */
+       m4_diversion *next;     /* free-list pointer */
+      } u;
+    int divnum;                        /* which diversion this represents */
     int size;                  /* usable size before reallocation */
     int used;                  /* used length in characters */
   };
 
-/* Table of diversions.  */
-static struct diversion *diversion_table;
+/* Table of diversions 1 through INT_MAX.  */
+static gl_oset_t diversion_table;
+
+/* Diversion 0 (not part of diversion_table).  */
+static m4_diversion div0;
 
-/* Number of entries in diversion table.  */
-static int diversions;
+/* Linked list of reclaimed diversion storage.  */
+static m4_diversion *free_list;
+
+/* Obstack from which diversion storage is allocated.  */
+static struct obstack diversion_storage;
 
 /* Total size of all in-memory buffer sizes.  */
 static int total_buffer_size;
@@ -67,7 +85,7 @@
 int current_diversion;
 
 /* Current output diversion, NULL if output is being currently discarded.  */
-static struct diversion *output_diversion;
+static m4_diversion *output_diversion;
 
 /* Values of some output_diversion fields, cached out for speed.  */
 static FILE *output_file;      /* current value of (file) */
@@ -77,40 +95,56 @@
 /* Number of input line we are generating output for.  */
 int output_current_line;
 
-typedef struct temp_dir m4_temp_dir;
-
 /* Temporary directory holding all spilled diversion files.  */
 static m4_temp_dir *output_temp_dir;
 
 
 
 /*------------------------.
-| Output initialisation.  |
+| Output initialization.  |
 `------------------------*/
 
+/* Callback for comparing list elements ELT1 and ELT2 for order in
+   diversion_table.  */
+static int
+cmp_diversion_CB (const void *elt1, const void *elt2)
+{
+  const m4_diversion *d1 = (const m4_diversion *) elt1;
+  const m4_diversion *d2 = (const m4_diversion *) elt2;
+  /* No need to worry about overflow, since we don't create diversions
+     with negative divnum.  */
+  return d1->divnum - d2->divnum;
+}
+
+/* Callback for comparing list element ELT against THRESHOLD.  */
+static bool
+threshold_diversion_CB (const void *elt, const void *threshold)
+{
+  const m4_diversion *div = (const m4_diversion *) elt;
+  /* No need to worry about overflow, since we don't create diversions
+     with negative divnum.  */
+  return div->divnum >= *(const int *) threshold;
+}
+
 void
 output_init (void)
 {
-  diversion_table = (struct diversion *) xmalloc (sizeof (struct diversion));
-  diversions = 1;
-  diversion_table[0].file = stdout;
-  diversion_table[0].buffer = NULL;
-  diversion_table[0].size = 0;
-  diversion_table[0].used = 0;
-
-  total_buffer_size = 0;
-  current_diversion = 0;
-  output_diversion = diversion_table;
+  diversion_table = gl_oset_create_empty (GL_AVLTREE_OSET, cmp_diversion_CB);
+  div0.u.file = stdout;
+  output_diversion = &div0;
   output_file = stdout;
-  output_cursor = NULL;
-  output_unused = 0;
+  obstack_init (&diversion_storage);
 }
 
 void
 output_exit (void)
 {
-  free (diversion_table);
+  /* Order is important, since we may have registered cleanup_tmpfile
+     as an atexit handler, and it must not traverse stale memory.  */
+  gl_oset_t table = diversion_table;
   diversion_table = NULL;
+  gl_oset_free (table);
+  obstack_free (&diversion_storage, NULL);
 }
 
 /* Clean up any temporary directory.  Designed for use as an atexit
@@ -120,20 +154,24 @@
 cleanup_tmpfile (void)
 {
   /* Close any open diversions.  */
-  int divnum;
-  struct diversion *diversion;
   bool fail = false;
 
   if (diversion_table)
-    for (divnum = 1; divnum < diversions; divnum++)
-      {
-       diversion = diversion_table + divnum;
-       if (diversion->file && close_stream_temp (diversion->file) != 0)
-         {
-           M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
-           fail = true;
-         }
-      }
+    {
+      const void *elt;
+      gl_oset_iterator_t iter = gl_oset_iterator (diversion_table);
+      while (gl_oset_iterator_next (&iter, &elt))
+       {
+         m4_diversion *diversion = (m4_diversion *) elt;
+         if (!diversion->size && diversion->u.file
+             && close_stream_temp (diversion->u.file) != 0)
+           {
+             M4ERROR ((0, errno,
+                       "cannot clean temporary file for diversion"));
+             fail = true;
+           }
+       }
+    }
 
   /* Clean up the temporary directory.  */
   if (cleanup_temp_dir (output_temp_dir) != 0)
@@ -187,6 +225,7 @@
 make_room_for (int length)
 {
   int wanted_size;
+  m4_diversion *selected_diversion = NULL;
 
   /* Compute needed size for in-memory buffer.  Diversions in-memory
      buffers start at 0 bytes, then 512, then keep doubling until it is
@@ -204,10 +243,12 @@
   if (total_buffer_size - output_diversion->size + wanted_size
       > MAXIMUM_TOTAL_SIZE)
     {
-      struct diversion *selected_diversion;
       int selected_used;
-      struct diversion *diversion;
+      char *selected_buffer;
+      m4_diversion *diversion;
       int count;
+      gl_oset_iterator_t iter;
+      const void *elt;
 
       /* Find out the buffer having most data, in view of flushing it to
         disk.  Fake the current buffer as having already received the
@@ -217,29 +258,38 @@
       selected_diversion = output_diversion;
       selected_used = output_diversion->used + length;
 
-      for (diversion = diversion_table + 1;
-          diversion < diversion_table + diversions;
-          diversion++)
-       if (diversion->used > selected_used)
-         {
-           selected_diversion = diversion;
-           selected_used = diversion->used;
-         }
+      iter = gl_oset_iterator (diversion_table);
+      while (gl_oset_iterator_next (&iter, &elt))
+       {
+         diversion = (m4_diversion *) elt;
+         if (diversion->used > selected_used)
+           {
+             selected_diversion = diversion;
+             selected_used = diversion->used;
+           }
+       }
+      gl_oset_iterator_free (&iter);
 
       /* Create a temporary file, write the in-memory buffer of the
-        diversion to this file, then release the buffer.  */
+        diversion to this file, then release the buffer.  Zero the
+        diversion before doing anything that can exit () (including
+        m4_tmpfile), so that the atexit handler doesn't try to close
+        a garbage pointer as a file.  */
 
-      selected_diversion->file = m4_tmpfile ();
-      if (set_cloexec_flag (fileno (selected_diversion->file), true) != 0)
+      selected_buffer = selected_diversion->u.buffer;
+      total_buffer_size -= selected_diversion->size;
+      selected_diversion->size = 0;
+      selected_diversion->u.file = NULL;
+      selected_diversion->u.file = m4_tmpfile ();
+
+      if (set_cloexec_flag (fileno (selected_diversion->u.file), true) != 0)
        M4ERROR ((warning_status, errno,
                  "Warning: cannot protect diversion across forks"));
 
       if (selected_diversion->used > 0)
        {
-         count = fwrite (selected_diversion->buffer,
-                         (size_t) selected_diversion->used,
-                         1,
-                         selected_diversion->file);
+         count = fwrite (selected_buffer, (size_t) selected_diversion->used,
+                         1, selected_diversion->u.file);
          if (count != 1)
            M4ERROR ((EXIT_FAILURE, errno,
                      "ERROR: cannot flush diversion to temporary file"));
@@ -247,37 +297,31 @@
 
       /* Reclaim the buffer space for other diversions.  */
 
-      free (selected_diversion->buffer);
-      total_buffer_size -= selected_diversion->size;
-
-      selected_diversion->buffer = NULL;
-      selected_diversion->size = 0;
+      free (selected_buffer);
       selected_diversion->used = 0;
     }
 
   /* Reload output_file, just in case the flushed diversion was current.  */
 
-  output_file = output_diversion->file;
-  if (output_file)
+  if (output_diversion == selected_diversion)
     {
-
       /* The flushed diversion was current indeed.  */
 
+      output_file = output_diversion->u.file;
       output_cursor = NULL;
       output_unused = 0;
     }
   else
     {
-
       /* The buffer may be safely reallocated.  */
 
-      output_diversion->buffer
-       = xrealloc (output_diversion->buffer, (size_t) wanted_size);
+      output_diversion->u.buffer
+       = xrealloc (output_diversion->u.buffer, (size_t) wanted_size);
 
       total_buffer_size += wanted_size - output_diversion->size;
       output_diversion->size = wanted_size;
 
-      output_cursor = output_diversion->buffer + output_diversion->used;
+      output_cursor = output_diversion->u.buffer + output_diversion->used;
       output_unused = wanted_size - output_diversion->used;
     }
 }
@@ -445,12 +489,22 @@
 void
 make_diversion (int divnum)
 {
-  struct diversion *diversion;
+  m4_diversion *diversion = NULL;
+
+  if (current_diversion == divnum)
+    return;
 
   if (output_diversion)
     {
-      output_diversion->file = output_file;
-      output_diversion->used = output_diversion->size - output_unused;
+      if (!output_diversion->size && !output_diversion->u.file)
+       {
+         if (!gl_oset_remove (diversion_table, output_diversion))
+           error (EXIT_FAILURE, 0, "INTERNAL ERROR: make_diversion failed");
+         output_diversion->u.next = free_list;
+         free_list = output_diversion;
+       }
+      else
+       output_diversion->used = output_diversion->size - output_unused;
       output_diversion = NULL;
       output_file = NULL;
       output_cursor = NULL;
@@ -462,26 +516,47 @@
   if (divnum < 0)
     return;
 
-  if (divnum >= diversions)
+  if (divnum == 0)
+    diversion = &div0;
+  else
+    {
+      const void *elt;
+      if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB,
+                                 &divnum, &elt))
+       {
+         m4_diversion *temp = (m4_diversion *) elt;
+         if (temp->divnum == divnum)
+           diversion = temp;
+       }
+    }
+  if (diversion == NULL)
     {
-      diversion_table = (struct diversion *)
-       xnrealloc (diversion_table, divnum + 1, sizeof (struct diversion));
-      for (diversion = diversion_table + diversions;
-          diversion <= diversion_table + divnum;
-          diversion++)
+      /* First time visiting this diversion.  */
+      if (free_list)
        {
-         diversion->file = NULL;
-         diversion->buffer = NULL;
+         diversion = free_list;
+         free_list = diversion->u.next;
+       }
+      else
+       {
+         diversion = (m4_diversion *) obstack_alloc (&diversion_storage,
+                                                     sizeof *diversion);
          diversion->size = 0;
          diversion->used = 0;
        }
-      diversions = divnum + 1;
+      diversion->u.file = NULL;
+      diversion->divnum = divnum;
+      gl_oset_add (diversion_table, diversion);
     }
 
-  output_diversion = diversion_table + divnum;
-  output_file = output_diversion->file;
-  output_cursor = output_diversion->buffer + output_diversion->used;
-  output_unused = output_diversion->size - output_diversion->used;
+  output_diversion = diversion;
+  if (output_diversion->size)
+    {
+      output_cursor = output_diversion->u.buffer + output_diversion->used;
+      output_unused = output_diversion->size - output_diversion->used;
+    }
+  else
+    output_file = output_diversion->u.file;
   output_current_line = -1;
 }
 
@@ -515,59 +590,66 @@
     }
 }
 
-/*-------------------------------------------------------------------------.
-| Insert diversion number DIVNUM into the current output file.  The       |
-| diversion is NOT placed on the expansion obstack, because it must not be |
-| rescanned.  When the file is closed, it is deleted by the system.       |
-`-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------.
+| Insert DIVERSION (but not div0) into the current output file.  The |
+| diversion is NOT placed on the expansion obstack, because it must  |
+| not be rescanned.  When the file is closed, it is deleted by the   |
+| system.                                                           |
+`-------------------------------------------------------------------*/
 
-void
-insert_diversion (int divnum)
+static void
+insert_diversion_helper (m4_diversion *diversion)
 {
-  struct diversion *diversion;
-
-  /* Do not care about unexisting diversions.  Also, diversion 0 is stdout,
-     which is effectively always empty.  */
-
-  if (divnum <= 0 || divnum >= diversions)
-    return;
-
-  /* Also avoid undiverting into self.  */
-
-  diversion = diversion_table + divnum;
-  if (diversion == output_diversion)
-    return;
-
   /* Effectively undivert only if an output stream is active.  */
-
   if (output_diversion)
     {
-      if (diversion->file)
+      if (diversion->size)
+       output_text (diversion->u.buffer, diversion->used);
+      else if (diversion->u.file)
        {
-         rewind (diversion->file);
-         insert_file (diversion->file);
+         rewind (diversion->u.file);
+         insert_file (diversion->u.file);
        }
-      else if (diversion->buffer)
-       output_text (diversion->buffer, diversion->used);
 
       output_current_line = -1;
     }
 
   /* Return all space used by the diversion.  */
-
-  if (diversion->file)
-    {
-      if (close_stream_temp (diversion->file) != 0)
-       M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
-      diversion->file = NULL;
-    }
-  else if (diversion->buffer)
+  if (diversion->size)
     {
-      free (diversion->buffer);
-      diversion->buffer = NULL;
+      free (diversion->u.buffer);
       diversion->size = 0;
       diversion->used = 0;
     }
+  else if (diversion->u.file && close_stream_temp (diversion->u.file) != 0)
+    M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
+  gl_oset_remove (diversion_table, diversion);
+  diversion->u.next = free_list;
+  free_list = diversion;
+}
+
+/*-------------------------------------------------------------------------.
+| Insert diversion number DIVNUM into the current output file.  The       |
+| diversion is NOT placed on the expansion obstack, because it must not be |
+| rescanned.  When the file is closed, it is deleted by the system.       |
+`-------------------------------------------------------------------------*/
+
+void
+insert_diversion (int divnum)
+{
+  const void *elt;
+
+  /* Do not care about nonexistent diversions, and undiverting stdout
+     or self is a no-op.  */
+  if (divnum <= 0 || current_diversion == divnum)
+    return;
+  if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB,
+                             &divnum, &elt))
+    {
+      m4_diversion *diversion = (m4_diversion *) elt;
+      if (diversion->divnum == divnum)
+       insert_diversion_helper (diversion);
+    }
 }
 
 /*-------------------------------------------------------------------------.
@@ -578,10 +660,15 @@
 void
 undivert_all (void)
 {
-  int divnum;
-
-  for (divnum = 1; divnum < diversions; divnum++)
-    insert_diversion (divnum);
+  const void *elt;
+  gl_oset_iterator_t iter = gl_oset_iterator (diversion_table);
+  while (gl_oset_iterator_next (&iter, &elt))
+    {
+      m4_diversion *diversion = (m4_diversion *) elt;
+      if (diversion->divnum != current_diversion)
+       insert_diversion_helper (diversion);
+    }
+  gl_oset_iterator_free (&iter);
 }
 
 /*-------------------------------------------------------------.
@@ -593,38 +680,39 @@
 {
   int saved_number;
   int last_inserted;
-  int divnum;
-  struct diversion *diversion;
-  struct stat file_stat;
+  gl_oset_iterator_t iter;
+  const void *elt;
 
   saved_number = current_diversion;
   last_inserted = 0;
   make_diversion (0);
   output_file = file;          /* kludge in the frozen file */
 
-  for (divnum = 1; divnum < diversions; divnum++)
+  iter = gl_oset_iterator (diversion_table);
+  while (gl_oset_iterator_next (&iter, &elt))
     {
-      diversion = diversion_table + divnum;
-      if (diversion->file || diversion->buffer)
+      m4_diversion *diversion = (m4_diversion *) elt;;
+      if (diversion->size || diversion->u.file)
        {
-         if (diversion->file)
+         if (diversion->size)
+           fprintf (file, "D%d,%d\n", diversion->divnum, diversion->used);
+         else
            {
-             fflush (diversion->file);
-             if (fstat (fileno (diversion->file), &file_stat) < 0)
+             struct stat file_stat;
+             fflush (diversion->u.file);
+             if (fstat (fileno (diversion->u.file), &file_stat) < 0)
                M4ERROR ((EXIT_FAILURE, errno, "cannot stat diversion"));
              if (file_stat.st_size < 0
-                 || file_stat.st_size != (unsigned long) file_stat.st_size)
+                 || file_stat.st_size != (unsigned long int) file_stat.st_size)
                M4ERROR ((EXIT_FAILURE, 0, "diversion too large"));
-             fprintf (file, "D%d,%lu", divnum,
+             fprintf (file, "D%d,%lu", diversion->divnum,
                       (unsigned long int) file_stat.st_size);
            }
-         else
-           fprintf (file, "D%d,%d\n", divnum, diversion->used);
 
-         insert_diversion (divnum);
+         insert_diversion_helper (diversion);
          putc ('\n', file);
 
-         last_inserted = divnum;
+         last_inserted = diversion->divnum;
        }
     }
 







reply via email to

[Prev in Thread] Current Thread [Next in Thread]