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