1 /* GNU m4 -- A simple macro processor
2 
3    Copyright (C) 1989-1994, 2004-2014, 2016-2017, 2020-2021 Free
4    Software Foundation, Inc.
5 
6    This file is part of GNU M4.
7 
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.
12 
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.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <https://www.gnu.org/licenses/>.
20 */
21 
22 #include "m4.h"
23 
24 #include <limits.h>
25 #include <sys/stat.h>
26 
27 #include "gl_avltree_oset.h"
28 #include "gl_xoset.h"
29 
30 /* Size of initial in-memory buffer size for diversions.  Small diversions
31    would usually fit in.  */
32 #define INITIAL_BUFFER_SIZE 512
33 
34 /* Maximum value for the total of all in-memory buffer sizes for
35    diversions.  */
36 #define MAXIMUM_TOTAL_SIZE (512 * 1024)
37 
38 /* Size of buffer size to use while copying files.  */
39 #define COPY_BUFFER_SIZE (32 * 512)
40 
41 /* Output functions.  Most of the complexity is for handling cpp like
42    sync lines.
43 
44    This code is fairly entangled with the code in input.c, and maybe it
45    belongs there?  */
46 
47 typedef struct temp_dir m4_temp_dir;
48 
49 /* When part of diversion_table, each struct m4_diversion either
50    represents an open file (zero size, non-NULL u.file), an in-memory
51    buffer (non-zero size, non-NULL u.buffer), or an unused placeholder
52    diversion (zero size, u is NULL, non-zero used indicates that a
53    file has been created).  When not part of diversion_table, u.next
54    is a pointer to the free_list chain.  */
55 
56 typedef struct m4_diversion m4_diversion;
57 
58 struct m4_diversion
59   {
60     union
61       {
62         FILE *file;             /* Diversion file on disk.  */
63         char *buffer;           /* Malloc'd diversion buffer.  */
64         m4_diversion *next;     /* Free-list pointer */
65       } u;
66     int divnum;                 /* Which diversion this represents.  */
67     int size;                   /* Usable size before reallocation.  */
68     int used;                   /* Used buffer length, or tmp file exists.  */
69   };
70 
71 /* Table of diversions 1 through INT_MAX.  */
72 static gl_oset_t diversion_table;
73 
74 /* Diversion 0 (not part of diversion_table).  */
75 static m4_diversion div0;
76 
77 /* Linked list of reclaimed diversion storage.  */
78 static m4_diversion *free_list;
79 
80 /* Obstack from which diversion storage is allocated.  */
81 static struct obstack diversion_storage;
82 
83 /* Total size of all in-memory buffer sizes.  */
84 static int total_buffer_size;
85 
86 /* The number of the currently active diversion.  This variable is
87    maintained for the `divnum' builtin function.  */
88 int current_diversion;
89 
90 /* Current output diversion, NULL if output is being currently
91    discarded.  output_diversion->u is guaranteed non-NULL except when
92    the diversion has never been used; use size to determine if it is a
93    malloc'd buffer or a FILE.  output_diversion->used is 0 if u.file
94    is stdout, and non-zero if this is a malloc'd buffer or a temporary
95    diversion file.  */
96 static m4_diversion *output_diversion;
97 
98 /* Cache of output_diversion->u.file, only valid when
99    output_diversion->size is 0.  */
100 static FILE *output_file;
101 
102 /* Cache of output_diversion->u.buffer + output_diversion->used, only
103    valid when output_diversion->size is non-zero.  */
104 static char *output_cursor;
105 
106 /* Cache of output_diversion->size - output_diversion->used, only
107    valid when output_diversion->size is non-zero.  */
108 static int output_unused;
109 
110 /* Number of input line we are generating output for.  */
111 int output_current_line;
112 
113 /* Temporary directory holding all spilled diversion files.  */
114 static m4_temp_dir *output_temp_dir;
115 
116 /* Cache of most recently used spilled diversion files.  */
117 static FILE *tmp_file1;
118 static FILE *tmp_file2;
119 
120 /* Diversions that own tmp_file, or 0.  */
121 static int tmp_file1_owner;
122 static int tmp_file2_owner;
123 
124 /* True if tmp_file2 is more recently used.  */
125 static bool tmp_file2_recent;
126 
127 
128 /* Internal routines.  */
129 
130 /* Callback for comparing list elements ELT1 and ELT2 for order in
131    diversion_table.  */
132 static int
cmp_diversion_CB(const void * elt1,const void * elt2)133 cmp_diversion_CB (const void *elt1, const void *elt2)
134 {
135   const m4_diversion *d1 = (const m4_diversion *) elt1;
136   const m4_diversion *d2 = (const m4_diversion *) elt2;
137   /* No need to worry about overflow, since we don't create diversions
138      with negative divnum.  */
139   return d1->divnum - d2->divnum;
140 }
141 
142 /* Callback for comparing list element ELT against THRESHOLD.  */
143 static bool
threshold_diversion_CB(const void * elt,const void * threshold)144 threshold_diversion_CB (const void *elt, const void *threshold)
145 {
146   const m4_diversion *diversion = (const m4_diversion *) elt;
147   /* No need to worry about overflow, since we don't create diversions
148      with negative divnum.  */
149   return diversion->divnum >= *(const int *) threshold;
150 }
151 
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
cleanup_tmpfile(void)156 cleanup_tmpfile (void)
157 {
158   /* Close any open diversions.  */
159   bool fail = false;
160 
161   if (diversion_table)
162     {
163       const void *elt;
164       gl_oset_iterator_t iter = gl_oset_iterator (diversion_table);
165       while (gl_oset_iterator_next (&iter, &elt))
166         {
167           m4_diversion *diversion = (m4_diversion *) elt;
168           if (!diversion->size && diversion->u.file
169               && close_stream_temp (diversion->u.file) != 0)
170             {
171               M4ERROR ((0, errno,
172                         _("cannot clean temporary file for diversion")));
173               fail = true;
174             }
175         }
176       gl_oset_iterator_free (&iter);
177     }
178 
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);
184 }
185 
186 /* Convert DIVNUM into a temporary file name for use in m4_tmp*.  */
187 static const char *
m4_tmpname(int divnum)188 m4_tmpname (int divnum)
189 {
190   static char *buffer;
191   static char *tail;
192   if (buffer == NULL)
193     {
194       tail = xasprintf ("%s/m4-%d", output_temp_dir->dir_name, INT_MAX);
195       buffer = (char *) obstack_copy0 (&diversion_storage, tail,
196                                        strlen (tail));
197       free (tail);
198       tail = strrchr (buffer, '-') + 1;
199     }
200   assert (0 < divnum);
201   sprintf (tail, "%d", divnum);
202   return buffer;
203 }
204 
205 /* Create a temporary file for diversion DIVNUM open for reading and
206    writing in a secure temp directory.  The file will be automatically
207    closed and deleted on a fatal signal.  The file can be closed and
208    reopened with m4_tmpclose and m4_tmpopen, or moved with
209    m4_tmprename; when finally done with the file, close it with
210    m4_tmpremove.  Exits on failure, so the return value is always an
211    open file.  */
212 static FILE *
m4_tmpfile(int divnum)213 m4_tmpfile (int divnum)
214 {
215   const char *name;
216   FILE *file;
217 
218   if (output_temp_dir == NULL)
219     {
220       output_temp_dir = create_temp_dir ("m4-", NULL, true);
221       if (output_temp_dir == NULL)
222         m4_failure (errno, _("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   file = fopen_temp (name, O_BINARY ? "wb+e" : "w+e", false);
228   if (file == NULL)
229     {
230       unregister_temp_file (output_temp_dir, name);
231       m4_failure (errno, _("cannot create temporary file for diversion"));
232     }
233   return file;
234 }
235 
236 /* Reopen a temporary file for diversion DIVNUM for reading and
237    writing in a secure temp directory.  If REREAD, the file is
238    positioned at offset 0, otherwise the file is positioned at the
239    end.  Exits on failure, so the return value is always an open
240    file.  */
241 static FILE *
m4_tmpopen(int divnum,bool reread)242 m4_tmpopen (int divnum, bool reread)
243 {
244   const char *name;
245   FILE *file;
246 
247   if (tmp_file1_owner == divnum)
248     {
249       if (reread && fseeko (tmp_file1, 0, SEEK_SET) != 0)
250         m4_failure (errno, _("cannot seek within diversion"));
251       tmp_file2_recent = false;
252       return tmp_file1;
253     }
254   else if (tmp_file2_owner == divnum)
255     {
256       if (reread && fseeko (tmp_file2, 0, SEEK_SET) != 0)
257         m4_failure (errno, _("cannot seek within diversion"));
258       tmp_file2_recent = true;
259       return tmp_file2;
260     }
261   name = m4_tmpname (divnum);
262   /* We need update mode, to avoid truncation.  */
263   file = fopen_temp (name, O_BINARY ? "rb+e" : "r+e", false);
264   if (file == NULL)
265     m4_failure (errno, _("cannot create temporary file for diversion"));
266   /* Update mode starts at the beginning of the stream, but sometimes
267      we want the end.  */
268   else if (!reread && fseeko (file, 0, SEEK_END) != 0)
269     m4_failure (errno, _("cannot seek within diversion"));
270   return file;
271 }
272 
273 /* Close, but don't delete, a temporary FILE for diversion DIVNUM.  To
274    reduce the I/O overhead of repeatedly opening and closing the same
275    file, this implementation caches the most recent spilled diversion.
276    On the other hand, keeping every spilled diversion open would run
277    into EMFILE limits.  */
278 static int
m4_tmpclose(FILE * file,int divnum)279 m4_tmpclose (FILE *file, int divnum)
280 {
281   int result = 0;
282   if (divnum != tmp_file1_owner && divnum != tmp_file2_owner)
283     {
284       if (tmp_file2_recent)
285         {
286           if (tmp_file1_owner)
287             result = close_stream_temp (tmp_file1);
288           tmp_file1 = file;
289           tmp_file1_owner = divnum;
290         }
291       else
292         {
293           if (tmp_file2_owner)
294             result = close_stream_temp (tmp_file2);
295           tmp_file2 = file;
296           tmp_file2_owner = divnum;
297         }
298     }
299   return result;
300 }
301 
302 /* Delete a closed temporary FILE for diversion DIVNUM.  */
303 static int
m4_tmpremove(int divnum)304 m4_tmpremove (int divnum)
305 {
306   if (divnum == tmp_file1_owner)
307     {
308       int result = close_stream_temp (tmp_file1);
309       if (result)
310         return result;
311       tmp_file1_owner = 0;
312     }
313   else if (divnum == tmp_file2_owner)
314     {
315       int result = close_stream_temp (tmp_file2);
316       if (result)
317         return result;
318       tmp_file2_owner = 0;
319     }
320   return cleanup_temp_file (output_temp_dir, m4_tmpname (divnum));
321 }
322 
323 /* Transfer the temporary file for diversion OLDNUM to the previously
324    unused diversion NEWNUM.  Return an open stream visiting the new
325    temporary file, positioned at the end, or exit on failure.  */
326 static FILE*
m4_tmprename(int oldnum,int newnum)327 m4_tmprename (int oldnum, int newnum)
328 {
329   /* m4_tmpname reuses its return buffer.  */
330   char *oldname = xstrdup (m4_tmpname (oldnum));
331   const char *newname = m4_tmpname (newnum);
332   register_temp_file (output_temp_dir, newname);
333   if (oldnum == tmp_file1_owner)
334     {
335       /* Be careful of mingw, which can't rename an open file.  */
336       if (RENAME_OPEN_FILE_WORKS)
337         tmp_file1_owner = newnum;
338       else
339         {
340           if (close_stream_temp (tmp_file1))
341             m4_failure (errno, _("cannot close temporary file for diversion"));
342           tmp_file1_owner = 0;
343         }
344     }
345   else if (oldnum == tmp_file2_owner)
346     {
347       /* Be careful of mingw, which can't rename an open file.  */
348       if (RENAME_OPEN_FILE_WORKS)
349         tmp_file2_owner = newnum;
350       else
351         {
352           if (close_stream_temp (tmp_file2))
353             m4_failure (errno, _("cannot close temporary file for diversion"));
354           tmp_file2_owner = 0;
355         }
356     }
357   /* Either it is safe to rename an open file, or no one should have
358      oldname open at this point.  */
359   if (rename (oldname, newname))
360     m4_failure (errno, _("cannot create temporary file for diversion"));
361   unregister_temp_file (output_temp_dir, oldname);
362   free (oldname);
363   return m4_tmpopen (newnum, false);
364 }
365 
366 
367 /*------------------------.
368 | Output initialization.  |
369 `------------------------*/
370 
371 void
output_init(void)372 output_init (void)
373 {
374   diversion_table = gl_oset_create_empty (GL_AVLTREE_OSET, cmp_diversion_CB,
375                                           NULL);
376   div0.u.file = stdout;
377   output_diversion = &div0;
378   output_file = stdout;
379   obstack_init (&diversion_storage);
380 }
381 
382 void
output_exit(void)383 output_exit (void)
384 {
385   /* Order is important, since we may have registered cleanup_tmpfile
386      as an atexit handler, and it must not traverse stale memory.  */
387   gl_oset_t table = diversion_table;
388   if (tmp_file1_owner)
389     m4_tmpremove (tmp_file1_owner);
390   if (tmp_file2_owner)
391     m4_tmpremove (tmp_file2_owner);
392   diversion_table = NULL;
393   gl_oset_free (table);
394   obstack_free (&diversion_storage, NULL);
395 }
396 
397 /*----------------------------------------------------------------.
398 | Reorganize in-memory diversion buffers so the current diversion |
399 | can accomodate LENGTH more characters without further           |
400 | reorganization.  The current diversion buffer is made bigger if |
401 | possible.  But to make room for a bigger buffer, one of the     |
402 | in-memory diversion buffers might have to be flushed to a newly |
403 | created temporary file.  This flushed buffer might well be the  |
404 | current one.                                                    |
405 `----------------------------------------------------------------*/
406 
407 static void
make_room_for(int length)408 make_room_for (int length)
409 {
410   int wanted_size;
411   m4_diversion *selected_diversion = NULL;
412 
413   /* Compute needed size for in-memory buffer.  Diversions in-memory
414      buffers start at 0 bytes, then 512, then keep doubling until it is
415      decided to flush them to disk.  */
416 
417   output_diversion->used = output_diversion->size - output_unused;
418 
419   for (wanted_size = output_diversion->size;
420        wanted_size < output_diversion->used + length;
421        wanted_size = wanted_size == 0 ? INITIAL_BUFFER_SIZE : wanted_size * 2)
422     ;
423 
424   /* Check if we are exceeding the maximum amount of buffer memory.  */
425 
426   if (total_buffer_size - output_diversion->size + wanted_size
427       > MAXIMUM_TOTAL_SIZE)
428     {
429       int selected_used;
430       char *selected_buffer;
431       m4_diversion *diversion;
432       int count;
433       gl_oset_iterator_t iter;
434       const void *elt;
435 
436       /* Find out the buffer having most data, in view of flushing it to
437          disk.  Fake the current buffer as having already received the
438          projected data, while making the selection.  So, if it is
439          selected indeed, we will flush it smaller, before it grows.  */
440 
441       selected_diversion = output_diversion;
442       selected_used = output_diversion->used + length;
443 
444       iter = gl_oset_iterator (diversion_table);
445       while (gl_oset_iterator_next (&iter, &elt))
446         {
447           diversion = (m4_diversion *) elt;
448           if (diversion->used > selected_used)
449             {
450               selected_diversion = diversion;
451               selected_used = diversion->used;
452             }
453         }
454       gl_oset_iterator_free (&iter);
455 
456       /* Create a temporary file, write the in-memory buffer of the
457          diversion to this file, then release the buffer.  Zero the
458          diversion before doing anything that can exit () (including
459          m4_tmpfile), so that the atexit handler doesn't try to close
460          a garbage pointer as a file.  */
461 
462       selected_buffer = selected_diversion->u.buffer;
463       total_buffer_size -= selected_diversion->size;
464       selected_diversion->size = 0;
465       selected_diversion->u.file = NULL;
466       selected_diversion->u.file = m4_tmpfile (selected_diversion->divnum);
467 
468       if (selected_diversion->used > 0)
469         {
470           count = fwrite (selected_buffer, (size_t) selected_diversion->used,
471                           1, selected_diversion->u.file);
472           if (count != 1)
473             m4_failure (errno,
474                         _("ERROR: cannot flush diversion to temporary file"));
475         }
476 
477       /* Reclaim the buffer space for other diversions.  */
478 
479       free (selected_buffer);
480       selected_diversion->used = 1;
481     }
482 
483   /* Reload output_file, just in case the flushed diversion was current.  */
484 
485   if (output_diversion == selected_diversion)
486     {
487       /* The flushed diversion was current indeed.  */
488 
489       output_file = output_diversion->u.file;
490       output_cursor = NULL;
491       output_unused = 0;
492     }
493   else
494     {
495       /* Close any selected file since it is not the current diversion.  */
496       if (selected_diversion)
497         {
498           FILE *file = selected_diversion->u.file;
499           selected_diversion->u.file = NULL;
500           if (m4_tmpclose (file, selected_diversion->divnum) != 0)
501             m4_error (0, errno,
502                       _("cannot close temporary file for diversion"));
503         }
504 
505       /* The current buffer may be safely reallocated.  */
506       {
507         char *buffer = output_diversion->u.buffer;
508         output_diversion->u.buffer = xcharalloc ((size_t) wanted_size);
509         memcpy (output_diversion->u.buffer, buffer, output_diversion->used);
510         free (buffer);
511       }
512 
513       total_buffer_size += wanted_size - output_diversion->size;
514       output_diversion->size = wanted_size;
515 
516       output_cursor = output_diversion->u.buffer + output_diversion->used;
517       output_unused = wanted_size - output_diversion->used;
518     }
519 }
520 
521 /*--------------------------------------------------------------.
522 | Output one character CHAR, when it is known that it goes to a |
523 | diversion file or an in-memory diversion buffer.              |
524 `--------------------------------------------------------------*/
525 
526 #define OUTPUT_CHARACTER(Char) \
527   if (output_file)                                                      \
528     putc ((Char), output_file);                                         \
529   else if (output_unused == 0)                                          \
530     output_character_helper ((Char));                                   \
531   else                                                                  \
532     (output_unused--, *output_cursor++ = (Char))
533 
534 static void
output_character_helper(int character)535 output_character_helper (int character)
536 {
537   make_room_for (1);
538 
539   if (output_file)
540     putc (character, output_file);
541   else
542     {
543       *output_cursor++ = character;
544       output_unused--;
545     }
546 }
547 
548 /*-------------------------------------------------------------------.
549 | Output one TEXT having LENGTH characters, when it is known that it |
550 | goes to a diversion file or an in-memory diversion buffer.         |
551 `-------------------------------------------------------------------*/
552 
553 void
output_text(const char * text,int length)554 output_text (const char *text, int length)
555 {
556   int count;
557 
558   if (!output_diversion || !length)
559     return;
560 
561   if (!output_file && length > output_unused)
562     make_room_for (length);
563 
564   if (output_file)
565     {
566       count = fwrite (text, length, 1, output_file);
567       if (count != 1)
568         m4_failure (errno, _("ERROR: copying inserted file"));
569     }
570   else
571     {
572       memcpy (output_cursor, text, (size_t) length);
573       output_cursor += length;
574       output_unused -= length;
575     }
576 }
577 
578 /*--------------------------------------------------------------------.
579 | Add some text into an obstack OBS, taken from TEXT, having LENGTH   |
580 | characters.  If OBS is NULL, output the text to an external file    |
581 | or an in-memory diversion buffer instead.  If OBS is NULL, and      |
582 | there is no output file, the text is discarded.  LINE is the line   |
583 | where the token starts (not necessarily current_line, in the case   |
584 | of multiline tokens).                                               |
585 |                                                                     |
586 | If we are generating sync lines, the output has to be examined,     |
587 | because we need to know how much output each input line generates.  |
588 | In general, sync lines are output whenever a single input lines     |
589 | generates several output lines, or when several input lines do not  |
590 | generate any output.                                                |
591 `--------------------------------------------------------------------*/
592 
593 void
shipout_text(struct obstack * obs,const char * text,int length,int line)594 shipout_text (struct obstack *obs, const char *text, int length, int line)
595 {
596   static bool start_of_output_line = true;
597   const char *cursor;
598 
599   /* If output goes to an obstack, merely add TEXT to it.  */
600 
601   if (obs != NULL)
602     {
603       obstack_grow (obs, text, length);
604       return;
605     }
606 
607   /* Do nothing if TEXT should be discarded.  */
608 
609   if (output_diversion == NULL)
610     return;
611 
612   /* Output TEXT to a file, or in-memory diversion buffer.  */
613 
614   if (!sync_output)
615     switch (length)
616       {
617 
618         /* In-line short texts.  */
619 
620       case 8: OUTPUT_CHARACTER (*text); text++; FALLTHROUGH;
621       case 7: OUTPUT_CHARACTER (*text); text++; FALLTHROUGH;
622       case 6: OUTPUT_CHARACTER (*text); text++; FALLTHROUGH;
623       case 5: OUTPUT_CHARACTER (*text); text++; FALLTHROUGH;
624       case 4: OUTPUT_CHARACTER (*text); text++; FALLTHROUGH;
625       case 3: OUTPUT_CHARACTER (*text); text++; FALLTHROUGH;
626       case 2: OUTPUT_CHARACTER (*text); text++; FALLTHROUGH;
627       case 1: OUTPUT_CHARACTER (*text); FALLTHROUGH;
628       case 0:
629         return;
630 
631         /* Optimize longer texts.  */
632 
633       default:
634         output_text (text, length);
635       }
636   else
637     {
638       /* Check for syncline only at the start of a token.  Multiline
639          tokens, and tokens that are out of sync but in the middle of
640          the line, must wait until the next raw newline triggers a
641          syncline.  */
642       if (start_of_output_line)
643         {
644           start_of_output_line = false;
645           output_current_line++;
646 #ifdef DEBUG_OUTPUT
647           xfprintf (stderr, "DEBUG: line %d, cur %d, cur out %d\n",
648                    line, current_line, output_current_line);
649 #endif
650 
651           /* Output a `#line NUM' synchronization directive if needed.
652              If output_current_line was previously given a negative
653              value (invalidated), output `#line NUM "FILE"' instead.  */
654 
655           if (output_current_line != line)
656             {
657               OUTPUT_CHARACTER ('#');
658               OUTPUT_CHARACTER ('l');
659               OUTPUT_CHARACTER ('i');
660               OUTPUT_CHARACTER ('n');
661               OUTPUT_CHARACTER ('e');
662               OUTPUT_CHARACTER (' ');
663               for (cursor = ntoa (line, 10); *cursor; cursor++)
664                 OUTPUT_CHARACTER (*cursor);
665               if (output_current_line < 1 && current_file[0] != '\0')
666                 {
667                   OUTPUT_CHARACTER (' ');
668                   OUTPUT_CHARACTER ('"');
669                   for (cursor = current_file; *cursor; cursor++)
670                     OUTPUT_CHARACTER (*cursor);
671                   OUTPUT_CHARACTER ('"');
672                 }
673               OUTPUT_CHARACTER ('\n');
674               output_current_line = line;
675             }
676         }
677 
678       /* Output the token, and track embedded newlines.  */
679       for (; length-- > 0; text++)
680         {
681           if (start_of_output_line)
682             {
683               start_of_output_line = false;
684               output_current_line++;
685 #ifdef DEBUG_OUTPUT
686               xfprintf (stderr, "DEBUG: line %d, cur %d, cur out %d\n",
687                        line, current_line, output_current_line);
688 #endif
689             }
690           OUTPUT_CHARACTER (*text);
691           if (*text == '\n')
692             start_of_output_line = true;
693         }
694     }
695 }
696 
697 /* Functions for use by diversions.  */
698 
699 /*------------------------------------------------------------------.
700 | Make a file for diversion DIVNUM, and install it in the diversion |
701 | table.  Grow the size of the diversion table as needed.           |
702 `------------------------------------------------------------------*/
703 
704 /* The number of possible diversions is limited only by memory and
705    available file descriptors (each overflowing diversion uses one).  */
706 
707 void
make_diversion(int divnum)708 make_diversion (int divnum)
709 {
710   m4_diversion *diversion = NULL;
711 
712   if (current_diversion == divnum)
713     return;
714 
715   if (output_diversion)
716     {
717       if (!output_diversion->size && !output_diversion->u.file)
718         {
719           assert (!output_diversion->used);
720           if (!gl_oset_remove (diversion_table, output_diversion))
721             assert (false);
722           output_diversion->u.next = free_list;
723           free_list = output_diversion;
724         }
725       else if (output_diversion->size)
726         output_diversion->used = output_diversion->size - output_unused;
727       else if (output_diversion->used)
728         {
729           FILE *file = output_diversion->u.file;
730           output_diversion->u.file = NULL;
731           if (m4_tmpclose (file, output_diversion->divnum) != 0)
732             m4_error (0, errno,
733                       _("cannot close temporary file for diversion"));
734         }
735       output_diversion = NULL;
736       output_file = NULL;
737       output_cursor = NULL;
738       output_unused = 0;
739     }
740 
741   current_diversion = divnum;
742 
743   if (divnum < 0)
744     return;
745 
746   if (divnum == 0)
747     diversion = &div0;
748   else
749     {
750       const void *elt;
751       if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB,
752                                   &divnum, &elt))
753         {
754           m4_diversion *temp = (m4_diversion *) elt;
755           if (temp->divnum == divnum)
756             diversion = temp;
757         }
758     }
759   if (diversion == NULL)
760     {
761       /* First time visiting this diversion.  */
762       if (free_list)
763         {
764           diversion = free_list;
765           free_list = diversion->u.next;
766         }
767       else
768         {
769           diversion = (m4_diversion *) obstack_alloc (&diversion_storage,
770                                                       sizeof *diversion);
771           diversion->size = 0;
772           diversion->used = 0;
773         }
774       diversion->u.file = NULL;
775       diversion->divnum = divnum;
776       gl_oset_add (diversion_table, diversion);
777     }
778 
779   output_diversion = diversion;
780   if (output_diversion->size)
781     {
782       output_cursor = output_diversion->u.buffer + output_diversion->used;
783       output_unused = output_diversion->size - output_diversion->used;
784     }
785   else
786     {
787       if (!output_diversion->u.file && output_diversion->used)
788         output_diversion->u.file = m4_tmpopen (output_diversion->divnum,
789                                                false);
790       output_file = output_diversion->u.file;
791     }
792   output_current_line = -1;
793 }
794 
795 /*-------------------------------------------------------------------.
796 | Insert a FILE into the current output file, in the same manner     |
797 | diversions are handled.  This allows files to be included, without |
798 | having them rescanned by m4.                                       |
799 `-------------------------------------------------------------------*/
800 
801 void
insert_file(FILE * file)802 insert_file (FILE *file)
803 {
804   static char buffer[COPY_BUFFER_SIZE];
805   size_t length;
806 
807   /* Optimize out inserting into a sink.  */
808   if (!output_diversion)
809     return;
810 
811   /* Insert output by big chunks.  */
812   while (1)
813     {
814       length = fread (buffer, 1, sizeof buffer, file);
815       if (ferror (file))
816         m4_failure (errno, _("error reading inserted file"));
817       if (length == 0)
818         break;
819       output_text (buffer, length);
820     }
821 }
822 
823 /*-------------------------------------------------------------------.
824 | Insert DIVERSION (but not div0) into the current output file.  The |
825 | diversion is NOT placed on the expansion obstack, because it must  |
826 | not be rescanned.  When the file is closed, it is deleted by the   |
827 | system.                                                            |
828 `-------------------------------------------------------------------*/
829 
830 static void
insert_diversion_helper(m4_diversion * diversion)831 insert_diversion_helper (m4_diversion *diversion)
832 {
833   /* Effectively undivert only if an output stream is active.  */
834   if (output_diversion)
835     {
836       if (diversion->size)
837         {
838           if (!output_diversion->u.file)
839             {
840               /* Transferring diversion metadata is faster than
841                  copying contents.  */
842               assert (!output_diversion->used && output_diversion != &div0
843                       && !output_file);
844               output_diversion->u.buffer = diversion->u.buffer;
845               output_diversion->size = diversion->size;
846               output_cursor = diversion->u.buffer + diversion->used;
847               output_unused = diversion->size - diversion->used;
848               diversion->u.buffer = NULL;
849             }
850           else
851             {
852               /* Avoid double-charging the total in-memory size when
853                  transferring from one in-memory diversion to
854                  another.  */
855               total_buffer_size -= diversion->size;
856               output_text (diversion->u.buffer, diversion->used);
857             }
858         }
859       else if (!output_diversion->u.file)
860         {
861           /* Transferring diversion metadata is faster than copying
862              contents.  */
863           assert (!output_diversion->used && output_diversion != &div0
864                   && !output_file);
865           output_diversion->u.file = m4_tmprename (diversion->divnum,
866                                                    output_diversion->divnum);
867           output_diversion->used = 1;
868           output_file = output_diversion->u.file;
869           diversion->u.file = NULL;
870           diversion->size = 1;
871         }
872       else
873         {
874           if (!diversion->u.file)
875             diversion->u.file = m4_tmpopen (diversion->divnum, true);
876           insert_file (diversion->u.file);
877         }
878 
879       output_current_line = -1;
880     }
881 
882   /* Return all space used by the diversion.  */
883   if (diversion->size)
884     {
885       if (!output_diversion)
886         total_buffer_size -= diversion->size;
887       free (diversion->u.buffer);
888       diversion->size = 0;
889     }
890   else
891     {
892       if (diversion->u.file)
893         {
894           FILE *file = diversion->u.file;
895           diversion->u.file = NULL;
896           if (m4_tmpclose (file, diversion->divnum) != 0)
897             m4_error (0, errno,
898                       _("cannot clean temporary file for diversion"));
899         }
900       if (m4_tmpremove (diversion->divnum) != 0)
901         M4ERROR ((0, errno, _("cannot clean temporary file for diversion")));
902     }
903   diversion->used = 0;
904   gl_oset_remove (diversion_table, diversion);
905   diversion->u.next = free_list;
906   free_list = diversion;
907 }
908 
909 /*------------------------------------------------------------------.
910 | Insert diversion number DIVNUM into the current output file.  The |
911 | diversion is NOT placed on the expansion obstack, because it must |
912 | not be rescanned.  When the file is closed, it is deleted by the  |
913 | system.                                                           |
914 `------------------------------------------------------------------*/
915 
916 void
insert_diversion(int divnum)917 insert_diversion (int divnum)
918 {
919   const void *elt;
920 
921   /* Do not care about nonexistent diversions, and undiverting stdout
922      or self is a no-op.  */
923   if (divnum <= 0 || current_diversion == divnum)
924     return;
925   if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB,
926                               &divnum, &elt))
927     {
928       m4_diversion *diversion = (m4_diversion *) elt;
929       if (diversion->divnum == divnum)
930         insert_diversion_helper (diversion);
931     }
932 }
933 
934 /*----------------------------------------------------------------.
935 | Get back all diversions.  This is done just before exiting from |
936 | main, and from m4_undivert (), if called without arguments.     |
937 `----------------------------------------------------------------*/
938 
939 void
undivert_all(void)940 undivert_all (void)
941 {
942   const void *elt;
943   gl_oset_iterator_t iter = gl_oset_iterator (diversion_table);
944   while (gl_oset_iterator_next (&iter, &elt))
945     {
946       m4_diversion *diversion = (m4_diversion *) elt;
947       if (diversion->divnum != current_diversion)
948         insert_diversion_helper (diversion);
949     }
950   gl_oset_iterator_free (&iter);
951 }
952 
953 /*-------------------------------------------------------------.
954 | Produce all diversion information in frozen format on FILE.  |
955 `-------------------------------------------------------------*/
956 
957 void
freeze_diversions(FILE * file)958 freeze_diversions (FILE *file)
959 {
960   int saved_number;
961   int last_inserted;
962   gl_oset_iterator_t iter;
963   const void *elt;
964 
965   saved_number = current_diversion;
966   last_inserted = 0;
967   make_diversion (0);
968   output_file = file; /* kludge in the frozen file */
969 
970   iter = gl_oset_iterator (diversion_table);
971   while (gl_oset_iterator_next (&iter, &elt))
972     {
973       m4_diversion *diversion = (m4_diversion *) elt;
974       if (diversion->size || diversion->used)
975         {
976           if (diversion->size)
977             xfprintf (file, "D%d,%d\n", diversion->divnum, diversion->used);
978           else
979             {
980               struct stat file_stat;
981               diversion->u.file = m4_tmpopen (diversion->divnum, true);
982               if (fstat (fileno (diversion->u.file), &file_stat) < 0)
983                 m4_failure (errno, _("cannot stat diversion"));
984               if (file_stat.st_size < 0
985                   || (file_stat.st_size + 0UL
986                       != (unsigned long int) file_stat.st_size))
987                 m4_failure (0, _("diversion too large"));
988               xfprintf (file, "D%d,%lu\n", diversion->divnum,
989                         (unsigned long int) file_stat.st_size);
990             }
991 
992           insert_diversion_helper (diversion);
993           putc ('\n', file);
994 
995           last_inserted = diversion->divnum;
996         }
997     }
998   gl_oset_iterator_free (&iter);
999 
1000   /* Save the active diversion number, if not already.  */
1001 
1002   if (saved_number != last_inserted)
1003     xfprintf (file, "D%d,0\n\n", saved_number);
1004 }
1005