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. */
+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>
# 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>
#include "wget.h"
#include "utils.h"
+#include "log.h"
#ifndef errno
extern int errno;
{
/* Allocate memory and concatenate the old and the new
contents. */
- ln->malloced_line = xmalloc (old_len + len + 1);
+ ln->malloced_line = (char *)xmalloc (old_len + len + 1);
memcpy (ln->malloced_line, ln->static_line,
old_len);
memcpy (ln->malloced_line + old_len, start, len);
/* 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.
+
+ (An alternative approach would be to use va_copy, but that's not
+ portable.) */
static int
-logvprintf (struct logvprintf_state *state, const char *fmt, va_list args)
+log_vprintf_internal (struct logvprintf_state *state, const char *fmt,
+ va_list args)
{
char smallmsg[128];
char *write_ptr = smallmsg;
/* 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. */
+ 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 (numwritten == -1)
{
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_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 */
+# define VA_START(args, arg1) va_start (args, arg1)
+#else
+# define VA_START(args, ignored) va_start (args)
+#endif
-/* Portability with pre-ANSI compilers makes these two functions look
- like @#%#@$@#$. */
+/* 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). */
-#ifdef WGET_USE_STDARG
void
logprintf (enum log_options o, const char *fmt, ...)
-#else /* not WGET_USE_STDARG */
-void
-logprintf (va_alist)
- va_dcl
-#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;
-
- /* 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));
+ xzero (lpstate);
do
{
- VA_START_2 (enum log_options, o, char *, fmt, args);
- done = logvprintf (&lpstate, fmt, args);
+ VA_START (args, fmt);
+ done = log_vprintf_internal (&lpstate, fmt, args);
va_end (args);
}
while (!done);
}
-#ifdef DEBUG
+#ifdef ENABLE_DEBUG
/* The same as logprintf(), but does anything only if opt.debug is
non-zero. */
-#ifdef WGET_USE_STDARG
void
debug_logprintf (const char *fmt, ...)
-#else /* not WGET_USE_STDARG */
-void
-debug_logprintf (va_alist)
- va_dcl
-#endif /* not WGET_USE_STDARG */
{
if (opt.debug)
{
va_list args;
-#ifndef WGET_USE_STDARG
- const char *fmt;
-#endif
struct logvprintf_state lpstate;
int done;
if (inhibit_logging)
return;
- memset (&lpstate, '\0', sizeof (lpstate));
+ xzero (lpstate);
do
{
- VA_START_1 (char *, fmt, args);
- done = logvprintf (&lpstate, fmt, args);
+ VA_START (args, fmt);
+ done = log_vprintf_internal (&lpstate, fmt, args);
va_end (args);
}
while (!done);
}
}
-#endif /* DEBUG */
+#endif /* ENABLE_DEBUG */
\f
/* Open FILE and set up a logging stream. If FILE cannot be opened,
exit with status of 1. */
logfp = fopen (file, appendp ? "a" : "w");
if (!logfp)
{
- perror (opt.lfilename);
+ fprintf (stderr, "%s: %s: %s\n", exec_name, file, strerror (errno));
exit (1);
}
}
easier on the user. */
logfp = stderr;
- /* 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). */
if (1
#ifdef HAVE_ISATTY
&& isatty (fileno (logfp))
#endif
)
{
+ /* 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 = 1;
}
}
fflush (fp);
}
\f
+/* String escape functions. */
+
+/* Return the number of non-printable characters in SOURCE.
+
+ Non-printable characters are determined as per safe-ctype.h,
+ i.e. the non-printable characters of the "C" locale. This code is
+ meant to be used to protect the user from binary characters in
+ (normally ASCII) server messages. */
+
+static int
+count_nonprint (const char *source)
+{
+ const char *p;
+ int cnt;
+ for (p = source, cnt = 0; *p; p++)
+ if (!ISPRINT (*p))
+ ++cnt;
+ return cnt;
+}
+
+/* Copy SOURCE to DEST, escaping non-printable characters. If FOR_URI
+ is 0, they are escaped as \ooo; otherwise, they are escaped as
+ %xx.
+
+ 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, int for_uri)
+{
+ const char *from;
+ char *to;
+
+ /* Copy the string, escaping non-printable chars. */
+ if (!for_uri)
+ {
+ for (from = source, to = dest; *from; from++)
+ if (ISPRINT (*from))
+ *to++ = *from;
+ else
+ {
+ const unsigned char c = *from;
+ *to++ = '\\';
+ *to++ = '0' + (c >> 6);
+ *to++ = '0' + ((c >> 3) & 7);
+ *to++ = '0' + (c & 7);
+ }
+ }
+ else
+ {
+ for (from = source, to = dest; *from; from++)
+ if (ISPRINT (*from))
+ *to++ = *from;
+ else
+ {
+ const unsigned char c = *from;
+ *to++ = '%';
+ *to++ = XNUM_TO_DIGIT (c >> 4);
+ *to++ = XNUM_TO_DIGIT (c & 0xf);
+ }
+ }
+ *to = '\0';
+}
+
+#define RING_SIZE 3
+struct ringel {
+ char *buffer;
+ int size;
+};
+
+static const char *
+escnonprint_internal (const char *str, int for_uri)
+{
+ static struct ringel ring[RING_SIZE]; /* ring data */
+ static int ringpos; /* current ring position */
+
+ int 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 "\ooo",
+ i.e. with three *additional* chars (two in URI-mode). Size
+ must also include the length of the original string and an
+ additional char for the terminating \0. */
+ int needed_size = strlen (str) + 1 + for_uri ? (2 * nprcnt) : (3 * 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);
+
+ copy_and_escape (str, r->buffer, for_uri);
+ 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.
+
+ 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, 0);
+}
+
+/* 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.
+
+ This function returns a pointer to static data which will be
+ overwritten by subsequent calls -- see escnonprint for details. */
+
+const char *
+escnonprint_uri (const char *str)
+{
+ return escnonprint_internal (str, 1);
+}
+\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 void
redirect_output (void)
{
- 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)
+ char *logfile;
+ logfp = unique_create (DEFAULT_LOGFILE, 0, &logfile);
+ if (logfp)
+ {
+ fprintf (stderr, _("\n%s received, redirecting output to `%s'.\n"),
+ redirect_request_signal_name, logfile);
+ xfree (logfile);
+ /* Dump the context output to the newly opened log. */
+ log_dump_context ();
+ }
+ else
{
/* 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, strerror (errno));
inhibit_logging = 1;
}
- else
- {
- /* Dump the context output to the newly opened log. */
- log_dump_context ();
- }
- xfree (logfile);
save_context_p = 0;
}