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