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