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