From: Ben Pfaff Date: Sun, 7 Feb 2010 17:42:53 +0000 (-0800) Subject: output: Make errors, warnings, and notes into a new "message_item". X-Git-Tag: v0.7.4~16 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp-builds.git;a=commitdiff_plain;h=ddb7b52128d8f1f54d9632dc3a15c7869e0fbcce output: Make errors, warnings, and notes into a new "message_item". This lets us move the terminal UI's support for writing errors to a file or to stdout into a new "msglog" output driver, and to convert journaling from a special case to an output driver of its own. --- diff --git a/src/language/command.c b/src/language/command.c index 803b5398..448fae7e 100644 --- a/src/language/command.c +++ b/src/language/command.c @@ -224,9 +224,7 @@ do_parse_command (struct lexer *lexer, else { /* Execute command. */ - msg_set_command_name (command->name); result = command->function (lexer, ds); - msg_set_command_name (NULL); } assert (cmd_result_is_valid (result)); diff --git a/src/libpspp/message.c b/src/libpspp/message.c index b3025156..c227a603 100644 --- a/src/libpspp/message.c +++ b/src/libpspp/message.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2009, 2010 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 @@ -26,14 +26,15 @@ #include #include +#include #include -#include "progname.h" -#include "xalloc.h" -#include "xvasprintf.h" +#include "gl/progname.h" +#include "gl/xalloc.h" +#include "gl/xvasprintf.h" -/* Current command name as set by msg_set_command_name(). */ -static char *command_name; +#include "gettext.h" +#define _(msgid) gettext (msgid) /* Message handler as set by msg_init(). */ static void (*msg_handler) (const struct msg *); @@ -73,11 +74,12 @@ void msg_done (void) { } - + +/* Working with messages. */ /* Duplicate a message */ struct msg * -msg_dup(const struct msg *m) +msg_dup (const struct msg *m) { struct msg *new_msg; @@ -102,6 +104,47 @@ msg_destroy (struct msg *m) free (m); } +char * +msg_to_string (const struct msg *m, const char *command_name) +{ + const char *label; + struct string s; + + ds_init_empty (&s); + + if (m->category != MSG_C_GENERAL + && (m->where.file_name || m->where.line_number != -1)) + { + if (m->where.file_name) + ds_put_format (&s, "%s:", m->where.file_name); + if (m->where.line_number != -1) + ds_put_format (&s, "%d:", m->where.line_number); + ds_put_char (&s, ' '); + } + + switch (m->severity) + { + case MSG_S_ERROR: + label = _("error"); + break; + case MSG_S_WARNING: + label = _("warning"); + break; + case MSG_S_NOTE: + default: + label = _("note"); + break; + } + ds_put_format (&s, "%s: ", label); + + if (m->category == MSG_C_SYNTAX && command_name != NULL) + ds_put_format (&s, "%s: ", command_name); + + ds_put_cstr (&s, m->text); + + return ds_cstr (&s); +} + /* Emits M as an error message. Frees allocated data in M. */ void @@ -140,22 +183,6 @@ msg_enable (void) /* Private functions. */ -/* Sets COMMAND_NAME as the command name included in some kinds - of error messages. */ -void -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) { diff --git a/src/libpspp/message.h b/src/libpspp/message.h index b1e09253..e1fc4f5a 100644 --- a/src/libpspp/message.h +++ b/src/libpspp/message.h @@ -90,8 +90,10 @@ void msg_init (struct source_stream *, void (*handler) (const struct msg *) ); void msg_done (void); -struct msg * msg_dup(const struct msg *m); -void msg_destroy(struct msg *m); +/* Working with messages. */ +struct msg *msg_dup (const struct msg *); +void msg_destroy(struct msg *); +char *msg_to_string (const struct msg *, const char *command_name); /* Emitting messages. */ void msg (enum msg_class, const char *format, ...) @@ -103,8 +105,6 @@ void msg_enable (void); void msg_disable (void); /* Error context. */ -void msg_set_command_name (const char *); -const char *msg_get_command_name (void); void msg_push_msg_locator (const struct msg_locator *); void msg_pop_msg_locator (const struct msg_locator *); diff --git a/src/output/ascii.c b/src/output/ascii.c index b427df53..c5f30816 100644 --- a/src/output/ascii.c +++ b/src/output/ascii.c @@ -26,12 +26,14 @@ #include #include #include +#include #include #include #include #include #include -#include "output/options.h" +#include +#include #include #include #include @@ -112,6 +114,7 @@ struct ascii_driver char *init; /* Device initialization string. */ /* Internal state. */ + char *command_name; char *title; char *subtitle; char *file_name; /* Output file name. */ @@ -209,6 +212,7 @@ ascii_create (const char *file_name, enum settings_output_devices device_type, } a->init = parse_string (opt (d, o, "init", "")); + a->command_name = NULL; a->title = xstrdup (""); a->subtitle = xstrdup (""); a->file_name = xstrdup (file_name); @@ -330,6 +334,7 @@ ascii_destroy (struct output_driver *driver) if (a->file != NULL) fn_close (a->file_name, a->file); + free (a->command_name); free (a->title); free (a->subtitle); free (a->file_name); @@ -367,103 +372,121 @@ ascii_init_caption_cell (const char *caption, struct table_cell *cell) } static void -ascii_submit (struct output_driver *driver, - const struct output_item *output_item) +ascii_output_table_item (struct ascii_driver *a, + const struct table_item *table_item) { - struct ascii_driver *a = ascii_driver_cast (driver); - if (a->error) - return; - if (is_table_item (output_item)) + const char *caption = table_item_get_caption (table_item); + struct render_params params; + struct render_page *page; + struct render_break x_break; + int caption_height; + int i; + + update_page_size (a, false); + + if (caption != NULL) + { + /* XXX doesn't do well with very large captions */ + struct table_cell cell; + ascii_init_caption_cell (caption, &cell); + caption_height = ascii_measure_cell_height (a, &cell, a->width); + } + else + caption_height = 0; + + params.draw_line = ascii_draw_line; + params.measure_cell_width = ascii_measure_cell_width; + params.measure_cell_height = ascii_measure_cell_height; + params.draw_cell = ascii_draw_cell, + params.aux = a; + params.size[H] = a->width; + params.size[V] = a->length - caption_height; + params.font_size[H] = 1; + params.font_size[V] = 1; + for (i = 0; i < RENDER_N_LINES; i++) { - struct table_item *table_item = to_table_item (output_item); - const char *caption = table_item_get_caption (table_item); - struct render_params params; - struct render_page *page; - struct render_break x_break; - int caption_height; - int i; + int width = i == RENDER_LINE_NONE ? 0 : 1; + params.line_widths[H][i] = width; + params.line_widths[V][i] = width; + } - update_page_size (a, false); + if (a->file == NULL && !ascii_open_page (a)) + return; - if (caption != NULL) - { - /* XXX doesn't do well with very large captions */ - struct table_cell cell; - ascii_init_caption_cell (caption, &cell); - caption_height = ascii_measure_cell_height (a, &cell, a->width); - } - else - caption_height = 0; - - params.draw_line = ascii_draw_line; - params.measure_cell_width = ascii_measure_cell_width; - params.measure_cell_height = ascii_measure_cell_height; - params.draw_cell = ascii_draw_cell, - params.aux = a; - params.size[H] = a->width; - params.size[V] = a->length - caption_height; - params.font_size[H] = 1; - params.font_size[V] = 1; - for (i = 0; i < RENDER_N_LINES; i++) + page = render_page_create (¶ms, table_item_get_table (table_item)); + for (render_break_init (&x_break, page, H); + render_break_has_next (&x_break); ) + { + struct render_page *x_slice; + struct render_break y_break; + + x_slice = render_break_next (&x_break, a->width); + for (render_break_init (&y_break, x_slice, V); + render_break_has_next (&y_break); ) { - int width = i == RENDER_LINE_NONE ? 0 : 1; - params.line_widths[H][i] = width; - params.line_widths[V][i] = width; - } + struct render_page *y_slice; + int space; - if (a->file == NULL && !ascii_open_page (a)) - return; + if (a->y > 0) + a->y++; - page = render_page_create (¶ms, table_item_get_table (table_item)); - for (render_break_init (&x_break, page, H); - render_break_has_next (&x_break); ) - { - struct render_page *x_slice; - struct render_break y_break; + space = a->length - a->y - caption_height; + if (render_break_next_size (&y_break) > space) + { + assert (a->y > 0); + ascii_close_page (a); + if (!ascii_open_page (a)) + return; + continue; + } - x_slice = render_break_next (&x_break, a->width); - for (render_break_init (&y_break, x_slice, V); - render_break_has_next (&y_break); ) + y_slice = render_break_next (&y_break, space); + if (caption_height) { - struct render_page *y_slice; - int space; - - if (a->y > 0) - a->y++; - - space = a->length - a->y - caption_height; - if (render_break_next_size (&y_break) > space) - { - assert (a->y > 0); - ascii_close_page (a); - if (!ascii_open_page (a)) - return; - continue; - } - - y_slice = render_break_next (&y_break, space); - if (caption_height) - { - struct table_cell cell; - int bb[TABLE_N_AXES][2]; - - ascii_init_caption_cell (caption, &cell); - bb[H][0] = 0; - bb[H][1] = a->width; - bb[V][0] = 0; - bb[V][1] = caption_height; - ascii_draw_cell (a, &cell, bb, bb); - a->y += caption_height; - caption_height = 0; - } - render_page_draw (y_slice); - a->y += render_page_get_size (y_slice, V); - render_page_unref (y_slice); + struct table_cell cell; + int bb[TABLE_N_AXES][2]; + + ascii_init_caption_cell (caption, &cell); + bb[H][0] = 0; + bb[H][1] = a->width; + bb[V][0] = 0; + bb[V][1] = caption_height; + ascii_draw_cell (a, &cell, bb, bb); + a->y += caption_height; + caption_height = 0; } - render_break_destroy (&y_break); + render_page_draw (y_slice); + a->y += render_page_get_size (y_slice, V); + render_page_unref (y_slice); } - render_break_destroy (&x_break); + render_break_destroy (&y_break); } + render_break_destroy (&x_break); +} + +static void +ascii_output_text (struct ascii_driver *a, const char *text) +{ + struct table_item *table_item; + + table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL); + ascii_output_table_item (a, table_item); + table_item_unref (table_item); +} + +static void +ascii_submit (struct output_driver *driver, + const struct output_item *output_item) +{ + struct ascii_driver *a = ascii_driver_cast (driver); + + output_driver_track_current_command (output_item, &a->command_name); + + if (a->error) + return; + + if (is_table_item (output_item)) + ascii_output_table_item (a, to_table_item (output_item)); else if (is_chart_item (output_item) && a->chart_file_name != NULL) { struct chart_item *chart_item = to_chart_item (output_item); @@ -515,17 +538,18 @@ ascii_submit (struct output_driver *driver, break; default: - { - struct table_item *item; - - item = table_item_create (table_from_string (TAB_LEFT, text), - NULL); - ascii_submit (&a->driver, &item->output_item); - table_item_unref (item); - } + ascii_output_text (a, text); break; } } + else if (is_message_item (output_item)) + { + const struct message_item *message_item = to_message_item (output_item); + const struct msg *msg = message_item_get_msg (message_item); + char *s = msg_to_string (msg, a->command_name); + ascii_output_text (a, s); + free (s); + } } const struct output_driver_factory txt_driver_factory = diff --git a/src/output/automake.mk b/src/output/automake.mk index 1d9f6586..259f3b0e 100644 --- a/src/output/automake.mk +++ b/src/output/automake.mk @@ -30,6 +30,10 @@ src_output_liboutput_la_SOURCES = \ src/output/journal.h \ src/output/measure.c \ src/output/measure.h \ + src/output/message-item.c \ + src/output/message-item.h \ + src/output/msglog.c \ + src/output/msglog.h \ src/output/odt.c \ src/output/options.c \ src/output/options.h \ diff --git a/src/output/cairo.c b/src/output/cairo.c index c6892793..4e8b1d38 100644 --- a/src/output/cairo.c +++ b/src/output/cairo.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -126,6 +128,7 @@ struct xr_driver /* Internal state. */ struct render_params *params; + char *command_name; char *title; char *subtitle; cairo_t *cairo; @@ -364,6 +367,7 @@ xr_destroy (struct output_driver *driver) cairo_destroy (xr->cairo); } + free (xr->command_name); for (i = 0; i < XR_N_FONTS; i++) free_font (&xr->fonts[i]); free (xr->params); @@ -406,67 +410,81 @@ xr_render_table_item (struct xr_driver *xr, const struct table_item *item, } static void -xr_submit (struct output_driver *driver, const struct output_item *output_item) +xr_output_table_item (struct xr_driver *xr, + const struct table_item *table_item) { - struct xr_driver *xr = xr_driver_cast (driver); - if (is_table_item (output_item)) - { - struct table_item *table_item = to_table_item (output_item); - struct render_break x_break; - struct render_page *page; - int caption_height; + struct render_break x_break; + struct render_page *page; + int caption_height; - if (xr->y > 0) - xr->y += xr->font_height; + if (xr->y > 0) + xr->y += xr->font_height; - page = xr_render_table_item (xr, table_item, &caption_height); - xr->params->size[V] = xr->length - caption_height; - for (render_break_init (&x_break, page, H); - render_break_has_next (&x_break); ) + page = xr_render_table_item (xr, table_item, &caption_height); + xr->params->size[V] = xr->length - caption_height; + for (render_break_init (&x_break, page, H); + render_break_has_next (&x_break); ) + { + struct render_page *x_slice; + struct render_break y_break; + + x_slice = render_break_next (&x_break, xr->width); + for (render_break_init (&y_break, x_slice, V); + render_break_has_next (&y_break); ) { - struct render_page *x_slice; - struct render_break y_break; + int space = xr->length - xr->y; + struct render_page *y_slice; - x_slice = render_break_next (&x_break, xr->width); - for (render_break_init (&y_break, x_slice, V); - render_break_has_next (&y_break); ) + /* XXX doesn't allow for caption or space between segments */ + if (render_break_next_size (&y_break) > space) { - int space = xr->length - xr->y; - struct render_page *y_slice; - - /* XXX doesn't allow for caption or space between segments */ - if (render_break_next_size (&y_break) > space) - { - assert (xr->y > 0); - xr_show_page (xr); - continue; - } - - y_slice = render_break_next (&y_break, space); - if (caption_height) - { - struct table_cell cell; - int bb[TABLE_N_AXES][2]; - - xr_init_caption_cell (table_item_get_caption (table_item), - &cell); - bb[H][0] = 0; - bb[H][1] = xr->width; - bb[V][0] = 0; - bb[V][1] = caption_height; - xr_draw_cell (xr, &cell, bb, bb); - xr->y += caption_height; - caption_height = 0; - } - - render_page_draw (y_slice); - xr->y += render_page_get_size (y_slice, V); - render_page_unref (y_slice); + assert (xr->y > 0); + xr_show_page (xr); + continue; } - render_break_destroy (&y_break); + + y_slice = render_break_next (&y_break, space); + if (caption_height) + { + struct table_cell cell; + int bb[TABLE_N_AXES][2]; + + xr_init_caption_cell (table_item_get_caption (table_item), + &cell); + bb[H][0] = 0; + bb[H][1] = xr->width; + bb[V][0] = 0; + bb[V][1] = caption_height; + xr_draw_cell (xr, &cell, bb, bb); + xr->y += caption_height; + caption_height = 0; + } + + render_page_draw (y_slice); + xr->y += render_page_get_size (y_slice, V); + render_page_unref (y_slice); } - render_break_destroy (&x_break); + render_break_destroy (&y_break); } + render_break_destroy (&x_break); +} + +static void +xr_output_text (struct xr_driver *xr, const char *text) +{ + struct table_item *table_item; + + table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL); + xr_output_table_item (xr, table_item); + table_item_unref (table_item); +} + +static void +xr_submit (struct output_driver *driver, const struct output_item *output_item) +{ + struct xr_driver *xr = xr_driver_cast (driver); + if (is_table_item (output_item)) + xr_output_table_item (xr, to_table_item (output_item)); else if (is_chart_item (output_item)) { if (xr->y > 0) @@ -507,17 +525,17 @@ xr_submit (struct output_driver *driver, const struct output_item *output_item) break; default: - { - struct table_item *item; - - item = table_item_create (table_from_string (TAB_LEFT, text), - NULL); - xr_submit (&xr->driver, &item->output_item); - table_item_unref (item); - } + xr_output_text (xr, text); break; } - + } + else if (is_message_item (output_item)) + { + const struct message_item *message_item = to_message_item (output_item); + const struct msg *msg = message_item_get_msg (message_item); + char *s = msg_to_string (msg, xr->command_name); + xr_output_text (xr, s); + free (s); } } @@ -960,6 +978,19 @@ xr_create_driver (cairo_t *cairo) return xr; } +static struct xr_rendering * +xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr) +{ + struct table_item *table_item; + struct xr_rendering *r; + + table_item = table_item_create (table_from_string (0, text), NULL); + r = xr_rendering_create (xr, &table_item->output_item, cr); + table_item_unref (table_item); + + return r; +} + struct xr_rendering * xr_rendering_create (struct xr_driver *xr, const struct output_item *item, cairo_t *cr) @@ -967,15 +998,15 @@ xr_rendering_create (struct xr_driver *xr, const struct output_item *item, struct xr_rendering *r = NULL; if (is_text_item (item)) + r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)), + cr); + else if (is_message_item (item)) { - const struct text_item *text_item = to_text_item (item); - const char *text = text_item_get_text (text_item); - struct table_item *table_item; - - table_item = table_item_create (table_from_string (TAB_LEFT, text), - NULL); - r = xr_rendering_create (xr, &table_item->output_item, cr); - table_item_unref (table_item); + const struct message_item *message_item = to_message_item (item); + const struct msg *msg = message_item_get_msg (message_item); + char *s = msg_to_string (msg, NULL); + r = xr_rendering_create_text (xr, s, cr); + free (s); } else if (is_table_item (item)) { diff --git a/src/output/csv.c b/src/output/csv.c index 6be9c43a..004558f1 100644 --- a/src/output/csv.c +++ b/src/output/csv.c @@ -19,15 +19,17 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "data/file-name.h" +#include "libpspp/assertion.h" +#include "libpspp/compiler.h" +#include "libpspp/message.h" +#include "libpspp/string-map.h" +#include "output/text-item.h" +#include "output/driver-provider.h" +#include "output/options.h" +#include "output/message-item.h" +#include "output/table-item.h" +#include "output/table-provider.h" #include "gl/error.h" #include "gl/xalloc.h" @@ -43,6 +45,7 @@ struct csv_driver char *separator; /* Comma or tab. */ char *file_name; /* Output file name. */ + char *command_name; /* Current command. */ FILE *file; /* Output file. */ int n_items; /* Number of items output so far. */ }; @@ -164,6 +167,8 @@ csv_submit (struct output_driver *driver, { struct csv_driver *csv = csv_driver_cast (driver); + output_driver_track_current_command (output_item, &csv->command_name); + if (is_table_item (output_item)) { struct table_item *table_item = to_table_item (output_item); @@ -227,6 +232,16 @@ csv_submit (struct output_driver *driver, } putc ('\n', csv->file); } + else if (is_message_item (output_item)) + { + const struct message_item *message_item = to_message_item (output_item); + const struct msg *msg = message_item_get_msg (message_item); + char *s = msg_to_string (msg, csv->command_name); + csv_put_separator (csv); + csv_output_field (csv->file, s); + free (s); + putc ('\n', csv->file); + } } struct output_driver_factory csv_driver_factory = { "csv", csv_create }; diff --git a/src/output/driver.c b/src/output/driver.c index 61852c4a..20364458 100644 --- a/src/output/driver.c +++ b/src/output/driver.c @@ -34,6 +34,7 @@ #include "libpspp/string-map.h" #include "libpspp/string-set.h" #include "libpspp/str.h" +#include "output/message-item.h" #include "output/output-item.h" #include "output/text-item.h" @@ -83,8 +84,16 @@ output_submit__ (struct output_item *item) next = llx_next (llx); - if (is_text_item (item) - && text_item_get_type (to_text_item (item)) == TEXT_ITEM_SYNTAX) + if (is_message_item (item)) + { + const struct msg *m = message_item_get_msg (to_message_item (item)); + if (m->severity == MSG_S_NOTE) + type = SETTINGS_OUTPUT_NOTE; + else + type = SETTINGS_OUTPUT_ERROR; + } + else if (is_text_item (item) + && text_item_get_type (to_text_item (item)) == TEXT_ITEM_SYNTAX) type = SETTINGS_OUTPUT_SYNTAX; else type = SETTINGS_OUTPUT_RESULT; diff --git a/src/output/html.c b/src/output/html.c index a3e14ba1..636450a9 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -23,19 +23,21 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "data/file-name.h" +#include "libpspp/assertion.h" +#include "libpspp/cast.h" +#include "libpspp/compiler.h" +#include "libpspp/message.h" +#include "libpspp/version.h" +#include "output/cairo.h" +#include "output/chart-item.h" +#include "output/driver-provider.h" +#include "output/message-item.h" +#include "output/options.h" +#include "output/output-item-provider.h" +#include "output/table-provider.h" +#include "output/table-item.h" +#include "output/text-item.h" #include "error.h" #include "xalloc.h" @@ -50,6 +52,7 @@ struct html_driver char *file_name; char *chart_file_name; + char *command_name; FILE *file; size_t chart_cnt; @@ -210,6 +213,7 @@ html_destroy (struct output_driver *driver) } free (html->chart_file_name); free (html->file_name); + free (html->command_name); free (html); } @@ -226,6 +230,8 @@ html_submit (struct output_driver *driver, { struct html_driver *html = html_driver_cast (driver); + output_driver_track_current_command (output_item, &html->command_name); + if (html->in_syntax && !is_syntax_item (output_item)) { fprintf (html->file, "\n"); @@ -313,6 +319,14 @@ html_submit (struct output_driver *driver, break; } } + else if (is_message_item (output_item)) + { + const struct message_item *message_item = to_message_item (output_item); + const struct msg *msg = message_item_get_msg (message_item); + char *s = msg_to_string (msg, html->command_name); + print_title_tag (html->file, "P", s); + free (s); + } } /* Write LENGTH characters in TEXT to file F, escaping characters diff --git a/src/output/journal.c b/src/output/journal.c index 67657f62..6b733277 100644 --- a/src/output/journal.c +++ b/src/output/journal.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007, 2010 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 @@ -16,91 +16,159 @@ #include -#include +#include "output/journal.h" #include #include #include -#include -#include +#include "data/file-name.h" +#include "libpspp/cast.h" +#include "libpspp/str.h" +#include "output/driver-provider.h" +#include "output/message-item.h" +#include "output/text-item.h" -#include "fwriteerror.h" -#include "error.h" -#include "xalloc.h" +#include "gl/error.h" +#include "gl/fwriteerror.h" +#include "gl/xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) -/* Journaling enabled? */ -static bool journal_enabled = false; +struct journal_driver + { + struct output_driver driver; + FILE *file; + char *command_name; + }; -/* Name of the journal file. */ -static char *journal_file_name = NULL; +static const struct output_driver_class journal_class; -/* Journal file. */ -static FILE *journal_file = NULL; +/* Journal driver, if journaling is enabled. */ +static struct journal_driver *journal; -/* Enables journaling. */ -void -journal_enable (void) +/* Name of journal file. */ +static char *journal_file_name; + +static struct journal_driver * +journal_driver_cast (struct output_driver *driver) { - journal_enabled = true; + assert (driver->class == &journal_class); + return UP_CAST (driver, struct journal_driver, driver); } -/* Disables journaling. */ -void -journal_disable (void) +static void +journal_close (void) { - journal_enabled = false; - if (journal_file != NULL) - fflush (journal_file); + if (journal != NULL && journal->file != NULL) + { + if (fwriteerror (journal->file)) + error (0, errno, _("error writing \"%s\""), journal_file_name); + journal->file = NULL; + } } -/* Sets the name of the journal file to FILE_NAME. */ -void -journal_set_file_name (const char *file_name) +static void +journal_destroy (struct output_driver *driver) { - assert (file_name != NULL); + struct journal_driver *j = journal_driver_cast (driver); + + journal_close (); + free (j->command_name); + free (j); - if (journal_file != NULL) + journal = NULL; +} + +static void +journal_output (struct journal_driver *j, const char *s) +{ + if (j->file == NULL) { - if (fwriteerror (journal_file)) - error (0, errno, _("error writing \"%s\""), journal_file_name); + j->file = fopen (journal_file_name, "a"); + if (j->file == NULL) + { + error (0, errno, _("%s: open failed"), journal_file_name); + output_driver_destroy (&j->driver); + return; + } } - free (journal_file_name); - journal_file_name = xstrdup (file_name); + fprintf (j->file, "%s\n", s); } -/* Writes LINE to the journal file (if journaling is enabled). - If PREFIX is non-null, the line will be prefixed by "> ". */ -void -journal_write (bool prefix, const char *line) +static void +journal_submit (struct output_driver *driver, const struct output_item *item) { - if (!journal_enabled) - return; + struct journal_driver *j = journal_driver_cast (driver); + + output_driver_track_current_command (item, &j->command_name); + + if (is_text_item (item)) + { + const struct text_item *text_item = to_text_item (item); + enum text_item_type type = text_item_get_type (text_item); - if (journal_file == NULL) + if (type == TEXT_ITEM_SYNTAX) + journal_output (j, text_item_get_text (text_item)); + } + else if (is_message_item (item)) + { + const struct message_item *message_item = to_message_item (item); + const struct msg *msg = message_item_get_msg (message_item); + char *s = msg_to_string (msg, j->command_name); + journal_output (j, s); + free (s); + } +} + +static const struct output_driver_class journal_class = + { + "journal", + journal_destroy, + journal_submit, + NULL /* flush */ + }; + +/* Enables journaling. */ +void +journal_enable (void) +{ + if (journal == NULL) { + /* If no journal file name is configured, use the default. */ if (journal_file_name == NULL) { const char *output_path = default_output_path (); journal_file_name = xasprintf ("%s%s", output_path, "pspp.jnl"); } - journal_file = fopen (journal_file_name, "w"); - if (journal_file == NULL) - { - error (0, errno, _("error creating \"%s\""), journal_file_name); - journal_enabled = false; - return; - } + + /* Create journal driver. */ + journal = xzalloc (sizeof *journal); + output_driver_init (&journal->driver, &journal_class, "journal", + SETTINGS_DEVICE_UNFILTERED); + journal->file = NULL; + journal->command_name = NULL; + + /* Register journal driver. */ + output_driver_register (&journal->driver); } +} + +/* Disables journaling. */ +void +journal_disable (void) +{ + if (journal != NULL) + output_driver_destroy (&journal->driver); +} - if (prefix) - fputs ("> ", journal_file); - fputs (line, journal_file); - if (strchr (line, '\n') == NULL) - putc ('\n', journal_file); - fflush (journal_file); +/* Sets the name of the journal file to FILE_NAME. */ +void +journal_set_file_name (const char *file_name) +{ + journal_close (); + free (journal_file_name); + journal_file_name = xstrdup (file_name); } diff --git a/src/output/journal.h b/src/output/journal.h index 3509ff09..5051193a 100644 --- a/src/output/journal.h +++ b/src/output/journal.h @@ -29,6 +29,5 @@ void journal_enable (void); void journal_disable (void); void journal_set_file_name (const char *); -void journal_write (bool prefix, const char *); #endif /* output/journal.h */ diff --git a/src/output/message-item.c b/src/output/message-item.c new file mode 100644 index 00000000..feb1d32c --- /dev/null +++ b/src/output/message-item.c @@ -0,0 +1,66 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2010 Free Sonftware 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 3 of the License, 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, see . */ + +#include + +#include "output/message-item.h" + +#include + +#include "libpspp/message.h" +#include "output/driver.h" +#include "output/output-item-provider.h" + +#include "gl/xalloc.h" + +struct message_item * +message_item_create (const struct msg *msg) +{ + struct message_item *item; + + item = xmalloc (sizeof *msg); + output_item_init (&item->output_item, &message_item_class); + item->msg = msg_dup (msg); + + return item; +} + +const struct msg * +message_item_get_msg (const struct message_item *item) +{ + return item->msg; +} + +static void +message_item_destroy (struct output_item *output_item) +{ + struct message_item *item = to_message_item (output_item); + msg_destroy (item->msg); + free (item); +} + +/* Submits ITEM to the configured output drivers, and transfers ownership to + the output subsystem. */ +void +message_item_submit (struct message_item *item) +{ + output_submit (&item->output_item); +} + +const struct output_item_class message_item_class = + { + message_item_destroy, + }; diff --git a/src/output/message-item.h b/src/output/message-item.h new file mode 100644 index 00000000..a97b2270 --- /dev/null +++ b/src/output/message-item.h @@ -0,0 +1,101 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2010 Free Sonftware 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 3 of the License, 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, see . */ + +#ifndef OUTPUT_MESSAGE_ITEM_H +#define OUTPUT_MESSAGE_ITEM_H 1 + +/* Message items. + + A message item is a subclass of an output item (see + output/output-item.h). + + A message item is an error, warning, or note to the user. + + Message items should not be submitted directly to the output subsystem. + Instead, use the msg() function in libpspp/message.h, which will ensure that + the message gets routed properly for the PSPP user interface in use. */ + +#include +#include + +/* A message item. */ +struct message_item + { + struct output_item output_item; + struct msg *msg; + }; + +struct message_item *message_item_create (const struct msg *); + +const struct msg *message_item_get_msg (const struct message_item *); + +/* This boilerplate for message_item, a subclass of output_item, was + autogenerated by mk-class-boilerplate. */ + +#include +#include + +extern const struct output_item_class message_item_class; + +/* Returns true if SUPER is a message_item, otherwise false. */ +static inline bool +is_message_item (const struct output_item *super) +{ + return super->class == &message_item_class; +} + +/* Returns SUPER converted to message_item. SUPER must be a message_item, as + reported by is_message_item. */ +static inline struct message_item * +to_message_item (const struct output_item *super) +{ + assert (is_message_item (super)); + return UP_CAST (super, struct message_item, output_item); +} + +/* Returns INSTANCE converted to output_item. */ +static inline struct output_item * +message_item_super (const struct message_item *instance) +{ + return CONST_CAST (struct output_item *, &instance->output_item); +} + +/* Increments INSTANCE's reference count and returns INSTANCE. */ +static inline struct message_item * +message_item_ref (const struct message_item *instance) +{ + return to_message_item (output_item_ref (&instance->output_item)); +} + +/* Decrements INSTANCE's reference count, then destroys INSTANCE if + the reference count is now zero. */ +static inline void +message_item_unref (struct message_item *instance) +{ + output_item_unref (&instance->output_item); +} + +/* Returns true if INSTANCE's reference count is greater than 1, + false otherwise. */ +static inline bool +message_item_is_shared (const struct message_item *instance) +{ + return output_item_is_shared (&instance->output_item); +} + +void message_item_submit (struct message_item *); + +#endif /* output/message-item.h */ diff --git a/src/output/msglog.c b/src/output/msglog.c new file mode 100644 index 00000000..d5c68069 --- /dev/null +++ b/src/output/msglog.c @@ -0,0 +1,119 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2010 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 3 of the License, 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, see . */ + +#include + +#include "output/msglog.h" + +#include +#include +#include +#include + +#include "data/file-name.h" +#include "data/settings.h" +#include "libpspp/cast.h" +#include "output/driver-provider.h" +#include "output/message-item.h" + +#include "gl/error.h" +#include "gl/fwriteerror.h" +#include "gl/xalloc.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +struct msglog_driver + { + struct output_driver driver; + FILE *file; + char *file_name; + char *command_name; + }; + +static const struct output_driver_class msglog_class; + +static struct msglog_driver * +msglog_driver_cast (struct output_driver *driver) +{ + assert (driver->class == &msglog_class); + return UP_CAST (driver, struct msglog_driver, driver); +} + +struct output_driver * +msglog_create (const char *file_name) +{ + enum settings_output_devices type; + struct msglog_driver *ml; + FILE *file; + + file = fn_open (file_name, "w"); + if (file == NULL) + { + error (0, errno, _("%s: open failed"), file_name); + return NULL; + } + + type = (!strcmp (file_name, "-") || isatty (fileno (file)) + ? SETTINGS_DEVICE_TERMINAL + : SETTINGS_DEVICE_UNFILTERED); + + ml = xzalloc (sizeof *ml); + output_driver_init (&ml->driver, &msglog_class, file_name, type); + ml->file = file; + ml->file_name = xstrdup (file_name); + ml->command_name = NULL; + + output_driver_register (&ml->driver); + + return &ml->driver; +} + +static void +msglog_destroy (struct output_driver *driver) +{ + struct msglog_driver *ml = msglog_driver_cast (driver); + + fn_close (ml->file_name, ml->file); + free (ml->file_name); + free (ml->command_name); + free (ml); +} + +static void +msglog_submit (struct output_driver *driver, const struct output_item *item) +{ + struct msglog_driver *ml = msglog_driver_cast (driver); + + output_driver_track_current_command (item, &ml->command_name); + + if (is_message_item (item)) + { + const struct message_item *message_item = to_message_item (item); + const struct msg *msg = message_item_get_msg (message_item); + char *s = msg_to_string (msg, ml->command_name); + fprintf (ml->file, "%s\n", s); + free (s); + } +} + +static const struct output_driver_class msglog_class = + { + "msglog", + msglog_destroy, + msglog_submit, + NULL + }; diff --git a/src/output/msglog.h b/src/output/msglog.h new file mode 100644 index 00000000..e5d54986 --- /dev/null +++ b/src/output/msglog.h @@ -0,0 +1,24 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2010 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 3 of the License, 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, see . */ + +#ifndef OUTPUT_MSGLOG_H +#define OUTPUT_MSGLOG_H 1 + +/* Output driver to log errors, warnings, and notes to files. */ + +struct output_driver *msglog_create (const char *file_name); + +#endif /* output/msglog.h */ diff --git a/src/output/odt.c b/src/output/odt.c index ab38cd04..0b55ac9a 100644 --- a/src/output/odt.c +++ b/src/output/odt.c @@ -32,6 +32,7 @@ #include "libpspp/str.h" #include "libpspp/version.h" #include "output/driver-provider.h" +#include "output/message-item.h" #include "output/options.h" #include "output/tab.h" #include "output/table-item.h" @@ -64,6 +65,9 @@ struct odt_driver /* Number of tables so far. */ int table_num; + + /* Name of current command. */ + char *command_name; }; static const struct output_driver_class odt_driver_class; @@ -403,6 +407,7 @@ odt_destroy (struct output_driver *driver) else fprintf (stderr, "Not removing directory %s\n", odt->dirname); + free (odt->command_name); free (odt->dirname); free (odt); } @@ -503,23 +508,37 @@ odt_submit_table (struct odt_driver *odt, struct table_item *item) xmlTextWriterEndElement (odt->content_wtr); /* table */ } +static void +odt_output_text (struct odt_driver *odt, const char *text) +{ + xmlTextWriterStartElement (odt->content_wtr, _xml("text:p")); + xmlTextWriterWriteString (odt->content_wtr, _xml(text)); + xmlTextWriterEndElement (odt->content_wtr); +} + /* Submit a table to the ODT driver */ static void odt_submit (struct output_driver *driver, const struct output_item *output_item) { struct odt_driver *odt = odt_driver_cast (driver); + + output_driver_track_current_command (output_item, &odt->command_name); + if (is_table_item (output_item)) odt_submit_table (odt, to_table_item (output_item)); else if (is_text_item (output_item)) { - const struct text_item *text_item = to_text_item (output_item); - const char *text = text_item_get_text (text_item); - /* XXX apply different styles based on text_item's type. */ - xmlTextWriterStartElement (odt->content_wtr, _xml("text:p")); - xmlTextWriterWriteString (odt->content_wtr, _xml(text)); - xmlTextWriterEndElement (odt->content_wtr); + odt_output_text (odt, text_item_get_text (to_text_item (output_item))); + } + else if (is_message_item (output_item)) + { + const struct message_item *message_item = to_message_item (output_item); + const struct msg *msg = message_item_get_msg (message_item); + char *s = msg_to_string (msg, odt->command_name); + odt_output_text (odt, s); + free (s); } } diff --git a/src/output/output-item.h b/src/output/output-item.h index 57ced303..b45e0087 100644 --- a/src/output/output-item.h +++ b/src/output/output-item.h @@ -27,6 +27,8 @@ - Charts (see output/chart-item.h). - Text strings (see output/text-item.h). + + - Messages (see output/message-item.h). */ #include diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index 3fbf4652..cb730ec2 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -61,7 +61,6 @@ UI_FILES = \ src/ui/gui/examine.ui \ src/ui/gui/find.ui \ src/ui/gui/frequencies.ui \ - src/ui/gui/message-dialog.ui \ src/ui/gui/oneway.ui \ src/ui/gui/psppire.ui \ src/ui/gui/rank.ui \ @@ -141,8 +140,6 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/helper.c \ src/ui/gui/helper.h \ src/ui/gui/main.c \ - src/ui/gui/message-dialog.c \ - src/ui/gui/message-dialog.h \ src/ui/gui/missing-val-dialog.c \ src/ui/gui/missing-val-dialog.h \ src/ui/gui/oneway-anova-dialog.c \ diff --git a/src/ui/gui/message-dialog.c b/src/ui/gui/message-dialog.c deleted file mode 100644 index ccb43640..00000000 --- a/src/ui/gui/message-dialog.c +++ /dev/null @@ -1,292 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2004, 2005, 2010 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 3 of the License, 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, see . */ - - -#include -#include - -#include -#include -#define _(msgid) gettext (msgid) -#define N_(msgid) msgid - -#include -#include -#include -#include -#include "message-dialog.h" -#include "progname.h" - - -#include -#include - -#include "helper.h" - -static void enqueue_msg (const struct msg *m); -static gboolean popup_messages (gpointer); - -#define MAX_EARLY_MESSAGES 100 -static GQueue *early_queue; - -static unsigned long dropped_messages; - -#define MAX_LATE_MESSAGES 10 -static GQueue *late_queue; - -static int error_cnt, warning_cnt, note_cnt; - -static GtkBuilder *message_xml; -static GtkWidget *message_dialog; - -void -message_dialog_init (struct source_stream *ss) -{ - early_queue = g_queue_new (); - dropped_messages = 0; - late_queue = g_queue_new (); - error_cnt = warning_cnt = note_cnt = 0; - msg_init (ss, enqueue_msg); - message_xml = builder_new ("message-dialog.ui"); - message_dialog = get_widget_assert (message_xml, "message-dialog"); - - GTK_WIDGET_SET_FLAGS (get_widget_assert (message_xml, "close-button"), - GTK_CAN_DEFAULT); - -} - -void -message_dialog_done (void) -{ - msg_done (); - g_queue_free (early_queue); - dropped_messages = 0; - g_queue_free (late_queue); - gtk_widget_destroy (message_dialog); - g_object_unref (message_xml); -} - -static void -format_message (struct msg *m, struct string *msg) -{ - const char *label; - - if (m->where.file_name) - ds_put_format (msg, "%s:", m->where.file_name); - if (m->where.line_number != -1) - ds_put_format (msg, "%d:", m->where.line_number); - if (m->where.file_name || m->where.line_number != -1) - ds_put_char (msg, ' '); - - switch (m->severity) - { - case MSG_S_ERROR: - switch (m->category) - { - case MSG_C_SYNTAX: - label = _("syntax error"); - break; - - case MSG_C_DATA: - label = _("data file error"); - break; - - case MSG_C_GENERAL: - default: - label = _("PSPP error"); - break; - } - break; - case MSG_S_WARNING: - switch (m->category) - { - case MSG_C_SYNTAX: - label = _("syntax warning"); - break; - - case MSG_C_DATA: - label = _("data file warning"); - break; - - case MSG_C_GENERAL: - default: - label = _("PSPP warning"); - break; - } - break; - case MSG_S_NOTE: - default: - switch (m->category) - { - case MSG_C_SYNTAX: - label = _("syntax information"); - break; - - case MSG_C_DATA: - label = _("data file information"); - break; - - case MSG_C_GENERAL: - default: - label = _("PSPP information"); - break; - } - break; - } - ds_put_format (msg, "%s: %s\n", label, m->text); - msg_destroy (m); -} - -static void -enqueue_msg (const struct msg *msg) -{ - struct msg *m = msg_dup (msg); - - switch (m->severity) - { - case MSG_S_ERROR: - error_cnt++; - break; - case MSG_S_WARNING: - warning_cnt++; - break; - case MSG_S_NOTE: - note_cnt++; - break; - case MSG_N_SEVERITIES: - NOT_REACHED (); - } - - if (g_queue_get_length (early_queue) < MAX_EARLY_MESSAGES) - { - if (g_queue_is_empty (early_queue)) - g_idle_add (popup_messages, NULL); - g_queue_push_tail (early_queue, m); - } - else - { - if (g_queue_get_length (late_queue) >= MAX_LATE_MESSAGES) - { - struct msg *m = g_queue_pop_head (late_queue); - msg_destroy (m); - dropped_messages++; - } - g_queue_push_tail (late_queue, m); - } -} - -static gboolean -popup_messages (gpointer unused UNUSED) -{ - GtkTextBuffer *text_buffer; - GtkTextIter end; - GtkTextView *text_view; - GtkLabel *label; - struct string lead = DS_EMPTY_INITIALIZER; - struct string msg = DS_EMPTY_INITIALIZER; - int message_cnt; - - gdk_threads_enter (); - - /* Set up the dialog. */ - if (message_xml == NULL || message_dialog == NULL) - goto use_fallback; - - /* If a pointer grab is in effect, then the combination of that, and - a modal dialog box, will cause an impossible situation. - So don't pop it up just yet. - */ - if ( gdk_display_pointer_is_grabbed (gtk_widget_get_display (message_dialog))) - { - ds_destroy (&lead); - ds_destroy (&msg); - gdk_threads_leave (); - return TRUE; - } - - /* Compose the lead-in. */ - message_cnt = error_cnt + warning_cnt + note_cnt; - if (dropped_messages == 0) - ds_put_format ( - &lead, - ngettext ("The PSPP processing engine reported the following message:", - "The PSPP processing engine reported the following messages:", - message_cnt)); - else - { - ds_put_format ( - &lead, - ngettext ("The PSPP processing engine reported %d message.", - "The PSPP processing engine reported %d messages.", - message_cnt), - message_cnt); - ds_put_cstr (&lead, " "); - ds_put_format ( - &lead, - ngettext ("%d of these messages are displayed below.", - "%d of these messages are displayed below.", - MAX_EARLY_MESSAGES + MAX_LATE_MESSAGES), - MAX_EARLY_MESSAGES + MAX_LATE_MESSAGES); - } - - - /* Compose the messages. */ - while (!g_queue_is_empty (early_queue)) - format_message (g_queue_pop_head (early_queue), &msg); - if (dropped_messages) - { - ds_put_format (&msg, "...\nOmitting %lu messages\n...\n", - dropped_messages); - dropped_messages = 0; - } - while (!g_queue_is_empty (late_queue)) - format_message (g_queue_pop_head (late_queue), &msg); - - text_buffer = gtk_text_buffer_new (NULL); - gtk_text_buffer_get_end_iter (text_buffer, &end); - gtk_text_buffer_insert (text_buffer, &end, ds_data (&msg), ds_length (&msg)); - - label = GTK_LABEL (get_widget_assert (message_xml, "lead-in")); - if (label == NULL) - goto use_fallback; - gtk_label_set_text (label, ds_cstr (&lead)); - - text_view = GTK_TEXT_VIEW (get_widget_assert (message_xml, "message")); - if (text_view == NULL) - goto use_fallback; - gtk_text_view_set_buffer (text_view, text_buffer); - - gtk_widget_grab_default (get_widget_assert (message_xml, "close-button")); - gtk_widget_grab_focus (get_widget_assert (message_xml, "close-button")); - gtk_dialog_run ( GTK_DIALOG (message_dialog)); - gtk_widget_hide (message_dialog); - - ds_destroy (&lead); - ds_destroy (&msg); - - gdk_threads_leave (); - return FALSE; - -use_fallback: - g_warning ("Could not create message dialog. " - "Is PSPPIRE properly installed?"); - fputs (ds_cstr (&msg), stderr); - ds_destroy (&lead); - ds_destroy (&msg); - gdk_threads_leave (); - return FALSE; -} - diff --git a/src/ui/gui/message-dialog.h b/src/ui/gui/message-dialog.h deleted file mode 100644 index 45eeca68..00000000 --- a/src/ui/gui/message-dialog.h +++ /dev/null @@ -1,27 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2004,2005 Free Software Foundation - - 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 3 of the License, 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, see . */ - -#ifndef ERROR_DIALOG_H -#define ERROR_DIALOG_H - -#include - -struct source_stream ; - -void message_dialog_init (struct source_stream *); -void message_dialog_done (void); - -#endif diff --git a/src/ui/gui/message-dialog.ui b/src/ui/gui/message-dialog.ui deleted file mode 100644 index 540a3909..00000000 --- a/src/ui/gui/message-dialog.ui +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - 600 - 350 - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 12 - Messages Reported - True - center-on-parent - dialog - False - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - vertical - 24 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 12 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - gtk-dialog-info - 6 - - - False - 0 - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - vertical - 6 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - The PSPP processor reported # errors. The first # and last # are shown below: - True - - - False - False - 0 - - - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - automatic - automatic - etched-in - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False - word - 6 - 6 - False - - - - - 1 - - - - - 1 - - - - - 1 - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - end - - - gtk-close - True - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - True - - - False - False - 0 - - - - - False - end - 0 - - - - - - close-button - - - diff --git a/src/ui/gui/psppire-dict.c b/src/ui/gui/psppire-dict.c index 9e2190b2..bc31022a 100644 --- a/src/ui/gui/psppire-dict.c +++ b/src/ui/gui/psppire-dict.c @@ -15,23 +15,22 @@ along with this program. If not, see . */ #include + +#include "ui/gui/psppire-dict.h" + #include #include - #include -#include - -#include "psppire-var-ptr.h" -#include "psppire-dict.h" -#include -#include -#include -#include -#include - -#include "helper.h" -#include "message-dialog.h" +#include "data/dictionary.h" +#include "data/missing-values.h" +#include "data/value-labels.h" +#include "data/variable.h" +#include "libpspp/i18n.h" +#include "libpspp/message.h" +#include "ui/gui/helper.h" +#include "ui/gui/psppire-marshal.h" +#include "ui/gui/psppire-var-ptr.h" enum { BACKEND_CHANGED, diff --git a/src/ui/gui/psppire.c b/src/ui/gui/psppire.c index 1c04e93b..e6126916 100644 --- a/src/ui/gui/psppire.c +++ b/src/ui/gui/psppire.c @@ -40,9 +40,9 @@ #include "libpspp/version.h" #include "output/driver.h" #include "output/journal.h" +#include "output/message-item.h" #include "ui/gui/dict-display.h" #include "ui/gui/executor.h" -#include "ui/gui/message-dialog.h" #include "ui/gui/psppire-data-store.h" #include "ui/gui/psppire-data-window.h" #include "ui/gui/psppire-dict.h" @@ -70,6 +70,7 @@ struct dataset * the_dataset = NULL; static GtkWidget *the_data_window; +static void handle_msg (const struct msg *); static void load_data_file (const char *); static void @@ -99,8 +100,8 @@ initialize (struct source_stream *ss, const char *data_file) the_dataset = create_dataset (); - message_dialog_init (the_source_stream); the_source_stream = ss; + msg_init (ss, handle_msg); dictionary = psppire_dict_new_from_dict (dataset_dict (the_dataset)); @@ -140,7 +141,6 @@ void de_initialize (void) { destroy_source_stream (the_source_stream); - message_dialog_done (); settings_done (); output_close (); i18n_done (); @@ -295,3 +295,9 @@ load_data_file (const char *arg) g_free (filename); } + +static void +handle_msg (const struct msg *m) +{ + message_item_submit (message_item_create (m)); +} diff --git a/src/ui/terminal/main.c b/src/ui/terminal/main.c index 10b3b7e9..02be4f78 100644 --- a/src/ui/terminal/main.c +++ b/src/ui/terminal/main.c @@ -129,13 +129,13 @@ main (int argc, char **argv) "a cascade of dependent command failures.")); getl_abort_noninteractive (the_source_stream); } - else - check_msg_count (the_source_stream); + else if (msg_ui_too_many_errors ()) + getl_abort_noninteractive (the_source_stream); } clean_up (); - return any_errors (); + return msg_ui_any_errors (); } diff --git a/src/ui/terminal/msg-ui.c b/src/ui/terminal/msg-ui.c index b5280fe6..c657f096 100644 --- a/src/ui/terminal/msg-ui.c +++ b/src/ui/terminal/msg-ui.c @@ -16,16 +16,7 @@ #include -#include "msg-ui.h" - -#include -#include -#include -#include -#include -#include -#include -#include +#include "ui/terminal/msg-ui.h" #include #include @@ -33,45 +24,37 @@ #include #include -#include "unilbrk.h" -#include "localcharset.h" +#include "data/settings.h" +#include "libpspp/getl.h" +#include "libpspp/message.h" +#include "libpspp/msg-locator.h" +#include "libpspp/str.h" +#include "output/journal.h" +#include "output/driver.h" +#include "output/tab.h" +#include "output/message-item.h" + +#include "gl/unilbrk.h" +#include "gl/localcharset.h" #include "gettext.h" #define _(msgid) gettext (msgid) #define N_(msgid) msgid -/* Number of errors, warnings reported. */ -static int error_count; -static int warning_count; -static const char *error_file; +/* Number of messages reported, by severity level. */ +static int counts[MSG_N_SEVERITIES]; -static void handle_msg (const struct msg *); +/* True after the maximum number of errors or warnings has been exceeded. */ +static bool too_many_errors; -static FILE *msg_file ; +/* True after the maximum number of notes has been exceeded. */ +static bool too_many_notes; -void -msg_ui_set_error_file (const char *filename) -{ - error_file = filename; -} +static void handle_msg (const struct msg *); void msg_ui_init (struct source_stream *ss) { - msg_file = stdout; - - if ( error_file ) - { - msg_file = fopen (error_file, "a"); - if ( NULL == msg_file ) - { - int err = errno; - printf ( _("Cannot open %s (%s). " - "Writing errors to stdout instead.\n"), - error_file, strerror(err) ); - msg_file = stdout; - } - } msg_init (ss, handle_msg); } @@ -80,202 +63,80 @@ msg_ui_done (void) { msg_done (); msg_locator_done (); - - if ( msg_file ) /* FIXME: do we really want to close stdout ?? */ - fclose (msg_file); } /* Checks whether we've had so many errors that it's time to quit processing this syntax file. */ -void -check_msg_count (struct source_stream *ss) +bool +msg_ui_too_many_errors (void) { - if (!getl_is_interactive (ss)) - { - int max_errors = settings_get_max_messages (MSG_S_ERROR); - int max_warnings = settings_get_max_messages (MSG_S_WARNING); - - if (error_count > max_errors) - msg (MN, _("Errors (%d) exceed limit (%d)."), - error_count, max_errors); - else if (error_count + warning_count > max_warnings) - msg (MN, _("Warnings (%d) exceed limit (%d)."), - error_count + warning_count, max_warnings); - else - return; - - getl_abort_noninteractive (ss); - } + return too_many_errors; } void -reset_msg_count (void) +msg_ui_reset_counts (void) { - error_count = warning_count = 0; + int i; + + for (i = 0; i < MSG_N_SEVERITIES; i++) + counts[i] = 0; + too_many_errors = false; + too_many_notes = false; } bool -any_errors (void) +msg_ui_any_errors (void) { - return error_count > 0; + return counts[MSG_S_ERROR] > 0; } - -typedef void write_line_func (int indent, struct substring line, void *aux); -static void dump_message (char *msg, unsigned width, unsigned indent, - write_line_func *, void *aux); -static write_line_func write_stream; -static write_line_func write_journal; static void -handle_msg (const struct msg *m) +submit_note (char *s) { - struct category - { - bool show_command_name; /* Show command name with error? */ - bool show_file_location; /* Show syntax file location? */ - }; - - static const struct category categories[] = - { - {false, false}, /* MSG_GENERAL. */ - {true, true}, /* MSG_SYNTAX. */ - {false, true}, /* MSG_DATA. */ - }; - - struct severity - { - enum settings_output_type type; - const char *name; /* How to identify this severity. */ - int *count; /* Number of msgs with this severity so far. */ - }; - - static struct severity severities[] = - { - { SETTINGS_OUTPUT_ERROR, N_("error"), &error_count }, - { SETTINGS_OUTPUT_ERROR, N_("warning"), &warning_count }, - { SETTINGS_OUTPUT_NOTE, NULL, NULL}, - }; - - const struct category *category = &categories[m->category]; - const struct severity *severity = &severities[m->severity]; - struct string string = DS_EMPTY_INITIALIZER; - enum settings_output_devices routing; - - if (category->show_file_location && m->where.file_name) - { - ds_put_format (&string, "%s:", m->where.file_name); - if (m->where.line_number != -1) - ds_put_format (&string, "%d:", m->where.line_number); - ds_put_char (&string, ' '); - } - - if (severity->name != NULL) - ds_put_format (&string, "%s: ", gettext (severity->name)); - - if (severity->count != NULL) - ++*severity->count; - - if (category->show_command_name && msg_get_command_name () != NULL) - ds_put_format (&string, "%s: ", msg_get_command_name ()); - - ds_put_cstr (&string, m->text); - - routing = settings_get_output_routing (severity->type); - if (msg_file != stdout || routing & SETTINGS_DEVICE_TERMINAL) - dump_message (ds_cstr (&string), - isatty (fileno (msg_file)) ? settings_get_viewwidth () : INT_MAX, 8, - write_stream, msg_file); - - dump_message (ds_cstr (&string), 78, 0, write_journal, NULL); - - if (routing & SETTINGS_DEVICE_LISTING) - { - /* Disable terminal output devices, because the error should already have - been reported to the terminal with the dump_message call above. */ - settings_set_output_routing (severity->type, - routing & ~SETTINGS_DEVICE_TERMINAL); - tab_output_text (TAB_LEFT, ds_cstr (&string)); - settings_set_output_routing (severity->type, routing); - } - - ds_destroy (&string); + struct msg m; + + m.category = MSG_C_GENERAL; + m.severity = MSG_S_NOTE; + m.where.file_name = NULL; + m.where.line_number = -1; + m.text = s; + message_item_submit (message_item_create (&m)); + free (s); } -/* Divides MSG into lines of WIDTH width for the first line and - WIDTH - INDENT width for each succeeding line, and writes the - lines by calling DUMP_LINE for each line, passing AUX as - auxiliary data. */ static void -dump_message (char *msg, unsigned width, unsigned indent, - write_line_func *dump_line, void *aux) +handle_msg (const struct msg *m) { - 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) - { - free (string); - free (breaks); - dump_line (0, ss_cstr (msg), aux); - return; - } - - /* Break into lines. */ - if (indent > width / 3) - indent = width / 3; - ulc_width_linebreaks (string, length, - width - indent, -indent, 0, - NULL, locale_charset (), breaks); + int n_msgs, max_msgs; - /* Write out lines. */ - line_start = 0; - line_indent = 0; - for (i = 0; i < length; i++) - if (breaks[i] == UC_BREAK_POSSIBLE || breaks[i] == UC_BREAK_MANDATORY) - { - dump_line (line_indent, - ss_buffer (string + line_start, i - line_start), aux); - line_indent = indent; + if (too_many_errors || (too_many_notes && m->severity == MSG_S_NOTE)) + return; - /* UC_BREAK_POSSIBLE means that a line break can be - inserted, and that the character should be included - in the next line. - UC_BREAK_MANDATORY means that this character is a line - break, so it should not be included in the next line. */ - line_start = i + (breaks[i] == UC_BREAK_MANDATORY); - } - if (line_start < length) - dump_line (line_indent, - ss_buffer (string + line_start, length - line_start), aux); - - free (string); - free (breaks); -} + message_item_submit (message_item_create (m)); -/* Write LINE_INDENT spaces, LINE, then a new-line to STREAM. */ -static void -write_stream (int line_indent, struct substring line, void *stream_) -{ - FILE *stream = stream_; - int i; - for (i = 0; i < line_indent; i++) - putc (' ', stream); - fwrite (ss_data (line), 1, ss_length (line), stream); - putc ('\n', stream); -} - -/* Writes LINE to the journal. */ -static void -write_journal (int line_indent UNUSED, struct substring line, void *unused UNUSED) -{ - char *s = xstrndup (ss_data (line), ss_length (line)); - journal_write (true, s); - free (s); + counts[m->severity]++; + max_msgs = settings_get_max_messages (m->severity); + n_msgs = counts[m->severity]; + if (m->severity == MSG_S_WARNING) + n_msgs += counts[MSG_S_ERROR]; + if (n_msgs > max_msgs) + { + if (m->severity == MSG_S_NOTE) + { + too_many_notes = true; + submit_note (xasprintf (_("Notes (%d) exceed limit (%d). " + "Suppressing further notes."), + n_msgs, max_msgs)); + } + else + { + too_many_errors = true; + if (m->severity == MSG_S_WARNING) + submit_note (xasprintf (_("Warnings (%d) exceed limit (%d)."), + n_msgs, max_msgs)); + else + submit_note (xasprintf (_("Errors (%d) exceed limit (%d)."), + n_msgs, max_msgs)); + } + } } diff --git a/src/ui/terminal/msg-ui.h b/src/ui/terminal/msg-ui.h index aa49e730..2c08a882 100644 --- a/src/ui/terminal/msg-ui.h +++ b/src/ui/terminal/msg-ui.h @@ -18,14 +18,15 @@ #define MSG_UI_H 1 #include +#include -struct source_stream ; +struct source_stream; -void msg_ui_set_error_file (const char *filename); +void msg_ui_set_error_file (FILE *); void msg_ui_init (struct source_stream *); void msg_ui_done (void); -void check_msg_count (struct source_stream *); -void reset_msg_count (void); -bool any_errors (void); +bool msg_ui_too_many_errors (void); +void msg_ui_reset_counts (void); +bool msg_ui_any_errors (void); #endif /* msg-ui.h */ diff --git a/src/ui/terminal/read-line.c b/src/ui/terminal/read-line.c index 54f90858..0fa145c9 100644 --- a/src/ui/terminal/read-line.c +++ b/src/ui/terminal/read-line.c @@ -151,7 +151,7 @@ readln_read (struct string *line, enum prompt_style style) assert (initialised); - reset_msg_count (); + msg_ui_reset_counts (); welcome (); diff --git a/src/ui/terminal/terminal-opts.c b/src/ui/terminal/terminal-opts.c index 886bcdc2..a0f6072b 100644 --- a/src/ui/terminal/terminal-opts.c +++ b/src/ui/terminal/terminal-opts.c @@ -38,6 +38,7 @@ #include "libpspp/version.h" #include "output/driver.h" #include "output/driver-provider.h" +#include "output/msglog.h" #include "ui/terminal/msg-ui.h" #include "ui/terminal/read-line.h" @@ -57,6 +58,7 @@ struct terminal_opts struct string_map options; /* Output driver options. */ bool has_output_driver; bool has_terminal_driver; + bool has_error_file; bool process_statrc; }; @@ -234,7 +236,8 @@ terminal_option_callback (int id, void *to_) break; case OPT_ERROR_FILE: - msg_ui_set_error_file (optarg); + if (!strcmp (optarg, "none") || msglog_create (optarg)) + to->has_error_file = true; break; case OPT_OUTPUT: @@ -279,6 +282,7 @@ terminal_opts_init (struct argv_parser *ap, struct source_stream *ss) to->syntax_mode = GETL_BATCH; string_map_init (&to->options); to->has_output_driver = false; + to->has_error_file = false; to->process_statrc = true; argv_parser_add_options (ap, terminal_argv_options, N_TERMINAL_OPTIONS, @@ -331,6 +335,9 @@ terminal_opts_done (struct terminal_opts *to, int argc, char *argv[]) register_output_driver (to); } + if (to->has_terminal_driver && !to->has_error_file) + msglog_create ("-"); + string_map_destroy (&to->options); free (to); }