]> 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                 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     case -1:                    /* syslogd */
118         unbuffered = FALSE;
119         use_syslog = TRUE;
120         break;
121     }
122 }
123
124 /* Build an report message by appending MESSAGE, which is a printf-style
125    format string with optional args, to the existing report message (which may
126    be empty.)  The completed report message is finally printed (and reset to
127    empty) by calling report_complete().
128    If an intervening call to report() occurs when a partially constructed
129    message exists, then, in an attempt to keep the messages in their proper
130    sequence, the partial message will be printed as-is (with a trailing 
131    newline) before report() prints its message. */
132
133 static void rep_ensuresize(void) {
134     /* Make an initial guess for the size of any single message fragment.  */
135     if (partial_message_size == 0)
136     {
137         partial_message_size_used = 0;
138         partial_message_size = 2048;
139         partial_message = (char *)MALLOC (partial_message_size);
140     }
141     else
142         if (partial_message_size - partial_message_size_used < 1024)
143         {
144             partial_message_size += 2048;
145             partial_message = (char *)REALLOC (partial_message, partial_message_size);
146         }
147 }
148
149 static void report_vbuild(const char *message, va_list args)
150 {
151     int n;
152
153     for ( ; ; )
154     {
155         /*
156          * args has to be initialized before every call of vsnprintf(), 
157          * because vsnprintf() invokes va_arg macro and thus args is 
158          * undefined after the call.
159          */
160         n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used,
161                        message, args);
162
163         /* output error, f. i. EILSEQ */
164         if (n < 0) break;
165
166         if (n >= 0
167             && (unsigned)n < partial_message_size - partial_message_size_used)
168         {
169             partial_message_size_used += n;
170             break;
171         }
172
173         partial_message_size += 2048;
174         partial_message = (char *)REALLOC (partial_message, partial_message_size);
175     }
176 }
177
178 void report_build (FILE *errfp, const char *message, ...)
179 {
180     va_list args;
181
182     rep_ensuresize();
183
184     va_start(args, message);
185     report_vbuild(message, args);
186     va_end(args);
187
188     if (unbuffered && partial_message_size_used != 0)
189     {
190         partial_message_size_used = 0;
191         fputs(partial_message, errfp);
192     }
193 }
194
195 void report_flush(FILE *errfp)
196 {
197     if (partial_message_size_used != 0)
198     {
199         partial_message_size_used = 0;
200         report(errfp, "%s", partial_message);
201         partial_suppress_tag = 1;
202     }
203 }
204
205 /* Complete a report message by appending MESSAGE, which is a printf-style
206    format string with optional args, to the existing report message (which may
207    be empty.)  The completed report message is then printed (and reset to
208    empty.) */
209 void report_complete (FILE *errfp, const char *message, ...)
210 {
211     va_list args;
212
213     rep_ensuresize();
214
215     va_start(args, message);
216     report_vbuild(message, args);
217     va_end(args);
218
219     /* Finally... print it.  */
220     partial_message_size_used = 0;
221
222     if (unbuffered)
223     {
224         fputs(partial_message, errfp);
225         fflush (errfp);
226     }
227     else
228         report(errfp, "%s", partial_message);
229 }
230
231 /* Sometimes we want to have at most one error per line.  This
232    variable controls whether this mode is selected or not.  */
233 static int error_one_per_line;
234
235 /* If errnum is nonzero, print its corresponding system error message. */
236 void report_at_line (FILE *errfp, int errnum, const char *file_name,
237                unsigned int line_number, const char *message, ...)
238 {
239     va_list args;
240
241     if (error_one_per_line)
242     {
243         static const char *old_file_name;
244         static unsigned int old_line_number;
245
246         if (old_line_number == line_number &&
247             (file_name == old_file_name || (old_file_name != NULL && 0 == strcmp (old_file_name, file_name))))
248             /* Simply return and print nothing.  */
249             return;
250
251         old_file_name = file_name;
252         old_line_number = line_number;
253     }
254
255     fflush (errfp);
256     if ( *message == '\n' )
257     {
258         fputc( '\n', errfp );
259         ++message;
260     }
261     fprintf (errfp, "%s:", program_name);
262
263     if (file_name != NULL)
264         fprintf (errfp, "%s:%u: ", file_name, line_number);
265
266     va_start(args, message);
267     vfprintf (errfp, message, args);
268     va_end (args);
269
270     if (errnum)
271         fprintf (errfp, ": %s", strerror (errnum));
272     putc ('\n', errfp);
273     fflush (errfp);
274 }