]> Pileus Git - ~andy/fetchmail/blob - report.c
Comment fix.
[~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       putc ('\n', errfp);
229       fflush (errfp);
230     }
231   ++error_message_count;
232 }
233 \f
234 /*
235  * Calling error_init(1) causes error_build and error_complete to write
236  * to errfp without buffering.  This is needed for the ticker dots to
237  * work correctly.
238  */
239 void error_init(int mode)
240 {
241     switch(mode)
242     {
243     case 0:                     /* errfp, buffered */
244     default:
245         use_stderr = FALSE;
246         use_syslog = FALSE;
247         break;
248
249     case 1:                     /* errfp, unbuffered */
250         use_stderr = TRUE;
251         use_syslog = FALSE;
252         break;
253
254 #ifdef HAVE_SYSLOG
255     case -1:                    /* syslogd */
256         use_stderr = FALSE;
257         use_syslog = TRUE;
258         break;
259 #endif /* HAVE_SYSLOG */
260     }
261 }
262 \f
263 /* Build an error message by appending MESSAGE, which is a printf-style
264    format string with optional args, to the existing error message (which may
265    be empty.)  The completed error message is finally printed (and reset to
266    empty) by calling error_complete().
267    If an intervening call to report() occurs when a partially constructed
268    message exists, then, in an attempt to keep the messages in their proper
269    sequence, the partial message will be printed as-is (with a trailing 
270    newline) before report() prints its message. */
271 /* VARARGS */
272
273 void
274 #ifdef HAVE_STDARG_H
275 report_build (FILE *errfp, const char *message, ...)
276 #else
277 report_build (FILE *errfp, message, va_alist)
278      const char *message;
279      va_dcl
280 #endif
281 {
282 #ifdef VA_START
283   va_list args;
284   int n;
285 #endif
286
287   /* Make an initial guess for the size of any single message fragment.  */
288   if (partial_message_size == 0)
289     {
290       partial_message_size_used = 0;
291       partial_message_size = 2048;
292       partial_message = MALLOC (partial_message_size);
293     }
294   else
295     if (partial_message_size - partial_message_size_used < 1024)
296       {
297         partial_message_size += 2048;
298         partial_message = REALLOC (partial_message, partial_message_size);
299       }
300
301 #if defined(VA_START)
302   VA_START (args, message);
303 #if HAVE_VSNPRINTF || _LIBC
304   for ( ; ; )
305     {
306       n = vsnprintf (partial_message + partial_message_size_used,
307                      partial_message_size - partial_message_size_used,
308                      message, args);
309
310       if (n < partial_message_size - partial_message_size_used)
311         {
312           partial_message_size_used += n;
313           break;
314         }
315
316       partial_message_size += 2048;
317       partial_message = REALLOC (partial_message, partial_message_size);
318     }
319 #else
320   vsprintf (partial_message + partial_message_size_used, message, args);
321   partial_message_size_used += strlen(partial_message+partial_message_size_used);
322
323   /* Attempt to catch memory overwrites... */
324   if (partial_message_size_used >= partial_message_size)
325     {
326       partial_message_size_used = 0;
327       report (PS_UNDEFINED, 0, _("partial error message buffer overflow"));
328     }
329 #endif
330   va_end (args);
331 #else
332 #if HAVE_SNPRINTF
333   for ( ; ; )
334     {
335       n = snprintf (partial_message + partial_message_size_used,
336                     partial_message_size - partial_message_size_used,
337                     message, a1, a2, a3, a4, a5, a6, a7, a8);
338
339       if (n < partial_message_size - partial_message_size_used)
340         {
341           partial_message_size_used += n;
342           break;
343         }
344
345       partial_message_size += 2048;
346       partial_message = REALLOC (partial_message, partial_message_size);
347     }
348 #else
349   sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8);
350
351   /* Attempt to catch memory overwrites... */
352   if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size)
353     {
354       partial_message_size_used = 0;
355       report (PS_UNDEFINED, 0, _("partial error message buffer overflow"));
356     }
357 #endif
358 #endif
359
360   if (use_stderr && partial_message_size_used != 0)
361     {
362       partial_message_size_used = 0;
363       fputs(partial_message, errfp);
364     }
365 }
366 \f
367 /* Complete an error message by appending MESSAGE, which is a printf-style
368    format string with optional args, to the existing error message (which may
369    be empty.)  The completed error message is then printed (and reset to
370    empty.) */
371 /* VARARGS */
372
373 void
374 #ifdef HAVE_STDARG_H
375 report_complete (FILE *errfp, int errnum, const char *message, ...)
376 #else
377 report_complete (FILE *errfp, errnum, message, va_alist)
378      int errnum;
379      const char *message;
380      va_dcl
381 #endif
382 {
383 #ifdef VA_START
384   va_list args;
385   int n;
386 #endif
387
388   /* Make an initial guess for the size of any single message fragment.  */
389   if (partial_message_size == 0)
390     {
391       partial_message_size_used = 0;
392       partial_message_size = 2048;
393       partial_message = MALLOC (partial_message_size);
394     }
395   else
396     if (partial_message_size - partial_message_size_used < 1024)
397       {
398         partial_message_size += 2048;
399         partial_message = REALLOC (partial_message, partial_message_size);
400       }
401
402 #if defined(VA_START)
403   VA_START (args, message);
404 #if HAVE_VSNPRINTF || _LIBC
405   for ( ; ; )
406     {
407       n = vsnprintf (partial_message + partial_message_size_used,
408                      partial_message_size - partial_message_size_used,
409                      message, args);
410
411       if (n < partial_message_size - partial_message_size_used)
412         {
413           partial_message_size_used += n;
414           break;
415         }
416
417       partial_message_size += 2048;
418       partial_message = REALLOC (partial_message, partial_message_size);
419     }
420 #else
421   vsprintf (partial_message + partial_message_size_used, message, args);
422   partial_message_size_used += strlen(partial_message+partial_message_size_used);
423
424   /* Attempt to catch memory overwrites... */
425   if (partial_message_size_used >= partial_message_size)
426     {
427       partial_message_size_used = 0;
428       report (PS_UNDEFINED, 0, _("partial error message buffer overflow"));
429     }
430 #endif
431   va_end (args);
432 #else
433 #if HAVE_SNPRINTF
434   for ( ; ; )
435     {
436       n = snprintf (partial_message + partial_message_size_used,
437                     partial_message_size - partial_message_size_used,
438                     message, a1, a2, a3, a4, a5, a6, a7, a8);
439
440       if (n < partial_message_size - partial_message_size_used)
441         {
442           partial_message_size_used += n;
443           break;
444         }
445
446       partial_message_size += 2048;
447       partial_message = REALLOC (partial_message, partial_message_size);
448     }
449 #else
450   sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8);
451
452   /* Attempt to catch memory overwrites... */
453   if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size)
454     {
455       partial_message_size_used = 0;
456       report (PS_UNDEFINED, 0, _("partial error message buffer overflow"));
457     }
458 #endif
459 #endif
460
461   /* Finally... print it.  */
462   partial_message_size_used = 0;
463
464   if (use_stderr)
465     {
466       fputs(partial_message, errfp);
467
468       if (errnum)
469         fprintf (errfp, ": %s", strerror (errnum));
470
471       putc ('\n', errfp);
472       fflush (errfp);
473
474       ++error_message_count;
475     }
476   else
477     report (errfp, errnum, "%s", partial_message);
478 }
479 \f
480 /* Sometimes we want to have at most one error per line.  This
481    variable controls whether this mode is selected or not.  */
482 int error_one_per_line;
483
484 void
485 #ifdef HAVE_STDARG_H
486 report_at_line (FILE *errfp, int errnum, const char *file_name,
487                unsigned int line_number, const char *message, ...)
488 #else
489 report_at_line (FILE *errfp, errnum, file_name, line_number, message, va_alist)
490      int errnum;
491      const char *file_name;
492      unsigned int line_number;
493      const char *message;
494      va_dcl
495 #endif
496 {
497 #ifdef VA_START
498   va_list args;
499 #endif
500
501   if (error_one_per_line)
502     {
503       static const char *old_file_name;
504       static unsigned int old_line_number;
505
506       if (old_line_number == line_number &&
507           (file_name == old_file_name || !strcmp (old_file_name, file_name)))
508         /* Simply return and print nothing.  */
509         return;
510
511       old_file_name = file_name;
512       old_line_number = line_number;
513     }
514
515   if (error_print_progname)
516     (*error_print_progname) ();
517   else
518     {
519       fflush (errfp);
520       if ( *message == '\n' )
521         {
522           fputc( '\n', errfp );
523           ++message;
524         }
525       fprintf (errfp, "%s:", program_name);
526     }
527
528   if (file_name != NULL)
529     fprintf (errfp, "%s:%d: ", file_name, line_number);
530
531 #ifdef VA_START
532   VA_START (args, message);
533 # if HAVE_VPRINTF || _LIBC
534   vfprintf (errfp, message, args);
535 # else
536   _doprnt (message, args, errfp);
537 # endif
538   va_end (args);
539 #else
540   fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
541 #endif
542
543   ++error_message_count;
544   if (errnum)
545     fprintf (errfp, ": %s", strerror (errnum));
546   putc ('\n', errfp);
547   fflush (errfp);
548 }