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