]> Pileus Git - ~andy/fetchmail/blobdiff - report.c
Merge logfile/syslog cleanup from legacy_63 branch.
[~andy/fetchmail] / report.c
index 19fb3f4598f695e9df411959df1691549789829c..b6101e406d7550c1b0be70c87a303344aea8e7cb 100644 (file)
--- a/report.c
+++ b/report.c
-/* error.c -- error handler for noninteractive utilities
-   Copyright (C) 1990, 91, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
-
-/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
+/** \file report.c report function for noninteractive utilities
+ *
+ * For license terms, see the file COPYING in this directory.
+ *
+ * This code is distantly descended from the error.c module written by
+ * David MacKenzie <djm@gnu.ai.mit.edu>.  It was redesigned and
+ * rewritten by Dave Bodenstab, then redesigned again by ESR, then
+ * bludgeoned into submission for SunOS 4.1.3 by Chris Cheyney
+ * <cheyney@netcom.com>.  It works even when the return from
+ * vprintf(3) is unreliable.
+ */
 
 #ifdef HAVE_CONFIG_H
-# include <config.h>
+# include "config.h"
 #endif
-
 #include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <syslog.h>
+#include "gettext.h"
+#include "fetchmail.h"
 
-#if HAVE_VPRINTF || HAVE_DOPRNT || _LIBC
-# if __STDC__
-#  include <stdarg.h>
-#  define VA_START(args, lastarg) va_start(args, lastarg)
-# else
-#  include <varargs.h>
-#  define VA_START(args, lastarg) va_start(args)
-# endif
-#else
-# define va_alist a1, a2, a3, a4, a5, a6, a7, a8
-# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
-#endif
+#include <stdarg.h>
 
-#if STDC_HEADERS || _LIBC
-# include <stdlib.h>
-# include <string.h>
-#else
-void exit ();
-#endif
+#define MALLOC(n)      xmalloc(n)      
+#define REALLOC(n,s)   xrealloc(n,s)   
 
-#ifndef _
-# define _(String) String
-#endif
+/* Used by report_build() and report_complete() to accumulate partial messages.
+ */
+static unsigned int partial_message_size = 0;
+static unsigned int partial_message_size_used = 0;
+static char *partial_message;
+static int partial_suppress_tag = 0;
 
-/* If NULL, error will flush stdout, then print on stderr the program
-   name, a colon and a space.  Otherwise, error will call this
-   function without parameters instead.  */
-void (*error_print_progname) (
-#if __STDC__ - 0
-                             void
-#endif
-                             );
+static unsigned unbuffered;
+static unsigned int use_syslog;
 
-/* This variable is incremented each time `error' is called.  */
-unsigned int error_message_count;
+/* Print the program name and error message MESSAGE, which is a printf-style
+   format string with optional args. */
+void report(FILE *errfp, const char *message, ...)
+{
+    va_list args;
+
+    /* If a partially built message exists, print it now so it's not lost.  */
+    if (partial_message_size_used != 0)
+    {
+       partial_message_size_used = 0;
+       report (errfp, GT_("%s (log message incomplete)\n"), partial_message);
+    }
 
-#ifdef _LIBC
-/* In the GNU C library, there is a predefined variable for this.  */
+    if (use_syslog)
+    {
+       int priority;
 
-# define program_name program_invocation_name
-# include <errno.h>
+       va_start (args, message);
+       priority = (errfp == stderr) ? LOG_ERR : LOG_INFO;
 
+#ifdef HAVE_VSYSLOG
+       vsyslog (priority, message, args);
 #else
+       {
+           char *a1 = va_arg(args, char *);
+           char *a2 = va_arg(args, char *);
+           char *a3 = va_arg(args, char *);
+           char *a4 = va_arg(args, char *);
+           char *a5 = va_arg(args, char *);
+           char *a6 = va_arg(args, char *);
+           char *a7 = va_arg(args, char *);
+           char *a8 = va_arg(args, char *);
+           syslog (priority, message, a1, a2, a3, a4, a5, a6, a7, a8);
+       }
+#endif
 
-/* The calling program should define program_name and set it to the
-   name of the executing program.  */
-extern char *program_name;
-
-# if HAVE_STRERROR
-#  ifndef strerror             /* On some systems, strerror is a macro */
-char *strerror ();
-#  endif
-# else
-static char *
-private_strerror (errnum)
-     int errnum;
+       va_end(args);
+    }
+    else /* i. e. not using syslog */
+    {
+       if ( *message == '\n' )
+       {
+           fputc( '\n', errfp );
+           ++message;
+       }
+       if (!partial_suppress_tag)
+               fprintf (errfp, "%s: ", program_name);
+       partial_suppress_tag = 0;
+
+       va_start (args, message);
+       vfprintf (errfp, message, args);
+       va_end (args);
+       fflush (errfp);
+    }
+}
+
+/**
+ * Configure the report module. The output is set according to
+ * \a mode.
+ */
+void report_init(int mode /** 0: regular output, 1: unbuffered output, -1: syslog */)
 {
-  extern char *sys_errlist[];
-  extern int sys_nerr;
+    switch(mode)
+    {
+    case 0:                    /* errfp, buffered */
+    default:
+       unbuffered = FALSE;
+       use_syslog = FALSE;
+       break;
+
+    case 1:                    /* errfp, unbuffered */
+       unbuffered = TRUE;
+       use_syslog = FALSE;
+       break;
 
-  if (errnum > 0 && errnum <= sys_nerr)
-    return sys_errlist[errnum];
-  return _("Unknown system error");
+    case -1:                   /* syslogd */
+       unbuffered = FALSE;
+       use_syslog = TRUE;
+       break;
+    }
 }
-#  define strerror private_strerror
-# endif        /* HAVE_STRERROR */
-#endif /* _LIBC */
 
-/* Print the program name and error message MESSAGE, which is a printf-style
-   format string with optional args.
-   If ERRNUM is nonzero, print its corresponding system error message.
-   Exit with status STATUS if it is nonzero.  */
+/* Build an report message by appending MESSAGE, which is a printf-style
+   format string with optional args, to the existing report message (which may
+   be empty.)  The completed report message is finally printed (and reset to
+   empty) by calling report_complete().
+   If an intervening call to report() occurs when a partially constructed
+   message exists, then, in an attempt to keep the messages in their proper
+   sequence, the partial message will be printed as-is (with a trailing 
+   newline) before report() prints its message. */
 /* VARARGS */
 
-void
-#if defined(VA_START) && __STDC__
-error (int status, int errnum, const char *message, ...)
-#else
-error (status, errnum, message, va_alist)
-     int status;
-     int errnum;
-     char *message;
-     va_dcl
-#endif
+static void rep_ensuresize(void) {
+    /* Make an initial guess for the size of any single message fragment.  */
+    if (partial_message_size == 0)
+    {
+       partial_message_size_used = 0;
+       partial_message_size = 2048;
+       partial_message = (char *)MALLOC (partial_message_size);
+    }
+    else
+       if (partial_message_size - partial_message_size_used < 1024)
+       {
+           partial_message_size += 2048;
+           partial_message = (char *)REALLOC (partial_message, partial_message_size);
+       }
+}
+
+static void report_vbuild(const char *message, va_list args)
 {
-#ifdef VA_START
-  va_list args;
-#endif
+    int n;
 
-  if (error_print_progname)
-    (*error_print_progname) ();
-  else
+    for ( ; ; )
     {
-      fflush (stdout);
-      if ( *message == '\n' )
-       {
-         fputc( '\n', stderr );
-         ++message;
+       /*
+        * args has to be initialized before every call of vsnprintf(), 
+        * because vsnprintf() invokes va_arg macro and thus args is 
+        * undefined after the call.
+        */
+       n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used,
+                      message, args);
+
+       /* output error, f. i. EILSEQ */
+       if (n < 0) break;
+
+       if (n >= 0
+           && (unsigned)n < partial_message_size - partial_message_size_used)
+        {
+           partial_message_size_used += n;
+           break;
        }
-      fprintf (stderr, "%s: ", program_name);
+
+       partial_message_size += 2048;
+       partial_message = (char *)REALLOC (partial_message, partial_message_size);
     }
+}
 
-#ifdef VA_START
-  VA_START (args, message);
-# if HAVE_VPRINTF || _LIBC
-  vfprintf (stderr, message, args);
-# else
-  _doprnt (message, args, stderr);
-# endif
-  va_end (args);
-#else
-  fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
-#endif
+void report_build (FILE *errfp, const char *message, ...)
+{
+    va_list args;
+
+    rep_ensuresize();
+
+    va_start(args, message);
+    report_vbuild(message, args);
+    va_end(args);
+
+    if (unbuffered && partial_message_size_used != 0)
+    {
+       partial_message_size_used = 0;
+       fputs(partial_message, errfp);
+    }
+}
+
+void report_flush(FILE *errfp)
+{
+    if (partial_message_size_used != 0)
+    {
+       partial_message_size_used = 0;
+       report(errfp, "%s", partial_message);
+       partial_suppress_tag = 1;
+    }
+}
+
+/* Complete a report message by appending MESSAGE, which is a printf-style
+   format string with optional args, to the existing report message (which may
+   be empty.)  The completed report message is then printed (and reset to
+   empty.) */
+/* VARARGS */
+void report_complete (FILE *errfp, const char *message, ...)
+{
+    va_list args;
 
-  ++error_message_count;
-  if (errnum)
-    fprintf (stderr, ": %s", strerror (errnum));
-  putc ('\n', stderr);
-  fflush (stderr);
-  if (status)
-    exit (status);
+    rep_ensuresize();
+
+    va_start(args, message);
+    report_vbuild(message, args);
+    va_end(args);
+
+    /* Finally... print it.  */
+    partial_message_size_used = 0;
+
+    if (unbuffered)
+    {
+       fputs(partial_message, errfp);
+       fflush (errfp);
+    }
+    else
+       report(errfp, "%s", partial_message);
 }
-\f
+
 /* Sometimes we want to have at most one error per line.  This
    variable controls whether this mode is selected or not.  */
-int error_one_per_line;
+static int error_one_per_line;
 
-void
-#if defined(VA_START) && __STDC__
-error_at_line (int status, int errnum, const char *file_name,
+/* If errnum is nonzero, print its corresponding system error message. */
+void report_at_line (FILE *errfp, int errnum, const char *file_name,
               unsigned int line_number, const char *message, ...)
-#else
-error_at_line (status, errnum, file_name, line_number, message, va_alist)
-     int status;
-     int errnum;
-     const char *file_name;
-     unsigned int line_number;
-     char *message;
-     va_dcl
-#endif
 {
-#ifdef VA_START
-  va_list args;
-#endif
+    va_list args;
 
-  if (error_one_per_line)
+    if (error_one_per_line)
     {
-      static const char *old_file_name;
-      static unsigned int old_line_number;
+       static const char *old_file_name;
+       static unsigned int old_line_number;
 
-      if (old_line_number == line_number &&
-         (file_name == old_file_name || !strcmp (old_file_name, file_name)))
-       /* Simply return and print nothing.  */
-       return;
+       if (old_line_number == line_number &&
+           (file_name == old_file_name || !strcmp (old_file_name, file_name)))
+           /* Simply return and print nothing.  */
+           return;
 
-      old_file_name = file_name;
-      old_line_number = line_number;
+       old_file_name = file_name;
+       old_line_number = line_number;
     }
 
-  if (error_print_progname)
-    (*error_print_progname) ();
-  else
+    fflush (errfp);
+    if ( *message == '\n' )
     {
-      fflush (stdout);
-      if ( *message == '\n' )
-       {
-         fputc( '\n', stderr );
-         ++message;
-       }
-      fprintf (stderr, "%s:", program_name);
+       fputc( '\n', errfp );
+       ++message;
     }
+    fprintf (errfp, "%s:", program_name);
 
-  if (file_name != NULL)
-    fprintf (stderr, "%s:%d: ", file_name, line_number);
-
-#ifdef VA_START
-  VA_START (args, message);
-# if HAVE_VPRINTF || _LIBC
-  vfprintf (stderr, message, args);
-# else
-  _doprnt (message, args, stderr);
-# endif
-  va_end (args);
-#else
-  fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
-#endif
+    if (file_name != NULL)
+       fprintf (errfp, "%s:%u: ", file_name, line_number);
+
+    va_start (args, message);
+    vfprintf (errfp, message, args);
+    va_end (args);
 
-  ++error_message_count;
-  if (errnum)
-    fprintf (stderr, ": %s", strerror (errnum));
-  putc ('\n', stderr);
-  fflush (stderr);
-  if (status)
-    exit (status);
+    if (errnum)
+       fprintf (errfp, ": %s", strerror (errnum));
+    putc ('\n', errfp);
+    fflush (errfp);
 }