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