1 /** \file report.c report function for noninteractive utilities
3 * For license terms, see the file COPYING in this directory.
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.
19 #if defined(HAVE_SYSLOG)
23 #include "fetchmail.h"
25 #if defined(HAVE_VPRINTF) || defined(HAVE_DOPRNT) || defined(_LIBC) || defined(HAVE_STDARG_H)
28 # define VA_START(args, lastarg) va_start(args, lastarg)
31 # define VA_START(args, lastarg) va_start(args)
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;
38 #define MALLOC(n) xmalloc(n)
39 #define REALLOC(n,s) xrealloc(n,s)
41 /* Used by report_build() and report_complete() to accumulate partial messages.
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;
48 static unsigned unbuffered;
49 static unsigned int use_syslog;
52 /* In the GNU C library, there is a predefined variable for this. */
54 # define program_name program_invocation_name
59 # if !HAVE_STRERROR && !defined(strerror)
60 char *strerror (int errnum)
62 extern char *sys_errlist[];
65 if (errnum > 0 && errnum <= sys_nerr)
66 return sys_errlist[errnum];
67 return GT_("Unknown system error");
69 # endif /* HAVE_STRERROR */
72 /* Print the program name and error message MESSAGE, which is a printf-style
73 format string with optional args. */
77 report (FILE *errfp, const char *message, ...)
79 report (FILE *errfp, message, va_alist)
88 /* If a partially built message exists, print it now so it's not lost. */
89 if (partial_message_size_used != 0)
91 partial_message_size_used = 0;
92 report (errfp, GT_("%s (log message incomplete)\n"), partial_message);
95 #if defined(HAVE_SYSLOG)
101 VA_START (args, message);
103 priority = (errfp == stderr) ? LOG_ERR : LOG_INFO;
106 vsyslog (priority, message, args);
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);
125 else /* i. e. not using syslog */
128 if ( *message == '\n' )
130 fputc( '\n', errfp );
133 if (!partial_suppress_tag)
134 fprintf (errfp, "%s: ", program_name);
135 partial_suppress_tag = 0;
138 VA_START (args, message);
139 # if defined(HAVE_VPRINTF) || defined(_LIBC)
140 vfprintf (errfp, message, args);
142 _doprnt (message, args, errfp);
146 fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
153 * Configure the report module. The output is set according to
156 void report_init(int mode /** 0: regular output, 1: unbuffered output, -1: syslog */)
160 case 0: /* errfp, buffered */
166 case 1: /* errfp, unbuffered */
172 case -1: /* syslogd */
176 #endif /* HAVE_SYSLOG */
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. */
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)
194 partial_message_size_used = 0;
195 partial_message_size = 2048;
196 partial_message = (char *)MALLOC (partial_message_size);
199 if (partial_message_size - partial_message_size_used < 1024)
201 partial_message_size += 2048;
202 partial_message = (char *)REALLOC (partial_message, partial_message_size);
208 report_build (FILE *errfp, const char *message, ...)
210 report_build (FILE *errfp, message, va_alist)
222 #if defined(VA_START)
226 * args has to be initialized before every call of vsnprintf(),
227 * because vsnprintf() invokes va_arg macro and thus args is
228 * undefined after the call.
230 VA_START(args, message);
231 n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used,
236 && (unsigned)n < partial_message_size - partial_message_size_used)
238 partial_message_size_used += n;
242 partial_message_size += 2048;
243 partial_message = (char *)REALLOC (partial_message, partial_message_size);
248 n = snprintf (partial_message + partial_message_size_used,
249 partial_message_size - partial_message_size_used,
250 message, a1, a2, a3, a4, a5, a6, a7, a8);
253 && (unsigned)n < partial_message_size - partial_message_size_used)
255 partial_message_size_used += n;
259 partial_message_size += 2048;
260 partial_message = REALLOC (partial_message, partial_message_size);
264 if (unbuffered && partial_message_size_used != 0)
266 partial_message_size_used = 0;
267 fputs(partial_message, errfp);
271 void report_flush(FILE *errfp)
273 if (partial_message_size_used != 0)
275 partial_message_size_used = 0;
276 report(errfp, "%s", partial_message);
277 partial_suppress_tag = 1;
281 /* Complete a report message by appending MESSAGE, which is a printf-style
282 format string with optional args, to the existing report message (which may
283 be empty.) The completed report message is then printed (and reset to
288 report_complete (FILE *errfp, const char *message, ...)
290 report_complete (FILE *errfp, message, va_alist)
302 #if defined(VA_START)
305 VA_START(args, message);
306 n = vsnprintf (partial_message + partial_message_size_used,
307 partial_message_size - partial_message_size_used,
311 /* old glibc versions return -1 for truncation */
313 && (unsigned)n < partial_message_size - partial_message_size_used)
315 partial_message_size_used += n;
319 partial_message_size += 2048;
320 partial_message = (char *)REALLOC (partial_message, partial_message_size);
325 n = snprintf (partial_message + partial_message_size_used,
326 partial_message_size - partial_message_size_used,
327 message, a1, a2, a3, a4, a5, a6, a7, a8);
330 && (unsigned)n < partial_message_size - partial_message_size_used)
332 partial_message_size_used += n;
336 partial_message_size += 2048;
337 partial_message = REALLOC (partial_message, partial_message_size);
341 /* Finally... print it. */
342 partial_message_size_used = 0;
346 fputs(partial_message, errfp);
350 report(errfp, "%s", partial_message);
353 /* Sometimes we want to have at most one error per line. This
354 variable controls whether this mode is selected or not. */
355 static int error_one_per_line;
357 /* If errnum is nonzero, print its corresponding system error message. */
360 report_at_line (FILE *errfp, int errnum, const char *file_name,
361 unsigned int line_number, const char *message, ...)
363 report_at_line (FILE *errfp, errnum, file_name, line_number, message, va_alist)
365 const char *file_name;
366 unsigned int line_number;
375 if (error_one_per_line)
377 static const char *old_file_name;
378 static unsigned int old_line_number;
380 if (old_line_number == line_number &&
381 (file_name == old_file_name || !strcmp (old_file_name, file_name)))
382 /* Simply return and print nothing. */
385 old_file_name = file_name;
386 old_line_number = line_number;
390 if ( *message == '\n' )
392 fputc( '\n', errfp );
395 fprintf (errfp, "%s:", program_name);
397 if (file_name != NULL)
398 fprintf (errfp, "%s:%u: ", file_name, line_number);
401 VA_START (args, message);
402 # if defined(HAVE_VPRINTF) || defined(_LIBC)
403 vfprintf (errfp, message, args);
405 _doprnt (message, args, errfp);
409 fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
413 fprintf (errfp, ": %s", strerror (errnum));