]> Pileus Git - ~andy/fetchmail/blob - report.c
Expose the ESMTP name and password options.
[~andy/fetchmail] / report.c
1 /* 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 #if defined(HAVE_SYSLOG)
19 #include <syslog.h>
20 #endif
21 #include "i18n.h"
22 #include "fetchmail.h"
23
24 #if HAVE_VPRINTF || HAVE_DOPRNT || _LIBC || HAVE_STDARG_H
25 # if HAVE_STDARG_H
26 #  include <stdarg.h>
27 #  define VA_START(args, lastarg) va_start(args, lastarg)
28 # else
29 #  include <varargs.h>
30 #  define VA_START(args, lastarg) va_start(args)
31 # endif
32 #else
33 # define va_alist a1, a2, a3, a4, a5, a6, a7, a8
34 # define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
35 #endif
36
37 #define MALLOC(n)       xmalloc(n)      
38 #define REALLOC(n,s)    xrealloc(n,s)   
39
40 /* If NULL, report will flush stderr, then print on stderr the program
41    name, a colon and a space.  Otherwise, report will call this
42    function without parameters instead.  */
43 void (*report_print_progname) (
44 #if __STDC__ - 0
45                               void
46 #endif
47                               );
48
49 /* Used by report_build() and report_complete() to accumulate partial messages.
50  */
51 static unsigned int partial_message_size = 0;
52 static unsigned int partial_message_size_used = 0;
53 static char *partial_message;
54 static unsigned use_stderr;
55 static unsigned int use_syslog;
56
57 /* This variable is incremented each time `report' is called.  */
58 static unsigned int report_message_count;
59
60 #ifdef _LIBC
61 /* In the GNU C library, there is a predefined variable for this.  */
62
63 # define program_name program_invocation_name
64 # include <errno.h>
65
66 #else
67
68 /* The calling program should define program_name and set it to the
69    name of the executing program.  */
70 extern char *program_name;
71
72 # if !HAVE_STRERROR && !defined(strerror)
73 char *strerror (int errnum)
74 {
75     extern char *sys_errlist[];
76     extern int sys_nerr;
77
78     if (errnum > 0 && errnum <= sys_nerr)
79         return sys_errlist[errnum];
80     return GT_("Unknown system error");
81 }
82 # endif /* HAVE_STRERROR */
83 #endif  /* _LIBC */
84
85 /* Print the program name and error message MESSAGE, which is a printf-style
86    format string with optional args.
87    If ERRNUM is nonzero, print its corresponding system error message. */
88 /* VARARGS */
89
90 void
91 #ifdef HAVE_STDARG_H
92 report (FILE *errfp, const char *message, ...)
93 #else
94 report (FILE *errfp, message, va_alist)
95      const char *message;
96      va_dcl
97 #endif
98 {
99 #ifdef VA_START
100     va_list args;
101 #endif
102
103     /* If a partially built message exists, print it now so it's not lost.  */
104     if (partial_message_size_used != 0)
105     {
106         partial_message_size_used = 0;
107         report (errfp, 0, GT_("%s (log message incomplete)"), partial_message);
108     }
109
110 #if defined(HAVE_SYSLOG)
111     if (use_syslog)
112     {
113         int priority;
114
115 #ifdef VA_START
116         VA_START (args, message);
117 #endif
118         priority = (errfp == stderr) ? LOG_ERR : LOG_INFO;
119
120 #ifdef HAVE_VSYSLOG
121         vsyslog (priority, message, args);
122 #else
123         {
124             char *a1 = va_arg(args, char *);
125             char *a2 = va_arg(args, char *);
126             char *a3 = va_arg(args, char *);
127             char *a4 = va_arg(args, char *);
128             char *a5 = va_arg(args, char *);
129             char *a6 = va_arg(args, char *);
130             char *a7 = va_arg(args, char *);
131             char *a8 = va_arg(args, char *);
132             syslog (priority, message, a1, a2, a3, a4, a5, a6, a7, a8);
133         }
134 #endif
135
136 #ifdef VA_START
137         va_end(args);
138 #endif
139     }
140     else
141 #endif
142     {
143         if (report_print_progname)
144             (*report_print_progname) ();
145         else
146         {
147             fflush (errfp);
148             if ( *message == '\n' )
149             {
150                 fputc( '\n', errfp );
151                 ++message;
152             }
153             fprintf (errfp, "%s: ", program_name);
154         }
155
156 #ifdef VA_START
157         VA_START (args, message);
158 # if HAVE_VPRINTF || _LIBC
159         vfprintf (errfp, message, args);
160 # else
161         _doprnt (message, args, errfp);
162 # endif
163         va_end (args);
164 #else
165         fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
166 #endif
167         fflush (errfp);
168     }
169     ++report_message_count;
170 }
171 \f
172 /*
173  * Calling report_init(1) causes report_build and report_complete to write
174  * to errfp without buffering.  This is needed for the ticker dots to
175  * work correctly.
176  */
177 void report_init(int mode)
178 {
179     switch(mode)
180     {
181     case 0:                     /* errfp, buffered */
182     default:
183         use_stderr = FALSE;
184         use_syslog = FALSE;
185         break;
186
187     case 1:                     /* errfp, unbuffered */
188         use_stderr = TRUE;
189         use_syslog = FALSE;
190         break;
191
192 #ifdef HAVE_SYSLOG
193     case -1:                    /* syslogd */
194         use_stderr = FALSE;
195         use_syslog = TRUE;
196         break;
197 #endif /* HAVE_SYSLOG */
198     }
199 }
200 \f
201 /* Build an report message by appending MESSAGE, which is a printf-style
202    format string with optional args, to the existing report message (which may
203    be empty.)  The completed report message is finally printed (and reset to
204    empty) by calling report_complete().
205    If an intervening call to report() occurs when a partially constructed
206    message exists, then, in an attempt to keep the messages in their proper
207    sequence, the partial message will be printed as-is (with a trailing 
208    newline) before report() prints its message. */
209 /* VARARGS */
210
211 void
212 #ifdef HAVE_STDARG_H
213 report_build (FILE *errfp, const char *message, ...)
214 #else
215 report_build (FILE *errfp, message, va_alist)
216      const char *message;
217      va_dcl
218 #endif
219 {
220 #ifdef VA_START
221     va_list args;
222     int n;
223 #endif
224
225     /* Make an initial guess for the size of any single message fragment.  */
226     if (partial_message_size == 0)
227     {
228         partial_message_size_used = 0;
229         partial_message_size = 2048;
230         partial_message = MALLOC (partial_message_size);
231     }
232     else
233         if (partial_message_size - partial_message_size_used < 1024)
234         {
235             partial_message_size += 2048;
236             partial_message = REALLOC (partial_message, partial_message_size);
237         }
238
239 #if defined(VA_START)
240     VA_START (args, message);
241 #if HAVE_VSNPRINTF || _LIBC
242     for ( ; ; )
243     {
244         n = vsnprintf (partial_message + partial_message_size_used,
245                        partial_message_size - partial_message_size_used,
246                        message, args);
247
248         if (n < partial_message_size - partial_message_size_used)
249         {
250             partial_message_size_used += n;
251             break;
252         }
253
254         partial_message_size += 2048;
255         partial_message = REALLOC (partial_message, partial_message_size);
256     }
257 #else
258     vsprintf (partial_message + partial_message_size_used, message, args);
259     partial_message_size_used += strlen(partial_message+partial_message_size_used);
260
261     /* Attempt to catch memory overwrites... */
262     if (partial_message_size_used >= partial_message_size)
263     {
264         partial_message_size_used = 0;
265         report (stderr, GT_("partial error message buffer overflow"));
266     }
267 #endif
268     va_end (args);
269 #else
270 #if HAVE_SNPRINTF
271     for ( ; ; )
272     {
273         n = snprintf (partial_message + partial_message_size_used,
274                       partial_message_size - partial_message_size_used,
275                       message, a1, a2, a3, a4, a5, a6, a7, a8);
276
277         if (n < partial_message_size - partial_message_size_used)
278         {
279             partial_message_size_used += n;
280             break;
281         }
282
283         partial_message_size += 2048;
284         partial_message = REALLOC (partial_message, partial_message_size);
285     }
286 #else
287     sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8);
288
289     /* Attempt to catch memory overwrites... */
290     if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size)
291     {
292         partial_message_size_used = 0;
293         report (stderr, GT_("partial error message buffer overflow"));
294     }
295 #endif
296 #endif
297
298     if (use_stderr && partial_message_size_used != 0)
299     {
300         partial_message_size_used = 0;
301         fputs(partial_message, errfp);
302     }
303 }
304 \f
305 /* Complete a report message by appending MESSAGE, which is a printf-style
306    format string with optional args, to the existing report message (which may
307    be empty.)  The completed report message is then printed (and reset to
308    empty.) */
309 /* VARARGS */
310
311 void
312 #ifdef HAVE_STDARG_H
313 report_complete (FILE *errfp, const char *message, ...)
314 #else
315 report_complete (FILE *errfp, message, va_alist)
316      const char *message;
317      va_dcl
318 #endif
319 {
320 #ifdef VA_START
321     va_list args;
322     int n;
323 #endif
324
325     /* Make an initial guess for the size of any single message fragment.  */
326     if (partial_message_size == 0)
327     {
328         partial_message_size_used = 0;
329         partial_message_size = 2048;
330         partial_message = MALLOC (partial_message_size);
331     }
332     else
333         if (partial_message_size - partial_message_size_used < 1024)
334         {
335             partial_message_size += 2048;
336             partial_message = REALLOC (partial_message, partial_message_size);
337         }
338
339 #if defined(VA_START)
340     VA_START (args, message);
341 #if HAVE_VSNPRINTF || _LIBC
342     for ( ; ; )
343     {
344         n = vsnprintf (partial_message + partial_message_size_used,
345                        partial_message_size - partial_message_size_used,
346                        message, args);
347
348         if (n < partial_message_size - partial_message_size_used)
349         {
350             partial_message_size_used += n;
351             break;
352         }
353
354         partial_message_size += 2048;
355         partial_message = REALLOC (partial_message, partial_message_size);
356     }
357 #else
358     vsprintf (partial_message + partial_message_size_used, message, args);
359     partial_message_size_used += strlen(partial_message+partial_message_size_used);
360
361     /* Attempt to catch memory overwrites... */
362     if (partial_message_size_used >= partial_message_size)
363     {
364         partial_message_size_used = 0;
365         report (stderr, GT_("partial error message buffer overflow"));
366     }
367 #endif
368     va_end (args);
369 #else
370 #if HAVE_SNPRINTF
371     for ( ; ; )
372     {
373         n = snprintf (partial_message + partial_message_size_used,
374                       partial_message_size - partial_message_size_used,
375                       message, a1, a2, a3, a4, a5, a6, a7, a8);
376
377         if (n < partial_message_size - partial_message_size_used)
378         {
379             partial_message_size_used += n;
380             break;
381         }
382
383         partial_message_size += 2048;
384         partial_message = REALLOC (partial_message, partial_message_size);
385     }
386 #else
387     sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8);
388
389     /* Attempt to catch memory overwrites... */
390     if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size)
391     {
392         partial_message_size_used = 0;
393         report (stderr, GT_("partial error message buffer overflow"));
394     }
395 #endif
396 #endif
397
398     /* Finally... print it.  */
399     partial_message_size_used = 0;
400
401     if (use_stderr)
402     {
403         fputs(partial_message, errfp);
404         fflush (errfp);
405
406         ++report_message_count;
407     }
408     else
409         report(errfp, "%s", partial_message);
410 }
411 \f
412 /* Sometimes we want to have at most one error per line.  This
413    variable controls whether this mode is selected or not.  */
414 int error_one_per_line;
415
416 void
417 #ifdef HAVE_STDARG_H
418 report_at_line (FILE *errfp, int errnum, const char *file_name,
419                unsigned int line_number, const char *message, ...)
420 #else
421 report_at_line (FILE *errfp, errnum, file_name, line_number, message, va_alist)
422      int errnum;
423      const char *file_name;
424      unsigned int line_number;
425      const char *message;
426      va_dcl
427 #endif
428 {
429 #ifdef VA_START
430     va_list args;
431 #endif
432
433     if (error_one_per_line)
434     {
435         static const char *old_file_name;
436         static unsigned int old_line_number;
437
438         if (old_line_number == line_number &&
439             (file_name == old_file_name || !strcmp (old_file_name, file_name)))
440             /* Simply return and print nothing.  */
441             return;
442
443         old_file_name = file_name;
444         old_line_number = line_number;
445     }
446
447     if (report_print_progname)
448         (*report_print_progname) ();
449     else
450     {
451         fflush (errfp);
452         if ( *message == '\n' )
453         {
454             fputc( '\n', errfp );
455             ++message;
456         }
457         fprintf (errfp, "%s:", program_name);
458     }
459
460     if (file_name != NULL)
461         fprintf (errfp, "%s:%d: ", file_name, line_number);
462
463 #ifdef VA_START
464     VA_START (args, message);
465 # if HAVE_VPRINTF || _LIBC
466     vfprintf (errfp, message, args);
467 # else
468     _doprnt (message, args, errfp);
469 # endif
470     va_end (args);
471 #else
472     fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
473 #endif
474
475     ++report_message_count;
476     if (errnum)
477         fprintf (errfp, ": %s", strerror (errnum));
478     putc ('\n', errfp);
479     fflush (errfp);
480 }