output: Modernize how drivers are initialized.
[pspp] / src / output / ascii.c
index 46275bc363d61a22850933b6ff5f69cb48c86559..b94d4941c2362d05a198e844e9685d02bce79e7b 100644 (file)
 #include <uniwidth.h>
 
 #ifdef HAVE_TERMIOS_H
-# include <termios.h>
-#endif
-
-#ifdef GWINSZ_IN_SYS_IOCTL
 # include <sys/ioctl.h>
+# include <termios.h>
 #endif
 
 #include "data/file-name.h"
 #include "libpspp/u8-line.h"
 #include "libpspp/version.h"
 #include "output/ascii.h"
-#ifdef HAVE_CAIRO
 #include "output/cairo-chart.h"
-#endif
-#include "output/chart-item-provider.h"
+#include "output/chart-provider.h"
 #include "output/driver-provider.h"
-#include "output/message-item.h"
 #include "output/options.h"
 #include "output/pivot-output.h"
 #include "output/pivot-table.h"
 #include "output/render.h"
-#include "output/table-item.h"
-#include "output/text-item.h"
+#include "output/output-item.h"
 
 #include "gl/minmax.h"
 #include "gl/xalloc.h"
@@ -180,7 +173,7 @@ get_unicode_box (void)
     {
       /* r  b  l   t:   _       d       S       D */
       .c[_][_][_] = { 0x0020, 0x2575, 0x2575, 0x2551, }, /*  ╵╵║ */
-      .c[_][_][d] = { 0x2574, 0x256f, 0x256f, 0x255c, }, /* ╴╯╯╜ */
+      .c[_][_][d] = { 0x254c, 0x256f, 0x256f, 0x255c, }, /* ╴╯╯╜ */
       .c[_][_][S] = { 0x2574, 0x256f, 0x256f, 0x255c, }, /* ╴╯╯╜ */
       .c[_][_][D] = { 0x2550, 0x255b, 0x255b, 0x255d, }, /* ═╛╛╝ */
       .c[_][S][_] = { 0x2577, 0x2502, 0x2502, 0x2551, }, /* ╷││║ */
@@ -195,7 +188,7 @@ get_unicode_box (void)
       .c[_][D][d] = { 0x2556, 0x2562, 0x2562, 0x2562, }, /* ╖╢╢╢ */
       .c[_][D][S] = { 0x2556, 0x2562, 0x2562, 0x2562, }, /* ╖╢╢╢ */
       .c[_][D][D] = { 0x2557, 0x2563, 0x2563, 0x2563, }, /* ╗╣╣╣ */
-      .c[d][_][_] = { 0x2576, 0x2570, 0x2570, 0x2559, }, /* ╶╰╰╙ */
+      .c[d][_][_] = { 0x254c, 0x2570, 0x2570, 0x2559, }, /* ╶╰╰╙ */
       .c[d][_][d] = { 0x254c, 0x2534, 0x2534, 0x2568, }, /* ╌┴┴╨ */
       .c[d][_][S] = { 0x2500, 0x2534, 0x2534, 0x2568, }, /* ─┴┴╨ */
       .c[d][_][D] = { 0x2550, 0x2567, 0x2567, 0x2569, }, /* ═╧╧╩ */
@@ -252,18 +245,18 @@ ascii_line_from_render_line (int render_line)
 {
   switch (render_line)
     {
-    case RENDER_LINE_NONE:
+    case TABLE_STROKE_NONE:
       return ASCII_LINE_NONE;
 
-    case RENDER_LINE_DASHED:
+    case TABLE_STROKE_DASHED:
       return ASCII_LINE_DASHED;
 
-    case RENDER_LINE_SINGLE:
-    case RENDER_LINE_THICK:
-    case RENDER_LINE_THIN:
+    case TABLE_STROKE_SOLID:
+    case TABLE_STROKE_THICK:
+    case TABLE_STROKE_THIN:
       return ASCII_LINE_SINGLE;
 
-    case RENDER_LINE_DOUBLE:
+    case TABLE_STROKE_DOUBLE:
       return ASCII_LINE_DOUBLE;
 
     default:
@@ -285,28 +278,29 @@ box_get (const struct box_chars *box,
   return box->c[right][bottom][left][top];
 }
 
+/* How the page width is determined. */
+enum ascii_width_mode
+  {
+    FIXED_WIDTH,              /* Specified by configuration. */
+    VIEW_WIDTH,               /* From SET WIDTH. */
+    TERMINAL_WIDTH            /* From the terminal's width. */
+  };
+
 /* ASCII output driver. */
 struct ascii_driver
   {
     struct output_driver driver;
 
     /* User parameters. */
-    bool append;                /* Append if output file already exists? */
     bool emphasis;              /* Enable bold and underline in output? */
     char *chart_file_name;      /* Name of files used for charts. */
 
-#ifdef HAVE_CAIRO
     /* Colours for charts */
     struct cell_color fg;
     struct cell_color bg;
-#endif
 
     /* How the page width is determined: */
-    enum {
-      FIXED_WIDTH,              /* Specified by configuration. */
-      VIEW_WIDTH,               /* From SET WIDTH. */
-      TERMINAL_WIDTH            /* From the terminal's width. */
-    } width_mode;
+    enum ascii_width_mode width_mode;
     int width;                  /* Page width. */
 
     int min_hbreak;             /* Min cell size to break across pages. */
@@ -319,8 +313,8 @@ struct ascii_driver
     bool error;                 /* Output error? */
     struct u8_line *lines;      /* Page content. */
     int allocated_lines;        /* Number of lines allocated. */
-    int chart_cnt;              /* Number of charts so far. */
-    int object_cnt;             /* Number of objects so far. */
+    int n_charts;               /* Number of charts so far. */
+    int n_objects;              /* Number of objects so far. */
     const struct pivot_table *pt;
     struct render_params params;
   };
@@ -335,8 +329,7 @@ static bool update_page_size (struct ascii_driver *, bool issue_error);
 static int parse_page_size (struct driver_option *);
 
 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
-                             enum render_line_style styles[TABLE_N_AXES][2],
-                             struct cell_color colors[TABLE_N_AXES][2]);
+                             const struct table_border_style[TABLE_N_AXES][2]);
 static void ascii_measure_cell_width (void *, const struct table_cell *,
                                       int *min, int *max);
 static int ascii_measure_cell_height (void *, const struct table_cell *,
@@ -354,10 +347,9 @@ ascii_driver_cast (struct output_driver *driver)
 }
 
 static struct driver_option *
-opt (struct output_driver *d, struct string_map *options, const char *key,
-     const char *default_value)
+opt (struct string_map *options, const char *key, const char *default_value)
 {
-  return driver_option_get (d, options, key, default_value);
+  return driver_option_get ("ascii", options, key, default_value);
 }
 
 /* Return true iff the terminal appears to be an xterm with
@@ -377,47 +369,25 @@ static struct output_driver *
 ascii_create (struct  file_handle *fh, enum settings_output_devices device_type,
               struct string_map *o)
 {
-  enum { BOX_ASCII, BOX_UNICODE } box;
-  struct output_driver *d;
-  struct ascii_driver *a;
-
-  a = xzalloc (sizeof *a);
-  d = &a->driver;
-  output_driver_init (&a->driver, &ascii_driver_class, fh_get_file_name (fh), device_type);
-  a->append = parse_boolean (opt (d, o, "append", "false"));
-  a->emphasis = parse_boolean (opt (d, o, "emphasis", "false"));
-
-  a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", fh_get_file_name (fh)));
-  a->handle = fh;
-
+  bool append = parse_boolean (opt (o, "append", "false"));
+  FILE *file = fn_open (fh, append ? "a" : "w");
+  if (!file)
+    {
+      msg_error (errno, _("ascii: opening output file `%s'"),
+                 fh_get_file_name (fh));
+      return NULL;
+    }
 
+  int width = parse_page_size (opt (o, "width", "-1"));
   bool terminal = !strcmp (fh_get_file_name (fh), "-") && isatty (1);
-  a->width = parse_page_size (opt (d, o, "width", "-1"));
-  a->width_mode = (a->width > 0 ? FIXED_WIDTH
-                   : terminal ? TERMINAL_WIDTH
-                   : VIEW_WIDTH);
-  a->min_hbreak = parse_int (opt (d, o, "min-hbreak", "-1"), -1, INT_MAX);
-
-#ifdef HAVE_CAIRO
-  a->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF"));
-  a->fg = parse_color (opt (d, o, "foreground-color", "#000000000000"));
-#endif
 
   const char *default_box = (terminal && (!strcmp (locale_charset (), "UTF-8")
                                           || term_is_utf8_xterm ())
                              ? "unicode" : "ascii");
-  box = parse_enum (opt (d, o, "box", default_box),
-                    "ascii", BOX_ASCII,
-                    "unicode", BOX_UNICODE,
-                    NULL_SENTINEL);
-  a->box = box == BOX_ASCII ? get_ascii_box () : get_unicode_box ();
-
-  a->file = NULL;
-  a->error = false;
-  a->lines = NULL;
-  a->allocated_lines = 0;
-  a->chart_cnt = 0;
-  a->object_cnt = 0;
+  enum { BOX_ASCII, BOX_UNICODE } box = parse_enum (opt (o, "box", default_box),
+                                                    "ascii", BOX_ASCII,
+                                                    "unicode", BOX_UNICODE,
+                                                    NULL_SENTINEL);
 
   static const struct render_ops ascii_render_ops = {
     .draw_line = ascii_draw_line,
@@ -426,41 +396,61 @@ ascii_create (struct  file_handle *fh, enum settings_output_devices device_type,
     .adjust_break = NULL,
     .draw_cell = ascii_draw_cell,
   };
-  a->params.ops = &ascii_render_ops;
-  a->params.aux = a;
-  a->params.size[H] = a->width;
-  a->params.size[V] = INT_MAX;
-  a->params.font_size[H] = 1;
-  a->params.font_size[V] = 1;
-
-  static const int ascii_line_widths[RENDER_N_LINES] = {
-    [RENDER_LINE_NONE] = 0,
-    [RENDER_LINE_SINGLE] = 1,
-    [RENDER_LINE_DASHED] = 1,
-    [RENDER_LINE_THICK] = 1,
-    [RENDER_LINE_THIN] = 1,
-    [RENDER_LINE_DOUBLE] = 1,
+
+  static const int ascii_line_widths[TABLE_N_STROKES] = {
+    [TABLE_STROKE_NONE] = 0,
+    [TABLE_STROKE_SOLID] = 1,
+    [TABLE_STROKE_DASHED] = 1,
+    [TABLE_STROKE_THICK] = 1,
+    [TABLE_STROKE_THIN] = 1,
+    [TABLE_STROKE_DOUBLE] = 1,
+  };
+
+  struct ascii_driver *a = xmalloc (sizeof *a);
+  *a = (struct ascii_driver) {
+    .driver = {
+      .class = &ascii_driver_class,
+      .name = xstrdup (fh_get_file_name (fh)),
+      .device_type = device_type
+    },
+
+    .emphasis = parse_boolean (opt (o, "emphasis", "false")),
+    .chart_file_name = parse_chart_file_name (opt (o, "charts",
+                                                   fh_get_file_name (fh))),
+
+    .fg = parse_color (opt (o, "foreground-color", "#000000000000")),
+    .bg = parse_color (opt (o, "background-color", "#FFFFFFFFFFFF")),
+
+    .width_mode = (width > 0 ? FIXED_WIDTH
+                   : terminal ? TERMINAL_WIDTH
+                   : VIEW_WIDTH),
+    .width = width,
+
+    .min_hbreak = parse_int (opt (o, "min-hbreak", "-1"), -1, INT_MAX),
+
+    .box = box == BOX_ASCII ? get_ascii_box () : get_unicode_box (),
+
+    .handle = fh,
+    .file = file,
+
+    .params = (struct render_params) {
+      .ops = &ascii_render_ops,
+      .aux = a,
+      .size = { [H] = a->width, [V] = INT_MAX },
+      .font_size = { [H] = 1, [V] = 1 },
+      .line_widths = ascii_line_widths,
+      .rtl = render_direction_rtl (),
+      .printing = true,
+    },
   };
-  a->params.line_widths = ascii_line_widths;
-  a->params.supports_margins = false;
-  a->params.rtl = render_direction_rtl ();
-  a->params.printing = true;
 
   if (!update_page_size (a, true))
     goto error;
 
-  a->file = fn_open (a->handle, a->append ? "a" : "w");
-  if (!a->file)
-    {
-      msg_error (errno, _("ascii: opening output file `%s'"),
-                 fh_get_file_name (a->handle));
-      goto error;
-    }
-
-  return d;
+  return &a->driver;
 
 error:
-  output_driver_destroy (d);
+  output_driver_destroy (&a->driver);
   return NULL;
 }
 
@@ -560,19 +550,19 @@ ascii_output_lines (struct ascii_driver *a, size_t n_lines)
 
 static void
 ascii_output_table_item (struct ascii_driver *a,
-                         const struct table_item *table_item)
+                         const struct output_item *item)
 {
   update_page_size (a, false);
-  a->pt = table_item->pt;
+  a->pt = item->table;
 
   size_t *layer_indexes;
-  PIVOT_OUTPUT_FOR_EACH_LAYER (layer_indexes, table_item->pt, true)
+  PIVOT_OUTPUT_FOR_EACH_LAYER (layer_indexes, item->table, true)
     {
-      struct render_pager *p = render_pager_create (&a->params, table_item,
+      struct render_pager *p = render_pager_create (&a->params, item->table,
                                                     layer_indexes);
       for (int i = 0; render_pager_has_next (p); i++)
         {
-          if (a->object_cnt++)
+          if (a->n_objects++)
             putc ('\n', a->file);
 
           ascii_output_lines (a, render_pager_draw_next (p, INT_MAX));
@@ -585,63 +575,82 @@ ascii_output_table_item (struct ascii_driver *a,
 
 static void
 ascii_output_table_item_unref (struct ascii_driver *a,
-                               struct table_item *table_item)
+                               struct output_item *table_item)
 {
   ascii_output_table_item (a, table_item);
-  table_item_unref (table_item);
+  output_item_unref (table_item);
 }
 
 static void
-ascii_submit (struct output_driver *driver,
-              const struct output_item *output_item)
+ascii_submit (struct output_driver *driver, const struct output_item *item)
 {
   struct ascii_driver *a = ascii_driver_cast (driver);
-
   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)
+  switch (item->type)
     {
-      struct chart_item *chart_item = to_chart_item (output_item);
-      char *file_name;
-
-      file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
-                                     ++a->chart_cnt,
-                                    &a->fg,
-                                    &a->bg);
-      if (file_name != NULL)
-        {
-          struct text_item *text_item;
+    case OUTPUT_ITEM_TABLE:
+      ascii_output_table_item (a, item);
+      break;
 
-          text_item = text_item_create_nocopy (
-            TEXT_ITEM_LOG,
-            xasprintf (_("See %s for a chart."), file_name),
-            NULL);
+    case OUTPUT_ITEM_IMAGE:
+      if (a->chart_file_name != NULL)
+        {
+          char *file_name = xr_write_png_image (
+            item->image, a->chart_file_name, ++a->n_charts);
+          if (file_name != NULL)
+            {
+              struct output_item *text_item = text_item_create_nocopy (
+                TEXT_ITEM_LOG,
+                xasprintf (_("See %s for an image."), file_name),
+                NULL);
+
+              ascii_submit (driver, text_item);
+              output_item_unref (text_item);
+              free (file_name);
+            }
+        }
+      break;
 
-          ascii_submit (driver, &text_item->output_item);
-          text_item_unref (text_item);
-          free (file_name);
+    case OUTPUT_ITEM_CHART:
+      if (a->chart_file_name != NULL)
+        {
+          char *file_name = xr_draw_png_chart (
+            item->chart, a->chart_file_name, ++a->n_charts, &a->fg, &a->bg);
+          if (file_name != NULL)
+            {
+              struct output_item *text_item = text_item_create_nocopy (
+                TEXT_ITEM_LOG,
+                xasprintf (_("See %s for a chart."), file_name),
+                NULL);
+
+              ascii_submit (driver, text_item);
+              output_item_unref (text_item);
+              free (file_name);
+            }
         }
-    }
-#endif  /* HAVE_CAIRO */
-  else if (is_text_item (output_item))
-    {
-      const struct text_item *text_item = to_text_item (output_item);
-      enum text_item_type type = text_item_get_type (text_item);
+      break;
 
-      if (type != TEXT_ITEM_PAGE_TITLE)
+    case OUTPUT_ITEM_TEXT:
+      if (item->text.subtype != TEXT_ITEM_PAGE_TITLE)
         ascii_output_table_item_unref (
-          a, text_item_to_table_item (text_item_ref (text_item)));
+          a, text_item_to_table_item (output_item_ref (item)));
+      break;
+
+    case OUTPUT_ITEM_MESSAGE:
+      ascii_output_table_item_unref (
+        a, text_item_to_table_item (
+          message_item_to_text_item (
+            output_item_ref (item))));
+      break;
+
+    case OUTPUT_ITEM_GROUP:
+      break;
+
+    case OUTPUT_ITEM_PAGE_BREAK:
+      break;
     }
-  else if (is_message_item (output_item))
-    ascii_output_table_item_unref (
-      a, text_item_to_table_item (
-        message_item_to_text_item (
-          to_message_item (
-            output_item_ref (output_item)))));
 }
 
 const struct output_driver_factory txt_driver_factory =
@@ -651,10 +660,10 @@ const struct output_driver_factory list_driver_factory =
 
 static const struct output_driver_class ascii_driver_class =
   {
-    "text",
-    ascii_destroy,
-    ascii_submit,
-    ascii_flush,
+    .name = "text",
+    .destroy = ascii_destroy,
+    .submit = ascii_submit,
+    .flush = ascii_flush,
   };
 \f
 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
@@ -667,8 +676,7 @@ static void ascii_layout_cell (struct ascii_driver *,
 
 static void
 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
-                 enum render_line_style styles[TABLE_N_AXES][2],
-                 struct cell_color colors[TABLE_N_AXES][2] UNUSED)
+                 const struct table_border_style styles[TABLE_N_AXES][2])
 {
   struct ascii_driver *a = a_;
   char mbchar[6];
@@ -686,7 +694,11 @@ ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
     return;
 
   /* Draw. */
-  uc = box_get (a->box, styles[V][0], styles[V][1], styles[H][0], styles[H][1]);
+  enum table_stroke v0 = styles[V][0].stroke;
+  enum table_stroke v1 = styles[V][1].stroke;
+  enum table_stroke h0 = styles[H][0].stroke;
+  enum table_stroke h1 = styles[H][1].stroke;
+  uc = box_get (a->box, v0, v1, h0, h1);
   mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
   for (y = y0; y < y1; y++)
     {
@@ -913,7 +925,7 @@ ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
   *heightp = 0;
 
   struct string body = DS_EMPTY_INITIALIZER;
-  bool numeric = pivot_value_format_body (cell->value, a->pt, &body);
+  bool numeric = pivot_value_format (cell->value, a->pt, &body);
 
   /* Calculate length; if it's zero, then there's nothing to do. */
   if (ds_is_empty (&body))
@@ -1025,8 +1037,8 @@ ascii_test_write (struct output_driver *driver,
     .underline = underline,
   };
   const struct pivot_value value = {
-    .type = PIVOT_VALUE_TEXT,
     .text = {
+      .type = PIVOT_VALUE_TEXT,
       .local = CONST_CAST (char *, s),
       .c = CONST_CAST (char *, s),
       .id = CONST_CAST (char *, s),