source: trunk/essentials/sys-devel/m4/src/output.c

Last change on this file was 3090, checked in by bird, 18 years ago

m4 1.4.8

File size: 24.0 KB
Line 
1/* GNU m4 -- A simple macro processor
2
3 Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006 Free
4 Software Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA
20*/
21
22#include "m4.h"
23
24#include <limits.h>
25#include <sys/stat.h>
26
27#include "gl_avltree_oset.h"
28
29/* Size of initial in-memory buffer size for diversions. Small diversions
30 would usually fit in. */
31#define INITIAL_BUFFER_SIZE 512
32
33/* Maximum value for the total of all in-memory buffer sizes for
34 diversions. */
35#define MAXIMUM_TOTAL_SIZE (512 * 1024)
36
37/* Size of buffer size to use while copying files. */
38#define COPY_BUFFER_SIZE (32 * 512)
39
40/* Output functions. Most of the complexity is for handling cpp like
41 sync lines.
42
43 This code is fairly entangled with the code in input.c, and maybe it
44 belongs there? */
45
46typedef struct temp_dir m4_temp_dir;
47
48/* When part of diversion_table, each struct m4_diversion either
49 represents an open file (zero size, non-NULL u.file), an in-memory
50 buffer (non-zero size, non-NULL u.buffer), or an unused placeholder
51 diversion (zero size, u is NULL, non-zero used indicates that a
52 file has been created). When not part of diversion_table, u.next
53 is a pointer to the free_list chain. */
54
55typedef struct m4_diversion m4_diversion;
56
57struct m4_diversion
58 {
59 union
60 {
61 FILE *file; /* diversion file on disk */
62 char *buffer; /* in-memory diversion buffer */
63 m4_diversion *next; /* free-list pointer */
64 } u;
65 int divnum; /* which diversion this represents */
66 int size; /* usable size before reallocation */
67 int used; /* used length in characters */
68 };
69
70/* Table of diversions 1 through INT_MAX. */
71static gl_oset_t diversion_table;
72
73/* Diversion 0 (not part of diversion_table). */
74static m4_diversion div0;
75
76/* Linked list of reclaimed diversion storage. */
77static m4_diversion *free_list;
78
79/* Obstack from which diversion storage is allocated. */
80static struct obstack diversion_storage;
81
82/* Total size of all in-memory buffer sizes. */
83static int total_buffer_size;
84
85/* The number of the currently active diversion. This variable is
86 maintained for the `divnum' builtin function. */
87int current_diversion;
88
89/* Current output diversion, NULL if output is being currently discarded. */
90static m4_diversion *output_diversion;
91
92/* Values of some output_diversion fields, cached out for speed. */
93static FILE *output_file; /* current value of (file) */
94static char *output_cursor; /* current value of (buffer + used) */
95static int output_unused; /* current value of (size - used) */
96
97/* Number of input line we are generating output for. */
98int output_current_line;
99
100/* Temporary directory holding all spilled diversion files. */
101static m4_temp_dir *output_temp_dir;
102
103
104
105
106/*------------------------.
107| Output initialization. |
108`------------------------*/
109
110/* Callback for comparing list elements ELT1 and ELT2 for order in
111 diversion_table. */
112static int
113cmp_diversion_CB (const void *elt1, const void *elt2)
114{
115 const m4_diversion *d1 = (const m4_diversion *) elt1;
116 const m4_diversion *d2 = (const m4_diversion *) elt2;
117 /* No need to worry about overflow, since we don't create diversions
118 with negative divnum. */
119 return d1->divnum - d2->divnum;
120}
121
122/* Callback for comparing list element ELT against THRESHOLD. */
123static bool
124threshold_diversion_CB (const void *elt, const void *threshold)
125{
126 const m4_diversion *div = (const m4_diversion *) elt;
127 /* No need to worry about overflow, since we don't create diversions
128 with negative divnum. */
129 return div->divnum >= *(const int *) threshold;
130}
131
132void
133output_init (void)
134{
135 diversion_table = gl_oset_create_empty (GL_AVLTREE_OSET, cmp_diversion_CB);
136 div0.u.file = stdout;
137 output_diversion = &div0;
138 output_file = stdout;
139 obstack_init (&diversion_storage);
140}
141
142void
143output_exit (void)
144{
145 /* Order is important, since we may have registered cleanup_tmpfile
146 as an atexit handler, and it must not traverse stale memory. */
147 gl_oset_t table = diversion_table;
148 diversion_table = NULL;
149 gl_oset_free (table);
150 obstack_free (&diversion_storage, NULL);
151}
152
153/* Clean up any temporary directory. Designed for use as an atexit
154 handler, where it is not safe to call exit() recursively; so this
155 calls _exit if a problem is encountered. */
156static void
157cleanup_tmpfile (void)
158{
159 /* Close any open diversions. */
160 bool fail = false;
161
162 if (diversion_table)
163 {
164 const void *elt;
165 gl_oset_iterator_t iter = gl_oset_iterator (diversion_table);
166 while (gl_oset_iterator_next (&iter, &elt))
167 {
168 m4_diversion *diversion = (m4_diversion *) elt;
169 if (!diversion->size && diversion->u.file
170 && close_stream_temp (diversion->u.file) != 0)
171 {
172 M4ERROR ((0, errno,
173 "cannot clean temporary file for diversion"));
174 fail = true;
175 }
176 }
177 gl_oset_iterator_free (&iter);
178 }
179
180 /* Clean up the temporary directory. */
181 if (cleanup_temp_dir (output_temp_dir) != 0)
182 fail = true;
183 if (fail)
184 _exit (exit_failure);
185}
186
187/* Convert DIVNUM into a temporary file name for use in m4_tmp*. */
188static const char *
189m4_tmpname (int divnum)
190{
191 static char *buffer;
192 static char *tail;
193 if (buffer == NULL)
194 {
195 tail = xasprintf ("%s/m4-%d", output_temp_dir->dir_name, INT_MAX);
196 buffer = obstack_copy0 (&diversion_storage, tail, strlen (tail));
197 free (tail);
198 tail = strrchr (buffer, '-') + 1;
199 }
200 sprintf (tail, "%d", divnum);
201 return buffer;
202}
203
204/* Create a temporary file for diversion DIVNUM open for reading and
205 writing in a secure temp directory. The file will be automatically
206 closed and deleted on a fatal signal. The file can be closed and
207 reopened with m4_tmpclose and m4_tmpopen; when finally done with
208 the file, close it with m4_tmpremove. Exits on failure, so the
209 return value is always an open file. */
210static FILE *
211m4_tmpfile (int divnum)
212{
213 const char *name;
214 FILE *file;
215
216 if (output_temp_dir == NULL)
217 {
218 errno = 0;
219 output_temp_dir = create_temp_dir ("m4-", NULL, true);
220 if (output_temp_dir == NULL)
221 M4ERROR ((EXIT_FAILURE, errno,
222 "cannot create temporary file for diversion"));
223 atexit (cleanup_tmpfile);
224 }
225 name = m4_tmpname (divnum);
226 register_temp_file (output_temp_dir, name);
227 errno = 0;
228 file = fopen_temp (name, O_BINARY ? "wb+" : "w+");
229 if (file == NULL)
230 {
231 unregister_temp_file (output_temp_dir, name);
232 M4ERROR ((EXIT_FAILURE, errno,
233 "cannot create temporary file for diversion"));
234 }
235 else if (set_cloexec_flag (fileno (file), true) != 0)
236 M4ERROR ((warning_status, errno,
237 "Warning: cannot protect diversion across forks"));
238 return file;
239}
240
241/* Reopen a temporary file for diversion DIVNUM for reading and
242 writing in a secure temp directory. Exits on failure, so the
243 return value is always an open file. */
244static FILE *
245m4_tmpopen (int divnum)
246{
247 const char *name = m4_tmpname (divnum);
248 FILE *file;
249
250 errno = 0;
251 file = fopen_temp (name, O_BINARY ? "ab+" : "a+");
252 if (file == NULL)
253 M4ERROR ((EXIT_FAILURE, errno,
254 "cannot create temporary file for diversion"));
255 else if (set_cloexec_flag (fileno (file), true) != 0)
256 M4ERROR ((warning_status, errno,
257 "Warning: cannot protect diversion across forks"));
258 return file;
259}
260
261/* Close, but don't delete, a temporary FILE. */
262static int
263m4_tmpclose (FILE *file)
264{
265 return close_stream_temp (file);
266}
267
268/* Delete a closed temporary FILE for diversion DIVNUM. */
269static int
270m4_tmpremove (int divnum)
271{
272 return cleanup_temp_file (output_temp_dir, m4_tmpname (divnum));
273}
274
275/*-----------------------------------------------------------------------.
276| Reorganize in-memory diversion buffers so the current diversion can |
277| accomodate LENGTH more characters without further reorganization. The |
278| current diversion buffer is made bigger if possible. But to make room |
279| for a bigger buffer, one of the in-memory diversion buffers might have |
280| to be flushed to a newly created temporary file. This flushed buffer |
281| might well be the current one. |
282`-----------------------------------------------------------------------*/
283
284static void
285make_room_for (int length)
286{
287 int wanted_size;
288 m4_diversion *selected_diversion = NULL;
289
290 /* Compute needed size for in-memory buffer. Diversions in-memory
291 buffers start at 0 bytes, then 512, then keep doubling until it is
292 decided to flush them to disk. */
293
294 output_diversion->used = output_diversion->size - output_unused;
295
296 for (wanted_size = output_diversion->size;
297 wanted_size < output_diversion->used + length;
298 wanted_size = wanted_size == 0 ? INITIAL_BUFFER_SIZE : wanted_size * 2)
299 ;
300
301 /* Check if we are exceeding the maximum amount of buffer memory. */
302
303 if (total_buffer_size - output_diversion->size + wanted_size
304 > MAXIMUM_TOTAL_SIZE)
305 {
306 int selected_used;
307 char *selected_buffer;
308 m4_diversion *diversion;
309 int count;
310 gl_oset_iterator_t iter;
311 const void *elt;
312
313 /* Find out the buffer having most data, in view of flushing it to
314 disk. Fake the current buffer as having already received the
315 projected data, while making the selection. So, if it is
316 selected indeed, we will flush it smaller, before it grows. */
317
318 selected_diversion = output_diversion;
319 selected_used = output_diversion->used + length;
320
321 iter = gl_oset_iterator (diversion_table);
322 while (gl_oset_iterator_next (&iter, &elt))
323 {
324 diversion = (m4_diversion *) elt;
325 if (diversion->used > selected_used)
326 {
327 selected_diversion = diversion;
328 selected_used = diversion->used;
329 }
330 }
331 gl_oset_iterator_free (&iter);
332
333 /* Create a temporary file, write the in-memory buffer of the
334 diversion to this file, then release the buffer. Zero the
335 diversion before doing anything that can exit () (including
336 m4_tmpfile), so that the atexit handler doesn't try to close
337 a garbage pointer as a file. */
338
339 selected_buffer = selected_diversion->u.buffer;
340 total_buffer_size -= selected_diversion->size;
341 selected_diversion->size = 0;
342 selected_diversion->u.file = NULL;
343 selected_diversion->u.file = m4_tmpfile (selected_diversion->divnum);
344
345 if (selected_diversion->used > 0)
346 {
347 count = fwrite (selected_buffer, (size_t) selected_diversion->used,
348 1, selected_diversion->u.file);
349 if (count != 1)
350 M4ERROR ((EXIT_FAILURE, errno,
351 "ERROR: cannot flush diversion to temporary file"));
352 }
353
354 /* Reclaim the buffer space for other diversions. */
355
356 free (selected_buffer);
357 selected_diversion->used = 1;
358 }
359
360 /* Reload output_file, just in case the flushed diversion was current. */
361
362 if (output_diversion == selected_diversion)
363 {
364 /* The flushed diversion was current indeed. */
365
366 output_file = output_diversion->u.file;
367 output_cursor = NULL;
368 output_unused = 0;
369 }
370 else
371 {
372 /* Close any selected file since it is not the current diversion. */
373 if (selected_diversion)
374 {
375 FILE *file = selected_diversion->u.file;
376 selected_diversion->u.file = 0;
377 if (m4_tmpclose (file) != 0)
378 M4ERROR ((0, errno, "cannot close temporary file for diversion"));
379 }
380
381 /* The current buffer may be safely reallocated. */
382 output_diversion->u.buffer
383 = xrealloc (output_diversion->u.buffer, (size_t) wanted_size);
384
385 total_buffer_size += wanted_size - output_diversion->size;
386 output_diversion->size = wanted_size;
387
388 output_cursor = output_diversion->u.buffer + output_diversion->used;
389 output_unused = wanted_size - output_diversion->used;
390 }
391}
392
393/*------------------------------------------------------------------------.
394| Output one character CHAR, when it is known that it goes to a diversion |
395| file or an in-memory diversion buffer. |
396`------------------------------------------------------------------------*/
397
398#define OUTPUT_CHARACTER(Char) \
399 if (output_file) \
400 putc ((Char), output_file); \
401 else if (output_unused == 0) \
402 output_character_helper ((Char)); \
403 else \
404 (output_unused--, *output_cursor++ = (Char))
405
406static void
407output_character_helper (int character)
408{
409 make_room_for (1);
410
411 if (output_file)
412 putc (character, output_file);
413 else
414 {
415 *output_cursor++ = character;
416 output_unused--;
417 }
418}
419
420/*------------------------------------------------------------------------.
421| Output one TEXT having LENGTH characters, when it is known that it goes |
422| to a diversion file or an in-memory diversion buffer. |
423`------------------------------------------------------------------------*/
424
425static void
426output_text (const char *text, int length)
427{
428 int count;
429
430 if (!output_file && length > output_unused)
431 make_room_for (length);
432
433 if (output_file)
434 {
435 count = fwrite (text, length, 1, output_file);
436 if (count != 1)
437 M4ERROR ((EXIT_FAILURE, errno, "ERROR: copying inserted file"));
438 }
439 else
440 {
441 memcpy (output_cursor, text, (size_t) length);
442 output_cursor += length;
443 output_unused -= length;
444 }
445}
446
447/*-------------------------------------------------------------------------.
448| Add some text into an obstack OBS, taken from TEXT, having LENGTH |
449| characters. If OBS is NULL, rather output the text to an external file |
450| or an in-memory diversion buffer. If OBS is NULL, and there is no |
451| output file, the text is discarded. |
452| |
453| If we are generating sync lines, the output have to be examined, because |
454| we need to know how much output each input line generates. In general, |
455| sync lines are output whenever a single input lines generates several |
456| output lines, or when several input lines does not generate any output. |
457`-------------------------------------------------------------------------*/
458
459void
460shipout_text (struct obstack *obs, const char *text, int length)
461{
462 static bool start_of_output_line = true;
463 char line[20];
464 const char *cursor;
465
466 /* If output goes to an obstack, merely add TEXT to it. */
467
468 if (obs != NULL)
469 {
470 obstack_grow (obs, text, length);
471 return;
472 }
473
474 /* Do nothing if TEXT should be discarded. */
475
476 if (output_diversion == NULL)
477 return;
478
479 /* Output TEXT to a file, or in-memory diversion buffer. */
480
481 if (!sync_output)
482 switch (length)
483 {
484
485 /* In-line short texts. */
486
487 case 8: OUTPUT_CHARACTER (*text); text++;
488 case 7: OUTPUT_CHARACTER (*text); text++;
489 case 6: OUTPUT_CHARACTER (*text); text++;
490 case 5: OUTPUT_CHARACTER (*text); text++;
491 case 4: OUTPUT_CHARACTER (*text); text++;
492 case 3: OUTPUT_CHARACTER (*text); text++;
493 case 2: OUTPUT_CHARACTER (*text); text++;
494 case 1: OUTPUT_CHARACTER (*text);
495 case 0:
496 return;
497
498 /* Optimize longer texts. */
499
500 default:
501 output_text (text, length);
502 }
503 else
504 for (; length-- > 0; text++)
505 {
506 if (start_of_output_line)
507 {
508 start_of_output_line = false;
509 output_current_line++;
510
511#ifdef DEBUG_OUTPUT
512 printf ("DEBUG: cur %d, cur out %d\n",
513 current_line, output_current_line);
514#endif
515
516 /* Output a `#line NUM' synchronization directive if needed.
517 If output_current_line was previously given a negative
518 value (invalidated), rather output `#line NUM "FILE"'. */
519
520 if (output_current_line != current_line)
521 {
522 sprintf (line, "#line %d", current_line);
523 for (cursor = line; *cursor; cursor++)
524 OUTPUT_CHARACTER (*cursor);
525 if (output_current_line < 1 && current_file[0] != '\0')
526 {
527 OUTPUT_CHARACTER (' ');
528 OUTPUT_CHARACTER ('"');
529 for (cursor = current_file; *cursor; cursor++)
530 OUTPUT_CHARACTER (*cursor);
531 OUTPUT_CHARACTER ('"');
532 }
533 OUTPUT_CHARACTER ('\n');
534 output_current_line = current_line;
535 }
536 }
537 OUTPUT_CHARACTER (*text);
538 if (*text == '\n')
539 start_of_output_line = true;
540 }
541}
542
543
544/* Functions for use by diversions. */
545
546/*--------------------------------------------------------------------------.
547| Make a file for diversion DIVNUM, and install it in the diversion table. |
548| Grow the size of the diversion table as needed. |
549`--------------------------------------------------------------------------*/
550
551/* The number of possible diversions is limited only by memory and
552 available file descriptors (each overflowing diversion uses one). */
553
554void
555make_diversion (int divnum)
556{
557 m4_diversion *diversion = NULL;
558
559 if (current_diversion == divnum)
560 return;
561
562 if (output_diversion)
563 {
564 if (!output_diversion->size && !output_diversion->u.file)
565 {
566 if (!gl_oset_remove (diversion_table, output_diversion))
567 error (EXIT_FAILURE, 0, "INTERNAL ERROR: make_diversion failed");
568 output_diversion->u.next = free_list;
569 output_diversion->used = 0;
570 free_list = output_diversion;
571 }
572 else if (output_diversion->size)
573 output_diversion->used = output_diversion->size - output_unused;
574 else if (output_diversion->used)
575 {
576 FILE *file = output_diversion->u.file;
577 output_diversion->u.file = NULL;
578 if (m4_tmpclose (file) != 0)
579 M4ERROR ((0, errno, "cannot close temporary file for diversion"));
580 }
581 output_diversion = NULL;
582 output_file = NULL;
583 output_cursor = NULL;
584 output_unused = 0;
585 }
586
587 current_diversion = divnum;
588
589 if (divnum < 0)
590 return;
591
592 if (divnum == 0)
593 diversion = &div0;
594 else
595 {
596 const void *elt;
597 if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB,
598 &divnum, &elt))
599 {
600 m4_diversion *temp = (m4_diversion *) elt;
601 if (temp->divnum == divnum)
602 diversion = temp;
603 }
604 }
605 if (diversion == NULL)
606 {
607 /* First time visiting this diversion. */
608 if (free_list)
609 {
610 diversion = free_list;
611 free_list = diversion->u.next;
612 }
613 else
614 {
615 diversion = (m4_diversion *) obstack_alloc (&diversion_storage,
616 sizeof *diversion);
617 diversion->size = 0;
618 diversion->used = 0;
619 }
620 diversion->u.file = NULL;
621 diversion->divnum = divnum;
622 gl_oset_add (diversion_table, diversion);
623 }
624
625 output_diversion = diversion;
626 if (output_diversion->size)
627 {
628 output_cursor = output_diversion->u.buffer + output_diversion->used;
629 output_unused = output_diversion->size - output_diversion->used;
630 }
631 else
632 {
633 if (!output_diversion->u.file && output_diversion->used)
634 output_diversion->u.file = m4_tmpopen (output_diversion->divnum);
635 output_file = output_diversion->u.file;
636 }
637 output_current_line = -1;
638}
639
640/*-------------------------------------------------------------------.
641| Insert a FILE into the current output file, in the same manner |
642| diversions are handled. This allows files to be included, without |
643| having them rescanned by m4. |
644`-------------------------------------------------------------------*/
645
646void
647insert_file (FILE *file)
648{
649 char buffer[COPY_BUFFER_SIZE];
650 size_t length;
651
652 /* Optimize out inserting into a sink. */
653
654 if (!output_diversion)
655 return;
656
657 /* Insert output by big chunks. */
658
659 for (;;)
660 {
661 length = fread (buffer, 1, COPY_BUFFER_SIZE, file);
662 if (ferror (file))
663 M4ERROR ((EXIT_FAILURE, errno, "ERROR: reading inserted file"));
664 if (length == 0)
665 break;
666 output_text (buffer, length);
667 }
668}
669
670/*-------------------------------------------------------------------.
671| Insert DIVERSION (but not div0) into the current output file. The |
672| diversion is NOT placed on the expansion obstack, because it must |
673| not be rescanned. When the file is closed, it is deleted by the |
674| system. |
675`-------------------------------------------------------------------*/
676
677static void
678insert_diversion_helper (m4_diversion *diversion)
679{
680 /* Effectively undivert only if an output stream is active. */
681 if (output_diversion)
682 {
683 if (diversion->size)
684 output_text (diversion->u.buffer, diversion->used);
685 else
686 {
687 if (!diversion->u.file && diversion->used)
688 diversion->u.file = m4_tmpopen (diversion->divnum);
689 if (diversion->u.file)
690 {
691 rewind (diversion->u.file);
692 insert_file (diversion->u.file);
693 }
694 }
695
696 output_current_line = -1;
697 }
698
699 /* Return all space used by the diversion. */
700 if (diversion->size)
701 {
702 free (diversion->u.buffer);
703 diversion->size = 0;
704 diversion->used = 0;
705 }
706 else
707 {
708 if (diversion->u.file)
709 {
710 FILE *file = diversion->u.file;
711 diversion->u.file = NULL;
712 diversion->used = 0;
713 if (m4_tmpclose (file) != 0)
714 M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
715 }
716 if (m4_tmpremove (diversion->divnum) != 0)
717 M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
718 }
719 gl_oset_remove (diversion_table, diversion);
720 diversion->u.next = free_list;
721 free_list = diversion;
722}
723
724/*-------------------------------------------------------------------------.
725| Insert diversion number DIVNUM into the current output file. The |
726| diversion is NOT placed on the expansion obstack, because it must not be |
727| rescanned. When the file is closed, it is deleted by the system. |
728`-------------------------------------------------------------------------*/
729
730void
731insert_diversion (int divnum)
732{
733 const void *elt;
734
735 /* Do not care about nonexistent diversions, and undiverting stdout
736 or self is a no-op. */
737 if (divnum <= 0 || current_diversion == divnum)
738 return;
739 if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB,
740 &divnum, &elt))
741 {
742 m4_diversion *diversion = (m4_diversion *) elt;
743 if (diversion->divnum == divnum)
744 insert_diversion_helper (diversion);
745 }
746}
747
748/*-------------------------------------------------------------------------.
749| Get back all diversions. This is done just before exiting from main (), |
750| and from m4_undivert (), if called without arguments. |
751`-------------------------------------------------------------------------*/
752
753void
754undivert_all (void)
755{
756 const void *elt;
757 gl_oset_iterator_t iter = gl_oset_iterator (diversion_table);
758 while (gl_oset_iterator_next (&iter, &elt))
759 {
760 m4_diversion *diversion = (m4_diversion *) elt;
761 if (diversion->divnum != current_diversion)
762 insert_diversion_helper (diversion);
763 }
764 gl_oset_iterator_free (&iter);
765}
766
767/*-------------------------------------------------------------.
768| Produce all diversion information in frozen format on FILE. |
769`-------------------------------------------------------------*/
770
771void
772freeze_diversions (FILE *file)
773{
774 int saved_number;
775 int last_inserted;
776 gl_oset_iterator_t iter;
777 const void *elt;
778
779 saved_number = current_diversion;
780 last_inserted = 0;
781 make_diversion (0);
782 output_file = file; /* kludge in the frozen file */
783
784 iter = gl_oset_iterator (diversion_table);
785 while (gl_oset_iterator_next (&iter, &elt))
786 {
787 m4_diversion *diversion = (m4_diversion *) elt;;
788 if (diversion->size || diversion->u.file)
789 {
790 if (diversion->size)
791 fprintf (file, "D%d,%d\n", diversion->divnum, diversion->used);
792 else
793 {
794 struct stat file_stat;
795 fflush (diversion->u.file);
796 if (fstat (fileno (diversion->u.file), &file_stat) < 0)
797 M4ERROR ((EXIT_FAILURE, errno, "cannot stat diversion"));
798 if (file_stat.st_size < 0
799 || file_stat.st_size != (unsigned long int) file_stat.st_size)
800 M4ERROR ((EXIT_FAILURE, 0, "diversion too large"));
801 fprintf (file, "D%d,%lu", diversion->divnum,
802 (unsigned long int) file_stat.st_size);
803 }
804
805 insert_diversion_helper (diversion);
806 putc ('\n', file);
807
808 last_inserted = diversion->divnum;
809 }
810 }
811 gl_oset_iterator_free (&iter);
812
813 /* Save the active diversion number, if not already. */
814
815 if (saved_number != last_inserted)
816 fprintf (file, "D%d,0\n\n", saved_number);
817}
Note: See TracBrowser for help on using the repository browser.