]> Pileus Git - ~andy/fetchmail/blob - report.c
Attempt merging from 6.3.24.
[~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 #include <syslog.h>
20 #include "gettext.h"
21 #include "fetchmail.h"
22
23 #include <stdarg.h>
24
25 #define MALLOC(n)       xmalloc(n)      
26 #define REALLOC(n,s)    xrealloc(n,s)   
27
28 /* Used by report_build() and report_complete() to accumulate partial messages.
29  */
30 static unsigned int partial_message_size = 0;
31 static unsigned int partial_message_size_used = 0;
32 static char *partial_message;
33 static int partial_suppress_tag = 0;
34
35 static unsigned unbuffered;
36 static unsigned int use_syslog;
37
38 /* Print the program name and error message MESSAGE, which is a printf-style
39    format string with optional args. */
40 void report(FILE *errfp, const char *message, ...)
41 {
42     va_list args;
43
44     /* If a partially built message exists, print it now so it's not lost.  */
45     if (partial_message_size_used != 0)
46     {
47         partial_message_size_used = 0;
48         report (errfp, GT_("%s (log message incomplete)\n"), partial_message);
49     }
50
51     if (use_syslog)
52     {
53         int priority;
54
55         va_start (args, message);
56         priority = (errfp == stderr) ? LOG_ERR : LOG_INFO;
57
58 #ifdef HAVE_VSYSLOG
59         vsyslog (priority, message, args);
60 #else
61         {
62             char *a1 = va_arg(args, char *);
63             char *a2 = va_arg(args, char *);
64             char *a3 = va_arg(args, char *);
65             char *a4 = va_arg(args, char *);
66             char *a5 = va_arg(args, char *);
67             char *a6 = va_arg(args, char *);
68             char *a7 = va_arg(args, char *);
69             char *a8 = va_arg(args, char *);
70             syslog (priority, message, a1, a2, a3, a4, a5, a6, a7, a8);
71         }
72 #endif
73
74         va_end(args);
75     }
76     else /* i. e. not using syslog */
77     {
78         if ( *message == '\n' )
79         {
80             fputc( '\n', errfp );
81             ++message;
82         }
83         if (!partial_suppress_tag)
84                 fprintf (errfp, "%s: ", program_name);
85         partial_suppress_tag = 0;
86
87         va_start (args, message);
88         vfprintf (errfp, message, args);
89         va_end (args);
90         fflush (errfp);
91     }
92 }
93
94 /**
95  * Configure the report module. The output is set according to
96  * \a mode.
97  */
98 void report_init(int mode /** 0: regular output, 1: unbuffered output, -1: syslog */)
99 {
100     switch(mode)
101     {
102     case 0:                     /* errfp, buffered */
103     default:
104         unbuffered = FALSE;
105         use_syslog = FALSE;
106         break;
107
108     case 1:                     /* errfp, unbuffered */
109         unbuffered = TRUE;
110         use_syslog = FALSE;
111         break;
112
113     case -1:                    /* syslogd */
114         unbuffered = FALSE;
115         use_syslog = TRUE;
116         break;
117     }
118 }
119
120 /* Build an report message by appending MESSAGE, which is a printf-style
121    format string with optional args, to the existing report message (which may
122    be empty.)  The completed report message is finally printed (and reset to
123    empty) by calling report_complete().
124    If an intervening call to report() occurs when a partially constructed
125    message exists, then, in an attempt to keep the messages in their proper
126    sequence, the partial message will be printed as-is (with a trailing 
127    newline) before report() prints its message. */
128 /* VARARGS */
129
130 static void rep_ensuresize(void) {
131     /* Make an initial guess for the size of any single message fragment.  */
132     if (partial_message_size == 0)
133     {
134         partial_message_size_used = 0;
135         partial_message_size = 2048;
136         partial_message = (char *)MALLOC (partial_message_size);
137     }
138     else
139         if (partial_message_size - partial_message_size_used < 1024)
140         {
141             partial_message_size += 2048;
142             partial_message = (char *)REALLOC (partial_message, partial_message_size);
143         }
144 }
145
146 static void report_vbuild(const char *message, va_list args)
147 {
148     int n;
149
150     for ( ; ; )
151     {
152         /*
153          * args has to be initialized before every call of vsnprintf(), 
154          * because vsnprintf() invokes va_arg macro and thus args is 
155          * undefined after the call.
156          */
157         n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used,
158                        message, args);
159
160         /* output error, f. i. EILSEQ */
161         if (n < 0) break;
162
163         if (n >= 0
164             && (unsigned)n < partial_message_size - partial_message_size_used)
165         {
166             partial_message_size_used += n;
167             break;
168         }
169
170         partial_message_size += 2048;
171         partial_message = (char *)REALLOC (partial_message, partial_message_size);
172     }
173 }
174
175 void report_build (FILE *errfp, const char *message, ...)
176 {
177     va_list args;
178
179     rep_ensuresize();
180
181     va_start(args, message);
182     report_vbuild(message, args);
183     va_end(args);
184
185     if (unbuffered && partial_message_size_used != 0)
186     {
187         partial_message_size_used = 0;
188         fputs(partial_message, errfp);
189     }
190 }
191
192 void report_flush(FILE *errfp)
193 {
194     if (partial_message_size_used != 0)
195     {
196         partial_message_size_used = 0;
197         report(errfp, "%s", partial_message);
198         partial_suppress_tag = 1;
199     }
200 }
201
202 /* Complete a report message by appending MESSAGE, which is a printf-style
203    format string with optional args, to the existing report message (which may
204    be empty.)  The completed report message is then printed (and reset to
205    empty.) */
206 /* VARARGS */
207 void report_complete (FILE *errfp, const char *message, ...)
208 {
209     va_list args;
210
211     rep_ensuresize();
212
213     va_start(args, message);
214     report_vbuild(message, args);
215     va_end(args);
216
217     /* Finally... print it.  */
218     partial_message_size_used = 0;
219
220     if (unbuffered)
221     {
222         fputs(partial_message, errfp);
223         fflush (errfp);
224     }
225     else
226         report(errfp, "%s", partial_message);
227 }
228
229 /* Sometimes we want to have at most one error per line.  This
230    variable controls whether this mode is selected or not.  */
231 static int error_one_per_line;
232
233 /* If errnum is nonzero, print its corresponding system error message. */
234 void report_at_line (FILE *errfp, int errnum, const char *file_name,
235                unsigned int line_number, const char *message, ...)
236 {
237     va_list args;
238
239     if (error_one_per_line)
240     {
241         static const char *old_file_name;
242         static unsigned int old_line_number;
243
244         if (old_line_number == line_number &&
245             (file_name == old_file_name || !strcmp (old_file_name, file_name)))
246             /* Simply return and print nothing.  */
247             return;
248
249         old_file_name = file_name;
250         old_line_number = line_number;
251     }
252
253     fflush (errfp);
254     if ( *message == '\n' )
255     {
256         fputc( '\n', errfp );
257         ++message;
258     }
259     fprintf (errfp, "%s:", program_name);
260
261     if (file_name != NULL)
262         fprintf (errfp, "%s:%u: ", file_name, line_number);
263
264     va_start (args, message);
265     vfprintf (errfp, message, args);
266     va_end (args);
267
268     if (errnum)
269         fprintf (errfp, ": %s", strerror (errnum));
270     putc ('\n', errfp);
271     fflush (errfp);
272 }