cast: New macro NULL_SENTINEL.
[pspp] / src / output / ascii.c
index ffe8515f8b51a7bc3147431e9b68ad30b44aa8b8..c9f96b21b58cbd287d2d41152e572ee6a658bd7d 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007, 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
 #include <stdint.h>
 #include <stdlib.h>
 
-#include <data/file-name.h>
-#include <data/settings.h>
-#include <libpspp/assertion.h>
-#include <libpspp/compiler.h>
-#include <libpspp/start-date.h>
-#include <libpspp/string-map.h>
-#include <libpspp/version.h>
-#include <output/cairo.h>
-#include <output/chart-item-provider.h>
+#include "data/file-name.h"
+#include "data/settings.h"
+#include "libpspp/assertion.h"
+#include "libpspp/cast.h"
+#include "libpspp/compiler.h"
+#include "libpspp/message.h"
+#include "libpspp/start-date.h"
+#include "libpspp/string-map.h"
+#include "libpspp/version.h"
+#include "output/cairo.h"
+#include "output/chart-item-provider.h"
+#include "output/driver-provider.h"
+#include "output/message-item.h"
 #include "output/options.h"
-#include <output/tab.h>
-#include <output/text-item.h>
-#include <output/driver-provider.h>
-#include <output/render.h>
-#include <output/table-item.h>
+#include "output/render.h"
+#include "output/tab.h"
+#include "output/table-item.h"
+#include "output/text-item.h"
 
-#include "error.h"
-#include "minmax.h"
-#include "xalloc.h"
+#include "gl/error.h"
+#include "gl/minmax.h"
+#include "gl/xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -93,7 +96,7 @@ struct ascii_driver
     struct output_driver driver;
 
     /* User parameters. */
-    bool append;                /* Append if output-file already exists? */
+    bool append;                /* Append if output file already exists? */
     bool headers;              /* Print headers at top of page? */
     bool paginate;             /* Insert formfeeds? */
     bool squeeze_blank_lines;   /* Squeeze multiple blank lines into one? */
@@ -112,6 +115,7 @@ struct ascii_driver
     char *init;                 /* Device initialization string. */
 
     /* Internal state. */
+    char *command_name;
     char *title;
     char *subtitle;
     char *file_name;            /* Output file name. */
@@ -124,6 +128,10 @@ struct ascii_driver
     int y;
   };
 
+static const struct output_driver_class ascii_driver_class;
+
+static void ascii_submit (struct output_driver *, const struct output_item *);
+
 static int vertical_margins (const struct ascii_driver *);
 
 static const char *get_default_box (int right, int bottom, int left, int top);
@@ -146,7 +154,7 @@ static void ascii_draw_cell (void *, const struct table_cell *,
 static struct ascii_driver *
 ascii_driver_cast (struct output_driver *driver)
 {
-  assert (driver->class == &ascii_class);
+  assert (driver->class == &ascii_driver_class);
   return UP_CAST (driver, struct ascii_driver, driver);
 }
 
@@ -158,7 +166,7 @@ opt (struct output_driver *d, struct string_map *options, const char *key,
 }
 
 static struct output_driver *
-ascii_create (const char *name, enum output_device_type device_type,
+ascii_create (const char *file_name, enum settings_output_devices device_type,
               struct string_map *o)
 {
   struct output_driver *d;
@@ -168,28 +176,21 @@ ascii_create (const char *name, enum output_device_type device_type,
 
   a = xzalloc (sizeof *a);
   d = &a->driver;
-  output_driver_init (&a->driver, &ascii_class, name, device_type);
+  output_driver_init (&a->driver, &ascii_driver_class, file_name, device_type);
   a->append = parse_boolean (opt (d, o, "append", "false"));
-  a->headers = parse_boolean (opt (d, o, "headers", "true"));
-  a->paginate = parse_boolean (opt (d, o, "paginate", "true"));
-  a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "false"));
-  a->emphasis = parse_enum (opt (d, o, "emphasis", "bold"),
+  a->headers = parse_boolean (opt (d, o, "headers", "false"));
+  a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
+  a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
+  a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
                             "bold", EMPH_BOLD,
                             "underline", EMPH_UNDERLINE,
                             "none", EMPH_NONE,
-                            (char *) NULL);
-
-  if (parse_enum (opt (d, o, "chart-type", "png"),
-                  "png", true,
-                  "none", false,
-                  (char *) NULL))
-    a->chart_file_name = parse_chart_file_name (opt (d, o, "chart-files",
-                                                     "pspp-#.png"));
-  else
-    a->chart_file_name = NULL;
+                            NULL_SENTINEL);
+
+  a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", file_name));
 
-  a->top_margin = parse_int (opt (d, o, "top-margin", "2"), 0, INT_MAX);
-  a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "2"), 0, INT_MAX);
+  a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
+  a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
 
   a->width = parse_page_size (opt (d, o, "width", "79"));
   paper_length = parse_page_size (opt (d, o, "length", "66"));
@@ -212,9 +213,10 @@ ascii_create (const char *name, enum output_device_type device_type,
           }
   a->init = parse_string (opt (d, o, "init", ""));
 
+  a->command_name = NULL;
   a->title = xstrdup ("");
   a->subtitle = xstrdup ("");
-  a->file_name = parse_string (opt (d, o, "output-file", "pspp.list"));
+  a->file_name = xstrdup (file_name);
   a->file = NULL;
   a->error = false;
   a->page_number = 0;
@@ -333,6 +335,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);
@@ -355,7 +358,7 @@ ascii_flush (struct output_driver *driver)
       ascii_close_page (a);
 
       if (fn_close (a->file_name, a->file) != 0)
-        error (0, errno, _("ascii: closing output file \"%s\""),
+        error (0, errno, _("ascii: closing output file `%s'"),
                a->file_name);
       a->file = NULL;
     }
@@ -370,103 +373,122 @@ 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)
     {
-      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;
+      /* 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++)
+    {
+      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 (&params, 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 (&params, 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));
+#ifdef HAVE_CAIRO
   else if (is_chart_item (output_item) && a->chart_file_name != NULL)
     {
       struct chart_item *chart_item = to_chart_item (output_item);
@@ -486,6 +508,7 @@ ascii_submit (struct output_driver *driver,
           free (file_name);
         }
     }
+#endif  /* HAVE_CAIRO */
   else if (is_text_item (output_item))
     {
       const struct text_item *text_item = to_text_item (output_item);
@@ -518,23 +541,28 @@ 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_class ascii_class =
+const struct output_driver_factory txt_driver_factory =
+  { "txt", ascii_create };
+const struct output_driver_factory list_driver_factory =
+  { "list", ascii_create };
+
+static const struct output_driver_class ascii_driver_class =
   {
-    "ascii",
-    ascii_create,
+    "text",
     ascii_destroy,
     ascii_submit,
     ascii_flush,
@@ -776,7 +804,7 @@ ascii_open_page (struct ascii_driver *a)
         }
       else
         {
-          error (0, errno, _("ascii: opening output file \"%s\""),
+          error (0, errno, _("ascii: opening output file `%s'"),
                  a->file_name);
           a->error = true;
           return false;