]> Pileus Git - ~andy/fetchmail/blob - report.c
Fix a few compiler warnings around implicit conversion or extra ";".
[~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 static 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 static void rep_ensuresize(void) {
209     /* Make an initial guess for the size of any single message fragment.  */
210     if (partial_message_size == 0)
211     {
212         partial_message_size_used = 0;
213         partial_message_size = 2048;
214         partial_message = (char *)MALLOC (partial_message_size);
215     }
216     else
217         if (partial_message_size - partial_message_size_used < 1024)
218         {
219             partial_message_size += 2048;
220             partial_message = (char *)REALLOC (partial_message, partial_message_size);
221         }
222 }
223
224 void
225 #ifdef HAVE_STDARG_H
226 report_build (FILE *errfp, const char *message, ...)
227 #else
228 report_build (FILE *errfp, message, va_alist)
229      const char *message;
230      va_dcl
231 #endif
232 {
233 #ifdef VA_START
234     va_list args;
235     int n;
236 #endif
237
238     rep_ensuresize();
239
240 #if defined(VA_START)
241     for ( ; ; )
242     {
243         /*
244          * args has to be initialized before every call of vsnprintf(), 
245          * because vsnprintf() invokes va_arg macro and thus args is 
246          * undefined after the call.
247          */
248         VA_START(args, message);
249         n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used,
250                        message, args);
251         va_end (args);
252
253         if (n >= 0
254             && (unsigned)n < partial_message_size - partial_message_size_used)
255         {
256             partial_message_size_used += n;
257             break;
258         }
259
260         partial_message_size += 2048;
261         partial_message = (char *)REALLOC (partial_message, partial_message_size);
262     }
263 #else
264     for ( ; ; )
265     {
266         n = snprintf (partial_message + partial_message_size_used,
267                       partial_message_size - partial_message_size_used,
268                       message, a1, a2, a3, a4, a5, a6, a7, a8);
269
270         if (n >= 0
271             && (unsigned)n < partial_message_size - partial_message_size_used)
272         {
273             partial_message_size_used += n;
274             break;
275         }
276
277         partial_message_size += 2048;
278         partial_message = REALLOC (partial_message, partial_message_size);
279     }
280 #endif
281
282     if (use_stderr && partial_message_size_used != 0)
283     {
284         partial_message_size_used = 0;
285         fputs(partial_message, errfp);
286     }
287 }
288 \f
289 /* Complete a report message by appending MESSAGE, which is a printf-style
290    format string with optional args, to the existing report message (which may
291    be empty.)  The completed report message is then printed (and reset to
292    empty.) */
293 /* VARARGS */
294
295 void
296 #ifdef HAVE_STDARG_H
297 report_complete (FILE *errfp, const char *message, ...)
298 #else
299 report_complete (FILE *errfp, message, va_alist)
300      const char *message;
301      va_dcl
302 #endif
303 {
304 #ifdef VA_START
305     va_list args;
306     int n;
307 #endif
308
309     rep_ensuresize();
310
311 #if defined(VA_START)
312     for ( ; ; )
313     {
314         VA_START(args, message);
315         n = vsnprintf (partial_message + partial_message_size_used,
316                        partial_message_size - partial_message_size_used,
317                        message, args);
318         va_end(args);
319
320         /* old glibc versions return -1 for truncation */
321         if (n >= 0
322             && (unsigned)n < partial_message_size - partial_message_size_used)
323         {
324             partial_message_size_used += n;
325             break;
326         }
327
328         partial_message_size += 2048;
329         partial_message = (char *)REALLOC (partial_message, partial_message_size);
330     }
331 #else
332     for ( ; ; )
333     {
334         n = snprintf (partial_message + partial_message_size_used,
335                       partial_message_size - partial_message_size_used,
336                       message, a1, a2, a3, a4, a5, a6, a7, a8);
337
338         if (n >= 0
339             && (unsigned)n < partial_message_size - partial_message_size_used)
340         {
341             partial_message_size_used += n;
342             break;
343         }
344
345         partial_message_size += 2048;
346         partial_message = REALLOC (partial_message, partial_message_size);
347     }
348 #endif
349
350     /* Finally... print it.  */
351     partial_message_size_used = 0;
352
353     if (use_stderr)
354     {
355         fputs(partial_message, errfp);
356         fflush (errfp);
357
358         ++report_message_count;
359     }
360     else
361         report(errfp, "%s", partial_message);
362 }
363 \f
364 /* Sometimes we want to have at most one error per line.  This
365    variable controls whether this mode is selected or not.  */
366 static int error_one_per_line;
367
368 void
369 #ifdef HAVE_STDARG_H
370 report_at_line (FILE *errfp, int errnum, const char *file_name,
371                unsigned int line_number, const char *message, ...)
372 #else
373 report_at_line (FILE *errfp, errnum, file_name, line_number, message, va_alist)
374      int errnum;
375      const char *file_name;
376      unsigned int line_number;
377      const char *message;
378      va_dcl
379 #endif
380 {
381 #ifdef VA_START
382     va_list args;
383 #endif
384
385     if (error_one_per_line)
386     {
387         static const char *old_file_name;
388         static unsigned int old_line_number;
389
390         if (old_line_number == line_number &&
391             (file_name == old_file_name || !strcmp (old_file_name, file_name)))
392             /* Simply return and print nothing.  */
393             return;
394
395         old_file_name = file_name;
396         old_line_number = line_number;
397     }
398
399     if (report_print_progname)
400         (*report_print_progname) ();
401     else
402     {
403         fflush (errfp);
404         if ( *message == '\n' )
405         {
406             fputc( '\n', errfp );
407             ++message;
408         }
409         fprintf (errfp, "%s:", program_name);
410     }
411
412     if (file_name != NULL)
413         fprintf (errfp, "%s:%u: ", file_name, line_number);
414
415 #ifdef VA_START
416     VA_START (args, message);
417 # if defined(HAVE_VPRINTF) || defined(_LIBC)
418     vfprintf (errfp, message, args);
419 # else
420     _doprnt (message, args, errfp);
421 # endif
422     va_end (args);
423 #else
424     fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
425 #endif
426
427     ++report_message_count;
428     if (errnum)
429         fprintf (errfp, ": %s", strerror (errnum));
430     putc ('\n', errfp);
431     fflush (errfp);
432 }