Whitespace changes only: Remove trailing whitespace
[pspp] / src / language / xforms / recode.c
index 62cf387eee9faf30b6afa4cff210c17124e76a26..361b5a64f867b27b20d4f69c1c7d968b3c79280d 100644 (file)
@@ -22,9 +22,9 @@
 
 #include "data/case.h"
 #include "data/data-in.h"
+#include "data/dataset.h"
 #include "data/dictionary.h"
 #include "data/format.h"
-#include "data/procedure.h"
 #include "data/transformations.h"
 #include "data/variable.h"
 #include "language/command.h"
@@ -85,8 +85,6 @@ struct recode_trns
   {
     struct pool *pool;
 
-
-
     /* Variable types, for convenience. */
     enum val_type src_type;     /* src_vars[*] type. */
     enum val_type dst_type;     /* dst_vars[*] type. */
@@ -106,25 +104,28 @@ struct recode_trns
   };
 
 static bool parse_src_vars (struct lexer *, struct recode_trns *, const struct dictionary *dict);
-static bool parse_mappings (struct lexer *, struct recode_trns *);
+static bool parse_mappings (struct lexer *, struct recode_trns *,
+                            const char *dict_encoding);
 static bool parse_dst_vars (struct lexer *, struct recode_trns *, const struct dictionary *dict);
 
 static void add_mapping (struct recode_trns *,
                          size_t *map_allocated, const struct map_in *);
 
 static bool parse_map_in (struct lexer *lexer, struct map_in *, struct pool *,
-                          enum val_type src_type, size_t max_src_width);
+                          enum val_type src_type, size_t max_src_width,
+                          const char *dict_encoding);
 static void set_map_in_generic (struct map_in *, enum map_in_type);
 static void set_map_in_num (struct map_in *, enum map_in_type, double, double);
 static void set_map_in_str (struct map_in *, struct pool *,
-                            struct substring, size_t width);
+                            struct substring, size_t width,
+                            const char *dict_encoding);
 
 static bool parse_map_out (struct lexer *lexer, struct pool *, struct map_out *);
 static void set_map_out_num (struct map_out *, double);
 static void set_map_out_str (struct map_out *, struct pool *,
                              struct substring);
 
-static void enlarge_dst_widths (struct recode_trns *);
+static bool enlarge_dst_widths (struct recode_trns *);
 static void create_dst_vars (struct recode_trns *, struct dictionary *);
 
 static trns_proc_func recode_trns_proc;
@@ -138,15 +139,16 @@ cmd_recode (struct lexer *lexer, struct dataset *ds)
 {
   do
     {
+      struct dictionary *dict = dataset_dict (ds);
       struct recode_trns *trns
         = pool_create_container (struct recode_trns, pool);
 
       /* Parse source variable names,
          then input to output mappings,
          then destintation variable names. */
-      if (!parse_src_vars (lexer, trns, dataset_dict (ds) )
-          || !parse_mappings (lexer, trns)
-          || !parse_dst_vars (lexer, trns, dataset_dict (ds)))
+      if (!parse_src_vars (lexer, trns, dict)
+          || !parse_mappings (lexer, trns, dict_get_encoding (dict))
+          || !parse_dst_vars (lexer, trns, dict))
         {
           recode_trns_free (trns);
           return CMD_FAILURE;
@@ -155,14 +157,20 @@ cmd_recode (struct lexer *lexer, struct dataset *ds)
       /* Ensure that all the output strings are at least as wide
          as the widest destination variable. */
       if (trns->dst_type == VAL_STRING)
-        enlarge_dst_widths (trns);
+       {
+         if ( ! enlarge_dst_widths (trns))
+           {
+             recode_trns_free (trns);
+             return CMD_FAILURE;
+           }
+       }
 
       /* Create destination variables, if needed.
          This must be the final step; otherwise we'd have to
          delete destination variables on failure. */
-      trns->dst_dict = dataset_dict (ds);
+      trns->dst_dict = dict;
       if (trns->src_vars != trns->dst_vars)
-       create_dst_vars (trns, dataset_dict (ds));
+       create_dst_vars (trns, dict);
 
       /* Done. */
       add_transformation (ds,
@@ -170,7 +178,7 @@ cmd_recode (struct lexer *lexer, struct dataset *ds)
     }
   while (lex_match (lexer, T_SLASH));
 
-  return lex_end_of_command (lexer);
+  return CMD_SUCCESS;
 }
 
 /* Parses a set of variables to recode into TRNS->src_vars and
@@ -192,7 +200,8 @@ parse_src_vars (struct lexer *lexer,
    into TRNS->mappings and TRNS->map_cnt.  Sets TRNS->dst_type.
    Returns true if successful, false on parse error. */
 static bool
-parse_mappings (struct lexer *lexer, struct recode_trns *trns)
+parse_mappings (struct lexer *lexer, struct recode_trns *trns,
+                const char *dict_encoding)
 {
   size_t map_allocated;
   bool have_dst_type;
@@ -232,7 +241,8 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns)
               struct map_in in;
 
               if (!parse_map_in (lexer, &in, trns->pool,
-                                 trns->src_type, trns->max_src_width))
+                                 trns->src_type, trns->max_src_width,
+                                 dict_encoding))
                 return false;
               add_mapping (trns, &map_allocated, &in);
               lex_match (lexer, T_COMMA);
@@ -292,7 +302,8 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns)
    false on parse error. */
 static bool
 parse_map_in (struct lexer *lexer, struct map_in *in, struct pool *pool,
-              enum val_type src_type, size_t max_src_width)
+              enum val_type src_type, size_t max_src_width,
+              const char *dict_encoding)
 {
 
   if (lex_match_id (lexer, "ELSE"))
@@ -317,14 +328,15 @@ parse_map_in (struct lexer *lexer, struct map_in *in, struct pool *pool,
         set_map_in_generic (in, MAP_MISSING);
       else if (!lex_force_string (lexer))
         return false;
-      else 
+      else
        {
-         set_map_in_str (in, pool, lex_tokss (lexer), max_src_width);
+         set_map_in_str (in, pool, lex_tokss (lexer), max_src_width,
+                          dict_encoding);
          lex_get (lexer);
          if (lex_token (lexer) == T_ID
              && lex_id_match (ss_cstr ("THRU"), lex_tokss (lexer)))
            {
-             msg (SE, _("THRU is not allowed with string variables."));
+             msg (SE, _("%s is not allowed with string variables."), "THRU");
              return false;
            }
        }
@@ -371,13 +383,16 @@ set_map_in_num (struct map_in *in, enum map_in_type type, double x, double y)
    right to WIDTH characters long. */
 static void
 set_map_in_str (struct map_in *in, struct pool *pool,
-                struct substring string, size_t width)
+                struct substring string, size_t width,
+                const char *dict_encoding)
 {
+  char *s = recode_string (dict_encoding, "UTF-8",
+                           ss_data (string), ss_length (string));
   in->type = MAP_SINGLE;
   value_init_pool (pool, &in->x, width);
   value_copy_buf_rpad (&in->x, width,
-                       CHAR_CAST_BUG (uint8_t *, ss_data (string)),
-                       ss_length (string), ' ');
+                       CHAR_CAST (uint8_t *, s), strlen (s), ' ');
+  free (s);
 }
 
 /* Parses a mapping output value into OUT, allocating memory from
@@ -397,10 +412,10 @@ parse_map_out (struct lexer *lexer, struct pool *pool, struct map_out *out)
       set_map_out_str (out, pool, lex_tokss (lexer));
       lex_get (lexer);
     }
-  else if (lex_match_id (lexer, "COPY")) 
+  else if (lex_match_id (lexer, "COPY"))
     {
       out->copy_input = true;
-      out->width = 0; 
+      out->width = 0;
     }
   else
     {
@@ -519,26 +534,46 @@ parse_dst_vars (struct lexer *lexer, struct recode_trns *trns,
 
 /* Ensures that all the output values in TRNS are as wide as the
    widest destination variable. */
-static void
+static bool
 enlarge_dst_widths (struct recode_trns *trns)
 {
   size_t i;
-
+  const struct variable *narrow_var = NULL;
+  int min_dst_width = INT_MAX;
   trns->max_dst_width = 0;
+
   for (i = 0; i < trns->var_cnt; i++)
     {
       const struct variable *v = trns->dst_vars[i];
       if (var_get_width (v) > trns->max_dst_width)
         trns->max_dst_width = var_get_width (v);
+
+      if (var_get_width (v) < min_dst_width)
+       {
+         min_dst_width = var_get_width (v);
+         narrow_var = v;
+       }
     }
 
   for (i = 0; i < trns->map_cnt; i++)
     {
       struct map_out *out = &trns->mappings[i].out;
       if (!out->copy_input)
-        value_resize_pool (trns->pool, &out->value,
-                           out->width, trns->max_dst_width);
+       {
+         if (out->width > min_dst_width)
+           {
+             msg (ME,
+                  _("Cannot recode because the variable %s would require a width of %d bytes or greater, but it has a width of only %d bytes."),
+                  var_get_name (narrow_var), out->width, min_dst_width);
+             return false;
+           }
+
+         value_resize_pool (trns->pool, &out->value,
+                            out->width, trns->max_dst_width);
+       }
     }
+
+  return true;
 }
 
 /* Creates destination variables that don't already exist. */