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