]> sjero.net Git - wget/blobdiff - src/log.c
[svn] Don't #define __STDC__ against the compiler's will.
[wget] / src / log.c
index 42a9364d4e4a2aeb5906812683a476af953d94b8..0ac45b76ece43822635b07f709a1b5e676dd0fbd 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1,24 +1,47 @@
 /* Messages logging.
-   Copyright (C) 1998 Free Software Foundation, Inc.
+   Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc.
 
-This file is part of Wget.
+This file is part of GNU Wget.
 
-This program is free software; you can redistribute it and/or modify
+GNU Wget is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 
-This program is distributed in the hope that it will be useful,
+GNU Wget is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+along with Wget; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+In addition, as a special exception, the Free Software Foundation
+gives permission to link the code of its release of Wget with the
+OpenSSL project's "OpenSSL" library (or with modified versions of it
+that use the same license as the "OpenSSL" library), and distribute
+the linked executables.  You must obey the GNU General Public License
+in all respects for all of the code used other than "OpenSSL".  If you
+modify this file, you may extend this exception to your version of the
+file, but you are not obligated to do so.  If you do not wish to do
+so, delete this exception statement from your version.  */
 
 #include <config.h>
 
+/* This allows the architecture-specific .h files to specify the use
+   of stdargs regardless of __STDC__.  */
+#ifndef WGET_USE_STDARG
+/* Use stdarg only if the compiler supports ANSI C and stdarg.h is
+   present.  We check for both because there are configurations where
+   stdarg.h exists, but doesn't work. */
+# ifdef __STDC__
+#  ifdef HAVE_STDARG_H
+#   define WGET_USE_STDARG
+#  endif
+# endif
+#endif /* not WGET_USE_STDARG */
+
 #include <stdio.h>
 #ifdef HAVE_STRING_H
 # include <string.h>
@@ -26,8 +49,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 # include <strings.h>
 #endif
 #include <stdlib.h>
-#ifdef HAVE_STDARG_H
-# define WGET_USE_STDARG
+#ifdef WGET_USE_STDARG
 # include <stdarg.h>
 #else
 # include <varargs.h>
@@ -45,25 +67,213 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 extern int errno;
 #endif
 
-/* We expect no message passed to logprintf() to be bigger than this.
-   Before a message is printed, we make sure that at least this much
-   room is left for printing it.  */
-#define SAVED_LOG_MAXMSG 32768
+/* This file impplement support for "logging".  Logging means printing
+   output, plus several additional features:
+
+   - Cataloguing output by importance.  You can specify that a log
+   message is "verbose" or "debug", and it will not be printed unless
+   in verbose or debug mode, respectively.
 
-/* Maximum allowed growing size.  */
-#define SAVED_LOG_MAXSIZE (10 * 1L << 20)
+   - Redirecting the log to the file.  When Wget's output goes to the
+   terminal, and Wget receives SIGHUP, all further output is
+   redirected to a log file.  When this is the case, Wget can also
+   print the last several lines of "context" to the log file so that
+   it does not begin in the middle of a line.  For this to work, the
+   logging code stores the last several lines of context.  Callers may
+   request for certain output not to be stored.
 
-static char *saved_log;
-/* Size of the current log.  */
-static long saved_log_size;
-/* Offset into the log where we are about to print (size of the
-   used-up part of SAVED_LOG).  */
-static long saved_log_offset;
-/* Whether logging is saved at all.  */
-int save_log_p;
+   - Inhibiting output.  When Wget receives SIGHUP, but redirecting
+   the output fails, logging is inhibited.  */
 
+\f
+/* The file descriptor used for logging.  This is NULL before log_init
+   is called; logging functions log to stderr then.  log_init sets it
+   either to stderr or to a file pointer obtained from fopen().  If
+   logging is inhibited, logfp is set back to NULL. */
 static FILE *logfp;
 
+/* If non-zero, it means logging is inhibited, i.e. nothing is printed
+   or stored.  */
+static int inhibit_logging;
+
+/* Whether the last output lines are stored for use as context.  */
+static int save_context_p;
+
+/* Whether the log is flushed after each command. */
+static int flush_log_p = 1;
+
+/* Whether any output has been received while flush_log_p was 0. */
+static int needs_flushing;
+
+/* In the event of a hang-up, and if its output was on a TTY, Wget
+   redirects its output to `wget-log'.
+
+   For the convenience of reading this newly-created log, we store the
+   last several lines ("screenful", hence the choice of 24) of Wget
+   output, and dump them as context when the time comes.  */
+#define SAVED_LOG_LINES 24
+
+/* log_lines is a circular buffer that stores SAVED_LOG_LINES lines of
+   output.  log_line_current always points to the position in the
+   buffer that will be written to next.  When log_line_current reaches
+   SAVED_LOG_LINES, it is reset to zero.
+
+   The problem here is that we'd have to either (re)allocate and free
+   strings all the time, or limit the lines to an arbitrary number of
+   characters.  Instead of settling for either of these, we do both:
+   if the line is smaller than a certain "usual" line length (128
+   chars by default), a preallocated memory is used.  The rare lines
+   that are longer than 128 characters are malloc'ed and freed
+   separately.  This gives good performance with minimum memory
+   consumption and fragmentation.  */
+
+#define STATIC_LENGTH 128
+
+static struct log_ln {
+  char static_line[STATIC_LENGTH + 1]; /* statically allocated
+                                          line. */
+  char *malloced_line;         /* malloc'ed line, for lines of output
+                                   larger than 80 characters. */
+  char *content;               /* this points either to malloced_line
+                                   or to the appropriate static_line.
+                                   If this is NULL, it means the line
+                                   has not yet been used. */
+} log_lines[SAVED_LOG_LINES];
+
+/* The current position in the ring. */
+static int log_line_current = -1;
+
+/* Whether the most recently written line was "trailing", i.e. did not
+   finish with \n.  This is an important piece of information because
+   the code is always careful to append data to trailing lines, rather
+   than create new ones.  */
+static int trailing_line;
+
+static void check_redirect_output PARAMS ((void));
+\f
+#define ROT_ADVANCE(num) do {                  \
+  if (++num >= SAVED_LOG_LINES)                        \
+    num = 0;                                   \
+} while (0)
+
+/* Free the log line index with NUM.  This calls free on
+   ln->malloced_line if it's non-NULL, and it also resets
+   ln->malloced_line and ln->content to NULL.  */
+
+static void
+free_log_line (int num)
+{
+  struct log_ln *ln = log_lines + num;
+  if (ln->malloced_line)
+    {
+      xfree (ln->malloced_line);
+      ln->malloced_line = NULL;
+    }
+  ln->content = NULL;
+}
+
+/* Append bytes in the range [start, end) to one line in the log.  The
+   region is not supposed to contain newlines, except for the last
+   character (at end[-1]).  */
+
+static void
+saved_append_1 (const char *start, const char *end)
+{
+  int len = end - start;
+  if (!len)
+    return;
+
+  /* First, check whether we need to append to an existing line or to
+     create a new one.  */
+  if (!trailing_line)
+    {
+      /* Create a new line. */
+      struct log_ln *ln;
+
+      if (log_line_current == -1)
+       log_line_current = 0;
+      else
+       free_log_line (log_line_current);
+      ln = log_lines + log_line_current;
+      if (len > STATIC_LENGTH)
+       {
+         ln->malloced_line = strdupdelim (start, end);
+         ln->content = ln->malloced_line;
+       }
+      else
+       {
+         memcpy (ln->static_line, start, len);
+         ln->static_line[len] = '\0';
+         ln->content = ln->static_line;
+       }
+    }
+  else
+    {
+      /* Append to the last line.  If the line is malloc'ed, we just
+         call realloc and append the new string.  If the line is
+         static, we have to check whether appending the new string
+         would make it exceed STATIC_LENGTH characters, and if so,
+         convert it to malloc(). */
+      struct log_ln *ln = log_lines + log_line_current;
+      if (ln->malloced_line)
+       {
+         /* Resize malloc'ed line and append. */
+         int old_len = strlen (ln->malloced_line);
+         ln->malloced_line = xrealloc (ln->malloced_line, old_len + len + 1);
+         memcpy (ln->malloced_line + old_len, start, len);
+         ln->malloced_line[old_len + len] = '\0';
+         /* might have changed due to realloc */
+         ln->content = ln->malloced_line;
+       }
+      else
+       {
+         int old_len = strlen (ln->static_line);
+         if (old_len + len > STATIC_LENGTH)
+           {
+             /* Allocate memory and concatenate the old and the new
+                 contents. */
+             ln->malloced_line = xmalloc (old_len + len + 1);
+             memcpy (ln->malloced_line, ln->static_line,
+                     old_len);
+             memcpy (ln->malloced_line + old_len, start, len);
+             ln->malloced_line[old_len + len] = '\0';
+             ln->content = ln->malloced_line;
+           }
+         else
+           {
+             /* Just append to the old, statically allocated
+                 contents.  */
+             memcpy (ln->static_line + old_len, start, len);
+             ln->static_line[old_len + len] = '\0';
+             ln->content = ln->static_line;
+           }
+       }
+    }
+  trailing_line = !(end[-1] == '\n');
+  if (!trailing_line)
+    ROT_ADVANCE (log_line_current);
+}
+
+/* Log the contents of S, as explained above.  If S consists of
+   multiple lines, they are logged separately.  If S does not end with
+   a newline, it will form a "trailing" line, to which things will get
+   appended the next time this function is called.  */
+
+static void
+saved_append (const char *s)
+{
+  while (*s)
+    {
+      const char *end = strchr (s, '\n');
+      if (!end)
+       end = s + strlen (s);
+      else
+       ++end;
+      saved_append_1 (s, end);
+      s = end;
+    }
+}
+\f
 /* Check X against opt.verbose and opt.quiet.  The semantics is as
    follows:
 
@@ -92,82 +302,194 @@ static FILE *logfp;
        return;                                 \
     }
 
-#define CANONICALIZE_LOGFP_OR_RETURN do {      \
-  if (logfp == stdin)                          \
-    return;                                    \
-  else if (!logfp)                             \
-    /* #### Should this ever happen?  */       \
-    logfp = stderr;                            \
-} while (0)
+/* Returns the file descriptor for logging.  This is LOGFP, except if
+   called before log_init, in which case it returns stderr.  This is
+   useful in case someone calls a logging function before log_init.
+
+   If logging is inhibited, return NULL.  */
 
+static FILE *
+get_log_fp (void)
+{
+  if (inhibit_logging)
+    return NULL;
+  if (logfp)
+    return logfp;
+  return stderr;
+}
 \f
+/* Log a literal string S.  The string is logged as-is, without a
+   newline appended.  */
+
 void
 logputs (enum log_options o, const char *s)
 {
+  FILE *fp;
+
+  check_redirect_output ();
+  if (!(fp = get_log_fp ()))
+    return;
   CHECK_VERBOSE (o);
-  CANONICALIZE_LOGFP_OR_RETURN;
 
-  fputs (s, logfp);
-  if (!opt.no_flush)
-    fflush (logfp);
+  fputs (s, fp);
+  if (save_context_p)
+    saved_append (s);
+  if (flush_log_p)
+    logflush ();
+  else
+    needs_flushing = 1;
+}
+
+struct logvprintf_state {
+  char *bigmsg;
+  int expected_size;
+  int allocated;
+};
 
-  if (save_log_p && saved_log_size < SAVED_LOG_MAXSIZE)
-    {
-      int len = strlen (s);
+/* Print a message to the log.  A copy of message will be saved to
+   saved_log, for later reusal by log_dump_context().
 
-      /* Increase size of SAVED_LOG exponentially.  */
-      DO_REALLOC (saved_log, saved_log_size,
-                 saved_log_offset + len + 1, char);
-      memcpy (saved_log + saved_log_offset, s, len + 1);
-      saved_log_offset += len;
-    }
-}
+   It is not possible to code this function in a "natural" way, using
+   a loop, because of the braindeadness of the varargs API.
+   Specifically, each call to vsnprintf() must be preceded by va_start
+   and followed by va_end.  And this is possible only in the function
+   that contains the `...' declaration.  The alternative would be to
+   use va_copy, but that's not portable.  */
 
-/* Print a message to the log file logfp.  If logfp is NULL, print to
-   stderr.  If logfp is stdin, don't print at all.  A copy of message
-   will be saved to saved_log, for later reusal by dump_log().  */
-static void
-logvprintf (enum log_options o, const char *fmt, va_list args)
+static int
+logvprintf (struct logvprintf_state *state, const char *fmt, va_list args)
 {
-  CHECK_VERBOSE (o);
-  CANONICALIZE_LOGFP_OR_RETURN;
+  char smallmsg[128];
+  char *write_ptr = smallmsg;
+  int available_size = sizeof (smallmsg);
+  int numwritten;
+  FILE *fp = get_log_fp ();
 
-  /* Originally, we first used vfprintf(), and then checked whether
-     the message needs to be stored with vsprintf().  However, Watcom
-     C didn't like ARGS being used twice, so now we first vsprintf()
-     the message, and then fwrite() it to LOGFP.  */
-  if (save_log_p && saved_log_size < SAVED_LOG_MAXSIZE)
+  if (!save_context_p)
     {
-      int len;
-      /* Increase size of `saved_log' exponentially.  */
-      DO_REALLOC (saved_log, saved_log_size,
-                 saved_log_offset + SAVED_LOG_MAXMSG, char);
-      /* Print the message to the log saver...  */
-      vsnprintf (saved_log + saved_log_offset, SAVED_LOG_MAXMSG, fmt, args);
-      /* ...and then dump it to LOGFP.  */
-      len = strlen (saved_log + saved_log_offset);
-      fwrite (saved_log + saved_log_offset, len, 1, logfp);
-      saved_log_offset += len;
-      /* If we ran off the limits and corrupted something, bail out
-        immediately.  */
-      assert (saved_log_size >= saved_log_offset);
+      /* In the simple case just call vfprintf(), to avoid needless
+         allocation and games with vsnprintf(). */
+      vfprintf (fp, fmt, args);
+      goto flush;
     }
+
+  if (state->allocated != 0)
+    {
+      write_ptr = state->bigmsg;
+      available_size = state->allocated;
+    }
+
+  /* The GNU coding standards advise not to rely on the return value
+     of sprintf().  However, vsnprintf() is a relatively new function
+     missing from legacy systems.  Therefore I consider it safe to
+     assume that its return value is meaningful.  On the systems where
+     vsnprintf() is not available, we use the implementation from
+     snprintf.c which does return the correct value.  */
+  numwritten = vsnprintf (write_ptr, available_size, fmt, args);
+
+  /* vsnprintf() will not step over the limit given by available_size.
+     If it fails, it will return either -1 (POSIX?) or the number of
+     characters that *would have* been written, if there had been
+     enough room.  In the former case, we double the available_size
+     and malloc() to get a larger buffer, and try again.  In the
+     latter case, we use the returned information to build a buffer of
+     the correct size.  */
+
+  if (numwritten == -1)
+    {
+      /* Writing failed, and we don't know the needed size.  Try
+        again with doubled size. */
+      int newsize = available_size << 1;
+      state->bigmsg = xrealloc (state->bigmsg, newsize);
+      state->allocated = newsize;
+      return 0;
+    }
+  else if (numwritten >= available_size)
+    {
+      /* Writing failed, but we know exactly how much space we
+        need. */
+      int newsize = numwritten + 1;
+      state->bigmsg = xrealloc (state->bigmsg, newsize);
+      state->allocated = newsize;
+      return 0;
+    }
+
+  /* Writing succeeded. */
+  saved_append (write_ptr);
+  fputs (write_ptr, fp);
+  if (state->bigmsg)
+    xfree (state->bigmsg);
+
+ flush:
+  if (flush_log_p)
+    logflush ();
   else
-    vfprintf (logfp, fmt, args);
+    needs_flushing = 1;
 
-  if (!opt.no_flush)
-    fflush (logfp);
+  return 1;
 }
 
-/* Flush LOGFP.  */
+/* Flush LOGFP.  Useful while flushing is disabled.  */
 void
 logflush (void)
 {
-  CANONICALIZE_LOGFP_OR_RETURN;
-  fflush (logfp);
+  FILE *fp = get_log_fp ();
+  if (fp)
+    fflush (fp);
+  needs_flushing = 0;
 }
 
-/* Portability makes these two functions look like @#%#@$@#$.  */
+/* Enable or disable log flushing. */
+void
+log_set_flush (int flush)
+{
+  if (flush == flush_log_p)
+    return;
+
+  if (flush == 0)
+    {
+      /* Disable flushing by setting flush_log_p to 0. */
+      flush_log_p = 0;
+    }
+  else
+    {
+      /* Reenable flushing.  If anything was printed in no-flush mode,
+        flush the log now.  */
+      if (needs_flushing)
+       logflush ();
+      flush_log_p = 1;
+    }
+}
+
+/* (Temporarily) disable storing log to memory.  Returns the old
+   status of storing, with which this function can be called again to
+   reestablish storing. */
+
+int
+log_set_save_context (int savep)
+{
+  int old = save_context_p;
+  save_context_p = savep;
+  return old;
+}
+
+#ifdef WGET_USE_STDARG
+# define VA_START_1(arg1_type, arg1, args) va_start(args, arg1)
+# define VA_START_2(arg1_type, arg1, arg2_type, arg2, args) va_start(args, arg2)
+#else  /* not WGET_USE_STDARG */
+# define VA_START_1(arg1_type, arg1, args) do {        \
+  va_start (args);                                                     \
+  arg1 = va_arg (args, arg1_type);                                     \
+} while (0)
+# define VA_START_2(arg1_type, arg1, arg2_type, arg2, args) do {       \
+  va_start (args);                                                     \
+  arg1 = va_arg (args, arg1_type);                                     \
+  arg2 = va_arg (args, arg2_type);                                     \
+} while (0)
+#endif /* not WGET_USE_STDARG */
+
+/* Portability with pre-ANSI compilers makes these two functions look
+   like @#%#@$@#$.  */
 
 #ifdef WGET_USE_STDARG
 void
@@ -179,20 +501,31 @@ logprintf (va_alist)
 #endif /* not WGET_USE_STDARG */
 {
   va_list args;
+  struct logvprintf_state lpstate;
+  int done;
+
 #ifndef WGET_USE_STDARG
   enum log_options o;
   const char *fmt;
-#endif
 
-#ifdef WGET_USE_STDARG
-  va_start (args, fmt);
-#else
-  va_start (args);
-  o = va_arg (args, enum log_options);
-  fmt = va_arg (args, char *);
-#endif
-  logvprintf (o, fmt, args);
+  /* Perform a "dry run" of VA_START_2 to get the value of O. */
+  VA_START_2 (enum log_options, o, char *, fmt, args);
   va_end (args);
+#endif
+
+  check_redirect_output ();
+  if (inhibit_logging)
+    return;
+  CHECK_VERBOSE (o);
+
+  memset (&lpstate, '\0', sizeof (lpstate));
+  do
+    {
+      VA_START_2 (enum log_options, o, char *, fmt, args);
+      done = logvprintf (&lpstate, fmt, args);
+      va_end (args);
+    }
+  while (!done);
 }
 
 #ifdef DEBUG
@@ -213,15 +546,21 @@ debug_logprintf (va_alist)
 #ifndef WGET_USE_STDARG
       const char *fmt;
 #endif
+      struct logvprintf_state lpstate;
+      int done;
 
-#ifdef WGET_USE_STDARG
-      va_start (args, fmt);
-#else
-      va_start (args);
-      fmt = va_arg (args, char *);
-#endif
-      logvprintf (LOG_ALWAYS, fmt, args);
-      va_end (args);
+      check_redirect_output ();
+      if (inhibit_logging)
+       return;
+
+      memset (&lpstate, '\0', sizeof (lpstate));
+      do
+       {
+         VA_START_1 (char *, fmt, args);
+         done = logvprintf (&lpstate, fmt, args);
+         va_end (args);
+       }
+      while (!done);
     }
 }
 #endif /* DEBUG */
@@ -242,8 +581,15 @@ log_init (const char *file, int appendp)
     }
   else
     {
+      /* The log goes to stderr to avoid collisions with the output if
+         the user specifies `-O -'.  #### Francois Pinard suggests
+         that it's a better idea to print to stdout by default, and to
+         stderr only if the user actually specifies `-O -'.  He says
+         this inconsistency is harder to document, but is overall
+         easier on the user.  */
       logfp = stderr;
-      /* If the output is a TTY, enable logging, which will make Wget
+
+      /* If the output is a TTY, enable storing, which will make Wget
          remember all the printed messages, to be able to dump them to
          a log file in case SIGHUP or SIGUSR1 is received (or
          Ctrl+Break is pressed under Windows).  */
@@ -253,7 +599,7 @@ log_init (const char *file, int appendp)
 #endif
          )
        {
-         save_log_p = 1;
+         save_context_p = 1;
        }
     }
 }
@@ -263,50 +609,101 @@ log_init (const char *file, int appendp)
 void
 log_close (void)
 {
-  fclose (logfp);
-  save_log_p = 0;
-  FREE_MAYBE (saved_log);
-  saved_log = NULL;
-  saved_log_size = saved_log_offset = 0;
+  int i;
+
+  if (logfp)
+    fclose (logfp);
+  logfp = NULL;
+  inhibit_logging = 1;
+  save_context_p = 0;
+
+  for (i = 0; i < SAVED_LOG_LINES; i++)
+    free_log_line (i);
+  log_line_current = -1;
+  trailing_line = 0;
 }
 
-/* Dump SAVED_LOG using logprintf(), but quit further logging to memory.
-   Also, free the memory occupied by saved_log.  */
+/* Dump saved lines to logfp. */
 static void
-log_dump (void)
+log_dump_context (void)
 {
-  save_log_p = 0;
-  if (!saved_log)
+  int num = log_line_current;
+  FILE *fp = get_log_fp ();
+  if (!fp)
     return;
-  logputs (LOG_ALWAYS, saved_log);
-  free (saved_log);
-  saved_log = NULL;
-  saved_log_size = saved_log_offset = 0;
+
+  if (num == -1)
+    return;
+  if (trailing_line)
+    ROT_ADVANCE (num);
+  do
+    {
+      struct log_ln *ln = log_lines + num;
+      if (ln->content)
+       fputs (ln->content, fp);
+      ROT_ADVANCE (num);
+    }
+  while (num != log_line_current);
+  if (trailing_line)
+    if (log_lines[log_line_current].content)
+      fputs (log_lines[log_line_current].content, fp);
+  fflush (fp);
 }
+\f
+/* When SIGHUP or SIGUSR1 are received, the output is redirected
+   elsewhere.  Such redirection is only allowed once. */
+enum { RR_NONE, RR_REQUESTED, RR_DONE } redirect_request = RR_NONE;
+static const char *redirect_request_signal_name;
 
-/* Redirect output to `wget-log' if opt.lfile is stdout.  MESSIJ is
-   printed on stdout, and should contain *exactly one* `%s', which
-   will be replaced by the log file name.
+/* Redirect output to `wget-log'.  */
 
-   If logging was not enabled, MESSIJ will not be printed.  */
-void
-redirect_output (const char *messij)
+static void
+redirect_output (void)
 {
-  char *logfile;
-
-  if (!save_log_p)
-    return;
-
-  logfile = unique_name (DEFAULT_LOGFILE);
+  char *logfile = unique_name (DEFAULT_LOGFILE);
+  fprintf (stderr, _("\n%s received, redirecting output to `%s'.\n"),
+          redirect_request_signal_name, logfile);
   logfp = fopen (logfile, "w");
   if (!logfp)
     {
-      printf ("%s: %s: %s\n", exec_name, logfile, strerror (errno));
-      /* `stdin' is magic to not print anything.  */
-      logfp = stdin;
+      /* Eek!  Opening the alternate log file has failed.  Nothing we
+         can do but disable printing completely. */
+      fprintf (stderr, _("%s: %s; disabling logging.\n"),
+              logfile, strerror (errno));
+      inhibit_logging = 1;
+    }
+  else
+    {
+      /* Dump the context output to the newly opened log.  */
+      log_dump_context ();
+    }
+  xfree (logfile);
+  save_context_p = 0;
+}
+
+/* Check whether a signal handler requested the output to be
+   redirected. */
+
+static void
+check_redirect_output (void)
+{
+  if (redirect_request == RR_REQUESTED)
+    {
+      redirect_request = RR_DONE;
+      redirect_output ();
     }
-  printf (messij, logfile);
-  free (logfile);
-  /* Dump all the previous messages to LOGFILE.  */
-  log_dump ();
+}
+
+/* Request redirection at a convenient time.  This may be called from
+   a signal handler. */
+
+void
+log_request_redirect_output (const char *signal_name)
+{
+  if (redirect_request == RR_NONE && save_context_p)
+    /* Request output redirection.  The request will be processed by
+       check_redirect_output(), which is called from entry point log
+       functions. */
+    redirect_request = RR_REQUESTED;
+  redirect_request_signal_name = signal_name;
 }