[Top][All Lists]
[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;
}
}