Re: add some fd tests to head

Eric Blake <[email protected]>
Newsgroups gmane.comp.gnu.m4.patches
Message-ID <[email protected]>
Eric Blake <ebb9 <at> byu.net> writes:

> 
> 2006-10-06  Eric Blake  <ebb9 <at> byu.net>
> 
> 	Replace uses of tmpfile with clean-temp, since tmpfile is
> 	incompatible with closeout.

This was relatively easy to port to the branch (although it breaks mingw builds 
unless a pending patch of mine is accepted in gnulib).

2006-10-13  Eric Blake  <[email protected]>

	Backport head's usage of clean-temp module, as it is cleaner than
	using tmpfile-safer.
	* m4/gnulib-cache.m4: Augment with 'gnulib-tool --import
	clean-temp closeout', and remove tmpfile-safer.
	* src/m4.h (includes): Adjust.
	(m4_path_search): Rename from path_search, to avoid collision
	with gnulib.
	* src/m4.c (main): Install closeout handler.  Adjust caller.
	(usage): Now done by closeout module.
	* src/builtin.c (m4_m4exit): Likewise.
	(m4_undivert, include): Adjust callers.
	* src/freeze.c (reload_frozen_state): Likewise.
	* src/path.c (m4_path_search): Rename from path_search.
	* src/output.c (output_temp_dir): New variable.
	(cleanup_tmpfile, m4_tmpfile): New functions, from head.
	(insert_diversion, make_room_for): Use them.
	* doc/m4.texinfo (Diversions): Document this, and add a test.
	(Improved fatal_error): Fix typo.
	(Maketemp): Port test to mingw, and no longer hide from
	documentation.
	* NEWS: Document the change in TMPDIR behavior.


Index: NEWS
===================================================================
RCS file: /sources/m4/m4/NEWS,v
retrieving revision 1.1.1.1.2.71
diff -u -r1.1.1.1.2.71 NEWS
--- NEWS	13 Oct 2006 12:59:16 -0000	1.1.1.1.2.71
+++ NEWS	13 Oct 2006 22:20:55 -0000
@@ -22,6 +22,10 @@
   token in an included file.
 * The `builtin' and `indir' macros now transparently handle builtin
   tokens generated by `defn'.
+* When diversions created by the `divert' macro collect enough text that
+  M4 must use temporary files, the environment variable $TMPDIR is now
+  consulted, and a better effort is made to clean up those files in the
+  event of a fatal signal.
 
 Version 1.4.7 - 25 September 2006, by Eric Blake  (CVS version 1.4.6a)
 
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.1.1.1.2.84
diff -u -r1.1.1.1.2.84 m4.texinfo
--- doc/m4.texinfo	13 Oct 2006 12:59:17 -0000	1.1.1.1.2.84
+++ doc/m4.texinfo	13 Oct 2006 22:20:55 -0000
@@ -3240,9 +3240,46 @@
 (512K, currently).  When this maximum is about to be exceeded,
 a temporary file is opened to receive the contents of the biggest
 diversion still in memory, freeing this memory for other diversions.
+When creating the temporary file, @code{m4} honors the value of the
+environment variable @env{TMPDIR}, and falls back to @file{/tmp}.
 So, it is theoretically possible that the number of diversions be
 limited by the number of available file descriptors.
 
+@ignore
+@comment We need to test spilled diversions, but don't need to expose
+@comment this highly repetitive test in the manual.
+
+@example
+divert(`-1')define(`f', `.')
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+divert`'dnl
+len(f)
+@result{}1048576
+divert(`1')
+f
+divert(`-1')undivert
+@end example
+@end ignore
+
 @c FIXME: need some explanation here why this is a useful feature, not
 @c just how you use it.
 
@@ -4394,16 +4431,9 @@
 @code{maketemp}, so a future version of @acronym{GNU} M4 may have
 changes in this area.
 
-@ignore
-@c This test makes sure maketemp gets testsuite coverage, but is
-@c somewhat complex for use in the manual.
 @example
-dnl This test assumes /tmp is a valid directory name, which is not true
-dnl for native Windows.
-ifdef(`__unix__', , `errprint(` skipping: not sure /tmp exists
-')m4exit(`77')')dnl
-define(`file1', maketemp(`/tmp/fooXXXXXX'))dnl
-define(`file2', maketemp(`/tmp/fooXXXXXX'))dnl
+define(`file1', maketemp(`fooXXXXXX'))dnl
+define(`file2', maketemp(`fooXXXXXX'))dnl
 ifelse(file1, file2, `same', `different')
 @result{}different
 syscmd(`rm 'file1 file2)
@@ -4411,7 +4441,6 @@
 sysval
 @result{}0
 @end example
-@end ignore
 
 @node Miscellaneous
 @chapter Miscellaneous builtin macros
@@ -5189,7 +5218,7 @@
 
 @example
 define(`fatal_error',
-  `errprint(ifdef(`__program', `__program__', ``m4'')'dnl
+  `errprint(ifdef(`__program__', `__program__', ``m4'')'dnl
 `:ifelse(__line__, `0', `',
     `__file__:__line__:')` fatal error: $*
 ')m4exit(`1')')
Index: m4/gnulib-cache.m4
===================================================================
RCS file: /sources/m4/m4/m4/Attic/gnulib-cache.m4,v
retrieving revision 1.1.2.15
diff -u -r1.1.2.15 gnulib-cache.m4
--- m4/gnulib-cache.m4	7 Sep 2006 22:48:15 -0000	1.1.2.15
+++ m4/gnulib-cache.m4	13 Oct 2006 22:20:55 -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 cloexec close-
stream error fdl fopen-safer free gendocs getopt gnupload mkstemp obstack regex 
stdlib-safer strtol tmpfile-safer 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 binary-io clean-temp 
cloexec close-stream closeout error fdl fopen-safer free gendocs getopt 
gnupload mkstemp obstack regex stdlib-safer 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 cloexec close-stream error fdl fopen-safer free gendocs 
getopt gnupload mkstemp obstack regex stdlib-safer strtol tmpfile-safer 
unlocked-io verror xalloc xvasprintf])
+gl_MODULES([binary-io clean-temp cloexec close-stream closeout error fdl fopen-
safer free gendocs getopt gnupload mkstemp obstack regex stdlib-safer strtol 
unlocked-io verror xalloc xvasprintf])
 gl_AVOID([])
 gl_SOURCE_BASE([lib])
 gl_M4_BASE([m4])
Index: src/builtin.c
===================================================================
RCS file: /sources/m4/m4/src/Attic/builtin.c,v
retrieving revision 1.1.1.1.2.42
diff -u -r1.1.1.1.2.42 builtin.c
--- src/builtin.c	13 Oct 2006 12:59:17 -0000	1.1.1.1.2.42
+++ src/builtin.c	13 Oct 2006 22:20:55 -0000
@@ -1088,7 +1088,7 @@
 		    "non-numeric argument to builtin `%s'", ARG (0)));
 	else
 	  {
-	    fp = path_search (ARG (i), NULL);
+	    fp = m4_path_search (ARG (i), NULL);
 	    if (fp != NULL)
 	      {
 		insert_file (fp);
@@ -1200,7 +1200,7 @@
   if (bad_argc (argv[0], argc, 2, 2))
     return;
 
-  fp = path_search (ARG (1), &name);
+  fp = m4_path_search (ARG (1), &name);
   if (fp == NULL)
     {
       if (!silent)
@@ -1329,16 +1329,11 @@
      detect any errors it might have encountered.  */
   debug_set_output (NULL);
   debug_flush_files ();
-  if (close_stream (stdout) != 0)
-    {
-      M4ERROR ((warning_status, errno, "write error"));
-      if (exit_code == 0)
-	exit_code = EXIT_FAILURE;
-    }
-  if (close_stream (stderr) != 0 && exit_code == 0)
-    exit_code = EXIT_FAILURE;
-  if (exit_code == 0 && retcode != 0)
+  if (exit_code == EXIT_SUCCESS && retcode != EXIT_SUCCESS)
     exit_code = retcode;
+  /* Propagate non-zero status to atexit handlers.  */
+  if (exit_code != EXIT_SUCCESS)
+    exit_failure = exit_code;
   exit (exit_code);
 }
 
Index: src/freeze.c
===================================================================
RCS file: /sources/m4/m4/src/freeze.c,v
retrieving revision 1.1.1.1.2.12
diff -u -r1.1.1.1.2.12 freeze.c
--- src/freeze.c	30 Jul 2006 03:18:12 -0000	1.1.1.1.2.12
+++ src/freeze.c	13 Oct 2006 22:20:55 -0000
@@ -222,7 +222,7 @@
     }                                                           \
   while (character == '\n')
 
-  file = path_search (name, NULL);
+  file = m4_path_search (name, NULL);
   if (file == NULL)
     M4ERROR ((EXIT_FAILURE, errno, "cannot open %s", name));
 
Index: src/m4.c
===================================================================
RCS file: /sources/m4/m4/src/Attic/m4.c,v
retrieving revision 1.1.1.1.2.33
diff -u -r1.1.1.1.2.33 m4.c
--- src/m4.c	10 Oct 2006 03:54:26 -0000	1.1.1.1.2.33
+++ src/m4.c	13 Oct 2006 22:20:55 -0000
@@ -204,11 +204,6 @@
 ", stdout);
       printf ("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT);
     }
-
-  if (close_stream (stdout) != 0)
-    M4ERROR ((EXIT_FAILURE, errno, "write error"));
-  if (close_stream (stderr) != 0)
-    exit (EXIT_FAILURE); /* Can't really do much else without stderr.  */
   exit (status);
 }
 
@@ -286,6 +281,7 @@
 
   program_name = argv[0];
   retcode = EXIT_SUCCESS;
+  atexit (close_stdout);
 
   include_init ();
   debug_init ();
@@ -421,20 +417,15 @@
 	break;
 
       case VERSION_OPTION:
-	 printf ("%s\n", PACKAGE_STRING);
-	 fputs ("\
+	printf ("%s\n", PACKAGE_STRING);
+	fputs ("\
 Copyright (C) 2006 Free Software Foundation, Inc.\n\
 This is free software; see the source for copying conditions.  There is NO\n\
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
 \n\
 Written by Rene' Seindal.\n\
 ", stdout);
-
-	 if (close_stream (stdout) != 0)
-	   M4ERROR ((EXIT_FAILURE, errno, "write error"));
-	 if (close_stream (stderr) != 0)
-	   exit (EXIT_FAILURE);
-	 exit (EXIT_SUCCESS);
+	exit (EXIT_SUCCESS);
 	break;
 
       case HELP_OPTION:
@@ -532,7 +523,7 @@
 	else
 	  {
 	    const char *name;
-	    fp = path_search (argv[optind], &name);
+	    fp = m4_path_search (argv[optind], &name);
 	    if (fp == NULL)
 	      {
 		error (0, errno, "%s", argv[optind]);
@@ -570,10 +561,5 @@
       make_diversion (0);
       undivert_all ();
     }
-
-  if (close_stream (stdout) != 0)
-    M4ERROR ((EXIT_FAILURE, errno, "write error"));
-  if (close_stream (stderr) != 0)
-    exit (EXIT_FAILURE); /* Can't really do much else without stderr.  */
   exit (retcode);
 }
Index: src/m4.h
===================================================================
RCS file: /sources/m4/m4/src/m4.h,v
retrieving revision 1.1.1.1.2.29
diff -u -r1.1.1.1.2.29 m4.h
--- src/m4.h	7 Sep 2006 22:48:15 -0000	1.1.1.1.2.29
+++ src/m4.h	13 Oct 2006 22:20:55 -0000
@@ -49,16 +49,20 @@
 #include <sys/types.h>
 
 #include "binary-io.h"
+#include "clean-temp.h"
 #include "cloexec.h"
 #include "close-stream.h"
+#include "closeout.h"
 #include "error.h"
 #include "exit.h"
+#include "exitfail.h"
 #include "obstack.h"
 #include "stdio--.h"
 #include "stdlib--.h"
 #include "unistd--.h"
 #include "verror.h"
 #include "xalloc.h"
+#include "xvasprintf.h"
 
 /* If FALSE is defined, we presume TRUE is defined too.  In this case,
    merely typedef boolean as being int.  Or else, define these all.  */
@@ -418,7 +422,7 @@
 void include_init (void);
 void include_env_init (void);
 void add_include_directory (const char *);
-FILE *path_search (const char *, const char **);
+FILE *m4_path_search (const char *, const char **);
 
 /* File: eval.c  --- expression evaluation.  */
 
Index: src/output.c
===================================================================
RCS file: /sources/m4/m4/src/Attic/output.c,v
retrieving revision 1.1.1.1.2.10
diff -u -r1.1.1.1.2.10 output.c
--- src/output.c	18 Aug 2006 23:11:37 -0000	1.1.1.1.2.10
+++ src/output.c	13 Oct 2006 22:20:56 -0000
@@ -76,6 +76,13 @@
 
 /* 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.  |
@@ -99,6 +106,48 @@
   output_unused = 0;
 }
 
+/* Clean up any temporary directory.  Designed for use as an atexit
+   handler, where it is not safe to call exit() recursively; so this
+   calls _exit if a problem is encountered.  */
+static void
+cleanup_tmpfile (void)
+{
+  if (cleanup_temp_dir (output_temp_dir) != 0)
+    _exit (exit_failure);
+}
+
+/* Create a temporary file open for reading and writing in a secure
+   temp directory.  The file will be automatically closed and deleted
+   on a fatal signal.  When done with the file, close it with
+   close_stream_temp.  Exits on failure, so the return value is always
+   an open file.  */
+static FILE *
+m4_tmpfile (void)
+{
+  static unsigned int count;
+  char *name;
+  FILE *file;
+
+  if (output_temp_dir == NULL)
+    {
+      errno = 0;
+      output_temp_dir = create_temp_dir ("m4-", NULL, true);
+      if (output_temp_dir == NULL)
+	M4ERROR ((EXIT_FAILURE, errno,
+		  "cannot create temporary file for diversion"));
+      atexit (cleanup_tmpfile);
+    }
+  name = xasprintf ("%s/m4-%d", output_temp_dir->dir_name, count++);
+  register_temp_file (output_temp_dir, name);
+  errno = 0;
+  file = fopen_temp (name, O_BINARY ? "wb+" : "w+");
+  if (file == NULL)
+    M4ERROR ((EXIT_FAILURE, errno,
+	      "cannot create temporary file for diversion"));
+  free (name);
+  return file;
+}
+
 /*-----------------------------------------------------------------------.
 | Reorganize in-memory diversion buffers so the current diversion can	 |
 | accomodate LENGTH more characters without further reorganization.  The |
@@ -154,10 +203,7 @@
       /* Create a temporary file, write the in-memory buffer of the
 	 diversion to this file, then release the buffer.  */
 
-      selected_diversion->file = tmpfile ();
-      if (selected_diversion->file == NULL)
-	M4ERROR ((EXIT_FAILURE, errno,
-		  "ERROR: cannot create temporary file for diversion"));
+      selected_diversion->file = m4_tmpfile ();
       if (set_cloexec_flag (fileno (selected_diversion->file), true) != 0)
 	M4ERROR ((warning_status, errno,
 		  "Warning: cannot protect diversion across forks"));
@@ -485,7 +531,7 @@
 
   if (diversion->file)
     {
-      fclose (diversion->file);
+      close_stream_temp (diversion->file);
       diversion->file = NULL;
     }
   else if (diversion->buffer)
Index: src/path.c
===================================================================
RCS file: /sources/m4/m4/src/Attic/path.c,v
retrieving revision 1.1.1.1.2.11
diff -u -r1.1.1.1.2.11 path.c
--- src/path.c	10 Oct 2006 11:36:40 -0000	1.1.1.1.2.11
+++ src/path.c	13 Oct 2006 22:20:56 -0000
@@ -111,7 +111,7 @@
    respect to the current working directory.  */
 
 FILE *
-path_search (const char *file, const char **result)
+m4_path_search (const char *file, const char **result)
 {
   FILE *fp;
   includes *incl;
@@ -154,7 +154,7 @@
       strcpy (name + incl->len + 1, file);
 
 #ifdef DEBUG_INCL
-      fprintf (stderr, "path_search (%s) -- trying %s\n", file, name);
+      fprintf (stderr, "m4_path_search (%s) -- trying %s\n", file, name);
 #endif
 
       fp = fopen (name, "r");
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.