Fix memory leaks.
[pspp-builds.git] / src / get.c
index be1c09a2988443b90327f938853e69b4d9e5b5a3..dc16d7a5372328482cd71d210ad9ff6a00872b41 100644 (file)
--- a/src/get.c
+++ b/src/get.c
    02111-1307, USA. */
 
 #include <config.h>
-#include <assert.h>
+#include "error.h"
 #include <stdlib.h>
 #include "alloc.h"
+#include "case.h"
 #include "command.h"
 #include "error.h"
 #include "file-handle.h"
 
 #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
   {
@@ -59,19 +67,15 @@ static int save_write_case_func (struct ccase *, void *);
 static trns_proc_func save_trns_proc;
 static trns_free_func save_trns_free;
 
-#if DEBUGGING
-void dump_dict_variables (struct dictionary *);
-#endif
-
 /* Parses the GET command. */
 int
 cmd_get (void)
 {
   struct file_handle *handle;
   struct dictionary *dict;
+  struct get_pgm *pgm;
   int options = GTSV_OPT_NONE;
 
-  lex_match_id ("GET");
   discard_variables ();
 
   lex_match ('/');
@@ -86,35 +90,21 @@ cmd_get (void)
   if (dict == NULL)
     return CMD_FAILURE;
 
-#if DEBUGGING
-  dump_dict_variables (dict);
-#endif
   if (0 == trim_dictionary (dict, &options))
     {
       fh_close_handle (handle);
       return CMD_FAILURE;
     }
-#if DEBUGGING
-  dump_dict_variables (dict);
-#endif
 
   dict_compact_values (dict);
 
-#if DEBUGGING
-  printf (_("GET translation table from file to memory:\n"));
-  for (i = 0; i < dict->nvar; i++)
-    {
-      struct variable *v = dict->var[i];
-
-      printf (_("  %8s from %3d,%3d to %3d,%3d\n"), v->name,
-             v->get.fv, v->get.nv, v->fv, v->nv);
-    }
-#endif
-
   dict_destroy (default_dict);
   default_dict = dict;
 
-  vfm_source = create_case_source (&get_source_class, 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;
 }
@@ -139,8 +129,6 @@ cmd_save_internal (enum save_cmd save_cmd)
 
   int i;
 
-  lex_match_id ("SAVE");
-
   lex_match ('/');
   if (lex_match_id ("OUTFILE"))
     lex_match ('=');
@@ -150,9 +138,6 @@ cmd_save_internal (enum save_cmd save_cmd)
     return CMD_FAILURE;
 
   dict = dict_clone (default_dict);
-#if DEBUGGING
-  dump_dict_variables (dict);
-#endif
   for (i = 0; i < dict_get_var_cnt (dict); i++) 
     dict_get_var (dict, i)->aux = dict_get_var (default_dict, i);
   if (0 == trim_dictionary (dict, &options))
@@ -161,10 +146,6 @@ cmd_save_internal (enum save_cmd save_cmd)
       return CMD_FAILURE;
     }
 
-#if DEBUGGING
-  dump_dict_variables (dict);
-#endif
-
   /* Write dictionary. */
   inf.h = handle;
   inf.dict = dict;
@@ -190,7 +171,7 @@ cmd_save_internal (enum save_cmd save_cmd)
 
   if (save_cmd == CMD_SAVE)
     {
-      procedure (NULL, save_write_case_func, NULL, t);
+      procedure (save_write_case_func, t);
       save_trns_free (&t->h);
     }
   else 
@@ -228,7 +209,7 @@ do_write_case (struct save_trns *t, struct ccase *c)
       struct variable *v = t->var[i];
       if (v->type == NUMERIC)
        {
-         double src = c->data[v->fv].f;
+         double src = case_num (c, v->fv);
          if (src == SYSMIS)
            *p++ = -FLT64_MAX;
          else
@@ -236,7 +217,7 @@ do_write_case (struct save_trns *t, struct ccase *c)
        }
       else
        {
-         memcpy (p, c->data[v->fv].s, v->width);
+         memcpy (p, case_str (c, v->fv), v->width);
          memset (&((char *) p)[v->width], ' ',
                  REM_RND_UP (v->width, sizeof *p));
          p += DIV_RND_UP (v->width, sizeof *p);
@@ -246,21 +227,24 @@ do_write_case (struct save_trns *t, struct ccase *c)
   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)
+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)
+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)
 {
@@ -285,7 +269,7 @@ static int rename_variables (struct dictionary * dict);
 static int
 trim_dictionary (struct dictionary *dict, int *options)
 {
-  if (set_scompression)
+  if (get_scompression())
     *options |= GTSV_OPT_COMPRESSED;
 
   if (*options & GTSV_OPT_SAVE)
@@ -298,7 +282,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);
@@ -459,40 +443,29 @@ done:
 
   return success;
 }
-
-#if DEBUGGING
-void
-dump_dict_variables (struct dictionary * dict)
-{
-  int i;
-
-  printf (_("\nVariables in dictionary:\n"));
-  for (i = 0; i < dict->nvar; i++)
-    printf ("%s, ", dict->var[i]->name);
-  printf ("\n");
-}
-#endif
 \f
 /* Clears internal state related to GET input procedure. */
 static void
 get_source_destroy (struct case_source *source)
 {
-  struct file_handle *handle = source->aux;
+  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 (handle);
+  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 (struct case_source *source,
+                 struct ccase *c,
                  write_case_func *write_case, write_case_data wc_data)
 {
-  struct file_handle *handle = source->aux;
+  struct get_pgm *pgm = source->aux;
 
-  while (sfm_read_case (handle, temp_case->data, default_dict)
+  while (sfm_read_case (pgm->handle, c, default_dict)
         && write_case (wc_data))
     ;
 }
@@ -530,30 +503,30 @@ struct mtf_file
     struct dictionary *dict;   /* Dictionary from system file. */
     char in[9];                        /* Name of the variable from IN=. */
     char first[9], last[9];    /* Name of the variables from FIRST=, LAST=. */
-    union value *input;                /* Input record. */
+    struct ccase input;         /* Input record. */
   };
 
-/* All the files mentioned on FILE or TABLE. */
-static struct mtf_file *mtf_head, *mtf_tail;
-
-/* Variables on the BY subcommand. */
-static struct variable **mtf_by;
-static int mtf_n_by;
-
-/* Master dictionary. */
-static struct dictionary *mtf_master;
+/* MATCH FILES procedure. */
+struct mtf_proc 
+  {
+    struct mtf_file *head;      /* First file mentioned on FILE or TABLE. */
+    struct mtf_file *tail;      /* Last file mentioned on FILE or TABLE. */
+    
+    struct variable **by;       /* Variables on the BY subcommand. */
+    size_t by_cnt;              /* Number of variables on BY subcommand. */
 
-/* Used to determine whether we've already initialized this
-   variable. */
-static unsigned mtf_seq_num;
+    struct dictionary *dict;    /* Dictionary of output file. */
+    struct case_sink *sink;     /* Sink to receive output. */
+    struct ccase *mtf_case;     /* Case used for output. */
 
-/* Sequence numbers for each variable in mtf_master. */
-static unsigned *mtf_seq_nums;
+    unsigned seq_num;           /* Have we initialized this variable? */
+    unsigned *seq_nums;         /* Sequence numbers for each var in dict. */
+  };
 
-static void mtf_free (void);
-static void mtf_free_file (struct mtf_file *file);
-static int mtf_merge_dictionary (struct mtf_file *f);
-static void mtf_delete_file_in_place (struct mtf_file **file);
+static void mtf_free (struct mtf_proc *);
+static void mtf_free_file (struct mtf_file *);
+static int mtf_merge_dictionary (struct dictionary *const, struct mtf_file *);
+static void mtf_delete_file_in_place (struct mtf_proc *, struct mtf_file **);
 
 static void mtf_read_nonactive_records (void *);
 static void mtf_processing_finish (void *);
@@ -565,20 +538,20 @@ static char *var_type_description (struct variable *);
 int
 cmd_match_files (void)
 {
+  struct mtf_proc mtf;
   struct mtf_file *first_table = NULL;
   
   int seen = 0;
   
-  lex_match_id ("MATCH");
-  lex_match_id ("FILES");
-
-  mtf_head = mtf_tail = NULL;
-  mtf_by = NULL;
-  mtf_n_by = 0;
-  mtf_master = dict_create ();
-  mtf_seq_num = 0;
-  mtf_seq_nums = NULL;
-  dict_set_case_limit (mtf_master, dict_get_case_limit (default_dict));
+  mtf.head = mtf.tail = NULL;
+  mtf.by = NULL;
+  mtf.by_cnt = 0;
+  mtf.dict = dict_create ();
+  mtf.sink = NULL;
+  mtf.mtf_case = NULL;
+  mtf.seq_num = 0;
+  mtf.seq_nums = NULL;
+  dict_set_case_limit (mtf.dict, dict_get_case_limit (default_dict));
   
   do
     {
@@ -594,7 +567,7 @@ cmd_match_files (void)
          seen |= 1;
              
          lex_match ('=');
-         if (!parse_variables (mtf_master, &mtf_by, &mtf_n_by,
+         if (!parse_variables (mtf.dict, &mtf.by, &mtf.by_cnt,
                                PV_NO_DUPLICATE | PV_NO_SCRATCH))
            goto lossage;
        }
@@ -610,7 +583,7 @@ cmd_match_files (void)
          file->in[0] = file->first[0] = file->last[0] = '\0';
          file->dict = NULL;
          file->by = NULL;
-         file->input = NULL;
+          case_nullify (&file->input);
 
          if (lex_match_id ("FILE"))
            file->type = MTF_FILE;
@@ -626,12 +599,12 @@ cmd_match_files (void)
          if (file->type == MTF_TABLE || first_table == NULL)
            {
              file->next = NULL;
-             file->prev = mtf_tail;
-             if (mtf_tail)
-               mtf_tail->next = file;
-             mtf_tail = file;
-             if (mtf_head == NULL)
-               mtf_head = file;
+             file->prev = mtf.tail;
+             if (mtf.tail)
+               mtf.tail->next = file;
+             mtf.tail = file;
+             if (mtf.head == NULL)
+               mtf.head = file;
              if (file->type == MTF_TABLE && first_table == NULL)
                first_table = file;
            }
@@ -643,7 +616,7 @@ cmd_match_files (void)
              if (first_table->prev)
                first_table->prev->next = file;
              else
-               mtf_head = file;
+               mtf.head = file;
              first_table->prev = file;
            }
          
@@ -668,6 +641,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
            {
@@ -681,10 +663,11 @@ cmd_match_files (void)
              file->dict = sfm_read_dictionary (file->handle, NULL);
              if (!file->dict)
                goto lossage;
+              case_create (&file->input, dict_get_next_value_idx (file->dict));
            }
          else
            file->dict = default_dict;
-         if (!mtf_merge_dictionary (file))
+         if (!mtf_merge_dictionary (mtf.dict, file))
            goto lossage;
        }
       else if (lex_id_match ("IN", tokid)
@@ -694,7 +677,7 @@ cmd_match_files (void)
          const char *sbc;
          char *name;
          
-         if (mtf_tail == NULL)
+         if (mtf.tail == NULL)
            {
              msg (SE, _("IN, FIRST, and LAST subcommands may not occur "
                         "before the first FILE or TABLE."));
@@ -703,21 +686,24 @@ cmd_match_files (void)
 
          if (lex_match_id ("IN"))
            {
-             name = mtf_tail->in;
+             name = mtf.tail->in;
              sbc = "IN";
            }
          else if (lex_match_id ("FIRST"))
            {
-             name = mtf_tail->first;
+             name = mtf.tail->first;
              sbc = "FIRST";
            }
          else if (lex_match_id ("LAST"))
            {
-             name = mtf_tail->last;
+             name = mtf.tail->last;
              sbc = "LAST";
            }
-         else
-           assert (0);
+         else 
+            {
+              assert (0);
+              abort ();
+            }
 
          lex_match ('=');
          if (token != T_ID)
@@ -736,7 +722,7 @@ cmd_match_files (void)
          strcpy (name, tokid);
          lex_get ();
 
-         if (!dict_create_var (mtf_master, name, 0))
+         if (!dict_create_var (mtf.dict, name, 0))
            {
              msg (SE, _("Duplicate variable name %s while creating %s "
                         "variable."),
@@ -750,14 +736,14 @@ cmd_match_files (void)
        {
          int options = GTSV_OPT_MATCH_FILES;
          
-         if (mtf_tail == NULL)
+         if (mtf.tail == NULL)
            {
              msg (SE, _("RENAME, KEEP, and DROP subcommands may not occur "
                         "before the first FILE or TABLE."));
              goto lossage;
            }
 
-         if (!trim_dictionary (mtf_tail->dict, &options))
+         if (!trim_dictionary (mtf.tail->dict, &options))
            goto lossage;
        }
       else if (lex_match_id ("MAP"))
@@ -786,35 +772,26 @@ cmd_match_files (void)
     {
       struct mtf_file *iter;
 
-      for (iter = mtf_head; iter; iter = iter->next)
+      for (iter = mtf.head; iter; iter = iter->next)
        {
          int i;
          
-         iter->by = xmalloc (sizeof *iter->by * mtf_n_by);
+         iter->by = xmalloc (sizeof *iter->by * mtf.by_cnt);
 
-         for (i = 0; i < mtf_n_by; i++)
+         for (i = 0; i < mtf.by_cnt; i++)
            {
-             iter->by[i] = dict_lookup_var (iter->dict, mtf_by[i]->name);
+             iter->by[i] = dict_lookup_var (iter->dict, mtf.by[i]->name);
              if (iter->by[i] == NULL)
                {
                  msg (SE, _("File %s lacks BY variable %s."),
-                      iter->handle ? fh_handle_name (iter->handle) : "*",
-                      mtf_by[i]->name);
+                      iter->handle ? handle_get_name (iter->handle) : "*",
+                      mtf.by[i]->name);
                  goto lossage;
                }
            }
        }
     }
 
-#if DEBUGGING
-  {
-    /* From sfm-read.c. */
-    extern void dump_dictionary (struct dictionary *);
-
-    dump_dictionary (mtf_master);
-  }
-#endif
-
   /* MATCH FILES performs an n-way merge on all its input files.
      Abstract algorithm:
 
@@ -848,53 +825,55 @@ 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.dict, 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.seq_nums = xmalloc (dict_get_var_cnt (mtf.dict)
+                          * sizeof *mtf.seq_nums);
+  memset (mtf.seq_nums, 0,
+          dict_get_var_cnt (mtf.dict) * sizeof *mtf.seq_nums);
+  mtf.mtf_case = xmalloc (dict_get_case_size (mtf.dict));
 
-  process_active_file (mtf_read_nonactive_records, mtf_processing,
-                      mtf_processing_finish, NULL);
-  mtf_master = NULL;
+  mtf_read_nonactive_records (NULL);
+  if (seen & 2)
+    procedure (mtf_processing, NULL);
+  mtf_processing_finish (NULL);
+
+  dict_destroy (default_dict);
+  default_dict = mtf.dict;
+  mtf.dict = NULL;
+  vfm_source = mtf.sink->class->make_source (mtf.sink);
+  free_case_sink (mtf.sink);
   
-  mtf_free ();
+  mtf_free (&mtf);
   return CMD_SUCCESS;
   
 lossage:
-  mtf_free ();
+  mtf_free (&mtf);
   return CMD_FAILURE;
 }
 
 /* Repeats 2...8 an arbitrary number of times. */
 static void
-mtf_processing_finish (void *aux UNUSED)
+mtf_processing_finish (void *mtf_)
 {
+  struct mtf_proc *mtf = mtf_;
+  struct mtf_file *iter;
+
   /* Find the active file and delete it. */
-  {
-    struct mtf_file *iter;
-    
-    for (iter = mtf_head; iter; iter = iter->next)
-      if (iter->handle == NULL)
-       {
-         mtf_delete_file_in_place (&iter);
-         break;
-       }
-  }
+  for (iter = mtf->head; iter; iter = iter->next)
+    if (iter->handle == NULL)
+      {
+        mtf_delete_file_in_place (mtf, &iter);
+        break;
+      }
   
-  while (mtf_head && mtf_head->type == MTF_FILE)
-    if (!mtf_processing (temp_case, NULL))
+  while (mtf->head && mtf->head->type == MTF_FILE)
+    if (!mtf_processing (NULL, mtf))
       break;
 }
 
@@ -929,33 +908,33 @@ mtf_free_file (struct mtf_file *file)
     dict_destroy (file->dict);
   free (file->by);
   if (file->handle)
-    free (file->input);
+    case_destroy (&file->input);
   free (file);
 }
 
 /* Free all the data for the MATCH FILES procedure. */
 static void
-mtf_free (void)
+mtf_free (struct mtf_proc *mtf)
 {
   struct mtf_file *iter, *next;
 
-  for (iter = mtf_head; iter; iter = next)
+  for (iter = mtf->head; iter; iter = next)
     {
       next = iter->next;
 
       mtf_free_file (iter);
     }
   
-  free (mtf_by);
-  if (mtf_master)
-    dict_destroy (mtf_master);
-  free (mtf_seq_nums);
+  free (mtf->by);
+  if (mtf->dict)
+    dict_destroy (mtf->dict);
+  free (mtf->seq_nums);
 }
 
 /* Remove *FILE from the mtf_file chain.  Make *FILE point to the next
    file in the chain, or to NULL if was the last in the chain. */
 static void
-mtf_delete_file_in_place (struct mtf_file **file)
+mtf_delete_file_in_place (struct mtf_proc *mtf, struct mtf_file **file)
 {
   struct mtf_file *f = *file;
 
@@ -963,10 +942,10 @@ mtf_delete_file_in_place (struct mtf_file **file)
     f->prev->next = f->next;
   if (f->next)
     f->next->prev = f->prev;
-  if (f == mtf_head)
-    mtf_head = f->next;
-  if (f == mtf_tail)
-    mtf_tail = f->prev;
+  if (f == mtf->head)
+    mtf->head = f->next;
+  if (f == mtf->tail)
+    mtf->tail = f->prev;
   *file = f->next;
 
   {
@@ -975,60 +954,61 @@ mtf_delete_file_in_place (struct mtf_file **file)
     for (i = 0; i < dict_get_var_cnt (f->dict); i++)
       {
        struct variable *v = dict_get_var (f->dict, i);
+        union value *out = case_data_rw (mtf->mtf_case, v->p.mtf.master->fv);
          
        if (v->type == NUMERIC)
-         compaction_case->data[v->p.mtf.master->fv].f = SYSMIS;
+          out->f = SYSMIS;
        else
-         memset (compaction_case->data[v->p.mtf.master->fv].s, ' ',
-                 v->width);
+         memset (out->s, ' ', v->width);
       }
   }
-  
+
   mtf_free_file (f);
 }
 
 /* Read a record from every input file except the active file. */
 static void
-mtf_read_nonactive_records (void *aux UNUSED)
+mtf_read_nonactive_records (void *mtf_ UNUSED)
 {
+  struct mtf_proc *mtf = mtf_;
   struct mtf_file *iter;
 
-  for (iter = mtf_head; iter; )
+  for (iter = mtf->head; iter; )
     {
       if (iter->handle)
        {
-         assert (iter->input == NULL);
-         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);
+         if (!sfm_read_case (iter->handle, &iter->input, iter->dict))
+           mtf_delete_file_in_place (mtf, &iter);
          else
            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_proc *mtf,
+                       struct mtf_file *a, struct mtf_file *b,
+                       struct ccase *c)
 {
+  struct ccase *a_input, *b_input;
   int i;
-  
-  for (i = 0; i < mtf_n_by; i++)
+
+  assert ((a == NULL) + (b == NULL) + (c == NULL) <= 1);
+  a_input = case_is_null (&a->input) ? c : &a->input;
+  b_input = case_is_null (&b->input) ? c : &b->input;
+  for (i = 0; i < mtf->by_cnt; i++)
     {
       assert (a->by[i]->type == b->by[i]->type);
       assert (a->by[i]->width == b->by[i]->width);
       
       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 = case_num (a_input, a->by[i]->fv);
+         double bf = case_num (b_input, b->by[i]->fv);
 
          if (af < bf)
            return -1;
@@ -1040,8 +1020,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 (case_str (a_input, a->by[i]->fv),
+                          case_str (b_input, b->by[i]->fv),
                           a->by[i]->width);
          if (result < 0)
            return -1;
@@ -1054,16 +1034,12 @@ 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 *mtf_ UNUSED)
 {
-  /* List of files with minimum BY values. */
-  struct mtf_file *min_head, *min_tail;
-
-  /* List of files with non-minimum BY values. */
-  struct mtf_file *max_head, *max_tail;
-
-  /* Iterator. */
-  struct mtf_file *iter;
+  struct mtf_proc *mtf = mtf_;
+  struct mtf_file *min_head, *min_tail; /* Files with minimum BY values. */
+  struct mtf_file *max_head, *max_tail; /* Files with non-minimum BY values. */
+  struct mtf_file *iter;                /* Iterator. */
 
   for (;;)
     {
@@ -1071,7 +1047,7 @@ mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
         return because that would cause a record to be skipped. */
       int advance = 1;
 
-      if (mtf_head->type == MTF_TABLE)
+      if (mtf->head->type == MTF_TABLE)
        return 0;
       
       /* 3. Find the FILE input record with minimum BY values.  Store
@@ -1080,11 +1056,11 @@ mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
         4. Find all the FILE input records with BY values identical
         to the minimums.  Store all the values from these input
         records into the output record. */
-      min_head = min_tail = mtf_head;
+      min_head = min_tail = mtf->head;
       max_head = max_tail = NULL;
-      for (iter = mtf_head->next; iter && iter->type == MTF_FILE;
+      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 (mtf, min_head, iter, c))
          {
          case -1:
            if (max_head)
@@ -1129,7 +1105,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 (mtf, min_head, iter, c))
            {
            case -1:
              if (max_head)
@@ -1145,9 +1121,9 @@ mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
            case 1:
              if (iter->handle == NULL)
                return 1;
-             if (sfm_read_case (iter->handle, iter->input, iter->dict))
+             if (sfm_read_case (iter->handle, &iter->input, iter->dict))
                goto again;
-             mtf_delete_file_in_place (&iter);
+             mtf_delete_file_in_place (mtf, &iter);
              break;
 
            default:
@@ -1158,7 +1134,7 @@ mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
        }
 
       /* Next sequence number. */
-      mtf_seq_num++;
+      mtf->seq_num++;
   
       /* Store data to all the records we are using. */
       if (min_tail)
@@ -1170,26 +1146,21 @@ 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);
+              struct ccase *record;
+              union value *out;
          
-             if (mtf_seq_nums[v->p.mtf.master->index] == mtf_seq_num)
+             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
+              mtf->seq_nums[v->p.mtf.master->index] = mtf->seq_num;
+
+              record = case_is_null (&iter->input) ? c : &iter->input;
+
+              assert (v->type == NUMERIC || v->type == ALPHA);
+              out = case_data_rw (mtf->mtf_case, v->p.mtf.master->fv);
              if (v->type == NUMERIC)
-               compaction_case->data[v->p.mtf.master->fv].f
-                 = iter->input[v->fv].f;
+               out->f = case_num (record, v->fv);
              else
-               {
-                 assert (v->type == ALPHA);
-                 memcpy (compaction_case->data[v->p.mtf.master->fv].s,
-                         iter->input[v->fv].s, v->width);
-               }
+                memcpy (out->s, case_str (record, v->fv), v->width);
            }
        }
 
@@ -1203,22 +1174,17 @@ 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 *out;
          
-             if (mtf_seq_nums[v->p.mtf.master->index] == mtf_seq_num)
+             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\n",
-                     fh_handle_name (iter->handle),
-                     v->name,
-                     v->p.mtf.master->fv);
-#endif
+              mtf->seq_nums[v->p.mtf.master->index] = mtf->seq_num;
+
+              out = case_data_rw (mtf->mtf_case, v->p.mtf.master->fv);
              if (v->type == NUMERIC)
-               compaction_case->data[v->p.mtf.master->fv].f = SYSMIS;
+                out->f = SYSMIS;
              else
-                memset (compaction_case->data[v->p.mtf.master->fv].s, ' ',
-                        v->width);
+                memset (out->s, ' ', v->width);
            }
 
          if (iter->handle == NULL)
@@ -1226,7 +1192,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->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
@@ -1238,10 +1204,8 @@ mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
          
          if (iter->handle)
            {
-             assert (iter->input != NULL);
-
-             if (!sfm_read_case (iter->handle, iter->input, iter->dict))
-               mtf_delete_file_in_place (&iter);
+             if (!sfm_read_case (iter->handle, &iter->input, iter->dict))
+               mtf_delete_file_in_place (mtf, &iter);
            }
 
          iter = next;
@@ -1251,15 +1215,14 @@ mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
        break;
     }
 
-  return (mtf_head && mtf_head->type != MTF_TABLE);
+  return (mtf->head && mtf->head->type != MTF_TABLE);
 }
 
 /* Merge the dictionary for file F into the master dictionary
-   mtf_master. */
+   mtf_dict. */
 static int
-mtf_merge_dictionary (struct mtf_file *f)
+mtf_merge_dictionary (struct dictionary *const m, struct mtf_file *f)
 {
-  struct dictionary *const m = mtf_master;
   struct dictionary *d = f->dict;
   const char *d_docs, *m_docs;
 
@@ -1319,7 +1282,7 @@ mtf_merge_dictionary (struct mtf_file *f)
            msg (SE, _("Variable %s in file %s (%s) has different "
                       "type or width from the same variable in "
                       "earlier file (%s)."),
-                dv->name, fh_handle_name (f->handle),
+                dv->name, handle_get_name (f->handle),
                 var_type_description (dv), var_type_description (mv));
            return 0;
          }
@@ -1338,11 +1301,10 @@ cmd_import (void)
 {
   struct file_handle *handle = NULL;
   struct dictionary *dict;
+  struct get_pgm *pgm;
   int options = GTSV_OPT_NONE;
   int type;
 
-  lex_match_id ("IMPORT");
-
   for (;;)
     {
       lex_match ('/');
@@ -1383,35 +1345,21 @@ cmd_import (void)
   if (dict == NULL)
     return CMD_FAILURE;
 
-#if DEBUGGING
-  dump_dict_variables (dict);
-#endif
   if (0 == trim_dictionary (dict, &options))
     {
       fh_close_handle (handle);
       return CMD_FAILURE;
     }
-#if DEBUGGING
-  dump_dict_variables (dict);
-#endif
 
   dict_compact_values (dict);
 
-#if DEBUGGING
-  printf (_("IMPORT translation table from file to memory:\n"));
-  for (i = 0; i < dict->nvar; i++)
-    {
-      struct variable *v = dict->var[i];
-
-      printf (_("  %8s from %3d,%3d to %3d,%3d\n"), v->name,
-             v->get.fv, v->get.nv, v->fv, v->nv);
-    }
-#endif
-
   dict_destroy (default_dict);
   default_dict = dict;
 
-  vfm_source = create_case_source (&import_source_class, 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;
 }
@@ -1420,10 +1368,12 @@ cmd_import (void)
    write_case(). */
 static void
 import_source_read (struct case_source *source,
+                    struct ccase *c,
                     write_case_func *write_case, write_case_data wc_data)
 {
-  struct file_handle *handle = source->aux;
-  while (pfm_read_case (handle, temp_case->data, default_dict))
+  struct get_pgm *pgm = source->aux;
+  
+  while (pfm_read_case (pgm->handle, c, default_dict))
     if (!write_case (wc_data))
       break;
 }
@@ -1451,8 +1401,6 @@ cmd_export (void)
 
   int i;
 
-  lex_match_id ("EXPORT");
-
   lex_match ('/');
   if (lex_match_id ("OUTFILE"))
     lex_match ('=');
@@ -1462,9 +1410,6 @@ cmd_export (void)
     return CMD_FAILURE;
 
   dict = dict_clone (default_dict);
-#if DEBUGGING
-  dump_dict_variables (dict);
-#endif
   for (i = 0; i < dict_get_var_cnt (dict); i++)
     dict_get_var (dict, i)->aux = dict_get_var (default_dict, i);
   if (0 == trim_dictionary (dict, &options))
@@ -1473,10 +1418,6 @@ cmd_export (void)
       return CMD_FAILURE;
     }
 
-#if DEBUGGING
-  dump_dict_variables (dict);
-#endif
-
   /* Write dictionary. */
   if (!pfm_write_dictionary (handle, dict))
     {
@@ -1497,12 +1438,13 @@ cmd_export (void)
   t->case_buf = xmalloc (sizeof *t->case_buf * t->nvar);
   dict_destroy (dict);
 
-  procedure (NULL, export_write_case_func, NULL, t);
+  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)
 {
@@ -1515,9 +1457,9 @@ export_write_case_func (struct ccase *c, void *aux)
       struct variable *v = t->var[i];
 
       if (v->type == NUMERIC)
-       *p++ = c->data[v->fv];
+       (*p++).f = case_num (c, v->fv);
       else
-       (*p++).c = c->data[v->fv].s;
+       (*p++).c = (char *) case_str (c, v->fv);
     }
 
   pfm_write_case (t->f, (union value *) t->case_buf);