]> Pileus Git - ~andy/fetchmail/blob - report.c
Other routine fixups.
[~andy/fetchmail] / report.c
1 /* report.c -- report function for noninteractive utilities
2  *
3  * For license terms, see the file COPYING in this directory.
4  *
5  * This code is distantly descended from the error.c module written by
6  * David MacKenzie <djm@gnu.ai.mit.edu>.  It was redesigned and
7  * rewritten by Dave Bodenstab, then redesigned again by ESR, then
8  * bludgeoned into submission for SunOS 4.1.3 by Chris Cheyney
9  * <cheyney@netcom.com>.  It works even when the return from
10  * vprintf(3) is unreliable.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16 #include <stdio.h>
17 #include <errno.h>
18 #include <string.h>
19 #if defined(HAVE_SYSLOG)
20 #include <syslog.h>
21 #endif
22 #include "i18n.h"
23 #include "fetchmail.h"
24
25 #if HAVE_VPRINTF || HAVE_DOPRNT || _LIBC || HAVE_STDARG_H
26 # if HAVE_STDARG_H
27 #  include <stdarg.h>
28 #  define VA_START(args, lastarg) va_start(args, lastarg)
29 # else
30 #  include <varargs.h>
31 #  define VA_START(args, lastarg) va_start(args)
32 # endif
33 #else
34 # define va_alist a1, a2, a3, a4, a5, a6, a7, a8
35 # define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
36 #endif
37
38 #define MALLOC(n)       xmalloc(n)      
39 #define REALLOC(n,s)    xrealloc(n,s)   
40
41 /* If NULL, report will flush stderr, then print on stderr the program
42    name, a colon and a space.  Otherwise, report will call this
43    function without parameters instead.  */
44 void (*report_print_progname) (
45 #if __STDC__ - 0
46                               void
47 #endif
48                               );
49
50 /* Used by report_build() and report_complete() to accumulate partial messages.
51  */
52 static unsigned int partial_message_size = 0;
53 static unsigned int partial_message_size_used = 0;
54 static char *partial_message;
55 static unsigned use_stderr;
56 static unsigned int use_syslog;
57
58 /* This variable is incremented each time `report' is called.  */
59 static unsigned int report_message_count;
60
61 #ifdef _LIBC
62 /* In the GNU C library, there is a predefined variable for this.  */
63
64 # define program_name program_invocation_name
65 # include <errno.h>
66
67 #else
68
69 /* The calling program should define program_name and set it to the
70    name of the executing program.  */
71 extern char *program_name;
72
73 # if !HAVE_STRERROR && !defined(strerror)
74 char *strerror (int errnum)
75 {
76     extern char *sys_errlist[];
77     extern int sys_nerr;
78
79     if (errnum > 0 && errnum <= sys_nerr)
80         return sys_errlist[errnum];
81     return GT_("Unknown system error");
82 }
83 # endif /* HAVE_STRERROR */
84 #endif  /* _LIBC */
85
86 /* Print the program name and error message MESSAGE, which is a printf-style
87    format string with optional args.
88    If ERRNUM is nonzero, print its corresponding system error message. */
89 /* VARARGS */
90
91 void
92 #ifdef HAVE_STDARG_H
93 report (FILE *errfp, const char *message, ...)
94 #else
95 report (FILE *errfp, message, va_alist)
96      const char *message;
97      va_dcl
98 #endif
99 {
100 #ifdef VA_START
101     va_list args;
102 #endif
103
104     /* If a partially built message exists, print it now so it's not lost.  */
105     if (partial_message_size_used != 0)
106     {
107         partial_message_size_used = 0;
108         report (errfp, GT_("%s (log message incomplete)"), partial_message);
109     }
110
111 #if defined(HAVE_SYSLOG)
112     if (use_syslog)
113     {
114         int priority;
115
116 #ifdef VA_START
117         VA_START (args, message);
118 #endif
119         priority = (errfp == stderr) ? LOG_ERR : LOG_INFO;
120
121 #ifdef HAVE_VSYSLOG
122         vsyslog (priority, message, args);
123 #else
124         {
125             char *a1 = va_arg(args, char *);
126             char *a2 = va_arg(args, char *);
127             char *a3 = va_arg(args, char *);
128             char *a4 = va_arg(args, char *);
129             char *a5 = va_arg(args, char *);
130             char *a6 = va_arg(args, char *);
131             char *a7 = va_arg(args, char *);
132             char *a8 = va_arg(args, char *);
133             syslog (priority, message, a1, a2, a3, a4, a5, a6, a7, a8);
134         }
135 #endif
136
137 #ifdef VA_START
138         va_end(args);
139 #endif
140     }
141     else
142 #endif
143     {
144         if (report_print_progname)
145             (*report_print_progname) ();
146         else
147         {
148             fflush (errfp);
149             if ( *message == '\n' )
150             {
151                 fputc( '\n', errfp );
152                 ++message;
153             }
154             fprintf (errfp, "%s: ", program_name);
155         }
156
157 #ifdef VA_START
158         VA_START (args, message);
159 # if HAVE_VPRINTF || _LIBC
160         vfprintf (errfp, message, args);
161 # else
162         _doprnt (message, args, errfp);
163 # endif
164         va_end (args);
165 #else
166         fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
167 #endif
168         fflush (errfp);
169     }
170     ++report_message_count;
171 }
172 \f
173 /*
174  * Calling report_init(1) causes report_build and report_complete to write
175  * to errfp without buffering.  This is needed for the ticker dots to
176  * work correctly.
177  */
178 void report_init(int mode)
179 {
180     switch(mode)
181     {
182     case 0:                     /* errfp, buffered */
183     default:
184         use_stderr = FALSE;
185         use_syslog = FALSE;
186         break;
187
188     case 1:                     /* errfp, unbuffered */
189         use_stderr = TRUE;
190         use_syslog = FALSE;
191         break;
192
193 #ifdef HAVE_SYSLOG
194     case -1:                    /* syslogd */
195         use_stderr = FALSE;
196         use_syslog = TRUE;
197         break;
198 #endif /* HAVE_SYSLOG */
199     }
200 }
201 \f
202 /* Build an report message by appending MESSAGE, which is a printf-style
203    format string with optional args, to the existing report message (which may
204    be empty.)  The completed report message is finally printed (and reset to
205    empty) by calling report_complete().
206    If an intervening call to report() occurs when a partially constructed
207    message exists, then, in an attempt to keep the messages in their proper
208    sequence, the partial message will be printed as-is (with a trailing 
209    newline) before report() prints its message. */
210 /* VARARGS */
211
212 void
213 #ifdef HAVE_STDARG_H
214 report_build (FILE *errfp, const char *message, ...)
215 #else
216 report_build (FILE *errfp, message, va_alist)
217      const char *message;
218      va_dcl
219 #endif
220 {
221 #ifdef VA_START
222     va_list args;
223     int n;
224 #endif
225
226     /* Make an initial guess for the size of any single message fragment.  */
227     if (partial_message_size == 0)
228     {
229         partial_message_size_used = 0;
230         partial_message_size = 2048;
231         partial_message = MALLOC (partial_message_size);
232     }
233     else
234         if (partial_message_size - partial_message_size_used < 1024)
235         {
236             partial_message_size += 2048;
237             partial_message = REALLOC (partial_message, partial_message_size);
238         }
239
240 #if defined(VA_START)
241     VA_START (args, message);
242 #if HAVE_VSNPRINTF || _LIBC
243     for ( ; ; )
244     {
245         n = vsnprintf (partial_message + partial_message_size_used,
246                        partial_message_size - partial_message_size_used,
247                        message, args);
248
249         if (n < partial_message_size - partial_message_size_used)
250         {
251             partial_message_size_used += n;
252             break;
253         }
254
255         partial_message_size += 2048;
256         partial_message = REALLOC (partial_message, partial_message_size);
257     }
258 #else
259     vsprintf (partial_message + partial_message_size_used, message, args);
260     partial_message_size_used += strlen(partial_message+partial_message_size_used);
261
262     /* Attempt to catch memory overwrites... */
263     if (partial_message_size_used >= partial_message_size)
264     {
265         partial_message_size_used = 0;
266         report (stderr, GT_("partial error message buffer overflow"));
267     }
268 #endif
269     va_end (args);
270 #else
271 #if HAVE_SNPRINTF
272     for ( ; ; )
273     {
274         n = snprintf (partial_message + partial_message_size_used,
275                       partial_message_size - partial_message_size_used,
276                       message, a1, a2, a3, a4, a5, a6, a7, a8);
277
278         if (n < partial_message_size - partial_message_size_used)
279         {
280             partial_message_size_used += n;
281             break;
282         }
283
284         partial_message_size += 2048;
285         partial_message = REALLOC (partial_message, partial_message_size);
286     }
287 #else
288     sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8);
289
290     /* Attempt to catch memory overwrites... */
291     if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size)
292     {
293         partial_message_size_used = 0;
294         report (stderr, GT_("partial error message buffer overflow"));
295     }
296 #endif
297 #endif
298
299     if (use_stderr && partial_message_size_used != 0)
300     {
301         partial_message_size_used = 0;
302         fputs(partial_message, errfp);
303     }
304 }
305 \f
306 /* Complete a report message by appending MESSAGE, which is a printf-style
307    format string with optional args, to the existing report message (which may
308    be empty.)  The completed report message is then printed (and reset to
309    empty.) */
310 /* VARARGS */
311
312 void
313 #ifdef HAVE_STDARG_H
314 report_complete (FILE *errfp, const char *message, ...)
315 #else
316 report_complete (FILE *errfp, message, va_alist)
317      const char *message;
318      va_dcl
319 #endif
320 {
321 #ifdef VA_START
322     va_list args;
323     int n;
324 #endif
325
326     /* Make an initial guess for the size of any single message fragment.  */
327     if (partial_message_size == 0)
328     {
329         partial_message_size_used = 0;
330         partial_message_size = 2048;
331         partial_message = MALLOC (partial_message_size);
332     }
333     else
334         if (partial_message_size - partial_message_size_used < 1024)
335         {
336             partial_message_size += 2048;
337             partial_message = REALLOC (partial_message, partial_message_size);
338         }
339
340 #if defined(VA_START)
341     VA_START (args, message);
342 #if HAVE_VSNPRINTF || _LIBC
343     for ( ; ; )
344     {
345         n = vsnprintf (partial_message + partial_message_size_used,
346                        partial_message_size - partial_message_size_used,
347                        message, args);
348
349         if (n < partial_message_size - partial_message_size_used)
350         {
351             partial_message_size_used += n;
352             break;
353         }
354
355         partial_message_size += 2048;
356         partial_message = REALLOC (partial_message, partial_message_size);
357     }
358 #else
359     vsprintf (partial_message + partial_message_size_used, message, args);
360     partial_message_size_used += strlen(partial_message+partial_message_size_used);
361
362     /* Attempt to catch memory overwrites... */
363     if (partial_message_size_used >= partial_message_size)
364     {
365         partial_message_size_used = 0;
366         report (stderr, GT_("partial error message buffer overflow"));
367     }
368 #endif
369     va_end (args);
370 #else
371 #if HAVE_SNPRINTF
372     for ( ; ; )
373     {
374         n = snprintf (partial_message + partial_message_size_used,
375                       partial_message_size - partial_message_size_used,
376                       message, a1, a2, a3, a4, a5, a6, a7, a8);
377
378         if (n < partial_message_size - partial_message_size_used)
379         {
380             partial_message_size_used += n;
381             break;
382         }
383
384         partial_message_size += 2048;
385         partial_message = REALLOC (partial_message, partial_message_size);
386     }
387 #else
388     sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8);
389
390     /* Attempt to catch memory overwrites... */
391     if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size)
392     {
393         partial_message_size_used = 0;
394         report (stderr, GT_("partial error message buffer overflow"));
395     }
396 #endif
397 #endif
398
399     /* Finally... print it.  */
400     partial_message_size_used = 0;
401
402     if (use_stderr)
403     {
404         fputs(partial_message, errfp);
405         fflush (errfp);
406
407         ++report_message_count;
408     }
409     else
410         report(errfp, "%s", partial_message);
411 }
412 \f
413 /* Sometimes we want to have at most one error per line.  This
414    variable controls whether this mode is selected or not.  */
415 int error_one_per_line;
416
417 void
418 #ifdef HAVE_STDARG_H
419 report_at_line (FILE *errfp, int errnum, const char *file_name,
420                unsigned int line_number, const char *message, ...)
421 #else
422 report_at_line (FILE *errfp, errnum, file_name, line_number, message, va_alist)
423      int errnum;
424      const char *file_name;
425      unsigned int line_number;
426      const char *message;
427      va_dcl
428 #endif
429 {
430 #ifdef VA_START
431     va_list args;
432 #endif
433
434     if (error_one_per_line)
435     {
436         static const char *old_file_name;
437         static unsigned int old_line_number;
438
439         if (old_line_number == line_number &&
440             (file_name == old_file_name || !strcmp (old_file_name, file_name)))
441             /* Simply return and print nothing.  */
442             return;
443
444         old_file_name = file_name;
445         old_line_number = line_number;
446     }
447
448     if (report_print_progname)
449         (*report_print_progname) ();
450     else
451     {
452         fflush (errfp);
453         if ( *message == '\n' )
454         {
455             fputc( '\n', errfp );
456             ++message;
457         }
458         fprintf (errfp, "%s:", program_name);
459     }
460
461     if (file_name != NULL)
462         fprintf (errfp, "%s:%d: ", file_name, line_number);
463
464 #ifdef VA_START
465     VA_START (args, message);
466 # if HAVE_VPRINTF || _LIBC
467     vfprintf (errfp, message, args);
468 # else
469     _doprnt (message, args, errfp);
470 # endif
471     va_end (args);
472 #else
473     fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
474 #endif
475
476     ++report_message_count;
477     if (errnum)
478         fprintf (errfp, ": %s", strerror (errnum));
479     putc ('\n', errfp);
480     fflush (errfp);
481 }