Add scratch file handles.
[pspp-builds.git] / src / vfm.c
index 796e3bcff07eda9daac4101bb5793d8721d2dd8f..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,21 +91,30 @@ 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);
 static void lag_case (const struct ccase *c);
-static void compact_case (struct ccase *dest, const struct ccase *src);
 static void clear_case (struct ccase *c);
 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
@@ -133,6 +147,7 @@ procedure (int (*proc_func) (struct ccase *, void *), void *aux)
       && n_trns == 0)
     {
       /* Nothing to do. */
+      update_last_vfm_invocation ();
       return;
     }
 
@@ -159,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,
@@ -172,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. */
@@ -208,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)
@@ -224,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
@@ -258,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
         {
-          compact_case (&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
@@ -295,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) 
 {
@@ -303,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:
@@ -333,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;
     }
 
@@ -357,43 +382,6 @@ lag_case (const struct ccase *c)
     lag_head = 0;
 }
 
-/* Copies case SRC to case DEST, compacting it in the process. */
-static void
-compact_case (struct ccase *dest, const struct ccase *src)
-{
-  int i;
-  int nval = 0;
-  size_t var_cnt;
-  
-  assert (compaction_necessary);
-
-  /* Copy all the variables except scratch variables from SRC to
-     DEST. */
-  /* FIXME: this should be temp_dict not default_dict I guess. */
-  var_cnt = dict_get_var_cnt (default_dict);
-  for (i = 0; i < var_cnt; i++)
-    {
-      struct variable *v = dict_get_var (default_dict, i);
-      
-      if (dict_class_from_id (v->name) == DC_SCRATCH)
-       continue;
-
-      if (v->type == NUMERIC) 
-        {
-          case_data_rw (dest, nval)->f = case_num (src, v->fv);
-          nval++; 
-        }
-      else
-       {
-         int w = DIV_RND_UP (v->width, sizeof (union value));
-         
-         memcpy (case_data_rw (dest, nval), case_str (src, v->fv),
-                  w * sizeof (union value));
-         nval += w;
-       }
-    }
-}
-
 /* Clears the variables in C that need to be cleared between
    processing cases.  */
 static void
@@ -439,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)
@@ -518,7 +506,7 @@ static struct case_source *
 storage_sink_make_source (struct case_sink *sink) 
 {
   struct case_source *source
-    = create_case_source (&storage_source_class, sink->dict, sink->aux);
+    = create_case_source (&storage_source_class, sink->aux);
   sink->aux = NULL;
   return source;
 }
@@ -594,14 +582,14 @@ storage_source_get_casefile (struct case_source *source)
 }
 
 struct case_source *
-storage_source_create (struct casefile *cf, const struct dictionary *dict) 
+storage_source_create (struct casefile *cf)
 {
   struct storage_stream_info *info;
 
   info = xmalloc (sizeof *info);
   info->casefile = cf;
 
-  return create_case_source (&storage_source_class, dict, info);
+  return create_case_source (&storage_source_class, info);
 }
 \f
 /* Null sink.  Used by a few procedures that keep track of output
@@ -622,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;
@@ -637,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
@@ -653,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
@@ -670,12 +669,10 @@ cancel_transformations (void)
    and based on dictionary DICT. */
 struct case_source *
 create_case_source (const struct case_source_class *class,
-                    const struct dictionary *dict,
                     void *aux) 
 {
   struct case_source *source = xmalloc (sizeof *source);
   source->class = class;
-  source->value_cnt = dict_get_next_value_idx (dict);
   source->aux = aux;
   return source;
 }
@@ -709,8 +706,8 @@ case_source_is_class (const struct case_source *source,
   return source != NULL && source->class == class;
 }
 
-/* Creates a case sink with class CLASS and auxiliary data
-   AUX. */
+/* Creates a case sink to accept cases from the given DICT with
+   class CLASS and auxiliary data AUX. */
 struct case_sink *
 create_case_sink (const struct case_sink_class *class,
                   const struct dictionary *dict,
@@ -718,8 +715,6 @@ create_case_sink (const struct case_sink_class *class,
 {
   struct case_sink *sink = xmalloc (sizeof *sink);
   sink->class = class;
-  sink->dict = dict;
-  sink->idx_to_fv = dict_get_compacted_idx_to_fv (dict);
   sink->value_cnt = dict_get_compacted_value_cnt (dict);
   sink->aux = aux;
   return sink;
@@ -733,7 +728,6 @@ free_case_sink (struct case_sink *sink)
     {
       if (sink->class->destroy != NULL)
         sink->class->destroy (sink);
-      free (sink->idx_to_fv);
       free (sink); 
     }
 }
@@ -783,10 +777,12 @@ procedure_with_splits (void (*begin_func) (void *aux),
   split_aux.end_func = end_func;
   split_aux.func_aux = func_aux;
 
-  procedure (procedure_with_splits_callback, &split_aux);
-
+  open_active_file ();
+  internal_procedure (procedure_with_splits_callback, &split_aux);
   if (split_aux.case_count > 0 && end_func != NULL)
     end_func (func_aux);
+  close_active_file ();
+
   case_destroy (&split_aux.prev_case);
 }
 
@@ -823,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. */
@@ -968,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;
+}