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