X-Git-Url: http://sjero.net/git/?a=blobdiff_plain;f=src%2Flog.c;h=d0b5370a432e4f27cab1d6053fc6f9d4a745ce78;hb=908d7a4bcee5adb7d4768499282cc83075d0332e;hp=1f829e493da2a2945d47ef99769bf3b933a1f3bf;hpb=4b1afddab3d7015a71d4358aa4c05b102ecddac5;p=wget diff --git a/src/log.c b/src/log.c index 1f829e49..d0b5370a 100644 --- a/src/log.c +++ b/src/log.c @@ -1,5 +1,5 @@ /* Messages logging. - Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc. + Copyright (C) 2005 Free Software Foundation, Inc. This file is part of GNU Wget. @@ -29,31 +29,10 @@ so, delete this exception statement from your version. */ #include -/* 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 -#ifdef HAVE_STRING_H -# include -#else -# include -#endif +#include #include -#ifdef WGET_USE_STDARG -# include -#else -# include -#endif +#include #ifdef HAVE_UNISTD_H # include #endif @@ -62,10 +41,7 @@ so, delete this exception statement from your version. */ #include "wget.h" #include "utils.h" - -#ifndef errno -extern int errno; -#endif +#include "log.h" /* This file impplement support for "logging". Logging means printing output, plus several additional features: @@ -149,7 +125,7 @@ static int log_line_current = -1; than create new ones. */ static int trailing_line; -static void check_redirect_output PARAMS ((void)); +static void check_redirect_output (void); #define ROT_ADVANCE(num) do { \ if (++num >= SAVED_LOG_LINES) \ @@ -327,7 +303,7 @@ logputs (enum log_options o, const char *s) FILE *fp; check_redirect_output (); - if (!(fp = get_log_fp ())) + if ((fp = get_log_fp ()) == NULL) return; CHECK_VERBOSE (o); @@ -349,15 +325,20 @@ 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. + + (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; @@ -390,10 +371,10 @@ logvprintf (struct logvprintf_state *state, const char *fmt, va_list 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. */ + 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) { @@ -473,79 +454,41 @@ log_set_save_context (int 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 @#%#@$@#$. */ +/* 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; @@ -553,17 +496,17 @@ debug_logprintf (va_alist) 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 */ /* Open FILE and set up a logging stream. If FILE cannot be opened, exit with status of 1. */ @@ -575,7 +518,7 @@ log_init (const char *file, int appendp) logfp = fopen (file, appendp ? "a" : "w"); if (!logfp) { - perror (opt.lfilename); + fprintf (stderr, "%s: %s: %s\n", exec_name, file, strerror (errno)); exit (1); } } @@ -589,16 +532,16 @@ 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 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; } } @@ -650,6 +593,180 @@ log_dump_context (void) fflush (fp); } +/* String escape functions. */ + +/* Return the number of non-printable characters in SOURCE. + Non-printable characters are determined as per safe-ctype.c. */ + +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. + + 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 (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 (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) +{ + int i; + for (i = 0; i < countof (ring); i++) + xfree_null (ring[i].buffer); +} + /* 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; @@ -660,24 +777,25 @@ 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, 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; }