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) {
92 tm_info = localtime(&timer);
93 strftime (buffer, sizeof(buffer), "%b %d %H:%M:%S", tm_info);
94 fprintf (errfp, "%s %s: ", buffer, program_name);
96 partial_suppress_tag = 0;
98 va_start (args, message);
99 vfprintf (errfp, message, args);
106 * Configure the report module. The output is set according to
109 void report_init(int mode /** 0: regular output, 1: unbuffered output, -1: syslog */)
113 case 0: /* errfp, buffered */
119 case 1: /* errfp, unbuffered */
124 case -1: /* syslogd */
131 /* Build an report message by appending MESSAGE, which is a printf-style
132 format string with optional args, to the existing report message (which may
133 be empty.) The completed report message is finally printed (and reset to
134 empty) by calling report_complete().
135 If an intervening call to report() occurs when a partially constructed
136 message exists, then, in an attempt to keep the messages in their proper
137 sequence, the partial message will be printed as-is (with a trailing
138 newline) before report() prints its message. */
140 static void rep_ensuresize(void) {
141 /* Make an initial guess for the size of any single message fragment. */
142 if (partial_message_size == 0)
144 partial_message_size_used = 0;
145 partial_message_size = 2048;
146 partial_message = (char *)MALLOC (partial_message_size);
149 if (partial_message_size - partial_message_size_used < 1024)
151 partial_message_size += 2048;
152 partial_message = (char *)REALLOC (partial_message, partial_message_size);
156 static void report_vbuild(const char *message, va_list args)
163 * args has to be initialized before every call of vsnprintf(),
164 * because vsnprintf() invokes va_arg macro and thus args is
165 * undefined after the call.
167 n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used,
170 /* output error, f. i. EILSEQ */
174 && (unsigned)n < partial_message_size - partial_message_size_used)
176 partial_message_size_used += n;
180 partial_message_size += 2048;
181 partial_message = (char *)REALLOC (partial_message, partial_message_size);
185 void report_build (FILE *errfp, const char *message, ...)
191 va_start(args, message);
192 report_vbuild(message, args);
195 if (unbuffered && partial_message_size_used != 0)
197 partial_message_size_used = 0;
198 fputs(partial_message, errfp);
202 void report_flush(FILE *errfp)
204 if (partial_message_size_used != 0)
206 partial_message_size_used = 0;
207 report(errfp, "%s", partial_message);
208 partial_suppress_tag = 1;
212 /* Complete a report message by appending MESSAGE, which is a printf-style
213 format string with optional args, to the existing report message (which may
214 be empty.) The completed report message is then printed (and reset to
216 void report_complete (FILE *errfp, const char *message, ...)
222 va_start(args, message);
223 report_vbuild(message, args);
226 /* Finally... print it. */
227 partial_message_size_used = 0;
231 fputs(partial_message, errfp);
235 report(errfp, "%s", partial_message);
238 /* Sometimes we want to have at most one error per line. This
239 variable controls whether this mode is selected or not. */
240 static int error_one_per_line;
242 /* If errnum is nonzero, print its corresponding system error message. */
243 void report_at_line (FILE *errfp, int errnum, const char *file_name,
244 unsigned int line_number, const char *message, ...)
248 if (error_one_per_line)
250 static const char *old_file_name;
251 static unsigned int old_line_number;
253 if (old_line_number == line_number &&
254 (file_name == old_file_name || (old_file_name != NULL && 0 == strcmp (old_file_name, file_name))))
255 /* Simply return and print nothing. */
258 old_file_name = file_name;
259 old_line_number = line_number;
263 if ( *message == '\n' )
265 fputc( '\n', errfp );
268 fprintf (errfp, "%s:", program_name);
270 if (file_name != NULL)
271 fprintf (errfp, "%s:%u: ", file_name, line_number);
273 va_start(args, message);
274 vfprintf (errfp, message, args);
278 fprintf (errfp, ": %s", strerror (errnum));