Fixed a bug causing pspp to crash when computed variables had no format
[pspp-builds.git] / src / vfm.c
index 17bd9f266e58e18fa4f67bf745bface9ec0556ec..42e6b7ccd29dfef5e2b5792cbe16c705fdceaec9 100644 (file)
--- a/src/vfm.c
+++ b/src/vfm.c
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA. */
 
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA. */
 
-/* AIX requires this to be the first thing in the file.  */
 #include <config.h>
 #include <config.h>
-#if __GNUC__
-#define alloca __builtin_alloca
-#else
-#if HAVE_ALLOCA_H
-#include <alloca.h>
-#else
-#ifdef _AIX
-#pragma alloca
-#else
-#ifndef alloca                 /* predefined by HP cc +Olibcalls */
-char *alloca ();
-#endif
-#endif
-#endif
-#endif
-
 #include "vfm.h"
 #include "vfmP.h"
 #include <assert.h>
 #include "vfm.h"
 #include "vfmP.h"
 #include <assert.h>
@@ -45,7 +28,6 @@ char *alloca ();
 #include <unistd.h>    /* Required by SunOS4. */
 #endif
 #include "alloc.h"
 #include <unistd.h>    /* Required by SunOS4. */
 #endif
 #include "alloc.h"
-#include "approx.h"
 #include "do-ifP.h"
 #include "error.h"
 #include "expr.h"
 #include "do-ifP.h"
 #include "error.h"
 #include "expr.h"
@@ -67,15 +49,18 @@ char *alloca ();
 
 #include "debug-print.h"
 
 
 #include "debug-print.h"
 
+/* Procedure execution data. */
+struct write_case_data
+  {
+    void (*beginfunc) (void *);
+    int (*procfunc) (struct ccase *, void *);
+    void (*endfunc) (void *);
+    void *aux;
+  };
+
 /* This is used to read from the active file. */
 struct case_stream *vfm_source;
 
 /* This is used to read from the active file. */
 struct case_stream *vfm_source;
 
-/* `value' indexes to initialize to particular values for certain cases. */
-struct long_vec reinit_sysmis;         /* SYSMIS for every case. */
-struct long_vec reinit_blanks;         /* Blanks for every case. */
-struct long_vec init_zero;             /* Zero for first case only. */
-struct long_vec init_blanks;           /* Blanks for first case only. */
-
 /* This is used to write to the replacement active file. */
 struct case_stream *vfm_sink;
 
 /* This is used to write to the replacement active file. */
 struct case_stream *vfm_sink;
 
@@ -85,17 +70,6 @@ struct stream_info vfm_source_info;
 /* Information about the data sink. */
 struct stream_info vfm_sink_info;
 
 /* Information about the data sink. */
 struct stream_info vfm_sink_info;
 
-/* Filter variable and  `value' index. */
-static struct variable *filter_var;
-static int filter_index;
-
-#define FILTERED                                                       \
-       (filter_index != -1                                             \
-        && (temp_case->data[filter_index].f == 0.0                     \
-            || temp_case->data[filter_index].f == SYSMIS               \
-            || is_num_user_missing (temp_case->data[filter_index].f,   \
-                                    filter_var)))
-
 /* Nonzero if the case needs to have values deleted before being
    stored, zero otherwise. */
 int compaction_necessary;
 /* Nonzero if the case needs to have values deleted before being
    stored, zero otherwise. */
 int compaction_necessary;
@@ -115,14 +89,6 @@ static int paging = 0;
 /* Time at which vfm was last invoked. */
 time_t last_vfm_invocation;
 
 /* Time at which vfm was last invoked. */
 time_t last_vfm_invocation;
 
-/* Functions called during procedure processing. */
-static int (*proc_func) (struct ccase *);      /* Called for each case. */
-static int (*virt_proc_func) (struct ccase *); /* From SPLIT_FILE_procfunc. */
-static void (*begin_func) (void);      /* Called at beginning of a series. */
-static void (*virt_begin_func) (void); /* Called by SPLIT_FILE_procfunc. */
-static void (*end_func) (void);        /* Called after end of a series. */
-int (*write_case) (void);
-
 /* Number of cases passed to proc_func(). */
 static int case_count;
 
 /* Number of cases passed to proc_func(). */
 static int case_count;
 
@@ -133,53 +99,74 @@ static int lag_head;               /* Index where next case will be added. */
 static struct ccase **lag_queue; /* Array of n_lag ccase * elements. */
 
 static void open_active_file (void);
 static struct ccase **lag_queue; /* Array of n_lag ccase * elements. */
 
 static void open_active_file (void);
-static void close_active_file (void);
-static int SPLIT_FILE_procfunc (struct ccase *);
+static void close_active_file (struct write_case_data *);
+static int SPLIT_FILE_procfunc (struct ccase *, void *);
 static void finish_compaction (void);
 static void lag_case (void);
 static void finish_compaction (void);
 static void lag_case (void);
-static int procedure_write_case (void);
+static int procedure_write_case (struct write_case_data *);
+static void clear_temp_case (void);
+static int exclude_this_case (void);
 \f
 /* Public functions. */
 
 \f
 /* Public functions. */
 
-/* Reads all the cases from the active file, transforms them by the
-   active set of transformations, calls PROCFUNC with CURCASE set to
-   the case and CASENUM set to the case number, and writes them to a
-   new active file.
+/* Reads all the cases from the active file, transforms them by
+   the active set of transformations, calls PROCFUNC with CURCASE
+   set to the case, and writes them to a new active file.
 
    Divides the active file into zero or more series of one or more
    cases each.  BEGINFUNC is called before each series.  ENDFUNC is
 
    Divides the active file into zero or more series of one or more
    cases each.  BEGINFUNC is called before each series.  ENDFUNC is
-   called after each series. */
+   called after each series.
+
+   Arbitrary user-specified data AUX is passed to BEGINFUNC,
+   PROCFUNC, and ENDFUNC as auxiliary data. */
 void
 void
-procedure (void (*beginfunc) (void),
-          int (*procfunc) (struct ccase *curcase),
-          void (*endfunc) (void))
+procedure (void (*beginfunc) (void *),
+          int (*procfunc) (struct ccase *curcase, void *),
+          void (*endfunc) (void *),
+           void *aux)
 {
 {
-  end_func = endfunc;
-  write_case = procedure_write_case;
+  static int recursive_call;
+
+  struct write_case_data procedure_write_data;
+  struct write_case_data split_file_data;
 
 
-  if (dict_get_split_cnt (default_dict) != 0 && procfunc != NULL)
+  assert (++recursive_call == 1);
+
+  if (dict_get_split_cnt (default_dict) == 0) 
     {
     {
-      virt_proc_func = procfunc;
-      proc_func = SPLIT_FILE_procfunc;
-      
-      virt_begin_func = beginfunc;
-      begin_func = NULL;
-    } else {
-      begin_func = beginfunc;
-      proc_func = procfunc;
+      /* Normally we just use the data passed by the user. */
+      procedure_write_data.beginfunc = beginfunc;
+      procedure_write_data.procfunc = procfunc;
+      procedure_write_data.endfunc = endfunc;
+      procedure_write_data.aux = aux;
+    }
+  else
+    {
+      /* Under SPLIT FILE, we add a layer of indirection. */
+      procedure_write_data.beginfunc = NULL;
+      procedure_write_data.procfunc = SPLIT_FILE_procfunc;
+      procedure_write_data.endfunc = endfunc;
+      procedure_write_data.aux = &split_file_data;
+
+      split_file_data.beginfunc = beginfunc;
+      split_file_data.procfunc = procfunc;
+      split_file_data.endfunc = endfunc;
+      split_file_data.aux = aux;
     }
 
   last_vfm_invocation = time (NULL);
 
   open_active_file ();
     }
 
   last_vfm_invocation = time (NULL);
 
   open_active_file ();
-  vfm_source->read ();
-  close_active_file ();
+  vfm_source->read (procedure_write_case, &procedure_write_data);
+  close_active_file (&procedure_write_data);
+
+  assert (--recursive_call == 0);
 }
 \f
 /* Active file processing support.  Subtly different semantics from
    procedure(). */
 
 }
 \f
 /* Active file processing support.  Subtly different semantics from
    procedure(). */
 
-static int process_active_file_write_case (void);
+static int process_active_file_write_case (struct write_case_data *data);
 
 /* The casefunc might want us to stop calling it. */
 static int not_canceled;
 
 /* The casefunc might want us to stop calling it. */
 static int not_canceled;
@@ -192,28 +179,35 @@ static int not_canceled;
 
    process_active_file() ignores TEMPORARY, SPLIT FILE, and N. */
 void
 
    process_active_file() ignores TEMPORARY, SPLIT FILE, and N. */
 void
-process_active_file (void (*beginfunc) (void),
-                    int (*casefunc) (struct ccase *curcase),
-                    void (*endfunc) (void))
+process_active_file (void (*beginfunc) (void *),
+                    int (*casefunc) (struct ccase *curcase, void *),
+                    void (*endfunc) (void *),
+                     void *aux)
 {
 {
-  proc_func = casefunc;
-  write_case = process_active_file_write_case;
+  struct write_case_data process_active_write_data;
+
+  process_active_write_data.beginfunc = beginfunc;
+  process_active_write_data.procfunc = casefunc;
+  process_active_write_data.endfunc = endfunc;
+  process_active_write_data.aux = aux;
+
   not_canceled = 1;
 
   open_active_file ();
   not_canceled = 1;
 
   open_active_file ();
-  beginfunc ();
+  beginfunc (aux);
   
   /* There doesn't necessarily need to be an active file. */
   if (vfm_source)
   
   /* There doesn't necessarily need to be an active file. */
   if (vfm_source)
-    vfm_source->read ();
+    vfm_source->read (process_active_file_write_case,
+                      &process_active_write_data);
   
   
-  endfunc ();
-  close_active_file ();
+  endfunc (aux);
+  close_active_file (&process_active_write_data);
 }
 
 /* Pass the current case to casefunc. */
 static int
 }
 
 /* Pass the current case to casefunc. */
 static int
-process_active_file_write_case (void)
+process_active_file_write_case (struct write_case_data *data)
 {
   /* Index of current transformation. */
   int cur_trns;
 {
   /* Index of current transformation. */
   int cur_trns;
@@ -243,25 +237,14 @@ process_active_file_write_case (void)
     lag_case ();
          
   /* Call the procedure if FILTER and PROCESS IF don't prohibit it. */
     lag_case ();
          
   /* Call the procedure if FILTER and PROCESS IF don't prohibit it. */
-  if (not_canceled
-      && !FILTERED
-      && (process_if_expr == NULL ||
-         expr_evaluate (process_if_expr, temp_case, NULL) == 1.0))
-    not_canceled = proc_func (temp_case);
+  if (not_canceled && !exclude_this_case ())
+    not_canceled = data->procfunc (temp_case, data->aux);
   
   case_count++;
   
  done:
   
   case_count++;
   
  done:
-  {
-    long *lp;
+  clear_temp_case ();
 
 
-    /* This case is finished.  Initialize the variables for the next case. */
-    for (lp = reinit_sysmis.vec; *lp != -1;)
-      temp_case->data[*lp++].f = SYSMIS;
-    for (lp = reinit_blanks.vec; *lp != -1;)
-      memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
-  }
-  
   return 1;
 }
 
   return 1;
 }
 
@@ -297,10 +280,8 @@ prepare_for_writing (void)
      file--it's just a waste of time and space. */
 
   vfm_sink_info.ncases = 0;
      file--it's just a waste of time and space. */
 
   vfm_sink_info.ncases = 0;
-  vfm_sink_info.nval = dict_get_value_cnt (default_dict);
-  vfm_sink_info.case_size = (sizeof (struct ccase)
-                            + ((dict_get_value_cnt (default_dict) - 1)
-                                * sizeof (union value)));
+  vfm_sink_info.nval = dict_get_next_value_idx (default_dict);
+  vfm_sink_info.case_size = dict_get_case_size (default_dict);
   
   if (vfm_sink == NULL)
     {
   
   if (vfm_sink == NULL)
     {
@@ -340,14 +321,17 @@ arrange_compaction (void)
             count_values += v->nv;
           } 
       }
             count_values += v->nv;
           } 
       }
-    assert (temporary == 2 || count_values <= dict_get_value_cnt (temp_dict));
+    assert (temporary == 2
+            || count_values <= dict_get_next_value_idx (temp_dict));
   }
   
   /* Compaction is only necessary if the number of `value's to output
      differs from the number already present. */
   compaction_nval = count_values;
   }
   
   /* Compaction is only necessary if the number of `value's to output
      differs from the number already present. */
   compaction_nval = count_values;
-  compaction_necessary = (temporary == 2
-                          || count_values != dict_get_value_cnt (temp_dict));
+  if (temporary == 2 || count_values != dict_get_next_value_idx (temp_dict))
+    compaction_necessary = 1;
+  else
+    compaction_necessary = 0;
   
   if (vfm_sink->init)
     vfm_sink->init ();
   
   if (vfm_sink->init)
     vfm_sink->init ();
@@ -383,62 +367,28 @@ index_to_varname (int ccase_index)
 }
 #endif
 
 }
 #endif
 
-/* Initializes temp_case from the vectors that say which `value's need
-   to be initialized just once, and which ones need to be
+/* Initializes temp_case from the vectors that say which `value's
+   need to be initialized just once, and which ones need to be
    re-initialized before every case. */
 static void
 vector_initialization (void)
 {
    re-initialized before every case. */
 static void
 vector_initialization (void)
 {
-  int i;
-  long *lp;
-  
-  /* Just once. */
-  for (i = 0; i < init_zero.n; i++)
-    temp_case->data[init_zero.vec[i]].f = 0.0;
-  for (i = 0; i < init_blanks.n; i++)
-    memset (temp_case->data[init_blanks.vec[i]].s, ' ', MAX_SHORT_STRING);
-
-  /* These vectors need to be repeatedly accessed, so we add a
-     sentinel to (hopefully) improve speed. */
-  vec_insert (&reinit_sysmis, -1);
-  vec_insert (&reinit_blanks, -1);
-
-  for (lp = reinit_sysmis.vec; *lp != -1;)
-    temp_case->data[*lp++].f = SYSMIS;
-  for (lp = reinit_blanks.vec; *lp != -1;)
-    memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
-  
-#if DEBUGGING
-  printf ("vfm: init_zero=");
-  for (i = 0; i < init_zero.n; i++)
-    printf ("%s%s", i ? "," : "", index_to_varname (init_zero.vec[i]));
-  printf (" init_blanks=");
-  for (i = 0; i < init_blanks.n; i++)
-    printf ("%s%s", i ? "," : "", index_to_varname (init_blanks.vec[i]));
-  printf (" reinit_sysmis=");
-  for (lp = reinit_sysmis.vec; *lp != -1; lp++)
-    printf ("%s%s", lp != reinit_sysmis.vec ? "," : "",
-           index_to_varname (*lp));
-  printf (" reinit_blanks=");
-  for (lp = reinit_blanks.vec; *lp != -1; lp++)
-    printf ("%s%s", lp != reinit_blanks.vec ? "," : "",
-           index_to_varname (*lp));
-  printf ("\n");
-#endif
-}
-
-/* Sets filter_index to an appropriate value. */
-static void
-setup_filter (void)
-{
-  filter_var = dict_get_filter (default_dict);
+  size_t var_cnt = dict_get_var_cnt (default_dict);
+  size_t i;
   
   
-  if (filter_var != NULL)
+  for (i = 0; i < var_cnt; i++) 
     {
     {
-      assert (filter_var->type == NUMERIC);
-      filter_index = filter_var->index;
-    } else {
-      filter_index = -1;
+      struct variable *v = dict_get_var (default_dict, i);
+
+      if (v->type == NUMERIC) 
+        {
+          if (v->reinit)
+            temp_case->data[v->fv].f = 0.0;
+          else
+            temp_case->data[v->fv].f = SYSMIS;
+        }
+      else
+        memset (temp_case->data[v->fv].s, ' ', v->width);
     }
 }
 
     }
 }
 
@@ -455,8 +405,7 @@ setup_lag (void)
   lag_head = 0;
   lag_queue = xmalloc (n_lag * sizeof *lag_queue);
   for (i = 0; i < n_lag; i++)
   lag_head = 0;
   lag_queue = xmalloc (n_lag * sizeof *lag_queue);
   for (i = 0; i < n_lag; i++)
-    lag_queue[i] = xmalloc (dict_get_value_cnt (temp_dict)
-                            * sizeof **lag_queue);
+    lag_queue[i] = xmalloc (dict_get_case_size (temp_dict));
 }
 
 /* There is a lot of potential confusion in the vfm and related
 }
 
 /* There is a lot of potential confusion in the vfm and related
@@ -517,7 +466,6 @@ open_active_file (void)
   make_temp_case ();
   vector_initialization ();
   discard_ctl_stack ();
   make_temp_case ();
   vector_initialization ();
   discard_ctl_stack ();
-  setup_filter ();
   setup_lag ();
 
   /* Debug output. */
   setup_lag ();
 
   /* Debug output. */
@@ -532,11 +480,11 @@ open_active_file (void)
 \f
 /* Closes the active file. */
 static void
 \f
 /* Closes the active file. */
 static void
-close_active_file (void)
+close_active_file (struct write_case_data *data)
 {
   /* Close the current case group. */
 {
   /* Close the current case group. */
-  if (case_count && end_func != NULL)
-    end_func ();
+  if (case_count && data->endfunc != NULL)
+    data->endfunc (data->aux);
 
   /* Stop lagging (catch up?). */
   if (n_lag)
 
   /* Stop lagging (catch up?). */
   if (n_lag)
@@ -592,18 +540,12 @@ close_active_file (void)
   process_if_expr = NULL;
 
   /* Cancel FILTER if temporary. */
   process_if_expr = NULL;
 
   /* Cancel FILTER if temporary. */
-  if (filter_var != NULL && !FILTER_before_TEMPORARY)
+  if (dict_get_filter (default_dict) != NULL && !FILTER_before_TEMPORARY)
     dict_set_filter (default_dict, NULL);
 
   /* Cancel transformations. */
   cancel_transformations ();
 
     dict_set_filter (default_dict, NULL);
 
   /* Cancel transformations. */
   cancel_transformations ();
 
-  /* Clear value-initialization vectors. */
-  vec_clear (&init_zero);
-  vec_clear (&init_blanks);
-  vec_clear (&reinit_sysmis);
-  vec_clear (&reinit_blanks);
-
   /* Turn off case limiter. */
   dict_set_case_limit (default_dict, 0);
 
   /* Turn off case limiter. */
   dict_set_case_limit (default_dict, 0);
 
@@ -636,7 +578,7 @@ disk_stream_init (void)
 /* Reads all cases from the disk source and passes them one by one to
    write_case(). */
 static void
 /* Reads all cases from the disk source and passes them one by one to
    write_case(). */
 static void
-disk_stream_read (void)
+disk_stream_read (write_case_func *write_case, write_case_data wc_data)
 {
   int i;
 
 {
   int i;
 
@@ -651,7 +593,7 @@ disk_stream_read (void)
          return;
        }
 
          return;
        }
 
-      if (!write_case ())
+      if (!write_case (wc_data))
        return;
     }
 }
        return;
     }
 }
@@ -755,7 +697,7 @@ memory_stream_init (void)
 
 /* Reads the case stream from memory and passes it to write_case(). */
 static void
 
 /* Reads the case stream from memory and passes it to write_case(). */
 static void
-memory_stream_read (void)
+memory_stream_read (write_case_func *write_case, write_case_data wc_data)
 {
   while (memory_source_cases != NULL)
     {
 {
   while (memory_source_cases != NULL)
     {
@@ -767,7 +709,7 @@ memory_stream_read (void)
        free (current);
       }
       
        free (current);
       }
       
-      if (!write_case ())
+      if (!write_case (wc_data))
        return;
     }
 }
        return;
     }
 }
@@ -944,7 +886,7 @@ lag_case (void)
   if (lag_count < n_lag)
     lag_count++;
   memcpy (lag_queue[lag_head], temp_case,
   if (lag_count < n_lag)
     lag_count++;
   memcpy (lag_queue[lag_head], temp_case,
-          sizeof (union value) * dict_get_value_cnt (temp_dict));
+          dict_get_case_size (temp_dict));
   if (++lag_head >= n_lag)
     lag_head = 0;
 }
   if (++lag_head >= n_lag)
     lag_head = 0;
 }
@@ -971,7 +913,7 @@ lagged_case (int n_before)
    otherwise.  Do not call this function again after it has returned
    zero once.  */
 int
    otherwise.  Do not call this function again after it has returned
    zero once.  */
 int
-procedure_write_case (void)
+procedure_write_case (write_case_data wc_data)
 {
   /* Index of current transformation. */
   int cur_trns;
 {
   /* Index of current transformation. */
   int cur_trns;
@@ -1029,36 +971,70 @@ procedure_write_case (void)
     }
 
   /* Call the beginning of group function. */
     }
 
   /* Call the beginning of group function. */
-  if (!case_count && begin_func != NULL)
-    begin_func ();
+  if (!case_count && wc_data->beginfunc != NULL)
+    wc_data->beginfunc (wc_data->aux);
 
   /* Call the procedure if there is one and FILTER and PROCESS IF
      don't prohibit it. */
 
   /* Call the procedure if there is one and FILTER and PROCESS IF
      don't prohibit it. */
-  if (proc_func != NULL
-      && !FILTERED
-      && (process_if_expr == NULL ||
-         expr_evaluate (process_if_expr, temp_case, NULL) == 1.0))
-    proc_func (temp_case);
+  if (wc_data->procfunc != NULL && !exclude_this_case ())
+    wc_data->procfunc (temp_case, wc_data->aux);
 
   case_count++;
   
 done:
   debug_putc ('\n', stdout);
 
   case_count++;
   
 done:
   debug_putc ('\n', stdout);
-  
-  {
-    long *lp;
 
 
-    /* This case is finished.  Initialize the variables for the next case. */
-    for (lp = reinit_sysmis.vec; *lp != -1;)
-      temp_case->data[*lp++].f = SYSMIS;
-    for (lp = reinit_blanks.vec; *lp != -1;)
-      memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
-  }
+  clear_temp_case ();
   
   /* Return previously determined value. */
   return more_cases;
 }
 
   
   /* Return previously determined value. */
   return more_cases;
 }
 
+/* Clears the variables in the temporary case that need to be
+   cleared between processing cases.  */
+static void
+clear_temp_case (void)
+{
+  /* FIXME?  This is linear in the number of variables, but
+     doesn't need to be, so it's an easy optimization target. */
+  size_t var_cnt = dict_get_var_cnt (default_dict);
+  size_t i;
+  
+  for (i = 0; i < var_cnt; i++) 
+    {
+      struct variable *v = dict_get_var (default_dict, i);
+      if (v->init && v->reinit) 
+        {
+          if (v->type == NUMERIC) 
+            temp_case->data[v->fv].f = SYSMIS;
+          else
+            memset (temp_case->data[v->fv].s, ' ', v->width);
+        } 
+    }
+}
+
+/* Returns nonzero if this case should be exclude as specified on
+   FILTER or PROCESS IF, otherwise zero. */
+static int
+exclude_this_case (void)
+{
+  /* FILTER. */
+  struct variable *filter_var = dict_get_filter (default_dict);
+  if (filter_var != NULL) 
+    {
+      double f = temp_case->data[filter_var->fv].f;
+      if (f == 0.0 || f == SYSMIS || is_num_user_missing (f, filter_var))
+        return 1;
+    }
+
+  /* PROCESS IF. */
+  if (process_if_expr != NULL
+      && expr_evaluate (process_if_expr, temp_case, NULL) != 1.0)
+    return 1;
+
+  return 0;
+}
+
 /* Appends TRNS to t_trns[], the list of all transformations to be
    performed on data as it is read from the active file. */
 void
 /* Appends TRNS to t_trns[], the list of all transformations to be
    performed on data as it is read from the active file. */
 void
@@ -1120,12 +1096,7 @@ dump_splits (struct ccase *c)
       assert (v->type == NUMERIC || v->type == ALPHA);
       tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", v->name);
       
       assert (v->type == NUMERIC || v->type == ALPHA);
       tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", v->name);
       
-      {
-       union value val = c->data[v->fv];
-       if (v->type == ALPHA)
-         val.c = c->data[v->fv].s;
-       data_out (temp_buf, &v->print, &val);
-      }
+      data_out (temp_buf, &v->print, &c->data[v->fv]);
       
       temp_buf[v->print.w] = 0;
       tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", v->print.w, temp_buf);
       
       temp_buf[v->print.w] = 0;
       tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", v->print.w, temp_buf);
@@ -1142,8 +1113,9 @@ dump_splits (struct ccase *c)
    SPLIT FILE is active.  This function forms a wrapper around that
    procfunc by dividing the input into series. */
 static int
    SPLIT FILE is active.  This function forms a wrapper around that
    procfunc by dividing the input into series. */
 static int
-SPLIT_FILE_procfunc (struct ccase *c)
+SPLIT_FILE_procfunc (struct ccase *c, void *data_)
 {
 {
+  struct write_case_data *data = data_;
   static struct ccase *prev_case;
   struct variable *const *split;
   size_t split_cnt;
   static struct ccase *prev_case;
   struct variable *const *split;
   size_t split_cnt;
@@ -1159,10 +1131,10 @@ SPLIT_FILE_procfunc (struct ccase *c)
       memcpy (prev_case, c, vfm_sink_info.case_size);
 
       dump_splits (c);
       memcpy (prev_case, c, vfm_sink_info.case_size);
 
       dump_splits (c);
-      if (virt_begin_func != NULL)
-       virt_begin_func ();
+      if (data->beginfunc != NULL)
+       data->beginfunc (data->aux);
       
       
-      return virt_proc_func (c);
+      return data->procfunc (c, data->aux);
     }
 
   /* Compare the value of each SPLIT FILE variable to the values on
     }
 
   /* Compare the value of each SPLIT FILE variable to the values on
@@ -1176,7 +1148,7 @@ SPLIT_FILE_procfunc (struct ccase *c)
       switch (v->type)
        {
        case NUMERIC:
       switch (v->type)
        {
        case NUMERIC:
-         if (approx_ne (c->data[v->fv].f, prev_case->data[v->fv].f))
+         if (c->data[v->fv].f != prev_case->data[v->fv].f)
            goto not_equal;
          break;
        case ALPHA:
            goto not_equal;
          break;
        case ALPHA:
@@ -1187,19 +1159,19 @@ SPLIT_FILE_procfunc (struct ccase *c)
          assert (0);
        }
     }
          assert (0);
        }
     }
-  return virt_proc_func (c);
+  return data->procfunc (c, data->aux);
   
 not_equal:
   /* The values of the SPLIT FILE variable are different from the
      values on the previous case.  That means that it's time to begin
      a new series. */
   
 not_equal:
   /* The values of the SPLIT FILE variable are different from the
      values on the previous case.  That means that it's time to begin
      a new series. */
-  if (end_func != NULL)
-    end_func ();
+  if (data->endfunc != NULL)
+    data->endfunc (data->aux);
   dump_splits (c);
   dump_splits (c);
-  if (virt_begin_func != NULL)
-    virt_begin_func ();
+  if (data->beginfunc != NULL)
+    data->beginfunc (data->aux);
   memcpy (prev_case, c, vfm_sink_info.case_size);
   memcpy (prev_case, c, vfm_sink_info.case_size);
-  return virt_proc_func (c);
+  return data->procfunc (c, data->aux);
 }
 \f
 /* Case compaction. */
 }
 \f
 /* Case compaction. */