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