Continue reforming error message support. In this phase, drop actual
[pspp] / src / message.c
index dbd3c638912918d8115cfdc1b171d2bb5f2f1397..27fe62d36d94de36ffc31f4a266cd9939c61a7ec 100644 (file)
    02110-1301, USA. */
 
 #include <config.h>
+
 #include <libpspp/message.h>
+
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <libpspp/alloc.h>
+
 #include <data/file-name.h>
-#include <language/line-buffer.h>
 #include <language/lexer/lexer.h>
-#include <data/settings.h>
-#include <ui/terminal/read-line.h>
+#include <libpspp/alloc.h>
 #include <libpspp/version.h>
-#include "progname.h"
 
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-#define N_(msgid) msgid
-
-int err_error_count;
-int err_warning_count;
-
-int err_already_flagged;
-
-int err_verbosity;
+#include "progname.h"
+#include "xvasprintf.h"
 
+/* Current command name as set by msg_set_command_name(). */
 static char *command_name;
-\f
-/* Fairly common public functions. */
 
-/* Writes error message in CLASS, with title TITLE and text FORMAT,
-   formatted with printf, to the standard places. */
-void
-tmsg (int class, const char *title, const char *format, ...)
-{
-  struct error e;
-  va_list args;
+/* Message handler as set by msg_init(). */
+static void (*msg_handler) (const struct msg *);
 
-  e.class = class;
-  err_location (&e.where);
-  e.title = title;
-
-  va_start (args, format);
-  err_vmsg (&e, format, args);
-  va_end (args);
-}
+/* Public functions. */
 
 /* Writes error message in CLASS, with text FORMAT, formatted with
    printf, to the standard places. */
 void
-msg (int class, const char *format, ...)
+msg (enum msg_class class, const char *format, ...)
 {
-  struct error e;
+  struct msg m;
   va_list args;
 
-  e.class = class;
-  err_location (&e.where);
-  e.title = NULL;
-
+  m.category = msg_class_to_category (class);
+  m.severity = msg_class_to_severity (class);
+  msg_location (&m.where);
   va_start (args, format);
-  err_vmsg (&e, format, args);
+  m.text = xvasprintf (format, args);
   va_end (args);
-}
 
-/* Writes MESSAGE formatted with printf, to stderr, if the
-   verbosity level is at least LEVEL. */
-void
-verbose_msg (int level, const char *format, ...)
-{
-  if (err_verbosity >= level)
-    {
-      va_list args;
-  
-      va_start (args, format);
-      fprintf (stderr, "%s: ", program_name);
-      vfprintf (stderr, format, args);
-      putc ('\n', stderr);
-      va_end (args);
-    }
+  msg_emit (&m);
 }
 
-/* Checks whether we've had so many errors that it's time to quit
-   processing this syntax file. */
 void
-err_check_count (void)
+msg_init (void (*handler) (const struct msg *)) 
 {
-  if (get_errorbreak() && err_error_count)
-    msg (MM, _("Terminating execution of syntax file due to error."));
-  else if (err_error_count > get_mxerrs() )
-    msg (MM, _("Errors (%d) exceeds limit (%d)."),
-        err_error_count, get_mxerrs());
-  else if (err_error_count + err_warning_count > get_mxwarns() )
-    msg (MM, _("Warnings (%d) exceed limit (%d)."),
-        err_error_count + err_warning_count, get_mxwarns() );
-  else
-    return;
-
-  getl_abort_noninteractive ();
+  msg_handler = handler;
 }
 
-/* Some machines are broken.  Compensate. */
-#ifndef EXIT_SUCCESS
-#define EXIT_SUCCESS 0
-#endif
-
-#ifndef EXIT_FAILURE
-#define EXIT_FAILURE 1
-#endif
-
-static void puts_stdout (const char *s);
-static void dump_message (char *errbuf, unsigned indent,
-                         void (*func) (const char *), unsigned width);
-
 void
-err_done (void) 
+msg_done (void) 
 {
-  lex_done();
-  getl_uninitialize ();
-  readln_uninitialize();
 }
 
+/* Emits M as an error message.
+   Frees allocated data in M. */
 void
-err_vmsg (const struct error *e, const char *format, va_list args)
+msg_emit (struct msg *m) 
 {
-  /* Class flags. */
-  enum
-    {
-      ERR_IN_PROCEDURE = 01,   /* 1=Display name of current procedure. */
-      ERR_WITH_FILE = 02,      /* 1=Display filename and line number. */
-    };
-
-  /* Describes one class of error. */
-  struct error_class
-    {
-      int flags;               /* Zero or more of ERR_*. */
-      int *count;              /* Counting category. */
-      const char *banner;      /* Banner. */
-    };
-
-  static const struct error_class error_classes[MSG_CLASS_CNT] =
-    {
-      {3, &err_error_count, N_("error")},      /* SE */
-      {3, &err_warning_count, N_("warning")},  /* SW */
-      {3, NULL, N_("note")},                   /* SM */
-
-      {2, &err_error_count, N_("error")},      /* DE */
-      {2, &err_warning_count, N_("warning")},  /* DW */
-
-      {0, &err_error_count, N_("error")},      /* ME */
-      {0, &err_warning_count, N_("warning")},  /* MW */
-      {0, NULL, N_("note")},                   /* MM */
-    };
-
-  struct string msg;
-
-  assert (e->class >= 0 && e->class < MSG_CLASS_CNT);
-  assert (format != NULL);
-  
-  ds_init (&msg, 64);
-  if (e->where.filename && (error_classes[e->class].flags & ERR_WITH_FILE))
-    {
-      ds_printf (&msg, "%s:", e->where.filename);
-      if (e->where.line_number != -1)
-       ds_printf (&msg, "%d:", e->where.line_number);
-      ds_putc (&msg, ' ');
-    }
-
-  ds_printf (&msg, "%s: ", gettext (error_classes[e->class].banner));
-  
-  {
-    int *count = error_classes[e->class].count;
-    if (count)
-      (*count)++;
-  }
-  
-  if (command_name != NULL
-      && (error_classes[e->class].flags & ERR_IN_PROCEDURE))
-    ds_printf (&msg, "%s: ", command_name);
-
-  if (e->title)
-    ds_puts (&msg, e->title);
-
-  ds_vprintf (&msg, format, args);
-
-  /* FIXME: Check set_messages and set_errors to determine where to
-     send errors and messages.
-
-     Please note that this is not trivial.  We have to avoid an
-     infinite loop in reporting errors that originate in the output
-     section. */
-  dump_message (ds_c_str (&msg), 8, puts_stdout, get_viewwidth());
-
-  ds_destroy (&msg);
+  msg_handler (m);
+  free (m->text);
 }
 \f
 /* Private functions. */
 
-#if 0
-/* Write S followed by a newline to stderr. */
-static void
-puts_stderr (const char *s)
-{
-  fputs (s, stderr);
-  fputc ('\n', stderr);
-}
-#endif
-
-/* Write S followed by a newline to stdout. */
-static void
-puts_stdout (const char *s)
-{
-  puts (s);
-}
-
-/* Returns 1 if the line must be broken here */
-static int
-compulsory_break(int c)
-{
-  return ( c == '\n' );
-}
-
-/* Returns 1 if C is a `break character', that is, if it is a good
-   place to break a message into lines. */
-static inline int
-char_is_break (int quote, int c)
-{
-  return ((quote && c == '/')
-          || (!quote && (isspace (c) || c == '-' || c == '/'))); 
-}
-
-/* Returns 1 if C is a break character where the break should be made
-   BEFORE the character. */
-static inline int
-break_before (int quote, int c)
-{
-  return !quote && isspace (c);
-}
-
-/* If C is a break character, returns 1 if the break should be made
-   AFTER the character.  Does not return a meaningful result if C is
-   not a break character. */
-static inline int
-break_after (int quote, int c)
-{
-  return !break_before (quote, c);
-}
-
-/* If you want very long words that occur at a bad break point to be
-   broken into two lines even if they're shorter than a whole line by
-   themselves, define as 2/3, or 4/5, or whatever fraction of a whole
-   line you think is necessary in order to consider a word long enough
-   to break into pieces.  Otherwise, define as 0.  See code to grok
-   the details.  Do NOT parenthesize the expression!  */
-#define BREAK_LONG_WORD 0
-/* #define BREAK_LONG_WORD 2/3 */
-/* #define BREAK_LONG_WORD 4/5 */
-
-/* Divides MSG into lines of WIDTH width for the first line and WIDTH
-   - INDENT width for each succeeding line.  Each line is dumped
-   through FUNC, which may do with the string what it will. */
-static void
-dump_message (char *msg, unsigned indent, void (*func) (const char *),
-             unsigned width)
-{
-  char *cp;
-
-  /* 1 when at a position inside double quotes ("). */
-  int quote = 0;
-
-  /* Buffer for a single line. */
-  char *buf;
-
-  /* If the message is short, just print the full thing. */
-  if (strlen (msg) < width)
-    {
-      func (msg);
-      return;
-    }
-
-  /* Make sure the indent isn't too big relative to the page width. */
-  if (indent > width / 3)
-    indent = width / 3;
-  
-  buf = local_alloc (width + 2);
-
-  /* Advance WIDTH characters into MSG.
-     If that's a valid breakpoint, keep it; otherwise, back up.
-     Output the line. */
-  for (cp = msg; (unsigned) (cp - msg) < width - 1 && 
-        ! compulsory_break(*cp); cp++)
-    if (*cp == '"')
-      quote ^= 1;
-
-  if (break_after (quote, (unsigned char) *cp))
-    {
-      for (cp--; !char_is_break (quote, (unsigned char) *cp) && cp > msg; cp--)
-       if (*cp == '"')
-         quote ^= 1;
-      
-      if (break_after (quote, (unsigned char) *cp))
-       cp++;
-    }
-
-  if (cp <= msg + width * BREAK_LONG_WORD)
-    for (; cp < msg + width - 1; cp++)
-      if (*cp == '"')
-       quote ^= 1;
-  
-  {
-    int c = *cp;
-    *cp = '\0';
-    func (msg);
-    *cp = c;
-  }
-
-
-  /* Repeat above procedure for remaining lines. */
-  for (;;)
-    {
-      static int hard_break=0;
-
-      int idx=0;
-      char *cp2;
-
-      /* Advance past whitespace. */
-      if (! hard_break ) 
-       while ( isspace ((unsigned char) *cp) )
-         cp++;
-      else
-       cp++;
-
-      if (*cp == 0)
-         break; 
-
-
-      /* Advance WIDTH - INDENT characters. */
-      for (cp2 = cp; (unsigned) (cp2 - cp) < width - indent && 
-            *cp2 && !compulsory_break(*cp2);  cp2++)
-       if (*cp2 == '"')
-         quote ^= 1;
-      
-      if ( compulsory_break(*cp2) )
-       hard_break = 1;
-      else
-       hard_break = 0;
-
-
-      /* Back up if this isn't a breakpoint. */
-      {
-       unsigned w = cp2 - cp;
-       if (*cp2 && ! compulsory_break(*cp2) )
-       for (cp2--; !char_is_break (quote, (unsigned char) *cp2) && 
-              cp2 > cp;
-              cp2--)
-         {
-
-           if (*cp2 == '"')
-             quote ^= 1;
-         }
-
-       if (w == width - indent
-           && (unsigned) (cp2 - cp) <= (width - indent) * BREAK_LONG_WORD)
-         for (; (unsigned) (cp2 - cp) < width - indent && *cp2 ; cp2++)
-           if (*cp2 == '"')
-             quote ^= 1;
-      }
-
-      
-      /* Write out the line. */
-
-      memset (buf, ' ', indent);
-      memcpy (&buf[indent], cp, cp2 - cp);
-
-      buf[indent + idx + cp2 - cp] = '\0';
-      func (buf);
-      cp = cp2;
-    }
-
-  local_free (buf);
-}
-
 /* Sets COMMAND_NAME as the command name included in some kinds
    of error messages. */
 void
-err_set_command_name (const char *command_name_) 
+msg_set_command_name (const char *command_name_) 
 {
   free (command_name);
   command_name = command_name_ ? xstrdup (command_name_) : NULL;
 }
 
+/* Returns the current command name, or NULL if none. */
+const char *
+msg_get_command_name (void) 
+{
+  return command_name;
+}
+
 void 
 request_bug_report_and_abort(const char *msg )
 {
@@ -458,7 +149,7 @@ request_bug_report_and_abort(const char *msg )
 }
 
 void 
-err_assert_fail(const char *expr, const char *file, int line)
+msg_assert_fail(const char *expr, const char *file, int line)
 {
   char msg[256];
   snprintf(msg,256,"Assertion failed: %s:%d; (%s)",file,line,expr);