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);
207 static void report_vbuild(const char *message, va_list args)
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.
218 n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used,
221 /* output error, f. i. EILSEQ */
225 && (unsigned)n < partial_message_size - partial_message_size_used)
227 partial_message_size_used += n;
231 partial_message_size += 2048;
232 partial_message = (char *)REALLOC (partial_message, partial_message_size);
239 report_build (FILE *errfp, const char *message, ...)
241 report_build (FILE *errfp, message, va_alist)
254 #if defined(VA_START)
255 VA_START(args, message);
256 report_vbuild(message, args);
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);
265 /* output error, f. i. EILSEQ */
269 && (unsigned)n < partial_message_size - partial_message_size_used)
271 partial_message_size_used += n;
275 partial_message_size += 2048;
276 partial_message = REALLOC (partial_message, partial_message_size);
280 if (unbuffered && partial_message_size_used != 0)
282 partial_message_size_used = 0;
283 fputs(partial_message, errfp);
287 void report_flush(FILE *errfp)
289 if (partial_message_size_used != 0)
291 partial_message_size_used = 0;
292 report(errfp, "%s", partial_message);
293 partial_suppress_tag = 1;
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
304 report_complete (FILE *errfp, const char *message, ...)
306 report_complete (FILE *errfp, message, va_alist)
317 #if defined(VA_START)
318 VA_START(args, message);
319 report_vbuild(message, args);
322 report_build(errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
325 /* Finally... print it. */
326 partial_message_size_used = 0;
330 fputs(partial_message, errfp);
334 report(errfp, "%s", partial_message);
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;
341 /* If errnum is nonzero, print its corresponding system error message. */
344 report_at_line (FILE *errfp, int errnum, const char *file_name,
345 unsigned int line_number, const char *message, ...)
347 report_at_line (FILE *errfp, errnum, file_name, line_number, message, va_alist)
349 const char *file_name;
350 unsigned int line_number;
359 if (error_one_per_line)
361 static const char *old_file_name;
362 static unsigned int old_line_number;
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. */
369 old_file_name = file_name;
370 old_line_number = line_number;
374 if ( *message == '\n' )
376 fputc( '\n', errfp );
379 fprintf (errfp, "%s:", program_name);
381 if (file_name != NULL)
382 fprintf (errfp, "%s:%u: ", file_name, line_number);
385 VA_START (args, message);
386 # if defined(HAVE_VPRINTF) || defined(_LIBC)
387 vfprintf (errfp, message, args);
389 _doprnt (message, args, errfp);
393 fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
397 fprintf (errfp, ": %s", strerror (errnum));