]> Pileus Git - ~andy/fetchmail/blob - report.c
Merge branch 'legacy_64' into legacy_6x
[~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 /* make glibc expose vsyslog(3): */
14 #define _DEFAULT_SOURCE
15 #define _BSD_SOURCE
16
17 #include "config.h"
18 #include "fetchmail.h"
19 #include <stdio.h>
20 #include <errno.h>
21 #include <string.h>
22 #include <syslog.h>
23 #include "i18n.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 #ifdef _LIBC
41 /* In the GNU C library, there is a predefined variable for this.  */
42
43 # define program_name program_invocation_name
44 # include <errno.h>
45
46 #endif  /* _LIBC */
47
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, ...)
51 {
52     va_list args;
53
54     /* If a partially built message exists, print it now so it's not lost.  */
55     if (partial_message_size_used != 0)
56     {
57         partial_message_size_used = 0;
58         report (errfp, GT_("%s (log message incomplete)\n"), partial_message);
59     }
60
61     if (use_syslog)
62     {
63         int priority;
64
65         va_start(args, message);
66         priority = (errfp == stderr) ? LOG_ERR : LOG_INFO;
67
68 #ifdef HAVE_VSYSLOG
69         vsyslog (priority, message, args);
70 #else
71         {
72             char tmpbuf[2048];
73             vsnprintf(tmpbuf, sizeof tmpbuf, message, args);
74             syslog(priority, "%s", tmpbuf);
75         }
76 #endif
77
78         va_end(args);
79     }
80     else /* i. e. not using syslog */
81     {
82         if ( *message == '\n' )
83         {
84             fputc( '\n', errfp );
85             ++message;
86         }
87         if (!partial_suppress_tag) {
88                 time_t timer;
89                 char buffer[64];
90                 struct tm* tm_info;
91                 timer = time(NULL);
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);
95         }
96         partial_suppress_tag = 0;
97
98         va_start (args, message);
99         vfprintf (errfp, message, args);
100         va_end (args);
101         fflush (errfp);
102     }
103 }
104
105 /**
106  * Configure the report module. The output is set according to
107  * \a mode.
108  */
109 void report_init(int mode /** 0: regular output, 1: unbuffered output, -1: syslog */)
110 {
111     switch(mode)
112     {
113     case 0:                     /* errfp, buffered */
114     default:
115         unbuffered = FALSE;
116         use_syslog = FALSE;
117         break;
118
119     case 1:                     /* errfp, unbuffered */
120         unbuffered = TRUE;
121         use_syslog = FALSE;
122         break;
123
124     case -1:                    /* syslogd */
125         unbuffered = FALSE;
126         use_syslog = TRUE;
127         break;
128     }
129 }
130
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. */
139
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)
143     {
144         partial_message_size_used = 0;
145         partial_message_size = 2048;
146         partial_message = (char *)MALLOC (partial_message_size);
147     }
148     else
149         if (partial_message_size - partial_message_size_used < 1024)
150         {
151             partial_message_size += 2048;
152             partial_message = (char *)REALLOC (partial_message, partial_message_size);
153         }
154 }
155
156 static void report_vbuild(const char *message, va_list args)
157 {
158     int n;
159
160     for ( ; ; )
161     {
162         /*
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.
166          */
167         n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used,
168                        message, args);
169
170         /* output error, f. i. EILSEQ */
171         if (n < 0) break;
172
173         if (n >= 0
174             && (unsigned)n < partial_message_size - partial_message_size_used)
175         {
176             partial_message_size_used += n;
177             break;
178         }
179
180         partial_message_size += 2048;
181         partial_message = (char *)REALLOC (partial_message, partial_message_size);
182     }
183 }
184
185 void report_build (FILE *errfp, const char *message, ...)
186 {
187     va_list args;
188
189     rep_ensuresize();
190
191     va_start(args, message);
192     report_vbuild(message, args);
193     va_end(args);
194
195     if (unbuffered && partial_message_size_used != 0)
196     {
197         partial_message_size_used = 0;
198         fputs(partial_message, errfp);
199     }
200 }
201
202 void report_flush(FILE *errfp)
203 {
204     if (partial_message_size_used != 0)
205     {
206         partial_message_size_used = 0;
207         report(errfp, "%s", partial_message);
208         partial_suppress_tag = 1;
209     }
210 }
211
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
215    empty.) */
216 void report_complete (FILE *errfp, const char *message, ...)
217 {
218     va_list args;
219
220     rep_ensuresize();
221
222     va_start(args, message);
223     report_vbuild(message, args);
224     va_end(args);
225
226     /* Finally... print it.  */
227     partial_message_size_used = 0;
228
229     if (unbuffered)
230     {
231         fputs(partial_message, errfp);
232         fflush (errfp);
233     }
234     else
235         report(errfp, "%s", partial_message);
236 }
237
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;
241
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, ...)
245 {
246     va_list args;
247
248     if (error_one_per_line)
249     {
250         static const char *old_file_name;
251         static unsigned int old_line_number;
252
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.  */
256             return;
257
258         old_file_name = file_name;
259         old_line_number = line_number;
260     }
261
262     fflush (errfp);
263     if ( *message == '\n' )
264     {
265         fputc( '\n', errfp );
266         ++message;
267     }
268     fprintf (errfp, "%s:", program_name);
269
270     if (file_name != NULL)
271         fprintf (errfp, "%s:%u: ", file_name, line_number);
272
273     va_start(args, message);
274     vfprintf (errfp, message, args);
275     va_end (args);
276
277     if (errnum)
278         fprintf (errfp, ": %s", strerror (errnum));
279     putc ('\n', errfp);
280     fflush (errfp);
281 }