#include <data/settings.h>
#include <ui/terminal/read-line.h>
#include <libpspp/version.h>
+#include "exit.h"
+#include "linebreak.h"
#include "progname.h"
#include "gettext.h"
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;
-
- 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);
+
+ msg_emit (&m);
}
/* Writes MESSAGE formatted with printf, to stderr, if the
err_check_count (void)
{
if (get_errorbreak() && err_error_count)
- msg (MM, _("Terminating execution of syntax file due to error."));
+ msg (MN, _("Terminating execution of syntax file due to error."));
else if (err_error_count > get_mxerrs() )
- msg (MM, _("Errors (%d) exceeds limit (%d)."),
+ msg (MN, _("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)."),
+ msg (MN, _("Warnings (%d) exceed limit (%d)."),
err_error_count + err_warning_count, get_mxwarns() );
else
return;
getl_abort_noninteractive ();
}
-/* 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);
+static void puts_stdout (int line_indent, const char *line, size_t length);
+static void dump_message (char *msg,
+ void (*func) (int line_indent,
+ const char *line, size_t length),
+ unsigned width, unsigned indent);
void
-err_done (void)
+msg_done (void)
{
lex_done();
getl_uninitialize ();
readln_uninitialize();
}
+/* Emits E as an error message.
+ Frees `text' member in E. */
void
-err_vmsg (const struct error *e, const char *format, va_list args)
+msg_emit (const struct msg *m)
{
- /* Class flags. */
- enum
+ struct category
{
- ERR_IN_PROCEDURE = 01, /* 1=Display name of current procedure. */
- ERR_WITH_FILE = 02, /* 1=Display file name and line number. */
+ bool show_command_name; /* Show command name with error? */
+ bool show_file_location; /* Show syntax file location? */
};
- /* Describes one class of error. */
- struct error_class
+ static const struct category categories[] =
{
- int flags; /* Zero or more of ERR_*. */
- int *count; /* Counting category. */
- const char *banner; /* Banner. */
+ {false, false}, /* MSG_GENERAL. */
+ {true, true}, /* MSG_SYNTAX. */
+ {false, true}, /* MSG_DATA. */
};
- static const struct error_class error_classes[MSG_CLASS_CNT] =
+ struct severity
{
- {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 */
+ const char *name; /* How to identify this severity. */
+ int *count; /* Number of msgs with this severity so far. */
+ };
+
+ static struct severity severities[] =
+ {
+ {N_("error"), &err_error_count}, /* MSG_ERROR. */
+ {N_("warning"), &err_warning_count}, /* MSG_WARNING. */
+ {NULL, NULL}, /* MSG_NOTE. */
};
- struct string msg;
+ const struct category *category = &categories[m->category];
+ const struct severity *severity = &severities[m->severity];
+ struct string string = DS_INITIALIZER;
- assert (e->class >= 0 && e->class < MSG_CLASS_CNT);
- assert (format != NULL);
-
- ds_init (&msg, 64);
- if (e->where.file_name && (error_classes[e->class].flags & ERR_WITH_FILE))
+ if (category->show_file_location && m->where.file_name)
{
- ds_printf (&msg, "%s:", e->where.file_name);
- if (e->where.line_number != -1)
- ds_printf (&msg, "%d:", e->where.line_number);
- ds_putc (&msg, ' ');
+ ds_printf (&string, "%s:", m->where.file_name);
+ if (m->where.line_number != -1)
+ ds_printf (&string, "%d:", m->where.line_number);
+ ds_putc (&string, ' ');
}
- ds_printf (&msg, "%s: ", gettext (error_classes[e->class].banner));
+ if (severity->name != NULL)
+ ds_printf (&string, "%s: ", gettext (severity->name));
- {
- int *count = error_classes[e->class].count;
- if (count)
- (*count)++;
- }
+ if (severity->count != NULL)
+ ++*severity->count;
- if (command_name != NULL
- && (error_classes[e->class].flags & ERR_IN_PROCEDURE))
- ds_printf (&msg, "%s: ", command_name);
+ if (category->show_command_name && command_name != NULL)
+ ds_printf (&string, "%s: ", command_name);
- if (e->title)
- ds_puts (&msg, e->title);
-
- ds_vprintf (&msg, format, args);
+ ds_puts (&string, m->text);
/* 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());
+ send errors and messages. */
+ dump_message (ds_c_str (&string), puts_stdout, get_viewwidth (), 8);
- ds_destroy (&msg);
+ ds_destroy (&string);
+ free (m->text);
}
\f
/* Private functions. */
-#if 0
-/* Write S followed by a newline to stderr. */
-static void
-puts_stderr (const char *s)
+/* Write LINE_INDENT spaces, the LENGTH characters in LINE, then
+ a new-line to stdout. */
+static void puts_stdout (int line_indent,
+ const char *line, size_t length)
{
- fputs (s, stderr);
- fputc ('\n', stderr);
+ int i;
+ for (i = 0; i < line_indent; i++)
+ putchar (' ');
+ fwrite (line, 1, length, stdout);
+ putchar ('\n');
}
-#endif
-/* Write S followed by a newline to stdout. */
+/* Divides MSG into lines of WIDTH width for the first line and
+ WIDTH - INDENT width for each succeeding line. Each line is
+ passed to FUNC as a null-terminated string (no new-line
+ character is included in the string). */
static void
-puts_stdout (const char *s)
+dump_message (char *msg,
+ void (*func) (int line_indent, const char *line, size_t length),
+ unsigned width, unsigned indent)
{
- 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)
+ size_t length = strlen (msg);
+ char *string, *breaks;
+ int line_indent;
+ size_t line_start, i;
+
+ /* Allocate temporary buffers.
+ If we can't get memory for them, then just dump the whole
+ message. */
+ string = strdup (msg);
+ breaks = malloc (length);
+ if (string == NULL || breaks == NULL)
{
- func (msg);
+ free (string);
+ free (breaks);
+ func (0, msg, length);
return;
}
- /* Make sure the indent isn't too big relative to the page width. */
+ /* Break into lines. */
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. */
+ mbs_width_linebreaks (string, length,
+ width - indent, -indent, 0,
+ NULL, locale_charset (), breaks);
+
+ /* Pass lines to FUNC. */
+ line_start = 0;
+ line_indent = 0;
+ for (i = 0; i < length; i++)
+ switch (breaks[i])
{
- 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;
+ case UC_BREAK_POSSIBLE:
+ /* Break before this character,
+ and include this character in the next line. */
+ func (line_indent, &string[line_start], i - line_start);
+ line_start = i;
+ line_indent = indent;
+ break;
+ case UC_BREAK_MANDATORY:
+ /* Break before this character,
+ but don't include this character in the next line
+ (because it'string a new-line). */
+ func (line_indent, &string[line_start], i - line_start);
+ line_start = i + 1;
+ line_indent = indent;
+ break;
+ default:
+ break;
}
+ if (line_start < length)
+ func (line_indent, &string[line_start], length - line_start);
-
- /* 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);
+ free (string);
+ free (breaks);
}
/* 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;
}
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);