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