]> sjero.net Git - wget/blobdiff - src/log.c
Fix build when libpsl is not available
[wget] / src / log.c
index 8ef12520e84304e2e700750f07ebe726dd81c87a..3bd583305dedff4d814546ce53d7466ae73a7397 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1,11 +1,12 @@
 /* Messages logging.
-   Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+   2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 
 This file is part of GNU Wget.
 
 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
+the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.
 
 GNU Wget is distributed in the hope that it will be useful,
@@ -14,60 +15,46 @@ 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 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 */
+along with Wget.  If not, see <http://www.gnu.org/licenses/>.
+
+Additional permission under GNU GPL version 3 section 7
+
+If you modify this program, or any covered work, by linking or
+combining it with the OpenSSL project's OpenSSL library (or a
+modified version of that library), containing parts covered by the
+terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
+grants you additional permission to convey the resulting work.
+Corresponding Source for a non-source form of such a combination
+shall include the source code for the parts of OpenSSL used as well
+as that of the covered work.  */
+
+#include "wget.h"
 
 #include <stdio.h>
-#ifdef HAVE_STRING_H
-# include <string.h>
-#else
-# include <strings.h>
-#endif
+#include <string.h>
 #include <stdlib.h>
-#ifdef WGET_USE_STDARG
-# include <stdarg.h>
-#else
-# include <varargs.h>
-#endif
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
+#include <stdarg.h>
+#include <unistd.h>
 #include <assert.h>
 #include <errno.h>
 
-#include "wget.h"
 #include "utils.h"
-
-#ifndef errno
-extern int errno;
-#endif
-
-/* This file impplement support for "logging".  Logging means printing
+#include "log.h"
+
+/* 2005-10-25 SMS.
+   VMS log files are often VFC record format, not stream, so fputs() can
+   produce multiple records, even when there's no newline terminator in
+   the buffer.  The result is unsightly output with spurious newlines.
+   Using fprintf() instead of fputs(), along with inhibiting some
+   fflush() activity below, seems to solve the problem.
+*/
+#ifdef __VMS
+# define FPUTS( s, f) fprintf( (f), "%s", (s))
+#else /* def __VMS */
+# define FPUTS( s, f) fputs( (s), (f))
+#endif /* def __VMS [else] */
+
+/* This file implements support for "logging".  Logging means printing
    output, plus several additional features:
 
    - Cataloguing output by importance.  You can specify that a log
@@ -92,18 +79,22 @@ extern int errno;
    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;
+/* A second file descriptor pointing to the temporary log file for the
+   WARC writer.  If WARC writing is disabled, this is NULL.  */
+static FILE *warclogfp;
+
+/* If true, it means logging is inhibited, i.e. nothing is printed or
+   stored.  */
+static bool inhibit_logging;
 
 /* Whether the last output lines are stored for use as context.  */
-static int save_context_p;
+static bool save_context_p;
 
 /* Whether the log is flushed after each command. */
-static int flush_log_p = 1;
+static bool flush_log_p = true;
 
 /* Whether any output has been received while flush_log_p was 0. */
-static int needs_flushing;
+static bool needs_flushing;
 
 /* In the event of a hang-up, and if its output was on a TTY, Wget
    redirects its output to `wget-log'.
@@ -132,9 +123,9 @@ static int needs_flushing;
 static struct log_ln {
   char static_line[STATIC_LENGTH + 1]; /* statically allocated
                                           line. */
-  char *malloced_line;         /* malloc'ed line, for lines of output
+  char *malloced_line;          /* malloc'ed line, for lines of output
                                    larger than 80 characters. */
-  char *content;               /* this points either to malloced_line
+  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. */
@@ -147,13 +138,13 @@ static int log_line_current = -1;
    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 bool trailing_line;
 
-static void check_redirect_output PARAMS ((void));
+static void check_redirect_output (void);
 \f
-#define ROT_ADVANCE(num) do {                  \
-  if (++num >= SAVED_LOG_LINES)                        \
-    num = 0;                                   \
+#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
@@ -191,21 +182,21 @@ saved_append_1 (const char *start, const char *end)
       struct log_ln *ln;
 
       if (log_line_current == -1)
-       log_line_current = 0;
+        log_line_current = 0;
       else
-       free_log_line (log_line_current);
+        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;
-       }
+        {
+          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;
-       }
+        {
+          memcpy (ln->static_line, start, len);
+          ln->static_line[len] = '\0';
+          ln->content = ln->static_line;
+        }
     }
   else
     {
@@ -216,38 +207,38 @@ saved_append_1 (const char *start, const char *end)
          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;
-       }
+        {
+          /* 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
+        {
+          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
+              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;
-           }
-       }
+              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)
@@ -266,9 +257,9 @@ saved_append (const char *s)
     {
       const char *end = strchr (s, '\n');
       if (!end)
-       end = s + strlen (s);
+        end = s + strlen (s);
       else
-       ++end;
+        ++end;
       saved_append_1 (s, end);
       s = end;
     }
@@ -284,22 +275,26 @@ saved_append (const char *s)
    * LOG_NONVERBOSE - print the message if opt.verbose is zero;
 
    * LOG_VERBOSE - print the message if opt.verbose is non-zero.  */
-#define CHECK_VERBOSE(x)                       \
-  switch (x)                                   \
-    {                                          \
-    case LOG_ALWAYS:                           \
-      break;                                   \
-    case LOG_NOTQUIET:                         \
-      if (opt.quiet)                           \
-       return;                                 \
-      break;                                   \
-    case LOG_NONVERBOSE:                       \
-      if (opt.verbose || opt.quiet)            \
-       return;                                 \
-      break;                                   \
-    case LOG_VERBOSE:                          \
-      if (!opt.verbose)                                \
-       return;                                 \
+#define CHECK_VERBOSE(x)                        \
+  switch (x)                                    \
+    {                                           \
+    case LOG_PROGRESS:                          \
+      if (!opt.show_progress)                   \
+        return;                                 \
+      break;                                    \
+    case LOG_ALWAYS:                            \
+      break;                                    \
+    case LOG_NOTQUIET:                          \
+      if (opt.quiet)                            \
+        return;                                 \
+      break;                                    \
+    case LOG_NONVERBOSE:                        \
+      if (opt.verbose || opt.quiet)             \
+        return;                                 \
+      break;                                    \
+    case LOG_VERBOSE:                           \
+      if (!opt.verbose)                         \
+        return;                                 \
     }
 
 /* Returns the file descriptor for logging.  This is LOGFP, except if
@@ -317,6 +312,31 @@ get_log_fp (void)
     return logfp;
   return stderr;
 }
+
+/* Returns the file descriptor for the secondary log file. This is
+   WARCLOGFP, 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_warc_log_fp (void)
+{
+  if (inhibit_logging)
+    return NULL;
+  if (warclogfp)
+    return warclogfp;
+  return NULL;
+}
+
+/* Sets the file descriptor for the secondary log file.  */
+
+void
+log_set_warc_log_fp (FILE * fp)
+{
+  warclogfp = fp;
+}
 \f
 /* Log a literal string S.  The string is logged as-is, without a
    newline appended.  */
@@ -325,19 +345,23 @@ void
 logputs (enum log_options o, const char *s)
 {
   FILE *fp;
+  FILE *warcfp;
 
   check_redirect_output ();
-  if (!(fp = get_log_fp ()))
+  if ((fp = get_log_fp ()) == NULL)
     return;
+  warcfp = get_warc_log_fp ();
   CHECK_VERBOSE (o);
 
-  fputs (s, fp);
+  FPUTS (s, fp);
+  if (warcfp != NULL)
+    FPUTS (s, warcfp);
   if (save_context_p)
     saved_append (s);
   if (flush_log_p)
     logflush ();
   else
-    needs_flushing = 1;
+    needs_flushing = true;
 }
 
 struct logvprintf_state {
@@ -349,23 +373,29 @@ struct logvprintf_state {
 /* Print a message to the log.  A copy of message will be saved to
    saved_log, for later reusal by log_dump_context().
 
-   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.  */
+   Normally we'd want this function to loop around vsnprintf until
+   sufficient room is allocated, as the Linux man page recommends.
+   However each call to vsnprintf() must be preceded by va_start and
+   followed by va_end.  Since calling va_start/va_end is possible only
+   in the function that contains the `...' declaration, we cannot call
+   vsnprintf more than once.  Therefore this function saves its state
+   to logvprintf_state and signals the parent to call it again.
 
-static int
-logvprintf (struct logvprintf_state *state, const char *fmt, va_list args)
+   (An alternative approach would be to use va_copy, but that's not
+   portable.)  */
+
+static bool
+log_vprintf_internal (struct logvprintf_state *state, const char *fmt,
+                      va_list args)
 {
   char smallmsg[128];
   char *write_ptr = smallmsg;
   int available_size = sizeof (smallmsg);
   int numwritten;
   FILE *fp = get_log_fp ();
+  FILE *warcfp = get_warc_log_fp ();
 
-  if (!save_context_p)
+  if (!save_context_p && warcfp == NULL)
     {
       /* In the simple case just call vfprintf(), to avoid needless
          allocation and games with vsnprintf(). */
@@ -388,35 +418,38 @@ logvprintf (struct logvprintf_state *state, const char *fmt, va_list args)
   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 (C99).  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 it fails, it returns either -1 (older implementations) or the
+     number of characters (not counting the terminating \0) that
+     *would have* been written if there had been enough room (C99).
+     In the former case, we double 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. */
+         again with doubled size. */
       int newsize = available_size << 1;
       state->bigmsg = xrealloc (state->bigmsg, newsize);
       state->allocated = newsize;
-      return 0;
+      return false;
     }
   else if (numwritten >= available_size)
     {
       /* Writing failed, but we know exactly how much space we
-        need. */
+         need. */
       int newsize = numwritten + 1;
       state->bigmsg = xrealloc (state->bigmsg, newsize);
       state->allocated = newsize;
-      return 0;
+      return false;
     }
 
   /* Writing succeeded. */
-  saved_append (write_ptr);
-  fputs (write_ptr, fp);
+  if (save_context_p)
+    saved_append (write_ptr);
+  FPUTS (write_ptr, fp);
+  if (warcfp != NULL)
+    FPUTS (write_ptr, warcfp);
   if (state->bigmsg)
     xfree (state->bigmsg);
 
@@ -424,9 +457,9 @@ logvprintf (struct logvprintf_state *state, const char *fmt, va_list args)
   if (flush_log_p)
     logflush ();
   else
-    needs_flushing = 1;
+    needs_flushing = true;
 
-  return 1;
+  return true;
 }
 
 /* Flush LOGFP.  Useful while flushing is disabled.  */
@@ -434,30 +467,47 @@ void
 logflush (void)
 {
   FILE *fp = get_log_fp ();
+  FILE *warcfp = get_warc_log_fp ();
   if (fp)
-    fflush (fp);
-  needs_flushing = 0;
+    {
+/* 2005-10-25 SMS.
+   On VMS, flush only for a terminal.  See note at FPUTS macro, above.
+*/
+#ifdef __VMS
+      if (isatty( fileno( fp)))
+        {
+          fflush (fp);
+        }
+#else /* def __VMS */
+      fflush (fp);
+#endif /* def __VMS [else] */
+    }
+
+  if (warcfp != NULL)
+    fflush (warcfp);
+
+  needs_flushing = false;
 }
 
 /* Enable or disable log flushing. */
 void
-log_set_flush (int flush)
+log_set_flush (bool flush)
 {
   if (flush == flush_log_p)
     return;
 
-  if (flush == 0)
+  if (flush == false)
     {
       /* Disable flushing by setting flush_log_p to 0. */
-      flush_log_p = 0;
+      flush_log_p = false;
     }
   else
     {
       /* Reenable flushing.  If anything was printed in no-flush mode,
-        flush the log now.  */
+         flush the log now.  */
       if (needs_flushing)
-       logflush ();
-      flush_log_p = 1;
+        logflush ();
+      flush_log_p = true;
     }
 }
 
@@ -465,24 +515,14 @@ log_set_flush (int flush)
    status of storing, with which this function can be called again to
    reestablish storing. */
 
-int
-log_set_save_context (int savep)
+bool
+log_set_save_context (bool savep)
 {
-  int old = save_context_p;
+  bool old = save_context_p;
   save_context_p = savep;
   return old;
 }
 
-/* Handle difference in va_start between pre-ANSI and ANSI C.  Note
-   that we always use `...' in function definitions and let ansi2knr
-   convert it for us.  */
-
-#ifdef WGET_USE_STDARG
-# define VA_START(args, arg1) va_start (args, arg1)
-#else
-# define VA_START(args, ignored) va_start (args)
-#endif
-
 /* Print a message to the screen or to the log.  The first argument
    defines the verbosity of the message, and the rest are as in
    printf(3).  */
@@ -492,26 +532,29 @@ logprintf (enum log_options o, const char *fmt, ...)
 {
   va_list args;
   struct logvprintf_state lpstate;
-  int done;
+  bool done;
 
   check_redirect_output ();
   if (inhibit_logging)
     return;
   CHECK_VERBOSE (o);
 
-  memset (&lpstate, '\0', sizeof (lpstate));
+  xzero (lpstate);
   do
     {
-      VA_START (args, fmt);
-      done = logvprintf (&lpstate, fmt, args);
+      va_start (args, fmt);
+      done = log_vprintf_internal (&lpstate, fmt, args);
       va_end (args);
+
+      if (done && errno == EPIPE)
+        exit (1);
     }
   while (!done);
 }
 
 #ifdef ENABLE_DEBUG
 /* The same as logprintf(), but does anything only if opt.debug is
-   non-zero.  */
+   true.  */
 void
 debug_logprintf (const char *fmt, ...)
 {
@@ -519,19 +562,19 @@ debug_logprintf (const char *fmt, ...)
     {
       va_list args;
       struct logvprintf_state lpstate;
-      int done;
+      bool done;
 
       check_redirect_output ();
       if (inhibit_logging)
-       return;
+        return;
 
-      memset (&lpstate, '\0', sizeof (lpstate));
+      xzero (lpstate);
       do
-       {
-         VA_START (args, fmt);
-         done = logvprintf (&lpstate, fmt, args);
-         va_end (args);
-       }
+        {
+          va_start (args, fmt);
+          done = log_vprintf_internal (&lpstate, fmt, args);
+          va_end (args);
+        }
       while (!done);
     }
 }
@@ -540,16 +583,16 @@ debug_logprintf (const char *fmt, ...)
 /* Open FILE and set up a logging stream.  If FILE cannot be opened,
    exit with status of 1.  */
 void
-log_init (const char *file, int appendp)
+log_init (const char *file, bool appendp)
 {
   if (file)
     {
       logfp = fopen (file, appendp ? "a" : "w");
       if (!logfp)
-       {
-         perror (opt.lfilename);
-         exit (1);
-       }
+        {
+          fprintf (stderr, "%s: %s: %s\n", exec_name, file, strerror (errno));
+          exit (1);
+        }
     }
   else
     {
@@ -561,38 +604,38 @@ log_init (const char *file, int appendp)
          easier on the user.  */
       logfp = stderr;
 
-      /* If the output is a TTY, enable storing, which will make Wget
-         remember the last several 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).  */
       if (1
 #ifdef HAVE_ISATTY
-         && isatty (fileno (logfp))
+          && isatty (fileno (logfp))
 #endif
-         )
-       {
-         save_context_p = 1;
-       }
+          )
+        {
+          /* If the output is a TTY, enable save context, i.e. store
+             the most recent several messages ("context") and dump
+             them to a log file in case SIGHUP or SIGUSR1 is received
+             (or Ctrl+Break is pressed under Windows).  */
+          save_context_p = true;
+        }
     }
 }
 
-/* Close LOGFP, inhibit further logging and free the memory associated
-   with it.  */
+/* Close LOGFP (only if we opened it, not if it's stderr), inhibit
+   further logging and free the memory associated with it.  */
 void
 log_close (void)
 {
   int i;
 
-  if (logfp)
+  if (logfp && (logfp != stderr))
     fclose (logfp);
   logfp = NULL;
-  inhibit_logging = 1;
-  save_context_p = 0;
+  inhibit_logging = true;
+  save_context_p = false;
 
   for (i = 0; i < SAVED_LOG_LINES; i++)
     free_log_line (i);
   log_line_current = -1;
-  trailing_line = 0;
+  trailing_line = false;
 }
 
 /* Dump saved lines to logfp. */
@@ -601,6 +644,7 @@ log_dump_context (void)
 {
   int num = log_line_current;
   FILE *fp = get_log_fp ();
+  FILE *warcfp = get_warc_log_fp ();
   if (!fp)
     return;
 
@@ -612,19 +656,202 @@ log_dump_context (void)
     {
       struct log_ln *ln = log_lines + num;
       if (ln->content)
-       fputs (ln->content, fp);
+        {
+          FPUTS (ln->content, fp);
+          if (warcfp != NULL)
+            FPUTS (ln->content, warcfp);
+        }
       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);
+      {
+        FPUTS (log_lines[log_line_current].content, fp);
+        if (warcfp != NULL)
+          FPUTS (log_lines[log_line_current].content, warcfp);
+      }
   fflush (fp);
+  fflush (warcfp);
+}
+\f
+/* String escape functions. */
+
+/* Return the number of non-printable characters in SOURCE.
+   Non-printable characters are determined as per c-ctype.c.  */
+
+static int
+count_nonprint (const char *source)
+{
+  const char *p;
+  int cnt;
+  for (p = source, cnt = 0; *p; p++)
+    if (!c_isprint (*p))
+      ++cnt;
+  return cnt;
+}
+
+/* Copy SOURCE to DEST, escaping non-printable characters.
+
+   Non-printable refers to anything outside the non-control ASCII
+   range (32-126) which means that, for example, CR, LF, and TAB are
+   considered non-printable along with ESC, BS, and other control
+   chars.  This is by design: it makes sure that messages from remote
+   servers cannot be easily used to deceive the users by mimicking
+   Wget's output.  Disallowing non-ASCII characters is another
+   necessary security measure, which makes sure that remote servers
+   cannot garble the screen or guess the local charset and perform
+   homographic attacks.
+
+   Of course, the above mandates that escnonprint only be used in
+   contexts expected to be ASCII, such as when printing host names,
+   URL components, HTTP headers, FTP server messages, and the like.
+
+   ESCAPE is the leading character of the escape sequence.  BASE
+   should be the base of the escape sequence, and must be either 8 for
+   octal or 16 for hex.
+
+   DEST must point to a location with sufficient room to store an
+   encoded version of SOURCE.  */
+
+static void
+copy_and_escape (const char *source, char *dest, char escape, int base)
+{
+  const char *from = source;
+  char *to = dest;
+  unsigned char c;
+
+  /* Copy chars from SOURCE to DEST, escaping non-printable ones. */
+  switch (base)
+    {
+    case 8:
+      while ((c = *from++) != '\0')
+        if (c_isprint (c))
+          *to++ = c;
+        else
+          {
+            *to++ = escape;
+            *to++ = '0' + (c >> 6);
+            *to++ = '0' + ((c >> 3) & 7);
+            *to++ = '0' + (c & 7);
+          }
+      break;
+    case 16:
+      while ((c = *from++) != '\0')
+        if (c_isprint (c))
+          *to++ = c;
+        else
+          {
+            *to++ = escape;
+            *to++ = XNUM_TO_DIGIT (c >> 4);
+            *to++ = XNUM_TO_DIGIT (c & 0xf);
+          }
+      break;
+    default:
+      abort ();
+    }
+  *to = '\0';
+}
+
+#define RING_SIZE 3
+struct ringel {
+  char *buffer;
+  int size;
+};
+static struct ringel ring[RING_SIZE];   /* ring data */
+
+static const char *
+escnonprint_internal (const char *str, char escape, int base)
+{
+  static int ringpos;                   /* current ring position */
+  int nprcnt;
+
+  assert (base == 8 || base == 16);
+
+  nprcnt = count_nonprint (str);
+  if (nprcnt == 0)
+    /* If there are no non-printable chars in STR, don't bother
+       copying anything, just return STR.  */
+    return str;
+
+  {
+    /* Set up a pointer to the current ring position, so we can write
+       simply r->X instead of ring[ringpos].X. */
+    struct ringel *r = ring + ringpos;
+
+    /* Every non-printable character is replaced with the escape char
+       and three (or two, depending on BASE) *additional* chars.  Size
+       must also include the length of the original string and one
+       additional char for the terminating \0. */
+    int needed_size = strlen (str) + 1 + (base == 8 ? 3 * nprcnt : 2 * nprcnt);
+
+    /* If the current buffer is uninitialized or too small,
+       (re)allocate it.  */
+    if (r->buffer == NULL || r->size < needed_size)
+      {
+        r->buffer = xrealloc (r->buffer, needed_size);
+        r->size = needed_size;
+      }
+
+    copy_and_escape (str, r->buffer, escape, base);
+    ringpos = (ringpos + 1) % RING_SIZE;
+    return r->buffer;
+  }
+}
+
+/* Return a pointer to a static copy of STR with the non-printable
+   characters escaped as \ooo.  If there are no non-printable
+   characters in STR, STR is returned.  See copy_and_escape for more
+   information on which characters are considered non-printable.
+
+   DON'T call this function on translated strings because escaping
+   will break them.  Don't call it on literal strings from the source,
+   which are by definition trusted.  If newlines are allowed in the
+   string, escape and print it line by line because escaping the whole
+   string will convert newlines to \012.  (This is so that expectedly
+   single-line messages cannot use embedded newlines to mimic Wget's
+   output and deceive the user.)
+
+   escnonprint doesn't quote its escape character because it is notf
+   meant as a general and reversible quoting mechanism, but as a quick
+   way to defang binary junk sent by malicious or buggy servers.
+
+   NOTE: since this function can return a pointer to static data, be
+   careful to copy its result before calling it again.  However, to be
+   more useful with printf, it maintains an internal ring of static
+   buffers to return.  Currently the ring size is 3, which means you
+   can print up to three values in the same printf; if more is needed,
+   bump RING_SIZE.  */
+
+const char *
+escnonprint (const char *str)
+{
+  return escnonprint_internal (str, '\\', 8);
+}
+
+/* Return a pointer to a static copy of STR with the non-printable
+   characters escaped as %XX.  If there are no non-printable
+   characters in STR, STR is returned.
+
+   See escnonprint for usage details.  */
+
+const char *
+escnonprint_uri (const char *str)
+{
+  return escnonprint_internal (str, '%', 16);
+}
+
+void
+log_cleanup (void)
+{
+  size_t i;
+  for (i = 0; i < countof (ring); i++)
+    xfree_null (ring[i].buffer);
 }
 \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 enum { RR_NONE, RR_REQUESTED, RR_DONE } redirect_request = RR_NONE;
 static const char *redirect_request_signal_name;
 
 /* Redirect output to `wget-log'.  */
@@ -632,25 +859,26 @@ static const char *redirect_request_signal_name;
 static void
 redirect_output (void)
 {
-  char *logfile = unique_name (DEFAULT_LOGFILE, 0);
-  fprintf (stderr, _("\n%s received, redirecting output to `%s'.\n"),
-          redirect_request_signal_name, logfile);
-  logfp = fopen (logfile, "w");
-  if (!logfp)
+  char *logfile;
+  logfp = unique_create (DEFAULT_LOGFILE, false, &logfile);
+  if (logfp)
     {
-      /* 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;
+      fprintf (stderr, _("\n%s received, redirecting output to %s.\n"),
+               redirect_request_signal_name, quote (logfile));
+      xfree (logfile);
+      /* Dump the context output to the newly opened log.  */
+      log_dump_context ();
     }
   else
     {
-      /* Dump the context output to the newly opened log.  */
-      log_dump_context ();
+      /* Eek!  Opening the alternate log file has failed.  Nothing we
+         can do but disable printing completely. */
+      fprintf (stderr, _("\n%s received.\n"), redirect_request_signal_name);
+      fprintf (stderr, _("%s: %s; disabling logging.\n"),
+               (logfile) ? logfile : DEFAULT_LOGFILE, strerror (errno));
+      inhibit_logging = true;
     }
-  xfree (logfile);
-  save_context_p = 0;
+  save_context_p = false;
 }
 
 /* Check whether a signal handler requested the output to be