output: Modernize how drivers are initialized.
[pspp] / src / output / ascii.c
index c058f23f8a645068ce33de1ca4eda607c339242c..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"
@@ -178,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, }, /* ╷││║ */
@@ -193,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, }, /* ═╧╧╩ */
@@ -250,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:
@@ -283,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. */
@@ -317,8 +313,9 @@ 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;
   };
 
@@ -332,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 *,
@@ -351,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
@@ -374,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,
@@ -423,40 +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 ();
 
   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;
 }
 
@@ -556,84 +550,107 @@ 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)
 {
-  struct render_pager *p;
-
   update_page_size (a, false);
+  a->pt = item->table;
 
-  if (a->object_cnt++)
-    putc ('\n', a->file);
-
-  p = render_pager_create (&a->params, table_item);
-  for (int i = 0; render_pager_has_next (p); i++)
+  size_t *layer_indexes;
+  PIVOT_OUTPUT_FOR_EACH_LAYER (layer_indexes, item->table, true)
     {
-      if (i)
-        putc ('\n', a->file);
-      ascii_output_lines (a, render_pager_draw_next (p, INT_MAX));
+      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->n_objects++)
+            putc ('\n', a->file);
+
+          ascii_output_lines (a, render_pager_draw_next (p, INT_MAX));
+        }
+      render_pager_destroy (p);
     }
-  render_pager_destroy (p);
+
+  a->pt = NULL;
 }
 
 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 =
@@ -643,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,
@@ -659,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];
@@ -678,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++)
     {
@@ -707,14 +727,8 @@ ascii_measure_cell_width (void *a_, const struct table_cell *cell,
   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
   ascii_layout_cell (a, cell, bb, clip, max_width, &h);
 
-  if (cell->n_footnotes || strchr (cell->text, ' ')
-      || cell->n_subscripts)
-    {
-      bb[H][1] = 1;
-      ascii_layout_cell (a, cell, bb, clip, min_width, &h);
-    }
-  else
-    *min_width = *max_width;
+  bb[H][1] = 1;
+  ascii_layout_cell (a, cell, bb, clip, min_width, &h);
 }
 
 static int
@@ -764,7 +778,7 @@ ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
 }
 
 static void
-text_draw (struct ascii_driver *a, enum table_halign halign, int options,
+text_draw (struct ascii_driver *a, enum table_halign halign, bool numeric,
            bool bold, bool underline,
            int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
            int y, const uint8_t *string, int n, size_t width)
@@ -778,7 +792,7 @@ text_draw (struct ascii_driver *a, enum table_halign halign, int options,
   if (y < y0 || y >= y1)
     return;
 
-  switch (table_halign_interpret (halign, options & TAB_NUMERIC))
+  switch (table_halign_interpret (halign, numeric))
     {
     case TABLE_HALIGN_LEFT:
       x = bb[H][0];
@@ -902,18 +916,6 @@ text_draw (struct ascii_driver *a, enum table_halign halign, int options,
     }
 }
 
-static char *
-add_markers (const char *text, const struct table_cell *cell)
-{
-  struct string s = DS_EMPTY_INITIALIZER;
-  ds_put_cstr (&s, text);
-  for (size_t i = 0; i < cell->n_subscripts; i++)
-    ds_put_format (&s, "%c%s", i ? ',' : '_', cell->subscripts[i]);
-  for (size_t i = 0; i < cell->n_footnotes; i++)
-    ds_put_format (&s, "[%s]", cell->footnotes[i]->marker);
-  return ds_steal_cstr (&s);
-}
-
 static void
 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
                    int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
@@ -922,34 +924,21 @@ ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
   *widthp = 0;
   *heightp = 0;
 
-  /* Get the basic textual contents. */
-  const char *plain_text = (cell->options & TAB_MARKUP
-                            ? output_get_text_from_markup (cell->text)
-                            : cell->text);
-
-  /* Append footnotes, subscripts if any. */
-  const char *text;
-  if (cell->n_footnotes || cell->n_subscripts)
-    {
-      text = add_markers (plain_text, cell);
-      if (plain_text != cell->text)
-        free (CONST_CAST (char *, plain_text));
-    }
-  else
-    text = plain_text;
+  struct string body = DS_EMPTY_INITIALIZER;
+  bool numeric = pivot_value_format (cell->value, a->pt, &body);
 
   /* Calculate length; if it's zero, then there's nothing to do. */
-  size_t length = strlen (text);
-  if (!length)
+  if (ds_is_empty (&body))
     {
-      if (text != cell->text)
-        free (CONST_CAST (char *, text));
+      ds_destroy (&body);
       return;
     }
 
+  size_t length = ds_length (&body);
+  const uint8_t *text = CHAR_CAST (uint8_t *, ds_cstr (&body));
+
   char *breaks = xmalloc (length + 1);
-  u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
-                          "UTF-8", breaks);
+  u8_possible_linebreaks (text, length, "UTF-8", breaks);
   breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
                     ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
 
@@ -957,7 +946,7 @@ ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
   int bb_width = bb[H][1] - bb[H][0];
   for (int y = bb[V][0]; y < bb[V][1] && pos < length; y++)
     {
-      const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
+      const uint8_t *line = text + pos;
       const char *b = breaks + pos;
       size_t n = length - pos;
 
@@ -1008,9 +997,9 @@ ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
       width -= ofs - graph_ofs;
 
       /* Draw text. */
-      text_draw (a, cell->style->cell_style.halign, cell->options,
-                 cell->style->font_style.bold,
-                 cell->style->font_style.underline,
+      text_draw (a, cell->cell_style->halign, numeric,
+                 cell->font_style->bold,
+                 cell->font_style->underline,
                  bb, clip, y, line, graph_ofs, width);
 
       /* If a new-line ended the line, just skip the new-line.  Otherwise, skip
@@ -1028,8 +1017,7 @@ ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
     }
 
   free (breaks);
-  if (text != cell->text)
-    free (CONST_CAST (char *, text));
+  ds_destroy (&body);
 }
 
 void
@@ -1043,14 +1031,24 @@ ascii_test_write (struct output_driver *driver,
   if (!a->file)
     return;
 
-  struct table_area_style style = {
-    .cell_style.halign = TABLE_HALIGN_LEFT,
-    .font_style.bold = bold,
-    .font_style.underline = underline,
+  struct cell_style cell_style = { .halign = TABLE_HALIGN_LEFT };
+  struct font_style font_style = {
+    .bold = bold,
+    .underline = underline,
+  };
+  const struct pivot_value value = {
+    .text = {
+      .type = PIVOT_VALUE_TEXT,
+      .local = CONST_CAST (char *, s),
+      .c = CONST_CAST (char *, s),
+      .id = CONST_CAST (char *, s),
+      .user_provided = true,
+    },
   };
   struct table_cell cell = {
-    .text = CONST_CAST (char *, s),
-    .style = &style,
+    .value = &value,
+    .font_style = &font_style,
+    .cell_style = &cell_style,
   };
 
   bb[TABLE_HORZ][0] = x;
@@ -1058,7 +1056,13 @@ ascii_test_write (struct output_driver *driver,
   bb[TABLE_VERT][0] = y;
   bb[TABLE_VERT][1] = INT_MAX;
 
+  struct pivot_table pt = {
+    .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
+    .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
+  };
+  a->pt = &pt;
   ascii_layout_cell (a, &cell, bb, bb, &width, &height);
+  a->pt = NULL;
 }
 
 void