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.
13 /* make glibc expose vsyslog(3): */
14 #define _DEFAULT_SOURCE
18 #include "fetchmail.h"
27 #define MALLOC(n) xmalloc(n)
28 #define REALLOC(n,s) xrealloc(n,s)
30 /* Used by report_build() and report_complete() to accumulate partial messages.
32 static unsigned int partial_message_size = 0;
33 static unsigned int partial_message_size_used = 0;
34 static char *partial_message;
35 static int partial_suppress_tag = 0;
37 static unsigned unbuffered;
38 static unsigned int use_syslog;
41 /* In the GNU C library, there is a predefined variable for this. */
43 # define program_name program_invocation_name
48 /* Print the program name and error message MESSAGE, which is a printf-style
49 format string with optional args. */
50 void report (FILE *errfp, const char *message, ...)
54 /* If a partially built message exists, print it now so it's not lost. */
55 if (partial_message_size_used != 0)
57 partial_message_size_used = 0;
58 report (errfp, GT_("%s (log message incomplete)\n"), partial_message);
65 va_start(args, message);
66 priority = (errfp == stderr) ? LOG_ERR : LOG_INFO;
69 vsyslog (priority, message, args);
73 vsnprintf(tmpbuf, sizeof tmpbuf, message, args);
74 syslog(priority, "%s", tmpbuf);
80 else /* i. e. not using syslog */
82 if ( *message == '\n' )
87 if (!partial_suppress_tag)
88 fprintf (errfp, "%s: ", program_name);
89 partial_suppress_tag = 0;
91 va_start (args, message);
92 vfprintf (errfp, message, args);
99 * Configure the report module. The output is set according to
102 void report_init(int mode /** 0: regular output, 1: unbuffered output, -1: syslog */)
106 case 0: /* errfp, buffered */
112 case 1: /* errfp, unbuffered */
117 case -1: /* syslogd */
124 /* Build an report message by appending MESSAGE, which is a printf-style
125 format string with optional args, to the existing report message (which may
126 be empty.) The completed report message is finally printed (and reset to
127 empty) by calling report_complete().
128 If an intervening call to report() occurs when a partially constructed
129 message exists, then, in an attempt to keep the messages in their proper
130 sequence, the partial message will be printed as-is (with a trailing
131 newline) before report() prints its message. */
133 static void rep_ensuresize(void) {
134 /* Make an initial guess for the size of any single message fragment. */
135 if (partial_message_size == 0)
137 partial_message_size_used = 0;
138 partial_message_size = 2048;
139 partial_message = (char *)MALLOC (partial_message_size);
142 if (partial_message_size - partial_message_size_used < 1024)
144 partial_message_size += 2048;
145 partial_message = (char *)REALLOC (partial_message, partial_message_size);
149 static void report_vbuild(const char *message, va_list args)
156 * args has to be initialized before every call of vsnprintf(),
157 * because vsnprintf() invokes va_arg macro and thus args is
158 * undefined after the call.
160 n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used,
163 /* output error, f. i. EILSEQ */
167 && (unsigned)n < partial_message_size - partial_message_size_used)
169 partial_message_size_used += n;
173 partial_message_size += 2048;
174 partial_message = (char *)REALLOC (partial_message, partial_message_size);
178 void report_build (FILE *errfp, const char *message, ...)
184 va_start(args, message);
185 report_vbuild(message, args);
188 if (unbuffered && partial_message_size_used != 0)
190 partial_message_size_used = 0;
191 fputs(partial_message, errfp);
195 void report_flush(FILE *errfp)
197 if (partial_message_size_used != 0)
199 partial_message_size_used = 0;
200 report(errfp, "%s", partial_message);
201 partial_suppress_tag = 1;
205 /* Complete a report message by appending MESSAGE, which is a printf-style
206 format string with optional args, to the existing report message (which may
207 be empty.) The completed report message is then printed (and reset to
209 void report_complete (FILE *errfp, const char *message, ...)
215 va_start(args, message);
216 report_vbuild(message, args);
219 /* Finally... print it. */
220 partial_message_size_used = 0;
224 fputs(partial_message, errfp);
228 report(errfp, "%s", partial_message);
231 /* Sometimes we want to have at most one error per line. This
232 variable controls whether this mode is selected or not. */
233 static int error_one_per_line;
235 /* If errnum is nonzero, print its corresponding system error message. */
236 void report_at_line (FILE *errfp, int errnum, const char *file_name,
237 unsigned int line_number, const char *message, ...)
241 if (error_one_per_line)
243 static const char *old_file_name;
244 static unsigned int old_line_number;
246 if (old_line_number == line_number &&
247 (file_name == old_file_name || (old_file_name != NULL && 0 == strcmp (old_file_name, file_name))))
248 /* Simply return and print nothing. */
251 old_file_name = file_name;
252 old_line_number = line_number;
256 if ( *message == '\n' )
258 fputc( '\n', errfp );
261 fprintf (errfp, "%s:", program_name);
263 if (file_name != NULL)
264 fprintf (errfp, "%s:%u: ", file_name, line_number);
266 va_start(args, message);
267 vfprintf (errfp, message, args);
271 fprintf (errfp, ": %s", strerror (errnum));