Fix texinfo grammar.
[m4/ericb.git] / src / output.c
blobd252d74e3864cde59e8fa51c8061e6dfa74f536d
1 /* GNU m4 -- A simple macro processor
3 Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006,
4 2007, 2008 Free Software Foundation, Inc.
6 This file is part of GNU M4.
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.
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.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "m4.h"
24 #include <sys/stat.h>
26 #include "gl_avltree_oset.h"
28 /* Size of initial in-memory buffer size for diversions. Small diversions
29 would usually fit in. */
30 #define INITIAL_BUFFER_SIZE 512
32 /* Maximum value for the total of all in-memory buffer sizes for
33 diversions. */
34 #define MAXIMUM_TOTAL_SIZE (512 * 1024)
36 /* Size of buffer size to use while copying files. */
37 #define COPY_BUFFER_SIZE (32 * 512)
39 /* Output functions. Most of the complexity is for handling cpp like
40 sync lines.
42 This code is fairly entangled with the code in input.c, and maybe it
43 belongs there? */
45 typedef struct temp_dir m4_temp_dir;
47 /* When part of diversion_table, each struct m4_diversion either
48 represents an open file (zero size, non-NULL u.file), an in-memory
49 buffer (non-zero size, non-NULL u.buffer), or an unused placeholder
50 diversion (zero size, u is NULL, non-zero used indicates that a
51 file has been created). When not part of diversion_table, u.next
52 is a pointer to the free_list chain. */
54 typedef struct m4_diversion m4_diversion;
56 struct m4_diversion
58 union
60 FILE *file; /* diversion file on disk */
61 char *buffer; /* in-memory diversion buffer */
62 m4_diversion *next; /* free-list pointer */
63 } u;
64 int divnum; /* which diversion this represents */
65 int size; /* usable size before reallocation */
66 int used; /* used length in characters */
69 /* Table of diversions 1 through INT_MAX. */
70 static gl_oset_t diversion_table;
72 /* Diversion 0 (not part of diversion_table). */
73 static m4_diversion div0;
75 /* Linked list of reclaimed diversion storage. */
76 static m4_diversion *free_list;
78 /* Obstack from which diversion storage is allocated. */
79 static struct obstack diversion_storage;
81 /* Total size of all in-memory buffer sizes. */
82 static int total_buffer_size;
84 /* The number of the currently active diversion. This variable is
85 maintained for the `divnum' builtin function. */
86 int current_diversion;
88 /* Current output diversion, NULL if output is being currently discarded. */
89 static m4_diversion *output_diversion;
91 /* Values of some output_diversion fields, cached out for speed. */
92 static FILE *output_file; /* current value of (file) */
93 static char *output_cursor; /* current value of (buffer + used) */
94 static int output_unused; /* current value of (size - used) */
96 /* Number of input line we are generating output for. */
97 int output_current_line;
99 /* Temporary directory holding all spilled diversion files. */
100 static m4_temp_dir *output_temp_dir;
104 /*------------------------.
105 | Output initialization. |
106 `------------------------*/
108 /* Callback for comparing list elements ELT1 and ELT2 for order in
109 diversion_table. */
110 static int
111 cmp_diversion_CB (const void *elt1, const void *elt2)
113 const m4_diversion *d1 = (const m4_diversion *) elt1;
114 const m4_diversion *d2 = (const m4_diversion *) elt2;
115 /* No need to worry about overflow, since we don't create diversions
116 with negative divnum. */
117 return d1->divnum - d2->divnum;
120 /* Callback for comparing list element ELT against THRESHOLD. */
121 static bool
122 threshold_diversion_CB (const void *elt, const void *threshold)
124 const m4_diversion *div = (const m4_diversion *) elt;
125 /* No need to worry about overflow, since we don't create diversions
126 with negative divnum. */
127 return div->divnum >= *(const int *) threshold;
130 void
131 output_init (void)
133 diversion_table = gl_oset_create_empty (GL_AVLTREE_OSET, cmp_diversion_CB,
134 NULL);
135 div0.u.file = stdout;
136 output_diversion = &div0;
137 output_file = stdout;
138 obstack_init (&diversion_storage);
141 void
142 output_exit (void)
144 /* Order is important, since we may have registered cleanup_tmpfile
145 as an atexit handler, and it must not traverse stale memory. */
146 gl_oset_t table = diversion_table;
147 diversion_table = NULL;
148 gl_oset_free (table);
149 obstack_free (&diversion_storage, NULL);
152 /* Clean up any temporary directory. Designed for use as an atexit
153 handler, where it is not safe to call exit() recursively; so this
154 calls _exit if a problem is encountered. */
155 static void
156 cleanup_tmpfile (void)
158 /* Close any open diversions. */
159 bool fail = false;
161 if (diversion_table)
163 const void *elt;
164 gl_oset_iterator_t iter = gl_oset_iterator (diversion_table);
165 while (gl_oset_iterator_next (&iter, &elt))
167 m4_diversion *diversion = (m4_diversion *) elt;
168 if (!diversion->size && diversion->u.file
169 && close_stream_temp (diversion->u.file) != 0)
171 m4_warn (errno, NULL,
172 _("cannot clean temporary file for diversion"));
173 fail = true;
176 gl_oset_iterator_free (&iter);
179 /* Clean up the temporary directory. */
180 if (cleanup_temp_dir (output_temp_dir) != 0)
181 fail = true;
182 if (fail)
183 _exit (exit_failure);
186 /* Convert DIVNUM into a temporary file name for use in m4_tmp*. */
187 static const char *
188 m4_tmpname (int divnum)
190 static char *buffer;
191 static size_t offset;
192 if (buffer == NULL)
194 obstack_grow (&diversion_storage, output_temp_dir->dir_name,
195 strlen (output_temp_dir->dir_name));
196 obstack_1grow (&diversion_storage, '/');
197 obstack_1grow (&diversion_storage, 'm');
198 obstack_1grow (&diversion_storage, '4');
199 obstack_1grow (&diversion_storage, '-');
200 offset = obstack_object_size (&diversion_storage);
201 buffer = obstack_alloc (&diversion_storage, INT_BUFSIZE_BOUND (divnum));
203 if (snprintf (&buffer[offset], INT_BUFSIZE_BOUND (divnum), "%d", divnum) < 0)
204 m4_error (EXIT_FAILURE, errno, NULL,
205 _("cannot create temporary file for diversion"));
206 return buffer;
209 /* Create a temporary file for diversion DIVNUM open for reading and
210 writing in a secure temp directory. The file will be automatically
211 closed and deleted on a fatal signal. The file can be closed and
212 reopened with m4_tmpclose and m4_tmpopen; when finally done with
213 the file, close it with m4_tmpremove. Exits on failure, so the
214 return value is always an open file. */
215 static FILE *
216 m4_tmpfile (int divnum)
218 const char *name;
219 FILE *file;
221 if (output_temp_dir == NULL)
223 output_temp_dir = create_temp_dir ("m4-", NULL, true);
224 if (output_temp_dir == NULL)
225 m4_error (EXIT_FAILURE, errno, NULL,
226 _("cannot create temporary file for diversion"));
227 atexit (cleanup_tmpfile);
229 name = m4_tmpname (divnum);
230 register_temp_file (output_temp_dir, name);
231 file = fopen_temp (name, O_BINARY ? "wb+" : "w+");
232 if (file == NULL)
234 unregister_temp_file (output_temp_dir, name);
235 m4_error (EXIT_FAILURE, errno, NULL,
236 _("cannot create temporary file for diversion"));
238 else if (set_cloexec_flag (fileno (file), true) != 0)
239 m4_warn (errno, NULL, _("cannot protect diversion across forks"));
240 return file;
243 /* Reopen a temporary file for diversion DIVNUM for reading and
244 writing in a secure temp directory. Exits on failure, so the
245 return value is always an open file. */
246 static FILE *
247 m4_tmpopen (int divnum)
249 const char *name = m4_tmpname (divnum);
250 FILE *file;
252 file = fopen_temp (name, O_BINARY ? "ab+" : "a+");
253 if (file == NULL)
254 m4_error (EXIT_FAILURE, errno, NULL,
255 _("cannot create temporary file for diversion"));
256 else if (set_cloexec_flag (fileno (file), true) != 0)
257 m4_warn (errno, NULL, _("cannot protect diversion across forks"));
258 /* POSIX states that it is undefined whether an append stream starts
259 at offset 0 or at the end. We want the beginning. */
260 else if (fseeko (file, 0, SEEK_SET) != 0)
261 m4_error (EXIT_FAILURE, errno, NULL,
262 _("cannot seek to beginning of diversion"));
263 return file;
266 /* Close, but don't delete, a temporary FILE. */
267 static int
268 m4_tmpclose (FILE *file)
270 return close_stream_temp (file);
273 /* Delete a closed temporary FILE for diversion DIVNUM. */
274 static int
275 m4_tmpremove (int divnum)
277 return cleanup_temp_file (output_temp_dir, m4_tmpname (divnum));
280 /*-----------------------------------------------------------------------.
281 | Reorganize in-memory diversion buffers so the current diversion can |
282 | accomodate LENGTH more characters without further reorganization. The |
283 | current diversion buffer is made bigger if possible. But to make room |
284 | for a bigger buffer, one of the in-memory diversion buffers might have |
285 | to be flushed to a newly created temporary file. This flushed buffer |
286 | might well be the current one. |
287 `-----------------------------------------------------------------------*/
289 static void
290 make_room_for (int length)
292 int wanted_size;
293 m4_diversion *selected_diversion = NULL;
295 /* Compute needed size for in-memory buffer. Diversions in-memory
296 buffers start at 0 bytes, then 512, then keep doubling until it is
297 decided to flush them to disk. */
299 output_diversion->used = output_diversion->size - output_unused;
301 for (wanted_size = output_diversion->size;
302 wanted_size < output_diversion->used + length;
303 wanted_size = wanted_size == 0 ? INITIAL_BUFFER_SIZE : wanted_size * 2)
306 /* Check if we are exceeding the maximum amount of buffer memory. */
308 if (total_buffer_size - output_diversion->size + wanted_size
309 > MAXIMUM_TOTAL_SIZE)
311 int selected_used;
312 char *selected_buffer;
313 m4_diversion *diversion;
314 int count;
315 gl_oset_iterator_t iter;
316 const void *elt;
318 /* Find out the buffer having most data, in view of flushing it to
319 disk. Fake the current buffer as having already received the
320 projected data, while making the selection. So, if it is
321 selected indeed, we will flush it smaller, before it grows. */
323 selected_diversion = output_diversion;
324 selected_used = output_diversion->used + length;
326 iter = gl_oset_iterator (diversion_table);
327 while (gl_oset_iterator_next (&iter, &elt))
329 diversion = (m4_diversion *) elt;
330 if (diversion->used > selected_used)
332 selected_diversion = diversion;
333 selected_used = diversion->used;
336 gl_oset_iterator_free (&iter);
338 /* Create a temporary file, write the in-memory buffer of the
339 diversion to this file, then release the buffer. Zero the
340 diversion before doing anything that can exit () (including
341 m4_tmpfile), so that the atexit handler doesn't try to close
342 a garbage pointer as a file. */
344 selected_buffer = selected_diversion->u.buffer;
345 total_buffer_size -= selected_diversion->size;
346 selected_diversion->size = 0;
347 selected_diversion->u.file = NULL;
348 selected_diversion->u.file = m4_tmpfile (selected_diversion->divnum);
350 if (selected_diversion->used > 0)
352 count = fwrite (selected_buffer, (size_t) selected_diversion->used,
353 1, selected_diversion->u.file);
354 if (count != 1)
355 m4_error (EXIT_FAILURE, errno, NULL,
356 _("cannot flush diversion to temporary file"));
359 /* Reclaim the buffer space for other diversions. */
361 free (selected_buffer);
362 selected_diversion->used = 1;
365 /* Reload output_file, just in case the flushed diversion was current. */
367 if (output_diversion == selected_diversion)
369 /* The flushed diversion was current indeed. */
371 output_file = output_diversion->u.file;
372 output_cursor = NULL;
373 output_unused = 0;
375 else
377 /* Close any selected file since it is not the current diversion. */
378 if (selected_diversion)
380 FILE *file = selected_diversion->u.file;
381 selected_diversion->u.file = NULL;
382 if (m4_tmpclose (file) != 0)
383 m4_warn (errno, NULL,
384 _("cannot close temporary file for diversion"));
387 /* The current buffer may be safely reallocated. */
388 output_diversion->u.buffer
389 = xrealloc (output_diversion->u.buffer, (size_t) wanted_size);
391 total_buffer_size += wanted_size - output_diversion->size;
392 output_diversion->size = wanted_size;
394 output_cursor = output_diversion->u.buffer + output_diversion->used;
395 output_unused = wanted_size - output_diversion->used;
399 /*------------------------------------------------------------------------.
400 | Output one character CHAR, when it is known that it goes to a diversion |
401 | file or an in-memory diversion buffer. |
402 `------------------------------------------------------------------------*/
404 #define OUTPUT_CHARACTER(Char) \
405 if (output_file) \
406 putc ((Char), output_file); \
407 else if (output_unused == 0) \
408 output_character_helper ((Char)); \
409 else \
410 (output_unused--, *output_cursor++ = (Char))
412 static void
413 output_character_helper (int character)
415 make_room_for (1);
417 if (output_file)
418 putc (character, output_file);
419 else
421 *output_cursor++ = character;
422 output_unused--;
426 /*------------------------------------------------------------------------.
427 | Output one TEXT having LENGTH characters, when it is known that it goes |
428 | to a diversion file or an in-memory diversion buffer. |
429 `------------------------------------------------------------------------*/
431 void
432 output_text (const char *text, int length)
434 int count;
436 if (!output_diversion || !length)
437 return;
439 if (!output_file && length > output_unused)
440 make_room_for (length);
442 if (output_file)
444 count = fwrite (text, length, 1, output_file);
445 if (count != 1)
446 m4_error (EXIT_FAILURE, errno, NULL, _("error copying inserted file"));
448 else
450 memcpy (output_cursor, text, (size_t) length);
451 output_cursor += length;
452 output_unused -= length;
456 /*--------------------------------------------------------------------.
457 | Add some text into an obstack OBS, taken from TEXT, having LENGTH |
458 | characters. If OBS is NULL, output the text to an external file |
459 | or an in-memory diversion buffer instead. If OBS is NULL, and |
460 | there is no output file, the text is discarded. LINE is the line |
461 | where the token starts (not necessarily current_line, in the case |
462 | of multiline tokens). |
464 | If we are generating sync lines, the output has to be examined, |
465 | because we need to know how much output each input line generates. |
466 | In general, sync lines are output whenever a single input lines |
467 | generates several output lines, or when several input lines do not |
468 | generate any output. |
469 `--------------------------------------------------------------------*/
471 void
472 shipout_text (struct obstack *obs, const char *text, int length, int line)
474 static bool start_of_output_line = true;
475 const char *cursor;
477 /* If output goes to an obstack, merely add TEXT to it. */
479 if (obs != NULL)
481 obstack_grow (obs, text, length);
482 return;
485 /* Do nothing if TEXT should be discarded. */
487 if (output_diversion == NULL)
488 return;
490 /* Output TEXT to a file, or in-memory diversion buffer. */
492 if (!sync_output)
493 switch (length)
496 /* In-line short texts. */
498 case 8: OUTPUT_CHARACTER (*text); text++;
499 case 7: OUTPUT_CHARACTER (*text); text++;
500 case 6: OUTPUT_CHARACTER (*text); text++;
501 case 5: OUTPUT_CHARACTER (*text); text++;
502 case 4: OUTPUT_CHARACTER (*text); text++;
503 case 3: OUTPUT_CHARACTER (*text); text++;
504 case 2: OUTPUT_CHARACTER (*text); text++;
505 case 1: OUTPUT_CHARACTER (*text);
506 case 0:
507 return;
509 /* Optimize longer texts. */
511 default:
512 output_text (text, length);
514 else
516 /* Check for syncline only at the start of a token. Multiline
517 tokens, and tokens that are out of sync but in the middle of
518 the line, must wait until the next raw newline triggers a
519 syncline. */
520 if (start_of_output_line)
522 start_of_output_line = false;
523 output_current_line++;
524 #ifdef DEBUG_OUTPUT
525 xfprintf (stderr, "DEBUG: line %d, cur %d, cur out %d\n",
526 line, current_line, output_current_line);
527 #endif
529 /* Output a `#line NUM' synchronization directive if needed.
530 If output_current_line was previously given a negative
531 value (invalidated), output `#line NUM "FILE"' instead. */
533 if (output_current_line != line)
535 OUTPUT_CHARACTER ('#');
536 OUTPUT_CHARACTER ('l');
537 OUTPUT_CHARACTER ('i');
538 OUTPUT_CHARACTER ('n');
539 OUTPUT_CHARACTER ('e');
540 OUTPUT_CHARACTER (' ');
541 for (cursor = ntoa (line, 10); *cursor; cursor++)
542 OUTPUT_CHARACTER (*cursor);
543 if (output_current_line < 1 && current_file[0] != '\0')
545 OUTPUT_CHARACTER (' ');
546 OUTPUT_CHARACTER ('"');
547 for (cursor = current_file; *cursor; cursor++)
548 OUTPUT_CHARACTER (*cursor);
549 OUTPUT_CHARACTER ('"');
551 OUTPUT_CHARACTER ('\n');
552 output_current_line = line;
556 /* Output the token, and track embedded newlines. */
557 for (; length-- > 0; text++)
559 if (start_of_output_line)
561 start_of_output_line = false;
562 output_current_line++;
563 #ifdef DEBUG_OUTPUT
564 xfprintf (stderr, "DEBUG: line %d, cur %d, cur out %d\n",
565 line, current_line, output_current_line);
566 #endif
568 OUTPUT_CHARACTER (*text);
569 if (*text == '\n')
570 start_of_output_line = true;
575 /* Functions for use by diversions. */
577 /*--------------------------------------------------------------------------.
578 | Make a file for diversion DIVNUM, and install it in the diversion table. |
579 | Grow the size of the diversion table as needed. |
580 `--------------------------------------------------------------------------*/
582 /* The number of possible diversions is limited only by memory and
583 available file descriptors (each overflowing diversion uses one). */
585 void
586 make_diversion (int divnum)
588 m4_diversion *diversion = NULL;
590 if (current_diversion == divnum)
591 return;
593 if (output_diversion)
595 if (!output_diversion->size && !output_diversion->u.file)
597 if (!gl_oset_remove (diversion_table, output_diversion))
598 assert (false);
599 output_diversion->u.next = free_list;
600 output_diversion->used = 0;
601 free_list = output_diversion;
603 else if (output_diversion->size)
604 output_diversion->used = output_diversion->size - output_unused;
605 else if (output_diversion->used)
607 FILE *file = output_diversion->u.file;
608 output_diversion->u.file = NULL;
609 if (m4_tmpclose (file) != 0)
610 m4_warn (errno, NULL,
611 _("cannot close temporary file for diversion"));
613 output_diversion = NULL;
614 output_file = NULL;
615 output_cursor = NULL;
616 output_unused = 0;
619 current_diversion = divnum;
621 if (divnum < 0)
622 return;
624 if (divnum == 0)
625 diversion = &div0;
626 else
628 const void *elt;
629 if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB,
630 &divnum, &elt))
632 m4_diversion *temp = (m4_diversion *) elt;
633 if (temp->divnum == divnum)
634 diversion = temp;
637 if (diversion == NULL)
639 /* First time visiting this diversion. */
640 if (free_list)
642 diversion = free_list;
643 free_list = diversion->u.next;
645 else
647 diversion = (m4_diversion *) obstack_alloc (&diversion_storage,
648 sizeof *diversion);
649 diversion->size = 0;
650 diversion->used = 0;
652 diversion->u.file = NULL;
653 diversion->divnum = divnum;
654 gl_oset_add (diversion_table, diversion);
657 output_diversion = diversion;
658 if (output_diversion->size)
660 output_cursor = output_diversion->u.buffer + output_diversion->used;
661 output_unused = output_diversion->size - output_diversion->used;
663 else
665 if (!output_diversion->u.file && output_diversion->used)
666 output_diversion->u.file = m4_tmpopen (output_diversion->divnum);
667 output_file = output_diversion->u.file;
669 output_current_line = -1;
672 /*-------------------------------------------------------------------.
673 | Insert a FILE into the current output file, in the same manner |
674 | diversions are handled. This allows files to be included, without |
675 | having them rescanned by m4. |
676 `-------------------------------------------------------------------*/
678 void
679 insert_file (FILE *file)
681 char buffer[COPY_BUFFER_SIZE];
682 size_t length;
684 /* Optimize out inserting into a sink. */
686 if (!output_diversion)
687 return;
689 /* Insert output by big chunks. */
691 for (;;)
693 length = fread (buffer, 1, COPY_BUFFER_SIZE, file);
694 if (ferror (file))
695 m4_error (EXIT_FAILURE, errno, NULL, _("error reading inserted file"));
696 if (length == 0)
697 break;
698 output_text (buffer, length);
702 /*-------------------------------------------------------------------.
703 | Insert DIVERSION (but not div0) into the current output file. The |
704 | diversion is NOT placed on the expansion obstack, because it must |
705 | not be rescanned. When the file is closed, it is deleted by the |
706 | system. |
707 `-------------------------------------------------------------------*/
709 static void
710 insert_diversion_helper (m4_diversion *diversion)
712 /* Effectively undivert only if an output stream is active. */
713 if (output_diversion)
715 if (diversion->size)
716 output_text (diversion->u.buffer, diversion->used);
717 else
719 if (!diversion->u.file)
720 diversion->u.file = m4_tmpopen (diversion->divnum);
721 insert_file (diversion->u.file);
724 output_current_line = -1;
727 /* Return all space used by the diversion. */
728 if (diversion->size)
730 free (diversion->u.buffer);
731 diversion->size = 0;
732 diversion->used = 0;
734 else
736 if (diversion->u.file)
738 FILE *file = diversion->u.file;
739 diversion->u.file = NULL;
740 diversion->used = 0;
741 if (m4_tmpclose (file) != 0)
742 m4_warn (errno, NULL,
743 _("cannot clean temporary file for diversion"));
745 if (m4_tmpremove (diversion->divnum) != 0)
746 m4_warn (errno, NULL, _("cannot clean temporary file for diversion"));
748 gl_oset_remove (diversion_table, diversion);
749 diversion->u.next = free_list;
750 free_list = diversion;
753 /*-------------------------------------------------------------------------.
754 | Insert diversion number DIVNUM into the current output file. The |
755 | diversion is NOT placed on the expansion obstack, because it must not be |
756 | rescanned. When the file is closed, it is deleted by the system. |
757 `-------------------------------------------------------------------------*/
759 void
760 insert_diversion (int divnum)
762 const void *elt;
764 /* Do not care about nonexistent diversions, and undiverting stdout
765 or self is a no-op. */
766 if (divnum <= 0 || current_diversion == divnum)
767 return;
768 if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB,
769 &divnum, &elt))
771 m4_diversion *diversion = (m4_diversion *) elt;
772 if (diversion->divnum == divnum)
773 insert_diversion_helper (diversion);
777 /*-------------------------------------------------------------------------.
778 | Get back all diversions. This is done just before exiting from main (), |
779 | and from m4_undivert (), if called without arguments. |
780 `-------------------------------------------------------------------------*/
782 void
783 undivert_all (void)
785 const void *elt;
786 gl_oset_iterator_t iter = gl_oset_iterator (diversion_table);
787 while (gl_oset_iterator_next (&iter, &elt))
789 m4_diversion *diversion = (m4_diversion *) elt;
790 if (diversion->divnum != current_diversion)
791 insert_diversion_helper (diversion);
793 gl_oset_iterator_free (&iter);
796 /*-------------------------------------------------------------.
797 | Produce all diversion information in frozen format on FILE. |
798 `-------------------------------------------------------------*/
800 void
801 freeze_diversions (FILE *file)
803 int saved_number;
804 int last_inserted;
805 gl_oset_iterator_t iter;
806 const void *elt;
808 saved_number = current_diversion;
809 last_inserted = 0;
810 make_diversion (0);
811 output_file = file; /* kludge in the frozen file */
813 iter = gl_oset_iterator (diversion_table);
814 while (gl_oset_iterator_next (&iter, &elt))
816 m4_diversion *diversion = (m4_diversion *) elt;;
817 if (diversion->size || diversion->used)
819 if (diversion->size)
820 xfprintf (file, "D%d,%d\n", diversion->divnum, diversion->used);
821 else
823 struct stat file_stat;
824 diversion->u.file = m4_tmpopen (diversion->divnum);
825 if (fstat (fileno (diversion->u.file), &file_stat) < 0)
826 m4_error (EXIT_FAILURE, errno, NULL,
827 _("cannot stat diversion"));
828 if (file_stat.st_size < 0
829 || file_stat.st_size != (unsigned long int) file_stat.st_size)
830 m4_error (EXIT_FAILURE, 0, NULL, _("diversion too large"));
831 xfprintf (file, "D%d,%lu\n", diversion->divnum,
832 (unsigned long int) file_stat.st_size);
835 insert_diversion_helper (diversion);
836 putc ('\n', file);
838 last_inserted = diversion->divnum;
841 gl_oset_iterator_free (&iter);
843 /* Save the active diversion number, if not already. */
845 if (saved_number != last_inserted)
846 xfprintf (file, "D%d,0\n\n", saved_number);