Actually implement the new procedure code and adapt all of its clients
[pspp-builds.git] / src / language / stats / crosstabs.q
index d7828bd5a19b2bdce28eb4935b809832d4cdc73e..1d2bdf74b4f0f6e7508b067dcf70e8e36100a5b5 100644 (file)
@@ -1,6 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
+   Copyright (C) 1997-9, 2000, 2006 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
 #include <stdio.h>
 
 #include <data/case.h>
+#include <data/casegrouper.h>
+#include <data/casereader.h>
+#include <data/data-out.h>
 #include <data/dictionary.h>
+#include <data/format.h>
 #include <data/procedure.h>
 #include <data/value-labels.h>
 #include <data/variable.h>
 #include <language/command.h>
 #include <language/dictionary/split-file.h>
 #include <language/lexer/lexer.h>
+#include <language/lexer/variable-parser.h>
 #include <libpspp/alloc.h>
 #include <libpspp/array.h>
+#include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
 #include <libpspp/hash.h>
 #include <libpspp/magic.h>
@@ -57,6 +62,8 @@
 #include <output/output.h>
 #include <output/table.h>
 
+#include "minmax.h"
+
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
@@ -67,7 +74,7 @@
    crosstabs (crs_):
      *^tables=custom;
      +variables=custom;
-     +missing=miss:!table/include/report;
+     missing=miss:!table/include/report;
      +write[wr_]=none,cells,all;
      +format=fmt:!labels/nolabels/novallabs,
             val:!avalue/dvalue,
@@ -111,7 +118,7 @@ struct crosstab
     int nvar;                  /* Number of variables. */
     double missing;            /* Missing cases count. */
     int ofs;                   /* Integer mode: Offset into sorted_tab[]. */
-    struct variable *vars[2];  /* At least two variables; sorted by
+    const struct variable *vars[2];    /* At least two variables; sorted by
                                   larger indices first. */
   };
 
@@ -124,11 +131,9 @@ struct var_range
   };
 
 static inline struct var_range *
-get_var_range (struct variable *v) 
+get_var_range (const struct variable *v) 
 {
-  assert (v != NULL);
-  assert (v->aux != NULL);
-  return v->aux;
+  return var_get_aux (v);
 }
 
 /* Indexes into crosstab.v. */
@@ -144,7 +149,7 @@ static int n_sorted_tab;            /* Number of entries in sorted_tab. */
 static struct table_entry **sorted_tab;        /* Sorted table. */
 
 /* Variables specifies on VARIABLES. */
-static struct variable **variables;
+static const struct variable **variables;
 static size_t variables_cnt;
 
 /* TABLES. */
@@ -164,7 +169,7 @@ static int num_cells;               /* Number of cells requested. */
 static int cells[8];           /* Cells requested. */
 
 /* WRITE. */
-static int write;              /* One of WR_* that specifies the WRITE style. */
+static int write_style;                /* One of WR_* that specifies the WRITE style. */
 
 /* Command parsing info. */
 static struct cmd_crosstabs cmd;
@@ -173,11 +178,11 @@ static struct cmd_crosstabs cmd;
 static struct pool *pl_tc;     /* For table cells. */
 static struct pool *pl_col;    /* For column data. */
 
-static int internal_cmd_crosstabs (void);
-static void precalc (const struct ccase *, void *);
-static bool calc_general (const struct ccase *, void *);
-static bool calc_integer (const struct ccase *, void *);
-static void postcalc (void *);
+static int internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds);
+static void precalc (struct casereader *, const struct dataset *);
+static void calc_general (struct ccase *, const struct dataset *);
+static void calc_integer (struct ccase *, const struct dataset *);
+static void postcalc (void);
 static void submit (struct tab_table *);
 
 static void format_short (char *s, const struct fmt_spec *fp,
@@ -185,9 +190,9 @@ static void format_short (char *s, const struct fmt_spec *fp,
 
 /* Parse and execute CROSSTABS, then clean up. */
 int
-cmd_crosstabs (void)
+cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
 {
-  int result = internal_cmd_crosstabs ();
+  int result = internal_cmd_crosstabs (lexer, ds);
 
   free (variables);
   pool_destroy (pl_tc);
@@ -198,10 +203,12 @@ cmd_crosstabs (void)
 
 /* Parses and executes the CROSSTABS procedure. */
 static int
-internal_cmd_crosstabs (void)
+internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
 {
-  int i;
+  struct casegrouper *grouper;
+  struct casereader *input, *group;
   bool ok;
+  int i;
 
   variables = NULL;
   variables_cnt = 0;
@@ -210,7 +217,7 @@ internal_cmd_crosstabs (void)
   pl_tc = pool_create ();
   pl_col = pool_create ();
 
-  if (!parse_crosstabs (&cmd))
+  if (!parse_crosstabs (lexer, ds, &cmd, NULL))
     return CMD_FAILURE;
 
   mode = variables ? INTEGER : GENERAL;
@@ -285,48 +292,68 @@ internal_cmd_crosstabs (void)
          + cmd.a_write[CRS_WR_CELLS] == 0))
     cmd.a_write[CRS_WR_CELLS] = 1;
   if (cmd.a_write[CRS_WR_CELLS])
-    write = CRS_WR_CELLS;
+    write_style = CRS_WR_CELLS;
   else if (cmd.a_write[CRS_WR_ALL])
-    write = CRS_WR_ALL;
+    write_style = CRS_WR_ALL;
   else
-    write = CRS_WR_NONE;
+    write_style = CRS_WR_NONE;
 
-  ok = procedure_with_splits (precalc,
-                              mode == GENERAL ? calc_general : calc_integer,
-                              postcalc, NULL);
+  input = casereader_create_filter_weight (proc_open (ds), dataset_dict (ds),
+                                           NULL, NULL);
+  grouper = casegrouper_create_splits (input, dataset_dict (ds));
+  while (casegrouper_get_next_group (grouper, &group)) 
+    {
+      struct ccase c;
+      
+      precalc (group, ds);
+      
+      for (; casereader_read (group, &c); case_destroy (&c)) 
+        {
+          if (mode == GENERAL)
+            calc_general (&c, ds);
+          else
+            calc_integer (&c, ds); 
+        }
+      casereader_destroy (group);
+
+      postcalc ();
+    }
+  ok = casegrouper_destroy (grouper);
+  ok = proc_commit (ds) && ok;
 
   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
 }
 
 /* Parses the TABLES subcommand. */
 static int
-crs_custom_tables (struct cmd_crosstabs *cmd UNUSED)
+crs_custom_tables (struct lexer *lexer, struct dataset *ds, struct cmd_crosstabs *cmd UNUSED, void *aux UNUSED)
 {
-  struct var_set *var_set;
+  struct const_var_set *var_set;
   int n_by;
-  struct variable ***by = NULL;
+  const struct variable ***by = NULL;
   size_t *by_nvar = NULL;
   size_t nx = 1;
   int success = 0;
 
   /* Ensure that this is a TABLES subcommand. */
-  if (!lex_match_id ("TABLES")
-      && (token != T_ID || dict_lookup_var (default_dict, tokid) == NULL)
-      && token != T_ALL)
+  if (!lex_match_id (lexer, "TABLES")
+      && (lex_token (lexer) != T_ID || 
+         dict_lookup_var (dataset_dict (ds), lex_tokid (lexer)) == NULL)
+      && lex_token (lexer) != T_ALL)
     return 2;
-  lex_match ('=');
+  lex_match (lexer, '=');
 
   if (variables != NULL)
-    var_set = var_set_create_from_array (variables, variables_cnt);
+    var_set = const_var_set_create_from_array (variables, variables_cnt);
   else
-    var_set = var_set_create_from_dict (default_dict);
+    var_set = const_var_set_create_from_dict (dataset_dict (ds));
   assert (var_set != NULL);
   
   for (n_by = 0; ;)
     {
       by = xnrealloc (by, n_by + 1, sizeof *by);
       by_nvar = xnrealloc (by_nvar, n_by + 1, sizeof *by_nvar);
-      if (!parse_var_set_vars (var_set, &by[n_by], &by_nvar[n_by],
+      if (!parse_const_var_set_vars (lexer, var_set, &by[n_by], &by_nvar[n_by],
                                PV_NO_DUPLICATE | PV_NO_SCRATCH))
        goto done;
       if (xalloc_oversized (nx, by_nvar[n_by])) 
@@ -337,11 +364,11 @@ crs_custom_tables (struct cmd_crosstabs *cmd UNUSED)
       nx *= by_nvar[n_by];
       n_by++;
 
-      if (!lex_match (T_BY))
+      if (!lex_match (lexer, T_BY))
        {
          if (n_by < 2)
            {
-             lex_error (_("expecting BY"));
+             lex_error (lexer, _("expecting BY"));
              goto done;
            }
          else 
@@ -397,14 +424,14 @@ crs_custom_tables (struct cmd_crosstabs *cmd UNUSED)
     free (by_nvar);
   }
 
-  var_set_destroy (var_set);
+  const_var_set_destroy (var_set);
 
   return success;
 }
 
 /* Parses the VARIABLES subcommand. */
 static int
-crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
+crs_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_crosstabs *cmd UNUSED, void *aux UNUSED)
 {
   if (nxtab)
     {
@@ -412,7 +439,7 @@ crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
       return 0;
     }
 
-  lex_match ('=');
+  lex_match (lexer, '=');
   
   for (;;)
     {
@@ -421,42 +448,43 @@ crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
 
       long min, max;
       
-      if (!parse_variables (default_dict, &variables, &variables_cnt,
+      if (!parse_variables_const (lexer, dataset_dict (ds), 
+                           &variables, &variables_cnt,
                            (PV_APPEND | PV_NUMERIC
                             | PV_NO_DUPLICATE | PV_NO_SCRATCH)))
        return 0;
 
-      if (token != '(')
+      if (lex_token (lexer) != '(')
        {
-         lex_error ("expecting `('");
+         lex_error (lexer, "expecting `('");
          goto lossage;
        }
-      lex_get ();
+      lex_get (lexer);
 
-      if (!lex_force_int ())
+      if (!lex_force_int (lexer))
        goto lossage;
-      min = lex_integer ();
-      lex_get ();
+      min = lex_integer (lexer);
+      lex_get (lexer);
 
-      lex_match (',');
+      lex_match (lexer, ',');
 
-      if (!lex_force_int ())
+      if (!lex_force_int (lexer))
        goto lossage;
-      max = lex_integer ();
+      max = lex_integer (lexer);
       if (max < min)
        {
          msg (SE, _("Maximum value (%ld) less than minimum value (%ld)."),
               max, min);
          goto lossage;
        }
-      lex_get ();
+      lex_get (lexer);
 
-      if (token != ')')
+      if (lex_token (lexer) != ')')
        {
-         lex_error ("expecting `)'");
+         lex_error (lexer, "expecting `)'");
          goto lossage;
        }
-      lex_get ();
+      lex_get (lexer);
       
       for (i = orig_nv; i < variables_cnt; i++) 
         {
@@ -467,7 +495,7 @@ crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
           var_attach_aux (variables[i], vr, var_dtor_free);
        }
       
-      if (token == '/')
+      if (lex_token (lexer) == '/')
        break;
     }
   
@@ -481,14 +509,20 @@ crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
 \f
 /* Data file processing. */
 
-static int compare_table_entry (const void *, const void *, void *);
-static unsigned hash_table_entry (const void *, void *);
+static int compare_table_entry (const void *, const void *, const void *);
+static unsigned hash_table_entry (const void *, const void *);
 
 /* Set up the crosstabulation tables for processing. */
 static void
-precalc (const struct ccase *first, void *aux UNUSED)
+precalc (struct casereader *input, const struct dataset *ds)
 {
-  output_split_file_values (first);
+  struct ccase c;
+
+  if (!casereader_peek (input, 0, &c))
+    return;
+  output_split_file_values (ds, &c);
+  case_destroy (&c);
+
   if (mode == GENERAL)
     {
       gen_tab = hsh_create (512, compare_table_entry, hash_table_entry,
@@ -556,16 +590,20 @@ precalc (const struct ccase *first, void *aux UNUSED)
                               n_sorted_tab + 1, sizeof *sorted_tab);
       sorted_tab[n_sorted_tab] = NULL;
     }
+
 }
 
 /* Form crosstabulations for general mode. */
-static bool
-calc_general (const struct ccase *c, void *aux UNUSED)
+static void
+calc_general (struct ccase *c, const struct dataset *ds)
 {
-  int bad_warn = 1;
+  /* Missing values to exclude. */
+  enum mv_class exclude = (cmd.miss == CRS_TABLE ? MV_ANY
+                           : cmd.miss == CRS_INCLUDE ? MV_SYSTEM
+                           : MV_NEVER);
 
   /* Case weight. */
-  double weight = dict_get_case_weight (default_dict, c, &bad_warn);
+  double weight = dict_get_case_weight (dataset_dict (ds), c, NULL);
 
   /* Flattened current table index. */
   int t;
@@ -585,26 +623,23 @@ calc_general (const struct ccase *c, void *aux UNUSED)
        assert (x != NULL);
        for (j = 0; j < x->nvar; j++)
          {
-            const union value *v = case_data (c, x->vars[j]->fv);
-            const struct missing_values *mv = &x->vars[j]->miss;
-           if ((cmd.miss == CRS_TABLE && mv_is_value_missing (mv, v))
-               || (cmd.miss == CRS_INCLUDE
-                   && mv_is_value_system_missing (mv, v)))
+            const union value *v = case_data (c, x->vars[j]);
+            if (var_is_value_missing (x->vars[j], v, exclude))
              {
                x->missing += weight;
                goto next_crosstab;
              }
              
-           if (x->vars[j]->type == NUMERIC)
-             te->values[j].f = case_num (c, x->vars[j]->fv);
+           if (var_is_numeric (x->vars[j]))
+             te->values[j].f = case_num (c, x->vars[j]);
            else
              {
-               memcpy (te->values[j].s, case_str (c, x->vars[j]->fv),
-                        x->vars[j]->width);
+               memcpy (te->values[j].s, case_str (c, x->vars[j]),
+                        var_get_width (x->vars[j]));
              
                /* Necessary in order to simplify comparisons. */
-               memset (&te->values[j].s[x->vars[j]->width], 0,
-                       sizeof (union value) - x->vars[j]->width);
+               memset (&te->values[j].s[var_get_width (x->vars[j])], 0,
+                       sizeof (union value) - var_get_width (x->vars[j]));
              }
          }
       }
@@ -629,17 +664,15 @@ calc_general (const struct ccase *c, void *aux UNUSED)
     next_crosstab:
       local_free (te);
     }
-  
-  return true;
 }
 
-static bool
-calc_integer (const struct ccase *c, void *aux UNUSED)
+static void
+calc_integer (struct ccase *c, const struct dataset *ds)
 {
-  int bad_warn = 1;
+  bool bad_warn = true;
 
   /* Case weight. */
-  double weight = dict_get_case_weight (default_dict, c, &bad_warn);
+  double weight = dict_get_case_weight (dataset_dict (ds), c, &bad_warn);
   
   /* Flattened current table index. */
   int t;
@@ -653,14 +686,14 @@ calc_integer (const struct ccase *c, void *aux UNUSED)
       ofs = x->ofs;
       for (i = 0; i < x->nvar; i++)
        {
-         struct variable *const v = x->vars[i];
+         const struct variable *const v = x->vars[i];
           struct var_range *vr = get_var_range (v);
-         double value = case_num (c, v->fv);
+         double value = case_num (c, v);
          
          /* Note that the first test also rules out SYSMIS. */
          if ((value < vr->min || value >= vr->max)
              || (cmd.miss == CRS_TABLE
-                  && mv_is_num_user_missing (&v->miss, value)))
+                  && var_is_num_missing (v, value, MV_USER)))
            {
              x->missing += weight;
              goto next_crosstab;
@@ -674,11 +707,11 @@ calc_integer (const struct ccase *c, void *aux UNUSED)
        }
       
       {
-        struct variable *row_var = x->vars[ROW_VAR];
-       const int row = case_num (c, row_var->fv) - get_var_range (row_var)->min;
+        const struct variable *row_var = x->vars[ROW_VAR];
+       const int row = case_num (c, row_var) - get_var_range (row_var)->min;
 
-        struct variable *col_var = x->vars[COL_VAR];
-       const int col = case_num (c, col_var->fv) - get_var_range (col_var)->min;
+        const struct variable *col_var = x->vars[COL_VAR];
+       const int col = case_num (c, col_var) - get_var_range (col_var)->min;
 
        const int col_dim = get_var_range (col_var)->count;
 
@@ -687,14 +720,12 @@ calc_integer (const struct ccase *c, void *aux UNUSED)
       
     next_crosstab: ;
     }
-  
-  return true;
 }
 
 /* Compare the table_entry's at A and B and return a strcmp()-type
    result. */
 static int 
-compare_table_entry (const void *a_, const void *b_, void *foo UNUSED)
+compare_table_entry (const void *a_, const void *b_, const void *aux UNUSED)
 {
   const struct table_entry *a = a_;
   const struct table_entry *b = b_;
@@ -709,7 +740,7 @@ compare_table_entry (const void *a_, const void *b_, void *foo UNUSED)
     int i;
 
     for (i = x->nvar - 1; i >= 0; i--)
-      if (x->vars[i]->type == NUMERIC)
+      if (var_is_numeric (x->vars[i]))
        {
          const double diffnum = a->values[i].f - b->values[i].f;
          if (diffnum < 0)
@@ -718,15 +749,12 @@ compare_table_entry (const void *a_, const void *b_, void *foo UNUSED)
            return 1;
        }
       else 
-       {
-         assert (x->vars[i]->type == ALPHA);
-         {
-           const int diffstr = strncmp (a->values[i].s, b->values[i].s,
-                                         x->vars[i]->width);
-           if (diffstr)
-             return diffstr;
-         }
-       }
+        {
+          const int diffstr = strncmp (a->values[i].s, b->values[i].s,
+                                       var_get_width (x->vars[i]));
+          if (diffstr)
+            return diffstr;
+        }
   }
   
   return 0;
@@ -734,7 +762,7 @@ compare_table_entry (const void *a_, const void *b_, void *foo UNUSED)
 
 /* Calculate a hash value from table_entry A. */
 static unsigned
-hash_table_entry (const void *a_, void *foo UNUSED)
+hash_table_entry (const void *a_, const void *aux UNUSED)
 {
   const struct table_entry *a = a_;
   unsigned long hash;
@@ -760,7 +788,7 @@ static void output_pivot_table (struct table_entry **, struct table_entry **,
 static void make_summary_table (void);
 
 static void
-postcalc (void *aux UNUSED)
+postcalc (void)
 {
   if (mode == GENERAL)
     {
@@ -893,8 +921,7 @@ insert_summary (struct tab_table *t, int tab_index, double valid)
        if (i > 0)
          cp = stpcpy (cp, " * ");
 
-       cp = stpcpy (cp,
-                     x->vars[i]->label ? x->vars[i]->label : x->vars[i]->name);
+       cp = stpcpy (cp, var_to_string (x->vars[i]));
       }
     tab_text (t, 0, 0, TAB_LEFT, buf);
 
@@ -947,7 +974,7 @@ static int n_rows;
 static int ns_cols, ns_rows;
 
 /* Crosstabulation. */
-static struct crosstab *x;
+static const struct crosstab *x;
 
 /* Number of variables from the crosstabulation to consider.  This is
    either x->nvar, if pivoting is on, or 2, if pivoting is off. */
@@ -1001,7 +1028,7 @@ output_pivot_table (struct table_entry **pb, struct table_entry **pe,
 
       /* First header line. */
       tab_joint_text (table, nvar - 1, 0, (nvar - 1) + (n_cols - 1), 0,
-                     TAB_CENTER | TAT_TITLE, x->vars[COL_VAR]->name);
+                     TAB_CENTER | TAT_TITLE, var_get_name (x->vars[COL_VAR]));
   
       tab_hline (table, TAL_1, nvar - 1, nvar + n_cols - 2, 1);
             
@@ -1011,11 +1038,9 @@ output_pivot_table (struct table_entry **pb, struct table_entry **pe,
 
        for (i = 2; i < nvar; i++)
          tab_joint_text (table, nvar - i - 1, 0, nvar - i - 1, 1,
-                         TAB_RIGHT | TAT_TITLE,
-                         (x->vars[i]->label
-                           ? x->vars[i]->label : x->vars[i]->name));
+                         TAB_RIGHT | TAT_TITLE, var_to_string (x->vars[i]));
        tab_text (table, nvar - 2, 1, TAB_RIGHT | TAT_TITLE,
-                 x->vars[ROW_VAR]->name);
+                 var_get_name (x->vars[ROW_VAR]));
        for (i = 0; i < n_cols; i++)
          table_value_missing (table, nvar + i - 1, 1, TAB_RIGHT, &cols[i],
                               x->vars[COL_VAR]);
@@ -1036,12 +1061,13 @@ output_pivot_table (struct table_entry **pb, struct table_entry **pe,
            {
              if (i)
                cp = stpcpy (cp, " by ");
-             cp = stpcpy (cp, x->vars[i]->name);
+             cp = stpcpy (cp, var_get_name (x->vars[i]));
            }
        else
          {
            cp = spprintf (cp, "%s by %s for",
-                           x->vars[0]->name, x->vars[1]->name);
+                           var_get_name (x->vars[0]),
+                           var_get_name (x->vars[1]));
            for (i = 2; i < nvar; i++)
              {
                char buf[64], *bufp;
@@ -1049,9 +1075,10 @@ output_pivot_table (struct table_entry **pb, struct table_entry **pe,
                if (i > 2)
                  *cp++ = ',';
                *cp++ = ' ';
-               cp = stpcpy (cp, x->vars[i]->name);
+               cp = stpcpy (cp, var_get_name (x->vars[i]));
                *cp++ = '=';
-               format_short (buf, &x->vars[i]->print, &(*pb)->values[i]);
+               format_short (buf, var_get_print_format (x->vars[i]),
+                              &(*pb)->values[i]);
                for (bufp = buf; isspace ((unsigned char) *bufp); bufp++)
                  ;
                cp = stpcpy (cp, bufp);
@@ -1215,7 +1242,7 @@ output_pivot_table (struct table_entry **pb, struct table_entry **pe,
       /* Allocate table space for the matrix. */
       if (table && tab_row (table) + (n_rows + 1) * num_cells > tab_nr (table))
        tab_realloc (table, -1,
-                    max (tab_nr (table) + (n_rows + 1) * num_cells,
+                    MAX (tab_nr (table) + (n_rows + 1) * num_cells,
                          tab_nr (table) * (pe - pb) / (te - tb)));
 
       if (mode == GENERAL)
@@ -1420,7 +1447,7 @@ delete_missing (void)
     int r;
 
     for (r = 0; r < n_rows; r++)
-      if (mv_is_num_user_missing (&x->vars[ROW_VAR]->miss, rows[r].f))
+      if (var_is_num_missing (x->vars[ROW_VAR], rows[r].f, MV_USER))
        {
          int c;
 
@@ -1434,7 +1461,7 @@ delete_missing (void)
     int c;
 
     for (c = 0; c < n_cols; c++)
-      if (mv_is_num_user_missing (&x->vars[COL_VAR]->miss, cols[c].f))
+      if (var_is_num_missing (x->vars[COL_VAR], cols[c].f, MV_USER))
        {
          int r;
 
@@ -1464,7 +1491,7 @@ submit (struct tab_table *t)
   if (t != table)
     for (i = 2; i < nvar; i++)
       tab_text (t, nvar - i - 1, 0, TAB_RIGHT | TAT_TITLE,
-               x->vars[i]->label ? x->vars[i]->label : x->vars[i]->name);
+                var_to_string (x->vars[i]));
   tab_box (t, TAL_2, TAL_2, -1, -1, 0, 0, tab_nc (t) - 1, tab_nr (t) - 1);
   tab_box (t, -1, -1, -1, TAL_1, tab_l (t), tab_t (t) - 1, tab_nc (t) - 1,
           tab_nr (t) - 1);
@@ -1605,7 +1632,7 @@ find_pivot_extent_integer (struct table_entry **tp, int *cnt, int pivot)
    result.  WIDTH_ points to an int which is either 0 for a
    numeric value or a string width for a string value. */
 static int
-compare_value (const void *a_, const void *b_, void *width_)
+compare_value (const void *a_, const void *b_, const void *width_)
 {
   const union value *a = a_;
   const union value *b = b_;
@@ -1628,11 +1655,11 @@ static void
 enum_var_values (struct table_entry **entries, int entry_cnt, int var_idx,
                  union value **values, int *value_cnt)
 {
-  struct variable *v = xtab[(*entries)->table]->vars[var_idx];
+  const struct variable *v = xtab[(*entries)->table]->vars[var_idx];
 
   if (mode == GENERAL)
     {
-      int width = v->width;
+      int width = var_get_width (v);
       int i;
 
       *values = xnmalloc (entry_cnt, sizeof **values);
@@ -1661,19 +1688,20 @@ static void
 table_value_missing (struct tab_table *table, int c, int r, unsigned char opt,
                     const union value *v, const struct variable *var)
 {
-  struct fixed_string s;
+  struct substring s;
+  const struct fmt_spec *print = var_get_print_format (var);
 
-  const char *label = val_labs_find (var->val_labs, *v);
+  const char *label = var_lookup_value_label (var, v);
   if (label) 
     {
       tab_text (table, c, r, TAB_LEFT, label);
       return;
     }
 
-  s.string = tab_alloc (table, var->print.w);
-  format_short (s.string, &var->print, v);
+  s.string = tab_alloc (table, print->w);
+  format_short (s.string, print, v);
   s.length = strlen (s.string);
-  if (cmd.miss == CRS_REPORT && mv_is_num_user_missing (&var->miss, v->f))
+  if (cmd.miss == CRS_REPORT && var_is_num_missing (var, v->f, MV_USER))
     s.string[s.length++] = 'M';
   while (s.length && *s.string == ' ')
     {
@@ -1700,20 +1728,20 @@ display_dimensions (struct tab_table *table, int first_difference, struct table_
 }
 
 /* Put VALUE into cell (C,R) of TABLE, suffixed with character
-   SUFFIX if nonzero.  If MARK_MISSING is nonzero the entry is
+   SUFFIX if nonzero.  If MARK_MISSING is true the entry is
    additionally suffixed with a letter `M'. */
 static void
 format_cell_entry (struct tab_table *table, int c, int r, double value,
-                   char suffix, int mark_missing)
+                   char suffix, bool mark_missing)
 {
   const struct fmt_spec f = {FMT_F, 10, 1};
   union value v;
-  struct fixed_string s;
+  struct substring s;
   
   s.length = 10;
   s.string = tab_alloc (table, 16);
   v.f = value;
-  data_out (s.string, &f, &v);
+  data_out (&v, &f, s.string);
   while (*s.string == ' ')
     {
       s.length--;
@@ -1753,13 +1781,13 @@ display_crosstabulation (void)
          tab_hline (table, TAL_1, -1, n_cols, 0);
        for (c = 0; c < n_cols; c++)
          {
-            int mark_missing = 0;
+            bool mark_missing = false;
             double expected_value = row_tot[r] * col_tot[c] / W;
             if (cmd.miss == CRS_REPORT
-                && (mv_is_num_user_missing (&x->vars[COL_VAR]->miss, cols[c].f)
-                    || mv_is_num_user_missing (&x->vars[ROW_VAR]->miss,
-                                               rows[r].f)))
-              mark_missing = 1;
+                && (var_is_num_missing (x->vars[COL_VAR], cols[c].f, MV_USER)
+                    || var_is_num_missing (x->vars[ROW_VAR], rows[r].f,
+                                           MV_USER)))
+              mark_missing = true;
            for (i = 0; i < num_cells; i++)
              {
                double v;
@@ -1798,8 +1826,7 @@ display_crosstabulation (void)
                                 * (1. - col_tot[c] / W)));
                    break;
                  default:
-                   assert (0);
-                    abort ();
+                    NOT_REACHED ();
                  }
 
                 format_cell_entry (table, c, i, v, suffix, mark_missing);
@@ -1820,11 +1847,11 @@ display_crosstabulation (void)
     for (r = 0; r < n_rows; r++) 
       {
         char suffix = 0;
-        int mark_missing = 0;
+        bool mark_missing = false;
 
         if (cmd.miss == CRS_REPORT
-            && mv_is_num_user_missing (&x->vars[ROW_VAR]->miss, rows[r].f))
-          mark_missing = 1;
+            && var_is_num_missing (x->vars[ROW_VAR], rows[r].f, MV_USER))
+          mark_missing = true;
 
         for (i = 0; i < num_cells; i++)
           {
@@ -1854,8 +1881,7 @@ display_crosstabulation (void)
                 v = 0.;
                 break;
               default:
-                assert (0);
-                abort ();
+                NOT_REACHED ();
               }
 
             format_cell_entry (table, n_cols, 0, v, suffix, mark_missing);
@@ -1874,13 +1900,13 @@ display_crosstabulation (void)
     for (c = 0; c <= n_cols; c++)
       {
        double ct = c < n_cols ? col_tot[c] : W;
-        int mark_missing = 0;
+        bool mark_missing = false;
         char suffix = 0;
         int i;
            
         if (cmd.miss == CRS_REPORT && c < n_cols 
-            && mv_is_num_user_missing (&x->vars[COL_VAR]->miss, cols[c].f))
-          mark_missing = 1;
+            && var_is_num_missing (x->vars[COL_VAR], cols[c].f, MV_USER))
+          mark_missing = true;
 
         for (i = 0; i < num_cells; i++)
          {
@@ -1910,8 +1936,7 @@ display_crosstabulation (void)
              case CRS_CL_ASRESIDUAL:
                continue;
              default:
-               assert (0);
-                abort ();
+                NOT_REACHED ();
              }
 
             format_cell_entry (table, c, i, v, suffix, mark_missing);
@@ -2076,24 +2101,24 @@ display_risk (void)
       switch (i)
        {
        case 0:
-         if (x->vars[COL_VAR]->type == NUMERIC)
+         if (var_is_numeric (x->vars[COL_VAR]))
            sprintf (buf, _("Odds Ratio for %s (%g / %g)"),
-                    x->vars[COL_VAR]->name, c[0].f, c[1].f);
+                    var_get_name (x->vars[COL_VAR]), c[0].f, c[1].f);
          else
            sprintf (buf, _("Odds Ratio for %s (%.*s / %.*s)"),
-                    x->vars[COL_VAR]->name,
-                    x->vars[COL_VAR]->width, c[0].s,
-                    x->vars[COL_VAR]->width, c[1].s);
+                    var_get_name (x->vars[COL_VAR]),
+                    var_get_width (x->vars[COL_VAR]), c[0].s,
+                    var_get_width (x->vars[COL_VAR]), c[1].s);
          break;
        case 1:
        case 2:
-         if (x->vars[ROW_VAR]->type == NUMERIC)
+         if (var_is_numeric (x->vars[ROW_VAR]))
            sprintf (buf, _("For cohort %s = %g"),
-                    x->vars[ROW_VAR]->name, rows[i - 1].f);
+                    var_get_name (x->vars[ROW_VAR]), rows[i - 1].f);
          else
            sprintf (buf, _("For cohort %s = %.*s"),
-                    x->vars[ROW_VAR]->name,
-                    x->vars[ROW_VAR]->width, rows[i - 1].s);
+                    var_get_name (x->vars[ROW_VAR]),
+                    var_get_width (x->vars[ROW_VAR]), rows[i - 1].s);
          break;
        }
                   
@@ -2202,15 +2227,15 @@ display_directional (void)
              
              for (; j < 3; j++)
                {
-                 char *string;
+                 const char *string;
                  int k = last[j] = stats_lookup[j][i];
 
                  if (k == 0)
                    string = NULL;
                  else if (k == 1)
-                   string = x->vars[0]->name;
+                   string = var_get_name (x->vars[0]);
                  else
-                   string = x->vars[1]->name;
+                   string = var_get_name (x->vars[1]);
                  
                  tab_text (direct, j, 0, TAB_LEFT | TAT_PRINTF,
                            gettext (stats_names[j][k]), string);
@@ -2273,9 +2298,9 @@ calc_fisher (int a, int b, int c, int d, double *fisher1, double *fisher2)
 {
   int x;
   
-  if (min (c, d) < min (a, b))
+  if (MIN (c, d) < MIN (a, b))
     swap (&a, &c), swap (&b, &d);
-  if (min (b, d) < min (a, c))
+  if (MIN (b, d) < MIN (a, c))
     swap (&a, &b), swap (&c, &d);
   if (b * c < a * d)
     {
@@ -2380,7 +2405,7 @@ calc_chisq (double chisq[N_CHISQ], int df[N_CHISQ],
     }
 
   /* Calculate Mantel-Haenszel. */
-  if (x->vars[ROW_VAR]->type == NUMERIC && x->vars[COL_VAR]->type == NUMERIC)
+  if (var_is_numeric (x->vars[ROW_VAR]) && var_is_numeric (x->vars[COL_VAR]))
     {
       double r, ase_0, ase_1;
       calc_r ((double *) rows, (double *) cols, &r, &ase_0, &ase_1);
@@ -2467,7 +2492,7 @@ static int
 calc_symmetric (double v[N_SYMMETRIC], double ase[N_SYMMETRIC],
                double t[N_SYMMETRIC])
 {
-  int q = min (ns_rows, ns_cols);
+  int q = MIN (ns_rows, ns_cols);
   
   if (q <= 1)
     return 0;
@@ -3184,22 +3209,22 @@ format_short (char *s, const struct fmt_spec *fp, const union value *v)
   struct fmt_spec fmt_subst;
 
   /* Limit to short string width. */
-  if (formats[fp->type].cat & FCAT_STRING
+  if (fmt_is_string (fp->type)
     {
       fmt_subst = *fp;
 
       assert (fmt_subst.type == FMT_A || fmt_subst.type == FMT_AHEX);
       if (fmt_subst.type == FMT_A)
-        fmt_subst.w = min (8, fmt_subst.w);
+        fmt_subst.w = MIN (8, fmt_subst.w);
       else
-        fmt_subst.w = min (16, fmt_subst.w);
+        fmt_subst.w = MIN (16, fmt_subst.w);
 
       fp = &fmt_subst;
     }
 
   /* Format. */
-  data_out (s, fp, v);
-  
+  data_out (v, fp, s);
+    
   /* Null terminate. */
   s[fp->w] = '\0';
 }