286b2ae6413541c2b6c3fe7b423cd5cfb95782e6
[pspp] / src / language / xforms / recode.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2009, 2010, 2011 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <ctype.h>
20 #include <math.h>
21 #include <stdlib.h>
22
23 #include "data/case.h"
24 #include "data/data-in.h"
25 #include "data/dataset.h"
26 #include "data/dictionary.h"
27 #include "data/format.h"
28 #include "data/transformations.h"
29 #include "data/variable.h"
30 #include "language/command.h"
31 #include "language/lexer/lexer.h"
32 #include "language/lexer/value-parser.h"
33 #include "language/lexer/variable-parser.h"
34 #include "libpspp/assertion.h"
35 #include "libpspp/cast.h"
36 #include "libpspp/compiler.h"
37 #include "libpspp/i18n.h"
38 #include "libpspp/message.h"
39 #include "libpspp/pool.h"
40 #include "libpspp/str.h"
41
42 #include "gl/xalloc.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46 \f
47 /* Definitions. */
48
49 /* Type of source value for RECODE. */
50 enum map_in_type
51   {
52     MAP_SINGLE,                 /* Specific value. */
53     MAP_RANGE,                  /* Range of values. */
54     MAP_SYSMIS,                 /* System missing value. */
55     MAP_MISSING,                /* Any missing value. */
56     MAP_ELSE,                   /* Any value. */
57     MAP_CONVERT                 /* "123" => 123. */
58   };
59
60 /* Describes input values to be mapped. */
61 struct map_in
62   {
63     enum map_in_type type;      /* One of MAP_*. */
64     union value x, y;           /* Source values. */
65   };
66
67 /* Describes the value used as output from a mapping. */
68 struct map_out
69   {
70     bool copy_input;            /* If true, copy input to output. */
71     union value value;          /* If copy_input false, recoded value. */
72     int width;                  /* If copy_input false, output value width. */
73     int ofs;                    /* Lexical location. */
74   };
75
76 /* Describes how to recode a single value or range of values into a
77    single value.  */
78 struct mapping
79   {
80     struct map_in in;           /* Input values. */
81     struct map_out out;         /* Output value. */
82   };
83
84 /* RECODE transformation. */
85 struct recode_trns
86   {
87     struct pool *pool;
88
89     /* Variable types, for convenience. */
90     enum val_type src_type;     /* src_vars[*] type. */
91     enum val_type dst_type;     /* dst_vars[*] type. */
92
93     /* Variables. */
94     const struct variable **src_vars;   /* Source variables. */
95     const struct variable **dst_vars;   /* Destination variables. */
96     const struct dictionary *dst_dict;  /* Dictionary of dst_vars */
97     char **dst_names;           /* Name of dest variables, if they're new. */
98     size_t n_vars;             /* Number of variables. */
99
100     /* Mappings. */
101     struct mapping *mappings;   /* Value mappings. */
102     size_t n_maps;              /* Number of mappings. */
103     int max_src_width;          /* Maximum width of src_vars[*]. */
104     int max_dst_width;          /* Maximum width of any map_out in mappings. */
105   };
106
107 static bool parse_src_vars (struct lexer *, struct recode_trns *, const struct dictionary *dict);
108 static bool parse_mappings (struct lexer *, struct recode_trns *,
109                             const char *dict_encoding);
110 static bool parse_dst_vars (struct lexer *, struct recode_trns *,
111                             const struct dictionary *,
112                             int src_start, int src_end,
113                             int mappings_start, int mappings_end);
114
115 static void add_mapping (struct recode_trns *,
116                          size_t *map_allocated, const struct map_in *);
117
118 static bool parse_map_in (struct lexer *lexer, struct map_in *, struct pool *,
119                           enum val_type src_type, size_t max_src_width,
120                           const char *dict_encoding);
121 static void set_map_in_str (struct map_in *, struct pool *,
122                             struct substring, size_t width,
123                             const char *dict_encoding);
124
125 static bool parse_map_out (struct lexer *lexer, struct pool *, struct map_out *);
126 static void set_map_out_str (struct map_out *, struct pool *,
127                              struct substring);
128
129 static bool enlarge_dst_widths (struct lexer *, struct recode_trns *,
130                                 int dst_start, int dst_end);
131 static void create_dst_vars (struct recode_trns *, struct dictionary *);
132
133 static bool recode_trns_free (void *trns_);
134
135 static const struct trns_class recode_trns_class;
136 \f
137 /* Parser. */
138
139 static bool
140 parse_one_recoding (struct lexer *lexer, struct dataset *ds,
141                     struct recode_trns *trns)
142 {
143   struct dictionary *dict = dataset_dict (ds);
144
145   /* Parse source variable names,
146      then input to output mappings,
147      then destination variable names. */
148   int src_start = lex_ofs (lexer);
149   if (!parse_src_vars (lexer, trns, dict))
150     return false;
151   int src_end = lex_ofs (lexer) - 1;
152
153   int mappings_start = lex_ofs (lexer);
154   if (!parse_mappings (lexer, trns, dict_get_encoding (dict)))
155     return false;
156   int mappings_end = lex_ofs (lexer) - 1;
157
158   int dst_start = lex_ofs (lexer);
159   if (!parse_dst_vars (lexer, trns, dict,
160                        src_start, src_end, mappings_start, mappings_end))
161     return false;
162   int dst_end = lex_ofs (lexer) - 1;
163   if (dst_end < dst_start)
164     {
165       /* There was no target variable syntax, so the target variables are the
166          same as the source variables. */
167       dst_start = src_start;
168       dst_end = src_end;
169     }
170
171   /* Ensure that all the output strings are at least as wide
172      as the widest destination variable. */
173   if (trns->dst_type == VAL_STRING
174       && !enlarge_dst_widths (lexer, trns, dst_start, dst_end))
175     return false;
176
177   /* Create destination variables, if needed.
178      This must be the final step; otherwise we'd have to
179      delete destination variables on failure. */
180   trns->dst_dict = dict;
181   if (trns->src_vars != trns->dst_vars)
182     create_dst_vars (trns, dict);
183
184   /* Done. */
185   add_transformation (ds, &recode_trns_class, trns);
186   return true;
187 }
188
189 /* Parses the RECODE transformation. */
190 int
191 cmd_recode (struct lexer *lexer, struct dataset *ds)
192 {
193   do
194     {
195       struct pool *pool = pool_create ();
196       struct recode_trns *trns = pool_alloc (pool, sizeof *trns);
197       *trns = (struct recode_trns) { .pool = pool };
198
199       if (!parse_one_recoding (lexer, ds, trns))
200         {
201           recode_trns_free (trns);
202           return CMD_FAILURE;
203         }
204     }
205   while (lex_match (lexer, T_SLASH));
206
207   return CMD_SUCCESS;
208 }
209
210 /* Parses a set of variables to recode into TRNS->src_vars and
211    TRNS->n_vars.  Sets TRNS->src_type.  Returns true if
212    successful, false on parse error. */
213 static bool
214 parse_src_vars (struct lexer *lexer,
215                 struct recode_trns *trns, const struct dictionary *dict)
216 {
217   if (!parse_variables_const (lexer, dict, &trns->src_vars, &trns->n_vars,
218                         PV_SAME_TYPE))
219     return false;
220   pool_register (trns->pool, free, trns->src_vars);
221   trns->src_type = var_get_type (trns->src_vars[0]);
222   return true;
223 }
224
225 /* Parses a set of mappings, which take the form (input=output),
226    into TRNS->mappings and TRNS->n_maps.  Sets TRNS->dst_type.
227    Returns true if successful, false on parse error. */
228 static bool
229 parse_mappings (struct lexer *lexer, struct recode_trns *trns,
230                 const char *dict_encoding)
231 {
232   /* Find length of longest source variable. */
233   trns->max_src_width = var_get_width (trns->src_vars[0]);
234   for (size_t i = 1; i < trns->n_vars; i++)
235     {
236       size_t var_width = var_get_width (trns->src_vars[i]);
237       if (var_width > trns->max_src_width)
238         trns->max_src_width = var_width;
239     }
240
241   /* Parse the mappings in parentheses. */
242   size_t map_allocated = 0;
243   bool have_dst_type = false;
244   if (!lex_force_match (lexer, T_LPAREN))
245     return false;
246   do
247     {
248       enum val_type dst_type;
249
250       if (!lex_match_id (lexer, "CONVERT"))
251         {
252           size_t first_map_idx = trns->n_maps;
253
254           /* Parse source specifications. */
255           do
256             {
257               struct map_in in;
258
259               if (!parse_map_in (lexer, &in, trns->pool,
260                                  trns->src_type, trns->max_src_width,
261                                  dict_encoding))
262                 return false;
263               add_mapping (trns, &map_allocated, &in);
264               lex_match (lexer, T_COMMA);
265             }
266           while (!lex_match (lexer, T_EQUALS));
267
268           struct map_out out;
269           if (!parse_map_out (lexer, trns->pool, &out))
270             return false;
271
272           dst_type = (out.copy_input
273                       ? trns->src_type
274                       : val_type_from_width (out.width));
275           for (size_t i = first_map_idx; i < trns->n_maps; i++)
276             trns->mappings[i].out = out;
277         }
278       else
279         {
280           /* Parse CONVERT as a special case. */
281           struct map_in in = { .type = MAP_CONVERT };
282           add_mapping (trns, &map_allocated, &in);
283
284           int ofs = lex_ofs (lexer) - 1;
285           trns->mappings[trns->n_maps - 1].out = (struct map_out) {
286             .ofs = ofs,
287           };
288
289           dst_type = VAL_NUMERIC;
290           if (trns->src_type != VAL_STRING)
291             {
292               lex_ofs_error (lexer, ofs, ofs,
293                              _("CONVERT requires string input values."));
294               return false;
295             }
296         }
297       if (have_dst_type && dst_type != trns->dst_type)
298         {
299           msg (SE, _("Output values must be all numeric or all string."));
300
301           assert (trns->n_maps > 1);
302           const struct map_out *numeric = &trns->mappings[trns->n_maps - 2].out;
303           const struct map_out *string = &trns->mappings[trns->n_maps - 1].out;
304
305           if (trns->dst_type == VAL_STRING)
306             {
307               const struct map_out *tmp = numeric;
308               numeric = string;
309               string = tmp;
310             }
311
312           lex_ofs_msg (lexer, SN, numeric->ofs, numeric->ofs,
313                        _("This output value is numeric."));
314           lex_ofs_msg (lexer, SN, string->ofs, string->ofs,
315                        _("This output value is string."));
316           return false;
317         }
318       trns->dst_type = dst_type;
319       have_dst_type = true;
320
321       if (!lex_force_match (lexer, T_RPAREN))
322         return false;
323     }
324   while (lex_match (lexer, T_LPAREN));
325
326   return true;
327 }
328
329 /* Parses a mapping input value into IN, allocating memory from
330    POOL.  The source value type must be provided as SRC_TYPE and,
331    if string, the maximum width of a string source variable must
332    be provided in MAX_SRC_WIDTH.  Returns true if successful,
333    false on parse error. */
334 static bool
335 parse_map_in (struct lexer *lexer, struct map_in *in, struct pool *pool,
336               enum val_type src_type, size_t max_src_width,
337               const char *dict_encoding)
338 {
339
340   if (lex_match_id (lexer, "ELSE"))
341     *in = (struct map_in) { .type = MAP_ELSE };
342   else if (src_type == VAL_NUMERIC)
343     {
344       if (lex_match_id (lexer, "MISSING"))
345         *in = (struct map_in) { .type = MAP_MISSING };
346       else if (lex_match_id (lexer, "SYSMIS"))
347         *in = (struct map_in) { .type = MAP_SYSMIS };
348       else
349         {
350           double x, y;
351           if (!parse_num_range (lexer, &x, &y, NULL))
352             return false;
353           *in = (struct map_in) {
354             .type = x == y ? MAP_SINGLE : MAP_RANGE,
355             .x = { .f = x },
356             .y = { .f = y },
357           };
358         }
359     }
360   else
361     {
362       if (lex_match_id (lexer, "MISSING"))
363         *in = (struct map_in) { .type = MAP_MISSING };
364       else if (!lex_force_string (lexer))
365         return false;
366       else
367         {
368           set_map_in_str (in, pool, lex_tokss (lexer), max_src_width,
369                           dict_encoding);
370           lex_get (lexer);
371           if (lex_match_id (lexer, "THRU"))
372             {
373               lex_next_error (lexer, -1, -1,
374                               _("%s is not allowed with string variables."),
375                               "THRU");
376               return false;
377             }
378         }
379     }
380
381   return true;
382 }
383
384 /* Adds IN to the list of mappings in TRNS.
385    MAP_ALLOCATED is the current number of allocated mappings,
386    which is updated as needed. */
387 static void
388 add_mapping (struct recode_trns *trns,
389              size_t *map_allocated, const struct map_in *in)
390 {
391   struct mapping *m;
392   if (trns->n_maps >= *map_allocated)
393     trns->mappings = pool_2nrealloc (trns->pool, trns->mappings,
394                                      map_allocated,
395                                      sizeof *trns->mappings);
396   m = &trns->mappings[trns->n_maps++];
397   m->in = *in;
398 }
399
400 /* Sets IN as a string mapping, with STRING as the string,
401    allocated from POOL.  The string is padded with spaces on the
402    right to WIDTH characters long. */
403 static void
404 set_map_in_str (struct map_in *in, struct pool *pool,
405                 struct substring string, size_t width,
406                 const char *dict_encoding)
407 {
408   *in = (struct map_in) { .type = MAP_SINGLE };
409
410   char *s = recode_string (dict_encoding, "UTF-8",
411                            ss_data (string), ss_length (string));
412   value_init_pool (pool, &in->x, width);
413   value_copy_buf_rpad (&in->x, width,
414                        CHAR_CAST (uint8_t *, s), strlen (s), ' ');
415   free (s);
416 }
417
418 /* Parses a mapping output value into OUT, allocating memory from
419    POOL.  Returns true if successful, false on parse error. */
420 static bool
421 parse_map_out (struct lexer *lexer, struct pool *pool, struct map_out *out)
422 {
423   if (lex_is_number (lexer))
424     {
425       *out = (struct map_out) { .value = { .f = lex_number (lexer) } };
426       lex_get (lexer);
427     }
428   else if (lex_match_id (lexer, "SYSMIS"))
429     *out = (struct map_out) { .value = { .f = SYSMIS } };
430   else if (lex_is_string (lexer))
431     {
432       set_map_out_str (out, pool, lex_tokss (lexer));
433       lex_get (lexer);
434     }
435   else if (lex_match_id (lexer, "COPY"))
436     *out = (struct map_out) { .copy_input = true };
437   else
438     {
439       lex_error (lexer, _("Syntax error expecting output value."));
440       return false;
441     }
442   out->ofs = lex_ofs (lexer) - 1;
443   return true;
444 }
445
446 /* Sets OUT as a string mapping output with the given VALUE. */
447 static void
448 set_map_out_str (struct map_out *out, struct pool *pool,
449                  const struct substring value)
450 {
451   const char *string = ss_data (value);
452   size_t length = ss_length (value);
453
454   if (length == 0)
455     {
456       /* A length of 0 will yield a numeric value, which is not
457          what we want. */
458       string = " ";
459       length = 1;
460     }
461
462   *out = (struct map_out) { .width = length };
463   value_init_pool (pool, &out->value, length);
464   memcpy (out->value.s, string, length);
465 }
466
467 /* Parses a set of target variables into TRNS->dst_vars and
468    TRNS->dst_names. */
469 static bool
470 parse_dst_vars (struct lexer *lexer, struct recode_trns *trns,
471                 const struct dictionary *dict, int src_start, int src_end,
472                 int mappings_start, int mappings_end)
473 {
474   int dst_start, dst_end;
475   if (lex_match_id (lexer, "INTO"))
476     {
477       dst_start = lex_ofs (lexer);
478       size_t n_names;
479       if (!parse_mixed_vars_pool (lexer, dict, trns->pool,
480                                   &trns->dst_names, &n_names,
481                                   PV_NONE))
482         return false;
483       dst_end = lex_ofs (lexer) - 1;
484
485       if (n_names != trns->n_vars)
486         {
487           msg (SE, _("Source and target variable counts must match."));
488           lex_ofs_msg (lexer, SN, src_start, src_end,
489                        ngettext ("There is %zu source variable.",
490                                  "There are %zu source variables.",
491                                  trns->n_vars),
492                        trns->n_vars);
493           lex_ofs_msg (lexer, SN, dst_start, dst_end,
494                        ngettext ("There is %zu target variable.",
495                                  "There are %zu target variables.",
496                                  n_names),
497                        n_names);
498           return false;
499         }
500
501       trns->dst_vars = pool_nalloc (trns->pool,
502                                     trns->n_vars, sizeof *trns->dst_vars);
503       for (size_t i = 0; i < trns->n_vars; i++)
504         {
505           const struct variable *v;
506           v = trns->dst_vars[i] = dict_lookup_var (dict, trns->dst_names[i]);
507           if (v == NULL && trns->dst_type == VAL_STRING)
508             {
509               msg (SE, _("All string variables specified on INTO must already "
510                          "exist.  (Use the STRING command to create a string "
511                          "variable.)"));
512               lex_ofs_msg (lexer, SN, dst_start, dst_end,
513                            _("There is no variable named %s."),
514                            trns->dst_names[i]);
515               return false;
516             }
517         }
518     }
519   else
520     {
521       dst_start = src_start;
522       dst_end = src_end;
523
524       trns->dst_vars = trns->src_vars;
525       if (trns->src_type != trns->dst_type)
526         {
527           if (trns->src_type == VAL_NUMERIC)
528             lex_ofs_error (lexer, mappings_start, mappings_end,
529                            _("INTO is required with numeric input values "
530                              "and string output values."));
531           else
532             lex_ofs_error (lexer, mappings_start, mappings_end,
533                            _("INTO is required with string input values "
534                              "and numeric output values."));
535           return false;
536         }
537     }
538
539   for (size_t i = 0; i < trns->n_vars; i++)
540     {
541       const struct variable *v = trns->dst_vars[i];
542       if (v && var_get_type (v) != trns->dst_type)
543         {
544           if (trns->dst_type == VAL_STRING)
545             lex_ofs_error (lexer, dst_start, dst_end,
546                            _("Type mismatch: cannot store string data in "
547                              "numeric variable %s."), var_get_name (v));
548           else
549             lex_ofs_error (lexer, dst_start, dst_end,
550                            _("Type mismatch: cannot store numeric data in "
551                              "string variable %s."), var_get_name (v));
552           return false;
553         }
554     }
555
556   return true;
557 }
558
559 /* Ensures that all the output values in TRNS are as wide as the
560    widest destination variable. */
561 static bool
562 enlarge_dst_widths (struct lexer *lexer, struct recode_trns *trns,
563                     int dst_start, int dst_end)
564 {
565   const struct variable *narrow_var = NULL;
566   int min_dst_width = INT_MAX;
567   trns->max_dst_width = 0;
568
569   for (size_t i = 0; i < trns->n_vars; i++)
570     {
571       const struct variable *v = trns->dst_vars[i];
572       if (var_get_width (v) > trns->max_dst_width)
573         trns->max_dst_width = var_get_width (v);
574
575       if (var_get_width (v) < min_dst_width)
576         {
577           min_dst_width = var_get_width (v);
578           narrow_var = v;
579         }
580     }
581
582   for (size_t i = 0; i < trns->n_maps; i++)
583     {
584       struct map_out *out = &trns->mappings[i].out;
585       if (!out->copy_input)
586         {
587           if (out->width > min_dst_width)
588             {
589               msg (SE, _("At least one target variable is too narrow for "
590                          "the output values."));
591               lex_ofs_msg (lexer, SN, out->ofs, out->ofs,
592                            _("This recoding output value has width %d."),
593                            out->width);
594               lex_ofs_msg (lexer, SN, dst_start, dst_end,
595                            _("Target variable %s only has width %d."),
596                            var_get_name (narrow_var),
597                            var_get_width (narrow_var));
598               return false;
599             }
600
601           value_resize_pool (trns->pool, &out->value,
602                              out->width, trns->max_dst_width);
603         }
604     }
605
606   return true;
607 }
608
609 /* Creates destination variables that don't already exist. */
610 static void
611 create_dst_vars (struct recode_trns *trns, struct dictionary *dict)
612 {
613   for (size_t i = 0; i < trns->n_vars; i++)
614     {
615       const struct variable **var = &trns->dst_vars[i];
616       const char *name = trns->dst_names[i];
617
618       *var = dict_lookup_var (dict, name);
619       if (*var == NULL)
620         *var = dict_create_var_assert (dict, name, 0);
621       assert (var_get_type (*var) == trns->dst_type);
622     }
623 }
624 \f
625 /* Data transformation. */
626
627 /* Returns the output mapping in TRNS for an input of VALUE on
628    variable V, or a null pointer if there is no mapping. */
629 static const struct map_out *
630 find_src_numeric (struct recode_trns *trns, double value, const struct variable *v)
631 {
632   for (struct mapping *m = trns->mappings; m < trns->mappings + trns->n_maps;
633        m++)
634     {
635       const struct map_in *in = &m->in;
636       const struct map_out *out = &m->out;
637       bool match;
638
639       switch (in->type)
640         {
641         case MAP_SINGLE:
642           match = value == in->x.f;
643           break;
644         case MAP_MISSING:
645           match = var_is_num_missing (v, value) != 0;
646           break;
647         case MAP_RANGE:
648           match = value >= in->x.f && value <= in->y.f;
649           break;
650         case MAP_SYSMIS:
651           match = value == SYSMIS;
652           break;
653         case MAP_ELSE:
654           match = true;
655           break;
656         default:
657           NOT_REACHED ();
658         }
659
660       if (match)
661         return out;
662     }
663
664   return NULL;
665 }
666
667 /* Returns the output mapping in TRNS for an input of VALUE with
668    the given WIDTH, or a null pointer if there is no mapping. */
669 static const struct map_out *
670 find_src_string (struct recode_trns *trns, const uint8_t *value,
671                  const struct variable *src_var)
672 {
673   const char *encoding = dict_get_encoding (trns->dst_dict);
674   int width = var_get_width (src_var);
675   for (struct mapping *m = trns->mappings; m < trns->mappings + trns->n_maps;
676        m++)
677     {
678       const struct map_in *in = &m->in;
679       struct map_out *out = &m->out;
680       bool match;
681
682       switch (in->type)
683         {
684         case MAP_SINGLE:
685           match = !memcmp (value, in->x.s, width);
686           break;
687         case MAP_ELSE:
688           match = true;
689           break;
690         case MAP_CONVERT:
691           {
692             union value uv;
693             char *error;
694
695             error = data_in (ss_buffer (CHAR_CAST_BUG (char *, value), width),
696                              C_ENCODING, FMT_F, settings_get_fmt_settings (),
697                              &uv, 0, encoding);
698             match = error == NULL;
699             free (error);
700
701             out->value.f = uv.f;
702             break;
703           }
704         case MAP_MISSING:
705           match = var_is_str_missing (src_var, value) != 0;
706           break;
707         default:
708           NOT_REACHED ();
709         }
710
711       if (match)
712         return out;
713     }
714
715   return NULL;
716 }
717
718 /* Performs RECODE transformation. */
719 static enum trns_result
720 recode_trns_proc (void *trns_, struct ccase **c, casenumber case_idx UNUSED)
721 {
722   struct recode_trns *trns = trns_;
723
724   *c = case_unshare (*c);
725   for (size_t i = 0; i < trns->n_vars; i++)
726     {
727       const struct variable *src_var = trns->src_vars[i];
728       const struct variable *dst_var = trns->dst_vars[i];
729       const struct map_out *out;
730
731       if (trns->src_type == VAL_NUMERIC)
732         out = find_src_numeric (trns, case_num (*c, src_var), src_var);
733       else
734         out = find_src_string (trns, case_str (*c, src_var), src_var);
735
736       if (trns->dst_type == VAL_NUMERIC)
737         {
738           double *dst = case_num_rw (*c, dst_var);
739           if (out != NULL)
740             *dst = !out->copy_input ? out->value.f : case_num (*c, src_var);
741           else if (trns->src_vars != trns->dst_vars)
742             *dst = SYSMIS;
743         }
744       else
745         {
746           char *dst = CHAR_CAST_BUG (char *, case_str_rw (*c, dst_var));
747           if (out != NULL)
748             {
749               if (!out->copy_input)
750                 memcpy (dst, out->value.s, var_get_width (dst_var));
751               else if (trns->src_vars != trns->dst_vars)
752                 {
753                   union value *dst_data = case_data_rw (*c, dst_var);
754                   const union value *src_data = case_data (*c, src_var);
755                   value_copy_rpad (dst_data, var_get_width (dst_var),
756                                    src_data, var_get_width (src_var), ' ');
757                 }
758             }
759           else if (trns->src_vars != trns->dst_vars)
760             memset (dst, ' ', var_get_width (dst_var));
761         }
762     }
763
764   return TRNS_CONTINUE;
765 }
766
767 /* Frees a RECODE transformation. */
768 static bool
769 recode_trns_free (void *trns_)
770 {
771   struct recode_trns *trns = trns_;
772   pool_destroy (trns->pool);
773   return true;
774 }
775
776 static const struct trns_class recode_trns_class = {
777   .name = "RECODE",
778   .execute = recode_trns_proc,
779   .destroy = recode_trns_free,
780 };