Fixed a crash when RECODE attempted to increase the length of a string beyond the...
authorJohn Darrington <john@darrington.wattle.id.au>
Sat, 27 Apr 2013 05:21:24 +0000 (07:21 +0200)
committerJohn Darrington <john@darrington.wattle.id.au>
Sat, 27 Apr 2013 05:21:24 +0000 (07:21 +0200)
Reported-by: Cristoph Amthor
doc/transformation.texi
src/language/xforms/recode.c
tests/language/xforms/recode.at

index 5ea809e54f7e0ebd67bfeb343cd22000b29e759c..dfacfcdaed1e5bb19e8e6055bdfef9b479dea12c 100644 (file)
@@ -579,13 +579,17 @@ The behaviour of the command is slightly different depending on whether it
 appears or not.
 
 If @samp{INTO @var{dest_vars}} does not appear, then values will be recoded
-``in place´´.  This means that the recoded values are written back to the
+``in place´´.
+This means that the recoded values are written back to the
 source variables from whence the original values came.
 In this case, the @var{dest_value} for every mapping must imply a value which
 has the same type as the @var{src_value}.
 For example, if the source value is a string value, it is not permissible for
 @var{dest_value} to be @samp{SYSMIS} or another forms which implies a numeric
 result.
+It is also not permissible for @var{dest_value} to be  longer than the width
+of the source variable.
+
 The following example two numeric variables @var{x} and @var{y} are recoded
 in place.
 Zero is recoded to 99, the values 1 to 10 inclusive are unchanged,
index 44e5b8513326e32a844ba9bee618d94ce74ba777..2849ea32d1510eb74f518506728edf65484bd401 100644 (file)
@@ -125,7 +125,7 @@ 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;
@@ -157,7 +157,12 @@ 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))
+           {
+             return CMD_FAILURE;
+           }
+       }
 
       /* Create destination variables, if needed.
          This must be the final step; otherwise we'd have to
@@ -528,26 +533,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, trns->max_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. */
index aa50054e09a111f08a3f8941084c402271ae0d58..ae434c657a8be0c18667e21ac6911f7bf6053492 100644 (file)
@@ -294,3 +294,40 @@ A,B,A1
 3,4,1
 ])
 AT_CLEANUP
+
+
+
+AT_SETUP([RECODE increased string widths])
+
+AT_DATA([recode.sps],[dnl
+data list list /x (a1) y (a8) z *.
+begin data.
+a a         2
+a two       2
+b three     2
+c b         2
+end data.
+
+recode x y ("a" = "first") .
+
+list.
+])
+
+AT_CHECK([pspp -O format=csv recode.sps], [1], [dnl
+Table: Reading free-form data from INLINE.
+Variable,Format
+x,A1
+y,A8
+z,F8.0
+
+"error: Cannot recode in place, because the variable x would require a width of 5 bytes or greater, but it has a width of only 8 bytes."
+
+Table: Data List
+x,y,z
+a,a       ,2.00
+a,two     ,2.00
+b,three   ,2.00
+c,b       ,2.00
+])
+
+AT_CLEANUP