Add scratch file handles.
[pspp-builds.git] / src / vfm.c
index 344f94b9a80224625fdfed08e9ed84fcb7dc9645..06cf67ec1ebc8ca70221875b428b43e16e09e30e 100644 (file)
--- a/src/vfm.c
+++ b/src/vfm.c
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -14,8 +14,8 @@
 
    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., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
 
 #include <config.h>
 #include "vfm.h"
 #include "alloc.h"
 #include "case.h"
 #include "casefile.h"
+#include "command.h"
 #include "dictionary.h"
-#include "do-ifP.h"
+#include "ctl-stack.h"
 #include "error.h"
 #include "expressions/public.h"
+#include "file-handle-def.h"
 #include "misc.h"
 #include "settings.h"
 #include "som.h"
@@ -42,6 +44,9 @@
 #include "var.h"
 #include "value-labels.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 /*
    Virtual File Manager (vfm):
 
@@ -71,12 +76,12 @@ struct case_source *vfm_source;
 /* The replacement active file, to which cases are written. */
 struct case_sink *vfm_sink;
 
-/* Nonzero if the case needs to have values deleted before being
-   stored, zero otherwise. */
-static int compaction_necessary;
+/* The compactor used to compact a compact, if necessary;
+   otherwise a null pointer. */
+static struct dict_compactor *compactor;
 
 /* Time at which vfm was last invoked. */
-time_t last_vfm_invocation;
+static time_t last_vfm_invocation;
 
 /* Lag queue. */
 int n_lag;                     /* Number of cases to lag. */
@@ -86,11 +91,12 @@ static struct ccase *lag_queue; /* Array of n_lag ccase * elements. */
 
 static void internal_procedure (int (*proc_func) (struct ccase *, void *),
                                 void *aux);
+static void update_last_vfm_invocation (void);
 static void create_trns_case (struct ccase *, struct dictionary *);
 static void open_active_file (void);
 static int write_case (struct write_case_data *wc_data);
 static int execute_transformations (struct ccase *c,
-                                    struct trns_header **trns,
+                                    struct transformation *trns,
                                     int first_idx, int last_idx,
                                     int case_num);
 static int filter_case (const struct ccase *c, int case_num);
@@ -100,6 +106,15 @@ static void close_active_file (void);
 \f
 /* Public functions. */
 
+/* Returns the last time the data was read. */
+time_t
+vfm_last_invocation (void) 
+{
+  if (last_vfm_invocation == 0)
+    update_last_vfm_invocation ();
+  return last_vfm_invocation;
+}
+
 /* Reads the data from the input program and writes it to a new
    active file.  For each case we read from the input program, we
    do the following
@@ -132,6 +147,7 @@ procedure (int (*proc_func) (struct ccase *, void *), void *aux)
       && n_trns == 0)
     {
       /* Nothing to do. */
+      update_last_vfm_invocation ();
       return;
     }
 
@@ -158,7 +174,7 @@ internal_procedure (int (*proc_func) (struct ccase *, void *), void *aux)
   case_create (&wc_data.sink_case, dict_get_next_value_idx (default_dict));
   wc_data.cases_written = 0;
 
-  last_vfm_invocation = time (NULL);
+  update_last_vfm_invocation ();
 
   if (vfm_source != NULL) 
     vfm_source->class->read (vfm_source,
@@ -171,6 +187,13 @@ internal_procedure (int (*proc_func) (struct ccase *, void *), void *aux)
   assert (--recursive_call == 0);
 }
 
+/* Updates last_vfm_invocation. */
+static void
+update_last_vfm_invocation (void) 
+{
+  last_vfm_invocation = time (NULL);
+}
+
 /* Creates and returns a case, initializing it 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. */
@@ -207,8 +230,9 @@ open_active_file (void)
     }
 
   /* Figure out compaction. */
-  compaction_necessary = (dict_get_next_value_idx (temp_dict)
-                          != dict_get_compacted_value_cnt (temp_dict));
+  compactor = (dict_needs_compaction (temp_dict)
+               ? dict_make_compactor (temp_dict)
+               : NULL);
 
   /* Prepare sink. */
   if (vfm_sink == NULL)
@@ -223,13 +247,13 @@ open_active_file (void)
   
       lag_count = 0;
       lag_head = 0;
-      lag_queue = xmalloc (n_lag * sizeof *lag_queue);
+      lag_queue = xnmalloc (n_lag, sizeof *lag_queue);
       for (i = 0; i < n_lag; i++)
         case_nullify (&lag_queue[i]);
     }
 
   /* Close any unclosed DO IF or LOOP constructs. */
-  discard_ctl_stack ();
+  ctl_stack_clear ();
 }
 
 /* Transforms trns_case and writes it to the replacement active
@@ -257,9 +281,10 @@ write_case (struct write_case_data *wc_data)
   /* Write case to replacement active file. */
   if (vfm_sink->class->write != NULL) 
     {
-      if (compaction_necessary
+      if (compactor != NULL
         {
-          dict_compact_case (temp_dict, &wc_data->sink_case, &wc_data->trns_case);
+          dict_compactor_compact (compactor, &wc_data->sink_case,
+                                  &wc_data->trns_case);
           vfm_sink->class->write (vfm_sink, &wc_data->sink_case);
         }
       else
@@ -294,7 +319,7 @@ write_case (struct write_case_data *wc_data)
    transformations, nonzero otherwise. */
 static int
 execute_transformations (struct ccase *c,
-                         struct trns_header **trns,
+                         struct transformation *trns,
                          int first_idx, int last_idx,
                          int case_num) 
 {
@@ -302,7 +327,8 @@ execute_transformations (struct ccase *c,
 
   for (idx = first_idx; idx != last_idx; )
     {
-      int retval = trns[idx]->proc (trns[idx], c, case_num);
+      struct transformation *t = &trns[idx];
+      int retval = t->proc (t->private, c, case_num);
       switch (retval)
         {
         case -1:
@@ -332,7 +358,7 @@ filter_case (const struct ccase *c, int case_idx)
   if (filter_var != NULL) 
     {
       double f = case_num (c, filter_var->fv);
-      if (f == 0.0 || f == SYSMIS || is_num_user_missing (f, filter_var))
+      if (f == 0.0 || mv_is_num_missing (&filter_var->miss, f))
         return 1;
     }
 
@@ -401,15 +427,15 @@ close_active_file (void)
     }
 
   /* Finish compaction. */
-  if (compaction_necessary)
-    dict_compact_values (default_dict);
-    
-  /* Free data source. */
-  if (vfm_source != NULL) 
+  if (compactor != NULL) 
     {
-      free_case_source (vfm_source);
-      vfm_source = NULL;
+      dict_compactor_destroy (compactor);
+      dict_compact_values (default_dict); 
     }
+    
+  /* Free data source. */
+  free_case_source (vfm_source);
+  vfm_source = NULL;
 
   /* Old data sink becomes new data source. */
   if (vfm_sink->class->make_source != NULL)
@@ -584,7 +610,9 @@ const struct case_sink_class null_sink_class =
 struct ccase *
 lagged_case (int n_before)
 {
-  assert (n_before >= 1 && n_before <= n_lag);
+  assert (n_before >= 1 );
+  assert (n_before <= n_lag);
+
   if (n_before <= lag_count)
     {
       int index = lag_head - n_before;
@@ -599,15 +627,24 @@ lagged_case (int n_before)
 /* Appends TRNS to t_trns[], the list of all transformations to be
    performed on data as it is read from the active file. */
 void
-add_transformation (struct trns_header * trns)
+add_transformation (trns_proc_func *proc, trns_free_func *free, void *private)
 {
+  struct transformation *trns;
   if (n_trns >= m_trns)
-    {
-      m_trns += 16;
-      t_trns = xrealloc (t_trns, sizeof *t_trns * m_trns);
-    }
-  t_trns[n_trns] = trns;
-  trns->index = n_trns++;
+    t_trns = x2nrealloc (t_trns, &m_trns, sizeof *t_trns);
+  trns = &t_trns[n_trns++];
+  trns->proc = proc;
+  trns->free = free;
+  trns->private = private;
+}
+
+/* Returns the index number that the next transformation added by
+   add_transformation() will receive.  A trns_proc_func that
+   returns this index causes control flow to jump to it. */
+size_t
+next_transformation (void) 
+{
+  return n_trns;
 }
 
 /* Cancels all active transformations, including any transformations
@@ -615,16 +652,16 @@ add_transformation (struct trns_header * trns)
 void
 cancel_transformations (void)
 {
-  int i;
+  size_t i;
   for (i = 0; i < n_trns; i++)
     {
-      if (t_trns[i]->free)
-       t_trns[i]->free (t_trns[i]);
-      free (t_trns[i]);
+      struct transformation *t = &t_trns[i];
+      if (t->free != NULL)
+       t->free (t->private);
     }
   n_trns = f_trns = 0;
   free (t_trns);
-  t_trns=NULL;
+  t_trns = NULL;
   m_trns = 0;
 }
 \f
@@ -782,32 +819,9 @@ procedure_with_splits_callback (struct ccase *c, void *split_aux_)
 static int
 equal_splits (const struct ccase *a, const struct ccase *b) 
 {
-  struct variable *const *split;
-  size_t split_cnt;
-  size_t i;
-    
-  split = dict_get_split_vars (default_dict);
-  split_cnt = dict_get_split_cnt (default_dict);
-  for (i = 0; i < split_cnt; i++)
-    {
-      struct variable *v = split[i];
-      
-      switch (v->type)
-       {
-       case NUMERIC:
-         if (case_num (a, v->fv) != case_num (b, v->fv))
-            return 0;
-         break;
-       case ALPHA:
-         if (memcmp (case_str (a, v->fv), case_str (b, v->fv), v->width))
-            return 0;
-         break;
-       default:
-         assert (0);
-       }
-    }
-
-  return 1;
+  return case_compare (a, b,
+                       dict_get_split_vars (default_dict),
+                       dict_get_split_cnt (default_dict)) == 0;
 }
 
 /* Dumps out the values of all the split variables for the case C. */
@@ -927,3 +941,32 @@ multipass_split_output (struct multipass_split_aux_data *aux)
   casefile_destroy (aux->casefile);
   aux->casefile = NULL;
 }
+
+
+/* Discards all the current state in preparation for a data-input
+   command like DATA LIST or GET. */
+void
+discard_variables (void)
+{
+  dict_clear (default_dict);
+  fh_set_default_handle (NULL);
+
+  n_lag = 0;
+  
+  if (vfm_source != NULL)
+    {
+      free_case_source (vfm_source);
+      vfm_source = NULL;
+    }
+
+  cancel_transformations ();
+
+  ctl_stack_clear ();
+
+  expr_free (process_if_expr);
+  process_if_expr = NULL;
+
+  cancel_temporary ();
+
+  pgm_state = STATE_INIT;
+}