]> sjero.net Git - wget/blob - src/log.c
[svn] Replace opt.no_flush with a function to disable/enable flushing.
[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 /* Print a message to the log.  A copy of message will be saved to
285    saved_log, for later reusal by log_dump().  */
286
287 static void
288 logvprintf (enum log_options o, const char *fmt, va_list args)
289 {
290   CHECK_VERBOSE (o);
291   CANONICALIZE_LOGFP_OR_RETURN;
292
293   /* Originally, we first used vfprintf(), and then checked whether
294      the message needs to be stored with vsprintf().  However, Watcom
295      C didn't like ARGS being used twice, so now we first vsprintf()
296      the message, and then fwrite() it to LOGFP.  */
297
298   if (!save_log_p)
299     {
300       /* In the simple case just call vfprintf(), to avoid needless
301          allocation and games with vsnprintf(). */
302       vfprintf (logfp, fmt, args);
303     }
304   else
305     {
306       char smallmsg[128];
307       char *bigmsg = NULL;
308       int available_size = sizeof (smallmsg);
309       char *write_ptr = smallmsg;
310
311       while (1)
312         {
313           /* The GNU coding standards advise not to rely on the return
314              value of sprintf().  However, vsnprintf() is a relatively
315              new function missing from legacy systems.  Therefore it's
316              safe to assume that its return value is meaningful.  On
317              the systems where vsnprintf() is not available, we use
318              the implementation from snprintf.c which does return the
319              correct value.  */
320           int numwritten = vsnprintf (write_ptr, available_size, fmt, args);
321
322           /* vsnprintf() will not step over the limit given by
323              available_size.  If it fails, it will return either -1
324              (POSIX?) or the number of characters that *would have*
325              been written, if there had been enough room.  In the
326              former case, we double the available_size and malloc() to
327              get a larger buffer, and try again.  In the latter case,
328              we use the returned information to build a buffer of the
329              correct size.  */
330
331           if (numwritten == -1)
332             {
333               /* Writing failed, and we don't know the needed size.
334                  Try again with doubled size. */
335               available_size <<= 1;
336               bigmsg = xrealloc (bigmsg, available_size);
337               write_ptr = bigmsg;
338             }
339           else if (numwritten >= available_size)
340             {
341               /* Writing failed, but we know exactly how much space we
342                  need. */
343               available_size = numwritten + 1;
344               bigmsg = xrealloc (bigmsg, available_size);
345               write_ptr = bigmsg;
346             }
347           else
348             {
349               /* Writing succeeded. */
350               break;
351             }
352         }
353       saved_append (write_ptr);
354       fputs (write_ptr, logfp);
355       if (bigmsg)
356         xfree (bigmsg);
357     }
358   if (flush_log_p)
359     logflush ();
360   else
361     needs_flushing = 1;
362 }
363
364 /* Flush LOGFP.  Useful while flushing is disabled.  */
365 void
366 logflush (void)
367 {
368   CANONICALIZE_LOGFP_OR_RETURN;
369   fflush (logfp);
370   needs_flushing = 0;
371 }
372
373 /* Enable or disable log flushing. */
374 void
375 log_set_flush (int flush)
376 {
377   if (flush == flush_log_p)
378     return;
379
380   if (flush == 0)
381     {
382       /* Disable flushing by setting flush_log_p to 0. */
383       flush_log_p = 0;
384     }
385   else
386     {
387       /* Reenable flushing.  If anything was printed in no-flush mode,
388          flush the log now.  */
389       if (needs_flushing)
390         logflush ();
391       flush_log_p = 1;
392     }
393 }
394
395 /* Portability with pre-ANSI compilers makes these two functions look
396    like @#%#@$@#$.  */
397
398 #ifdef WGET_USE_STDARG
399 void
400 logprintf (enum log_options o, const char *fmt, ...)
401 #else  /* not WGET_USE_STDARG */
402 void
403 logprintf (va_alist)
404      va_dcl
405 #endif /* not WGET_USE_STDARG */
406 {
407   va_list args;
408 #ifndef WGET_USE_STDARG
409   enum log_options o;
410   const char *fmt;
411 #endif
412
413 #ifdef WGET_USE_STDARG
414   va_start (args, fmt);
415 #else
416   va_start (args);
417   o = va_arg (args, enum log_options);
418   fmt = va_arg (args, char *);
419 #endif
420   logvprintf (o, fmt, args);
421   va_end (args);
422 }
423
424 #ifdef DEBUG
425 /* The same as logprintf(), but does anything only if opt.debug is
426    non-zero.  */
427 #ifdef WGET_USE_STDARG
428 void
429 debug_logprintf (const char *fmt, ...)
430 #else  /* not WGET_USE_STDARG */
431 void
432 debug_logprintf (va_alist)
433      va_dcl
434 #endif /* not WGET_USE_STDARG */
435 {
436   if (opt.debug)
437     {
438       va_list args;
439 #ifndef WGET_USE_STDARG
440       const char *fmt;
441 #endif
442
443 #ifdef WGET_USE_STDARG
444       va_start (args, fmt);
445 #else
446       va_start (args);
447       fmt = va_arg (args, char *);
448 #endif
449       logvprintf (LOG_ALWAYS, fmt, args);
450       va_end (args);
451     }
452 }
453 #endif /* DEBUG */
454 \f
455 /* Open FILE and set up a logging stream.  If FILE cannot be opened,
456    exit with status of 1.  */
457 void
458 log_init (const char *file, int appendp)
459 {
460   if (file)
461     {
462       logfp = fopen (file, appendp ? "a" : "w");
463       if (!logfp)
464         {
465           perror (opt.lfilename);
466           exit (1);
467         }
468     }
469   else
470     {
471       /* The log goes to stderr to avoid collisions with the output if
472          the user specifies `-O -'.  #### Francois Pinard suggests
473          that it's a better idea to print to stdout by default, and to
474          stderr only if the user actually specifies `-O -'.  He says
475          this inconsistency is harder to document, but is overall
476          easier on the user.  */
477       logfp = stderr;
478
479       /* If the output is a TTY, enable logging, which will make Wget
480          remember all the printed messages, to be able to dump them to
481          a log file in case SIGHUP or SIGUSR1 is received (or
482          Ctrl+Break is pressed under Windows).  */
483       if (1
484 #ifdef HAVE_ISATTY
485           && isatty (fileno (logfp))
486 #endif
487           )
488         {
489           save_log_p = 1;
490         }
491     }
492 }
493
494 /* Close LOGFP, inhibit further logging and free the memory associated
495    with it.  */
496 void
497 log_close (void)
498 {
499   int i;
500
501   if (logfp != stdin)
502     fclose (logfp);
503   save_log_p = 0;
504   for (i = 0; i < SAVED_LOG_LINES; i++)
505     free_log_line (i);
506   log_line_current = -1;
507   trailing_line = 0;
508 }
509
510 /* Dump saved lines to logfp. */
511 static void
512 log_dump (void)
513 {
514   int num = log_line_current;
515   FILE *fp = logfp;
516
517   if (num == -1)
518     return;
519   if (trailing_line)
520     ROT_ADVANCE (num);
521   do
522     {
523       struct log_ln *ln = log_lines + num;
524       if (ln->content)
525         fputs (ln->content, fp);
526       ROT_ADVANCE (num);
527     }
528   while (num != log_line_current);
529   if (trailing_line)
530     if (log_lines[log_line_current].content)
531       fputs (log_lines[log_line_current].content, fp);
532   fflush (fp);
533 }
534
535 /* Redirect output to `wget-log'.  MESSIJ is printed on stdout, and
536    should contain *exactly one* `%s', which will be replaced by the
537    log file name.
538
539    If logging was not enabled, MESSIJ will not be printed.  */
540 void
541 redirect_output (const char *messij)
542 {
543   char *logfile;
544
545   if (!save_log_p)
546     return;
547
548   logfile = unique_name (DEFAULT_LOGFILE);
549   logfp = fopen (logfile, "w");
550   if (!logfp)
551     {
552       /* Eek!  Opening the alternate log file has failed.  Nothing we
553          can do but disable printing completely. */
554       fprintf (stderr, "%s: %s: %s\n", exec_name, logfile, strerror (errno));
555       /* `stdin' is magic to not print anything, ever.  */
556       logfp = stdin;
557     }
558   fprintf (stderr, messij, logfile);
559   xfree (logfile);
560   /* Dump the previous screenful of output to LOGFILE.  */
561   log_dump ();
562   save_log_p = 0;
563 }