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