Completely rewrite src/data/format.[ch], to achieve better
[pspp-builds.git] / src / language / data-io / get.c
index 052dbeabbbd63931189aa120c833fb0ffb451a9c..d79206d361754ec7ae0c71b4c5a55952861434fe 100644 (file)
    02110-1301, USA. */
 
 #include <config.h>
-#include <libpspp/message.h>
+
 #include <stdlib.h>
-#include <libpspp/alloc.h>
+
 #include <data/any-reader.h>
 #include <data/any-writer.h>
+#include <data/case-sink.h>
+#include <data/case-source.h>
 #include <data/case.h>
-#include <language/command.h>
-#include <libpspp/compiler.h>
+#include <data/casefile.h>
+#include <data/fastfile.h>
 #include <data/dictionary.h>
-#include <libpspp/message.h>
-#include <language/data-io/file-handle.h>
-#include <libpspp/hash.h>
-#include <language/lexer/lexer.h>
-#include <libpspp/misc.h>
 #include <data/por-file-writer.h>
+#include <data/procedure.h>
 #include <data/settings.h>
+#include <data/storage-stream.h>
 #include <data/sys-file-writer.h>
-#include <libpspp/str.h>
+#include <data/transformations.h>
 #include <data/value-labels.h>
 #include <data/variable.h>
-#include <procedure.h>
+#include <language/command.h>
+#include <language/data-io/file-handle.h>
+#include <language/lexer/lexer.h>
+#include <language/lexer/variable-parser.h>
+#include <libpspp/alloc.h>
+#include <libpspp/assertion.h>
+#include <libpspp/compiler.h>
+#include <libpspp/hash.h>
+#include <libpspp/message.h>
+#include <libpspp/message.h>
+#include <libpspp/misc.h>
+#include <libpspp/str.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-#include <libpspp/debug-print.h>
-
 /* Rearranging and reducing a dictionary. */
 static void start_case_map (struct dictionary *);
 static struct case_map *finish_case_map (struct dictionary *);
@@ -77,7 +85,7 @@ static void case_reader_pgm_free (struct case_reader_pgm *);
 
 /* Parses a GET or IMPORT command. */
 static int
-parse_read_command (enum reader_command type)
+parse_read_command (struct dataset *ds, enum reader_command type)
 {
   struct case_reader_pgm *pgm = NULL;
   struct file_handle *fh = NULL;
@@ -119,7 +127,7 @@ parse_read_command (enum reader_command type)
       goto error;
     }
               
-  discard_variables ();
+  discard_variables (ds);
 
   pgm = xmalloc (sizeof *pgm);
   pgm->reader = any_reader_open (fh, &dict);
@@ -141,10 +149,11 @@ parse_read_command (enum reader_command type)
 
   pgm->map = finish_case_map (dict);
   
-  dict_destroy (default_dict);
-  default_dict = dict;
+  dict_destroy (dataset_dict (ds));
+  dataset_set_dict (ds, dict);
 
-  vfm_source = create_case_source (&case_reader_source_class, pgm);
+  proc_set_source (ds, 
+                  create_case_source (&case_reader_source_class, pgm));
 
   return CMD_SUCCESS;
 
@@ -218,16 +227,16 @@ static const struct case_source_class case_reader_source_class =
 \f
 /* GET. */
 int
-cmd_get (void
+cmd_get (struct dataset *ds
 {
-  return parse_read_command (GET_CMD);
+  return parse_read_command (ds, GET_CMD);
 }
 
 /* IMPORT. */
 int
-cmd_import (void
+cmd_import (struct dataset *ds
 {
-  return parse_read_command (IMPORT_CMD);
+  return parse_read_command (ds, IMPORT_CMD);
 }
 \f
 /* Writing system and portable files. */ 
@@ -281,7 +290,8 @@ case_writer_destroy (struct case_writer *aw)
 
    On failure, returns a null pointer. */
 static struct case_writer *
-parse_write_command (enum writer_type writer_type,
+parse_write_command (struct dataset *ds, 
+                    enum writer_type writer_type,
                      enum command_type command_type,
                      bool *retain_unselected)
 {
@@ -304,7 +314,7 @@ parse_write_command (enum writer_type writer_type,
     *retain_unselected = true;
 
   handle = NULL;
-  dict = dict_clone (default_dict);
+  dict = dict_clone (dataset_dict (ds));
   aw = xmalloc (sizeof *aw);
   aw->writer = NULL;
   aw->map = NULL;
@@ -434,6 +444,8 @@ parse_write_command (enum writer_type writer_type,
     }
   else
     aw->writer = any_writer_open (handle, dict);
+  if (aw->writer == NULL)
+    goto error;
   dict_destroy (dict);
   
   return aw;
@@ -446,7 +458,7 @@ parse_write_command (enum writer_type writer_type,
 
 /* Writes case C to writer AW. */
 static bool
-case_writer_write_case (struct case_writer *aw, struct ccase *c) 
+case_writer_write_case (struct case_writer *aw, const struct ccase *c) 
 {
   if (aw->map != NULL) 
     {
@@ -458,26 +470,26 @@ case_writer_write_case (struct case_writer *aw, struct ccase *c)
 \f
 /* SAVE and EXPORT. */
 
-static bool output_proc (struct ccase *, void *);
+static bool output_proc (const struct ccase *, void *, const struct dataset *);
 
 /* Parses and performs the SAVE or EXPORT procedure. */
 static int
-parse_output_proc (enum writer_type writer_type)
+parse_output_proc (struct dataset *ds, enum writer_type writer_type)
 {
   bool retain_unselected;
   struct variable *saved_filter_variable;
   struct case_writer *aw;
   bool ok;
 
-  aw = parse_write_command (writer_type, PROC_CMD, &retain_unselected);
+  aw = parse_write_command (ds, writer_type, PROC_CMD, &retain_unselected);
   if (aw == NULL) 
     return CMD_CASCADING_FAILURE;
 
-  saved_filter_variable = dict_get_filter (default_dict);
+  saved_filter_variable = dict_get_filter (dataset_dict (ds));
   if (retain_unselected) 
-    dict_set_filter (default_dict, NULL);
-  ok = procedure (output_proc, aw);
-  dict_set_filter (default_dict, saved_filter_variable);
+    dict_set_filter (dataset_dict (ds), NULL);
+  ok = procedure (ds, output_proc, aw);
+  dict_set_filter (dataset_dict (ds), saved_filter_variable);
 
   case_writer_destroy (aw);
   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
@@ -485,22 +497,22 @@ parse_output_proc (enum writer_type writer_type)
 
 /* Writes case C to file. */
 static bool
-output_proc (struct ccase *c, void *aw_
+output_proc (const struct ccase *c, void *aw_, const struct dataset *ds UNUSED
 {
   struct case_writer *aw = aw_;
   return case_writer_write_case (aw, c);
 }
 
 int
-cmd_save (void
+cmd_save (struct dataset *ds
 {
-  return parse_output_proc (SYSFILE_WRITER);
+  return parse_output_proc (ds, SYSFILE_WRITER);
 }
 
 int
-cmd_export (void
+cmd_export (struct dataset *ds
 {
-  return parse_output_proc (PORFILE_WRITER);
+  return parse_output_proc (ds, PORFILE_WRITER);
 }
 \f
 /* XSAVE and XEXPORT. */
@@ -516,23 +528,23 @@ static trns_free_func output_trns_free;
 
 /* Parses the XSAVE or XEXPORT transformation command. */
 static int
-parse_output_trns (enum writer_type writer_type) 
+parse_output_trns (struct dataset *ds, enum writer_type writer_type) 
 {
   struct output_trns *t = xmalloc (sizeof *t);
-  t->aw = parse_write_command (writer_type, XFORM_CMD, NULL);
+  t->aw = parse_write_command (ds, writer_type, XFORM_CMD, NULL);
   if (t->aw == NULL) 
     {
       free (t);
       return CMD_CASCADING_FAILURE;
     }
 
-  add_transformation (output_trns_proc, output_trns_free, t);
+  add_transformation (ds, output_trns_proc, output_trns_free, t);
   return CMD_SUCCESS;
 }
 
 /* Writes case C to the system file specified on XSAVE or XEXPORT. */
 static int
-output_trns_proc (void *trns_, struct ccase *c, int case_num UNUSED)
+output_trns_proc (void *trns_, struct ccase *c, casenumber case_num UNUSED)
 {
   struct output_trns *t = trns_;
   case_writer_write_case (t->aw, c);
@@ -557,16 +569,16 @@ output_trns_free (void *trns_)
 
 /* XSAVE command. */
 int
-cmd_xsave (void
+cmd_xsave (struct dataset *ds
 {
-  return parse_output_trns (SYSFILE_WRITER);
+  return parse_output_trns (ds, SYSFILE_WRITER);
 }
 
 /* XEXPORT command. */
 int
-cmd_xexport (void
+cmd_xexport (struct dataset *ds
 {
-  return parse_output_trns (PORFILE_WRITER);
+  return parse_output_trns (ds, PORFILE_WRITER);
 }
 \f
 static bool rename_variables (struct dictionary *dict);
@@ -618,7 +630,7 @@ rename_variables (struct dictionary *dict)
     {
       struct variable *v;
 
-      v = parse_dict_variable (dict);
+      v = parse_variable (dict);
       if (v == NULL)
        return 0;
       if (!lex_force_match ('=')
@@ -735,8 +747,6 @@ keep_variables (struct dictionary *dict)
 \f
 /* MATCH FILES. */
 
-#include <libpspp/debug-print.h>
-
 /* File types. */
 enum
   {
@@ -777,7 +787,7 @@ struct mtf_proc
     char first[LONG_NAME_LEN + 1], last[LONG_NAME_LEN + 1];
     
     struct dictionary *dict;    /* Dictionary of output file. */
-    struct case_sink *sink;     /* Sink to receive output. */
+    struct casefile *output;    /* MATCH FILES output. */
     struct ccase mtf_case;      /* Case used for output. */
 
     unsigned seq_num;           /* Have we initialized this variable? */
@@ -790,8 +800,8 @@ static int mtf_merge_dictionary (struct dictionary *const, struct mtf_file *);
 static bool mtf_delete_file_in_place (struct mtf_proc *, struct mtf_file **);
 
 static bool mtf_read_nonactive_records (void *);
-static bool mtf_processing_finish (void *);
-static bool mtf_processing (struct ccase *, void *);
+static bool mtf_processing_finish (void *, const struct dataset *);
+static bool mtf_processing (const struct ccase *, void *, const struct dataset *);
 
 static char *var_type_description (struct variable *);
 
@@ -800,7 +810,7 @@ static struct variable *get_master (struct variable *);
 
 /* Parse and execute the MATCH FILES command. */
 int
-cmd_match_files (void)
+cmd_match_files (struct dataset *ds)
 {
   struct mtf_proc mtf;
   struct mtf_file *first_table = NULL;
@@ -817,11 +827,11 @@ cmd_match_files (void)
   mtf.first[0] = '\0';
   mtf.last[0] = '\0';
   mtf.dict = dict_create ();
-  mtf.sink = NULL;
+  mtf.output = NULL;
   case_nullify (&mtf.mtf_case);
   mtf.seq_num = 0;
   mtf.seq_nums = NULL;
-  dict_set_case_limit (mtf.dict, dict_get_case_limit (default_dict));
+  dict_set_case_limit (mtf.dict, dict_get_case_limit (dataset_dict (ds)));
 
   lex_match ('/');
   while (token == T_ID
@@ -837,7 +847,7 @@ cmd_match_files (void)
           saw_table = true;
         }
       else
-        assert (0);
+        NOT_REACHED ();
       lex_match ('=');
 
       file->by = NULL;
@@ -886,24 +896,20 @@ cmd_match_files (void)
             }
           used_active_file = true;
 
-          assert (pgm_state != STATE_INPUT);
-          if (pgm_state == STATE_INIT)
+          if (!proc_has_source (ds))
             {
               msg (SE, _("Cannot specify the active file since no active "
                          "file has been defined."));
               goto error;
             }
 
-          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 (); 
-            }
+          if (proc_make_temporary_transformations_permanent (ds))
+            msg (SE,
+                 _("MATCH FILES may not be used after TEMPORARY when "
+                   "the active file is an input source.  "
+                   "Temporary transformations will be made permanent."));
 
-          file->dict = default_dict;
+          file->dict = dataset_dict (ds);
         }
       else
         {
@@ -1083,7 +1089,7 @@ cmd_match_files (void)
             goto error;
           }
         iter->in_var->print = iter->in_var->write
-          = make_output_format (FMT_F, 1, 0);
+          = fmt_for_output (FMT_F, 1, 0);
       }
     
   /* MATCH FILES performs an n-way merge on all its input files.
@@ -1120,13 +1126,10 @@ cmd_match_files (void)
      values. */
 
   if (!used_active_file)
-    discard_variables ();
+    discard_variables (ds);
 
   dict_compact_values (mtf.dict);
-  mtf.sink = create_case_sink (&storage_sink_class, mtf.dict, NULL);
-  if (mtf.sink->class->open != NULL)
-    mtf.sink->class->open (mtf.sink);
-
+  mtf.output = fastfile_create (dict_get_next_value_idx (mtf.dict));
   mtf.seq_nums = xcalloc (dict_get_var_cnt (mtf.dict), sizeof *mtf.seq_nums);
   case_create (&mtf.mtf_case, dict_get_next_value_idx (mtf.dict));
 
@@ -1134,18 +1137,24 @@ cmd_match_files (void)
     goto error;
 
   if (used_active_file) 
-    ok = procedure (mtf_processing, &mtf) && mtf_processing_finish (&mtf);
+    {
+      proc_set_sink (ds, 
+                    create_case_sink (&null_sink_class, 
+                                      dataset_dict (ds), NULL));
+      ok = 
+       ( procedure (ds, mtf_processing, &mtf) && 
+         mtf_processing_finish (&mtf, ds) ); 
+    }
   else
-    ok = mtf_processing_finish (&mtf);
+    ok = mtf_processing_finish (&mtf, ds);
 
-  free_case_source (vfm_source);
-  vfm_source = NULL;
+  discard_variables (ds);
 
-  dict_destroy (default_dict);
-  default_dict = mtf.dict;
+  dict_destroy (dataset_dict (ds));
+  dataset_set_dict (ds, mtf.dict);
   mtf.dict = NULL;
-  vfm_source = mtf.sink->class->make_source (mtf.sink);
-  free_case_sink (mtf.sink);
+  proc_set_source (ds, storage_source_create (mtf.output));
+  mtf.output = NULL;
   
   if (!mtf_free (&mtf))
     ok = false;
@@ -1158,7 +1167,7 @@ cmd_match_files (void)
 
 /* Repeats 2...7 an arbitrary number of times. */
 static bool
-mtf_processing_finish (void *mtf_)
+mtf_processing_finish (void *mtf_, const struct dataset *ds)
 {
   struct mtf_proc *mtf = mtf_;
   struct mtf_file *iter;
@@ -1168,12 +1177,12 @@ mtf_processing_finish (void *mtf_)
     if (iter->handle == NULL)
       {
         if (!mtf_delete_file_in_place (mtf, &iter))
-          abort ();
+          NOT_REACHED ();
         break;
       }
   
   while (mtf->head && mtf->head->type == MTF_FILE)
-    if (!mtf_processing (NULL, mtf))
+    if (!mtf_processing (NULL, mtf, ds))
       return false;
 
   return true;
@@ -1306,10 +1315,10 @@ mtf_read_nonactive_records (void *mtf_)
 static inline int
 mtf_compare_BY_values (struct mtf_proc *mtf,
                        struct mtf_file *a, struct mtf_file *b,
-                       struct ccase *c)
+                       const struct ccase *c)
 {
-  struct ccase *ca = case_is_null (&a->input) ? c : &a->input;
-  struct ccase *cb = case_is_null (&b->input) ? c : &b->input;
+  const struct ccase *ca = case_is_null (&a->input) ? c : &a->input;
+  const struct ccase *cb = case_is_null (&b->input) ? c : &b->input;
   assert ((a == NULL) + (b == NULL) + (c == NULL) <= 1);
   return case_compare_2dict (ca, cb, a->by, b->by, mtf->by_cnt);
 }
@@ -1317,7 +1326,7 @@ mtf_compare_BY_values (struct mtf_proc *mtf,
 /* Perform one iteration of steps 3...7 above.
    Returns true if successful, false if an I/O error occurred. */
 static bool
-mtf_processing (struct ccase *c, void *mtf_)
+mtf_processing (const struct ccase *c, void *mtf_, const struct dataset *ds UNUSED)
 {
   struct mtf_proc *mtf = mtf_;
 
@@ -1421,7 +1430,7 @@ mtf_processing (struct ccase *c, void *mtf_)
          
              if (mv != NULL && mtf->seq_nums[mv->index] != mtf->seq_num) 
                 {
-                  struct ccase *record
+                  const struct ccase *record
                     = case_is_null (&iter->input) ? c : &iter->input;
                   union value *out = case_data_rw (&mtf->mtf_case, mv->fv);
 
@@ -1468,7 +1477,7 @@ mtf_processing (struct ccase *c, void *mtf_)
        }
 
       /* 5. Write the output record. */
-      mtf->sink->class->write (mtf->sink, &mtf->mtf_case);
+      casefile_append (mtf->output, &mtf->mtf_case);
 
       /* 6. Read another record from each input file FILE and TABLE
         that we stored values from above.  If we come to the end of
@@ -1543,8 +1552,11 @@ mtf_merge_dictionary (struct dictionary *const m, struct mtf_file *f)
           if (dv->width == mv->width)
             {
               if (val_labs_count (dv->val_labs)
-                  && !val_labs_count (mv->val_labs))
-                mv->val_labs = val_labs_copy (dv->val_labs);
+                  && !val_labs_count (mv->val_labs)) 
+                {
+                  val_labs_destroy (mv->val_labs);
+                  mv->val_labs = val_labs_copy (dv->val_labs); 
+                }
               if (!mv_is_empty (&dv->miss) && mv_is_empty (&mv->miss))
                 mv_copy (&mv->miss, &dv->miss);
             }