]> Pileus Git - ~andy/fetchmail/blob - report.c
Add AM_PROG_CC_C_O.
[~andy/fetchmail] / report.c
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
48 static unsigned unbuffered;
49 static unsigned int use_syslog;
50
51 #ifdef _LIBC
52 /* In the GNU C library, there is a predefined variable for this.  */
53
54 # define program_name program_invocation_name
55 # include <errno.h>
56
57 #else
58
59 # if !HAVE_STRERROR && !defined(strerror)
60 char *strerror (int errnum)
61 {
62     extern char *sys_errlist[];
63     extern int sys_nerr;
64
65     if (errnum > 0 && errnum <= sys_nerr)
66         return sys_errlist[errnum];
67     return GT_("Unknown system error");
68 }
69 # endif /* HAVE_STRERROR */
70 #endif  /* _LIBC */
71
72 /* Print the program name and error message MESSAGE, which is a printf-style
73    format string with optional args. */
74 /* VARARGS */
75 void
76 #ifdef HAVE_STDARG_H
77 report (FILE *errfp, const char *message, ...)
78 #else
79 report (FILE *errfp, message, va_alist)
80      const char *message;
81      va_dcl
82 #endif
83 {
84 #ifdef VA_START
85     va_list args;
86 #endif
87
88     /* If a partially built message exists, print it now so it's not lost.  */
89     if (partial_message_size_used != 0)
90     {
91         partial_message_size_used = 0;
92         report (errfp, GT_("%s (log message incomplete)\n"), partial_message);
93     }
94
95 #if defined(HAVE_SYSLOG)
96     if (use_syslog)
97     {
98         int priority;
99
100 #ifdef VA_START
101         VA_START (args, message);
102 #endif
103         priority = (errfp == stderr) ? LOG_ERR : LOG_INFO;
104
105 #ifdef HAVE_VSYSLOG
106         vsyslog (priority, message, args);
107 #else
108         {
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);
118         }
119 #endif
120
121 #ifdef VA_START
122         va_end(args);
123 #endif
124     }
125     else /* i. e. not using syslog */
126 #endif
127     {
128         if ( *message == '\n' )
129         {
130             fputc( '\n', errfp );
131             ++message;
132         }
133         if (!partial_suppress_tag)
134                 fprintf (errfp, "%s: ", program_name);
135         partial_suppress_tag = 0;
136
137 #ifdef VA_START
138         VA_START (args, message);
139 # if defined(HAVE_VPRINTF) || defined(_LIBC)
140         vfprintf (errfp, message, args);
141 # else
142         _doprnt (message, args, errfp);
143 # endif
144         va_end (args);
145 #else
146         fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
147 #endif
148         fflush (errfp);
149     }
150 }
151
152 /**
153  * Configure the report module. The output is set according to
154  * \a mode.
155  */
156 void report_init(int mode /** 0: regular output, 1: unbuffered output, -1: syslog */)
157 {
158     switch(mode)
159     {
160     case 0:                     /* errfp, buffered */
161     default:
162         unbuffered = FALSE;
163         use_syslog = FALSE;
164         break;
165
166     case 1:                     /* errfp, unbuffered */
167         unbuffered = TRUE;
168         use_syslog = FALSE;
169         break;
170
171 #ifdef HAVE_SYSLOG
172     case -1:                    /* syslogd */
173         unbuffered = FALSE;
174         use_syslog = TRUE;
175         break;
176 #endif /* HAVE_SYSLOG */
177     }
178 }
179
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. */
188 /* VARARGS */
189
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)
193     {
194         partial_message_size_used = 0;
195         partial_message_size = 2048;
196         partial_message = (char *)MALLOC (partial_message_size);
197     }
198     else
199         if (partial_message_size - partial_message_size_used < 1024)
200         {
201             partial_message_size += 2048;
202             partial_message = (char *)REALLOC (partial_message, partial_message_size);
203         }
204 }
205
206 void
207 #ifdef HAVE_STDARG_H
208 report_build (FILE *errfp, const char *message, ...)
209 #else
210 report_build (FILE *errfp, message, va_alist)
211      const char *message;
212      va_dcl
213 #endif
214 {
215 #ifdef VA_START
216     va_list args;
217     int n;
218 #endif
219
220     rep_ensuresize();
221
222 #if defined(VA_START)
223     for ( ; ; )
224     {
225         /*
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.
229          */
230         VA_START(args, message);
231         n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used,
232                        message, args);
233         va_end (args);
234
235         if (n >= 0
236             && (unsigned)n < partial_message_size - partial_message_size_used)
237         {
238             partial_message_size_used += n;
239             break;
240         }
241
242         partial_message_size += 2048;
243         partial_message = (char *)REALLOC (partial_message, partial_message_size);
244     }
245 #else
246     for ( ; ; )
247     {
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);
251
252         if (n >= 0
253             && (unsigned)n < partial_message_size - partial_message_size_used)
254         {
255             partial_message_size_used += n;
256             break;
257         }
258
259         partial_message_size += 2048;
260         partial_message = REALLOC (partial_message, partial_message_size);
261     }
262 #endif
263
264     if (unbuffered && partial_message_size_used != 0)
265     {
266         partial_message_size_used = 0;
267         fputs(partial_message, errfp);
268     }
269 }
270
271 void report_flush(FILE *errfp)
272 {
273     if (partial_message_size_used != 0)
274     {
275         partial_message_size_used = 0;
276         report(errfp, "%s", partial_message);
277         partial_suppress_tag = 1;
278     }
279 }
280
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
284    empty.) */
285 /* VARARGS */
286 void
287 #ifdef HAVE_STDARG_H
288 report_complete (FILE *errfp, const char *message, ...)
289 #else
290 report_complete (FILE *errfp, message, va_alist)
291      const char *message;
292      va_dcl
293 #endif
294 {
295 #ifdef VA_START
296     va_list args;
297     int n;
298 #endif
299
300     rep_ensuresize();
301
302 #if defined(VA_START)
303     for ( ; ; )
304     {
305         VA_START(args, message);
306         n = vsnprintf (partial_message + partial_message_size_used,
307                        partial_message_size - partial_message_size_used,
308                        message, args);
309         va_end(args);
310
311         /* old glibc versions return -1 for truncation */
312         if (n >= 0
313             && (unsigned)n < partial_message_size - partial_message_size_used)
314         {
315             partial_message_size_used += n;
316             break;
317         }
318
319         partial_message_size += 2048;
320         partial_message = (char *)REALLOC (partial_message, partial_message_size);
321     }
322 #else
323     for ( ; ; )
324     {
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);
328
329         if (n >= 0
330             && (unsigned)n < partial_message_size - partial_message_size_used)
331         {
332             partial_message_size_used += n;
333             break;
334         }
335
336         partial_message_size += 2048;
337         partial_message = REALLOC (partial_message, partial_message_size);
338     }
339 #endif
340
341     /* Finally... print it.  */
342     partial_message_size_used = 0;
343
344     if (unbuffered)
345     {
346         fputs(partial_message, errfp);
347         fflush (errfp);
348     }
349     else
350         report(errfp, "%s", partial_message);
351 }
352
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;
356
357 /* If errnum is nonzero, print its corresponding system error message. */
358 void
359 #ifdef HAVE_STDARG_H
360 report_at_line (FILE *errfp, int errnum, const char *file_name,
361                unsigned int line_number, const char *message, ...)
362 #else
363 report_at_line (FILE *errfp, errnum, file_name, line_number, message, va_alist)
364      int errnum;
365      const char *file_name;
366      unsigned int line_number;
367      const char *message;
368      va_dcl
369 #endif
370 {
371 #ifdef VA_START
372     va_list args;
373 #endif
374
375     if (error_one_per_line)
376     {
377         static const char *old_file_name;
378         static unsigned int old_line_number;
379
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.  */
383             return;
384
385         old_file_name = file_name;
386         old_line_number = line_number;
387     }
388
389     fflush (errfp);
390     if ( *message == '\n' )
391     {
392         fputc( '\n', errfp );
393         ++message;
394     }
395     fprintf (errfp, "%s:", program_name);
396
397     if (file_name != NULL)
398         fprintf (errfp, "%s:%u: ", file_name, line_number);
399
400 #ifdef VA_START
401     VA_START (args, message);
402 # if defined(HAVE_VPRINTF) || defined(_LIBC)
403     vfprintf (errfp, message, args);
404 # else
405     _doprnt (message, args, errfp);
406 # endif
407     va_end (args);
408 #else
409     fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
410 #endif
411
412     if (errnum)
413         fprintf (errfp, ": %s", strerror (errnum));
414     putc ('\n', errfp);
415     fflush (errfp);
416 }