Redo VFM interface. Get rid of compaction_necessary, compaction_nval,
[pspp-builds.git] / src / get.c
index 182ef0e748cbf864811cab64cc8e3f6269132b71..5b28a7fcc3ad74b20f74c08176ef66067ea116ad 100644 (file)
--- a/src/get.c
+++ b/src/get.c
 
 #include "debug-print.h"
 
+/* GET or IMPORT input program. */
+struct get_pgm 
+  {
+    struct file_handle *handle; /* File to GET or IMPORT from. */
+    size_t case_size;           /* Case size in bytes. */
+  };
+
 /* XSAVE transformation (and related SAVE, EXPORT procedures). */
 struct save_trns
   {
@@ -54,16 +61,10 @@ struct save_trns
 #define GTSV_OPT_MATCH_FILES   004     /* The MATCH FILES procedure. */
 #define GTSV_OPT_NONE          0
 
-/* The file being read by the input program. */
-static struct file_handle *get_file;
-
-/* The transformation being used by the SAVE procedure. */
-static struct save_trns *trns;
-
 static int trim_dictionary (struct dictionary * dict, int *options);
 static int save_write_case_func (struct ccase *, void *);
-static int save_trns_proc (struct trns_header *, struct ccase *);
-static void save_trns_free (struct trns_header *);
+static trns_proc_func save_trns_proc;
+static trns_free_func save_trns_free;
 
 #if DEBUGGING
 void dump_dict_variables (struct dictionary *);
@@ -75,6 +76,7 @@ cmd_get (void)
 {
   struct file_handle *handle;
   struct dictionary *dict;
+  struct get_pgm *pgm;
   int options = GTSV_OPT_NONE;
 
   lex_match_id ("GET");
@@ -120,16 +122,24 @@ cmd_get (void)
   dict_destroy (default_dict);
   default_dict = dict;
 
-  vfm_source = &get_source;
-  get_file = handle;
+  pgm = xmalloc (sizeof *pgm);
+  pgm->handle = handle;
+  pgm->case_size = dict_get_case_size (default_dict);
+  vfm_source = create_case_source (&get_source_class, default_dict, pgm);
 
   return CMD_SUCCESS;
 }
 
-/* Parses the SAVE (for XSAVE==0) and XSAVE (for XSAVE==1)
-   commands.  */
+/* SAVE or XSAVE command? */
+enum save_cmd 
+  {
+    CMD_SAVE,
+    CMD_XSAVE
+  };
+
+/* Parses the SAVE and XSAVE commands.  */
 static int
-cmd_save_internal (int xsave)
+cmd_save_internal (enum save_cmd save_cmd)
 {
   struct file_handle *handle;
   struct dictionary *dict;
@@ -178,7 +188,7 @@ cmd_save_internal (int xsave)
     }
 
   /* Fill in transformation structure. */
-  t = trns = xmalloc (sizeof *t);
+  t = xmalloc (sizeof *t);
   t->h.proc = save_trns_proc;
   t->h.free = save_trns_free;
   t->f = handle;
@@ -189,15 +199,16 @@ cmd_save_internal (int xsave)
   t->case_buf = xmalloc (sizeof *t->case_buf * inf.case_size);
   dict_destroy (dict);
 
-  if (xsave == 0)
-    /* SAVE. */
+  if (save_cmd == CMD_SAVE)
     {
-      procedure (NULL, save_write_case_func, NULL, NULL);
+      procedure (save_write_case_func, t);
       save_trns_free (&t->h);
     }
-  else
-    /* XSAVE. */
-    add_transformation (&t->h);
+  else 
+    {
+      assert (save_cmd == CMD_XSAVE);
+      add_transformation (&t->h); 
+    }
 
   return CMD_SUCCESS;
 }
@@ -206,32 +217,26 @@ cmd_save_internal (int xsave)
 int
 cmd_save (void)
 {
-  return cmd_save_internal (0);
+  return cmd_save_internal (CMD_SAVE);
 }
 
 /* Parses the XSAVE transformation command. */
 int
 cmd_xsave (void)
 {
-  return cmd_save_internal (1);
-}
-
-static int
-save_write_case_func (struct ccase * c, void *aux UNUSED)
-{
-  save_trns_proc (&trns->h, c);
-  return 1;
+  return cmd_save_internal (CMD_XSAVE);
 }
 
-static int
-save_trns_proc (struct trns_header * t UNUSED, struct ccase * c)
+/* Writes the given C to the file specified by T. */
+static void
+do_write_case (struct save_trns *t, struct ccase *c) 
 {
-  flt64 *p = trns->case_buf;
+  flt64 *p = t->case_buf;
   int i;
 
-  for (i = 0; i < trns->nvar; i++)
+  for (i = 0; i < t->nvar; i++)
     {
-      struct variable *v = trns->var[i];
+      struct variable *v = t->var[i];
       if (v->type == NUMERIC)
        {
          double src = c->data[v->fv].f;
@@ -249,10 +254,27 @@ save_trns_proc (struct trns_header * t UNUSED, struct ccase * c)
        }
     }
 
-  sfm_write_case (trns->f, trns->case_buf, p - trns->case_buf);
+  sfm_write_case (t->f, t->case_buf, p - t->case_buf);
+}
+
+/* Writes case C to the system file specified on SAVE. */
+static int
+save_write_case_func (struct ccase *c, void *aux UNUSED)
+{
+  do_write_case (aux, c);
+  return 1;
+}
+
+/* Writes case C to the system file specified on XSAVE. */
+static int
+save_trns_proc (struct trns_header *h, struct ccase *c, int case_num UNUSED)
+{
+  struct save_trns *t = (struct save_trns *) h;
+  do_write_case (t, c);
   return -1;
 }
 
+/* Frees a SAVE transformation. */
 static void
 save_trns_free (struct trns_header *pt)
 {
@@ -290,7 +312,7 @@ trim_dictionary (struct dictionary *dict, int *options)
       v = xmalloc (sizeof *v * dict_get_var_cnt (dict));
       nv = 0;
       for (i = 0; i < dict_get_var_cnt (dict); i++) 
-        if (dict_get_var (dict, i)->name[0] == '#')
+        if (dict_class_from_id (dict_get_var (dict, i)->name) == DC_SCRATCH)
           v[nv++] = dict_get_var (dict, i);
       dict_delete_vars (dict, v, nv);
       free (v);
@@ -467,33 +489,36 @@ dump_dict_variables (struct dictionary * dict)
 \f
 /* Clears internal state related to GET input procedure. */
 static void
-get_source_destroy_source (void)
+get_source_destroy (struct case_source *source)
 {
+  struct get_pgm *pgm = source->aux;
+
   /* It is not necessary to destroy the dictionary because if we get
      to this point then the dictionary is default_dict. */
-  fh_close_handle (get_file);
+  fh_close_handle (pgm->handle);
+  free (pgm);
 }
 
-/* Reads all the cases from the data file and passes them to
-   write_case(). */
+/* Reads all the cases from the data file into C and passes them
+   to WRITE_CASE one by one, passing WC_DATA. */
 static void
-get_source_read (write_case_func *write_case, write_case_data wc_data)
+get_source_read (struct case_source *source,
+                 struct ccase *c,
+                 write_case_func *write_case, write_case_data wc_data)
 {
-  while (sfm_read_case (get_file, temp_case->data, default_dict)
+  struct get_pgm *pgm = source->aux;
+
+  while (sfm_read_case (pgm->handle, c->data, default_dict)
         && write_case (wc_data))
     ;
-  get_source_destroy_source ();
 }
 
-struct case_stream get_source =
+const struct case_source_class get_source_class =
   {
+    "GET",
     NULL,
     get_source_read,
-    NULL,
-    NULL,
-    get_source_destroy_source,
-    NULL,
-    "GET",
+    get_source_destroy,
   };
 
 \f
@@ -541,6 +566,12 @@ static unsigned mtf_seq_num;
 /* Sequence numbers for each variable in mtf_master. */
 static unsigned *mtf_seq_nums;
 
+/* Sink for MATCH FILES output. */
+static struct case_sink *mtf_sink;
+
+/* Case used for MATCH FILES output. */
+static struct ccase *mtf_case;
+
 static void mtf_free (void);
 static void mtf_free_file (struct mtf_file *file);
 static int mtf_merge_dictionary (struct mtf_file *f);
@@ -659,6 +690,15 @@ cmd_match_files (void)
                             "file has been defined."));
                  goto lossage;
                }
+
+              if (temporary != 0)
+                {
+                  msg (SE,
+                       _("MATCH FILES may not be used after TEMPORARY when "
+                         "the active file is an input source.  "
+                         "Temporary transformations will be made permanent."));
+                  cancel_temporary (); 
+                }
            }
          else
            {
@@ -839,26 +879,29 @@ cmd_match_files (void)
 
      FIXME: For merging large numbers of files (more than 10?) a
      better algorithm would use a heap for finding minimum
-     values, or replacement selection, as described by Knuth in
-     _Art of Computer Programming, Vol. 3_.  The SORT CASES
-     procedure does this, and perhaps some of its code could be
-     adapted. */
+     values. */
 
   if (!(seen & 2))
     discard_variables ();
 
-  temporary = 2;
-  temp_dict = mtf_master;
-  temp_trns = n_trns;
+  mtf_sink = create_case_sink (&storage_sink_class, mtf_master, NULL);
 
   mtf_seq_nums = xmalloc (dict_get_var_cnt (mtf_master)
                           * sizeof *mtf_seq_nums);
   memset (mtf_seq_nums, 0,
           dict_get_var_cnt (mtf_master) * sizeof *mtf_seq_nums);
+  mtf_case = xmalloc (dict_get_case_size (mtf_master));
+
+  mtf_read_nonactive_records (NULL);
+  if (seen & 2)
+    procedure (mtf_processing, NULL);
+  mtf_processing_finish (NULL);
 
-  process_active_file (mtf_read_nonactive_records, mtf_processing,
-                      mtf_processing_finish, NULL);
+  dict_destroy (default_dict);
+  default_dict = mtf_master;
   mtf_master = NULL;
+  vfm_source = mtf_sink->class->make_source (mtf_sink);
+  free_case_sink (mtf_sink);
   
   mtf_free ();
   return CMD_SUCCESS;
@@ -885,7 +928,7 @@ mtf_processing_finish (void *aux UNUSED)
   }
   
   while (mtf_head && mtf_head->type == MTF_FILE)
-    if (!mtf_processing (temp_case, NULL))
+    if (!mtf_processing (NULL, NULL))
       break;
 }
 
@@ -968,13 +1011,12 @@ mtf_delete_file_in_place (struct mtf_file **file)
        struct variable *v = dict_get_var (f->dict, i);
          
        if (v->type == NUMERIC)
-         compaction_case->data[v->p.mtf.master->fv].f = SYSMIS;
+         mtf_case->data[v->p.mtf.master->fv].f = SYSMIS;
        else
-         memset (compaction_case->data[v->p.mtf.master->fv].s, ' ',
-                 v->width);
+         memset (mtf_case->data[v->p.mtf.master->fv].s, ' ', v->width);
       }
   }
-  
+
   mtf_free_file (f);
 }
 
@@ -989,8 +1031,7 @@ mtf_read_nonactive_records (void *aux UNUSED)
       if (iter->handle)
        {
          assert (iter->input == NULL);
-         iter->input = xmalloc (sizeof *iter->input
-                                 * dict_get_value_cnt (iter->dict));
+         iter->input = xmalloc (dict_get_case_size (iter->dict));
          
          if (!sfm_read_case (iter->handle, iter->input, iter->dict))
            mtf_delete_file_in_place (&iter);
@@ -998,20 +1039,22 @@ mtf_read_nonactive_records (void *aux UNUSED)
            iter = iter->next;
        }
       else
-       {
-         iter->input = temp_case->data;
-         iter = iter->next;
-       }
+        iter = iter->next;
     }
 }
 
 /* Compare the BY variables for files A and B; return -1 if A < B, 0
    if A == B, 1 if A > B. */
 static inline int
-mtf_compare_BY_values (struct mtf_file *a, struct mtf_file *b)
+mtf_compare_BY_values (struct mtf_file *a, struct mtf_file *b,
+                       struct ccase *c)
 {
+  union value *a_input, *b_input;
   int i;
-  
+
+  assert ((a == NULL) + (b == NULL) + (c == NULL) <= 1);
+  a_input = a->input != NULL ? a->input : c->data;
+  b_input = b->input != NULL ? b->input : c->data;
   for (i = 0; i < mtf_n_by; i++)
     {
       assert (a->by[i]->type == b->by[i]->type);
@@ -1019,8 +1062,8 @@ mtf_compare_BY_values (struct mtf_file *a, struct mtf_file *b)
       
       if (a->by[i]->type == NUMERIC)
        {
-         double af = a->input[a->by[i]->fv].f;
-         double bf = b->input[b->by[i]->fv].f;
+         double af = a_input[a->by[i]->fv].f;
+         double bf = b_input[b->by[i]->fv].f;
 
          if (af < bf)
            return -1;
@@ -1032,8 +1075,8 @@ mtf_compare_BY_values (struct mtf_file *a, struct mtf_file *b)
          int result;
          
          assert (a->by[i]->type == ALPHA);
-         result = memcmp (a->input[a->by[i]->fv].s,
-                          b->input[b->by[i]->fv].s,
+         result = memcmp (a_input[a->by[i]->fv].s,
+                          b_input[b->by[i]->fv].s,
                           a->by[i]->width);
          if (result < 0)
            return -1;
@@ -1046,7 +1089,7 @@ mtf_compare_BY_values (struct mtf_file *a, struct mtf_file *b)
 
 /* Perform one iteration of steps 3...7 above. */
 static int
-mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
+mtf_processing (struct ccase *c, void *aux UNUSED)
 {
   /* List of files with minimum BY values. */
   struct mtf_file *min_head, *min_tail;
@@ -1076,7 +1119,7 @@ mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
       max_head = max_tail = NULL;
       for (iter = mtf_head->next; iter && iter->type == MTF_FILE;
           iter = iter->next)
-       switch (mtf_compare_BY_values (min_head, iter))
+       switch (mtf_compare_BY_values (min_head, iter, c))
          {
          case -1:
            if (max_head)
@@ -1121,7 +1164,7 @@ mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
            advance = 0;
 
        again:
-         switch (mtf_compare_BY_values (min_head, iter))
+         switch (mtf_compare_BY_values (min_head, iter, c))
            {
            case -1:
              if (max_head)
@@ -1162,26 +1205,20 @@ mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
          for (i = 0; i < dict_get_var_cnt (iter->dict); i++)
            {
              struct variable *v = dict_get_var (iter->dict, i);
+              union value *record;
          
              if (mtf_seq_nums[v->p.mtf.master->index] == mtf_seq_num)
                continue;
               mtf_seq_nums[v->p.mtf.master->index] = mtf_seq_num;
 
-#if 0
-             printf ("%s/%s: dest-fv=%d, src-fv=%d\n",
-                     fh_handle_name (iter->handle),
-                     v->name,
-                     v->p.mtf.master->fv, v->fv);
-#endif
+              record = iter->input != NULL ? iter->input : c->data;
+
+              assert (v->type == NUMERIC || v->type == ALPHA);
              if (v->type == NUMERIC)
-               compaction_case->data[v->p.mtf.master->fv].f
-                 = iter->input[v->fv].f;
+               mtf_case->data[v->p.mtf.master->fv].f = record[v->fv].f;
              else
-               {
-                 assert (v->type == ALPHA);
-                 memcpy (compaction_case->data[v->p.mtf.master->fv].s,
-                         iter->input[v->fv].s, v->width);
-               }
+                memcpy (mtf_case->data[v->p.mtf.master->fv].s,
+                        record[v->fv].s, v->width);
            }
        }
 
@@ -1207,9 +1244,9 @@ mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
                      v->p.mtf.master->fv);
 #endif
              if (v->type == NUMERIC)
-               compaction_case->data[v->p.mtf.master->fv].f = SYSMIS;
+               mtf_case->data[v->p.mtf.master->fv].f = SYSMIS;
              else
-                memset (compaction_case->data[v->p.mtf.master->fv].s, ' ',
+                memset (mtf_case->data[v->p.mtf.master->fv].s, ' ',
                         v->width);
            }
 
@@ -1218,7 +1255,7 @@ mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
        }
 
       /* 6. Write the output record. */
-      process_active_file_output_case ();
+      mtf_sink->class->write (mtf_sink, mtf_case);
 
       /* 7. Read another record from each input file FILE and TABLE
         that we stored values from above.  If we come to the end of
@@ -1330,6 +1367,7 @@ cmd_import (void)
 {
   struct file_handle *handle = NULL;
   struct dictionary *dict;
+  struct get_pgm *pgm;
   int options = GTSV_OPT_NONE;
   int type;
 
@@ -1403,8 +1441,10 @@ cmd_import (void)
   dict_destroy (default_dict);
   default_dict = dict;
 
-  vfm_source = &import_source;
-  get_file = handle;
+  pgm = xmalloc (sizeof *pgm);
+  pgm->handle = handle;
+  pgm->case_size = dict_get_case_size (default_dict);
+  vfm_source = create_case_source (&import_source_class, default_dict, pgm);
 
   return CMD_SUCCESS;
 }
@@ -1412,23 +1452,23 @@ cmd_import (void)
 /* Reads all the cases from the data file and passes them to
    write_case(). */
 static void
-import_source_read (write_case_func *write_case, write_case_data wc_data)
+import_source_read (struct case_source *source,
+                    struct ccase *c,
+                    write_case_func *write_case, write_case_data wc_data)
 {
-  while (pfm_read_case (get_file, temp_case->data, default_dict)
-        && write_case (wc_data))
-    ;
-  get_source_destroy_source ();
+  struct get_pgm *pgm = source->aux;
+  
+  while (pfm_read_case (pgm->handle, c->data, default_dict))
+    if (!write_case (wc_data))
+      break;
 }
 
-struct case_stream import_source =
+const struct case_source_class import_source_class =
   {
+    "IMPORT",
     NULL,
     import_source_read,
-    NULL,
-    NULL,
-    get_source_destroy_source,
-    NULL,
-    "IMPORT",
+    get_source_destroy,
   };
 \f
 static int export_write_case_func (struct ccase *c, void *);
@@ -1481,7 +1521,7 @@ cmd_export (void)
     }
 
   /* Fill in transformation structure. */
-  t = trns = xmalloc (sizeof *t);
+  t = xmalloc (sizeof *t);
   t->h.proc = save_trns_proc;
   t->h.free = save_trns_free;
   t->f = handle;
@@ -1492,21 +1532,23 @@ cmd_export (void)
   t->case_buf = xmalloc (sizeof *t->case_buf * t->nvar);
   dict_destroy (dict);
 
-  procedure (NULL, export_write_case_func, NULL, NULL);
+  procedure (export_write_case_func, t);
   save_trns_free (&t->h);
 
   return CMD_SUCCESS;
 }
 
+/* Writes case C to the EXPORT file. */
 static int
-export_write_case_func (struct ccase *c, void *aux UNUSED)
+export_write_case_func (struct ccase *c, void *aux)
 {
-  union value *p = (union value *) trns->case_buf;
+  struct save_trns *t = aux;
+  union value *p = (union value *) t->case_buf;
   int i;
 
-  for (i = 0; i < trns->nvar; i++)
+  for (i = 0; i < t->nvar; i++)
     {
-      struct variable *v = trns->var[i];
+      struct variable *v = t->var[i];
 
       if (v->type == NUMERIC)
        *p++ = c->data[v->fv];
@@ -1514,6 +1556,6 @@ export_write_case_func (struct ccase *c, void *aux UNUSED)
        (*p++).c = c->data[v->fv].s;
     }
 
-  pfm_write_case (trns->f, (union value *) trns->case_buf);
+  pfm_write_case (t->f, (union value *) t->case_buf);
   return 1;
 }