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