]> Pileus Git - ~andy/fetchmail/blob - report.c
50637d7bdd3781918b936f78927b3db9539020c4
[~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 GT_("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, const char *message, ...)
114 #else
115 report (FILE *errfp, message, va_alist)
116      const char *message;
117      va_dcl
118 #endif
119 {
120 #ifdef VA_START
121   va_list args;
122 #endif
123
124   /* If a partially built message exists, print it now so it's not lost.  */
125   if (partial_message_size_used != 0)
126     {
127       partial_message_size_used = 0;
128       report (errfp, 0, GT_("%s (log message incomplete)"), partial_message);
129     }
130
131 #if defined(HAVE_SYSLOG)
132   if (use_syslog)
133     {
134       int priority;
135
136 #ifdef VA_START
137       VA_START (args, message);
138 #endif
139       priority = (errfp == stderr) ? LOG_ERR : LOG_INFO;
140
141 #ifdef HAVE_VSYSLOG
142       vsyslog (priority, message, args);
143 #else
144       {
145           char *a1 = va_arg(args, char *);
146           char *a2 = va_arg(args, char *);
147           char *a3 = va_arg(args, char *);
148           char *a4 = va_arg(args, char *);
149           char *a5 = va_arg(args, char *);
150           char *a6 = va_arg(args, char *);
151           char *a7 = va_arg(args, char *);
152           char *a8 = va_arg(args, char *);
153           syslog (priority, message, a1, a2, a3, a4, a5, a6, a7, a8);
154       }
155 #endif
156
157 #ifdef VA_START
158       va_end(args);
159 #endif
160     }
161   else
162 #endif
163     {
164       if (error_print_progname)
165         (*error_print_progname) ();
166       else
167         {
168           fflush (errfp);
169           if ( *message == '\n' )
170             {
171               fputc( '\n', errfp );
172               ++message;
173             }
174           fprintf (errfp, "%s: ", program_name);
175         }
176
177 #ifdef VA_START
178       VA_START (args, message);
179 # if HAVE_VPRINTF || _LIBC
180       vfprintf (errfp, message, args);
181 # else
182       _doprnt (message, args, errfp);
183 # endif
184       va_end (args);
185 #else
186       fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
187 #endif
188       fflush (errfp);
189     }
190   ++error_message_count;
191 }
192 \f
193 /*
194  * Calling report_init(1) causes error_build and error_complete to write
195  * to errfp without buffering.  This is needed for the ticker dots to
196  * work correctly.
197  */
198 void report_init(int mode)
199 {
200     switch(mode)
201     {
202     case 0:                     /* errfp, buffered */
203     default:
204         use_stderr = FALSE;
205         use_syslog = FALSE;
206         break;
207
208     case 1:                     /* errfp, unbuffered */
209         use_stderr = TRUE;
210         use_syslog = FALSE;
211         break;
212
213 #ifdef HAVE_SYSLOG
214     case -1:                    /* syslogd */
215         use_stderr = FALSE;
216         use_syslog = TRUE;
217         break;
218 #endif /* HAVE_SYSLOG */
219     }
220 }
221 \f
222 /* Build an error message by appending MESSAGE, which is a printf-style
223    format string with optional args, to the existing error message (which may
224    be empty.)  The completed error message is finally printed (and reset to
225    empty) by calling error_complete().
226    If an intervening call to report() occurs when a partially constructed
227    message exists, then, in an attempt to keep the messages in their proper
228    sequence, the partial message will be printed as-is (with a trailing 
229    newline) before report() prints its message. */
230 /* VARARGS */
231
232 void
233 #ifdef HAVE_STDARG_H
234 report_build (FILE *errfp, const char *message, ...)
235 #else
236 report_build (FILE *errfp, message, va_alist)
237      const char *message;
238      va_dcl
239 #endif
240 {
241 #ifdef VA_START
242   va_list args;
243   int n;
244 #endif
245
246   /* Make an initial guess for the size of any single message fragment.  */
247   if (partial_message_size == 0)
248     {
249       partial_message_size_used = 0;
250       partial_message_size = 2048;
251       partial_message = MALLOC (partial_message_size);
252     }
253   else
254     if (partial_message_size - partial_message_size_used < 1024)
255       {
256         partial_message_size += 2048;
257         partial_message = REALLOC (partial_message, partial_message_size);
258       }
259
260 #if defined(VA_START)
261   VA_START (args, message);
262 #if HAVE_VSNPRINTF || _LIBC
263   for ( ; ; )
264     {
265       n = vsnprintf (partial_message + partial_message_size_used,
266                      partial_message_size - partial_message_size_used,
267                      message, args);
268
269       if (n < partial_message_size - partial_message_size_used)
270         {
271           partial_message_size_used += n;
272           break;
273         }
274
275       partial_message_size += 2048;
276       partial_message = REALLOC (partial_message, partial_message_size);
277     }
278 #else
279   vsprintf (partial_message + partial_message_size_used, message, args);
280   partial_message_size_used += strlen(partial_message+partial_message_size_used);
281
282   /* Attempt to catch memory overwrites... */
283   if (partial_message_size_used >= partial_message_size)
284     {
285       partial_message_size_used = 0;
286       report (stderr, GT_("partial error message buffer overflow"));
287     }
288 #endif
289   va_end (args);
290 #else
291 #if HAVE_SNPRINTF
292   for ( ; ; )
293     {
294       n = snprintf (partial_message + partial_message_size_used,
295                     partial_message_size - partial_message_size_used,
296                     message, a1, a2, a3, a4, a5, a6, a7, a8);
297
298       if (n < partial_message_size - partial_message_size_used)
299         {
300           partial_message_size_used += n;
301           break;
302         }
303
304       partial_message_size += 2048;
305       partial_message = REALLOC (partial_message, partial_message_size);
306     }
307 #else
308   sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8);
309
310   /* Attempt to catch memory overwrites... */
311   if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size)
312     {
313       partial_message_size_used = 0;
314       report (stderr, GT_("partial error message buffer overflow"));
315     }
316 #endif
317 #endif
318
319   if (use_stderr && partial_message_size_used != 0)
320     {
321       partial_message_size_used = 0;
322       fputs(partial_message, errfp);
323     }
324 }
325 \f
326 /* Complete an error message by appending MESSAGE, which is a printf-style
327    format string with optional args, to the existing error message (which may
328    be empty.)  The completed error message is then printed (and reset to
329    empty.) */
330 /* VARARGS */
331
332 void
333 #ifdef HAVE_STDARG_H
334 report_complete (FILE *errfp, const char *message, ...)
335 #else
336 report_complete (FILE *errfp, message, va_alist)
337      const char *message;
338      va_dcl
339 #endif
340 {
341 #ifdef VA_START
342   va_list args;
343   int n;
344 #endif
345
346   /* Make an initial guess for the size of any single message fragment.  */
347   if (partial_message_size == 0)
348     {
349       partial_message_size_used = 0;
350       partial_message_size = 2048;
351       partial_message = MALLOC (partial_message_size);
352     }
353   else
354     if (partial_message_size - partial_message_size_used < 1024)
355       {
356         partial_message_size += 2048;
357         partial_message = REALLOC (partial_message, partial_message_size);
358       }
359
360 #if defined(VA_START)
361   VA_START (args, message);
362 #if HAVE_VSNPRINTF || _LIBC
363   for ( ; ; )
364     {
365       n = vsnprintf (partial_message + partial_message_size_used,
366                      partial_message_size - partial_message_size_used,
367                      message, args);
368
369       if (n < partial_message_size - partial_message_size_used)
370         {
371           partial_message_size_used += n;
372           break;
373         }
374
375       partial_message_size += 2048;
376       partial_message = REALLOC (partial_message, partial_message_size);
377     }
378 #else
379   vsprintf (partial_message + partial_message_size_used, message, args);
380   partial_message_size_used += strlen(partial_message+partial_message_size_used);
381
382   /* Attempt to catch memory overwrites... */
383   if (partial_message_size_used >= partial_message_size)
384     {
385       partial_message_size_used = 0;
386       report (stderr, GT_("partial error message buffer overflow"));
387     }
388 #endif
389   va_end (args);
390 #else
391 #if HAVE_SNPRINTF
392   for ( ; ; )
393     {
394       n = snprintf (partial_message + partial_message_size_used,
395                     partial_message_size - partial_message_size_used,
396                     message, a1, a2, a3, a4, a5, a6, a7, a8);
397
398       if (n < partial_message_size - partial_message_size_used)
399         {
400           partial_message_size_used += n;
401           break;
402         }
403
404       partial_message_size += 2048;
405       partial_message = REALLOC (partial_message, partial_message_size);
406     }
407 #else
408   sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8);
409
410   /* Attempt to catch memory overwrites... */
411   if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size)
412     {
413       partial_message_size_used = 0;
414       report (stderr, GT_("partial error message buffer overflow"));
415     }
416 #endif
417 #endif
418
419   /* Finally... print it.  */
420   partial_message_size_used = 0;
421
422   if (use_stderr)
423     {
424       fputs(partial_message, errfp);
425       fflush (errfp);
426
427       ++error_message_count;
428     }
429   else
430     report(errfp, "%s", partial_message);
431 }
432 \f
433 /* Sometimes we want to have at most one error per line.  This
434    variable controls whether this mode is selected or not.  */
435 int error_one_per_line;
436
437 void
438 #ifdef HAVE_STDARG_H
439 report_at_line (FILE *errfp, int errnum, const char *file_name,
440                unsigned int line_number, const char *message, ...)
441 #else
442 report_at_line (FILE *errfp, errnum, file_name, line_number, message, va_alist)
443      int errnum;
444      const char *file_name;
445      unsigned int line_number;
446      const char *message;
447      va_dcl
448 #endif
449 {
450 #ifdef VA_START
451   va_list args;
452 #endif
453
454   if (error_one_per_line)
455     {
456       static const char *old_file_name;
457       static unsigned int old_line_number;
458
459       if (old_line_number == line_number &&
460           (file_name == old_file_name || !strcmp (old_file_name, file_name)))
461         /* Simply return and print nothing.  */
462         return;
463
464       old_file_name = file_name;
465       old_line_number = line_number;
466     }
467
468   if (error_print_progname)
469     (*error_print_progname) ();
470   else
471     {
472       fflush (errfp);
473       if ( *message == '\n' )
474         {
475           fputc( '\n', errfp );
476           ++message;
477         }
478       fprintf (errfp, "%s:", program_name);
479     }
480
481   if (file_name != NULL)
482     fprintf (errfp, "%s:%d: ", file_name, line_number);
483
484 #ifdef VA_START
485   VA_START (args, message);
486 # if HAVE_VPRINTF || _LIBC
487   vfprintf (errfp, message, args);
488 # else
489   _doprnt (message, args, errfp);
490 # endif
491   va_end (args);
492 #else
493   fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
494 #endif
495
496   ++error_message_count;
497   if (errnum)
498     fprintf (errfp, ": %s", strerror (errnum));
499   putc ('\n', errfp);
500   fflush (errfp);
501 }