1 /** \file 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 /* Used by report_build() and report_complete() to accumulate partial messages.
42 */
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;
47 /* default size for the allocation of the report buffer */
48 const size_t defaultsize = 4096;
49
50 static unsigned unbuffered;
51 static unsigned int use_syslog;
52
53 #ifdef _LIBC
54 /* In the GNU C library, there is a predefined variable for this. */
55
56 # define program_name program_invocation_name
57 # include <errno.h>
58
59 #else
60
61 # if !HAVE_STRERROR && !defined(strerror)
strerror(int errnum)62 char *strerror (int errnum)
63 {
64 extern char *sys_errlist[];
65 extern int sys_nerr;
66
67 if (errnum > 0 && errnum <= sys_nerr)
68 return sys_errlist[errnum];
69 return GT_("Unknown system error");
70 }
71 # endif /* HAVE_STRERROR */
72 #endif /* _LIBC */
73
74 /* Print the program name and error message MESSAGE, which is a printf-style
75 format string with optional args. */
76 /* VARARGS */
77 void
78 #ifdef HAVE_STDARG_H
report(FILE * errfp,const char * message,...)79 report (FILE *errfp, const char *message, ...)
80 #else
81 report (FILE *errfp, message, va_alist)
82 const char *message;
83 va_dcl
84 #endif
85 {
86 #ifdef VA_START
87 va_list args;
88 #endif
89
90 /* If a partially built message exists, print it now so it's not lost. */
91 if (partial_message_size_used != 0)
92 {
93 partial_message_size_used = 0;
94 report (errfp, GT_("%s (log message incomplete)\n"), partial_message);
95 }
96
97 #if defined(HAVE_SYSLOG)
98 if (use_syslog)
99 {
100 int priority;
101
102 #ifdef VA_START
103 VA_START (args, message);
104 #endif
105 priority = (errfp == stderr) ? LOG_ERR : LOG_INFO;
106
107 #ifdef HAVE_VSYSLOG
108 vsyslog (priority, message, args);
109 #else
110 {
111 char *a1 = va_arg(args, char *);
112 char *a2 = va_arg(args, char *);
113 char *a3 = va_arg(args, char *);
114 char *a4 = va_arg(args, char *);
115 char *a5 = va_arg(args, char *);
116 char *a6 = va_arg(args, char *);
117 char *a7 = va_arg(args, char *);
118 char *a8 = va_arg(args, char *);
119 syslog (priority, message, a1, a2, a3, a4, a5, a6, a7, a8);
120 }
121 #endif
122
123 #ifdef VA_START
124 va_end(args);
125 #endif
126 }
127 else /* i. e. not using syslog */
128 #endif
129 {
130 if ( *message == '\n' )
131 {
132 fputc( '\n', errfp );
133 ++message;
134 }
135 if (!partial_suppress_tag)
136 fprintf (errfp, "%s: ", program_name);
137 partial_suppress_tag = 0;
138
139 #ifdef VA_START
140 VA_START (args, message);
141 # if defined(HAVE_VPRINTF) || defined(_LIBC)
142 vfprintf (errfp, message, args);
143 # else
144 _doprnt (message, args, errfp);
145 # endif
146 va_end (args);
147 #else
148 fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
149 #endif
150 fflush (errfp);
151 }
152 }
153
154 /**
155 * Configure the report module. The output is set according to
156 * \a mode.
157 */
report_init(int mode)158 void report_init(int mode /** 0: regular output, 1: unbuffered output, -1: syslog */)
159 {
160 switch(mode)
161 {
162 case 0: /* errfp, buffered */
163 default:
164 unbuffered = FALSE;
165 use_syslog = FALSE;
166 break;
167
168 case 1: /* errfp, unbuffered */
169 unbuffered = TRUE;
170 use_syslog = FALSE;
171 break;
172
173 #ifdef HAVE_SYSLOG
174 case -1: /* syslogd */
175 unbuffered = FALSE;
176 use_syslog = TRUE;
177 break;
178 #endif /* HAVE_SYSLOG */
179 }
180 }
181
rep_ensuresize(size_t increment)182 static void rep_ensuresize(size_t increment) {
183 if (partial_message_size == 0)
184 {
185 /* initialization */
186 partial_message_size_used = 0;
187 /* avoid too many small allocations initially */
188 if (increment < defaultsize) increment = defaultsize;
189 partial_message_size = increment;
190 partial_message = (char *)MALLOC (partial_message_size);
191 }
192 else /* already have buffer -> resize if too little room */
193 {
194 if (increment < defaultsize) increment = defaultsize;
195 if (partial_message_size - partial_message_size_used < increment)
196 {
197 partial_message_size += increment;
198 partial_message = (char *)REALLOC (partial_message, partial_message_size);
199 }
200 }
201 }
202
203 /* Build an report message by appending MESSAGE, which is a printf-style
204 format string with optional args, to the existing report message (which may
205 be empty.) The completed report message is finally printed (and reset to
206 empty) by calling report_complete().
207 If an intervening call to report() occurs when a partially constructed
208 message exists, then, in an attempt to keep the messages in their proper
209 sequence, the partial message will be printed as-is (with a trailing
210 newline) before report() prints its message. */
211
212
213 /* VARARGS */
214 #ifdef HAVE_STDARG_H
report_vgetsize(const char * message,va_list args)215 static int report_vgetsize(const char *message, va_list args)
216 {
217 char tmp[1];
218
219 return vsnprintf(tmp, 1, message, args);
220 }
221
222 /* note that report_vbuild assumes that the buffer was already allocated. */
223 /* VARARGS */
report_vbuild(const char * message,va_list args)224 static int report_vbuild(const char *message, va_list args)
225 {
226 int n;
227
228 n = vsnprintf (partial_message + partial_message_size_used,
229 partial_message_size - partial_message_size_used,
230 message, args);
231
232 /* output error, f. i. EILSEQ */
233 if (n < 0)
234 return -1;
235
236 if (n > 0)
237 {
238 partial_message_size_used += n;
239 }
240
241 return n;
242 }
243 #endif
244
245 void
246 #ifdef HAVE_STDARG_H
report_build(FILE * errfp,const char * message,...)247 report_build (FILE *errfp, const char *message, ...)
248 #else
249 report_build (FILE *errfp, message, va_alist)
250 const char *message;
251 va_dcl
252 #endif
253 {
254 int n;
255 #ifdef VA_START
256 va_list args;
257 #endif
258
259 /* the logic is to first calculate the size,
260 * then reallocate, then fill the buffer
261 */
262
263 #if defined(VA_START)
264 VA_START(args, message);
265 n = report_vgetsize(message, args);
266 va_end(args);
267
268 rep_ensuresize(n + 1);
269
270 VA_START(args, message);
271 (void)report_vbuild(message, args);
272 va_end(args);
273 #else
274 {
275 char tmp[1];
276 /* note that SUSv2 specifies that with the 2nd argument zero, an
277 * unspecified value less than 1 were to be returned. This is not
278 * useful, so pass 1. */
279 n = snprintf (tmp, 1,
280 message, a1, a2, a3, a4, a5, a6, a7, a8);
281
282 if (n > 0)
283 rep_ensuresize(n + 1);
284 }
285
286 n = snprintf (partial_message + partial_message_size_used,
287 partial_message_size - partial_message_size_used,
288 message, a1, a2, a3, a4, a5, a6, a7, a8);
289
290 if (n > 0) partial_message_size_used += n;
291
292 #endif
293
294 if (unbuffered && partial_message_size_used != 0)
295 {
296 partial_message_size_used = 0;
297 fputs(partial_message, errfp);
298 }
299 }
300
report_flush(FILE * errfp)301 void report_flush(FILE *errfp)
302 {
303 if (partial_message_size_used != 0)
304 {
305 partial_message_size_used = 0;
306 report(errfp, "%s", partial_message);
307 partial_suppress_tag = 1;
308 }
309 }
310
311 /* Complete a report message by appending MESSAGE, which is a printf-style
312 format string with optional args, to the existing report message (which may
313 be empty.) The completed report message is then printed (and reset to
314 empty.) */
315 /* VARARGS */
316 void
317 #ifdef HAVE_STDARG_H
report_complete(FILE * errfp,const char * message,...)318 report_complete (FILE *errfp, const char *message, ...)
319 #else
320 report_complete (FILE *errfp, message, va_alist)
321 const char *message;
322 va_dcl
323 #endif
324 {
325 int n;
326 #ifdef VA_START
327 va_list args;
328
329 VA_START(args, message);
330 n = report_vgetsize(message, args);
331 va_end(args);
332
333 rep_ensuresize(n + 1);
334
335 VA_START(args, message);
336 (void)report_vbuild(message, args);
337 va_end(args);
338 #else
339 report_build(errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
340 #endif
341
342 /* Finally... print it. */
343 partial_message_size_used = 0;
344
345 if (unbuffered)
346 {
347 fputs(partial_message, errfp);
348 fflush (errfp);
349 }
350 else
351 report(errfp, "%s", partial_message);
352 }
353
354 /* Sometimes we want to have at most one error per line. This
355 variable controls whether this mode is selected or not. */
356 static int error_one_per_line;
357
358 /* If errnum is nonzero, print its corresponding system error message. */
359 void
360 #ifdef HAVE_STDARG_H
report_at_line(FILE * errfp,int errnum,const char * file_name,unsigned int line_number,const char * message,...)361 report_at_line (FILE *errfp, int errnum, const char *file_name,
362 unsigned int line_number, const char *message, ...)
363 #else
364 report_at_line (FILE *errfp, errnum, file_name, line_number, message, va_alist)
365 int errnum;
366 const char *file_name;
367 unsigned int line_number;
368 const char *message;
369 va_dcl
370 #endif
371 {
372 #ifdef VA_START
373 va_list args;
374 #endif
375
376 if (error_one_per_line)
377 {
378 static const char *old_file_name;
379 static unsigned int old_line_number;
380
381 if (old_line_number == line_number &&
382 (file_name == old_file_name || (old_file_name != NULL && 0 == strcmp (old_file_name, file_name))))
383 /* Simply return and print nothing. */
384 return;
385
386 old_file_name = file_name;
387 old_line_number = line_number;
388 }
389
390 fflush (errfp);
391 if ( *message == '\n' )
392 {
393 fputc( '\n', errfp );
394 ++message;
395 }
396 fprintf (errfp, "%s:", program_name);
397
398 if (file_name != NULL)
399 fprintf (errfp, "%s:%u: ", file_name, line_number);
400
401 #ifdef VA_START
402 VA_START (args, message);
403 # if defined(HAVE_VPRINTF) || defined(_LIBC)
404 vfprintf (errfp, message, args);
405 # else
406 _doprnt (message, args, errfp);
407 # endif
408 va_end (args);
409 #else
410 fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
411 #endif
412
413 if (errnum)
414 fprintf (errfp, ": %s", strerror (errnum));
415 putc ('\n', errfp);
416 fflush (errfp);
417 }
418