case: Introduce new functions for numbers and substrings in cases.
[pspp] / src / language / stats / flip.c
index 3e595968d9c4c3232f719a1b48520a2a71dfdc37..993f8975a629d6beae47ba95b7fed7f0c0cf2747 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-#include "config.h"
+#include <config.h>
 
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdlib.h>
 
-#include <data/case.h>
-#include <data/casereader.h>
-#include <data/casereader-provider.h>
-#include <data/dictionary.h>
-#include <data/procedure.h>
-#include <data/settings.h>
-#include <data/short-names.h>
-#include <data/value.h>
-#include <data/variable.h>
-#include <language/command.h>
-#include <language/lexer/lexer.h>
-#include <language/lexer/variable-parser.h>
-#include <libpspp/array.h>
-#include <libpspp/assertion.h>
-#include <libpspp/message.h>
-#include <libpspp/misc.h>
-#include <libpspp/pool.h>
-#include <libpspp/str.h>
-#include <data/data-in.h>
-#include <data/data-out.h>
-#include "intprops.h"
-#include "minmax.h"
-#include "xalloc.h"
+#include "data/case.h"
+#include "data/casereader.h"
+#include "data/casereader-provider.h"
+#include "data/dataset.h"
+#include "data/dictionary.h"
+#include "data/settings.h"
+#include "data/short-names.h"
+#include "data/value.h"
+#include "data/variable.h"
+#include "language/command.h"
+#include "language/lexer/lexer.h"
+#include "language/lexer/variable-parser.h"
+#include "libpspp/array.h"
+#include "libpspp/assertion.h"
+#include "libpspp/message.h"
+#include "libpspp/misc.h"
+#include "libpspp/pool.h"
+#include "libpspp/str.h"
+#include "data/data-in.h"
+#include "data/data-out.h"
+
+#include "gl/intprops.h"
+#include "gl/minmax.h"
+#include "gl/xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -67,7 +68,7 @@ struct flip_pgm
     int n_cases;                /* Pre-flip number of cases. */
 
     struct variable *new_names_var; /* Variable with new variable names. */
-    struct dictionary *dict;     /* Dictionary of the names */
+    const char *encoding;           /* Variable names' encoding. */
     struct var_names old_names; /* Variable names before FLIP. */
     struct var_names new_names; /* Variable names after FLIP. */
 
@@ -86,7 +87,8 @@ static void make_new_var (struct dictionary *, const char *name);
 int
 cmd_flip (struct lexer *lexer, struct dataset *ds)
 {
-  struct dictionary *dict = dataset_dict (ds);
+  struct dictionary *old_dict = dataset_dict (ds);
+  struct dictionary *new_dict = NULL;
   const struct variable **vars;
   struct flip_pgm *flip;
   struct casereader *input, *reader;
@@ -95,8 +97,8 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
   bool ok;
 
   if (proc_make_temporary_transformations_permanent (ds))
-    msg (SW, _("FLIP ignores TEMPORARY.  "
-               "Temporary transformations will be made permanent."));
+    msg (SW, _("%s ignores %s.  "
+               "Temporary transformations will be made permanent."), "FLIP", "TEMPORARY");
 
   flip = pool_create_container (struct flip_pgm, pool);
   flip->n_vars = 0;
@@ -107,31 +109,30 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
   flip->file = NULL;
   flip->cases_read = 0;
   flip->error = false;
-  flip->dict = dict;
 
-  lex_match (lexer, '/');
+  lex_match (lexer, T_SLASH);
   if (lex_match_id (lexer, "VARIABLES"))
     {
-      lex_match (lexer, '=');
-      if (!parse_variables_const (lexer, dict, &vars, &flip->n_vars,
+      lex_match (lexer, T_EQUALS);
+      if (!parse_variables_const (lexer, old_dict, &vars, &flip->n_vars,
                                   PV_NO_DUPLICATE))
        goto error;
-      lex_match (lexer, '/');
+      lex_match (lexer, T_SLASH);
     }
   else
-    dict_get_vars (dict, &vars, &flip->n_vars, DC_SYSTEM);
+    dict_get_vars (old_dict, &vars, &flip->n_vars, DC_SYSTEM);
   pool_register (flip->pool, free, vars);
 
-  lex_match (lexer, '/');
+  lex_match (lexer, T_SLASH);
   if (lex_match_id (lexer, "NEWNAMES"))
     {
-      lex_match (lexer, '=');
-      flip->new_names_var = parse_variable (lexer, dict);
+      lex_match (lexer, T_EQUALS);
+      flip->new_names_var = parse_variable (lexer, old_dict);
       if (!flip->new_names_var)
         goto error;
     }
   else
-    flip->new_names_var = dict_lookup_var (dict, "CASE_LBL");
+    flip->new_names_var = dict_lookup_var (old_dict, "CASE_LBL");
 
   if (flip->new_names_var)
     {
@@ -143,11 +144,13 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
            break;
          }
     }
+  if (flip->n_vars <= 0)
+    goto error;
 
-  flip->file = pool_tmpfile (flip->pool);
+  flip->file = pool_create_temp_file (flip->pool);
   if (flip->file == NULL)
     {
-      msg (SE, _("Could not create temporary file for FLIP."));
+      msg (SE, _("Could not create temporary file for %s."), "FLIP");
       goto error;
     }
 
@@ -157,10 +160,15 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
     var_names_add (flip->pool, &flip->old_names,
                    pool_strdup (flip->pool, var_get_name (vars[i])));
 
-  /* Read the active file into a flip_sink. */
+  /* Read the active dataset into a flip_sink. */
   proc_discard_output (ds);
 
-  input = proc_open (ds);
+  /* Save old dictionary. */
+  new_dict = dict_clone (old_dict);
+  flip->encoding = dict_get_encoding (new_dict);
+  dict_clear (new_dict);
+
+  input = proc_open_filtering (ds, false);
   while ((c = casereader_read (input)) != NULL)
     {
       flip->n_cases++;
@@ -184,9 +192,9 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
             }
           else
             {
-              name = data_out_pool (value, dict_get_encoding (flip->dict), var_get_write_format (flip->new_names_var),
-                flip->pool);
-       
+              name = data_out_pool (value, dict_get_encoding (old_dict),
+                                    var_get_write_format (flip->new_names_var),
+                                    settings_get_fmt_settings (), flip->pool);
             }
           var_names_add (flip->pool, &flip->new_names, name);
         }
@@ -198,31 +206,32 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
   /* Flip the data we read. */
   if (!ok || !flip_file (flip))
     {
-      proc_discard_active_file (ds);
+      dataset_clear (ds);
       goto error;
     }
 
   /* Flip the dictionary. */
-  dict_clear (dict);
-  dict_create_var_assert (dict, "CASE_LBL", 8);
+  dict_create_var_assert (new_dict, "CASE_LBL", 8);
   for (i = 0; i < flip->n_cases; i++)
     if (flip->new_names.n_names)
-      make_new_var (dict, flip->new_names.names[i]);
+      make_new_var (new_dict, flip->new_names.names[i]);
     else
       {
-        char s[VAR_NAME_LEN + 1];
-        sprintf (s, "VAR%03d", i);
-        dict_create_var_assert (dict, s, 0);
+        char s[3 + INT_STRLEN_BOUND (i) + 1];
+        sprintf (s, "VAR%03zu", i);
+        dict_create_var_assert (new_dict, s, 0);
       }
 
   /* Set up flipped data for reading. */
-  reader = casereader_create_sequential (NULL, dict_get_proto (dict),
+  reader = casereader_create_sequential (NULL, dict_get_proto (new_dict),
                                          flip->n_vars,
                                          &flip_casereader_class, flip);
-  proc_set_active_file_data (ds, reader);
-  return lex_end_of_command (lexer);
+  dataset_set_dict (ds, new_dict);
+  dataset_set_source (ds, reader);
+  return CMD_SUCCESS;
 
  error:
+  dict_unref (new_dict);
   destroy_flip_pgm (flip);
   return CMD_CASCADING_FAILURE;
 }
@@ -249,7 +258,7 @@ make_new_var (struct dictionary *dict, const char *name_)
     *--cp = '\0';
 
   /* Fix invalid characters. */
-  for (cp = name; *cp && cp < name + VAR_NAME_LEN; cp++)
+  for (cp = name; *cp && cp < name + ID_MAX_LEN; cp++)
     if (cp == name)
       {
         if (!lex_is_id1 (*cp) || *cp == '$')
@@ -261,22 +270,27 @@ make_new_var (struct dictionary *dict, const char *name_)
           *cp = '_';
       }
   *cp = '\0';
-  str_uppercase (name);
+
+  if (strlen (name) == 0)
+    {
+      free (name);
+      name = xstrdup ("v");
+    }
 
   /* Use the mangled name, if it is available, or add numeric
      extensions until we find one that is. */
-  if (!dict_create_var (dict, name, 0))
+  if (!id_is_plausible (name, false) || !dict_create_var (dict, name, 0))
     {
       int len = strlen (name);
       int i;
       for (i = 1; ; i++)
         {
-          char n[VAR_NAME_LEN + 1];
-          int ofs = MIN (VAR_NAME_LEN - 1 - intlog10 (i), len);
+          char n[ID_MAX_LEN + 1];
+          int ofs = MIN (ID_MAX_LEN - 1 - intlog10 (i), len);
           strncpy (n, name, ofs);
           sprintf (&n[ofs], "%d", i);
 
-          if (dict_create_var (dict, n, 0))
+          if (id_is_plausible (n, false) && dict_create_var (dict, n, 0))
             break;
         }
     }
@@ -322,20 +336,20 @@ flip_file (struct flip_pgm *flip)
   output_buf = input_buf + flip->n_vars * case_capacity;
 
   input_file = flip->file;
-  if (fseek (input_file, 0, SEEK_SET) != 0)
+  if (fseeko (input_file, 0, SEEK_SET) != 0)
     {
-      msg (SE, _("Error rewinding FLIP file: %s."), strerror (errno));
+      msg (SE, _("Error rewinding %s file: %s."), "FLIP", strerror (errno));
       return false;
     }
 
-  output_file = pool_tmpfile (flip->pool);
+  output_file = pool_create_temp_file (flip->pool);
   if (output_file == NULL)
     {
-      msg (SE, _("Error creating FLIP source file."));
+      msg (SE, _("Error creating %s source file."), "FLIP");
       return false;
     }
 
-  for (case_idx = 0; case_idx < flip->n_cases; )
+  for (case_idx = 0; case_idx < flip->n_cases;)
     {
       unsigned long read_cases = MIN (flip->n_cases - case_idx,
                                       case_capacity);
@@ -344,9 +358,9 @@ flip_file (struct flip_pgm *flip)
       if (read_cases != fread (input_buf, case_bytes, read_cases, input_file))
         {
           if (ferror (input_file))
-            msg (SE, _("Error reading FLIP file: %s."), strerror (errno));
+            msg (SE, _("Error reading %s file: %s."), "FLIP", strerror (errno));
           else
-            msg (SE, _("Unexpected end of file reading FLIP file."));
+            msg (SE, _("Unexpected end of file reading %s file."), "FLIP");
           return false;
         }
 
@@ -362,7 +376,7 @@ flip_file (struct flip_pgm *flip)
                                            + (off_t) i * flip->n_cases),
                       SEEK_SET) != 0)
             {
-              msg (SE, _("Error seeking FLIP source file: %s."),
+              msg (SE, _("Error seeking %s source file: %s."), "FLIP",
                    strerror (errno));
               return false;
             }
@@ -370,7 +384,7 @@ flip_file (struct flip_pgm *flip)
          if (fwrite (output_buf, sizeof *output_buf, read_cases, output_file)
              != read_cases)
             {
-              msg (SE, _("Error writing FLIP source file: %s."),
+              msg (SE, _("Error writing %s source file: %s."), "FLIP",
                    strerror (errno));
               return false;
             }
@@ -379,17 +393,13 @@ flip_file (struct flip_pgm *flip)
       case_idx += read_cases;
     }
 
-  if (pool_fclose (flip->pool, input_file) == EOF)
-    {
-      msg (SE, _("Error closing FLIP source file: %s."), strerror (errno));
-      return false;
-    }
+  pool_fclose_temp_file (flip->pool, input_file);
   pool_unregister (flip->pool, input_buf);
   free (input_buf);
 
-  if (fseek (output_file, 0, SEEK_SET) != 0)
+  if (fseeko (output_file, 0, SEEK_SET) != 0)
     {
-      msg (SE, _("Error rewinding FLIP source file: %s."), strerror (errno));
+      msg (SE, _("Error rewinding %s source file: %s."), "FLIP", strerror (errno));
       return false;
     }
   flip->file = output_file;
@@ -410,12 +420,10 @@ flip_casereader_read (struct casereader *reader, void *flip_)
     return false;
 
   c = case_create (casereader_get_proto (reader));
-  data_in (ss_cstr (flip->old_names.names[flip->cases_read]), dict_get_encoding (flip->dict), 
-       FMT_A, 0,
-       0, 0,
-       flip->dict, 
-       case_data_rw_idx (c, 0), 8);
-       
+  data_in (ss_cstr (flip->old_names.names[flip->cases_read]), flip->encoding,
+           FMT_A, settings_get_fmt_settings (), case_data_rw_idx (c, 0),
+           8, flip->encoding);
+
   for (i = 0; i < flip->n_cases; i++)
     {
       double in;
@@ -423,16 +431,16 @@ flip_casereader_read (struct casereader *reader, void *flip_)
         {
           case_unref (c);
           if (ferror (flip->file))
-            msg (SE, _("Error reading FLIP temporary file: %s."),
+            msg (SE, _("Error reading %s temporary file: %s."), "FLIP",
                  strerror (errno));
           else if (feof (flip->file))
-            msg (SE, _("Unexpected end of file reading FLIP temporary file."));
+            msg (SE, _("Unexpected end of file reading %s temporary file."), "FLIP");
           else
             NOT_REACHED ();
           flip->error = true;
           return NULL;
         }
-      case_data_rw_idx (c, i + 1)->f = in;
+      *case_num_rw_idx (c, i + 1) = in;
     }
 
   flip->cases_read++;
@@ -444,7 +452,7 @@ flip_casereader_read (struct casereader *reader, void *flip_)
    Returns true if successful read, false if an I/O occurred
    during destruction or previously. */
 static void
-flip_casereader_destroy (struct casereader *reader UNUSED, void *flip_)
+flip_casereader_destroy (struct casereader *reader, void *flip_)
 {
   struct flip_pgm *flip = flip_;
   if (flip->error)