]> sjero.net Git - wget/blob - src/log.c
[svn] Fix varargs usage in logvprintf.
[wget] / src / log.c
1 /* Messages logging.
2    Copyright (C) 1998, 2000 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Wget; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #ifdef HAVE_STRING_H
24 # include <string.h>
25 #else
26 # include <strings.h>
27 #endif
28 #include <stdlib.h>
29 #ifdef HAVE_STDARG_H
30 # define WGET_USE_STDARG
31 # include <stdarg.h>
32 #else
33 # include <varargs.h>
34 #endif
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #include <assert.h>
39 #include <errno.h>
40
41 #include "wget.h"
42 #include "utils.h"
43
44 #ifndef errno
45 extern int errno;
46 #endif
47 \f
48 /* The file descriptor used for logging. */
49
50 static FILE *logfp;
51
52 /* Whether logging is saved at all.  */
53 int save_log_p;
54
55 /* Whether the log is flushed after each command. */
56 int flush_log_p = 1;
57 int needs_flushing;
58
59 /* In the event of a hang-up, and if its output was on a TTY, Wget
60    redirects its output to `wget-log'.
61
62    For the convenience of reading this newly-created log, we store the
63    last several lines ("screenful", hence the choice of 24) of Wget
64    output, and dump them as context when the time comes.  */
65 #define SAVED_LOG_LINES 24
66
67 /* log_lines is a circular buffer that stores SAVED_LOG_LINES lines of
68    output.  log_line_current always points to the position in the
69    buffer that will be written to next.  When log_line_current reaches
70    SAVED_LOG_LINES, it is reset to zero.
71
72    The problem here is that we'd have to either (re)allocate and free
73    strings all the time, or limit the lines to an arbitrary number of
74    characters.  Instead of settling for either of these, we do both:
75    if the line is smaller than a certain "usual" line length (80 chars
76    by default), a preallocated memory is used.  The rare lines that
77    are longer than 80 characters are malloc'ed and freed separately.
78    This gives good performance with minimum memory consumption and
79    fragmentation.  */
80
81 #define STATIC_LENGTH 80
82
83 static struct log_ln {
84   char static_line[STATIC_LENGTH + 1]; /* statically allocated
85                                           line. */
86   char *malloced_line;          /* malloc'ed line, for lines of output
87                                    larger than 80 characters. */
88   char *content;                /* this points either to malloced_line
89                                    or to the appropriate static_line.
90                                    If this is NULL, it means the line
91                                    has not yet been used. */
92 } log_lines[SAVED_LOG_LINES];
93
94 /* The current position in the ring. */
95 static int log_line_current = -1;
96
97 /* Whether the most recently written line was "trailing", i.e. did not
98    finish with \n.  This is an important piece of information because
99    the code is always careful to append data to trailing lines, rather
100    than create new ones.  */
101 static int trailing_line;
102
103 \f
104 #define ROT_ADVANCE(num) do {                   \
105   if (++num >= SAVED_LOG_LINES)                 \
106     num = 0;                                    \
107 } while (0)
108
109 /* Free the log line index with NUM.  This calls free on
110    ln->malloced_line if it's non-NULL, and it also resets
111    ln->malloced_line and ln->content to NULL.  */
112
113 static void
114 free_log_line (int num)
115 {
116   struct log_ln *ln = log_lines + num;
117   if (ln->malloced_line)
118     {
119       xfree (ln->malloced_line);
120       ln->malloced_line = NULL;
121     }
122   ln->content = NULL;
123 }
124
125 /* Append bytes in the range [start, end) to one line in the log.  The
126    region is not supposed to contain newlines, except for the last
127    character (at end[-1]).  */
128
129 static void
130 saved_append_1 (const char *start, const char *end)
131 {
132   int len = end - start;
133   if (!len)
134     return;
135
136   /* First, check whether we need to append to an existing line or to
137      create a new one.  */
138   if (!trailing_line)
139     {
140       /* Create a new line. */
141       struct log_ln *ln;
142
143       if (log_line_current == -1)
144         log_line_current = 0;
145       else
146         free_log_line (log_line_current);
147       ln = log_lines + log_line_current;
148       if (len > STATIC_LENGTH)
149         {
150           ln->malloced_line = strdupdelim (start, end);
151           ln->content = ln->malloced_line;
152         }
153       else
154         {
155           memcpy (ln->static_line, start, len);
156           ln->static_line[len] = '\0';
157           ln->content = ln->static_line;
158         }
159     }
160   else
161     {
162       /* Append to the last line.  If the line is malloc'ed, we just
163          call realloc and append the new string.  If the line is
164          static, we have to check whether appending the new string
165          would make it exceed STATIC_LENGTH characters, and if so,
166          convert it to malloc(). */
167       struct log_ln *ln = log_lines + log_line_current;
168       if (ln->malloced_line)
169         {
170           /* Resize malloc'ed line and append. */
171           int old_len = strlen (ln->malloced_line);
172           ln->malloced_line = xrealloc (ln->malloced_line, old_len + len + 1);
173           memcpy (ln->malloced_line + old_len, start, len);
174           ln->malloced_line[old_len + len] = '\0';
175           /* might have changed due to realloc */
176           ln->content = ln->malloced_line;
177         }
178       else
179         {
180           int old_len = strlen (ln->static_line);
181           if (old_len + len > STATIC_LENGTH)
182             {
183               /* Allocate memory and concatenate the old and the new
184                  contents. */
185               ln->malloced_line = xmalloc (old_len + len + 1);
186               memcpy (ln->malloced_line, ln->static_line,
187                       old_len);
188               memcpy (ln->malloced_line + old_len, start, len);
189               ln->malloced_line[old_len + len] = '\0';
190               ln->content = ln->malloced_line;
191             }
192           else
193             {
194               /* Just append to the old, statically allocated
195                  contents.  */
196               memcpy (ln->static_line + old_len, start, len);
197               ln->static_line[old_len + len] = '\0';
198               ln->content = ln->static_line;
199             }
200         }
201     }
202   trailing_line = !(end[-1] == '\n');
203   if (!trailing_line)
204     ROT_ADVANCE (log_line_current);
205 }
206
207 /* Log the contents of S, as explained above.  If S consists of
208    multiple lines, they are logged separately.  If S does not end with
209    a newline, it will form a "trailing" line, to which things will get
210    appended the next time this function is called.  */
211
212 static void
213 saved_append (const char *s)
214 {
215   while (*s)
216     {
217       const char *end = strchr (s, '\n');
218       if (!end)
219         end = s + strlen (s);
220       else
221         ++end;
222       saved_append_1 (s, end);
223       s = end;
224     }
225 }
226 \f
227 /* Check X against opt.verbose and opt.quiet.  The semantics is as
228    follows:
229
230    * LOG_ALWAYS - print the message unconditionally;
231
232    * LOG_NOTQUIET - print the message if opt.quiet is non-zero;
233
234    * LOG_NONVERBOSE - print the message if opt.verbose is zero;
235
236    * LOG_VERBOSE - print the message if opt.verbose is non-zero.  */
237 #define CHECK_VERBOSE(x)                        \
238   switch (x)                                    \
239     {                                           \
240     case LOG_ALWAYS:                            \
241       break;                                    \
242     case LOG_NOTQUIET:                          \
243       if (opt.quiet)                            \
244         return;                                 \
245       break;                                    \
246     case LOG_NONVERBOSE:                        \
247       if (opt.verbose || opt.quiet)             \
248         return;                                 \
249       break;                                    \
250     case LOG_VERBOSE:                           \
251       if (!opt.verbose)                         \
252         return;                                 \
253     }
254
255 #define CANONICALIZE_LOGFP_OR_RETURN do {       \
256   if (logfp == stdin)                           \
257     return;                                     \
258   else if (!logfp)                              \
259     /* This might happen if somebody calls a */ \
260     /* log* function before log_init(). */      \
261     logfp = stderr;                             \
262 } while (0)
263
264 \f
265 /* Log a literal string S.  The string is logged as-is, without a
266    newline appended.  */
267
268 void
269 logputs (enum log_options o, const char *s)
270 {
271   CHECK_VERBOSE (o);
272   CANONICALIZE_LOGFP_OR_RETURN;
273
274   fputs (s, logfp);
275   if (save_log_p)
276     saved_append (s);
277
278   if (flush_log_p)
279     logflush ();
280   else
281     needs_flushing = 1;
282 }
283
284 struct logvprintf_state {
285   char *bigmsg;
286   int expected_size;
287   int allocated;
288 };
289
290 /* Print a message to the log.  A copy of message will be saved to
291    saved_log, for later reusal by log_dump().
292
293    It is not possible to code this function in a "natural" way, using
294    a loop, because of the braindeadness of the varargs API.
295    Specifically, each call to vsnprintf() must be preceded by va_start
296    and followed by va_end.  And this is possible only in the function
297    that contains the `...' declaration.  The alternative would be to
298    use va_copy, but that's not portable.  */
299
300 static int
301 logvprintf (struct logvprintf_state *state, const char *fmt, va_list args)
302 {
303   char smallmsg[128];
304   char *write_ptr = smallmsg;
305   int available_size = sizeof (smallmsg);
306   int numwritten;
307
308   if (!save_log_p)
309     {
310       /* In the simple case just call vfprintf(), to avoid needless
311          allocation and games with vsnprintf(). */
312       vfprintf (logfp, fmt, args);
313       goto flush;
314     }
315
316   if (state->allocated != 0)
317     {
318       write_ptr = state->bigmsg;
319       available_size = state->allocated;
320     }
321
322   /* The GNU coding standards advise not to rely on the return value
323      of sprintf().  However, vsnprintf() is a relatively new function
324      missing from legacy systems.  Therefore I consider it safe to
325      assume that its return value is meaningful.  On the systems where
326      vsnprintf() is not available, we use the implementation from
327      snprintf.c which does return the correct value.  */
328   numwritten = vsnprintf (write_ptr, available_size, fmt, args);
329
330   /* vsnprintf() will not step over the limit given by available_size.
331      If it fails, it will return either -1 (POSIX?) or the number of
332      characters that *would have* been written, if there had been
333      enough room.  In the former case, we double the available_size
334      and malloc() to get a larger buffer, and try again.  In the
335      latter case, we use the returned information to build a buffer of
336      the correct size.  */
337
338   if (numwritten == -1)
339     {
340       /* Writing failed, and we don't know the needed size.  Try
341          again with doubled size. */
342       int newsize = available_size << 1;
343       state->bigmsg = xrealloc (state->bigmsg, newsize);
344       state->allocated = newsize;
345       return 0;
346     }
347   else if (numwritten >= available_size)
348     {
349       /* Writing failed, but we know exactly how much space we
350          need. */
351       int newsize = numwritten + 1;
352       state->bigmsg = xrealloc (state->bigmsg, newsize);
353       state->allocated = newsize;
354       return 0;
355     }
356
357   /* Writing succeeded. */
358   saved_append (write_ptr);
359   fputs (write_ptr, logfp);
360   if (state->bigmsg)
361     xfree (state->bigmsg);
362
363  flush:
364   if (flush_log_p)
365     logflush ();
366   else
367     needs_flushing = 1;
368
369   return 1;
370 }
371
372 /* Flush LOGFP.  Useful while flushing is disabled.  */
373 void
374 logflush (void)
375 {
376   CANONICALIZE_LOGFP_OR_RETURN;
377   fflush (logfp);
378   needs_flushing = 0;
379 }
380
381 /* Enable or disable log flushing. */
382 void
383 log_set_flush (int flush)
384 {
385   if (flush == flush_log_p)
386     return;
387
388   if (flush == 0)
389     {
390       /* Disable flushing by setting flush_log_p to 0. */
391       flush_log_p = 0;
392     }
393   else
394     {
395       /* Reenable flushing.  If anything was printed in no-flush mode,
396          flush the log now.  */
397       if (needs_flushing)
398         logflush ();
399       flush_log_p = 1;
400     }
401 }
402
403 #ifdef WGET_USE_STDARG
404 # define VA_START_1(arg1_type, arg1, args) va_start(args, arg1)
405 # define VA_START_2(arg1_type, arg1, arg2_type, arg2, args) va_start(args, arg2)
406 #else  /* not WGET_USE_STDARG */
407 # define VA_START_1(arg1_type, arg1, args) do { \
408   va_start (args);                                                      \
409   arg1 = va_arg (args, arg1_type);                                      \
410 } while (0)
411 # define VA_START_2(arg1_type, arg1, arg2_type, arg2, args) do {        \
412   va_start (args);                                                      \
413   arg1 = va_arg (args, arg1_type);                                      \
414   arg2 = va_arg (args, arg2_type);                                      \
415 } while (0)
416 #endif /* not WGET_USE_STDARG */
417
418 /* Portability with pre-ANSI compilers makes these two functions look
419    like @#%#@$@#$.  */
420
421 #ifdef WGET_USE_STDARG
422 void
423 logprintf (enum log_options o, const char *fmt, ...)
424 #else  /* not WGET_USE_STDARG */
425 void
426 logprintf (va_alist)
427      va_dcl
428 #endif /* not WGET_USE_STDARG */
429 {
430   va_list args;
431   struct logvprintf_state lpstate;
432   int done;
433
434 #ifndef WGET_USE_STDARG
435   enum log_options o;
436   const char *fmt;
437
438   /* Perform a "dry run" of VA_START_2 to get the value of O. */
439   VA_START_2 (enum log_options, o, char *, fmt, args);
440   va_end (args);
441 #endif
442
443   CHECK_VERBOSE (o);
444   CANONICALIZE_LOGFP_OR_RETURN;
445
446   memset (&lpstate, '\0', sizeof (lpstate));
447   do
448     {
449       VA_START_2 (enum log_options, o, char *, fmt, args);
450       done = logvprintf (&lpstate, fmt, args);
451       va_end (args);
452     }
453   while (!done);
454 }
455
456 #ifdef DEBUG
457 /* The same as logprintf(), but does anything only if opt.debug is
458    non-zero.  */
459 #ifdef WGET_USE_STDARG
460 void
461 debug_logprintf (const char *fmt, ...)
462 #else  /* not WGET_USE_STDARG */
463 void
464 debug_logprintf (va_alist)
465      va_dcl
466 #endif /* not WGET_USE_STDARG */
467 {
468   if (opt.debug)
469     {
470       va_list args;
471 #ifndef WGET_USE_STDARG
472       const char *fmt;
473 #endif
474       struct logvprintf_state lpstate;
475       int done;
476
477       CANONICALIZE_LOGFP_OR_RETURN;
478
479       memset (&lpstate, '\0', sizeof (lpstate));
480       do
481         {
482           VA_START_1 (char *, fmt, args);
483           done = logvprintf (&lpstate, fmt, args);
484           va_end (args);
485         }
486       while (!done);
487     }
488 }
489 #endif /* DEBUG */
490 \f
491 /* Open FILE and set up a logging stream.  If FILE cannot be opened,
492    exit with status of 1.  */
493 void
494 log_init (const char *file, int appendp)
495 {
496   if (file)
497     {
498       logfp = fopen (file, appendp ? "a" : "w");
499       if (!logfp)
500         {
501           perror (opt.lfilename);
502           exit (1);
503         }
504     }
505   else
506     {
507       /* The log goes to stderr to avoid collisions with the output if
508          the user specifies `-O -'.  #### Francois Pinard suggests
509          that it's a better idea to print to stdout by default, and to
510          stderr only if the user actually specifies `-O -'.  He says
511          this inconsistency is harder to document, but is overall
512          easier on the user.  */
513       logfp = stderr;
514
515       /* If the output is a TTY, enable logging, which will make Wget
516          remember all the printed messages, to be able to dump them to
517          a log file in case SIGHUP or SIGUSR1 is received (or
518          Ctrl+Break is pressed under Windows).  */
519       if (1
520 #ifdef HAVE_ISATTY
521           && isatty (fileno (logfp))
522 #endif
523           )
524         {
525           save_log_p = 1;
526         }
527     }
528 }
529
530 /* Close LOGFP, inhibit further logging and free the memory associated
531    with it.  */
532 void
533 log_close (void)
534 {
535   int i;
536
537   if (logfp != stdin)
538     fclose (logfp);
539   save_log_p = 0;
540   for (i = 0; i < SAVED_LOG_LINES; i++)
541     free_log_line (i);
542   log_line_current = -1;
543   trailing_line = 0;
544 }
545
546 /* Dump saved lines to logfp. */
547 static void
548 log_dump (void)
549 {
550   int num = log_line_current;
551   FILE *fp = logfp;
552
553   if (num == -1)
554     return;
555   if (trailing_line)
556     ROT_ADVANCE (num);
557   do
558     {
559       struct log_ln *ln = log_lines + num;
560       if (ln->content)
561         fputs (ln->content, fp);
562       ROT_ADVANCE (num);
563     }
564   while (num != log_line_current);
565   if (trailing_line)
566     if (log_lines[log_line_current].content)
567       fputs (log_lines[log_line_current].content, fp);
568   fflush (fp);
569 }
570
571 /* Redirect output to `wget-log'.  MESSIJ is printed on stdout, and
572    should contain *exactly one* `%s', which will be replaced by the
573    log file name.
574
575    If logging was not enabled, MESSIJ will not be printed.  */
576 void
577 redirect_output (const char *messij)
578 {
579   char *logfile;
580
581   if (!save_log_p)
582     return;
583
584   logfile = unique_name (DEFAULT_LOGFILE);
585   logfp = fopen (logfile, "w");
586   if (!logfp)
587     {
588       /* Eek!  Opening the alternate log file has failed.  Nothing we
589          can do but disable printing completely. */
590       fprintf (stderr, "%s: %s: %s\n", exec_name, logfile, strerror (errno));
591       /* `stdin' is magic to not print anything, ever.  */
592       logfp = stdin;
593     }
594   fprintf (stderr, messij, logfile);
595   xfree (logfile);
596   /* Dump the previous screenful of output to LOGFILE.  */
597   log_dump ();
598   save_log_p = 0;
599 }