#include <unistd.h>
#include "libpspp/cast.h"
+#include "libpspp/intern.h"
#include "libpspp/str.h"
#include "libpspp/version.h"
#include "data/settings.h"
#define _(msgid) gettext (msgid)
/* Message handler as set by msg_set_handler(). */
-static void (*msg_handler) (const struct msg *, void *aux);
-static void *msg_aux;
+static struct msg_handler msg_handler = { .output_msg = NULL };
/* Disables emitting messages if positive. */
static int messages_disabled;
void
-vmsg (enum msg_class class, const char *format, va_list args)
+vmsg (enum msg_class class, const struct msg_location *location,
+ const char *format, va_list args)
{
struct msg *m = xmalloc (sizeof *m);
*m = (struct msg) {
.category = msg_class_to_category (class),
.severity = msg_class_to_severity (class),
+ .location = msg_location_dup (location),
.text = xvasprintf (format, args),
};
msg_emit (m);
{
va_list args;
va_start (args, format);
- vmsg (class, format, args);
+ vmsg (class, NULL, format, args);
va_end (args);
}
-
+/* Outputs error message in CLASS, with text FORMAT, formatted with printf.
+ LOCATION is the reported location for the message. */
+void
+msg_at (enum msg_class class, const struct msg_location *location,
+ const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ vmsg (class, location, format, args);
+ va_end (args);
+}
void
msg_error (int errnum, const char *format, ...)
msg_emit (m);
}
-
-
void
-msg_set_handler (void (*handler) (const struct msg *, void *aux), void *aux)
+msg_set_handler (const struct msg_handler *handler)
{
- msg_handler = handler;
- msg_aux = aux;
+ msg_handler = *handler;
}
\f
/* msg_location. */
+void
+msg_location_uninit (struct msg_location *loc)
+{
+ if (msg_handler.lex_source_unref)
+ msg_handler.lex_source_unref (loc->src);
+ intern_unref (loc->file_name);
+}
+
void
msg_location_destroy (struct msg_location *loc)
{
if (loc)
{
- free (loc->file_name);
+ msg_location_uninit (loc);
free (loc);
}
}
+static int
+msg_point_compare_3way (const struct msg_point *a, const struct msg_point *b)
+{
+ return (!a->line ? 1
+ : !b->line ? -1
+ : a->line > b->line ? 1
+ : a->line < b->line ? -1
+ : !a->column ? 1
+ : !b->column ? -1
+ : a->column > b->column ? 1
+ : a->column < b->column ? -1
+ : 0);
+}
+
+void
+msg_location_remove_columns (struct msg_location *location)
+{
+ location->start.column = 0;
+ location->end.column = 0;
+}
+
+void
+msg_location_merge (struct msg_location **dstp, const struct msg_location *src)
+{
+ struct msg_location *dst = *dstp;
+ if (!dst)
+ {
+ *dstp = msg_location_dup (src);
+ return;
+ }
+
+ if (dst->file_name != src->file_name)
+ {
+ /* Failure. */
+ return;
+ }
+ if (msg_point_compare_3way (&dst->start, &src->start) > 0)
+ dst->start = src->start;
+ if (msg_point_compare_3way (&dst->end, &src->end) < 0)
+ dst->end = src->end;
+}
+
struct msg_location *
msg_location_dup (const struct msg_location *src)
{
return NULL;
struct msg_location *dst = xmalloc (sizeof *dst);
- *dst = (struct msg_location) {
- .file_name = xstrdup_if_nonnull (src->file_name),
- .first_line = src->first_line,
- .last_line = src->last_line,
- .first_column = src->first_column,
- .last_column = src->last_column,
- };
+ *dst = *src;
+ if (src->file_name)
+ dst->file_name = intern_ref (src->file_name);
+ if (msg_handler.lex_source_ref && src->src)
+ msg_handler.lex_source_ref (dst->src);
return dst;
}
msg_location_is_empty (const struct msg_location *loc)
{
return !loc || (!loc->file_name
- && loc->first_line <= 0
- && loc->first_column <= 0);
+ && loc->start.line <= 0
+ && loc->start.column <= 0);
}
void
if (loc->file_name)
ds_put_cstr (s, loc->file_name);
- int l1 = loc->first_line;
- int l2 = MAX (loc->first_line, loc->last_line - 1);
- int c1 = loc->first_column;
- int c2 = MAX (loc->first_column, loc->last_column - 1);
+ int l1 = loc->start.line;
+ int l2 = MAX (l1, loc->end.line);
+ int c1 = loc->start.column;
+ int c2 = MAX (c1, loc->end.column);
if (l1 > 0)
{
}
}
\f
+/* msg_stack */
+
+void
+msg_stack_destroy (struct msg_stack *stack)
+{
+ if (stack)
+ {
+ msg_location_destroy (stack->location);
+ free (stack->description);
+ free (stack);
+ }
+}
+
+struct msg_stack *
+msg_stack_dup (const struct msg_stack *src)
+{
+ struct msg_stack *dst = xmalloc (sizeof *src);
+ *dst = (struct msg_stack) {
+ .location = msg_location_dup (src->location),
+ .description = xstrdup_if_nonnull (src->description),
+ };
+ return dst;
+}
+\f
/* Working with messages. */
const char *
struct msg *
msg_dup (const struct msg *src)
{
+ struct msg_stack **ms = xmalloc (src->n_stack * sizeof *ms);
+ for (size_t i = 0; i < src->n_stack; i++)
+ ms[i] = msg_stack_dup (src->stack[i]);
+
struct msg *dst = xmalloc (sizeof *dst);
*dst = (struct msg) {
.category = src->category,
.severity = src->severity,
+ .stack = ms,
+ .n_stack = src->n_stack,
.location = msg_location_dup (src->location),
.command_name = xstrdup_if_nonnull (src->command_name),
.text = xstrdup (src->text),
{
if (m)
{
+ for (size_t i = 0; i < m->n_stack; i++)
+ msg_stack_destroy (m->stack[i]);
+ free (m->stack);
msg_location_destroy (m->location);
free (m->text);
free (m->command_name);
ds_init_empty (&s);
+ for (size_t i = 0; i < m->n_stack; i++)
+ {
+ const struct msg_stack *ms = m->stack[i];
+ if (!msg_location_is_empty (ms->location))
+ {
+ msg_location_format (ms->location, &s);
+ ds_put_cstr (&s, ": ");
+ }
+ ds_put_format (&s, "%s\n", ms->description);
+ }
if (m->category != MSG_C_GENERAL && !msg_location_is_empty (m->location))
{
msg_location_format (m->location, &s);
ds_put_cstr (&s, m->text);
+ const struct msg_location *loc = m->location;
+ if (m->category != MSG_C_GENERAL
+ && loc->src && loc->start.line && loc->start.column
+ && msg_handler.lex_source_get_line)
+ {
+ int l0 = loc->start.line;
+ int l1 = loc->end.line;
+ int nl = l1 - l0;
+ for (int ln = l0; ln <= l1; ln++)
+ {
+ if (nl > 3 && ln == l0 + 2)
+ {
+ ds_put_cstr (&s, "\n ... |");
+ ln = l1;
+ }
+
+ struct substring line = msg_handler.lex_source_get_line (
+ loc->src, ln);
+ ss_rtrim (&line, ss_cstr ("\n\r"));
+
+ ds_put_format (&s, "\n%5d | ", ln);
+ ds_put_substring (&s, line);
+
+ int c0 = ln == l0 ? loc->start.column : 1;
+ int c1 = ln == l1 ? loc->end.column : ss_utf8_count_columns (line);
+ if (c0 > 0 && c1 >= c0)
+ {
+ ds_put_cstr (&s, "\n |");
+ ds_put_byte_multiple (&s, ' ', c0);
+ if (ln == l0)
+ {
+ ds_put_byte (&s, '^');
+ if (c1 > c0)
+ ds_put_byte_multiple (&s, '~', c1 - c0);
+ }
+ else
+ ds_put_byte_multiple (&s, '-', c1 - c0 + 1);
+ }
+ }
+ }
+
return ds_cstr (&s);
}
\f
return;
stack[n++] = m;
- if (msg_handler && n <= 1)
- msg_handler (m, msg_aux);
+ if (msg_handler.output_msg && n <= 1)
+ msg_handler.output_msg (m, msg_handler.aux);
else
fprintf (stderr, "%s\n", m->text);
n--;