data_out function to dynamically allocate return value.
[pspp-builds.git] / src / output / table.c
index eac8d90f186b9d2b7d13dfe628e1b34800183834..a86f407d5f3aa18cab0bbc0376a7eb603de3ceb9 100644 (file)
@@ -1,48 +1,53 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2006, 2009 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 2 of the
-   License, or (at your option) any later version.
+   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.
+   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, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
+
 #include "table.h"
+
 #include <ctype.h>
 #include <stdarg.h>
 #include <limits.h>
 #include <stdlib.h>
-#include <libpspp/alloc.h>
-#include <libpspp/compiler.h>
+
+#include "output.h"
+#include "manager.h"
+
+#include <data/data-out.h>
 #include <data/format.h>
-#include <libpspp/magic.h>
+#include <data/value.h>
+#include <libpspp/assertion.h>
+#include <libpspp/compiler.h>
 #include <libpspp/misc.h>
-#include "minmax.h"
-#include "output.h"
 #include <libpspp/pool.h>
-#include "manager.h"
-#include <data/variable.h>
+
+#include <data/settings.h>
+
+#include "minmax.h"
+#include "xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 \f
-struct som_table_class tab_table_class;
+const struct som_table_class tab_table_class;
 static char *command_name;
 
 /* Returns the font to use for a cell with the given OPTIONS. */
 static enum outp_font
-options_to_font (unsigned options) 
+options_to_font (unsigned options)
 {
   return (options & TAB_FIX ? OUTP_FIXED
           : options & TAB_EMPH ? OUTP_EMPHASIS
@@ -58,7 +63,7 @@ tab_create (int nc, int nr, int reallocable UNUSED)
   t = pool_create_container (struct tab_table, container);
   t->col_style = TAB_COL_NONE;
   t->col_group = 0;
-  ls_null (&t->title);
+  t->title = NULL;
   t->flags = SOMF_NONE;
   t->nr = nr;
   t->nc = t->cf = nc;
@@ -71,15 +76,9 @@ tab_create (int nc, int nr, int reallocable UNUSED)
   t->rh = pool_nmalloc (t->container, nc, nr + 1);
   memset (t->rh, 0, nc * (nr + 1));
 
-  t->hrh = pool_nmalloc (t->container, nr + 1, sizeof *t->hrh);
-  memset (t->hrh, 0, sizeof *t->hrh * (nr + 1));
-
   t->rv = pool_nmalloc (t->container, nr, nc + 1);
   memset (t->rv, UCHAR_MAX, nr * (nc + 1));
 
-  t->wrv = pool_nmalloc (t->container, nc + 1, sizeof *t->wrv);
-  memset (t->wrv, 0, sizeof *t->wrv * (nc + 1));
-
   t->dim = NULL;
   t->w = t->h = NULL;
   t->col_ofs = t->row_ofs = 0;
@@ -92,6 +91,7 @@ void
 tab_destroy (struct tab_table *t)
 {
   assert (t != NULL);
+  free (t->title);
   pool_destroy (t->container);
 }
 
@@ -124,7 +124,7 @@ void
 tab_realloc (struct tab_table *t, int nc, int nr)
 {
   int ro, co;
-  
+
   assert (t != NULL);
   ro = t->row_ofs;
   co = t->col_ofs;
@@ -135,15 +135,15 @@ tab_realloc (struct tab_table *t, int nc, int nr)
     nc = t->nc;
   if (nr == -1)
     nr = t->nr;
-  
+
   assert (nc == t->nc);
-  
+
   if (nc > t->cf)
     {
-      int mr1 = min (nr, t->nr);
-      int mc1 = min (nc, t->nc);
-      
-      struct fixed_string *new_cc;
+      int mr1 = MIN (nr, t->nr);
+      int mc1 = MIN (nc, t->nc);
+
+      struct substring *new_cc;
       unsigned char *new_ct;
       int r;
 
@@ -168,7 +168,7 @@ tab_realloc (struct tab_table *t, int nc, int nr)
 
       t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
       t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
-      
+
       if (nr > t->nr)
        {
          memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
@@ -178,7 +178,7 @@ tab_realloc (struct tab_table *t, int nc, int nr)
     }
 
   memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
-  
+
   t->nr = nr;
   t->nc = nc;
 
@@ -273,7 +273,7 @@ tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
   y += t->row_ofs;
 
   assert (y >= 0);
-  assert (y < t->nr);
+  assert (y <= t->nr);
   assert (x2 >= x1 );
   assert (x1 >= 0 );
   assert (x2 < t->nc);
@@ -299,9 +299,9 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
   assert (t != NULL);
 
 #if DEBUGGING
-  if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc 
+  if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
       || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
-      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr 
+      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
     {
       printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
@@ -311,7 +311,7 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
              x2, t->col_ofs, x2 + t->col_ofs,
              y2, t->row_ofs, y2 + t->row_ofs,
              t->nc, t->nr);
-      abort ();
+      NOT_REACHED ();
     }
 #endif
 
@@ -349,7 +349,7 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
   if (i_h != -1)
     {
       int y;
-      
+
       for (y = y1 + 1; y <= y2; y++)
        {
          int x;
@@ -361,34 +361,27 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
   if (i_v != -1)
     {
       int x;
-      
+
       for (x = x1 + 1; x <= x2; x++)
        {
          int y;
-         
+
           for (y = y1; y <= y2; y++)
             t->rv[x + (t->cf + 1) * y] = i_v;
        }
     }
 }
 
-/* Formats text TEXT and arguments ARGS as indicated in OPT and sets
-   the resultant string into S in TABLE's pool. */
-static void
-text_format (struct tab_table *table, int opt, const char *text, va_list args,
-            struct fixed_string *s)
+/* Formats text TEXT and arguments ARGS as indicated in OPT in
+   TABLE's pool and returns the resultant string. */
+static struct substring
+text_format (struct tab_table *table, int opt, const char *text, va_list args)
 {
-  char *tmp = NULL;
-  
-  assert (table != NULL && text != NULL && s != NULL);
-  
-  if (opt & TAT_PRINTF) 
-    text = tmp = xvasprintf (text, args);
+  assert (table != NULL && text != NULL);
 
-  ls_create_buffer (s, text, strlen (text));
-  pool_register (table->container, free, s->string);
-  
-  free (tmp);
+  return ss_cstr (opt & TAT_PRINTF
+                  ? pool_vasprintf (table->container, text, args)
+                  : pool_strdup (table->container, text));
 }
 
 /* Set the title of table T to TITLE, which is formatted as if
@@ -400,16 +393,17 @@ tab_title (struct tab_table *t, const char *title, ...)
 
   assert (t != NULL && title != NULL);
   va_start (args, title);
-  text_format (t, TAT_PRINTF, title, args, &t->title);
+  t->title = xvasprintf (title, args);
   va_end (args);
 }
 
 /* Set DIM_FUNC as the dimension function for table T. */
 void
-tab_dim (struct tab_table *t, tab_dim_func *dim_func)
+tab_dim (struct tab_table *t, tab_dim_func *dim_func, void *aux)
 {
   assert (t != NULL && t->dim == NULL);
   t->dim = dim_func;
+  t->dim_aux = aux;
 }
 
 /* Returns the natural width of column C in table T for driver D, that
@@ -430,12 +424,11 @@ tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
        struct outp_text text;
        unsigned char opt = t->ct[c + r * t->cf];
         int w;
-               
+
        if (opt & (TAB_JOIN | TAB_EMPTY))
          continue;
 
        text.string = t->cc[c + r * t->cf];
-       assert (!ls_null_p (&text.string));
        text.justification = OUTP_LEFT;
         text.font = options_to_font (opt);
         text.h = text.v = INT_MAX;
@@ -448,15 +441,15 @@ tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
 
   if (width == 0)
     {
+      /* FIXME: This is an ugly kluge to compensate for the fact
+         that we don't let joined cells contribute to column
+         widths. */
       width = d->prop_em_width * 8;
-#if DEBUGGING
-      printf ("warning: table column %d contains no data.\n", c);
-#endif
     }
-  
+
   {
     const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
-    
+
     if (width > clamp)
       width = clamp;
   }
@@ -473,22 +466,20 @@ tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
   int height;
 
   assert (t != NULL && r >= 0 && r < t->nr);
-  
+
   {
     int c;
-    
+
     for (height = d->font_height, c = 0; c < t->nc; c++)
       {
        struct outp_text text;
        unsigned char opt = t->ct[c + r * t->cf];
         int h;
 
-       assert (t->w[c] != NOT_INT);
        if (opt & (TAB_JOIN | TAB_EMPTY))
          continue;
 
        text.string = t->cc[c + r * t->cf];
-       assert (!ls_null_p (&text.string));
         text.justification = OUTP_LEFT;
         text.font = options_to_font (opt);
        text.h = t->w[c];
@@ -506,15 +497,16 @@ tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
 /* Callback function to set all columns and rows to their natural
    dimensions.  Not really meant to be called directly.  */
 void
-tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
+tab_natural_dimensions (struct tab_table *t, struct outp_driver *d,
+                        void *aux UNUSED)
 {
   int i;
 
   assert (t != NULL);
-  
+
   for (i = 0; i < t->nc; i++)
     t->w[i] = tab_natural_width (t, d, i);
-  
+
   for (i = 0; i < t->nr; i++)
     t->h[i] = tab_natural_height (t, d, i);
 }
@@ -545,40 +537,38 @@ tab_value (struct tab_table *table, int c, int r, unsigned char opt,
     }
 #endif
 
-  contents = pool_alloc (table->container, f->w);
-  ls_init (&table->cc[c + r * table->cf], contents, f->w);
+  contents = data_out_pool (v, f, table->container);
+
+  table->cc[c + r * table->cf] = ss_cstr (contents);
   table->ct[c + r * table->cf] = opt;
-  
-  data_out (contents, f, v);
 }
 
 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
    with NDEC decimal places. */
 void
-tab_float (struct tab_table *table, int c, int r, unsigned char opt,
+tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
           double val, int w, int d)
 {
-  char *contents;
-  char buf[40], *cp;
-  
+  char *s, *cp;
+
   struct fmt_spec f;
   union value double_value;
 
   assert (table != NULL && w <= 40);
-  
+
   assert (c >= 0);
   assert (c < table->nc);
   assert (r >= 0);
   assert (r < table->nr);
 
-  f = make_output_format (FMT_F, w, d);
-  
+  f = fmt_for_output (FMT_F, w, d);
+
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
       || c + table->col_ofs >= table->nc
       || r + table->row_ofs >= table->nr)
     {
-      printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
+      printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
              "(%d,%d)\n",
              c, table->col_ofs, c + table->col_ofs,
              r, table->row_ofs, r + table->row_ofs,
@@ -588,19 +578,71 @@ tab_float (struct tab_table *table, int c, int r, unsigned char opt,
 #endif
 
   double_value.f = val;
-  data_out (buf, &f, &double_value);
+  s = data_out_pool (&double_value, &f, table->container);
 
-  cp = buf;
-  while (isspace ((unsigned char) *cp) && cp < &buf[w])
+  cp = s;
+  while (isspace ((unsigned char) *cp) && cp < &s[w])
     cp++;
-  f.w = w - (cp - buf);
+  f.w = w - (cp - s);
 
-  contents = pool_alloc (table->container, f.w);
-  ls_init (&table->cc[c + r * table->cf], contents, f.w);
+  table->cc[c + r * table->cf] = ss_buffer (cp, f.w);
   table->ct[c + r * table->cf] = opt;
-  memcpy (contents, cp, f.w);
 }
 
+/* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
+   formatted by FMT.
+   If FMT is null, then the default print format will be used.
+*/
+void
+tab_double (struct tab_table *table, int c, int r, unsigned char opt,
+          double val, const struct fmt_spec *fmt)
+{
+  int w;
+  char *s, *cp;
+
+  union value double_value ;
+
+  assert (table != NULL);
+
+  assert (c >= 0);
+  assert (c < table->nc);
+  assert (r >= 0);
+  assert (r < table->nr);
+
+  if ( fmt == NULL)
+    fmt = settings_get_format ();
+
+  fmt_check_output (fmt);
+
+#if DEBUGGING
+  if (c + table->col_ofs < 0 || r + table->row_ofs < 0
+      || c + table->col_ofs >= table->nc
+      || r + table->row_ofs >= table->nr)
+    {
+      printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
+             "(%d,%d)\n",
+             c, table->col_ofs, c + table->col_ofs,
+             r, table->row_ofs, r + table->row_ofs,
+             table->nc, table->nr);
+      return;
+    }
+#endif
+
+  double_value.f = val;
+  s = data_out_pool (&double_value, fmt, table->container);
+
+  cp = s;
+  while (isspace ((unsigned char) *cp) && cp < s + fmt->w)
+    {
+      cp++;
+    }
+  w = fmt->w - (cp - s);
+  
+  table->cc[c + r * table->cf] = ss_buffer (cp, w);
+  table->ct[c + r * table->cf] = opt;
+}
+
+
 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
    TEXT. */
 void
@@ -614,7 +656,7 @@ tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text,
   assert (r >= 0 );
   assert (c < table->nc);
   assert (r < table->nr);
-  
+
 
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
@@ -631,7 +673,7 @@ tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text,
 #endif
 
   va_start (args, text);
-  text_format (table, opt, text, args, &table->cc[c + r * table->cf]);
+  table->cc[c + r * table->cf] = text_format (table, opt, text, args);
   table->ct[c + r * table->cf] = opt;
   va_end (args);
 }
@@ -671,41 +713,41 @@ tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
 #endif
 
   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
-  
+
   j = pool_alloc (table->container, sizeof *j);
   j->hit = 0;
   j->x1 = x1 + table->col_ofs;
   j->y1 = y1 + table->row_ofs;
   j->x2 = ++x2 + table->col_ofs;
   j->y2 = ++y2 + table->row_ofs;
-  
+
   {
     va_list args;
-    
+
     va_start (args, text);
-    text_format (table, opt, text, args, &j->contents);
+    j->contents = text_format (table, opt, text, args);
     va_end (args);
   }
-  
+
   opt |= TAB_JOIN;
-  
+
   {
-    struct fixed_string *cc = &table->cc[x1 + y1 * table->cf];
+    struct substring *cc = &table->cc[x1 + y1 * table->cf];
     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
     const int ofs = table->cf - (x2 - x1);
 
     int y;
-    
+
     for (y = y1; y < y2; y++)
       {
        int x;
-       
+
        for (x = x1; x < x2; x++)
          {
-           ls_init (cc++, (char *) j, 0);
+           *cc++ = ss_buffer ((char *) j, 0);
            *ct++ = opt;
          }
-       
+
        cc += ofs;
        ct += ofs;
       }
@@ -715,16 +757,16 @@ tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
 void
 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
-        struct fixed_string *string)
+        struct substring *string)
 {
   assert (table != NULL && string != NULL);
-  
+
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
       || c + table->col_ofs >= table->nc
       || r + table->row_ofs >= table->nr)
     {
-      printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
+      printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
              "(%d,%d)\n",
              c, table->col_ofs, c + table->col_ofs,
              r, table->row_ofs, r + table->row_ofs,
@@ -742,7 +784,7 @@ tab_raw (struct tab_table *table, int c, int r, unsigned opt,
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-nowrap_dim (struct tab_table *t, struct outp_driver *d)
+nowrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
 {
   t->w[0] = tab_natural_width (t, d, 0);
   t->h[0] = d->font_height;
@@ -751,7 +793,7 @@ nowrap_dim (struct tab_table *t, struct outp_driver *d)
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-wrap_dim (struct tab_table *t, struct outp_driver *d)
+wrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
 {
   t->w[0] = tab_natural_width (t, d, 0);
   t->h[0] = tab_natural_height (t, d, 0);
@@ -769,17 +811,17 @@ tab_output_text (int options, const char *buf, ...)
   if (options & TAT_PRINTF)
     {
       va_list args;
-      
+
       va_start (args, buf);
       buf = tmp_buf = xvasprintf (buf, args);
       va_end (args);
     }
-  
+
   tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
-  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
+  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
   tab_submit (t);
-  
+
   free (tmp_buf);
 }
 
@@ -816,15 +858,15 @@ tab_offset (struct tab_table *t, int col, int row)
 
   assert (t != NULL);
 #if DEBUGGING
-  if (row < -1 || row >= t->nr)
+  if (row < -1 || row > t->nr)
     {
       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
-      abort ();
+      NOT_REACHED ();
     }
-  if (col < -1 || col >= t->nc)
+  if (col < -1 || col > t->nc)
     {
       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
-      abort ();
+      NOT_REACHED ();
     }
 #endif
 
@@ -851,7 +893,7 @@ tab_next_row (struct tab_table *t)
 \f
 static struct tab_table *t;
 static struct outp_driver *d;
-int tab_hit;
+static int tab_hit;
 
 /* Set the current table to TABLE. */
 static void
@@ -862,18 +904,20 @@ tabi_table (struct som_entity *table)
 
   t = table->ext;
   tab_offset (t, 0, 0);
-  
+
   assert (t->w == NULL && t->h == NULL);
   t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
   t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
+  t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
+  t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
 }
 
 /* Returns the line style to use for spacing purposes for a rule
    of the given TYPE. */
 static enum outp_line_style
-rule_to_spacing_type (unsigned char type) 
+rule_to_spacing_type (unsigned char type)
 {
-  switch (type) 
+  switch (type)
     {
     case TAL_0:
       return OUTP_L_NONE;
@@ -883,7 +927,7 @@ rule_to_spacing_type (unsigned char type)
     case TAL_2:
       return OUTP_L_DOUBLE;
     default:
-      abort ();
+      NOT_REACHED ();
     }
 }
 
@@ -893,28 +937,28 @@ tabi_driver (struct outp_driver *driver)
 {
   int c, r;
   int i;
-  
+
   assert (driver != NULL);
   d = driver;
-  
+
   /* Figure out sizes of rules. */
-  for (r = 0; r <= t->nr; r++) 
+  for (r = 0; r <= t->nr; r++)
     {
       int width = 0;
-      for (c = 0; c < t->nc; c++) 
+      for (c = 0; c < t->nc; c++)
         {
           unsigned char rh = t->rh[c + r * t->cf];
           int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
           if (w > width)
-            width = w; 
+            width = w;
         }
-      t->hrh[r] = width; 
+      t->hrh[r] = width;
     }
 
-  for (c = 0; c <= t->nc; c++) 
+  for (c = 0; c <= t->nc; c++)
     {
       int width = 0;
-      for (r = 0; r < t->nr; r++) 
+      for (r = 0; r < t->nr; r++)
         {
           unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
           int w;
@@ -924,7 +968,7 @@ tabi_driver (struct outp_driver *driver)
           if (w > width)
             width = w;
         }
-      t->wrv[c] = width; 
+      t->wrv[c] = width;
     }
 
 #if DEBUGGING
@@ -935,7 +979,7 @@ tabi_driver (struct outp_driver *driver)
 #endif
 
   assert (t->dim != NULL);
-  t->dim (t, d);
+  t->dim (t, d, t->dim_aux);
 
 #if DEBUGGING
   {
@@ -950,7 +994,7 @@ tabi_driver (struct outp_driver *driver)
          }
        assert (t->h[i] > 0);
       }
-    
+
     for (i = 0; i < t->nc; i++)
       {
        if (t->w[i] == -1)
@@ -962,7 +1006,7 @@ tabi_driver (struct outp_driver *driver)
       }
   }
 #endif
-    
+
   /* Add up header sizes. */
   for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
     t->wl += t->w[i] + t->wrv[i + 1];
@@ -972,7 +1016,7 @@ tabi_driver (struct outp_driver *driver)
     t->wr += t->w[i] + t->wrv[i + 1];
   for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
     t->hb += t->h[i] + t->hrh[i + 1];
-  
+
   /* Title. */
   if (!(t->flags & SOMF_NO_TITLE))
     t->ht += d->font_height;
@@ -997,16 +1041,16 @@ static void
 tabi_area (int *horiz, int *vert)
 {
   assert (horiz != NULL && vert != NULL);
-  
+
   {
     int w, c;
-    
+
     for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
         c < t->nc - t->r; c++)
       w += t->w[c] + t->wrv[c];
     *horiz = w;
   }
-  
+
   {
     int h, r;
     for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
@@ -1050,7 +1094,7 @@ tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
   int *d;
   int *r;
   int total;
-  
+
   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
   if (cumtype == SOM_ROWS)
     {
@@ -1068,7 +1112,7 @@ tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
       r = &t->wrv[start + 1];
       total = t->wl + t->wr;
     }
-  
+
   total += *d++;
   if (total > max)
     {
@@ -1078,14 +1122,14 @@ tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
        *actual = 0;
       return;
     }
-    
+
   {
     int x;
-      
+
     for (x = start + 1; x < n; x++)
       {
        int amt = *d++ + *r++;
-       
+
        total += amt;
        if (total > max)
          {
@@ -1096,7 +1140,7 @@ tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
 
     if (end)
       *end = x;
-    
+
     if (actual)
       *actual = total;
   }
@@ -1113,7 +1157,7 @@ tabi_flags (unsigned *flags)
 /* Returns true if the table will fit in the given page WIDTH,
    false otherwise. */
 static bool
-tabi_fits_width (int width) 
+tabi_fits_width (int width)
 {
   int i;
 
@@ -1127,7 +1171,7 @@ tabi_fits_width (int width)
 /* Returns true if the table will fit in the given page LENGTH,
    false otherwise. */
 static bool
-tabi_fits_length (int length) 
+tabi_fits_length (int length)
 {
   int i;
 
@@ -1160,7 +1204,7 @@ tabi_title (int x, int y)
 
   if (t->flags & SOMF_NO_TITLE)
     return;
-  
+
   cp = spprintf (buf, "%d.%d", table_num, subtable_num);
   if (x && y)
     cp = spprintf (cp, "(%d:%d)", x, y);
@@ -1169,19 +1213,20 @@ tabi_title (int x, int y)
   if (command_name != NULL)
     cp = spprintf (cp, " %s", command_name);
   cp = stpcpy (cp, ".  ");
-  if (!ls_empty_p (&t->title))
+  if (t->title != NULL)
     {
-      memcpy (cp, ls_c_str (&t->title), ls_length (&t->title));
-      cp += ls_length (&t->title);
+      size_t length = strlen (t->title);
+      memcpy (cp, t->title, length);
+      cp += length;
     }
   *cp = 0;
-  
+
   {
     struct outp_text text;
 
     text.font = OUTP_PROPORTIONAL;
     text.justification = OUTP_LEFT;
-    ls_init (&text.string, buf, cp - buf);
+    text.string = ss_buffer (buf, cp - buf);
     text.h = d->width;
     text.v = d->font_height;
     text.x = 0;
@@ -1201,13 +1246,13 @@ static int
 render_rows (int y, int c0, int c1, int r0, int r1)
 {
   int r;
-  for (r = r0; r < r1; r++) 
+  for (r = r0; r < r1; r++)
     {
       int x = d->cp_x;
       x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
       x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
       x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
-      y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2]; 
+      y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
     }
   return y;
 }
@@ -1218,7 +1263,7 @@ static void
 tabi_render (int c0, int r0, int c1, int r1)
 {
   int y;
-  
+
   tab_hit++;
 
   y = d->cp_y;
@@ -1230,11 +1275,11 @@ tabi_render (int c0, int r0, int c1, int r1)
   y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
 }
 
-struct som_table_class tab_table_class =
+const struct som_table_class tab_table_class =
   {
     tabi_table,
     tabi_driver,
-    
+
     tabi_count,
     tabi_area,
     NULL,
@@ -1247,7 +1292,7 @@ struct som_table_class tab_table_class =
     tabi_flags,
     tabi_fits_width,
     tabi_fits_length,
-    
+
     NULL,
     NULL,
     tabi_set_headers,
@@ -1259,7 +1304,7 @@ struct som_table_class tab_table_class =
 static enum outp_justification
 translate_justification (unsigned int opt)
 {
-  switch (opt & TAB_ALIGN_MASK) 
+  switch (opt & TAB_ALIGN_MASK)
     {
     case TAB_RIGHT:
       return OUTP_RIGHT;
@@ -1268,16 +1313,16 @@ translate_justification (unsigned int opt)
     case TAB_CENTER:
       return OUTP_CENTER;
     default:
-      abort ();
+      NOT_REACHED ();
     }
 }
 
 /* Returns the line style to use for drawing a rule of the given
    TYPE. */
 static enum outp_line_style
-rule_to_draw_type (unsigned char type) 
+rule_to_draw_type (unsigned char type)
 {
-  switch (type) 
+  switch (type)
     {
     case TAL_0:
     case TAL_GAP:
@@ -1287,20 +1332,20 @@ rule_to_draw_type (unsigned char type)
     case TAL_2:
       return OUTP_L_DOUBLE;
     default:
-      abort ();
+      NOT_REACHED ();
     }
 }
 
 /* Returns the horizontal rule at the given column and row. */
 static int
-get_hrule (int c, int r) 
+get_hrule (int c, int r)
 {
   return t->rh[c + r * t->cf];
 }
 
 /* Returns the vertical rule at the given column and row. */
 static int
-get_vrule (int c, int r) 
+get_vrule (int c, int r)
 {
   return t->rv[c + r * (t->cf + 1)];
 }
@@ -1363,7 +1408,7 @@ strip_width (int c1, int c2)
   int width = 0;
   int c;
 
-  for (c = c1; c < c2; c++) 
+  for (c = c1; c < c2; c++)
     width += t->w[c] + t->wrv[c + 1];
   if (c1 < c2)
     width -= t->wrv[c2];
@@ -1378,7 +1423,7 @@ strip_height (int r1, int r2)
   int height = 0;
   int r;
 
-  for (r = r1; r < r2; r++) 
+  for (r = r1; r < r2; r++)
     height += t->h[r] + t->hrh[r + 1];
   if (r1 < r2)
     height -= t->hrh[r2];
@@ -1393,8 +1438,8 @@ render_cell (int x, int y, int c, int r, int c1, int r1)
 {
   const int index = c + (r * t->cf);
   unsigned char type = t->ct[index];
-  struct fixed_string *content = &t->cc[index];
-  
+  struct substring *content = &t->cc[index];
+
   if (!(type & TAB_JOIN))
     {
       if (!(type & TAB_EMPTY))
@@ -1413,7 +1458,7 @@ render_cell (int x, int y, int c, int r, int c1, int r1)
   else
     {
       struct tab_joined_cell *j
-        = (struct tab_joined_cell *) ls_c_str (content);
+        = (struct tab_joined_cell *) ss_data (*content);
 
       if (j->hit != tab_hit)
         {
@@ -1447,7 +1492,7 @@ render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
   int c;
 
   for (c = c0; c < c1; c++)
-    if (c & 1) 
+    if (c & 1)
       {
         if (r & 1)
           render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
@@ -1463,14 +1508,14 @@ render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
           render_rule_intersection (x, y, c / 2, r / 2);
         x += t->wrv[c / 2];
       }
-  
+
   return x;
 }
 
 /* Sets COMMAND_NAME as the name of the current command,
    for embedding in output. */
 void
-tab_set_command_name (const char *command_name_) 
+tab_set_command_name (const char *command_name_)
 {
   free (command_name);
   command_name = command_name_ ? xstrdup (command_name_) : NULL;